mercurial/patch.py
changeset 3967 dccb83241dd0
parent 3963 ba45041827a2
child 3970 fff8a5345eb0
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,