Skip to content

fix(init): layer the repo's .gitignore into the init index walk - #332

Merged
zzet merged 1 commit into
zzet:mainfrom
tiendungdev:fix/init-respect-repo-gitignore
Jul 24, 2026
Merged

fix(init): layer the repo's .gitignore into the init index walk#332
zzet merged 1 commit into
zzet:mainfrom
tiendungdev:fix/init-respect-repo-gitignore

Conversation

@tiendungdev

Copy link
Copy Markdown
Contributor

Fixes #324.

Root cause

indexRepoForInit resolved its config with:

cfg, err := config.Load("")

config.Load takes a config file path, not a repo path, so the per-repo
layering never ran and cfg.Index.Exclude came back empty. indexer.New then
hit its own documented fallback:

patterns := idx.config.Exclude
// A nil/empty list from upstream means "no layering was applied"
// (e.g. a direct caller of indexer.New without ConfigManager).
if len(patterns) == 0 {
    patterns = excludes.Builtin
}

cmd/gortex/init.go is exactly that direct caller, so the init walk ran on
excludes.Builtin alone and admitted every gitignored tree that does not happen
to match a builtin pattern. Only the daemon reached the layered list, via
ConfigManager.GetRepoConfigEffectiveExclude.

The admission path never consults .gitignore on its own either —
dirIgnoreFiles is {".gortexignore", ".ignore", ".rgignore"} — so the
.gitignore layer exists solely in internal/config.

Why this is a bug rather than a design choice

RespectGitignore is documented as defaulting to true. The promise held for the
daemon and silently did not hold for init.

Symptom (from #324)

On a repo whose .gitignore excludes tmp/ (holding four linked worktrees) and
a generated output directory:

files
tracked set 2,693
queued by init 15,496 (5.7×)

Still parsing at 18m46s. node_modules/ (29.7k) and .next/ (5.1k) were
correctly skipped because they match builtin patterns — which is what made the
failure look inconsistent rather than absent.

Note for #324 readers: the issue guessed that nested git worktrees were being
treated as admissible repos. That turned out to be a red herring —
LinkedWorktreeRoots only reports already-tracked repos. The worktrees under
tmp/ were swallowed simply because tmp/ was never pruned.

Fix

Resolve the config for the actual root through the same path the daemon uses.
The new initRepoConfig helper:

  • keeps the previous best-effort fallback when the global config cannot be read,
    so an unreadable ~/.gortex/config.yaml still does not block indexing;
  • takes an explicit global path so tests never read the developer's real global
    config.

Tests

cmd/gortex/init_gitignore_test.go:

  • TestInitRepoConfigLayersRepoGitignore — gitignored dirs are excluded, and a
    tracked dir is still walked (so the fix cannot be "exclude everything").
  • TestInitRepoConfigHonoursRespectGitignoreOptOutrespect_gitignore: false
    still walks ignored trees.

Sabotage-verified: reverting the helper to the old config.Load("") behaviour
makes TestInitRepoConfigLayersRepoGitignore fail; restoring it passes.

Verification

gofmt, go vet and go build clean.

Full-package runs on Windows show six failures — TestReviewCLI_AudienceAgent
(path separators), TestComputePurgePlan* and TestDefaultGlobalConfigPath_*
(they read the real HOME rather than the temp one), and TestMatchRepo_SuffixMatch
(needs a running daemon). I confirmed all six fail identically on the base commit
7de88fb without this change, so they are pre-existing environment coupling and
not introduced here.

`gortex init` resolved its config with `config.Load("")`. That argument is a
*config file* path, not a repo path, so the per-repo layering never ran and
`cfg.Index.Exclude` came back empty. `indexer.New` then hit its documented
"no layering was applied" fallback and used `excludes.Builtin` alone, so the
init walk admitted every gitignored tree that does not happen to match a
builtin pattern.

On a repo whose `.gitignore` excludes `tmp/` (holding linked worktrees) and a
generated output directory, init queued 15,496 files against a tracked set of
2,693 — 5.7x — and was still parsing at 18m46s. `node_modules/` and `.next/`
were correctly skipped because they match builtin patterns, which is what made
the failure look inconsistent rather than absent.

`respect_gitignore` is documented as defaulting to true, so this was a gap
between the promise and the init path, not a design choice: only the daemon
reached the layering, through `ConfigManager.GetRepoConfig`.

Resolve the config for the actual root through that same path. The new
`initRepoConfig` helper keeps the previous best-effort fallback when the global
config cannot be read, and takes an explicit global path so tests never read the
developer's real `~/.gortex/config.yaml`.

Fixes zzet#324
@zzet
zzet merged commit 9511c35 into zzet:main Jul 24, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: gortex init parses gitignored trees (git worktrees under an ignored dir, generated output dirs) — 5.7× the admitted file set, 18m+ init

2 participants