mercurial/patch.py
changeset 41646 d4c9eebdd72d
parent 41624 3751595ec45e
child 41759 aaad36b88298
equal deleted inserted replaced
41645:2306158314e9 41646:d4c9eebdd72d
    13 import copy
    13 import copy
    14 import email
    14 import email
    15 import errno
    15 import errno
    16 import hashlib
    16 import hashlib
    17 import os
    17 import os
    18 import posixpath
       
    19 import re
    18 import re
    20 import shutil
    19 import shutil
    21 import zlib
    20 import zlib
    22 
    21 
    23 from .i18n import _
    22 from .i18n import _
  2237 diffopts = diffutil.diffallopts
  2236 diffopts = diffutil.diffallopts
  2238 diffallopts = diffutil.diffallopts
  2237 diffallopts = diffutil.diffallopts
  2239 difffeatureopts = diffutil.difffeatureopts
  2238 difffeatureopts = diffutil.difffeatureopts
  2240 
  2239 
  2241 def diff(repo, node1=None, node2=None, match=None, changes=None,
  2240 def diff(repo, node1=None, node2=None, match=None, changes=None,
  2242          opts=None, losedatafn=None, prefix='', relroot='', copy=None,
  2241          opts=None, losedatafn=None, pathfn=None, copy=None,
  2243          copysourcematch=None, hunksfilterfn=None):
  2242          copysourcematch=None, hunksfilterfn=None):
  2244     '''yields diff of changes to files between two nodes, or node and
  2243     '''yields diff of changes to files between two nodes, or node and
  2245     working directory.
  2244     working directory.
  2246 
  2245 
  2247     if node1 is None, use first dirstate parent instead.
  2246     if node1 is None, use first dirstate parent instead.
  2275 
  2274 
  2276     ctx1 = repo[node1]
  2275     ctx1 = repo[node1]
  2277     ctx2 = repo[node2]
  2276     ctx2 = repo[node2]
  2278 
  2277 
  2279     for fctx1, fctx2, hdr, hunks in diffhunks(
  2278     for fctx1, fctx2, hdr, hunks in diffhunks(
  2280             repo, ctx1=ctx1, ctx2=ctx2,
  2279             repo, ctx1=ctx1, ctx2=ctx2, match=match, changes=changes, opts=opts,
  2281             match=match, changes=changes, opts=opts,
  2280             losedatafn=losedatafn, pathfn=pathfn, copy=copy,
  2282             losedatafn=losedatafn, prefix=prefix, relroot=relroot, copy=copy,
       
  2283             copysourcematch=copysourcematch):
  2281             copysourcematch=copysourcematch):
  2284         if hunksfilterfn is not None:
  2282         if hunksfilterfn is not None:
  2285             # If the file has been removed, fctx2 is None; but this should
  2283             # If the file has been removed, fctx2 is None; but this should
  2286             # not occur here since we catch removed files early in
  2284             # not occur here since we catch removed files early in
  2287             # logcmdutil.getlinerangerevs() for 'hg log -L'.
  2285             # logcmdutil.getlinerangerevs() for 'hg log -L'.
  2292         if hdr and (text or len(hdr) > 1):
  2290         if hdr and (text or len(hdr) > 1):
  2293             yield '\n'.join(hdr) + '\n'
  2291             yield '\n'.join(hdr) + '\n'
  2294         if text:
  2292         if text:
  2295             yield text
  2293             yield text
  2296 
  2294 
  2297 def diffhunks(repo, ctx1, ctx2, match=None, changes=None,
  2295 def diffhunks(repo, ctx1, ctx2, match=None, changes=None, opts=None,
  2298               opts=None, losedatafn=None, prefix='', relroot='', copy=None,
  2296               losedatafn=None, pathfn=None, copy=None, copysourcematch=None):
  2299               copysourcematch=None):
       
  2300     """Yield diff of changes to files in the form of (`header`, `hunks`) tuples
  2297     """Yield diff of changes to files in the form of (`header`, `hunks`) tuples
  2301     where `header` is a list of diff headers and `hunks` is an iterable of
  2298     where `header` is a list of diff headers and `hunks` is an iterable of
  2302     (`hunkrange`, `hunklines`) tuples.
  2299     (`hunkrange`, `hunklines`) tuples.
  2303 
  2300 
  2304     See diff() for the meaning of parameters.
  2301     See diff() for the meaning of parameters.
  2374         repo, list(modifiedset | addedset | removedset))
  2371         repo, list(modifiedset | addedset | removedset))
  2375     scmutil.prefetchfiles(repo, [ctx1.rev(), ctx2.rev()], prefetchmatch)
  2372     scmutil.prefetchfiles(repo, [ctx1.rev(), ctx2.rev()], prefetchmatch)
  2376 
  2373 
  2377     def difffn(opts, losedata):
  2374     def difffn(opts, losedata):
  2378         return trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
  2375         return trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
  2379                        copy, getfilectx, opts, losedata, prefix, relroot)
  2376                        copy, getfilectx, opts, losedata, pathfn)
  2380     if opts.upgrade and not opts.git:
  2377     if opts.upgrade and not opts.git:
  2381         try:
  2378         try:
  2382             def losedata(fn):
  2379             def losedata(fn):
  2383                 if not losedatafn or not losedatafn(fn=fn):
  2380                 if not losedatafn or not losedatafn(fn=fn):
  2384                     raise GitDiffRequired
  2381                     raise GitDiffRequired
  2589                     and copy[copyto[f]] == f):
  2586                     and copy[copyto[f]] == f):
  2590                     continue
  2587                     continue
  2591         yield f1, f2, copyop
  2588         yield f1, f2, copyop
  2592 
  2589 
  2593 def trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
  2590 def trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
  2594             copy, getfilectx, opts, losedatafn, prefix, relroot):
  2591             copy, getfilectx, opts, losedatafn, pathfn):
  2595     '''given input data, generate a diff and yield it in blocks
  2592     '''given input data, generate a diff and yield it in blocks
  2596 
  2593 
  2597     If generating a diff would lose data like flags or binary data and
  2594     If generating a diff would lose data like flags or binary data and
  2598     losedatafn is not None, it will be called.
  2595     losedatafn is not None, it will be called.
  2599 
  2596 
  2600     relroot is removed and prefix is added to every path in the diff output.
  2597     pathfn is applied to every path in the diff output.
  2601 
  2598     '''
  2602     If relroot is not empty, this function expects every path in modified,
       
  2603     added, removed and copy to start with it.'''
       
  2604 
  2599 
  2605     def gitindex(text):
  2600     def gitindex(text):
  2606         if not text:
  2601         if not text:
  2607             text = ""
  2602             text = ""
  2608         l = len(text)
  2603         l = len(text)
  2626     date1 = dateutil.datestr(ctx1.date())
  2621     date1 = dateutil.datestr(ctx1.date())
  2627     date2 = dateutil.datestr(ctx2.date())
  2622     date2 = dateutil.datestr(ctx2.date())
  2628 
  2623 
  2629     gitmode = {'l': '120000', 'x': '100755', '': '100644'}
  2624     gitmode = {'l': '120000', 'x': '100755', '': '100644'}
  2630 
  2625 
  2631     if relroot != '' and (repo.ui.configbool('devel', 'all-warnings')
  2626     if not pathfn:
  2632                           or repo.ui.configbool('devel', 'check-relroot')):
  2627         pathfn = lambda f: f
  2633         for f in modified + added + removed + list(copy) + list(copy.values()):
       
  2634             if f is not None and not f.startswith(relroot):
       
  2635                 raise AssertionError(
       
  2636                     "file %s doesn't start with relroot %s" % (f, relroot))
       
  2637 
  2628 
  2638     for f1, f2, copyop in _filepairs(modified, added, removed, copy, opts):
  2629     for f1, f2, copyop in _filepairs(modified, added, removed, copy, opts):
  2639         content1 = None
  2630         content1 = None
  2640         content2 = None
  2631         content2 = None
  2641         fctx1 = None
  2632         fctx1 = None
  2668                 (not f1 and flag2) or
  2659                 (not f1 and flag2) or
  2669                 # change flags
  2660                 # change flags
  2670                 (f1 and f2 and flag1 != flag2)):
  2661                 (f1 and f2 and flag1 != flag2)):
  2671                 losedatafn(f2 or f1)
  2662                 losedatafn(f2 or f1)
  2672 
  2663 
  2673         path1 = f1 or f2
  2664         path1 = pathfn(f1 or f2)
  2674         path2 = f2 or f1
  2665         path2 = pathfn(f2 or f1)
  2675         path1 = posixpath.join(prefix, path1[len(relroot):])
       
  2676         path2 = posixpath.join(prefix, path2[len(relroot):])
       
  2677         header = []
  2666         header = []
  2678         if opts.git:
  2667         if opts.git:
  2679             header.append('diff --git %s%s %s%s' %
  2668             header.append('diff --git %s%s %s%s' %
  2680                           (aprefix, path1, bprefix, path2))
  2669                           (aprefix, path1, bprefix, path2))
  2681             if not f1: # added
  2670             if not f1: # added