A Delphi VCL application for committing and pushing changes to multiple Git repositories simultaneously with a single commit message.
GitBatchCommit simplifies the workflow of updating multiple projects when a shared library or component is modified. Rather than opening each repository individually in a Git client, this tool allows you to select multiple repositories, enter a single commit message, and push all changes in one operation.
-
Repository Management - Add and remove Git repositories from a persistent list
-
Delete Repositories - Dispose of finished repositories rather than merely forgetting them: File > Delete Selected... (or the list's right-click menu) deletes any combination of the list entry, the remote repository on GitHub, and the local folder. Irreversible options require the word
DELETEto be typed; the local folder goes to the Recycle Bin -
Drag and Drop - Drag repository folders from Windows Explorer directly onto the application. Dropping a non-Git folder offers to initialise it, create a GitHub repository, and push. Every question is asked first, then the work runs in the background
-
New Repository Initialisation - Project type selection (Delphi by default) with
.gitignoregeneration for Delphi, C/C++, C#, Java, Python, JavaScript, TypeScript, Go, Rust, and HTML -
GitHub Integration - Create new GitHub repositories and push directly from the application. GitHub is the only host the application creates, deletes or changes the visibility of
-
Codeberg - Removed in 1.9.0. A repository whose remote is on Codeberg (or any other non-GitHub host) shows the provider Other; it can still be committed, pushed and pulled, and can be migrated to GitHub
-
Migrate to GitHub - Move a repository's remote from another host (Codeberg, for example) to GitHub via the GitHub menu. Creates the new repo on GitHub, preserves the previous origin as a secondary remote for safety, swaps
origin, and pushes all branches and tags -
Visibility Management - Change a GitHub repository's visibility (public/private) via right-click context menu; the change runs in the background
-
Parallel Status Refresh - Repository status checks run in parallel on background threads, keeping the UI responsive
-
Status Detection - Automatically detects repository status:
- Clean - No local changes, and level with the remote
- Modified - Local uncommitted changes present (modified, staged, untracked, or deleted files). A repository that is also behind its upstream reads
Modified - n to pull, and Commit & Push refuses it until the pull is done - Conflicted - Unmerged paths, or an unfinished merge/rebase/cherry-pick/revert/bisect. Commit & Push refuses to act on these
- Pull Required - The upstream has commits this clone does not, shown with the count
- Push Required - This clone has commits the upstream does not, shown with the count
- Diverged - Both of the above, shown as
(+ahead/-behind) - Error - Repository not accessible or not a valid Git repo
A branch that has commits and a remote but no upstream reads
Push Required (not on the remote); Push Only and Commit & Push set its upstream. When the remote could not be reached during a refresh,- fetch failedis appended, and the comparison is against the last fetched state.The working tree is assessed with
git status --porcelain, which covers modified, staged, untracked, deleted and renamed files as well as dirty submodules. Unmerged paths are identified from the porcelainXYcodes (DD,AU,UD,UA,DU,AA,UU) and take priority over everything else, because staging them withadd -Awould commit the conflict markers. Changes that are only build output (.dcu,.map, platform build folders and so on;.exeand.hppare deliberately NOT treated as build output, since plenty of repositories track tooling binaries and C/C++ headers on purpose) do not count as modifications. The relationship to the remote is measured withgit rev-list --left-right --count @{upstream}...HEAD, which is locale-independent and reports both directions at once. It is measured for every repository, whatever the state of the working tree: a repository can be modified and behind at once, and the single status slot can only name one of the two, so the pull it owes is carried in the status text instead. -
Branch Display - Shows the current branch for each repository
-
Remote Provider Display - Shows the remote provider (GitHub, Other, None) for each repository. This is the provider of the remote the branch actually tracks (
branch.<name>.remote), falling back tooriginwhen a branch has no upstream - not alwaysorigin, because that is the remote a push or pull will contact and the one the ahead/behind counts are measured against -
Version Display - Shows the project version extracted from Delphi
.dprojfiles (reads the root.dproj, falling back to a subdirectory scan) - resolving the version the project's active configuration builds, not the highest number in the file -
Automatic Version Tagging - For Delphi projects, automatically creates and pushes Git tags based on the version number in the
.dprojfile when committing -
Batch Operations - Commit and push to multiple repositories with one click
-
Quick Selection - Buttons to select all, none, or only modified repositories
-
Shift-Click Selection - Hold Shift and click to select/deselect a range of repositories
-
Edit .gitignore - Right-click a repository to edit its .gitignore file
-
Column Sorting - Click any column header to sort; click again to reverse order
-
Status Filtering - Use View > Filter menu to show only repositories with specific status
-
Visual Styles - Use View > Style to switch between Aqua Light Slate (default), Carbon, Glow, Windows, Windows10 Dark and Windows11 Modern Light; the choice is remembered
-
Smart Button State - Commit & Push button only enabled when repositories are selected, commit message is entered, and no operation is running
-
Operation Log - Detailed log of all Git operations performed
-
Help System - Press F1 to view
Users Guide.md; Help > About for version info -
Colour-Coded Status - Repository rows are colour-coded by status (light green=Clean, light yellow=Modified, light orange=Pull Required, pale green=Push Required, light purple=Diverged, strong red=Conflicted, light red=Error)
-
Open in Git Client - Right-click to open repository in configured external Git client (e.g., Fork)
-
Safe Refusals - Declines rather than guessing when a repository's state cannot be established: an unfinished merge, rebase, cherry-pick, revert or bisect; unresolved conflicts; a detached or unborn HEAD; a branch that is behind its upstream and owes a pull; or a
git statusthat failed or timed out. Force Push additionally refuses when the remote has moved since the last refresh, or when no upstream commit was recorded at the last refresh -
Worktree and Submodule Support - The real Git directory is resolved with
git rev-parse, so linked worktrees and submodules (where.gitis a file, not a folder) are handled like any other repository, and can be added with File > Add Repository -
Background Work - Every Git operation runs off the UI thread. While one runs, every command that starts Git work is disabled and folders dropped on the window are ignored (drop them again afterwards). Closing the window while work is running asks whether to cancel it; cancellation takes effect between repositories
-
Open in Explorer - Right-click to open repository folder in Windows Explorer
-
Pull Operation - Right-click to pull changes from remote for the right-clicked repository, through the same preview-and-backup flow as Pull Selected (fast-forward only — refuses to create a merge commit on diverged history)
-
Commit Message History - Click the dropdown button next to the commit message to select from recent messages (up to 20 stored)
-
Commit Message Templates - Define reusable commit message templates via File > Templates; select from the templates dropdown (▽) next to the commit message
-
Commit Details - Click the "..." button to add a detailed description below the summary line (standard Git commit format)
-
Repository Groups - Assign repositories to groups for easy filtering; use the Group dropdown in the toolbar to filter by group
-
File Pattern Filtering - Optionally stage only files matching a pattern (e.g.,
*.pas) via File > Settings -
delphi-lookup Integration - Automatically triggers incremental reindexing of committed repositories for delphi-lookup symbol search (optional, auto-detected)
-
Pull Selected - Pull changes from remote for multiple selected repositories at once (with safeguards)
-
Resolve Conflicts - Resolve an in-progress merge by keeping your local version of every conflicted file (the incoming remote version of those files is discarded; files that merged cleanly keep the merged result), then commit and push. Repositories with no merge in progress are skipped; one in a rebase or cherry-pick is refused
-
Push Only - Push already-committed changes without creating a new commit (useful when local branch is ahead of remote)
-
Force Push - Force push to overwrite remote history when local code is the source of truth
-
Pull Safeguards - Multiple safety features protect your local code when pulling:
- Warning dialog clearly explains that local files may be modified
- Preview of incoming changes before pulling
- Automatic backup branch creation for every repository that has incoming changes (or whose preview failed); a repository whose backup branch cannot be created is not pulled
- Delphi 13 or higher (uses inline variable declarations and conditional expressions)
- Git installed and available in system PATH
- Windows 10 or later
| Component | Description | Source |
|---|---|---|
| FastMM5 | High-performance memory manager | https://github.com/pleriche/FastMM5 |
| OmniThreadLibrary | Background work runs on its thread pool (OtlParallel, used by MainFrm.pas and uGitRepoManager.pas) |
D:\gllOmniThreadLibrary (GITLAK fork) |
| VCL styles | Aqua Light Slate, Carbon, Glow, Windows10 Dark, Windows11 Modern Light - linked in via Custom_Styles; View > Style lists whatever is linked |
Included with RAD Studio ($(BDSCOMMONDIR)\Styles) |
| delphi-lookup | Optional: Delphi symbol indexer for auto-reindex after commits | https://github.com/JavierusTk/delphi-lookup |
| EurekaLog 7 | Exception reporting; compiled in under the EurekaLog conditional define |
https://www.eurekalog.com |
| ELExtraPlugIns | GITLAK EurekaLog plug-in aggregator, referenced by bare unit name from GitBatchCommit.dpr |
E:\EurekaLog |
Notes:
- To remove the FastMM5 dependency, remove
FastMM5from the uses clause inGitBatchCommit.dpr. The project will then use Delphi's default memory manager. - delphi-lookup integration is completely optional. GitBatchCommit works normally without it installed.
- The EurekaLog units are compiled from the EurekaLog installation (
Lib\andSource\Extras\, both on the IDE's Win64 library path). Do not keep local copies of EurekaLog source or.dcufiles in the project folder — the project directory is searched first, so a stale copy shadows the installed version and the build fails withF2051 Unit … was compiled with a different version of …. The cure is to delete the offending.dcu/.pasfrom the project root and rebuild. ELExtraPlugInsis resolved fromE:\EurekaLog, which is on the RAD Studio Win64 library search path - there is noin '<path>'clause and no project search-path entry. If that folder is moved or renamed, update both or the build fails withF1026 File not found.
- Download or clone the source files
- Open
GitBatchCommit.dprin Delphi - Build the project (Ctrl+F9) or Run (F9)
Method 1: Drag and Drop (Recommended)
- Open Windows Explorer and navigate to your Git repositories
- Select one or more repository folders
- Drag and drop them onto the GitBatchCommit window
- Valid repositories are added automatically. For each folder that is not a repository yet you are asked whether to initialise it and push it to a new GitHub repository (see Creating a New GitHub Repository); every question is asked first, then the work runs in the background
A folder already in the list is reported in the log as Already in the list. Folders dropped while an operation is running are ignored; drop them again when it has finished.
Method 2: Menu
- Select File > Add Repository
- Browse to a folder containing a Git repository (a
.gitfolder, or a.gitfile for a linked worktree or submodule) - The repository is added to the list and its local status is shown; adding does not contact the remote, so the comparison with the remote arrives with the next refresh
Trust: Git hooks in a listed repository run when this application commits or pushes there (versions before 1.9.0 disabled them with -c core.hooksPath=), and the repository's own Git configuration is honoured. Adding a repository to the list means trusting its hooks: do not add a folder you do not trust.
- Tick the repositories in the list
- Select File > Remove Selected
- Confirm the removal
Note: This only removes the repository from the list - it does not delete any files.
File > Delete Selected..., or Delete Selected... on the list's right-click menu, deletes repositories. The File menu command acts on the ticked rows. The right-click command acts on the right-clicked row when that row is not ticked, and on every ticked row when it is. Closing the window during a Delete stops it after the repository it is on.
The dialog lists every repository it is about to act on - name, the exact remote it resolved (as GitHub: owner/name), then the folder - and offers three independent choices. The remote comes before the path because a path is long and unbounded; the list scrolls horizontally when one runs past the edge:
| Option | Effect |
|---|---|
| Remove the entry from this list | The list entry only. Nothing on disk or on the host is touched |
| Delete the remote repository on its host | Deletes the repository on GitHub through its API, with its issues, releases and history. Permanent. A remote on any other host cannot be deleted from here |
| Delete the local folder | Sends the working-tree folder to the Recycle Bin, so it can be restored from there |
Either of the last two requires the word DELETE to be typed, exactly and in capitals, before the dialog's Delete button becomes available. Removing list entries alone needs no typed confirmation.
Deleting the local folder forces the entry removal as well - the tick is applied and locked - because an entry whose folder has gone can only ever show Error.
The remote is resolved from the branch's upstream remote, the same one the Remote column reports and that push and pull contact, not from whatever happens to be called origin.
Caveats:
- The remote option is disabled, with the reason in its caption, when not one of the checked repositories has a remote this application can delete
- Deleting a remote on GitHub needs an access token carrying the
delete_reposcope. That scope is not part ofrepo, so a token that creates repositories, pushes and changes visibility perfectly well will still be refused here - When a step fails for a repository, the remaining steps for that repository are skipped and it is left exactly as it was: a failed remote deletion does not go on to delete the local folder, and a failed local deletion keeps the list entry so the deletion can be retried
- A local folder cannot be deleted while a file in it is open in an editor, a Git client or an indexer
- Windows deletes permanently instead of recycling when the folder will not fit in the Recycle Bin or the bin is disabled for that drive. The application asks before that happens rather than letting it happen silently
All repositories are refreshed automatically when the application starts: the list appears immediately with the status columns empty, and they fill in as each repository is checked in the background. The log panel reports Refresh complete. when every repository has been checked.
To refresh again at any time, select File > Refresh Status (or press F5). Either way, the operation:
- Runs
git fetchto check for remote updates - Runs
git statusto detect local modifications - Updates the display accordingly
- Tick the checkboxes next to the repositories you want to update
- Use the quick selection buttons if needed:
- Select Modified - Selects only repositories with local changes
- Select All - Selects all repositories
- Select None - Clears all selections
- Enter your commit message in the text field
- Click Commit & Push Selected
- Confirm the operation. Only ticked repositories whose status is Modified are committed; the confirmation names the ticked ones that will be skipped
- Monitor progress in the log panel. If any repository does not complete, a summary names it, and the log gives the reason
The application performs the following Git commands for each selected repository:
git add -A
git commit -F <temp message file>
git push (git push -u <remote> -- <branch> when the branch has no upstream)
Git hooks in the repository (pre-commit, commit-msg, pre-push and so on) run as they would from the command line.
Automatic Version Tagging (Delphi Projects):
If the repository contains a Delphi project file (.dproj) with version information, GitBatchCommit will automatically:
- Extract the
FileVersionthe project's active configuration builds, from the.dprojfile (e.g.,1.0.1.25) - Create an annotated Git tag with
vprefix (e.g.,v1.0.1.25) - Push the tag to the same remote as the commit
Tags are only created if they don't already exist. This means:
- First commit with version
1.0.1.25→ creates tagv1.0.1.25 - Subsequent commits with same version → no new tag (already exists)
- Commit after incrementing version to
1.0.1.26→ creates tagv1.0.1.26
The tags appear in your repository's Tags or Releases section on GitHub.
Repositories flagged as Pull Required can be updated in several ways:
Single Repository (Right-Click):
- Right-click on a repository with Pull Required status
- Select Pull
- Confirm the operation; the same warning, preview and backup steps as Pull Selected follow
- The repository status will be refreshed after the pull completes
Multiple Repositories (Pull Selected Button):
- Tick the checkboxes next to the repositories you want to pull
- Click Pull Selected in the toolbar
- Review the warning about local files being modified
- Incoming changes are fetched; review the preview of files that will change
- Confirm to proceed - a backup branch is created for each repository that has incoming changes
- All selected repositories will be pulled and their status refreshed
Pull Safeguards:
The Pull operation includes multiple safety features to protect your local code:
- Warning Dialog - Clear warning that local files may be modified
- Change Preview - Fetches first and shows exactly which files will be modified before pulling
- Automatic Backup - Creates a timestamped backup branch (e.g.,
backup-2026-01-15-143022-517) before pulling, for each repository that has incoming changes or whose preview failed (not on every pull). If the backup branch cannot be created, that repository is not pulled - Recovery Option - If something goes wrong, reset to the backup branch with:
git reset --hard backup-YYYY-MM-DD-HHMMSS-zzz
To delete backup branches after confirming everything is OK: git branch -d backup-YYYY-MM-DD-HHMMSS-zzz
If a merge performed outside this application has left conflicts (shown in the log), you can resolve them automatically:
- Tick the checkboxes next to repositories with conflicts
- Click Resolve Conflicts in the toolbar
- Confirm the operation
- The conflicted files are resolved by keeping your local versions; files that merged cleanly keep the merged result, and uncommitted or untracked files are left as they are
- The resolution is committed and pushed automatically
This is a "keep local" strategy - your local file versions take precedence over remote changes for the conflicted files. Use this when you want to ensure your local code is preserved. It acts only on a merge in progress: a repository with no merge in progress is skipped, and one in a rebase or cherry-pick is refused.
For repositories where you have committed changes locally but haven't pushed them yet (branch is "ahead" of remote):
- Tick the checkboxes next to the repositories you want to push
- Click Push Only in the toolbar
- Confirm the operation
- Changes are pushed without creating a new commit. A branch with no upstream (
Push Required (not on the remote)) is pushed with-u, which sets its upstream
This is useful when:
- You've already committed manually in another Git client
- You resolved conflicts and just need to push the result
- Your local branch is ahead of remote and needs syncing
When your local code is the "source of truth" and you need to overwrite the remote:
- Tick the checkboxes next to the repositories you want to force push
- Click Force Push in the toolbar
- Read the warning carefully - remote history will be overwritten
- Confirm twice (two confirmation dialogs for safety)
- Remote repositories are updated to match your local code exactly
When to use Force Push:
- Your local code is the definitive version and remote has diverged
- Push fails because remote is "ahead" but you don't want to pull those changes
- You want to ensure remote exactly matches your local state
Warning: Force push overwrites remote history. Any commits on the remote that are not in your local repository will be permanently lost. Use with caution.
GitBatchCommit uses git push --force-with-lease rather than a bare --force, and pins the lease to the upstream commit recorded when the repository's status was last refreshed — that is, what the list was showing you. A push made by someone else since then aborts the operation instead of destroying their work. If you see a "stale info" or rejection message, refresh and review the incoming commits before trying again — that message means the force push just prevented data loss.
The pinning matters: a bare --force-with-lease compares against the remote-tracking ref, and this application fetches on every status refresh. Without an explicit commit, its own background fetch would quietly move the ref forward and the lease would pass on commits you had never seen. For the same reason a repository with no upstream commit recorded at the last refresh is refused rather than force pushed; use Push Only to publish such a branch.
GitBatchCommit can initialise a local folder as a Git repository, create a corresponding repository on GitHub, and push the initial commit - all in one operation, run in the background.
First-time Setup:
- Select GitHub > Settings
- Enter your GitHub username
- Enter a personal access token (generate at github.com/settings/tokens with
reposcope) - Click OK - credentials are saved for future use. A username without a token, or a token without a username, is refused; clear both to remove the credentials
Creating a Repository:
- Select GitHub > Initialise & Push to GitHub...
- Select the folder you want to publish
- Enter the repository name (pre-filled from folder name). A name GitHub would reject is refused, with a suggested alternative
- Optionally add a description
- Choose whether the repository should be private (new repositories default to private)
- For a folder that is not a repository yet, choose the project type, which writes a matching
.gitignore(Delphi by default;(None)writes none, and an existing.gitignoreis never overwritten) - Confirm the operation
The application will, in the background:
- Initialise a Git repository in the folder
- Create the repository on GitHub
- Add the remote origin
- Commit all files
- Push to GitHub
- Add the repository to your managed list
The command can also be used on a folder that is already a Git repository: when it has no origin a GitHub repository is created for it, and its commits are then pushed; when it already has an origin, nothing is created and it is pushed there. Each step already done is skipped, so a run that stopped part-way (a failed push, for example) is finished by running the command again on the same folder.
Dropping folders that are not repositories on the window does the same for each of them: every question is asked first, then the work runs in the background.
Codeberg: Removed in 1.9.0. There is no Codeberg menu, settings dialog, Initialise & Push to Codeberg or Migrate to Codeberg any more. A Codeberg username and token stored by an older version are ignored, and dropped from the configuration file the next time it is saved.
GitBatchCommit can move a repository's remote from another host (Codeberg, for example) to GitHub in a single operation.
Steps:
- Select the repository in the list
- Choose GitHub > Migrate Selected Repository to GitHub...
- Confirm/adjust the target repository name, description, and visibility in the dialog
- Review the confirmation summary and click Yes
What happens:
- The target repository is created on GitHub using your configured credentials
- The existing
originremote is renamed toold-originso the old URL is preserved locally and can be restored if needed originis repointed to GitHubgit push -u origin --allpushes every branch to GitHub and sets upstream trackinggit push origin --tagspushes every tag
Important:
- The old remote repository is not deleted by the migration. Once you have confirmed the migration succeeded, delete it manually via the web interface on the source host (Delete Selected can delete only a GitHub remote, and it resolves the remote from the branch's upstream, which migration has already repointed at GitHub).
- GitHub credentials must be configured first (GitHub > Settings...). If missing, the settings dialog opens automatically.
- The working tree must be clean and the repository must have at least one commit.
- If a local remote named
old-originalready exists, it is left alone and a numbered suffix is used instead (old-origin-N) — a mirror remote you set up yourself is never destroyed, along with its refspecs and push URL. - Branches that exist only on the old remote are recovered locally before the push, so nothing is left behind on the host you are migrating away from.
- If any step fails, the original remotes, branch upstreams and branch list are restored, so a failed migration cannot leave the repository with no
originor withoriginpointing at an empty new remote. The repository created on GitHub is left for you to remove.
You can change the visibility of GitHub repositories directly from the application:
- Right-click on a repository in the list
- Select Set Public or Set Private
- Confirm the operation; the change runs in the background
Requirements:
- The repository must be hosted on GitHub
- You must have the appropriate credentials configured
- You must have permission to modify the repository settings
You can view and edit a repository's .gitignore file directly from the application:
- Right-click on a repository in the list
- Select Edit .gitignore...
- If no .gitignore file exists, you'll be prompted to create one
- The file opens in your system's default text editor
Fix .gitignore (Add Delphi Patterns) on the same menu appends the standard Delphi build-output patterns that are missing. The file keeps its line endings and encoding. An ignore rule does not stop Git tracking a file it already tracks, and the application says so: run git rm --cached <file> in the repository and commit to stop tracking one.
To quickly check or uncheck multiple consecutive repositories:
- Click on a repository to set the anchor point
- Hold Shift and click on another repository
- All repositories between the two clicks will be toggled (if anchor was unchecked, all become checked; if anchor was checked, all become unchecked)
Select File > Settings to configure:
- Git Client Path - Full path to your external Git client executable (e.g.,
C:\Program Files\Fork\Fork.exe), used by Open in Git Client. GitBatchCommit runs this executable for its own Git operations only when its filename isgit.exe; anything else is used solely for Open in Git Client instead of resolvinggitfromPATH - File Pattern - Optional pattern for staging files (e.g.,
*.pas). Leave empty to stage all files. Shell metacharacters are rejected for safety. - delphi-indexer.exe Path - Optional path to delphi-indexer.exe for automatic symbol reindexing after commits (auto-detected if not configured)
These settings are saved to the configuration file and persist between sessions.
GitBatchCommit includes optional integration with delphi-lookup, a high-performance Delphi symbol search tool. When enabled, GitBatchCommit automatically triggers incremental reindexing of committed repositories, keeping your symbol database up-to-date.
How it works:
- After successfully pushing changes (Commit & Push, Push Only, Force Push, or Resolve Conflicts), GitBatchCommit checks if the repository path matches (or is a subdirectory of) any delphi-lookup indexed directories
- If a match is found, incremental reindexing runs in the background (non-blocking, typically 100-500ms)
- The parent indexed directory gets reindexed - no unnecessary processing of other indexed directories
Example: If you have E:\DBiWorkflow Development indexed and commit to E:\DBiWorkflow Development\DBiFoneology, the entire E:\DBiWorkflow Development directory is reindexed (including DBiFoneology's changes). Other indexed directories are skipped.
Confirmation: When reindexing is triggered, you'll see log messages:
Triggering delphi-lookup reindex: E:\DBiWorkflow Development
delphi-lookup reindex completed successfully
If reindexing fails, you'll see:
Triggering delphi-lookup reindex: E:\DBiWorkflow Development
delphi-lookup reindex FAILED
Error: [error details from delphi-indexer]
Setup:
-
Automatic Detection: GitBatchCommit automatically finds delphi-indexer.exe if:
- It's in your system PATH environment variable, or
- It's installed at the default location:
D:\glldelphi-lookup\delphi-indexer.exe - The discovered path is saved to configuration for faster future lookups
-
Manual Configuration: If auto-detection fails, configure the path via File > Settings:
- Select File > Settings
- When prompted "Configure delphi-indexer.exe path?", click Yes
- Browse to your delphi-indexer.exe location
- Click OK to save
Requirements:
- delphi-lookup installed (from https://github.com/JavierusTk/delphi-lookup)
- Repository must be configured as an indexed directory in delphi-lookup
Note: If delphi-indexer.exe is not found, GitBatchCommit silently skips reindexing - all other functionality works normally. The integration is completely optional and non-intrusive.
Attribution: delphi-lookup integration uses delphi-indexer by JavierusTk for symbol indexing.
The application remembers your last 20 commit messages:
- Click the dropdown button (▼) next to the commit message field
- Select a previous message from the list
- The message is inserted into the commit field
Messages are automatically added to history as soon as a commit is made, whether or not the push that follows succeeds.
For more detailed commit messages, you can add a description below the summary line:
- Enter your summary in the main commit message field
- Click the ... button to reveal the details panel
- Enter your detailed description in the memo field
- Click Commit & Push Selected
The commit message will be formatted as:
Summary line
Detailed description explaining the changes,
which can span multiple lines.
Once every repository in the batch has succeeded, the details panel is automatically cleared and hidden. While any repository is outstanding the details are kept, so the same message can be used again.
Create reusable commit message templates for common operations:
Managing Templates:
- Select File > Templates...
- Use Add... to create a new template
- Use Edit... to modify an existing template
- Use Delete to remove a template
- Click OK to save changes
A blank template, or one already in the list, is refused.
Using Templates:
- Click the templates dropdown button (▽) next to the commit message field (second button)
- Select a template from the list
- The template text is inserted into the commit field
Templates are saved to the configuration file and persist between sessions.
Organise your repositories into groups for easier management:
Assigning a Group:
- Tick the checkboxes next to the repositories you want to group, or skip this to group a single repository
- Right-click on a ticked row (or on the single unticked row to group just that one)
- Select Set Group
- If no groups exist yet, you'll be prompted to enter a new group name
- If groups exist, choose from the submenu:
- Select an existing group name
- Select New Group... to create a new group
- Select (Clear Group) to remove the group assignment
The group is applied to the right-clicked row when that row is not ticked, and to every ticked repository when it is.
Filtering by Group:
- Use the Group dropdown in the toolbar
- Select a group name to show only repositories in that group
- Select (All Groups) to show all repositories
Groups are saved to the configuration file and persist between sessions.
Right-click on a repository to access quick actions:
- Open in Explorer - Opens the repository folder in Windows Explorer
- Open in Git Client - Opens the repository in your configured Git client (requires Git Client Path in Settings)
- Pull - Pulls changes from the remote repository
- Press F1 or select Help > Help Contents to open
Users Guide.md - Select Help > About to view version information and application details
Repository paths and credentials are stored in a JSON file located at:
%APPDATA%\GitBatchCommit\repositories.json
The file is created automatically when you first add a repository or configure credentials.
If the file cannot be read (not valid JSON, for example), it is copied to <config file>.unreadable-<timestamp> beside it, a warning names the copy, and the session starts with an empty configuration; the original is never written over before it has been copied. If even the copy cannot be made, nothing is saved in that session. A failure to save settings, templates or credentials is reported in a warning, not logged as a success.
A Codeberg username and token stored by a version before 1.9.0 are ignored, and dropped from the file the next time it is saved.
Note: Access tokens for GitHub are encrypted with the Windows Data Protection API (DPAPI) under your user account, with application-specific entropy, before being written to this file. They appear as an opaque dpapi:-prefixed value and can only be decrypted by the same Windows user on the same machine, so copying the file elsewhere does not carry a usable credential.
If encryption is ever unavailable, the token is omitted from the file rather than written in clear text, and the settings dialog tells you so. A stored token that cannot be decrypted is never overwritten with an empty value either — the original is written back untouched, so an environment problem cannot silently destroy the credential. Tokens written by an earlier build, whether in plain text or without entropy, are still read and are re-encrypted the next time settings are saved.
| File | Description |
|---|---|
GitBatchCommit.dpr |
Main project file |
MainFrm.pas |
Main form unit - UI and user interaction |
MainFrm.dfm |
Main form design file |
uGitRepoManager.pas |
Git repository manager class - handles all Git and GitHub operations |
uNewRepositoryDialog.pas |
Dialog for entering new repository details (name, description, visibility, project type) |
uDeleteRepositoryDialog.pas |
Confirmation dialog for Delete Selected |
uGitHubSettings.pas |
Dialog for configuring GitHub credentials |
uTemplateSettings.pas |
Dialog for managing commit message templates |
Tests\ |
DUnitX test suite for the engine (uGitRepoManager.pas), GitBatchCommitTests |
- Windows only (uses Windows API for process creation)
- Requires Git to be installed and in the system PATH
- Merge conflict resolution uses "keep local" strategy only - for complex merges, use an external Git client
- Single commit message for all repositories (by design)
- GitHub only: a remote on any other host can be committed, pushed and pulled, but cannot be created, deleted or have its visibility changed from here
Potential features for future versions:
- Support for additional Git hosting providers (GitLab, Bitbucket)
This project is provided as-is for personal use only.
Full detail lives in
CHANGELOG.md, which is the authoritative record of changes. This section is a short summary only.
- Removed Codeberg support: the Codeberg menu and settings dialog, Initialise & Push to Codeberg and Migrate to Codeberg. A Codeberg (or other non-GitHub) remote shows as Other and can still be committed, pushed, pulled and migrated to GitHub; stored Codeberg credentials are dropped at the next save
- Changed Git hooks in a listed repository now run (earlier versions disabled them with
-c core.hooksPath=); adding a repository means trusting its hooks - Changed GitHub > Initialise & Push to GitHub runs in the background, works on a folder that is already a repository, and resumes a run that stopped part-way; dropped folders are asked about first, then processed in the background; new repositories default to private
- Changed the right-click Set Group and Delete Selected act on the right-clicked row when it is not ticked; Remove Selected asks for confirmation
- Changed Commit & Push names the ticked repositories it will skip and the ones that did not complete, and keeps the details text until every repository succeeds
- Changed Pull fetches and previews first, and creates a backup branch only where something is incoming; no backup, no pull
- Fixed Resolve Conflicts keeps the local version of the conflicted files only, and only for a merge in progress
- Fixed Force Push is refused when no upstream commit was recorded, or when the last refresh could not fetch from the remote; status text gains
- fetch failedandPush Required (not on the remote), and Push Only sets the upstream for such a branch - Fixed an unreadable configuration file is copied aside rather than overwritten, and failed saves are reported
- Fixed Fix .gitignore keeps the file's line endings and encoding
- Added linked worktrees and submodules can be added with File > Add Repository; dialogs refuse invalid repository names, half-entered credentials and blank or duplicate templates
- Changed background work uses the OmniThreadLibrary thread pool, and every command that starts Git work is disabled while one runs
- Added View > Style - choose between Aqua Light Slate (the default), Carbon, Glow, Windows10 Dark, Windows11 Modern Light and the plain Windows look; the choice is remembered between sessions
- Fixed version detection taking the highest
FileVersionanywhere in a.dproj; it now resolves the version the default configuration actually builds, so the tag pushed on commit matches the binary - Changed the main unit header to list the 3rd-party libraries the project uses, at their installed versions
- Added Delete Selected - deletes any combination of the list entry, the remote repository on GitHub or Codeberg, and the local folder. Irreversible options need the word
DELETEtyped; the local folder goes to the Recycle Bin - Fixed the confirmation dialog clipping the resolved remote off the right-hand edge with no scroll bar - the one detail it exists to state before an irreversible deletion
- Changed every repository count from
%d repository(ies)to1 repository/N repositories, which also uncovered a latentEConvertErrorin the Resolve Conflicts confirmation - Fixed
ProductVersionreporting1.6.0.0whileFileVersionread1.6.0.121; they now carry the same value, kept in step by a pre-commit hook
- Fixed a batch abort with
No mapping for the Unicode character exists in the target multi-byte code page- captured process output was decoded one pipe read at a time, so a multi-byte character split across a read boundary raised - Fixed every commit message beginning with an invisible UTF-8 BOM
- Fixed commit log blocks and list-row refreshes being attributed to the wrong repository
- Fixed Fix .gitignore adding
*.res, which would have stopped compiled resources being staged - Added Conflicted, Push Required and Diverged statuses; Commit & Push now refuses a repository with unresolved conflicts or an unfinished merge/rebase
- Changed Force Push to
--force-with-lease, so it aborts rather than destroying someone else's commits - Security - GitHub and Codeberg tokens are now DPAPI-encrypted at rest, and credentials are redacted from the log
- Numerous thread-safety, shutdown, re-entrancy and resource-lifetime fixes
- Added delphi-lookup Integration - automatically triggers incremental reindexing after commits
- Auto-detects delphi-indexer.exe from PATH or default location
- Reindexes parent indexed directory when committing to exact match or subdirectory
- Captures output and reports success/failure with error details in log
- 30-second timeout with automatic termination if indexing hangs
- Manual path configuration via File > Settings if auto-detection fails
- Path verification with automatic fallback if configured path becomes invalid
- Completely optional - gracefully skips if delphi-indexer.exe not found
- Attribution: Integration uses delphi-indexer by JavierusTk
- Added Version column to repository list - displays version extracted from Delphi
.dprojfiles - Reads the
.dprojin the repository root; only when there is none does it scan subdirectories - Added automatic version tagging for Delphi projects - creates Git tags (e.g.,
v3.9.1.719) on commit - Tags are automatically pushed to remote (GitHub/Codeberg) and appear in Releases section
- Added Pull Selected button - pull changes for multiple repositories at once
- Added Resolve Conflicts button - resolve merge conflicts by keeping local versions
- Added Push Only button - push without committing (for repos that are ahead of remote)
- Added Force Push button - overwrite remote with local code (with double confirmation)
- Added Pull Safeguards to protect local code:
- Warning dialog about local files being modified
- Preview of incoming changes before pulling
- Automatic backup branch creation before any pull
- Added Ctrl+A support in log panel to select all text
- Added commit message templates - create and manage reusable commit messages via File > Templates
- Added commit details panel - click "..." to add detailed description (standard Git format)
- Added repository groups - organise repositories into groups for filtering
- Added Group filter dropdown in toolbar
- Added Set Group context menu item
- Fixed shift-click range selection for checking/unchecking repositories
- Added parallel status refresh - repositories are now checked concurrently for faster performance
- UI remains responsive during status refresh operations
- Optimized git command execution (reduced redundant calls)
- Real-time list updates as each repository status completes
- Added colour-coded status indicators (green=Clean, yellow=Modified, orange=Pull Required, red=Error)
- Added context menu options: Open in Explorer, Open in Git Client, Pull
- Added commit message history (up to 20 messages, accessible via dropdown)
- Added Settings dialog for Git client path and file pattern filtering
- Added file pattern filtering for selective staging
- Initial release
Version: 1.0 – 31 December 2025 09:00 Version: 1.1 – 31 December 2025 09:30 Version: 1.2 – 1 January 2026 14:40 Version: 1.3 – 1 January 2026 15:00 Version: 1.4 – 15 January 2026 Version: 1.5 – 26 January 2026 Version: 1.5.1 – 28 July 2026 Version: 1.6.0 – 30 August 2026 Version: 1.7.0 – 19 September 2026 Version: 1.8.0 – 12 October 2026
Requires to be built with Delphi v 13 or higher. Libraries named in this project's own units, at the installed version and the date it was installed (or, for a fork or our own library, last changed):
- FastMM5 v 5.07 - Updated 24/10/2026
- EurekaLog v 7.16.2.1 - 22/10/2026
- ETHEA Styled Components v 4.6.0 - 04/11/2026
- OmniThreadLibrary v 3.09 (gllOmniThreadLibrary) - 06/11/2026
Version: 1.9.0 – 10 November 2026