Skip to main content

Scenario: patch-branch-rebased

A fork carries patches of its own on a branch, patches, on top of the upstream's main, and the monorepo's main follows that branch instead of the upstream's main. When the upstream releases, the fork rebases patches onto the new main. Patches the upstream took drop out, the others get new commits. Either way, the commit the monorepo synced last is on no upstream branch any more (#169).

Here, the upstream's m is p reworded, and n is q rebased onto it:

The monorepo's a records q, the old patches on top of k; T is n, the other patch rebased onto m, the reworded patch

Git's merge base of q and n is k: merging from there, p and m would be changes on both sides, and conflict. Like git pull --rebase, pull merges from the branch's fork point instead: q, where the fetched patches was before the rebase, as its reflog has it. The monorepo has changed nothing since it took q, so the upstream's new version simply replaces it.

Another upstream's commit, like a fork's patch the original never had, isn't rewritten away: the original's fetched branch never pointed at it (original-without-the-patch).

The reflog is local, as in Git: a clone of the monorepo that first fetched after the rebase has no q in it, and its pull conflicts as git merge would (fetched after the rebase). So does git splice pull --no-fork-point.

That's only safe as long as everything the monorepo changed in the folder is pushed, so the rebased branch has it. An unpushed local commit is a different story.

Output​

setup.bash builds this state:

$ build_scenario scenario_patch_branch_rebased https://git.example.com/a.git

On the monorepo's default branch, a splice syncs with its upstream's default-branch. Setting it to the fork's patch branch makes main follow that branch, for pull, push and status alike:

$ cat vendor/a/.splice
[splice]
commit = f463f92ba0f70a0da1ffaa64c5af8e3b64f04c55
id = db77d5957130b5d2
[upstream "origin"]
url = https://git.example.com/a.git
default-branch = patches
$ git -C "$UPSTREAM" log --format=%s patches
other patch
m: patch, reworded upstream
k: seed
$ git splice status
On branch main

ahead 2, behind 2 vendor/a -> patches

ahead 2 are p and q: the rebased branch lacks them, though the monorepo has nothing to push. That count is misleading after a rewrite upstream.

The fetched branch's reflog has n, and q before it:

$ git log -g --format='%h %s' refs/splices/a/heads/patches
28d3fa4 other patch
f463f92 other patch
$ [ "$(git merge-base --fork-point refs/splices/a/heads/patches "$(git splice rebuild vendor/a)")" = "$(git config --file vendor/a/.splice splice.commit)" ] &&
> echo "the fork point is q"
the fork point is q
$ git splice pull -m "x: pull the rebased patches" vendor/a
ok vendor/a fetched
ok vendor/a: pulled 28d3fa4

The folder has the upstream's version of the first patch, and the other patch as before:

$ cat vendor/a/file.txt vendor/a/other.txt
k: seed
m: patch, reworded upstream
other patch
$ git splice status
On branch main

up to date vendor/a -> patches

R, what a push would send, is the upstream's branch itself: the old patches, p and q, don't come back.

$ [ "$(git splice rebuild vendor/a)" = "$(git rev-parse refs/splices/a/heads/patches)" ] && echo "R is T"
R is T

After the pull, x:

The monorepo's a and the pull x, H, B and main; upstream, n on top of m on top of k is T, R, U(H) and U(B), m is main; p and q, dashed, on top of k, are left behind