Fold fixups into the change that owns them

tovio change absorb — what it operates on, how it attributes, and when it declines.

Written By Dustin

Last updated 28 minutes ago

tovio change absorb --dry-runtovio change absorb

Distributes your current change — the tip commit of the lane you are on — into the earlier unlanded changes that own the files it fixes, then re-stacks the rest. Commit your fixups first: absorb reads the tip commit, not the working copy.

The problem it solves

You are working on a stack, review comes back on three changes at once, you fix all three and commit. Manually splitting those fixes back across the right changes is tedious and easy to get wrong. Absorb does the attribution for you.

How it attributes, most conservative first

  1. A file exactly one unlanded ancestor touched folds whole into that ancestor.
  2. A file several ancestors touched, whose fixup edit blames to exactly one of them, folds line-level — only that owned hunk is spliced into that ancestor.
  3. Everything else stays in your change. Absorb never guesses.

The stack it considers is the current lane's first-parent chain back to where it forked from main.

It only ever improves

A line-level splice can make the cascade conflict when the fix sits next to a later ancestor's edit. Absorb runs a trial cascade first and rolls back any path that would gain a conflict, leaving it in your change. So absorb never introduces a conflict you did not already have. Every change id is preserved, and the whole thing is one reversible undo.

Always dry-run first

tovio change absorb --dry-run

Prints the post-rollback routing plan — which file would fold into which ancestor, and which stay — without advancing the lane.

When it declines

  • TVO-OP-016 — nothing to absorb: every edit was new code, or spanned several ancestors, or there is no unlanded stack forked from main, or this change already landed.
  • TVO-CONFLICT-003 — your fixup change itself carries unresolved conflicts. Resolve them first: distributing it would copy a conflict object into an ancestor.

A merge commit anywhere in the unlanded stack is refused too, for the same reason rebase and split refuse one.

Undo

tovio undo