mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-11 09:25:35 +08:00
Git exports repository-local variables (GIT_DIR, GIT_WORK_TREE,
GIT_INDEX_FILE, GIT_COMMON_DIR, ...) into hooks, and some editors export
them into their terminals. They take precedence over `git -C <dir>`, and
every git child cbm spawned inherited them. A cbm started from such an
environment therefore resolved every project against the CALLER's
repository: git_context reported a plain directory as is_git=true with
the caller's worktree root, branch and HEAD, the history pass attributed
the caller's commits to it, and the watcher / detect_changes /
artifact git calls read (and `config merge.ours.driver` could write) the
wrong repository.
Fix: one production helper, src/foundation/git_env.{c,h}, holds the
15-variable list `git rev-parse --local-env-vars` prints and builds the
child environment with exactly those removed -- a POSIX envp array or a
Windows CREATE_UNICODE_ENVIRONMENT block (case-insensitive names). It is
applied per spawn; cbm's own environment is never modified (no unsetenv
in the parent, which would race other threads):
- cbm_popen_git(cmd): read-mode popen for git command lines. POSIX spawns
/bin/sh -c via posix_spawn with the scrubbed envp (stdin /dev/null,
pipe ends close-on-exec, reaped by cbm_pclose); Windows passes the block
to the existing handle-isolated CreateProcessW spawn. Fails closed.
- cbm_proc_opts_t.strip_git_repo_env: cbm_subprocess_spawn passes the
scrubbed envp to posix_spawnp / the fork+exec child, or the block to
CreateProcessW. Fails closed.
- Every git spawn uses them: git_context, pass_githistory, artifact
(4 sites), the watcher's git runner, and detect_changes' git commands.
search_code's grep scan keeps the inherited environment.
Tests (RED before, green after, RED on revert of the helper and of the
call sites):
- git_context::git_context_ignores_inherited_repo_env -- GIT_DIR and the
hook set pointing at a decoy repo: a plain dir is is_git=false, a
fixture repo reports its own HEAD and branch, the decoy is untouched.
- git_context::githistory_ignores_inherited_repo_env -- the history pass
on a plain dir sees 0 commits (was the decoy's 4).
- subprocess::subprocess_strip_git_repo_env_is_per_child and
popen_git_strips_repo_env_and_reports_exit_status -- all 15 variables
are gone in the child, unrelated variables pass through, the parent
environment is unchanged, and cbm_pclose keeps pclose() status
semantics.
Proof (macOS arm64, GIT_DIR/GIT_WORK_TREE/GIT_INDEX_FILE/GIT_PREFIX
exported at a decoy clone, `cli index_status verbose=true`): a plain dir
went from is_git=true, worktree_root=<decoy>, branch=<decoy's> (v0.11.0)
to is_git=false; a fixture repo went from the decoy's root/branch/HEAD
to its own root and branch.
Refs #2003
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
48 lines
2.0 KiB
C
48 lines
2.0 KiB
C
/*
|
|
* git_env.h — the environment cbm hands to its OWN git child processes.
|
|
*
|
|
* Git exports repository-local variables into hooks and some editors export
|
|
* them into their terminals (GIT_DIR, GIT_WORK_TREE, GIT_INDEX_FILE, ...). They
|
|
* take precedence over `git -C <dir>`: a cbm started from such an environment
|
|
* would otherwise resolve every indexed project against the CALLER's repository
|
|
* (a non-git directory reported as is_git=true, history and branch data from
|
|
* the wrong repo). Every git spawn therefore gets a child environment with
|
|
* exactly the variables `git rev-parse --local-env-vars` prints removed. The
|
|
* scrub is per spawn: cbm's own environment is never modified (unsetenv in the
|
|
* parent would race every other thread reading the environment).
|
|
*/
|
|
#ifndef CBM_GIT_ENV_H
|
|
#define CBM_GIT_ENV_H
|
|
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
|
|
#ifdef _WIN32
|
|
#include <wchar.h>
|
|
#endif
|
|
|
|
/* `git rev-parse --local-env-vars` (git 2.50) — the single source of truth. */
|
|
enum { CBM_GIT_REPO_ENV_VAR_COUNT = 15 };
|
|
extern const char *const cbm_git_repo_env_vars[CBM_GIT_REPO_ENV_VAR_COUNT];
|
|
|
|
/* True when a "NAME=value" environment entry names one of the variables above.
|
|
* The name compare is case-insensitive on Windows (its environment is). */
|
|
bool cbm_git_env_entry_is_repo_local(const char *entry);
|
|
|
|
#ifdef _WIN32
|
|
/* A CREATE_UNICODE_ENVIRONMENT block: the current environment minus the
|
|
* repository-local git variables. One allocation; release with cbm_git_child_env_free().
|
|
* NULL on allocation/snapshot failure. */
|
|
wchar_t *cbm_git_child_env_block(void);
|
|
#else
|
|
/* A NULL-terminated envp: the current environment minus the repository-local
|
|
* git variables. Pointers and strings live in ONE allocation; release with
|
|
* cbm_git_child_env_free(). NULL on allocation failure. */
|
|
char **cbm_git_child_envp(void);
|
|
#endif
|
|
|
|
/* Release a block from cbm_git_child_env_block / cbm_git_child_envp. NULL is a no-op. */
|
|
void cbm_git_child_env_free(void *env);
|
|
|
|
#endif /* CBM_GIT_ENV_H */
|