Mercurial > public > mercurial-scm > hg-stable
diff mercurial/patch.py @ 43076:2372284d9457
formatting: blacken the codebase
This is using my patch to black
(https://github.com/psf/black/pull/826) so we don't un-wrap collection
literals.
Done with:
hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S
# skip-blame mass-reformatting only
# no-check-commit reformats foo_bar functions
Differential Revision: https://phab.mercurial-scm.org/D6971
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:45:02 -0400 |
parents | db33e4f25729 |
children | 687b865b95ad |
line wrap: on
line diff
--- a/mercurial/patch.py Sat Oct 05 10:29:34 2019 -0400 +++ b/mercurial/patch.py Sun Oct 06 09:45:02 2019 -0400 @@ -49,15 +49,18 @@ gitre = re.compile(br'diff --git a/(.*) b/(.*)') tabsplitter = re.compile(br'(\t+|[^\t]+)') -wordsplitter = re.compile(br'(\t+| +|[a-zA-Z0-9_\x80-\xff]+|' - b'[^ \ta-zA-Z0-9_\x80-\xff])') +wordsplitter = re.compile( + br'(\t+| +|[a-zA-Z0-9_\x80-\xff]+|' b'[^ \ta-zA-Z0-9_\x80-\xff])' +) PatchError = error.PatchError # public functions + def split(stream): '''return an iterator of individual patches from a stream''' + def isheader(line, inheader): if inheader and line.startswith((' ', '\t')): # continuation @@ -185,12 +188,15 @@ # if we are here, we have a very plain patch return remainder(cur) + ## Some facility for extensible patch parsing: # list of pairs ("header to match", "data key") -patchheadermap = [('Date', 'date'), - ('Branch', 'branch'), - ('Node ID', 'nodeid'), - ] +patchheadermap = [ + ('Date', 'date'), + ('Branch', 'branch'), + ('Node ID', 'nodeid'), +] + @contextlib.contextmanager def extract(ui, fileobj): @@ -218,15 +224,18 @@ tmpfp.close() os.unlink(tmpname) + def _extract(ui, fileobj, tmpname, tmpfp): # attempt to detect the start of a patch # (this heuristic is borrowed from quilt) - diffre = re.compile(br'^(?:Index:[ \t]|diff[ \t]-|RCS file: |' - br'retrieving revision [0-9]+(\.[0-9]+)*$|' - br'---[ \t].*?^\+\+\+[ \t]|' - br'\*\*\*[ \t].*?^---[ \t])', - re.MULTILINE | re.DOTALL) + diffre = re.compile( + br'^(?:Index:[ \t]|diff[ \t]-|RCS file: |' + br'retrieving revision [0-9]+(\.[0-9]+)*$|' + br'---[ \t].*?^\+\+\+[ \t]|' + br'\*\*\*[ \t].*?^---[ \t])', + re.MULTILINE | re.DOTALL, + ) data = {} @@ -236,8 +245,12 @@ data['user'] = msg[r'From'] and mail.headdecode(msg[r'From']) if not subject and not data['user']: # Not an email, restore parsed headers if any - subject = '\n'.join(': '.join(map(encoding.strtolocal, h)) - for h in msg.items()) + '\n' + subject = ( + '\n'.join( + ': '.join(map(encoding.strtolocal, h)) for h in msg.items() + ) + + '\n' + ) # should try to parse msg['Date'] parents = [] @@ -246,7 +259,7 @@ if subject.startswith('[PATCH'): pend = subject.find(']') if pend >= 0: - subject = subject[pend + 1:].lstrip() + subject = subject[pend + 1 :].lstrip() subject = re.sub(br'\n[ \t]+', ' ', subject) ui.debug('Subject: %s\n' % subject) if data['user']: @@ -269,7 +282,7 @@ ui.debug('found patch at byte %d\n' % m.start(0)) diffs_seen += 1 cfp = stringio() - for line in payload[:m.start(0)].splitlines(): + for line in payload[: m.start(0)].splitlines(): if line.startswith('# HG changeset patch') and not hgpatch: ui.debug('patch generated by hg export\n') hgpatch = True @@ -288,7 +301,7 @@ for header, key in patchheadermap: prefix = '# %s ' % header if line.startswith(prefix): - data[key] = line[len(prefix):] + data[key] = line[len(prefix) :] ui.debug('%s: %s\n' % (header, data[key])) else: hgpatchheader = False @@ -319,6 +332,7 @@ return data + class patchmeta(object): """Patched file metadata @@ -329,6 +343,7 @@ 'islink' is True if the file is a symlink and 'isexec' is True if the file is executable. Otherwise, 'mode' is None. """ + def __init__(self, path): self.path = path self.oldpath = None @@ -365,6 +380,7 @@ def __repr__(self): return r"<patchmeta %s %r>" % (self.op, self.path) + def readgitpatch(lr): """extract git-style metadata about patches from <patchname>""" @@ -409,6 +425,7 @@ return gitpatches + class linereader(object): # simple class to allow pushing lines back into the input stream def __init__(self, fp): @@ -429,6 +446,7 @@ def __iter__(self): return iter(self.readline, '') + class abstractbackend(object): def __init__(self, ui): self.ui = ui @@ -463,6 +481,7 @@ def close(self): raise NotImplementedError + class fsbackend(abstractbackend): def __init__(self, ui, basedir): super(fsbackend, self).__init__(ui) @@ -504,8 +523,9 @@ def writerej(self, fname, failed, total, lines): fname = fname + ".rej" self.ui.warn( - _("%d out of %d hunks FAILED -- saving rejects to file %s\n") % - (failed, total, fname)) + _("%d out of %d hunks FAILED -- saving rejects to file %s\n") + % (failed, total, fname) + ) fp = self.opener(fname, 'w') fp.writelines(lines) fp.close() @@ -513,6 +533,7 @@ def exists(self, fname): return self.opener.lexists(fname) + class workingbackend(fsbackend): def __init__(self, ui, repo, similarity): super(workingbackend, self).__init__(ui, repo.root) @@ -557,6 +578,7 @@ scmutil.marktouched(self.repo, changed, self.similarity) return sorted(self.changed) + class filestore(object): def __init__(self, maxsize=None): self.opener = None @@ -564,7 +586,7 @@ self.created = 0 self.maxsize = maxsize if self.maxsize is None: - self.maxsize = 4*(2**20) + self.maxsize = 4 * (2 ** 20) self.size = 0 self.data = {} @@ -594,6 +616,7 @@ if self.opener: shutil.rmtree(self.opener.base) + class repobackend(abstractbackend): def __init__(self, ui, repo, ctx, store): super(repobackend, self).__init__(ui) @@ -636,11 +659,13 @@ def close(self): return self.changed | self.removed + # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1 unidesc = re.compile(br'@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@') contextdesc = re.compile(br'(?:---|\*\*\*) (\d+)(?:,(\d+))? (?:---|\*\*\*)') eolmodes = ['strict', 'crlf', 'lf', 'auto'] + class patchfile(object): def __init__(self, ui, gp, backend, store, eolmode='strict'): self.fname = gp.path @@ -686,8 +711,12 @@ self.mode = (False, False) if self.missing: self.ui.warn(_("unable to find '%s' for patching\n") % self.fname) - self.ui.warn(_("(use '--prefix' to apply patch relative to the " - "current directory)\n")) + self.ui.warn( + _( + "(use '--prefix' to apply patch relative to the " + "current directory)\n" + ) + ) self.hash = {} self.dirty = 0 @@ -727,7 +756,6 @@ else: self.ui.note(s) - def findlines(self, l, linenum): # looks through the hash and finds candidate lines. The # result is a list of line numbers sorted based on distance @@ -757,9 +785,10 @@ def apply(self, h): if not h.complete(): - raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") % - (h.number, h.desc, len(h.a), h.lena, len(h.b), - h.lenb)) + raise PatchError( + _("bad hunk #%d %s (%d %d %d %d)") + % (h.number, h.desc, len(h.a), h.lena, len(h.b), h.lenb) + ) self.hunks += 1 @@ -769,8 +798,10 @@ if self.exists and self.create: if self.copysource: - self.ui.warn(_("cannot create %s: destination already " - "exists\n") % self.fname) + self.ui.warn( + _("cannot create %s: destination already " "exists\n") + % self.fname + ) else: self.ui.warn(_("file %s already exists\n") % self.fname) self.rej.append(h) @@ -787,8 +818,11 @@ return 0 horig = h - if (self.eolmode in ('crlf', 'lf') - or self.eolmode == 'auto' and self.eol): + if ( + self.eolmode in ('crlf', 'lf') + or self.eolmode == 'auto' + and self.eol + ): # If new eols are going to be normalized, then normalize # hunk data before patching. Otherwise, preserve input # line-endings. @@ -805,7 +839,7 @@ if self.remove: self.backend.unlink(self.fname) else: - self.lines[oldstart:oldstart + len(old)] = new + self.lines[oldstart : oldstart + len(old)] = new self.offset += len(new) - len(old) self.dirty = True return 0 @@ -835,15 +869,20 @@ self.dirty = True offset = l - orig_start - fuzzlen if fuzzlen: - msg = _("Hunk #%d succeeded at %d " - "with fuzz %d " - "(offset %d lines).\n") + msg = _( + "Hunk #%d succeeded at %d " + "with fuzz %d " + "(offset %d lines).\n" + ) self.printfile(True) - self.ui.warn(msg % - (h.number, l + 1, fuzzlen, offset)) + self.ui.warn( + msg % (h.number, l + 1, fuzzlen, offset) + ) else: - msg = _("Hunk #%d succeeded at %d " - "(offset %d lines).\n") + msg = _( + "Hunk #%d succeeded at %d " + "(offset %d lines).\n" + ) self.ui.note(msg % (h.number, l + 1, offset)) return fuzzlen self.printfile(True) @@ -857,9 +896,11 @@ self.write_rej() return len(self.rej) + class header(object): """patch header """ + diffgit_re = re.compile('diff --git a/(.*) b/(.*)$') diff_re = re.compile('diff -r .* (.*)$') allhunks_re = re.compile('(?:index|deleted file) ') @@ -885,9 +926,13 @@ fp.write(_('this is a binary file\n')) break if h.startswith('---'): - fp.write(_('%d hunks, %d lines changed\n') % - (len(self.hunks), - sum([max(h.added, h.removed) for h in self.hunks]))) + fp.write( + _('%d hunks, %d lines changed\n') + % ( + len(self.hunks), + sum([max(h.added, h.removed) for h in self.hunks]), + ) + ) break fp.write(h) @@ -926,8 +971,10 @@ # if they have some content as we want to be able to change it nocontent = len(self.header) == 2 emptynewfile = self.isnewfile() and nocontent - return (emptynewfile - or any(self.special_re.match(h) for h in self.header)) + return emptynewfile or any( + self.special_re.match(h) for h in self.header + ) + class recordhunk(object): """patch hunk @@ -935,8 +982,17 @@ XXX shouldn't we merge this with the other hunk class? """ - def __init__(self, header, fromline, toline, proc, before, hunk, after, - maxcontext=None): + def __init__( + self, + header, + fromline, + toline, + proc, + before, + hunk, + after, + maxcontext=None, + ): def trimcontext(lines, reverse=False): if maxcontext is not None: delta = len(lines) - maxcontext @@ -960,16 +1016,22 @@ if not isinstance(v, recordhunk): return False - return ((v.hunk == self.hunk) and - (v.proc == self.proc) and - (self.fromline == v.fromline) and - (self.header.files() == v.header.files())) + return ( + (v.hunk == self.hunk) + and (v.proc == self.proc) + and (self.fromline == v.fromline) + and (self.header.files() == v.header.files()) + ) def __hash__(self): - return hash((tuple(self.hunk), - tuple(self.header.files()), - self.fromline, - self.proc)) + return hash( + ( + tuple(self.hunk), + tuple(self.header.files()), + self.fromline, + self.proc, + ) + ) def countchanges(self, hunk): """hunk -> (n+,n-)""" @@ -986,8 +1048,15 @@ """ m = {'+': '-', '-': '+', '\\': '\\'} hunk = ['%s%s' % (m[l[0:1]], l[1:]) for l in self.hunk] - return recordhunk(self.header, self.toline, self.fromline, self.proc, - self.before, hunk, self.after) + return recordhunk( + self.header, + self.toline, + self.fromline, + self.proc, + self.before, + hunk, + self.after, + ) def write(self, fp): delta = len(self.before) + len(self.after) @@ -995,9 +1064,16 @@ delta -= 1 fromlen = delta + self.removed tolen = delta + self.added - fp.write('@@ -%d,%d +%d,%d @@%s\n' % - (self.fromline, fromlen, self.toline, tolen, - self.proc and (' ' + self.proc))) + fp.write( + '@@ -%d,%d +%d,%d @@%s\n' + % ( + self.fromline, + fromlen, + self.toline, + tolen, + self.proc and (' ' + self.proc), + ) + ) fp.write(''.join(self.before + self.hunk + self.after)) pretty = write @@ -1008,6 +1084,7 @@ def __repr__(self): return '<hunk %r@%d>' % (self.filename(), self.fromline) + def getmessages(): return { 'multiple': { @@ -1023,49 +1100,58 @@ 'record': _("record this change to '%s'?"), }, 'help': { - 'apply': _('[Ynesfdaq?]' - '$$ &Yes, apply this change' - '$$ &No, skip this change' - '$$ &Edit this change manually' - '$$ &Skip remaining changes to this file' - '$$ Apply remaining changes to this &file' - '$$ &Done, skip remaining changes and files' - '$$ Apply &all changes to all remaining files' - '$$ &Quit, applying no changes' - '$$ &? (display help)'), - 'discard': _('[Ynesfdaq?]' - '$$ &Yes, discard this change' - '$$ &No, skip this change' - '$$ &Edit this change manually' - '$$ &Skip remaining changes to this file' - '$$ Discard remaining changes to this &file' - '$$ &Done, skip remaining changes and files' - '$$ Discard &all changes to all remaining files' - '$$ &Quit, discarding no changes' - '$$ &? (display help)'), - 'keep': _('[Ynesfdaq?]' - '$$ &Yes, keep this change' - '$$ &No, skip this change' - '$$ &Edit this change manually' - '$$ &Skip remaining changes to this file' - '$$ Keep remaining changes to this &file' - '$$ &Done, skip remaining changes and files' - '$$ Keep &all changes to all remaining files' - '$$ &Quit, keeping all changes' - '$$ &? (display help)'), - 'record': _('[Ynesfdaq?]' - '$$ &Yes, record this change' - '$$ &No, skip this change' - '$$ &Edit this change manually' - '$$ &Skip remaining changes to this file' - '$$ Record remaining changes to this &file' - '$$ &Done, skip remaining changes and files' - '$$ Record &all changes to all remaining files' - '$$ &Quit, recording no changes' - '$$ &? (display help)'), - } + 'apply': _( + '[Ynesfdaq?]' + '$$ &Yes, apply this change' + '$$ &No, skip this change' + '$$ &Edit this change manually' + '$$ &Skip remaining changes to this file' + '$$ Apply remaining changes to this &file' + '$$ &Done, skip remaining changes and files' + '$$ Apply &all changes to all remaining files' + '$$ &Quit, applying no changes' + '$$ &? (display help)' + ), + 'discard': _( + '[Ynesfdaq?]' + '$$ &Yes, discard this change' + '$$ &No, skip this change' + '$$ &Edit this change manually' + '$$ &Skip remaining changes to this file' + '$$ Discard remaining changes to this &file' + '$$ &Done, skip remaining changes and files' + '$$ Discard &all changes to all remaining files' + '$$ &Quit, discarding no changes' + '$$ &? (display help)' + ), + 'keep': _( + '[Ynesfdaq?]' + '$$ &Yes, keep this change' + '$$ &No, skip this change' + '$$ &Edit this change manually' + '$$ &Skip remaining changes to this file' + '$$ Keep remaining changes to this &file' + '$$ &Done, skip remaining changes and files' + '$$ Keep &all changes to all remaining files' + '$$ &Quit, keeping all changes' + '$$ &? (display help)' + ), + 'record': _( + '[Ynesfdaq?]' + '$$ &Yes, record this change' + '$$ &No, skip this change' + '$$ &Edit this change manually' + '$$ &Skip remaining changes to this file' + '$$ Record remaining changes to this &file' + '$$ &Done, skip remaining changes and files' + '$$ Record &all changes to all remaining files' + '$$ &Quit, recording no changes' + '$$ &? (display help)' + ), + }, } + def filterpatch(ui, headers, match, operation=None): """Interactively filter patch chunks into applied-only chunks""" messages = getmessages() @@ -1094,15 +1180,15 @@ # chars is a good target) because of issue6158. r = ui.promptchoice("%s\n(enter ? for help) %s" % (query, resps)) ui.write("\n") - if r == 8: # ? + if r == 8: # ? for c, t in ui.extractchoices(resps)[1]: ui.write('%s - %s\n' % (c, encoding.lower(t))) continue - elif r == 0: # yes + elif r == 0: # yes ret = True - elif r == 1: # no + elif r == 1: # no ret = False - elif r == 2: # Edit patch + elif r == 2: # Edit patch if chunk is None: ui.write(_('cannot edit patch for whole file')) ui.write("\n") @@ -1113,7 +1199,8 @@ continue # Patch comment based on the Git one (based on comment at end of # https://mercurial-scm.org/wiki/RecordExtension) - phelp = '---' + _(""" + phelp = '---' + _( + """ To remove '-' lines, make them ' ' lines (context). To remove '+' lines, delete them. Lines starting with # will be removed from the patch. @@ -1123,23 +1210,28 @@ file will be generated: you can use that when you try again. If all lines of the hunk are removed, then the edit is aborted and the hunk is left unchanged. -""") - (patchfd, patchfn) = pycompat.mkstemp(prefix="hg-editor-", - suffix=".diff") +""" + ) + (patchfd, patchfn) = pycompat.mkstemp( + prefix="hg-editor-", suffix=".diff" + ) ncpatchfp = None try: # Write the initial patch f = util.nativeeolwriter(os.fdopen(patchfd, r'wb')) chunk.header.write(f) chunk.write(f) - f.write(''.join(['# ' + i + '\n' - for i in phelp.splitlines()])) + f.write( + ''.join(['# ' + i + '\n' for i in phelp.splitlines()]) + ) f.close() # Start the editor and wait for it to complete editor = ui.geteditor() - ret = ui.system("%s \"%s\"" % (editor, patchfn), - environ={'HGUSER': ui.username()}, - blockedtag='filterpatch') + ret = ui.system( + "%s \"%s\"" % (editor, patchfn), + environ={'HGUSER': ui.username()}, + blockedtag='filterpatch', + ) if ret != 0: ui.warn(_("editor exited with exit code %d\n") % ret) continue @@ -1159,20 +1251,20 @@ # Signal that the chunk shouldn't be applied as-is, but # provide the new patch to be used instead. ret = False - elif r == 3: # Skip + elif r == 3: # Skip ret = skipfile = False - elif r == 4: # file (Record remaining) + elif r == 4: # file (Record remaining) ret = skipfile = True - elif r == 5: # done, skip remaining + elif r == 5: # done, skip remaining ret = skipall = False - elif r == 6: # all + elif r == 6: # all ret = skipall = True - elif r == 7: # quit + elif r == 7: # quit raise error.Abort(_('user quit')) return ret, skipfile, skipall, newpatches seen = set() - applied = {} # 'filename' -> [] of chunks + applied = {} # 'filename' -> [] of chunks skipfile, skipall = None, None pos, total = 1, sum(len(h.hunks) for h in headers) for h in headers: @@ -1186,8 +1278,9 @@ if skipall is None: h.pretty(ui) files = h.files() - msg = (_('examine changes to %s?') % - _(' and ').join("'%s'" % f for f in files)) + msg = _('examine changes to %s?') % _(' and ').join( + "'%s'" % f for f in files + ) if all(match.exact(f) for f in files): r, skipall, np = True, None, None else: @@ -1205,10 +1298,14 @@ msg = messages['single'][operation] % chunk.filename() else: idx = pos - len(h.hunks) + i - msg = messages['multiple'][operation] % (idx, total, - chunk.filename()) - r, skipfile, skipall, newpatches = prompt(skipfile, - skipall, msg, chunk) + msg = messages['multiple'][operation] % ( + idx, + total, + chunk.filename(), + ) + r, skipfile, skipall, newpatches = prompt( + skipfile, skipall, msg, chunk + ) if r: if fixoffset: chunk = copy.copy(chunk) @@ -1222,8 +1319,15 @@ applied[newhunk.filename()].append(newhunk) else: fixoffset += chunk.removed - chunk.added - return (sum([h for h in applied.itervalues() - if h[0].special() or len(h) > 1], []), {}) + return ( + sum( + [h for h in applied.itervalues() if h[0].special() or len(h) > 1], + [], + ), + {}, + ) + + class hunk(object): def __init__(self, desc, num, lr, context): self.number = num @@ -1279,8 +1383,9 @@ self.starta = int(self.starta) self.startb = int(self.startb) try: - diffhelper.addlines(lr, self.hunk, self.lena, self.lenb, - self.a, self.b) + diffhelper.addlines( + lr, self.hunk, self.lena, self.lenb, self.a, self.b + ) except error.ParseError as e: raise PatchError(_("bad hunk #%d: %s") % (self.number, e)) # if we hit eof before finishing out the hunk, the last line will @@ -1317,8 +1422,9 @@ elif l.startswith(' '): u = ' ' + s else: - raise PatchError(_("bad hunk #%d old text line %d") % - (self.number, x)) + raise PatchError( + _("bad hunk #%d old text line %d") % (self.number, x) + ) self.a.append(u) self.hunk.append(u) @@ -1363,8 +1469,9 @@ lr.push(l) break else: - raise PatchError(_("bad hunk #%d old text line %d") % - (self.number, x)) + raise PatchError( + _("bad hunk #%d old text line %d") % (self.number, x) + ) self.b.append(s) while True: if hunki >= len(self.hunk): @@ -1391,8 +1498,12 @@ if x.startswith('+') or x.startswith(' '): self.b.append(x[1:]) # @@ -start,len +start,len @@ - self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena, - self.startb, self.lenb) + self.desc = "@@ -%d,%d +%d,%d @@\n" % ( + self.starta, + self.lena, + self.startb, + self.lenb, + ) self.hunk[0] = self.desc self._fixnewline(lr) @@ -1430,7 +1541,7 @@ bot = min(fuzz, bot) top = min(fuzz, top) - return old[top:len(old) - bot], new[top:len(new) - bot], top + return old[top : len(old) - bot], new[top : len(new) - bot], top return old, new, 0 def fuzzit(self, fuzz, toponly): @@ -1444,8 +1555,10 @@ newstart -= 1 return old, oldstart, new, newstart + class binhunk(object): 'A binary patch file.' + def __init__(self, lr, fname): self.text = None self.delta = False @@ -1470,8 +1583,9 @@ while True: line = getline(lr, self.hunk) if not line: - raise PatchError(_('could not extract "%s" binary data') - % self._fname) + raise PatchError( + _('could not extract "%s" binary data') % self._fname + ) if line.startswith('literal '): size = int(line[8:].rstrip()) break @@ -1490,15 +1604,20 @@ try: dec.append(util.b85decode(line[1:])[:l]) except ValueError as e: - raise PatchError(_('could not decode "%s" binary patch: %s') - % (self._fname, stringutil.forcebytestr(e))) + raise PatchError( + _('could not decode "%s" binary patch: %s') + % (self._fname, stringutil.forcebytestr(e)) + ) line = getline(lr, self.hunk) text = zlib.decompress(''.join(dec)) if len(text) != size: - raise PatchError(_('"%s" length is %d bytes, should be %d') - % (self._fname, len(text), size)) + raise PatchError( + _('"%s" length is %d bytes, should be %d') + % (self._fname, len(text), size) + ) self.text = text + def parsefilename(str): # --- filename \t|space stuff s = str[4:].rstrip('\r\n') @@ -1509,6 +1628,7 @@ return s return s[:i] + def reversehunks(hunks): '''reverse the signs in the hunks given as argument @@ -1572,6 +1692,7 @@ newhunks.append(c) return newhunks + def parsepatch(originalchunks, maxcontext=None): """patch -> [] of headers -> [] of hunks @@ -1615,8 +1736,10 @@ 8 +9 """ + class parser(object): """patch parsing state machine""" + def __init__(self): self.fromline = 0 self.toline = 0 @@ -1636,8 +1759,16 @@ def addcontext(self, context): if self.hunk: - h = recordhunk(self.header, self.fromline, self.toline, - self.proc, self.before, self.hunk, context, maxcontext) + h = recordhunk( + self.header, + self.fromline, + self.toline, + self.proc, + self.before, + self.hunk, + context, + maxcontext, + ) self.header.hunks.append(h) self.fromline += len(self.before) + h.removed self.toline += len(self.before) + h.added @@ -1660,28 +1791,29 @@ self.header = h def addother(self, line): - pass # 'other' lines are ignored + pass # 'other' lines are ignored def finished(self): self.addcontext([]) return self.headers transitions = { - 'file': {'context': addcontext, - 'file': newfile, - 'hunk': addhunk, - 'range': addrange}, - 'context': {'file': newfile, - 'hunk': addhunk, - 'range': addrange, - 'other': addother}, - 'hunk': {'context': addcontext, - 'file': newfile, - 'range': addrange}, - 'range': {'context': addcontext, - 'hunk': addhunk}, + 'file': { + 'context': addcontext, + 'file': newfile, + 'hunk': addhunk, + 'range': addrange, + }, + 'context': { + 'file': newfile, + 'hunk': addhunk, + 'range': addrange, + 'other': addother, + }, + 'hunk': {'context': addcontext, 'file': newfile, 'range': addrange}, + 'range': {'context': addcontext, 'hunk': addhunk}, 'other': {'other': addother}, - } + } p = parser() fp = stringio() @@ -1693,12 +1825,14 @@ try: p.transitions[state][newstate](p, data) except KeyError: - raise PatchError('unhandled transition: %s -> %s' % - (state, newstate)) + raise PatchError( + 'unhandled transition: %s -> %s' % (state, newstate) + ) state = newstate del fp return p.finished() + def pathtransform(path, strip, prefix): '''turn a path from a patch into a path suitable for the repository @@ -1728,15 +1862,18 @@ while count > 0: i = path.find('/', i) if i == -1: - raise PatchError(_("unable to strip away %d of %d dirs from %s") % - (count, strip, path)) + raise PatchError( + _("unable to strip away %d of %d dirs from %s") + % (count, strip, path) + ) i += 1 # consume '//' in the path - while i < pathlen - 1 and path[i:i + 1] == '/': + while i < pathlen - 1 and path[i : i + 1] == '/': i += 1 count -= 1 return path[:i].lstrip(), prefix + path[i:].rstrip() + def makepatchmeta(backend, afile_orig, bfile_orig, hunk, strip, prefix): nulla = afile_orig == "/dev/null" nullb = bfile_orig == "/dev/null" @@ -1753,17 +1890,22 @@ # some diff programs apparently produce patches where the afile is # not /dev/null, but afile starts with bfile - abasedir = afile[:afile.rfind('/') + 1] - bbasedir = bfile[:bfile.rfind('/') + 1] - if (missing and abasedir == bbasedir and afile.startswith(bfile) - and hunk.starta == 0 and hunk.lena == 0): + abasedir = afile[: afile.rfind('/') + 1] + bbasedir = bfile[: bfile.rfind('/') + 1] + if ( + missing + and abasedir == bbasedir + and afile.startswith(bfile) + and hunk.starta == 0 + and hunk.lena == 0 + ): create = True missing = False # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the # diff is between a file and its backup. In this case, the original # file should be patched (see original mpatch code). - isbackup = (abase == bbase and bfile.startswith(afile)) + isbackup = abase == bbase and bfile.startswith(afile) fname = None if not missing: if gooda and goodb: @@ -1792,6 +1934,7 @@ gp.op = 'DELETE' return gp + def scanpatch(fp): """like patch.iterhunks, but yield different events @@ -1816,9 +1959,11 @@ for line in iter(lr.readline, ''): if line.startswith('diff --git a/') or line.startswith('diff -r '): + def notheader(line): s = line.split(None, 1) return not s or s[0] not in ('---', 'diff') + header = scanwhile(line, notheader) fromfile = lr.readline() if fromfile.startswith('---'): @@ -1840,6 +1985,7 @@ else: yield 'other', line + def scangitpatch(lr, firstline): """ Git patches can emit: @@ -1866,6 +2012,7 @@ fp.seek(pos) return gitpatches + def iterhunks(fp): """Read a patch and yield the following events: - ("file", afile, bfile, firsthunk): select a new target file. @@ -1890,10 +2037,10 @@ if state == BFILE and ( (not context and x.startswith('@')) or (context is not False and x.startswith('***************')) - or x.startswith('GIT binary patch')): + or x.startswith('GIT binary patch') + ): gp = None - if (gitpatches and - gitpatches[-1].ispatching(afile, bfile)): + if gitpatches and gitpatches[-1].ispatching(afile, bfile): gp = gitpatches.pop() if x.startswith('GIT binary patch'): h = binhunk(lr, gp.path) @@ -1913,8 +2060,9 @@ if gitpatches is None: # scan whole input for git metadata gitpatches = scangitpatch(lr, x) - yield 'git', [g.copy() for g in gitpatches - if g.op in ('COPY', 'RENAME')] + yield 'git', [ + g.copy() for g in gitpatches if g.op in ('COPY', 'RENAME') + ] gitpatches.reverse() afile = 'a/' + m.group(1) bfile = 'b/' + m.group(2) @@ -1922,8 +2070,9 @@ gp = gitpatches.pop() yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy()) if not gitpatches: - raise PatchError(_('failed to synchronize metadata for "%s"') - % afile[2:]) + raise PatchError( + _('failed to synchronize metadata for "%s"') % afile[2:] + ) newfile = True elif x.startswith('---'): # check for a unified diff @@ -1961,10 +2110,12 @@ gp = gitpatches.pop() yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy()) + def applybindelta(binchunk, data): """Apply a binary delta hunk The algorithm used is the algorithm from git's patch-delta.c """ + def deltahead(binchunk): i = 0 for c in pycompat.bytestr(binchunk): @@ -1972,6 +2123,7 @@ if not (ord(c) & 0x80): return i return i + out = "" s = deltahead(binchunk) binchunk = binchunk[s:] @@ -1979,31 +2131,31 @@ binchunk = binchunk[s:] i = 0 while i < len(binchunk): - cmd = ord(binchunk[i:i + 1]) + cmd = ord(binchunk[i : i + 1]) i += 1 - if (cmd & 0x80): + if cmd & 0x80: offset = 0 size = 0 - if (cmd & 0x01): - offset = ord(binchunk[i:i + 1]) + if cmd & 0x01: + offset = ord(binchunk[i : i + 1]) i += 1 - if (cmd & 0x02): - offset |= ord(binchunk[i:i + 1]) << 8 + if cmd & 0x02: + offset |= ord(binchunk[i : i + 1]) << 8 i += 1 - if (cmd & 0x04): - offset |= ord(binchunk[i:i + 1]) << 16 + if cmd & 0x04: + offset |= ord(binchunk[i : i + 1]) << 16 i += 1 - if (cmd & 0x08): - offset |= ord(binchunk[i:i + 1]) << 24 + if cmd & 0x08: + offset |= ord(binchunk[i : i + 1]) << 24 i += 1 - if (cmd & 0x10): - size = ord(binchunk[i:i + 1]) + if cmd & 0x10: + size = ord(binchunk[i : i + 1]) i += 1 - if (cmd & 0x20): - size |= ord(binchunk[i:i + 1]) << 8 + if cmd & 0x20: + size |= ord(binchunk[i : i + 1]) << 8 i += 1 - if (cmd & 0x40): - size |= ord(binchunk[i:i + 1]) << 16 + if cmd & 0x40: + size |= ord(binchunk[i : i + 1]) << 16 i += 1 if size == 0: size = 0x10000 @@ -2017,6 +2169,7 @@ raise PatchError(_('unexpected delta opcode 0')) return out + def applydiff(ui, fp, backend, store, strip=1, prefix='', eolmode='strict'): """Reads a patch from fp and tries to apply it. @@ -2027,8 +2180,17 @@ read in binary mode. Otherwise, line endings are ignored when patching then normalized according to 'eolmode'. """ - return _applydiff(ui, fp, patchfile, backend, store, strip=strip, - prefix=prefix, eolmode=eolmode) + return _applydiff( + ui, + fp, + patchfile, + backend, + store, + strip=strip, + prefix=prefix, + eolmode=eolmode, + ) + def _canonprefix(repo, prefix): if prefix: @@ -2037,9 +2199,12 @@ prefix += '/' return prefix -def _applydiff(ui, fp, patcher, backend, store, strip=1, prefix='', - eolmode='strict'): + +def _applydiff( + ui, fp, patcher, backend, store, strip=1, prefix='', eolmode='strict' +): prefix = _canonprefix(backend.repo, prefix) + def pstrip(p): return pathtransform(p, strip - 1, prefix)[1] @@ -2064,8 +2229,9 @@ if gp.oldpath: gp.oldpath = pstrip(gp.oldpath) else: - gp = makepatchmeta(backend, afile, bfile, first_hunk, strip, - prefix) + gp = makepatchmeta( + backend, afile, bfile, first_hunk, strip, prefix + ) if gp.op == 'RENAME': backend.unlink(gp.oldpath) if not first_hunk: @@ -2077,8 +2243,9 @@ data, mode = store.getfile(gp.oldpath)[:2] if data is None: # This means that the old path does not exist - raise PatchError(_("source file '%s' does not exist") - % gp.oldpath) + raise PatchError( + _("source file '%s' does not exist") % gp.oldpath + ) if gp.mode: mode = gp.mode if gp.op == 'ADD': @@ -2086,15 +2253,17 @@ # must be created data = '' if data or mode: - if (gp.op in ('ADD', 'RENAME', 'COPY') - and backend.exists(gp.path)): - raise PatchError(_("cannot create %s: destination " - "already exists") % gp.path) + if gp.op in ('ADD', 'RENAME', 'COPY') and backend.exists( + gp.path + ): + raise PatchError( + _("cannot create %s: destination " "already exists") + % gp.path + ) backend.setfile(gp.path, data, mode, gp.oldpath) continue try: - current_file = patcher(ui, gp, backend, store, - eolmode=eolmode) + current_file = patcher(ui, gp, backend, store, eolmode=eolmode) except PatchError as inst: ui.warn(str(inst) + '\n') current_file = None @@ -2122,8 +2291,8 @@ return -1 return err -def _externalpatch(ui, repo, patcher, patchname, strip, files, - similarity): + +def _externalpatch(ui, repo, patcher, patchname, strip, files, similarity): """use <patcher> to apply <patchname> to the working directory. returns whether patch was applied with fuzz factor.""" @@ -2132,8 +2301,12 @@ cwd = repo.root if cwd: args.append('-d %s' % procutil.shellquote(cwd)) - cmd = ('%s %s -p%d < %s' - % (patcher, ' '.join(args), strip, procutil.shellquote(patchname))) + cmd = '%s %s -p%d < %s' % ( + patcher, + ' '.join(args), + strip, + procutil.shellquote(patchname), + ) ui.debug('Using external patch tool: %s\n' % cmd) fp = procutil.popen(cmd, 'rb') try: @@ -2162,12 +2335,15 @@ scmutil.marktouched(repo, files, similarity) code = fp.close() if code: - raise PatchError(_("patch command failed: %s") % - procutil.explainexit(code)) + raise PatchError( + _("patch command failed: %s") % procutil.explainexit(code) + ) return fuzz -def patchbackend(ui, backend, patchobj, strip, prefix, files=None, - eolmode='strict'): + +def patchbackend( + ui, backend, patchobj, strip, prefix, files=None, eolmode='strict' +): if files is None: files = set() if eolmode is None: @@ -2182,8 +2358,9 @@ except TypeError: fp = patchobj try: - ret = applydiff(ui, fp, backend, store, strip=strip, prefix=prefix, - eolmode=eolmode) + ret = applydiff( + ui, fp, backend, store, strip=strip, prefix=prefix, eolmode=eolmode + ) finally: if fp != patchobj: fp.close() @@ -2193,20 +2370,40 @@ raise PatchError(_('patch failed to apply')) return ret > 0 -def internalpatch(ui, repo, patchobj, strip, prefix='', files=None, - eolmode='strict', similarity=0): + +def internalpatch( + ui, + repo, + patchobj, + strip, + prefix='', + files=None, + eolmode='strict', + similarity=0, +): """use builtin patch to apply <patchobj> to the working directory. returns whether patch was applied with fuzz factor.""" backend = workingbackend(ui, repo, similarity) return patchbackend(ui, backend, patchobj, strip, prefix, files, eolmode) -def patchrepo(ui, repo, ctx, store, patchobj, strip, prefix, files=None, - eolmode='strict'): + +def patchrepo( + ui, repo, ctx, store, patchobj, strip, prefix, files=None, eolmode='strict' +): backend = repobackend(ui, repo, ctx, store) return patchbackend(ui, backend, patchobj, strip, prefix, files, eolmode) -def patch(ui, repo, patchname, strip=1, prefix='', files=None, eolmode='strict', - similarity=0): + +def patch( + ui, + repo, + patchname, + strip=1, + prefix='', + files=None, + eolmode='strict', + similarity=0, +): """Apply <patchname> to the working directory. 'eolmode' specifies how end of lines should be handled. It can be: @@ -2222,10 +2419,13 @@ if files is None: files = set() if patcher: - return _externalpatch(ui, repo, patcher, patchname, strip, - files, similarity) - return internalpatch(ui, repo, patchname, strip, prefix, files, eolmode, - similarity) + return _externalpatch( + ui, repo, patcher, patchname, strip, files, similarity + ) + return internalpatch( + ui, repo, patchname, strip, prefix, files, eolmode, similarity + ) + def changedfiles(ui, repo, patchpath, strip=1, prefix=''): backend = fsbackend(ui, repo.root) @@ -2238,11 +2438,13 @@ if gp: gp.path = pathtransform(gp.path, strip - 1, prefix)[1] if gp.oldpath: - gp.oldpath = pathtransform(gp.oldpath, strip - 1, - prefix)[1] + gp.oldpath = pathtransform( + gp.oldpath, strip - 1, prefix + )[1] else: - gp = makepatchmeta(backend, afile, bfile, first_hunk, strip, - prefix) + gp = makepatchmeta( + backend, afile, bfile, first_hunk, strip, prefix + ) changed.add(gp.path) if gp.op == 'RENAME': changed.add(gp.oldpath) @@ -2250,16 +2452,29 @@ raise error.Abort(_('unsupported parser state: %s') % state) return changed + class GitDiffRequired(Exception): pass + diffopts = diffutil.diffallopts diffallopts = diffutil.diffallopts difffeatureopts = diffutil.difffeatureopts -def diff(repo, node1=None, node2=None, match=None, changes=None, - opts=None, losedatafn=None, pathfn=None, copy=None, - copysourcematch=None, hunksfilterfn=None): + +def diff( + repo, + node1=None, + node2=None, + match=None, + changes=None, + opts=None, + losedatafn=None, + pathfn=None, + copy=None, + copysourcematch=None, + hunksfilterfn=None, +): '''yields diff of changes to files between two nodes, or node and working directory. @@ -2296,15 +2511,24 @@ ctx2 = repo[node2] for fctx1, fctx2, hdr, hunks in diffhunks( - repo, ctx1=ctx1, ctx2=ctx2, match=match, changes=changes, opts=opts, - losedatafn=losedatafn, pathfn=pathfn, copy=copy, - copysourcematch=copysourcematch): + repo, + ctx1=ctx1, + ctx2=ctx2, + match=match, + changes=changes, + opts=opts, + losedatafn=losedatafn, + pathfn=pathfn, + copy=copy, + copysourcematch=copysourcematch, + ): if hunksfilterfn is not None: # If the file has been removed, fctx2 is None; but this should # not occur here since we catch removed files early in # logcmdutil.getlinerangerevs() for 'hg log -L'. - assert fctx2 is not None, ( - 'fctx2 unexpectly None in diff hunks filtering') + assert ( + fctx2 is not None + ), 'fctx2 unexpectly None in diff hunks filtering' hunks = hunksfilterfn(fctx2, hunks) text = ''.join(sum((list(hlines) for hrange, hlines in hunks), [])) if hdr and (text or len(hdr) > 1): @@ -2312,8 +2536,19 @@ if text: yield text -def diffhunks(repo, ctx1, ctx2, match=None, changes=None, opts=None, - losedatafn=None, pathfn=None, copy=None, copysourcematch=None): + +def diffhunks( + repo, + ctx1, + ctx2, + match=None, + changes=None, + opts=None, + losedatafn=None, + pathfn=None, + copy=None, + copysourcematch=None, +): """Yield diff of changes to files in the form of (`header`, `hunks`) tuples where `header` is a list of diff headers and `hunks` is an iterable of (`hunkrange`, `hunklines`) tuples. @@ -2327,6 +2562,7 @@ def lrugetfilectx(): cache = {} order = collections.deque() + def getfilectx(f, ctx): fctx = ctx.filectx(f, filelog=cache.get(f)) if f not in cache: @@ -2337,7 +2573,9 @@ order.remove(f) order.append(f) return fctx + return getfilectx + getfilectx = lrugetfilectx() if not changes: @@ -2361,8 +2599,9 @@ if copysourcematch: # filter out copies where source side isn't inside the matcher # (copies.pathcopies() already filtered out the destination) - copy = {dst: src for dst, src in copy.iteritems() - if copysourcematch(src)} + copy = { + dst: src for dst, src in copy.iteritems() if copysourcematch(src) + } modifiedset = set(modified) addedset = set(added) @@ -2388,17 +2627,33 @@ del copy[dst] prefetchmatch = scmutil.matchfiles( - repo, list(modifiedset | addedset | removedset)) + repo, list(modifiedset | addedset | removedset) + ) scmutil.prefetchfiles(repo, [ctx1.rev(), ctx2.rev()], prefetchmatch) def difffn(opts, losedata): - return trydiff(repo, revs, ctx1, ctx2, modified, added, removed, - copy, getfilectx, opts, losedata, pathfn) + return trydiff( + repo, + revs, + ctx1, + ctx2, + modified, + added, + removed, + copy, + getfilectx, + opts, + losedata, + pathfn, + ) + if opts.upgrade and not opts.git: try: + def losedata(fn): if not losedatafn or not losedatafn(fn=fn): raise GitDiffRequired + # Buffer the whole output until we are sure it can be generated return list(difffn(opts.copy(git=False), losedata)) except GitDiffRequired: @@ -2406,6 +2661,7 @@ else: return difffn(opts, None) + def diffsinglehunk(hunklines): """yield tokens for a list of lines in a single hunk""" for line in hunklines: @@ -2426,9 +2682,10 @@ yield (token, label) if chompline != stripline: - yield (chompline[len(stripline):], 'diff.trailingwhitespace') + yield (chompline[len(stripline) :], 'diff.trailingwhitespace') if chompline != line: - yield (line[len(chompline):], '') + yield (line[len(chompline) :], '') + def diffsinglehunkinline(hunklines): """yield tokens for a list of lines in a single hunk, with inline colors""" @@ -2467,8 +2724,10 @@ btokens.append((changed, token)) # yield deleted tokens, then inserted ones - for prefix, label, tokens in [('-', 'diff.deleted', atokens), - ('+', 'diff.inserted', btokens)]: + for prefix, label, tokens in [ + ('-', 'diff.deleted', atokens), + ('+', 'diff.inserted', btokens), + ]: nextisnewline = True for changed, token in tokens: if nextisnewline: @@ -2477,12 +2736,12 @@ # special handling line end isendofline = token.endswith('\n') if isendofline: - chomp = token[:-1] # chomp + chomp = token[:-1] # chomp if chomp.endswith('\r'): chomp = chomp[:-1] - endofline = token[len(chomp):] - token = chomp.rstrip() # detect spaces at the end - endspaces = chomp[len(token):] + endofline = token[len(chomp) :] + token = chomp.rstrip() # detect spaces at the end + endspaces = chomp[len(token) :] # scan tabs for maybetab in tabsplitter.findall(token): if b'\t' == maybetab[0:1]: @@ -2499,29 +2758,34 @@ yield (endofline, '') nextisnewline = True + def difflabel(func, *args, **kw): '''yields 2-tuples of (output, label) based on the output of func()''' if kw.get(r'opts') and kw[r'opts'].worddiff: dodiffhunk = diffsinglehunkinline else: dodiffhunk = diffsinglehunk - headprefixes = [('diff', 'diff.diffline'), - ('copy', 'diff.extended'), - ('rename', 'diff.extended'), - ('old', 'diff.extended'), - ('new', 'diff.extended'), - ('deleted', 'diff.extended'), - ('index', 'diff.extended'), - ('similarity', 'diff.extended'), - ('---', 'diff.file_a'), - ('+++', 'diff.file_b')] - textprefixes = [('@', 'diff.hunk'), - # - and + are handled by diffsinglehunk - ] + headprefixes = [ + ('diff', 'diff.diffline'), + ('copy', 'diff.extended'), + ('rename', 'diff.extended'), + ('old', 'diff.extended'), + ('new', 'diff.extended'), + ('deleted', 'diff.extended'), + ('index', 'diff.extended'), + ('similarity', 'diff.extended'), + ('---', 'diff.file_a'), + ('+++', 'diff.file_b'), + ] + textprefixes = [ + ('@', 'diff.hunk'), + # - and + are handled by diffsinglehunk + ] head = False # buffers a hunk, i.e. adjacent "-", "+" lines without other changes. hunkbuffer = [] + def consumehunkbuffer(): if hunkbuffer: for token in dodiffhunk(hunkbuffer): @@ -2560,8 +2824,10 @@ if stripline.startswith(prefix): yield (stripline, label) if line != stripline: - yield (line[len(stripline):], - 'diff.trailingwhitespace') + yield ( + line[len(stripline) :], + 'diff.trailingwhitespace', + ) break else: yield (line, '') @@ -2570,10 +2836,12 @@ for token in consumehunkbuffer(): yield token + def diffui(*args, **kw): '''like diff(), but yields 2-tuples of (output, label) for ui.write()''' return difflabel(diff, *args, **kw) + def _filepairs(modified, added, removed, copy, opts): '''generates tuples (f1, f2, copyop), where f1 is the name of the file before and f2 is the the name after. For added files, f1 will be None, @@ -2602,13 +2870,29 @@ f2 = None if opts.git: # have we already reported a copy above? - if (f in copyto and copyto[f] in addedset - and copy[copyto[f]] == f): + if ( + f in copyto + and copyto[f] in addedset + and copy[copyto[f]] == f + ): continue yield f1, f2, copyop -def trydiff(repo, revs, ctx1, ctx2, modified, added, removed, - copy, getfilectx, opts, losedatafn, pathfn): + +def trydiff( + repo, + revs, + ctx1, + ctx2, + modified, + added, + removed, + copy, + getfilectx, + opts, + losedatafn, + pathfn, +): '''given input data, generate a diff and yield it in blocks If generating a diff would lose data like flags or binary data and @@ -2668,28 +2952,36 @@ binary = any(f.isbinary() for f in [fctx1, fctx2] if f is not None) if losedatafn and not opts.git: - if (binary or + if ( + binary + or # copy/rename - f2 in copy or + f2 in copy + or # empty file creation - (not f1 and isempty(fctx2)) or + (not f1 and isempty(fctx2)) + or # empty file deletion - (isempty(fctx1) and not f2) or + (isempty(fctx1) and not f2) + or # create with flags - (not f1 and flag2) or + (not f1 and flag2) + or # change flags - (f1 and f2 and flag1 != flag2)): + (f1 and f2 and flag1 != flag2) + ): losedatafn(f2 or f1) path1 = pathfn(f1 or f2) path2 = pathfn(f2 or f1) header = [] if opts.git: - header.append('diff --git %s%s %s%s' % - (aprefix, path1, bprefix, path2)) - if not f1: # added + header.append( + 'diff --git %s%s %s%s' % (aprefix, path1, bprefix, path2) + ) + if not f1: # added header.append('new file mode %s' % gitmode[flag2]) - elif not f2: # removed + elif not f2: # removed header.append('deleted file mode %s' % gitmode[flag1]) else: # modified/copied/renamed mode1, mode2 = gitmode[flag1], gitmode[flag2] @@ -2716,8 +3008,9 @@ # yes | yes * * * | text diff | yes # no | * * * * | text diff | yes # [1]: hash(fctx.data()) is outputted. so fctx.data() cannot be faked - if binary and (not opts.git or (opts.git and opts.nobinary and not - opts.index)): + if binary and ( + not opts.git or (opts.git and opts.nobinary and not opts.index) + ): # fast path: no binary content will be displayed, content1 and # content2 are only used for equivalent test. cmp() could have a # fast path. @@ -2725,7 +3018,7 @@ content1 = b'\0' if fctx2 is not None: if fctx1 is not None and not fctx1.cmp(fctx2): - content2 = b'\0' # not different + content2 = b'\0' # not different else: content2 = b'\0\0' else: @@ -2738,26 +3031,38 @@ if binary and opts.git and not opts.nobinary: text = mdiff.b85diff(content1, content2) if text: - header.append('index %s..%s' % - (gitindex(content1), gitindex(content2))) - hunks = (None, [text]), + header.append( + 'index %s..%s' % (gitindex(content1), gitindex(content2)) + ) + hunks = ((None, [text]),) else: if opts.git and opts.index > 0: flag = flag1 if flag is None: flag = flag2 - header.append('index %s..%s %s' % - (gitindex(content1)[0:opts.index], - gitindex(content2)[0:opts.index], - gitmode[flag])) - - uheaders, hunks = mdiff.unidiff(content1, date1, - content2, date2, - path1, path2, - binary=binary, opts=opts) + header.append( + 'index %s..%s %s' + % ( + gitindex(content1)[0 : opts.index], + gitindex(content2)[0 : opts.index], + gitmode[flag], + ) + ) + + uheaders, hunks = mdiff.unidiff( + content1, + date1, + content2, + date2, + path1, + path2, + binary=binary, + opts=opts, + ) header.extend(uheaders) yield fctx1, fctx2, header, hunks + def diffstatsum(stats): maxfile, maxtotal, addtotal, removetotal, binary = 0, 0, 0, 0, False for f, a, r, b in stats: @@ -2769,6 +3074,7 @@ return maxfile, maxtotal, addtotal, removetotal, binary + def diffstatdata(lines): diffre = re.compile(br'^diff .*-r [a-z0-9]+\s(.*)$') @@ -2802,8 +3108,9 @@ adds += 1 elif line.startswith('-') and not inheader: removes += 1 - elif (line.startswith('GIT binary patch') or - line.startswith('Binary file')): + elif line.startswith('GIT binary patch') or line.startswith( + 'Binary file' + ): isbinary = True elif line.startswith('rename from'): filename = line[12:] @@ -2812,6 +3119,7 @@ addresult() return results + def diffstat(lines, width=80): output = [] stats = diffstatdata(lines) @@ -2839,17 +3147,27 @@ count = '%d' % (adds + removes) pluses = '+' * scale(adds) minuses = '-' * scale(removes) - output.append(' %s%s | %*s %s%s\n' % - (filename, ' ' * (maxname - encoding.colwidth(filename)), - countwidth, count, pluses, minuses)) + output.append( + ' %s%s | %*s %s%s\n' + % ( + filename, + ' ' * (maxname - encoding.colwidth(filename)), + countwidth, + count, + pluses, + minuses, + ) + ) if stats: - output.append(_(' %d files changed, %d insertions(+), ' - '%d deletions(-)\n') - % (len(stats), totaladds, totalremoves)) + output.append( + _(' %d files changed, %d insertions(+), ' '%d deletions(-)\n') + % (len(stats), totaladds, totalremoves) + ) return ''.join(output) + def diffstatui(*args, **kw): '''like diffstat(), but yields 2-tuples of (output, label) for ui.write()