You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by pb...@apache.org on 2011/04/04 22:16:16 UTC
svn commit: r1088773 - /subversion/trunk/subversion/libsvn_client/merge.c
Author: pburba
Date: Mon Apr 4 20:16:16 2011
New Revision: 1088773
URL: http://svn.apache.org/viewvc?rev=1088773&view=rev
Log:
Leverage an existing status check to avoid a redundant query to detect mixed-
revision WCs during sync-type (i.e. non-reintegrate) merge-tracking aware
merges.
When r1087866 removed the final node walker from the merge tracking code, it
added a single call to svn_client_status5 to get numerous bits of info
relevant to merge tracking. One bit of information it could also derive
rather cheaply is if the target has mixed-revisions. We were checking for
this in libsvn_client/merge.c:ensure_wc_is_suitable_merge_target(). With
this change we still use that function for non-merge-tracking merges and
reintegrate merges, but for sync-style merge-tracking merges we use the
status call and get a bit faster.
* subversion/libsvn_client/merge.c
(pre_merge_status_baton_t): Add members to track min and max revision in
the merge target.
(pre_merge_status_cb): Set new pre_merge_status_baton_t members.
(get_mergeinfo_paths): Leverage existing svn_client_status5 call to cheaply
check for mixed-revision working copies.
(do_directory_merge): Add ALLOW_MIX_REV argument and pass it along.
(do_merge): Remove the check for mergeinfo capable servers and instead pass
that information in as a boolean argument. Add ALLOW_MIX_REV argument.
(merge_cousins_and_supplement_mergeinfo): Add ALLOW_MIX_REV and
MERGEINFO_CAPABLE_SERVER arguments.
(merge_locked): Check for a mergeinfo capable server here
instead of waiting until do_merge().
(merge_reintegrate_locked): Check for a mergeinfo capable server here
instead of waiting until do_merge(). Note: Unfortunately we can't skip
the check for mixed-rev working copies because of the way reintegrate
merges are implemented; as three separate merges, the first of which
is a merge-tracking unaware merge and thus cannot leverage the check in
get_mergeinfo_paths(). Reimplementing reintegrate merges to spare this
single query doesn't strike me as a particularly good use of time at
the moment.
(merge_peg_locked): Check for a mergeinfo capable server here instead of
waiting until do_merge(). That is the final bit of information needed to
tell if this is a merge-tracking aware merge, which allows us to
potentially skip the redundant check for mixed-revs here.
Modified:
subversion/trunk/subversion/libsvn_client/merge.c
Modified: subversion/trunk/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/merge.c?rev=1088773&r1=1088772&r2=1088773&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/merge.c (original)
+++ subversion/trunk/subversion/libsvn_client/merge.c Mon Apr 4 20:16:16 2011
@@ -5593,12 +5593,18 @@ struct pre_merge_status_baton_t
relative path it is switched to. */
apr_hash_t *switched_subtrees;
+ /* Minimum and maximum working revisions found in the working copy. */
+ svn_revnum_t min_rev;
+ svn_revnum_t max_rev;
+
/* 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. */
+ all switched, absent, and missing subtrees under a merge target as well
+ as the minimum and maximum revision for any non-external with
+ svn_wc_status_normal or svn_wc_status_incomplete. */
static svn_error_t *
pre_merge_status_cb(void *baton,
const char *path,
@@ -5655,6 +5661,21 @@ pre_merge_status_cb(void *baton,
APR_HASH_KEY_STRING, local_abspath);
}
+ if (status->versioned
+ && SVN_IS_VALID_REVNUM(status->revision)
+ && !status->file_external
+ && (status->node_status == svn_wc_status_normal
+ || status->node_status == svn_wc_status_incomplete))
+ {
+ if (!SVN_IS_VALID_REVNUM(pmsb->min_rev)
+ || status->revision < pmsb->min_rev)
+ pmsb->min_rev = status->revision;
+
+ if (!SVN_IS_VALID_REVNUM(pmsb->max_rev)
+ || status->revision > pmsb->max_rev)
+ pmsb->max_rev = status->revision;
+ }
+
return SVN_NO_ERROR;
}
@@ -5691,6 +5712,8 @@ pre_merge_status_cb(void *baton,
If subtrees within the requested DEPTH are unexpectedly missing disk,
then raise SVN_ERR_CLIENT_NOT_READY_TO_MERGE.
+ Handle ALLOW_MIX_REV as per svn_client_merge4().
+
Store the svn_client__merge_path_t *'s in *CHILDREN_WITH_MERGEINFO in
depth-first order based on the svn_client__merge_path_t *s path member as
sorted by svn_path_compare_paths(). Set the remaining_ranges field of each
@@ -5719,6 +5742,7 @@ get_mergeinfo_paths(apr_array_header_t *
const char *url2,
svn_revnum_t revision1,
svn_revnum_t revision2,
+ svn_boolean_t allow_mixed_rev,
svn_ra_session_t *ra_session,
svn_depth_t depth,
apr_pool_t *result_pool,
@@ -5803,6 +5827,14 @@ get_mergeinfo_paths(apr_array_header_t *
&pre_merge_status_baton,
scratch_pool));
+ if (!allow_mixed_rev
+ && pre_merge_status_baton.min_rev != pre_merge_status_baton.max_rev)
+ return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
+ _("Cannot merge into mixed-revision working "
+ "copy [%lu:%lu]; try updating first"),
+ pre_merge_status_baton.min_rev,
+ pre_merge_status_baton.max_rev);
+
/* 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. */
@@ -8165,7 +8197,7 @@ remove_noop_subtree_ranges(const char *u
paths and the values are the new mergeinfos for each. Allocate additions
to RESULT_CATALOG in pool which RESULT_CATALOG was created in.
- Handle DEPTH as documented for svn_client_merge3().
+ Handle ALLOW_MIX_REV and DEPTH as per svn_client_merge4().
NOTE: This is a wrapper around drive_merge_report_editor() which
handles the complexities inherent to situations where a given
@@ -8180,6 +8212,7 @@ do_directory_merge(svn_mergeinfo_catalog
svn_revnum_t revision2,
const char *target_abspath,
svn_depth_t depth,
+ svn_boolean_t allow_mixed_rev,
svn_boolean_t squelch_mergeinfo_notifications,
notification_receiver_baton_t *notify_b,
merge_cmd_baton_t *merge_b,
@@ -8242,7 +8275,8 @@ do_directory_merge(svn_mergeinfo_catalog
SVN_ERR(get_mergeinfo_paths(notify_b->children_with_mergeinfo, merge_b,
mergeinfo_path, source_root_url,
url1, url2, revision1, revision2,
- ra_session, depth, pool, pool));
+ allow_mixed_rev, ra_session, depth,
+ pool, pool));
/* The first item from the NOTIFY_B->CHILDREN_WITH_MERGEINFO is always
the target thanks to depth-first ordering. */
@@ -8573,6 +8607,11 @@ ensure_ra_session_url(svn_ra_session_t *
by the merge. Keys and values of the hash are both (const char *)
absolute paths. The contents of the hash are allocated in POOL.
+ MERGEINFO_CAPABLE_SERVER is true if the merge source server is
+ capable of Merge Tracking.
+
+ Handle ALLOW_MIX_REV as per svn_client_merge4().
+
If SOURCES_ANCESTRAL is set, then for every merge source in
MERGE_SOURCES, the "left" and "right" side of the merge source are
ancestrally related. (See 'MERGEINFO MERGE SOURCE NORMALIZATION'
@@ -8620,6 +8659,8 @@ do_merge(apr_hash_t **modified_subtrees,
svn_mergeinfo_catalog_t result_catalog,
const apr_array_header_t *merge_sources,
const char *target_abspath,
+ svn_boolean_t mergeinfo_capable_server,
+ svn_boolean_t allow_mixed_rev,
svn_boolean_t sources_ancestral,
svn_boolean_t sources_related,
svn_boolean_t same_repos,
@@ -8642,7 +8683,6 @@ do_merge(apr_hash_t **modified_subtrees,
svn_config_t *cfg;
const char *diff3_cmd;
int i;
- svn_boolean_t checked_mergeinfo_capability = FALSE;
svn_ra_session_t *ra_session1 = NULL, *ra_session2 = NULL;
svn_node_kind_t target_kind;
@@ -8769,16 +8809,7 @@ do_merge(apr_hash_t **modified_subtrees,
merge_cmd_baton.paths_with_deleted_mergeinfo = NULL;
merge_cmd_baton.ra_session1 = ra_session1;
merge_cmd_baton.ra_session2 = ra_session2;
-
- /* Populate the portions of the merge context baton that require
- an RA session to set, but shouldn't be reset for each iteration. */
- if (! checked_mergeinfo_capability)
- {
- SVN_ERR(svn_ra_has_capability(ra_session1,
- &merge_cmd_baton.mergeinfo_capable,
- SVN_RA_CAPABILITY_MERGEINFO, subpool));
- checked_mergeinfo_capability = TRUE;
- }
+ merge_cmd_baton.mergeinfo_capable = mergeinfo_capable_server;
/* Call our merge helpers based on TARGET_ABSPATH's kind. */
if (target_kind == svn_node_file)
@@ -8794,7 +8825,8 @@ do_merge(apr_hash_t **modified_subtrees,
{
SVN_ERR(do_directory_merge(result_catalog,
url1, rev1, url2, rev2, target_abspath,
- depth, squelch_mergeinfo_notifications,
+ depth, allow_mixed_rev,
+ squelch_mergeinfo_notifications,
¬ify_baton, &merge_cmd_baton,
subpool));
@@ -8852,8 +8884,9 @@ do_merge(apr_hash_t **modified_subtrees,
The merge is between URL1@REV1 (in URL1_RA_SESSION1) and URL2@REV2 (in
URL2_RA_SESSION2); YC_REV is their youngest common ancestor.
SOURCE_REPOS_ROOT and WC_REPOS_ROOT are the repository roots of the
- source URL and the target working copy. Other arguments are as in
- all of the public merge APIs.
+ source URL and the target working copy. MERGEINFO_CAPABLE_SERVER is
+ true if the merge source server is capable of Merge Tracking. Other
+ arguments are as in all of the public merge APIs.
*USE_SLEEP will be set TRUE if a sleep is required to ensure timestamp
integrity, *USE_SLEEP will be unchanged if no sleep is required.
@@ -8870,6 +8903,8 @@ merge_cousins_and_supplement_mergeinfo(c
const char *source_repos_root,
const char *wc_repos_root,
svn_depth_t depth,
+ svn_boolean_t mergeinfo_capable_server,
+ svn_boolean_t allow_mixed_rev,
svn_boolean_t ignore_ancestry,
svn_boolean_t force,
svn_boolean_t record_only,
@@ -8941,10 +8976,10 @@ merge_cousins_and_supplement_mergeinfo(c
faux_source->rev2 = rev2;
APR_ARRAY_PUSH(faux_sources, merge_source_t *) = faux_source;
SVN_ERR(do_merge(&modified_subtrees, NULL, faux_sources, target_abspath,
- FALSE, TRUE, same_repos,
- ignore_ancestry, force, dry_run, FALSE, NULL, TRUE,
- FALSE, depth, merge_options, use_sleep, ctx,
- pool));
+ mergeinfo_capable_server, allow_mixed_rev, FALSE, TRUE,
+ same_repos, ignore_ancestry, force, dry_run, FALSE,
+ NULL, TRUE, FALSE, depth, merge_options, use_sleep,
+ ctx, pool));
}
else if (! same_repos)
{
@@ -8974,12 +9009,13 @@ merge_cousins_and_supplement_mergeinfo(c
ctx->notify_func2(ctx->notify_baton2, notify, pool);
SVN_ERR(do_merge(NULL, add_result_catalog, add_sources, target_abspath,
- TRUE, TRUE, same_repos,
- ignore_ancestry, force, dry_run, TRUE,
+ mergeinfo_capable_server, allow_mixed_rev, TRUE, TRUE,
+ same_repos, ignore_ancestry, force, dry_run, TRUE,
modified_subtrees, TRUE,
TRUE, depth, merge_options, use_sleep, ctx, pool));
SVN_ERR(do_merge(NULL, remove_result_catalog, remove_sources,
- target_abspath, TRUE, TRUE, same_repos,
+ target_abspath, mergeinfo_capable_server,
+ allow_mixed_rev, TRUE, TRUE, same_repos,
ignore_ancestry, force, dry_run, TRUE,
modified_subtrees, TRUE,
TRUE, depth, merge_options, use_sleep, ctx, pool));
@@ -9145,6 +9181,7 @@ merge_locked(const char *source1,
svn_boolean_t same_repos;
const char *source_repos_uuid1, *source_repos_uuid2;
svn_node_kind_t target_kind;
+ svn_boolean_t mergeinfo_capable_server;
/* Make sure the target is really there. */
SVN_ERR(svn_io_check_path(target_abspath, &target_kind, scratch_pool));
@@ -9220,6 +9257,9 @@ merge_locked(const char *source1,
NULL, NULL, FALSE, TRUE,
ctx, sesspool));
+ SVN_ERR(svn_ra_has_capability(ra_session1, &mergeinfo_capable_server,
+ SVN_RA_CAPABILITY_MERGEINFO, scratch_pool));
+
/* Resolve revisions to real numbers. */
SVN_ERR(svn_client__get_revision_number(&rev1, &youngest_rev, ctx->wc_ctx,
NULL, ra_session1, revision1,
@@ -9332,20 +9372,23 @@ merge_locked(const char *source1,
side, and merge the right. */
else
{
- err = merge_cousins_and_supplement_mergeinfo(target_abspath,
- ra_session1,
- ra_session2,
- URL1, rev1,
- URL2, rev2,
- yc_rev,
- source_repos_root,
- wc_repos_root,
- depth,
- ignore_ancestry, force,
- record_only, dry_run,
- merge_options,
- &use_sleep, ctx,
- scratch_pool);
+ err = merge_cousins_and_supplement_mergeinfo(
+ target_abspath,
+ ra_session1,
+ ra_session2,
+ URL1, rev1,
+ URL2, rev2,
+ yc_rev,
+ source_repos_root,
+ wc_repos_root,
+ depth,
+ mergeinfo_capable_server,
+ allow_mixed_rev,
+ ignore_ancestry, force,
+ record_only, dry_run,
+ merge_options,
+ &use_sleep, ctx,
+ scratch_pool);
if (err)
{
if (use_sleep)
@@ -9378,8 +9421,8 @@ merge_locked(const char *source1,
svn_pool_destroy(sesspool);
err = do_merge(NULL, NULL, merge_sources, target_abspath,
- ancestral, related, same_repos,
- ignore_ancestry, force, dry_run,
+ mergeinfo_capable_server, allow_mixed_rev, ancestral,
+ related, same_repos, ignore_ancestry, force, dry_run,
record_only, NULL, FALSE, FALSE, depth, merge_options,
&use_sleep, ctx, scratch_pool);
@@ -10365,6 +10408,7 @@ merge_reintegrate_locked(const char *sou
const char *target_url;
svn_revnum_t target_base_rev;
svn_node_kind_t kind;
+ svn_boolean_t mergeinfo_capable_server;
/* Make sure the target is really there. */
SVN_ERR(svn_io_check_path(target_abspath, &kind, scratch_pool));
@@ -10449,6 +10493,9 @@ merge_reintegrate_locked(const char *sou
url2, NULL, NULL, FALSE, FALSE,
ctx, scratch_pool));
+ SVN_ERR(svn_ra_has_capability(target_ra_session, &mergeinfo_capable_server,
+ SVN_RA_CAPABILITY_MERGEINFO, scratch_pool));
+
SVN_ERR(svn_client__get_revision_number(&rev2, NULL, ctx->wc_ctx,
"",
source_ra_session, peg_revision,
@@ -10539,9 +10586,10 @@ merge_reintegrate_locked(const char *sou
source_repos_root,
wc_repos_root,
svn_depth_infinity,
- FALSE, FALSE, FALSE, dry_run,
- merge_options, &use_sleep,
- ctx, scratch_pool);
+ mergeinfo_capable_server,
+ FALSE, FALSE, FALSE, FALSE,
+ dry_run, merge_options,
+ &use_sleep, ctx, scratch_pool);
if (use_sleep)
svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
@@ -10636,6 +10684,7 @@ merge_peg_locked(const char *source,
svn_error_t *err;
svn_boolean_t same_repos;
svn_node_kind_t target_kind;
+ svn_boolean_t mergeinfo_capable_server;
SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
@@ -10663,10 +10712,6 @@ merge_peg_locked(const char *source,
_("Merge target '%s' does not exist in the "
"working copy"), target_abspath));
- SVN_ERR(ensure_wc_is_suitable_merge_target(target_abspath, ctx,
- allow_mixed_rev, TRUE, TRUE,
- scratch_pool));
-
/* Determine the working copy target's repository root URL. */
working_rev.kind = svn_opt_revision_working;
SVN_ERR(svn_client__get_repos_root(&wc_repos_root, target_abspath,
@@ -10686,6 +10731,9 @@ merge_peg_locked(const char *source,
ranges_to_merge, ra_session, ctx,
scratch_pool));
+ SVN_ERR(svn_ra_has_capability(ra_session, &mergeinfo_capable_server,
+ SVN_RA_CAPABILITY_MERGEINFO, scratch_pool));
+
/* Check for same_repos. */
if (strcmp(wc_repos_root, source_repos_root) != 0)
{
@@ -10703,10 +10751,24 @@ merge_peg_locked(const char *source,
/* We're done with our little RA session. */
svn_pool_destroy(sesspool);
+ /* If this is a merge-tracking aware merge then postpone checking for a
+ mixed-revision working copy, this is already done as part of any
+ merge-tracking aware merge, see get_mergeinfo_paths(). */
+ if (!(!allow_mixed_rev
+ && !ignore_ancestry
+ && mergeinfo_capable_server
+ && same_repos))
+ {
+ SVN_ERR(ensure_wc_is_suitable_merge_target(target_abspath, ctx,
+ allow_mixed_rev, TRUE, TRUE,
+ scratch_pool));
+ }
+
/* Do the real merge! (We say with confidence that our merge
sources are both ancestral and related.) */
err = do_merge(NULL, NULL, merge_sources, target_abspath,
- TRUE, TRUE, same_repos, ignore_ancestry, force, dry_run,
+ mergeinfo_capable_server, allow_mixed_rev, TRUE, TRUE,
+ same_repos, ignore_ancestry, force, dry_run,
record_only, NULL, FALSE, FALSE, depth, merge_options,
&use_sleep, ctx, scratch_pool);