##// END OF EJS Templates
Add git-1.4 binary patch support
Brendan Cully -
r3367:7f486971 default
parent child Browse files
Show More
@@ -8,9 +8,9 b''
8 8 from demandload import demandload
9 9 from i18n import gettext as _
10 10 from node import *
11 demandload(globals(), "cmdutil mdiff util")
12 demandload(globals(), '''cStringIO email.Parser errno os re shutil sys tempfile
13 popen2''')
11 demandload(globals(), "base85 cmdutil mdiff util")
12 demandload(globals(), "cStringIO email.Parser errno os re shutil sha sys")
13 demandload(globals(), "tempfile zlib")
14 14
15 15 # helper functions
16 16
@@ -128,6 +128,7 b' def readgitpatch(patchname):'
128 128 self.op = 'MODIFY'
129 129 self.copymod = False
130 130 self.lineno = 0
131 self.binary = False
131 132
132 133 # Filter patch for git information
133 134 gitre = re.compile('diff --git a/(.*) b/(.*)')
@@ -175,6 +176,10 b' def readgitpatch(patchname):'
175 176 gp.mode = int(line.rstrip()[-3:], 8)
176 177 elif line.startswith('new mode '):
177 178 gp.mode = int(line.rstrip()[-3:], 8)
179 elif line.startswith('GIT binary patch'):
180 if not dopatch:
181 dopatch = 'binary'
182 gp.binary = True
178 183 if gp:
179 184 gitpatches.append(gp)
180 185
@@ -185,6 +190,25 b' def readgitpatch(patchname):'
185 190
186 191 def dogitpatch(patchname, gitpatches, cwd=None):
187 192 """Preprocess git patch so that vanilla patch can handle it"""
193 def extractbin(fp):
194 line = fp.readline()
195 while line and not line.startswith('literal '):
196 line = fp.readline()
197 if not line:
198 return
199 size = int(line[8:].rstrip())
200 dec = []
201 line = fp.readline()
202 while line:
203 line = line[1:-1]
204 dec.append(base85.b85decode(line))
205 line = fp.readline()
206 text = zlib.decompress(''.join(dec))
207 if len(text) != size:
208 raise util.Abort(_('binary patch is %d bytes, not %d') %
209 (len(text), size))
210 return text
211
188 212 pf = file(patchname)
189 213 pfline = 1
190 214
@@ -194,15 +218,29 b' def dogitpatch(patchname, gitpatches, cw'
194 218 try:
195 219 for i in range(len(gitpatches)):
196 220 p = gitpatches[i]
197 if not p.copymod:
221 if not p.copymod and not p.binary:
198 222 continue
199 223
200 copyfile(p.oldpath, p.path, basedir=cwd)
201
202 224 # rewrite patch hunk
203 225 while pfline < p.lineno:
204 226 tmpfp.write(pf.readline())
205 227 pfline += 1
228
229 if p.binary:
230 text = extractbin(pf)
231 if not text:
232 raise util.Abort(_('binary patch extraction failed'))
233 if not cwd:
234 cwd = os.getcwd()
235 absdst = os.path.join(cwd, p.path)
236 basedir = os.path.dirname(absdst)
237 if not os.path.isdir(basedir):
238 os.makedirs(basedir)
239 out = file(absdst, 'wb')
240 out.write(text)
241 out.close()
242 elif p.copymod:
243 copyfile(p.oldpath, p.path, basedir=cwd)
206 244 tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path))
207 245 line = pf.readline()
208 246 pfline += 1
@@ -270,16 +308,16 b' def patch(patchname, ui, strip=1, cwd=No'
270 308
271 309 (dopatch, gitpatches) = readgitpatch(patchname)
272 310
311 files, fuzz = {}, False
273 312 if dopatch:
274 if dopatch == 'filter':
313 if dopatch in ('filter', 'binary'):
275 314 patchname = dogitpatch(patchname, gitpatches, cwd=cwd)
276 315 try:
316 if dopatch != 'binary':
277 317 files, fuzz = __patch(patchname)
278 318 finally:
279 319 if dopatch == 'filter':
280 320 os.unlink(patchname)
281 else:
282 files, fuzz = {}, False
283 321
284 322 for gp in gitpatches:
285 323 files[gp.path] = (gp.op, gp)
@@ -340,6 +378,40 b' def updatedir(ui, repo, patches, wlock=N'
340 378
341 379 return files
342 380
381 def b85diff(fp, to, tn):
382 '''print base85-encoded binary diff'''
383 def gitindex(text):
384 if not text:
385 return '0' * 40
386 l = len(text)
387 s = sha.new('blob %d\0' % l)
388 s.update(text)
389 return s.hexdigest()
390
391 def fmtline(line):
392 l = len(line)
393 if l <= 26:
394 l = chr(ord('A') + l - 1)
395 else:
396 l = chr(l - 26 + ord('a') - 1)
397 return '%c%s\n' % (l, base85.b85encode(line, True))
398
399 def chunk(text, csize=52):
400 l = len(text)
401 i = 0
402 while i < l:
403 yield text[i:i+csize]
404 i += csize
405
406 # TODO: deltas
407 l = len(tn)
408 fp.write('index %s..%s\nGIT binary patch\nliteral %s\n' %
409 (gitindex(to), gitindex(tn), len(tn)))
410
411 tn = ''.join([fmtline(l) for l in chunk(zlib.compress(tn))])
412 fp.write(tn)
413 fp.write('\n')
414
343 415 def diff(repo, node1=None, node2=None, files=None, match=util.always,
344 416 fp=None, changes=None, opts=None):
345 417 '''print diff of changes to files between two nodes, or node and
@@ -496,6 +568,8 b' def diff(repo, node1=None, node2=None, f'
496 568 to = getfile(a).read(arev)
497 569 else:
498 570 header.append('new file mode %s\n' % mode)
571 if util.binary(tn):
572 dodiff = 'binary'
499 573 elif f in removed:
500 574 if f in srcs:
501 575 dodiff = False
@@ -509,9 +583,14 b' def diff(repo, node1=None, node2=None, f'
509 583 else:
510 584 nmode = gitmode(util.is_exec(repo.wjoin(f), mmap.execf(f)))
511 585 addmodehdr(header, omode, nmode)
586 if util.binary(to) or util.binary(tn):
587 dodiff = 'binary'
512 588 r = None
513 589 header.insert(0, 'diff --git a/%s b/%s\n' % (a, b))
514 if dodiff:
590 if dodiff == 'binary':
591 fp.write(''.join(header))
592 b85diff(fp, to, tn)
593 elif dodiff:
515 594 text = mdiff.unidiff(to, date1, tn, date2(f), f, r, opts=opts)
516 595 if text or len(header) > 1:
517 596 fp.write(''.join(header))
General Comments 0
You need to be logged in to leave comments. Login now