You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2015/12/16 14:57:16 UTC

svn commit: r1720359 - in /subversion/trunk/subversion: libsvn_client/patch.c libsvn_diff/parse-diff.c tests/cmdline/patch_tests.py

Author: rhuijben
Date: Wed Dec 16 13:57:15 2015
New Revision: 1720359

URL: http://svn.apache.org/viewvc?rev=1720359&view=rev
Log:
Following up on r1720351, fix handling patch files that have more context
than the diff headers tell us in the same way as a shortage of context.

This change makes a bad patch in our testsuite visible, where we expected
bad behavior. This patch fixes that test and adds more testing.

* subversion/libsvn_client/patch.c
  (hunk_info_t): Separate reported fuzz from matching fuzz as mixing them
    causes errors.
  (get_hunk_info): Store both fuzz values.
  (apply_hunk): Use match fuzz for apply-magic.
  (send_hunk_notification): Report fuzz with penalty.

* subversion/libsvn_diff/parse-diff.c
  (parse_next_hunk): Parse extra context when that exists. Report this case
    as fuzz penalty, like a fuzz shortage.

* subversion/tests/cmdline/patch_tests.py
  (patch_empty_file): Fix patch to work as document. Expect the proper result.
    Extract the original version as new test.
  (missing_trailing_context): Extend test with reverse patching and too much
    trailing context.
  (patch_missed_trail): New test, with the original case of patch_empty_file.
    Expect that it no longer ignores the 'replacement' line.
  (test_list): Add patch_missed_trail.

Modified:
    subversion/trunk/subversion/libsvn_client/patch.c
    subversion/trunk/subversion/libsvn_diff/parse-diff.c
    subversion/trunk/subversion/tests/cmdline/patch_tests.py

Modified: subversion/trunk/subversion/libsvn_client/patch.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/patch.c?rev=1720359&r1=1720358&r2=1720359&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/patch.c (original)
+++ subversion/trunk/subversion/libsvn_client/patch.c Wed Dec 16 13:57:15 2015
@@ -67,7 +67,10 @@ typedef struct hunk_info_t {
 
   /* The fuzz factor used when matching this hunk, i.e. how many
    * lines of leading and trailing context to ignore during matching. */
-  svn_linenum_t fuzz;
+  svn_linenum_t match_fuzz;
+
+  /* match_fuzz + the penalty caused by bad patch files */
+  svn_linenum_t report_fuzz;
 } hunk_info_t;
 
 /* A struct carrying information related to the patched and unpatched
@@ -2087,7 +2090,8 @@ get_hunk_info(hunk_info_t **hi, patch_ta
   (*hi)->matched_line = matched_line;
   (*hi)->rejected = (matched_line == 0);
   (*hi)->already_applied = already_applied;
-  (*hi)->fuzz = fuzz;
+  (*hi)->report_fuzz = fuzz;
+  (*hi)->match_fuzz = fuzz - svn_diff_hunk__get_fuzz_penalty(hunk);
 
   return SVN_NO_ERROR;
 }
@@ -2209,6 +2213,7 @@ apply_hunk(patch_target_t *target, targe
   svn_linenum_t lines_read;
   svn_boolean_t eof;
   apr_pool_t *iterpool;
+  svn_linenum_t fuzz = hi->match_fuzz;
 
   /* ### Is there a cleaner way to describe if we have an existing target?
    */
@@ -2220,13 +2225,13 @@ apply_hunk(patch_target_t *target, targe
        * Also copy leading lines of context which matched with fuzz.
        * The target has changed on the fuzzy-matched lines,
        * so we should retain the target's version of those lines. */
-      SVN_ERR(copy_lines_to_target(content, hi->matched_line + hi->fuzz,
+      SVN_ERR(copy_lines_to_target(content, hi->matched_line + fuzz,
                                    pool));
 
       /* Skip the target's version of the hunk.
        * Don't skip trailing lines which matched with fuzz. */
       line = content->current_line +
-             svn_diff_hunk_get_original_length(hi->hunk) - (2 * hi->fuzz);
+             svn_diff_hunk_get_original_length(hi->hunk) - (2 * fuzz);
       SVN_ERR(seek_to_line(content, line, pool));
       if (content->current_line != line && ! content->eof)
         {
@@ -2253,8 +2258,8 @@ apply_hunk(patch_target_t *target, targe
                                                    &eol_str, &eof,
                                                    iterpool, iterpool));
       lines_read++;
-      if (lines_read > hi->fuzz &&
-          lines_read <= svn_diff_hunk_get_modified_length(hi->hunk) - hi->fuzz)
+      if (lines_read > fuzz &&
+          lines_read <= svn_diff_hunk_get_modified_length(hi->hunk) - fuzz)
         {
           apr_size_t len;
 
@@ -2323,7 +2328,7 @@ send_hunk_notification(const hunk_info_t
   notify->hunk_modified_length =
     svn_diff_hunk_get_modified_length(hi->hunk);
   notify->hunk_matched_line = hi->matched_line;
-  notify->hunk_fuzz = hi->fuzz;
+  notify->hunk_fuzz = hi->report_fuzz;
   notify->prop_name = prop_name;
 
   ctx->notify_func2(ctx->notify_baton2, notify, pool);

Modified: subversion/trunk/subversion/libsvn_diff/parse-diff.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_diff/parse-diff.c?rev=1720359&r1=1720358&r2=1720359&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_diff/parse-diff.c (original)
+++ subversion/trunk/subversion/libsvn_diff/parse-diff.c Wed Dec 16 13:57:15 2015
@@ -1193,24 +1193,38 @@ parse_next_hunk(svn_diff_hunk_t **hunk,
             }
 
           c = line->data[0];
-          if (original_lines > 0 && modified_lines > 0 &&
-              ((c == ' ')
+          if (c == ' '
+              || ((original_lines > 0 && modified_lines > 0)
+                  && ( 
                /* Tolerate chopped leading spaces on empty lines. */
-               || (! eof && line->len == 0)
+                      (! eof && line->len == 0)
                /* Maybe tolerate chopped leading spaces on non-empty lines. */
-               || (ignore_whitespace && c != del && c != add)))
+                      || (ignore_whitespace && c != del && c != add))))
             {
               /* It's a "context" line in the hunk. */
               hunk_seen = TRUE;
-              original_lines--;
-              modified_lines--;
+              if (original_lines > 0)
+                original_lines--;
+              else
+                {
+                  (*hunk)->original_length++;
+                  (*hunk)->original_fuzz++;
+                }
+              if (modified_lines > 0)
+                modified_lines--;
+              else
+                {
+                  (*hunk)->modified_length++;
+                  (*hunk)->modified_fuzz++;
+                }
               if (changed_line_seen)
                 trailing_context++;
               else
                 leading_context++;
               last_line_type = context_line;
             }
-          else if (original_lines > 0 && c == del)
+          else if (c == del
+                   && (original_lines > 0 || line->data[1] != del))
             {
               /* It's a "deleted" line in the hunk. */
               hunk_seen = TRUE;
@@ -1221,10 +1235,17 @@ parse_next_hunk(svn_diff_hunk_t **hunk,
               if (trailing_context > 0)
                 trailing_context = 0;
 
-              original_lines--;
+              if (original_lines > 0)
+                original_lines--;
+              else
+                {
+                  (*hunk)->original_length++;
+                  (*hunk)->original_fuzz++;
+                }
               last_line_type = original_line;
             }
-          else if (modified_lines > 0 && c == add)
+          else if (c == add
+                   && (modified_lines > 0 || line->data[1] != add))
             {
               /* It's an "added" line in the hunk. */
               hunk_seen = TRUE;
@@ -1235,7 +1256,13 @@ parse_next_hunk(svn_diff_hunk_t **hunk,
               if (trailing_context > 0)
                 trailing_context = 0;
 
-              modified_lines--;
+              if (modified_lines > 0)
+                modified_lines--;
+              else
+                {
+                  (*hunk)->modified_length++;
+                  (*hunk)->modified_fuzz++;
+                }
               last_line_type = modified_line;
             }
           else
@@ -1251,7 +1278,6 @@ parse_next_hunk(svn_diff_hunk_t **hunk,
                    * after the hunk text. */
                   end = last_line;
                 }
-
               if (original_end == 0)
                 original_end = end;
               if (modified_end == 0)
@@ -1335,12 +1361,12 @@ parse_next_hunk(svn_diff_hunk_t **hunk,
       if (original_lines)
         {
           (*hunk)->original_length -= original_lines;
-          (*hunk)->original_fuzz = original_lines;
+          (*hunk)->original_fuzz += original_lines;
         }
       if (modified_lines)
         {
           (*hunk)->modified_length -= modified_lines;
-          (*hunk)->modified_fuzz = modified_lines;
+          (*hunk)->modified_fuzz += modified_lines;
         }
 
       (*hunk)->patch = patch;

Modified: subversion/trunk/subversion/tests/cmdline/patch_tests.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/patch_tests.py?rev=1720359&r1=1720358&r2=1720359&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/patch_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/patch_tests.py Wed Dec 16 13:57:15 2015
@@ -4518,7 +4518,7 @@ def patch_empty_file(sbox):
     "--- lf.txt\t(revision 2)\n",
     "+++ lf.txt\t(working copy)\n",
     "@@ -1 +1 @@\n",
-    "\n"
+    "-\n"
     "+replacement\n",
 
   # patch a new file 'new.txt\n'
@@ -4553,7 +4553,7 @@ def patch_empty_file(sbox):
   # Current result: lf.txt patched ok, new created, empty succeeds with offset.
   expected_disk = svntest.main.greek_state.copy()
   expected_disk.add({
-    'lf.txt'            : Item(contents="\n"),
+    'lf.txt'            : Item(contents="replacement\n"),
     'new.txt'           : Item(contents="new file\n"),
     'empty.txt'         : Item(contents="replacement\n"),
   })
@@ -7582,6 +7582,100 @@ def missing_trailing_context(sbox):
                                        expected_output, expected_disk,
                                        expected_status, expected_skip)
 
+  # Try reverse patch
+  expected_disk.tweak('A/mu', contents =
+                     'a\n'
+                     'b\n'
+                     'c\n'
+                     'd\n'
+                     'e\n')
+  svntest.actions.run_and_verify_patch(wc_dir, patch_file_path,
+                                       expected_output, expected_disk,
+                                       expected_status, expected_skip,
+                                       [], False, True, '--reverse-diff')
+
+  # The hunk is expected to have two lines of trailing context but
+  # only has one.
+  unidiff_patch = [
+    "Index: A/mu\n"
+    "===================================================================\n",
+    "--- A/mu\t(revision 2)\n",
+    "+++ A/mu\t(working copy)\n",
+    "@@ -1,4 +1,4 @@\n",
+    " a\n",
+    " b\n",
+    "-c\n",
+    "+cc\n",
+    " d\n",
+    " e\n",
+  ]
+  patch_file_path = sbox.get_tempname('my2.patch')
+  svntest.main.file_write(patch_file_path, ''.join(unidiff_patch))
+
+  expected_output = [
+    'U         %s\n' % sbox.ospath('A/mu'),
+    '>         applied hunk @@ -1,5 +1,5 @@ with fuzz 1\n',
+  ]
+  expected_disk.tweak('A/mu', contents =
+                     'a\n'
+                     'b\n'
+                     'cc\n'
+                     'd\n'
+                     'e\n')
+  svntest.actions.run_and_verify_patch(wc_dir, patch_file_path,
+                                       expected_output, expected_disk,
+                                       expected_status, expected_skip)
+
+  # Try reverse patch
+  expected_disk.tweak('A/mu', contents =
+                     'a\n'
+                     'b\n'
+                     'c\n'
+                     'd\n'
+                     'e\n')
+  svntest.actions.run_and_verify_patch(wc_dir, patch_file_path,
+                                       expected_output, expected_disk,
+                                       expected_status, expected_skip,
+                                       [], False, True, '--reverse-diff')
+
+def patch_missed_trail(sbox):
+  "apply a patch to an empty file"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  patch_file_path = sbox.get_tempname('my.patch')
+  svntest.main.file_write(patch_file_path, ''.join([
+  # Add a line to a file with just '\n' with bad header (should be +1,2)
+    "Index: lf.txt\n",
+    "===================================================================\n",
+    "--- lf.txt\t(revision 2)\n",
+    "+++ lf.txt\t(working copy)\n",
+    "@@ -1 +1 @@\n",
+    "\n"
+    "+replacement\n",
+  ]))
+
+  sbox.simple_add_text('\n', 'lf.txt')
+  sbox.simple_commit()
+
+  expected_output = [
+    'U         %s\n' % sbox.ospath('lf.txt'),
+    '>         applied hunk @@ -1,1 +1,2 @@ with fuzz 1\n',
+  ]
+
+  # Current result: lf.txt patched ok, new created, empty succeeds with offset.
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.add({
+    'lf.txt'            : Item(contents="\nreplacement\n"),
+  })
+  expected_skip = wc.State(wc_dir, {})
+  expected_status = None
+
+  svntest.actions.run_and_verify_patch(wc_dir, patch_file_path,
+                                       expected_output, expected_disk,
+                                       expected_status, expected_skip)
+
 ########################################################################
 #Run the tests
 
@@ -7663,6 +7757,7 @@ test_list = [ None,
               patch_with_mergeinfo,
               patch_move_and_change,
               missing_trailing_context,
+              patch_missed_trail,
             ]
 
 if __name__ == '__main__':