如何将svn分支和标签导入到git-svn中?

75

我有一个中央的SVN仓库需要提交,但是我像其他开发者一样非常喜欢git。这种情况是众所周知的。

然后我了解到git-svn并尝试了一下。由于我只需要最近两个月的历史记录,并不需要完整的历史记录,所以我做了如下操作:

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

SubProject像往常一样有子目录trunktagsbranches。 很好。

然后,为了获取最后一个版本,我执行了以下操作:

git svn rebase

下载了一些内容,很好。查看了最新修订、日志等。现在,我将切换到我的功能分支。

$ git branch 
* master
$ git branch -r  
  trunk
$ git branch -a  
* master
  remotes/trunk

问题是:我的分支在哪里?我做错了什么吗?为了将我的分支放入新的 git repo 中,我应该怎么做?

我从任何地方了解到的 git-svn 对分支和标签处理得很好,但行为并不符合我的期望。谢谢!

编辑:我刚刚发现 git svn fetch 可以做到这一点。但它会获取所有修订版本,而我不想要这样。


2
好的,这并不能回答你的问题,因此我要说一下:当使用git-svn时,你会破坏subversion的合并跟踪功能,因为git-svn不支持它们。在我看来,这个问题本身就使得git-svn在与subversion存储库的严肃使用中失去了资格。我没有找到任何关于是否会开发这个功能的信息——可能不会,因为人们喜欢切换到DVCS而不是使用这样的技巧。 - gimpf
我曾经在某个地方读到,使用--squash将git合并转换为提交不会破坏子版本合并跟踪。 - Luís Guilherme
5
对于您似乎在使用的标准“trunk/branches/tags”布局,您可以尝试使用--stdlayout,例如 git svn clone --stdlayout svn://... - 参见 https://dev59.com/VG435IYBdhLWcg3wigkk。 - Joel Purra
@JoelPurra 不知道别人怎么样,但 --stdlayout 就是我需要的。 - slf
4
“-s”不是“--stdlayout”的缩写吗? - GabrielF
9个回答

82
你需要进行几个步骤。
  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.


3
对于标签迁移,我的 "cut" 命令无法正常工作,可能是因为 "git" 命令的输出不同。这个修改后的命令行可以正常使用: git for-each-ref --format="%(refname:short) %(objectname)" refs/remotes/tags | while read tag ref; do echo git tag -a $tag -m \"Import $tag from svn\" $ref; done - Stéphane
6
我也是。请注意,一旦您满意于命令的执行结果并想启用实际命令,您需要删除命令中的“echo”。我最终得到了以下直接标记父级并使用 SVN 消息的行:git for-each-ref --format="%(refname:short) %(objectname)" refs/remotes/tags | cut -d / -f 2- | while read tag ref ; do msg=$(git log --pretty=format:'%s' -1 ${ref}) ; git tag -f -a $tag -m "$msg" ${ref}^ ; done - Florian
在你的情况下,git for-each-ref --format="%(refname:short)" refs/remotes/tags 的输出是什么? - Vanuan
2
所以,这不是git的输出问题。因为对我来说,“echo tags/v0.0.1 | cut -d / -f 2-”输出“v0.0.1”。你能运行这个命令吗? - Vanuan
11
第二步:在我的计算机上,标签不在refs/remotes/tags中,而是在refs/remotes/origin/tags中。 - Weltraumschaf
显示剩余2条评论

25

这篇内容借鉴了Vanuan在上面的回答,但保留了原始svn标签中的信息,并将其体现在新的git标签中。

$ 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

21

这与nicolai.rostov上面的答案相同,但我只更改了参考路径。 我用cygwin终端中的git版本2.1.1,将refs/remotes/tags替换为refs/remotes/origin/tags

$ 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

在GNOME终端中,已确认可以使用Git v2.17.1。 - zett42
请确认这是与git 2.21.0一起使用所必需的。 - Dirk
4
我发现这会把标签名称创建成 "tag/<标签名称>" 的形式 - 我希望标签名称只是名称本身(主要是因为有强迫症,想要和 gitkraken 显示的一样!),所以我将其更改为 TAG_NAME=${BRANCH#*/*/} - ChrisW

9
你说你在检出时没有获取到分支。
这可能是svn仓库布局的问题。
“标准布局”是:
branches/
tags/
trunk/
如果你的布局像这样:
branches/user1/
branches/user2/
那么,在执行git svn fetch / clone时,你将失去你的分支。
为了解决这个问题,你应该给git clone传递参数
--branches=branches/*/*。

但是如果分支位于根目录中怎么办,就像这里http://svn.code.sf.net/p/azconvert/code/?文件夹`phpport`和`otherfiles`似乎是分支。 - thorn0
你有完全相同的问题吗?你找到了这种情况的解决方案吗? - bigge

7
我编写了一个脚本来帮助您进行迁移。虽然这个脚本不完美,但我希望这可以帮到您:
要获取更多信息,请访问: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.'

6
如果您想在从svn导入后执行git branch时查看分支,您应该使用ruby脚本svn2git(和git2svn)

这比git svn clone更好,因为如果您在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会遍历提交历史记录以构建新的git仓库。
它将所有分支和标签作为远程SVN分支导入,而你真正想要的是本地的git原生分支和git标签对象。 因此,在导入此项目后,您将获得:

  $ 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 ]
< p >在 svn2git 处理完您的项目后,您将得到以下结果:

  $ 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

但是这样做不会杀死提交到svn仓库的所有可能性吗?另外,我的问题不是这个。我还没有得到我的分支。所以这不是代码组织的问题。 - Luís Guilherme
我使用了svn2git,它把我所有的分支和标签都当作了标签处理...换句话说,当我执行git branch命令时,它只显示* master。而当我执行git tag -l命令时,它会显示来自SVN的所有分支和标签...这没有意义。我错过了什么吗? - Sharique Abdullah
@ShariqueAbdullah 不太确定。我现在使用SubGit:https://dev59.com/questions/WHbZa4cB1Zd3GeqPH589#18693798 - VonC

2

对于那些需要在工作中使用Windows的人,这里提供了一个与git版本2.17.0最新同步的解决方案(理论上也适用于之前的版本)。

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"

0

我遇到了同样的问题 - 当我指定版本时,标签和分支都不见了:

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

解决方法是指定一系列的修订版本,-r 34000:HEAD:

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

Git 邮件列表给了我提示。


0

要将新的SVN分支拉到本地Git-SVN存储库中,我发现了一种简单的方法,不需要编辑任何配置文件等。

您只需要知道要添加到本地git-svn存储库中的分支中最后一次提交的修订号。

如果您拥有新分支的最新SVN检出和TortoiseSVN安装,则可以右键单击此文件夹并使用“TortoiseSVN / Show Log”。

记下最上面的提交(例如45065)。

现在在Git Bash中使用以下命令:

git svn fetch -r45065

注意:您需要将数字45065替换为您的提交编号。
git现在将新的远程分支拉入您的本地仓库。

然后,您可以创建一个本地分支进行检查。我使用Git Extension来检出新分支,使用“Remote-Branch”和选项“Create local branch with name: ...”。
提示:您可以从本地分支名称中删除“origin /”前缀,因为这样可以避免警告“refname'origin / V8.0' is ambiguous.”。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接