diff --git a/src/main/kotlin/app/git/GitManager.kt b/src/main/kotlin/app/git/GitManager.kt index 436a434..5ae9291 100644 --- a/src/main/kotlin/app/git/GitManager.kt +++ b/src/main/kotlin/app/git/GitManager.kt @@ -210,10 +210,12 @@ class GitManager @Inject constructor( fun resetStaged(diffEntry: DiffEntry) = managerScope.launch { statusManager.reset(safeGit, diffEntry, staged = true) + loadLog() } fun resetUnstaged(diffEntry: DiffEntry) = managerScope.launch { statusManager.reset(safeGit, diffEntry, staged = false) + loadLog() } fun statusShouldBeUpdated() { @@ -254,6 +256,13 @@ class GitManager @Inject constructor( } } + fun resetToCommit(revCommit: RevCommit, resetType: ResetType) = managerScope.launch { + safeProcessing { + logManager.resetToCommit(safeGit, revCommit, resetType = resetType) + refreshRepositoryInfo() + } + } + fun createBranchOnCommit(branch: String, revCommit: RevCommit) = managerScope.launch { safeProcessing { branchesManager.createBranchOnCommit(safeGit, branch, revCommit) diff --git a/src/main/kotlin/app/git/LogManager.kt b/src/main/kotlin/app/git/LogManager.kt index d2d186c..48ce02d 100644 --- a/src/main/kotlin/app/git/LogManager.kt +++ b/src/main/kotlin/app/git/LogManager.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.withContext import org.eclipse.jgit.api.Git +import org.eclipse.jgit.api.ResetCommand import org.eclipse.jgit.lib.Constants import org.eclipse.jgit.lib.ObjectId import org.eclipse.jgit.lib.Ref @@ -70,8 +71,26 @@ class LogManager @Inject constructor( .include(revCommit) .call() } -} + suspend fun resetToCommit(git: Git, revCommit: RevCommit, resetType: ResetType) = withContext(Dispatchers.IO) { + val reset = when(resetType) { + ResetType.SOFT -> ResetCommand.ResetType.SOFT + ResetType.MIXED -> ResetCommand.ResetType.MIXED + ResetType.HARD -> ResetCommand.ResetType.HARD + } + git + .reset() + .setMode(reset) + .setRef(revCommit.name) + .call() + } +} +// TODO Move this to +enum class ResetType { + SOFT, + MIXED, + HARD, +} sealed class LogStatus { object Loading : LogStatus() diff --git a/src/main/kotlin/app/ui/Log.kt b/src/main/kotlin/app/ui/Log.kt index 1247689..8ad3acf 100644 --- a/src/main/kotlin/app/ui/Log.kt +++ b/src/main/kotlin/app/ui/Log.kt @@ -29,6 +29,7 @@ import app.extensions.simpleName import app.extensions.toSmartSystemString import app.git.GitManager import app.git.LogStatus +import app.git.ResetType import app.git.graph.GraphNode import app.theme.headerBackground import app.theme.headerText @@ -38,6 +39,7 @@ import app.ui.components.ScrollableLazyColumn import app.ui.dialogs.MergeDialog import app.ui.dialogs.NewBranchDialog import app.ui.dialogs.NewTagDialog +import app.ui.dialogs.ResetBranchDialog import org.eclipse.jgit.lib.ObjectIdRef import org.eclipse.jgit.lib.Ref import org.eclipse.jgit.revwalk.RevCommit @@ -67,6 +69,7 @@ fun Log( onUncommitedChangesSelected: () -> Unit, onCheckoutCommit: (graphNode: GraphNode) -> Unit, onRevertCommit: (graphNode: GraphNode) -> Unit, + onResetToCommit: (graphNode: GraphNode, resetType: ResetType) -> Unit, onCreateBranchOnCommit: (branchName: String, graphNode: GraphNode) -> Unit, onCreateTagOnCommit: (tagName: String, graphNode: GraphNode) -> Unit, onCheckoutRef: (ref: Ref) -> Unit, @@ -247,6 +250,23 @@ fun Log( onClick = { onRevertCommit(item) } + ), + + ContextMenuItem( + label = "Reset current branch to this commit", + onClick = { + dialogManager.show { + ResetBranchDialog( + onReject = { + dialogManager.dismiss() + }, + onAccept = { resetType -> + onResetToCommit(item, resetType) + dialogManager.dismiss() + } + ) + } + } ) ) }, diff --git a/src/main/kotlin/app/ui/RepositoryOpen.kt b/src/main/kotlin/app/ui/RepositoryOpen.kt index c7bef03..e56c350 100644 --- a/src/main/kotlin/app/ui/RepositoryOpen.kt +++ b/src/main/kotlin/app/ui/RepositoryOpen.kt @@ -99,6 +99,9 @@ fun RepositoryOpenPage(gitManager: GitManager, dialogManager: DialogManager) { onRevertCommit = { graphNode -> gitManager.revertCommit(graphNode) }, + onResetToCommit = { graphNode, resetType -> + gitManager.resetToCommit(graphNode, resetType) + }, onCreateBranchOnCommit = { branch, graphNode -> gitManager.createBranchOnCommit(branch, graphNode) }, diff --git a/src/main/kotlin/app/ui/dialogs/ResetDialog.kt b/src/main/kotlin/app/ui/dialogs/ResetDialog.kt new file mode 100644 index 0000000..64bf7e5 --- /dev/null +++ b/src/main/kotlin/app/ui/dialogs/ResetDialog.kt @@ -0,0 +1,113 @@ +package app.ui.dialogs + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.mouseClickable +import androidx.compose.material.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusOrder +import androidx.compose.ui.input.pointer.isPrimaryPressed +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import app.git.ResetType + +@Composable +fun ResetBranchDialog( + onReject: () -> Unit, + onAccept: (resetType: ResetType) -> Unit +) { + var resetType by remember { mutableStateOf(ResetType.MIXED) } + + Column( + modifier = Modifier + .background(MaterialTheme.colors.background), + verticalArrangement = Arrangement.Center, + ) { + RadioButtonText( + selected = resetType == ResetType.SOFT, + onClick = { + resetType = ResetType.SOFT + }, + text = "Soft reset" + ) + RadioButtonText( + selected = resetType == ResetType.MIXED, + onClick = { + resetType = ResetType.MIXED + }, + text = "Mixed reset" + ) + RadioButtonText( + selected = resetType == ResetType.HARD, + onClick = { + resetType = ResetType.HARD + }, + text = "Hard reset" + ) + Row( + modifier = Modifier + .padding(top = 16.dp) + .align(Alignment.End) + ) { + TextButton( + modifier = Modifier.padding(end = 8.dp), + onClick = { + onReject() + } + ) { + Text("Cancel") + } + Button( + onClick = { + onAccept(resetType) + } + ) { + Text("Reset branch") + } + } + } +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun RadioButtonText( + selected: Boolean, + text: String, + onClick: (() -> Unit)?, + modifier: Modifier = Modifier, + enabled: Boolean = true, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + colors: RadioButtonColors = RadioButtonDefaults.colors() +) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .mouseClickable { + if(this.buttons.isPrimaryPressed) { + if (onClick != null) { + onClick() + } + } + } + ) { + RadioButton( + selected, + onClick, + modifier, + enabled, + interactionSource, + colors + ) + + Text( + text = text, + modifier = Modifier.padding(horizontal = 8.dp) + ) + } +} \ No newline at end of file