From 15827d119aedf7b6c33967985b3397f788a2f4a6 Mon Sep 17 00:00:00 2001 From: Abdelilah El Aissaoui Date: Sun, 19 Jun 2022 19:52:53 +0200 Subject: [PATCH] Added option to discard hunks --- .../kotlin/app/extensions/StringExtensions.kt | 11 ++++ src/main/kotlin/app/git/StatusManager.kt | 52 +++++++++++++++++++ src/main/kotlin/app/ui/Diff.kt | 21 ++++++-- src/main/kotlin/app/ui/FileHistory.kt | 3 +- .../kotlin/app/viewmodels/DiffViewModel.kt | 7 +++ 5 files changed, 90 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/app/extensions/StringExtensions.kt b/src/main/kotlin/app/extensions/StringExtensions.kt index 9df07de..fee8f83 100644 --- a/src/main/kotlin/app/extensions/StringExtensions.kt +++ b/src/main/kotlin/app/extensions/StringExtensions.kt @@ -29,3 +29,14 @@ val String.dirPath: String } else this } + + +val String.lineDelimiter: String? + get() { + return if (this.contains("\r\n")) + "\r\n" + else if (this.contains("\n")) + "\n" + else + null + } \ No newline at end of file diff --git a/src/main/kotlin/app/git/StatusManager.kt b/src/main/kotlin/app/git/StatusManager.kt index f5aeb73..f0a511b 100644 --- a/src/main/kotlin/app/git/StatusManager.kt +++ b/src/main/kotlin/app/git/StatusManager.kt @@ -20,6 +20,8 @@ import org.eclipse.jgit.lib.FileMode import org.eclipse.jgit.lib.ObjectInserter import org.eclipse.jgit.lib.Repository import java.io.ByteArrayInputStream +import java.io.File +import java.io.FileWriter import java.io.IOException import java.nio.ByteBuffer import java.time.Instant @@ -145,6 +147,10 @@ class StatusManager @Inject constructor( val content = rawFile.rawContent.toString(Charsets.UTF_8)//.removeSuffix(rawFile.lineDelimiter) val lineDelimiter: String? = rawFile.lineDelimiter + return getTextLines(content, lineDelimiter) + } + + private fun getTextLines(content: String, lineDelimiter: String?): List { var splitted: List = if (lineDelimiter != null) { content.split(lineDelimiter).toMutableList().apply { if (this.last() == "") @@ -344,6 +350,52 @@ class StatusManager @Inject constructor( addCommand.call() } } + + suspend fun resetHunk(git: Git, diffEntry: DiffEntry, hunk: Hunk) = withContext(Dispatchers.IO) { + val repository = git.repository + + try { + val file = File(repository.directory.parent, diffEntry.oldPath) + + val content = file.readText() + val textLines = getTextLines(content, content.lineDelimiter).toMutableList() + val hunkLines = hunk.lines.filter { it.lineType != LineType.CONTEXT } + + val addedLines = hunkLines + .filter { it.lineType == LineType.ADDED } + .sortedBy { it.newLineNumber } + val removedLines = hunkLines + .filter { it.lineType == LineType.REMOVED } + .sortedBy { it.newLineNumber } + + var linesRemoved = 0 + + // Start by removing the added lines to the index + for (line in addedLines) { + textLines.removeAt(line.newLineNumber + linesRemoved) + linesRemoved-- + } + + var linesAdded = 0 + + // Restore previously removed lines to the index + for (line in removedLines) { + // Check how many lines before this one have been deleted + val previouslyRemovedLines = addedLines.count { it.newLineNumber < line.newLineNumber } + textLines.add(line.newLineNumber + linesAdded - previouslyRemovedLines, line.text) + linesAdded++ + } + + val stagedFileText = textLines.joinToString("") + + + FileWriter(file).use { fw -> + fw.write(stagedFileText) + } + } catch (ex: Exception) { + throw Exception("Discard hunk failed. Check if the file still exists and has the write permissions set", ex) + } + } } diff --git a/src/main/kotlin/app/ui/Diff.kt b/src/main/kotlin/app/ui/Diff.kt index 408471f..fbbf064 100644 --- a/src/main/kotlin/app/ui/Diff.kt +++ b/src/main/kotlin/app/ui/Diff.kt @@ -105,9 +105,13 @@ fun Diff( onUnstageHunk = { entry, hunk -> diffViewModel.unstageHunk(entry, hunk) }, - ) { entry, hunk -> - diffViewModel.stageHunk(entry, hunk) - } + onStageHunk = { entry, hunk -> + diffViewModel.stageHunk(entry, hunk) + }, + onResetHunk = { entry, hunk -> + diffViewModel.resetHunk(entry, hunk) + } + ) } else if (diffResult is DiffResult.NonText) { NonTextDiff(diffResult) } @@ -220,6 +224,7 @@ fun TextDiff( diffResult: DiffResult.Text, onUnstageHunk: (DiffEntry, Hunk) -> Unit, onStageHunk: (DiffEntry, Hunk) -> Unit, + onResetHunk: (DiffEntry, Hunk) -> Unit, ) { val hunks = diffResult.hunks @@ -237,6 +242,7 @@ fun TextDiff( diffEntryType = diffEntryType, onUnstageHunk = { onUnstageHunk(diffResult.diffEntry, hunk) }, onStageHunk = { onStageHunk(diffResult.diffEntry, hunk) }, + onResetHunk = { onResetHunk(diffResult.diffEntry, hunk) }, ) } } @@ -261,6 +267,7 @@ fun HunkHeader( diffEntryType: DiffEntryType, onUnstageHunk: () -> Unit, onStageHunk: () -> Unit, + onResetHunk: () -> Unit, ) { Row( modifier = Modifier @@ -293,6 +300,14 @@ fun HunkHeader( color = MaterialTheme.colors.stageButton } + if(diffEntryType is DiffEntryType.UnstagedDiff) { + SecondaryButton( + text = "Discard hunk", + backgroundButton = MaterialTheme.colors.error, + onClick = onResetHunk + ) + } + SecondaryButton( text = buttonText, backgroundButton = color, diff --git a/src/main/kotlin/app/ui/FileHistory.kt b/src/main/kotlin/app/ui/FileHistory.kt index a9c9c8b..a7828c1 100644 --- a/src/main/kotlin/app/ui/FileHistory.kt +++ b/src/main/kotlin/app/ui/FileHistory.kt @@ -173,7 +173,8 @@ fun HistoryContentLoaded( scrollState = scrollState, diffResult = diffResult, onUnstageHunk = { _, _ -> }, - onStageHunk = { _, _ -> } + onStageHunk = { _, _ -> }, + onResetHunk = { _, _ -> }, ) } else { Box( diff --git a/src/main/kotlin/app/viewmodels/DiffViewModel.kt b/src/main/kotlin/app/viewmodels/DiffViewModel.kt index 40e49a9..02b1bce 100644 --- a/src/main/kotlin/app/viewmodels/DiffViewModel.kt +++ b/src/main/kotlin/app/viewmodels/DiffViewModel.kt @@ -73,6 +73,13 @@ class DiffViewModel @Inject constructor( statusManager.stageHunk(git, diffEntry, hunk) } + fun resetHunk(diffEntry: DiffEntry, hunk: Hunk) = tabState.runOperation( + refreshType = RefreshType.UNCOMMITED_CHANGES, + showError = true, + ) { git -> + statusManager.resetHunk(git, diffEntry, hunk) + } + fun unstageHunk(diffEntry: DiffEntry, hunk: Hunk) = tabState.runOperation( refreshType = RefreshType.UNCOMMITED_CHANGES, ) { git ->