# git-spice > git-spice is a tool for stacking Git branches. It helps you manage and navigate a stack of branches that build on top of each other. # Home # git-spice git-spice is a tool for stacking Git branches. It lets you manage and navigate stacks of branches, conveniently modify and rebase them, and create GitHub *Pull Requests* or GitLab *Merge Requests* from them. It works with Git instead of trying to replace Git. Introduce it in small places in your existing workflow without changing how you work wholesale. ``` # Create a branch $ git checkout -b feat1 $ gs branch track # Or $ gs branch create feat1 # Restack a branch $ git rebase -i base # Or $ gs branch restack ``` ``` # Restack all branches $ gs stack restack # Submit a PR $ gs branch submit # Submit all PRs $ gs stack submit # Sync with trunk $ gs repo sync ``` What is stacking? Stacking refers to the practice of creating branches or pull requests that build on top of each other. It allows chaining interdependent changes together, while still keeping the individual changes small and focused. See also [FAQ > What is stacking?](https://abhinav.github.io/git-spice/community/faq/#what-is-stacking) [Get started](https://abhinav.github.io/git-spice/start/index.md) [Learn more](https://abhinav.github.io/git-spice/guide/index.md) ## Used by engineers at... [...and your organization](# "To get added or removed, email us at git-spice-users [at] abhg [dot] dev.") ## Features - [**Manage local branches**](https://abhinav.github.io/git-spice/guide/branch/index.md) ______________________________________________________________________ Create, edit, and navigate stacks of branches with ease. With git-spice's branch management commands, you can keep your stack in sync with the trunk branch, automatically rebase dependent branches, and more. - [**Submit change requests**](https://abhinav.github.io/git-spice/guide/cr/index.md) GitHub GitLab ______________________________________________________________________ Submit branches in your stack with a single command. git-spice can submit [the current branch](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-submit), [the entire stack](https://abhinav.github.io/git-spice/cli/reference/#gs-stack-submit), or [parts of](https://abhinav.github.io/git-spice/cli/reference/#gs-upstack-submit) [the stack](https://abhinav.github.io/git-spice/cli/reference/#gs-downstack-submit). If a branch has already been submitted, git-spice will update the submission. If it has been merged, git-spice will automatically restack branches that depend on it. - [**Incremental improvements**](https://abhinav.github.io/git-spice/start/stack/index.md) ______________________________________________________________________ git-spice does not need to be adopted all at once. It does not expect you to flip your entire workflow upside down. Incorporate it into your workflow at your own pace, one feature at a time. - [**Offline-first**](https://abhinav.github.io/git-spice/guide/internals/index.md) ______________________________________________________________________ git-spice operates entirely locally. It talks directly to Git, and when you ask for it, to GitHub/GitLab. All state is stored locally in your Git repository. A network connection is not required, except when pushing or pulling. - [**Intuitive shorthands**](https://abhinav.github.io/git-spice/cli/shorthand/index.md) ______________________________________________________________________ Most commands in git-spice have easy-to-remember shorthands. For example, [gs branch create](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-create) can be shortened to [gs bc](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-create). Explore the list of shorthands with `--help` or at [Shorthands](https://abhinav.github.io/git-spice/cli/shorthand/index.md). We recommend adopting these incrementally. - [**Free and open-source**](https://github.com/abhinav/git-spice) ______________________________________________________________________ git-spice is free and open-source software. It is made available under the GPL-3.0 license. You may use it to develop proprietary software, but any changes you make to git-spice itself must be shared. # Get started # Get started Start here if you're just exploring and would like to see what git-spice can do. # Installation git-spice may be installed with a pre-built binary or built from source. System requirements - **Operating system**: Linux and macOS are fully supported. Windows support was added in [v0.6.0](https://abhinav.github.io/git-spice/changelog/#v0.6.0). - **Architecture**: x86_64 and aarch64 architectures are supported. Additionally on Linux, some 32-bit ARM architectures are also supported. - **Git**: At least Git 2.38 is required for git-spice to operate correctly. Earlier versions may work, but are not officially supported. ## Pre-built binary To install a **pre-built binary**, use one of the following methods: ### Homebrew/Linuxbrew git-spice is available in homebrew-core (the default formulae repository for Homebrew and Linuxbrew). Install git-spice with the following command: ``` brew install git-spice ``` You can also use my Homebrew Tap to install the latest release: ``` brew install --cask abhinav/tap/git-spice ``` ### Binary installation tools #### ubi [ubi](https://github.com/houseabsolute/ubi) is a binary installation tool that is able to download pre-built binaries from GitHub Releases. If you use ubi, use the following command to install git-spice: ``` ubi --project abhinav/git-spice --exe gs ``` #### mise [mise](https://mise.jdx.dev) supports installing tools from various sources, and includes a ubi backend. If you use mise, use the following command to install git-spice: ``` mise use --global 'ubi:abhinav/git-spice[exe=gs]' ``` ### AUR (ArchLinux) If you're using ArchLinux, install the 'git-spice-bin' package from the AUR: ``` git clone https://aur.archlinux.org/git-spice-bin.git cd git-spice-bin makepkg -si # Or, with an AUR helper like yay: yay -S git-spice-bin ``` ### Manual download You can manually download the latest release of git-spice from the [GitHub Releases page](https://github.com/abhinav/git-spice/releases). ## Build from source To **build from source**, follow these steps: 1. Install the [Go compiler](https://go.dev/dl). 1. Run the following command: ``` go install go.abhg.dev/gs@latest ``` ## Next steps - [Create your first stack](https://abhinav.github.io/git-spice/start/stack/index.md) With git-spice installed, start experimenting with stacking. ## Initialize git-spice 1. Start by creating an new Git repository to play inside. ``` mkdir repo cd repo git init git commit --allow-empty -m "Initial commit" ``` 1. Initialize git-spice in the repository. This will set up the internal storage for git-spice in your repository. ``` gs repo init ``` Info This step isn't absolutely required. git-spice will initialize itself automatically when needed. ## Track a branch Next, stack a branch on top of `main`. 1. Create a new branch and make some changes: ``` git checkout -b feat1 echo "Hello, world!" > hello.txt git add hello.txt git commit -m "Add hello.txt" ``` 1. Add the branch to git-spice. ``` gs branch track ``` This results in a single branch stacked on top of `main`. The above operations are frequently done together. git-spice provides a command to do all of them in one go: `gs branch create`. ## Use gs branch create Stack another branch on top of `feat1` with [gs branch create](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-create). 1. Check out feat1 and prepare another change: ``` echo "This project is cool!" > README.md git add README.md ``` 1. Create a new branch, commit the staged changes, and add the branch to git-spice. ``` gs branch create feat2 ``` Tip Use `gs branch create -a` to automatically stage changes to tracked files. This behaves similarly to `git commit -a`. This results in a stack that looks like this: ## Modify mid-stack Time to modify a branch in the middle of the stack. 1. Check out `feat1`: ``` gs down ``` Info The [gs down](https://abhinav.github.io/git-spice/cli/reference/#gs-down) command moves us "down" the stack, as opposed to [gs up](https://abhinav.github.io/git-spice/cli/reference/#gs-up) which moves us "up". 1. Make a change to `hello.txt` and commit it: ``` echo "How are you?" >> hello.txt git add hello.txt git commit -m "Add a question to hello.txt" ``` 1. **Restack** branches that are out of sync with the current branch. ``` gs upstack restack ``` Tip You can use [gs commit create](https://abhinav.github.io/git-spice/cli/reference/#gs-commit-create) to combine the commit and restack steps. 1. Go back to `feat2` and verify: ``` gs up cat hello.txt ``` ## Summary **This section covered:** - [gs branch create](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-create) is a shortcut for creating a branch, committing to it, and tracking it with git-spice. - [gs up](https://abhinav.github.io/git-spice/cli/reference/#gs-up) and [gs down](https://abhinav.github.io/git-spice/cli/reference/#gs-down) provide relative navigation through the stack. - [gs upstack restack](https://abhinav.github.io/git-spice/cli/reference/#gs-upstack-restack) updates branches that are out of sync with the current branch. ## Next steps - Use [gs commit create](https://abhinav.github.io/git-spice/cli/reference/#gs-commit-create) to combine the commit and restack steps - Explore different flags of [gs branch create](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-create) - [Create your first stacked CRs](https://abhinav.github.io/git-spice/start/submit/index.md) # Your first stacked Change Requests This page will walk you through creating and updating *GitHub Pull Requests* or *GitLab Merge Requests* with git-spice. git-spice refers to these as *Change Requests* (CRs). ## Prerequisites - [Create a stack of branches](https://abhinav.github.io/git-spice/start/stack/index.md). For this tutorial, we'll assume the following stack. Go back to the previous page if you need to create it. - Set up a GitHub or GitLab repository. Optional: Create an experimental repository If you're following along with the tutorial, you may want to create a new repository on GitHub to experiment with instead of using a real project. To do this, if you have the GitHub or GitLab CLI installed, run the following inside your experimental repository: ``` gh repo create gs-playground \ --public \ --source=$(pwd) \ --push ``` If you don't have the GitHub CLI installed, go to and follow the instructions there. ``` glab repo create gs-playground --public ``` If you don't have the GitLab CLI installed, go to and follow the instructions there. ## Create a Change Request 1. Check out `feat1`. ``` git checkout feat1 ``` ``` gs branch checkout feat1 ``` ``` gs bco feat1 ``` 1. Submit the change. ``` gs branch submit ``` ``` gs bs ``` 1. Follow the prompts on-screen to create the CR. For example: ``` $ gs branch submit Title: branch submit: Fix directory+ref conflict Body: Press [e] to open nvim or [enter/tab] to skip Draft: [y/N] INF Created #261: https://github.com/abhinav/git-spice/pull/261 ``` ## Stack on top 1. Check out `feat2`. ``` gs up ``` 1. Submit a CR. ``` gs branch submit ``` 1. Follow the prompts on-screen to create the CR. The new CR will now be stacked on top of the previous one: the remote will show `feat1` as the base branch for the CR. ## Modify mid-stack Modify the `feat1` branch and update the CR. 1. Check out `feat1`. ``` gs down ``` 1. Make some changes. ``` echo "Not bad, how about you?" >> hello.txt git add hello.txt gs commit create -m "follow up" ``` ``` echo "Not bad, how about you?" >> hello.txt git add hello.txt gs cc -m "follow up" ``` Info We use the [gs commit create](https://abhinav.github.io/git-spice/cli/reference/#gs-commit-create) command here. This will commit to the current branch, and rebase the upstack branches on top of the new commit. 1. Update all CRs in the stack. ``` gs stack submit ``` This will push to both CRs in the stack. If one of the branches was not submitted yet, it will prompt you to create a CR for it. ## Merge a CR 1. Open up the CR for `feat1` in your browser and merge it into `main`. 1. Run the following command to sync the stack with the trunk: ``` gs repo sync ``` This will delete `feat1` locally, and rebase `feat2` on top of `main`. 1. Submit the CR for `feat2` to update the pull request if necessary. ``` gs branch submit ``` ## Summary **This section covered:** - [gs branch submit](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-submit) creates or updates a CR for the current branch. - [gs stack submit](https://abhinav.github.io/git-spice/cli/reference/#gs-stack-submit) creates or updates CRs for the entire stack. - [gs repo sync](https://abhinav.github.io/git-spice/cli/reference/#gs-repo-sync) syncs the stack with the trunk branch, deletes merged branches, and rebases the remaining branches. ## Next steps - Explore other submit commands: [gs upstack submit](https://abhinav.github.io/git-spice/cli/reference/#gs-upstack-submit), [gs downstack submit](https://abhinav.github.io/git-spice/cli/reference/#gs-downstack-submit) - Browse the [User Guide](https://abhinav.github.io/git-spice/guide/index.md) to learn more # User Guide The User Guide covers the features of git-spice in more detail. For a list of all available commands, see the [Command Reference](https://abhinav.github.io/git-spice/cli/reference/index.md). # Concepts git-spice introduces a few concepts on top of Git. This section lists a few that are frequently used in the documentation. **Branch** : A regular Git branch. Branches can have a *base*: the branch they were created from. The branch currently checked out is called the *current branch*. ``` In the diagram, `B` is the current branch, and `A` is its base. ``` **Trunk** : The default branch of a repository. This is "main" or "master" in most repositories. Trunk is the only branch that does not have a base branch. **Change Request** : Change Request refers to a single merge-able unit of work submitted to GitHub or GitLab. Each Change Request corresponds to a branch. On GitHub, these are called Pull Requests, and on GitLab, they are called Merge Requests. Since git-spice supports both platforms, the term Change Request is used to refer to both. **Stack** : A stack is a collection of branches stacked on top of each other in a way that each branch except the trunk has a base branch. ``` In the diagram, `A` is stacked on top of trunk, `B` is stacked on top of `A`, and so on. A branch can have multiple branches stacked on top of it. ``` **Downstack** : Downstack refers to the branches below the current branch, all the way to, but not including, the trunk branch. **Upstack** : Upstack refers to the branches stacked on top of the current branch, those branches' upstacks, and so on until no more branches remain. If a branch has multiple branches stacked on top of it, they are both upstack from it. **Sibling** : A sibling to a branch is a branch that shares the same base branch. In the diagram, `F` is a sibling to `B`, and `C` is a sibling to `D`. **Restacking** : Restacking is the process of rebasing the contents of a branch on top of its base branch, which it may have diverged from. This is done to keep the branch up-to-date with its base branch, and maintain a linear history. # 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](https://abhinav.github.io/git-spice/guide/internals/index.md). Branches may be tracked manually or automatically: - [**Automatic stacking**](#automatic-stacking) with [gs branch create](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-create) - [**Manual stacking**](#manual-stacking) with [gs branch track](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-track) ### 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](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-create). The steps are simple: 1. Modify your code and prepare your changes to be committed with `git add`. 1. Run [gs branch create](https://abhinav.github.io/git-spice/cli/reference/#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](https://abhinav.github.io/git-spice/cli/reference/#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](https://abhinav.github.io/git-spice/community/recipes/#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](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-track) to inform git-spice of the branch after creating it. ``` $ git checkout -b feat1 # make your changes $ git commit $ gs branch track INF feat1: tracking with base main ``` For example, you may: 1. Create a new branch with `git checkout -b`. 1. Make changes and commit them with `git commit`. 1. Run [gs branch track](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-track) to track the branch. The [gs branch track](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-track) command automatically guesses the base branch for the newly tracked branch. Use the `--base` option to set it manually. #### Tracking multiple branches at once [v0.19.0](https://abhinav.github.io/git-spice/changelog/#v0.19.0) If you manually created a stack of branches, you can track them all at once with [gs downstack track](https://abhinav.github.io/git-spice/cli/reference/#gs-downstack-track). This command traverses the commit graph downwards from the current branch, identifying other branches that need to be tracked along the way. ``` $ gs downstack track Track fire with base: air Track air with base: earth Track earth with base: ▶ water None of these ``` The above would be similar to running: ``` $ gs branch track earth --base water $ gs branch track air --base earth $ gs branch track fire --base air ``` ## 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](https://abhinav.github.io/git-spice/setup/shell/index.md) 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](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-create) without any arguments. git-spice will use the commit message to generate a branch name for you. ## Navigating the stack git-spice offers the following commands to navigate within a stack of branches: - [gs down](https://abhinav.github.io/git-spice/cli/reference/#gs-down) checks out the branch below the current branch - [gs up](https://abhinav.github.io/git-spice/cli/reference/#gs-up) checks out a branch stacked on top of the current branch, prompting to pick one if there are multiple - [gs bottom](https://abhinav.github.io/git-spice/cli/reference/#gs-bottom) moves to the bottommost branch in the stack, right above the trunk - [gs top](https://abhinav.github.io/git-spice/cli/reference/#gs-top) moves to the topmost branch in the stack, prompting to pick one if there are multiple - [gs trunk](https://abhinav.github.io/git-spice/cli/reference/#gs-trunk) checks out the trunk branch - [gs branch checkout](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-checkout) checks out any branch in the repository 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](https://abhinav.github.io/git-spice/cli/reference/#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 bco Select a branch to checkout: ┏━■ docs ◀ ┃ ┏━□ github-optimization ┣━┻□ github-support main ``` 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](https://abhinav.github.io/git-spice/cli/reference/#gs-upstack-restack) to do this. ``` $ gs branch checkout feat1 $ $EDITOR file.txt # prepare your changes $ git commit $ gs upstack restack ``` Tip git-spice also offers [gs branch restack](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-restack) to restack just the current branch onto its base, and [gs stack restack](https://abhinav.github.io/git-spice/cli/reference/#gs-stack-restack) to restack all branches in the current stack. ### Automatic restacking git-spice provides several convenience commands that run common Git operations and automatically restack upstack branches. These include but are not limited to: - [gs commit create](https://abhinav.github.io/git-spice/cli/reference/#gs-commit-create) (or [gs cc](https://abhinav.github.io/git-spice/cli/reference/#gs-commit-create)) commits changes to the current branch and restacks upstack branches - [gs commit amend](https://abhinav.github.io/git-spice/cli/reference/#gs-commit-amend) (or [gs ca](https://abhinav.github.io/git-spice/cli/reference/#gs-commit-amend)) amends the last commit and restacks upstack branches - [gs commit split](https://abhinav.github.io/git-spice/cli/reference/#gs-commit-split) (or [gs csp](https://abhinav.github.io/git-spice/cli/reference/#gs-commit-split)) interactively splits the last commit into two and restacks upstack branches 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 ``` ### Editing commits in a branch The [gs branch edit](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-edit) command starts a `git rebase --interactive` for the commits in the current branch. ``` $ gs branch edit ``` ``` pick c0d0855d feat: Add support for things pick 78c047c5 doc: Document something pick 4dbf01a5 fix: Fix a thing that was broken ``` Use the usual Git rebase commands to edit, reorder, squash, or split commits. After the rebase operation completes successfully, upstack branches will be restacked on top of the current branch. #### Handling rebase interruptions ``` $ gs rebase continue $ gs rebase abort ``` If a rebase operation is interrupted due to a conflict, or because an `edit` or `break` instruction was used in the rebase script, git-spice will pause execution and let you resolve the issue. You may then: - Resolve the conflict, make any planned changes, and run [gs rebase continue](https://abhinav.github.io/git-spice/cli/reference/#gs-rebase-continue) (`gs rbc` for short) to let git-spice continue the rest of the operation; or - Run [gs rebase abort](https://abhinav.github.io/git-spice/cli/reference/#gs-rebase-abort) (`gs rba` for short) to abort the operation and go back to the state before the rebase started. ### Squashing commits in a branch [v0.11.0](https://abhinav.github.io/git-spice/changelog/#v0.11.0) ``` $ gs branch squash ``` [gs branch squash](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-squash) will squash all commits in the current branch and open an editor to let you write a commit message for the squashed commit. Use the `-m`/`--message` flag to provide a message without opening an editor. If you want to squash only a subset of commits in the branch, use [gs branch edit](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-edit) and add `squash` or `fixup` commands. ## Inserting into the stack By default, [gs branch create](https://abhinav.github.io/git-spice/cli/reference/#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 ``` ``` $ gs branch checkout feat1 $ gs branch create feat4 ``` ## Splitting the stack Use [gs branch split](https://abhinav.github.io/git-spice/cli/reference/#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 split Select 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](https://abhinav.github.io/git-spice/cli/reference/#gs-commit-split) and [gs branch edit](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-edit) to safely and easily manipulate the commits in the branch before splitting it into multiple branches. [v0.8.0](https://abhinav.github.io/git-spice/changelog/#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 pangolin ▶ penguin Branch being split has an open CR assigned to it. Select which branch should take over the CR. ``` [v0.18.0](https://abhinav.github.io/git-spice/changelog/#v0.18.0) When prompted for branch names during a split, you can reuse the original branch name for one of the intermediate commits. When you do this, the original branch will be reassigned to that commit, preserving its metadata (such as change requests), and you'll be prompted to provide a new name for the remaining HEAD commits. This gives you more control over which commits retain the original branch's identity. ### 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](https://abhinav.github.io/git-spice/cli/reference/#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 ``` 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](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-onto) for this purpose. ``` $ gs branch checkout feat2 $ gs branch onto main ``` 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](https://abhinav.github.io/git-spice/cli/reference/#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 feat2 INF feat2: deleted (was 644a286) ``` 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](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-untrack) command. # Working with remote repositories Note This page assumes you are using one of the supported Git forges. These are: - GitHub - GitLab ([v0.9.0](https://abhinav.github.io/git-spice/changelog/#v0.9.0)) If you're using a different service, you can still use git-spice, but some features may not be available. See: - [Recipes > Working with unsupported remotes](https://abhinav.github.io/git-spice/community/recipes/#working-with-unsupported-remotes) - [FAQ > Will git-spice add support for other Git hosting services](https://abhinav.github.io/git-spice/community/faq/#will-git-spice-add-support-for-other-git-hosting-services) ## Submitting change requests Info git-spice uses the term *Change Request* to refer to submitted branches. These corespond to Pull Requests on GitHub, and to Merge Requests on GitLab. When your local changes are ready, use the following commands to submit your changes upstream: - [gs branch submit](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-submit) (or [gs bs](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-submit)) submits the current branch - [gs downstack submit](https://abhinav.github.io/git-spice/cli/reference/#gs-downstack-submit) (or [gs dss](https://abhinav.github.io/git-spice/cli/reference/#gs-downstack-submit)) submits the current branch and all branches below it - [gs upstack submit](https://abhinav.github.io/git-spice/cli/reference/#gs-upstack-submit) (or [gs uss](https://abhinav.github.io/git-spice/cli/reference/#gs-upstack-submit)) submits the current branch and all branches above it - [gs stack submit](https://abhinav.github.io/git-spice/cli/reference/#gs-stack-submit) (or [gs ss](https://abhinav.github.io/git-spice/cli/reference/#gs-stack-submit)) submits all branches in the stack Branch submission is an idempotent operation: change requests will be created for branches that don't already have them, and updated for branches that do. For new change requests, these commands will prompt you for CR information. For example: ``` $ gs branch submit Title: branch submit: Fix directory+ref conflict Body: Press [e] to open nvim or [enter/tab] to skip Draft: [y/N] INF Created #261: https://github.com/abhinav/git-spice/pull/261 ``` Important Be aware that for stacks with multiple branches, you must have write access to the repository so that you can push branches to it. See [Limitations](https://abhinav.github.io/git-spice/guide/limits/index.md) for more information. ### Navigation comments Change Requests created by git-spice will include a navigation comment at the top with a visual representation of the stack, and the position of the current branch in it. This behavior may be changed with the [spice.submit.navigationComment](https://abhinav.github.io/git-spice/cli/config/#spicesubmitnavigationcomment) configuration key. Stack history in navigation comments [v0.10.0](https://abhinav.github.io/git-spice/changelog/#v0.10.0) When possible, git-spice will remember CRs as they're merged into trunk, and continue to list them in navigation comments of branches based on those changes. However, it is unable to do this following complex stack manipulation operations. ### Non-interactive submission Use the `--fill` flag (or `-c` since [v0.3.0](https://abhinav.github.io/git-spice/changelog/#v0.3.0)) provided by all the above commands to fill in the PR information from commit messages and skip the interactive prompts. ``` $ gs stack submit --fill INF Created #123: https://github.com/abhinav/git-spice/pull/123 INF Created #125: https://github.com/abhinav/git-spice/pull/125 ``` Additionally, with [gs branch submit](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-submit), you may also specify title and body directly. ``` $ gs branch submit \ --title "Fix a bug" \ --body "This fixes a very bad bug." INF Created #123: https://github.com/abhinav/git-spice/pull/123 ``` Setting draft status non-interactively Change requests may be marked as draft or ready for review non-interactively with the `--draft` and `--no-draft` flags. By default, the submit commands will leave the draft state of existing PRs unchanged. If the `--draft` or `--no-draft` flags are provided, the draft state of all PRs will be set accordingly. ### Force pushing [v0.2.0](https://abhinav.github.io/git-spice/changelog/#v0.2.0) ``` $ gs branch submit --force ``` By default, git-spice will refuse to push to branches if the operation could result in data loss. To override these safety checks and push to a branch anyway, use the `--force` flag. ### Update existing CRs only [v0.10.0](https://abhinav.github.io/git-spice/changelog/#v0.10.0) All submit commands support the `--update-only` flag. If provided, the submission will update existing CRs in a stack, but not create new ones. This is most convenient with [gs stack submit](https://abhinav.github.io/git-spice/cli/reference/#gs-stack-submit) and friends, allowing you to iterate on a local change that isn't ready for submission, while still being able to pull updates into downstack PRs that have already been submitted. Example workflow Suppose we're starting with a stack: ``` main -> bird (#1) -> fish (#2) -> goat ``` `bird` and `fish` have already been submitted, `goat` is in-progress. ``` # While working in goat, make a minor fixup to bird. [goat] $ gs commit create -m 'bird: preen a little' # Pull that change into bird [goat] $ gs branch checkout bird [bird] $ git restore --source $(gs top -n) -- bird.go [bird] $ gs commit create -a -m 'preen a little' INF fish: restacked on bird INF goat: restacked on fish # Update fish and bird in one command without submitting goat [bird] $ gs stack submit --update-only INF bird: Updated #1 INF goat: Updated #2 INF goat: Skipping unsubmitted branch: --update-only ``` Tip The above example makes use of the `-n`/`--dry-run` flag of the [stack navigation commands](https://abhinav.github.io/git-spice/guide/branch/#navigating-the-stack). With this flag, the command prints the hash of the target branch without checking it out. ## Syncing with upstream To sync with the upstream repository, use [gs repo sync](https://abhinav.github.io/git-spice/cli/reference/#gs-repo-sync) (or [gs rs](https://abhinav.github.io/git-spice/cli/reference/#gs-repo-sync)). ``` $ gs repo sync INF main: pulled 3 new commit(s) INF feat1: #123 was merged INF feat1: deleted (was 9f1c9af) ``` This will update the trunk branch (e.g. `main`) with the latest changes from the upstream repository, and delete any local branches whose PRs have been merged. ### Handling closed Change Requests When running [gs repo sync](https://abhinav.github.io/git-spice/cli/reference/#gs-repo-sync), if a Change Request was closed (but not merged), git-spice will detect this and offer you the choice of deleting that branch locally. If you prefer not to be prompted about closed CRs, you can set the configuration option to ignore them: ``` $ git config spice.repoSync.closedChanges ignore ``` For more details, see the [configuration reference](https://abhinav.github.io/git-spice/cli/config/#spicereposyncclosedchanges). ## Adding labels [v0.16.0](https://abhinav.github.io/git-spice/changelog/#v0.16.0) You can add labels to change requests when submitting them with the `-l`/`--label` flag. ``` $ gs branch submit --label bug --label urgent INF Created #123: https://github.com/abhinav/git-spice/pull/123 ``` You can also configure default labels with the [spice.submit.label](https://abhinav.github.io/git-spice/cli/config/#spicesubmitlabel) configuration option. Labels specified with the `--label` flag will be combined with configured labels. ``` $ git config spice.submit.label 'needs-review' ``` When updating existing change requests, new labels are added to any existing labels on the CR. ## Requesting reviews [v0.21.0](https://abhinav.github.io/git-spice/changelog/#v0.21.0) You can request reviews from specific users or teams when submitting change requests with the `-r`/`--reviewer` flag. ``` $ gs branch submit --reviewer alice --reviewer bob INF Created #123: https://github.com/abhinav/git-spice/pull/123 ``` You can also configure default reviewers with the [spice.submit.reviewers](https://abhinav.github.io/git-spice/cli/config/#spicesubmitreviewers) configuration option. Reviewers specified with the `--reviewer` flag will be combined with configured reviewers. ``` $ git config spice.submit.reviewers 'alice,myorg/backend-team' ``` When updating existing change requests, new reviewers are added to any existing reviewers on the CR. ## Assigning change requests [v0.21.0](https://abhinav.github.io/git-spice/changelog/#v0.21.0) You can assign change requests to specific users when submitting them with the `-a`/`--assign` flag. ``` $ gs branch submit --assign alice --assign bob INF Created #123: https://github.com/abhinav/git-spice/pull/123 ``` You can also configure default assignees with the [spice.submit.assignees](https://abhinav.github.io/git-spice/cli/config/#spicesubmitassignees) configuration option. Assignees specified with the `--assign` flag will be combined with configured assignees. ``` $ git config spice.submit.assignees 'alice,bob' ``` When updating existing change requests, new assignees are added to any existing assignees on the CR. ## Importing open CRs You can import an existing open CR into git-spice by checking it out locally, tracking the branch with git-spice, and re-submitting it. For example: ``` # Check out the PR locally $ gh pr checkout 359 # Track it with git-spice $ gs branch track # Re-submit it $ gs branch submit INF comment-recovery: Found existing CR #359 INF CR #359 is up-to-date: https://github.com/abhinav/git-spice/pull/359 ``` ``` # Check out the MR locally $ glab mr checkout 8 # Track it with git-spice $ gs branch track # Re-submit it $ gs branch submit INF reticulating-splines: Found existing CR !8 INF CR !8 is up-to-date: https://gitlab.com/abg/test-repo/-/merge_requests/8 ``` Important For this to work, the following MUST all be true: - The CR is pushed to a branch in the upstream repository - The local branch name exactly matches the upstream branch name This will work even for CRs that were not created by git-spice, or authored by someone else, as long as the above conditions are met. In [v0.5.0](https://abhinav.github.io/git-spice/changelog/#v0.5.0) or newer, this will also auto-detect [navigation comments](#navigation-comments) posted to the PR by git-spice, and update them if necessary. ``` $ gs branch submit INF comment-recovery: Found existing CR #359 INF comment-recovery: Found existing navigation comment: ... ``` ### Importing stacks of CRs [v0.19.0](https://abhinav.github.io/git-spice/changelog/#v0.19.0) If you have a stack of related CRs to import, check out all the branches locally and use [gs downstack track](https://abhinav.github.io/git-spice/cli/reference/#gs-downstack-track) to track them all at once. ``` # Check out the PRs locally $ gh pr checkout 359 $ gh pr checkout 360 $ gh pr checkout 361 # Track the entire downstack $ gs downstack track ``` See also [Recipes > Track an existing stack](https://abhinav.github.io/git-spice/community/recipes/#track-an-existing-stack). Usage of git-spice with GitHub and GitLab runs into limitations of what is possible on those platforms, and how they handle Git commits. Some limitations imposed on git-spice are listed below. ## Write access required When a branch `F` is stacked on another branch `B`, and you want to submit Change Requests for both, the CR for `F` will be created against `B`. To do this, git-spice needs to push both branches to the same repository. Therefore, to use git-spice to stack PRs, you need write access to the repository: specifically the ability to push new branches. ## Squash-merges restack the upstack On GitHub, when a Pull Request is squash-merged into the trunk branch, all commits in that PR are replaced with a single commit with a different hash. Similarly on GitLab, when fast-forward merges are enabled, and commits are squashed, the commits in the MR are replaced with a single commit with a different hash. The branches upstack from that CR are not aware of this new commit, still referring to the old, unsquashed history of the branch. GitHub and GitLab do not yet know to reconcile this new commit with the upstack branches, even though the contents are the same. As a result of this, when a branch is squash-merged into the trunk branch, branches upstack from it need to be restacked, and all their CRs updated. ## Base branch change may dismiss approvals Some remote repositories are configured to dismiss prior approvals of PRs when the base branch of that PR is changed. There is no workaround to this except to reconfigure the repository as this setting is fundamentally incompatible with a PR stacking workflow. This page covers common issues you may encounter while using git-spice and their solutions. ## `fatal: Cannot rebase onto multiple branches.` [gs repo sync](https://abhinav.github.io/git-spice/cli/reference/#gs-repo-sync) may fail with the following error intermittently: ``` fatal: Cannot rebase onto multiple branches. ``` If the command succeeds on retry, the error is likely caused by background Git processes that are running concurrently with git-spice operations. Common sources of this include: - Shell plugins configured to automatically fetch Git repository updates - Editors or IDEs that automatically sync Git repositories - Git GUI clients that run background synchronization tasks To fix this, disable automatic Git fetching for your environment. Common solutions include: - **Pure Zsh**: Add the following to your `.zshrc`: ``` export PURE_GIT_PULL=0 ``` - **VS Code**: add the following to your `settings.json`: ``` "git.autofetch": false ``` # git-spice internals Warning This page covers internal details about the implementation of git-spice. Most users do not need to read this. It is presented here for the curious, and in the interest of transparency. Do not rely on internal details to remain stable. These may change at any time. ## Local storage git-spice stores information about your repository and branches in a local Git ref named: `refs/spice/data`. Information is stored in JSON files with roughly the following structure: ``` repo # repository-level information templates # cached change templates rebase-continue # information about ongoing operations 📁 branches # branch tracking information ├── feat1 ├── feat2 └── ... 📁 prepared # ephemeral per-branch PR form information ├── feat1 ├── feat2 └── ... ``` git-spice operations that manipulate this information will usually include what prompted the change. You can explore this information by running the following command in a repository using git-spice: ``` git log --patch refs/spice/data ``` ## Git interactions git-spice does not use a third-party Git implementation. All operations are performed directly against the Git CLI, often relying on Git's [plumbing commands](https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Porcelain). Why? Most third-party Git implementations trail behind in feature parity. For example, many third-party implementations can misbehave when you make use of advanced features like [git worktree](https://git-scm.com/docs/git-worktree), [git sparse-checkout](https://git-scm.com/docs/git-sparse-checkout), or [sparse indexes](https://git-scm.com/docs/sparse-index). By relying on the highly scriptable and machine-consumable Git plumbing we don't have to deal with those issues. # Setting up # Setting up This section guides you through various **optional** setup steps for git-spice. # Authentication git-spice is offline-first. It does not require authentication for local stacking operations. However, once you want to push or pull changes to/from a remote repository, you will need to authenticate with the respective service. This page covers methods to authenticate git-spice with GitHub and GitLab. Note that GitLab support requires at least version [v0.9.0](https://abhinav.github.io/git-spice/changelog/#v0.9.0). ## Logging in Take the following steps to authenticate with a service: 1. Run the following command: ``` gs auth login ``` 1. Pick the service you want to authenticate with. ``` Select a Forge:   ▶ github gitlab ``` 1. You will be presented with a list of authentication methods. Pick the one that suits you best. Tip Skip prompt (2) by running [gs auth login](https://abhinav.github.io/git-spice/cli/reference/#gs-auth-login) inside a Git repository cloned from GitHub or GitLab. ## Authentication methods Each supported service supports different authentication methods. - [OAuth](#oauth): GitHub GitLab - [GitHub App](#github-app): GitHub - [Personal Access Token](#personal-access-token): GitHub GitLab - [Service CLI](#service-cli): GitHub GitLab - [Environment variable](#environment-variable): GitHub GitLab Read on for more details on each method, or skip on to [Pick an authentication method](#picking-an-authentication-method). ### OAuth **Supported by** GitHub GitLab With OAuth authentication, you will take the following steps: 1. Authenticate yourself on the service website in your browser. 1. Authorize git-spice to act on your behalf on the **current device only**. ``` $ gs auth login Select an authentication method: OAuth 1. Visit https://github.com/login/device 2. Enter code: ABCD-1234 The code expires in a few minutes. It will take a few seconds to verify after you enter it. ``` On GitHub, OAuth is available in two flavors: - **OAuth**: grants access to all repositories, public and private. - **OAuth: Public repositories only**: grants access to public repositories only. For more granular control than that, use [GitHub App](#github-app) authentication. Note For private repositories owned by organizations, you will need a member with administrative access to the repository to allow installation of the git-spice OAuth App. If that is not an option, use a [Personal Access Token](#personal-access-token). For Self-Hosted GitLab instances, an administrator will need to set up a git-spice OAuth App. Be sure to **uncheck** the "Confidential" option when creating the App. If that is not an option, use a [Personal Access Token](#personal-access-token). ### GitHub App **Supported by** GitHub With GitHub App authentication, you will take the following steps: 1. Authenticate yourself on github.com in your browser. 1. Authorize git-spice to act on your behalf on the **current device only**. 1. Install the [git-spice GitHub App](https://github.com/apps/git-spice) on the repositories you want to use git-spice with. ``` $ gs auth login Select an authentication method: GitHub App 1. Visit https://github.com/login/device 2. Enter code: ABCD-1234 The code expires in a few minutes. It will take a few seconds to verify after you enter it. ``` **Important**: Authentication alone does not grant any access. You **must** install the GitHub App to access repositories with git-spice. Note For private repositories owned by organizations, you will need a member with administrative access to the repository to allow installation of the git-spice GitHub App. If that is not an option, use a [Personal Access Token](#personal-access-token). ### Personal Access Token **Supported by** GitHub GitLab To use a Personal Access Token with git-spice, you will generate a Personal Access Token on the website and enter it in the prompt. ``` $ gs auth login Select an authentication method: Personal Access Token Enter Personal Access Token: ``` The token may be a classic token or a fine-grained token. With classic tokens, you can grant access to all repositories, or all public repositories only. These tokens have the ability to never expire. To use a classic token: 1. Go to . This may ask you to re-authenticate. 1. In the token creation form: - enter a descriptive note for the token - pick an expiration window, or select "No expiration" - select the following scopes: `repo`, `read:org` for full access to all repositories, or just `public_repo` for access to public repositories only 1. Click "Generate token" and copy the token. With fine-grained tokens, you have more granular control over repositories that you grant access to. These token must always have an expiration date. To use a fine-grained token: 1. Go to . This may ask you to re-authenticate. 1. In the token creation form: - pick a descriptive note for the token - pick an expiration window - in the *Repository access* section, select the repositories you want to use git-spice with - in the *Repository permissions* section, grant **Read and write** access to **Pull requests** and **Contents** - ([v0.21.0](https://abhinav.github.io/git-spice/changelog/#v0.21.0) or higher) in the *Organization permissions* section, grant **Read-only** access to **Members** 1. Click "Generate token" and copy the token. To use a Personal Access Token with GitLab: 1. Go to . 1. Select *Add new token* 1. In the token creation form: - pick a descriptive name for the token - pick an expiration date if needed - select the `api` scope After you have a token, enter it into the prompt. ### Service CLI **Supported by** GitHub GitLab If you have the GitHub or GitLab CLIs installed and authenticated, you can get authentication tokens for git-spice from them. 1. Install the [GitHub CLI](https://github.com/cli/cli#installation) 1. Authenticate it: ``` $ gh auth login ``` 1. Install the [GitLab CLI](https://gitlab.com/gitlab-org/cli#installation). 1. Authenticate it: ``` $ glab auth login ``` Once you pick this authentication option, no additional steps are required. git-spice will request a token from the CLI as needed. ### Environment variable **Supported by** GitHub GitLab You can provide the authentication token as an environment variable. This is not recommended as a primary authentication method, but it can be useful in CI/CD environments. Set the `GITHUB_TOKEN` environment variable to your token. Set the `GITLAB_TOKEN` environment variable to your token. If you have the environment variable set, this takes precedence over all other authentication methods. The [gs auth login](https://abhinav.github.io/git-spice/cli/reference/#gs-auth-login) operation will always fail if you use this method. ## Picking an authentication method [OAuth](#oauth) is best if you have the permissions needed to install it on all repositories that you want to use git-spice with. Additionally, on GitHub, [GitHub App](#github-app) is similar, but it may be preferable if you don't want to give git-spice access to all your repositories. [Service CLI](#service-cli) is the most convenient method if you already have the CLI for the service installed and authenticated, and your organization already allows its use. It loses security benefits of the other methods, as it re-uses the token assigned to the CLI. For example, it you lose the ability to revoke the git-spice token without revoking the CLI token. [Personal Access Token](#personal-access-token) is flexible and secure. It may be used even with repositories where you don't have permission to install OAuth or GitHub Apps. However, it requires manual token management, making it less convenient. [Environment variable](#environment-variable) is the least convenient and the least secure method. End users should typically never pick this. It is intended only for CI/CD environments where you have no other choice. ## Self-hosted instances ### GitHub Enterprise To use git-spice with a GitHub Enterprise instance, inform it of the instance URL, authenticate, and use git-spice as usual. Set the [spice.forge.github.url](https://abhinav.github.io/git-spice/cli/config/#spiceforgegithuburl) configuration option to the address of your GitHub Enterprise instance. ``` $ git config spice.forge.github.url https://github.example.com ``` **Optionally**, also set the GitHub API URL with the [spice.forge.github.apiUrl](https://abhinav.github.io/git-spice/cli/config/#spiceforgegithubapiurl) configuration option. By default, the API URL is assumed to be at `/api` under the GitHub URL. ``` $ git config spice.forge.github.apiUrl https://github.example.com/api ``` The GitHub API URL will typically end with `/api`, not `/api/v3` or similar. Alternatively, these configuration options may also be set with the `GITHUB_URL` and `GITHUB_API_URL` environment variables. Set the `GITHUB_URL` and `GITHUB_API_URL` environment variables to the address of your GitHub Enterprise instance and its API endpoint, respectively. These must both be set for git-spice to work with GitHub Enterprise. ``` export GITHUB_URL=https://github.example.com export GITHUB_API_URL=https://github.example.com/api ``` ### GitLab Self-Hosted To use git-spice with a self-hosted GitLab instance, set [spice.forge.gitlab.url](https://abhinav.github.io/git-spice/cli/config/#spiceforgegitlaburl) to the address of your GitLab instance. ``` $ git config spice.forge.gitlab.url https://gitlab.example.com ``` [v0.13.0](https://abhinav.github.io/git-spice/changelog/#v0.13.0) *Optionally*, also set the GitLab API URL with the [spice.forge.gitlab.apiUrl](https://abhinav.github.io/git-spice/cli/config/#spiceforgegitlabapiurl) configuration option. By default, the API URL is the same as the GitLab URL. ``` $ git config spice.forge.gitlab.apiUrl https://gitlab.example.com/api/v4 ``` Alternatively, set these configuration options with the `GITLAB_URL` and `GITLAB_API_URL` environment variables. ``` export GITLAB_URL=https://gitlab.example.com export GITLAB_API_URL=https://gitlab-api.example.com ``` #### OAuth with GitLab Self-Hosted To use OAuth authentication with a self-hosted GitLab instance, you must first set up an OAuth App on the GitLab instance. Be sure to **uncheck** the "Confidential" option when creating the App. This will generate an OAuth Client ID for the App. Feed that into git-spice with the [spice.forge.gitlab.oauth.clientID](https://abhinav.github.io/git-spice/cli/config/#spiceforgegitlaboauthclientid) configuration option. ``` $ git config spice.forge.gitlab.oauth.clientID your-client-id ``` This may also be set with the `GITLAB_OAUTH_CLIENT_ID` environment variable. ``` export GITLAB_OAUTH_CLIENT_ID=your-client-id ``` Authenticate with [gs auth login](https://abhinav.github.io/git-spice/cli/reference/#gs-auth-login) as usual after that. ## Safety By default, git-spice stores your authentication token in a system-specific secure storage. On macOS, this is the system Keychain. On Linux, it uses the [Secret Service](https://specifications.freedesktop.org/secret-service/latest/), which is typically provided by [GNOME Keyring](https://specifications.freedesktop.org/secret-service/latest/). Since version [v0.3.0](https://abhinav.github.io/git-spice/changelog/#v0.3.0), if your system does not provide a secure storage service, git-spice will fall back to storing secrets in a plain-text file at `$XDG_CONFIG_HOME/git-spice/secrets.json` or the user's configuration directory. If it does that, it will clearly indicate so at login time, reporting the full path to the secrets file. Example ``` $ gs auth login ... WRN Storing secrets in plain text at /home/user/.config/git-spice/secrets.json. Be careful! INF github: successfully logged in ``` # Shell completion git-spice supports completion for Bash, Zsh, and Fish. To set up completion, follow the instructions below. Add the following line to your `.bashrc` or `.bash_profile`: ``` eval "$(gs shell completion bash)" ``` Add the following line to your `.zshrc` or `.zprofile`: ``` eval "$(gs shell completion zsh)" ``` Add the following line to your `config.fish`: ``` eval "$(gs shell completion fish)" ``` Then restart the shell or create a new shell session. Debugging shell completion If shell completion misbehaves and you want to file a bug report, first retry the completion with `COMP_DEBUG=1` set. ``` $ export COMP_DEBUG=1 $ gs branch onto # ``` This will print debugging information about the completion process. Include this output in your bug report. # CLI # CLI The git-spice CLI is delivered as the command `gs`. Explore the [complete list of available commands here](https://abhinav.github.io/git-spice/cli/reference/index.md). # CLI Reference ``` gs [flags] ``` gs (git-spice) is a command line tool for stacking Git branches. **Global flags** - `-h`, `--help`: Show help for the command - `--version`: Print version information and quit - `-v`, `--verbose`, `$GIT_SPICE_VERBOSE`: Enable verbose output - `-C`, `--dir=DIR`: Change to DIR before doing anything - `--[no-]prompt`: Whether to prompt for missing information **Configuration**: [spice.forge.github.apiUrl](https://abhinav.github.io/git-spice/cli/config/#spiceforgegithubapiurl), [spice.forge.github.url](https://abhinav.github.io/git-spice/cli/config/#spiceforgegithuburl), [spice.forge.gitlab.apiURL](https://abhinav.github.io/git-spice/cli/config/#spiceforgegitlabapiurl), [spice.forge.gitlab.oauth.clientID](https://abhinav.github.io/git-spice/cli/config/#spiceforgegitlaboauthclientid), [spice.forge.gitlab.removeSourceBranch](https://abhinav.github.io/git-spice/cli/config/#spiceforgegitlabremovesourcebranch), [spice.forge.gitlab.url](https://abhinav.github.io/git-spice/cli/config/#spiceforgegitlaburl) ## Shell ### gs shell completion ``` gs shell completion [] ``` Generate shell completion script To set up shell completion, eval the output of this command from your shell's rc file. For example: ``` # bash eval "$(gs shell completion bash)" # zsh eval "$(gs shell completion zsh)" # fish eval "$(gs shell completion fish)" ``` If shell name is not provided, the current shell is guessed using a heuristic. **Arguments** - `shell`: Shell to generate completions for. ## Authentication ### gs auth login ``` gs auth login [flags] ``` Log in to a service A prompt will allow selecting between available authentication methods. Available methods include: - OAuth: Web-based authentication flow - GitHub App (GitHub only): Authenticate using a GitHub App installation - Personal Access Token: Directly provide a personal access token - CLI: Use the GitHub or GitLab CLI tool for authentication (if installed) The differences between them are explained in the prompt. The authentication token is stored in a system-provided secure storage if available. Use 'gs auth logout' to log out and delete the token from storage. Fails if already logged in. Use --refresh to force a refresh of the authentication token or change the authentication method. **Flags** - `--refresh`: Force a refresh of the authentication token ### gs auth status ``` gs auth status [flags] ``` Show current login status Exits with a non-zero code if not logged in. ### gs auth logout ``` gs auth logout [flags] ``` Log out of a service The stored authentication information is deleted. Use 'gs auth login' to log in again. Does not do anything if not logged in. ## Repository ### gs repo init ``` gs repo (r) init (i) [flags] ``` Initialize a repository A trunk branch is required. This is the branch that changes will be merged into. A prompt will ask for one if not provided with --trunk. Most branch stacking operations are local and do not require a network connection. For operations that push or pull commits, a remote is required. A prompt will ask for one during initialization if not provided with --remote. Re-run the command on an already initialized repository to change the trunk or remote. If the trunk branch is changed on re-initialization, existing branches stacked on the old trunk will be updated to point to the new trunk. Re-run with --reset to discard all stored information and untrack all branches. **Flags** - `--trunk=BRANCH`: Name of the trunk branch - `--remote=NAME`: Name of the remote to push changes to - `--reset`: Forget all information about the repository ### gs repo sync ``` gs repo (r) sync (s) [flags] ``` Pull latest changes from the remote Branches with merged Change Requests will be deleted after syncing. The repository must have a remote associated for syncing. A prompt will ask for one if the repository was not initialized with a remote. **Flags** - `--restack`: Restack the current stack after syncing **Configuration**: [spice.repoSync.closedChanges](https://abhinav.github.io/git-spice/cli/config/#spicereposyncclosedchanges) ### gs repo restack ``` gs repo (r) restack (r) ``` [v0.16.0](https://abhinav.github.io/git-spice/changelog/#v0.16.0) Restack all tracked branches All tracked branches in the repository are rebased on top of their respective bases in dependency order, ensuring a linear history. ## Log ### gs log short ``` gs log (l) short (s) [flags] ``` List branches Only branches that are upstack and downstack from the current branch are shown. Use with the -a/--all flag to show all tracked branches. With --json, prints output to stdout as a stream of JSON objects. See https://abhinav.github.io/git-spice/cli/json/ for details. **Flags** - `-a`, `--all` (): Show all tracked branches, not just the current stack. - `-S`, `--[no-]cr-status` (): Request and include information about the Change Request - `--json`: Write to stdout as a stream of JSON objects in an unspecified order [v0.18.0](https://abhinav.github.io/git-spice/changelog/#v0.18.0) **Configuration**: [spice.log.all](https://abhinav.github.io/git-spice/cli/config/#spicelogall), [spice.log.crFormat](https://abhinav.github.io/git-spice/cli/config/#spicelogcrformat), [spice.log.crStatus](https://abhinav.github.io/git-spice/cli/config/#spicelogcrstatus), [spice.log.pushStatusFormat](https://abhinav.github.io/git-spice/cli/config/#spicelogpushstatusformat), [spice.logLong.crFormat](https://abhinav.github.io/git-spice/cli/config/#spiceloglongcrformat), [spice.logShort.crFormat](https://abhinav.github.io/git-spice/cli/config/#spicelogshortcrformat) ### gs log long ``` gs log (l) long (l) [flags] ``` List branches and commits Only branches that are upstack and downstack from the current branch are shown. Use with the -a/--all flag to show all tracked branches. With --json, prints output to stdout as a stream of JSON objects. See https://abhinav.github.io/git-spice/cli/json/ for details. **Flags** - `-a`, `--all` (): Show all tracked branches, not just the current stack. - `-S`, `--[no-]cr-status` (): Request and include information about the Change Request - `--json`: Write to stdout as a stream of JSON objects in an unspecified order [v0.18.0](https://abhinav.github.io/git-spice/changelog/#v0.18.0) **Configuration**: [spice.log.all](https://abhinav.github.io/git-spice/cli/config/#spicelogall), [spice.log.crFormat](https://abhinav.github.io/git-spice/cli/config/#spicelogcrformat), [spice.log.crStatus](https://abhinav.github.io/git-spice/cli/config/#spicelogcrstatus), [spice.log.pushStatusFormat](https://abhinav.github.io/git-spice/cli/config/#spicelogpushstatusformat), [spice.logLong.crFormat](https://abhinav.github.io/git-spice/cli/config/#spiceloglongcrformat), [spice.logShort.crFormat](https://abhinav.github.io/git-spice/cli/config/#spicelogshortcrformat) ## Stack ### gs stack submit ``` gs stack (s) submit (s) [flags] ``` Submit a stack Change Requests are created or updated for all branches in the current stack. Use --dry-run to print what would be submitted without submitting it. For new Change Requests, a prompt will allow filling metadata. Use --fill to populate title and body from the commit messages. The --[no-]draft flag marks the CR as draft or not. Use the 'spice.submit.draft' configuration option to mark new CRs as drafts (or not) by default, skipping the prompt. For updating Change Requests, use --[no-]draft to change its draft status. Without the flag, the draft status is not changed. Use --no-publish to push branches without creating CRs. This has no effect if a branch already has an open CR. Use --update-only to only update branches with existing CRs, and skip those that would create new CRs. Use --nav-comment=false to disable navigation comments in CRs, or --nav-comment=multiple to post those comments only if there are multiple CRs in the stack. **Flags** - `-n`, `--dry-run`: Don't actually submit the stack - `-c`, `--fill`: Fill in the change title and body from the commit messages - `--[no-]draft`: Whether to mark change requests as drafts - `--[no-]publish` (): Whether to create CRs for pushed branches. Defaults to true. - `-w`, `--web` (): Open submitted changes in a web browser. Accepts an optional argument: 'true', 'false', 'created'. - `--nav-comment=true` (): Whether to add a navigation comment to the change request. Must be one of: true, false, multiple. - `--force`: Force push, bypassing safety checks - `--no-verify`: Bypass pre-push hooks when pushing to the remote. [v0.15.0](https://abhinav.github.io/git-spice/changelog/#v0.15.0) - `-u`, `--[no-]update-only`: Only update existing change requests, do not create new ones - `-l`, `--label=LABEL,...`: Add labels to the change request. Pass multiple times or separate with commas. - `-r`, `--reviewer=REVIEWER,...`: Add reviewers to the change request. Pass multiple times or separate with commas. [v0.21.0](https://abhinav.github.io/git-spice/changelog/#v0.21.0) - `-a`, `--assign=ASSIGNEE,...`: Assign the change request to these users. Pass multiple times or separate with commas. [v0.21.0](https://abhinav.github.io/git-spice/changelog/#v0.21.0) - `--no-web`: Alias for --web=false. **Configuration**: [spice.submit.assignees](https://abhinav.github.io/git-spice/cli/config/#spicesubmitassignees), [spice.submit.draft](https://abhinav.github.io/git-spice/cli/config/#spicesubmitdraft), [spice.submit.label](https://abhinav.github.io/git-spice/cli/config/#spicesubmitlabel), [spice.submit.listTemplatesTimeout](https://abhinav.github.io/git-spice/cli/config/#spicesubmitlisttemplatestimeout), [spice.submit.navigationComment](https://abhinav.github.io/git-spice/cli/config/#spicesubmitnavigationcomment), [spice.submit.navigationComment.downstack](https://abhinav.github.io/git-spice/cli/config/#spicesubmitnavigationcommentdownstack), [spice.submit.navigationCommentStyle.marker](https://abhinav.github.io/git-spice/cli/config/#spicesubmitnavigationcommentstylemarker), [spice.submit.navigationCommentSync](https://abhinav.github.io/git-spice/cli/config/#spicesubmitnavigationcommentsync), [spice.submit.publish](https://abhinav.github.io/git-spice/cli/config/#spicesubmitpublish), [spice.submit.reviewers](https://abhinav.github.io/git-spice/cli/config/#spicesubmitreviewers), [spice.submit.reviewers.addWhen](https://abhinav.github.io/git-spice/cli/config/#spicesubmitreviewersaddwhen), [spice.submit.template](https://abhinav.github.io/git-spice/cli/config/#spicesubmittemplate), [spice.submit.updateOnly](https://abhinav.github.io/git-spice/cli/config/#spicesubmitupdateonly), [spice.submit.web](https://abhinav.github.io/git-spice/cli/config/#spicesubmitweb) ### gs stack restack ``` gs stack (s) restack (r) [flags] ``` Restack a stack All branches in the current stack are rebased on top of their respective bases, ensuring a linear history. Use --branch to rebase the stack of a different branch. **Flags** - `--branch=NAME`: Branch to restack the stack of ### gs stack edit ``` gs stack (s) edit (e) [flags] ``` Edit the order of branches in a stack This operation requires a linear stack: no branch can have multiple branches above it. An editor opens with a list of branches in the current stack in-order, with the topmost branch at the top of the file, and the branch closest to the trunk at the bottom. Modifications to the list will be reflected in the stack when the editor is closed. If the file is cleared, no changes will be made. Branches that are deleted from the list will be ignored. **Flags** - `--editor=STRING`: Editor to use for editing the downstack. Defaults to Git's default editor. - `--branch=NAME`: Branch whose stack we're editing. Defaults to current branch. ### gs stack delete ``` gs stack (s) delete (d) [flags] ``` [v0.16.0](https://abhinav.github.io/git-spice/changelog/#v0.16.0) Delete all branches in a stack Deletes all branches in the current branch's stack. This includes both upstack and downstack branches. The deleted branches and their commits are removed from the stack. This is a convenient way to clean up completed or abandoned feature stacks. As this is a destructive operation, you must use the --force flag to confirm deletion. **Flags** - `--force`: Force deletion of the branches ### gs upstack submit ``` gs upstack (us) submit (s) [flags] ``` Submit a branch and those above it Change Requests are created or updated for the current branch and all branches upstack from it. If the base of the current branch is not trunk, it must have already been submitted by a prior command. Use --branch to start at a different branch. Use --dry-run to print what would be submitted without submitting it. For new Change Requests, a prompt will allow filling metadata. Use --fill to populate title and body from the commit messages. The --[no-]draft flag marks the CR as draft or not. Use the 'spice.submit.draft' configuration option to mark new CRs as drafts (or not) by default, skipping the prompt. For updating Change Requests, use --[no-]draft to change its draft status. Without the flag, the draft status is not changed. Use --no-publish to push branches without creating CRs. This has no effect if a branch already has an open CR. Use --update-only to only update branches with existing CRs, and skip those that would create new CRs. Use --nav-comment=false to disable navigation comments in CRs, or --nav-comment=multiple to post those comments only if there are multiple CRs in the stack. **Flags** - `-n`, `--dry-run`: Don't actually submit the stack - `-c`, `--fill`: Fill in the change title and body from the commit messages - `--[no-]draft`: Whether to mark change requests as drafts - `--[no-]publish` (): Whether to create CRs for pushed branches. Defaults to true. - `-w`, `--web` (): Open submitted changes in a web browser. Accepts an optional argument: 'true', 'false', 'created'. - `--nav-comment=true` (): Whether to add a navigation comment to the change request. Must be one of: true, false, multiple. - `--force`: Force push, bypassing safety checks - `--no-verify`: Bypass pre-push hooks when pushing to the remote. [v0.15.0](https://abhinav.github.io/git-spice/changelog/#v0.15.0) - `-u`, `--[no-]update-only`: Only update existing change requests, do not create new ones - `-l`, `--label=LABEL,...`: Add labels to the change request. Pass multiple times or separate with commas. - `-r`, `--reviewer=REVIEWER,...`: Add reviewers to the change request. Pass multiple times or separate with commas. [v0.21.0](https://abhinav.github.io/git-spice/changelog/#v0.21.0) - `-a`, `--assign=ASSIGNEE,...`: Assign the change request to these users. Pass multiple times or separate with commas. [v0.21.0](https://abhinav.github.io/git-spice/changelog/#v0.21.0) - `--no-web`: Alias for --web=false. - `--branch=NAME`: Branch to start at **Configuration**: [spice.submit.assignees](https://abhinav.github.io/git-spice/cli/config/#spicesubmitassignees), [spice.submit.draft](https://abhinav.github.io/git-spice/cli/config/#spicesubmitdraft), [spice.submit.label](https://abhinav.github.io/git-spice/cli/config/#spicesubmitlabel), [spice.submit.listTemplatesTimeout](https://abhinav.github.io/git-spice/cli/config/#spicesubmitlisttemplatestimeout), [spice.submit.navigationComment](https://abhinav.github.io/git-spice/cli/config/#spicesubmitnavigationcomment), [spice.submit.navigationComment.downstack](https://abhinav.github.io/git-spice/cli/config/#spicesubmitnavigationcommentdownstack), [spice.submit.navigationCommentStyle.marker](https://abhinav.github.io/git-spice/cli/config/#spicesubmitnavigationcommentstylemarker), [spice.submit.navigationCommentSync](https://abhinav.github.io/git-spice/cli/config/#spicesubmitnavigationcommentsync), [spice.submit.publish](https://abhinav.github.io/git-spice/cli/config/#spicesubmitpublish), [spice.submit.reviewers](https://abhinav.github.io/git-spice/cli/config/#spicesubmitreviewers), [spice.submit.reviewers.addWhen](https://abhinav.github.io/git-spice/cli/config/#spicesubmitreviewersaddwhen), [spice.submit.template](https://abhinav.github.io/git-spice/cli/config/#spicesubmittemplate), [spice.submit.updateOnly](https://abhinav.github.io/git-spice/cli/config/#spicesubmitupdateonly), [spice.submit.web](https://abhinav.github.io/git-spice/cli/config/#spicesubmitweb) ### gs upstack restack ``` gs upstack (us) restack (r) [flags] ``` Restack a branch and its upstack The current branch and all branches above it are rebased on top of their respective bases, ensuring a linear history. Use --branch to start at a different branch. Use --skip-start to skip the starting branch, but still rebase all branches above it. **Flags** - `--skip-start`: Do not restack the starting branch - `--branch=NAME`: Branch to restack the upstack of ### gs upstack onto ``` gs upstack (us) onto (o) [] [flags] ``` Move a branch onto another branch The current branch and its upstack will move onto the new base. A prompt will allow selecting the new base for the branch. Provide an argument to skip the prompt. Use the --branch flag to target a different branch for the move. For example, given the following stack with B checked out, 'gs upstack onto main' will have the following effect: ``` gs upstack onto main ┌── C ┌── C ┌─┴ B ◀ ┌─┴ B ◀ ┌─┴ A ├── A trunk trunk ``` Use 'gs branch onto' to leave the branch's upstack alone. **Arguments** - `onto`: Destination branch **Flags** - `--branch=NAME`: Branch to start at **Configuration**: [spice.branchPrompt.sort](https://abhinav.github.io/git-spice/cli/config/#spicebranchpromptsort) ### gs upstack delete ``` gs upstack (us) delete (d) [flags] ``` [v0.16.0](https://abhinav.github.io/git-spice/changelog/#v0.16.0) Delete all branches above the current branch Deletes all branches above the current branch in the stack, not including the current branch. The current branch remains unchanged. This is a convenient way to clean up abandoned or completed parts of a feature stack. As this is a destructive operation, you must use the --force flag to confirm deletion. **Flags** - `--force`: Force deletion of the branches ### gs downstack track ``` gs downstack (ds) track (tr) [] ``` Track all untracked branches below a branch Track all untracked branches in the downstack of a branch. Starting from the specified branch (or current branch), identify and track any untracked branches downstack from it, until reaching trunk or an already-tracked branch. **Arguments** - `branch`: Name of the branch to start tracking from ### gs downstack submit ``` gs downstack (ds) submit (s) [flags] ``` Submit a branch and those below it Change Requests are created or updated for the current branch and all branches below it until trunk. Use --branch to start at a different branch. Use --dry-run to print what would be submitted without submitting it. For new Change Requests, a prompt will allow filling metadata. Use --fill to populate title and body from the commit messages. The --[no-]draft flag marks the CR as draft or not. Use the 'spice.submit.draft' configuration option to mark new CRs as drafts (or not) by default, skipping the prompt. For updating Change Requests, use --[no-]draft to change its draft status. Without the flag, the draft status is not changed. Use --no-publish to push branches without creating CRs. This has no effect if a branch already has an open CR. Use --update-only to only update branches with existing CRs, and skip those that would create new CRs. Use --nav-comment=false to disable navigation comments in CRs, or --nav-comment=multiple to post those comments only if there are multiple CRs in the stack. **Flags** - `-n`, `--dry-run`: Don't actually submit the stack - `-c`, `--fill`: Fill in the change title and body from the commit messages - `--[no-]draft`: Whether to mark change requests as drafts - `--[no-]publish` (): Whether to create CRs for pushed branches. Defaults to true. - `-w`, `--web` (): Open submitted changes in a web browser. Accepts an optional argument: 'true', 'false', 'created'. - `--nav-comment=true` (): Whether to add a navigation comment to the change request. Must be one of: true, false, multiple. - `--force`: Force push, bypassing safety checks - `--no-verify`: Bypass pre-push hooks when pushing to the remote. [v0.15.0](https://abhinav.github.io/git-spice/changelog/#v0.15.0) - `-u`, `--[no-]update-only`: Only update existing change requests, do not create new ones - `-l`, `--label=LABEL,...`: Add labels to the change request. Pass multiple times or separate with commas. - `-r`, `--reviewer=REVIEWER,...`: Add reviewers to the change request. Pass multiple times or separate with commas. [v0.21.0](https://abhinav.github.io/git-spice/changelog/#v0.21.0) - `-a`, `--assign=ASSIGNEE,...`: Assign the change request to these users. Pass multiple times or separate with commas. [v0.21.0](https://abhinav.github.io/git-spice/changelog/#v0.21.0) - `--no-web`: Alias for --web=false. - `--branch=NAME`: Branch to start at **Configuration**: [spice.submit.assignees](https://abhinav.github.io/git-spice/cli/config/#spicesubmitassignees), [spice.submit.draft](https://abhinav.github.io/git-spice/cli/config/#spicesubmitdraft), [spice.submit.label](https://abhinav.github.io/git-spice/cli/config/#spicesubmitlabel), [spice.submit.listTemplatesTimeout](https://abhinav.github.io/git-spice/cli/config/#spicesubmitlisttemplatestimeout), [spice.submit.navigationComment](https://abhinav.github.io/git-spice/cli/config/#spicesubmitnavigationcomment), [spice.submit.navigationComment.downstack](https://abhinav.github.io/git-spice/cli/config/#spicesubmitnavigationcommentdownstack), [spice.submit.navigationCommentStyle.marker](https://abhinav.github.io/git-spice/cli/config/#spicesubmitnavigationcommentstylemarker), [spice.submit.navigationCommentSync](https://abhinav.github.io/git-spice/cli/config/#spicesubmitnavigationcommentsync), [spice.submit.publish](https://abhinav.github.io/git-spice/cli/config/#spicesubmitpublish), [spice.submit.reviewers](https://abhinav.github.io/git-spice/cli/config/#spicesubmitreviewers), [spice.submit.reviewers.addWhen](https://abhinav.github.io/git-spice/cli/config/#spicesubmitreviewersaddwhen), [spice.submit.template](https://abhinav.github.io/git-spice/cli/config/#spicesubmittemplate), [spice.submit.updateOnly](https://abhinav.github.io/git-spice/cli/config/#spicesubmitupdateonly), [spice.submit.web](https://abhinav.github.io/git-spice/cli/config/#spicesubmitweb) ### gs downstack edit ``` gs downstack (ds) edit (e) [flags] ``` Edit the order of branches below a branch An editor opens with a list of branches in-order, starting from the current branch until trunk. The current branch is at the top of the list. Use --branch to start at a different branch. Modifications to the list will be reflected in the stack when the editor is closed, and the topmost branch will be checked out. If the file is cleared, no changes will be made. Branches that are deleted from the list will be ignored. Branches that are upstack of the current branch will not be modified. **Flags** - `--editor=STRING`: Editor to use for editing the downstack. Defaults to Git's default editor. - `--branch=NAME`: Branch to edit from. Defaults to current branch. ## Branch ### gs branch track ``` gs branch (b) track (tr) [] [flags] ``` Track a branch A branch must be tracked to be able to run gs operations on it. Use 'gs branch create' to automatically track new branches. The base is guessed by comparing against other tracked branches. Use --base to specify a base explicitly. Use 'gs downstack track' from the topmost branch to track a manully created stack of branches at once. **Arguments** - `branch`: Name of the branch to track **Flags** - `-b`, `--base=BRANCH`: Base branch this merges into ### gs branch untrack ``` gs branch (b) untrack (untr) [] ``` Forget a tracked branch The current branch is deleted from git-spice's data store but not deleted from the repository. Branches upstack from it are not affected, and will use the next branch downstack as their new base. Provide a branch name as an argument to target a different branch. **Arguments** - `branch`: Name of the branch to untrack. Defaults to current. ### gs branch checkout ``` gs branch (b) checkout (co) [] [flags] ``` Switch to a branch A prompt will allow selecting between tracked branches. Provide a branch name as an argument to skip the prompt. Use -u/--untracked to show untracked branches in the prompt. Use --detach to detach HEAD to the commit of the selected branch. Use -n to print the selected branch name to stdout without checking it out. **Arguments** - `branch`: Name of the branch to checkout **Flags** - `-n`, `--dry-run`: Print the target branch without checking it out - `--detach`: Detach HEAD after checking out - `-u`, `--untracked` (): Show untracked branches if one isn't supplied **Configuration**: [spice.branchCheckout.showUntracked](https://abhinav.github.io/git-spice/cli/config/#spicebranchcheckoutshowuntracked), [spice.branchCheckout.trackUntrackedPrompt](https://abhinav.github.io/git-spice/cli/config/#spicebranchcheckouttrackuntrackedprompt), [spice.branchPrompt.sort](https://abhinav.github.io/git-spice/cli/config/#spicebranchpromptsort), [spice.checkout.verbose](https://abhinav.github.io/git-spice/cli/config/#spicecheckoutverbose) ### gs branch create ``` gs branch (b) create (c) [] [flags] ``` Create a new branch Staged changes will be committed to the new branch. If there are no staged changes, an empty commit will be created. Use -a/--all to automatically stage modified and deleted files, just like 'git commit -a'. Use --no-commit to create the branch without committing. -m/--message always implies --commit. If a branch name is not provided, it will be generated from the commit message. If the 'spice.branchCreate.prefix' configuration option is set, branch names will be prefixed with its value. If the 'spice.branchCreate.generatedBranchNameLimit' configuration option is set, auto-generated branch names will be truncated to that length at word boundaries (defaults to 32). The new branch will use the current branch as its base. Use --target to specify a different base branch. --insert will move the branches upstack from the target branch on top of the new branch. --below will create the new branch below the target branch. For example, given the following stack, with A checked out: ``` ┌── C ┌─┴ B ┌─┴ A ◀ trunk ``` 'gs branch create X' will have the following effects with different flags: ``` gs branch create X default │ --insert │ --below ──────────┼──────────────┼────────── ┌── X │ ┌── C │ ┌── C │ ┌── C │ ┌─┴ B │ ┌─┴ B ├─┴ B │ ┌─┴ X │ ┌─┴ A ┌─┴ A │ ┌─┴ A │ ┌─┴ X trunk │ trunk │ trunk ``` In all cases above, use of -t/--target flag will change the target (A) to the specified branch: ``` gs branch create X --target B default │ --insert │ --below ──────────┼──────────────┼──────────── ┌── X │ ┌── C │ ┌── C ├── C │ ┌─┴ X │ ┌─┴ B ┌─┴ B │ ┌─┴ B │ ┌─┴ X ┌─┴ A │ ┌─┴ A │ ┌─┴ A trunk │ trunk │ trunk ``` **Arguments** - `name`: Name of the new branch **Flags** - `--insert`: Restack the upstack of the target branch onto the new branch - `--below`: Place the branch below the target branch and restack its upstack - `-t`, `--target=BRANCH`: Branch to create the new branch above/below - `-a`, `--all`: Automatically stage modified and deleted files - `-m`, `--message=MSG`: Commit message - `--no-verify`: Bypass pre-commit and commit-msg hooks. - `--signoff` (): Add Signed-off-by trailer to the commit message - `--[no-]commit` (): Commit staged changes to the new branch, or create an empty commit **Configuration**: [spice.branchCreate.commit](https://abhinav.github.io/git-spice/cli/config/#spicebranchcreatecommit), [spice.branchCreate.generatedBranchNameLimit](https://abhinav.github.io/git-spice/cli/config/#spicebranchcreategeneratedbranchnamelimit), [spice.branchCreate.prefix](https://abhinav.github.io/git-spice/cli/config/#spicebranchcreateprefix), [spice.commit.signoff](https://abhinav.github.io/git-spice/cli/config/#spicecommitsignoff) ### gs branch delete ``` gs branch (b) delete (d,rm) [ ...] [flags] ``` Delete branches The deleted branches and their commits are removed from the stack. Branches above the deleted branches are first rebased onto the next branches available downstack, or onto trunk if there are no branches available below. Without any arguments, a prompt will allow selecting the branch to delete. By default, if the branch to be deleted has unmerged changes, the deletion will be aborted. Use --force to delete the branch regardless of unmerged changes. **Arguments** - `branches`: Names of the branches to delete **Flags** - `--force`: Force deletion of the branch **Configuration**: [spice.branchPrompt.sort](https://abhinav.github.io/git-spice/cli/config/#spicebranchpromptsort) ### gs branch fold ``` gs branch (b) fold (fo) [flags] ``` Merge a branch into its base Commits from the current branch will be merged into its base and the current branch will be deleted. Branches above the folded branch will point to the next branch downstack. Use the --branch flag to target a different branch. **Flags** - `--branch=NAME`: Name of the branch ### gs branch split ``` gs branch (b) split (sp) [flags] ``` Split a branch on commits Splits the current branch into two or more branches at specific commits, inserting the new branches into the stack at the positions of the commits. Use the --branch flag to specify a different branch to split. The command will prompt for commits to introduce splits at. Supply the --at flag one or more times to split a branch without a prompt. ``` --at COMMIT:NAME ``` Where COMMIT resolves to a commit per gitrevisions(7), and NAME is the name of the new branch. For example: ``` # split at a specific commit gs branch split --at 1234567:newbranch # split at the previous commit gs branch split --at HEAD^:newbranch ``` If the original branch is assigned to one of the splits, it is required to provide a new name for the commit at HEAD. Fo example, if we have branch A with three commits: ``` ┌─ A │ abcdef1 Commit 3 (HEAD) │ bcdef12 Commit 2 │ cdef123 Commit 1 trunk ``` A split at commit 2 using the branch name "A" would require a new name to be provided for commit 3. **Flags** - `--at=COMMIT:NAME,...`: Commits to split the branch at. - `--branch=NAME`: Branch to split commits of. ### gs branch squash ``` gs branch (b) squash (sq) [flags] ``` [v0.11.0](https://abhinav.github.io/git-spice/changelog/#v0.11.0) Squash a branch into one commit Squash all commits in the current branch into a single commit and restack upstack branches. An editor will open to edit the commit message of the squashed commit. Use the -m/--message flag to specify a commit message without editing. **Flags** - `--no-verify`: Bypass pre-commit and commit-msg hooks. - `--no-edit`: Do not open an editor to edit the squashed commit message. Only applicable if --message is not used. [v0.16.0](https://abhinav.github.io/git-spice/changelog/#v0.16.0) - `-m`, `--message=MSG`: Use the given message as the commit message. - `--branch=NAME`: Branch to squash. Defaults to current branch. [v0.16.0](https://abhinav.github.io/git-spice/changelog/#v0.16.0) ### gs branch edit ``` gs branch (b) edit (e) ``` Edit the commits in a branch Starts an interactive rebase with only the commits from this branch. After the rebase, branches upstack from this branch will be restacked. ### gs branch rename ``` gs branch (b) rename (rn,mv) [ []] ``` Rename a branch The following usage modes are supported: ``` # Rename to gs branch rename # Rename current branch to gs branch rename # Rename current branch interactively gs branch rename ``` If a branch was renamed outside of 'gs', for example with 'git branch -m', the branch tracking information will be out of date. To fix this, untrack the old branch name with 'gs branch untrack ', and track the new branch name with 'gs branch track '. **Arguments** - `old-name`: Old name of the branch - `new-name`: New name of the branch ### gs branch restack ``` gs branch (b) restack (r) [flags] ``` Restack a branch The current branch will be rebased onto its base, ensuring a linear history. Use --branch to target a different branch. **Flags** - `--branch=NAME`: Branch to restack ### gs branch onto ``` gs branch (b) onto (on) [] [flags] ``` Move a branch onto another branch Commits of the current branch are transplanted onto another branch while leaving the rest of the stack intact. That is, branches above the current branch are first rebased onto its original base, and then the current branch is moved onto the new base. A prompt will allow selecting the new base for the branch. Provide an argument to skip the prompt. Use the --branch flag to target a different branch for the move. For example, given the following stack with B checked out, running 'gs branch onto main' will move B onto main and leave C on top of A. ``` gs branch onto main ┌── C ┌── B ◀ ┌─┴ B ◀ │ ┌── C ┌─┴ A ├─┴ A trunk trunk ``` Use 'gs upstack onto' to also move the upstack branches. **Arguments** - `onto`: Destination branch **Flags** - `--branch=NAME`: Branch to move **Configuration**: [spice.branchPrompt.sort](https://abhinav.github.io/git-spice/cli/config/#spicebranchpromptsort) ### gs branch submit ``` gs branch (b) submit (s) [flags] ``` Submit a branch A Change Request is created for the current branch, or updated if it already exists. Use the --branch flag to target a different branch. For new Change Requests, a prompt will allow filling metadata. Use the --title and --body flags to skip the prompt, or the --fill flag to use the commit message to fill them in. The --[no-]draft flag marks the CR as draft or not. Use the 'spice.submit.draft' configuration option to mark new CRs as drafts (or not) by default, skipping the prompt. For updating Change Requests, use --[no-]draft to change its draft status. Without the flag, the draft status is not changed. Use --no-publish to push branches without creating CRs. This has no effect if a branch already has an open CR. Use --update-only to only update branches with existing CRs, and skip those that would create new CRs. Use --nav-comment=false to disable navigation comments in CRs, or --nav-comment=multiple to post those comments only if there are multiple CRs in the stack. **Flags** - `-n`, `--dry-run`: Don't actually submit the stack - `-c`, `--fill`: Fill in the change title and body from the commit messages - `--[no-]draft`: Whether to mark change requests as drafts - `--[no-]publish` (): Whether to create CRs for pushed branches. Defaults to true. - `-w`, `--web` (): Open submitted changes in a web browser. Accepts an optional argument: 'true', 'false', 'created'. - `--nav-comment=true` (): Whether to add a navigation comment to the change request. Must be one of: true, false, multiple. - `--force`: Force push, bypassing safety checks - `--no-verify`: Bypass pre-push hooks when pushing to the remote. [v0.15.0](https://abhinav.github.io/git-spice/changelog/#v0.15.0) - `-u`, `--[no-]update-only`: Only update existing change requests, do not create new ones - `-l`, `--label=LABEL,...`: Add labels to the change request. Pass multiple times or separate with commas. - `-r`, `--reviewer=REVIEWER,...`: Add reviewers to the change request. Pass multiple times or separate with commas. [v0.21.0](https://abhinav.github.io/git-spice/changelog/#v0.21.0) - `-a`, `--assign=ASSIGNEE,...`: Assign the change request to these users. Pass multiple times or separate with commas. [v0.21.0](https://abhinav.github.io/git-spice/changelog/#v0.21.0) - `--no-web`: Alias for --web=false. - `--title=TITLE`: Title of the change request - `--body=BODY`: Body of the change request - `--branch=NAME`: Branch to submit **Configuration**: [spice.submit.assignees](https://abhinav.github.io/git-spice/cli/config/#spicesubmitassignees), [spice.submit.draft](https://abhinav.github.io/git-spice/cli/config/#spicesubmitdraft), [spice.submit.label](https://abhinav.github.io/git-spice/cli/config/#spicesubmitlabel), [spice.submit.listTemplatesTimeout](https://abhinav.github.io/git-spice/cli/config/#spicesubmitlisttemplatestimeout), [spice.submit.navigationComment](https://abhinav.github.io/git-spice/cli/config/#spicesubmitnavigationcomment), [spice.submit.navigationComment.downstack](https://abhinav.github.io/git-spice/cli/config/#spicesubmitnavigationcommentdownstack), [spice.submit.navigationCommentStyle.marker](https://abhinav.github.io/git-spice/cli/config/#spicesubmitnavigationcommentstylemarker), [spice.submit.navigationCommentSync](https://abhinav.github.io/git-spice/cli/config/#spicesubmitnavigationcommentsync), [spice.submit.publish](https://abhinav.github.io/git-spice/cli/config/#spicesubmitpublish), [spice.submit.reviewers](https://abhinav.github.io/git-spice/cli/config/#spicesubmitreviewers), [spice.submit.reviewers.addWhen](https://abhinav.github.io/git-spice/cli/config/#spicesubmitreviewersaddwhen), [spice.submit.template](https://abhinav.github.io/git-spice/cli/config/#spicesubmittemplate), [spice.submit.web](https://abhinav.github.io/git-spice/cli/config/#spicesubmitweb) ## Commit ### gs commit create ``` gs commit (c) create (c) [flags] ``` Create a new commit Staged changes are committed to the current branch. Branches upstack are restacked if necessary. Use this as a shortcut for 'git commit' followed by 'gs upstack restack'. An editor is opened to edit the commit message. Use the -m/--message option to specify the message without opening an editor. Git hooks are run unless the --no-verify flag is given. Use the -a/--all flag to stage all changes before committing. Use the --fixup flag to create a new commit that will be merged into another commit when run with 'git rebase --autosquash'. See also, the 'gs commit fixup' command, which is preferable when you want to apply changes to an older commit. **Flags** - `-a`, `--all`: Stage all changes before committing. - `--allow-empty`: Create a new commit even if it contains no changes. - `--fixup=COMMIT`: Create a fixup commit. See also 'gs commit fixup'. - `-m`, `--message=MSG`: Use the given message as the commit message. - `--no-verify`: Bypass pre-commit and commit-msg hooks. - `--signoff` (): Add Signed-off-by trailer to the commit message **Configuration**: [spice.commit.signoff](https://abhinav.github.io/git-spice/cli/config/#spicecommitsignoff) ### gs commit amend ``` gs commit (c) amend (a) [flags] ``` Amend the current commit Staged changes are amended into the topmost commit. Branches upstack are restacked if necessary. This is a shortcut for 'git commit --amend' followed by 'gs upstack restack'. Use 'gs commit fixup' to amend commits that are further downstack. An editor is opened to edit the commit message unless the --no-edit flag is given. Use the -m/--message option to specify the message on the command line. Git hooks are run unless the --no-verify flag is given. Use the -a/--all flag to stage all changes before committing. To prevent accidental amends on the trunk branch, a prompt will require confirmation when amending on trunk. The --no-prompt flag can be used to skip this prompt in scripts. **Flags** - `-a`, `--all`: Stage all changes before committing. - `--allow-empty`: Create a commit even if it contains no changes. - `-m`, `--message=MSG`: Use the given message as the commit message. - `--no-edit`: Don't edit the commit message - `--no-verify`: Bypass pre-commit and commit-msg hooks. - `--signoff` (): Add Signed-off-by trailer to the commit message **Configuration**: [spice.branchCreate.generatedBranchNameLimit](https://abhinav.github.io/git-spice/cli/config/#spicebranchcreategeneratedbranchnamelimit), [spice.branchCreate.prefix](https://abhinav.github.io/git-spice/cli/config/#spicebranchcreateprefix), [spice.commit.signoff](https://abhinav.github.io/git-spice/cli/config/#spicecommitsignoff) ### gs commit split ``` gs commit (c) split (sp) [flags] ``` Split the current commit Interactively select hunks from the current commit to split into new commits below it. Branches upstack are restacked as needed. **Flags** - `-m`, `--message=MSG`: Use the given message as the commit message. - `--no-verify`: Bypass pre-commit and commit-msg hooks. ### gs commit fixup ``` gs commit (c) fixup (f) [] [flags] ``` [commitFixup](https://abhinav.github.io/git-spice/cli/experiments/#commitfixup) Fixup a commit below the current commit Apply staged uncommited changes to another commit down the stack, and restack the rest of the stack on top of it. If a commit is not specified, a prompt is shown to select one. If the commit is specified, it must be reachable from the current commit, (i.e. it must be down the stack). If it's not possible to apply the staged changes to the commit without causing a conflict, the command will fail. This command requires at least Git 2.45. **Arguments** - `commit`: The commit to fixup. Must be reachable from the HEAD commit. ### gs commit pick ``` gs commit (c) pick (p) [] [flags] ``` [commitPick](https://abhinav.github.io/git-spice/cli/experiments/#commitpick) Cherry-pick a commit Apply the changes introduced by a commit to the current branch and restack the upstack branches. If a commit is not specified, a prompt will allow picking from commits of upstack branches of the current branch. Use the --from option to pick a commit from a different branch or its upstack. If it's not possible to cherry-pick the requested commit without causing a conflict, the command will fail. If the requested commit is a merge commit, the command will fail. This command requires at least Git 2.45. **Arguments** - `commit`: Commit to cherry-pick **Flags** - `--from=NAME`: Branch whose upstack commits will be considered. ## Rebase ### gs rebase continue ``` gs rebase (rb) continue (c) [flags] ``` Continue an interrupted operation Continues an ongoing git-spice operation interrupted by a git rebase after all conflicts have been resolved. For example, if 'gs upstack restack' gets interrupted because a conflict arises during the rebase, you can resolve the conflict and run 'gs rebase continue' (or its shorthand 'gs rbc') to continue the operation. The command can be used in place of 'git rebase --continue' even if a git-spice operation is not currently in progress. Use the --no-edit flag to continue without opening an editor. Make --no-edit the default by setting 'spice.rebaseContinue.edit' to false and use --edit to override it. **Flags** - `--[no-]edit` (): Whether to open an editor to edit the commit message. **Configuration**: [spice.rebaseContinue.edit](https://abhinav.github.io/git-spice/cli/config/#spicerebasecontinueedit) ### gs rebase abort ``` gs rebase (rb) abort (a) ``` Abort an operation Cancels an ongoing git-spice operation that was interrupted by a git rebase. For example, if 'gs upstack restack' encounters a conflict, cancel the operation with 'gs rebase abort' (or its shorthand 'gs rba'), going back to the state before the rebase. The command can be used in place of 'git rebase --abort' even if a git-spice operation is not currently in progress. ## Navigation ### gs up ``` gs up (u) [] [flags] ``` Move up one branch Checks out the branch above the current one. If there are multiple branches with the current branch as base, a prompt will allow picking between them. Use the -n flag to print the branch without checking it out. **Arguments** - `n`: Number of branches to move up. **Flags** - `-n`, `--dry-run`: Print the target branch without checking it out - `--detach`: Detach HEAD after checking out **Configuration**: [spice.checkout.verbose](https://abhinav.github.io/git-spice/cli/config/#spicecheckoutverbose) ### gs down ``` gs down (d) [] [flags] ``` Move down one branch Checks out the branch below the current branch. If the current branch is at the bottom of the stack, checks out the trunk branch. Use the -n flag to print the branch without checking it out. **Arguments** - `n`: Number of branches to move up. **Flags** - `-n`, `--dry-run`: Print the target branch without checking it out - `--detach`: Detach HEAD after checking out **Configuration**: [spice.checkout.verbose](https://abhinav.github.io/git-spice/cli/config/#spicecheckoutverbose) ### gs top ``` gs top (U) [flags] ``` Move to the top of the stack Checks out the top-most branch in the current branch's stack. If there are multiple possible top-most branches, a prompt will ask you to pick one. Use the -n flag to print the branch without checking it out. **Flags** - `-n`, `--dry-run`: Print the target branch without checking it out - `--detach`: Detach HEAD after checking out **Configuration**: [spice.checkout.verbose](https://abhinav.github.io/git-spice/cli/config/#spicecheckoutverbose) ### gs bottom ``` gs bottom (D) [flags] ``` Move to the bottom of the stack Checks out the bottom-most branch in the current branch's stack. Does nothing if already on the bottom-most branch, and returns an error if on trunk. Use -n to print the branch name to stdout without checking it out. **Flags** - `-n`, `--dry-run`: Print the target branch without checking it out - `--detach`: Detach HEAD after checking out **Configuration**: [spice.checkout.verbose](https://abhinav.github.io/git-spice/cli/config/#spicecheckoutverbose) ### gs trunk ``` gs trunk [flags] ``` Move to the trunk branch **Flags** - `-n`, `--dry-run`: Print the target branch without checking it out - `--detach`: Detach HEAD after checking out **Configuration**: [spice.checkout.verbose](https://abhinav.github.io/git-spice/cli/config/#spicecheckoutverbose) ## gs version ``` gs version [flags] ``` Print version information and quit **Flags** - `--short`: Print only the version number. # CLI configuration [v0.4.0](https://abhinav.github.io/git-spice/changelog/#v0.4.0) The behavior of git-spice can be customized with `git config`. Configuration options may be set at the user level with the `--global` flag, or at the repository level with the `--local` flag. ``` # Set an option for current user $ git config --global # Set an option for current repository $ git config --local ``` What about `--system` and `--worktree`? All configuration levels supported by `git config` are allowed, although `--system` and `--worktree` are less commonly used. Use `--worktree` to override repository-level settings for a specific [git-worktree](https://git-scm.com/docs/git-worktree). ## Available options ### spice.branchCheckout.showUntracked [v0.5.0](https://abhinav.github.io/git-spice/changelog/#v0.5.0) When running [gs branch checkout](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-checkout) without any arguments, git-spice presents a prompt to select a branch to checkout. This option controls whether untracked branches are shown in the prompt. **Accepted values:** - `true` - `false` (default) ### spice.branchCheckout.trackUntrackedPrompt [v0.14.0](https://abhinav.github.io/git-spice/changelog/#v0.14.0) If [gs branch checkout](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-checkout) is used to checkout a branch that is not tracked, git-spice will present a prompt like the following to begin tracking it: ``` $ gs branch checkout mybranch WRN mybranch: branch not tracked Do you want to track this branch now?: [Y/n] ``` This option allows you to disable the prompt if you frequently checkout untracked branches and don't want to be prompted to track them. **Accepted values:** - `true` (default) - `false` ### spice.branchPrompt.sort [v0.11.0](https://abhinav.github.io/git-spice/changelog/#v0.11.0) Commands like [gs branch checkout](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-checkout), [gs branch onto](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-onto), etc., that require a branch name will present an interactive prompt to select the branch when one isn't provided. This option controls the sort order of branches in the prompt. It is git-spice's equivalent of [git's branch.sort configuration](https://git-scm.com/docs/git-config#Documentation/git-config.txt-branchsort). Commonly used values are: - `committerdate`: sort by commit date - `refname`: sort by branch name (default) - `authordate`: sort by author date See [git-for-each-ref(1) field names](https://git-scm.com/docs/git-for-each-ref#_field_names) for a full list of available fields. Prefix a field name with `-` to sort in descending order. For example, use `-committerdate` to sort by commit date in descending order. ### spice.branchCreate.commit [v0.5.0](https://abhinav.github.io/git-spice/changelog/#v0.5.0) Whether [gs branch create](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-create) should commit staged changes to the new branch. Set this to `false` to default to creating new branches without committing, and use the `--commit` flag to commit changes when needed. - `true` (default) - `false` ### spice.branchCreate.prefix [v0.14.0](https://abhinav.github.io/git-spice/changelog/#v0.14.0) If set, the prefix will be prepended to the name of every branch created with [gs branch create](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-create). Commonly used values are: - `/`: the committer's name - `/`: the committer's username ### spice.branchCreate.generatedBranchNameLimit [v0.20.0](https://abhinav.github.io/git-spice/changelog/#v0.20.0) The maximum length of branch names which are automatically generated by [gs branch create](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-create). **Accepted values:** - Any integer (defaults to 32) ### spice.commit.signoff [v0.20.0](https://abhinav.github.io/git-spice/changelog/#v0.20.0) Whether commit commands should add a `Signed-off-by` trailer to commit messages by default. **Accepted values:** - `true` - `false` (default) ### spice.checkout.verbose [v0.16.0](https://abhinav.github.io/git-spice/changelog/#v0.16.0) Whether branch navigation commands ( [gs up](https://abhinav.github.io/git-spice/cli/reference/#gs-up), [gs down](https://abhinav.github.io/git-spice/cli/reference/#gs-down), [gs top](https://abhinav.github.io/git-spice/cli/reference/#gs-top), [gs bottom](https://abhinav.github.io/git-spice/cli/reference/#gs-bottom), [gs trunk](https://abhinav.github.io/git-spice/cli/reference/#gs-trunk), [gs branch checkout](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-checkout)) should print a message when switching branches. **Accepted values:** - `true` (default) - `false` ### spice.forge.github.apiUrl URL at which the GitHub API is available. Defaults to `$GITHUB_API_URL` if set, or computed from the GitHub URL if not set. See also: [GitHub Enterprise](https://abhinav.github.io/git-spice/setup/auth/#github-enterprise). ### spice.forge.github.url URL of the GitHub instance used for GitHub requests. Defaults to `$GITHUB_URL` if set, or `https://github.com` otherwise. See also: [GitHub Enterprise](https://abhinav.github.io/git-spice/setup/auth/#github-enterprise). ### spice.forge.gitlab.url [v0.9.0](https://abhinav.github.io/git-spice/changelog/#v0.9.0) URL of the GitLab instance used for GitLab requests. Defaults to `$GITLAB_URL` if set, or `https://gitlab.com` otherwise. See also [GitLab Self-Hosted](https://abhinav.github.io/git-spice/setup/auth/#gitlab-self-hosted). ### spice.forge.gitlab.apiUrl [v0.13.0](https://abhinav.github.io/git-spice/changelog/#v0.13.0) URL at which the GitLab API is available. Defaults to `$GITLAB_API_URL` if set, or the GitLab URL otherwise. See also [GitLab Self-Hosted](https://abhinav.github.io/git-spice/setup/auth/#gitlab-self-hosted). ### spice.forge.gitlab.oauth.clientID [v0.9.0](https://abhinav.github.io/git-spice/changelog/#v0.9.0) Client ID for OAuth authentication with GitLab. Defaults to git-spice's built-in Client ID (valid only for https://gitlab.com) or `$GITLAB_OAUTH_CLIENT_ID` if set. For Self-Hosted GitLab instances, you must set this value to a custom Client ID. See also [GitLab Self-Hosted](https://abhinav.github.io/git-spice/setup/auth/#gitlab-self-hosted). ### spice.forge.gitlab.removeSourceBranch [v0.18.0](https://abhinav.github.io/git-spice/changelog/#v0.18.0) Whether to remove the source branch when a Merge Request is merged. **Accepted values:** - `true` (default) - `false` ### spice.log.all Whether [gs log short](https://abhinav.github.io/git-spice/cli/reference/#gs-log-short) and [gs log long](https://abhinav.github.io/git-spice/cli/reference/#gs-log-long) should show all stacks by default, instead of showing just the current stack. **Accepted values:** - `true` - `false` (default) ### spice.log.crFormat [v0.13.0](https://abhinav.github.io/git-spice/changelog/#v0.13.0) Whether [gs log short](https://abhinav.github.io/git-spice/cli/reference/#gs-log-short) and [gs log long](https://abhinav.github.io/git-spice/cli/reference/#gs-log-long) should show the CR URL or the ID next to any remote branches. **Accepted values:** - `"url"`: show the CR URL - `"id"`: (default) show the CR ID ### spice.logShort.crFormat [v0.15.0](https://abhinav.github.io/git-spice/changelog/#v0.15.0) Override for [gs log short](https://abhinav.github.io/git-spice/cli/reference/#gs-log-short) to control how change requests are displayed. If not set, falls back to `spice.log.crFormat`. **Accepted values:** - `"url"`: show the CR URL - `"id"`: show the CR ID ### spice.logLong.crFormat [v0.15.0](https://abhinav.github.io/git-spice/changelog/#v0.15.0) Override for [gs log long](https://abhinav.github.io/git-spice/cli/reference/#gs-log-long) to control how change requests are displayed. If not set, falls back to `spice.log.crFormat`. **Accepted values:** - `"url"`: show the CR URL - `"id"`: show the CR ID ### spice.log.crStatus [v0.18.0](https://abhinav.github.io/git-spice/changelog/#v0.18.0) Specifies whether [gs log short](https://abhinav.github.io/git-spice/cli/reference/#gs-log-short) and [gs log long](https://abhinav.github.io/git-spice/cli/reference/#gs-log-long) should request and show the status of associated Change Requests. It is not recommended to set this option to true as it adds a network request for each 'gs log' operation. **Accepted values:** - `false` (default) - `true` ### spice.log.pushStatusFormat [v0.13.0](https://abhinav.github.io/git-spice/changelog/#v0.13.0) Whether [gs log short](https://abhinav.github.io/git-spice/cli/reference/#gs-log-short) and [gs log long](https://abhinav.github.io/git-spice/cli/reference/#gs-log-long) should show whether the branch is in sync with its pushed counterpart. **Accepted values:** - `true` (default): show the push status - `false`: don't show the push status - `"aheadBehind"`: show the number of outgoing and incoming commits in the form `⇡1⇣2`, where `⇡` indicates outgoing commits and `⇣` indicates incoming commits ### spice.rebaseContinue.edit [v0.10.0](https://abhinav.github.io/git-spice/changelog/#v0.10.0) Whether [gs rebase continue](https://abhinav.github.io/git-spice/cli/reference/#gs-rebase-continue) should open an editor to modify the commit message when continuing after resolving a rebase conflict. If set to false, you can opt in to opening the editor with the `--edit` flag. **Accepted values:** - `true` (default) - `false` ### spice.submit.draft [v0.16.0](https://abhinav.github.io/git-spice/changelog/#v0.16.0) Default value for the `--draft`/`--no-draft` flag when creating new change requests with [gs branch submit](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-submit) and friends. ``` $ git config spice.submit.draft true $ gs branch submit # ... Draft: [Y/n] Mark the change as a draft? $ git config spice.submit.draft false $ gs branch submit # ... Draft: [y/N] Mark the change as a draft? ``` This option affects both interactive and non-interactive modes: - In *non-interactive mode*, setting this value to true will cause git-spice to assume `--draft` when creating new change requests. - In *interactive mode*, this value is used as the default for the prompt asking whether to create the change request as a draft. **Accepted values:** - `true`: create CRs as drafts by default - `false` (default): create CRs as ready for review by default ### spice.submit.assignees [v0.21.0](https://abhinav.github.io/git-spice/changelog/#v0.21.0) Assign the configured users to all submitted and updated change requests when using [gs branch submit](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-submit) and friends. The value must be a comma-separated list of usernames. Assignees specified with the `--assign` flag will be combined with the configured assignees. ### spice.submit.label [v0.16.0](https://abhinav.github.io/git-spice/changelog/#v0.16.0) Add the configured labels to all submitted and updated change requests when using [gs branch submit](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-submit) and friends. The value must be a comma-separated list of labels. Labels specified with the `-l`/`--label` flags will be combined with the configured labels. ### spice.submit.reviewers [v0.21.0](https://abhinav.github.io/git-spice/changelog/#v0.21.0) Add the configured reviewers to all submitted and updated change requests when using [gs branch submit](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-submit) and friends. The value must be a comma-separated list of reviewers. For GitHub, use usernames for individual reviewers or `org/team` format for team reviewers. Reviewers specified with the `--reviewer` flag will be combined with the configured reviewers. ### spice.submit.reviewers.addWhen [v0.23.0](https://abhinav.github.io/git-spice/changelog/#v0.23.0) Controls when [configured reviewers](https://abhinav.github.io/git-spice/cli/config/#spicesubmitreviewers) are added to change requests. When set to `ready`, configured reviewers are skipped for draft CRs, except when added explicitly via the `--reviewer` flag. **Accepted values:** - `always` (default): add configured reviewers to all CRs - `ready`: only add configured reviewers when the CR is not a draft ### spice.submit.listTemplatesTimeout [v0.8.0](https://abhinav.github.io/git-spice/changelog/#v0.8.0) Maximum duration that [gs branch submit](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-submit) will wait to receive a list of available CR templates from the forge. If the timeout is reached, the command will proceed without a template. Value must be a duration string such as `5s`, `1m`, `1h`, etc. Defaults to `1s`. Bump this value if you see warnings like any of the following: ``` WRN Failed to cache templates err="cache templates: write object: hash-object: signal: killed" WRN Could not list change templates error="list templates: Post \"https://api.github.com/graphql\": context deadline exceeded" ``` Set to `0` to disable the timeout completely. ### spice.submit.template [v0.17.0](https://abhinav.github.io/git-spice/changelog/#v0.17.0) Template to use when submitting a change request with [gs branch submit](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-submit), and multiple templates are available. If set, this template will be selected without prompting the user to pick one. The value should match the filename of one of the available templates (e.g., `PULL_REQUEST_TEMPLATE.md`). **Example:** ``` git config spice.submit.template "PULL_REQUEST_TEMPLATE.md" ``` When this is configured and multiple templates exist, git-spice will automatically use the specified template without prompting the user for selection. ### spice.submit.navigationComment Specifies whether CR submission commands ( [gs branch submit](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-submit) and friends) should post or update a navigation comment to the CR. **Accepted values:** - `true` (default): always post or update navigation comments - `false`: don't post or update navigation comments - `multiple`: post or update navigation comments only for stacks with at least two CRs ### spice.submit.navigationCommentSync [v0.16.0](https://abhinav.github.io/git-spice/changelog/#v0.16.0) Controls which branches' navigation comments are synced (created or updated) when submitting branches. **Accepted values:** - `branch` (default): sync navigation comments only for the submitted branches - `downstack`: sync navigation comments for all downstack branches When set to `branch`, if a branch stacked on top of another branch is submitted with [gs branch submit](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-submit), only the navigation comment for the submitted branch will be updated. If both branches are submitted (e.g. with [gs downstack submit](https://abhinav.github.io/git-spice/cli/reference/#gs-downstack-submit)), then both branches' navigation comments will be updated. When set to `downstack`, any time a branch is submitted, git-spice will update the navigation comment for that branch and all branches below it in the stack. ### spice.submit.navigationCommentStyle.marker [v0.19.0](https://abhinav.github.io/git-spice/changelog/#v0.19.0) Specifies the marker to use for the current change in navigation comments. By default, git-spice uses `◀` to indicate the current change. This can be customized to any string. **Example:** ``` git config spice.submit.navigationCommentStyle.marker "<-- you are here" ``` This will render navigation comments like: ``` - #123 - #124 <-- you are here - #125 ``` ### spice.submit.navigationComment.downstack [v0.20.0](https://abhinav.github.io/git-spice/changelog/#v0.20.0) git-spice keeps track of stack associations between branches even after they are merged. Using this information, it generates stack navigation comments that show the full stack history of a branch, including merged downstack CRs. This configuration option controls which downstack CRs are included in navigation comments posted to CRs. **Accepted values:** - `all` (default): include all downstack CRs (both open and merged) - `open`: only include CRs open at the time of submission ### spice.submit.publish [v0.5.0](https://abhinav.github.io/git-spice/changelog/#v0.5.0) Whether submission commands ( [gs branch submit](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-submit) and friends) should publish a CR to the forge. If this is set to false, submit commands will push branches, but not create CRs. In that case, the `--publish` flag will opt-in to creating CRs on a case-by-case basis. **Accepted values:** - `true` (default) - `false` ### spice.submit.updateOnly [v0.17.0](https://abhinav.github.io/git-spice/changelog/#v0.17.0) Whether multi-branch submission commands ( [gs stack submit](https://abhinav.github.io/git-spice/cli/reference/#gs-stack-submit) and friends) should assume --update-only mode by default. If true, submit operations will only update existing branches by default. Use the --no-update-only flag to override this behavior. This option has no effect on [gs branch submit](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-submit), **Accepted values:** - `true` - `false` (default) ### spice.repoSync.closedChanges [v0.17.0](https://abhinav.github.io/git-spice/changelog/#v0.17.0) How to handle closed Change Requests that have not been merged when running [gs repo sync](https://abhinav.github.io/git-spice/cli/reference/#gs-repo-sync). **Accepted values:** - `ask` (default): prompt the user whether to delete the branch - `ignore`: ignore closed CRs without prompting and leave the branch intact When set to `ignore`, [gs repo sync](https://abhinav.github.io/git-spice/cli/reference/#gs-repo-sync) will skip closed CRs entirely and log an informational message about the closed CR being ignored. The branch will remain on the system. ### spice.submit.web [v0.8.0](https://abhinav.github.io/git-spice/changelog/#v0.8.0) Whether submission commands ( [gs branch submit](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-submit) and friends) should open a web browser with submitted CRs. [v0.16.0](https://abhinav.github.io/git-spice/changelog/#v0.16.0) If set to `created`, git-spice will open the web browser only for newly created CRs, and not for existing ones that were updated. **Accepted values:** - `true` - `false` (default) - `created` ([v0.16.0](https://abhinav.github.io/git-spice/changelog/#v0.16.0)) # Experiments git-spice includes experimental features that can be enabled on an opt-in basis. Before you use experiments Be aware that experimental features: - may be incomplete or buggy - may change or be removed in future releases - may not be well-documented - may destroy your work ``` # Enable an experiment $ git config spice.experiment. true # Disable an experiment $ git config spice.experiment. false ``` Experiments are enabled with `git config`, by setting `spice.experiment.` to `true` or `false`. If you use an experimental feature, feel free to report issues and provide feedback about them. ## Available experiments ### commitFixup **Added**: [v0.18.0](https://abhinav.github.io/git-spice/changelog/#v0.18.0) Enables the [gs commit fixup](https://abhinav.github.io/git-spice/cli/reference/#gs-commit-fixup) command. This command acts like `gs commit amend`, but is able to amend any commit in the current branch, or downstack from it -- except those that are already on main. ### commitPick **Added**: [v0.19.0](https://abhinav.github.io/git-spice/changelog/#v0.19.0) Enables the [gs commit pick](https://abhinav.github.io/git-spice/cli/reference/#gs-commit-pick) command. This command is a stack-aware variant of `git cherry-pick`. It will automatically restack upstack branches after cherry-picking a commit. # CLI shorthands git-spice comes built-in with short versions of most commands to make them easier to remember and type. To determine the shorthand for a command, run the command with the `--help` flag. ``` $ gs branch create --help Usage: gs branch (b) create (c) [] [flags] # ... ``` The shorthand for a command is the bits in parentheses joined together. For example, the shorthand for [gs branch create](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-create) above is `gs bc`. As another example, the shorthand for [gs branch checkout](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-checkout) is `gs bco`. ``` $ gs branch checkout --help Usage: gs branch (b) checkout (co) [] [flags] # ... ``` Some commands have multiple aliases, but there's always only one shorthand. For example, [gs branch delete](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-delete) has two aliases: `d` and `rm`. ``` $ gs branch delete --help Usage: gs branch (b) delete (d,rm) [ ...] [flags] # ... ``` So valid ways to invoke the command are: ``` $ gs bd $ gs b d $ gs b rm $ gs branch rm $ gs branch delete ``` When to use built-in shorthands? We encourage adopting the built-in shorthands after you are comfortable with the corresponding full command names. The shorthands are designed to be easy to remember and type, but they may not be obvious if you don't know the full command name. The shorthands act as a mnemonic aid: invoke the full command name in your head while typing the shorthand. ## Built-in shorthands Below is a complete list of shorthands built into git-spice. | **Shorthand** | **Long form** | | ------------- | --------------------------------------------------------------------------------------------- | | gs bc | [gs branch create](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-create) | | gs bco | [gs branch checkout](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-checkout) | | gs bd | [gs branch delete](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-delete) | | gs be | [gs branch edit](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-edit) | | gs bfo | [gs branch fold](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-fold) | | gs bon | [gs branch onto](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-onto) | | gs br | [gs branch restack](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-restack) | | gs brn | [gs branch rename](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-rename) | | gs bs | [gs branch submit](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-submit) | | gs bsp | [gs branch split](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-split) | | gs bsq | [gs branch squash](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-squash) | | gs btr | [gs branch track](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-track) | | gs buntr | [gs branch untrack](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-untrack) | | gs ca | [gs commit amend](https://abhinav.github.io/git-spice/cli/reference/#gs-commit-amend) | | gs cc | [gs commit create](https://abhinav.github.io/git-spice/cli/reference/#gs-commit-create) | | gs cf | [gs commit fixup](https://abhinav.github.io/git-spice/cli/reference/#gs-commit-fixup) | | gs cp | [gs commit pick](https://abhinav.github.io/git-spice/cli/reference/#gs-commit-pick) | | gs csp | [gs commit split](https://abhinav.github.io/git-spice/cli/reference/#gs-commit-split) | | gs dse | [gs downstack edit](https://abhinav.github.io/git-spice/cli/reference/#gs-downstack-edit) | | gs dss | [gs downstack submit](https://abhinav.github.io/git-spice/cli/reference/#gs-downstack-submit) | | gs dstr | [gs downstack track](https://abhinav.github.io/git-spice/cli/reference/#gs-downstack-track) | | gs ll | [gs log long](https://abhinav.github.io/git-spice/cli/reference/#gs-log-long) | | gs ls | [gs log short](https://abhinav.github.io/git-spice/cli/reference/#gs-log-short) | | gs rba | [gs rebase abort](https://abhinav.github.io/git-spice/cli/reference/#gs-rebase-abort) | | gs rbc | [gs rebase continue](https://abhinav.github.io/git-spice/cli/reference/#gs-rebase-continue) | | gs ri | [gs repo init](https://abhinav.github.io/git-spice/cli/reference/#gs-repo-init) | | gs rr | [gs repo restack](https://abhinav.github.io/git-spice/cli/reference/#gs-repo-restack) | | gs rs | [gs repo sync](https://abhinav.github.io/git-spice/cli/reference/#gs-repo-sync) | | gs sd | [gs stack delete](https://abhinav.github.io/git-spice/cli/reference/#gs-stack-delete) | | gs se | [gs stack edit](https://abhinav.github.io/git-spice/cli/reference/#gs-stack-edit) | | gs sr | [gs stack restack](https://abhinav.github.io/git-spice/cli/reference/#gs-stack-restack) | | gs ss | [gs stack submit](https://abhinav.github.io/git-spice/cli/reference/#gs-stack-submit) | | gs usd | [gs upstack delete](https://abhinav.github.io/git-spice/cli/reference/#gs-upstack-delete) | | gs uso | [gs upstack onto](https://abhinav.github.io/git-spice/cli/reference/#gs-upstack-onto) | | gs usr | [gs upstack restack](https://abhinav.github.io/git-spice/cli/reference/#gs-upstack-restack) | | gs uss | [gs upstack submit](https://abhinav.github.io/git-spice/cli/reference/#gs-upstack-submit) | ## Custom shorthands [v0.4.0](https://abhinav.github.io/git-spice/changelog/#v0.4.0) git-spice's [configuration system](https://abhinav.github.io/git-spice/cli/config/index.md) supports defining custom shorthands for git-spice commands by setting configuration keys under the `spice.shorthand` namespace. ``` spice.shorthand. = ... ``` Shorthands begin with the name of a git-spice command, followed by zero or more arguments to the command. For example: ``` # Define a shorthand for the current user $ git config --global spice.shorthand.ch "branch checkout" # Define a shorthand in just the current repository $ git config --local spice.shorthand.can "commit amend --no-edit" ``` ### Overriding built-in shorthands User-defined shorthands take precedence over built-in shorthands. You may use this to override a built-in shorthand with a custom one. For example: ``` # Replace the "branch restack" shorthand $ git config --global spice.shorthand.br "branch rename" ``` If the result of a user-defined shorthand refers to a built-in shorthand, both will be expanded. ``` $ git config --global spice.shorthand.bb bco # bb will expand to bco, which will expand to "branch checkout" ``` ### Shell command aliases [v0.16.0](https://abhinav.github.io/git-spice/changelog/#v0.16.0) In addition to git-spice command shorthands, you can define aliases that execute arbitrary shell commands by prefixing the command with `!`. ``` spice.shorthand. = ! ``` Shell command aliases run the specified command in a shell and pass any additional arguments to the command as `$1`, `$2`, etc. If you want to pass all arguments through to the command, add `"$@"` to the end of the command alias. You can use shell command aliases to create custom helpers on top of git-spice commands, and invoke them through the `gs` command. For example: ``` # Shell alias that accepts arguments. $ git config spice.shorthand.ls-commits \ '!git log --oneline -n "${1:-1}"' $ gs ls-commits 3 abc1234 Latest commit def5678 Second commit ghi9012 First commit # Shell alias that consumes all arguments. $ git config spice.shorthand.from-up \ '!git checkout -p $(gs up -n) -- "$@"' $ gs from-up -- file1.txt path/to/file2.txt ``` Argument handling Shell command aliases receive arguments as positional parameters (`$1`, `$2`, etc.). You can use shell parameter expansion like `"${1:-default}"` to provide default values when no arguments are given. # JSON output Some commands in git-spice support JSON output. Unlike the default human-readable output (which is written to standard error), JSON output is written to standard output so that it can be piped to other programs. ``` $ gs ls --json | jq .name ``` For commands that support it, enable JSON output with the `--json` flag. ## Commands that support JSON output This section lists the commands that support JSON output, and describes the structure of their JSON output. ### [gs log long](https://abhinav.github.io/git-spice/cli/reference/#gs-log-long), [gs log short](https://abhinav.github.io/git-spice/cli/reference/#gs-log-short) [v0.18.0](https://abhinav.github.io/git-spice/changelog/#v0.18.0) Writes a stream of JSON outputs to standard output, one per line, each representing a tracked branch. These objects take the following form: ``` { // Name of the branch. name: string, // Whether this branch is the current branch. // May be omitted if false. current?: boolean, // Worktree that this branch is checked out in (if any) // if it's not the current worktree. // // This is always unset if current is true. worktree?: string, // Branch directly below this one in the stack. // 'gs down' will check out this branch. // Omitted if this is the trunk branch. down?: { // Name of the downstack branch. name: string, // Whether the downstack branch is in-sync with this one. // If false, the branch needs to be restacked. // May be omitted if false. needsRestack?: boolean, }, // Zero or more branches directly above this one in the stack. // 'gs up' will check out one of these branches. // Omitted if there are no branches above this one. ups?: [ { name: string, // name of the upstack branch } ] // Commits that are part of this branch. // Does not include commits that are part of downstack branches. // Preset only if 'gs log long' is used. // May be omitted if there are no commits in this branch. commits?: [ { sha: string, // full hash of the Git commit subject: string, // first line of the commit message }, ], // Information about the Change Request for this branch. // This is present if the branch was submitted and published // (e.g. with 'gs branch submit'). change?: { // Human-readable identifier for the Change Request. // This is the PR number for GitHub (e.g. "#123"), // and the MR IID for GitLab (e.g. "!123"). id: string, // URL at which the Change Request can be viewed. url: string, // Status of the Change Request. // Present only if '--cr-status=true' (or 'spice.log.crStatus=true'). // May be omitted if the remote forge is unsupported, // authentication is missing, or the status could not be determined. status?: "open" | "closed" | "merged", }, // Push status of the branch. // This is present if the branch was submitted, even if not published // (e.g. with 'gs branch submit --no-publish'). push?: { ahead: int, // number of commits ahead of the remote branch behind: int,// number of commits behind the remote branch // Whether the branch needs to be pushed to the remote. // Always false if ahead and behind are both zero. // May be omitted if false. needsPush?: boolean, }, } ``` # Community # Community # Frequently Asked Questions ## What's with the logo? > *[Xe] who controls the spice controls the universe* ## What is stacking? Stacking refers to the practice of creating interdependent branches on top of each other. Each branch in the stack builds on top of the previous one. For example, you might have a branch `feature-a` that adds a new feature, and a branch `feature-b` that builds on top of `feature-a`. Stacking your changes lets you: - **Unblock yourself**: While one branch is under review, you can start working on the next one. - **Be kinder to your team**: By keeping changes small and focused, you make it easier for your team to review, test, and understand your work. A 100 line PR gets a more meaningful review than a 1000 line PR. git-spice helps you manage your stack of branches, keeping them up-to-date and in sync with each other. **Related**: - [The stacking workflow](https://www.stacking.dev/) ## Where is the authentication token stored? git-spice stores the GitHub authentication in a system-specific secure storage. See [Authentication > Safety](https://abhinav.github.io/git-spice/setup/auth/#safety) for details. ## Why doesn't git-spice create one CR per commit? With tooling like this, there are two options: each commit is an atomic unit of work, or each branch is. While the former might be more in line with Git's original philosophy, the latter is more practical for most teams with GitHub or GitLab-based workflows. With a PR per commit, when a PR gets review feedback, you must amend that commit with fixes and force-push. This is inconvenient for PR reviewers as there's no distinction between the original changes and those addressing feedback. However, with a PR per branch, you can keep the original changes separate from follow-up fixes, even if the branch is force-pushed. This makes it easier for PR reviewers to work through the changes. And with squash-merges, you can still get a clean history consisting of atomic, revertible commits on the trunk branch. ## How does git-spice interact with `rebase.updateRefs`? The [--update-refs](https://git-scm.com/docs/git-rebase/2.42.1#Documentation/git-rebase.txt---update-refs) flag and its accompanying [`rebase.updateRefs`](https://git-scm.com/docs/git-rebase/2.42.1#Documentation/git-rebase.txt-rebaseupdateRefs) configuration tell `git rebase` to automatically force-update intermediate branches associated with commits affected by the rebase. Some use it to help locally manage their stack of branches. git-spice does not conflict with `--update-refs`. If you prefer to use `--update-refs` for branch stacking, you can continue to do so, while still using git-spice to navigate the stack and submit PRs. If you run a git-spice restack operation, it will automatically detect that the branches are already properly stacked, and leave them as-is. ## Will git-spice add support for other Git hosting services? As of writing this, git-spice supports GitHub and GitLab. It is specifically designed to support other forges; most of the code is forge-agnostic, with forge-specific code is isolated to their own directories inside [internal/forge/](https://github.com/abhinav/git-spice/tree/05280813ee113f09ee23529235a585a2388218da/internal/forge). In fact, - git-spice's own integration tests run against a simulated forge that acts similarly to GitHub and GitLab; - [GitLab support was added by an external contributor](https://github.com/abhinav/git-spice/pull/477) without meaningful changes to the rest of the codebase Therefore we're confident that adding support for other forges is feasible. That said, we do not plan to implement support for additional forges ourselves. If you would like to see support for a specific forge, please [open an issue](https://github.com/abhinav/git-spice/issues) signaling your interest. If you have the time and inclination to contribute, mention that in the issue and we will be happy to provide guidance and work with you to get the contribution merged. # Recipes ## Customization ### Auto-track branches on checkout [v0.23.0](https://abhinav.github.io/git-spice/changelog/#v0.23.0) Git's post-checkout hook can be used to invoke git-spice automatically when a branch is checked out, and track the branch with git-spice if it is not already tracked. **Prerequisites:** - git-spice [v0.23.0](https://abhinav.github.io/git-spice/changelog/#v0.23.0) - Git hooks are enabled (this is the default, but certain setups may disable them) - The repository is already initialized with git-spice ( [gs repo init](https://abhinav.github.io/git-spice/cli/reference/#gs-repo-init)) **Steps:** 1. Copy this script under `.git/hooks/post-checkout` in your repository. .git/hooks/post-checkout ``` #!/usr/bin/env bash set -euo pipefail # post-checkout is invoked with: # $1 - ref of the previous HEAD # $2 - ref of the new HEAD # $3 - 1 for a branch checkout, 0 for a file checkout shift # old SHA shift # new SHA checkout_type=$1 # Ignore non-branch checkouts. if [[ "$checkout_type" -eq 0 ]]; then exit 0 fi # Don't do anything if this was invoked during a git-spice operation; # if git-spice runs checkout, let it do what it's doing without interference. if [[ -n "${GIT_SPICE:-}" ]]; then exit 0 fi # post-checkout hook does not receive the branch name, # so get it from the new HEAD. branch_name=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "") if [[ -z "$branch_name" ]]; then exit 0 # not a branch fi # ...and verify it's actually a local branch. if ! git show-ref --verify --quiet refs/heads/"$branch_name"; then exit 0 fi # Don't attempt to track trunk. trunk_name=$(gs trunk -n 2>/dev/null || echo "master") if [[ "$branch_name" == "$trunk_name" ]]; then exit 0 fi # Check if the branch is already tracked by git-spice # by poking at the internals. (gs ls --json will be slower here.) # # Warning: This may break if git-spice's internal storage format changes. if ! git rev-parse --verify --quiet refs/spice/data:branches/"$branch_name" >/dev/null; then echo >&2 "Branch not tracked with git-spice: '$branch_name'. Tracking it now..." # We use 'downstack track' so that if there are any untracked branches # downstack from this one, they get tracked too. gs downstack track "$branch_name" fi ``` 1. Make sure the script is executable. ``` chmod +x .git/hooks/post-checkout ``` 1. Test it out by checking out an untracked branch. ``` git checkout -b my-feature main ``` **How this works:** - The post-checkout hook is invoked by Git after a checkout operation, whether with `git checkout` or `git switch`. - The script checks `GIT_SPICE` (added in [v0.23.0](https://abhinav.github.io/git-spice/changelog/#v0.23.0)) to ensure it does not interfere with git-spice's own operations (e.g. [gs branch checkout](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-checkout)). - It checks whether the new branch is already tracked by git-spice by looking inside its internal storage ([Internals](https://abhinav.github.io/git-spice/guide/internals/index.md)). - If the branch is not tracked, it invokes [gs downstack track](https://abhinav.github.io/git-spice/cli/reference/#gs-downstack-track) to track the branch and any untracked branches downstack from it. ## Workflows ### Create branches without committing [v0.5.0](https://abhinav.github.io/git-spice/changelog/#v0.5.0) The default workflow for git-spice forces you to commit immediately to new branches: [gs branch create](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-create) will create a new branch, and commit staged changes to it immediately, or if there are no staged changes, it will create an empty commit. If you have a workflow where you prefer to create branches first, and then work on them, you can use the following to adjust the workflow: - Configure [gs branch create](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-create) to never commit by default by setting [spice.branchCreate.commit](https://abhinav.github.io/git-spice/cli/config/#spicebranchcreatecommit) to false. ``` git config --global spice.branchCreate.commit false ``` - Use [gs branch create](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-create) as usual to create branches. Changes will not be committed automatically anymore. ``` gs branch create my-branch ``` - If, for some branches, you do want to commit staged changes upon creation, use the `--commit` flag or `-m`/`--message` (which always implies `--commit`). ``` gs branch create my-branch --commit gs branch create my-branch -m "Commit message" ``` ### Working with unsupported remotes [v0.6.0](https://abhinav.github.io/git-spice/changelog/#v0.6.0) If you're using a Git hosting service that is not supported by git-spice (e.g. Bitbucket, SourceHut, etc.), you can use git-spice to manage your branches locally without any issues. However, when it comes to pushing branches to the remote, there are some options that can help your workflow. - Stop git-spice from trying to submit changes to the service by setting [spice.submit.publish](https://abhinav.github.io/git-spice/cli/config/#spicesubmitpublish) to false. ``` git config spice.submit.publish false ``` - [gs repo sync](https://abhinav.github.io/git-spice/cli/reference/#gs-repo-sync) will detect branches that were merged with merge commits or fast-forwards, and delete them locally. For branches that were merged by rebasing or squashing, you'll need to manually delete merged branches with [gs branch delete](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-delete). ## Tasks ### Import a Pull Request from GitHub git-spice can recognize and manage GitHub Pull Requests that were not created using git-spice. **Steps:** 1. Check the PR out locally. For example, if you're using the GitHub CLI: ``` gh pr checkout 123 ``` 1. Track the branch with git-spice. ``` gs branch track ``` 1. Attempt to re-submit the branch. git-spice will automatically detect the existing open PR for it, and associate the branch with that PR. ``` gs branch submit ``` ### Track an existing stack If you have an existing, manually managed stack of branches, you can import it into git-spice in one of two ways: [v0.19.0](https://abhinav.github.io/git-spice/changelog/#v0.19.0) The fastest way to track an existing stack is to use [gs downstack track](https://abhinav.github.io/git-spice/cli/reference/#gs-downstack-track) from the topmost branch. **Steps:** 1. Check out the topmost branch. ``` git checkout feat3 ``` 1. Verify that the branches in the stack are reachable from each other in the correct order. You can use `git log --graph` or a visual tool for this. ``` git log --oneline --graph ``` 1. Track the entire stack at once. ``` gs downstack track ``` This will look for branches down the commit history and prompt you to track branches as it encounters them. 1. You may now use git-spice to manage the stack as usual. You can also track each branch individually with repeated use of [gs branch track](https://abhinav.github.io/git-spice/cli/reference/#gs-branch-track). **Steps:** 1. Check out the base branch and initialize git-spice. ``` git checkout main gs repo init ``` 1. Check out the first branch in the stack and track it. ``` git checkout feat1 gs branch track ``` 1. Repeat the previous step for each branch in the stack. ``` git checkout feat2 gs branch track # ... repeat until done ... ``` 1. You may now use git-spice to manage the stack as usual. # Integrations ## Emacs Magit support [v0.18.0](https://abhinav.github.io/git-spice/changelog/#v0.18.0) [jesse-c/git-spice.el](https://github.com/jesse-c/git-spice.el) provides Magit integration for git-spice. A demonstration can be found on Jesse's blog: . # Large Language Models ## Development transparency The maintainers of git-spice often use Large Language Models (LLMs) for implementation and debugging assistance during development. All LLM-generated output goes through human review, and the project maintains a high bar for code quality and testing. If LLM usage in development is a concern for you, all versions up to and including [v0.15.0](https://abhinav.github.io/git-spice/changelog/#v0.15.0) are free of LLM-generated code. These versions are quite featureful. ## LLM-friendly documentation If you want to feed git-spice documentation to an LLM, this website provides the following files per [llmstxt.org](https://llmstxt.org/) guidelines: - [llms.txt](https://abhinav.github.io/git-spice/llms.txt) - [llms-full.txt](https://abhinav.github.io/git-spice/llms-full.txt)