# HG changeset patch # User Brendan Cully # Date 1155336616 25200 # Node ID 0f08f2c042ec8bec27a21268600f0656c1f59873 # Parent b3d1145ed06c6c715508efce158973838cc524a3 Move patch-related code into its own module. diff -r b3d1145ed06c -r 0f08f2c042ec mercurial/commands.py --- a/mercurial/commands.py Fri Aug 11 15:50:07 2006 -0700 +++ b/mercurial/commands.py Fri Aug 11 15:50:16 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") @@ -1826,21 +1826,21 @@ lock = repo.lock() wlock = repo.wlock() - 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') @@ -1908,7 +1908,7 @@ 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.keys() diff -r b3d1145ed06c -r 0f08f2c042ec mercurial/patch.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/patch.py Fri Aug 11 15:50:16 2006 -0700 @@ -0,0 +1,165 @@ +# patch.py - patch file parsing routines +# +# 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)] + + print "copying %s to %s" % (src, dst) + 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 b3d1145ed06c -r 0f08f2c042ec mercurial/util.py --- a/mercurial/util.py Fri Aug 11 15:50:07 2006 -0700 +++ b/mercurial/util.py Fri Aug 11 15:50:16 2006 -0700 @@ -93,163 +93,6 @@ return p_name return default -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 Abort(_("cannot create %s: destination already exists") % - p.path) - - (src, dst) = [os.path.join(os.getcwd(), n) - for n in (p.oldpath, p.path)] - - print "copying %s to %s" % (src, dst) - 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 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 = 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))) - - 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 = parse_patch_output(line) - files.setdefault(pf, (None, None)) - code = fp.close() - if code: - raise Abort(_("patch command failed: %s") % explain_exit(code)[0]) - - for gp in gitpatches: - files[gp.path] = (gp.op, gp) - - return files - def binary(s): """return true if a string is binary data using diff's heuristic""" if s and '\0' in s[:4096]: