| name | derustfmt |
| description | Remove rustfmt formatting and import-order noise from staged Rust diffs while preserving semantic changes. Use when preparing diffs for human review, cleaning up mixed format/semantic changes, or when `git diff` is cluttered with whitespace-only hunks. |
derustfmt — Remove rustfmt noise from Rust diffs
Skill type: Rigid
Follow the procedure exactly. Do not skip verification steps, do not change semantic code, do not touch non-Rust files, and do not commit during cleanup without explicit approval.
When to apply
- User has staged Rust diffs with mixed semantic changes and formatting noise
- User wants to prepare diffs for human review
git diff --stat shows large diffs but git diff -w --stat shows much smaller
Do NOT apply to:
- New files (
src/mgpu/*) — they have no "base format" to restore
- C/CUDA/C++/build files — this skill targets Rust only
- Files with zero
diff -w reduction — nothing to clean
Methodology
Phase 1: Quantify noise
For each repo with staged changes:
git -C <repo> diff --cached --stat -- '*.rs'
git -C <repo> diff --cached --stat -- '*.rs'
git -C <repo> diff --cached -w --ignore-blank-lines --stat -- '*.rs'
Classify each file:
| Type | Criteria | Action |
|---|
| Pure noise | diff -w shows no change | Restore to HEAD |
| Mixed | diff -w shows real changes but normal diff is much larger | Hunk-level de-noise |
| New file | No base version in HEAD | DO NOT TOUCH |
| Already clean | Normal diff ≈ diff -w | Skip |
Phase 2: Backup
Create a timestamped backup of all staged .rs files BEFORE any modification:
mkdir -p /path/to/backups/derustfmt-$(date +%Y%m%dT%H%M%S)
for repo in <repos>; do
git -C "$repo" diff --cached --name-only -- '*.rs' | while read f; do
mkdir -p "$backup_dir/$repo/$(dirname $f)"
cp "$repo/$f" "$backup_dir/$repo/$f"
done
done
Phase 3: Pure noise files
For files where diff -w --ignore-blank-lines is effectively empty (import reorder or whitespace only):
git -C <repo> checkout HEAD -- <file>
Verify:
- The file now shows zero staged diff
- The semantic changes from other files still compile together (cargo check if available)
Phase 4: Hunk-level de-noise (for mixed files)
This is the core algorithm.
4a: Extract semantic-only patch
git -C <repo> diff --cached -U0 -w --ignore-blank-lines -- <file> \
> /tmp/semantic_${filename}.patch
-U0 (zero context) avoids context mismatch from surrounding format changes.
4b: Restore base, reapply semantic patch
cp <file> /tmp/current_${filename}.rs
git -C <repo> checkout HEAD -- <file>
git -C <repo> apply --cached /tmp/semantic_${filename}.patch
If git apply succeeds: the semantic changes have been cleanly separated. Verify with Phase 5.
4c: If semantic patch fails (large files, import reorder)
Fall back to hunk-level equivalence testing:
Implementation approach: Script that iterates hunks, for each hunk:
for each hunk:
1. apply base version of this hunk
2. rustfmt --emit stdout both backup and current → compare
3. if equal: hunk was noise, keep base
4. if not equal: hunk has semantics, revert to staged content for this hunk
This is expensive for large files. Prioritize by noise level.
Phase 5: Two-layer verification
Layer 1: Whitespace/format equivalence
rustfmt --edition 2021 --emit stdout backup.rs > /tmp/a.rs
rustfmt --edition 2021 --emit stdout cleaned.rs > /tmp/b.rs
diff /tmp/a.rs /tmp/b.rs
Important: rustfmt may fail on isolated lib.rs files due to missing cfg/include!/macro context. This is acceptable — note it and skip rustfmt verification for those files, relying on diff -w comparison instead.
Layer 2: Import order normalization
rustfmt does NOT canonicalize import order. Two files can both be "rustfmt-compliant" with different use orderings.
Validation approach:
grep '^use ' backup.rs | sort > /tmp/use_backup.txt
grep '^use ' cleaned.rs | sort > /tmp/use_cleaned.txt
diff /tmp/use_backup.txt /tmp/use_cleaned.txt
Import restoration rule: Restore the original file's import order/style, only adding genuinely new symbols needed by the semantic diff.
Phase 6: Final validation
git -C <repo> status -- <file>
git -C <repo> diff --cached --check -- <file>
git -C <repo> diff --cached -w --ignore-blank-lines -- <file> \
> /tmp/cleaned_semantic.diff
diff <(filter_hunks backup_semantic.diff) /tmp/cleaned_semantic.diff
git -C <repo> diff --cached --stat -- <file>
git -C <repo> diff --cached -w --ignore-blank-lines --stat -- <file>
Phase 7: Report
Summarize what was done:
| File | Before (diff lines) | After (diff lines) | Noise removed | Action |
|---|
| src/foo_lib.rs | 1200 | 150 | 1050 | Hunk-level |
| src/baz_util.rs | 5 | 0 | 5 | Restored to HEAD |
| ... | ... | ... | ... | ... |
Key principles
- Never change semantics. If uncertain about a hunk, keep it.
- Backup first, always. Every file must have a pre-cleanup backup.
- rustfmt is NOT your canonical formatter for imports. It won't reorder
use statements into a canonical order. Handle import noise separately.
- New files are never noisy. If the file doesn't exist in HEAD, all its content is "new" by definition.
- Isolated-file rustfmt may fail on lib.rs. Files with
#[cfg(...)], include!(), or macro-heavy code may fail isolated rustfmt. Use diff -w as fallback verification.
- Don't commit during cleanup. Only stage. Let the user review and commit.
Constraints
- Do NOT run
cargo fmt --all — it may fail due to private registries, and sweeping formatting defeats the purpose
- Do NOT touch
.c/.h/.cpp/.cu/.py/build files
- Do NOT modify the
.cargo/config.toml or registry configuration
- Do NOT push or commit without explicit user request
- Preserve the existing project formatting style (which may differ slightly from rustfmt defaults)