GIT版本控制系统(二)

以下内容为转载:

用Git 就是要爱用Branch 啊,Branch 很好用,开Branch 不用钱。开Branch 的情境除了在上一篇中提到因应产品release 需求的stable/production branch 之外,其他开branch 情况有:

  • 带有实验性质的变更,例如想改写新的演算法、重构程式码等
  • 新功能feature 开发
  • Bug fixes,你可能需要做些实验才知道到底怎么修

这些事情都可以先在本地开local branch 做,而不需要立即Push 分享给别人。


git branch <new_branch_name>建立本地local branch 
git branch -m <old_name> <new_name>改名字(如果有同名会失败,改用-M可以强制覆盖) 
git branch列出目前有那些branch以及目前在那个branch 
git checkout <branch_name>切换branch (注意到如果你有档案修改了却还没commit,会不能切换branch,解法稍后会谈) 
git checkout -b <new_branch_name> (<from_branch_name>)本地建立branch并立即checkout切换过去
git branch -d <branch_name>删除local branch

开Branch 最大的好处除了可以不影响stable 和其他分支版本的开发,另一个超棒的地方是”你可以决定Merge 的方式”。Git 的Merge 方式可以分成四种:

  • Straight merge 预设的合并模式,会有全部的被合并的branch commits 记录加上一个merge-commit,看线图会有两条Parents 线,并保留所有commit log。
  • Squashed commit 压缩成只有一个merge-commit,不会有被合并的log。SVN 的merge 即是如此。
  • cherry-pick 只合并指定的commit
  • rebase 变更branch 的分支点:将目前branch 的commits,一个个重新重新apply (或叫做patch) 到要被rebase 的branch 上。例如在A branch rebase B branch,就会把原本分支点之后所有的A commits 记录,重新在B branch 上再commit 一遍,这时新分支点变成B branch 的最新的commit。这方式仅适合还没分享给别人的local branch,因为等于砍掉重练目前branch 的commits 记录。

其中rebase比较难理解在下一篇有图解。


git merge <branch_name>合并另一个branch,若没有conflict冲突会直接commit。若需要解决冲突则会再多一个commit。
git merge --squash <branch_name>将另一个branch的commit合并为一笔,特别适合需要做实验的fixes bug或new feature,最后只留结果。合并完不会帮你先commit。
git cherry-pick 321d76f只合并特定其中一个commit。如果要合并多个,可以加上-n指令就不会先帮你commit,这样可以多pick几个要合并的commit,最后再git commit即可。

使用merge 可能会有部分程式码会conflict 冲突:简单的情况只要编辑档案处理<<<< ===== >>>>> 即可,然后重新add 到staging area 并commit (没有像SVN 的resolve指令)。复杂一点的可以再用git mergetool 选档案合并的GUI 工具(OS X 下面可以用opendiff, linux 可以用kdiff3 ),处理好后git commit。

一旦merge 好了,git branch -d <branch_name> 可以删除branch。但如果要删除的branch 还没有合并,就会有错误讯息。如果真的要强制删除可以用-D

Git的working tree是从SVN换过来一个不习惯的地方,因为它只是一个工作暂存区,在切换Branch时就会整个换掉。也因为如此,如果有档案有修改还没有commit出去,切换branch时就会出现error不能切换 (除非是新的untracking档案),例如有修改还没add会出现error: Entry 'ooxx' not uptodate. Cannot merge.有修改且已经add(还没ci)会出现error: Entry 'ooxx' would be overwritten by merge. Cannot merge.

最理想的处理当然是事情刚好做到一个段落,把东西commit 出去才切换branch 做事。不过事情总有临时,如果要换branch 的暂时的解决方式是使用git stash 会先把修改暂存下来,要回复则执行git stash pop。下一篇等你学会git reset 之后,你会发现就算把还没完成的东西commit 也不会怎么样,只要还没push 出去一切commit 纪录都是可以改的。

REMOTE REPO. 操作

首先要认识的是Protocol,像在Github 上面看自己的Project,会有分Public Clone URL 跟Your Clone URL,这有什么差?

  • git://github.com/ihower/project.git 这种的是使用Git 自己的prototol,优点是速度快,但是没有认证机制,只适合read only (port:9418)
  • git@github.com/ihower/project.git 这种的是使用SSH,可以有认证(SSH key)
  • Git 也可以透过HTTPS 的方式,不过速度较慢,比较适合对firewall 有限制的情况

其中Github 就是同时用SSH + Git protocol,兼顾认证需求及速度。


git clone <remote_address> 
git checkout --track -b foobar origin/foobar将远端的branch checkout回来并建立一个新的local branch,加上--track表示你之后还要pull、push回去,所以请Git记住对应关系。
git pull (<local_branch_name> origin/<remote_branch_name>)去远端fetch新版并merge进local branch 
git push将local branch的commit纪录更新到远端

git pull 要注意的是,如果别人在你上次pull 之后有push 新东西上去(也就是说跟你的branch 产生分岔了),此时有两种情况: 一是Git 可以顺利auto merge 的话, git 会自动多一次merge commit,这也就为什么常常log 会跑出Merge branch 'master' of git@foobar.com。二是如果有conflict,这时候就需要你手动处理然后commit。话说如果觉得这种local branch 和remote branch 的merge commit log 很烦,建议可以改使用git pull –rebase 指令来变成fast-forward 形式(就会变得像svn up,而不会有merge commit log)。rebase 的意思可能要下一篇才会详细说明的清楚,简单的说(?),就是先砍掉local branch 分岔点之后自己的commit,然后把远端的commit 先一个个apply 进来,最后再把自己的commit 再apply 进去(如果有conflict 会中途停下来,等你修好才会继续apply),如此一来看线图就会变成一条线而已,也就没有所谓merge 这个动作了。

git push 预设的远端是origin,并且会将所有有和remote 有对应的local branch 都push 上去。如果要把新的local branch push 上去,需要下git push origin <local_vranch_name> 指令。

git push 也可能会失败,例如出现! [rejected] master -> master (non-fast forward),这个non-fast forward 的意思是你的parent commit 和远端的不相同,也就是线图有分岔,需要先pull 回来处理好merge 才能push 上去。

fast-forward 在Git 是一种merge 术语,当B branch (例如一个local branch) 是从A branch (例如一个remote branch) 的最新版(HEAD)分支出来的,那当A 要把B merge 进来时,因为B 的parent commit 是A 的HEAD,所以这两个branch 唯一的差异就是B 后来的commit 而已,而不会有任何conflict。所以实际上的动作只要把A 的HEAD 改成B 的HEAD 就好了,线图上这两个branch 根本是同一条线,此谓fast-forward。

其他操作还有:


git fetch把远端的branch更新下载回来,但不会merge到local branch 
git branch -r显示local有追踪的远端branch。注意到你不能直接修改这个remote branch,一定要用一个local branch对应它。
git remote show origin显示远端server的branch 
git remote add foobar git://可以新增别的repo.位置,于是pull的时候就可以指定要从哪一个远端更新回来。
git push origin :foobar删除远端的branch

因为远端的操作指令比较杂,所以也有人写了git_remote_branch来简化操作。

原文地址:https://www.cnblogs.com/yangtze736-2013-3-6/p/3357551.html