зеркало из https://github.com/microsoft/git.git
Merge branch 'pw/p4-submit-conflicts'
Add '--conflict' option to git-p4 subcommand to specify what action to take when conflicts are found during 'p4 submit'. * pw/p4-submit-conflicts: git-p4: add submit --conflict option and config varaiable git p4: add submit --prepare-p4-only option git p4: add submit --dry-run option git p4: accept -v for --verbose git p4: revert deleted files after submit cancel git p4: rearrange submit template construction git p4: test clean-up after failed submit, fix added files git p4: standardize submit cancel due to unchanged template git p4: move conflict prompt into run, add [q]uit input git p4: remove submit failure options [a]pply and [w]rite git p4: gracefully fail if some commits could not be applied git p4 test: remove bash-ism of combined export/assignment
This commit is contained in:
Коммит
8db3865936
|
@ -163,7 +163,7 @@ All commands except clone accept these options.
|
|||
--git-dir <dir>::
|
||||
Set the 'GIT_DIR' environment variable. See linkgit:git[1].
|
||||
|
||||
--verbose::
|
||||
--verbose, -v::
|
||||
Provide more progress information.
|
||||
|
||||
Sync options
|
||||
|
@ -269,6 +269,24 @@ 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.
|
||||
|
||||
--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.
|
||||
|
||||
--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.
|
||||
|
@ -519,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.
|
||||
|
|
239
git-p4.py
239
git-p4.py
|
@ -844,6 +844,9 @@ class P4RollBack(Command):
|
|||
return True
|
||||
|
||||
class P4Submit(Command, P4UserMap):
|
||||
|
||||
conflict_behavior_choices = ("ask", "skip", "quit")
|
||||
|
||||
def __init__(self):
|
||||
Command.__init__(self)
|
||||
P4UserMap.__init__(self)
|
||||
|
@ -853,12 +856,19 @@ class P4Submit(Command, P4UserMap):
|
|||
# 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"),
|
||||
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]"
|
||||
self.origin = ""
|
||||
self.detectRenames = False
|
||||
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")
|
||||
|
@ -1088,7 +1098,10 @@ class P4Submit(Command, P4UserMap):
|
|||
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)
|
||||
|
||||
|
@ -1195,34 +1208,13 @@ class P4Submit(Command, P4UserMap):
|
|||
patch_succeeded = True
|
||||
|
||||
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) ")
|
||||
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
|
||||
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")
|
||||
for f in editedFiles:
|
||||
p4_revert(f)
|
||||
return False
|
||||
|
||||
#
|
||||
# Apply the patch for real, and do add/delete/+x handling.
|
||||
#
|
||||
system(applyPatchCmd)
|
||||
|
||||
for f in filesToAdd:
|
||||
|
@ -1236,6 +1228,10 @@ class P4Submit(Command, P4UserMap):
|
|||
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)
|
||||
|
@ -1244,8 +1240,16 @@ class P4Submit(Command, P4UserMap):
|
|||
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 = ""
|
||||
|
@ -1253,6 +1257,7 @@ class P4Submit(Command, P4UserMap):
|
|||
diff += p4_read_pipe(['diff', '-du',
|
||||
wildcard_encode(editedFile)])
|
||||
|
||||
# new file diff
|
||||
newdiff = ""
|
||||
for newFile in filesToAdd:
|
||||
newdiff += "==== new file ====\n"
|
||||
|
@ -1263,13 +1268,7 @@ class P4Submit(Command, P4UserMap):
|
|||
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:
|
||||
|
@ -1279,8 +1278,47 @@ class P4Submit(Command, P4UserMap):
|
|||
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 <change>\" 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.
|
||||
#
|
||||
if self.edit_template(fileName):
|
||||
# read the edited message and submit
|
||||
ret = True
|
||||
tmpFile = open(fileName, "rb")
|
||||
message = tmpFile.read()
|
||||
tmpFile.close()
|
||||
|
@ -1304,14 +1342,18 @@ class P4Submit(Command, P4UserMap):
|
|||
|
||||
else:
|
||||
# skip this patch
|
||||
ret = False
|
||||
print "Submission cancelled, undoing p4 changes."
|
||||
for f in editedFiles:
|
||||
p4_revert(f)
|
||||
for f in filesToAdd:
|
||||
p4_revert(f)
|
||||
os.remove(f)
|
||||
for f in filesToDelete:
|
||||
p4_revert(f)
|
||||
|
||||
os.remove(fileName)
|
||||
return ret
|
||||
|
||||
# Export git tags as p4 labels. Create a p4 label and then tag
|
||||
# with that.
|
||||
|
@ -1369,14 +1411,20 @@ class P4Submit(Command, P4UserMap):
|
|||
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
|
||||
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)
|
||||
|
||||
# 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:
|
||||
|
@ -1403,6 +1451,16 @@ class P4Submit(Command, P4UserMap):
|
|||
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
|
||||
|
||||
|
@ -1435,12 +1493,15 @@ class P4Submit(Command, P4UserMap):
|
|||
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 = []
|
||||
|
@ -1487,14 +1548,64 @@ class P4Submit(Command, P4UserMap):
|
|||
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)
|
||||
#
|
||||
# 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):
|
||||
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:
|
||||
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:
|
||||
# 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 len(commits) == 0:
|
||||
print "All changes applied!"
|
||||
chdir(self.oldWorkingDirectory)
|
||||
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)
|
||||
|
||||
if self.dry_run:
|
||||
pass
|
||||
elif self.prepare_p4_only:
|
||||
pass
|
||||
elif len(commits) == len(applied):
|
||||
print "All commits applied!"
|
||||
|
||||
sync = P4Sync()
|
||||
sync.run([])
|
||||
|
@ -1502,6 +1613,20 @@ class P4Submit(Command, P4UserMap):
|
|||
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 +1637,10 @@ class P4Submit(Command, P4UserMap):
|
|||
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):
|
||||
|
@ -3015,7 +3144,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"))
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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 &&
|
||||
|
@ -334,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
|
||||
)
|
||||
'
|
||||
|
||||
|
|
|
@ -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
|
||||
'
|
||||
|
|
|
@ -0,0 +1,429 @@
|
|||
#!/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' '
|
||||
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" &&
|
||||
test_expect_code 1 git p4 submit >out &&
|
||||
test_i18ngrep "No commits applied" out
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'conflict on second of two commits' '
|
||||
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" &&
|
||||
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 '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 '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
|
||||
# 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"
|
||||
)
|
||||
'
|
||||
|
||||
#
|
||||
# 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
|
||||
'
|
||||
|
||||
test_done
|
Загрузка…
Ссылка в новой задаче