Skip to content

Rewrite relative alternates so go-git can follow shared clones#1288

Merged
pjbgf merged 3 commits into
mainfrom
alter2
May 28, 2026
Merged

Rewrite relative alternates so go-git can follow shared clones#1288
pjbgf merged 3 commits into
mainfrom
alter2

Conversation

@pjbgf
Copy link
Copy Markdown
Member

@pjbgf pjbgf commented May 28, 2026

https://entire.io/gh/entireio/cli/trails/445

go-git's dotgit.Alternates() strips leading "../" and anchors entries at the filesystem root, so relative objects/info/alternates entries — the shape produced by git clone --shared/--reference — point at non-existent paths. The pre-push checkpoint sync then fails to resolve alternate-resident commits even though git itself reads them.

Wrap both the dotgit and common-git filesystems with an alternates rewriter that resolves relative entries against /objects on read. The rewriter preserves file structure (comments, blanks, absolute entries pass through untouched), caps reads at 4096 bytes, and applies to nested alternate chains via the os-rooted AlternatesFS. Linked worktrees are covered because the wrap applies to the common dir where the alternates file actually lives.

Adds unit tests for the rewriter, integration tests for OpenPath across multiple/nested/linked-worktree relative-alternate layouts, and an e2e test reproducing the shared-clone checkpoint sync failure. Tests that shell out to git get GIT_CONFIG_* isolation so they don't pick up host config.

Support for Windows confirmed via E2E tests:

✓ TestAlternates_RelativeObjectAlternate_CheckpointSync (5.2s)
− TestAttachSessionAddsToExistingCheckpoint
− TestAttachSessionCreatesCheckpoint

Note

Medium Risk
Changes how repositories with alternates are opened for all go-git paths (checkpoint sync, tests using OpenPath); behavior is well-tested but incorrect rewriting could break object resolution for shared or worktree layouts.

Overview
go-git can’t resolve relative objects/info/alternates entries (shared/--reference clones). This PR rewrites those paths on read so checkpoint sync and OpenPath can load objects from alternate stores.

On open, dot-git and common-git billy filesystems are wrapped so objects/info/alternates serves in-memory content with relative lines resolved against <git-dir>/objects, matching Git. Comments, blanks, and absolute paths are unchanged; reads are capped at 4KB. The OS-rooted alternates FS applies the same rewrite for nested …/objects/info/alternates files in alternate chains.

Tests: unit coverage for the rewriter; OpenPath integration for single/multiple/nested relative alternates and linked worktrees; e2e pre-push checkpoint sync with a forced relative alternate. IsolateGitConfigEnv and broader GIT_CONFIG_* filtering stabilize tests that call git via os.Environ().

Reviewed by Cursor Bugbot for commit 8f14f10. Configure here.

Copilot AI review requested due to automatic review settings May 28, 2026 09:26
@pjbgf pjbgf requested a review from a team as a code owner May 28, 2026 09:26
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes go-git repository opening for shared/reference clones that use relative objects/info/alternates paths, so checkpoint sync can resolve commits stored in alternate object directories.

Changes:

  • Adds an alternates rewriter that resolves relative alternates against the owning objects directory.
  • Applies the rewriter to dot-git/common-git filesystems and nested alternate chains.
  • Adds unit, repository-level, strategy, and e2e coverage for relative alternates and Git config isolation.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
cmd/entire/cli/gitrepo/alternates_rewrite.go Adds the filesystem wrapper and rewrite helpers for relative alternates.
cmd/entire/cli/gitrepo/alternates_fs.go Rewrites nested alternate files when go-git follows alternate chains.
cmd/entire/cli/gitrepo/repository.go Wraps dot-git and common-git filesystems during repository open.
cmd/entire/cli/gitrepo/alternates_rewrite_test.go Adds unit coverage for alternates rewriting and read limits.
cmd/entire/cli/gitrepo/repository_test.go Adds OpenPath coverage for single, multiple, nested, and linked-worktree relative alternates.
cmd/entire/cli/testutil/testutil.go Expands Git config isolation helpers for tests.
cmd/entire/cli/strategy/push_common_test.go Applies in-process Git config isolation to fetch/rebase tests.
e2e/tests/alternates_test.go Adds an e2e reproduction for checkpoint sync through a relative alternate.

Comment thread cmd/entire/cli/testutil/testutil.go Outdated
Comment thread cmd/entire/cli/testutil/testutil.go Outdated
Comment thread cmd/entire/cli/gitrepo/alternates_rewrite.go Outdated
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 8f14f10. Configure here.

Comment thread e2e/tests/alternates_test.go Outdated
pjbgf added 3 commits May 28, 2026 11:06
go-git's dotgit.Alternates() strips leading "../" and anchors entries at
the filesystem root, so relative objects/info/alternates entries — the
shape produced by `git clone --shared`/`--reference` — point at
non-existent paths. The pre-push checkpoint sync then fails to resolve
alternate-resident commits even though git itself reads them.

Wrap both the dotgit and common-git filesystems with an alternates
rewriter that resolves relative entries against <root>/objects on read.
The rewriter preserves file structure (comments, blanks, absolute
entries pass through untouched), caps reads at 4096 bytes, and applies
to nested alternate chains via the os-rooted AlternatesFS. Linked
worktrees are covered because the wrap applies to the common dir where
the alternates file actually lives.

Adds unit tests for the rewriter, integration tests for OpenPath across
multiple/nested/linked-worktree relative-alternate layouts, and an e2e
test reproducing the shared-clone checkpoint sync failure. Tests that
shell out to git get GIT_CONFIG_* isolation so they don't pick up host
config.

Assisted-by: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Paulo Gomes <paulo@entire.io>
Entire-Checkpoint: 6de31b509acf
- Don't fall back to the uncapped original alternates file when our
  capped read found no relative entry to rewrite. The past-cap content
  could itself be a relative entry, which go-git would then mangle.
  readAlternatesContent now reports truncation and rewriteRelative-
  Alternates returns ok=true on truncation so the caller always serves
  the capped view.
- Filter every inherited GIT_CONFIG_* variable in GitIsolatedEnv and
  IsolateGitConfigEnv (not just the explicit subset). This includes
  GIT_CONFIG_PARAMETERS, which can inject `git -c` overrides into
  child git invocations and would otherwise survive isolation.
- Use upsertEnv when building cmd.Env in the alternates e2e test so a
  parent ENTIRE_TEST_TTY=1 (e.g. from a CI wrapper) cannot shadow the
  appended ENTIRE_TEST_TTY=0 and put the child into interactive mode.

Assisted-by: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Paulo Gomes <paulo@entire.io>
Entire-Checkpoint: 23224dbe8460
Add cases for a file containing only blank lines (should return not ok)
and blank lines between two relative alternates (should be preserved).

Assisted-by: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Paulo Gomes <paulo@entire.io>
Entire-Checkpoint: 9b750209b4f0
@pjbgf pjbgf merged commit 6ebbc83 into main May 28, 2026
9 checks passed
@pjbgf pjbgf deleted the alter2 branch May 28, 2026 11:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants