[PATCH 1 of 1] convert: make convert from cvs recognise branch points correctly

Henrik Stuart henrik.stuart at edlund.dk
Tue Apr 14 02:46:40 CDT 2009


# HG changeset patch
# User Henrik Stuart <henrik.stuart at edlund.dk>
# Date 1239545362 -7200
# Node ID bc98742e4eb7c2d8af9568ca80f38c3937cd076b
# Parent  aece3c9e62f1a685e4cebd3ecf0b22e1c8101ae1
convert: make convert from cvs recognise branch points correctly

This patch fixes issue 1447 by using the symbolic names for branches
in the rlog output to determine the branch point for each file and
then finding the latest possible branch point in the given changesets
such that all the file revisions match and the date is earlier than
the branch commit. For commits on non-branches, the current
functionality is maintained.

Co-contributor: Greg Ward <greg-hg at gerg.ca>

diff --git a/hgext/convert/cvsps.py b/hgext/convert/cvsps.py
--- a/hgext/convert/cvsps.py
+++ b/hgext/convert/cvsps.py
@@ -35,6 +35,7 @@
         .tags      - list of tags on the file
         .synthetic - is this a synthetic "file ... added on ..." revision?
         .mergepoint- the branch that has been merged from (if present in rlog output)
+        .branchesto- list of branches that occur at this revision
     '''
     def __init__(self, **entries):
         self.__dict__.update(entries)
@@ -378,6 +379,8 @@
             # clean up the results and save in the log.
             store = False
             e.tags = util.sort([scache(x) for x in tags.get(e.revision, [])])
+            e.branchesto = [tag for tag in branchmap if len(branchmap[tag].split('.')) > 2 and branchmap[tag].split('.')[-2] == '0' and e.revision == tuple([int(x) for x in branchmap[tag].split('.')[:-2]])]
+
             e.comment = scache('\n'.join(e.comment))
 
             revn = len(e.revision)
@@ -582,6 +585,7 @@
 
     versions = {}    # changeset index where we saw any particular file version
     branches = {}    # changeset index where we saw a branch
+    has_logentry_without_branchesto = False # variable to only output warning for missing branchesto once
     n = len(changesets)
     i = 0
     while i<n:
@@ -599,8 +603,38 @@
 
         c.parents = []
         if p is not None:
+            parent_idx = p
             p = changesets[p]
 
+            if p is not None:
+                if c.branch != p.branch:
+                    earliest_commit = min([l.date for l in log if l.branch == c.branch]) # necessary as the first commit on a branch can be broken into multiple bites
+
+                    q = parent_idx
+                    seen_branched_files = util.set([e.file for e in c.entries]) # already in p at the latest
+                    while q + 1 < i: # only need to search up to current
+                        qcs = changesets[q + 1]
+
+                        if qcs.date >= earliest_commit:
+                            break
+
+                        if any([e.file in seen_branched_files for e in qcs.entries]):
+                            break # we cannot progress past this place
+
+                        branchesto = getattr(e, 'branchesto', None)
+                        if branchesto == None and not has_logentry_without_branchesto:
+                            has_logentry_without_branchesto = True
+                            ui.warn(_("CVS cache has been compiled without correct branch information.\n"
+                                "If you have branches in your CVS repository it is strongly adviced to\n"
+                                "delete the cache and start over.\n"))
+                        if branchesto == None:
+                            branchesto = []
+                        seen_branched_files |= util.set([e.file for e in qcs.entries if c.branch in branchesto])
+                                
+                        q += 1
+
+                    p = changesets[q]
+
             # Ensure no changeset has a synthetic changeset as a parent.
             while p.synthetic:
                 assert len(p.parents) <= 1, \
diff --git a/tests/test-convert-cvs-branchpoints b/tests/test-convert-cvs-branchpoints
new file mode 100755
--- /dev/null
+++ b/tests/test-convert-cvs-branchpoints
@@ -0,0 +1,166 @@
+#!/bin/sh
+
+"$TESTDIR/hghave" cvs || exit 80
+
+cvscall()
+{
+    echo cvs -f "$@"
+    cvs -f "$@"
+    sleep 1
+}
+
+# output of 'cvs ci' varies unpredictably, so just discard it
+cvsci()
+{
+    echo cvs -f ci "$@"
+    cvs -f ci "$@" >/dev/null 2>&1
+    sleep 1
+}
+
+createmaster()
+{
+    rm -rf master
+    mkdir master
+    export CVSROOT=`pwd`/master
+    cvscall -q init
+    mkdir -p master/work
+    rm -rf work
+    cvscall -q co -d work work
+}
+
+hgcvsps()
+{
+    sleep 1
+    hg debugcvsps -x --parents work | sed -e 's/Author:.*/Author:/' -e 's/Date:.*/Date:/'
+}
+
+echo "[extensions]" >> $HGRCPATH
+echo "convert = " >> $HGRCPATH
+echo "graphlog = " >> $HGRCPATH
+echo "[convert]" >> $HGRCPATH
+echo "cvsps=builtin" >> $HGRCPATH
+
+createvar0()
+{
+    echo Creating 1447 var 0
+    createmaster
+    cd work
+    echo a > a.txt
+    echo b > b.txt
+    cvscall -Q add *.txt
+    cvsci -m "initial"
+    echo "b fix" >> b.txt
+    cvsci -m "b fix"
+    echo "a fix" >> a.txt
+    cvsci -m "a fix"
+    cvscall -q rtag -b BRANCH work
+    cvscall -q up -r BRANCH
+    echo "b branch fix" >> b.txt
+    cvsci -m "b branch fix"
+    hgcvsps
+    cd ..
+}
+
+createvar1()
+{
+    echo Creating 1447 var 1
+    createmaster
+    cd work
+    echo a > a.txt
+    echo b > b.txt
+    cvscall -Q add *.txt
+    cvsci -m "initial"
+    echo "b fix" >> b.txt
+    cvsci -m "b fix"
+    echo "a fix" >> a.txt
+    cvsci -m "a fix"
+    cvscall -q tag FOO
+    echo "b fix 2" >> b.txt
+    cvsci -m "b fix 2"
+    cvscall -q up -r 1.2 *.txt
+    cvscall -q rtag -b -r FOO BRANCH work
+    cvscall -q up -r BRANCH
+    echo "b branch fix" >> b.txt
+    cvsci -m "b branch fix"
+    hgcvsps
+    cd ..
+}
+
+createvar2()
+{
+    echo Creating 1447 var 2
+    createmaster
+    cd work
+    echo a > a.txt
+    echo b > b.txt
+    cvscall -Q add *.txt
+    cvsci -m "initial"
+    echo "b fix" >> b.txt
+    cvsci -m "b fix"
+    echo "a fix" >> a.txt
+    cvsci -m "a fix"
+    cvscall -q tag FOO
+    echo "a fix 2" >> a.txt
+    cvsci -m "a fix 2"
+    cvscall -q up -r 1.2 *.txt
+    cvscall -q rtag -b -r FOO BRANCH work
+    cvscall -q up -r BRANCH
+    echo "b branch fix" >> b.txt
+    cvsci -m "b branch fix"
+    hgcvsps
+    cd ..
+}
+
+createvar3()
+{
+    echo Creating 1447 var 3
+    createmaster
+    cd work
+    echo a > a.txt
+    echo b > b.txt
+    cvscall -Q add *.txt
+    cvsci -m "initial"
+    echo "b fix" >> b.txt
+    cvsci -m "b fix"
+    echo "a fix" >> a.txt
+    cvsci -m "a fix"
+    cvscall -q rtag -b BRANCH work
+    cvscall -q up -r BRANCH
+    echo "b branch fix" >> b.txt
+    cvsci -m "b branch fix"
+    cvscall -q up -A
+    echo "b fix 2" >> b.txt
+    cvsci -m "b fix 2"
+    hgcvsps
+    cd ..
+}
+
+createvar4()
+{
+    echo Creating 1447 var 3
+    createmaster
+    cd work
+    echo a > a.txt
+    echo b > b.txt
+    cvscall -Q add *.txt
+    cvsci -m "initial"
+    echo "b fix" >> b.txt
+    cvsci -m "b fix"
+    echo "a fix" >> a.txt
+    cvsci -m "a fix"
+    cvscall -q rtag -b BRANCH work
+    cvscall -q up -r BRANCH
+    echo "b branch fix" >> b.txt
+    cvsci -m "b branch fix"
+    cvscall -q up -A
+    echo "a fix 2" >> a.txt
+    cvsci -m "a fix 2"
+    hgcvsps
+    cd ..
+}
+
+createvar0
+createvar1
+createvar2
+createvar3
+createvar4
diff --git a/tests/test-convert-cvs-branchpoints.out b/tests/test-convert-cvs-branchpoints.out
new file mode 100644
--- /dev/null
+++ b/tests/test-convert-cvs-branchpoints.out
@@ -0,0 +1,400 @@
+Creating 1447 var 0
+cvs -f -q init
+cvs -f -q co -d work work
+cvs -f -Q add a.txt b.txt
+cvs -f ci -m initial
+cvs -f ci -m b fix
+cvs -f ci -m a fix
+cvs -f -q rtag -b BRANCH work
+cvs -f -q up -r BRANCH
+cvs -f ci -m b branch fix
+collecting CVS rlog
+5 log entries
+creating changesets
+4 changeset entries
+---------------------
+PatchSet 1 
+Date:
+Author:
+Branch: HEAD
+Tag: (none) 
+Log:
+initial
+
+Members: 
+	a.txt:INITIAL->1.1 
+	b.txt:INITIAL->1.1 
+
+---------------------
+PatchSet 2 
+Date:
+Author:
+Branch: HEAD
+Tag: (none) 
+Parent: 1
+Log:
+b fix
+
+Members: 
+	b.txt:1.1->1.2 
+
+---------------------
+PatchSet 3 
+Date:
+Author:
+Branch: HEAD
+Tag: (none) 
+Parent: 2
+Log:
+a fix
+
+Members: 
+	a.txt:1.1->1.2 
+
+---------------------
+PatchSet 4 
+Date:
+Author:
+Branch: BRANCH
+Tag: (none) 
+Parent: 3
+Log:
+b branch fix
+
+Members: 
+	b.txt:1.2->1.2.2.1 
+
+Creating 1447 var 1
+cvs -f -q init
+cvs -f -q co -d work work
+cvs -f -Q add a.txt b.txt
+cvs -f ci -m initial
+cvs -f ci -m b fix
+cvs -f ci -m a fix
+cvs -f -q tag FOO
+T a.txt
+T b.txt
+cvs -f ci -m b fix 2
+cvs -f -q up -r 1.2 a.txt b.txt
+U b.txt
+cvs -f -q rtag -b -r FOO BRANCH work
+cvs -f -q up -r BRANCH
+cvs -f ci -m b branch fix
+collecting CVS rlog
+6 log entries
+creating changesets
+5 changeset entries
+---------------------
+PatchSet 1 
+Date:
+Author:
+Branch: HEAD
+Tag: (none) 
+Log:
+initial
+
+Members: 
+	a.txt:INITIAL->1.1 
+	b.txt:INITIAL->1.1 
+
+---------------------
+PatchSet 2 
+Date:
+Author:
+Branch: HEAD
+Tag: (none) 
+Parent: 1
+Log:
+b fix
+
+Members: 
+	b.txt:1.1->1.2 
+
+---------------------
+PatchSet 3 
+Date:
+Author:
+Branch: HEAD
+Tag: FOO 
+Parent: 2
+Log:
+a fix
+
+Members: 
+	a.txt:1.1->1.2 
+
+---------------------
+PatchSet 4 
+Date:
+Author:
+Branch: HEAD
+Tag: (none) 
+Parent: 3
+Log:
+b fix 2
+
+Members: 
+	b.txt:1.2->1.3 
+
+---------------------
+PatchSet 5 
+Date:
+Author:
+Branch: BRANCH
+Tag: (none) 
+Parent: 3
+Log:
+b branch fix
+
+Members: 
+	b.txt:1.2->1.2.2.1 
+
+Creating 1447 var 2
+cvs -f -q init
+cvs -f -q co -d work work
+cvs -f -Q add a.txt b.txt
+cvs -f ci -m initial
+cvs -f ci -m b fix
+cvs -f ci -m a fix
+cvs -f -q tag FOO
+T a.txt
+T b.txt
+cvs -f ci -m a fix 2
+cvs -f -q up -r 1.2 a.txt b.txt
+U a.txt
+cvs -f -q rtag -b -r FOO BRANCH work
+cvs -f -q up -r BRANCH
+cvs -f ci -m b branch fix
+collecting CVS rlog
+6 log entries
+creating changesets
+5 changeset entries
+---------------------
+PatchSet 1 
+Date:
+Author:
+Branch: HEAD
+Tag: (none) 
+Log:
+initial
+
+Members: 
+	a.txt:INITIAL->1.1 
+	b.txt:INITIAL->1.1 
+
+---------------------
+PatchSet 2 
+Date:
+Author:
+Branch: HEAD
+Tag: (none) 
+Parent: 1
+Log:
+b fix
+
+Members: 
+	b.txt:1.1->1.2 
+
+---------------------
+PatchSet 3 
+Date:
+Author:
+Branch: HEAD
+Tag: FOO 
+Parent: 2
+Log:
+a fix
+
+Members: 
+	a.txt:1.1->1.2 
+
+---------------------
+PatchSet 4 
+Date:
+Author:
+Branch: HEAD
+Tag: (none) 
+Parent: 3
+Log:
+a fix 2
+
+Members: 
+	a.txt:1.2->1.3 
+
+---------------------
+PatchSet 5 
+Date:
+Author:
+Branch: BRANCH
+Tag: (none) 
+Parent: 3
+Log:
+b branch fix
+
+Members: 
+	b.txt:1.2->1.2.2.1 
+
+Creating 1447 var 3
+cvs -f -q init
+cvs -f -q co -d work work
+cvs -f -Q add a.txt b.txt
+cvs -f ci -m initial
+cvs -f ci -m b fix
+cvs -f ci -m a fix
+cvs -f -q rtag -b BRANCH work
+cvs -f -q up -r BRANCH
+cvs -f ci -m b branch fix
+cvs -f -q up -A
+U b.txt
+cvs -f ci -m b fix 2
+collecting CVS rlog
+6 log entries
+creating changesets
+5 changeset entries
+---------------------
+PatchSet 1 
+Date:
+Author:
+Branch: HEAD
+Tag: (none) 
+Log:
+initial
+
+Members: 
+	a.txt:INITIAL->1.1 
+	b.txt:INITIAL->1.1 
+
+---------------------
+PatchSet 2 
+Date:
+Author:
+Branch: HEAD
+Tag: (none) 
+Parent: 1
+Log:
+b fix
+
+Members: 
+	b.txt:1.1->1.2 
+
+---------------------
+PatchSet 3 
+Date:
+Author:
+Branch: HEAD
+Tag: (none) 
+Parent: 2
+Log:
+a fix
+
+Members: 
+	a.txt:1.1->1.2 
+
+---------------------
+PatchSet 4 
+Date:
+Author:
+Branch: BRANCH
+Tag: (none) 
+Parent: 3
+Log:
+b branch fix
+
+Members: 
+	b.txt:1.2->1.2.2.1 
+
+---------------------
+PatchSet 5 
+Date:
+Author:
+Branch: HEAD
+Tag: (none) 
+Parent: 3
+Log:
+b fix 2
+
+Members: 
+	b.txt:1.2->1.3 
+
+Creating 1447 var 3
+cvs -f -q init
+cvs -f -q co -d work work
+cvs -f -Q add a.txt b.txt
+cvs -f ci -m initial
+cvs -f ci -m b fix
+cvs -f ci -m a fix
+cvs -f -q rtag -b BRANCH work
+cvs -f -q up -r BRANCH
+cvs -f ci -m b branch fix
+cvs -f -q up -A
+U b.txt
+cvs -f ci -m a fix 2
+collecting CVS rlog
+6 log entries
+creating changesets
+5 changeset entries
+---------------------
+PatchSet 1 
+Date:
+Author:
+Branch: HEAD
+Tag: (none) 
+Log:
+initial
+
+Members: 
+	a.txt:INITIAL->1.1 
+	b.txt:INITIAL->1.1 
+
+---------------------
+PatchSet 2 
+Date:
+Author:
+Branch: HEAD
+Tag: (none) 
+Parent: 1
+Log:
+b fix
+
+Members: 
+	b.txt:1.1->1.2 
+
+---------------------
+PatchSet 3 
+Date:
+Author:
+Branch: HEAD
+Tag: (none) 
+Parent: 2
+Log:
+a fix
+
+Members: 
+	a.txt:1.1->1.2 
+
+---------------------
+PatchSet 4 
+Date:
+Author:
+Branch: BRANCH
+Tag: (none) 
+Parent: 3
+Log:
+b branch fix
+
+Members: 
+	b.txt:1.2->1.2.2.1 
+
+---------------------
+PatchSet 5 
+Date:
+Author:
+Branch: HEAD
+Tag: (none) 
+Parent: 3
+Log:
+a fix 2
+
+Members: 
+	a.txt:1.2->1.3 
+


More information about the Mercurial-devel mailing list