mercurial/patch.py
changeset 45942 89a2afe31e82
parent 45877 ac362d5a7893
child 46030 2cf61e66c6d0
equal deleted inserted replaced
45941:346af7687c6f 45942:89a2afe31e82
   198 ]
   198 ]
   199 
   199 
   200 
   200 
   201 @contextlib.contextmanager
   201 @contextlib.contextmanager
   202 def extract(ui, fileobj):
   202 def extract(ui, fileobj):
   203     '''extract patch from data read from fileobj.
   203     """extract patch from data read from fileobj.
   204 
   204 
   205     patch can be a normal patch or contained in an email message.
   205     patch can be a normal patch or contained in an email message.
   206 
   206 
   207     return a dictionary. Standard keys are:
   207     return a dictionary. Standard keys are:
   208       - filename,
   208       - filename,
   212       - branch,
   212       - branch,
   213       - node,
   213       - node,
   214       - p1,
   214       - p1,
   215       - p2.
   215       - p2.
   216     Any item can be missing from the dictionary. If filename is missing,
   216     Any item can be missing from the dictionary. If filename is missing,
   217     fileobj did not contain a patch. Caller must unlink filename when done.'''
   217     fileobj did not contain a patch. Caller must unlink filename when done."""
   218 
   218 
   219     fd, tmpname = pycompat.mkstemp(prefix=b'hg-patch-')
   219     fd, tmpname = pycompat.mkstemp(prefix=b'hg-patch-')
   220     tmpfp = os.fdopen(fd, 'wb')
   220     tmpfp = os.fdopen(fd, 'wb')
   221     try:
   221     try:
   222         yield _extract(ui, fileobj, tmpname, tmpfp)
   222         yield _extract(ui, fileobj, tmpname, tmpfp)
   903         self.write_rej()
   903         self.write_rej()
   904         return len(self.rej)
   904         return len(self.rej)
   905 
   905 
   906 
   906 
   907 class header(object):
   907 class header(object):
   908     """patch header
   908     """patch header"""
   909     """
       
   910 
   909 
   911     diffgit_re = re.compile(b'diff --git a/(.*) b/(.*)$')
   910     diffgit_re = re.compile(b'diff --git a/(.*) b/(.*)$')
   912     diff_re = re.compile(b'diff -r .* (.*)$')
   911     diff_re = re.compile(b'diff -r .* (.*)$')
   913     allhunks_re = re.compile(b'(?:index|deleted file) ')
   912     allhunks_re = re.compile(b'(?:index|deleted file) ')
   914     pretty_re = re.compile(b'(?:new file|deleted file) ')
   913     pretty_re = re.compile(b'(?:new file|deleted file) ')
  1852     del fp
  1851     del fp
  1853     return p.finished()
  1852     return p.finished()
  1854 
  1853 
  1855 
  1854 
  1856 def pathtransform(path, strip, prefix):
  1855 def pathtransform(path, strip, prefix):
  1857     '''turn a path from a patch into a path suitable for the repository
  1856     """turn a path from a patch into a path suitable for the repository
  1858 
  1857 
  1859     prefix, if not empty, is expected to be normalized with a / at the end.
  1858     prefix, if not empty, is expected to be normalized with a / at the end.
  1860 
  1859 
  1861     Returns (stripped components, path in repository).
  1860     Returns (stripped components, path in repository).
  1862 
  1861 
  1871     >>> pathtransform(b'   a//b/c   ', 2, b'd/e/')
  1870     >>> pathtransform(b'   a//b/c   ', 2, b'd/e/')
  1872     ('a//b/', 'd/e/c')
  1871     ('a//b/', 'd/e/c')
  1873     >>> pathtransform(b'a/b/c', 3, b'')
  1872     >>> pathtransform(b'a/b/c', 3, b'')
  1874     Traceback (most recent call last):
  1873     Traceback (most recent call last):
  1875     PatchError: unable to strip away 1 of 3 dirs from a/b/c
  1874     PatchError: unable to strip away 1 of 3 dirs from a/b/c
  1876     '''
  1875     """
  1877     pathlen = len(path)
  1876     pathlen = len(path)
  1878     i = 0
  1877     i = 0
  1879     if strip == 0:
  1878     if strip == 0:
  1880         return b'', prefix + path.rstrip()
  1879         return b'', prefix + path.rstrip()
  1881     count = strip
  1880     count = strip
  2501     pathfn=None,
  2500     pathfn=None,
  2502     copy=None,
  2501     copy=None,
  2503     copysourcematch=None,
  2502     copysourcematch=None,
  2504     hunksfilterfn=None,
  2503     hunksfilterfn=None,
  2505 ):
  2504 ):
  2506     '''yields diff of changes to files between two nodes, or node and
  2505     """yields diff of changes to files between two nodes, or node and
  2507     working directory.
  2506     working directory.
  2508 
  2507 
  2509     if node1 is None, use first dirstate parent instead.
  2508     if node1 is None, use first dirstate parent instead.
  2510     if node2 is None, compare node1 with working directory.
  2509     if node2 is None, compare node1 with working directory.
  2511 
  2510 
  2529     if copysourcematch is not None, then copy sources will be filtered by this
  2528     if copysourcematch is not None, then copy sources will be filtered by this
  2530     matcher
  2529     matcher
  2531 
  2530 
  2532     hunksfilterfn, if not None, should be a function taking a filectx and
  2531     hunksfilterfn, if not None, should be a function taking a filectx and
  2533     hunks generator that may yield filtered hunks.
  2532     hunks generator that may yield filtered hunks.
  2534     '''
  2533     """
  2535     if not node1 and not node2:
  2534     if not node1 and not node2:
  2536         node1 = repo.dirstate.p1()
  2535         node1 = repo.dirstate.p1()
  2537 
  2536 
  2538     ctx1 = repo[node1]
  2537     ctx1 = repo[node1]
  2539     ctx2 = repo[node2]
  2538     ctx2 = repo[node2]
  2884     '''like diff(), but yields 2-tuples of (output, label) for ui.write()'''
  2883     '''like diff(), but yields 2-tuples of (output, label) for ui.write()'''
  2885     return difflabel(diff, *args, **kw)
  2884     return difflabel(diff, *args, **kw)
  2886 
  2885 
  2887 
  2886 
  2888 def _filepairs(modified, added, removed, copy, opts):
  2887 def _filepairs(modified, added, removed, copy, opts):
  2889     '''generates tuples (f1, f2, copyop), where f1 is the name of the file
  2888     """generates tuples (f1, f2, copyop), where f1 is the name of the file
  2890     before and f2 is the the name after. For added files, f1 will be None,
  2889     before and f2 is the the name after. For added files, f1 will be None,
  2891     and for removed files, f2 will be None. copyop may be set to None, 'copy'
  2890     and for removed files, f2 will be None. copyop may be set to None, 'copy'
  2892     or 'rename' (the latter two only if opts.git is set).'''
  2891     or 'rename' (the latter two only if opts.git is set)."""
  2893     gone = set()
  2892     gone = set()
  2894 
  2893 
  2895     copyto = {v: k for k, v in copy.items()}
  2894     copyto = {v: k for k, v in copy.items()}
  2896 
  2895 
  2897     addedset, removedset = set(added), set(removed)
  2896     addedset, removedset = set(added), set(removed)
  2946     getfilectx,
  2945     getfilectx,
  2947     opts,
  2946     opts,
  2948     losedatafn,
  2947     losedatafn,
  2949     pathfn,
  2948     pathfn,
  2950 ):
  2949 ):
  2951     '''given input data, generate a diff and yield it in blocks
  2950     """given input data, generate a diff and yield it in blocks
  2952 
  2951 
  2953     If generating a diff would lose data like flags or binary data and
  2952     If generating a diff would lose data like flags or binary data and
  2954     losedatafn is not None, it will be called.
  2953     losedatafn is not None, it will be called.
  2955 
  2954 
  2956     pathfn is applied to every path in the diff output.
  2955     pathfn is applied to every path in the diff output.
  2957     '''
  2956     """
  2958 
  2957 
  2959     if opts.noprefix:
  2958     if opts.noprefix:
  2960         aprefix = bprefix = b''
  2959         aprefix = bprefix = b''
  2961     else:
  2960     else:
  2962         aprefix = b'a/'
  2961         aprefix = b'a/'
  3077         data2 = (ctx2, fctx2, path2, flag2, content2, date2)
  3076         data2 = (ctx2, fctx2, path2, flag2, content2, date2)
  3078         yield diffcontent(data1, data2, header, binary, opts)
  3077         yield diffcontent(data1, data2, header, binary, opts)
  3079 
  3078 
  3080 
  3079 
  3081 def diffcontent(data1, data2, header, binary, opts):
  3080 def diffcontent(data1, data2, header, binary, opts):
  3082     """ diffs two versions of a file.
  3081     """diffs two versions of a file.
  3083 
  3082 
  3084     data1 and data2 are tuples containg:
  3083     data1 and data2 are tuples containg:
  3085 
  3084 
  3086         * ctx: changeset for the file
  3085         * ctx: changeset for the file
  3087         * fctx: file context for that file
  3086         * fctx: file context for that file
  3239 
  3238 
  3240     return b''.join(output)
  3239     return b''.join(output)
  3241 
  3240 
  3242 
  3241 
  3243 def diffstatui(*args, **kw):
  3242 def diffstatui(*args, **kw):
  3244     '''like diffstat(), but yields 2-tuples of (output, label) for
  3243     """like diffstat(), but yields 2-tuples of (output, label) for
  3245     ui.write()
  3244     ui.write()
  3246     '''
  3245     """
  3247 
  3246 
  3248     for line in diffstat(*args, **kw).splitlines():
  3247     for line in diffstat(*args, **kw).splitlines():
  3249         if line and line[-1] in b'+-':
  3248         if line and line[-1] in b'+-':
  3250             name, graph = line.rsplit(b' ', 1)
  3249             name, graph = line.rsplit(b' ', 1)
  3251             yield (name + b' ', b'')
  3250             yield (name + b' ', b'')