This is an automated email from the ASF dual-hosted git repository.

shahar1 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 49a52987956 Stop version-check hooks from clobbering the committing 
worktree's index (#74115)
49a52987956 is described below

commit 49a52987956c514025171627b19ba536be6d713f
Author: Ash Berlin-Taylor <[email protected]>
AuthorDate: Fri Oct 2 19:27:27 2026 +0100

    Stop version-check hooks from clobbering the committing worktree's index 
(#74115)
    
    This took me a while to track down, but when I ran a prek on a file that
    triggers the supervisor-schema check, the entire git index (i.e. what
    git thinks is changed or not) got completely mangled.
    
    This turns out to be because git commit exports GIT_INDEX_FILE (and
    related variables) to hooks. The supervisor-schema and execution-API
    version checks run `git worktree add` as a child process, so the
    checkout of origin/main wrote into the committing repo/worktree's index
    instead of the temporary worktree's own. Anyone committing a file that
    triggers either hook got origin/main's whole tree silently staged, and
    the commit itself could end up with that tree.
---
 scripts/ci/prek/check_execution_api_versions.py     | 15 ++++++++++++---
 .../ci/prek/check_supervisor_schemas_versions.py    | 11 +++++++++--
 scripts/ci/prek/common_prek_utils.py                | 21 +++++++++++++++++++++
 3 files changed, 42 insertions(+), 5 deletions(-)

diff --git a/scripts/ci/prek/check_execution_api_versions.py 
b/scripts/ci/prek/check_execution_api_versions.py
index 833ce32bcf7..570a7ed1236 100755
--- a/scripts/ci/prek/check_execution_api_versions.py
+++ b/scripts/ci/prek/check_execution_api_versions.py
@@ -30,7 +30,7 @@ import sys
 import tempfile
 from pathlib import Path
 
-from common_prek_utils import console, get_remote_for_main
+from common_prek_utils import console, get_remote_for_main, 
git_env_without_repo_overrides
 
 DATAMODELS_PREFIX = 
"airflow-core/src/airflow/api_fastapi/execution_api/datamodels/"
 VERSIONS_PREFIX = 
"airflow-core/src/airflow/api_fastapi/execution_api/versions/"
@@ -76,12 +76,21 @@ def generate_schema_from_main() -> dict:
     ref = f"{remote}/{target_branch}"
     worktree_path = Path(tempfile.mkdtemp()) / "airflow-main"
     subprocess.run(["git", "fetch", remote, target_branch], 
capture_output=True, check=False)
-    subprocess.run(["git", "worktree", "add", str(worktree_path), ref], 
capture_output=True, check=True)
+    git_env = git_env_without_repo_overrides()
+    subprocess.run(
+        ["git", "worktree", "add", str(worktree_path), ref],
+        capture_output=True,
+        check=True,
+        env=git_env,
+    )
     try:
         return generate_schema(worktree_path)
     finally:
         subprocess.run(
-            ["git", "worktree", "remove", "--force", str(worktree_path)], 
capture_output=True, check=False
+            ["git", "worktree", "remove", "--force", str(worktree_path)],
+            capture_output=True,
+            check=False,
+            env=git_env,
         )
 
 
diff --git a/scripts/ci/prek/check_supervisor_schemas_versions.py 
b/scripts/ci/prek/check_supervisor_schemas_versions.py
index 5e011e8f43f..217b459b09b 100755
--- a/scripts/ci/prek/check_supervisor_schemas_versions.py
+++ b/scripts/ci/prek/check_supervisor_schemas_versions.py
@@ -47,7 +47,7 @@ import sys
 import tempfile
 from pathlib import Path
 
-from common_prek_utils import console, get_remote_for_main
+from common_prek_utils import console, get_remote_for_main, 
git_env_without_repo_overrides
 
 SUPERVISOR_SCHEMAS_PREFIX = "task-sdk/src/airflow/sdk/execution_time/schema/"
 VERSIONS_PREFIX = SUPERVISOR_SCHEMAS_PREFIX + "versions/"
@@ -123,7 +123,13 @@ def dump_snapshot_from_main() -> str:
     ref = f"{remote}/{target_branch}"
     worktree_path = Path(tempfile.mkdtemp()) / "airflow-main"
     subprocess.run(["git", "fetch", remote, target_branch], 
capture_output=True, check=False)
-    subprocess.run(["git", "worktree", "add", str(worktree_path), ref], 
capture_output=True, check=True)
+    git_env = git_env_without_repo_overrides()
+    subprocess.run(
+        ["git", "worktree", "add", str(worktree_path), ref],
+        capture_output=True,
+        check=True,
+        env=git_env,
+    )
     try:
         return dump_snapshot(worktree_path)
     finally:
@@ -131,6 +137,7 @@ def dump_snapshot_from_main() -> str:
             ["git", "worktree", "remove", "--force", str(worktree_path)],
             capture_output=True,
             check=False,
+            env=git_env,
         )
 
 
diff --git a/scripts/ci/prek/common_prek_utils.py 
b/scripts/ci/prek/common_prek_utils.py
index b68ec286611..bbc6e1ecc42 100644
--- a/scripts/ci/prek/common_prek_utils.py
+++ b/scripts/ci/prek/common_prek_utils.py
@@ -901,6 +901,27 @@ def get_imports_from_file(file_path: Path, *, 
only_top_level: bool) -> list[str]
     return imports
 
 
+GIT_REPO_OVERRIDE_VARIABLES = (
+    "GIT_INDEX_FILE",
+    "GIT_DIR",
+    "GIT_WORK_TREE",
+    "GIT_PREFIX",
+    "GIT_COMMON_DIR",
+    "GIT_OBJECT_DIRECTORY",
+    "GIT_ALTERNATE_OBJECT_DIRECTORIES",
+)
+
+
+def git_env_without_repo_overrides() -> dict[str, str]:
+    """
+    Return an environment for git commands that must not act on the repo being 
committed.
+
+    ``git commit`` exports ``GIT_INDEX_FILE`` (and friends) to hooks; a child 
``git worktree add``
+    would otherwise check out into the committing repo's index instead of its 
own.
+    """
+    return {k: v for k, v in os.environ.items() if k not in 
GIT_REPO_OVERRIDE_VARIABLES}
+
+
 def get_remote_for_main() -> str:
     """
     Return the remote name to use when fetching main.

Reply via email to