DEV Community

Fernando Paladini
Fernando Paladini

Posted on

Translate Git Commit Messages Offline Without Rewriting Code

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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: and fix:;
  • 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
Enter fullscreen mode Exit fullscreen mode

You can also select an author:

git-translate-commits \
  --lang en \
  --author "developer@example.com" \
  --dry-run
Enter fullscreen mode Exit fullscreen mode

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-by and Signed-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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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}
Enter fullscreen mode Exit fullscreen mode

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:

  1. the expected number of commits still exists;
  2. author identities and timestamps are preserved;
  3. issue references and trailers remain intact;
  4. application tests pass;
  5. the backup branch resolves to the original history;
  6. 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)