diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 66f68e1..8e2ec07 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -19,8 +19,8 @@ android { applicationId = "solutions.tretter.githugandroid" minSdk = 26 targetSdk = 35 - versionCode = 123 - versionName = "0.1.122" + versionCode = 124 + versionName = "0.1.123" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true diff --git a/app/src/main/java/solutions/tretter/githugandroid/GitRuntime.kt b/app/src/main/java/solutions/tretter/githugandroid/GitRuntime.kt index 5c30161..27e7e67 100644 --- a/app/src/main/java/solutions/tretter/githugandroid/GitRuntime.kt +++ b/app/src/main/java/solutions/tretter/githugandroid/GitRuntime.kt @@ -2,7 +2,9 @@ package solutions.tretter.githugandroid import android.os.Build import android.content.Context +import android.system.Os import java.io.File +import java.nio.file.Files class GitRepositoryRuntime private constructor( private val context: Context?, @@ -11,6 +13,34 @@ class GitRepositoryRuntime private constructor( ) { private companion object { 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( @@ -816,11 +846,13 @@ class GitRepositoryRuntime private constructor( private fun runProcess(binary: File, workingDir: File, arguments: List): ProcessExecutionResult { return try { + val gitExecPath = gitExecDirectory(binary) val process = ProcessBuilder(listOf(binary.absolutePath) + arguments) .directory(workingDir) .redirectErrorStream(true) .apply { environment()["HOME"] = workingDir.absolutePath + environment()["GIT_EXEC_PATH"] = gitExecPath.absolutePath environment()["GIT_CONFIG_NOSYSTEM"] = "1" environment()["GIT_AUTHOR_NAME"] = "GitHug" 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}")) } } + + 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(