diff --git a/hgext/keyword.py b/hgext/keyword.py --- a/hgext/keyword.py +++ b/hgext/keyword.py @@ -595,12 +595,10 @@ def reposetup(ui, repo): wlock.release() # monkeypatches - def kwpatchfile_init(orig, self, ui, fname, backend, store, mode, create, - remove, eolmode=None, copysource=None): + def kwpatchfile_init(orig, self, ui, gp, backend, store, eolmode=None): '''Monkeypatch/wrap patch.patchfile.__init__ to avoid rejects or conflicts due to expanded keywords in working dir.''' - orig(self, ui, fname, backend, store, mode, create, remove, - eolmode, copysource) + orig(self, ui, gp, backend, store, eolmode) # shrink keywords read from working dir self.lines = kwt.shrinklines(self.fname, self.lines) diff --git a/mercurial/patch.py b/mercurial/patch.py --- a/mercurial/patch.py +++ b/mercurial/patch.py @@ -281,6 +281,14 @@ class patchmeta(object): isexec = mode & 0100 self.mode = (islink, isexec) + 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 + def __repr__(self): return "" % (self.op, self.path) @@ -509,9 +517,8 @@ contextdesc = re.compile('(---|\*\*\*) ( eolmodes = ['strict', 'crlf', 'lf', 'auto'] class patchfile(object): - def __init__(self, ui, fname, backend, store, mode, create, remove, - eolmode='strict', copysource=None): - self.fname = fname + def __init__(self, ui, gp, backend, store, eolmode='strict'): + self.fname = gp.path self.eolmode = eolmode self.eol = None self.backend = backend @@ -519,17 +526,17 @@ class patchfile(object): self.lines = [] self.exists = False self.missing = True - self.mode = mode - self.copysource = copysource - self.create = create - self.remove = remove + self.mode = gp.mode + self.copysource = gp.oldpath + self.create = gp.op in ('ADD', 'COPY', 'RENAME') + self.remove = gp.op == 'DELETE' try: - if copysource is None: - data, mode = backend.getfile(fname) + if self.copysource is None: + data, mode = backend.getfile(self.fname) self.exists = True else: - data, mode = store.getfile(copysource) - self.exists = backend.exists(fname) + data, mode = store.getfile(self.copysource) + self.exists = backend.exists(self.fname) self.missing = False if data: self.lines = data.splitlines(True) @@ -549,7 +556,7 @@ class patchfile(object): nlines.append(l) self.lines = nlines except IOError: - if create: + if self.create: self.missing = False if self.mode is None: self.mode = (False, False) @@ -1016,14 +1023,7 @@ def pathstrip(path, strip): count -= 1 return path[:i].lstrip(), path[i:].rstrip() -def selectfile(backend, afile_orig, bfile_orig, hunk, strip, gp): - if gp: - # Git patches do not play games. Excluding copies from the - # following heuristic avoids a lot of confusion - fname = pathstrip(gp.path, strip - 1)[1] - create = gp.op in ('ADD', 'COPY', 'RENAME') - remove = gp.op == 'DELETE' - return fname, create, remove +def makepatchmeta(backend, afile_orig, bfile_orig, hunk, strip): nulla = afile_orig == "/dev/null" nullb = bfile_orig == "/dev/null" create = nulla and hunk.starta == 0 and hunk.lena == 0 @@ -1065,7 +1065,12 @@ def selectfile(backend, afile_orig, bfil else: raise PatchError(_("undefined source and destination files")) - return fname, create, remove + gp = patchmeta(fname) + if create: + gp.op = 'ADD' + elif remove: + gp.op = 'DELETE' + return gp def scangitpatch(lr, firstline): """ @@ -1134,7 +1139,7 @@ def iterhunks(fp): hunknum += 1 if emitfile: emitfile = False - yield 'file', (afile, bfile, h, gp) + yield 'file', (afile, bfile, h, gp and gp.copy() or None) yield 'hunk', h elif x.startswith('diff --git'): m = gitre.match(x) @@ -1144,14 +1149,14 @@ def iterhunks(fp): # scan whole input for git metadata gitpatches = [('a/' + gp.path, 'b/' + gp.path, gp) for gp in scangitpatch(lr, x)] - yield 'git', [g[2] for g in gitpatches + yield 'git', [g[2].copy() for g in gitpatches if g[2].op in ('COPY', 'RENAME')] gitpatches.reverse() afile = 'a/' + m.group(1) bfile = 'b/' + m.group(2) while afile != gitpatches[-1][0] and bfile != gitpatches[-1][1]: gp = gitpatches.pop()[2] - yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp) + yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy()) gp = gitpatches[-1][2] # copy/rename + modify should modify target, not source if gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD') or gp.mode: @@ -1191,7 +1196,7 @@ def iterhunks(fp): while gitpatches: gp = gitpatches.pop()[2] - yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp) + yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy()) def applydiff(ui, fp, backend, store, strip=1, eolmode='strict'): """Reads a patch from fp and tries to apply it. @@ -1228,41 +1233,38 @@ def _applydiff(ui, fp, patcher, backend, rejects += current_file.close() current_file = None afile, bfile, first_hunk, gp = values - copysource = None if gp: path = pstrip(gp.path) + gp.path = pstrip(gp.path) if gp.oldpath: - copysource = pstrip(gp.oldpath) - if gp.op == 'RENAME': - backend.unlink(copysource) - if not first_hunk: - if gp.op == 'DELETE': - backend.unlink(path) - continue - data, mode = None, None - if gp.op in ('RENAME', 'COPY'): - data, mode = store.getfile(copysource) - if gp.mode: - mode = gp.mode - if gp.op == 'ADD': - # Added files without content have no hunk and - # must be created - data = '' - if data or mode: - if (gp.op in ('ADD', 'RENAME', 'COPY') - and backend.exists(path)): - raise PatchError(_("cannot create %s: destination " - "already exists") % path) - backend.setfile(path, data, mode, copysource) + gp.oldpath = pstrip(gp.oldpath) + else: + gp = makepatchmeta(backend, afile, bfile, first_hunk, strip) + if gp.op == 'RENAME': + backend.unlink(gp.oldpath) if not first_hunk: + if gp.op == 'DELETE': + backend.unlink(gp.path) + continue + data, mode = None, None + if gp.op in ('RENAME', 'COPY'): + data, mode = store.getfile(gp.oldpath) + if gp.mode: + mode = gp.mode + if gp.op == 'ADD': + # Added files without content have no hunk and + # must be created + data = '' + if data or mode: + if (gp.op in ('ADD', 'RENAME', 'COPY') + and backend.exists(gp.path)): + raise PatchError(_("cannot create %s: destination " + "already exists") % gp.path) + backend.setfile(gp.path, data, mode, gp.oldpath) continue try: - mode = gp and gp.mode or None - current_file, create, remove = selectfile( - backend, afile, bfile, first_hunk, strip, gp) - current_file = patcher(ui, current_file, backend, store, mode, - create, remove, eolmode=eolmode, - copysource=copysource) + current_file = patcher(ui, gp, backend, store, + eolmode=eolmode) except PatchError, inst: ui.warn(str(inst) + '\n') current_file = None @@ -1395,14 +1397,14 @@ def changedfiles(ui, repo, patchpath, st if state == 'file': afile, bfile, first_hunk, gp = values if gp: - changed.add(pathstrip(gp.path, strip - 1)[1]) - if gp.op == 'RENAME': - changed.add(pathstrip(gp.oldpath, strip - 1)[1]) - if not first_hunk: - continue - current_file, create, remove = selectfile( - backend, afile, bfile, first_hunk, strip, gp) - changed.add(current_file) + gp.path = pathstrip(gp.path, strip - 1)[1] + if gp.oldpath: + gp.oldpath = pathstrip(gp.oldpath, strip - 1)[1] + else: + gp = makepatchmeta(backend, afile, bfile, first_hunk, strip) + changed.add(gp.path) + if gp.op == 'RENAME': + changed.add(gp.oldpath) elif state not in ('hunk', 'git'): raise util.Abort(_('unsupported parser state: %s') % state) return changed