Emacs

The Emacs client lives in clients/emacs/ and is modeled on Magit. It uses eglot (Emacs 29+ built-in LSP client) and transient (the popup-menu library Magit itself uses).

Requirements

  • Emacs 29+ (ships built-in eglot and transient)
  • badjuju server binary on your PATH
  • jj on your PATH

Installation

Vanilla Emacs (manual)

(add-to-list 'load-path "/path/to/badjuju/clients/emacs")
(require 'badjuju)

use-package

(use-package badjuju
  :load-path "/path/to/badjuju/clients/emacs"
  :commands (badjuju-status badjuju-log badjuju-diff))

straight.el

(straight-use-package
 '(badjuju :type git
            :host github
            :repo "jennings/badjuju"
            :files ("clients/emacs/*.el")))

Or with use-package integration:

(use-package badjuju
  :straight (badjuju :type git
                      :host github
                      :repo "jennings/badjuju"
                      :files ("clients/emacs/*.el"))
  :commands (badjuju-status badjuju-log badjuju-diff))

Doom Emacs

In packages.el:

(package! badjuju
  :recipe (:host github :repo "jennings/badjuju"
           :files ("clients/emacs/*.el")))

In config.el:

(use-package! badjuju
  :commands (badjuju-status badjuju-log badjuju-diff)
  :config
  (map! :leader "g j" #'badjuju-status))

Configuration

;; Path to the jj binary; nil uses PATH.
(setq badjuju-binary-path nil)

;; Hotkey profile: "magit" (default) or "none".
(setq badjuju-keymap-profile "magit")

Top-level commands

CommandDescription
M-x badjuju-statusOpen the working-copy status view
M-x badjuju-logOpen the commit log
M-x badjuju-diffDiff for @ (change mode, updates on amend)
M-x badjuju-describeEdit the commit message for @
M-x badjuju-newCreate a new child change
M-x badjuju-editMove @ to a different commit
M-x badjuju-abandonAbandon the working copy
M-x badjuju-squashSquash into the parent
M-x badjuju-unsquashPull a file back from the parent
M-x badjuju-undoUndo the last operation
M-x badjuju-fetchjj git fetch
M-x badjuju-pushjj git push
M-x badjuju-refreshRefresh the current buffer

Most workflows live in the status buffer — open it with M-x badjuju-status and use the hotkeys below. Press ? at any time for a popup of active bindings in the current buffer.

Keybindings (magit profile)

Status buffer

KeyAction
nNew child change
cCommit transient (reword / new child)
c wCommit transient → reword (describe) commit at cursor
c nCommit transient → new child commit
dDiff change at cursor (updates on amend)
DDiff commit at cursor (pinned, immutable)
=Diff (alias for d)
eEdit commit at cursor (move @)
aAbandon commit at cursor
sSelect squash source or destination (two-step)
SSquash file at cursor into parent
uUnsquash file at cursor
UUndo last operation
r sMark rebase source (--source)
r rMark rebase source (--revisions)
r bMark rebase source (--branch)
r oExecute rebase onto cursor
r AExecute rebase insert-after cursor
r BExecute rebase insert-before cursor
xCancel pending squash or rebase
bBookmark manager
fjj git fetch
pjj git push
Pjj git push --force-with-lease
LOpen log
RRefresh
TABToggle fold at cursor
RETGo to definition
gdGo to definition
?Show help popup
qBury buffer

Code actions intentionally have no default binding — use the global M-x eglot-code-actions (Emacs binds it to C-c C-a by default in eglot-managed buffers).

Log buffer

KeyAction
cCommit transient (reword / new child)
c wCommit transient → reword (describe) commit at cursor
c nCommit transient → new child commit
dDiff change at cursor (updates on amend)
DDiff commit at cursor (pinned, immutable)
=Diff (alias for d)
eEdit commit at cursor
aAbandon commit at cursor
sSelect squash source or destination (two-step)
SSquash file at cursor into parent
UUndo
r sMark rebase source (--source)
r rMark rebase source (--revisions)
r bMark rebase source (--branch)
r oExecute rebase onto cursor
r AExecute rebase insert-after cursor
r BExecute rebase insert-before cursor
xCancel pending squash or rebase
bBookmark manager
RRefresh
RETApply revset shortcut on a JJ: line / go to definition
gdGo to definition
?Show help popup
qBury buffer

Diff buffer

KeyAction
RRefresh
RET / gdGo to definition
?Show help popup
qBury buffer

Squash buffer

KeyAction
sToggle hunk/file between SELECTED and REMAINING
eEdit hunk before squashing
aSelect all changes
ADeselect all changes
uUndo
TABToggle fold
gdGo to definition
?Show help popup
qBury buffer

Describe buffer

KeyAction
C-c C-cFinalize and close (saves the commit message)
C-c C-kAbort without saving

Folding

Status and squash buffers open fully folded. The WORKING COPY CHANGES and PARENT CHANGES sections are automatically expanded on first open; TAB toggles individual sections.