One article to figure out how to support Hash-Based Bisect debugging in Go packages

Permanent link to this article– https://ift.tt/rF6CnVu

bisect is an English verb meaning “to bisect” or “to divide into two parts”. In mathematics and computer science, it usually refers to dividing an interval or a set into two equal parts.

For programmers, there are no more familiar bisect applications than the following two:

  • Binary search in algorithms

Bisection lookup is a classic and efficient lookup algorithm, and any book on data structures or computer algorithms will contain a systematic description of bisection lookup. A bisect search finds the target value by continually bisecting the search interval into two. Some sorting algorithms also apply the idea of bisect, such as QuickSort.

  • git bisect

git bisect is a very useful Git command that helps developers quickly locate commits that introduce errors by narrowing down the commits that may cause errors in a bisect way. It works by repeatedly checking out different commits from the version control system and running tests, labeling the results as “good” or “bad”. This process continues until the specific commit that introduced the bug is found (bad commit):

git bisect is particularly useful when you suspect that a bug is due to a specific change in the history of the codebase, which is very common in day-to-day development.

However, not all bugs can be found with git bisect. Especially in compilers, runtime libraries, and large, complex projects, problems often lurk in call stacks, data flows, or code paths that are difficult to troubleshoot. In these cases, traditional tools such as git bisect may be out of their depth.

Note: If you’re not familiar with using git bisect, you can refer to the Getting Started example in the appendix later in this article.

In July of this year, Russ Cox, former technical director of the Go team, published a post on his blog titled “Hash-Based Bisect Debugging in Compilers and Runtimes” article on Hash-Based Bisect, an advanced debugging technique used internally by the Go compiler and runtime teams. this technique provides a new way of locating problems.

In this post, I’m going to take you on a deep dive into the advanced debugging technique of Hash-Based Bisect, explore how we can get our own Go packages to support this debugging technique, and how it can help us quickly pinpoint some of the hard-to-troubleshoot potential problems in our day-to-day development.

1. Hash-Based Bisect是什么

As mentioned earlier, git bisect is often used to troubleshoot commit history regressions. However, git bisect is not useful when the problem is not caused by commit history, but by dynamic changes in program behavior. For example:

  • Certain code paths or optimization rules trigger errors at specific runtimes.
  • The test program behaves abnormally on certain paths in the call stack.
  • Problems caused by runtime scheduling in multithreaded or parallel execution.

Hash-Based Bisect is designed to solve these problems. It breaks through the limitations of the static version and extends debugging to the dynamic behavioral level.

So what exactly is Hash-Based Bisect technique? It is a hash-based and bisect search debugging technique designed to quickly locate the smallest set of change points in a complex program that cause problems. By generating unique hash values for change points in the code (e.g., functions, line numbers, or call stacks), the technique maps program behavior to these identifiers. The problem is then recursively narrowed down by incrementally enabling or disabling specific changepoints in conjunction with the results of running the test program, ultimately pinpointing the root cause of the problem (a certain number of lines of code or even a specific line of code):

Unlike git bisect, which focuses on finding commits that introduce errors, theInstead of traversing the version history, hash-based bisect operates directly on the structure of the code and execution flow, and the results of its debugging will not be related to a specific commit, but rather to the interaction of the code with a specific execution path or function, i.e., thePinpoint specific lines of code, function calls, and even call stacks that trigger failures

Let’s explain more closely how the technology works.

2. How Hash-Based Bisect works

The core of Hash-Based Bisect is the use of hash values to assign unique identifiers to program change points (e.g., functions, lines of code, call stacks, etc.) and to progressively narrow down the scope of the problem by means of a bisection search algorithm. It locates the smallest set of changes that cause a problem by dynamically enabling or disabling these change points and determining whether the problem has been triggered in conjunction with test results.

There are two key elements to this approach:

  • Unique identification of change points

In Russ Cox’s article, he mentions some traditional bisecting methods such as List-Based Bisect-Reduce, Counter-Based Bisect-Reduce, etc., but these methods suffer from unstable numbering order, difficulty in debugging multiple change points, limited scalability, and unsuitability for concurrent or dynamic scenarios.

Instead, hash functions are used to generate changepoint identifiers, ensuring that changepoint identifiers are always unique and stable, regardless of the code execution order, environment, or concurrency. The input is also more concise, avoiding long lists or complex numbering through short hash patterns (e.g., 001+110), and can be adapted to a wide range of problem types (optimization rules, runtime behavior, dynamic call stacks, etc.).

  • binary search

Dynamically enabling and disabling changepoints at runtime using a binary search algorithm efficiently narrows down the problem and reduces the complexity of needing to manually troubleshoot.

Below we go through a typical workflow of Hash-Based Bisect to further understand its principle.

firstlyDefining change points

Abstract the change points in the program that could cause problems, for example:

  • Functions (function name, file path)
  • Line of code (file path and line number)
  • Call stack (runtime capture)

Next.Generate a unique hash of the change points

Take Go’s currenthash-based bisect工具and Go packages that support debugging with this tool, for example, for each change point, the Go package needs to generate a hash value via the bisect.Hash method, which is used to uniquely identify it. For example:

id := bisect.Hash("foo.go", 10) // 生成foo.go文件第10行的唯一标识。 

Step three.Automated recursive testing using binary search.. Specifically, change points are enabled or disabled incrementally through a dichotomous search:

  • Enable a collection of changepoints, run the test program, and observe if it triggers a problem.
  • Narrow down the range based on the test results and continue the recursion until the minimum set of change points is found.

Finally.Reporting change points, i.e., the final output of the smallest set of changes that led to the problem, helps the developer to quickly localize the problem.

Russ Cox’s article gives an example of “a compilation optimization rule for a function that causes the test to fail” for a set of mathematical functions:

add, cos, div, exp, mod, mul, sin, sqr, sub, tan 

The first step to debugging for this problem scenario using hash-based bisect is toDefine the function change point, and generates unique hash identifiers for each change point:

add: 00110010 
cos: 00010000 
sin: 11000111 
... 

The bisect search is then enabled, and the Hash-Based Bisect tool is used to disable the optimization of certain functions in turn, gradually narrowing down the scope. Example:

第一步:禁用add, cos, div, exp, mod,测试通过。 
第二步:禁用mul, sin, sqr, sub, tan,测试失败。 
第三步:进一步细分,最终定位sin为导致问题的函数。开发者只需检查该函数的优化规则即可解决问题。 

In the original article, Russ Cox constructed a binary tree (below) using bitwise suffixes of function change-point hashes and used differences in suffix patterns for problem localization:

Pic from Russ Cox’s blog

After understanding the general workings, let’s take a look at the current state of Hash-Based Bisect usage in the Go project.

3. Current status of the use of Hash-Based Bisect in the Go project

Currently Hash-Based Bisect has become one of the important debugging tools for the compiler and runtime of Go projects, and its toolchain (golang.org/x/tools/cmd/bisect) and libraries (golang.org/x/tools/internal/bisect) provide powerful feature support to help the Go teams quickly locate problems in scenarios such as compiler development, runtime library upgrades, and language feature modifications.

The Go implementation of the hash-based bisect debugging technique consists of two parts:

The bisect command line tool can be used to drive test runs (e.g., go test) and automate the debugging process, with support for flexible mode definitions (e.g., -godebug, -compile options), combined with user input to locate trouble spots.

  • golang.org/x/tools/internal/bisect包

This package provides an interface for library and tool developers to easily realize the integration with bisect tools. And provides hash generation, enablement judgment and change point reporting and other functions to adapt to complex debugging needs.

The above tools are currently used in SSA (Static Single Assignment) backend development for Go compilers, Go runtime library upgrades (such as theNew Implementation of Timer Stop/Reset for Go 1.23) and modifications to language features (such asloopvar semantic change), etc. have important applications, greatly improving the debugging efficiency of the Go team in locating complex problems.

The above tools and packages have evolved in the Go project over the years and are quite mature.Russ Cox has initiated proposal #67140.The purpose of this project is to publish the golang.org/x/tools/internal/bisect package as a standard library debug/bisect package, so that compilers, runtimes, standard libraries, and even packages outside of the standard library can achieve compatibility with the bisect tool based on the functionality provided by it and utilize the bisect tool to achieve advanced debugging based on the hash value of the change point. and use the bisect tool to realize advanced debugging based on the hash value of the change point.

Are you feeling “impatient” in front of the screen? Such a great tool! Can we use it now? Is it possible to apply it to the debugging process of our own Go packages? Next, I’ll use an example to demonstrate how we can make our own packages support the Go bisect tool to help us improve debugging efficiency.

4. Make your library support Hash-Based Bisect debugging

To utilize the bisect debugging technique, we first need to solve the problem that the bisect package is located in the internal, the good thing is that Russ Cox in the implementation of the bisect package to consider this problem, the bisect package does not have any external dependencies, even the Go standard library do not rely on, which avoids the subsequent change to debug/bisect leads to the standard library loop dependencies. Now we can just copy it and use it in our own project.

Here is the directory structure of the example I prepared:

$tree -F hash-based-bisect/bisect-demo 
hash-based-bisect/bisect-demo 
├── bisect/ 
│   └── bisect.go 
├── foo/ 
│   ├── foo.go 
│   └── foo_test.go 
└── go.mod 
 

The bisect.go in the bisect directory comes from github.com/golang/tools/blob/master/internal/bisect/bisect.go, and the foo package is the target package we’re going to debug this time, so let’s take a look at the code in foo.go:

// bisect-demo/foo/foo.go 
 
package foo 
 
import ( 
    "bisect-demo/bisect" 
    "flag" 
) 
 
var ( 
    bisectFlag = flag.String("bisect", "", "bisect pattern") 
    matcher    *bisect.Matcher 
) 
 
// Features represents different features that might cause issues 
const ( 
    FeatureRangeIteration  = "range-iteration"  // Using range vs classic for loop 
    FeatureConcurrentLogic = "concurrent-logic" // Adding concurrent modifications 
) 
 
func Init() { 
    flag.Parse() 
    if *bisectFlag != "" { 
        matcher, _ = bisect.New(*bisectFlag) 
    } 
} 
 
func ProcessItems(items []int) []int { 
    result := make([]int, 0, len(items)) 
 
    // First potential problematic change: different iteration approach 
    id1 := bisect.Hash(FeatureRangeIteration) 
    if matcher == nil || matcher.ShouldEnable(id1) { 
        if matcher != nil && matcher.ShouldReport(id1) { 
            println(bisect.Marker(id1), "enabled feature:", FeatureRangeIteration) 
        } 
        // Potentially problematic implementation using range 
        for i := range items { 
            result = append(result, items[i]*2) 
        } 
    } else { 
        // Correct implementation using value iteration 
        for _, v := range items { 
            result = append(result, v*2) 
        } 
    } 
 
    // Second potential problematic change: concurrent modifications 
    id2 := bisect.Hash(FeatureConcurrentLogic) 
    if matcher == nil || matcher.ShouldEnable(id2) { 
        if matcher != nil && matcher.ShouldReport(id2) { 
            println(bisect.Marker(id2), "enabled feature:", FeatureConcurrentLogic) 
        } 
        // Potentially problematic implementation with concurrency 
        for i := 0; i < len(result); i++ { 
            go func(idx int) { 
                result[idx] += 1 // Race condition 
            }(i) 
        } 
    } 
 
    return result 
} 

One can understand the above code in the context of the typical workflow of Hash-Based Bisect mentioned earlier.

First, we model the two functional features that may cause the problem and define the change point, which is identified by the hash value of the feature identifier, which we define here:

const ( 
    // 使用有意义的特性名称作为 hash 的输入 
    FeatureRangeIteration  = "range-iteration"  // 使用 range vs 经典 for 循环 
    FeatureConcurrentLogic = "concurrent-logic" // 添加并发修改逻辑 
) 

Next, the same pattern is followed for each potentially problematic change point:

// 1. 计算特性的唯一Hash值 
id1 := bisect.Hash(FeatureRangeIteration) 
 
// 2. 检查是否应该启用该特性 
if matcher == nil || matcher.ShouldEnable(id1) { 
    // 3. 如果需要,报告该特性被启用 
    if matcher != nil && matcher.ShouldReport(id1) { 
        println(bisect.Marker(id1), "enabled feature:", FeatureRangeIteration) 
    } 
 
    // 4. 执行可能有问题的实现 
    for i := range items { 
        result = append(result, items[i]*2) 
    } 
} else { 
    // 5. 执行正确的实现 
    for _, v := range items { 
        result = append(result, v*2) 
    } 
} 

The check for matcher == nil is a small optimization: when not in bisect debug mode, the matcher is nil, so we can just enable all the features without having to compute the hash and call other methods.

The code ShouldEnable() determines whether the code enables the feature, and ShouldReport() determines whether the feature needs to be reported as enabled. These two may return different values, especially when bisect searches for the minimum set of failures.

Marker() is used to generate standard-format matching markers that will be used by the bisect tool to identify and track which features are enabled, the markers will be removed in the final output and only the actual description text will be displayed.

There is also a setting to receive the bisect pattern, we receive the pattern that bisect passes to the foo package via a command line argument. We call Parse in the Init function instead of the init function because calling Parse in the init function will interfere with the go test test framework and cause a test execution error such as test execution error like “flag provided but not defined: -test.paniconexit0”.

Here is the code for foo_test.go:

// bisect-demo/foo/foo_test.go 
 
package foo 
 
import ( 
    "flag" 
    "testing" 
    "time" 
) 
 
func TestMain(m *testing.M) { 
    flag.Parse() 
    Init() 
    m.Run() 
} 
 
func TestProcessItems(t *testing.T) { 
    input := []int{1, 2, 3, 4, 5} 
    result := ProcessItems(input) 
 
    // Wait for all goroutines to complete 
    time.Sleep(1000 * time.Millisecond) 
 
    // Verify results 
    if len(result) != len(input) { 
        t.Fatalf("got len=%d, want len=%d", len(result), len(input)) 
    } 
 
    // Check if results are correct 
    for i, v := range input { 
        expected := v * 2 
        if result[i] != expected { 
            t.Errorf("result[%d] = %d, want %d", i, result[i], expected) 
        } 
    } 
} 

Obviously in order for the foo package to successfully fetch the command line arguments, we have rewritten TestMain to call the foo.Init function in it.

Next, let’s execute the bisect tool to debug the foo package. You can install bisect via go install golang.org/x/tools/cmd/bisect@latest. In addition, the PATTERN in the bisect command line below is a ” placeholder” in the bisect command line below. The bisect command recognizes this “placeholder” and replaces it with the appropriate string, as you will see during the execution of bisect:

// 在hash-based-bisect/bisect-demo/foo目录下执行 
 
$bisect -v go test -v -args -bisect=PATTERN 
bisect: checking target with all changes disabled 
bisect: run: go test -v -args -bisect=n... ok (0 matches) 
bisect: matches: 
bisect: run: go test -v -args -bisect=n... ok (0 matches) 
bisect: matches: 
bisect: checking target with all changes enabled 
bisect: run: go test -v -args -bisect=y... FAIL (2 matches) 
bisect: matches: 
[bisect-match 0xcf0b8943315a7804] enabled feature: range-iteration 
[bisect-match 0x4d642a7960e4693f] enabled feature: concurrent-logic 
bisect: run: go test -v -args -bisect=y... FAIL (2 matches) 
bisect: matches: 
[bisect-match 0xcf0b8943315a7804] enabled feature: range-iteration 
[bisect-match 0x4d642a7960e4693f] enabled feature: concurrent-logic 
bisect: target succeeds with no changes, fails with all changes 
bisect: searching for minimal set of enabled changes causing failure 
bisect: run: go test -v -args -bisect=+0... ok (1 matches) 
bisect: matches: 
[bisect-match 0xcf0b8943315a7804] enabled feature: range-iteration 
bisect: run: go test -v -args -bisect=+0... ok (1 matches) 
bisect: matches: 
[bisect-match 0xcf0b8943315a7804] enabled feature: range-iteration 
bisect: run: go test -v -args -bisect=+1... FAIL (1 matches) 
bisect: matches: 
[bisect-match 0x4d642a7960e4693f] enabled feature: concurrent-logic 
bisect: run: go test -v -args -bisect=+1... FAIL (1 matches) 
bisect: matches: 
[bisect-match 0x4d642a7960e4693f] enabled feature: concurrent-logic 
bisect: confirming failing change set 
bisect: run: go test -v -args -bisect=v+x3f... FAIL (1 matches) 
bisect: matches: 
[bisect-match 0x4d642a7960e4693f] enabled feature: concurrent-logic 
bisect: run: go test -v -args -bisect=v+x3f... FAIL (1 matches) 
bisect: matches: 
[bisect-match 0x4d642a7960e4693f] enabled feature: concurrent-logic 
bisect: FOUND failing change set 
--- change set #1 (enabling changes causes failure) 
enabled feature: concurrent-logic 
--- 
bisect: checking for more failures 
bisect: run: go test -v -args -bisect=-x3f... ok (1 matches) 
bisect: matches: 
[bisect-match 0xcf0b8943315a7804] enabled feature: range-iteration 
bisect: run: go test -v -args -bisect=-x3f... ok (1 matches) 
bisect: matches: 
[bisect-match 0xcf0b8943315a7804] enabled feature: range-iteration 
bisect: target succeeds with all remaining changes enabled 

Briefly interpret the output of this bisect debugging process.

The bisect execution is divided into several stages:

  • Initial inspection phase

Test by first disabling all changes with -bisect=n → Test passes (ok)
Then enable all changes for testing with -bisect=y → Test failed (FAIL)

This indicates that the program works fine without any changes, but fails with all changes enabled.

Two features are observed when all changes are enabled:

[bisect-match 0xcf0b8943315a7804] enabled feature: range-iteration 
[bisect-match 0x4d642a7960e4693f] enabled feature: concurrent-logic 
  • bisection stage

Test + 0 (enable first change: range-iteration) → test passes (ok)
Test +1 (enable second change: current-logic) → Test failed (FAIL)

This process helps to locate exactly which change is causing the failure.

  • acknowledgement stage

Confirm again using v+x3f mode → Test failed (FAIL)
The set of changes that led to the failure was explicitly found:

--- change set #1 (enabling changes causes failure) 
enabled feature: concurrent-logic 
--- 
  • final verification

Test with -x3f mode (disable confirmed problem changes) → Test passes (ok)
Verify that the program works when all other changes (except concurrent-logic) are enabled.

The debugging conclusion from this is that the bisect tool has successfully located theThe problem is with the concurrent-logic featureOn the other hand, the range-iteration feature is safe and does not cause the test to fail. The problem is clearly caused by “intentional” logic in the concurrency logic, which is in line with the expected problem in our code implementation (in the concurrent-logic feature, we do modify the data intentionally).

5. Summary

In this article, we take an in-depth look at Hash-Based Bisect, a state-of-the-art debugging technique, especially in Go language projects.Hash-Based Bisect goes beyond the traditional git bisect approach by generating unique hashes for code change points, combined with bisect search algorithms, to help developers quickly locate problems in complex programs. We’ve also covered in detail how it works. We also covered in detail how it works, its current status in the Go project, and how you can integrate this technique into your own Go libraries to improve debugging efficiency. Perhaps the example here may not be appropriate, but it has accomplished my goal of showing you how to use the bisect tool and the bisect package.

Although Hash-Based Bisect performs well in locating complex problems, it is felt that there are still some shortcomings in its current design that may affect the developer’s experience, especially when integrating it into Go packages or projects, and this shortcoming is mainly reflected in the intrusiveness to the code. In order to support Hash-Based Bisect, Go packages need to explicitly implement protocols for interacting with the bisect tool, including support for receiving incoming patterns (patterns) from bisect from the command line or from environment variables; they need to create a bisect.Matcher object in their code and call the ShouldEnable and ShouldReport interfaces to manage changepoints; unique hash values must be explicitly generated in code for potential changepoints and enabled or disabled as needed.

This explicit integration results in code logic being “polluted” by debugging-related code, increasing code complexity and maintenance costs. For simple libraries or projects, developers may not want to add this burden to their debugging needs.

In \$GOROOT/src/cmd/compile/internal/base, the compiler-related code then encapsulates the bisect into a HashDebug structure, which somewhat reduces the depth of code intrusion as well as the amount of work involved in manual integration.

In addition, the golang.org/x/tools/internal/bisect package has not yet been officially changed to debug/bisect, and it is not yet known whether its API will change subsequently, and the example code in this article is not guaranteed to work correctly even after subsequent Go version adjustments.

The source code covered in this article can be found athere areDownload.

6. References

7. Appendix: git bisect usage examples

Suppose you have a Go language project and you find that one of the recent commits introduced an issue (for example, a test case failed). You want to use git bisect to find the specific commit that introduced the issue.

Your project directory is designed as follows:

my-go-project/ 
├── main.go 
└── main_test.go 

Let’s build this sample project:

// 在hash-based-bisect/git-bisect下面执行 
$mkdir my-go-project 
$cd my-go-project 
$git init 

Create main.go:

// main.go 
package main 
 
func main() { 
    println("Hello, world!") 
} 
 
func Add(a, b int) int { 
    return a + b 
} 

Submit changes:

$git add main.go 
git commit -m "Initial commit with Add function" 
[master (root-commit) 16f8736] Initial commit with Add function 
 1 file changed, 9 insertions(+) 
 create mode 100644 main.go 

Create main_test.go:

// main_test.go 
package main 
 
import "testing" 
 
func TestAdd(t *testing.T) { 
    if Add(2, 3) != 5 { 
        t.Error("Expected 5, got something else") 
    } 
} 

Submit changes:

$git add main_test.go 
git commit -m "Add test for Add function" 
[master b7b3c44] Add test for Add function 
 1 file changed, 9 insertions(+) 
 create mode 100644 main_test.go 

Deliberately introduce a bug and commit the change:

$sed -i 's/return a + b/return a - b/' main.go 
$git commit -am "Introduce a bug in Add function" 
[master 977e647] Introduce a bug in Add function 
 1 file changed, 1 insertion(+), 1 deletion(-) 

Add some other commits (unrelated changes):

$echo "// Just a comment" >> main.go 
$git commit -am "Add a comment" 
[master 25f88b0] Add a comment 
 1 file changed, 2 insertions(+) 

Here is a list of all the commits above for easy cross-referencing:

$git log --oneline 
25f88b0 (HEAD -> master) Add a comment 
977e647 Introduce a bug in Add function 
b7b3c44 Add test for Add function 
16f8736 Initial commit with Add function 

Next, we can demonstrate git bisect, starting with a manual bisect.

Start git bisect mode:

$git bisect start 

Marks the current latest commit as bad:

$git bisect bad 

Mark the first commit as good:

$git bisect good 16f8736 
Bisecting: 0 revisions left to test after this (roughly 1 step) 
[977e647e7461c4c03ee25e53728dd743af925f17] Introduce a bug in Add function 

We see that git bisect automatically switches to an intermediate commit, and we need to verify that this intermediate commit passes the test:

$go test 
--- FAIL: TestAdd (0.00s) 
    main_test.go:7: Expected 5, got something else 
FAIL 
exit status 1 
FAIL    github.com/bigwhite/experiments/hash-based-bisect/git-bisect/my-go-project  0.006s 

The test fails and we mark the commit as bad:

$git bisect bad 
Bisecting: 0 revisions left to test after this (roughly 0 steps) 
[b7b3c444f0fd55086e6ce36fb543a136a1611b61] Add test for Add function 

git bisect switches to another intermediate commit, and we use go test to verify that it passes:

$go test 
PASS 
ok      github.com/bigwhite/experiments/hash-based-bisect/git-bisect/my-go-project  0.005s 

The test passes and we mark this intermediate commit as good:

$git bisect good 
977e647e7461c4c03ee25e53728dd743af925f17 is the first bad commit 
commit 977e647e7461c4c03ee25e53728dd743af925f17 
Author: Tony Bai <[email protected]> 
Date:   Fri Nov 24 13:27:08 2024 +0800 
 
    Introduce a bug in Add function 
 
:100644 100644 e357c05d933724eb8b7c1aafee34b8f95913355e e65baa0414a2a1f983379c23ac549b7d8b056db3 M  main.go 

We see that: git bisect found a bad commit and says “977e647e7461c4c03ee25e53728dd743af925f17 is the first bad commit”.

End git bisect mode:

$git bisect reset 

The above process can be automated using git bisect run without having to manually run go test and markup multiple times in between, here is an equivalent git bisect process:

$git bisect start 
 
$git bisect bad 
 
$git bisect good 16f8736 
Bisecting: 0 revisions left to test after this (roughly 1 step) 
[977e647e7461c4c03ee25e53728dd743af925f17] Introduce a bug in Add function 
 
$git bisect run go test 
running go test 
--- FAIL: TestAdd (0.00s) 
    main_test.go:7: Expected 5, got something else 
FAIL 
exit status 1 
FAIL    github.com/bigwhite/experiments/hash-based-bisect/git-bisect/my-go-project  0.006s 
Bisecting: 0 revisions left to test after this (roughly 0 steps) 
[b7b3c444f0fd55086e6ce36fb543a136a1611b61] Add test for Add function 
running go test 
PASS 
ok      github.com/bigwhite/experiments/hash-based-bisect/git-bisect/my-go-project  0.006s 
977e647e7461c4c03ee25e53728dd743af925f17 is the first bad commit 
commit 977e647e7461c4c03ee25e53728dd743af925f17 
Author: Tony Bai <[email protected]> 
Date:   Fri Nov 24 13:27:08 2024 +0800 
 
    Introduce a bug in Add function 
 
:100644 100644 e357c05d933724eb8b7c1aafee34b8f95913355e e65baa0414a2a1f983379c23ac549b7d8b056db3 M  main.go 
bisect run success 
 
$git bisect reset 
Previous HEAD position was b7b3c44 Add test for Add function 
Switched to branch 'master' 

We’ve seen that git bisect run allows us to locate problems much more quickly without the need for intermediate manual work, and it’s the main means of bisecting that we use in our day-to-day development!


Gopher Tribe Knowledge PlanetIn 2024 will continue to work on building a high quality Go language learning and communication platform. We will continue to provide high-quality Go technical article debut and reading experience. At the same time, we will also enhance the sharing of code quality and best practices, including how to write concise, readable and testable Go code. In addition, we will also strengthen the communication and interaction among our star friends. You are welcome to ask questions, share tips and discuss techniques. I will be the first time to answer and exchange. I sincerely hope that Gopher tribe can become a harbor for everyone to learn, progress and communicate. Let me meet in Gopher tribe and enjoy the joy of coding! Welcome to join!

img{512x368}
img{512x368}

img{512x368}
img{512x368}

Famous cloud hosting service vendor DigitalOcean released its latest hosting plan, the entry-level Droplet configuration is upgraded to: 1 core CPU, 1G RAM, 25G high-speed SSD, price 5$/month. For those who have the need to use DigitalOcean, you can open thislink address: https://ift.tt/17JxXG4 Kick off your journey to DO hosting.

Gopher Daily(Gopher每日新闻) – https://ift.tt/HD0NvQt

My contact information:

  • Microblogging (not available at this time): https://ift.tt/I2td3of
  • 微博2:https://ift.tt/hxkYLBH
  • Blog: tonybai.com
  • github: https://ift.tt/sh06RgY
  • Gopher Daily归档 – https://ift.tt/rBqyh17
  • Gopher Daily Feed订阅 – https://ift.tt/1geVGzQ

Business cooperation methods: writing articles, publishing books, training, online courses, joint ventures, consulting, advertising cooperation.

© 2024, bigwhite. All rights reserved.