Ask for last good commit in bisect level
This commit is contained in:
@@ -584,8 +584,8 @@ class GitRepositoryRuntime private constructor(
|
||||
|
||||
private fun materializeNativeGitState(nativeGit: File, sandbox: File, desired: RepoState, level: Level) {
|
||||
level.nativeSetup?.let { setup ->
|
||||
val nativeSetup = NativeLevelSetup(sandbox) { directory, arguments ->
|
||||
runGit(nativeGit, directory, arguments).exitCode
|
||||
val nativeSetup = NativeLevelSetup(sandbox) { directory, arguments, environment ->
|
||||
runGit(nativeGit, directory, arguments, environment).exitCode
|
||||
}
|
||||
if (nativeSetup.setup()) {
|
||||
return
|
||||
|
||||
@@ -4,8 +4,10 @@ import java.io.File
|
||||
|
||||
class NativeLevelSetup internal constructor(
|
||||
internal val sandbox: File,
|
||||
private val runGit: (File, List<String>) -> Int,
|
||||
private val runGit: (File, List<String>, Map<String, String>) -> Int,
|
||||
) {
|
||||
private var commitSequence = 0
|
||||
|
||||
fun resetFiles() {
|
||||
sandbox.listFiles()
|
||||
?.filterNot { it.name == ".git" }
|
||||
@@ -13,9 +15,9 @@ class NativeLevelSetup internal constructor(
|
||||
git("checkout", "-B", "master")
|
||||
}
|
||||
|
||||
fun git(vararg arguments: String): Int = runGit(sandbox, arguments.toList())
|
||||
fun git(vararg arguments: String): Int = runGit(sandbox, arguments.toList(), emptyMap())
|
||||
|
||||
fun git(directory: File, vararg arguments: String): Int = runGit(directory, arguments.toList())
|
||||
fun git(directory: File, vararg arguments: String): Int = runGit(directory, arguments.toList(), emptyMap())
|
||||
|
||||
fun initRepo(directory: File) {
|
||||
directory.mkdirs()
|
||||
@@ -43,10 +45,15 @@ class NativeLevelSetup internal constructor(
|
||||
}
|
||||
|
||||
fun commit(message: String, author: String? = null) {
|
||||
val commitDate = nextDeterministicCommitDate()
|
||||
val environment = mapOf(
|
||||
"GIT_AUTHOR_DATE" to commitDate,
|
||||
"GIT_COMMITTER_DATE" to commitDate,
|
||||
)
|
||||
if (author == null) {
|
||||
git("commit", "-m", message)
|
||||
gitWithEnvironment(environment, "commit", "-m", message)
|
||||
} else {
|
||||
git("commit", "--author", author, "-m", message)
|
||||
gitWithEnvironment(environment, "commit", "--author", author, "-m", message)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,4 +92,15 @@ class NativeLevelSetup internal constructor(
|
||||
fun tag(name: String) {
|
||||
git("tag", "-f", name)
|
||||
}
|
||||
|
||||
private fun gitWithEnvironment(environment: Map<String, String>, vararg arguments: String): Int {
|
||||
return runGit(sandbox, arguments.toList(), environment)
|
||||
}
|
||||
|
||||
private fun nextDeterministicCommitDate(): String {
|
||||
commitSequence += 1
|
||||
val minute = (commitSequence / 60).toString().padStart(2, '0')
|
||||
val second = (commitSequence % 60).toString().padStart(2, '0')
|
||||
return "2000-01-01T00:$minute:$second+0000"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,16 +10,19 @@ package solutions.tretter.githugandroid
|
||||
internal fun bisectLevel(): Level = level(
|
||||
id = "bisect",
|
||||
title = "Bisect",
|
||||
description = "A balance check started failing somewhere in the history. Run `./test-balance.sh` to test the current commit. Use `git bisect` to identify the first bad commit.",
|
||||
description = "A balance check started failing somewhere in the history. The current HEAD is bad: `./test-balance.sh` prints `balance broken`. Use `git bisect` to locate the break, then enter the abbreviated hash of the last good commit.",
|
||||
hints = listOf(
|
||||
"Start with a known bad commit and a known good commit.",
|
||||
"Mark the current HEAD as bad, because the test fails there.",
|
||||
"`known-good` marks a commit where the balance check passes.",
|
||||
"You can automate the search with `git bisect run ./test-balance.sh`.",
|
||||
"`git bisect run ./test-balance.sh` identifies the first bad commit; the last good commit is its parent in this linear history.",
|
||||
),
|
||||
commandSuggestions = listOf(
|
||||
"./test-balance.sh",
|
||||
"git bisect start HEAD known-good",
|
||||
"git bisect start",
|
||||
"git bisect bad HEAD",
|
||||
"git bisect good known-good",
|
||||
"git bisect run ./test-balance.sh",
|
||||
"git rev-parse --short HEAD^",
|
||||
),
|
||||
setup = {
|
||||
RepoState(
|
||||
@@ -68,22 +71,26 @@ internal fun bisectLevel(): Level = level(
|
||||
addCommit("Update help text", "README")
|
||||
append("notes.txt", "Forecast still depends on the balance check\n")
|
||||
addCommit("Add forecast note", "notes.txt")
|
||||
append("README", "Use git bisect to find the first broken commit.\n")
|
||||
append("README", "Use git bisect to find the last good commit before the report broke.\n")
|
||||
addCommit("Polish report labels", "README")
|
||||
true
|
||||
},
|
||||
validator = { repo, command ->
|
||||
val badCommit = repo.commits.firstOrNull { it.message == "Break closing balance calculation" }
|
||||
val lastGoodCommit = repo.commits.firstOrNull { it.message == "Add audit note" }
|
||||
val normalized = command.trim()
|
||||
val answerMatches = badCommit != null &&
|
||||
normalized.isNotBlank() &&
|
||||
lastGoodCommit != null &&
|
||||
normalized.length >= 4 &&
|
||||
!normalized.startsWith("git ") &&
|
||||
(normalized.startsWith(badCommit.id, ignoreCase = true) || badCommit.id.startsWith(normalized, ignoreCase = true))
|
||||
val bisectFoundBadCommit = normalized.startsWith("git bisect run", ignoreCase = true) &&
|
||||
"bisect-found" in repo.maintenanceActions
|
||||
answerMatches || bisectFoundBadCommit
|
||||
(normalized.startsWith(lastGoodCommit.id, ignoreCase = true) || lastGoodCommit.id.startsWith(normalized, ignoreCase = true))
|
||||
},
|
||||
testCases = listOf(
|
||||
levelTestCase("automated bisect", "git bisect start HEAD known-good", "git bisect run ./test-balance.sh"),
|
||||
levelTestCase(
|
||||
"answer last good commit",
|
||||
"git bisect start",
|
||||
"git bisect bad HEAD",
|
||||
"git bisect good known-good",
|
||||
"git bisect run ./test-balance.sh",
|
||||
"c8c7c00",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user