Your first feature
Build a feature in private, gate it, and publish it to your public repo as a pull request.
It takes seven steps, from sync to the merge, and each one is a single command. The examples use a public repo called you/project and a feature called add-search.
Before you start
Section titled “Before you start”- Ghostbranch is installed and you’ve logged in.
- You ran
ghostbranch initin a clone of your public repo (Set up a repo). - The repo has at least one GitHub Actions workflow. The gate needs CI to run.
1. Sync
Section titled “1. Sync”Bring the shadow repo up to date with the public repo first, because new features start from shadow main:
Sync only ever fast-forwards the shadow repo. It never writes to the public repo.
2. Start a feature
Section titled “2. Start a feature”This creates the branch in the shadow repo, not in your checkout. Every result ends with Next: the command to run after it, and why.
The “Check it out with” line is the exact command for your checkout. The first time, your checkout has no remote for the shadow repo yet, so the line adds one too:
After that, it’s the shorter form shown above.
3. Commit and push to the shadow repo
Section titled “3. Commit and push to the shadow repo”Run the “Check it out with” line exactly as printed, work on the branch as usual, and push it to the shadow repo:
# run the "Check it out with" line from feature start# edit, test, commitgit push shadow add-searchPush to shadow, never to origin. If you push feature work to the public repo by mistake, the push guard refuses it and nothing is sent.
4. Gate it
Section titled “4. 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:
5. Plan the publish
Section titled “5. Plan the publish”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>".
6. Apply it
Section titled “6. Apply it”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 step 5’s, 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.
7. Merge, then sync
Section titled “7. 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.
Trying it out without publishing
Section titled “Trying it out without publishing”To try the loop without opening a pull request, stop after 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.
What’s next
Section titled “What’s next”- Working with a coding agent: let an agent run this loop for you.
ghostbranch statusshows every feature and where it is in the loop.