GitMasterDocs

User guide

Stacks and restack Partly available Preview

This is a preview feature. It is hidden in the release build; start GitMaster with GITMASTER_ENABLE_STACKS=1 to turn it on.

A stack is a chain of branches in which each branch builds on its parent. A typical stack has a base branch, an API branch and a UI branch on top of the base, and an end-to-end branch on top of the UI branch. When the base changes, restack replays each branch on the new tip of its parent, so the whole stack stays current. If you never give a branch a parent, GitMaster works as a normal Git client and the stack views stay out of your way. Stacks and restack are a preview feature, hidden in the release build: start GitMaster with GITMASTER_ENABLE_STACKS=1 to turn them on.

How a stack is shaped#

Each branch has at most one parent and any number of children. Every stack is a tree rooted at main:

main
└ ledger-base
  ├ ledger-api
  └ ledger-ui
    └ ledger-e2e

A branch cannot have two parents, so a diamond shape is not supported. To combine two branches that both feed one piece of work, create an integration branch that merges both, then make the integration branch the child of one of them.

Where the parent is stored#

GitMaster records the parent in the .git/config file of the repository, outside the working tree. Each branch stores two values:

KeyValue
branch.<name>.stackParentThe name of the parent branch.
branch.<name>.stackParentBaseThe commit of the parent that the branch's own commits start from.
[branch "ledger-api"]
	stackParent = ledger-base
	stackParentBase = <commit>

Each value is a single value, not a list. The values stay in your local clone. They never appear in a commit or in a file of the working tree.

GitMaster writes these values with git config, and each write shows in the command panel. The recorded base is set as follows:

ChangeRecorded base
New Child BranchThe tip of the parent, where the new branch starts.
Set Parent…The merge base of the branch and its new parent.
Reparent to GrandparentUnchanged. The branch's own commits still start at the same commit, so restack replays only those.
Remove From StackBoth values are removed.

When git branch -d deletes a branch, Git removes that branch's own values too. Its children keep their values, and their parent shows as gone.

Derived states#

GitMaster compares the stored values with the repository and derives the state of each branch that has a parent. The comparison runs off the UI thread, and the sidebar shows Calculating… until it finishes.

StateWhen it appliesWhat you see
StaleThe tip of the parent is no longer the commit in stackParentBase, because the parent has new commits or was rewritten.The child shows an amber dot, and the parent shows a restack indicator.
Diverged baseThe recorded base no longer marks where the branch's own commits start: it is not in the history of the branch, or the branch already contains newer commits of its parent. This happens after a rebase or a merge made outside GitMaster.The branch shows the diverged tag, from the first time the repository opens.
Parent goneThe parent branch no longer exists.The branch shows the parent gone tag, and its menu offers Reparent to Grandparent.
Parent mergedThe changes of the parent are already in the root of the stack. GitMaster detects this by patch ID, so a squash merge and a rebase merge count, as well as a normal merge.The branch shows the parent merged tag, and its menu offers Reparent to Grandparent.

When a branch is in more than one state, the tag shows the first that applies in this order: parent gone, parent merged, diverged base, stale.

Reparent to Grandparent makes the parent of the old parent the new parent. When the old parent is gone and its values went with it, the new parent is the default branch: the branch of origin/HEAD, else the first of main, master, trunk and develop that exists. From the branch menu, you confirm before anything changes. The branch's commits do not move until you restack.

GitMaster compares patch IDs against the most recent 2,000 commits of the root that the parent's branch does not have. A parent merged further back than that is not detected.

A parent value that points back at its own child, directly or through other branches, is a loop that Git cannot express as a tree. GitMaster shows the branches of a loop under No stack, with the parent loop tag, until you set or remove one of the parents.

The By stack view#

The sidebar has two views of your branches: By stack and By name. By name groups branches by prefix, such as feature/, as described in Branches.

By stack shows an indented tree of dependencies, in two sections:

  • Stacks lists each tree, with its root branch at the top.
  • No stack lists the branches that have no parent and no children.

The sidebar shows these indicators:

IndicatorWhereMeaning
restack NOn a parentN branches below it need a restack: its stale children and every branch under them. The indicator sits on the parent, because the parent is the branch that changed.
↑NOn a branchThe branch has N commits that its parent does not have. The count is against the parent, not against origin.
Amber dotOn a childThe child is stale.

Hover a tag or an indicator to read what it means.

The list is virtualized, so a repository with hundreds of branches stays responsive.

Set a parent#

Every action below is in the branch menu of the sidebar and in the command palette. Each one changes only the two values above. No commit moves until you restack.

  • New Child Branch creates a branch at the tip of this branch, with this branch as its parent. The dialog is the New Branch dialog, with the parent shown and the start point fixed.
  • Set Parent… makes another local branch the parent of this branch. Pick the parent from the list. GitMaster refuses the branch itself, a branch under it (that would make a loop), a remote branch, and a branch that shares no history with it.
  • Remove From Stack removes the parent of this branch. Its children keep it as their parent, so it becomes the root of their stack.
  • Reparent to Grandparent is offered when the parent is gone or merged, as described in Derived states.

Drag to set a parent#

In the By stack view, drag a branch onto another branch. The branch you drop it on becomes the parent of the dragged branch, as with Set Parent…. While you drag, the row under the pointer is highlighted when it can be the parent, and the status bar says what the drop does. A row that cannot be the parent is dimmed, and the status bar says why: the branch itself, a branch under it, or its parent already.

Stacks in the history graph#

The History page draws stacks in its graph:

  • A child branch that starts from a commit other than the tip of its parent has a dashed amber edge, with the badge N commits behind parent.
  • Each commit belongs to the branch whose range, from the recorded base to the branch tip, contains it, and its node takes that branch's color. GitMaster does not guess from branch names.
  • When HEAD is in a stack, the default scope is the stack of HEAD plus the default branch, shown by the chip Scope: current stack. Choose All refs in the scope pill to show every reference.
  • Each branch has a color derived from its name, and the color stays the same across sessions. The dashed edge and the badge also show the state, so color is never the only signal.

Commits and Stacks modes#

The graph has two modes. Commits shows the graph of commits. Stacks shows one node per branch, with its commits collapsed or expanded.

Restack#

Restack brings a stack up to date. It walks the tree from the top down, so parents come before their children. For each branch, it replays the branch's own commits onto the new tip of its parent. When the branch is done, its base moves to that tip, and the walk continues with the next branch.

How restack works#

  • The plan is exact. Before anything runs, GitMaster builds a plan. The plan replays each commit in order, with the same algorithm that runs the restack, so it shows exactly which commits conflict. It never reports a probable conflict.
  • A plan is tied to the commits it reads. A plan refers to the commit IDs of the branches it touches. If any of those branches moves, the plan is invalid, and you build a new one.
  • The restack does not use your working copy. The commits are built without checking anything out, so your files and staged changes stay as they are. When the branch you have checked out is part of the restack, its files move to the new commit once the branches have moved.
  • Undo is atomic. Before a restack starts, GitMaster records the commit of every branch it touches. Undo restack restores all of them in one step. Each restack is recorded in the operation log.
  • The state survives. GitMaster saves the state of a restack as it runs. Closing the app or a crash does not lose it.
  • A conflict holds back only its subtree. The branch with the conflict and its children stay where they are. Independent sibling branches are restacked.
  • A dirty working copy can block the restack. If your working copy has uncommitted changes and the current branch is part of the plan, the restack does not start. A banner explains the block. Commit your changes, then start the restack again.
  • Push is a separate step. GitMaster never pushes restacked branches on its own. The restack dialog has no push button. To push a restacked branch, check it out and use Push With Lease from its menu. That push uses --force-with-lease, so it fails when the remote branch has changed since your last fetch. See Branches.

The four steps#

  1. Plan. Review the ordered list of branches, how many commits each one replays, and what depends on what. A conflict shows the commit and the files. Skip branch and children leaves that branch and the ones under it out, and Include puts them back. Start the restack from here.

  2. Running. The branches move together in one step.

  3. Conflict. A branch that conflicts is listed in the plan and stays where it is, with its children. Close the dialog with Cancel, restack the rest, or resolve the conflict.

    Resolve conflict opens the resolver on the commit that conflicts. GitMaster replays that commit in a private worktree of its own, so your working copy, your staged changes and your checked-out branch stay as they are, even when the conflicting branch is the one you have checked out. That worktree is not listed under Worktrees. When every file is resolved, Continue restack turns your resolution into the replayed commit and plans the restack again: the step shows as Resolved, the rest of the branch replays, and the branches under it plan as usual. A later commit can conflict too, and you resolve it the same way. Cancel drops the resolution in progress. Nothing moves until you choose Restack N branches, so a resolution is undone like any other restack. A finished resolution is kept when you close the app, and it is used only while the parent stays where it was: if the parent moves, the commit is replayed again and the conflict comes back, with your recorded resolution offered. See Resolving conflicts.

  4. Done. Choose Undo restack to restore every branch the restack moved. The undo is also offered after you reopen the app.

Recorded resolutions#

GitMaster uses Git's rerere when the resolver runs, so a resolution you record there is offered again when the same conflict comes back. The plan itself does not use rerere yet, so a conflict that has a recorded resolution is still reported as a conflict in the plan. Open Resolve conflict to see the recorded resolution. See Resolving conflicts.

Push from the restack#

Automate stack updates#

Automation is built with workflows, not with a restack setting. Three built-in workflows ship with GitMaster. They are listed on the Workflows page under Built-in, and each one is off in every repository until you switch it on there. Switching one on asks whether its applying actions may run without asking, as reviewing a version does. They ship with the app, so they need no review. Copy to Edit writes a copy into your workflows folder, which you then review and change like your own.

  • Restack children when parent moves runs on the stack.parent_moved event.
  • Restack stale stacks after fetch runs after a fetch.
  • Reparent to grandparent when parent is merged runs on the stack.parent_merged event.

A workflow that restacks shows the same four steps as the manual restack. When an event run asks to apply its restack, the approval lists each branch the plan moves, with its commits and the parent it moves onto, and each branch it holds back, with the reason. On a conflict, the run pauses only that run and its subtree, shows a notice that names the workflow and the branch, and waits. Resolve conflict in the notice opens the restack dialog on the plan, and the resolution goes on as above. Continue restack lets the run plan and apply the rest again on its own. After a workflow restack, Undo restack is offered in the restack dialog, which the Restack command opens. The run never resolves a conflict by itself. A push in a workflow always asks for your approval. The restack apply step follows the approval rules of the workflow, as described in Approvals and security.

To write your own stack workflows, see the Workflows overview and Triggers and phases.

Restack with agents#

Agents use the same stack primitives through MCP. An agent reads the stack with stack_tree, builds a plan with restack_plan, and asks to apply the plan with restack_apply. The apply accepts only a plan ID, so you approve exactly the plan that runs. The apply always waits for your approval in the app. See the MCP server page.

GitMaster documentation · e66d34c