def get_next(self): """Computes the next commit to test. This takes skip-ranges into account and prioritizes finding the first commit that unbreaks a skip range. May add commits for cherry pick. Returns `False` when the bisect is finished. """ patchset = read_patchset() considered_good = get_good_commits() + get_skip_range_commits(patchset) candidates = git.get_bisect_all(considered_good, "refs/bisect/bad") # It would be better to use a more sophisticated algorithm like # https://github.com/git/git/commit/ebc9529f0358bdb10192fa27bc75f5d4e452ce90 # This works for now though. commit = first_not_skipped(candidates) if git.rev_parse(commit) == git.rev_parse("refs/bisect/bad"): skip_ranges = [] good_commits = [git.rev_parse(ref) for ref in get_good_commits()] for parent in git.parents(commit): if parent in good_commits: print(f"First bad found! Here it is: {commit}") return None skip_ranges += skip_ranges_of_commit(parent, patchset) print(f"cherry-pick {commit} to unbreak {skip_ranges}") patchset.insert(0, commit) git.update_ref(f"refs/bisect/{patchset_identifier(patchset)}/head", commit) return self.get_next() return commit
def get_next(self): """Computes the next commit to test. This takes skip-ranges into account and prioritizes finding the first commit that unbreaks a skip range. May add commits for cherry pick. Returns `False` when the bisect is finished. """ patchset = read_patchset() considered_good = get_good_commits() + get_skip_range_commits(patchset) commit = git.get_bisect_info(considered_good, "refs/bisect/bad")["bisect_rev"] if git.rev_parse(commit) == git.rev_parse("refs/bisect/bad"): skip_ranges = [] good_commits = [git.rev_parse(ref) for ref in get_good_commits()] for parent in git.parents(commit): if parent in good_commits: print(f"First bad found! Here it is: {commit}") return None skip_ranges += skip_ranges_of_commit(parent, patchset) print(f"cherry-pick {commit} to unbreak {skip_ranges}") patchset.insert(0, commit) git.update_ref(f"refs/bisect/{patchset_identifier(patchset)}/head", commit) return self.get_next() return commit
def bisect_good(commit): """Mark a commit as good. The same disclaimer as for `bisect_bad` applies. """ # alternative: `git bisect--helper bisect-write` rev_parsed = git.rev_parse(commit) git.update_ref(f"refs/bisect/good-{rev_parsed}", commit) bisect_append_log(f"# good: {git.rev_pretty(commit)}") bisect_append_log(f"git bisect good {git.rev_parse(commit)}")
def bisect_skip(commit): """Mark a single commit as skipped. This is the traditional `git bisect skip`. The commits skipped with this do not mark a range, and the algorithm will not attempt to "unbreak" the commits. """ rev_parsed = git.rev_parse(commit) git.update_ref(f"refs/bisect/skip-{rev_parsed}", commit) bisect_append_log(f"# skip: {git.rev_pretty(commit)}") bisect_append_log(f"git bisect skip {git.rev_parse(commit)}")
def named_skip(name, patchset, commit): """Mark a commit as belonging to a named skip range. In contrast to a regular `git bisect skip`, all commits between two commits in the range are considered skipped as well. """ unique_name = git.rev_parse(commit) git.update_ref( f"refs/bisect/break/{patchset_identifier(patchset)}/markers/{name}/{unique_name}", commit, )
def has_good_and_bad(): """Determines if the bisect can start""" return len(get_good_commits()) > 0 and git.rev_parse( "refs/bisect/bad") is not None