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