ate changes. * - The client networking library receives that reply, and produces a * tree delta --- hopefully equivalent to the one the Subversion * server produced above. * - The working copy library consumes that delta, and makes the * appropriate changes to the working copy. * * The simplest approach would be to represent tree deltas using the * obvious data structure. To do an update, the server would * construct a delta structure, and the working copy library would * apply that structure to the working copy; the network layer's job * would simply be to get the structure across the net intact. * * However, we expect that these deltas will occasionally be too large * to fit in a typical workstation's swap area. For example, in * checking out a 200Mb source tree, the entire source tree is * represented by a single tree delta. So it's important to handle * deltas that are too large to fit in swap all at once. * * So instead of representing the tree delta explicitly, we define a * standard way for a consumer to process each piece of a tree delta * as soon as the producer creates it. The #svn_delta_editor_t * structure is a set of callback functions to be defined by a delta * consumer, and invoked by a delta producer. Each invocation of a * callback function describes a piece of the delta --- a file's * contents changing, something being renamed, etc. * * @defgroup svn_delta_tree_deltas Tree deltas * @{ */ /** A structure full of callback functions the delta source will invoke * as it produces the delta. * * @note Don't try to allocate one of these yourself. Instead, always * use svn_delta_default_editor() or some other constructor, to avoid * backwards compatibility problems if the structure is extended in * future releases and to ensure that unused slots are filled in with * no-op functions. * *

Function Usage

* * Here's how to use these functions to express a tree delta. * * The delta consumer implements the callback functions described in * this structure, and the delta producer invokes them. So the * caller (producer) is pushing tree delta data at the callee * (consumer). * * At the start of traversal, the consumer provides @a edit_baton, a * baton global to the entire delta edit. If there is a target * revision that needs to be set for this operation, the producer * should call the @c set_target_revision function at this point. * * Next, if there are any tree deltas to express, the producer should * pass the @a edit_baton to the @c open_root function, to get a baton * representing root of the tree being edited. * * Most of the callbacks work in the obvious way: * * @c delete_entry * @c add_file * @c add_directory * @c open_file * @c open_directory * * Each of these takes a directory baton, indicating the directory * in which the change takes place, and a @a path argument, giving the * path of the file, subdirectory, or directory entry to change. * * The @a path argument to each of the callbacks is relative to the * root of the edit. Editors will usually want to join this relative * path with some base stored in the edit baton (e.g. a URL, or a * location in the OS filesystem). * * Since every call requires a parent directory baton, including * @c add_directory and @c open_directory, where do we ever get our * initial directory baton, to get things started? The @c open_root * function returns a baton for the top directory of the change. In * general, the producer needs to invoke the editor's @c open_root * function before it can get anything of interest done. * * While @c open_root provides a directory baton for the root of * the tree being changed, the @c add_directory and @c open_directory * callbacks provide batons for other directories. Like the * callbacks above, they take a @a parent_baton and a relative path * @a path, and then return a new baton for the subdirectory being * created / modified --- @a child_baton. The producer can then use * @a child_baton to make further changes in that subdirectory. * * So, if we already have subdirectories named `foo' and `foo/bar', * then the producer can create a new file named `foo/bar/baz.c' by * calling: * * - @c open_root () --- yielding a baton @a root for the top directory * * - @c open_directory (@a root, "foo") --- yielding a baton @a f for `foo' * * - @c open_directory (@a f, "foo/bar") --- yielding a baton @a b for * `foo/bar' * * - @c add_file (@a b, "foo/bar/baz.c") * * When the producer is finished making changes to a directory, it * should call @c close_directory. This lets the consumer do any * necessary cleanup, and free the baton's storage. * * The @c add_file and @c open_file callbacks each return a baton * for the file being created or changed. This baton can then be * passed to @c apply_textdelta or @c apply_textdelta_stream to change * the file's contents, or @c change_file_prop to change the file's * properties. When the producer is finished making changes to a * file, it should call @c close_file, to let the consumer clean up * and free the baton. * * The @c add_file and @c add_directory functions each take arguments * @a copyfrom_path and @a copyfrom_revision. If @a copyfrom_path is * non-@c NULL, then @a copyfrom_path and @a copyfrom_revision indicate where * the file or directory should be copied from (to create the file * or directory being added). In that case, @a copyfrom_path must be * either a path relative to the root of the edit, or a URI from the * repository being edited. If @a copyfrom_path is @c NULL, then @a * copyfrom_revision must be #SVN_INVALID_REVNUM; it is invalid to * pass a mix of valid and invalid copyfrom arguments. * * *

Function Call Ordering

* * There are six restrictions on the order in which the producer * may use the batons: * * 1. The producer may call @c open_directory, @c add_directory, * @c open_file, @c add_file at most once on any given directory * entry. @c delete_entry may be called at most once on any given * directory entry and may later be followed by @c add_directory or * @c add_file on the same directory entry. @c delete_entry may * not be called on any directory entry after @c open_directory, * @c add_directory, @c open_file or @c add_file has been called on * that directory entry. * * 2. The producer may not close a directory baton until it has * closed all batons for its subdirectories. * * 3. When a producer calls @c open_directory or @c add_directory, * it must specify the most recently opened of the currently open * directory batons. Put another way, the producer cannot have * two sibling directory batons open at the same time. * * 4. A producer must call @c change_dir_prop on a directory either * before opening any of the directory's subdirs or after closing * them, but not in the middle. * * 5. When the producer calls @c open_file or @c add_file, either: * * (a) The producer must follow with any changes to the file * (@c change_file_prop and/or @c apply_textdelta / * @c apply_textdelta_stream, as applicable), followed by * a @c close_file call, before issuing any other file or * directory calls, or * * (b) The producer must follow with a @c change_file_prop call if * it is applicable, before issuing any other file or directory * calls; later, after all directory batons including the root * have been closed, the producer must issue @c apply_textdelta / * @c apply_textdelta_stream and @c close_file calls. * * 6. When the producer calls @c apply_textdelta, it must make all of * the window handler calls (including the @c NULL window at the * end) before issuing any other #svn_delta_editor_t calls. * * So, the producer needs to use directory and file batons as if it * is doing a single depth-first traversal of the tree, with the * exception that the producer may keep file batons open in order to * make @c apply_textdelta / @c apply_textdelta_stream calls at the end. * * *

Pool Usage

* * Many editor functions are invoked multiple times, in a sequence * determined by the editor "driver". The driver is responsible for * creating a pool for use on each iteration of the editor function, * and clearing that pool between each iteration. The driver passes * the appropriate pool on each function invocation. * * Based on the requirement of calling the editor functions in a * depth-first style, it is usually customary for the driver to similarly * nest the pools. However, this is only a safety feature to ensure * that pools associated with deeper items are always cleared when the * top-level items are also cleared. The interface does not assume, nor * require, any particular organization of the pools passed to these * functions. In fact, if "postfix deltas" are used for files, the file * pools definitely need to live outside the scope of their parent * directories' pools. * * Note that close_directory can be called *before* a file in that * directory has been closed. That is, the directory's baton is * closed before the file's baton. The implication is that * @c apply_textdelta / @c apply_textdelta_stream and @c close_file * should not refer to a parent directory baton UNLESS the editor has * taken precautions to allocate it in a pool of the appropriate * lifetime (the @a result_pool passed to @c open_directory and * @c add_directory definitely does not have the proper lifetime). * In general, it is recommended to simply avoid keeping a parent * directory baton in a file baton. * * *

Errors

* * At least one implementation of the editor interface is * asynchronous; an error from one operation may be detected some * number of operations later. As a result, an editor driver must not * assume that an error from an editing function resulted from the * particular operation being detected. Moreover, once an editing * function (including @c close_edit) returns an error, the edit is * dead; the only further operation which may be called on the editor * is @c abort_edit. */ typedef struct svn_delta_editor_t { /** Set the target revision for this edit to @a target_revision. This * call, if used, should precede all other editor calls. * * @note This is typically used only for server->client update-type * operations. It doesn't really make much sense for commit-type * operations, because the revision of a commit isn't known until * the commit is finalized. * * Any temporary allocations may be performed in @a scratch_pool. */ svn_error_t *(*set_target_revision)(void *edit_baton, svn_revnum_t target_revision, apr_pool_t *scratch_pool); /** Set @a *root_baton to a baton for the top directory of the change. * (This is the top of the subtree being changed, not necessarily * the root of the filesystem.) As with any other directory baton, the * producer should call @c close_directory on @a root_baton when done. * And as with other @c open_* calls, the @a base_revision here is the * current revision of the directory (before getting bumped up to the * new target revision set with @c set_target_revision). * * Allocations for the returned @a root_baton should be performed in * @a result_pool. It is also typical to (possibly) save this pool for * later usage by @c close_directory. */ svn_error_t *(*open_root)(void *edit_baton, svn_revnum_t base_revision, apr_pool_t *result_pool, void **root_baton); /** Remove the directory entry at @a path, a child of the directory * represented by @a parent_baton. If @a revision is a valid * revision number, it is used as a sanity check to ensure that you * are really removing the revision of @a path that you think you are. * * Any temporary allocations may be performed in @a scratch_pool. * * @note The @a revision parameter is typically used only for * client->server commit-type operations, allowing the server to * verify that it is deleting what the client thinks it should be * deleting. It only really makes sense in the opposite direction * (during server->client update-type operations) when the trees * whose delta is being described are ancestrally related (that is, * one tree is an ancestor of the other). */ svn_error_t *(*delete_entry)(const char *path, svn_revnum_t revision, void *parent_baton, apr_pool_t *scratch_pool); /** We are going to add a new subdirectory at @a path, a child of * the directory represented by @a parent_baton. We will use * the value this callback stores in @a *child_baton as the * parent baton for further changes in the new subdirectory. * * If @a copyfrom_path is non-@c NULL, this add has history (i.e., is a * copy), and the origin of the copy may be recorded as * @a copyfrom_path under @a copyfrom_revision. * * Allocations for the returned @a child_baton should be performed in * @a result_pool. It is also typical to (possibly) save this pool for * later usage by @c close_directory. */ svn_error_t *(*add_directory)(const char *path, void *parent_baton, const char *copyfrom_path, svn_revnum_t copyfrom_revision, apr_pool_t *result_pool, void **child_baton); /** We are going to make changes in the subdirectory at @a path, a * child of the directory represented by @a parent_baton. * The callback must store a value in @a *child_baton that * should be used as the parent baton for subsequent changes in this * subdirectory. If a valid revnum, @a base_revision is the current * revision of the subdirectory. * * Allocations for the returned @a child_baton should be performed in * @a result_pool. It is also typical to (possibly) save this pool for * later usage by @c close_directory. */ svn_error_t *(*open_directory)(const char *path, void *parent_baton, svn_revnum_t base_revision, apr_pool_t *result_pool, void **child_baton); /** Change the value of a directory's property. * - @a dir_baton specifies the directory whose property should change. * - @a name is the name of the property to change. * - @a value is the new (final) value of the property, or @c NULL if the * property should be removed altogether. * * The callback is guaranteed to be called exactly once for each property * whose value differs between the start and the end of the edit. * * Any temporary allocations may be performed in @a scratch_pool. */ svn_error_t *(*change_dir_prop)(void *dir_baton, const char *name, const svn_string_t *value, apr_pool_t *scratch_pool); /** We are done processing a subdirectory, whose baton is @a dir_baton * (set by @c add_directory or @c open_directory). We won't be using * the baton any more, so whatever resources it refers to may now be * freed. * * Any temporary allocations may be performed in @a scratch_pool. */ svn_error_t *(*close_directory)(void *dir_baton, apr_pool_t *scratch_pool); /** In the directory represented by @a parent_baton, indicate that * @a path is present as a subdirectory in the edit source, but * cannot be conveyed to the edit consumer. Currently, this would * only occur because of authorization restrictions, but may change * in the future. * * Any temporary allocations may be performed in @a scratch_pool. */ svn_error_t *(*absent_directory)(const char *path, void *parent_baton, apr_pool_t *scratch_pool); /** We are going to add a new file at @a path, a child of the * directory represented by @a parent_baton. The callback can * store a baton for this new file in @a **file_baton; whatever value * it stores there should be passed through to @c apply_textdelta or * @c apply_textdelta_stream. * * If @a copyfrom_path is non-@c NULL, this add has history (i.e., is a * copy), and the origin of the copy may be recorded as * @a copyfrom_path under @a copyfrom_revision. * * Allocations for the returned @a file_baton should be performed in * @a result_pool. It is also typical to save this pool for later usage * by @c apply_textdelta and possibly @c close_file. * * @note Because the editor driver could be employing the "postfix * deltas" paradigm, @a result_pool could potentially be relatively * long-lived. Every file baton created by the editor for a given * editor drive might be resident in memory similtaneously. Editor * implementations should ideally keep their file batons as * conservative (memory-usage-wise) as possible, and use @a result_pool * only for those batons. (Consider using a subpool of @a result_pool * for scratch work, destroying the subpool before exiting this * function's implementation.) */ svn_error_t *(*add_file)(const char *path, void *parent_baton, const char *copyfrom_path, svn_revnum_t copyfrom_revision, apr_pool_t *result_pool, void **file_baton); /** We are going to make changes to a file at @a path, a child of the * directory represented by @a parent_baton. * * The callback can store a baton for this new file in @a **file_baton; * whatever value it stores there should be passed through to * @c apply_textdelta or @c apply_textdelta_stream. If a valid revnum, * @a base_revision is the current revision of the file. * * Allocations for the returned @a file_baton should be performed in * @a result_pool. It is also typical to save this pool for later usage * by @c apply_textdelta and possibly @c close_file. * * @note See note about memory usage on @a add_file, which also * applies here. */ svn_error_t *(*open_file)(const char *path, void *parent_baton, svn_revnum_t base_revision, apr_pool_t *result_pool, void **file_baton); /** Apply a text delta, yielding the new revision of a file. * * @a file_baton indicates the file we're creating or updating, and the * ancestor file on which it is based; it is the baton set by some * prior @c add_file or @c open_file callback. * * The callback should set @a *handler to a text delta window * handler; we will then call @a *handler on successive text * delta windows as we receive them. The callback should set * @a *handler_baton to the value we should pass as the @a baton * argument to @a *handler. These values should be allocated within * @a result_pool. * * @a base_checksum is the hex MD5 digest for the base text against * which the delta is being applied; it is ignored if NULL, and may * be ignored even if not NULL. If it is not ignored, it must match * the checksum of the base text against which svndiff data is being * applied; if it does not, @c apply_textdelta or the @a *handler call * which detects the mismatch will return the error * SVN_ERR_CHECKSUM_MISMATCH (if there is no base text, there may * still be an error if @a base_checksum is neither NULL nor the hex * MD5 checksum of the empty string). */ svn_error_t *(*apply_textdelta)(void *file_baton, const char *base_checksum, apr_pool_t *result_pool, svn_txdelta_window_handler_t *handler, void **handler_baton); /** Change the value of a file's property. * - @a file_baton specifies the file whose property should change. * - @a name is the name of the property to change. * - @a value is the new (final) value of the property, or @c NULL if the * property should be removed altogether. * * The callback is guaranteed to be called exactly once for each property * whose value differs between the start and the end of the edit. * * Any temporary allocations may be performed in @a scratch_pool. */ svn_error_t *(*change_file_prop)(void *file_baton, const char *name, const svn_string_t *value, apr_pool_t *scratch_pool); /** We are done processing a file, whose baton is @a file_baton (set by * @c add_file or @c open_file). We won't be using the baton any * more, so whatever resources it refers to may now be freed. * * @a text_checksum is the hex MD5 digest for the fulltext that * resulted from a delta application, see @c apply_textdelta and * @c apply_textdelta_stream. The checksum is ignored if NULL. * If not null, it is compared to the checksum of the new fulltext, * and the error SVN_ERR_CHECKSUM_MISMATCH is returned if they do * not match. If there is no new fulltext, @a text_checksum is ignored. * * Any temporary allocations may be performed in @a scratch_pool. */ svn_error_t *(*close_file)(void *file_baton, const char *text_checksum, apr_pool_t *scratch_pool); /** In the directory represented by @a parent_baton, indicate that * @a path is present as a file in the edit source, but cannot be * cannot be conveyed to the edit consumer. Currently, this would * only occur because of authorization restrictions, but may change * in the future. * * Any temporary allocations may be performed in @a scratch_pool. */ svn_error_t *(*absent_file)(const char *path, void *parent_baton, apr_pool_t *scratch_pool); /** All delta processing is done. Call this, with the @a edit_baton for * the entire edit. * * Any temporary allocations may be performed in @a scratch_pool. */ svn_error_t *(*close_edit)(void *edit_baton, apr_pool_t *scratch_pool); /** The editor-driver has decided to bail out. Allow the editor to * gracefully clean up things if it needs to. * * Any temporary allocations may be performed in @a scratch_pool. */ svn_error_t *(*abort_edit)(void *edit_baton, apr_pool_t *scratch_pool); /** Apply a text delta stream, yielding the new revision of a file. * This callback operates on the passed-in @a editor instance. * * @a file_baton indicates the file we're creating or updating, and the * ancestor file on which it is based; it is the baton set by some * prior @c add_file or @c open_file callback. * * @a open_func is a function that opens a #svn_txdelta_stream_t object. * @a open_baton is provided by the caller. * * @a base_checksum is the hex MD5 digest for the base text against * which the delta is being applied; it is ignored if NULL, and may * be ignored even if not NULL. If it is not ignored, it must match * the checksum of the base text against which svndiff data is being * applied; if it does not, @c apply_textdelta_stream call which detects * the mismatch will return the error #SVN_ERR_CHECKSUM_MISMATCH * (if there is no base text, there may still be an error if * @a base_checksum is neither NULL nor the hex MD5 checksum of the * empty string). * * Any temporary allocations may be performed in @a scratch_pool. * * @since New in 1.10. */ svn_error_t *(*apply_textdelta_stream)( const struct svn_delta_editor_t *editor, void *file_baton, const char *base_checksum, svn_txdelta_stream_open_func_t open_func, void *open_baton, apr_pool_t *scratch_pool); /* Be sure to update svn_delta_get_cancellation_editor() and * svn_delta_default_editor() if you add a new callback here. */ } svn_delta_editor_t; /** Return a default delta editor template, allocated in @a pool. * * The editor functions in the template do only the most basic * baton-swapping: each editor function that produces a baton does so * by copying its incoming baton into the outgoing baton reference. * * This editor is not intended to be useful by itself, but is meant to * be the basis for a useful editor. After getting a default editor, * you substitute in your own implementations for the editor functions * you care about. The ones you don't care about, you don't have to * implement -- you can rely on the template's implementation to * safely do nothing of consequence. */ svn_delta_editor_t * svn_delta_default_editor(apr_pool_t *pool); /** A text-delta window handler which does nothing. * * Editors can return this handler from @c apply_textdelta if they don't * care about text delta windows. */ svn_error_t * svn_delta_noop_window_handler(svn_txdelta_window_t *window, void *baton); /** Set @a *editor and @a *edit_baton to a cancellation editor that * wraps @a wrapped_editor and @a wrapped_baton. * * The @a editor will call @a cancel_func with @a cancel_baton when each of * its functions is called, continuing on to call the corresponding wrapped * function if @a cancel_func returns #SVN_NO_ERROR. * * If @a cancel_func is @c NULL, set @a *editor to @a wrapped_editor and * @a *edit_baton to @a wrapped_baton. */ svn_error_t * svn_delta_get_cancellation_editor(svn_cancel_func_t cancel_func, void *cancel_baton, const svn_delta_editor_t *wrapped_editor, void *wrapped_baton, const svn_delta_editor_t **editor, void **edit_baton, apr_pool_t *pool); /** Set @a *editor and @a *edit_baton to an depth-based filtering * editor that wraps @a wrapped_editor and @a wrapped_baton. * * The @a editor will track the depth of this drive against the @a * requested_depth, taking into account whether not the edit drive is * making use of a target (via @a has_target), and forward editor * calls which operate "within" the request depth range through to @a * wrapped_editor. * * @a requested_depth must be one of the following depth values: * #svn_depth_infinity, #svn_depth_empty, #svn_depth_files, * #svn_depth_immediates, or #svn_depth_unknown. * * If filtering is deemed unnecessary (or if @a requested_depth is * #svn_depth_unknown), @a *editor and @a *edit_baton will be set to @a * wrapped_editor and @a wrapped_baton, respectively; otherwise, * they'll be set to new objects allocated from @a pool. * * @note Because the svn_delta_editor_t interface's @c delete_entry() * function doesn't carry node kind information, a depth-based * filtering editor being asked to filter for #svn_depth_files but * receiving a @c delete_entry() call on an immediate child of the * editor's target is unable to know if that deletion should be * allowed or filtered out -- a delete of a top-level file is okay in * this case, a delete of a top-level subdirectory is not. As such, * this filtering editor takes a conservative approach, and ignores * top-level deletion requests when filtering for #svn_depth_files. * Fortunately, most non-depth-aware (pre-1.5) Subversion editor * drivers can be told to drive non-recursively (where non-recursive * means essentially #svn_depth_files), which means they won't * transmit out-of-scope editor commands anyway. * * @since New in 1.5. */ svn_error_t * svn_delta_depth_filter_editor(const svn_delta_editor_t **editor, void **edit_baton, const svn_delta_editor_t *wrapped_editor, void *wrapped_edit_baton, svn_depth_t requested_depth, svn_boolean_t has_target, apr_pool_t *pool); /** @} */