Changes and trunk
gm's change-based model, how undo and automatic rebasing work, and how gm push lands work on trunk without pull requests.
gm is change-based, like jujutsu, rather than commit-and-stage based like git. It is built for agents that commit very often: commit early, commit often, no staging area, no pull requests. Under the hood it stays a normal git repository.
The working copy is a change
Your files always belong to a change called @, the working copy. Every workspace command first snapshots the files on disk into @, so nothing is ever “uncommitted” and there is no index to manage.
gm commit -m describes @ and starts a new, empty @ on top of it. That is the “save my work” step:
gm status # where am I, what changed
gm commit -m "Add retry to fetch"
gm commit -m "Cover retry in tests"
Pass paths to commit only those files; the rest stays in the new working copy:
gm commit -m "Only the docs" docs/
Change ids and revisions
Every change has a change id, written in the letters k to z (for example kxmqvtyl). It stays the same when the change is rewritten. Commit ids (hex) change on every rewrite. Prefer change ids when you refer to a change.
Anywhere a revision is expected you can use:
| Revision | Meaning |
|---|---|
@ |
the working copy |
@-, @-- |
its parent, its grandparent |
trunk |
the server’s main line |
kxmq |
a change id prefix |
1a2b3c |
a commit id prefix |
name |
a bookmark (a git branch) |
gm log -r also takes ranges such as trunk..@ and all.
Rewrites rebase descendants
You can edit, squash, split, abandon or describe any change in a stack, and everything above it is rebased automatically.
gm squash --into kxmq # move @'s edits into an earlier change
gm edit kxmq # make an earlier change the working copy
gm new # start a fresh change on top again
gm describe -m "Better message" # reword the working copy's description
gm abandon kxmq # drop a change; its descendants move onto its parent
Conflicts never stop a command halfway. A conflicted change gets conflict markers in its files and is listed under conflicts in gm status. Edit the markers away in @ (or gm edit the change first) and the next gm command records the fix. For a conflict in a change below @, fix the files in @ and then gm squash --into <change>. gm push refuses changes that still have conflicts.
gm merge <rev> creates a merge change with parents @- and <rev>, below @. Rewrites keep every parent of a merge.
Everything can be undone
Each workspace change is an operation. gm undo reverses the last one; repeat it to go further back. gm op log lists operations and gm op restore <id> jumps to any of them.
gm undo
gm op log
gm op restore <operation-id>
Undo covers your local workspace only. It does not reverse tracker changes, timeline records on the server, or anything already pushed.
Trunk is the truth
trunk is the server’s main line, and changes in trunk are immutable. There are no pull requests: gm push fast-forwards trunk to your change, and its ancestors go with it.
gm push # land @ (or @- when @ is empty) on trunk
gm push -r kxmq # land a specific change and its ancestors
If trunk has moved since you last fetched, gm push stops with exit code 4. gm sync fetches and rebases your changes onto the new trunk, and gm push --sync does both before pushing:
gm sync # like git pull --rebase
gm push --sync
When trunk has landed some of your changes (for example as rewritten copies), gm sync drops them from your stack and moves what was built on them onto trunk.
To push to a branch instead of trunk, use a bookmark: gm push -b feature-x creates or moves the bookmark and pushes it.
Each push to main runs the repository’s CI. See CI and steps.
Other ways to land
- The merge queue. For independent work tracked in the issue graph,
gm push --queue --issue <key>submits your change to the server, which merges it onto current main, runs the gates, and advances main only with a passing candidate. Submission is not landing. See The issue graph. - Timelines. Broad or risky work can go to a timeline, where
gm pushupdates the timeline instead of main, and lands later. See Timelines.
It stays a git repository
Bookmarks are git branches. HEAD sits on @’s parent, so git status and git diff show the change in progress. Change ids live in a change-id commit header, the same one jujutsu uses. Git shows a detached HEAD; that is gm’s working copy, not a problem.
Git equivalents
| gm | git |
|---|---|
gm commit -m |
git commit -am |
gm squash |
git commit --amend / fixup |
gm split -m msg <paths> |
partial commit |
gm restore [paths] |
discard edits |
gm new |
start a fresh change (also how you stash) |
gm rebase -d trunk |
git rebase |
gm backout -r <rev> |
git revert |
gm duplicate <rev> |
git cherry-pick |
gm sync |
git pull --rebase |
gm push |
git push to main |
gm cli search "<task>" returns the matching commands with their git equivalents.
Worktrees
gm worktree add creates a parallel working directory without cloning objects again. Each worktree has its own files, @ and undo log; objects and bookmarks are shared. This suits several agents working in one repository.
gm worktree add ../feature # a new change on the current @ snapshot
gm worktree add ../clean -r trunk # start from trunk instead
gm -C ../feature status
gm worktree list
gm worktree remove ../feature
--include copies ignored, untracked files that match the root .worktreeinclude (gitignore syntax), such as local environment files. Dependencies are not installed for you. Creating and removing worktrees is not undoable, though removal archives gm’s recorded history for recovery.
See the gm reference for each command, for example gm push, gm sync and gm undo.