JJ-FZF and Emacs

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.

Post comment via email