[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: [PATCH] Implicit detection of merge source URL and merge revision range

From: Daniel Rall <dlr_at_collab.net>
Date: 2007-04-25 07:21:02 CEST

Do we also need this in libsvn_client/merge.c:do_single_file_merge()?
I recall that you might be able to switch a file to a directory; would
that function kick in under those circumstances?

On Tue, 24 Apr 2007, Paul Burba wrote:

> Hi Kamesh,
>
> Attached are my tweaks to Dan's version of your patch which fixes the
> problem with merge_test-48 and also allows compilation on Win32 (as
> previously mentioned in
> http://svn.haxx.se/dev/archive-2007-04/0676.shtml).
>
> The fix for merge test 48 is here, in merge.c:do_merge():
>
> const svn_wc_entry_t *entry;
> int i;
> svn_boolean_t inherited;
> + svn_opt_revision_t assumed_initial_revision1,
> assumed_initial_revision2;
> apr_size_t target_count, merge_target_count;
>
> - ENSURE_VALID_REVISION_KINDS(initial_revision1->kind,
> - initial_revision2->kind);
> + /* Establish first RA session to initial_URL1. */
> + SVN_ERR(svn_client__open_ra_session_internal(&ra_session,
> initial_URL1, NULL,
> + NULL, NULL, FALSE, TRUE,
> + ctx, pool));
> + SVN_ERR(assume_default_rev_range(initial_revision1,
> + &assumed_initial_revision1,
> + initial_revision2,
> + &assumed_initial_revision2,
> + ra_session,
> + pool));
>
> + ENSURE_VALID_REVISION_KINDS(assumed_initial_revision1.kind,
> + assumed_initial_revision2.kind);
> +
> +
> /* If we are performing a pegged merge, we need to find out what our
> actual URLs will be. */
> if (peg_revision->kind != svn_opt_revision_unspecified)
> @@ -1673,10 +1740,13 @@
> initial_path2 ? initial_path2
> : initial_URL2,
> peg_revision,
> - initial_revision1,
> - initial_revision2,
> + &assumed_initial_revision1,
> + &assumed_initial_revision2,
> ctx, pool));
>
> + /* Reparent session if actual URL changed. */
> + if (strcmp(initial_URL1, URL1))
> + SVN_ERR(svn_ra_reparent(ra_session, URL1, pool));
> +
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>
> Merge test 48 failed, not because of anything to do with switched paths,
> but because the initial session opened in do_merge() was for the wrong
> URL for the revision being merged in. Reparenting the session if the
> actual URL was different solved the problem.
>
> Anyway, per discussion with Dan in IRC, please go ahead and commit this
> version of the patch.
>
> Thanks,
>
> Paul B.
>
> > -----Original Message-----
> > From: Daniel Rall [mailto:dlr@collab.net]
> > Sent: Tuesday, April 24, 2007 7:24 PM
> > To: Kamesh Jayachandran
> > Cc: dev@subversion.tigris.org
> > Subject: Re: [PATCH] Implicit detection of merge source URL
> > and merge revision range
> >
> > On Tue, 24 Apr 2007, Kamesh Jayachandran wrote:
> >
> > > Hi Dan,
> > > Find the attached patch and log incorporating your review.
> > > Somehow merge_tests 48 (new testcase added by Paul
> > yesterday, earlier
> > > it was in XFail) is failing with this patch.
> >
> > Thanks Kamesh! I really appreciate you cranking another
> > revision of this patch out, and responding so thoroughly to
> > the review.
> >
> > This is a really important change that I'd like to start
> > exercising ASAP.
> >
> > > >>--- subversion/svn/main.c (revision 24527)
> > > >>+++ subversion/svn/main.c (working copy)
> > > >>@@ -513,7 +513,7 @@
> > > >> ("Apply the differences between two sources to a
> > working copy
> > > >> path.\n"
> > > >> "usage: 1. merge sourceURL1[@N] sourceURL2[@M] [WCPATH]\n"
> > > >> " 2. merge sourceWCPATH1@N sourceWCPATH2@M [WCPATH]\n"
> > > >>- " 3. merge [-c M | -r N:M] SOURCE[@REV] [WCPATH]\n"
> > > >>+ " 3. merge [-c M | -r N:M] [SOURCE[@REV]] [WCPATH]\n"
> > > >>
> > > >
> > > >I'd expect to this to be:
> > > >
> > > > merge [-c M | -r N:M] [SOURCE[@REV] [WCPATH]]
> > >
> > > No. It fails to cover the following case.
> > >
> > > merge [-c M | -r N:M] MY_WCPATH
> > >
> > > According to above syntax MY_WCPATH would be equated with
> > SOURCE which
> > > is not correct.
> >
> > With this syntax, you can't differentiate between SOURCE and
> > WCPATH, since SOURCE can be a WC path (which corresponds to
> > the merge source URL). When SOURCE is itself a WC path,
> > we've always assumed that the target WC path for the merge is
> > ".", which effectively means that the WCPATH parameter can't
> > be specified unless a SOURCE parameter is provided.
> >
> > Can you describe why it makes sense to change this now? Thanks!
> >
> > > >>- " 3. In the third form, SOURCE can be a URL, or
> > working copy
> > > >>item\n"
> > > >>- " in which case the corresponding URL is used.
> > This URL in\n"
> > > >>+ " 3. In the third form, SOURCE should be a URL if
> > given. \n"
> > > >>
> > > >
> > > >Since SOURCE can be a WCPATH, the above wording needs to
> > be changed.
> > >
> > > I am referring SOURCE in the above context not in a generic
> > sense. All
> > > I mean is SOURCE(If given) should be a absolute URI, If SOURCE is
> > > omitted, we will derive the SOURCE URL from from WCPATH target.
> > >
> > > <snip from my original log>
> > > This feature causes conflicts with one use case of existing feature.
> > > Currently revision based ranges can be of the form "3.
> > merge [-c M |
> > > -r N:M] SOURCE[@REV] [WCPATH]\n"
> > > Making SOURCE and REV_RANGE optional in the above command
> > results in
> > > the following type of invocation.
> > > "merge WC_SOURCE[@REV] [WCPATH]\n", this causes the
> > ambiguity with "2.
> > > merge sourceWCPATH1@N sourceWCPATH2@M [WCPATH]\n".
> > >
> > > So to fix this we mandate SOURCE in 3rd style of merges to
> > be a URL if
> > > given.
> > > </snip>
> > > I think I should use 'sourceURL' instead of 'SOURCE' to make things
> > > less ambiguous.
> >
> > This alters both the UI and the semantics of the command-line
> > in an incompatible fashion. You're suggesting that we make
> > the assumption that WCPATH is the merge source, in addition
> > to its existing semantics of being the WC target path.
> >
> > > >>+ " If SOURCE is not given SOURCE URL is deduced
> > from the \n"
> > > >>+ " copy source URL of WCPATH. If the WCPATH is
> > not a copy of
> > > >>some \n"
> > > >>+ " other node error is thrown asking for URL.
> > This URL in\n"
> > > >>
> > > >
> > > >We should talk about SOURCE here, rather than WCPATH.
> > >
> > > I believe you meant we should use 'SOURCE' rather than 'URL'.
> >
> > Nope -- I meant SOURCE.
> >
> > > >
> > > >> " revision REV is compared as it existed
> > between revisions N
> > > >> and \n"
> > > >> " M. If REV is not specified, HEAD is assumed.\n"
> > > >> " The '-c M' option is equivalent to '-r N:M'
> > where N = M-1.\n"
> > > >> " Using '-c -M' does the reverse: '-r M:N'
> > where N = M-1.\n"
> > > >>+ " If revision range is not specified, it is
> > assumed to be \n"
> > > >>+ " -r oldest_source_node_rev:HEAD.\n"
> > > >>
> > > >
> > > >I don't have a great alternate suggestion, but think that
> > > >"oldest_source_node_rev" should be formatted differently.
> > > >
> > > Changed it to OLDEST_SOURCE_NODE_REV.
> >
> > The word "node" shouldn't appear in our end user docs for the client.
> >
> > > >>--- subversion/svn/merge-cmd.c (revision 24527)
> > > >>+++ subversion/svn/merge-cmd.c (working copy)
> > ...
> > > >> svn_boolean_t using_rev_range_syntax = FALSE;
> > > >> svn_error_t *err;
> > > >>- svn_opt_revision_t peg_revision;
> > > >>+ svn_opt_revision_t peg_revision, peg_revision1, peg_revision2;
> > > >
> > > >Do we need three peg revision variables? In the
> > > >using_rev_range_syntax block, it looks like the two new
> > ones are only
> > > >temporary.
> > >
> > > Yes we need this new variables, they are there to avoid multiple
> > > 'svn_opt_parse_path' calls on different invocation scenarios.
> >
> > But all we really do with peg_revision is set peg_revision =
> > peg_revision1
> > -- we should be able to dump one of them.
> >
> > ...
> > > >>+ peg_revision = peg_revision1;
> > > >
> > > >I think we can do away with this statement by removing the two new
> > > >peg rev variables.
> > ...
> > > No. We need them as explained above. Thanks for catching I
> > need such
> > > assignments inside !using_rev_range block.
> >
> > Yup, I noticed that we need one more peg rev (as shown in patch v7).
> > However, we don't need two more, so we can do away with this
> > statement.
> >
> > ...
> > > >There's a mailing list thread about these APIs that I need
> > to follow
> > > >up on. We'll probably also want to bring existing merge
> > sources into
> > > >consideration where we use the svn_client__get_copy_source() API
> > > >below.
> >
> > We still need to do this.
> >
> > ...
> > > >>+struct copy_source_baton
> > > >>+{
> > > >>+ const char **copy_source;
> > > >>+ apr_pool_t *pool;
> > > >>+};
> > > >>
> > > >
> > > >Doesn't this need a copyfrom_rev field, too? Did you
> > happen to see
> > > >the patch I sent to the dev list on "Fri, 30 Mar 2007",
> > attached to
> > > >the message with "Subject: [RFC] Identifying copy/move sources",
> > > >which supplies a very similar API?
> > >
> > > Sorry, I did not look at it.
> > > Here I don't need copy_from_rev.
> >
> > Yeah, but I think the API should provide it nonetheless.
> >
> > > >>+/* A log callback conforming to the svn_log_message_receiver_t
> > > >>+ interface for obtaining the copy source of a node at
> > a path and
> > > >>+ storing it in *BATON (a struct copy_source_baton *).
> > */ static
> > > >>+svn_error_t * copy_source_receiver(void *baton,
> > > >>+ apr_hash_t *changed_paths,
> > > >>+ svn_revnum_t revision,
> > > >>+ const char *author,
> > > >>+ const char *date,
> > > >>+ const char *message,
> > > >>+ apr_pool_t *pool) {
> > > >>+ apr_hash_index_t *hi;
> > > >>+ void *val;
> > > >>+ const char *copy_source_path;
> > > >>+ svn_log_changed_path_t *changed_path;
> > > >>+ struct copy_source_baton *copy_source_baton;
> > > >>+ copy_source_baton = baton;
> > > >>+ /*FIXME: if the rev at which this node is created has
> > few other node
> > > >>+ changes too extract only our node. */
> > > >>+ for (hi = apr_hash_first(pool, changed_paths); hi; hi =
> > > >>apr_hash_next(hi))
> > > >>+ {
> > > >>+ apr_hash_this(hi, NULL, NULL, &val);
> > > >>+ changed_path = val;
> > > >>+ }
> > > >>+ copy_source_path = changed_path->copyfrom_path;
> > > >>+
> > > >>+ *((char **) copy_source_baton->copy_source) =
> > > >>+ apr_pstrdup(copy_source_baton->pool,
> > > >>copy_source_path);
> > > >>+ return SVN_NO_ERROR;
> > > >>+}
> > > >>
> > > >
> > > >You probably want to look at my receiver implementation.
> > >
> > > Will look at it.
> > >
> > > >>--- subversion/libsvn_client/merge.c (revision 24527)
> > > >>+++ subversion/libsvn_client/merge.c (working copy)
> > ...
> > > >>+static svn_error_t *
> > > >>+assume_default_rev_range(const svn_opt_revision_t *revision1,
> > > >>+ svn_opt_revision_t *assumed_revision1,
> > > >>+ const svn_opt_revision_t *revision2,
> > > >>+ svn_opt_revision_t *assumed_revision2,
> > > >>+ svn_ra_session_t *ra_session,
> > > >>+ apr_pool_t *pool) {
> > ...
> > > >>+ if (revision2->kind == svn_opt_revision_unspecified)
> > > >>+ {
> > > >>+ if (head_revnum == SVN_INVALID_REVNUM)
> > > >>
> > > >
> > > >...and here (reversing the blocks).
> > >
> > > I could not get you here.
> >
> > if (SVN_IS_VALID_REVNUM(head_revnum))
> > {
> > assumed_revision2->value.number = head_revnum;
> > assumed_revision2->kind = svn_opt_revision_number;
> > }
> > else
> > {
> > assumed_revision2->kind = svn_opt_revision_head;
> > }
> >
> > > >>+ assumed_revision2->kind = svn_opt_revision_head;
> > > >>+ else
> > > >>+ {
> > > >>+ assumed_revision2->value.number = head_revnum;
> > > >>+ assumed_revision2->kind = svn_opt_revision_number;
> > > >>+ }
> > ...
> > > >>+ /* Establish first RA session to URL1. */
> > > >>+ /*FIXME: use initial_URL1 for now which may barf for
> > pegged rev
> > > >>+ merges
> > > >>*/
> > > >>
> > > >
> > > >What are these FIXME's all about? Can you give more context?
> > > >
> > > I was not sure how things would behave if URL given to
> > > 'svn_client__open_ra_session_internal' if of form
> > > scheme://hostname/path/to/repo/subdir_at_173. So I put a FIXME marker.
> >
> > Okay. I think it's assumed that the peg revision has already
> > been parsed off of the URL, no? It is already a separate
> > parameter, at least.
> >
> > ...
> > > >>--- subversion/tests/cmdline/merge_tests.py (revision 24527)
> > > >>+++ subversion/tests/cmdline/merge_tests.py (working copy)
> > > >>@@ -1243,7 +1243,7 @@
> > > >> def merge_with_implicit_target_helper(sbox, arg_flav):
> > > >> "ARG_FLAV is one of 'r' (revision range) or 'c'
> > (single change)."
> > > >>
> > > >>- if arg_flav not in ('r', 'c'):
> > > >>+ if arg_flav not in ('r', 'c', '*'):
> > > >> raise svntest.Failure("Unrecognized flavor of merge
> > argument")
> > > >>
> > > >> sbox.build()
> > > >>@@ -1294,9 +1294,15 @@
> > > >> svntest.actions.run_and_verify_svn(None, ['U mu\n'], [],
> > > >> 'merge', '-c', '-2',
> > > >>mu_url)
> > > >>
> > > >>+ #implicit rev and url detection is for forward merge only.
> > > >>+ elif arg_flav == '*':
> > > >>+ svntest.actions.run_and_verify_svn(None, ['U mu\n'], [],
> > > >>+ 'merge', '-c', '-2',
> > > >>+ mu_url)
> > > >>
> > > >
> > > >We should document this in the command-line client.
> > ...
> > > Fixed the command line help text.
> >
> > I see "which is a forward merge", which is implied by the
> > preceding text. Maybe we should just leave that off, hmm...
> >
> > > Here we should undo the forward merge of r2 to continue
> > with rest of
> > > the testcase, instead of reverting, I would prefer merge -c
> > -2 for now.
> > > Later would see if possible to make it something like
> > this(implicitly
> > > detecting the source atleast).
> >
> > Okay.
> >
> > ...
> > > >>@@ -5370,6 +5384,7 @@
> > > >> simple_property_merges,
> > > >> merge_with_implicit_target_using_r,
> > > >> merge_with_implicit_target_using_c,
> > > >>+ merge_with_implicit_target_and_revs,
> > > >>
> > > >
> > > >I seem to recall a second existing test case which could
> > house this
> > > >type of regression testing...merge_one_file_helper() it is. An
> > > >additional consumer -- a la merge_one_file_using_r() and
> > > >merge_one_file_using_c() -- could be added. (I didn't do this.)
> > >
> > > Will write such a test later.
> >
> > Sounds good.
> >
> > I removed SVN_ERR_CLIENT_PATH_HAS_NO_COPY_SOURCE and its
> > usage, which is really more about auto-detecting a merge
> > source. An error like PATH_HAS_NO_COPY_SOURCE is too
> > algorithm-specific; there's more information than just
> > copy-source that we can use here (e.g. most recent merge source).
> >
> >
> > I'm attaching a revised version of the patch (v9) which
> > accounts for my tweaks from v7, plus most of the discussion above.
> >
> > > [[[
> > > 'merge' subcommand should automatically identify the source
> > from which
> > > to merge and the applicable revisions to merge for
> > 'revision based merges'.
> > >
> > > This feature causes conflicts with one use case of existing feature.
> > > Currently revision based ranges can be of the form "3.
> > merge [-c M |
> > > -r N:M] SOURCE[@REV] [WCPATH]\n"
> > > Making SOURCE and REV_RANGE optional in the above command
> > results in
> > > the following type of invocation.
> > > "merge WC_SOURCE[@REV] [WCPATH]\n", this causes the
> > ambiguity with "2.
> > > merge sourceWCPATH1@N sourceWCPATH2@M [WCPATH]\n".
> > >
> > > So to fix this we mandate SOURCE in 3rd style of merges to
> > be a URL if given.
> > >
> > > * subversion/svn/main.c
> > > (svn_cl__cmd_table):
> > > Update help text for merge subcommand.
> > > * subversion/svn/merge-cmd.c
> > > (svn_cl__merge):
> > > Implementing the above change.
> > > * subversion/libsvn_client/client.h
> > > (svn_client__oldest_rev_at_path): Prototype the new function.
> > > (svn_client__get_copy_source): Prototype the new function.
> > > * subversion/libsvn_client/copy.c
> > > (revnum_receiver): Moved to subversion/libsvn_client/log.c.
> > > (get_implied_merge_info): Implementation to find the rev
> > at which a
> > > given path is created has been moved to
> > 'svn_client__oldest_rev_at_path'.
> > > This function calls 'svn_client__oldest_rev_at_path'.
> > > * subversion/libsvn_client/log.c
> > > (revnum_receiver): Moved from subversion/libsvn_client/copy.c.
> > > (svn_client__oldest_rev_at_path): New function.
> > > (struct copy_source_baton): New baton to hold the
> > copy_source_path.
> > > (copy_source_receiver): New function.
> > > (svn_client__get_copy_source): New function.
> > > * subversion/libsvn_client/merge.c
> > > (assume_default_rev_range): New function.
> > > (do_merge, do_single_file_merge): Deduce the revison
> > range to merge in case
> > > not provided.
> > > (svn_client_merge_peg3): Deduce source if not given. Fix
> > the error message
> > > for 'undeterminable source URL'
> > > * subversion/include/svn_error_codes.h
> > > (SVN_ERR_CLIENT_PATH_HAS_NO_COPY_SOURCE): define new
> > error code if merge
> > > source could not be deduced.
> > >
> > > * subversion/tests/cmdline/merge_tests.py
> > > (merge_with_implicit_target_helper):
> > > Support implicit merge URL and revisions feature like
> > other 2 variants.
> > > (merge_with_implicit_target_and_revs): New function.
> > > (test_list): include
> > 'merge_with_implicit_target_and_revs' test case.
> > >
> > > Patch by: kameshj
> > > dlr
> > > ]]]
> >

Content-Description: automerge-v10.patch.txt
> Index: subversion/libsvn_client/client.h
> ===================================================================
> --- subversion/libsvn_client/client.h (revision 24767)
> +++ subversion/libsvn_client/client.h (working copy)
> @@ -209,6 +209,28 @@
> svn_boolean_t recurse, svn_client_ctx_t *ctx,
> apr_pool_t *pool);
>
> +/* Retrieve the oldest revision of the node at REL_PATH at REV since
> + it was last copied (if applicable), and store it in OLDEST_REV. If
> + REL_PATH does not exist in that REV, set *OLDEST_REV to
> + SVN_INVALID_REVNUM. Use POOL for temporary allocations. */
> +svn_error_t *
> +svn_client__oldest_rev_at_path(svn_revnum_t *oldest_rev,
> + svn_ra_session_t *ra_session,
> + const char *rel_path,
> + svn_revnum_t rev,
> + apr_pool_t *pool);
> +
> +
> +/* Retrieve the copy source of the node at REL_PATH at REV and store
> + it in COPY_SOURCE. If REL_PATH is not a copy (or doesn't exist),
> + set *COPY_SOURCE to NULL. Use POOL for temporary allocations. */
> +svn_error_t *
> +svn_client__get_copy_source(const char **copy_source,
> + svn_ra_session_t *ra_session,
> + const char *rel_path,
> + svn_revnum_t rev,
> + apr_pool_t *pool);
> +
> /* ---------------------------------------------------------------- */
>
> /*** RA callbacks ***/
> Index: subversion/libsvn_client/copy.c
> ===================================================================
> --- subversion/libsvn_client/copy.c (revision 24767)
> +++ subversion/libsvn_client/copy.c (working copy)
> @@ -351,22 +351,6 @@
> svn_boolean_t is_move;
> };
>
> -/* A log callback conforming to the svn_log_message_receiver_t
> - interface for obtaining the last revision of a node at a path and
> - storing it in *BATON (an svn_revnum_t). */
> -static svn_error_t *
> -revnum_receiver(void *baton,
> - apr_hash_t *changed_paths,
> - svn_revnum_t revision,
> - const char *author,
> - const char *date,
> - const char *message,
> - apr_pool_t *pool)
> -{
> - *((svn_revnum_t *) baton) = revision;
> - return SVN_NO_ERROR;
> -}
> -
> /* Obtain the implied merge info of repository-relative path PATH in
> *IMPLIED_MERGEINFO (e.g. every revision of the node at PATH since
> it last appeared). REL_PATH corresponds to PATH, but is relative
> @@ -379,33 +363,17 @@
> svn_revnum_t rev,
> apr_pool_t *pool)
> {
> - svn_error_t *err;
> - svn_revnum_t oldest_rev = SVN_INVALID_REVNUM;
> + svn_revnum_t oldest_rev;
> svn_merge_range_t *range;
> apr_array_header_t *rangelist;
> - apr_array_header_t *rel_paths = apr_array_make(pool, 1, sizeof(rel_path));
>
> *implied_mergeinfo = apr_hash_make(pool);
> - APR_ARRAY_PUSH(rel_paths, const char *) = rel_path;
>
> - /* Trace back in history to find the revision at which this node
> - was created (copied or added). */
> - err = svn_ra_get_log(ra_session, rel_paths, 1, rev, 1, FALSE, TRUE,
> - revnum_receiver, &oldest_rev, pool);
> - if (err)
> - {
> - if (err->apr_err == SVN_ERR_FS_NOT_FOUND ||
> - err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED)
> - {
> - /* A locally-added but uncommitted versioned resource won't
> - exist in the repository. */
> - svn_error_clear(err);
> - err = SVN_NO_ERROR;
> - }
> + SVN_ERR(svn_client__oldest_rev_at_path(&oldest_rev, ra_session, rel_path,
> + rev, pool));
> + if (oldest_rev == SVN_INVALID_REVNUM)
> + return SVN_NO_ERROR;
>
> - return err;
> - }
> -
> range = apr_palloc(pool, sizeof(*range));
> range->start = oldest_rev;
> range->end = rev;
> @@ -413,7 +381,7 @@
> APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = range;
> apr_hash_set(*implied_mergeinfo, path, APR_HASH_KEY_STRING, rangelist);
>
> - return err;
> + return SVN_NO_ERROR;
> }
>
> /* Obtain the implied merge info and the existing merge info of the
> Index: subversion/libsvn_client/log.c
> ===================================================================
> --- subversion/libsvn_client/log.c (revision 24767)
> +++ subversion/libsvn_client/log.c (working copy)
> @@ -371,3 +371,114 @@
>
> return err;
> }
> +
> +/* A log callback conforming to the svn_log_message_receiver_t
> + interface for obtaining the last revision of a node at a path and
> + storing it in *BATON (an svn_revnum_t). */
> +static svn_error_t *
> +revnum_receiver(void *baton,
> + apr_hash_t *changed_paths,
> + svn_revnum_t revision,
> + const char *author,
> + const char *date,
> + const char *message,
> + apr_pool_t *pool)
> +{
> + *((svn_revnum_t *) baton) = revision;
> + return SVN_NO_ERROR;
> +}
> +
> +svn_error_t *
> +svn_client__oldest_rev_at_path(svn_revnum_t *oldest_rev,
> + svn_ra_session_t *ra_session,
> + const char *rel_path,
> + svn_revnum_t rev,
> + apr_pool_t *pool)
> +{
> + svn_error_t *err;
> + apr_array_header_t *rel_paths = apr_array_make(pool, 1, sizeof(rel_path));
> + *oldest_rev = SVN_INVALID_REVNUM;
> + APR_ARRAY_PUSH(rel_paths, const char *) = rel_path;
> +
> + /* Trace back in history to find the revision at which this node
> + was created (copied or added). */
> + err = svn_ra_get_log(ra_session, rel_paths, 1, rev, 1, FALSE, TRUE,
> + revnum_receiver, oldest_rev, pool);
> + if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND ||
> + err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED))
> + {
> + /* A locally-added but uncommitted versioned resource won't
> + exist in the repository. */
> + svn_error_clear(err);
> + err = SVN_NO_ERROR;
> + }
> + return err;
> +}
> +
> +struct copy_source_baton
> +{
> + const char **copy_source;
> + apr_pool_t *pool;
> +};
> +
> +/* A log callback conforming to the svn_log_message_receiver_t
> + interface for obtaining the copy source of a node at a path and
> + storing it in *BATON (a struct copy_source_baton *). */
> +static svn_error_t *
> +copy_source_receiver(void *baton,
> + apr_hash_t *changed_paths,
> + svn_revnum_t revision,
> + const char *author,
> + const char *date,
> + const char *message,
> + apr_pool_t *pool)
> +{
> + apr_hash_index_t *hi;
> + void *val;
> + const char *copy_source_path;
> + svn_log_changed_path_t *changed_path;
> + struct copy_source_baton *copy_source_baton;
> + copy_source_baton = baton;
> + /*FIXME: if the rev at which this node is created has few other node
> + changes too extract only our node. */
> + for (hi = apr_hash_first(pool, changed_paths); hi; hi = apr_hash_next(hi))
> + {
> + apr_hash_this(hi, NULL, NULL, &val);
> + changed_path = val;
> + }
> + copy_source_path = changed_path->copyfrom_path;
> +
> + *((char **) copy_source_baton->copy_source) =
> + apr_pstrdup(copy_source_baton->pool, copy_source_path);
> + return SVN_NO_ERROR;
> +}
> +
> +svn_error_t *
> +svn_client__get_copy_source(const char **copy_source,
> + svn_ra_session_t *ra_session,
> + const char *rel_path,
> + svn_revnum_t rev,
> + apr_pool_t *pool)
> +{
> + svn_error_t *err;
> + struct copy_source_baton copy_source_baton;
> + apr_array_header_t *rel_paths = apr_array_make(pool, 1, sizeof(rel_path));
> + *copy_source = NULL;
> + copy_source_baton.copy_source = copy_source;
> + copy_source_baton.pool = pool;
> + APR_ARRAY_PUSH(rel_paths, const char *) = rel_path;
> +
> + /* Trace back in history to find the revision at which this node
> + was created (copied or added). */
> + err = svn_ra_get_log(ra_session, rel_paths, 1, rev, 1, TRUE, TRUE,
> + copy_source_receiver, &copy_source_baton, pool);
> + if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND ||
> + err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED))
> + {
> + /* A locally-added but uncommitted versioned resource won't
> + exist in the repository. */
> + svn_error_clear(err);
> + err = SVN_NO_ERROR;
> + }
> + return err;
> +}
> Index: subversion/libsvn_client/merge.c
> ===================================================================
> --- subversion/libsvn_client/merge.c (revision 24767)
> +++ subversion/libsvn_client/merge.c (working copy)
> @@ -1604,6 +1604,60 @@
> return SVN_NO_ERROR;
> }
>
> +/* Default the values of REVISION1 and REVISION2 to be oldest rev at
> + which ra_session's root got created and HEAD (respectively), if
> + REVISION1 and REVISION2 are unspecified. This assumed value is set
> + at *ASSUMED_REVISION1 and *ASSUMED_REVISION2. RA_SESSION is used
> + to retrieve the revision of the current HEAD revision. Use POOL
> + for temporary allocations. */
> +static svn_error_t *
> +assume_default_rev_range(const svn_opt_revision_t *revision1,
> + svn_opt_revision_t *assumed_revision1,
> + const svn_opt_revision_t *revision2,
> + svn_opt_revision_t *assumed_revision2,
> + svn_ra_session_t *ra_session,
> + apr_pool_t *pool)
> +{
> + svn_opt_revision_t head_rev_opt;
> + svn_revnum_t head_revnum = SVN_INVALID_REVNUM;
> + head_rev_opt.kind = svn_opt_revision_head;
> + /* Provide reasonable defaults for unspecified revisions. */
> + if (revision1->kind == svn_opt_revision_unspecified)
> + {
> + SVN_ERR(svn_client__get_revision_number(&head_revnum, ra_session,
> + &head_rev_opt, "", pool));
> + SVN_ERR(svn_client__oldest_rev_at_path(&assumed_revision1->value.number,
> + ra_session, "",
> + head_revnum,
> + pool));
> + if (SVN_IS_VALID_REVNUM(assumed_revision1->value.number))
> + {
> + assumed_revision1->kind = svn_opt_revision_number;
> + }
> + }
> + else
> + {
> + *assumed_revision1 = *revision1;
> + }
> + if (revision2->kind == svn_opt_revision_unspecified)
> + {
> + if (SVN_IS_VALID_REVNUM(head_revnum))
> + {
> + assumed_revision2->value.number = head_revnum;
> + assumed_revision2->kind = svn_opt_revision_number;
> + }
> + else
> + {
> + assumed_revision2->kind = svn_opt_revision_head;
> + }
> + }
> + else
> + {
> + *assumed_revision2 = *revision2;
> + }
> + return SVN_NO_ERROR;
> +}
> +
> /* URL1/PATH1, URL2/PATH2, and TARGET_WCPATH all better be
> directories. For the single file case, the caller does the merging
> manually. PATH1 and PATH2 can be NULL.
> @@ -1658,11 +1712,24 @@
> const svn_wc_entry_t *entry;
> int i;
> svn_boolean_t inherited;
> + svn_opt_revision_t assumed_initial_revision1, assumed_initial_revision2;
> apr_size_t target_count, merge_target_count;
>
> - ENSURE_VALID_REVISION_KINDS(initial_revision1->kind,
> - initial_revision2->kind);
> + /* Establish first RA session to initial_URL1. */
> + SVN_ERR(svn_client__open_ra_session_internal(&ra_session, initial_URL1, NULL,
> + NULL, NULL, FALSE, TRUE,
> + ctx, pool));
> + SVN_ERR(assume_default_rev_range(initial_revision1,
> + &assumed_initial_revision1,
> + initial_revision2,
> + &assumed_initial_revision2,
> + ra_session,
> + pool));
>
> + ENSURE_VALID_REVISION_KINDS(assumed_initial_revision1.kind,
> + assumed_initial_revision2.kind);
> +
> +
> /* If we are performing a pegged merge, we need to find out what our
> actual URLs will be. */
> if (peg_revision->kind != svn_opt_revision_unspecified)
> @@ -1673,10 +1740,14 @@
> initial_path2 ? initial_path2
> : initial_URL2,
> peg_revision,
> - initial_revision1,
> - initial_revision2,
> + &assumed_initial_revision1,
> + &assumed_initial_revision2,
> ctx, pool));
>
> + /* Reparent session if actual URL changed. */
> + if (strcmp(initial_URL1, URL1))
> + SVN_ERR(svn_ra_reparent(ra_session, URL1, pool));
> +
> merge_b->url = URL2;
> path1 = NULL;
> path2 = NULL;
> @@ -1689,15 +1760,11 @@
> path1 = initial_path1;
> path2 = initial_path2;
> revision1 = apr_pcalloc(pool, sizeof(*revision1));
> - *revision1 = *initial_revision1;
> + *revision1 = assumed_initial_revision1;
> revision2 = apr_pcalloc(pool, sizeof(*revision2));
> - *revision2 = *initial_revision2;
> + *revision2 = assumed_initial_revision2;
> }
>
> - /* Establish first RA session to URL1. */
> - SVN_ERR(svn_client__open_ra_session_internal(&ra_session, URL1, NULL,
> - NULL, NULL, FALSE, TRUE,
> - ctx, pool));
>
> notify_b.same_urls = (strcmp(URL1, URL2) == 0);
> if (!notify_b.same_urls && merge_b->record_only)
> @@ -1988,10 +2055,22 @@
> int i;
> svn_boolean_t inherited = FALSE;
> apr_size_t target_count, merge_target_count;
> + svn_opt_revision_t assumed_initial_revision1, assumed_initial_revision2;
>
> - ENSURE_VALID_REVISION_KINDS(initial_revision1->kind,
> - initial_revision2->kind);
> + /* Establish first RA session to URL1. */
> + SVN_ERR(svn_client__open_ra_session_internal(&ra_session1, initial_URL1, NULL,
> + NULL, NULL, FALSE, TRUE,
> + ctx, pool));
> + SVN_ERR(assume_default_rev_range(initial_revision1,
> + &assumed_initial_revision1,
> + initial_revision2,
> + &assumed_initial_revision2,
> + ra_session1,
> + pool));
>
> + ENSURE_VALID_REVISION_KINDS(assumed_initial_revision1.kind,
> + assumed_initial_revision2.kind);
> +
> /* If we are performing a pegged merge, we need to find out what our
> actual URLs will be. */
> if (peg_revision->kind != svn_opt_revision_unspecified)
> @@ -2007,8 +2086,8 @@
> initial_path2 ? initial_path2
> : initial_URL2,
> peg_revision,
> - initial_revision1,
> - initial_revision2,
> + &assumed_initial_revision1,
> + &assumed_initial_revision2,
> ctx, pool);
> if (err)
> {
> @@ -2032,15 +2111,14 @@
> path1 = initial_path1;
> path2 = initial_path2;
> revision1 = apr_pcalloc(pool, sizeof(*revision1));
> - *revision1 = *initial_revision1;
> + *revision1 = assumed_initial_revision1;
> revision2 = apr_pcalloc(pool, sizeof(*revision2));
> - *revision2 = *initial_revision2;
> + *revision2 = assumed_initial_revision2;
> }
>
> - /* Establish RA sessions to both URLs. */
> - SVN_ERR(svn_client__open_ra_session_internal(&ra_session1, URL1, NULL,
> - NULL, NULL, FALSE, TRUE,
> - ctx, pool));
> + /* reparent RA session to URL1. */
> + SVN_ERR(svn_ra_reparent(ra_session1, URL1, pool));
> + /* Establish RA session to URL2. */
> SVN_ERR(svn_client__open_ra_session_internal(&ra_session2, URL2, NULL,
> NULL, NULL, FALSE, TRUE,
> ctx, pool));
> @@ -2547,18 +2625,56 @@
> const char *path;
> apr_array_header_t *children_with_mergeinfo;
>
> - /* If source is a path, we need to get the underlying URL
> - * from the wc and save the initial path we were passed so we can use it as
> - * a path parameter (either in the baton or not). otherwise, the path
> - * will just be NULL, which means we won't be able to figure out some kind
> - * of revision specifications, but in that case it won't matter, because
> - * those ways of specifying a revision are meaningless for a url.
> - */
> - SVN_ERR(svn_client_url_from_path(&URL, source, pool));
> - if (! URL)
> - return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
> - _("'%s' has no URL"),
> - svn_path_local_style(source, pool));
> + if (source)
> + {
> + /* If source is a path, we need to get the underlying URL from
> + the wc and save the initial path we were passed so we can use
> + it as a path parameter (either in the baton or not).
> + otherwise, the path will just be NULL, which means we won't
> + be able to figure out some kind of revision specifications,
> + but in that case it won't matter, because those ways of
> + specifying a revision are meaningless for a URL. */
> + SVN_ERR(svn_client_url_from_path(&URL, source, pool));
> + if (! URL)
> + return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
> + _("'%s' has no URL"),
> + svn_path_local_style(source, pool));
> + }
> + else
> + {
> + /* If a merge source was not specified, try to derive it from
> + copy source. */
> + svn_ra_session_t *ra_session;
> + svn_revnum_t working_revnum;
> + svn_opt_revision_t wc_revision;
> + const char *working_copy_url;
> + const char *copy_source_path;
> + wc_revision.kind = svn_opt_revision_working;
> + SVN_ERR(svn_client__ra_session_from_path(&ra_session,
> + &working_revnum,
> + &working_copy_url,
> + target_wcpath,
> + &wc_revision,
> + &wc_revision,
> + ctx,
> + pool));
> + SVN_ERR(svn_client__get_copy_source(&copy_source_path, ra_session,
> + "", working_revnum, pool));
> + if (copy_source_path)
> + {
> + const char *repos_root;
> + SVN_ERR(svn_ra_get_repos_root(ra_session, &repos_root, pool));
> + URL = apr_pstrcat(pool, repos_root, copy_source_path, NULL);
> + }
> + else
> + {
> + return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
> + _("Unable to determine merge source for "
> + "'%s', please provide an explicit source"),
> + svn_path_local_style(target_wcpath, pool));
> + }
> + }
> +
> if (URL == source)
> path = NULL;
> else
> Index: subversion/svn/main.c
> ===================================================================
> --- subversion/svn/main.c (revision 24767)
> +++ subversion/svn/main.c (working copy)
> @@ -513,7 +513,7 @@
> ("Apply the differences between two sources to a working copy path.\n"
> "usage: 1. merge sourceURL1[@N] sourceURL2[@M] [WCPATH]\n"
> " 2. merge sourceWCPATH1@N sourceWCPATH2@M [WCPATH]\n"
> - " 3. merge [-c M | -r N:M] SOURCE[@REV] [WCPATH]\n"
> + " 3. merge [-c M | -r N:M] [SOURCE[@REV] [WCPATH]]\n"
> "\n"
> " 1. In the first form, the source URLs are specified at revisions\n"
> " N and M. These are the two sources to be compared. The revisions\n"
> @@ -524,11 +524,15 @@
> " be specified.\n"
> "\n"
> " 3. In the third form, SOURCE can be a URL, or working copy item\n"
> - " in which case the corresponding URL is used. This URL in\n"
> - " revision REV is compared as it existed between revisions N and \n"
> - " M. If REV is not specified, HEAD is assumed.\n"
> - " The '-c M' option is equivalent to '-r N:M' where N = M-1.\n"
> - " Using '-c -M' does the reverse: '-r M:N' where N = M-1.\n"
> + " in which case the corresponding URL is used. If not specified,\n"
> + " the copy source URL of SOURCE is used. If the WCPATH cannot be\n"
> + " determined automatically, an error is displayed asking for an\n"
> + " explicit SOURCE. This URL in revision REV is compared as it\n"
> + " existed between revisions N and M. If REV is not specified, HEAD\n"
> + " is assumed. The '-c M' option is equivalent to '-r N:M' where\n"
> + " N = M-1. Using '-c -M' does the reverse: '-r M:N' where N = M-1.\n"
> + " If a revision range is not specified, it is assumed to be\n"
> + " -r OLDEST_REV_OF_SOURCE_AT_URL:HEAD.\n"
> "\n"
> " WCPATH is the working copy path that will receive the changes.\n"
> " If WCPATH is omitted, a default value of '.' is assumed, unless\n"
> Index: subversion/svn/merge-cmd.c
> ===================================================================
> --- subversion/svn/merge-cmd.c (revision 24767)
> +++ subversion/svn/merge-cmd.c (working copy)
> @@ -43,12 +43,35 @@
> svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
> svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
> apr_array_header_t *targets;
> - const char *sourcepath1, *sourcepath2, *targetpath;
> + const char *sourcepath1 = NULL, *sourcepath2 = NULL, *targetpath = "";
> svn_boolean_t using_rev_range_syntax = FALSE;
> svn_error_t *err;
> - svn_opt_revision_t peg_revision;
> + svn_opt_revision_t peg_revision1, peg_revision2;
> apr_array_header_t *options;
>
> + SVN_ERR(svn_opt_args_to_target_array2(&targets, os,
> + opt_state->targets, pool));
> + if (targets->nelts >= 1)
> + {
> + SVN_ERR(svn_opt_parse_path(&peg_revision1, &sourcepath1,
> + APR_ARRAY_IDX(targets, 0, const char *), pool));
> + if (targets->nelts >= 2)
> + SVN_ERR(svn_opt_parse_path(&peg_revision2, &sourcepath2,
> + APR_ARRAY_IDX(targets, 1, const char *), pool));
> + }
> +
> + /* If an (optional) source, or a source plus WC path is provided,
> + the revision range syntax is in use. */
> + if (targets->nelts <= 1)
> + {
> + using_rev_range_syntax = TRUE;
> + }
> + else if (targets->nelts == 2)
> + {
> + if (svn_path_is_url(sourcepath1) && !svn_path_is_url(sourcepath2))
> + using_rev_range_syntax = TRUE;
> + }
> +
> /* If the first opt_state revision is filled in at this point, then
> we know the user must have used the '-r' or '-c' switch. */
> if (opt_state->start_revision.kind != svn_opt_revision_unspecified)
> @@ -60,33 +83,31 @@
>
> using_rev_range_syntax = TRUE;
> }
> -
> - SVN_ERR(svn_opt_args_to_target_array2(&targets, os,
> - opt_state->targets, pool));
> -
> if (using_rev_range_syntax)
> {
> - if (targets->nelts < 1)
> - return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, NULL);
> if (targets->nelts > 2)
> return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
> _("Too many arguments given"));
>
> - SVN_ERR(svn_opt_parse_path(&peg_revision, &sourcepath1,
> - APR_ARRAY_IDX(targets, 0, const char *),
> - pool));
> - sourcepath2 = sourcepath1;
> + if (targets->nelts == 0)
> + {
> + /* Set the default peg revision if one was not specified. */
> + if (peg_revision1.kind == svn_opt_revision_unspecified)
> + peg_revision1.kind = svn_opt_revision_head;
> + }
> + else
> + {
> + /* targets->nelts is 1 or 2 here. */
> + sourcepath2 = sourcepath1;
>
> - /* Set the default peg revision if one was not specified. */
> - if (peg_revision.kind == svn_opt_revision_unspecified)
> - peg_revision.kind = svn_path_is_url(sourcepath1)
> - ? svn_opt_revision_head : svn_opt_revision_working;
> + /* Set the default peg revision if one was not specified. */
> + if (peg_revision1.kind == svn_opt_revision_unspecified)
> + peg_revision1.kind = svn_path_is_url(sourcepath1)
> + ? svn_opt_revision_head : svn_opt_revision_working;
>
> - /* decide where to apply the diffs, defaulting to '.' */
> - if (targets->nelts == 2)
> - targetpath = APR_ARRAY_IDX(targets, 1, const char *);
> - else
> - targetpath = "";
> + if (targets->nelts == 2)
> + targetpath = APR_ARRAY_IDX(targets, 1, const char *);
> + }
> }
> else /* using @rev syntax */
> {
> @@ -96,14 +117,9 @@
> return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
> _("Too many arguments given"));
>
> - /* the first two paths become the 'sources' */
> - SVN_ERR(svn_opt_parse_path(&opt_state->start_revision, &sourcepath1,
> - APR_ARRAY_IDX(targets, 0, const char *),
> - pool));
> - SVN_ERR(svn_opt_parse_path(&opt_state->end_revision, &sourcepath2,
> - APR_ARRAY_IDX(targets, 1, const char *),
> - pool));
> -
> + opt_state->start_revision = peg_revision1;
> + opt_state->end_revision = peg_revision2;
> +
> /* Catch 'svn merge wc_path1 wc_path2 [target]' without explicit
> revisions--since it ignores local modifications it may not do what
> the user expects. Forcing the user to specify a repository
> @@ -117,22 +133,26 @@
> (SVN_ERR_CLIENT_BAD_REVISION, 0,
> _("A working copy merge source needs an explicit revision"));
>
> - /* decide where to apply the diffs, defaulting to '.' */
> + /* Default peg revisions to each URL's youngest revision. */
> + if (opt_state->start_revision.kind == svn_opt_revision_unspecified)
> + opt_state->start_revision.kind = svn_opt_revision_head;
> + if (opt_state->end_revision.kind == svn_opt_revision_unspecified)
> + opt_state->end_revision.kind = svn_opt_revision_head;
> +
> + /* Decide where to apply the delta (defaulting to "."). */
> if (targets->nelts == 3)
> targetpath = APR_ARRAY_IDX(targets, 2, const char *);
> - else
> - targetpath = "";
> }
>
> /* If no targetpath was specified, see if we can infer it from the
> sourcepaths. */
> - if (! strcmp(targetpath, ""))
> + if (sourcepath1 && sourcepath2 && strcmp(targetpath, "") == 0)
> {
> char *sp1_basename, *sp2_basename;
> sp1_basename = svn_path_basename(sourcepath1, pool);
> sp2_basename = svn_path_basename(sourcepath2, pool);
>
> - if (! strcmp(sp1_basename, sp2_basename))
> + if (strcmp(sp1_basename, sp2_basename) == 0)
> {
> svn_node_kind_t kind;
> const char *decoded_path = svn_path_uri_decode(sp1_basename, pool);
> @@ -144,11 +164,6 @@
> }
> }
>
> - if (opt_state->start_revision.kind == svn_opt_revision_unspecified)
> - opt_state->start_revision.kind = svn_opt_revision_head;
> - if (opt_state->end_revision.kind == svn_opt_revision_unspecified)
> - opt_state->end_revision.kind = svn_opt_revision_head;
> -
> if (! opt_state->quiet)
> svn_cl__get_notifier(&ctx->notify_func2, &ctx->notify_baton2, FALSE,
> FALSE, FALSE, pool);
> @@ -163,7 +178,7 @@
> err = svn_client_merge_peg3(sourcepath1,
> &(opt_state->start_revision),
> &(opt_state->end_revision),
> - &peg_revision,
> + &peg_revision1,
> targetpath,
> opt_state->depth,
> opt_state->ignore_ancestry,
> Index: subversion/tests/cmdline/merge_tests.py
> ===================================================================
> --- subversion/tests/cmdline/merge_tests.py (revision 24767)
> +++ subversion/tests/cmdline/merge_tests.py (working copy)
> @@ -1243,7 +1243,7 @@
> def merge_with_implicit_target_helper(sbox, arg_flav):
> "ARG_FLAV is one of 'r' (revision range) or 'c' (single change)."
>
> - if arg_flav not in ('r', 'c'):
> + if arg_flav not in ('r', 'c', '*'):
> raise svntest.Failure("Unrecognized flavor of merge argument")
>
> sbox.build()
> @@ -1293,11 +1293,19 @@
> elif arg_flav == 'c':
> svntest.actions.run_and_verify_svn(None, ['U mu\n'], [],
> 'merge', '-c', '-2', mu_url)
> + elif arg_flav == '*':
> + # Implicit merge source URL and revision range detection is for
> + # forward merges only (e.g. non-reverts). Undo application of
> + # r2 to enable continuation of the test case.
> + svntest.actions.run_and_verify_svn(None, ['U mu\n'], [],
> + 'merge', '-c', '-2', mu_url)
>
> # sanity-check resulting file
> if (svntest.tree.get_text('mu') != orig_mu_text):
> - raise svntest.Failure("Unexpected text in 'mu'")
> + raise svntest.Failure("Unexpected text '%s' in 'mu', expected '%s'" %
> + (svntest.tree.get_text('mu'), orig_mu_text))
>
> +
> # merge using filename for sourcepath
> # Cannot use run_and_verify_merge with a file target
> if arg_flav == 'r':
> @@ -1307,6 +1315,10 @@
> svntest.actions.run_and_verify_svn(None, ['G mu\n'], [],
> 'merge', '-c', '2', 'mu')
>
> + elif arg_flav == '*':
> + svntest.actions.run_and_verify_svn(None, ['G mu\n'], [],
> + 'merge', 'mu')
> +
> # sanity-check resulting file
> if (svntest.tree.get_text('mu') != orig_mu_text + added_mu_text):
> raise svntest.Failure("Unexpected text in 'mu'")
> @@ -1322,6 +1334,11 @@
> "merging a file w/no explicit target path using -c"
> merge_with_implicit_target_helper(sbox, 'c')
>
> +def merge_with_implicit_target_and_revs(sbox):
> + "merging a file w/no explicit target path or revs"
> + merge_with_implicit_target_helper(sbox, '*')
> +
> +
> #----------------------------------------------------------------------
>
> def merge_with_prev (sbox):
> @@ -5566,6 +5583,7 @@
> simple_property_merges,
> merge_with_implicit_target_using_r,
> merge_with_implicit_target_using_c,
> + merge_with_implicit_target_and_revs,
> merge_catches_nonexistent_target,
> merge_tree_deleted_in_target,
> merge_similar_unrelated_trees,

> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
> For additional commands, e-mail: dev-help@subversion.tigris.org

  • application/pgp-signature attachment: stored
Received on Wed Apr 25 07:21:46 2007

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.