--- a/hgext/convert/subversion.py Sat Jan 26 14:45:04 2008 +0100
+++ b/hgext/convert/subversion.py Sat Jan 26 14:45:04 2008 +0100
@@ -200,9 +200,9 @@
except IOError, e:
pass
- self.last_changed = self.latest(self.module, latest)
-
- self.head = self.revid(self.last_changed)
+ self.head = self.latest(self.module, latest)
+ self.last_changed = self.revnum(self.head)
+
self._changescache = None
if os.path.exists(os.path.join(url, '.svn/entries')):
@@ -252,8 +252,7 @@
if trunk:
oldmodule = self.module or ''
self.module += '/' + trunk
- lt = self.latest(self.module, self.last_changed)
- self.head = self.revid(lt)
+ self.head = self.latest(self.module, self.last_changed)
# First head in the list is the module's head
self.heads = [self.head]
@@ -266,10 +265,10 @@
self.ctx)
for branch in branchnames.keys():
module = '%s/%s/%s' % (oldmodule, branches, branch)
- brevnum = self.latest(module, self.last_changed)
- brev = self.revid(brevnum, module)
- self.ui.note('found branch %s at %d\n' % (branch, brevnum))
- self.heads.append(brev)
+ brevid = self.latest(module, self.last_changed)
+ self.ui.note('found branch %s at %d\n' %
+ (branch, self.revnum(brevid)))
+ self.heads.append(brevid)
return self.heads
@@ -369,7 +368,10 @@
return uuid, mod, revnum
def latest(self, path, stop=0):
- 'find the latest revision affecting path, up to stop'
+ """Find the latest revid affecting path, up to stop. It may return
+ a revision in a different module, since a branch may be moved without
+ a change being reported.
+ """
if not stop:
stop = svn.ra.get_latest_revnum(self.ra)
try:
@@ -381,7 +383,28 @@
if not dirent:
raise util.Abort('%s not found up to revision %d' % (path, stop))
- return dirent.created_rev
+ # stat() gives us the previous revision on this line of development, but
+ # it might be in *another module*. Fetch the log and detect renames down
+ # to the latest revision.
+ stream = get_log(self.url, [path], stop, dirent.created_rev)
+ try:
+ for entry in stream:
+ paths, revnum, author, date, message = entry
+ if revnum <= dirent.created_rev:
+ break
+
+ for p in paths:
+ if not path.startswith(p) or not paths[p].copyfrom_path:
+ continue
+ newpath = paths[p].copyfrom_path + path[len(p):]
+ self.ui.debug("branch renamed from %s to %s at %d\n" %
+ (path, newpath, revnum))
+ path = newpath
+ break
+ finally:
+ stream.close()
+
+ return self.revid(dirent.created_rev, path)
def get_blacklist(self):
"""Avoid certain revision numbers.
@@ -624,10 +647,11 @@
ent = orig_paths[self.module]
if ent.copyfrom_path:
# ent.copyfrom_rev may not be the actual last revision
- prev = self.latest(ent.copyfrom_path, ent.copyfrom_rev)
- parents = [self.revid(prev, ent.copyfrom_path)]
- self.ui.note('found parent of branch %s at %d: %s\n' % \
- (self.module, prev, ent.copyfrom_path))
+ previd = self.latest(ent.copyfrom_path, ent.copyfrom_rev)
+ parents = [previd]
+ prevmodule, prevnum = self.revsplit(previd)[1:]
+ self.ui.note('found parent of branch %s at %d: %s\n' %
+ (self.module, prevnum, prevmodule))
else:
self.ui.debug("No copyfrom path, don't know what to do.\n")
@@ -704,7 +728,7 @@
firstrevnum = self.revnum(firstcset.rev)
if firstrevnum > 1:
latest = self.latest(self.module, firstrevnum - 1)
- firstcset.parents.append(self.revid(latest))
+ firstcset.parents.append(latest)
except util.Abort:
pass
except SubversionException, (inst, num):
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-convert-svn-move Sat Jan 26 14:45:04 2008 +0100
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+"$TESTDIR/hghave" svn svn-bindings || exit 80
+
+fix_path()
+{
+ tr '\\' /
+}
+
+echo "[extensions]" >> $HGRCPATH
+echo "convert = " >> $HGRCPATH
+echo "hgext.graphlog =" >> $HGRCPATH
+
+svnadmin create svn-repo
+
+svnpath=`pwd | fix_path`
+# SVN wants all paths to start with a slash. Unfortunately,
+# Windows ones don't. Handle that.
+expr $svnpath : "\/" > /dev/null
+if [ $? -ne 0 ]; then
+ svnpath='/'$svnpath
+fi
+
+echo % initial svn import
+mkdir projA
+cd projA
+mkdir trunk
+mkdir trunk/d1
+echo b > trunk/d1/b
+cd ..
+
+svnurl=file://$svnpath/svn-repo/projA
+svn import -m "init projA" projA $svnurl | fix_path
+
+# Build a module renaming chain which used to confuse the converter.
+echo % update svn repository
+svn co $svnurl A | fix_path
+cd A
+svn mv $svnurl/trunk $svnurl/subproject -m movedtrunk
+svn up
+mkdir subproject/trunk
+svn add subproject/trunk
+svn ci -m createtrunk
+mkdir subproject/branches
+svn add subproject/branches
+svn ci -m createbranches
+svn mv $svnurl/subproject/d1 $svnurl/subproject/trunk/d1 -m moved1
+svn up
+echo b >> subproject/trunk/d1/b
+svn ci -m changeb
+svn mv $svnurl/subproject/trunk/d1 $svnurl/subproject/branches/d1 -m moved1again
+cd ..
+
+echo % convert trunk and branches
+hg convert --datesort $svnurl/subproject A-hg
+
+cd A-hg
+hg glog --template '#rev# #desc|firstline# files: #files#\n'
+hg branches | sed 's/:.*/:/'
+cd ..
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-convert-svn-move.out Sat Jan 26 14:45:04 2008 +0100
@@ -0,0 +1,68 @@
+% initial svn import
+Adding projA/trunk
+Adding projA/trunk/d1
+Adding projA/trunk/d1/b
+
+Committed revision 1.
+% update svn repository
+A A/trunk
+A A/trunk/d1
+A A/trunk/d1/b
+Checked out revision 1.
+
+Committed revision 2.
+D trunk
+A subproject
+A subproject/d1
+A subproject/d1/b
+Updated to revision 2.
+A subproject/trunk
+Adding subproject/trunk
+
+Committed revision 3.
+A subproject/branches
+Adding subproject/branches
+
+Committed revision 4.
+
+Committed revision 5.
+A subproject/trunk/d1
+A subproject/trunk/d1/b
+D subproject/d1
+Updated to revision 5.
+Sending subproject/trunk/d1/b
+Transmitting file data .
+Committed revision 6.
+
+Committed revision 7.
+% convert trunk and branches
+initializing destination A-hg repository
+scanning source...
+sorting...
+converting...
+7 init projA
+6 createtrunk
+5 moved1
+4 moved1
+3 changeb
+2 changeb
+1 moved1again
+0 moved1again
+o 7 moved1again files: d1/b
+|
+| o 6 moved1again files:
+| |
+o | 5 changeb files: d1/b
+| |
+| o 4 changeb files: b
+| |
+o | 3 moved1 files: d1/b
+| |
+| o 2 moved1 files:
+| |
+o | 1 createtrunk files:
+ /
+o 0 init projA files: b
+
+default 7:
+d1 6: