1
0
mirror of https://github.com/j178/prek.git synced 2026-04-25 02:11:36 +02:00

Respect GIT_DIR set by git (#1258)

* Respect `GIT_DIR` set by git

* test

* Set GIT_WORK_TREE

* Add test
This commit is contained in:
Jo
2025-12-23 10:48:59 +08:00
committed by GitHub
parent f3de2b2ba6
commit 19dcd5ce73
6 changed files with 102 additions and 116 deletions
+2
View File
@@ -10,6 +10,8 @@ impl EnvVars {
pub const CI: &'static str = "CI";
// Git related
pub const GIT_DIR: &'static str = "GIT_DIR";
pub const GIT_WORK_TREE: &'static str = "GIT_WORK_TREE";
pub const GIT_TERMINAL_PROMPT: &'static str = "GIT_TERMINAL_PROMPT";
pub const SKIP: &'static str = "SKIP";
+1 -7
View File
@@ -286,18 +286,12 @@ fn install_hook_script(
/// The version of the hook script. Increment this when the script changes in a way that
/// requires re-installation.
pub(crate) static CUR_SCRIPT_VERSION: usize = 3;
pub(crate) static CUR_SCRIPT_VERSION: usize = 4;
static HOOK_TMPL: &str = r#"[SHEBANG]
# File generated by prek: https://github.com/j178/prek
# ID: 182c10f181da4464a3eec51b83331688
# Unset GIT_DIR to avoid git taking current directory as the git dir.
# See:
# https://github.com/pre-commit/pre-commit/issues/2295
# https://www.spinics.net/lists/git/msg374197.html
unset GIT_DIR
ARGS=([PREK_ARGS])
HERE="$(cd "$(dirname "$0")" && pwd)"
+22 -6
View File
@@ -9,6 +9,7 @@ use anyhow::{Context, Result};
use clap::{CommandFactory, Parser};
use clap_complete::CompleteEnv;
use owo_colors::OwoColorize;
use prek_consts::env_vars::EnvVars;
use tracing::debug;
use tracing::level_filters::LevelFilter;
use tracing_subscriber::filter::Directive;
@@ -137,7 +138,7 @@ fn setup_logging(level: Level, log_file: LogFile, store: &Store) -> Result<()> {
Ok(())
}
async fn run(mut cli: Cli) -> Result<ExitStatus> {
async fn run(cli: Cli) -> Result<ExitStatus> {
// Enabled ANSI colors on Windows.
let _ = anstyle_query::windows::enable_ansi_colors();
@@ -175,12 +176,24 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
warnings::enable();
}
if cli.command.is_none() {
cli.command = Some(Command::Run(Box::new(cli.run_args.clone())));
}
debug!("prek: {}", version::version());
// If `GIT_DIR` is set, prek may be running from a git hook.
// Git exports `GIT_DIR` but *not* `GIT_WORK_TREE`. Without `GIT_WORK_TREE`, git
// treats the current working directory as the working tree. If prek changes the current
// working directory (with `--cd`), git commands run by prek may behave unexpectedly.
//
// To make git behavior stable, we set `GIT_WORK_TREE` ourselves to where prek is run from.
// If `GIT_WORK_TREE` is already set, we leave it alone.
// If `GIT_DIR` is not set, we let git discover `.git` after an optional `cd`.
// See: https://www.spinics.net/lists/git/msg374197.html
// https://github.com/pre-commit/pre-commit/issues/2295
if EnvVars::is_set(EnvVars::GIT_DIR) && !EnvVars::is_set(EnvVars::GIT_WORK_TREE) {
let cwd = std::env::current_dir().context("Failed to get current directory")?;
debug!("Setting {} to `{}`", EnvVars::GIT_WORK_TREE, cwd.display());
unsafe { std::env::set_var(EnvVars::GIT_WORK_TREE, cwd) }
}
if let Some(dir) = cli.globals.cd.as_ref() {
debug!("Changing current directory to: `{}`", dir.display());
std::env::set_current_dir(dir)?;
@@ -203,7 +216,10 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
}
show_settings!(cli.globals, false);
match cli.command.unwrap() {
let command = cli
.command
.unwrap_or_else(|| Command::Run(Box::new(cli.run_args)));
match command {
Command::Install(args) => {
show_settings!(args);
+62 -5
View File
@@ -1,9 +1,10 @@
use std::process::Command;
use assert_cmd::assert::OutputAssertExt;
use assert_fs::fixture::{FileWriteStr, PathChild, PathCreateDir};
use indoc::indoc;
use prek_consts::CONFIG_FILE;
use prek_consts::env_vars::EnvVars;
use std::process::Command;
use crate::common::TestContext;
use crate::common::cmd_snapshot;
@@ -187,6 +188,8 @@ fn hook_impl_pre_push() -> anyhow::Result<()> {
fn run_worktree() -> anyhow::Result<()> {
let context = TestContext::new();
context.init_project();
context.configure_git_author();
context.disable_auto_crlf();
context.write_pre_commit_config(indoc! { r"
repos:
- repo: local
@@ -197,8 +200,6 @@ fn run_worktree() -> anyhow::Result<()> {
entry: always fail
always_run: true
"});
context.configure_git_author();
context.disable_auto_crlf();
context.git_add(".");
context.git_commit("Initial commit");
@@ -249,6 +250,61 @@ fn run_worktree() -> anyhow::Result<()> {
Ok(())
}
/// Test prek hooks runs with `GIT_DIR` respected.
#[test]
fn git_dir_respected() {
let context = TestContext::new();
context.init_project();
context.configure_git_author();
context.disable_auto_crlf();
context.write_pre_commit_config(indoc! { r#"
repos:
- repo: local
hooks:
- id: print-git-dir
name: Print Git Dir
language: python
entry: python -c 'import os, sys; print("GIT_DIR:", os.environ.get("GIT_DIR")); print("GIT_WORK_TREE:", os.environ.get("GIT_WORK_TREE")); sys.exit(1)'
pass_filenames: false
"#});
context.git_add(".");
let cwd = context.work_dir();
cmd_snapshot!(context.filters(), context.install(), @r#"
success: true
exit_code: 0
----- stdout -----
prek installed at `.git/hooks/pre-commit`
----- stderr -----
"#);
let mut commit = Command::new("git");
commit
.arg("--git-dir")
.arg(cwd.join(".git"))
.arg("--work-tree")
.arg(&**cwd)
.current_dir(context.home_dir())
.arg("commit")
.arg("-m")
.arg("Test commit with GIT_DIR set");
cmd_snapshot!(context.filters(), commit, @r"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
Print Git Dir............................................................Failed
- hook id: print-git-dir
- exit code: 1
GIT_DIR: [TEMP_DIR]/.git
GIT_WORK_TREE: .
");
}
#[test]
fn workspace_hook_impl_root() -> anyhow::Result<()> {
let context = TestContext::new();
@@ -455,6 +511,7 @@ fn workspace_hook_impl_worktree_subdirectory() -> anyhow::Result<()> {
let mut commit = Command::new("git");
commit
.current_dir(cwd.child("worktree"))
.env(EnvVars::PREK_HOME, &**context.home_dir())
.arg("commit")
.arg("-m")
.arg("Test commit from subdirectory")
@@ -494,14 +551,14 @@ fn workspace_hook_impl_no_project_found() -> anyhow::Result<()> {
context.git_add(".");
// Install hook that allows missing config
cmd_snapshot!(context.filters(), context.install(), @r#"
cmd_snapshot!(context.filters(), context.install(), @r"
success: true
exit_code: 0
----- stdout -----
prek installed at `.git/hooks/pre-commit`
----- stderr -----
"#);
");
// Try to run hook-impl from directory without config
let mut commit = Command::new("git");
+14 -98
View File
@@ -38,13 +38,7 @@ fn install() -> anyhow::Result<()> {
# File generated by prek: https://github.com/j178/prek
# ID: 182c10f181da4464a3eec51b83331688
# Unset GIT_DIR to avoid git taking current directory as the git dir.
# See:
# https://github.com/pre-commit/pre-commit/issues/2295
# https://www.spinics.net/lists/git/msg374197.html
unset GIT_DIR
ARGS=(hook-impl --hook-type=pre-commit --script-version=3)
ARGS=(hook-impl --hook-type=pre-commit --script-version=4)
HERE="$(cd "$(dirname "$0")" && pwd)"
ARGS+=(--hook-dir "$HERE" -- "$@")
@@ -84,13 +78,7 @@ fn install() -> anyhow::Result<()> {
# File generated by prek: https://github.com/j178/prek
# ID: 182c10f181da4464a3eec51b83331688
# Unset GIT_DIR to avoid git taking current directory as the git dir.
# See:
# https://github.com/pre-commit/pre-commit/issues/2295
# https://www.spinics.net/lists/git/msg374197.html
unset GIT_DIR
ARGS=(hook-impl --hook-type=pre-commit --script-version=3)
ARGS=(hook-impl --hook-type=pre-commit --script-version=4)
HERE="$(cd "$(dirname "$0")" && pwd)"
ARGS+=(--hook-dir "$HERE" -- "$@")
@@ -119,13 +107,7 @@ fn install() -> anyhow::Result<()> {
# File generated by prek: https://github.com/j178/prek
# ID: 182c10f181da4464a3eec51b83331688
# Unset GIT_DIR to avoid git taking current directory as the git dir.
# See:
# https://github.com/pre-commit/pre-commit/issues/2295
# https://www.spinics.net/lists/git/msg374197.html
unset GIT_DIR
ARGS=(hook-impl --hook-type=post-commit --script-version=3)
ARGS=(hook-impl --hook-type=post-commit --script-version=4)
HERE="$(cd "$(dirname "$0")" && pwd)"
ARGS+=(--hook-dir "$HERE" -- "$@")
@@ -162,13 +144,7 @@ fn install() -> anyhow::Result<()> {
# File generated by prek: https://github.com/j178/prek
# ID: 182c10f181da4464a3eec51b83331688
# Unset GIT_DIR to avoid git taking current directory as the git dir.
# See:
# https://github.com/pre-commit/pre-commit/issues/2295
# https://www.spinics.net/lists/git/msg374197.html
unset GIT_DIR
ARGS=(hook-impl --hook-type=pre-commit --script-version=3)
ARGS=(hook-impl --hook-type=pre-commit --script-version=4)
HERE="$(cd "$(dirname "$0")" && pwd)"
ARGS+=(--hook-dir "$HERE" -- "$@")
@@ -191,13 +167,7 @@ fn install() -> anyhow::Result<()> {
# File generated by prek: https://github.com/j178/prek
# ID: 182c10f181da4464a3eec51b83331688
# Unset GIT_DIR to avoid git taking current directory as the git dir.
# See:
# https://github.com/pre-commit/pre-commit/issues/2295
# https://www.spinics.net/lists/git/msg374197.html
unset GIT_DIR
ARGS=(hook-impl --hook-type=post-commit --script-version=3)
ARGS=(hook-impl --hook-type=post-commit --script-version=4)
HERE="$(cd "$(dirname "$0")" && pwd)"
ARGS+=(--hook-dir "$HERE" -- "$@")
@@ -269,13 +239,7 @@ fn install_with_hooks() -> anyhow::Result<()> {
# File generated by prek: https://github.com/j178/prek
# ID: 182c10f181da4464a3eec51b83331688
# Unset GIT_DIR to avoid git taking current directory as the git dir.
# See:
# https://github.com/pre-commit/pre-commit/issues/2295
# https://www.spinics.net/lists/git/msg374197.html
unset GIT_DIR
ARGS=(hook-impl --hook-type=pre-commit --script-version=3)
ARGS=(hook-impl --hook-type=pre-commit --script-version=4)
HERE="$(cd "$(dirname "$0")" && pwd)"
ARGS+=(--hook-dir "$HERE" -- "$@")
@@ -450,13 +414,7 @@ fn init_template_dir() -> anyhow::Result<()> {
# File generated by prek: https://github.com/j178/prek
# ID: 182c10f181da4464a3eec51b83331688
# Unset GIT_DIR to avoid git taking current directory as the git dir.
# See:
# https://github.com/pre-commit/pre-commit/issues/2295
# https://www.spinics.net/lists/git/msg374197.html
unset GIT_DIR
ARGS=(hook-impl --hook-type=pre-commit --skip-on-missing-config --script-version=3)
ARGS=(hook-impl --hook-type=pre-commit --skip-on-missing-config --script-version=4)
HERE="$(cd "$(dirname "$0")" && pwd)"
ARGS+=(--hook-dir "$HERE" -- "$@")
@@ -493,13 +451,7 @@ fn init_template_dir() -> anyhow::Result<()> {
# File generated by prek: https://github.com/j178/prek
# ID: 182c10f181da4464a3eec51b83331688
# Unset GIT_DIR to avoid git taking current directory as the git dir.
# See:
# https://github.com/pre-commit/pre-commit/issues/2295
# https://www.spinics.net/lists/git/msg374197.html
unset GIT_DIR
ARGS=(hook-impl --hook-type=pre-commit --skip-on-missing-config --script-version=3)
ARGS=(hook-impl --hook-type=pre-commit --skip-on-missing-config --script-version=4)
HERE="$(cd "$(dirname "$0")" && pwd)"
ARGS+=(--hook-dir "$HERE" -- "$@")
@@ -533,13 +485,7 @@ fn init_template_dir() -> anyhow::Result<()> {
# File generated by prek: https://github.com/j178/prek
# ID: 182c10f181da4464a3eec51b83331688
# Unset GIT_DIR to avoid git taking current directory as the git dir.
# See:
# https://github.com/pre-commit/pre-commit/issues/2295
# https://www.spinics.net/lists/git/msg374197.html
unset GIT_DIR
ARGS=(hook-impl --hook-type=pre-commit --config="non-exist-config" --skip-on-missing-config --script-version=3)
ARGS=(hook-impl --hook-type=pre-commit --config="non-exist-config" --skip-on-missing-config --script-version=4)
HERE="$(cd "$(dirname "$0")" && pwd)"
ARGS+=(--hook-dir "$HERE" -- "$@")
@@ -639,13 +585,7 @@ fn workspace_install() -> anyhow::Result<()> {
# File generated by prek: https://github.com/j178/prek
# ID: 182c10f181da4464a3eec51b83331688
# Unset GIT_DIR to avoid git taking current directory as the git dir.
# See:
# https://github.com/pre-commit/pre-commit/issues/2295
# https://www.spinics.net/lists/git/msg374197.html
unset GIT_DIR
ARGS=(hook-impl --hook-type=pre-commit --script-version=3)
ARGS=(hook-impl --hook-type=pre-commit --script-version=4)
HERE="$(cd "$(dirname "$0")" && pwd)"
ARGS+=(--hook-dir "$HERE" -- "$@")
@@ -682,13 +622,7 @@ fn workspace_install() -> anyhow::Result<()> {
# File generated by prek: https://github.com/j178/prek
# ID: 182c10f181da4464a3eec51b83331688
# Unset GIT_DIR to avoid git taking current directory as the git dir.
# See:
# https://github.com/pre-commit/pre-commit/issues/2295
# https://www.spinics.net/lists/git/msg374197.html
unset GIT_DIR
ARGS=(hook-impl --hook-type=pre-commit --cd="project3" --script-version=3)
ARGS=(hook-impl --hook-type=pre-commit --cd="project3" --script-version=4)
HERE="$(cd "$(dirname "$0")" && pwd)"
ARGS+=(--hook-dir "$HERE" -- "$@")
@@ -722,13 +656,7 @@ fn workspace_install() -> anyhow::Result<()> {
# File generated by prek: https://github.com/j178/prek
# ID: 182c10f181da4464a3eec51b83331688
# Unset GIT_DIR to avoid git taking current directory as the git dir.
# See:
# https://github.com/pre-commit/pre-commit/issues/2295
# https://www.spinics.net/lists/git/msg374197.html
unset GIT_DIR
ARGS=(hook-impl project3/ --skip=project2/ --hook-type=pre-commit --script-version=3)
ARGS=(hook-impl project3/ --skip=project2/ --hook-type=pre-commit --script-version=4)
HERE="$(cd "$(dirname "$0")" && pwd)"
ARGS+=(--hook-dir "$HERE" -- "$@")
@@ -774,13 +702,7 @@ fn workspace_install() -> anyhow::Result<()> {
# File generated by prek: https://github.com/j178/prek
# ID: 182c10f181da4464a3eec51b83331688
# Unset GIT_DIR to avoid git taking current directory as the git dir.
# See:
# https://github.com/pre-commit/pre-commit/issues/2295
# https://www.spinics.net/lists/git/msg374197.html
unset GIT_DIR
ARGS=(hook-impl project3/ --hook-type=pre-commit --script-version=3)
ARGS=(hook-impl project3/ --hook-type=pre-commit --script-version=4)
HERE="$(cd "$(dirname "$0")" && pwd)"
ARGS+=(--hook-dir "$HERE" -- "$@")
@@ -1005,13 +927,7 @@ fn workspace_init_template_dir() -> anyhow::Result<()> {
# File generated by prek: https://github.com/j178/prek
# ID: 182c10f181da4464a3eec51b83331688
# Unset GIT_DIR to avoid git taking current directory as the git dir.
# See:
# https://github.com/pre-commit/pre-commit/issues/2295
# https://www.spinics.net/lists/git/msg374197.html
unset GIT_DIR
ARGS=(hook-impl --hook-type=pre-commit --skip-on-missing-config --script-version=3)
ARGS=(hook-impl --hook-type=pre-commit --skip-on-missing-config --script-version=4)
HERE="$(cd "$(dirname "$0")" && pwd)"
ARGS+=(--hook-dir "$HERE" -- "$@")
+1
View File
@@ -2040,6 +2040,7 @@ fn git_commit_a() -> Result<()> {
.arg("-a")
.arg("-m")
.arg("Update file")
.env(EnvVars::PREK_HOME, &**context.home_dir())
.current_dir(cwd);
let filters = context