Implemented amend previous commit

This commit is contained in:
Abdelilah El Aissaoui 2022-02-05 03:01:03 +01:00
parent e473d29167
commit ac21b59f6c
7 changed files with 136 additions and 25 deletions

View File

@ -18,6 +18,7 @@ Right now you CAN:
- Commit.
- Reset commits.
- Revert commits.
- Amend previous commit.
- Merge.
- Rebase.
- Create and delete branches locally.

View File

@ -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,

View File

@ -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()
}

View File

@ -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)
}

View File

@ -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,

View File

@ -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()

View File

@ -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)