summaryrefslogtreecommitdiffstats
path: root/app/src
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-11-02 12:16:42 +0100
committerDanilo M. <danix@danix.xyz>2026-11-02 12:16:42 +0100
commite33c0ada6e09d309938325a5e6d914514e14de72 (patch)
treecdb3c4dabbe75380970930f801475e6578a6d7d6 /app/src
parentc9a53ac6c1b59572f8d6cba8f41e6fcd242c4fa1 (diff)
downloadHardenedPass-e33c0ada6e09d309938325a5e6d914514e14de72.tar.gz
HardenedPass-e33c0ada6e09d309938325a5e6d914514e14de72.zip
fix(git): keep the branch name when breaking out of a failed rebase
When a pull with rebase stops on a conflict, HEAD is detached and getCurrentBranch() returned null. "Abort rebase and push new branch" then pushed the local commits to "conflicting-null-<time>" and failed to switch back with "Branch name <null> is not allowed", leaving the repository on the conflict branch. Fall back to the branch JGit records in rebase-merge/head-name (or rebase-apply/head-name) while a rebase is in progress. Found while driving the conflict flow on an emulator against the e2e test server; verified there: the edit is pushed to conflicting-master-<time> and the operation completes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'app/src')
-rw-r--r--app/src/main/java/app/passwordstore/data/repo/PasswordRepository.kt17
-rw-r--r--app/src/test/java/app/passwordstore/data/repo/PasswordRepositoryTest.kt28
2 files changed, 43 insertions, 2 deletions
diff --git a/app/src/main/java/app/passwordstore/data/repo/PasswordRepository.kt b/app/src/main/java/app/passwordstore/data/repo/PasswordRepository.kt
index 71d771e1..7287044e 100644
--- a/app/src/main/java/app/passwordstore/data/repo/PasswordRepository.kt
+++ b/app/src/main/java/app/passwordstore/data/repo/PasswordRepository.kt
@@ -130,7 +130,10 @@ object PasswordRepository {
return repository
}
- /** Get the currently checked out branch. */
+ /**
+ * Get the currently checked out branch. While a rebase is stopped HEAD is detached, so this
+ * returns the branch being rebased instead.
+ */
fun getCurrentBranch(): String? {
val repository = repository ?: return null
val headRef = repository.findRef(Constants.HEAD) ?: return null
@@ -138,10 +141,20 @@ object PasswordRepository {
val branchName = headRef.target.name
Repository.shortenRefName(branchName)
} else {
- null
+ rebasedBranch(repository.directory)
}
}
+ /** Branch recorded by an in-progress rebase in [gitDir], or null when no rebase is running. */
+ fun rebasedBranch(gitDir: File): String? =
+ listOf("rebase-merge", "rebase-apply")
+ .map { gitDir.resolve(it).resolve("head-name") }
+ .firstOrNull { it.isFile }
+ ?.readText()
+ ?.trim()
+ ?.takeIf { it.startsWith(Constants.R_HEADS) }
+ ?.let(Repository::shortenRefName)
+
/**
* Gets the .gpg files in a directory
*
diff --git a/app/src/test/java/app/passwordstore/data/repo/PasswordRepositoryTest.kt b/app/src/test/java/app/passwordstore/data/repo/PasswordRepositoryTest.kt
new file mode 100644
index 00000000..1ebe9840
--- /dev/null
+++ b/app/src/test/java/app/passwordstore/data/repo/PasswordRepositoryTest.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+package app.passwordstore.data.repo
+
+import java.nio.file.Files
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertNull
+
+class PasswordRepositoryTest {
+
+ @Test
+ fun rebasedBranchReadsTheBranchOfAStoppedRebase() {
+ val gitDir = Files.createTempDirectory("aps-git").toFile()
+ assertNull(PasswordRepository.rebasedBranch(gitDir))
+
+ val headName = gitDir.resolve("rebase-merge").apply { mkdirs() }.resolve("head-name")
+ headName.writeText("refs/heads/master\n")
+ assertEquals("master", PasswordRepository.rebasedBranch(gitDir))
+
+ // A rebase of a detached HEAD records "detached HEAD", which is not a branch.
+ headName.writeText("detached HEAD\n")
+ assertNull(PasswordRepository.rebasedBranch(gitDir))
+ gitDir.deleteRecursively()
+ }
+}