mirror of
https://github.com/facebook/sapling.git
synced 2024-10-09 16:31:02 +03:00
p4fastimport: work around P4 symlink/directory bug
The P4 repository can erroneously consider a file to be "inside" a symlink to a directory. 'p4 sync' (the client) reports an error if it encounters this situation, but 'hg update' aborts. Work around the problem by discarding such files. In the testcase, we can't accept changelist #2, because we have no changelist that deletes the symlink, so instead we have to ignore the new file. Differential Revision: https://phab.mercurial-scm.org/D1314
This commit is contained in:
parent
87eaccd8c9
commit
6fe60c08e1
@ -20,6 +20,10 @@ Config example:
|
||||
# and a p4fastimporter import
|
||||
ignore-time-delta = None
|
||||
|
||||
# The P4 database can become corrupted when it tracks symlinks to
|
||||
# directories. Keep this corruption out of the Mercurial repo.
|
||||
checksymlinks = True
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
|
@ -125,6 +125,45 @@ class ChangeManifestImporter(object):
|
||||
continue
|
||||
info = fileinfo[depotname]
|
||||
localname, baserev = info['localname'], info['baserev']
|
||||
|
||||
if self._ui.configbool('p4fastimport', 'checksymlinks', True):
|
||||
# Under rare situations, when a symlink points to a directory,
|
||||
# the P4 server can report a file "under" it (as if it really
|
||||
# were a directory). 'p4 sync' reports this as an error and
|
||||
# continues, but 'hg update' will abort if it encounters this.
|
||||
# We need to keep such damage out of the hg repository.
|
||||
depotparentname = os.path.dirname(depotname)
|
||||
|
||||
# The manifest's flags for the parent haven't been updated to
|
||||
# reflect this changelist yet. If the parent's flags are
|
||||
# changing right now, use them. Otherwise, use the manifest's
|
||||
# flags.
|
||||
parentflags = None
|
||||
parentinfo = fileinfo.get(depotparentname, None)
|
||||
if parentinfo:
|
||||
parentflags = parentinfo['flags'].get(change.cl, None)
|
||||
|
||||
localparentname = localname
|
||||
while parentflags == None:
|
||||
# This P4 commit didn't change parent's flags at all.
|
||||
# Therefore, we can consult the Hg metadata.
|
||||
localparentname = os.path.dirname(localparentname)
|
||||
if localparentname == '':
|
||||
# There was no parent file above localname; only
|
||||
# directories. That's good/expected.
|
||||
parentflags = ''
|
||||
break
|
||||
parentflags = mf.flags(localparentname, None)
|
||||
|
||||
if 'l' in parentflags:
|
||||
# It turns out that some parent is a symlink, so this file
|
||||
# can't exist. However, we already wrote the filelog! Oh
|
||||
# well. Just don't reference it in the manifest.
|
||||
# TODO: hgfilelog.strip()?
|
||||
self._ui.warn("warning: ignoring {} because it's under a "
|
||||
"symlink ({})\n".format(localname, localparentname))
|
||||
continue
|
||||
|
||||
hgfilelog = self._repo.file(localname)
|
||||
try:
|
||||
mf[localname] = hgfilelog.node(baserev)
|
||||
|
82
tests/test-p4fastimport-import-badsymlink.t
Normal file
82
tests/test-p4fastimport-import-badsymlink.t
Normal file
@ -0,0 +1,82 @@
|
||||
#require p4
|
||||
|
||||
$ . $TESTDIR/p4setup.sh
|
||||
|
||||
populate the depot
|
||||
|
||||
$ mkdir Main
|
||||
$ mkdir Main/b
|
||||
$ echo a > Main/a
|
||||
$ echo c > Main/b/c
|
||||
$ ln -s b Main/d
|
||||
$ p4 add Main/a Main/b/c Main/d
|
||||
//depot/Main/a#1 - opened for add
|
||||
//depot/Main/b/c#1 - opened for add
|
||||
//depot/Main/d#1 - opened for add
|
||||
$ p4 submit -d initial
|
||||
Submitting change 1.
|
||||
Locking 3 files ...
|
||||
add //depot/Main/a#1
|
||||
add //depot/Main/b/c#1
|
||||
add //depot/Main/d#1
|
||||
Change 1 submitted.
|
||||
$ rm Main/d
|
||||
$ mkdir -p Main/d
|
||||
$ echo d > Main/d/d
|
||||
$ p4 add Main/d/d
|
||||
//depot/Main/d/d#1 - opened for add
|
||||
$ p4 submit -d two
|
||||
Submitting change 2.
|
||||
Locking 1 files ...
|
||||
add //depot/Main/d/d#1
|
||||
Change 2 submitted.
|
||||
$ p4 files ...
|
||||
//depot/Main/a#1 - add change 1 (text)
|
||||
//depot/Main/b/c#1 - add change 1 (text)
|
||||
//depot/Main/d#1 - add change 1 (symlink)
|
||||
//depot/Main/d/d#1 - add change 2 (text)
|
||||
|
||||
According to P4, Main/d/d is a file inside Main/d, which is a symlink(!)
|
||||
|
||||
$ cd $hgwd
|
||||
$ hg init --config 'format.usefncache=False'
|
||||
$ hg p4fastimport --bookmark master -P $P4ROOT hg-p4-import
|
||||
warning: ignoring Main/d/d because it's under a symlink (Main/d)
|
||||
$ hg update
|
||||
3 files updated, 0 files merged, 0 files removed, 0 files unresolved
|
||||
$ ls -l Main
|
||||
total 8
|
||||
-rw-r--r-- 1 .* a (re)
|
||||
drwxr-xr-x 2 .* b (re)
|
||||
lrwxrwxrwx 1 .* d -> b (re)
|
||||
|
||||
Repeat with a file nested under several directories
|
||||
|
||||
$ cd $p4wd
|
||||
$ mkdir -p Main/d/e
|
||||
$ echo e > Main/d/e/e
|
||||
$ p4 add Main/d/e/e
|
||||
//depot/Main/d/e/e#1 - opened for add
|
||||
$ p4 submit -d three
|
||||
Submitting change 3.
|
||||
Locking 1 files ...
|
||||
add //depot/Main/d/e/e#1
|
||||
Change 3 submitted.
|
||||
$ p4 files ...
|
||||
//depot/Main/a#1 - add change 1 (text)
|
||||
//depot/Main/b/c#1 - add change 1 (text)
|
||||
//depot/Main/d#1 - add change 1 (symlink)
|
||||
//depot/Main/d/d#1 - add change 2 (text)
|
||||
//depot/Main/d/e/e#1 - add change 3 (text)
|
||||
|
||||
Second import
|
||||
|
||||
$ cd $hgwd
|
||||
$ hg p4fastimport --bookmark master -P $P4ROOT hg-p4-import
|
||||
warning: ignoring Main/d/e/e because it's under a symlink (Main/d)
|
||||
$ hg update
|
||||
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
|
||||
|
||||
End Test
|
||||
|
||||
stopping the p4 server
|
Loading…
Reference in New Issue
Block a user