зеркало из https://github.com/microsoft/git.git
Merge branch 'bk/p4-pre-edit-changelist'
"git p4" learned four new hooks and also "--no-verify" option to bypass them (and the existing "p4-pre-submit" hook). * bk/p4-pre-edit-changelist: git-p4: add RCS keyword status message git-p4: add p4 submit hooks git-p4: restructure code in submit git-p4: add --no-verify option git-p4: add p4-pre-submit exit text git-p4: create new function run_git_hook git-p4: rewrite prompt to be Windows compatible
This commit is contained in:
Коммит
5f2ec211f6
|
@ -374,14 +374,55 @@ These options can be used to modify 'git p4 submit' behavior.
|
|||
been submitted. Implies --disable-rebase. Can also be set with
|
||||
git-p4.disableP4Sync. Sync with origin/master still goes ahead if possible.
|
||||
|
||||
Hook for submit
|
||||
~~~~~~~~~~~~~~~
|
||||
Hooks for submit
|
||||
----------------
|
||||
|
||||
p4-pre-submit
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
The `p4-pre-submit` hook is executed if it exists and is executable.
|
||||
The hook takes no parameters and nothing from standard input. Exiting with
|
||||
non-zero status from this script prevents `git-p4 submit` from launching.
|
||||
It can be bypassed with the `--no-verify` command line option.
|
||||
|
||||
One usage scenario is to run unit tests in the hook.
|
||||
|
||||
p4-prepare-changelist
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The `p4-prepare-changelist` hook is executed right after preparing
|
||||
the default changelist message and before the editor is started.
|
||||
It takes one parameter, the name of the file that contains the
|
||||
changelist text. Exiting with a non-zero status from the script
|
||||
will abort the process.
|
||||
|
||||
The purpose of the hook is to edit the message file in place,
|
||||
and it is not supressed by the `--no-verify` option. This hook
|
||||
is called even if `--prepare-p4-only` is set.
|
||||
|
||||
p4-changelist
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
The `p4-changelist` hook is executed after the changelist
|
||||
message has been edited by the user. It can be bypassed with the
|
||||
`--no-verify` option. It takes a single parameter, the name
|
||||
of the file that holds the proposed changelist text. Exiting
|
||||
with a non-zero status causes the command to abort.
|
||||
|
||||
The hook is allowed to edit the changelist file and can be used
|
||||
to normalize the text into some project standard format. It can
|
||||
also be used to refuse the Submit after inspect the message file.
|
||||
|
||||
p4-post-changelist
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The `p4-post-changelist` hook is invoked after the submit has
|
||||
successfully occured in P4. It takes no parameters and is meant
|
||||
primarily for notification and cannot affect the outcome of the
|
||||
git p4 submit action.
|
||||
|
||||
|
||||
|
||||
Rebase options
|
||||
~~~~~~~~~~~~~~
|
||||
These options can be used to modify 'git p4 rebase' behavior.
|
||||
|
|
|
@ -522,12 +522,61 @@ The exit status determines whether git will use the data from the
|
|||
hook to limit its search. On error, it will fall back to verifying
|
||||
all files and folders.
|
||||
|
||||
p4-changelist
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
This hook is invoked by `git-p4 submit`.
|
||||
|
||||
The `p4-changelist` hook is executed after the changelist
|
||||
message has been edited by the user. It can be bypassed with the
|
||||
`--no-verify` option. It takes a single parameter, the name
|
||||
of the file that holds the proposed changelist text. Exiting
|
||||
with a non-zero status causes the command to abort.
|
||||
|
||||
The hook is allowed to edit the changelist file and can be used
|
||||
to normalize the text into some project standard format. It can
|
||||
also be used to refuse the Submit after inspect the message file.
|
||||
|
||||
Run `git-p4 submit --help` for details.
|
||||
|
||||
p4-prepare-changelist
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This hook is invoked by `git-p4 submit`.
|
||||
|
||||
The `p4-prepare-changelist` hook is executed right after preparing
|
||||
the default changelist message and before the editor is started.
|
||||
It takes one parameter, the name of the file that contains the
|
||||
changelist text. Exiting with a non-zero status from the script
|
||||
will abort the process.
|
||||
|
||||
The purpose of the hook is to edit the message file in place,
|
||||
and it is not supressed by the `--no-verify` option. This hook
|
||||
is called even if `--prepare-p4-only` is set.
|
||||
|
||||
Run `git-p4 submit --help` for details.
|
||||
|
||||
p4-post-changelist
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This hook is invoked by `git-p4 submit`.
|
||||
|
||||
The `p4-post-changelist` hook is invoked after the submit has
|
||||
successfully occured in P4. It takes no parameters and is meant
|
||||
primarily for notification and cannot affect the outcome of the
|
||||
git p4 submit action.
|
||||
|
||||
Run `git-p4 submit --help` for details.
|
||||
|
||||
p4-pre-submit
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
This hook is invoked by `git-p4 submit`. It takes no parameters and nothing
|
||||
from standard input. Exiting with non-zero status from this script prevent
|
||||
`git-p4 submit` from launching. Run `git-p4 submit --help` for details.
|
||||
`git-p4 submit` from launching. It can be bypassed with the `--no-verify`
|
||||
command line option. Run `git-p4 submit --help` for details.
|
||||
|
||||
|
||||
|
||||
post-index-change
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
|
234
git-p4.py
234
git-p4.py
|
@ -34,6 +34,7 @@ import zipfile
|
|||
import zlib
|
||||
import ctypes
|
||||
import errno
|
||||
import glob
|
||||
|
||||
# On python2.7 where raw_input() and input() are both availble,
|
||||
# we want raw_input's semantics, but aliased to input for python3
|
||||
|
@ -165,7 +166,10 @@ def prompt(prompt_text):
|
|||
"""
|
||||
choices = set(m.group(1) for m in re.finditer(r"\[(.)\]", prompt_text))
|
||||
while True:
|
||||
response = input(prompt_text).strip().lower()
|
||||
sys.stderr.flush()
|
||||
sys.stdout.write(prompt_text)
|
||||
sys.stdout.flush()
|
||||
response=sys.stdin.readline().strip().lower()
|
||||
if not response:
|
||||
continue
|
||||
response = response[0]
|
||||
|
@ -202,6 +206,73 @@ def decode_path(path):
|
|||
print('Path with non-ASCII characters detected. Used {} to decode: {}'.format(encoding, path))
|
||||
return path
|
||||
|
||||
def run_git_hook(cmd, param=[]):
|
||||
"""Execute a hook if the hook exists."""
|
||||
if verbose:
|
||||
sys.stderr.write("Looking for hook: %s\n" % cmd)
|
||||
sys.stderr.flush()
|
||||
|
||||
hooks_path = gitConfig("core.hooksPath")
|
||||
if len(hooks_path) <= 0:
|
||||
hooks_path = os.path.join(os.environ["GIT_DIR"], "hooks")
|
||||
|
||||
if not isinstance(param, list):
|
||||
param=[param]
|
||||
|
||||
# resolve hook file name, OS depdenent
|
||||
hook_file = os.path.join(hooks_path, cmd)
|
||||
if platform.system() == 'Windows':
|
||||
if not os.path.isfile(hook_file):
|
||||
# look for the file with an extension
|
||||
files = glob.glob(hook_file + ".*")
|
||||
if not files:
|
||||
return True
|
||||
files.sort()
|
||||
hook_file = files.pop()
|
||||
while hook_file.upper().endswith(".SAMPLE"):
|
||||
# The file is a sample hook. We don't want it
|
||||
if len(files) > 0:
|
||||
hook_file = files.pop()
|
||||
else:
|
||||
return True
|
||||
|
||||
if not os.path.isfile(hook_file) or not os.access(hook_file, os.X_OK):
|
||||
return True
|
||||
|
||||
return run_hook_command(hook_file, param) == 0
|
||||
|
||||
def run_hook_command(cmd, param):
|
||||
"""Executes a git hook command
|
||||
cmd = the command line file to be executed. This can be
|
||||
a file that is run by OS association.
|
||||
|
||||
param = a list of parameters to pass to the cmd command
|
||||
|
||||
On windows, the extension is checked to see if it should
|
||||
be run with the Git for Windows Bash shell. If there
|
||||
is no file extension, the file is deemed a bash shell
|
||||
and will be handed off to sh.exe. Otherwise, Windows
|
||||
will be called with the shell to handle the file assocation.
|
||||
|
||||
For non Windows operating systems, the file is called
|
||||
as an executable.
|
||||
"""
|
||||
cli = [cmd] + param
|
||||
use_shell = False
|
||||
if platform.system() == 'Windows':
|
||||
(root,ext) = os.path.splitext(cmd)
|
||||
if ext == "":
|
||||
exe_path = os.environ.get("EXEPATH")
|
||||
if exe_path is None:
|
||||
exe_path = ""
|
||||
else:
|
||||
exe_path = os.path.join(exe_path, "bin")
|
||||
cli = [os.path.join(exe_path, "SH.EXE")] + cli
|
||||
else:
|
||||
use_shell = True
|
||||
return subprocess.call(cli, shell=use_shell)
|
||||
|
||||
|
||||
def write_pipe(c, stdin):
|
||||
if verbose:
|
||||
sys.stderr.write('Writing pipe: %s\n' % str(c))
|
||||
|
@ -1567,13 +1638,39 @@ class P4Submit(Command, P4UserMap):
|
|||
"work from a local git branch that is not master"),
|
||||
optparse.make_option("--disable-p4sync", dest="disable_p4sync", action="store_true",
|
||||
help="Skip Perforce sync of p4/master after submit or shelve"),
|
||||
optparse.make_option("--no-verify", dest="no_verify", action="store_true",
|
||||
help="Bypass p4-pre-submit and p4-changelist hooks"),
|
||||
]
|
||||
self.description = """Submit changes from git to the perforce depot.\n
|
||||
The `p4-pre-submit` hook is executed if it exists and is executable.
|
||||
The hook takes no parameters and nothing from standard input. Exiting with
|
||||
non-zero status from this script prevents `git-p4 submit` from launching.
|
||||
The `p4-pre-submit` hook is executed if it exists and is executable. It
|
||||
can be bypassed with the `--no-verify` command line option. The hook takes
|
||||
no parameters and nothing from standard input. Exiting with a non-zero status
|
||||
from this script prevents `git-p4 submit` from launching.
|
||||
|
||||
One usage scenario is to run unit tests in the hook."""
|
||||
One usage scenario is to run unit tests in the hook.
|
||||
|
||||
The `p4-prepare-changelist` hook is executed right after preparing the default
|
||||
changelist message and before the editor is started. It takes one parameter,
|
||||
the name of the file that contains the changelist text. Exiting with a non-zero
|
||||
status from the script will abort the process.
|
||||
|
||||
The purpose of the hook is to edit the message file in place, and it is not
|
||||
supressed by the `--no-verify` option. This hook is called even if
|
||||
`--prepare-p4-only` is set.
|
||||
|
||||
The `p4-changelist` hook is executed after the changelist message has been
|
||||
edited by the user. It can be bypassed with the `--no-verify` option. It
|
||||
takes a single parameter, the name of the file that holds the proposed
|
||||
changelist text. Exiting with a non-zero status causes the command to abort.
|
||||
|
||||
The hook is allowed to edit the changelist file and can be used to normalize
|
||||
the text into some project standard format. It can also be used to refuse the
|
||||
Submit after inspect the message file.
|
||||
|
||||
The `p4-post-changelist` hook is invoked after the submit has successfully
|
||||
occured in P4. It takes no parameters and is meant primarily for notification
|
||||
and cannot affect the outcome of the git p4 submit action.
|
||||
"""
|
||||
|
||||
self.usage += " [name of git branch to submit into perforce depot]"
|
||||
self.origin = ""
|
||||
|
@ -1591,6 +1688,7 @@ class P4Submit(Command, P4UserMap):
|
|||
self.exportLabels = False
|
||||
self.p4HasMoveCommand = p4_has_move_command()
|
||||
self.branch = None
|
||||
self.no_verify = False
|
||||
|
||||
if gitConfig('git-p4.largeFileSystem'):
|
||||
die("Large file system not supported for git-p4 submit command. Please remove it from config.")
|
||||
|
@ -1978,6 +2076,9 @@ class P4Submit(Command, P4UserMap):
|
|||
applyPatchCmd = patchcmd + "--check --apply -"
|
||||
patch_succeeded = True
|
||||
|
||||
if verbose:
|
||||
print("TryPatch: %s" % tryPatchCmd)
|
||||
|
||||
if os.system(tryPatchCmd) != 0:
|
||||
fixed_rcs_keywords = False
|
||||
patch_succeeded = False
|
||||
|
@ -2017,6 +2118,7 @@ class P4Submit(Command, P4UserMap):
|
|||
print("Retrying the patch with RCS keywords cleaned up")
|
||||
if os.system(tryPatchCmd) == 0:
|
||||
patch_succeeded = True
|
||||
print("Patch succeesed this time with RCS keywords cleaned")
|
||||
|
||||
if not patch_succeeded:
|
||||
for f in editedFiles:
|
||||
|
@ -2077,55 +2179,73 @@ class P4Submit(Command, P4UserMap):
|
|||
tmpFile.write(encode_text_stream(submitTemplate))
|
||||
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.
|
||||
#
|
||||
submitted = False
|
||||
|
||||
try:
|
||||
# Allow the hook to edit the changelist text before presenting it
|
||||
# to the user.
|
||||
if not run_git_hook("p4-prepare-changelist", [fileName]):
|
||||
return False
|
||||
|
||||
if self.prepare_p4_only:
|
||||
#
|
||||
# Leave the p4 tree prepared, and the submit template around
|
||||
# and let the user decide what to do next
|
||||
#
|
||||
submitted = True
|
||||
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("")
|
||||
sys.stdout.flush()
|
||||
return True
|
||||
|
||||
if self.edit_template(fileName):
|
||||
if not self.no_verify:
|
||||
if not run_git_hook("p4-changelist", [fileName]):
|
||||
print("The p4-changelist hook failed.")
|
||||
sys.stdout.flush()
|
||||
return False
|
||||
|
||||
# read the edited message and submit
|
||||
tmpFile = open(fileName, "rb")
|
||||
message = decode_text_stream(tmpFile.read())
|
||||
tmpFile.close()
|
||||
if self.isWindows:
|
||||
message = message.replace("\r\n", "\n")
|
||||
submitTemplate = message[:message.index(separatorLine)]
|
||||
if message.find(separatorLine) != -1:
|
||||
submitTemplate = message[:message.index(separatorLine)]
|
||||
else:
|
||||
submitTemplate = message
|
||||
|
||||
if len(submitTemplate.strip()) == 0:
|
||||
print("Changelist is empty, aborting this changelist.")
|
||||
sys.stdout.flush()
|
||||
return False
|
||||
|
||||
if update_shelve:
|
||||
p4_write_pipe(['shelve', '-r', '-i'], submitTemplate)
|
||||
|
@ -2148,20 +2268,23 @@ class P4Submit(Command, P4UserMap):
|
|||
|
||||
submitted = True
|
||||
|
||||
run_git_hook("p4-post-changelist")
|
||||
finally:
|
||||
# skip this patch
|
||||
# Revert changes if we skip this patch
|
||||
if not submitted or self.shelve:
|
||||
if self.shelve:
|
||||
print ("Reverting shelved files.")
|
||||
else:
|
||||
print ("Submission cancelled, undoing p4 changes.")
|
||||
sys.stdout.flush()
|
||||
for f in editedFiles | filesToDelete:
|
||||
p4_revert(f)
|
||||
for f in filesToAdd:
|
||||
p4_revert(f)
|
||||
os.remove(f)
|
||||
|
||||
os.remove(fileName)
|
||||
if not self.prepare_p4_only:
|
||||
os.remove(fileName)
|
||||
return submitted
|
||||
|
||||
# Export git tags as p4 labels. Create a p4 label and then tag
|
||||
|
@ -2385,13 +2508,17 @@ class P4Submit(Command, P4UserMap):
|
|||
sys.exit("number of commits (%d) must match number of shelved changelist (%d)" %
|
||||
(len(commits), num_shelves))
|
||||
|
||||
hooks_path = gitConfig("core.hooksPath")
|
||||
if len(hooks_path) <= 0:
|
||||
hooks_path = os.path.join(os.environ.get("GIT_DIR", ".git"), "hooks")
|
||||
|
||||
hook_file = os.path.join(hooks_path, "p4-pre-submit")
|
||||
if os.path.isfile(hook_file) and os.access(hook_file, os.X_OK) and subprocess.call([hook_file]) != 0:
|
||||
sys.exit(1)
|
||||
if not self.no_verify:
|
||||
try:
|
||||
if not run_git_hook("p4-pre-submit"):
|
||||
print("\nThe p4-pre-submit hook failed, aborting the submit.\n\nYou can skip " \
|
||||
"this pre-submission check by adding\nthe command line option '--no-verify', " \
|
||||
"however,\nthis will also skip the p4-changelist hook as well.")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print("\nThe p4-pre-submit hook failed, aborting the submit.\n\nThe hook failed "\
|
||||
"with the error '{0}'".format(e.message) )
|
||||
sys.exit(1)
|
||||
|
||||
#
|
||||
# Apply the commits, one at a time. On failure, ask if should
|
||||
|
@ -4205,7 +4332,6 @@ commands = {
|
|||
"unshelve" : P4Unshelve,
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv[1:]) == 0:
|
||||
printUsage(commands.keys())
|
||||
|
|
Загрузка…
Ссылка в новой задаче