[PATCH] patch: fuzz more aggressively to match patch(1) behaviour

Patrick Mezard patrick at mezard.eu
Mon Feb 13 10:28:06 CST 2012


# HG changeset patch
# User Patrick Mezard <patrick at mezard.eu>
# Date 1329150155 -3600
# Branch stable
# Node ID 4826fe61e876583aab4675e89bafa10b0c64f469
# Parent  3a4855a08072bd2ca2b3ed8a60fad0c21488f1f7
patch: fuzz more aggressively to match patch(1) behaviour

The previous code was assuming a default context of 3 lines. When fuzzing, it
would take this value in account to reduce the amount of removed line from
hunks top or bottom. For instance, if a hunk has only 2 lines of bottom
context, fuzzing with fuzz=1 would do nothing and with fuzz=2 it would remove
one of those lines. A hunk with one line of bottom context could not be fuzzed
at all.  patch(1) has apparently no such restrictions and takes the fuzz level
at face value.

- test-import.t: fuzz/offset changes at the beginning of file are explained by
  the new fuzzing behaviour and match patch(1) ones. Patching locations are
  different but those of my patch(1) do not make a lot of sense right now
  (patched output are the same)

- test-import-bypass.t: more agressive fuzzing makes a patching supposed to
  fail because of context, succeed. Change the diff to avoid this.

- test-mq-merge.t: more agressive fuzzing would allow the merged patch to apply
  with fuzz, but fortunately we disallow this behaviour. The new output is
  kept.

I have not enough experience with patch(1) fuzzing to know whether aligning our
implementation on it is a good or bad idea. Until now, it has been the
implementation reference. For instance, "qpush" tolerates fuzz (test-mq-merge.t
runs the special case of pushing merge revisions where fuzzing is forbidden).

diff --git a/mercurial/patch.py b/mercurial/patch.py
--- a/mercurial/patch.py
+++ b/mercurial/patch.py
@@ -967,7 +967,7 @@
         # this removes context lines from the top and bottom of list 'l'.  It
         # checks the hunk to make sure only context lines are removed, and then
         # returns a new shortened list of lines.
-        fuzz = min(fuzz, len(old)-1)
+        fuzz = min(fuzz, len(old))
         if fuzz:
             top = 0
             bot = 0
@@ -985,18 +985,8 @@
                     else:
                         break
 
-            # top and bot now count context in the hunk
-            # adjust them if either one is short
-            context = max(top, bot, 3)
-            if bot < context:
-                bot = max(0, fuzz - (context - bot))
-            else:
-                bot = min(fuzz, bot)
-            if top < context:
-                top = max(0, fuzz - (context - top))
-            else:
-                top = min(fuzz, top)
-
+            bot = min(fuzz, bot)
+            top = min(fuzz, top)
             return old[top:len(old)-bot], new[top:len(new)-bot], top
         return old, new, 0
 
diff --git a/tests/test-import-bypass.t b/tests/test-import-bypass.t
--- a/tests/test-import-bypass.t
+++ b/tests/test-import-bypass.t
@@ -111,7 +111,15 @@
 
 Test commit editor
 
-  $ hg diff -c 1 > ../test.diff
+  $ cat > ../test.diff <<EOF
+  > diff -r 07f494440405 -r 4e322f7ce8e3 a
+  > --- a/a	Thu Jan 01 00:00:00 1970 +0000
+  > +++ b/a	Thu Jan 01 00:00:00 1970 +0000
+  > @@ -1,1 +1,2 @@
+  > -a
+  > +b
+  > +c
+  > EOF
   $ HGEDITOR=cat hg import --bypass ../test.diff
   applying ../test.diff
   
@@ -138,7 +146,7 @@
   $ hg --config patch.eol=auto import -d '0 0' -m 'test patch.eol' --bypass ../test.diff
   applying ../test.diff
   $ shortlog
-  o  3:d7805b4d2cb3 test 0 0 - default - test patch.eol
+  o  3:c606edafba99 test 0 0 - default - test patch.eol
   |
   @  2:872023de769d test 0 0 - default - makeacrlf
   |
diff --git a/tests/test-import.t b/tests/test-import.t
--- a/tests/test-import.t
+++ b/tests/test-import.t
@@ -445,7 +445,7 @@
   $ hg import --no-commit -v fuzzy-tip.patch
   applying fuzzy-tip.patch
   patching file a
-  Hunk #1 succeeded at 1 with fuzz 2 (offset -2 lines).
+  Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines).
   applied to working directory
   $ hg revert -a
   reverting a
@@ -462,7 +462,7 @@
   $ hg --config patch.eol=auto import --no-commit -v fuzzy-tip.patch
   applying fuzzy-tip.patch
   patching file a
-  Hunk #1 succeeded at 1 with fuzz 2 (offset -2 lines).
+  Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines).
   applied to working directory
   $ cd ..
 
@@ -1031,6 +1031,19 @@
   > +line
   > EOF
 
+  $ cat > 04-middle-of-file-completely-fuzzed.diff <<EOF
+  > diff --git a/a b/a
+  > --- a/a
+  > +++ b/a
+  > @@ -1,1 +1,1 @@
+  > -2
+  > +add some skew
+  > @@ -2,2 +2,3 @@
+  >  not matching, should fuzz
+  >  ... a bit
+  > +line
+  > EOF
+
   $ cat > a <<EOF
   > 1
   > 2
@@ -1073,4 +1086,14 @@
   3
   4
   line
+  applying 04-middle-of-file-completely-fuzzed.diff
+  patching file a
+  Hunk #1 succeeded at 2 (offset 1 lines).
+  Hunk #2 succeeded at 5 with fuzz 2 (offset 1 lines).
+  applied to working directory
+  1
+  add some skew
+  3
+  4
+  line
 
diff --git a/tests/test-mq-merge.t b/tests/test-mq-merge.t
--- a/tests/test-mq-merge.t
+++ b/tests/test-mq-merge.t
@@ -125,12 +125,10 @@
   merging with queue at: $TESTTMP/t2/.hg/refqueue (glob)
   applying patcha
   patching file a
-  Hunk #1 FAILED at 0
-  1 out of 1 hunks FAILED -- saving rejects to file a.rej
-  patch failed, unable to continue (try -v)
-  patch failed, rejects left in working dir
+  Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines).
+  fuzz found when applying patch, stopping
   patch didn't work out, merging patcha
-  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
   0 files updated, 2 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
   applying patcha2


More information about the Mercurial-devel mailing list