# HG changeset patch # User Augie Fackler # Date 1235507421 21600 # Node ID 91db8fc049b094261a56d5f022aeaa718181ae47 # Parent df46110502861f3b38f99ddeca6f3b9e1ca18c4a Add a genignore utility command that generates an hgignore file by scraping svn:ignore properties. diff --git a/__init__.py b/__init__.py --- 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(), ), diff --git a/tests/fixtures/ignores.svndump b/tests/fixtures/ignores.svndump 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 + + diff --git a/tests/test_utility_commands.py b/tests/test_utility_commands.py --- 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), diff --git a/utility_commands.py b/utility_commands.py --- 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' %