Your first rename
Goal: turn camera names into holiday names. The folder contains IMG_0042.JPG, IMG_0043.JPG, IMG_0044.JPG and a few .txt notes you want to leave alone.
- Click Select Folder… (or drag the folder onto the window).
- Under 1. Filters, click + Add filter, choose
extensionand typejpg. Only the photos remain in the list. - Under 2. Rename steps, click + Add rename step. Choose Replace text: find
IMG_, replace withHoliday_. - Add a second step, Change case →
lowercase extension. - Check the preview. Untick any file you want to skip.
- Click Apply with Dry run ticked (the default). RenForge only reports what it would do.
- Happy? Untick Dry run and click Apply again. Keep Create undo log ticked.
The window
| Area | What it does |
|---|---|
| Top bar | Select Folder…, Recent… (last 5 folders), Refresh, Include subfolders, A− / A+ (text size), Auto / Light / Dark (colours), Help, About. You can also drag a folder onto the window. Window size, text size and colour mode are remembered. |
| 1. Filters | Choose which files are affected (optional). |
| 2. Rename steps | Describe how names change. Each step has a checkbox (on/off), ▲ ▼ (reorder), ✕ (remove) and Presets… |
| 3. Options | Also rename related files (sidecars, RAW+JPEG pairs); Edit file types… opens the list of extensions. |
| Preview (right) | Original name next to the new name (the changed part is highlighted), a checkbox per file and ⚠ warnings. Order sorts the list; drag the divider in the column header to resize the columns. A summary line shows what will rename, what is invalid, duplicated or taken, and what stays unchanged; click a problem count to see just those files. |
| Bottom bar | Dry run, Create undo log, Undo… and Apply. |
Filters
Filters decide which files appear in the preview. With no filters every file in the folder is included. A filter with an empty value is ignored, and only the file name is checked, never the folder path.
| Mode | Matches when… | Example value |
|---|---|---|
| contains | the name contains the text | holiday |
| starts with | the name begins with the text | IMG_ |
| ends with | the name ends with the text (extension included) | _edit.jpg |
| extension | the extension equals the value | png, .png, tar.gz |
| regex | the name matches a regular expression | ^IMG_\d{4}\.jpe?g$ |
- Match ALL (AND): a file must satisfy every filter. Match ANY (OR): at least one.
- Case sensitive is off by default, so
PNGandpngare the same. - An invalid regular expression is flagged in red and matches nothing.
Filters: starts with IMG_ AND extension jpg
Selecting & sorting
- Every matching file has a checkbox. Untick files to leave them alone. Select All / Deselect All act on the files currently listed.
- Show: All / Changed / Problems narrows the preview. Changed hides unchanged and unticked files; Problems shows only files with a ⚠ (invalid name, duplicate name, or a name already taken by a file that is not being renamed). The buttons show live counts, and Problems turns amber when something needs fixing.
- The header shows the counts, for example
42 matches · 38 selected · 100 total files. - The list scrolls smoothly even with tens of thousands of files, and folders are scanned in the background.
- Order sorts by name (natural:
img2beforeimg10), date (capture date, else modified) or size. Numbering follows the order shown. - Rows marked
(unchanged)already have the target name and are skipped.
Rename steps
Steps form a pipeline: the result of step 1 is the input of step 2, and so on. Use ▲ ▼ to reorder, the checkbox to switch a step off without deleting it, and ✕ to remove it. Order matters: Prepend then Change case is not the same as the reverse. Each step shows an Example line (the first selected file, before → after that step), and the arrow button collapses a step to one line.
Replace text & Remove text
Replace swaps every occurrence of A for B (leave B empty to delete). Remove deletes the text. Matching is case sensitive.
Prepend, Append & Insert before extension
Change extension
With or without the dot; empty removes the extension. .tar.gz, .tar.bz2, .tar.xz and .tar.zst count as one extension, and dotfiles such as .bashrc have none.
Change case
Changes the case of the name; the extension is untouched unless you pick an extension option.
| Option | Before | After |
|---|---|---|
| lowercase | My FILE.TXT | my file.TXT |
| UPPERCASE | my file.txt | MY FILE.txt |
| Title Case | the don't STOP-me_now.mp3 | The Don't Stop-Me_Now.mp3 |
| Sentence case | hELLO wORLD.txt | Hello world.txt |
| snake_case | My Great-Photo (final).JPG | my_great_photo_final.JPG |
| kebab-case | My Great Photo.jpg | my-great-photo.jpg |
| lowercase extension | A.JPG | A.jpg |
| UPPERCASE extension | a.jpg | a.JPG |
Clean up
| Option | Before | After |
|---|---|---|
| Collapse repeated spaces | a b c.txt | a b c.txt |
| Trim spaces around name | a b .txt | a b.txt |
| Remove accents | Café Crème.txt | Cafe Creme.txt |
| Dots and underscores to spaces | My.Cool_Song.mp3 | My Cool Song.mp3 |
| Remove copy markers | report (2).pdf, report - Copy.pdf, Copy of report.pdf | report.pdf |
Numbering
Number files gives every selected file a sequence number, in the order shown. Untick a file and the others are renumbered.
- A (template):
{n}= number,{n:3}= padded to 3 digits,{name}= original name without extension. The extension is kept automatically. Empty means{name}_{n:3}. - B (start, step):
1starts at 1;100,10gives 100, 110, 120… - Tick Restart numbering in each folder when Include subfolders is on and each folder should start again.
Regex
A regular expression (regex) describes a pattern. In Regex replace, A is the pattern and B the replacement; $1, $2… insert the parts captured by brackets. The whole name, extension included, is matched. Start the pattern with (?i) to ignore case. The regex filter uses the same syntax and follows the Case sensitive box. The syntax is Go's RE2.
| Pattern (A) | Replacement (B) | Before → after |
|---|---|---|
IMG_(\d+) | Holiday-$1 | IMG_0042.jpg → Holiday-0042.jpg |
\s+ | _ | my file.txt → my_file.txt |
^(\d{4})-(\d{2})-(\d{2}) | $3.$2.$1 | 2024-06-01_x.jpg → 01.06.2024_x.jpg |
\[.*?\] | (empty) | a [1080p] b.mp4 → a b.mp4 |
(?i)\.jpe?g$ | .jpg | pic.JPEG → pic.jpg |
Tip: follow a regex that leaves double spaces with Clean up → Collapse repeated spaces.
Photos & EXIF dates
Rename from template builds the whole name from tokens. The capture date is read from the EXIF data inside the picture (JPEG and TIFF-based RAW formats such as CR2, NEF, DNG, ARW). Files without EXIF (and HEIC/CR3, whose metadata RenForge cannot read yet) use their modified date, so the template still works. EXIF is only read, never written.
| Token | Meaning |
|---|---|
{date} / {date:YYYY-MM-DD_HHmmss} | capture date; format letters YYYY YY MM DD HH mm ss (default YYYY-MM-DD) |
{modified} / {modified:YYYY} | file modification date, same format letters |
{n} / {n:3} | sequence number, padded to 3 digits |
{name} | original name without extension |
{parent} | name of the folder the file is in |
{camera} {make} {lens} {iso} | from EXIF (shows unknown when missing) |
Photos taken on 1 June 2024, in this order:
{n} in the template when several photos can share a second (bursts, two cameras). RenForge warns when it is missing. Reading dates for a large folder happens in the background; a progress bar appears above the list and the preview fills in when it is done.Image sequences
Shift frame numbers adds an offset to the number at the end of the name. A is the offset (+1000, -1), B an optional padding (empty keeps the current width). Only the last number changes.
Shifts such as +1 are fine: RenForge moves all files out of the way first. If a file that is not part of the batch occupies a target, the whole affected chain is skipped and reported, so nothing is half-shifted. Undo restores such shifts too. A frame that would become negative is left unchanged.
Presets
Click Presets… (next to + Add rename step):
- Load a built-in or your own preset. Loading replaces the current filters and steps.
- Save current as… stores your filters, steps and match options under a name (saving under an existing name asks before replacing it).
- Delete removes your own presets; built-ins cannot be deleted.
| Built-in preset | Example |
|---|---|
| Clean up downloads | My.Cool_Song (1).MP3 → My Cool Song.MP3 |
| Lowercase names and extensions | HOLIDAY.JPG → holiday.jpg |
| kebab-case names | My Great Photo.JPG → my-great-photo.jpg |
| Photos: Trip_0001 | DSC_9981.jpg → Trip_0001.jpg |
| Photos by date | IMG_4021.CR2 → 2024-06-01_001.CR2 |
| Photos by date and time | IMG_4021.CR2 → 20240601_140509_01.CR2 |
| Image sequence: start at frame 1001 | shot.0001.exr → shot.1001.exr |
| Add number suffix | beach.jpg → beach_001.jpg |
Your presets are plain JSON files in renforge/presets inside your user configuration folder (macOS ~/Library/Application Support, Windows %AppData%, Linux ~/.config). Copy them to another computer to share them.
Dry run & safety checks
When you click Apply, RenForge checks every selected file and skips any that would cause trouble. Dry run is ticked by default and changes nothing.
| Problem | Meaning |
|---|---|
| Invalid name | Empty; contains \ / : * ? " < > | or control characters; starts with a space; ends with a space or dot; longer than 255 bytes; or a reserved Windows name (CON, NUL, COM1…). These rules keep files usable on every system. |
| Conflict | Two selected files in the same folder would get the same name. |
| Target exists | A different file that is not being renamed already has the new name. (A target held by a file that is itself being renamed away, as in +1 frame shifts or swaps, is fine.) |
Allowed: swapping names (a.txt ↔ b.txt), and changing only the letter case (IMG.jpg → img.jpg), even on Windows and macOS where names are case-insensitive.
How it renames: each file first moves to a temporary name (.renforge_tmp_…), then to its final name. Existing files are never overwritten. If a file cannot be renamed it goes back to its old name; in the very unlikely case that fails too, the result dialog lists the file left under its temporary name.
Undo
Keep Create undo log (CSV) ticked. RenForge asks where to save the log (it suggests the folder you are working in) and writes it after the rename, so it records what really happened.
- Click Undo…. RenForge offers the most recent log, or lets you choose any log file.
- It shows how many renames can be restored and lists any it cannot: not found (moved or deleted since) or in use again (another file now has the original name).
- Confirm, and the original names come back.
A log from a dry run contains no completed renames, so there is nothing to undo. The CSV has the columns old_path, new_path, old_name, new_name, status, reason and opens in any spreadsheet.
Recipes
| I want to… | Filters | Steps |
|---|---|---|
| Replace underscores with spaces | – | Replace _ → (space) |
| Remove “ (1)” copies | contains (1) | Clean up → Remove copy markers |
| Add a date prefix | – | Prepend 2024-06-01_ |
| Mark finals | extension mp4 | Insert before extension _final |
Convert .jpeg to .jpg | extension jpeg | Change extension jpg |
| Strip a download-site tag | contains [site.com] | Remove text [site.com] |
| Lower-case every extension | – | Change case → lowercase extension |
| Name photos by date | extension jpg (order: Date, oldest first) | Rename from template {date:YYYY-MM-DD}_{n:3} |
| Keep RAW + XMP + JPG together | extension cr2, option related files on | any step, for example Prepend Trip_ |
| Start a render sequence at 1001 | extension exr | Shift frame numbers +1000 |
| kebab-case everything | – | Change case → kebab-case |
Troubleshooting
| Problem | What to do |
|---|---|
| The preview is empty | Check that a folder is selected and the filters are not too strict. Tick Include subfolders if the files are inside sub-folders. |
| ⚠ conflict | Two files would get the same name. Make the name more unique (add {n}) or untick a file. |
| ⚠ target exists | A file with the new name is already in that folder. Rename or move it first. |
| Files changed in another program | Click Refresh. |
| “Cannot undo” | The files in the log were moved, deleted or renamed again. Nothing was changed. |
A .renforge_tmp_… file is left | Only after a failed rename that also could not be restored. The result dialog names the file; rename it by hand (the undo log has its original name). |
| macOS: “cannot be opened” | If the build is not notarized, run xattr -dr com.apple.quarantine renforge.app. |
| Windows: SmartScreen warning | Windows builds are not code-signed yet: More info → Run anyway. |
Still stuck? Report a bug with your OS, the build you used, and the steps to reproduce.
Build from source
Prerequisites
- Go: the version listed in
go.mod(or newer) from go.dev/dl. - A C compiler and graphics libraries, because the UI toolkit (Fyne) needs them:
- macOS: Xcode command line tools:
xcode-select --install - Windows: MinGW-w64 gcc, for example via MSYS2
- Linux (Debian/Ubuntu):
sudo apt install gcc pkg-config libgl1-mesa-dev xorg-dev libasound2-dev
- macOS: Xcode command line tools:
Run, test and build
# get the code $ git clone https://github.com/blackarck/renforge.git $ cd renforge # run the app $ go run . # run the tests (the rename engine is UI-free and well covered) $ go test ./... # build a plain binary $ go build -o renforge .
A plain go build has no application icon, and on Windows it opens a console window. For a proper app, use the Fyne packager; it reads FyneApp.toml and icon.png:
$ go install fyne.io/tools/cmd/fyne@latest $ fyne package -os darwin # renforge.app $ fyne package -os windows # renforge.exe, no console window $ fyne package -os linux # renforge.tar.xz
Project layout
| Path | Contains |
|---|---|
renforge.go, rows.go, presets.go, help.go, icon.go | The Fyne user interface, in-app help and window icon |
internal/rename | Rename steps, name validation, plan building, sidecars, two-phase apply |
internal/filter | Filter rules |
internal/undo | Undo log (CSV) reading, writing and reverting |
internal/meta | Cached file size, dates and EXIF |
internal/preset | Saved and built-in presets |
docs/ | User guide, release and signing notes |
Want to help? Read CONTRIBUTING.md or jump to how to support the project.