如果你跟我一样,以前只会用 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_id 和 git 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.

