百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

问题排查神器 - Git Bisect 命令实战分享

wptr33 2025-01-16 21:12 25 浏览

摘要:前段时间 Git 发布正式版本 2.33.0 遇到一个客户端与服务端不兼容的一个问题,在排查问题的过程中又一次用到了 Git bisect 命令,解决问题的同时结合近期的一些认知,又刷新了自己对 bisect 命令的认识,bisect 可以结合一些场景发挥其妙用,借此分享下 bisect 命令以及 bisect 命令的一些其它使用方式的思考。


背景

Git 于 8 月 17 号发布了 2.33.0 正式版本,本次的发布内容可以参见:

https://gitee.com/mirrors/git/blob/master/Documentation/RelNotes/2.33.0.txt

不幸的是,在 2.33.0 版本发布没多久,就有用户反馈 2.33.0 版本的 Git 在使用 SSH 通信协议的时候无法进行正常的 Clone/Fetch/Push 操作,具体的现象如下:

Cloning into 'xxxxxx'...
fetch-pack: unexpected disconnect while reading sideband packet
fatal: early EOF
fatal: fetch-pack: invalid index-pack output

看到这个提示第一时间就想到 Git 2.33.0 的更新日志里面好像是有关于 Fetch 和 Sideband 相关的更新,于是前往更新日志发现了如下的两个信息:

 * "git fetch" over protocol v2 left its side of the socket open after
   it finished speaking, which unnecessarily wasted the resource on
   the other side.
   (merge ae1a7eefff jk/fetch-pack-v2-half-close-early later to maint).


 * The side-band demultiplexer that is used to display progress output
   from the remote end did not clear the line properly when the end of
   line hits at a packet boundary, which has been corrected.

但是,为了进一步定位此问题的源头,方便精准的定位问题,所以使用了 Git Bisect 命令进行测试,找到具体的原因,才能够更好的去排查分析问题并加以解决。

Git Bisect 介绍

git bisect 官方文档:
https://git-scm.com/docs/git-bisect

git bisect 命令的作用是使用二分查找法找到具体引起问题的 Commit。

上图引自阮一峰的网络日志:
http://www.ruanyifeng.com/blog/2018/12/git-bisect.html

简单来说就是我们给到 bisect 命令一个范围,它会自动的帮我们确认当前范围的中点,在这个中点上进行测试,并且告诉它这是一个好的提交(good commit)还是一个坏的提交(bad commit),进而来缩小查找范围,通过二分查找,我们就可以很快的定位到出问题的 Commit 以方便我们针对性的解决问题。

问题排查

由于我们已经确定了这个问题是 Git 2.33.0 版本引起的(因为测试了 Git 2.32.0没问题呀 :D),所以在使用 bisect 命令的时候,这个版本范围就是v2.32.0 ~ v2.33.0,我们只要找到具体引入这个问题的提交,就能够更精准的定位问题,从而避免盲目的猜测和常识。

进入 bisect 模式

我们下载 Git 最新源码,并且使用 git bisect start 命令来进入 bisect 模式

git bisect start 命令指定了一个范围,并且 bisect 命令告诉我们,在这两个版本之间一共有304个提交,大概需要8步就可以定位到具体的 commit,这就是二分查找的好处。

开始第一次测试

我们使用 make 命令进行 Git 源码编译构建,编译构建完成后,就可以使用 Git 提供的一个 wrapper 进行 Git 命令的调用,这里我们可以加上 -j4 参数来增加编译构建的速度。命令完成后,我们就可以使用 ./bin-wrappers/git 来进行测试:

哦吼,还是不行,那么我们可以确定导致问题出现的 Commit 是出现在这次提交之后的提交里面。当然,这些不用我们自己来去记,使用 git bisect bad 命令标记即可:

标记完成后 Git 就告诉我们,接下来还有大概7次的测试就可以定位到引发问题的 Commit,如果遇到的提交是可以通过的,那么需要使用 git bisect good 命令标记。

自动化的 bisect

以上的步骤我们只需要重复的进行即可,但是很明显根本不需要人为的去跟进,作为一个新生代农民工,我们需要能够自动化可自动化的一切,所以一方面是为了省时间,另一方面是为了避免人为的失误,我们可以使用 git bisect run 命令来自动化的进行执行。

git bisect run my_script arguments

bisect 命令是以 my_script 脚本的返回值来确定当前的提交是好是坏,来看看官方的介绍:

Note that the script (my_script in the above example) should exit with code 0 if the current source code is good/old, and exit with a code between 1 and 127 (inclusive), except 125, if the current source code is bad/new.

简单来说就是:

  • 如果以 0 值退出,说明当前版本是好的
  • 如果以 1~124 126 127退出,说明当前版本是坏的
  • 如果以 125 退出,说明编译构建有问题

所以我们来根据这个规则写个脚本,但首先我们需要看看 Clone 失败的返回值是多少

OK, let's do it via Shell Scripts ~~~

#!/bin/zsh


# 每次将 Clone 的代码放到以当前 CommitID 为名称的目录,避免冲突
make -j4 && ./bin-wrappers/git clone git@gitee.com:/kesin/taskover.git `git log -n 1 --pretty=format:"%H"`
s=$?
if [ $s -eq 0 ]; then # 正常
  exit 0;
elif [ $s -eq 128 ]; then # 失败
  exit 1;
else # 其他情况,此处只是测试,建议针对不同的情况更加严谨点 XD
  exit 128;
fi

然后我们来执行这个脚本自动的进行二分查找定位:

这个时候 bisect 命令就会自动的执行编译构建和 Clone 过程,并且根据返回值自动的确定 Commit 的范围:

这里是 bisect run 命令根据我们写的脚本自动的判定当前提交是一个坏提交,进而自动的进行下一步。当然,在二分查找的过程中也会遇到好的提交,bisect 命令将会根据我们提供的返回值自动的缩小范围:

最终,bisect 将会为我们定位到第一个出现此问题的提交:

ae1a7eefffe60425e6bf6a2065e042ae051cfb6c is the first bad commit

并且 bisect 命令也把这个导致此问题的提交的详细信息打印了出来,接下来我们就可以根据这个提交相关的改动来分析我们的问题。

分析并解决问题

上面我们使用 bisect 命令找到出问题的提交:
https://gitee.com/mirrors/git/commit/ae1a7eefffe60425e6bf6a2065e042ae051cfb6c

/*
 * this is the final request we'll make of the server;
 * do a half-duplex shutdown to indicate that they can
 * hang up as soon as the pack is sent.
 */
close(fd[1]);
fd[1] = -1;

分析下提交的变更我们可以知道,这次变更主要是为了优化网络连接的占用,Git V2 via SSH 在传输的过程中,当客户端接收完数据后,还需要进行一系列的本地操作,这个操作过程已经不需要再维持跟服务端的链接了,所以需要在客户端发送完数据后就给服务端发送 FIN,进入半双工状态,等待服务端发送完数据后关闭连接即可,而无需经过漫长的本地操作后才关闭连接,从而避免了不必要的网络资源占用。

知道问题的原因之后,我们分析 Gitee 的 SSH 分发代理,发现我们的 SSH 代理在接收到客户端的 FIN 之后马上就会关闭这个 SSH 链接,进而导致上面的问题:客户端还没有接收完数据链接就提前断开了

解决的方式也很简单,在收到客户端的 FIN 之后不马上进行网络连接的关闭,而是等数据发送完之后才进行关闭。

Git Bisect 使用的思考

现在的组织都在推崇提升研发效能,推崇 DevOps 文化及工具,是不是可以把 Bisect 这种逻辑用到整个流程中呢?

比如在自动化测试中,我们遇到一些测试不通过的 Case 的时候是直接失败的,虽然告知了具体的用例以及相关的输入输出,但是如果能够通过 Bisect 命令自动的找出第一个出现此问题的提交,那么对于组织无疑是有价值的:

  • Case 不通过的同时直接给出了 Bad Case 的 Owner,精准通知,快速修复
  • 在集中测试中,可以避免过度的通知,从而干扰到本来无关的人员
  • 避免问题不明确,相互推诿,产生不良情绪,影响团队气氛
  • ...

关于算法思想的思考

Git Bisect 所采用的二分查找思想我们耳熟能详,在 Git 源码中,同样采用二分查找算法的地方还有 Git Pack idx 文件的查找,通过精妙的扇区划分,加上二分查找算法快速定位到object的偏移量。除此之外,Git 中还采用了 SHA-1 Hash 算法、不同的 Diff 算法、大量的递归等。

/* hash-lookup.c */
int bsearch_hash(const unsigned char *hash, const uint32_t *fanout_nbo,
         const unsigned char *table, size_t stride, uint32_t *result)
{
    uint32_t hi, lo;


    hi = ntohl(fanout_nbo[*hash]);
    lo = ((*hash == 0x0) ? 0 : ntohl(fanout_nbo[*hash - 1]));


    while (lo < hi) {
        unsigned mi = lo + (hi - lo) / 2;
        int cmp = hashcmp(table + mi * stride, hash);


        if (!cmp) {
            if (result)
                *result = mi;
            return 1;
        }
        if (cmp > 0)
            hi = mi;
        else
            lo = mi + 1;
    }


    if (result)
        *result = lo;
    return 0;
}

但是在实际编码的过程中,有多少开发者能够拍着胸脯说:在编码过程中,我脑中有模型,心中有算法,能够以高效的方式、采用合理的逻辑去编写代码。

这个答案我想不言自明吧。

研究开源项目是一个很好的学习路径,能够从大量优秀的代码中学习到优秀的实践和思想,从研究学习到参与贡献,相比于这个过程,结果倒显得不那么重要了。

最后

善用工具,乐于思考,多多来 Gitee(https://gitee.com )学习研究开源项目并贡献代码。

相关推荐

MySQL进阶五之自动读写分离mysql-proxy

自动读写分离目前,大量现网用户的业务场景中存在读多写少、业务负载无法预测等情况,在有大量读请求的应用场景下,单个实例可能无法承受读取压力,甚至会对业务产生影响。为了实现读取能力的弹性扩展,分担数据库压...

Postgres vs MySQL_vs2022连接mysql数据库

...

3分钟短文 | Laravel SQL筛选两个日期之间的记录,怎么写?

引言今天说一个细分的需求,在模型中,或者使用laravel提供的EloquentORM功能,构造查询语句时,返回位于两个指定的日期之间的条目。应该怎么写?本文通过几个例子,为大家梳理一下。学习时...

一文由浅入深带你完全掌握MySQL的锁机制原理与应用

本文将跟大家聊聊InnoDB的锁。本文比较长,包括一条SQL是如何加锁的,一些加锁规则、如何分析和解决死锁问题等内容,建议耐心读完,肯定对大家有帮助的。为什么需要加锁呢?...

验证Mysql中联合索引的最左匹配原则

后端面试中一定是必问mysql的,在以往的面试中好几个面试官都反馈我Mysql基础不行,今天来着重复习一下自己的弱点知识。在Mysql调优中索引优化又是非常重要的方法,不管公司的大小只要后端项目中用到...

MySQL索引解析(联合索引/最左前缀/覆盖索引/索引下推)

目录1.索引基础...

你会看 MySQL 的执行计划(EXPLAIN)吗?

SQL执行太慢怎么办?我们通常会使用EXPLAIN命令来查看SQL的执行计划,然后根据执行计划找出问题所在并进行优化。用法简介...

MySQL 从入门到精通(四)之索引结构

索引概述索引(index),是帮助MySQL高效获取数据的数据结构(有序),在数据之外,数据库系统还维护者满足特定查询算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构...

mysql总结——面试中最常问到的知识点

mysql作为开源数据库中的榜一大哥,一直是面试官们考察的重中之重。今天,我们来总结一下mysql的知识点,供大家复习参照,看完这些知识点,再加上一些边角细节,基本上能够应付大多mysql相关面试了(...

mysql总结——面试中最常问到的知识点(2)

首先我们回顾一下上篇内容,主要复习了索引,事务,锁,以及SQL优化的工具。本篇文章接着写后面的内容。性能优化索引优化,SQL中索引的相关优化主要有以下几个方面:最好是全匹配。如果是联合索引的话,遵循最...

MySQL基础全知全解!超详细无废话!轻松上手~

本期内容提醒:全篇2300+字,篇幅较长,可搭配饭菜一同“食”用,全篇无废话(除了这句),干货满满,可收藏供后期反复观看。注:MySQL中语法不区分大小写,本篇中...

深入剖析 MySQL 中的锁机制原理_mysql 锁详解

在互联网软件开发领域,MySQL作为一款广泛应用的关系型数据库管理系统,其锁机制在保障数据一致性和实现并发控制方面扮演着举足轻重的角色。对于互联网软件开发人员而言,深入理解MySQL的锁机制原理...

Java 与 MySQL 性能优化:MySQL分区表设计与性能优化全解析

引言在数据库管理领域,随着数据量的不断增长,如何高效地管理和操作数据成为了一个关键问题。MySQL分区表作为一种有效的数据管理技术,能够将大型表划分为多个更小、更易管理的分区,从而提升数据库的性能和可...

MySQL基础篇:DQL数据查询操作_mysql 查

一、基础查询DQL基础查询语法SELECT字段列表FROM表名列表WHERE条件列表GROUPBY分组字段列表HAVING分组后条件列表ORDERBY排序字段列表LIMIT...

MySql:索引的基本使用_mysql索引的使用和原理

一、索引基础概念1.什么是索引?索引是数据库表的特殊数据结构(通常是B+树),用于...