Mercurial > hg-git
diff hggit/overlay.py @ 408:2dcfd4bbfc1a
Support for hg incoming
author | Brendan Cully <brendan@kublai.com> |
---|---|
date | Tue, 24 May 2011 11:16:45 -0700 |
parents | |
children | f29401590803 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hggit/overlay.py Tue May 24 11:16:45 2011 -0700 @@ -0,0 +1,254 @@ +# overlay classes for repositories +# unifies access to unimported git objects and committed hg objects +# designed to support incoming +# +# incomplete, implemented on demand + +from mercurial import context +from mercurial.node import bin, hex, nullid + +class overlaymanifest(object): + def __init__(self, repo, sha): + self.repo = repo + self.tree = repo.handler.git.get_object(sha) + self._map = None + self._flagmap = None + + def copy(self): + return overlaymanifest(self.repo, self.tree.id) + + def keys(self): + self.load() + return self._map.keys() + + def flags(self, path): + self.load() + + def hgflag(gitflag): + if gitflag & 0100: + return 'x' + elif gitflag & 020000: + return 'l' + else: + return '' + + return hgflag(self._flagmap[path]) + + def load(self): + if self._map is not None: + return + + self._map = {} + self._flagmap = {} + + def addtree(tree, dirname): + for entry in tree.entries(): + if entry[0] & 040000: + # expand directory + subtree = self.repo.handler.git.get_object(entry[2]) + addtree(subtree, dirname + entry[1] + '/') + else: + path = dirname + entry[1] + self._map[path] = bin(entry[2]) + self._flagmap[path] = entry[0] + + addtree(self.tree, '') + + def __iter__(self): + self.load() + return self._map.__iter__() + + def __getitem__(self, path): + self.load() + return self._map[path] + + def __delitem__(self, path): + del self._map[path] + +class overlayfilectx(object): + def __init__(self, repo, path, fileid=None): + self.repo = repo + self._path = path + self.fileid = fileid + + # this is a hack to skip copy detection + def ancestors(self): + return [self, self] + + def rev(self): + return -1 + + def path(self): + return self._path + + def filelog(self): + return self.fileid + + def data(self): + blob = self.repo.handler.git.get_object(self.fileid) + return blob.data + +class overlaychangectx(context.changectx): + def __init__(self, repo, sha): + self.repo = repo + self.commit = repo.handler.git.get_object(sha) + + def node(self): + return bin(self.commit.id) + + def rev(self): + return self.repo.rev(bin(self.commit.id)) + + def date(self): + return self.commit.author_time, self.commit.author_timezone + + def branch(self): + return 'default' + + def user(self): + return self.commit.author + + def files(self): + return [] + + def extra(self): + return {} + + def description(self): + return self.commit.message + + def parents(self): + return [overlaychangectx(self.repo, sha) for sha in self.commit.parents] + + def manifestnode(self): + return bin(self.commit.tree) + + def hex(self): + return self.commit.id + + def tags(self): + return [] + + def bookmarks(self): + return [] + + def manifest(self): + return overlaymanifest(self.repo, self.commit.tree) + + def filectx(self, path, filelog=None): + mf = self.manifest() + return overlayfilectx(self.repo, path, mf[path]) + + def flags(self, path): + mf = self.manifest() + return mf.flags(path) + + def __nonzero__(self): + return True + +class overlayrevlog(object): + def __init__(self, repo, base): + self.repo = repo + self.base = base + + def parents(self, n): + gitrev = self.repo.revmap.get(n) + if not gitrev: + # we've reached a revision we have + return self.base.parents(n) + commit = self.repo.handler.git.get_object(n) + + def gitorhg(n): + hn = self.repo.handler.map_hg_get(hex(n)) + if hn is not None: + return bin(hn) + return n + + # currently ignores the octopus + p1 = gitorhg(bin(commit.parents[0])) + if len(commit.parents) > 1: + p2 = gitorhg(bin(commit.parents[1])) + else: + p2 = nullid + + return [p1, p2] + + def parentrevs(self, rev): + return [self.rev(p) for p in self.parents(self.node(rev))] + + def node(self, rev): + gitnode = self.repo.nodemap.get(rev) + if gitnode is None: + return self.base.node(rev) + return gitnode + + def rev(self, n): + gitrev = self.repo.revmap.get(n) + if gitrev is None: + return self.base.rev(n) + return gitrev + + def nodesbetween(self, nodelist, revs): + # this is called by pre-1.9 incoming with the nodelist we returned from + # getremotechanges. Just return it back. + return [nodelist] + + def __len__(self): + return len(self.repo.handler.repo) + len(self.repo.revmap) + + +class overlayrepo(object): + def __init__(self, handler, commits, refs): + self.handler = handler + + self.changelog = overlayrevlog(self, handler.repo.changelog) + self.manifest = overlayrevlog(self, handler.repo.manifest) + + # for incoming -p + self.root = handler.repo.root + self.getcwd = handler.repo.getcwd + self.status = handler.repo.status + self.ui = handler.repo.ui + + self.revmap = None + self.nodemap = None + self.refmap = None + self.tagmap = None + + self._makemaps(commits, refs) + + def __getitem__(self, n): + if n not in self.revmap: + return self.handler.repo[n] + return overlaychangectx(self, n) + + def nodebookmarks(self, n): + return self.refmap.get(n, []) + + def nodetags(self, n): + return self.tagmap.get(n, []) + + def rev(self, n): + return self.revmap[n] + + def filectx(self, path, fileid=None): + return overlayfilectx(self, path, fileid=fileid) + + def _makemaps(self, commits, refs): + baserev = self.handler.repo['tip'].rev() + self.revmap = {} + self.nodemap = {} + for i, n in enumerate(commits): + rev = baserev + i + 1 + self.revmap[n] = rev + self.nodemap[rev] = n + + self.refmap = {} + self.tagmap = {} + for ref in refs: + if ref.startswith('refs/heads/'): + refname = ref[11:] + self.refmap.setdefault(bin(refs[ref]), []).append(refname) + elif ref.startswith('refs/tags/'): + tagname = ref[10:] + self.tagmap.setdefault(bin(refs[ref]), []).append(tagname)