Only a couple of nit-pick comments. Generally, since there is already discussion in the file itself, I'd support committing additional discussion directly, and then discussing it on the mailing list if needed.
-Hyrum On Thu, Nov 3, 2011 at 1:10 PM, Julian Foad <julian.f...@wandisco.com>wrote: > Here, in the form of a patch, are many suggestions and queries I've > made on Ev2. I'm not intimately familiar with the design and goals of > it, I'm just responding to how it's currently written up in the header > file, with some memory of past discussions. Discussion welcomed. > > [[[ > Index: subversion/include/svn_editor.h > =================================================================== > --- subversion/include/svn_editor.h (revision 1197094) > +++ subversion/include/svn_editor.h (working copy) > @@ -177,18 +177,25 @@ > * \n\n > * Just before each callback invocation is carried out, the @a > cancel_func > * that was passed to svn_editor_create() is invoked to poll any > * external reasons to cancel the sequence of operations. Unless it > * overrides the cancellation (denoted by #SVN_ERR_CANCELLED), the > driver > * aborts the transmission by invoking the svn_editor_abort() callback. > * Exceptions to this are calls to svn_editor_complete() and > * svn_editor_abort(), which cannot be canceled externally. > * > + * ### JAF: It should check for cancellation before 'complete'. > + * Imagine the user runs 'svn commit large-new-file' and then > + * tries to cancel it, and imagine svn_editor_add() does not > + * internally respond to cancellation. If the driver then calls > + * 'complete' without intercepting the cancellation, the user > + * would not be happy to see that commit being completed. > + * > * - @b Receive: While the driver invokes operations upon the editor, the > * receiver finds its callback functions called with the information > * to operate on its tree. Each actual callback function receives those > * arguments that the driver passed to the "driving" functions, plus > these: > * - @a baton: This is the @a editor_baton pointer originally passed to > * svn_editor_create(). It may be freely used by the callback > * implementation to store information across all callbacks. > * - @a scratch_pool: This temporary pool is cleared directly after > * each callback returns. See "Pool Usage". > @@ -203,76 +210,111 @@ > * > * <h3>Driving Order Restrictions</h3> > * In order to reduce complexity of callback receivers, the editor > callbacks > * must be driven in adherence to these rules: > * > * - svn_editor_add_directory() -- Another svn_editor_add_*() call must > * follow for each child mentioned in the @a children argument of any > * svn_editor_add_directory() call. > * > + * ### JAF: ... must follow at any time after the parent > add_directory(), > + * and not before it. > + * > * - svn_editor_set_props() > * - The @a complete argument must be TRUE if no more calls will follow > on > * the same path. @a complete must always be TRUE for directories. > * - If @a complete is FALSE, and: > * - if @a relpath is a file, this must (at some point) be followed by > * an svn_editor_set_text() call on the same path. > * - if @a relpath is a symlink, this must (at some point) be followed > by > * an svn_editor_set_target() call on the same path. > * > * - svn_editor_set_text() and svn_editor_set_target() must always occur > * @b after an svn_editor_set_props() call on the same path, if any. > * > * In other words, if there are two calls coming in on the same path, the > * first of them has to be svn_editor_set_props(). > * > + * ### JAF: The set_* functions treat the properties of a node as > + * separate from the node's text or symlink target, whereas the > + * add_* functions treat the properties as an integral part of each > + * kind of node. This seems like a needless difference. It would make > + * sense for set_text() to take the properties with the text, and let > + * the implementation worry about whether the props and/or the text > + * have actually changed (as well as how to transmit the changes > + * most efficiently). For symlinks, include the properties with the > + * 'set target' call (rename to 'set symlink') and for directories > + * introduce a 'set directory' function, and remove the set_props() > + * function. This would eliminate the exception in the "Once Rule". > + * > * - Other than the above two pairs of linked operations, a path should > * never be referenced more than once by the add_* and set_* and the > * delete operations (the "Once Rule"). The source path of a copy (and > * its children, if a directory) may be copied many times, and are > * otherwise subject to the Once Rule. The destination path of a copy > * or move may have set_* operations applied, but not add_* or delete. > * If the destination path of a copy or move is a directory, then its > * children are subject to the Once Rule. The source path of a move > * (and its child paths) may be referenced in add_*, or as the > * destination of a copy (where these new, copied nodes are subject to > * the Once Rule). > * > * - The ancestor of an added or modified node may not be deleted. The > * ancestor may not be moved (instead: perform the move, *then* the > edits). > + * > + * ### JAF: Ancestor == parent dir? > + * > + * ### JAF: The ancestor of a node that is modified or added or > copied-here > + * or moved-here ...? > + * > + * ### JAF: The ancestor of ... [can | may not?] be copied or moved. > * > * - svn_editor_delete() must not be used to replace a path -- i.e. > * svn_editor_delete() must not be followed by an svn_editor_add_*() on > * the same path, nor by an svn_editor_copy() or svn_editor_move() with > * the same path as the copy/move target. > + * > + * ### JAF: Nor followed by set_*(). > + * > + * ### JAF: Nor followed by copy() or move() with the same path as the > + * copy/move source? > * > * Instead of a prior delete call, the add/copy/move callbacks should be > * called with the @a replaces_rev argument set to the revision number of > * the node at this path that is being replaced. Note that the path and > * revision number are the key to finding any other information about the > * replaced node, like node kind, etc. > * @todo say which function(s) to use. > + * > + * ### JAF: Can we have a way to refer to replacing a node in a tree that > + * is not a committed revision and so doesn't have a revision number? > + * This seems to be the only place where the editor definition > requires > + * that a target node is a (copy of a) committed repository node. > * > - * - svn_editor_delete() must not be used to move a path -- i.e. > - * svn_editor_delete() must not delete the source path of a previous > + * - svn_editor_delete() should not be used to move a path -- i.e. > + * svn_editor_delete() should not delete the source path of a previous > These should be left as-is, per RFC 2119. > * svn_editor_copy() call. Instead, svn_editor_move() must be used. > * Note: if the desired semantics is one (or more) copies, followed > * by a delete... that is fine. It is simply that svn_editor_move() > * should be used to describe a semantic move. > * > * - One of svn_editor_complete() or svn_editor_abort() must be called > * exactly once, which must be the final call the driver invokes. > * Invoking svn_editor_complete() must imply that the set of changes has > * been transmitted completely and without errors, and invoking > * svn_editor_abort() must imply that the transformation was not > completed > * successfully. > * > * - If any callback invocation returns with an error, the driver must > * invoke svn_editor_abort() and stop transmitting operations. > + * > + * - ### JAF: Some restriction analogous to the receiver's "All callbacks > + * must complete their handling of a path before they return ..."? > * \n\n > * > * <h3>Receiving Restrictions</h3> > * All callbacks must complete their handling of a path before they > * return, except for the following pairs, where a change must be completed > * when receiving the second callback in each pair: > * - svn_editor_set_props() (if @a complete is FALSE) and > * svn_editor_set_text() (if the node is a file) > * - svn_editor_set_props() (if @a complete is FALSE) and > @@ -328,25 +370,45 @@ > * calling any of the driving functions (e.g. svn_editor_add_directory()). > * As with any other error, the driver must then invoke svn_editor_abort() > * and abort the transformation sequence. See #svn_cancel_func_t. > * > * The @a cancel_baton argument to svn_editor_create() is passed > * unchanged to each poll of @a cancel_func. > * > * The cancellation function and baton are typically provided by the client > * context. > + * > + * ### JAF: Bring the section on cancellation under the Life-Cycle heading > + * down to here and combine it. > * > * > * @todo ### TODO anything missing? -- allow text and prop change to follow > - * a move or copy. -- set_text() vs. apply_text_delta()? -- If a > + * a move or copy. -- set_text() vs. apply_text_delta()? > + * > + * ### JAF: Those two seem to be done already. > + * > + * -- If a > * set_props/set_text/set_target/copy/move/delete in a merge source is > * applied to a different branch, which side will REVISION arguments > reflect > * and is there still a problem? > + * > + * ### JAF: The 'revision' argument will always refer to the 'before' > + * state of the source node that the editor is editing. The receiver > + * must know about this source node to make sense of the edit. If > + * the receiver is merging the source-node edits into some other > + * node on a different target branch, the editor knows nothing about > + * that other node (which may not even have a revision number if > + * it's a working version that's been modified already by some > + * previous merge or user intervention). > + * > + * ### JAF: As well as the text checksum for files, consider adding an > + * (optional?) checksum for the full content of every node kind -- > + * text with props, symlink with props, dir with list of children. > * > * @since New in 1.8. > */ > typedef struct svn_editor_t svn_editor_t; > > > /** These function types define the callback functions a tree delta > consumer > * implements. > * > @@ -698,57 +760,51 @@ > * > * Create a new directory at @a relpath. The immediate parent of @a relpath > * is expected to exist. > * > * Set the properties of the new directory to @a props, which is an > * apr_hash_t holding key-value pairs. Each key is a const char* of a > * property name, each value is a const svn_string_t*. If no properties are > * being set on the new directory, @a props must be NULL. > * > - * If this add is expected to replace a previously existing file or > - * directory at @a relpath, the revision number of the node to be replaced > + * If this add is expected to replace a previously existing node > Use the more specific "directory" rather than "node". (This is a doctoring which only applies to directories.) > + * at @a relpath, the revision number of the node to be replaced > * must be given in @a replaces_rev. Otherwise, @a replaces_rev must be > * SVN_INVALID_REVNUM. Note: it is not allowed to call a "delete" followed > * by an "add" on the same path. Instead, an "add" with @a replaces_rev set > * accordingly MUST be used. > * > * A complete listing of the immediate children of @a relpath that will be > * added subsequently is given in @a children. @a children is an array of > * const char*s, each giving the basename of an immediate child. > * > * For all restrictions on driving the editor, see #svn_editor_t. > */ > svn_error_t * > svn_editor_add_directory(svn_editor_t *editor, > const char *relpath, > const apr_array_header_t *children, > apr_hash_t *props, > svn_revnum_t replaces_rev); > +/* ### JAF: Let's keep the 'relpath' and 'replaces_rev' params together, > + * like they are in the delete/copy/move callbacks, because > + * logically they're tightly related. Also in other add_*(). */ > > /** Drive @a editor's #svn_editor_cb_add_file_t callback. > * > * Create a new file at @a relpath. The immediate parent of @a relpath > * is expected to exist. > * > * The file's contents are specified in @a contents which has a checksum > * matching @a checksum. > * > - * Set the properties of the new file to @a props, which is an > - * apr_hash_t holding key-value pairs. Each key is a const char* of a > - * property name, each value is a const svn_string_t*. If no properties > are > - * being set on the new file, @a props must be NULL. > - * > - * If this add is expected to replace a previously existing file or > - * directory at @a relpath, the revision number of the node to be replaced > - * must be given in @a replaces_rev. Otherwise, @a replaces_rev must be > - * SVN_INVALID_REVNUM. Note: it is not allowed to call a "delete" > followed > - * by an "add" on the same path. Instead, an "add" with @a replaces_rev > set > - * accordingly MUST be used. > + * For descriptions of @a props and @a replaces_rev, see > + * svn_editor_add_file(). > * > * For all restrictions on driving the editor, see #svn_editor_t. > * @since New in 1.8. > */ > svn_error_t * > svn_editor_add_file(svn_editor_t *editor, > const char *relpath, > const svn_checksum_t *checksum, > svn_stream_t *contents, > @@ -772,18 +828,19 @@ > const char *target, > apr_hash_t *props, > svn_revnum_t replaces_rev); > > /** Drive @a editor's #svn_editor_cb_add_absent_t callback. > * > * Create an "absent" node of kind @a kind at @a relpath. The immediate > * parent of @a relpath is expected to exist. > * ### TODO @todo explain "absent". > + * ### JAF: What are the allowed values of 'kind'? > * > * For a description of @a replaces_rev, see svn_editor_add_file(). > * > * For all restrictions on driving the editor, see #svn_editor_t. > * @since New in 1.8. > */ > svn_error_t * > svn_editor_add_absent(svn_editor_t *editor, > const char *relpath, > @@ -794,18 +851,20 @@ > * > * Set or change properties on the existing node at @a relpath. This > * function sends *all* properties, both existing and changes. > * ### TODO @todo What is REVISION for? > * ### HKW: This is puzzling to me as well... > * ### > * ### what about "entry props"? will these still be handled via > * ### the general prop function? > * > + * For a description of @a props, see svn_editor_add_file(). > + * > * @a complete must be FALSE if and only if > * - @a relpath is a file and an svn_editor_set_text() call will follow on > * the same path, or > * - @a relpath is a symbolic link and an svn_editor_set_target() call will > * follow on the same path. > * > * For all restrictions on driving the editor, see #svn_editor_t. > * @since New in 1.8. > */ > @@ -815,18 +874,47 @@ > svn_revnum_t revision, > apr_hash_t *props, > svn_boolean_t complete); > > /** Drive @a editor's #svn_editor_cb_set_text_t callback. > * > * Set/change the text content of a file at @a relpath to @a contents > * with checksum @a checksum. > * ### TODO @todo Does this send the *complete* content, always? > + * ### JAF: I think the idea is that 'contents' is the new full text and, > + * if wanted, the driver and receiver implementations should diff and > + * patch (respectively) it against the old full text. > + * > + * ### JAF: This may be inefficient for an implementation that already > + * has a diff available but doesn't have a full text available > + * (perhaps neither the 'before' nor the 'after' full text). We > + * should consider providing an alternative driver-side API so > + * that the driver can choose to bypass this full-text phase and > + * send the new text directly in any representation agreed with > + * the receiver, including as a diff against the old. On the > + * receiver side the same consideration applies. > Please, please, please, please, no secret backdoors! Let's get this implementation figured out, and then worry about potential optimizations. > + * > + * ### JAF: For the driver: @a contents is a readable stream. The editor > + * implementation may pull text from it as required, and will then > + * close it before returning. The editor [### need not | will] > + * pull all the text from the stream before closing it. > + * > + * ### JAF: For the receiver: @a contents is a readable stream. The > + * receiver may pull text from it as required, and will then > + * close it before returning. The receiver [### need not | will] > + * pull all the text from the stream before closing it. > + * > + * ### JAF: Is it permissible for the text change to be a no-op? The > + * driver may wish to avoid doing so for efficiency if it knows, > + * but I'd say we should not forbid it. Either way we should > + * consider how to be consistent in this regard across all the > + * whole editor. > No-op text changes are permissible. > + * > * ### TODO @todo What is REVISION for? > * > * For all restrictions on driving the editor, see #svn_editor_t. > * @since New in 1.8. > */ > svn_error_t * > svn_editor_set_text(svn_editor_t *editor, > const char *relpath, > svn_revnum_t revision, > ]]] > > - Julian > -- uberSVN: Apache Subversion Made Easy http://www.uberSVN.com/