2018-04-03 00:59:03 +03:00
|
|
|
# (c) 2017-present Facebook Inc.
|
|
|
|
from __future__ import absolute_import
|
|
|
|
|
2018-04-03 00:59:36 +03:00
|
|
|
import re
|
2018-04-03 00:59:07 +03:00
|
|
|
|
2018-04-03 00:59:03 +03:00
|
|
|
from mercurial.i18n import _
|
2018-04-03 00:59:10 +03:00
|
|
|
from mercurial import (
|
2018-04-03 00:59:31 +03:00
|
|
|
context,
|
2018-04-03 00:59:10 +03:00
|
|
|
)
|
2018-04-03 00:59:03 +03:00
|
|
|
|
2018-04-03 00:59:22 +03:00
|
|
|
from . import importer, lfs, p4
|
2018-04-03 00:59:03 +03:00
|
|
|
|
|
|
|
class ChangelistImporter(object):
|
2018-04-03 00:59:46 +03:00
|
|
|
def __init__(self, ui, repo, ctx, client, storepath):
|
2018-04-03 00:59:03 +03:00
|
|
|
self.ui = ui
|
|
|
|
self.repo = repo
|
2018-04-03 00:59:31 +03:00
|
|
|
self.node = self.repo[ctx].node()
|
2018-04-03 00:59:03 +03:00
|
|
|
self.client = client
|
|
|
|
self.storepath = storepath
|
|
|
|
|
2018-04-03 00:59:28 +03:00
|
|
|
def importcl(self, p4cl, bookmark=None):
|
2018-04-03 00:59:03 +03:00
|
|
|
try:
|
2018-04-03 00:59:31 +03:00
|
|
|
ctx, largefiles = self._import(p4cl)
|
|
|
|
self.node = self.repo[ctx].node()
|
|
|
|
return ctx, largefiles
|
2018-04-03 00:59:03 +03:00
|
|
|
except Exception as e:
|
2018-04-03 00:59:10 +03:00
|
|
|
self.ui.write_err(_('Failed importing CL%d: %s\n') % (p4cl.cl, e))
|
2018-04-03 00:59:20 +03:00
|
|
|
raise
|
2018-04-03 00:59:28 +03:00
|
|
|
|
2018-04-03 00:59:10 +03:00
|
|
|
def _import(self, p4cl):
|
2018-04-03 00:59:20 +03:00
|
|
|
'''Converts the provided p4 CL into a commit in hg.
|
2018-04-03 00:59:22 +03:00
|
|
|
Returns a tuple containing hg node and largefiles for new commit'''
|
2018-04-03 00:59:10 +03:00
|
|
|
self.ui.debug('importing CL%d\n' % p4cl.cl)
|
|
|
|
fstat = p4.parse_fstat(p4cl.cl, self.client)
|
2018-04-03 00:59:22 +03:00
|
|
|
added_or_modified = []
|
2018-04-03 00:59:31 +03:00
|
|
|
removed = set()
|
|
|
|
p4flogs = {}
|
2018-04-03 00:59:03 +03:00
|
|
|
for info in fstat:
|
|
|
|
action = info['action']
|
|
|
|
p4path = info['depotFile']
|
2018-04-03 00:59:25 +03:00
|
|
|
data = {p4cl.cl: {'action': action, 'type': info['type']}}
|
2018-04-03 00:59:03 +03:00
|
|
|
hgpath = importer.relpath(self.client, p4path)
|
2018-04-03 00:59:31 +03:00
|
|
|
p4flogs[hgpath] = p4.P4Filelog(p4path, data)
|
|
|
|
|
2018-04-03 00:59:03 +03:00
|
|
|
if action in p4.ACTION_DELETE + p4.ACTION_ARCHIVE:
|
2018-04-03 00:59:31 +03:00
|
|
|
removed.add(hgpath)
|
2018-04-03 00:59:03 +03:00
|
|
|
else:
|
2018-04-03 00:59:22 +03:00
|
|
|
added_or_modified.append((p4path, hgpath))
|
2018-04-03 00:59:15 +03:00
|
|
|
|
|
|
|
moved = self._get_move_info(p4cl)
|
2018-04-03 00:59:31 +03:00
|
|
|
node = self._create_commit(p4cl, p4flogs, removed, moved)
|
2018-04-03 00:59:41 +03:00
|
|
|
largefiles = self._get_largefiles(p4cl, added_or_modified, node)
|
2018-04-03 00:59:31 +03:00
|
|
|
|
2018-04-03 00:59:20 +03:00
|
|
|
return node, largefiles
|
2018-04-03 00:59:03 +03:00
|
|
|
|
2018-04-03 00:59:41 +03:00
|
|
|
def _get_largefiles(self, p4cl, files, node):
|
2018-04-03 00:59:22 +03:00
|
|
|
largefiles = []
|
2018-04-03 00:59:41 +03:00
|
|
|
ctx = self.repo[node]
|
2018-04-03 00:59:22 +03:00
|
|
|
for p4path, hgpath in files:
|
|
|
|
flog = self.repo.file(hgpath)
|
2018-04-03 00:59:41 +03:00
|
|
|
fnode = ctx.filenode(hgpath)
|
|
|
|
islfs, oid = lfs.getlfsinfo(flog, fnode)
|
2018-04-03 00:59:22 +03:00
|
|
|
if islfs:
|
|
|
|
largefiles.append((p4cl.cl, p4path, oid))
|
|
|
|
self.ui.debug('largefile: %s, oid: %s\n' % (hgpath, oid))
|
|
|
|
return largefiles
|
|
|
|
|
2018-04-03 00:59:15 +03:00
|
|
|
def _get_move_info(self, p4cl):
|
2018-04-03 00:59:31 +03:00
|
|
|
'''Returns a dict where entries are (dst, src)'''
|
|
|
|
moves = {}
|
2018-04-03 00:59:15 +03:00
|
|
|
for filename, info in p4cl.parsed['files'].items():
|
|
|
|
src = info.get('src')
|
|
|
|
if src:
|
|
|
|
hgsrc = importer.relpath(self.client, src)
|
|
|
|
hgdst = importer.relpath(self.client, filename)
|
2018-04-03 00:59:31 +03:00
|
|
|
moves[hgdst] = hgsrc
|
2018-04-03 00:59:15 +03:00
|
|
|
return moves
|
|
|
|
|
2018-04-03 00:59:31 +03:00
|
|
|
def _create_commit(self, p4cl, p4flogs, removed, moved):
|
|
|
|
'''Uses a memory context to commit files into the repo'''
|
|
|
|
def getfile(repo, memctx, path):
|
|
|
|
if path in removed:
|
|
|
|
# A path that shows up in files (below) but returns None in this
|
|
|
|
# function implies a deletion.
|
|
|
|
return None
|
|
|
|
|
|
|
|
p4flog = p4flogs[path]
|
|
|
|
data = p4.get_file(p4flog._depotfile, clnum=p4cl.cl)
|
|
|
|
islink = p4flog.issymlink(p4cl.cl)
|
|
|
|
if islink:
|
|
|
|
# p4 will give us content with a trailing newline, symlinks
|
|
|
|
# cannot end with newline
|
|
|
|
data = data.rstrip()
|
2018-04-03 00:59:36 +03:00
|
|
|
if p4flog.iskeyworded(p4cl.cl):
|
|
|
|
data = re.sub(importer.KEYWORD_REGEX, r'$\1$', data)
|
2018-04-03 00:59:31 +03:00
|
|
|
|
|
|
|
return context.memfilectx(
|
|
|
|
repo,
|
|
|
|
memctx,
|
|
|
|
path,
|
|
|
|
data,
|
|
|
|
islink=islink,
|
2018-04-03 00:59:33 +03:00
|
|
|
isexec=p4flog.isexec(p4cl.cl),
|
2018-04-03 00:59:31 +03:00
|
|
|
copied=moved.get(path),
|
|
|
|
)
|
|
|
|
|
|
|
|
return context.memctx(
|
|
|
|
self.repo, # repository
|
|
|
|
(self.node, None), # parents
|
|
|
|
p4cl.description, # commit message
|
|
|
|
p4flogs.keys(), # files affected by this change
|
|
|
|
getfile, # fn - see above
|
|
|
|
user=p4cl.user, # commit author
|
|
|
|
date=p4cl.hgdate, # commit date
|
|
|
|
extra={'p4changelist': p4cl.cl}, # commit extras
|
|
|
|
).commit()
|