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