##// END OF EJS Templates
interfaces: convert `imanifestrevisionstored` from zope `Attribute` attrs...
interfaces: convert `imanifestrevisionstored` from zope `Attribute` attrs This is the same transformation as b455dfddfed0 did for dirstate.

File last commit:

r53276:c921c23a default
r53370:a1dd484b default
Show More
patch.py
3255 lines | 101.6 KiB | text/x-python | PythonLexer
Brendan Cully
Move patch-related code into its own module.
r2861 # patch.py - patch file parsing routines
#
Vadim Gelfer
merge git patch code.
r2865 # Copyright 2006 Brendan Cully <brendan@kublai.com>
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
Vadim Gelfer
merge git patch code.
r2865 #
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Brendan Cully
Move patch-related code into its own module.
r2861
Matt Harbison
typing: add `from __future__ import annotations` to most files...
r52756 from __future__ import annotations
Gregory Szorc
patch: use absolute_import
r27485
Martin von Zweigbergk
util: drop alias for collections.deque...
r25113 import collections
Gregory Szorc
patch: make extract() a context manager (API)...
r37639 import contextlib
Gregory Szorc
patch: use absolute_import
r27485 import copy
import os
import re
import shutil
import zlib
Augie Fackler
patch: move mercurial-specific imports after stdlib imports
r10965
Gregory Szorc
patch: use absolute_import
r27485 from .i18n import _
from .node import (
hex,
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 sha1nodeconstants,
Gregory Szorc
patch: use absolute_import
r27485 short,
)
from . import (
copies,
Yuya Nishihara
diffhelper: rename module to avoid conflicts with ancient C module (issue5846)...
r37821 diffhelper,
Yuya Nishihara
diffutil: move the module out of utils package...
r38607 diffutil,
Gregory Szorc
patch: use absolute_import
r27485 encoding,
error,
Julien Cristau
patch: when importing from email, RFC2047-decode From/Subject headers...
r28341 mail,
Gregory Szorc
patch: use absolute_import
r27485 mdiff,
pathutil,
Pulkit Goyal
py3: convert the mode argument of os.fdopen to unicodes (1 of 2)...
r30924 pycompat,
Gregory Szorc
patch: use absolute_import
r27485 scmutil,
Sean Farley
patch: use opt.showsimilarity to calculate and show the similarity...
r30807 similar,
Gregory Szorc
patch: use absolute_import
r27485 util,
Pierre-Yves David
vfs: use 'vfs' module directly in 'mercurial.patch'...
r31233 vfs as vfsmod,
Gregory Szorc
patch: use absolute_import
r27485 )
Yuya Nishihara
stringutil: bulk-replace call sites to point to new module...
r37102 from .utils import (
dateutil,
Augie Fackler
core: migrate uses of hashlib.sha1 to hashutil.sha1...
r44517 hashutil,
Yuya Nishihara
procutil: bulk-replace function calls to point to new module
r37138 procutil,
Yuya Nishihara
stringutil: bulk-replace call sites to point to new module...
r37102 stringutil,
)
Yuya Nishihara
diffhelpers: switch to policy importer...
r32370
timeless
pycompat: switch to util.stringio for py3 compat
r28861 stringio = util.stringio
Vadim Gelfer
commands.import: refactor patch parsing into patch.extract.
r2866
Pulkit Goyal
patch: make regular expressions bytes by adding b''
r31630 gitre = re.compile(br'diff --git a/(.*) b/(.*)')
tabsplitter = re.compile(br'(\t+|[^\t]+)')
Augie Fackler
formatting: blacken the codebase...
r43346 wordsplitter = re.compile(
Martin von Zweigbergk
cleanup: join string literals that are already on one line...
r43387 br'(\t+| +|[a-zA-Z0-9_\x80-\xff]+|[^ \ta-zA-Z0-9_\x80-\xff])'
Augie Fackler
formatting: blacken the codebase...
r43346 )
Dirkjan Ochtman
patch: consolidate two different regexes for parsing of git diffs
r7199
Yuya Nishihara
error: move patch.PatchError so it can easily implement __bytes__ (API)
r34252 PatchError = error.PatchError
Martin von Zweigbergk
errors: return more detailed errors when failing to parse or apply patch...
r49186 PatchParseError = error.PatchParseError
PatchApplicationError = error.PatchApplicationError
Brendan Cully
Move import's working dir update code into patch.updatedir
r2933
# public functions
Augie Fackler
formatting: blacken the codebase...
r43346
Brendan Cully
import: import each patch in a file or stream as a separate change...
r10384 def split(stream):
'''return an iterator of individual patches from a stream'''
Augie Fackler
formatting: blacken the codebase...
r43346
Brendan Cully
import: import each patch in a file or stream as a separate change...
r10384 def isheader(line, inheader):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if inheader and line.startswith((b' ', b'\t')):
Brendan Cully
import: import each patch in a file or stream as a separate change...
r10384 # continuation
return True
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if line.startswith((b' ', b'-', b'+')):
Peter Arrenbrecht
patch: don't look for headers in diff lines...
r10883 # diff line - don't check for header pattern in there
return False
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 l = line.split(b': ', 1)
return len(l) == 2 and b' ' not in l[0]
Brendan Cully
import: import each patch in a file or stream as a separate change...
r10384
def chunk(lines):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return stringio(b''.join(lines))
Brendan Cully
import: import each patch in a file or stream as a separate change...
r10384
def hgsplit(stream, cur):
inheader = True
for line in stream:
if not line.strip():
inheader = False
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if not inheader and line.startswith(b'# HG changeset patch'):
Brendan Cully
import: import each patch in a file or stream as a separate change...
r10384 yield chunk(cur)
cur = []
inheader = True
cur.append(line)
if cur:
yield chunk(cur)
def mboxsplit(stream, cur):
for line in stream:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if line.startswith(b'From '):
Brendan Cully
import: import each patch in a file or stream as a separate change...
r10384 for c in split(chunk(cur[1:])):
yield c
cur = []
cur.append(line)
if cur:
for c in split(chunk(cur[1:])):
yield c
def mimesplit(stream, cur):
def msgfp(m):
timeless
pycompat: switch to util.stringio for py3 compat
r28861 fp = stringio()
Matt Harbison
pytype: stop excluding patch.py...
r49314 # pytype: disable=wrong-arg-types
Denis Laxalde
py3: use email.generator.BytesGenerator in patch.split()...
r43426 g = mail.Generator(fp, mangle_from_=False)
Matt Harbison
pytype: stop excluding patch.py...
r49314 # pytype: enable=wrong-arg-types
Brendan Cully
import: import each patch in a file or stream as a separate change...
r10384 g.flatten(m)
fp.seek(0)
return fp
for line in stream:
cur.append(line)
c = chunk(cur)
Yuya Nishihara
py3: ditch email.parser.BytesParser which appears to be plain crap...
r38354 m = mail.parse(c)
Brendan Cully
import: import each patch in a file or stream as a separate change...
r10384 if not m.is_multipart():
yield msgfp(m)
else:
Manuel Jacob
import: fix erroneous comparison of str with bytes
r52689 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
Brendan Cully
import: import each patch in a file or stream as a separate change...
r10384 for part in m.walk():
ct = part.get_content_type()
if ct not in ok_types:
continue
yield msgfp(part)
def headersplit(stream, cur):
inheader = False
for line in stream:
if not inheader and isheader(line, inheader):
yield chunk(cur)
cur = []
inheader = True
if inheader and not isheader(line, inheader):
inheader = False
cur.append(line)
if cur:
yield chunk(cur)
def remainder(cur):
yield chunk(cur)
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class fiter:
Brendan Cully
import: import each patch in a file or stream as a separate change...
r10384 def __init__(self, fp):
self.fp = fp
def __iter__(self):
return self
def next(self):
l = self.fp.readline()
if not l:
raise StopIteration
return l
Gregory Szorc
py3: define __next__ in patch.py...
r35192 __next__ = next
Brendan Cully
import: import each patch in a file or stream as a separate change...
r10384 inheader = False
cur = []
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 mimeheaders = [b'content-type']
safehasattr: drop usage in favor of hasattr...
r51821 if not hasattr(stream, 'next'):
Brendan Cully
import: import each patch in a file or stream as a separate change...
r10384 # http responses, for example, have readline but not next
stream = fiter(stream)
for line in stream:
cur.append(line)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if line.startswith(b'# HG changeset patch'):
Brendan Cully
import: import each patch in a file or stream as a separate change...
r10384 return hgsplit(stream, cur)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif line.startswith(b'From '):
Brendan Cully
import: import each patch in a file or stream as a separate change...
r10384 return mboxsplit(stream, cur)
elif isheader(line, inheader):
inheader = True
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if line.split(b':', 1)[0].lower() in mimeheaders:
Brendan Cully
import: import each patch in a file or stream as a separate change...
r10384 # let email parser handle this
return mimesplit(stream, cur)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif line.startswith(b'--- ') and inheader:
Brendan Cully
import: if in doubt, consume stream until start of diff...
r10501 # No evil headers seen by diff start, split by hand
Brendan Cully
import: import each patch in a file or stream as a separate change...
r10384 return headersplit(stream, cur)
# Not enough info, keep reading
# if we are here, we have a very plain patch
return remainder(cur)
Augie Fackler
formatting: blacken the codebase...
r43346
Pierre-Yves David
extract: add some facility for extensible header parsing...
r26557 ## Some facility for extensible patch parsing:
# list of pairs ("header to match", "data key")
Augie Fackler
formatting: blacken the codebase...
r43346 patchheadermap = [
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 (b'Date', b'date'),
(b'Branch', b'branch'),
(b'Node ID', b'nodeid'),
Augie Fackler
formatting: blacken the codebase...
r43346 ]
Pierre-Yves David
extract: add some facility for extensible header parsing...
r26557
Gregory Szorc
patch: make extract() a context manager (API)...
r37639 @contextlib.contextmanager
Vadim Gelfer
commands.import: refactor patch parsing into patch.extract.
r2866 def extract(ui, fileobj):
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """extract patch from data read from fileobj.
Vadim Gelfer
commands.import: refactor patch parsing into patch.extract.
r2866
Brendan Cully
Add import --exact....
r4263 patch can be a normal patch or contained in an email message.
Vadim Gelfer
commands.import: refactor patch parsing into patch.extract.
r2866
Mads Kiilerich
spelling: trivial spell checking
r26781 return a dictionary. Standard keys are:
Pierre-Yves David
patch: move 'extract' return to a dictionnary...
r26547 - filename,
- message,
- user,
- date,
- branch,
- node,
- p1,
- p2.
Mads Kiilerich
spelling: trivial spell checking
r26781 Any item can be missing from the dictionary. If filename is missing,
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 fileobj did not contain a patch. Caller must unlink filename when done."""
Vadim Gelfer
commands.import: refactor patch parsing into patch.extract.
r2866
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fd, tmpname = pycompat.mkstemp(prefix=b'hg-patch-')
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 tmpfp = os.fdopen(fd, 'wb')
Gregory Szorc
patch: make extract() a context manager (API)...
r37639 try:
yield _extract(ui, fileobj, tmpname, tmpfp)
finally:
tmpfp.close()
os.unlink(tmpname)
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
patch: make extract() a context manager (API)...
r37639 def _extract(ui, fileobj, tmpname, tmpfp):
Vadim Gelfer
commands.import: refactor patch parsing into patch.extract.
r2866 # attempt to detect the start of a patch
# (this heuristic is borrowed from quilt)
Augie Fackler
formatting: blacken the codebase...
r43346 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,
)
Vadim Gelfer
commands.import: refactor patch parsing into patch.extract.
r2866
Pierre-Yves David
patch: move 'extract' return to a dictionnary...
r26547 data = {}
Gregory Szorc
patch: make extract() a context manager (API)...
r37639
Yuya Nishihara
py3: ditch email.parser.BytesParser which appears to be plain crap...
r38354 msg = mail.parse(fileobj)
Vadim Gelfer
commands.import: refactor patch parsing into patch.extract.
r2866
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 subject = msg['Subject'] and mail.headdecode(msg['Subject'])
data[b'user'] = msg['From'] and mail.headdecode(msg['From'])
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if not subject and not data[b'user']:
Gregory Szorc
patch: make extract() a context manager (API)...
r37639 # Not an email, restore parsed headers if any
Augie Fackler
formatting: blacken the codebase...
r43346 subject = (
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'\n'.join(
b': '.join(map(encoding.strtolocal, h)) for h in msg.items()
Augie Fackler
formatting: blacken the codebase...
r43346 )
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 + b'\n'
Augie Fackler
formatting: blacken the codebase...
r43346 )
Patrick Mezard
patch: do not swallow header-like patch first line (issue1859)...
r9573
Gregory Szorc
patch: make extract() a context manager (API)...
r37639 # should try to parse msg['Date']
parents = []
Vadim Gelfer
commands.import: refactor patch parsing into patch.extract.
r2866
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 nodeid = msg['X-Mercurial-Node']
Denis Laxalde
import: read X-Mercurial-Node email header to determine nodeid...
r43367 if nodeid:
data[b'nodeid'] = nodeid = mail.headdecode(nodeid)
ui.debug(b'Node ID: %s\n' % nodeid)
Gregory Szorc
patch: make extract() a context manager (API)...
r37639 if subject:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if subject.startswith(b'[PATCH'):
pend = subject.find(b']')
Gregory Szorc
patch: make extract() a context manager (API)...
r37639 if pend >= 0:
Augie Fackler
formatting: blacken the codebase...
r43346 subject = subject[pend + 1 :].lstrip()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 subject = re.sub(br'\n[ \t]+', b' ', subject)
ui.debug(b'Subject: %s\n' % subject)
if data[b'user']:
ui.debug(b'From: %s\n' % data[b'user'])
Gregory Szorc
patch: make extract() a context manager (API)...
r37639 diffs_seen = 0
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ok_types = (b'text/plain', b'text/x-diff', b'text/x-patch')
message = b''
Gregory Szorc
patch: make extract() a context manager (API)...
r37639 for part in msg.walk():
content_type = pycompat.bytestr(part.get_content_type())
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.debug(b'Content-Type: %s\n' % content_type)
Gregory Szorc
patch: make extract() a context manager (API)...
r37639 if content_type not in ok_types:
continue
payload = part.get_payload(decode=True)
m = diffre.search(payload)
if m:
hgpatch = False
hgpatchheader = False
ignoretext = False
Brendan Cully
git-send-email compatibility: stop reading changelog after ^---$
r4220
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.debug(b'found patch at byte %d\n' % m.start(0))
Gregory Szorc
patch: make extract() a context manager (API)...
r37639 diffs_seen += 1
cfp = stringio()
Augie Fackler
formatting: blacken the codebase...
r43346 for line in payload[: m.start(0)].splitlines():
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if line.startswith(b'# HG changeset patch') and not hgpatch:
ui.debug(b'patch generated by hg export\n')
Gregory Szorc
patch: make extract() a context manager (API)...
r37639 hgpatch = True
hgpatchheader = True
# drop earlier commit message content
cfp.seek(0)
cfp.truncate()
subject = None
elif hgpatchheader:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if line.startswith(b'# User '):
data[b'user'] = line[7:]
ui.debug(b'From: %s\n' % data[b'user'])
elif line.startswith(b"# Parent "):
Gregory Szorc
patch: make extract() a context manager (API)...
r37639 parents.append(line[9:].lstrip())
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif line.startswith(b"# "):
Gregory Szorc
patch: make extract() a context manager (API)...
r37639 for header, key in patchheadermap:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 prefix = b'# %s ' % header
Gregory Szorc
patch: make extract() a context manager (API)...
r37639 if line.startswith(prefix):
Augie Fackler
formatting: blacken the codebase...
r43346 data[key] = line[len(prefix) :]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.debug(b'%s: %s\n' % (header, data[key]))
Gregory Szorc
patch: make extract() a context manager (API)...
r37639 else:
hgpatchheader = False
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif line == b'---':
Gregory Szorc
patch: make extract() a context manager (API)...
r37639 ignoretext = True
if not hgpatchheader and not ignoretext:
cfp.write(line)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 cfp.write(b'\n')
Gregory Szorc
patch: make extract() a context manager (API)...
r37639 message = cfp.getvalue()
if tmpfp:
tmpfp.write(payload)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if not payload.endswith(b'\n'):
tmpfp.write(b'\n')
elif not diffs_seen and message and content_type == b'text/plain':
message += b'\n' + payload
Vadim Gelfer
commands.import: refactor patch parsing into patch.extract.
r2866
Brendan Cully
patch.extract: do not prepend subject if the description already starts with it
r4777 if subject and not message.startswith(subject):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 message = b'%s\n%s' % (subject, message)
data[b'message'] = message
Vadim Gelfer
commands.import: refactor patch parsing into patch.extract.
r2866 tmpfp.close()
Jordi Gutiérrez Hermoso
style: kill ersatz if-else ternary operators...
r24306 if parents:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 data[b'p1'] = parents.pop(0)
Pierre-Yves David
extract: simplify parents assignement...
r26548 if parents:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 data[b'p2'] = parents.pop(0)
Jordi Gutiérrez Hermoso
style: kill ersatz if-else ternary operators...
r24306
Pierre-Yves David
extract: use a single return...
r26555 if diffs_seen:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 data[b'filename'] = tmpname
Gregory Szorc
patch: make extract() a context manager (API)...
r37639
Pierre-Yves David
patch: move 'extract' return to a dictionnary...
r26547 return data
Brendan Cully
Move patch-related code into its own module.
r2861
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class patchmeta:
Patrick Mezard
patch: extract and rename gitpatch into patchmeta, document
r7148 """Patched file metadata
'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
or COPY. 'path' is patched file path. 'oldpath' is set to the
Patrick Mezard
patch: patchmeta gives (islink, isexec) tuple instead of int mode
r7149 origin file when 'op' is either COPY or RENAME, None otherwise. If
file mode is changed, 'mode' is a tuple (islink, isexec) where
'islink' is True if the file is a symlink and 'isexec' is True if
the file is executable. Otherwise, 'mode' is None.
Patrick Mezard
patch: extract and rename gitpatch into patchmeta, document
r7148 """
Augie Fackler
formatting: blacken the codebase...
r43346
Patrick Mezard
patch: extract and rename gitpatch into patchmeta, document
r7148 def __init__(self, path):
self.path = path
self.oldpath = None
self.mode = None
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self.op = b'MODIFY'
Patrick Mezard
patch: extract and rename gitpatch into patchmeta, document
r7148 self.binary = False
Patrick Mezard
patch: patchmeta gives (islink, isexec) tuple instead of int mode
r7149 def setmode(self, mode):
Gregory Szorc
global: mass rewrite to use modern octal syntax...
r25658 islink = mode & 0o20000
isexec = mode & 0o100
Patrick Mezard
patch: patchmeta gives (islink, isexec) tuple instead of int mode
r7149 self.mode = (islink, isexec)
Patrick Mezard
patch: generalize the use of patchmeta in applydiff()...
r14566 def copy(self):
other = patchmeta(self.path)
other.oldpath = self.oldpath
other.mode = self.mode
other.op = self.op
other.binary = self.binary
return other
Patrick Mezard
patch: fix patch hunk/metdata synchronization (issue3384)...
r16506 def _ispatchinga(self, afile):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if afile == b'/dev/null':
return self.op == b'ADD'
return afile == b'a/' + (self.oldpath or self.path)
Patrick Mezard
patch: fix patch hunk/metdata synchronization (issue3384)...
r16506
def _ispatchingb(self, bfile):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if bfile == b'/dev/null':
return self.op == b'DELETE'
return bfile == b'b/' + self.path
Patrick Mezard
patch: fix patch hunk/metdata synchronization (issue3384)...
r16506
def ispatching(self, afile, bfile):
return self._ispatchinga(afile) and self._ispatchingb(bfile)
Mads Kiilerich
patch: descriptive patchmeta.__repr__ to help debugging
r11018 def __repr__(self):
Augie Fackler
cleanup: remove pointless r-prefixes on double-quoted strings...
r43809 return "<patchmeta %s %r>" % (self.op, self.path)
Mads Kiilerich
patch: descriptive patchmeta.__repr__ to help debugging
r11018
Augie Fackler
formatting: blacken the codebase...
r43346
Patrick Mezard
patch: pass linereader to scangitpatch(), extract from iterhunks()...
r7152 def readgitpatch(lr):
Brendan Cully
Move patch-related code into its own module.
r2861 """extract git-style metadata about patches from <patchname>"""
Thomas Arendsen Hein
Whitespace/Tab cleanup
r3223
Brendan Cully
Move patch-related code into its own module.
r2861 # Filter patch for git information
gp = None
gitpatches = []
Patrick Mezard
patch: pass linereader to scangitpatch(), extract from iterhunks()...
r7152 for line in lr:
Kyle Lippincott
patch: handle filenames with trailing spaces...
r46863 line = line.rstrip(b'\r\n')
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if line.startswith(b'diff --git a/'):
Brendan Cully
Move patch-related code into its own module.
r2861 m = gitre.match(line)
if m:
if gp:
gitpatches.append(gp)
Nicolas Dumazet
patch: readgitpatch: remove unused variable 'src'
r9392 dst = m.group(2)
Patrick Mezard
patch: extract and rename gitpatch into patchmeta, document
r7148 gp = patchmeta(dst)
Brendan Cully
Move patch-related code into its own module.
r2861 elif gp:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if line.startswith(b'--- '):
Brendan Cully
Move patch-related code into its own module.
r2861 gitpatches.append(gp)
gp = None
continue
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if line.startswith(b'rename from '):
gp.op = b'RENAME'
Bill Barry
fix issue 1763: strip chars from end of line when parsing gitpatch lines
r9243 gp.oldpath = line[12:]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif line.startswith(b'rename to '):
Bill Barry
fix issue 1763: strip chars from end of line when parsing gitpatch lines
r9243 gp.path = line[10:]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif line.startswith(b'copy from '):
gp.op = b'COPY'
Bill Barry
fix issue 1763: strip chars from end of line when parsing gitpatch lines
r9243 gp.oldpath = line[10:]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif line.startswith(b'copy to '):
Bill Barry
fix issue 1763: strip chars from end of line when parsing gitpatch lines
r9243 gp.path = line[8:]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif line.startswith(b'deleted file'):
gp.op = b'DELETE'
elif line.startswith(b'new file mode '):
gp.op = b'ADD'
Bill Barry
fix issue 1763: strip chars from end of line when parsing gitpatch lines
r9243 gp.setmode(int(line[-6:], 8))
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif line.startswith(b'new mode '):
Bill Barry
fix issue 1763: strip chars from end of line when parsing gitpatch lines
r9243 gp.setmode(int(line[-6:], 8))
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif line.startswith(b'GIT binary patch'):
Brendan Cully
Add git-1.4 binary patch support
r3367 gp.binary = True
Brendan Cully
Move patch-related code into its own module.
r2861 if gp:
gitpatches.append(gp)
Patrick Mezard
patch: remove unused flags from readgitpatch()
r12669 return gitpatches
Brendan Cully
Move patch-related code into its own module.
r2861
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class linereader:
Patrick Mezard
Add patch.eol to ignore EOLs when patching (issue1019)...
r8810 # simple class to allow pushing lines back into the input stream
Patrick Mezard
patch: remove EOL support from linereader class...
r14418 def __init__(self, fp):
Patrick Mezard
Add patch.eol to ignore EOLs when patching (issue1019)...
r8810 self.fp = fp
self.buf = []
def push(self, line):
if line is not None:
self.buf.append(line)
def readline(self):
if self.buf:
l = self.buf[0]
del self.buf[0]
return l
Patrick Mezard
patch: remove EOL support from linereader class...
r14418 return self.fp.readline()
Patrick Mezard
Add patch.eol to ignore EOLs when patching (issue1019)...
r8810
def __iter__(self):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return iter(self.readline, b'')
Patrick Mezard
Add patch.eol to ignore EOLs when patching (issue1019)...
r8810
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class abstractbackend:
Patrick Mezard
patch: extract fs access from patchfile into fsbackend...
r14348 def __init__(self, ui):
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 self.ui = ui
Patrick Mezard
patch: extract fs access from patchfile into fsbackend...
r14348
Patrick Mezard
patch: unify backend file access interface...
r14391 def getfile(self, fname):
"""Return target file data and flags as a (data, (islink,
Mads Kiilerich
convert: use None value for missing files instead of overloading IOError...
r22296 isexec)) tuple. Data is None if file is missing/deleted.
Patrick Mezard
patch: extract fs access from patchfile into fsbackend...
r14348 """
raise NotImplementedError
Patrick Mezard
patch: use temporary files to handle intermediate copies...
r14452 def setfile(self, fname, data, mode, copysource):
Patrick Mezard
patch: unify backend file access interface...
r14391 """Write data to target file fname and set its mode. mode is a
(islink, isexec) tuple. If data is None, the file content should
Patrick Mezard
patch: use temporary files to handle intermediate copies...
r14452 be left unchanged. If the file is modified after being copied,
copysource is set to the original file name.
Patrick Mezard
patch: set desired mode when patching, not in updatedir()...
r14367 """
Patrick Mezard
patch: extract fs access from patchfile into fsbackend...
r14348 raise NotImplementedError
Patrick Mezard
patch: write rej files for missing targets (issue 853)
r5652
Patrick Mezard
patch: extract fs access from patchfile into fsbackend...
r14348 def unlink(self, fname):
"""Unlink target file."""
raise NotImplementedError
def writerej(self, fname, failed, total, lines):
"""Write rejected lines for fname. total is the number of hunks
which failed to apply and total the total number of hunks for this
files.
"""
Patrick Mezard
patch: add lexists() to backends, use it in selectfile()...
r14351 def exists(self, fname):
raise NotImplementedError
Martin von Zweigbergk
patch: add close() to abstractbackend...
r33156 def close(self):
raise NotImplementedError
Augie Fackler
formatting: blacken the codebase...
r43346
Patrick Mezard
patch: extract fs access from patchfile into fsbackend...
r14348 class fsbackend(abstractbackend):
Patrick Mezard
patch: move copyfile() into backends, abstract basedir
r14350 def __init__(self, ui, basedir):
Patrick Mezard
patch: extract fs access from patchfile into fsbackend...
r14348 super(fsbackend, self).__init__(ui)
Pierre-Yves David
vfs: use 'vfs' module directly in 'mercurial.patch'...
r31233 self.opener = vfsmod.vfs(basedir)
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897
Patrick Mezard
patch: unify backend file access interface...
r14391 def getfile(self, fname):
Chinmay Joshi
patch: replace functions in fsbackend to use vfs...
r21717 if self.opener.islink(fname):
return (self.opener.readlink(fname), (True, False))
Patrick Mezard
patch: remove redundant islink() call
r14531 isexec = False
Patrick Mezard
patch: isolate patchfile filesystem calls into methods...
r7392 try:
Gregory Szorc
global: mass rewrite to use modern octal syntax...
r25658 isexec = self.opener.lstat(fname).st_mode & 0o100 != 0
Manuel Jacob
py3: catch FileNotFoundError instead of checking errno == ENOENT
r50201 except FileNotFoundError:
pass
Mads Kiilerich
convert: use None value for missing files instead of overloading IOError...
r22296 try:
return (self.opener.read(fname), (False, isexec))
Manuel Jacob
py3: catch FileNotFoundError instead of checking errno == ENOENT
r50201 except FileNotFoundError:
Mads Kiilerich
convert: use None value for missing files instead of overloading IOError...
r22296 return None, None
Patrick Mezard
patch: isolate patchfile filesystem calls into methods...
r7392
Patrick Mezard
patch: use temporary files to handle intermediate copies...
r14452 def setfile(self, fname, data, mode, copysource):
Patrick Mezard
patch: unify backend file access interface...
r14391 islink, isexec = mode
if data is None:
Chinmay Joshi
patch: replace functions in fsbackend to use vfs...
r21717 self.opener.setflags(fname, islink, isexec)
Patrick Mezard
patch: merge backend setmode() into writelines()...
r14390 return
Patrick Mezard
patch: unify backend file access interface...
r14391 if islink:
self.opener.symlink(data, fname)
Patrick Mezard
patch: set desired mode when patching, not in updatedir()...
r14367 else:
Patrick Mezard
patch: unify backend file access interface...
r14391 self.opener.write(fname, data)
Patrick Mezard
patch: set desired mode when patching, not in updatedir()...
r14367 if isexec:
Chinmay Joshi
patch: replace functions in fsbackend to use vfs...
r21717 self.opener.setflags(fname, False, True)
Patrick Mezard
patch: isolate patchfile filesystem calls into methods...
r7392
def unlink(self, fname):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 rmdir = self.ui.configbool(b'experimental', b'removeemptydirs')
Kyle Lippincott
unlinkpath: make empty directory removal optional (issue5901) (issue5826)...
r38512 self.opener.unlinkpath(fname, ignoremissing=True, rmdir=rmdir)
Patrick Mezard
patch: isolate patchfile filesystem calls into methods...
r7392
Patrick Mezard
patch: extract fs access from patchfile into fsbackend...
r14348 def writerej(self, fname, failed, total, lines):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fname = fname + b".rej"
Patrick Mezard
patch: extract fs access from patchfile into fsbackend...
r14348 self.ui.warn(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b"%d out of %d hunks FAILED -- saving rejects to file %s\n")
Augie Fackler
formatting: blacken the codebase...
r43346 % (failed, total, fname)
)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fp = self.opener(fname, b'w')
Patrick Mezard
patch: extract fs access from patchfile into fsbackend...
r14348 fp.writelines(lines)
fp.close()
Patrick Mezard
patch: add lexists() to backends, use it in selectfile()...
r14351 def exists(self, fname):
Chinmay Joshi
patch: replace functions in fsbackend to use vfs...
r21717 return self.opener.lexists(fname)
Patrick Mezard
patch: add lexists() to backends, use it in selectfile()...
r14351
Augie Fackler
formatting: blacken the codebase...
r43346
Patrick Mezard
patch: add a workingbackend dirstate layer on top of fsbackend...
r14370 class workingbackend(fsbackend):
def __init__(self, ui, repo, similarity):
super(workingbackend, self).__init__(ui, repo.root)
self.repo = repo
self.similarity = similarity
self.removed = set()
self.changed = set()
self.copied = []
Patrick Mezard
patch: do not patch unknown files (issue752)
r14453 def _checkknown(self, fname):
dirstate-item: use `any_tracked` instead of `state` to apply patches...
r48911 if not self.repo.dirstate.get_entry(fname).any_tracked and self.exists(
fname
):
Martin von Zweigbergk
errors: return more detailed errors when failing to parse or apply patch...
r49186 raise PatchApplicationError(
_(b'cannot patch %s: file is not tracked') % fname
)
Patrick Mezard
patch: do not patch unknown files (issue752)
r14453
Patrick Mezard
patch: use temporary files to handle intermediate copies...
r14452 def setfile(self, fname, data, mode, copysource):
Patrick Mezard
patch: do not patch unknown files (issue752)
r14453 self._checkknown(fname)
Patrick Mezard
patch: use temporary files to handle intermediate copies...
r14452 super(workingbackend, self).setfile(fname, data, mode, copysource)
if copysource is not None:
self.copied.append((copysource, fname))
Patrick Mezard
patch: add a workingbackend dirstate layer on top of fsbackend...
r14370 self.changed.add(fname)
def unlink(self, fname):
Patrick Mezard
patch: do not patch unknown files (issue752)
r14453 self._checkknown(fname)
Patrick Mezard
patch: add a workingbackend dirstate layer on top of fsbackend...
r14370 super(workingbackend, self).unlink(fname)
self.removed.add(fname)
self.changed.add(fname)
def close(self):
dirstate: use `dirstate.change_files` to scope the change in `import`...
r50939 with self.repo.dirstate.changing_files(self.repo):
wctx = self.repo[None]
changed = set(self.changed)
for src, dst in self.copied:
scmutil.dirstatecopy(self.ui, self.repo, wctx, src, dst)
if self.removed:
wctx.forget(sorted(self.removed))
for f in self.removed:
if f not in self.repo.dirstate:
# File was deleted and no longer belongs to the
# dirstate, it was probably marked added then
# deleted, and should not be considered by
# marktouched().
changed.discard(f)
if changed:
scmutil.marktouched(self.repo, changed, self.similarity)
return sorted(self.changed)
Patrick Mezard
patch: add a workingbackend dirstate layer on top of fsbackend...
r14370
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class filestore:
Patrick Mezard
patch: make filestore store data in memory and fallback to fs
r14658 def __init__(self, maxsize=None):
Patrick Mezard
patch: use temporary files to handle intermediate copies...
r14452 self.opener = None
self.files = {}
self.created = 0
Patrick Mezard
patch: make filestore store data in memory and fallback to fs
r14658 self.maxsize = maxsize
if self.maxsize is None:
Raphaël Gomès
black: format the codebase with 23.3.0...
r52596 self.maxsize = 4 * (2**20)
Patrick Mezard
patch: make filestore store data in memory and fallback to fs
r14658 self.size = 0
self.data = {}
Patrick Mezard
patch: use temporary files to handle intermediate copies...
r14452
Patrick Mezard
patch: extend filtestore to store an optional copy source...
r14609 def setfile(self, fname, data, mode, copied=None):
Patrick Mezard
patch: make filestore store data in memory and fallback to fs
r14658 if self.maxsize < 0 or (len(data) + self.size) <= self.maxsize:
self.data[fname] = (data, mode, copied)
self.size += len(data)
else:
if self.opener is None:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 root = pycompat.mkdtemp(prefix=b'hg-patch-')
Pierre-Yves David
vfs: use 'vfs' module directly in 'mercurial.patch'...
r31233 self.opener = vfsmod.vfs(root)
Patrick Mezard
patch: make filestore store data in memory and fallback to fs
r14658 # Avoid filename issues with these simple names
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fn = b'%d' % self.created
Patrick Mezard
patch: make filestore store data in memory and fallback to fs
r14658 self.opener.write(fn, data)
self.created += 1
self.files[fname] = (fn, mode, copied)
Patrick Mezard
patch: use temporary files to handle intermediate copies...
r14452
def getfile(self, fname):
Patrick Mezard
patch: make filestore store data in memory and fallback to fs
r14658 if fname in self.data:
return self.data[fname]
if not self.opener or fname not in self.files:
Mads Kiilerich
convert: use None value for missing files instead of overloading IOError...
r22296 return None, None, None
Patrick Mezard
patch: extend filtestore to store an optional copy source...
r14609 fn, mode, copied = self.files[fname]
return self.opener.read(fn), mode, copied
Patrick Mezard
patch: use temporary files to handle intermediate copies...
r14452
def close(self):
if self.opener:
shutil.rmtree(self.opener.base)
Augie Fackler
formatting: blacken the codebase...
r43346
Patrick Mezard
import: add --bypass option...
r14611 class repobackend(abstractbackend):
def __init__(self, ui, repo, ctx, store):
super(repobackend, self).__init__(ui)
self.repo = repo
self.ctx = ctx
self.store = store
self.changed = set()
self.removed = set()
self.copied = {}
def _checkknown(self, fname):
if fname not in self.ctx:
Martin von Zweigbergk
errors: return more detailed errors when failing to parse or apply patch...
r49186 raise PatchApplicationError(
_(b'cannot patch %s: file is not tracked') % fname
)
Patrick Mezard
import: add --bypass option...
r14611
def getfile(self, fname):
try:
fctx = self.ctx[fname]
except error.LookupError:
Mads Kiilerich
convert: use None value for missing files instead of overloading IOError...
r22296 return None, None
Patrick Mezard
import: add --bypass option...
r14611 flags = fctx.flags()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return fctx.data(), (b'l' in flags, b'x' in flags)
Patrick Mezard
import: add --bypass option...
r14611
def setfile(self, fname, data, mode, copysource):
if copysource:
self._checkknown(copysource)
if data is None:
data = self.ctx[fname].data()
self.store.setfile(fname, data, mode, copysource)
self.changed.add(fname)
if copysource:
self.copied[fname] = copysource
def unlink(self, fname):
self._checkknown(fname)
self.removed.add(fname)
def exists(self, fname):
return fname in self.ctx
def close(self):
return self.changed | self.removed
Augie Fackler
formatting: blacken the codebase...
r43346
Patrick Mezard
patch: extract fs access from patchfile into fsbackend...
r14348 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
Gregory Szorc
global: use raw strings for regular expressions with escapes...
r41673 unidesc = re.compile(br'@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@')
contextdesc = re.compile(br'(?:---|\*\*\*) (\d+)(?:,(\d+))? (?:---|\*\*\*)')
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 eolmodes = [b'strict', b'crlf', b'lf', b'auto']
Patrick Mezard
patch: extract fs access from patchfile into fsbackend...
r14348
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class patchfile:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 def __init__(self, ui, gp, backend, store, eolmode=b'strict'):
Patrick Mezard
patch: generalize the use of patchmeta in applydiff()...
r14566 self.fname = gp.path
Patrick Mezard
patch: extract fs access from patchfile into fsbackend...
r14348 self.eolmode = eolmode
self.eol = None
self.backend = backend
self.ui = ui
self.lines = []
self.exists = False
Patrick Mezard
patch: use temporary files to handle intermediate copies...
r14452 self.missing = True
Patrick Mezard
patch: generalize the use of patchmeta in applydiff()...
r14566 self.mode = gp.mode
self.copysource = gp.oldpath
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self.create = gp.op in (b'ADD', b'COPY', b'RENAME')
self.remove = gp.op == b'DELETE'
Mads Kiilerich
convert: use None value for missing files instead of overloading IOError...
r22296 if self.copysource is None:
data, mode = backend.getfile(self.fname)
else:
data, mode = store.getfile(self.copysource)[:2]
if data is not None:
self.exists = self.copysource is None or backend.exists(self.fname)
Patrick Mezard
patch: use temporary files to handle intermediate copies...
r14452 self.missing = False
if data:
Wagner Bruna
patch: fix parsing patch files containing CRs not followed by LFs...
r14832 self.lines = mdiff.splitnewlines(data)
Patrick Mezard
patch: use temporary files to handle intermediate copies...
r14452 if self.mode is None:
self.mode = mode
if self.lines:
# Normalize line endings
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if self.lines[0].endswith(b'\r\n'):
self.eol = b'\r\n'
elif self.lines[0].endswith(b'\n'):
self.eol = b'\n'
if eolmode != b'strict':
Patrick Mezard
patch: use temporary files to handle intermediate copies...
r14452 nlines = []
for l in self.lines:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if l.endswith(b'\r\n'):
l = l[:-2] + b'\n'
Patrick Mezard
patch: use temporary files to handle intermediate copies...
r14452 nlines.append(l)
self.lines = nlines
Mads Kiilerich
convert: use None value for missing files instead of overloading IOError...
r22296 else:
Patrick Mezard
patch: generalize the use of patchmeta in applydiff()...
r14566 if self.create:
Patrick Mezard
patch: use temporary files to handle intermediate copies...
r14452 self.missing = False
if self.mode is None:
self.mode = (False, False)
if self.missing:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self.ui.warn(_(b"unable to find '%s' for patching\n") % self.fname)
Augie Fackler
formatting: blacken the codebase...
r43346 self.ui.warn(
_(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b"(use '--prefix' to apply patch relative to the "
b"current directory)\n"
Augie Fackler
formatting: blacken the codebase...
r43346 )
)
Patrick Mezard
patch: extract fs access from patchfile into fsbackend...
r14348
self.hash = {}
self.dirty = 0
self.offset = 0
self.skew = 0
self.rej = []
self.fileprinted = False
self.printfile(False)
self.hunks = 0
Patrick Mezard
patch: set desired mode when patching, not in updatedir()...
r14367 def writelines(self, fname, lines, mode):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if self.eolmode == b'auto':
Patrick Mezard
patch: extract fs access from patchfile into fsbackend...
r14348 eol = self.eol
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif self.eolmode == b'crlf':
eol = b'\r\n'
Patrick Mezard
patch: extract fs access from patchfile into fsbackend...
r14348 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 eol = b'\n'
if self.eolmode != b'strict' and eol and eol != b'\n':
Patrick Mezard
patch: extract fs access from patchfile into fsbackend...
r14348 rawlines = []
for l in lines:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if l and l.endswith(b'\n'):
Patrick Mezard
patch: extract fs access from patchfile into fsbackend...
r14348 l = l[:-1] + eol
rawlines.append(l)
lines = rawlines
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self.backend.setfile(fname, b''.join(lines), mode, self.copysource)
Patrick Mezard
patch: extract fs access from patchfile into fsbackend...
r14348
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 def printfile(self, warn):
if self.fileprinted:
return
if warn or self.ui.verbose:
self.fileprinted = True
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 s = _(b"patching file %s\n") % self.fname
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 if warn:
self.ui.warn(s)
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
# from linenum
Thomas Arendsen Hein
Remove trailing spaces, fix indentation
r5143
Benoit Boissinot
patch: simplify logic
r9681 cand = self.hash.get(l, [])
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 if len(cand) > 1:
# resort our list of potentials forward then back.
Alejandro Santos
compat: use 'key' argument instead of 'cmp' when sorting a list
r9032 cand.sort(key=lambda x: abs(x - linenum))
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 return cand
def write_rej(self):
# our rejects are a little different from patch(1). This always
# creates rejects in the same form as the original patch. A file
# header is inserted so that you can run the reject through patch again
# without having to type the filename.
if not self.rej:
return
Patrick Mezard
patch: merge makerejlines() into write_rej()
r14349 base = os.path.basename(self.fname)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 lines = [b"--- %s\n+++ %s\n" % (base, base)]
Patrick Mezard
patch: merge makerejlines() into write_rej()
r14349 for x in self.rej:
for l in x.hunk:
lines.append(l)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if l[-1:] != b'\n':
Rodrigo Damazio Bovendorp
diff: move no-eol text constant to a common location...
r45714 lines.append(b'\n' + diffhelper.MISSING_NEWLINE_MARKER)
Patrick Mezard
patch: merge makerejlines() into write_rej()
r14349 self.backend.writerej(self.fname, len(self.rej), self.hunks, lines)
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897
Nicolas Dumazet
patch: remove the unused, broken reverse() function
r9393 def apply(self, h):
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 if not h.complete():
Martin von Zweigbergk
errors: return more detailed errors when failing to parse or apply patch...
r49186 raise PatchParseError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b"bad hunk #%d %s (%d %d %d %d)")
Augie Fackler
formatting: blacken the codebase...
r43346 % (h.number, h.desc, len(h.a), h.lena, len(h.b), h.lenb)
)
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897
self.hunks += 1
Patrick Mezard
patch: write rej files for missing targets (issue 853)
r5652 if self.missing:
self.rej.append(h)
return -1
Patrick Mezard
patch: refactor file creation/removal detection...
r14451 if self.exists and self.create:
Patrick Mezard
patch: use temporary files to handle intermediate copies...
r14452 if self.copysource:
Augie Fackler
formatting: blacken the codebase...
r43346 self.ui.warn(
Martin von Zweigbergk
cleanup: join string literals that are already on one line...
r43387 _(b"cannot create %s: destination already exists\n")
Augie Fackler
formatting: blacken the codebase...
r43346 % self.fname
)
Patrick Mezard
patch: use temporary files to handle intermediate copies...
r14452 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self.ui.warn(_(b"file %s already exists\n") % self.fname)
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 self.rej.append(h)
return -1
Patrick Mezard
patch: handle symlinks without symlinkhunk...
r9585 if isinstance(h, binhunk):
Patrick Mezard
patch: refactor file creation/removal detection...
r14451 if self.remove:
Patrick Mezard
patch: extract fs access from patchfile into fsbackend...
r14348 self.backend.unlink(self.fname)
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 else:
Nicolas Vigier
patch: add support for git delta hunks...
r20137 l = h.new(self.lines)
self.lines[:] = l
self.offset += len(l)
Martin Geisler
patchfile: use real Booleans instead of 0/1
r14217 self.dirty = True
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 return 0
Patrick Mezard
patch: fix eolmode=auto with new files...
r10127 horig = h
Augie Fackler
formatting: blacken the codebase...
r43346 if (
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self.eolmode in (b'crlf', b'lf')
or self.eolmode == b'auto'
Augie Fackler
formatting: blacken the codebase...
r43346 and self.eol
):
Patrick Mezard
patch: drop eol normalization fast-path for 'lf' and 'crlf'...
r10128 # If new eols are going to be normalized, then normalize
# hunk data before patching. Otherwise, preserve input
# line-endings.
Patrick Mezard
patch: fix eolmode=auto with new files...
r10127 h = h.getnormalized()
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 # fast case first, no offsets, no fuzz
Patrick Mezard
patch: make hunk.fuzzit() compute the fuzzed start locations...
r16122 old, oldstart, new, newstart = h.fuzzit(0, False)
oldstart += self.offset
orig_start = oldstart
Greg Onufer
patch: better handling of sequence of offset patch hunks (issue1941)...
r10135 # if there's skew we want to emit the "(offset %d lines)" even
# when the hunk cleanly applies at start + skew, so skip the
# fast case code
Yuya Nishihara
diffhelper: rename module to avoid conflicts with ancient C module (issue5846)...
r37821 if self.skew == 0 and diffhelper.testhunk(old, self.lines, oldstart):
Patrick Mezard
patch: refactor file creation/removal detection...
r14451 if self.remove:
Patrick Mezard
patch: extract fs access from patchfile into fsbackend...
r14348 self.backend.unlink(self.fname)
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 else:
Augie Fackler
formatting: blacken the codebase...
r43346 self.lines[oldstart : oldstart + len(old)] = new
Patrick Mezard
patch: make hunk.fuzzit() compute the fuzzed start locations...
r16122 self.offset += len(new) - len(old)
Martin Geisler
patchfile: use real Booleans instead of 0/1
r14217 self.dirty = True
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 return 0
Patrick Mezard
patch: inline patchfile.hashlines()
r13700 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
self.hash = {}
for x, s in enumerate(self.lines):
self.hash.setdefault(s, []).append(x)
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897
Manuel Jacob
py3: replace `pycompat.xrange` by `range`
r50179 for fuzzlen in range(self.ui.configint(b"patch", b"fuzz") + 1):
Matt Mackall
many, many trivial check-code fixups
r10282 for toponly in [True, False]:
Patrick Mezard
patch: make hunk.fuzzit() compute the fuzzed start locations...
r16122 old, oldstart, new, newstart = h.fuzzit(fuzzlen, toponly)
Patrick Mezard
patch: fix fuzzing of hunks without previous lines (issue3264)...
r16123 oldstart = oldstart + self.offset + self.skew
oldstart = min(oldstart, len(self.lines))
if old:
cand = self.findlines(old[0][1:], oldstart)
else:
# Only adding lines with no or fuzzed context, just
# take the skew in account
cand = [oldstart]
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897
for l in cand:
Yuya Nishihara
diffhelper: rename module to avoid conflicts with ancient C module (issue5846)...
r37821 if not old or diffhelper.testhunk(old, self.lines, l):
Patrick Mezard
patch: fuzz old and new lines at the same time...
r16121 self.lines[l : l + len(old)] = new
self.offset += len(new) - len(old)
Greg Onufer
patch: better handling of sequence of offset patch hunks (issue1941)...
r10135 self.skew = l - orig_start
Martin Geisler
patchfile: use real Booleans instead of 0/1
r14217 self.dirty = True
Wagner Bruna
patch, i18n: avoid parameterized messages...
r10518 offset = l - orig_start - fuzzlen
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 if fuzzlen:
Augie Fackler
formatting: blacken the codebase...
r43346 msg = _(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b"Hunk #%d succeeded at %d "
b"with fuzz %d "
b"(offset %d lines).\n"
Augie Fackler
formatting: blacken the codebase...
r43346 )
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 self.printfile(True)
Augie Fackler
formatting: blacken the codebase...
r43346 self.ui.warn(
msg % (h.number, l + 1, fuzzlen, offset)
)
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 else:
Augie Fackler
formatting: blacken the codebase...
r43346 msg = _(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b"Hunk #%d succeeded at %d "
b"(offset %d lines).\n"
Augie Fackler
formatting: blacken the codebase...
r43346 )
Wagner Bruna
patch, i18n: avoid parameterized messages...
r10518 self.ui.note(msg % (h.number, l + 1, offset))
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 return fuzzlen
self.printfile(True)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self.ui.warn(_(b"Hunk #%d FAILED at %d\n") % (h.number, orig_start))
Patrick Mezard
patch: fix eolmode=auto with new files...
r10127 self.rej.append(horig)
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 return -1
Patrick Mezard
patch: move closefile() into patchfile.close()
r13701 def close(self):
if self.dirty:
Patrick Mezard
patch: set desired mode when patching, not in updatedir()...
r14367 self.writelines(self.fname, self.lines, self.mode)
Patrick Mezard
patch: move closefile() into patchfile.close()
r13701 self.write_rej()
return len(self.rej)
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class header:
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """patch header"""
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 diffgit_re = re.compile(b'diff --git a/(.*) b/(.*)$')
diff_re = re.compile(b'diff -r .* (.*)$')
allhunks_re = re.compile(b'(?:index|deleted file) ')
pretty_re = re.compile(b'(?:new file|deleted file) ')
special_re = re.compile(b'(?:index|deleted|copy|rename|new mode) ')
newfile_re = re.compile(b'(?:new file|copy to|rename to)')
Laurent Charignon
record: move header class from record to patch...
r24261
def __init__(self, header):
self.header = header
self.hunks = []
def binary(self):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return any(h.startswith(b'index ') for h in self.header)
Laurent Charignon
record: move header class from record to patch...
r24261
def pretty(self, fp):
for h in self.header:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if h.startswith(b'index '):
fp.write(_(b'this modifies a binary file (all or nothing)\n'))
Laurent Charignon
record: move header class from record to patch...
r24261 break
if self.pretty_re.match(h):
fp.write(h)
if self.binary():
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fp.write(_(b'this is a binary file\n'))
Laurent Charignon
record: move header class from record to patch...
r24261 break
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if h.startswith(b'---'):
Augie Fackler
formatting: blacken the codebase...
r43346 fp.write(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'%d hunks, %d lines changed\n')
Augie Fackler
formatting: blacken the codebase...
r43346 % (
len(self.hunks),
sum([max(h.added, h.removed) for h in self.hunks]),
)
)
Laurent Charignon
record: move header class from record to patch...
r24261 break
fp.write(h)
def write(self, fp):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fp.write(b''.join(self.header))
Laurent Charignon
record: move header class from record to patch...
r24261
def allhunks(self):
Augie Fackler
cleanup: use __builtins__.any instead of util.any...
r25149 return any(self.allhunks_re.match(h) for h in self.header)
Laurent Charignon
record: move header class from record to patch...
r24261
def files(self):
match = self.diffgit_re.match(self.header[0])
if match:
fromfile, tofile = match.groups()
if fromfile == tofile:
return [fromfile]
return [fromfile, tofile]
else:
return self.diff_re.match(self.header[0]).groups()
def filename(self):
return self.files()[-1]
def __repr__(self):
Matt Harbison
patch: fix header.__repr__() to not have `b''` prefixes in file names...
r44396 return '<header %s>' % (
' '.join(pycompat.rapply(pycompat.fsdecode, self.files()))
)
Laurent Charignon
record: move header class from record to patch...
r24261
Laurent Charignon
record: edit patch of newly added files (issue4304)...
r24845 def isnewfile(self):
Augie Fackler
cleanup: use __builtins__.any instead of util.any...
r25149 return any(self.newfile_re.match(h) for h in self.header)
Laurent Charignon
record: edit patch of newly added files (issue4304)...
r24845
Laurent Charignon
record: move header class from record to patch...
r24261 def special(self):
Laurent Charignon
record: edit patch of newly added files (issue4304)...
r24845 # Special files are shown only at the header level and not at the hunk
# level for example a file that has been deleted is a special file.
# The user cannot change the content of the operation, in the case of
# the deleted file he has to take the deletion or not take it, he
# cannot take some of it.
# Newly added files are special if they are empty, they are not special
# if they have some content as we want to be able to change it
nocontent = len(self.header) == 2
emptynewfile = self.isnewfile() and nocontent
Augie Fackler
formatting: blacken the codebase...
r43346 return emptynewfile or any(
self.special_re.match(h) for h in self.header
)
Laurent Charignon
record: move header class from record to patch...
r24261
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class recordhunk:
Laurent Charignon
record: move hunk class from record to patch...
r24263 """patch hunk
XXX shouldn't we merge this with the other hunk class?
"""
Augie Fackler
formatting: blacken the codebase...
r43346 def __init__(
self,
header,
fromline,
toline,
proc,
before,
hunk,
after,
maxcontext=None,
):
Jun Wu
patch: make parsepatch optionally trim context lines...
r33270 def trimcontext(lines, reverse=False):
if maxcontext is not None:
delta = len(lines) - maxcontext
if delta > 0:
if reverse:
return delta, lines[delta:]
else:
return delta, lines[:maxcontext]
return 0, lines
Laurent Charignon
record: move hunk class from record to patch...
r24263
self.header = header
Jun Wu
patch: make parsepatch optionally trim context lines...
r33270 trimedbefore, self.before = trimcontext(before, True)
self.fromline = fromline + trimedbefore
self.toline = toline + trimedbefore
_trimedafter, self.after = trimcontext(after, False)
Laurent Charignon
record: move hunk class from record to patch...
r24263 self.proc = proc
self.hunk = hunk
self.added, self.removed = self.countchanges(self.hunk)
Laurent Charignon
record: add comparison methods for recordhunk class
r24346 def __eq__(self, v):
if not isinstance(v, recordhunk):
return False
Augie Fackler
formatting: blacken the codebase...
r43346 return (
(v.hunk == self.hunk)
and (v.proc == self.proc)
and (self.fromline == v.fromline)
and (self.header.files() == v.header.files())
)
Laurent Charignon
record: add comparison methods for recordhunk class
r24346
def __hash__(self):
Augie Fackler
formatting: blacken the codebase...
r43346 return hash(
(
tuple(self.hunk),
tuple(self.header.files()),
self.fromline,
self.proc,
)
)
Laurent Charignon
record: add comparison methods for recordhunk class
r24346
Laurent Charignon
record: move hunk class from record to patch...
r24263 def countchanges(self, hunk):
"""hunk -> (n+,n-)"""
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 add = len([h for h in hunk if h.startswith(b'+')])
rem = len([h for h in hunk if h.startswith(b'-')])
Laurent Charignon
record: move hunk class from record to patch...
r24263 return add, rem
Jun Wu
patch: rewrite reversehunks (issue5337)...
r32979 def reversehunk(self):
"""return another recordhunk which is the reverse of the hunk
If this hunk is diff(A, B), the returned hunk is diff(B, A). To do
that, swap fromline/toline and +/- signs while keep other things
unchanged.
"""
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 m = {b'+': b'-', b'-': b'+', b'\\': b'\\'}
hunk = [b'%s%s' % (m[l[0:1]], l[1:]) for l in self.hunk]
Augie Fackler
formatting: blacken the codebase...
r43346 return recordhunk(
self.header,
self.toline,
self.fromline,
self.proc,
self.before,
hunk,
self.after,
)
Jun Wu
patch: rewrite reversehunks (issue5337)...
r32979
Laurent Charignon
record: move hunk class from record to patch...
r24263 def write(self, fp):
delta = len(self.before) + len(self.after)
Rodrigo Damazio Bovendorp
diff: move no-eol text constant to a common location...
r45714 if self.after and self.after[-1] == diffhelper.MISSING_NEWLINE_MARKER:
Laurent Charignon
record: move hunk class from record to patch...
r24263 delta -= 1
fromlen = delta + self.removed
tolen = delta + self.added
Augie Fackler
formatting: blacken the codebase...
r43346 fp.write(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'@@ -%d,%d +%d,%d @@%s\n'
Augie Fackler
formatting: blacken the codebase...
r43346 % (
self.fromline,
fromlen,
self.toline,
tolen,
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self.proc and (b' ' + self.proc),
Augie Fackler
formatting: blacken the codebase...
r43346 )
)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fp.write(b''.join(self.before + self.hunk + self.after))
Laurent Charignon
record: move hunk class from record to patch...
r24263
pretty = write
def filename(self):
return self.header.filename()
Kyle Lippincott
py3: __repr__ needs to return str, not bytes...
r44742 @encoding.strmethod
Laurent Charignon
record: move hunk class from record to patch...
r24263 def __repr__(self):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return b'<hunk %r@%d>' % (self.filename(), self.fromline)
Laurent Charignon
record: move hunk class from record to patch...
r24263
Augie Fackler
formatting: blacken the codebase...
r43346
Jun Wu
patch: do not cache translated messages (API)...
r34567 def getmessages():
return {
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'multiple': {
b'apply': _(b"apply change %d/%d to '%s'?"),
b'discard': _(b"discard change %d/%d to '%s'?"),
b'keep': _(b"keep change %d/%d to '%s'?"),
b'record': _(b"record change %d/%d to '%s'?"),
Jun Wu
patch: do not cache translated messages (API)...
r34567 },
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'single': {
b'apply': _(b"apply this change to '%s'?"),
b'discard': _(b"discard this change to '%s'?"),
b'keep': _(b"keep this change to '%s'?"),
b'record': _(b"record this change to '%s'?"),
Jun Wu
patch: do not cache translated messages (API)...
r34567 },
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'help': {
b'apply': _(
b'[Ynesfdaq?]'
b'$$ &Yes, apply this change'
b'$$ &No, skip this change'
b'$$ &Edit this change manually'
b'$$ &Skip remaining changes to this file'
b'$$ Apply remaining changes to this &file'
b'$$ &Done, skip remaining changes and files'
b'$$ Apply &all changes to all remaining files'
b'$$ &Quit, applying no changes'
b'$$ &? (display help)'
Augie Fackler
formatting: blacken the codebase...
r43346 ),
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'discard': _(
b'[Ynesfdaq?]'
b'$$ &Yes, discard this change'
b'$$ &No, skip this change'
b'$$ &Edit this change manually'
b'$$ &Skip remaining changes to this file'
b'$$ Discard remaining changes to this &file'
b'$$ &Done, skip remaining changes and files'
b'$$ Discard &all changes to all remaining files'
b'$$ &Quit, discarding no changes'
b'$$ &? (display help)'
Augie Fackler
formatting: blacken the codebase...
r43346 ),
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'keep': _(
b'[Ynesfdaq?]'
b'$$ &Yes, keep this change'
b'$$ &No, skip this change'
b'$$ &Edit this change manually'
b'$$ &Skip remaining changes to this file'
b'$$ Keep remaining changes to this &file'
b'$$ &Done, skip remaining changes and files'
b'$$ Keep &all changes to all remaining files'
b'$$ &Quit, keeping all changes'
b'$$ &? (display help)'
Augie Fackler
formatting: blacken the codebase...
r43346 ),
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'record': _(
b'[Ynesfdaq?]'
b'$$ &Yes, record this change'
b'$$ &No, skip this change'
b'$$ &Edit this change manually'
b'$$ &Skip remaining changes to this file'
b'$$ Record remaining changes to this &file'
b'$$ &Done, skip remaining changes and files'
b'$$ Record &all changes to all remaining files'
b'$$ &Quit, recording no changes'
b'$$ &? (display help)'
Augie Fackler
formatting: blacken the codebase...
r43346 ),
},
Pulkit Goyal
patch: take messages out of the function so that extensions can add entries...
r34044 }
Augie Fackler
formatting: blacken the codebase...
r43346
Denis Laxalde
interactive: do not prompt about files given in command line...
r42238 def filterpatch(ui, headers, match, operation=None):
Laurent Charignon
record: move filterpatch from record to patch...
r24269 """Interactively filter patch chunks into applied-only chunks"""
Jun Wu
patch: do not cache translated messages (API)...
r34567 messages = getmessages()
Laurent Charignon
record: add default value for operation argument...
r25359 if operation is None:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 operation = b'record'
Laurent Charignon
record: move filterpatch from record to patch...
r24269
def prompt(skipfile, skipall, query, chunk):
"""prompt query, and process base inputs
- y/n for the rest of file
- y/n for the rest
- ? (help)
- q (quit)
Return True/False and possibly updated skipfile and skipall.
"""
newpatches = None
if skipall is not None:
return skipall, skipfile, skipall, newpatches
if skipfile is not None:
return skipfile, skipfile, skipall, newpatches
while True:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 resps = messages[b'help'][operation]
Kyle Lippincott
patch: use a short, fixed-size message for last line of prompt (issue6158)...
r42766 # IMPORTANT: keep the last line of this prompt short (<40 english
# chars is a good target) because of issue6158.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 r = ui.promptchoice(b"%s\n(enter ? for help) %s" % (query, resps))
ui.write(b"\n")
Augie Fackler
formatting: blacken the codebase...
r43346 if r == 8: # ?
Laurent Charignon
record: move filterpatch from record to patch...
r24269 for c, t in ui.extractchoices(resps)[1]:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.write(b'%s - %s\n' % (c, encoding.lower(t)))
Laurent Charignon
record: move filterpatch from record to patch...
r24269 continue
Augie Fackler
formatting: blacken the codebase...
r43346 elif r == 0: # yes
Laurent Charignon
record: move filterpatch from record to patch...
r24269 ret = True
Augie Fackler
formatting: blacken the codebase...
r43346 elif r == 1: # no
Laurent Charignon
record: move filterpatch from record to patch...
r24269 ret = False
Augie Fackler
formatting: blacken the codebase...
r43346 elif r == 2: # Edit patch
Laurent Charignon
record: move filterpatch from record to patch...
r24269 if chunk is None:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.write(_(b'cannot edit patch for whole file'))
ui.write(b"\n")
Laurent Charignon
record: move filterpatch from record to patch...
r24269 continue
if chunk.header.binary():
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.write(_(b'cannot edit patch for binary file'))
ui.write(b"\n")
Laurent Charignon
record: move filterpatch from record to patch...
r24269 continue
# Patch comment based on the Git one (based on comment at end of
Matt Mackall
urls: bulk-change primary website URLs
r26421 # https://mercurial-scm.org/wiki/RecordExtension)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 phelp = b'---' + _(
Matt Harbison
patch: convert a UI message to bytes when editing a patch...
r47520 b"""
Laurent Charignon
record: move filterpatch from record to patch...
r24269 To remove '-' lines, make them ' ' lines (context).
To remove '+' lines, delete them.
Lines starting with # will be removed from the patch.
If the patch applies cleanly, the edited hunk will immediately be
added to the record list. If it does not apply cleanly, a rejects
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.
Augie Fackler
formatting: blacken the codebase...
r43346 """
)
(patchfd, patchfn) = pycompat.mkstemp(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 prefix=b"hg-editor-", suffix=b".diff"
Augie Fackler
formatting: blacken the codebase...
r43346 )
Laurent Charignon
record: move filterpatch from record to patch...
r24269 ncpatchfp = None
try:
# Write the initial patch
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 f = util.nativeeolwriter(os.fdopen(patchfd, 'wb'))
Laurent Charignon
record: move filterpatch from record to patch...
r24269 chunk.header.write(f)
chunk.write(f)
Augie Fackler
formatting: blacken the codebase...
r43346 f.write(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b''.join(
[b'# ' + i + b'\n' for i in phelp.splitlines()]
)
Augie Fackler
formatting: blacken the codebase...
r43346 )
Laurent Charignon
record: move filterpatch from record to patch...
r24269 f.close()
# Start the editor and wait for it to complete
editor = ui.geteditor()
Augie Fackler
formatting: blacken the codebase...
r43346 ret = ui.system(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b"%s \"%s\"" % (editor, patchfn),
environ={b'HGUSER': ui.username()},
blockedtag=b'filterpatch',
Augie Fackler
formatting: blacken the codebase...
r43346 )
Laurent Charignon
record: exiting editor with non-zero status should not stop recording session...
r25483 if ret != 0:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.warn(_(b"editor exited with exit code %d\n") % ret)
Laurent Charignon
record: exiting editor with non-zero status should not stop recording session...
r25483 continue
Laurent Charignon
record: move filterpatch from record to patch...
r24269 # Remove comment lines
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 patchfp = open(patchfn, 'rb')
timeless
pycompat: switch to util.stringio for py3 compat
r28861 ncpatchfp = stringio()
Gregory Szorc
py3: stop using util.iterfile()...
r49796 for line in patchfp:
Yuya Nishihara
py3: open patch file in binary mode and convert eol manually...
r36856 line = util.fromnativeeol(line)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if not line.startswith(b'#'):
Laurent Charignon
record: move filterpatch from record to patch...
r24269 ncpatchfp.write(line)
patchfp.close()
ncpatchfp.seek(0)
newpatches = parsepatch(ncpatchfp)
finally:
os.unlink(patchfn)
del ncpatchfp
# Signal that the chunk shouldn't be applied as-is, but
# provide the new patch to be used instead.
ret = False
Augie Fackler
formatting: blacken the codebase...
r43346 elif r == 3: # Skip
Laurent Charignon
record: move filterpatch from record to patch...
r24269 ret = skipfile = False
Augie Fackler
formatting: blacken the codebase...
r43346 elif r == 4: # file (Record remaining)
Laurent Charignon
record: move filterpatch from record to patch...
r24269 ret = skipfile = True
Augie Fackler
formatting: blacken the codebase...
r43346 elif r == 5: # done, skip remaining
Laurent Charignon
record: move filterpatch from record to patch...
r24269 ret = skipall = False
Augie Fackler
formatting: blacken the codebase...
r43346 elif r == 6: # all
Laurent Charignon
record: move filterpatch from record to patch...
r24269 ret = skipall = True
Augie Fackler
formatting: blacken the codebase...
r43346 elif r == 7: # quit
Martin von Zweigbergk
errors: introduce CanceledError and use it in a few places...
r46489 raise error.CanceledError(_(b'user quit'))
Laurent Charignon
record: move filterpatch from record to patch...
r24269 return ret, skipfile, skipall, newpatches
seen = set()
Augie Fackler
formatting: blacken the codebase...
r43346 applied = {} # 'filename' -> [] of chunks
Laurent Charignon
record: move filterpatch from record to patch...
r24269 skipfile, skipall = None, None
pos, total = 1, sum(len(h.hunks) for h in headers)
for h in headers:
pos += len(h.hunks)
skipfile = None
fixoffset = 0
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 hdr = b''.join(h.header)
Laurent Charignon
record: move filterpatch from record to patch...
r24269 if hdr in seen:
continue
seen.add(hdr)
if skipall is None:
h.pretty(ui)
Denis Laxalde
interactive: do not prompt about files given in command line...
r42238 files = h.files()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 msg = _(b'examine changes to %s?') % _(b' and ').join(
b"'%s'" % f for f in files
Augie Fackler
formatting: blacken the codebase...
r43346 )
Denis Laxalde
interactive: do not prompt about files given in command line...
r42238 if all(match.exact(f) for f in files):
r, skipall, np = True, None, None
else:
r, skipfile, skipall, np = prompt(skipfile, skipall, msg, None)
Laurent Charignon
record: move filterpatch from record to patch...
r24269 if not r:
continue
applied[h.filename()] = [h]
if h.allhunks():
applied[h.filename()] += h.hunks
continue
for i, chunk in enumerate(h.hunks):
if skipfile is None and skipall is None:
chunk.pretty(ui)
if total == 1:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 msg = messages[b'single'][operation] % chunk.filename()
Laurent Charignon
record: move filterpatch from record to patch...
r24269 else:
idx = pos - len(h.hunks) + i
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 msg = messages[b'multiple'][operation] % (
Augie Fackler
formatting: blacken the codebase...
r43346 idx,
total,
chunk.filename(),
)
r, skipfile, skipall, newpatches = prompt(
skipfile, skipall, msg, chunk
)
Laurent Charignon
record: move filterpatch from record to patch...
r24269 if r:
if fixoffset:
chunk = copy.copy(chunk)
chunk.toline += fixoffset
applied[chunk.filename()].append(chunk)
elif newpatches is not None:
for newpatch in newpatches:
for newhunk in newpatch.hunks:
if fixoffset:
newhunk.toline += fixoffset
applied[newhunk.filename()].append(newhunk)
else:
fixoffset += chunk.removed - chunk.added
Augie Fackler
formatting: blacken the codebase...
r43346 return (
sum(
Gregory Szorc
py3: replace pycompat.itervalues(x) with x.values()...
r49790 [h for h in applied.values() if h[0].special() or len(h) > 1],
Augie Fackler
formatting: blacken the codebase...
r43346 [],
),
{},
)
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class hunk:
Patrick Mezard
patch: refactor file creation/removal detection...
r14451 def __init__(self, desc, num, lr, context):
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 self.number = num
self.desc = desc
Matt Mackall
many, many trivial check-code fixups
r10282 self.hunk = [desc]
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 self.a = []
self.b = []
Benoit Boissinot
patch: initialize all attributes of the hunk class
r9682 self.starta = self.lena = None
self.startb = self.lenb = None
Patrick Mezard
patch: fix eolmode=auto with new files...
r10127 if lr is not None:
if context:
self.read_context_hunk(lr)
else:
self.read_unified_hunk(lr)
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897
Patrick Mezard
patch: fix eolmode=auto with new files...
r10127 def getnormalized(self):
"""Return a copy with line endings normalized to LF."""
def normalize(lines):
nlines = []
for line in lines:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if line.endswith(b'\r\n'):
line = line[:-2] + b'\n'
Patrick Mezard
patch: fix eolmode=auto with new files...
r10127 nlines.append(line)
return nlines
# Dummy object, it is rebuilt manually
Patrick Mezard
patch: refactor file creation/removal detection...
r14451 nh = hunk(self.desc, self.number, None, None)
Patrick Mezard
patch: fix eolmode=auto with new files...
r10127 nh.number = self.number
nh.desc = self.desc
Patrick Mezard
patch: fix patching with fuzz and eol normalization
r10524 nh.hunk = self.hunk
Patrick Mezard
patch: fix eolmode=auto with new files...
r10127 nh.a = normalize(self.a)
nh.b = normalize(self.b)
nh.starta = self.starta
nh.startb = self.startb
nh.lena = self.lena
nh.lenb = self.lenb
return nh
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 def read_unified_hunk(self, lr):
m = unidesc.match(self.desc)
if not m:
Martin von Zweigbergk
errors: return more detailed errors when failing to parse or apply patch...
r49186 raise PatchParseError(_(b"bad hunk #%d") % self.number)
Patrick Mezard
patch: simplify hunk extents parsing...
r15510 self.starta, self.lena, self.startb, self.lenb = m.groups()
Martin Geisler
use 'x is None' instead of 'x == None'...
r8527 if self.lena is None:
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 self.lena = 1
else:
self.lena = int(self.lena)
Martin Geisler
use 'x is None' instead of 'x == None'...
r8527 if self.lenb is None:
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 self.lenb = 1
else:
self.lenb = int(self.lenb)
self.starta = int(self.starta)
self.startb = int(self.startb)
Yuya Nishihara
patch: error out if reached to EOF while reading hunk...
r37591 try:
Augie Fackler
formatting: blacken the codebase...
r43346 diffhelper.addlines(
lr, self.hunk, self.lena, self.lenb, self.a, self.b
)
Yuya Nishihara
patch: error out if reached to EOF while reading hunk...
r37591 except error.ParseError as e:
Martin von Zweigbergk
errors: return more detailed errors when failing to parse or apply patch...
r49186 raise PatchParseError(_(b"bad hunk #%d: %s") % (self.number, e))
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 # if we hit eof before finishing out the hunk, the last line will
# be zero length. Lets try to fix it up.
while len(self.hunk[-1]) == 0:
Dirkjan Ochtman
fix double indentation and trailing whitespace
r6948 del self.hunk[-1]
del self.a[-1]
del self.b[-1]
self.lena -= 1
self.lenb -= 1
Patrick Mezard
patch: fix hunk newlines when parsing hunks, not in iterhunks()
r13699 self._fixnewline(lr)
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897
def read_context_hunk(self, lr):
self.desc = lr.readline()
m = contextdesc.match(self.desc)
if not m:
Martin von Zweigbergk
errors: return more detailed errors when failing to parse or apply patch...
r49186 raise PatchParseError(_(b"bad hunk #%d") % self.number)
Patrick Mezard
patch: simplify hunk extents parsing...
r15510 self.starta, aend = m.groups()
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 self.starta = int(self.starta)
Martin Geisler
use 'x is None' instead of 'x == None'...
r8527 if aend is None:
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 aend = self.starta
self.lena = int(aend) - self.starta
if self.starta:
self.lena += 1
Manuel Jacob
py3: replace `pycompat.xrange` by `range`
r50179 for x in range(self.lena):
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 l = lr.readline()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if l.startswith(b'---'):
Patrick Mezard
Test applying context diffs
r12825 # lines addition, old block is empty
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 lr.push(l)
break
s = l[2:]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if l.startswith(b'- ') or l.startswith(b'! '):
u = b'-' + s
elif l.startswith(b' '):
u = b' ' + s
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 else:
Martin von Zweigbergk
errors: return more detailed errors when failing to parse or apply patch...
r49186 raise PatchParseError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b"bad hunk #%d old text line %d") % (self.number, x)
Augie Fackler
formatting: blacken the codebase...
r43346 )
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 self.a.append(u)
self.hunk.append(u)
l = lr.readline()
Gregory Szorc
patch: properly escape \ in string literals...
r41675 if l.startswith(br'\ '):
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 s = self.a[-1][:-1]
self.a[-1] = s
self.hunk[-1] = s
l = lr.readline()
m = contextdesc.match(l)
if not m:
Martin von Zweigbergk
errors: return more detailed errors when failing to parse or apply patch...
r49186 raise PatchParseError(_(b"bad hunk #%d") % self.number)
Patrick Mezard
patch: simplify hunk extents parsing...
r15510 self.startb, bend = m.groups()
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 self.startb = int(self.startb)
Martin Geisler
use 'x is None' instead of 'x == None'...
r8527 if bend is None:
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 bend = self.startb
self.lenb = int(bend) - self.startb
if self.startb:
self.lenb += 1
hunki = 1
Manuel Jacob
py3: replace `pycompat.xrange` by `range`
r50179 for x in range(self.lenb):
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 l = lr.readline()
Gregory Szorc
patch: properly escape \ in string literals...
r41675 if l.startswith(br'\ '):
Patrick Mezard
Test applying context diffs
r12825 # XXX: the only way to hit this is with an invalid line range.
# The no-eol marker is not counted in the line range, but I
# guess there are diff(1) out there which behave differently.
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 s = self.b[-1][:-1]
self.b[-1] = s
Matt Mackall
many, many trivial check-code fixups
r10282 self.hunk[hunki - 1] = s
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 continue
if not l:
Patrick Mezard
Test applying context diffs
r12825 # line deletions, new block is empty and we hit EOF
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 lr.push(l)
break
s = l[2:]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if l.startswith(b'+ ') or l.startswith(b'! '):
u = b'+' + s
elif l.startswith(b' '):
u = b' ' + s
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 elif len(self.b) == 0:
Patrick Mezard
Test applying context diffs
r12825 # line deletions, new block is empty
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 lr.push(l)
break
else:
Martin von Zweigbergk
errors: return more detailed errors when failing to parse or apply patch...
r49186 raise PatchParseError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b"bad hunk #%d old text line %d") % (self.number, x)
Augie Fackler
formatting: blacken the codebase...
r43346 )
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 self.b.append(s)
while True:
if hunki >= len(self.hunk):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 h = b""
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 else:
h = self.hunk[hunki]
hunki += 1
if h == u:
break
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif h.startswith(b'-'):
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 continue
else:
Matt Mackall
many, many trivial check-code fixups
r10282 self.hunk.insert(hunki - 1, u)
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 break
if not self.a:
# this happens when lines were only added to the hunk
for x in self.hunk:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if x.startswith(b'-') or x.startswith(b' '):
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 self.a.append(x)
if not self.b:
# this happens when lines were only deleted from the hunk
for x in self.hunk:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if x.startswith(b'+') or x.startswith(b' '):
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 self.b.append(x[1:])
# @@ -start,len +start,len @@
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self.desc = b"@@ -%d,%d +%d,%d @@\n" % (
Augie Fackler
formatting: blacken the codebase...
r43346 self.starta,
self.lena,
self.startb,
self.lenb,
)
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 self.hunk[0] = self.desc
Patrick Mezard
patch: fix hunk newlines when parsing hunks, not in iterhunks()
r13699 self._fixnewline(lr)
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897
Patrick Mezard
patch: fix hunk newlines when parsing hunks, not in iterhunks()
r13699 def _fixnewline(self, lr):
l = lr.readline()
Gregory Szorc
patch: properly escape \ in string literals...
r41675 if l.startswith(br'\ '):
Yuya Nishihara
diffhelper: rename module to avoid conflicts with ancient C module (issue5846)...
r37821 diffhelper.fixnewline(self.hunk, self.a, self.b)
Patrick Mezard
patch: fix hunk newlines when parsing hunks, not in iterhunks()
r13699 else:
lr.push(l)
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897
def complete(self):
return len(self.a) == self.lena and len(self.b) == self.lenb
Patrick Mezard
patch: fuzz old and new lines at the same time...
r16121 def _fuzzit(self, old, new, fuzz, toponly):
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 # this removes context lines from the top and bottom of list 'l'. It
# checks the hunk to make sure only context lines are removed, and then
# returns a new shortened list of lines.
Patrick Mezard
patch: fuzz more aggressively to match patch(1) behaviour...
r16124 fuzz = min(fuzz, len(old))
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 if fuzz:
top = 0
bot = 0
hlen = len(self.hunk)
Manuel Jacob
py3: replace `pycompat.xrange` by `range`
r50179 for x in range(hlen - 1):
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 # the hunk starts with the @@ line, so use x+1
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if self.hunk[x + 1].startswith(b' '):
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 top += 1
else:
break
if not toponly:
Manuel Jacob
py3: replace `pycompat.xrange` by `range`
r50179 for x in range(hlen - 1):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if self.hunk[hlen - bot - 1].startswith(b' '):
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 bot += 1
else:
break
Patrick Mezard
patch: fuzz more aggressively to match patch(1) behaviour...
r16124 bot = min(fuzz, bot)
top = min(fuzz, top)
Augie Fackler
formatting: blacken the codebase...
r43346 return old[top : len(old) - bot], new[top : len(new) - bot], top
Patrick Mezard
patch: make hunk.fuzzit() compute the fuzzed start locations...
r16122 return old, new, 0
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897
Patrick Mezard
patch: fuzz old and new lines at the same time...
r16121 def fuzzit(self, fuzz, toponly):
Patrick Mezard
patch: make hunk.fuzzit() compute the fuzzed start locations...
r16122 old, new, top = self._fuzzit(self.a, self.b, fuzz, toponly)
oldstart = self.starta + top
newstart = self.startb + top
# zero length hunk ranges already have their start decremented
Yuya Nishihara
patch: fix segfault against unified diffs which start line is zero...
r16650 if self.lena and oldstart > 0:
Patrick Mezard
patch: make hunk.fuzzit() compute the fuzzed start locations...
r16122 oldstart -= 1
Yuya Nishihara
patch: fix segfault against unified diffs which start line is zero...
r16650 if self.lenb and newstart > 0:
Patrick Mezard
patch: make hunk.fuzzit() compute the fuzzed start locations...
r16122 newstart -= 1
return old, oldstart, new, newstart
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class binhunk:
Matt Harbison
cleanup: fix docstring formatting...
r44226 """A binary patch file."""
Augie Fackler
formatting: blacken the codebase...
r43346
Patrick Mezard
patch: include file name in binary patch error messages...
r16523 def __init__(self, lr, fname):
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 self.text = None
Nicolas Vigier
patch: add support for git delta hunks...
r20137 self.delta = False
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self.hunk = [b'GIT binary patch\n']
Patrick Mezard
patch: include file name in binary patch error messages...
r16523 self._fname = fname
Patrick Mezard
patch: construct and parse binary hunks at the same time
r14384 self._read(lr)
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897
def complete(self):
return self.text is not None
Nicolas Vigier
patch: add support for git delta hunks...
r20137 def new(self, lines):
if self.delta:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return [applybindelta(self.text, b''.join(lines))]
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 return [self.text]
Patrick Mezard
patch: construct and parse binary hunks at the same time
r14384 def _read(self, lr):
Patrick Mezard
patch: be more tolerant with EOLs in binary diffs (issue2870)...
r16524 def getline(lr, hunk):
l = lr.readline()
hunk.append(l)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return l.rstrip(b'\r\n')
Patrick Mezard
patch: be more tolerant with EOLs in binary diffs (issue2870)...
r16524
Patrick Mezard
patch: clarify binary hunk parsing loop
r16567 while True:
Patrick Mezard
patch: be more tolerant with EOLs in binary diffs (issue2870)...
r16524 line = getline(lr, self.hunk)
Patrick Mezard
patch: clarify binary hunk parsing loop
r16567 if not line:
Martin von Zweigbergk
errors: return more detailed errors when failing to parse or apply patch...
r49186 raise PatchParseError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'could not extract "%s" binary data') % self._fname
Augie Fackler
formatting: blacken the codebase...
r43346 )
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if line.startswith(b'literal '):
Nicolas Vigier
patch: add support for git delta hunks...
r20137 size = int(line[8:].rstrip())
Patrick Mezard
patch: clarify binary hunk parsing loop
r16567 break
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if line.startswith(b'delta '):
Nicolas Vigier
patch: add support for git delta hunks...
r20137 size = int(line[6:].rstrip())
self.delta = True
break
Brendan Cully
Add git-1.4 binary patch support
r3367 dec = []
Patrick Mezard
patch: be more tolerant with EOLs in binary diffs (issue2870)...
r16524 line = getline(lr, self.hunk)
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 while len(line) > 1:
Pulkit Goyal
py3: slice over bytes to prevent getting ascii values...
r36210 l = line[0:1]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if l <= b'Z' and l >= b'A':
l = ord(l) - ord(b'A') + 1
Brendan Cully
Use line length field when extracting git binary patches
r3374 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 l = ord(l) - ord(b'a') + 27
Patrick Mezard
patch: display a nice error for invalid base85 data...
r16522 try:
Yuya Nishihara
base85: proxy through util module...
r32200 dec.append(util.b85decode(line[1:])[:l])
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except ValueError as e:
Martin von Zweigbergk
errors: return more detailed errors when failing to parse or apply patch...
r49186 raise PatchParseError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'could not decode "%s" binary patch: %s')
Augie Fackler
formatting: blacken the codebase...
r43346 % (self._fname, stringutil.forcebytestr(e))
)
Patrick Mezard
patch: be more tolerant with EOLs in binary diffs (issue2870)...
r16524 line = getline(lr, self.hunk)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 text = zlib.decompress(b''.join(dec))
Brendan Cully
Add git-1.4 binary patch support
r3367 if len(text) != size:
Martin von Zweigbergk
errors: return more detailed errors when failing to parse or apply patch...
r49186 raise PatchParseError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'"%s" length is %d bytes, should be %d')
Augie Fackler
formatting: blacken the codebase...
r43346 % (self._fname, len(text), size)
)
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 self.text = text
Brendan Cully
Add git-1.4 binary patch support
r3367
Augie Fackler
formatting: blacken the codebase...
r43346
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 def parsefilename(str):
# --- filename \t|space stuff
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 s = str[4:].rstrip(b'\r\n')
i = s.find(b'\t')
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 if i < 0:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 i = s.find(b' ')
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 if i < 0:
return s
return s[:i]
Brendan Cully
Move patch-related code into its own module.
r2861
Augie Fackler
formatting: blacken the codebase...
r43346
Laurent Charignon
revert: add an experimental config to use inverted selection...
r25424 def reversehunks(hunks):
'''reverse the signs in the hunks given as argument
This function operates on hunks coming out of patch.filterpatch, that is
a list of the form: [header1, hunk1, hunk2, header2...]. Example usage:
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> rawpatch = b"""diff --git a/folder1/g b/folder1/g
Laurent Charignon
revert: add an experimental config to use inverted selection...
r25424 ... --- a/folder1/g
... +++ b/folder1/g
... @@ -1,7 +1,7 @@
... +firstline
... c
... 1
... 2
... + 3
... -4
... 5
... d
... +lastline"""
Yuya Nishihara
py3: fix doctests in patch.py to be compatible with Python 3...
r34254 >>> hunks = parsepatch([rawpatch])
Laurent Charignon
revert: add an experimental config to use inverted selection...
r25424 >>> hunkscomingfromfilterpatch = []
>>> for h in hunks:
... hunkscomingfromfilterpatch.append(h)
... hunkscomingfromfilterpatch.extend(h.hunks)
>>> reversedhunks = reversehunks(hunkscomingfromfilterpatch)
timeless
pycompat: switch to util.stringio for py3 compat
r28861 >>> from . import util
>>> fp = util.stringio()
Laurent Charignon
revert: add an experimental config to use inverted selection...
r25424 >>> for c in reversedhunks:
... c.write(fp)
Yuya Nishihara
py3: fix doctests in patch.py to be compatible with Python 3...
r34254 >>> fp.seek(0) or None
Laurent Charignon
revert: add an experimental config to use inverted selection...
r25424 >>> reversedpatch = fp.read()
Yuya Nishihara
doctest: use print_function and convert bytes to unicode where needed
r34139 >>> print(pycompat.sysstr(reversedpatch))
Laurent Charignon
revert: add an experimental config to use inverted selection...
r25424 diff --git a/folder1/g b/folder1/g
--- a/folder1/g
+++ b/folder1/g
@@ -1,4 +1,3 @@
-firstline
c
1
2
Jun Wu
patch: rewrite reversehunks (issue5337)...
r32979 @@ -2,6 +1,6 @@
Laurent Charignon
revert: add an experimental config to use inverted selection...
r25424 c
1
2
- 3
+4
5
d
Jun Wu
patch: rewrite reversehunks (issue5337)...
r32979 @@ -6,3 +5,2 @@
Laurent Charignon
revert: add an experimental config to use inverted selection...
r25424 5
d
-lastline
'''
newhunks = []
for c in hunks:
safehasattr: drop usage in favor of hasattr...
r51821 if hasattr(c, 'reversehunk'):
Jun Wu
patch: rewrite reversehunks (issue5337)...
r32979 c = c.reversehunk()
Laurent Charignon
revert: add an experimental config to use inverted selection...
r25424 newhunks.append(c)
return newhunks
Augie Fackler
formatting: blacken the codebase...
r43346
Jun Wu
patch: make parsepatch optionally trim context lines...
r33270 def parsepatch(originalchunks, maxcontext=None):
"""patch -> [] of headers -> [] of hunks
If maxcontext is not None, trim context lines if necessary.
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> rawpatch = b'''diff --git a/folder1/g b/folder1/g
Jun Wu
patch: make parsepatch optionally trim context lines...
r33270 ... --- a/folder1/g
... +++ b/folder1/g
... @@ -1,8 +1,10 @@
... 1
... 2
... -3
... 4
... 5
... 6
... +6.1
... +6.2
... 7
... 8
... +9'''
>>> out = util.stringio()
>>> headers = parsepatch([rawpatch], maxcontext=1)
>>> for header in headers:
... header.write(out)
... for hunk in header.hunks:
... hunk.write(out)
Yuya Nishihara
doctest: use print_function and convert bytes to unicode where needed
r34139 >>> print(pycompat.sysstr(out.getvalue()))
Jun Wu
patch: make parsepatch optionally trim context lines...
r33270 diff --git a/folder1/g b/folder1/g
--- a/folder1/g
+++ b/folder1/g
@@ -2,3 +2,2 @@
2
-3
4
@@ -6,2 +5,4 @@
6
+6.1
+6.2
7
@@ -8,1 +9,2 @@
8
+9
"""
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class parser:
Laurent Charignon
record: move parsepatch from record to patch...
r24265 """patch parsing state machine"""
Augie Fackler
formatting: blacken the codebase...
r43346
Laurent Charignon
record: move parsepatch from record to patch...
r24265 def __init__(self):
self.fromline = 0
self.toline = 0
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self.proc = b''
Laurent Charignon
record: move parsepatch from record to patch...
r24265 self.header = None
self.context = []
self.before = []
self.hunk = []
self.headers = []
def addrange(self, limits):
Kyle Lippincott
patch: handle 0 context lines (diff.unified=0) when parsing patches...
r41497 self.addcontext([])
Laurent Charignon
record: move parsepatch from record to patch...
r24265 fromstart, fromend, tostart, toend, proc = limits
self.fromline = int(fromstart)
self.toline = int(tostart)
self.proc = proc
def addcontext(self, context):
if self.hunk:
Augie Fackler
formatting: blacken the codebase...
r43346 h = recordhunk(
self.header,
self.fromline,
self.toline,
self.proc,
self.before,
self.hunk,
context,
maxcontext,
)
Laurent Charignon
record: move parsepatch from record to patch...
r24265 self.header.hunks.append(h)
self.fromline += len(self.before) + h.removed
self.toline += len(self.before) + h.added
self.before = []
self.hunk = []
self.context = context
def addhunk(self, hunk):
if self.context:
self.before = self.context
self.context = []
Kyle Lippincott
patch: handle 0 context lines (diff.unified=0) when parsing patches...
r41497 if self.hunk:
self.addcontext([])
Laurent Charignon
record: move parsepatch from record to patch...
r24265 self.hunk = hunk
def newfile(self, hdr):
self.addcontext([])
h = header(hdr)
self.headers.append(h)
self.header = h
def addother(self, line):
Augie Fackler
formatting: blacken the codebase...
r43346 pass # 'other' lines are ignored
Laurent Charignon
record: move parsepatch from record to patch...
r24265
def finished(self):
self.addcontext([])
return self.headers
transitions = {
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'file': {
b'context': addcontext,
b'file': newfile,
b'hunk': addhunk,
b'range': addrange,
Augie Fackler
formatting: blacken the codebase...
r43346 },
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'context': {
b'file': newfile,
b'hunk': addhunk,
b'range': addrange,
b'other': addother,
Augie Fackler
formatting: blacken the codebase...
r43346 },
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'hunk': {
b'context': addcontext,
b'file': newfile,
b'range': addrange,
},
b'range': {b'context': addcontext, b'hunk': addhunk},
b'other': {b'other': addother},
Augie Fackler
formatting: blacken the codebase...
r43346 }
Laurent Charignon
record: move parsepatch from record to patch...
r24265
p = parser()
timeless
pycompat: switch to util.stringio for py3 compat
r28861 fp = stringio()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fp.write(b''.join(originalchunks))
Laurent Charignon
record: change interface of the filtering function...
r24341 fp.seek(0)
Laurent Charignon
record: move parsepatch from record to patch...
r24265
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 state = b'context'
Laurent Charignon
record: move parsepatch from record to patch...
r24265 for newstate, data in scanpatch(fp):
try:
p.transitions[state][newstate](p, data)
except KeyError:
Martin von Zweigbergk
errors: return more detailed errors when failing to parse or apply patch...
r49186 raise PatchParseError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'unhandled transition: %s -> %s' % (state, newstate)
Augie Fackler
formatting: blacken the codebase...
r43346 )
Laurent Charignon
record: move parsepatch from record to patch...
r24265 state = newstate
Laurent Charignon
record: change interface of the filtering function...
r24341 del fp
Laurent Charignon
record: move parsepatch from record to patch...
r24265 return p.finished()
Augie Fackler
formatting: blacken the codebase...
r43346
Siddharth Agarwal
patch.pathtransform: add a prefix parameter...
r24244 def pathtransform(path, strip, prefix):
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """turn a path from a patch into a path suitable for the repository
Siddharth Agarwal
patch.pathtransform: add doctests...
r24243
Siddharth Agarwal
patch.pathtransform: add a prefix parameter...
r24244 prefix, if not empty, is expected to be normalized with a / at the end.
Siddharth Agarwal
patch.pathtransform: add doctests...
r24243 Returns (stripped components, path in repository).
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> pathtransform(b'a/b/c', 0, b'')
Siddharth Agarwal
patch.pathtransform: add doctests...
r24243 ('', 'a/b/c')
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> pathtransform(b' a/b/c ', 0, b'')
Siddharth Agarwal
patch.pathtransform: add doctests...
r24243 ('', ' a/b/c')
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> pathtransform(b' a/b/c ', 2, b'')
Siddharth Agarwal
patch.pathtransform: add doctests...
r24243 ('a/b/', 'c')
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> pathtransform(b'a/b/c', 0, b'd/e/')
Siddharth Agarwal
patch.pathtransform: prepend prefix even if strip is 0...
r24385 ('', 'd/e/a/b/c')
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> pathtransform(b' a//b/c ', 2, b'd/e/')
Siddharth Agarwal
patch.pathtransform: add a prefix parameter...
r24244 ('a//b/', 'd/e/c')
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> pathtransform(b'a/b/c', 3, b'')
Siddharth Agarwal
patch.pathtransform: add doctests...
r24243 Traceback (most recent call last):
Martin von Zweigbergk
errors: return more detailed errors when failing to parse or apply patch...
r49186 PatchApplicationError: unable to strip away 1 of 3 dirs from a/b/c
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """
Mads Kiilerich
patch: strip paths in leaked git patchmeta objects
r11022 pathlen = len(path)
i = 0
if strip == 0:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return b'', prefix + path.rstrip()
Mads Kiilerich
patch: strip paths in leaked git patchmeta objects
r11022 count = strip
while count > 0:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 i = path.find(b'/', i)
Mads Kiilerich
patch: strip paths in leaked git patchmeta objects
r11022 if i == -1:
Martin von Zweigbergk
errors: return more detailed errors when failing to parse or apply patch...
r49186 raise PatchApplicationError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b"unable to strip away %d of %d dirs from %s")
Augie Fackler
formatting: blacken the codebase...
r43346 % (count, strip, path)
)
Mads Kiilerich
patch: strip paths in leaked git patchmeta objects
r11022 i += 1
# consume '//' in the path
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 while i < pathlen - 1 and path[i : i + 1] == b'/':
Mads Kiilerich
patch: strip paths in leaked git patchmeta objects
r11022 i += 1
count -= 1
Siddharth Agarwal
patch.pathtransform: add a prefix parameter...
r24244 return path[:i].lstrip(), prefix + path[i:].rstrip()
Mads Kiilerich
patch: strip paths in leaked git patchmeta objects
r11022
Augie Fackler
formatting: blacken the codebase...
r43346
Siddharth Agarwal
patch.makepatchmeta: accept a prefix parameter...
r24245 def makepatchmeta(backend, afile_orig, bfile_orig, hunk, strip, prefix):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 nulla = afile_orig == b"/dev/null"
nullb = bfile_orig == b"/dev/null"
Patrick Mezard
patch: refactor file creation/removal detection...
r14451 create = nulla and hunk.starta == 0 and hunk.lena == 0
remove = nullb and hunk.startb == 0 and hunk.lenb == 0
Siddharth Agarwal
patch.makepatchmeta: accept a prefix parameter...
r24245 abase, afile = pathtransform(afile_orig, strip, prefix)
Patrick Mezard
patch: add lexists() to backends, use it in selectfile()...
r14351 gooda = not nulla and backend.exists(afile)
Siddharth Agarwal
patch.makepatchmeta: accept a prefix parameter...
r24245 bbase, bfile = pathtransform(bfile_orig, strip, prefix)
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 if afile == bfile:
goodb = gooda
else:
Patrick Mezard
patch: add lexists() to backends, use it in selectfile()...
r14351 goodb = not nullb and backend.exists(bfile)
Patrick Mezard
patch: refactor file creation/removal detection...
r14451 missing = not goodb and not gooda and not create
Brendan Cully
patch: create file even if source is not /dev/null...
r9328
Martin Geisler
patch: fix typo in comment
r11820 # some diff programs apparently produce patches where the afile is
# not /dev/null, but afile starts with bfile
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 abasedir = afile[: afile.rfind(b'/') + 1]
bbasedir = bfile[: bfile.rfind(b'/') + 1]
Augie Fackler
formatting: blacken the codebase...
r43346 if (
missing
and abasedir == bbasedir
and afile.startswith(bfile)
and hunk.starta == 0
and hunk.lena == 0
):
Patrick Mezard
patch: refactor file creation/removal detection...
r14451 create = True
missing = False
Brendan Cully
patch: create file even if source is not /dev/null...
r9328
Patrick Mezard
patch: fix corner case with update + copy patch handling (issue 937)...
r6295 # 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).
Augie Fackler
formatting: blacken the codebase...
r43346 isbackup = abase == bbase and bfile.startswith(afile)
Patrick Mezard
patch: write rej files for missing targets (issue 853)
r5652 fname = None
if not missing:
if gooda and goodb:
Jordi Gutiérrez Hermoso
style: kill ersatz if-else ternary operators...
r24306 if isbackup:
fname = afile
else:
fname = bfile
Patrick Mezard
patch: write rej files for missing targets (issue 853)
r5652 elif gooda:
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 fname = afile
Thomas Arendsen Hein
Removed tabs and trailing whitespace in python files
r5760
Patrick Mezard
patch: write rej files for missing targets (issue 853)
r5652 if not fname:
if not nullb:
Jordi Gutiérrez Hermoso
style: kill ersatz if-else ternary operators...
r24306 if isbackup:
fname = afile
else:
fname = bfile
Patrick Mezard
patch: write rej files for missing targets (issue 853)
r5652 elif not nulla:
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 fname = afile
Patrick Mezard
patch: write rej files for missing targets (issue 853)
r5652 else:
Martin von Zweigbergk
errors: return more detailed errors when failing to parse or apply patch...
r49186 raise PatchParseError(_(b"undefined source and destination files"))
Thomas Arendsen Hein
Removed tabs and trailing whitespace in python files
r5760
Patrick Mezard
patch: generalize the use of patchmeta in applydiff()...
r14566 gp = patchmeta(fname)
if create:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 gp.op = b'ADD'
Patrick Mezard
patch: generalize the use of patchmeta in applydiff()...
r14566 elif remove:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 gp.op = b'DELETE'
Patrick Mezard
patch: generalize the use of patchmeta in applydiff()...
r14566 return gp
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897
Augie Fackler
formatting: blacken the codebase...
r43346
Laurent Charignon
record: move scanpatch from record to patch...
r24264 def scanpatch(fp):
"""like patch.iterhunks, but yield different events
- ('file', [header_lines + fromfile + tofile])
- ('context', [context_lines])
- ('hunk', [hunk_lines])
- ('range', (-start,len, +start,len, proc))
"""
Yuya Nishihara
py3: fix type of regex literals in patch.py
r34068 lines_re = re.compile(br'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)')
Laurent Charignon
record: move scanpatch from record to patch...
r24264 lr = linereader(fp)
def scanwhile(first, p):
"""scan lr while predicate holds"""
lines = [first]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 for line in iter(lr.readline, b''):
Laurent Charignon
record: move scanpatch from record to patch...
r24264 if p(line):
lines.append(line)
else:
lr.push(line)
break
return lines
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 for line in iter(lr.readline, b''):
if line.startswith(b'diff --git a/') or line.startswith(b'diff -r '):
Augie Fackler
formatting: blacken the codebase...
r43346
Laurent Charignon
record: move scanpatch from record to patch...
r24264 def notheader(line):
s = line.split(None, 1)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return not s or s[0] not in (b'---', b'diff')
Augie Fackler
formatting: blacken the codebase...
r43346
Laurent Charignon
record: move scanpatch from record to patch...
r24264 header = scanwhile(line, notheader)
fromfile = lr.readline()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if fromfile.startswith(b'---'):
Laurent Charignon
record: move scanpatch from record to patch...
r24264 tofile = lr.readline()
header += [fromfile, tofile]
else:
lr.push(fromfile)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield b'file', header
elif line.startswith(b' '):
cs = (b' ', b'\\')
yield b'context', scanwhile(line, lambda l: l.startswith(cs))
elif line.startswith((b'-', b'+')):
cs = (b'-', b'+', b'\\')
yield b'hunk', scanwhile(line, lambda l: l.startswith(cs))
Laurent Charignon
record: move scanpatch from record to patch...
r24264 else:
m = lines_re.match(line)
if m:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield b'range', m.groups()
Laurent Charignon
record: move scanpatch from record to patch...
r24264 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield b'other', line
Laurent Charignon
record: move scanpatch from record to patch...
r24264
Augie Fackler
formatting: blacken the codebase...
r43346
Patrick Mezard
patch: pass linereader to scangitpatch(), extract from iterhunks()...
r7152 def scangitpatch(lr, firstline):
Dirkjan Ochtman
clean up trailing spaces, leading spaces in C
r7186 """
Patrick Mezard
patch: pass linereader to scangitpatch(), extract from iterhunks()...
r7152 Git patches can emit:
- rename a to b
- change b
- copy a to c
- change c
Dirkjan Ochtman
clean up trailing spaces, leading spaces in C
r7186
Patrick Mezard
patch: pass linereader to scangitpatch(), extract from iterhunks()...
r7152 We cannot apply this sequence as-is, the renamed 'a' could not be
found for it would have been renamed already. And we cannot copy
from 'b' instead because 'b' would have been changed already. So
we scan the git patch for copy and rename commands so we can
perform the copies ahead of time.
"""
pos = 0
try:
pos = lr.fp.tell()
fp = lr.fp
except IOError:
timeless
pycompat: switch to util.stringio for py3 compat
r28861 fp = stringio(lr.fp.read())
Patrick Mezard
patch: remove EOL support from linereader class...
r14418 gitlr = linereader(fp)
Patrick Mezard
patch: pass linereader to scangitpatch(), extract from iterhunks()...
r7152 gitlr.push(firstline)
Patrick Mezard
patch: remove unused flags from readgitpatch()
r12669 gitpatches = readgitpatch(gitlr)
Patrick Mezard
patch: pass linereader to scangitpatch(), extract from iterhunks()...
r7152 fp.seek(pos)
Patrick Mezard
patch: remove unused flags from readgitpatch()
r12669 return gitpatches
Patrick Mezard
patch: pass linereader to scangitpatch(), extract from iterhunks()...
r7152
Augie Fackler
formatting: blacken the codebase...
r43346
Idan Kamara
patch: remove unused ui arg to iterhunks
r14240 def iterhunks(fp):
Patrick Mezard
patch: move diff parsing in iterhunks generator
r5650 """Read a patch and yield the following events:
- ("file", afile, bfile, firsthunk): select a new target file.
- ("hunk", hunk): a new hunk is ready to be applied, follows a
"file" event.
- ("git", gitchanges): current diff is in git format, gitchanges
maps filenames to gitpatch records. Unique event.
"""
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 afile = b""
bfile = b""
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 state = None
hunknum = 0
Patrick Mezard
patch: remove redundant variable in iterhunks()
r14017 emitfile = newfile = False
Patrick Mezard
patch: stop handling hunkless git blocks out of stream...
r14388 gitpatches = None
Brendan Cully
Move patch-related code into its own module.
r2861
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 # our states
BFILE = 1
context = None
Patrick Mezard
patch: drop eol normalization fast-path for 'lf' and 'crlf'...
r10128 lr = linereader(fp)
Brendan Cully
Move patch-related code into its own module.
r2861
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 for x in iter(lr.readline, b''):
Patrick Mezard
patch: refactor iterhunks() regular and binary files emission
r14383 if state == BFILE and (
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 (not context and x.startswith(b'@'))
or (context is not False and x.startswith(b'***************'))
or x.startswith(b'GIT binary patch')
Augie Fackler
formatting: blacken the codebase...
r43346 ):
Patrick Mezard
patch: stop handling hunkless git blocks out of stream...
r14388 gp = None
Augie Fackler
formatting: blacken the codebase...
r43346 if gitpatches and gitpatches[-1].ispatching(afile, bfile):
Patrick Mezard
patch: fix patch hunk/metdata synchronization (issue3384)...
r16506 gp = gitpatches.pop()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if x.startswith(b'GIT binary patch'):
Patrick Mezard
patch: include file name in binary patch error messages...
r16523 h = binhunk(lr, gp.path)
Patrick Mezard
patch: refactor iterhunks() regular and binary files emission
r14383 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if context is None and x.startswith(b'***************'):
Patrick Mezard
patch: refactor iterhunks() regular and binary files emission
r14383 context = True
Patrick Mezard
patch: refactor file creation/removal detection...
r14451 h = hunk(x, hunknum + 1, lr, context)
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 hunknum += 1
Patrick Mezard
patch: move diff parsing in iterhunks generator
r5650 if emitfile:
emitfile = False
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield b'file', (afile, bfile, h, gp and gp.copy() or None)
yield b'hunk', h
elif x.startswith(b'diff --git a/'):
Kyle Lippincott
patch: handle filenames with trailing spaces...
r46863 m = gitre.match(x.rstrip(b'\r\n'))
Patrick Mezard
patch: reindent code
r14387 if not m:
continue
Patrick Mezard
patch: fix patch hunk/metdata synchronization (issue3384)...
r16506 if gitpatches is None:
Patrick Mezard
patch: reindent code
r14387 # scan whole input for git metadata
Patrick Mezard
patch: fix patch hunk/metdata synchronization (issue3384)...
r16506 gitpatches = scangitpatch(lr, x)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield b'git', [
g.copy() for g in gitpatches if g.op in (b'COPY', b'RENAME')
Augie Fackler
formatting: blacken the codebase...
r43346 ]
Patrick Mezard
patch: stop handling hunkless git blocks out of stream...
r14388 gitpatches.reverse()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 afile = b'a/' + m.group(1)
bfile = b'b/' + m.group(2)
Patrick Mezard
patch: fix patch hunk/metdata synchronization (issue3384)...
r16506 while gitpatches and not gitpatches[-1].ispatching(afile, bfile):
gp = gitpatches.pop()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield b'file', (
b'a/' + gp.path,
b'b/' + gp.path,
None,
gp.copy(),
)
Patrick Mezard
patch: fix patch hunk/metdata synchronization (issue3384)...
r16506 if not gitpatches:
Martin von Zweigbergk
errors: return more detailed errors when failing to parse or apply patch...
r49186 raise PatchParseError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'failed to synchronize metadata for "%s"') % afile[2:]
Augie Fackler
formatting: blacken the codebase...
r43346 )
Patrick Mezard
patch: reindent code
r14387 newfile = True
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif x.startswith(b'---'):
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 # check for a unified diff
l2 = lr.readline()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if not l2.startswith(b'+++'):
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 lr.push(l2)
continue
newfile = True
context = False
afile = parsefilename(x)
bfile = parsefilename(l2)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif x.startswith(b'***'):
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 # check for a context diff
l2 = lr.readline()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if not l2.startswith(b'---'):
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 lr.push(l2)
continue
l3 = lr.readline()
lr.push(l3)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if not l3.startswith(b"***************"):
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 lr.push(l2)
continue
newfile = True
context = True
afile = parsefilename(x)
bfile = parsefilename(l2)
Benoit Boissinot
unlink temporary patch files even when an exception is raised
r3057
Patrick Mezard
patch: remove redundant variable in iterhunks()
r14017 if newfile:
newfile = False
Patrick Mezard
patch: move diff parsing in iterhunks generator
r5650 emitfile = True
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 state = BFILE
hunknum = 0
Patrick Mezard
patch: move diff parsing in iterhunks generator
r5650
Patrick Mezard
patch: stop handling hunkless git blocks out of stream...
r14388 while gitpatches:
Patrick Mezard
patch: fix patch hunk/metdata synchronization (issue3384)...
r16506 gp = gitpatches.pop()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield b'file', (b'a/' + gp.path, b'b/' + gp.path, None, gp.copy())
Patrick Mezard
patch: stop handling hunkless git blocks out of stream...
r14388
Augie Fackler
formatting: blacken the codebase...
r43346
Nicolas Vigier
patch: add support for git delta hunks...
r20137 def applybindelta(binchunk, data):
"""Apply a binary delta hunk
The algorithm used is the algorithm from git's patch-delta.c
"""
Augie Fackler
formatting: blacken the codebase...
r43346
Nicolas Vigier
patch: add support for git delta hunks...
r20137 def deltahead(binchunk):
i = 0
Pulkit Goyal
py3: bytestr() bytes to get bytechar while iterating on it...
r38096 for c in pycompat.bytestr(binchunk):
Nicolas Vigier
patch: add support for git delta hunks...
r20137 i += 1
if not (ord(c) & 0x80):
return i
return i
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 out = b""
Nicolas Vigier
patch: add support for git delta hunks...
r20137 s = deltahead(binchunk)
binchunk = binchunk[s:]
s = deltahead(binchunk)
binchunk = binchunk[s:]
i = 0
while i < len(binchunk):
Augie Fackler
formatting: blacken the codebase...
r43346 cmd = ord(binchunk[i : i + 1])
Nicolas Vigier
patch: add support for git delta hunks...
r20137 i += 1
Augie Fackler
formatting: blacken the codebase...
r43346 if cmd & 0x80:
Nicolas Vigier
patch: add support for git delta hunks...
r20137 offset = 0
size = 0
Augie Fackler
formatting: blacken the codebase...
r43346 if cmd & 0x01:
offset = ord(binchunk[i : i + 1])
Nicolas Vigier
patch: add support for git delta hunks...
r20137 i += 1
Augie Fackler
formatting: blacken the codebase...
r43346 if cmd & 0x02:
offset |= ord(binchunk[i : i + 1]) << 8
Nicolas Vigier
patch: add support for git delta hunks...
r20137 i += 1
Augie Fackler
formatting: blacken the codebase...
r43346 if cmd & 0x04:
offset |= ord(binchunk[i : i + 1]) << 16
Nicolas Vigier
patch: add support for git delta hunks...
r20137 i += 1
Augie Fackler
formatting: blacken the codebase...
r43346 if cmd & 0x08:
offset |= ord(binchunk[i : i + 1]) << 24
Nicolas Vigier
patch: add support for git delta hunks...
r20137 i += 1
Augie Fackler
formatting: blacken the codebase...
r43346 if cmd & 0x10:
size = ord(binchunk[i : i + 1])
Nicolas Vigier
patch: add support for git delta hunks...
r20137 i += 1
Augie Fackler
formatting: blacken the codebase...
r43346 if cmd & 0x20:
size |= ord(binchunk[i : i + 1]) << 8
Nicolas Vigier
patch: add support for git delta hunks...
r20137 i += 1
Augie Fackler
formatting: blacken the codebase...
r43346 if cmd & 0x40:
size |= ord(binchunk[i : i + 1]) << 16
Nicolas Vigier
patch: add support for git delta hunks...
r20137 i += 1
if size == 0:
size = 0x10000
offset_end = offset + size
out += data[offset:offset_end]
elif cmd != 0:
offset_end = i + cmd
out += binchunk[i:offset_end]
i += cmd
else:
Martin von Zweigbergk
errors: return more detailed errors when failing to parse or apply patch...
r49186 raise PatchApplicationError(_(b'unexpected delta opcode 0'))
Nicolas Vigier
patch: add support for git delta hunks...
r20137 return out
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 def applydiff(ui, fp, backend, store, strip=1, prefix=b'', eolmode=b'strict'):
Augie Fackler
patch: refactor applydiff to allow for mempatching
r10966 """Reads a patch from fp and tries to apply it.
Patrick Mezard
patch: move diff parsing in iterhunks generator
r5650
Patrick Mezard
patch: stop updating changed files set in applydiff()...
r14565 Returns 0 for a clean patch, -1 if any rejects were found and 1 if
there was any fuzz.
Patrick Mezard
Add patch.eol to ignore EOLs when patching (issue1019)...
r8810
Martin Geisler
patch: propagate eolmode down to patchfile...
r10101 If 'eolmode' is 'strict', the patch content and patched file are
read in binary mode. Otherwise, line endings are ignored when
patching then normalized according to 'eolmode'.
Patrick Mezard
Add patch.eol to ignore EOLs when patching (issue1019)...
r8810 """
Augie Fackler
formatting: blacken the codebase...
r43346 return _applydiff(
ui,
fp,
patchfile,
backend,
store,
strip=strip,
prefix=prefix,
eolmode=eolmode,
)
Augie Fackler
patch: refactor applydiff to allow for mempatching
r10966
Martin von Zweigbergk
patch: accept prefix argument to changedfiles() helper...
r35053 def _canonprefix(repo, prefix):
if prefix:
prefix = pathutil.canonpath(repo.root, repo.getcwd(), prefix)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if prefix != b'':
prefix += b'/'
Martin von Zweigbergk
patch: accept prefix argument to changedfiles() helper...
r35053 return prefix
Augie Fackler
formatting: blacken the codebase...
r43346
def _applydiff(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui, fp, patcher, backend, store, strip=1, prefix=b'', eolmode=b'strict'
Augie Fackler
formatting: blacken the codebase...
r43346 ):
Martin von Zweigbergk
patch: accept prefix argument to changedfiles() helper...
r35053 prefix = _canonprefix(backend.repo, prefix)
Augie Fackler
formatting: blacken the codebase...
r43346
Patrick Mezard
patch: stop modifying gitpatch objects...
r14389 def pstrip(p):
Siddharth Agarwal
patch._applydiff: accept a prefix parameter...
r24246 return pathtransform(p, strip - 1, prefix)[1]
Patrick Mezard
patch: stop modifying gitpatch objects...
r14389
Patrick Mezard
patch: move diff parsing in iterhunks generator
r5650 rejects = 0
err = 0
current_file = None
Idan Kamara
patch: remove unused ui arg to iterhunks
r14240 for state, values in iterhunks(fp):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if state == b'hunk':
Patrick Mezard
patch: move diff parsing in iterhunks generator
r5650 if not current_file:
continue
Mads Kiilerich
patch: minor cleanup of _applydiff
r11021 ret = current_file.apply(values)
Patrick Mezard
patch: stop updating changed files set in applydiff()...
r14565 if ret > 0:
err = 1
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif state == b'file':
Patrick Mezard
patch: move closefile() into patchfile.close()
r13701 if current_file:
rejects += current_file.close()
Patrick Mezard
patch: stop handling hunkless git blocks out of stream...
r14388 current_file = None
afile, bfile, first_hunk, gp = values
if gp:
Patrick Mezard
patch: generalize the use of patchmeta in applydiff()...
r14566 gp.path = pstrip(gp.path)
Patrick Mezard
patch: use temporary files to handle intermediate copies...
r14452 if gp.oldpath:
Patrick Mezard
patch: generalize the use of patchmeta in applydiff()...
r14566 gp.oldpath = pstrip(gp.oldpath)
else:
Augie Fackler
formatting: blacken the codebase...
r43346 gp = makepatchmeta(
backend, afile, bfile, first_hunk, strip, prefix
)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if gp.op == b'RENAME':
Patrick Mezard
patch: generalize the use of patchmeta in applydiff()...
r14566 backend.unlink(gp.oldpath)
Patrick Mezard
patch: stop handling hunkless git blocks out of stream...
r14388 if not first_hunk:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if gp.op == b'DELETE':
Patrick Mezard
patch: generalize the use of patchmeta in applydiff()...
r14566 backend.unlink(gp.path)
continue
data, mode = None, None
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if gp.op in (b'RENAME', b'COPY'):
Patrick Mezard
patch: extend filtestore to store an optional copy source...
r14609 data, mode = store.getfile(gp.oldpath)[:2]
Ryan McElroy
import: abort instead of crashing when copy source does not exist (issue5375)...
r30078 if data is None:
# This means that the old path does not exist
Martin von Zweigbergk
errors: return more detailed errors when failing to parse or apply patch...
r49186 raise PatchApplicationError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b"source file '%s' does not exist") % gp.oldpath
Augie Fackler
formatting: blacken the codebase...
r43346 )
Patrick Mezard
patch: generalize the use of patchmeta in applydiff()...
r14566 if gp.mode:
mode = gp.mode
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if gp.op == b'ADD':
Patrick Mezard
patch: generalize the use of patchmeta in applydiff()...
r14566 # Added files without content have no hunk and
# must be created
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 data = b''
Patrick Mezard
patch: generalize the use of patchmeta in applydiff()...
r14566 if data or mode:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if gp.op in (b'ADD', b'RENAME', b'COPY') and backend.exists(
Augie Fackler
formatting: blacken the codebase...
r43346 gp.path
):
Martin von Zweigbergk
errors: return more detailed errors when failing to parse or apply patch...
r49186 raise PatchApplicationError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(
b"cannot create %s: destination "
b"already exists"
)
Augie Fackler
formatting: blacken the codebase...
r43346 % gp.path
)
Patrick Mezard
patch: generalize the use of patchmeta in applydiff()...
r14566 backend.setfile(gp.path, data, mode, gp.oldpath)
Patrick Mezard
patch: stop handling hunkless git blocks out of stream...
r14388 continue
Patrick Mezard
patch: move diff parsing in iterhunks generator
r5650 try:
Augie Fackler
formatting: blacken the codebase...
r43346 current_file = patcher(ui, gp, backend, store, eolmode=eolmode)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except PatchError as inst:
Matt Harbison
patch: fix a str + bytes issue in an exception handler...
r44127 ui.warn(stringutil.forcebytestr(inst) + b'\n')
Mads Kiilerich
patch: minor cleanup of _applydiff
r11021 current_file = None
Patrick Mezard
patch: move diff parsing in iterhunks generator
r5650 rejects += 1
continue
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif state == b'git':
Mads Kiilerich
patch: minor cleanup of _applydiff
r11021 for gp in values:
Patrick Mezard
patch: use temporary files to handle intermediate copies...
r14452 path = pstrip(gp.oldpath)
Mads Kiilerich
convert: use None value for missing files instead of overloading IOError...
r22296 data, mode = backend.getfile(path)
if data is None:
Patrick Mezard
patch: keep patching after missing copy source (issue3480)...
r16813 # The error ignored here will trigger a getfile()
# error in a place more appropriate for error
# handling, and will not interrupt the patching
# process.
Mads Kiilerich
convert: use None value for missing files instead of overloading IOError...
r22296 pass
Patrick Mezard
patch: keep patching after missing copy source (issue3480)...
r16813 else:
store.setfile(path, data, mode)
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.Abort(_(b'unsupported parser state: %s') % state)
Patrick Mezard
patch: move NoHunk detection up with parsing code
r5649
Patrick Mezard
patch: move closefile() into patchfile.close()
r13701 if current_file:
rejects += current_file.close()
Patrick Mezard
patch: move diff parsing in iterhunks generator
r5650
Bryan O'Sullivan
Add Chris Mason's mpatch library....
r4897 if rejects:
return -1
return err
Vadim Gelfer
refactor text diff/patch code....
r2874
Augie Fackler
formatting: blacken the codebase...
r43346
def _externalpatch(ui, repo, patcher, patchname, strip, files, similarity):
Patrick Mezard
patch: change functions definition order for readability
r7151 """use <patcher> to apply <patchname> to the working directory.
returns whether patch was applied with fuzz factor."""
fuzz = False
Patrick Mezard
patch: simplify externalpatch() arguments
r12673 args = []
Patrick Mezard
patch: remove patch.patch() cwd argument
r14382 cwd = repo.root
Patrick Mezard
patch: change functions definition order for readability
r7151 if cwd:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 args.append(b'-d %s' % procutil.shellquote(cwd))
cmd = b'%s %s -p%d < %s' % (
Augie Fackler
formatting: blacken the codebase...
r43346 patcher,
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b' '.join(args),
Augie Fackler
formatting: blacken the codebase...
r43346 strip,
procutil.shellquote(patchname),
)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.debug(b'Using external patch tool: %s\n' % cmd)
fp = procutil.popen(cmd, b'rb')
Patrick Mezard
patch: merge _updatedir() into externalpatch()
r14381 try:
Gregory Szorc
py3: stop using util.iterfile()...
r49796 for line in fp:
Patrick Mezard
patch: merge _updatedir() into externalpatch()
r14381 line = line.rstrip()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.note(line + b'\n')
if line.startswith(b'patching file '):
Patrick Mezard
patch: merge _updatedir() into externalpatch()
r14381 pf = util.parsepatchoutput(line)
printed_file = False
Patrick Mezard
patch: turn patch() touched files dict into a set
r14564 files.add(pf)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif line.find(b'with fuzz') >= 0:
Patrick Mezard
patch: merge _updatedir() into externalpatch()
r14381 fuzz = True
if not printed_file:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.warn(pf + b'\n')
Patrick Mezard
patch: merge _updatedir() into externalpatch()
r14381 printed_file = True
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.warn(line + b'\n')
elif line.find(b'saving rejects to file') >= 0:
ui.warn(line + b'\n')
elif line.find(b'FAILED') >= 0:
Patrick Mezard
patch: merge _updatedir() into externalpatch()
r14381 if not printed_file:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.warn(pf + b'\n')
Patrick Mezard
patch: merge _updatedir() into externalpatch()
r14381 printed_file = True
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.warn(line + b'\n')
Patrick Mezard
patch: merge _updatedir() into externalpatch()
r14381 finally:
if files:
Siddharth Agarwal
patch: use scmutil.marktouched instead of scmutil.addremove...
r19155 scmutil.marktouched(repo, files, similarity)
Patrick Mezard
patch: change functions definition order for readability
r7151 code = fp.close()
if code:
Martin von Zweigbergk
errors: return more detailed errors when failing to parse or apply patch...
r49186 raise PatchApplicationError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b"patch command failed: %s") % procutil.explainexit(code)
Augie Fackler
formatting: blacken the codebase...
r43346 )
Patrick Mezard
patch: change functions definition order for readability
r7151 return fuzz
Augie Fackler
formatting: blacken the codebase...
r43346
def patchbackend(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui, backend, patchobj, strip, prefix, files=None, eolmode=b'strict'
Augie Fackler
formatting: blacken the codebase...
r43346 ):
Benoit Boissinot
patch: don't use mutable object as default argument
r9683 if files is None:
Patrick Mezard
patch: turn patch() touched files dict into a set
r14564 files = set()
Patrick Mezard
Add patch.eol to ignore EOLs when patching (issue1019)...
r8810 if eolmode is None:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 eolmode = ui.config(b'patch', b'eol')
Martin Geisler
patch: propagate eolmode down to patchfile...
r10101 if eolmode.lower() not in eolmodes:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.Abort(_(b'unsupported line endings type: %s') % eolmode)
Martin Geisler
patch: propagate eolmode down to patchfile...
r10101 eolmode = eolmode.lower()
Dirkjan Ochtman
kill trailing whitespace
r8843
Patrick Mezard
patch: use temporary files to handle intermediate copies...
r14452 store = filestore()
Patrick Mezard
patch: change functions definition order for readability
r7151 try:
Matt Harbison
patch: stop using the `pycompat.open()` shim
r53276 fp = open(patchobj, 'rb')
Patrick Mezard
patch: change functions definition order for readability
r7151 except TypeError:
fp = patchobj
try:
Augie Fackler
formatting: blacken the codebase...
r43346 ret = applydiff(
ui, fp, backend, store, strip=strip, prefix=prefix, eolmode=eolmode
)
Patrick Mezard
patch: change functions definition order for readability
r7151 finally:
Patrick Mezard
patch: explicitely close input patch files when leaving...
r10203 if fp != patchobj:
fp.close()
Patrick Mezard
patch: turn patch() touched files dict into a set
r14564 files.update(backend.close())
Patrick Mezard
patch: use temporary files to handle intermediate copies...
r14452 store.close()
Patrick Mezard
patch: change functions definition order for readability
r7151 if ret < 0:
Martin von Zweigbergk
errors: return more detailed errors when failing to parse or apply patch...
r49186 raise PatchApplicationError(_(b'patch failed to apply'))
Patrick Mezard
patch: change functions definition order for readability
r7151 return ret > 0
Augie Fackler
formatting: blacken the codebase...
r43346
def internalpatch(
ui,
repo,
patchobj,
strip,
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 prefix=b'',
Augie Fackler
formatting: blacken the codebase...
r43346 files=None,
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 eolmode=b'strict',
Augie Fackler
formatting: blacken the codebase...
r43346 similarity=0,
):
Patrick Mezard
import: add --bypass option...
r14611 """use builtin patch to apply <patchobj> to the working directory.
returns whether patch was applied with fuzz factor."""
backend = workingbackend(ui, repo, similarity)
Siddharth Agarwal
patch.internalpatch: accept a prefix parameter
r24254 return patchbackend(ui, backend, patchobj, strip, prefix, files, eolmode)
Patrick Mezard
import: add --bypass option...
r14611
Augie Fackler
formatting: blacken the codebase...
r43346
def patchrepo(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui, repo, ctx, store, patchobj, strip, prefix, files=None, eolmode=b'strict'
Augie Fackler
formatting: blacken the codebase...
r43346 ):
Patrick Mezard
import: add --bypass option...
r14611 backend = repobackend(ui, repo, ctx, store)
Siddharth Agarwal
cmdutil.tryimportone: allow importing relative patches with --bypass
r24260 return patchbackend(ui, backend, patchobj, strip, prefix, files, eolmode)
Patrick Mezard
import: add --bypass option...
r14611
Augie Fackler
formatting: blacken the codebase...
r43346
def patch(
ui,
repo,
patchname,
strip=1,
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 prefix=b'',
Augie Fackler
formatting: blacken the codebase...
r43346 files=None,
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 eolmode=b'strict',
Augie Fackler
formatting: blacken the codebase...
r43346 similarity=0,
):
Patrick Mezard
Add patch.eol to ignore EOLs when patching (issue1019)...
r8810 """Apply <patchname> to the working directory.
'eolmode' specifies how end of lines should be handled. It can be:
- 'strict': inputs are read in binary mode, EOLs are preserved
- 'crlf': EOLs are ignored when patching and reset to CRLF
- 'lf': EOLs are ignored when patching and reset to LF
- None: get it from user settings, default to 'strict'
'eolmode' is ignored when using an external patcher program.
Returns whether patch was applied with fuzz factor.
"""
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 patcher = ui.config(b'ui', b'patch')
Benoit Boissinot
patch: don't use mutable object as default argument
r9683 if files is None:
Patrick Mezard
patch: turn patch() touched files dict into a set
r14564 files = set()
Pierre-Yves David
import: add --partial flag to create a changeset despite failed hunks...
r21553 if patcher:
Augie Fackler
formatting: blacken the codebase...
r43346 return _externalpatch(
ui, repo, patcher, patchname, strip, files, similarity
)
return internalpatch(
ui, repo, patchname, strip, prefix, files, eolmode, similarity
)
Patrick Mezard
patch: change functions definition order for readability
r7151
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 def changedfiles(ui, repo, patchpath, strip=1, prefix=b''):
Patrick Mezard
patch: add lexists() to backends, use it in selectfile()...
r14351 backend = fsbackend(ui, repo.root)
Martin von Zweigbergk
patch: accept prefix argument to changedfiles() helper...
r35053 prefix = _canonprefix(repo, prefix)
Matt Harbison
patch: stop using the `pycompat.open()` shim
r53276 with open(patchpath, 'rb') as fp:
Idan Kamara
patch: introduce changedfiles...
r14255 changed = set()
for state, values in iterhunks(fp):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if state == b'file':
Patrick Mezard
patch: stop handling hunkless git blocks out of stream...
r14388 afile, bfile, first_hunk, gp = values
if gp:
Martin von Zweigbergk
patch: accept prefix argument to changedfiles() helper...
r35053 gp.path = pathtransform(gp.path, strip - 1, prefix)[1]
Patrick Mezard
patch: generalize the use of patchmeta in applydiff()...
r14566 if gp.oldpath:
Augie Fackler
formatting: blacken the codebase...
r43346 gp.oldpath = pathtransform(
gp.oldpath, strip - 1, prefix
)[1]
Patrick Mezard
patch: generalize the use of patchmeta in applydiff()...
r14566 else:
Augie Fackler
formatting: blacken the codebase...
r43346 gp = makepatchmeta(
backend, afile, bfile, first_hunk, strip, prefix
)
Patrick Mezard
patch: generalize the use of patchmeta in applydiff()...
r14566 changed.add(gp.path)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if gp.op == b'RENAME':
Patrick Mezard
patch: generalize the use of patchmeta in applydiff()...
r14566 changed.add(gp.oldpath)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif state not in (b'hunk', b'git'):
raise error.Abort(_(b'unsupported parser state: %s') % state)
Idan Kamara
patch: introduce changedfiles...
r14255 return changed
Augie Fackler
formatting: blacken the codebase...
r43346
Patrick Mezard
patch: support diff data loss detection and upgrade...
r10189 class GitDiffRequired(Exception):
pass
Dirkjan Ochtman
patch: extract local function addmodehdr
r7198
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
diffutil: remove diffopts() in favor of diffallopts()...
r38606 diffopts = diffutil.diffallopts
Boris Feld
diffutil: extract diff options code into a dedicated util-module...
r38581 diffallopts = diffutil.diffallopts
difffeatureopts = diffutil.difffeatureopts
Benoit Boissinot
patch/diff: move diff related code next to each other
r10615
Augie Fackler
formatting: blacken the codebase...
r43346
def diff(
repo,
node1=None,
node2=None,
match=None,
changes=None,
opts=None,
losedatafn=None,
pathfn=None,
copy=None,
copysourcematch=None,
hunksfilterfn=None,
):
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """yields diff of changes to files between two nodes, or node and
Vadim Gelfer
refactor text diff/patch code....
r2874 working directory.
if node1 is None, use first dirstate parent instead.
Patrick Mezard
patch: support diff data loss detection and upgrade...
r10189 if node2 is None, compare node1 with working directory.
losedatafn(**kwarg) is a callable run when opts.upgrade=True and
every time some change cannot be represented with the current
patch format. Return False to upgrade to git patch format, True to
accept the loss or raise an exception to abort the diff. It is
called with the name of current file being diffed as 'fn'. If set
to None, patches will always be upgraded to git format when
necessary.
Martin Geisler
diff: recurse into subrepositories with --subrepos/-S flag
r12167
prefix is a filename prefix that is prepended to all filenames on
display (used for subrepos).
Siddharth Agarwal
patch.diff: add support for diffs relative to a subdirectory...
r24417
relroot, if not empty, must be normalized with a trailing /. Any match
Henrik Stuart
patch: allow copy information to be passed in...
r29422 patterns that fall outside it will be ignored.
copy, if not empty, should contain mappings {dst@y: src@x} of copy
Denis Laxalde
diff: pass a diff hunks filter function from changeset_printer to patch.diff()...
r34857 information.
Martin von Zweigbergk
patch: accept second matcher that applies only to copy sources (API)...
r41769 if copysourcematch is not None, then copy sources will be filtered by this
matcher
Denis Laxalde
diff: pass a diff hunks filter function from changeset_printer to patch.diff()...
r34857 hunksfilterfn, if not None, should be a function taking a filectx and
hunks generator that may yield filtered hunks.
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """
Martin von Zweigbergk
patch: pass in context objects into diffhunks() (API)...
r41767 if not node1 and not node2:
node1 = repo.dirstate.p1()
ctx1 = repo[node1]
ctx2 = repo[node2]
Denis Laxalde
diff: also yield file context objects in patch.trydiff() (API)...
r34856 for fctx1, fctx2, hdr, hunks in diffhunks(
Augie Fackler
formatting: blacken the codebase...
r43346 repo,
ctx1=ctx1,
ctx2=ctx2,
match=match,
changes=changes,
opts=opts,
losedatafn=losedatafn,
pathfn=pathfn,
copy=copy,
copysourcematch=copysourcematch,
):
Denis Laxalde
diff: pass a diff hunks filter function from changeset_printer to patch.diff()...
r34857 if hunksfilterfn is not None:
Denis Laxalde
log: add an assertion about fctx not being None in patch.diff()...
r34909 # If the file has been removed, fctx2 is None; but this should
# not occur here since we catch removed files early in
Yuya Nishihara
cmdutil: drop aliases for logcmdutil functions (API)...
r35906 # logcmdutil.getlinerangerevs() for 'hg log -L'.
Augie Fackler
formatting: blacken the codebase...
r43346 assert (
fctx2 is not None
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ), b'fctx2 unexpectly None in diff hunks filtering'
Denis Laxalde
diff: pass a diff hunks filter function from changeset_printer to patch.diff()...
r34857 hunks = hunksfilterfn(fctx2, hunks)
Elmar Bartel
diff: re-establish linear runtime performance...
r45266 text = b''.join(b''.join(hlines) for hrange, hlines in hunks)
Denis Laxalde
patch: rename "header" variable into "hdr" in diff()...
r34562 if hdr and (text or len(hdr) > 1):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield b'\n'.join(hdr) + b'\n'
Denis Laxalde
patch: add a diffhunks function yielding (diffheaders, hunks)...
r31274 if text:
yield text
Augie Fackler
formatting: blacken the codebase...
r43346
def diffhunks(
repo,
ctx1,
ctx2,
match=None,
changes=None,
opts=None,
losedatafn=None,
pathfn=None,
copy=None,
copysourcematch=None,
):
Denis Laxalde
patch: add a diffhunks function yielding (diffheaders, hunks)...
r31274 """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.
See diff() for the meaning of parameters.
"""
Vadim Gelfer
refactor text diff/patch code....
r2874
if opts is None:
opts = mdiff.defaultopts
Brendan Cully
Make patch.diff filelog cache LRU of 20 files. Fixes issue1738....
r9123 def lrugetfilectx():
cache = {}
Martin von Zweigbergk
util: drop alias for collections.deque...
r25113 order = collections.deque()
Augie Fackler
formatting: blacken the codebase...
r43346
Brendan Cully
Make patch.diff filelog cache LRU of 20 files. Fixes issue1738....
r9123 def getfilectx(f, ctx):
fctx = ctx.filectx(f, filelog=cache.get(f))
if f not in cache:
if len(cache) > 20:
Bryan O'Sullivan
cleanup: use the deque type where appropriate...
r16803 del cache[order.popleft()]
Benoit Boissinot
patch: use the public ctx API instead of the internals
r9684 cache[f] = fctx.filelog()
Brendan Cully
Make patch.diff filelog cache LRU of 20 files. Fixes issue1738....
r9123 else:
order.remove(f)
order.append(f)
return fctx
Augie Fackler
formatting: blacken the codebase...
r43346
Brendan Cully
Make patch.diff filelog cache LRU of 20 files. Fixes issue1738....
r9123 return getfilectx
Augie Fackler
formatting: blacken the codebase...
r43346
Brendan Cully
Make patch.diff filelog cache LRU of 20 files. Fixes issue1738....
r9123 getfilectx = lrugetfilectx()
Brendan Cully
Teach mq about git patches
r2934
Vadim Gelfer
refactor text diff/patch code....
r2874 if not changes:
Martin von Zweigbergk
patch: use ctx1.status(ctx2) instead of repo.status(ctx1, ctx2)...
r38796 changes = ctx1.status(ctx2, match=match)
Augie Fackler
patch: use field names instead of field numbers on scmutil.status...
r44048 if isinstance(changes, list):
modified, added, removed = changes[:3]
else:
modified, added, removed = (
changes.modified,
changes.added,
changes.removed,
)
Vadim Gelfer
refactor text diff/patch code....
r2874
if not modified and not added and not removed:
Patrick Mezard
patch: support diff data loss detection and upgrade...
r10189 return []
Jordi Gutiérrez Hermoso
style: kill ersatz if-else ternary operators...
r24306 if repo.ui.debugflag:
hexfunc = hex
else:
hexfunc = short
Sean Farley
patch: use ctx.node() instead of bare node variable...
r21833 revs = [hexfunc(node) for node in [ctx1.node(), ctx2.node()] if node]
Patrick Mezard
patch: support diff data loss detection and upgrade...
r10189
Henrik Stuart
patch: allow copy information to be passed in...
r29422 if copy is None:
copy = {}
if opts.git or opts.upgrade:
copy = copies.pathcopies(ctx1, ctx2, match=match)
Patrick Mezard
patch: support diff data loss detection and upgrade...
r10189
Martin von Zweigbergk
patch: accept second matcher that applies only to copy sources (API)...
r41769 if copysourcematch:
# filter out copies where source side isn't inside the matcher
Martin von Zweigbergk
diff: drop duplicate filter of copies by destination...
r41640 # (copies.pathcopies() already filtered out the destination)
Gregory Szorc
global: bulk replace simple pycompat.iteritems(x) with x.items()...
r49768 copy = {dst: src for dst, src in copy.items() if copysourcematch(src)}
Siddharth Agarwal
patch.diff: add support for diffs relative to a subdirectory...
r24417
Martin von Zweigbergk
diff: move status fixup earlier, out of _filepairs()...
r27900 modifiedset = set(modified)
addedset = set(added)
Martin von Zweigbergk
diff: don't crash when merged-in addition was removed (issue4786)...
r27901 removedset = set(removed)
Martin von Zweigbergk
diff: move status fixup earlier, out of _filepairs()...
r27900 for f in modified:
if f not in ctx1:
# Fix up added, since merged-in additions appear as
# modifications during merges
modifiedset.remove(f)
addedset.add(f)
Martin von Zweigbergk
diff: don't crash when merged-in addition was removed (issue4786)...
r27901 for f in removed:
if f not in ctx1:
# Merged-in additions that are then removed are reported as removed.
# They are not in ctx1, so We don't want to show them in the diff.
removedset.remove(f)
Martin von Zweigbergk
diff: move status fixup earlier, out of _filepairs()...
r27900 modified = sorted(modifiedset)
added = sorted(addedset)
Martin von Zweigbergk
diff: don't crash when merged-in addition was removed (issue4786)...
r27901 removed = sorted(removedset)
Pulkit Goyal
py3: use list() to get a list of items using dict.items()...
r35607 for dst, src in list(copy.items()):
Martin von Zweigbergk
diff: don't crash when merged-in addition is copied...
r27902 if src not in ctx1:
# Files merged in during a merge and then copied/renamed are
# reported as copies. We want to show them in the diff as additions.
del copy[dst]
Martin von Zweigbergk
diff: move status fixup earlier, out of _filepairs()...
r27900
Matt Harbison
diff: invoke the file prefetch hook...
r37782 prefetchmatch = scmutil.matchfiles(
Augie Fackler
formatting: blacken the codebase...
r43346 repo, list(modifiedset | addedset | removedset)
)
Rodrigo Damazio Bovendorp
scmutil: allowing different files to be prefetched per revision...
r45632 revmatches = [
(ctx1.rev(), prefetchmatch),
(ctx2.rev(), prefetchmatch),
]
scmutil.prefetchfiles(repo, revmatches)
Matt Harbison
diff: invoke the file prefetch hook...
r37782
Mads Kiilerich
check-code: indent 4 spaces in py files
r17299 def difffn(opts, losedata):
Augie Fackler
formatting: blacken the codebase...
r43346 return trydiff(
repo,
revs,
ctx1,
ctx2,
modified,
added,
removed,
copy,
getfilectx,
opts,
losedata,
pathfn,
)
Patrick Mezard
patch: support diff data loss detection and upgrade...
r10189 if opts.upgrade and not opts.git:
try:
Augie Fackler
formatting: blacken the codebase...
r43346
Patrick Mezard
patch: support diff data loss detection and upgrade...
r10189 def losedata(fn):
if not losedatafn or not losedatafn(fn=fn):
Brodie Rao
cleanup: "raise SomeException()" -> "raise SomeException"
r16687 raise GitDiffRequired
Augie Fackler
formatting: blacken the codebase...
r43346
Patrick Mezard
patch: support diff data loss detection and upgrade...
r10189 # Buffer the whole output until we are sure it can be generated
return list(difffn(opts.copy(git=False), losedata))
except GitDiffRequired:
return difffn(opts.copy(git=True), None)
else:
return difffn(opts, None)
Augie Fackler
formatting: blacken the codebase...
r43346
Jun Wu
patch: buffer lines for a same hunk...
r37749 def diffsinglehunk(hunklines):
"""yield tokens for a list of lines in a single hunk"""
for line in hunklines:
# chomp
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 chompline = line.rstrip(b'\r\n')
Jun Wu
patch: buffer lines for a same hunk...
r37749 # highlight tabs and trailing whitespace
stripline = chompline.rstrip()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if line.startswith(b'-'):
label = b'diff.deleted'
elif line.startswith(b'+'):
label = b'diff.inserted'
Jun Wu
patch: buffer lines for a same hunk...
r37749 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ProgrammingError(b'unexpected hunk line: %s' % line)
Jun Wu
patch: buffer lines for a same hunk...
r37749 for token in tabsplitter.findall(stripline):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if token.startswith(b'\t'):
yield (token, b'diff.tab')
Jun Wu
patch: buffer lines for a same hunk...
r37749 else:
yield (token, label)
if chompline != stripline:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield (chompline[len(stripline) :], b'diff.trailingwhitespace')
Jun Wu
patch: buffer lines for a same hunk...
r37749 if chompline != line:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield (line[len(chompline) :], b'')
Augie Fackler
formatting: blacken the codebase...
r43346
Jun Wu
patch: buffer lines for a same hunk...
r37749
Jun Wu
patch: implement a new worddiff algorithm...
r37750 def diffsinglehunkinline(hunklines):
"""yield tokens for a list of lines in a single hunk, with inline colors"""
# prepare deleted, and inserted content
Yuya Nishihara
diff: do not concatenate immutable bytes while building a/b bodies (issue6445)...
r46624 a = bytearray()
b = bytearray()
Jun Wu
patch: implement a new worddiff algorithm...
r37750 for line in hunklines:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if line[0:1] == b'-':
Jun Wu
patch: implement a new worddiff algorithm...
r37750 a += line[1:]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif line[0:1] == b'+':
Jun Wu
patch: implement a new worddiff algorithm...
r37750 b += line[1:]
else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ProgrammingError(b'unexpected hunk line: %s' % line)
Jun Wu
patch: implement a new worddiff algorithm...
r37750 # fast path: if either side is empty, use diffsinglehunk
if not a or not b:
for t in diffsinglehunk(hunklines):
yield t
return
# re-split the content into words
Yuya Nishihara
diff: do not concatenate immutable bytes while building a/b bodies (issue6445)...
r46624 al = wordsplitter.findall(bytes(a))
bl = wordsplitter.findall(bytes(b))
Jun Wu
patch: implement a new worddiff algorithm...
r37750 # re-arrange the words to lines since the diff algorithm is line-based
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 aln = [s if s == b'\n' else s + b'\n' for s in al]
bln = [s if s == b'\n' else s + b'\n' for s in bl]
an = b''.join(aln)
bn = b''.join(bln)
Jun Wu
patch: implement a new worddiff algorithm...
r37750 # run the diff algorithm, prepare atokens and btokens
atokens = []
btokens = []
blocks = mdiff.allblocks(an, bn, lines1=aln, lines2=bln)
for (a1, a2, b1, b2), btype in blocks:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 changed = btype == b'!'
for token in mdiff.splitnewlines(b''.join(al[a1:a2])):
Jun Wu
patch: implement a new worddiff algorithm...
r37750 atokens.append((changed, token))
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 for token in mdiff.splitnewlines(b''.join(bl[b1:b2])):
Jun Wu
patch: implement a new worddiff algorithm...
r37750 btokens.append((changed, token))
# yield deleted tokens, then inserted ones
Augie Fackler
formatting: blacken the codebase...
r43346 for prefix, label, tokens in [
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 (b'-', b'diff.deleted', atokens),
(b'+', b'diff.inserted', btokens),
Augie Fackler
formatting: blacken the codebase...
r43346 ]:
Jun Wu
patch: implement a new worddiff algorithm...
r37750 nextisnewline = True
for changed, token in tokens:
if nextisnewline:
yield (prefix, label)
nextisnewline = False
# special handling line end
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 isendofline = token.endswith(b'\n')
Jun Wu
patch: implement a new worddiff algorithm...
r37750 if isendofline:
Augie Fackler
formatting: blacken the codebase...
r43346 chomp = token[:-1] # chomp
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if chomp.endswith(b'\r'):
Sune Foldager
patch: don't separate \r and \n when colorizing diff output...
r38649 chomp = chomp[:-1]
Augie Fackler
formatting: blacken the codebase...
r43346 endofline = token[len(chomp) :]
token = chomp.rstrip() # detect spaces at the end
endspaces = chomp[len(token) :]
Jun Wu
patch: implement a new worddiff algorithm...
r37750 # scan tabs
for maybetab in tabsplitter.findall(token):
Mark Thomas
py3: fix test-diff-color.t...
r40318 if b'\t' == maybetab[0:1]:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 currentlabel = b'diff.tab'
Jun Wu
patch: implement a new worddiff algorithm...
r37750 else:
if changed:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 currentlabel = label + b'.changed'
Jun Wu
patch: implement a new worddiff algorithm...
r37750 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 currentlabel = label + b'.unchanged'
Jun Wu
patch: implement a new worddiff algorithm...
r37750 yield (maybetab, currentlabel)
if isendofline:
if endspaces:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield (endspaces, b'diff.trailingwhitespace')
yield (endofline, b'')
Jun Wu
patch: implement a new worddiff algorithm...
r37750 nextisnewline = True
Augie Fackler
formatting: blacken the codebase...
r43346
Brodie Rao
diff: make use of output labeling
r10818 def difflabel(func, *args, **kw):
'''yields 2-tuples of (output, label) based on the output of func()'''
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 if kw.get('opts') and kw['opts'].worddiff:
Jun Wu
patch: implement a new worddiff algorithm...
r37750 dodiffhunk = diffsinglehunkinline
else:
dodiffhunk = diffsinglehunk
Augie Fackler
formatting: blacken the codebase...
r43346 headprefixes = [
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 (b'diff', b'diff.diffline'),
(b'copy', b'diff.extended'),
(b'rename', b'diff.extended'),
(b'old', b'diff.extended'),
(b'new', b'diff.extended'),
(b'deleted', b'diff.extended'),
(b'index', b'diff.extended'),
(b'similarity', b'diff.extended'),
(b'---', b'diff.file_a'),
(b'+++', b'diff.file_b'),
Augie Fackler
formatting: blacken the codebase...
r43346 ]
textprefixes = [
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 (b'@', b'diff.hunk'),
Augie Fackler
formatting: blacken the codebase...
r43346 # - and + are handled by diffsinglehunk
]
Kirill Elagin
diff: enhance highlighting with color (issue3034)...
r15201 head = False
Jun Wu
patch: buffer lines for a same hunk...
r37749
# buffers a hunk, i.e. adjacent "-", "+" lines without other changes.
hunkbuffer = []
Augie Fackler
formatting: blacken the codebase...
r43346
Jun Wu
patch: buffer lines for a same hunk...
r37749 def consumehunkbuffer():
if hunkbuffer:
Jun Wu
patch: implement a new worddiff algorithm...
r37750 for token in dodiffhunk(hunkbuffer):
Jun Wu
patch: buffer lines for a same hunk...
r37749 yield token
hunkbuffer[:] = []
Brodie Rao
diff: make use of output labeling
r10818 for chunk in func(*args, **kw):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 lines = chunk.split(b'\n')
Jun Wu
patch: move yielding "\n" to the end of loop...
r37748 linecount = len(lines)
Brodie Rao
diff: make use of output labeling
r10818 for i, line in enumerate(lines):
Kirill Elagin
diff: enhance highlighting with color (issue3034)...
r15201 if head:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if line.startswith(b'@'):
Kirill Elagin
diff: enhance highlighting with color (issue3034)...
r15201 head = False
else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if line and not line.startswith(
(b' ', b'+', b'-', b'@', b'\\')
):
Kirill Elagin
diff: enhance highlighting with color (issue3034)...
r15201 head = True
Jordi Gutiérrez Hermoso
patch: enable diff.tab markup for the color extension...
r22460 diffline = False
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if not head and line and line.startswith((b'+', b'-')):
Jordi Gutiérrez Hermoso
patch: enable diff.tab markup for the color extension...
r22460 diffline = True
Kirill Elagin
diff: enhance highlighting with color (issue3034)...
r15201 prefixes = textprefixes
if head:
prefixes = headprefixes
Jun Wu
patch: buffer lines for a same hunk...
r37749 if diffline:
# buffered
bufferedline = line
if i + 1 < linecount:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 bufferedline += b"\n"
Jun Wu
patch: buffer lines for a same hunk...
r37749 hunkbuffer.append(bufferedline)
Brodie Rao
diff: make use of output labeling
r10818 else:
Jun Wu
patch: buffer lines for a same hunk...
r37749 # unbuffered
for token in consumehunkbuffer():
yield token
stripline = line.rstrip()
for prefix, label in prefixes:
if stripline.startswith(prefix):
yield (stripline, label)
if line != stripline:
Augie Fackler
formatting: blacken the codebase...
r43346 yield (
line[len(stripline) :],
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'diff.trailingwhitespace',
Augie Fackler
formatting: blacken the codebase...
r43346 )
Matthieu Laneuville
patch: add within-line color diff capacity...
r35278 break
Jun Wu
patch: buffer lines for a same hunk...
r37749 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield (line, b'')
Jun Wu
patch: buffer lines for a same hunk...
r37749 if i + 1 < linecount:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield (b'\n', b'')
Jun Wu
patch: buffer lines for a same hunk...
r37749 for token in consumehunkbuffer():
yield token
Matthieu Laneuville
patch: add within-line color diff capacity...
r35278
Augie Fackler
formatting: blacken the codebase...
r43346
Brodie Rao
diff: make use of output labeling
r10818 def diffui(*args, **kw):
'''like diff(), but yields 2-tuples of (output, label) for ui.write()'''
return difflabel(diff, *args, **kw)
Augie Fackler
formatting: blacken the codebase...
r43346
Martin von Zweigbergk
diff: move status fixup earlier, out of _filepairs()...
r27900 def _filepairs(modified, added, removed, copy, opts):
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """generates tuples (f1, f2, copyop), where f1 is the name of the file
Martin von Zweigbergk
trydiff: extract function that generates filename pairs...
r24106 before and f2 is the the name after. For added files, f1 will be None,
and for removed files, f2 will be None. copyop may be set to None, 'copy'
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 or 'rename' (the latter two only if opts.git is set)."""
Martin von Zweigbergk
trydiff: extract function that generates filename pairs...
r24106 gone = set()
Augie Fackler
cleanup: run pyupgrade on our source tree to clean up varying things...
r44937 copyto = {v: k for k, v in copy.items()}
Martin von Zweigbergk
trydiff: extract function that generates filename pairs...
r24106
addedset, removedset = set(added), set(removed)
for f in sorted(modified + added + removed):
copyop = None
f1, f2 = f, f
if f in addedset:
f1 = None
if f in copy:
if opts.git:
f1 = copy[f]
if f1 in removedset and f1 not in gone:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 copyop = b'rename'
Martin von Zweigbergk
trydiff: extract function that generates filename pairs...
r24106 gone.add(f1)
else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 copyop = b'copy'
Martin von Zweigbergk
trydiff: extract function that generates filename pairs...
r24106 elif f in removedset:
f2 = None
if opts.git:
# have we already reported a copy above?
Augie Fackler
formatting: blacken the codebase...
r43346 if (
f in copyto
and copyto[f] in addedset
and copy[copyto[f]] == f
):
Martin von Zweigbergk
trydiff: extract function that generates filename pairs...
r24106 continue
yield f1, f2, copyop
Augie Fackler
formatting: blacken the codebase...
r43346
Pulkit Goyal
patch: refactor content diffing part in separate fn so extensions can wrap...
r45658 def _gitindex(text):
if not text:
text = b""
l = len(text)
s = hashutil.sha1(b'blob %d\0' % l)
s.update(text)
return hex(s.digest())
_gitmode = {b'l': b'120000', b'x': b'100755', b'': b'100644'}
Augie Fackler
formatting: blacken the codebase...
r43346 def trydiff(
repo,
revs,
ctx1,
ctx2,
modified,
added,
removed,
copy,
getfilectx,
opts,
losedatafn,
pathfn,
):
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """given input data, generate a diff and yield it in blocks
Siddharth Agarwal
patch.trydiff: add a docstring...
r24371
If generating a diff would lose data like flags or binary data and
losedatafn is not None, it will be called.
Martin von Zweigbergk
patch: replace "prefix" and "relroot" arguments by "pathfn" (API)...
r41795 pathfn is applied to every path in the diff output.
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """
Martin Geisler
diff: recurse into subrepositories with --subrepos/-S flag
r12167
Siddharth Agarwal
patch.trydiff: add support for noprefix
r23300 if opts.noprefix:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 aprefix = bprefix = b''
Siddharth Agarwal
patch.trydiff: add support for noprefix
r23300 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 aprefix = b'a/'
bprefix = b'b/'
Siddharth Agarwal
patch.trydiff: add support for noprefix
r23300
Martin von Zweigbergk
trydiff: remove unused argument to diffline()...
r24021 def diffline(f, revs):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 revinfo = b' '.join([b"-r %s" % rev for rev in revs])
return b'diff %s %s' % (revinfo, f)
Guillermo Pérez
diff: move diffline to patch module...
r17941
Jun Wu
diff: use fctx.size() to test empty...
r32188 def isempty(fctx):
return fctx is None or fctx.size() == 0
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 date1 = dateutil.datestr(ctx1.date())
date2 = dateutil.datestr(ctx2.date())
Benoit Boissinot
patch: use contexts for diff
r3967
Martin von Zweigbergk
patch: replace "prefix" and "relroot" arguments by "pathfn" (API)...
r41795 if not pathfn:
pathfn = lambda f: f
Siddharth Agarwal
patch.trydiff: add support for stripping a relative root...
r24416
Martin von Zweigbergk
diff: move status fixup earlier, out of _filepairs()...
r27900 for f1, f2, copyop in _filepairs(modified, added, removed, copy, opts):
Martin von Zweigbergk
trydiff: read file data in only one place...
r24105 content1 = None
content2 = None
Jun Wu
diff: use fctx.isbinary() to test binary...
r32187 fctx1 = None
fctx2 = None
Martin von Zweigbergk
trydiff: read flags in one place...
r24103 flag1 = None
flag2 = None
Martin von Zweigbergk
trydiff: transpose 'if opts.git or losedatafn' with 'if f[12]'...
r24107 if f1:
Jun Wu
diff: use fctx.isbinary() to test binary...
r32187 fctx1 = getfilectx(f1, ctx1)
Martin von Zweigbergk
trydiff: transpose 'if opts.git or losedatafn' with 'if f[12]'...
r24107 if opts.git or losedatafn:
flag1 = ctx1.flags(f1)
if f2:
Jun Wu
diff: use fctx.isbinary() to test binary...
r32187 fctx2 = getfilectx(f2, ctx2)
Martin von Zweigbergk
trydiff: transpose 'if opts.git or losedatafn' with 'if f[12]'...
r24107 if opts.git or losedatafn:
flag2 = ctx2.flags(f2)
Jun Wu
diff: correct binary testing logic...
r32190 # if binary is True, output "summary" or "base85", but not "text diff"
Joerg Sonnenberger
patch: avoid repeated binary checks if all files in a patch are text...
r35868 if opts.text:
binary = False
else:
Yuya Nishihara
patch: unify check_binary and binary flags...
r35969 binary = any(f.isbinary() for f in [fctx1, fctx2] if f is not None)
Martin von Zweigbergk
trydiff: collect all lossiness checks in one place...
r24057
if losedatafn and not opts.git:
Augie Fackler
formatting: blacken the codebase...
r43346 if (
binary
or
Martin von Zweigbergk
trydiff: collect all lossiness checks in one place...
r24057 # copy/rename
Augie Fackler
formatting: blacken the codebase...
r43346 f2 in copy
or
Martin von Zweigbergk
trydiff: collect all lossiness checks in one place...
r24057 # empty file creation
Augie Fackler
formatting: blacken the codebase...
r43346 (not f1 and isempty(fctx2))
or
Martin von Zweigbergk
trydiff: collect all lossiness checks in one place...
r24057 # empty file deletion
Augie Fackler
formatting: blacken the codebase...
r43346 (isempty(fctx1) and not f2)
or
Martin von Zweigbergk
trydiff: collect all lossiness checks in one place...
r24057 # create with flags
Augie Fackler
formatting: blacken the codebase...
r43346 (not f1 and flag2)
or
Martin von Zweigbergk
trydiff: collect all lossiness checks in one place...
r24057 # change flags
Augie Fackler
formatting: blacken the codebase...
r43346 (f1 and f2 and flag1 != flag2)
):
Martin von Zweigbergk
trydiff: extract function that generates filename pairs...
r24106 losedatafn(f2 or f1)
Patrick Mezard
patch: support diff data loss detection and upgrade...
r10189
Martin von Zweigbergk
patch: replace "prefix" and "relroot" arguments by "pathfn" (API)...
r41795 path1 = pathfn(f1 or f2)
path2 = pathfn(f2 or f1)
Martin von Zweigbergk
trydiff: collect header-writing in one place...
r23998 header = []
Martin von Zweigbergk
trydiff: make 'revs' ignored if opts.git is set...
r24022 if opts.git:
Augie Fackler
formatting: blacken the codebase...
r43346 header.append(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'diff --git %s%s %s%s' % (aprefix, path1, bprefix, path2)
Augie Fackler
formatting: blacken the codebase...
r43346 )
if not f1: # added
Pulkit Goyal
patch: refactor content diffing part in separate fn so extensions can wrap...
r45658 header.append(b'new file mode %s' % _gitmode[flag2])
Augie Fackler
formatting: blacken the codebase...
r43346 elif not f2: # removed
Pulkit Goyal
patch: refactor content diffing part in separate fn so extensions can wrap...
r45658 header.append(b'deleted file mode %s' % _gitmode[flag1])
Martin von Zweigbergk
trydiff: collect header-writing in one place...
r23998 else: # modified/copied/renamed
Pulkit Goyal
patch: refactor content diffing part in separate fn so extensions can wrap...
r45658 mode1, mode2 = _gitmode[flag1], _gitmode[flag2]
Martin von Zweigbergk
trydiff: inline sole addmodehdr() call...
r24000 if mode1 != mode2:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 header.append(b'old mode %s' % mode1)
header.append(b'new mode %s' % mode2)
Martin von Zweigbergk
trydiff: rename 'op' to make it more specific...
r24055 if copyop is not None:
Sean Farley
patch: use opt.showsimilarity to calculate and show the similarity...
r30807 if opts.showsimilarity:
sim = similar.score(ctx1[path1], ctx2[path2]) * 100
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 header.append(b'similarity index %d%%' % sim)
header.append(b'%s from %s' % (copyop, path1))
header.append(b'%s to %s' % (copyop, path2))
Pulkit Goyal
diff: make sure we output stat even when --git is not passed (issue4037) (BC)...
r41950 elif revs:
Martin von Zweigbergk
trydiff: make 'revs' ignored if opts.git is set...
r24022 header.append(diffline(path1, revs))
Martin von Zweigbergk
trydiff: collect header-writing in one place...
r23998
Jun Wu
diff: draw a table about binary diff behaviors...
r32189 # fctx.is | diffopts | what to | is fctx.data()
# binary() | text nobinary git index | output? | outputted?
# ------------------------------------|----------------------------
# yes | no no no * | summary | no
# yes | no no yes * | base85 | yes
# yes | no yes no * | summary | no
# yes | no yes yes 0 | summary | no
# yes | no yes yes >0 | summary | semi [1]
# yes | yes * * * | text diff | yes
# no | * * * * | text diff | yes
# [1]: hash(fctx.data()) is outputted. so fctx.data() cannot be faked
Augie Fackler
formatting: blacken the codebase...
r43346 if binary and (
not opts.git or (opts.git and opts.nobinary and not opts.index)
):
Jun Wu
diff: add a fast path to avoid loading binary contents...
r32191 # fast path: no binary content will be displayed, content1 and
# content2 are only used for equivalent test. cmp() could have a
# fast path.
if fctx1 is not None:
content1 = b'\0'
if fctx2 is not None:
if fctx1 is not None and not fctx1.cmp(fctx2):
Augie Fackler
formatting: blacken the codebase...
r43346 content2 = b'\0' # not different
Jun Wu
diff: add a fast path to avoid loading binary contents...
r32191 else:
content2 = b'\0\0'
else:
# normal path: load contents
if fctx1 is not None:
content1 = fctx1.data()
if fctx2 is not None:
content2 = fctx2.data()
Pulkit Goyal
patch: refactor content diffing part in separate fn so extensions can wrap...
r45658 data1 = (ctx1, fctx1, path1, flag1, content1, date1)
data2 = (ctx2, fctx2, path2, flag2, content2, date2)
yield diffcontent(data1, data2, header, binary, opts)
def diffcontent(data1, data2, header, binary, opts):
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """diffs two versions of a file.
Pulkit Goyal
patch: refactor content diffing part in separate fn so extensions can wrap...
r45658
data1 and data2 are tuples containg:
* ctx: changeset for the file
* fctx: file context for that file
* path1: name of the file
* flag: flags of the file
* content: full content of the file (can be null in case of binary)
* date: date of the changeset
header: the patch header
binary: whether the any of the version of file is binary or not
opts: user passed options
It exists as a separate function so that extensions like extdiff can wrap
it and use the file content directly.
"""
ctx1, fctx1, path1, flag1, content1, date1 = data1
ctx2, fctx2, path2, flag2, content2, date2 = data2
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 index1 = _gitindex(content1) if path1 in ctx1 else sha1nodeconstants.nullhex
index2 = _gitindex(content2) if path2 in ctx2 else sha1nodeconstants.nullhex
Pulkit Goyal
patch: refactor content diffing part in separate fn so extensions can wrap...
r45658 if binary and opts.git and not opts.nobinary:
text = mdiff.b85diff(content1, content2)
if text:
Sushil khanchi
patch: fix a formatting issue...
r47364 header.append(b'index %s..%s' % (index1, index2))
Pulkit Goyal
patch: refactor content diffing part in separate fn so extensions can wrap...
r45658 hunks = ((None, [text]),)
else:
if opts.git and opts.index > 0:
flag = flag1
if flag is None:
flag = flag2
header.append(
b'index %s..%s %s'
% (
Sushil khanchi
patch: make diff --git to differentiate b/w file is empty or doesn't exists...
r47279 index1[0 : opts.index],
index2[0 : opts.index],
Pulkit Goyal
patch: refactor content diffing part in separate fn so extensions can wrap...
r45658 _gitmode[flag],
Augie Fackler
formatting: blacken the codebase...
r43346 )
)
Pulkit Goyal
patch: refactor content diffing part in separate fn so extensions can wrap...
r45658
uheaders, hunks = mdiff.unidiff(
content1,
date1,
content2,
date2,
path1,
path2,
binary=binary,
opts=opts,
)
header.extend(uheaders)
return fctx1, fctx2, header, hunks
Vadim Gelfer
refactor text diff/patch code....
r2874
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
patch: add diffstatsum helper
r14401 def diffstatsum(stats):
Steven Brown
patch: restore the previous output of 'diff --stat'...
r14437 maxfile, maxtotal, addtotal, removetotal, binary = 0, 0, 0, 0, False
Matt Mackall
patch: add diffstatsum helper
r14401 for f, a, r, b in stats:
maxfile = max(maxfile, encoding.colwidth(f))
Steven Brown
patch: restore the previous output of 'diff --stat'...
r14437 maxtotal = max(maxtotal, a + r)
Matt Mackall
patch: add diffstatsum helper
r14401 addtotal += a
removetotal += r
binary = binary or b
Steven Brown
patch: restore the previous output of 'diff --stat'...
r14437 return maxfile, maxtotal, addtotal, removetotal, binary
Matt Mackall
patch: add diffstatsum helper
r14401
Augie Fackler
formatting: blacken the codebase...
r43346
Alexander Solovyov
python implementation of diffstat...
r7547 def diffstatdata(lines):
Gregory Szorc
global: use raw strings for regular expressions with escapes...
r41673 diffre = re.compile(br'^diff .*-r [a-z0-9]+\s(.*)$')
Gastón Kleiman
diffstat: fix parsing of filenames with spaces...
r13395
Matt Mackall
diffstatdata: no longer a generator...
r14400 results = []
Patrick Mezard
diffstat: be more picky when marking file as 'binary' (issue2816)...
r15363 filename, adds, removes, isbinary = None, 0, 0, False
Matt Mackall
diffstatdata: no longer a generator...
r14400
def addresult():
if filename:
results.append((filename, adds, removes, isbinary))
Andrew Zwicky
diffstat: properly count lines starting in '--' or '++' (issue5479)...
r32321 # inheader is used to track if a line is in the
# header portion of the diff. This helps properly account
# for lines that start with '--' or '++'
inheader = False
Alexander Solovyov
python implementation of diffstat...
r7547 for line in lines:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if line.startswith(b'diff'):
Matt Mackall
diffstatdata: no longer a generator...
r14400 addresult()
Andrew Zwicky
diffstat: properly count lines starting in '--' or '++' (issue5479)...
r32321 # starting a new file diff
# set numbers to 0 and reset inheader
inheader = True
Patrick Mezard
diffstat: be more picky when marking file as 'binary' (issue2816)...
r15363 adds, removes, isbinary = 0, 0, False
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if line.startswith(b'diff --git a/'):
Matt Mackall
diff: use second filename for --stat reporting on git patches (issue4221)
r20972 filename = gitre.search(line).group(2)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif line.startswith(b'diff -r'):
timeless
Generally replace "file name" with "filename" in help and comments.
r8761 # format: "diff -r ... -r ... filename"
Gastón Kleiman
diffstat: fix parsing of filenames with spaces...
r13395 filename = diffre.search(line).group(1)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif line.startswith(b'@@'):
Andrew Zwicky
diffstat: properly count lines starting in '--' or '++' (issue5479)...
r32321 inheader = False
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif line.startswith(b'+') and not inheader:
Alexander Solovyov
python implementation of diffstat...
r7547 adds += 1
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif line.startswith(b'-') and not inheader:
Alexander Solovyov
python implementation of diffstat...
r7547 removes += 1
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif line.startswith(b'GIT binary patch') or line.startswith(
b'Binary file'
Augie Fackler
formatting: blacken the codebase...
r43346 ):
Patrick Mezard
diffstat: be more picky when marking file as 'binary' (issue2816)...
r15363 isbinary = True
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif line.startswith(b'rename from'):
Navaneeth Suresh
diffstat: support filenames with whitespaces on renames...
r41446 filename = line[12:]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif line.startswith(b'rename to'):
filename += b' => %s' % line[10:]
Matt Mackall
diffstatdata: no longer a generator...
r14400 addresult()
return results
Alexander Solovyov
python implementation of diffstat...
r7547
Augie Fackler
formatting: blacken the codebase...
r43346
Henning Schild
patch: remove unused git parameter from patch.diffstat()...
r30407 def diffstat(lines, width=80):
Alexander Solovyov
python implementation of diffstat...
r7547 output = []
Matt Mackall
patch: use diffstatsum in diffstat
r14402 stats = diffstatdata(lines)
Steven Brown
patch: restore the previous output of 'diff --stat'...
r14437 maxname, maxtotal, totaladds, totalremoves, hasbinary = diffstatsum(stats)
Alexander Solovyov
python implementation of diffstat...
r7547
countwidth = len(str(maxtotal))
Brodie Rao
diffstat: with --git, mark binary files with Bin...
r9642 if hasbinary and countwidth < 3:
countwidth = 3
Brodie Rao
diffstat: scale adds/removes proportionally to graph width...
r9330 graphwidth = width - countwidth - maxname - 6
Alexander Solovyov
python implementation of diffstat...
r7547 if graphwidth < 10:
graphwidth = 10
Brodie Rao
diffstat: scale adds/removes proportionally to graph width...
r9330 def scale(i):
if maxtotal <= graphwidth:
return i
# If diffstat runs out of room it doesn't print anything,
# which isn't very useful, so always print at least one + or -
# if there were at least some changes.
return max(i * graphwidth // maxtotal, int(bool(i)))
Alexander Solovyov
python implementation of diffstat...
r7547
Matt Mackall
patch: use diffstatsum in diffstat
r14402 for filename, adds, removes, isbinary in stats:
Patrick Mezard
diffstat: be more picky when marking file as 'binary' (issue2816)...
r15363 if isbinary:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 count = b'Bin'
Brodie Rao
diffstat: with --git, mark binary files with Bin...
r9642 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 count = b'%d' % (adds + removes)
pluses = b'+' * scale(adds)
minuses = b'-' * scale(removes)
Augie Fackler
formatting: blacken the codebase...
r43346 output.append(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b' %s%s | %*s %s%s\n'
Augie Fackler
formatting: blacken the codebase...
r43346 % (
filename,
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b' ' * (maxname - encoding.colwidth(filename)),
Augie Fackler
formatting: blacken the codebase...
r43346 countwidth,
count,
pluses,
minuses,
)
)
Alexander Solovyov
python implementation of diffstat...
r7547
if stats:
Augie Fackler
formatting: blacken the codebase...
r43346 output.append(
Martin von Zweigbergk
cleanup: join string literals that are already on one line...
r43387 _(b' %d files changed, %d insertions(+), %d deletions(-)\n')
Augie Fackler
formatting: blacken the codebase...
r43346 % (len(stats), totaladds, totalremoves)
)
Alexander Solovyov
python implementation of diffstat...
r7547
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return b''.join(output)
Brodie Rao
diff: make use of output labeling
r10818
Augie Fackler
formatting: blacken the codebase...
r43346
Brodie Rao
diff: make use of output labeling
r10818 def diffstatui(*args, **kw):
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """like diffstat(), but yields 2-tuples of (output, label) for
Brodie Rao
diff: make use of output labeling
r10818 ui.write()
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """
Brodie Rao
diff: make use of output labeling
r10818
for line in diffstat(*args, **kw).splitlines():
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if line and line[-1] in b'+-':
name, graph = line.rsplit(b' ', 1)
yield (name + b' ', b'')
Pulkit Goyal
py3: add b'' to make the regex pattern bytes
r33096 m = re.search(br'\++', graph)
Brodie Rao
diff: make use of output labeling
r10818 if m:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield (m.group(0), b'diffstat.inserted')
Pulkit Goyal
py3: add b'' to make the regex pattern bytes
r33096 m = re.search(br'-+', graph)
Brodie Rao
diff: make use of output labeling
r10818 if m:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield (m.group(0), b'diffstat.deleted')
Brodie Rao
diff: make use of output labeling
r10818 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield (line, b'')
yield (b'\n', b'')