转载 Git鲜为人知的四个命令:bisect,blame,reflog和提交范围

转载 Git鲜为人知的四个命令:bisect,blame,reflog和提交范围

虫虫搜奇

发布时间:18-04-2807:00科技达人,优质创作者

转载 Git鲜为人知的四个命令:bisect,blame,reflog和提交范围

现在每个人都在学编程,可能你就是正在成为百万码农大军中的一个。好吧,就算你没有,那么你也可能在你的日常工作中使用版本控制系统(也不是…好吧,那就忽略上面的两句话)。做版本管理最流行的工具是Git,什么,你是SVN!

好吧,遇到杠精,老子也是服了!

转载 Git鲜为人知的四个命令:bisect,blame,reflog和提交范围

关于Git你可能用过,也知道基础知识,比如git add,git commit,git push,git pull。你还懂其它的一些,比如git reset,git stash,git checkout。但是今天我们不讲这些,如果你对他们有兴趣,请给虫虫留言,或者自己去搜索好了。

今天我们要讲的是四个鲜为人知的git命令,知道的人很少,但是很有用。

1、Git bisect

如果你处于一个组织良好的开发环境中,这个命令对你来说几乎是无用的。但现实中的场景往是,你是宅在家里一个人做小型项目,或者你的公司还没要建立完善的CI/CD(持续的集成和发布)流程,更大的可能是你们的测试被狗吃了(不是@了狗的测试,虽然也差不多),并出现以下情况:
转载 Git鲜为人知的四个命令:bisect,blame,reflog和提交范围

试想,当你在愉快的开发着你想象中的炫酷功能的时候,突然销售妹纸(想得美,是个老爷们大胖子)给你打了一个紧急电话说你发布版本崩溃了,没法工作。你试着看了看代码和错误日志,但没有任何头绪,在qq群询问老司机后,老司机建议你找之前正常的版本,然后对比下。因此你现在问题是要找到引入错误的那次commite。该怎么做?什么,不会了把。

那么这里虫虫教你用二分法搜索来帮你寻找那次罪恶的commit。git提供了git bisect命令就是用来干这事的。

想法很easy。你要使用git log从历史中选择一个commit,那次commit之前的版本你确定是没有问题。于现在使用git bisect来执行一个二分搜索,找到引入错误的commit。如果听起来太抽象,我们来实例展示:

假设我这有一个项目,是关于记单词的,每天递增把陌生的单词存到github的项目中,以便以后复习和存储你学习生词的历史。

下面是git repo中的最新版本:
转载 Git鲜为人知的四个命令:bisect,blame,reflog和提交范围

我们开发了一个程序,getWord.pl,让他解析这个文件,并且每次随机的显示一个单词来显示。现在我们发现以下内容:

似乎似有些格式除了问题,使解析出问题了。现在需要通过每次提交逐一验证,找出引入错误的commit,这需要花费很长时间。幸运的是,我们有了法宝git bisect。

我们签出第一次提交并确认它是正确的(通常选择一个确认是没有问题commit你的)。为了方便起见,我们将第一次提交标记为one,将我们的最新提交标记为last:

让我们来启动查询:

1、git bisect start

git bisect good one

git bisect bad last
转载 Git鲜为人知的四个命令:bisect,blame,reflog和提交范围

这样git 会头指针指向两个版本之间的那次提交,这儿是08b9941
转载 Git鲜为人知的四个命令:bisect,blame,reflog和提交范围

2、执行脚本,测试是不是还报错,如果报错,继续搜索,这次只需输入git bisect bad

转载 Git鲜为人知的四个命令:bisect,blame,reflog和提交范围

3、如果测试正常了,输入git bisect good

重复2,3,根据结果输入git bisect bad或git bisect good,直到找到引入错误的确切那次commit。

转载 Git鲜为人知的四个命令:bisect,blame,reflog和提交范围

ok,剩下的就是,你修改此次提交中引入的问题,必要时候可以通过git show获得这次commit更多的信息(比如文件对比等)。
转载 Git鲜为人知的四个命令:bisect,blame,reflog和提交范围

在我们的例子中是有大量为未识别的符号,用?代替了,主要是音标符不支持在简单文本中显示导致的。

git bisect reset 返回之前的版本,把错误作为一个补丁进行修改即可。

注意git bisect也支持图形化界面搞git bisect visualize会运行gitk,让你更方便的选择commit。git bisect bad 和 git bisect good中的bad/good也可以用new/old取代。

2、Git blame

这可能是名单上最为人熟知的命令,但是我在git群里面问过很多人,都没听说过,所以就把他给加进来了。

Git blame用对你文件的每行信息都进行注释。你能看到关于该行修改的每一次commit的哈希标签、作者和提交日期。

以从我们从前面的仓库为例子:
转载 Git鲜为人知的四个命令:bisect,blame,reflog和提交范围

效果如上图,可以让我快速的浏览整个仓库的变化,提交时间,及作者,可以让你快速找到,谁修改的,谁的锅,骂丫的让丫来扛。

关于git blame 还有以下两点我们要牢记:

(1):如果一个提交哈希前面有^号,那么自该文件创建以来,相关联的行就没有被修改过。

(2):Git还可以跟踪跨文件的行内容变化。如果你要对一个大文件代码重构或者你的配置文件重新发布到多个小文件中时,git会,那么git会显示大文件中的原始提交和大文件的名称。你可通过-C选项来实现。

3、Git reflog

可能老司机警告过你要避免使用git reset。因为他是一种破坏性的操作。它不仅修改HEAD(–soft)和索引(–mixed),还可能修改你的工作目录。如果你不会使用它的话,会把工程搞乱,搞的不正常(怎么办,你说,老司机说了,乱了就从新clone一个呗,有啥大不了的)。

其实上虫虫告诉你,在某些情况下做hard reset是必不可少,没有啥好担心的,用git reflog中你可以看到所有的变化。比如你一不小心误删除了个文件,你想把它找回来,因为还有变化未提交,所以你必须想办法恢复回来。

reflog是一个本地结构,它记录了HEAD和分支引用在过去指向的位置。reflog信息没法与其他任何人共享,每个人都是自己特有的reflog。重要的一点是,它不是永久保存的,有一个可配置的过期时间,reflog中过期的信息会被自动删除。

我们来看看我们目前的提交历史。
转载 Git鲜为人知的四个命令:bisect,blame,reflog和提交范围

注意:git reset的作用是将当前分支(和HEAD隐式地)的指针移动到一个新的提交,如果使用–mixed(如果没有指定标志,这是缺省值),更新索引。如果使用—hard会更新索引以及工作目录。出于某种原因,我们不满最新的提交,因此我们将重置为前一个(你可以通过指定某种引用来重置为任何提交)

git reset HEAD^

git log --pretty=oneline
转载 Git鲜为人知的四个命令:bisect,blame,reflog和提交范围

我们注意到git日志输出中的最新提交已经没有了。但它并没有被删除,它只是在git log的等正常功能中不再可见,因为默认情况下它只是按照commit来继承延续的。
转载 Git鲜为人知的四个命令:bisect,blame,reflog和提交范围

现在突然意识到实际上我们reset的commit实际上是完美的,我要想希望恢复它。怎么恢复呢?那么我们可以使用git reset,并让我们的分支再次指向之前提交,但是我们如何获得对该提交的引用,因为它不再出现在git log中。ok这儿就需要git reflog大显身手了:
转载 Git鲜为人知的四个命令:bisect,blame,reflog和提交范围

上图中,我们感兴趣是前两项。从输出中我们可以发现HEAD指向哪个提交以及它现在指向的位置([email protected]{0})。因此,在我们可以直接reset所需的commit哈希标(即b30981a)前,将分支移回原来的状态。

注意这时候仓库的状态是落后于远程仓库的,所以一切pull push都会有问题。
转载 Git鲜为人知的四个命令:bisect,blame,reflog和提交范围

现在HEAD指向(由HEAD @ {0}指示)。因此,在我们可以直接使用所需的commit哈希(即b30981a)或其引用(即[email protected]{1}),将分支移回原来的状态。注意,不要重复提交引用,因为每次使用后引用指向都会变。reset重置仓库:

git reset [email protected]{1}
转载 Git鲜为人知的四个命令:bisect,blame,reflog和提交范围

正如我们所看到的,事情已经恢复正常。所以下次你因为git重置错误时候,不要恐慌,更不要做重新clone,你有reflog守护天使,可以帮你找回来。
转载 Git鲜为人知的四个命令:bisect,blame,reflog和提交范围

注意:我们前面提到过,默认情况下,git log只会按照commit继承链来显示。你可以通过-g/–walk-reflogs标志来让它按reflog链显示:

git log -g
转载 Git鲜为人知的四个命令:bisect,blame,reflog和提交范围

4、commit范围

最后虫虫要给说的是commit范围, 如何使用三种方法来指定提交的范围,每种方法都有其用途。在用于多分支管理时,它们特别方便。在我们深入研究之前,下面是我们将要研究的分支和commit树的图片。
转载 Git鲜为人知的四个命令:bisect,blame,reflog和提交范围

两个点(…)范围

我估计你用过这个,即使是你不完全知道它到底在做什么。它的典型用法是过滤从同一分支中commiy的范围内的日志,如下所示:
转载 Git鲜为人知的四个命令:bisect,blame,reflog和提交范围

我们用本节开始的图为例子说明则是:

git log --pretty=oneline B…F

F

D

我们注意到,他显示了不包括最左对象(B)的指定范围之间的提交,用数学中集合表示方法为(B,F]。我们把他尝试用于不同的分支,结果大家可以看看:

git log --pretty=oneline G…F

F

D

git log --pretty=oneline F…G

G

E

git log --pretty=oneline C…G

G

E

git log --pretty=oneline G…C

C

如果你还没有看出以上输出是基于什么规律的话,那么就让虫虫告诉你好了。使用这种表示法我们可以看出是不包含第一个commit范围符(左边…),但包含第二个commit(…右边)的分支。

请自己核对图片和例子,看看是不是这样的呢?欢迎留言说明。

还有一个很有用的用法是在要将你的变化提交到远程仓库之前(git服务器)执行下面的命令:

git log --pretty=oneline origin/master…HEAD

上述命令会输出你现在本地当前仓库和远程仓库之间的差异。

多分支范围

两个点的范围很酷,解决很大的问题。但是如果要想指定多个分支时,该怎么做?比如看到你想看到当前分支中没有其他分支的commit的?

实际上,两个点语法只是一种语法的缩写。如果你在给引用指定了^字符或者—not选项,那么git log将只显示那些不能从指定的commit引用到达的分支。比如,以下命令是等效的:

git log --pretty=oneline G…F

git log --pretty=oneline ^G F

git log --pretty=oneline F --not G

举一个多分支的例子则是这样的:

git log --pretty=oneline G --not H

F

G

三个点(…)范围

我们用于指定提交范围的最后一个方法是…符号。你可以说它是包含所有两个范围commit两边的分支。通常来说,它是由包含两个commit的所有提交和分支。

最后一个例子:

git log --pretty=oneline C…G

G

E

C

好了,虫虫今天要介绍的4个git命令都说完了,剩下的就是要你去理解和实操,多练练就都知道了。如果本文章请关注虫虫,并给我点赞。