Patchwork patch: add support for git delta hunks

login
register
mail settings
Submitter 'Nicolas Vigier
Date Nov. 26, 2013, 1:02 a.m.
Message ID <5f9f242f7f8a78ef658a.1385427774@localhost>
Download mbox | patch
Permalink /patch/3141/
State Superseded
Commit 9f1d4323c749a8b902095f15587a8d0ec843e1d5
Headers show

Comments

'Nicolas Vigier - Nov. 26, 2013, 1:02 a.m.
# HG changeset patch
# User 'Nicolas Vigier <boklm@mars-attacks.org>'
# Date 1385424419 -3600
# Node ID 5f9f242f7f8a78ef658aa2e26d9857ca66ab09dc
# Parent  1c46b18b0e1c47fa4cecf21b78c083a54ae9903f
patch: add support for git delta hunks

When creating patches modifying binary files using "git format-patch",
git creates 'literal' and 'delta' hunks. Mercurial currently supports
'literal' hunks only, which makes it impossible to import patches with
'delta' hunks.

This changeset adds support for 'delta' hunks. It is a reimplementation
of patch-delta.c from git :
http://git.kernel.org/cgit/git/git.git/tree/patch-delta.c
Sean Farley - Nov. 26, 2013, 1:21 a.m.
boklm@mars-attacks.org writes:

> # HG changeset patch
> # User 'Nicolas Vigier <boklm@mars-attacks.org>'

Should we automatically warn the user when the entire value is
surrounded with the same type of quotes?
'Nicolas Vigier - Nov. 26, 2013, 11:15 a.m.
On Mon, 25 Nov 2013, Sean Farley wrote:

> 
> boklm@mars-attacks.org writes:
> 
> > # HG changeset patch
> > # User 'Nicolas Vigier <boklm@mars-attacks.org>'
> 
> Should we automatically warn the user when the entire value is
> surrounded with the same type of quotes?

That could be useful for new mercurial users. I didn't notice the
unneeded quotes until after sending this patch.

I will send it again with this corrected.

Patch

diff -r 1c46b18b0e1c -r 5f9f242f7f8a mercurial/patch.py
--- a/mercurial/patch.py	Fri Nov 22 17:26:58 2013 -0600
+++ b/mercurial/patch.py	Tue Nov 26 01:06:59 2013 +0100
@@ -721,8 +721,9 @@ 
             if self.remove:
                 self.backend.unlink(self.fname)
             else:
-                self.lines[:] = h.new()
-                self.offset += len(h.new())
+                l = h.new(self.lines)
+                self.lines[:] = l
+                self.offset += len(l)
                 self.dirty = True
             return 0
 
@@ -1016,9 +1017,10 @@ 
         return old, oldstart, new, newstart
 
 class binhunk(object):
-    'A binary patch file. Only understands literals so far.'
+    'A binary patch file.'
     def __init__(self, lr, fname):
         self.text = None
+        self.delta = False
         self.hunk = ['GIT binary patch\n']
         self._fname = fname
         self._read(lr)
@@ -1026,8 +1028,63 @@ 
     def complete(self):
         return self.text is not None
 
-    def new(self):
-        return [self.text]
+    def new(self, lines):
+        def deltahead(binchunk):
+            i = 0
+            for c in binchunk:
+                i += 1
+                if not (ord(c) & 0x80):
+                    return i
+            return i
+        def applydelta(binchunk, data):
+            out = ""
+            s = deltahead(binchunk)
+            binchunk = binchunk[s:]
+            s = deltahead(binchunk)
+            binchunk = binchunk[s:]
+            i = 0
+            while i < len(binchunk):
+                cmd = ord(binchunk[i])
+                i += 1
+                if (cmd & 0x80):
+                    offset = 0
+                    size = 0
+                    if (cmd & 0x01):
+                        offset = ord(binchunk[i])
+                        i += 1
+                    if (cmd & 0x02):
+                        offset |= ord(binchunk[i]) << 8
+                        i += 1
+                    if (cmd & 0x04):
+                        offset |= ord(binchunk[i]) << 16
+                        i += 1
+                    if (cmd & 0x08):
+                        offset |= ord(binchunk[i]) << 24
+                        i += 1
+                    if (cmd & 0x10):
+                        size = ord(binchunk[i])
+                        i += 1
+                    if (cmd & 0x20):
+                        size |= ord(binchunk[i]) << 8
+                        i += 1
+                    if (cmd & 0x40):
+                        size |= ord(binchunk[i]) << 16
+                        i += 1
+                    if size == 0:
+                        size = 0x10000
+                    offset_end = offset + size
+                    out += data[offset:offset_end]
+                elif cmd != 0:
+                    offset_end = i + cmd
+                    out += binchunk[i:offset_end]
+                    i += cmd
+                else:
+                    raise PatchError(_('unexpected delta opcode 0'))
+            return out
+        if self.delta:
+            return [applydelta(self.text, ''.join(lines))]
+        else:
+            return [self.text]
 
     def _read(self, lr):
         def getline(lr, hunk):
@@ -1035,14 +1092,19 @@ 
             hunk.append(l)
             return l.rstrip('\r\n')
 
+        size = 0
         while True:
             line = getline(lr, self.hunk)
             if not line:
                 raise PatchError(_('could not extract "%s" binary data')
                                  % self._fname)
             if line.startswith('literal '):
+                size = int(line[8:].rstrip())
                 break
-        size = int(line[8:].rstrip())
+            if line.startswith('delta '):
+                size = int(line[6:].rstrip())
+                self.delta = True
+                break
         dec = []
         line = getline(lr, self.hunk)
         while len(line) > 1: