Mercurial > hg
view hgext/convert/git.py @ 14133:28085b82f801
graphlog: always sort revisions topologically
The grapher cannot really handled revisions if they are not emitted in
topological order. The previous 'reverse()' revset was not enough to achieve
that and was replaced by an explicit sort call for simplicity. The --limit
option is now also handled as usual with cmdutil.loglimit() instead of a
'limit' revset.
author | Patrick Mezard <pmezard@gmail.com> |
---|---|
date | Sun, 01 May 2011 15:51:52 +0200 |
parents | 6b7077df4aa5 |
children | fc004d16633f |
line wrap: on
line source
# git.py - git support for the convert extension # # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. import os from mercurial import util from mercurial.node import hex, nullid from mercurial.i18n import _ from common import NoRepo, commit, converter_source, checktool class convert_git(converter_source): # Windows does not support GIT_DIR= construct while other systems # cannot remove environment variable. Just assume none have # both issues. if hasattr(os, 'unsetenv'): def gitopen(self, s, noerr=False): prevgitdir = os.environ.get('GIT_DIR') os.environ['GIT_DIR'] = self.path try: if noerr: (stdin, stdout, stderr) = util.popen3(s) return stdout else: return util.popen(s, 'rb') finally: if prevgitdir is None: del os.environ['GIT_DIR'] else: os.environ['GIT_DIR'] = prevgitdir else: def gitopen(self, s, noerr=False): if noerr: (sin, so, se) = util.popen3('GIT_DIR=%s %s' % (self.path, s)) return stdout else: util.popen('GIT_DIR=%s %s' % (self.path, s), 'rb') def gitread(self, s): fh = self.gitopen(s) data = fh.read() return data, fh.close() def __init__(self, ui, path, rev=None): super(convert_git, self).__init__(ui, path, rev=rev) if os.path.isdir(path + "/.git"): path += "/.git" if not os.path.exists(path + "/objects"): raise NoRepo(_("%s does not look like a Git repository") % path) checktool('git', 'git') self.path = path def getheads(self): if not self.rev: heads, ret = self.gitread('git rev-parse --branches --remotes') heads = heads.splitlines() else: heads, ret = self.gitread("git rev-parse --verify %s" % self.rev) heads = [heads[:-1]] if ret: raise util.Abort(_('cannot retrieve git heads')) return heads def catfile(self, rev, type): if rev == hex(nullid): raise IOError() data, ret = self.gitread("git cat-file %s %s" % (type, rev)) if ret: raise util.Abort(_('cannot read %r object at %s') % (type, rev)) return data def getfile(self, name, rev): data = self.catfile(rev, "blob") mode = self.modecache[(name, rev)] return data, mode def getchanges(self, version): self.modecache = {} fh = self.gitopen("git diff-tree -z --root -m -r %s" % version) changes = [] seen = set() entry = None for l in fh.read().split('\x00'): if not entry: if not l.startswith(':'): continue entry = l continue f = l if f not in seen: seen.add(f) entry = entry.split() h = entry[3] p = (entry[1] == "100755") s = (entry[1] == "120000") self.modecache[(f, h)] = (p and "x") or (s and "l") or "" changes.append((f, h)) entry = None if fh.close(): raise util.Abort(_('cannot read changes in %s') % version) return (changes, {}) def getcommit(self, version): c = self.catfile(version, "commit") # read the commit hash end = c.find("\n\n") message = c[end + 2:] message = self.recode(message) l = c[:end].splitlines() parents = [] author = committer = None for e in l[1:]: n, v = e.split(" ", 1) if n == "author": p = v.split() tm, tz = p[-2:] author = " ".join(p[:-2]) if author[0] == "<": author = author[1:-1] author = self.recode(author) if n == "committer": p = v.split() tm, tz = p[-2:] committer = " ".join(p[:-2]) if committer[0] == "<": committer = committer[1:-1] committer = self.recode(committer) if n == "parent": parents.append(v) if committer and committer != author: message += "\ncommitter: %s\n" % committer tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:] tz = -int(tzs) * (int(tzh) * 3600 + int(tzm)) date = tm + " " + str(tz) c = commit(parents=parents, date=date, author=author, desc=message, rev=version) return c def gettags(self): tags = {} fh = self.gitopen('git ls-remote --tags "%s"' % self.path) prefix = 'refs/tags/' for line in fh: line = line.strip() if not line.endswith("^{}"): continue node, tag = line.split(None, 1) if not tag.startswith(prefix): continue tag = tag[len(prefix):-3] tags[tag] = node if fh.close(): raise util.Abort(_('cannot read tags from %s') % self.path) return tags def getchangedfiles(self, version, i): changes = [] if i is None: fh = self.gitopen("git diff-tree --root -m -r %s" % version) for l in fh: if "\t" not in l: continue m, f = l[:-1].split("\t") changes.append(f) else: fh = self.gitopen('git diff-tree --name-only --root -r %s "%s^%s" --' % (version, version, i + 1)) changes = [f.rstrip('\n') for f in fh] if fh.close(): raise util.Abort(_('cannot read changes in %s') % version) return changes def getbookmarks(self): bookmarks = {} # Interesting references in git are prefixed prefix = 'refs/heads/' prefixlen = len(prefix) # factor two commands gitcmd = { 'remote/': 'git ls-remote --heads origin', '': 'git show-ref'} # Origin heads for reftype in gitcmd: try: fh = self.gitopen(gitcmd[reftype], noerr=True) for line in fh: line = line.strip() rev, name = line.split(None, 1) if not name.startswith(prefix): continue name = '%s%s' % (reftype, name[prefixlen:]) bookmarks[name] = rev except: pass return bookmarks