git-svn 的知识总结

概论

先简单介绍一下git 和svn 的差异之处. 我认为比较需要注意的部份如下:

  1. git 是分散式版本控制系统. 这点和svn 的中央控管式有很大的不同. 最显​​著的差别有:
    • git 的commit 只是送到本地的repo 而已. 如果本地repo 有上游的话, 需要手动送回上游, 才会把本地更动送回去. 也就是说, 在本地repo 随意乱搞是不会影响上游的.
    • 因为是分散式, 所以本地repo 拥有所有的history.
    • 因为本地repo 拥有所有的history, 所以所有的动作都在本地进行, 速度飞快~
    • 除了跟上游repo 同步之外, 其它时间不会使用到网路. 意思是你在车上, 在飞机上, 在深山的温泉里都可以进行commit, 等到有网路时再同步.
  2. git 分支(branch) 功能超容易, merge 的功能也超强. svn 以目录习惯来做分支. 虽然比cvs 方便, 但是也因此被很多人误用. 我在工作上看过好几次别人跨越分支目录在commit程式的.

初步设定

在开始使用之前, 系统要先装git 和git-svn. 这部份我相信稍微搜寻一下, 应该就有一卡车的文章. 而且各un*x 系统应该都有提供套件可供安装, 应该不会太困难.

程式装好之后, 再来就是​​先把自己想要使用的svn repo clone 一份下来. 如果svn repo 使用svnbook 建议的目录结构, 它应该会有SVN_URL/{trunk,branches,tags} 的结构. 指令就会是:

git svn init -s SVN_URL {TARGET_DIR}

其中{TARGET_DIR}是想要储存在本地磁碟的目录名称.如果你的状况复杂点,请参考git svn --help看看有什么参数可以使用.

请注意以下有关于分支的介绍, 都是以标准目录结构的svn repo 来讲的. 如果你的svn repo 不是的话(是说真的有人这样做吗?), 那你得自己再研究怎么用git管理svn repo.

这个步骤会把svn repo 整个clone 下来. 视svn repo 的大小, 这个步骤可能只要几分钟, 也可能要几个礼拜(是的, 我跑过超过一个礼拜的). 完成之后, 你应该就有一个上游是svn repo 的git repo 可以用了. 可以用

git svn info

看看这个git repo 对应的svn repo 资讯.

现在可以用

git branch

看看目前的git 分支. 应该只会有master. 这个是对应到svn repo 的trunk. 原则上我会把master 只对应到svn 的trunk. 我以前曾经让它在不同的svn 分支切换, 结果有一次就造成严重的问题, 一堆commit 都不见了. 修了很久才把消失的commit 都捡回来. 以下的使用方法, 在我自己使用一年多以来, 还没发生过问题. 只要好好遵​​守, 我想应该不会出什么大问题才是.

基本流程

懒得一步步写, 直接看吧.

git svn rebase # 抓回上游svn repo 的更新
git log --name-status # 类似svn log -v. 看看commit log.
# 编辑程式
git diff # 看看改了什么
git add -i # 挑要commit 的档案. -i 超方便的~
git commit # commit 回*本地* 的git repo
# 重复编辑程式到commit 之间的动作
git svn rebase # 再跟上游svn repo 进行同步. 有conflict 要修好~
git svn dcommit # 把本地的commit 送回svn repo

需要一提的是git在repo和working copy之间,还有一个stage.你得把working copy里需要commit的档案,先透过git add加进stage,再用git commit将stage里的更动commit回git repo.我会建议使用git add -i提供的互动命令列来把档案加进stage,还满方便的.

svn repo 的分支

很不幸地,我们不能完全丢掉svn.要建立svn的分支,还是建议使用svn来建.虽然有git svn branch可以使用,不过我并不推荐.

另外,要merge svn的分支一样得使用svn. git在这里也帮不上忙.最主要的原因,是git-svn并不会维护svn:merge-info .这在svn里可是很重要的!

但是建立和merge svn 的分支的情况毕竟不多. 绝大多数的使用情况, 还是基本流程而已. 我们还是可以用git 来同步上游svn 分支.

最开始的git clone -s会把分支和标记(tag)都一并复制下来.我们可以使用

git branch -r

来看看上游svn的分支.不过它会列出*所有*曾经使用过的分支名称,所以我们还是得使用svn ls SVN_URL/branches来看看目前有哪些分支.

决定好要使用哪个svn repo, 接下来就是切换过去使用. 假设分支的名称叫BRANCH:

git checkout -b svn-BRANCH remotes/BRANCH

现在我们建立了一个本地的git分支,它的名称是svn-BRANCH ,它的上游是svn的BRANCH分支,而且我们也切换到svn-BRANCH分支里来了.接下来的基本流程,就都是发生在svn-BRANCH里.和上游同步也会送到BRANCH分支去:

git svn fetch --all # 抓回上游svn repo *所有* 的更新
git svn rebase # 同步上游svn repo 的更新
git log --name-status # 接近svn log -v. 看看commit log.
# 编辑程式
git diff # 看看改了什么
git add -i # 挑要commit 的档案
git commit # commit 回*本地* 的git repo
# 重复编辑程式到commit 之间的动作
git svn rebase # 再跟上游svn repo 进行同步. 有conflict 要修好~
git svn dcommit # 把本地的commit 送回svn repo

请注意这里多了git svn fetch --all .这里是抓回上游svn repo的所有更新,包含各个分支的更新.而且它只会抓到本地端而已,不会合并至目前的git repo.要到git svn rebase指令才会合并至目前的git repo.使用git svn fetch --all的原因是分支之间可能会有merge, git-svn会自动找出来,并建立之间的关联.

建立本地git 分支

前面有提到git 的分支功能非常强大, 使用git 而不用到它的分支功能, 可以说是一种浪费. XD

本节会放在svn branch 的后面, 是因为我会介绍我惯用的命名法. 它可以让我只要看分支的名称, 就可以知道它的来源, 以决定我在分支做完之后要merge 回哪里,减少我犯错的机会.

命名法很简单, 它包含三个部份:

{SOURCE_TYPE}-{NAME}-{SOURCE}

其中:

  • {SOURCE_TYPE} : git或svn .标明它的上游在哪一个repo.
  • {NAME} :分支名称.基本上就是描述这个分支要做什么.
  • {SOURCE} :从哪一个分支建立出来的.

git 分支的建立原则上是:

  • svn-*分支与上游svn的分支为一对一的对应关系.
  • git-*-*应该是由本地的svn-*再建立出来.

来看几个例子会比较清楚:

  • svn-BRANCH :这是一个对应到上游svn的BRANCH分支的分支.
  • git-GreatIdea-trunk :这是一个从本地git trunk分支建立出来,名叫GreatIdea的分支.它对应到的svn分支为trunk .还记得master永远对应到trunk ?
  • git-Refactor-BRANCH :这是从本地的svn-BRANCH分支建立出来,名叫Refactor的分支.

所以git 分支的流程大概是:

git checkout svn-BRANCH # 假设我们目前在svn-BRANCH
git checkout -b git-Refactor-BRANCH # 依普拉拉命名法建立git 分支
# 编辑程式
git diff # 看看改了什么
git add -i # 挑要commit 的档案
git commit # commit 回*本地* 的git repo
# 重复编辑程式到commit 之间的动作
git checkout svn-BRANCH # 准备merge 回svn-BRANCH
git merge git-Refactor-BRANCH # merge
git log --name-status # 检查merge 是否有问题
git branch -D git-Refactor-BRANCH # 移除刚刚建立的分支
# 接下来同"svn repo 的分支"

git merge可能会产生冲突.解决之后,记得要使用git add , git commit手动commit进去.

使用git branch -D强制移除分支,是因为前一步的git merge只会merge commit,但是还不会标记merge point.此时移除分支,会有"分支未merge"的警告出现.事实上我们也不要git标记merge point,否则送回svn repo会变成一个什么更动都没有的commit,反而更困扰.


git-svn简单使用教程,

svn是集中式的版本控制系统, 而git是分布式的版本控制系统...所以最好的办法不是谁替代谁...而是他们的协同工作.
你可以用git svn(注意有的版本并没有git-svn).

1. 建立本地目录, 比如假定是myproject, 那么就是
$mkdir myproject
$cd myproject

2. 初始化并获取某个版本
$git svn init http://xxxx       <= svn的仓库路径
$git svn fetch -r xxxxx        <= 获取某个版本(-r和xxxxx中间有空格哦, 假如不指定版本,它就会根据svn记录一级一级获取了哦)

(update Thu Nov 26 09:45:36 CST 2009)
以上两步也可以并成一步, 就是

$git svn clone http://xxxx myproject

(update on 2012-03-20: 如果远程的svn repo比较标准, 那么可以使用 $git svn clone http://xxxx/svn xxxx --username xxx@xxx -s  类google code例子)

然后就是通常的本地操作了...另外,


git svn rebase可以更新本地文件(类似svn update)
git status == svn status
每次操作,git都会给出相应的提示
git config --global core.whitespace -trailing-space (git智能化到一定程度了,假如你的编程习惯不好,它也会给出警告, 比如这个程序行结尾部分有空格. 你可以设置去掉警告)

最后假如你要提交,那么就使用
git svn dcommit

kdebase这篇比较不错:http://techbase.kde.org/Development/Tutorials/Git/git-svn

 

update on 2012-03-20:

若要保持一个空的文件夹, 那么需要在该文件夹下放一个.gitignore, 内容如下:(from: http://stackoverflow.com/questions/115983/how-do-i-add-an-empty-directory-to-a-git-repository)

# Ignore everything in this directory

*

# Except this file

!.gitignore

update on 2011-07-21:

要忽略某些文件, 需要首先:

git config --global core.excludesfile ~/.gitignore

然后编辑 vi ~/.gitignore. 

例如: 需要忽略vim的临时文件,就写:

.*.swp

你可能感兴趣的:(SVN,git,merge,branch,版本控制系统)