fix: reject an existing bare repository as an init destination - #29
fix: reject an existing bare repository as an init destination#29ashproto wants to merge 1 commit into
Conversation
`initialize_repository` guarded against an existing repository two ways, and a bare one slipped between them: it has no `.git` child, and `rev-parse --is-inside-work-tree` answers `false` inside it. The destination is non-empty, so the user got the "folder is not empty" prompt rather than "already a Git repository" — and on confirming, `git init` ran with the bare repo as its working directory. Verified against real git: it prints "Initialized empty Git repository in .../bare.git/.git/" and creates a nested repository inside the bare one. The bare repo's own HEAD, refs and core.bare survive untouched — `diff -rq` against a backup shows only the added `.git` — so this is a wrong state rather than data loss, but it is exactly what the sibling guard exists to prevent. Probe the destination itself with `rev-parse --resolve-git-dir`. It answers for the path GIVEN and does not walk up to a parent, so a plain folder that merely sits inside a repository is still a valid destination — that case belongs to is_inside_worktree, and a test now pins it. The `.git` check is kept alongside rather than replaced, so nothing that was refused before is accepted now. Reported by Codex review on #26; the code is not part of that PR — it came from 15e51c3 (2026-07-17) and git_ops.rs is untouched there — so it is fixed here. cargo test full workspace green (git-core 185); npm run check 524 files 0 errors; npm test 330 passed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
💡 Codex Reviewgit-it/crates/git-core/src/git_ops.rs Lines 118 to 120 in a6673e8 When the selected parent is itself a bare repository or a normal repository's Line 40 in a6673e8 The upgrade to Vite 8 raises the runtime requirement to Node ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Reported by Codex review on #26 as P2. The code is not part of that PR —
crates/git-core/src/git_ops.rsis untouched by #26, and the guard came from15e51c3(2026-07-17) — so it is fixed here on its own branch, same as #27.The gap
initialize_repositoryguards against an existing repository two ways, and a bare one slips between them:destination.join(".git").exists().gitchildis_inside_worktree(destination)rev-parse --is-inside-work-treesaysfalseinside a bare repoThe destination is non-empty, so the user gets the "folder is not empty" prompt rather than "already a Git repository". On confirming,
git initruns with the bare repository as its working directory.What actually happens
Verified against real git rather than assumed — this is the exact command
git_ops.rsissues,cwdset to a bare repo holding a real branch:So it is a wrong state, not data loss — the bare repo survives intact and the only change is the nested
.git. Worth stating plainly since "mutates a directory that should have been rejected" could read as destructive. It is still exactly what the sibling guard exists to prevent, and the app then reports success and opens the nested repo.The fix
Probe the destination itself with
rev-parse --resolve-git-dir:I characterised the probe against every shape before picking it, because the risk is over-reach — refusing a legitimate destination:
--resolve-git-dir.gitThat last row is the important one:
--resolve-git-diranswers for the path given, so a plain folder that merely sits inside a repository is still a valid destination. Nesting remainsis_inside_worktree's job, and a test now pins that boundary.The
.gitcheck is kept alongside rather than replaced, so nothing refused before is accepted now.The operand is always absolute (
parentisfs::canonicalized before the join), so it cannot be read as a flag — noted in the doc comment since the project's shell-out rule would otherwise want a--, which--resolve-git-dircannot take because it consumes the next argument.Tests (TDD)
initialize_repository_rejects_an_existing_bare_repository— watched fail first, returningOk(initialized: true, existing_entries: 7). Asserts the error and that no nested.gitwas created.initialize_repository_still_accepts_a_plain_empty_folder— the over-reach guard. Passed before the change and after.Both run with
cwdinside a repository (cargo's working dir), so the "already inside a repo" case is exercised naturally.Gates:
cargo testfull workspace green (git-core 185) ·npm run check524 files / 0 errors ·npm test330 passed.🤖 Generated with Claude Code