A feature in detail
What each step of the feature loop prints, from sync to the merge, and what to do when feature start only fetches, CI is long, apply asks Archive or Keep, or a review needs a fix.
This page expands Your first feature: what each step prints, and what to do when a step doesn’t go the plain way. The examples use a public repo called you/project and a feature called add-search. The concept pages go further: The gate, Promote and approval, and Merging and closing.
Sync only ever fast-forwards the shadow repo. It never writes to the public repo. Sync has the rest.
Start a feature
Section titled “Start a feature”Run it in your clone of the public repo:
This creates the branch in the shadow repo. Then it gets your checkout ready:
- It adds a remote called
shadowfor the shadow repo, the first time. If a remote already reaches the shadow repo, it uses that one. - It fetches the shadow repo.
- It switches your checkout to the new branch, tracking
shadow/add-search.
Every result ends with Next: the command to run after it, and why.
When it only fetches
Section titled “When it only fetches”feature start never switches when that could lose work or surprise you. In these cases it fetches, leaves your checkout where it is, and says why and what to do:
- Uncommitted or untracked files. Commit or stash them first (untracked files too, with
git stash --include-untracked), thengit switch add-search. - A detached HEAD with commits no branch has. Keep them on a branch first, with
git switch -c <branch-name>, thengit switch add-search. - A local branch called
add-searchalready exists. Ghostbranch leaves it alone. Compare it withshadow/add-search, and switch to the one you mean to work on. - A remote called
shadowthat points somewhere else. Ghostbranch never rewrites a remote. It prints the commands to check the branch out instead. - The fetch failed. The branch exists in the shadow repo either way. If git says the repository was not found, git may be signed in to GitHub as an account that can’t see the shadow repo: Ghostbranch’s own login doesn’t cover git. The message names a
git remote set-urlcommand that fixes it for that remote only.
For example, with uncommitted changes:
Leave the checkout alone
Section titled “Leave the checkout alone”To create the branch without touching your checkout, add --no-checkout. It prints the commands to run instead. In a checkout that already has the shadow remote, that looks like this:
Without the remote, the line adds it first. Outside a clone of the public repo, feature start leaves things alone in the same way, and the line clones the shadow repo instead.
Gate it
Section titled “Gate it”The gate rebases the feature onto public main, scans exactly what would be published for leaks, and runs your CI on the result, in a draft pull request in the shadow repo. Nothing goes public.
ghostbranch gate add-search waits for CI and prints the result. CI can take a while, so you can also start it and come back:
The leak scan has already passed by now: the gate scans before it starts CI. wait finishes the job when CI is done. You can run it from either of your machines, and a job that has already finished answers at once:
The gate explains the rebase, the leak scan and CI parity, and Long CI explains jobs and wait.
The plan
Section titled “The plan”promote --plan builds the public commits on top of public main and saves a plan of exactly what will go public: every commit, file and author, and the pull request’s title and text. It publishes nothing yet.
The default history mode, preserve, publishes each of your commits as it is, messages included. With more than one commit, give the pull request a title with --title. To publish one squashed commit instead, use --mode squash -m "<message>".
Make a plan and History modes have the rest.
Apply: Archive or Keep
Section titled “Apply: Archive or Keep”apply publishes the plan, if nothing has moved since you made it. It shows what will go public on your own terminal, asks what happens to the feature afterwards, and shows a one-time code. Type the code back to confirm:
This run is a later one than the plan above, so its plan ID differs.
The first time you publish, the question just above the code matters most:
- Archive (press Enter) hands the pull request off. The feature’s private branch is archived in the shadow repo, so nothing is lost. From now on you fix the pull request by pushing straight to its branch, with plain git: no gate, no plan, no code.
- Keep (type
k) keeps the feature private. Fixes go through the shadow branch,gateandpromote, as before.
Pick Archive unless you have a reason to keep the work private, such as more private changes still to come. Promote and approval has the details, and ghostbranch feature archive add-search hands a kept feature off later.
If anything moved after the plan was made (public main, the feature, or its gate), apply refuses with exit code 6 and publishes nothing. Make a new plan and apply that one.
Merge, then sync
Section titled “Merge, then sync”The pull request is an ordinary one on your public repo, and it’s reviewed and merged as usual. Merge it with a merge commit or a rebase, not “Squash and merge”, or the commits preserve published are squashed after all.
To answer a review before the merge, push the fix to the pull request’s branch. With Archive:
git fetch origin add-searchgit switch --detach origin/add-search# commit your fix heregit push origin HEAD:add-searchStart from origin/add-search, not your old add-search branch: that one holds the private history, and the push guard refuses it. If you chose Keep, commit on add-search, push to the shadow repo, then gate and promote again: see Updating an open PR.
After the merge, run ghostbranch sync again. It brings the merge into the shadow repo and closes the feature: if nothing changed since you published it, sync deletes what’s left of the feature in the shadow repo, its archive included, and says so.
Merging and closing covers merging a stack, closing a feature yourself, and parking one.
Trying it out without publishing
Section titled “Trying it out without publishing”To try the loop without opening a pull request, stop after the plan (step 5) and abandon the feature. Nothing is deleted, and nothing is published:
ghostbranch feature list hides an abandoned feature, and --undo brings it back. A plan you never apply does nothing. The gate’s draft pull request in the shadow repo stays open; close it on GitHub if you like.