Don't mix the strategies
In the same state, feature is rebased in the monorepo,
and then the PR, which was never rebased, is pulled. That mixes the two
strategies: the next push sends the old commits back to the PR, next to
the rebased ones.
Output
rebase-setup.bash builds this state:
$ build_scenario scenario_feature_branch_behind_default_branch
The rebase copies b onto p as c:
$ git rebase -q main
push refuses: upstream has a commit this branch lacks, b':
$ git splice push vendor/a
!! vendor/a: upstream has commits this branch lacks
!! Failed: vendor/a
[1]
Out of habit, you pull before pushing again. Neither pull is the way here, but only one of them stops you:
- A plain
pullkeeps every change, asgit pulldoes, so it goes through. What it does to the PR is what this page is about. pull --rebasewould take b''s files and drop m: b' lacks it, and never had it, sincemainbrought it in, notfeature. That would lose a change, whichgit pull --rebasekeeps, so it refuses.
The refusal first, since a pull changes the state it needs:
$ git splice pull --rebase vendor/a
ok vendor/a fetched
!! vendor/a: 'feature' lacks the synced commit 48aa825, and its reflog here doesn't show it ever had it -- --rebase would drop that commit's changes
!! Failed: vendor/a
[1]
It states only what it knows. It can't tell this feature from one
that upstream rebased before this clone fetched it, e.g. in a fresh
clone, so it suggests no command; here, the
rebase strategies are the ways out.
The plain pull merges b', the copy of b from before the rebase,
into feature as the pull x. No change is lost,
and lock.txt stays:
$ git splice pull -m "x: pull the PR" vendor/a
ok vendor/a fetched
ok vendor/a: pulled 1041672
$ cat vendor/a/lock.txt
m: bump dep
But the push then sends both copies of the feature change to the PR:
$ git splice push vendor/a
ok vendor/a: pushed 7680df9 to feature
$ git -C "$UPSTREAM" log --graph --format=%s feature
* x: pull the PR
|\
| * b: feature change
* | b: feature change
* | m: bump dep
|/
* k: seed
The PR lists b: feature change twice, and the merge between them. A
squash merge of the PR hides that; a rebase merge puts both copies on
main.
If it happened
Before anyone else builds on the PR, drop the pull.
--keep refuses if that would lose uncommitted changes:
$ git reset -q --keep HEAD~1
The PR's merge x' now has exactly the folder's files, so to
git-splice the splice is up to date, and even push --force has
nothing to push:
$ git splice push --force vendor/a
ok vendor/a: nothing to push
It doesn't replace a history whose files are the same: that's how an
upstream that rewrote its history, e.g. to remove a secret, stays
rewritten. Name the commit to push instead, the rebuild c', with
--rebuilt-commit:
$ r="$(git splice rebuild vendor/a)"
$ git splice push --force --rebuilt-commit "$r" vendor/a
ok vendor/a: pushed 125a49b to feature
The PR ends up as rebase via monorepo leaves it:
$ git -C "$UPSTREAM" log --graph --format=%s feature
* b: feature change
* m: bump dep
* k: seed