How to import svn branches and tags into git-svn?

SvnGitGit Svn

Svn Problem Overview


I have got a central SVN repository I must commit to, but I've got a passion for git (like any other developer I know). The case is well known.

Then I read about git-svn and gave it a try. Since I don't need the full history, just from two months or so, I did like this:

git svn clone -r 34000 -s https://svn.ourdomain.com/svn/repos/Project/SubProject

SubProject had, as usual, the subdirectories trunk, tags and branches. Great.

Then, in order to get the last revision, I did

git svn rebase

Some downloads later, great. Last revision, logs, etc. Ok, now I'll switch to my feature branch.

$ git branch 
* master

$ git branch -r  
  trunk

$ git branch -a  
* master
  remotes/trunk

The questions are: Where are my branches? Have I done something wrong? How should I do in order to get my branches in the new git repo?

git-svn, wherever I have read about it, dealt wisely with branches and tags, but the behaviour is not what I expected. Thanks!

EDIT: I have just found out that git svn fetch will do it. But it will get all revisions, which is something I wouldn't like.

Svn Solutions


Solution 1 - Svn

You'll need several steps.

  1. supply proper trunk, branches and tags folder names and fetch svn repo:

     git svn init -t tags -b branches -T trunk https://mysvn.com/svnrepo
     git svn fetch
    
  2. Since tags in svn are real branches, create git tags from tag branches:

     git for-each-ref --format="%(refname:short) %(objectname)" refs/remotes/tags |  cut -d / -f 3- |
     while read ref
     do
       echo git tag -a $ref -m 'import tag from svn'
     done
    
  3. Delete tag branches

     git for-each-ref --format="%(refname:short)" refs/remotes/tags | cut -d / -f 2- |
     while read ref
     do 
       echo git branch -rd $ref
     done
    
  4. Since tags marked in the previous step point to a commit "create tag", we need to derive "real" tags, i.e. parents of "create tag" commits.

     git for-each-ref --format="%(refname:short)" refs/tags |
     while read ref
     do
       tag=`echo $ref | sed 's/_/./g'` # give tags a new name
       echo $ref -\> $tag
       git tag -a $tag `git rev-list -2 $ref | tail -1` -m "proper svn tag"
     done
    
  5. All we have to do now is to remove old tags.

Solution 2 - Svn

This draws on Vanuan's answer above, but it preserves the message of the original svn tag in the new git tag.

$ git for-each-ref --format="%(refname:short) %(objectname)" refs/remotes/tags \
| while read BRANCH REF
  do
        TAG_NAME=${BRANCH#*/}
        BODY="$(git log -1 --format=format:%B $REF)"

        echo "ref=$REF parent=$(git rev-parse $REF^) tagname=$TAG_NAME body=$BODY" >&2

        git tag -a -m "$BODY" $TAG_NAME $REF^  &&\
        git branch -r -d $BRANCH
  done

Solution 3 - Svn

This is the same as nicolai.rostov's answer above, but i just change the refs path I replaced refs/remotes/tags by refs/remotes/origin/tags I'm using git version 2.1.1 into cygwin terminal.

$ git for-each-ref --format="%(refname:short) %(objectname)" refs/remotes/origin/tags \
| while read BRANCH REF
  do
        TAG_NAME=${BRANCH#*/}
        BODY="$(git log -1 --format=format:%B $REF)"

        echo "ref=$REF parent=$(git rev-parse $REF^) tagname=$TAG_NAME body=$BODY" >&2

        git tag -a -m "$BODY" $TAG_NAME $REF^  &&\
        git branch -r -d $BRANCH
  done

Solution 4 - Svn

You say that you haven't gotten your branches in your checkout.

This is likely a problem with the layout of your svn repo.

The 'standard layout' is:

branches/

tags/

trunk/

If you have your layout like this:

branches/user1/

branches/user2/

Then, you will lose your branches when you do git svn fetch / clone.

To fix this, you should give the argument

--branches=branches/*/* to git clone.

Solution 5 - Svn

I wrote a script to help migrate as you want. The script is not perfect, but I hope this could help you:

For more information, you can visit: https://github.com/MPDFT/svn-to-git

#!/bin/bash

####### Project name 
PROJECT_NAME="myproject"
EMAIL="mycompany.com"

###########################
####### SVN 
# SVN repository to be migrated
BASE_SVN="http://svn.mycompany.com/svn/repo/sistemas/myproject"

# Organization inside BASE_SVN
BRANCHES="branches"
TAGS="tags"
TRUNK="trunk"

###########################
####### GIT 
# Git repository to migrate
GIT_URL="https://git.mycompany.com/git/repo/sistemas/myproject.git"

###########################
#### Don't need to change from here
###########################

# Geral Configuration
ABSOLUTE_PATH=$(pwd)
TMP=$ABSOLUTE_PATH/"migration-"$PROJECT_NAME

# Branchs Configuration
SVN_BRANCHES=$BASE_SVN/$BRANCHES
SVN_TAGS=$BASE_SVN/$TAGS
SVN_TRUNK=$BASE_SVN/$TRUNK

AUTHORS=$PROJECT_NAME"-authors.txt"

echo '[LOG] Starting migration of '$SVN_TRUNK
echo '[LOG] Using: '$(git --version)
echo '[LOG] Using: '$(svn --version | grep svn,)

mkdir $TMP
cd $TMP

echo
echo '[LOG] Getting authors'
svn log -q $BASE_SVN | awk -F '|' '/^r/ {sub("^ ", "", $2); sub(" $", "", $2); print $2" = "$2" <"$2"@"$EMAIL">"}' | sort -u >> $AUTHORS

echo
echo '[RUN] git svn clone --authors-file='$AUTHORS' --trunk='$TRUNK' --branches='$BRANCHES' --tags='$TAGS $BASE_SVN $TMP
git svn clone --authors-file=$AUTHORS --trunk=$TRUNK --branches=$BRANCHES --tags=$TAGS $BASE_SVN $TMP

echo
echo '[LOG] Getting first revision'
FIRST_REVISION=$( svn log -r 1:HEAD --limit 1 $BASE_SVN | awk -F '|' '/^r/ {sub("^ ", "", $1); sub(" $", "", $1); print $1}' )

echo
echo '[RUN] git svn fetch -'$FIRST_REVISION':HEAD'
git svn fetch -$FIRST_REVISION:HEAD

echo
echo '[RUN] git remote add origin '$GIT_URL
git remote add origin $GIT_URL

echo
echo '[RUN] svn ls '$SVN_BRANCHES
for BRANCH in $(svn ls $SVN_BRANCHES); do
    echo git branch ${BRANCH%/} remotes/svn/${BRANCH%/}
    git branch ${BRANCH%/} remotes/svn/${BRANCH%/}
done

git for-each-ref --format="%(refname:short) %(objectname)" refs/remotes/origin/tags | grep -v "@" | cut -d / -f 3- |
while read ref
do
  echo git tag -a $ref -m 'import tag from svn'
  git tag -a $ref -m 'import tag from svn'
done

git for-each-ref --format="%(refname:short)" refs/remotes/origin/tags | cut -d / -f 1- |
while read ref
do
  git branch -rd $ref
done

echo
echo '[RUN] git push'
git push origin --all --force
git push origin --tags

echo 'Sucessufull.'

Solution 6 - Svn

If you want to see your branches when doing a git branch after a import from svn, you should use the ruby script svn2git (and git2svn)

> It is better than git svn clone because if you have this code in svn:

  trunk
    ...
  branches
    1.x
    2.x
  tags
    1.0.0
    1.0.1
    1.0.2
    1.1.0
    2.0.0

> git-svn will go through the commit history to build a new git repo.
It will import all branches and tags as remote SVN branches, whereas what you really want is git-native local branches and git tag objects. So after importing this project, you would get:

  $ git branch
  * master
  $ git branch -a
  * master
    1.x
    2.x
    tags/1.0.0
    tags/1.0.1
    tags/1.0.2
    tags/1.1.0
    tags/2.0.0
    trunk
  $ git tag -l
  [ empty ]

After svn2git is done with your project, you'll get this instead:

  $ git branch
  * master
    1.x
    2.x
  $ git tag -l
    1.0.0
    1.0.1
    1.0.2
    1.1.0
    2.0.0

Solution 7 - Svn

For those who have to work on Windws at work, here is a solution up-to-date with git version 2.17.0 (and theoretically also works for versions before)

git svn init -t tags -b branches -T trunk https://mysvn.com/svnrepo

git svn fetch

for /f "tokens=1-2 delims= " %a in ('git for-each-ref --format="%(refname:lstrip=-1) %(objectname)" refs/remotes/origin/tags') do git tag -a %a %b -m "import tag from svn"

Solution 8 - Svn

I had the same problem - tags and branches were missing when I specified the revision:

$ git svn clone -r 34000 -s https://...

The fix was to specifiy a range of revisions, -r 34000:HEAD:

$ git svn clone -r 34000:HEAD -s https://...

The git mailing list gave me the hint.

Solution 9 - Svn

To pull a new SVN branch into your local Git-SVN repository, I've found an easy method, which does not require editing any config files or so.

You only need the revision number of the last commit in the branch that you want to add to your local git-svn repository.

If you have an up-to-date SVN checkout of the new branch and TortoiseSVN installed, you can right-click on this folder and use "TortoiseSVN / Show Log".

Make a note of the topmost commit (e.g. 45065).

Now use in Git Bash the following command:

git svn fetch -r45065

NOTE: You need to replace the number 45065 against your commit number.
git will now pull the new remote branch into your local repository.

You can then check it out with creating a local Branch. I'm using Git Extension for checking out the new branch, using "Remote-Branch" and the option "Create local branch with name: ...".
Hint: You can remove the "origin/" prefix from your local branch name, as this avoids the warning "refname 'origin/V8.0' is ambiguous.".

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLu&#237;s GuilhermeView Question on Stackoverflow
Solution 1 - SvnVanuanView Answer on Stackoverflow
Solution 2 - Svnn.r.View Answer on Stackoverflow
Solution 3 - SvnMohamed EL HABIBView Answer on Stackoverflow
Solution 4 - SvnrmkView Answer on Stackoverflow
Solution 5 - SvncarolnogueiraView Answer on Stackoverflow
Solution 6 - SvnVonCView Answer on Stackoverflow
Solution 7 - SvnQianlongView Answer on Stackoverflow
Solution 8 - SvncweiskeView Answer on Stackoverflow
Solution 9 - SvnDirkscheView Answer on Stackoverflow