0%

Git Cookbook

这里介绍Git的常用命令、用法

abstract.png

基本模型

工作区域

Git有三个工作区域,分别是:

  • Working Directory 工作目录:其是本地文件系统中的一个目录,包含了项目文件的当前状态。从Git仓库中检出时,项目文件即会位于工作目录中. 可以在这里对项目文件进行修改、查看、删除等操作
  • Staging Area 暂存区:其是Repository仓库中名为index的文件,保存了下次将要提交的文件列表信息
  • Repository 仓库:其是工作目录下一个名为.git的隐藏目录。仓库是git的核心部分,存储了项目的版本信息、历史记录等数据

figure 1.png

文件状态

Git中文件的状态:

  • Untracked 未跟踪:在工作目录下新建文件后,该文件即会处于未跟踪的状态。只有将其添加到git的暂存区后,git才会对其进行版本管理
  • Unmodified 未修改:文件已经被git跟踪,且内容与仓库中的版本完全一致
  • Modified 已修改:文件已经被git跟踪。其内容虽然被修改,但这些修改尚未添加到暂存区
  • Staged 已暂存:文件已经被git跟踪。其内容虽然被修改,但这些修改已经被添加到暂存区。这样下次提交时这些被暂存的修改即被记录到仓库中

figure 2.png

合并 VS 变基

合并命令如下

1
2
# 将指定分支的更改合并到当前分支中
git merge <branch>

Fast-forward Merge 快进合并:如果当前分支是 分支的直接祖先,则Git可以将当前分支的指针直接移动到分支的最新提交处即可完成合并。此时将不会创建一个新的合并提交。如下图所示

figure 3.png

Non-fast-forward Merge 非快进合并:如果当前分支不是 分支的直接祖先,则Git会通过两个分支的最新提交、及两个分支的最近公共祖先进行Three-way Merge三方合并创建一个新的合并提交(即下图的C5)。合并成功后,则当前分支的指针将指向这个新的合并提交。如下图所示

figure 4.png

变基命令如下,base branch又被称为目标基底分支。该命令会将当前分支上的所有提交都移至目标基底分支上。特别地,当base branch分支未指定时,Git会使用当前分支的上游分支

1
2
# 将当前分支上的提交重新应用到 <base branch> 分支上
git rebase <base branch>

变基的基本原理:

  1. 首先,找到这两个分支的最近公共祖先(即C1),然后对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件
  2. 然后,将 当前分支 指向 目标基底分支的最新提交(即C2)
  3. 最后,以此将之前另存为临时文件的修改依序应用

figure 5.png

从上图不难看出,在我们完成变基后只需切到master分支,进行一次快进合并就可以将feature分支的修改整合到master分支中了。不难看出,变基与合并这两种整合方式在最终结果上没有区别。而且相比较于直接合并,先变基再合并(快进合并)可以让(这里的master分支的)提交历史更加整洁、清晰。因为没有产生新的合并提交

But……

从上图我们会发现一个问题,当我们在feature分支上执行变基命令后,该分支的提交历史发生了改变(上图置为灰色的提交历史)。因为,变基的本质是丢弃一些现有的提交(例如:C3、C4),然后相应地创建一些内容一样但实质上不同的新提交(例如:C3’、C4’)。所以,如果你此前已经将这些提交(例如:C3、C4)push到远程仓库,而其他人又从该仓库拉取这些提交并进行了后续工作……接下来迎接你们的就是Boom……

所以,正确使用变基的命令原则是:永远只在私有分支上执行变基命令。即,只有在当前分支是私有分支的前提下,才允许执行变基命令。换言之,一旦某个分支已经push到远程仓库成为协作分支了。不论别人是否真的会拉取、修改、提交该分支。都永远不要在这个分支下执行变基命令,而是老老实实的使用常规的合并命令来进行整合。而对于 base branch目标基底分支 就没有这个限制了,无论是私有分支还是协作分支都可以。因为变基命令不会影响该分支,更不会修改该分支的提交历史了

分支比较

两点语法:差集

下述命令会展示存在于分支B而不存在于分支A中的提交,即:B-A的差集。如果未指定分支B时,则默认为HEAD,即当前分支

1
2
3
4
5
# 展示存在于分支B而不存在于分支A中的提交
git log 分支A..分支B

# 展示存在于当前分支而不存在于分支A的提交
git log 分支A..

常用场景如下:

查看feature分支中未合并到master分支的提交

1
git log master..feature

假设HEAD指向feature分支。如果想知道push时,哪些提交会被push到远程仓库时,可以通过下述方式查看

1
git log origin/feature..HEAD

三点语法:对称差集

下述命令会展示 只存在于分支A只存在于分支B 的提交,而不会展示同时存在于A、B分支的提交。即:A和B的对称差集。如果未指定分支B时,则默认为HEAD,即当前分支。可以添加 —left-right 选项,这样输出结果中带<标记的表示是分支A的提交、带>标记的表示是分支B的提交

1
2
3
4
5
6
7
8
9
# 展示 只存在于分支A 和 只存在于分支B 的提交
git log 分支A...分支B

# 展示 只存在于分支A 和 只存在于分支B 的提交
# 其中:带<标记的表示是分支A的提交;带>标记的表示是分支B的提交
git log --left-right 分支A...分支B

# 展示 只存在于分支A 和 只存在于当前分支 的提交
git log 分支A...

figure 6.png

fetch VS pull

git fetch抓取命令

仅仅会从指定的远程仓库获取最新数据到本地,并在本地创建对应的远程分支引用(e.g., origin/master、origin/feature等)。该命令并不会把远程分支的提交合并、修改到本地分支当中。故你可以使用该命令来查看远程分支的最新状态,而不影响你的本地仓库、分支

1
2
3
4
5
# 从远程仓库抓取全部分支的最新数据
git fetch <remote>

# 从远程仓库抓取指定分支的最新数据
git fetch <remote> <branch>

git pull拉取命令

该命令首先会从远程仓库获取某个远程分支(指定分支 或 当前本地分支关联的远程分支)最新的数据到本地,然后将获取到该远程分支的数据自动合并到当前的本地分支。如果在自动合并过程中发生冲突,Git会停止合并并让你解决这些冲突

1
2
3
4
5
# 拉取远程仓库指定分支的数据,并合并到当前的本地分支上
git pull <origin> <branch>

# 如果当前的本地分支设置了upstream branch上游分支,则git pull命令可以省略远程仓库名、远程分支名
git pull

所以git pull命令实际上是git fetch、git merge命令的组合

1
2
3
4
# 从远程仓库抓取指定分支的最新数据
git fetch <origin> <branch>
# 将远程分支合并到当前的本地分支
git merge <origin>/<branch>

撤销修改

撤销 工作区的修改

下述两个命令均可以撤销工作目录中未暂存的修改。换言之,如果某个文件在工作区被修改后,未进行暂存。则使用该命令会将该文件恢复到上一次提交的版本;如果某个文件经历了第一次修改、暂存、第二次修改过程,则此时执行该命令后,该文件只会丢弃第二次的修改。第一次的修改依然保留在暂存区了

1
2
3
git checkout --  <file> 

git restore <file>

figure 7.png

撤销 暂存区的修改

如果误使用git add命令,将文件修改添加到暂存区。则下述两个命令都可以实现撤销对文件修改的暂存。下图可看到撤销暂存后,文件修改保留在工作目录中,并没有丢失。等待我们下一次添加暂存

1
2
3
git reset HEAD <file>

git restore --staged <file>

figure 8.png

git diff命令

git diff命令

该命令比较的是 修改后但还未暂存起来(git add)的差异。所以如果你暂存了(git add)所有修改过的文件,此时执行git diff后会发现差异结果为空

1
2
# 比较 工作目录下文件 与 暂存区域快照 之间的差异
git diff

figure 9.png

—staged/—cached 选项

下述两个命令功能上是一样的。都是比较 暂存区(git add)和上一次提交(HEAD)之间的差异

1
2
3
# 下述两个命令功能上是一样的
git diff --cached
git diff --staged

figure 10.png

git commit命令

-a选项

使用该选项后,git commit命令会把已跟踪文件的所有修改直接进行提交。相当于跳过了手动执行git add命令的过程。但对于未跟踪的新文件,仍需先使用git add命令手动将它们添加到暂存区

1
git commit -am <commit message>

figure 11.png

—amend选项

该选项用于修改最近一次提交。注意,如果最近一次的提交已经push到远程仓库了,强烈不推荐使用该选项。原因详见变基rebase相关部分的内容

修改提交信息

进行提交后发现message信息写错了。可以直接执行下述命令,用于修改上一次提交的message

1
git commit --amend -m <new commit message>

figure 12.png

提交遗漏修改

进行git commit操作提交后,如果发现某些文件遗漏了。我们就需要再次修改、提交。如果不希望出现两次提交记录,就可以提交时添加该选项。这样最终两次git commit操作也只会有一个提交记录,因为第二次git commit操作会包含第一次git commit的修改

1
2
# 与上次提交进行合并,并使用新的提交信息
git commit --amend -am <new commit message>

figure 13.png

参考文献

  1. 精通Git·第2版 Scott Chacon、Ben Straub著
请我喝杯咖啡捏~

欢迎关注我的微信公众号:青灯抽丝