Emacs: replace regexp as diff

By Xah Lee. Date: .
xtodo

replace-regexp-as-diff

replace-regexp-as-diff

new in Emacs 30 (date 2025)

replace-regexp-as-diff (AI generated)

ai replace-regexp-as-diff 2026-04-15 1e497
ai replace-regexp-as-diff 2026-04-15 1e497

replace-regexp-as-diff is a powerful new command introduced in Emacs 30.1. It lets you preview the effects of a replace-regexp operation as a unified diff before applying any changes. This is excellent for safe bulk refactoring, avoiding accidental matches (like turning "DEBUG" into "DEFEATURE"), and reviewing complex regex replacements.

Why Use It?

Traditional replace-regexp or query-replace-regexp applies changes immediately or asks per match. With *-as-diff, you get a full preview in a diff buffer (using Emacs's excellent diff mode), review hunks, and then apply the patch only if everything looks good. Nothing changes in your original buffer(s) until you explicitly apply.

Basic Usage: Single Buffer (replace-regexp-as-diff)

  1. Open the buffer you want to modify.
  2. Run M-x replace-regexp-as-diff.
  3. Enter the search regexp (same syntax as replace-regexp).
  4. Enter the replacement string (supports back-references like \1, \&, and even \, for Lisp expressions).

Emacs generates a *replace-regexp-as-diff* buffer (or similar) showing a unified diff of all proposed changes.

In the diff buffer: - Use standard diff navigation: n/p for next/previous hunk, SPC/DEL to scroll. - Review carefully—false positives are easy to spot. - To apply: Use C-c C-c (or the command diff-apply / vc-diff-apply style binding) or follow the buffer's instructions to apply the patch to the original buffer.

If you close the diff buffer without applying, no changes occur.

Multi-File Variants

multi-file-replace-regexp-as-diff
Prompts for a list of files (you can select them one by one and finish with RET). It shows one combined diff across all files.

dired-do-replace-regexp-as-diff (most convenient for projects)
1. Open Dired (C-x d or M-x dired). 2. Mark the files you want (m for individual, U to unmark, % m to mark by regexp, * . for extension, etc.). 3. Run M-x dired-do-replace-regexp-as-diff. 4. Enter regexp and replacement. 5. Review the combined diff and apply.

This works great with Projectile, VC directory, or any file set.

Practical Examples

Example 1: Simple rename
Regexp: \b old_function_name \b
Replacement: new_function_name

Example 2: Capture groups
Regexp: (\w+)_(\d+)
Replacement: \2_\1 (swaps parts)

Example 3: Lisp computation in replacement (advanced)
Regexp: price: \([0-9.]+\)
Replacement: price: \,(format "%.2f" (* 1.1 (string-to-number \1))) (adds 10% tax, formats to 2 decimals)

Example 4: Catch false positives
Replacing "BUG" with "FEATURE" on code containing "DEBUG" — the diff clearly shows "DEFEATURE", so you can refine the regexp (e.g., add word boundaries \bBUG\b).

Key Tips and Tricks

Comparison with Similar Commands

Command Preview Interactive per-match Multi-file Best for
replace-regexp No No No Quick, trusted changes
query-replace-regexp No Yes Via Dired Careful per-instance
project-query-replace-regexp No Yes Project Project-wide careful
replace-regexp-as-diff Yes (diff) No Yes Safe bulk review

Getting Started Quickly

Try this in a test buffer:

  1. M-x replace-regexp-as-diff
  2. Regexp: foo
  3. Replacement: bar
  4. Review the diff and apply.

Once comfortable, use it on real code.

This feature makes risky regex operations much less stressful. It's a fantastic addition for power users doing refactoring, cleanup, or data transformations.

Enjoy safer editing in Emacs 30+! If you're on an older version, packages like visual-replace or manual diff workflows are alternatives.