##// END OF EJS Templates
encoding: add hfsignoreclean to clean out HFS-ignored characters...
encoding: add hfsignoreclean to clean out HFS-ignored characters According to Apple Technote 1150 (unavailable from Apple as far as I can tell, but archived in several places online), HFS+ ignores sixteen specific unicode runes when doing path normalization. We need to handle those cases, so this function lets us efficiently strip the offending characters from a UTF-8 encoded string (which is the only way it seems to matter on OS X.)

File last commit:

r23206:a7f25a31 stable
r23596:885bd7c5 stable
Show More
git.py
385 lines | 13.3 KiB | text/x-python | PythonLexer
Martin Geisler
convert: add copyright and license headers to back-ends
r8250 # 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
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Brendan Cully
Split convert extension into common and repository type modules
r4536
import os
Ross Lagerwall
convert/git: catch errors from modern git-ls-remote (issue3428)...
r18570 import subprocess
YaNan Xu
convert: add support for converting git submodule (issue3528)...
r17929 from mercurial import util, config
Martin Geisler
clone, patch, convert: use hex(nullid) instead of '0'*40
r12144 from mercurial.node import hex, nullid
Martin Geisler
convert: mark strings for translation
r10939 from mercurial.i18n import _
Brendan Cully
Split convert extension into common and repository type modules
r4536
Patrick Mezard
convert: fail if an external required tool is not found
r5497 from common import NoRepo, commit, converter_source, checktool
Brendan Cully
Split convert extension into common and repository type modules
r4536
YaNan Xu
convert: add support for converting git submodule (issue3528)...
r17929 class submodule(object):
def __init__(self, path, node, url):
self.path = path
self.node = node
self.url = url
def hgsub(self):
return "%s = [git]%s" % (self.path, self.url)
def hgsubstate(self):
return "%s %s" % (self.node, self.path)
Brendan Cully
Split convert extension into common and repository type modules
r4536 class convert_git(converter_source):
Patrick Mezard
convert: fix issue702 about GIT_DIR= construct unsupported under Windows.
r5217 # Windows does not support GIT_DIR= construct while other systems
# cannot remove environment variable. Just assume none have
# both issues.
Augie Fackler
hgext: replace uses of hasattr with util.safehasattr
r14945 if util.safehasattr(os, 'unsetenv'):
Ross Lagerwall
convert/git: catch errors from modern git-ls-remote (issue3428)...
r18570 def gitopen(self, s, err=None):
Patrick Mezard
convert: fix issue702 about GIT_DIR= construct unsupported under Windows.
r5217 prevgitdir = os.environ.get('GIT_DIR')
os.environ['GIT_DIR'] = self.path
try:
Ross Lagerwall
convert/git: catch errors from modern git-ls-remote (issue3428)...
r18570 if err == subprocess.PIPE:
Edouard Gomez
convert: add bookmarks reading support to git backend
r13756 (stdin, stdout, stderr) = util.popen3(s)
return stdout
Ross Lagerwall
convert/git: catch errors from modern git-ls-remote (issue3428)...
r18570 elif err == subprocess.STDOUT:
return self.popen_with_stderr(s)
Edouard Gomez
convert: add bookmarks reading support to git backend
r13756 else:
return util.popen(s, 'rb')
Patrick Mezard
convert: fix issue702 about GIT_DIR= construct unsupported under Windows.
r5217 finally:
if prevgitdir is None:
del os.environ['GIT_DIR']
else:
os.environ['GIT_DIR'] = prevgitdir
David Schleimer
convert: drastically speed up git conversions...
r21630
def gitpipe(self, s):
prevgitdir = os.environ.get('GIT_DIR')
os.environ['GIT_DIR'] = self.path
try:
return util.popen3(s)
finally:
if prevgitdir is None:
del os.environ['GIT_DIR']
else:
os.environ['GIT_DIR'] = prevgitdir
Patrick Mezard
convert: fix issue702 about GIT_DIR= construct unsupported under Windows.
r5217 else:
Ross Lagerwall
convert/git: catch errors from modern git-ls-remote (issue3428)...
r18570 def gitopen(self, s, err=None):
if err == subprocess.PIPE:
Edouard Gomez
convert: add bookmarks reading support to git backend
r13756 (sin, so, se) = util.popen3('GIT_DIR=%s %s' % (self.path, s))
timeless
convert: fix error in git solaris code
r14177 return so
Ross Lagerwall
convert/git: catch errors from modern git-ls-remote (issue3428)...
r18570 elif err == subprocess.STDOUT:
return self.popen_with_stderr(s)
Edouard Gomez
convert: add bookmarks reading support to git backend
r13756 else:
Mads Kiilerich
convert: fix git convert on solaris - it cannot remove environment variables
r14735 return util.popen('GIT_DIR=%s %s' % (self.path, s), 'rb')
Brendan Cully
convert: gitcmd wrapper for os.popen
r4767
David Schleimer
convert: drastically speed up git conversions...
r21630 def gitpipe(self, s):
return util.popen3('GIT_DIR=%s %s' % (self.path, s))
Ross Lagerwall
convert/git: catch errors from modern git-ls-remote (issue3428)...
r18570 def popen_with_stderr(self, s):
p = subprocess.Popen(s, shell=True, bufsize=-1,
close_fds=util.closefds,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=False,
env=None)
return p.stdout
Patrick Mezard
convert/git: check status when reading the whole output
r10986 def gitread(self, s):
fh = self.gitopen(s)
data = fh.read()
return data, fh.close()
Brendan Cully
convert: add -r argument specifying latest revision to convert
r4760 def __init__(self, ui, path, rev=None):
Brendan Cully
convert: call superclass init from engine init functions
r4807 super(convert_git, self).__init__(ui, path, rev=rev)
Brendan Cully
Split convert extension into common and repository type modules
r4536 if os.path.isdir(path + "/.git"):
path += "/.git"
if not os.path.exists(path + "/objects"):
Martin Geisler
convert: mark strings for translation
r10939 raise NoRepo(_("%s does not look like a Git repository") % path)
Patrick Mezard
convert: fail if an external required tool is not found
r5497
Siddharth Agarwal
convert: change default for git rename detection to 50%...
r22512 # The default value (50) is based on the default for 'git diff'.
similarity = ui.configint('convert', 'git.similarity', default=50)
Siddharth Agarwal
convert: add support to detect git renames and copies...
r22470 if similarity < 0 or similarity > 100:
raise util.Abort(_('similarity must be between 0 and 100'))
if similarity > 0:
Thomas Arendsen Hein
convert: use git diff-tree -Cn% instead of --find-copies=n% for older git...
r23206 self.simopt = '-C%d%%' % similarity
Siddharth Agarwal
convert: add support to find git copies from all files in the working copy...
r22471 findcopiesharder = ui.configbool('convert', 'git.findcopiesharder',
False)
if findcopiesharder:
self.simopt += ' --find-copies-harder'
Siddharth Agarwal
convert: add support to detect git renames and copies...
r22470 else:
self.simopt = ''
Dhruva Krishnamurthy
convert: use git executable only, with subcommands...
r6837 checktool('git', 'git')
Patrick Mezard
convert: fail if an external required tool is not found
r5497
Brendan Cully
Split convert extension into common and repository type modules
r4536 self.path = path
YaNan Xu
convert: add support for converting git submodule (issue3528)...
r17929 self.submodules = []
Brendan Cully
Split convert extension into common and repository type modules
r4536
David Schleimer
convert: drastically speed up git conversions...
r21630 self.catfilepipe = self.gitpipe('git cat-file --batch')
def after(self):
for f in self.catfilepipe:
f.close()
Brendan Cully
Split convert extension into common and repository type modules
r4536 def getheads(self):
Brendan Cully
convert: import all branches from git repositories
r4768 if not self.rev:
Patrick Mezard
convert/git: check status when reading the whole output
r10986 heads, ret = self.gitread('git rev-parse --branches --remotes')
heads = heads.splitlines()
Brendan Cully
convert: import all branches from git repositories
r4768 else:
Patrick Mezard
convert/git: check status when reading the whole output
r10986 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
Brendan Cully
Split convert extension into common and repository type modules
r4536
def catfile(self, rev, type):
Martin Geisler
clone, patch, convert: use hex(nullid) instead of '0'*40
r12144 if rev == hex(nullid):
Brodie Rao
cleanup: "raise SomeException()" -> "raise SomeException"
r16687 raise IOError
David Schleimer
convert: drastically speed up git conversions...
r21630 self.catfilepipe[0].write(rev+'\n')
self.catfilepipe[0].flush()
info = self.catfilepipe[1].readline().split()
if info[1] != type:
Patrick Mezard
convert/git: check status when reading the whole output
r10986 raise util.Abort(_('cannot read %r object at %s') % (type, rev))
David Schleimer
convert: drastically speed up git conversions...
r21630 size = int(info[2])
data = self.catfilepipe[1].read(size)
if len(data) < size:
FUJIWARA Katsunori
convert: fix argument mismatch at formatting the abort message...
r21958 raise util.Abort(_('cannot read %r object at %s: unexpected size')
% (type, rev))
David Schleimer
convert: drastically speed up git conversions...
r21630 # read the trailing newline
self.catfilepipe[1].read(1)
Patrick Mezard
convert/git: check status when reading the whole output
r10986 return data
Brendan Cully
Split convert extension into common and repository type modules
r4536
def getfile(self, name, rev):
FUJIWARA Katsunori
convert: detect removal of ".gitmodules" at git source revisions correctly...
r21868 if rev == hex(nullid):
Mads Kiilerich
convert: use None value for missing files instead of overloading IOError...
r22296 return None, None
YaNan Xu
convert: add support for converting git submodule (issue3528)...
r17929 if name == '.hgsub':
data = '\n'.join([m.hgsub() for m in self.submoditer()])
mode = ''
elif name == '.hgsubstate':
data = '\n'.join([m.hgsubstate() for m in self.submoditer()])
mode = ''
else:
data = self.catfile(rev, "blob")
mode = self.modecache[(name, rev)]
Patrick Mezard
convert: merge sources getmode() into getfile()
r11134 return data, mode
Brendan Cully
Split convert extension into common and repository type modules
r4536
YaNan Xu
convert: add support for converting git submodule (issue3528)...
r17929 def submoditer(self):
null = hex(nullid)
for m in sorted(self.submodules, key=lambda p: p.path):
if m.node != null:
yield m
def parsegitmodules(self, content):
"""Parse the formatted .gitmodules file, example file format:
[submodule "sub"]\n
\tpath = sub\n
\turl = git://giturl\n
"""
self.submodules = []
c = config.config()
# Each item in .gitmodules starts with \t that cant be parsed
c.parse('.gitmodules', content.replace('\t',''))
for sec in c.sections():
s = c[sec]
if 'url' in s and 'path' in s:
self.submodules.append(submodule(s['path'], '', s['url']))
def retrievegitmodules(self, version):
modules, ret = self.gitread("git show %s:%s" % (version, '.gitmodules'))
if ret:
Bryan O'Sullivan
convert: fix a too-long line nag
r17930 raise util.Abort(_('cannot read submodules config file in %s') %
version)
YaNan Xu
convert: add support for converting git submodule (issue3528)...
r17929 self.parsegitmodules(modules)
for m in self.submodules:
node, ret = self.gitread("git rev-parse %s:%s" % (version, m.path))
if ret:
continue
m.node = node.strip()
Mads Kiilerich
convert: introduce --full for converting all files...
r22300 def getchanges(self, version, full):
if full:
raise util.Abort(_("convert from git do not support --full"))
Brendan Cully
Split convert extension into common and repository type modules
r4536 self.modecache = {}
Siddharth Agarwal
convert: add support to detect git renames and copies...
r22470 fh = self.gitopen("git diff-tree -z --root -m -r %s %s" % (
self.simopt, version))
Brendan Cully
Split convert extension into common and repository type modules
r4536 changes = []
Siddharth Agarwal
convert: add support to detect git renames and copies...
r22470 copies = {}
Benoit Boissinot
convert: use set instead of dict
r8456 seen = set()
Patrick Mezard
convert: fix non-ASCII filenames retrieval from git sources (issue 1360)
r7242 entry = None
Siddharth Agarwal
convert: for git, factor out code to add entries to a separate function...
r22469 subexists = [False]
subdeleted = [False]
Siddharth Agarwal
convert: for git's getchanges, use explicit index for iteration...
r22467 difftree = fh.read().split('\x00')
lcount = len(difftree)
i = 0
Siddharth Agarwal
convert: for git, factor out code to add entries to a separate function...
r22469
Siddharth Agarwal
convert: add support to detect git renames and copies...
r22470 def add(entry, f, isdest):
Siddharth Agarwal
convert: for git, factor out code to add entries to a separate function...
r22469 seen.add(f)
h = entry[3]
p = (entry[1] == "100755")
s = (entry[1] == "120000")
Siddharth Agarwal
convert: add support to detect git renames and copies...
r22470 renamesource = (not isdest and entry[4][0] == 'R')
Siddharth Agarwal
convert: for git, factor out code to add entries to a separate function...
r22469
if f == '.gitmodules':
subexists[0] = True
Siddharth Agarwal
convert: add support to detect git renames and copies...
r22470 if entry[4] == 'D' or renamesource:
Siddharth Agarwal
convert: for git, factor out code to add entries to a separate function...
r22469 subdeleted[0] = True
changes.append(('.hgsub', hex(nullid)))
else:
changes.append(('.hgsub', ''))
elif entry[1] == '160000' or entry[0] == ':160000':
subexists[0] = True
else:
Siddharth Agarwal
convert: add support to detect git renames and copies...
r22470 if renamesource:
h = hex(nullid)
Siddharth Agarwal
convert: for git, factor out code to add entries to a separate function...
r22469 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
changes.append((f, h))
Siddharth Agarwal
convert: for git's getchanges, use explicit index for iteration...
r22467 while i < lcount:
l = difftree[i]
i += 1
Patrick Mezard
convert: fix non-ASCII filenames retrieval from git sources (issue 1360)
r7242 if not entry:
if not l.startswith(':'):
continue
Siddharth Agarwal
convert: for git's getchanges, always split entry line into components...
r22468 entry = l.split()
Alexis S. L. Carvalho
convert_git: avoid returning two entries for the same file in getchanges...
r5335 continue
Patrick Mezard
convert: fix non-ASCII filenames retrieval from git sources (issue 1360)
r7242 f = l
if f not in seen:
Siddharth Agarwal
convert: add support to detect git renames and copies...
r22470 add(entry, f, False)
# A file can be copied multiple times, or modified and copied
# simultaneously. So f can be repeated even if fdest isn't.
if entry[4][0] in 'RC':
# rename or copy: next line is the destination
fdest = difftree[i]
i += 1
if fdest not in seen:
add(entry, fdest, True)
# .gitmodules isn't imported at all, so it being copied to
# and fro doesn't really make sense
if f != '.gitmodules' and fdest != '.gitmodules':
copies[fdest] = f
Patrick Mezard
convert: fix non-ASCII filenames retrieval from git sources (issue 1360)
r7242 entry = None
Patrick Mezard
convert/git: check status when reading output stream
r10987 if fh.close():
raise util.Abort(_('cannot read changes in %s') % version)
YaNan Xu
convert: add support for converting git submodule (issue3528)...
r17929
Siddharth Agarwal
convert: for git, factor out code to add entries to a separate function...
r22469 if subexists[0]:
if subdeleted[0]:
FUJIWARA Katsunori
convert: detect removal of ".gitmodules" at git source revisions correctly...
r21868 changes.append(('.hgsubstate', hex(nullid)))
else:
self.retrievegitmodules(version)
changes.append(('.hgsubstate', ''))
Siddharth Agarwal
convert: add support to detect git renames and copies...
r22470 return (changes, copies)
Brendan Cully
Split convert extension into common and repository type modules
r4536
def getcommit(self, version):
c = self.catfile(version, "commit") # read the commit hash
end = c.find("\n\n")
Matt Mackall
many, many trivial check-code fixups
r10282 message = c[end + 2:]
Brendan Cully
convert: ove recode method into converter_source
r4759 message = self.recode(message)
Brendan Cully
Split convert extension into common and repository type modules
r4536 l = c[:end].splitlines()
parents = []
Richard Quirk
Add committer tag only when needed in git conversion...
r8271 author = committer = None
Brendan Cully
Split convert extension into common and repository type modules
r4536 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]
Brendan Cully
convert: ove recode method into converter_source
r4759 author = self.recode(author)
Brendan Cully
Split convert extension into common and repository type modules
r4536 if n == "committer":
p = v.split()
tm, tz = p[-2:]
committer = " ".join(p[:-2])
if committer[0] == "<": committer = committer[1:-1]
Brendan Cully
convert: ove recode method into converter_source
r4759 committer = self.recode(committer)
Matt Mackall
many, many trivial check-code fixups
r10282 if n == "parent":
parents.append(v)
Brendan Cully
Split convert extension into common and repository type modules
r4536
Richard Quirk
Add committer tag only when needed in git conversion...
r8271 if committer and committer != author:
message += "\ncommitter: %s\n" % committer
Brendan Cully
Split convert extension into common and repository type modules
r4536 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
date = tm + " " + str(tz)
Brendan Cully
convert: record the source revision in the changelog
r4873 c = commit(parents=parents, date=date, author=author, desc=message,
rev=version)
Brendan Cully
Split convert extension into common and repository type modules
r4536 return c
Augie Fackler
convert: enable deterministic conversion progress bar for git
r22413 def numcommits(self):
return len([None for _ in self.gitopen('git rev-list --all')])
Brendan Cully
Split convert extension into common and repository type modules
r4536 def gettags(self):
tags = {}
Edouard Gomez
convert: support non annotated tags in git backend...
r16259 alltags = {}
Ross Lagerwall
convert/git: catch errors from modern git-ls-remote (issue3428)...
r18570 fh = self.gitopen('git ls-remote --tags "%s"' % self.path,
err=subprocess.STDOUT)
Brendan Cully
Split convert extension into common and repository type modules
r4536 prefix = 'refs/tags/'
Edouard Gomez
convert: support non annotated tags in git backend...
r16259
# Build complete list of tags, both annotated and bare ones
Brendan Cully
Split convert extension into common and repository type modules
r4536 for line in fh:
line = line.strip()
Augie Fackler
git convert: some versions of git use fatal: instead of error:...
r18572 if line.startswith("error:") or line.startswith("fatal:"):
Ross Lagerwall
convert/git: catch errors from modern git-ls-remote (issue3428)...
r18570 raise util.Abort(_('cannot read tags from %s') % self.path)
Brendan Cully
Split convert extension into common and repository type modules
r4536 node, tag = line.split(None, 1)
if not tag.startswith(prefix):
continue
Edouard Gomez
convert: support non annotated tags in git backend...
r16259 alltags[tag[len(prefix):]] = node
Patrick Mezard
convert/git: check status when reading output stream
r10987 if fh.close():
raise util.Abort(_('cannot read tags from %s') % self.path)
Brendan Cully
Split convert extension into common and repository type modules
r4536
Edouard Gomez
convert: support non annotated tags in git backend...
r16259 # Filter out tag objects for annotated tag refs
for tag in alltags:
if tag.endswith('^{}'):
tags[tag[:-3]] = alltags[tag]
else:
if tag + '^{}' in alltags:
continue
else:
tags[tag] = alltags[tag]
Brendan Cully
Split convert extension into common and repository type modules
r4536 return tags
Alexis S. L. Carvalho
convert_git: add --filemap support
r5380
def getchangedfiles(self, version, i):
changes = []
if i is None:
Patrick Mezard
convert/git: rename gitcmd() into gitopen() for readability
r10985 fh = self.gitopen("git diff-tree --root -m -r %s" % version)
Alexis S. L. Carvalho
convert_git: add --filemap support
r5380 for l in fh:
if "\t" not in l:
continue
m, f = l[:-1].split("\t")
changes.append(f)
else:
Brodie Rao
cleanup: eradicate long lines
r16683 fh = self.gitopen('git diff-tree --name-only --root -r %s '
'"%s^%s" --' % (version, version, i + 1))
Alexis S. L. Carvalho
convert_git: add --filemap support
r5380 changes = [f.rstrip('\n') for f in fh]
Patrick Mezard
convert/git: check status when reading output stream
r10987 if fh.close():
raise util.Abort(_('cannot read changes in %s') % version)
Alexis S. L. Carvalho
convert_git: add --filemap support
r5380
return changes
Edouard Gomez
convert: add bookmarks reading support to git backend
r13756
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:
Ross Lagerwall
convert/git: catch errors from modern git-ls-remote (issue3428)...
r18570 fh = self.gitopen(gitcmd[reftype], err=subprocess.PIPE)
Edouard Gomez
convert: add bookmarks reading support to git backend
r13756 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
Brodie Rao
cleanup: replace naked excepts with except Exception: ...
r16689 except Exception:
Edouard Gomez
convert: add bookmarks reading support to git backend
r13756 pass
return bookmarks
Ben Goswami
splicemap: improve error handling when source is git (issue2084)...
r19121
Sean Farley
convert: add mapname parameter to checkrevformat...
r20373 def checkrevformat(self, revstr, mapname='splicemap'):
Ben Goswami
splicemap: improve error handling when source is git (issue2084)...
r19121 """ git revision string is a 40 byte hex """
Sean Farley
convert: add mapname parameter to checkrevformat...
r20373 self.checkhexformat(revstr, mapname)