diff --git a/src/main/kotlin/app/di/RawFileManagerFactory.kt b/src/main/kotlin/app/di/RawFileManagerFactory.kt new file mode 100644 index 0000000..5d54d2e --- /dev/null +++ b/src/main/kotlin/app/di/RawFileManagerFactory.kt @@ -0,0 +1,16 @@ +package app.di + +import app.git.RawFileManager +import app.git.diff.HunkDiffGenerator +import dagger.assisted.AssistedFactory +import org.eclipse.jgit.lib.Repository + +@AssistedFactory +interface RawFileManagerFactory { + fun create(repository: Repository): RawFileManager +} + +@AssistedFactory +interface HunkDiffGeneratorFactory { + fun create(repository: Repository, rawFileManager: RawFileManager): HunkDiffGenerator +} \ No newline at end of file diff --git a/src/main/kotlin/app/git/DiffManager.kt b/src/main/kotlin/app/git/DiffManager.kt index ac1ef94..59e7b72 100644 --- a/src/main/kotlin/app/git/DiffManager.kt +++ b/src/main/kotlin/app/git/DiffManager.kt @@ -1,11 +1,12 @@ package app.git +import app.di.HunkDiffGeneratorFactory +import app.di.RawFileManagerFactory import app.extensions.fullData import app.git.diff.Hunk import app.git.diff.HunkDiffGenerator import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import org.apache.commons.logging.LogFactory.objectId import org.eclipse.jgit.api.Git import org.eclipse.jgit.diff.DiffEntry import org.eclipse.jgit.diff.DiffFormatter @@ -21,7 +22,10 @@ import java.io.ByteArrayOutputStream import javax.inject.Inject -class DiffManager @Inject constructor() { +class DiffManager @Inject constructor( + private val rawFileManagerFactory: RawFileManagerFactory, + private val hunkDiffGeneratorFactory: HunkDiffGeneratorFactory, +) { suspend fun diffFormat(git: Git, diffEntryType: DiffEntryType): List = withContext(Dispatchers.IO) { val diffEntry = diffEntryType.diffEntry val byteArrayOutputStream = ByteArrayOutputStream() @@ -42,7 +46,9 @@ class DiffManager @Inject constructor() { formatter.flush() } - val hunkDiffGenerator = HunkDiffGenerator(git.repository) + val rawFileManager = rawFileManagerFactory.create(repository) + val hunkDiffGenerator = hunkDiffGeneratorFactory.create(repository, rawFileManager) + val hunks = mutableListOf() hunkDiffGenerator.use { diff --git a/src/main/kotlin/app/git/RawFileManager.kt b/src/main/kotlin/app/git/RawFileManager.kt new file mode 100644 index 0000000..459f279 --- /dev/null +++ b/src/main/kotlin/app/git/RawFileManager.kt @@ -0,0 +1,55 @@ +package app.git + +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import org.eclipse.jgit.diff.ContentSource +import org.eclipse.jgit.diff.DiffEntry +import org.eclipse.jgit.diff.RawText +import org.eclipse.jgit.lib.Constants +import org.eclipse.jgit.lib.FileMode +import org.eclipse.jgit.lib.ObjectReader +import org.eclipse.jgit.lib.Repository +import org.eclipse.jgit.storage.pack.PackConfig +import org.eclipse.jgit.treewalk.AbstractTreeIterator +import org.eclipse.jgit.treewalk.WorkingTreeIterator +import org.eclipse.jgit.util.LfsFactory + +private const val DEFAULT_BINARY_FILE_THRESHOLD = PackConfig.DEFAULT_BIG_FILE_THRESHOLD + +class RawFileManager @AssistedInject constructor( + @Assisted private val repository: Repository +) : AutoCloseable { + private var reader: ObjectReader = repository.newObjectReader() + private var source: ContentSource.Pair + + init { + val cs = ContentSource.create(reader) + source = ContentSource.Pair(cs, cs) + } + + fun scan(oldTreeIterator: AbstractTreeIterator, newTreeIterator: AbstractTreeIterator) { + source = ContentSource.Pair(source(oldTreeIterator), source(newTreeIterator)) + } + + private fun source(iterator: AbstractTreeIterator): ContentSource { + return if (iterator is WorkingTreeIterator) + ContentSource.create(iterator) + else + ContentSource.create(reader) + } + + fun getRawContent(side: DiffEntry.Side, entry: DiffEntry): RawText { + if (entry.getMode(side) === FileMode.MISSING) return RawText.EMPTY_TEXT + if (entry.getMode(side).objectType != Constants.OBJ_BLOB) return RawText.EMPTY_TEXT + + val ldr = LfsFactory.getInstance().applySmudgeFilter( + repository, + source.open(side, entry), entry.diffAttribute + ) + return RawText.load(ldr, DEFAULT_BINARY_FILE_THRESHOLD) + } + + override fun close() { + reader.close() + } +} \ No newline at end of file diff --git a/src/main/kotlin/app/git/diff/Hunk.kt b/src/main/kotlin/app/git/diff/Hunk.kt index 46bf481..3c8804d 100644 --- a/src/main/kotlin/app/git/diff/Hunk.kt +++ b/src/main/kotlin/app/git/diff/Hunk.kt @@ -4,6 +4,6 @@ data class Hunk(val header: String, val lines: List) sealed class Line(val content: String) { class ContextLine(content: String, val oldLineNumber: Int, val newLineNumber: Int): Line(content) - class AddedLine(content: String, val lineNumber: Int): Line(content) + class AddedLine(content: String, val oldLineNumber: Int, val newLineNumber: Int): Line(content) class RemovedLine(content: String, val lineNumber: Int): Line(content) } \ No newline at end of file diff --git a/src/main/kotlin/app/git/diff/HunkDiffGenerator.kt b/src/main/kotlin/app/git/diff/HunkDiffGenerator.kt index e4c397e..374286a 100644 --- a/src/main/kotlin/app/git/diff/HunkDiffGenerator.kt +++ b/src/main/kotlin/app/git/diff/HunkDiffGenerator.kt @@ -1,104 +1,49 @@ package app.git.diff import app.extensions.lineAt +import app.git.RawFileManager +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject import org.eclipse.jgit.diff.* -import org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm -import org.eclipse.jgit.lib.* +import org.eclipse.jgit.lib.Repository import org.eclipse.jgit.patch.FileHeader import org.eclipse.jgit.patch.FileHeader.PatchType -import org.eclipse.jgit.storage.pack.PackConfig import org.eclipse.jgit.treewalk.AbstractTreeIterator -import org.eclipse.jgit.treewalk.WorkingTreeIterator -import org.eclipse.jgit.util.LfsFactory import java.io.ByteArrayOutputStream import java.io.IOException import kotlin.math.max import kotlin.math.min -private const val DEFAULT_BINARY_FILE_THRESHOLD = PackConfig.DEFAULT_BIG_FILE_THRESHOLD - +private const val CONTEXT_LINES = 3 /** * Generator of [Hunk] lists from [DiffEntry] */ -class HunkDiffGenerator( - private val repository: Repository, +class HunkDiffGenerator @AssistedInject constructor( + @Assisted private val repository: Repository, + @Assisted private val rawFileManager: RawFileManager, ) : AutoCloseable { - private var reader: ObjectReader? = null - private lateinit var diffCfg: DiffConfig - private lateinit var diffAlgorithm: DiffAlgorithm - private var context = 3 - private var binaryFileThreshold = DEFAULT_BINARY_FILE_THRESHOLD + private val outputStream = ByteArrayOutputStream() // Dummy output stream used for the diff formatter private val diffFormatter = DiffFormatter(outputStream).apply { setRepository(repository) } - init { - setReader(repository.newObjectReader(), repository.config) - } - - private var source: ContentSource.Pair? = null - private var quotePaths: Boolean? = null - - private fun setReader(reader: ObjectReader, cfg: Config) { - close() - this.reader = reader - diffCfg = cfg.get(DiffConfig.KEY) - if (quotePaths == null) { - quotePaths = java.lang.Boolean - .valueOf( - cfg.getBoolean( - ConfigConstants.CONFIG_CORE_SECTION, - ConfigConstants.CONFIG_KEY_QUOTE_PATH, true - ) - ) - } - val cs = ContentSource.create(reader) - source = ContentSource.Pair(cs, cs) - diffAlgorithm = DiffAlgorithm.getAlgorithm( - cfg.getEnum( - ConfigConstants.CONFIG_DIFF_SECTION, null, - ConfigConstants.CONFIG_KEY_ALGORITHM, - SupportedAlgorithm.HISTOGRAM - ) - ) - } - override fun close() { - reader?.close() outputStream.close() } fun scan(oldTreeIterator: AbstractTreeIterator, newTreeIterator: AbstractTreeIterator) { - source = ContentSource.Pair(source(oldTreeIterator), source(newTreeIterator)) + rawFileManager.scan(oldTreeIterator, newTreeIterator) diffFormatter.scan(oldTreeIterator, newTreeIterator) } - private fun source(iterator: AbstractTreeIterator): ContentSource { - return if (iterator is WorkingTreeIterator) - ContentSource.create(iterator) - else - ContentSource.create(reader) - } - fun format(ent: DiffEntry): List { val fileHeader = diffFormatter.toFileHeader(ent) - val rawOld = getRawContent(DiffEntry.Side.OLD, ent) - val rawNew = getRawContent(DiffEntry.Side.NEW, ent) + val rawOld = rawFileManager.getRawContent(DiffEntry.Side.OLD, ent) + val rawNew = rawFileManager.getRawContent(DiffEntry.Side.NEW, ent) return format(fileHeader, rawOld, rawNew) } - private fun getRawContent(side: DiffEntry.Side, entry: DiffEntry): RawText { - if (entry.getMode(side) === FileMode.MISSING) return RawText.EMPTY_TEXT - if (entry.getMode(side).objectType != Constants.OBJ_BLOB) return RawText.EMPTY_TEXT - - val ldr = LfsFactory.getInstance().applySmudgeFilter( - repository, - source!!.open(side, entry), entry.diffAttribute - ) - return RawText.load(ldr, binaryFileThreshold) - } - /** * Given a [FileHeader] and the both [RawText], generate a [List] of [Hunk] */ @@ -116,10 +61,10 @@ class HunkDiffGenerator( var curEdit = edits[curIdx] val endIdx = findCombinedEnd(edits, curIdx) val endEdit = edits[endIdx] - var oldCurrentLine = max(0, curEdit.beginA - context) - var newCurrentLine = max(0, curEdit.beginB - context) - val oldEndLine = min(oldRawText.size(), endEdit.endA + context) - val newEndLine = min(newRawText.size(), endEdit.endB + context) + var oldCurrentLine = max(0, curEdit.beginA - CONTEXT_LINES) + var newCurrentLine = max(0, curEdit.beginB - CONTEXT_LINES) + val oldEndLine = min(oldRawText.size(), endEdit.endA + CONTEXT_LINES) + val newEndLine = min(newRawText.size(), endEdit.endB + CONTEXT_LINES) val headerText = createHunkHeader(oldCurrentLine, oldEndLine, newCurrentLine, newEndLine) val lines = mutableListOf() @@ -138,7 +83,7 @@ class HunkDiffGenerator( oldCurrentLine++ } else if (newCurrentLine < curEdit.endB) { val lineText = newRawText.lineAt(newCurrentLine) - lines.add(Line.AddedLine(lineText, newCurrentLine)) + lines.add(Line.AddedLine(lineText, oldCurrentLine, newCurrentLine)) newCurrentLine++ } @@ -187,11 +132,11 @@ class HunkDiffGenerator( } private fun combineA(e: List, i: Int): Boolean { - return e[i].beginA - e[i - 1].endA <= 2 * context + return e[i].beginA - e[i - 1].endA <= 2 * CONTEXT_LINES } private fun combineB(e: List, i: Int): Boolean { - return e[i].beginB - e[i - 1].endB <= 2 * context + return e[i].beginB - e[i - 1].endB <= 2 * CONTEXT_LINES } private fun end(edit: Edit, a: Int, b: Int): Boolean {