6 # of the GNU General Public License, incorporated herein by reference. |
6 # of the GNU General Public License, incorporated herein by reference. |
7 |
7 |
8 from demandload import demandload |
8 from demandload import demandload |
9 from i18n import gettext as _ |
9 from i18n import gettext as _ |
10 from node import * |
10 from node import * |
11 demandload(globals(), "cmdutil mdiff util") |
11 demandload(globals(), "base85 cmdutil mdiff util") |
12 demandload(globals(), '''cStringIO email.Parser errno os re shutil sys tempfile |
12 demandload(globals(), "cStringIO email.Parser errno os re shutil sha sys") |
13 popen2''') |
13 demandload(globals(), "tempfile zlib") |
14 |
14 |
15 # helper functions |
15 # helper functions |
16 |
16 |
17 def copyfile(src, dst, basedir=None): |
17 def copyfile(src, dst, basedir=None): |
18 if not basedir: |
18 if not basedir: |
173 elif line.startswith('new file mode '): |
174 elif line.startswith('new file mode '): |
174 gp.op = 'ADD' |
175 gp.op = 'ADD' |
175 gp.mode = int(line.rstrip()[-3:], 8) |
176 gp.mode = int(line.rstrip()[-3:], 8) |
176 elif line.startswith('new mode '): |
177 elif line.startswith('new mode '): |
177 gp.mode = int(line.rstrip()[-3:], 8) |
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 if gp: |
183 if gp: |
179 gitpatches.append(gp) |
184 gitpatches.append(gp) |
180 |
185 |
181 if not gitpatches: |
186 if not gitpatches: |
182 dopatch = True |
187 dopatch = True |
183 |
188 |
184 return (dopatch, gitpatches) |
189 return (dopatch, gitpatches) |
185 |
190 |
186 def dogitpatch(patchname, gitpatches, cwd=None): |
191 def dogitpatch(patchname, gitpatches, cwd=None): |
187 """Preprocess git patch so that vanilla patch can handle it""" |
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 pf = file(patchname) |
212 pf = file(patchname) |
189 pfline = 1 |
213 pfline = 1 |
190 |
214 |
191 fd, patchname = tempfile.mkstemp(prefix='hg-patch-') |
215 fd, patchname = tempfile.mkstemp(prefix='hg-patch-') |
192 tmpfp = os.fdopen(fd, 'w') |
216 tmpfp = os.fdopen(fd, 'w') |
193 |
217 |
194 try: |
218 try: |
195 for i in range(len(gitpatches)): |
219 for i in range(len(gitpatches)): |
196 p = gitpatches[i] |
220 p = gitpatches[i] |
197 if not p.copymod: |
221 if not p.copymod and not p.binary: |
198 continue |
222 continue |
199 |
|
200 copyfile(p.oldpath, p.path, basedir=cwd) |
|
201 |
223 |
202 # rewrite patch hunk |
224 # rewrite patch hunk |
203 while pfline < p.lineno: |
225 while pfline < p.lineno: |
204 tmpfp.write(pf.readline()) |
226 tmpfp.write(pf.readline()) |
205 pfline += 1 |
227 pfline += 1 |
206 tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path)) |
228 |
207 line = pf.readline() |
229 if p.binary: |
208 pfline += 1 |
230 text = extractbin(pf) |
209 while not line.startswith('--- a/'): |
231 if not text: |
210 tmpfp.write(line) |
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) |
|
244 tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path)) |
211 line = pf.readline() |
245 line = pf.readline() |
212 pfline += 1 |
246 pfline += 1 |
213 tmpfp.write('--- a/%s\n' % p.path) |
247 while not line.startswith('--- a/'): |
|
248 tmpfp.write(line) |
|
249 line = pf.readline() |
|
250 pfline += 1 |
|
251 tmpfp.write('--- a/%s\n' % p.path) |
214 |
252 |
215 line = pf.readline() |
253 line = pf.readline() |
216 while line: |
254 while line: |
217 tmpfp.write(line) |
255 tmpfp.write(line) |
218 line = pf.readline() |
256 line = pf.readline() |
268 util.explain_exit(code)[0]) |
306 util.explain_exit(code)[0]) |
269 return files, fuzz |
307 return files, fuzz |
270 |
308 |
271 (dopatch, gitpatches) = readgitpatch(patchname) |
309 (dopatch, gitpatches) = readgitpatch(patchname) |
272 |
310 |
|
311 files, fuzz = {}, False |
273 if dopatch: |
312 if dopatch: |
274 if dopatch == 'filter': |
313 if dopatch in ('filter', 'binary'): |
275 patchname = dogitpatch(patchname, gitpatches, cwd=cwd) |
314 patchname = dogitpatch(patchname, gitpatches, cwd=cwd) |
276 try: |
315 try: |
277 files, fuzz = __patch(patchname) |
316 if dopatch != 'binary': |
|
317 files, fuzz = __patch(patchname) |
278 finally: |
318 finally: |
279 if dopatch == 'filter': |
319 if dopatch == 'filter': |
280 os.unlink(patchname) |
320 os.unlink(patchname) |
281 else: |
|
282 files, fuzz = {}, False |
|
283 |
321 |
284 for gp in gitpatches: |
322 for gp in gitpatches: |
285 files[gp.path] = (gp.op, gp) |
323 files[gp.path] = (gp.op, gp) |
286 |
324 |
287 return (files, fuzz) |
325 return (files, fuzz) |
338 files.extend([r for r in removes if r not in files]) |
376 files.extend([r for r in removes if r not in files]) |
339 files.sort() |
377 files.sort() |
340 |
378 |
341 return files |
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 def diff(repo, node1=None, node2=None, files=None, match=util.always, |
415 def diff(repo, node1=None, node2=None, files=None, match=util.always, |
344 fp=None, changes=None, opts=None): |
416 fp=None, changes=None, opts=None): |
345 '''print diff of changes to files between two nodes, or node and |
417 '''print diff of changes to files between two nodes, or node and |
346 working directory. |
418 working directory. |
347 |
419 |
494 header.append('%s from %s\n' % (op, a)) |
566 header.append('%s from %s\n' % (op, a)) |
495 header.append('%s to %s\n' % (op, f)) |
567 header.append('%s to %s\n' % (op, f)) |
496 to = getfile(a).read(arev) |
568 to = getfile(a).read(arev) |
497 else: |
569 else: |
498 header.append('new file mode %s\n' % mode) |
570 header.append('new file mode %s\n' % mode) |
|
571 if util.binary(tn): |
|
572 dodiff = 'binary' |
499 elif f in removed: |
573 elif f in removed: |
500 if f in srcs: |
574 if f in srcs: |
501 dodiff = False |
575 dodiff = False |
502 else: |
576 else: |
503 mode = gitmode(mmap.execf(f)) |
577 mode = gitmode(mmap.execf(f)) |
507 if node2: |
581 if node2: |
508 nmode = gitmode(mmap2.execf(f)) |
582 nmode = gitmode(mmap2.execf(f)) |
509 else: |
583 else: |
510 nmode = gitmode(util.is_exec(repo.wjoin(f), mmap.execf(f))) |
584 nmode = gitmode(util.is_exec(repo.wjoin(f), mmap.execf(f))) |
511 addmodehdr(header, omode, nmode) |
585 addmodehdr(header, omode, nmode) |
|
586 if util.binary(to) or util.binary(tn): |
|
587 dodiff = 'binary' |
512 r = None |
588 r = None |
513 header.insert(0, 'diff --git a/%s b/%s\n' % (a, b)) |
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 text = mdiff.unidiff(to, date1, tn, date2(f), f, r, opts=opts) |
594 text = mdiff.unidiff(to, date1, tn, date2(f), f, r, opts=opts) |
516 if text or len(header) > 1: |
595 if text or len(header) > 1: |
517 fp.write(''.join(header)) |
596 fp.write(''.join(header)) |
518 fp.write(text) |
597 fp.write(text) |
519 |
598 |