checkpoint resume: fall back to remote branches in auto-detection · Entire
checkpoint resume: fall back to remote branches in auto-detection
8828752→main·
pfleidi·1w ago·2 files·+46 added/-10 removed
Resuming another machine's work usually means the branch only exists on origin. When a target matches no checkpoint, local branch, or commit, check the remote and hand off to the branch flow, which offers to fetch it. Remote branches are tried only after commit resolution so revision syntax like HEAD cannot be misrouted into the branch flow.
Sessions
01KX4D7RQN0TKWZFESR1N8PZTHView transcript
Changes
2
cmd/entire/cli
Mcheckpoint_resume.go+13/-10
Mcheckpoint_resume_test.go+33
34 unmodified lines
The target can be a checkpoint ID (or prefix), a commit SHA (or ref) whose message carries an Entire-Checkpoint trailer, or a branch name. Auto-detection tries checkpoint ID first, then local branch, then commit; use the flags to force one interpretation. tries checkpoint ID first, then local branch, then commit, then remote branch (offering to fetch it); use the flags to force one interpretation.
For a checkpoint or commit target, the branch containing the checkpoint's commit is checked out at its current tip before the session logs are
// resumeAutoTarget resolves a positional target, trying in order: local
// checkpoint-ID prefix, local branch, remote checkpoint fallback, commit
// revision. The local branch check runs before the remote checkpoint fetch so
// branch names never pay a network round-trip. Only local branches are
// auto-detected as branch targets: branchCommit also resolves origin/<name>
// and, for a target like "HEAD", would wrongly match refs/remotes/origin/HEAD,
// misrouting revision syntax that must fall through to commit resolution
// instead (--branch still handles remote-only branches explicitly via
// runResume). A lookup swapped in by the remote fallback is closed here; the
// caller keeps ownership of the lookup it passed in.
// revision, and finally remote branch — resuming another machine's work
// usually means the branch isn't local yet, so runResume offers to fetch it.
// The local branch check runs before the remote checkpoint fetch so branch
// names never pay a network round-trip, and remote branches are tried only
// after commit resolution so revision syntax like HEAD (which would match
// refs/remotes/origin/HEAD) cannot be misrouted into the branch flow. A
// lookup swapped in by the remote fallback is closed here; the caller keeps
// ownership of the lookup it passed in.
func resumeAutoTarget(ctx context.Context, cmd *cobra.Command, lookup *explainCheckpointLookup, target string, force bool) error {
// Targets that can't be checkpoint IDs (e.g. "feature/foo") skip the
// store lookup and its remote-fetch fallback entirely.
err := resumeCommitTarget(ctx, cmd, lookup, target, force)
if errors.Is(err, errNoResumeCommit) {
if remoteExists, remoteErr := BranchExistsOnRemote(ctx, target); remoteErr == nil && remoteExists {
return runResume(ctx, cmd, target, force)
}
return fmt.Errorf("nothing matched %q as a checkpoint ID, branch, or commit\nHint: run 'entire checkpoint list' to see available checkpoints", target)
}
return err
}
Mcmd/entire/cli/checkpoint_resume.go+13/-10
15 unmodified lines
"github.com/spf13/cobra"
"github.com/go-git/go-git/v6"
"github.com/go-git/go-git/v6/config"
"github.com/go-git/go-git/v6/plumbing"
"github.com/go-git/go-git/v6/plumbing/object"
)
246 unmodified lines
// A target that is neither a checkpoint, local branch, nor commit must fall
// back to remote branches: resuming another machine's work usually means the
// branch only exists on origin. --force skips the fetch confirmation.
func TestCheckpointResumeAuto_RemoteBranchFallback(t *testing.T) {
testutil.IsolateGitConfigEnv(t)
repo, _, _ := setupCheckpointResumeRepo(t)
originDir := t.TempDir()
testutil.InitRepo(t, originDir)
testutil.WriteFile(t, originDir, "f.txt", "remote content")
testutil.GitAdd(t, originDir, "f.txt")
testutil.GitCommit(t, originDir, "remote work")
branchCmd := exec.CommandContext(context.Background(), "git", "branch", "remote-feature")
branchCmd.Dir = originDir
if out, err := branchCmd.CombinedOutput(); err != nil {
t.Fatalf("git branch: %v\n%s", err, out)
}
if _, err := repo.CreateRemote(&config.RemoteConfig{Name: "origin", URLs: []string{originDir}}); err != nil {
t.Fatalf("create remote: %v", err)
}
cmd, out := newCheckpointResumeTestCmd(t)
cmd.SetArgs([]string{"remote-feature", "--force"})
if err := cmd.Execute(); err != nil {
t.Fatalf("Execute() error = %v\noutput: %s", err, out.String())
}
branch, err := GetCurrentBranch(context.Background())
if err != nil || branch != "remote-feature" {
t.Errorf("current branch = %q err = %v, want remote-feature", branch, err)
}
}
func TestCheckpointResumeCommit_NoTrailer(t *testing.T) {
tmpDir := t.TempDir()
t.Chdir(tmpDir)