Author: pburba Date: Fri Apr 1 19:32:11 2011 New Revision: 1087866 URL: http://svn.apache.org/viewvc?rev=1087866&view=rev Log: Remove final node walker from merge code.
>From the ad hoc testing files: A simple merge of single revision, affecting a single file in a "large" WC on windows (399 MB, 28,479 files, 1,767 dirs) drops from 00:02:48.974 to 00:00:02.896. * notes/wc_node_walkers.txt: Remove off another dead node walker. * subversion/include/private/svn_wc_private.h (svn_wc__get_absent_subtrees): New. * subversion/libsvn_wc/node.c (svn_wc__get_absent_subtrees): New. * subversion/libsvn_wc/wc-queries.sql (STMT_SELECT_ALL_ABSENT_NODES): New, similar to STMT_SELECT_ABSENT_NODES but with no limit. * subversion/libsvn_wc/wc_db.c (svn_wc__db_get_absent_subtrees): New. * subversion/libsvn_wc/wc_db.h (svn_wc__db_get_absent_subtrees): New. * subversion/libsvn_client/merge.c (populate_remaining_ranges): Don't get pre-merge mergeinfo unconditionally. We may have already obtained it now in get_mergeinfo_paths(). (get_mergeinfo_walk_baton, record_missing_subtree_roots, get_mergeinfo_walk_cb): Delete node-walker plumbing. (pre_merge_status_baton_t, pre_merge_status_cb): New. (get_mergeinfo_paths): Reimplement using svn_client_propget3, svn_client_status5, and svn_wc__get_absent_subtrees in place of svn_wc__node_walk_children. Tweak doc string to mention two conditions we've long covered. Modified: subversion/trunk/notes/wc_node_walkers.txt subversion/trunk/subversion/include/private/svn_wc_private.h subversion/trunk/subversion/libsvn_client/merge.c subversion/trunk/subversion/libsvn_wc/node.c subversion/trunk/subversion/libsvn_wc/wc-queries.sql subversion/trunk/subversion/libsvn_wc/wc_db.c subversion/trunk/subversion/libsvn_wc/wc_db.h Modified: subversion/trunk/notes/wc_node_walkers.txt URL: http://svn.apache.org/viewvc/subversion/trunk/notes/wc_node_walkers.txt?rev=1087866&r1=1087865&r2=1087866&view=diff ============================================================================== --- subversion/trunk/notes/wc_node_walkers.txt (original) +++ subversion/trunk/notes/wc_node_walkers.txt Fri Apr 1 19:32:11 2011 @@ -33,13 +33,6 @@ subversion/libsvn_client/info.c callback it drives peeks back into the WC database many, many times per node. -subversion/libsvn_client/merge.c - - get_mergeinfo_paths() - Crawl a tree (up to specified depth), - locating children of a given directory for which the parent's - mergeinfo cannot be naively inherited and storing info about - those children in an array. No notification required. - subversion/libsvn_client/prop_commands.c set_props_cb() - Crawl a tree (up to specified depth) setting a Modified: subversion/trunk/subversion/include/private/svn_wc_private.h URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/private/svn_wc_private.h?rev=1087866&r1=1087865&r2=1087866&view=diff ============================================================================== --- subversion/trunk/subversion/include/private/svn_wc_private.h (original) +++ subversion/trunk/subversion/include/private/svn_wc_private.h Fri Apr 1 19:32:11 2011 @@ -875,6 +875,19 @@ svn_wc__has_switched_subtrees(svn_boolea const char *trail_url, apr_pool_t *scratch_pool); +/* Set @a *absent_subtrees to a hash mapping <tt>const char *</tt> local + * absolute paths to <tt>const char *</tt> local absolute paths for every + * path at or under @a local_abspath in @a wc_ctx which are absent (excluded + * by authz). If no absent paths are found then @a *absent_subtrees is set + * to @c NULL. Allocate the hash and all items therein from @a result_pool. + */ +svn_error_t * +svn_wc__get_absent_subtrees(apr_hash_t **absent_subtrees, + svn_wc_context_t *wc_ctx, + const char *local_abspath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + /* Indicate in @a *is_modified whether the working copy has local * modifications, using context @a wc_ctx. * Use @a scratch_pool for temporary allocations. Modified: subversion/trunk/subversion/libsvn_client/merge.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/merge.c?rev=1087866&r1=1087865&r2=1087866&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_client/merge.c (original) +++ subversion/trunk/subversion/libsvn_client/merge.c Fri Apr 1 19:32:11 2011 @@ -4365,7 +4365,7 @@ populate_remaining_ranges(apr_array_head this until we know it is absolutely necessary, since it requires an expensive round trip communication with the server. */ SVN_ERR(get_full_mergeinfo( - &(child->pre_merge_mergeinfo), + child->pre_merge_mergeinfo ? NULL : &(child->pre_merge_mergeinfo), /* Get implicit only for merge target. */ (i == 0) ? &(child->implicit_mergeinfo) : NULL, &(child->indirect_mergeinfo), @@ -5391,266 +5391,6 @@ single_file_merge_notify(void *notify_ba notification_receiver(notify_baton, notify, pool); } - -/* A baton for get_mergeinfo_walk_cb. */ -struct get_mergeinfo_walk_baton -{ - /* Array of paths that have explicit mergeinfo and/or are switched. */ - apr_array_header_t *children_with_mergeinfo; - - /* A hash of MERGE_TARGET_ABSPATH's subdirectories' dirents. Maps - const char * absolute working copy paths to dirent hashes as obtained - by svn_io_get_dirents3(). Contents are allocated in CB_POOL. */ - apr_hash_t *subtree_dirents; - - /* A hash to keep track of any subtrees in the merge target which are - unexpectedly missing from disk. Maps const char * absolute working - copy paths to the same. Contents are allocated in CB_POOL. */ - apr_hash_t *missing_subtrees; - - /* Merge source canonical path. */ - const char* merge_src_canon_path; - - /* Information on the merge cascaded from do_directory_merge() */ - const char* merge_target_abspath; - const char *source_root_url; - const char* url1; - const char* url2; - svn_revnum_t revision1; - svn_revnum_t revision2; - - /* merge depth requested. */ - svn_depth_t depth; - - /* RA session and client context cascaded from do_directory_merge() */ - svn_ra_session_t *ra_session; - svn_client_ctx_t *ctx; - - /* Pool from which to allocate new elements of CHILDREN_WITH_MERGEINFO. */ - apr_pool_t *pool; - - /* Pool with a lifetime guaranteed over all the get_mergeinfo_walk_cb - callbacks. */ - apr_pool_t *cb_pool; -}; - -/* Helper for the svn_wc__node_found_func_t callback get_mergeinfo_walk_cb(). - - Checks for issue #2915 subtrees, i.e. those that the WC thinks are on disk - but have been removed due to an OS-level deletion. - - If the supposed working path LOCAL_ABSPATH, of kind KIND, is the root - of a missing subtree, then add a (const char *) WC absolute path to - (const char *) WC absolute path mapping to MISSING_SUBTREES, where the - paths are both a copy of LOCAL_ABSPATH, allocated in RESULT_POOL. - - If LOCAL_ABSPATH is a directory and is not missing from disk, then add - a (const char *) WC absolute path to (svn_io_dirent2_t *) dirent mapping - to SUBTREE_DIRENTS, again allocated in RESULT_POOL (see - svn_io_get_dirents3). - - SCRATCH_POOL is used for temporary allocations. - - Note: Since this is effetively a svn_wc__node_found_func_t callback, it - must be called in depth-first order. */ -static svn_error_t * -record_missing_subtree_roots(const char *local_abspath, - svn_node_kind_t kind, - apr_hash_t *subtree_dirents, - apr_hash_t *missing_subtrees, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - svn_boolean_t missing_subtree_root = FALSE; - - /* Store the dirents for each directory in SUBTREE_DIRENTS. */ - if (kind == svn_node_dir) - { - /* If SUBTREE_DIRENTS is empty LOCAL_ABSPATH is merge target. */ - if (apr_hash_count(subtree_dirents) == 0 - || apr_hash_get(subtree_dirents, - svn_dirent_dirname(local_abspath, - scratch_pool), - APR_HASH_KEY_STRING)) - { - apr_hash_t *dirents; - svn_error_t *err = svn_io_get_dirents3(&dirents, local_abspath, - TRUE, result_pool, - scratch_pool); - if (err) - { - if (APR_STATUS_IS_ENOENT(err->apr_err) - || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)) - { - /* We can't get this directory's dirents, it's missing - from disk. */ - svn_error_clear(err); - missing_subtree_root = TRUE; - } - else - { - return err; - } - } - else - { - apr_hash_set(subtree_dirents, - apr_pstrdup(result_pool, local_abspath), - APR_HASH_KEY_STRING, dirents); - } - } - } - else /* kind != svn_node_dir */ - { - /* Is this non-directory missing from disk? Check LOCAL_ABSPATH's - parent's dirents. */ - apr_hash_t *parent_dirents = apr_hash_get(subtree_dirents, - svn_dirent_dirname(local_abspath, - scratch_pool), - APR_HASH_KEY_STRING); - - /* If the parent_dirents is NULL, then LOCAL_ABSPATH is the - subtree of a missing subtree. Since we only report the roots - of missing subtrees there is nothing more to do in that case. */ - if (parent_dirents) - { - svn_io_dirent2_t *dirent = - apr_hash_get(parent_dirents, - svn_dirent_basename(local_abspath, scratch_pool), - APR_HASH_KEY_STRING); - if (!dirent) - missing_subtree_root = TRUE; - } - } - - if (missing_subtree_root) - { - const char *path = apr_pstrdup(result_pool, local_abspath); - - apr_hash_set(missing_subtrees, path, - APR_HASH_KEY_STRING, path); - } - - return SVN_NO_ERROR; -} - -/* svn_wc__node_found_func_t callback for get_mergeinfo_paths(). - - Given LOCAL_ABSPATH and WB, where WB is struct get_mergeinfo_walk_baton *, - if LOCAL_ABSPATH is switched, has explicit working svn:mergeinfo, is - missing a child due to a sparse checkout, is absent from disk, or is - deleted, then create a svn_client__merge_path_t * representing *PATH, - allocated in BATON->POOL, and push it onto the - WB->CHILDREN_WITH_MERGEINFO array. */ -static svn_error_t * -get_mergeinfo_walk_cb(const char *local_abspath, - svn_node_kind_t kind, - void *walk_baton, - apr_pool_t *scratch_pool) -{ - struct get_mergeinfo_walk_baton *wb = walk_baton; - const svn_string_t *propval = NULL; - svn_boolean_t has_mergeinfo = FALSE; - svn_boolean_t path_is_merge_target = - !svn_path_compare_paths(local_abspath, wb->merge_target_abspath); - const char *abs_parent_path = svn_dirent_dirname(local_abspath, - scratch_pool); - svn_depth_t depth; - svn_boolean_t is_present; - svn_boolean_t deleted; - svn_boolean_t absent; - svn_boolean_t switched; - svn_boolean_t file_external; - svn_boolean_t immediate_child_dir; - - /* TODO(#2843) How to deal with a excluded item on merge? */ - - SVN_ERR(svn_wc__get_mergeinfo_walk_info(&is_present, &deleted, &absent, - &switched, &file_external, &depth, - wb->ctx->wc_ctx, local_abspath, - scratch_pool)); - - /* Ignore LOCAL_ABSPATH if its parent thinks it exists, but it is not - actually present. */ - if (!is_present) - return SVN_NO_ERROR; - - if (! (deleted || absent)) - { - SVN_ERR(svn_wc_prop_get2(&propval, wb->ctx->wc_ctx, local_abspath, - SVN_PROP_MERGEINFO, scratch_pool, scratch_pool)); - if (propval) - has_mergeinfo = TRUE; - - /* Make sure what the WC thinks is present on disk really is. */ - SVN_ERR(record_missing_subtree_roots(local_abspath, kind, - wb->subtree_dirents, - wb->missing_subtrees, - wb->cb_pool, - scratch_pool)); - } - - immediate_child_dir = ((wb->depth == svn_depth_immediates) - &&(kind == svn_node_dir) - && (strcmp(abs_parent_path, - wb->merge_target_abspath) == 0)); - - /* Store PATHs with explict mergeinfo, which are switched, are missing - children due to a sparse checkout, are scheduled for deletion are absent - from the WC, are first level sub directories relative to merge target if - depth is immediates, and/or are file children of the merge target if - depth is files. */ - if (path_is_merge_target - || has_mergeinfo - || deleted - || (switched && !file_external) - || depth == svn_depth_empty - || depth == svn_depth_files - || absent - || immediate_child_dir - || ((wb->depth == svn_depth_files) && - (kind == svn_node_file) && - (strcmp(abs_parent_path, wb->merge_target_abspath) == 0)) - ) - { - svn_client__merge_path_t *child = - apr_pcalloc(wb->pool, sizeof(*child)); - child->abspath = apr_pstrdup(wb->pool, local_abspath); - child->missing_child = (depth == svn_depth_empty - || depth == svn_depth_files - || ((wb->depth == svn_depth_immediates) && - (kind == svn_node_dir) && - (strcmp(abs_parent_path, - wb->merge_target_abspath) == 0))); - child->switched = switched; - child->absent = absent; - child->scheduled_for_deletion = deleted; - - if (propval) - SVN_ERR(svn_mergeinfo__string_has_noninheritable( - &(child->has_noninheritable), propval->data, scratch_pool)); - - /* A little trickery: If PATH doesn't have any mergeinfo or has - only inheritable mergeinfo, we still describe it as having - non-inheritable mergeinfo if it is missing a child. Why? Because - the mergeinfo we'll add to PATH as a result of the merge will need - to be non-inheritable (since PATH is missing children) and doing - this now allows get_mergeinfo_paths() to properly account for PATH's - other children. */ - if (!child->has_noninheritable - && (depth == svn_depth_empty - || depth == svn_depth_files)) - child->has_noninheritable = TRUE; - - child->immediate_child_dir = immediate_child_dir; - - APR_ARRAY_PUSH(wb->children_with_mergeinfo, - svn_client__merge_path_t *) = child; - } - - return SVN_NO_ERROR; -} - /* Compare two svn_client__merge_path_t elements **A and **B, given the addresses of pointers to them. Return an integer less than, equal to, or greater than zero if A sorts before, the same as, or after B, respectively. @@ -5809,6 +5549,87 @@ insert_parent_and_sibs_of_sw_absent_del_ return SVN_NO_ERROR; } +/* pre_merge_status_cb's baton */ +struct pre_merge_status_baton_t +{ + /* const char *absolute_wc_path to svn_depth_t * mapping for depths + of empty, immediates, and files. */ + apr_hash_t *shallow_subtrees; + + /* const char *absolute_wc_path to the same, for all paths missing + from the working copy. */ + apr_hash_t *missing_subtrees; + + /* const char *absolute_wc_path const char * repos relative path, describing + the root of each switched subtree in the working copy and the repository + relative path it is switched to. */ + apr_hash_t *switched_subtrees; + + /* A pool to allocate additions to the above hashes in. */ + apr_pool_t *pool; +}; + +/* A svn_client_status_func_t callback used by get_mergeinfo_paths to gather + all switched, absent, and missing subtrees under a merge target. */ +static svn_error_t * +pre_merge_status_cb(void *baton, + const char *path, + const svn_client_status_t *status, + apr_pool_t *pool) +{ + struct pre_merge_status_baton_t *pmsb = baton; + + if (status->switched) + { + apr_hash_set(pmsb->switched_subtrees, + apr_pstrdup(pmsb->pool, status->local_abspath), + APR_HASH_KEY_STRING, + apr_pstrdup(pmsb->pool, status->repos_relpath)); + } + + if (status->depth == svn_depth_empty + || status->depth == svn_depth_files) + { + const char *local_abspath = apr_pstrdup(pmsb->pool, + status->local_abspath); + svn_depth_t *depth = apr_pcalloc(pmsb->pool, sizeof *depth); + + *depth = status->depth; + apr_hash_set(pmsb->shallow_subtrees, + local_abspath, + APR_HASH_KEY_STRING, + depth); + } + + if (status->node_status == svn_wc_status_missing) + { + svn_boolean_t new_missing_root = TRUE; + apr_hash_index_t *hi; + const char *local_abspath = apr_pstrdup(pmsb->pool, + status->local_abspath); + + for (hi = apr_hash_first(pool, pmsb->missing_subtrees); + hi; + hi = apr_hash_next(hi)) + { + const char *missing_root_path = svn__apr_hash_index_key(hi); + + if (svn_dirent_is_ancestor(missing_root_path, + status->local_abspath)) + { + new_missing_root = FALSE; + break; + } + } + + if (new_missing_root) + apr_hash_set(pmsb->missing_subtrees, local_abspath, + APR_HASH_KEY_STRING, local_abspath); + } + + return SVN_NO_ERROR; +} + /* Helper for do_directory_merge() If HONOR_MERGEINFO is TRUE, then perform a depth first walk of the working @@ -5836,6 +5657,8 @@ insert_parent_and_sibs_of_sw_absent_del_ MERGE_CMD_BATON->TARGET_ABSPATH and DEPTH is svn_depth_immediates. 9) Path is an immediate *file* child of MERGE_CMD_BATON->TARGET_ABSPATH and DEPTH is svn_depth_files. + 10) Path is at a depth of 'empty' or 'files'. + 11) Path is missing from disk (e.g. due to an OS-level deletion). If HONOR_MERGEINFO is FALSE, then create an svn_client__merge_path_t * only for MERGE_CMD_BATON->TARGET_ABSPATH (i.e. only criteria 7 is applied). @@ -5878,37 +5701,88 @@ get_mergeinfo_paths(apr_array_header_t * apr_pool_t *scratch_pool) { int i; - apr_pool_t *iterpool; - struct get_mergeinfo_walk_baton wb = { 0 }; + apr_pool_t *iterpool = NULL; + apr_hash_t *subtrees_with_mergeinfo; + apr_hash_t *absent_subtrees; + apr_hash_t *switched_subtrees; + apr_hash_t *shallow_subtrees; + apr_hash_t *missing_subtrees; + struct pre_merge_status_baton_t pre_merge_status_baton; + svn_opt_revision_t working_revision; - wb.children_with_mergeinfo = children_with_mergeinfo; - wb.cb_pool = svn_pool_create(scratch_pool); - wb.subtree_dirents = apr_hash_make(wb.cb_pool); - wb.missing_subtrees = apr_hash_make(wb.cb_pool); - wb.merge_src_canon_path = merge_src_canon_path; - wb.merge_target_abspath = merge_cmd_baton->target_abspath; - wb.source_root_url = source_root_url; - wb.url1 = url1; - wb.url2 = url2; - wb.revision1 = revision1; - wb.revision2 = revision2; - wb.depth = depth; - wb.ra_session = ra_session; - wb.ctx = merge_cmd_baton->ctx; - wb.pool = result_pool; - - /* Cover cases 1), 2), and 6), 7), 8), 9), and 10) by walking the WC to get - all paths which have mergeinfo and/or are switched or are absent from - disk or is the target of the merge. */ - SVN_ERR(svn_wc__node_walk_children(merge_cmd_baton->ctx->wc_ctx, - merge_cmd_baton->target_abspath, TRUE, - get_mergeinfo_walk_cb, &wb, - honor_mergeinfo ? depth : svn_depth_empty, - merge_cmd_baton->ctx->cancel_func, - merge_cmd_baton->ctx->cancel_baton, - scratch_pool)); + working_revision.kind = svn_opt_revision_working; - if (apr_hash_count(wb.missing_subtrees)) + /* Case 1: Subtrees with explicit mergeinfo. */ + SVN_ERR(svn_client_propget3(&subtrees_with_mergeinfo, SVN_PROP_MERGEINFO, + merge_cmd_baton->target_abspath, &working_revision, + &working_revision, NULL, depth, NULL, + merge_cmd_baton->ctx, scratch_pool)); + + if (subtrees_with_mergeinfo) + { + apr_hash_index_t *hi; + + if (!iterpool) + iterpool = svn_pool_create(scratch_pool); + + for (hi = apr_hash_first(scratch_pool, subtrees_with_mergeinfo); + hi; + hi = apr_hash_next(hi)) + { + svn_mergeinfo_t child_pre_merge_mergeinfo; + const char *wc_path = svn__apr_hash_index_key(hi); + svn_string_t *mergeinfo_string = svn__apr_hash_index_val(hi); + svn_client__merge_path_t *mergeinfo_child = + apr_pcalloc(result_pool, sizeof(*mergeinfo_child)); + + svn_pool_clear(iterpool); + mergeinfo_child->abspath = apr_pstrdup(result_pool, wc_path); + SVN_ERR(svn_mergeinfo_parse(&child_pre_merge_mergeinfo, + mergeinfo_string->data, result_pool)); + + /* Stash this child's pre-existing mergeinfo. */ + mergeinfo_child->pre_merge_mergeinfo = child_pre_merge_mergeinfo; + + /* Note if this child has non-inheritable mergeinfo */ + SVN_ERR(svn_mergeinfo__string_has_noninheritable( + &(mergeinfo_child->has_noninheritable), mergeinfo_string->data, + iterpool)); + + insert_child_to_merge(children_with_mergeinfo, mergeinfo_child, + result_pool); + } + + /* Sort CHILDREN_WITH_MERGEINFO by each child's path (i.e. as per + compare_merge_path_t_as_paths). Any subsequent insertions of new + children with insert_child_to_merge() require this ordering. */ + qsort(children_with_mergeinfo->elts, + children_with_mergeinfo->nelts, + children_with_mergeinfo->elt_size, + compare_merge_path_t_as_paths); + } + + /* Case 2: Switched subtrees + Case 10: Paths at depths of 'empty' or 'files' + Case 11: Paths missing from disk */ + switched_subtrees = apr_hash_make(scratch_pool); + pre_merge_status_baton.switched_subtrees = switched_subtrees; + shallow_subtrees = apr_hash_make(scratch_pool); + pre_merge_status_baton.shallow_subtrees = shallow_subtrees; + missing_subtrees = apr_hash_make(scratch_pool); + pre_merge_status_baton.missing_subtrees = missing_subtrees; + pre_merge_status_baton.pool = scratch_pool; + SVN_ERR(svn_client_status5(NULL, merge_cmd_baton->ctx, + merge_cmd_baton->target_abspath, + &working_revision, depth, + TRUE, FALSE, TRUE, TRUE, FALSE, NULL, + pre_merge_status_cb, + &pre_merge_status_baton, + scratch_pool)); + + /* Issue #2915: Raise an error describing the roots of any missing + subtrees, i.e. those that the WC thinks are on disk but have been + removed outside of Subversion. */ + if (apr_hash_count(missing_subtrees)) { apr_hash_index_t *hi; svn_stringbuf_t *missing_subtree_err_buf = @@ -5918,7 +5792,7 @@ get_mergeinfo_paths(apr_array_header_t * iterpool = svn_pool_create(scratch_pool); - for (hi = apr_hash_first(scratch_pool, wb.missing_subtrees); + for (hi = apr_hash_first(scratch_pool, missing_subtrees); hi; hi = apr_hash_next(hi)) { @@ -5929,25 +5803,189 @@ get_mergeinfo_paths(apr_array_header_t * svn_stringbuf_appendcstr(missing_subtree_err_buf, "\n"); } - return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, - NULL, missing_subtree_err_buf->data); - } - - /* This pool is only needed across all the callbacks to detect - missing subtrees. */ - svn_pool_destroy(wb.cb_pool); - - /* CHILDREN_WITH_MERGEINFO must be in depth first order, but the node - walk code returns nodes in a non particular order. Also, we may need - to add elements to the array to cover case 3) through 5) from the - docstring. If so, it is more efficient to find and insert these paths - if the sibling paths are in a guaranteed depth-first order. For the - first reason we sort the array, for the second reason we do it now - rather than at the end of this function. */ - qsort(children_with_mergeinfo->elts, - children_with_mergeinfo->nelts, - children_with_mergeinfo->elt_size, - compare_merge_path_t_as_paths); + return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, + NULL, missing_subtree_err_buf->data); + } + + if (apr_hash_count(switched_subtrees)) + { + apr_hash_index_t *hi; + + for (hi = apr_hash_first(scratch_pool, switched_subtrees); + hi; + hi = apr_hash_next(hi)) + { + const char *wc_path = svn__apr_hash_index_key(hi); + svn_client__merge_path_t *child = get_child_with_mergeinfo( + children_with_mergeinfo, wc_path); + + if (child) + { + child->switched = TRUE; + } + else + { + svn_client__merge_path_t *switched_child = + apr_pcalloc(result_pool, sizeof(*switched_child)); + switched_child->abspath = apr_pstrdup(result_pool, wc_path); + switched_child->switched = TRUE; + insert_child_to_merge(children_with_mergeinfo, switched_child, + result_pool); + } + } + } + + if (apr_hash_count(shallow_subtrees)) + { + apr_hash_index_t *hi; + + for (hi = apr_hash_first(scratch_pool, shallow_subtrees); + hi; + hi = apr_hash_next(hi)) + { + svn_boolean_t new_shallow_child = FALSE; + const char *wc_path = svn__apr_hash_index_key(hi); + svn_depth_t *child_depth = svn__apr_hash_index_val(hi); + svn_client__merge_path_t *shallow_child = get_child_with_mergeinfo( + children_with_mergeinfo, wc_path); + + if (shallow_child) + { + if (*child_depth == svn_depth_empty + || *child_depth == svn_depth_files) + shallow_child->missing_child = TRUE; + } + else + { + shallow_child = apr_pcalloc(result_pool, + sizeof(*shallow_child)); + new_shallow_child = TRUE; + shallow_child->abspath = apr_pstrdup(result_pool, wc_path); + + if (*child_depth == svn_depth_empty + || *child_depth == svn_depth_files) + shallow_child->missing_child = TRUE; + } + + /* A little trickery: If PATH doesn't have any mergeinfo or has + only inheritable mergeinfo, we still describe it as having + non-inheritable mergeinfo if it is missing a child due to + a shallow depth. Why? Because the mergeinfo we'll add to PATH + to describe the merge must be non-inheritable, so PATH's missing + children don't inherit it. Marking these PATHs as non- + inheritable allows the logic for case 3 to properly account + for PATH's children. */ + if (!shallow_child->has_noninheritable + && (*child_depth == svn_depth_empty + || *child_depth == svn_depth_files)) + { + shallow_child->has_noninheritable = TRUE; + } + + if (new_shallow_child) + insert_child_to_merge(children_with_mergeinfo, shallow_child, + result_pool); + } + } + + /* Case 6: Paths absent from disk due to an authz restrictions. */ + SVN_ERR(svn_wc__get_absent_subtrees(&absent_subtrees, + merge_cmd_baton->ctx->wc_ctx, + merge_cmd_baton->target_abspath, + result_pool, scratch_pool)); + if (absent_subtrees) + { + apr_hash_index_t *hi; + + for (hi = apr_hash_first(scratch_pool, absent_subtrees); + hi; + hi = apr_hash_next(hi)) + { + const char *wc_path = svn__apr_hash_index_key(hi); + svn_client__merge_path_t *child = get_child_with_mergeinfo( + children_with_mergeinfo, wc_path); + + if (child) + { + child->absent = TRUE; + } + else + { + svn_client__merge_path_t *absent_child = + apr_pcalloc(result_pool, sizeof(*absent_child)); + absent_child->abspath = apr_pstrdup(result_pool, wc_path); + absent_child->absent = TRUE; + insert_child_to_merge(children_with_mergeinfo, absent_child, + result_pool); + } + } + } + + /* Case 7: The merge target MERGE_CMD_BATON->TARGET_ABSPATH is always + present. */ + if (!get_child_with_mergeinfo(children_with_mergeinfo, + merge_cmd_baton->target_abspath)) + { + svn_client__merge_path_t *target_child = + apr_pcalloc(result_pool, sizeof(*target_child)); + target_child->abspath = apr_pstrdup(result_pool, + merge_cmd_baton->target_abspath); + insert_child_to_merge(children_with_mergeinfo, target_child, + result_pool); + } + + /* Case 8: Path is an immediate *directory* child of + MERGE_CMD_BATON->TARGET_ABSPATH and DEPTH is svn_depth_immediates. + + Case 9: Path is an immediate *file* child of + MERGE_CMD_BATON->TARGET_ABSPATH and DEPTH is svn_depth_files. */ + if (depth == svn_depth_immediates || depth == svn_depth_files) + { + int i; + const apr_array_header_t *immediate_children; + + SVN_ERR(svn_wc__node_get_children_of_working_node( + &immediate_children, merge_cmd_baton->ctx->wc_ctx, + merge_cmd_baton->target_abspath, FALSE, scratch_pool, scratch_pool)); + + if (!iterpool) + iterpool = svn_pool_create(scratch_pool); + + for (i = 0; i < immediate_children->nelts; i++) + { + const char *immediate_child_abspath = + APR_ARRAY_IDX(immediate_children, i, const char *); + svn_node_kind_t immediate_child_kind; + + svn_pool_clear(iterpool); + SVN_ERR(svn_wc_read_kind(&immediate_child_kind, + merge_cmd_baton->ctx->wc_ctx, + immediate_child_abspath, FALSE, + iterpool)); + if ((immediate_child_kind == svn_node_dir + && depth == svn_depth_immediates) + || (immediate_child_kind == svn_node_file + && depth == svn_depth_files)) + { + if (!get_child_with_mergeinfo(children_with_mergeinfo, + immediate_child_abspath)) + { + svn_client__merge_path_t *immediate_child = + apr_pcalloc(result_pool, sizeof(*immediate_child)); + + immediate_child->abspath = + apr_pstrdup(result_pool, immediate_child_abspath); + + if (immediate_child_kind == svn_node_dir + && depth == svn_depth_immediates) + immediate_child->immediate_child_dir = TRUE; + + insert_child_to_merge(children_with_mergeinfo, + immediate_child, result_pool); + } + } + } + } /* If DEPTH isn't empty then cover cases 3), 4), and 5), possibly adding elements to CHILDREN_WITH_MERGEINFO. */ Modified: subversion/trunk/subversion/libsvn_wc/node.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/node.c?rev=1087866&r1=1087865&r2=1087866&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/node.c (original) +++ subversion/trunk/subversion/libsvn_wc/node.c Fri Apr 1 19:32:11 2011 @@ -1591,6 +1591,20 @@ svn_wc__node_get_lock_tokens_recursive(a } svn_error_t * +svn_wc__get_absent_subtrees(apr_hash_t **absent_subtrees, + svn_wc_context_t *wc_ctx, + const char *local_abspath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + return svn_error_return(svn_wc__db_get_absent_subtrees(absent_subtrees, + wc_ctx->db, + local_abspath, + result_pool, + scratch_pool)); +} + +svn_error_t * svn_wc__node_get_commit_status(svn_node_kind_t *kind, svn_boolean_t *added, svn_boolean_t *deleted, Modified: subversion/trunk/subversion/libsvn_wc/wc-queries.sql URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc-queries.sql?rev=1087866&r1=1087865&r2=1087866&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/wc-queries.sql (original) +++ subversion/trunk/subversion/libsvn_wc/wc-queries.sql Fri Apr 1 19:32:11 2011 @@ -686,6 +686,12 @@ SELECT local_relpath FROM nodes WHERE wc_id = ?1 AND (local_relpath = ?2 OR local_relpath LIKE ?3 ESCAPE '#') AND op_depth = 0 AND presence = 'absent' LIMIT 1 +/* ### Select all absent nodes. */ +-- STMT_SELECT_ALL_ABSENT_NODES +SELECT local_relpath FROM nodes +WHERE wc_id = ?1 AND (local_relpath = ?2 OR local_relpath LIKE ?3 ESCAPE '#') + AND op_depth = 0 AND presence = 'absent' + /* ### Why can't this query not just use the BASE repository location values, instead of taking 3 additional parameters?! */ -- STMT_INSERT_WORKING_NODE_COPY_FROM_BASE Modified: subversion/trunk/subversion/libsvn_wc/wc_db.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.c?rev=1087866&r1=1087865&r2=1087866&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/wc_db.c (original) +++ subversion/trunk/subversion/libsvn_wc/wc_db.c Fri Apr 1 19:32:11 2011 @@ -10000,6 +10000,56 @@ svn_wc__db_has_switched_subtrees(svn_boo scratch_pool)); } +svn_error_t * +svn_wc__db_get_absent_subtrees(apr_hash_t **absent_subtrees, + svn_wc__db_t *db, + const char *local_abspath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_wc__db_wcroot_t *wcroot; + const char *local_relpath; + svn_sqlite__stmt_t *stmt; + const char *wcroot_repos_relpath; + svn_boolean_t have_row; + + SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); + SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, + db, local_abspath, + scratch_pool, scratch_pool)); + VERIFY_USABLE_WCROOT(wcroot); + + SVN_ERR(read_info(NULL, NULL, NULL, &wcroot_repos_relpath, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + wcroot, "", scratch_pool, scratch_pool)); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_ALL_ABSENT_NODES)); + SVN_ERR(svn_sqlite__bindf(stmt, "iss", + wcroot->wc_id, + local_relpath, + construct_like_arg(local_relpath, + scratch_pool))); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + + if (have_row) + *absent_subtrees = apr_hash_make(result_pool); + else + *absent_subtrees = NULL; + + while (have_row) + { + const char *abs_path = + svn_dirent_join(wcroot->abspath, + svn_sqlite__column_text(stmt, 0, scratch_pool), + result_pool); + apr_hash_set(*absent_subtrees, abs_path, APR_HASH_KEY_STRING, abs_path); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + } + + SVN_ERR(svn_sqlite__reset(stmt)); + return SVN_NO_ERROR; +} /* Like svn_wc__db_has_local_mods(), * but accepts a WCROOT/LOCAL_RELPATH pair. Modified: subversion/trunk/subversion/libsvn_wc/wc_db.h URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.h?rev=1087866&r1=1087865&r2=1087866&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/wc_db.h (original) +++ subversion/trunk/subversion/libsvn_wc/wc_db.h Fri Apr 1 19:32:11 2011 @@ -2669,6 +2669,19 @@ svn_wc__db_has_switched_subtrees(svn_boo const char *trail_url, apr_pool_t *scratch_pool); +/* Set @a *absent_subtrees to a hash mapping <tt>const char *</tt> local + * absolute paths to <tt>const char *</tt> local absolute paths for every + * path at or under @a local_abspath in @a db which are absent (excluded + * by authz). If no absent paths are found then @a *absent_subtrees is set + * to @c NULL. Allocate the hash and all items therein from @a result_pool. + */ +svn_error_t * +svn_wc__db_get_absent_subtrees(apr_hash_t **absent_subtrees, + svn_wc__db_t *db, + const char *local_abspath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + /* Indicate in *IS_MODIFIED whether the working copy has local modifications, * using DB. Use SCRATCH_POOL for temporary allocations. *