changeset 150:4b63e8425565

merged in tag support branch
author Scott Chacon <schacon@gmail.com>
date Wed, 27 May 2009 16:05:22 -0700
parents eb1fcdb8fc9b (current diff) 845039d8d90a (diff)
children 240ec7b38f98
files __init__.py git_handler.py
diffstat 13 files changed, 275 insertions(+), 56 deletions(-) [+]
line wrap: on
line diff
--- a/__init__.py	Wed May 27 15:58:07 2009 -0700
+++ b/__init__.py	Wed May 27 16:05:22 2009 -0700
@@ -45,21 +45,27 @@
     git = GitHandler(repo, ui)
     git.push(remote_name)
 
+def gimport(ui, repo, remote_name=None):
+    git = GitHandler(repo, ui)
+    git.import_commits(remote_name)
+
 def gexport(ui, repo):
     git = GitHandler(repo, ui)
-    git.export()
+    git.export_commits()
 
 def gremote(ui, repo, *args):
     git = GitHandler(repo, ui)
 
     if len(args) == 0:
         git.remote_list()
+    elif len(args) < 2:
+        repo.ui.warn(_("must supply an action and a remote\n"))
     else:
         verb = args[0]
         nick = args[1]
 
         if verb == 'add':
-            if args[2]:
+            if len(args) == 3:
                 git.remote_add(nick, args[2])
             else:
                 repo.ui.warn(_("must supply a url to add as a remote\n"))
@@ -87,7 +93,9 @@
        _('Clone a git repository into an hg repository.'),
        ),
   "gpush":
-        (gpush, [], _('hg gpush')),
+        (gpush, [], _('hg gpush remote')),
+  "gimport":
+        (gimport, [], _('hg gimport')),
   "gexport":
         (gexport, [], _('hg gexport')),
   "gfetch":
--- a/dulwich/object_store.py	Wed May 27 15:58:07 2009 -0700
+++ b/dulwich/object_store.py	Wed May 27 16:05:22 2009 -0700
@@ -22,6 +22,7 @@
 
 import itertools
 import os
+import shutil
 import stat
 import tempfile
 import urllib2
@@ -248,7 +249,12 @@
         basename = os.path.join(self.pack_dir, 
             "pack-%s" % iter_sha1(entry[0] for entry in entries))
         write_pack_index_v2(basename+".idx", entries, p.get_stored_checksum())
-        os.rename(path, basename + ".pack")
+        try:
+            os.rename(path, basename + ".pack")
+        except OSError:
+            # Hack for Windows access denied error.
+            # TODO: mark the original for deletion later.
+            shutil.copyfile(path, basename + ".pack")
         self._add_known_pack(basename)
 
     def add_thin_pack(self):
--- a/dulwich/objects.py	Wed May 27 15:58:07 2009 -0700
+++ b/dulwich/objects.py	Wed May 27 16:05:22 2009 -0700
@@ -479,15 +479,19 @@
 
 def parse_timezone(text):
     offset = int(text)
+    signum = (offset < 0) and -1 or 1
+    offset = abs(offset)
     hours = int(offset / 100)
     minutes = (offset % 100)
-    return (hours * 3600) + (minutes * 60)
+    return signum * (hours * 3600 + minutes * 60)
 
 
 def format_timezone(offset):
     if offset % 60 != 0:
         raise ValueError("Unable to handle non-minute offset.")
-    return '%+03d%02d' % (offset / 3600, (offset / 60) % 60)
+    sign = (offset < 0) and '-' or '+'
+    offset = abs(offset)
+    return '%c%02d%02d' % (sign, offset / 3600, (offset / 60) % 60)
 
 
 class Commit(ShaFile):
--- a/dulwich/pack.py	Wed May 27 15:58:07 2009 -0700
+++ b/dulwich/pack.py	Wed May 27 16:05:22 2009 -0700
@@ -128,7 +128,7 @@
 
 
 def load_pack_index(filename):
-    f = open(filename, 'r')
+    f = open(filename, 'rb')
     if f.read(4) == '\377tOc':
         version = struct.unpack(">L", f.read(4))[0]
         if version == 2:
@@ -181,7 +181,7 @@
         # ensure that it hasn't changed.
         self._size = os.path.getsize(filename)
         if file is None:
-            self._file = open(filename, 'r')
+            self._file = open(filename, 'rb')
         else:
             self._file = file
         self._contents, map_offset = simple_mmap(self._file, 0, self._size)
@@ -733,7 +733,7 @@
     :param objects: Iterable over (object, path) tuples to write
     :param num_objects: Number of objects to write
     """
-    f = open(filename + ".pack", 'w')
+    f = open(filename + ".pack", 'wb')
     try:
         entries, data_sum = write_pack_data(f, objects, num_objects)
     finally:
@@ -797,7 +797,7 @@
             crc32_checksum.
     :param pack_checksum: Checksum of the pack file.
     """
-    f = open(filename, 'w')
+    f = open(filename, 'wb')
     f = SHA1Writer(f)
     fan_out_table = defaultdict(lambda: 0)
     for (name, offset, entry_checksum) in entries:
@@ -945,7 +945,7 @@
             crc32_checksum.
     :param pack_checksum: Checksum of the pack file.
     """
-    f = open(filename, 'w')
+    f = open(filename, 'wb')
     f = SHA1Writer(f)
     f.write('\377tOc') # Magic!
     f.write(struct.pack(">L", 2))
--- a/dulwich/repo.py	Wed May 27 15:58:07 2009 -0700
+++ b/dulwich/repo.py	Wed May 27 16:05:22 2009 -0700
@@ -173,14 +173,12 @@
     def _get_ref(self, file):
         f = open(file, 'rb')
         try:
-            contents = f.read()
+            contents = f.read().strip()
             if contents.startswith(SYMREF):
                 ref = contents[len(SYMREF):]
-                if ref[-1] == '\n':
-                    ref = ref[:-1]
                 return self.ref(ref)
-            assert len(contents) == 41, 'Invalid ref in %s' % file
-            return contents[:-1]
+            assert len(contents) == 40, 'Invalid ref in %s' % file
+            return contents
         finally:
             f.close()
 
@@ -291,9 +289,12 @@
 
     def remote_refs(self, remote_name):
         ret = {}
-        for root, dirs, files in os.walk(os.path.join(self.controldir(), 'refs', 'remotes', remote_name)):
+        r = os.path.join(self.controldir(), 'refs', 'remotes', remote_name)
+        for root, dirs, files in os.walk(r):
             for name in files:
-                ret[name] = self._get_ref(os.path.join(root, name))
+                if root != r:
+                    name = os.path.join(root[len(r) + 1:], name)
+                ret[name] = self._get_ref(os.path.join(r, name))
         return ret
 
     def head(self):
@@ -424,11 +425,10 @@
             basefiles = set()
             changes = list()
             csha = None
-            ctree = None
             cmode = None
             if basetree:
                 for (bmode, bname, bsha) in basetree.entries():
-                    if bmode == 57344: # TODO : properly handle submodules
+                    if bmode == 0160000: # TODO : properly handle submodules
                         continue
                     basefiles.add(bname)
                     bobj = self.get_object(bsha)
@@ -438,6 +438,7 @@
                         if isinstance (bobj, Blob):
                             changes.append (prefix + bname)
                         elif isinstance(bobj, Tree):
+                            ctree = None
                             if csha:
                                 ctree = self.get_object(csha)
                             changes.extend(filenames(bobj,
@@ -447,7 +448,7 @@
             # handle removals
             if comptree:
                 for (bmode, bname, bsha, ) in comptree.entries():
-                    if bmode == 57344: # TODO: hande submodles
+                    if bmode == 0160000: # TODO: hande submodles
                         continue
                     if bname not in basefiles:
                         bobj = self.get_object(bsha)
--- a/git_handler.py	Wed May 27 15:58:07 2009 -0700
+++ b/git_handler.py	Wed May 27 16:05:22 2009 -0700
@@ -14,7 +14,8 @@
     Tag,
     Tree,
     hex_to_sha,
-    sha_to_hex
+    sha_to_hex,
+    format_timezone,
     )
 
 import math
@@ -51,7 +52,14 @@
         self.ui = ui
         self.mapfile = 'git-mapfile'
         self.configfile = 'git-config'
-        self.gitdir = self.repo.join('git')
+
+        if ui.config('git', 'intree'):
+            self.gitdir = self.repo.wjoin('.git')
+        else:
+            self.gitdir = self.repo.join('git')
+
+        self.importbranch = ui.config('git', 'importbranch')
+
         self.init_if_missing()
         self.load_git()
         self.load_map()
@@ -109,6 +117,10 @@
 
     ## END FILE LOAD AND SAVE METHODS
 
+    def import_commits(self, remote_name):
+        self.import_git_objects(remote_name)
+        self.save_map()
+
     def fetch(self, remote_name):
         self.ui.status(_("fetching from : %s\n") % remote_name)
         self.export_git_objects()
@@ -118,7 +130,7 @@
             self.import_local_tags(refs)
         self.save_map()
 
-    def export(self):
+    def export_commits(self):
         self.export_git_objects()
         self.export_hg_tags()
         self.update_references()
@@ -126,7 +138,8 @@
 
     def push(self, remote_name):
         self.ui.status(_("pushing to : %s\n") % remote_name)
-        self.export()
+        self.export_commits()
+        self.update_remote_references(remote_name)
         self.upload_pack(remote_name)
 
     def remote_add(self, remote_name, git_url):
@@ -157,7 +170,23 @@
         return self._config['remote.' + remote_name + '.url']
 
     def update_references(self):
-        # TODO : if bookmarks exist, add them as git branches
+        try:
+            # We only care about bookmarks of the form 'name',
+            # not 'remote/name'.
+            def is_local_ref(item): return item[0].count('/') == 0
+            bms = bookmarks.parse(self.repo)
+            bms = dict(filter(is_local_ref, bms.items()))
+
+            # Create a local Git branch name for each
+            # Mercurial bookmark.
+            for key in bms:
+                hg_sha  = hex(bms[key])
+                git_sha = self.map_git_get(hg_sha)
+                self.git.set_ref('refs/heads/' + key, git_sha)
+        except AttributeError:
+            # No bookmarks extension
+            pass
+
         c = self.map_git_get(hex(self.repo.changelog.tip()))
         self.git.set_ref('refs/heads/master', c)
 
@@ -167,8 +196,18 @@
                 continue 
             self.git.set_ref('refs/tags/' + tag, self.map_git_get(hex(sha)))
 
+    # Make sure there's a refs/remotes/remote_name/name
+    #           for every refs/heads/name
+    def update_remote_references(self, remote_name):
+        self.git.set_remote_refs(self.local_heads(), remote_name)
+
+    def local_heads(self):
+        def is_local_head(item): return item[0].startswith('refs/heads')
+        refs = self.git.get_refs()
+        return dict(filter(is_local_head, refs.items()))
+
     def export_git_objects(self):
-        self.ui.status(_("exporting git objects\n"))
+        self.ui.status(_("importing Hg objects into Git\n"))
         total = len(self.repo.changelog)
         if total:
           magnitude = int(math.log(total, 10)) + 1
@@ -215,13 +254,19 @@
         author = ctx.user()
         if not '>' in author: # TODO : this kills losslessness - die (submodules)?
             author = author + ' <none@none>'
-        commit['author'] = author + ' ' + str(int(time)) + ' ' + seconds_to_offset(timezone)
+        commit['author'] = author + ' ' + str(int(time)) + ' ' + format_timezone(-timezone)
         message = ctx.description()
         commit['message'] = ctx.description() + "\n"
 
         extra = ctx.extra()
         if 'committer' in extra:
-            commit['committer'] = extra['committer']
+            # fixup timezone
+            (name_timestamp, timezone) = extra['committer'].rsplit(' ', 1)
+            try:
+                timezone = format_timezone(-int(timezone))
+                commit['committer'] = '%s %s' % (name_timestamp, timezone)
+            except ValueError:
+                self.ui.warn(_("Ignoring committer in extra, invalid timezone in r%s: '%s'.\n") % (rev, timezone))
         if 'encoding' in extra:
             commit['encoding'] = extra['encoding']
 
@@ -306,12 +351,17 @@
                     trees['/'] = []
                 trees['/'].append(fileentry)
 
-        # sort by tree depth, so we write the deepest trees first
         dirs = trees.keys()
-        dirs.sort(lambda a, b: len(b.split('/'))-len(a.split('/')))
-        dirs.remove('/')
-        dirs.append('/')
-        
+        if dirs:
+            # sort by tree depth, so we write the deepest trees first
+            dirs.sort(lambda a, b: len(b.split('/'))-len(a.split('/')))
+            dirs.remove('/')
+            dirs.append('/')
+        else:
+            # manifest is empty => make empty root tree
+            trees['/'] = []
+            dirs = ['/']
+
         # write all the trees
         tree_sha = None
         tree_shas = {}
@@ -382,6 +432,13 @@
                     if local_ref:
                         if not local_ref == refs[ref_name]:
                             changed[ref_name] = local_ref
+        
+        # Also push any local branches not on the server yet
+        for head in self.local_heads():
+            if not head in refs:
+                ref = self.git.ref(head)
+                changed[head] = ref
+
         return changed
 
     # takes a list of shas the server wants and shas the server has
@@ -406,7 +463,7 @@
             changes = list()
             changes.append((tree, path))
             for (mode, name, sha) in tree.entries():
-                if mode == 57344: # TODO : properly handle submodules and document what 57344 means
+                if mode == 0160000: # TODO : properly handle submodules and document what 57344 means
                     continue
                 if sha in seen:
                     continue
@@ -473,17 +530,26 @@
                         self.repo.tag(ref_name, hex_to_sha(sha), '', True, None, None)
                     
         
-    def import_git_objects(self, remote_name, refs):
+    def import_git_objects(self, remote_name=None, refs=None):
         self.ui.status(_("importing Git objects into Hg\n"))
         # import heads and fetched tags as remote references
         todo = []
         done = set()
         convert_list = {}
         self.renames = {}
-        
+
         # get a list of all the head shas
-        for head, sha in refs.iteritems():
+        if refs: 
+          for head, sha in refs.iteritems():
             todo.append(sha)
+        else:
+          if remote_name:
+              todo = self.git.remote_refs(remote_name).values()[:]
+          elif self.importbranch:
+              branches = self.importbranch.split(',')
+              todo = [self.git.ref(i.strip()) for i in branches]
+          else:
+              todo = self.git.heads().values()[:]
 
         # traverse the heads getting a list of all the unique commits
         while todo:
@@ -507,15 +573,20 @@
         commits = toposort.TopoSort(convert_list).items()
         
         # import each of the commits, oldest first
-        for csha in commits:
+        total = len(commits)
+        magnitude = int(math.log(total, 10)) + 1 if total else 1
+        for i, csha in enumerate(commits):
+            if i%100 == 0:
+                self.ui.status(_("at: %*d/%d\n") % (magnitude, i, total))
             commit = convert_list[csha]
             if not self.map_hg_get(csha): # it's already here
                 self.import_git_commit(commit)
             else:
                 # we need to get rename info for further upstream
                 self.pseudo_import_git_commit(commit)
-                
-        self.update_hg_bookmarks(remote_name)
+
+        if remote_name:
+            self.update_hg_bookmarks(remote_name)
 
     def update_hg_bookmarks(self, remote_name):
         try:
@@ -532,9 +603,9 @@
     def convert_git_int_mode(self, mode):
 	# TODO : make these into constants
         convert = {
-         33188: '',
-         40960: 'l',
-         33261: 'x'}
+         0100644: '',
+         0100755: 'x',
+         0120000: 'l'}
         if mode in convert:
             return convert[mode]
         return ''
@@ -561,8 +632,8 @@
     def pseudo_import_git_commit(self, commit):
         (strip_message, hg_renames, hg_branch) = self.extract_hg_metadata(commit.message)
         cs = self.map_hg_get(commit.id)
-        p1 = "0" * 40
-        p2 = "0" * 40
+        p1 = nullid
+        p2 = nullid
         if len(commit.parents) > 0:
             sha = commit.parents[0]
             p1 = self.map_hg_get(sha)
@@ -573,7 +644,7 @@
             # TODO : map extra parents to the extras file
             pass
         # saving rename info
-        if (not (p2 == "0"*40) or (p1 == "0"*40)):
+        if (not (p2 == nullid) or (p1 == nullid)):
             self.renames[cs] = {}
         else:
             self.renames[cs] = self.renames[p1].copy()
@@ -599,8 +670,8 @@
                 copied_path = None
             return context.memfilectx(f, data, 'l' in e, 'x' in e, copied_path)
 
-        p1 = "0" * 40
-        p2 = "0" * 40
+        p1 = nullid
+        p2 = nullid
         if len(commit.parents) > 0:
             sha = commit.parents[0]
             p1 = self.map_hg_get(sha)
@@ -615,7 +686,7 @@
         files = self.git.get_files_changed(commit)
 
         # wierd hack for explicit file renames in first but not second branch
-        if not (p2 == "0"*40):
+        if not (p2 == nullid):
             vals = [item for item in self.renames[p1].values() if not item in self.renames[p2].values()]
             for removefile in vals:
                 files.remove(removefile)
@@ -628,7 +699,7 @@
 
         # if committer is different than author, add it to extra
         if not commit._author_raw == commit._committer_raw:
-            extra['committer'] = commit._committer_raw
+            extra['committer'] = "%s %d %d" % (commit.committer, commit.commit_time, -commit.commit_timezone)
 
         if commit._encoding:
             extra['encoding'] = commit._encoding
@@ -637,7 +708,7 @@
             extra['branch'] = hg_branch
 
         text = strip_message
-        date = datetime.datetime.fromtimestamp(commit.author_time).strftime("%Y-%m-%d %H:%M:%S")
+        date = (commit.author_time, -commit.author_timezone)
         ctx = context.memctx(self.repo, (p1, p2), text, files, getfilectx,
                              commit.author, date, extra)
         a = self.repo.commitctx(ctx)
@@ -648,7 +719,7 @@
         gitsha = commit.id
         
         # saving rename info
-        if (not (p2 == "0"*40) or (p1 == "0"*40)):
+        if (not (p2 == nullid) or (p1 == nullid)):
             self.renames[cs] = {}
         else:
             self.renames[cs] = self.renames[p1].copy()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-empty-working-tree	Wed May 27 16:05:22 2009 -0700
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+# Fails for some reason, need to investigate
+# "$TESTDIR/hghave" git || exit 80
+
+# bail early if the user is already running git-daemon
+echo hi | nc localhost 9418 && exit 80
+
+echo "[extensions]" >> $HGRCPATH
+echo "hggit=$(echo $(dirname $(dirname $0)))" >> $HGRCPATH
+echo 'hgext.bookmarks =' >> $HGRCPATH
+
+GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
+GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
+GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0000"; export GIT_AUTHOR_DATE
+GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
+GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
+GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
+
+mkdir gitrepo
+cd gitrepo
+git init | python -c "import sys; print sys.stdin.read().replace('$(dirname $(pwd))/', '')"
+
+git commit --allow-empty -m empty
+
+# dulwich does not presently support local git repos, workaround
+cd ..
+git daemon --base-path="$(pwd)"\
+ --listen=localhost\
+ --export-all\
+  --pid-file=gitdaemon.pid \
+ --detach --reuseaddr
+
+hg gclone git://localhost/gitrepo hgrepo
+cd hgrepo
+hg log -r tip --template 'files: {files}\n'
+
+hg gclear
+hg gexport
+
+cd ..
+kill `cat gitdaemon.pid`
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-empty-working-tree.out	Wed May 27 16:05:22 2009 -0700
@@ -0,0 +1,14 @@
+Initialized empty Git repository in gitrepo/.git/
+
+[master (root-commit) 6782568] empty
+fetching from : origin
+exporting git objects
+Counting objects: 2, done.
+Total 2 (delta 0), reused 0 (delta 0)
+importing Git objects into Hg
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+files: 
+clearing out the git cache data
+exporting git objects
+at: 0/1
+converting revision 0
--- a/tests/test-file-removal	Wed May 27 15:58:07 2009 -0700
+++ b/tests/test-file-removal	Wed May 27 16:05:22 2009 -0700
@@ -4,7 +4,7 @@
 # "$TESTDIR/hghave" git || exit 80
 
 # bail early if the user is already running git-daemon
-echo hi | nc localhost 9418 && exit 80
+echo hi | nc localhost 9418 2>/dev/null && exit 80
 
 echo "[extensions]" >> $HGRCPATH
 echo "hggit=$(echo $(dirname $(dirname $0)))" >> $HGRCPATH
--- a/tests/test-git-clone	Wed May 27 15:58:07 2009 -0700
+++ b/tests/test-git-clone	Wed May 27 16:05:22 2009 -0700
@@ -4,7 +4,7 @@
 # "$TESTDIR/hghave" git || exit 80
 
 # bail early if the user is already running git-daemon
-echo hi | nc localhost 9418 && exit 80
+echo hi | nc localhost 9418 2>/dev/null && exit 80
 
 echo "[extensions]" >> $HGRCPATH
 echo "hggit=$(echo $(dirname $(dirname $0)))" >> $HGRCPATH
--- a/tests/test-sane-without-bookmarks	Wed May 27 15:58:07 2009 -0700
+++ b/tests/test-sane-without-bookmarks	Wed May 27 16:05:22 2009 -0700
@@ -4,7 +4,7 @@
 # "$TESTDIR/hghave" git || exit 80
 
 # bail early if the user is already running git-daemon
-echo hi | nc localhost 9418 && exit 80
+echo hi | nc localhost 9418 2>/dev/null && exit 80
 
 echo "[extensions]" >> $HGRCPATH
 echo "hggit=$(echo $(dirname $(dirname $0)))" >> $HGRCPATH
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-tree-decomposition	Wed May 27 16:05:22 2009 -0700
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+# Fails for some reason, need to investigate
+# "$TESTDIR/hghave" git || exit 80
+
+# bail early if the user is already running git-daemon
+echo hi | nc localhost 9418 && exit 80
+
+echo "[extensions]" >> $HGRCPATH
+echo "hggit=$(echo $(dirname $(dirname $0)))" >> $HGRCPATH
+echo 'hgext.bookmarks =' >> $HGRCPATH
+
+GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
+GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
+GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0000"; export GIT_AUTHOR_DATE
+GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
+GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
+GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
+
+count=10
+commit()
+{
+    GIT_AUTHOR_DATE="2007-01-01 00:00:$count +0000"
+    GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
+    git commit "$@" >/dev/null 2>/dev/null || echo "git commit error"
+    count=`expr $count + 1`
+}
+
+mkdir gitrepo
+cd gitrepo
+git init | python -c "import sys; print sys.stdin.read().replace('$(dirname $(pwd))/', '')"
+
+mkdir d1
+touch d1/f1 d1/f2
+git add d1/f1 d1/f2
+git commit -m initial
+
+mkdir d2
+git mv d1/f2 d2/f2
+git commit -m 'rename'
+
+# dulwich does not presently support local git repos, workaround
+cd ..
+git daemon --base-path="$(pwd)"\
+ --listen=localhost\
+ --export-all\
+  --pid-file=gitdaemon.pid \
+ --detach --reuseaddr
+
+hg gclone git://localhost/gitrepo hgrepo
+cd hgrepo
+hg log -r tip --template 'adds: {file_adds}\ndels: {file_dels}\n'
+
+cd ..
+kill `cat gitdaemon.pid`
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-tree-decomposition.out	Wed May 27 16:05:22 2009 -0700
@@ -0,0 +1,18 @@
+Initialized empty Git repository in gitrepo/.git/
+
+[master (root-commit) 60fd61f] initial
+ 0 files changed, 0 insertions(+), 0 deletions(-)
+ create mode 100644 d1/f1
+ create mode 100644 d1/f2
+[master a2e8665] rename
+ 1 files changed, 0 insertions(+), 0 deletions(-)
+ rename {d1 => d2}/f2 (100%)
+fetching from : origin
+exporting git objects
+Counting objects: 8, done.
+Compressing objects:  25% (1/4)   
Compressing objects:  50% (2/4)   
Compressing objects:  75% (3/4)   
Compressing objects: 100% (4/4)   
Compressing objects: 100% (4/4), done.
+Total 8 (delta 0), reused 0 (delta 0)
+importing Git objects into Hg
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+adds: d2/f2
+dels: d1/f2