Add git bisector scripts

This contains bisect_git.py and switch_git.py

Change-Id: I7cf5eb895ebd9ce803b745c51498b4b151ddc76b
diff --git a/bisect_kit/git_util.py b/bisect_kit/git_util.py
new file mode 100644
index 0000000..426406b
--- /dev/null
+++ b/bisect_kit/git_util.py
@@ -0,0 +1,85 @@
+# Copyright 2017 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Git utility."""
+
+from __future__ import print_function
+import logging
+import re
+import subprocess
+
+from bisect_kit import cli
+from bisect_kit import util
+
+logger = logging.getLogger(__name__)
+
+GIT_FULL_COMMIT_ID_LENGTH = 40
+
+# Minimal acceptable length of git commit id.
+#
+# For chromium, hash collision rate over number of digits:
+#   - 6 digits: 4.85%
+#   - 7 digits: 0.32%
+#   - 8 digits: 0.01%
+# As foolproof check, 7 digits should be enough.
+GIT_MIN_COMMIT_ID_LENGTH = 7
+
+
+def is_git_rev(s):
+  """Is a git hash-like version string.
+
+  It accepts shortened hash with at least 7 digits.
+  """
+  if not GIT_MIN_COMMIT_ID_LENGTH <= len(s) <= GIT_FULL_COMMIT_ID_LENGTH:
+    return False
+  return bool(re.match(r'^[0-9a-f]+$', s))
+
+
+def argtype_git_rev(s):
+  """Validates git hash."""
+  if not is_git_rev(s):
+    msg = 'should be git hash, at least %d digits' % GIT_MIN_COMMIT_ID_LENGTH
+    raise cli.ArgTypeError(msg, '1a2b3c4d5e')
+  return s
+
+
+def checkout_version(git_repo, rev):
+  """git checkout.
+
+  Args:
+    git_repo: path of git repo.
+    rev: git commit revision to checkout.
+  """
+  util.check_call('git', 'checkout', '-q', '-f', rev, cwd=git_repo)
+
+
+def is_containing_commit(git_repo, rev):
+  """Determines given commit exists.
+
+  Args:
+    git_repo: path of git repo.
+    rev: git commit revision in query.
+  """
+  try:
+    return util.check_output(
+        'git', 'cat-file', '-t', rev, cwd=git_repo) == 'commit\n'
+  except subprocess.CalledProcessError:
+    return False
+
+
+def get_revlist(git_repo, old, new):
+  """Enumerates git commit between two revisions (inclusive).
+
+  Args:
+    git_repo: path of git repo.
+    old: git commit revision.
+    new: git commit revision.
+
+  Returns:
+    list of git revisions. The list contains the input revisions, old and new.
+  """
+  assert old
+  assert new
+  cmd = ['git', 'rev-list', '--reverse', '%s^..%s' % (old, new)]
+  revlist = util.check_output(*cmd, cwd=git_repo).splitlines()
+  return revlist