Fix bisect script run and level validators

This commit is contained in:
Joe Tretter
2026-06-26 19:00:51 -05:00
parent caaf26a541
commit d703f539d3
5 changed files with 79 additions and 4 deletions

View File

@@ -20,8 +20,8 @@ android {
applicationId = "solutions.tretter.githugandroid"
minSdk = 26
targetSdk = 35
versionCode = 182
versionName = "0.1.181"
versionCode = 183
versionName = "0.1.182"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true

View File

@@ -24,6 +24,27 @@ internal fun directoryCompletionCandidates(repo: RepoState): List<String> {
.distinct()
}
internal fun contextualCompletionCandidates(
candidates: List<String>,
commandBeforeToken: String,
token: String,
directoriesOnly: Boolean,
): List<String> {
val matches = candidates.sorted().filter { it.startsWith(token) }
if (directoriesOnly) return matches
val commandTokens = GitSandboxEngine.tokenizeCommand(commandBeforeToken.trim())
if (commandTokens == listOf("git", "bisect", "run")) {
val scriptMatches = matches.filter { candidate ->
val normalized = candidate.removePrefix("./")
normalized.endsWith(".sh") && '/' !in normalized && !normalized.startsWith(".")
}
if (scriptMatches.isNotEmpty()) return scriptMatches
}
return matches
}
private fun RepoState.currentDirPrefix(): String {
return if (currentDir == ".") "" else currentDir.trimEnd('/') + "/"
}

View File

@@ -369,7 +369,12 @@ fun GitHugApp() {
if (token.isBlank() && !isCdCompletion) return
val candidates = runtime.completionCandidates(currentLevel, repo, directoriesOnly = isCdCompletion)
val matches = candidates.sorted().filter { it.startsWith(token) }
val matches = contextualCompletionCandidates(
candidates = candidates,
commandBeforeToken = beforeCursor.substring(0, tokenStart),
token = token,
directoriesOnly = isCdCompletion,
)
if (matches.isEmpty()) return
val replacement = if (matches.size == 1) matches.first() else commonPrefix(matches)

View File

@@ -144,7 +144,12 @@ class GitRepositoryRuntime private constructor(
val result = when (expandedTokens.first()) {
"git" -> {
val gitResult = runGit(nativeGit, workingDir, expandedTokens.drop(1), invocation.environment)
val gitResult = runGit(
nativeGit,
workingDir,
normalizeGitArgumentsForAndroid(expandedTokens.drop(1)),
invocation.environment,
)
AppLog.d(
"GitRuntime",
"Git command result level=${level.id} exit=${gitResult.exitCode} output=${gitResult.outputLines}",
@@ -167,6 +172,19 @@ class GitRepositoryRuntime private constructor(
return refreshedRepo to result.second
}
private fun normalizeGitArgumentsForAndroid(arguments: List<String>): List<String> {
if (
arguments.size >= 3 &&
arguments[0] == "bisect" &&
arguments[1] == "run" &&
arguments[2].startsWith("./") &&
arguments[2].endsWith(".sh")
) {
return arguments.take(2) + listOf("sh", arguments[2]) + arguments.drop(3)
}
return arguments
}
fun completionCandidates(level: Level, currentRepo: RepoState, directoriesOnly: Boolean): List<String> {
requireNativeGit()
val sandbox = sandboxDir(level)

View File

@@ -504,6 +504,37 @@ class GitSandboxEngineTest {
}
}
@Test
fun nativeBisectRunScriptShortcutRunsThroughShell() {
val git = testGitBinary()
assumeTrue(git.exists() && git.canExecute())
val root = Files.createTempDirectory("githug-bisect-run-script").toFile()
try {
val runtime = GitRepositoryRuntime(root, git)
val level = bisectLevel()
var repo = runtime.prepareLevel(level)
listOf(
"git bisect start",
"git bisect bad HEAD",
"git bisect good known-good",
).forEach { command ->
val (nextRepo, _) = runtime.execute(level, repo, command)
repo = nextRepo
}
val (_, output) = runtime.execute(level, repo, "git bisect run ./test-balance.sh")
val text = output.joinToString("\n")
assertFalse(text, text.contains("Permission denied", ignoreCase = true))
assertFalse(text, text.contains("can't execute", ignoreCase = true))
assertFalse(text, text.contains("bogus exit code", ignoreCase = true))
assertTrue(text, text.contains("first bad commit", ignoreCase = true))
} finally {
root.deleteRecursively()
}
}
private fun testGitBinary(): File {
System.getenv("GITHUG_TEST_GIT_BINARY")
?.takeIf { it.isNotBlank() }