Azure DevOps: Migrating from SVN to Git on Windows
- Nigel Browne
- Jul 15, 2019
For awhile now, I’ve wanted to move the team from our on-premise Redmine and subversion (SVN) servers to git repositories on Azure DevOps (formerly known as Visual Studio Team Services, and several other names). Unfortunately, whenever we talked about it, the conversation went like this:
Me: Ok, let’s move to git and Azure DevOps
Team: Woohoo! Yay Git! Git-er done! Life will be so much better!
Me: Calm down, calm down… We can only do it if we migrate the commit histories.
Team: sudden silence followed by mysterious calendar conflicts
It turns out when migrating from SVN to Git, the prevailing opinion is that moving the commit histories is difficult, painful, and generally not worth the time. Even Microsoft advises against it, particularly if you have complicated repositories and/or want to keep your commit author information. Well dear friends, I’m stubborn and we don’t have particularly complicated repositories, so off we went.
We knew that since this was going to be a phased migration, we were going to leave our Redmine/SVN server running while we moved specific repositories to Azure DevOps. Since we like belts *and *suspenders, we wanted to leave the old repos available with read-only access until we could decommission our subversion server.
This is procedure that worked for us, but if you manage to destroy your repositories, I’m not responsible. We also do all of our development on Windows, so this procedure if for migrating on that operating system.
Goals
The goals for this migration were:
- Migrate from subversion to git and host the repositories on Azure DevOps
- Perform the migration using Windows
- Keep the commit histories
- Keep the authors associated with each commit
Before You Jump In
Like any good adventure, you’ll need some supplies:
- Git command line tools and TortoiseGit installed
- SVN command line tools (comes with TortoiseSVN)
- A current backup of your SVN repository (trust me on this one)
- Coffee (or beverage of choice)
- Knowledge of your SVN repository layout - does it follow the standard trunk/branches/tags structure, or is it more… creative?
- An up-to-date SVN repo. No mysterious-uncommited-but-running-in-production changes on developers computers.
Procedure
- Create a temporary folder for your git migration on your local machine:
mkdir d:\git-temp
- Change to the root directory of your checked out subversion repository.
- Extract the commit authors from your subversion repository:
svn.exe log --quiet | ? { $_ -notlike '-*' } | % { "{0} = {0} <{0}>" -f ($_ -split ' \| ')[1] } | Select-Object -Unique > d:\git-temp\authors-transform.txt
-
Update the authors transform file with the full name and email addresses. If you’re using Azure Active Directory to sign into Azure DevOps, make sure the email address match your sign-in emails. The encoding of the file should be “UTF-8 without BOM”. If anything else is used, you will likley get an an error that it can’t find the author name during the conversion. Notepad seems to default to the wrong format, but Notepad++ or Visual Studio Code both work.
-
Change to your migration directory:
cd \git-temp
-
Get the url of the existing subversion repository
-
Depending on the layout of your SVN repository, you’ll need to do either A or B below. For non-standard layouts use A and for standard layouts use B.
-
The next step will vary depending on your repo layouts.
For non-standard layouts, you’ll need to specify where the trunk, branches and tags are located in the SVN repo.
git svn clone ["http://redmine.woodbridge.corp/svn/ivp/"] --prefix=svn/ --no-metadata --authors-file "authors-transform.txt" -T "http://redmine.woodbridge.corp/svn/ivp/" d:\git-temp\IVP
For standard layouts.
git svn clone ["http://redmine.woodbridge.corp/svn/ivp/"] --prefix=svn/ --no-metadata --authors-file "authors-transform.txt" --stdlayout d:\git-temp\IVP
-
Confirm that the files, authors and history were imported using the tortoisegit -> show log
-
List all fetch entries:
git config --get-all svn-remote.svn.fetch
- Pull the svn ignore properties and output to the .gitignore:
git svn show-ignore --id=svn/trunk > .gitignore
- Add the ignore file to the repo and commit
git add .gitignore``````git commit -m 'Convert svn:ignore properties to .gitignore.'
- In azure devops, create a new bare git repository, no readme, not .gitignore
- Add the remote and push to the ADO repo, the repo specific details are showing in ADO
git remote add origin https://woodbridgegroup.visualstudio.com/Woodbridge/_git/IVP``````git push -u origin --all
The Home Stretch: Making SVN Read-Only
Once you’re confident your Git migration worked (and you’ve tested it thoroughly), you’ll want to prevent any new commits to the old SVN repository. Here’s how to do that without completely cutting off access to the historical repository…
- Connect to the SVN server (redmine.woodbridge.corp) using SSH
- Change the svn server directory, then your repository name, then the hooks directory. For example:
/home/svn/ivp/hooks
- Run an editor to create the pre-commit hook script:
nano pre-commit
- Paste the following script and modify it with your name, the date and the new repository URL.
#!/bin/bash
# This script disables writing to
echo -e "Commits disabled. This repository has been moved to git on Azure DevOps. See https://woodbridgegroup.visualstudio.com/Woodbridge/_git/IVP" 1>&2
exit 1
- Make the file executable
chmod +x pre-commit
- Change the ownership to the process that will access the SVN repo (normally the web server)
sudo chown www-data:www-data pre-commit
- Test a commit against the SVN repository, it should fail with the error message you wrote.
When Things Go Wrong (Because They Might)
Here are some common issues we ran into and how to fix them:
-
Author mapping fails: Usually means your authors-transform.txt file encoding is wrong. See the section below on fixing file encoding.
- At the bottom of Visual Studio code, you’ll see the encoding of the file. In this case, it shows UTF-8.
- Left click the encoding, and you should see a drop down that says Save with encoding. Click that. Then select UTF-8.
Final Thoughts
Was this migration perfect? Nope. Was it worth it? Absolutely. Your mileage may vary, but having a clear process and understanding what can go wrong makes it much more manageable than the horror stories suggest.
Remember, if something goes wrong, you still have your original SVN repository. Take it step by step, test thoroughly, and you’ll be Git-ing (sorry, couldn’t resist) in no time. Got questions or hit a snag? Drop me an email - I’d love to hear about your migration adventures!
Next up, migrating all of our Redmine wiki content. I can already hear the groans starting and the calendars mysteriously filling up.
Author’s Note
This article was originally written in 2019 and somehow slumbered in my unpublished articles folder. I’ve posted it in 2025 and hope that someone still finds it useful.