##// END OF EJS Templates
diff: move status fixup earlier, out of _filepairs()...
diff: move status fixup earlier, out of _filepairs() This prepares for future patches, and it also lets us remove the ugly "ctx1" argument to _filepairs() (ugly because of its assymmetry -- there's no "ctx2" argument).

File last commit:

r26779:aaa33ec3 default
r27900:27572a5c default
Show More
p4.py
290 lines | 10.2 KiB | text/x-python | PythonLexer
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823 # Perforce source for convert extension.
#
# Copyright 2009, Frank Kingswood <frank@kingswood-consulting.co.uk>
#
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # 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.
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 from mercurial import util, error
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823 from mercurial.i18n import _
Matt Mackall
convert: attempt to check repo type before checking for tool
r7973 from common import commit, converter_source, checktool, NoRepo
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823 import marshal
Frank Kingswood
convert: Make P4 conversion cope with keywords, binary files and symbolic links....
r8829 import re
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823
def loaditer(f):
"Yield the dictionary objects generated by p4"
try:
while True:
d = marshal.load(f)
if not d:
break
yield d
except EOFError:
pass
Eugene Baranov
convert: unescape Perforce-escaped special characters in filenames
r25788 def decodefilename(filename):
"""Perforce escapes special characters @, #, *, or %
with %40, %23, %2A, or %25 respectively
>>> decodefilename('portable-net45%252Bnetcore45%252Bwp8%252BMonoAndroid')
'portable-net45%2Bnetcore45%2Bwp8%2BMonoAndroid'
>>> decodefilename('//Depot/Directory/%2525/%2523/%23%40.%2A')
'//Depot/Directory/%25/%23/#@.*'
"""
replacements = [('%2A', '*'), ('%23', '#'), ('%40', '@'), ('%25', '%')]
for k, v in replacements:
filename = filename.replace(k, v)
return filename
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823 class p4_source(converter_source):
Durham Goode
convert: add support for specifying multiple revs...
r25748 def __init__(self, ui, path, revs=None):
Eugene Baranov
convert: when converting from Perforce use original local encoding by default...
r25884 # avoid import cycle
import convcmd
Durham Goode
convert: add support for specifying multiple revs...
r25748 super(p4_source, self).__init__(ui, path, revs=revs)
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823
Frank Kingswood
convert: Make P4 conversion cope with keywords, binary files and symbolic links....
r8829 if "/" in path and not path.startswith('//'):
Martin Geisler
convert: mark strings for translation
r10939 raise NoRepo(_('%s does not look like a P4 repository') % path)
Matt Mackall
convert: attempt to check repo type before checking for tool
r7973
Mads Kiilerich
convert: missing p4 tool is only slightly fatal...
r7905 checktool('p4', abort=False)
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823
self.p4changes = {}
self.heads = {}
self.changeset = {}
self.files = {}
Eugene Baranov
convert: handle copies when converting from Perforce (issue4744)
r25751 self.copies = {}
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823 self.tags = {}
self.lastbranch = {}
self.parent = {}
Eugene Baranov
convert: when converting from Perforce use original local encoding by default...
r25884 self.encoding = self.ui.config('convert', 'p4.encoding',
default=convcmd.orig_encoding)
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823 self.depotname = {} # mapping from local name to depot name
Eugene Baranov
convert: handle copies when converting from Perforce (issue4744)
r25751 self.localname = {} # mapping from depot name to local name
Matt Mackall
many, many trivial check-code fixups
r10282 self.re_type = re.compile(
"([a-z]+)?(text|binary|symlink|apple|resource|unicode|utf\d+)"
"(\+\w+)?$")
self.re_keywords = re.compile(
r"\$(Id|Header|Date|DateTime|Change|File|Revision|Author)"
r":[^$\n]*\$")
Frank Kingswood
convert: Make P4 conversion cope with keywords, binary files and symbolic links....
r8829 self.re_keywords_old = re.compile("\$(Id|Header):[^$\n]*\$")
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823
Durham Goode
convert: add support for specifying multiple revs...
r25748 if revs and len(revs) > 1:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("p4 source does not support specifying "
Durham Goode
convert: add support for specifying multiple revs...
r25748 "multiple revisions"))
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823 self._parse(ui, path)
def _parse_view(self, path):
"Read changes affecting the path"
Frank Kingswood
Fix shell quoting on p4 conversion
r11347 cmd = 'p4 -G changes -s submitted %s' % util.shellquote(path)
Peter Ingebretson
convert: fix marshalling in P4 convert extension to use a binary stream...
r9474 stdout = util.popen(cmd, mode='rb')
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823 for d in loaditer(stdout):
c = d.get("change", None)
if c:
self.p4changes[c] = True
def _parse(self, ui, path):
"Prepare list of P4 filenames and revisions to import"
ui.status(_('reading p4 views\n'))
# read client spec or view
if "/" in path:
self._parse_view(path)
if path.startswith("//") and path.endswith("/..."):
views = {path[:-3]:""}
else:
views = {"//": ""}
else:
Frank Kingswood
Fix shell quoting on p4 conversion
r11347 cmd = 'p4 -G client -o %s' % util.shellquote(path)
Peter Ingebretson
convert: fix marshalling in P4 convert extension to use a binary stream...
r9474 clientspec = marshal.load(util.popen(cmd, mode='rb'))
Dirkjan Ochtman
cleanup: remove all trailing whitespace
r7869
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823 views = {}
for client in clientspec:
if client.startswith("View"):
sview, cview = clientspec[client].split()
self._parse_view(sview)
if sview.endswith("...") and cview.endswith("..."):
sview = sview[:-3]
cview = cview[:-3]
cview = cview[2:]
cview = cview[cview.find("/") + 1:]
views[sview] = cview
# list of changes that affect our source files
self.p4changes = self.p4changes.keys()
self.p4changes.sort(key=int)
# list with depot pathnames, longest first
vieworder = views.keys()
Martin Geisler
p4: simplify sort key
r9039 vieworder.sort(key=len, reverse=True)
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823
# handle revision limiting
startrev = self.ui.config('convert', 'p4.startrev', default=0)
Dirkjan Ochtman
cleanup: remove all trailing whitespace
r7869 self.p4changes = [x for x in self.p4changes
if ((not startrev or int(x) >= int(startrev)) and
Durham Goode
convert: add support for specifying multiple revs...
r25748 (not self.revs or int(x) <= int(self.revs[0])))]
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823
# now read the full changelists to get the list of file revisions
ui.status(_('collecting p4 changelists\n'))
lastid = None
for change in self.p4changes:
Mike Sperber
convert: Work around p4 instability (issue2465)...
r12891 cmd = "p4 -G describe -s %s" % change
Peter Ingebretson
convert: fix marshalling in P4 convert extension to use a binary stream...
r9474 stdout = util.popen(cmd, mode='rb')
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823 d = marshal.load(stdout)
Matt Mackall
p4: accept changesets with no description (issue4282)
r21758 desc = self.recode(d.get("desc", ""))
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823 shortdesc = desc.split("\n", 1)[0]
t = '%s %s' % (d["change"], repr(shortdesc)[1:-1])
ui.status(util.ellipsis(t, 80) + '\n')
if lastid:
parents = [lastid]
else:
parents = []
Dirkjan Ochtman
cleanup: remove all trailing whitespace
r7869
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823 date = (int(d["time"]), 0) # timezone not set
FUJIWARA Katsunori
i18n: use locale insensitive format for datetimes as intermediate representation (issue3398)...
r16514 c = commit(author=self.recode(d["user"]),
date=util.datestr(date, '%Y-%m-%d %H:%M:%S %1%2'),
Eugene Baranov
convert: use 'default' for specifying branch name in branchmap (issue4753)...
r25805 parents=parents, desc=desc, branch=None,
Matt Mackall
many, many trivial check-code fixups
r10282 extra={"p4": change})
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823
files = []
Eugene Baranov
convert: handle copies when converting from Perforce (issue4744)
r25751 copies = {}
copiedfiles = []
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823 i = 0
while ("depotFile%d" % i) in d and ("rev%d" % i) in d:
oldname = d["depotFile%d" % i]
filename = None
for v in vieworder:
Eugene Baranov
convert: ignore case changes in vieworder for Perforce...
r25776 if oldname.lower().startswith(v.lower()):
Eugene Baranov
convert: unescape Perforce-escaped special characters in filenames
r25788 filename = decodefilename(views[v] + oldname[len(v):])
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823 break
if filename:
files.append((filename, d["rev%d" % i]))
self.depotname[filename] = oldname
Eugene Baranov
convert: handle copies when converting from Perforce (issue4744)
r25751 if (d.get("action%d" % i) == "move/add"):
copiedfiles.append(filename)
self.localname[oldname] = filename
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823 i += 1
Eugene Baranov
convert: handle copies when converting from Perforce (issue4744)
r25751
# Collect information about copied files
for filename in copiedfiles:
oldname = self.depotname[filename]
flcmd = 'p4 -G filelog %s' \
% util.shellquote(oldname)
flstdout = util.popen(flcmd, mode='rb')
copiedfilename = None
for d in loaditer(flstdout):
copiedoldname = None
i = 0
while ("change%d" % i) in d:
if (d["change%d" % i] == change and
d["action%d" % i] == "move/add"):
j = 0
while ("file%d,%d" % (i, j)) in d:
if d["how%d,%d" % (i, j)] == "moved from":
copiedoldname = d["file%d,%d" % (i, j)]
break
j += 1
i += 1
if copiedoldname and copiedoldname in self.localname:
copiedfilename = self.localname[copiedoldname]
break
if copiedfilename:
copies[filename] = copiedfilename
else:
ui.warn(_("cannot find source for copied file: %s@%s\n")
% (filename, change))
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823 self.changeset[change] = c
self.files[change] = files
Eugene Baranov
convert: handle copies when converting from Perforce (issue4744)
r25751 self.copies[change] = copies
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823 lastid = change
Dirkjan Ochtman
cleanup: remove all trailing whitespace
r7869
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823 if lastid:
self.heads = [lastid]
def getheads(self):
return self.heads
def getfile(self, name, rev):
Martin Geisler
p4: fix long line and bad spacing around %
r11348 cmd = 'p4 -G print %s' \
% util.shellquote("%s#%s" % (self.depotname[name], rev))
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823
Eugene Baranov
convert: if getting a file from Perforce fails try to get it one more time...
r25775 lasterror = None
while True:
stdout = util.popen(cmd, mode='rb')
mode = None
Eugene Baranov
convert: when getting file from Perforce concatenate data at the end...
r25882 contents = []
Eugene Baranov
convert: if getting a file from Perforce fails try to get it one more time...
r25775 keywords = None
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823
Eugene Baranov
convert: if getting a file from Perforce fails try to get it one more time...
r25775 for d in loaditer(stdout):
code = d["code"]
data = d.get("data")
Frank Kingswood
convert: Make P4 conversion cope with keywords, binary files and symbolic links....
r8829
Eugene Baranov
convert: if getting a file from Perforce fails try to get it one more time...
r25775 if code == "error":
# if this is the first time error happened
# re-attempt getting the file
if not lasterror:
lasterror = IOError(d["generic"], data)
# this will exit inner-most for-loop
break
else:
raise lasterror
Dirkjan Ochtman
kill trailing whitespace
r8843
Eugene Baranov
convert: if getting a file from Perforce fails try to get it one more time...
r25775 elif code == "stat":
action = d.get("action")
if action in ["purge", "delete", "move/delete"]:
return None, None
p4type = self.re_type.match(d["type"])
if p4type:
mode = ""
flags = ((p4type.group(1) or "")
+ (p4type.group(3) or ""))
if "x" in flags:
mode = "x"
if p4type.group(2) == "symlink":
mode = "l"
if "ko" in flags:
keywords = self.re_keywords_old
elif "k" in flags:
keywords = self.re_keywords
Dirkjan Ochtman
kill trailing whitespace
r8843
Eugene Baranov
convert: if getting a file from Perforce fails try to get it one more time...
r25775 elif code == "text" or code == "binary":
Eugene Baranov
convert: when getting file from Perforce concatenate data at the end...
r25882 contents.append(data)
Eugene Baranov
convert: if getting a file from Perforce fails try to get it one more time...
r25775
lasterror = None
if not lasterror:
break
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823
if mode is None:
Mads Kiilerich
convert: use None value for missing files instead of overloading IOError...
r22296 return None, None
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823
Eugene Baranov
convert: when getting file from Perforce concatenate data at the end...
r25882 contents = ''.join(contents)
Frank Kingswood
convert: Make P4 conversion cope with keywords, binary files and symbolic links....
r8829 if keywords:
contents = keywords.sub("$\\1$", contents)
if mode == "l" and contents.endswith("\n"):
contents = contents[:-1]
Patrick Mezard
convert: merge sources getmode() into getfile()
r11134 return contents, mode
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823
Mads Kiilerich
convert: introduce --full for converting all files...
r22300 def getchanges(self, rev, full):
if full:
timeless@mozdev.org
grammar: use does instead of do where appropriate
r26779 raise error.Abort(_("convert from p4 does not support --full"))
Eugene Baranov
convert: handle copies when converting from Perforce (issue4744)
r25751 return self.files[rev], self.copies[rev], set()
Frank Kingswood
convert: Perforce source for conversion to Mercurial
r7823
def getcommit(self, rev):
return self.changeset[rev]
def gettags(self):
return self.tags
def getchangedfiles(self, rev, i):
Matt Mackall
replace util.sort with sorted built-in...
r8209 return sorted([x[0] for x in self.files[rev]])