Skip to content

feat: inherit sparse-checkout in new worktrees#186

Open
adamwett wants to merge 5 commits into
coderabbitai:mainfrom
adamwett:feat/sparse-checkout-inheritance
Open

feat: inherit sparse-checkout in new worktrees#186
adamwett wants to merge 5 commits into
coderabbitai:mainfrom
adamwett:feat/sparse-checkout-inheritance

Conversation

@adamwett

@adamwett adamwett commented Jun 29, 2026

Copy link
Copy Markdown

Summary

New worktrees can inherit sparse-checkout settings from the worktree that supplies their base ref. This keeps large monorepo worktrees narrow without materializing the full tree first.

  • gtr.sparse.inherit controls inheritance and defaults to true.
  • --sparse and --no-sparse override the setting per command.
  • Cone mode, non-cone patterns, sparse-index mode, and worktree-specific Git config are preserved through Git's native worktree behavior.
  • Explicit opt-outs always produce a full checkout, including when git gtr is run from a sparse worktree.

Implementation

On Git 2.36+, git gtr new resolves the worktree holding --from (falling back to the current worktree), then runs only git worktree add in that source context. Git natively copies its sparse patterns and config.worktree before checkout.

Source selection is deterministic:

  • Git’s own ref resolution determines whether --from names a local branch, remote-tracking branch, tag, or non-symbolic commit-ish.
  • Ambiguous or non-branch refs never borrow sparse settings from a similarly named branch; they use the current-worktree fallback.
  • Remote prefixes are stripped only for a configured/default remote.
  • The current worktree is preferred when the same branch is force-checked out more than once; otherwise ambiguity falls back to a full checkout.
  • Missing, unreadable, or invalid sparse settings fall back safely to a full checkout.

Git 2.17–2.35 retains the existing full-checkout behavior. An explicit --sparse request prints a Git 2.36+ requirement warning.

User experience

# Inherit the sparse slice from my-app.
git gtr new my-app-feature --from my-app

# Force a full checkout.
git gtr new big-refactor --from my-app --no-sparse

# Disable inheritance through repository/team config.
git gtr config set gtr.sparse.inherit false

Validation

  • Full project ShellCheck: passed.
  • Completion generation check: passed.
  • Full BATS suite: 512/512 passed.
  • Focused config/sparse tests under macOS Bash 3.2: 51/51 passed.
  • Git 2.36.6 Alpine: full sparse suite 30/30 passed.
  • Git 2.34.8 Alpine smoke: dense fallback and explicit warning passed.
  • Git 2.17.1 Ubuntu: ref resolution, dense compatibility, and old-Git fast path passed.

No new external dependencies are introduced.

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
    • Worktree creation now supports sparse-checkout inheritance from a base worktree (default via gtr.sparse.inherit).
    • Added git gtr new --sparse / --no-sparse to override sparse behavior per command.
  • Documentation
    • Expanded advanced usage and configuration docs with a new “Sparse-Checkout Inheritance” section, plus updated sample config and setup guidance.
  • Bug Fixes
    • Improved completion suggestions and updated git gtr config boolean handling for gtr.sparse.inherit.
  • Tests
    • Added Bats coverage for sparse inheritance decisions and boolean config parsing.

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.
@adamwett adamwett requested a review from NatoBoram as a code owner June 29, 2026 00:41
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds sparse-checkout inheritance to git gtr new through --sparse, --no-sparse, and gtr.sparse.inherit. Core helpers resolve sparse sources and preserve sparse settings or fall back to full checkout. Completions, help, documentation, configuration examples, and Bats coverage are included.

Changes

Sparse-Checkout Inheritance

Layer / File(s) Summary
Sparse source resolution and worktree creation
lib/core.sh, lib/config.sh
Adds sparse worktree lookup, source selection, Git version checks, full-checkout fallback, configuration parsing, and source-aware worktree creation.
Create command sparse behavior
lib/commands/create.sh, lib/commands/help.sh
Parses sparse flags, applies precedence between flags and configuration, resolves inheritance capability, and validates sparse or full checkout after creation.
Sparse inheritance test coverage
tests/sparse.bats, tests/config.bats
Covers source resolution, cone and non-cone patterns, sparse-index preservation, configuration precedence, overrides, ambiguity, version fallback, and full-checkout behavior.
Completions, documentation, and configuration examples
completions/*, scripts/generate-completions.sh, README.md, docs/*.md, templates/.gtrconfig.example
Adds sparse flags and configuration-key completions, help and setup examples, inheritance documentation, and a sparse configuration template section.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant cmd_create
  participant WorktreeHelpers
  participant Git
  User->>cmd_create: git gtr new --sparse or --no-sparse
  cmd_create->>WorktreeHelpers: resolve sparse source and capability
  WorktreeHelpers-->>cmd_create: source and checkout mode
  cmd_create->>Git: create worktree with sparse context
  Git-->>cmd_create: created worktree
  cmd_create->>Git: validate sparse state or force full checkout
Loading

Poem

🐇 Sparse little paths now hop in a line,
New worktrees inherit settings just fine.
Flags choose the checkout, configs softly guide,
Full trees return when sparse paths hide.
Completions and docs join the ride!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 80.77% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: enabling sparse-checkout inheritance for newly created worktrees.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
tests/sparse.bats (2)

116-154: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Exercise sparse config/flag precedence explicitly.

The command tests cover the default-on path and --no-sparse, but they never assert gtr.sparse.inherit=false or --sparse overriding that config. That leaves the precedence block in lib/commands/create.sh only 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 win

Add a non-cone inheritance test.

This only exercises the core.sparseCheckoutCone=true branch. apply_inherited_sparse() has a separate non-cone path that initializes sparse-checkout without --cone and pipes raw patterns through set --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

📥 Commits

Reviewing files that changed from the base of the PR and between ad7a3c5 and f85e0f4.

📒 Files selected for processing (13)
  • README.md
  • completions/_git-gtr
  • completions/git-gtr.fish
  • completions/gtr.bash
  • docs/advanced-usage.md
  • docs/configuration.md
  • lib/commands/create.sh
  • lib/commands/help.sh
  • lib/config.sh
  • lib/core.sh
  • scripts/generate-completions.sh
  • templates/.gtrconfig.example
  • tests/sparse.bats

Comment thread lib/commands/create.sh Outdated
Comment thread lib/core.sh Outdated
Comment thread lib/core.sh Outdated
Comment thread lib/core.sh Outdated
…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
@adamwett

adamwett commented Jun 29, 2026

Copy link
Copy Markdown
Author

from my agent:

Here's what I fixed against each finding:

core.sh

  • _worktree_path_for_ref (remote ref matching): changed ${ref##/} (last segment only) → ${ref#/} (strip only the remote name), so origin/feature/user-auth now matches the branch feature/user-auth instead of collapsing to user-auth.
  • _resolve_sparse_source (no short-circuit on non-sparse match): a matching-but-non-sparse worktree no longer returns empty — it now falls through to checking the current/top-level worktree. Extracted a _worktree_is_sparse helper.
  • apply_inherited_sparse (Git version guard): added _git_supports_sparse_checkout (Git 2.25+ check). On older clients it falls back to a full git checkout so the worktree is populated rather than left empty.
  • Bonus real bug: the non-cone branch used sparse-checkout init, which defaults to cone mode on modern git — silently breaking non-cone inheritance. Fixed to init --no-cone. (Surfaced by the new non-cone test.)

create.sh

  • Sparse failure now triggers a proper fallback: since the worktree is created with --no-checkout, a failed apply_inherited_sparse previously left it empty. It now disables sparse and does a full checkout, hard-erroring (exit 1) only if even that fails — before copy/hooks/success proceed.

tests/sparse.bats

  • Added: slash-separated remote ref match, non-cone inheritance, gtr.sparse.inherit=false prevents inheritance, and --sparse overrides that config.

All nitpicks were valid and addressed (the non-cone test caught a genuine latent bug, so no skips were warranted).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/config.sh`:
- Around line 223-242: Update cfg_bool’s source-selection loop to skip the
worktree case when the installed Git lacks --worktree support (Git versions
older than 2.20), using the repository’s existing Git version or
capability-checking helpers if available. Ensure unsupported worktree options do
not cause an early return, allowing local, file, global, and system sources to
be checked normally.
🪄 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: d4565dcf-380b-43de-8847-88324c828361

📥 Commits

Reviewing files that changed from the base of the PR and between f85e0f4 and d3e3885.

📒 Files selected for processing (13)
  • README.md
  • completions/_git-gtr
  • completions/git-gtr.fish
  • docs/advanced-usage.md
  • docs/configuration.md
  • lib/commands/create.sh
  • lib/commands/help.sh
  • lib/config.sh
  • lib/core.sh
  • scripts/generate-completions.sh
  • templates/.gtrconfig.example
  • tests/config.bats
  • tests/sparse.bats
✅ Files skipped from review due to trivial changes (5)
  • templates/.gtrconfig.example
  • lib/commands/help.sh
  • docs/advanced-usage.md
  • README.md
  • docs/configuration.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • completions/_git-gtr
  • completions/git-gtr.fish
  • scripts/generate-completions.sh
  • lib/commands/create.sh
  • lib/core.sh

Comment thread lib/config.sh
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