Teach mq about git patches
authorBrendan Cully <brendan@kublai.com>
Wed, 16 Aug 2006 19:49:45 -0700
changeset 2934 2f190e998eb3
parent 2933 439fd013360d
child 2935 1c66aad252f9
Teach mq about git patches
hgext/mq.py
mercurial/patch.py
tests/test-mq
tests/test-mq.out
--- a/hgext/mq.py	Wed Aug 16 19:46:18 2006 -0700
+++ b/hgext/mq.py	Wed Aug 16 19:49:45 2006 -0700
@@ -252,6 +252,9 @@
 
         for line in file(pf):
             line = line.rstrip()
+            if line.startswith('diff --git'):
+                diffstart = 2
+                break
             if diffstart:
                 if line.startswith('+++ '):
                     diffstart = 2
@@ -408,7 +411,7 @@
                 self.ui.warn("patch failed, unable to continue (try -v)\n")
             return (False, [], False)
 
-        return (True, files.keys(), fuzz)
+        return (True, files, fuzz)
 
     def apply(self, repo, series, list=False, update_status=True,
               strict=False, patchdir=None, merge=None, wlock=None):
@@ -421,42 +424,37 @@
         lock = repo.lock()
         tr = repo.transaction()
         n = None
-        for patch in series:
-            pushable, reason = self.pushable(patch)
+        for patchname in series:
+            pushable, reason = self.pushable(patchname)
             if not pushable:
-                self.explain_pushable(patch, all_patches=True)
+                self.explain_pushable(patchname, all_patches=True)
                 continue
-            self.ui.warn("applying %s\n" % patch)
-            pf = os.path.join(patchdir, patch)
+            self.ui.warn("applying %s\n" % patchname)
+            pf = os.path.join(patchdir, patchname)
 
             try:
-                message, comments, user, date, patchfound = self.readheaders(patch)
+                message, comments, user, date, patchfound = self.readheaders(patchname)
             except:
-                self.ui.warn("Unable to read %s\n" % pf)
+                self.ui.warn("Unable to read %s\n" % patchname)
                 err = 1
                 break
 
             if not message:
-                message = "imported patch %s\n" % patch
+                message = "imported patch %s\n" % patchname
             else:
                 if list:
-                    message.append("\nimported patch %s" % patch)
+                    message.append("\nimported patch %s" % patchname)
                 message = '\n'.join(message)
 
             (patcherr, files, fuzz) = self.patch(repo, pf)
             patcherr = not patcherr
 
-            if merge and len(files) > 0:
+            if merge and files:
                 # Mark as merged and update dirstate parent info
-                repo.dirstate.update(repo.dirstate.filterfiles(files), 'm')
+                repo.dirstate.update(repo.dirstate.filterfiles(files.keys()), 'm')
                 p1, p2 = repo.dirstate.parents()
                 repo.dirstate.setparents(p1, merge)
-            if len(files) > 0:
-                cwd = repo.getcwd()
-                cfiles = files
-                if cwd:
-                    cfiles = [util.pathto(cwd, f) for f in files]
-                cmdutil.addremove(repo, cfiles, wlock=wlock)
+            files = patch.updatedir(self.ui, repo, files, wlock=wlock)
             n = repo.commit(files, message, user, date, force=1, lock=lock,
                             wlock=wlock)
 
@@ -464,11 +462,11 @@
                 raise util.Abort(_("repo commit failed"))
 
             if update_status:
-                self.applied.append(statusentry(revlog.hex(n), patch))
+                self.applied.append(statusentry(revlog.hex(n), patchname))
 
             if patcherr:
                 if not patchfound:
-                    self.ui.warn("patch %s is empty\n" % patch)
+                    self.ui.warn("patch %s is empty\n" % patchname)
                     err = 0
                 else:
                     self.ui.warn("patch failed, rejects left in working dir\n")
@@ -999,7 +997,10 @@
 
             changes = repo.changelog.read(tip)
             repo.dirstate.setparents(*cparents)
+            copies = [(f, repo.dirstate.copied(f)) for f in a]
             repo.dirstate.update(a, 'a')
+            for dst, src in copies:
+                repo.dirstate.copy(src, dst)
             repo.dirstate.update(r, 'r')
             repo.dirstate.update(m, 'n')
             repo.dirstate.forget(forget)
--- a/mercurial/patch.py	Wed Aug 16 19:46:18 2006 -0700
+++ b/mercurial/patch.py	Wed Aug 16 19:49:45 2006 -0700
@@ -343,10 +343,27 @@
 
     if not node1:
         node1 = repo.dirstate.parents()[0]
+
+    clcache = {}
+    def getchangelog(n):
+        if n not in clcache:
+            clcache[n] = repo.changelog.read(n)
+        return clcache[n]
+    mcache = {}
+    def getmanifest(n):
+        if n not in mcache:
+            mcache[n] = repo.manifest.read(n)
+        return mcache[n]
+    fcache = {}
+    def getfile(f):
+        if f not in fcache:
+            fcache[f] = repo.file(f)
+        return fcache[f]
+
     # reading the data for node1 early allows it to play nicely
     # with repo.status and the revlog cache.
-    change = repo.changelog.read(node1)
-    mmap = repo.manifest.read(change[0])
+    change = getchangelog(node1)
+    mmap = getmanifest(change[0])
     date1 = util.datestr(change[2])
 
     if not changes:
@@ -367,17 +384,32 @@
     if not modified and not added and not removed:
         return
 
+    def renamedbetween(f, n1, n2):
+        r1, r2 = map(repo.changelog.rev, (n1, n2))
+        src = None
+        while r2 > r1:
+            cl = getchangelog(n2)[0]
+            m = getmanifest(cl)
+            try:
+                src = getfile(f).renamed(m[f])
+            except KeyError:
+                return None
+            if src:
+                f = src[0]
+            n2 = repo.changelog.parents(n2)[0]
+            r2 = repo.changelog.rev(n2)
+        return src
+
     if node2:
-        change = repo.changelog.read(node2)
-        mmap2 = repo.manifest.read(change[0])
+        change = getchangelog(node2)
+        mmap2 = getmanifest(change[0])
         _date2 = util.datestr(change[2])
         def date2(f):
             return _date2
         def read(f):
-            return repo.file(f).read(mmap2[f])
+            return getfile(f).read(mmap2[f])
         def renamed(f):
-            src = repo.file(f).renamed(mmap2[f])
-            return src and src[0] or None
+            return renamedbetween(f, node1, node2)
     else:
         tz = util.makedate()[1]
         _date2 = util.datestr()
@@ -390,7 +422,18 @@
         def read(f):
             return repo.wread(f)
         def renamed(f):
-            return repo.dirstate.copies.get(f)
+            src = repo.dirstate.copies.get(f)
+            parent = repo.dirstate.parents()[0]
+            if src:
+                f = src[0]
+            of = renamedbetween(f, node1, parent)
+            if of:
+                return of
+            elif src:
+                cl = getchangelog(parent)[0]
+                return (src, getmanifest(cl)[src])
+            else:
+                return None
 
     if repo.ui.quiet:
         r = None
@@ -404,7 +447,7 @@
             src = renamed(f)
             if src:
                 copied[f] = src
-        srcs = [x[1] for x in copied.items()]
+        srcs = [x[1][0] for x in copied.items()]
 
     all = modified + added + removed
     all.sort()
@@ -413,7 +456,7 @@
         tn = None
         dodiff = True
         if f in mmap:
-            to = repo.file(f).read(mmap[f])
+            to = getfile(f).read(mmap[f])
         if f not in removed:
             tn = read(f)
         if opts.git:
@@ -432,13 +475,13 @@
                 else:
                     mode = gitmode(util.is_exec(repo.wjoin(f), None))
                 if f in copied:
-                    a = copied[f]
+                    a, arev = 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])
+                    to = getfile(a).read(arev)
                 else:
                     header.append('new file mode %s\n' % mode)
             elif f in removed:
--- a/tests/test-mq	Wed Aug 16 19:46:18 2006 -0700
+++ b/tests/test-mq	Wed Aug 16 19:49:45 2006 -0700
@@ -126,3 +126,28 @@
 hg ci -Ama
 hg strip tip 2>&1 | sed 's/\(saving bundle to \).*/\1/'
 hg unbundle .hg/strip-backup/*
+
+cat >>$HGTMP/.hgrc <<EOF
+[diff]
+git = True
+EOF
+cd ..
+hg init git
+cd git
+hg qinit
+
+hg qnew -m'new file' new
+echo foo > new
+chmod +x new
+hg add new
+hg qrefresh
+sed -e "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/new
+
+hg qnew -m'copy file' copy
+hg cp new copy
+hg qrefresh
+sed -e "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/copy
+
+hg qpop
+hg qpush
+hg qdiff
--- a/tests/test-mq.out	Wed Aug 16 19:46:18 2006 -0700
+++ b/tests/test-mq.out	Wed Aug 16 19:49:45 2006 -0700
@@ -127,3 +127,22 @@
 adding file changes
 added 1 changesets with 1 changes to 1 files
 (run 'hg update' to get a working copy)
+new file
+
+diff --git a/new b/new
+new file mode 100755
+--- /dev/null
++++ b/new
+@@ -0,0 +1,1 @@
++foo
+copy file
+
+diff --git a/new b/copy
+copy from new
+copy to copy
+Now at: new
+applying copy
+Now at: copy
+diff --git a/new b/copy
+copy from new
+copy to copy