Run a regex codemod across many files and preview the diff

Paste a bundle of files, write one pattern and replacement, and see exactly which lines change in every file. Capture groups, the i/m/s flags, whole-word and first-match-only are all here, and the output is a unified diff you can copy straight into git apply.

Try:
Unified diff

About this tool

A codemod is a mechanical, repeatable edit applied across a whole set of files — renaming a symbol, reshaping a date format, swapping an import path, redacting a field. Doing it by hand in a dozen files is slow and easy to get subtly wrong; doing it with a one-line shell pipeline is fast and gives you no chance to look before it lands.

This tool sits in between. Paste the files you want to change as one bundle, write a single regular expression and its replacement, and get back a unified diff showing exactly which lines move in which files. Nothing is written anywhere — the diff is a preview you read, copy, or feed to git apply. Everything runs locally in your browser, so the code you paste never leaves the page.

How the bundle is split

The paste is cut into files at marker lines. By default (Auto-detect) any of these work, and you can mix them in one paste:

MarkerExample
equals=== src/a.js ===
dashes--- src/a.js ---
arrow==> src/a.js <== (what tail -n +1 src/*.js prints)
comment# file: src/a.js or // file: src/a.js

If your bundle uses something else, pick Custom pattern and supply a marker regex with one capture group for the path, such as ^@@@ (\S+)$. Pick No markers to treat the whole paste as a single file. Any content before the first marker is treated as a file named input.

A quick way to build a bundle on the command line:

tail -n +1 src/*.js

Worked example

Paste this, with Find \boldName\b and Replace with newName:

=== src/a.js ===
const oldName = 1;
use(oldName);
=== src/b.js ===
const other = 2;

The diff preview comes back as:

# 2 replacements in 1 of 2 files
--- a/src/a.js
+++ b/src/a.js
@@ -1,2 +1,2 @@
-const oldName = 1;
-use(oldName);
+const newName = 1;
+use(newName);

src/b.js has no match, so it does not appear at all — switch on List untouched files too to see it reported as # unchanged: src/b.js. Switch Output to Full rewritten files to get the whole bundle back with the edit applied, or to JSON report for per-file replacement counts.

Capture groups

The replacement understands $1, $2, … for numbered groups, ${name} for named groups ((?P<name>…)), and $$ for a literal dollar sign. Reformatting American dates to ISO is a one-liner: find (\d{2})/(\d{2})/(\d{4}), replace with $3-$1-$2. Braces are worth using whenever a digit could run into the following text — ${1}0 means "group 1 then a zero", while $10 means "group 10".

Matching options

Limits

FAQ

Can I paste the output straight into git apply?

Yes. The diff uses standard --- a/path / +++ b/path headers and @@ hunks, and the leading # summary line is treated as preamble, so git apply and patch -p1 both accept it. The paths in the diff are exactly the paths from your marker lines, so make sure those are repo-relative before you apply.

Why did my lookahead or backreference pattern fail?

The matcher is the Rust regex engine, which has no backtracking. That guarantees every pattern runs in time linear in the input — no pattern can lock up the page — but it also means lookaround ((?=…), (?!…), (?<=…)) and backreferences inside the pattern (\1) are not available. You can usually restructure the pattern with capture groups: instead of foo(?=bar)newfoo, use foo(bar)newfoo$1.

How do I replace text that spans several lines?

Turn on . matches newlines (s) so . crosses line boundaries, then a pattern like <a>.*?</a> will match across lines. Use the non-greedy .*? rather than .* unless you really want the match to run to the last occurrence in the file. ^ and $ match every line (m) is the complementary flag: it makes the anchors line-based instead of file-based.

Nothing changed — what went wrong?

The summary line tells you: # 0 replacements in 0 of N files. If N is 1 when you pasted several files, your markers were not recognised — check the File markers setting, or pick No markers deliberately. If N looks right, the pattern itself did not match: try Ignore case, or switch on Plain text if your search string contains regex metacharacters such as ., (, [ or $.

Is anything uploaded?

No. The tool is compiled to WebAssembly and runs entirely inside your browser tab. The files you paste, the pattern and the diff never travel to a server, which makes it safe for private source code and for logs you are redacting.

Can I run this from the command line?

Yes — the same block ships in the gizza CLI, so you can pipe a bundle through it in a script or call it from an agent. The generated example above the FAQ is copy-paste runnable, and gizza describe regex-codemod prints every parameter with its default.

Developer & Automation Access

Run it from the terminal

Same engine as this page, headless — via the gizza CLI:

gizza tool regex-codemod "=== src/a.js ===
const oldName = 1;
use(oldName);
=== src/b.js ===
const other = 2;" 'pattern=\boldName\b'

New to the CLI? Get gizza →

Open it by URL

Pre-fill and auto-run this tool with query parameters — the names match the API/CLI:

https://gizza.ai/tools/regex-codemod/?text=%3D%3D%3D%20src%2Fa.js%20%3D%3D%3D%0Aconst%20oldName%20%3D%201%3B%0Ause%28oldName%29%3B%0A%3D%3D%3D%20src%2Fb.js%20%3D%3D%3D%0Aconst%20other%20%3D%202%3B&pattern=%5CboldName%5Cb&replacement=newName&literal=true&ignore_case=true&multiline=true&dot_matches_newline=true&whole_word=true&replace_all=true&file_marker=auto&marker_regex=%5E%40%40%40%20%28%5CS%2B%29%24&output=diff&context=3&include_unchanged=true

Machine-readable descriptor: tool.json — title + parameters JSON Schema for agents.