git-p4: update multiple shelved change lists

--update-shelve can now be specified multiple times on the
command-line, to update multiple shelved changelists in a single
submit.

This then means that a git patch series can be mirrored to a
sequence of shelved changelists, and (relatively easily) kept in
sync as changes are made in git.

Note that Perforce does not really support overlapping shelved
changelists where one change touches the files modified by
another. Trying to do this will result in merge conflicts.

Signed-off-by: Luke Diamand <luke@diamand.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Luke Diamand 2017-12-21 11:06:14 +00:00 committed by Junio C Hamano
parent 936d1b9894
commit 8cf422dbf1
3 changed files with 47 additions and 26 deletions

View File

@ -157,6 +157,12 @@ The p4 changes will be created as the user invoking 'git p4 submit'. The
according to the author of the Git commit. This option requires admin according to the author of the Git commit. This option requires admin
privileges in p4, which can be granted using 'p4 protect'. privileges in p4, which can be granted using 'p4 protect'.
To shelve changes instead of submitting, use `--shelve` and `--update-shelve`:
----
$ git p4 submit --shelve
$ git p4 submit --update-shelve 1234 --update-shelve 2345
----
OPTIONS OPTIONS
------- -------
@ -310,7 +316,7 @@ These options can be used to modify 'git p4 submit' behavior.
--update-shelve CHANGELIST:: --update-shelve CHANGELIST::
Update an existing shelved changelist with this commit. Implies Update an existing shelved changelist with this commit. Implies
--shelve. --shelve. Repeat for multiple shelved changelists.
--conflict=(ask|skip|quit):: --conflict=(ask|skip|quit)::
Conflicts can occur when applying a commit to p4. When this Conflicts can occur when applying a commit to p4. When this

View File

@ -1178,6 +1178,12 @@ class Command:
self.needsGit = True self.needsGit = True
self.verbose = False self.verbose = False
# This is required for the "append" cloneExclude action
def ensure_value(self, attr, value):
if not hasattr(self, attr) or getattr(self, attr) is None:
setattr(self, attr, value)
return getattr(self, attr)
class P4UserMap: class P4UserMap:
def __init__(self): def __init__(self):
self.userMapFromPerforceServer = False self.userMapFromPerforceServer = False
@ -1343,9 +1349,10 @@ class P4Submit(Command, P4UserMap):
optparse.make_option("--shelve", dest="shelve", action="store_true", optparse.make_option("--shelve", dest="shelve", action="store_true",
help="Shelve instead of submit. Shelved files are reverted, " help="Shelve instead of submit. Shelved files are reverted, "
"restoring the workspace to the state before the shelve"), "restoring the workspace to the state before the shelve"),
optparse.make_option("--update-shelve", dest="update_shelve", action="store", type="int", optparse.make_option("--update-shelve", dest="update_shelve", action="append", type="int",
metavar="CHANGELIST", metavar="CHANGELIST",
help="update an existing shelved changelist, implies --shelve") help="update an existing shelved changelist, implies --shelve, "
"repeat in-order for multiple shelved changelists")
] ]
self.description = "Submit changes from git to the perforce depot." self.description = "Submit changes from git to the perforce depot."
self.usage += " [name of git branch to submit into perforce depot]" self.usage += " [name of git branch to submit into perforce depot]"
@ -1354,7 +1361,7 @@ class P4Submit(Command, P4UserMap):
self.preserveUser = gitConfigBool("git-p4.preserveUser") self.preserveUser = gitConfigBool("git-p4.preserveUser")
self.dry_run = False self.dry_run = False
self.shelve = False self.shelve = False
self.update_shelve = None self.update_shelve = list()
self.prepare_p4_only = False self.prepare_p4_only = False
self.conflict_behavior = None self.conflict_behavior = None
self.isWindows = (platform.system() == "Windows") self.isWindows = (platform.system() == "Windows")
@ -1809,9 +1816,10 @@ class P4Submit(Command, P4UserMap):
mode = filesToChangeExecBit[f] mode = filesToChangeExecBit[f]
setP4ExecBit(f, mode) setP4ExecBit(f, mode)
if self.update_shelve: update_shelve = 0
print("all_files = %s" % str(all_files)) if len(self.update_shelve) > 0:
p4_reopen_in_change(self.update_shelve, all_files) update_shelve = self.update_shelve.pop(0)
p4_reopen_in_change(update_shelve, all_files)
# #
# Build p4 change description, starting with the contents # Build p4 change description, starting with the contents
@ -1821,7 +1829,7 @@ class P4Submit(Command, P4UserMap):
logMessage = logMessage.strip() logMessage = logMessage.strip()
(logMessage, jobs) = self.separate_jobs_from_description(logMessage) (logMessage, jobs) = self.separate_jobs_from_description(logMessage)
template = self.prepareSubmitTemplate(self.update_shelve) template = self.prepareSubmitTemplate(update_shelve)
submitTemplate = self.prepareLogMessage(template, logMessage, jobs) submitTemplate = self.prepareLogMessage(template, logMessage, jobs)
if self.preserveUser: if self.preserveUser:
@ -1894,7 +1902,7 @@ class P4Submit(Command, P4UserMap):
message = message.replace("\r\n", "\n") message = message.replace("\r\n", "\n")
submitTemplate = message[:message.index(separatorLine)] submitTemplate = message[:message.index(separatorLine)]
if self.update_shelve: if update_shelve:
p4_write_pipe(['shelve', '-r', '-i'], submitTemplate) p4_write_pipe(['shelve', '-r', '-i'], submitTemplate)
elif self.shelve: elif self.shelve:
p4_write_pipe(['shelve', '-i'], submitTemplate) p4_write_pipe(['shelve', '-i'], submitTemplate)
@ -2012,6 +2020,10 @@ class P4Submit(Command, P4UserMap):
else: else:
return False return False
for i in self.update_shelve:
if i <= 0:
sys.exit("invalid changelist %d" % i)
if self.master: if self.master:
allowSubmit = gitConfig("git-p4.allowSubmit") allowSubmit = gitConfig("git-p4.allowSubmit")
if len(allowSubmit) > 0 and not self.master in allowSubmit.split(","): if len(allowSubmit) > 0 and not self.master in allowSubmit.split(","):
@ -2022,7 +2034,7 @@ class P4Submit(Command, P4UserMap):
if len(self.origin) == 0: if len(self.origin) == 0:
self.origin = upstream self.origin = upstream
if self.update_shelve: if len(self.update_shelve) > 0:
self.shelve = True self.shelve = True
if self.preserveUser: if self.preserveUser:
@ -2134,6 +2146,11 @@ class P4Submit(Command, P4UserMap):
if gitConfigBool("git-p4.detectCopiesHarder"): if gitConfigBool("git-p4.detectCopiesHarder"):
self.diffOpts += " --find-copies-harder" self.diffOpts += " --find-copies-harder"
num_shelves = len(self.update_shelve)
if num_shelves > 0 and num_shelves != len(commits):
sys.exit("number of commits (%d) must match number of shelved changelist (%d)" %
(len(commits), num_shelves))
# #
# Apply the commits, one at a time. On failure, ask if should # Apply the commits, one at a time. On failure, ask if should
# continue to try the rest of the patches, or quit. # continue to try the rest of the patches, or quit.
@ -2404,12 +2421,6 @@ class P4Sync(Command, P4UserMap):
if gitConfig("git-p4.syncFromOrigin") == "false": if gitConfig("git-p4.syncFromOrigin") == "false":
self.syncWithOrigin = False self.syncWithOrigin = False
# This is required for the "append" cloneExclude action
def ensure_value(self, attr, value):
if not hasattr(self, attr) or getattr(self, attr) is None:
setattr(self, attr, value)
return getattr(self, attr)
# Force a checkpoint in fast-import and wait for it to finish # Force a checkpoint in fast-import and wait for it to finish
def checkpoint(self): def checkpoint(self):
self.gitStream.write("checkpoint\n\n") self.gitStream.write("checkpoint\n\n")

View File

@ -460,7 +460,13 @@ test_expect_success 'submit --shelve' '
) )
' '
# Update an existing shelved changelist make_shelved_cl() {
test_commit "$1" >/dev/null &&
git p4 submit --origin HEAD^ --shelve >/dev/null &&
p4 -G changes -s shelved -m 1 | marshal_dump change
}
# Update existing shelved changelists
test_expect_success 'submit --update-shelve' ' test_expect_success 'submit --update-shelve' '
test_when_finished cleanup_git && test_when_finished cleanup_git &&
@ -470,21 +476,19 @@ test_expect_success 'submit --update-shelve' '
p4 revert ... && p4 revert ... &&
cd "$git" && cd "$git" &&
git config git-p4.skipSubmitEdit true && git config git-p4.skipSubmitEdit true &&
test_commit "test-update-shelved-change" && shelved_cl0=$(make_shelved_cl "shelved-change-0") &&
git p4 submit --origin=HEAD^ --shelve && echo shelved_cl0=$shelved_cl0 &&
shelved_cl1=$(make_shelved_cl "shelved-change-1") &&
shelf_cl=$(p4 -G changes -s shelved -m 1 |\ echo "updating shelved change lists $shelved_cl0 and $shelved_cl1" &&
marshal_dump change) &&
test -n $shelf_cl &&
echo "updating shelved change list $shelf_cl" &&
echo "updated-line" >>shelf.t && echo "updated-line" >>shelf.t &&
echo added-file.t >added-file.t && echo added-file.t >added-file.t &&
git add shelf.t added-file.t && git add shelf.t added-file.t &&
git rm -f test-update-shelved-change.t && git rm -f shelved-change-1.t &&
git commit --amend -C HEAD && git commit --amend -C HEAD &&
git show --stat HEAD && git show --stat HEAD &&
git p4 submit -v --origin HEAD^ --update-shelve $shelf_cl && git p4 submit -v --origin HEAD~2 --update-shelve $shelved_cl0 --update-shelve $shelved_cl1 &&
echo "done git p4 submit" echo "done git p4 submit"
) && ) &&
( (
@ -494,7 +498,7 @@ test_expect_success 'submit --update-shelve' '
p4 unshelve -c $change -s $change && p4 unshelve -c $change -s $change &&
grep -q updated-line shelf.t && grep -q updated-line shelf.t &&
p4 describe -S $change | grep added-file.t && p4 describe -S $change | grep added-file.t &&
test_path_is_missing test-update-shelved-change.t test_path_is_missing shelved-change-1.t
) )
' '