对于版本提交为什么不能依次进行,以便形成一条直线型的提交历史记录,我们认为有 以下两个重要原因。

  • 有两个以上的开发者在对同一个项目进行并行式开发。
  • 为修复旧版本中的 bug 而必须要创建和发布新的版本。

如果遇到以上两种情况,我们的提交历史图中就会出现分叉的情况。

【Git教程】(五)分支 —— 并行式开发,分支相关操作(创建、切换、删除)~-LMLPHP


1️⃣ 并行式开发

当有多个开发者用 Git 处理同一个软件开发项目时,他们就会在版本库的提交图中创建 各自的分支。下面我们来看下图, 其上半部分所显示的是两个独立的开发者在各自的本 地版本库中,基于提交B 成功创建了各自的版本(即提交C 和 D) 。 而在图的下半部分, 你将看到的是它们合并之后的版本库(关于合并的相关细节请参见后续文章)。正如你所见,它创建了一个分支,这种类型的分叉在并行式开发中是难以避免的。

【Git教程】(五)分支 —— 并行式开发,分支相关操作(创建、切换、删除)~-LMLPHP


2️⃣ 修复旧版本中的 bug

正如我们之前所说,分支可能会因并行式开发的需要 而创建。但除此之外,它也有可能会因修复软件旧版本中 的 bug 而创建。下面我们来看一个例子(见下图):假设 当开发者们正在为即将发布的版本(提交 C 和 D) 加班加点时,我们在该软件的当前版本(提交B) 中检测到了一个错误。由于眼下带有新功能的提交 C 和 D 都还没有 准备好交付,用于错误修复的提交 E 只能基于提交 B 来创建。

【Git教程】(五)分支 —— 并行式开发,分支相关操作(创建、切换、删除)~-LMLPHP


3️⃣ 分支

下面再来看看下图,在这个例子中,我们一方面在 release1 这一当前发行版上继续当前 开发分支 master 上的工作。随着各轮新的提交,该分支始终处于活跃向前的状态。而另一方面,我们会看到 release1 分支从右边岔开了,它对自身的bug 进行了修复。

【Git教程】(五)分支 —— 并行式开发,分支相关操作(创建、切换、删除)~-LMLPHP

分支可以看作是开发过程当中的并行线,我们可以把该提交图想象成游泳池中的泳道(见 下图)。

【Git教程】(五)分支 —— 并行式开发,分支相关操作(创建、切换、删除)~-LMLPHP

注意:Git并不知道某次提交是否被分配给了某个分支,划分泳道在这里某些程度上来 说算是一种比喻性的说法。

4️⃣ 当前活跃分支

在一个 Git 版本库中,总是唯一存在着一个活动分支。我们可以用 branch 命令(不带选
项)来列出当前所有的分支。其中用星号 (*) 凸显的就是当前的当前活跃分支。

> git branch
  a-branch
* master
  still-a-branch

一般情况下,活动分支将会被继续用于接受所有新的提交,并将分支指针移动至最近的
那次提交。当然,我们也可以用checkout 命令来改变当前的活跃分支。

> git checkout a-branch

通常情况下,我们可以用 checkout 命令在分支之间来回切换。但是,如果这时候工作 区还存在着一些修改,我们就必须要先决定好如何处理这部分修改。

  1. 进行 Checkout
    下面这个checkout 很可能会被拒绝。

    > git checkout a-branch
    	error:Your local changes to the following files would be overwritten by checkout:foo.txt
    	Please,commit your changes or stash them before you can switch branches.
    	Aborting
    

    如你所见,工作区或暂存区存在着一些修改,它们还没有被确认为一次提交。所以我们必须先决定以下何种方式来处理这些修改。

  2. 提交修改并切换

    > git commit --all
    > git checkout a-branch
    
  3. 放弃这部分修改并进行切换
    我们可以用 --force 选项来进行强制切换,但这样做会令这部分修改被覆盖!

    > git checkout --force a-branch
    
  4. 储藏修改并切换
    我们可以用stash 命令先将这部分修改储藏起来,然后再进行切换。 之后再用 stash pop 命令来恢复它们。

    > git stash
    > git checkout a-branch
    

5️⃣ 重置分支指针

分支指针主要用于指向活动分支,它会每次提交时移动到最新提交上。因此在通常情况 下,我们几乎不太需要去直接设置分支指针。但偶尔我们也会因一些偶发事件而失去对该指针的跟踪,想将其恢复到之前的状态。在这种情况下,我们可以用 reset 命令来重置分支指针。

> git reset --hard 39ea2la

这样一来,该指针就被重置到了提交39ea21a 所在的活动分支上。其中的 --hard 选项用于确保工作区和暂存区也都会被设置都提交39ea21a 的状态。

需要提醒的是,reset --hard 命令会覆盖当前工作区和暂存区中的所有修改。所以最好在 执行重置之前先用 git stash 命令存储一下这些修改。


6️⃣ 删除分支

我们可以通过branch -d 命令来删除分支。

  1. 删除一个已被终止的分支

    > git branch -d b-branch
    
  2. 删除一个打开的分支
    如果我们在试图删除一个分支时自己还未转移到不同的分支(例如 master分支)上,Git 就会给出一个警告,并拒绝该删除操作。如果你坚持要删除该分支的话,就需要在命令中使用-D 选项。

    error: The branch 'b-branch' is not fully merged.
    If you are sure you want to delete it,run 'git branch -D b-branch'.
    > git branch -D b-branch
    Deleted branch b-branch(was 742dcf6).
    

Git会自行负责分支的管理,所以当我们删除一个分支时,Git只是删除了指向相关提 交的指针,但该提交对象依然会留在版本库中。因此,如果我们知道删除分支时的散列信
息,就可以将某个已删除的分支恢复过来。

  1. (在已知提交的散列值的情况下) 恢复某个分支
    > git branch a-branch 742dcf6
    
    1.1. 先确定相关的提交散列值
    如果我们不知道想要恢复分支的提交散列值,可以reflog 命令将它找出来。
    > git reflog
    	d765ale HEAD@{0}: checkout: moving from b-branch to master
    	88117f6 HEADC{1}: merge b-branch:Fast-forward
    	9332b08 HEAD@{2}: checkout: moving from a-branch to b-branch 
    	441cdef HEAD@{3}: commit: Expanded important stuff
    
    1.2. (通过 reflog 命令找到的散列值) 恢复该分支
    > git branch b-branch HEAD@{1}
    

7️⃣ 清理提交对象

gc 命令(gc 指的是垃圾回收) 可用于清理版本库,移除所有不属于当前分支的提交对象。 如果我们想进一步净化自己的版本库,可以先将它克隆一份,并删除其源版本库。


🌾 总结

  • 提交图中的分叉:主要是因为修复旧版本 bug 以及并行式开发的需要。
  • 分支:上述提交图中出现的那个分叉就叫做分支。该分支会有一个指针,指向该分支 下的最后一次提交。
  • 当前活跃分支:我们平常工作所在的就是所谓的当前活跃分支。当新的提交发生时, 该分支指针就会知道被设置到该新提交上。
  • 创建分支:我们可以用branch 命令来新建分支。
  • checkout: 我们可以用 checkout 命令切换到另一个分支上。
  • Reflog: git 会记录我们在每次提交中对分支指针所做的所有修改。如果你想恢复某个不小心删除的分支,这是非常有用的工具。
  • 垃圾处理:对于那些不属于任何分支前身的提交,我们将其视为垃圾,可以用 gc 命令将其清理掉。


【Git教程】(五)分支 —— 并行式开发,分支相关操作(创建、切换、删除)~-LMLPHP

1

02-29 19:01