You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2010/02/05 18:47:20 UTC
svn commit: r907016 - in /subversion/trunk/subversion: include/
include/private/ libsvn_client/ libsvn_diff/ svn/ tests/cmdline/
tests/libsvn_diff/
Author: stsp
Date: Fri Feb 5 17:47:20 2010
New Revision: 907016
URL: http://svn.apache.org/viewvc?rev=907016&view=rev
Log:
Teach 'svn patch' to apply unidiffs in reverse. Equivalent to "patch -R".
* subversion/include/private/svn_diff_private.h
(svn_diff__parse_next_patch): Add REVERSE parameter.
* subversion/include/svn_client.h
(svn_client_patch): Add REVERSE parameter.
* subversion/libsvn_client/patch.c
(apply_patches_baton_t): Add REVERSE field. Group patching-related
parameters in this baton together.
(apply_patches): Pass REVERSE parameter from baton to
svn_diff__parse_next_patch().
(svn_client_patch): Add REVERSE parameter. Pass it on via baton
to apply_patches.
* subversion/libsvn_diff/parse-diff.c
(parse_hunk_header): Add REVERSE parameter.
Swap offsets and lengths parsed from hunk header around if reversing.
(reverse_diff_transformer): New. A line transformer which reverts unidiff
hunk text. Used for writing diff text to reject files in reverse.
(parse_next_hunk): Add REVERSE parameter. Swap the meaning of '+' and '-'
around when reversing.
(svn_diff__parse_next_patch): Add REVERSE parameter. Swap old and new
filenames parsed from patch header around if reversing.
* subversion/svn/cl.h
(svn_cl__opt_state_t): Add new REVERSE_DIFF field.
* subversion/svn/main.c
(svn_cl__longopt_t): Add OPT_REVERSE_DIFF field.
(svn_cl__options): Define a new --reverse-diff option.
(svn_cl__cmd_table): Add --reverse-diff option to svn patch. While here,
clean up options svn patch has not been supporting anymore for some time.
(main): Handle --reverse-diff option.
* subversion/svn/patch-cmd.c
(svn_cl__patch): Pass REVERSE argument to svn_client_patch().
* subversion/tests/cmdline/patch_tests.py
(patch_reverse): New test. Applies a patch in reverse.
(test_list): Add new test.
* subversion/tests/libsvn_diff/parse-diff-test.c
(test_parse_unidiff): Make this test also test parsing unidiffs in reverse.
Modified:
subversion/trunk/subversion/include/private/svn_diff_private.h
subversion/trunk/subversion/include/svn_client.h
subversion/trunk/subversion/libsvn_client/patch.c
subversion/trunk/subversion/libsvn_diff/parse-diff.c
subversion/trunk/subversion/svn/cl.h
subversion/trunk/subversion/svn/main.c
subversion/trunk/subversion/svn/patch-cmd.c
subversion/trunk/subversion/tests/cmdline/patch_tests.py
subversion/trunk/subversion/tests/libsvn_diff/parse-diff-test.c
Modified: subversion/trunk/subversion/include/private/svn_diff_private.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/private/svn_diff_private.h?rev=907016&r1=907015&r2=907016&view=diff
==============================================================================
--- subversion/trunk/subversion/include/private/svn_diff_private.h (original)
+++ subversion/trunk/subversion/include/private/svn_diff_private.h Fri Feb 5 17:47:20 2010
@@ -110,11 +110,13 @@
/* Return the next *PATCH in PATCH_FILE.
* If no patch can be found, set *PATCH to NULL.
+ * If reverse is TRUE, invert the patch while parsing it.
* Allocate results in RESULT_POOL.
* Use SCRATCH_POOL for all other allocations. */
svn_error_t *
svn_diff__parse_next_patch(svn_patch_t **patch,
apr_file_t *patch_file,
+ svn_boolean_t reverse,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
Modified: subversion/trunk/subversion/include/svn_client.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_client.h?rev=907016&r1=907015&r2=907016&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_client.h (original)
+++ subversion/trunk/subversion/include/svn_client.h Fri Feb 5 17:47:20 2010
@@ -4843,6 +4843,11 @@
* stripped from paths obtained from the patch. It is an error is a
* negative strip count is passed.
*
+ * If @a reverse is @c TRUE, apply patches in reverse, deleting lines
+ * the patch would add and adding lines the patch would delete.
+ * This is useful when applying a unidiff which was created with the
+ * original and modified files swapped due to human error.
+ *
* If @a ctx->notify_func2 is non-NULL, invoke @a ctx->notify_func2 with
* @a ctx->notify_baton2 as patching progresses.
*
@@ -4856,6 +4861,7 @@
const char *local_abspath,
svn_boolean_t dry_run,
int strip_count,
+ svn_boolean_t reverse,
svn_client_ctx_t *ctx,
apr_pool_t *pool);
Modified: subversion/trunk/subversion/libsvn_client/patch.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/patch.c?rev=907016&r1=907015&r2=907016&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/patch.c (original)
+++ subversion/trunk/subversion/libsvn_client/patch.c Fri Feb 5 17:47:20 2010
@@ -1336,11 +1336,14 @@
/* Indicates whether we're doing a dry run. */
svn_boolean_t dry_run;
- /* The client context. */
- svn_client_ctx_t *ctx;
-
/* Number of leading components to strip from patch target paths. */
int strip_count;
+
+ /* Whether to apply the patch in reverse. */
+ svn_boolean_t reverse;
+
+ /* The client context. */
+ svn_client_ctx_t *ctx;
} apply_patches_baton_t;
/* Callback for use with svn_wc__call_with_write_lock().
@@ -1384,7 +1387,7 @@
SVN_ERR(btn->ctx->cancel_func(btn->ctx->cancel_baton));
SVN_ERR(svn_diff__parse_next_patch(&patch, patch_file,
- scratch_pool, iterpool));
+ btn->reverse, scratch_pool, iterpool));
if (patch)
{
patch_target_t *target;
@@ -1425,6 +1428,7 @@
const char *local_abspath,
svn_boolean_t dry_run,
int strip_count,
+ svn_boolean_t reverse,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
@@ -1439,6 +1443,7 @@
baton.dry_run = dry_run;
baton.ctx = ctx;
baton.strip_count = strip_count;
+ baton.reverse = reverse;
SVN_ERR(svn_wc__call_with_write_lock(apply_patches, &baton,
ctx->wc_ctx, local_abspath,
Modified: subversion/trunk/subversion/libsvn_diff/parse-diff.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_diff/parse-diff.c?rev=907016&r1=907015&r2=907016&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_diff/parse-diff.c (original)
+++ subversion/trunk/subversion/libsvn_diff/parse-diff.c Fri Feb 5 17:47:20 2010
@@ -103,9 +103,11 @@
/* Try to parse a hunk header in string HEADER, putting parsed information
* into HUNK. Return TRUE if the header parsed correctly.
+ * If REVERSE is TRUE, invert the hunk header while parsing it.
* Do all allocations in POOL. */
static svn_boolean_t
-parse_hunk_header(const char *header, svn_hunk_t *hunk, apr_pool_t *pool)
+parse_hunk_header(const char *header, svn_hunk_t *hunk,
+ svn_boolean_t reverse, apr_pool_t *pool)
{
static const char * const atat = "@@";
const char *p;
@@ -132,8 +134,16 @@
return FALSE;
/* Try to parse the first range. */
- if (! parse_range(&hunk->original_start, &hunk->original_length, range->data))
- return FALSE;
+ if (reverse)
+ {
+ if (! parse_range(&hunk->modified_start, &hunk->modified_length, range->data))
+ return FALSE;
+ }
+ else
+ {
+ if (! parse_range(&hunk->original_start, &hunk->original_length, range->data))
+ return FALSE;
+ }
/* Clear the stringbuf so we can reuse it for the second range. */
svn_stringbuf_setempty(range);
@@ -161,8 +171,16 @@
* but we ignore that. */
/* Try to parse the second range. */
- if (! parse_range(&hunk->modified_start, &hunk->modified_length, range->data))
- return FALSE;
+ if (reverse)
+ {
+ if (! parse_range(&hunk->original_start, &hunk->original_length, range->data))
+ return FALSE;
+ }
+ else
+ {
+ if (! parse_range(&hunk->modified_start, &hunk->modified_length, range->data))
+ return FALSE;
+ }
/* Hunk header is good. */
return TRUE;
@@ -204,14 +222,54 @@
return SVN_NO_ERROR;
}
+/** line-transformer callback to reverse a diff text. */
+static svn_error_t *
+reverse_diff_transformer(svn_stringbuf_t **buf,
+ const char *line,
+ void *baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_hunk_t hunk;
+
+ /* ### Pass the already parsed hunk via the baton?
+ * ### Maybe we should really make svn_stream_readline() a proper stream
+ * ### method and override it instead of adding special callbacks? */
+ if (parse_hunk_header(line, &hunk, FALSE, scratch_pool))
+ {
+ *buf = svn_stringbuf_createf(result_pool,
+ "@@ -%lu,%lu +%lu,%lu @@",
+ hunk.modified_start,
+ hunk.modified_length,
+ hunk.original_start,
+ hunk.original_length);
+ }
+ else if (line[0] == '+')
+ {
+ *buf = svn_stringbuf_create(line, result_pool);
+ (*buf)->data[0] = '-';
+ }
+ else if (line[0] == '-')
+ {
+ *buf = svn_stringbuf_create(line, result_pool);
+ (*buf)->data[0] = '+';
+ }
+ else
+ *buf = svn_stringbuf_create(line, result_pool);
+
+ return SVN_NO_ERROR;
+}
+
/* Return the next *HUNK from a PATCH, using STREAM to read data
* from the patch file. If no hunk can be found, set *HUNK to NULL.
+ * If REVERSE is TRUE, invert the hunk while parsing it.
* Allocate results in RESULT_POOL.
* Use SCRATCH_POOL for all other allocations. */
static svn_error_t *
parse_next_hunk(svn_hunk_t **hunk,
svn_patch_t *patch,
svn_stream_t *stream,
+ svn_boolean_t reverse,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
@@ -275,6 +333,19 @@
if (in_hunk)
{
char c;
+ char add;
+ char del;
+
+ if (reverse)
+ {
+ add = '-';
+ del = '+';
+ }
+ else
+ {
+ add = '+';
+ del = '-';
+ }
if (! hunk_seen)
{
@@ -294,7 +365,7 @@
else
leading_context++;
}
- else if (c == '+' || c == '-')
+ else if (c == add || c == del)
{
hunk_seen = TRUE;
changed_line_seen = TRUE;
@@ -304,7 +375,7 @@
if (trailing_context > 0)
trailing_context = 0;
- if (original_lines > 0 && c == '-')
+ if (original_lines > 0 && c == del)
original_lines--;
}
else
@@ -322,8 +393,9 @@
{
if (starts_with(line->data, atat))
{
- /* Looks like we have a hunk header, let's try to rip it apart. */
- in_hunk = parse_hunk_header(line->data, *hunk, iterpool);
+ /* Looks like we have a hunk header, try to rip it apart. */
+ in_hunk = parse_hunk_header(line->data, *hunk, reverse,
+ iterpool);
if (in_hunk)
original_lines = (*hunk)->original_length;
}
@@ -377,8 +449,18 @@
remove_leading_char_transformer);
/* Set the hunk's texts. */
(*hunk)->diff_text = diff_text;
- (*hunk)->original_text = original_text;
- (*hunk)->modified_text = modified_text;
+ if (reverse)
+ {
+ svn_stream_set_line_transformer_callback(diff_text,
+ reverse_diff_transformer);
+ (*hunk)->original_text = modified_text;
+ (*hunk)->modified_text = original_text;
+ }
+ else
+ {
+ (*hunk)->original_text = original_text;
+ (*hunk)->modified_text = modified_text;
+ }
(*hunk)->leading_context = leading_context;
(*hunk)->trailing_context = trailing_context;
}
@@ -419,6 +501,7 @@
svn_error_t *
svn_diff__parse_next_patch(svn_patch_t **patch,
apr_file_t *patch_file,
+ svn_boolean_t reverse,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
@@ -489,14 +572,20 @@
if ((! in_header) && strcmp(indicator, minus) == 0)
{
/* First line of header contains old filename. */
- (*patch)->old_filename = apr_pstrdup(result_pool, canon_path);
+ if (reverse)
+ (*patch)->new_filename = apr_pstrdup(result_pool, canon_path);
+ else
+ (*patch)->old_filename = apr_pstrdup(result_pool, canon_path);
indicator = plus;
in_header = TRUE;
}
else if (in_header && strcmp(indicator, plus) == 0)
{
/* Second line of header contains new filename. */
- (*patch)->new_filename = apr_pstrdup(result_pool, canon_path);
+ if (reverse)
+ (*patch)->old_filename = apr_pstrdup(result_pool, canon_path);
+ else
+ (*patch)->new_filename = apr_pstrdup(result_pool, canon_path);
in_header = FALSE;
break; /* All good! */
}
@@ -519,8 +608,8 @@
{
svn_pool_clear(iterpool);
- SVN_ERR(parse_next_hunk(&hunk, *patch, stream, result_pool,
- iterpool));
+ SVN_ERR(parse_next_hunk(&hunk, *patch, stream, reverse,
+ result_pool, iterpool));
if (hunk)
APR_ARRAY_PUSH((*patch)->hunks, svn_hunk_t *) = hunk;
}
Modified: subversion/trunk/subversion/svn/cl.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/cl.h?rev=907016&r1=907015&r2=907016&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/cl.h (original)
+++ subversion/trunk/subversion/svn/cl.h Fri Feb 5 17:47:20 2010
@@ -223,6 +223,7 @@
otherwise be rejected as "untrusted" */
int strip_count; /* number of leading path components to strip */
svn_boolean_t ignore_keywords; /* do not expand keywords */
+ svn_boolean_t reverse_diff; /* reverse a diff (e.g. when patching) */
} svn_cl__opt_state_t;
Modified: subversion/trunk/subversion/svn/main.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/main.c?rev=907016&r1=907015&r2=907016&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/main.c (original)
+++ subversion/trunk/subversion/svn/main.c Fri Feb 5 17:47:20 2010
@@ -115,7 +115,8 @@
opt_reintegrate,
opt_trust_server_cert,
opt_show_copies_as_adds,
- opt_ignore_keywords
+ opt_ignore_keywords,
+ opt_reverse_diff,
} svn_cl__longopt_t;
/* Option codes and descriptions for the command line client.
@@ -342,7 +343,10 @@
N_("don't expand keywords\n"
" "
"[alias: --ik]")},
-
+ {"reverse-diff", opt_reverse_diff, 0,
+ N_("apply the unidiff in reverse\n"
+ " "
+ "[alias: --rd]")},
/* Long-opt Aliases
*
* These have NULL desriptions, but an option code that matches some
@@ -358,6 +362,7 @@
{"na", opt_notice_ancestry, 0, NULL},
{"ia", opt_ignore_ancestry, 0, NULL},
{"ie", opt_ignore_externals, 0, NULL},
+ {"rd", opt_reverse_diff, 0, NULL},
{"ro", opt_record_only, 0, NULL},
{"cd", opt_config_dir, 1, NULL},
{"cl", opt_changelist, 1, NULL},
@@ -800,7 +805,7 @@
" for addition. Use 'svn revert' to undo deletions and additions you\n"
" do not agree with.\n"
),
- {'q', opt_dry_run, opt_accept, opt_merge_cmd, 'p'} },
+ {'q', opt_dry_run, 'p', opt_reverse_diff} },
{ "propdel", svn_cl__propdel, {"pdel", "pd"}, N_
("Remove a property from files, dirs, or revisions.\n"
@@ -1712,6 +1717,9 @@
case opt_ignore_keywords:
opt_state.ignore_keywords = TRUE;
break;
+ case opt_reverse_diff:
+ opt_state.reverse_diff = TRUE;
+ break;
default:
/* Hmmm. Perhaps this would be a good place to squirrel away
opts that commands like svn diff might need. Hmmm indeed. */
Modified: subversion/trunk/subversion/svn/patch-cmd.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/patch-cmd.c?rev=907016&r1=907015&r2=907016&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/patch-cmd.c (original)
+++ subversion/trunk/subversion/svn/patch-cmd.c Fri Feb 5 17:47:20 2010
@@ -78,7 +78,7 @@
SVN_ERR(svn_client_patch(abs_patch_path, abs_target_path,
opt_state->dry_run, opt_state->strip_count,
- ctx, pool));
+ opt_state->reverse_diff, ctx, pool));
if (! opt_state->quiet)
SVN_ERR(svn_cl__print_conflict_stats(ctx->notify_baton2, pool));
Modified: subversion/trunk/subversion/tests/cmdline/patch_tests.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/patch_tests.py?rev=907016&r1=907015&r2=907016&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/patch_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/patch_tests.py Fri Feb 5 17:47:20 2010
@@ -1179,6 +1179,176 @@
1, # check-props
1) # dry-run
+def patch_reverse(sbox):
+ "apply a patch in reverse"
+
+ sbox.build()
+ wc_dir = sbox.wc_dir
+
+ patch_file_path = tempfile.mkstemp(dir=os.path.abspath(svntest.main.temp_dir))[1]
+ mu_path = os.path.join(wc_dir, 'A', 'mu')
+
+ mu_contents = [
+ "Dear internet user,\n",
+ "\n",
+ "We wish to congratulate you over your email success in our computer\n",
+ "Balloting. This is a Millennium Scientific Electronic Computer Draw\n",
+ "in which email addresses were used. All participants were selected\n",
+ "through a computer ballot system drawn from over 100,000 company\n",
+ "and 50,000,000 individual email addresses from all over the world.\n",
+ "\n",
+ "Your email address drew and have won the sum of 750,000 Euros\n",
+ "( Seven Hundred and Fifty Thousand Euros) in cash credited to\n",
+ "file with\n",
+ " REFERENCE NUMBER: ESP/WIN/008/05/10/MA;\n",
+ " WINNING NUMBER : 14-17-24-34-37-45-16\n",
+ " BATCH NUMBERS :\n",
+ " EULO/1007/444/606/08;\n",
+ " SERIAL NUMBER: 45327\n",
+ "and PROMOTION DATE: 13th June. 2009\n",
+ "\n",
+ "To claim your winning prize, you are to contact the appointed\n",
+ "agent below as soon as possible for the immediate release of your\n",
+ "winnings with the below details.\n",
+ "\n",
+ "Again, we wish to congratulate you over your email success in our\n"
+ "computer Balloting.\n"
+ ]
+
+ # Set mu contents
+ svntest.main.file_write(mu_path, ''.join(mu_contents))
+ expected_output = svntest.wc.State(wc_dir, {
+ 'A/mu' : Item(verb='Sending'),
+ })
+ expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+ expected_status.tweak('A/mu', wc_rev=2)
+ svntest.actions.run_and_verify_commit(wc_dir, expected_output,
+ expected_status, None, wc_dir)
+
+ # Apply patch
+
+ unidiff_patch = [
+ "Index: A/D/gamma\n",
+ "===================================================================\n",
+ "--- A/D/gamma\t(revision 1)\n",
+ "+++ A/D/gamma\t(working copy)\n",
+ "@@ -1 +1 @@\n",
+ "+This is the file 'gamma'.\n",
+ "-It is the file 'gamma'.\n",
+ "Index: iota\n",
+ "===================================================================\n",
+ "--- iota\t(revision 1)\n",
+ "+++ iota\t(working copy)\n",
+ "@@ -1,2 +1 @@\n",
+ " This is the file 'iota'.\n",
+ "-Some more bytes\n",
+ "\n",
+ "Index: new\n",
+ "===================================================================\n",
+ "--- new (revision 0)\n",
+ "+++ new (revision 0)\n",
+ "@@ -1 +0,0 @@\n",
+ "-new\n",
+ "\n",
+ "--- A/mu 2009-06-24 15:23:55.000000000 +0100\n",
+ "+++ A/mu.orig 2009-06-24 15:21:23.000000000 +0100\n",
+ "@@ -6,9 +6,6 @@\n",
+ " through a computer ballot system drawn from over 100,000 company\n",
+ " and 50,000,000 individual email addresses from all over the world.\n",
+ " \n",
+ "-It is a promotional program aimed at encouraging internet users;\n",
+ "-therefore you do not need to buy ticket to enter for it.\n",
+ "-\n",
+ " Your email address drew and have won the sum of 750,000 Euros\n",
+ " ( Seven Hundred and Fifty Thousand Euros) in cash credited to\n",
+ " file with\n",
+ "@@ -17,8 +14,11 @@\n",
+ " BATCH NUMBERS :\n",
+ " EULO/1007/444/606/08;\n",
+ " SERIAL NUMBER: 45327\n",
+ "+and PROMOTION DATE: 13th June. 2009\n",
+ "-and PROMOTION DATE: 14th June. 2009\n",
+ " \n",
+ " To claim your winning prize, you are to contact the appointed\n",
+ " agent below as soon as possible for the immediate release of your\n",
+ " winnings with the below details.\n",
+ "+\n",
+ "+Again, we wish to congratulate you over your email success in our\n",
+ "+computer Balloting.\n",
+ "Index: A/B/E/beta\n",
+ "===================================================================\n",
+ "--- A/B/E/beta (working copy)\n",
+ "+++ A/B/E/beta (revision 1)\n",
+ "@@ -0,0 +1 @@\n",
+ "+This is the file 'beta'.\n",
+ ]
+
+ svntest.main.file_write(patch_file_path, ''.join(unidiff_patch))
+
+ gamma_contents = "It is the file 'gamma'.\n"
+ iota_contents = "This is the file 'iota'.\nSome more bytes\n"
+ new_contents = "new\n"
+ mu_contents = [
+ "Dear internet user,\n",
+ "\n",
+ "We wish to congratulate you over your email success in our computer\n",
+ "Balloting. This is a Millennium Scientific Electronic Computer Draw\n",
+ "in which email addresses were used. All participants were selected\n",
+ "through a computer ballot system drawn from over 100,000 company\n",
+ "and 50,000,000 individual email addresses from all over the world.\n",
+ "\n",
+ "It is a promotional program aimed at encouraging internet users;\n",
+ "therefore you do not need to buy ticket to enter for it.\n",
+ "\n",
+ "Your email address drew and have won the sum of 750,000 Euros\n",
+ "( Seven Hundred and Fifty Thousand Euros) in cash credited to\n",
+ "file with\n",
+ " REFERENCE NUMBER: ESP/WIN/008/05/10/MA;\n",
+ " WINNING NUMBER : 14-17-24-34-37-45-16\n",
+ " BATCH NUMBERS :\n",
+ " EULO/1007/444/606/08;\n",
+ " SERIAL NUMBER: 45327\n",
+ "and PROMOTION DATE: 14th June. 2009\n",
+ "\n",
+ "To claim your winning prize, you are to contact the appointed\n",
+ "agent below as soon as possible for the immediate release of your\n",
+ "winnings with the below details.\n",
+ ]
+
+ expected_output = [
+ 'U %s\n' % os.path.join(wc_dir, 'A', 'D', 'gamma'),
+ 'U %s\n' % os.path.join(wc_dir, 'iota'),
+ 'A %s\n' % os.path.join(wc_dir, 'new'),
+ 'U %s\n' % os.path.join(wc_dir, 'A', 'mu'),
+ 'D %s\n' % os.path.join(wc_dir, 'A', 'B', 'E', 'beta'),
+ ]
+
+ expected_disk = svntest.main.greek_state.copy()
+ expected_disk.tweak('A/D/gamma', contents=gamma_contents)
+ expected_disk.tweak('iota', contents=iota_contents)
+ expected_disk.add({'new' : Item(contents=new_contents)})
+ expected_disk.tweak('A/mu', contents=''.join(mu_contents))
+ expected_disk.remove('A/B/E/beta')
+
+ expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+ expected_status.tweak('A/D/gamma', status='M ')
+ expected_status.tweak('iota', status='M ')
+ expected_status.add({'new' : Item(status='A ', wc_rev=0)})
+ expected_status.tweak('A/mu', status='M ', wc_rev=2)
+ expected_status.tweak('A/B/E/beta', status='D ')
+
+ expected_skip = wc.State('', { })
+
+ svntest.actions.run_and_verify_patch(wc_dir, os.path.abspath(patch_file_path),
+ expected_output,
+ expected_disk,
+ expected_status,
+ expected_skip,
+ None, # expected err
+ 1, # check-props
+ 1, # dry-run
+ '--reverse-diff')
+
########################################################################
#Run the tests
@@ -1193,6 +1363,7 @@
patch_reject,
patch_keywords,
patch_with_fuzz,
+ patch_reverse,
]
if __name__ == '__main__':
Modified: subversion/trunk/subversion/tests/libsvn_diff/parse-diff-test.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_diff/parse-diff-test.c?rev=907016&r1=907015&r2=907016&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_diff/parse-diff-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_diff/parse-diff-test.c Fri Feb 5 17:47:20 2010
@@ -63,12 +63,10 @@
apr_file_t *patch_file;
apr_status_t status;
apr_size_t len;
- apr_off_t pos;
const char *fname = "test_parse_unidiff.patch";
- svn_patch_t *patch;
- svn_hunk_t *hunk;
- svn_stringbuf_t *buf;
- svn_boolean_t eof;
+ svn_boolean_t reverse;
+ int i;
+ apr_pool_t *iterpool;
/* Create a patch file. */
status = apr_file_open(&patch_file, fname,
@@ -83,69 +81,121 @@
return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
"Cannot write to '%s'", fname);
- /* Reset file pointer. */
- pos = 0;
- SVN_ERR(svn_io_file_seek(patch_file, APR_SET, &pos, pool));
-
- /* We have two patches with one hunk each.
- * Parse the first patch. */
- SVN_ERR(svn_diff__parse_next_patch(&patch, patch_file, pool, pool));
- SVN_ERR_ASSERT(patch);
- SVN_ERR_ASSERT(! strcmp(patch->old_filename, "A/C/gamma"));
- SVN_ERR_ASSERT(! strcmp(patch->new_filename, "A/C/gamma"));
- SVN_ERR_ASSERT(patch->hunks->nelts == 1);
-
- /* Make sure original text was parsed correctly. */
- hunk = APR_ARRAY_IDX(patch->hunks, 0, svn_hunk_t *);
- SVN_ERR(svn_stream_readline(hunk->original_text, &buf, NL, &eof, pool));
- SVN_ERR_ASSERT(! eof);
- SVN_ERR_ASSERT(! strcmp(buf->data, "This is the file 'gamma'."));
- /* Now we should get EOF. */
- SVN_ERR(svn_stream_readline(hunk->original_text, &buf, NL, &eof, pool));
- SVN_ERR_ASSERT(eof);
- SVN_ERR_ASSERT(buf->len == 0);
-
- /* Make sure modified text was parsed correctly. */
- SVN_ERR(svn_stream_readline(hunk->modified_text, &buf, NL, &eof, pool));
- SVN_ERR_ASSERT(! eof);
- SVN_ERR_ASSERT(! strcmp(buf->data, "This is the file 'gamma'."));
- SVN_ERR(svn_stream_readline(hunk->modified_text, &buf, NL, &eof, pool));
- SVN_ERR_ASSERT(! eof);
- SVN_ERR_ASSERT(! strcmp(buf->data, "some more bytes to 'gamma'"));
- /* Now we should get EOF. */
- SVN_ERR(svn_stream_readline(hunk->modified_text, &buf, NL, &eof, pool));
- SVN_ERR_ASSERT(eof);
- SVN_ERR_ASSERT(buf->len == 0);
-
- /* Parse the second patch. */
- SVN_ERR(svn_diff__parse_next_patch(&patch, patch_file, pool, pool));
- SVN_ERR_ASSERT(patch);
- SVN_ERR_ASSERT(! strcmp(patch->old_filename, "A/D/gamma.orig"));
- SVN_ERR_ASSERT(! strcmp(patch->new_filename, "A/D/gamma"));
- SVN_ERR_ASSERT(patch->hunks->nelts == 1);
-
- /* Make sure original text was parsed correctly. */
- hunk = APR_ARRAY_IDX(patch->hunks, 0, svn_hunk_t *);
- SVN_ERR(svn_stream_readline(hunk->original_text, &buf, NL, &eof, pool));
- SVN_ERR_ASSERT(! eof);
- SVN_ERR_ASSERT(! strcmp(buf->data, "This is the file 'gamma'."));
- SVN_ERR(svn_stream_readline(hunk->original_text, &buf, NL, &eof, pool));
- SVN_ERR_ASSERT(! eof);
- SVN_ERR_ASSERT(! strcmp(buf->data, "some less bytes to 'gamma'"));
- /* Now we should get EOF. */
- SVN_ERR(svn_stream_readline(hunk->original_text, &buf, NL, &eof, pool));
- SVN_ERR_ASSERT(eof);
- SVN_ERR_ASSERT(buf->len == 0);
-
- /* Make sure modified text was parsed correctly. */
- SVN_ERR(svn_stream_readline(hunk->modified_text, &buf, NL, &eof, pool));
- SVN_ERR_ASSERT(! eof);
- SVN_ERR_ASSERT(! strcmp(buf->data, "This is the file 'gamma'."));
- /* Now we should get EOF. */
- SVN_ERR(svn_stream_readline(hunk->modified_text, &buf, NL, &eof, pool));
- SVN_ERR_ASSERT(eof);
- SVN_ERR_ASSERT(buf->len == 0);
-
+ reverse = FALSE;
+ iterpool = svn_pool_create(pool);
+ for (i = 0; i < 2; i++)
+ {
+ svn_patch_t *patch;
+ svn_hunk_t *hunk;
+ svn_stringbuf_t *buf;
+ svn_boolean_t eof;
+ apr_off_t pos;
+ svn_stream_t *original_text;
+ svn_stream_t *modified_text;
+
+ svn_pool_clear(iterpool);
+
+ /* Reset file pointer. */
+ pos = 0;
+ SVN_ERR(svn_io_file_seek(patch_file, APR_SET, &pos, iterpool));
+
+ /* We have two patches with one hunk each.
+ * Parse the first patch. */
+ SVN_ERR(svn_diff__parse_next_patch(&patch, patch_file, reverse,
+ iterpool, iterpool));
+ SVN_ERR_ASSERT(patch);
+ SVN_ERR_ASSERT(! strcmp(patch->old_filename, "A/C/gamma"));
+ SVN_ERR_ASSERT(! strcmp(patch->new_filename, "A/C/gamma"));
+ SVN_ERR_ASSERT(patch->hunks->nelts == 1);
+
+ hunk = APR_ARRAY_IDX(patch->hunks, 0, svn_hunk_t *);
+ if (reverse)
+ {
+ /* Hunk texts come out of the parser inverted,
+ * so this inverts them a second time. */
+ original_text = hunk->modified_text;
+ modified_text = hunk->original_text;
+ }
+ else
+ {
+ original_text = hunk->original_text;
+ modified_text = hunk->modified_text;
+ }
+
+ /* Make sure original text was parsed correctly. */
+ SVN_ERR(svn_stream_readline(original_text, &buf, NL, &eof, pool));
+ SVN_ERR_ASSERT(! eof);
+ SVN_ERR_ASSERT(! strcmp(buf->data, "This is the file 'gamma'."));
+ /* Now we should get EOF. */
+ SVN_ERR(svn_stream_readline(original_text, &buf, NL, &eof, pool));
+ SVN_ERR_ASSERT(eof);
+ SVN_ERR_ASSERT(buf->len == 0);
+
+ /* Make sure modified text was parsed correctly. */
+ SVN_ERR(svn_stream_readline(modified_text, &buf, NL, &eof, pool));
+ SVN_ERR_ASSERT(! eof);
+ SVN_ERR_ASSERT(! strcmp(buf->data, "This is the file 'gamma'."));
+ SVN_ERR(svn_stream_readline(modified_text, &buf, NL, &eof, pool));
+ SVN_ERR_ASSERT(! eof);
+ SVN_ERR_ASSERT(! strcmp(buf->data, "some more bytes to 'gamma'"));
+ /* Now we should get EOF. */
+ SVN_ERR(svn_stream_readline(modified_text, &buf, NL, &eof, pool));
+ SVN_ERR_ASSERT(eof);
+ SVN_ERR_ASSERT(buf->len == 0);
+
+ /* Parse the second patch. */
+ SVN_ERR(svn_diff__parse_next_patch(&patch, patch_file, reverse, pool, pool));
+ SVN_ERR_ASSERT(patch);
+ if (reverse)
+ {
+ SVN_ERR_ASSERT(! strcmp(patch->new_filename, "A/D/gamma.orig"));
+ SVN_ERR_ASSERT(! strcmp(patch->old_filename, "A/D/gamma"));
+ }
+ else
+ {
+ SVN_ERR_ASSERT(! strcmp(patch->old_filename, "A/D/gamma.orig"));
+ SVN_ERR_ASSERT(! strcmp(patch->new_filename, "A/D/gamma"));
+ }
+ SVN_ERR_ASSERT(patch->hunks->nelts == 1);
+
+ hunk = APR_ARRAY_IDX(patch->hunks, 0, svn_hunk_t *);
+ if (reverse)
+ {
+ /* Hunk texts come out of the parser inverted,
+ * so this inverts them a second time. */
+ original_text = hunk->modified_text;
+ modified_text = hunk->original_text;
+ }
+ else
+ {
+ original_text = hunk->original_text;
+ modified_text = hunk->modified_text;
+ }
+
+ /* Make sure original text was parsed correctly. */
+ SVN_ERR(svn_stream_readline(original_text, &buf, NL, &eof, pool));
+ SVN_ERR_ASSERT(! eof);
+ SVN_ERR_ASSERT(! strcmp(buf->data, "This is the file 'gamma'."));
+ SVN_ERR(svn_stream_readline(original_text, &buf, NL, &eof, pool));
+ SVN_ERR_ASSERT(! eof);
+ SVN_ERR_ASSERT(! strcmp(buf->data, "some less bytes to 'gamma'"));
+ /* Now we should get EOF. */
+ SVN_ERR(svn_stream_readline(original_text, &buf, NL, &eof, pool));
+ SVN_ERR_ASSERT(eof);
+ SVN_ERR_ASSERT(buf->len == 0);
+
+ /* Make sure modified text was parsed correctly. */
+ SVN_ERR(svn_stream_readline(modified_text, &buf, NL, &eof, pool));
+ SVN_ERR_ASSERT(! eof);
+ SVN_ERR_ASSERT(! strcmp(buf->data, "This is the file 'gamma'."));
+ /* Now we should get EOF. */
+ SVN_ERR(svn_stream_readline(modified_text, &buf, NL, &eof, pool));
+ SVN_ERR_ASSERT(eof);
+ SVN_ERR_ASSERT(buf->len == 0);
+
+ reverse = !reverse;
+ }
+ svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}