Skip to content

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

git-cascade

Git-native CLI for planning and applying cascade rebases across dependent local branch stacks.

When the git-cascade binary is installed on PATH, Git exposes it as:

git cascade <command>

Pick the command based on what changed in your branch stack:

git cascade sync
git cascade restack [branch]
git cascade landed <old-tip> [--onto <ref>]

Use git cascade replay --old-base <ref> --old-tip <ref> --new-tip <ref> when none of those workflows fit and you can name the exact before/after refs. The lower-level git cascade plan ... commands are for power users who need to inspect, save, or apply plans manually.

Common Use Cases

git-cascade is built for local branch stacks where one branch is the base for another:

main -- A
         \
pr-1      B -- C
                \
pr-2             D -- E
                       \
pr-3                    F

The common situations are:

  • Your target branch advanced, and local branch stacks should catch up.
  • A parent branch gained commits, and child branches should follow it.
  • A parent branch landed, and child branches should now start from the landed result.

Target Branch Advanced

Use sync after updating your target branch when your local branch stacks should catch up to it.

For example, main advanced from A to G while pr-1 and pr-2 still point at the old stack:

main -- A -- G
         \
pr-1      B -- C
                \
pr-2             D -- E

Run:

git cascade sync

Result:

main -- A -- G
              \
pr-1           B' -- C'
                      \
pr-2                   D' -- E'

By default, sync uses the repository default branch, preferring origin/HEAD, then local main, then local master. After sync, each affected local stack starts from the current tip of the selected target branch.

If you know the oldest local branch that should be included, pass it explicitly to avoid considering older unrelated branches:

git cascade sync --oldest-branch pr-1

If this repository targets a non-default integration branch, pass it explicitly:

git cascade sync --base develop

Preview the operation first:

git cascade sync --dry-run

Branch Was Updated

Use restack when a parent branch gained commits and child branches should follow that parent branch.

For example, pr-1 gained commit G:

main -- A
         \
pr-1      B -- C -- G
                \
pr-2             D -- E
                       \
pr-3                    F

Move pr-2 so it starts from the new tip of pr-1, and move pr-3 so it starts from the new tip of pr-2:

git cascade restack pr-1

Result:

main -- A
         \
pr-1      B -- C -- G
                     \
pr-2                  D' -- E'
                             \
pr-3                          F'

If you are currently on the updated branch, the branch argument can be omitted:

git switch pr-1
git cascade restack

If the stack is based on a non-default integration branch, pass that base explicitly:

git cascade restack pr-1 --base develop

Preview the operation first:

git cascade restack pr-1 --dry-run

Branch Was Landed

Use landed when a parent branch was merged or squashed into a target branch, and child branches should now start from the landed result instead of the old branch.

For example, pr-1 was squash-merged into main as commit S:

main -- A -- S
         \
pr-1      B -- C
                \
pr-2             D -- E
                       \
pr-3                    F

Move pr-2 so it starts from the landed result on main, and move pr-3 so it starts from the new tip of pr-2:

git cascade landed pr-1 --onto main

Result:

main -- A -- S
              \
pr-2           D' -- E'
                      \
pr-3                   F'

When --onto is omitted, git-cascade uses the default branch, preferring origin/HEAD, then local main, then local master.

For a true merge commit, landed uses the merge commit that introduced the old tip. That keeps child branches attached to the landing point instead of accidentally including unrelated later target-branch commits:

main -- A -------- M
         \        /
pr-1      B -- C
                \
pr-2             D -- E

Fast-forward landings do not leave enough graph information to tell where the target branch was before the landing. Pass that previous target-branch tip explicitly:

git cascade landed pr-1 --onto main --old-base <previous-main-tip>

Preview the operation first:

git cascade landed pr-1 --onto main --dry-run

Explicit Replay

Use replay when none of the named workflows fit and you can identify the exact old range and replacement tip:

git cascade replay --old-base main --old-tip pr-1 --new-tip rewritten-pr-1

replay moves the same kind of dependent branch stack as sync, restack, and landed, but it does not infer the situation for you. You provide the before/after refs directly.

The targeted workflow commands and replay default to move-to-current-tips, so each child branch moves to its parent's rewritten apply-time tip. All of them expose --strategy for explicit-control cases.

Preview the generic replay first:

git cascade replay --old-base main --old-tip pr-1 --new-tip rewritten-pr-1 --dry-run

Underlying Model

git-cascade works by creating a repository-local plan and then applying it.

A plan records:

  • the old root range, expressed as old-base..old-tip
  • the local branches that depend on commits in that range
  • the parent/child relationships between dependent branches
  • each branch's planned commits and fork point

The targeted workflow commands and explicit replay generate plans for you:

  • sync creates a generated plan under generated/sync/...
  • restack creates a generated plan under generated/restack/...
  • landed creates a generated plan under generated/landed/...
  • replay creates a generated plan under generated/replay/...

Generated plans are deleted after a successful apply. If replay stops on a conflict, the generated plan is kept and the active state file points to it, so git cascade continue can recover.

By default, generated one-shot commands replay each child branch onto its parent's rewritten apply-time tip. This is the move-to-current-tips strategy. Pass --strategy ... when you need a different strategy.

Merge Commits

Dependent branches are replayed along their first-parent chain, producing linear results:

  • A merge of target-branch history (typically a previous git merge main to catch up) is flattened away: the merged-in commits are already part of the new base, and the branch becomes linear.
  • A merge of history that the new tip does not contain (an unrelated local branch, or stacked work) cannot be flattened. Such branches are skipped during plan generation with a warning, or rejected at apply time; rebase them to linearize first.

Two caveats about flattening:

  • Conflicts that were resolved in a flattened merge re-fire during replay and are resolved through the normal conflict flow. Edits that were made only in the merge commit itself, to content neither side had touched, are not replayed and disappear with the merge.
  • Branches that already start at their replay base are left untouched, including their merges. Linearization happens when a branch is actually replayed, not on no-op runs.

Fork points that are ambiguous because of criss-cross merges make a branch unplannable; it is skipped with a warning.

Conflicts

If replay conflicts, permanent branch refs remain unchanged and the active operation state is preserved.

Inspect the active operation:

git cascade status

Resolve conflicts in the reported worktree:

git -C <worktree> status
git -C <worktree> add <resolved-files>

Continue after resolving:

git cascade continue

continue also resumes an operation that was interrupted mid-replay, for example by a crash or Ctrl-C; already-finished branches are not replayed again.

Abort and clean the active operation:

git cascade abort

Abort cleans temporary state and leaves the stored plan intact so it can be retried.

Pause For Checks

Pass --pause-at-checkpoints when you want to run tests or make manual fixes at every point later replay depends on:

git cascade restack pr-1 --pause-at-checkpoints

Replay pauses after each rewritten branch. It also pauses at any rewritten commit that a child branch will use as its replay base, such as an intermediate preserved fork point or a planned parent tip before extra parent commits are replayed.

When replay pauses, permanent branch refs are still unchanged. Run checks in the reported worktree. If a branch needs fixes, commit them in that worktree, then continue:

git -C <worktree> status
git -C <worktree> add <files>
git -C <worktree> commit -m "fix after replay"
git cascade continue

continue records the paused worktree's current HEAD as the paused branch tip or child-base mapping, then replays dependent work using the selected strategy. It refuses to continue if the paused worktree has uncommitted changes or if HEAD no longer descends from the paused rewritten commit.

Power-User Plan Commands

Use git cascade plan ... when you need to snapshot topology before a destructive rewrite, inspect a plan before applying it, or script the lower-level workflow directly.

Create a named repository-local plan:

git cascade plan create stack --old-base main --old-tip pr-1

For a single-commit root rewrite, use that commit's parent as the explicit old base:

git cascade plan create stack --old-base '<old-commit>^' --old-tip <old-commit>

Inspect stored plans:

git cascade plan list
git cascade plan show stack

Delete a stored plan:

git cascade plan remove stack

Apply a stored plan:

git cascade plan apply stack --new-tip pr-1

Preview a stored plan apply without mutating refs, worktrees, plans, or state:

git cascade plan apply stack --new-tip pr-1 --dry-run

Use move-to-current-tips to replay every child onto the parent's rewritten apply-time tip:

git cascade plan apply stack --new-tip pr-1 --strategy move-to-current-tips

Use move-to-planned-tips when children should move to each parent's rewritten planned tip, ignoring commits added to the parent after planning.

The low-level plan apply default strategy is preserve-fork-points:

git cascade plan apply stack --new-tip pr-1 --strategy preserve-fork-points

Replay in the current worktree instead of a temporary worktree:

git cascade plan apply stack --new-tip pr-1 --in-place

--in-place requires a clean worktree. If replay conflicts, the conflict is left in the current worktree and git cascade abort restores the checkout that was active before apply started.

Pause after each replayed branch when applying a stored plan:

git cascade plan apply stack --new-tip pr-1 --pause-at-checkpoints

Replace an existing plan:

git cascade plan create stack --old-base main --old-tip pr-1 --replace

Shell Completions

Generate completion scripts with Clap's built-in shell generators:

git cascade completions bash
git cascade completions zsh
git cascade completions fish

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages