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;
 }