1. Augie Fackler
  2. hgsubversion

Commits

David Schleimer  committed 9a7e3db

layouts: add support for an infix between tbt and the hg root

  • Participants
  • Parent commits 791382a
  • Branches default

Comments (0)

Files changed (9)

File hgsubversion/__init__.py

View file
  • Ignore whitespace
          'list of paths to search for tags in Subversion repositories'),
         ('', 'branchdir', '',
          'path to search for branches in subversion repositories'),
+        ('', 'infix', '',
+         'path relative to trunk, branch an tag dirs to import'),
         ('A', 'authors', '',
          'file mapping Subversion usernames to Mercurial authors'),
         ('', 'filemap', '',

File hgsubversion/help/subversion.rst

View file
  • Ignore whitespace
 directories. By default, hgsubversion will use this layout whenever it finds any
 of these directories at the specified directory on the server.  Standard layout
 also supports alternate names for the ``branches`` directory and multiple tags
-locations.
+locations.  Finally, Standard Layout supports selecting a subdirectory relative
+to ``trunk``, and each branch and tag dir.  This is useful if you have a single
+``trunk``, ``branches``, and ``tags`` with several projects inside, and you wish
+to import only a single project.
 
 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
     default is ``branches``.  This option has no effect for
     single-directory clones.
 
+  ``hgsubversion.infix``
+
+    Specifies a path to strip between relative to the trunk/branch/tag
+    root as the mercurial root.  This can be used to import a single
+    sub-project when you have several sub-projects under a single
+    trunk/branches/tags layout in subversion.
+
   ``hgsubversion.filemap``
 
     Path to a file for filtering files during the conversion. Files may either

File hgsubversion/layouts/base.py

View file
  • Ignore whitespace
 
         local_path should be relative to the root of the Mercurial working dir
 
+        Note that it is permissible to return a longer branch_path
+        than is passed in iff the path that is passed in is a parent
+        directory of exactly one branch.  This is intended to handle
+        the case where we are importing a particular subdirectory of
+        asubversion branch structure.
+
         """
         self.__unimplemented('split_remote_name')

File hgsubversion/layouts/standard.py

View file
  • Ignore whitespace
         if self._branch_dir[-1] != '/':
             self._branch_dir += '/'
 
+        self._infix = ui.config('hgsubversion', 'infix', '').strip('/')
+        if self._infix:
+            self._infix = '/' + self._infix
+
+        self._trunk = 'trunk%s' % self._infix
+
     def localname(self, path):
-        if path == 'trunk':
+        if path == self._trunk:
             return None
-        elif path.startswith(self._branch_dir):
-            return path[len(self._branch_dir):]
+        elif path.startswith(self._branch_dir) and path.endswith(self._infix):
+            path = path[len(self._branch_dir):]
+            if self._infix:
+                path = path[:-len(self._infix)]
+            return path
         return  '../%s' % path
 
     def remotename(self, branch):
         if branch == 'default' or branch is None:
-            return 'trunk'
+            path = self._trunk
         elif branch.startswith('../'):
-            return branch[3:]
-        return '%s%s' % (self._branch_dir, branch)
+            path =  branch[3:]
+        else:
+            path = ''.join((self._branch_dir, branch, self._infix))
+
+        return path
 
     def remotepath(self, branch, subdir='/'):
         if subdir == '/':
             subdir = ''
-        branchpath = 'trunk'
+        branchpath = self._trunk
         if branch and branch != 'default':
             if branch.startswith('../'):
                 branchpath = branch[3:]
             else:
-                branchpath = '%s%s' % (self._branch_dir, branch)
+                branchpath = ''.join((self._branch_dir, branch, self._infix))
 
         return '%s/%s' % (subdir or '', branchpath)
 
             return candidate, '/'.join(components)
 
         if path == 'trunk' or path.startswith('trunk/'):
-            return 'trunk', path[len('trunk/'):]
+            return self._trunk, path[len(self._trunk) + 1:]
 
         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])
+            branch_path = ''.join((self._branch_dir, components[0]))
             if len(components) == 1:
                 local_path = ''
             else:
                 local_path = components[1]
+
+            if local_path == '':
+                branch_path += self._infix
+            elif local_path.startswith(self._infix[1:] + '/'):
+                branch_path += self._infix
+                local_path = local_path[len(self._infix):]
             return branch_path, local_path
 
         components = path.split('/')

File hgsubversion/svnmeta.py

View file
  • Ignore whitespace
             src_file, src_branch = self.split_branch_path(src_path)[:2]
             src_tag = self.get_path_tag(src_path)
             if src_tag or src_file == '':
-                ln = self.localname(p)
+                brpath, fpath = self.layoutobj.split_remote_name(p,
+                                                                 self.branches)
+                # we'll sometimes get a different path out of
+                # split_remate_name than the one we passed in, but
+                # only for the root of a branch, since the svn copies
+                # of those will sometimes be of parent directories of
+                # our root
+                if fpath == '':
+                    ln = self.localname(brpath)
+                else:
+                    ln = self.localname(p)
                 if src_tag in self.tags:
                     changeid = self.tags[src_tag]
                     src_rev, src_branch = self.get_source_rev(changeid)[:2]

File hgsubversion/wrappers.py

View file
  • Ignore whitespace
     'tagpaths': ('hgsubversion', 'tagpaths'),
     'authors': ('hgsubversion', 'authormap'),
     'branchdir': ('hgsubversion', 'branchdir'),
+    'infix': ('hgsubversion', 'infix'),
     'filemap': ('hgsubversion', 'filemap'),
     'branchmap': ('hgsubversion', 'branchmap'),
     'tagmap': ('hgsubversion', 'tagmap'),

File tests/fixtures/subprojects.sh

View file
  • Ignore whitespace
+#!/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 branches
+mkdir -p tags
+
+svn add trunk branches tags
+svn commit -m "Initial commit"
+
+mkdir trunk/project trunk/other
+echo "project trunk" > trunk/project/file
+echo "other trunk" > trunk/other/phile
+svn add trunk/project trunk/other
+svn commit -m "Added file and phile in trunk"
+
+svn up
+
+svn cp trunk tags/tag_from_trunk
+svn ci -m 'created tag from trunk'
+
+svn up
+
+svn cp trunk branches/branch
+svn ci -m 'created branch from trunk'
+
+svn up
+
+echo "project branch" > branches/branch/project/file
+svn ci -m "committed to the project branch"
+
+svn up
+
+echo "trunk2" > trunk/project/file
+svn ci -m "committed to trunk again"
+
+svn up
+
+echo "other branch" > branches/branch/other/phile
+svn ci -m "committed to the other branch"
+
+svn up
+
+svn cp branches/branch tags/tag_from_branch
+svn ci -m "create tag from branch"
+
+cd ..
+svnadmin dump testrepo > ../subprojects.svndump
+
+echo "Created subprojects.svndump"
+echo "You might want to clean up ${PWD} now"

File tests/fixtures/subprojects.svndump

View file
  • Ignore whitespace
+SVN-fs-dump-format-version: 2
+
+UUID: 03c99a5f-42f9-43e0-bb0d-03549a88a7e4
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2013-07-23T22:47:56.963334Z
+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-07-23T22:47:57.401454Z
+K 7
+svn:log
+V 14
+Initial commit
+PROPS-END
+
+Node-path: branches
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: tags
+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: 135
+Content-length: 135
+
+K 10
+svn:author
+V 10
+dschleimer
+K 8
+svn:date
+V 27
+2013-07-23T22:47:57.849874Z
+K 7
+svn:log
+V 29
+Added file and phile in trunk
+PROPS-END
+
+Node-path: trunk/other
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: trunk/other/phile
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 12
+Text-content-md5: fe5279547ba9d8c257b67c1938853896
+Text-content-sha1: 6c94bf284aa7bc931c358ae3dfcfb4fc9f335579
+Content-length: 22
+
+PROPS-END
+other trunk
+
+
+Node-path: trunk/project
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: trunk/project/file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 14
+Text-content-md5: d61b3a5935cb974e41082d9eb8eb912e
+Text-content-sha1: 1e7f7740062dc540ab20fb6cf395cad3c55f396f
+Content-length: 24
+
+PROPS-END
+project 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-07-23T22:47:58.281764Z
+K 7
+svn:log
+V 22
+created tag from trunk
+PROPS-END
+
+Node-path: tags/tag_from_trunk
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 2
+Node-copyfrom-path: trunk
+
+
+Revision-number: 4
+Prop-content-length: 131
+Content-length: 131
+
+K 10
+svn:author
+V 10
+dschleimer
+K 8
+svn:date
+V 27
+2013-07-23T22:47:59.456625Z
+K 7
+svn:log
+V 25
+created branch from trunk
+PROPS-END
+
+Node-path: branches/branch
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 3
+Node-copyfrom-path: trunk
+
+
+Revision-number: 5
+Prop-content-length: 137
+Content-length: 137
+
+K 10
+svn:author
+V 10
+dschleimer
+K 8
+svn:date
+V 27
+2013-07-23T22:47:59.862054Z
+K 7
+svn:log
+V 31
+committed to the project branch
+PROPS-END
+
+Node-path: branches/branch/project/file
+Node-kind: file
+Node-action: change
+Text-content-length: 15
+Text-content-md5: 64cdb38c10361681c4c2918a222a3102
+Text-content-sha1: 545ef3bb672a1dd01fb9bd2a2eb7621882a4c701
+Content-length: 15
+
+project branch
+
+
+Revision-number: 6
+Prop-content-length: 130
+Content-length: 130
+
+K 10
+svn:author
+V 10
+dschleimer
+K 8
+svn:date
+V 27
+2013-07-23T22:48:00.345069Z
+K 7
+svn:log
+V 24
+committed to trunk again
+PROPS-END
+
+Node-path: trunk/project/file
+Node-kind: file
+Node-action: change
+Text-content-length: 7
+Text-content-md5: 28d0a7e7ef2864416b7a9398623e4d09
+Text-content-sha1: 91454e2d3487f712490f17481157e389c11a6fe0
+Content-length: 7
+
+trunk2
+
+
+Revision-number: 7
+Prop-content-length: 135
+Content-length: 135
+
+K 10
+svn:author
+V 10
+dschleimer
+K 8
+svn:date
+V 27
+2013-07-23T22:48:00.751804Z
+K 7
+svn:log
+V 29
+committed to the other branch
+PROPS-END
+
+Node-path: branches/branch/other/phile
+Node-kind: file
+Node-action: change
+Text-content-length: 13
+Text-content-md5: 7c133b867f55c0ba8688e1f111ddebaf
+Text-content-sha1: aee59a1c349cedc1ab035263bd7f14d58c6ab33b
+Content-length: 13
+
+other branch
+
+
+Revision-number: 8
+Prop-content-length: 128
+Content-length: 128
+
+K 10
+svn:author
+V 10
+dschleimer
+K 8
+svn:date
+V 27
+2013-07-23T22:48:01.199203Z
+K 7
+svn:log
+V 22
+create tag from branch
+PROPS-END
+
+Node-path: tags/tag_from_branch
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 7
+Node-copyfrom-path: branches/branch
+
+

File tests/test_fetch_branches.py

View file
  • Ignore whitespace
         expected_tags = set(['tip', 'tag_from_trunk', 'tag_from_branch'])
         self.assertEqual(tags, expected_tags)
 
+    def test_subproject_fetch(self):
+        config = {
+            'hgsubversion.infix': 'project',
+            }
+        repo = self._load_fixture_and_fetch('subprojects.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)
+
+        for head in repo.heads():
+            ctx = repo[head]
+            self.assertFalse('project/file' in ctx, 'failed to strip infix')
+            self.assertTrue('file' in ctx, 'failed to track a simple file')
+            self.assertFalse('other/phile' in ctx, 'pulled in other project')
+            self.assertFalse('phile' in ctx, 'merged other project in repo')
+
+
 def suite():
     all_tests = [unittest.TestLoader().loadTestsFromTestCase(TestFetchBranches),
           ]