Make tree conflict detection notice modifications deep inside a tree that "update" is about to delete. ### FAIL: depth_tests.py 25 * subversion/libsvn_wc/update_editor.c (modcheck_baton_t): New data type. (modcheck_found_entry, modcheck_handle_error, tree_has_local_mods): New functions. (check_tree_conflict): Use tree_has_local_mods() to detect deep mods. * subversion/tests/cmdline/update_tests.py (update_delete_modified_files): Expect another conflict. Index: subversion/tests/cmdline/update_tests.py =================================================================== --- subversion/tests/cmdline/update_tests.py (revision 33519) +++ subversion/tests/cmdline/update_tests.py (working copy) @@ -656,6 +656,7 @@ expected_output = svntest.wc.State(wc_dir, { 'A/B/E' : Item(status='C '), # tree conflict (issue #2282) 'A/B/E/alpha' : Item(status='D '), + 'A/D' : Item(status='C '), # tree conflict (issue #2282) 'A/D/G' : Item(status='D '), }) expected_disk = svntest.main.greek_state.copy() @@ -670,6 +671,7 @@ expected_status = svntest.actions.get_virginal_state(wc_dir, 2) expected_status.tweak('A/B/E', status='C ') # tree conflict (issue #2282) expected_status.remove('A/B/E/alpha') + expected_status.tweak('A/D', status='C ') # tree conflict (issue #2282) expected_status.remove('A/D/G') expected_status.remove('A/D/G/pi') expected_status.remove('A/D/G/rho') Index: subversion/libsvn_wc/update_editor.c =================================================================== --- subversion/libsvn_wc/update_editor.c (revision 33577) +++ subversion/libsvn_wc/update_editor.c (working copy) @@ -1071,6 +1071,10 @@ return err; } + +/* ===================================================================== */ +/* Checking for local modifications. */ + /* Set *MODIFIED to true iff the item described by (ADM_ACCESS, FULL_PATH, * KIND) has local modifications. * For a file, this means text mods or property mods. @@ -1101,6 +1105,74 @@ return SVN_NO_ERROR; } +/* A baton for use with modcheck_found_entry(). */ +typedef struct modcheck_baton_t { + svn_wc_adm_access_t *adm_access; /* access for the root of the sub-tree */ + svn_boolean_t found_mod; /* whether a modification has been found */ +} modcheck_baton_t; + +static svn_error_t * +modcheck_found_entry(const char *path, + const svn_wc_entry_t *entry, + void *walk_baton, + apr_pool_t *pool) +{ + modcheck_baton_t *baton = walk_baton; + svn_wc_adm_access_t *adm_access; + svn_boolean_t modified; + + SVN_ERR(svn_wc_adm_probe_retrieve(&adm_access, baton->adm_access, path, + pool)); + SVN_ERR(entry_has_local_mods(&modified, adm_access, entry->kind, path, pool)); + if (modified) + baton->found_mod = TRUE; + + return SVN_NO_ERROR; +} + +static svn_error_t * +modcheck_handle_error(const char *path, + svn_error_t *err, + void *walk_baton, + apr_pool_t *pool) +{ + return err; +} + +/* Set *MODIFIED to true iff there are any local modifications within the + * tree rooted at PATH whose admin access baton is ADM_ACCESS. PATH may be a + * file or a directory. */ +static svn_error_t * +tree_has_local_mods(svn_boolean_t *modified, + const char *path, + svn_wc_adm_access_t *adm_access, + apr_pool_t *pool) +{ + + static svn_wc_entry_callbacks2_t modcheck_callbacks = + { modcheck_found_entry, modcheck_handle_error }; + modcheck_baton_t modcheck_baton = { NULL, FALSE }; + + modcheck_baton.adm_access = adm_access; + + /* Walk the WC tree to its full depth, looking for any local modifications. + * If it's a "sparse" directory, that's OK: there can be no local mods in + * the pieces that aren't present in the WC. */ + + /* ### TODO: If this tree is not (read-) locked to depth infinity, e.g. + * because of "svn update --depth=immediates", this will error out. We first + * need to extend the lock deeper. */ + + SVN_ERR(svn_wc_walk_entries3(path, adm_access, + &modcheck_callbacks, &modcheck_baton, + svn_depth_infinity, FALSE /* show_hidden */, + NULL /* cancel_func */, NULL /* cancel_baton */, + pool)); + + *modified = modcheck_baton.found_mod; + return SVN_NO_ERROR; +} + /* Check whether the incoming change ACTION on FULL_PATH would conflict with * FULL_PATH's scheduled change. If so, then raise a tree conflict with * FULL_PATH as the victim, by appending log actions to LOG_ACCUM. @@ -1183,21 +1255,13 @@ */ SVN_ERR(svn_wc_adm_probe_retrieve(&adm_access, parent_adm_access, full_path, pool)); - SVN_ERR(entry_has_local_mods(&modified, adm_access, - entry->kind, full_path, pool)); - - - /* ### TODO: Also detect deep modifications in a directory tree. - * The update editor will not visit subdirectories of a - * directory it wants to delete. Therefore, we need to start - * a separate crawl here to make sure the directory tree we - * are about to delete hasn't been locally modified anywhere. - */ -#if 0 - SVN_ERR(subtrees_have_local_mods(&modified, parent_adm_access, - entry, full_path, pool)); -#endif + /* Detect deep modifications in a directory tree. + * The update editor will not visit subdirectories of a + * directory it wants to delete. Therefore, we need to start + * a separate crawl here. */ + SVN_ERR(tree_has_local_mods(&modified, full_path, adm_access, + pool)); if (modified) { reason = svn_wc_conflict_reason_edited;