Saphan StudioDocs
Working with git

Delivering a branch to a remote agent

The act that moves a branch to a machine somewhere else, what it refuses, and the trap that returns a number instead of an error.

Delivering a branch to a remote agent

A remote machine does not share your filesystem, and the product does not hand code to it by copying files. A delivery moves one named branch, and it spans four repositories:

the repositoryits part in one delivery
your local owning checkoutthe source, and the only place the branch is measured from
the machine's admission mirrora bare repository on the machine; the one push target
the machine's shared clonethe working repository; it fetches from the mirror
the machine itselfwhere the two far repositories live

The order is not an implementation detail. The mirror is advanced first, then the clone fetches it. A clone cannot fetch a commit the mirror does not yet have.

saphan machine deliver-branch <machine> --repo <repository> --branch <branch>

--repo names both the local source and the (machine, repository) pair whose mirror receives the branch. --branch names the branch. Neither is ever guessed.

Rehearse it first:

saphan dry-run machine deliver-branch <machine> --repo <repository> --branch <branch>

The rehearsal performs the local, far-mirror and far-clone measurements, changes neither far repository, and exits non-zero when either far reference is stale — so a non-zero rehearsal is the answer "there is work to do here", not a failure.

What it refuses, and why

the refusalwhat it means
--repo or --branch absentthe verb never guesses either; rerun naming both
the branch is not a named brancha delivery with no named reference resolves an unresolvable bare HEAD on a fresh mirror and comes back as an empty tree at exit zero. There is no defensible default, so absence refuses
the machine is not admittedit is not in your registry
the machine binds repository pairs, but not this onerefused by name; it never advances a different repository's mirror instead
the machine has no absolute worktrees root recordedthe far shared clone cannot be addressed; the refusal carries the verb that records one
the far mirror has diverged from your local branchrefused, never forced. A mirror that is a strict ancestor of your branch may be fast-forwarded; a mirror that is anything else holds commits a force would destroy

What is structurally impossible here: a force-push, a prune, a deletion, or any push to your own origin. Nothing in the delivery path names, fetches from, pushes to, or mutates origin — that remote stays exclusively in your hands.

What it proves when it finishes. After the two halves run, it re-measures both far references. Unless the mirror and the clone both equal your local branch, the verb fails its own success predicate and says all three values. A push and a fetch that both exit zero while the far side still reads short is exactly the outcome this postcondition exists to catch.

The sibling verb delivers the stream's base the same way:

saphan machine refresh-repo <machine> --repo <repository>

⛔ The trap that costs the most: which <branch> is fresh depends on where you stand

The same branch name means two different things in two places, and the mistake does not look like a mistake — you get a number, not an error.

you stand inFRESH isSTALE iswhy
your workspace (home)<branch> (local)origin/<branch>merges land locally and nothing self-pushes, so the trunk lives in the local reference
a machine's shared clone (far)origin/<branch><branch> (local)a delivery moves origin/<branch>; the local branch of the same name stays where it was, sometimes hundreds of commits back

One check works in both places. Run it before you count anything:

git -C <repo> for-each-ref --format='%(refname:short) %(objectname:short)' \
  refs/heads/<branch> refs/remotes/origin/<branch>

It names each reference beside its object, so you do not have to remember argument order to apply the table. A missing reference simply omits its line instead of collapsing the measurement into a fatal error.

If the two objects differ, one of them is lying about everything you are about to count. Which one is decided by the table above, not by reflex. Measured costs of getting it backwards, in both directions: a commit count taken against the stale local branch inside a far clone reported 630 commits on a branch that had contributed none; the same reflex at home reads origin/<trunk> and reports green for a trunk that is red; on one measurement the remote-tracking reference stood 392 commits behind the local trunk.

Write this into the order for a remote agent. An agent in confinement will make exactly the reflex you would, and it has no way to tell which clone it is sitting in unless you tell it.

On this page