git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2 4/4] git-p4: add p4 submit hooks

From
Ben Keene via GitGitGadget <gitgitgadget@gmail.com>
Date
Jan 31, 2020, 21:58 UTC
Message-ID
<3d34dd042e47fce7bc49a63f676378172e15775e.1580507895.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.698.v2.git.git.1580507895.gitgitgadget@gmail.com>
From: Ben Keene <seraphire@gmail.com>

The git command "commit" supports a number of hooks that support changing the behavior of the commit command. The git-p4.py program only has one existing hook, "p4-p4-pre-submit". This command occurs early in the process. There are no hooks in the process flow for modifying the P4 changelist text programmatically.

Adds 3 new hooks and 1 new option to git-p4.py to the submit option.
The new command line option --no-verify:

Add a new command line option "--no-verify" to the Submit command of git-p4.py. This option will function in the spirit of the existing --no-verify command line option found in git commit. It will cause the P4 Submit function to ignore the existing p4-pre-submit and the new p4-changelist hooks.

The new hooks are:
* p4-prepare-changelist - Execute this hook after the changelist file
  has been created. The hook will be executed even if the
  --prepare-p4-only option is set.  This hook ignores the --no-verify
  option in keeping with the existing behavior of git commit.
* p4-changelist - Execute this hook after the user has edited the
  changelist. Do not execute this hook if the user has selected the
  --prepare-p4-only option. This hook will honor the --no-verify,
  following the conventions of git commit.
* p4-post-changelist - Execute this hook after the P4 submission process
  has completed successfully. This hook takes no parameters and is
  executed regardless of the --no-verify option.  It's return value will
  not be checked.

Change the execution of the existing trigger p4-pre-submit to honor the --no-verify option. Before exiting on failure of this hook, display text to the user explaining which hook has failed and the impact of using the --no-verify option.

Move all the code after creating the submitTemplate text into the try-finally block in the applyCommit() method. By restructuring the code, the P4 workspace is protected against otherwise unhandled exceptions and other early exits.

The calls to the new hooks: p4-prepare-changelist, p4-changelist, and p4-post-changelist should all be called inside the try-finally block.

Existing flow control can remain as defined - the if-block for prepare-p4-only sets the local variable "submitted" to True and exits the function. All other early exits, leave submitted set to False so the Finally block will undo changes to the P4 workspace.

Make the small usability change of adding an empty string to the print statements displayed to the user when the prepare-p4-only option is selected. On Windows, the command print() may display a set of parentheses () to the user when the print() function is called with no parameters. By supplying an empty string, the intended blank line will print as expected.

Fix a small bug when the submittedTemplate is edited by the user and all content in the file is removed. The existing code will throw an exception if the separateLine is not found in the file. Change this code to test for the separator line using a find() test first and only split on the separator if it is found.

Additionally, add the new behavior that if the changelist file has been completely emptied that the Submit action for this changelist will be aborted.

Signed-off-by: Ben Keene <seraphire@gmail.com>
---
 Documentation/git-p4.txt   |  44 +++++++++-
 Documentation/githooks.txt |  46 ++++++++++
 git-p4.py                  | 167 +++++++++++++++++++++++--------------
 3 files changed, 191 insertions(+), 66 deletions(-)
diff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt
index 3494a1db3e..f17c1d0561 100644
--- a/Documentation/git-p4.txt
+++ b/Documentation/git-p4.txt
@@ -374,14 +374,54 @@ 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.
 
 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.
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 50365f2914..67e78a8a05 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -515,6 +515,52 @@ 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
 ~~~~~~~~~~~~~
 
diff --git a/git-p4.py b/git-p4.py
index 61cfd1c1ae..fe633acf84 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -1521,13 +1521,39 @@ def __init__(self):
                                      "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.
-
-    One usage scenario is to run unit tests in the hook."""
+    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.
+
+    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 = ""
@@ -1545,6 +1571,7 @@ def __init__(self):
         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.")
@@ -1931,6 +1958,9 @@ def applyCommit(self, id):
         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
@@ -1970,6 +2000,7 @@ def applyCommit(self, id):
                 print("Retrying the patch with RCS keywords cleaned up")
                 if os.system(tryPatchCmd) == 0:
                     patch_succeeded = True
+                    print("Patch succeesed this time")
 
         if not patch_succeeded:
             for f in editedFiles:
@@ -2030,66 +2061,73 @@ def applyCommit(self, id):
         tmpFile.write(submitTemplate)
         tmpFile.close()
 
-        # Run the pre-edit hook to allow programmatic update to the changelist
-        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-edit-changelist")
-        if os.path.isfile(hook_file) and os.access(hook_file, os.X_OK) and subprocess.call([hook_file, fileName]) != 0:
-            for f in editedFiles:
-                p4_revert(f)
-            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
-            #
-            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 gitRunHook("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 gitRunHook("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 = 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)
@@ -2112,20 +2150,22 @@ def applyCommit(self, id):
 
                 submitted = True
 
+                gitRunHook("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)
+                os.remove(fileName)
         return submitted
 
     # Export git tags as p4 labels. Create a p4 label and then tag
@@ -2349,13 +2389,12 @@ def run(self, args):
             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:
+            if not gitRunHook("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)
 
         #
         # Apply the commits, one at a time.  On failure, ask if should
-- 
gitgitgadget
Previous: Junio C HamanoNext: Junio C Hamano
Message 20 of 57 in “git-p4: Add hook p4-pre-pedit-changelist”
  1. git-p4: Add hook p4-pre-pedit-changelistBen Keene via GitGitGadget, Jan 20, 2020
  2. Junio C HamanoJan 21, 2020
  3. Luke DiamandJan 29, 2020
  4. Junio C HamanoJan 29, 2020
  5. Luke DiamandJan 29, 2020
  6. Junio C HamanoJan 30, 2020
  7. Ben KeeneJan 30, 2020
  8. Junio C HamanoJan 30, 2020
  9. Bryan TurnerJan 30, 2020
  10. Ben KeeneJan 30, 2020
  11. 0/4 git-p4: add hook p4-pre-edit-changelistBen Keene via GitGitGadget, Jan 31, 2020
  12. 1/4 git-p4: rewrite prompt to be Windows compatibleBen Keene via GitGitGadget, Jan 31, 2020
  13. 3/4 git-p4: add hook p4-pre-edit-changelistBen Keene via GitGitGadget, Jan 31, 2020
  14. 2/4 git-p4: create new method gitRunHookBen Keene via GitGitGadget, Jan 31, 2020
  15. Junio C HamanoFeb 4, 2020
  16. Ben KeeneFeb 5, 2020
  17. Junio C HamanoFeb 5, 2020
  18. Ben KeeneFeb 6, 2020
  19. Junio C HamanoFeb 6, 2020
  20. 4/4 git-p4: add p4 submit hooksBen Keene via GitGitGadget, Jan 31, 2020
  21. Junio C HamanoFeb 4, 2020
  22. 0/5 git-p4: add hook p4-pre-edit-changelistBen Keene via GitGitGadget, Feb 6, 2020
  23. 1/5 git-p4: rewrite prompt to be Windows compatibleBen Keene via GitGitGadget, Feb 6, 2020
  24. Junio C HamanoFeb 6, 2020
  25. Ben KeeneFeb 10, 2020
  26. 2/5 git-p4: create new function run_git_hookBen Keene via GitGitGadget, Feb 6, 2020
  27. Junio C HamanoFeb 6, 2020
  28. Ben KeeneFeb 10, 2020
  29. 3/5 git-p4: add --no-verify optionBen Keene via GitGitGadget, Feb 6, 2020
  30. Junio C HamanoFeb 6, 2020
  31. Ben KeeneFeb 10, 2020
  32. 4/5 git-p4: restructure code in submitBen Keene via GitGitGadget, Feb 6, 2020
  33. 5/5 git-p4: add p4 submit hooksBen Keene via GitGitGadget, Feb 6, 2020
  34. 0/6 git-p4: add hooks for p4-changelistBen Keene via GitGitGadget, Feb 10, 2020
  35. 1/6 git-p4: rewrite prompt to be Windows compatibleBen Keene via GitGitGadget, Feb 10, 2020
  36. 4/6 git-p4: restructure code in submitBen Keene via GitGitGadget, Feb 10, 2020
  37. 6/6 git-4: add RCS keyword status messageBen Keene via GitGitGadget, Feb 10, 2020
  38. 2/6 git-p4: create new function run_git_hookBen Keene via GitGitGadget, Feb 10, 2020
  39. Junio C HamanoFeb 10, 2020
  40. 3/6 git-p4: add --no-verify optionBen Keene via GitGitGadget, Feb 10, 2020
  41. 5/6 git-p4: add p4 submit hooksBen Keene via GitGitGadget, Feb 10, 2020
  42. 0/7 git-p4: add hooks for p4-changelistBen Keene via GitGitGadget, Feb 11, 2020
  43. 1/7 git-p4: rewrite prompt to be Windows compatibleBen Keene via GitGitGadget, Feb 11, 2020
  44. 2/7 git-p4: create new function run_git_hookBen Keene via GitGitGadget, Feb 11, 2020
  45. 5/7 git-p4: restructure code in submitBen Keene via GitGitGadget, Feb 11, 2020
  46. 6/7 git-p4: add p4 submit hooksBen Keene via GitGitGadget, Feb 11, 2020
  47. 3/7 git-p4: add p4-pre-submit exit textBen Keene via GitGitGadget, Feb 11, 2020
  48. 4/7 git-p4: add --no-verify optionBen Keene via GitGitGadget, Feb 11, 2020
  49. 7/7 git-p4: add RCS keyword status messageBen Keene via GitGitGadget, Feb 11, 2020
  50. 0/7 git-p4: add hooks for p4-changelistBen Keene via GitGitGadget, Feb 14, 2020
  51. 1/7 git-p4: rewrite prompt to be Windows compatibleBen Keene via GitGitGadget, Feb 14, 2020
  52. 2/7 git-p4: create new function run_git_hookBen Keene via GitGitGadget, Feb 14, 2020
  53. 3/7 git-p4: add p4-pre-submit exit textBen Keene via GitGitGadget, Feb 14, 2020
  54. 5/7 git-p4: restructure code in submitBen Keene via GitGitGadget, Feb 14, 2020
  55. 4/7 git-p4: add --no-verify optionBen Keene via GitGitGadget, Feb 14, 2020
  56. 6/7 git-p4: add p4 submit hooksBen Keene via GitGitGadget, Feb 14, 2020
  57. 7/7 git-p4: add RCS keyword status messageBen Keene via GitGitGadget, Feb 14, 2020

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.