如果你跟我一样,以前只会用 git add/commit/pull/push 这几个简单的命令(也就是至少你得用过git,如果没有使用过可以去看看廖雪峰的git教程猴子都能懂的git教程),或许这篇文章能够帮到你一丢丢

近期我终于(为什么要说终于?距离我注册Github已经4年了!)参与了一些开源项目,开源世界的人都很热心,很感谢在他们的帮助下学会这些

本文并非教程,可能讲的不会很系统,所以会出现想到哪里说哪里的情况

一切都从commit树说起

在此之前,我知道有 commit树,但从未将 保持commit树的整洁 放在心上

比如说:当我完成了一次 commit 之后,发现刚刚的 commit 有误,第一反应并不是将它撤销掉,而是通过下一个 commit 来修改它,或许从操作上来说,这么做也是允许的,但是这样势必会导致你的 commit 可读性变差,看起来更加混乱

当我交出自己的 PR 时,项目的管理者review后叫我把 commit 记录整理一下,整个人都是懵的,只好从删库 fork 重新开始(啊啊啊,简直就是黑历史,现在还在项目的 PR 列表里,我才不会告诉你是哪个项目……)

那难不成每次做错了都要重头来过吗?
当然不是!

git reset

这是一个能把你的项目带回过去的命令,能够使你的项目变为过去某个时间点的模样

使用 git log 命令查看提交历史,commit 后面那一长串就是你那次 commit 的id

分为两种:git reset --soft commit_idgit reset --hard commit_id

第一种:将你的 commit状态 回调到指定的时间点,但是保留你现在的代码,又记作 软reset
第二种:将你的一切都回调到指定的时间点,那时间点以后的代码更改、commit记录都会消失,记作 硬reset(但其实还是回得去的,这不是本文的重点,如果感兴趣可以去搜索 git reflog

但是这种方法有一个问题:无论是 软reset 还是 硬reset,使用了这种方法都会导致后面的 commit记录 丢失,所以通常不用于修改久远的 commit记录

git revert

命令很简单 git revert commit_id
这样就能撤销单个 commit 了,但是撤销本身就会成为一次 commit记录于是你就可以套娃使用,撤销、撤销之前的撤销……

emmm.... 反正我不喜欢这种方法

git rebase

这个命令功能不那么单一(分支合并、修改历史提交记录),通常在一些git教程中都不会放入入门的章节里去讲,你甚至可以完全避开它不使用,但在我眼里,他很重要,因为 它是保证 commit树 整洁必不可少的命令

这里先讲它作为修改历史提交记录部分的功能

语法:git rebase -i commit_id

使用这种方法,git 会直接将 指定commit 后的所有 commit 罗列出来,可以根据 git 的指示对 commit树 编辑(比如说 修改某次commit的信息撤回某次commit),操作本身也不会成为一次 commit记录

git rebase -i 能够做到 git revert 可以做到的事,反之则不行
这也是我更喜欢这种方式的原因

冲突与合并:merge/rebase

我认为诀窍就是在合并之前先 rebase(没错,还是它)

rebase 这个名字起的可真好,即 re(重新/变更)+base(基础/根基),每次的commit都会有它们的共同根基,合并前的两个分支也一样,它们都由一个共同的祖先通过不同的提交变成了不同的样子,通过 rebase 将你当前的代码的根基切换为你要合并的分支上去,达到移花接木的效果

假设有两个分支:master 和 dev,现在要将 dev 合并进 master 中

git checkout dev
git rebase master
处理冲突...
git checkout master
git merge dev

通过这种方式,而不是直接 merge 就能达到 保持 commit树 整洁 的效果

pull/fetch+rebase

push 之前,通常需要更新源码,以免和远程库发生冲突,其实本质上和分支合并是差不多的,但还是再说一下

以前的我肯定会无脑 git pull,然后再处理冲突
其实有种说法:pull = fetch + merge
然而 merge 可能会产生一个 merge commit,为了 保持 commit树 整洁

请务必使用:

git fetch origin
git rebase origin/your_branch

Q.E.D.


此 生 无 悔 恋 真 白 ,来 世 愿 入 樱 花 庄 。