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

git-loom weaves your feature branches into a single integration branch, so you can work on all of them at once — committing, amending, splitting and reordering — without ever switching branches. It is inspired by jujutsu and GitButler.

See It in Action

From the command line, with the status pinned on the left:

The same operations from the interactive git loom tui:

Core Concepts

Integration Branch

The branch you work on. It tracks a remote branch (e.g. origin/main) and merges in all your feature branches, so you build and test them together. Create it with git loom init.

Feature Branches

Independent branches woven into the integration branch, each pushed and reviewed on its own. You manage them from the integration branch:

  • commit commits to a feature branch, creating it if needed
  • fold amends, fixes up, or moves commits and files between branches
  • push pushes a branch and opens its pull request

Tip

git loom commit -b <name> is the usual way to start a branch. git loom branch is for creating an empty branch ahead of time, or turning existing commits into one.

Weaving

Each feature branch sits on the integration branch as a side branch joined by a merge, which is what git loom status draws. Every loom command rewrites history to keep that shape, so you never rebase by hand.

A rewrite keeps the modification time of files whose content did not change, so your build tool does not rebuild everything. Pause watcher-driven builds (cargo watch, build on save) while a loom command runs: they may compile half-rewritten files.

Short IDs

git loom status labels every branch, commit and file with a short ID (fa, pop, ma) that you type in other commands instead of hashes, names or paths.

Commit IDs are persistent: they come from a Change-Id trailer that loom adds to every commit it creates (see loom.changeId), so they survive rewrites and you can chain commands on the same ID.

Quick Start

# Create an integration branch from your main branch
git checkout main
git loom init

# Commit everything to a new feature branch, then add a second commit
git loom commit -b feature-auth -m "add login form" zz
git loom commit -b feature-auth -m "improve login form" zz

# See the branch-aware status
git loom

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

Next: the Tutorial, or Working with AI Agents to let Claude Code drive loom for you.

git-loom 1.0

Weave your branches together.

git-loom is a Git extension for working on several features at once. You stay on one integration branch that merges all your feature branches, so you build and test them together, and from there loom commits to, rewrites and pushes each branch on its own. You never switch branches, and you never rebase by hand.

It is plain Git all the way down. Feature branches are ordinary branches, the integration branch is ordinary merges, and every other Git tool keeps working on the same repository.

With 1.0, the command line, the loom.* settings and agent mode are stable: see Compatibility.

See It in Action

From the command line:

The same operations from the TUI:

The Idea

Your integration branch tracks the upstream, origin/main for instance, and merges in each feature branch as a side branch. git loom status, or just git loom, draws it:

╭─ zz [local changes]
│   ma  M src/main.rs
│   nf A  new_file.rs
│   un  ⁕ untracked.txt
│
│╭─ fb [feature-b] ✓
│●    pop  d0472f9 Fix bug in feature B
│●    hug  7a067a9 Start feature B
├╯
│
│╭─ fa [feature-a] ↑
│●    dog  2ee61e1 Add feature A
├╯
│
● ff1b247 (upstream) [origin/main] Initial commit

Each branch is pushed and reviewed on its own; the integration branch is where you work on all of them together. Every line carries a short ID (zz, fb, pop, ma) that other commands take in place of a branch name, a hash or a path.

Pick How You Work

Every loom operation is there three ways, and you can mix them in the same repository:

  • The command line. Each operation is one command on short IDs: git loom fold ma pop amends src/main.rs into commit pop. Completions cover PowerShell, Clink, bash, zsh and fish.
  • The TUI. git loom tui puts the branch tree beside the diff of the row under the cursor, with loom’s actions one key away: c commits the selected files, f folds them, s splits a commit, p pushes a branch. When an action needs a target, you move the new commit or the fold through the tree with the arrow keys and place it with Enter. A conflict opens a paused view where you stage the resolved files, then continue or abort.
  • An AI agent. git loom agent install teaches Claude Code to use loom. Ask for the result, “put this CSRF fix into the login form commit”, and it runs git loom fold src/auth.rs pop. Loom never guesses on its behalf: when a choice is missing, it answers with the question, changes no history, and the agent brings the question to you. Any other agent or script can drive loom with --agent, which ends every command with one line of JSON.

What You Can Do

  • Commit to any branch without leaving the integration branch: git loom commit -b feature-a -m "…" <files>. A new name creates the branch.
  • Rewrite history with one command each: fold amends a past commit, fixes it up, or moves commits and files between commits and branches; split, swap, reword and drop do what they say; absorb sends each change to the commit that last touched its lines.
  • Pick hunks: -p on add, commit, fold and split opens a hunk picker.
  • Stack branches: git loom branch --on <branch> starts a branch on top of another, and push sends the whole stack.
  • Push for review: one pull request per branch on GitHub (stacked PRs, forks), GitLab, Azure DevOps or Gerrit, or a plain push anywhere else.
  • Stay up to date: update pull-rebases the whole weave, updates submodules, and offers to remove branches merged upstream. update <branch> takes a branch’s remote version, pushed from another machine or by a teammate.
  • Bring branches in and out: merge weaves in an existing branch, a colleague’s included; unmerge takes it out and keeps the branch; switch checks out any branch to test it alone.
  • Recover from anything: a conflict pauses the operation until you continue or abort. No loom command ever discards staged, unstaged or uncommitted changes, abort included.

Worktrees: Several Integration Branches at Once

Need a second line of work going at the same time, such as a hotfix while a long build runs, or a second agent working in parallel? Give it a worktree. Each worktree holds its own integration branch, tracking the same upstream:

git loom wt new hotfix
# ✓ Created worktree /src/repo-hotfix on integration-hotfix tracking origin/main
cd "$(git loom wt path hotfix)"

Every loom command works there as in the main checkout, and a paused operation stays private to its worktree. git loom wt lists the worktrees and marks the dirty ones; git loom wt drop removes one, and keeps its integration branch if it holds commits found nowhere else. A feature branch is woven into one integration branch at a time, so merge refuses a branch already woven into another worktree. A worktree made with git worktree add works too, after a git loom init inside it.

What Makes It Different

Mix and Match with Raw Git

Loom adds commands to Git; it does not take Git away. Use whichever is handier for each step:

  • Stage with git add -p or your IDE, then git loom commit -b <branch> commits exactly what is staged.
  • Commit straight to the integration branch with git commit: it shows up as a loose commit, and git loom branch <name> --on <commit> turns it into a feature branch later.
  • Make a branch with plain Git, or fetch a colleague’s, and weave it in with git loom merge.
  • Inspect with git log, git blame or any Git GUI: the weave is ordinary merge commits.
  • git loom commit runs your hooks, and anything after -- goes to the git command behind a loom command: git loom commit -m "wip" -- --no-verify.

Rewriting the integration branch is the one thing to leave to loom: a plain git rebase would flatten the merges that hold the weave.

Short IDs You Can Say Out Loud

Every branch, commit and file in the status gets a short ID, and zz always means all your local changes. Branches and files take two letters from their name: fa for feature-a, ma for main.rs. Commits that loom creates get a persistent ID, such as pop, sun or dog, in which consonants and vowels alternate, so every ID can be said aloud and many are words.

Like jj’s change IDs, a persistent ID survives every rewrite, so you can chain commands on it. Unlike them, it comes from a standard Change-Id trailer in the commit message, the one Gerrit uses, so it survives any Git tool that keeps the message.

git loom fold ma pop                    # amend src/main.rs into commit pop
git loom reword pop -m "Fix the parser" # still pop

Local Branches

A branch whose name starts with local- is woven like any other, so its commits are in everything you build and run. But status hides it, and push never publishes it. Keep your personal configuration, debug logging or a workaround you are not ready to share there, and it never leaks into a pull request. git loom status --all shows it, and loom.hideBranchPattern changes the prefix.

Builds That Stay Incremental

A Git rebase rewrites every file the replayed commits touch, even when its content ends up the same, and the fresh timestamp makes make, cargo or ninja rebuild it. Loom records modification times before each rebase and puts them back on every file whose bytes did not change. After a reword, swap or branch, nothing rebuilds; after a fold or split, only what changed does. Pause watch-mode builds while a loom command runs: they would compile half-rewritten files.

How It Works: the Weave Engine

Loom does not reimplement rebase. Every history-changing command builds a model of the integration branch (its base, each woven branch and its commits, the merges that join them), changes that model in memory, and writes the complete git rebase --rebase-merges todo from it. It then runs one native interactive rebase with itself as the sequence editor, so the todo Git executes is exactly the one loom wrote. Git does the work: --update-refs moves every branch along, a conflict stops as in any rebase, and git loom abort puts back the history, the branches and every uncommitted change.

How It Compares

git-loom borrows from the two tools that inspired it: persistent change IDs and absorb from jujutsu, working on many branches at once from GitButler. All three work with Git repositories; they differ in how much of Git’s own model you keep.

jujutsu (jj)

jj is a version control system of its own. It can store its commits in a regular Git repository, and a colocated workspace lets you mix jj and git commands, but its model is different: the working copy is committed automatically, there is no index, and Git usually sees a detached HEAD. git-loom keeps Git’s model. You stay on a branch, and git add, the index and git commit work as always; loom only adds commands on top.

GitButler

GitButler is the closest in spirit. Its parallel branches are applied together in one working directory, and Git sees them as a merge commit on a gitbutler/workspace branch that GitButler rebuilds as you change branches. That branch belongs to GitButler, though: a pre-commit hook refuses git commit on it, and switching branches with git switch pauses GitButler until you come back through it. git-loom’s integration branch is yours: git commit works on it, and you can switch away with git and come back, with no mode to leave or set up again (see Mix and Match with Raw Git).

Commands

Workflow:
  init              Initialize a new integration branch
  update, up        Pull-rebase and update submodules
  push, pr          Push a branch to remote
  agent             Install the loom skill for AI agents

Staging:
  add               Stage files using short IDs or paths [-p for interactive hunks]

Commits:
  commit, ci        Create a commit on a feature branch [-p for interactive hunks]
  fold              Amend, fixup, or move commits [-p for interactive hunks] [amend, fixup, mv, rub]
  absorb            Auto-distribute changes into originating commits
  split             Split a commit into two [-p for interactive hunks]
  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        Create a feature branch
  merge             Merge an existing branch into integration
  unmerge           Remove a branch from integration (keeps the ref)
  switch, sw        Switch to any branch for testing (without merging)
  worktree, wt      Manage worktrees, each with its own integration branch (list by default, new, drop, path)

Inspection:
  status            Show the branch-aware status (default command)
  tui               Interactive status TUI (tree + diff, with actions)
  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, cont    Resume a paused operation after resolving conflicts
  abort             Cancel a paused operation and restore original state

Configuration

Set these with git config <setting> <value>; Configuration has the details.

SettingDefaultWhat it does
loom.remoteTypeDetected, else askedHow push pushes: github, gitlab, azure, gerrit or plain
loom.pushRemoteDetectedThe remote push pushes to
loom.hideBranchPatternlocal-Prefix of the branches status hides and push never publishes
loom.statusContext1Commits shown at and before the base by status and tui
loom.pruneGoneBranchesfalseLet update remove merged and gone branches without asking
loom.changeIdtrueAdd a Change-Id trailer to the commits loom creates

Get Started

cargo install git-loom

# Create an integration branch from your main branch
git checkout main
git loom init

# Commit everything to a new feature branch, see the status, push it
git loom commit -b feature-auth -m "Add the login form" zz
git loom
git loom push feature-auth

git-loom needs Git 2.43 or later, and the gh or az CLI to open pull requests on GitHub or Azure DevOps. It is also on Scoop and as pre-built binaries for Linux, macOS and Windows.

Installation

Cargo (all platforms)

cargo install git-loom

Scoop (Windows)

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

Pre-built binaries

Download the archive for your platform from the Releases page and put the binary on your PATH:

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

From Source

Requires Rust 1.88 or later.

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

Requirements

  • Git 2.43 or later.
  • Optional: the gh (2.81 or later) or az CLI, for git loom push to create pull requests on GitHub or Azure DevOps.

Once installed, the binary answers to git loom as well as git-loom.

Shell Setup

git-loom completes commands and their flags in PowerShell, Clink, Bash, Zsh and Fish. Short aliases such as ci or up are not offered, and options passed to git after -- are not completed.

PowerShell

Add to your profile (echo $PROFILE shows where it is):

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

loom completes too, so Set-Alias loom git-loom works wherever you put it. Any other alias for git-loom completes only when it is defined before that line.

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

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

To complete your own doskey macros, list them in loom_commands before the load line (default "loom"):

loom_commands = "loom l"

Bash

Add to ~/.bashrc:

eval "$(git loom completions bash)"

git loom <Tab> completes too when git’s own bash completion is loaded.

Bash does not complete through aliases, so loom is completed by name. To complete your own aliases, list them in LOOM_COMMANDS before the eval line (default "loom"):

LOOM_COMMANDS="loom gl"

Zsh

Add to ~/.zshrc, after compinit:

eval "$(git loom completions zsh)"

git loom <Tab> completes too, through zsh’s git completion. An alias for git-loom completes like it; a function needs compdef _git-loom <name>.

Fish

Add to ~/.config/fish/config.fish:

git loom completions fish | source

git loom <Tab> completes too, through fish’s git completion. An alias for git-loom completes like it; a function needs --wraps git-loom.

Tutorial

This tutorial walks through a typical git-loom session: set up an integration branch, work on two features at once, and stay up to date with upstream.

Getting Started

Your project has a remote origin/main. Create an integration branch from it:

git checkout main
git loom init

A fresh clone works the same. If it is a GitHub fork, init offers to track the original repository as upstream (see init).

Run git loom status (or just git loom) to see where you are:

╭─ zz [local changes]
│   no changes
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit

Nothing yet: no local changes, and the upstream commit your work will build on.

Your First Commit

You add a login form: src/auth.rs and templates/login.html. They show up as local changes:

╭─ zz [local changes]
│   au  ⁕ src/auth.rs
│   lo  ⁕ templates/login.html
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit

Commit them straight to a feature branch:

git loom commit -b feature-auth -m "add login form" zz

zz means “all local changes”. The feature-auth branch doesn’t exist yet, so loom creates it and weaves it into the integration branch:

╭─ zz [local changes]
│   no changes
│
│╭─ fa [feature-auth]
│●    pop  711d082 add login form
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit

fa and pop are short IDs for the branch and the commit; you type them in later commands. Add password validation and commit again, this time naming the branch by its short ID:

git loom commit -b fa -m "add password validation" zz
╭─ zz [local changes]
│   no changes
│
│╭─ fa [feature-auth]
│●    hug  4f825d2 add password validation
│●    pop  711d082 add login form
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit

Working on Multiple Features

Now you start a dashboard, src/dashboard.rs and templates/dashboard.html. No need to switch branches — commit to a new one:

git loom commit -b feature-dashboard -m "add dashboard layout" zz
╭─ zz [local changes]
│   no changes
│
│╭─ fd [feature-dashboard]
│●    dog  7a5bb0b add dashboard layout
├╯
│
│╭─ fa [feature-auth]
│●    hug  4f825d2 add password validation
│●    pop  711d082 add login form
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit

Both branches are in your working tree, so you build and test them together, while each stays independent for review. That is the core of git-loom.

Staying Up to Date

Your teammates pushed to origin/main. Fetch their work and rebase yours on top:

git loom update
╭─ zz [local changes]
│   no changes
│
│╭─ fd [feature-dashboard]
│●    dog  8f74d1c add dashboard layout
├╯
│
│╭─ fa [feature-auth]
│●    hug  660e970 add password validation
│●    pop  885cc0c add login form
├╯
│
● b2c3d4e (upstream) [origin/main] Teammate's latest commit

Every branch moved onto the new upstream. The hashes changed, the short IDs did not. Your uncommitted changes are kept.

Once a branch you pushed is merged and deleted on the remote, update offers to remove your local copy:

#   › `feature-auth`: upstream gone
# ? Remove local branch? [y/N]

If the rebase hits a conflict, update pauses so you can resolve it — see Resolving Conflicts.

Next Steps

Working with AI Agents

AI coding agents know git, not loom. Left alone, an agent amends with git commit --amend or reorders with git rebase -i, which tangles your integration branch. This tutorial sets up Claude Code to drive loom instead, and shows what happens when it does.

It assumes a repository already set up with git loom init.

1. Install the skill

git loom agent install
# ✓ Installed Claude skill at `/home/me/.claude/skills/git-loom/SKILL.md`
#   › Restart Claude Code to pick up the new skill

The skill tells the agent to use loom for every history change in a repository where git loom status works. Other repositories are unaffected. Restart Claude Code to load it, then ask for the status: the agent should answer by running git loom status --agent.

To share the skill with your team, install it into the repository instead and commit the file:

git loom agent install --project
# ✓ Installed Claude skill at `/src/app/.claude/skills/git-loom/SKILL.md`

Everyone then needs the same loom version: with any other, loom reports the committed skill as out of date.

2. Ask for results, not commands

You don’t need to know which loom command does what. Describe the result you want:

You askThe agent runs
“Commit the login form to a new feature-auth branch”git loom commit -b feature-auth -m "Add login form" src/auth.rs
“Put this CSRF fix into the login form commit”git loom fold src/auth.rs pop
“Move the logging helper to feature-dashboard”git loom fold sun feature-dashboard
“Split the validation helpers out of that commit”git loom split pop -m "Add validation helpers" src/validation.rs
“Pull main and rebase everything”git loom update -y
“Open a PR for feature-auth”git loom push feature-auth

The agent names files explicitly, so unrelated edits in your working tree stay out of its commits. It always passes a message with -m, and it pushes only when you ask, because a push is visible to others. Its update -y also removes local branches that were merged upstream or deleted on the remote.

3. Loom never guesses

The agent spells out every choice — which branch, which files — from what you asked. When a command still leaves a question open, loom doesn’t guess or wait at a prompt: it answers with the question, changing no history, and the agent brings it to you. Here the agent left out the branch:

$ git loom commit --agent -m "Limit user name length" src/auth.rs
{"status":"needs_input","kind":"select","prompt":"Select target branch",
 "options":["feature-auth","feature-dashboard"],"allow_other":true,
 "hint":"re-run with: loom commit -b <branch> -m <message> [files...] (a new name creates the branch), or -l for a loose commit on the integration branch"}

The agent asks you which branch, then re-runs the command with your answer.

4. Let it pick hunks

When one file holds two unrelated changes, the agent uses -p as you would. Instead of opening the hunk picker, loom lists the hunks:

$ git loom commit --agent -b feature-auth -m "Trim user names" -p src/auth.rs
{"status":"needs_input","kind":"multiselect","prompt":"Select hunks",
 "options":["src/auth.rs:1","src/auth.rs:2"],"fingerprint":"3d12f8f83f89",
 "items":[{"id":"src/auth.rs:1","path":"src/auth.rs","diff":"@@ -1,5 +1,5 @@\n..."},
          {"id":"src/auth.rs:2","path":"src/auth.rs","diff":"@@ -15,6 +15,6 @@\n..."}],
 "hint":"re-run with: loom commit -b feature-auth -m 'Trim user names' -p src/auth.rs --hunks <id> [--hunks <id>...] --hunks-from 3d12f8f83f89"}

The agent reads the diffs, picks, and re-runs:

$ git loom commit --agent -b feature-auth -m "Trim user names" -p src/auth.rs --hunks src/auth.rs:1 --hunks-from 3d12f8f83f89
{"status":"ok","messages":["Applied 1 change(s) across 1 file(s)","Created commit `hug` (c420bf8) on branch `feature-auth`"]}

If the file changes between the two calls, the fingerprint no longer matches and loom refuses the selection rather than commit the wrong hunk.

5. Conflicts

A conflict pauses the operation, as it does for you, and the agent can’t mistake it for success:

$ git loom update --agent -y
{"status":"paused","message":"Conflicts detected — the `loom update` is paused",
 "hint":"resolve conflicts, stage them, then run: loom continue (or loom abort)",
 "messages":["Fetched latest changes"]}

The agent resolves the files, stages them with git add, and runs git loom continue — or git loom abort if you’d rather back out. Other loom commands are refused until then.

6. Keep the skill current

The skill ships inside the loom binary. After a loom upgrade the installed copy is out of date, and loom tells the agent so on its next command:

The Claude git-loom skill at `/home/me/.claude/skills/git-loom/SKILL.md` differs from the one this loom ships. Run `git-loom agent install` to refresh it (local edits are overwritten). Restart Claude Code to pick up the new skill.

Run the install again (the agent can do it), then restart Claude Code. Don’t edit the installed skill: loom reports any change as out of date, and the next install overwrites it.

Other agents and scripts

agent install supports Claude Code only. Any other agent, or your own script, can drive loom through agent mode: add --agent to every command, or set LOOM_AGENT=1.

  • Read the last line of stdout: it is always one JSON object with a status, except for a malformed command line (exit 2). Ignore stderr; it is for people.
  • ok (exit 0) succeeded. needs_input and needs_confirmation (exit 10) changed no history and give the command to re-run. paused (exit 0) is a conflict, not a success. error (exit 1) failed.
  • git loom status --agent returns the whole branch graph, with every short ID.

See agent for the full contract.

Amending a Past Commit

The login form is missing a CSRF token. You fix src/auth.rs:

╭─ zz [local changes]
│   au  M src/auth.rs
│
│╭─ fa [feature-auth]
│●    hug  81356bf add password validation
│●    pop  a337eda add login form
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit

The fix belongs in “add login form” (pop), not in a new commit. Fold it in:

git loom fold src/auth.rs pop

pop now includes the fix; hug is replayed on top of it. Other ways to say what to fold:

git loom fold au pop                     # the file by short ID
git loom fold zz pop                     # all local changes
git add src/auth.rs && git loom fold pop # only what is staged

To fold only some of a file’s changes, use fold -p. To let loom find the right commit for each change, use absorb.

Tip

git loom status -f pop lists the files in a commit.

See also: fold reference

Fixing Up a Commit

“add password validation” (hug) is really part of “add login form” (pop):

│╭─ fa [feature-auth]
│●    hug  2b48f49 add password validation
│●    pop  bf7e5af add login form
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit

Fold the newer commit into the older one:

git loom fold hug pop
│╭─ fa [feature-auth]
│●    pop  9dff7bd add login form
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit

hug is gone and its changes are in pop, which keeps its message. The source must be newer than the target. To fix up only some of hug’s hunks, use git loom fold -p hug pop.

See also: fold reference

Splitting a Commit

“add login form” (pop) also brought in validation helpers that deserve their own commit:

git loom status -f pop
│╭─ fa [feature-auth]
│●    pop  291658f add login form
│┊      pop:0 A  src/auth.rs
│┊      pop:1 A  src/validation.rs
│┊      pop:2 A  templates/login.html
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit

Split it, picking the files for the first commit:

git loom split pop -m "add validation helpers"
# ? Select files for the first commit
# > [x] src/validation.rs
#   [ ] src/auth.rs
#   [ ] templates/login.html
│╭─ fa [feature-auth]
│●    pop  5e1a9c2 add login form
│●    cup  8c2b2fa add validation helpers
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit

The files you picked form the first commit, with the new message. The rest stay in the second, which keeps the original message and its short ID pop.

To skip the picker, name the files: git loom split pop -m "add validation helpers" src/validation.rs. To split within a file, or a commit that touches a single file, pick hunks with git loom split -p pop. Without -m, your editor opens 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]
│●    dog  24a86e6 add dashboard layout
├╯
│
│╭─ fa [feature-auth]
│●    sun  6395f01 add logging helper
│●    pop  64518a4 add login form
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit

Fold the commit into the branch:

git loom fold sun fd
│╭─ fd [feature-dashboard]
│●    sun  db04256 add logging helper
│●    dog  24a86e6 add dashboard layout
├╯
│
│╭─ fa [feature-auth]
│●    pop  64518a4 add login form
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit

The commit lands on top of the branch. Some variations:

git loom fold sun pop fd            # move several commits at once
git loom fold sun --below dog       # land right under dog instead of on top
git loom fold -c sun feature-logging   # move it into a new branch

See also: fold reference

Moving Files Between Commits

A commit can hold a file that belongs in another commit. git loom status -f gives each file of each commit an ID such as pop:1:

git loom status -f
│╭─ fd [feature-dashboard]
│●    dog  147aa31 add dashboard layout
│┊      dog:0 A  src/dashboard.rs
│┊      dog:1 A  templates/dashboard.html
├╯
│
│╭─ fa [feature-auth]
│●    pop  c32bc09 add login form
│┊      pop:0 M  src/auth.rs
│┊      pop:1 A  templates/login.html
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit

templates/login.html (pop:1) belongs with the dashboard. Move it:

git loom fold pop:1 dog
│╭─ fd [feature-dashboard]
│●    dog  a3b2ef8 add dashboard layout
│┊      dog:0 A  src/dashboard.rs
│┊      dog:1 A  templates/dashboard.html
│┊      dog:2 A  templates/login.html
├╯
│
│╭─ fa [feature-auth]
│●    pop  cb15064 add login form
│┊      pop:0 M  src/auth.rs
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit

Pass commit IDs to -f (git loom status -f pop dog) to list only their files.

See also: fold reference

Uncommitting Changes

To rework a commit, or take a file out of one, fold it into zz, your local changes:

git loom status -f
│╭─ fa [feature-auth]
│●    hug  92b8427 add password validation
│┊      hug:0 M  src/auth.rs
│●    pop  bae0b72 add login form
│┊      pop:0 A  src/auth.rs
│┊      pop:1 A  templates/login.html
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit

A Whole Commit

“add password validation” (hug) was premature:

git loom fold hug zz
╭─ zz [local changes]
│   au  M src/auth.rs
│
│╭─ fa [feature-auth]
│●    pop  bae0b72 add login form
│┊      pop:0 A  src/auth.rs
│┊      pop:1 A  templates/login.html
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit

The commit is gone and its changes are back in the working tree, unstaged.

A Single File

Instead, take only templates/login.html (pop:1) out of pop:

git loom fold pop:1 zz
╭─ zz [local changes]
│   lo  ⁕ templates/login.html
│
│╭─ fa [feature-auth]
│●    hug  2a660a7 add password validation
│┊      hug:0 M  src/auth.rs
│●    pop  4afdd0c add login form
│┊      pop:0 A  src/auth.rs
├╯
│
● a1b2c3d (upstream) [origin/main] Latest upstream commit

The commit added the file, so it comes back untracked. For individual hunks, use git loom fold -p pop zz.

See also: fold reference

Auto-absorbing Changes

You’ve made small fixes across several files: a typo in src/auth.rs, a layout tweak in templates/dashboard.html. Each belongs in a different commit. Instead of folding them one by one, let loom work out where each goes:

git loom absorb -n
#   src/auth.rs -> d0472f9 "add login form" (feature-auth)
#   templates/dashboard.html -> 2ee61e1 "add dashboard layout" (feature-dashboard)
#   src/shared.rs [hunk 1/2] -> d0472f9 "add login form" (feature-auth)
#   src/shared.rs [hunk 2/2] -- skipped (pure addition)
# Dry run: would absorb 3 hunk(s) from 3 file(s) into 2 commit(s)

-n shows the plan without changing anything. Each hunk goes to the commit that last changed the lines it touches, so one file can feed several commits. When the plan looks right, run it:

git loom absorb

Hunks without a clear target stay in the working tree. Newly added lines, in particular, have no history to trace: fold them yourself. To absorb only some files, name them: git loom absorb src/auth.rs.

See also: absorb reference

Selecting Hunks with -p

To stage, commit, fold or split part of a file rather than all of it, add -p (patch). An interactive picker opens before the command runs. It works with add, commit, fold and split.

The Picker

╭─ 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     ││                                │
╰──────────────────╯╰────────────────────────────────╯
 Toggle: space | Confirm: Enter | Help: ? | Cancel: Esc

Files are on the left, their hunks on the right. Check the hunks you want, then confirm. A binary, deleted or empty file, or a submodule, has no hunks to choose from: it shows as one Whole file entry.

KeyAction
↑/k, ↓/jMove
Tab / Shift+TabSwitch pane
SpaceToggle a hunk, or every hunk of a file or directory
EnterConfirm
q / Esc / Ctrl+CCancel, changing nothing
?Help

Commands

CommandPicks fromResult
git loom add -p [<files>]Local changesSelected hunks staged, the others unstaged
git loom commit -b <branch> -p [<files>]Local changesSelected hunks committed to the branch
git loom fold -p [<files>] <commit>Local changesSelected hunks amended into the commit
git loom fold -p <source> <target>A commitSelected hunks moved into an older commit
git loom fold -p <commit> zzA commitSelected hunks uncommitted
git loom split -p <commit>A commitSelected hunks become the first of two commits

Listing files narrows the picker to them. Hunks you leave out are untouched, except with add -p, where unticking a staged hunk unstages it.

Example: Two Changes in One File

You edited src/auth.rs for two features. Send each hunk to its own branch:

git loom commit -b feature-auth -p src/auth.rs -m "tighten auth check"
# Pick the first hunk

git loom commit -b feature-ui -p src/auth.rs -m "restyle auth form"
# Pick the remaining hunk

See also: add · commit · fold · split

Pushing for Review

feature-auth is ready for review. Push it:

git loom push fa
# ✓ Pushed `feature-auth` to `origin`
# ✓ PR created: https://github.com/owner/repo/pull/42

Loom detects the forge and opens the review there: a pull request on GitHub (with gh) or Azure DevOps (with az), a merge request on GitLab, a review on Gerrit. Push again after changing the branch and the same review is updated. Without gh or az, loom prints a link to open the pull request yourself.

On GitHub and Azure DevOps, a branch with one commit takes its title from that commit. For a branch with several, loom asks for a title, or pass it with --title.

If feature-auth is stacked on another branch, the branch below is pushed too, and each gets its own review targeting the one below it. A stack needs gh on GitHub, and Azure DevOps has no stacked pull requests: both refuse unless you pass --no-pr.

To push without opening a review, for a backup or work in progress:

git loom push fa --no-pr

Without a branch argument, loom asks which one to push.

See also: push reference

Resolving Conflicts

When a loom command rewrites history and two changes touch the same lines, git can’t combine them on its own. Most commands then pause, so you can resolve the conflict and carry on. Nothing is lost either way.

1. The Pause

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

Until you continue or abort, most loom commands are refused; show, diff, trace and tui still work.

2. Find the Conflicts

git status lists them under Unmerged paths, and git loom tui marks them !!. git diff shows the conflicts inline.

3. Resolve Each File

Git marks both versions in the file:

<<<<<<< HEAD
// existing middleware code
=======
// your new auth middleware
>>>>>>> 1a2b3c4 (add auth middleware)

Edit the file to keep what you want, and remove the <<<<<<<, ======= and >>>>>>> lines. Most editors have buttons for this, or run git mergetool.

4. Mark Them Resolved

Stage each resolved file with plain git:

git add src/middleware.rs

Use git rm for a file the resolution deletes.

5. Continue or Abort

git loom continue
# ✓ Created commit `pop` (b2c3d4e) on branch `feature-auth`

Loom finishes the command as if nothing had happened, staged changes included. If a later commit conflicts too, it pauses again: repeat steps 2 to 5.

To give up instead:

git loom abort
# ✓ Aborted `loom commit` and restored original state

Everything goes back to how it was before the command: history, branches, staged and unstaged changes.

You can also resolve with the TUI, which opens on a paused operation.

Commands That Don’t Pause

split, branch, unmerge, dropping a branch, and some fold forms don’t pause: on a conflict they roll back and leave everything as it was. See continue for the full list.

When Things Go Off Script

  • You finished the rebase with git: if you ran git rebase --continue to the end, git loom continue still completes the loom command.
  • Your resolution left a commit with no changes: git drops that commit, and the command still reports success as if it had survived; git reflog still has it. A change you were folding or absorbing is not lost this way: it still lands, or comes back to your working tree, and loom says so — as long as you resolved with git checkout HEAD -- <file> and git add rather than git reset --hard or a commit of your own.
  • Changes that can’t be put back: if git couldn’t replay its stash, your changes wait in git stash list. If your staged changes no longer apply, loom saves them as a patch and prints the git apply command that restores them.
  • Loom says an operation is paused but no rebase is in progress: run git loom continue if you finished it yourself, or git loom abort to discard it. Never delete the paused-operation state by hand: it is what abort uses to restore your changes.

See also: continue · abort

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
  agent             Install the loom skill for AI agents

Staging:
  add               Stage files using short IDs or paths [-p for interactive hunks]

Commits:
  commit, ci        Create a commit on a feature branch [-p for interactive hunks]
  fold              Amend, fixup, or move commits [-p for interactive hunks] [amend, fixup, mv, rub]
  absorb            Auto-distribute changes into originating commits
  split             Split a commit into two [-p for interactive hunks]
  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        Create a feature branch
  merge             Merge an existing branch into integration
  unmerge           Remove a branch from integration (keeps the ref)
  switch, sw        Switch to any branch for testing (without merging)
  worktree, wt      Manage worktrees, each with its own integration branch (list by default, new, drop, path)

Inspection:
  status            Show the branch-aware status (default command)
  tui               Interactive status TUI (tree + diff, with actions)
  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, cont    Resume a paused operation after resolving conflicts
  abort             Cancel a paused operation and restore original state

Options:
      --no-color       Disable colored output
      --agent          Machine-readable JSON status output for AI agents (see also LOOM_AGENT)
      --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

git loom with no command runs git loom status.

Wherever a command takes a commit, branch or file, you can give its short ID from git loom status, as well as a hash, branch name or path.

Passing Options to Git

show, diff, add and commit each wrap one git command. Anything after a -- separator goes to it untouched:

git loom show -- --stat
git loom diff pop..hug -- --name-only
git loom commit -m "wip" -- --no-verify
git loom add zz -- -f

fold, split, absorb and reword accept them too, for the commits they create. The other commands take none.

Before the --, loom accepts only its own options: an unknown one is an error that tells you to move it after the separator. A flag means loom’s option on one side and git’s on the other: git loom diff -a is loom’s --all, git loom diff -- -a is git’s --text.

Warning

-- is expert mode: you can shoot yourself in the foot. Loom passes the options to git as written and checks nothing. An option that changes what git commits or how — -a, a pathspec, -m, --amend, --dry-run — does exactly that, even in the middle of a loom command that makes several commits, and loom carries on with whatever git did. Use it only when you know what the option does to that git command.

init

Create an integration branch tracking a remote branch, and switch to it. This is where a git-loom workflow starts.

Usage

git loom init [<name>]

Arguments

ArgumentDescription
<name>Branch name. Default: integration, or integration-<name> in a linked worktree.

What It Does

Creates the branch at the tip of the upstream it finds, sets it to track that upstream, and checks it out. The upstream is, in order:

  1. On GitHub, the default branch of a remote named upstream (the fork workflow)
  2. The upstream of the branch you are on, e.g. origin/main when you are on main
  3. In a linked worktree, the upstream of the main checkout’s branch
  4. Each remote’s default branch, else its main, master or develop, asking you to choose when there are several

In a linked worktree, the default name comes from the worktree’s directory: repo-foo beside repo gives integration-foo, any other directory integration-<directory>.

GitHub forks

When origin is a GitHub fork and no remote points at the original repository, init asks gh for it and offers to add it as upstream, fetch it and track it. That’s the setup gh repo clone makes for a fork, so git loom push then pushes to your fork and opens pull requests on the original. Without gh, nothing changes. In agent mode, or when nobody can answer, init keeps tracking origin and prints the commands that switch to upstream.

Examples

git loom init
# ✓ Initialized integration branch `integration` tracking `origin/main`

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

On a GitHub fork:

git loom init
#   › Adds `upstream` (git@github.com:cli/cli.git) and fetches it
# ? `origin` is a fork of `cli/cli`: track it as remote `upstream`? Yes
# ✓ Added remote `upstream` for `cli/cli`
# ✓ Initialized integration branch `integration` tracking `upstream/trunk`

Prerequisites

  • A remote with at least one branch
  • No existing branch with the same name

update

Fetch upstream, rebase your integration branch and all its feature branches onto it, and update submodules. With a branch name, take that branch’s remote version first.

Alias: up

Usage

git loom update [<branch>] [-y]

Arguments

ArgumentDescription
<branch>A woven branch to update from its remote branch, by name or short ID

Options

OptionDescription
-y, --yesRemove merged and gone branches without asking

What It Does

  1. Fetches the tracked remote, tags included. In a fork setup, the remote you push to is fetched too, and so is a named <branch>’s remote branch when it lives on yet another remote.
  2. Takes the remote version of <branch>, when one is named: its commits are replaced by those of the branch it tracks, whatever the local branch had. The message names the tip it had, so you can bring it back.
  3. Drops commits that already landed upstream, merged or cherry-picked.
  4. Rebases every feature branch onto the new upstream, keeping the integration layout. Uncommitted changes are kept, staged ones staged.
  5. Updates submodules, if any.
  6. Offers to remove local branches that were fully merged upstream, or whose remote branch was deleted. Set loom.pruneGoneBranches to always remove them without asking.

An integration branch holding an octopus merge (several branches merged at once) is still updated, by git’s own rebase, with a warning: branches already merged upstream are not offered for removal, and a named branch is refused.

Examples

git loom update
# ✓ Fetched latest changes
# ✓ Rebased onto upstream
# ✓ Updated branch `integration` with `origin/main` (abc1234 Latest commit)
#   › `feature-x`: fully merged upstream
#   › `feature-y`: upstream gone
# ? Remove 2 local branches? [y/N] y
# ✓ Removed branch `feature-x` (was 4f825d2)
# ✓ Removed branch `feature-y` (was 711d082)

A removed branch’s message gives its last commit, so you can bring it back with git branch <name> <commit>. A branch that can’t be deleted, such as one checked out in another worktree, is skipped with a warning.

A branch you pushed and then advanced elsewhere, from another machine, a colleague’s push or an update to your pull request, is refreshed by naming it:

git loom update feature-x
# ✓ Fetched latest changes
# ✓ Rebased onto upstream
# ✓ Updated branch `integration` with `origin/main` (abc1234 Latest commit)
# ✓ Updated branch `feature-x` with `origin/feature-x` (9c1e2f3 Second new commit, was 4f825d2)

Nothing is compared: commits the local branch had and the remote lacks go with the update, and was 4f825d2 is the way back: git loom unmerge feature-x, git branch -f feature-x 4f825d2, then git loom merge feature-x. A branch sharing commits with another one (stacked, or at the same tip), one whose remote holds a merge commit, one that is not woven, and one without a remote branch are refused before anything is rewritten. A branch whose remote version landed upstream whole is offered for removal instead.

Conflicts

If the rebase conflicts, the update 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
git add <resolved-files> && git loom continue
# ✓ Updated branch `integration` with `origin/main` (abc1234 Latest commit)

See continue and abort.

Prerequisites

  • On a branch with an upstream (see init)

push

Push a feature branch, with the branches it is stacked on, and open or update its pull request.

Alias: pr

Usage

git loom push [<branch>] [--no-pr] [-f] [--title <title>]

Arguments

ArgumentDescription
<branch>Branch name or short ID. A picker opens if omitted.

Options

OptionDescription
--no-prPush only: no pull request or Gerrit review
-f, --forceForce the push, even if the branch changed on the remote
--title <title>Title of the branch’s new pull request (see PR Title and Description)

What It Does

Pushes the branch safely: the push is refused if the remote branch has commits you haven’t seen. Then, depending on the forge:

ForgeDetected byPull requestStacked branchNeeds
GitHubgithub.com in the remote URLCreated or updatedOne PR per branch, linked into a GitHub stackgh
GitLabgitlab in the remote URLMerge request created or updatedOne MR per branch—
Azure DevOpsdev.azure.com in the remote URLCreated or updatedRefused (use --no-pr)az
GerritAn ssh:// URL on port 29418, or Gerrit’s commit-msg hookReview pushed to refs/for/<upstream>Gerrit chains the reviews—
Plain GitYour choice—Branches pushed—

When the forge isn’t detected (a GitLab server whose URL lacks gitlab, for instance), loom asks once and saves your answer in loom.remoteType. Forgejo, Gitea and Bitbucket work as plain Git; the link their server prints after a push is shown.

git loom push feature-a
# ✓ Pushed `feature-a` to `origin`
# ✓ PR created: https://github.com/owner/repo/pull/42

Without gh or az, the branch is still pushed, and loom prints a link to create the pull request in the browser. A stacked branch on GitHub needs gh.

Stacked Branches

A branch built on top of another one is stacked on it — what git loom status draws with ├─. Pushing it pushes what its review depends on. With d on c on b on a:

git loom push b
# ✓ Pushed `a`, `b` to `origin`
#   Re-pushed above `b`: `c`
# ! Not pushed above `b`: `d`
#   Run `loom push d` to publish them
  • a and b, the branch and everything below it, are pushed, each with its own pull request targeting the branch below. The bottom one targets your upstream branch.
  • c was pushed before and has changed since, so it is pushed again to keep the remote consistent.
  • d was never pushed, so it is left for you to push.

On GitHub the pull requests are linked into a stack, and a PR whose base is wrong is retargeted. In a fork, every PR targets the upstream branch instead, since GitHub can’t stack across forks. When the bottom PR merges, git loom update drops its commits and the stack shrinks.

To build a stack, start a branch on another with branch b --on a, or split a branch in two with branch part-1 --on <commit>.

Hidden branches are never pushed, nor is a branch stacked on one.

PR Title and Description

A new pull request is titled and described from its branch’s commits:

  • One commit: its subject and body.
  • Several commits: loom asks for a title, and the description lists every commit message. --title answers in advance.

--title applies to the branch you named, not to the branches below it. It works on GitHub, Azure DevOps and GitLab.

Change-Id trailers are left out of the description. On GitLab the server writes the description, so they stay there.

Forcing a Push

A forge that rebased your branch on the server (landing the pull request below it, for example) makes the next push fail. Loom suggests the way past it:

✗ git push failed
  › If `feature-a` has diverged on the remote, push again with `loom push feature-a -f`

Check what is on the remote first: -f overwrites it, for every branch of the stack. A Gerrit review push never forces.

Push Remote

Loom pushes to the remote your integration branch tracks. On GitHub, if that remote is upstream and an origin exists, it pushes to origin, your fork. Set loom.pushRemote for any other layout, and when your integration branch tracks a local branch, which is on no remote.

With several GitHub accounts logged in to gh, loom uses the first one that can push to the push remote: the account that owns your fork, or one that is a collaborator on a shared repository. Setting GH_TOKEN or GITHUB_TOKEN overrides that. This needs gh 2.81 or later; Linux distributions often ship an older one, so install it from cli.github.com.

Pushing Without a Pull Request

--no-pr pushes the branch and its stack (on Gerrit, the branch alone) without creating anything else — to back up work in progress, for example.

On Gerrit it pushes a real branch, which only an admin can delete. Unless the branch name starts with wip/, loom offers to push it as wip/<branch> instead:

? 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

Agent Mode

With --agent, the JSON ok line carries a push report:

{"status":"ok","messages":["…"],"push":{"remote":"origin","forge":"github","pushed":["a","b"],
 "prs":[{"branch":"a","base":"main","state":"created","url":"https://github.com/owner/repo/pull/41"},
        {"branch":"b","base":"a","state":"not_created","reason":"it has several commits and no `--title`",
         "create_url":"https://github.com/owner/repo/compare/a...b?expand=1","hint":"Re-run with `loom push b --title <title>`"}]}}

Each PR’s state is created, updated or not_created. A branch of several commits can’t be asked for a title, so its PR is not_created unless you pass --title. republished, not_pushed, links and the GitHub stack number appear when they apply.

Examples

git loom push feature-a                      # push and open or update the PR
git loom push feature-b --title "Login page" # several commits: give the PR a title
git loom push feature-a --no-pr              # push only
git loom push                                # pick the branch

Prerequisites

  • On an integration branch, with the branch woven into it

agent

Install the loom skill for an AI coding agent, and drive loom from agents or scripts with --agent.

For a walkthrough, see Working with AI Agents.

Usage

git loom agent install [<agent>] [--project]

Arguments

ArgumentDescription
<agent>Agent to install the skill for. Only claude (the default).

Options

OptionDescription
--projectInstall into the repository (.claude/skills/git-loom/SKILL.md) instead of your home directory

What It Does

Writes the skill to ~/.claude/skills/git-loom/SKILL.md, or into the repository with --project. The skill teaches the agent to use loom instead of raw git history rewrites.

Re-run it after upgrading loom: it reports whether the skill was installed, updated, or already up to date. The file belongs to loom, so local edits are overwritten.

In agent mode, every command checks the installed skills. One that differs from the skill this loom ships adds a message to the JSON output, telling the agent to run git-loom agent install and restart.

Agent Mode

The global --agent flag, or the LOOM_AGENT environment variable set to any non-empty value other than 0, makes loom machine-readable. It is never turned on automatically.

Output

  • stdout ends with exactly one JSON object on its last line. show, diff, trace, absorb, worktree list and worktree path print their own output before it; every other command prints only the JSON.
  • stderr carries the human output: progress lines and the status tree.
StatusExit codeMeaning
ok0Success. messages lists what happened.
needs_input10A prompt would have opened. No history changed (files you named may already be staged). options lists the choices (allow_other: true also accepts a new value) and hint the command to re-run.
needs_confirmation10A yes/no question would have opened. No history changed. Re-run with -y to confirm.
paused0A conflict paused the operation. Resolve it, then continue or abort.
error1The command failed; message says why.

A malformed command line exits with 2 and prints no JSON.

What Changes

  • Prompts never open: they answer needs_input or needs_confirmation. Two questions come after the work is done, so they take the safe default instead: update keeps branches it would offer to remove (pass -y to remove them), and push leaves a branch of several commits without a PR unless you pass --title.
  • commit, split and reword need -m; no editor opens.
  • -p lists hunks instead of opening the picker (see below).
  • show and diff never start a pager.
  • tui is unavailable.
  • status adds the branch graph to its JSON as graph (see status), and push adds a push report (see push).

Hunk Selection

Every -p form (add, commit, fold, split) takes two calls. The first lists the hunks and changes nothing:

git loom split pop --agent -m "Fix the off-by-one" -p
# {"status":"needs_input","kind":"multiselect","prompt":"Select hunks",
#  "options":["src/parse.rs:1","src/parse.rs:2"],"fingerprint":"a91c3f2be417",
#  "items":[{"id":"src/parse.rs:1","path":"src/parse.rs","diff":"@@ -12,7 +12,7 @@ fn scan\n..."},
#           {"id":"src/parse.rs:2", ...}],
#  "hint":"re-run with: loom split pop -m 'Fix the off-by-one' -p --hunks <id> [--hunks <id>...] --hunks-from a91c3f2be417"}

The second re-runs the hint with the chosen ids, one --hunks per id:

git loom split pop --agent -m "Fix the off-by-one" -p --hunks src/parse.rs:1 --hunks-from a91c3f2be417
# {"status":"ok","messages":["Split `b41c298` into `cup` (2a0a929) and `pop` (d979b2b)"]}
  • --hunks is the whole selection. Hunks already staged are listed with "staged": true: leaving one out unstages it with add -p, and keeps it staged but out of the commit with commit -p and fold -p.
  • A binary file, a deleted file and a submodule are a single entry that moves whole; its diff is a label such as (binary file). An untracked file is listed as (new file, <n> line(s)). Read the file at path for its content.
  • If the diff changed since the listing, the fingerprint no longer matches and loom refuses the selection. List again.
  • Keep every argument of the hint, including file filters: they shape the listing.

--hunks also works outside agent mode, but only a listing gives you the fingerprint.

Examples

Install the Claude skill

git loom agent install
# ✓ Installed Claude skill at `/home/me/.claude/skills/git-loom/SKILL.md`
#   › Restart Claude Code to pick up the new skill

Commit without choosing a branch

git loom commit --agent -m "Fix login"
# {"status":"needs_input","kind":"select","prompt":"Select target branch",
#  "options":["feature-auth","feature-ui"],"allow_other":true,
#  "hint":"re-run with: loom commit -b <branch> -m <message> [files...] (a new name creates the branch), or -l for a loose commit on the integration branch"}

git loom commit --agent -b feature-auth -m "Fix login"
# {"status":"ok","messages":["Created commit `pop` (1a2b3c4) on branch `feature-auth`"]}

A conflicting update

git loom update --agent -y
# {"status":"paused","message":"Conflicts detected — the `loom update` is paused",
#  "hint":"resolve conflicts, stage them, then run: loom continue (or loom abort)"}

git add src/auth.rs && git loom continue --agent
# {"status":"ok","messages":["Updated branch `integration` with `origin/main`"]}

add

Stage files using short IDs, paths, or zz for everything — or pick individual hunks.

Usage

git loom add [-p] [<files>...] [-- <git args>...]

With no files, add opens the hunk picker, as with -p.

Arguments

ArgumentDescription
<files>...File short IDs, paths, or zz for all changes. zz wins over any other argument.

Options

OptionDescription
-p, --patchPick hunks to stage or unstage in the hunk picker; files narrow it down
--hunks <id>Select that hunk instead of opening the picker; repeat per hunk. Needs -p and --hunks-from — see agent mode
--hunks-from <fingerprint>Fingerprint of the listing the --hunks ids came from

Git Options

Arguments after -- go to git add, unchecked — expert mode, see Passing Options to Git. Hunk picking takes none.

git loom add zz -- -f            # stage ignored files too
git loom add src/main.rs -- -N   # intent to add

What It Does

Staging files

Stages each named file as git add would.

Picking hunks (-p)

The picker shows staged and unstaged hunks: staged ones start selected. Toggle them either way, then confirm with Enter to apply everything at once. q or Esc cancels.

Examples

Stage files by short ID or path

git loom add ma src/lib.rs
# ✓ Staged 2 file(s)

Stage everything

git loom add zz
# ✓ Staged all changes

Pick hunks in one file

git loom add -p ma
# Opens the hunk picker on src/main.rs

Prerequisites

  • A working tree with changes

commit

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

Alias: ci

Usage

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

Arguments

ArgumentDescription
(none)Commit what is staged
zzStage and commit all changes; wins over any other file argument
<files>...File short IDs or paths: commit only these. Other staged files stay staged, out of the commit.

Options

OptionDescription
-b, --branch <branch>Target branch: a woven branch’s name or short ID, or a new name to create it. Prompts if omitted.
-l, --looseCommit on the integration branch itself, outside any feature branch
-m, --message <message>Commit message. Opens your editor if omitted.
-p, --patchPick the hunks to commit in the hunk picker; files narrow it down
--hunks <id>Select that hunk instead of opening the picker; repeat per hunk. Needs -p and --hunks-from — see agent mode
--hunks-from <fingerprint>Fingerprint of the listing the --hunks ids came from

Git Options

Arguments after -- go to git commit, unchecked — expert mode, see Passing Options to Git:

git loom commit -m "wip" -- --no-verify
git loom commit -m "fix" -- --signoff

What It Does

Creates the commit, then moves it to the top of the target branch. You stay on the integration branch.

Choosing the branch

  • -b <branch> — an existing woven branch receives the commit. A new name creates the branch at the upstream base and weaves it in. A branch that exists but is not woven is refused: merge it first.
  • No -b — a picker lists the woven branches, plus an option to create one. With no woven branch, loom asks for a name.
  • -l — a loose commit on the integration branch.

When the integration branch has the same name as its upstream (main tracking origin/main), a commit without -b is loose and no picker opens.

Picking hunks (-p)

Only the hunks you keep in the picker are committed. Everything else, staged or not, stays as it was.

A hook that fixes files

When a pre-commit hook fixes files and fails, as formatters run from the pre-commit framework do, loom stages the fixed files and commits again, once. It only stages a fixed file again if it was fully staged, so unstaged work never gets into the commit. It does not retry when git opens the editor (no -m) or takes options after -- outside agent mode, since you see the hook’s output then.

fold of files, hunks or staged changes retries too, options after -- or not. absorb does not, since a formatter rewrites whole files and the retry would put lines other commits own into one fixup: run the formatter yourself before absorbing.

git loom commit -b feature-auth -m "Check the token" src/auth.rs
# ! a commit hook changed `src/auth.rs`; committing again with its changes
# ✓ Created commit `hug` (4f825d2) on branch `feature-auth`

A change that is already upstream

If upstream already has everything the commit changes, loom undoes it instead of creating an empty commit. Your changes come back:

git loom commit -b feature-auth -m "Restore the check" src/auth.rs
# ✗ Commit `4783c1b` is redundant — the history below it already has its change
#   › The `loom commit` was rolled back, so there is nothing left to drop

Examples

Commit staged changes, picking the branch

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

Commit everything to a branch

git loom commit -b feature-auth -m "add password validation" zz
# ✓ Created commit `hug` (4f825d2) on branch `feature-auth`

Commit one file to a new branch

git loom commit -b feature-logging -m "add request logging" au
# ✓ Created branch `feature-logging` at `a1b2c3d`
# ✓ Created commit `sun` (6395f01) on branch `feature-logging`

Loose commit on the integration branch

git loom commit -l -m "bump integration config" zz

Commit some hunks of a file

git loom commit -b feature-auth -p au -m "fix auth check"
# Opens the hunk picker on src/auth.rs

Conflicts

If moving the commit onto its branch conflicts, the commit pauses:

git loom commit -b feature-auth -m "add auth" zz
# ! Conflicts detected — resolve them with git, then run:
#   › loom continue   to complete the commit
#   › loom abort      to cancel and restore original state
git add <resolved-files> && git loom continue
# ✓ Created commit `pop` (b4c5d6e) on branch `feature-auth`

git loom abort undoes the commit and puts your changes back as they were, staged ones staged. See continue and abort.

Prerequisites

  • On an integration branch (a branch with an upstream)
  • Something to commit

fold

Fold sources into a target: amend changes into a commit, fix up one commit into another, move commits or files between branches, or uncommit.

Aliases: amend, fixup, mv, rub

Usage

git loom fold <commit>
git loom fold <source>... <target>
git loom fold <commit>... --above <commit>
git loom fold <commit>... --below <commit>
git loom fold -c <commit>... <new-branch>
git loom fold -p [<files>...] <commit>
git loom fold -p <commit> <commit>
git loom fold -p <commit> zz

The last argument is the target and the others are sources. With only a target, the staged changes are folded into it. With --above/--below, the option names the target.

Options

OptionDescription
-c, --createCreate a new branch and move the source commits into it
--above <commit>Move the source commits directly above (after) this commit
--below <commit>Move the source commits directly below (before) this commit
-p, --patchPick hunks in the hunk picker instead of folding whole files or commits
--hunks <id>Select that hunk instead of opening the picker; repeat per hunk. Needs -p and --hunks-from — see agent mode
--hunks-from <fingerprint>Fingerprint of the listing the --hunks ids came from

Git Options

Folds that amend a commit with files or hunks pass arguments after -- to that git commit, unchecked — expert mode, see Passing Options to Git. Folds that move, fix up, or uncommit whole commits take none.

git loom fold src/auth.rs pop -- --no-verify

Type Dispatch

The action follows from what the arguments are:

SourceTargetAction
(staged changes)CommitAmend the staged changes into the commit
Files or zzCommitAmend those files (or all changes) into the commit
CommitCommitFix up: merge the source into the target
CommitBranchMove the commit to the top of the branch
Commit--above/--below commitMove the commit next to another commit
CommitNew branch (-c)Create a branch holding the commit
CommitzzUncommit: back to the working tree
Commit file (pop:1)CommitMove a file to another commit
Commit file (pop:1)zzUncommit a file

Commit files use the <commit>:<index> IDs shown by git loom status -f.

Actions

Amend a commit

git loom fold src/auth.rs pop     # amend one file
git loom fold zz pop              # amend all changes
git add src/auth.rs && git loom fold pop   # amend what is staged

Unstaged changes to the same files stay in the working tree when you fold only the staged ones.

When you amend with files, hunks or staged changes, a pre-commit hook that fixes files and fails gets one retry with its fixes, as in commit.

Fix up a commit

git loom fold hug pop
# hug's changes go into pop; hug disappears, pop keeps its message

The source must be newer than the target.

Move commits to a branch

git loom fold sun feature-dashboard
# ✓ Moved `sun` (was: 6395f01, now: db04256) to branch `feature-dashboard`

Name several commits to move them together; they keep their history order whatever order you list them in. The target can be a branch lower in a stack: the commit lands on its tip and the branches above are replayed on it.

A branch left with no commits stays, empty, at the base, and the result says so:

git loom fold pop feature-b
# ✓ Moved `pop` (was: a337eda, now: e1f2a3b) to branch `feature-b`
#   › branch feature-x now empty, at the base

Move a commit next to another

--above and --below place the commit right after or right before another commit, in the same branch (a reorder), another branch, or the integration line. “Above” reads as in git loom status, where newer commits are higher.

git loom fold dog --below pop     # reorder within a branch
git loom fold pop --above cup     # land right after cup, in cup's branch

Moving a commit above a branch tip makes it the branch’s new tip.

Create a branch from commits

git loom fold -c pop sun new-feature
# Creates new-feature holding pop and sun

The name must be new: -c refuses an existing branch, so a typo can’t drop your commits into another one.

Uncommit

git loom fold hat zz       # the whole commit
git loom fold hat:1 zz     # one file of it

The changes come back as unstaged changes (a new file as untracked). If they don’t apply cleanly — over uncommitted edits to the same files, for example — the fold fails and nothing changes. (After a loom continue, loom saves them to a patch file instead and says where.) A branch whose only commit this was stays, empty, at the base.

Move a file to another commit

git loom fold hug:1 cup
# The second file of hug moves to cup

Fold hunks (-p)

git loom fold -p pop                # working-tree hunks → amend pop
git loom fold -p src/auth.rs pop    # same, picking only from src/auth.rs
git loom fold -p hug cup            # hunks of hug → move into the older cup
git loom fold -p hat zz             # hunks of hat → uncommit

A binary file, a deleted file or a submodule is a single entry that moves whole.

Commits Already Upstream

If upstream already has everything a commit you’re folding from or into changes, loom refuses rather than fold into something that would vanish:

git loom fold pop feature-b
# ✗ Commit `a337eda` is redundant — the history below it already has its change
#   › Nothing was rewritten. Run `loom update` if it landed upstream, or `loom drop a337eda -y` to remove it now

Any other such commit is dropped along the way, and loom says so.

Conflicts

These folds pause on a conflict:

  • Amending a commit other than the last one, -p included
  • Fixing up a commit
  • Moving a single commit, to a branch or with --above/--below
  • Uncommitting a whole commit
git loom fold pop 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 `pop` (was: a337eda, now: e1f2a3b) to branch `feature-b`

Every other fold — moving several commits, -c, moving or uncommitting hunks with -p, and moving or uncommitting a single file — rolls back on a conflict and leaves everything as it was. See continue and abort.

absorb

Fold each uncommitted change into the commit that last touched the same lines.

Usage

git loom absorb [-n] [<files>...] [-- <git args>...]

Arguments

ArgumentDescription
<files>...File short IDs or paths to absorb (default: every changed tracked file)

Options

OptionDescription
-n, --dry-runShow where each change would go, without changing anything

Git Options

Arguments after -- go to the git commit behind each fixup, unchecked — expert mode, see Passing Options to Git:

git loom absorb -- --no-verify

What It Does

For each hunk, absorb finds the commit that last changed the lines it modifies or deletes, among the commits on your integration branch. Hunks of one file can go to different commits. All the fixups happen in one rewrite.

When a pre-commit hook fixes files and fails, absorb rolls back, the hook’s fixes included, and does not commit again: a formatter’s whole-file rewrite would land in one fixup, with lines other commits own. Run the formatter yourself first (e.g. pre-commit run --files <files>), then absorb: each fixed line goes to its own commit.

A hunk stays in the working tree when its target can’t be told:

  • it only adds lines, so there is nothing to trace back (use fold instead)
  • its lines come from several commits, or from upstream
  • the file is binary or new

Examples

Absorb all changes

git loom absorb
#   src/auth.rs -> a1b2c3d "Add authentication" (feature-auth)
#   src/shared.rs [hunk 1/2] -> a1b2c3d "Add authentication" (feature-auth)
#   src/shared.rs [hunk 2/2] -> d4e5f6a "Add dashboard" (feature-dashboard)
# ✓ Absorbed 3 hunk(s) from 2 file(s) into 2 commit(s)

Dry run

git loom absorb -n
#   src/auth.rs -> a1b2c3d "Add authentication" (feature-auth)
#   src/shared.rs -- skipped (pure addition)
# Dry run: would absorb 1 hunk(s) from 1 file(s) into 1 commit(s)

Restrict to some files

git loom absorb src/auth.rs src/utils.rs

Commits Already Upstream

If upstream already has everything a target commit changes, absorb refuses and leaves your changes in the working tree:

git loom absorb
# ✗ Commit `4783c1b` is redundant — the history below it already has its change
#   › Nothing was rewritten. Run `loom update` if it landed upstream

Conflicts

If the rewrite conflicts, absorb pauses:

git loom absorb
# ! 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.

Prerequisites

  • On an integration branch with uncommitted changes

split

Split a commit in two, choosing the files or hunks that go into the first one.

Usage

git loom split [-p] [-m <message>] <target> [<files>...] [-- <git args>...]

Arguments

ArgumentDescription
<target>The commit: short ID or hash
<files>...Files for the first commit; a picker opens if omitted. With -p, they narrow the hunk picker.

Options

OptionDescription
-m, --message <message>Message of the first commit. Opens your editor if omitted.
-p, --patchPick hunks for the first commit in the hunk picker
--hunks <id>Select that hunk instead of opening the picker; repeat per hunk. Needs -p and --hunks-from — see agent mode
--hunks-from <fingerprint>Fingerprint of the listing the --hunks ids came from

Git Options

Arguments after -- go to both git commits split makes, unchecked — expert mode, see Passing Options to Git:

git loom split pop -m "Extract config" config.rs -- --no-verify

What It Does

The files or hunks you pick become the first commit, with the new message. The rest stays in the second commit, which keeps the original message — and short ID, for a commit loom created.

  • By file (default): the commit must touch at least two files, and each side gets at least one.
  • By hunk (-p): works on a single-file commit too; leave at least one hunk for the second commit.

Examples

Pick the files

git loom split pop
# ? Select files for the first commit
# > [x] src/validation.rs
#   [ ] src/auth.rs
# (opens the editor for the first commit's message)
# ✓ Split `291658f` into `cup` (8c2b2fa) and `pop` (5e1a9c2)

Name the files

git loom split pop -m "add validation helpers" src/validation.rs

Pick hunks

git loom split -p pop -m "fix: extract bounds check"
# Opens the hunk picker on the commit's changes

Conflicts

split never pauses: if replaying the commits above the split conflicts, it rolls back and leaves everything as it was.

Prerequisites

  • The target is a commit on your integration branch, not a merge

swap

Swap the positions of two commits in the same branch, or both on the integration line.

Usage

git loom swap <a> <b>

Arguments

ArgumentDescription
<a>, <b>The two commits: short IDs, hashes, or any git revision. Branch names are refused.

What It Does

Exchanges the two commits and replays the commits above them. Both must sit in the same sequence: the same branch, or both loose on the integration line. To move a commit anywhere else, use fold --above/--below.

Examples

git loom swap hug pop
# ✓ Swapped commits `4f825d2` and `711d082`
git loom swap dog pop
# ✗ Cannot swap commits from different branch sections

Commits Already Upstream

If upstream already has everything one of the commits changes, swap refuses:

git loom swap hug pop
# ✗ Commit `4f825d2` is redundant — the history below it already has its change
#   › Nothing was rewritten. Run `loom update` if it landed upstream, or `loom drop 4f825d2 -y` to remove it now

Conflicts

If the replay conflicts, the swap pauses:

git loom swap hug pop
# ! 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 commits `4f825d2` and `711d082`

See continue and abort.

reword

Change a commit’s message, or rename a branch.

Alias: rw

Usage

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

Arguments

ArgumentDescription
<target>A commit (short ID or hash) or a branch (name or short ID)

Options

OptionDescription
-m, --message <message>The new message or branch name. Without it, your editor opens for a commit, a prompt for a branch.

Git Options

For a commit, arguments after -- go to the git commit --amend that rewrites it, unchecked — expert mode, see Passing Options to Git:

git loom reword pop -- --reset-author

What It Does

Commit

Replaces the message and replays the commits above it. Content and branches are unchanged, and so is the short ID of a commit loom created. On an integration branch, any commit loom status shows can be reworded; outside one, any commit in HEAD’s history with no merge between it and HEAD, the root included. A merge commit cannot be reworded.

Branch

Renames the branch. A name starting with the hidden prefix prints a warning, since the branch disappears from git loom status.

Target Resolution

  1. Branch names: rename the branch
  2. Git revisions: hashes, HEAD, … reword the commit
  3. Short IDs: a branch ID renames, a commit ID rewords

To reword the commit at a branch tip, name the commit, not the branch.

Examples

Reword a commit

git loom reword pop -m "Fix authentication bug in login flow"
# ✓ Updated commit message for `pop` (was: ab12cd3, now: e45f678)

Rename a branch

git loom reword fa -m feature-authentication
# ✓ Renamed branch `feature-auth` to `feature-authentication`

Commits Already Upstream

If upstream already has everything the commit changes, loom refuses rather than rewrite something else:

git loom reword pop
# ✗ Commit `4783c1b` is redundant — the history below it already has its change
#   › Nothing was rewritten. Run `loom update` if it landed upstream, or `loom drop 4783c1b -y` to remove it now

Conflicts

Rewording replays the merges above the commit. A merge whose conflicts you resolved by hand conflicts again, and the reword pauses:

git loom reword pop -m "Fix authentication bug"
# ! Conflicts detected — resolve them with git, then run:
#   › loom continue   to complete the reword
#   › loom abort      to cancel and restore original state
git add shared.rs && git loom continue
# ✓ Updated commit message for `pop` (was: ab12cd3, now: e45f678)

With git’s rerere enabled, your recorded resolution is replayed for you: stage it and continue. With rerere.autoUpdate too, the reword carries on by itself. See continue and abort.

drop

Drop a commit, a branch, or local changes.

Alias: rm

Usage

git loom drop [-y] <target>...

Arguments

ArgumentDescription
<target>...A commit, a branch, files or directories (short IDs, names, hashes or paths), or zz for all local changes. Several files can be dropped at once.

Options

OptionDescription
-y, --yesSkip the confirmation prompt

What It Does

Every drop asks for confirmation first, listing what it will remove; -y skips the question.

Commit

Removes the commit from history. A branch whose only commit it was is kept, empty, at the base, ready for a new git loom commit -b:

git loom drop pop
# Drop commit `4783c1b` Add login form, leaving branch `feature-a` empty? (y/N)
# ✓ Dropped commit `4783c1b`
#   › branch feature-a now empty, at the base

Branch

Removes the branch and the commits it owns, and takes it out of the integration branch:

git loom drop feature-a
# Drop branch `feature-a` and its 3 commits? (y/N)
# ✓ Dropped branch `feature-a` and its 3 commits

An empty branch is deleted without asking. When the commits are shared, only the branch goes:

  • Co-located branches (same tip): the commits stay with the other branch.
  • Stacked branches: dropping the lower branch keeps its commits on the branch above. Dropping the upper one keeps the lower branch.
git loom drop feat1
# Drop branch `feat1`, keeping its commits on `feat2`? (y/N)
# ✓ Dropped branch `feat1`, its commits stay on `feat2`

Files and directories

A modified file is restored to its committed version; a new or untracked file is deleted. A directory gets both: its changes are discarded and its untracked files deleted.

git loom drop ma untracked.txt
#   › restore `src/main.rs`
#   › delete `untracked.txt`
# Discard all selected changes and delete all selected files? (y/N)
# ✓ Restored `src/main.rs`
# ✓ Deleted `untracked.txt`

zz

Discards all local changes, staged or not, and deletes untracked files. Ignored files are kept.

Target Resolution

  1. Paths of changed files or directories
  2. Branch names
  3. Git revisions: hashes, HEAD, …
  4. Short IDs of branches, commits and files
  5. zz

Conflicts

Dropping a commit pauses on a conflict:

git loom drop pop
# ! 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 `4783c1b`

Dropping a branch rolls back on a conflict, leaving everything as it was. See continue and abort.

branch

Create a feature branch, empty or from existing commits.

Alias: br

To start a branch with a new commit, commit -b <name> is usually simpler. To bring in a branch that already exists, use merge.

Usage

git loom branch [<name>] [--on <branch|commit>]

Arguments

ArgumentDescription
<name>Name of the new branch. Prompts if omitted.

Options

OptionDescription
--on <branch|commit>Where the branch starts: a branch or commit, by name, short ID or hash. Default: the upstream base.

What It Does

Where the new branch starts depends on --on:

  • Nothing: an empty branch at the upstream base. Its first commit makes it a branch of its own.
  • A woven branch: an empty branch at that branch’s tip. Its first commit stacks on it.
  • A commit inside a branch: splits the branch in two. The new branch takes that commit and the ones below it; the existing branch keeps the commits above and is stacked on the new one.
  • A loose commit on the integration branch: the loose commits up to and including it become the new branch. --on HEAD takes all of them.

git loom branch <name> <commit> is refused: write --on <commit>.

A name starting with the hidden prefix prints a warning, since the branch won’t show in git loom status.

Target Resolution

  1. Branch names: the branch’s tip
  2. Git revisions: hashes, HEAD, HEAD~2, …
  3. Short IDs of branches and commits

Examples

Empty branch at the base

git loom branch feature-auth
# ✓ Created branch `feature-auth` at `abc1234`

Stack on another branch

git loom branch feature-b --on feature-a
# ✓ Created branch `feature-b` at `5c1d9e2`

Turn loose commits into a branch

git loom branch feature-a --on HEAD
# ✓ Created branch `feature-a` at `3e8b1c0`
# ✓ Woven `feature-a` into integration branch

Conflicts

If turning commits into a branch conflicts, branch rolls back and deletes the new branch, so you can retry with the same name.

Prerequisites

  • On an integration branch (a branch with an upstream), unless --on names a commit directly

merge

Weave an existing branch into the integration branch.

To take a branch back out, use unmerge. To create a new branch, use branch or commit -b.

Usage

git loom merge [<branch>] [--all]

Arguments

ArgumentDescription
<branch>A local branch, or a remote one such as origin/feature-x. A picker opens if omitted.

Options

OptionDescription
-a, --allAlso list remote branches that have no local branch

What It Does

Merges the branch so it gets its own section in git loom status. Merging a remote branch first creates a local branch tracking it.

A branch can be woven into only one integration branch at a time. merge refuses a branch already woven here or into another worktree’s integration branch, as well as a branch with nothing to merge. It also refuses while the working tree has unresolved conflicts, a staged submodule change, or a file removed from the index with git rm --cached but kept on disk, and when the branch deletes a file you changed or changes one you deleted, since your change could not be put back over it.

The merge is one transaction: your staged and unstaged changes are set aside, the branch is merged, then your changes are put back on top — staged ones staged again where possible.

Examples

Merge a branch

git loom merge feature-auth
# ✓ Merged `feature-auth` into integration branch

Pick one, remote branches included

git loom merge --all
# ? Select branch to merge ›
#   feature-auth
#   origin/feature-logging

Conflicts

If the merge conflicts, it pauses:

git loom 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
git add <resolved-files> && git loom continue
# ✓ Merged `feature-auth` into integration branch

If your uncommitted changes overlap what the merge brings in, putting them back conflicts and the merge pauses the same way. A binary file cannot show conflict markers: loom leaves your version in it, and git checkout HEAD -- <file> takes the merge’s instead.

loom abort at any point returns to where the merge started — the merge undone, your changes back as they were — and drops whatever was done in between.

See continue and abort.

unmerge

Take a branch out of the integration branch, keeping the branch itself.

Usage

git loom unmerge [<branch>]

Arguments

ArgumentDescription
<branch>A woven branch, by name or short ID. A picker opens if omitted.

What It Does

Removes the branch’s commits from the integration branch. The branch keeps them, so merge brings it back. To delete the branch and its commits instead, use drop.

  • Branches sharing the same tip leave together, and the result names each one.
  • A branch with another stacked on it is refused: unmerge the upper branch first.

Examples

git loom unmerge feature-auth
# ✓ Unmerged `feature-auth` from integration branch

Conflicts

If removing the commits conflicts, unmerge rolls back and leaves everything as it was.

switch

Check out any branch to test it on its own, without merging it.

Alias: sw

Usage

git loom switch [<branch>]

Arguments

ArgumentDescription
<branch>A local branch (name or short ID) or a remote one such as origin/feature-x. A picker opens if omitted.

What It Does

  • Local branch: checks it out, like git switch.
  • Remote branch with no local copy: checks out its commit on a detached HEAD, creating no local branch.

Nothing is rewritten. Switch back to your integration branch the same way.

Examples

git loom switch fx
# ✓ Switched to `feature-x`

git loom switch origin/colleague-work
# ✓ Detached HEAD at `origin/colleague-work`

git loom switch integration
# ✓ Switched to `integration`

Prerequisites

  • No staged changes or modified tracked files (untracked files are fine)

worktree

Work on several integration branches side by side, one per git worktree.

Alias: wt

Each worktree holds its own integration branch, tracking the same upstream. Every loom command works there as in the main checkout, and a paused operation stays private to its worktree.

Usage

git loom worktree new <name>
git loom worktree [list]
git loom worktree drop <worktree>
git loom worktree path [<worktree>]

worktree alone runs list. list has the alias ls, and drop the alias rm.

Layout

For a main checkout in repo, the worktree hotfix lives beside it in repo-hotfix:

repo/          integration          (tracks origin/main)
repo-hotfix/   integration-hotfix   (tracks origin/main)

A worktree made with git worktree add works too: run git loom init inside it.

One Integration Branch per Feature Branch

A feature branch is woven into one integration branch at a time, so merge refuses a branch woven into another worktree. To move it, unmerge it there first:

cd ../repo-hotfix && git loom unmerge feature-x
cd ../repo        && git loom merge feature-x

worktree new

Creates <repo>-<name> beside the main checkout, on a new integration-<name> branch tracking the main checkout’s upstream.

git loom wt new hotfix
# ✓ Created worktree /src/repo-hotfix on integration-hotfix tracking origin/main

worktree list

Lists the worktrees, main checkout first, with their short IDs. * marks the one you are in, dirty one with uncommitted changes.

git loom wt
# 🏠 re   repo   [integration] /src/repo *
# 🔗 ho   hotfix [integration-hotfix] /src/repo-hotfix dirty

worktree drop

Removes a worktree, named by short ID, name, branch or path. Its integration branch is deleted too, unless it holds commits found nowhere else; then it is kept, with a warning. Woven feature branches stay.

drop refuses the main checkout, the worktree you are in, and a worktree with uncommitted changes or a paused operation. Ignored files are deleted with the worktree.

git loom wt drop ho
# ✓ Removed worktree /src/repo-hotfix
# ✓ Deleted branch integration-hotfix

worktree path

Prints a worktree’s path, for cd. Without an argument it prints the main checkout’s path from a linked worktree, and opens a picker from the main checkout.

cd "$(git loom wt path hotfix)"   # bash, zsh
cd (git loom wt path)             # fish, PowerShell
for /f "delims=" %p in ('git loom wt path hotfix') do cd /d "%p"

status

Show the branch-aware status: local changes, then your branches and their commits. This is the default command, so git loom alone runs it.

Usage

git loom [status] [-f [<commit>...]] [-n <N>] [-a]

Options

OptionDescription
-f, --files [<commit>...]Show the files changed in each commit, or only in the commits named
-n, --context <N>Show N-1 commits before the base, dimmed (default: loom.statusContext, else 1)
-a, --allAlso show hidden branches

Output

╭─ zz [local changes]
│   ma  M src/main.rs
│   nf A  new_file.rs
│   un  ⁕ untracked.txt
│
│╭─ fb [feature-b] ✓
│●    pop  d0472f9 Fix bug in feature B
│●    hug  7a067a9 Start feature B
├╯
│
│╭─ fa [feature-a] ↑
│●    dog  2ee61e1 Add feature A
├╯
│
● ff1b247 (upstream) [origin/main] Initial commit

From top to bottom:

  1. Local changes (zz): conflicted files (!!), then changed files with their git status --short code, then untracked files (⁕). Shows no changes when the working tree is clean.
  2. Loose commits: commits on the integration branch that belong to no feature branch.
  3. Feature branches: empty ones first, then each branch with its commits, newest first.
  4. Upstream: the commit your branches are based on.

Symbols

SymbolMeaning
●A commit
├─Next branch of a stack, or another branch at the same tip
!!Conflicted file
⁕Untracked file
✓Branch is pushed and up to date
↑Branch differs from its pushed version
✗Branch’s remote was deleted
⏫Upstream has new commits; run update
·Context commit before the base (-n)

Short IDs

Every branch, commit and file gets a short ID — what you see is what you type in other commands. zz always means all local changes.

  • Commits made by loom get a persistent ID of three or more letters, such as pop. It comes from the commit’s Change-Id trailer, so it survives every rewrite. IDs alternate a consonant from c d f g h m n p r s t w y and a vowel from a e i o u, so every ID can be said aloud and many are words (pop, sun, dog); the letters were picked to spell short words, with look-alikes left out. Other commits get a hash prefix, such as 3a, which changes when the commit is rewritten. In the rare case where an ID of four or more letters is also a prefix of some object’s hash, loom refuses the ambiguous argument instead of guessing; give the full hash or the Change-Id.
  • IDs are as short as they can be while staying unique. When a new commit shares an ID’s first letters, both IDs grow, and the shorter form stops working rather than point at the wrong commit.
  • Any longer prefix of a letter ID works too; a hash ID works from four characters, as in git.

Showing Files

git loom status -f dog
│╭─ fa [feature-a]
│●    dog  2ee61e1 Add feature A
│┊      dog:0 M  src/feature.rs
│┊      dog:1 A  tests/feature_test.rs
├╯

Commit files get IDs such as dog:0, which fold accepts to move or uncommit a single file.

Branch Layouts

│╭─ fb [feature-b]        │╭─ fv [feature-a-v2]     │╭─ fs [feature-stale]
│●    pop  4e046ab B      │├─ fa [feature-a]        ├╯
││                        │●    dog  2ee61e1 A      │
│├─ fa [feature-a]        ├╯
│●    dog  caa87a9 A
├╯

From left to right: feature-b stacked on feature-a; two branches sharing one tip; an empty branch at the base.

When upstream has moved on since your base:

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

Hidden Branches

Branches starting with loom.hideBranchPattern (default local-) are hidden, commits included — handy for local-only configuration. Every other command still accepts them. --all shows them.

Agent Mode

With --agent, the tree goes to stderr and stdout gets the same graph as JSON, under graph:

{"status":"ok","graph":{
  "schema": 1,
  "integration_branch": "integration",
  "cwd_prefix": "",
  "local_changes": {"id": "zz", "files": [
    {"id":"ma","path":"src/main.rs","index":"M","worktree":" ","state":"tracked"}]},
  "branches": [
    {"names": [{"id":"fu","name":"feature-ui","remote":null}],
     "stacked_on": "feature-api", "stacked_on_hidden": false,
     "commits": [{"id":"sun","hash":"9c1d044","oid":"9c1d0448…",
                  "subject":"feat(ui): settings panel","change_id":"I9c1d…","files":[]}]}
  ],
  "loose_commits": [],
  "upstream": {"label":"origin/main","base_hash":"7f3e9b0","base_oid":"7f3e9b0c…",
               "base_subject":"chore(release): 2.4.0","base_date":"2026-09-14","commits_ahead":0},
  "context_commits": []
}}
FieldMeaning
schemaBumped on any breaking change to this format, possibly in a minor release (see Compatibility)
cwd_prefixThe directory paths are relative to
branches[]In display order. names[] lists branches sharing one tip.
stacked_onThe branch below in a stack, or null
stacked_on_hiddenThe branch below is hidden: push refuses this branch
remotesynced, different, gone, or null if never pushed
commits[]Newest first; each branch lists only its own commits
stateconflicted, tracked or untracked; index and worktree hold the XY status code
commits[].filesFilled in with -f

Prerequisites

  • On a branch with an upstream

tui

An interactive, full-screen status: the branch tree on the left, the diff of the row under the cursor on the right, and loom’s actions one key away.

Usage

git loom tui

No arguments. The global --theme and --no-color options apply.

Layout

┌ Status ──────────────────────────────┐┌ Diff ─────────────────────────┐
│ ╭─ zz [local changes]                ││ diff --git a/src/main.rs ...  │
│ │   ma M  src/main.rs                ││ @@ -1,3 +1,4 @@               │
│ │╭─ fa [feature-a] ✓                 ││ +new line                     │
│ │●  pop  Add parser (2 files)        ││ ...                           │
│ ├╯                                   ││                               │
│ ●  9999999 (upstream) ...            ││                               │
└──────────────────────────────────────┘└───────────────────────────────┘
 Commit: c/C | Fold: f/F | Branch: b | Drop: d | Help: ? | Quit: q

The tree is the one git loom status -f shows, with the same short IDs. Commits start collapsed; open one to see its files.

KeyEffect
↑/k, ↓/jMove the cursor, or scroll the diff when it has focus
←/h, →/l, EnterClose / open the current row
TabSwitch focus between tree and diff
PgUp/PgDnScroll the diff by a page
Ctrl-← / Ctrl-→Narrow / widen the tree pane
MouseClick to focus and move, wheel to scroll
SpaceSelect the row (✓) and move down
+ (or =) / -One more / one fewer context commit before the base
R / F5Reload
LShow the action log
TShow the latest trace
?Show every key
EscCancel the current mode, else clear the selection. Never quits.
q / Ctrl-CQuit

Actions take the selected rows, or the row under the cursor when nothing is selected. A selection holds one kind of row: working files, commits, branches, commit files, or the [local changes] header.

Actions

KeyCommandWhat it does
ccommitCommit the selected files (or the [local changes] header for everything). The new commit appears in the tree: move it with ↑/↓ to the integration branch or any woven branch, then Enter. To commit to a new branch, create it with b first.
Cadd -p + commitPick hunks first, then place the commit as with c. What you pick is staged, so cancelling the placement leaves it staged.
ffoldFold the selection: move to a target with ↑/↓ — each tagged [AMEND], [MOVE] or [UNCOMMIT] — then Enter.
Ffold -pLike f, picking hunks first: working-tree hunks to amend into a commit, or a commit’s hunks to move or uncommit.
mfold --above/--belowMove a commit: ↑/↓ show it in each place it can go, Enter moves it there.
ssplitOn commit files: those files become the first commit. On a commit: like S.
Ssplit -pPick the hunks that become the first commit.
bbranchType the new branch’s name where it will appear: at the cursor commit or branch, else at the base.
aabsorbShow where each change would go, then absorb on confirmation.
ddropDrop the selected files, or the row under the cursor, after confirmation.
rrewordOn a commit: edit its message. On a branch: rename it in place.
MmergePick a branch to merge, local or remote.
UunmergeTake a branch out of the integration branch, after confirmation.
uupdateFetch and rebase onto upstream. On a pushed branch, a menu also offers to take the branch’s remote version, replacing its commits.
ppushPush a branch with its stack and open its pull request.
Ppush -fForce-push, after confirmation — for a push refused because the branch changed on the remote.

Actions run with the TUI still up. Questions appear as popups and menus, and only an editor (a commit message, a reword) takes over the terminal. When an action finishes, its result shows in the status bar, or an error popup opens, and the tree reloads. L shows each action’s full output.

Note

Git can’t ask for a password inside the TUI. If u or p need credentials, use a credential helper or ssh-agent, or run the command from a terminal once.

Conflicts

When an action pauses on a conflict — or when you open the TUI while an operation is paused — the tree gives way to the paused view. It lists the changed files, conflicts marked !!, and the diff pane shows conflict markers:

┌ Paused: loom fold — step 2/5 ────────┐┌ Diff ─────────────────────────┐
│ Conflicts — resolve them, stage each ││ <<<<<<< HEAD                  │
│                                      ││ old line                      │
│  !! src/main.rs                      ││ =======                       │
│  M  src/lib.rs                       ││ new line                      │
│                                      ││ >>>>>>> 1a2b3c4 Add parser    │
└──────────────────────────────────────┘└───────────────────────────────┘
 Continue: c | Abort: a | Stage: Space | Trace: T | Help: ? | Quit: q

Resolve the files in your editor, then:

KeyEffect
SpaceStage the file under the cursor and move to the next
cContinue
aAbort, after confirmation
R / F5Reload, after resolving outside the TUI

Prerequisites

  • On a branch with an upstream, as for status
  • An interactive terminal: the TUI is not available in agent mode

show

Show a commit’s message and diff, like git show, or every commit of a branch.

Alias: sh

Usage

git loom show [<target>] [-- <git args>...]

Arguments

ArgumentDescription
<target>A commit or a branch, by short ID, hash or name. Default: the commit at the top of git loom status.

Git Options

Arguments after -- go to git show — see Passing Options to Git:

git loom show pop -- --stat
git loom show pop -- -- src/main.rs

What It Does

  • A commit: shows it, as git show does.
  • A feature branch: shows every commit the branch owns, newest first — the commits git loom status lists under it. Hidden branches work too.
  • The integration branch: shows its loose commits.

Merge commits can’t be shown this way; use git show. A branch outside your integration branch shows only its tip commit.

Target Resolution

  1. Branch names
  2. Git revisions: hashes, HEAD, …
  3. Short IDs of branches and commits

Examples

git loom show pop          # one commit
git loom show feature-a    # every commit of the branch

Prerequisites

  • For short IDs and branches: on a branch with an upstream

diff

Show a diff, like git diff, naming files and commits by short ID.

Alias: di

Usage

git loom diff [<arg>...] [--staged | --all] [-- <git args>...]

Arguments

ArgumentDescription
<arg>...Files (short ID or path), commits (short ID or hash), or ranges such as pop..hug. Mix them freely.

Options

OptionDescription
--staged (alias --cached)Staged changes only
-a, --allStaged and unstaged changes together

Without either, diff shows unstaged changes, like git diff.

Git Options

Arguments after -- go to git diff — see Passing Options to Git:

git loom diff -- --stat
git loom diff pop..hug -- --name-only
git loom diff -- -a            # git's --text; loom's own -a is --all

What It Does

ArgumentsDiff
(none)Your unstaged changes
A fileThat file’s unstaged changes (or staged, or both, with --staged/--all)
A commitBetween that commit and your working tree
A range a..bBetween the two ends; each end can also be a branch, tag or HEAD
A commit and a fileThat file, between the commit and your working tree

A single target can’t be a branch name or a merge commit: use a range.

Examples

git loom diff --all          # everything not committed
git loom diff ma             # one file, by short ID
git loom diff pop            # since commit pop
git loom diff pop..hug       # between two commits
git loom diff pop ma         # one file, since commit pop

Prerequisites

  • For short IDs: on a branch with an upstream

trace

Show the git commands the last loom command ran — the first thing to look at when a command fails, and to attach to a bug report.

Usage

git loom trace

What It Does

Loom records each git command it runs — arguments, duration, failures, git’s error output, and the rebase steps it generated — for every command except show and diff, including actions run from the TUI. trace prints the latest record.

continue and abort add to the record of the command they resume, so the trace shows the whole operation.

The last 10 records are kept in .git/loom/logs/.

Example

git loom trace
# [2026-03-04 14:30:00.123] git loom fold hug pop
# ================================================================================
#
#   [git] rebase --interactive --autostash ...  [230ms]
#   [git] commit --amend --no-edit  [12ms] FAILED
#     [stderr]
# error: could not apply abc1234

continue

Resume a paused loom operation once its conflicts are resolved.

Alias: cont

Usage

git loom continue

What It Does

When a command hits a conflict, it pauses:

! Conflicts detected — resolve them with git, then run:
  › loom continue   to complete the commit
  › loom abort      to cancel and restore original state

Resolve each conflicting file, stage it with git add, then run git loom continue. Loom resumes the rewrite and finishes the command, printing the success line it would have printed. If another commit conflicts, it pauses again; repeat until done:

git add src/auth.rs && git loom continue
# ! Conflicts remain — resolve them and run `loom continue` again

git add src/shared.rs && git loom continue
# ✓ Created commit `pop` (a1b2c3d) on branch `feature-auth`

If your resolution leaves a commit with no changes, git drops it, and the command still reports success as if the commit had survived: commit names the commit below as the one it created. git reflog still has the dropped commit. A change you were folding or absorbing is not lost this way: see When Things Go Off Script.

If you already finished the rebase yourself with git rebase --continue, loom continue still completes the loom command.

Continuing a merge puts your set-aside uncommitted changes back over it; if they conflict, it pauses again for you to resolve them.

To give up instead, run abort. The conflict guide walks through a full resolution.

Which Commands Pause

Pause on a conflictRoll back on a conflict
updatesplit
commitbranch
absorbunmerge
swapdrop of a branch
mergefold moving several commits, -c, a commit file (pop:0), or a commit’s hunks (-p)
reword of a commit
drop of a commit
fold amending (-p included) or fixing up a commit, moving one commit, uncommitting a commit

A command that rolls back leaves the repository as it was before you ran it.

While Paused

Other loom commands are refused until you continue or abort. These still work: show, diff, trace, tui, worktree list, worktree path and agent install. Stage resolutions with plain git add.

Without a Paused Loom Operation

If git has a rebase or merge in progress that loom didn’t start, loom continue continues it:

git loom continue
# ✓ Completed the rebase git had in progress (no loom state, so nothing else was done)

With nothing in progress at all, it fails with No loom operation is in progress.

abort

Cancel a paused loom operation and put everything back as it was.

Usage

git loom abort

What It Does

Cancels the paused operation and restores the state from before the command: history, branches, staged changes and working-tree changes. Branches the command created are deleted.

git loom commit -b feature-auth -m "add auth" zz
# ! Conflicts detected...

git loom abort
# ✓ Aborted `loom commit` and restored original state

After aborting a commit, the changes you were committing are back as they were: staged ones staged, the rest unstaged.

After aborting a merge, the merge is undone and your uncommitted changes are back as they were, staging included. Anything you changed while it was paused is dropped.

If the abort can’t complete — another git process is holding the repository, for example — loom says so and keeps the paused operation, so you can run loom abort again.

Warning

Never delete the paused-operation state under .git/loom/ to get unstuck: it is what loom abort uses to restore your changes. If loom reports it as corrupted, move it aside and keep it — anything the command had set aside can then only be recovered from it by hand — and run loom abort if git still has a rebase in progress.

Without a Paused Loom Operation

If git has a rebase or merge in progress that loom didn’t start, loom abort cancels it:

git loom abort
# ✓ Canceled the rebase git had in progress (no loom state to roll back)

With nothing in progress at all, it fails with No loom operation is in progress.

See also continue.

Configuration

Git Config Settings

Set them with git config <setting> <value>.

SettingValuesDefaultDescription
loom.remoteTypegithub, gitlab, azure, gerrit, plainDetected, else askedHow git loom push pushes
loom.pushRemoteA remote nameDetectedThe remote git loom push pushes to
loom.hideBranchPatternA prefixlocal-Branches hidden from git loom status
loom.statusContextAn integer ≥ 11Commits shown at and before the base by status and tui
loom.pruneGoneBranchestrue, falsefalseLet git loom update remove merged and gone branches without asking
loom.changeIdtrue, falsetrueAdd a Change-Id trailer to the commits loom creates

loom.remoteType

git loom push detects the forge from the remote URL (github.com, gitlab, dev.azure.com, an ssh:// URL on Gerrit’s port 29418) or a Gerrit commit-msg hook. When it can’t tell, it asks. Either way the result is saved here; set it yourself if detection guessed wrong:

git config loom.remoteType gitlab

See push for what each type does.

loom.pushRemote

By default, git loom push pushes to the remote your integration branch tracks. On GitHub, if that remote is upstream and an origin exists, it pushes to origin, your fork. Set this for any other layout, and when your integration branch tracks a local branch, which is on no remote:

git config loom.pushRemote personal

loom.hideBranchPattern

Branches whose names start with this prefix are hidden from git loom status, commits included, unless you pass --all. Use it for local-only work such as personal configuration. Every other command still accepts them, but push never publishes them.

git config loom.hideBranchPattern "secret-"   # hide secret-* instead of local-*
git config loom.hideBranchPattern ""          # hide nothing

loom.statusContext

How much history to show at and before the base: 1 shows the base alone, 3 adds the two commits before it, dimmed. git loom status -n overrides it for one run, and +/- change it live in git loom tui.

loom.pruneGoneBranches

When true, git loom update removes branches that were fully merged upstream, or whose remote branch was deleted, without asking — as update -y does.

loom.changeId

Commits created by commit, split and reword get a Gerrit-style Change-Id trailer, which gives them short IDs that survive rewrites. Set it to false to create commits without one.

Gerrit’s own settings are honored: gerrit.createChangeId false also turns it off, and gerrit.reviewUrl switches to Gerrit’s Link: form. A commit that already has a Change-Id, from a commit-msg hook for example, keeps it.

Note

When you write the message in your editor, loom adds the trailer by amending the commit afterwards. That amend uses your git config, not options passed after --: a commit signed with a forwarded -S loses its signature. Use commit.gpgsign, or -m.

Environment Variables

VariableDescription
NO_COLORDisable colored output when set (see no-color.org)
TERMdumb disables colored output
LOOM_AGENTAny non-empty value other than 0 turns on agent mode, like --agent

Global Flags

These go before or after the command.

FlagDescription
--no-colorDisable colored output
--theme <auto|dark|light>Colors for a dark or light terminal background. auto (the default) detects it, and falls back to dark.
--agentMachine-readable output for AI agents and scripts — see agent mode

Compatibility

From 1.0, git-loom follows semantic versioning. What this page calls stable changes only in a major release; minor releases add features, and patch releases fix bugs.

Stable

WhatPromise
Commands and optionsEvery command, alias and option that --help or these docs show keeps its spelling and meaning. A renamed one keeps its old spelling, hidden, until the next major release.
ConfigurationThe loom.* settings and the values they accept, and the LOOM_AGENT variable.
Commit identityThe Change-Id trailer loom writes (Change-Id: I<40 hex>, or Gerrit’s Link: form), and the persistent ID spelled from it: a commit keeps the same letters across releases.
RequirementsGit 2.43 or later. A minor release can raise the minimum to a later Git 2.x; requiring Git 3 waits for a major release, git-loom 2.0 at the earliest.
Agent modeAs described below.

Agent Mode

Agent mode is read by tools, so it is stable like the CLI, except the status graph, which has its own version number.

Stable like the CLI:

  • --agent and LOOM_AGENT turn it on.
  • stdout ends with one JSON object, on its last line.
  • The status values (ok, needs_input, needs_confirmation, paused, error), their fields, and the exit codes 0, 1, 2 and 10.
  • The hunk listing that -p answers with (items, fingerprint), and re-running with --hunks and --hunks-from.
  • The push report.
  • How messages names a commit that a command created or rewrote: `pop` (1a2b3c4) and `pop` (was: 1a2b3c4, now: 5d6e7f8), or `1a2b3c4` and `1a2b3c4` (now `5d6e7f8`) for a commit without a Change-Id. The rest of a message’s wording can change.

Versioned by schema:

  • The graph object of git loom status --agent. Its schema number goes up when a field is removed or renamed, or a value changes meaning. That can happen in a minor release, and the release notes say so. Read schema and stop on a number you don’t know.

In both:

  • New fields can appear in any release: ignore the ones you don’t know.
  • A new status value or exit code comes only in a major release.

Not Covered

These can change in any release:

  • Human output: the status tree, messages, prompts, colors, and the TUI’s layout and keys.
  • Which short ID a branch, a file or a commit without a Change-Id gets, and how many of a persistent ID’s letters status shows.
  • In agent mode, everything on stderr.
  • The output of git loom trace, and that of show and diff, which is git’s.
  • The agent skill: git loom agent install rewrites it for each release.
  • A paused operation: finish it with git loom continue or git loom abort before upgrading.
  • Options passed to git after --, which loom hands on unchecked.
  • The Rust version needed to build from source.