Skip to content

Local development with branch stacks

Stacking branches

Starting at the trunk branch, any number of branches may be stacked on top. git-spice learns about the relationships between branches by 'tracking' them in an internal data store. Branches may be tracked manually or automatically:

Automatic stacking

$ $EDITOR file.txt# make your changes$ git add file.txt$ gs branch create feat1

The preferred way to create and track a stacked branch is with gs branch create. The steps are simple:

  1. Modify your code and prepare your changes to be committed with git add.
  2. Run gs branch create.

This will create a new branch stacked on top of the current branch, commit the staged files to it, and track it with the current branch as base. An editor will open to let you write a commit message if one was not provided with the -m/--message flag.

But I use git commit -a

If you prefer to use git commit -a to automatically stage files before committing, use gs branch create -a to do the same with git-spice.

Explore the full list of options at gs branch create.

Creating branches without committing

If you prefer a workflow where you create branches first and then work on them, you can configure git-spice to never commit by default. See Create branches without committing.

Manual stacking

git-spice does not require to change your workflow too drastically. If you prefer to use your usual workflow to create branches and commit changes, use gs branch track to inform git-spice of the branch after creating it.

$ git checkout -b feat1# make your changes$ git commit$ gs branch trackINF feat1: tracking with base main

For example, you may:

  1. Create a new branch with git checkout -b.
  2. Make changes and commit them with git commit.
  3. Run gs branch track to track the branch.

The gs branch track command automatically guesses the base branch for the newly tracked branch. Use the --base option to set it manually.

Naming branches

We advise picking descriptive names for branches. You don't need to remember the exact names as git-spice provides a number of utilities to help navigate and manipulate the stack. Most places where a branch name is required provide shell completion and fuzzy-searchable lists.

Can't think of a branch name?

If you can't think of a name for a branch, run gs branch create without any arguments. git-spice will use the commit message to generate a branch name for you.

git-spice offers the following commands to navigate within a stack of branches:

  • gs down checks out the branch below the current branch
  • gs up checks out a branch stacked on top of the current branch, prompting to pick one if there are multiple
  • gs bottom moves to the bottommost branch in the stack, right above the trunk
  • gs top moves to the topmost branch in the stack, prompting to pick one if there are multiple
  • gs trunk checks out the trunk branch
  • gs branch checkout checks out any branch in the repository
main A B C D E gs trunk   gs down gs up  gs top gs top 

These commands allow for relative movement within the stack, so that you don't have to remember exact branch names or their positions in the stack.

What's the value of gs branch checkout?

$ gs branch checkout my-feature

You may be wondering about gs branch checkout when git checkout exists. The command behaves not much differently than git checkout when provided with a branch name as an argument.

$ gs branch checkout # or gs bcoSelect a branch to checkout:┏━■ docs ◀┃ ┏━□ github-optimization┣━┻□ github-supportmain

Its real value lies in the interactive mode. Invoke it without arguments to get a fuzzy-searchable list of branches, visualized as a tree-like structure to help you navigate the stack.

Committing and restacking

With a stacked branch checked out, you can commit to it as usual with git commit, git commit --amend, etc. However, after committing, the branches upstack from the current branch need to be restacked to maintain a linear history. Use gs upstack restack to do this.

$ gs branch checkout feat1$ $EDITOR file.txt# prepare your changes$ git commit$ gs upstack restack
A B C feat1 feat2 A B D C feat1 feat2 A B D C feat1 feat2 git commit gs upstack restack

Tip

git-spice also offers gs branch restack to restack just the current branch onto its base, and gs stack restack to restack all branches in the current stack.

Automatic restacking

git-spice provides a handful of convenience commands for common commit-related tasks that will automatically restack upstack branches for you:

For example, the interaction above can be shortened to:

$ gs branch checkout feat1$ $EDITOR file.txt# prepare your changes$ gs commit create # or gs cc

Inserting into the stack

By default, gs branch create creates a branch stacked on top of the current branch. It does not manipulate the existing stack in any way.

If you're in the middle of the stack, use the --insert option to stack the new branch between the current branch and its upstack branches.

$ gs branch checkout feat1$ gs branch create --insert feat4
feat1 feat2 feat3 feat1 feat4 feat2 feat3
$ gs branch checkout feat1$ gs branch create feat4
feat1 feat2 feat3 feat1 feat2 feat3 feat4

Splitting the stack

Use gs branch split to split a branch with one or more commits into two or more branches.

The command presents a prompt allowing you to select one or more split points in the branch's history. After selecting the split points, it will prompt you for a name for each new branch.

$ gs branch splitSelect commits:▶ c4fb996 Add an aardvark (12 minutes ago) f1d2d2f Refactor the pangolin (5 minutes ago)4186a53 Invent penguins (1 second ago) [penguin] Done

Tip

You can use gs commit split and gs branch edit to safely and easily manipulate the commits in the branch before splitting it into multiple branches.

v0.8.0 If you split a branch after submitting it for review, git-spice will prompt you to assign the submitted CR to one of the branches.

Assign CR #123 to branch: aardvark pangolinpenguinBranch being split has an open CR assigned to it.Select which branch should take over the CR.

Splitting non-interactively

$ gs branch split \ --at HEAD~2:aardvark \ --at HEAD~1:pangolin

If the interactive prompt is not suitable for your workflow, you can also split a branch non-interactively by specifying the split points with the --at option one or more times.

The option takes the form:

--at COMMIT:NAME

Where COMMIT is a reference to a commit in the branch's history, and NAME is the name of the new branch.

If running in non-interactive mode and the branch has already been submitted for review, it will be left assigned to the original branch.

Moving branches around

Use the gs upstack onto command to move a branch onto another base branch, and bring its upstack branches along with it.

$ gs branch checkout feat2$ gs upstack onto main
main feat1 feat2 feat3 main feat1 feat2 feat3

Tip

Omit the target branch name to get a list of branches to choose from.

This is useful when you realize that a branch does not actually depend on the base branch it is stacked on. With this command, you can move an entire section of the stack down to a different base branch, or even to the trunk branch, reducing the number of changes that need to merge before the target branch.

Moving only the current branch

In rarer cases, you want to extract the current branch from the stack and move it to a different base branch, while leaving the upstack branches where they are.

Use gs branch onto for this purpose.

$ gs branch checkout feat2$ gs branch onto main
main feat1 feat2 feat3 main feat1 feat3 feat2

This is most useful when you find that a branch is completely independent of other changes in the stack.

Removing branches from the stack

Use gs branch delete to remove a branch from the stack and delete it from the repository. Branches that are upstack from the deleted branch will be restacked on top of the deleted branch's original base branch.

$ gs branch delete feat2INF feat2: deleted (was 644a286)
main feat1 feat2 feat3 main feat1 feat3

Tip

Omit the target branch name to get a list of branches to choose from.

Removing a branch without deleting it

$ gs branch untrack feat2

If you want to remove a branch from the stack but don't want to delete the branch from the repository, use the gs branch untrack command.