fastmod: Fast regex-based replacements for codebases


fastmod overview

fastmod is a fast partial replacement tool designed for codebases. It uses the Rust regex crate to find patterns and replace them across files, with sensible defaults and a few handy flags for common tasks.

Quick start: simple replacements

Replace a regex in all files of the current directory, skipping ignored files:

fastmod {{regex}} {{replacement}}

This runs the replacement in every file under the current directory, respecting .ignore and .gitignore patterns. If you’re just trying something small, this is the simplest way to experiment.

Case-insensitive replacements

To perform a case-insensitive replacement in specific files or directories, use:

fastmod --ignore-case {{regex}} {{replacement}} -- {{path/to/file path/to/directory ...}}

The —ignore-case flag applies to the regex search, not to the filesystem paths. It’s handy when you know the target strings may vary in case.

Directory-scoped, with glob filters

If you want to search within a directory and limit candidate files by a glob pattern, you can combine —dir and —iglob:

fastmod {{regex}} {{replacement}} --dir {{path/to/directory}} --iglob {{'**/*.{js,json}'}}

This restricts work to files matching the glob, which can reduce accidental edits and speed up the run.

Exact-string replacements (no regex)

When you need to replace a literal string (no regex interpretation), use —fixed-strings:

fastmod --fixed-strings {{exact_string}} {{replacement}} --extensions {{json,js}}

This is safer when you know the exact text and want to avoid regex metacharacters triggering unintended matches.

Non-interactive, accepted changes only

To perform replacements without prompting for confirmation (and to disable regex mode), use:

fastmod --accept-all --fixed-strings {{exact_string}} {{replacement}}

This is convenient in automation, CI, or when you’re confident about the change. Note that —accept-all bypasses the interactive prompt, so review your exact_string and replacement carefully.

If you also want to see which files changed without stopping the run, add —print-changed-files:

fastmod --accept-all --print-changed-files --fixed-strings {{exact_string}} {{replacement}}

This helps you verify the impact in large codebases.

Practical examples

  • Replace a deprecated API name across a repository:
fastmod --ignore-case "oldApiName" "newApiName" -- {{src tests}}
  • Update a version string in package.json and related config files using exact strings:
fastmod --fixed-strings "version": "1.2.3" "version": "1.3.0" --extensions json
  • Bulk rename a function in JavaScript files without touching other languages:
fastmod --ignore-case --dir ./frontend --iglob '**/*.js' "function oldName(" "function newName(" 

Common pitfalls to watch out for

  • Regex vs. fixed strings: If you don’t need regex features, prefer —fixed-strings to avoid unintended matches.
  • Over-broad patterns: A regex like ”.*” can touch every file. Start with a narrow pattern and broaden it only after tests.
  • Ignored files: .ignore and .gitignore influence what fastmod processes. If a file is ignored, it won’t be touched, even if you expected it to be included.
  • Backups and review: For high-risk changes, consider running in a test branch and using —print-changed-files to review edits before committing.

Workflow tips

  • Start local: run without —accept-all to see what would change and confirm each replacement.
  • Narrow scope: use —dir and —iglob to limit the target set to relevant files.
  • Logs: combine with —print-changed-files to build a changelist you can review.

When to choose fastmod

  • You need high-speed, pattern-based edits across many files.
  • You want a safer alternative to ad-hoc sed scripts with built-in ignore handling.
  • You prefer a Rust-powered tool with a straightforward CLI and common options.

Summary

fastmod offers a pragmatic approach to large-scale text replacements with sensible defaults, support for case-insensitive and fixed-string modes, and helpful options to review changes before applying them. Start simple, test in a controlled scope, and use the non-interactive modes only when you’re confident in the exact strings involved.

See Also