One of the frequent questions for the built-in Version Control support in Emacs has been the ability to commit only a part of the changes in a file or several.

In the command-line, it’s simple (though not always easy): you call git add -p and iterate through the whole repository diff with y/n/etc. Magit also has an interface for staging and committing from the staging area, but that only works for Git and requires you to switch to its UI first.

Using VC

One of the less noticed additions in Emacs 29 is the ability to create a commit from a diff buffer. And since diff-mode provides functionality for editing the diff, you can drop the unwanted pieces first (they stay saved on disk) and commit the amended changeset.

Here’s how it works: you press C-x v = or C-x v D to show the diff for the current file, or the whole repository, edit the diff using commands such as diff-hunk-kill (M-k), diff-split-hunk (C-c C-s), and then type C-x v v in the same buffer to proceed to the familiar step “Edit commit message”.

I also recommend customizing diff-default-read-only to t so that the short key combinations work right away in diff-mode, including n and p for hunk navigation and k for removing hunks. Otherwise those only work after switching to read-only-mode (C-x C-q)

Anyway, you enter the commit message and press C-c C-c to create the commit. This works not only with Git, but also with Mercurial repositories, and the fallback implementation endeavors to support other VC systems as well (such as Bazaar and SVN), though using a slower method. The latter is not as well-tested (the latest bug report was regarding an incompatible command-line flags in OpenBSD’s patch), so more feedback welcome.

Using Diff-HL

The workflow described above doesn’t use the staging area, and if you are using Magit, you might prefer a simply faster way to stage hunks.

By popular demand, the new additions to the diff-hl package are the commands diff-hl-stage-current-hunk and, more recently, diff-hl-stage-dwim.

Either type C-x v S to stage the hunk at or above point, or prepend it with C-u to similarly pop up a buffer where you can edit the changeset to stage. Then do the commit with Magit or the command line.