Added remote branch deletion

This commit is contained in:
Abdelilah El Aissaoui 2022-02-04 18:53:21 +01:00
parent fc4d52b57a
commit 07703a66be
6 changed files with 121 additions and 39 deletions

View File

@ -9,6 +9,7 @@ import kotlinx.coroutines.isActive
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.ProgressMonitor import org.eclipse.jgit.lib.ProgressMonitor
import org.eclipse.jgit.lib.Ref
import org.eclipse.jgit.transport.* import org.eclipse.jgit.transport.*
import java.io.File import java.io.File
import javax.inject.Inject import javax.inject.Inject
@ -25,11 +26,7 @@ class RemoteOperationsManager @Inject constructor(
git git
.pull() .pull()
.setTransportConfigCallback { .setTransportConfigCallback {
if (it is SshTransport) { handleTransportCredentials(it)
it.sshSessionFactory = sessionManager.generateSshSessionFactory()
} else if (it is HttpTransport) {
it.credentialsProvider = HttpCredentialsProvider()
}
} }
.setRebase(rebase) .setRebase(rebase)
.setCredentialsProvider(CredentialsProvider.getDefault()) .setCredentialsProvider(CredentialsProvider.getDefault())
@ -44,11 +41,7 @@ class RemoteOperationsManager @Inject constructor(
.setRemote(remote.name) .setRemote(remote.name)
.setRefSpecs(remote.fetchRefSpecs) .setRefSpecs(remote.fetchRefSpecs)
.setTransportConfigCallback { .setTransportConfigCallback {
if (it is SshTransport) { handleTransportCredentials(it)
it.sshSessionFactory = sessionManager.generateSshSessionFactory()
} else if (it is HttpTransport) {
it.credentialsProvider = HttpCredentialsProvider()
}
} }
.setCredentialsProvider(CredentialsProvider.getDefault()) .setCredentialsProvider(CredentialsProvider.getDefault())
.call() .call()
@ -64,11 +57,7 @@ class RemoteOperationsManager @Inject constructor(
.setForce(force) .setForce(force)
.setPushTags() .setPushTags()
.setTransportConfigCallback { .setTransportConfigCallback {
if (it is SshTransport) { handleTransportCredentials(it)
it.sshSessionFactory = sessionManager.generateSshSessionFactory()
} else if (it is HttpTransport) {
it.credentialsProvider = HttpCredentialsProvider()
}
} }
.call() .call()
@ -87,6 +76,40 @@ class RemoteOperationsManager @Inject constructor(
} }
} }
private fun handleTransportCredentials(transport: Transport?) {
if (transport is SshTransport) {
transport.sshSessionFactory = sessionManager.generateSshSessionFactory()
} else if (transport is HttpTransport) {
transport.credentialsProvider = HttpCredentialsProvider()
}
}
suspend fun deleteBranch(git: Git, ref: Ref) = withContext(Dispatchers.IO) {
git
.branchDelete()
.setBranchNames(ref.name)
.call()
val branchSplit = ref.name.split("/").toMutableList()
val remoteName = branchSplit[2] // Remote name
repeat(3) {
branchSplit.removeAt(0)
}
val branchName = "refs/heads/${branchSplit.joinToString("/")}"
val refSpec = RefSpec()
.setSource(null)
.setDestination(branchName)
git.push()
.setTransportConfigCallback {
handleTransportCredentials(it)
}
.setRefSpecs(refSpec)
.setRemote(remoteName)
.call()
}
private val RemoteRefUpdate.Status.isRejected: Boolean private val RemoteRefUpdate.Status.isRejected: Boolean
get() { get() {
return this == RemoteRefUpdate.Status.REJECTED_NONFASTFORWARD || return this == RemoteRefUpdate.Status.REJECTED_NONFASTFORWARD ||
@ -140,11 +163,7 @@ class RemoteOperationsManager @Inject constructor(
}) })
.setTransportConfigCallback { .setTransportConfigCallback {
if (it is SshTransport) { handleTransportCredentials(it)
it.sshSessionFactory = sessionManager.generateSshSessionFactory()
} else if (it is HttpTransport) {
it.credentialsProvider = HttpCredentialsProvider()
}
} }
.call() .call()
@ -158,6 +177,7 @@ class RemoteOperationsManager @Inject constructor(
_cloneStatus.value = CloneStatus.None _cloneStatus.value = CloneStatus.None
} }
} }
sealed class CloneStatus { sealed class CloneStatus {

View File

@ -1,5 +1,7 @@
package app.ui package app.ui
import androidx.compose.foundation.ContextMenuArea
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@ -11,10 +13,16 @@ import app.extensions.simpleVisibleName
import app.git.RemoteInfo import app.git.RemoteInfo
import app.ui.components.SideMenuPanel import app.ui.components.SideMenuPanel
import app.ui.components.SideMenuSubentry import app.ui.components.SideMenuSubentry
import app.ui.components.VerticalExpandable
import app.ui.context_menu.remoteBranchesContextMenu
import app.viewmodels.RemotesViewModel import app.viewmodels.RemotesViewModel
import org.eclipse.jgit.lib.Ref
@Composable @Composable
fun Remotes(remotesViewModel: RemotesViewModel) { fun Remotes(
remotesViewModel: RemotesViewModel,
onBranchClicked: (Ref) -> Unit,
) {
val remotes by remotesViewModel.remotes.collectAsState() val remotes by remotesViewModel.remotes.collectAsState()
val itemsCount = remember(remotes) { val itemsCount = remember(remotes) {
@ -28,28 +36,48 @@ fun Remotes(remotesViewModel: RemotesViewModel) {
items = remotes, items = remotes,
itemsCountForMaxHeight = itemsCount, itemsCountForMaxHeight = itemsCount,
itemContent = { remoteInfo -> itemContent = { remoteInfo ->
RemoteRow(remoteInfo) RemoteRow(
remote = remoteInfo,
onBranchClicked = { branch -> onBranchClicked(branch) },
onDeleteBranch = { branch -> remotesViewModel.deleteBranch(branch) }
)
} }
) )
} }
@OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
private fun RemoteRow( private fun RemoteRow(
remote: RemoteInfo, remote: RemoteInfo,
onBranchClicked: (Ref) -> Unit,
onDeleteBranch: (Ref) -> Unit,
) { ) {
SideMenuSubentry( VerticalExpandable(
text = remote.remoteConfig.name, header = {
iconResourcePath = "cloud.svg",
)
val branches = remote.branchesList
Column {
branches.forEach { branch ->
SideMenuSubentry( SideMenuSubentry(
text = branch.simpleVisibleName, text = remote.remoteConfig.name,
extraPadding = 8.dp, iconResourcePath = "cloud.svg",
iconResourcePath = "branch.svg",
) )
} }
) {
val branches = remote.branchesList
Column {
branches.forEach { branch ->
ContextMenuArea(
items = {
remoteBranchesContextMenu(
onDeleteBranch = { onDeleteBranch(branch) }
)
}
) {
SideMenuSubentry(
text = branch.simpleVisibleName,
extraPadding = 8.dp,
iconResourcePath = "branch.svg",
onClick = { onBranchClicked(branch) }
)
}
}
}
} }
} }

View File

@ -65,7 +65,12 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
tabViewModel.newSelectedRef(it.objectId) tabViewModel.newSelectedRef(it.objectId)
} }
) )
Remotes(remotesViewModel = tabViewModel.remotesViewModel) Remotes(
remotesViewModel = tabViewModel.remotesViewModel,
onBranchClicked = {
tabViewModel.newSelectedRef(it.objectId)
}
)
Tags( Tags(
tagsViewModel = tabViewModel.tagsViewModel, tagsViewModel = tabViewModel.tagsViewModel,
onTagClicked = { onTagClicked = {

View File

@ -25,14 +25,19 @@ fun SideMenuSubentry(
iconResourcePath: String, iconResourcePath: String,
bold: Boolean = false, bold: Boolean = false,
extraPadding: Dp = 0.dp, extraPadding: Dp = 0.dp,
onClick: () -> Unit = {}, onClick: (() -> Unit)? = null,
additionalInfo: @Composable () -> Unit = {} additionalInfo: @Composable () -> Unit = {}
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
.height(ENTRY_HEIGHT.dp) .height(ENTRY_HEIGHT.dp)
.fillMaxWidth() .fillMaxWidth()
.clickable(onClick = onClick) .run {
if(onClick != null)
clickable(onClick = onClick)
else
this
}
.padding(start = extraPadding), .padding(start = extraPadding),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {

View File

@ -0,0 +1,17 @@
@file:OptIn(ExperimentalFoundationApi::class)
package app.ui.context_menu
import androidx.compose.foundation.ContextMenuItem
import androidx.compose.foundation.ExperimentalFoundationApi
fun remoteBranchesContextMenu(
onDeleteBranch: () -> Unit
): List<ContextMenuItem> {
return mutableListOf(
ContextMenuItem(
label = "Delete remote branch",
onClick = onDeleteBranch
),
)
}

View File

@ -1,18 +1,19 @@
package app.viewmodels package app.viewmodels
import app.git.BranchesManager import app.git.*
import app.git.RemoteInfo
import app.git.RemotesManager
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.Ref
import javax.inject.Inject import javax.inject.Inject
class RemotesViewModel @Inject constructor( class RemotesViewModel @Inject constructor(
private val remotesManager: RemotesManager, private val remotesManager: RemotesManager,
private val remoteOperationsManager: RemoteOperationsManager,
private val branchesManager: BranchesManager, private val branchesManager: BranchesManager,
private val tabState: TabState,
) { ) {
private val _remotes = MutableStateFlow<List<RemoteInfo>>(listOf()) private val _remotes = MutableStateFlow<List<RemoteInfo>>(listOf())
val remotes: StateFlow<List<RemoteInfo>> val remotes: StateFlow<List<RemoteInfo>>
@ -34,6 +35,12 @@ class RemotesViewModel @Inject constructor(
_remotes.value = remoteInfoList _remotes.value = remoteInfoList
} }
fun deleteBranch(ref: Ref) = tabState.safeProcessing { git ->
remoteOperationsManager.deleteBranch(git, ref)
return@safeProcessing RefreshType.ALL_DATA
}
suspend fun refresh(git: Git) = withContext(Dispatchers.IO) { suspend fun refresh(git: Git) = withContext(Dispatchers.IO) {
loadRemotes(git) loadRemotes(git)
} }