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, ©_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(©_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,