Implemented edit, drop & restore of Rebase interactive state even if started from external software
Fixes #143 and #65
This commit is contained in:
parent
1e012d759b
commit
da9a5c1f17
@ -0,0 +1,58 @@
|
|||||||
|
package com.jetpackduba.gitnuro
|
||||||
|
|
||||||
|
import com.jetpackduba.gitnuro.di.TabScope
|
||||||
|
import com.jetpackduba.gitnuro.git.RefreshType
|
||||||
|
import com.jetpackduba.gitnuro.git.TabState
|
||||||
|
import com.jetpackduba.gitnuro.git.rebase.GetRebaseInteractiveStateUseCase
|
||||||
|
import com.jetpackduba.gitnuro.git.rebase.RebaseInteractiveState
|
||||||
|
import com.jetpackduba.gitnuro.git.repository.GetRepositoryStateUseCase
|
||||||
|
import com.jetpackduba.gitnuro.logging.printLog
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.eclipse.jgit.api.Git
|
||||||
|
import org.eclipse.jgit.lib.RepositoryState
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private const val TAG = "SharedRepositoryStateMa"
|
||||||
|
|
||||||
|
@TabScope
|
||||||
|
class SharedRepositoryStateManager @Inject constructor(
|
||||||
|
private val tabState: TabState,
|
||||||
|
private val getRebaseInteractiveStateUseCase: GetRebaseInteractiveStateUseCase,
|
||||||
|
private val getRepositoryStateUseCase: GetRepositoryStateUseCase,
|
||||||
|
tabScope: CoroutineScope,
|
||||||
|
) {
|
||||||
|
private val _repositoryState = MutableStateFlow(RepositoryState.SAFE)
|
||||||
|
val repositoryState = _repositoryState.asStateFlow()
|
||||||
|
|
||||||
|
private val _rebaseInteractiveState = MutableStateFlow<RebaseInteractiveState>(RebaseInteractiveState.None)
|
||||||
|
val rebaseInteractiveState = _rebaseInteractiveState.asStateFlow()
|
||||||
|
|
||||||
|
init {
|
||||||
|
tabScope.apply {
|
||||||
|
launch {
|
||||||
|
tabState.refreshFlowFiltered(RefreshType.ALL_DATA, RefreshType.REBASE_INTERACTIVE_STATE) {
|
||||||
|
updateRebaseInteractiveState(tabState.git)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
launch {
|
||||||
|
tabState.refreshFlowFiltered(RefreshType.ALL_DATA, RefreshType.REPO_STATE) {
|
||||||
|
updateRepositoryState(tabState.git)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun updateRepositoryState(git: Git) {
|
||||||
|
_repositoryState.value = getRepositoryStateUseCase(git)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun updateRebaseInteractiveState(git: Git) {
|
||||||
|
val newRepositoryState = getRebaseInteractiveStateUseCase(git)
|
||||||
|
printLog(TAG, "Refreshing repository state $newRepositoryState")
|
||||||
|
|
||||||
|
_rebaseInteractiveState.value = newRepositoryState
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ package com.jetpackduba.gitnuro.git
|
|||||||
import com.jetpackduba.gitnuro.managers.ErrorsManager
|
import com.jetpackduba.gitnuro.managers.ErrorsManager
|
||||||
import com.jetpackduba.gitnuro.di.TabScope
|
import com.jetpackduba.gitnuro.di.TabScope
|
||||||
import com.jetpackduba.gitnuro.extensions.delayedStateChange
|
import com.jetpackduba.gitnuro.extensions.delayedStateChange
|
||||||
|
import com.jetpackduba.gitnuro.git.log.FindCommitUseCase
|
||||||
import com.jetpackduba.gitnuro.logging.printError
|
import com.jetpackduba.gitnuro.logging.printError
|
||||||
import com.jetpackduba.gitnuro.managers.newErrorNow
|
import com.jetpackduba.gitnuro.managers.newErrorNow
|
||||||
import com.jetpackduba.gitnuro.ui.SelectedItem
|
import com.jetpackduba.gitnuro.ui.SelectedItem
|
||||||
@ -10,6 +11,7 @@ import kotlinx.coroutines.*
|
|||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.lib.ObjectId
|
import org.eclipse.jgit.lib.ObjectId
|
||||||
|
import org.eclipse.jgit.lib.RepositoryState
|
||||||
import org.eclipse.jgit.revwalk.RevCommit
|
import org.eclipse.jgit.revwalk.RevCommit
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -33,6 +35,7 @@ sealed interface ProcessingState {
|
|||||||
class TabState @Inject constructor(
|
class TabState @Inject constructor(
|
||||||
val errorsManager: ErrorsManager,
|
val errorsManager: ErrorsManager,
|
||||||
private val scope: CoroutineScope,
|
private val scope: CoroutineScope,
|
||||||
|
private val findCommitUseCase: FindCommitUseCase,
|
||||||
) {
|
) {
|
||||||
private val _selectedItem = MutableStateFlow<SelectedItem>(SelectedItem.UncommitedChanges)
|
private val _selectedItem = MutableStateFlow<SelectedItem>(SelectedItem.UncommitedChanges)
|
||||||
val selectedItem: StateFlow<SelectedItem> = _selectedItem
|
val selectedItem: StateFlow<SelectedItem> = _selectedItem
|
||||||
@ -247,15 +250,16 @@ class TabState @Inject constructor(
|
|||||||
if (objectId == null) {
|
if (objectId == null) {
|
||||||
newSelectedItem(SelectedItem.None)
|
newSelectedItem(SelectedItem.None)
|
||||||
} else {
|
} else {
|
||||||
val commit = findCommit(git, objectId)
|
val commit = findCommitUseCase(git, objectId)
|
||||||
|
|
||||||
|
if(commit == null) {
|
||||||
|
newSelectedItem(SelectedItem.None)
|
||||||
|
} else {
|
||||||
val newSelectedItem = SelectedItem.Ref(commit)
|
val newSelectedItem = SelectedItem.Ref(commit)
|
||||||
newSelectedItem(newSelectedItem)
|
newSelectedItem(newSelectedItem)
|
||||||
_taskEvent.emit(TaskEvent.ScrollToGraphItem(newSelectedItem))
|
_taskEvent.emit(TaskEvent.ScrollToGraphItem(newSelectedItem))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findCommit(git: Git, objectId: ObjectId): RevCommit {
|
|
||||||
return git.repository.parseCommit(objectId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun newSelectedItem(selectedItem: SelectedItem, scrollToItem: Boolean = false) {
|
suspend fun newSelectedItem(selectedItem: SelectedItem, scrollToItem: Boolean = false) {
|
||||||
@ -300,4 +304,5 @@ enum class RefreshType {
|
|||||||
UNCOMMITED_CHANGES,
|
UNCOMMITED_CHANGES,
|
||||||
UNCOMMITED_CHANGES_AND_LOG,
|
UNCOMMITED_CHANGES_AND_LOG,
|
||||||
REMOTES,
|
REMOTES,
|
||||||
|
REBASE_INTERACTIVE_STATE,
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,5 @@ import com.jetpackduba.gitnuro.ui.SelectedItem
|
|||||||
import org.eclipse.jgit.revwalk.RevCommit
|
import org.eclipse.jgit.revwalk.RevCommit
|
||||||
|
|
||||||
sealed interface TaskEvent {
|
sealed interface TaskEvent {
|
||||||
data class RebaseInteractive(val revCommit: RevCommit) : TaskEvent
|
|
||||||
data class ScrollToGraphItem(val selectedItem: SelectedItem) : TaskEvent
|
data class ScrollToGraphItem(val selectedItem: SelectedItem) : TaskEvent
|
||||||
}
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.jetpackduba.gitnuro.git.log
|
||||||
|
|
||||||
|
import com.jetpackduba.gitnuro.logging.printError
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.eclipse.jgit.api.Git
|
||||||
|
import org.eclipse.jgit.lib.ObjectId
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private const val TAG = "FindCommitUseCase"
|
||||||
|
|
||||||
|
class FindCommitUseCase @Inject constructor() {
|
||||||
|
suspend operator fun invoke(git: Git, commitId: String): RevCommit? = withContext(Dispatchers.IO) {
|
||||||
|
val objectId = ObjectId.fromString(commitId)
|
||||||
|
return@withContext invoke(git, objectId)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend operator fun invoke(git: Git, commitId: ObjectId): RevCommit? = withContext(Dispatchers.IO) {
|
||||||
|
return@withContext try {
|
||||||
|
git.repository.parseCommit(commitId)
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
printError(TAG, "Commit $commitId not found", ex)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.jetpackduba.gitnuro.git.log
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.eclipse.jgit.api.Git
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class GetSpecificCommitMessageUseCase @Inject constructor(
|
||||||
|
private val findCommitUseCase: FindCommitUseCase,
|
||||||
|
) {
|
||||||
|
suspend operator fun invoke(git: Git, commitId: String): String = withContext(Dispatchers.IO) {
|
||||||
|
return@withContext findCommitUseCase(git, commitId)?.fullMessage.orEmpty()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.jetpackduba.gitnuro.git.rebase
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.eclipse.jgit.api.Git
|
||||||
|
import org.eclipse.jgit.api.RebaseCommand
|
||||||
|
import java.io.File
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class GetRebaseAmendCommitIdUseCase @Inject constructor() {
|
||||||
|
suspend operator fun invoke(git: Git): String? = withContext(Dispatchers.IO) {
|
||||||
|
val repository = git.repository
|
||||||
|
|
||||||
|
val amendFile = File(repository.directory, "${RebaseCommand.REBASE_MERGE}/${RebaseConstants.AMEND}")
|
||||||
|
|
||||||
|
if (!amendFile.exists()) {
|
||||||
|
return@withContext null
|
||||||
|
}
|
||||||
|
|
||||||
|
return@withContext amendFile.readText().removeSuffix("\n").removeSuffix("\r\n")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.jetpackduba.gitnuro.git.rebase
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.eclipse.jgit.api.Git
|
||||||
|
import java.io.File
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class GetRebaseInteractiveStateUseCase @Inject constructor(
|
||||||
|
private val getRebaseAmendCommitIdUseCase: GetRebaseAmendCommitIdUseCase,
|
||||||
|
) {
|
||||||
|
suspend operator fun invoke(git: Git): RebaseInteractiveState = withContext(Dispatchers.IO) {
|
||||||
|
val repository = git.repository
|
||||||
|
|
||||||
|
val rebaseMergeDir = File(repository.directory, RebaseConstants.REBASE_MERGE)
|
||||||
|
val doneFile = File(rebaseMergeDir, RebaseConstants.DONE)
|
||||||
|
val stoppedShaFile = File(rebaseMergeDir, RebaseConstants.STOPPED_SHA)
|
||||||
|
|
||||||
|
return@withContext when {
|
||||||
|
!rebaseMergeDir.exists() -> RebaseInteractiveState.None
|
||||||
|
doneFile.exists() || stoppedShaFile.exists() -> {
|
||||||
|
val commitId: String? = getRebaseAmendCommitIdUseCase(git)
|
||||||
|
|
||||||
|
RebaseInteractiveState.ProcessingCommits(commitId)
|
||||||
|
}
|
||||||
|
else -> RebaseInteractiveState.AwaitingInteraction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package com.jetpackduba.gitnuro.git.rebase
|
||||||
|
|
||||||
|
import com.jetpackduba.gitnuro.logging.printDebug
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.eclipse.jgit.api.Git
|
||||||
|
import org.eclipse.jgit.api.RebaseCommand
|
||||||
|
import org.eclipse.jgit.lib.RebaseTodoLine
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private const val TAG = "GetRebaseInteractiveTod"
|
||||||
|
|
||||||
|
class GetRebaseInteractiveTodoLinesUseCase @Inject constructor() {
|
||||||
|
suspend operator fun invoke(git: Git): List<RebaseTodoLine> = withContext(Dispatchers.IO) {
|
||||||
|
val repository = git.repository
|
||||||
|
|
||||||
|
val filePath = "${RebaseCommand.REBASE_MERGE}/${RebaseConstants.GIT_REBASE_TODO}"
|
||||||
|
val lines = repository.readRebaseTodo(filePath, false)
|
||||||
|
|
||||||
|
printDebug(TAG, "There are ${lines.count()} lines")
|
||||||
|
|
||||||
|
return@withContext lines
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.jetpackduba.gitnuro.git.rebase
|
||||||
|
|
||||||
|
|
||||||
|
object RebaseConstants {
|
||||||
|
const val GIT_REBASE_TODO = "git-rebase-todo"
|
||||||
|
const val REBASE_MERGE = "rebase-merge" //$NON-NLS-1$
|
||||||
|
const val DONE = "done"
|
||||||
|
const val STOPPED_SHA = "stopped-sha"
|
||||||
|
const val AMEND = "amend"
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.jetpackduba.gitnuro.git.rebase
|
||||||
|
|
||||||
|
sealed interface RebaseInteractiveState {
|
||||||
|
object None : RebaseInteractiveState
|
||||||
|
object AwaitingInteraction : RebaseInteractiveState
|
||||||
|
data class ProcessingCommits(val commitToAmendId: String?) : RebaseInteractiveState {
|
||||||
|
val isCurrentStepAmenable: Boolean = !commitToAmendId.isNullOrBlank()
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package com.jetpackduba.gitnuro.git.rebase
|
package com.jetpackduba.gitnuro.git.rebase
|
||||||
|
|
||||||
import com.jetpackduba.gitnuro.exceptions.UncommitedChangesDetectedException
|
import com.jetpackduba.gitnuro.exceptions.UncommitedChangesDetectedException
|
||||||
import com.jetpackduba.gitnuro.logging.printDebug
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
@ -9,22 +8,22 @@ import org.eclipse.jgit.api.RebaseCommand
|
|||||||
import org.eclipse.jgit.api.RebaseResult
|
import org.eclipse.jgit.api.RebaseResult
|
||||||
import org.eclipse.jgit.lib.RebaseTodoLine
|
import org.eclipse.jgit.lib.RebaseTodoLine
|
||||||
import org.eclipse.jgit.revwalk.RevCommit
|
import org.eclipse.jgit.revwalk.RevCommit
|
||||||
import java.io.File
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
private const val GIT_REBASE_TODO = "git-rebase-todo"
|
|
||||||
private const val TAG = "StartRebaseInteractiveU"
|
|
||||||
|
|
||||||
class StartRebaseInteractiveUseCase @Inject constructor() {
|
class StartRebaseInteractiveUseCase @Inject constructor() {
|
||||||
suspend operator fun invoke(
|
suspend operator fun invoke(
|
||||||
git: Git,
|
git: Git,
|
||||||
interactiveHandler: RebaseCommand.InteractiveHandler,
|
|
||||||
commit: RevCommit,
|
commit: RevCommit,
|
||||||
stop: Boolean
|
) = withContext(Dispatchers.IO) {
|
||||||
): List<RebaseTodoLine> =
|
|
||||||
withContext(Dispatchers.IO) {
|
val interactiveHandler = object : RebaseCommand.InteractiveHandler {
|
||||||
|
override fun prepareSteps(steps: MutableList<RebaseTodoLine>?) {}
|
||||||
|
override fun modifyCommitMessage(message: String?): String = ""
|
||||||
|
}
|
||||||
|
|
||||||
val rebaseResult = git.rebase()
|
val rebaseResult = git.rebase()
|
||||||
.runInteractively(interactiveHandler, stop)
|
.runInteractively(interactiveHandler, true)
|
||||||
.setOperation(RebaseCommand.Operation.BEGIN)
|
.setOperation(RebaseCommand.Operation.BEGIN)
|
||||||
.setUpstream(commit)
|
.setUpstream(commit)
|
||||||
.call()
|
.call()
|
||||||
@ -37,12 +36,5 @@ class StartRebaseInteractiveUseCase @Inject constructor() {
|
|||||||
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
val repository = git.repository
|
|
||||||
val lines = repository.readRebaseTodo("${RebaseCommand.REBASE_MERGE}/$GIT_REBASE_TODO", false)
|
|
||||||
|
|
||||||
printDebug(TAG, "There are ${lines.count()} lines")
|
|
||||||
|
|
||||||
return@withContext lines
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,16 +15,15 @@ import com.jetpackduba.gitnuro.AppIcons
|
|||||||
import com.jetpackduba.gitnuro.ui.components.AdjustableOutlinedTextField
|
import com.jetpackduba.gitnuro.ui.components.AdjustableOutlinedTextField
|
||||||
import com.jetpackduba.gitnuro.ui.components.PrimaryButton
|
import com.jetpackduba.gitnuro.ui.components.PrimaryButton
|
||||||
import com.jetpackduba.gitnuro.ui.components.ScrollableLazyColumn
|
import com.jetpackduba.gitnuro.ui.components.ScrollableLazyColumn
|
||||||
|
import com.jetpackduba.gitnuro.ui.components.gitnuroDynamicViewModel
|
||||||
import com.jetpackduba.gitnuro.viewmodels.RebaseAction
|
import com.jetpackduba.gitnuro.viewmodels.RebaseAction
|
||||||
import com.jetpackduba.gitnuro.viewmodels.RebaseInteractiveState
|
import com.jetpackduba.gitnuro.viewmodels.RebaseInteractiveViewState
|
||||||
import com.jetpackduba.gitnuro.viewmodels.RebaseInteractiveViewModel
|
import com.jetpackduba.gitnuro.viewmodels.RebaseInteractiveViewModel
|
||||||
import com.jetpackduba.gitnuro.viewmodels.RebaseLine
|
import com.jetpackduba.gitnuro.viewmodels.RebaseLine
|
||||||
import org.eclipse.jgit.lib.RebaseTodoLine
|
|
||||||
import org.eclipse.jgit.lib.RebaseTodoLine.Action
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RebaseInteractive(
|
fun RebaseInteractive(
|
||||||
rebaseInteractiveViewModel: RebaseInteractiveViewModel,
|
rebaseInteractiveViewModel: RebaseInteractiveViewModel = gitnuroDynamicViewModel(),
|
||||||
) {
|
) {
|
||||||
val rebaseState = rebaseInteractiveViewModel.rebaseState.collectAsState()
|
val rebaseState = rebaseInteractiveViewModel.rebaseState.collectAsState()
|
||||||
val rebaseStateValue = rebaseState.value
|
val rebaseStateValue = rebaseState.value
|
||||||
@ -35,8 +34,8 @@ fun RebaseInteractive(
|
|||||||
.fillMaxSize(),
|
.fillMaxSize(),
|
||||||
) {
|
) {
|
||||||
when (rebaseStateValue) {
|
when (rebaseStateValue) {
|
||||||
is RebaseInteractiveState.Failed -> {}
|
is RebaseInteractiveViewState.Failed -> {}
|
||||||
is RebaseInteractiveState.Loaded -> {
|
is RebaseInteractiveViewState.Loaded -> {
|
||||||
RebaseStateLoaded(
|
RebaseStateLoaded(
|
||||||
rebaseInteractiveViewModel,
|
rebaseInteractiveViewModel,
|
||||||
rebaseStateValue,
|
rebaseStateValue,
|
||||||
@ -46,7 +45,7 @@ fun RebaseInteractive(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
RebaseInteractiveState.Loading -> {
|
RebaseInteractiveViewState.Loading -> {
|
||||||
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
|
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,7 +55,7 @@ fun RebaseInteractive(
|
|||||||
@Composable
|
@Composable
|
||||||
fun RebaseStateLoaded(
|
fun RebaseStateLoaded(
|
||||||
rebaseInteractiveViewModel: RebaseInteractiveViewModel,
|
rebaseInteractiveViewModel: RebaseInteractiveViewModel,
|
||||||
rebaseState: RebaseInteractiveState.Loaded,
|
rebaseState: RebaseInteractiveViewState.Loaded,
|
||||||
onCancel: () -> Unit,
|
onCancel: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val stepsList = rebaseState.stepsList
|
val stepsList = rebaseState.stepsList
|
||||||
@ -220,6 +219,7 @@ fun ActionDropdown(
|
|||||||
val firstItemActions = listOf(
|
val firstItemActions = listOf(
|
||||||
RebaseAction.PICK,
|
RebaseAction.PICK,
|
||||||
RebaseAction.REWORD,
|
RebaseAction.REWORD,
|
||||||
|
RebaseAction.DROP,
|
||||||
)
|
)
|
||||||
|
|
||||||
val actions = listOf(
|
val actions = listOf(
|
||||||
|
@ -18,11 +18,12 @@ import androidx.compose.ui.input.pointer.PointerIcon
|
|||||||
import androidx.compose.ui.input.pointer.pointerHoverIcon
|
import androidx.compose.ui.input.pointer.pointerHoverIcon
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.*
|
import androidx.compose.ui.unit.dp
|
||||||
import com.jetpackduba.gitnuro.AppConstants
|
import com.jetpackduba.gitnuro.AppConstants
|
||||||
import com.jetpackduba.gitnuro.LocalTabScope
|
import com.jetpackduba.gitnuro.LocalTabScope
|
||||||
import com.jetpackduba.gitnuro.extensions.handMouseClickable
|
import com.jetpackduba.gitnuro.extensions.handMouseClickable
|
||||||
import com.jetpackduba.gitnuro.git.DiffEntryType
|
import com.jetpackduba.gitnuro.git.DiffEntryType
|
||||||
|
import com.jetpackduba.gitnuro.git.rebase.RebaseInteractiveState
|
||||||
import com.jetpackduba.gitnuro.keybindings.KeybindingOption
|
import com.jetpackduba.gitnuro.keybindings.KeybindingOption
|
||||||
import com.jetpackduba.gitnuro.keybindings.matchesBinding
|
import com.jetpackduba.gitnuro.keybindings.matchesBinding
|
||||||
import com.jetpackduba.gitnuro.ui.components.PrimaryButton
|
import com.jetpackduba.gitnuro.ui.components.PrimaryButton
|
||||||
@ -53,6 +54,7 @@ fun RepositoryOpenPage(
|
|||||||
val blameState by tabViewModel.blameState.collectAsState()
|
val blameState by tabViewModel.blameState.collectAsState()
|
||||||
val showHistory by tabViewModel.showHistory.collectAsState()
|
val showHistory by tabViewModel.showHistory.collectAsState()
|
||||||
val showAuthorInfo by tabViewModel.showAuthorInfo.collectAsState()
|
val showAuthorInfo by tabViewModel.showAuthorInfo.collectAsState()
|
||||||
|
val rebaseInteractiveState by tabViewModel.rebaseInteractiveState.collectAsState()
|
||||||
|
|
||||||
var showNewBranchDialog by remember { mutableStateOf(false) }
|
var showNewBranchDialog by remember { mutableStateOf(false) }
|
||||||
var showStashWithMessageDialog by remember { mutableStateOf(false) }
|
var showStashWithMessageDialog by remember { mutableStateOf(false) }
|
||||||
@ -129,14 +131,8 @@ fun RepositoryOpenPage(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
val rebaseInteractiveViewModel = tabViewModel.rebaseInteractiveViewModel
|
if (rebaseInteractiveState == RebaseInteractiveState.AwaitingInteraction) {
|
||||||
|
RebaseInteractive()
|
||||||
if (repositoryState == RepositoryState.REBASING_INTERACTIVE && rebaseInteractiveViewModel != null) {
|
|
||||||
RebaseInteractive(rebaseInteractiveViewModel)
|
|
||||||
} else if (repositoryState == RepositoryState.REBASING_INTERACTIVE) {
|
|
||||||
RebaseInteractiveStartedExternally(
|
|
||||||
onCancelRebaseInteractive = { tabViewModel.cancelRebaseInteractive() }
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
val currentTabInformation = LocalTabScope.current
|
val currentTabInformation = LocalTabScope.current
|
||||||
Column(modifier = Modifier.weight(1f)) {
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
@ -366,8 +362,8 @@ fun MainContentView(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
) {
|
) {
|
||||||
val safeSelectedItem = selectedItem
|
when (selectedItem) {
|
||||||
if (safeSelectedItem == SelectedItem.UncommitedChanges) {
|
SelectedItem.UncommitedChanges -> {
|
||||||
UncommitedChanges(
|
UncommitedChanges(
|
||||||
selectedEntryType = diffSelected,
|
selectedEntryType = diffSelected,
|
||||||
repositoryState = repositoryState,
|
repositoryState = repositoryState,
|
||||||
@ -394,9 +390,10 @@ fun MainContentView(
|
|||||||
onBlameFile = { tabViewModel.blameFile(it) },
|
onBlameFile = { tabViewModel.blameFile(it) },
|
||||||
onHistoryFile = { tabViewModel.fileHistory(it) }
|
onHistoryFile = { tabViewModel.fileHistory(it) }
|
||||||
)
|
)
|
||||||
} else if (safeSelectedItem is SelectedItem.CommitBasedItem) {
|
}
|
||||||
|
is SelectedItem.CommitBasedItem -> {
|
||||||
CommitChanges(
|
CommitChanges(
|
||||||
selectedItem = safeSelectedItem,
|
selectedItem = selectedItem,
|
||||||
diffSelected = diffSelected,
|
diffSelected = diffSelected,
|
||||||
onDiffSelected = { diffEntry ->
|
onDiffSelected = { diffEntry ->
|
||||||
tabViewModel.minimizeBlame()
|
tabViewModel.minimizeBlame()
|
||||||
@ -406,6 +403,8 @@ fun MainContentView(
|
|||||||
onHistory = { tabViewModel.fileHistory(it) },
|
onHistory = { tabViewModel.fileHistory(it) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
SelectedItem.None -> {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,11 +29,11 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
import androidx.compose.ui.text.input.TextFieldValue
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.Density
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.jetpackduba.gitnuro.AppIcons
|
import com.jetpackduba.gitnuro.AppIcons
|
||||||
import com.jetpackduba.gitnuro.extensions.*
|
import com.jetpackduba.gitnuro.extensions.*
|
||||||
import com.jetpackduba.gitnuro.git.DiffEntryType
|
import com.jetpackduba.gitnuro.git.DiffEntryType
|
||||||
|
import com.jetpackduba.gitnuro.git.rebase.RebaseInteractiveState
|
||||||
import com.jetpackduba.gitnuro.git.workspace.StatusEntry
|
import com.jetpackduba.gitnuro.git.workspace.StatusEntry
|
||||||
import com.jetpackduba.gitnuro.git.workspace.StatusType
|
import com.jetpackduba.gitnuro.git.workspace.StatusType
|
||||||
import com.jetpackduba.gitnuro.keybindings.KeybindingOption
|
import com.jetpackduba.gitnuro.keybindings.KeybindingOption
|
||||||
@ -66,14 +66,19 @@ fun UncommitedChanges(
|
|||||||
val stagedListState by statusViewModel.stagedLazyListState.collectAsState()
|
val stagedListState by statusViewModel.stagedLazyListState.collectAsState()
|
||||||
val unstagedListState by statusViewModel.unstagedLazyListState.collectAsState()
|
val unstagedListState by statusViewModel.unstagedLazyListState.collectAsState()
|
||||||
val isAmend by statusViewModel.isAmend.collectAsState()
|
val isAmend by statusViewModel.isAmend.collectAsState()
|
||||||
|
val isAmendRebaseInteractive by statusViewModel.isAmendRebaseInteractive.collectAsState()
|
||||||
val committerDataRequestState = statusViewModel.committerDataRequestState.collectAsState()
|
val committerDataRequestState = statusViewModel.committerDataRequestState.collectAsState()
|
||||||
val committerDataRequestStateValue = committerDataRequestState.value
|
val committerDataRequestStateValue = committerDataRequestState.value
|
||||||
|
val rebaseInteractiveState = statusViewModel.rebaseInteractiveState.collectAsState().value
|
||||||
|
|
||||||
val showSearchStaged by statusViewModel.showSearchStaged.collectAsState()
|
val showSearchStaged by statusViewModel.showSearchStaged.collectAsState()
|
||||||
val searchFilterStaged by statusViewModel.searchFilterStaged.collectAsState()
|
val searchFilterStaged by statusViewModel.searchFilterStaged.collectAsState()
|
||||||
val showSearchUnstaged by statusViewModel.showSearchUnstaged.collectAsState()
|
val showSearchUnstaged by statusViewModel.showSearchUnstaged.collectAsState()
|
||||||
val searchFilterUnstaged by statusViewModel.searchFilterUnstaged.collectAsState()
|
val searchFilterUnstaged by statusViewModel.searchFilterUnstaged.collectAsState()
|
||||||
|
|
||||||
|
val isAmenableRebaseInteractive =
|
||||||
|
repositoryState.isRebasing && rebaseInteractiveState is RebaseInteractiveState.ProcessingCommits && rebaseInteractiveState.isCurrentStepAmenable
|
||||||
|
|
||||||
val staged: List<StatusEntry>
|
val staged: List<StatusEntry>
|
||||||
val unstaged: List<StatusEntry>
|
val unstaged: List<StatusEntry>
|
||||||
val isLoading: Boolean
|
val isLoading: Boolean
|
||||||
@ -247,9 +252,9 @@ fun UncommitedChanges(
|
|||||||
|
|
||||||
statusViewModel.updateCommitMessage(it)
|
statusViewModel.updateCommitMessage(it)
|
||||||
},
|
},
|
||||||
enabled = !repositoryState.isRebasing,
|
enabled = !repositoryState.isRebasing || isAmenableRebaseInteractive,
|
||||||
label = {
|
label = {
|
||||||
val text = if (repositoryState.isRebasing) {
|
val text = if (repositoryState.isRebasing && !isAmenableRebaseInteractive) {
|
||||||
"Commit message (read-only)"
|
"Commit message (read-only)"
|
||||||
} else {
|
} else {
|
||||||
"Write your commit message here"
|
"Write your commit message here"
|
||||||
@ -275,15 +280,20 @@ fun UncommitedChanges(
|
|||||||
onMerge = { doCommit() }
|
onMerge = { doCommit() }
|
||||||
)
|
)
|
||||||
|
|
||||||
repositoryState.isRebasing -> RebasingButtons(
|
repositoryState.isRebasing && rebaseInteractiveState is RebaseInteractiveState.ProcessingCommits -> RebasingButtons(
|
||||||
canContinue = staged.isNotEmpty() || unstaged.isNotEmpty(),
|
canContinue = staged.isNotEmpty() || unstaged.isNotEmpty() || (isAmenableRebaseInteractive && isAmendRebaseInteractive && commitMessage.isNotEmpty()),
|
||||||
haveConflictsBeenSolved = unstaged.isEmpty(),
|
haveConflictsBeenSolved = unstaged.isEmpty(),
|
||||||
onAbort = {
|
onAbort = {
|
||||||
statusViewModel.abortRebase()
|
statusViewModel.abortRebase()
|
||||||
statusViewModel.updateCommitMessage("")
|
statusViewModel.updateCommitMessage("")
|
||||||
},
|
},
|
||||||
onContinue = { statusViewModel.continueRebase() },
|
onContinue = { statusViewModel.continueRebase(commitMessage) },
|
||||||
onSkip = { statusViewModel.skipRebase() },
|
onSkip = { statusViewModel.skipRebase() },
|
||||||
|
isAmendable = rebaseInteractiveState.isCurrentStepAmenable,
|
||||||
|
isAmend = isAmendRebaseInteractive,
|
||||||
|
onAmendChecked = { isAmend ->
|
||||||
|
statusViewModel.amendRebaseInteractive(isAmend)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
repositoryState.isCherryPicking -> CherryPickingButtons(
|
repositoryState.isCherryPicking -> CherryPickingButtons(
|
||||||
@ -338,31 +348,11 @@ fun UncommitedChangesButtons(
|
|||||||
"Commit"
|
"Commit"
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
Row(
|
CheckboxText(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
value = isAmend,
|
||||||
modifier = Modifier.handMouseClickable(
|
onCheckedChange = { onAmendChecked(!isAmend) },
|
||||||
interactionSource = remember { MutableInteractionSource() },
|
text = "Amend previous commit"
|
||||||
indication = null,
|
|
||||||
) {
|
|
||||||
onAmendChecked(!isAmend)
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Checkbox(
|
|
||||||
checked = isAmend,
|
|
||||||
onCheckedChange = {
|
|
||||||
onAmendChecked(!isAmend)
|
|
||||||
},
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(all = 8.dp)
|
|
||||||
.size(12.dp)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Text(
|
|
||||||
"Amend previous commit",
|
|
||||||
style = MaterialTheme.typography.caption,
|
|
||||||
color = MaterialTheme.colors.onBackground,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(top = 2.dp)
|
.padding(top = 2.dp)
|
||||||
@ -439,11 +429,23 @@ fun CherryPickingButtons(
|
|||||||
@Composable
|
@Composable
|
||||||
fun RebasingButtons(
|
fun RebasingButtons(
|
||||||
canContinue: Boolean,
|
canContinue: Boolean,
|
||||||
|
isAmendable: Boolean,
|
||||||
|
isAmend: Boolean,
|
||||||
|
onAmendChecked: (Boolean) -> Unit,
|
||||||
haveConflictsBeenSolved: Boolean,
|
haveConflictsBeenSolved: Boolean,
|
||||||
onAbort: () -> Unit,
|
onAbort: () -> Unit,
|
||||||
onContinue: () -> Unit,
|
onContinue: () -> Unit,
|
||||||
onSkip: () -> Unit,
|
onSkip: () -> Unit,
|
||||||
) {
|
) {
|
||||||
|
Column {
|
||||||
|
if (isAmendable) {
|
||||||
|
CheckboxText(
|
||||||
|
value = isAmend,
|
||||||
|
onCheckedChange = { onAmendChecked(!isAmend) },
|
||||||
|
text = "Amend previous commit"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
@ -468,11 +470,11 @@ fun RebasingButtons(
|
|||||||
text = "Skip",
|
text = "Skip",
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
.padding(end = 4.dp),
|
.padding(start = 4.dp),
|
||||||
onClick = onSkip,
|
onClick = onSkip,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -759,18 +761,6 @@ private fun FileEntry(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Stable
|
|
||||||
val BottomReversed = object : Arrangement.Vertical {
|
|
||||||
override fun Density.arrange(
|
|
||||||
totalSize: Int,
|
|
||||||
sizes: IntArray,
|
|
||||||
outPositions: IntArray
|
|
||||||
) = placeRightOrBottom(totalSize, sizes, outPositions, reverseInput = true)
|
|
||||||
|
|
||||||
override fun toString() = "Arrangement#BottomReversed"
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun placeRightOrBottom(
|
internal fun placeRightOrBottom(
|
||||||
totalSize: Int,
|
totalSize: Int,
|
||||||
size: IntArray,
|
size: IntArray,
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
package com.jetpackduba.gitnuro.ui.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material.Checkbox
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.jetpackduba.gitnuro.extensions.handMouseClickable
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CheckboxText(
|
||||||
|
value: Boolean,
|
||||||
|
onCheckedChange: () -> Unit,
|
||||||
|
text: String,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.handMouseClickable(
|
||||||
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
|
indication = null,
|
||||||
|
onClick = onCheckedChange,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Checkbox(
|
||||||
|
checked = value,
|
||||||
|
onCheckedChange = { onCheckedChange() },
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(all = 8.dp)
|
||||||
|
.size(12.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text,
|
||||||
|
style = MaterialTheme.typography.caption,
|
||||||
|
color = MaterialTheme.colors.onBackground,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@ import com.jetpackduba.gitnuro.git.graph.GraphCommitList
|
|||||||
import com.jetpackduba.gitnuro.git.graph.GraphNode
|
import com.jetpackduba.gitnuro.git.graph.GraphNode
|
||||||
import com.jetpackduba.gitnuro.git.log.*
|
import com.jetpackduba.gitnuro.git.log.*
|
||||||
import com.jetpackduba.gitnuro.git.rebase.RebaseBranchUseCase
|
import com.jetpackduba.gitnuro.git.rebase.RebaseBranchUseCase
|
||||||
|
import com.jetpackduba.gitnuro.git.rebase.StartRebaseInteractiveUseCase
|
||||||
import com.jetpackduba.gitnuro.git.remote_operations.DeleteRemoteBranchUseCase
|
import com.jetpackduba.gitnuro.git.remote_operations.DeleteRemoteBranchUseCase
|
||||||
import com.jetpackduba.gitnuro.git.remote_operations.PullFromSpecificBranchUseCase
|
import com.jetpackduba.gitnuro.git.remote_operations.PullFromSpecificBranchUseCase
|
||||||
import com.jetpackduba.gitnuro.git.remote_operations.PushToSpecificBranchUseCase
|
import com.jetpackduba.gitnuro.git.remote_operations.PushToSpecificBranchUseCase
|
||||||
@ -65,6 +66,7 @@ class LogViewModel @Inject constructor(
|
|||||||
private val createTagOnCommitUseCase: CreateTagOnCommitUseCase,
|
private val createTagOnCommitUseCase: CreateTagOnCommitUseCase,
|
||||||
private val deleteTagUseCase: DeleteTagUseCase,
|
private val deleteTagUseCase: DeleteTagUseCase,
|
||||||
private val rebaseBranchUseCase: RebaseBranchUseCase,
|
private val rebaseBranchUseCase: RebaseBranchUseCase,
|
||||||
|
private val startRebaseInteractiveUseCase: StartRebaseInteractiveUseCase,
|
||||||
private val tabState: TabState,
|
private val tabState: TabState,
|
||||||
private val appSettings: AppSettings,
|
private val appSettings: AppSettings,
|
||||||
private val tabScope: CoroutineScope,
|
private val tabScope: CoroutineScope,
|
||||||
@ -97,7 +99,6 @@ class LogViewModel @Inject constructor(
|
|||||||
val verticalListState = MutableStateFlow(LazyListState(0, 0))
|
val verticalListState = MutableStateFlow(LazyListState(0, 0))
|
||||||
val horizontalListState = MutableStateFlow(ScrollState(0))
|
val horizontalListState = MutableStateFlow(ScrollState(0))
|
||||||
|
|
||||||
|
|
||||||
private val _logSearchFilterResults = MutableStateFlow<LogSearch>(LogSearch.NotSearching)
|
private val _logSearchFilterResults = MutableStateFlow<LogSearch>(LogSearch.NotSearching)
|
||||||
val logSearchFilterResults: StateFlow<LogSearch> = _logSearchFilterResults
|
val logSearchFilterResults: StateFlow<LogSearch> = _logSearchFilterResults
|
||||||
|
|
||||||
@ -428,10 +429,10 @@ class LogViewModel @Inject constructor(
|
|||||||
_logSearchFilterResults.value = LogSearch.NotSearching
|
_logSearchFilterResults.value = LogSearch.NotSearching
|
||||||
}
|
}
|
||||||
|
|
||||||
fun rebaseInteractive(revCommit: RevCommit) = tabState.runOperation(
|
fun rebaseInteractive(revCommit: RevCommit) = tabState.safeProcessing(
|
||||||
refreshType = RefreshType.NONE
|
refreshType = RefreshType.REBASE_INTERACTIVE_STATE,
|
||||||
) {
|
) { git ->
|
||||||
tabState.emitNewTaskEvent(TaskEvent.RebaseInteractive(revCommit))
|
startRebaseInteractiveUseCase(git, revCommit)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteRemoteBranch(branch: Ref) = tabState.safeProcessing(
|
fun deleteRemoteBranch(branch: Ref) = tabState.safeProcessing(
|
||||||
|
@ -4,10 +4,7 @@ import com.jetpackduba.gitnuro.exceptions.InvalidMessageException
|
|||||||
import com.jetpackduba.gitnuro.exceptions.RebaseCancelledException
|
import com.jetpackduba.gitnuro.exceptions.RebaseCancelledException
|
||||||
import com.jetpackduba.gitnuro.git.RefreshType
|
import com.jetpackduba.gitnuro.git.RefreshType
|
||||||
import com.jetpackduba.gitnuro.git.TabState
|
import com.jetpackduba.gitnuro.git.TabState
|
||||||
import com.jetpackduba.gitnuro.git.rebase.AbortRebaseUseCase
|
import com.jetpackduba.gitnuro.git.rebase.*
|
||||||
import com.jetpackduba.gitnuro.git.rebase.GetRebaseLinesFullMessageUseCase
|
|
||||||
import com.jetpackduba.gitnuro.git.rebase.ResumeRebaseInteractiveUseCase
|
|
||||||
import com.jetpackduba.gitnuro.git.rebase.StartRebaseInteractiveUseCase
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import org.eclipse.jgit.api.RebaseCommand.InteractiveHandler
|
import org.eclipse.jgit.api.RebaseCommand.InteractiveHandler
|
||||||
@ -22,21 +19,23 @@ private const val TAG = "RebaseInteractiveViewMo"
|
|||||||
class RebaseInteractiveViewModel @Inject constructor(
|
class RebaseInteractiveViewModel @Inject constructor(
|
||||||
private val tabState: TabState,
|
private val tabState: TabState,
|
||||||
private val getRebaseLinesFullMessageUseCase: GetRebaseLinesFullMessageUseCase,
|
private val getRebaseLinesFullMessageUseCase: GetRebaseLinesFullMessageUseCase,
|
||||||
private val startRebaseInteractiveUseCase: StartRebaseInteractiveUseCase,
|
private val getRebaseInteractiveTodoLinesUseCase: GetRebaseInteractiveTodoLinesUseCase,
|
||||||
private val abortRebaseUseCase: AbortRebaseUseCase,
|
private val abortRebaseUseCase: AbortRebaseUseCase,
|
||||||
private val resumeRebaseInteractiveUseCase: ResumeRebaseInteractiveUseCase,
|
private val resumeRebaseInteractiveUseCase: ResumeRebaseInteractiveUseCase,
|
||||||
) {
|
) {
|
||||||
private lateinit var commit: RevCommit
|
private lateinit var commit: RevCommit
|
||||||
private val _rebaseState = MutableStateFlow<RebaseInteractiveState>(RebaseInteractiveState.Loading)
|
private val _rebaseState = MutableStateFlow<RebaseInteractiveViewState>(RebaseInteractiveViewState.Loading)
|
||||||
val rebaseState: StateFlow<RebaseInteractiveState> = _rebaseState
|
val rebaseState: StateFlow<RebaseInteractiveViewState> = _rebaseState
|
||||||
var rewordSteps = ArrayDeque<RebaseLine>()
|
var rewordSteps = ArrayDeque<RebaseLine>()
|
||||||
|
|
||||||
var onRebaseComplete: () -> Unit = {}
|
init {
|
||||||
|
loadRebaseInteractiveData()
|
||||||
|
}
|
||||||
|
|
||||||
private var interactiveHandlerContinue = object : InteractiveHandler {
|
private var interactiveHandlerContinue = object : InteractiveHandler {
|
||||||
override fun prepareSteps(steps: MutableList<RebaseTodoLine>) {
|
override fun prepareSteps(steps: MutableList<RebaseTodoLine>) {
|
||||||
val rebaseState = _rebaseState.value
|
val rebaseState = _rebaseState.value
|
||||||
if (rebaseState !is RebaseInteractiveState.Loaded) {
|
if (rebaseState !is RebaseInteractiveViewState.Loaded) {
|
||||||
throw Exception("prepareSteps called when rebaseState is not Loaded") // Should never happen, just in case
|
throw Exception("prepareSteps called when rebaseState is not Loaded") // Should never happen, just in case
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +55,7 @@ class RebaseInteractiveViewModel @Inject constructor(
|
|||||||
val step = rewordSteps.removeLastOrNull() ?: return commit
|
val step = rewordSteps.removeLastOrNull() ?: return commit
|
||||||
|
|
||||||
val rebaseState = _rebaseState.value
|
val rebaseState = _rebaseState.value
|
||||||
if (rebaseState !is RebaseInteractiveState.Loaded) {
|
if (rebaseState !is RebaseInteractiveViewState.Loaded) {
|
||||||
throw Exception("modifyCommitMessage called when rebaseState is not Loaded") // Should never happen, just in case
|
throw Exception("modifyCommitMessage called when rebaseState is not Loaded") // Should never happen, just in case
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,19 +64,11 @@ class RebaseInteractiveViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun startRebaseInteractive(revCommit: RevCommit) = tabState.safeProcessing(
|
private fun loadRebaseInteractiveData() = tabState.safeProcessing(
|
||||||
refreshType = RefreshType.ALL_DATA,
|
refreshType = RefreshType.NONE,
|
||||||
showError = true
|
|
||||||
) { git ->
|
) { git ->
|
||||||
this@RebaseInteractiveViewModel.commit = revCommit
|
|
||||||
|
|
||||||
val interactiveHandler = object : InteractiveHandler {
|
|
||||||
override fun prepareSteps(steps: MutableList<RebaseTodoLine>?) {}
|
|
||||||
override fun modifyCommitMessage(message: String?): String = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val lines = startRebaseInteractiveUseCase(git, interactiveHandler, revCommit, true)
|
val lines = getRebaseInteractiveTodoLinesUseCase(git)
|
||||||
val messages = getRebaseLinesFullMessageUseCase(tabState.git, lines)
|
val messages = getRebaseLinesFullMessageUseCase(tabState.git, lines)
|
||||||
val rebaseLines = lines.map {
|
val rebaseLines = lines.map {
|
||||||
RebaseLine(
|
RebaseLine(
|
||||||
@ -87,7 +78,7 @@ class RebaseInteractiveViewModel @Inject constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
_rebaseState.value = RebaseInteractiveState.Loaded(rebaseLines, messages)
|
_rebaseState.value = RebaseInteractiveViewState.Loaded(rebaseLines, messages)
|
||||||
|
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
if (ex is RebaseCancelledException) {
|
if (ex is RebaseCancelledException) {
|
||||||
@ -102,17 +93,13 @@ class RebaseInteractiveViewModel @Inject constructor(
|
|||||||
fun continueRebaseInteractive() = tabState.safeProcessing(
|
fun continueRebaseInteractive() = tabState.safeProcessing(
|
||||||
refreshType = RefreshType.ALL_DATA,
|
refreshType = RefreshType.ALL_DATA,
|
||||||
) { git ->
|
) { git ->
|
||||||
try {
|
|
||||||
resumeRebaseInteractiveUseCase(git, interactiveHandlerContinue)
|
resumeRebaseInteractiveUseCase(git, interactiveHandlerContinue)
|
||||||
} finally {
|
|
||||||
onRebaseComplete()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onCommitMessageChanged(commit: AbbreviatedObjectId, message: String) {
|
fun onCommitMessageChanged(commit: AbbreviatedObjectId, message: String) {
|
||||||
val rebaseState = _rebaseState.value
|
val rebaseState = _rebaseState.value
|
||||||
|
|
||||||
if (rebaseState !is RebaseInteractiveState.Loaded)
|
if (rebaseState !is RebaseInteractiveViewState.Loaded)
|
||||||
return
|
return
|
||||||
|
|
||||||
val messagesMap = rebaseState.messages.toMutableMap()
|
val messagesMap = rebaseState.messages.toMutableMap()
|
||||||
@ -124,7 +111,7 @@ class RebaseInteractiveViewModel @Inject constructor(
|
|||||||
fun onCommitActionChanged(commit: AbbreviatedObjectId, rebaseAction: RebaseAction) {
|
fun onCommitActionChanged(commit: AbbreviatedObjectId, rebaseAction: RebaseAction) {
|
||||||
val rebaseState = _rebaseState.value
|
val rebaseState = _rebaseState.value
|
||||||
|
|
||||||
if (rebaseState !is RebaseInteractiveState.Loaded)
|
if (rebaseState !is RebaseInteractiveViewState.Loaded)
|
||||||
return
|
return
|
||||||
|
|
||||||
val newStepsList =
|
val newStepsList =
|
||||||
@ -149,17 +136,17 @@ class RebaseInteractiveViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun cancel() = tabState.runOperation(
|
fun cancel() = tabState.runOperation(
|
||||||
refreshType = RefreshType.REPO_STATE
|
refreshType = RefreshType.ALL_DATA,
|
||||||
) { git ->
|
) { git ->
|
||||||
abortRebaseUseCase(git)
|
abortRebaseUseCase(git)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sealed interface RebaseInteractiveState {
|
sealed interface RebaseInteractiveViewState {
|
||||||
object Loading : RebaseInteractiveState
|
object Loading : RebaseInteractiveViewState
|
||||||
data class Loaded(val stepsList: List<RebaseLine>, val messages: Map<String, String>) : RebaseInteractiveState
|
data class Loaded(val stepsList: List<RebaseLine>, val messages: Map<String, String>) : RebaseInteractiveViewState
|
||||||
data class Failed(val error: String) : RebaseInteractiveState
|
data class Failed(val error: String) : RebaseInteractiveViewState
|
||||||
}
|
}
|
||||||
|
|
||||||
data class RebaseLine(
|
data class RebaseLine(
|
||||||
|
@ -2,6 +2,7 @@ package com.jetpackduba.gitnuro.viewmodels
|
|||||||
|
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.compose.ui.text.input.TextFieldValue
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
|
import com.jetpackduba.gitnuro.SharedRepositoryStateManager
|
||||||
import com.jetpackduba.gitnuro.extensions.delayedStateChange
|
import com.jetpackduba.gitnuro.extensions.delayedStateChange
|
||||||
import com.jetpackduba.gitnuro.extensions.isMerging
|
import com.jetpackduba.gitnuro.extensions.isMerging
|
||||||
import com.jetpackduba.gitnuro.extensions.isReverting
|
import com.jetpackduba.gitnuro.extensions.isReverting
|
||||||
@ -12,9 +13,9 @@ import com.jetpackduba.gitnuro.git.author.LoadAuthorUseCase
|
|||||||
import com.jetpackduba.gitnuro.git.author.SaveAuthorUseCase
|
import com.jetpackduba.gitnuro.git.author.SaveAuthorUseCase
|
||||||
import com.jetpackduba.gitnuro.git.log.CheckHasPreviousCommitsUseCase
|
import com.jetpackduba.gitnuro.git.log.CheckHasPreviousCommitsUseCase
|
||||||
import com.jetpackduba.gitnuro.git.log.GetLastCommitMessageUseCase
|
import com.jetpackduba.gitnuro.git.log.GetLastCommitMessageUseCase
|
||||||
import com.jetpackduba.gitnuro.git.rebase.AbortRebaseUseCase
|
import com.jetpackduba.gitnuro.git.log.GetSpecificCommitMessageUseCase
|
||||||
import com.jetpackduba.gitnuro.git.rebase.ContinueRebaseUseCase
|
import com.jetpackduba.gitnuro.git.rebase.*
|
||||||
import com.jetpackduba.gitnuro.git.rebase.SkipRebaseUseCase
|
import com.jetpackduba.gitnuro.git.repository.GetRepositoryStateUseCase
|
||||||
import com.jetpackduba.gitnuro.git.repository.ResetRepositoryStateUseCase
|
import com.jetpackduba.gitnuro.git.repository.ResetRepositoryStateUseCase
|
||||||
import com.jetpackduba.gitnuro.git.workspace.*
|
import com.jetpackduba.gitnuro.git.workspace.*
|
||||||
import com.jetpackduba.gitnuro.models.AuthorInfo
|
import com.jetpackduba.gitnuro.models.AuthorInfo
|
||||||
@ -52,6 +53,10 @@ class StatusViewModel @Inject constructor(
|
|||||||
private val doCommitUseCase: DoCommitUseCase,
|
private val doCommitUseCase: DoCommitUseCase,
|
||||||
private val loadAuthorUseCase: LoadAuthorUseCase,
|
private val loadAuthorUseCase: LoadAuthorUseCase,
|
||||||
private val saveAuthorUseCase: SaveAuthorUseCase,
|
private val saveAuthorUseCase: SaveAuthorUseCase,
|
||||||
|
private val getRepositoryStateUseCase: GetRepositoryStateUseCase,
|
||||||
|
private val getRebaseAmendCommitIdUseCase: GetRebaseAmendCommitIdUseCase,
|
||||||
|
private val sharedRepositoryStateManager: SharedRepositoryStateManager,
|
||||||
|
private val getSpecificCommitMessageUseCase: GetSpecificCommitMessageUseCase,
|
||||||
private val tabScope: CoroutineScope,
|
private val tabScope: CoroutineScope,
|
||||||
private val appSettings: AppSettings,
|
private val appSettings: AppSettings,
|
||||||
) {
|
) {
|
||||||
@ -68,6 +73,7 @@ class StatusViewModel @Inject constructor(
|
|||||||
val searchFilterStaged: StateFlow<TextFieldValue> = _searchFilterStaged
|
val searchFilterStaged: StateFlow<TextFieldValue> = _searchFilterStaged
|
||||||
|
|
||||||
val swapUncommitedChanges = appSettings.swapUncommitedChangesFlow
|
val swapUncommitedChanges = appSettings.swapUncommitedChangesFlow
|
||||||
|
val rebaseInteractiveState = sharedRepositoryStateManager.rebaseInteractiveState
|
||||||
|
|
||||||
private val _stageState = MutableStateFlow<StageState>(StageState.Loading)
|
private val _stageState = MutableStateFlow<StageState>(StageState.Loading)
|
||||||
|
|
||||||
@ -123,6 +129,9 @@ class StatusViewModel @Inject constructor(
|
|||||||
private val _isAmend = MutableStateFlow(false)
|
private val _isAmend = MutableStateFlow(false)
|
||||||
val isAmend: StateFlow<Boolean> = _isAmend
|
val isAmend: StateFlow<Boolean> = _isAmend
|
||||||
|
|
||||||
|
private val _isAmendRebaseInteractive = MutableStateFlow(false)
|
||||||
|
val isAmendRebaseInteractive: StateFlow<Boolean> = _isAmendRebaseInteractive
|
||||||
|
|
||||||
init {
|
init {
|
||||||
tabScope.launch {
|
tabScope.launch {
|
||||||
tabState.refreshFlowFiltered(
|
tabState.refreshFlowFiltered(
|
||||||
@ -262,6 +271,14 @@ class StatusViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun amendRebaseInteractive(isAmend: Boolean) {
|
||||||
|
_isAmendRebaseInteractive.value = isAmend
|
||||||
|
|
||||||
|
if (isAmend && savedCommitMessage.message.isEmpty()) {
|
||||||
|
takeMessageFromAmendCommit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun commit(message: String) = tabState.safeProcessing(
|
fun commit(message: String) = tabState.safeProcessing(
|
||||||
refreshType = RefreshType.ALL_DATA,
|
refreshType = RefreshType.ALL_DATA,
|
||||||
) { git ->
|
) { git ->
|
||||||
@ -272,9 +289,18 @@ class StatusViewModel @Inject constructor(
|
|||||||
} else
|
} else
|
||||||
message
|
message
|
||||||
|
|
||||||
|
|
||||||
|
val personIdent = getPersonIdent(git)
|
||||||
|
|
||||||
|
doCommitUseCase(git, commitMessage, amend, personIdent)
|
||||||
|
updateCommitMessage("")
|
||||||
|
_isAmend.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getPersonIdent(git: Git): PersonIdent? {
|
||||||
val author = loadAuthorUseCase(git)
|
val author = loadAuthorUseCase(git)
|
||||||
|
|
||||||
val personIdent = if (
|
return if (
|
||||||
author.name.isNullOrEmpty() && author.globalName.isNullOrEmpty() ||
|
author.name.isNullOrEmpty() && author.globalName.isNullOrEmpty() ||
|
||||||
author.email.isNullOrEmpty() && author.globalEmail.isNullOrEmpty()
|
author.email.isNullOrEmpty() && author.globalEmail.isNullOrEmpty()
|
||||||
) {
|
) {
|
||||||
@ -299,10 +325,6 @@ class StatusViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
null
|
null
|
||||||
|
|
||||||
doCommitUseCase(git, commitMessage, amend, personIdent)
|
|
||||||
updateCommitMessage("")
|
|
||||||
_isAmend.value = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun refresh(git: Git) = withContext(Dispatchers.IO) {
|
suspend fun refresh(git: Git) = withContext(Dispatchers.IO) {
|
||||||
@ -326,9 +348,25 @@ class StatusViewModel @Inject constructor(
|
|||||||
return (hasNowUncommitedChanges != hadUncommitedChanges)
|
return (hasNowUncommitedChanges != hadUncommitedChanges)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun continueRebase() = tabState.safeProcessing(
|
fun continueRebase(message: String) = tabState.safeProcessing(
|
||||||
refreshType = RefreshType.ALL_DATA,
|
refreshType = RefreshType.ALL_DATA,
|
||||||
) { git ->
|
) { git ->
|
||||||
|
val repositoryState = sharedRepositoryStateManager.repositoryState.value
|
||||||
|
val rebaseInteractiveState = sharedRepositoryStateManager.rebaseInteractiveState.value
|
||||||
|
|
||||||
|
if (
|
||||||
|
repositoryState == RepositoryState.REBASING_INTERACTIVE &&
|
||||||
|
rebaseInteractiveState is RebaseInteractiveState.ProcessingCommits &&
|
||||||
|
rebaseInteractiveState.isCurrentStepAmenable &&
|
||||||
|
isAmendRebaseInteractive.value
|
||||||
|
) {
|
||||||
|
val amendCommitId = getRebaseAmendCommitIdUseCase(git)
|
||||||
|
|
||||||
|
if (!amendCommitId.isNullOrBlank()) {
|
||||||
|
doCommitUseCase(git, message, true, getPersonIdent(git))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
continueRebaseUseCase(git)
|
continueRebaseUseCase(git)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,6 +411,22 @@ class StatusViewModel @Inject constructor(
|
|||||||
_commitMessageChangesFlow.emit(savedCommitMessage.message)
|
_commitMessageChangesFlow.emit(savedCommitMessage.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun takeMessageFromAmendCommit() = tabState.runOperation(
|
||||||
|
refreshType = RefreshType.NONE,
|
||||||
|
) { git ->
|
||||||
|
val rebaseInteractiveState = rebaseInteractiveState.value
|
||||||
|
if (rebaseInteractiveState !is RebaseInteractiveState.ProcessingCommits) {
|
||||||
|
return@runOperation
|
||||||
|
}
|
||||||
|
|
||||||
|
val commitId = rebaseInteractiveState.commitToAmendId ?: return@runOperation
|
||||||
|
val message = getSpecificCommitMessageUseCase(git, commitId)
|
||||||
|
|
||||||
|
savedCommitMessage = savedCommitMessage.copy(message = message)
|
||||||
|
persistMessage()
|
||||||
|
_commitMessageChangesFlow.emit(savedCommitMessage.message)
|
||||||
|
}
|
||||||
|
|
||||||
fun onRejectCommitterData() {
|
fun onRejectCommitterData() {
|
||||||
this._committerDataRequestState.value = CommitterDataRequestState.Reject
|
this._committerDataRequestState.value = CommitterDataRequestState.Reject
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
package com.jetpackduba.gitnuro.viewmodels
|
package com.jetpackduba.gitnuro.viewmodels
|
||||||
|
|
||||||
|
import com.jetpackduba.gitnuro.SharedRepositoryStateManager
|
||||||
import com.jetpackduba.gitnuro.credentials.CredentialsAccepted
|
import com.jetpackduba.gitnuro.credentials.CredentialsAccepted
|
||||||
import com.jetpackduba.gitnuro.credentials.CredentialsState
|
import com.jetpackduba.gitnuro.credentials.CredentialsState
|
||||||
import com.jetpackduba.gitnuro.credentials.CredentialsStateManager
|
import com.jetpackduba.gitnuro.credentials.CredentialsStateManager
|
||||||
import com.jetpackduba.gitnuro.git.*
|
import com.jetpackduba.gitnuro.git.*
|
||||||
import com.jetpackduba.gitnuro.git.branches.CreateBranchUseCase
|
import com.jetpackduba.gitnuro.git.branches.CreateBranchUseCase
|
||||||
import com.jetpackduba.gitnuro.git.rebase.AbortRebaseUseCase
|
import com.jetpackduba.gitnuro.git.rebase.GetRebaseInteractiveStateUseCase
|
||||||
|
import com.jetpackduba.gitnuro.git.rebase.RebaseInteractiveState
|
||||||
import com.jetpackduba.gitnuro.git.repository.GetRepositoryStateUseCase
|
import com.jetpackduba.gitnuro.git.repository.GetRepositoryStateUseCase
|
||||||
import com.jetpackduba.gitnuro.git.repository.InitLocalRepositoryUseCase
|
import com.jetpackduba.gitnuro.git.repository.InitLocalRepositoryUseCase
|
||||||
import com.jetpackduba.gitnuro.git.repository.OpenRepositoryUseCase
|
import com.jetpackduba.gitnuro.git.repository.OpenRepositoryUseCase
|
||||||
@ -54,7 +56,6 @@ class TabViewModel @Inject constructor(
|
|||||||
private val openRepositoryUseCase: OpenRepositoryUseCase,
|
private val openRepositoryUseCase: OpenRepositoryUseCase,
|
||||||
private val openSubmoduleRepositoryUseCase: OpenSubmoduleRepositoryUseCase,
|
private val openSubmoduleRepositoryUseCase: OpenSubmoduleRepositoryUseCase,
|
||||||
private val diffViewModelProvider: Provider<DiffViewModel>,
|
private val diffViewModelProvider: Provider<DiffViewModel>,
|
||||||
private val rebaseInteractiveViewModelProvider: Provider<RebaseInteractiveViewModel>,
|
|
||||||
private val historyViewModelProvider: Provider<HistoryViewModel>,
|
private val historyViewModelProvider: Provider<HistoryViewModel>,
|
||||||
private val authorViewModelProvider: Provider<AuthorViewModel>,
|
private val authorViewModelProvider: Provider<AuthorViewModel>,
|
||||||
private val tabState: TabState,
|
private val tabState: TabState,
|
||||||
@ -65,9 +66,10 @@ class TabViewModel @Inject constructor(
|
|||||||
private val createBranchUseCase: CreateBranchUseCase,
|
private val createBranchUseCase: CreateBranchUseCase,
|
||||||
private val stashChangesUseCase: StashChangesUseCase,
|
private val stashChangesUseCase: StashChangesUseCase,
|
||||||
private val stageUntrackedFileUseCase: StageUntrackedFileUseCase,
|
private val stageUntrackedFileUseCase: StageUntrackedFileUseCase,
|
||||||
private val abortRebaseUseCase: AbortRebaseUseCase,
|
|
||||||
private val openFilePickerUseCase: OpenFilePickerUseCase,
|
private val openFilePickerUseCase: OpenFilePickerUseCase,
|
||||||
private val openUrlInBrowserUseCase: OpenUrlInBrowserUseCase,
|
private val openUrlInBrowserUseCase: OpenUrlInBrowserUseCase,
|
||||||
|
private val getRebaseInteractiveStateUseCase: GetRebaseInteractiveStateUseCase,
|
||||||
|
private val sharedRepositoryStateManager: SharedRepositoryStateManager,
|
||||||
private val tabsManager: TabsManager,
|
private val tabsManager: TabsManager,
|
||||||
private val tabScope: CoroutineScope,
|
private val tabScope: CoroutineScope,
|
||||||
) {
|
) {
|
||||||
@ -76,13 +78,13 @@ class TabViewModel @Inject constructor(
|
|||||||
val selectedItem: StateFlow<SelectedItem> = tabState.selectedItem
|
val selectedItem: StateFlow<SelectedItem> = tabState.selectedItem
|
||||||
var diffViewModel: DiffViewModel? = null
|
var diffViewModel: DiffViewModel? = null
|
||||||
|
|
||||||
var rebaseInteractiveViewModel: RebaseInteractiveViewModel? = null
|
|
||||||
private set
|
|
||||||
|
|
||||||
private val _repositorySelectionStatus = MutableStateFlow<RepositorySelectionStatus>(RepositorySelectionStatus.None)
|
private val _repositorySelectionStatus = MutableStateFlow<RepositorySelectionStatus>(RepositorySelectionStatus.None)
|
||||||
val repositorySelectionStatus: StateFlow<RepositorySelectionStatus>
|
val repositorySelectionStatus: StateFlow<RepositorySelectionStatus>
|
||||||
get() = _repositorySelectionStatus
|
get() = _repositorySelectionStatus
|
||||||
|
|
||||||
|
val repositoryState: StateFlow<RepositoryState> = sharedRepositoryStateManager.repositoryState
|
||||||
|
val rebaseInteractiveState: StateFlow<RebaseInteractiveState> = sharedRepositoryStateManager.rebaseInteractiveState
|
||||||
|
|
||||||
val processing: StateFlow<ProcessingState> = tabState.processing
|
val processing: StateFlow<ProcessingState> = tabState.processing
|
||||||
|
|
||||||
val credentialsState: StateFlow<CredentialsState> = credentialsStateManager.credentialsState
|
val credentialsState: StateFlow<CredentialsState> = credentialsStateManager.credentialsState
|
||||||
@ -97,9 +99,6 @@ class TabViewModel @Inject constructor(
|
|||||||
updateDiffEntry()
|
updateDiffEntry()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val _repositoryState = MutableStateFlow(RepositoryState.SAFE)
|
|
||||||
val repositoryState: StateFlow<RepositoryState> = _repositoryState
|
|
||||||
|
|
||||||
private val _blameState = MutableStateFlow<BlameState>(BlameState.None)
|
private val _blameState = MutableStateFlow<BlameState>(BlameState.None)
|
||||||
val blameState: StateFlow<BlameState> = _blameState
|
val blameState: StateFlow<BlameState> = _blameState
|
||||||
|
|
||||||
@ -123,28 +122,8 @@ class TabViewModel @Inject constructor(
|
|||||||
init {
|
init {
|
||||||
tabScope.run {
|
tabScope.run {
|
||||||
launch {
|
launch {
|
||||||
tabState.refreshData.collect { refreshType ->
|
tabState.refreshFlowFiltered(RefreshType.ALL_DATA, RefreshType.REPO_STATE) {
|
||||||
when (refreshType) {
|
loadAuthorInfo(tabState.git)
|
||||||
RefreshType.NONE -> printLog(TAG, "Not refreshing...")
|
|
||||||
RefreshType.REPO_STATE -> refreshRepositoryState()
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
launch {
|
|
||||||
tabState.taskEvent.collect { taskEvent ->
|
|
||||||
when (taskEvent) {
|
|
||||||
is TaskEvent.RebaseInteractive -> onRebaseInteractive(taskEvent)
|
|
||||||
else -> { /*Nothing to do here*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
launch {
|
|
||||||
tabState.refreshFlowFiltered(RefreshType.ALL_DATA, RefreshType.REPO_STATE)
|
|
||||||
{
|
|
||||||
loadRepositoryState(tabState.git)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,19 +135,6 @@ class TabViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshRepositoryState() = tabState.safeProcessing(
|
|
||||||
refreshType = RefreshType.NONE,
|
|
||||||
) { git ->
|
|
||||||
loadRepositoryState(git)
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun onRebaseInteractive(taskEvent: TaskEvent.RebaseInteractive) {
|
|
||||||
rebaseInteractiveViewModel = rebaseInteractiveViewModelProvider.get()
|
|
||||||
rebaseInteractiveViewModel?.startRebaseInteractive(taskEvent.revCommit)
|
|
||||||
rebaseInteractiveViewModel?.onRebaseComplete = {
|
|
||||||
rebaseInteractiveViewModel = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To make sure the tab opens the new repository with a clean state,
|
* To make sure the tab opens the new repository with a clean state,
|
||||||
@ -220,16 +186,6 @@ class TabViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun loadRepositoryState(git: Git) = withContext(Dispatchers.IO) {
|
|
||||||
val newRepoState = getRepositoryStateUseCase(git)
|
|
||||||
printLog(TAG, "Refreshing repository state $newRepoState")
|
|
||||||
_repositoryState.value = newRepoState
|
|
||||||
|
|
||||||
loadAuthorInfo(git)
|
|
||||||
|
|
||||||
onRepositoryStateChanged(newRepoState)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadAuthorInfo(git: Git) {
|
private fun loadAuthorInfo(git: Git) {
|
||||||
val config = git.repository.config
|
val config = git.repository.config
|
||||||
config.load()
|
config.load()
|
||||||
@ -250,13 +206,6 @@ class TabViewModel @Inject constructor(
|
|||||||
authorViewModel = null
|
authorViewModel = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onRepositoryStateChanged(newRepoState: RepositoryState) {
|
|
||||||
if (newRepoState != RepositoryState.REBASING_INTERACTIVE && rebaseInteractiveViewModel != null) {
|
|
||||||
rebaseInteractiveViewModel?.cancel()
|
|
||||||
rebaseInteractiveViewModel = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun watchRepositoryChanges(git: Git) = tabScope.launch(Dispatchers.IO) {
|
private suspend fun watchRepositoryChanges(git: Git) = tabScope.launch(Dispatchers.IO) {
|
||||||
val ignored = git.status().call().ignoredNotInIndex.toList()
|
val ignored = git.status().call().ignoredNotInIndex.toList()
|
||||||
var asyncJob: Job? = null
|
var asyncJob: Job? = null
|
||||||
@ -470,13 +419,6 @@ class TabViewModel @Inject constructor(
|
|||||||
Desktop.getDesktop().open(git.repository.workTree)
|
Desktop.getDesktop().open(git.repository.workTree)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cancelRebaseInteractive() = tabState.safeProcessing(
|
|
||||||
refreshType = RefreshType.ALL_DATA,
|
|
||||||
) { git ->
|
|
||||||
abortRebaseUseCase(git)
|
|
||||||
rebaseInteractiveViewModel = null // shouldn't be necessary but just to make sure
|
|
||||||
}
|
|
||||||
|
|
||||||
fun gpgCredentialsAccepted(password: String) {
|
fun gpgCredentialsAccepted(password: String) {
|
||||||
credentialsStateManager.updateState(CredentialsAccepted.GpgCredentialsAccepted(password))
|
credentialsStateManager.updateState(CredentialsAccepted.GpgCredentialsAccepted(password))
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user