On this page
surgically rewrite git history to remove or replace sensitive content: file and match rewrite the commits, trees and blobs of a range the caller selects (--from or --entire-history), run applies a recipe of such operations in one coordinated pass, and verify only reads -- it confirms that the patterns named on its command line are absent from the whole object store
#safegit scrub
surgically rewrite git history to remove or replace sensitive content: file and match rewrite the commits, trees and blobs of a range the caller selects (--from or --entire-history), run applies a recipe of such operations in one coordinated pass, and verify only reads -- it confirms that the patterns named on its command line are absent from the whole object store
#scrub file
replace or remove a specific file across every commit in a SELECTED RANGE of history -- --from
Effect: mutating · consequential (prompts before running; --approve-consequential skips)
#Flags
| Name | Short | Type | Presence | Env | Description |
|---|---|---|---|---|---|
--reason | str | required | mandatory audit trail message explaining why this scrub operation is needed | ||
--remap-shas-in | list[str] (unique) | optional | glob selecting files whose full 40-character commit hashes are remapped to the rewritten SHAs during the walk, keeping hash-referencing files like JSONL changelogs self-consistent at every commit (repeatable; same matching semantics as --scope; not applied inside submodule histories) | ||
mode | choice | required | Selection (not typed as a flag). Elect exactly one of --delete, --replace-with. what happens to the file at every commit in range | ||
--delete | required | Elects mode = delete. delete the file from every commit in range | |||
--replace-with | str | required | Elects mode = replace-with. replace the file's contents with those of a sanitized file Its value: path to the file whose contents replace the target in every commit; resolved against YOUR current directory, unlike the target argument, which is repository-relative | ||
range | choice | required | Selection (not typed as a flag). Elect exactly one of --from, --entire-history. how much of the history is rewritten | ||
--from | str | required | Elects range = from. rewrite the commits from a given commit forward Its value: first commit hash to include when rewriting history | ||
--entire-history | required | Elects range = entire-history. rewrite every commit from the root of the repository to HEAD |
#Arguments
| Name | Type | Presence | Description |
|---|---|---|---|
file | str | required | repository-relative path to the file that should be scrubbed from history |
#scrub match
replace every occurrence of a regex pattern in the blobs, commit messages and tag annotations of a SELECTED RANGE of history -- --from
Effect: mutating · consequential (prompts before running; --approve-consequential skips)
#Flags
| Name | Short | Type | Presence | Env | Description |
|---|---|---|---|---|---|
--pattern | str | required | regular expression pattern to search for across all blobs in history | ||
--reason | str | required | mandatory audit trail message explaining why this scrub operation is needed | ||
--scope | str | optional | glob pattern limiting which file paths are searched (e.g. '.env', 'config/*') | ||
--remap-shas-in | list[str] (unique) | optional | glob selecting files whose full 40-character commit hashes are remapped to the rewritten SHAs during the walk, keeping hash-referencing files like JSONL changelogs self-consistent at every commit (repeatable; same matching semantics as --scope; not applied inside submodule histories) | ||
substitution | choice | required | Selection (not typed as a flag). Elect exactly one of --replace, --mangle. what replaces each match | ||
--replace | str | required | Elects substitution = replace. substitute a literal string for every match Its value: literal string to substitute for each regex match found in history | ||
--mangle | required | Elects substitution = mangle. substitute random printable ASCII of the same length for every match | |||
range | choice | required | Selection (not typed as a flag). Elect exactly one of --from, --entire-history. how much of the history is rewritten | ||
--from | str | required | Elects range = from. rewrite the commits from a given commit forward Its value: first commit hash to include when rewriting history | ||
--entire-history | required | Elects range = entire-history. rewrite every commit from the root of the repository to HEAD |
#scrub run
execute a multi-operation scrub recipe from a TOML file, applying all pattern replacements and file removals across history in a single coordinated pass with topological commit ordering, overlap detection between operations, and automatic verification that no matched content survives in the rewritten object store — use --diff to preview all changes as unified diffs before committing to the rewrite
Effect: mutating · consequential (prompts before running; --approve-consequential skips)
#Flags
| Name | Short | Type | Presence | Env | Description |
|---|---|---|---|---|---|
--reason | str | required | mandatory audit trail message explaining why this scrub operation is needed | ||
--diff, --no-diff | bool | optional | preview what would change without modifying any objects, showing unified diffs; omitted means the rewrite is performed | ||
--limit | int | optional | maximum number of blob diffs to show in --diff mode; omitted means 50 | ||
--remap-shas-in | list[str] (unique) | optional | glob selecting files whose full 40-character commit hashes are remapped to the rewritten SHAs during the walk, keeping hash-referencing files like JSONL changelogs self-consistent at every commit (repeatable; same matching semantics as --scope; not applied inside submodule histories) | ||
range | choice | required | Selection (not typed as a flag). Elect exactly one of --from, --entire-history. how much of the history is rewritten | ||
--from | str | required | Elects range = from. rewrite the commits from a given commit forward Its value: first commit hash to include when rewriting history | ||
--entire-history | required | Elects range = entire-history. rewrite every commit from the root of the repository to HEAD |
#Arguments
| Name | Type | Presence | Description |
|---|---|---|---|
recipe | str | required | path to the TOML recipe file containing scrub operations |
#scrub verify
confirm that the patterns named on the command line -- repeatable --pattern regexes, the operations of a scrub recipe file, or both -- are absent from every object in the git object store, scanning blobs, commit messages, and tag annotations and reporting detailed per-pattern pass or fail results with match locations for any violations found
Effect: read_only
#Flags
| Name | Short | Type | Presence | Env | Description |
|---|---|---|---|---|---|
--pattern | list[str] (unique) | optional | regular expression that must be absent from every object in the repository (repeatable) | ||
--scope | str | optional | glob pattern limiting which blob file paths a --pattern match counts against (e.g. '.env', 'config/*'); recipe operations carry their own scope in the recipe file |
#Arguments
| Name | Type | Presence | Description |
|---|---|---|---|
recipe | str | optional | path to a scrub recipe TOML file whose operations' patterns are verified; the format is the one 'scrub run' takes, and its replace/mangle/depends_on fields are ignored here because verification substitutes nothing |
#Constraints
The framework enforces these before the command runs.
| Rule | What it requires |
|---|---|
verify-input | At least one of --pattern (when supplied), recipe (when supplied). |
verify-scope | --scope requires --pattern. |