# HG changeset patch # User Vadim Gelfer # Date 1155412038 25200 # Node ID 71e78f2ca5ae4c7fb06551caf5e4fc4e4d977498 # Parent 345bac2bc4ec22acdae6f1edce58270cd6043957# Parent e2b69dbb2daa8a7777ed8f601fca4d8ed761ba0c merge git patch code. diff -r 345bac2bc4ec -r 71e78f2ca5ae mercurial/commands.py --- a/mercurial/commands.py Sat Aug 12 12:30:02 2006 -0700 +++ b/mercurial/commands.py Sat Aug 12 12:47:18 2006 -0700 @@ -10,7 +10,7 @@ from i18n import gettext as _ demandload(globals(), "os re sys signal shutil imp urllib pdb") demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo") -demandload(globals(), "fnmatch mdiff random signal tempfile time") +demandload(globals(), "fnmatch mdiff patch random signal tempfile time") demandload(globals(), "traceback errno socket version struct atexit sets bz2") demandload(globals(), "archival cStringIO changegroup email.Parser") demandload(globals(), "hgweb.server sshserver") @@ -1825,21 +1825,21 @@ wlock = repo.wlock() lock = repo.lock() - for patch in patches: - pf = os.path.join(d, patch) + for p in patches: + pf = os.path.join(d, p) message = None user = None date = None hgpatch = False - p = email.Parser.Parser() + parser = email.Parser.Parser() if pf == '-': - msg = p.parse(sys.stdin) + msg = parser.parse(sys.stdin) ui.status(_("applying patch from stdin\n")) else: - msg = p.parse(file(pf)) - ui.status(_("applying %s\n") % patch) + msg = parser.parse(file(pf)) + ui.status(_("applying %s\n") % p) fd, tmpname = tempfile.mkstemp(prefix='hg-patch-') tmpfp = os.fdopen(fd, 'w') @@ -1907,13 +1907,45 @@ if not diffs_seen: raise util.Abort(_('no diffs found')) - files = util.patch(strip, tmpname, ui, cwd=repo.root) + files = patch.patch(strip, tmpname, ui, cwd=repo.root) + removes = [] if len(files) > 0: - cfiles = files + cfiles = files.keys() + copies = [] + copts = {'after': False, 'force': False} cwd = repo.getcwd() if cwd: - cfiles = [util.pathto(cwd, f) for f in files] + cfiles = [util.pathto(cwd, f) for f in files.keys()] + for f in files: + ctype, gp = files[f] + if ctype == 'RENAME': + copies.append((gp.oldpath, gp.path, gp.copymod)) + removes.append(gp.oldpath) + elif ctype == 'COPY': + copies.append((gp.oldpath, gp.path, gp.copymod)) + elif ctype == 'DELETE': + removes.append(gp.path) + for src, dst, after in copies: + absdst = os.path.join(repo.root, dst) + if not after and os.path.exists(absdst): + raise util.Abort(_('patch creates existing file %s') % dst) + if cwd: + src, dst = [util.pathto(cwd, f) for f in (src, dst)] + copts['after'] = after + errs, copied = docopy(ui, repo, (src, dst), copts, wlock=wlock) + if errs: + raise util.Abort(errs) + if removes: + repo.remove(removes, True, wlock=wlock) + for f in files: + ctype, gp = files[f] + if gp and gp.mode: + x = gp.mode & 0100 != 0 + dst = os.path.join(repo.root, gp.path) + util.set_exec(dst, x) addremove_lock(ui, repo, cfiles, {}, wlock=wlock) + files = files.keys() + files.extend([r for r in removes if r not in files]) repo.commit(files, message, user, date, wlock=wlock, lock=lock) finally: os.unlink(tmpname) diff -r 345bac2bc4ec -r 71e78f2ca5ae mercurial/patch.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/patch.py Sat Aug 12 12:47:18 2006 -0700 @@ -0,0 +1,166 @@ +# patch.py - patch file parsing routines +# +# Copyright 2006 Brendan Cully +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. + +from demandload import demandload +demandload(globals(), "util") +demandload(globals(), "os re shutil tempfile") + +def readgitpatch(patchname): + """extract git-style metadata about patches from """ + class gitpatch: + "op is one of ADD, DELETE, RENAME, MODIFY or COPY" + def __init__(self, path): + self.path = path + self.oldpath = None + self.mode = None + self.op = 'MODIFY' + self.copymod = False + self.lineno = 0 + + # Filter patch for git information + gitre = re.compile('diff --git a/(.*) b/(.*)') + pf = file(patchname) + gp = None + gitpatches = [] + # Can have a git patch with only metadata, causing patch to complain + dopatch = False + + lineno = 0 + for line in pf: + lineno += 1 + if line.startswith('diff --git'): + m = gitre.match(line) + if m: + if gp: + gitpatches.append(gp) + src, dst = m.group(1,2) + gp = gitpatch(dst) + gp.lineno = lineno + elif gp: + if line.startswith('--- '): + if gp.op in ('COPY', 'RENAME'): + gp.copymod = True + dopatch = 'filter' + gitpatches.append(gp) + gp = None + if not dopatch: + dopatch = True + continue + if line.startswith('rename from '): + gp.op = 'RENAME' + gp.oldpath = line[12:].rstrip() + elif line.startswith('rename to '): + gp.path = line[10:].rstrip() + elif line.startswith('copy from '): + gp.op = 'COPY' + gp.oldpath = line[10:].rstrip() + elif line.startswith('copy to '): + gp.path = line[8:].rstrip() + elif line.startswith('deleted file'): + gp.op = 'DELETE' + elif line.startswith('new file mode '): + gp.op = 'ADD' + gp.mode = int(line.rstrip()[-3:], 8) + elif line.startswith('new mode '): + gp.mode = int(line.rstrip()[-3:], 8) + if gp: + gitpatches.append(gp) + + if not gitpatches: + dopatch = True + + return (dopatch, gitpatches) + +def dogitpatch(patchname, gitpatches): + """Preprocess git patch so that vanilla patch can handle it""" + pf = file(patchname) + pfline = 1 + + fd, patchname = tempfile.mkstemp(prefix='hg-patch-') + tmpfp = os.fdopen(fd, 'w') + + try: + for i in range(len(gitpatches)): + p = gitpatches[i] + if not p.copymod: + continue + + if os.path.exists(p.path): + raise util.Abort(_("cannot create %s: destination already exists") % + p.path) + + (src, dst) = [os.path.join(os.getcwd(), n) + for n in (p.oldpath, p.path)] + + targetdir = os.path.dirname(dst) + if not os.path.isdir(targetdir): + os.makedirs(targetdir) + try: + shutil.copyfile(src, dst) + shutil.copymode(src, dst) + except shutil.Error, inst: + raise util.Abort(str(inst)) + + # rewrite patch hunk + while pfline < p.lineno: + tmpfp.write(pf.readline()) + pfline += 1 + tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path)) + line = pf.readline() + pfline += 1 + while not line.startswith('--- a/'): + tmpfp.write(line) + line = pf.readline() + pfline += 1 + tmpfp.write('--- a/%s\n' % p.path) + + line = pf.readline() + while line: + tmpfp.write(line) + line = pf.readline() + except: + tmpfp.close() + os.unlink(patchname) + raise + + tmpfp.close() + return patchname + +def patch(strip, patchname, ui, cwd=None): + """apply the patch to the working directory. + a list of patched files is returned""" + + (dopatch, gitpatches) = readgitpatch(patchname) + + files = {} + if dopatch: + if dopatch == 'filter': + patchname = dogitpatch(patchname, gitpatches) + patcher = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch') + args = [] + if cwd: + args.append('-d %s' % util.shellquote(cwd)) + fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip, + util.shellquote(patchname))) + + if dopatch == 'filter': + False and os.unlink(patchname) + + for line in fp: + line = line.rstrip() + ui.status("%s\n" % line) + if line.startswith('patching file '): + pf = util.parse_patch_output(line) + files.setdefault(pf, (None, None)) + code = fp.close() + if code: + raise util.Abort(_("patch command failed: %s") % explain_exit(code)[0]) + + for gp in gitpatches: + files[gp.path] = (gp.op, gp) + + return files diff -r 345bac2bc4ec -r 71e78f2ca5ae mercurial/util.py --- a/mercurial/util.py Sat Aug 12 12:30:02 2006 -0700 +++ b/mercurial/util.py Sat Aug 12 12:47:18 2006 -0700 @@ -95,27 +95,6 @@ return p_name return default -def patch(strip, patchname, ui, cwd=None): - """apply the patch to the working directory. - a list of patched files is returned""" - patcher = find_in_path('gpatch', os.environ.get('PATH', ''), 'patch') - args = [] - if cwd: - args.append('-d %s' % shellquote(cwd)) - fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip, - shellquote(patchname))) - files = {} - for line in fp: - line = line.rstrip() - ui.status("%s\n" % line) - if line.startswith('patching file '): - pf = parse_patch_output(line) - files.setdefault(pf, 1) - code = fp.close() - if code: - raise Abort(_("patch command failed: %s") % explain_exit(code)[0]) - return files.keys() - def binary(s): """return true if a string is binary data using diff's heuristic""" if s and '\0' in s[:4096]: diff -r 345bac2bc4ec -r 71e78f2ca5ae tests/test-git-import --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-git-import Sat Aug 12 12:47:18 2006 -0700 @@ -0,0 +1,122 @@ +#!/bin/sh + +hg init a +cd a + +echo % new file +hg import -mnew - <