Jj Private Commits
This is an LLM ass-isted document,
as are all in this subdirectory.
Met as a flag first: jj git push --help lists --allow-private next to
--allow-empty-description, and the name raised the question. A private
commit is any commit matching the revset in git.private-commits. jj git
push refuses to publish it — and everything descended from it — unless
told otherwise. Nothing else changes: it is a push guard, not a hiding
mechanism. The name echoes Mercurial's "secret" phase1.
1. What counts
The default is none(): nothing is private until configured2. The
documentation's own example labels work in progress:
[git]
# Prevent pushing work in progress or anything explicitly labeled "private"
private-commits = "description('wip:') | description('private:')"
set repo-locally with:
jj config set --repo git.private-commits "description('wip:') | description('private:')"
Three refinements3:
- a commit already on the remote is no longer private — the guard bites only before first publication;
- immutable commits are excluded from the set;
- descendants cannot be pushed past a private commit, since that would publish the private ancestor.
2. Pushing anyway
jj git push --allow-private
3. Getting past one
Nothing happens implicitly: a private commit inside the push range simply
aborts the push, and jj never folds content on its own. Two explicit ways
out besides --allow-private:
- Un-private it. Usually only the description makes it private: rewrite it
(
jj describe) without the marker the revset keys on, and the commit pushes like any other. - Squash it away. Sitting on the child,
jj squash --from @-folds the private changes into@; the emptied source is abandoned automatically, descendants rebase, and the child pushes alone4.
4. Caveats
- It is push-only:
jj logshows private commits like any other, and there is noprivate()revset to query the set (one is being argued for)1. - Description-based rules meet this repo's taste for empty messages
(JjVsGitHabit): an empty description matches
description(exact:""), neverdescription('wip:').
October, 2026.
Footnotes:
jj-vcs/jj issue #9308, FR: Add builtinprivateroots() revset,
https://github.com/jj-vcs/jj/issues/9308, which draws the Mercurial-phases
analogy: the git.private-commits option and the --allow-private flag
exist "to prevent accidentally pushing commits you didn't mean to".
cli/src/config-schema.json at v0.45.1: "Revset of commits to
refuse to push to remotes", default none(),
https://github.com/jj-vcs/jj/blob/v0.45.1/cli/src/config-schema.json.
Jujutsu documentation, Configuration, on git.private-commits,
https://docs.jj-vcs.dev/latest/config.
CLI reference, on jj squash: "If, after moving changes out,
the source revision is empty compared to its parent(s), and --keep-emptied
is not set, it will be abandoned"; and of combined descriptions, "if either
was empty, the other one will be used", https://docs.jj-vcs.dev/latest/cli-reference.