Added remote branch deletion
This commit is contained in:
parent
fc4d52b57a
commit
07703a66be
@ -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 {
|
||||||
|
@ -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) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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 = {
|
||||||
|
@ -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,
|
||||||
) {
|
) {
|
||||||
|
@ -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
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
@ -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)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user