Skip to content

feat(box): new worktrees start from origin's main, and local main keeps up - #160

Merged
sean-brydon merged 2 commits into
mainfrom
feat/fresh-main
Oct 19, 2026
Merged

sean-brydon merged 2 commits into
mainfrom
feat/fresh-main

Conversation

@sean-brydon

Copy link
Copy Markdown
Collaborator

A new worktree from main now starts from what origin has, not from whenever the box last pulled. This is the same behaviour as Orca's "Keep Local Main Up to Date", but on by default.

Before: new branches started from local main with no fetch (git worktree add -b x path main). A main checkout that hadn't been pulled in a week meant every new task started a week behind, and git diff main...HEAD in it compared against old history.

Now, before a new branch is made from the default branch, berthd:

  1. Fetches the default branch from origin, with a 20 s timeout. A failed fetch changes nothing and the create carries on.
  2. Fast-forwards local main:
    • If nothing has main checked out, it moves it with a compare-and-swap update-ref.
    • If the main checkout has it, it runs merge --ff-only there, with hooks, autostash, gc and branch merge options off and --no-overwrite-ignore.
  3. Never moves past your work:
    • If the main checkout has uncommitted changes, it is left alone, and the new branch starts from origin/main with --no-track, so its upstream is set by its first push.
    • If local main has commits origin doesn't, it isn't moved, and the new branch starts from those commits.

Only creates from the default branch are affected. Existing branches, PRs and other bases are unchanged. Creates in one repo are serialised so two can't race on index.lock. worktree.created reports what happened as main: { branch, status, behind? }.

Turning it off: "keep_main": false in any config layer (repo, kit or the box's own), or the new Main branch section in Project settings.

The docs cover the config key, the event field and the concept page. I also fixed the sync endpoint's docs, which left out the pull mode. Changelog entry is under Unreleased.

Proof

A real, isolated berthd (own BERTH_HOME, port 7499) against a throwaway origin and clone:

$ berthd location add acme ~/work/acme
Added location acme → ~/work/acme (git repository, 1 worktree(s))

# origin gets a commit the clone has not fetched
origin/main: b9c86c3   local main: 117c331

$ berthd worktree new acme/fix-login --no-setup
Created acme/fix-login at ~/work/acme-fix-login on fix-login
local main now: b9c86c3   fix-login HEAD: b9c86c3
main checkout has feature.txt: yes

# another upstream commit, and an uncommitted edit in the main checkout
$ berthd worktree new acme/fix-signup --no-setup
Created acme/fix-signup at ~/work/acme-fix-signup on fix-signup
origin/main: 31bdeb2   local main: b9c86c3 (left alone)   fix-signup HEAD: 31bdeb2
fix-signup upstream: fatal: no upstream configured for branch 'fix-signup'
main checkout edit kept:  1 file changed, 1 insertion(+)

Tests (internal/box/freshbase_test.go, real git repos):

--- PASS: TestANewBranchStartsFromOriginsMainAndMainCatchesUp (0.80s)
--- PASS: TestNoBaseMeansTheMainCheckoutsBranchKeptCurrent (0.78s)
--- PASS: TestAMainCheckoutWithChangesIsLeftAlone (0.76s)
--- PASS: TestLocalCommitsOnMainAreKept (0.75s)
--- PASS: TestMainNobodyHasCheckedOutMovesToo (0.75s)
--- PASS: TestKeepMainOffLeavesMainAsItIs (0.54s)
--- PASS: TestAFetchThatFailsStillMakesTheWorktree (0.57s)
--- PASS: TestOtherBasesAreNotFetched (0.53s)
ok  	github.com/cosscom/shipyard/internal/box	7.334s

Project settings › Main branch (mock mode, e2e/keep-main.spec.ts), on by default, then turned off on this box:

Light Dark
On
Off

Checks

  • go vet ./... passes. go test -race ./... passes except TestAttachPassesShiftEnter, which fails the same way on main with this Mac's tmux 3.7c, so it comes from that tmux, not from this change.
  • App: typecheck:plugins, check:titles, check:csp, check:themes, check:type, check:grid, unit tests (548), build and tsc -p e2e all pass. keep-main.spec.ts passes, 2 tests.
  • docs-site builds.

🤖 Generated with Claude Code

…ps up

Before a new branch is made from the default branch, berthd fetches it and
fast-forwards the local one: a compare-and-swap update-ref when nothing has
it checked out, `merge --ff-only` with hooks and merge options off when the
main checkout does. A checkout with uncommitted changes, or a main with
commits origin doesn't have, is left alone; the branch then starts from
origin/main (with no upstream until its first push) or from the local
commits. A failed fetch changes nothing. worktree.created reports what it
did as "main".

On by default; "keep_main": false in any config layer, or the new Main
branch section in Project settings, turns it off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
berth Ready Ready Preview Oct 19, 2026 9:51am UTC
berth-docs Ready Ready Preview Oct 19, 2026 9:51am UTC

Request Review

@sean-brydon
sean-brydon merged commit b1a52f1 into main Oct 19, 2026
8 checks passed
@linear-code

linear-code Bot commented Oct 19, 2026

Copy link
Copy Markdown

ENG-6427

@sean-brydon sean-brydon mentioned this pull request Oct 19, 2026

This branch was successfully deployed

2 active deployments
Preview – berth-docs — 8acdbba2 Deployed Oct 19, 2026 by vercel[bot]
Preview – berth — 8acdbba2 Deployed Oct 19, 2026 by vercel[bot]
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.

1 participant