# HG changeset patch # User Scott Chacon <schacon@gmail.com> # Date 1240525570 25200 # Thu Apr 23 15:26:10 2009 -0700 # Node ID d6c443a91b1834130535c537d6db7d01c0f18d95 # Parent 9150a8f9d1f283b85dc9fbecd500c3a105ade3ba refactored the git handling stuff out into another class diff --git a/DESIGN.txt b/DESIGN.txt --- a/DESIGN.txt +++ b/DESIGN.txt @@ -9,6 +9,14 @@ (Dulwich library) += Lossless Two Way = +We need to store data that Git records that Merc does not in a git/extra_data file. This would be parents over two and committer information (author will be mapped to Hg committer). This way two Hg developers can collaborate without the Git transport messing up the local commits. +Each Git commit should be reproducable as a Merc ID and vice versa on any system. + += ISSUES = + +* currently sorts by date, though it should be sorting topologically. if it gets to a commit where we haven't processed it's parents, it will probably fuck up. + May need to use bookmarks extension to do everything better. * Cloning from a Git Repository * diff --git a/__init__.py b/__init__.py --- a/__init__.py +++ b/__init__.py @@ -34,6 +34,7 @@ import os, errno, sys import subprocess import dulwich +from git_handler import GitHandler def gclone(ui, git_url, hg_repo_path=None): ## TODO : add git_url as the default remote path @@ -50,7 +51,8 @@ dulwich.repo.Repo.init_bare(git_hg_path) # fetch the initial git data - git_fetch(dest_repo, git_url) + git = GitHandler(dest_repo, ui) + git.fetch(git_url) # checkout the tip # hg.update(ui, dest_repo) @@ -60,34 +62,7 @@ def gpull(ui, repo): dest_repo.ui.status(_("pulling from git url\n")) - - -def git_fetch(dest_repo, git_url): - dest_repo.ui.status(_("fetching from git url\n")) - git_fetch_pack(dest_repo, git_url) - -def git_fetch_pack(dest_repo, git_url): - from dulwich.repo import Repo - from dulwich.client import SimpleFetchGraphWalker - client, path = get_transport_and_path(git_url) - git_dir = os.path.join(dest_repo.path, 'git') - r = Repo(git_dir) - graphwalker = SimpleFetchGraphWalker(r.heads().values(), r.get_parents) - f, commit = r.object_store.add_pack() - refs = client.fetch_pack(path, r.object_store.determine_wants_all, graphwalker, f.write, sys.stdout.write) - f.close() - commit() - r.set_refs(refs) - -def get_transport_and_path(uri): - from dulwich.client import TCPGitClient, SSHGitClient, SubprocessGitClient - for handler, transport in (("git://", TCPGitClient), ("git+ssh://", SSHGitClient)): - if uri.startswith(handler): - host, path = uri[len(handler):].split("/", 1) - return transport(host), "/"+path - # if its not git or git+ssh, try a local url.. - return SubprocessGitClient(), uri - + commands.norepo += " gclone" cmdtable = { "gclone": @@ -102,4 +77,4 @@ _('hg gpush remote')), "gpull": (gpull, [], _('hg gpull [--merge] remote')), -} \ No newline at end of file +} diff --git a/git_handler.py b/git_handler.py new file mode 100644 --- /dev/null +++ b/git_handler.py @@ -0,0 +1,80 @@ +import os, errno, sys +import dulwich +from dulwich.repo import Repo +from dulwich.client import SimpleFetchGraphWalker +from hgext import bookmarks +from mercurial.i18n import _ + +class GitHandler(object): + + def __init__(self, dest_repo, ui): + self.repo = dest_repo + self.ui = ui + git_dir = os.path.join(self.repo.path, 'git') + self.git = Repo(git_dir) + + def fetch(self, git_url): + self.ui.status(_("fetching from git url: " + git_url + "\n")) + self.export_git_objects() + self.fetch_pack(git_url) + self.import_git_objects() + + def fetch_pack(self, git_url): + client, path = self.get_transport_and_path(git_url) + graphwalker = SimpleFetchGraphWalker(self.git.heads().values(), self.git.get_parents) + f, commit = self.git.object_store.add_pack() + try: + determine_wants = self.git.object_store.determine_wants_all + refs = client.fetch_pack(path, determine_wants, graphwalker, f.write, sys.stdout.write) + f.close() + commit() + self.git.set_refs(refs) + except: + f.close() + raise + + def import_git_objects(self): + self.ui.status(_("importing Git objects into Hg\n")) + # import heads as remote references + todo = [] + done = set() + convert_list = [] + for head, sha in self.git.heads().iteritems(): + todo.append(sha) + while todo: + sha = todo.pop() + assert isinstance(sha, str) + if sha in done: + continue + done.add(sha) + commit = self.git.commit(sha) + convert_list.append(commit) + todo.extend([p for p in commit.parents if p not in done]) + + convert_list.sort(cmp=lambda x,y: x.commit_time-y.commit_time) + for commit in convert_list: + print "commit: %s" % sha + self.import_git_commit(commit) + + print bookmarks.parse(self.repo) + + def import_git_commit(self, commit): + # check that parents are all in Hg, or no parents + print commit.parents + pass + + def export_git_objects(self): + pass + + def check_bookmarks(self): + if self.ui.config('extensions', 'hgext.bookmarks') is not None: + print "YOU NEED TO SETUP BOOKMARKS" + + def get_transport_and_path(self, uri): + from dulwich.client import TCPGitClient, SSHGitClient, SubprocessGitClient + for handler, transport in (("git://", TCPGitClient), ("git+ssh://", SSHGitClient)): + if uri.startswith(handler): + host, path = uri[len(handler):].split("/", 1) + return transport(host), "/"+path + # if its not git or git+ssh, try a local url.. + return SubprocessGitClient(), uri