除此之外利用分支还可以做很多事情,比如现在有一个需求不确定要不要上线,但是得先做,此时可以单独创建一个分支开发该功能,等到啥时候需要上线直接合并到主分支即可。分支适用的场景很多就不一一列举了。
注意点
当在某个节点创建一个分支后,并不会把该节点对应的代码复制一份出来,只是将新分支指向该节点,因此可以很大程度减少空间上的开销。一定要记着不管是HEAD还是分支它们都只是引用而已,量级非常轻
3. 命令详解
3.1 提交相关
前面我们提到过,想要对代码进行提交必须得先加入到暂存区,Git中是通过命令 add 实现
添加某个文件到暂存区:
git add 文件路径 # 添加所有文件到暂存区:
git add . 同时Git也提供了撤销工作区和暂存区命令 # 撤销工作区改动:
git checkout -- 文件名 # 清空暂存区:git reset HEAD 文件名 # 提交: 将改动文件加入到暂存区后就可以进行提交了,提交后会生成一个新的提交节点,具体命令如下:git commit -m "该节点的描述信息" # 3.2 分支相关 # 创建分支 创建一个分支后该分支会与HEAD指向同一节点,说通俗点就是HEAD指向哪创建的新分支就指向哪,命令如下:git branch 分支名 # 切换分支 当切换分支后,默认情况下HEAD会指向当前分支,即HEAD间接指向当前分支指向的节点git checkout 分支名 同时也可以创建一个分支后立即切换,命令如下:git checkout -b 分支名 # 删除分支 为了保证仓库分支的简洁,当某个分支完成了它的使命后应该被删除。比如前面所说的单独开一个分支完成某个功能,当这个功能被合并到主分支后应该将这个分支及时删除。 删除命令如下:git branch -d 分支名 # 3.3 合并相关 关于合并的命令是最难掌握同时也是最重要的。我们常用的合并命令大概有三个merge、rebase、cherry-pick # merge merge是最常用的合并命令,它可以将某个分支或者某个节点的代码合并至当前分支。具体命令如下:git merge 分支名/节点哈希值 如果需要合并的分支完全领先于当前分支,如图3-1所示 ![image](https://img-blog.csdnimg.cn/img_convert/e91a85a6e856e729d76bc6c9fa3ed89d.png) 由于分支ft-1完全领先分支ft-2即ft-1完全包含ft-2,所以ft-2执行了“git merge ft-1”后会触发fast forward(快速合并),此时两个分支指向同一节点,这是最理想的状态。但是实际开发中我们往往碰到是是下面这种情况:如图3-2(左) ![image](https://img-blog.csdnimg.cn/img_convert/b5b9c84ad1a711ecd486fec90337a16f.png) 这种情况就不能直接合了,当ft-2执行了“git merge ft-1”后Git会将节点C3、C4合并随后生成一个新节点C5,最后将ft-2指向C5 如图3-2(右) 注意点: > 如果C3、C4同时修改了同一个文件中的同一句代码,这个时候合并会出错,因为Git不知道该以哪个节点为标准,所以这个时候需要我们自己手动合并代码 # rebase rebase也是一种合并指令,命令行如下:git rebase 分支名/节点哈希值 与merge不同的是rebase合并看起来不会产生新的节点(实际上是会产生的,只是做了一次复制),而是将需要合并的节点直接累加 如图3-3 ![image](https://img-blog.csdnimg.cn/img_convert/c208c8b9e1e7d7ebc24f6429bfe73d89.png) 当左边示意图的ft-1.0执行了git rebase master后会将C4节点复制一份到C3后面,也就是C4',C4与C4'相对应,但是哈希值却不一样。 rebase相比于merge提交历史更加线性、干净,使并行的开发流程看起来像串行,更符合我们的直觉。既然rebase这么好用是不是可以抛弃merge了?其实也不是了,下面我罗列一些merge和rebase的优缺点: merge优缺点: > 优点:每个节点都是严格按照时间排列。当合并发生冲突时,只需要解决两个分支所指向的节点的冲突即可 缺点:合并两个分支时大概率会生成新的节点并分叉,久而久之提交历史会变成一团乱麻 rebase优缺点: > 优点:会使提交历史看起来更加线性、干净 缺点:虽然提交看起来像是线性的,但并不是真正的按时间排序,比如图3-3中,不管C4早于或者晚于C3提交它最终都会放在C3后面。并且当合并发生冲突时,理论上来讲有几个节点rebase到目标分支就可能处理几次冲突 对于网络上一些只用rebase的观点,作者表示不太认同,如果不同分支的合并使用rebase可能需要重复解决冲突,这样就得不偿失了。但如果是本地推到远程并对应的是同一条分支可以优先考虑rebase。所以我的观点是 根据不同场景合理搭配使用merge和rebase,如果觉得都行那优先使用rebase # cherry-pick cherry-pick的合并不同于merge和rebase,它可以选择某几个节点进行合并,如图3-4 命令行:git cherry-pick 节点哈希值 ![image](https://img-blog.csdnimg.cn/img_convert/2f36ecc53d7347a13cc5d8cf3376daa0.png) 假设当前分支是master,执行了git cherry-pick C3(哈希值),C4(哈希值)命令后会直接将C3、C4节点抓过来放在后面,对应C3'和C4' # 3.4 回退相关 # 分离HEAD 在默认情况下HEAD是指向分支的,但也可以将HEAD从分支上取下来直接指向某个节点,此过程就是分离HEAD,具体命令如下:git checkout 节点哈希值 //也可以直接脱离分支指向当前节点 git checkout --detach 由于哈希值是一串很长很长的乱码,在实际操作中使用哈希值分离HEAD很麻烦,所以Git也提供了HEAD基于某一特殊位置(分支/HEAD)直接指向前一个或前N个节点的命令,也即相对引用,如下://HEAD分离并指向前一个节点 git checkout 分支名/HEAD^//HEAD分离并指向前N个节点 git checkout 分支名~N 将HEAD分离出来指向节点有什么用呢?举个例子:如果开发过程发现之前的提交有问题,此时可以将HEAD指向对应的节点,修改完毕后再提交,此时你肯定不希望再生成一个新的节点,而你只需在提交时加上--amend即可,具体命令如下:git commit --amend # 回退 回退场景在平时开发中还是比较常见的,比如你巴拉巴拉写了一大堆代码然后提交,后面发现写的有问题,于是你想将代码回到前一个提交,这种场景可以通过reset解决,具体命令如下://回退N个提交 git reset HEAD~N reset和相对引用很像,区别是reset会使分支和HEAD一并回退。 # 3.5 远程相关 当我们接触一个新项目时,第一件事情肯定是要把它的代码拿下来,在Git中可以通过clone从远程仓库复制一份代码到本地,具体命令如下:git clone 仓库地址 前面的章节我也有提到过,clone不仅仅是复制代码,它还会把远程仓库的引用(分支/HEAD)一并取下保存在本地,如图3-5所示: ![image](https://img-blog.csdnimg.cn/img_convert/822335f91d68da772fcedc48fe287071.png) 其中origin/master和origin/ft-1为远程仓库的分支,而远程的这些引用状态是不会实时更新到本地的,比如远程仓库origin/master分支增加了一次提交,此时本地是感知不到的,所以本地的origin/master分支依旧指向C4节点。我们可以通过fetch命令来手动更新远程仓库状态 **小提示:** > 并不是存在服务器上的才能称作是远程仓库,你也可以clone本地仓库作为远程,当然实际开发中我们不可能把本地仓库当作公有仓库,说这个只是单纯的帮助你更清晰的理解分布式 # fetch 说的通俗一点,fetch命令就是一次下载操作,它会将远程新增加的节点以及引用(分支/HEAD)的状态下载到本地,具体命令如下:git fetch 远程仓库地址/分支名 # pull pull命令可以从远程仓库的某个引用拉取代码,具体命令如下:git pull 远程分支名 其实pull的本质就是fetch+merge,首先更新远程仓库所有状态到本地,随后再进行合并。合并完成后本地分支会指向最新节点 另外pull命令也可以通过rebase进行合并,具体命令如下:git pull --rebase 远程分支名 # push true">git pull 远程分支名 其实pull的本质就是fetch+merge,首先更新远程仓库所有状态到本地,随后再进行合并。合并完成后本地分支会指向最新节点 另外pull命令也可以通过rebase进行合并,具体命令如下:git pull --rebase 远程分支名 # push