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

WIP and private commits

Sometimes I do not know what I am building yet.

I still commit.

Not because the work is done, but because I want to make the idea durable. Once it is a commit, I can move it, compare it, abandon it, rebase it, or come back to it later.

jj diff
jj commit -m "wip: try stacked push flow"

If the work is not meant for review, I mark it more clearly:

jj commit -m "private: notes from debugging auth callback"

Private does not mean secret

private: is just a label in my commit message. It helps me search and remember intent. It is not access control, and I do not put secrets there.

Keep private commits off the remote

Once I started using private: as a real parking lot, I wanted one extra guardrail: if I ever try to push those commits, jj should refuse.

This config does that:

[git]
private-commits = "subject('private:*')"

With that set, jj git push refuses to push commits whose subject matches private:*. It also refuses to push descendants that would require pushing the private commit.

The command-line form is:

jj config set --user git.private-commits "'''subject('private:*')'''"

The nested quotes are only there so the revset quotes survive the shell.

I leave wip: pushable because sometimes the whole point of a WIP commit is to get CI or a teammate’s eyes on it. private: is the stronger word in my vocabulary.

Find the messy stuff later

Because I use a prefix, I can search for these commits later:

jj log -r 'subject("wip:*")'
jj log -r 'subject("private:*")'

That searches all visible history, so it can return old commits from other people too.

Most of the time I only want WIP commits in my current stack. For that, I intersect the subject search with trunk()..@:

jj log -r 'subject("wip:*") & trunk()..@'
jj log -r 'subject("private:*") & trunk()..@'

In my config, those stack-scoped searches are:

jj wip
jj private

Direction matters

trunk()..@ means commits reachable from my current working copy that are not already in trunk. @..trunk() is the opposite direction and is usually not what I want for "my current work".

Promote it when it becomes real

At some point a WIP commit becomes real.

When I write <change> below, I usually copy the short change id from jj log.

First I rename it:

jj describe -r <change>

or directly:

jj describe -r <change> -m "docs: explain private commit workflow"

Then I move it where it belongs.

If it should sit on top of main:

jj rebase -r <change> -o main

If it should become part of an existing stack:

jj rebase -r <change> -o <bookmark>

If it is a stack of WIP commits:

jj rebase -s <first-wip-change> -o <bookmark>

Then I inspect it:

jj log -r main..@
jj diff -r <change>

With my alias, the first command is:

jj stack

And push it when it is ready:

jj git push -c <change>

If the promoted work is the last completed change, I usually use:

jj git push -c @-

Why this feels good

I do not need to decide up front whether an idea is real. I can give it a cheap name, keep moving, and promote it only after it proves useful.