# 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