Route interactive Git through native terminal sessions

Remove Kotlin interactive-add emulation and run patch add via the native Git runtime with PTY-backed session IO.
This commit is contained in:
Joe Tretter
2026-06-27 13:07:35 -05:00
parent d703f539d3
commit 943bf03ca0
19 changed files with 684 additions and 549 deletions

View File

@@ -535,6 +535,59 @@ class GitSandboxEngineTest {
}
}
@Test
fun nativeInteractiveAddKeepsCompiledGitSessionOpenForInput() {
val git = testGitBinary()
assumeTrue(git.exists() && git.canExecute())
val root = Files.createTempDirectory("githug-native-add-i").toFile()
try {
val runtime = GitRepositoryRuntime(root, git)
val level = addLevel()
var repo = runtime.prepareLevel(level)
val (menuRepo, menuOutput) = runtime.execute(level, repo, "git add -i")
repo = menuRepo
val (quitRepo, quitOutput) = runtime.execute(level, repo, "q")
assertNotNull(menuRepo.nativeGitSession)
assertTrue(menuOutput.joinToString("\n"), menuOutput.any { it.contains("What now") })
assertNull(quitRepo.nativeGitSession)
assertTrue(quitOutput.joinToString("\n"), quitOutput.any { it.contains("Bye") })
} finally {
root.deleteRecursively()
}
}
@Test
fun nativePatchAddUsesCompiledGitEditorAndStagesOnlyEditedStageLinesHunk() {
val git = testGitBinary()
assumeTrue(git.exists() && git.canExecute())
val root = Files.createTempDirectory("githug-stage-lines-native-patch").toFile()
try {
val runtime = GitRepositoryRuntime(root, git)
val level = stageLinesLevel()
var repo = runtime.prepareLevel(level)
val (patchRepo, patchOutput) = runtime.execute(
level,
repo,
"GIT_EDITOR=\"sed -i '/second feature/d'\" git add -p feature.rb",
)
repo = patchRepo
val (resultRepo, _) = runtime.execute(level, repo, "e")
val file = resultRepo.files.single { it.name == "feature.rb" }
assertNotNull(patchRepo.nativeGitSession)
assertTrue(patchOutput.joinToString("\n"), patchOutput.any { it.contains("Stage this hunk") })
assertNull(resultRepo.nativeGitSession)
assertTrue(resultRepo.diagnosticSnapshot(), level.validator(resultRepo, "e"))
assertTrue(file.stagedContent.orEmpty(), file.stagedContent.orEmpty().contains("This change belongs to the first feature"))
assertFalse(file.stagedContent.orEmpty(), file.stagedContent.orEmpty().contains("This change belongs to the second feature"))
} finally {
root.deleteRecursively()
}
}
private fun testGitBinary(): File {
System.getenv("GITHUG_TEST_GIT_BINARY")
?.takeIf { it.isNotBlank() }