git-loom is a Git CLI tool that weaves your branches together 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:
- Commit to the feature branch from the integration branch with
git loom commit - Move commits between branches with
git loom fold - Remove branches cleanly with
git loom drop - Create feature branches with
git loom branch
Tip
git loom commitis the primary way to create branches — it will prompt you for the branch name, or let you create a new one on the fly. Usegit loom branchonly for advanced cases where you need to create an empty branch ahead of time or for branching out loose commits.
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 a commit on a feature branch (branch `feature-auth` is automatically created)
git loom commit -b feature-auth -m "add login form" zz
# Create a second commit on the branch
git loom commit -b feature-auth -m "improve 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"))()
Tutorial
This tutorial walks you through a typical git-loom workflow — from initializing an integration branch to working on multiple features simultaneously.
Getting Started
You have a project tracked by Git with a remote origin/main. Let’s set up git-loom.
git checkout main
git loom init
git-loom creates an integration branch that tracks origin/main. Run git loom status (or just git loom) to see the baseline:
╭─ zz [local changes]
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit
A clean slate — the zz local changes section is empty, and you can see the upstream marker. Time to start working.
Your First Commit
You add a login form to your project — create src/auth.rs and templates/login.html. Check the status:
╭─ zz [local changes]
│ ⁕ src/auth.rs
│ ⁕ templates/login.html
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit
Your new files show up as untracked in the local changes section. Instead of the usual git add / git commit dance, you use git-loom to commit directly to a feature branch:
git loom commit -b feature-auth -m "add login form" zz
This single command:
- Stages all your changes (
zzmeans “everything”) - Creates the
feature-authbranch (it didn’t exist yet) - Weaves it into the integration topology
- Creates the commit on that branch
Check the status:
│╭─ fa [feature-auth]
│● d0 add login form
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit
Your commit sits on feature-auth, shown as a side branch off the integration line. The short IDs fa (branch) and d0 (commit) are what you’ll type in subsequent commands.
You keep working on the same feature — add password validation and commit again:
git loom commit -b fa -m "add password validation" zz
Notice you can use the short ID fa instead of the full branch name. The status now shows:
│╭─ fa [feature-auth]
│● c2 add password validation
│● d0 add login form
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit
Working on Multiple Features
While feature-auth is in progress, you want to start on a dashboard. You create src/dashboard.rs and templates/dashboard.html:
╭─ zz [local changes]
│ ⁕ src/dashboard.rs
│ ⁕ templates/dashboard.html
│
│╭─ fa [feature-auth]
│● c2 add password validation
│● d0 add login form
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit
No need to switch branches — just commit to a new one:
git loom commit -b feature-dashboard -m "add dashboard layout" zz
│╭─ fd [feature-dashboard]
│● e1 add dashboard layout
├╯
│
│╭─ fa [feature-auth]
│● c2 add password validation
│● d0 add login form
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit
Two independent feature branches, both woven into the integration branch. You can build and test everything together while keeping the branches separate. This is the core of git-loom: work on multiple features simultaneously without branch switching.
Staying Up to Date
Your teammates have been pushing to origin/main. Time to pull their changes and rebase your work on top:
git loom update
This fetches upstream changes, rebases your integration branch (including all woven feature branches) on top, and updates submodules if any. Your working tree changes are automatically preserved.
│╭─ fd [feature-dashboard]
│● e1 add dashboard layout
├╯
│
│╭─ fa [feature-auth]
│● c2 add password validation
│● d0 add login form
├╯
│
● b2c3d4e (upstream) [origin/main] Teammate's latest commit
· a1b2c3d 2026-03-07 Latest upstream commit
The upstream marker moved forward — your branches are now rebased on top of the latest changes.
If any of your pushed feature branches have been merged and deleted on the remote, update will notice and offer to clean up the local branches:
# ! 1 local branch with a gone upstream:
# · feature-auth
# ? Remove them? [y/N]
Note
If a rebase conflict occurs,
updateaborts automatically and tells you the fullgit rebasecommand to re-run and resolve conflicts manually.
See also: update reference
Now that you know the basics, check out the recipe guides for common operations:
- Amending a Past Commit
- Fixing Up a Commit
- Splitting a Commit
- Moving a Commit Between Branches
- Moving Files Between Commits
- Uncommitting Changes
- Auto-absorbing Changes
- Pushing for Review
Amending a Past Commit
You realize the login form is missing a CSRF token. You fix src/auth.rs and check the status:
╭─ zz [local changes]
│ M src/auth.rs
│
│╭─ fa [feature-auth]
│● c2 add password validation
│● d0 add login form
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit
You want to amend this change into the original “add login form” commit (d0), not create a new commit.
$ git loom fold src/auth.rs d0
This stages src/auth.rs and amends it into commit d0. The branch topology stays the same — the commit just gains the new changes.
If you’ve already staged the files you want to amend, you can use the single-argument form:
$ git add src/auth.rs
$ git loom fold d0
This folds only the staged changes — any unstaged modifications to the same files are preserved.
To amend all working tree changes into a commit at once:
$ git loom fold zz d0
Tip
Use
git loom status -f d0to see which files are in a commit before and after amending.
See also: fold reference
Fixing Up a Commit
You have two commits on feature-auth and realize that c2 (“add password validation”) should really be part of d0 (“add login form”) — they’re logically the same change.
│╭─ fa [feature-auth]
│● c2 add password validation
│● d0 add login form
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit
Fold the newer commit into the older one:
$ git loom fold c2 d0
Commit c2 disappears from history and its changes are absorbed into d0:
│╭─ fa [feature-auth]
│● d0 add login form
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit
The source commit must be newer than the target. The target keeps its message.
See also: fold reference
Splitting a Commit
A commit touches multiple files that should really be separate commits. Let’s look at the current state:
git loom status -f d0
│╭─ fa [feature-auth]
│● d0 add login form
│┊ d0:0 A src/auth.rs
│┊ d0:1 A src/validation.rs
│┊ d0:2 A templates/login.html
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit
You want src/validation.rs in its own commit. Split the commit:
$ git loom split d0 -m "add validation helpers"
# ? Select files for the first commit
# > [x] src/validation.rs
# [ ] src/auth.rs
# [ ] templates/login.html
Select the files for the first commit — the remaining files stay in the second commit, which keeps the original message. The result:
│╭─ fa [feature-auth]
│● d1 add login form
│● d0 add validation helpers
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit
The commit must have at least two files — otherwise there’s nothing to split. Both sides must get at least one file.
Tip
If you omit
-m, git-loom opens your editor for the first commit’s message.
See also: split reference
Moving a Commit Between Branches
You committed a logging helper to feature-auth by mistake — it belongs in feature-dashboard.
│╭─ fd [feature-dashboard]
│● e1 add dashboard layout
├╯
│
│╭─ fa [feature-auth]
│● a3 add logging helper
│● d0 add login form
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit
Move it with fold:
$ git loom fold a3 fd
Commit a3 is removed from feature-auth and appended to feature-dashboard:
│╭─ fd [feature-dashboard]
│● a3 add logging helper
│● e1 add dashboard layout
├╯
│
│╭─ fa [feature-auth]
│● d0 add login form
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit
You can also move a commit into a new branch in one step with --create:
$ git loom fold -c a3 feature-logging
See also: fold reference
Moving Files Between Commits
Sometimes a commit touches files that belong in different commits. Use the commit:index syntax shown by git loom status -f to move a single file.
First, check which files are in each commit:
$ git loom status -f
Note
-fwithout arguments shows files for all commits. You can pass specific short IDs (e.g.git loom status -f d0) to limit the output.
│╭─ fd [feature-dashboard]
│● e1 add dashboard layout
│┊ e1:0 A src/dashboard.rs
│┊ e1:1 A templates/dashboard.html
├╯
│
│╭─ fa [feature-auth]
│● d0 add login form
│┊ d0:0 M src/auth.rs
│┊ d0:1 A templates/login.html
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit
You realize templates/login.html (index d0:1) would be better off in the dashboard commit. Move it:
$ git loom fold d0:1 e1
The file’s changes are removed from d0 and applied to e1:
│╭─ fd [feature-dashboard]
│● e1 add dashboard layout
│┊ e1:0 A src/dashboard.rs
│┊ e1:1 A templates/dashboard.html
│┊ e1:2 A templates/login.html
├╯
│
│╭─ fa [feature-auth]
│● d0 add login form
│┊ d0:0 M src/auth.rs
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit
See also: fold reference
Uncommitting Changes
Sometimes you want to undo a commit or pull a file out of one — maybe to re-split changes differently. Here’s the starting point:
$ git loom status -f
│╭─ fa [feature-auth]
│● c2 add password validation
│┊ c2:0 M src/auth.rs
│● d0 add login form
│┊ d0:0 A src/auth.rs
│┊ d0:1 A templates/login.html
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit
Uncommitting a Commit
You decide c2 (“add password validation”) was premature — you want its changes back in the working tree. Fold it into zz (the working directory):
$ git loom fold c2 zz
The commit is removed from history and its changes appear as unstaged modifications:
╭─ zz [local changes]
│ M src/auth.rs
│
│╭─ fa [feature-auth]
│● d0 add login form
│┊ d0:0 A src/auth.rs
│┊ d0:1 A templates/login.html
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit
Uncommitting a File
Instead of removing the whole commit, you just want to extract templates/login.html (index d0:1) from d0:
$ git loom fold d0:1 zz
The file is removed from the commit and appears as an untracked file in the working directory, leaving the rest of d0 intact:
╭─ zz [local changes]
│ ⁕ templates/login.html
│
│╭─ fa [feature-auth]
│● c2 add password validation
│┊ c2:0 M src/auth.rs
│● d0 add login form
│┊ d0:0 A src/auth.rs
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit
See also: fold reference
Auto-absorbing Changes
You’ve been tweaking code across several files — fixing a typo in src/auth.rs, adjusting a layout in templates/dashboard.html. Each change was last touched by a different commit. Instead of manually folding each one, let git-loom figure it out:
git loom absorb
For each changed file, absorb splits the diff into hunks, blames each hunk’s original lines to find the originating commit, and folds each hunk into the right place — even when a single file has changes belonging to different commits.
Run a dry run first to see what would happen:
$ git loom absorb -n # or --dry-run
# src/auth.rs -> d0 "add login form"
# templates/dashboard.html -> e1 "add dashboard layout"
# src/shared.rs [hunk 1/2] -> d0 "add login form"
# src/shared.rs [hunk 2/2] -- skipped (pure addition)
# Dry run: would absorb 3 hunk(s) from 3 file(s) into 2 commit(s)
When a file has hunks going to different commits, each hunk is absorbed independently. Hunks that can’t be attributed are left in the working tree.
Note
Absorb works by blaming existing lines to find their originating commit. It handles modified and deleted lines well, but newly added lines (insertions that don’t replace existing code) cannot be traced to a commit and will be skipped. Use
foldfor those.
You can also restrict absorption to specific files:
git loom absorb src/auth.rs
See also: absorb reference
Selecting Hunks with -p
Sometimes you want to stage or commit only part of your changes — a few specific lines rather than entire files. The -p (patch) flag opens an interactive TUI that lets you pick individual hunks before the operation runs. It works with add, commit, and fold.
The TUI
╭─ Files ──────────╮╭─ Diff ─────────────────────────╮
│ M main.rs ││ [✓] Hunk 1/3 (staged) │
│ ▼ src/ ││ @@ -10,4 +10,6 @@ │
│ MM lib.rs ││ -old line │
│ A new.rs ││ +new line │
│ ?? README.md ││ │
╰──────────────────╯╰────────────────────────────────╯
Navigate: ↑/↓ or j/k | Switch Pane: tab | Toggle: space | Confirm: c or Enter | Quit: q or Esc
The left pane lists files with git status–style codes (M, MM, A, ??, D). The right pane shows diff hunks — check the ones you want, leave the rest unchecked, then confirm.
| Key | Action |
|---|---|
↑ / k, ↓ / j | Navigate up/down |
Tab / Shift+Tab | Switch between left and right pane |
Space | Toggle hunk (right pane) or all hunks in file/directory (left pane) |
c / Enter | Confirm selections |
q / Esc / Ctrl+C | Cancel without changes |
Staging hunks (add -p)
To stage a subset of changes before any commit:
$ git loom add -p
# Opens TUI showing all changed files
Filter to specific files:
$ git loom add -p src/auth.rs
$ git loom add -p a3 # using a short ID
Committing hunks (commit -p)
Commit only selected hunks to a feature branch, without staging anything first:
$ git loom commit -b feature-auth -p
# Opens TUI for all working tree changes
# Only selected hunks are committed to feature-auth
You can narrow the picker to specific files:
$ git loom commit -b feature-auth -p src/auth.rs -m "partial auth fix"
# TUI shows only src/auth.rs hunks
# Other staged files are saved aside and restored after the commit
Amending hunks into a past commit (fold -p)
Fold only selected working tree hunks into an existing commit:
$ git loom fold -p d0
# Opens TUI for all working tree changes
# Selected hunks are staged and folded into commit d0
Narrow to specific files by listing them before the target:
$ git loom fold -p src/auth.rs d0
# TUI shows only src/auth.rs hunks
# Selected hunks are folded into d0; the rest stay in the working tree
Common patterns
Split a file’s changes across two commits
You edited src/auth.rs and want different hunks in different branches.
$ git loom commit -b feature-auth -p src/auth.rs -m "tighten auth check"
# Pick the first hunk → committed to feature-auth
$ git loom commit -b feature-ui -p src/auth.rs -m "restyle auth form"
# Pick the remaining hunk → committed to feature-ui
Amend only part of a file into a past commit
$ git loom fold -p src/auth.rs d0
# Opens TUI filtered to src/auth.rs
# Pick only the hunks that belong in d0
# Unselected hunks stay in the working tree unchanged
Stage interactively, then commit
If you prefer to separate the two steps:
$ git loom add -p
# Select exactly what to stage
$ git loom commit -b feature-auth -m "fix auth check"
# Commits whatever is staged
Pushing for Review
Your feature-auth branch is ready. Push it to the remote:
$ git loom push fa
git-loom detects your remote type automatically and runs the appropriate commands:
- GitHub — pushes the branch, then checks if a PR exists. If a PR already exists, prints its URL (
PR updated: https://...). Otherwise creates a PR via theghCLI with a title and description auto-generated from the branch’s commits. - Azure DevOps — pushes the branch, then checks if a PR exists. If a PR already exists, prints its URL. Otherwise creates a PR via the
azCLI with a title and description auto-generated from the branch’s commits. - Gerrit — pushes to
refs/for/<target>(where<target>is your upstream branch, e.g.mainormaster) with the branch name as topic. Review URLs from the Gerrit remote are displayed after the push. - Plain Git — pushes with
--force-with-lease.
If gh or az are not installed, the push still succeeds — you just won’t get the automatic PR creation.
If you just want to push without creating a PR (e.g. to back up your work):
$ git loom push fa --no-pr
When you omit the branch argument, git-loom shows an interactive picker:
$ git loom push
# ? Select branch to push
# > feature-auth
# feature-dashboard
See also: push reference
Resolving Conflicts
When a loom operation rewrites history and two commits touch the same lines, git can’t merge them automatically. Instead of aborting, loom pauses the operation and lets you fix the conflict before continuing.
What a Paused Operation Looks Like
$ git loom commit -b feature-auth -m "add auth middleware" zz
✓ Created branch `feature-auth` at `a1b2c3d`
! Conflicts detected — resolve them with git, then run:
loom continue to complete the commit
loom abort to cancel and restore original state
The process exits with code 0. Your work is safe — loom saved the operation
state to .git/loom/state.json and left the rebase paused at the conflicting
commit.
Step 1: Find the Conflicts
$ git status
You are currently rebasing branch 'integration' on 'a1b2c3d'.
(fix conflicts and then run "git rebase --continue")
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: src/middleware.rs
The conflicting files are listed under Unmerged paths. You can also run
git diff to see the conflict markers inline.
Step 2: Resolve Each File
Open each conflicting file in your editor. Git inserts conflict markers to show both versions:
<<<<<<< HEAD
// existing middleware code
=======
// your new auth middleware
>>>>>>> feature-auth
Edit the file to keep what you want — either one side, the other, or a
combination of both — and remove the <<<<<<<, =======, and >>>>>>>
markers entirely.
Tip
Most editors have built-in conflict resolution UI. In VS Code, click Accept Current, Accept Incoming, or Accept Both above each conflict block. For a dedicated mergetool, run
git mergetool. See the git documentation on resolving conflicts for more detail.
Step 3: Mark Files as Resolved
Once a file is clean (no more conflict markers), stage it:
$ git add src/middleware.rs
For a file that should be deleted entirely as the resolution, use:
$ git rm src/middleware.rs
Repeat for every conflicting file. When git status shows no more unmerged
paths, you’re ready to continue.
Step 4: Continue or Abort
To finish the operation:
$ git loom continue
✓ Created commit `b2c3d4e` on branch `feature-auth`
Loom runs git rebase --continue internally, completes the interrupted
command’s post-rebase work (restoring staged patches, printing the success
message), and removes the saved state.
To cancel and go back to where you started:
$ git loom abort
✓ Aborted `loom commit` and restored original state
Abort rolls back all branch refs, removes any branches created during the
operation, and restores any staged changes that were saved aside. For commit,
the content you were committing comes back as unstaged working-tree changes so
nothing is lost.
Multiple Conflicts
If your branch has several commits that conflict, each loom continue may
pause again at the next one. Repeat the resolve → git add → loom continue
cycle until the operation completes:
$ git loom update
! Conflicts detected...
$ git add src/api.rs && git loom continue
! Conflicts remain — resolve them and run `loom continue` again
$ git add src/models.rs && git loom continue
✓ Updated branch `integration` with `origin/main` (abc1234 Latest commit)
If You Accidentally Run git rebase --continue
That’s fine. Loom detects that the rebase is no longer active and skips
straight to the post-rebase work when you run loom continue.
If the State File is Stale
If loom blocks you with a “paused operation” error but you know no operation is actually in progress (e.g., after a crash or force-reset), delete the state file to reset:
rm .git/loom/state.json
Warning
Only do this if you are certain no loom operation is paused. If a rebase is still in progress, run
loom abortinstead — that also aborts the rebase and restores your branch refs.
See Also
continue— reference forloom continueabort— reference forloom abort- Git documentation: Basic Merge Conflicts
Commands Overview
Usage: git-loom [OPTIONS] [COMMAND]
Workflow:
init Initialize a new integration branch
update, up Pull-rebase and update submodules
push, pr Push a branch to remote
Staging:
add Stage files using short IDs or paths [-p for interactive hunks]
Commits:
commit, ci Create a commit on a feature branch
fold Amend, fixup, or move commits [amend, am, fixup, mv, rub]
absorb Auto-distribute changes into originating commits
split Split a commit into two
swap Swap two commits
reword, rw Reword a commit message or rename a branch
drop, rm Drop a change, commit, or branch
Branches:
branch, br Manage feature branches (create, merge, unmerge)
switch, sw Switch to any branch for testing (without weaving)
Inspection:
status Show the branch-aware status (default command)
show, sh Show commit details (like git show)
diff, di Show a diff using short IDs (like git diff)
trace Show the latest command trace
Recovery:
continue, c Resume a paused operation after resolving conflicts
abort, a Cancel a paused operation and restore original state
Options:
--no-color Disable colored output
--theme <THEME> Color theme for graph output [default: auto] [possible values: auto, dark, light]
-h, --help Print help (see more with '--help')
-V, --version Print version
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.
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
update
Pull-rebase the integration branch onto the latest upstream and update submodules.
Alias: up
Usage
git loom update [-y]
Options
| Option | Description |
|---|---|
-y, --yes | Skip confirmation prompt when removing branches with a gone upstream |
What It Does
Fetch
Runs git fetch --tags --force --prune against the tracked remote. Force-updates moved tags and prunes deleted remote branches from local tracking refs.
Upstream Commit Filtering
Before rebasing, loom scans every feature-branch commit against the new upstream and drops any that are already present. Two strategies are applied:
- Direct merge — if the upstream is a descendant of the commit’s OID, the commit was merged directly.
- Cherry-pick — if the commit’s patch-ID matches a new upstream commit, it was cherry-picked.
If an entire branch section empties out after filtering, its section and merge entry are removed from the rebase todo. The branch ref is left intact for manual cleanup.
Rebase
Replays local commits onto the updated upstream using a topology-aware weave model — ensuring new upstream commits land on the base line, not inside feature branch sections. Uncommitted working tree changes are automatically stashed and restored.
If the current branch has no weave topology (a plain tracked branch), loom falls back to a standard git rebase --autostash --update-refs --rebase-merges.
Submodule Update
If .gitmodules exists, runs git submodule update --init --recursive.
Gone Upstream Cleanup
Lists any local branches whose upstream tracking ref was pruned in the fetch step, then prompts once to remove them. Pass -y to skip the prompt. Each branch is deleted individually; if a branch has unmerged local commits, it is skipped with a warning rather than aborting the cleanup.
Examples
Standard update
git loom update
# ✓ Fetched latest changes
# ✓ Rebased onto upstream
# ✓ Updated branch `integration` with `origin/main` (abc1234 Latest commit)
Cherry-picked commits auto-dropped
# feature-a had commits F1, F2, F3 — upstream cherry-picked F1 and F2
git loom update
# ✓ Fetched latest changes
# ✓ Rebased onto upstream
# ✓ Updated branch `integration` with `origin/main` (abc1234 Latest commit)
# F1 and F2 are silently dropped; F3 remains on feature-a
With submodules
git loom update
# ✓ Fetched latest changes
# ✓ Rebased onto upstream
# ✓ Updated submodules
# ✓ Updated branch `integration` with `origin/main` (abc1234 Latest commit)
Gone upstream branches
git loom update
# ✓ Fetched latest changes
# ✓ Rebased onto upstream
# ✓ Updated branch `integration` with `origin/main` (abc1234 Latest commit)
# ! 2 local branches with a gone upstream:
# · feature-x
# · feature-y
# ? Remove them? [y/N]
Skip gone-upstream prompt
git loom update -y
# ✓ Fetched latest changes
# ✓ Rebased onto upstream
# ✓ Updated branch `integration` with `origin/main` (abc1234 Latest commit)
# ✓ Removed branch `old-feature`
# ✓ Removed branch `closed-pr`
Gone branch with unmerged commits
git loom update
# ✓ Fetched latest changes
# ✓ Rebased onto upstream
# ✓ Updated branch `integration` with `origin/main` (abc1234 Latest commit)
# ! 1 local branch with a gone upstream:
# work-in-progress
# Remove it? [y/N] y
# ! Skipped branch `work-in-progress` — it has unmerged local commits.
# Use `git branch -D work-in-progress` to force-delete.
Conflicts
If the rebase encounters a conflict, loom saves state and pauses:
git loom update
# ✓ Fetched latest changes
# ! Conflicts detected — resolve them with git, then run:
# loom continue to complete the update
# loom abort to cancel and restore original state
After resolving:
git add <resolved-files> && git loom continue
# ✓ Updated branch `integration` with `origin/main` (abc1234 Latest commit)
Or cancel:
git loom abort
# ✓ Aborted `loom update` and restored original state
See continue and abort for details.
Prerequisites
- Must be in a git repository with a working tree
- Current branch must have upstream tracking configured (use
initfirst) - 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] [--no-pr]
Arguments
| Argument | Description |
|---|---|
[branch] | Branch name or short ID (optional; interactive picker if omitted) |
Flags
| Flag | Description |
|---|---|
--no-pr | Push without creating a PR or Gerrit review (see below) |
Remote Type Detection
Detection priority (first match wins):
- Explicit config —
git config loom.remote-typeset togithub,azure, orgerrit - URL heuristics — remote URL contains
github.com→ GitHub - URL heuristics — remote URL contains
dev.azure.com→ Azure DevOps - Hook inspection —
.git/hooks/commit-msgcontains “gerrit” → Gerrit - Fallback — Plain Git
Push Remote Selection
Detection priority (first match wins):
- Explicit config —
git config loom.push-remote <remote> - GitHub fork convention — if the integration remote is named
upstreamandoriginexists, push toorigin - Fallback — integration branch’s remote
For non-standard fork setups (e.g., integration branch tracks origin but you push to personal), set:
git config loom.push-remote personal
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
Pushes the branch with --force-with-lease, then checks whether a PR already exists for the branch:
- PR exists — prints the PR URL (
PR updated: https://github.com/owner/repo/pull/42) without opening the browser - No PR — creates the PR via
gh pr createwith an auto-generated title and description (see PR Title and Description below)
If gh is not installed, the push succeeds with a message suggesting to install it.
In a fork workflow (tracking upstream/main), pushes go to origin (your fork) and the PR targets the upstream repository automatically.
If the branch being pushed is the upstream target branch itself, PR creation is skipped.
Azure DevOps
Pushes the branch with --force-with-lease, then checks whether a PR already exists for the branch:
- PR exists — prints the PR URL (
PR updated: https://dev.azure.com/...) without opening the browser - No PR — creates the PR via
az repos pr createwith an auto-generated title and description (see PR Title and Description below)
--detect auto-detects the organization and project from the remote URL. If az 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. After pushing, any review URLs returned by Gerrit are extracted from the remote output and displayed below the success message.
PR Title and Description
When creating a new PR (GitHub or Azure DevOps), git-loom auto-generates the title and description from the branch’s commits:
- Single commit — the commit subject becomes the PR title and the commit body becomes the description.
- Multiple commits — you are prompted for a PR title. The description is built by concatenating all commit messages (oldest to newest), separated by
---dividers. - Empty branch — the branch name is used as the title with an empty description.
Pushing Without a PR or Review
Use --no-pr when you want to push a branch to the remote without triggering PR or review creation — for example, to back up a branch, share work-in-progress, or push to a staging ref.
| Remote type | --no-pr behavior |
|---|---|
| Plain | Same as normal (force-with-lease push) |
| GitHub | Skips gh pr create |
| Azure DevOps | Skips az repos pr create |
| Gerrit | Plain push to branch ref instead of refs/for/ (see below) |
Gerrit: wip/ prefix warning
In Gerrit, pushing directly to a branch ref (not refs/for/) creates a remote branch that requires a project admin to delete. To protect against accidental non-deletable branches, --no-pr on Gerrit prompts when the branch name doesn’t start with wip/:
? Branch `feature-a` is not prefixed with `wip/` — a Gerrit admin will be needed to delete the remote branch later
> Push as `feature-a` (admin required to delete it later)
Push as `wip/feature-a` instead
Cancel
- Push as-is — pushes to
remote/feature-a; an admin is needed to delete it later - Push as
wip/<branch>— pushes with refspecfeature-a:wip/feature-a; your local branch name is unchanged - Cancel — aborts the push
If the branch already starts with wip/, no prompt is shown.
Examples
Push to a plain remote
git loom push feature-a
# Pushed 'feature-a' to origin
Push to GitHub (new PR)
git loom push feature-a
# Pushed 'feature-a' to origin
# (browser opens to PR creation page)
Push to GitHub (PR already exists)
git loom push feature-a
# Pushed 'feature-a' to origin
# PR updated: https://github.com/owner/repo/pull/42
Push to Azure DevOps (new PR)
git loom push feature-a
# Pushed 'feature-a' to origin
# (browser opens to PR creation page)
Push to Azure DevOps (PR already exists)
git loom push feature-a
# Pushed 'feature-a' to origin
# PR updated: https://dev.azure.com/org/project/_git/repo/pullrequest/42
Push to Gerrit
git loom push feature-a
# Pushed 'feature-a' to origin (Gerrit: refs/for/main)
# › https://gerrit.example.com/c/project/+/12345
Interactive selection
git loom push
# ? Select branch to push
# > feature-a
# feature-b
# Pushed 'feature-a' to origin
Push without opening a PR (GitHub)
git loom push feature-a --no-pr
# Pushed 'feature-a' to origin
Push without a review, renaming to wip/ (Gerrit)
git loom push feature-a --no-pr
# ? Branch `feature-a` is not prefixed with `wip/`...
# > Push as `wip/feature-a` instead
# Pushed 'feature-a' to origin as 'wip/feature-a'
Override remote type
git config loom.remote-type gerrit
git loom push feature-a
# Pushed 'feature-a' to origin (Gerrit: refs/for/main)
# › https://gerrit.example.com/c/project/+/12345
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)azCLI (optional, for Azure DevOps PR creation)
add
Stage files into the git index using short IDs, paths, or zz for all — with optional interactive hunk selection.
Usage
git-loom add [-p] [<files...>]
Without -p, at least one file argument is required. With -p, files are optional (omitting them shows all changed files).
Arguments
| Argument | Description |
|---|---|
<files...> | Files to stage: short IDs from loom status, relative paths, or zz to stage everything |
Options
| Option | Description |
|---|---|
-p, --patch | Open the interactive hunk selector TUI |
What It Does
Plain Staging
Resolves each argument to a file path (via short ID or filename) and stages it. If any argument is zz, all changes are staged immediately regardless of other arguments.
Prints "Staged N file(s)" on success, or "Staged all changes" when zz is used.
If no arguments are provided and -p is not set, the command exits with an error.
Interactive Hunk Staging (-p)
Opens a two-pane TUI showing all staged and unstaged hunks across the affected files. Staged hunks start selected; unstaged hunks start deselected. The user can toggle individual hunks (or entire files/directories) in either direction, then confirm to apply all changes atomically.
On confirm, prints "Applied N change(s) across M file(s)" or "No changes to apply" if nothing was toggled.
File Resolution
Arguments (in both plain and -p modes) are resolved in this order:
zz— always stages everything (plain mode) or shows all files (-pmode)- Short IDs — file short IDs from
loom statusoutput (e.g.a3,0f) - Plain paths — relative file paths (e.g.
src/main.rs)
Examples
Stage a file by short ID
git-loom add a3
# Staged 1 file(s)
Stage multiple files
git-loom add a3 0f src/lib.rs
# Staged 3 file(s)
Stage everything
git-loom add zz
# Staged all changes
Interactive hunk selection for all files
git-loom add -p
# Opens TUI — confirm with c/Enter, cancel with q/Esc
Interactive hunk selection for a specific file
git-loom add -p src/main.rs
# Opens TUI filtered to src/main.rs hunks
Interactive hunk selection by short ID
git-loom add -p a3
# Opens TUI filtered to the file identified by short ID a3
Prerequisites
- Must be in a git repository with a working tree (not bare)
- At least one file must have changes (staged, unstaged, or untracked) for
-pmode
commit
Create a commit on a feature branch without leaving the integration branch.
Usage
git loom commit [-b <branch>] [-m <message>] [-p] [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. |
-p, --patch | Interactively select hunks to stage before committing. |
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
Patch Mode
With -p, an interactive TUI opens before staging, letting you pick individual hunks to include in the commit. Any file arguments narrow the picker to those files; omitting them (or using zz) shows all changes.
If specific files are given alongside -p, any other staged files are saved aside first so they don’t accidentally end up in the commit. They are restored automatically afterward.
Loose Commit
When -b is omitted and the integration branch name matches the upstream’s local counterpart (e.g. main tracking origin/main), the commit is created directly on the integration branch as a loose commit. No branch targeting or rebase is needed. This works regardless of whether local commits or woven branches already exist.
Branches with names that differ from their upstream (e.g. integration tracking origin/main) always require an explicit -b flag.
Branch Resolution
When the integration branch has diverged (woven branches exist):
- 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
Loose commit on a fresh integration branch
git loom commit -m "initial scaffold" zz
# No -b flag, branch matches remote → creates loose commit directly
Interactive hunk selection
git loom commit -b feature-auth -p -m "fix auth check"
# Opens hunk picker for all changes
# Only selected hunks are staged and committed to feature-auth
Hunk selection for specific files
git loom commit -b feature-auth -p ar -m "partial auth fix"
# Opens hunk picker filtered to src/auth.rs
# Other staged files are saved aside and restored after the commit
Conflicts
If the rebase that moves the commit to its target branch hits a conflict, the
operation is paused rather than aborted. The committed content is safe in
git history; loom saves recovery state to .git/loom/state.json and exits
with code 0.
git loom commit -b feature-auth -m "add auth" zz
# ✓ Created branch `feature-auth` at `a1b2c3d`
# ! Conflicts detected — resolve them with git, then run:
# loom continue to complete the commit
# loom abort to cancel and restore original state
Resolve conflicts, then:
git add <resolved-files>
git loom continue
# ✓ Created commit `b4c5d6e` on branch `feature-auth`
Or cancel and return to the original state (the commit content comes back as unstaged working-tree changes):
git loom abort
# ✓ Aborted `loom commit` and restored original state
See continue and abort for details.
Prerequisites
- Must be on an integration branch (has upstream tracking and woven feature branches)
- Must have something to commit (staged or stageable changes)
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 <target>
git loom fold <source>... <target>
git loom fold -p [<files>...] <target>
git loom fold -p <commit1> <commit2>
git loom fold -p <commit> zz
git loom fold --create <commit> <new-branch>
When only a target is given, currently staged files are folded into the target commit. When two or more arguments are provided, the last argument is the target and all preceding arguments are sources.
Options
| Option | Description |
|---|---|
-p, --patch | Interactively select hunks before folding. Three forms depending on argument types (see below). |
-c, --create | Create a new branch and move the source commit into it. |
Type Dispatch
The action depends on the types of the arguments, detected automatically:
| Source | Target | Action |
|---|---|---|
| (staged) | Commit | Amend staged: fold currently staged files into the commit |
| File(s) | Commit | Amend: stage files into the commit |
zz | Commit | Amend all: stage all changed 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 |
| Commit | New branch (-c) | Create: make a new branch and move the commit into it |
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
Fold staged files into a commit
When only a target is given, staged files are folded into the commit:
git add src/auth.rs
git loom fold ab
# Folds staged changes into commit ab
Only files in the git index are folded — unstaged changes to the same files are preserved. Errors with "Nothing to commit" if nothing is staged.
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
Use zz to fold all working tree changes at once (staged and unstaged):
git loom fold zz ab
# Stages all changed files and amends them into commit ab
If zz is mixed with individual file arguments, zz takes precedence and all changed files are folded.
Interactive hunk selection (-p)
With -p, an interactive TUI opens for hunk-level selection. There are three forms depending on the argument types.
Form 1 — pick working-tree hunks → fold into commit:
git loom fold -p ab
# Opens hunk picker for all working-tree changes
# Selected hunks are staged and folded into commit ab
Provide file arguments before the target to narrow the picker:
git loom fold -p src/auth.rs ab
# Opens hunk picker filtered to src/auth.rs
Form 2 — pick hunks from a commit → move into another commit:
git loom fold -p c2 c1
# Opens commit-diff picker for c2
# Selected hunks are removed from c2 and added to c1
The source (c2) must be newer than the target (c1). Binary and deleted files are not supported.
Form 3 — pick hunks from a commit → uncommit to working tree:
git loom fold -p ab zz
# Opens commit-diff picker for ab
# Selected hunks are removed from ab and appear as unstaged modifications
All -p forms error with "No hunks selected" if nothing is selected.
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
Create a new branch and move a commit into it
Use --create (-c) to create a new branch and move the commit in one step. Works whether the commit is a loose commit on the integration line or already on an existing branch.
git loom fold -c d0 new-feature
# Creates new-feature and moves commit d0 into it
If the branch already exists, a warning is printed and the commit is moved there anyway — same as a normal fold <commit> <branch>.
git loom fold -c d0 existing-branch
# ! Branch `existing-branch` already exists — moving commit to it
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
Conflicts
The following fold operations support conflict recovery (pause/resume):
- Amend files into a non-HEAD commit
- Fixup a commit into another
- Move a commit to a branch
- Uncommit a commit to the working directory (non-HEAD)
If a supported fold hits a conflict, the operation is paused:
git loom fold d0 feature-b
# ! Conflicts detected — resolve them with git, then run:
# loom continue to complete the fold
# loom abort to cancel and restore original state
git add <resolved-files> && git loom continue
# ✓ Moved `d0` to branch `feature-b` (now `e1f2a3b`)
The following fold operations do not support pause/resume and abort immediately on conflict:
- All
-p(patch mode) forms — any conflict causes an automatic abort and restores the original state - Uncommit a single file (
CommitFile → zz) - Move a file between commits (
CommitFile → Commit) - Create a new branch and move a commit (
--create)
See continue and abort for details.
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
absorb
Automatically distribute working tree changes into the commits that last touched the affected lines. Uses blame to determine the correct target for each hunk, then amends those commits — all in a single operation.
Usage
git loom absorb [-n] [files...]
Options
| Option | Description |
|---|---|
-n, --dry-run | Show what would be absorbed without making changes |
Arguments
| Argument | Description |
|---|---|
[files...] | Files to restrict absorption to (default: all tracked changed files) |
How It Works
For each file with uncommitted changes:
- Parses the unified diff into individual hunks
- For each hunk, blames the modified/deleted lines to find their originating commit
- If all hunks trace to the same in-scope commit, the whole file is absorbed
- If hunks trace to different commits, each hunk is independently absorbed into its target
- Hunks that can’t be attributed (pure additions, ambiguous) are skipped and left in the working tree
After analysis, all assigned hunks are folded into their target commits in a single rebase operation.
Examples
Absorb all changes
git loom absorb
# src/auth.rs -> a1b2c3d "Add authentication"
# src/utils.rs -> d4e5f6a "Add utility helpers"
# Absorbed 2 hunk(s) from 2 file(s) into 2 commit(s)
Absorb hunks into different commits
# src/shared.rs has changes in two separate regions,
# each originating from a different commit
git loom absorb
# src/shared.rs [hunk 1/2] -> a1b2c3d "Add login form"
# src/shared.rs [hunk 2/2] -> d4e5f6a "Add dashboard"
# Absorbed 2 hunk(s) from 1 file(s) into 2 commit(s)
Dry run
git loom absorb --dry-run
# src/auth.rs -> a1b2c3d "Add authentication"
# src/shared.rs [hunk 1/2] -> d4e5f6a "Add utility helpers"
# src/shared.rs [hunk 2/2] -- skipped (pure addition)
# Dry run: would absorb 2 hunk(s) from 2 file(s) into 2 commit(s)
Restrict to specific files
git loom absorb src/auth.rs src/utils.rs
# src/auth.rs -> a1b2c3d "Add authentication"
# src/utils.rs -> d4e5f6a "Add utility helpers"
# Absorbed 2 hunk(s) from 2 file(s) into 2 commit(s)
Conflicts
If the rebase that folds the fixup commits hits a conflict, the operation is paused. Pre-existing staged changes are saved aside automatically.
git loom absorb
# src/auth.rs -> a1b2c3d "Add authentication"
# ! Conflicts detected — resolve them with git, then run:
# loom continue to complete the absorb
# loom abort to cancel and restore original state
git add <resolved-files> && git loom continue
# ✓ Absorbed 1 hunk(s) from 1 file(s) into 1 commit(s)
See continue and abort for details.
Prerequisites
- Must be on an integration branch
- Working tree must have uncommitted changes
- Target commits must be in scope (between merge-base and HEAD)
split
Split a commit into two sequential commits by selecting which files (or hunks) go into the first.
Usage
git loom split [-p] [-m <message>] <target> [<files>...]
Arguments
| Argument | Description |
|---|---|
<target> | Commit hash, short ID, or HEAD |
<files>... | Files for the first commit. Shows an interactive picker if omitted. Ignored when -p is used. |
Options
| Option | Description |
|---|---|
-m, --message <message> | Message for the first commit. Opens editor if omitted. |
-p, --patch | Interactively pick individual hunks for the first commit |
What It Does
File-based split (default)
Shows an interactive multi-select of all files changed in the commit. The files you pick go into the first commit; the rest stay in the second commit, which keeps the original message.
You can skip the picker by listing <files> on the command line. The commit must touch at least two files.
Hunk-based split (-p)
Opens the hunk picker TUI showing every hunk in the commit. All hunks start unselected (no-op). Toggle hunks with Space; selected hunks go into the first commit, unselected hunks stay in the second. Works on single-file commits.
HEAD vs non-HEAD
- HEAD commit:
reset --mixed HEAD~1then re-commit in two steps — no rebase needed. - Non-HEAD commit: uses an edit-and-continue rebase to pause at the target, split it, then replay descendants.
Both paths preserve any pre-existing staged changes and abort cleanly on error.
Examples
Split HEAD interactively by file
git loom split HEAD
# ? Select files for the first commit
# > [x] src/auth.rs
# [ ] src/main.rs
# (opens editor for the first commit message)
# ✓ Split `abc123d` into `def456a` and `789bcd0`
Split HEAD by file non-interactively
git loom split HEAD -m "refactor: extract auth" src/auth.rs
# ✓ Split `abc123d` into `def456a` and `789bcd0`
Split a commit by short ID using the hunk picker
git loom split -p ab -m "fix: extract bounds check"
# (hunk picker TUI opens — toggle hunks for first commit)
# ✓ Split `ab12345` into `cd67890` and `ef01234`
Split a non-HEAD commit by file
git loom split ab -m "refactor: extract helpers" src/helpers.rs
# ✓ Split `ab12345` into `cd67890` and `ef01234`
Prerequisites
- Must be in a git repository with a working tree
- Target must be a commit (not a branch, file, or
zz) - Merge commits cannot be split
- File-based split requires the commit to touch at least two files
- Git ≥ 2.38
swap
Swap two commits within the same sequence.
Usage
git loom swap <a> <b>
Arguments
| Argument | Description |
|---|---|
<a> | Commit hash or short ID — first commit |
<b> | Commit hash or short ID — second commit |
What It Does
Swaps the positions of two commits within their shared sequence (a branch section or the integration line). All descendant commits are replayed in the new order.
Both commits must belong to the same sequence — swapping commits across different branch sections, or between a branch section and the integration line, is an error.
Target Resolution
Accepts full OID, partial OID prefix, or 2-char short ID. Branch names are not accepted.
Examples
Swap two commits on the integration line
git loom swap abc123 def456
# Swapped commits `abc123` and `def456`
Swap two commits in a branch section using short IDs
git loom swap aa bb
# Swapped commits `aa` and `bb`
Error: commits in different branch sections
git loom swap ca1 cb1
# ! Cannot swap commits from different branch sections
Conflicts
If a conflict occurs during the rebase, the operation is paused:
git loom swap abc123 def456
# ! Conflicts detected — resolve them with git, then run:
# loom continue to complete the swap
# loom abort to cancel and restore original state
git add <resolved-files> && git loom continue
# ✓ Swapped `abc123` and `def456`
See continue and abort for details.
Prerequisites
- Both commits must be woven into the current integration branch
- Both commits must be in the same sequence (same branch section or both on the integration line)
- Uncommitted working tree changes are preserved automatically
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
Rename to a hidden branch name
If the new name matches the configured hidden prefix (default: local-), git-loom prints a warning before the success message:
git loom reword feature-secrets -m local-secrets
# ! Branch `local-secrets` is hidden from status by default. Use `--all` to show it.
# ✓ Renamed branch `feature-secrets` to `local-secrets`
Prerequisites
- Any git repository for commit rewording
- For short IDs: must be on a branch with upstream tracking configured
drop
Drop a commit, branch, file, or all local changes.
Usage
git loom drop [-y] <target>
Arguments
| Argument | Description |
|---|---|
<target> | Commit hash, branch name, file short ID, or short ID |
Options
| Option | Description |
|---|---|
-y, --yes | Skip confirmation prompt |
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.
When Target is a File
Behavior depends on the file’s status:
- Tracked file with modifications —
git restore --staged --worktree <path>. Prompt:"Discard changes to '<path>'?". Output:"Restored '<path>'". - Staged new file (
Ain index) —git rm --force <path>. Prompt:"Delete '<path>'?". Output:"Deleted '<path>'". - Untracked file (
??) — deleted from disk. Prompt:"Delete '<path>'?". Output:"Deleted '<path>'".
A confirmation prompt is shown first (skippable with -y).
When Target is zz (all local changes)
Discards everything in the working tree and index:
git restore --staged --worktree .— reverts all tracked modificationsgit clean -fd— deletes all untracked files and directories
If there are no local changes, the command errors with "No local changes to discard".
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 short IDs to files
zz— always resolves to all local changes
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 file (discard changes)
git loom drop ma
# Discard changes to `src/main.rs`? (y/n)
# Restored `src/main.rs`
Drop a new or untracked file
git loom drop nf
# Delete `new_feature.rs`? (y/n)
# Deleted `new_feature.rs`
Drop all local changes
git loom drop zz
# Discard all local changes? (y/n)
# Discarded all local changes
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
Conflicts
Dropping a commit supports conflict recovery. If the rebase hits a conflict, the operation is paused:
git loom drop ab
# ! Conflicts detected — resolve them with git, then run:
# loom continue to complete the drop
# loom abort to cancel and restore original state
git add <resolved-files> && git loom continue
# ✓ Dropped commit `ab`
Dropping a branch does not support pause/resume — if a conflict occurs it aborts immediately and leaves the repository in its original state.
See continue and abort for details.
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
branch
Manage feature branches: create new branches, weave existing branches into the integration topology, or remove them.
Alias: br
Subcommands
| Subcommand | Description |
|---|---|
new (alias: create) | Create a new feature branch at a specified commit |
merge | Weave an existing branch into the integration branch |
unmerge | Remove a branch from integration (keeps the branch ref) |
Running git loom branch without a subcommand defaults to new.
branch new
Create a new feature branch at a specified commit.
Usage
git loom branch [name] [-t <target>]
git loom branch new [name] [-t <target>]
git loom branch create [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 on the first-parent line from HEAD to the merge-base, 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:
A1 → A2 (feature-a)
/ \
origin/main merge → A3' (HEAD)
All first-parent commits from the start up to (and including) the target move into the new branch section. Commits after the target are replayed on top of the resulting merge commit.
No-op cases — weaving does not trigger and only the branch ref is created:
- Branch at merge-base — no commits to move into the branch.
- Branch inside an existing side branch — the target commit is already part of a merge topology (reachable through a merge second-parent), so no restructuring is needed.
Branching at HEAD weaves all current first-parent commits into the new branch:
git loom branch feature-a # target = HEAD (all commits go into feature-a)
If the working tree has uncommitted changes, they are automatically stashed and restored after the operation.
If a weave rebase encounters conflicts, it aborts automatically and reports an error — no state is saved and no loom continue is available. Resolve the situation and retry.
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
At a specific commit by short ID
git loom branch feature-auth -t ab
# ✓ Created branch `feature-auth` at 72f9d3a
# ✓ Woven `feature-auth` into integration branch
At another branch’s tip
git loom branch feature-b -t feature-a
# ✓ Created branch `feature-b` at feature-a's tip commit
Branching at HEAD (weaves all commits)
git loom branch feature-a
# ✓ Created branch `feature-a` at HEAD
# ✓ Woven `feature-a` into integration branch
branch merge
Weave an existing branch into the integration branch using a merge commit.
Usage
git loom branch merge [branch] [--all]
Arguments
| Argument | Description |
|---|---|
[branch] | Branch name (optional; shows interactive picker if omitted) |
Options
| Option | Description |
|---|---|
-a, --all | Also show remote branches without a local counterpart |
What It Does
- Branch selection — uses the provided name, or shows an interactive picker listing non-woven local branches
- Validation — checks that the branch exists and is not already woven into integration
- Remote handling — if a remote branch is selected (with
--all), creates a local tracking branch automatically - Merge — performs a
git merge --no-ffto weave the branch into the integration topology
Examples
Merge a specific branch
git loom branch merge feature-auth
# ✓ Woven `feature-auth` into integration branch
Interactive picker
git loom branch merge
# ? Select branch to weave ›
# feature-auth
# feature-logging
# ✓ Woven `feature-auth` into integration branch
Include remote branches
git loom branch merge --all
# ? Select branch to weave ›
# feature-auth
# origin/feature-logging
branch unmerge
Remove a branch from the integration topology without deleting the branch ref.
Usage
git loom branch unmerge [branch]
Arguments
| Argument | Description |
|---|---|
[branch] | Branch name or short ID (optional; shows interactive picker if omitted) |
What It Does
- Branch selection — uses the provided name/short ID, or shows an interactive picker listing woven branches
- Validation — checks that the branch is actually woven into the integration branch
- Unweave — rebases the integration branch to remove the branch’s merge topology
- Preserve — the branch ref is kept intact, pointing at its original commits
This is different from drop, which deletes the branch entirely.
If the unweave rebase encounters conflicts, it aborts automatically and reports an error — no state is saved and no loom continue is available.
Examples
Unmerge a specific branch
git loom branch unmerge feature-auth
# ✓ Unwoven `feature-auth` from integration branch
Interactive picker
git loom branch unmerge
# ? Select branch to unmerge ›
# feature-auth
# feature-logging
# ✓ Unwoven `feature-auth` from integration branch
Conflicts
branch merge — supports pause/resume
If the merge encounters a conflict, loom saves state and pauses:
git loom branch merge feature-auth
# ! Conflicts detected — resolve them with git, then run:
# loom continue to complete the merge
# loom abort to cancel and restore original state
After resolving:
git add <resolved-files> && git loom continue
# ✓ Woven `feature-auth` into integration branch
branch new / branch unmerge — hard fail
Neither subcommand supports pause/resume. If a rebase conflict occurs, the rebase is aborted automatically, the repository is left in its original state, and an error is reported. Retry after resolving the conflicting situation.
See continue and abort for details.
Hidden Branch Warning
If the branch name matches the configured hidden prefix (default: local-), git-loom prints a warning before the success message:
! Branch `local-secrets` is hidden from status by default. Use `--all` to show it.
✓ Created branch `local-secrets` at abc1234
See Configuration to customize the prefix.
Reserved Names
The subcommand names new, create, merge, and unmerge are reserved and cannot be used as branch names.
Prerequisites
- Must be in a git repository with a working tree
- For the default target: must have upstream tracking configured
- For short ID targets: must have upstream tracking configured
switch
Check out any branch for testing without weaving it into the integration branch.
Usage
git loom switch [<branch>]
git loom sw [<branch>]
If no branch is given, an interactive picker lists all local branches and all remote-only branches.
Arguments
| Argument | Description |
|---|---|
<branch> | Branch to switch to: local branch name, remote-tracking name (e.g. origin/feature-x), or short ID of a woven branch. Optional — omit to pick interactively. |
What It Does
When Target is a Local Branch
HEAD moves to the named local branch (attached, not detached). No branch refs or commit history are changed.
When Target is a Remote-Only Branch
A remote-only branch is a remote-tracking ref (e.g. origin/colleague-work) with no local counterpart. HEAD is detached at that ref’s commit. No local tracking branch is created, so there is nothing to clean up afterward.
Interactive Picker (no argument)
Shows all local branches except the current one, followed by all remote-only branches. Selecting a local branch switches normally; selecting a remote-only branch detaches HEAD.
Target Resolution
- Local branch name — exact match against local branches
- Remote branch name — exact match against remote-tracking refs (e.g.
origin/feature-x) - Short ID — best-effort lookup via the woven-branch graph (requires being on an integration branch with upstream tracking configured; silently skipped otherwise)
Examples
Switch to a local branch
git loom switch feature-x
# ✓ Switched to `feature-x`
Switch using a short ID
git loom switch fx
# ✓ Switched to `feature-x`
Inspect a remote-only branch
git loom switch origin/colleague-work
# ✓ Detached HEAD at `origin/colleague-work`
# No local branch is created.
Return to the integration branch
git loom switch integration
# ✓ Switched to `integration`
Prerequisites
- Must be in a git repository with a working tree (not bare)
- Working tree must be clean: no staged changes and no unstaged modifications to tracked files (untracked files are allowed)
- Blocked while a loom operation is paused — run
continueorabortfirst
status
Show the branch-aware commit graph. This is the default command when running git loom with no arguments.
Usage
git loom [status] [-f [COMMIT...]] [N]
Arguments
| Argument | Description |
|---|---|
N | Number of context commits to show before the base (default: 1) |
Options
| Option | Description |
|---|---|
-f, --files [COMMIT...] | Show files changed in each commit, optionally filtered to specific commits |
-a, --all | Show all branches including hidden ones |
Output
The status displays a branch-aware commit graph using UTF-8 box-drawing characters, showing commits grouped by feature branch:
╭─ [local changes]
│ !! conflicted.rs
│ M file.txt
│ A new_file.rs
│ ⁕ untracked.txt
│
│╭─ [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. Files are split into three groups:
- Conflicted files are shown first with a
!!marker in bold red (filename also bold red). These appear during an in-progress rebase or merge. - Tracked changes are listed next with a 2-char
XYstatus matchinggit status --short(index green, worktree red). - Untracked files are listed last with a
⁕marker (magenta). When there are more than 5 untracked files, they are displayed in a multi-column grid layout sized to the terminal width.
- Conflicted files are shown first with a
-
Feature branches — each branch is rendered as a side branch with its name in brackets, followed by its commits, closed with
├╯. A remote tracking indicator appears after the closing]when an upstream has been configured for the branch. -
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 |
!! | Conflicted file marker (bold red) |
⁕ | Untracked file marker (magenta) |
⏫ | Upstream has new commits |
· | Context commit before the base (dimmed) |
✓ | Branch remote is in sync (green) |
↑ | Branch has unpushed commits (yellow) |
✗ | Branch remote is gone (red) |
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.
Showing Files
Use -f to show the files changed in each commit:
git loom status -f
│╭─ fa [feature-a]
│● d0 Add feature A
│┊ d0:0 M src/feature.rs
│┊ d0:1 A tests/feature_test.rs
├╯
To show files for specific commits only, pass their short IDs or git hashes after -f:
git loom status -f d0
git loom status -f d0 ab
git loom status -f abc1234
Only the listed commits display their file list; all other commits are rendered normally. Unknown identifiers are silently ignored.
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
Context commits
Show history before the base with a positional argument (git loom 3 or git loom status 3):
● ff1b247 (upstream) [origin/main] Initial commit
· abc1234 2025-07-05 Previous work
· def5678 2025-07-04 Earlier change
Context commits are dimmed and display-only (no short ID, not actionable). The default is 1 (no extra context).
Hidden Branches
Branches whose names start with the configured prefix (default: local-) are hidden from the status output by default. Both the branch section and its commits are fully suppressed — they do not appear as loose commits either.
This is useful for keeping local-only branches (personal configuration, secrets) out of the status view without removing them from the integration branch.
git loom --all # show all branches including hidden
git loom status --all # same, explicit
The hidden prefix is configurable (see Configuration).
Theming
The graph colors adapt to the terminal background via the global --theme flag:
git loom --theme light status # Light terminal background
git loom --theme dark status # Dark terminal background
git loom --theme auto status # Auto-detect (default)
See Configuration for details.
Prerequisites
- Must be on a local branch (not detached HEAD)
- Branch must have an upstream tracking branch configured
show
Show the diff and metadata for a commit, like git show.
Usage
git loom show <target>
Arguments
| Argument | Description |
|---|---|
<target> | Commit hash, branch name, or short ID |
What It Does
Displays the commit metadata (author, date, message) and diff for the resolved commit, exactly like git show. Uses git’s native pager when running in a terminal.
- When given a commit (hash, partial hash, or short ID): shows that commit
- When given a branch (name or short ID): shows the branch’s tip commit
Target Resolution
The target is resolved in this order:
- Branch names — exact match resolves to the branch tip commit
- Git references — full/partial hashes,
HEAD, etc. resolve to commits - Short IDs — branch short IDs resolve to the branch tip, commit short IDs to commits
Examples
Show a commit by short ID
git loom show ab
# Displays commit info and diff for the commit with short ID "ab"
Show a commit by hash
git loom show 9f484b6
Show the tip of a branch
git loom show feature-a
# Shows the latest commit on feature-a
Prerequisites
- Any git repository
- For short IDs: must be on a branch with upstream tracking configured
diff
Show a diff using short IDs, like git diff.
Usage
git loom diff [args...]
Alias: di
Each argument is a file, commit, or commit range. Arguments can be mixed freely in a single invocation.
Arguments
| Argument | Description |
|---|---|
[args...] | Files (short ID or path), commits (short ID, hash), or ranges (left..right) |
What It Does
When No Arguments Are Given
Shows unstaged changes in the working tree — identical to git diff with no arguments.
When a Commit Is Given
Resolves the token to a full hash and passes it to git diff, showing the diff between that commit and the working tree.
When a File Is Given
Resolves the token to a file path and runs git diff HEAD -- <path>, showing all changes to that file since HEAD (staged and unstaged combined).
When a Commit Range Is Given
Tokens of the form left..right are resolved on each side and forwarded to git diff. Branch names, HEAD, and tags that can’t be resolved as short IDs are passed through to git unchanged, so all standard range forms work:
git loom diff HEAD~3..HEAD
git loom diff main..HEAD
git loom diff ab..3c
When a Commit and a File Are Both Given
The commit is placed before -- and the file after, limiting the diff to that file at that commit.
Target Resolution
Single tokens (not ranges) are resolved in this order:
- Files — short ID or repository-relative path (checked before commits)
- Commits — short ID, partial hash, or full hash
Range endpoints use lenient resolution: if a token cannot be resolved as a short ID or hash it is passed to git as-is, allowing HEAD, branch names, and tags.
Examples
Show unstaged changes
git loom diff
# Equivalent to: git diff
Diff a commit by short ID
git loom diff ab
# Shows the diff between commit "ab" and the working tree
Diff a file by short ID
git loom diff ma
# Shows all changes to the file with short ID "ma" since HEAD
Diff a commit range
git loom diff ab..d0
# Shows what changed between those two commits
Limit diff to a file at a specific commit
git loom diff ab ma
# Equivalent to: git diff <hash-of-ab> -- src/auth/login.rs
Prerequisites
- A non-bare git repository.
- For short IDs: upstream tracking configured on the current branch (same as
git loom status).
trace
Show the latest command trace — a detailed audit trail of every git operation performed by the last loom command.
Usage
git loom trace
No arguments.
What It Does
Every time you run a loom command that modifies the repository (e.g. fold, commit, drop, reword, split, absorb, branch, push, update), git-loom records a trace file to .git/loom/logs/. The trace captures:
- Every git command executed, with the full argument list
- Timing for each command (in milliseconds)
- Success/failure status
- stderr output on failure
- Rebase todo annotations — both the original git todo and the generated todo
Only the 10 most recent trace files are kept; older ones are automatically pruned.
Running git loom trace prints the latest trace file to stdout with colored output.
Output Format
[2026-03-04 14:30:00.123] git loom fold aa bb
================================================================================
[git] rebase --interactive --autostash ...abc1234 [230ms]
[original git todo]
pick abc1234 First commit
noop
[generated todo]
label onto
reset onto
pick abc1234 First commit
[git] reset --hard HEAD [5ms]
[git] commit --amend --no-edit [12ms] FAILED
[stderr]
error: could not apply abc1234
- Header — timestamp and the full command line
- Command entries — program, arguments, duration, and optional
FAILEDmarker - Annotations — rebase todo content (original and generated)
- stderr — shown only for failed commands
Colors
When output is a terminal:
- Header: bold
- Command entries: cyan (with
FAILEDin red bold) - Annotation labels: yellow
- Annotation content: dimmed
- stderr: red
Storage
Trace files are stored at .git/loom/logs/<timestamp>.log with the naming pattern YYYY-MM-DD_HH-MM-SS_mmm.log. The file path is printed at the end of the output.
Examples
After a fold operation
git loom fold aa bb
git loom trace
# Shows the full sequence: rebase, reset, commit --amend, etc.
After a failed rebase
git loom drop aa
# x Rebase failed with conflicts — aborted
git loom trace
# Shows the rebase command with FAILED status and stderr output
No trace available
git loom trace
# x No log files found
# › Run a command first to generate a log
Notes
- Read-only commands (
status,trace) do not generate trace files - The
internal-write-todosubprocess does not generate its own trace - Trace files are plain text and can be inspected directly in
.git/loom/logs/
continue
Resume a paused loom operation after resolving rebase conflicts.
Usage
git loom continue
When to Use It
When an in-scope loom command (commit, update, absorb, drop, fold) encounters a
rebase conflict, it pauses instead of aborting. The operation is saved to
.git/loom/state.json and the process exits with code 0.
The terminal output shows which files are conflicted and what to do next:
! Conflicts detected — resolve them with git, then run:
loom continue to complete the commit
loom abort to cancel and restore original state
Once you’ve resolved conflicts and staged the resolution:
# resolve conflicts in your editor, then:
git add <resolved-files>
git loom continue
What It Does
- Loads the saved state from
.git/loom/state.json - If a rebase is still in progress, runs
git rebase --continue- If that hits another conflict: stays paused, keeps the state file, exits successfully
- If it completes: moves on
- If no rebase is in progress (e.g. you already ran
git rebase --continuemanually): skips to dispatch - Dispatches to the interrupted command’s post-rebase work (restoring staged patches, printing the success message, etc.)
- Deletes the state file on success
Double Conflicts
If your branch has multiple conflicting commits, each loom continue may hit
a new conflict at the next commit. Repeat the resolve-and-continue cycle as
many times as needed:
git loom commit -b feature-auth -m "add auth" zz
# ! Conflicts detected...
git add auth.rs && git loom continue
# ! Conflicts remain — resolve them and run `loom continue` again
git add shared.rs && git loom continue
# ✓ Created commit `a1b2c3d` on branch `feature-auth`
Which Commands Are Paused
| Command | Pauseable |
|---|---|
update | ✓ |
commit | ✓ |
absorb | ✓ |
drop <commit> | ✓ |
fold (simple paths) | ✓ |
drop <branch> | — (aborts immediately) |
reword | — (aborts immediately) |
split | — (aborts immediately) |
fold (edit/multi-phase paths) | — (aborts immediately) |
Commands Allowed While Paused
While a loom operation is paused, most commands are blocked. The following are still available:
show— inspect commitstrace— check recent command outputcontinue— resume the paused operationabort— cancel the paused operation
Error: No Operation in Progress
git loom continue
# error: No loom operation is in progress
See Also
abort— cancel instead of resuming
abort
Cancel a paused loom operation and restore the repository to its original state.
Usage
git loom abort
When to Use It
When a loom operation is paused due to a conflict and you decide you don’t want
to complete it, loom abort cancels the operation and rolls back all changes
made so far:
git loom commit -b feature-auth -m "add auth" zz
# ! Conflicts detected...
git loom abort
# ✓ Aborted `loom commit` and restored original state
What It Does
- Loads the saved state from
.git/loom/state.json - Aborts the active rebase (if one is in progress)
- Applies rollback:
- Hard-resets HEAD to the pre-operation state
- Restores all branch refs to their pre-operation positions
- Deletes any branches that were created during the operation
- Re-applies pre-existing staged changes (if any were saved aside)
- Re-applies working-tree changes (if any were saved)
- Deletes the state file
- Reports success
Special case: commit
After aborting a commit, the committed content is returned to the working
tree as unstaged changes (via git reset --mixed) rather than being
discarded. Your work is preserved; the commit is simply undone.
Error: No Operation in Progress
git loom abort
# error: No loom operation is in progress
See Also
continue— resume instead of cancelling
Configuration
Git Config Settings
| Setting | Values | Default | Description |
|---|---|---|---|
loom.remote-type | github, azure, gerrit | Auto-detected | Override the remote type for git loom push |
loom.push-remote | Any remote name | Auto-detected | Override which remote to push to (e.g., personal for fork workflows) |
loom.hideBranchPattern | Any prefix string | local- | Prefix for branches hidden from loom status by default |
loom.remote-type
By default, git loom push auto-detects the remote type:
- GitHub — if the remote URL contains
github.com - Azure DevOps — if the remote URL contains
dev.azure.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 azure # Force Azure DevOps push (push + open PR)
git config loom.remote-type gerrit # Force Gerrit push (refs/for/<branch>)
loom.push-remote
By default, git loom push uses the integration branch’s remote for pushing. One exception: if the integration branch tracks a remote named upstream and a remote named origin also exists, pushes go to origin automatically (the standard GitHub fork convention).
For non-standard fork setups where your remotes have different names, set this explicitly:
git config loom.push-remote personal
For example, with remotes:
origin→ upstream read-only repositorypersonal→ your fork (where you push)
Now git loom push will push to personal regardless of remote names.
loom.hideBranchPattern
Branches whose names start with this prefix are hidden from loom status by default — both the branch section and its commits are suppressed. Pass --all to show them.
git config loom.hideBranchPattern "local-" # default: hide local-* branches
git config loom.hideBranchPattern "secret-" # hide secret-* branches instead
git config loom.hideBranchPattern "" # disable hiding entirely
Hidden branches remain fully accessible to all other loom commands (fold, drop, commit, push, etc.).
When creating or renaming a branch to a name that matches this prefix, git-loom prints a warning.
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 |
--theme <auto|dark|light> | Set the graph color theme (default: auto) |
--theme
Controls the color palette used for graph output.
| Value | Behavior |
|---|---|
auto | Detect the terminal background and choose dark or light automatically. Falls back to dark if detection fails or output is not a TTY. |
dark | Always use the dark theme (optimized for dark terminal backgrounds). |
light | Always use the light theme (optimized for light terminal backgrounds). |
git loom --theme light
git loom --theme dark status