Gitnuro/src/main/kotlin/app/git/DiffManager.kt
2022-04-07 19:19:21 +02:00

133 lines
4.9 KiB
Kotlin

package app.git
import app.di.HunkDiffGeneratorFactory
import app.di.RawFileManagerFactory
import app.exceptions.MissingDiffEntryException
import app.extensions.fullData
import app.extensions.isMerging
import app.git.diff.DiffResult
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.diff.DiffEntry
import org.eclipse.jgit.diff.DiffFormatter
import org.eclipse.jgit.dircache.DirCacheIterator
import org.eclipse.jgit.lib.Repository
import org.eclipse.jgit.revwalk.RevCommit
import org.eclipse.jgit.revwalk.RevTree
import org.eclipse.jgit.revwalk.RevWalk
import org.eclipse.jgit.treewalk.AbstractTreeIterator
import org.eclipse.jgit.treewalk.CanonicalTreeParser
import org.eclipse.jgit.treewalk.EmptyTreeIterator
import org.eclipse.jgit.treewalk.FileTreeIterator
import org.eclipse.jgit.treewalk.filter.PathFilter
import java.io.ByteArrayOutputStream
import javax.inject.Inject
class DiffManager @Inject constructor(
private val rawFileManagerFactory: RawFileManagerFactory,
private val hunkDiffGeneratorFactory: HunkDiffGeneratorFactory,
private val branchesManager: BranchesManager,
private val repositoryManager: RepositoryManager,
) {
suspend fun diffFormat(git: Git, diffEntryType: DiffEntryType): DiffResult = withContext(Dispatchers.IO) {
val byteArrayOutputStream = ByteArrayOutputStream()
val repository = git.repository
val diffEntry: DiffEntry
DiffFormatter(byteArrayOutputStream).use { formatter ->
formatter.setRepository(repository)
val oldTree = DirCacheIterator(repository.readDirCache())
val newTree = FileTreeIterator(repository)
if (diffEntryType is DiffEntryType.UnstagedDiff)
formatter.scan(oldTree, newTree)
diffEntry = when (diffEntryType) {
is DiffEntryType.CommitDiff -> {
diffEntryType.diffEntry
}
is DiffEntryType.UncommitedDiff -> {
val statusEntry = diffEntryType.statusEntry
val cached = diffEntryType is DiffEntryType.StagedDiff
val firstDiffEntry = git.diff()
.setPathFilter(PathFilter.create(statusEntry.filePath))
.setCached(cached).apply {
val repositoryState = repositoryManager.getRepositoryState(git)
if (
branchesManager.currentBranchRef(git) == null &&
!repositoryState.isMerging &&
!repositoryState.isRebasing &&
cached
) {
setOldTree(EmptyTreeIterator()) // Required if the repository is empty
}
}
.call()
.firstOrNull()
?: throw MissingDiffEntryException("Diff entry not found")
firstDiffEntry
}
}
formatter.format(diffEntry)
formatter.flush()
}
val rawFileManager = rawFileManagerFactory.create(repository)
val hunkDiffGenerator = hunkDiffGeneratorFactory.create(repository, rawFileManager)
var diffResult: DiffResult
hunkDiffGenerator.use {
if (diffEntryType is DiffEntryType.UnstagedDiff) {
val oldTree = DirCacheIterator(repository.readDirCache())
val newTree = FileTreeIterator(repository)
hunkDiffGenerator.scan(oldTree, newTree)
}
diffResult = hunkDiffGenerator.format(diffEntry)
}
return@withContext diffResult
}
suspend fun commitDiffEntries(git: Git, commit: RevCommit): List<DiffEntry> = withContext(Dispatchers.IO) {
val fullCommit = commit.fullData(git.repository) ?: return@withContext emptyList()
val repository = git.repository
val parent = if (fullCommit.parentCount == 0) {
null
} else
fullCommit.parents.first().fullData(git.repository)
val oldTreeParser = if (parent != null)
prepareTreeParser(repository, parent)
else {
CanonicalTreeParser()
}
val newTreeParser = prepareTreeParser(repository, fullCommit)
return@withContext git.diff()
.setNewTree(newTreeParser)
.setOldTree(oldTreeParser)
.call()
}
}
fun prepareTreeParser(repository: Repository, commit: RevCommit): AbstractTreeIterator {
// from the commit we can build the tree which allows us to construct the TreeParser
RevWalk(repository).use { walk ->
val tree: RevTree = walk.parseTree(commit.tree.id)
val treeParser = CanonicalTreeParser()
repository.newObjectReader().use { reader -> treeParser.reset(reader, tree.id) }
return treeParser
}
}