diff mbox series

[v2] githooks: discuss Git operations in foreign repositories

Message ID pull.1457.v2.git.1673293508399.gitgitgadget@gmail.com (mailing list archive)
State Accepted
Commit 772f8ff826fcb15cba94bfd8f23eb0917f3e9edc
Headers show
Series [v2] githooks: discuss Git operations in foreign repositories | expand

Commit Message

Eric Sunshine Jan. 9, 2023, 7:45 p.m. UTC
From: Eric Sunshine <sunshine@sunshineco.com>

Hook authors are periodically caught off-guard by difficult-to-diagnose
errors when their hook invokes Git commands in a repository other than
the local one. In particular, Git environment variables, such as GIT_DIR
and GIT_WORK_TREE, which reference the local repository cause the Git
commands to operate on the local repository rather than on the
repository which the author intended. This is true whether the
environment variables have been set manually by the user or
automatically by Git itself. The same problem crops up when a hook
invokes Git commands in a different worktree of the same repository, as
well.

Recommended best-practice[1,2,3,4,5,6] for avoiding this problem is for
the hook to ensure that Git variables are unset before invoking Git
commands in foreign repositories or other worktrees:

    unset $(git rev-parse --local-env-vars)

However, this advice is not documented anywhere. Rectify this
shortcoming by mentioning it in githooks.txt documentation.

[1]: https://lore.kernel.org/git/YFuHd1MMlJAvtdzb@coredump.intra.peff.net/
[2]: https://lore.kernel.org/git/20200228190218.GC1408759@coredump.intra.peff.net/
[3]: https://lore.kernel.org/git/20190516221702.GA11784@sigill.intra.peff.net/
[4]: https://lore.kernel.org/git/20190422162127.GC9680@sigill.intra.peff.net/
[5]: https://lore.kernel.org/git/20180716183942.GB22298@sigill.intra.peff.net/
[6]: https://lore.kernel.org/git/20150203163235.GA9325@peff.net/

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
---
    githooks: discuss Git operations in foreign repositories
    
    This is a re-roll of [1]. It incorporates a refined version of Junio's
    suggested improvement[2].
    
    [1]
    https://lore.kernel.org/git/pull.1457.git.1673171924727.gitgitgadget@gmail.com/
    [2] https://lore.kernel.org/git/xmqqwn5wuwvs.fsf@gitster.g/

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1457%2Fsunshineco%2Fhookenv-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1457/sunshineco/hookenv-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/1457

Range-diff vs v1:

 1:  b9a2e23359a ! 1:  d58694a4137 githooks: discuss Git operations in foreign repositories
     @@ Commit message
          Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
      
       ## Documentation/githooks.txt ##
     -@@ Documentation/githooks.txt: Hooks can get their arguments via the environment, command-line
     - arguments, and stdin. See the documentation for each hook below for
     - details.
     +@@ Documentation/githooks.txt: repository. An exception are hooks triggered during a push ('pre-receive',
     + 'update', 'post-receive', 'post-update', 'push-to-checkout') which are always
     + executed in $GIT_DIR.
       
     -+If your hook needs to invoke Git commands in a foreign repository or in a
     -+different working tree of the same repository, then it should clear local Git
     -+environment variables, such as `GIT_DIR`, `GIT_WORK_TREE`, etc., which could
     -+interfere with Git operations in the foreign repository since those variables
     -+will be referencing the local repository and working tree. For example:
     ++Environment variables, such as `GIT_DIR`, `GIT_WORK_TREE`, etc., are exported
     ++so that Git commands run by the hook can correctly locate the repository.  If
     ++your hook needs to invoke Git commands in a foreign repository or in a
     ++different working tree of the same repository, then it should clear these
     ++environment variables so they do not interfere with Git operations at the
     ++foreign location.  For example:
      +
      +------------
      +local_desc=$(git describe)
      +foreign_desc=$(unset $(git rev-parse --local-env-vars); git -C ../foreign-repo describe)
      +------------
      +
     - `git init` may copy hooks to the new repository, depending on its
     - configuration. See the "TEMPLATE DIRECTORY" section in
     - linkgit:git-init[1] for details. When the rest of this document refers
     + Hooks can get their arguments via the environment, command-line
     + arguments, and stdin. See the documentation for each hook below for
     + details.


 Documentation/githooks.txt | 12 ++++++++++++
 1 file changed, 12 insertions(+)


base-commit: a38d39a4c50d1275833aba54c4dbdfce9e2e9ca1

Comments

Preston Tunnell Wilson Jan. 9, 2023, 8:12 p.m. UTC | #1
On Mon, Jan 9, 2023 at 1:45 PM Eric Sunshine via GitGitGadget
<gitgitgadget@gmail.com> wrote:
>  Documentation/githooks.txt | 12 ++++++++++++
>  1 file changed, 12 insertions(+)
>
> diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
> index a16e62bc8c8..62908602e7b 100644
> --- a/Documentation/githooks.txt
> +++ b/Documentation/githooks.txt
> @@ -27,6 +27,18 @@ repository. An exception are hooks triggered during a push ('pre-receive',
>  'update', 'post-receive', 'post-update', 'push-to-checkout') which are always
>  executed in $GIT_DIR.
>
> +Environment variables, such as `GIT_DIR`, `GIT_WORK_TREE`, etc., are exported
> +so that Git commands run by the hook can correctly locate the repository.  If
> +your hook needs to invoke Git commands in a foreign repository or in a
> +different working tree of the same repository, then it should clear these
> +environment variables so they do not interfere with Git operations at the
> +foreign location.  For example:
> +
> +------------
> +local_desc=$(git describe)
> +foreign_desc=$(unset $(git rev-parse --local-env-vars); git -C ../foreign-repo describe)
> +------------

This looks good to me! Thank you! (And I'm sorry about top-posting
*again*! I think I have the hang of it now.)
Jeff King Jan. 11, 2023, 7:01 p.m. UTC | #2
On Mon, Jan 09, 2023 at 07:45:08PM +0000, Eric Sunshine via GitGitGadget wrote:

> Recommended best-practice[1,2,3,4,5,6] for avoiding this problem is for
> the hook to ensure that Git variables are unset before invoking Git
> commands in foreign repositories or other worktrees:
> 
>     unset $(git rev-parse --local-env-vars)
> 
> However, this advice is not documented anywhere. Rectify this
> shortcoming by mentioning it in githooks.txt documentation.
> 
> [1]: https://lore.kernel.org/git/YFuHd1MMlJAvtdzb@coredump.intra.peff.net/
> [2]: https://lore.kernel.org/git/20200228190218.GC1408759@coredump.intra.peff.net/
> [3]: https://lore.kernel.org/git/20190516221702.GA11784@sigill.intra.peff.net/
> [4]: https://lore.kernel.org/git/20190422162127.GC9680@sigill.intra.peff.net/
> [5]: https://lore.kernel.org/git/20180716183942.GB22298@sigill.intra.peff.net/
> [6]: https://lore.kernel.org/git/20150203163235.GA9325@peff.net/

Boy, I'm like a broken record.

The patch here looks good to me. The problem is wider than just hooks,
but it seems like that's going to be a common place for people to get
caught by it. So certainly this is going in the right direction.

The other place I've run into it is writing a script meant to be run as
an external command. E.g., running this:

  git --git-dir=/some/path my-external-command

means that "my-external-command" is going to have $GIT_DIR set. If it
wants to operate on another repository it needs to take care to clear
that from the environment.

-Peff
diff mbox series

Patch

diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index a16e62bc8c8..62908602e7b 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -27,6 +27,18 @@  repository. An exception are hooks triggered during a push ('pre-receive',
 'update', 'post-receive', 'post-update', 'push-to-checkout') which are always
 executed in $GIT_DIR.
 
+Environment variables, such as `GIT_DIR`, `GIT_WORK_TREE`, etc., are exported
+so that Git commands run by the hook can correctly locate the repository.  If
+your hook needs to invoke Git commands in a foreign repository or in a
+different working tree of the same repository, then it should clear these
+environment variables so they do not interfere with Git operations at the
+foreign location.  For example:
+
+------------
+local_desc=$(git describe)
+foreign_desc=$(unset $(git rev-parse --local-env-vars); git -C ../foreign-repo describe)
+------------
+
 Hooks can get their arguments via the environment, command-line
 arguments, and stdin. See the documentation for each hook below for
 details.