Implemented amend previous commit
This commit is contained in:
parent
e473d29167
commit
ac21b59f6c
@ -18,6 +18,7 @@ Right now you CAN:
|
||||
- Commit.
|
||||
- Reset commits.
|
||||
- Revert commits.
|
||||
- Amend previous commit.
|
||||
- Merge.
|
||||
- Rebase.
|
||||
- Create and delete branches locally.
|
||||
|
@ -70,9 +70,38 @@ class LogManager @Inject constructor(
|
||||
.setRef(revCommit.name)
|
||||
.call()
|
||||
}
|
||||
|
||||
suspend fun latestMessage(git: Git): String = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val log = git.log().setMaxCount(1).call()
|
||||
val latestCommitNode = log.firstOrNull()
|
||||
|
||||
return@withContext if(latestCommitNode == null)
|
||||
""
|
||||
else
|
||||
latestCommitNode.fullMessage
|
||||
|
||||
} catch (ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
return@withContext ""
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
suspend fun hasPreviousCommits(git: Git): Boolean = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val log = git.log().setMaxCount(1).call()
|
||||
val latestCommitNode = log.firstOrNull()
|
||||
|
||||
return@withContext latestCommitNode != null
|
||||
|
||||
} catch (ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
return@withContext false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Move this to
|
||||
enum class ResetType {
|
||||
SOFT,
|
||||
MIXED,
|
||||
|
@ -188,10 +188,11 @@ class StatusManager @Inject constructor(
|
||||
.call()
|
||||
}
|
||||
|
||||
suspend fun commit(git: Git, message: String) = withContext(Dispatchers.IO) {
|
||||
suspend fun commit(git: Git, message: String, amend: Boolean) = withContext(Dispatchers.IO) {
|
||||
git.commit()
|
||||
.setMessage(message)
|
||||
.setAllowEmpty(false)
|
||||
.setAmend(amend)
|
||||
.call()
|
||||
}
|
||||
|
||||
|
@ -122,6 +122,8 @@ class TabState @Inject constructor(
|
||||
if (showError)
|
||||
errorsManager.addError(newErrorNow(ex, ex.localizedMessage))
|
||||
} finally {
|
||||
operationRunning = false
|
||||
|
||||
if (refreshType != RefreshType.NONE && (!hasProcessFailed || refreshEvenIfCrashes))
|
||||
_refreshData.emit(refreshType)
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowDropDown
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
@ -33,6 +35,8 @@ import app.git.StatusEntry
|
||||
import app.theme.*
|
||||
import app.ui.components.ScrollableLazyColumn
|
||||
import app.ui.components.SecondaryButton
|
||||
import app.ui.context_menu.DropDownContent
|
||||
import app.ui.context_menu.DropDownContentData
|
||||
import app.ui.context_menu.stagedEntriesContextMenuItems
|
||||
import app.ui.context_menu.unstagedEntriesContextMenuItems
|
||||
import app.viewmodels.StageStatus
|
||||
@ -75,12 +79,14 @@ fun UncommitedChanges(
|
||||
unstaged = listOf() // return empty lists if still loading
|
||||
}
|
||||
|
||||
val doCommit = {
|
||||
statusViewModel.commit(commitMessage)
|
||||
val doCommit = { amend: Boolean ->
|
||||
statusViewModel.commit(commitMessage, amend)
|
||||
onStagedDiffEntrySelected(null)
|
||||
statusViewModel.newCommitMessage = ""
|
||||
}
|
||||
|
||||
val canCommit = commitMessage.isNotEmpty() && staged.isNotEmpty()
|
||||
val canAmend = (commitMessage.isNotEmpty() || staged.isNotEmpty()) && statusViewModel.hasPreviousCommits
|
||||
|
||||
Column {
|
||||
AnimatedVisibility(
|
||||
@ -91,7 +97,6 @@ fun UncommitedChanges(
|
||||
LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
|
||||
}
|
||||
|
||||
|
||||
EntriesList(
|
||||
modifier = Modifier
|
||||
.padding(start = 8.dp, end = 8.dp, bottom = 4.dp)
|
||||
@ -169,7 +174,7 @@ fun UncommitedChanges(
|
||||
.weight(weight = 1f, fill = true)
|
||||
.onPreviewKeyEvent {
|
||||
if (it.isCtrlPressed && it.key == Key.Enter && canCommit) {
|
||||
doCommit()
|
||||
doCommit(false)
|
||||
true
|
||||
} else
|
||||
false
|
||||
@ -185,7 +190,7 @@ fun UncommitedChanges(
|
||||
repositoryState.isMerging -> MergeButtons(
|
||||
haveConflictsBeenSolved = unstaged.isEmpty(),
|
||||
onAbort = { statusViewModel.abortMerge() },
|
||||
onMerge = { doCommit() }
|
||||
onMerge = { doCommit(false) }
|
||||
)
|
||||
repositoryState.isRebasing -> RebasingButtons(
|
||||
canContinue = staged.isNotEmpty() || unstaged.isNotEmpty(),
|
||||
@ -194,27 +199,86 @@ fun UncommitedChanges(
|
||||
onContinue = { statusViewModel.continueRebase() },
|
||||
onSkip = { statusViewModel.skipRebase() },
|
||||
)
|
||||
else -> {
|
||||
Button(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
onClick = doCommit,
|
||||
enabled = canCommit,
|
||||
shape = RectangleShape,
|
||||
) {
|
||||
|
||||
Text(
|
||||
text = "Commit",
|
||||
fontSize = 14.sp,
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> UncommitedChangesButtons(
|
||||
canCommit = canCommit,
|
||||
canAmend = canAmend,
|
||||
onCommit = { amend -> doCommit(amend) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UncommitedChangesButtons(
|
||||
canCommit: Boolean,
|
||||
canAmend: Boolean,
|
||||
onCommit: (Boolean) -> Unit
|
||||
) {
|
||||
var showDropDownMenu by remember { mutableStateOf(false) }
|
||||
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(top = 2.dp)
|
||||
) {
|
||||
Button(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.height(40.dp),
|
||||
onClick = { onCommit(false) },
|
||||
enabled = canCommit,
|
||||
shape = RectangleShape,
|
||||
) {
|
||||
Text(
|
||||
text = "Commit",
|
||||
fontSize = 14.sp,
|
||||
)
|
||||
}
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.width(1.dp)
|
||||
.height(40.dp),
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(40.dp)
|
||||
.background(MaterialTheme.colors.primary)
|
||||
.clickable { showDropDownMenu = true },
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.ArrowDropDown,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colors.inversePrimaryTextColor,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 8.dp)
|
||||
.align(Alignment.Center),
|
||||
)
|
||||
DropdownMenu(
|
||||
onDismissRequest = {
|
||||
showDropDownMenu = false
|
||||
},
|
||||
content = {
|
||||
DropDownContent(
|
||||
enabled = canAmend,
|
||||
dropDownContentData = DropDownContentData(
|
||||
label = "Amend previous commit",
|
||||
icon = null,
|
||||
onClick = { onCommit(true) }
|
||||
),
|
||||
onDismiss = { showDropDownMenu = false }
|
||||
)
|
||||
},
|
||||
expanded = showDropDownMenu,
|
||||
)
|
||||
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MergeButtons(
|
||||
haveConflictsBeenSolved: Boolean,
|
||||
|
@ -8,8 +8,13 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
@Composable
|
||||
fun DropDownContent(dropDownContentData: DropDownContentData, onDismiss: () -> Unit) {
|
||||
fun DropDownContent(
|
||||
dropDownContentData: DropDownContentData,
|
||||
enabled: Boolean = true,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
enabled = enabled,
|
||||
onClick = {
|
||||
dropDownContentData.onClick()
|
||||
onDismiss()
|
||||
|
@ -17,6 +17,7 @@ class StatusViewModel @Inject constructor(
|
||||
private val repositoryManager: RepositoryManager,
|
||||
private val rebaseManager: RebaseManager,
|
||||
private val mergeManager: MergeManager,
|
||||
private val logManager: LogManager,
|
||||
) {
|
||||
private val _stageStatus = MutableStateFlow<StageStatus>(StageStatus.Loaded(listOf(), listOf()))
|
||||
val stageStatus: StateFlow<StageStatus> = _stageStatus
|
||||
@ -29,6 +30,8 @@ class StatusViewModel @Inject constructor(
|
||||
_commitMessage.value = value
|
||||
}
|
||||
|
||||
var hasPreviousCommits = true // When false, disable "amend previous commit"
|
||||
|
||||
private var lastUncommitedChangesState = false
|
||||
|
||||
fun stage(diffEntry: DiffEntry) = tabState.runOperation(
|
||||
@ -89,10 +92,15 @@ class StatusViewModel @Inject constructor(
|
||||
lastUncommitedChangesState = statusManager.hasUncommitedChanges(git)
|
||||
}
|
||||
|
||||
fun commit(message: String) = tabState.safeProcessing(
|
||||
fun commit(message: String, amend: Boolean) = tabState.safeProcessing(
|
||||
refreshType = RefreshType.ALL_DATA,
|
||||
) { git ->
|
||||
statusManager.commit(git, message)
|
||||
val commitMessage = if(amend && message.isBlank()) {
|
||||
logManager.latestMessage(git)
|
||||
} else
|
||||
message
|
||||
|
||||
statusManager.commit(git, commitMessage, amend)
|
||||
}
|
||||
|
||||
suspend fun refresh(git: Git) = withContext(Dispatchers.IO) {
|
||||
@ -110,6 +118,7 @@ class StatusViewModel @Inject constructor(
|
||||
loadHasUncommitedChanges(git)
|
||||
|
||||
val hasNowUncommitedChanges = this.lastUncommitedChangesState
|
||||
hasPreviousCommits = logManager.hasPreviousCommits(git)
|
||||
|
||||
// Return true to update the log only if the uncommitedChanges status has changed
|
||||
return (hasNowUncommitedChanges != hadUncommitedChanges)
|
||||
|
Loading…
Reference in New Issue
Block a user