--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -2877,6 +2877,7 @@ table = {
('a', 'text', None, _('treat all files as text')),
('p', 'show-function', None,
_('show which function each change is in')),
+ ('g', 'git', None, _('use git extended diff format')),
('w', 'ignore-all-space', None,
_('ignore white space when comparing lines')),
('b', 'ignore-space-change', None,
--- a/mercurial/mdiff.py
+++ b/mercurial/mdiff.py
@@ -23,6 +23,7 @@ class diffopts(object):
'''context is the number of context lines
text treats all files as text
showfunc enables diff -p output
+ git enables the git extended patch format
ignorews ignores all whitespace changes in the diff
ignorewsamount ignores changes in the amount of whitespace
ignoreblanklines ignores changes whose lines are all blank'''
@@ -31,6 +32,7 @@ class diffopts(object):
'context': 3,
'text': False,
'showfunc': True,
+ 'git': False,
'ignorews': False,
'ignorewsamount': False,
'ignoreblanklines': False,
--- a/mercurial/patch.py
+++ b/mercurial/patch.py
@@ -298,6 +298,9 @@ def diff(repo, node1=None, node2=None, f
return _date2
def read(f):
return repo.file(f).read(mmap2[f])
+ def renamed(f):
+ src = repo.file(f).renamed(mmap2[f])
+ return src and src[0] or None
else:
tz = util.makedate()[1]
_date2 = util.datestr()
@@ -309,6 +312,8 @@ def diff(repo, node1=None, node2=None, f
return _date2
def read(f):
return repo.wread(f)
+ def renamed(f):
+ return repo.dirstate.copies.get(f)
if repo.ui.quiet:
r = None
@@ -316,16 +321,65 @@ def diff(repo, node1=None, node2=None, f
hexfunc = repo.ui.verbose and hex or short
r = [hexfunc(node) for node in [node1, node2] if node]
+ if opts.git:
+ copied = {}
+ for f in added:
+ src = renamed(f)
+ if src:
+ copied[f] = src
+ srcs = [x[1] for x in copied.items()]
+
all = modified + added + removed
all.sort()
for f in all:
to = None
tn = None
+ dodiff = True
if f in mmap:
to = repo.file(f).read(mmap[f])
if f not in removed:
tn = read(f)
- fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, opts=opts))
+ 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
+ header = []
+ if f in added:
+ if node2:
+ mode = gitmode(mmap2.execf(f))
+ else:
+ mode = gitmode(util.is_exec(repo.wjoin(f), None))
+ if f in copied:
+ a = copied[f]
+ omode = gitmode(mmap.execf(a))
+ addmodehdr(header, omode, mode)
+ op = a in removed and 'rename' or 'copy'
+ header.append('%s from %s\n' % (op, a))
+ header.append('%s to %s\n' % (op, f))
+ to = repo.file(a).read(mmap[a])
+ else:
+ header.append('new file mode %s\n' % mode)
+ elif f in removed:
+ if f in srcs:
+ dodiff = False
+ else:
+ mode = gitmode(mmap.execf(f))
+ header.append('deleted file mode %s\n' % mode)
+ else:
+ omode = gitmode(mmap.execf(f))
+ nmode = gitmode(util.is_exec(repo.wjoin(f), mmap.execf(f)))
+ addmodehdr(header, omode, nmode)
+ r = None
+ if dodiff:
+ header.insert(0, 'diff --git a/%s b/%s\n' % (a, b))
+ fp.write(''.join(header))
+ if dodiff:
+ fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, opts=opts))
def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
opts=None):
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -174,6 +174,8 @@ class ui(object):
text=opts.get('text'),
showfunc=(opts.get('show_function') or
self.configbool('diff', 'showfunc', None)),
+ git=(opts.get('git') or
+ self.configbool('diff', 'git', None)),
ignorews=(opts.get('ignore_all_space') or
self.configbool('diff', 'ignorews', None)),
ignorewsamount=(opts.get('ignore_space_change') or
new file mode 100755
--- /dev/null
+++ b/tests/test-git-export
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+hg init a
+cd a
+
+echo start > start
+hg ci -Amstart -d '0 0'
+echo new > new
+hg ci -Amnew -d '0 0'
+echo '% new file'
+hg diff --git -r 0 | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/"
+
+hg cp new copy
+hg ci -mcopy -d '0 0'
+echo '% copy'
+hg diff --git -r 1:tip | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/"
+
+hg mv copy rename
+hg ci -mrename -d '0 0'
+echo '% rename'
+hg diff --git -r 2:tip | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/"
+
+hg rm rename
+hg ci -mdelete -d '0 0'
+echo '% delete'
+hg diff --git -r 3:tip | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/"
+
+cat > src <<EOF
+1
+2
+3
+4
+5
+EOF
+hg ci -Amsrc -d '0 0'
+chmod +x src
+hg ci -munexec -d '0 0'
+echo '% chmod 644'
+hg diff --git -r 5:tip | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/"
+
+hg mv src dst
+chmod -x dst
+echo a >> dst
+hg ci -mrenamemod -d '0 0'
+echo '% rename+mod+chmod'
+hg diff --git -r 6:tip | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/"
new file mode 100644
--- /dev/null
+++ b/tests/test-git-export.out
@@ -0,0 +1,42 @@
+adding start
+adding new
+% new file
+diff --git a/new b/new
+new file mode 100644
+--- /dev/null
++++ b/new
+@@ -0,0 +1,1 @@
++new
+% copy
+diff --git a/new b/copy
+copy from new
+copy to copy
+% rename
+diff --git a/copy b/rename
+rename from copy
+rename to rename
+% delete
+diff --git a/rename b/rename
+deleted file mode 100644
+--- a/rename
++++ /dev/null
+@@ -1,1 +0,0 @@
+-new
+adding src
+% chmod 644
+diff --git a/src b/src
+old mode 100644
+new mode 100755
+% rename+mod+chmod
+diff --git a/src b/dst
+old mode 100755
+new mode 100644
+rename from src
+rename to dst
+--- a/dst
++++ b/dst
+@@ -3,3 +3,4 @@ 3
+ 3
+ 4
+ 5
++a
--- a/tests/test-help.out
+++ b/tests/test-help.out
@@ -176,6 +176,7 @@ options:
-r --rev revision
-a --text treat all files as text
-p --show-function show which function each change is in
+ -g --git use git extended diff format
-w --ignore-all-space ignore white space when comparing lines
-b --ignore-space-change ignore changes in the amount of white space
-B --ignore-blank-lines ignore changes whose lines are all blank