##// END OF EJS Templates
git-send-email compatibility: stop reading changelog after ^---$
git-send-email compatibility: stop reading changelog after ^---$

File last commit:

r4220:12537038 default
r4220:12537038 default
Show More
patch.py
648 lines | 21.0 KiB | text/x-python | PythonLexer
Brendan Cully
Move patch-related code into its own module.
r2861 # patch.py - patch file parsing routines
#
Vadim Gelfer
merge git patch code.
r2865 # Copyright 2006 Brendan Cully <brendan@kublai.com>
#
Brendan Cully
Move patch-related code into its own module.
r2861 # This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
Matt Mackall
Simplify i18n imports
r3891 from i18n import _
Vadim Gelfer
refactor text diff/patch code....
r2874 from node import *
Benoit Boissinot
patch: use contexts for diff
r3967 import base85, cmdutil, mdiff, util, context, revlog
Benoit Boissinot
remove various unused import
r3963 import cStringIO, email.Parser, os, popen2, re, sha
Matt Mackall
Replace demandload with new demandimport
r3877 import sys, tempfile, zlib
Vadim Gelfer
commands.import: refactor patch parsing into patch.extract.
r2866
Brendan Cully
Move import's working dir update code into patch.updatedir
r2933 # helper functions
def copyfile(src, dst, basedir=None):
if not basedir:
basedir = os.getcwd()
abssrc, absdst = [os.path.join(basedir, n) for n in (src, dst)]
if os.path.exists(absdst):
raise util.Abort(_("cannot create %s: destination already exists") %
dst)
targetdir = os.path.dirname(absdst)
if not os.path.isdir(targetdir):
os.makedirs(targetdir)
Matt Mackall
util: add copyfile function
r3629
util.copyfile(abssrc, absdst)
Brendan Cully
Move import's working dir update code into patch.updatedir
r2933
# public functions
Vadim Gelfer
commands.import: refactor patch parsing into patch.extract.
r2866 def extract(ui, fileobj):
'''extract patch from data read from fileobj.
patch can be normal patch or contained in email message.
return tuple (filename, message, user, date). any item in returned
tuple can be None. if filename is None, fileobj did not contain
patch. caller must unlink filename when done.'''
# attempt to detect the start of a patch
# (this heuristic is borrowed from quilt)
diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
'retrieving revision [0-9]+(\.[0-9]+)*$|' +
'(---|\*\*\*)[ \t])', re.MULTILINE)
fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
tmpfp = os.fdopen(fd, 'w')
try:
msg = email.Parser.Parser().parse(fileobj)
message = msg['Subject']
user = msg['From']
# should try to parse msg['Date']
date = None
if message:
Brendan Cully
Strip [PATCH...] from message subject when importing patches
r4201 if message.startswith('[PATCH'):
pend = message.find(']')
Brendan Cully
Make [PATCH] removal slightly more robust
r4208 if pend >= 0:
message = message[pend+1:].lstrip()
Vadim Gelfer
commands.import: refactor patch parsing into patch.extract.
r2866 message = message.replace('\n\t', ' ')
ui.debug('Subject: %s\n' % message)
if user:
ui.debug('From: %s\n' % user)
diffs_seen = 0
ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
for part in msg.walk():
content_type = part.get_content_type()
ui.debug('Content-Type: %s\n' % content_type)
if content_type not in ok_types:
continue
payload = part.get_payload(decode=True)
m = diffre.search(payload)
if m:
Brendan Cully
git-send-email compatibility: stop reading changelog after ^---$
r4220 hgpatch = False
ignoretext = False
Vadim Gelfer
commands.import: refactor patch parsing into patch.extract.
r2866 ui.debug(_('found patch at byte %d\n') % m.start(0))
diffs_seen += 1
cfp = cStringIO.StringIO()
if message:
cfp.write(message)
cfp.write('\n')
for line in payload[:m.start(0)].splitlines():
if line.startswith('# HG changeset patch'):
ui.debug(_('patch generated by hg export\n'))
hgpatch = True
# drop earlier commit message content
cfp.seek(0)
cfp.truncate()
elif hgpatch:
if line.startswith('# User '):
user = line[7:]
ui.debug('From: %s\n' % user)
elif line.startswith("# Date "):
date = line[7:]
Brendan Cully
git-send-email compatibility: stop reading changelog after ^---$
r4220 elif line == '---' and 'git-send-email' in msg['X-Mailer']:
ignoretext = True
if not line.startswith('# ') and not ignoretext:
Vadim Gelfer
commands.import: refactor patch parsing into patch.extract.
r2866 cfp.write(line)
cfp.write('\n')
message = cfp.getvalue()
if tmpfp:
tmpfp.write(payload)
if not payload.endswith('\n'):
tmpfp.write('\n')
elif not diffs_seen and message and content_type == 'text/plain':
message += '\n' + payload
except:
tmpfp.close()
os.unlink(tmpname)
raise
tmpfp.close()
if not diffs_seen:
os.unlink(tmpname)
return None, message, user, date
return tmpname, message, user, date
Brendan Cully
Move patch-related code into its own module.
r2861
Alexis S. L. Carvalho
handle files with both git binary patches and copy/rename ops
r3716 GP_PATCH = 1 << 0 # we have to run patch
GP_FILTER = 1 << 1 # there's some copy/rename operation
GP_BINARY = 1 << 2 # there's a binary patch
Brendan Cully
Move patch-related code into its own module.
r2861 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
Brendan Cully
Add git-1.4 binary patch support
r3367 self.binary = False
Thomas Arendsen Hein
Whitespace/Tab cleanup
r3223
Brendan Cully
Move patch-related code into its own module.
r2861 # 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
Alexis S. L. Carvalho
handle files with both git binary patches and copy/rename ops
r3716 dopatch = 0
Brendan Cully
Move patch-related code into its own module.
r2861
lineno = 0
for line in pf:
lineno += 1
if line.startswith('diff --git'):
m = gitre.match(line)
if m:
if gp:
gitpatches.append(gp)
Thomas Arendsen Hein
white space and line break cleanups
r3673 src, dst = m.group(1, 2)
Brendan Cully
Move patch-related code into its own module.
r2861 gp = gitpatch(dst)
gp.lineno = lineno
elif gp:
if line.startswith('--- '):
if gp.op in ('COPY', 'RENAME'):
gp.copymod = True
Alexis S. L. Carvalho
handle files with both git binary patches and copy/rename ops
r3716 dopatch |= GP_FILTER
Brendan Cully
Move patch-related code into its own module.
r2861 gitpatches.append(gp)
gp = None
Alexis S. L. Carvalho
handle files with both git binary patches and copy/rename ops
r3716 dopatch |= GP_PATCH
Brendan Cully
Move patch-related code into its own module.
r2861 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)
Brendan Cully
Add git-1.4 binary patch support
r3367 elif line.startswith('GIT binary patch'):
Alexis S. L. Carvalho
handle files with both git binary patches and copy/rename ops
r3716 dopatch |= GP_BINARY
Brendan Cully
Add git-1.4 binary patch support
r3367 gp.binary = True
Brendan Cully
Move patch-related code into its own module.
r2861 if gp:
gitpatches.append(gp)
if not gitpatches:
Alexis S. L. Carvalho
handle files with both git binary patches and copy/rename ops
r3716 dopatch = GP_PATCH
Brendan Cully
Move patch-related code into its own module.
r2861
return (dopatch, gitpatches)
Brendan Cully
Fix git patch application when cwd != repo.root
r3055 def dogitpatch(patchname, gitpatches, cwd=None):
Brendan Cully
Move patch-related code into its own module.
r2861 """Preprocess git patch so that vanilla patch can handle it"""
Brendan Cully
Add git-1.4 binary patch support
r3367 def extractbin(fp):
Alexis S. L. Carvalho
handle files with more than one git binary patch
r3717 i = [0] # yuck
def readline():
i[0] += 1
return fp.readline().rstrip()
line = readline()
Brendan Cully
Add git-1.4 binary patch support
r3367 while line and not line.startswith('literal '):
Alexis S. L. Carvalho
handle files with more than one git binary patch
r3717 line = readline()
Brendan Cully
Add git-1.4 binary patch support
r3367 if not line:
Alexis S. L. Carvalho
handle files with more than one git binary patch
r3717 return None, i[0]
Brendan Cully
Use line length field when extracting git binary patches
r3374 size = int(line[8:])
Brendan Cully
Add git-1.4 binary patch support
r3367 dec = []
Alexis S. L. Carvalho
handle files with more than one git binary patch
r3717 line = readline()
Brendan Cully
Add git-1.4 binary patch support
r3367 while line:
Brendan Cully
Use line length field when extracting git binary patches
r3374 l = line[0]
if l <= 'Z' and l >= 'A':
l = ord(l) - ord('A') + 1
else:
l = ord(l) - ord('a') + 27
dec.append(base85.b85decode(line[1:])[:l])
Alexis S. L. Carvalho
handle files with more than one git binary patch
r3717 line = readline()
Brendan Cully
Add git-1.4 binary patch support
r3367 text = zlib.decompress(''.join(dec))
if len(text) != size:
raise util.Abort(_('binary patch is %d bytes, not %d') %
(len(text), size))
Alexis S. L. Carvalho
handle files with more than one git binary patch
r3717 return text, i[0]
Brendan Cully
Add git-1.4 binary patch support
r3367
Brendan Cully
Move patch-related code into its own module.
r2861 pf = file(patchname)
pfline = 1
fd, patchname = tempfile.mkstemp(prefix='hg-patch-')
tmpfp = os.fdopen(fd, 'w')
try:
Benoit Boissinot
use xrange instead of range
r3473 for i in xrange(len(gitpatches)):
Brendan Cully
Move patch-related code into its own module.
r2861 p = gitpatches[i]
Brendan Cully
Add git-1.4 binary patch support
r3367 if not p.copymod and not p.binary:
Brendan Cully
Move patch-related code into its own module.
r2861 continue
# rewrite patch hunk
while pfline < p.lineno:
tmpfp.write(pf.readline())
pfline += 1
Brendan Cully
Add git-1.4 binary patch support
r3367
if p.binary:
Alexis S. L. Carvalho
handle files with more than one git binary patch
r3717 text, delta = extractbin(pf)
Brendan Cully
Add git-1.4 binary patch support
r3367 if not text:
raise util.Abort(_('binary patch extraction failed'))
Alexis S. L. Carvalho
handle files with more than one git binary patch
r3717 pfline += delta
Brendan Cully
Add git-1.4 binary patch support
r3367 if not cwd:
cwd = os.getcwd()
absdst = os.path.join(cwd, p.path)
basedir = os.path.dirname(absdst)
if not os.path.isdir(basedir):
os.makedirs(basedir)
out = file(absdst, 'wb')
out.write(text)
out.close()
elif p.copymod:
copyfile(p.oldpath, p.path, basedir=cwd)
tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path))
Brendan Cully
Move patch-related code into its own module.
r2861 line = pf.readline()
pfline += 1
Brendan Cully
Add git-1.4 binary patch support
r3367 while not line.startswith('--- a/'):
tmpfp.write(line)
line = pf.readline()
pfline += 1
tmpfp.write('--- a/%s\n' % p.path)
Brendan Cully
Move patch-related code into its own module.
r2861
line = pf.readline()
while line:
tmpfp.write(line)
line = pf.readline()
except:
tmpfp.close()
os.unlink(patchname)
raise
tmpfp.close()
return patchname
Brendan Cully
patch: return list of modified files even when an exception is raised...
r3465 def patch(patchname, ui, strip=1, cwd=None, files={}):
Brendan Cully
Move patch-related code into its own module.
r2861 """apply the patch <patchname> to the working directory.
a list of patched files is returned"""
Benoit Boissinot
unlink temporary patch files even when an exception is raised
r3057 # helper function
def __patch(patchname):
"""patch and updates the files and fuzz variables"""
fuzz = False
Brendan Cully
Move patch-related code into its own module.
r2861
Benoit Boissinot
unlink temporary patch files even when an exception is raised
r3057 patcher = util.find_in_path('gpatch', os.environ.get('PATH', ''),
'patch')
Brendan Cully
Move patch-related code into its own module.
r2861 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)))
for line in fp:
line = line.rstrip()
Brendan Cully
Unify mq and hg patch invocation....
r2919 ui.note(line + '\n')
Brendan Cully
Move patch-related code into its own module.
r2861 if line.startswith('patching file '):
pf = util.parse_patch_output(line)
Brendan Cully
Unify mq and hg patch invocation....
r2919 printed_file = False
Brendan Cully
Move patch-related code into its own module.
r2861 files.setdefault(pf, (None, None))
Brendan Cully
Unify mq and hg patch invocation....
r2919 elif line.find('with fuzz') >= 0:
fuzz = True
if not printed_file:
ui.warn(pf + '\n')
printed_file = True
ui.warn(line + '\n')
elif line.find('saving rejects to file') >= 0:
ui.warn(line + '\n')
elif line.find('FAILED') >= 0:
if not printed_file:
ui.warn(pf + '\n')
printed_file = True
ui.warn(line + '\n')
Brendan Cully
Move patch-related code into its own module.
r2861 code = fp.close()
if code:
Vadim Gelfer
util: qualify name properly.
r2868 raise util.Abort(_("patch command failed: %s") %
util.explain_exit(code)[0])
Brendan Cully
patch: return list of modified files even when an exception is raised...
r3465 return fuzz
Benoit Boissinot
unlink temporary patch files even when an exception is raised
r3057
(dopatch, gitpatches) = readgitpatch(patchname)
Brendan Cully
patch: return list of modified files even when an exception is raised...
r3465 for gp in gitpatches:
files[gp.path] = (gp.op, gp)
Benoit Boissinot
unlink temporary patch files even when an exception is raised
r3057
Brendan Cully
patch: return list of modified files even when an exception is raised...
r3465 fuzz = False
Benoit Boissinot
unlink temporary patch files even when an exception is raised
r3057 if dopatch:
Alexis S. L. Carvalho
handle files with both git binary patches and copy/rename ops
r3716 filterpatch = dopatch & (GP_FILTER | GP_BINARY)
if filterpatch:
Benoit Boissinot
unlink temporary patch files even when an exception is raised
r3057 patchname = dogitpatch(patchname, gitpatches, cwd=cwd)
try:
Alexis S. L. Carvalho
handle files with both git binary patches and copy/rename ops
r3716 if dopatch & GP_PATCH:
Brendan Cully
patch: return list of modified files even when an exception is raised...
r3465 fuzz = __patch(patchname)
Benoit Boissinot
unlink temporary patch files even when an exception is raised
r3057 finally:
Alexis S. L. Carvalho
handle files with both git binary patches and copy/rename ops
r3716 if filterpatch:
Benoit Boissinot
unlink temporary patch files even when an exception is raised
r3057 os.unlink(patchname)
Brendan Cully
Move patch-related code into its own module.
r2861
Brendan Cully
patch: return list of modified files even when an exception is raised...
r3465 return fuzz
Vadim Gelfer
refactor text diff/patch code....
r2874
Alexis S. L. Carvalho
add untrusted argument to patch.diffopts
r3554 def diffopts(ui, opts={}, untrusted=False):
def get(key, name=None):
return (opts.get(key) or
ui.configbool('diff', name or key, None, untrusted=untrusted))
Matt Mackall
Move ui.diffopts to patch.diffopts where it belongs
r2888 return mdiff.diffopts(
text=opts.get('text'),
Alexis S. L. Carvalho
add untrusted argument to patch.diffopts
r3554 git=get('git'),
nodates=get('nodates'),
showfunc=get('show_function', 'showfunc'),
ignorews=get('ignore_all_space', 'ignorews'),
ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'))
Matt Mackall
Move ui.diffopts to patch.diffopts where it belongs
r2888
Brendan Cully
Move import's working dir update code into patch.updatedir
r2933 def updatedir(ui, repo, patches, wlock=None):
'''Update dirstate after patch application according to metadata'''
if not patches:
return
copies = []
Alexis S. L. Carvalho
handle git patches that rename a file to more than one destination
r3701 removes = {}
Brendan Cully
Move import's working dir update code into patch.updatedir
r2933 cfiles = patches.keys()
cwd = repo.getcwd()
if cwd:
cfiles = [util.pathto(cwd, f) for f in patches.keys()]
for f in patches:
ctype, gp = patches[f]
if ctype == 'RENAME':
copies.append((gp.oldpath, gp.path, gp.copymod))
Alexis S. L. Carvalho
handle git patches that rename a file to more than one destination
r3701 removes[gp.oldpath] = 1
Brendan Cully
Move import's working dir update code into patch.updatedir
r2933 elif ctype == 'COPY':
copies.append((gp.oldpath, gp.path, gp.copymod))
elif ctype == 'DELETE':
Alexis S. L. Carvalho
handle git patches that rename a file to more than one destination
r3701 removes[gp.path] = 1
Brendan Cully
Move import's working dir update code into patch.updatedir
r2933 for src, dst, after in copies:
if not after:
copyfile(src, dst, repo.root)
repo.copy(src, dst, wlock=wlock)
Alexis S. L. Carvalho
handle git patches that rename a file to more than one destination
r3701 removes = removes.keys()
Brendan Cully
Move import's working dir update code into patch.updatedir
r2933 if removes:
Alexis S. L. Carvalho
handle git patches that rename a file to more than one destination
r3701 removes.sort()
Brendan Cully
Move import's working dir update code into patch.updatedir
r2933 repo.remove(removes, True, wlock=wlock)
for f in patches:
ctype, gp = patches[f]
if gp and gp.mode:
x = gp.mode & 0100 != 0
dst = os.path.join(repo.root, gp.path)
Brendan Cully
git patch: create empty added files
r3588 # patch won't create empty files
if ctype == 'ADD' and not os.path.exists(dst):
Matt Mackall
symlinks: add flags param to wwrite...
r4006 repo.wwrite(gp.path, '', x and 'x' or '')
else:
util.set_exec(dst, x)
Brendan Cully
Move import's working dir update code into patch.updatedir
r2933 cmdutil.addremove(repo, cfiles, wlock=wlock)
files = patches.keys()
files.extend([r for r in removes if r not in files])
files.sort()
return files
Brendan Cully
Add git-1.4 binary patch support
r3367 def b85diff(fp, to, tn):
'''print base85-encoded binary diff'''
def gitindex(text):
if not text:
return '0' * 40
l = len(text)
s = sha.new('blob %d\0' % l)
s.update(text)
return s.hexdigest()
def fmtline(line):
l = len(line)
if l <= 26:
l = chr(ord('A') + l - 1)
else:
l = chr(l - 26 + ord('a') - 1)
return '%c%s\n' % (l, base85.b85encode(line, True))
def chunk(text, csize=52):
l = len(text)
i = 0
while i < l:
yield text[i:i+csize]
i += csize
Alexis S. L. Carvalho
git binary patches: use hashes to detect identical files
r4105 tohash = gitindex(to)
tnhash = gitindex(tn)
if tohash == tnhash:
Alexis S. L. Carvalho
git binary patches: don't print the header for identical files
r4106 return ""
Brendan Cully
Add git-1.4 binary patch support
r3367 # TODO: deltas
Alexis S. L. Carvalho
git binary patches: don't print the header for identical files
r4106 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
(tohash, tnhash, len(tn))]
for l in chunk(zlib.compress(tn)):
ret.append(fmtline(l))
ret.append('\n')
return ''.join(ret)
Brendan Cully
Add git-1.4 binary patch support
r3367
Vadim Gelfer
refactor text diff/patch code....
r2874 def diff(repo, node1=None, node2=None, files=None, match=util.always,
fp=None, changes=None, opts=None):
'''print diff of changes to files between two nodes, or node and
working directory.
if node1 is None, use first dirstate parent instead.
if node2 is None, compare node1 with working directory.'''
if opts is None:
opts = mdiff.defaultopts
if fp is None:
fp = repo.ui
if not node1:
node1 = repo.dirstate.parents()[0]
Brendan Cully
Teach mq about git patches
r2934
Benoit Boissinot
patch: use contexts for diff
r3967 ccache = {}
def getctx(r):
if r not in ccache:
ccache[r] = context.changectx(repo, r)
return ccache[r]
flcache = {}
def getfilectx(f, ctx):
flctx = ctx.filectx(f, filelog=flcache.get(f))
if f not in flcache:
flcache[f] = flctx._filelog
return flctx
Brendan Cully
Teach mq about git patches
r2934
Vadim Gelfer
refactor text diff/patch code....
r2874 # reading the data for node1 early allows it to play nicely
Vadim Gelfer
remove localrepository.changes....
r2875 # with repo.status and the revlog cache.
Benoit Boissinot
patch: use contexts for diff
r3967 ctx1 = context.changectx(repo, node1)
# force manifest reading
man1 = ctx1.manifest()
date1 = util.datestr(ctx1.date())
Vadim Gelfer
refactor text diff/patch code....
r2874
if not changes:
Vadim Gelfer
remove localrepository.changes....
r2875 changes = repo.status(node1, node2, files, match=match)[:5]
Vadim Gelfer
refactor text diff/patch code....
r2874 modified, added, removed, deleted, unknown = changes
if not modified and not added and not removed:
return
Benoit Boissinot
patch: use contexts for diff
r3967 if node2:
ctx2 = context.changectx(repo, node2)
else:
ctx2 = context.workingctx(repo)
man2 = ctx2.manifest()
# returns False if there was no rename between ctx1 and ctx2
# returns None if the file was created between ctx1 and ctx2
# returns the (file, node) present in ctx1 that was renamed to f in ctx2
def renamed(f):
startrev = ctx1.rev()
c = ctx2
crev = c.rev()
if crev is None:
crev = repo.changelog.count()
Alexis S. L. Carvalho
renamedbetween: only return (file, node) pairs that exist in the original rev
r3694 orig = f
Benoit Boissinot
patch: use contexts for diff
r3967 while crev > startrev:
if f in c.files():
Alexis S. L. Carvalho
diff: improve detection of renames when diffing across many revisions
r3693 try:
Benoit Boissinot
patch: use contexts for diff
r3967 src = getfilectx(f, c).renamed()
except revlog.LookupError:
Alexis S. L. Carvalho
diff: improve detection of renames when diffing across many revisions
r3693 return None
if src:
f = src[0]
Benoit Boissinot
patch: use contexts for diff
r3967 crev = c.parents()[0].rev()
# try to reuse
c = getctx(crev)
if f not in man1:
Alexis S. L. Carvalho
renamedbetween: only return (file, node) pairs that exist in the original rev
r3694 return None
Alexis S. L. Carvalho
diff: better detection of renames when comparing with the working dir.
r3696 if f == orig:
return False
Benoit Boissinot
patch: use contexts for diff
r3967 return f
Vadim Gelfer
refactor text diff/patch code....
r2874
if repo.ui.quiet:
r = None
else:
Alexis S. L. Carvalho
use short hashes with diff -v
r3387 hexfunc = repo.ui.debugflag and hex or short
Vadim Gelfer
refactor text diff/patch code....
r2874 r = [hexfunc(node) for node in [node1, node2] if node]
Brendan Cully
Add diff --git option
r2907 if opts.git:
copied = {}
for f in added:
src = renamed(f)
if src:
copied[f] = src
Benoit Boissinot
patch: use contexts for diff
r3967 srcs = [x[1] for x in copied.items()]
Brendan Cully
Add diff --git option
r2907
Vadim Gelfer
refactor text diff/patch code....
r2874 all = modified + added + removed
all.sort()
Alexis S. L. Carvalho
Don't generate git patches that rename a file to multiple destinations...
r3702 gone = {}
Matt Mackall
exec: add execfunc to simplify exec flag support on non-exec filesystems
r3996
Vadim Gelfer
refactor text diff/patch code....
r2874 for f in all:
to = None
tn = None
Brendan Cully
Add diff --git option
r2907 dodiff = True
Brendan Cully
Don't generate git diff header for empty diffs
r3329 header = []
Benoit Boissinot
patch: use contexts for diff
r3967 if f in man1:
to = getfilectx(f, ctx1).data()
Vadim Gelfer
refactor text diff/patch code....
r2874 if f not in removed:
Benoit Boissinot
patch: use contexts for diff
r3967 tn = getfilectx(f, ctx2).data()
Brendan Cully
Add diff --git option
r2907 if opts.git:
def gitmode(x):
return x and '100755' or '100644'
def addmodehdr(header, omode, nmode):
if omode != nmode:
header.append('old mode %s\n' % omode)
header.append('new mode %s\n' % nmode)
a, b = f, f
if f in added:
Benoit Boissinot
patch: use contexts for diff
r3967 mode = gitmode(man2.execf(f))
Brendan Cully
Add diff --git option
r2907 if f in copied:
Benoit Boissinot
patch: use contexts for diff
r3967 a = copied[f]
omode = gitmode(man1.execf(a))
Brendan Cully
Add diff --git option
r2907 addmodehdr(header, omode, mode)
Alexis S. L. Carvalho
Don't generate git patches that rename a file to multiple destinations...
r3702 if a in removed and a not in gone:
op = 'rename'
gone[a] = 1
else:
op = 'copy'
Brendan Cully
Add diff --git option
r2907 header.append('%s from %s\n' % (op, a))
header.append('%s to %s\n' % (op, f))
Benoit Boissinot
patch: use contexts for diff
r3967 to = getfilectx(a, ctx1).data()
Brendan Cully
Add diff --git option
r2907 else:
header.append('new file mode %s\n' % mode)
Alexis S. L. Carvalho
git patches: handle renames of binary files
r4092 if util.binary(tn):
dodiff = 'binary'
Brendan Cully
Add diff --git option
r2907 elif f in removed:
if f in srcs:
dodiff = False
else:
Benoit Boissinot
patch: use contexts for diff
r3967 mode = gitmode(man1.execf(f))
Brendan Cully
Add diff --git option
r2907 header.append('deleted file mode %s\n' % mode)
else:
Benoit Boissinot
patch: use contexts for diff
r3967 omode = gitmode(man1.execf(f))
nmode = gitmode(man2.execf(f))
Brendan Cully
Add diff --git option
r2907 addmodehdr(header, omode, nmode)
Brendan Cully
Add git-1.4 binary patch support
r3367 if util.binary(to) or util.binary(tn):
dodiff = 'binary'
Brendan Cully
Add diff --git option
r2907 r = None
Brendan Cully
Don't generate git diff header for empty diffs
r3329 header.insert(0, 'diff --git a/%s b/%s\n' % (a, b))
Alexis S. L. Carvalho
git binary patches: don't print the header for identical files
r4106 if dodiff:
if dodiff == 'binary':
text = b85diff(fp, to, tn)
else:
Thomas Arendsen Hein
merge with crew-stable
r4108 text = mdiff.unidiff(to, date1,
# ctx2 date may be dynamic
tn, util.datestr(ctx2.date()),
f, r, opts=opts)
Brendan Cully
Don't generate git diff header for empty diffs
r3329 if text or len(header) > 1:
Brendan Cully
Add diff --git option
r2907 fp.write(''.join(header))
Brendan Cully
Don't generate git diff header for empty diffs
r3329 fp.write(text)
Vadim Gelfer
refactor text diff/patch code....
r2874
def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
opts=None):
'''export changesets as hg patches.'''
total = len(revs)
Thomas Arendsen Hein
Don't use node length for calculating revision number length....
r3900 revwidth = max([len(str(rev)) for rev in revs])
Vadim Gelfer
refactor text diff/patch code....
r2874
Benoit Boissinot
commands.py: use contexts in export
r3970 def single(rev, seqno, fp):
ctx = repo.changectx(rev)
node = ctx.node()
parents = [p.node() for p in ctx.parents() if p]
Vadim Gelfer
refactor text diff/patch code....
r2874 if switch_parent:
parents.reverse()
prev = (parents and parents[0]) or nullid
if not fp:
fp = cmdutil.make_file(repo, template, node, total=total,
seqno=seqno, revwidth=revwidth)
Brendan Cully
Suppress <stdout> before hg export -v (regression from previous patch).
r4125 if fp != sys.stdout and hasattr(fp, 'name'):
Vadim Gelfer
refactor text diff/patch code....
r2874 repo.ui.note("%s\n" % fp.name)
fp.write("# HG changeset patch\n")
Benoit Boissinot
commands.py: use contexts in export
r3970 fp.write("# User %s\n" % ctx.user())
fp.write("# Date %d %d\n" % ctx.date())
Vadim Gelfer
refactor text diff/patch code....
r2874 fp.write("# Node ID %s\n" % hex(node))
fp.write("# Parent %s\n" % hex(prev))
if len(parents) > 1:
fp.write("# Parent %s\n" % hex(parents[1]))
Benoit Boissinot
commands.py: use contexts in export
r3970 fp.write(ctx.description().rstrip())
Vadim Gelfer
refactor text diff/patch code....
r2874 fp.write("\n\n")
diff(repo, prev, node, fp=fp, opts=opts)
if fp not in (sys.stdout, repo.ui):
fp.close()
Thomas Arendsen Hein
Don't use node length for calculating revision number length....
r3900 for seqno, rev in enumerate(revs):
Benoit Boissinot
commands.py: use contexts in export
r3970 single(rev, seqno+1, fp)
Matt Doar
Add support for diffstat in commit emails, and move diffstat from...
r3096
def diffstat(patchlines):
fd, name = tempfile.mkstemp(prefix="hg-patchbomb-", suffix=".txt")
try:
p = popen2.Popen3('diffstat -p1 -w79 2>/dev/null > ' + name)
try:
for line in patchlines: print >> p.tochild, line
p.tochild.close()
if p.wait(): return
fp = os.fdopen(fd, 'r')
stat = []
for line in fp: stat.append(line.lstrip())
last = stat.pop()
stat.insert(0, last)
stat = ''.join(stat)
if stat.startswith('0 files'): raise ValueError
return stat
except: raise
finally:
try: os.unlink(name)
except: pass