Make "resolved" handle tree conflicts per victim rather than per parent. ### update_tests.py 15 fails ### tree_conflict_tests.py all tests fail * subversion/libsvn_wc/adm_ops.c (resolve_conflict_on_entry): Do not clear tree conflicts that are recorded in this entry. Do not send a notification, but instead report (through a new boolean output parameter) whether something was resolved. (resolve_found_entry_callback): Resolve any tree conflict for this victim. Send a notification if any conflict was resolved. (svn_wc_resolved_conflict4): Use an entries-walker function that visits unversioned tree conflict victims as well as ordinary entries. * subversion/tests/cmdline/log_tests.py (merge_history_repos): Resolve the victim, not the parent. * subversion/tests/cmdline/tree_conflict_tests.py (ensure_tree_conflict): Resolve the victim, not the parent. * subversion/tests/cmdline/update_tests.py (another_hudson_problem): Resolve the victim, not the parent. --This line, and those below, will be ignored-- Index: subversion/libsvn_wc/adm_ops.c =================================================================== --- subversion/libsvn_wc/adm_ops.c (revision 34116) +++ subversion/libsvn_wc/adm_ops.c (working copy) @@ -2676,11 +2676,16 @@ attempt_deletion(const char *parent_dir, and clearing the conflict filenames from the entry. The latter needs to be done whether or not the conflict files exist. + Tree conflicts are not resolved here, because the data stored in one + entry does not refer to that entry but to children of it. + PATH is the path to the item to be resolved, BASE_NAME is the basename of PATH, and CONFLICT_DIR is the access baton for PATH. ORIG_ENTRY is - the entry prior to resolution. RESOLVE_TEXT, RESOLVE_PROPS and - RESOLVE_TREE are TRUE if text, property and tree conflicts respectively - are to be resolved. + the entry prior to resolution. RESOLVE_TEXT and RESOLVE_PROPS are TRUE + if text and property conflicts respectively are to be resolved. + + If this call marks any conflict as resolved, set *DID_RESOLVE to true, + else do not change *DID_RESOLVE. See svn_wc_resolved_conflict3() for how CONFLICT_CHOICE behaves. @@ -2696,10 +2701,8 @@ resolve_conflict_on_entry(const char *pa const char *base_name, svn_boolean_t resolve_text, svn_boolean_t resolve_props, - svn_boolean_t resolve_tree, svn_wc_conflict_choice_t conflict_choice, - svn_wc_notify_func2_t notify_func, - void *notify_baton, + svn_boolean_t *did_resolve, apr_pool_t *pool) { svn_boolean_t was_present, need_feedback = FALSE; @@ -2812,14 +2815,6 @@ resolve_conflict_on_entry(const char *pa need_feedback |= was_present; } - if (resolve_tree && (entry->kind == svn_node_dir) - && entry->tree_conflict_data) - { - modify_flags |= SVN_WC__ENTRY_MODIFY_TREE_CONFLICT_DATA; - entry->tree_conflict_data = NULL; - need_feedback = TRUE; - } - if (modify_flags) { /* Although removing the files is sufficient to indicate that the @@ -2832,22 +2827,8 @@ resolve_conflict_on_entry(const char *pa /* No feedback if no files were deleted and all we did was change the entry, such a file did not appear as a conflict */ - if (need_feedback && notify_func) - { - /* Sanity check: see if libsvn_wc *still* thinks this item is in a - state of conflict that we have asked to resolve. If not, report - the successful resolution. */ - svn_boolean_t text_conflict, prop_conflict, tree_conflict; - SVN_ERR(svn_wc_conflicted_p2(&text_conflict, &prop_conflict, - &tree_conflict, path, conflict_dir, - pool)); - if ((! (resolve_text && text_conflict)) - && (! (resolve_props && prop_conflict)) - && (! (resolve_tree && tree_conflict))) - (*notify_func)(notify_baton, - svn_wc_create_notify(path, svn_wc_notify_resolved, - pool), pool); - } + if (need_feedback) + *did_resolve = TRUE; } return SVN_NO_ERROR; @@ -2872,6 +2853,16 @@ struct resolve_callback_baton void *notify_baton; }; +/* An svn_wc_entry_callbacks2_t callback function. + * + * Mark as resolved any tree conflict on PATH, even if ENTRY is null, and + * any other conflicts on (PATH, ENTRY). Send a notification if any such + * change is made. Ignore the entry if it is deleted or absent, or if it is + * a duplicate report of a directory. + * + * Do this all according to the contents of (struct resolve_callback_baton + * *)WALK_BATON. + */ static svn_error_t * resolve_found_entry_callback(const char *path, const svn_wc_entry_t *entry, @@ -2881,27 +2872,78 @@ resolve_found_entry_callback(const char struct resolve_callback_baton *baton = walk_baton; const char *conflict_dir, *base_name = NULL; svn_wc_adm_access_t *adm_access; + svn_boolean_t resolved = FALSE; + + if (!entry) + { + printf("### Unversioned... woo-hoo! resolve_f_e_cb('%s')\n", path); + return SVN_NO_ERROR; + } + if (entry->deleted || entry->absent) + { + /* printf("### resolve_f_e_cb('%s'): Deleted or Absent\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; /* ### ? From now on, I think 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 + first one (where it's a child of a parent dir), and only process the second one (where we're looking at THIS_DIR). */ - if ((entry->kind == svn_node_dir) - && (strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR))) + if (entry && (entry->kind == svn_node_dir) + && (strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR)) != 0) return SVN_NO_ERROR; /* Figger out the directory in which the conflict resides. */ - if (entry->kind == svn_node_dir) + if (entry && entry->kind == svn_node_dir) conflict_dir = path; else svn_path_split(path, &conflict_dir, &base_name, pool); - SVN_ERR(svn_wc_adm_retrieve(&adm_access, baton->adm_access, conflict_dir, - pool)); + SVN_ERR(svn_wc_adm_probe_retrieve(&adm_access, baton->adm_access, + conflict_dir, pool)); + + /* Clear any tree conflict on the path. */ + if (baton->resolve_tree) + { + svn_wc_conflict_description_t *conflict; - return resolve_conflict_on_entry(path, entry, adm_access, base_name, - baton->resolve_text, baton->resolve_props, - baton->resolve_tree, - baton->conflict_choice, baton->notify_func, - baton->notify_baton, pool); + SVN_ERR(svn_wc_get_tree_conflict(&conflict, path, adm_access, pool)); + if (conflict) + { + SVN_ERR(svn_wc__del_tree_conflict(path, adm_access, pool)); + resolved = TRUE; + } + } + + /* If this is a versioned entry, resolve its other conflicts, if any. */ + if (entry) + SVN_ERR(resolve_conflict_on_entry(path, entry, adm_access, base_name, + baton->resolve_text, baton->resolve_props, + baton->conflict_choice, &resolved, + pool)); + + /* Notify */ + if (baton->notify_func && resolved) + { + /* Sanity check: see if libsvn_wc *still* thinks this item is in a + state of conflict that we have asked to resolve. If not, report + the successful resolution. */ + svn_boolean_t text_conflict, prop_conflict, tree_conflict; + SVN_ERR(svn_wc_conflicted_p2(&text_conflict, &prop_conflict, + &tree_conflict, path, adm_access, + pool)); + printf("### resolve_found_entry cb ('%s'):%d,%d,%d\n", path, text_conflict, prop_conflict, tree_conflict); + if ((! (baton->resolve_text && text_conflict)) + && (! (baton->resolve_props && prop_conflict)) + && (! (baton->resolve_tree && tree_conflict))) + (*baton->notify_func)(baton->notify_baton, + svn_wc_create_notify(path, svn_wc_notify_resolved, + pool), pool); + } + return SVN_NO_ERROR; } static const svn_wc_entry_callbacks2_t @@ -2937,9 +2979,9 @@ 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, + return svn_wc__walk_entries_and_tc(path, adm_access, &resolve_walk_callbacks, baton, depth, - FALSE, cancel_func, cancel_baton, pool); + cancel_func, cancel_baton, pool); } svn_error_t *svn_wc_add_lock(const char *path, const svn_lock_t *lock, Index: subversion/tests/cmdline/log_tests.py =================================================================== --- subversion/tests/cmdline/log_tests.py (revision 34116) +++ subversion/tests/cmdline/log_tests.py (working copy) @@ -202,7 +202,7 @@ def guarantee_repos_and_wc(sbox): def merge_history_repos(sbox): """Make a repos with varied and interesting merge history, similar -to the repos found at: log_tests_data/merge_history_dump.png""" + to the repos found at: log_tests_data/merge_history_dump.png""" upsilon_path = os.path.join('A', 'upsilon') omicron_path = os.path.join('blocked', 'omicron') @@ -390,8 +390,9 @@ to the repos found at: log_tests_data/me "This is yet more content in 'mu'.", "wb") # Resolve conflicts, and commit - svntest.main.run_svn(None, 'resolved', os.path.join('A', 'mu')) - svntest.main.run_svn(None, 'resolved', 'A') + svntest.main.run_svn(None, 'resolved', os.path.join('A', 'mu'), + os.path.join('A', 'xi'), + os.path.join('A', 'upsilon')) svntest.main.run_svn(None, 'ci', '-m', "Merge branches/c to trunk, " + "resolving a conflict in 'mu'.", Index: subversion/tests/cmdline/tree_conflict_tests.py =================================================================== --- subversion/tests/cmdline/tree_conflict_tests.py (revision 34116) +++ subversion/tests/cmdline/tree_conflict_tests.py (working copy) @@ -475,8 +475,8 @@ def ensure_tree_conflict(sbox, operation # Resolve recursively on the parent, because per-victim resolve is # not yet implemented. run_and_verify_svn(None, - "Resolved .* '" + re.escape(target_path) + "'", [], - 'resolved', '-R', target_path) + "Resolved .* '" + re.escape(victim) + "'", [], + 'resolved', victim) if 'status-nc' in test_what: verbose_print("--- Checking that 'status' does not report a conflict") Index: subversion/tests/cmdline/update_tests.py =================================================================== --- subversion/tests/cmdline/update_tests.py (revision 34116) +++ subversion/tests/cmdline/update_tests.py (working copy) @@ -1134,11 +1134,10 @@ def another_hudson_problem(sbox): # This update created a tree conflict ("update tried to # delete a directory that was locally deleted"), marking - # C A/D. Just ignore it, i.e. resolve it. - D_path = os.path.join(wc_dir, 'A', 'D') + # " C A/D/G". Just ignore it, i.e. resolve it. svntest.actions.run_and_verify_svn(None, - ["Resolved conflicted state of '" + D_path + "'\n"], [], - 'resolved', D_path) + ["Resolved conflicted state of '" + G_path + "'\n"], [], + 'resolved', G_path) # Both G and gamma should be 'deleted', update should produce no output expected_status = svntest.actions.get_virginal_state(wc_dir, 3)