# HG changeset patch # User Siddharth Agarwal <sid0@fb.com> # Date 1409492958 25200 # Sun Aug 31 06:49:18 2014 -0700 # Node ID 13a3513f8e675b49a716b2c74875cd8aa6cfd975 # Parent 0a6e3f9acb819fdc7a337b82ec603c5eaf10e6ac git_handler: support extracting metadata from Git extra fields Git has a well-hidden notion of extra fields. They aren't accessible from the CLI, but Dulwich can create them. They're a better place to store Mercurial's extra metadata than the commit message. An upcoming patch will switch hg-git to saving the metadata in the Git extra fields. This commit: (a) prepares for that (b) extracts other Git extra fields so that commits from Git with such fields roundtrip correctly diff --git a/hggit/git_handler.py b/hggit/git_handler.py --- a/hggit/git_handler.py +++ b/hggit/git_handler.py @@ -680,7 +680,8 @@ self.ui.debug(_("importing: %s\n") % commit.id) (strip_message, hg_renames, - hg_branch, extra) = self.extract_hg_metadata(commit.message) + hg_branch, extra) = self.extract_hg_metadata( + commit.message, commit.extra) gparents = map(self.map_hg_get, commit.parents) @@ -1244,7 +1245,7 @@ return convert[mode] return '' - def extract_hg_metadata(self, message): + def extract_hg_metadata(self, message, git_extra): split = message.split("\n--HG--\n", 1) renames = {} extra = {} @@ -1266,8 +1267,27 @@ if command == 'branch': branch = data if command == 'extra': - before, after = data.split(" : ", 1) - extra[before] = urllib.unquote(after) + k, v = data.split(" : ", 1) + extra[k] = urllib.unquote(v) + + git_fn = 0 + for field, data in git_extra: + if field.startswith('HG:'): + command = field[3:] + if command == 'rename': + before, after = data.split(':', 1) + renames[urllib.unquote(after)] = urllib.unquote(before) + elif command == 'extra': + k, v = data.split(':', 1) + extra[urllib.unquote(k)] = urllib.unquote(v) + else: + # preserve ordering in Git by using an incrementing integer for + # each field. Note that extra metadata in Git is an ordered list + # of pairs. + hg_field = 'GIT%d-%s' % (git_fn, field) + git_fn += 1 + extra[urllib.quote(hg_field)] = urllib.quote(data) + return (message, renames, branch, extra) def get_file(self, commit, f): diff --git a/tests/test-extra.t b/tests/test-extra.t --- a/tests/test-extra.t +++ b/tests/test-extra.t @@ -66,11 +66,55 @@ extra : aaaaaaa : dataaaa extra : zzzzzzz : datazzz + $ cd ../gitrepo + $ git checkout b1 + Switched to branch 'b1' + $ commit_sha=$(git rev-parse HEAD) + $ tree_sha=$(git rev-parse HEAD^{tree}) + +There's no way to create a Git repo with extra metadata via the CLI. Dulwich +lets you do that, though. + + >>> from dulwich.objects import Commit + >>> from dulwich.porcelain import open_repo + >>> repo = open_repo('.') + >>> c = Commit() + >>> c.author = 'test <test@example.org>' + >>> c.author_time = 0 + >>> c.author_timezone = 0 + >>> c.committer = c.author + >>> c.commit_time = 0 + >>> c.commit_timezone = 0 + >>> c.parents = ['$commit_sha'] + >>> c.tree = '$tree_sha' + >>> c.message = 'extra commit\n' + >>> c.extra.extend([('zzz:zzz', 'data:zzz'), ('aaa:aaa', 'data:aaa'), + ... ('HG:extra', 'hgaaa:dataaaa'), + ... ('HG:extra', 'hgzzz:datazzz')]) + >>> repo.object_store.add_object(c) + >>> repo.refs.set_if_equals('refs/heads/master', None, c.id) + True + + $ git cat-file commit master + tree 1b773a2eb70f29397356f8069c285394835ff85a + parent ca11864bb2a84c3996929d42cf38bae3d0f7aae0 + author test <test@example.org> 0 +0000 + committer test <test@example.org> 0 +0000 + zzz:zzz data:zzz + aaa:aaa data:aaa + HG:extra hgaaa:dataaaa + HG:extra hgzzz:datazzz + + extra commit + $ cd .. $ hg clone -q gitrepo hgrepo2 $ cd hgrepo2 $ hg log --graph --template "{rev} {node} {desc|firstline}\n{join(extras, ' ')}\n\n" - @ 3 f15e01c73845392d86a5ed10fb0753d09bca13d3 + @ 4 f5fddc070b0648a5cddb98b43bbd527e98f4b4d2 extra commit + | GIT0-zzz%3Azzz=data%3Azzz GIT1-aaa%3Aaaa=data%3Aaaa branch=default hgaaa=dataaaa hgzzz=datazzz + | + o 3 f15e01c73845392d86a5ed10fb0753d09bca13d3 | aaaaaaa=dataaaa branch=default zzzzzzz=datazzz | o 2 dcec77c6ae3cff594c4435e5820bec4ec9e57440 b