Upgrade svn_wc_get_status_editor() interface to use depth instead of recurse. *** PATCH IN PROGRESS -- SEE NOTE ABOUT FAILING TESTS BELOW *** * subversion/include/svn_wc.h (svn_wc_get_status_editor3): New prototype. (svn_wc_get_status_editor2): Deprecate. (svn_wc_status_set_repos_locks): Doc tweak. * subversion/libsvn_wc/status.c (struct edit_baton): Replace 'descend' field with 'depth'. (struct dir_baton): Add new 'depth' field. (make_dir_baton): Set and use new 'depth' field in dir_baton. (get_dir_status, handle_dir_entry, handle_statii): Take 'depth' instead of 'descend'. All callers changed. (svn_wc_get_status_editor3): New function. (svn_wc_get_status_editor2, svn_wc_get_status_editor): Implement via svn_wc_get_status_editor3. * subversion/libsvn_wc/revision_status.c (svn_wc_revision_status): Update caller. * subversion/libsvn_client/status.c (svn_client_status3): Update caller. * subversion/include/svn_types.h (svn_depth_t): Note that order of depths is important, and take the tentativity markers off svn_depth_unknown and svn_depth_exclude. * README.branch: Remove this item from the API TODO list. -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- With this patch, the sparse-directories branch fails 16 tests right now: FAIL: diff_tests.py 33: nonrecursive diff + deleted directories FAIL: stat_tests.py 7: status -u with new files in the repository FAIL: stat_tests.py 10: status -u indicates out-of-dateness FAIL: stat_tests.py 15: status -u in partially non-recursive wc FAIL: stat_tests.py 18: status on ignored directory FAIL: stat_tests.py 20: status on missing directory FAIL: stat_tests.py 24: run 'status -u' variations w/ incoming propchanges FAIL: stat_tests.py 25: run 'status -uv' w/ incoming propchanges FAIL: depth_tests.py 1: depth-empty checkout FAIL: depth_tests.py 2: depth-files checkout FAIL: depth_tests.py 3: non-recursive checkout equals depth-files FAIL: depth_tests.py 4: update depth-empty wc shouldn't receive file mod FAIL: depth_tests.py 5: update depth-immediates wc gets top file mod only FAIL: depth_tests.py 7: act on a file in a depth-empty working copy FAIL: depth_tests.py 8: bring a dir into a depth-empty working copy FAIL: depth_tests.py 13: depth-empty working copy ignores a deletion -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- Index: README.branch =================================================================== --- README.branch (revision 23808) +++ README.branch (working copy) @@ -394,7 +394,6 @@ svn_client_info() # I don't think this needs to take depth. svn_client_add3() # I don't think this needs to take depth. - svn_wc_get_status_editor2() # Yes, probably should take depth. svn_wc_get_switch_editor3() # Yes, probably should take depth. svn_wc_revert2() # Really not sure about this one. svn_wc_resolved_conflict2() # Really not sure about this one. Index: subversion/include/svn_types.h =================================================================== --- subversion/include/svn_types.h (revision 23808) +++ subversion/include/svn_types.h (working copy) @@ -208,12 +208,14 @@ */ typedef enum { - /* Depth undetermined or ignored. - ### TODO: This depth may turn out to be unnecessary. ### */ + /* The order of these depths is important: the higher the number, + the deeper it descends. This allows us to compare two depths + numerically to decide which should govern. */ + + /* Depth undetermined or ignored. */ svn_depth_unknown = -2, - /* Exclude (remove, whatever) directory D. - ### TODO: This depth may turn out to be unnecessary. ### */ + /* Exclude (i.e., don't descend into) directory D. */ svn_depth_exclude = -1, /* Just the named directory D, no entries. Updates will not pull in Index: subversion/include/svn_wc.h =================================================================== --- subversion/include/svn_wc.h (revision 23809) +++ subversion/include/svn_wc.h (working copy) @@ -2040,10 +2040,19 @@ * - If @a get_all is false, then only locally-modified entries will be * returned. If true, then all entries will be returned. * - * - If @a recurse is false, status structures will be returned only - * for the target and its immediate children. Otherwise, this - * operation is fully recursive. + * - If @a depth is @c svn_depth_empty, a status structure will + * be returned for the target only; if @c svn_depth_files, for the + * target and its immediate file children; if + * @c svn_depth_immediates, for the target and its immediate + * children; if @c svn_depth_infinity, for the target and + * everything underneath it, fully recursively. * + * If @a depth is @c svn_depth_unknown, take depths from the + * working copy and behave as above in each directory's case. + * + * If the given @a depth is incompatible with the depth found in a + * working copy directory, the found depth always governs. + * * If @a no_ignore is set, statuses that would typically be ignored * will instead be reported. * @@ -2057,7 +2066,32 @@ * Allocate the editor itself in @a pool, but the editor does temporary * allocations in a subpool of @a pool. * + * @since New in 1.5. + */ +svn_error_t *svn_wc_get_status_editor3(const svn_delta_editor_t **editor, + void **edit_baton, + void **set_locks_baton, + svn_revnum_t *edit_revision, + svn_wc_adm_access_t *anchor, + const char *target, + apr_hash_t *config, + svn_depth_t depth, + svn_boolean_t get_all, + svn_boolean_t no_ignore, + svn_wc_status_func2_t status_func, + void *status_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + svn_wc_traversal_info_t *traversal_info, + apr_pool_t *pool); + +/* + * Like svn_wc_get_status_editor3(), but with @a recurse instead of @a depth. + * If @a recurse is true, behave as if for @c svn_depth_infinity; else + * if @a recurse is false, behave as if for @c svn_depth_files. + * * @since New in 1.2. + * @deprecated Provided for backward compatibility with the 1.4 API. */ svn_error_t *svn_wc_get_status_editor2(const svn_delta_editor_t **editor, void **edit_baton, @@ -2076,7 +2110,6 @@ svn_wc_traversal_info_t *traversal_info, apr_pool_t *pool); - /** * Same as svn_wc_get_status_editor2(), but with @a set_locks_baton set * to @c NULL, and taking a deprecated svn_wc_status_func_t argument. @@ -2104,7 +2137,7 @@ * Associate @a locks, a hash table mapping const char* * absolute repository paths to svn_lock_t objects, with a * @a set_locks_baton returned by an earlier call to - * svn_wc_get_status_editor2(). @a repos_root is the repository root URL. + * svn_wc_get_status_editor3(). @a repos_root is the repository root URL. * Perform all allocations in @a pool. * * @note @a locks will not be copied, so it must be valid throughout the Index: subversion/libsvn_wc/revision_status.c =================================================================== --- subversion/libsvn_wc/revision_status.c (revision 23808) +++ subversion/libsvn_wc/revision_status.c (working copy) @@ -108,10 +108,10 @@ cancel_func, cancel_baton, pool)); - SVN_ERR(svn_wc_get_status_editor2(&editor, &edit_baton, NULL, + SVN_ERR(svn_wc_get_status_editor3(&editor, &edit_baton, NULL, &edit_revision, anchor_access, target, NULL /* config */, - TRUE /* recurse */, + svn_depth_infinity, TRUE /* get_all */, FALSE /* no_ignore */, analyze_status, &sb, Index: subversion/libsvn_wc/status.c =================================================================== --- subversion/libsvn_wc/status.c (revision 23808) +++ subversion/libsvn_wc/status.c (working copy) @@ -46,13 +46,21 @@ struct edit_baton { - /* For status, the "destination" of the edit and whether to honor - any paths that are 'below'. */ + /* For status, the "destination" of the edit. */ const char *anchor; const char *target; svn_wc_adm_access_t *adm_access; - svn_boolean_t descend; + /* The overall depth of this edit (a dir baton may override this). + * + * If this is svn_depth_unknown, the depths found in the working + * copy will govern the edit; or if the edit depth indicates a + * descent deeper than the found depths are capable of, the found + * depths also govern, of course (there's no point descending into + * something that's not there). + */ + svn_depth_t depth; + /* Do we want all statuses (instead of just the interesting ones) ? */ svn_boolean_t get_all; @@ -111,6 +119,14 @@ directory. */ struct dir_baton *parent_baton; + /* The ambient requested depth below this point in the edit. This + can differ from the parent baton's depth (with the edit baton + considered the ultimate parent baton). For example, if the + parent baton has svn_depth_immediates, then here we should have + svn_depth_empty, because there would be no further recursion, not + even to file children. */ + svn_depth_t depth; + /* 'svn status' shouldn't print status lines for things that are added; we're only interest in asking if objects that the user *already* has are up-to-date or not. Thus if this flag is set, @@ -674,7 +690,7 @@ svn_wc_adm_access_t *adm_access, const char *entry, apr_array_header_t *ignores, - svn_boolean_t descend, + svn_depth_t depth, svn_boolean_t get_all, svn_boolean_t no_ignore, svn_boolean_t skip_this_dir, @@ -697,7 +713,7 @@ svn_node_kind_t kind, svn_boolean_t special, apr_array_header_t *ignores, - svn_boolean_t descend, + svn_depth_t depth, svn_boolean_t get_all, svn_boolean_t no_ignore, svn_wc_status_func2_t status_func, @@ -731,13 +747,16 @@ } /* Descend only if the subdirectory is a working copy directory - (and DESCEND is non-zero ofcourse) */ - if (descend && (full_entry != entry)) + (and DEPTH permits it, of course) */ + if (full_entry != entry + && (depth == svn_depth_unknown + || depth == svn_depth_immediates + || depth == svn_depth_infinity)) { svn_wc_adm_access_t *dir_access; SVN_ERR(svn_wc_adm_retrieve(&dir_access, adm_access, path, pool)); SVN_ERR(get_dir_status(eb, dir_entry, dir_access, NULL, ignores, - descend, get_all, no_ignore, FALSE, + depth, get_all, no_ignore, FALSE, status_func, status_baton, cancel_func, cancel_baton, pool)); } @@ -774,14 +793,14 @@ *will* be reported, regardless of this parameter's value. Other arguments are the same as those passed to - svn_wc_get_status_editor2(). */ + svn_wc_get_status_editor3(). */ static svn_error_t * get_dir_status(struct edit_baton *eb, const svn_wc_entry_t *parent_entry, svn_wc_adm_access_t *adm_access, const char *entry, apr_array_header_t *ignores, - svn_boolean_t descend, + svn_depth_t depth, svn_boolean_t get_all, svn_boolean_t no_ignore, svn_boolean_t skip_this_dir, @@ -872,7 +891,7 @@ entry_entry, dirent_p ? dirent_p->kind : svn_node_none, dirent_p ? dirent_p->special : FALSE, - ignores, descend, get_all, + ignores, depth, get_all, no_ignore, status_func, status_baton, cancel_func, cancel_baton, subpool)); } @@ -965,7 +984,7 @@ dirent_p ? dirent_p->kind : svn_node_none, dirent_p ? dirent_p->special : FALSE, ignores, - descend, get_all, no_ignore, + depth, get_all, no_ignore, status_func, status_baton, cancel_func, cancel_baton, iterpool)); } @@ -1190,6 +1209,7 @@ struct dir_baton *d = apr_pcalloc(pool, sizeof(*d)); const char *full_path; svn_wc_status2_t *status_in_parent; + svn_depth_t parent_depth = pb ? pb->depth : eb->depth; /* Don't do this. Just do NOT do this to me. */ if (pb && (! path)) @@ -1214,6 +1234,16 @@ d->ood_kind = svn_node_dir; d->ood_last_cmt_author = NULL; + if (parent_depth == svn_depth_immediates) + d->depth = svn_depth_empty; + else if (parent_depth == svn_depth_files || parent_depth == svn_depth_empty) + d->depth = svn_depth_exclude; + else if (parent_depth == svn_depth_unknown) + /* This is only tentative, it can be overridden from d's entry later. */ + d->depth = svn_depth_unknown; + else + d->depth = svn_depth_infinity; + /* Get the status for this path's children. Of course, we only want to do this if the path is versioned as a directory. */ if (pb) @@ -1233,15 +1263,46 @@ && (status_in_parent->text_status != svn_wc_status_external) && (status_in_parent->text_status != svn_wc_status_ignored) && (status_in_parent->entry->kind == svn_node_dir) - && (eb->descend || (! pb))) + && (d->depth == svn_depth_unknown + || d->depth == svn_depth_infinity + || d->depth == svn_depth_files + || d->depth == svn_depth_immediates)) { svn_wc_adm_access_t *dir_access; + svn_wc_status2_t *this_dir_status; apr_array_header_t *ignores = eb->ignores; SVN_ERR(svn_wc_adm_retrieve(&dir_access, eb->adm_access, d->path, pool)); + SVN_ERR(get_dir_status(eb, status_in_parent->entry, dir_access, NULL, - ignores, FALSE, TRUE, TRUE, TRUE, hash_stash, - d->statii, NULL, NULL, pool)); + ignores, svn_depth_empty, TRUE, TRUE, TRUE, + hash_stash, d->statii, NULL, NULL, pool)); + + if (0) + { + apr_hash_index_t *hi; + printf("KFF: d->path: '%s'\n", d->path); + for (hi = apr_hash_first(pool, d->statii); hi; hi = apr_hash_next(hi)) + { + const void *key; + void *val; + svn_wc_status2_t *status; + apr_hash_this(hi, &key, NULL, &val); + status = val; + printf("KFF:\n"); + printf("KFF: '%s'\n", (char *) key); + printf("KFF: '%s'\n", status->url); + } + } + + /* If we found a depth here, it should govern. */ + this_dir_status = apr_hash_get(d->statii, d->path, APR_HASH_KEY_STRING); + if (this_dir_status && this_dir_status->entry + && (d->depth == svn_depth_unknown + || d->depth > status_in_parent->entry->depth)) + { + d->depth = this_dir_status->entry->depth; + } } *dir_baton = d; @@ -1360,18 +1421,17 @@ /* Handle a directory's STATII hash. EB is the edit baton. DIR_PATH and DIR_ENTRY are the on-disk path and entry, respectively, for the - directory itself. If DESCEND is set, this function will recurse - into subdirectories. Also, if DIR_WAS_DELETED is set, each status - that is reported through this function will have its - repos_text_status field showing a deletion. Use POOL for all - allocations. */ + directory itself. Descend into subdirectories according to DEPTH. + Also, if DIR_WAS_DELETED is set, each status that is reported + through this function will have its repos_text_status field showing + a deletion. Use POOL for all allocations. */ static svn_error_t * handle_statii(struct edit_baton *eb, svn_wc_entry_t *dir_entry, const char *dir_path, apr_hash_t *statii, svn_boolean_t dir_was_deleted, - svn_boolean_t descend, + svn_depth_t depth, apr_pool_t *pool) { apr_array_header_t *ignores = eb->ignores; @@ -1405,13 +1465,16 @@ /* Now, handle the status. */ if (svn_wc__adm_missing(eb->adm_access, key)) status->text_status = svn_wc_status_missing; - else if (descend && status->entry && status->entry->kind == svn_node_dir) + else if (status->entry && status->entry->kind == svn_node_dir + && (depth == svn_depth_unknown + || depth == svn_depth_immediates + || depth == svn_depth_infinity)) { svn_wc_adm_access_t *dir_access; SVN_ERR(svn_wc_adm_retrieve(&dir_access, eb->adm_access, key, subpool)); SVN_ERR(get_dir_status(eb, dir_entry, dir_access, NULL, - ignores, TRUE, eb->get_all, + ignores, depth, eb->get_all, eb->no_ignore, TRUE, status_func, status_baton, eb->cancel_func, eb->cancel_baton, subpool)); @@ -1674,7 +1737,7 @@ /* Handle this directory's statuses, and then note in the parent that this has been done. */ - if (pb && eb->descend) + if (pb && db->depth != svn_depth_exclude) { svn_boolean_t was_deleted = FALSE; @@ -1687,7 +1750,8 @@ /* Now do the status reporting. */ SVN_ERR(handle_statii(eb, dir_status ? dir_status->entry : NULL, - db->path, db->statii, was_deleted, TRUE, pool)); + db->path, db->statii, was_deleted, db->depth, + pool)); if (dir_status && is_sendable_status(dir_status, eb)) (eb->status_func)(eb->status_baton, db->path, dir_status); apr_hash_set(pb->statii, db->path, APR_HASH_KEY_STRING, NULL); @@ -1704,7 +1768,7 @@ tgt_status = apr_hash_get(db->statii, path, APR_HASH_KEY_STRING); if (tgt_status) { - if ((eb->descend) + if (eb->depth != svn_depth_exclude /* ### TODO: can't happen? */ && (tgt_status->entry) && (tgt_status->entry->kind == svn_node_dir)) { @@ -1713,7 +1777,7 @@ path, pool)); SVN_ERR(get_dir_status (eb, tgt_status->entry, dir_access, NULL, - eb->ignores, TRUE, eb->get_all, eb->no_ignore, + eb->ignores, eb->depth, eb->get_all, eb->no_ignore, TRUE, eb->status_func, eb->status_baton, eb->cancel_func, eb->cancel_baton, pool)); } @@ -1727,7 +1791,7 @@ Note that our directory couldn't have been deleted, because it is the root of the edit drive. */ SVN_ERR(handle_statii(eb, eb->anchor_status->entry, db->path, - db->statii, FALSE, eb->descend, pool)); + db->statii, FALSE, eb->depth, pool)); if (is_sendable_status(eb->anchor_status, eb)) (eb->status_func)(eb->status_baton, db->path, eb->anchor_status); eb->anchor_status = NULL; @@ -1915,7 +1979,7 @@ if (! tgt_entry) { err = get_dir_status(eb, NULL, eb->adm_access, eb->target, - ignores, FALSE, eb->get_all, TRUE, + ignores, eb->depth, eb->get_all, TRUE, TRUE, eb->status_func, eb->status_baton, eb->cancel_func, eb->cancel_baton, pool); @@ -1928,7 +1992,7 @@ if (err) goto cleanup; err = get_dir_status(eb, NULL, tgt_access, NULL, ignores, - eb->descend, eb->get_all, + eb->depth, eb->get_all, eb->no_ignore, FALSE, eb->status_func, eb->status_baton, eb->cancel_func, eb->cancel_baton, @@ -1939,7 +2003,7 @@ else { err = get_dir_status(eb, NULL, eb->adm_access, eb->target, - ignores, FALSE, eb->get_all, TRUE, + ignores, eb->depth, eb->get_all, TRUE, TRUE, eb->status_func, eb->status_baton, eb->cancel_func, eb->cancel_baton, pool); if (err) goto cleanup; @@ -1948,7 +2012,7 @@ else { err = get_dir_status(eb, NULL, eb->adm_access, NULL, ignores, - eb->descend, eb->get_all, eb->no_ignore, + eb->depth, eb->get_all, eb->no_ignore, FALSE, eb->status_func, eb->status_baton, eb->cancel_func, eb->cancel_baton, pool); if (err) goto cleanup; @@ -1973,14 +2037,14 @@ /*** Public API ***/ svn_error_t * -svn_wc_get_status_editor2(const svn_delta_editor_t **editor, +svn_wc_get_status_editor3(const svn_delta_editor_t **editor, void **edit_baton, void **set_locks_baton, svn_revnum_t *edit_revision, svn_wc_adm_access_t *anchor, const char *target, apr_hash_t *config, - svn_boolean_t recurse, + svn_depth_t depth, svn_boolean_t get_all, svn_boolean_t no_ignore, svn_wc_status_func2_t status_func, @@ -1995,7 +2059,7 @@ /* Construct an edit baton. */ eb = apr_palloc(pool, sizeof(*eb)); - eb->descend = recurse; + eb->depth = depth; eb->target_revision = edit_revision; eb->adm_access = anchor; eb->config = config; @@ -2047,7 +2111,43 @@ } +svn_error_t * +svn_wc_get_status_editor2(const svn_delta_editor_t **editor, + void **edit_baton, + void **set_locks_baton, + svn_revnum_t *edit_revision, + svn_wc_adm_access_t *anchor, + const char *target, + apr_hash_t *config, + svn_boolean_t recurse, + svn_boolean_t get_all, + svn_boolean_t no_ignore, + svn_wc_status_func2_t status_func, + void *status_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + svn_wc_traversal_info_t *traversal_info, + apr_pool_t *pool) +{ + return svn_wc_get_status_editor3(editor, + edit_baton, + set_locks_baton, + edit_revision, + anchor, + target, + config, + SVN_DEPTH_FROM_RECURSE(recurse), + get_all, + no_ignore, + status_func, + status_baton, + cancel_func, + cancel_baton, + traversal_info, + pool); +} + /* Helpers for deprecated svn_wc_status_editor(), of type svn_wc_status_func2_t. */ struct old_status_func_cb_baton @@ -2087,8 +2187,9 @@ b->original_func = status_func; b->original_baton = status_baton; - return svn_wc_get_status_editor2(editor, edit_baton, NULL, edit_revision, - anchor, target, config, recurse, + return svn_wc_get_status_editor3(editor, edit_baton, NULL, edit_revision, + anchor, target, config, + SVN_DEPTH_FROM_RECURSE(recurse), get_all, no_ignore, old_status_func_cb, b, cancel_func, cancel_baton, traversal_info, pool); Index: subversion/libsvn_client/status.c =================================================================== --- subversion/libsvn_client/status.c (revision 23808) +++ subversion/libsvn_client/status.c (working copy) @@ -221,8 +221,6 @@ const svn_wc_entry_t *entry; struct status_baton sb; svn_revnum_t edit_revision = SVN_INVALID_REVNUM; - /* ### TODO: This is a shim, we should use depth for real. */ - svn_depth_t recurse = SVN_DEPTH_TO_RECURSE(depth); sb.real_status_func = status_func; sb.real_status_baton = status_baton; @@ -240,10 +238,9 @@ /* Get the status edit, and use our wrapping status function/baton as the callback pair. */ - /* ### TODO: ...and this would take depth, not recurse... */ - SVN_ERR(svn_wc_get_status_editor2(&editor, &edit_baton, &set_locks_baton, + SVN_ERR(svn_wc_get_status_editor3(&editor, &edit_baton, &set_locks_baton, &edit_revision, anchor_access, target, - ctx->config, recurse, get_all, no_ignore, + ctx->config, depth, get_all, no_ignore, tweak_status, &sb, ctx->cancel_func, ctx->cancel_baton, traversal_info, pool));