侧边栏壁纸
博主头像
孔子说JAVA博主等级

成功只是一只沦落在鸡窝里的鹰,成功永远属于自信且有毅力的人!

  • 累计撰写 285 篇文章
  • 累计创建 125 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

代码从svn迁移到git实战记录,保留历史提交记录

孔子说JAVA
2022-04-22 / 0 评论 / 0 点赞 / 151 阅读 / 8,172 字 / 正在检测是否收录...

产品线之前一直用的svn作为项目版本控制库,完成后需要移交项目以及便于管理所以将svn转到git仓库。本教程使用git svn clone 命令进行迁移,当然你也可以尝试使用subgit、svn2git等其他工具或命令进行迁移。

1、准备工作

1.1 环境准备

  • 本地需要安装svn,git客户端。
  • git服务器需要创建对应的仓库并开通相应权限。
  • 安装并且配置本地git账户信息,可通过 git config -l 验证
  • 个人SVN的用户名和密码(同步时需要输入)

1.2 svn目录结构

先来看看svn目录结构,这个会关系到我们如何迁移。以下说的都是单个项目的迁移。

1)标准的目录结构

/d/proj1
├── branches
│   ├── a
│   │   └── readme.txt
│   └── b
│       ├── 11.txt
│       └── readme.txt
├── tags
│   └── v1.0
│       ├── 11.txt
│       └── readme.txt
└── trunk
    └── readme.txt

2)非标准的目录结构

此存储库没有分支或标签不是标准化的,需要将其转换为git存储库并维护提交历史记录。

/d/proj1
├── branches
│   ├── a
│   │   └── readme.txt
│   └── b
│       ├── 11.txt
│       └── readme.txt
├── develop
└── trunk
    └── readme.txt

3)扁平化的目录结构

此存储库没有分支或标签,需要将其转换为git存储库并维护提交历史记录。

/d/proj1
├── file1.c
├── file2.c
├── file3.c
└── readme.txt

2、迁移步骤

从svn迁移到git的流程就是从svn拉取代码到本地,然后推送到git。当然我们肯定是希望能保留svn中的提交记录,这个完全没有问题。

2.1 签名映射

在迁移之前,我们先要确认svn中的提交署名和要迁入的git系统署名(即同一用户在svn仓库和git仓库中的用户名是否一致),如果不一致,这个时候可以指定名字映射,通过参数--authors-file指定。如果签名要保持一致,那么可以忽略这个参数。

  • 如果具备svn账号又没有svn管理员权限,可以通过导出所有提交历史并用代码提取一下用户清单(开发人员都懂,不赘述)。

获取原svn仓库的贡献者名单,通过以下命令即可:

$ svn log –xml | grep author | sort -u | perl -pe ‘s/.*>(.*?)<.*/$1 = /’

获得名单后,创建一个userinfo.txt文件,格式如下:

假设获取到的用户清单为user1,user2,user3。创建文件users.txt,按如下格式存入svn账号与git账号的映射关系:

user1=Zhang San<zhangsan@126.com>
user2=user2<user2@126.com>
user3=user<user2@126.com>
……
  • “=”前为svn账号,“=”号后为git账号,尖括号为git用户的邮箱,尖括号及邮箱必须要有。
  • 对于无法对应的人员,如离职人员,有SVN账号,但是没有现在的GIT账号,保险起见,可以采用无关紧要的账号进行对应,或者单独建一个账号也可以,我就使用的test账号进行标识的。
  • 获取的每一个svn仓库贡献者都必须在users.txt文件中列出,一个都不能少(貌似多几个没关系)!否则后面迁移的时候将前功尽弃!本人就是因为偷懒少写了一个名字,结果屡次迁移不成功。
  • 最好在 users.txt 中增加一个这样的用户名。我发现在后面git svn clone的时候,会提示找不到(no author)这个用户而导致clone失败:
(no author)=No Author<noauthor@localhost>

2.2 svn克隆

创建空文件夹用于存放SVN的代码,如果userinfo.txt存在的话需将 userinfo.txt 文件放置在同一层级。

使用 git svn clone 命令克隆svn项目,这是svn迁移到git的核心命令。对于不同目录结构的svn项目来说,命令行参数是不一样的。

  • 该命令包含在git中,需要先安装git。
  • 语法:git svn clone svn地址 参数....
# 没有指定签名映射表示所有签名保持一致,同步所有历史记录
git svn clone https://svn.***.io/***/demo/ --no-metadata demo

# 指定了签名映射,同步所有历史记录
git svn clone https://svn.***.io/***/demo/ --no-metadata --authors-file=userinfo.txt demo

# 指定了签名映射,只同步版本号38163以后的提交记录(-r 38163:head 表示只同步版本号38163以后的提交记录,避免过多的同步无用数据,如果需要同步所有的提交记录,可删除该段命令)
git svn clone -r 38163:head https://svn.***.io/***/demo/ --no-metadata --authors-file=userinfo.txt demo
git svn clone -r 38163:HEAD svn地址 --no-metadata --authors-file=userinfo.txt lihua

这个命令时间比较长,因为需要同步所有的提交历史,仅此一次,以后不会这么慢了。做完这一步,在本地就有了一个完整的代码库,包括所有commit的历史和log,已经可以开始用它来进行开发工作了。

  • 注意:第一次操作,会多次要求账号密码:请注意每次提示信息,第一次密码输入是操作系统的用户密码,第二次为svn账号和密码,第三次为git的账号密码。注意按提示输入。
  • 查看具体的SVN的提交记录,在SVN项目中右击tortoiseSVN–>show log–>show all,即可看到所有的提交记录,复制对应的版本号,替换上面的命令即可

注:在开始开发之前,最好先做一次垃圾搜集:

git gc

它是对代码库的信息进行垃圾搜集和压缩,最明显的作用就是减小磁盘占用空间。第一次做效果尤其明显。

2.2.1 标准的目录结构

命令示例:

git svn clone svn://192.168.1.102:8888/proj/ --no-metadata --authors-file=userinfo.txt --trunk=trunk --tags=tags --branches=branches projname

示例中各参数的含义如下:

  • 参数–no-metadata表示阻止git导出SVN包含的一些无用信息(即svn的特殊标记)
  • 参数–authors-file表示SVN账号映射到git账号文件,所有svn作者都要做映射,userinfo.txt,为你的用户映射文件。
  • 参数–trunk表示主开发项目,一般就是trunk
  • 参数–branches表示分支项目,--ignore-refs表示不包含后面的分支项目
  • 参数 projname 表示git项目名称

对于标准svn目录结构的项目可以使用上例中的命令,也可以使用以下简化的命令:

git svn clone svn://192.168.1.102:8888/proj/ --no-metadata -s
  • 参数–no-metadata表示阻止git导出SVN包含的一些无用信息(即svn的特殊标记)。
  • 参数–-s表示使用svn的标准布局(比如trunk,tags等)。

2.2.2 非标准的目录结构

检出不包含develop分支的代码,通过 --trunk=trunk, --tags=tags等参数指定映射关系。

git svn clone svn://192.168.1.102:8888/proj/ --no-metadata --trunk=/ --tags=tags --branches=branches proj
  • --trunk=/ 表示trunk对应本地的根目录

2.2.3 扁平化的目录结构

git svn clone svn://192.168.1.102:8888/proj/ --no-metadata proj
  • 参数–no-metadata表示阻止git导出SVN包含的一些无用信息(即svn的特殊标记),可以去掉。

2.3 关联git远程仓库

执行 cd 项目名称 进入迁移项目路径下,执行git remote add 命令,git remote 命令用于在远程仓库的操作。

  • 语法:git remote add [shortname] [url]
    • shortname 为本地的版本库(远程地址的别名)
    • url 为git仓库地址

示例:

git remote add origin https://git.***.io/media/.***./demo.git

执行 git remote –v 命令可查看关联情况,关联成功后提示信息如下:

E:\ideagit\demo>git remote -v
origin  https://git.***.io/media/.***./demo.git (fetch)
origin  https://git..***..io/media/.***./demo.git (push)

如果关联错了,可以使用 git remote rm origin 删除错位关联后重新关联即可。

git remote rm origin

2.4 拉取远程代码到本地(非必须)

git pull 命令用于从远程获取代码并合并本地的版本。git pull 其实就是 git fetchgit merge FETCH_HEAD 的简写。 命令格式如下:

  • git pull <远程主机名> <远程分支名>:<本地分支名>

如果创建git版本库的时候勾选了README.md.gitignore并生成了这两个文件,需要先拉取代码再提交;或者运行git push -u origin master -f 强制提交(仅用于初次提交);或者干脆创建空的git版本库,再手动添加这两个文件。

git pull origin develop:develop
# 或
git pull --rebase origin master
# 同步本地分支和远程分支
git pull 或 git fetch
# 同步所有分支
git pull --all 或 git fetch --all

git pull 和 git fetch的区别?

git fetch 是将远程主机的最新内容拉到本地,用户在检查了以后决定是否合并到工作本机分支中。 而 git pull 则是将远程主机的最新内容拉下来后直接合并,即: git pull = git fetch + git merge ,这样可能会产生冲突,需要手动解决。

2.5 提交代码到git远程仓库

svn项目中的所有提交记录和历史版本都已经导出本地并关联了远程仓库,现在只需要将这些记录推送至远程仓库即可。使用git命令将各个分支推送到git远端,git push 命令用于从将本地的分支版本上传到远程并合并。命令输入后需要输入git账号\密码。

  • 语法:git push <远程主机名> <本地分支名>:<远程分支名>
  • 如果本地分支名与远程分支名相同,则可以省略冒号:git push <远程主机名> <本地分支名>

示例:

git push origin develop

git push -u origin master

git push -u origin develop:kongzi

git push --all origin
  • 带上 -u 参数其实就相当于记录了push到远端分支的默认值,这样当下次我们还想要继续push的这个远端分支的时候推送命令就可以简写成 git push 即可。
  • git push --all origin 表示不管是否存在对应的远程分支,将本地的所有分支都推送到远程主机,这时需要 -all 选项。

执行成功后,就完成了整个迁移过程。登录git网站,可以看到空白仓库中多了很多提交记录和文件。

3、通过新分支提交代码(出现了 Everything up-to-date 错误的解决步骤)

如果出现了 Everything up-to-date 错误,原因是git提交改动到缓存,要push的时候不会将本地所有的分支都push掉,所以出现这个问题。我们应该告诉git提交哪个分支。这里有种特殊的情况是如果你是fork别人的仓库再clone到本地的话,即使git上只有一个主分支,他还是可能出现这个错误。那么我们就需要新建分支提交改动然后合并分支。这时候我们按以下步骤解决。具体错误信息如下:

# 错误信息
Everything up-to-date
branch 'develop' set up to track 'origin/develop'.

3.1 创建新分支提交后合并

先使用 git branch newbranch 命令创建一个新分支提交改动的代码。

  • newbranch 为新分支的名称

然后输入 git branch 命令检查是否创建成功,这时候输出内容如下:

  newbranch
* master

这样就创建成功了,前面的*代表的是当前你所在的工作分支。newbranch 是我们新创建的分支,我们接下来就要切换工作分支。

$ git checkout newbranch

这样就切换完了,可以 git branch确认下。然后你要将你的改动提交到新的分支上。

$ git add .
$ git commit -a

此时可以 git status 检查下提交情况。如果提交成功,我们接下来就要回主分支了,代码和之前一样。

$ git checkout master

然后我们要将新分支提交的改动合并到主分支上

$ git merge newbranch

合并分支可能产生冲突这是正常的,虽然我们这是新建的分支不会产生冲突,但还是在这里记录下。下面的代码可以查看产生冲突的文件,然后做对应的修改再提交一次就可以了。

$ git diff

我们的问题就解决了,接下来就可以push代码了。

$ git push -u origin master

新建分支的朋友别忘了删除这个分支

$ git branch -D newbranch

如果想保留分支只是想删除已经合并的部分只要把大写的D改成小写的d就行了。

3.2 开发分支合并示例

1) 开发分支(dev)上的代码达到上线标准后,合并到 master 分支

git checkout dev
git pull
git checkout master
git pull
# merge  --no-ff参数,表示禁用Fast forward;可以保存之前的分支历史。能够更好的查看merge历史,以及branch状态.
#保证版本提交、分支结构清晰
git merge --no-ff  dev
git push -u origin master

master 分支为保护分支时,执行 git push -u origin master 会提示远程服务器拒绝,此时需要在浏览器进行远程仓库 `merge 操作。

2) 当master代码改动,需要更新开发分支(dev)上的代码

git checkout master 
git pull 
git checkout dev
# merge  --no-ff参数,表示禁用Fast forward;可以保存之前的分支历史。能够更好的查看merge历史,以及branch状态.
#保证版本提交、分支结构清晰
git merge --no-ff  master
git push -u origin dev

3、善后操作(可忽略)

$ cp -Rf .git/refs/remotes/origin/tags/* .git/refs/tags/
$ rm -Rf .git/refs/remotes/origin/tags
$ cp -Rf .git/refs/remotes/* .git/refs/heads/
$ rm -Rf .git/refs/remotes

4、常见问题

1)svn-remote.svn.url already set

svn-remote.svn.url already set: httpssvn.***.io***....,产生这个错误的原因是赋值的url地址粘贴到dos窗口中会自动去除路径中的 /,如果执行了会在.git中保存有错误的历史信息,如果改正了命令重新执行时需要删除本地项目目录下旧的.git文件夹。

2)clone时出现问题导致中断,运行git svn fetch可以继续clone

3)无法push代码error: failed to push some refs to

如果创建git版本库的时候勾选了README.md.gitignore并生成了这两个文件,需要先拉取代码再提交;或者运行git push -u origin master -f 强制提交(仅用于初次提交);或者干脆创建空的git版本库,再手动添加这两个文件。

4)error: src refspec develop does not match any.

如果说我在远程创建分支develop,它是基于master创建的,那么当我想把代码往develop提交的时候报错,

error: src refspec develop does not match any
error: failed to push some refs to 'https://git.***.io/***.git'

这种原因是因为本地没有develop分支。解决:git push origin master:develop

  • 语法:git push <远程主机名> <本地分支名>:<远程分支名>

5)Everything up-to-date

# 错误信息
Everything up-to-date
branch 'develop' set up to track 'origin/develop'.

出现以上提示信息原因是git提交改动到缓存,要push的时候不会将本地所有的分支都push掉,所以出现这个问题。我们应该告诉git提交哪个分支。
这里有种特殊的情况是如果你是fork别人的仓库再clone到本地的话,即使git上只有一个主分支,他还是可能出现这个错误。那么我们就需要新建分支提交改动然后合并分支。

5、常用命令

# 1. 下载一份代码

git svn clone **** --username=***

# 2. 查看log

git log (git svn log -v 可以显示每次修改提交的文件)

git log -p (可以查看每次提交log和对应的修改内容)

git log --stat (可以查看每次提交log和对应的修改文件)

# 3. 查看某人的log

git log --author=***

# 4. 撤销工作区的修改

git checkout -- file 

#注:如果没有--,表示切换到某个分支。

# 5. 撤销暂存区的修改

git reset --hard HEAD file

# 6. 与远端同步

git svn rebase

# 7. 将修改提交到svn

git svn dcommit

# 8. 查看状态

git status

# 9. 显示分支情况

git branch -a

# 查看本地分支和对应的远程分支
git branch -vv

# 10. 新建分支

git branch new-branch-name (git checkout -b new_branch(新建分支并跳转到分支))

# 11. 删除分支

git branch -d new_branch (删除分支时,不可处于当前分支)

# 12. 远程仓库的操作命令

a、git remote     命令列出所有远程主机/仓库

b、git remote -v      命令列出远程主机/仓库以及其网址
  
c、git remote add <主机名><网址>     命令用于添加远程主机/仓库

d、git remote rm <主机名>     命令 用于删除远程主机/仓库

e、git remote rename <原主机名><新主机名>     命令用于修改主机/仓库名称

f、git remote set-url <主机名><新URL>     命令用于修改远程仓库/仓库网址

# 13 在本地创建一个分支并且与远程仓库的origin/xxx分支关联

git checkout --track origin/develop
git checkout --track origin/kongzi


# 14 优雅的撤销某次提交【中间的某次也可以】
git revert
0

评论区