From 1fde98d0db690e2e02ef5399d3a6582241518e0a Mon Sep 17 00:00:00 2001 From: Alexandre Marin Date: Tue, 10 Apr 2018 15:19:42 -0700 Subject: [PATCH] seqimport - make it survive CLs that have moves outside of clientspec Summary: seqimport currently has a gap: if a changelist touches files outside of clientspec, it will blow up when trying to get the move info for it. Even if that was not the case, it could blow up if the file was moved into the clientspec. This change makes it resilient to that by providing the same list created by `parse_fstat` to prefilter files we check for move info, and then making `parse_where` take an optional parameter saying it is fine if file is not on client (i.e. moved into clientspec) Differential Revision: D7574415 fbshipit-source-id: 63f6a32436d3d53d6f9402575a9a13bb4187b76c --- hgext/p4fastimport/importer.py | 10 +++++++--- hgext/p4fastimport/seqimporter.py | 24 ++++++++++++++++++++---- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/hgext/p4fastimport/importer.py b/hgext/p4fastimport/importer.py index 7fc049fcff..98edfe6537 100644 --- a/hgext/p4fastimport/importer.py +++ b/hgext/p4fastimport/importer.py @@ -26,10 +26,14 @@ KEYWORD_REGEX = "\$(Id|Header|DateTime|" + \ #TODO: make p4 user configurable P4_ADMIN_USER = 'p4admin' -def relpath(client, depotfile): +def relpath(client, depotfile, ignore_nonexisting=False): where = p4.parse_where(client, depotfile) - filename = where['clientFile'].replace('//%s/' % client, '') - return p4.decodefilename(filename) + filename = where.get('clientFile') + if filename is not None: + filename = filename.replace('//%s/' % client, '') + elif not ignore_nonexisting: + raise error.Abort('Could not find file %s' % (depotfile)) + return p4.decodefilename(filename) if filename is not None else filename def get_localname(client, p4filelogs): for p4fl in p4filelogs: diff --git a/hgext/p4fastimport/seqimporter.py b/hgext/p4fastimport/seqimporter.py index 3df327d891..18c42bbe35 100644 --- a/hgext/p4fastimport/seqimporter.py +++ b/hgext/p4fastimport/seqimporter.py @@ -47,7 +47,7 @@ class ChangelistImporter(object): else: added_or_modified.append((p4path, hgpath)) - moved = self._get_move_info(p4cl) + moved = self._get_move_info(p4cl, p4flogs) node = self._create_commit(p4cl, p4flogs, removed, moved) largefiles = self._get_largefiles(p4cl, added_or_modified, node) @@ -65,14 +65,30 @@ class ChangelistImporter(object): self.ui.debug('largefile: %s, oid: %s\n' % (hgpath, oid)) return largefiles - def _get_move_info(self, p4cl): + def _get_move_info(self, p4cl, p4flogs): '''Returns a dict where entries are (dst, src)''' moves = {} + files_in_clientspec = { + p4flog._depotfile: hgpath for hgpath, p4flog in p4flogs.items() + } for filename, info in p4cl.parsed['files'].items(): + if filename not in files_in_clientspec: + continue src = info.get('src') if src: - hgsrc = importer.relpath(self.client, src) - hgdst = importer.relpath(self.client, filename) + hgdst = files_in_clientspec[filename] + # The below could return None if the source of the move is + # outside of client view. That is expected. + # This info will be used when creating the commit, and value of + # None in the moves dictionary is a no-op, it will treat it as + # an add in hg. As it just came into the client view we cannot + # store any move info for it in hg (even though it was a legit + # move in perforce). + hgsrc = importer.relpath( + self.client, + src, + ignore_nonexisting=True, + ) moves[hgdst] = hgsrc return moves