git-p4: support explicit sync of arbitrary existing git-p4 refs

With the --branch argument of the "sync" subcommand, git-p4 enables
you to import a perforce branch/path to an arbitrary git ref, using
a full ref path, or to refs/remotes/p4/* or refs/heads/p4/*,
depending on --import-local, using a short ref name.

However, when you later want to explicitly sync such a given ref to
pick up subsequent p4 changes, it only works if the ref was placed
in the p4 path *and* has only one path component (no "/").

This limitation results from a bad assumption in the
existing-branch sync logic, and also means you cannot individually
sync branches detected by --detect-branches, as these also get a
"/" in their names.

Fix "git p4 sync --branch", when called with an existing ref, so
that it works correctly regardless of whether the ref is in the p4
path or not, and (in the case of refs in the p4 path) regardless of
whether it has a "/" in its short name or not.

Also add tests to validate that these branch-specific syncs work
as expected.

Signed-off-by: Tao Klerks <tao@klerks.biz>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Tao Klerks 2022-04-04 05:10:54 +00:00 committed by Junio C Hamano
parent faa21c10d4
commit 17f273ffba
3 changed files with 133 additions and 14 deletions

View File

@ -893,6 +893,36 @@ def gitConfigList(key):
_gitConfig[key] = []
return _gitConfig[key]
def fullP4Ref(incomingRef, importIntoRemotes=True):
"""Standardize a given provided p4 ref value to a full git ref:
refs/foo/bar/branch -> use it exactly
p4/branch -> prepend refs/remotes/ or refs/heads/
branch -> prepend refs/remotes/p4/ or refs/heads/p4/"""
if incomingRef.startswith("refs/"):
return incomingRef
if importIntoRemotes:
prepend = "refs/remotes/"
else:
prepend = "refs/heads/"
if not incomingRef.startswith("p4/"):
prepend += "p4/"
return prepend + incomingRef
def shortP4Ref(incomingRef, importIntoRemotes=True):
"""Standardize to a "short ref" if possible:
refs/foo/bar/branch -> ignore
refs/remotes/p4/branch or refs/heads/p4/branch -> shorten
p4/branch -> shorten"""
if importIntoRemotes:
longprefix = "refs/remotes/p4/"
else:
longprefix = "refs/heads/p4/"
if incomingRef.startswith(longprefix):
return incomingRef[len(longprefix):]
if incomingRef.startswith("p4/"):
return incomingRef[3:]
return incomingRef
def p4BranchesInGit(branchesAreInRemotes=True):
"""Find all the branches whose names start with "p4/", looking
in remotes or heads as specified by the argument. Return
@ -3750,9 +3780,13 @@ class P4Sync(Command, P4UserMap):
# restrict to just this one, disabling detect-branches
if branch_arg_given:
short = self.branch.split("/")[-1]
short = shortP4Ref(self.branch, self.importIntoRemotes)
if short in branches:
self.p4BranchesInGit = [ short ]
elif self.branch.startswith('refs/') and \
branchExists(self.branch) and \
'[git-p4:' in extractLogMessageFromGitCommit(self.branch):
self.p4BranchesInGit = [ self.branch ]
else:
self.p4BranchesInGit = branches.keys()
@ -3769,7 +3803,8 @@ class P4Sync(Command, P4UserMap):
p4Change = 0
for branch in self.p4BranchesInGit:
logMsg = extractLogMessageFromGitCommit(self.refPrefix + branch)
logMsg = extractLogMessageFromGitCommit(fullP4Ref(branch,
self.importIntoRemotes))
settings = extractSettingsGitLog(logMsg)
@ -3802,18 +3837,7 @@ class P4Sync(Command, P4UserMap):
if not self.silent and not self.detectBranches:
print("Performing incremental import into %s git branch" % self.branch)
# accept multiple ref name abbreviations:
# refs/foo/bar/branch -> use it exactly
# p4/branch -> prepend refs/remotes/ or refs/heads/
# branch -> prepend refs/remotes/p4/ or refs/heads/p4/
if not self.branch.startswith("refs/"):
if self.importIntoRemotes:
prepend = "refs/remotes/"
else:
prepend = "refs/heads/"
if not self.branch.startswith("p4/"):
prepend += "p4/"
self.branch = prepend + self.branch
self.branch = fullP4Ref(self.branch, self.importIntoRemotes)
if len(args) == 0 and self.depotPaths:
if not self.silent:

View File

@ -74,6 +74,91 @@ test_expect_success 'git p4 sync new branch' '
)
'
#
# Setup as before, and then explicitly sync imported branch, using a
# different ref format.
#
test_expect_success 'git p4 sync existing branch without changes' '
test_create_repo "$git" &&
test_when_finished cleanup_git &&
(
cd "$git" &&
test_commit head &&
git p4 sync --branch=depot //depot@all &&
git p4 sync --branch=refs/remotes/p4/depot >out &&
test_i18ngrep "No changes to import!" out
)
'
#
# Same as before, relative branch name.
#
test_expect_success 'git p4 sync existing branch with relative name' '
test_create_repo "$git" &&
test_when_finished cleanup_git &&
(
cd "$git" &&
test_commit head &&
git p4 sync --branch=branch1 //depot@all &&
git p4 sync --branch=p4/branch1 >out &&
test_i18ngrep "No changes to import!" out
)
'
#
# Same as before, with a nested branch path, referenced different ways.
#
test_expect_success 'git p4 sync existing branch with nested path' '
test_create_repo "$git" &&
test_when_finished cleanup_git &&
(
cd "$git" &&
test_commit head &&
git p4 sync --branch=p4/some/path //depot@all &&
git p4 sync --branch=some/path >out &&
test_i18ngrep "No changes to import!" out
)
'
#
# Same as before, with a full ref path outside the p4/* namespace.
#
test_expect_success 'git p4 sync branch explicit ref without p4 in path' '
test_create_repo "$git" &&
test_when_finished cleanup_git &&
(
cd "$git" &&
test_commit head &&
git p4 sync --branch=refs/remotes/someremote/depot //depot@all &&
git p4 sync --branch=refs/remotes/someremote/depot >out &&
test_i18ngrep "No changes to import!" out
)
'
test_expect_success 'git p4 sync nonexistent ref' '
test_create_repo "$git" &&
test_when_finished cleanup_git &&
(
cd "$git" &&
test_commit head &&
git p4 sync --branch=depot //depot@all &&
test_must_fail git p4 sync --branch=depot2 2>errs &&
test_i18ngrep "Perhaps you never did" errs
)
'
test_expect_success 'git p4 sync existing non-p4-imported ref' '
test_create_repo "$git" &&
test_when_finished cleanup_git &&
(
cd "$git" &&
test_commit head &&
git p4 sync --branch=depot //depot@all &&
test_must_fail git p4 sync --branch=refs/heads/master 2>errs &&
test_i18ngrep "Perhaps you never did" errs
)
'
test_expect_success 'clone two dirs' '
(
cd "$cli" &&

View File

@ -129,6 +129,16 @@ test_expect_success 'import depot, branch detection' '
)
'
test_expect_success 'sync specific detected branch' '
test_when_finished cleanup_git &&
git p4 clone --dest="$git" --detect-branches //depot@all &&
(
cd "$git" &&
git p4 sync --branch=depot/branch2 >out &&
test_i18ngrep "No changes to import!" out
)
'
test_expect_success 'import depot, branch detection, branchList branch definition' '
test_when_finished cleanup_git &&
test_create_repo "$git" &&