Hi,
Here is a patch to help flesh out the utility of the -c/--change
option.
Some background in this thread:
http://svn.haxx.se/dev/archive-2005-10/1324.shtml
(should I put that in the commit message?)
E.g.,
svn merge -c 100 --reverse URL
same as
svn merge -r100:99 URL
Do the tests look ok? I'm not a python stud so I just basically
imitated what Dan Berlin did in r17054.
All tests pass aside from a utf8 one that was SKIP'd.
Thanks
-- bart
[[[
Add a --reverse option reverse the sense of the -c/--change option,
making it possible to back out changes with -c rather than -r.
* subversion/svn/cl.h
(svn_cl__longopt_t): Add enum for '--reverse' option.
* subversion/svn/main.c
(svn_cl__options): Add 'reverse' option.
(svn_cl__cmd_table): Merge and diff can take 'reverse' argument.
(main): Handle parsing for 'reverse'.
* subversion/tests/cmdline/diff_tests.py
(diff_pure_repository_update_a_file): Modify test to also verify
that '--reverse' may not be used with '-r', only '-c'.
(diff_only_property_change): Modify test to also use '--reverse'
with '-c'.
* subversion/tests/cmdline/merge_tests.py
(merge_with_implicit_target_using_c_and_reverse): New test: a copy
of merge_with_implicit_target that uses the '-c' and '--reverse'
options.
(test_list): Add the new test.
]]]
Index: subversion/svn/cl.h
===================================================================
--- subversion/svn/cl.h (revision 18084)
+++ subversion/svn/cl.h (working copy)
@@ -76,7 +76,8 @@ typedef enum {
svn_cl__strict_opt,
svn_cl__targets_opt,
svn_cl__version_opt,
- svn_cl__xml_opt
+ svn_cl__xml_opt,
+ svn_cl__reverse_opt
} svn_cl__longopt_t;
Index: subversion/svn/main.c
===================================================================
--- subversion/svn/main.c (revision 18084)
+++ subversion/svn/main.c (working copy)
@@ -149,6 +149,8 @@ const apr_getopt_option_t svn_cl__option
N_("maximum number of log entries")},
{"no-unlock", svn_cl__no_unlock_opt, 0,
N_("don't unlock the targets")},
+ {"reverse", svn_cl__reverse_opt, 0,
+ N_("reverse operation sense (use with -c)")},
{0, 0, 0, 0}
};
@@ -273,8 +275,8 @@ const svn_opt_subcommand_desc_t svn_cl__
{ "diff", svn_cl__diff, {"di"}, N_
("Display the differences between two paths.\n"
- "usage: 1. diff [-c M | -r N[:M]] [TARGET[@REV]...]\n"
- " 2. diff [-c M | -r N[:M]] --old=OLD-TGT[@OLDREV] [--new=NEW-TGT[@NEWREV]] \\\n"
+ "usage: 1. diff [-c M | -r N[:M]] [--reverse] [TARGET[@REV]...]\n"
+ " 2. diff [-c M | -r N[:M]] [--reverse] --old=OLD-TGT[@OLDREV] [--new=NEW-TGT[@NEWREV]] \\\n"
" [PATH...]\n"
" 3. diff OLD-URL[@OLDREV] NEW-URL[@NEWREV]\n"
"\n"
@@ -285,6 +287,7 @@ const svn_opt_subcommand_desc_t svn_cl__
" must be specified. M defaults to the current working version if any\n"
" TARGET is a working copy path, otherwise it defaults to HEAD.\n"
" The '-c M' option is equivalent to '-r N:M' where N = M-1.\n"
+ " Specifying --reverse will reverse this to mean '-r M:N'\n"
"\n"
" 2. Display the differences between OLD-TGT as it was seen in OLDREV and\n"
" NEW-TGT as it was seen in NEWREV. PATHs, if given, are relative to\n"
@@ -293,12 +296,14 @@ const svn_opt_subcommand_desc_t svn_cl__
" NEW-TGT defaults to OLD-TGT if not specified. -r N makes OLDREV default\n"
" to N, -r N:M makes OLDREV default to N and NEWREV default to M,\n"
" -c M makes OLDREV default to M-1 and NEWREV default to M.\n"
+ " --reverse will reverse the sense of -c M to make OLDREV default to M\n"
+ " and NEWREV default to M-1."
"\n"
" 3. Shorthand for 'svn diff --old=OLD-URL[@OLDREV] --new=NEW-URL[@NEWREV]'\n"
"\n"
" Use just 'svn diff' to display local modifications in a working copy.\n"),
{'r', 'c', svn_cl__old_cmd_opt, svn_cl__new_cmd_opt, 'N',
- svn_cl__diff_cmd_opt, 'x', svn_cl__no_diff_deleted,
+ svn_cl__reverse_opt, svn_cl__diff_cmd_opt, 'x', svn_cl__no_diff_deleted,
svn_cl__notice_ancestry_opt, svn_cl__force_opt, SVN_CL__AUTH_OPTIONS,
svn_cl__config_dir_opt} },
@@ -420,7 +425,7 @@ const svn_opt_subcommand_desc_t svn_cl__
("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] [--reverse] 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"
@@ -434,14 +439,15 @@ const svn_opt_subcommand_desc_t svn_cl__
" 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. The '-c M'\n"
- " option is equivalent to '-r N:M' where N = M-1.\n"
+ " option is equivalent to '-r N:M' where N = M-1. --reverse will\n"
+ " reverse this sense to mean '-r M:N' (for backing out a change).\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"
" the sources have identical basenames that match a file within '.':\n"
" in which case, the differences will be applied to that file.\n"),
{'r', 'c', 'N', 'q', svn_cl__force_opt, svn_cl__dry_run_opt,
- svn_cl__merge_cmd_opt, svn_cl__ignore_ancestry_opt,
+ svn_cl__reverse_opt, svn_cl__merge_cmd_opt, svn_cl__ignore_ancestry_opt,
SVN_CL__AUTH_OPTIONS, svn_cl__config_dir_opt} },
{ "mkdir", svn_cl__mkdir, {0}, N_
@@ -780,6 +786,8 @@ main (int argc, const char * const *argv
svn_cl__cmd_baton_t command_baton;
svn_auth_baton_t *ab;
svn_config_t *cfg;
+ svn_boolean_t opt_c_given = FALSE;
+ svn_boolean_t reverse_opt_sense = FALSE;
/* Initialize the app. */
if (svn_cmdline_init ("svn", stderr) != EXIT_SUCCESS)
@@ -905,6 +913,7 @@ case 'c':
}
opt_state.start_revision.value.number = opt_state.end_revision.value.number - 1;
opt_state.start_revision.kind = svn_opt_revision_number;
+ opt_c_given = TRUE;
}
break;
case 'r':
@@ -1110,6 +1119,9 @@ main (int argc, const char * const *argv
case svn_cl__no_unlock_opt:
opt_state.no_unlock = TRUE;
break;
+ case svn_cl__reverse_opt:
+ reverse_opt_sense = TRUE;
+ break;
default:
/* Hmmm. Perhaps this would be a good place to squirrel away
opts that commands like svn diff might need. Hmmm indeed. */
@@ -1117,6 +1129,29 @@ main (int argc, const char * const *argv
}
}
+ /* If --reverse was given, flip the -c option, if given.
+ If there was no -c option, flag an error.
+ Note that we don't want to flip start/end if they did -r N:M explicitly,
+ only if they did -c. */
+ if (reverse_opt_sense)
+ {
+ if (opt_c_given)
+ {
+ /* Swap start and end. */
+ svn_revnum_t start = opt_state.start_revision.value.number;
+ opt_state.start_revision.value.number =
+ opt_state.end_revision.value.number;
+ opt_state.end_revision.value.number = start;
+ }
+ else
+ {
+ err = svn_error_create
+ (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Cannot specify --revision without -c"));
+ return svn_cmdline_handle_exit_error (err, pool, "svn: ");
+ }
+ }
+
/* ### This really belongs in libsvn_client. The trouble is,
there's no one place there to run it from, no
svn_client_init(). We'd have to add it to all the public
Index: subversion/tests/cmdline/diff_tests.py
===================================================================
--- subversion/tests/cmdline/diff_tests.py (revision 18084)
+++ subversion/tests/cmdline/diff_tests.py (working copy)
@@ -629,6 +629,16 @@ def diff_pure_repository_update_a_file(s
svntest.main.wc_passwd)
if check_update_added_file(diff_output): raise svntest.Failure
+ # Make sure specifying --reverse without a reversable co-argument fails.
+ expected_output = "svn: Cannot specify --revision without.*"
+ svntest.actions.run_and_verify_svn(None, None, expected_output,
+ 'diff', '-r', '4:5',
+ '--reverse',
+ '--username',
+ svntest.main.wc_author,
+ '--password',
+ svntest.main.wc_passwd)
+
diff_output, err_output = svntest.main.run_svn(None, 'diff', '-r', 'head')
if check_add_a_file_in_a_subdir_reverse(diff_output): raise svntest.Failure
@@ -671,6 +681,9 @@ def diff_only_property_change(sbox):
svntest.actions.run_and_verify_svn(None, expected_reverse_output, [],
'diff', '-r', '2:1')
+ svntest.actions.run_and_verify_svn(None, expected_reverse_output, [],
+ 'diff', '-c', '2', '--reverse')
+
svntest.actions.run_and_verify_svn(None, expected_output, [],
'diff', '-r', '1')
Index: subversion/tests/cmdline/merge_tests.py
===================================================================
--- subversion/tests/cmdline/merge_tests.py (revision 18084)
+++ subversion/tests/cmdline/merge_tests.py (working copy)
@@ -1282,6 +1282,72 @@ def merge_with_implicit_target (sbox):
#----------------------------------------------------------------------
+def merge_with_implicit_target_using_c_and_reverse (sbox):
+ "merge using the -c and --reverse options"
+
+ sbox.build()
+ wc_dir = sbox.wc_dir
+
+ # Change mu for revision 2
+ mu_path = os.path.join(wc_dir, 'A', 'mu')
+ orig_mu_text = svntest.tree.get_text(mu_path)
+ added_mu_text = ""
+ for x in range(2,11):
+ added_mu_text = added_mu_text + 'This is line ' + `x` + ' in mu\n'
+ svntest.main.file_append(mu_path, added_mu_text)
+
+ # Create expected output tree for initial commit
+ expected_output = wc.State(wc_dir, {
+ 'A/mu' : Item(verb='Sending'),
+ })
+
+ # Create expected status tree; all local revisions should be at 1,
+ # but mu should be at revision 2.
+ expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
+ expected_status.tweak(wc_rev=1)
+ expected_status.tweak('A/mu', wc_rev=2)
+
+ # Initial commit.
+ svntest.actions.run_and_verify_commit (wc_dir,
+ expected_output,
+ expected_status,
+ None,
+ None, None, None, None,
+ wc_dir)
+
+ # Make the "other" working copy
+ other_wc = sbox.add_wc_path('other')
+ svntest.actions.duplicate_dir(wc_dir, other_wc)
+
+ # Try the merge without an explicit target; it should succeed.
+ # Can't use run_and_verify_merge cuz it expects a directory argument.
+ mu_url = svntest.main.current_repo_url + '/A/mu'
+ was_cwd = os.getcwd()
+ try:
+ os.chdir(os.path.join(other_wc, 'A'))
+
+ # merge using URL for sourcepath
+ svntest.actions.run_and_verify_svn(None, ['U mu\n'], [],
+ 'merge', '-c', '2', '--reverse', mu_url)
+
+ # sanity-check resulting file
+ if (svntest.tree.get_text('mu') != orig_mu_text):
+ raise svntest.Failure
+
+ # merge using filename for sourcepath
+ # Cannot use run_and_verify_merge with a file target
+ svntest.actions.run_and_verify_svn(None, ['G mu\n'], [],
+ 'merge', '-c', '2', 'mu')
+
+ # sanity-check resulting file
+ if (svntest.tree.get_text('mu') != orig_mu_text + added_mu_text):
+ raise svntest.Failure
+
+ finally:
+ os.chdir(was_cwd)
+
+#----------------------------------------------------------------------
+
def merge_with_prev (sbox):
"merge operations using PREV revision"
@@ -3208,6 +3274,7 @@ test_list = [ None,
delete_file_and_dir,
simple_property_merges,
merge_with_implicit_target,
+ merge_with_implicit_target_using_c_and_reverse,
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
Received on Fri Jan 13 00:22:44 2006