changeset 199:91db8fc049b0

Add a genignore utility command that generates an hgignore file by scraping svn:ignore properties.
author Augie Fackler <durin42@gmail.com>
date Tue, 24 Feb 2009 14:30:21 -0600
parents df4611050286
children 2e8c527f0456
files __init__.py tests/fixtures/ignores.svndump tests/test_utility_commands.py utility_commands.py
diffstat 4 files changed, 200 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/__init__.py
+++ b/__init__.py
@@ -63,6 +63,7 @@ cmdtable = {
           ('A', 'authors', '', 'username mapping filename'),
           ('', 'filemap', '',
            'remap file to exclude paths or include only certain paths'),
+          ('', 'force', False, 'force an operation to happen'),
           ],
          svncommand.generate_help(),
          ),
new file mode 100644
--- /dev/null
+++ b/tests/fixtures/ignores.svndump
@@ -0,0 +1,132 @@
+SVN-fs-dump-format-version: 2
+
+UUID: 8ad10f29-7a81-4fb6-a789-b0b0ade2ec54
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2009-02-24T20:04:33.977054Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 111
+Content-length: 111
+
+K 7
+svn:log
+V 11
+Add a file.
+K 10
+svn:author
+V 5
+durin
+K 8
+svn:date
+V 27
+2009-02-24T20:05:12.825468Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: trunk/bar
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 4
+Text-content-md5: d3b07384d113edec49eaa6238ad5ff00
+Text-content-sha1: f1d2d2f924e986ac86fdf7b36c94bcdf32beec15
+Content-length: 14
+
+PROPS-END
+foo
+
+
+Revision-number: 2
+Prop-content-length: 105
+Content-length: 105
+
+K 7
+svn:log
+V 6
+ignore
+K 10
+svn:author
+V 5
+durin
+K 8
+svn:date
+V 27
+2009-02-24T20:05:49.334191Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 47
+Content-length: 47
+
+K 10
+svn:ignore
+V 15
+blah
+otherblah
+
+PROPS-END
+
+
+Revision-number: 3
+Prop-content-length: 124
+Content-length: 124
+
+K 7
+svn:log
+V 24
+another file and ignore.
+K 10
+svn:author
+V 5
+durin
+K 8
+svn:date
+V 27
+2009-02-24T20:07:13.884019Z
+PROPS-END
+
+Node-path: trunk/baz
+Node-kind: dir
+Node-action: add
+Prop-content-length: 37
+Content-length: 37
+
+K 10
+svn:ignore
+V 6
+magic
+
+PROPS-END
+
+
+Node-path: trunk/baz/xyzzy
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 6
+Text-content-md5: 89d447eb9afaeb94e463615e8ded6479
+Text-content-sha1: 9b3802ecbad114267dd0d1a431e57b0bca95930a
+Content-length: 16
+
+PROPS-END
+xyzzy
+
+
--- a/tests/test_utility_commands.py
+++ b/tests/test_utility_commands.py
@@ -155,6 +155,20 @@ class UtilityTests(test_util.TestBase):
         expected = test_util.fileurl(self.repo_path) + '\n'
         self.assertEqual(u.stream.getvalue(), expected)
 
+    def test_genignore(self):
+        """Verify url gets normalized on initial clone.
+        """
+        test_util.load_svndump_fixture(self.repo_path, 'ignores.svndump')
+        fetch_command.fetch_revisions(ui.ui(),
+                                      svn_url=test_util.fileurl(self.repo_path)+'/',
+                                      hg_repo_path=self.wc_path,
+                                      stupid=False)
+        hg.update(self.repo, 'tip')
+        u = ui.ui()
+        utility_commands.generate_ignore(u, self.repo, self.wc_path)
+        self.assertEqual(open(os.path.join(self.wc_path, '.hgignore')).read(),
+                         '.hgignore\nsyntax:glob\nblah\notherblah\nbaz/magic\n')
+
 
 def suite():
     all = [unittest.TestLoader().loadTestsFromTestCase(UtilityTests),
--- a/utility_commands.py
+++ b/utility_commands.py
@@ -1,3 +1,5 @@
+import os
+
 import mercurial
 from mercurial import cmdutil
 from mercurial import node
@@ -17,6 +19,51 @@ def print_wc_url(ui, repo, hg_repo_path,
 print_wc_url = util.register_subcommand('url')(print_wc_url)
 
 
+def find_wc_parent_rev(ui, repo, hge, svn_commit_hashes):
+    """Find the svn parent revision of the repo's dirstate.
+    """
+    workingctx = repo.parents()[0]
+    o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, workingctx.node())
+    if o_r:
+        workingctx = repo[o_r[-1]].parents()[0]
+    return workingctx
+
+
+def generate_ignore(ui, repo, hg_repo_path, force=False, **opts):
+    """generate .hgignore from svn:ignore properties.
+    """
+    ignpath = os.path.join(hg_repo_path, '.hgignore')
+    if not force and os.path.exists(ignpath):
+        raise mutil.Abort('not overwriting existing .hgignore, try --force?')
+    ignorefile = open(ignpath, 'w')
+    ignorefile.write('.hgignore\nsyntax:glob\n')
+    hge = hg_delta_editor.HgChangeReceiver(hg_repo_path,
+                                           ui_=ui)
+    svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
+                                 hge.revmap.iterkeys()))
+    parent = find_wc_parent_rev(ui, repo, hge, svn_commit_hashes)
+    r, br = svn_commit_hashes[parent.node()]
+    if br == None:
+        branchpath = 'trunk'
+    else:
+        branchpath = 'branches/%s' % br
+    url = hge.url
+    if url[-1] == '/':
+        url = url[:-1]
+    svn = svnwrap.SubversionRepo(url)
+    dirs = [''] + [d[0] for d in svn.list_files(branchpath, r) if d[1] == 'd']
+    for dir in dirs:
+        props = svn.list_props('%s/%s/' % (branchpath,dir), r)
+        if 'svn:ignore' in props:
+            lines = props['svn:ignore'].strip().split('\n')
+            for prop in lines:
+                if dir:
+                    ignorefile.write('%s/%s\n' % (dir, prop))
+                else:
+                    ignorefile.write('%s\n' % prop)
+generate_ignore = util.register_subcommand('genignore')(generate_ignore)
+
+
 def run_svn_info(ui, repo, hg_repo_path, **opts):
     """show Subversion details similar to `svn info'
     """
@@ -24,11 +71,8 @@ def run_svn_info(ui, repo, hg_repo_path,
                                            ui_=ui)
     svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
                                  hge.revmap.iterkeys()))
-    workingctx = repo.parents()[0]
-    o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, workingctx.node())
-    if o_r:
-        workingctx = repo[o_r[-1]].parents()[0]
-    r, br = svn_commit_hashes[workingctx.node()]
+    parent = find_wc_parent_rev(ui, repo, hge, svn_commit_hashes)
+    r, br = svn_commit_hashes[parent.node()]
     if br == None:
         branchpath = '/trunk'
     else:
@@ -37,9 +81,9 @@ def run_svn_info(ui, repo, hg_repo_path,
     if url[-1] == '/':
         url = url[:-1]
     url = '%s%s' % (url, branchpath)
-    author = '@'.join(workingctx.user().split('@')[:-1])
+    author = '@'.join(parent.user().split('@')[:-1])
     # cleverly figure out repo root w/o actually contacting the server
-    subdir = workingctx.extra()['convert_revision'][40:].split('@')[0]
+    subdir = parent.extra()['convert_revision'][40:].split('@')[0]
     reporoot = url[:len(url)-len(subdir)]
     ui.status('''URL: %(url)s
 Repository Root: %(reporoot)s
@@ -55,7 +99,7 @@ Last Changed Date: %(date)s\n''' %
                'author': author,
                'revision': r,
                # TODO I'd like to format this to the user's local TZ if possible
-               'date': mutil.datestr(workingctx.date(),
+               'date': mutil.datestr(parent.date(),
                                      '%Y-%m-%d %H:%M:%S %1%2 (%a, %d %b %Y)')
               })
 run_svn_info = util.register_subcommand('info')(run_svn_info)
@@ -68,10 +112,7 @@ def print_parent_revision(ui, repo, hg_r
                                            ui_=ui)
     svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
                                  hge.revmap.iterkeys()))
-    ha = repo.parents()[0]
-    o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, ha.node())
-    if o_r:
-        ha = repo[o_r[-1]].parents()[0]
+    ha = find_wc_parent_rev(ui, repo, hge, svn_commit_hashes)
     if ha.node() != node.nullid:
         r, br = svn_commit_hashes[ha.node()]
         ui.status('Working copy parent revision is %s: r%s on %s\n' %