Add diff --git option
authorBrendan Cully <brendan@kublai.com>
Mon, 14 Aug 2006 22:48:03 -0700
changeset 2907 8b02af865990
parent 2906 453097750fbf
child 2909 75bcb8210a37
Add diff --git option
mercurial/commands.py
mercurial/mdiff.py
mercurial/patch.py
mercurial/ui.py
tests/test-git-export
tests/test-git-export.out
tests/test-help.out
--- a/mercurial/commands.py	Mon Aug 14 15:51:35 2006 -0700
+++ b/mercurial/commands.py	Mon Aug 14 22:48:03 2006 -0700
@@ -2877,6 +2877,7 @@
           ('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	Mon Aug 14 15:51:35 2006 -0700
+++ b/mercurial/mdiff.py	Mon Aug 14 22:48:03 2006 -0700
@@ -23,6 +23,7 @@
     '''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 @@
         'context': 3,
         'text': False,
         'showfunc': True,
+        'git': False,
         'ignorews': False,
         'ignorewsamount': False,
         'ignoreblanklines': False,
--- a/mercurial/patch.py	Mon Aug 14 15:51:35 2006 -0700
+++ b/mercurial/patch.py	Mon Aug 14 22:48:03 2006 -0700
@@ -298,6 +298,9 @@
             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 @@
                 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 @@
         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	Mon Aug 14 15:51:35 2006 -0700
+++ b/mercurial/ui.py	Mon Aug 14 22:48:03 2006 -0700
@@ -174,6 +174,8 @@
             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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-git-export	Mon Aug 14 22:48:03 2006 -0700
@@ -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/"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-git-export.out	Mon Aug 14 22:48:03 2006 -0700
@@ -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	Mon Aug 14 15:51:35 2006 -0700
+++ b/tests/test-help.out	Mon Aug 14 22:48:03 2006 -0700
@@ -176,6 +176,7 @@
  -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