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


69

我有一个必须致力于的中央SVN存储库,但是我对git充满热情(就像我认识的任何其他开发人员一样)。此案是众所周知的。

然后,我阅读了有关git-svn的信息,并进行了尝试。由于我不需要完整的历史记录,所以只需要两个月左右的时间,因此我做到了:

git svn clone -r 34000 -s https://svn.ourdomain.com/svn/repos/Project/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 2010年

我读过某个地方,使用--squash在提交中转换git合并不会破坏subversion合并跟踪。
路易斯·吉列尔梅

5
对于标准的“主干/分支/标签”的布局,你似乎可以用,你可以尝试--stdlayout,如git svn clone --stdlayout svn://...-见stackoverflow.com/questions/5361559/...
乔尔Purra

@JoelPurra不了解其他人,但是--stdlayout那是我所需要的
SLF 2013年

4
是不是-s短裤--stdlayout
加布里埃尔(JabrielF)2015年

Answers:


76

您将需要几个步骤。

  1. 提供适当的主干,分支和标签文件夹名称并获取svn repo:

    git svn init -t tags -b branches -T trunk https://mysvn.com/svnrepo
    git svn fetch
    
  2. 由于svn中的标签是真实分支,因此请从标签分支创建git标签:

    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. 删除标签分支

    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. 由于在上一步中标记的标签指向提交“创建标签”,因此我们需要派生“真实”标签,即“创建标签”提交的父级。

    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. 我们现在要做的就是删除旧标签。


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
斯特凡

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

7
第2步:在我的机器上的代码并不refs/remotes/tags代替refs/remotes/origin/tags
Weltraumschaf

24

这借鉴了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

15

这与上面的nicolai.rostov的答案相同,但是我只是将 使用git version替换refs/remotes/tagsrefs/remotes/origin/tags我的refs路径更改2.1.1cygwin终端。

$ 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一起使用。
德克

1
我发现这会将标签名称创建为“标签/ <标签名称>”-我希望标签名称仅作为名称(主要是由于OCD,以匹配gitkraken显示的内容!),所以我将其更改为TAG_NAME=${BRANCH#*/*/}
ChrisW

9

您说您尚未结帐。

这可能是您svn库的布局问题。

“标准布局”为:

branches/

tags/

trunk/

如果您的布局是这样的:

branches/user1/

branches/user2/

然后,当您执行git svn fetch / clone时,您将失去分支。

为了解决这个问题,你应该给参数

--branches=branches/*/* git克隆。


但是,如果分支位于此处svn.code.sf.net/p/azconvert/code的根中怎么办?这些文件夹phpportotherfiles似乎是分支机构。
刺̈16-10-25

5

如果想从svn导入后执行git分支时看到分支,则应使用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 repo。
它将所有分支和标签导入为远程SVN分支,而您真正想要的是git-native本地分支和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 ]

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 repo的所有可能性?另外,我的问题不是那样。我没有分公司。因此,这与代码组织无关。
路易斯·吉列尔梅

我用了svn2git,它把我所有的分支和标签都当作标签。所以换句话说,当做git branch时,它只是向我显示* master。当执行git tag -l时,它向我显示了SVN的所有分支和标记...没有任何意义。我错过了什么 ?
Sharique Abdullah

@ShariqueAbdullah不确定。我这几天使用SubGit:stackoverflow.com/a/18693798/6309
VonC

5

我写了一个脚本来帮助您进行迁移。该脚本并不完美,但是我希望这可以对您有所帮助:

有关更多信息,您可以访问: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.'

2

对于那些需要在工作中使用Windws的人,这是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的邮件列表给我的暗示。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.