认识分支

Git中分支管理是Git的一大特色,由于在实际项目开发中的也确定性以及需求变更的复杂性,所有的开发都集中在一条分支上开发势必造成开发与维护成本的提高。以软件项目开发为例,由于开发会有多个开发周期,发布多个软件版本,每个已发布的版本又可能出现Bug,以及适应时代的变化,不断升级软件,推出新功能。这些都是开发中可能出现的情况,试想这些开发都在一个分支(目前是master)上进行开发,那么会严重影响软件的开发进度,这样算下来的时间成本是非常之高的。现在流行的敏捷开发方式就需要完善的分支管理方案。

使用分支将某个功能或者Bug的修复独立出来,这样就实现了与主开发分支的隔离,不同开发组的开发工作也不会发生冲突,大大提高了开发效率。

分支操作

分支命令

在Git中使用非分支操作命令是git branch。该命令的用法有如下几种:

//查看分支
git branch
//创建分支
git branch <branchname>
//从某个起始点创建分支
git branch <branchname> <start-point>
//删除分支
git branch -d <branchname>
//强制删除分支
git branch -D <branchname>
//重命名分支
git branch -m <oldbranch> <newbranch>
//强制重命名分支
git branch -M <oldbranch> <newbranch>

分支操作实例

继续上一篇文章的操作,查看之前的提交日志:

项目初始化

//配置命令别名
git config --global alias.lol "log --oneline --decorate --graph --all"
//之后就可以使用git lol查看日志了
git lol

输出如下:

*   e4e0025 (HEAD -> master, origin/master) Merge tag 'mytag3' of file:///home/rhwayfun/java/notes/repos2/share
|\
| * 1ed8141 (tag: mytag3) blank commit for GnuPG-signed tag test.
|/
* 6d095e6 blank commit for annotated tag test.
* abb48d5 (tag: mytag) blank commit.
*   1e5c656 Merge README.txt: Hello, user2 and user1.
|\
| * 04eed97 create README.txt
* | b14d8be create README.txt
|/
*   0432ca1 Merge branch 'master' of file:///home/rhwayfun/java/notes/repos2/share
|\
| * 5f642a9 create team/user2.txt
* 14cc834 create team/user1.txt

现在开始在这个基础上执行分支操作,为了与之前的提交区别开,开始先创建一个空提交,然后在该提交创建带说明的里程碑(上一篇有讲解哦),执行操作如下:

//创建一个空白提交
git commit --allow-empty -m "start git branch learning."
//创建里程碑v1.0
git tag -m "Release 1.0" v1.0
//将里程碑推送到共享仓库
git push origin v1.0

user1完成speak功能

现在已经创建了里程碑并推送到了远程分支,接下来就开始创建分支,并在分支上进行开发了。

首先需要创建分支:

//切换到user1的工程目录
cd user1/project
//创建user1/dev分支
git branch user1/dev
//切换到user1/dev分支
git checkout user1/dev

查看创建的分支:

master
* user1/dev

这样就把分支切换到user1/dev分支上了,在user1/dev分支需要实现人说话和走路的功能,说话的功能由user1实现,走路的功能由user2实现。执行如下操作:

//创建PersonService接口
touch src/main/java/PersonService.java
//编辑PersonService.java
vim src/main/java/PersonService.java

在接口中定义了两个方法:说话和走路的方法。

package com.rhwayfun.hello.service;public interface PersonService{//speak--->assign to user1 to finish.void speak();//walk--->assign to user2 to finish.void walk();
}

编译PersonService.java文件:

javac -d /home/rhwayfun/java/notes/to2/user1/project/src/main/java/com/rhwayfun/hello/service src/main/java/com/rhwayfun/hello/service/PersonService.java

创建PersonServiceImpl.java文件,然后由user1实现speak方法,如下:

package com.rhwayfun.hello.service.impl;import com.rhwayfun.hello.service.PersonService;public class PersonServiceImpl implements PersonService {//finish by user1.public void speak(){System.out.println("I can speak English.");}//finish by user2.public void walk(){// to do by user2}
}

现在user1已经完成了speak功能的开发,然后执行如下操作将修改推送到共享版本库:

//将工作区所有的修改都添加到暂存区
git add .
//提交到本地版本库
git commit -m "user1 finish speak function."
//切换到master主分支
git checkout master
//合并user1/dev开发分支
git merge user1/dev

这样在本地版本库就有了最新的提交,在正式推送到共享仓库之前,可以简单分析以上命令的执行过程,分支实际上是在目录.git/refs/heads下的引用,版本库初始时创建的master分支就在该目录下,为了验证这一点,可以执行如下命令:

ls -F .git/refs/heads

输出结果为:

master
user1/

ls -F .git/refs/user1

输出结果为:

dev

创建非分支user1/dev是基于头指针HEAD创建的,因此当前分支和master分支的指向是一致的。

现在可以将user1的成果推送到共享版本库了,只需要执行git push就可以完成。接下来的走路的功能需要user2来完成,因此首先切换到user2的工作区:

cd user2/project
//拉取最新的提交
git pull

user2完成walk功能

现在user2已经拥有了最新的功能,为了不影响master主分支的开发,需要将walk功能的开发切换到一个分支上进行:

//创建user2/dev分支并切换到该分支上
git checkout -b user2/dev

在PersonServiceImpl.java文件中完成walk方法的开发,代码如下:

...//finish by user2.public void walk(){// to do by user2System.out.println("Nice, I can walk.");}
...

这样user2就完成了walk功能的开发,执行如下操作将user2完成的功能推送到共享版本库:

git add .
git commit -m "user2 finish walk function."
git checkout master
git merge user2/dev
git tag -m "Release 1.1" v1.1
git push
//推送里程碑v1.1到远程仓库
git push origin v1.1

要注意的是里程碑必须额外手动推送到远程仓库,不然直接执行git push只会将master分支的修改推送到远程仓库。最后我们可以看看user1和user2完成PersonService接口开发的提交日志情况:

* 4fc2175 (HEAD -> master, tag: v1.1, origin/master) user2 finish walk function.
* f62c2cf user1 finish speak function.
* 8f126fc (tag: v1.0) start git branch leanring.
*   e4e0025 Merge tag 'mytag3' of file:///home/rhwayfun/java/notes/repos2/share
|\
| * 1ed8141 (tag: mytag3) blank commit for GnuPG-signed tag test.
|/
* 6d095e6 blank commit for annotated tag test.
* abb48d5 (tag: mytag2, tag: mytag) blank commit.
*   1e5c656 Merge README.txt: Hello, user2 and user1.
|\
| * 04eed97 create README.txt
* | b14d8be create README.txt
|/
*   0432ca1 Merge branch 'master' of file:///home/rhwayfun/java/notes/repos2/share
|\
| * 5f642a9 create team/user2.txt
* 14cc834 create team/user1.txt

Bug分支协同操作

现在user1和user2已经通过协作共同完成PersonService接口的开发,嗯,非常好,这个过程通过使用Git 分支操作很顺利地完成了功能的开发。现在因为需求变更,需要修改speak和walk功能的实现,因为两个功能是由两个开发者user1和user2协同完成的,所以需要创建一个bug分支,修复代码并提交。

在user1的工作区下执行如下命令:

//创建协同工作分支
git checkout -b hello-1.x v1.1
//查看两者的对应的提交
git rev-parse hello-1.x v1.1^{} master

修改PersonServiceImpl.java代码如下:

...//finish by user1.public void speak(){System.out.println("I can speak English."); }
...

这样user1就完成了speak功能的bug修复,于是执行如下操作:

git add -u
git commit -m "fix speak bug."
git checkout master
git merge hello-1.x
git push origin hello-1.x 

现在已经把需要协同处理的bug分支提交到共享仓库了,切换到user2的工作区目录下,执行如下命令:

//获取共享最新的更新,但不执行merge操作
git fetch
//显示远程仓库指向的引用
git ls-remote origin
//从上一个命令中可以发现有一个hello-1.x的bug分支
//从远程仓库origin/hello-1.x分支创建本地hello-1.x分支并切换到该分支
git checkout -b hello-1.x origin/hello-1.x

打开PersonServiceImpl.java文件,修改walk方法的代码如下:

...//finish by user2.public void walk(){// to do by user2System.out.println("Oh oh, I can only walk.");}
...

这样user2也完成了bug的修复,于是执行如下操作:

git add -u
git commit -m "fix walk bug."
git checkout master
git merge hello-1.x
git push

现在user2已经最后的bug修复修复结果合并到master分支了,为了保持同步,user1需要执行如下操作:

git pull
//因为user1的本地hello-1.x分支不是最新的
git checkout hello-1.x
//合并到master分支
git merge master

完成以上bug修复之后,显示的最新提交日志输出如下:

* 0b8844a (HEAD -> master, origin/master, hello-1.x) fix walk bug.
* 55889c6 (origin/hello-1.x) fix speak bug.
* 4fc2175 (tag: v1.1) user2 finish walk function.
* f62c2cf user1 finish speak function.
* 8f126fc (tag: v1.0) start git branch leanring.
*   e4e0025 Merge tag 'mytag3' of file:///home/rhwayfun/java/notes/repos2/share
|\
| * 1ed8141 (tag: mytag3) blank commit for GnuPG-signed tag test.
|/
* 6d095e6 blank commit for annotated tag test.
* abb48d5 (tag: mytag2, tag: mytag) blank commit.
*   1e5c656 Merge README.txt: Hello, user2 and user1.
|\
| * 04eed97 create README.txt
* | b14d8be create README.txt
|/
*   0432ca1 Merge branch 'master' of file:///home/rhwayfun/java/notes/repos2/share
|\
| * 5f642a9 create team/user2.txt
* 14cc834 create team/user1.txt

变基操作和拣选操作

变基操作使用的命令是git rebase
拣选操作使用的命令是git cherry-pick

先说说拣选操作,可以选择某一个分支中的一个或几个commit(s)来进行操作,使用的场景有两个:

1、把弄错分支的提交移动到正确的地方
2、把其他分支的提交添加到现在的分支

再说说变基操作,可以改写、替换、删除或合并提交。常在需要合并的场景下使用,而合并和变基的操作可以使用如下简图加以区别:

假设初始状态是这样的:

合并操作是这样的:

变基操作是这样的:

可以看到变基操作不会产生新的提交,降低了代码维护的成本,而且合并方式更加优雅。

下面通过实际操作演示变基操作:

在user2的工作区执行如下操作:

touch rebase.txt
vim rebase.txt

编辑rebase.txt文件如下:

This is rebase operatio by user2.

之后将所做的修改提交到本地仓库,然后推送到共享版本库,这个过程执行的一系列操作如下:

git add rebase.txt
git commit -m "create rebase.txt file"
git checkout master
touch rebase.txt
vim rebase.txt

在user2的master分支编辑rebase.txt文件如下:

It is a rebase operation by user2.

之后执行如下操作,在master分支上添加到本地仓库,这样做的目的是造成变基操作过程冲突:

git add .
git commit -m "create rebase.txt on master."
git checkout user2/dev
git rebase master

在执行变基操作git rebase时出现如下错误:

First, rewinding head to replay your work on top of it…
Applying: create rebase.txt file
Using index info to reconstruct a base tree…
Falling back to patching base and 3-way merge…
Auto-merging rebase.txt
CONFLICT (add/add): Merge conflict in rebase.txt
Failed to merge in the changes.
Patch failed at 0001 create rebase.txt file
The copy of the patch that failed is found in:
/home/rhwayfun/java/notes/to2/user2/project/.git/rebase-apply/patch
When you have resolved this problem, run “git rebase –continue”.
If you prefer to skip this patch, run “git rebase –skip” instead.
To check out the original branch and stop rebasing, run “git rebase –abort”.

意思就是变基操作出现了冲突,需要在工作区处理冲突后将处理冲突的文件添加到暂存区,然后继续变基操作。同时我们直到发生冲突的文件是rebase.txt文件,将工作区的rebase.txt文件修改如下:

This is a rebase operation by user2.

再执行如下操作,完成最终的变基操作:

git add -u
git rebase --continue
git checkout master
git merge user2/dev
//用本地的user2/dev分支直接更新远程版本库的master分支
git push origin user2/dev:master

这样就是完成了变基操作,而拣选操作就很好理解了,可以在不同的分支上将其他的commit拿过来并接到该分支之后,就完成了拣选操作。

小结

通过以上实例的演示,可以非常清楚地看到分支操作在协同开发中的重要性,分支操作大大节省了开发时间,所以好好掌握分支操作是很重要的。此外,演示中还将Git中比较难以理解的变基操作、合并操作以及拣选操作进行了说明和演示。变基操作相比合并操作是一种更优雅的合并方式,因为合并操作会产生第三个提交,在合并操作很多的时候,代码维护的难度就会上升。

Git学习8:Git分支操作相关推荐

  1. Git学习笔记:分支管理3

    前言 在补习python的时候主要参考的是廖雪峰的教程Python教程,在学习完后准备完成期末作业时,遇到了一个技术难题,需要初步掌握git,因此开始了git的学习. 本教程参考廖雪峰的Git教程 G ...

  2. Git学习笔记:分支管理(2)

    前言 在补习python的时候主要参考的是廖雪峰的教程Python教程,在学习完后准备完成期末作业时,遇到了一个技术难题,需要初步掌握git,因此开始了git的学习. 本教程参考廖雪峰的Git教程 G ...

  3. Git实战:branch分支操作详解

    什么是分支 分支是指在主干道上分支的支线,可以前往不同的地方,也可以到达相同的终点(只是实现的路线不同).Git指向团队开发中的个体,各开发者可以有自己的分支,开发时不会影响其他分支的开发进度.分支完 ...

  4. 超详细的Git学习记录(Git基础内容/IDEA集成Git/GitHub/Gitee/GitLab及Centos7部署GitLab)

    超详细的Git学习笔记 从B站搜到的尚硅谷视频学习了Git,记录了一下学习的内容,收获很大 学习地址: https://www.bilibili.com/video/BV1vy4y1s7k6?p=11 ...

  5. Git之常见的分支操作

    目录 分支管理 1.创建与合并分支 1.2switch 2.解决冲突 3.分支管理策略 4.分支策略 5.Bug分支 6.Feature分支 7.多人协作 8.推送分支 9.抓取分支 10.Rebas ...

  6. Git学习小记之分支原理

    介绍 如果想要熟练使用 Git,没有分支理念是绝对行不通的,在用 Git 管理项目的时候,经常需要使用 commit 这个命令,那么这个 commit 到底是指什么呢? 按照官方的解释,这应该成为一个 ...

  7. Git学习系列(五)分支管理详解

    分支管理在咱们实际工作中经常用到,因此掌握分支对于咱们从事移动开发并且使用到了Git这样的分布式版本管理工具来说是很有必要的.比如当你饿了的时候,甲帮你做饭,一帮你烧菜: 当然你也可以自己做饭烧菜.但 ...

  8. Git - 学习/实践 - 以及相关操作

    1.环境 win10 64位 专业版 默认, 已安装git.[git bash]所有操作均在git bash 中. 2.使用 1. 文档阅读/查阅 https://git-scm.com/docsht ...

  9. git常用命令,分支操作,子模块

    Git 是一个很强大的分布式版本管理工具,它不但适用于管理大型开源软件的源代码(如:linux kernel),管理私人的文档和源代码也有很多优势(如:wsi-lgame-pro) 二. Git 常用 ...

  10. git学习------gt;Git 分支管理最佳实践

    ps:本文转载于 : https://www.ibm.com/developerworks/cn/java/j-lo-git-mange/index.html Git 是目前最流行的源代码管理工具.大 ...

最新文章

  1. 一行CSS样式去除百度地图版权,去除百度地图右上角平移缩放控件的市县区文字
  2. python能实现excel什么功能_Python pandas对excel的操作实现示例
  3. 软件测试——性能测试、压力测试、负载测试等详解
  4. php保存gbk字符串,php判断字符串gbk/utf8编码和转换
  5. 深度学习与计算机视觉系列(3)_线性SVM与SoftMax分类器--在深度学习的视觉分类中的,这两个分类器的原理和比较
  6. innobackupex参数之 --throttle 限速这个值设置多少合理 原创
  7. ge linux安装apt_linux – 一个通用的bash脚本,用于安装apt-ge...
  8. margin负值的巧妙运用(HTML、CSS)
  9. 青岛农业大学计算机分数线,青岛农业大学录取分数线2021是多少分(附历年录取分数线)...
  10. 基于python的web框架——Flask 学习笔记
  11. [OPS][GPU]GPU峰值计算能力计算
  12. 快速pow算法c语言_c语言pow(c语言中用pow算法求)
  13. C语言预处理指令-单片机必备技能
  14. JAVA经典兔子问题
  15. 基于MAE的人脸素描图像属性识别和分类
  16. 辞职信微信html,女教师辞职信走红微信背后:“走心”
  17. 招银网络 Java开发-二面面经
  18. 开发辅助:从Eclipse转到Android Studio/Intellij Idea需要做的设置
  19. 计算数据的平均值、方差和标准差
  20. 神经网络中的对抗样本

热门文章

  1. 一分钟带你解读收发器指示灯以及SFP光模块搭配(二)
  2. java获取https网页代码_java抓取Https协议url地址的源码的方法
  3. android 8.0 图标规范,Android 8.0自适应图标
  4. Dapr for dotnet | 发布与订阅- Publish Subscribe
  5. 边缘计算与智慧城市应用
  6. linux安装CUPS详细教程,CUPS介紹
  7. 中文.com域名如何申请 什么是.com域名过期
  8. 彻底解决tplink路由器无法访问部分https网站
  9. FTP无法在资源管理器中打开
  10. NUCLE0 STM32L476RGT6开发板的资料整理