From 8c2913508644bb3ef511c7edac1b15cfafe61df5 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Sun, 9 Sep 2012 16:16:02 -0400 Subject: [PATCH 01/12] git p4 test: remove bash-ism of combined export/assignment Signed-off-by: Pete Wyckoff Acked-by: Luke Diamand Signed-off-by: Junio C Hamano --- t/lib-git-p4.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/t/lib-git-p4.sh b/t/lib-git-p4.sh index 2d753ab7e118e0..482eeac4d9b0d6 100644 --- a/t/lib-git-p4.sh +++ b/t/lib-git-p4.sh @@ -26,9 +26,10 @@ testid=${this_test#t} git_p4_test_start=9800 P4DPORT=$((10669 + ($testid - $git_p4_test_start))) -export P4PORT=localhost:$P4DPORT -export P4CLIENT=client -export P4EDITOR=: +P4PORT=localhost:$P4DPORT +P4CLIENT=client +P4EDITOR=: +export P4PORT P4CLIENT P4EDITOR db="$TRASH_DIRECTORY/db" cli=$(test-path-utils real_path "$TRASH_DIRECTORY/cli") From 67b0fe2eb6b70f530be347260c3792509a900feb Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Sun, 9 Sep 2012 16:16:03 -0400 Subject: [PATCH 02/12] git p4: gracefully fail if some commits could not be applied If a commit fails to apply cleanly to the p4 tree, an interactive prompt asks what to do next. In all cases (skip, apply, write), the behavior after the prompt had a few problems. Change it so that it does not claim erroneously that all commits were applied. Instead list the set of the patches under consideration, and mark with an asterisk those that were applied successfully. Like this example: Applying 592f1f9 line5 in file1 will conflict ... Unfortunately applying the change failed! What do you want to do? [s]kip this patch / [a]pply the patch forcibly and with .rej files / [w]rite the patch to a file (patch.txt) s Skipping! Good luck with the next patches... //depot/file1#4 - was edit, reverted Applying b8db1c6 okay_commit_after_skip ... Change 6 submitted. Applied only the commits marked with '*': 592f1f9 line5 in file1 will conflict * b8db1c6 okay_commit_after_skip Do not try to sync and rebase unless all patches were applied. If there was a conflict during the submit, there is sure to be one at the rebase. Let the user to do the sync and rebase manually. This changes how a couple tets in t9810-git-p4-rcs.sh behave: - git p4 now does not leave files open and edited in the client - If a git commit contains a change to a file that was deleted in p4, the test used to check that the sync/rebase loop happened after the failure to apply the change. Since now sync/rebase does not happen after failure, do not test this. Normal rebase machinery, outside of git p4, will let rebase --skip work. Signed-off-by: Pete Wyckoff Acked-by: Luke Diamand Signed-off-by: Junio C Hamano --- git-p4.py | 42 ++++++++++++---- t/t9810-git-p4-rcs.sh | 50 ++----------------- t/t9815-git-p4-submit-fail.sh | 92 +++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 56 deletions(-) create mode 100755 t/t9815-git-p4-submit-fail.sh diff --git a/git-p4.py b/git-p4.py index e67d37d2f93361..2405f383e1907a 100755 --- a/git-p4.py +++ b/git-p4.py @@ -1088,7 +1088,10 @@ def edit_template(self, template_file): return False def applyCommit(self, id): - print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id)) + """Apply one commit, return True if it succeeded.""" + + print "Applying", read_pipe(["git", "show", "-s", + "--format=format:%h %s", id]) (p4User, gitEmail) = self.p4UserForCommit(id) @@ -1206,7 +1209,7 @@ def applyCommit(self, id): p4_revert(f) for f in filesToAdd: os.remove(f) - return + return False elif response == "a": os.system(applyPatchCmd) if len(filesToAdd) > 0: @@ -1312,6 +1315,7 @@ def applyCommit(self, id): os.remove(f) os.remove(fileName) + return True # success # Export git tags as p4 labels. Create a p4 label and then tag # with that. @@ -1487,14 +1491,16 @@ def run(self, args): if gitConfig("git-p4.detectCopiesHarder", "--bool") == "true": self.diffOpts += " --find-copies-harder" - while len(commits) > 0: - commit = commits[0] - commits = commits[1:] - self.applyCommit(commit) + applied = [] + for commit in commits: + ok = self.applyCommit(commit) + if ok: + applied.append(commit) - if len(commits) == 0: - print "All changes applied!" - chdir(self.oldWorkingDirectory) + chdir(self.oldWorkingDirectory) + + if len(commits) == len(applied): + print "All commits applied!" sync = P4Sync() sync.run([]) @@ -1502,6 +1508,20 @@ def run(self, args): rebase = P4Rebase() rebase.rebase() + else: + if len(applied) == 0: + print "No commits applied." + else: + print "Applied only the commits marked with '*':" + for c in commits: + if c in applied: + star = "*" + else: + star = " " + print star, read_pipe(["git", "show", "-s", + "--format=format:%h %s", c]) + print "You will have to do 'git p4 sync' and rebase." + if gitConfig("git-p4.exportLabels", "--bool") == "true": self.exportLabels = True @@ -1512,6 +1532,10 @@ def run(self, args): missingGitTags = gitTags - p4Labels self.exportGitTags(missingGitTags) + # exit with error unless everything applied perfecly + if len(commits) != len(applied): + sys.exit(1) + return True class View(object): diff --git a/t/t9810-git-p4-rcs.sh b/t/t9810-git-p4-rcs.sh index e9daa9c4f6bab4..fe30ad881f67c9 100755 --- a/t/t9810-git-p4-rcs.sh +++ b/t/t9810-git-p4-rcs.sh @@ -160,9 +160,6 @@ test_expect_success 'cleanup after failure' ' # the cli file so that submit will get a conflict. Make sure that # scrubbing doesn't make a mess of things. # -# Assumes that git-p4 exits leaving the p4 file open, with the -# conflict-generating patch unapplied. -# # This might happen only if the git repo is behind the p4 repo at # submit time, and there is a conflict. # @@ -181,14 +178,11 @@ test_expect_success 'do not scrub plain text' ' sed -i "s/^line5/line5 p4 edit/" file_text && p4 submit -d "file5 p4 edit" ) && - ! git p4 submit && + echo s | test_expect_code 1 git p4 submit && ( - # exepct something like: - # file_text - file(s) not opened on this client - # but not copious diff output + # make sure the file is not left open cd "$cli" && - p4 diff file_text >wc && - test_line_count = 1 wc + ! p4 fstat -T action file_text ) ) ' @@ -343,44 +337,6 @@ test_expect_failure 'Add keywords in git which do not match the default p4 value ) ' -# Check that the existing merge conflict handling still works. -# Modify kwfile1.c in git, and delete in p4. We should be able -# to skip the git commit. -# -test_expect_success 'merge conflict handling still works' ' - test_when_finished cleanup_git && - ( - cd "$cli" && - echo "Hello:\$Id\$" >merge2.c && - echo "World" >>merge2.c && - p4 add -t ktext merge2.c && - p4 submit -d "add merge test file" - ) && - git p4 clone --dest="$git" //depot && - ( - cd "$git" && - sed -e "/Hello/d" merge2.c >merge2.c.tmp && - mv merge2.c.tmp merge2.c && - git add merge2.c && - git commit -m "Modifying merge2.c" - ) && - ( - cd "$cli" && - p4 delete merge2.c && - p4 submit -d "remove merge test file" - ) && - ( - cd "$git" && - test -f merge2.c && - git config git-p4.skipSubmitEdit true && - git config git-p4.attemptRCSCleanup true && - !(echo "s" | git p4 submit) && - git rebase --skip && - ! test -f merge2.c - ) -' - - test_expect_success 'kill p4d' ' kill_p4d ' diff --git a/t/t9815-git-p4-submit-fail.sh b/t/t9815-git-p4-submit-fail.sh new file mode 100755 index 00000000000000..5c367141ce5083 --- /dev/null +++ b/t/t9815-git-p4-submit-fail.sh @@ -0,0 +1,92 @@ +#!/bin/sh + +test_description='git p4 submit failure handling' + +. ./lib-git-p4.sh + +test_expect_success 'start p4d' ' + start_p4d +' + +test_expect_success 'init depot' ' + ( + cd "$cli" && + p4 client -o | sed "/LineEnd/s/:.*/:unix/" | p4 client -i && + echo line1 >file1 && + p4 add file1 && + p4 submit -d "line1 in file1" + ) +' + +test_expect_success 'conflict on one commit, skip' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$cli" && + p4 open file1 && + echo line2 >>file1 && + p4 submit -d "line2 in file1" + ) && + ( + # now this commit should cause a conflict + cd "$git" && + git config git-p4.skipSubmitEdit true && + echo line3 >>file1 && + git add file1 && + git commit -m "line3 in file1 will conflict" && + echo s | test_expect_code 1 git p4 submit >out && + test_i18ngrep "No commits applied" out + ) +' + +test_expect_success 'conflict on second of two commits, skip' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$cli" && + p4 open file1 && + echo line3 >>file1 && + p4 submit -d "line3 in file1" + ) && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + # this commit is okay + test_commit "first_commit_okay" && + # now this submit should cause a conflict + echo line4 >>file1 && + git add file1 && + git commit -m "line4 in file1 will conflict" && + echo s | test_expect_code 1 git p4 submit >out && + test_i18ngrep "Applied only the commits" out + ) +' + +test_expect_success 'conflict on first of two commits, skip' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$cli" && + p4 open file1 && + echo line4 >>file1 && + p4 submit -d "line4 in file1" + ) && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + # this submit should cause a conflict + echo line5 >>file1 && + git add file1 && + git commit -m "line5 in file1 will conflict" && + # but this commit is okay + test_commit "okay_commit_after_skip" && + echo s | test_expect_code 1 git p4 submit >out && + test_i18ngrep "Applied only the commits" out + ) +' + +test_expect_success 'kill p4d' ' + kill_p4d +' + +test_done From 449bb9cf1a0c42ddf6e9b9b6fd447eda12f9bc92 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Sun, 9 Sep 2012 16:16:04 -0400 Subject: [PATCH 03/12] git p4: remove submit failure options [a]pply and [w]rite When a patch failed to apply, these interactive options offered to: 1) apply the patch anyway, leaving reject (.rej) files around, or, 2) write the patch to a file (patch.txt) In both cases it suggested to invoke "git p4 submit --continue", an unimplemented option. While manually fixing the rejects and submitting the result might work, there are many steps that must be done to the job properly: * apply patch * invoke p4 add and delete * change executable bits * p4 sync -f renamed/copied files * extract commit message into p4 change description and move Jobs lines out of description section * set changelist owner for --preserve-user Plus the following manual sync/rebase will cause conflicts too, which must be resolved once again. Drop these workflows. Instead users should do a sync/rebase in git, fix the conflicts there, and do a clean "git p4 submit". Signed-off-by: Pete Wyckoff Acked-by: Luke Diamand Signed-off-by: Junio C Hamano --- git-p4.py | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/git-p4.py b/git-p4.py index 2405f383e1907a..e08fea18d22dff 100755 --- a/git-p4.py +++ b/git-p4.py @@ -1200,9 +1200,8 @@ def applyCommit(self, id): if not patch_succeeded: print "What do you want to do?" response = "x" - while response != "s" and response != "a" and response != "w": - response = raw_input("[s]kip this patch / [a]pply the patch forcibly " - "and with .rej files / [w]rite the patch to a file (patch.txt) ") + while response != "s": + response = raw_input("[s]kip this patch ") if response == "s": print "Skipping! Good luck with the next patches..." for f in editedFiles: @@ -1210,21 +1209,6 @@ def applyCommit(self, id): for f in filesToAdd: os.remove(f) return False - elif response == "a": - os.system(applyPatchCmd) - if len(filesToAdd) > 0: - print "You may also want to call p4 add on the following files:" - print " ".join(filesToAdd) - if len(filesToDelete): - print "The following files should be scheduled for deletion with p4 delete:" - print " ".join(filesToDelete) - die("Please resolve and submit the conflict manually and " - + "continue afterwards with git p4 submit --continue") - elif response == "w": - system(diffcmd + " > patch.txt") - print "Patch saved to patch.txt in %s !" % self.clientPath - die("Please resolve and submit the conflict manually and " - "continue afterwards with git p4 submit --continue") system(applyPatchCmd) From 7e5dd9f2ccf2141d65e4621b765a136796b0d55b Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Sun, 9 Sep 2012 16:16:05 -0400 Subject: [PATCH 04/12] git p4: move conflict prompt into run, add [q]uit input When applying a commit to the p4 workspace fails, a prompt asks what to do next. This belongs up in run() instead of in applyCommit(), where run() can notice, for instance, that the prompt is unnecessary because this is the last commit. Offer two options about how to continue at conflict: [s]kip or [q]uit. Having an explicit "quit" option gives git p4 a chance to clean up, show the applied-commit summary, and do tag export. Signed-off-by: Pete Wyckoff Acked-by: Luke Diamand Signed-off-by: Junio C Hamano --- git-p4.py | 41 +++++++++++++++++++++++++---------- t/t9815-git-p4-submit-fail.sh | 31 ++++++++++++++++++++++---- 2 files changed, 56 insertions(+), 16 deletions(-) diff --git a/git-p4.py b/git-p4.py index e08fea18d22dff..479f1fc7d29b3e 100755 --- a/git-p4.py +++ b/git-p4.py @@ -1198,17 +1198,11 @@ def applyCommit(self, id): patch_succeeded = True if not patch_succeeded: - print "What do you want to do?" - response = "x" - while response != "s": - response = raw_input("[s]kip this patch ") - if response == "s": - print "Skipping! Good luck with the next patches..." - for f in editedFiles: - p4_revert(f) - for f in filesToAdd: - os.remove(f) - return False + for f in editedFiles: + p4_revert(f) + for f in filesToAdd: + os.remove(f) + return False system(applyPatchCmd) @@ -1475,11 +1469,34 @@ def run(self, args): if gitConfig("git-p4.detectCopiesHarder", "--bool") == "true": self.diffOpts += " --find-copies-harder" + # + # Apply the commits, one at a time. On failure, ask if should + # continue to try the rest of the patches, or quit. + # applied = [] - for commit in commits: + last = len(commits) - 1 + for i, commit in enumerate(commits): ok = self.applyCommit(commit) if ok: applied.append(commit) + else: + if i < last: + quit = False + while True: + print "What do you want to do?" + response = raw_input("[s]kip this commit but apply" + " the rest, or [q]uit? ") + if not response: + continue + if response[0] == "s": + print "Skipping this commit, but applying the rest" + break + if response[0] == "q": + print "Quitting" + quit = True + break + if quit: + break chdir(self.oldWorkingDirectory) diff --git a/t/t9815-git-p4-submit-fail.sh b/t/t9815-git-p4-submit-fail.sh index 5c367141ce5083..397b3e857da179 100755 --- a/t/t9815-git-p4-submit-fail.sh +++ b/t/t9815-git-p4-submit-fail.sh @@ -18,7 +18,7 @@ test_expect_success 'init depot' ' ) ' -test_expect_success 'conflict on one commit, skip' ' +test_expect_success 'conflict on one commit' ' test_when_finished cleanup_git && git p4 clone --dest="$git" //depot && ( @@ -34,12 +34,12 @@ test_expect_success 'conflict on one commit, skip' ' echo line3 >>file1 && git add file1 && git commit -m "line3 in file1 will conflict" && - echo s | test_expect_code 1 git p4 submit >out && + test_expect_code 1 git p4 submit >out && test_i18ngrep "No commits applied" out ) ' -test_expect_success 'conflict on second of two commits, skip' ' +test_expect_success 'conflict on second of two commits' ' test_when_finished cleanup_git && git p4 clone --dest="$git" //depot && ( @@ -57,7 +57,7 @@ test_expect_success 'conflict on second of two commits, skip' ' echo line4 >>file1 && git add file1 && git commit -m "line4 in file1 will conflict" && - echo s | test_expect_code 1 git p4 submit >out && + test_expect_code 1 git p4 submit >out && test_i18ngrep "Applied only the commits" out ) ' @@ -85,6 +85,29 @@ test_expect_success 'conflict on first of two commits, skip' ' ) ' +test_expect_success 'conflict on first of two commits, quit' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$cli" && + p4 open file1 && + echo line7 >>file1 && + p4 submit -d "line7 in file1" + ) && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + # this submit should cause a conflict + echo line8 >>file1 && + git add file1 && + git commit -m "line8 in file1 will conflict" && + # but this commit is okay + test_commit "okay_commit_after_quit" && + echo q | test_expect_code 1 git p4 submit >out && + test_i18ngrep "No commits applied" out + ) +' + test_expect_success 'kill p4d' ' kill_p4d ' From 5a41c16a81927bbccbcb00895b168a6d560b8c2c Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Sun, 9 Sep 2012 16:16:06 -0400 Subject: [PATCH 05/12] git p4: standardize submit cancel due to unchanged template When editing the submit template, if no change was made to it, git p4 offers a prompt "Submit anyway?". Answering "no" cancels the submit. Previously, a "no" answer behaves like a "[s]kip" answer to the failed-patch prompt, in that it proceeded to try to apply the rest of the commits. Instead, put users back into the new "[s]kip / [c]ontinue" loop so that they can decide. This makes both cases of patch failure behave identically. The return code of git p4 after a "no" answer is now the same as that for a "skip" due to failed patch; update a test to understand this. Signed-off-by: Pete Wyckoff Acked-by: Luke Diamand Signed-off-by: Junio C Hamano --- git-p4.py | 4 +++- t/t9805-git-p4-skip-submit-edit.sh | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/git-p4.py b/git-p4.py index 479f1fc7d29b3e..39fa2e186f04c9 100755 --- a/git-p4.py +++ b/git-p4.py @@ -1262,6 +1262,7 @@ def applyCommit(self, id): if self.edit_template(fileName): # read the edited message and submit + ret = True tmpFile = open(fileName, "rb") message = tmpFile.read() tmpFile.close() @@ -1285,6 +1286,7 @@ def applyCommit(self, id): else: # skip this patch + ret = False print "Submission cancelled, undoing p4 changes." for f in editedFiles: p4_revert(f) @@ -1293,7 +1295,7 @@ def applyCommit(self, id): os.remove(f) os.remove(fileName) - return True # success + return ret # Export git tags as p4 labels. Create a p4 label and then tag # with that. diff --git a/t/t9805-git-p4-skip-submit-edit.sh b/t/t9805-git-p4-skip-submit-edit.sh index fb3c8ec12c7a37..ff2cc79701cce5 100755 --- a/t/t9805-git-p4-skip-submit-edit.sh +++ b/t/t9805-git-p4-skip-submit-edit.sh @@ -38,7 +38,7 @@ test_expect_success 'no config, unedited, say no' ' cd "$git" && echo line >>file1 && git commit -a -m "change 3 (not really)" && - printf "bad response\nn\n" | git p4 submit && + printf "bad response\nn\n" | test_expect_code 1 git p4 submit && p4 changes //depot/... >wc && test_line_count = 2 wc ) From f7fbc981a4effde51446c2229e34f2fc9210d31d Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Sun, 9 Sep 2012 16:16:07 -0400 Subject: [PATCH 06/12] git p4: test clean-up after failed submit, fix added files Test a variety of cases where a patch failed to apply to p4 and had to be cleaned up. If the patch failed to apply cleanly, do not try to remove to-be-added files, as they have not really been added yet. Signed-off-by: Pete Wyckoff Acked-by: Luke Diamand Signed-off-by: Junio C Hamano --- git-p4.py | 2 - t/t9815-git-p4-submit-fail.sh | 132 ++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 2 deletions(-) diff --git a/git-p4.py b/git-p4.py index 39fa2e186f04c9..0481f432f23431 100755 --- a/git-p4.py +++ b/git-p4.py @@ -1200,8 +1200,6 @@ def applyCommit(self, id): if not patch_succeeded: for f in editedFiles: p4_revert(f) - for f in filesToAdd: - os.remove(f) return False system(applyPatchCmd) diff --git a/t/t9815-git-p4-submit-fail.sh b/t/t9815-git-p4-submit-fail.sh index 397b3e857da179..e73d6ab6b2b7ab 100755 --- a/t/t9815-git-p4-submit-fail.sh +++ b/t/t9815-git-p4-submit-fail.sh @@ -108,6 +108,138 @@ test_expect_success 'conflict on first of two commits, quit' ' ) ' +# +# Cleanup after submit fail, all cases. Some modifications happen +# before trying to apply the patch. Make sure these are unwound +# properly. Put each one in a diff along with something that will +# obviously conflict. Make sure it is back to normal after. +# + +test_expect_success 'cleanup edit p4 populate' ' + ( + cd "$cli" && + echo text file >text && + p4 add text && + echo text+x file >text+x && + chmod 755 text+x && + p4 add text+x && + p4 submit -d "populate p4" + ) +' + +setup_conflict() { + # clone before modifying file1 to force it to conflict + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + # ticks outside subshells + test_tick && + ( + cd "$cli" && + p4 open file1 && + echo $test_tick >>file1 && + p4 submit -d "$test_tick in file1" + ) && + test_tick && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + # easy conflict + echo $test_tick >>file1 && + git add file1 + # caller will add more and submit + ) +} + +test_expect_success 'cleanup edit after submit fail' ' + setup_conflict && + ( + cd "$git" && + echo another line >>text && + git add text && + git commit -m "conflict" && + test_expect_code 1 git p4 submit + ) && + ( + cd "$cli" && + # make sure it is not open + ! p4 fstat -T action text + ) +' + +test_expect_success 'cleanup add after submit fail' ' + setup_conflict && + ( + cd "$git" && + echo new file >textnew && + git add textnew && + git commit -m "conflict" && + test_expect_code 1 git p4 submit + ) && + ( + cd "$cli" && + # make sure it is not there + # and that p4 thinks it is not added + # P4 returns 0 both for "not there but added" and + # "not there", so grep. + test_path_is_missing textnew && + p4 fstat -T action textnew 2>&1 | grep "no such file" + ) +' + +test_expect_success 'cleanup delete after submit fail' ' + setup_conflict && + ( + cd "$git" && + git rm text+x && + git commit -m "conflict" && + test_expect_code 1 git p4 submit + ) && + ( + cd "$cli" && + # make sure it is there + test_path_is_file text+x && + ! p4 fstat -T action text+x + ) +' + +test_expect_success 'cleanup copy after submit fail' ' + setup_conflict && + ( + cd "$git" && + cp text text2 && + git add text2 && + git commit -m "conflict" && + git config git-p4.detectCopies true && + git config git-p4.detectCopiesHarder true && + # make sure setup is okay + git diff-tree -r -C --find-copies-harder HEAD | grep text2 | grep C100 && + test_expect_code 1 git p4 submit + ) && + ( + cd "$cli" && + test_path_is_missing text2 && + p4 fstat -T action text2 2>&1 | grep "no such file" + ) +' + +test_expect_success 'cleanup rename after submit fail' ' + setup_conflict && + ( + cd "$git" && + git mv text text2 && + git commit -m "conflict" && + git config git-p4.detectRenames true && + # make sure setup is okay + git diff-tree -r -M HEAD | grep text2 | grep R100 && + test_expect_code 1 git p4 submit + ) && + ( + cd "$cli" && + test_path_is_missing text2 && + p4 fstat -T action text2 2>&1 | grep "no such file" + ) +' + test_expect_success 'kill p4d' ' kill_p4d ' From 55ac2ed6f56c153bc71dc80eb7634e2df2747670 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Sun, 9 Sep 2012 16:16:08 -0400 Subject: [PATCH 07/12] git p4: rearrange submit template construction Put all items in order as they appear, and add comments. Signed-off-by: Pete Wyckoff Acked-by: Luke Diamand Signed-off-by: Junio C Hamano --- git-p4.py | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/git-p4.py b/git-p4.py index 0481f432f23431..ea14c1f633ba04 100755 --- a/git-p4.py +++ b/git-p4.py @@ -1202,6 +1202,9 @@ def applyCommit(self, id): p4_revert(f) return False + # + # Apply the patch for real, and do add/delete/+x handling. + # system(applyPatchCmd) for f in filesToAdd: @@ -1215,6 +1218,10 @@ def applyCommit(self, id): mode = filesToChangeExecBit[f] setP4ExecBit(f, mode) + # + # Build p4 change description, starting with the contents + # of the git commit message. + # logMessage = extractLogMessageFromGitCommit(id) logMessage = logMessage.strip() (logMessage, jobs) = self.separate_jobs_from_description(logMessage) @@ -1223,8 +1230,16 @@ def applyCommit(self, id): submitTemplate = self.prepareLogMessage(template, logMessage, jobs) if self.preserveUser: - submitTemplate = submitTemplate + ("\n######## Actual user %s, modified after commit\n" % p4User) + submitTemplate += "\n######## Actual user %s, modified after commit\n" % p4User + + if self.checkAuthorship and not self.p4UserIsMe(p4User): + submitTemplate += "######## git author %s does not match your p4 account.\n" % gitEmail + submitTemplate += "######## Use option --preserve-user to modify authorship.\n" + submitTemplate += "######## Variable git-p4.skipUserNameCheck hides this message.\n" + + separatorLine = "######## everything below this line is just the diff #######\n" + # diff if os.environ.has_key("P4DIFF"): del(os.environ["P4DIFF"]) diff = "" @@ -1232,6 +1247,7 @@ def applyCommit(self, id): diff += p4_read_pipe(['diff', '-du', wildcard_encode(editedFile)]) + # new file diff newdiff = "" for newFile in filesToAdd: newdiff += "==== new file ====\n" @@ -1242,13 +1258,7 @@ def applyCommit(self, id): newdiff += "+" + line f.close() - if self.checkAuthorship and not self.p4UserIsMe(p4User): - submitTemplate += "######## git author %s does not match your p4 account.\n" % gitEmail - submitTemplate += "######## Use option --preserve-user to modify authorship.\n" - submitTemplate += "######## Variable git-p4.skipUserNameCheck hides this message.\n" - - separatorLine = "######## everything below this line is just the diff #######\n" - + # change description file: submitTemplate, separatorLine, diff, newdiff (handle, fileName) = tempfile.mkstemp() tmpFile = os.fdopen(handle, "w+") if self.isWindows: @@ -1258,6 +1268,9 @@ def applyCommit(self, id): tmpFile.write(submitTemplate + separatorLine + diff + newdiff) tmpFile.close() + # + # Let the user edit the change description, then submit it. + # if self.edit_template(fileName): # read the edited message and submit ret = True From df9c5453b220923d7ec3e6291498d519596e23ed Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Sun, 9 Sep 2012 16:16:09 -0400 Subject: [PATCH 08/12] git p4: revert deleted files after submit cancel The user can decide not to continue with a submission, by not saving the p4 submit template, then answering "no" to the "Submit anyway?" prompt. In this case, be sure to return the p4 client to its initial state. Deleted files were not reverted; fix this and test all cases. Signed-off-by: Pete Wyckoff Acked-by: Luke Diamand Signed-off-by: Junio C Hamano --- git-p4.py | 2 + t/t9815-git-p4-submit-fail.sh | 119 ++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/git-p4.py b/git-p4.py index ea14c1f633ba04..60012bc63cad67 100755 --- a/git-p4.py +++ b/git-p4.py @@ -1304,6 +1304,8 @@ def applyCommit(self, id): for f in filesToAdd: p4_revert(f) os.remove(f) + for f in filesToDelete: + p4_revert(f) os.remove(fileName) return ret diff --git a/t/t9815-git-p4-submit-fail.sh b/t/t9815-git-p4-submit-fail.sh index e73d6ab6b2b7ab..1823cff12bf6b4 100755 --- a/t/t9815-git-p4-submit-fail.sh +++ b/t/t9815-git-p4-submit-fail.sh @@ -240,6 +240,125 @@ test_expect_success 'cleanup rename after submit fail' ' ) ' +# +# Cleanup after deciding not to submit during editTemplate. This +# involves unwinding more work, because files have been added, deleted +# and chmod-ed now. Same approach as above. +# + +test_expect_success 'cleanup edit after submit cancel' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + echo line >>text && + git add text && + git commit -m text && + echo n | test_expect_code 1 git p4 submit && + git reset --hard HEAD^ + ) && + ( + cd "$cli" && + ! p4 fstat -T action text && + test_cmp "$git"/text text + ) +' + +test_expect_success 'cleanup add after submit cancel' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + echo line >textnew && + git add textnew && + git commit -m textnew && + echo n | test_expect_code 1 git p4 submit + ) && + ( + cd "$cli" && + test_path_is_missing textnew && + p4 fstat -T action textnew 2>&1 | grep "no such file" + ) +' + +test_expect_success 'cleanup delete after submit cancel' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git rm text && + git commit -m "rm text" && + echo n | test_expect_code 1 git p4 submit + ) && + ( + cd "$cli" && + test_path_is_file text && + ! p4 fstat -T action text + ) +' + +test_expect_success 'cleanup copy after submit cancel' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + cp text text2 && + git add text2 && + git commit -m text2 && + git config git-p4.detectCopies true && + git config git-p4.detectCopiesHarder true && + git diff-tree -r -C --find-copies-harder HEAD | grep text2 | grep C100 && + echo n | test_expect_code 1 git p4 submit + ) && + ( + cd "$cli" && + test_path_is_missing text2 && + p4 fstat -T action text2 2>&1 | grep "no such file" + ) +' + +test_expect_success 'cleanup rename after submit cancel' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git mv text text2 && + git commit -m text2 && + git config git-p4.detectRenames true && + git diff-tree -r -M HEAD | grep text2 | grep R100 && + echo n | test_expect_code 1 git p4 submit + ) && + ( + cd "$cli" && + test_path_is_missing text2 && + p4 fstat -T action text2 2>&1 | grep "no such file" + test_path_is_file text && + ! p4 fstat -T action text + ) +' + +test_expect_success 'cleanup chmod after submit cancel' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + chmod u+x text && + chmod u-x text+x && + git add text text+x && + git commit -m "chmod texts" && + echo n | test_expect_code 1 git p4 submit + ) && + ( + cd "$cli" && + test_path_is_file text && + ! p4 fstat -T action text && + stat --format=%A text | egrep ^-r-- && + test_path_is_file text+x && + ! p4 fstat -T action text+x && + stat --format=%A text+x | egrep ^-r-x + ) +' + test_expect_success 'kill p4d' ' kill_p4d ' From b0ccc80d3cd12e41eaa9bc643b3aec3f50d134d8 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Sun, 9 Sep 2012 16:16:10 -0400 Subject: [PATCH 09/12] git p4: accept -v for --verbose The short form "-v" is common in many git commands as an alias for "--verbose". Signed-off-by: Pete Wyckoff Acked-by: Luke Diamand Signed-off-by: Junio C Hamano --- Documentation/git-p4.txt | 2 +- git-p4.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt index 8228f33e3fdf0e..4b03356a3a2976 100644 --- a/Documentation/git-p4.txt +++ b/Documentation/git-p4.txt @@ -163,7 +163,7 @@ All commands except clone accept these options. --git-dir :: Set the 'GIT_DIR' environment variable. See linkgit:git[1]. ---verbose:: +--verbose, -v:: Provide more progress information. Sync options diff --git a/git-p4.py b/git-p4.py index 60012bc63cad67..464f64da952dde 100755 --- a/git-p4.py +++ b/git-p4.py @@ -3028,7 +3028,7 @@ def main(): args = sys.argv[2:] - options.append(optparse.make_option("--verbose", dest="verbose", action="store_true")) + options.append(optparse.make_option("--verbose", "-v", dest="verbose", action="store_true")) if cmd.needsGit: options.append(optparse.make_option("--git-dir", dest="gitdir")) From ef739f0829f866b7e70e072a6744dc9cd1e12822 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Sun, 9 Sep 2012 16:16:11 -0400 Subject: [PATCH 10/12] git p4: add submit --dry-run option A new option, "git p4 submit --dry-run" can be used to verify what commits and labels would be moved into p4. Signed-off-by: Pete Wyckoff Acked-by: Luke Diamand Signed-off-by: Junio C Hamano --- Documentation/git-p4.txt | 4 ++++ git-p4.py | 43 ++++++++++++++++++++++++++++------------ t/t9807-git-p4-submit.sh | 41 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 13 deletions(-) diff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt index 4b03356a3a2976..1f32b8eaa5758e 100644 --- a/Documentation/git-p4.txt +++ b/Documentation/git-p4.txt @@ -269,6 +269,10 @@ These options can be used to modify 'git p4 submit' behavior. Export tags from git as p4 labels. Tags found in git are applied to the perforce working directory. +--dry-run, -n:: + Show just what commits would be submitted to p4; do not change + state in git or p4. + Rebase options ~~~~~~~~~~~~~~ These options can be used to modify 'git p4 rebase' behavior. diff --git a/git-p4.py b/git-p4.py index 464f64da952dde..04e9a80db41f30 100755 --- a/git-p4.py +++ b/git-p4.py @@ -853,12 +853,14 @@ def __init__(self): # preserve the user, requires relevant p4 permissions optparse.make_option("--preserve-user", dest="preserveUser", action="store_true"), optparse.make_option("--export-labels", dest="exportLabels", action="store_true"), + optparse.make_option("--dry-run", "-n", dest="dry_run", action="store_true"), ] self.description = "Submit changes from git to the perforce depot." self.usage += " [name of git branch to submit into perforce depot]" self.origin = "" self.detectRenames = False self.preserveUser = gitConfig("git-p4.preserveUser").lower() == "true" + self.dry_run = False self.isWindows = (platform.system() == "Windows") self.exportLabels = False self.p4HasMoveCommand = p4_has_command("move") @@ -1366,14 +1368,17 @@ def exportGitTags(self, gitTags): for mapping in clientSpec.mappings: labelTemplate += "\t%s\n" % mapping.depot_side.path - p4_write_pipe(["label", "-i"], labelTemplate) + if self.dry_run: + print "Would create p4 label %s for tag" % name + else: + p4_write_pipe(["label", "-i"], labelTemplate) - # Use the label - p4_system(["tag", "-l", name] + - ["%s@%s" % (mapping.depot_side.path, changelist) for mapping in clientSpec.mappings]) + # Use the label + p4_system(["tag", "-l", name] + + ["%s@%s" % (mapping.depot_side.path, changelist) for mapping in clientSpec.mappings]) - if verbose: - print "created p4 label for tag %s" % name + if verbose: + print "created p4 label for tag %s" % name def run(self, args): if len(args) == 0: @@ -1432,12 +1437,15 @@ def run(self, args): os.makedirs(self.clientPath) chdir(self.clientPath) - print "Synchronizing p4 checkout..." - if new_client_dir: - # old one was destroyed, and maybe nobody told p4 - p4_sync("...", "-f") + if self.dry_run: + print "Would synchronize p4 checkout in %s" % self.clientPath else: - p4_sync("...") + print "Synchronizing p4 checkout..." + if new_client_dir: + # old one was destroyed, and maybe nobody told p4 + p4_sync("...", "-f") + else: + p4_sync("...") self.check() commits = [] @@ -1488,10 +1496,17 @@ def run(self, args): # Apply the commits, one at a time. On failure, ask if should # continue to try the rest of the patches, or quit. # + if self.dry_run: + print "Would apply" applied = [] last = len(commits) - 1 for i, commit in enumerate(commits): - ok = self.applyCommit(commit) + if self.dry_run: + print " ", read_pipe(["git", "show", "-s", + "--format=format:%h %s", commit]) + ok = True + else: + ok = self.applyCommit(commit) if ok: applied.append(commit) else: @@ -1515,7 +1530,9 @@ def run(self, args): chdir(self.oldWorkingDirectory) - if len(commits) == len(applied): + if self.dry_run: + pass + elif len(commits) == len(applied): print "All commits applied!" sync = P4Sync() diff --git a/t/t9807-git-p4-submit.sh b/t/t9807-git-p4-submit.sh index 9394fd4e9b5bd7..9cb6aa79ed7db7 100755 --- a/t/t9807-git-p4-submit.sh +++ b/t/t9807-git-p4-submit.sh @@ -54,6 +54,47 @@ test_expect_success 'submit --origin' ' ) ' +test_expect_success 'submit --dry-run' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + test_commit "dry-run1" && + test_commit "dry-run2" && + git p4 submit --dry-run >out && + test_i18ngrep "Would apply" out + ) && + ( + cd "$cli" && + test_path_is_missing "dry-run1.t" && + test_path_is_missing "dry-run2.t" + ) +' + +test_expect_success 'submit --dry-run --export-labels' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + echo dry-run1 >dry-run1 && + git add dry-run1 && + git commit -m "dry-run1" dry-run1 && + git config git-p4.skipSubmitEdit true && + git p4 submit && + echo dry-run2 >dry-run2 && + git add dry-run2 && + git commit -m "dry-run2" dry-run2 && + git tag -m "dry-run-tag1" dry-run-tag1 HEAD^ && + git p4 submit --dry-run --export-labels >out && + test_i18ngrep "Would create p4 label" out + ) && + ( + cd "$cli" && + test_path_is_file "dry-run1" && + test_path_is_missing "dry-run2" + ) +' + test_expect_success 'submit with allowSubmit' ' test_when_finished cleanup_git && git p4 clone --dest="$git" //depot && From 728b7ad8bb6491d1d8bcd7498e06474b15812d52 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Sun, 9 Sep 2012 16:16:12 -0400 Subject: [PATCH 11/12] git p4: add submit --prepare-p4-only option This option can be used to prepare the client workspace for submission, only. It does not invoke the final "p4 submit". A message describes how to proceed, either submitting the changes or reverting. Signed-off-by: Pete Wyckoff Acked-by: Luke Diamand Signed-off-by: Junio C Hamano --- Documentation/git-p4.txt | 7 ++++++ git-p4.py | 46 ++++++++++++++++++++++++++++++++++++++++ t/t9807-git-p4-submit.sh | 24 +++++++++++++++++++++ 3 files changed, 77 insertions(+) diff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt index 1f32b8eaa5758e..4be42902eea5c6 100644 --- a/Documentation/git-p4.txt +++ b/Documentation/git-p4.txt @@ -273,6 +273,13 @@ These options can be used to modify 'git p4 submit' behavior. Show just what commits would be submitted to p4; do not change state in git or p4. +--prepare-p4-only:: + Apply a commit to the p4 workspace, opening, adding and deleting + files in p4 as for a normal submit operation. Do not issue the + final "p4 submit", but instead print a message about how to + submit manually or revert. This option always stops after the + first (oldest) commit. Git tags are not exported to p4. + Rebase options ~~~~~~~~~~~~~~ These options can be used to modify 'git p4 rebase' behavior. diff --git a/git-p4.py b/git-p4.py index 04e9a80db41f30..2dd2b76b1c3057 100755 --- a/git-p4.py +++ b/git-p4.py @@ -854,6 +854,7 @@ def __init__(self): optparse.make_option("--preserve-user", dest="preserveUser", action="store_true"), optparse.make_option("--export-labels", dest="exportLabels", action="store_true"), optparse.make_option("--dry-run", "-n", dest="dry_run", action="store_true"), + optparse.make_option("--prepare-p4-only", dest="prepare_p4_only", action="store_true"), ] self.description = "Submit changes from git to the perforce depot." self.usage += " [name of git branch to submit into perforce depot]" @@ -861,6 +862,7 @@ def __init__(self): self.detectRenames = False self.preserveUser = gitConfig("git-p4.preserveUser").lower() == "true" self.dry_run = False + self.prepare_p4_only = False self.isWindows = (platform.system() == "Windows") self.exportLabels = False self.p4HasMoveCommand = p4_has_command("move") @@ -1270,6 +1272,41 @@ def applyCommit(self, id): tmpFile.write(submitTemplate + separatorLine + diff + newdiff) tmpFile.close() + if self.prepare_p4_only: + # + # Leave the p4 tree prepared, and the submit template around + # and let the user decide what to do next + # + print + print "P4 workspace prepared for submission." + print "To submit or revert, go to client workspace" + print " " + self.clientPath + print + print "To submit, use \"p4 submit\" to write a new description," + print "or \"p4 submit -i %s\" to use the one prepared by" \ + " \"git p4\"." % fileName + print "You can delete the file \"%s\" when finished." % fileName + + if self.preserveUser and p4User and not self.p4UserIsMe(p4User): + print "To preserve change ownership by user %s, you must\n" \ + "do \"p4 change -f \" after submitting and\n" \ + "edit the User field." + if pureRenameCopy: + print "After submitting, renamed files must be re-synced." + print "Invoke \"p4 sync -f\" on each of these files:" + for f in pureRenameCopy: + print " " + f + + print + print "To revert the changes, use \"p4 revert ...\", and delete" + print "the submit template file \"%s\"" % fileName + if filesToAdd: + print "Since the commit adds new files, they must be deleted:" + for f in filesToAdd: + print " " + f + print + return True + # # Let the user edit the change description, then submit it. # @@ -1370,6 +1407,9 @@ def exportGitTags(self, gitTags): if self.dry_run: print "Would create p4 label %s for tag" % name + elif self.prepare_p4_only: + print "Not creating p4 label %s for tag due to option" \ + " --prepare-p4-only" % name else: p4_write_pipe(["label", "-i"], labelTemplate) @@ -1510,6 +1550,10 @@ def run(self, args): if ok: applied.append(commit) else: + if self.prepare_p4_only and i < last: + print "Processing only the first commit due to option" \ + " --prepare-p4-only" + break if i < last: quit = False while True: @@ -1532,6 +1576,8 @@ def run(self, args): if self.dry_run: pass + elif self.prepare_p4_only: + pass elif len(commits) == len(applied): print "All commits applied!" diff --git a/t/t9807-git-p4-submit.sh b/t/t9807-git-p4-submit.sh index 9cb6aa79ed7db7..0ae048f29f548e 100755 --- a/t/t9807-git-p4-submit.sh +++ b/t/t9807-git-p4-submit.sh @@ -375,6 +375,30 @@ test_expect_success 'description with Jobs section and bogus following text' ' make_job $(cat jobname) && test_must_fail git p4 submit 2>err && test_i18ngrep "Unknown field name" err + ) && + ( + cd "$cli" && + p4 revert desc6 && + rm desc6 + ) +' + +test_expect_success 'submit --prepare-p4-only' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + echo prep-only-add >prep-only-add && + git add prep-only-add && + git commit -m "prep only add" && + git p4 submit --prepare-p4-only >out && + test_i18ngrep "prepared for submission" out && + test_i18ngrep "must be deleted" out + ) && + ( + cd "$cli" && + test_path_is_file prep-only-add && + p4 fstat -T action prep-only-add | grep -w add ) ' From 6bbfd1372d2aa5b856fc7fb1616cb06c982fcb83 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Sun, 9 Sep 2012 16:16:13 -0400 Subject: [PATCH 12/12] git-p4: add submit --conflict option and config varaiable This allows specifying what to do when a conflict happens when applying a commit to p4, automating the interactive prompt. Signed-off-by: Pete Wyckoff Acked-by: Luke Diamand Signed-off-by: Junio C Hamano --- Documentation/git-p4.txt | 11 ++++++ git-p4.py | 36 +++++++++++++++++--- t/t9815-git-p4-submit-fail.sh | 63 +++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 5 deletions(-) diff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt index 4be42902eea5c6..beff6229c8cc45 100644 --- a/Documentation/git-p4.txt +++ b/Documentation/git-p4.txt @@ -280,6 +280,13 @@ These options can be used to modify 'git p4 submit' behavior. submit manually or revert. This option always stops after the first (oldest) commit. Git tags are not exported to p4. +--conflict=(ask|skip|quit):: + Conflicts can occur when applying a commit to p4. When this + happens, the default behavior ("ask") is to prompt whether to + skip this commit and continue, or quit. This option can be used + to bypass the prompt, causing conflicting commits to be automatically + skipped, or to quit trying to apply commits, without prompting. + Rebase options ~~~~~~~~~~~~~~ These options can be used to modify 'git p4 rebase' behavior. @@ -530,6 +537,10 @@ git-p4.labelExportRegexp:: Only p4 labels matching this regular expression will be exported. The default value is '[a-zA-Z0-9_\-.]+$'. +git-p4.conflict:: + Specify submit behavior when a conflict with p4 is found, as per + --conflict. The default behavior is 'ask'. + IMPLEMENTATION DETAILS ---------------------- * Changesets from p4 are imported using git fast-import. diff --git a/git-p4.py b/git-p4.py index 2dd2b76b1c3057..96f6bc0a76a13a 100755 --- a/git-p4.py +++ b/git-p4.py @@ -844,6 +844,9 @@ def run(self, args): return True class P4Submit(Command, P4UserMap): + + conflict_behavior_choices = ("ask", "skip", "quit") + def __init__(self): Command.__init__(self) P4UserMap.__init__(self) @@ -855,6 +858,8 @@ def __init__(self): optparse.make_option("--export-labels", dest="exportLabels", action="store_true"), optparse.make_option("--dry-run", "-n", dest="dry_run", action="store_true"), optparse.make_option("--prepare-p4-only", dest="prepare_p4_only", action="store_true"), + optparse.make_option("--conflict", dest="conflict_behavior", + choices=self.conflict_behavior_choices) ] self.description = "Submit changes from git to the perforce depot." self.usage += " [name of git branch to submit into perforce depot]" @@ -863,6 +868,7 @@ def __init__(self): self.preserveUser = gitConfig("git-p4.preserveUser").lower() == "true" self.dry_run = False self.prepare_p4_only = False + self.conflict_behavior = None self.isWindows = (platform.system() == "Windows") self.exportLabels = False self.p4HasMoveCommand = p4_has_command("move") @@ -1445,6 +1451,16 @@ def run(self, args): if not self.canChangeChangelists(): die("Cannot preserve user names without p4 super-user or admin permissions") + # if not set from the command line, try the config file + if self.conflict_behavior is None: + val = gitConfig("git-p4.conflict") + if val: + if val not in self.conflict_behavior_choices: + die("Invalid value '%s' for config git-p4.conflict" % val) + else: + val = "ask" + self.conflict_behavior = val + if self.verbose: print "Origin branch is " + self.origin @@ -1557,11 +1573,21 @@ def run(self, args): if i < last: quit = False while True: - print "What do you want to do?" - response = raw_input("[s]kip this commit but apply" - " the rest, or [q]uit? ") - if not response: - continue + # prompt for what to do, or use the option/variable + if self.conflict_behavior == "ask": + print "What do you want to do?" + response = raw_input("[s]kip this commit but apply" + " the rest, or [q]uit? ") + if not response: + continue + elif self.conflict_behavior == "skip": + response = "s" + elif self.conflict_behavior == "quit": + response = "q" + else: + die("Unknown conflict_behavior '%s'" % + self.conflict_behavior) + if response[0] == "s": print "Skipping this commit, but applying the rest" break diff --git a/t/t9815-git-p4-submit-fail.sh b/t/t9815-git-p4-submit-fail.sh index 1823cff12bf6b4..d2b7b3d98d77a1 100755 --- a/t/t9815-git-p4-submit-fail.sh +++ b/t/t9815-git-p4-submit-fail.sh @@ -108,6 +108,69 @@ test_expect_success 'conflict on first of two commits, quit' ' ) ' +test_expect_success 'conflict cli and config options' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git p4 submit --conflict=ask && + git p4 submit --conflict=skip && + git p4 submit --conflict=quit && + test_expect_code 2 git p4 submit --conflict=foo && + test_expect_code 2 git p4 submit --conflict && + git config git-p4.conflict foo && + test_expect_code 1 git p4 submit && + git config --unset git-p4.conflict && + git p4 submit + ) +' + +test_expect_success 'conflict on first of two commits, --conflict=skip' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$cli" && + p4 open file1 && + echo line9 >>file1 && + p4 submit -d "line9 in file1" + ) && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + # this submit should cause a conflict + echo line10 >>file1 && + git add file1 && + git commit -m "line10 in file1 will conflict" && + # but this commit is okay + test_commit "okay_commit_after_auto_skip" && + test_expect_code 1 git p4 submit --conflict=skip >out && + test_i18ngrep "Applied only the commits" out + ) +' + +test_expect_success 'conflict on first of two commits, --conflict=quit' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$cli" && + p4 open file1 && + echo line11 >>file1 && + p4 submit -d "line11 in file1" + ) && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + # this submit should cause a conflict + echo line12 >>file1 && + git add file1 && + git commit -m "line12 in file1 will conflict" && + # but this commit is okay + test_commit "okay_commit_after_auto_quit" && + test_expect_code 1 git p4 submit --conflict=quit >out && + test_i18ngrep "No commits applied" out + ) +' + # # Cleanup after submit fail, all cases. Some modifications happen # before trying to apply the patch. Make sure these are unwound