eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/hgext/convert/git.py
changeset 69 c6bca38c1cbf
equal deleted inserted replaced
68:5ff1fc726848 69:c6bca38c1cbf
       
     1 # git.py - git support for the convert extension
       
     2 #
       
     3 #  Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
       
     4 #
       
     5 # This software may be used and distributed according to the terms of the
       
     6 # GNU General Public License version 2 or any later version.
       
     7 
       
     8 import os
       
     9 from mercurial import util
       
    10 from mercurial.node import hex, nullid
       
    11 from mercurial.i18n import _
       
    12 
       
    13 from common import NoRepo, commit, converter_source, checktool
       
    14 
       
    15 class convert_git(converter_source):
       
    16     # Windows does not support GIT_DIR= construct while other systems
       
    17     # cannot remove environment variable. Just assume none have
       
    18     # both issues.
       
    19     if hasattr(os, 'unsetenv'):
       
    20         def gitopen(self, s):
       
    21             prevgitdir = os.environ.get('GIT_DIR')
       
    22             os.environ['GIT_DIR'] = self.path
       
    23             try:
       
    24                 return util.popen(s, 'rb')
       
    25             finally:
       
    26                 if prevgitdir is None:
       
    27                     del os.environ['GIT_DIR']
       
    28                 else:
       
    29                     os.environ['GIT_DIR'] = prevgitdir
       
    30     else:
       
    31         def gitopen(self, s):
       
    32             return util.popen('GIT_DIR=%s %s' % (self.path, s), 'rb')
       
    33 
       
    34     def gitread(self, s):
       
    35         fh = self.gitopen(s)
       
    36         data = fh.read()
       
    37         return data, fh.close()
       
    38 
       
    39     def __init__(self, ui, path, rev=None):
       
    40         super(convert_git, self).__init__(ui, path, rev=rev)
       
    41 
       
    42         if os.path.isdir(path + "/.git"):
       
    43             path += "/.git"
       
    44         if not os.path.exists(path + "/objects"):
       
    45             raise NoRepo(_("%s does not look like a Git repository") % path)
       
    46 
       
    47         checktool('git', 'git')
       
    48 
       
    49         self.path = path
       
    50 
       
    51     def getheads(self):
       
    52         if not self.rev:
       
    53             heads, ret = self.gitread('git rev-parse --branches --remotes')
       
    54             heads = heads.splitlines()
       
    55         else:
       
    56             heads, ret = self.gitread("git rev-parse --verify %s" % self.rev)
       
    57             heads = [heads[:-1]]
       
    58         if ret:
       
    59             raise util.Abort(_('cannot retrieve git heads'))
       
    60         return heads
       
    61 
       
    62     def catfile(self, rev, type):
       
    63         if rev == hex(nullid):
       
    64             raise IOError()
       
    65         data, ret = self.gitread("git cat-file %s %s" % (type, rev))
       
    66         if ret:
       
    67             raise util.Abort(_('cannot read %r object at %s') % (type, rev))
       
    68         return data
       
    69 
       
    70     def getfile(self, name, rev):
       
    71         data = self.catfile(rev, "blob")
       
    72         mode = self.modecache[(name, rev)]
       
    73         return data, mode
       
    74 
       
    75     def getchanges(self, version):
       
    76         self.modecache = {}
       
    77         fh = self.gitopen("git diff-tree -z --root -m -r %s" % version)
       
    78         changes = []
       
    79         seen = set()
       
    80         entry = None
       
    81         for l in fh.read().split('\x00'):
       
    82             if not entry:
       
    83                 if not l.startswith(':'):
       
    84                     continue
       
    85                 entry = l
       
    86                 continue
       
    87             f = l
       
    88             if f not in seen:
       
    89                 seen.add(f)
       
    90                 entry = entry.split()
       
    91                 h = entry[3]
       
    92                 p = (entry[1] == "100755")
       
    93                 s = (entry[1] == "120000")
       
    94                 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
       
    95                 changes.append((f, h))
       
    96             entry = None
       
    97         if fh.close():
       
    98             raise util.Abort(_('cannot read changes in %s') % version)
       
    99         return (changes, {})
       
   100 
       
   101     def getcommit(self, version):
       
   102         c = self.catfile(version, "commit") # read the commit hash
       
   103         end = c.find("\n\n")
       
   104         message = c[end + 2:]
       
   105         message = self.recode(message)
       
   106         l = c[:end].splitlines()
       
   107         parents = []
       
   108         author = committer = None
       
   109         for e in l[1:]:
       
   110             n, v = e.split(" ", 1)
       
   111             if n == "author":
       
   112                 p = v.split()
       
   113                 tm, tz = p[-2:]
       
   114                 author = " ".join(p[:-2])
       
   115                 if author[0] == "<": author = author[1:-1]
       
   116                 author = self.recode(author)
       
   117             if n == "committer":
       
   118                 p = v.split()
       
   119                 tm, tz = p[-2:]
       
   120                 committer = " ".join(p[:-2])
       
   121                 if committer[0] == "<": committer = committer[1:-1]
       
   122                 committer = self.recode(committer)
       
   123             if n == "parent":
       
   124                 parents.append(v)
       
   125 
       
   126         if committer and committer != author:
       
   127             message += "\ncommitter: %s\n" % committer
       
   128         tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
       
   129         tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
       
   130         date = tm + " " + str(tz)
       
   131 
       
   132         c = commit(parents=parents, date=date, author=author, desc=message,
       
   133                    rev=version)
       
   134         return c
       
   135 
       
   136     def gettags(self):
       
   137         tags = {}
       
   138         fh = self.gitopen('git ls-remote --tags "%s"' % self.path)
       
   139         prefix = 'refs/tags/'
       
   140         for line in fh:
       
   141             line = line.strip()
       
   142             if not line.endswith("^{}"):
       
   143                 continue
       
   144             node, tag = line.split(None, 1)
       
   145             if not tag.startswith(prefix):
       
   146                 continue
       
   147             tag = tag[len(prefix):-3]
       
   148             tags[tag] = node
       
   149         if fh.close():
       
   150             raise util.Abort(_('cannot read tags from %s') % self.path)
       
   151 
       
   152         return tags
       
   153 
       
   154     def getchangedfiles(self, version, i):
       
   155         changes = []
       
   156         if i is None:
       
   157             fh = self.gitopen("git diff-tree --root -m -r %s" % version)
       
   158             for l in fh:
       
   159                 if "\t" not in l:
       
   160                     continue
       
   161                 m, f = l[:-1].split("\t")
       
   162                 changes.append(f)
       
   163         else:
       
   164             fh = self.gitopen('git diff-tree --name-only --root -r %s "%s^%s" --'
       
   165                              % (version, version, i + 1))
       
   166             changes = [f.rstrip('\n') for f in fh]
       
   167         if fh.close():
       
   168             raise util.Abort(_('cannot read changes in %s') % version)
       
   169 
       
   170         return changes