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).

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:
- Create feature branches with
git loom branch - Commit to them from the integration branch with
git loom commit - Move commits between branches with
git loom fold - Remove branches cleanly with
git loom drop
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:
| Platform | Archive |
|---|---|
| Linux x86_64 | git-loom-x86_64-unknown-linux-gnu.tar.gz |
| Linux aarch64 | git-loom-aarch64-unknown-linux-gnu.tar.gz |
| macOS x86_64 | git-loom-x86_64-apple-darwin.tar.gz |
| macOS Apple Silicon | git-loom-aarch64-apple-darwin.tar.gz |
| Windows x86_64 | git-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.
ghCLI (optional) — needed for automatic GitHub PR creation withgit 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
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
| Option | Description |
|---|---|
-f, --files | Show 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:
-
Local changes — shown only if the working tree has modifications, new files, or deletions. Each file is listed with a 2-char
XYstatus matchinggit status --short. -
Feature branches — each branch is rendered as a side branch with its name in brackets, followed by its commits, closed with
├╯. -
Loose commits — commits not belonging to any feature branch, shown on the main integration line.
-
Upstream marker — the merge-base between HEAD and the upstream tracking branch.
Symbols
| Symbol | Meaning |
|---|---|
╭─ | 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
| Argument | Description |
|---|---|
[name] | Branch name (optional, defaults to integration) |
What It Does
- Creates a new local branch at the upstream tip
- Configures upstream tracking (e.g.
origin/main) - Switches HEAD to the new branch
All three happen in a single atomic operation.
Upstream Detection
The upstream is resolved automatically in priority order:
- Current branch’s upstream — if you’re on
maintrackingorigin/main, the integration branch will also trackorigin/main - Remote scan — scans all remotes for branches named
main,master, ordevelop - Interactive prompt — if multiple candidates are found, you’re asked to choose
- 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
| Argument | Description |
|---|---|
[name] | Branch name (optional; prompts interactively if omitted) |
Options
| Option | Description |
|---|---|
-t, --target <target> | Commit hash, short ID, or branch name (defaults to upstream merge-base) |
What It Does
- Name resolution — if no name is provided, an interactive prompt asks for one
- Validation — the name is trimmed, checked for emptiness, validated against git’s naming rules, and checked for duplicates
- Target resolution — the target is resolved to a commit via the shared resolution system, or defaults to the merge-base
- 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
| Option | Description |
|---|---|
-b, --branch <branch> | Target feature branch (name or short ID). Prompts if omitted. |
-m, --message <message> | Commit message. Opens editor if omitted. |
File Arguments
| Argument | Description |
|---|---|
| (none) | Uses already-staged files (index as-is) |
zz | Stages all unstaged changes (like git add -A) |
| short IDs / filenames | Stages only those specific files |
When zz appears alongside other file arguments, zz wins and stages everything.
What It Does
- Stage — applies the staging rules based on file arguments
- Branch resolution — determines the target feature branch
- Message resolution — gets the commit message (flag or editor)
- Commit — creates the commit
- Relocate — moves the commit to the target feature branch, updating all branch refs and integration topology automatically
Branch Resolution
- If
-bmatches a woven feature branch: uses it - If
-bmatches an unwoven branch: error - If
-bdoesn’t match any branch: creates a new branch at the merge-base and weaves it - If
-bis 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
| Argument | Description |
|---|---|
<target> | Commit hash, branch name, or short ID |
Options
| Option | Description |
|---|---|
-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:
- Branch names — exact match resolves to a branch (for renaming)
- Git references — full/partial hashes,
HEAD, etc. resolve to commits - 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:
| Source | Target | Action |
|---|---|---|
| File(s) | Commit | Amend: stage files into the commit |
| Commit | Commit | Fixup: absorb source commit into target |
| Commit | Branch | Move: relocate commit to the branch |
| Commit | zz | Uncommit: remove commit, put changes in working directory |
| CommitFile | zz | Uncommit file: remove one file from a commit to working directory |
| CommitFile | Commit | Move 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 references —
HEAD,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
| Argument | Description |
|---|---|
<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
- Branch names — exact match resolves to a branch (drops the branch)
- Git references — full/partial hashes resolve to commits
- 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
- Fetch — fetches all upstream changes, including tags (force-updated) and pruning deleted remote branches
- Rebase — replays local commits on top of the updated upstream
- 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
| Argument | Description |
|---|---|
[branch] | Branch name or short ID (optional; interactive picker if omitted) |
Remote Type Detection
Detection priority (first match wins):
- Explicit config —
git config loom.remote-typeset togithuborgerrit - URL heuristics — remote URL contains
github.com→ GitHub - Hook inspection —
.git/hooks/commit-msgcontains “gerrit” → Gerrit - 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
ghCLI (optional, for GitHub PR creation)
Configuration
Git Config Settings
| Setting | Values | Default | Description |
|---|---|---|---|
loom.remote-type | github, gerrit | Auto-detected | Override 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-msgcontains “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
| Variable | Description |
|---|---|
NO_COLOR | Disable colored output when set (follows the NO_COLOR standard) |
TERM | Colors are automatically disabled when TERM=dumb |
CLI Flags
| Flag | Description |
|---|---|
--no-color | Disable colored output |