# HG changeset patch # User Patrick Mezard # Date 2008-01-26 13:45:04 # Node ID c4496b7c10cea2ee11598622bb2041c87a40a396 # Parent 851402e533370209bb1719bb06eb82796410b829 convert: fix svn_source.latest() diff --git a/hgext/convert/subversion.py b/hgext/convert/subversion.py --- a/hgext/convert/subversion.py +++ b/hgext/convert/subversion.py @@ -200,9 +200,9 @@ class svn_source(converter_source): 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 @@ class svn_source(converter_source): 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 @@ class svn_source(converter_source): 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 @@ class svn_source(converter_source): 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 @@ class svn_source(converter_source): 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 @@ class svn_source(converter_source): 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 @@ class svn_source(converter_source): 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): diff --git a/tests/test-convert-svn-move b/tests/test-convert-svn-move new file mode 100755 --- /dev/null +++ b/tests/test-convert-svn-move @@ -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 .. diff --git a/tests/test-convert-svn-move.out b/tests/test-convert-svn-move.out new file mode 100644 --- /dev/null +++ b/tests/test-convert-svn-move.out @@ -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: