##// END OF EJS Templates
patch: fix patch hunk/metdata synchronization (issue3384)...
patch: fix patch hunk/metdata synchronization (issue3384) Git patches are parsed in two phases: 1) extract metadata, 2) parse actual deltas and merge them with the previous metadata. We do this to avoid dependency issues like "modify a; copy a to b", where "b" must be copied from the unmodified "a". Issue3384 is caused by flaky code I wrote to synchronize the patch metadata with the emitted hunk: if (gitpatches and (gitpatches[-1][0] == afile or gitpatches[-1][1] == bfile)): gp = gitpatches.pop()[2] With a patch like: diff --git a/a b/c copy from a copy to c --- a/a +++ b/c @@ -1,1 +1,2 @@ a +a @@ -2,1 +2,2 @@ a +a diff --git a/a b/a --- a/a +++ b/a @@ -1,1 +1,2 @@ a +b the first hunk of the first block is matched with the metadata for the block "diff --git a/a b/c", then the second hunk of the first block is matched with the metadata of the second block "diff --git a/a b/a", because of the "or" in the code paste above. Turning the "or" into an "and" is not enough as we have to deal with /dev/null cases for each file. We I remove this broken piece of code: # copy/rename + modify should modify target, not source if gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD') or gp.mode: afile = bfile because "afile = bfile" set "afile" to stuff like "b/file" instead of "a/file", and because this only happens for git patches, which afile/bfile are ignored anyway by applydiff(). v2: - Avoid a traceback on git metadata desynchronization

File last commit:

r15407:ee112eb6 stable
r16506:fc4e0fec stable
Show More
ignore.py
105 lines | 3.3 KiB | text/x-python | PythonLexer
Matt Mackall
dirstate: move ignore to its own file
r4609 # ignore.py - ignored file handling for mercurial
#
# Copyright 2007 Matt Mackall <mpm@selenic.com>
#
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.
Matt Mackall
dirstate: move ignore to its own file
r4609
from i18n import _
Matt Mackall
match: change all users of util.matcher to match.match
r8566 import util, match
Simon Heimberg
separate import lines from mercurial and general python modules
r8312 import re
Matt Mackall
ignore: fix up comment parsing...
r5640
_commentre = None
Matt Mackall
dirstate: move ignore to its own file
r4609
Brendan Cully
ignore: separate pattern extraction from match compilation...
r9091 def ignorepats(lines):
'''parse lines (iterable) of .hgignore text, returning a tuple of
(patterns, parse errors). These patterns should be given to compile()
to be validated and converted into a match function.'''
syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
syntax = 'relre:'
patterns = []
warnings = []
for line in lines:
Matt Mackall
ignore: fix up comment parsing...
r5640 if "#" in line:
global _commentre
if not _commentre:
_commentre = re.compile(r'((^|[^\\])(\\\\)*)#.*')
# remove comments prefixed by an even number of escapes
line = _commentre.sub(r'\1', line)
# fixup properly escaped comments that survived the above
line = line.replace("\\#", "#")
line = line.rstrip()
Brendan Cully
ignore: separate pattern extraction from match compilation...
r9091 if not line:
continue
if line.startswith('syntax:'):
s = line[7:].strip()
try:
syntax = syntaxes[s]
except KeyError:
warnings.append(_("ignoring invalid syntax '%s'") % s)
continue
pat = syntax + line
for s, rels in syntaxes.iteritems():
if line.startswith(rels):
pat = line
break
elif line.startswith(s+':'):
pat = rels + line[len(s)+1:]
break
patterns.append(pat)
return patterns, warnings
Matt Mackall
dirstate: move ignore to its own file
r4609
def ignore(root, files, warn):
Brendan Cully
ignore: separate pattern extraction from match compilation...
r9091 '''return matcher covering patterns in 'files'.
Matt Mackall
dirstate: move ignore to its own file
r4609
the files parsed for patterns include:
.hgignore in the repository root
any additional files specified in the [ui] section of ~/.hgrc
trailing white space is dropped.
the escape character is backslash.
comments start with #.
empty lines are skipped.
lines can be of the following formats:
syntax: regexp # defaults following lines to non-rooted regexps
syntax: glob # defaults following lines to non-rooted globs
re:pattern # non-rooted regular expression
glob:pattern # non-rooted glob
pattern # pattern of the current default type'''
pats = {}
for f in files:
try:
pats[f] = []
fp = open(f)
Brendan Cully
ignore: separate pattern extraction from match compilation...
r9091 pats[f], warnings = ignorepats(fp)
Matt Mackall
misc: adding missing file close() calls...
r15407 fp.close()
Brendan Cully
ignore: separate pattern extraction from match compilation...
r9091 for warning in warnings:
warn("%s: %s\n" % (f, warning))
Matt Mackall
dirstate: move ignore to its own file
r4609 except IOError, inst:
if f != files[0]:
warn(_("skipping unreadable ignore file '%s': %s\n") %
(f, inst.strerror))
allpats = []
Martin Geisler
remove unnecessary list comprehensions...
r13412 for patlist in pats.values():
allpats.extend(patlist)
Matt Mackall
dirstate: move ignore to its own file
r4609 if not allpats:
return util.never
try:
Matt Mackall
match: add some default args
r8567 ignorefunc = match.match(root, '', [], allpats)
Matt Mackall
dirstate: move ignore to its own file
r4609 except util.Abort:
# Re-raise an exception where the src is the right file
Dirkjan Ochtman
use dict.iteritems() rather than dict.items()...
r7622 for f, patlist in pats.iteritems():
Matt Mackall
match: change all users of util.matcher to match.match
r8566 try:
Matt Mackall
match: add some default args
r8567 match.match(root, '', [], patlist)
Matt Mackall
match: change all users of util.matcher to match.match
r8566 except util.Abort, inst:
raise util.Abort('%s: %s' % (f, inst[0]))
Matt Mackall
dirstate: move ignore to its own file
r4609
return ignorefunc