##// END OF EJS Templates
convert: handle copies when converting from Perforce (issue4744)
convert: handle copies when converting from Perforce (issue4744)

File last commit:

r25749:f2748cc4 default
r25751:17a9da90 default
Show More
git.py
400 lines | 13.9 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
Durham Goode
convert: improve support for unusual .gitmodules...
r25699 from mercurial import util, config, error
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()
Durham Goode
convert: add support for specifying multiple revs...
r25748 def __init__(self, ui, path, revs=None):
super(convert_git, self).__init__(ui, path, revs=revs)
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):
Durham Goode
convert: add support for specifying multiple revs...
r25748 if not self.revs:
Patrick Mezard
convert/git: check status when reading the whole output
r10986 heads, ret = self.gitread('git rev-parse --branches --remotes')
heads = heads.splitlines()
Durham Goode
convert: support multiple specifed revs in git source...
r25749 if ret:
raise util.Abort(_('cannot retrieve git heads'))
Brendan Cully
convert: import all branches from git repositories
r4768 else:
Durham Goode
convert: support multiple specifed revs in git source...
r25749 heads = []
for rev in self.revs:
rawhead, ret = self.gitread("git rev-parse --verify %s" % rev)
heads.append(rawhead[:-1])
if ret:
raise util.Abort(_('cannot retrieve git head "%s"') % rev)
Patrick Mezard
convert/git: check status when reading the whole output
r10986 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()
Durham Goode
convert: handle .gitmodules with non-tab whitespaces...
r25698 # Each item in .gitmodules starts with whitespace that cant be parsed
c.parse('.gitmodules', '\n'.join(line.strip() for line in
content.split('\n')))
YaNan Xu
convert: add support for converting git submodule (issue3528)...
r17929 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:
Durham Goode
convert: improve support for unusual .gitmodules...
r25699 # This can happen if a file is in the repo that has permissions
# 160000, but there is no .gitmodules file.
self.ui.warn(_("warning: cannot read submodules config file in "
"%s\n") % version)
return
try:
self.parsegitmodules(modules)
except error.ParseError:
self.ui.warn(_("warning: unable to parse .gitmodules in %s\n")
% version)
return
YaNan Xu
convert: add support for converting git submodule (issue3528)...
r17929 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', ''))
Mads Kiilerich
convert: optimize convert of files that are unmodified from p2 in merges...
r24395 return (changes, copies, set())
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)