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.
*