mirror of
https://github.com/facebook/sapling.git
synced 2024-10-07 15:27:13 +03:00
Move patch-related code into its own module.
This commit is contained in:
parent
7601c19932
commit
4f1b3a83bb
@ -10,7 +10,7 @@ from node import *
|
||||
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 @@ def import_(ui, repo, patch1, *patches, **opts):
|
||||
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 @@ def import_(ui, repo, patch1, *patches, **opts):
|
||||
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()
|
||||
|
165
mercurial/patch.py
Normal file
165
mercurial/patch.py
Normal file
@ -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 <patchname>"""
|
||||
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 <patchname> 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
|
@ -93,163 +93,6 @@ def find_in_path(name, path, default=None):
|
||||
return p_name
|
||||
return default
|
||||
|
||||
def readgitpatch(patchname):
|
||||
"""extract git-style metadata about patches from <patchname>"""
|
||||
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 <patchname> 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]:
|
||||
|
Loading…
Reference in New Issue
Block a user