Added conflict indicator during merge in uncommited changes
This commit is contained in:
parent
2313ad4591
commit
c0c07ef5b1
@ -1,12 +1,16 @@
|
||||
package app.git
|
||||
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Warning
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import app.di.RawFileManagerFactory
|
||||
import app.extensions.filePath
|
||||
import app.extensions.hasUntrackedChanges
|
||||
import app.extensions.isMerging
|
||||
import app.extensions.withoutLineEnding
|
||||
import app.extensions.*
|
||||
import app.git.diff.Hunk
|
||||
import app.git.diff.LineType
|
||||
import app.theme.conflictFile
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@ -66,38 +70,53 @@ class StatusManager @Inject constructor(
|
||||
loadHasUncommitedChanges(git)
|
||||
val currentBranch = branchesManager.currentBranchRef(git)
|
||||
val repositoryState = git.repository.repositoryState
|
||||
val staged = git.diff().apply {
|
||||
if (currentBranch == null && !repositoryState.isMerging && !repositoryState.isRebasing)
|
||||
setOldTree(EmptyTreeIterator()) // Required if the repository is empty
|
||||
|
||||
setCached(true)
|
||||
}
|
||||
val staged = git
|
||||
.diff()
|
||||
.setShowNameAndStatusOnly(true).apply {
|
||||
if (currentBranch == null && !repositoryState.isMerging && !repositoryState.isRebasing)
|
||||
setOldTree(EmptyTreeIterator()) // Required if the repository is empty
|
||||
|
||||
setCached(true)
|
||||
}
|
||||
.call()
|
||||
// TODO: Grouping and fitlering allows us to remove duplicates when conflicts appear, requires more testing (what happens in windows? /dev/null is a unix thing)
|
||||
.groupBy { it.oldPath }
|
||||
// TODO: Test if we should group by old path or new path
|
||||
.groupBy {
|
||||
if(it.newPath != "/dev/null")
|
||||
it.newPath
|
||||
else
|
||||
it.oldPath
|
||||
}
|
||||
.map {
|
||||
val entries = it.value
|
||||
|
||||
if (entries.count() > 1 && (repositoryState.isMerging || repositoryState.isRebasing))
|
||||
entries.filter { entry -> entry.oldPath != "/dev/null" }
|
||||
else
|
||||
entries
|
||||
}.flatten()
|
||||
val hasConflicts =
|
||||
(entries.count() > 1 && (repositoryState.isMerging || repositoryState.isRebasing))
|
||||
|
||||
StatusEntry(entries.first(), isConflict = hasConflicts)
|
||||
}
|
||||
|
||||
ensureActive()
|
||||
|
||||
val unstaged = git
|
||||
.diff()
|
||||
.setShowNameAndStatusOnly(true)
|
||||
.call()
|
||||
.groupBy { it.oldPath }
|
||||
.groupBy {
|
||||
if(it.oldPath != "/dev/null")
|
||||
it.oldPath
|
||||
else
|
||||
it.newPath
|
||||
}
|
||||
.map {
|
||||
val entries = it.value
|
||||
|
||||
if (entries.count() > 1 && (repositoryState.isMerging || repositoryState.isRebasing))
|
||||
entries.filter { entry -> entry.newPath != "/dev/null" }
|
||||
else
|
||||
entries
|
||||
}.flatten()
|
||||
val hasConflicts =
|
||||
(entries.count() > 1 && (repositoryState.isMerging || repositoryState.isRebasing))
|
||||
|
||||
StatusEntry(entries.first(), isConflict = hasConflicts)
|
||||
}
|
||||
|
||||
ensureActive()
|
||||
_stageStatus.value = StageStatus.Loaded(staged, unstaged)
|
||||
@ -209,7 +228,7 @@ class StatusManager @Inject constructor(
|
||||
|
||||
loadStatus(git)
|
||||
} finally {
|
||||
if(completedWithErrors)
|
||||
if (completedWithErrors)
|
||||
dirCache.unlock()
|
||||
}
|
||||
}
|
||||
@ -301,5 +320,23 @@ class StatusManager @Inject constructor(
|
||||
|
||||
sealed class StageStatus {
|
||||
object Loading : StageStatus()
|
||||
data class Loaded(val staged: List<DiffEntry>, val unstaged: List<DiffEntry>) : StageStatus()
|
||||
data class Loaded(val staged: List<StatusEntry>, val unstaged: List<StatusEntry>) : StageStatus()
|
||||
}
|
||||
|
||||
data class StatusEntry(val diffEntry: DiffEntry, val isConflict: Boolean) {
|
||||
val icon: ImageVector
|
||||
get() {
|
||||
return if (isConflict)
|
||||
Icons.Default.Warning
|
||||
else
|
||||
diffEntry.icon
|
||||
}
|
||||
val iconColor: Color
|
||||
@Composable
|
||||
get() {
|
||||
return if (isConflict)
|
||||
MaterialTheme.colors.conflictFile
|
||||
else
|
||||
diffEntry.iconColor
|
||||
}
|
||||
}
|
@ -24,6 +24,7 @@ val headerBackgroundDark = Color(0xFF303132)
|
||||
val addFileLight = Color(0xFF32A852)
|
||||
val deleteFileLight = errorColor
|
||||
val modifyFileLight = primary
|
||||
val conflictFileLight = Color(0xFFFFB638)
|
||||
|
||||
val tabColorActiveDark = Color(0xFF606061)
|
||||
val tabColorInactiveDark = Color(0xFF262626)
|
||||
|
@ -85,6 +85,10 @@ val Colors.deleteFile: Color
|
||||
val Colors.modifyFile: Color
|
||||
get() = modifyFileLight
|
||||
|
||||
@get:Composable
|
||||
val Colors.conflictFile: Color
|
||||
get() = conflictFileLight
|
||||
|
||||
@get:Composable
|
||||
val Colors.headerText: Color
|
||||
get() = if (isLight) primary else mainTextDark
|
||||
|
@ -30,9 +30,11 @@ import androidx.compose.ui.unit.sp
|
||||
import app.extensions.filePath
|
||||
import app.extensions.icon
|
||||
import app.extensions.iconColor
|
||||
import app.extensions.isMerging
|
||||
import app.git.DiffEntryType
|
||||
import app.git.GitManager
|
||||
import app.git.StageStatus
|
||||
import app.git.StatusEntry
|
||||
import app.theme.headerBackground
|
||||
import app.theme.headerText
|
||||
import app.theme.primaryTextColor
|
||||
@ -57,8 +59,8 @@ fun UncommitedChanges(
|
||||
gitManager.loadStatus()
|
||||
}
|
||||
|
||||
val staged: List<DiffEntry>
|
||||
val unstaged: List<DiffEntry>
|
||||
val staged: List<StatusEntry>
|
||||
val unstaged: List<StatusEntry>
|
||||
if (stageStatus is StageStatus.Loaded) {
|
||||
staged = stageStatus.staged
|
||||
unstaged = stageStatus.unstaged
|
||||
@ -74,8 +76,8 @@ fun UncommitedChanges(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
staged = listOf<DiffEntry>()
|
||||
unstaged = listOf<DiffEntry>() // return empty lists if still loading
|
||||
staged = listOf<StatusEntry>()
|
||||
unstaged = listOf<StatusEntry>() // return empty lists if still loading
|
||||
}
|
||||
|
||||
|
||||
@ -178,8 +180,14 @@ fun UncommitedChanges(
|
||||
enabled = canCommit,
|
||||
shape = RectangleShape,
|
||||
) {
|
||||
val buttonText = if(repositoryState.isMerging)
|
||||
"Merge"
|
||||
else if (repositoryState.isRebasing)
|
||||
"Continue rebasing"
|
||||
else
|
||||
"Commit"
|
||||
Text(
|
||||
text = "Commit",
|
||||
text = buttonText,
|
||||
fontSize = 14.sp,
|
||||
)
|
||||
}
|
||||
@ -190,8 +198,8 @@ fun UncommitedChanges(
|
||||
|
||||
fun checkIfSelectedEntryShouldBeUpdated(
|
||||
selectedEntryType: DiffEntryType,
|
||||
staged: List<DiffEntry>,
|
||||
unstaged: List<DiffEntry>,
|
||||
staged: List<StatusEntry>,
|
||||
unstaged: List<StatusEntry>,
|
||||
onStagedDiffEntrySelected: (DiffEntry?) -> Unit,
|
||||
onUnstagedDiffEntrySelected: (DiffEntry) -> Unit,
|
||||
) {
|
||||
@ -199,26 +207,29 @@ fun checkIfSelectedEntryShouldBeUpdated(
|
||||
val selectedEntryTypeNewId = selectedDiffEntry.newId.name()
|
||||
|
||||
if (selectedEntryType is DiffEntryType.StagedDiff) {
|
||||
val entryType = staged.firstOrNull { it.newPath == selectedDiffEntry.newPath }
|
||||
val entryType = staged.firstOrNull { stagedEntry -> stagedEntry.diffEntry.newPath == selectedDiffEntry.newPath }?.diffEntry
|
||||
|
||||
if(
|
||||
entryType != null &&
|
||||
selectedEntryTypeNewId != entryType.newId.name()
|
||||
) {
|
||||
onStagedDiffEntrySelected(entryType)
|
||||
} else if (entryType == null)
|
||||
|
||||
} else if (entryType == null) {
|
||||
onStagedDiffEntrySelected(null)
|
||||
}
|
||||
} else if(selectedEntryType is DiffEntryType.UnstagedDiff) {
|
||||
val entryType = unstaged.firstOrNull {
|
||||
val entryType = unstaged.firstOrNull { unstagedEntry ->
|
||||
if(selectedDiffEntry.changeType == DiffEntry.ChangeType.DELETE)
|
||||
it.oldPath == selectedDiffEntry.oldPath
|
||||
unstagedEntry.diffEntry.oldPath == selectedDiffEntry.oldPath
|
||||
else
|
||||
it.newPath == selectedDiffEntry.newPath
|
||||
unstagedEntry.diffEntry.newPath == selectedDiffEntry.newPath
|
||||
}
|
||||
|
||||
if(entryType != null) {
|
||||
onUnstagedDiffEntrySelected(entryType)
|
||||
} else onStagedDiffEntrySelected(null)
|
||||
onUnstagedDiffEntrySelected(entryType.diffEntry)
|
||||
} else
|
||||
onStagedDiffEntrySelected(null)
|
||||
}
|
||||
}
|
||||
|
||||
@ -229,7 +240,7 @@ private fun EntriesList(
|
||||
title: String,
|
||||
actionTitle: String,
|
||||
actionColor: Color,
|
||||
diffEntries: List<DiffEntry>,
|
||||
diffEntries: List<StatusEntry>,
|
||||
onDiffEntrySelected: (DiffEntry) -> Unit,
|
||||
onDiffEntryOptionSelected: (DiffEntry) -> Unit,
|
||||
onReset: (DiffEntry) -> Unit,
|
||||
@ -266,9 +277,10 @@ private fun EntriesList(
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colors.background),
|
||||
) {
|
||||
itemsIndexed(diffEntries) { index, diffEntry ->
|
||||
itemsIndexed(diffEntries) { index, statusEntry ->
|
||||
val diffEntry = statusEntry.diffEntry
|
||||
FileEntry(
|
||||
diffEntry = diffEntry,
|
||||
statusEntry = statusEntry,
|
||||
actionTitle = actionTitle,
|
||||
actionColor = actionColor,
|
||||
onClick = {
|
||||
@ -296,7 +308,7 @@ private fun EntriesList(
|
||||
)
|
||||
@Composable
|
||||
private fun FileEntry(
|
||||
diffEntry: DiffEntry,
|
||||
statusEntry: StatusEntry,
|
||||
actionTitle: String,
|
||||
actionColor: Color,
|
||||
onClick: () -> Unit,
|
||||
@ -304,6 +316,7 @@ private fun FileEntry(
|
||||
onReset: () -> Unit,
|
||||
) {
|
||||
var active by remember { mutableStateOf(false) }
|
||||
val diffEntry = statusEntry.diffEntry
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@ -338,12 +351,12 @@ private fun FileEntry(
|
||||
) {
|
||||
|
||||
Icon(
|
||||
imageVector = diffEntry.icon,
|
||||
imageVector = statusEntry.icon,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 8.dp)
|
||||
.size(16.dp),
|
||||
tint = diffEntry.iconColor,
|
||||
tint = statusEntry.iconColor,
|
||||
)
|
||||
|
||||
Text(
|
||||
|
Loading…
Reference in New Issue
Block a user