内容简介: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
示意图如下:
时间轴是从左往右,但是 commit 的指向正好相反,因为每个新创建的 commit,都以前一个 commit 为父节点,所以指向前一个 commit。
每一个 commit 都包含了 当时版本的文件快照 ,也就是说,切换到某一个 commit,就能回到对应的版本上去。
分支
所以,我们如何知道自己在哪个 commit 上?一般来说,我们都是工作在分支上的,比如当一个项目初始化时,都默认有一个 master 分支, 分支本质上仅仅是指向提交对象的可变指针 。比如如下的 master 分支上,README 已经是 v3 版本了:
当在 master 分支上 checkout 一个分支出来时,仅仅是创建了一个新的指针,指向了当前的 commit。
所以此时 master 分支上和 feature 分支上的内容是一样的。
我们接着 commit,然后 master 的指针就会指向新的 commit:
为什么不是 feature 的指针改变?换句话说,git 怎么知道我们处在哪个分支?这是因为有一个特殊的指针 HEAD
,它指向当前所在的本地分支:
刚才创建分支使用的是 git branch
命令,它只是创建一个分支,并不会自动切换过去,使用 git checkout
命令使得 HEAD
指向特定的分支:
继续 commit,feature 将往前移动:
这时可以发现两个分支开始分叉了,也就是说这两个分支基于同一个版本分别做了不同的改动。
三个集合
要理解如何操纵 commit,还需要理解 git 中的三个集合。在 git 中,文件有三种状态:已修改(modified)、已暂存(staged)和已提交(committed),它们分别对应于三个区域: 工作区 (working directory)、 暂存区 (staging area) 和 版本库 (repository)。
git 通过比较三个区域的内容,来提示用户需要做的操作。比如工作区与暂存区不同,意味着你需要 add;暂存区与版本库不同,意味着你已经 add 但尚未 commit。
我们知道,基本的 Git 工作流程如下:
- 在 工作目录 中修改文件。
- 暂存文件(add),将文件的快照放入 暂存区域 。
- 提交更新(commit),找到暂存区域的文件,将快照永久性存储到 Git 仓库目录 。
假设我们处在 feature 分支,当我们进行以上操作后,commit 与三个区域的内容变化分别如下:
从左到右分别是工作区、暂存区和版本库,版本库上面标注了 HEAD,这表示我们展示的是 HEAD 指针指向的内容,也就是当前分支所在的 commit。
接下来对工作区内的文件进行修改,换句话说就是修改磁盘上的文件:
修改磁盘上的文件只是改变了工作区的内容,接下来使用 git add
命令将修改提交到暂存区:
将修改提交到暂存区并不会新增一个 commit,接下来通过 git commit
来提交:
此时新的 commit 被创建,HEAD 指向的 commit 相应发生改变。
有了上面这些 git 操作的印象后,解释如何切换版本就容易多了。
Git 中的撤销
撤销可能是使用过程中最需要的操作,你可能在任何时候都需要撤销。根据情况不同,撤销的命令也是不同的。
撤销最近几次 commit
要撤销最近几次的提交,可以使用 git reset
,下面介绍它的三种模式: soft
、 mixed
和 hard
。
假设目前分支情况如下,我们需要撤销到 98c27
commit 上去。
1)soft 模式
执行 git reset --soft 98c27
,git 会首先修改 HEAD 的指向,它会连带修改 HEAD 所在分支的指向:
如上图所示,现在的暂存区和 HEAD 是不同的,这个操作本质上撤销了 2c9be
这个 commit,如同回到了上次准备 commit 的时候。(git 中的时光机!)
此时你可以进行后悔操作,继续修改文件再 add,然后重新 commit,这时会提交一个新的 commit。
2)mixed 模式
回到一开始的时候,假如我们执行的是 git reset --mixed 98c27
,它也会首先修改 HEAD 的指向,使得 HEAD 上的 commit 为 98c27
。
但还不够,git 还会接着更新你的暂存区,如同回到了你准备 add 的时候。(时光机再向前!)
对你来说可能更方便了,继续改就行,然后重新 add、commit。这实际上是 reset 的默认模式,等同于 git reset 98c27
。
3)hard 模式
不用我多说你可能已经意识到 hard 是干什么用的了。这一次 git 摧枯拉朽,把你的 HEAD、暂存区、工作区全给干掉了:
一下子回到了你开始写需求的时候。所以这个命令是 危险 的,除非你真的打算不要这些修改了,否则最好不要用。
不过即使你真的用了又后悔,那也是有办法的,在 git 里面,既然能回到过去,也能在过去穿越到未来。使用 git reflog
可以查看你最近的修改,找到最前面的 commit id,可以继续使用 reset 穿回去。
合并 commit
有时候你可能发现自己刚才提交的好几个 commit 其实都是中间状态,还不如把它们合并成一个。根据上面的 reset,实际上就能完成这件事情。
比如下面的场景,我们多提交了一个 File V1.1 的中间版本,希望将其从 commit 历史中去掉:
那其实可以直接 reset 到 v1 版本:
然后重新进行 commit,这样就会将 v1 之后的修改都提交到了新的版本,如同移除了中间的版本。
当然,这个场景也能用 rebase 解决,之后会提到。
挪动 commit
在多个分支上切换开发的时候,有时候会忘记切换分支就开始开发。当发现自己提交的 commit 放错分支怎么办呢?在 git 中,这也不算个事,通过 git cherry-pick
就能解决。
cherry-pick 可以将指定的 commit “摘到”当前的分支上面,git 会为你重新生成一个 commit,但内容与 pick 的 commit 一致。
如果你要 pick 好几个 commit,它们之间有依赖关系,那需要根据先后顺序依次进行 cherry pick。
当发生冲突时,此时需要修改文件解决冲突,可以使用 git cherry-pick --abort
放弃此次 pick,或者解决完 add 进暂存区,然后使用 git cherry-pick --continue
。注意这里并不是使用 git commit
,如果你需要改变 commit 的信息,可以使用 commit,否则 git 会默认使用 pick 的 commit 的信息。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Egret场景切换管理类切换和单例使用方法
- Spring项目中使用两种方法动态切换数据源,多数据源切换
- Pear Admin Ant 1.1.0.Release 正式发布,新增布局切换、主题切换、工作空间
- MySQL -- 主从切换
- Android自动切换夜间主题
- 动态切换数据源原理
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。