Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

36 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

CI Crates.io version Latest release Rust version License

jj-commit

A guided, convention-aware commit-message builder for Jujutsu.

jjc wraps the jj workflow with interactive prompts that enforce a consistent commit style β€” either Conventional Commits or Gitmoji β€” so you never have to remember the format again.

Table of contents


Features

  • 🎯 Convention auto-detection β€” Inspects the last 10 commits and picks the convention used most often. No flags needed for the common case.
  • ✍️ Two conventions, one tool β€” Full support for Conventional Commits (feat:, fix(scope):, …) and Gitmoji (✨, πŸ›, …).
  • πŸ”€ Interactive prompts β€” Type, scope(s), and description are gathered through prompts with sensible defaults and pre-fill support.
  • 🧩 Pre-fill everything β€” Pass --message, --type, and --scopes to seed the prompts or skip them entirely. Great foraliases and editor integrations.
  • 🏷️ Bookmarks β€” Advance the nearest ancestor bookmark, or create/move named bookmarks to the new commit. Supports interactive selection when multiple ancestors match.

Install

cargo install --git https://github.com/Odonno/jj-commit

Or build from source:

git clone https://github.com/Odonno/jj-commit
cd jj-commit
cargo build --release
# binary is at ./target/release/jjc

Get started

Auto-detect convention

With no flags, jjc inspects the last 10 commits and picks the convention used most often.

jjc
? Commit type
> feat
  fix
  chore
  docs
  style
  refactor
  perf
[↑↓ to move, enter to select]

? Scope (leave empty to finish): auth

? Description: add OAuth2 login support

Resulting commit message:

feat(auth): add OAuth2 login support

Specify a convention explicitly

Skip auto-detection and force a convention:

jjc --convention conventional
jjc --convention gitmoji

Pre-fill the commit type

Skip the type prompt entirely by passing --type (Conventional Commits only):

jjc --type fix
? Scope (leave empty to finish):

? Description: handle null pointer in user resolver

Resulting commit message:

fix: handle null pointer in user resolver

Pre-fill scopes

Pass one or more --scopes flags to seed the scope list (Conventional Commits only):

jjc --type feat --scopes api --scopes ui
? Scope (leave empty to finish):   ← api and ui already added

? Description: expose dark mode toggle

Resulting commit message:

feat(api,ui): expose dark mode toggle

Pre-fill from an existing message

Use --message to parse an existing commit string into the prompts so you can review and amend each field:

jjc --message "fix(auth): wrong token expiry"
? Commit type  [fix]
? Scope        [auth]
? Description  [wrong token expiry]

--message works with Gitmoji too, accepting both the shortcode (:bug:) and raw emoji (πŸ›) forms.

Gitmoji workflow

jjc --convention gitmoji
? Gitmoji
> ✨  Introduce new features.
  πŸ›  Fix a bug.
  πŸš‘οΈ  Critical hotfix.
  πŸ“  Add or update documentation.
  ♻️   Refactor code.
  πŸ”₯  Remove code or files.
[↑↓ to move, enter to select]

? Description: streaming support for chat API

Resulting commit message:

✨ streaming support for chat API

Bookmarks

jjc can manage bookmarks as part of your commit.

Advance the nearest ancestor bookmark

Use --advance-bookmark (-a) to find the closest ancestor that holds a local bookmark and move it onto the newly created commit β€” handy for keeping a moving "main"-style bookmark pinned to your latest work:

jjc -a

If that ancestor carries several bookmarks, you get an interactive multi-select:

? Select bookmarks to advance to the new commit:
  > [x] main
    [x] release
    [ ] wip
[↑↓ to move, space to toggle, enter to confirm]

With a single bookmark the choice is applied automatically. If no ancestor has any bookmark, jjc prints a warning instead of failing.

Create or move named bookmarks

Use --bookmarks (-b, repeatable) to point one or more bookmarks at the new commit, creating them if they don't exist:

jjc -b feature-x -b v2

Combine the two

The two flags cooperate: --bookmarks destinations are applied directly, while --advance-bookmark discovers ancestors interactively. They can be used together in a single invocation:

jjc --type feat --scopes ui --advance-bookmark --bookmarks release

Configuration

jjc reads the same configuration the jj CLI does, in the same order:

  1. Built-in defaults from jj-lib.
  2. User config file β€” $JJ_CONFIG (colon-separated, like $PATH), or otherwise $XDG_CONFIG_HOME/jj/config.toml (falling back to ~/.config/jj/config.toml), the legacy ~/.jjconfig.toml, and %APPDATA%\jj\config.toml on Windows.
  3. Environment overrides β€” JJ_USER sets user.name and JJ_EMAIL sets user.email, taking precedence over the config file.

So if you've already configured jj, you're configured for jjc β€” nothing extra to do.


Supported conventions

Conventional Commits types

The --type / --convention conventional flow knows these types:

Type Use for
feat A new feature
fix A bug fix
chore Maintenance tasks that don't touch src/docs
docs Documentation only changes
style Formatting, whitespace, semicolons, etc.
refactor Code changes that neither fix a bug nor add a feature
perf Performance improvements
test Adding or correcting tests
build Build system or external dependencies
ci CI configuration files and scripts
revert Reverting a previous commit

The breaking-change marker (feat!:) is parsed from --message but not added by a prompt β€” use --message when you need it.

Gitmoji

The Gitmoji flow ships the full gitmoji.dev table (80+ entries), presented with their description. Both the shortcode (:sparkles:) and raw emoji (✨) forms are recognized when pre-filling with --message.


How it works

Unlike a thin wrapper that calls out to the jj binary, jjc links against jj-lib and performs the commit transaction in-process:

  1. Loads your real jj stacked config and workspace (mirroring the jj CLI's lookup rules).
  2. Snapshots the working copy β€” respecting .gitignore and auto-tracking new files, just like jj's default snapshot.auto-track = "all()".
  3. Rewrites the open working-copy commit with the snapshotted tree and your crafted message.
  4. Rebases any descendants, then checks out a fresh empty working-copy commit on top.
  5. Syncs the Git index and HEAD for co-located Git repos so the Git view matches Jujutsu.
  6. Optionally advances bookmarks before or after the commit lands.

This means you get the same resulting topology as jj commit β€” without spawning jj.


Contributing

Before contributing a change, please run:

cargo fmt
cargo clippy -- -D warnings
cargo test

License

See LICENSE.

About

Simplify the `jj commit` experience

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Contributors

Languages