Skip to content
GhostbranchdocsGitHub

The push guard

The git hook that stops feature work from reaching your public repo by a plain git push.

Your checkout has both remotes, origin and shadow, so one wrong git push origin could publish unreleased work without any gate. The guard runs inside every git push, so it catches that push even when you don’t notice the remote’s name.

When you push to the public repo, the guard checks what you’re pushing and refuses:

  • a branch named after one of your features, whatever it holds, except an archived feature’s own pull request branch;
  • any push that carries a feature commit that hasn’t been published, under any branch name;
  • the overlay branch ghostbranch and the gate branches;
  • a pushed ref that isn’t a commit.
Terminal
git push origin add-search
error: pushing Ghostbranch feature work to the public repo you/project is blocked:
refs/heads/add-search: "add-search" is a Ghostbranch feature
publish features only with: ghostbranch promote <feature> [PUSH_BLOCKED]
error: failed to push some refs to 'https://github.com/you/project.git'

A new name doesn’t get past it, because it looks at the commits:

Terminal
git push origin add-search:try-this
error: pushing Ghostbranch feature work to the public repo you/project is blocked:
refs/heads/try-this: it contains the commits of feature "add-search"
publish features only with: ghostbranch promote <feature> [PUSH_BLOCKED]
error: failed to push some refs to 'https://github.com/you/project.git'

In both blocks, the guard’s lines are from real runs of its check, and the last line is the one git prints when a hook stops a push.

Nothing was sent. Pushes to the shadow repo, and ordinary pushes to the public repo, such as main, go through untouched. To publish a feature, use ghostbranch promote.

The guard knows your features from Ghostbranch’s copy of the shadow repo on your machine. Run ghostbranch sync now and then, so it has seen features started on your other machine.

After apply hands a feature off (Archive, its default), the feature’s own pull request branch takes plain pushes. That’s how you fix the pull request from then on: no gate, no leak scan, no plan and no code. Start from the public branch, not the old shadow branch:

Terminal window
git fetch origin add-search
git switch --detach origin/add-search
# commit your fix here
git push origin HEAD:add-search

The exception is narrow. The guard still refuses:

  • a push that carries the archive’s private commits, the shadow branch’s history that never went public. A plain git switch add-search lands on the old shadow branch, so a fix committed there is refused;
  • other features’ unpublished commits, and the overlay and gate branches, as before;
  • the branch name, when the guard can’t read the feature’s record or find its archive. It fails closed.

Here a fix was committed on the old shadow branch and pushed to the pull request. The guard refuses it, because the branch carries the feature’s private commit:

Terminal
git push origin add-search
error: pushing Ghostbranch feature work to the public repo you/project is blocked:
refs/heads/add-search: it contains commit 0312254b6ef3 of feature "add-search"
publish features only with: ghostbranch promote <feature> [PUSH_BLOCKED]
error: failed to push some refs to 'https://github.com/you/project.git'

The guard’s lines are from a real run of its check, as above. Start again from origin/add-search, and make the fix there.

Promote and approval explains Archive and Keep.

ghostbranch init offers to install the guard when you set up a repo. To install it later, run this in your checkout:

Terminal
ghostbranch guard install

It’s the person’s command, not an agent’s: an agent may suggest it, never run it. Add --plan to see what it would change first.

If the checkout already has a pre-push hook, it keeps running: the guard renames it pre-push.ghostbranch-chained and calls it after its own check. A hook that decides what to do from its own file name may behave differently.

ghostbranch guard uninstall removes the guard and puts back any hook it chained:

Terminal
ghostbranch guard uninstall --plan
Dry run; nothing was changed. This would:
- remove /home/you/project/.git/hooks/pre-push

ghostbranch guard status says whether the guard is installed in this checkout, and which public repo it covers through which remote:

Terminal
ghostbranch guard status
The guard is installed in /home/you/project/.git/hooks, covering you/project via origin.

ghostbranch doctor checks it too, among the rest of your setup.

The hook runs ghostbranch guard check <remote> <url>, with the list git hands every pre-push hook on its input. You never need to run it yourself. When a push is fine it prints nothing, so a guarded push looks like any other.

If the guard can’t tell what a push contains, or can’t read your features, it refuses the push rather than guess, and says why.

The guard stops mistakes. It isn’t a wall:

  • It covers only the checkout it’s installed in. Install it in each clone, on each machine.
  • git push --no-verify skips every pre-push hook, the guard included. Never use it to get past the guard.
  • It recognizes GitHub remotes by their URL. An SSH host alias, such as git@github-work:…, isn’t recognized.
  • It can’t recognize commits that were rewritten after they left the shadow repo, such as a cherry-pick onto a new branch.
  • It reads Ghostbranch’s state from the default place, unless GHOSTBRANCH_HOME is set where git runs the hook.

guard install refuses with GUARD_UNSUPPORTED when:

  • git is older than 2.31;
  • the checkout’s hooks are managed by Husky;
  • the hooks directory is inside the working tree, and the guard’s files there aren’t ignored by git, so they could be committed;
  • a leftover pre-push.ghostbranch-chained file is in the way.

Upgrade git, ignore the files, or point core.hooksPath at a directory outside the working tree, then install again.

PUSH_BLOCKEDexit 1The pre-push guard refused to push feature work to the public repo.
Causes
  • pushing a feature branch, the overlay branch or a gate branch to the public remote
  • pushing commits that contain a shadow feature
What to do

Publish features only with ghostbranch promote. Never bypass the hook with --no-verify.

GUARD_UNSUPPORTEDexit 1The pre-push guard cannot be installed here.
Causes
  • git is older than 2.31
  • the hooks directory is inside the working tree and the guard's files there (pre-push and pre-push.ghostbranch-*) are not all git-ignored, so they could be committed
  • the checkout's hooks are managed by Husky
  • a leftover pre-push.ghostbranch-chained file without a pre-push hook, or both files already exist
What to do

Upgrade git, ignore pre-push and pre-push.ghostbranch-* in the hooks directory, or point core.hooksPath at a directory that is ignored or outside the working tree.