Speed up runtime state refresh

This commit is contained in:
Joe Tretter
2026-06-24 17:44:53 -05:00
parent 0554c736e4
commit 1c15297ae6
7 changed files with 180 additions and 22 deletions

View File

@@ -6,16 +6,21 @@ internal class GitRepositoryInspector(
private val nativeGit: () -> File,
private val runGit: (File, File, List<String>, Map<String, String>) -> ProcessExecutionResult,
) {
fun inspectConfig(sandbox: File, currentDir: String = "."): Map<String, String> {
val git = nativeGit()
val workingDir = File(sandbox, currentDir).canonicalFile
.takeIf { it.path == sandbox.canonicalPath || it.path.startsWith(sandbox.canonicalPath + File.separator) }
?: sandbox
val repositoryRoot = repositoryRoot(sandbox, workingDir) ?: sandbox
return readConfig(git, repositoryRoot)
}
fun inspect(sandbox: File, currentDir: String = "."): RepoState {
val git = nativeGit()
val workingDir = File(sandbox, currentDir).canonicalFile
.takeIf { it.path == sandbox.canonicalPath || it.path.startsWith(sandbox.canonicalPath + File.separator) }
?: sandbox
val repositoryRoot = generateSequence(workingDir) { directory ->
directory.parentFile?.takeIf {
it.path == sandbox.canonicalPath || it.path.startsWith(sandbox.canonicalPath + File.separator)
}
}.firstOrNull { File(it, ".git").exists() }
val repositoryRoot = repositoryRoot(sandbox, workingDir)
val inspectionRoot = repositoryRoot ?: sandbox
val filesOnDisk = inspectionRoot.walkTopDown()
.filter { it.isFile && !it.relativeTo(inspectionRoot).path.startsWith(".git/") }
@@ -54,8 +59,6 @@ internal class GitRepositoryInspector(
val remoteResult = run(git, inspectionRoot, listOf("remote", "-v"))
val headResult = run(git, inspectionRoot, listOf("branch", "--show-current"))
val exactTagResult = run(git, inspectionRoot, listOf("describe", "--tags", "--exact-match"))
val userNameResult = run(git, inspectionRoot, listOf("config", "--get", "user.name"))
val userEmailResult = run(git, inspectionRoot, listOf("config", "--get", "user.email"))
val stashResult = run(git, inspectionRoot, listOf("stash", "list", "--format=%gd"))
val submodules = inspectSubmodules(git, inspectionRoot)
val maintenanceActions = buildSet {
@@ -68,14 +71,7 @@ internal class GitRepositoryInspector(
?.readLines()
?.count { it.isNotBlank() }
?: 0
val config = buildMap {
userNameResult.outputLines.firstOrNull()
?.takeIf { userNameResult.exitCode == 0 && it.isNotBlank() }
?.let { put("user.name", it) }
userEmailResult.outputLines.firstOrNull()
?.takeIf { userEmailResult.exitCode == 0 && it.isNotBlank() }
?.let { put("user.email", it) }
}
val config = readConfig(git, inspectionRoot)
val commits = if (logResult.exitCode == 0) {
logResult.outputLines.filter { it.isNotBlank() }.mapNotNull { line ->
@@ -185,6 +181,28 @@ internal class GitRepositoryInspector(
environment: Map<String, String> = emptyMap(),
): ProcessExecutionResult = runGit(binary, workingDir, arguments, environment)
private fun repositoryRoot(sandbox: File, workingDir: File): File? {
val sandboxPath = sandbox.canonicalPath
return generateSequence(workingDir) { directory ->
directory.parentFile?.takeIf {
it.path == sandboxPath || it.path.startsWith(sandboxPath + File.separator)
}
}.firstOrNull { File(it, ".git").exists() }
}
private fun readConfig(git: File, inspectionRoot: File): Map<String, String> {
val userNameResult = run(git, inspectionRoot, listOf("config", "--get", "user.name"))
val userEmailResult = run(git, inspectionRoot, listOf("config", "--get", "user.email"))
return buildMap {
userNameResult.outputLines.firstOrNull()
?.takeIf { userNameResult.exitCode == 0 && it.isNotBlank() }
?.let { put("user.name", it) }
userEmailResult.outputLines.firstOrNull()
?.takeIf { userEmailResult.exitCode == 0 && it.isNotBlank() }
?.let { put("user.email", it) }
}
}
private fun inspectRemoteRefs(git: File, inspectionRoot: File, remoteLines: List<String>): RemoteRefs {
val remotes = remoteLines.mapNotNull { line ->
val parts = line.trim().split(Regex("\\s+"))

View File

@@ -128,8 +128,8 @@ class GitRepositoryRuntime private constructor(
?: helperCommands.execute(sandboxRoot, workingDir, currentRepo, expandedTokens)
}
val inspectedRepo = inspectSandbox(level, result.first.currentDir).copy(currentDir = result.first.currentDir)
return inspectedRepo to result.second
val refreshedRepo = refreshRepoAfterCommand(level, currentRepo, result.first, expandedTokens)
return refreshedRepo to result.second
}
fun completionCandidates(level: Level, currentRepo: RepoState, directoriesOnly: Boolean): List<String> {
@@ -236,6 +236,72 @@ class GitRepositoryRuntime private constructor(
private fun inspectSandbox(level: Level, currentDir: String = "."): RepoState =
repositoryInspector.inspect(sandboxDir(level), currentDir)
private fun refreshRepoAfterCommand(
level: Level,
previousRepo: RepoState,
commandRepo: RepoState,
tokens: List<String>,
): RepoState {
if (tokens.isEmpty()) return previousRepo
if (!commandNeedsStateRefresh(tokens)) return commandRepo
if (tokens.isGitConfigCommand()) {
return commandRepo.copy(
config = repositoryInspector.inspectConfig(sandboxDir(level), commandRepo.currentDir),
)
}
return inspectSandbox(level, commandRepo.currentDir).copy(currentDir = commandRepo.currentDir)
}
private fun commandNeedsStateRefresh(tokens: List<String>): Boolean {
return when (tokens.firstOrNull()) {
"git" -> tokens.gitCommandNeedsStateRefresh()
"sh" -> true
"touch", "mkdir", "md", "rm", "del" -> true
"echo" -> tokens.any { it == ">" || it == ">>" }
else -> tokens.firstOrNull()?.startsWith("./") == true
}
}
private fun List<String>.gitCommandNeedsStateRefresh(): Boolean {
val command = drop(1).firstOrNull { it != "-C" && !it.startsWith("--git-dir=") && !it.startsWith("--work-tree=") }
?: return false
return when (command) {
"init",
"config",
"add",
"stage",
"rm",
"mv",
"commit",
"commit-tree",
"checkout",
"switch",
"restore",
"branch",
"tag",
"remote",
"fetch",
"pull",
"push",
"merge",
"rebase",
"reset",
"revert",
"stash",
"cherry-pick",
"repack",
"submodule",
"update-ref",
"read-tree",
-> true
else -> false
}
}
private fun List<String>.isGitConfigCommand(): Boolean {
return firstOrNull() == "git" && drop(1).firstOrNull { !it.startsWith("-") } == "config"
}
private fun nativeGitBinary(): File? {
return nativeGitOverride
?.takeIf { it.exists() && it.canExecute() }