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.di.TabScope
|
||||
import com.jetpackduba.gitnuro.extensions.delayedStateChange
|
||||
import com.jetpackduba.gitnuro.git.log.FindCommitUseCase
|
||||
import com.jetpackduba.gitnuro.logging.printError
|
||||
import com.jetpackduba.gitnuro.managers.newErrorNow
|
||||
import com.jetpackduba.gitnuro.ui.SelectedItem
|
||||
@ -10,6 +11,7 @@ import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.lib.ObjectId
|
||||
import org.eclipse.jgit.lib.RepositoryState
|
||||
import org.eclipse.jgit.revwalk.RevCommit
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -33,6 +35,7 @@ sealed interface ProcessingState {
|
||||
class TabState @Inject constructor(
|
||||
val errorsManager: ErrorsManager,
|
||||
private val scope: CoroutineScope,
|
||||
private val findCommitUseCase: FindCommitUseCase,
|
||||
) {
|
||||
private val _selectedItem = MutableStateFlow<SelectedItem>(SelectedItem.UncommitedChanges)
|
||||
val selectedItem: StateFlow<SelectedItem> = _selectedItem
|
||||
@ -247,15 +250,16 @@ class TabState @Inject constructor(
|
||||
if (objectId == null) {
|
||||
newSelectedItem(SelectedItem.None)
|
||||
} else {
|
||||
val commit = findCommit(git, objectId)
|
||||
val commit = findCommitUseCase(git, objectId)
|
||||
|
||||
if(commit == null) {
|
||||
newSelectedItem(SelectedItem.None)
|
||||
} else {
|
||||
val newSelectedItem = SelectedItem.Ref(commit)
|
||||
newSelectedItem(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) {
|
||||
@ -300,4 +304,5 @@ enum class RefreshType {
|
||||
UNCOMMITED_CHANGES,
|
||||
UNCOMMITED_CHANGES_AND_LOG,
|
||||
REMOTES,
|
||||
REBASE_INTERACTIVE_STATE,
|
||||
}
|
||||
|
@ -4,6 +4,5 @@ import com.jetpackduba.gitnuro.ui.SelectedItem
|
||||
import org.eclipse.jgit.revwalk.RevCommit
|
||||
|
||||
sealed interface TaskEvent {
|
||||
data class RebaseInteractive(val revCommit: RevCommit) : 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
|
||||
|
||||
import com.jetpackduba.gitnuro.exceptions.UncommitedChangesDetectedException
|
||||
import com.jetpackduba.gitnuro.logging.printDebug
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
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.lib.RebaseTodoLine
|
||||
import org.eclipse.jgit.revwalk.RevCommit
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val GIT_REBASE_TODO = "git-rebase-todo"
|
||||
private const val TAG = "StartRebaseInteractiveU"
|
||||
|
||||
class StartRebaseInteractiveUseCase @Inject constructor() {
|
||||
suspend operator fun invoke(
|
||||
git: Git,
|
||||
interactiveHandler: RebaseCommand.InteractiveHandler,
|
||||
commit: RevCommit,
|
||||
stop: Boolean
|
||||
): List<RebaseTodoLine> =
|
||||
withContext(Dispatchers.IO) {
|
||||
) = withContext(Dispatchers.IO) {
|
||||
|
||||
val interactiveHandler = object : RebaseCommand.InteractiveHandler {
|
||||
override fun prepareSteps(steps: MutableList<RebaseTodoLine>?) {}
|
||||
override fun modifyCommitMessage(message: String?): String = ""
|
||||
}
|
||||
|
||||
val rebaseResult = git.rebase()
|
||||
.runInteractively(interactiveHandler, stop)
|
||||
.runInteractively(interactiveHandler, true)
|
||||
.setOperation(RebaseCommand.Operation.BEGIN)
|
||||
.setUpstream(commit)
|
||||
.call()
|
||||
@ -37,12 +36,5 @@ class StartRebaseInteractiveUseCase @Inject constructor() {
|
||||
|
||||
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.PrimaryButton
|
||||
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.RebaseInteractiveState
|
||||
import com.jetpackduba.gitnuro.viewmodels.RebaseInteractiveViewState
|
||||
import com.jetpackduba.gitnuro.viewmodels.RebaseInteractiveViewModel
|
||||
import com.jetpackduba.gitnuro.viewmodels.RebaseLine
|
||||
import org.eclipse.jgit.lib.RebaseTodoLine
|
||||
import org.eclipse.jgit.lib.RebaseTodoLine.Action
|
||||
|
||||
@Composable
|
||||
fun RebaseInteractive(
|
||||
rebaseInteractiveViewModel: RebaseInteractiveViewModel,
|
||||
rebaseInteractiveViewModel: RebaseInteractiveViewModel = gitnuroDynamicViewModel(),
|
||||
) {
|
||||
val rebaseState = rebaseInteractiveViewModel.rebaseState.collectAsState()
|
||||
val rebaseStateValue = rebaseState.value
|
||||
@ -35,8 +34,8 @@ fun RebaseInteractive(
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
when (rebaseStateValue) {
|
||||
is RebaseInteractiveState.Failed -> {}
|
||||
is RebaseInteractiveState.Loaded -> {
|
||||
is RebaseInteractiveViewState.Failed -> {}
|
||||
is RebaseInteractiveViewState.Loaded -> {
|
||||
RebaseStateLoaded(
|
||||
rebaseInteractiveViewModel,
|
||||
rebaseStateValue,
|
||||
@ -46,7 +45,7 @@ fun RebaseInteractive(
|
||||
)
|
||||
}
|
||||
|
||||
RebaseInteractiveState.Loading -> {
|
||||
RebaseInteractiveViewState.Loading -> {
|
||||
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
|
||||
}
|
||||
}
|
||||
@ -56,7 +55,7 @@ fun RebaseInteractive(
|
||||
@Composable
|
||||
fun RebaseStateLoaded(
|
||||
rebaseInteractiveViewModel: RebaseInteractiveViewModel,
|
||||
rebaseState: RebaseInteractiveState.Loaded,
|
||||
rebaseState: RebaseInteractiveViewState.Loaded,
|
||||
onCancel: () -> Unit,
|
||||
) {
|
||||
val stepsList = rebaseState.stepsList
|
||||
@ -220,6 +219,7 @@ fun ActionDropdown(
|
||||
val firstItemActions = listOf(
|
||||
RebaseAction.PICK,
|
||||
RebaseAction.REWORD,
|
||||
RebaseAction.DROP,
|
||||
)
|
||||
|
||||
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.text.font.FontWeight
|
||||
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.LocalTabScope
|
||||
import com.jetpackduba.gitnuro.extensions.handMouseClickable
|
||||
import com.jetpackduba.gitnuro.git.DiffEntryType
|
||||
import com.jetpackduba.gitnuro.git.rebase.RebaseInteractiveState
|
||||
import com.jetpackduba.gitnuro.keybindings.KeybindingOption
|
||||
import com.jetpackduba.gitnuro.keybindings.matchesBinding
|
||||
import com.jetpackduba.gitnuro.ui.components.PrimaryButton
|
||||
@ -53,6 +54,7 @@ fun RepositoryOpenPage(
|
||||
val blameState by tabViewModel.blameState.collectAsState()
|
||||
val showHistory by tabViewModel.showHistory.collectAsState()
|
||||
val showAuthorInfo by tabViewModel.showAuthorInfo.collectAsState()
|
||||
val rebaseInteractiveState by tabViewModel.rebaseInteractiveState.collectAsState()
|
||||
|
||||
var showNewBranchDialog by remember { mutableStateOf(false) }
|
||||
var showStashWithMessageDialog by remember { mutableStateOf(false) }
|
||||
@ -129,14 +131,8 @@ fun RepositoryOpenPage(
|
||||
}
|
||||
}
|
||||
) {
|
||||
val rebaseInteractiveViewModel = tabViewModel.rebaseInteractiveViewModel
|
||||
|
||||
if (repositoryState == RepositoryState.REBASING_INTERACTIVE && rebaseInteractiveViewModel != null) {
|
||||
RebaseInteractive(rebaseInteractiveViewModel)
|
||||
} else if (repositoryState == RepositoryState.REBASING_INTERACTIVE) {
|
||||
RebaseInteractiveStartedExternally(
|
||||
onCancelRebaseInteractive = { tabViewModel.cancelRebaseInteractive() }
|
||||
)
|
||||
if (rebaseInteractiveState == RebaseInteractiveState.AwaitingInteraction) {
|
||||
RebaseInteractive()
|
||||
} else {
|
||||
val currentTabInformation = LocalTabScope.current
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
@ -366,8 +362,8 @@ fun MainContentView(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
) {
|
||||
val safeSelectedItem = selectedItem
|
||||
if (safeSelectedItem == SelectedItem.UncommitedChanges) {
|
||||
when (selectedItem) {
|
||||
SelectedItem.UncommitedChanges -> {
|
||||
UncommitedChanges(
|
||||
selectedEntryType = diffSelected,
|
||||
repositoryState = repositoryState,
|
||||
@ -394,9 +390,10 @@ fun MainContentView(
|
||||
onBlameFile = { tabViewModel.blameFile(it) },
|
||||
onHistoryFile = { tabViewModel.fileHistory(it) }
|
||||
)
|
||||
} else if (safeSelectedItem is SelectedItem.CommitBasedItem) {
|
||||
}
|
||||
is SelectedItem.CommitBasedItem -> {
|
||||
CommitChanges(
|
||||
selectedItem = safeSelectedItem,
|
||||
selectedItem = selectedItem,
|
||||
diffSelected = diffSelected,
|
||||
onDiffSelected = { diffEntry ->
|
||||
tabViewModel.minimizeBlame()
|
||||
@ -406,6 +403,8 @@ fun MainContentView(
|
||||
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.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.jetpackduba.gitnuro.AppIcons
|
||||
import com.jetpackduba.gitnuro.extensions.*
|
||||
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.StatusType
|
||||
import com.jetpackduba.gitnuro.keybindings.KeybindingOption
|
||||
@ -66,14 +66,19 @@ fun UncommitedChanges(
|
||||
val stagedListState by statusViewModel.stagedLazyListState.collectAsState()
|
||||
val unstagedListState by statusViewModel.unstagedLazyListState.collectAsState()
|
||||
val isAmend by statusViewModel.isAmend.collectAsState()
|
||||
val isAmendRebaseInteractive by statusViewModel.isAmendRebaseInteractive.collectAsState()
|
||||
val committerDataRequestState = statusViewModel.committerDataRequestState.collectAsState()
|
||||
val committerDataRequestStateValue = committerDataRequestState.value
|
||||
val rebaseInteractiveState = statusViewModel.rebaseInteractiveState.collectAsState().value
|
||||
|
||||
val showSearchStaged by statusViewModel.showSearchStaged.collectAsState()
|
||||
val searchFilterStaged by statusViewModel.searchFilterStaged.collectAsState()
|
||||
val showSearchUnstaged by statusViewModel.showSearchUnstaged.collectAsState()
|
||||
val searchFilterUnstaged by statusViewModel.searchFilterUnstaged.collectAsState()
|
||||
|
||||
val isAmenableRebaseInteractive =
|
||||
repositoryState.isRebasing && rebaseInteractiveState is RebaseInteractiveState.ProcessingCommits && rebaseInteractiveState.isCurrentStepAmenable
|
||||
|
||||
val staged: List<StatusEntry>
|
||||
val unstaged: List<StatusEntry>
|
||||
val isLoading: Boolean
|
||||
@ -247,9 +252,9 @@ fun UncommitedChanges(
|
||||
|
||||
statusViewModel.updateCommitMessage(it)
|
||||
},
|
||||
enabled = !repositoryState.isRebasing,
|
||||
enabled = !repositoryState.isRebasing || isAmenableRebaseInteractive,
|
||||
label = {
|
||||
val text = if (repositoryState.isRebasing) {
|
||||
val text = if (repositoryState.isRebasing && !isAmenableRebaseInteractive) {
|
||||
"Commit message (read-only)"
|
||||
} else {
|
||||
"Write your commit message here"
|
||||
@ -275,15 +280,20 @@ fun UncommitedChanges(
|
||||
onMerge = { doCommit() }
|
||||
)
|
||||
|
||||
repositoryState.isRebasing -> RebasingButtons(
|
||||
canContinue = staged.isNotEmpty() || unstaged.isNotEmpty(),
|
||||
repositoryState.isRebasing && rebaseInteractiveState is RebaseInteractiveState.ProcessingCommits -> RebasingButtons(
|
||||
canContinue = staged.isNotEmpty() || unstaged.isNotEmpty() || (isAmenableRebaseInteractive && isAmendRebaseInteractive && commitMessage.isNotEmpty()),
|
||||
haveConflictsBeenSolved = unstaged.isEmpty(),
|
||||
onAbort = {
|
||||
statusViewModel.abortRebase()
|
||||
statusViewModel.updateCommitMessage("")
|
||||
},
|
||||
onContinue = { statusViewModel.continueRebase() },
|
||||
onContinue = { statusViewModel.continueRebase(commitMessage) },
|
||||
onSkip = { statusViewModel.skipRebase() },
|
||||
isAmendable = rebaseInteractiveState.isCurrentStepAmenable,
|
||||
isAmend = isAmendRebaseInteractive,
|
||||
onAmendChecked = { isAmend ->
|
||||
statusViewModel.amendRebaseInteractive(isAmend)
|
||||
}
|
||||
)
|
||||
|
||||
repositoryState.isCherryPicking -> CherryPickingButtons(
|
||||
@ -338,31 +348,11 @@ fun UncommitedChangesButtons(
|
||||
"Commit"
|
||||
|
||||
Column {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.handMouseClickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = null,
|
||||
) {
|
||||
onAmendChecked(!isAmend)
|
||||
}
|
||||
) {
|
||||
Checkbox(
|
||||
checked = isAmend,
|
||||
onCheckedChange = {
|
||||
onAmendChecked(!isAmend)
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(all = 8.dp)
|
||||
.size(12.dp)
|
||||
CheckboxText(
|
||||
value = isAmend,
|
||||
onCheckedChange = { onAmendChecked(!isAmend) },
|
||||
text = "Amend previous commit"
|
||||
)
|
||||
|
||||
Text(
|
||||
"Amend previous commit",
|
||||
style = MaterialTheme.typography.caption,
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
)
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(top = 2.dp)
|
||||
@ -439,11 +429,23 @@ fun CherryPickingButtons(
|
||||
@Composable
|
||||
fun RebasingButtons(
|
||||
canContinue: Boolean,
|
||||
isAmendable: Boolean,
|
||||
isAmend: Boolean,
|
||||
onAmendChecked: (Boolean) -> Unit,
|
||||
haveConflictsBeenSolved: Boolean,
|
||||
onAbort: () -> Unit,
|
||||
onContinue: () -> Unit,
|
||||
onSkip: () -> Unit,
|
||||
) {
|
||||
Column {
|
||||
if (isAmendable) {
|
||||
CheckboxText(
|
||||
value = isAmend,
|
||||
onCheckedChange = { onAmendChecked(!isAmend) },
|
||||
text = "Amend previous commit"
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
@ -468,11 +470,11 @@ fun RebasingButtons(
|
||||
text = "Skip",
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(end = 4.dp),
|
||||
.padding(start = 4.dp),
|
||||
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(
|
||||
totalSize: Int,
|
||||
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.log.*
|
||||
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.PullFromSpecificBranchUseCase
|
||||
import com.jetpackduba.gitnuro.git.remote_operations.PushToSpecificBranchUseCase
|
||||
@ -65,6 +66,7 @@ class LogViewModel @Inject constructor(
|
||||
private val createTagOnCommitUseCase: CreateTagOnCommitUseCase,
|
||||
private val deleteTagUseCase: DeleteTagUseCase,
|
||||
private val rebaseBranchUseCase: RebaseBranchUseCase,
|
||||
private val startRebaseInteractiveUseCase: StartRebaseInteractiveUseCase,
|
||||
private val tabState: TabState,
|
||||
private val appSettings: AppSettings,
|
||||
private val tabScope: CoroutineScope,
|
||||
@ -97,7 +99,6 @@ class LogViewModel @Inject constructor(
|
||||
val verticalListState = MutableStateFlow(LazyListState(0, 0))
|
||||
val horizontalListState = MutableStateFlow(ScrollState(0))
|
||||
|
||||
|
||||
private val _logSearchFilterResults = MutableStateFlow<LogSearch>(LogSearch.NotSearching)
|
||||
val logSearchFilterResults: StateFlow<LogSearch> = _logSearchFilterResults
|
||||
|
||||
@ -428,10 +429,10 @@ class LogViewModel @Inject constructor(
|
||||
_logSearchFilterResults.value = LogSearch.NotSearching
|
||||
}
|
||||
|
||||
fun rebaseInteractive(revCommit: RevCommit) = tabState.runOperation(
|
||||
refreshType = RefreshType.NONE
|
||||
) {
|
||||
tabState.emitNewTaskEvent(TaskEvent.RebaseInteractive(revCommit))
|
||||
fun rebaseInteractive(revCommit: RevCommit) = tabState.safeProcessing(
|
||||
refreshType = RefreshType.REBASE_INTERACTIVE_STATE,
|
||||
) { git ->
|
||||
startRebaseInteractiveUseCase(git, revCommit)
|
||||
}
|
||||
|
||||
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.git.RefreshType
|
||||
import com.jetpackduba.gitnuro.git.TabState
|
||||
import com.jetpackduba.gitnuro.git.rebase.AbortRebaseUseCase
|
||||
import com.jetpackduba.gitnuro.git.rebase.GetRebaseLinesFullMessageUseCase
|
||||
import com.jetpackduba.gitnuro.git.rebase.ResumeRebaseInteractiveUseCase
|
||||
import com.jetpackduba.gitnuro.git.rebase.StartRebaseInteractiveUseCase
|
||||
import com.jetpackduba.gitnuro.git.rebase.*
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import org.eclipse.jgit.api.RebaseCommand.InteractiveHandler
|
||||
@ -22,21 +19,23 @@ private const val TAG = "RebaseInteractiveViewMo"
|
||||
class RebaseInteractiveViewModel @Inject constructor(
|
||||
private val tabState: TabState,
|
||||
private val getRebaseLinesFullMessageUseCase: GetRebaseLinesFullMessageUseCase,
|
||||
private val startRebaseInteractiveUseCase: StartRebaseInteractiveUseCase,
|
||||
private val getRebaseInteractiveTodoLinesUseCase: GetRebaseInteractiveTodoLinesUseCase,
|
||||
private val abortRebaseUseCase: AbortRebaseUseCase,
|
||||
private val resumeRebaseInteractiveUseCase: ResumeRebaseInteractiveUseCase,
|
||||
) {
|
||||
private lateinit var commit: RevCommit
|
||||
private val _rebaseState = MutableStateFlow<RebaseInteractiveState>(RebaseInteractiveState.Loading)
|
||||
val rebaseState: StateFlow<RebaseInteractiveState> = _rebaseState
|
||||
private val _rebaseState = MutableStateFlow<RebaseInteractiveViewState>(RebaseInteractiveViewState.Loading)
|
||||
val rebaseState: StateFlow<RebaseInteractiveViewState> = _rebaseState
|
||||
var rewordSteps = ArrayDeque<RebaseLine>()
|
||||
|
||||
var onRebaseComplete: () -> Unit = {}
|
||||
init {
|
||||
loadRebaseInteractiveData()
|
||||
}
|
||||
|
||||
private var interactiveHandlerContinue = object : InteractiveHandler {
|
||||
override fun prepareSteps(steps: MutableList<RebaseTodoLine>) {
|
||||
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
|
||||
}
|
||||
|
||||
@ -56,7 +55,7 @@ class RebaseInteractiveViewModel @Inject constructor(
|
||||
val step = rewordSteps.removeLastOrNull() ?: return commit
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@ -65,19 +64,11 @@ class RebaseInteractiveViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun startRebaseInteractive(revCommit: RevCommit) = tabState.safeProcessing(
|
||||
refreshType = RefreshType.ALL_DATA,
|
||||
showError = true
|
||||
private fun loadRebaseInteractiveData() = tabState.safeProcessing(
|
||||
refreshType = RefreshType.NONE,
|
||||
) { git ->
|
||||
this@RebaseInteractiveViewModel.commit = revCommit
|
||||
|
||||
val interactiveHandler = object : InteractiveHandler {
|
||||
override fun prepareSteps(steps: MutableList<RebaseTodoLine>?) {}
|
||||
override fun modifyCommitMessage(message: String?): String = ""
|
||||
}
|
||||
|
||||
try {
|
||||
val lines = startRebaseInteractiveUseCase(git, interactiveHandler, revCommit, true)
|
||||
val lines = getRebaseInteractiveTodoLinesUseCase(git)
|
||||
val messages = getRebaseLinesFullMessageUseCase(tabState.git, lines)
|
||||
val rebaseLines = lines.map {
|
||||
RebaseLine(
|
||||
@ -87,7 +78,7 @@ class RebaseInteractiveViewModel @Inject constructor(
|
||||
)
|
||||
}
|
||||
|
||||
_rebaseState.value = RebaseInteractiveState.Loaded(rebaseLines, messages)
|
||||
_rebaseState.value = RebaseInteractiveViewState.Loaded(rebaseLines, messages)
|
||||
|
||||
} catch (ex: Exception) {
|
||||
if (ex is RebaseCancelledException) {
|
||||
@ -102,17 +93,13 @@ class RebaseInteractiveViewModel @Inject constructor(
|
||||
fun continueRebaseInteractive() = tabState.safeProcessing(
|
||||
refreshType = RefreshType.ALL_DATA,
|
||||
) { git ->
|
||||
try {
|
||||
resumeRebaseInteractiveUseCase(git, interactiveHandlerContinue)
|
||||
} finally {
|
||||
onRebaseComplete()
|
||||
}
|
||||
}
|
||||
|
||||
fun onCommitMessageChanged(commit: AbbreviatedObjectId, message: String) {
|
||||
val rebaseState = _rebaseState.value
|
||||
|
||||
if (rebaseState !is RebaseInteractiveState.Loaded)
|
||||
if (rebaseState !is RebaseInteractiveViewState.Loaded)
|
||||
return
|
||||
|
||||
val messagesMap = rebaseState.messages.toMutableMap()
|
||||
@ -124,7 +111,7 @@ class RebaseInteractiveViewModel @Inject constructor(
|
||||
fun onCommitActionChanged(commit: AbbreviatedObjectId, rebaseAction: RebaseAction) {
|
||||
val rebaseState = _rebaseState.value
|
||||
|
||||
if (rebaseState !is RebaseInteractiveState.Loaded)
|
||||
if (rebaseState !is RebaseInteractiveViewState.Loaded)
|
||||
return
|
||||
|
||||
val newStepsList =
|
||||
@ -149,17 +136,17 @@ class RebaseInteractiveViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
fun cancel() = tabState.runOperation(
|
||||
refreshType = RefreshType.REPO_STATE
|
||||
refreshType = RefreshType.ALL_DATA,
|
||||
) { git ->
|
||||
abortRebaseUseCase(git)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sealed interface RebaseInteractiveState {
|
||||
object Loading : RebaseInteractiveState
|
||||
data class Loaded(val stepsList: List<RebaseLine>, val messages: Map<String, String>) : RebaseInteractiveState
|
||||
data class Failed(val error: String) : RebaseInteractiveState
|
||||
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(
|
||||
|
@ -2,6 +2,7 @@ package com.jetpackduba.gitnuro.viewmodels
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import com.jetpackduba.gitnuro.SharedRepositoryStateManager
|
||||
import com.jetpackduba.gitnuro.extensions.delayedStateChange
|
||||
import com.jetpackduba.gitnuro.extensions.isMerging
|
||||
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.log.CheckHasPreviousCommitsUseCase
|
||||
import com.jetpackduba.gitnuro.git.log.GetLastCommitMessageUseCase
|
||||
import com.jetpackduba.gitnuro.git.rebase.AbortRebaseUseCase
|
||||
import com.jetpackduba.gitnuro.git.rebase.ContinueRebaseUseCase
|
||||
import com.jetpackduba.gitnuro.git.rebase.SkipRebaseUseCase
|
||||
import com.jetpackduba.gitnuro.git.log.GetSpecificCommitMessageUseCase
|
||||
import com.jetpackduba.gitnuro.git.rebase.*
|
||||
import com.jetpackduba.gitnuro.git.repository.GetRepositoryStateUseCase
|
||||
import com.jetpackduba.gitnuro.git.repository.ResetRepositoryStateUseCase
|
||||
import com.jetpackduba.gitnuro.git.workspace.*
|
||||
import com.jetpackduba.gitnuro.models.AuthorInfo
|
||||
@ -52,6 +53,10 @@ class StatusViewModel @Inject constructor(
|
||||
private val doCommitUseCase: DoCommitUseCase,
|
||||
private val loadAuthorUseCase: LoadAuthorUseCase,
|
||||
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 appSettings: AppSettings,
|
||||
) {
|
||||
@ -68,6 +73,7 @@ class StatusViewModel @Inject constructor(
|
||||
val searchFilterStaged: StateFlow<TextFieldValue> = _searchFilterStaged
|
||||
|
||||
val swapUncommitedChanges = appSettings.swapUncommitedChangesFlow
|
||||
val rebaseInteractiveState = sharedRepositoryStateManager.rebaseInteractiveState
|
||||
|
||||
private val _stageState = MutableStateFlow<StageState>(StageState.Loading)
|
||||
|
||||
@ -123,6 +129,9 @@ class StatusViewModel @Inject constructor(
|
||||
private val _isAmend = MutableStateFlow(false)
|
||||
val isAmend: StateFlow<Boolean> = _isAmend
|
||||
|
||||
private val _isAmendRebaseInteractive = MutableStateFlow(false)
|
||||
val isAmendRebaseInteractive: StateFlow<Boolean> = _isAmendRebaseInteractive
|
||||
|
||||
init {
|
||||
tabScope.launch {
|
||||
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(
|
||||
refreshType = RefreshType.ALL_DATA,
|
||||
) { git ->
|
||||
@ -272,9 +289,18 @@ class StatusViewModel @Inject constructor(
|
||||
} else
|
||||
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 personIdent = if (
|
||||
return if (
|
||||
author.name.isNullOrEmpty() && author.globalName.isNullOrEmpty() ||
|
||||
author.email.isNullOrEmpty() && author.globalEmail.isNullOrEmpty()
|
||||
) {
|
||||
@ -299,10 +325,6 @@ class StatusViewModel @Inject constructor(
|
||||
}
|
||||
} else
|
||||
null
|
||||
|
||||
doCommitUseCase(git, commitMessage, amend, personIdent)
|
||||
updateCommitMessage("")
|
||||
_isAmend.value = false
|
||||
}
|
||||
|
||||
suspend fun refresh(git: Git) = withContext(Dispatchers.IO) {
|
||||
@ -326,9 +348,25 @@ class StatusViewModel @Inject constructor(
|
||||
return (hasNowUncommitedChanges != hadUncommitedChanges)
|
||||
}
|
||||
|
||||
fun continueRebase() = tabState.safeProcessing(
|
||||
fun continueRebase(message: String) = tabState.safeProcessing(
|
||||
refreshType = RefreshType.ALL_DATA,
|
||||
) { 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)
|
||||
}
|
||||
|
||||
@ -373,6 +411,22 @@ class StatusViewModel @Inject constructor(
|
||||
_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() {
|
||||
this._committerDataRequestState.value = CommitterDataRequestState.Reject
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
package com.jetpackduba.gitnuro.viewmodels
|
||||
|
||||
import com.jetpackduba.gitnuro.SharedRepositoryStateManager
|
||||
import com.jetpackduba.gitnuro.credentials.CredentialsAccepted
|
||||
import com.jetpackduba.gitnuro.credentials.CredentialsState
|
||||
import com.jetpackduba.gitnuro.credentials.CredentialsStateManager
|
||||
import com.jetpackduba.gitnuro.git.*
|
||||
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.InitLocalRepositoryUseCase
|
||||
import com.jetpackduba.gitnuro.git.repository.OpenRepositoryUseCase
|
||||
@ -54,7 +56,6 @@ class TabViewModel @Inject constructor(
|
||||
private val openRepositoryUseCase: OpenRepositoryUseCase,
|
||||
private val openSubmoduleRepositoryUseCase: OpenSubmoduleRepositoryUseCase,
|
||||
private val diffViewModelProvider: Provider<DiffViewModel>,
|
||||
private val rebaseInteractiveViewModelProvider: Provider<RebaseInteractiveViewModel>,
|
||||
private val historyViewModelProvider: Provider<HistoryViewModel>,
|
||||
private val authorViewModelProvider: Provider<AuthorViewModel>,
|
||||
private val tabState: TabState,
|
||||
@ -65,9 +66,10 @@ class TabViewModel @Inject constructor(
|
||||
private val createBranchUseCase: CreateBranchUseCase,
|
||||
private val stashChangesUseCase: StashChangesUseCase,
|
||||
private val stageUntrackedFileUseCase: StageUntrackedFileUseCase,
|
||||
private val abortRebaseUseCase: AbortRebaseUseCase,
|
||||
private val openFilePickerUseCase: OpenFilePickerUseCase,
|
||||
private val openUrlInBrowserUseCase: OpenUrlInBrowserUseCase,
|
||||
private val getRebaseInteractiveStateUseCase: GetRebaseInteractiveStateUseCase,
|
||||
private val sharedRepositoryStateManager: SharedRepositoryStateManager,
|
||||
private val tabsManager: TabsManager,
|
||||
private val tabScope: CoroutineScope,
|
||||
) {
|
||||
@ -76,13 +78,13 @@ class TabViewModel @Inject constructor(
|
||||
val selectedItem: StateFlow<SelectedItem> = tabState.selectedItem
|
||||
var diffViewModel: DiffViewModel? = null
|
||||
|
||||
var rebaseInteractiveViewModel: RebaseInteractiveViewModel? = null
|
||||
private set
|
||||
|
||||
private val _repositorySelectionStatus = MutableStateFlow<RepositorySelectionStatus>(RepositorySelectionStatus.None)
|
||||
val repositorySelectionStatus: StateFlow<RepositorySelectionStatus>
|
||||
get() = _repositorySelectionStatus
|
||||
|
||||
val repositoryState: StateFlow<RepositoryState> = sharedRepositoryStateManager.repositoryState
|
||||
val rebaseInteractiveState: StateFlow<RebaseInteractiveState> = sharedRepositoryStateManager.rebaseInteractiveState
|
||||
|
||||
val processing: StateFlow<ProcessingState> = tabState.processing
|
||||
|
||||
val credentialsState: StateFlow<CredentialsState> = credentialsStateManager.credentialsState
|
||||
@ -97,9 +99,6 @@ class TabViewModel @Inject constructor(
|
||||
updateDiffEntry()
|
||||
}
|
||||
|
||||
private val _repositoryState = MutableStateFlow(RepositoryState.SAFE)
|
||||
val repositoryState: StateFlow<RepositoryState> = _repositoryState
|
||||
|
||||
private val _blameState = MutableStateFlow<BlameState>(BlameState.None)
|
||||
val blameState: StateFlow<BlameState> = _blameState
|
||||
|
||||
@ -123,28 +122,8 @@ class TabViewModel @Inject constructor(
|
||||
init {
|
||||
tabScope.run {
|
||||
launch {
|
||||
tabState.refreshData.collect { refreshType ->
|
||||
when (refreshType) {
|
||||
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)
|
||||
tabState.refreshFlowFiltered(RefreshType.ALL_DATA, RefreshType.REPO_STATE) {
|
||||
loadAuthorInfo(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,
|
||||
@ -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) {
|
||||
val config = git.repository.config
|
||||
config.load()
|
||||
@ -250,13 +206,6 @@ class TabViewModel @Inject constructor(
|
||||
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) {
|
||||
val ignored = git.status().call().ignoredNotInIndex.toList()
|
||||
var asyncJob: Job? = null
|
||||
@ -470,13 +419,6 @@ class TabViewModel @Inject constructor(
|
||||
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) {
|
||||
credentialsStateManager.updateState(CredentialsAccepted.GpgCredentialsAccepted(password))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user