你的位置:首页 > 信息动态 > 新闻中心
信息动态
联系我们

Lite Git (IV) - Branch

2021-10-28 13:38:26

Lite Git (IV) - Branch

前言

本专栏名为Lite Git。主要想与Pro Git对应,后者为Git官方指南,有兴趣,或者想了解更多细节的同学,请移步官网下载PDF版。

本专栏主要为了让初出茅庐的同学更快、更合理地掌握Git的基本运用;

同时,本专栏也会介绍一下作为Android开发人员关心的:repo的运用;

本篇是该专栏的第三篇,主要介绍Git的分支创建、删除,已经了解的可以跳过此篇;

分支概念

在介绍分支的概念前,先介绍一下git log这个命令:

虽然在上一节我们就使用过git log来查看提交信息,但是对其显示的内容的标准并未给出详细定义。事实上,git log可以显示从指定端点开始的树状历史记录

这里有几个重点:

  • 指定端点,即提交的哈希值,未指定的话,则默认为HEAD指向的提交;
  • 树状历史记录,即显示出来的历史提交必须是从指定端点开始,可以通过从上至下一层一层可以到达的提交节点;

还是以demo_client这个仓库为例,上一节我们进行了两笔提交,因此这里使用git log理应看到两笔提交的信息:

ryan ~/git_demo/demo_client (master) $ git log
commit 0b64004ae55297fee8ba6453f065a43a9135e5d7 (HEAD -> master)
Author: Ryan_ZHENG <*****>
Date:   Wed Oct 27 14:19:08 2021 +0800

    [demo_client]delete FileA

commit 0dbaef0a8d45b36725fe6df20e1f1484709cea1f
Author: Ryan_ZHENG <*****>
Date:   Wed Oct 27 13:58:48 2021 +0800

    [demo_client]add FileA

git log的更多参数会在后面单独介绍,这里先理解一下这个树状结构的概念即可;

在讲解分支之前,我需要对demo_client仓库的内容需要一些填充,以更好说明问题:

# 创建若干文件
ryan ~/git_demo/demo_client (master) $ touch FileA FileB FileC FileD FileE
# 查看工作区
ryan ~/git_demo/demo_client (master) $ ll
total 12
drwxr-xr-x 3 ryan ryan 4096 Oct 28 10:57 ./
drwxr-xr-x 5 ryan ryan 4096 Oct 27 13:36 ../
drwxr-xr-x 8 ryan ryan 4096 Oct 28 10:57 .git/
-rw-r--r-- 1 ryan ryan    0 Oct 28 10:57 FileA
-rw-r--r-- 1 ryan ryan    0 Oct 28 10:57 FileB
-rw-r--r-- 1 ryan ryan    0 Oct 28 10:57 FileC
-rw-r--r-- 1 ryan ryan    0 Oct 28 10:57 FileD
-rw-r--r-- 1 ryan ryan    0 Oct 28 10:57 FileE
# 添加进暂存区
ryan ~/git_demo/demo_client (master) $ git add FileA FileB FileC FileD FileE
# 提交进仓库
ryan ~/git_demo/demo_client (master) $ git commit -m '[demo_client]add FileA FileB FileC FileD FileE'
[master 91a45d2] [demo_client]add FileA FileB FileC FileD FileE
 5 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 FileA
 create mode 100644 FileB
 create mode 100644 FileC
 create mode 100644 FileD
 create mode 100644 FileE

# 查看提交历史
ryan ~/git_demo/demo_client (master) $ git log
commit 91a45d2c680b096610e8cb785ffb16f3ec478de4 (HEAD -> master)
Author: Ryan_ZHENG <*****>
Date:   Thu Oct 28 10:57:51 2021 +0800

    [demo_client]add FileA FileB FileC FileD FileE

commit 0b64004ae55297fee8ba6453f065a43a9135e5d7
Author: Ryan_ZHENG <*****>
Date:   Wed Oct 27 14:19:08 2021 +0800

    [demo_client]delete FileA

commit 0dbaef0a8d45b36725fe6df20e1f1484709cea1f
Author: Ryan_ZHENG <*****>
Date:   Wed Oct 27 13:58:48 2021 +0800

    [demo_client]add FileA

# 推送至远端demo_bare仓库
ryan ~/git_demo/demo_client (master) $ git push origin master
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 6 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 290 bytes | 290.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To /home/ryan/git_demo/demo_bare
   0b64004..91a45d2  master -> master

然后,我们将这些修改通过git push命令推送到远端的demo_bare仓库;

ryan ~/git_demo/demo_client (master) $ git push origin master
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 6 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 290 bytes | 290.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To /home/ryan/git_demo/demo_bare
   0b64004..91a45d2  master -> master

好了,接下来我们来看考虑一下这么一个问题:

假设有两个开发团队:A与B,两者职责划分如下:

  • A为demo_bare仓库的维护者,需要负责所有文件的更新;
  • B为demo_bare仓库的使用者,需要针对项目需求,对demo_bare仓库的内容进行修改,但这些修改又不能作为公共的修改提交到master分支;

此时,就需要引入分支的概念了:

所谓分支,实际上指的是从某个指定的端点开始,从上至下到达整个仓库最初的提交的一条通路;

是不是有点熟悉,是不是和git log表达方式有点像?

没错,git log显示的就是一条通路,而当git log后面带上分支名(例如git log master),那么就是显示这条分支上的提交历史;

为了贴合上面假设的场景,我们重新从demo_bare克隆一个仓库,模拟团队B的使用情况:

# 克隆到一个名为demo_client_2的目录下
ryan ~/git_demo $ git clone ~/git_demo/demo_bare demo_client_2
Cloning into 'demo_client_2'...
done.

ryan ~/git_demo $ cd demo_client_2
# 确认当前进度
ryan ~/git_demo/demo_client_2 (master) $ git log
commit 91a45d2c680b096610e8cb785ffb16f3ec478de4 (HEAD -> master, origin/master, origin/HEAD)
Author: Ryan_ZHENG <*****>
Date:   Thu Oct 28 10:57:51 2021 +0800

    [demo_client]add FileA FileB FileC FileD FileE

commit 0b64004ae55297fee8ba6453f065a43a9135e5d7
Author: Ryan_ZHENG <*****>
Date:   Wed Oct 27 14:19:08 2021 +0800

    [demo_client]delete FileA

commit 0dbaef0a8d45b36725fe6df20e1f1484709cea1f
Author: Ryan_ZHENG <*****>
Date:   Wed Oct 27 13:58:48 2021 +0800

    [demo_client]add FileA
# 使用git branch查看本地分支情况
ryan ~/git_demo/demo_client_2 (master) $ git branch
* master

使用无参的git branch,可以查看当前本地分支的情况,此处我们只有一个master分支,且前面的*表示当前所在分支就是master分支;

而如果我们想要创建分支,使用的命令还是git branch,只是需要带上一个分支名,例如这里我取名为dev

# 创建名为dev的分支,分支进度与当前的HEAD保持一致
ryan ~/git_demo/demo_client_2 (master) $ git branch dev
# 再次查看本地分支信息,发现有了一个dev分支,但是当前还是在master分支上
ryan ~/git_demo/demo_client_2 (master) $ git branch
  dev
* master

这里需要指出,事实上创建分支的方式有很多,这里只提到其中一种,也是本地创建新分支最常规的一种;
而如果创建的新分支不希望以HEAD为进度,那么可以在git branch <branchname>后面再加上你希望分叉的节点,例如:

#提交节点可缩写为0b64004
ryan ~/git_demo/demo_client_2 (master) $ git branch dev 0b64004ae55297fee8ba6453f065a43a9135e5d7

此处我们希望以HEAD创建分支,因此就不带最后这个参数了;

然后,如果想要切换分支到dev,则使用git checkout dev即可(2.23版本以后的git可使用git switch dev实现同样效果):

# 切换分支到dev
ryan ~/git_demo/demo_client_2 (master) $ git checkout dev
Switched to branch 'dev'
# 查看本地分支信息
ryan ~/git_demo/demo_client_2 (dev) $ git branch
* dev
  master
# 查看当前分支提交历史
ryan ~/git_demo/demo_client_2 (dev) $ git log
commit 91a45d2c680b096610e8cb785ffb16f3ec478de4 (HEAD -> dev, origin/master, origin/HEAD, master)
Author: Ryan_ZHENG <*****>
Date:   Thu Oct 28 10:57:51 2021 +0800

    [demo_client]add FileA FileB FileC FileD FileE

commit 0b64004ae55297fee8ba6453f065a43a9135e5d7
Author: Ryan_ZHENG <*****>
Date:   Wed Oct 27 14:19:08 2021 +0800

    [demo_client]delete FileA

commit 0dbaef0a8d45b36725fe6df20e1f1484709cea1f
Author: Ryan_ZHENG <*****>
Date:   Wed Oct 27 13:58:48 2021 +0800

    [demo_client]add FileA

这样,我们的dev分支就在本地创建完成了。
那么按照我们假设的场景,对部分文件进行修改:

# 修改FileB的内容
ryan ~/git_demo/demo_client_2 (dev) $ echo bbb > FileB

# 查看修改情况
ryan ~/git_demo/demo_client_2 (dev) $ git status
On branch dev
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   FileB

no changes added to commit (use "git add" and/or "git commit -a")

# 对比FileB的工作区与暂存区差异
ryan ~/git_demo/demo_client_2 (dev) $ git diff -- FileB
diff --git a/FileB b/FileB
index e69de29..f761ec1 100644
--- a/FileB
+++ b/FileB
@@ -0,0 +1 @@
+bbb

# 将FileB添加进暂存区
ryan ~/git_demo/demo_client_2 (dev) $ git add FileB

# 将暂存区的改动提交进仓库
ryan ~/git_demo/demo_client_2 (dev) $ git commit -m '[demo_client_2]update FileB'
[dev 81355ba] [demo_client_2]update FileB
 1 file changed, 1 insertion(+)

# 查看当前分支(即dev分支)提交记录
ryan ~/git_demo/demo_client_2 (dev) $ git log
commit 81355ba55b9819d84a10d9d215150001f7224a4b (HEAD -> dev)
Author: Ryan_ZHENG <*****>
Date:   Thu Oct 28 13:05:27 2021 +0800

    [demo_client_2]update FileB

commit 91a45d2c680b096610e8cb785ffb16f3ec478de4 (origin/master, origin/HEAD, master)
Author: Ryan_ZHENG <*****>
Date:   Thu Oct 28 10:57:51 2021 +0800

    [demo_client]add FileA FileB FileC FileD FileE

commit 0b64004ae55297fee8ba6453f065a43a9135e5d7
Author: Ryan_ZHENG <*****>
Date:   Wed Oct 27 14:19:08 2021 +0800

    [demo_client]delete FileA

commit 0dbaef0a8d45b36725fe6df20e1f1484709cea1f
Author: Ryan_ZHENG <*****>
Date:   Wed Oct 27 13:58:48 2021 +0800

    [demo_client]add FileA
# 查看master分支的提交记录
ryan ~/git_demo/demo_client_2 (dev) $ git log master
commit 91a45d2c680b096610e8cb785ffb16f3ec478de4 (origin/master, origin/HEAD, master)
Author: Ryan_ZHENG <*****>
Date:   Thu Oct 28 10:57:51 2021 +0800

    [demo_client]add FileA FileB FileC FileD FileE

commit 0b64004ae55297fee8ba6453f065a43a9135e5d7
Author: Ryan_ZHENG <*****>
Date:   Wed Oct 27 14:19:08 2021 +0800

    [demo_client]delete FileA

commit 0dbaef0a8d45b36725fe6df20e1f1484709cea1f
Author: Ryan_ZHENG <*****>
Date:   Wed Oct 27 13:58:48 2021 +0800

    [demo_client]add FileA

可以看到,新提交的“对FileB的修改”只记录在了dev分支上,而在使用git log master查看master分支提交记录时,是没有这笔提交信息的,这里上一张图:
在这里插入图片描述
再次强调一下,git log与分支的概念都是从顶端自上而下的。因此,虽然91a45d281355ba有唯一、且直接的联系,但81355ba是在91a45d2之上的,而master分支的顶点是在91a45d2,所以master分支没有81355ba这笔提交的信息,反之dev分支此时是包含master分支所有提交信息的;(注意,只是此时

为何强调此时,这是由于dev分支创建时是基于master分支,即彼时master分支的HEAD 91a45d2,因此dev分支至少包含master分支从91a45d2到第一笔提交的所有提交信息;而由于master分支在dev分支创建之后并未有HEAD的变动,因此在
master分支顶点发生改变之前” 这段时间内,dev分支是包含master分支上所有提交的;

那么现在,我们就来让master分支的顶点(HEAD)动一动,以加深各位对分支的理解;

在此之前,我们要将dev分支的信息推送到远端demo_bare中去;

ryan ~/git_demo/demo_client_2 (dev) $ git push origin dev
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 6 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 303 bytes | 303.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
To /home/ryan/git_demo/demo_bare
 * [new branch]      dev -> dev

好了,现在我们回到demo_client这个本地仓库,模拟团队A的工作:

# 回到demo_client的这个目录,模拟上面举例的团队A的工作
ryan ~/git_demo/demo_client_2 (dev) $ cd ~/git_demo/demo_client
# 使用git pull从demo_bare更新所有分支信息
ryan ~/git_demo/demo_client (master) $ git pull origin
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (3/3), 283 bytes | 283.00 KiB/s, done.
From /home/ryan/git_demo/demo_bare
 * [new branch]      dev        -> origin/dev
Already up to date.

# 再使用git pull确保master本地分支同步到最新状态
ryan ~/git_demo/demo_client (master) $ git pull origin master
From /home/ryan/git_demo/demo_bare
 * branch            master     -> FETCH_HEAD
Already up to date.

# 查看当前分支(即master)的提交记录
ryan ~/git_demo/demo_client (master) $ git log
commit 7a0a4ffe3713ed92346a43f56b2159b02ac7f24c (HEAD -> master)
Author: Ryan_ZHENG <*****>
Date:   Thu Oct 28 13:23:30 2021 +0800

    [demo_client]update FileB

commit 91a45d2c680b096610e8cb785ffb16f3ec478de4 (origin/master)
Author: Ryan_ZHENG <*****>
Date:   Thu Oct 28 10:57:51 2021 +0800

    [demo_client]add FileA FileB FileC FileD FileE

commit 0b64004ae55297fee8ba6453f065a43a9135e5d7
Author: Ryan_ZHENG <*****>
Date:   Wed Oct 27 14:19:08 2021 +0800

    [demo_client]delete FileA

commit 0dbaef0a8d45b36725fe6df20e1f1484709cea1f
Author: Ryan_ZHENG <*****>
Date:   Wed Oct 27 13:58:48 2021 +0800

    [demo_client]add FileA

准备就绪以后,我们对master分支的文件进行修改:

# 团队A同样对FileB进行了修改,但内容不同
ryan ~/git_demo/demo_client (master) $ echo "b2b2b2" > FileB
# 查看修改情况
ryan ~/git_demo/demo_client (master) $ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   FileB

no changes added to commit (use "git add" and/or "git commit -a")
# 查看FileB在工作区与暂存区中的差异
ryan ~/git_demo/demo_client (master) $ git diff -- FileB
diff --git a/FileB b/FileB
index e69de29..5d8239e 100644
--- a/FileB
+++ b/FileB
@@ -0,0 +1 @@
+b2b2b2
# 添加FileB到暂存区
ryan ~/git_demo/demo_client (master) $ git add FileB
# 提交进仓库
ryan ~/git_demo/demo_client (master) $ git commit -m '[demo_client]update FileB'
[master 7a0a4ff] [demo_client]update FileB
 1 file changed, 1 insertion(+)
# 查看
ryan ~/git_demo/demo_client (master) $ git log
commit 7a0a4ffe3713ed92346a43f56b2159b02ac7f24c (HEAD -> master)
Author: Ryan_ZHENG <easter_walk@hotmail.com>
Date:   Thu Oct 28 13:23:30 2021 +0800

    [demo_client]update FileB

commit 91a45d2c680b096610e8cb785ffb16f3ec478de4 (origin/master)
Author: Ryan_ZHENG <easter_walk@hotmail.com>
Date:   Thu Oct 28 10:57:51 2021 +0800

    [demo_client]add FileA FileB FileC FileD FileE

commit 0b64004ae55297fee8ba6453f065a43a9135e5d7
Author: Ryan_ZHENG <easter_walk@hotmail.com>
Date:   Wed Oct 27 14:19:08 2021 +0800

    [demo_client]delete FileA

commit 0dbaef0a8d45b36725fe6df20e1f1484709cea1f
Author: Ryan_ZHENG <easter_walk@hotmail.com>
Date:   Wed Oct 27 13:58:48 2021 +0800

    [demo_client]add FileA

此时,master分支的顶点发生了改变,因此此时的dev分支不再包含master分支的所有提交,两者正式分叉。分支结构图如下图:

在这里插入图片描述

总结

本文介绍了分支概念,并介绍了新分支的创建;

细心读者可能会有疑问,masterdev分支都修改了FileB,那么怎么实现假设的场景里“A的改动通知到B”呢?这个我们下一节,介绍git merge时再介绍;