Added push with lease + fixed push after changing tracking branch

Fixes #147
This commit is contained in:
Abdelilah El Aissaoui 2023-07-16 19:24:30 +02:00
parent 333d57e162
commit 3611f3339c
No known key found for this signature in database
GPG Key ID: 7587FC860F594869
5 changed files with 101 additions and 14 deletions

View File

@ -9,11 +9,15 @@ import javax.inject.Inject
class GetTrackingBranchUseCase @Inject constructor() {
operator fun invoke(git: Git, ref: Ref): TrackingBranch? {
return this.invoke(git, ref.simpleName)
}
operator fun invoke(git: Git, refName: String): TrackingBranch? {
val repository: Repository = git.repository
val config: Config = repository.config
val remote: String? = config.getString("branch", ref.simpleName, "remote")
val branch: String? = config.getString("branch", ref.simpleName, "merge")
val remote: String? = config.getString("branch", refName, "remote")
val branch: String? = config.getString("branch", refName, "merge")
if (remote != null && branch != null) {
return TrackingBranch(remote, branch.removePrefix(BranchesConstants.UPSTREAM_BRANCH_CONFIG_PREFIX))

View File

@ -1,28 +1,70 @@
package com.jetpackduba.gitnuro.git.remote_operations
import com.jetpackduba.gitnuro.git.branches.GetTrackingBranchUseCase
import com.jetpackduba.gitnuro.git.isRejected
import com.jetpackduba.gitnuro.git.statusMessage
import com.jetpackduba.gitnuro.preferences.AppSettings
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.isActive
import kotlinx.coroutines.withContext
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.Constants
import org.eclipse.jgit.lib.ProgressMonitor
import org.eclipse.jgit.transport.RefLeaseSpec
import org.eclipse.jgit.transport.RefSpec
import javax.inject.Inject
class PushBranchUseCase @Inject constructor(
private val handleTransportUseCase: HandleTransportUseCase,
private val getTrackingBranchUseCase: GetTrackingBranchUseCase,
private val appSettings: AppSettings,
) {
suspend operator fun invoke(git: Git, force: Boolean, pushTags: Boolean) = withContext(Dispatchers.IO) {
val currentBranchRefSpec = git.repository.fullBranch
val currentBranch = git.repository.fullBranch
val tracking = getTrackingBranchUseCase(git, git.repository.branch)
val refSpecStr = if (tracking != null) {
"$currentBranch:${Constants.R_HEADS}${tracking.branch}"
} else {
currentBranch
}
val pushResult = git
.push()
.setRefSpecs(RefSpec(currentBranchRefSpec))
.setRefSpecs(RefSpec(refSpecStr))
.run {
if (tracking != null) {
setRemote(tracking.remote)
} else {
this
}
}
.setForce(force)
.apply {
if (pushTags)
.run {
if (force && appSettings.pushWithLease) {
if (tracking != null) {
val remoteBranchName = "${Constants.R_REMOTES}$remote/${tracking.branch}"
val remoteBranchRef = git.repository.findRef(remoteBranchName)
if (remoteBranchRef != null) {
return@run setRefLeaseSpecs(
RefLeaseSpec(
"${Constants.R_HEADS}${tracking.branch}",
remoteBranchRef.objectId.name
)
)
}
}
}
return@run this
}
.run {
if (pushTags) {
setPushTags()
} else {
this
}
}
.setTransportConfigCallback { handleTransportUseCase(it, git) }
.setProgressMonitor(object : ProgressMonitor {
@ -35,14 +77,28 @@ class PushBranchUseCase @Inject constructor(
})
.call()
val results =
pushResult.map { it.remoteUpdates.filter { remoteRefUpdate -> remoteRefUpdate.status.isRejected } }
.flatten()
val results = pushResult
.map {
it.remoteUpdates.filter { remoteRefUpdate -> remoteRefUpdate.status.isRejected }
}
.flatten()
if (results.isNotEmpty()) {
val error = StringBuilder()
results.forEach { result ->
error.append(result.statusMessage)
val statusMessage = result.statusMessage
val extraMessage = if (statusMessage == "Ref rejected, old object id in remote has changed.") {
"Force push can't be completed without fetching first the remote changes."
} else
null
error.append(statusMessage)
if (extraMessage != null) {
error.append("\n")
error.append(extraMessage)
}
error.append("\n")
}

View File

@ -7,10 +7,7 @@ import com.jetpackduba.gitnuro.theme.ColorsScheme
import com.jetpackduba.gitnuro.theme.Theme
import com.jetpackduba.gitnuro.viewmodels.TextDiffType
import com.jetpackduba.gitnuro.viewmodels.textDiffTypeFromValue
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.*
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import java.io.File
@ -37,6 +34,7 @@ private const val PREF_TERMINAL_PATH = "terminalPath"
private const val PREF_GIT_FF_MERGE = "gitFFMerge"
private const val PREF_GIT_PULL_REBASE = "gitPullRebase"
private const val PREF_GIT_PUSH_WITH_LEASE = "gitPushWithLease"
private const val DEFAULT_COMMITS_LIMIT = 1000
private const val DEFAULT_COMMITS_LIMIT_ENABLED = true
@ -62,6 +60,9 @@ class AppSettings @Inject constructor() {
private val _pullRebaseFlow = MutableStateFlow(pullRebase)
val pullRebaseFlow = _pullRebaseFlow.asStateFlow()
private val _pushWithLeaseFlow = MutableStateFlow(pushWithLease)
val pushWithLeaseFlow: StateFlow<Boolean> = _pushWithLeaseFlow.asStateFlow()
private val _commitsLimitFlow = MutableSharedFlow<Int>()
val commitsLimitFlow = _commitsLimitFlow.asSharedFlow()
@ -164,6 +165,15 @@ class AppSettings @Inject constructor() {
_pullRebaseFlow.value = value
}
var pushWithLease: Boolean
get() {
return preferences.getBoolean(PREF_GIT_PUSH_WITH_LEASE, true)
}
set(value) {
preferences.putBoolean(PREF_GIT_PUSH_WITH_LEASE, value)
_pushWithLeaseFlow.value = value
}
val commitsLimit: Int
get() {
return preferences.getInt(PREF_COMMITS_LIMIT, DEFAULT_COMMITS_LIMIT)

View File

@ -219,6 +219,7 @@ private fun CommitsHistory(settingsViewModel: SettingsViewModel) {
@Composable
private fun RemoteActions(settingsViewModel: SettingsViewModel) {
val pullRebase by settingsViewModel.pullRebaseFlow.collectAsState()
val pushWithLease by settingsViewModel.pushWithLeaseFlow.collectAsState()
SettingToggle(
title = "Pull with rebase as default",
@ -228,6 +229,15 @@ private fun RemoteActions(settingsViewModel: SettingsViewModel) {
settingsViewModel.pullRebase = value
}
)
SettingToggle(
title = "Force push with lease",
subtitle = "Check if the local version remote branch is up to date to avoid accidentally overriding unintended commits",
value = pushWithLease,
onValueChanged = { value ->
settingsViewModel.pushWithLease = value
}
)
}
@Composable

View File

@ -24,6 +24,7 @@ class SettingsViewModel @Inject constructor(
val themeState = appSettings.themeState
val ffMergeFlow = appSettings.ffMergeFlow
val pullRebaseFlow = appSettings.pullRebaseFlow
val pushWithLeaseFlow = appSettings.pushWithLeaseFlow
val commitsLimitEnabledFlow = appSettings.commitsLimitEnabledFlow
val swapUncommitedChangesFlow = appSettings.swapUncommitedChangesFlow
val terminalPathFlow = appSettings.terminalPathFlow
@ -58,6 +59,12 @@ class SettingsViewModel @Inject constructor(
appSettings.pullRebase = value
}
var pushWithLease: Boolean
get() = appSettings.pushWithLease
set(value) {
appSettings.pushWithLease = value
}
var theme: Theme
get() = appSettings.theme
set(value) {