Git3:Git分支
一、概念
分支就像漫威漫画宇宙里的平行宇宙。在一个宇宙中,美国队长是正义的化身,是复仇者的领导者。而在另一个宇宙中,美队成了九头蛇。
两个平行宇宙互不干扰,那么也没啥影响。不过在某个时间点,两个宇宙交叉了,于是就出现了死侍大战死侍。
而每一个平行宇宙就相当于一个分支。平行宇宙会在某个时间点出现交叉,而分支也会在某个时间点被合并。
一个团队中有多个人在开发一下项目,某个同事在开发一个新的功能,需要一周时间完成,他写了其中的30%还没有写完,如果他提交了这个版本,那么团队中的其他人就不能继续开发了。但是等到他全部写完再全部提交,大家又看不到他的开发进度,也不能继续干活,这如何是好呢?
对于上面的这个问题,我们就可以用分支管理的办法来解决,一同事开发新功能他可以创建一个属于他自己的分支,其它同事暂时看不到,继续在开发分支(一般都有多个分支)上干活,他在自己的分支上干活,等他全部开发完成,再一次性的合并到开发分支上,这样我们既可知道他的开发进度,又不影响大家干活。
分支本质上其实就是一个指向某次提交的可变指针。Git 的默认分支名字为 master 。而我们是怎么知道当前处于哪个分支当中呢?答案就是在于 HEAD 这个十分特殊的指针,它专门用于指向于本地分支中的当前分支
二、创建与合并分支
1、创建分支原理分析
之前我们每次的版本提交,Git都把它们串成一条时间线,这条时间线就是一个分支。默认情况下,Git只有一个分支,即主分支,也叫master分支。HEAD严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是当前分支。
一开始的时候,master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点:
每次提交,master分支都会向前移动一步,随着不断提交,master分支的线也越来越长。
当我们创建新的分支,例如dev时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上:
Git创建一个分支很快,因为除了增加一个dev指针,改改HEAD的指向,工作区的文件都没有任何变化!不过,从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变:
假如我们在dev上的工作完成了,就可以把dev合并到master上。最简单的方法,就是直接把master指向dev的当前提交,就完成了合并:
所以Git合并分支也很快!就改改指针,工作区内容也不变!
合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,我们就剩下了一条master分支:
2、创建分支语法
创建一个分支并切换到新分支上:
# 创建一个dev分支,并切换到dev分支上,-b参数的意思是创建并切换分支
git checkout -b dev
# 相当于执行了如下两个命令:
git branch dev
git checkout dev
查看当前位于哪个分支:
git branch
查看当前仓库的所有分支:
git branch -a
切换分支:
#切换到master分支
git branch master
合并分支:
git checkout master
git merge dev #当dev分支合并到master分支上
删除分支:
git branch -d dev
#当一个分支还未被合并时,使用-d选项无法删除,需要使用-D选项强行删除
git branch -D dev
三、解决冲突
创建一个新的dev分支:
yanwei@ubuntu:~/git_test$ git checkout -b dev
切换到一个新分支 'dev'
yanwei@ubuntu:~/git_test$ git branch
* dev
master
修改code.txt内容并提交:
# 修改后的code.txt内容如下:
yanwei@ubuntu:~/git_test$ cat code.txt
this is the first line
this is the second line
this is the third line
this is the forth line
this is dev branch
yanwei@ubuntu:~/git_test$ git add code.txt
yanwei@ubuntu:~/git_test$ git commit -m "dev first commit"
[dev 187dee6] dev first commit
1 file changed, 1 insertion(+)
再切换回master分支,修改code.txt内容并提交:
# 切回master分支
yanwei@ubuntu:~/git_test$ git checkout master
切换到分支 'master'
# 修改内容并提交:
yanwei@ubuntu:~/git_test$ cat code.txt
this is the first line
this is the second line
this is the third line
this is the forth line
this is the master branch
yanwei@ubuntu:~/git_test$ git add code.txt
yanwei@ubuntu:~/git_test$ git commit -m "master new commit"
[master 2015000] master new commit
1 file changed, 1 insertion(+)
现在master分支和dev分支就各自有了新的提交 ,变成了这样:
这种情况下,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突:
yanwei@ubuntu:~/git_test$ git merge dev
自动合并 code.txt
冲突(内容):合并冲突于 code.txt
自动合并失败,修正冲突然后提交修正的结果。
git告诉我们code.txt这个文件存在冲突,必须手动解决冲突后再提交。git status
也可以告诉我们冲突的文件:
yanwei@ubuntu:~/git_test$ git status
位于分支 master
您有尚未合并的路径。
(解决冲突并运行 "git commit")
(使用 "git merge --abort" 终止合并)
未合并的路径:
(使用 "git add <文件>..." 标记解决方案)
双方修改: code.txt
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
我们查看code.txt的内容如下:
yanwei@ubuntu:~/git_test$ cat code.txt
this is the first line
this is the second line
this is the third line
this is the forth line
<<<<<<< HEAD
this is the master branch
=======
this is dev branch
>>>>>>> dev
Git用<<<<<<<,=======,>>>>>>>
标记出不同分支的内容,我们修改如下后保存如下:
yanwei@ubuntu:~/git_test$ cat code.txt
this is the first line
this is the second line
this is the third line
this is the forth line
this is the master branch
this is dev branch
再次提交:
yanwei@ubuntu:~/git_test$ git add code.txt
yanwei@ubuntu:~/git_test$ git commit -m "解决冲突"
[master 6c1828d] 解决冲突
这个时候,master分支和dev分支就变成了如下图所示:
我们也可以通过如下指令查看到分支合并情况:
yanwei@ubuntu:~/git_test$ git log --graph --oneline
* 6c1828d (HEAD -> master) 解决冲突
|\
| * 187dee6 (dev) dev first commit
* | 2015000 master new commit
|/
* 0a96a0f forth commit
* e4fb2aa third commit
* 227ecaa second commit
* d66bdc0 first commit
最后删除dev分支:
yanwei@ubuntu:~/git_test$ git branch -d dev
已删除分支 dev(曾为 187dee6)。
四、分支管理策略
1、保留分支历史
通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
下面通过一个例子来说明--no-ff
方式的git merge:
再次创建并切换到dev分支
yanwei@ubuntu:~/git_test$ git checkout -b dev
切换到一个新分支 'dev'
修改code.txt内容并提交版本:
yanwei@ubuntu:~/git_test$ cat code.txt
this is the first line
this is the second line
this is the third line
this is the forth line
this is the master branch
this is dev branch
this is dev branch new line
yanwei@ubuntu:~/git_test$ git add code.txt
yanwei@ubuntu:~/git_test$ git commit -m 'dev branch another commit'
[dev b89266d] dev branch another commit
1 file changed, 1 insertion(+)
切换回master分支,然后通过--no-ff
选项合并分支:
yanwei@ubuntu:~/git_test$ git checkout master
切换到分支 'master'
yanwei@ubuntu:~/git_test$ git merge --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
code.txt | 1 +
1 file changed, 1 insertion(+)
最后查看分支历史:
yanwei@ubuntu:~/git_test$ git log --graph --oneline
* ea9a7d5 (HEAD -> master) merge with no-ff
|\
| * b89266d (dev) dev branch another commit
|/
* 6c1828d 解决冲突
|\
| * 187dee6 dev first commit
* | 2015000 master new commit
|/
* 0a96a0f forth commit
* e4fb2aa third commit
* 227ecaa second commit
* d66bdc0 first commit
可以看到,不使用Fast forward模式,merge后就像这样:
2、分支管理原则
在实际开发中,针对分支的管理,我们应该遵循以下几个基本原则:
- master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
- 干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;
- 你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。
所以,团队合作的分支看起来就像这样:
五、 bug分支
软件开发中,bug就像家常便饭一样。有了bug就需要修复,在Git中,由于分支是如此的强大,所以,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。当你接到一个修复一个代号101的bug的任务时,很自然地,你想创建一个分支issue-101来修复它,但是,当前正在dev上进行的工作还没有提交:
yanwei@ubuntu:~/git_test$ git status
位于分支 master
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git checkout -- <文件>..." 丢弃工作区的改动)
修改: code.txt
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
并不是你不想提交,而是工作只进行到一半,还没法提交,预计完成还需1天时间。但是,必须在两个小时内修复该bug,怎么办?幸好,Git还提供了一个stash功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:
yanwei@ubuntu:~/git_test$ git stash
保存工作目录和索引状态 WIP on master: ea9a7d5 merge with no-ff
现在,用git status查看工作区,就是干净的(除非有没有被Git管理的文件),这时候,就可以放心的创建bug分支来修复bug。
首先确定要在哪个分支上修复bug,假定需要在master分支上修复,就从master创建临时分支:
yanwei@ubuntu:~/git_test$ git checkout master
切换到分支 'master'
yanwei@ubuntu:~/git_test$ git checkout -b issue-101
切换到一个新分支 'issue-101'
然后在issue-101分支上进行bug修复并提交,最后合并到master,最后删除bug分支:
# 在bug分支创建一个readme.txt
yanwei@ubuntu:~/git_test$ touch readme.txt
#提交该文件
yanwei@ubuntu:~/git_test$ git add readme.txt
yanwei@ubuntu:~/git_test$ git commit -m "修复bug"
[issue-101 67de5f6] 修复bug
1 file changed, 1 insertion(+)
create mode 100644 readme.txt
# 切换到master合并bug分支
yanwei@ubuntu:~/git_test$ git checkout master
切换到分支 'master'
yanwei@ubuntu:~/git_test$ git merge --no-ff -m "合并bug分支" issue-101
Merge made by the 'recursive' strategy.
readme.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 readme.txt
#删除bug分支
yanwei@ubuntu:~/git_test$ git branch -d issue-101
已删除分支 issue-101(曾为 67de5f6)。
bug修复完成以后,现在要回到dev分支干活:
yanwei@ubuntu:~/git_test$ git checkout dev
切换到分支 'dev'
工作区是干净的,刚才的工作现场存到哪去了?用git stash list
命令看看:
yanwei@ubuntu:~/git_test$ git stash list
stash@{0}: WIP on dev: b89266d dev branch another commit
工作现场还在,Git把stash内容存在某个地方了,但是需要恢复一下,有两个办法:
- 用
git stash apply
恢复,但是恢复后,stash内容并不删除,你需要用git stash drop
来删除; - 用
git stash pop
,恢复的同时把stash内容也删了
yanwei@ubuntu:~/git_test$ git stash pop
位于分支 dev
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git checkout -- <文件>..." 丢弃工作区的改动)
修改: code.txt
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
丢弃了 refs/stash@{0} (971af18ec9510e4175dc7e7f4e11417731040231)
还可以多次git stash,然后通过git stash list查看stash列表,如果要恢复指定的stash,可以使用如下方法:
# 查看stash列表
yanwei@ubuntu:~/git_test$ git stash list
stash@{0}: WIP on master: d69c612 合并bug分支
# 恢复指定的stash
yanwei@ubuntu:~/git_test$ git stash apply stash@{0}
位于分支 master
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git checkout -- <文件>..." 丢弃工作区的改动)
修改: code.txt
未跟踪的文件:
(使用 "git add <文件>..." 以包含要提交的内容)
new.txt
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
六、推送和拉取远程分支
推送分支,就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,这样,Git就会把该分支推送到远程库对应的远程分支上:
$ git push origin master
如果要推送其他分支,比如dev,就改成:
$ git push origin dev
但是,并不是一定要把本地分支往远程推送,需要推送的远程分支说明:
- master分支是主分支,因此要时刻与远程同步;
- dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;
- bug分支只用于在本地修复bug,就没必要推到远程
- feature分支是否推到远程,取决于你是否和同事在上面协作开发。
多人协作时,大家都会往master和dev分支上推送各自的修改。现在,假如你的同事在另一台电脑上克隆远程仓库:
$ git clone git@github.com:michaelliao/learngit.git
当你的同事从远程库clone时,默认情况下,看到本地的master分支。现在,要在dev分支上开发,就必须创建远程origin的dev分支到本地,于是他用这个命令创建本地dev分支:
$ git checkout -b dev origin/dev
现在,他就可以在dev上继续修改,然后,时不时地把dev分支push到远程:
$ git commit -m "add /usr/bin/env"
你的同事已经向origin/dev分支推送了他的提交,而碰巧你也对同样的文件作了修改,并试图推送:
# 提交修改
$ git add hello.py
$git commit -m "add coding: utf-8"
[dev bd6ae48] add coding: utf-81 file changed, 1 insertion(+)
# 推送远程dev分支
$ git push origin dev
To git@github.com:michaelliao/learngit.git
! [rejected] dev -> dev (non-fast-forward)
error: failed to push some refs to'git@github.com:michaelliao/learngit.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards'in'git push --help'for details.
推送失败,因为你的同事的最新提交和你试图推送的提交有冲突,解决办法也很简单,Git已经提示我们,先用git pull把最新的提交从origin/dev抓下来,然后,在本地合并,解决冲突,再推送:
$ git pull
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0)
Unpacking objects: 100% (3/3), done.
From github.com:michaelliao/learngit
fc38031..291bea8 dev -> origin/dev
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details
git pull <remote> <branch>
If you wish to set tracking information for this branch you can do so with:
git branch --set-upstream dev origin/<branch>
git pull也失败了,原因是没有指定本地dev分支与远程origin/dev分支的链接,根据提示,设置dev和origin/dev的链接:
$ git branch --set-upstream dev origin/dev
Branch dev set up to track remote branch dev from origin.
再pull:
$ git pull
Auto-merging hello.py
CONFLICT (content):Merge conflict in hello.py
Automatic merge failed; fix conflicts andthen commit the result.
这回git pull成功,但是合并有冲突,需要手动解决,解决的方法和分支管理中的解决冲突完全一样。解决后,提交,再push:
$ git commit -m "merge & fix hello.py"
[dev adca45d] merge & fix hello.py
$ git push origin dev
Counting objects: 10, done.
Delta compression using up to4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (6/6), 747 bytes, done.
Total 6 (delta 0), reused 0 (delta 0)
To git@github.com:michaelliao/learngit.git
291bea8..adca45d dev -> dev
因此,多人协作的工作模式通常是这样:
- 首先,可以试图用git push origin branch-name推送自己的修改;
- 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;
- 如果合并有冲突,则解决冲突,并在本地提交;
- 没有冲突或者解决掉冲突后,再用
git push origin branch-name
推送就能成功! - 如果
git pull
提示no tracking information
,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream branch-name origin/branch-name
。
Git3:Git分支的更多相关文章
- Git 分支
Git 保存的不是文件的变化或者差异,而是一系列不同时刻的文件快照,某一次的提交指向这处时刻的文件快照,看起来就像每次提交都保存了当时的文件,连续的提交形成一条长链 分支 指向某一个特定的提交,不同的 ...
- Git分支管理
一.Git分支的使用 查看分支: git branch 创建分支: git branch branch1 切换到branch1 git checkout branch1 再用git branch查看, ...
- Git分支的前世今生
摘自Jack__CJ CSDN博客,写得很好,保存一下. 导读 几乎所有的版本控制系统都以某种形式支持分支. 使用分支意味着你可以把你的工作从开发主线上分离开来,以免影响开发主线. 在很多版本控制系 ...
- GIT分支管理模型
GIT分支管理模型 link: git-branching-model 主分支(Main branches) 项目两个常驻分支: master 主干分支(锁定),仅用于发布新版本,平时不能在上面干活, ...
- Git详解之三 Git分支
相关文档 — 更多 Git 基础培训.ppt GIT 使用经验.ppt GIT 介绍.pptx GIT 分支管理是一门艺术.docx Eclipse上GIT插件EGIT使用手册.docx git/gi ...
- git初体验(三)git分支
分支的理念就是分身,就像孙悟空拔出猴毛变出很多跟自己一模一样的猴子,然后每个猴子做自己的事情互不干涉,等到所有猴子做完之后,猴子集合来合并劳动成果,然后悟空就把那些猴子猴孙门统统收回了. 你创建了一个 ...
- 如何在终端实时展现git分支
在微博上看到ghosTM55在终端可以实时展现出当前运行的分支,觉得很奇特,于是google了一把.这里面存在两个内容,第一个bash,第二个是git bash基础: 了解到linux的shell存在 ...
- php 通过exec 创建git分支失败
今天给我们自己的发布系统增加一个新建分支的功能,操作比较简单,但是使用php执行shell命令的时候总是无法push分支到远程,但是登陆服务器执行却是可以的 新建分支命令如下 git fetch -- ...
- 开发与测试整体过程中的Git分支merge流程
开发与测试整体过程中的Git分支merge流程 Git分支merge之开发流程 首先在Gitlab上有个仓库存储着原始的项目代码,其中包含一个叫master的分支.然后可能按功能进行分配,由不同的开发 ...
- Git 分支管理是一门艺术
转载: Git 分支管理是一门艺术 1 要确保:团队成员从主分支(master)获得的都是处于可发布状态的代码,而从开发分支(develop)应该总能够获得最新开发进展的代码. 2 "辅助分 ...
随机推荐
- Linux下oracle启动/关闭监听(bash:lsnrctl:command not found)
打开终端 切换帐户 # su - Oracle 启动监听 $ lsnrctl start 关闭监听 $ lsnrctl stop 切换帐户一定要加 "-" 否则会出现: bas ...
- 2017年第八届蓝桥杯【C++省赛B组】
1.标题: 购物单 小明刚刚找到工作,老板人很好,只是老板夫人很爱购物.老板忙的时候经常让小明帮忙到商场代为购物.小明很厌烦,但又不好推辞. 这不,XX大促销又来了!老板夫人开出了长长的购物单,都是有 ...
- BZOJ 3132(上帝造题的七分钟-树状数组求和+2D逆求和数组)
3132: 上帝造题的七分钟 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 46 Solved: 18[Submit][Status][Discus ...
- 关于javascript异步编程的理解
在开发手机app的时候,要使用ajax想向后台发送数据.然后遇到了一个bug,通过这个bug,理解了ajax异步请求的工作原理.下面是登录页面的源代码. <!DOCTYPE html> & ...
- ADT图及图的实现及图的应用
图: 图中涉及的定义: 有向图: 顶点之间的相关连接具有方向性: 无向图: 顶点之间相关连接没有方向性: 完全图: 若G是无向图,则顶点数n和边数e满足:0<=e<=n(n-1)/2,当e ...
- windows下的C++ socket服务器(1)
windows下的一个C++ socket服务器,用到了C++11的相关内容,现在还不是很完善,以后会不断改进的! #include <winsock2.h>//1 以后会用这种方式对特定 ...
- n位格雷曼实现
参考: 格雷码的实现 问题:产生n位元的所有格雷码. 格雷码(Gray Code)是一个数列集合,每个数使用二进位来表示,假设使用n位元来表示每个数字,任两个数之间只有一个位元值不同. 例如以下为 ...
- 结对编程:四则运算。组员:闫浩楠 杨钰宁 开发语言:C语言
需求分析:1.能够自动出题并给出答案 2.包含“+,—,*,/,()” 的四则运算. 3.显示题目的答案 结构设计:1.自动出题用随机数生成语句实现:包括随机生成数字.运算符号和题目长度 2.用变量约 ...
- 关于.net服务启动注册到zookeeper,但是注册节点20分钟自动消失解决办法
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,作用简单描述就是相当于一个中介,服务提供者将服务注册到zk,服务调用者直接从zk获取,zk的作用就是协调 最近碰到公 ...
- pygame学习笔记(6)——一个超级简单的游戏
转载请注明:@小五义 http://www.cnblogs.com/xiaowuyi 学了这么长时间的Pygame,一直想写个游戏实战一下.看起来很简单的游戏,写其来怎么这么难.最初想写个俄罗斯方块 ...