Skip to content

Instantly share code, notes, and snippets.

@kolo
Created March 18, 2010 08:26
Show Gist options
  • Save kolo/336157 to your computer and use it in GitHub Desktop.
Save kolo/336157 to your computer and use it in GitHub Desktop.
From fad23f414a762d74bdcd77290ef206110327bb46 Mon Sep 17 00:00:00 2001
From: Dmitry Maksimov <dmtmax@gmail.com>
Date: Tue, 16 Mar 2010 08:47:04 +0300
Subject: [PATCH] Basic implementation of git-bisect
---
lib/grit.rb | 1 +
lib/grit/bisect.rb | 37 +++++++++++++++++++++++++++++++++
lib/grit/git.rb | 4 +++
lib/grit/repo.rb | 20 ++++++++++++++++++
test/test_bisect.rb | 31 +++++++++++++++++++++++++++
test/test_bisect_object.rb | 49 ++++++++++++++++++++++++++++++++++++++++++++
test/test_git.rb | 5 ++++
7 files changed, 147 insertions(+), 0 deletions(-)
create mode 100644 lib/grit/bisect.rb
create mode 100644 test/test_bisect.rb
create mode 100644 test/test_bisect_object.rb
diff --git a/lib/grit.rb b/lib/grit.rb
index d6f6148..426a2d9 100644
--- a/lib/grit.rb
+++ b/lib/grit.rb
@@ -50,6 +50,7 @@ require 'grit/status'
require 'grit/submodule'
require 'grit/blame'
require 'grit/merge'
+require 'grit/bisect'
module Grit
diff --git a/lib/grit/bisect.rb b/lib/grit/bisect.rb
new file mode 100644
index 0000000..092150e
--- /dev/null
+++ b/lib/grit/bisect.rb
@@ -0,0 +1,37 @@
+module Grit
+
+ class BisectObject
+ def initialize(git)
+ @git = git
+ end
+
+ def good(ref = nil)
+ response = @git.bisect(:good, ref)
+ parse_response(response)
+ end
+
+ def bad(ref = nil)
+ response = @git.bisect(:bad, ref)
+ parse_response(response)
+ end
+
+ def start
+ @git.bisect(:start)
+ end
+
+ def reset
+ @git.bisect(:reset)
+ end
+
+ private
+ def parse_response(response)
+ first_line = response.first
+ return nil if first_line.match(/^Bisecting:/)
+ return first_line.split.first if first_line.match(/is\sthe\sfirst\sbad\scommit$/) # broken commit sha
+
+ raise Exception, "Grit::BisectObject#parse_response is outdated"
+ end
+
+ end # BisectObject
+
+end # Grit
diff --git a/lib/grit/git.rb b/lib/grit/git.rb
index 1831fe2..5d83a46 100644
--- a/lib/grit/git.rb
+++ b/lib/grit/git.rb
@@ -183,6 +183,10 @@ module Grit
false
end
+ def bisect(subcommand, *args)
+ run("", "bisect #{subcommand.to_s}", "", {}, args.compact)
+ end
+
# RAW CALLS WITH ENV SETTINGS
def raw_git_call(command, index)
tmp = ENV['GIT_INDEX_FILE']
diff --git a/lib/grit/repo.rb b/lib/grit/repo.rb
index 9eb3354..8a72a89 100644
--- a/lib/grit/repo.rb
+++ b/lib/grit/repo.rb
@@ -477,6 +477,26 @@ module Grit
def inspect
%Q{#<Grit::Repo "#{@path}">}
end
+
+ # Find a broken commit using git bisect
+ # +good_commit_ref+ is the reference to the last good commit
+ # +bad_commit_ref+ is the reference to the bad commit, by default points to HEAD
+ #
+ # Returns nothing
+ def bisect(good_commit_ref, bad_commit_ref = nil, &block)
+ if block
+ bisect_obj = BisectObject.new(self.git)
+ bisect_obj.start
+ bisect_obj.bad(bad_commit_ref)
+ bisect_obj.good(good_commit_ref)
+
+ yield bisect_obj
+
+ bisect_obj.reset
+ else
+ raise ArgumentError, "A code block should be passed to Repo#bisect"
+ end
+ end
end # Repo
end # Grit
diff --git a/test/test_bisect.rb b/test/test_bisect.rb
new file mode 100644
index 0000000..68d1948
--- /dev/null
+++ b/test/test_bisect.rb
@@ -0,0 +1,31 @@
+require File.dirname(__FILE__) + '/helper'
+
+class TestBisect < Test::Unit::TestCase
+
+ def setup
+ @r = Repo.new(File.join(File.dirname(__FILE__), %w[dot_git]), :is_bare => true)
+
+ @good_commit = "d6016bc9fa3950ad18e3028f9d2d26f831061a62"
+ @bad_commit = "ca8a30f5a7f0f163bbe3b6f0abf18a6c83b0687a"
+ end
+
+ def test_bisect_with_empty_block
+ assert_raise ArgumentError do
+ @r.bisect(@good_commit)
+ end
+ end
+
+ def test_bisect_calls
+ bisect_obj = mock("BisectObject instance")
+ bisect_obj.expects(:start)
+ bisect_obj.expects(:bad).with(@bad_commit)
+ bisect_obj.expects(:good).with(@good_commit)
+ bisect_obj.expects(:reset)
+
+ BisectObject.expects(:new).returns(bisect_obj)
+
+ @r.bisect(@good_commit, @bad_commit) do |b|
+ end
+ end
+
+end
diff --git a/test/test_bisect_object.rb b/test/test_bisect_object.rb
new file mode 100644
index 0000000..abc02e3
--- /dev/null
+++ b/test/test_bisect_object.rb
@@ -0,0 +1,49 @@
+require File.dirname(__FILE__) + '/helper'
+
+class TestBisectObject < Test::Unit::TestCase
+
+ def setup
+ @git_mock = mock("git")
+ @bisect_obj = BisectObject.new(@git_mock)
+ end
+
+ def test_start
+ @git_mock.expects(:bisect).with(:start)
+ @bisect_obj.start
+ end
+
+ def test_reset
+ @git_mock.expects(:bisect).with(:reset)
+ @bisect_obj.reset
+ end
+
+ def test_bad
+ @git_mock.expects(:bisect).with(:bad, nil).returns("Bisecting: ...")
+ @bisect_obj.bad
+ end
+
+ def test_good
+ @git_mock.expects(:bisect).with(:good, nil).returns("Bisecting: ...")
+ @bisect_obj.good
+ end
+
+ def test_parse_response_returns_nil
+ @git_mock.stubs(:bisect).with(:bad, nil).returns("Bisecting: ...")
+ assert_equal nil, @bisect_obj.bad
+ end
+
+ def test_parse_response_returns_commit_sha
+ sha = "d6016bc9fa3950ad18e3028f9d2d26f831061a62"
+
+ @git_mock.stubs(:bisect).with(:good, nil).returns("#{sha} is the first bad commit\n")
+ assert sha, @bisect_obj.good
+ end
+
+ def test_parse_response_raises_error
+ @git_mock.stubs(:bisect).with(:bad, nil).returns("Someting changed in git bisect messages\n")
+ assert_raise Exception do
+ @bisect_obj.bad
+ end
+ end
+
+end
diff --git a/test/test_git.rb b/test/test_git.rb
index 1d9d9e0..1a95363 100644
--- a/test/test_git.rb
+++ b/test/test_git.rb
@@ -101,4 +101,9 @@ class TestGit < Test::Unit::TestCase
FileUtils.expects(:rm_rf).with(File.join(@git.git_dir, 'foo'))
@git.fs_delete('foo')
end
+
+ def test_bisect
+ @git.expects(:run).with("", "bisect start", "", {}, [])
+ @git.bisect(:start)
+ end
end
--
1.6.3.3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment