# HG changeset patch # User David Schleimer # Date 1370998411 25200 # Node ID 791382a21cc4c55114fafe251cbfada95fd7a59e # Parent cd0d14e257574eb4046ef1b65f4182dc17e206fc layouts: add support for configuring branches directory This should work for both alternately named directories (e.g. releases instead of branches), as well as subdirs (branches/releases), and combinations thereof (releases/public). diff --git a/hgsubversion/__init__.py b/hgsubversion/__init__.py --- a/hgsubversion/__init__.py +++ b/hgsubversion/__init__.py @@ -84,6 +84,8 @@ wrapcmds = { # cmd: generic, target, fix 'clone': (False, 'sources', True, True, [ ('T', 'tagpaths', '', 'list of paths to search for tags in Subversion repositories'), + ('', 'branchdir', '', + 'path to search for branches in subversion repositories'), ('A', 'authors', '', 'file mapping Subversion usernames to Mercurial authors'), ('', 'filemap', '', diff --git a/hgsubversion/help/subversion.rst b/hgsubversion/help/subversion.rst --- a/hgsubversion/help/subversion.rst +++ b/hgsubversion/help/subversion.rst @@ -32,7 +32,9 @@ all its tags and branches. In such cases trunk, as in the example above. This is known as `standard layout`, and works with repositories that use the conventional ``trunk``, ``tags`` and ``branches`` directories. By default, hgsubversion will use this layout whenever it finds any -of these directories at the specified directory on the server. +of these directories at the specified directory on the server. Standard layout +also supports alternate names for the ``branches`` directory and multiple tags +locations. If you instead want to clone just a single directory or branch, clone the specific directory path. In the example above, to get *only* trunk, you would @@ -300,6 +302,12 @@ settings: Path to a file for changing branch names during the conversion from Subversion to Mercurial. + ``hgsubversion.branchdir`` + + Specifies the subdirectory to look for branches under. The + default is ``branches``. This option has no effect for + single-directory clones. + ``hgsubversion.filemap`` Path to a file for filtering files during the conversion. Files may either diff --git a/hgsubversion/layouts/standard.py b/hgsubversion/layouts/standard.py --- a/hgsubversion/layouts/standard.py +++ b/hgsubversion/layouts/standard.py @@ -12,11 +12,17 @@ class StandardLayout(base.BaseLayout): self._tag_locations = None + self._branch_dir = ui.config('hgsubversion', 'branchdir', 'branches') + if self._branch_dir[0] == '/': + self._branch_dir = self._branch_dir[1:] + if self._branch_dir[-1] != '/': + self._branch_dir += '/' + def localname(self, path): if path == 'trunk': return None - elif path.startswith('branches/'): - return path[len('branches/'):] + elif path.startswith(self._branch_dir): + return path[len(self._branch_dir):] return '../%s' % path def remotename(self, branch): @@ -24,7 +30,7 @@ class StandardLayout(base.BaseLayout): return 'trunk' elif branch.startswith('../'): return branch[3:] - return 'branches/%s' % branch + return '%s%s' % (self._branch_dir, branch) def remotepath(self, branch, subdir='/'): if subdir == '/': @@ -34,7 +40,7 @@ class StandardLayout(base.BaseLayout): if branch.startswith('../'): branchpath = branch[3:] else: - branchpath = 'branches/%s' % branch + branchpath = '%s%s' % (self._branch_dir, branch) return '%s/%s' % (subdir or '', branchpath) @@ -88,14 +94,17 @@ class StandardLayout(base.BaseLayout): return candidate, '/'.join(components) if path == 'trunk' or path.startswith('trunk/'): - branch_path = 'trunk' - local_path = '/'.join(path.split('/')[1:]) - elif path.startswith('branches/'): - components = path.split('/') - branch_path = '/'.join(components[:2]) - local_path = '/'.join(components[2:]) - else: - components = path.split('/') - branch_path = '/'.join(components[:-1]) - local_path = components[-1] - return branch_path, local_path + return 'trunk', path[len('trunk/'):] + + if path.startswith(self._branch_dir): + path = path[len(self._branch_dir):] + components = path.split('/', 1) + branch_path = '%s%s' % (self._branch_dir, components[0]) + if len(components) == 1: + local_path = '' + else: + local_path = components[1] + return branch_path, local_path + + components = path.split('/') + return '/'.join(components[:-1]), components[-1] diff --git a/hgsubversion/wrappers.py b/hgsubversion/wrappers.py --- a/hgsubversion/wrappers.py +++ b/hgsubversion/wrappers.py @@ -533,6 +533,7 @@ def rebase(orig, ui, repo, **opts): optionmap = { 'tagpaths': ('hgsubversion', 'tagpaths'), 'authors': ('hgsubversion', 'authormap'), + 'branchdir': ('hgsubversion', 'branchdir'), 'filemap': ('hgsubversion', 'filemap'), 'branchmap': ('hgsubversion', 'branchmap'), 'tagmap': ('hgsubversion', 'tagmap'), diff --git a/tests/fixtures/misspelled_branches_tags.sh b/tests/fixtures/misspelled_branches_tags.sh new file mode 100755 --- /dev/null +++ b/tests/fixtures/misspelled_branches_tags.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +set -e + +mkdir temp +cd temp + +svnadmin create testrepo +svn checkout file://`pwd`/testrepo client + +cd client +mkdir trunk +mkdir branchez +mkdir tagz + +svn add trunk branchez tagz +svn commit -m "Initial commit" + +echo "trunk" >> trunk/file +svn add trunk/file +svn commit -m "Added file in trunk" + +svn cp trunk tagz/tag_from_trunk +svn ci -m 'created tag from trunk' + +svn cp trunk branchez/branch +svn ci -m 'created branch from trunk' + +echo "branch" > branchez/branch/file +svn ci -m "committed to the branch" + +svn cp branchez/branch tagz/tag_from_branch +svn ci -m "create tag from branch" + +cd .. +svnadmin dump testrepo > ../misspelled_branches_tags.svndump + +echo "Created misspelled_branches_tags.svndump" +echo "You might want to clean up ${PWD} now" diff --git a/tests/fixtures/misspelled_branches_tags.svndump b/tests/fixtures/misspelled_branches_tags.svndump new file mode 100644 --- /dev/null +++ b/tests/fixtures/misspelled_branches_tags.svndump @@ -0,0 +1,227 @@ +SVN-fs-dump-format-version: 2 + +UUID: a4f285b8-14d5-4bc0-92c8-0e5438624f2e + +Revision-number: 0 +Prop-content-length: 56 +Content-length: 56 + +K 8 +svn:date +V 27 +2013-06-13T00:26:00.303912Z +PROPS-END + +Revision-number: 1 +Prop-content-length: 120 +Content-length: 120 + +K 10 +svn:author +V 10 +dschleimer +K 8 +svn:date +V 27 +2013-06-13T00:26:00.666275Z +K 7 +svn:log +V 14 +Initial commit +PROPS-END + +Node-path: branchez +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: tagz +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: trunk +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Revision-number: 2 +Prop-content-length: 125 +Content-length: 125 + +K 10 +svn:author +V 10 +dschleimer +K 8 +svn:date +V 27 +2013-06-13T00:26:00.997106Z +K 7 +svn:log +V 19 +Added file in trunk +PROPS-END + +Node-path: trunk/file +Node-kind: file +Node-action: add +Prop-content-length: 10 +Text-content-length: 6 +Text-content-md5: edf45fe5c98c5367733b39bbb2bb20d9 +Text-content-sha1: 7361d1685e5c86dfc523620cfaf598f196f86239 +Content-length: 16 + +PROPS-END +trunk + + +Revision-number: 3 +Prop-content-length: 128 +Content-length: 128 + +K 10 +svn:author +V 10 +dschleimer +K 8 +svn:date +V 27 +2013-06-13T00:26:01.435764Z +K 7 +svn:log +V 22 +created tag from trunk +PROPS-END + +Node-path: tagz/tag_from_trunk +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 1 +Node-copyfrom-path: trunk + + +Node-path: tagz/tag_from_trunk/file +Node-kind: file +Node-action: add +Node-copyfrom-rev: 2 +Node-copyfrom-path: trunk/file +Text-copy-source-md5: edf45fe5c98c5367733b39bbb2bb20d9 +Text-copy-source-sha1: 7361d1685e5c86dfc523620cfaf598f196f86239 + + +Revision-number: 4 +Prop-content-length: 131 +Content-length: 131 + +K 10 +svn:author +V 10 +dschleimer +K 8 +svn:date +V 27 +2013-06-13T00:26:01.816716Z +K 7 +svn:log +V 25 +created branch from trunk +PROPS-END + +Node-path: branchez/branch +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 1 +Node-copyfrom-path: trunk + + +Node-path: branchez/branch/file +Node-kind: file +Node-action: add +Node-copyfrom-rev: 2 +Node-copyfrom-path: trunk/file +Text-copy-source-md5: edf45fe5c98c5367733b39bbb2bb20d9 +Text-copy-source-sha1: 7361d1685e5c86dfc523620cfaf598f196f86239 + + +Revision-number: 5 +Prop-content-length: 129 +Content-length: 129 + +K 10 +svn:author +V 10 +dschleimer +K 8 +svn:date +V 27 +2013-06-13T00:26:02.232496Z +K 7 +svn:log +V 23 +committed to the branch +PROPS-END + +Node-path: branchez/branch/file +Node-kind: file +Node-action: change +Text-content-length: 7 +Text-content-md5: 99df69f80e72a660346459fa63c31fd4 +Text-content-sha1: f49390feacc0a7fb2b36ad16dc0bc44036193402 +Content-length: 7 + +branch + + +Revision-number: 6 +Prop-content-length: 128 +Content-length: 128 + +K 10 +svn:author +V 10 +dschleimer +K 8 +svn:date +V 27 +2013-06-13T00:26:02.596105Z +K 7 +svn:log +V 22 +create tag from branch +PROPS-END + +Node-path: tagz/tag_from_branch +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 4 +Node-copyfrom-path: branchez/branch + + +Node-path: tagz/tag_from_branch/file +Node-kind: file +Node-action: delete + +Node-path: tagz/tag_from_branch/file +Node-kind: file +Node-action: add +Node-copyfrom-rev: 5 +Node-copyfrom-path: branchez/branch/file +Text-copy-source-md5: 99df69f80e72a660346459fa63c31fd4 +Text-copy-source-sha1: f49390feacc0a7fb2b36ad16dc0bc44036193402 + + + + diff --git a/tests/fixtures/subdir_branches_tags.sh b/tests/fixtures/subdir_branches_tags.sh new file mode 100755 --- /dev/null +++ b/tests/fixtures/subdir_branches_tags.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +set -e + +mkdir temp +cd temp + +svnadmin create testrepo +svn checkout file://`pwd`/testrepo client + +cd client +mkdir trunk +mkdir -p bran/ches +mkdir -p ta/gs + +svn add trunk bran ta +svn commit -m "Initial commit" + +echo "trunk" >> trunk/file +svn add trunk/file +svn commit -m "Added file in trunk" + +svn cp trunk ta/gs/tag_from_trunk +svn ci -m 'created tag from trunk' + +svn cp trunk bran/ches/branch +svn ci -m 'created branch from trunk' + +echo "branch" > bran/ches/branch/file +svn ci -m "committed to the branch" + +svn cp bran/ches/branch ta/gs/tag_from_branch +svn ci -m "create tag from branch" + +cd .. +svnadmin dump testrepo > ../subdir_branches_tags.svndump + +echo "Created subdir_branches_tags.svndump" +echo "You might want to clean up ${PWD} now" diff --git a/tests/fixtures/subdir_branches_tags.svndump b/tests/fixtures/subdir_branches_tags.svndump new file mode 100644 --- /dev/null +++ b/tests/fixtures/subdir_branches_tags.svndump @@ -0,0 +1,245 @@ +SVN-fs-dump-format-version: 2 + +UUID: 591b9313-8b8d-45af-bb0f-4d8efe82f2b0 + +Revision-number: 0 +Prop-content-length: 56 +Content-length: 56 + +K 8 +svn:date +V 27 +2013-06-13T00:25:39.145214Z +PROPS-END + +Revision-number: 1 +Prop-content-length: 120 +Content-length: 120 + +K 10 +svn:author +V 10 +dschleimer +K 8 +svn:date +V 27 +2013-06-13T00:25:39.542218Z +K 7 +svn:log +V 14 +Initial commit +PROPS-END + +Node-path: bran +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: bran/ches +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: ta +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: ta/gs +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: trunk +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Revision-number: 2 +Prop-content-length: 125 +Content-length: 125 + +K 10 +svn:author +V 10 +dschleimer +K 8 +svn:date +V 27 +2013-06-13T00:25:39.963701Z +K 7 +svn:log +V 19 +Added file in trunk +PROPS-END + +Node-path: trunk/file +Node-kind: file +Node-action: add +Prop-content-length: 10 +Text-content-length: 6 +Text-content-md5: edf45fe5c98c5367733b39bbb2bb20d9 +Text-content-sha1: 7361d1685e5c86dfc523620cfaf598f196f86239 +Content-length: 16 + +PROPS-END +trunk + + +Revision-number: 3 +Prop-content-length: 128 +Content-length: 128 + +K 10 +svn:author +V 10 +dschleimer +K 8 +svn:date +V 27 +2013-06-13T00:25:40.344923Z +K 7 +svn:log +V 22 +created tag from trunk +PROPS-END + +Node-path: ta/gs/tag_from_trunk +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 1 +Node-copyfrom-path: trunk + + +Node-path: ta/gs/tag_from_trunk/file +Node-kind: file +Node-action: add +Node-copyfrom-rev: 2 +Node-copyfrom-path: trunk/file +Text-copy-source-md5: edf45fe5c98c5367733b39bbb2bb20d9 +Text-copy-source-sha1: 7361d1685e5c86dfc523620cfaf598f196f86239 + + +Revision-number: 4 +Prop-content-length: 131 +Content-length: 131 + +K 10 +svn:author +V 10 +dschleimer +K 8 +svn:date +V 27 +2013-06-13T00:25:40.700526Z +K 7 +svn:log +V 25 +created branch from trunk +PROPS-END + +Node-path: bran/ches/branch +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 1 +Node-copyfrom-path: trunk + + +Node-path: bran/ches/branch/file +Node-kind: file +Node-action: add +Node-copyfrom-rev: 2 +Node-copyfrom-path: trunk/file +Text-copy-source-md5: edf45fe5c98c5367733b39bbb2bb20d9 +Text-copy-source-sha1: 7361d1685e5c86dfc523620cfaf598f196f86239 + + +Revision-number: 5 +Prop-content-length: 129 +Content-length: 129 + +K 10 +svn:author +V 10 +dschleimer +K 8 +svn:date +V 27 +2013-06-13T00:25:41.081165Z +K 7 +svn:log +V 23 +committed to the branch +PROPS-END + +Node-path: bran/ches/branch/file +Node-kind: file +Node-action: change +Text-content-length: 7 +Text-content-md5: 99df69f80e72a660346459fa63c31fd4 +Text-content-sha1: f49390feacc0a7fb2b36ad16dc0bc44036193402 +Content-length: 7 + +branch + + +Revision-number: 6 +Prop-content-length: 128 +Content-length: 128 + +K 10 +svn:author +V 10 +dschleimer +K 8 +svn:date +V 27 +2013-06-13T00:25:41.512313Z +K 7 +svn:log +V 22 +create tag from branch +PROPS-END + +Node-path: ta/gs/tag_from_branch +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 4 +Node-copyfrom-path: bran/ches/branch + + +Node-path: ta/gs/tag_from_branch/file +Node-kind: file +Node-action: delete + +Node-path: ta/gs/tag_from_branch/file +Node-kind: file +Node-action: add +Node-copyfrom-rev: 5 +Node-copyfrom-path: bran/ches/branch/file +Text-copy-source-md5: 99df69f80e72a660346459fa63c31fd4 +Text-copy-source-sha1: f49390feacc0a7fb2b36ad16dc0bc44036193402 + + + + diff --git a/tests/test_fetch_branches.py b/tests/test_fetch_branches.py --- a/tests/test_fetch_branches.py +++ b/tests/test_fetch_branches.py @@ -128,3 +128,45 @@ class TestFetchBranches(test_util.TestBa for f in ctx: self.assertTrue(not ctx[f].renamed()) + def test_misspelled_branches_tags(self): + config = { + 'hgsubversion.branchdir': 'branchez', + 'hgsubversion.tagpaths': 'tagz', + } + '''Tests using the tags dir for branches and the branches dir for tags''' + repo = self._load_fixture_and_fetch('misspelled_branches_tags.svndump', + layout='standard', + config=config) + + heads = set([repo[n].branch() for n in repo.heads()]) + expected_heads = set(['default', 'branch']) + + self.assertEqual(heads, expected_heads) + + tags = set(repo.tags()) + expected_tags = set(['tip', 'tag_from_trunk', 'tag_from_branch']) + self.assertEqual(tags, expected_tags) + + def test_subdir_branches_tags(self): + '''Tests using the tags dir for branches and the branches dir for tags''' + config = { + 'hgsubversion.branchdir': 'bran/ches', + 'hgsubversion.tagpaths': 'ta/gs', + } + repo = self._load_fixture_and_fetch('subdir_branches_tags.svndump', + layout='standard', + config=config) + + heads = set([repo[n].branch() for n in repo.heads()]) + expected_heads = set(['default', 'branch']) + + self.assertEqual(heads, expected_heads) + + tags = set(repo.tags()) + expected_tags = set(['tip', 'tag_from_trunk', 'tag_from_branch']) + self.assertEqual(tags, expected_tags) + +def suite(): + all_tests = [unittest.TestLoader().loadTestsFromTestCase(TestFetchBranches), + ] + return unittest.TestSuite(all_tests)