--- 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()