![JJ-FZF and Emacs](emacs-jj-fzf.png)
Introduction
Built on jj and fzf, jj-fzf offers a text-based user interface (TUI) that simplifies complex versioning control operations like rebasing, squashing, and merging commits. This post will guide you through integrating jj-fzf into your Emacs workflow, allowing to switch between emacs and jj-fzf with a simple key combination.
Setting Up jj-fzf in Emacs
Step 1: Handling ioctl(TIOCSTI)
Restrictions
In order to suspend Emacs and start jj-fzf, we need to use
suspend-emacs
. However, recent Linux versions have
restricted the use of ioctl(TIOCSTI)
, which is used by
suspend-emacs
to inject commands into stdin of the parent
shell (see the Linux
TIOCSTI documentation for more details).
To avoid this problem, we use suspend-with-shell
which
wraps (suspend-emacs)
so that a subshell is executed with
$SHELL
pointing to a script that in turn runs the desired
command. The suspend-with-shell
function is provided in the
jj-fzf trunk at jj-fzf/contrib/suspend-with-shell.el
and will be part of the next jj-fzf release. Ensure to load it in your
Emacs configuration file .emacs
(or paste the elisp file
directly into your Emacs configuration):
(load (expand-file-name "~/jj-fzf/contrib/suspend-with-shell.el")) ; provides suspend-with-shell
Note: Make sure to replace ~/jj-fzf/
with the actual path where jj-fzf is residing on your system.
Step 2: Configuring jj-fzf as Ctrl+T in Emacs
To integrate jj-fzf into Emacs, we’ll assign it a short and easily
remembered keyboard shortcut, for example Ctrl+T. Add
the following code snippet to your .emacs
file:
(load (expand-file-name "~/jj-fzf/contrib/suspend-with-shell.el")) ; provides suspend-with-shell
(global-set-key (kbd "C-t") (lambda () (interactive) (suspend-with-shell "jj-fzf")))
This configuration binds Ctrl+T to a lambda function
that suspends Emacs and starts jj-fzf in a new shell, using
suspend-with-shell
because of the aforementioned
restrictions. In jj-fzf, you can press Ctrl+C any time
to terminate and return to Emacs back where you left off.
Note: This setup works only for Emacs running in a terminal (not in graphical mode). Feedback is welcome for XEmacs and other graphical environments.
Step 3: Browsing Line History with Ctrl+X B
As another useful feature, you can bind a key combination to browse the commit history of the current source code line under the cursor. Here’s how to do it:
(load (expand-file-name "~/jj-fzf/contrib/suspend-with-shell.el")) ; provides suspend-with-shell
(global-set-key (kbd "C-t") (lambda () (interactive) (suspend-with-shell "jj-fzf")))
(global-set-key (kbd "C-x b")
(lambda () (interactive)
(suspend-with-shell
(concat "jj-fzf +" (number-to-string (line-number-at-pos)) " '" (buffer-file-name) "'"))))
This configuration binds Ctrl+X B to a function that opens jj-fzf as a file browser, selects the current line and shows the commit history for this line in the preview pane.
Step 4: Automatic Snapshots on Save
For developers who want to track intermediate stages of their work in
emacs, the jj-undirty.el
script provides a convenient way
to snapshot your working directory with jj into the jj op log. This
allows you to revisit past states of your codebase without explicit
commits. Using the jj op log, such intermediate snapshots can be turned
into commits at a later point if needed, e.g. in jj-fzf with
Ctrl+O (op log browser) and Alt+J
(inject historic commit).
To enable this functionality, add the following to your
.emacs
file (or paste the elisp file directly into your
Emacs configuration):
;; == jj-undirty ==
;; Install an 'after-save-hook' to automatically run `jj status` after saving
(load (expand-file-name "~/jj-fzf/contrib/jj-undirty.el"))
This setup ensures that every buffer save in a jj repository
automatically triggers a jj status
command, recording the
current state of your working directory into the jj op log.
Final Notes
The above steps provide a simple and very quick integration of jj-fzf with Emacs. This has replaced the need for any other version control utilities like gitk or vc-mode in my daily workflow. For further exploration, you can visit the jj-fzf GitHub repository and take a look at the screencasts for various workflows and key bindings.
I would love to hear your feedback about jj-fzf and any other tricks that have improved your Emacs and jj integration. Feel free to send a comment or reach out via IRC in #jujutsu on Libera.