Implement a WC entries walker like svn_wc_walk_entries3() but that also visits unversioned tree conflict victims. ### Not yet working. ### Private API; should be public. ### Some preliminary testing/debugging code is present in adm_ops.c. * subversion/libsvn_wc/wc.h (svn_wc__walk_entries_and_tc): New function. * subversion/libsvn_wc/entries.c (visit_tc_too_baton_t): New type. (visit_tc_too_callbacks): New constant. (visit_tc_too_found_entry, visit_tc_too_error_handler, svn_wc__walk_entries_and_tc): New functions. * subversion/libsvn_wc/adm_ops.c (revert_walk_baton_t): New baton. ### NOT USED (svn_wc_revert3): Start to use the new walker. ### NOT REACHED (resolve_found_entry_callback): Look for null/deleted/absent/schedule- delete entry, and debug-print if found, to see if the new walker is working. (svn_wc_resolved_conflict4): Call the new walker instead of the old one. Index: subversion/libsvn_wc/wc.h =================================================================== --- subversion/libsvn_wc/wc.h (revision 34070) +++ subversion/libsvn_wc/wc.h (working copy) @@ -312,6 +312,21 @@ svn_wc__ambient_depth_filter_editor(cons svn_wc_adm_access_t *adm_access, apr_pool_t *pool); +/* Similar to svn_wc_walk_entries3(), but also visit unversioned paths that + * are tree conflict victims. For such a path, call the "found_entry" + * callback but with a null "entry" parameter. Walk all entries including + * hidden and schedule-delete entries, like with "show_hidden = TRUE" in + * svn_wc_walk_entries3(). */ +svn_error_t * +svn_wc__walk_entries_and_tc(const char *path, + svn_wc_adm_access_t *adm_access, + const svn_wc_entry_callbacks2_t *walk_callbacks, + void *walk_baton, + svn_depth_t depth, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool); + #ifdef __cplusplus } #endif /* __cplusplus */ Index: subversion/libsvn_wc/entries.c =================================================================== --- subversion/libsvn_wc/entries.c (revision 34070) +++ subversion/libsvn_wc/entries.c (working copy) @@ -34,6 +34,7 @@ #include "adm_ops.h" #include "entries.h" #include "lock.h" +#include "tree_conflicts.h" #include "svn_private_config.h" #include "private/svn_wc_private.h" @@ -3265,6 +3266,149 @@ svn_wc_walk_entries3(const char *path, } +/* A baton for use with visit_tc_too_callbacks. */ +typedef struct visit_tc_too_baton_t + { + svn_wc_adm_access_t *adm_access; + const svn_wc_entry_callbacks2_t *callbacks; + void *baton; + } visit_tc_too_baton_t; + +/* An svn_wc_entry_callbacks2_t callback function. + * + * Call the user's "found entry" callback + * WALK_BATON->callbacks->found_entry(), passing it PATH, ENTRY and + * WALK_BATON->baton. Then call it once for each unversioned tree-conflicted + * child of this entry, passing it the child path, a null "entry", and + * WALK_BATON->baton. WALK_BATON is of type (visit_tc_too_baton_t *). + */ +static svn_error_t * +visit_tc_too_found_entry(const char *path, + const svn_wc_entry_t *entry, + void *walk_baton, + apr_pool_t *pool) +{ + struct visit_tc_too_baton_t *baton = walk_baton; + + /* Call the entry callback for this entry. */ + SVN_ERR(baton->callbacks->found_entry(path, entry, baton->baton, pool)); + + /* If this is a directory, also visit any unversioned children that are + * tree conflict victims. */ + if (entry->kind == svn_node_dir && !entry->deleted && !entry->absent) + { + svn_wc_adm_access_t *adm_access; + apr_array_header_t *conflicts + = apr_array_make(pool, 0, sizeof(svn_wc_conflict_description_t *)); + int i; + + SVN_ERR(svn_wc_adm_retrieve(&adm_access, baton->adm_access, path, + pool)); + + /* Loop through all the tree conflict victims */ + SVN_ERR(svn_wc__read_tree_conflicts_from_entry(conflicts, entry, + path, pool)); + for (i = 0; i < conflicts->nelts; i++) + { + svn_wc_conflict_description_t *conflict + = APR_ARRAY_IDX(conflicts, i, svn_wc_conflict_description_t *); + const svn_wc_entry_t *child_entry; + + /* If this victim is not in this dir's entries ... */ + SVN_ERR(svn_wc_entry(&child_entry, conflict->path, adm_access, + TRUE, pool)); + if (!child_entry) + { + /* Found an unversioned tree conflict victim. Call the "found + * entry" callback with a null "entry" parameter. */ + SVN_ERR(baton->callbacks->found_entry(conflict->path, NULL, + baton->baton, pool)); + } + } + } + + return SVN_NO_ERROR; +} + +/* An svn_wc_entry_callbacks2_t callback function. + * + * If the error ERR is because this PATH is an unversioned tree conflict + * victim, call the user's "found entry" callback + * WALK_BATON->callbacks->found_entry(), passing it this PATH, a null + * "entry" parameter, and WALK_BATON->baton. Otherwise, forward this call + * to the user's "handle error" callback + * WALK_BATON->callbacks->handle_error(). + */ +static svn_error_t * +visit_tc_too_error_handler(const char *path, + svn_error_t *err, + void *walk_baton, + apr_pool_t *pool) +{ + struct visit_tc_too_baton_t *baton = walk_baton; + + /* First, if this is an unversioned tree conflict victim, call the + * "found entry" callback for a null entry. + * The "unversioned resource" error occurs when the root of the walk is + * an unversioned resource; it can't occur for nodes recursed into. */ + if (err && (err->apr_err == SVN_ERR_UNVERSIONED_RESOURCE)) + { + svn_wc_adm_access_t *adm_access; + svn_wc_conflict_description_t *conflict; + + /* See if there is any tree conflict on this path. */ + SVN_ERR(svn_wc_adm_retrieve(&adm_access, baton->adm_access, path, + pool)); + SVN_ERR(svn_wc_get_tree_conflict(&conflict, path, adm_access, pool)); + + /* If so, don't regard it as an error but call the "found entry" + * callback with a null "entry" parameter. */ + if (conflict) + { + svn_error_clear(err); + err = NULL; + + SVN_ERR(baton->callbacks->found_entry(conflict->path, NULL, + baton->baton, pool)); + } + } + + /* Call the user's error handler for this entry. */ + return baton->callbacks->handle_error(path, err, baton->baton, pool); +} + +/* Callbacks used by svn_wc_walk_entries_and_tc(). */ +static const svn_wc_entry_callbacks2_t +visit_tc_too_callbacks = + { + visit_tc_too_found_entry, + visit_tc_too_error_handler + }; + +svn_error_t * +svn_wc__walk_entries_and_tc(const char *path, + svn_wc_adm_access_t *adm_access, + const svn_wc_entry_callbacks2_t *walk_callbacks, + void *walk_baton, + svn_depth_t depth, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool) +{ + visit_tc_too_baton_t visit_tc_too_baton; + + visit_tc_too_baton.adm_access = adm_access; + visit_tc_too_baton.callbacks = walk_callbacks; + visit_tc_too_baton.baton = walk_baton; + + SVN_ERR(svn_wc_walk_entries3(path, adm_access, + &visit_tc_too_callbacks, &visit_tc_too_baton, + depth, TRUE /*show_hidden*/, + cancel_func, cancel_baton, pool)); + return SVN_NO_ERROR; +} + + svn_error_t * svn_wc_mark_missing_deleted(const char *path, svn_wc_adm_access_t *parent, Index: subversion/libsvn_wc/adm_ops.c =================================================================== --- subversion/libsvn_wc/adm_ops.c (revision 34070) +++ subversion/libsvn_wc/adm_ops.c (working copy) @@ -2316,6 +2316,14 @@ revert_internal(const char *path, return SVN_NO_ERROR; } +typedef struct revert_walk_baton_t +{ + svn_boolean_t use_commit_times; + apr_hash_t *changelist_hash; + svn_wc_notify_func2_t notify_func; + void *notify_baton; +} revert_walk_baton_t; + svn_error_t * svn_wc_revert3(const char *path, @@ -2335,6 +2343,19 @@ svn_wc_revert3(const char *path, return revert_internal(path, parent_access, depth, use_commit_times, changelist_hash, cancel_func, cancel_baton, notify_func, notify_baton, pool); + { + svn_wc_entry_callbacks2_t walk_callbacks = + { NULL, svn_wc__walker_default_error_handler }; + revert_walk_baton_t walk_baton; + + walk_baton.use_commit_times = use_commit_times; + walk_baton.changelist_hash = changelist_hash; + walk_baton.notify_func = notify_func; + walk_baton.notify_baton = notify_baton; + return svn_wc__walk_entries_and_tc(path, parent_access, + &walk_callbacks, &walk_baton, depth, + cancel_func, cancel_baton, pool); + } } @@ -2851,6 +2872,26 @@ resolve_found_entry_callback(const char const char *conflict_dir, *base_name = NULL; svn_wc_adm_access_t *adm_access; + if (!entry) + { + printf("### Unversioned... woo-hoo! resolve_f_e_cb('%s')\n", path); + return SVN_NO_ERROR; + } + if (entry->deleted) + { + printf("### Deleted: resolve_f_e_cb('%s')\n", path); + return SVN_NO_ERROR; + } + if (entry->absent) + { + printf("### Absent: resolve_f_e_cb('%s')\n", path); + return SVN_NO_ERROR; + } + if (entry->schedule == svn_wc_schedule_delete) + { + printf("### Sched-delete: resolve_f_e_cb('%s')\n", path); + return SVN_NO_ERROR; /* ### ah, but from now on we want to resolve it anyway */ + } /* We're going to receive dirents twice; we want to ignore the first one (where it's a child of a parent dir), and only print the second one (where we're looking at THIS_DIR). */ @@ -2906,9 +2947,14 @@ svn_wc_resolved_conflict4(const char *pa baton->notify_baton = notify_baton; baton->conflict_choice = conflict_choice; + /* return svn_wc_walk_entries3(path, adm_access, &resolve_walk_callbacks, baton, depth, FALSE, cancel_func, cancel_baton, pool); + */ + return svn_wc__walk_entries_and_tc(path, adm_access, + &resolve_walk_callbacks, baton, depth, + cancel_func, cancel_baton, pool); } svn_error_t *svn_wc_add_lock(const char *path, const svn_lock_t *lock,