diff -r dca067d751a9 -r 7f486971d263 mercurial/patch.py --- a/mercurial/patch.py Thu Oct 12 17:48:09 2006 +0200 +++ b/mercurial/patch.py Thu Oct 12 09:17:16 2006 -0700 @@ -8,9 +8,9 @@ from demandload import demandload from i18n import gettext as _ from node import * -demandload(globals(), "cmdutil mdiff util") -demandload(globals(), '''cStringIO email.Parser errno os re shutil sys tempfile - popen2''') +demandload(globals(), "base85 cmdutil mdiff util") +demandload(globals(), "cStringIO email.Parser errno os re shutil sha sys") +demandload(globals(), "tempfile zlib") # helper functions @@ -128,6 +128,7 @@ self.op = 'MODIFY' self.copymod = False self.lineno = 0 + self.binary = False # Filter patch for git information gitre = re.compile('diff --git a/(.*) b/(.*)') @@ -175,6 +176,10 @@ gp.mode = int(line.rstrip()[-3:], 8) elif line.startswith('new mode '): gp.mode = int(line.rstrip()[-3:], 8) + elif line.startswith('GIT binary patch'): + if not dopatch: + dopatch = 'binary' + gp.binary = True if gp: gitpatches.append(gp) @@ -185,6 +190,25 @@ def dogitpatch(patchname, gitpatches, cwd=None): """Preprocess git patch so that vanilla patch can handle it""" + def extractbin(fp): + line = fp.readline() + while line and not line.startswith('literal '): + line = fp.readline() + if not line: + return + size = int(line[8:].rstrip()) + dec = [] + line = fp.readline() + while line: + line = line[1:-1] + dec.append(base85.b85decode(line)) + line = fp.readline() + text = zlib.decompress(''.join(dec)) + if len(text) != size: + raise util.Abort(_('binary patch is %d bytes, not %d') % + (len(text), size)) + return text + pf = file(patchname) pfline = 1 @@ -194,23 +218,37 @@ try: for i in range(len(gitpatches)): p = gitpatches[i] - if not p.copymod: + if not p.copymod and not p.binary: continue - copyfile(p.oldpath, p.path, basedir=cwd) - # rewrite patch hunk while pfline < p.lineno: tmpfp.write(pf.readline()) pfline += 1 - tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path)) - line = pf.readline() - pfline += 1 - while not line.startswith('--- a/'): - tmpfp.write(line) + + if p.binary: + text = extractbin(pf) + if not text: + raise util.Abort(_('binary patch extraction failed')) + if not cwd: + cwd = os.getcwd() + absdst = os.path.join(cwd, p.path) + basedir = os.path.dirname(absdst) + if not os.path.isdir(basedir): + os.makedirs(basedir) + out = file(absdst, 'wb') + out.write(text) + out.close() + elif p.copymod: + copyfile(p.oldpath, p.path, basedir=cwd) + tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path)) line = pf.readline() pfline += 1 - tmpfp.write('--- a/%s\n' % p.path) + while not line.startswith('--- a/'): + tmpfp.write(line) + line = pf.readline() + pfline += 1 + tmpfp.write('--- a/%s\n' % p.path) line = pf.readline() while line: @@ -270,16 +308,16 @@ (dopatch, gitpatches) = readgitpatch(patchname) + files, fuzz = {}, False if dopatch: - if dopatch == 'filter': + if dopatch in ('filter', 'binary'): patchname = dogitpatch(patchname, gitpatches, cwd=cwd) try: - files, fuzz = __patch(patchname) + if dopatch != 'binary': + files, fuzz = __patch(patchname) finally: if dopatch == 'filter': os.unlink(patchname) - else: - files, fuzz = {}, False for gp in gitpatches: files[gp.path] = (gp.op, gp) @@ -340,6 +378,40 @@ return files +def b85diff(fp, to, tn): + '''print base85-encoded binary diff''' + def gitindex(text): + if not text: + return '0' * 40 + l = len(text) + s = sha.new('blob %d\0' % l) + s.update(text) + return s.hexdigest() + + def fmtline(line): + l = len(line) + if l <= 26: + l = chr(ord('A') + l - 1) + else: + l = chr(l - 26 + ord('a') - 1) + return '%c%s\n' % (l, base85.b85encode(line, True)) + + def chunk(text, csize=52): + l = len(text) + i = 0 + while i < l: + yield text[i:i+csize] + i += csize + + # TODO: deltas + l = len(tn) + fp.write('index %s..%s\nGIT binary patch\nliteral %s\n' % + (gitindex(to), gitindex(tn), len(tn))) + + tn = ''.join([fmtline(l) for l in chunk(zlib.compress(tn))]) + fp.write(tn) + fp.write('\n') + def diff(repo, node1=None, node2=None, files=None, match=util.always, fp=None, changes=None, opts=None): '''print diff of changes to files between two nodes, or node and @@ -496,6 +568,8 @@ to = getfile(a).read(arev) else: header.append('new file mode %s\n' % mode) + if util.binary(tn): + dodiff = 'binary' elif f in removed: if f in srcs: dodiff = False @@ -509,9 +583,14 @@ else: nmode = gitmode(util.is_exec(repo.wjoin(f), mmap.execf(f))) addmodehdr(header, omode, nmode) + if util.binary(to) or util.binary(tn): + dodiff = 'binary' r = None header.insert(0, 'diff --git a/%s b/%s\n' % (a, b)) - if dodiff: + if dodiff == 'binary': + fp.write(''.join(header)) + b85diff(fp, to, tn) + elif dodiff: text = mdiff.unidiff(to, date1, tn, date2(f), f, r, opts=opts) if text or len(header) > 1: fp.write(''.join(header))