How to export revision history from mercurial or git to cvs?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Exporting revision history from Git or Mercurial to CVS is not a clean one-command migration because CVS cannot represent the full history model of modern distributed version control systems. The honest answer is that you can approximate a usable history by replaying commits, but you should not expect a lossless conversion of merges, branch structure, and metadata semantics.
Understand the model mismatch first
Git and Mercurial track changesets in a directed graph. CVS stores per-file revisions with a much older centralized model. That creates immediate gaps:
- merge commits do not map cleanly to CVS history
- distributed branch topology is far richer than CVS branches
- changeset identity is lost
- author and timestamp preservation is possible only approximately
So the real question is not "how do I perfectly export history," but "what subset of history do I need to preserve well enough for the legacy CVS target?"
A perfect history transfer is usually impossible
If the source repository has heavy branching and merging, a faithful CVS representation is effectively impossible. CVS simply does not have the same data model.
That means the practical options are usually:
- export only the latest tree state
- replay a linearized mainline history
- preserve selected tags or milestones manually
This is why many migrations into CVS are business compromises rather than technical mirrors.
Linearize the history you intend to replay
The most realistic approach is to choose one branch, usually the main branch, and replay its commits in chronological order into a CVS working copy.
In Git, a simplified export list might come from a revision log:
In Mercurial, the equivalent idea is similar:
These commands do not solve the migration on their own. They give you the ordered history you would need to replay.
Replay snapshots or patches into CVS
A practical migration strategy is:
- create a CVS module with the initial imported tree
- walk the chosen Git or Mercurial commits in order
- check out the source tree at each commit
- copy the resulting files into the CVS working directory
- commit into CVS with the original message and, where possible, preserved author and date metadata
For a simple Git snapshot export, the source-tree extraction step can look like this:
You then synchronize that tree into a CVS working copy and commit it. The same idea works from Mercurial with archive or checkout commands.
Expect metadata loss and manual cleanup
Even with careful replay, several things usually degrade:
- merge commits become ordinary replayed content states
- rename history may be flattened into delete plus add behavior
- CVS tags and branches need separate handling
- binary file handling may need extra attention
So after the automated replay portion, plan for manual cleanup and manual tagging of important release points.
Consider whether you really need CVS history at all
Before investing in a replay migration, challenge the requirement. In many organizations, the better answer is:
- keep the old Git or Mercurial repository as the authoritative history archive
- import only the current tree into CVS
- document the mapping between CVS start point and legacy source history
That gives users a working CVS module without pretending the old history model survived intact.
Use the migration only as a compatibility bridge
Moving from Git or Mercurial into CVS is almost always a legacy-compatibility move. Treat it as such. The goal is usually to satisfy an old toolchain, vendor system, or policy constraint, not to improve version control quality.
That framing matters because it tells you how much fidelity is worth paying for. Often, "good enough mainline replay plus archived original repo" is the right answer.
Common Pitfalls
- Expecting Git or Mercurial branch and merge history to survive intact in CVS.
- Confusing
git cvsimportwith export to CVS; it imports CVS into Git, not the other way around. - Replaying every branch when only one maintained line actually matters.
- Ignoring rename, binary, and tag behavior differences until late in the migration.
- Throwing away the original Git or Mercurial repository after creating an approximate CVS replay.
Summary
- There is no fully faithful export of modern DVCS history into CVS.
- The practical solution is usually to linearize and replay a selected history, not to preserve everything.
- Snapshot or patch replay into a CVS working copy is the standard approximation strategy.
- Keep the original Git or Mercurial repository as the authoritative archive.
- Treat the CVS result as a compatibility target, not as a perfect historical mirror.

