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
与分支的概念都是从顶端自上而下的。因此,虽然91a45d2
与81355ba
有唯一、且直接的联系,但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
分支的所有提交,两者正式分叉。分支结构图如下图:
总结
本文介绍了分支概念,并介绍了新分支的创建;
细心读者可能会有疑问,master
和dev
分支都修改了FileB,那么怎么实现假设的场景里“A的改动通知到B”呢?这个我们下一节,介绍git merge
时再介绍;