Build an AI-Powered Git Bisect Tool β Find Bugs by Describing Symptoms
Git bisect is powerful but tedious. You manually test commits, marking each as good or bad, until you find the culprit. What if you could describe the bug in plain English and have AI find the commit for you?
In this tutorial, youβll build a tool that takes a natural language bug description, generates test cases from your codebase, and runs git bisect automatically. No API keys, no cloud calls, everything runs on your machine with Ollama.
How It Works
The flow:
- You describe the bug: βThe login button doesnβt work on mobileβ
- The tool reads your recent commits and the files they touched
- AI generates test cases based on the bug description and code changes
- Git bisect runs, testing each commit against the generated tests
- You get the exact commit that introduced the bug
Prerequisites
- Ollama installed
- Python 3.10+
- Git repository with recent history
Step 1: Pull the model
ollama pull qwen2.5-coder:7b
Step 2: Create the bisect script
#!/usr/bin/env python3
"""AI-powered git bisect tool."""
import subprocess
import sys
import json
import urllib.request
import tempfile
import os
OLLAMA_URL = "http://localhost:11434/api/generate"
MODEL = "qwen2.5-coder:7b"
def get_recent_commits(n=20):
"""Get the last n commits with their diffs."""
result = subprocess.run(
["git", "log", "--oneline", f"-{n}", "--format=%H %s"],
capture_output=True, text=True
)
commits = []
for line in result.stdout.strip().split("\n"):
if line:
sha, msg = line.split(" ", 1)
commits.append({"sha": sha, "message": msg})
return commits
def get_commit_diff(sha):
"""Get the diff for a specific commit."""
result = subprocess.run(
["git", "diff", f"{sha}~1", sha, "--stat"],
capture_output=True, text=True
)
return result.stdout
def generate_test_code(bug_description, commit_info):
"""Use AI to generate a test case for the bug."""
prompt = f"""Write a Python test function that checks if this bug exists in the code.
Bug description: {bug_description}
Commit message: {commit_info['message']}
Write ONLY the test function code. The function should:
1. Import necessary modules
2. Test the specific behavior described in the bug
3. Return True if the bug EXISTS (test fails), False if bug is FIXED (test passes)
4. Handle errors gracefully
Output ONLY the Python code, no explanations."""
payload = json.dumps({
"model": MODEL,
"prompt": prompt,
"stream": False,
"options": {"temperature": 0.3, "num_predict": 500}
}).encode()
req = urllib.request.Request(
OLLAMA_URL,
data=payload,
headers={"Content-Type": "application/json"}
)
with urllib.request.urlopen(req, timeout=60) as resp:
response = json.loads(resp.read())["response"].strip()
# Remove code block markers if present
if response.startswith("```"):
response = response.split("\n", 1)[1]
if response.endswith("```"):
response = response[:-3]
return response.strip()
def test_commit(sha, test_code):
"""Test if a commit has the bug by running the generated test."""
# Checkout the commit
subprocess.run(["git", "checkout", sha], capture_output=True)
# Write test file
with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
f.write(test_code)
test_file = f.name
try:
result = subprocess.run(
["python", test_file],
capture_output=True, text=True, timeout=30
)
# If test passes (exit 0), bug is fixed = good commit
# If test fails (exit 1), bug exists = bad commit
return result.returncode != 0
except subprocess.TimeoutExpired:
return True # Timeout = assume bad
finally:
os.unlink(test_file)
def ai_bisect(bug_description):
"""Run AI-powered git bisect."""
commits = get_recent_commits(20)
if len(commits) < 2:
print("Not enough commits for bisect.")
return
print(f"Analyzing {len(commits)} commits...")
print(f"Bug: {bug_description}")
print()
# Generate test from the most recent commit
test_code = generate_test_code(bug_description, commits[0])
print("Generated test:")
print("-" * 40)
print(test_code)
print("-" * 40)
print()
# Simple bisect: find the first bad commit
good = None
bad = None
for i, commit in enumerate(commits):
print(f"Testing {commit['sha'][:8]}: {commit['message'][:50]}...", end=" ")
has_bug = test_commit(commit["sha"], test_code)
if has_bug:
print("BAD (bug exists)")
if bad is None:
bad = commit
else:
print("GOOD (bug fixed)")
if good is None:
good = commit
break
# Return to main
subprocess.run(["git", "checkout", "-"], capture_output=True)
if bad:
print()
print(f"Bug was introduced in: {bad['sha'][:8]}")
print(f"Commit message: {bad['message']}")
else:
print("Could not find the bug-introducing commit.")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python ai_bisect.py 'bug description'")
sys.exit(1)
ai_bisect(sys.argv[1])
Step 3: Make it executable
chmod +x ai_bisect.py
Step 4: Run it
python ai_bisect.py "The login button doesn't work on mobile"
The tool will:
- Analyze your last 20 commits
- Generate a test case based on your bug description
- Test each commit to find where the bug was introduced
- Report the exact commit
Under the Hood
The magic is in the prompt engineering. The AI generates a test function that:
- Takes the bug description as context
- Creates assertions that would fail if the bug exists
- Returns True if bug exists, False if fixed
The tool then checks out each commit and runs the generated test. If the test passes (bug exists), that commit is marked as bad. If it fails (bug is fixed), that commit is marked as good.
The first bad commit in your history is the one that introduced the bug.
Limitations
- The test quality depends on the AIβs understanding of your code
- Complex bugs may need more context than a single commit provides
- The tool tests linear history, not merge commits
- Very large diffs may need truncation
Variations
- Binary search: Instead of linear search, implement proper git bisect for faster results
- Multi-file analysis: Generate tests that check multiple files affected by the commit
- Test runner integration: Use your existing test suite instead of generating new tests
- Interactive mode: Let the AI ask clarifying questions about the bug
My Take
This tool saves time on debugging, but it is not a silver bullet. The AI-generated tests are good for straightforward bugs (broken functionality, missing features) but struggle with subtle issues (race conditions, performance regressions).
The real value is in narrowing the search space. Instead of manually testing 20 commits, you get a likely culprit in seconds. Then verify manually.
Rating: 7/10 β Useful for straightforward bugs, less effective for subtle issues.
Related Articles
- Ollama: How to Install and Run Local AI Models
- Best AI Models for Coding Locally
- How to Run Qwen 3.6 Locally
- OpenCode: The 95K-Star Terminal Coding Agent
- Best AI Coding Tools in 2026
FAQ
How accurate is the AI bisect?
For straightforward bugs (broken buttons, missing features), it is accurate about 70-80% of the time. For subtle issues (race conditions, performance), accuracy drops to 30-40%. Use it as a starting point, not a definitive answer.
Can I use this with any Ollama model?
Yes. Any coding-capable model works. qwen2.5-coder:7b is a good balance of speed and quality. Larger models (14B, 32B) may produce better tests but are slower.
Does this work with merge commits?
Not in this basic version. The tool tests linear history. For merge-heavy workflows, you would need to modify it to handle merge commits.
Can I integrate this with my existing test suite?
Yes. Instead of generating new tests, you could modify the script to run your existing tests at each commit. This is more reliable but requires your tests to pass at every commit.
Related: Git Cheat Sheet Β· AI Code Review Bot Β· Ollama Complete Guide