“Diffing” refers to the process of comparing one thing against another, viewing the “differences” between the two.
Because comparing text is a common occurrence when writing code, there exists an entire category of tools dedicated to helping developers compare text snippets. In this post, I go over some of the tools I rely on for this task.
Who is this post for?
- You use neovim (or considering it).
- You have yet to go down the diff rabbit hole yourself.
- You’re (at least mildly) interested in improving your diff workflow.
On the other hand, if you’ve already been down this path and have found better methods, do consider sharing them.
Table of Contents
- Neovim: diffing while editing code
- Shell: diffing the output of two shell commands
- Filesize: recursively diffing file sizes in a directory
- Misc: quality of life tips for working with diffs
- Alternatives: other related tools
Neovim: diffing while editing code
The following examples require diffview.nvim and gitsigns.nvim to be installed.
Deep diffing: comparing against one or more commits
Repo
Trace the history of the entire git repository.
File
This can be used to step through the changes of a single file (the current file). The --follow flag tells git to trace a file’s history through renames.
It opens in the same view as the previous example, except that the commits listed are limited to those affecting the current file.
vim.keymap.set( 'n', ',hf', '<cmd>DiffviewFileHistory --follow %<cr>', { desc = 'File history' } )Visual selection
Trace the history of the current visual selection. Use this command if you’re only interested in tracing changes affecting a specific section of a file.
vim.keymap.set( 'v', ',hl', "<Esc><Cmd>'<,'>DiffviewFileHistory --follow<CR>", { desc = 'Range history' } )Line
Finally, the most granular form is tracing the history of a single line.
vim.keymap.set( 'n', ',hl', '<Cmd>.DiffviewFileHistory --follow<CR>', { desc = 'Line history' } )Shallow diffing: comparing against HEAD
Repo
Diff the working directory against HEAD (and staging area). This will open a new tab.

We can slightly alter the previous command to serve another common use case: diffing the working directory against the master branch. This is useful before merging or when first continuing work on an existing feature branch.
local function get_default_branch_name() local res = vim .system({ 'git', 'rev-parse', '--verify', 'main' }, { capture_output = true }) :wait() return res.code == 0 and 'main' or 'master' end -- Diff against local master branch vim.keymap.set( 'n', ',hm', function() vim.cmd('DiffviewOpen ' .. get_default_branch_name()) end, { desc = 'Diff against master' } ) -- Diff against remote master branch vim.keymap.set( 'n', ',hM', function() vim.cmd('DiffviewOpen HEAD..origin/' .. get_default_branch_name()) end, { desc = 'Diff against origin/master' } )File
The following commands use highlighting and virtual text to visualize changes without leaving the current buffer.
-- Highlight changed words. vim.keymap.set( 'n', ',vw', require('gitsigns').toggle_word_diff, { desc = 'Toggle word diff' } ) -- Highlight added lines. vim.keymap.set( 'n', ',vL', require('gitsigns').toggle_linehl, { desc = 'Toggle line highlight' } ) -- Highlight removed lines. vim.keymap.set( 'n', ',vv', require('gitsigns').toggle_deleted, { desc = 'Toggle deleted (all)' } )Hunk
Diff the current hunk in a floating window (against the staging area).
vim.keymap.set( 'n', ',vh', require('gitsigns').preview_hunk, { desc = 'Preview hunk' } )Clipboard
Clipboard vs. current file
Diff the current buffer against the clipboard contents.
Clipboard vs. visual selection
Diff the current visual selection against the clipboard contents.
Shell: diffing the output of two shell commands
You can use process substitution to diff the output of any two shell commands. The following works for bash and zsh.
nvim -d <(ls -l) <(ls -la) # you can also use another difftool delta <(ls -l) <(ls -la)Filesize: recursively diffing file sizes in a directory
This bash function uses dust to diff the sizes of two directories. I don’t use this much, but it comes in handy when I’m experimenting with different bundler options (such as in a tsconfig.json or vite.config.js) and want to know more about how the changes I’m making affect the size of the generated output.

Depending on the output, it can be tricky to accurately detect and properly highlight moved vs changed lines. Delta has some options that you can tweak such as --word-diff-regex.
Misc: quality of life tips for working with diffs
Syntax highlighting for code snippets
- my original line + my updated line my unchanged lineYou can enable this form of syntax highlighting for a markdown code block by specifying diff as the language. Then, prefix any line with either a - or a + to highlight it.
This works automatically on github.com. In neovim, you might need to explicitly add diff to the list of languages in your treesitter config (in the ensure_installed table).
Delta
Delta makes diffs easier to read.


Using delta on the command line
# delta can be used to diff any two files delta file1 file2 # or directories. delta dir1 dir2Delta can be set as git’s default pager. See delta’s documentation for how to do so in your .gitconfig. Additionally, below are some extra delta options I’ve added to my .gitconfig:
# .gitconfig [core] pager = delta [delta] features = decorations light = false tabs = 2 line-numbers = true navigate = true hyperlinks = true # side-by-side = trueHint: For even greater readability, set gruvmax-fang as your custom theme.
You can find out more about these options by running delta --help or visiting delta’s docs.
Delta can also be integrated into other tools such as lazygit.
meld
If you’d rather work with a mouse, or simply prefer a GUI, meld can compare files or entire directories recursively.

comm
comm can print common or unique lines between two files. A helpful cheat sheet can be found here.
.png)
 4 months ago
                                14
                        4 months ago
                                14
                     
  

