A repository can have clean code and still have a difficult history. One commit says fix: corrige timeout, another says feat: add retry logic, and the next was generated by an AI coding assistant in a third language.
That inconsistency makes git log, release-note preparation, blame investigation, and onboarding harder than they need to be. Manually editing hundreds of messages is not realistic, while sending an entire private history to an external translation API may violate your team's privacy expectations.
This tutorial shows how to use git-translate-commits, an open-source Python CLI, to preview and normalize Git commit messages with a local translation engine. We will test the operation in a disposable clone first because changing a commit message necessarily changes its commit hash.
TL;DR
Install the CLI, create a disposable clone, and start with:
git-translate-commits --lang en --dry-run
The default engine uses Argos Translate. It downloads the required language model on first use and can then translate locally without an API key. Do not remove --dry-run until you have inspected the proposed messages and coordinated any history rewrite with everyone using the repository.
Why multilingual Git history becomes a maintenance problem
Mixed-language commits are not inherently wrong. The problem appears when a team expects one searchable language but people, automation, and coding agents produce several.
Consider a production incident. You search for "payment timeout", but the relevant fix was committed as corrige tempo limite do pagamento. Git cannot retrieve the concept if the words do not match. The same inconsistency affects changelog generation and tools that classify Conventional Commits.
The safest time to establish a commit-language policy is before merging. Existing repositories, however, may already contain years of mixed messages. A controlled history rewrite can normalize that history, provided the team understands the consequences.
Prerequisites
The current package metadata requires Python 3.10 or newer. You also need Git and either pipx, uv, or pip.
Install the command in an isolated environment:
pipx install git-translate-commits
# Alternative with uv
uv tool install git-translate-commits
The published PyPI package is currently version 1.0.1. During verification for this tutorial, the installed command responded to --version, although it printed v1.0.0. Treat PyPI metadata as the package-release version until that CLI display mismatch is corrected.
Step 1: Work in a disposable clone
Do not test a history-rewriting tool in your only local copy. Clone the repository into a separate directory and keep the original remote unchanged:
git clone https://github.com/your-org/your-repository.git history-translation-test
cd history-translation-test
git status
Replace the placeholder URL with a repository you are authorized to modify. Confirm that the working tree is clean before continuing.
For a shared repository, announce the experiment before anybody bases new work on rewritten commits. Even a correct rewrite creates new commit identifiers.
Step 2: Preview the translation
The required option is --lang. Use a language code such as en, es, or pt-BR.
Run the default local engine in dry-run mode:
git-translate-commits --lang en --dry-run
This is the critical safety step. --dry-run shows what would change without modifying the repository.
Review the preview for:
- technical terms that should remain unchanged;
- issue identifiers such as
#123; - Conventional Commit prefixes such as
feat:andfix:; - names, product terminology, and acronyms;
- messages already written in the target language.
By default, the CLI is documented to skip messages already detected in the target language and preserve Conventional Commit prefixes.
Step 3: Reduce the scope when necessary
You rarely need to translate everything on the first attempt. Filters make a smaller experiment easier to review.
For example, preview commits from a date forward:
git-translate-commits \
--lang en \
--since "2026-01-01" \
--dry-run
You can also select an author:
git-translate-commits \
--lang en \
--author "developer@example.com" \
--dry-run
Other documented filters include --until, --branch, and --all-branches. Start with the current branch unless you have a reviewed migration plan for the entire repository.
Step 4: Understand what the tool preserves
git-translate-commits changes commit messages rather than source files. Its documented preservation rules include:
- file contents;
- author and committer names;
- email addresses and timestamps;
- Conventional Commit prefixes;
- issue references;
- Git trailers such as
Co-authored-byandSigned-off-by.
Preserving those fields does not preserve commit hashes. A commit hash incorporates the commit message, so changing the message produces a different hash. Every descendant commit is rewritten as well.
The CLI creates a backup branch by default and writes .git-translate-log.json with the old-to-new mapping. Keep both until the rewritten history has been reviewed.
Step 5: Apply the rewrite only after review
Once the dry run is correct and the team has agreed on the migration window, run the same command without --dry-run:
git-translate-commits --lang en
The command asks for confirmation unless --force is used. Avoid --force during an initial migration because the prompt is a useful final pause.
After the command completes, inspect the result locally:
git log --oneline --decorate -n 20
git branch --list
git status
Compare representative messages with the preview, confirm that the backup branch exists, and inspect .git-translate-log.json.
Do not force-push immediately. Run your tests and compare the rewritten tree with the original branch first. The file trees should remain equivalent even though commit hashes differ.
Optional: use an LLM translation engine
The default local engine is the privacy-oriented path. The project also offers an optional llm extra through LiteLLM:
pipx install "git-translate-commits[llm]"
git-translate-commits --lang en --engine llm
This mode can use OpenAI, Anthropic, or an OpenAI-compatible provider. It changes the privacy, cost, and credential boundaries of the workflow. Prefer environment variables over command-line API keys, and confirm whether commit messages are allowed to leave your environment.
An OpenAI-compatible local server can provide a different local path, but model behavior and compatibility depend on that server. Validate it separately rather than assuming it behaves like the default Argos engine.
How to verify that only history metadata changed
Before rewriting, record the tree hash of the branch tip:
git rev-parse HEAD^{tree}
Run the same command after translation. If the tree hash is identical, the checked-in file snapshot at the branch tip is unchanged. This does not replace tests, but it is a useful deterministic check that the final source tree stayed the same.
Also verify:
- the expected number of commits still exists;
- author identities and timestamps are preserved;
- issue references and trailers remain intact;
- application tests pass;
- the backup branch resolves to the original history;
- collaborators know that their existing branches need reconciliation.
Limitations and safer alternatives
History rewriting is disruptive. Open pull requests, signed commits, release tags, CI references, and external links to commits can be affected. A backup branch helps recovery, but it does not remove the coordination cost.
For an active public repository, keeping existing history and enforcing one language only for future commits may be the better choice. A commit-message hook, contribution guideline, or pull-request squash policy can prevent new drift without invalidating old hashes.
Local neural translation also has quality limits. Domain-specific messages may need manual review. The optional LLM engine may improve context in some cases, but it introduces a provider and a data-transfer boundary.
Frequently asked questions
Does this translate source code?
No. The tool targets Git commit messages. You should still compare tree hashes and run tests before sharing rewritten history.
Can I preview without changing anything?
Yes. Use --dry-run, and keep it enabled while tuning language, branch, author, and date filters.
Will commit hashes change?
Yes. The message is part of the commit object, so changing it creates a new hash.
Does the default engine need an API key?
No. The default Argos Translate engine downloads language data on first use and operates locally afterward.
Should I rewrite a shared default branch?
Only with explicit team coordination and a recovery plan. For many active repositories, a future-only commit-language policy is safer.
Takeaway
A consistent Git history is useful, but consistency is not worth a surprise rewrite. Start in a disposable clone, use the offline dry run, narrow the scope, verify tree hashes, and involve the team before changing a shared branch.
git-translate-commits turns a repetitive editing problem into a reviewable workflow. The most important feature is not automatic translation. It is the ability to inspect the plan before accepting a destructive change.
Disclosure: This article was researched, fact-checked, and drafted with AI assistance using the current primary project sources linked above.
Would you rewrite an existing multilingual history, or enforce one commit language only from today forward?
Top comments (0)