bisect-kit: migrate chromeos bisector to use codechange module

After this CL, chromeos localbuild bisector is more robust and can
handle following issues:
 - add and remove repo projects
 - manifest snapshot racing

Like android local build bisector, the setup step of chromeos checkout
is changed as well. Now you have to make a repo mirror and sync the
tree from the mirror. The exact steps are:

 1. setup a repo mirror
    $ cd $CHROMEOS_REPO_MIRROR_DIR
    $ repo init ...<original flags>... --mirror
    $ mkdir .repo/local_manifests
    $ cat << END > .repo/local_manifests/manifest-versions.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <manifest>
      <project name="chromeos/manifest-versions" remote="cros-internal" />
    </manifest>
    END
    $ repo sync

 2. checkout chromeos tree from the mirror
    $ cd $CHROMEOS_ROOT
    $ repo init ...<original flags>...  --reference=$CHROMEOS_REPO_MIRROR_DIR
    $ repo sync

 3. specify --chromeos_repo_mirror_dir $CHROMEOS_REPO_MIRROR_DIR when you
    use bisect_cros_repo.py and switch_cros_localbuild.py

Due to crbug.com/864886, this bisector can only work with version of
chromite after 2018-07-19. Workaround will be provided in separate CL
later.

BUG=chromium:827092
TEST=unit test and following commands
$ ./bisect_cros_repo.py init --old R69-10883.0.0 --new R69-10884.0.0 \
  --chromeos_root $CHROMEOS_ROOT \
  --chromeos_repo_mirror_dir $CHROMEOS_REPO_MIRROR_DIR \
  --board samus
$ ./switch_cros_localbuild.py samus-test R69-10883.0.0~R69-10884.0.0/10 \
  --chromeos_root $CHROMEOS_ROOT \
  --chromeos_repo_mirror_dir $CHROMEOS_REPO_MIRROR_DIR

Change-Id: I594d850f80a9ff05b7241106bdc7ff191cf12676
Reviewed-on: https://chromium-review.googlesource.com/1143114
Commit-Ready: Kuang-che Wu <kcwu@chromium.org>
Tested-by: Kuang-che Wu <kcwu@chromium.org>
Reviewed-by: Chi-Ngai Wan <cnwan@google.com>
diff --git a/bisect_kit/codechange.py b/bisect_kit/codechange.py
index abc8e83..624437d 100644
--- a/bisect_kit/codechange.py
+++ b/bisect_kit/codechange.py
@@ -492,16 +492,16 @@
     Args:
       spec: Spec object
       path: local path relative to project root
-      timestamp:
+      timestamp: timestamp
 
     Returns:
       The commit hash of given time. If there are commits with the given
       timestamp, returns the last commit.
     """
     git_root = self.cached_git_root(spec[path].repo_url)
-    # spec[path].at is remote reference name. Since git_root is a mirror, it's
-    # no need to convert the name.
-    return git_util.get_rev_by_time(git_root, timestamp, spec[path].at)
+    # spec[path].at is remote reference name. Since git_root is a mirror (not
+    # a local checkout), there is no need to convert the name.
+    return git_util.get_rev_by_time(git_root, timestamp, None, spec[path].at)
 
   def get_actions_between_two_commit(self, spec, path, old, new):
     git_root = self.cached_git_root(spec[path].repo_url)
@@ -704,7 +704,7 @@
           '%s (%s) matched earlier spec at %s instead of %s, racing? offset %d',
           target.name, target.timestamp, specs[index].timestamp,
           specs[ideal_index].timestamp,
-          specs[ideal_index].timestamp - target.timestamp)
+          specs[index].timestamp - target.timestamp)
     if index > ideal_index:
       logger.warning(
           'spec committed at %d matched later commit at %d. bad server clock?',
@@ -762,7 +762,9 @@
 
     # step 1, find all float and fixed specs in the given range.
     fixed_specs = self.spec_manager.collect_fixed_spec(old, new)
+    assert fixed_specs
     float_specs = self.spec_manager.collect_float_spec(old, new)
+    assert float_specs
     while float_specs[-1].timestamp > fixed_specs[-1].timestamp:
       float_specs.pop()
     assert float_specs