Speed up runtime state refresh
This commit is contained in:
@@ -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+"))
|
||||
|
||||
@@ -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() }
|
||||
|
||||
Reference in New Issue
Block a user