# HG changeset patch # User Benoit Boissinot # Date 1206821754 -3600 # Node ID f08662abdf3f3a4806e39bf64d208ea134857dc4 # Parent f615ece5fec37c4769e26ce4b117e4ca8034e6c9# Parent d1cf40b596f8c175d87c874f060ac5d4e4e74939 merge with crew diff -r f615ece5fec3 -r f08662abdf3f hgext/convert/subversion.py --- a/hgext/convert/subversion.py Sat Mar 29 21:15:34 2008 +0100 +++ b/hgext/convert/subversion.py Sat Mar 29 21:15:54 2008 +0100 @@ -95,6 +95,10 @@ else: pickle.dump(None, fp, protocol) fp.close() + # With large history, cleanup process goes crazy and suddenly + # consumes *huge* amount of memory. The output file being closed, + # there is no need for clean termination. + os._exit(0) def debugsvnlog(ui, **opts): """Fetch SVN log in a subprocess and channel them back to parent to @@ -259,7 +263,7 @@ rev = optrev(self.last_changed) oldmodule = '' trunk = getcfgpath('trunk', rev) - tags = getcfgpath('tags', rev) + self.tags = getcfgpath('tags', rev) branches = getcfgpath('branches', rev) # If the project has a trunk or branches, we will extract heads @@ -274,7 +278,8 @@ # First head in the list is the module's head self.heads = [self.head] - self.tags = '%s/%s' % (oldmodule , (tags or 'tags')) + if self.tags is not None: + self.tags = '%s/%s' % (oldmodule , (self.tags or 'tags')) # Check if branches bring a few more heads to the list if branches: @@ -365,18 +370,58 @@ if self.tags is None: return tags - start = self.revnum(self.head) + # svn tags are just a convention, project branches left in a + # 'tags' directory. There is no other relationship than + # ancestry, which is expensive to discover and makes them hard + # to update incrementally. Worse, past revisions may be + # referenced by tags far away in the future, requiring a deep + # history traversal on every calculation. Current code + # performs a single backward traversal, tracking moves within + # the tags directory (tag renaming) and recording a new tag + # everytime a project is copied from outside the tags + # directory. It also lists deleted tags, this behaviour may + # change in the future. + pendings = [] + tagspath = self.tags + start = svn.ra.get_latest_revnum(self.ra) try: - for entry in get_log(self.url, [self.tags], self.startrev, start): - orig_paths, revnum, author, date, message = entry - for path in orig_paths: - if not path.startswith(self.tags+'/'): + for entry in get_log(self.url, [self.tags], start, self.startrev): + origpaths, revnum, author, date, message = entry + copies = [(e.copyfrom_path, e.copyfrom_rev, p) for p,e + in origpaths.iteritems() if e.copyfrom_path] + copies.sort() + # Apply moves/copies from more specific to general + copies.reverse() + + srctagspath = tagspath + if copies and copies[-1][2] == tagspath: + # Track tags directory moves + srctagspath = copies.pop()[0] + + for source, sourcerev, dest in copies: + if not dest.startswith(tagspath + '/'): continue - ent = orig_paths[path] - source = ent.copyfrom_path - rev = ent.copyfrom_rev - tag = path.split('/')[-1] - tags[tag] = self.revid(rev, module=source) + for tag in pendings: + if tag[0].startswith(dest): + tagpath = source + tag[0][len(dest):] + tag[:2] = [tagpath, sourcerev] + break + else: + pendings.append([source, sourcerev, dest.split('/')[-1]]) + + # Tell tag renamings from tag creations + remainings = [] + for source, sourcerev, tagname in pendings: + if source.startswith(srctagspath): + remainings.append([source, sourcerev, tagname]) + continue + # From revision may be fake, get one with changes + tagid = self.latest(source, sourcerev) + if tagid: + tags[tagname] = tagid + pendings = remainings + tagspath = srctagspath + except SubversionException, (inst, num): self.ui.note('no tags found at revision %d\n' % start) return tags diff -r f615ece5fec3 -r f08662abdf3f tests/test-convert-mtn --- a/tests/test-convert-mtn Sat Mar 29 21:15:34 2008 +0100 +++ b/tests/test-convert-mtn Sat Mar 29 21:15:54 2008 +0100 @@ -58,6 +58,15 @@ # Test directory move mtn mv dir dir2 mtn ci -m movedir +# Test directory removal with empty directory +mkdir dir2/dir +mkdir dir2/dir/subdir +echo f > dir2/dir/subdir/f +mkdir dir2/dir/emptydir +mtn add -R dir2/dir +mtn ci -m emptydir +mtn drop -R dir2/dir +mtn ci -m dropdirectory cd .. echo % convert incrementally @@ -75,4 +84,6 @@ hg manifest echo % contents cat dir2/a +test -d dir2/dir && echo 'removed dir2/dir is still there!' +exit 0 diff -r f615ece5fec3 -r f08662abdf3f tests/test-convert-mtn.out --- a/tests/test-convert-mtn.out Sat Mar 29 21:15:34 2008 +0100 +++ b/tests/test-convert-mtn.out Sat Mar 29 21:15:54 2008 +0100 @@ -29,15 +29,33 @@ mtn: renaming dir to dir2 in workspace manifest mtn: beginning commit on branch 'com.selenic.test' mtn: committed revision 5de5abe7c15eae70cf3acdda23c9c319ea50c1af +mtn: adding dir2/dir to workspace manifest +mtn: adding dir2/dir/emptydir to workspace manifest +mtn: adding dir2/dir/subdir to workspace manifest +mtn: adding dir2/dir/subdir/f to workspace manifest +mtn: beginning commit on branch 'com.selenic.test' +mtn: committed revision 27a423be1e406595cc57f50f42a8790fa0a93d8e +mtn: dropping dir2/dir/subdir/f from workspace manifest +mtn: dropping dir2/dir/subdir from workspace manifest +mtn: dropping dir2/dir/emptydir from workspace manifest +mtn: dropping dir2/dir from workspace manifest +mtn: beginning commit on branch 'com.selenic.test' +mtn: committed revision ba57ba5ac63178529d37fa8a2a1a012fc0e42047 % convert incrementally assuming destination repo.mtn-hg scanning source... sorting... converting... -1 update2 -0 movedir +3 update2 +2 movedir +1 emptydir +0 dropdirectory 3 files updated, 0 files merged, 0 files removed, 0 files unresolved -@ 3 "movedir" files: dir/a dir2/a +@ 5 "dropdirectory" files: dir2/dir/subdir/f +| +o 4 "emptydir" files: dir2/dir/subdir/f +| +o 3 "movedir" files: dir/a dir2/a | o 2 "update2" files: bin bin2 dir/b e | diff -r f615ece5fec3 -r f08662abdf3f tests/test-convert-svn-source --- a/tests/test-convert-svn-source Sat Mar 29 21:15:34 2008 +0100 +++ b/tests/test-convert-svn-source Sat Mar 29 21:15:54 2008 +0100 @@ -9,15 +9,10 @@ echo "[extensions]" >> $HGRCPATH echo "convert = " >> $HGRCPATH +echo 'hgext.graphlog =' >> $HGRCPATH svnadmin create svn-repo -echo % initial svn import -mkdir t -cd t -echo a > a -cd .. - svnpath=`pwd | fix_path` # SVN wants all paths to start with a slash. Unfortunately, # Windows ones don't. Handle that. @@ -26,106 +21,6 @@ svnpath='/'$svnpath fi -svnurl=file://$svnpath/svn-repo/trunk/test -svn import -m init t $svnurl | fix_path - -echo % update svn repository -svn co $svnurl t2 | fix_path -cd t2 -echo b >> a -echo b > b -svn add b -svn ci -m changea -cd .. - -echo % convert to hg once -hg convert $svnurl - -echo % update svn repository again -cd t2 -echo c >> a -echo c >> b -svn ci -m changeb -cd .. - -echo % test incremental conversion -hg convert -v $svnurl | sed 's/source:.*/source:/' - -echo % test filemap -echo 'include b' > filemap -hg convert --filemap filemap $svnurl fmap -echo '[extensions]' >> $HGRCPATH -echo 'hgext.graphlog =' >> $HGRCPATH -hg glog -R fmap --template '#rev# #desc|firstline# files: #files#\n' - -echo % test stop revision -hg convert --rev 1 $svnurl stoprev -# Check convert_revision extra-records. -# This is also the only place testing more than one extra field -# in a revision. -hg --cwd stoprev tip --debug | grep extra | sed 's/=.*/=/' - -######################################## - -echo "# now tests that it works with trunk/branches/tags layout" -echo -echo % initial svn import -mkdir projA -cd projA -mkdir trunk -mkdir branches -mkdir tags -cd .. - -svnurl=file://$svnpath/svn-repo/projA -svn import -m "init projA" projA $svnurl | fix_path - - -echo % update svn repository -svn co $svnurl/trunk A | fix_path -cd A -echo hello > letter.txt -svn add letter.txt -svn ci -m hello - -echo world >> letter.txt -svn ci -m world - -svn copy -m "tag v0.1" $svnurl/trunk $svnurl/tags/v0.1 - -echo 'nice day today!' >> letter.txt -svn ci -m "nice day" -cd .. - -echo % convert to hg once -hg convert $svnurl A-hg - -echo % update svn repository again -cd A -echo "see second letter" >> letter.txt -# Put it in a subdirectory to test duplicate file records -# from svn source (issue 714) -mkdir todo -echo "nice to meet you" > todo/letter2.txt -svn add todo -svn ci -m "second letter" - -svn copy -m "tag v0.2" $svnurl/trunk $svnurl/tags/v0.2 - -echo "blah-blah-blah" >> todo/letter2.txt -svn ci -m "work in progress" -cd .. - -echo % test incremental conversion -hg convert $svnurl A-hg - -cd A-hg -hg glog --template '#rev# #desc|firstline# files: #files#\n' -hg tags -q -cd .. - -######################################## - echo "# now tests that it works with trunk/tags layout, but no branches yet" echo echo % initial svn import @@ -171,6 +66,8 @@ svn ci -m "work in progress" cd .. +######################################## + echo % test incremental conversion hg convert $svnurl B-hg @@ -178,3 +75,15 @@ hg glog --template '#rev# #desc|firstline# files: #files#\n' hg tags -q cd .. + +echo % test filemap +echo 'include letter2.txt' > filemap +hg convert --filemap filemap $svnurl/trunk fmap +hg glog -R fmap --template '#rev# #desc|firstline# files: #files#\n' + +echo % test stop revision +hg convert --rev 1 $svnurl/trunk stoprev +# Check convert_revision extra-records. +# This is also the only place testing more than one extra field +# in a revision. +hg --cwd stoprev tip --debug | grep extra | sed 's/=.*/=/' diff -r f615ece5fec3 -r f08662abdf3f tests/test-convert-svn-source.out --- a/tests/test-convert-svn-source.out Sat Mar 29 21:15:34 2008 +0100 +++ b/tests/test-convert-svn-source.out Sat Mar 29 21:15:54 2008 +0100 @@ -1,151 +1,24 @@ -% initial svn import -Adding t/a - -Committed revision 1. -% update svn repository -A t2/a -Checked out revision 1. -A b -Sending a -Adding b -Transmitting file data .. -Committed revision 2. -% convert to hg once -assuming destination test-hg -initializing destination test-hg repository -scanning source... -sorting... -converting... -1 init -0 changea -% update svn repository again -Sending a -Sending b -Transmitting file data .. -Committed revision 3. -% test incremental conversion -assuming destination test-hg -scanning source... -fetching revision log for "/trunk/test" from 3 to 2 -sorting... -converting... -0 changeb -source: -a -b -no tags found at revision 3 -% test filemap -initializing destination fmap repository -scanning source... -sorting... -converting... -2 init -1 changea -0 changeb -o 1 changeb files: b -| -o 0 changea files: b - -% test stop revision -initializing destination stoprev repository -scanning source... -sorting... -converting... -0 init -extra: branch= -extra: convert_revision= -# now tests that it works with trunk/branches/tags layout - -% initial svn import -Adding projA/trunk -Adding projA/branches -Adding projA/tags - -Committed revision 4. -% update svn repository -Checked out revision 4. -A letter.txt -Adding letter.txt -Transmitting file data . -Committed revision 5. -Sending letter.txt -Transmitting file data . -Committed revision 6. - -Committed revision 7. -Sending letter.txt -Transmitting file data . -Committed revision 8. -% convert to hg once -initializing destination A-hg repository -scanning source... -sorting... -converting... -3 init projA -2 hello -1 world -0 nice day -updating tags -% update svn repository again -A todo -A todo/letter2.txt -Sending letter.txt -Adding todo -Adding todo/letter2.txt -Transmitting file data .. -Committed revision 9. - -Committed revision 10. -Sending todo/letter2.txt -Transmitting file data . -Committed revision 11. -% test incremental conversion -scanning source... -sorting... -converting... -1 second letter -0 work in progress -updating tags -o 7 update tags files: .hgtags -| -o 6 work in progress files: todo/letter2.txt -| -o 5 second letter files: letter.txt todo/letter2.txt -| -o 4 update tags files: .hgtags -| -o 3 nice day files: letter.txt -| -o 2 world files: letter.txt -| -o 1 hello files: letter.txt -| -o 0 init projA files: - -tip -v0.2 -v0.1 # now tests that it works with trunk/tags layout, but no branches yet % initial svn import Adding projB/trunk Adding projB/tags -Committed revision 12. +Committed revision 1. % update svn repository -Checked out revision 12. +Checked out revision 1. A letter.txt Adding letter.txt Transmitting file data . -Committed revision 13. +Committed revision 2. Sending letter.txt Transmitting file data . -Committed revision 14. +Committed revision 3. -Committed revision 15. +Committed revision 4. Sending letter.txt Transmitting file data . -Committed revision 16. +Committed revision 5. % convert to hg once initializing destination B-hg repository scanning source... @@ -161,12 +34,12 @@ Sending letter.txt Adding letter2.txt Transmitting file data .. -Committed revision 17. +Committed revision 6. -Committed revision 18. +Committed revision 7. Sending letter2.txt Transmitting file data . -Committed revision 19. +Committed revision 8. % test incremental conversion scanning source... sorting... @@ -193,3 +66,26 @@ tip v0.2 v0.1 +% test filemap +initializing destination fmap repository +scanning source... +sorting... +converting... +5 init projB +4 hello +3 world +2 nice day +1 second letter +0 work in progress +o 1 work in progress files: letter2.txt +| +o 0 second letter files: letter2.txt + +% test stop revision +initializing destination stoprev repository +scanning source... +sorting... +converting... +0 init projB +extra: branch= +extra: convert_revision= diff -r f615ece5fec3 -r f08662abdf3f tests/test-convert-svn-tags --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-svn-tags Sat Mar 29 21:15:54 2008 +0100 @@ -0,0 +1,76 @@ +#!/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 branches +mkdir tags +mkdir unrelated +cd .. + +svnurl=file://$svnpath/svn-repo/projA +svn import -m "init projA" projA $svnurl | fix_path + +echo % update svn repository +svn co $svnurl A | fix_path +cd A +echo a > trunk/a +svn add trunk/a +svn ci -m adda +echo a >> trunk/a +svn ci -m changea +echo a >> trunk/a +svn ci -m changea2 +# Add an unrelated commit to test that tags are bound to the +# correct "from" revision and not a dummy one +echo a >> unrelated/dummy +svn add unrelated/dummy +svn ci -m unrelatedchange +echo % tag current revision +svn up +svn copy trunk tags/trunk.v1 +svn copy trunk tags/trunk.badtag +svn ci -m "tagging trunk.v1 trunk.badtag" +echo a >> trunk/a +svn ci -m changea3 +echo % fix the bad tag +# trunk.badtag should not show in converted tags +svn up +svn mv tags/trunk.badtag tags/trunk.goodtag +svn ci -m "fix trunk.badtag" +cd .. + +echo % convert +hg convert --datesort $svnurl A-hg + +cd A-hg +hg glog --template '#rev# #desc|firstline# tags: #tags#\n' +hg tags -q +cd .. + +echo % convert without tags +hg convert --datesort --config convert.svn.tags= $svnurl A-notags-hg +hg -R a-notags-hg tags -q + diff -r f615ece5fec3 -r f08662abdf3f tests/test-convert-svn-tags.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-svn-tags.out Sat Mar 29 21:15:54 2008 +0100 @@ -0,0 +1,84 @@ +% initial svn import +Adding projA/trunk +Adding projA/unrelated +Adding projA/branches +Adding projA/tags + +Committed revision 1. +% update svn repository +A A/trunk +A A/unrelated +A A/branches +A A/tags +Checked out revision 1. +A trunk/a +Adding trunk/a +Transmitting file data . +Committed revision 2. +Sending trunk/a +Transmitting file data . +Committed revision 3. +Sending trunk/a +Transmitting file data . +Committed revision 4. +A unrelated/dummy +Adding unrelated/dummy +Transmitting file data . +Committed revision 5. +% tag current revision +At revision 5. +A tags/trunk.v1 +A tags/trunk.badtag +Adding tags/trunk.badtag +Adding tags/trunk.v1 + +Committed revision 6. +Sending trunk/a +Transmitting file data . +Committed revision 7. +% fix the bad tag +At revision 7. +A tags/trunk.goodtag +D tags/trunk.badtag/a +D tags/trunk.badtag +Deleting tags/trunk.badtag +Adding tags/trunk.goodtag + +Committed revision 8. +% convert +initializing destination A-hg repository +scanning source... +sorting... +converting... +4 init projA +3 adda +2 changea +1 changea2 +0 changea3 +updating tags +o 5 update tags tags: tip +| +o 4 changea3 tags: +| +o 3 changea2 tags: trunk.v1 trunk.goodtag +| +o 2 changea tags: +| +o 1 adda tags: +| +o 0 init projA tags: + +tip +trunk.v1 +trunk.goodtag +% convert without tags +initializing destination A-notags-hg repository +scanning source... +sorting... +converting... +4 init projA +3 adda +2 changea +1 changea2 +0 changea3 +tip