branch: elpa/magit
commit 72d95d60dd67e7fd9e16f912d956d324827ba7fa
Author: Jonas Bernoulli <[email protected]>
Commit: Jonas Bernoulli <[email protected]>
Use new hook.*.{event,command} variables to call elisp from githooks
After waiting for such functionality for two decades, I came to the
conclusion that this will never be implemented, and finally added a
noisy kludge to get the same functionality anyway. Of course, just
one release later, upstream adds the missing functionality.
Zap the noise, and instead use the new variables. A consequence of
this is that our functionality that depends on githooks can only work
when using Git v2.54.0 or later.
---
docs/magit.org | 81 ++++++++++++++++++++++
docs/magit.texi | 108 +++++++++++++++++++++++++++++
git-hooks/applypatch-msg | 1 -
git-hooks/commit-msg | 1 -
git-hooks/fallthrough | 8 ---
git-hooks/fsmonitor-watchman | 1 -
git-hooks/p4-changelist | 1 -
git-hooks/p4-post-changelist | 1 -
git-hooks/p4-pre-submit | 1 -
git-hooks/p4-prepare-changelist | 1 -
git-hooks/post-applypatch | 1 -
git-hooks/post-checkout | 1 -
git-hooks/post-commit | 13 ----
git-hooks/post-index-change | 1 -
git-hooks/post-merge | 13 ----
git-hooks/post-receive | 1 -
git-hooks/post-rewrite | 13 ----
git-hooks/post-update | 1 -
git-hooks/pre-applypatch | 1 -
git-hooks/pre-auto-gc | 1 -
git-hooks/pre-commit | 1 -
git-hooks/pre-merge-commit | 1 -
git-hooks/pre-push | 1 -
git-hooks/pre-rebase | 1 -
git-hooks/pre-receive | 1 -
git-hooks/prepare-commit-msg | 1 -
git-hooks/proc-receive | 1 -
git-hooks/push-to-checkout | 1 -
git-hooks/reference-transaction | 1 -
git-hooks/sendemail-validate | 1 -
git-hooks/update | 1 -
githooks/config | 6 ++
githooks/magit-run-git-hook | 8 +++
lisp/magit-commit.el | 12 ++--
lisp/magit-git.el | 148 ++++++++++++++++++++++------------------
lisp/magit-mode.el | 2 +-
lisp/magit-process.el | 18 ++---
lisp/magit-wip.el | 5 +-
38 files changed, 300 insertions(+), 160 deletions(-)
diff --git a/docs/magit.org b/docs/magit.org
index 2e566dfb5a..28aed472cf 100644
--- a/docs/magit.org
+++ b/docs/magit.org
@@ -2049,6 +2049,87 @@ Emacs is searching for ~git~.
are doing. And think very hard before adding something; it will be
used every time Magit runs Git for any purpose.
+*** Mapping Git Hooks to Lisp Hooks
+
+Git hooks can be configured to call Lisp hooks, starting with Git
+v2.54.0. The mechanism described here is only used when ~git~ is
+invoked by Magit, and ~git~ is run asynchronously and on the local
+machine.
+
+- User option: magit-run-hooks-from-githooks ::
+
+ This option controls whether Git hooks may run Lisp hooks.
+
+ Magit only defines one such hook mapping, but users can add more.
+
+- User option: magit-user-githook-file ::
+
+ This option controls the location of a user-editable file containing
+ additional hook mappings.
+
+- Variable: magit-githook-directory ::
+
+ The value of this variable is the directory containing files Magit
+ needs to map Git hooks to Lisp hooks.
+
+ This directory must contains two files; ~config~, which maps Git
+ hooks to the Lisp hook ~magit-common-git-post-commit-functions~,
+ and an executable ~magit-run-git-hook~, which is used in that, and
+ potentially other, hook mappings.
+
+ The value of this variable is set by ~magit-process-git-arguments~,
+ when it is first needed. Users can set it to another directory,
+ but that should rarely be necessary. Additional hook mappings
+ should instead be defined in ~magit-user-githook-file~.
+
+- Function: magit-run-git-hook (hook &rest args) ::
+
+ This function runs the Lisp hook HOOK with the specified arguments
+ ARGS. ARGS are the arguments (a possibly empty list of strings),
+ which the Git hook was called with. HOOK is a string naming a hook.
+
+ This function is only intended to be called via ~emacsclient~, by an
+ executable by the same name, which in turn is only intended to be
+ called by Git hooks.
+
+- Variable: magit-common-git-post-commit-functions ::
+
+ This Lisp hook is run by the Git hooks ~post-commit~, ~post-merge~ and
+ ~post-rewrite~. There is not a single Git hook, which is called after
+ a commit is created; to achieve that, all of these hooks have to be
+ used.
+
+Git hooks are documented in [[man:githooks]] Also see [[man:git-hook]] Using
+~magit-common-git-post-commit-functions~ as an example, the following
+is how Magit gets Git to map Git hooks to Lisp hooks.
+
+Iff Magit calls Git asynchronously and on the local machine, it
+injects the global arguments ~-c include.path=/path/to/githooks/config~.
+That file contains:
+
+#+begin_src conf-mode
+ [hook "magit-common-post-commit"]
+ event = post-commit
+ event = post-merge
+ event = post-rewrite
+ command = magit-run-git-hook magit-common-git-post-commit-functions
+#+end_src
+
+Consequently when one of the Git hooks ~post-commit~, ~post-merge~ or
+~post-rewrite~ is triggered, that causes the ~magit-run-git-hook~
+executable to be called with ~magit-common-git-post-commit-functions~
+as the first argument.
+
+That executable used the ~emacsclient~ to call a Lisp function by the
+same name, which takes the name of a Lisp hook as the first argument.
+The executable may receive additional arguments from the Git hook,
+which it passes on to the function. All arguments are strings.
+
+The function ~magit-run-git-hook~ runs the Lisp hook using
+~run-hook-with-args~, passing along all arguments, starting with the
+second. It also arranges for ~message~ to output to standard output,
+so that messages appear in Magit's process buffer.
+
* Inspecting
The functionality provided by Magit can be roughly divided into three
diff --git a/docs/magit.texi b/docs/magit.texi
index 880e789ab4..e6bed2e1f9 100644
--- a/docs/magit.texi
+++ b/docs/magit.texi
@@ -2406,6 +2406,114 @@ are doing. And think very hard before adding
something; it will be
used every time Magit runs Git for any purpose.
@end defopt
+@anchor{Mapping Git Hooks to Lisp Hooks}
+@subsection Mapping Git Hooks to Lisp Hooks
+
+Git hooks can be configured to call Lisp hooks, starting with Git
+v2.54.0. The mechanism described here is only used when @code{git} is
+invoked by Magit, and @code{git} is run asynchronously and on the local
+machine.
+
+@table @asis
+@item @kbd{magit-run-hooks-from-githooks}
+@kindex magit-run-hooks-from-githooks
+This option controls whether Git hooks may run Lisp hooks.
+
+Magit only defines one such hook mapping, but users can add more.
+
+@item @kbd{magit-user-githook-file}
+@kindex magit-user-githook-file
+This option controls the location of a user-editable file containing
+additional hook mappings.
+@end table
+
+@defvar magit-githook-directory
+The value of this variable is the directory containing files Magit
+needs to map Git hooks to Lisp hooks.
+
+This directory must contains two files; @code{config}, which maps Git
+hooks to the Lisp hook @code{magit-common-git-post-commit-functions},
+and an executable @code{magit-run-git-hook}, which is used in that, and
+potentially other, hook mappings.
+
+The value of this variable is set by @code{magit-process-git-arguments},
+when it is first needed. Users can set it to another directory,
+but that should rarely be necessary. Additional hook mappings
+should instead be defined in @code{magit-user-githook-file}.
+@end defvar
+
+@defun magit-run-git-hook (hook &rest args)
+This function runs the Lisp hook HOOK with the specified arguments
+ARGS@. ARGS are the arguments (a possibly empty list of strings),
+which the Git hook was called with. HOOK is a string naming a hook.
+
+This function is only intended to be called via @code{emacsclient}, by an
+executable by the same name, which in turn is only intended to be
+called by Git hooks.
+@end defun
+
+@defvar magit-common-git-post-commit-functions
+This Lisp hook is run by the Git hooks @code{post-commit}, @code{post-merge}
and
+@code{post-rewrite}. There is not a single Git hook, which is called after
+a commit is created; to achieve that, all of these hooks have to be
+used.
+@end defvar
+
+Git hooks are documented in
+@ifinfo
+@ref{githooks,,,gitman,}.
+@end ifinfo
+@ifhtml
+@html
+the <a href="http://git-scm.com/docs/githooks">githooks(1)</a> manpage.
+@end html
+@end ifhtml
+@iftex
+the githooks(1) manpage.
+@end iftex
+ Also see
+@ifinfo
+@ref{git-hook,,,gitman,}.
+@end ifinfo
+@ifhtml
+@html
+the <a href="http://git-scm.com/docs/git-hook">git-hook(1)</a> manpage.
+@end html
+@end ifhtml
+@iftex
+the git-hook(1) manpage.
+@end iftex
+ Using
+@code{magit-common-git-post-commit-functions} as an example, the following
+is how Magit gets Git to map Git hooks to Lisp hooks.
+
+Iff Magit calls Git asynchronously and on the local machine, it
+injects the global arguments @code{-c include.path=/path/to/githooks/config}.
+That file contains:
+
+@example
+[hook "magit-common-post-commit"]
+ event = post-commit
+ event = post-merge
+ event = post-rewrite
+ command = magit-run-git-hook magit-common-git-post-commit-functions
+@end example
+
+Consequently when one of the Git hooks @code{post-commit}, @code{post-merge} or
+@code{post-rewrite} is triggered, that causes the @code{magit-run-git-hook}
+executable to be called with @code{magit-common-git-post-commit-functions}
+as the first argument.
+
+That executable used the @code{emacsclient} to call a Lisp function by the
+same name, which takes the name of a Lisp hook as the first argument.
+The executable may receive additional arguments from the Git hook,
+which it passes on to the function. All arguments are strings.
+
+The function @code{magit-run-git-hook} runs the Lisp hook using
+@code{run-hook-with-args}, passing along all arguments, starting with the
+second. It also arranges for @code{message} to output to standard output,
+so that messages appear in Magit's process buffer.
+
@node Inspecting
@chapter Inspecting
diff --git a/git-hooks/applypatch-msg b/git-hooks/applypatch-msg
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/applypatch-msg
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/commit-msg b/git-hooks/commit-msg
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/commit-msg
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/fallthrough b/git-hooks/fallthrough
deleted file mode 100755
index 51928e7f3d..0000000000
--- a/git-hooks/fallthrough
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env bash
-
-hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)"
-
-if [[ -x "$hook" ]]
-then
- "$hook" "$@"
-fi
diff --git a/git-hooks/fsmonitor-watchman b/git-hooks/fsmonitor-watchman
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/fsmonitor-watchman
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/p4-changelist b/git-hooks/p4-changelist
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/p4-changelist
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/p4-post-changelist b/git-hooks/p4-post-changelist
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/p4-post-changelist
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/p4-pre-submit b/git-hooks/p4-pre-submit
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/p4-pre-submit
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/p4-prepare-changelist b/git-hooks/p4-prepare-changelist
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/p4-prepare-changelist
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/post-applypatch b/git-hooks/post-applypatch
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/post-applypatch
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/post-checkout b/git-hooks/post-checkout
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/post-checkout
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/post-commit b/git-hooks/post-commit
deleted file mode 100755
index e02e5e7c3f..0000000000
--- a/git-hooks/post-commit
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/usr/bin/env bash
-
-if [[ "$INSIDE_EMACS" == *magit ]]
-then
- for arg in "$@"; do args+="\"$arg\""; done
- $GIT_EDITOR --eval \
- "(magit-run-git-hook 'common-post-commit ${args[@]})"
-fi
-
-if [[ -x "$SHADOWED_GITHOOK_DIRECTORY" ]]
-then
- "$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" "$@"
-fi
diff --git a/git-hooks/post-index-change b/git-hooks/post-index-change
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/post-index-change
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/post-merge b/git-hooks/post-merge
deleted file mode 100755
index e02e5e7c3f..0000000000
--- a/git-hooks/post-merge
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/usr/bin/env bash
-
-if [[ "$INSIDE_EMACS" == *magit ]]
-then
- for arg in "$@"; do args+="\"$arg\""; done
- $GIT_EDITOR --eval \
- "(magit-run-git-hook 'common-post-commit ${args[@]})"
-fi
-
-if [[ -x "$SHADOWED_GITHOOK_DIRECTORY" ]]
-then
- "$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" "$@"
-fi
diff --git a/git-hooks/post-receive b/git-hooks/post-receive
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/post-receive
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/post-rewrite b/git-hooks/post-rewrite
deleted file mode 100755
index e02e5e7c3f..0000000000
--- a/git-hooks/post-rewrite
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/usr/bin/env bash
-
-if [[ "$INSIDE_EMACS" == *magit ]]
-then
- for arg in "$@"; do args+="\"$arg\""; done
- $GIT_EDITOR --eval \
- "(magit-run-git-hook 'common-post-commit ${args[@]})"
-fi
-
-if [[ -x "$SHADOWED_GITHOOK_DIRECTORY" ]]
-then
- "$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" "$@"
-fi
diff --git a/git-hooks/post-update b/git-hooks/post-update
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/post-update
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/pre-applypatch b/git-hooks/pre-applypatch
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/pre-applypatch
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/pre-auto-gc b/git-hooks/pre-auto-gc
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/pre-auto-gc
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/pre-commit b/git-hooks/pre-commit
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/pre-commit
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/pre-merge-commit b/git-hooks/pre-merge-commit
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/pre-merge-commit
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/pre-push b/git-hooks/pre-push
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/pre-push
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/pre-rebase b/git-hooks/pre-rebase
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/pre-rebase
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/pre-receive b/git-hooks/pre-receive
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/pre-receive
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/prepare-commit-msg b/git-hooks/prepare-commit-msg
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/prepare-commit-msg
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/proc-receive b/git-hooks/proc-receive
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/proc-receive
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/push-to-checkout b/git-hooks/push-to-checkout
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/push-to-checkout
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/reference-transaction b/git-hooks/reference-transaction
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/reference-transaction
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/sendemail-validate b/git-hooks/sendemail-validate
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/sendemail-validate
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/git-hooks/update b/git-hooks/update
deleted file mode 120000
index 833950e79b..0000000000
--- a/git-hooks/update
+++ /dev/null
@@ -1 +0,0 @@
-fallthrough
\ No newline at end of file
diff --git a/githooks/config b/githooks/config
new file mode 100644
index 0000000000..cc44e82b41
--- /dev/null
+++ b/githooks/config
@@ -0,0 +1,6 @@
+# -*- mode: gitconfig -*-
+[hook "magit-common-post-commit"]
+ event = post-commit
+ event = post-merge
+ event = post-rewrite
+ command = magit-run-git-hook magit-common-git-post-commit-functions
diff --git a/githooks/magit-run-git-hook b/githooks/magit-run-git-hook
new file mode 100755
index 0000000000..228621635b
--- /dev/null
+++ b/githooks/magit-run-git-hook
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+if [[ "$INSIDE_EMACS" == *magit ]]
+then
+ for arg in "$@"; do args+="\"$arg\""; done
+ $GIT_EDITOR --eval "(magit-run-git-hook ${args[@]})"
+else
+ true
+fi
diff --git a/lisp/magit-commit.el b/lisp/magit-commit.el
index 6d1c1541ec..7f7899a0f4 100644
--- a/lisp/magit-commit.el
+++ b/lisp/magit-commit.el
@@ -102,10 +102,14 @@ Also see https://github.com/magit/magit/issues/4132."
(defvar magit-common-git-post-commit-functions nil
"Hook run by Git hooks `post-commit', `post-merge' and `post-rewrite'.
-This hook is run if `magit-overriding-githook-directory' is non-nil.
-The functions are called with the same arguments as the Git hook.
-
-This hook is still experimental.")
+There is not a single Git hook, which is called after a commit is
+created; to achieve that, all of these Git hooks have to be used.
+
+Git hooks are documented in the githooks(5) manpage. This Lisp hook
+is only run if `magit-run-hooks-from-githooks' is non-nil, and Magit
+runs Git asynchronously and on the local machine. The hook functions
+are called with the same arguments as the Git hook; see the mentioned
+manpage for details.")
;;; Popup
diff --git a/lisp/magit-git.el b/lisp/magit-git.el
index b340f98fbf..9a302b9c92 100644
--- a/lisp/magit-git.el
+++ b/lisp/magit-git.el
@@ -155,48 +155,57 @@ option."
:group 'magit-process
:type 'string)
-(defvar magit--githook-directory nil)
+(defcustom magit-run-hooks-from-githooks t
+ "Whether Git hooks may run Lisp hooks.
-(defcustom magit-githook-directory nil
- "Directory containing the Git hook scripts used by Magit.
+By default the Lisp hook `magit-common-git-post-commit-functions' is
+run by the Git hooks `post-commit', `post-merge' and `post-rewrite'.
+Use `magit-user-githook-file' (which see) to define additional hooks.
-No Magit-specific Git hook scripts are used if this is nil, which it
-is the default. This feature is still experimental.
-
-Git does not allow overriding just an individual hook. It is only
-possible to point Git at an alternative directory containing hook
-scripts, using the Git variable `core.hooksPath'. When doing that,
-the hooks located in `$GIT_DIR/hooks' are ignored.
+Git hooks can only run Lisp hooks, if Magit invokes Git asynchronously
+and on the local machine, and at least Git v2.54.0 is used."
+ :package-version '(magit . "4.6.0")
+ :group 'magit-process
+ :type 'boolean)
-If `magit', use the directory containing Git hook scripts distributed
-with Magit. To counteract Git's limited granularity, Magit provides a
-script for every Git hook, most of which only run the respective script
-located in `$GIT_DIR/hooks', provided it exists and is executable.
+(defcustom magit-user-githook-file (locate-user-emacs-file "magit-githooks")
+ "File containing user Git hook to Lisp hook mappings.
-A few Git hooks additionally run Lisp hooks:
+Magit ships with one such mapping, defined in the included file
+\"githooks/config\", which looks like this:
-- `post-commit' runs `magit-git-post-commit-functions'
-- `post-merge' runs `magit-git-post-merge-functions'
-- `post-rewrite' runs `magit-git-post-rewrite-functions'
+[hook \"magit-common-post-commit\"]
+ event = post-commit
+ event = post-merge
+ event = post-rewrite
+ command = magit-run-git-hook magit-common-git-post-commit-functions
-All of these hooks also run `magit-common-git-post-commit-functions'.
-For many uses this hook variable is more useful than the three above.
+That file should not be edited by users, as those edits would be lost
+when Magit is updated; instead the file specified by this option, has
+to be used, to add additional mappings.
-If you want to teach additional Git hooks to run Lisp hooks, you have to
-copy Magit's hook script directory elsewhere, modify the hook scripts in
-question, and point this variable at the used directory.
+See the git-hook(1) and githooks(5) manpages for details about the
+Git part. The command should always use the \"magit-run-git-hook\"
+executable, which takes the name of the Lisp hook to be run as the
+first argument.
-Magit only sets `core.hooksPath' when calling Git asynchronously. Doing
-the same when calling Git synchronously would cause Git and Magit to wait
-on one another."
- :package-version '(magit . "4.5.0")
+See also `magit-run-hooks-from-githooks'."
+ :package-version '(magit . "4.6.0")
:group 'magit-process
- :set (lambda (symbol value)
- (set-default-toplevel-value symbol value)
- (setq magit--githook-directory nil))
- :type '(choice (const :tag "Do not shadow Git's hook directory" nil)
- (const :tag "Use Magit's hook directory" magit)
- (directory :tag "Custom directory")))
+ :type 'file)
+
+(defvar magit-githook-directory nil
+ "Directory containing files Magit needs to map Git to Lisp hooks.
+
+This directory must contains two files; \"config\", which maps Git
+hooks to the `magit-common-git-post-commit-functions' hook, and an
+executable \"magit-run-git-hook\", which is used in that, and
+potentially other hooks.
+
+The value of this variable is set by `magit-process-git-arguments',
+when it is first needed. Users can set it to another directory,
+but that should rarely be necessary. Additional hook mappings
+should instead be defined in `magit-user-githook-file'.")
(defcustom magit-git-global-arguments
`("--no-pager" "--literal-pathspecs"
@@ -407,7 +416,7 @@ is remote."
(let* ((length (length magit-git-global-arguments))
(global (seq-take args length))
(local (seq-drop args length)))
- (when (equal (car local) "-c")
+ (while (equal (car local) "-c")
(setq global (append global (seq-take local 2)))
(setq local (seq-drop local 2)))
(list global local))
@@ -421,31 +430,38 @@ pass arguments through this function before handing them
to Git,
to do the following.
* Prepend `magit-git-global-arguments' to ARGS.
-* If ASYNC is non-nil and `magit-githook-directory' is non-nil
- and valid, set `core.hooksPath' by adding additional arguments to ARGS.
+* If ASYNC is non-nil, potentially add additional arguments to load
+ the hook configuration in \"/path/to/magit/githooks/config\" and/or
+ `magit-user-githook-file'.
* Flatten ARGS, removing nil arguments.
* If `system-type' is `windows-nt', encode ARGS to `w32-ansi-code-page'."
- (let ((githookp (and async (not (file-remote-p default-directory)))))
+ (let ((githookp (and async
+ (magit-git-version>= "2.54")
+ (not (file-remote-p default-directory)))))
(cond
((not githookp))
- (magit--githook-directory)
- ((eq magit-githook-directory 'magit)
- (setq magit--githook-directory
+ (magit-githook-directory)
+ ((and (stringp magit-run-hooks-from-githooks)
+ (file-directory-p magit-run-hooks-from-githooks))
+ (setq magit-githook-directory
(magit-convert-filename-for-git
- (expand-file-name "git-hooks"
- (locate-dominating-file
- (locate-library "magit.el") "git-hooks")))))
- ((and magit-githook-directory
- (file-directory-p magit-githook-directory))
- (setq magit--githook-directory
+ magit-run-hooks-from-githooks)))
+ (magit-run-hooks-from-githooks
+ (setq magit-githook-directory
(magit-convert-filename-for-git
- magit-githook-directory))))
+ (expand-file-name "githooks"
+ (locate-dominating-file
+ (locate-library "magit.el") "githooks"))))))
(setq args
(append magit-git-global-arguments
(and githookp
- magit--githook-directory
- (list "-c" (format "core.hooksPath=%s"
- magit--githook-directory)))
+ magit-githook-directory
+ `("-c" ,(format "include.path=%s/config"
+ magit-githook-directory)
+ ,@(and magit-user-githook-file
+ (file-exists-p magit-user-githook-file)
+ `("-c" ,(concat "include.path="
+ magit-user-githook-file)))))
(flatten-tree args))))
(if (and (eq system-type 'windows-nt) (boundp 'w32-ansi-code-page))
;; On w32, the process arguments *must* be encoded in the
@@ -3086,21 +3102,23 @@ out. Only existing branches can be selected."
;;; Git Hooks
-(defun magit-run-git-hook (githook &rest args)
- (dolist (githook (ensure-list githook))
- (let* ((githook (symbol-name githook))
- (hook (save-match-data
- (if (string-match "\\`common-" githook)
- (intern (format "magit-common-git-%s-functions"
- (substring githook (match-end 0))))
- (intern (format "magit-git-%s-functions" githook))))))
- (when (and (boundp hook)
- (symbol-value hook))
- (magit--client-message "Running %s..." hook)
- (advice-add 'message :override #'magit--client-message)
- (unwind-protect (apply #'run-hook-with-args hook args)
- (advice-remove 'message #'magit--client-message))
- (magit--client-message "Running %s...done" hook))))
+(defun magit-run-git-hook (hook &rest args)
+ "Run the Lisp HOOK with the specified arguments ARGS.
+
+ARGS are the arguments (a possibly empty list of strings), which
+the Git hook was called with. HOOK is a string naming a hook.
+
+This function is only intended to be called via \"emacsclient\", by
+an executable by the same name, which in turn is only intended to
+be called by Git hooks."
+ (setq hook (intern hook))
+ (when (and (boundp hook)
+ (symbol-value hook))
+ (magit--client-message "Running %s..." hook)
+ (advice-add 'message :override #'magit--client-message)
+ (unwind-protect (apply #'run-hook-with-args hook args)
+ (advice-remove 'message #'magit--client-message))
+ (magit--client-message "Running %s...done" hook))
'---) ; Emacsclient insists on printing the value.
(defun magit--client-message (format-string &rest args)
diff --git a/lisp/magit-mode.el b/lisp/magit-mode.el
index 2e81447b65..c068e7f324 100644
--- a/lisp/magit-mode.el
+++ b/lisp/magit-mode.el
@@ -1571,7 +1571,7 @@ mentioned caches completely."
(cond (all
(setq magit-repository-local-cache nil)
(setq magit--host-git-version-cache nil)
- (setq magit--githook-directory nil)
+ (setq magit-githook-directory nil)
(dolist (buffer (buffer-list))
(with-current-buffer buffer
(when (derived-mode-p 'magit-mode)
diff --git a/lisp/magit-process.el b/lisp/magit-process.el
index 3968c4009f..c35cfe1d3b 100644
--- a/lisp/magit-process.el
+++ b/lisp/magit-process.el
@@ -524,27 +524,17 @@ eol conversion."
(default-process-coding-system (magit--process-coding-system)))
(apply #'process-file process infile buffer display args)))
-(defvar magit--shadowed-githook-directory nil)
-
-(defun magit--shadowed-githook-directory ()
- (or magit--shadowed-githook-directory
- (setq magit--shadowed-githook-directory
- (let ((magit-git-global-arguments nil))
- (cl-letf (((symbol-function 'magit-process-environment)
- (lambda () process-environment)))
- (or (magit-get "core.hooksPath")
- (expand-file-name "hooks" (magit-gitdir))))))))
-
(defun magit-process-environment ()
(cond
((file-remote-p default-directory)
`(,@magit-git-environment
,@process-environment))
(`(,@magit-git-environment
- ,@(and magit--githook-directory
+ ,@(and magit-githook-directory
(not (file-remote-p default-directory))
- (list (concat "SHADOWED_GITHOOK_DIRECTORY="
- (magit--shadowed-githook-directory))))
+ (list (format "PATH=%s:%s"
+ magit-githook-directory
+ (getenv "PATH"))))
;; The various w32 hacks are only applicable when running on the
;; local machine. A local binding of process-environment different
;; from the top-level value affects the environment used by Tramp.
diff --git a/lisp/magit-wip.el b/lisp/magit-wip.el
index 24a8503c83..e6cdc642f2 100644
--- a/lisp/magit-wip.el
+++ b/lisp/magit-wip.el
@@ -60,10 +60,7 @@ makes it easier to inspect wip history and the wip commits
are
never garbage collected.
If `githook', then use `magit-common-git-post-commit-hook' to
-create the merge commit. This uses the experimental support for
-calling Lisp hooks from Git hooks, which is disabled by default,
-Customize `magit-overriding-githook-directory' to enable use of
-Git hooks."
+create the merge commit."
:package-version '(magit . "2.90.0")
:group 'magit-wip
:type '(choice