Skip to content

feat(init): write a self-scoped .gitignore inside the index root - #270

Merged
sunerpy merged 6 commits into
sunerpy:mainfrom
0717wuwang:feat/init-index-gitignore
Oct 8, 2026
Merged

sunerpy merged 6 commits into
sunerpy:mainfrom
0717wuwang:feat/init-index-gitignore

Conversation

@0717wuwang

@0717wuwang 0717wuwang commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • When CodeGraph creates a genuinely new index root, create a nested .gitignore containing * so the local index stays out of git status without changing the repository's root .gitignore.
  • Support both the default .codegraph root and a safe CODEGRAPH_DIR override through IndexPaths::gitignore().
  • Never backfill an existing index root, preserving unrelated files and any existing .gitignore byte-for-byte.

Filesystem safety

  • Open the canonical project and new index root as no-follow directory capabilities.
  • Create the initial namespace, permanent lock, and nested .gitignore relative to retained directory handles, with atomic create_new and no symlink/reparse following.
  • Revalidate the held exclusive lease and exact permanent-lock identity before Git hygiene.
  • Treat .gitignore creation as best effort: a warning never invalidates an otherwise completed index.

Regression coverage includes replacement races at the project root, index root, permanent lock, and .gitignore entry, plus Git visibility and custom-root behavior.

Verification

  • cargo test -p codegraph-rs --test install_init --locked — 16 passed
  • cargo test -p codegraph-store --locked — passed
  • Windows MSVC checks for x86_64 and aarch64 — passed
  • make check — passed
  • pre-push make pre-ci — passed, including UI 569 tests, viewer bundle byte-check, and release archive smoke

Issue #269 remains open and independent.

Init now drops a .gitignore containing only '*' in the index root (.codegraph by default, or the CODEGRAPH_DIR override). A nested .gitignore scopes only to the directory it sits in, so the project's own root .gitignore is never created or modified and the index disappears from git status on its own.

The path is derived through a new IndexPaths::gitignore() accessor so the CODEGRAPH_DIR override keeps working and no production code reconstructs .codegraph* strings.

The write is idempotent (an existing file is left byte-for-byte untouched) and runs on both exit paths of init, including the already-initialized early return, so pre-existing indexes are backfilled. A write failure logs a warning without failing the command: git never reads the index root as source, so it must not undo an index that index_project already completed.
@sunerpy

sunerpy commented Sep 23, 2026

Copy link
Copy Markdown
Owner

@codex

@sunerpy

sunerpy commented Sep 23, 2026

Copy link
Copy Markdown
Owner

@codex review

@sunerpy sunerpy left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nested ignore file is a useful direction, but two filesystem-safety issues block merging.

  1. crates/codegraph-cli/src/main.rs:1406-1412 checks gitignore.exists() and then calls std::fs::write(). A dangling .codegraph/.gitignore symlink makes exists() return false, and write() follows the link: I reproduced codegraph init creating the link target outside the project, with *\n as its content. That breaks the project-containment invariant (AGENTS.md, invariant 5), and the separate check and write also leave a TOCTOU race. Please use a contained, atomic no-clobber creation path that refuses symlinks/reparse points and cannot follow a replaced parent. ensure_tombstone in crates/codegraph-store/src/uninit.rs:251 already does this for the file itself: symlink_metadata first, then create_new, under the exclusive index lease. Add a regression test proving that a dangling link stays untouched and its external target is never created.

  2. Writing * hides every untracked file in an existing custom index root. With CODEGRAPH_DIR=cache and an existing cache/user-note.txt, init keeps the file, but it drops out of git status. Please either ignore only CodeGraph-owned artifacts, or write a whole-directory * only when init can prove it just created the root as a new, dedicated directory. Add a Git integration test showing that an unrelated file in an existing custom root stays visible in git status --porcelain.

Once those are fixed, please rebase onto current main and update the docs that still tell users to add .codegraph/ to .gitignore by hand: the English and Chinese quick-start and FAQ pages (docs/site/en/guide/quick-start.md, docs/site/guide/quick-start.md, docs/site/en/reference/faq.md, docs/site/reference/faq.md). The "Data and network" pages (docs/site/en/privacy.md, docs/site/privacy.md) list what CodeGraph writes under the index root, so they need the new file too. Then run make check; CI Success is the required check. The install_init and index_paths tests pass with this patch applied on top of current main, but neither covers the two cases above.

- create the nested ignore only for a freshly created leased root\n- refuse raced aliases and leave every existing root untouched\n- cover Git visibility, custom roots, and bilingual user documentation
@sunerpy

sunerpy commented Oct 8, 2026

Copy link
Copy Markdown
Owner

I updated this branch on top of current main and addressed the two requested safety boundaries.

What changed:

  • The nested .gitignore is now created only by the genuinely-new-root lease path, after the exclusive kernel lock has been acquired and corroborated. The CLI no longer performs a post-index exists() / write() backfill.
  • Existing roots are never backfilled, including lockless recovery and pre-existing CODEGRAPH_DIR roots, so unrelated files remain visible to Git.
  • Creation uses symlink_metadata plus create_new; a dangling or raced symlink/reparse entry is refused and never followed. Failure remains best-effort and does not invalidate a completed index.
  • The English and Chinese quick-start, FAQ, privacy, and canonical CLI reference now document the fresh-root-only behavior.

Added coverage proves:

  • fresh default and custom roots contain *\n and stay out of git status;
  • an existing custom root keeps user-note.txt visible;
  • an existing root is not backfilled;
  • an existing file is not overwritten;
  • a dangling link and an alias raced after lock validation stay untouched and never create their external target.

Verification on exact head e944b6dffd285de1c70b0f44e7e710f4d4e327e8:

  • cargo test -p codegraph-rs --test install_init --locked — 16 passed
  • cargo test -p codegraph-store --locked — passed
  • make check — passed
  • pre-push make pre-ci — passed, including UI 569 tests, viewer bundle byte-check, and release archive smoke

Issue #269 is unchanged and remains open; this PR does not close it.

@codecov

codecov Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.17204% with 33 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/codegraph-store/src/index_lease.rs 88.00% 33 Missing ⚠️

❌ Your patch check has failed because the patch coverage (88.17%) is below the target coverage (95.00%). You can increase the patch coverage or adjust the target coverage.

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #270      +/-   ##
==========================================
- Coverage   95.07%   95.06%   -0.02%     
==========================================
  Files         198      198              
  Lines      109033   109293     +260     
==========================================
+ Hits       103659   103894     +235     
- Misses       5374     5399      +25     
Files with missing lines Coverage Δ
crates/codegraph-core/src/index_paths.rs 89.86% <100.00%> (+0.09%) ⬆️
crates/codegraph-store/src/index_lease.rs 88.98% <88.00%> (+0.38%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@sunerpy sunerpy left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two previously blocking issues are resolved on exact head e944b6dffd285de1c70b0f44e7e710f4d4e327e8: nested .gitignore creation is confined to the genuinely new-root lease path with no-follow/no-clobber handling, and existing/custom roots are never backfilled. The requested regression coverage and documentation updates are present. Both CI run 37746280160 (including CI Success) and Docs site run 37746280204 passed. Approved; issue #269 remains open and independent.

CodeGraph Test and others added 2 commits October 8, 2026 17:11
- create the initial namespace, lock, and nested ignore through retained directory handles
- refuse project, index-root, and leaf alias replacement races without external writes
- pin capability filesystem dependencies and document the no-follow boundary

@sunerpy sunerpy left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final approval on exact head f2fba45c445c12a5751544cfe340f8ad8a702705. The review-found parent-path replacement race is closed by capability-relative, no-follow creation from the retained project/root handles; project-root, index-root, lock-entry, and .gitignore-entry races have regression coverage. Local make check and pre-push make pre-ci passed. CI run 37758165611 (including Windows Clippy, Windows Tests, and CI Success) and Docs site run 37758165696 passed.

@sunerpy
sunerpy merged commit c9924e4 into sunerpy:main Oct 8, 2026
12 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.

2 participants