197 lines
6.7 KiB
Kotlin
197 lines
6.7 KiB
Kotlin
package com.jetpackduba.gitnuro.viewmodels
|
|
|
|
import com.jetpackduba.gitnuro.exceptions.InvalidMessageException
|
|
import com.jetpackduba.gitnuro.exceptions.RebaseCancelledException
|
|
import com.jetpackduba.gitnuro.git.RefreshType
|
|
import com.jetpackduba.gitnuro.git.TabState
|
|
import com.jetpackduba.gitnuro.git.rebase.*
|
|
import kotlinx.coroutines.flow.MutableStateFlow
|
|
import kotlinx.coroutines.flow.StateFlow
|
|
import org.eclipse.jgit.api.RebaseCommand.InteractiveHandler
|
|
import org.eclipse.jgit.lib.AbbreviatedObjectId
|
|
import org.eclipse.jgit.lib.RebaseTodoLine
|
|
import org.eclipse.jgit.lib.RebaseTodoLine.Action
|
|
import org.eclipse.jgit.revwalk.RevCommit
|
|
import javax.inject.Inject
|
|
|
|
private const val TAG = "RebaseInteractiveViewMo"
|
|
|
|
class RebaseInteractiveViewModel @Inject constructor(
|
|
private val tabState: TabState,
|
|
private val getRebaseLinesFullMessageUseCase: GetRebaseLinesFullMessageUseCase,
|
|
private val getRebaseInteractiveTodoLinesUseCase: GetRebaseInteractiveTodoLinesUseCase,
|
|
private val abortRebaseUseCase: AbortRebaseUseCase,
|
|
private val resumeRebaseInteractiveUseCase: ResumeRebaseInteractiveUseCase,
|
|
) {
|
|
private lateinit var commit: RevCommit
|
|
private val _rebaseState = MutableStateFlow<RebaseInteractiveViewState>(RebaseInteractiveViewState.Loading)
|
|
val rebaseState: StateFlow<RebaseInteractiveViewState> = _rebaseState
|
|
var rewordSteps = ArrayDeque<RebaseLine>()
|
|
|
|
init {
|
|
loadRebaseInteractiveData()
|
|
}
|
|
|
|
private var interactiveHandlerContinue = object : InteractiveHandler {
|
|
override fun prepareSteps(steps: MutableList<RebaseTodoLine>) {
|
|
val rebaseState = _rebaseState.value
|
|
if (rebaseState !is RebaseInteractiveViewState.Loaded) {
|
|
throw Exception("prepareSteps called when rebaseState is not Loaded") // Should never happen, just in case
|
|
}
|
|
|
|
val newSteps = rebaseState.stepsList.toMutableList()
|
|
rewordSteps = ArrayDeque(newSteps.filter { it.rebaseAction == RebaseAction.REWORD })
|
|
|
|
val newRebaseTodoLines = newSteps
|
|
.filter { it.rebaseAction != RebaseAction.DROP } // Remove dropped lines
|
|
.map { it.toRebaseTodoLine() }
|
|
|
|
steps.clear()
|
|
steps.addAll(newRebaseTodoLines)
|
|
}
|
|
|
|
override fun modifyCommitMessage(commit: String): String {
|
|
// This can be called when there aren't any reword steps if squash is used.
|
|
val step = rewordSteps.removeLastOrNull() ?: return commit
|
|
|
|
val rebaseState = _rebaseState.value
|
|
if (rebaseState !is RebaseInteractiveViewState.Loaded) {
|
|
throw Exception("modifyCommitMessage called when rebaseState is not Loaded") // Should never happen, just in case
|
|
}
|
|
|
|
return rebaseState.messages[step.commit.name()]
|
|
?: throw InvalidMessageException("Message for commit $commit is unexpectedly null")
|
|
}
|
|
}
|
|
|
|
private fun loadRebaseInteractiveData() = tabState.safeProcessing(
|
|
refreshType = RefreshType.NONE,
|
|
) { git ->
|
|
try {
|
|
val lines = getRebaseInteractiveTodoLinesUseCase(git)
|
|
val messages = getRebaseLinesFullMessageUseCase(tabState.git, lines)
|
|
val rebaseLines = lines.map {
|
|
RebaseLine(
|
|
it.action.toRebaseAction(),
|
|
it.commit,
|
|
it.shortMessage,
|
|
)
|
|
}
|
|
|
|
_rebaseState.value = RebaseInteractiveViewState.Loaded(rebaseLines, messages)
|
|
|
|
} catch (ex: Exception) {
|
|
if (ex is RebaseCancelledException) {
|
|
println("Rebase cancelled")
|
|
} else {
|
|
cancel()
|
|
throw ex
|
|
}
|
|
}
|
|
}
|
|
|
|
fun continueRebaseInteractive() = tabState.safeProcessing(
|
|
refreshType = RefreshType.ALL_DATA,
|
|
) { git ->
|
|
resumeRebaseInteractiveUseCase(git, interactiveHandlerContinue)
|
|
}
|
|
|
|
fun onCommitMessageChanged(commit: AbbreviatedObjectId, message: String) {
|
|
val rebaseState = _rebaseState.value
|
|
|
|
if (rebaseState !is RebaseInteractiveViewState.Loaded)
|
|
return
|
|
|
|
val messagesMap = rebaseState.messages.toMutableMap()
|
|
messagesMap[commit.name()] = message
|
|
|
|
_rebaseState.value = rebaseState.copy(messages = messagesMap)
|
|
}
|
|
|
|
fun onCommitActionChanged(commit: AbbreviatedObjectId, rebaseAction: RebaseAction) {
|
|
val rebaseState = _rebaseState.value
|
|
|
|
if (rebaseState !is RebaseInteractiveViewState.Loaded)
|
|
return
|
|
|
|
val newStepsList =
|
|
rebaseState.stepsList.toMutableList() // Change the list reference to update the flow with .toList()
|
|
|
|
val stepIndex = newStepsList.indexOfFirst {
|
|
it.commit == commit
|
|
}
|
|
|
|
if (stepIndex >= 0) {
|
|
val step = newStepsList[stepIndex]
|
|
val newTodoLine = RebaseLine(
|
|
rebaseAction,
|
|
step.commit,
|
|
step.shortMessage
|
|
)
|
|
|
|
newStepsList[stepIndex] = newTodoLine
|
|
|
|
_rebaseState.value = rebaseState.copy(stepsList = newStepsList)
|
|
}
|
|
}
|
|
|
|
fun cancel() = tabState.runOperation(
|
|
refreshType = RefreshType.ALL_DATA,
|
|
) { git ->
|
|
abortRebaseUseCase(git)
|
|
}
|
|
}
|
|
|
|
|
|
sealed interface RebaseInteractiveViewState {
|
|
object Loading : RebaseInteractiveViewState
|
|
data class Loaded(val stepsList: List<RebaseLine>, val messages: Map<String, String>) : RebaseInteractiveViewState
|
|
data class Failed(val error: String) : RebaseInteractiveViewState
|
|
}
|
|
|
|
data class RebaseLine(
|
|
val rebaseAction: RebaseAction,
|
|
val commit: AbbreviatedObjectId,
|
|
val shortMessage: String
|
|
) {
|
|
fun toRebaseTodoLine(): RebaseTodoLine {
|
|
return RebaseTodoLine(
|
|
rebaseAction.toAction(),
|
|
commit,
|
|
shortMessage
|
|
)
|
|
}
|
|
}
|
|
|
|
enum class RebaseAction(val displayName: String) {
|
|
PICK("Pick"),
|
|
REWORD("Reword"),
|
|
SQUASH("Squash"),
|
|
FIXUP("Fixup"),
|
|
EDIT("Edit"),
|
|
DROP("Drop"),
|
|
COMMENT("Comment");
|
|
|
|
fun toAction(): Action {
|
|
return when (this) {
|
|
PICK -> Action.PICK
|
|
REWORD -> Action.REWORD
|
|
SQUASH -> Action.SQUASH
|
|
FIXUP -> Action.FIXUP
|
|
EDIT -> Action.EDIT
|
|
COMMENT -> Action.COMMENT
|
|
DROP -> throw NotImplementedError("To action should not be called when the RebaseAction is DROP")
|
|
}
|
|
}
|
|
}
|
|
|
|
fun Action.toRebaseAction(): RebaseAction {
|
|
return when (this) {
|
|
Action.PICK -> RebaseAction.PICK
|
|
Action.REWORD -> RebaseAction.REWORD
|
|
Action.EDIT -> RebaseAction.EDIT
|
|
Action.SQUASH -> RebaseAction.SQUASH
|
|
Action.FIXUP -> RebaseAction.FIXUP
|
|
Action.COMMENT -> RebaseAction.COMMENT
|
|
}
|
|
} |