diff --git a/mercurial/patch.py b/mercurial/patch.py --- a/mercurial/patch.py +++ b/mercurial/patch.py @@ -721,8 +721,9 @@ class patchfile(object): 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 @@ class hunk(object): 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,7 +1028,9 @@ class binhunk(object): def complete(self): return self.text is not None - def new(self): + def new(self, lines): + if self.delta: + return [applybindelta(self.text, ''.join(lines))] return [self.text] def _read(self, lr): @@ -1035,14 +1039,19 @@ class binhunk(object): 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: @@ -1265,6 +1274,62 @@ def iterhunks(fp): gp = gitpatches.pop() yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy()) +def applybindelta(binchunk, data): + """Apply a binary delta hunk + The algorithm used is the algorithm from git's patch-delta.c + """ + def deltahead(binchunk): + i = 0 + for c in binchunk: + i += 1 + if not (ord(c) & 0x80): + return i + return i + 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 + def applydiff(ui, fp, backend, store, strip=1, eolmode='strict'): """Reads a patch from fp and tries to apply it. diff --git a/tests/test-import-git.t b/tests/test-import-git.t --- a/tests/test-import-git.t +++ b/tests/test-import-git.t @@ -320,6 +320,115 @@ Multiple binary files: 045c85ba38952325e126c70962cc0f9d9077bc67 644 mbinary1 a874b471193996e7cb034bb301cac7bdaf3e3f46 644 mbinary2 +Binary file and delta hunk: + + $ hg import -d "1000000 0" -m delta - <<'EOF' + > diff --git a/delta b/delta + > new file mode 100644 + > index 0000000000000000000000000000000000000000..8c9b7831b231c2600843e303e66b521353a200b3 + > GIT binary patch + > literal 3749 + > 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 + > P00000NkvXXu0mjfZ5|Er + > + > literal 0 + > HcmV?d00001 + > + > EOF + applying patch from stdin + + $ hg manifest --debug | grep delta + 9600f98bb60ce732634d126aaa4ac1ec959c573e 644 delta + + $ hg import -d "1000000 0" -m delta - <<'EOF' + > diff --git a/delta b/delta + > index 8c9b7831b231c2600843e303e66b521353a200b3..0021dd95bc0dba53c39ce81377126d43731d68df 100644 + > GIT binary patch + > delta 49 + > zcmZ1~yHs|=21Z8J$r~9bFdA-lVv=EEw4WT$qRf2QSa5SIOAHI6(&k4T8H|kLo4vWB + > FSO9ZT4bA`n + > + > delta 49 + > zcmV-10M7rV9i<(xumJ(}ld%Di0Xefm0vrMXpOaq%BLm9I%d>?9Tm%6Vv*HM70RcC& + > HOA1;9yU-AD + > + > EOF + applying patch from stdin + + $ hg manifest --debug | grep delta + 56094bbea136dcf8dbd4088f6af469bde1a98b75 644 delta + Filenames with spaces: $ sed 's,EOL$,,g' < diff --git a/text2 b/binary2