Create packaged Git exec aliases for helper commands
- Create a runtime git-exec directory containing git-* aliases that point at the packaged libgit.so binary. - Set GIT_EXEC_PATH for every native Git invocation so Git subcommands like fetch, merge-base, upload-pack, and receive-pack resolve the same way in tests and on-device. - Keep JVM tests on GitRepositoryRuntime with the compiled host libgit.so so Push exercises the packaged-runtime execution model.
This commit is contained in:
@@ -19,8 +19,8 @@ android {
|
|||||||
applicationId = "solutions.tretter.githugandroid"
|
applicationId = "solutions.tretter.githugandroid"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 123
|
versionCode = 124
|
||||||
versionName = "0.1.122"
|
versionName = "0.1.123"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ package solutions.tretter.githugandroid
|
|||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.system.Os
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.nio.file.Files
|
||||||
|
|
||||||
class GitRepositoryRuntime private constructor(
|
class GitRepositoryRuntime private constructor(
|
||||||
private val context: Context?,
|
private val context: Context?,
|
||||||
@@ -11,6 +13,34 @@ class GitRepositoryRuntime private constructor(
|
|||||||
) {
|
) {
|
||||||
private companion object {
|
private companion object {
|
||||||
const val SyntheticRemoteUrl = "remote"
|
const val SyntheticRemoteUrl = "remote"
|
||||||
|
val RequiredGitCommandAliases = listOf(
|
||||||
|
"add",
|
||||||
|
"branch",
|
||||||
|
"checkout",
|
||||||
|
"commit",
|
||||||
|
"commit-tree",
|
||||||
|
"config",
|
||||||
|
"diff",
|
||||||
|
"fetch",
|
||||||
|
"index-pack",
|
||||||
|
"merge",
|
||||||
|
"merge-base",
|
||||||
|
"pack-objects",
|
||||||
|
"pull",
|
||||||
|
"push",
|
||||||
|
"read-tree",
|
||||||
|
"rebase",
|
||||||
|
"receive-pack",
|
||||||
|
"rev-list",
|
||||||
|
"rev-parse",
|
||||||
|
"show-ref",
|
||||||
|
"stash",
|
||||||
|
"status",
|
||||||
|
"symbolic-ref",
|
||||||
|
"unpack-objects",
|
||||||
|
"update-ref",
|
||||||
|
"upload-pack",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(context: Context) : this(
|
constructor(context: Context) : this(
|
||||||
@@ -816,11 +846,13 @@ class GitRepositoryRuntime private constructor(
|
|||||||
|
|
||||||
private fun runProcess(binary: File, workingDir: File, arguments: List<String>): ProcessExecutionResult {
|
private fun runProcess(binary: File, workingDir: File, arguments: List<String>): ProcessExecutionResult {
|
||||||
return try {
|
return try {
|
||||||
|
val gitExecPath = gitExecDirectory(binary)
|
||||||
val process = ProcessBuilder(listOf(binary.absolutePath) + arguments)
|
val process = ProcessBuilder(listOf(binary.absolutePath) + arguments)
|
||||||
.directory(workingDir)
|
.directory(workingDir)
|
||||||
.redirectErrorStream(true)
|
.redirectErrorStream(true)
|
||||||
.apply {
|
.apply {
|
||||||
environment()["HOME"] = workingDir.absolutePath
|
environment()["HOME"] = workingDir.absolutePath
|
||||||
|
environment()["GIT_EXEC_PATH"] = gitExecPath.absolutePath
|
||||||
environment()["GIT_CONFIG_NOSYSTEM"] = "1"
|
environment()["GIT_CONFIG_NOSYSTEM"] = "1"
|
||||||
environment()["GIT_AUTHOR_NAME"] = "GitHug"
|
environment()["GIT_AUTHOR_NAME"] = "GitHug"
|
||||||
environment()["GIT_AUTHOR_EMAIL"] = "githug@example.com"
|
environment()["GIT_AUTHOR_EMAIL"] = "githug@example.com"
|
||||||
@@ -837,6 +869,42 @@ class GitRepositoryRuntime private constructor(
|
|||||||
ProcessExecutionResult(exitCode = -1, outputLines = listOf("Native Git execution failed: ${error.message ?: error::class.java.simpleName}"))
|
ProcessExecutionResult(exitCode = -1, outputLines = listOf("Native Git execution failed: ${error.message ?: error::class.java.simpleName}"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun gitExecDirectory(binary: File): File {
|
||||||
|
val directory = if (context != null) {
|
||||||
|
File(context.filesDir, "git-exec")
|
||||||
|
} else {
|
||||||
|
File(binary.parentFile ?: binary.absoluteFile.parentFile ?: sandboxesRoot, "git-exec")
|
||||||
|
}
|
||||||
|
directory.mkdirs()
|
||||||
|
|
||||||
|
RequiredGitCommandAliases.forEach { command ->
|
||||||
|
val alias = File(directory, "git-$command")
|
||||||
|
if (!alias.exists()) {
|
||||||
|
createGitAlias(binary, alias)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return directory
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createGitAlias(binary: File, alias: File) {
|
||||||
|
if (context != null) {
|
||||||
|
try {
|
||||||
|
Os.symlink(binary.absolutePath, alias.absolutePath)
|
||||||
|
alias.setExecutable(true, false)
|
||||||
|
return
|
||||||
|
} catch (_: Exception) {
|
||||||
|
// Fall through to the portable options below.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Files.createSymbolicLink(alias.toPath(), binary.toPath())
|
||||||
|
} catch (_: Exception) {
|
||||||
|
binary.copyTo(alias, overwrite = true)
|
||||||
|
}
|
||||||
|
alias.setExecutable(true, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class ProcessExecutionResult(
|
private data class ProcessExecutionResult(
|
||||||
|
|||||||
Reference in New Issue
Block a user