Skip to main content

Recursion

These scenarios show how git-splice pulls through nested splices: as if each splice's upstream were cloned on its own, updated there, and the result handed up to the splice around it. Pages that did that by hand, one level at a time with a worktree per level, showed it ends where the monorepo's own pull ends; git splice pull --recursive and pull --rebase --recursive do it now, and the pages below show those.

The tree​

A splice and the splices inside it form a tree. Its root is the monorepo, every other node a splice, and the edge from a parent to a child is the child's .splice in the parent's folder: which upstream the child comes from, and at which commit, its synced commit. Each splice's history is computed from the monorepo's: its rebuild, R.

The passes​

A pull goes through the tree three times:

PassDoesOrderConflicts
discoverfetches each upstream, and reads the .splice files in it for the ones insideparent before its childrennone
mergemerges each upstream into its parent's version of the folderparent before its childrenat most one per splice
take upputs each new commit into its parent's folderchildren before their parentnone

The parent goes first, since its upstream decides which splices are inside it, and which commit of each: a newer base for the child's merge, which then doesn't ask a question the parent's merge already answered. The first conflict stops the pull, until it's resolved. A take-up can't conflict: the parent's folder hasn't changed since the child's merge started from it.

How many conflicts​

A pull through E splices, with C commits of the monorepo's own on top:

At mostWhy
mergeEone merge per splice: base its synced commit, ours its head, theirs its upstream
--rebaseE + Cthe pulls, top-down, then each commit of the monorepo's own, replayed once

The scenarios​

  • every level conflicts: three levels, where every upstream changed the folder of the splice inside it: one conflict per edge, each resolved in the parent of the two, and each take-up conflict-free.
  • a recursive pull: a tree with branches, vendor/a/ with b/, c/ in it, d/ and e/, pulled with three runs of pull --recursive, each to the next conflict, and one commit for all of it.
  • discovered top-down: why the splice above goes first: its pull drops d/ and adds e/, so naming vendor/a/d beside it means nothing definite.
  • step by step: one recursive pull, recorded, wound back, and replayed one step at a time, with the tree after each.
  • a conflict in an update: why updating a splice isn't always a fast-forward.
  • rebased: the recursive pull with --rebase: the pulls first, as one commit, then the monorepo's own commit, rebased onto it once.
  • status and push: a push goes bottom-up, and the rebuild of each parent records what its child's push sends, so one push --all leaves every record right.
  • a failed push below: the upstream above records a commit the one below never got; a clone of it makes the commit again and counts it as its own.