comparison mercurial/patch.py @ 3967:dccb83241dd0

patch: use contexts for diff
author Benoit Boissinot <benoit.boissinot@ens-lyon.org>
date Mon, 25 Dec 2006 17:43:49 +0100
parents ba45041827a2
children fff8a5345eb0
comparison
equal deleted inserted replaced
3966:b4eaa68dea1b 3967:dccb83241dd0
5 # This software may be used and distributed according to the terms 5 # This software may be used and distributed according to the terms
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 i18n import _ 8 from i18n import _
9 from node import * 9 from node import *
10 import base85, cmdutil, mdiff, util 10 import base85, cmdutil, mdiff, util, context, revlog
11 import cStringIO, email.Parser, os, popen2, re, sha 11 import cStringIO, email.Parser, os, popen2, re, sha
12 import sys, tempfile, zlib 12 import sys, tempfile, zlib
13 13
14 # helper functions 14 # helper functions
15 15
434 fp = repo.ui 434 fp = repo.ui
435 435
436 if not node1: 436 if not node1:
437 node1 = repo.dirstate.parents()[0] 437 node1 = repo.dirstate.parents()[0]
438 438
439 clcache = {} 439 ccache = {}
440 def getchangelog(n): 440 def getctx(r):
441 if n not in clcache: 441 if r not in ccache:
442 clcache[n] = repo.changelog.read(n) 442 ccache[r] = context.changectx(repo, r)
443 return clcache[n] 443 return ccache[r]
444 mcache = {} 444
445 def getmanifest(n): 445 flcache = {}
446 if n not in mcache: 446 def getfilectx(f, ctx):
447 mcache[n] = repo.manifest.read(n) 447 flctx = ctx.filectx(f, filelog=flcache.get(f))
448 return mcache[n] 448 if f not in flcache:
449 fcache = {} 449 flcache[f] = flctx._filelog
450 def getfile(f): 450 return flctx
451 if f not in fcache:
452 fcache[f] = repo.file(f)
453 return fcache[f]
454 451
455 # reading the data for node1 early allows it to play nicely 452 # reading the data for node1 early allows it to play nicely
456 # with repo.status and the revlog cache. 453 # with repo.status and the revlog cache.
457 change = getchangelog(node1) 454 ctx1 = context.changectx(repo, node1)
458 mmap = getmanifest(change[0]) 455 # force manifest reading
459 date1 = util.datestr(change[2]) 456 man1 = ctx1.manifest()
457 date1 = util.datestr(ctx1.date())
460 458
461 if not changes: 459 if not changes:
462 changes = repo.status(node1, node2, files, match=match)[:5] 460 changes = repo.status(node1, node2, files, match=match)[:5]
463 modified, added, removed, deleted, unknown = changes 461 modified, added, removed, deleted, unknown = changes
464 if files: 462 if files:
474 modified, added, removed = map(filterfiles, (modified, added, removed)) 472 modified, added, removed = map(filterfiles, (modified, added, removed))
475 473
476 if not modified and not added and not removed: 474 if not modified and not added and not removed:
477 return 475 return
478 476
479 # returns False if there was no rename between n1 and n2 477 if node2:
480 # returns None if the file was created between n1 and n2 478 ctx2 = context.changectx(repo, node2)
481 # returns the (file, node) present in n1 that was renamed to f in n2 479 else:
482 def renamedbetween(f, n1, n2): 480 ctx2 = context.workingctx(repo)
483 r1, r2 = map(repo.changelog.rev, (n1, n2)) 481 man2 = ctx2.manifest()
482
483 # returns False if there was no rename between ctx1 and ctx2
484 # returns None if the file was created between ctx1 and ctx2
485 # returns the (file, node) present in ctx1 that was renamed to f in ctx2
486 def renamed(f):
487 startrev = ctx1.rev()
488 c = ctx2
489 crev = c.rev()
490 if crev is None:
491 crev = repo.changelog.count()
484 orig = f 492 orig = f
485 src = None 493 while crev > startrev:
486 while r2 > r1: 494 if f in c.files():
487 cl = getchangelog(n2)
488 if f in cl[3]:
489 m = getmanifest(cl[0])
490 try: 495 try:
491 src = getfile(f).renamed(m[f]) 496 src = getfilectx(f, c).renamed()
492 except KeyError: 497 except revlog.LookupError:
493 return None 498 return None
494 if src: 499 if src:
495 f = src[0] 500 f = src[0]
496 n2 = repo.changelog.parents(n2)[0] 501 crev = c.parents()[0].rev()
497 r2 = repo.changelog.rev(n2) 502 # try to reuse
498 cl = getchangelog(n1) 503 c = getctx(crev)
499 m = getmanifest(cl[0]) 504 if f not in man1:
500 if f not in m:
501 return None 505 return None
502 if f == orig: 506 if f == orig:
503 return False 507 return False
504 return f, m[f] 508 return f
505
506 if node2:
507 change = getchangelog(node2)
508 mmap2 = getmanifest(change[0])
509 _date2 = util.datestr(change[2])
510 def date2(f):
511 return _date2
512 def read(f):
513 return getfile(f).read(mmap2[f])
514 def renamed(f):
515 return renamedbetween(f, node1, node2)
516 else:
517 tz = util.makedate()[1]
518 _date2 = util.datestr()
519 def date2(f):
520 try:
521 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
522 except OSError, err:
523 if err.errno != errno.ENOENT: raise
524 return _date2
525 def read(f):
526 return repo.wread(f)
527 def renamed(f):
528 src = repo.dirstate.copied(f)
529 parent = repo.dirstate.parents()[0]
530 if src:
531 f = src
532 of = renamedbetween(f, node1, parent)
533 if of or of is None:
534 return of
535 elif src:
536 cl = getchangelog(parent)[0]
537 return (src, getmanifest(cl)[src])
538 else:
539 return None
540 509
541 if repo.ui.quiet: 510 if repo.ui.quiet:
542 r = None 511 r = None
543 else: 512 else:
544 hexfunc = repo.ui.debugflag and hex or short 513 hexfunc = repo.ui.debugflag and hex or short
548 copied = {} 517 copied = {}
549 for f in added: 518 for f in added:
550 src = renamed(f) 519 src = renamed(f)
551 if src: 520 if src:
552 copied[f] = src 521 copied[f] = src
553 srcs = [x[1][0] for x in copied.items()] 522 srcs = [x[1] for x in copied.items()]
554 523
555 all = modified + added + removed 524 all = modified + added + removed
556 all.sort() 525 all.sort()
557 gone = {} 526 gone = {}
558 for f in all: 527 for f in all:
559 to = None 528 to = None
560 tn = None 529 tn = None
561 dodiff = True 530 dodiff = True
562 header = [] 531 header = []
563 if f in mmap: 532 if f in man1:
564 to = getfile(f).read(mmap[f]) 533 to = getfilectx(f, ctx1).data()
565 if f not in removed: 534 if f not in removed:
566 tn = read(f) 535 tn = getfilectx(f, ctx2).data()
567 if opts.git: 536 if opts.git:
568 def gitmode(x): 537 def gitmode(x):
569 return x and '100755' or '100644' 538 return x and '100755' or '100644'
570 def addmodehdr(header, omode, nmode): 539 def addmodehdr(header, omode, nmode):
571 if omode != nmode: 540 if omode != nmode:
572 header.append('old mode %s\n' % omode) 541 header.append('old mode %s\n' % omode)
573 header.append('new mode %s\n' % nmode) 542 header.append('new mode %s\n' % nmode)
574 543
575 a, b = f, f 544 a, b = f, f
576 if f in added: 545 if f in added:
577 if node2: 546 mode = gitmode(man2.execf(f))
578 mode = gitmode(mmap2.execf(f))
579 else:
580 mode = gitmode(util.is_exec(repo.wjoin(f), None))
581 if f in copied: 547 if f in copied:
582 a, arev = copied[f] 548 a = copied[f]
583 omode = gitmode(mmap.execf(a)) 549 omode = gitmode(man1.execf(a))
584 addmodehdr(header, omode, mode) 550 addmodehdr(header, omode, mode)
585 if a in removed and a not in gone: 551 if a in removed and a not in gone:
586 op = 'rename' 552 op = 'rename'
587 gone[a] = 1 553 gone[a] = 1
588 else: 554 else:
589 op = 'copy' 555 op = 'copy'
590 header.append('%s from %s\n' % (op, a)) 556 header.append('%s from %s\n' % (op, a))
591 header.append('%s to %s\n' % (op, f)) 557 header.append('%s to %s\n' % (op, f))
592 to = getfile(a).read(arev) 558 to = getfilectx(a, ctx1).data()
593 else: 559 else:
594 header.append('new file mode %s\n' % mode) 560 header.append('new file mode %s\n' % mode)
595 if util.binary(tn): 561 if util.binary(tn):
596 dodiff = 'binary' 562 dodiff = 'binary'
597 elif f in removed: 563 elif f in removed:
598 if f in srcs: 564 if f in srcs:
599 dodiff = False 565 dodiff = False
600 else: 566 else:
601 mode = gitmode(mmap.execf(f)) 567 mode = gitmode(man1.execf(f))
602 header.append('deleted file mode %s\n' % mode) 568 header.append('deleted file mode %s\n' % mode)
603 else: 569 else:
604 omode = gitmode(mmap.execf(f)) 570 omode = gitmode(man1.execf(f))
605 if node2: 571 nmode = gitmode(man2.execf(f))
606 nmode = gitmode(mmap2.execf(f))
607 else:
608 nmode = gitmode(util.is_exec(repo.wjoin(f), mmap.execf(f)))
609 addmodehdr(header, omode, nmode) 572 addmodehdr(header, omode, nmode)
610 if util.binary(to) or util.binary(tn): 573 if util.binary(to) or util.binary(tn):
611 dodiff = 'binary' 574 dodiff = 'binary'
612 r = None 575 r = None
613 header.insert(0, 'diff --git a/%s b/%s\n' % (a, b)) 576 header.insert(0, 'diff --git a/%s b/%s\n' % (a, b))
614 if dodiff == 'binary': 577 if dodiff == 'binary':
615 fp.write(''.join(header)) 578 fp.write(''.join(header))
616 b85diff(fp, to, tn) 579 b85diff(fp, to, tn)
617 elif dodiff: 580 elif dodiff:
618 text = mdiff.unidiff(to, date1, tn, date2(f), f, r, opts=opts) 581 text = mdiff.unidiff(to, date1,
582 # ctx2 date may be dynamic
583 tn, util.datestr(ctx2.date()),
584 f, r, opts=opts)
619 if text or len(header) > 1: 585 if text or len(header) > 1:
620 fp.write(''.join(header)) 586 fp.write(''.join(header))
621 fp.write(text) 587 fp.write(text)
622 588
623 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False, 589 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,