2 * editor.c : editing trees of versioned resources
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
24 #include <apr_pools.h>
26 #include "svn_types.h"
27 #include "svn_error.h"
28 #include "svn_pools.h"
29 #include "svn_dirent_uri.h"
31 #include "private/svn_editor.h"
34 /* This enables runtime checks of the editor API constraints. This may
35 introduce additional memory and runtime overhead, and should not be used
38 ### Remove before release?
40 ### Disabled for now. If I call svn_editor_alter_directory(A) then
41 svn_editor_add_file(A/f) the latter fails on SHOULD_ALLOW_ADD.
42 If I modify svn_editor_alter_directory to MARK_ALLOW_ADD(child)
43 then if I call svn_editor_alter_directory(A) followed by
44 svn_editor_alter_directory(A/B/C) the latter fails on
45 VERIFY_PARENT_MAY_EXIST. */
47 #define ENABLE_ORDERING_CHECK
56 /* Standard cancellation function. Called before each callback. */
57 svn_cancel_func_t cancel_func;
60 /* Our callback functions match that of the set-many structure, so
62 svn_editor_cb_many_t funcs;
64 /* This pool is used as the scratch_pool for all callbacks. */
65 apr_pool_t *scratch_pool;
67 #ifdef ENABLE_ORDERING_CHECK
68 svn_boolean_t within_callback;
70 apr_hash_t *pending_incomplete_children;
71 apr_hash_t *completed_nodes;
72 svn_boolean_t finished;
74 apr_pool_t *state_pool;
79 #ifdef ENABLE_ORDERING_CHECK
81 #define START_CALLBACK(editor) \
83 svn_editor_t *editor__tmp_e = (editor); \
84 SVN_ERR_ASSERT(!editor__tmp_e->within_callback); \
85 editor__tmp_e->within_callback = TRUE; \
87 #define END_CALLBACK(editor) ((editor)->within_callback = FALSE)
89 /* Marker to indicate no further changes are allowed on this node. */
90 static const int marker_done = 0;
91 #define MARKER_DONE (&marker_done)
93 /* Marker indicating that add_* may be called for this path, or that it
94 can be the destination of a copy or move. For copy/move, the path
95 will switch to MARKER_ALLOW_ALTER, to enable further tweaks. */
96 static const int marker_allow_add = 0;
97 #define MARKER_ALLOW_ADD (&marker_allow_add)
99 /* Marker indicating that alter_* may be called for this path. */
100 static const int marker_allow_alter = 0;
101 #define MARKER_ALLOW_ALTER (&marker_allow_alter)
103 /* Just like MARKER_DONE, but also indicates that the node was created
104 via add_directory(). This allows us to verify that the CHILDREN param
105 was comprehensive. */
106 static const int marker_added_dir = 0;
107 #define MARKER_ADDED_DIR (&marker_added_dir)
109 #define MARK_FINISHED(editor) ((editor)->finished = TRUE)
110 #define SHOULD_NOT_BE_FINISHED(editor) SVN_ERR_ASSERT(!(editor)->finished)
112 #define CLEAR_INCOMPLETE(editor, relpath) \
113 svn_hash_sets((editor)->pending_incomplete_children, relpath, NULL);
115 #define MARK_RELPATH(editor, relpath, value) \
116 svn_hash_sets((editor)->completed_nodes, \
117 apr_pstrdup((editor)->state_pool, relpath), value)
119 #define MARK_COMPLETED(editor, relpath) \
120 MARK_RELPATH(editor, relpath, MARKER_DONE)
121 #define SHOULD_NOT_BE_COMPLETED(editor, relpath) \
122 SVN_ERR_ASSERT(svn_hash_gets((editor)->completed_nodes, relpath) == NULL)
124 #define MARK_ALLOW_ADD(editor, relpath) \
125 MARK_RELPATH(editor, relpath, MARKER_ALLOW_ADD)
126 #define SHOULD_ALLOW_ADD(editor, relpath) \
127 SVN_ERR_ASSERT(allow_either(editor, relpath, MARKER_ALLOW_ADD, NULL))
129 #define MARK_ALLOW_ALTER(editor, relpath) \
130 MARK_RELPATH(editor, relpath, MARKER_ALLOW_ALTER)
131 #define SHOULD_ALLOW_ALTER(editor, relpath) \
132 SVN_ERR_ASSERT(allow_either(editor, relpath, MARKER_ALLOW_ALTER, NULL))
134 #define MARK_ADDED_DIR(editor, relpath) \
135 MARK_RELPATH(editor, relpath, MARKER_ADDED_DIR)
136 #define CHECK_UNKNOWN_CHILD(editor, relpath) \
137 SVN_ERR_ASSERT(check_unknown_child(editor, relpath))
139 /* When a child is changed in some way, mark the parent directory as needing
140 to be "stable" (no future structural changes). IOW, only allow "alter" on
141 the parent. Prevents parent-add/delete/move after any child operation. */
142 #define MARK_PARENT_STABLE(editor, relpath) \
143 mark_parent_stable(editor, relpath)
145 /* If the parent is MARKER_ALLOW_ADD, then it has been moved-away, and we
146 know it does not exist. All other cases: it might exist. */
147 #define VERIFY_PARENT_MAY_EXIST(editor, relpath) \
148 SVN_ERR_ASSERT(svn_hash_gets((editor)->completed_nodes, \
149 svn_relpath_dirname(relpath, \
150 (editor)->scratch_pool)) \
153 /* If the parent is MARKER_ADDED_DIR, then we should not be deleting
154 children(*). If the parent is MARKER_ALLOW_ADD, then it has been
155 moved-away, so children cannot exist. That leaves MARKER_DONE,
156 MARKER_ALLOW_ALTER, and NULL as possible values. Just assert that
157 we didn't get either of the bad ones.
159 (*) if the child as added via add_*(), then it would have been marked
160 as completed and delete/move-away already test against completed nodes.
161 This test is to beware of trying to delete "children" that are not
162 actually (and can't possibly be) present. */
163 #define CHILD_DELETIONS_ALLOWED(editor, relpath) \
164 SVN_ERR_ASSERT(!allow_either(editor, \
165 svn_relpath_dirname(relpath, \
166 (editor)->scratch_pool), \
167 MARKER_ADDED_DIR, MARKER_ALLOW_ADD))
170 allow_either(const svn_editor_t *editor,
175 void *value = svn_hash_gets(editor->completed_nodes, relpath);
176 return value == marker1 || value == marker2;
180 check_unknown_child(const svn_editor_t *editor,
185 /* If we already know about the new child, then exit early. */
186 if (svn_hash_gets(editor->pending_incomplete_children, relpath) != NULL)
189 parent = svn_relpath_dirname(relpath, editor->scratch_pool);
191 /* Was this parent created via svn_editor_add_directory() ? */
192 if (svn_hash_gets(editor->completed_nodes, parent)
195 /* Whoops. This child should have been listed in that add call,
196 and placed into ->pending_incomplete_children. */
200 /* The parent was not added in this drive. */
205 mark_parent_stable(const svn_editor_t *editor,
208 const char *parent = svn_relpath_dirname(relpath, editor->scratch_pool);
209 const void *marker = svn_hash_gets(editor->completed_nodes, parent);
211 /* If RELPATH has already been marked (to disallow adds, or that it
212 has been fully-completed), then do nothing. */
213 if (marker == MARKER_ALLOW_ALTER
214 || marker == MARKER_DONE
215 || marker == MARKER_ADDED_DIR)
218 /* If the marker is MARKER_ALLOW_ADD, then that means the parent was
219 moved away. There is no way to work on a child. That should have
220 been tested before we got here by VERIFY_PARENT_MAY_EXIST(). */
221 SVN_ERR_ASSERT_NO_RETURN(marker != MARKER_ALLOW_ADD);
223 /* MARKER is NULL. Upgrade it to MARKER_ALLOW_ALTER. */
224 MARK_RELPATH(editor, parent, MARKER_ALLOW_ALTER);
229 /* Be wary with the definition of these macros so that we don't
230 end up with "statement with no effect" warnings. Obviously, this
231 depends upon particular usage, which is easy to verify. */
233 #define START_CALLBACK(editor) /* empty */
234 #define END_CALLBACK(editor) /* empty */
236 #define MARK_FINISHED(editor) /* empty */
237 #define SHOULD_NOT_BE_FINISHED(editor) /* empty */
239 #define CLEAR_INCOMPLETE(editor, relpath) /* empty */
241 #define MARK_COMPLETED(editor, relpath) /* empty */
242 #define SHOULD_NOT_BE_COMPLETED(editor, relpath) /* empty */
244 #define MARK_ALLOW_ADD(editor, relpath) /* empty */
245 #define SHOULD_ALLOW_ADD(editor, relpath) /* empty */
247 #define MARK_ALLOW_ALTER(editor, relpath) /* empty */
248 #define SHOULD_ALLOW_ALTER(editor, relpath) /* empty */
250 #define MARK_ADDED_DIR(editor, relpath) /* empty */
251 #define CHECK_UNKNOWN_CHILD(editor, relpath) /* empty */
253 #define MARK_PARENT_STABLE(editor, relpath) /* empty */
254 #define VERIFY_PARENT_MAY_EXIST(editor, relpath) /* empty */
255 #define CHILD_DELETIONS_ALLOWED(editor, relpath) /* empty */
257 #endif /* ENABLE_ORDERING_CHECK */
261 svn_editor_create(svn_editor_t **editor,
263 svn_cancel_func_t cancel_func,
265 apr_pool_t *result_pool,
266 apr_pool_t *scratch_pool)
268 *editor = apr_pcalloc(result_pool, sizeof(**editor));
270 (*editor)->baton = editor_baton;
271 (*editor)->cancel_func = cancel_func;
272 (*editor)->cancel_baton = cancel_baton;
273 (*editor)->scratch_pool = svn_pool_create(result_pool);
275 #ifdef ENABLE_ORDERING_CHECK
276 (*editor)->pending_incomplete_children = apr_hash_make(result_pool);
277 (*editor)->completed_nodes = apr_hash_make(result_pool);
278 (*editor)->finished = FALSE;
279 (*editor)->state_pool = result_pool;
287 svn_editor_get_baton(const svn_editor_t *editor)
289 return editor->baton;
294 svn_editor_setcb_add_directory(svn_editor_t *editor,
295 svn_editor_cb_add_directory_t callback,
296 apr_pool_t *scratch_pool)
298 editor->funcs.cb_add_directory = callback;
304 svn_editor_setcb_add_file(svn_editor_t *editor,
305 svn_editor_cb_add_file_t callback,
306 apr_pool_t *scratch_pool)
308 editor->funcs.cb_add_file = callback;
314 svn_editor_setcb_add_symlink(svn_editor_t *editor,
315 svn_editor_cb_add_symlink_t callback,
316 apr_pool_t *scratch_pool)
318 editor->funcs.cb_add_symlink = callback;
324 svn_editor_setcb_add_absent(svn_editor_t *editor,
325 svn_editor_cb_add_absent_t callback,
326 apr_pool_t *scratch_pool)
328 editor->funcs.cb_add_absent = callback;
334 svn_editor_setcb_alter_directory(svn_editor_t *editor,
335 svn_editor_cb_alter_directory_t callback,
336 apr_pool_t *scratch_pool)
338 editor->funcs.cb_alter_directory = callback;
344 svn_editor_setcb_alter_file(svn_editor_t *editor,
345 svn_editor_cb_alter_file_t callback,
346 apr_pool_t *scratch_pool)
348 editor->funcs.cb_alter_file = callback;
354 svn_editor_setcb_alter_symlink(svn_editor_t *editor,
355 svn_editor_cb_alter_symlink_t callback,
356 apr_pool_t *scratch_pool)
358 editor->funcs.cb_alter_symlink = callback;
364 svn_editor_setcb_delete(svn_editor_t *editor,
365 svn_editor_cb_delete_t callback,
366 apr_pool_t *scratch_pool)
368 editor->funcs.cb_delete = callback;
374 svn_editor_setcb_copy(svn_editor_t *editor,
375 svn_editor_cb_copy_t callback,
376 apr_pool_t *scratch_pool)
378 editor->funcs.cb_copy = callback;
384 svn_editor_setcb_move(svn_editor_t *editor,
385 svn_editor_cb_move_t callback,
386 apr_pool_t *scratch_pool)
388 editor->funcs.cb_move = callback;
394 svn_editor_setcb_complete(svn_editor_t *editor,
395 svn_editor_cb_complete_t callback,
396 apr_pool_t *scratch_pool)
398 editor->funcs.cb_complete = callback;
404 svn_editor_setcb_abort(svn_editor_t *editor,
405 svn_editor_cb_abort_t callback,
406 apr_pool_t *scratch_pool)
408 editor->funcs.cb_abort = callback;
414 svn_editor_setcb_many(svn_editor_t *editor,
415 const svn_editor_cb_many_t *many,
416 apr_pool_t *scratch_pool)
418 #define COPY_CALLBACK(NAME) if (many->NAME) editor->funcs.NAME = many->NAME
420 COPY_CALLBACK(cb_add_directory);
421 COPY_CALLBACK(cb_add_file);
422 COPY_CALLBACK(cb_add_symlink);
423 COPY_CALLBACK(cb_add_absent);
424 COPY_CALLBACK(cb_alter_directory);
425 COPY_CALLBACK(cb_alter_file);
426 COPY_CALLBACK(cb_alter_symlink);
427 COPY_CALLBACK(cb_delete);
428 COPY_CALLBACK(cb_copy);
429 COPY_CALLBACK(cb_move);
430 COPY_CALLBACK(cb_complete);
431 COPY_CALLBACK(cb_abort);
440 check_cancel(svn_editor_t *editor)
442 svn_error_t *err = NULL;
444 if (editor->cancel_func)
446 START_CALLBACK(editor);
447 err = editor->cancel_func(editor->cancel_baton);
448 END_CALLBACK(editor);
451 return svn_error_trace(err);
456 svn_editor_add_directory(svn_editor_t *editor,
458 const apr_array_header_t *children,
460 svn_revnum_t replaces_rev)
462 svn_error_t *err = SVN_NO_ERROR;
464 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
465 SVN_ERR_ASSERT(children != NULL);
466 SVN_ERR_ASSERT(props != NULL);
467 /* ### validate children are just basenames? */
468 SHOULD_NOT_BE_FINISHED(editor);
469 SHOULD_ALLOW_ADD(editor, relpath);
470 VERIFY_PARENT_MAY_EXIST(editor, relpath);
471 CHECK_UNKNOWN_CHILD(editor, relpath);
473 SVN_ERR(check_cancel(editor));
475 if (editor->funcs.cb_add_directory)
477 START_CALLBACK(editor);
478 err = editor->funcs.cb_add_directory(editor->baton, relpath, children,
480 editor->scratch_pool);
481 END_CALLBACK(editor);
484 MARK_ADDED_DIR(editor, relpath);
485 MARK_PARENT_STABLE(editor, relpath);
486 CLEAR_INCOMPLETE(editor, relpath);
488 #ifdef ENABLE_ORDERING_CHECK
491 for (i = 0; i < children->nelts; i++)
493 const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
494 const char *child = svn_relpath_join(relpath, child_basename,
497 svn_hash_sets(editor->pending_incomplete_children, child, "");
502 svn_pool_clear(editor->scratch_pool);
503 return svn_error_trace(err);
508 svn_editor_add_file(svn_editor_t *editor,
510 const svn_checksum_t *checksum,
511 svn_stream_t *contents,
513 svn_revnum_t replaces_rev)
515 svn_error_t *err = SVN_NO_ERROR;
517 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
518 SVN_ERR_ASSERT(checksum != NULL
519 && checksum->kind == SVN_EDITOR_CHECKSUM_KIND);
520 SVN_ERR_ASSERT(contents != NULL);
521 SVN_ERR_ASSERT(props != NULL);
522 SHOULD_NOT_BE_FINISHED(editor);
523 SHOULD_ALLOW_ADD(editor, relpath);
524 VERIFY_PARENT_MAY_EXIST(editor, relpath);
525 CHECK_UNKNOWN_CHILD(editor, relpath);
527 SVN_ERR(check_cancel(editor));
529 if (editor->funcs.cb_add_file)
531 START_CALLBACK(editor);
532 err = editor->funcs.cb_add_file(editor->baton, relpath,
533 checksum, contents, props,
534 replaces_rev, editor->scratch_pool);
535 END_CALLBACK(editor);
538 MARK_COMPLETED(editor, relpath);
539 MARK_PARENT_STABLE(editor, relpath);
540 CLEAR_INCOMPLETE(editor, relpath);
542 svn_pool_clear(editor->scratch_pool);
543 return svn_error_trace(err);
548 svn_editor_add_symlink(svn_editor_t *editor,
552 svn_revnum_t replaces_rev)
554 svn_error_t *err = SVN_NO_ERROR;
556 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
557 SVN_ERR_ASSERT(props != NULL);
558 SHOULD_NOT_BE_FINISHED(editor);
559 SHOULD_ALLOW_ADD(editor, relpath);
560 VERIFY_PARENT_MAY_EXIST(editor, relpath);
561 CHECK_UNKNOWN_CHILD(editor, relpath);
563 SVN_ERR(check_cancel(editor));
565 if (editor->funcs.cb_add_symlink)
567 START_CALLBACK(editor);
568 err = editor->funcs.cb_add_symlink(editor->baton, relpath, target, props,
569 replaces_rev, editor->scratch_pool);
570 END_CALLBACK(editor);
573 MARK_COMPLETED(editor, relpath);
574 MARK_PARENT_STABLE(editor, relpath);
575 CLEAR_INCOMPLETE(editor, relpath);
577 svn_pool_clear(editor->scratch_pool);
578 return svn_error_trace(err);
583 svn_editor_add_absent(svn_editor_t *editor,
585 svn_node_kind_t kind,
586 svn_revnum_t replaces_rev)
588 svn_error_t *err = SVN_NO_ERROR;
590 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
591 SHOULD_NOT_BE_FINISHED(editor);
592 SHOULD_ALLOW_ADD(editor, relpath);
593 VERIFY_PARENT_MAY_EXIST(editor, relpath);
594 CHECK_UNKNOWN_CHILD(editor, relpath);
596 SVN_ERR(check_cancel(editor));
598 if (editor->funcs.cb_add_absent)
600 START_CALLBACK(editor);
601 err = editor->funcs.cb_add_absent(editor->baton, relpath, kind,
602 replaces_rev, editor->scratch_pool);
603 END_CALLBACK(editor);
606 MARK_COMPLETED(editor, relpath);
607 MARK_PARENT_STABLE(editor, relpath);
608 CLEAR_INCOMPLETE(editor, relpath);
610 svn_pool_clear(editor->scratch_pool);
611 return svn_error_trace(err);
616 svn_editor_alter_directory(svn_editor_t *editor,
618 svn_revnum_t revision,
619 const apr_array_header_t *children,
622 svn_error_t *err = SVN_NO_ERROR;
624 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
625 SVN_ERR_ASSERT(children != NULL || props != NULL);
626 /* ### validate children are just basenames? */
627 SHOULD_NOT_BE_FINISHED(editor);
628 SHOULD_ALLOW_ALTER(editor, relpath);
629 VERIFY_PARENT_MAY_EXIST(editor, relpath);
631 SVN_ERR(check_cancel(editor));
633 if (editor->funcs.cb_alter_directory)
635 START_CALLBACK(editor);
636 err = editor->funcs.cb_alter_directory(editor->baton,
639 editor->scratch_pool);
640 END_CALLBACK(editor);
643 MARK_COMPLETED(editor, relpath);
644 MARK_PARENT_STABLE(editor, relpath);
646 #ifdef ENABLE_ORDERING_CHECK
647 /* ### this is not entirely correct. we probably need to adjust the
648 ### check_unknown_child() function for this scenario. */
652 for (i = 0; i < children->nelts; i++)
654 const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
655 const char *child = svn_relpath_join(relpath, child_basename,
658 apr_hash_set(editor->pending_incomplete_children, child,
659 APR_HASH_KEY_STRING, "");
660 /* Perhaps MARK_ALLOW_ADD(editor, child); ? */
666 svn_pool_clear(editor->scratch_pool);
667 return svn_error_trace(err);
672 svn_editor_alter_file(svn_editor_t *editor,
674 svn_revnum_t revision,
675 const svn_checksum_t *checksum,
676 svn_stream_t *contents,
679 svn_error_t *err = SVN_NO_ERROR;
681 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
682 SVN_ERR_ASSERT((checksum != NULL && contents != NULL)
683 || (checksum == NULL && contents == NULL));
684 SVN_ERR_ASSERT(props != NULL || checksum != NULL);
686 SVN_ERR_ASSERT(checksum->kind == SVN_EDITOR_CHECKSUM_KIND);
687 SHOULD_NOT_BE_FINISHED(editor);
688 SHOULD_ALLOW_ALTER(editor, relpath);
689 VERIFY_PARENT_MAY_EXIST(editor, relpath);
691 SVN_ERR(check_cancel(editor));
693 if (editor->funcs.cb_alter_file)
695 START_CALLBACK(editor);
696 err = editor->funcs.cb_alter_file(editor->baton,
698 checksum, contents, props,
699 editor->scratch_pool);
700 END_CALLBACK(editor);
703 MARK_COMPLETED(editor, relpath);
704 MARK_PARENT_STABLE(editor, relpath);
706 svn_pool_clear(editor->scratch_pool);
707 return svn_error_trace(err);
712 svn_editor_alter_symlink(svn_editor_t *editor,
714 svn_revnum_t revision,
718 svn_error_t *err = SVN_NO_ERROR;
720 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
721 SVN_ERR_ASSERT(props != NULL || target != NULL);
722 SHOULD_NOT_BE_FINISHED(editor);
723 SHOULD_ALLOW_ALTER(editor, relpath);
724 VERIFY_PARENT_MAY_EXIST(editor, relpath);
726 SVN_ERR(check_cancel(editor));
728 if (editor->funcs.cb_alter_symlink)
730 START_CALLBACK(editor);
731 err = editor->funcs.cb_alter_symlink(editor->baton,
734 editor->scratch_pool);
735 END_CALLBACK(editor);
738 MARK_COMPLETED(editor, relpath);
739 MARK_PARENT_STABLE(editor, relpath);
741 svn_pool_clear(editor->scratch_pool);
742 return svn_error_trace(err);
747 svn_editor_delete(svn_editor_t *editor,
749 svn_revnum_t revision)
751 svn_error_t *err = SVN_NO_ERROR;
753 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
754 SHOULD_NOT_BE_FINISHED(editor);
755 SHOULD_NOT_BE_COMPLETED(editor, relpath);
756 VERIFY_PARENT_MAY_EXIST(editor, relpath);
757 CHILD_DELETIONS_ALLOWED(editor, relpath);
759 SVN_ERR(check_cancel(editor));
761 if (editor->funcs.cb_delete)
763 START_CALLBACK(editor);
764 err = editor->funcs.cb_delete(editor->baton, relpath, revision,
765 editor->scratch_pool);
766 END_CALLBACK(editor);
769 MARK_COMPLETED(editor, relpath);
770 MARK_PARENT_STABLE(editor, relpath);
772 svn_pool_clear(editor->scratch_pool);
773 return svn_error_trace(err);
778 svn_editor_copy(svn_editor_t *editor,
779 const char *src_relpath,
780 svn_revnum_t src_revision,
781 const char *dst_relpath,
782 svn_revnum_t replaces_rev)
784 svn_error_t *err = SVN_NO_ERROR;
786 SVN_ERR_ASSERT(svn_relpath_is_canonical(src_relpath));
787 SVN_ERR_ASSERT(svn_relpath_is_canonical(dst_relpath));
788 SHOULD_NOT_BE_FINISHED(editor);
789 SHOULD_ALLOW_ADD(editor, dst_relpath);
790 VERIFY_PARENT_MAY_EXIST(editor, src_relpath);
791 VERIFY_PARENT_MAY_EXIST(editor, dst_relpath);
793 SVN_ERR(check_cancel(editor));
795 if (editor->funcs.cb_copy)
797 START_CALLBACK(editor);
798 err = editor->funcs.cb_copy(editor->baton, src_relpath, src_revision,
799 dst_relpath, replaces_rev,
800 editor->scratch_pool);
801 END_CALLBACK(editor);
804 MARK_ALLOW_ALTER(editor, dst_relpath);
805 MARK_PARENT_STABLE(editor, dst_relpath);
806 CLEAR_INCOMPLETE(editor, dst_relpath);
808 svn_pool_clear(editor->scratch_pool);
809 return svn_error_trace(err);
814 svn_editor_move(svn_editor_t *editor,
815 const char *src_relpath,
816 svn_revnum_t src_revision,
817 const char *dst_relpath,
818 svn_revnum_t replaces_rev)
820 svn_error_t *err = SVN_NO_ERROR;
822 SVN_ERR_ASSERT(svn_relpath_is_canonical(src_relpath));
823 SVN_ERR_ASSERT(svn_relpath_is_canonical(dst_relpath));
824 SHOULD_NOT_BE_FINISHED(editor);
825 SHOULD_NOT_BE_COMPLETED(editor, src_relpath);
826 SHOULD_ALLOW_ADD(editor, dst_relpath);
827 VERIFY_PARENT_MAY_EXIST(editor, src_relpath);
828 CHILD_DELETIONS_ALLOWED(editor, src_relpath);
829 VERIFY_PARENT_MAY_EXIST(editor, dst_relpath);
831 SVN_ERR(check_cancel(editor));
833 if (editor->funcs.cb_move)
835 START_CALLBACK(editor);
836 err = editor->funcs.cb_move(editor->baton, src_relpath, src_revision,
837 dst_relpath, replaces_rev,
838 editor->scratch_pool);
839 END_CALLBACK(editor);
842 MARK_ALLOW_ADD(editor, src_relpath);
843 MARK_PARENT_STABLE(editor, src_relpath);
844 MARK_ALLOW_ALTER(editor, dst_relpath);
845 MARK_PARENT_STABLE(editor, dst_relpath);
846 CLEAR_INCOMPLETE(editor, dst_relpath);
848 svn_pool_clear(editor->scratch_pool);
849 return svn_error_trace(err);
854 svn_editor_complete(svn_editor_t *editor)
856 svn_error_t *err = SVN_NO_ERROR;
858 SHOULD_NOT_BE_FINISHED(editor);
859 #ifdef ENABLE_ORDERING_CHECK
860 SVN_ERR_ASSERT(apr_hash_count(editor->pending_incomplete_children) == 0);
863 if (editor->funcs.cb_complete)
865 START_CALLBACK(editor);
866 err = editor->funcs.cb_complete(editor->baton, editor->scratch_pool);
867 END_CALLBACK(editor);
870 MARK_FINISHED(editor);
872 svn_pool_clear(editor->scratch_pool);
873 return svn_error_trace(err);
878 svn_editor_abort(svn_editor_t *editor)
880 svn_error_t *err = SVN_NO_ERROR;
882 SHOULD_NOT_BE_FINISHED(editor);
884 if (editor->funcs.cb_abort)
886 START_CALLBACK(editor);
887 err = editor->funcs.cb_abort(editor->baton, editor->scratch_pool);
888 END_CALLBACK(editor);
891 MARK_FINISHED(editor);
893 svn_pool_clear(editor->scratch_pool);
894 return svn_error_trace(err);