GitLab has no built-in importer for Subversion, so to import an SVN repository you convert it to Git locally with git svn, keep the history and authors, and then push the result to a new GitLab project.
The conversion is a one-time job that is easy to start and hard to redo, so the preparation is where the time should go.
Table of contents
- Why GitLab cannot import SVN directly
- Check the layout and gather authors
- Run the conversion
- Clean up branches and tags
- Push to GitLab and set up the project
- Helping the team switch
- Deploying after the move
- How this fits the rest of the stack
- FAQ
Why GitLab cannot import SVN directly
GitLab’s import page lists the sources it can pull from: other Git hosts, a few project management tools, and file-based exports. Subversion is a different kind of system, and the documentation points to a conversion workflow rather than a one-click importer.
The difference matters. Subversion keeps a single central history with numeric revisions and treats branches and tags as directory copies. Git keeps a graph of commits where branches and tags are lightweight pointers. Converting means translating every revision into a commit and deciding what each directory copy under branches and tags becomes.
You have two common tools for the job. git svn ships with Git and does the conversion by cloning the SVN repository into a Git repository. svn2git is a wrapper around it that handles the tag and branch cleanup that git svn leaves for you to do by hand. The community write-ups on the forum and in gists mostly use the first approach with a few manual steps, which is what this post follows.
One more point of choice: some teams keep SVN running alongside Git during a transition. That is possible by mirroring, but it doubles the places people can commit. For most teams a clean cutover on a chosen date is less confusing than a long overlap.
If you want background on moving a repository between Git hosts afterwards, the post on cloning a project and pushing to a new repository covers that half.
Check the layout and gather authors
Before converting, look at your SVN repository’s structure. The cleanest case is the standard layout with trunk, branches and tags directories at the top. The conversion tools expect that, and the community guides note that the SVN URL you give should stop at the level immediately above those three folders.
svn ls https://svn.example.com/repos/project
# expected: branches/ tags/ trunk/
If your repository uses a different layout, or several projects live in one repository, you can pass explicit paths for trunk, branches and tags, or convert only the subdirectory you need.
Next, build an authors file. Subversion records only usernames, while Git commits need a name and an email. Without a mapping, your history will show commits by plain usernames with a placeholder email address.
svn log --xml -q https://svn.example.com/repos/project \
| grep '<author>' | sort -u | sed 's/<author>\(.*\)<\/author>/\1/' > users.txt
Edit the list into the mapping format, one line per author:
jsmith = Jane Smith <[email protected]>
bwong = Ben Wong <[email protected]>
Use the email addresses people use on GitLab, so contributions link to the right accounts. Check the list carefully now, because correcting authors after the history is imported means rewriting every commit.
Run the conversion
With the layout confirmed and the authors file ready, clone the repository into Git. The standard layout flag tells git svn where to find trunk, branches and tags.
git svn clone https://svn.example.com/repos/project \
--stdlayout \
--authors-file=users.txt \
--prefix=origin/ \
project-git
This can take a long time. Each revision is fetched and converted in order, and a repository with tens of thousands of revisions can run for hours. Run it on a machine with a stable connection, and use a terminal multiplexer so a dropped session does not kill it. If it is interrupted you can resume with git svn fetch from inside the directory.
Expect to be asked for credentials if the SVN server requires them. If the repository is huge, consider limiting how much history you bring along with a starting revision, which shortens the import at the cost of older history.
Run the conversion against a copy or a read-only mirror of the SVN server where possible, since a long-running clone puts load on the source. When it finishes, you have a local Git repository whose history mirrors the SVN revisions, with SVN branches and tags present as remote references.
Clean up branches and tags
After the clone, the SVN structure is represented in an odd way. Trunk becomes the master branch, and SVN branches and tags both appear as remote branches under the prefix you chose. You need to turn the tags into real Git tags and the branches into real branches.
cd project-git
git branch -m master main
for t in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin/tags); do
git tag "${t#origin/tags/}" "$t"
git branch -r -d "$t"
done
for b in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin | grep -v tags | grep -v trunk); do
git branch "${b#origin/}" "$b"
git branch -r -d "$b"
done
Review the result with git branch -a and git tag. You will often find branches that are long dead. Decide now what to keep, because every stale branch you push becomes clutter in the new project.
Then check that the conversion is faithful. Compare the latest trunk revision with the head of main by exporting both and diffing the trees. A silent mismatch at this stage is far better than one discovered after the team has switched.
Also look for large binary files in the history. Subversion handles big files comfortably and Git does not, so consider Git LFS for assets that must stay versioned.
Push to GitLab and set up the project
Create an empty project in GitLab with no README, so there is nothing to conflict with. Then add it as a remote and push everything.
git remote add gitlab [email protected]:your-group/your-project.git
git push gitlab --all
git push gitlab --tags
Check the project in the browser: the commit history, the branches list and the tags. Confirm that author names link to people and that the file tree matches what you expect.
Then configure the project properly while you are there. Set the default branch, protect it so direct pushes are limited, set merge request rules and add the team with appropriate roles. If you used CI in your old system, recreate the pipeline as a file in the repository.
Keep the SVN repository, set read-only, for a defined period. Teams are often reluctant to cut over because they fear losing something, and a read-only archive removes that fear. Announce the cutover date, freeze commits on the SVN side at that moment, run a final incremental git svn fetch to catch the last revisions and push again.
Helping the team switch
The technical conversion is the easy half. Teams used to Subversion need a short orientation, because several habits change. Commits are local until pushed. Branching is cheap and expected. There is no global revision number, and a commit is identified by a hash. Merge conflicts are resolved in your own working copy before you push.
Give people a one-page cheat sheet with the six commands they will use daily: clone, status, add, commit, pull and push, plus how to create a branch and open a merge request. The existing post on the Git command cheatsheet is a decent starting point.
Decide the branching model before the first day. A simple model with a main branch and short-lived feature branches merged through merge requests suits most teams, and it is easier to learn than a complex one. And agree on conventions early: commit message style, who reviews, and what must pass before merging.
For context on how the hosting platforms differ, the post comparing Git, GitHub and GitLab explains the roles each plays.
Deploying after the move
Once the history lives in Git, deployment can move to a Git-driven flow. RunxBuild deploys web services and static sites from a GitHub repository, with a build log, a live route, runtime logs and rollback to the previous deploy. If your canonical repository is on GitLab, you would need a GitHub copy, for example a mirror of the project, to connect it, so decide which host is the source of truth before you wire deployment.
The GitHub getting started guide describes the connection. Whatever host you choose, the useful habit is the same: every deploy traces back to a commit, and a rollback is a known earlier commit rather than a manual file copy from a server.
How this fits the rest of the stack
A repository migration is a weekend of effort, and what it unlocks is a cheaper, repeatable deploy. When you are planning where the migrated project will run, line up the services and database it needs in the RunxBuild hosting calculator. When the history is safely in Git, the RunxBuild dashboard is where you connect it to a deploy.
Useful related references:
- Python Import From Another Directory: Why It Fails and What to Do
- All-in-One WP Migration: How It Works and the Import Limit That Blocks You
- phpMyAdmin Upload Limit: The Four Numbers That Cap Your Import
- Services on RunxBuild
FAQ
Does GitLab have a built-in SVN importer?
No. GitLab’s documentation directs you to convert the Subversion repository to Git first, typically with git svn or svn2git, and then push the Git repository to a new GitLab project.
How do I keep author names when converting?
Create an authors file that maps each SVN username to a Git name and email, and pass it to git svn clone with the authors-file option. Do this before the clone, because fixing authors later rewrites every commit.
What layout does git svn expect?
The standard trunk, branches and tags layout. Point the clone at the URL one level above those folders and use the stdlayout flag, or give explicit paths if your layout differs.
Why is my conversion so slow?
It processes revisions one at a time, so large histories take hours. Run it on a stable machine, resume with git svn fetch if interrupted, or limit history from a starting revision.
What happens to SVN tags?
They appear as remote branches after the clone. Convert them to real Git tags with a short script, then delete the remote branch references before pushing.