diff options
| author | Danilo M. <danix@danix.xyz> | 2026-11-02 12:16:42 +0100 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-11-02 12:16:42 +0100 |
| commit | e33c0ada6e09d309938325a5e6d914514e14de72 (patch) | |
| tree | cdb3c4dabbe75380970930f801475e6578a6d7d6 | |
| parent | c9a53ac6c1b59572f8d6cba8f41e6fcd242c4fa1 (diff) | |
| download | HardenedPass-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>
| -rw-r--r-- | app/src/main/java/app/passwordstore/data/repo/PasswordRepository.kt | 17 | ||||
| -rw-r--r-- | app/src/test/java/app/passwordstore/data/repo/PasswordRepositoryTest.kt | 28 |
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() + } +} |
