| ### |
| ### |
| ### THIS FILE IS HISTORICAL ONLY. FOR CURRENT INFORMATION, PLEASE SEE: |
| ### include/svn_editor.h |
| ### |
| ### |
| |
| |
| NOTES on a revamped editor interface |
| |
| -- all paths are relpaths |
| -- could be: relative to wcroot, or repos, or <what else?> |
| -- editor/driver can map into other path spaces as appropriate |
| |
| Terminology: |
| "Driver" -- the code *calling* these APIs to tell the Receiver how |
| to edit a tree into a final state |
| |
| "Receiver" -- the code *implementing* the callbacks to receive |
| information on editing its tree |
| |
| |
| API will be like svn_stream_t, RA, and FS |
| -- functional API to invoke callbacks |
| -- can remap between versions |
| -- implementor can provide vtable-ish thing for quick-load |
| -- svn_editor_setcb_many(...) |
| -- API can debug the control flow |
| -- enforcement of constraints |
| -- printf() for debugging |
| -- single baton passed to all callbacks |
| -- no directory or file batons |
| -- only cross-call state that needs to be held is to manage the |
| two-call change operations. state may need to be held for |
| directory children that have not been added via add_*(). |
| -- no return values, so scratch_pool everywhere |
| -- get_info takes a result_pool/scratch_pool |
| -- treedit object passed to all calls: used for get_info callback |
| -- separate baton for get_info callback |
| -- build cancellation in by default |
| -- notify is too operation specific, so it gets omitted |
| |
| |
| add_directory(path, children, props, replaces_rev) |
| -- name all children. receiver can put on "pending" list |
| -- MUST be followed (at some point) by add_*() calls for each child |
| -- if replaces_rev isn't SVN_INVALID_REVNUM, this is an atomic replace. |
| Use a revnum because it implies all other info like node kind etc. |
| add_directory_streamy(path, children_stream, props, replaces_rev) |
| -- receiver reads (streamily) the names of all children |
| -- MUST be followed (at some point) by add_*() calls for each child |
| -- this alternative is to avoid cases where a directory might have |
| 100k child names held in memory at once |
| -- not entirely sure about this, since we *already* hold all |
| metadata for a single commit in memory. but maybe this will be |
| able to help us moving forward to reach a more streamy commit |
| -- if replaces_rev isn't SVN_INVALID_REVNUM, this is an atomic replace. |
| add_file(path, props, replaces_rev) |
| -- MUST be followed (at some point) by set_text() |
| -- if replaces_rev isn't SVN_INVALID_REVNUM, this is an atomic replace. |
| add_symlink(path, target, props, replaces_rev) |
| add_absent(path, kind, replaces_rev) |
| set_props(path, against_rev, props, complete) |
| -- 'complete' indicates whether this completes the change to this node |
| -- MUST be TRUE for directories |
| -- if FALSE, then a set_text() MUST follow if node is a file |
| -- if FALSE, then a set_target() MUST follow if node is a symlink |
| set_text(path, against_rev, checksum, stream) |
| -- completes the change for this file (ie. if set_props was called) |
| -- stream can be ignored by recipient if it knows the checksum |
| -- for example: optimize away multiple transfers of the same file |
| -- include a mime type to help recipient manage stream processing? |
| set_target(path, against_rev, target) |
| -- completes the change for this symlink (ie. if set_props was called) |
| delete(path, in_rev) |
| copy(src_path, src_rev, dst_path) |
| move(src_path, in_src_rev, dst_path) |
| complete() |
| -- all done. edit runs are not intended to be transactional, but |
| this *could* be used for that. |
| abort() |
| -- edit runs are not fully transactional. this is not guaranteed to |
| undo everything. |
| |
| |
| |
| -- expectations around *what* is being changed |
| -- add into parent REV. nah. you're adding to HEAD. only Q: already exists. |
| -- set props/text/target on REV. oops. there is newer. |
| -- delete REV. woah. edited, or deleted already. |
| -- copy: nah. just like add. (rev specifics source, not an expectation) |
| -- move REV: source will be like a delete, so could have been edited/deleted |
| |
| |
| note: all calls complete their change to the node before they return, |
| except for the following pairs of functions: |
| -- add_file / set_text |
| -- set_props / set_text |
| -- set_props / set_target |
| |
| |
| callback for more details about the BASE: |
| get_info(path, *kind, *revision, *checksum, *children, *target, |
| *props, *stream) |
| -- I think we might want to eliminate this, and make it specific to |
| the RA layer. I'm not sure that any other caller needs |
| information about the BASE in order to construct a delta. |
| -- well, maybe mod_dav_svn in order to get BASE info to construct |
| differential data for transmit-over-wire |
| -- in v1, we had the BASE checksum as we attempted to modify a file. |
| this api says "working against REV, here are the new contents". |
| we can verify REV, but how to verify the checksum? thus, the |
| callback to get more information about BASE. |
| |
| |
| other constraints: |
| -- one or both of add_directory() and add_directory_streamy() MUST be |
| implemented. the editor layer can remap between drivers and receivers |
| -- parent add_directory() MUST be called prior to add_* of any child |
| -- set_props() MUST be called before set_text() and set_target() if a |
| two-part change is occurring |
| -- add_*() called where a node already exists; must pass a REPLACES_REV |
| parameter to add_*(), or delete() or move() before calling add_*(). |
| Passing REPLACES_REV is preferable to a separate delete(). Having a |
| separate delete() (a non-atomic replace) should be considered carefully |
| and, if chosen, should be well contained in a small amount of code. |
| -- set_*(), delete(), copy(), move() called on a non-existent node |
| -- call flow errors if: |
| -- at complete() call time: |
| -- add_* not called for a child listed in add_directory's CHILDREN |
| -- set_text() not called after an add_file() |
| -- set_text() not called after a set_props() on a file where |
| COMPLETE=FALSE |
| -- set_target() not called after a set_props() on a symlink where |
| COMPLETE=FALSE |
| -- anything called after complete() or abort() |
| |
| |
| Q: why specify the entire list of children at add_directory() time? |
| A: makes add_directory() an "atomic" operation. if it returns, then |
| the directory is added (no multi-step change). you will have a |
| bunch of incomplete *children* but the directory will be |
| fine. makes it easier to resume. |
| |
| the alternative is a directory that stays "incomplete" until all |
| children have been added. and that isn't known without some kind of |
| "close_directory" call, or possibly a counter passed to the add. in |
| either case, midway failure leaves you in a state where you don't |
| know which children need to be resent. gotta grab them all again. |
| |
| |
| new interface: svn_editor.h |
| -- don't try to rev svn_delta_editor_t. creates a huge mess |
| -- svn_editor_* prefix |
| -- svn_editor_setcb_* to set the various callbacks |
| -- svn_editor_setcb_many(vtable) to copy funcs in |
| |
| -- provide an svn_delta_editor_t that can drive an svn_editor_t |
| -- driver will need to "peek" around the shim in order to pass |
| the list of all children to add_directory(). otherwise, we'd |
| have to buffer *everything*. not to mention that children |
| aren't really described unless a change is happening, so this |
| shim needs special access to get the list anyways |
| -- provide an svn_editor_t that can drive an svn_delta_editor_t |
| -- note that using the above two shims, we "should" actually be able |
| insert a matched pair "anywhere" between existing drivers/editors |
| and have the code continue to function properly |
| |
| |
| svn_error_t * |
| my_custom_editor(svn_editor_t **editor, info_cb, info_baton, pools) |
| { |
| SVN_ERR(svn_editor_create(editor, info_cb, info_baton, |
| result_pool, scratch_pool)); |
| SVN_ERR(svn_editor_setcb_add_directory(*editor, my_add_directory, |
| scratch_pool)); |
| ... |
| return SVN_NO_ERROR; |
| } |
| |
| |
| using: |
| |
| SVN_ERR(my_custom_editor(&editor, get_info, &get_baton, pools)); |
| SVN_ERR(svn_editor_add_directory(editor, ...)); |
| |
| |
| |
| EDITORS IN USE TODAY |
| |
| - export.c: editor passed to RA |
| - commit_util.c: debug editor |
| - mergeinfo.c: appears to use an editor as a kludge to collect |
| directory names |
| - repos_diff.c: some kind of "diff editor" ### need to look more |
| - repos_diff_summarize.c: another "diff editor" thing |
| - cancel.c: cancellation editor. now built-in |
| - debug_editor.c: debug the control flow. now built-in. |
| - default_editor.c: not needed |
| - depth_filter_editor.c: ### investigate |
| - ra_neon/commit.c: RA commit editor |
| - ra_serf/commit.c: RA commit editor |
| - ra_svn/editorp.c: RA commit editor |
| - repos/commit.c: commit editor to drive changes into FS |
| - dump.c: generate an svn dump |
| - diff.c: some kind of "diff editor" ### need to look more |
| - status.c: editor for driving status callbacks |
| - update_editor.c: passed to RA in order to edit the working copy |
| - mod_dav_svn/.../replay.c: ### investigate |
| - mod_dav_svn/.../update.c: generate an update report. ### how is this called? |
| - svnsync/main.c: ### investigate |
| - bindings/... |