开源版本控制之版本库Git 和 SVN 协同模型.docx
《开源版本控制之版本库Git 和 SVN 协同模型.docx》由会员分享,可在线阅读,更多相关《开源版本控制之版本库Git 和 SVN 协同模型.docx(14页珍藏版)》请在冰点文库上搜索。
开源版本控制之版本库Git和SVN协同模型
Git和SVN协同模型
第五个部分是我撰写的GIT图书的最重要的一个部分,这个部分马上就要收尾了,以git-svn作为该部分的最后一章。
在Git协同模型部分的最后,我们将会在另外的一个角度上看Git版本库的协同。
不是不同的用户在使用Git版本库时如何协同,也不是一个项目包含多个Git版本库时如何协同,而是当版本控制系统不是Git(如Subversion)时,如何能够继续使用Git的方式进行操作。
5.7 Git和SVN协同模型
Subversion会一直在商业软件开发占据主导,只要商业软件公司封闭源代码的策略不改变。
对于熟悉了Git的用户,一定会对Subversion的那种一旦脱离网络、脱离服务器便寸步难行的工作模式厌烦透顶。
实际上对Subversion的集中式版本控制的不满和改进在Git诞生之前就发生了,这就是SVK。
在2003年(Git诞生的前两年),台湾的高嘉良就开发了SVK,用分布式版本控制的方法操作SVN。
其设计思想非常朴素,既然SVN的用户可以看到有访问权限数据的全部历史,那么也应该能够依据历史重建一个本地的SVN版本库,这样很多SVN操作都可以通过本地的SVN进行从而脱离网络。
当对本地版本库的修改感到满意后,通过本地SVN版本和服务器的SVN版本库的双向同步将改动归并到服务器上。
这种工作方式真的非常酷。
我们不必为SVK的文档缺乏以及不再维护而感到惋惜,因为有更强的工具登场了,这就是git-svn。
Git-svn是Git软件包的一部分,用Perl语言开发。
它的工作原理是:
∙将Subversion版本库在本地转换为一个Git库。
∙转换可以基于Subversion的某个目录,或者基于某个分支,或者整个Subversion代码库的所有分支和里程碑。
∙远程的Subversion版本库可以和本地的Git双向同步。
Git本地库修改推送到远程Subversion版本库,反之亦然。
Git-svn作为Git软件包的一部分,当Git从源码包进行安装时会默认安装,提供gitsvn 命令。
但一些Linux发行版git-svn作为一个独立的软件包,需要手动进行安装。
例如Debian和Ubuntu运行下面命令安装git-svn。
$sudoaptitudeinstallgit-svn
将git-svn独立安装是因为git-svn软件包有着特殊的依赖,即依赖Subversion的perl语言绑定接口,Debian/Ubuntu上由libsvn-perl软件包提供。
当git-svn正确安装后,就可以使用gitsvn 命令了。
但如果在执行gitsvn–version 时遇到下面的错误,则说明Subversion的perl语言绑定没有正确安装。
$gitsvn--version
Can'tlocateloadableobjectformoduleSVN:
:
_Corein@INC(@INCcontains:
/usr/share/perl/5.10.1/etc/perl/usr/local/lib/perl/5.10.1/usr/local/share/perl/5.10.1/usr/lib/perl5/usr/share/perl5/usr/lib/perl/5.10/usr/share/perl/5.10/usr/local/lib/site_perl/usr/local/lib/perl/5.10.0/usr/local/share/perl/5.10.0.)at/usr/lib/perl5/SVN/Base.pmline59
BEGINfailed--compilationabortedat/usr/lib/perl5/SVN/Core.pmline5.
Compilationfailedinrequireat/usr/lib/git-core/git-svnline41.
遇到上面的情况,需要检查本机是否正确安装了Subversion以及Subversion的perl语言绑定。
为了便于对git-svn的介绍和演示,我们需要有一个Subversion版本库,并且需要有提交权限以便演示用Git向Subversion进行提交。
最好的办法是在本地创建一个Subversion版本库。
$svnadmincreate/path/to/svn/repos
$svncofile:
///path/to/svn/repossvndemo
取出版本0
$cdsvndemo
$mkdirtrunktagsbranches
$svnadd*
Abranches
Atags
Atrunk
$svnci-m"initialized."
增加branches
增加tags
增加trunk
提交后的版本为1。
我们再向Subversion开发主线trunk中添加些数据。
$echohello>trunk/README
$svnaddtrunk/README
Atrunk/README
$svnci-m"hello"
增加trunk/README
传输文件数据.
提交后的版本为2。
建立分支:
$svnup
$svncptrunkbranches/demo-1.0
Abranches/demo-1.0
$svnci-m"newbranch:
demo-1.0"
增加branches/demo-1.0
提交后的版本为3。
建立里程碑:
$svncp-m"newtag:
v1.0"trunkfile:
///path/to/svn/repos/tags/v1.0
提交后的版本为4。
5.7.1 使用git-svn的一般流程
使用git-svn的一般流程为:
gitsvnclone
|
v
(本地Git库)
|
v
+->(hack...)
||
|v
|gitadd
||
|v
|gitcommit
||
+-----+
|
v
gitsvnrebase
|
v
gitsvndcommit
首先用gitsvnclone命令对Subversion进行克隆,创建一个包含git-svn扩展的本地Git库。
在下面的示例中,我们使用Subversion的本地协议(file:
//)来访问之前创立的Subversion示例版本库,实际上git-svn可以使用任何Subversion可用的协议,并可以对远程版本库进行操作。
$gitsvnclone-sfile:
///path/to/svn/reposgit-svn-demo
InitializedemptyGitrepositoryin/my/workspace/git-svn-demo/.git/
r1=2c73d657dfc3a1ceca9d465b0b98f9e123b92bb4(refs/remotes/trunk)
AREADME
r2=1863f91b45def159a3ed2c4c4c9428c25213f956(refs/remotes/trunk)
Foundpossiblebranchpoint:
file:
///path/to/svn/repos/trunk=>file:
///path/to/svn/repos/branches/demo-1.0,2
Foundbranchparent:
(refs/remotes/demo-1.0)1863f91b45def159a3ed2c4c4c9428c25213f956
Followingparentwithdo_switch
Successfullyfollowedparent
r3=1adcd5526976fe2a796d932ff92d6c41b7eedcc4(refs/remotes/demo-1.0)
Foundpossiblebranchpoint:
file:
///path/to/svn/repos/trunk=>file:
///path/to/svn/repos/tags/v1.0,2
Foundbranchparent:
(refs/remotes/tags/v1.0)1863f91b45def159a3ed2c4c4c9428c25213f956
Followingparentwithdo_switch
Successfullyfollowedparent
r4=c12aa40c494b495a846e73ab5a3c787ca1ad81e9(refs/remotes/tags/v1.0)
CheckedoutHEAD:
file:
///path/to/svn/repos/trunkr2
从上面的输出我们看到,当执行了gitsvnclone之后,在本地工作目录创建了一个Git库(git-svn-demo),并将Subversion的每一个提交都转换为Git库中的提交。
我们进入git-svn-demo目录,看看我们用git-svn克隆出来的版本库。
$cdgit-svn-demo/
$gitbranch-a
*master
remotes/demo-1.0
remotes/tags/v1.0
remotes/trunk
$gitlog
commit1863f91b45def159a3ed2c4c4c9428c25213f956
Author:
jiangxin
Date:
MonNov105:
49:
412010+0000
hello
git-svn-id:
file:
///path/to/svn/repos/trunk@2f79726c4-f016-41bd-acd5-6c9acb7664b2
commit2c73d657dfc3a1ceca9d465b0b98f9e123b92bb4
Author:
jiangxin
Date:
MonNov105:
47:
032010+0000
initialized.
git-svn-id:
file:
///path/to/svn/repos/trunk@1f79726c4-f016-41bd-acd5-6c9acb7664b2
我们看到Subversion版本库的分支和里程碑都被克隆出来,并保存在refs/remotes下的引用中。
在gitlog 的输出中,我们可以看到Subversion的提交的确被转换为Git的提交。
下面我们就可以在Git库中进行修改,并在本地提交(用gitcommit命令)。
$catREADME
hello
$echo"Iamfine.">>README
$gitadd-u
$gitcommit-m"myhack1."
[master55e5fd7]myhack1.
1fileschanged,1insertions(+),0deletions(-)
$echo"Thankyou.">>README
$gitadd-u
$gitcommit-m"myhack2."
[masterf1e00b5]myhack2.
1fileschanged,1insertions(+),0deletions(-)
我们对工作区中的README文件修改了两次,并进行了本地的提交。
我们查看这时的提交日志,会发现最新两个只在本地Subversion版本库的提交和之前Subversion中的提交的不同。
区别在于最新在Git中的提交没有用git-svn-id:
标签标记的行。
$gitlog
commitf1e00b52209f6522dd8135d27e86370de552a7b6
Author:
JiangXin
Date:
ThuNov415:
05:
472010+0800
myhack2.
commit55e5fd794e6208703aa999004ec2e422b3673ade
Author:
JiangXin
Date:
ThuNov415:
05:
322010+0800
myhack1.
commit1863f91b45def159a3ed2c4c4c9428c25213f956
Author:
jiangxin
Date:
MonNov105:
49:
412010+0000
hello
git-svn-id:
file:
///path/to/svn/repos/trunk@2f79726c4-f016-41bd-acd5-6c9acb7664b2
commit2c73d657dfc3a1ceca9d465b0b98f9e123b92bb4
Author:
jiangxin
Date:
MonNov105:
47:
032010+0000
initialized.
git-svn-id:
file:
///path/to/svn/repos/trunk@1f79726c4-f016-41bd-acd5-6c9acb7664b2
现在我们就可以向Subversion服务器推送我们的改动了。
但真实的环境中,往往在我们向服务器推送时,已经有其它用户先于我们在服务器上进行了提交。
而且往往更糟的是,先于我们的提交会造成我们的提交冲突!
我们现在就人为的制造一个冲突:
使用svn命令在Subversion版本库中执行一次提交。
$svncheckoutfile:
///path/to/svn/repos/trunkdemo
Ademo/README
取出版本4。
$cddemo/
$catREADME
hello
$echo"HELLO.">README
$svncommit-m"hello->HELLO."
正在发送README
传输文件数据.
提交后的版本为5。
好的,我们已经模拟了一个用户先于我们更改了Subversion版本库。
现在回到我们用git-svn克隆的本地版本库,执行gitsvndcommit 操作,将我们在Git中的提交推送的Subversion版本库中。
$gitsvndcommit
Committingtofile:
///path/to/svn/repos/trunk...
事务过时:
文件“/trunk/README”已经过时at/usr/lib/git-core/git-svnline572
显然,由于Subversion版本库中包含了新的提交,导致我们执行gitsvndcommit 出错。
这时我们需执行gitsvnfetch 命令,以从Subversion版本库获取更新。
$gitsvnfetch
MREADME
r5=fae6dab863ed2152f71bcb2348d476d47194fdd4(refs/remotes/trunk)
15:
37:
08jiangxin@hp:
/my/workspace/git-svn-demo$gitst
#Onbranchmaster
nothingtocommit(workingdirectoryclean)
当我们获取了新的Subversion提交之后,我们需要执行gitsvnrebase 将我们Git中未推送到Subversion的提交通过变基(rebase)形成包含Subversion最新提交的线性提交。
这是因为Subversion的提交都是线性的。
$gitsvnrebase
First,rewindingheadtoreplayyourworkontopofit...
Applying:
myhack1.
Usingindexinfotoreconstructabasetree...
Fallingbacktopatchingbaseand3-waymerge...
Auto-mergingREADME
CONFLICT(content):
MergeconflictinREADME
Failedtomergeinthechanges.
Patchfailedat0001myhack1.
Whenyouhaveresolvedthisproblemrun"gitrebase--continue".
Ifyouwouldprefertoskipthispatch,insteadrun"gitrebase--skip".
Torestoretheoriginalbranchandstoprebasingrun"gitrebase--abort".
rebaserefs/remotes/trunk:
commandreturnederror:
1
果不其然,变基时发生了冲突,这是因为Subversion中他人的修改和我们在Git库中的修改都改动了同一个文件,并且改动了相近的行。
下面按照gitrebase 冲突解决的一般步骤进行,直到成功完成变基操作。
先编辑README文件,以解决冲突。
$gitstatus
#Notcurrentlyonanybranch.
#Unmergedpaths:
#(use"gitresetHEAD..."tounstage)
#(use"gitadd/rm..."asappropriatetomarkresolution)
#
#bothmodified:
README
#
nochangesaddedtocommit(use"gitadd"and/or"gitcommit-a")
15:
49:
30jiangxin@hp:
/my/workspace/git-svn-demo$viREADME
处于冲突状态的REAEME文件内容。
<<<<<<
HELLO.
=======
hello
Iamfine.
>>>>>>>myhack1.
下面是我们修改后的内容。
保存退出。
HELLO.
Iamfine.
执行gitadd命令解决冲突
$gitaddREADME
调用gitrebase–continue 完成变基操作。
$gitrebase--continue
Applying:
myhack1.
Applying:
myhack2.
Usingindexinfotoreconstructabasetree...
Fallingbacktopatchingbaseand3-waymerge...
Auto-mergingREADME
看看变基之后的Git库日志:
$gitlog
commite382f2e99eca07bc3a92ece89f80a7a5457acfd8
Author:
JiangXin
Date:
ThuNov415:
05:
472010+0800
myhack2.
commit6e7e0c7dccf5a072404a28f06ce0c83d77988b0b
Author:
JiangXin
Date:
ThuNov415:
05:
322010+0800
myhack1.
commitfae6dab863ed2152f71bcb2348d476d47194fdd4
Author:
jiangxin
Date:
ThuNov407:
15:
582010+0000
hello->HELLO.
git-svn-id:
file:
///path/to/svn/repos/trunk@5f79726c4-f016-41bd-acd5-6c9acb7664b2
commit1863f91b45def159a3ed2c4c4c9428c25213f956
Author:
jiangxin
Date:
MonNov105:
49:
412010+0000
hello
git-svn-id:
file:
///path/to/svn/repos/trunk@2f79726c4-f016-41bd-acd5-6c9acb7664b2
commit2c73d657dfc3a1ceca9d465b0b98f9e123b92bb4
Author:
jiangxin
Date:
MonNov105:
47:
032010+0000
initialized.
git-svn-id:
file:
///path/to/svn/repos/trunk@1f79726c4-f016-41bd-acd5-6c9acb7664b2
当变基操作成功完成后,我们再执行gitsvndcommit 向Subversion推送我们在Git库中的两个新提交。
$gitsvndcommit
Committingtofile:
///path/to/svn/repos/trunk...
MREADME
Committedr6
MREADME
r6=d0eb86bdfad4720e0a24edc49ec2b52e50473e83(refs/re