[PATCH 1 of 2] convert: fix history topology when using hg.tagsbranch

Edouard Gomez ed.gomez at free.fr
Sat Aug 15 15:06:08 CDT 2009


# HG changeset patch
# User Edouard Gomez <ed.gomez at free.fr>
# Date 1250366593 -7200
# Node ID 67e0abddf6605e2c16771324b5f3202b7dc30ac3
# Parent  cc3916411873596afa470e000d9436fff2939c59
convert: fix history topology when using hg.tagsbranch

When using hg.tagsbranch, all conversions fail at
transcribing the source repo topology with conversion
scenarios interleaving:
 - initial repo conversion,
 - then upstream tag
 - then later incremental conversion

The problem is caused by the usage of the last source
revid as source revid for the tagnode in the revision
map.

On a later incremental conversion, when resolving some
child commit of the true last source revid, convert
will parent the nodes to the tag node. If using
tagsbranch you get a bad topology where all incremental
commit nodes end up in the tag branch.

This patch addresses the problem using a fake id
provided by the source class which is used for all
tagnodes when using hg.tagsbranch. That way no regular
commits will ever happen in the tag branch.

The patch also adds a test case.

It uses a foreign SCM to make sure the test doesn't rely on
any magic that could be applied on a hg->hg conversion.

diff --git a/hgext/convert/common.py b/hgext/convert/common.py
--- a/hgext/convert/common.py
+++ b/hgext/convert/common.py
@@ -154,6 +154,19 @@
         """
         return None
 
+    def gettagsnodename(self):
+        """Return a source node name that can be used to track 'tagsbranch'
+        head.
+
+        The default implementation returns '0'
+        
+        If '0' is not valid for the source format, override the function so
+        that it returns a revid that fullfil the following conditions:
+          - it must be parseable by the revmap parser as source revid
+          - it must not match a possible source revid
+        """
+        return '0'
+
 class converter_sink(object):
     """Conversion sink (target) interface"""
 
diff --git a/hgext/convert/convcmd.py b/hgext/convert/convcmd.py
--- a/hgext/convert/convcmd.py
+++ b/hgext/convert/convcmd.py
@@ -340,7 +340,11 @@
                 # write another hash correspondence to override the previous
                 # one so we don't end up with extra tag heads
                 if nrev:
-                    self.map[c] = nrev
+                    if isinstance(self.dest, mercurial_sink) and self.dest.tagsbranch != 'default':
+                        tagsnodename = self.source.gettagsnodename()
+                    else:
+                        tagsnodename = c
+                    self.map[tagsnodename] = nrev
 
             self.writeauthormap()
         finally:
diff --git a/hgext/convert/subversion.py b/hgext/convert/subversion.py
--- a/hgext/convert/subversion.py
+++ b/hgext/convert/subversion.py
@@ -510,6 +510,9 @@
     def revid(self, revnum, module=None):
         return 'svn:%s%s@%s' % (self.uuid, module or self.module, revnum)
 
+    def gettagsnodename(self):
+        return self.revid(0)
+
     def revnum(self, rev):
         return int(rev.split('@')[-1])
 
diff --git a/tests/test-convert-tagsbranch-topology b/tests/test-convert-tagsbranch-topology
new file mode 100755
--- /dev/null
+++ b/tests/test-convert-tagsbranch-topology
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+"$TESTDIR/hghave" git || exit 80
+
+echo "[extensions]" >> $HGRCPATH
+echo "convert=" >> $HGRCPATH
+echo 'hgext.graphlog =' >> $HGRCPATH
+echo '[convert]' >> $HGRCPATH
+echo 'hg.usebranchnames = True' >> $HGRCPATH
+echo 'hg.tagsbranch = tags-update' >> $HGRCPATH
+
+GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
+GIT_AUTHOR_EMAIL='test at example.org'; export GIT_AUTHOR_EMAIL
+GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0000"; export GIT_AUTHOR_DATE
+GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
+GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
+GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
+
+count=10
+action()
+{
+    GIT_AUTHOR_DATE="2007-01-01 00:00:$count +0000"
+    GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
+    git "$@" >/dev/null 2>/dev/null || echo "git command error"
+    count=`expr $count + 1`
+}
+
+glog()
+{
+    hg glog --template '{rev} "{desc|firstline}" files: {files}\n' "$@"
+}
+
+convertrepo()
+{
+    hg convert --datesort git-repo hg-repo
+}
+
+# Build a GIT repo with at least 1 tag
+mkdir git-repo
+cd git-repo
+git init >/dev/null 2>&1
+echo a > a
+git add a
+action commit -m "rev1"
+action tag -m "tag1" tag1
+cd ..
+
+# Do a first conversion
+convertrepo
+
+# Simulate upstream  updates after first conversion
+cd git-repo
+echo b > a
+git add a
+action commit -m "rev2"
+action tag -m "tag2" tag2
+cd ..
+
+# Perform an incremental conversion
+convertrepo
+
+# Print the log
+cd hg-repo
+glog
+
+# exit nicely
+true
diff --git a/tests/test-convert-tagsbranch-topology.out b/tests/test-convert-tagsbranch-topology.out
new file mode 100644
--- /dev/null
+++ b/tests/test-convert-tagsbranch-topology.out
@@ -0,0 +1,19 @@
+initializing destination hg-repo repository
+scanning source...
+sorting...
+converting...
+0 rev1
+updating tags
+scanning source...
+sorting...
+converting...
+0 rev2
+updating tags
+o  3 "update tags" files: .hgtags
+|
+| o  2 "rev2" files: a
+| |
+o |  1 "update tags" files: .hgtags
+ /
+o  0 "rev1" files: a
+


More information about the Mercurial-devel mailing list