Git - 在版本之间切换自如

栏目: 编程工具 · 发布时间: 5年前

内容简介:Git 的使用说难也不难,对很多人而言,git 无外乎那么几种命令。使用 git 开发就如同一条流水线式的作业:pull 下来、checkout 分支、add 一下、commit 一下、push 完事。Git 用起来非常舒适,因为靠着这么一顿操作,大多数情况下都能满足需要。但是一旦有一天中午,昏昏欲睡的你猛然发现自己好几次 commit 错了东西,或是一直工作在一个错误的分支上,那么 git 对于你来说,突然间就变成了洪水猛兽,平常使用的命令没有一个派的上用场。痛定思痛,除了满足日常开发,还是得掌握更多 g

Git 的使用说难也不难,对很多人而言,git 无外乎那么几种命令。使用 git 开发就如同一条流水线式的作业:pull 下来、checkout 分支、add 一下、commit 一下、push 完事。Git 用起来非常舒适,因为靠着这么一顿操作,大多数情况下都能满足需要。

但是一旦有一天中午,昏昏欲睡的你猛然发现自己好几次 commit 错了东西,或是一直工作在一个错误的分支上,那么 git 对于你来说,突然间就变成了洪水猛兽,平常使用的命令没有一个派的上用场。

痛定思痛,除了满足日常开发,还是得掌握更多 git 的使用,才能让在下一次犯错的时候吃上一瓶后悔药。

基础知识

学习 git 的一个重要的技巧就是结合类弹珠图的 git 提交历史来看,推荐使用一些图形化界面比如 Sourcetree 来观察每次执行命令之后 history 的变化。

提交对象

使用 git 作为版本控制系统,首先要理解什么是一个版本:每当进行一次 commit 操作时,Git 会保存一个提交对象(commit object),可以理解这个提交对象就包含了这次改动的内容快照,根据不同提交对象的 commit id ,可以随时访问不同版本的内容。

举例来讲:

1)添加 README 文件,add -> commit -> git 生成 commit 98c27 2)更新 README 为 v2、添加 app.js、package.json 文件,add -> commit -> git 生成 commit 2c9be 3)更新 README 为 v3、更新 app.js 为 v2,add -> commit -> git 生成 commit 1a35c

示意图如下:

Git - 在版本之间切换自如

时间轴是从左往右,但是 commit 的指向正好相反,因为每个新创建的 commit,都以前一个 commit 为父节点,所以指向前一个 commit。

每一个 commit 都包含了 当时版本的文件快照 ,也就是说,切换到某一个 commit,就能回到对应的版本上去。

分支

所以,我们如何知道自己在哪个 commit 上?一般来说,我们都是工作在分支上的,比如当一个项目初始化时,都默认有一个 master 分支, 分支本质上仅仅是指向提交对象的可变指针 。比如如下的 master 分支上,README 已经是 v3 版本了:

Git - 在版本之间切换自如

当在 master 分支上 checkout 一个分支出来时,仅仅是创建了一个新的指针,指向了当前的 commit。

Git - 在版本之间切换自如

所以此时 master 分支上和 feature 分支上的内容是一样的。

我们接着 commit,然后 master 的指针就会指向新的 commit:

Git - 在版本之间切换自如

为什么不是 feature 的指针改变?换句话说,git 怎么知道我们处在哪个分支?这是因为有一个特殊的指针 HEAD ,它指向当前所在的本地分支:

Git - 在版本之间切换自如

刚才创建分支使用的是 git branch 命令,它只是创建一个分支,并不会自动切换过去,使用 git checkout 命令使得 HEAD 指向特定的分支:

Git - 在版本之间切换自如

继续 commit,feature 将往前移动:

Git - 在版本之间切换自如

这时可以发现两个分支开始分叉了,也就是说这两个分支基于同一个版本分别做了不同的改动。

三个集合

要理解如何操纵 commit,还需要理解 git 中的三个集合。在 git 中,文件有三种状态:已修改(modified)、已暂存(staged)和已提交(committed),它们分别对应于三个区域: 工作区 (working directory)、 暂存区 (staging area) 和 版本库 (repository)。

Git - 在版本之间切换自如

git 通过比较三个区域的内容,来提示用户需要做的操作。比如工作区与暂存区不同,意味着你需要 add;暂存区与版本库不同,意味着你已经 add 但尚未 commit。

我们知道,基本的 Git 工作流程如下:

  1. 工作目录 中修改文件。
  2. 暂存文件(add),将文件的快照放入 暂存区域
  3. 提交更新(commit),找到暂存区域的文件,将快照永久性存储到 Git 仓库目录

假设我们处在 feature 分支,当我们进行以上操作后,commit 与三个区域的内容变化分别如下:

Git - 在版本之间切换自如

从左到右分别是工作区、暂存区和版本库,版本库上面标注了 HEAD,这表示我们展示的是 HEAD 指针指向的内容,也就是当前分支所在的 commit。

接下来对工作区内的文件进行修改,换句话说就是修改磁盘上的文件:

Git - 在版本之间切换自如

修改磁盘上的文件只是改变了工作区的内容,接下来使用 git add 命令将修改提交到暂存区:

Git - 在版本之间切换自如

将修改提交到暂存区并不会新增一个 commit,接下来通过 git commit 来提交:

Git - 在版本之间切换自如

此时新的 commit 被创建,HEAD 指向的 commit 相应发生改变。

有了上面这些 git 操作的印象后,解释如何切换版本就容易多了。

Git 中的撤销

撤销可能是使用过程中最需要的操作,你可能在任何时候都需要撤销。根据情况不同,撤销的命令也是不同的。

撤销最近几次 commit

要撤销最近几次的提交,可以使用 git reset ,下面介绍它的三种模式: softmixedhard

假设目前分支情况如下,我们需要撤销到 98c27 commit 上去。

Git - 在版本之间切换自如

1)soft 模式

执行 git reset --soft 98c27 ,git 会首先修改 HEAD 的指向,它会连带修改 HEAD 所在分支的指向:

Git - 在版本之间切换自如

如上图所示,现在的暂存区和 HEAD 是不同的,这个操作本质上撤销了 2c9be 这个 commit,如同回到了上次准备 commit 的时候。(git 中的时光机!)

此时你可以进行后悔操作,继续修改文件再 add,然后重新 commit,这时会提交一个新的 commit。

2)mixed 模式

回到一开始的时候,假如我们执行的是 git reset --mixed 98c27 ,它也会首先修改 HEAD 的指向,使得 HEAD 上的 commit 为 98c27

但还不够,git 还会接着更新你的暂存区,如同回到了你准备 add 的时候。(时光机再向前!)

Git - 在版本之间切换自如

对你来说可能更方便了,继续改就行,然后重新 add、commit。这实际上是 reset 的默认模式,等同于 git reset 98c27

3)hard 模式

不用我多说你可能已经意识到 hard 是干什么用的了。这一次 git 摧枯拉朽,把你的 HEAD、暂存区、工作区全给干掉了:

Git - 在版本之间切换自如

一下子回到了你开始写需求的时候。所以这个命令是 危险 的,除非你真的打算不要这些修改了,否则最好不要用。

不过即使你真的用了又后悔,那也是有办法的,在 git 里面,既然能回到过去,也能在过去穿越到未来。使用 git reflog 可以查看你最近的修改,找到最前面的 commit id,可以继续使用 reset 穿回去。

合并 commit

有时候你可能发现自己刚才提交的好几个 commit 其实都是中间状态,还不如把它们合并成一个。根据上面的 reset,实际上就能完成这件事情。

比如下面的场景,我们多提交了一个 File V1.1 的中间版本,希望将其从 commit 历史中去掉:

Git - 在版本之间切换自如

那其实可以直接 reset 到 v1 版本:

Git - 在版本之间切换自如

然后重新进行 commit,这样就会将 v1 之后的修改都提交到了新的版本,如同移除了中间的版本。

Git - 在版本之间切换自如

当然,这个场景也能用 rebase 解决,之后会提到。

挪动 commit

在多个分支上切换开发的时候,有时候会忘记切换分支就开始开发。当发现自己提交的 commit 放错分支怎么办呢?在 git 中,这也不算个事,通过 git cherry-pick 就能解决。

cherry-pick 可以将指定的 commit “摘到”当前的分支上面,git 会为你重新生成一个 commit,但内容与 pick 的 commit 一致。

Git - 在版本之间切换自如

如果你要 pick 好几个 commit,它们之间有依赖关系,那需要根据先后顺序依次进行 cherry pick。

当发生冲突时,此时需要修改文件解决冲突,可以使用 git cherry-pick --abort 放弃此次 pick,或者解决完 add 进暂存区,然后使用 git cherry-pick --continue 。注意这里并不是使用 git commit ,如果你需要改变 commit 的信息,可以使用 commit,否则 git 会默认使用 pick 的 commit 的信息。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

信息论基础

信息论基础

[美] Thomas M.Cover,Joy A.Thomas / 机械工业出版社 / 2005-5 / 56.00元

信息论基础,ISBN:9787111162452,作者:(美)Thomas M.Cover,(美)Joy A.Thomas著;阮吉寿,张华译一起来看看 《信息论基础》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

在线进制转换器
在线进制转换器

各进制数互转换器