A very limited integration between jj and pre-commit allowing you to run your pre-push hooks in a colocated jj/git repository.
I don't expect this to last forever, it's just a stopgap until some jj-native mechanism arrives that can take over.
Prior art:
- https://www.aazuspan.dev/blog/automating-pre-push-checks-with-jujutsu/
- Various comments on jj-vcs/jj#405
Use jj-pre-push push
(or an alias - personally I use jj push
) as a replacement for
jj git push
. It takes all the same arguments, and does the following:
- Determines which bookmarks the corresponding
jj git push
will update on the remote, and how they would change. - For each of these bookmarks in turn:
- Checks out the bookmark to the working copy
- Runs the pre-push hooks defined in your .pre-commit-config.yaml on the same set of
files pre-commit would when pushing the same change. (For existing branches that's
the files touched in the range
old
...new
; for new branches it's the files touched in all ancestors ofnew
that aren't present on the remote.) - Reports any failures; and if any files were modified reports the change ID(s) in which these modifications can be found.
- If all hooks succeeded on all branches, executes
jj git push
with the arguments provided; otherwise nothing is pushed. - Returns the working copy to its original change.
If there is no .pre-commit-config.yaml in your workspace root, jj-pre-push push
immediately delegates to jj git push
.
By default jj-pre-push
produces no console output of its own unless hooks fail. If
you'd like to see more details about what's happening, you can use jj-pre-push --log-level=INFO push
.
If you have uv installed and you're planning to use an
alias anyway, you can avoid explicitly installing at all with uvx
, e.g. with this jj
configuration for jj push
:
[aliases]
push = ["util", "exec", "--", "uvx", "jj-pre-push", "push"]
Otherwise, install the PyPI package jj-pre-push
in whichever way you prefer; e.g. uv tool install jj-pre-push
or pip install jj-pre-push
, and use jj-pre-push push
as
described earlier.
If you're a jjui fan (I think maybe you should be!),
here's an example jjui/config.toml
snippet showing how you can define custom commands
to invoke jj-pre-push: one to perform the default jj push
, and one to push the
bookmarks attached to the change you have currently selected in the UI.
[custom_commands]
"jj push" = { key = ["p"], args = ["push"] }
"jj push selected bookmark(s)" = { key = ["P"], args = ["push", "-r", "$change_id"] }
Note that these depend upon the jj push
alias defined in the previous section.