Skip to content
GhostbranchdocsGitHub

Updating an open PR

Answer a review by promoting again, and bring in commits someone made on GitHub.

A reviewer asks for a change. How you make it depends on what you chose when apply published the feature:

  • Archive (the default): the feature was handed off, and its pull request is now its home. Commit the fix on the pull request’s branch and push it there with plain git: git fetch origin add-search, git switch --detach origin/add-search, commit, then git push origin HEAD:add-search. No gate, no leak scan, no plan and no code. promote, gate and feature pull refuse an archived feature with FEATURE_ARCHIVED, and next gives these steps. See After publishing: Archive or Keep.
  • Keep: the feature stays private, and you update its pull request the way you made it, as the rest of this page shows.

For a kept feature, commit on the feature branch, push to the shadow repo, gate, and promote. Promote sees that the feature already has an open pull request and updates it, instead of opening another. The examples update pull request #26 of add-search, which was kept. The part on commits made on GitHub comes from an earlier run, pull request #23.

To hand a kept feature off later, run ghostbranch feature archive add-search. It needs the pull request to be open.

Once you push a new commit to the feature, the pull request no longer has everything the feature has. status shows the feature as pr_behind:

Terminal
ghostbranch status
you/project -> you/project-shadow: shadow main up to date, overlay current, scheduled sync off
NAME STATE DRIFT GATE PR
add-search pr_behind clean passed (stale) #26 (behind)
Next:
ghostbranch gate add-search --repo you/project
it has commits its open PR doesn't; gate them, then promote to update the PR

That run had other features too: their rows and Next lines are left out above, and the columns closed up. Gate it and plan the update as before: ghostbranch gate add-search, then ghostbranch promote add-search --plan.

An update reaches the pull request in one of two ways, and promote picks it for you:

  • Append. If you only added commits, they’re added on top of the pull request’s branch. Nothing is force-pushed, and reviewers see just the new commits.
  • Replace. If the history changed, the branch is replaced. Promote pushes it with a lease on the head the plan saw, so if anything else moved the branch in the meantime, the push fails and nothing is lost.

The history counts as changed after a rebase or restack, a reworded message, a conflict with the pull request’s branch, a changed base, or a squash or curated publish, which builds new commits every time. A rebase onto a newer main replaces too, even when no commit changed.

The approval summary says which, on its first line:

Terminal
an append
Update PR #26 (adds 1 commit) in you/project:
a replace
Replace PR #26's branch (history changed: squash) in you/project:

These two lines are the format Ghostbranch prints, not a captured run. The rest of the summary is as for a first publish, except that an update keeps the pull request’s title and body as they are on GitHub, unless you pass --title or --body again. Every update needs the same approval as the first publish: your code, or an agent grant.

Here is an append, and then a replace of the same pull request by a squash update:

Terminal
ghostbranch apply 20261013T080434Z-052401 --yes
progress, on stderr
Reading you/project and you/project-shadow from GitHub
Updating PR #26 on you/project (append)
add-search: 317ed6cd409d → 0833548611eb, leased on 317ed6cd409d
ok: Updated PR #26 on you/project (append)
result
Updated PR #26 on add-search: https://github.com/you/project/pull/26
Next:
ghostbranch sync
a maintainer reviews and merges PR #26 on GitHub; then sync closes the feature
ghostbranch apply 20261013T080641Z-c40e76 --yes
progress, on stderr
Reading you/project and you/project-shadow from GitHub
Updating PR #26 on you/project (replace)
add-search: 0833548611eb → c29bdd4c2ac2, leased on 0833548611eb
ok: Updated PR #26 on you/project (replace)
result
Replaced PR #26's branch add-search: https://github.com/you/project/pull/26
Next:
ghostbranch sync
a maintainer reviews and merges PR #26 on GitHub; then sync closes the feature

An agent ran these under a grant, hence --yes. The replace was planned with ghostbranch promote add-search --mode squash -m "…" --plan, which ran the gate again first (“The gate for add-search is missing or stale, so it runs first”): the last gate had scanned the commits for a preserve publish.

With --json, the plan says which too: publish.update is true, publish.style is append or replace, replace_reason says why (squash here), and expected_head is the head the lease checks.

In a stack, promote add-search also updates the open pull requests above it, under the same approval, bottom up. A pull request in a stack, or with open pull requests above it, is always replaced, never appended. Restack and gate up first: promote says so with NOT_A_STACK if you didn’t.

If one of the pushes fails, the ones below it stay updated, and running apply again carries on from it.

Sometimes a commit lands on the pull request’s branch on GitHub itself: a maintainer’s fix, an accepted review suggestion, or the “Update branch” button. Ghostbranch never overwrites those. status shows the pull request as #23 (changed on GitHub), and promote refuses to build an update that would drop them:

Terminal
ghostbranch promote add-search --plan
error: public PR #23's branch add-search holds 1 commit(s) Ghostbranch didn't publish (ddbc673778ff); an update would drop them, so nothing was built. Bring them into add-search with ghostbranch feature pull add-search, then gate and promote again [PUBLIC_BRANCH_CHANGED]
Next:
ghostbranch feature pull add-search --repo you/project
someone pushed commits to add-search's public PR on GitHub (error.details.foreign); bring them into the feature branch: an update never overwrites them
ghostbranch gate add-search --repo you/project
then gate it, and promote again: the update keeps their commits

ghostbranch feature pull <feature> brings those commits into the feature’s branch in the shadow repo, and replays your later commits on top. --plan shows the branch it would move first:

Terminal
ghostbranch feature pull add-search
Brought 1 commit(s) from the public PR into add-search:
ddbc673778ff Add a note from review (you <12345+you@users.noreply.github.com>)
add-search moved 8794834e2f64 -> 684d2349ec83 in the shadow repo: in your checkout, git fetch, then git reset --keep <remote>/add-search.
Next:
git fetch shadow
in your checkout: fetch the feature branch pull rewrote in the shadow repo
git reset --keep shadow/add-search
with add-search checked out, move it to the pulled branch (--keep refuses rather than lose uncommitted changes)
ghostbranch gate add-search --repo you/project
the PR's commits are in the feature branch now; gate it, then promote: the update keeps them

What happens to the pulled commits:

  • They keep their authors, whatever promote.author_email says. A squash publish credits them with a Co-authored-by: trailer, when their author isn’t the publishing account.
  • They’re already public, so the next gate doesn’t scan them again. Your own commits are scanned as always.
  • They’re protected: a later update that would drop them is refused.

After the pull above, a gate and a squash promote replaced the pull request’s branch once more, and the commit made on GitHub was folded into it.

A merge commit, such as one from “Update branch”, or a conflict stops feature pull, and next gives the git steps instead: fetch the pull request’s branch from origin, merge it into the feature, push the feature to the shadow repo, and run feature pull again. Ghostbranch never resolves a conflict.

feature pull only works on a feature with an open pull request:

Terminal
ghostbranch feature pull add-search --plan
error: feature pull brings in commits from a feature's open public PR, and add-search has none (never published, its PR merged or closed, or its branch gone) [BAD_FLAG]

When the pull request moves after the plan

Section titled “When the pull request moves after the plan”

If the pull request’s branch moves between promote --plan and apply, apply refuses with PLAN_STALE (exit 6), and public_branch is in error.details.moved. Plan again.

PUBLIC_BRANCH_CHANGEDexit 4The open PR's public branch holds commits Ghostbranch didn't publish (error.details.foreign), so the PR was not updated: the update would drop them. error.details.reason, when set, names a special case.
Causes
  • someone pushed a commit to the PR's branch
  • a review suggestion was committed on GitHub
  • Update branch on GitHub merged main into the PR's branch
  • the branch was force-pushed outside Ghostbranch
  • unrecorded_update: the branch is at an update Ghostbranch pushed whose record was never written (error.details.plan); nothing is foreign
  • adopted_missing: a preserve update would drop commits feature pull brought in from GitHub that the feature no longer has (error.details.foreign)
What to do

Bring them into the feature with ghostbranch feature pull <feature>, then gate and promote again. They are already public, so the gate doesn't scan them again; it scans your commits as always. If feature pull can't replay them cleanly, it stops with the git steps to merge them in yourself. For unrecorded_update, run ghostbranch apply <error.details.plan> on the machine that made the plan: it finds the push landed and writes the record (never feature pull, which would copy Ghostbranch's own commits into the feature). For adopted_missing, cherry-pick the listed commits back onto the feature, push it to the shadow repo, then gate and promote again. Ghostbranch never resolves conflicts, and never overwrites commits made on GitHub. error.details.foreign lists each commit's sha, author and subject.

FEATURE_ARCHIVEDexit 2This feature was handed off to the public repo (archived): its shadow branch is archived, so promote, gate, feature pull and waivers add no longer work on it, and feature start --from and feature restack --onto can't build on it (error.details.feature, error.details.public_pr, error.details.branch; error.details.pr_state says whether the PR is still open, merged or closed).
Causes
  • apply archived it after publishing (Archive is apply's default)
  • ghostbranch feature archive archived it
What to do

When feature start --from or feature restack --onto refused it (error.field from or onto), start from main or restack onto main instead, after ghostbranch sync if pr_state is merged. Otherwise, fix or change its PR by pushing to the PR's branch: git fetch origin <error.details.branch>, commit on origin/<error.details.branch> (never on the old shadow branch: the guard refuses its private commits), then git push origin HEAD:<error.details.branch>. When the PR merges, ghostbranch sync closes the feature; once it merged (pr_state merged) run sync, and once it was closed without merging (pr_state closed) a person closes the feature at the console. next gives these steps.

NO_OPEN_PRexit 2feature pull and feature archive need a feature's open public PR, and this feature (error.details.feature) has none (error.details.reason; error.details.public_pr once it was published).
Causes
  • not_published: it was never published
  • merged: its PR merged
  • closed: its PR was closed without merging
  • branch_gone: its public branch is gone
What to do

Follow next: for not_published, promote <feature> --plan publishes it; for merged, sync closes the feature; for closed or branch_gone, a person decides whether to promote again (a new PR) or close the feature.

PLAN_STALEexit 6The repos changed since the plan was made.
What to do

Make a new plan with ghostbranch promote <feature> --plan and apply that.

NOTHING_TO_PUBLISHexit 1Nothing would be published (error.details.reason).
Causes
  • no_commits: the feature has no commits of its own yet
  • all_stripped: every commit is empty, or the commits cancel out, once overlay paths and internal sections are stripped (error.details.dropped_empty)
  • an update of an open PR that already holds every change (no reason)
What to do

For no_commits, commit on the feature branch, push it to the shadow repo, then gate. Otherwise there is nothing to do: the feature has no public changes.

REF_MOVEDexit 4A ref changed on the remote while the command ran.
What to do

Run the command again.