Migrating TFVC to Git With Full History — The Honest Guide
Every team still on TFVC knows the migration to Git is coming. What keeps it perpetually on next quarter's list isn't the tooling — git-tfs is mature and does the heavy lifting — it's the fear of two things: losing history, and stopping work. Here's the honest version of how to think about both, without the step-by-step commands (those are well documented elsewhere), and with the decisions that actually determine whether the migration goes smoothly.
Decision one: history is non-negotiable, so plan around it
The tempting shortcut is a "tip migration" — take the latest code, start a fresh Git repo, keep the old TFVC server around as a read-only archive. It feels pragmatic. It's usually a mistake for any codebase with real age, because the moments you need history are exactly the moments you can least afford friction: a production bug that traces back three years, an audit question about when a change shipped, a departed developer's reasoning recoverable only from commit context.
git-tfs converts full TFVC history into Git commits, preserving the timeline. It's slower — on a large repository, cloning years of history takes many hours or days — but that cost is paid once, by a machine, in the background. The cost of missing history is paid repeatedly, by people, at the worst times.
Decision two: separate the slow part from the risky part
The insight that makes this migration low-drama is that it has two phases with completely different risk profiles, and they shouldn't be mixed.
Phase one — the historical clone — is slow but zero-risk. It runs in the background against the TFVC server while the team keeps working normally. Nobody freezes, nothing changes, and if it fails halfway you simply restart it. Let it take the days it takes.
Phase two — the cutover — is fast but concentrated. Once the background clone has caught up to near-present, you declare a short freeze: developers check in their final TFVC changes, the clone pulls the last few changesets, the Git repository is pushed to Azure DevOps, and the team switches. Done well, the freeze is measured in hours, not days — because all the slow work already happened in phase one.
Teams that skip this separation end up freezing development for the entire clone duration, which is how a migration becomes a week of lost work and an organizational memory that says "never again."
Decision three: the migration is the easy half
The under-planned part is never the repository conversion — it's everything attached to it. Build pipelines pointed at TFVC paths. Developer muscle memory around check-in/check-out workflows versus commit/push. Branching conventions that made sense in TFVC's model and translate awkwardly to Git. Budget as much attention for the two weeks after cutover as for the migration itself, because that's when the real friction surfaces — and when a team member quietly keeping a local TFVC workspace "just in case" tells you the switch hasn't actually happened yet.
The short version
Keep the history — the one-time cost is worth it. Split the slow work from the risky work so the freeze window stays short. And treat the human cutover as seriously as the technical one. The tooling has been solved for years; the migrations that go badly are the ones planned as a single big-bang event instead of two small, different problems.