feat: inherit sparse-checkout in new worktrees#186
Conversation
When creating a new worktree from one with sparse-checkout enabled, the new worktree inherits the cone pattern automatically. Controlled by gtr.sparse.inherit config (default on) and --sparse/--no-sparse flags. Adds reusable helpers for sparse-checkout replication.
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (3)
WalkthroughAdds sparse-checkout inheritance to ChangesSparse-Checkout Inheritance
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
tests/sparse.bats (2)
116-154: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExercise sparse config/flag precedence explicitly.
The command tests cover the default-on path and
--no-sparse, but they never assertgtr.sparse.inherit=falseor--sparseoverriding that config. That leaves the precedence block inlib/commands/create.shonly partially covered, even though the feature contract depends on it.Suggested test additions
+@test "cmd_create honors gtr.sparse.inherit=false" { + source_gtr_commands + make_sparse_worktree "$TEST_WORKTREES_DIR/base" base apps/web + git -C "$TEST_REPO" config gtr.sparse.inherit false + + run cmd_create feat-config-off --from base --yes --no-fetch --no-hooks --no-copy + [ "$status" -eq 0 ] + + wt="$TEST_WORKTREES_DIR/feat-config-off" + [ -d "$wt/apps/api" ] + [ "$(git -C "$wt" config --bool core.sparseCheckout 2>/dev/null || echo false)" != "true" ] +} + +@test "cmd_create --sparse overrides gtr.sparse.inherit=false" { + source_gtr_commands + make_sparse_worktree "$TEST_WORKTREES_DIR/base" base apps/web + git -C "$TEST_REPO" config gtr.sparse.inherit false + + run cmd_create feat-config-override --from base --sparse --yes --no-fetch --no-hooks --no-copy + [ "$status" -eq 0 ] + + wt="$TEST_WORKTREES_DIR/feat-config-override" + [ "$(git -C "$wt" config --bool core.sparseCheckout)" = "true" ] + [ -d "$wt/apps/web" ] + [ ! -d "$wt/apps/api" ] +}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/sparse.bats` around lines 116 - 154, Add tests in sparse.bats to cover sparse precedence in cmd_create: explicitly verify that gtr.sparse.inherit=false prevents sparse-checkout inheritance from a sparse --from worktree, and that --sparse overrides that config to keep sparse checkout enabled. Reuse the existing cmd_create, make_sparse_worktree, and git -C "$wt" config assertions so the new cases exercise the precedence logic in lib/commands/create.sh alongside the current default-on and --no-sparse coverage.
93-114: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a non-cone inheritance test.
This only exercises the
core.sparseCheckoutCone=truebranch.apply_inherited_sparse()has a separate non-cone path that initializes sparse-checkout without--coneand pipes raw patterns throughset --stdin, so a regression there would currently ship untested.Suggested test shape
+@test "apply_inherited_sparse replicates non-cone patterns into a new worktree" { + git -C "$TEST_REPO" worktree add --quiet -b base-noncone "$TEST_WORKTREES_DIR/base-noncone" HEAD + git -C "$TEST_WORKTREES_DIR/base-noncone" sparse-checkout init --no-cone >/dev/null + printf 'apps/web/*\n!apps/web/file.txt\npackages/*\n' \ + | git -C "$TEST_WORKTREES_DIR/base-noncone" sparse-checkout set --stdin >/dev/null + git -C "$TEST_REPO" worktree add --no-checkout --quiet -b feat-noncone "$TEST_WORKTREES_DIR/feat-noncone" base-noncone + + run apply_inherited_sparse "$TEST_WORKTREES_DIR/feat-noncone" "$TEST_WORKTREES_DIR/base-noncone" + [ "$status" -eq 0 ] + [ "$(git -C "$TEST_WORKTREES_DIR/feat-noncone" config --bool core.sparseCheckout)" = "true" ] + [ "$(git -C "$TEST_WORKTREES_DIR/feat-noncone" config --bool core.sparseCheckoutCone 2>/dev/null || echo false)" != "true" ] + [ "$(git -C "$TEST_WORKTREES_DIR/base-noncone" sparse-checkout list)" = "$(git -C "$TEST_WORKTREES_DIR/feat-noncone" sparse-checkout list)" ] +}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/sparse.bats` around lines 93 - 114, Add a test that covers the non-cone inheritance path in apply_inherited_sparse, since the current sparse.bats case only verifies core.sparseCheckoutCone=true. Create a fixture where the source worktree uses non-cone sparse-checkout patterns, then assert the inherited worktree preserves core.sparseCheckout=true, keeps core.sparseCheckoutCone false, and applies the raw patterns via sparse-checkout set --stdin behavior rather than cone directories. Use apply_inherited_sparse, make_sparse_worktree, and sparse-checkout list to locate and validate the non-cone branch.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@lib/commands/create.sh`:
- Around line 200-206: The sparse-checkout inheritance failure is being
downgraded to a warning in the create_worktree flow, which can incorrectly
continue after create_worktree with no_checkout set and leave a partially
initialized worktree. Update the create.sh logic around apply_inherited_sparse
so that a failure is treated as a hard error (or otherwise triggers a proper
fallback) before the copy/hooks/success path continues, and make sure the
behavior is enforced in the create_worktree/no_checkout branch rather than only
logging through log_warn.
In `@lib/core.sh`:
- Around line 622-630: The worktree selection in the sparse-checkout helper
currently stops at the first matching ref even when that worktree is not
sparse-enabled, causing the fallback path to be skipped. Update the logic in the
helper that uses _worktree_path_for_ref and the core.sparseCheckout check so it
only accepts a matching worktree when sparse checkout is true; otherwise
continue falling back to the current repo/top-level worktree instead of
returning empty. Keep the existing behavior for enabled sparse worktrees, but
ensure non-sparse matches do not short-circuit the search.
- Around line 593-603: The branch matching in the worktree lookup is stripping
too much from remote refs, causing slash-separated names like feature/user-auth
to lose their prefix and fail to match. Update the normalization logic in the
worktree scan around the ref_short/branch comparison so it preserves the branch
path after the remote name instead of using only the last path segment, and keep
the comparison in the same block that reads worktree and branch entries.
- Around line 640-676: The apply_inherited_sparse function currently assumes git
sparse-checkout init/list/set --stdin are available, but that only works on Git
2.25+, so older supported clients can leave the new worktree unmaterialized. Add
a Git version guard in apply_inherited_sparse and, when the src_wt/new_wt
commands are unsupported, either skip sparse inheritance entirely or fall back
to a normal checkout so the new worktree is fully populated. Keep the existing
log_warn paths for real failures, and use the apply_inherited_sparse flow to
locate the compatibility branch.
---
Nitpick comments:
In `@tests/sparse.bats`:
- Around line 116-154: Add tests in sparse.bats to cover sparse precedence in
cmd_create: explicitly verify that gtr.sparse.inherit=false prevents
sparse-checkout inheritance from a sparse --from worktree, and that --sparse
overrides that config to keep sparse checkout enabled. Reuse the existing
cmd_create, make_sparse_worktree, and git -C "$wt" config assertions so the new
cases exercise the precedence logic in lib/commands/create.sh alongside the
current default-on and --no-sparse coverage.
- Around line 93-114: Add a test that covers the non-cone inheritance path in
apply_inherited_sparse, since the current sparse.bats case only verifies
core.sparseCheckoutCone=true. Create a fixture where the source worktree uses
non-cone sparse-checkout patterns, then assert the inherited worktree preserves
core.sparseCheckout=true, keeps core.sparseCheckoutCone false, and applies the
raw patterns via sparse-checkout set --stdin behavior rather than cone
directories. Use apply_inherited_sparse, make_sparse_worktree, and
sparse-checkout list to locate and validate the non-cone branch.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 2aeb9dfc-e4e0-4bd2-8819-9bf9289313c3
📒 Files selected for processing (13)
README.mdcompletions/_git-gtrcompletions/git-gtr.fishcompletions/gtr.bashdocs/advanced-usage.mddocs/configuration.mdlib/commands/create.shlib/commands/help.shlib/config.shlib/core.shscripts/generate-completions.shtemplates/.gtrconfig.exampletests/sparse.bats
…heritance - Preserve slash-separated branch paths in _worktree_path_for_ref - Allow fallback to top-level worktree when matching worktree is not sparse - Add Git 2.25+ guard for sparse-checkout support with full checkout fallback - Fix non-cone mode to use init --no-cone (git defaults to cone mode) - Improve error handling: failed sparse inheritance now falls back to full checkout and hard-errors if that fails, instead of leaving --no-checkout worktree empty - Add tests for slash refs, non-cone inheritance, and sparse config precedence
|
from my agent: Here's what I fixed against each finding: core.sh
create.sh
tests/sparse.bats
All nitpicks were valid and addressed (the non-cone test caught a genuine latent bug, so no skips were warranted). |
Pull Request
full disclaimer: this is entirely ai generated. works for me & my monorepo. thought i'd share in case the maintainers see a valid usecase for this. let me know if you'd like me to provide my claude code convo.
Description
When creating a new worktree from one that has sparse-checkout enabled, the new worktree now inherits the cone pattern automatically. Controlled by the
gtr.sparse.inheritconfig (default on) and--sparse/--no-sparseflags.Mechanism: when the base worktree is sparse,
git gtr newcreates the worktree with--no-checkout(so the full tree is never materialized), then replicates the source's sparse config and patterns — cone mode (sparse-checkout init --cone+set <dirs>) or raw patterns (set --stdin) — and checks out the narrowed working tree.Adds reusable helpers for sparse-checkout replication:
_worktree_path_for_ref— find the worktree that has a given ref checked out_resolve_sparse_source— resolve which worktree's sparse config to inherit (the--frombase worktree, falling back to the current worktree), only when it is sparseapply_inherited_sparse— replicate a source worktree's cone (or pattern set) into a new worktreecreate_worktreegains ano_checkoutparameterMotivation
In large monorepos, a worktree often uses sparse-checkout to materialize only a slice of the tree (e.g. one app + shared packages). Today
git gtr newruns a plaingit worktree add, which always produces a full checkout regardless of the base worktree's sparse state — so branching off a sparse worktree explodes into the entire repo. This change carries the sparse slice into worktrees you branch off it, keeping feature worktrees lean and fast to create.Fixes # (issue)
Type of Change
Testing
Added
tests/sparse.bats(BATS) covering the new helpers, the--sparse/--no-sparse/ config decision precedence, and end-to-endcmd_createinheritance (config replicated, pattern list matches source, working tree narrowed to the cone).Verified on two platforms:
cmd_cleantest that also fails unchanged onmain.shasum, which is absent on Alpine (musl shipssha256sum); these fail identically onmainand are unrelated to this PR.Manual Testing Checklist
Tested on:
Core functionality tested:
git gtr new <branch>- Create worktree (full checkout,--from,--sparse,--no-sparse)git gtr go <branch>- Navigate to worktree (incl.go 1for main repo)git gtr editor <branch>- Open in editor (verified dispatch via--editor)git gtr ai <branch>- Start AI tool (verified dispatch via--ai)git gtr rm <branch>- Remove worktreegit gtr list- List worktreesgit gtr config- Configuration commands (set/get/list, incl.gtr.sparse.inherit)git gtr new --sparse/--no-sparse(also smoke-testedrun,mv,doctor,adapter,completion,init)Test Steps
Against a monorepo whose base worktree uses cone-mode sparse-checkout (only
apps/<one-app>+packagesmaterialized):git gtr new feature-xyz --from <sparse-branch>ls "$(git gtr go feature-xyz)"— only the inherited cone is present; excluded top-level dirs are absent.git -C "$(git gtr go feature-xyz)" sparse-checkout list— matches the source worktree.git gtr new other --from <sparse-branch> --no-sparse— full checkout (opt-out works).Expected behavior:
The worktree created in step 1 has
core.sparseCheckout=true(andsparseCheckoutCone=truefor cone repos), the same pattern list as the source, and a working tree narrowed to that cone — without ever materializing the full tree. Full-checkout repos are unaffected and still get a full checkout.Actual behavior:
As expected. Verified live: the new worktree reported
Inherited sparse-checkout from <source>,core.sparseCheckout=true,sparseCheckoutCone=true, an identicalsparse-checkout list, and the excluded directories were absent from disk.Breaking Changes
Not a breaking change for full-checkout repositories — they are untouched. For repositories whose worktrees already use sparse-checkout, new worktrees branched from a sparse worktree now inherit the cone instead of getting a full checkout. This is opt-out at any time via
--no-sparseor globally viagit gtr config set gtr.sparse.inherit false.Checklist
Before submitting this PR, please check:
git gtr(production) and./bin/gtr(development)Additional Context
Scope of changes (13 files): sparse logic and helpers in
lib/core.sh; flags, decision, and post-create apply inlib/commands/create.sh; thegtr.sparse.inherit↔sparse.inheritkey mapping inlib/config.sh; help text; the--sparse/--no-sparseflags in the completion generatorscripts/generate-completions.shplus the regeneratedcompletions/{gtr.bash,_git-gtr,git-gtr.fish}; README,docs/configuration.md,docs/advanced-usage.md, andtemplates/.gtrconfig.example; plustests/sparse.bats.Implementation notes:
mapfile); allgit configreads are guarded againstset -e.git sparse-checkout setafter--no-checkoutdoes not populate the working tree on its own, soapply_inherited_sparseruns an explicitgit checkoutto materialize the cone.scripts/generate-completions.sh; the new flags were added to the generator and the files regenerated, so./scripts/generate-completions.sh --check(the CI completions check) passes. Regenerating also picked upgtr.sparse.inheritin the config-key completions automatically.cmd_clean --merged uses nested registered worktree pathtest that also fails unchanged onmainin this environment (pre-existing, unrelated to this PR).License Acknowledgment
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache License 2.0.
Summary by CodeRabbit
New Features
Documentation
Bug Fixes