2 * revert.c: Handling of the in-wc side of the revert operation
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 * ====================================================================
29 #include <apr_pools.h>
30 #include <apr_tables.h>
32 #include "svn_types.h"
33 #include "svn_pools.h"
34 #include "svn_string.h"
35 #include "svn_error.h"
36 #include "svn_dirent_uri.h"
43 #include "adm_files.h"
44 #include "workqueue.h"
46 #include "svn_private_config.h"
47 #include "private/svn_io_private.h"
48 #include "private/svn_wc_private.h"
49 #include "private/svn_sorts_private.h"
51 /* Thoughts on Reversion.
53 What does is mean to revert a given PATH in a tree? We'll
54 consider things by their modifications.
58 - For files, svn_wc_remove_from_revision_control(), baby.
60 - Added directories may contain nothing but added children, and
61 reverting the addition of a directory necessarily means reverting
62 the addition of all the directory's children. Again,
63 svn_wc_remove_from_revision_control() should do the trick.
67 - Restore properties to their unmodified state.
69 - For files, restore the pristine contents, and reset the schedule
72 - For directories, reset the schedule to 'normal'. All children
73 of a directory marked for deletion must also be marked for
74 deletion, but it's okay for those children to remain deleted even
75 if their parent directory is restored. That's what the
76 recursive flag is for.
80 - Restore properties to their unmodified state.
82 - For files, restore the pristine contents, and reset the schedule
85 - For directories, reset the schedule to normal. A replaced
86 directory can have deleted children (left over from the initial
87 deletion), replaced children (children of the initial deletion
88 now re-added), and added children (new entries under the
89 replaced directory). Since this is technically an addition, it
90 necessitates recursion.
94 - Restore properties and, for files, contents to their unmodified
100 /* Remove conflict file CONFLICT_ABSPATH, which may not exist, and set
101 * *NOTIFY_REQUIRED to TRUE if the file was present and removed. */
103 remove_conflict_file(svn_boolean_t *notify_required,
104 const char *conflict_abspath,
105 const char *local_abspath,
106 apr_pool_t *scratch_pool)
108 if (conflict_abspath)
110 svn_error_t *err = svn_io_remove_file2(conflict_abspath, FALSE,
113 svn_error_clear(err);
115 *notify_required = TRUE;
122 /* Sort copied children obtained from the revert list based on
123 * their paths in descending order (longest paths first). */
125 compare_revert_list_copied_children(const void *a, const void *b)
127 const svn_wc__db_revert_list_copied_child_info_t * const *ca = a;
128 const svn_wc__db_revert_list_copied_child_info_t * const *cb = b;
131 i = svn_path_compare_paths(ca[0]->abspath, cb[0]->abspath);
133 /* Reverse the result of svn_path_compare_paths() to achieve
134 * descending order. */
139 /* Remove all reverted copied children from the directory at LOCAL_ABSPATH.
140 * If REMOVE_SELF is TRUE, try to remove LOCAL_ABSPATH itself (REMOVE_SELF
141 * should be set if LOCAL_ABSPATH is itself a reverted copy).
143 * If REMOVED_SELF is not NULL, indicate in *REMOVED_SELF whether
144 * LOCAL_ABSPATH itself was removed.
146 * All reverted copied file children are removed from disk. Reverted copied
147 * directories left empty as a result are also removed from disk.
150 revert_restore_handle_copied_dirs(svn_boolean_t *removed_self,
152 const char *local_abspath,
153 svn_boolean_t remove_self,
154 svn_cancel_func_t cancel_func,
156 apr_pool_t *scratch_pool)
158 apr_array_header_t *copied_children;
159 svn_wc__db_revert_list_copied_child_info_t *child_info;
161 svn_node_kind_t on_disk;
162 apr_pool_t *iterpool;
166 *removed_self = FALSE;
168 SVN_ERR(svn_wc__db_revert_list_read_copied_children(&copied_children,
172 iterpool = svn_pool_create(scratch_pool);
174 /* Remove all copied file children. */
175 for (i = 0; i < copied_children->nelts; i++)
177 child_info = APR_ARRAY_IDX(
179 svn_wc__db_revert_list_copied_child_info_t *);
182 SVN_ERR(cancel_func(cancel_baton));
184 if (child_info->kind != svn_node_file)
187 svn_pool_clear(iterpool);
189 /* Make sure what we delete from disk is really a file. */
190 SVN_ERR(svn_io_check_path(child_info->abspath, &on_disk, iterpool));
191 if (on_disk != svn_node_file)
194 SVN_ERR(svn_io_remove_file2(child_info->abspath, TRUE, iterpool));
197 /* Delete every empty child directory.
198 * We cannot delete children recursively since we want to keep any files
199 * that still exist on disk (e.g. unversioned files within the copied tree).
200 * So sort the children list such that longest paths come first and try to
201 * remove each child directory in order. */
202 svn_sort__array(copied_children, compare_revert_list_copied_children);
203 for (i = 0; i < copied_children->nelts; i++)
205 child_info = APR_ARRAY_IDX(
207 svn_wc__db_revert_list_copied_child_info_t *);
210 SVN_ERR(cancel_func(cancel_baton));
212 if (child_info->kind != svn_node_dir)
215 svn_pool_clear(iterpool);
217 err = svn_io_dir_remove_nonrecursive(child_info->abspath, iterpool);
220 if (APR_STATUS_IS_ENOENT(err->apr_err) ||
221 SVN__APR_STATUS_IS_ENOTDIR(err->apr_err) ||
222 APR_STATUS_IS_ENOTEMPTY(err->apr_err))
223 svn_error_clear(err);
225 return svn_error_trace(err);
231 /* Delete LOCAL_ABSPATH itself if no children are left. */
232 err = svn_io_dir_remove_nonrecursive(local_abspath, iterpool);
235 if (APR_STATUS_IS_ENOTEMPTY(err->apr_err))
236 svn_error_clear(err);
238 return svn_error_trace(err);
240 else if (removed_self)
241 *removed_self = TRUE;
244 svn_pool_destroy(iterpool);
249 /* Forward definition */
251 revert_wc_data(svn_boolean_t *run_wq,
252 svn_boolean_t *notify_required,
254 const char *local_abspath,
255 svn_wc__db_status_t status,
256 svn_node_kind_t kind,
257 svn_node_kind_t reverted_kind,
258 svn_filesize_t recorded_size,
259 apr_time_t recorded_time,
260 svn_boolean_t copied_here,
261 svn_boolean_t use_commit_times,
262 svn_cancel_func_t cancel_func,
264 apr_pool_t *scratch_pool);
266 /* Make the working tree under LOCAL_ABSPATH to depth DEPTH match the
267 versioned tree. This function is called after svn_wc__db_op_revert
268 has done the database revert and created the revert list. Notifies
269 for all paths equal to or below LOCAL_ABSPATH that are reverted.
271 REVERT_ROOT is true for explicit revert targets and FALSE for targets
272 reached via recursion.
274 Sets *RUN_WQ to TRUE when the caller should (eventually) run the workqueue.
275 (The function sets it to FALSE when it has run the WQ itself)
277 If INFO is NULL, LOCAL_ABSPATH doesn't exist in DB. Otherwise INFO
278 specifies the state of LOCAL_ABSPATH in DB.
281 revert_restore(svn_boolean_t *run_wq,
283 const char *local_abspath,
285 svn_boolean_t metadata_only,
286 svn_boolean_t use_commit_times,
287 svn_boolean_t revert_root,
288 const struct svn_wc__db_info_t *info,
289 svn_cancel_func_t cancel_func,
291 svn_wc_notify_func2_t notify_func,
293 apr_pool_t *scratch_pool)
295 svn_wc__db_status_t status;
296 svn_node_kind_t kind;
297 svn_boolean_t notify_required;
298 const apr_array_header_t *conflict_files;
299 svn_filesize_t recorded_size;
300 apr_time_t recorded_time;
301 svn_boolean_t copied_here;
302 svn_node_kind_t reverted_kind;
304 SVN_ERR(cancel_func(cancel_baton));
308 svn_boolean_t is_wcroot;
310 SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, local_abspath, scratch_pool));
313 /* Issue #4162: Obstructing working copy. We can't access the working
314 copy data from the parent working copy for this node by just using
319 svn_wc_notify_t *notify =
320 svn_wc_create_notify(
322 svn_wc_notify_update_skip_obstruction,
325 notify_func(notify_baton, notify, scratch_pool);
328 return SVN_NO_ERROR; /* We don't revert obstructing working copies */
332 SVN_ERR(svn_wc__db_revert_list_read(¬ify_required,
334 &copied_here, &reverted_kind,
336 scratch_pool, scratch_pool));
340 status = info->status;
342 recorded_size = info->recorded_size;
343 recorded_time = info->recorded_time;
349 if (notify_func && notify_required)
350 notify_func(notify_baton,
351 svn_wc_create_notify(local_abspath,
352 svn_wc_notify_revert,
357 SVN_ERR(svn_wc__db_revert_list_notify(notify_func, notify_baton,
364 /* ### Initialise to values which prevent the code below from
365 * ### trying to restore anything to disk.
366 * ### 'status' should be status_unknown but that doesn't exist. */
367 status = svn_wc__db_status_normal;
368 kind = svn_node_unknown;
369 recorded_size = SVN_INVALID_FILESIZE;
376 SVN_ERR(revert_wc_data(run_wq,
378 db, local_abspath, status, kind,
379 reverted_kind, recorded_size, recorded_time,
380 copied_here, use_commit_times,
381 cancel_func, cancel_baton, scratch_pool));
384 /* We delete these marker files even though they are not strictly metadata.
385 But for users that use revert as an API with metadata_only, these are. */
389 for (i = 0; i < conflict_files->nelts; i++)
391 SVN_ERR(remove_conflict_file(¬ify_required,
392 APR_ARRAY_IDX(conflict_files, i,
394 local_abspath, scratch_pool));
398 if (notify_func && notify_required)
399 notify_func(notify_baton,
400 svn_wc_create_notify(local_abspath, svn_wc_notify_revert,
404 if (depth == svn_depth_infinity && kind == svn_node_dir)
406 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
407 apr_hash_t *children, *conflicts;
408 apr_hash_index_t *hi;
410 SVN_ERR(revert_restore_handle_copied_dirs(NULL, db, local_abspath, FALSE,
411 cancel_func, cancel_baton,
414 SVN_ERR(svn_wc__db_read_children_info(&children, &conflicts,
415 db, local_abspath, FALSE,
416 scratch_pool, iterpool));
418 for (hi = apr_hash_first(scratch_pool, children);
420 hi = apr_hash_next(hi))
422 const char *child_name = apr_hash_this_key(hi);
423 const char *child_abspath;
425 svn_pool_clear(iterpool);
427 child_abspath = svn_dirent_join(local_abspath, child_name, iterpool);
429 SVN_ERR(revert_restore(run_wq,
430 db, child_abspath, depth, metadata_only,
431 use_commit_times, FALSE /* revert root */,
432 apr_hash_this_val(hi),
433 cancel_func, cancel_baton,
434 notify_func, notify_baton,
438 /* Run the queue per directory */
441 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
446 svn_pool_destroy(iterpool);
449 if (notify_func && (revert_root || kind == svn_node_dir))
450 SVN_ERR(svn_wc__db_revert_list_notify(notify_func, notify_baton,
451 db, local_abspath, scratch_pool));
456 /* Perform the in-working copy revert of LOCAL_ABSPATH, to what is stored in DB */
458 revert_wc_data(svn_boolean_t *run_wq,
459 svn_boolean_t *notify_required,
461 const char *local_abspath,
462 svn_wc__db_status_t status,
463 svn_node_kind_t kind,
464 svn_node_kind_t reverted_kind,
465 svn_filesize_t recorded_size,
466 apr_time_t recorded_time,
467 svn_boolean_t copied_here,
468 svn_boolean_t use_commit_times,
469 svn_cancel_func_t cancel_func,
471 apr_pool_t *scratch_pool)
475 svn_node_kind_t on_disk;
477 svn_boolean_t special;
480 /* Would be nice to use svn_io_dirent2_t here, but the performance
481 improvement that provides doesn't work, because we need the read
482 only and executable bits later on, in the most likely code path */
483 err = svn_io_stat(&finfo, local_abspath,
484 APR_FINFO_TYPE | APR_FINFO_LINK
485 | APR_FINFO_SIZE | APR_FINFO_MTIME
486 | SVN__APR_FINFO_EXECUTABLE
487 | SVN__APR_FINFO_READONLY,
490 if (err && (APR_STATUS_IS_ENOENT(err->apr_err)
491 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
493 svn_error_clear(err);
494 on_disk = svn_node_none;
501 if (finfo.filetype == APR_REG || finfo.filetype == APR_LNK)
502 on_disk = svn_node_file;
503 else if (finfo.filetype == APR_DIR)
504 on_disk = svn_node_dir;
506 on_disk = svn_node_unknown;
509 special = (finfo.filetype == APR_LNK);
513 return svn_error_trace(err);
517 /* The revert target itself is the op-root of a copy. */
518 if (reverted_kind == svn_node_file && on_disk == svn_node_file)
520 SVN_ERR(svn_io_remove_file2(local_abspath, TRUE, scratch_pool));
521 on_disk = svn_node_none;
523 else if (reverted_kind == svn_node_dir && on_disk == svn_node_dir)
525 svn_boolean_t removed;
527 SVN_ERR(revert_restore_handle_copied_dirs(&removed, db,
529 cancel_func, cancel_baton,
532 on_disk = svn_node_none;
536 /* If we expect a versioned item to be present then check that any
537 item on disk matches the versioned item, if it doesn't match then
538 fix it or delete it. */
539 if (on_disk != svn_node_none
540 && status != svn_wc__db_status_server_excluded
541 && status != svn_wc__db_status_deleted
542 && status != svn_wc__db_status_excluded
543 && status != svn_wc__db_status_not_present)
545 if (on_disk == svn_node_dir && kind != svn_node_dir)
547 SVN_ERR(svn_io_remove_dir2(local_abspath, FALSE,
548 cancel_func, cancel_baton, scratch_pool));
549 on_disk = svn_node_none;
551 else if (on_disk == svn_node_file && kind != svn_node_file)
554 /* Preserve symlinks pointing at directories. Changes on the
555 * directory node have been reverted. The symlink should remain. */
556 if (!(special && kind == svn_node_dir))
559 SVN_ERR(svn_io_remove_file2(local_abspath, FALSE, scratch_pool));
560 on_disk = svn_node_none;
563 else if (on_disk == svn_node_file)
565 svn_boolean_t modified;
568 svn_string_t *special_prop;
571 SVN_ERR(svn_wc__db_read_pristine_props(&props, db, local_abspath,
572 scratch_pool, scratch_pool));
575 special_prop = svn_hash_gets(props, SVN_PROP_SPECIAL);
577 if ((special_prop != NULL) != special)
579 /* File/symlink mismatch. */
580 SVN_ERR(svn_io_remove_file2(local_abspath, FALSE, scratch_pool));
581 on_disk = svn_node_none;
586 /* Issue #1663 asserts that we should compare a file in its
587 working copy format here, but before r1101473 we would only
588 do that if the file was already unequal to its recorded
591 r1101473 removes the option of asking for a working format
592 compare but *also* check the recorded information first, as
593 that combination doesn't guarantee a stable behavior.
594 (See the revert_test.py: revert_reexpand_keyword)
596 But to have the same issue #1663 behavior for revert as we
597 had in <=1.6 we only have to check the recorded information
598 ourselves. And we already have everything we need, because
599 we called stat ourselves. */
600 if (recorded_size != SVN_INVALID_FILESIZE
601 && recorded_time != 0
602 && recorded_size == finfo.size
603 && recorded_time == finfo.mtime)
608 /* Side effect: fixes recorded timestamps */
609 SVN_ERR(svn_wc__internal_file_modified_p(&modified,
611 TRUE, scratch_pool));
615 /* Install will replace the file */
616 on_disk = svn_node_none;
620 if (status == svn_wc__db_status_normal)
622 svn_boolean_t read_only;
623 svn_string_t *needs_lock_prop;
625 SVN_ERR(svn_io__is_finfo_read_only(&read_only, &finfo,
628 needs_lock_prop = svn_hash_gets(props,
629 SVN_PROP_NEEDS_LOCK);
630 if (needs_lock_prop && !read_only)
632 SVN_ERR(svn_io_set_file_read_only(local_abspath,
635 *notify_required = TRUE;
637 else if (!needs_lock_prop && read_only)
639 SVN_ERR(svn_io_set_file_read_write(local_abspath,
642 *notify_required = TRUE;
646 #if !defined(WIN32) && !defined(__OS2__)
651 svn_boolean_t executable;
652 svn_string_t *executable_prop;
654 SVN_ERR(svn_io__is_finfo_executable(&executable, &finfo,
656 executable_prop = svn_hash_gets(props,
657 SVN_PROP_EXECUTABLE);
658 if (executable_prop && !executable)
660 SVN_ERR(svn_io_set_file_executable(local_abspath,
663 *notify_required = TRUE;
665 else if (!executable_prop && executable)
667 SVN_ERR(svn_io_set_file_executable(local_abspath,
670 *notify_required = TRUE;
679 /* If we expect a versioned item to be present and there is nothing
680 on disk then recreate it. */
681 if (on_disk == svn_node_none
682 && status != svn_wc__db_status_server_excluded
683 && status != svn_wc__db_status_deleted
684 && status != svn_wc__db_status_excluded
685 && status != svn_wc__db_status_not_present)
687 if (kind == svn_node_dir)
688 SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT, scratch_pool));
690 if (kind == svn_node_file)
692 svn_skel_t *work_item;
694 SVN_ERR(svn_wc__wq_build_file_install(&work_item, db, local_abspath,
695 NULL, use_commit_times, TRUE,
696 scratch_pool, scratch_pool));
697 SVN_ERR(svn_wc__db_wq_add(db, local_abspath, work_item,
701 *notify_required = TRUE;
707 /* Revert tree LOCAL_ABSPATH to depth DEPTH and notify for all reverts. */
709 revert(svn_wc__db_t *db,
710 const char *local_abspath,
712 svn_boolean_t use_commit_times,
713 svn_boolean_t clear_changelists,
714 svn_boolean_t metadata_only,
715 svn_cancel_func_t cancel_func,
717 svn_wc_notify_func2_t notify_func,
719 apr_pool_t *scratch_pool)
722 const struct svn_wc__db_info_t *info = NULL;
723 svn_boolean_t run_queue = FALSE;
725 SVN_ERR_ASSERT(depth == svn_depth_empty || depth == svn_depth_infinity);
727 /* We should have a write lock on the parent of local_abspath, except
728 when local_abspath is the working copy root. */
730 const char *dir_abspath;
731 svn_boolean_t is_wcroot;
733 SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, local_abspath, scratch_pool));
736 dir_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
738 dir_abspath = local_abspath;
740 SVN_ERR(svn_wc__write_check(db, dir_abspath, scratch_pool));
743 err = svn_error_trace(
744 svn_wc__db_op_revert(db, local_abspath, depth, clear_changelists,
745 scratch_pool, scratch_pool));
749 err = svn_error_trace(
750 svn_wc__db_read_single_info(&info, db, local_abspath, FALSE,
751 scratch_pool, scratch_pool));
753 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
755 svn_error_clear(err);
762 err = svn_error_trace(
763 revert_restore(&run_queue, db, local_abspath, depth, metadata_only,
764 use_commit_times, TRUE /* revert root */,
765 info, cancel_func, cancel_baton,
766 notify_func, notify_baton,
770 err = svn_error_compose_create(err,
771 svn_wc__wq_run(db, local_abspath,
772 cancel_func, cancel_baton,
775 err = svn_error_compose_create(err,
776 svn_wc__db_revert_list_done(db,
784 /* Revert files in LOCAL_ABSPATH to depth DEPTH that match
785 CHANGELIST_HASH and notify for all reverts. */
787 revert_changelist(svn_wc__db_t *db,
788 const char *local_abspath,
790 svn_boolean_t use_commit_times,
791 apr_hash_t *changelist_hash,
792 svn_boolean_t clear_changelists,
793 svn_boolean_t metadata_only,
794 svn_cancel_func_t cancel_func,
796 svn_wc_notify_func2_t notify_func,
798 apr_pool_t *scratch_pool)
800 apr_pool_t *iterpool;
801 const apr_array_header_t *children;
805 SVN_ERR(cancel_func(cancel_baton));
807 /* Revert this node (depth=empty) if it matches one of the changelists. */
808 if (svn_wc__internal_changelist_match(db, local_abspath, changelist_hash,
810 SVN_ERR(revert(db, local_abspath,
811 svn_depth_empty, use_commit_times, clear_changelists,
813 cancel_func, cancel_baton,
814 notify_func, notify_baton,
817 if (depth == svn_depth_empty)
820 iterpool = svn_pool_create(scratch_pool);
822 /* We can handle both depth=files and depth=immediates by setting
823 depth=empty here. We don't need to distinguish files and
824 directories when making the recursive call because directories
825 can never match a changelist, so making the recursive call for
826 directories when asked for depth=files is a no-op. */
827 if (depth == svn_depth_files || depth == svn_depth_immediates)
828 depth = svn_depth_empty;
830 SVN_ERR(svn_wc__db_read_children_of_working_node(&children, db,
834 for (i = 0; i < children->nelts; ++i)
836 const char *child_abspath;
838 svn_pool_clear(iterpool);
840 child_abspath = svn_dirent_join(local_abspath,
841 APR_ARRAY_IDX(children, i,
845 SVN_ERR(revert_changelist(db, child_abspath, depth,
846 use_commit_times, changelist_hash,
847 clear_changelists, metadata_only,
848 cancel_func, cancel_baton,
849 notify_func, notify_baton,
853 svn_pool_destroy(iterpool);
859 /* Does a partially recursive revert of LOCAL_ABSPATH to depth DEPTH
860 (which must be either svn_depth_files or svn_depth_immediates) by
861 doing a non-recursive revert on each permissible path. Notifies
864 ### This won't revert a copied dir with one level of children since
865 ### the non-recursive revert on the dir will fail. Not sure how a
866 ### partially recursive revert should handle actual-only nodes. */
868 revert_partial(svn_wc__db_t *db,
869 const char *local_abspath,
871 svn_boolean_t use_commit_times,
872 svn_boolean_t clear_changelists,
873 svn_boolean_t metadata_only,
874 svn_cancel_func_t cancel_func,
876 svn_wc_notify_func2_t notify_func,
878 apr_pool_t *scratch_pool)
880 apr_pool_t *iterpool;
881 const apr_array_header_t *children;
884 SVN_ERR_ASSERT(depth == svn_depth_files || depth == svn_depth_immediates);
887 SVN_ERR(cancel_func(cancel_baton));
889 iterpool = svn_pool_create(scratch_pool);
891 /* Revert the root node itself (depth=empty), then move on to the
893 SVN_ERR(revert(db, local_abspath, svn_depth_empty,
894 use_commit_times, clear_changelists, metadata_only,
895 cancel_func, cancel_baton,
896 notify_func, notify_baton, iterpool));
898 SVN_ERR(svn_wc__db_read_children_of_working_node(&children, db,
902 for (i = 0; i < children->nelts; ++i)
904 const char *child_abspath;
906 svn_pool_clear(iterpool);
908 child_abspath = svn_dirent_join(local_abspath,
909 APR_ARRAY_IDX(children, i, const char *),
912 /* For svn_depth_files: don't revert non-files. */
913 if (depth == svn_depth_files)
915 svn_node_kind_t kind;
917 SVN_ERR(svn_wc__db_read_kind(&kind, db, child_abspath,
918 FALSE /* allow_missing */,
919 TRUE /* show_deleted */,
920 FALSE /* show_hidden */,
922 if (kind != svn_node_file)
926 /* Revert just this node (depth=empty). */
927 SVN_ERR(revert(db, child_abspath,
928 svn_depth_empty, use_commit_times, clear_changelists,
930 cancel_func, cancel_baton,
931 notify_func, notify_baton,
935 svn_pool_destroy(iterpool);
942 svn_wc_revert5(svn_wc_context_t *wc_ctx,
943 const char *local_abspath,
945 svn_boolean_t use_commit_times,
946 const apr_array_header_t *changelist_filter,
947 svn_boolean_t clear_changelists,
948 svn_boolean_t metadata_only,
949 svn_cancel_func_t cancel_func,
951 svn_wc_notify_func2_t notify_func,
953 apr_pool_t *scratch_pool)
955 if (changelist_filter && changelist_filter->nelts)
957 apr_hash_t *changelist_hash;
959 SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter,
961 return svn_error_trace(revert_changelist(wc_ctx->db, local_abspath,
962 depth, use_commit_times,
966 cancel_func, cancel_baton,
967 notify_func, notify_baton,
971 if (depth == svn_depth_empty || depth == svn_depth_infinity)
972 return svn_error_trace(revert(wc_ctx->db, local_abspath,
973 depth, use_commit_times, clear_changelists,
975 cancel_func, cancel_baton,
976 notify_func, notify_baton,
979 /* The user may expect svn_depth_files/svn_depth_immediates to work
980 on copied dirs with one level of children. It doesn't, the user
981 will get an error and will need to invoke an infinite revert. If
982 we identified those cases where svn_depth_infinity would not
983 revert too much we could invoke the recursive call above. */
985 if (depth == svn_depth_files || depth == svn_depth_immediates)
986 return svn_error_trace(revert_partial(wc_ctx->db, local_abspath,
987 depth, use_commit_times,
988 clear_changelists, metadata_only,
989 cancel_func, cancel_baton,
990 notify_func, notify_baton,
993 /* Bogus depth. Tell the caller. */
994 return svn_error_create(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL, NULL);