Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

git-loom is a Git CLI tool that 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).

git-loom status

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:

Tip

git loom commit is the primary way to create branches — it will prompt you for the branch name, or let you create a new one on the fly. Use git loom branch only 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:

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

Extract the binary and place it somewhere on your PATH.

From Source

Requires Rust 1.90 or later.

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

Requirements

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

Shell Setup

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

PowerShell

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

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

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

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

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

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:

  1. Stages all your changes (zz means “everything”)
  2. Creates the feature-auth branch (it didn’t exist yet)
  3. Weaves it into the integration topology
  4. 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, update aborts automatically and tells you the full git rebase command 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

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 d0 to 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

-f without 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 fold for 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.

KeyAction
/ k, / jNavigate up/down
Tab / Shift+TabSwitch between left and right pane
SpaceToggle hunk (right pane) or all hunks in file/directory (left pane)
c / EnterConfirm selections
q / Esc / Ctrl+CCancel 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

See also: add · commit · fold

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 the gh CLI 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 az CLI 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. main or master) 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 addloom 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 abort instead — that also aborts the rebase and restores your branch refs.

See Also

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

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

What It Does

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

All three happen in a single atomic operation.

Upstream Detection

The upstream is resolved automatically in priority order:

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

Examples

Default

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

Custom name

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

Error: branch already exists

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

Error: no remotes

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

Prerequisites

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

update

Pull-rebase the integration branch onto the latest upstream and update submodules.

Alias: up

Usage

git loom update [-y]

Options

OptionDescription
-y, --yesSkip 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:

  1. Direct merge — if the upstream is a descendant of the commit’s OID, the commit was merged directly.
  2. 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 init first)
  • 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

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

Flags

FlagDescription
--no-prPush without creating a PR or Gerrit review (see below)

Remote Type Detection

Detection priority (first match wins):

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

Push Remote Selection

Detection priority (first match wins):

  1. Explicit configgit config loom.push-remote <remote>
  2. GitHub fork convention — if the integration remote is named upstream and origin exists, push to origin
  3. 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 create with 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 create with 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
PlainSame as normal (force-with-lease push)
GitHubSkips gh pr create
Azure DevOpsSkips az repos pr create
GerritPlain 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 refspec feature-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
  • gh CLI (optional, for GitHub PR creation)
  • az CLI (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

ArgumentDescription
<files...>Files to stage: short IDs from loom status, relative paths, or zz to stage everything

Options

OptionDescription
-p, --patchOpen 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:

  1. zz — always stages everything (plain mode) or shows all files (-p mode)
  2. Short IDs — file short IDs from loom status output (e.g. a3, 0f)
  3. 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 -p mode

commit

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

Usage

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

Options

OptionDescription
-b, --branch <branch>Target feature branch (name or short ID). Prompts if omitted.
-m, --message <message>Commit message. Opens editor if omitted.
-p, --patchInteractively select hunks to stage before committing.

File Arguments

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

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

What It Does

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

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

New Branch Creation

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

Examples

Interactive

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

Fully specified

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

Specific files by short ID

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

To a new branch

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

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

OptionDescription
-p, --patchInteractively select hunks before folding. Three forms depending on argument types (see below).
-c, --createCreate a new branch and move the source commit into it.

Type Dispatch

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

SourceTargetAction
(staged)CommitAmend staged: fold currently staged files into the commit
File(s)CommitAmend: stage files into the commit
zzCommitAmend all: stage all changed files into the commit
CommitCommitFixup: absorb source commit into target
CommitBranchMove: relocate commit to the branch
CommitzzUncommit: remove commit, put changes in working directory
CommitFilezzUncommit file: remove one file from a commit to working directory
CommitFileCommitMove file: move one file’s changes between commits
CommitNew 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 referencesHEAD, 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

OptionDescription
-n, --dry-runShow what would be absorbed without making changes

Arguments

ArgumentDescription
[files...]Files to restrict absorption to (default: all tracked changed files)

How It Works

For each file with uncommitted changes:

  1. Parses the unified diff into individual hunks
  2. For each hunk, blames the modified/deleted lines to find their originating commit
  3. If all hunks trace to the same in-scope commit, the whole file is absorbed
  4. If hunks trace to different commits, each hunk is independently absorbed into its target
  5. 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

ArgumentDescription
<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

OptionDescription
-m, --message <message>Message for the first commit. Opens editor if omitted.
-p, --patchInteractively 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~1 then 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

ArgumentDescription
<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

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

Options

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

What It Does

When Target is a Commit

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

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

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

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

When Target is a Branch

Renames the branch using git branch -m.

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

Target Resolution

The target is resolved in this order:

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

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

Examples

Reword a commit with editor

git loom reword ab
# Opens editor with current message

Reword a commit directly

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

Rename a branch interactively

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

Rename a branch directly

git loom reword fa -m feature-authentication

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

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

Options

OptionDescription
-y, --yesSkip 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 modificationsgit restore --staged --worktree <path>. Prompt: "Discard changes to '<path>'?". Output: "Restored '<path>'".
  • Staged new file (A in 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:

  1. git restore --staged --worktree . — reverts all tracked modifications
  2. git 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

  1. Branch names — exact match resolves to a branch (drops the branch)
  2. Git references — full/partial hashes resolve to commits
  3. Short IDs — branch short IDs resolve to branches, commit short IDs to commits, file short IDs to files
  4. 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

SubcommandDescription
new (alias: create)Create a new feature branch at a specified commit
mergeWeave an existing branch into the integration branch
unmergeRemove 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

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

Options

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

What It Does

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

Automatic Weaving

When a branch is created at a commit 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:

  1. Branch names — resolves to the branch’s tip commit
  2. Git hashes — full or partial commit hashes
  3. Short IDs — the compact IDs shown in git loom status
  4. 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

ArgumentDescription
[branch]Branch name (optional; shows interactive picker if omitted)

Options

OptionDescription
-a, --allAlso show remote branches without a local counterpart

What It Does

  1. Branch selection — uses the provided name, or shows an interactive picker listing non-woven local branches
  2. Validation — checks that the branch exists and is not already woven into integration
  3. Remote handling — if a remote branch is selected (with --all), creates a local tracking branch automatically
  4. Merge — performs a git merge --no-ff to 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

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

What It Does

  1. Branch selection — uses the provided name/short ID, or shows an interactive picker listing woven branches
  2. Validation — checks that the branch is actually woven into the integration branch
  3. Unweave — rebases the integration branch to remove the branch’s merge topology
  4. 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

ArgumentDescription
<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

  1. Local branch name — exact match against local branches
  2. Remote branch name — exact match against remote-tracking refs (e.g. origin/feature-x)
  3. 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 continue or abort first

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

ArgumentDescription
NNumber of context commits to show before the base (default: 1)

Options

OptionDescription
-f, --files [COMMIT...]Show files changed in each commit, optionally filtered to specific commits
-a, --allShow 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:

  1. 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 XY status matching git 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.
  2. 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.

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

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

Symbols

SymbolMeaning
╭─Start of a section
├─Start of a subsequent branch in a stack
Integration line continuation
││Continuation between stacked branches
A commit
├╯End of a side branch
!!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

ArgumentDescription
<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:

  1. Branch names — exact match resolves to the branch tip commit
  2. Git references — full/partial hashes, HEAD, etc. resolve to commits
  3. 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

ArgumentDescription
[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:

  1. Files — short ID or repository-relative path (checked before commits)
  2. 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 FAILED marker
  • 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 FAILED in 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-todo subprocess 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

  1. Loads the saved state from .git/loom/state.json
  2. 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
  3. If no rebase is in progress (e.g. you already ran git rebase --continue manually): skips to dispatch
  4. Dispatches to the interrupted command’s post-rebase work (restoring staged patches, printing the success message, etc.)
  5. 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

CommandPauseable
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 commits
  • trace — check recent command output
  • continue — resume the paused operation
  • abort — 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

  1. Loads the saved state from .git/loom/state.json
  2. Aborts the active rebase (if one is in progress)
  3. 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)
  4. Deletes the state file
  5. 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

SettingValuesDefaultDescription
loom.remote-typegithub, azure, gerritAuto-detectedOverride the remote type for git loom push
loom.push-remoteAny remote nameAuto-detectedOverride which remote to push to (e.g., personal for fork workflows)
loom.hideBranchPatternAny prefix stringlocal-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-msg contains “gerrit”
  • Plain Git — otherwise

You can override this with:

git config loom.remote-type github   # Force GitHub push (push + open PR)
git config loom.remote-type 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 repository
  • personal → 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

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

CLI Flags

FlagDescription
--no-colorDisable colored output
--theme <auto|dark|light>Set the graph color theme (default: auto)

--theme

Controls the color palette used for graph output.

ValueBehavior
autoDetect the terminal background and choose dark or light automatically. Falls back to dark if detection fails or output is not a TTY.
darkAlways use the dark theme (optimized for dark terminal backgrounds).
lightAlways use the light theme (optimized for light terminal backgrounds).
git loom --theme light
git loom --theme dark status