Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

git-loom is a Git CLI tool that supercharges your workflow by weaving together multiple feature branches into a single integration branch. Inspired by tools like jujutsu and Git Butler, git-loom helps you work on multiple features simultaneously while keeping your branches organized and independent.

Think of it as a loom that weaves multiple threads (feature branches) into a single fabric (integration branch).

git-loom screencast

Core Concepts

Integration Branch

An integration branch merges multiple feature branches together, allowing you to:

  • Work on several features at once in a single branch
  • Test how features interact with each other
  • Keep feature branches independent and manageable
  • See a clear relationship between your integration and feature branches

You create an integration branch with git loom init. It tracks a remote upstream (e.g. origin/main) and serves as the hub for all your feature work.

Feature Branches

Feature branches are independent branches combined into the integration branch. You can manage them — reorder, amend, split — without leaving the integration context:

Weaving

When a feature branch is created inside the integration branch, git-loom automatically weaves it into the topology — restructuring the linear history into a merge-based layout where each feature branch appears as a side branch joined by a merge commit. This is what makes git loom status able to display the clear branch-aware graph.

Short IDs

git-loom assigns compact, human-friendly identifiers to branches, commits, and files shown in git loom status. You can use these short IDs with any command instead of typing full hashes or branch names. What you see in the status output is what you type.

Quick Start

# Start on your main branch
git checkout main

# Create an integration branch
git loom init

# Create feature branches
git loom branch feature-auth
git loom branch feature-ui

# Make changes and commit to a feature branch
git loom commit -b feature-auth -m "add login form" zz

# See the branch-aware status
git loom status

# Push a feature branch for review
git loom push feature-auth

Installation

Cargo (all platforms)

If you have Rust installed, the easiest way to install git-loom is via crates.io:

cargo install git-loom

Scoop (Windows)

Install git-loom with Scoop:

scoop bucket add narnaud https://github.com/narnaud/scoop-bucket
scoop install git-loom

Pre-built binaries

Download the latest archive for your platform from the Releases page:

PlatformArchive
Linux x86_64git-loom-x86_64-unknown-linux-gnu.tar.gz
Linux aarch64git-loom-aarch64-unknown-linux-gnu.tar.gz
macOS x86_64git-loom-x86_64-apple-darwin.tar.gz
macOS Apple Silicongit-loom-aarch64-apple-darwin.tar.gz
Windows x86_64git-loom-x86_64-pc-windows-msvc.zip

Extract the binary and place it somewhere on your PATH.

From Source

Requires Rust 1.90 or later.

git clone https://github.com/narnaud/git-loom.git
cd git-loom
cargo install --path .

Requirements

  • Git 2.38 or later — git-loom checks the Git version at startup and will report an error if the version is too old.
  • gh CLI (optional) — needed for automatic GitHub PR creation with git loom push. Install from cli.github.com.

Shell Setup

git-loom provides shell completions for tab-completion of commands and options.

PowerShell

Add the following to your PowerShell profile ($PROFILE):

Invoke-Expression (&git-loom completions powershell | Out-String)

To find your profile path, run echo $PROFILE in PowerShell.

Clink adds completion support to cmd.exe. Create a file at %LocalAppData%\clink\git-loom.lua with:

load(io.popen('git-loom completions clink'):read("*a"))()

Commands Overview

Usage: git-loom [OPTIONS] [COMMAND]

Commands:
  status       Show the branch-aware status (default)
  init         Initialize a new integration branch tracking a remote
  branch       Create a new feature branch
  commit       Create a commit on a feature branch without leaving integration
  reword       Reword a commit message or rename a branch
  fold         Fold source(s) into a target (amend files, fixup commits, move commits)
  drop         Drop a commit or a branch from history
  update       Pull-rebase the integration branch and update submodules
  push         Push a feature branch to remote
  completions  Generate shell completions (powershell, clink)

Options:
      --no-color  Disable colored output
  -h, --help      Print help

Running git-loom with no command is equivalent to git loom status.

All commands that accept a target (commit, branch, or file) support short IDs — the compact identifiers shown in the status output. You can also use full git hashes, branch names, or partial hashes.

status

Show the branch-aware commit graph. This is the default command when running git-loom with no arguments.

Usage

git-loom [status] [-f]

Options

OptionDescription
-f, --filesShow files changed in each commit

Output

The status displays a branch-aware commit graph using UTF-8 box-drawing characters, showing commits grouped by feature branch:

╭─ [local changes]
│    M file.txt
│   A  new_file.rs
│
│╭─ [feature-b]
│●   d0472f9 Fix bug in feature B
│●   7a067a9 Start feature B
├╯
│
│╭─ [feature-a]
│●   2ee61e1 Add feature A
├╯
│
● ff1b247 (upstream) [origin/main] Initial commit

Sections

The graph is rendered top-to-bottom with these sections:

  1. Local changes — shown only if the working tree has modifications, new files, or deletions. Each file is listed with a 2-char XY status matching git status --short.

  2. Feature branches — each branch is rendered as a side branch with its name in brackets, followed by its commits, closed with ├╯.

  3. Loose commits — commits not belonging to any feature branch, shown on the main integration line.

  4. Upstream marker — the merge-base between HEAD and the upstream tracking branch.

Symbols

SymbolMeaning
╭─Start of a section
├─Start of a subsequent branch in a stack
Integration line continuation
││Continuation between stacked branches
A commit
├╯End of a side branch
Upstream has new commits

Short IDs

Each branch, commit, and file in the output is assigned a short ID — a compact identifier you can use with other git-loom commands. What you see in the status is what you type.

Branch Topologies

Independent branches

Each feature branch forks from the integration line independently:

│╭─ [feature-b]
│●   d0472f9 Fix bug in feature B
├╯
│
│╭─ [feature-a]
│●   2ee61e1 Add feature A
├╯

Stacked branches

Feature-b is stacked on top of feature-a:

│╭─ [feature-b]
│●   4e046ab Second commit on feature-b
│●   0b85ca7 First commit on feature-b
││
│├─ [feature-a]
│●   caa87a9 Second commit on feature-a
│●   18faee8 First commit on feature-a
├╯

Co-located branches

Multiple branches pointing to the same commit:

│╭─ [feature-a-v2]
│├─ [feature-a]
│●   2ee61e1 Add feature A
├╯

Upstream ahead

When upstream has new commits beyond the common base:

●   abc1234 Fix typo
│
│●  [origin/main] ⏫ 3 new commits
├╯ 204e309 (common base) 2025-07-06 Merge pull request #10

Prerequisites

  • Must be on a local branch (not detached HEAD)
  • Branch must have an upstream tracking branch configured

init

Initialize a new integration branch tracking a remote upstream. This is the entry point for starting a git-loom workflow.

Usage

git-loom init [name]

Arguments

ArgumentDescription
[name]Branch name (optional, defaults to integration)

What It Does

  1. Creates a new local branch at the upstream tip
  2. Configures upstream tracking (e.g. origin/main)
  3. Switches HEAD to the new branch

All three happen in a single atomic operation.

Upstream Detection

The upstream is resolved automatically in priority order:

  1. Current branch’s upstream — if you’re on main tracking origin/main, the integration branch will also track origin/main
  2. Remote scan — scans all remotes for branches named main, master, or develop
  3. Interactive prompt — if multiple candidates are found, you’re asked to choose
  4. Error — if no remote tracking branches are found

Examples

Default

git-loom init
# Initialized integration branch 'integration' tracking origin/main

Custom name

git-loom init my-integration
# Initialized integration branch 'my-integration' tracking origin/main

Error: branch already exists

git-loom init
# error: Branch 'integration' already exists

Error: no remotes

git-loom init
# error: No remote tracking branches found.
# Set up a remote with: git remote add origin <url>

Prerequisites

  • Must be in a git repository with a working tree
  • At least one remote with a fetchable branch must be configured

branch

Create a new feature branch at a specified commit.

Usage

git-loom branch [name] [-t <target>]

Arguments

ArgumentDescription
[name]Branch name (optional; prompts interactively if omitted)

Options

OptionDescription
-t, --target <target>Commit hash, short ID, or branch name (defaults to upstream merge-base)

What It Does

  1. Name resolution — if no name is provided, an interactive prompt asks for one
  2. Validation — the name is trimmed, checked for emptiness, validated against git’s naming rules, and checked for duplicates
  3. Target resolution — the target is resolved to a commit via the shared resolution system, or defaults to the merge-base
  4. Creation — the branch is created at the resolved commit

Automatic Weaving

When a branch is created at a commit between the merge-base and HEAD, git-loom automatically weaves it into the integration branch — restructuring the linear history into a merge-based topology.

Before (linear):

origin/main → A1 → A2 → A3 → HEAD

After git-loom branch feature-a -t A2:

origin/main → A1 → A2 (feature-a)
                         ↘
              A3' -----→ merge (HEAD)

Weaving does not trigger when branching at HEAD or at the merge-base.

If the working tree has uncommitted changes, they are automatically stashed and restored after the operation.

Target Resolution

The -t flag accepts:

  • Branch names — resolves to the branch’s tip commit
  • Git hashes — full or partial commit hashes
  • Short IDs — the compact IDs shown in git loom status
  • Default — the merge-base between HEAD and upstream

Examples

Interactive

git-loom branch
# ? Branch name ›
# User types: feature-authentication
# Created branch 'feature-authentication' at abc1234

At merge-base (default)

git-loom branch feature-auth
# Created branch 'feature-auth' at abc1234 (merge-base)

At a specific commit by short ID

git-loom branch feature-auth -t ab
# Created branch 'feature-auth' at 72f9d3a

At another branch’s tip

git-loom branch feature-b -t feature-a
# Created branch 'feature-b' at feature-a's tip commit

Prerequisites

  • Must be in a git repository with a working tree
  • For the default target: must have upstream tracking configured

commit

Create a commit on a feature branch without leaving the integration branch.

Usage

git-loom commit [-b <branch>] [-m <message>] [files...]

Options

OptionDescription
-b, --branch <branch>Target feature branch (name or short ID). Prompts if omitted.
-m, --message <message>Commit message. Opens editor if omitted.

File Arguments

ArgumentDescription
(none)Uses already-staged files (index as-is)
zzStages all unstaged changes (like git add -A)
short IDs / filenamesStages only those specific files

When zz appears alongside other file arguments, zz wins and stages everything.

What It Does

  1. Stage — applies the staging rules based on file arguments
  2. Branch resolution — determines the target feature branch
  3. Message resolution — gets the commit message (flag or editor)
  4. Commit — creates the commit
  5. Relocate — moves the commit to the target feature branch, updating all branch refs and integration topology automatically

Branch Resolution

  • If -b matches a woven feature branch: uses it
  • If -b matches an unwoven branch: error
  • If -b doesn’t match any branch: creates a new branch at the merge-base and weaves it
  • If -b is omitted: interactive picker with all woven branches + option to create a new one

New Branch Creation

When the target branch doesn’t exist, git-loom validates the name, creates the branch at the merge-base, and weaves it into the integration topology — all automatically.

Examples

Interactive

git-loom commit
# ? Select target branch
# > feature-auth
#   feature-ui
# (opens editor for commit message)

Fully specified

git-loom commit -b feature-auth -m "add password validation" zz
# Stages all changes, commits to feature-auth

Specific files by short ID

git-loom commit -b feature-auth ar -m "fix auth check"
# Stages only src/auth.rs (short ID: ar), commits to feature-auth

To a new branch

git-loom commit -b feature-logging -m "add request logging" zz
# Creates feature-logging, weaves it, stages all, commits

Prerequisites

  • Must be on an integration branch (has upstream tracking and woven feature branches)
  • Must have something to commit (staged or stageable changes)

reword

Reword a commit message or rename a branch.

Usage

git-loom reword <target> [-m <message>]

Arguments

ArgumentDescription
<target>Commit hash, branch name, or short ID

Options

OptionDescription
-m, --message <message>New commit message or branch name. Opens editor/prompt if omitted.

What It Does

When Target is a Commit

Changes the commit message using git’s native interactive rebase. All descendant commits are replayed to update their hashes.

  • Works on any commit in history, including the root commit
  • With -m: applies the new message non-interactively
  • Without -m: opens the git editor with the current message

What changes: target commit gets a new message and hash; all descendant commits get new hashes.

What stays the same: commit content (files, diffs), topology, and branches outside the ancestry chain.

When Target is a Branch

Renames the branch using git branch -m.

  • With -m: renames non-interactively
  • Without -m: interactive prompt showing current name as placeholder

Target Resolution

The target is resolved in this order:

  1. Branch names — exact match resolves to a branch (for renaming)
  2. Git references — full/partial hashes, HEAD, etc. resolve to commits
  3. Short IDs — branch short IDs resolve to branches, commit short IDs to commits

To reword the commit at a branch tip, use its commit hash or commit short ID (not the branch name, which would trigger a rename).

Examples

Reword a commit with editor

git-loom reword ab
# Opens editor with current message

Reword a commit directly

git-loom reword ab -m "Fix authentication bug in login flow"

Rename a branch interactively

git-loom reword feature-a
# ? New branch name › feature-a
# User types: feature-authentication

Rename a branch directly

git-loom reword fa -m feature-authentication

Prerequisites

  • Any git repository for commit rewording
  • For short IDs: must be on a branch with upstream tracking configured

fold

Fold source(s) into a target — a polymorphic command that amends files into commits, fixups commits together, moves commits between branches, or uncommits changes.

Usage

git-loom fold <source>... <target>

The last argument is always the target. All preceding arguments are sources. At least two arguments are required.

Type Dispatch

The action depends on the types of the arguments, detected automatically:

SourceTargetAction
File(s)CommitAmend: stage files into the commit
CommitCommitFixup: absorb source commit into target
CommitBranchMove: relocate commit to the branch
CommitzzUncommit: remove commit, put changes in working directory
CommitFilezzUncommit file: remove one file from a commit to working directory
CommitFileCommitMove file: move one file’s changes between commits

CommitFile sources use the commit_sid:index format shown by git loom status -f (e.g. fa:0 for the first file in commit fa).

Actions

Amend files into a commit

git-loom fold src/auth.rs ab
# Stages src/auth.rs and amends it into commit ab

Multiple files can be folded at once:

git-loom fold src/main.rs src/lib.rs HEAD
# Amends both files into the HEAD commit

Fixup a commit into another

Absorbs the source commit’s changes into the target. The source disappears from history; the target keeps its message.

git-loom fold c2 c1
# c2's changes are absorbed into c1, c2 disappears

The source commit must be newer than the target.

Move a commit to another branch

Removes the commit from its current branch and appends it to the target branch’s tip.

git-loom fold d0 feature-b
# Commit d0 moves to feature-b, removed from its original branch

Uncommit to the working directory

Removes a commit from history and places its changes as unstaged modifications.

git-loom fold ab zz
# Removes commit ab, its changes appear as unstaged modifications

Uncommit a single file

Removes one file’s changes from a commit, preserving the rest of the commit.

git-loom fold ab:1 zz
# Removes the second file from commit ab to the working directory

Move a file between commits

Moves one file’s changes from one commit to another.

git-loom fold c2:1 c1
# Moves the second file from c2 to c1

Arguments

Arguments can be:

  • File paths — files with changes in the working tree
  • Commit hashes — full or partial git hashes
  • Branch names — local branch names
  • Short IDs — compact IDs from git loom status
  • Git referencesHEAD, HEAD~2, etc.
  • zz — reserved token for the unstaged working directory

Prerequisites

  • Must be in a git repository with a working tree
  • For short ID arguments: must have upstream tracking configured
  • All operations are atomic and automatically preserve uncommitted changes

drop

Drop a commit or an entire branch from history.

Usage

git-loom drop <target>

Arguments

ArgumentDescription
<target>Commit hash, branch name, or short ID

What It Does

When Target is a Commit

Removes the commit from history. All descendant commits are replayed to maintain a consistent history.

If the commit is the only commit on a branch, the entire branch is dropped automatically (commits removed, merge topology unwoven, branch ref deleted).

When Target is a Branch

Removes the entire branch in a single operation:

  • All commits owned by the branch are removed
  • The merge topology is unwoven (if the branch was woven)
  • The branch ref is deleted

Co-located branches (sharing the same tip commit with another branch): only the branch ref is deleted. Commits are preserved for the surviving sibling branch, and the merge topology is reassigned.

Target Resolution

  1. Branch names — exact match resolves to a branch (drops the branch)
  2. Git references — full/partial hashes resolve to commits
  3. Short IDs — branch short IDs resolve to branches, commit short IDs to commits

File targets are rejected.

Examples

Drop a commit by short ID

git-loom drop ab
# Removes the commit from history

Drop a commit by hash

git-loom drop abc123d
# Removes the commit from history

Drop a branch

git-loom drop feature-a
# Removes all commits, unweaves merge topology, deletes branch ref

Drop a branch by short ID

git-loom drop fa
# Same as above, using the short ID

Drop a co-located branch

git-loom drop feature-a
# Removes feature-a ref, reassigns section to sibling branch
# Commits preserved for the surviving branch

Prerequisites

  • Must be in a git repository with a working tree
  • For branch drops: the branch must be in the integration range
  • All operations are atomic and automatically preserve uncommitted changes

update

Pull-rebase the integration branch and update submodules.

Usage

git-loom update

No arguments.

What It Does

  1. Fetch — fetches all upstream changes, including tags (force-updated) and pruning deleted remote branches
  2. Rebase — replays local commits on top of the updated upstream
  3. Submodule update (if applicable) — initializes and updates submodules recursively

Uncommitted working tree changes are automatically preserved during the rebase.

Examples

Standard update

git-loom update
# > Fetching latest changes...
# > Fetched latest changes
# > Rebasing onto upstream...
# > Rebased onto upstream

With submodules

git-loom update
# > Fetching latest changes...
# > Fetched latest changes
# > Rebasing onto upstream...
# > Rebased onto upstream
# > Updating submodules...
# > Updated submodules

Merge conflict

git-loom update
# > Fetching latest changes...
# > Fetched latest changes
# > Rebasing onto upstream...
# x Rebase failed
# error: CONFLICT (content): Merge conflict in file.txt

Resolve conflicts with standard git commands (git rebase --continue, etc.).

Error: not on an integration branch

git-loom update
# error: Branch has no upstream tracking branch.
# Run 'git-loom init' to set up an integration branch.

Prerequisites

  • Must be on a branch with upstream tracking configured
  • Network access to the remote

push

Push a feature branch to the remote. Automatically detects the remote type and uses the appropriate push strategy.

Usage

git-loom push [branch]

Arguments

ArgumentDescription
[branch]Branch name or short ID (optional; interactive picker if omitted)

Remote Type Detection

Detection priority (first match wins):

  1. Explicit configgit config loom.remote-type set to github or gerrit
  2. URL heuristics — remote URL contains github.com → GitHub
  3. Hook inspection.git/hooks/commit-msg contains “gerrit” → Gerrit
  4. Fallback — Plain Git

Push Strategies

Plain Git (default)

git push --force-with-lease --force-if-includes -u <remote> <branch>

Uses --force-with-lease because woven branches are frequently rebased. --force-if-includes adds extra safety.

GitHub

git push -u <remote> <branch>
gh pr create --web --head <branch>

Pushes the branch, then opens the GitHub PR creation page in the browser. If gh is not installed, the push succeeds with a message suggesting to install it.

Gerrit

git push -o topic=<branch> <remote> <branch>:refs/for/<target>

Uses the refs/for/ refspec and sets the topic to the branch name.

Examples

Push to a plain remote

git-loom push feature-a
# Pushed 'feature-a' to origin

Push to GitHub

git-loom push feature-a
# Pushed 'feature-a' to origin
# (browser opens to PR creation page)

Push to Gerrit

git-loom push feature-a
# Pushed 'feature-a' to origin (Gerrit: refs/for/main)

Interactive selection

git-loom push
# ? Select branch to push
# > feature-a
#   feature-b
# Pushed 'feature-a' to origin

Override remote type

git config loom.remote-type gerrit
git-loom push feature-a
# Pushed 'feature-a' to origin (Gerrit: refs/for/main)

Prerequisites

  • Must be on an integration branch with upstream tracking
  • The target branch must be woven into the integration branch
  • Network access to the remote
  • gh CLI (optional, for GitHub PR creation)

Configuration

Git Config Settings

SettingValuesDefaultDescription
loom.remote-typegithub, gerritAuto-detectedOverride the remote type for git loom push

loom.remote-type

By default, git loom push auto-detects the remote type:

  • GitHub — if the remote URL contains github.com
  • Gerrit — if .git/hooks/commit-msg contains “gerrit”
  • Plain Git — otherwise

You can override this with:

git config loom.remote-type github   # Force GitHub push (push + open PR)
git config loom.remote-type gerrit   # Force Gerrit push (refs/for/<branch>)

Environment Variables

VariableDescription
NO_COLORDisable colored output when set (follows the NO_COLOR standard)
TERMColors are automatically disabled when TERM=dumb

CLI Flags

FlagDescription
--no-colorDisable colored output