Skip to content

Instantly share code, notes, and snippets.

@aberonni
Created May 1, 2024 07:00
Show Gist options
  • Save aberonni/785d3088e32214510f45809a90511ef6 to your computer and use it in GitHub Desktop.
Save aberonni/785d3088e32214510f45809a90511ef6 to your computer and use it in GitHub Desktop.
Performing a git bisect

git bisect is a really useful tool when you want to answer the question "when did this thing change?" but it's hard to understand from just looking at the git logs.

The way git bisect works is that you define a "good" and a "bad" commit. The git bisect command then uses a binary search to narrow down the commit that made the repository go from "good" to "bad".

You can further make your life easier by asking git bisect to execute a script for you on each commit, automating the search, and providing the commit in question with no further manual intervention.

Below is an example, with an example script, that illustrates how git-bisect was used on the 121-platform to find the answer to the question "when did typeorm start generating nonsense migrations?".

Setup

git checkout main
git pull
# Following command must be executed in the root of the working directory
git bisect start
# Use the SHA of a commit that you know is "good" (in this example, that you know does NOT generate a migration file when it shouldn't)
git bisect good de978708e7fd2f673a7f57bf2ec654e1dae33e85
# Use the SHA of a commit that you know is "bad" (in this example, that you know generates a migration file when it shouldn't)
git bisect bad 95fa43cdf89e99e63d89cd5f3f9abd94b670573b

Now, git bisect will checkout a commit that is somewhere within the range between "good" and "bad" that you have provided. At this point, you can decide how to proceed.

Option 1 - Manual search

For each commit that git bisect offers, you can run your manual tests (or other means of determining whether the commit is "bad" or "good") and then notify git bisect.

# If the current commit is good
git bisect good
# If the current commit is bad
git bisect bad
# If the state of the current commit is for some reason to be ignored
git bisect skip

The binary search will continue, so git bisect will propose a new commit, and you will repeat this process until you've reached the end of the search. You should also be given an estimate of how many more commits you will likely need to check (how many more "steps" in the binary search).

Option 2 - Automated search

You can just ask git bisect to run a script on each commit, and deem whether the commit is "good" or "bad" based on the exit code of the script.

  • exit code 0: good commit
  • exit code 1: bad commit
  • exit code 125: skip commit

Below is the script that was used in this example. This script ran automatically on ~20 different commits until eventually the faulty commit was automatically identified by git bisect.

git bisect run ./bisect-script.sh
#!/bin/bash
currentsha=$(git rev-parse --short HEAD)
# Update this to the correct folder if necessary
cd ~/git/121-platform/services
echo "Restarting 121-service..."
docker-compose -f docker-compose.yml -f docker-compose.development.yml down
docker rm -f $(docker ps -a -q)
npm run start:services
attempts=0
until curl --head --silent --fail http://localhost:3000/docs 1> /dev/null 2>&1; do
echo "Waiting for 121-service to be ready ($attempts)..."
sleep 1
attempts=$((attempts + 1))
if [ $attempts -gt 45 ]; then
echo "121-service did not start in time"
# Probably there is something in this commit preventing the service froms starting correctly
# so it cannot be judged
# (Skip commit - git bisect will not take into account this commit while bisecting)
exit 125
fi
done
echo "121-service is running on commit $currentsha"
# Run migration script using current sha as variable
docker exec 121-service npm run migration:generate migration/$currentsha
# Store new migration files in list, relative to this folder, ignoring ?? in git status output
new_migration_files=$(git status --porcelain | grep migration | cut -c13-)
# If there are any migration files, delete them one by one, and then exit with error
if [ -n "$new_migration_files" ]; then
echo "New migration files detected"
for file in $new_migration_files; do
echo "Removing $file"
rm $file
done
# (Bad commit)
exit 1
fi
echo "No new migration files detected"
# (Good commit)
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment