diff --git a/CHANGELOG.md b/CHANGELOG.md index 58e172d3..2f0d1ca1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file. - On Android 11, Autofill will use the new [inline autofill](https://developer.android.com/guide/topics/text/ime-autofill#configure-provider) UI that integrates Autofill results into your keyboard app. - Invalid `.gpg-id` files can now be fixed automatically by deleting them and then trying to create a new password. - Suggest users to re-clone repository when it is deemed to be broken +- Allow doing a merge instead of a rebase when pulling or syncing ### Fixed diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/git/base/BaseGitActivity.kt b/app/src/main/java/dev/msfjarvis/aps/ui/git/base/BaseGitActivity.kt index 8b288d00..e5737227 100644 --- a/app/src/main/java/dev/msfjarvis/aps/ui/git/base/BaseGitActivity.kt +++ b/app/src/main/java/dev/msfjarvis/aps/ui/git/base/BaseGitActivity.kt @@ -66,9 +66,9 @@ abstract class BaseGitActivity : ContinuationContainerActivity() { } val op = when (operation) { GitOp.CLONE -> CloneOperation(this, GitSettings.url!!) - GitOp.PULL -> PullOperation(this) + GitOp.PULL -> PullOperation(this, GitSettings.rebaseOnPull) GitOp.PUSH -> PushOperation(this) - GitOp.SYNC -> SyncOperation(this) + GitOp.SYNC -> SyncOperation(this, GitSettings.rebaseOnPull) GitOp.BREAK_OUT_OF_DETACHED -> BreakOutOfDetached(this) GitOp.RESET -> ResetToRemoteOperation(this) } diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/git/config/GitConfigActivity.kt b/app/src/main/java/dev/msfjarvis/aps/ui/git/config/GitConfigActivity.kt index d354ce4e..2a31aeee 100644 --- a/app/src/main/java/dev/msfjarvis/aps/ui/git/config/GitConfigActivity.kt +++ b/app/src/main/java/dev/msfjarvis/aps/ui/git/config/GitConfigActivity.kt @@ -29,6 +29,7 @@ import dev.msfjarvis.aps.util.extensions.viewBinding import kotlinx.coroutines.launch import org.eclipse.jgit.lib.Constants import org.eclipse.jgit.lib.Repository +import org.eclipse.jgit.lib.RepositoryState class GitConfigActivity : BaseGitActivity() { @@ -79,10 +80,10 @@ class GitConfigActivity : BaseGitActivity() { val repo = PasswordRepository.getRepository(null) if (repo != null) { binding.gitHeadStatus.text = headStatusMsg(repo) - // enable the abort button only if we're rebasing - val isRebasing = repo.repositoryState.isRebasing - binding.gitAbortRebase.isEnabled = isRebasing - binding.gitAbortRebase.alpha = if (isRebasing) 1.0f else 0.5f + // enable the abort button only if we're rebasing or merging + val needsAbort = repo.repositoryState.isRebasing || repo.repositoryState == RepositoryState.MERGING + binding.gitAbortRebase.isEnabled = needsAbort + binding.gitAbortRebase.alpha = if (needsAbort) 1.0f else 0.5f } binding.gitLog.setOnClickListener { runCatching { diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/settings/RepositorySettings.kt b/app/src/main/java/dev/msfjarvis/aps/ui/settings/RepositorySettings.kt index 32d04c9b..22ac899a 100644 --- a/app/src/main/java/dev/msfjarvis/aps/ui/settings/RepositorySettings.kt +++ b/app/src/main/java/dev/msfjarvis/aps/ui/settings/RepositorySettings.kt @@ -56,6 +56,12 @@ class RepositorySettings(private val activity: FragmentActivity) : SettingsProvi override fun provideSettings(builder: PreferenceScreen.Builder) { builder.apply { + checkBox(PreferenceKeys.REBASE_ON_PULL) { + titleRes = R.string.pref_rebase_on_pull_title + summaryRes = R.string.pref_rebase_on_pull_summary + summaryOnRes = R.string.pref_rebase_on_pull_summary_on + defaultValue = true + } pref(PreferenceKeys.GIT_SERVER_INFO) { titleRes = R.string.pref_edit_git_server_settings visible = PasswordRepository.isGitRepo() diff --git a/app/src/main/java/dev/msfjarvis/aps/util/git/ErrorMessages.kt b/app/src/main/java/dev/msfjarvis/aps/util/git/ErrorMessages.kt index 44eb11e1..e03ebe0e 100644 --- a/app/src/main/java/dev/msfjarvis/aps/util/git/ErrorMessages.kt +++ b/app/src/main/java/dev/msfjarvis/aps/util/git/ErrorMessages.kt @@ -28,7 +28,8 @@ sealed class GitException(@StringRes res: Int, vararg fmt: String) : Exception(b */ sealed class PullException(@StringRes res: Int, vararg fmt: String) : GitException(res, *fmt) { - object PullRebaseFailed : PullException(R.string.git_pull_fail_error) + object PullRebaseFailed : PullException(R.string.git_pull_rebase_fail_error) + object PullMergeFailed : PullException(R.string.git_pull_merge_fail_error) } /** diff --git a/app/src/main/java/dev/msfjarvis/aps/util/git/GitCommandExecutor.kt b/app/src/main/java/dev/msfjarvis/aps/util/git/GitCommandExecutor.kt index 429ea2c5..eca74762 100644 --- a/app/src/main/java/dev/msfjarvis/aps/util/git/GitCommandExecutor.kt +++ b/app/src/main/java/dev/msfjarvis/aps/util/git/GitCommandExecutor.kt @@ -21,7 +21,6 @@ import kotlinx.coroutines.withContext import org.eclipse.jgit.api.CommitCommand import org.eclipse.jgit.api.PullCommand import org.eclipse.jgit.api.PushCommand -import org.eclipse.jgit.api.RebaseResult import org.eclipse.jgit.api.StatusCommand import org.eclipse.jgit.lib.PersonIdent import org.eclipse.jgit.transport.RemoteRefUpdate @@ -62,9 +61,14 @@ class GitCommandExecutor( val result = withContext(Dispatchers.IO) { command.call() } - val rr = result.rebaseResult - if (rr.status == RebaseResult.Status.STOPPED) { - throw PullException.PullRebaseFailed + if (result.rebaseResult != null) { + if (!result.rebaseResult.status.isSuccessful) { + throw PullException.PullRebaseFailed + } + } else if (result.mergeResult != null) { + if (!result.mergeResult.mergeStatus.isSuccessful) { + throw PullException.PullMergeFailed + } } } is PushCommand -> { diff --git a/app/src/main/java/dev/msfjarvis/aps/util/git/operation/BreakOutOfDetached.kt b/app/src/main/java/dev/msfjarvis/aps/util/git/operation/BreakOutOfDetached.kt index 1aff34de..635813e7 100644 --- a/app/src/main/java/dev/msfjarvis/aps/util/git/operation/BreakOutOfDetached.kt +++ b/app/src/main/java/dev/msfjarvis/aps/util/git/operation/BreakOutOfDetached.kt @@ -8,12 +8,13 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import dev.msfjarvis.aps.R import dev.msfjarvis.aps.util.git.sshj.ContinuationContainerActivity import org.eclipse.jgit.api.RebaseCommand +import org.eclipse.jgit.api.ResetCommand +import org.eclipse.jgit.lib.RepositoryState class BreakOutOfDetached(callingActivity: ContinuationContainerActivity) : GitOperation(callingActivity) { - override val commands = arrayOf( - // abort the rebase - git.rebase().setOperation(RebaseCommand.Operation.ABORT), + private val merging = repository.repositoryState == RepositoryState.MERGING + private val resetCommands = arrayOf( // git checkout -b conflict-branch git.checkout().setCreateBranch(true).setName("conflicting-$remoteBranch-${System.currentTimeMillis()}"), // push the changes @@ -22,7 +23,26 @@ class BreakOutOfDetached(callingActivity: ContinuationContainerActivity) : GitOp git.checkout().setName(remoteBranch), ) - override fun preExecute() = if (!git.repository.repositoryState.isRebasing) { + override val commands by lazy(LazyThreadSafetyMode.NONE) { + if (merging) { + // We need to run some non-command operations first + repository.writeMergeCommitMsg(null) + repository.writeMergeHeads(null) + arrayOf( + // reset hard back to our local HEAD + git.reset().setMode(ResetCommand.ResetType.HARD), + *resetCommands, + ) + } else { + arrayOf( + // abort the rebase + git.rebase().setOperation(RebaseCommand.Operation.ABORT), + *resetCommands, + ) + } + } + + override fun preExecute() = if (!git.repository.repositoryState.isRebasing && !merging) { MaterialAlertDialogBuilder(callingActivity) .setTitle(callingActivity.resources.getString(R.string.git_abort_and_push_title)) .setMessage(callingActivity.resources.getString(R.string.git_break_out_of_detached_unneeded)) diff --git a/app/src/main/java/dev/msfjarvis/aps/util/git/operation/PullOperation.kt b/app/src/main/java/dev/msfjarvis/aps/util/git/operation/PullOperation.kt index 7bee775a..938bc146 100644 --- a/app/src/main/java/dev/msfjarvis/aps/util/git/operation/PullOperation.kt +++ b/app/src/main/java/dev/msfjarvis/aps/util/git/operation/PullOperation.kt @@ -7,7 +7,10 @@ package dev.msfjarvis.aps.util.git.operation import dev.msfjarvis.aps.util.git.sshj.ContinuationContainerActivity import org.eclipse.jgit.api.GitCommand -class PullOperation(callingActivity: ContinuationContainerActivity) : GitOperation(callingActivity) { +class PullOperation( + callingActivity: ContinuationContainerActivity, + rebase: Boolean, +) : GitOperation(callingActivity) { /** * The story of why the pull operation is committing files goes like this: Once upon a time when @@ -26,6 +29,6 @@ class PullOperation(callingActivity: ContinuationContainerActivity) : GitOperati // Commit everything! If needed, obviously. git.commit().setAll(true).setMessage("[Android Password Store] Sync"), // Pull and rebase on top of the remote branch - git.pull().setRebase(true).setRemote("origin"), + git.pull().setRebase(rebase).setRemote("origin"), ) } diff --git a/app/src/main/java/dev/msfjarvis/aps/util/git/operation/SyncOperation.kt b/app/src/main/java/dev/msfjarvis/aps/util/git/operation/SyncOperation.kt index 512d6b48..e6a61f60 100644 --- a/app/src/main/java/dev/msfjarvis/aps/util/git/operation/SyncOperation.kt +++ b/app/src/main/java/dev/msfjarvis/aps/util/git/operation/SyncOperation.kt @@ -6,7 +6,10 @@ package dev.msfjarvis.aps.util.git.operation import dev.msfjarvis.aps.util.git.sshj.ContinuationContainerActivity -class SyncOperation(callingActivity: ContinuationContainerActivity) : GitOperation(callingActivity) { +class SyncOperation( + callingActivity: ContinuationContainerActivity, + rebase: Boolean, +) : GitOperation(callingActivity) { override val commands = arrayOf( // Stage all files @@ -16,7 +19,7 @@ class SyncOperation(callingActivity: ContinuationContainerActivity) : GitOperati // Commit everything! If needed, obviously. git.commit().setAll(true).setMessage("[Android Password Store] Sync"), // Pull and rebase on top of the remote branch - git.pull().setRebase(true).setRemote("origin"), + git.pull().setRebase(rebase).setRemote("origin"), // Push it all back git.push().setPushAll().setRemote("origin"), ) diff --git a/app/src/main/java/dev/msfjarvis/aps/util/settings/GitSettings.kt b/app/src/main/java/dev/msfjarvis/aps/util/settings/GitSettings.kt index 82af814b..0b5f0270 100644 --- a/app/src/main/java/dev/msfjarvis/aps/util/settings/GitSettings.kt +++ b/app/src/main/java/dev/msfjarvis/aps/util/settings/GitSettings.kt @@ -147,6 +147,14 @@ object GitSettings { } } + var rebaseOnPull + get() = settings.getBoolean(PreferenceKeys.REBASE_ON_PULL, true) + set(value) { + settings.edit { + putBoolean(PreferenceKeys.REBASE_ON_PULL, value) + } + } + sealed class UpdateConnectionSettingsResult { class MissingUsername(val newProtocol: Protocol) : UpdateConnectionSettingsResult() class AuthModeMismatch(val newProtocol: Protocol, val validModes: List) : UpdateConnectionSettingsResult() diff --git a/app/src/main/java/dev/msfjarvis/aps/util/settings/PreferenceKeys.kt b/app/src/main/java/dev/msfjarvis/aps/util/settings/PreferenceKeys.kt index 198be889..930a5427 100644 --- a/app/src/main/java/dev/msfjarvis/aps/util/settings/PreferenceKeys.kt +++ b/app/src/main/java/dev/msfjarvis/aps/util/settings/PreferenceKeys.kt @@ -82,4 +82,6 @@ object PreferenceKeys { const val PROXY_PORT = "proxy_port" const val PROXY_USERNAME = "proxy_username" const val PROXY_PASSWORD = "proxy_password" + + const val REBASE_ON_PULL = "rebase_on_pull" } diff --git a/app/src/main/res/layout/activity_git_config.xml b/app/src/main/res/layout/activity_git_config.xml index bd19f3d7..0cd5fad3 100644 --- a/app/src/main/res/layout/activity_git_config.xml +++ b/app/src/main/res/layout/activity_git_config.xml @@ -9,7 +9,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:padding="@dimen/activity_horizontal_margin" - tools:context="dev.msfjarvis.aps.git.GitConfigActivity" + tools:context="dev.msfjarvis.aps.ui.git.config.GitConfigActivity" tools:layout_editor_absoluteX="0dp" tools:layout_editor_absoluteY="81dp"> diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 5de9af33..7b45182b 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -314,7 +314,7 @@ a app desde unha fonte de confianza, como a Play Store, Amazon Appstore, F-Droid Establece chave GPG para o directorio Fallo descoñecido - Fallou a acción pull, estás nun head diferente. Utiliza \"axustes > utilidades git\", garda os cambios no remoto nunha nova rama e resolve o conflicto nun ordenador. + Fallou a acción pull, estás nun head diferente. Utiliza \"axustes > utilidades git\", garda os cambios no remoto nunha nova rama e resolve o conflicto nun ordenador. O push foi rexeitado polo remoto, executa pull antes de voltar a subilos de novo. Podes usar Sincronizar mellor que push/pull xa que inclú ambos. O push foi rexeitado polo remoto, razón: O remoto rexeitou o push non-fast-forward. Comproba a variable receive.denyNonFastForwards no ficheiro de configuración do repositorio de destino. diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 103283ca..ccce743c 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -313,7 +313,7 @@ Imposta la chiave GPG per la directory Errore sconosciuto - Pull non è riuscito, sei in un capo distaccato. Usando \"impostazioni e utilità di git\", salvi le tue modifiche in remoto in un nuovo ramo e risolvi il conflitto sul tuo computer. + Pull non è riuscito, sei in un capo distaccato. Usando \"impostazioni e utilità di git\", salvi le tue modifiche in remoto in un nuovo ramo e risolvi il conflitto sul tuo computer. Push è stato rifiutato da remoto, esegui pull prima di premere nuovamente. Puoi usare Sincronizza piuttosto che pull/push implementando entrambi Push è stato rifiutato da remoto, ragione: %1$s Remoto ha rifiutato il push non avanti veloce. Controlla la variabile receive.denyNonFastForwards nel file di configurazione della repository di destinazione. diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index ff6b5fc5..27094d17 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -312,7 +312,7 @@ Definir chave GPG para diretório Erro desconhecido - O pull falhou, você está em uma Head avulsa. Usando \"configurações > utils\" do git, salve suas alterações no remoto em uma nova branch e resolva o conflito no seu computador. + O pull falhou, você está em uma Head avulsa. Usando \"configurações > utils\" do git, salve suas alterações no remoto em uma nova branch e resolva o conflito no seu computador. Push rejeitado pelo remoto, execute o pull antes de fazer push novamente. Você pode usar Sincronização em vez de pull/push conforme implementa ambos Push rejeitado pelo remoto, razão: %1$s O remoto rejeito o push non-fast-foward. Cheque a variável receive.denyNonFastForwards no arquivo de configuração do repositório de destino. diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index f0e3c6d4..39701817 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -316,7 +316,7 @@ Установить GPG ключ для каталога Неизвестная ошибка - Не удалось получить изменения, вы находитесь в состоянии \"оторванной головы\". Используйте \"настройки > утилиты git\", сохраните ваши изменения в новую удаленную ветку и разрешите конфликты на своем компьютере. + Не удалось получить изменения, вы находитесь в состоянии \"оторванной головы\". Используйте \"настройки > утилиты git\", сохраните ваши изменения в новую удаленную ветку и разрешите конфликты на своем компьютере. Запись изменений была отклонена удаленным репозиторием, сначала пполучите изменения перед повторной записью. Вы можете использовать Синхронизацию вместо получения/записи изменений, т.к. она реализует оба подхда. Запись изменений была отклонена удаленным репозиторием, причина: %1$s Удаленный репозиторий отклонил запись изменений без быстрой перемотки вперед. Проверьте переменную receive.denyNonFastForwards в файле конфигурации репозитория назначения. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 46e85334..57af2db3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -147,6 +147,9 @@ No external repository selected Export passwords Exports the encrypted passwords to an external directory + Rebase on pull + When pulling or syncing, create a merge commit with upstream changes + When pulling or syncing, rebase commits that are not present in the remote repository Generate Password @@ -345,7 +348,9 @@ Unknown error - Pull has failed, you\'re in a detached head. Using "settings > git utils", save your changes to the remote in a new branch and resolve the conflict on your computer. + Pull has failed, you\'re in a detached head. Using "settings > git utils", save your changes to the remote in a new branch and resolve the conflict on your computer. + Merge has failed, you\'re in a conflicting state. TODO: Add a recovery method. + Push was rejected by remote, run pull before pushing again. You can use Synchronize rather than pull/push as it implements both Push was rejected by remote, reason: %1$s Remote rejected non-fast-forward push. Check receive.denyNonFastForwards variable in config file of destination repository.