Cleanup and changed current branch priority if there are uncommited changes
This commit is contained in:
parent
1476295415
commit
a7d40638ab
12 changed files with 96 additions and 835 deletions
|
@ -1,15 +1,10 @@
|
||||||
package com.jetpackduba.gitnuro.git.graph
|
package com.jetpackduba.gitnuro.git.graph
|
||||||
|
|
||||||
import com.jetpackduba.gitnuro.extensions.isTag
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.lib.Constants
|
|
||||||
import org.eclipse.jgit.lib.Ref
|
import org.eclipse.jgit.lib.Ref
|
||||||
import org.eclipse.jgit.lib.Repository
|
|
||||||
import org.eclipse.jgit.revwalk.RevCommit
|
import org.eclipse.jgit.revwalk.RevCommit
|
||||||
import org.eclipse.jgit.revwalk.RevWalk
|
|
||||||
import org.eclipse.jgit.revwalk.filter.RevFilter
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,9 +14,12 @@ class GenerateLogWalkUseCase @Inject constructor() {
|
||||||
firstCommit: RevCommit,
|
firstCommit: RevCommit,
|
||||||
allRefs: List<Ref>,
|
allRefs: List<Ref>,
|
||||||
stashes: List<RevCommit>,
|
stashes: List<RevCommit>,
|
||||||
|
hasUncommittedChanges: Boolean,
|
||||||
|
commitsLimit: Int?,
|
||||||
): GraphCommitList2 = withContext(Dispatchers.IO) {
|
): GraphCommitList2 = withContext(Dispatchers.IO) {
|
||||||
val reservedLanes = mutableMapOf<Int, String>()
|
val reservedLanes = mutableMapOf<Int, String>()
|
||||||
val graphNodes = mutableListOf<GraphNode2>()
|
val graphNodes = mutableListOf<GraphNode2>()
|
||||||
|
var maxLane = 0
|
||||||
|
|
||||||
val availableCommitsToAdd = mutableMapOf<String, RevCommit>()
|
val availableCommitsToAdd = mutableMapOf<String, RevCommit>()
|
||||||
|
|
||||||
|
@ -42,21 +40,21 @@ class GenerateLogWalkUseCase @Inject constructor() {
|
||||||
availableCommitsToAdd.putAll(commitsOfStashes)
|
availableCommitsToAdd.putAll(commitsOfStashes)
|
||||||
|
|
||||||
var currentCommit = getNextCommit(availableCommitsToAdd.values.toList())
|
var currentCommit = getNextCommit(availableCommitsToAdd.values.toList())
|
||||||
|
|
||||||
|
if (hasUncommittedChanges) {
|
||||||
|
reservedLanes[0] = firstCommit.name
|
||||||
|
}
|
||||||
|
|
||||||
availableCommitsToAdd.remove(currentCommit?.name)
|
availableCommitsToAdd.remove(currentCommit?.name)
|
||||||
|
|
||||||
while (currentCommit != null) {
|
while (currentCommit != null && (commitsLimit == null || graphNodes.count() <= commitsLimit)) {
|
||||||
val lanes = getReservedLanes(reservedLanes, currentCommit.name)
|
val lanes = getReservedLanes(reservedLanes, currentCommit.name)
|
||||||
val lane = lanes.first()
|
val lane = lanes.first()
|
||||||
val forkingLanes = lanes - lane
|
val forkingLanes = lanes - lane
|
||||||
val isStash = stashes.any { it == currentCommit }
|
val isStash = stashes.any { it == currentCommit }
|
||||||
|
|
||||||
val parents = sortParentsByPriority(git, currentCommit).run {
|
val parents = sortParentsByPriority(git, currentCommit)
|
||||||
if(isStash) {
|
.filterStashParentsIfRequired(isStash)
|
||||||
filterNot { it.shortMessage.startsWith("index on") }
|
|
||||||
} else {
|
|
||||||
this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val parentsCount = parents.count()
|
val parentsCount = parents.count()
|
||||||
val mergingLanes = mutableListOf<Int>()
|
val mergingLanes = mutableListOf<Int>()
|
||||||
|
@ -72,24 +70,22 @@ class GenerateLogWalkUseCase @Inject constructor() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val currentCommitName = currentCommit.name
|
|
||||||
val refs = refsByCommit(refsWithCommits, currentCommit)
|
val refs = refsByCommit(refsWithCommits, currentCommit)
|
||||||
|
|
||||||
val graphNode = GraphNode2(
|
val graphNode = createGraphNode(
|
||||||
currentCommitName,
|
currentCommit = currentCommit,
|
||||||
currentCommit.shortMessage,
|
|
||||||
currentCommit.fullMessage,
|
|
||||||
currentCommit.authorIdent,
|
|
||||||
currentCommit.committerIdent,
|
|
||||||
currentCommit.parentCount,
|
|
||||||
isStash = isStash,
|
isStash = isStash,
|
||||||
lane = lane,
|
lane = lane,
|
||||||
forkingLanes = forkingLanes,
|
forkingLanes = forkingLanes,
|
||||||
passingLanes = reservedLanes.keys.toList() - mergingLanes.toSet() - forkingLanes.toSet(),
|
reservedLanes = reservedLanes,
|
||||||
mergingLanes = mergingLanes,
|
mergingLanes = mergingLanes,
|
||||||
refs = refs,
|
refs = refs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (lane > maxLane) {
|
||||||
|
maxLane = lane
|
||||||
|
}
|
||||||
|
|
||||||
graphNodes.add(graphNode)
|
graphNodes.add(graphNode)
|
||||||
removeFromAllLanes(reservedLanes, graphNode.name)
|
removeFromAllLanes(reservedLanes, graphNode.name)
|
||||||
|
|
||||||
|
@ -99,9 +95,32 @@ class GenerateLogWalkUseCase @Inject constructor() {
|
||||||
availableCommitsToAdd.remove(currentCommit?.name)
|
availableCommitsToAdd.remove(currentCommit?.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphCommitList2(graphNodes)
|
GraphCommitList2(graphNodes, maxLane)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createGraphNode(
|
||||||
|
currentCommit: RevCommit,
|
||||||
|
isStash: Boolean,
|
||||||
|
lane: Int,
|
||||||
|
forkingLanes: List<Int>,
|
||||||
|
reservedLanes: MutableMap<Int, String>,
|
||||||
|
mergingLanes: MutableList<Int>,
|
||||||
|
refs: List<Ref>
|
||||||
|
) = GraphNode2(
|
||||||
|
currentCommit.name,
|
||||||
|
currentCommit.shortMessage,
|
||||||
|
currentCommit.fullMessage,
|
||||||
|
currentCommit.authorIdent,
|
||||||
|
currentCommit.committerIdent,
|
||||||
|
currentCommit.parentCount,
|
||||||
|
isStash = isStash,
|
||||||
|
lane = lane,
|
||||||
|
forkingLanes = forkingLanes,
|
||||||
|
passingLanes = reservedLanes.keys.toList() - mergingLanes.toSet() - forkingLanes.toSet(),
|
||||||
|
mergingLanes = mergingLanes,
|
||||||
|
refs = refs,
|
||||||
|
)
|
||||||
|
|
||||||
private fun refsByCommit(
|
private fun refsByCommit(
|
||||||
refsWithCommits: List<Pair<RevCommit, Ref>>,
|
refsWithCommits: List<Pair<RevCommit, Ref>>,
|
||||||
commit: RevCommit
|
commit: RevCommit
|
||||||
|
@ -128,47 +147,18 @@ class GenerateLogWalkUseCase @Inject constructor() {
|
||||||
} else {
|
} else {
|
||||||
parents.sortedBy { it.committerIdent.`when` }
|
parents.sortedBy { it.committerIdent.`when` }
|
||||||
}
|
}
|
||||||
|
|
||||||
// parents.sortedWith { o1, o2 ->
|
|
||||||
// TODO("Not yet implemented")
|
|
||||||
// }
|
|
||||||
// val parentsWithPriority = mapOf<RevCommit, Int>()
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// if (currentCommit.name.startsWith("Merge")) {
|
|
||||||
// val originMergeParent = parents.firstOrNull {
|
|
||||||
// currentCommit.fullMessage.contains(it.shortMessage)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (originMergeParent != null) {
|
|
||||||
// (parents - currentCommit) + currentCommit // this will remove the currentItem and re-add it to the end
|
|
||||||
// } else
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun List<RevCommit>.filterStashParentsIfRequired(isStash: Boolean): List<RevCommit> {
|
||||||
fun t(commit1: RevCommit, commit2: RevCommit, repository: Repository): RevCommit? {
|
return if (isStash) {
|
||||||
return RevWalk(repository).use { walk ->
|
filterNot {
|
||||||
walk.setRevFilter(RevFilter.MERGE_BASE)
|
it.shortMessage.startsWith("index on") ||
|
||||||
walk.markStart(commit1)
|
it.shortMessage.startsWith("untracked files on")
|
||||||
walk.markStart(commit2)
|
}
|
||||||
|
} else {
|
||||||
walk.next()
|
this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// fun getShortestParent(parent1: RevCommit, parent2: RevCommit) {
|
|
||||||
// val logParent1 = mutableListOf<String>(parent1.name)
|
|
||||||
// val logParent2 = mutableListOf<String>(parent2.name)
|
|
||||||
//
|
|
||||||
// var newParent1: RevCommit? = parent1
|
|
||||||
// var newParent2: RevCommit? = parent2
|
|
||||||
//
|
|
||||||
// while (newParent1 != null && newParent2 != null) {
|
|
||||||
// newParent1.pa
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
fun getNextCommit(availableCommits: List<RevCommit>): RevCommit? {
|
fun getNextCommit(availableCommits: List<RevCommit>): RevCommit? {
|
||||||
return availableCommits.sortedByDescending { it.committerIdent.`when` }.firstOrNull()
|
return availableCommits.sortedByDescending { it.committerIdent.`when` }.firstOrNull()
|
||||||
|
@ -222,4 +212,11 @@ class GenerateLogWalkUseCase @Inject constructor() {
|
||||||
.firstOrNull() ?: (sortedKeys.max() + 1)
|
.firstOrNull() ?: (sortedKeys.max() + 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed interface ReservationType {
|
||||||
|
val hash: String
|
||||||
|
|
||||||
|
class ParentInSameLane(override val hash: String): ReservationType
|
||||||
|
class ParentInVariableLane(override val hash: String): ReservationType
|
||||||
}
|
}
|
|
@ -1,366 +0,0 @@
|
||||||
package com.jetpackduba.gitnuro.git.graph
|
|
||||||
|
|
||||||
import org.eclipse.jgit.internal.JGitText
|
|
||||||
import org.eclipse.jgit.lib.AnyObjectId
|
|
||||||
import org.eclipse.jgit.revwalk.RevCommit
|
|
||||||
import org.eclipse.jgit.revwalk.RevCommitList
|
|
||||||
import org.eclipse.jgit.revwalk.RevWalk
|
|
||||||
import java.text.MessageFormat
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An ordered list of [GraphNode] subclasses.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Commits are allocated into lanes as they enter the list, based upon their
|
|
||||||
* connections between descendant (child) commits and ancestor (parent) commits.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* The source of the list must be a [GraphWalk]
|
|
||||||
* and [.fillTo] must be used to populate the list.
|
|
||||||
*
|
|
||||||
* type of lane used by the application.
|
|
||||||
</L> */
|
|
||||||
class GraphCommitList : RevCommitList<GraphNode>() {
|
|
||||||
private var positionsAllocated = 0
|
|
||||||
private val freePositions = TreeSet<Int>()
|
|
||||||
private val activeLanes = HashSet<GraphLane>(32)
|
|
||||||
|
|
||||||
var maxLine = 0
|
|
||||||
private set
|
|
||||||
|
|
||||||
/** number of (child) commits on a lane */
|
|
||||||
private val laneLength = HashMap<GraphLane, Int?>(
|
|
||||||
32
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun clear() {
|
|
||||||
super.clear()
|
|
||||||
positionsAllocated = 0
|
|
||||||
freePositions.clear()
|
|
||||||
activeLanes.clear()
|
|
||||||
laneLength.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun source(revWalk: RevWalk) {
|
|
||||||
if (revWalk !is GraphWalk) throw ClassCastException(
|
|
||||||
MessageFormat.format(
|
|
||||||
JGitText.get().classCastNotA,
|
|
||||||
GraphWalk::class.java.name
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
super.source(revWalk)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var parentId: AnyObjectId? = null
|
|
||||||
|
|
||||||
private val graphCommit = UncommittedChangesGraphNode()
|
|
||||||
|
|
||||||
fun addUncommittedChangesGraphCommit(parent: RevCommit) {
|
|
||||||
parentId = parent.id
|
|
||||||
graphCommit.lane = nextFreeLane()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun enter(index: Int, currCommit: GraphNode) {
|
|
||||||
var isUncommittedChangesNodeParent = false
|
|
||||||
if (currCommit.id == parentId) {
|
|
||||||
graphCommit.graphParent = currCommit
|
|
||||||
currCommit.addChild(graphCommit, addFirst = true)
|
|
||||||
isUncommittedChangesNodeParent = true
|
|
||||||
}
|
|
||||||
|
|
||||||
setupChildren(currCommit)
|
|
||||||
val nChildren = currCommit.childCount
|
|
||||||
if (nChildren == 0) {
|
|
||||||
currCommit.lane = nextFreeLane()
|
|
||||||
} else if (nChildren == 1
|
|
||||||
&& currCommit.children[0].graphParentCount < 2
|
|
||||||
) {
|
|
||||||
// Only one child, child has only us as their parent.
|
|
||||||
// Stay in the same lane as the child.
|
|
||||||
val graphNode: GraphNode = currCommit.children[0]
|
|
||||||
currCommit.lane = graphNode.lane
|
|
||||||
var len = laneLength[currCommit.lane]
|
|
||||||
len = if (len != null) Integer.valueOf(len.toInt() + 1) else Integer.valueOf(0)
|
|
||||||
|
|
||||||
if (currCommit.lane.position != INVALID_LANE_POSITION)
|
|
||||||
laneLength[currCommit.lane] = len
|
|
||||||
} else {
|
|
||||||
// We look for the child lane the current commit should continue.
|
|
||||||
// Candidate lanes for this are those with children, that have the
|
|
||||||
// current commit as their first parent.
|
|
||||||
// There can be multiple candidate lanes. In that case the longest
|
|
||||||
// lane is chosen, as this is usually the lane representing the
|
|
||||||
// branch the commit actually was made on.
|
|
||||||
|
|
||||||
// When there are no candidate lanes (i.e. the current commit has
|
|
||||||
// only children whose non-first parent it is) we place the current
|
|
||||||
// commit on a new lane.
|
|
||||||
|
|
||||||
// The lane the current commit will be placed on:
|
|
||||||
var reservedLane: GraphLane? = null
|
|
||||||
var childOnReservedLane: GraphNode? = null
|
|
||||||
var lengthOfReservedLane = -1
|
|
||||||
|
|
||||||
|
|
||||||
if (isUncommittedChangesNodeParent) {
|
|
||||||
val length = laneLength[graphCommit.lane]
|
|
||||||
if (length != null) {
|
|
||||||
reservedLane = graphCommit.lane
|
|
||||||
childOnReservedLane = graphCommit
|
|
||||||
lengthOfReservedLane = length
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val children = currCommit.children.sortedBy { it.lane.position }
|
|
||||||
for (i in 0 until nChildren) {
|
|
||||||
val c: GraphNode = children[i]
|
|
||||||
if (c.getGraphParent(0) === currCommit) {
|
|
||||||
if (c.lane.position < 0)
|
|
||||||
println("c.lane.position is invalid (${c.lane.position})")
|
|
||||||
|
|
||||||
val length = laneLength[c.lane]
|
|
||||||
|
|
||||||
// we may be the first parent for multiple lines of
|
|
||||||
// development, try to continue the longest one
|
|
||||||
if (length != null && length > lengthOfReservedLane) {
|
|
||||||
reservedLane = c.lane
|
|
||||||
childOnReservedLane = c
|
|
||||||
lengthOfReservedLane = length
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (reservedLane != null) {
|
|
||||||
currCommit.lane = reservedLane
|
|
||||||
laneLength[reservedLane] = Integer.valueOf(lengthOfReservedLane + 1)
|
|
||||||
handleBlockedLanes(index, currCommit, childOnReservedLane)
|
|
||||||
} else {
|
|
||||||
currCommit.lane = nextFreeLane()
|
|
||||||
handleBlockedLanes(index, currCommit, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
// close lanes of children, if there are no first parents that might
|
|
||||||
// want to continue the child lanes
|
|
||||||
for (i in 0 until nChildren) {
|
|
||||||
val graphNode = currCommit.children[i]
|
|
||||||
|
|
||||||
val firstParent = graphNode.getGraphParent(0)
|
|
||||||
|
|
||||||
if (firstParent.lane.position != INVALID_LANE_POSITION && firstParent.lane !== graphNode.lane)
|
|
||||||
closeLane(graphNode.lane)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
continueActiveLanes(currCommit)
|
|
||||||
|
|
||||||
if (currCommit.parentCount == 0 && currCommit.lane.position == INVALID_LANE_POSITION)
|
|
||||||
closeLane(currCommit.lane)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun continueActiveLanes(currCommit: GraphNode) {
|
|
||||||
for (lane in activeLanes) {
|
|
||||||
if (lane !== currCommit.lane) {
|
|
||||||
currCommit.addPassingLane(lane)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up fork and merge information in the involved PlotCommits.
|
|
||||||
* Recognizes and handles blockades that involve forking or merging arcs.
|
|
||||||
*
|
|
||||||
* @param index
|
|
||||||
* the index of `currCommit` in the list
|
|
||||||
* @param currentNode
|
|
||||||
* @param childOnLane
|
|
||||||
* the direct child on the same lane as `currCommit`,
|
|
||||||
* may be null if `currCommit` is the first commit on
|
|
||||||
* the lane
|
|
||||||
*/
|
|
||||||
private fun handleBlockedLanes(
|
|
||||||
index: Int, currentNode: GraphNode,
|
|
||||||
childOnLane: GraphNode?
|
|
||||||
) {
|
|
||||||
for (child in currentNode.children) {
|
|
||||||
if (child === childOnLane) continue // simple continuations of lanes are handled by
|
|
||||||
// continueActiveLanes() calls in enter()
|
|
||||||
|
|
||||||
// Is the child a merge or is it forking off?
|
|
||||||
val childIsMerge = child.getGraphParent(0) !== currentNode
|
|
||||||
if (childIsMerge) {
|
|
||||||
var laneToUse = currentNode.lane
|
|
||||||
laneToUse = handleMerge(
|
|
||||||
index, currentNode, childOnLane, child,
|
|
||||||
laneToUse
|
|
||||||
)
|
|
||||||
child.addMergingLane(laneToUse)
|
|
||||||
} else {
|
|
||||||
// We want to draw a forking arc in the child's lane.
|
|
||||||
// As an active lane, the child lane already continues
|
|
||||||
// (unblocked) up to this commit, we only need to mark it as
|
|
||||||
// forking off from the current commit.
|
|
||||||
val laneToUse = child.lane
|
|
||||||
currentNode.addForkingOffLane(laneToUse)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handles the case where currCommit is a non-first parent of the child
|
|
||||||
private fun handleMerge(
|
|
||||||
index: Int, currCommit: GraphNode,
|
|
||||||
childOnLane: GraphNode?, child: GraphNode, laneToUse: GraphLane
|
|
||||||
): GraphLane {
|
|
||||||
|
|
||||||
// find all blocked positions between currCommit and this child
|
|
||||||
var newLaneToUse = laneToUse
|
|
||||||
var childIndex = index // useless initialization, should
|
|
||||||
// always be set in the loop below
|
|
||||||
val blockedPositions = BitSet()
|
|
||||||
for (r in index - 1 downTo 0) {
|
|
||||||
val graphNode: GraphNode? = get(r)
|
|
||||||
if (graphNode === child) {
|
|
||||||
childIndex = r
|
|
||||||
break
|
|
||||||
}
|
|
||||||
addBlockedPosition(blockedPositions, graphNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle blockades
|
|
||||||
if (blockedPositions[newLaneToUse.position]) {
|
|
||||||
// We want to draw a merging arc in our lane to the child,
|
|
||||||
// which is on another lane, but our lane is blocked.
|
|
||||||
|
|
||||||
// Check if childOnLane is beetween commit and the child we
|
|
||||||
// are currently processing
|
|
||||||
var needDetour = false
|
|
||||||
if (childOnLane != null) {
|
|
||||||
for (r in index - 1 downTo childIndex + 1) {
|
|
||||||
val graphNode: GraphNode? = get(r)
|
|
||||||
if (graphNode === childOnLane) {
|
|
||||||
needDetour = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (needDetour) {
|
|
||||||
// It is childOnLane which is blocking us. Repositioning
|
|
||||||
// our lane would not help, because this repositions the
|
|
||||||
// child too, keeping the blockade.
|
|
||||||
// Instead, we create a "detour lane" which gets us
|
|
||||||
// around the blockade. That lane has no commits on it.
|
|
||||||
newLaneToUse = nextFreeLane(blockedPositions)
|
|
||||||
currCommit.addForkingOffLane(newLaneToUse)
|
|
||||||
closeLane(newLaneToUse)
|
|
||||||
} else {
|
|
||||||
// The blockade is (only) due to other (already closed)
|
|
||||||
// lanes at the current lane's position. In this case we
|
|
||||||
// reposition the current lane.
|
|
||||||
// We are the first commit on this lane, because
|
|
||||||
// otherwise the child commit on this lane would have
|
|
||||||
// kept other lanes from blocking us. Since we are the
|
|
||||||
// first commit, we can freely reposition.
|
|
||||||
val newPos = getFreePosition(blockedPositions)
|
|
||||||
freePositions.add(newLaneToUse.position)
|
|
||||||
|
|
||||||
newLaneToUse.position = newPos
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actually connect currCommit to the merge child
|
|
||||||
drawLaneToChild(index, child, newLaneToUse)
|
|
||||||
return newLaneToUse
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connects the commit at commitIndex to the child, using the given lane.
|
|
||||||
* All blockades on the lane must be resolved before calling this method.
|
|
||||||
*
|
|
||||||
* @param commitIndex
|
|
||||||
* @param child
|
|
||||||
* @param laneToContinue
|
|
||||||
*/
|
|
||||||
private fun drawLaneToChild(
|
|
||||||
commitIndex: Int, child: GraphNode,
|
|
||||||
laneToContinue: GraphLane
|
|
||||||
) {
|
|
||||||
for (index in commitIndex - 1 downTo 0) {
|
|
||||||
val graphNode: GraphNode? = get(index)
|
|
||||||
if (graphNode === child) break
|
|
||||||
graphNode?.addPassingLane(laneToContinue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun closeLane(lane: GraphLane) {
|
|
||||||
if (activeLanes.remove(lane)) {
|
|
||||||
laneLength.remove(lane)
|
|
||||||
freePositions.add(Integer.valueOf(lane.position))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupChildren(currCommit: GraphNode) {
|
|
||||||
val nParents = currCommit.parentCount
|
|
||||||
for (i in 0 until nParents) (currCommit.getParent(i) as GraphNode).addChild(currCommit)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun nextFreeLane(blockedPositions: BitSet? = null): GraphLane {
|
|
||||||
val newPlotLane = GraphLane(position = getFreePosition(blockedPositions))
|
|
||||||
activeLanes.add(newPlotLane)
|
|
||||||
laneLength[newPlotLane] = Integer.valueOf(1)
|
|
||||||
|
|
||||||
return newPlotLane
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param blockedPositions
|
|
||||||
* may be null
|
|
||||||
* @return a free lane position
|
|
||||||
*/
|
|
||||||
private fun getFreePosition(blockedPositions: BitSet?): Int {
|
|
||||||
if (freePositions.isEmpty()) return positionsAllocated++
|
|
||||||
if (blockedPositions != null) {
|
|
||||||
for (pos in freePositions) if (!blockedPositions[pos]) {
|
|
||||||
freePositions.remove(pos)
|
|
||||||
return pos
|
|
||||||
}
|
|
||||||
return positionsAllocated++
|
|
||||||
}
|
|
||||||
val min = freePositions.first()
|
|
||||||
freePositions.remove(min)
|
|
||||||
return min.toInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addBlockedPosition(
|
|
||||||
blockedPositions: BitSet,
|
|
||||||
graphNode: GraphNode?
|
|
||||||
) {
|
|
||||||
if (graphNode != null) {
|
|
||||||
val lane = graphNode.lane
|
|
||||||
|
|
||||||
// Positions may be blocked by a commit on a lane.
|
|
||||||
if (lane.position != INVALID_LANE_POSITION) {
|
|
||||||
blockedPositions.set(lane.position)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Positions may also be blocked by forking off and merging lanes.
|
|
||||||
// We don't consider passing lanes, because every passing lane forks
|
|
||||||
// off and merges at it ends.
|
|
||||||
for (graphLane in graphNode.forkingOffLanes) {
|
|
||||||
blockedPositions.set(graphLane.position)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (graphLane in graphNode.mergingLanes) {
|
|
||||||
blockedPositions.set(graphLane.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun calcMaxLine() {
|
|
||||||
if (this.isNotEmpty()) {
|
|
||||||
maxLine = this.maxOf { it.lane.position }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +1,6 @@
|
||||||
package com.jetpackduba.gitnuro.git.graph
|
package com.jetpackduba.gitnuro.git.graph
|
||||||
|
|
||||||
class GraphCommitList2(val nodes: List<GraphNode2>) : List<GraphNode2> by nodes {
|
data class GraphCommitList2(
|
||||||
var maxLine = 0
|
val nodes: List<GraphNode2>,
|
||||||
private set
|
val maxLane: Int
|
||||||
|
) : List<GraphNode2> by nodes
|
||||||
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
package com.jetpackduba.gitnuro.git.graph
|
|
||||||
|
|
||||||
const val INVALID_LANE_POSITION = -1
|
|
||||||
|
|
||||||
class GraphLane(var position: Int = 0)
|
|
|
@ -1,105 +0,0 @@
|
||||||
package com.jetpackduba.gitnuro.git.graph
|
|
||||||
|
|
||||||
import org.eclipse.jgit.lib.AnyObjectId
|
|
||||||
import org.eclipse.jgit.lib.Ref
|
|
||||||
import org.eclipse.jgit.revwalk.RevCommit
|
|
||||||
|
|
||||||
private val NO_CHILDREN = arrayOf<GraphNode>()
|
|
||||||
private val NO_LANES = arrayOf<GraphLane>()
|
|
||||||
private val NO_LANE = GraphLane(INVALID_LANE_POSITION)
|
|
||||||
val NO_REFS = listOf<Ref>()
|
|
||||||
|
|
||||||
open class GraphNode(id: AnyObjectId?) : RevCommit(id), IGraphNode {
|
|
||||||
var forkingOffLanes: Array<GraphLane> = NO_LANES
|
|
||||||
var passingLanes: Array<GraphLane> = NO_LANES
|
|
||||||
var mergingLanes: Array<GraphLane> = NO_LANES
|
|
||||||
var lane: GraphLane = NO_LANE
|
|
||||||
var children: Array<GraphNode> = NO_CHILDREN
|
|
||||||
var refs: List<Ref> = NO_REFS
|
|
||||||
var isStash: Boolean = false
|
|
||||||
|
|
||||||
fun addForkingOffLane(graphLane: GraphLane) {
|
|
||||||
forkingOffLanes = addLane(graphLane, forkingOffLanes)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addPassingLane(graphLane: GraphLane) {
|
|
||||||
passingLanes = addLane(graphLane, passingLanes)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addMergingLane(graphLane: GraphLane) {
|
|
||||||
mergingLanes = addLane(graphLane, mergingLanes)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addChild(c: GraphNode, addFirst: Boolean = false) {
|
|
||||||
when (val childrenCount = children.count()) {
|
|
||||||
0 -> children = arrayOf(c)
|
|
||||||
1 -> {
|
|
||||||
if (!c.id.equals(children[0].id)) {
|
|
||||||
children = if (addFirst) {
|
|
||||||
arrayOf(c, children[0])
|
|
||||||
} else
|
|
||||||
arrayOf(children[0], c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
for (pc in children)
|
|
||||||
if (c.id.equals(pc.id))
|
|
||||||
return
|
|
||||||
|
|
||||||
val resultArray = if (addFirst) {
|
|
||||||
val childList = mutableListOf(c)
|
|
||||||
childList.addAll(children)
|
|
||||||
childList.toTypedArray()
|
|
||||||
} else {
|
|
||||||
children.copyOf(childrenCount + 1).run {
|
|
||||||
this[childrenCount] = c
|
|
||||||
requireNoNulls()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
children = resultArray
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val childCount: Int
|
|
||||||
get() {
|
|
||||||
return children.size
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun reset() {
|
|
||||||
forkingOffLanes = NO_LANES
|
|
||||||
passingLanes = NO_LANES
|
|
||||||
mergingLanes = NO_LANES
|
|
||||||
children = NO_CHILDREN
|
|
||||||
lane = NO_LANE
|
|
||||||
super.reset()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addLane(graphLane: GraphLane, lanes: Array<GraphLane>): Array<GraphLane> {
|
|
||||||
var newLines = lanes
|
|
||||||
|
|
||||||
when (val linesCount = newLines.count()) {
|
|
||||||
0 -> newLines = arrayOf(graphLane)
|
|
||||||
1 -> newLines = arrayOf(newLines[0], graphLane)
|
|
||||||
else -> {
|
|
||||||
val n = newLines.copyOf(linesCount + 1).run {
|
|
||||||
this[linesCount] = graphLane
|
|
||||||
requireNoNulls()
|
|
||||||
}
|
|
||||||
|
|
||||||
newLines = n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return newLines
|
|
||||||
}
|
|
||||||
|
|
||||||
override val graphParentCount: Int
|
|
||||||
get() = parentCount
|
|
||||||
|
|
||||||
override fun getGraphParent(nth: Int): GraphNode {
|
|
||||||
return getParent(nth) as GraphNode
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,191 +0,0 @@
|
||||||
package com.jetpackduba.gitnuro.git.graph
|
|
||||||
|
|
||||||
import org.eclipse.jgit.errors.MissingObjectException
|
|
||||||
import org.eclipse.jgit.internal.JGitText
|
|
||||||
import org.eclipse.jgit.lib.AnyObjectId
|
|
||||||
import org.eclipse.jgit.lib.Constants
|
|
||||||
import org.eclipse.jgit.lib.Ref
|
|
||||||
import org.eclipse.jgit.lib.Repository
|
|
||||||
import org.eclipse.jgit.revwalk.*
|
|
||||||
import java.io.IOException
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specialized RevWalk to be used for load data in a structured way to be displayed in a graph of commits.
|
|
||||||
*/
|
|
||||||
class GraphWalk(private var repository: Repository?) : RevWalk(repository) {
|
|
||||||
private var additionalRefMap: MutableMap<AnyObjectId, Set<Ref>>? = HashMap()
|
|
||||||
private var reverseRefMap: MutableMap<AnyObjectId, Set<Ref>>? = null
|
|
||||||
|
|
||||||
init {
|
|
||||||
super.sort(RevSort.TOPO, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun dispose() {
|
|
||||||
super.dispose()
|
|
||||||
if (reverseRefMap != null) {
|
|
||||||
reverseRefMap?.clear()
|
|
||||||
reverseRefMap = null
|
|
||||||
}
|
|
||||||
if (additionalRefMap != null) {
|
|
||||||
additionalRefMap?.clear()
|
|
||||||
additionalRefMap = null
|
|
||||||
}
|
|
||||||
repository = null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun sort(revSort: RevSort, use: Boolean) {
|
|
||||||
require(!(revSort == RevSort.TOPO && !use)) {
|
|
||||||
JGitText.get().topologicalSortRequired
|
|
||||||
}
|
|
||||||
|
|
||||||
super.sort(revSort, use)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createCommit(id: AnyObjectId): RevCommit {
|
|
||||||
return GraphNode(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createCommitFromStash(id: AnyObjectId): RevCommit {
|
|
||||||
return GraphNode(id).apply {
|
|
||||||
isStash = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun next(): RevCommit? {
|
|
||||||
val c = super.next()
|
|
||||||
val graphNode = c as GraphNode?
|
|
||||||
|
|
||||||
if (graphNode != null) {
|
|
||||||
val refs = getRefs(graphNode)
|
|
||||||
|
|
||||||
graphNode.refs = refs
|
|
||||||
}
|
|
||||||
|
|
||||||
return graphNode
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getRefs(commitId: AnyObjectId): List<Ref> {
|
|
||||||
val repository = this.repository
|
|
||||||
var reverseRefMap = this.reverseRefMap
|
|
||||||
var additionalRefMap = this.additionalRefMap
|
|
||||||
if (reverseRefMap == null && repository != null && additionalRefMap != null) {
|
|
||||||
|
|
||||||
reverseRefMap = repository.allRefsByPeeledObjectId
|
|
||||||
this.reverseRefMap = reverseRefMap
|
|
||||||
|
|
||||||
for (entry in additionalRefMap.entries) {
|
|
||||||
val refsSet = reverseRefMap[entry.key]
|
|
||||||
var additional = entry.value.toMutableSet()
|
|
||||||
|
|
||||||
if (refsSet != null) {
|
|
||||||
if (additional.size == 1) {
|
|
||||||
// It's an unmodifiable singleton set...
|
|
||||||
additional = HashSet(additional)
|
|
||||||
}
|
|
||||||
additional.addAll(refsSet)
|
|
||||||
}
|
|
||||||
reverseRefMap[entry.key] = additional
|
|
||||||
}
|
|
||||||
|
|
||||||
additionalRefMap.clear()
|
|
||||||
additionalRefMap = null
|
|
||||||
|
|
||||||
this.additionalRefMap = additionalRefMap
|
|
||||||
}
|
|
||||||
|
|
||||||
requireNotNull(reverseRefMap) // This should never be null
|
|
||||||
|
|
||||||
val refsSet = reverseRefMap[commitId]
|
|
||||||
?: return NO_REFS
|
|
||||||
val tags = refsSet.toList()
|
|
||||||
|
|
||||||
tags.sortedWith(GraphRefComparator())
|
|
||||||
|
|
||||||
return tags
|
|
||||||
}
|
|
||||||
|
|
||||||
fun markStartAllRefs(prefix: String) {
|
|
||||||
repository?.let { repo ->
|
|
||||||
for (ref in repo.refDatabase.getRefsByPrefix(prefix)) {
|
|
||||||
if (ref.isSymbolic) continue
|
|
||||||
markStartRef(ref)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun markStartFromStashes(stashes: List<RevObject>) {
|
|
||||||
for (stash in stashes) {
|
|
||||||
markStartRevObject(createCommitFromStash(stash))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun markStartRef(ref: Ref) {
|
|
||||||
try {
|
|
||||||
val refTarget = parseAny(ref.leaf.objectId)
|
|
||||||
markStartRevObject(refTarget)
|
|
||||||
} catch (e: MissingObjectException) {
|
|
||||||
// Ignore missing Refs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun markStartRevObject(refTarget: RevObject) {
|
|
||||||
try {
|
|
||||||
when (refTarget) {
|
|
||||||
is RevCommit -> markStart(refTarget)
|
|
||||||
// RevTag case handles commits without branches but only tags.
|
|
||||||
is RevTag -> {
|
|
||||||
if (refTarget.`object` is RevCommit) {
|
|
||||||
val commit = lookupCommit(refTarget.`object`)
|
|
||||||
markStart(commit)
|
|
||||||
} else {
|
|
||||||
println("Tag ${refTarget.tagName} is pointing to ${refTarget.`object`::class.simpleName}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: MissingObjectException) {
|
|
||||||
// Ignore missing Refs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal inner class GraphRefComparator : Comparator<Ref> {
|
|
||||||
override fun compare(o1: Ref, o2: Ref): Int {
|
|
||||||
try {
|
|
||||||
val obj1 = parseAny(o1.objectId)
|
|
||||||
val obj2 = parseAny(o2.objectId)
|
|
||||||
val t1 = timeOf(obj1)
|
|
||||||
val t2 = timeOf(obj2)
|
|
||||||
if (t1 > t2) return -1
|
|
||||||
if (t1 < t2) return 1
|
|
||||||
} catch (e: IOException) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
var cmp = kind(o1) - kind(o2)
|
|
||||||
|
|
||||||
if (cmp == 0)
|
|
||||||
cmp = o1.name.compareTo(o2.name)
|
|
||||||
|
|
||||||
return cmp
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun timeOf(revObject: RevObject): Long {
|
|
||||||
if (revObject is RevCommit) return revObject.commitTime.toLong()
|
|
||||||
if (revObject is RevTag) {
|
|
||||||
try {
|
|
||||||
parseBody(revObject)
|
|
||||||
} catch (e: IOException) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
val who = revObject.taggerIdent
|
|
||||||
return who?.getWhen()?.time ?: 0
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun kind(r: Ref): Int {
|
|
||||||
if (r.name.startsWith(Constants.R_TAGS)) return 0
|
|
||||||
if (r.name.startsWith(Constants.R_HEADS)) return 1
|
|
||||||
return if (r.name.startsWith(Constants.R_REMOTES)) 2 else 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
package com.jetpackduba.gitnuro.git.graph
|
|
||||||
|
|
||||||
interface IGraphNode {
|
|
||||||
val graphParentCount: Int
|
|
||||||
fun getGraphParent(nth: Int): GraphNode
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package com.jetpackduba.gitnuro.git.graph
|
|
||||||
|
|
||||||
import org.eclipse.jgit.lib.ObjectId
|
|
||||||
|
|
||||||
class UncommittedChangesGraphNode : GraphNode(ObjectId(0, 0, 0, 0, 0)) {
|
|
||||||
|
|
||||||
var graphParent: GraphNode? = null
|
|
||||||
|
|
||||||
override val graphParentCount: Int
|
|
||||||
get() = 1 // Uncommitted changes can have a max of 1 parent commit
|
|
||||||
|
|
||||||
override fun getGraphParent(nth: Int): GraphNode {
|
|
||||||
return requireNotNull(graphParent)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,77 +2,37 @@ package com.jetpackduba.gitnuro.git.log
|
||||||
|
|
||||||
|
|
||||||
import com.jetpackduba.gitnuro.git.graph.GenerateLogWalkUseCase
|
import com.jetpackduba.gitnuro.git.graph.GenerateLogWalkUseCase
|
||||||
import com.jetpackduba.gitnuro.git.graph.GraphCommitList
|
import com.jetpackduba.gitnuro.git.graph.GraphCommitList2
|
||||||
import com.jetpackduba.gitnuro.git.graph.GraphWalk
|
|
||||||
import com.jetpackduba.gitnuro.git.stash.GetStashListUseCase
|
import com.jetpackduba.gitnuro.git.stash.GetStashListUseCase
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.ensureActive
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.lib.Constants
|
import org.eclipse.jgit.lib.Constants
|
||||||
import org.eclipse.jgit.lib.Ref
|
|
||||||
import org.eclipse.jgit.lib.Repository
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.math.log
|
|
||||||
|
|
||||||
class GetLogUseCase @Inject constructor(
|
class GetLogUseCase @Inject constructor(
|
||||||
private val getStashListUseCase: GetStashListUseCase,
|
private val getStashListUseCase: GetStashListUseCase,
|
||||||
private val generateLogWalkUseCase: GenerateLogWalkUseCase,
|
private val generateLogWalkUseCase: GenerateLogWalkUseCase,
|
||||||
) {
|
) {
|
||||||
private var graphWalkCached: GraphWalk? = null
|
suspend operator fun invoke(git: Git, hasUncommittedChanges: Boolean, commitsLimit: Int?) =
|
||||||
|
|
||||||
suspend operator fun invoke(git: Git, currentBranch: Ref?, hasUncommittedChanges: Boolean, commitsLimit: Int) =
|
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
val logList = git.log().setMaxCount(1).call().toList()
|
val logList = git.log().setMaxCount(1).call().toList()
|
||||||
val allRefs = git.repository.refDatabase.refs.filterNot { it.name.startsWith(Constants.R_STASH) } // remove stash as it only returns the latest, we get all afterward
|
val firstCommit = logList.firstOrNull()
|
||||||
|
val allRefs =
|
||||||
|
git.repository.refDatabase.refs.filterNot { it.name.startsWith(Constants.R_STASH) } // remove stash as it only returns the latest, we get all afterward
|
||||||
val stashes = getStashListUseCase(git)
|
val stashes = getStashListUseCase(git)
|
||||||
return@withContext generateLogWalkUseCase(git, logList.first(), allRefs, stashes)
|
|
||||||
// val commitList = GraphCommitList()
|
|
||||||
// val repositoryState = git.repository.repositoryState
|
|
||||||
//
|
|
||||||
// if (currentBranch != null || repositoryState.isRebasing) { // Current branch is null when there is no log (new repo) or rebasing
|
|
||||||
// val logList = git.log().setMaxCount(1).call().toList()
|
|
||||||
//
|
|
||||||
// val walk = GraphWalk(git.repository)
|
|
||||||
//
|
|
||||||
// walk.use {
|
|
||||||
// // Without this, during rebase conflicts the graph won't show the HEAD commits (new commits created
|
|
||||||
// // by the rebase)
|
|
||||||
// walk.markStart(walk.lookupCommit(logList.first()))
|
|
||||||
//
|
|
||||||
// walk.markStartAllRefs(Constants.R_HEADS)
|
|
||||||
// walk.markStartAllRefs(Constants.R_REMOTES)
|
|
||||||
// walk.markStartAllRefs(Constants.R_TAGS)
|
|
||||||
//// walk.markStartAllRefs(Constants.R_STASH)
|
|
||||||
//
|
|
||||||
|
|
||||||
//
|
return@withContext if (firstCommit == null) {
|
||||||
// if (hasUncommittedChanges)
|
GraphCommitList2(emptyList(), 0)
|
||||||
// commitList.addUncommittedChangesGraphCommit(logList.first())
|
} else {
|
||||||
//
|
generateLogWalkUseCase.invoke(
|
||||||
// commitList.source(walk)
|
git,
|
||||||
// commitList.fillTo(commitsLimit)
|
firstCommit,
|
||||||
// }
|
allRefs,
|
||||||
//
|
stashes,
|
||||||
// ensureActive()
|
hasUncommittedChanges,
|
||||||
//
|
commitsLimit
|
||||||
// }
|
)
|
||||||
//
|
}
|
||||||
// commitList.calcMaxLine()
|
|
||||||
//
|
|
||||||
// return@withContext commitList
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun cachedGraphWalk(repository: Repository): GraphWalk {
|
|
||||||
val graphWalkCached = this.graphWalkCached
|
|
||||||
|
|
||||||
return if (graphWalkCached != null) {
|
|
||||||
graphWalkCached
|
|
||||||
} else {
|
|
||||||
val newGraphWalk = GraphWalk(repository)
|
|
||||||
this.graphWalkCached = newGraphWalk
|
|
||||||
|
|
||||||
newGraphWalk
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -38,9 +38,7 @@ import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.jetpackduba.gitnuro.AppIcons
|
import com.jetpackduba.gitnuro.AppIcons
|
||||||
import com.jetpackduba.gitnuro.extensions.*
|
import com.jetpackduba.gitnuro.extensions.*
|
||||||
import com.jetpackduba.gitnuro.git.graph.GraphCommitList
|
|
||||||
import com.jetpackduba.gitnuro.git.graph.GraphCommitList2
|
import com.jetpackduba.gitnuro.git.graph.GraphCommitList2
|
||||||
import com.jetpackduba.gitnuro.git.graph.GraphNode
|
|
||||||
import com.jetpackduba.gitnuro.git.graph.GraphNode2
|
import com.jetpackduba.gitnuro.git.graph.GraphNode2
|
||||||
import com.jetpackduba.gitnuro.git.workspace.StatusSummary
|
import com.jetpackduba.gitnuro.git.workspace.StatusSummary
|
||||||
import com.jetpackduba.gitnuro.keybindings.KeybindingOption
|
import com.jetpackduba.gitnuro.keybindings.KeybindingOption
|
||||||
|
@ -147,18 +145,18 @@ private fun LogLoaded(
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
// LaunchedEffect(verticalScrollState, commitList) {
|
LaunchedEffect(verticalScrollState, commitList) {
|
||||||
// launch {
|
launch {
|
||||||
// logViewModel.focusCommit.collect { commit ->
|
logViewModel.focusCommit.collect { commit ->
|
||||||
// scrollToCommit(verticalScrollState, commitList, commit)
|
scrollToCommit(verticalScrollState, commitList, commit)
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// launch {
|
launch {
|
||||||
// logViewModel.scrollToUncommittedChanges.collect {
|
logViewModel.scrollToUncommittedChanges.collect {
|
||||||
// scrollToUncommittedChanges(verticalScrollState, commitList)
|
scrollToUncommittedChanges(verticalScrollState, commitList)
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
LogDialogs(
|
LogDialogs(
|
||||||
logViewModel,
|
logViewModel,
|
||||||
|
@ -177,7 +175,7 @@ private fun LogLoaded(
|
||||||
if (graphWidth.value < CANVAS_MIN_WIDTH) graphWidth = CANVAS_MIN_WIDTH.dp
|
if (graphWidth.value < CANVAS_MIN_WIDTH) graphWidth = CANVAS_MIN_WIDTH.dp
|
||||||
|
|
||||||
val maxLinePosition = if (commitList.isNotEmpty())
|
val maxLinePosition = if (commitList.isNotEmpty())
|
||||||
commitList.maxLine
|
commitList.maxLane
|
||||||
else
|
else
|
||||||
MIN_GRAPH_LANES
|
MIN_GRAPH_LANES
|
||||||
|
|
||||||
|
@ -309,7 +307,7 @@ private fun LogLoaded(
|
||||||
|
|
||||||
suspend fun scrollToCommit(
|
suspend fun scrollToCommit(
|
||||||
verticalScrollState: LazyListState,
|
verticalScrollState: LazyListState,
|
||||||
commitList: GraphCommitList,
|
commitList: GraphCommitList2,
|
||||||
commit: RevCommit?,
|
commit: RevCommit?,
|
||||||
) {
|
) {
|
||||||
val index = commitList.indexOfFirst { it.name == commit?.name }
|
val index = commitList.indexOfFirst { it.name == commit?.name }
|
||||||
|
@ -321,7 +319,7 @@ suspend fun scrollToCommit(
|
||||||
|
|
||||||
suspend fun scrollToUncommittedChanges(
|
suspend fun scrollToUncommittedChanges(
|
||||||
verticalScrollState: LazyListState,
|
verticalScrollState: LazyListState,
|
||||||
commitList: GraphCommitList,
|
commitList: GraphCommitList2,
|
||||||
) {
|
) {
|
||||||
if (commitList.isNotEmpty())
|
if (commitList.isNotEmpty())
|
||||||
verticalScrollState.scrollToItem(0)
|
verticalScrollState.scrollToItem(0)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package com.jetpackduba.gitnuro.ui.log
|
package com.jetpackduba.gitnuro.ui.log
|
||||||
|
|
||||||
import com.jetpackduba.gitnuro.git.graph.GraphNode
|
|
||||||
import com.jetpackduba.gitnuro.git.graph.GraphNode2
|
import com.jetpackduba.gitnuro.git.graph.GraphNode2
|
||||||
import org.eclipse.jgit.lib.Ref
|
import org.eclipse.jgit.lib.Ref
|
||||||
|
|
||||||
|
|
|
@ -3,15 +3,12 @@ package com.jetpackduba.gitnuro.viewmodels
|
||||||
import androidx.compose.foundation.ScrollState
|
import androidx.compose.foundation.ScrollState
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import com.jetpackduba.gitnuro.extensions.delayedStateChange
|
import com.jetpackduba.gitnuro.extensions.delayedStateChange
|
||||||
import com.jetpackduba.gitnuro.extensions.shortName
|
|
||||||
import com.jetpackduba.gitnuro.extensions.simpleName
|
import com.jetpackduba.gitnuro.extensions.simpleName
|
||||||
import com.jetpackduba.gitnuro.git.RefreshType
|
import com.jetpackduba.gitnuro.git.RefreshType
|
||||||
import com.jetpackduba.gitnuro.git.TabState
|
import com.jetpackduba.gitnuro.git.TabState
|
||||||
import com.jetpackduba.gitnuro.git.TaskEvent
|
import com.jetpackduba.gitnuro.git.TaskEvent
|
||||||
import com.jetpackduba.gitnuro.git.branches.*
|
import com.jetpackduba.gitnuro.git.branches.*
|
||||||
import com.jetpackduba.gitnuro.git.graph.GraphCommitList
|
|
||||||
import com.jetpackduba.gitnuro.git.graph.GraphCommitList2
|
import com.jetpackduba.gitnuro.git.graph.GraphCommitList2
|
||||||
import com.jetpackduba.gitnuro.git.graph.GraphNode
|
|
||||||
import com.jetpackduba.gitnuro.git.graph.GraphNode2
|
import com.jetpackduba.gitnuro.git.graph.GraphNode2
|
||||||
import com.jetpackduba.gitnuro.git.log.*
|
import com.jetpackduba.gitnuro.git.log.*
|
||||||
import com.jetpackduba.gitnuro.git.rebase.RebaseBranchUseCase
|
import com.jetpackduba.gitnuro.git.rebase.RebaseBranchUseCase
|
||||||
|
@ -150,14 +147,14 @@ class LogViewModel @Inject constructor(
|
||||||
val commitsLimit = if (appSettings.commitsLimitEnabled) {
|
val commitsLimit = if (appSettings.commitsLimitEnabled) {
|
||||||
appSettings.commitsLimit
|
appSettings.commitsLimit
|
||||||
} else
|
} else
|
||||||
Int.MAX_VALUE
|
null
|
||||||
|
|
||||||
val commitsLimitDisplayed = if (appSettings.commitsLimitEnabled) {
|
val commitsLimitDisplayed = if (appSettings.commitsLimitEnabled) {
|
||||||
appSettings.commitsLimit
|
appSettings.commitsLimit
|
||||||
} else
|
} else
|
||||||
-1
|
-1
|
||||||
|
|
||||||
val log = getLogUseCase(git, currentBranch, hasUncommittedChanges, commitsLimit)
|
val log = getLogUseCase(git, hasUncommittedChanges, commitsLimit)
|
||||||
|
|
||||||
_logStatus.value =
|
_logStatus.value =
|
||||||
LogStatus.Loaded(hasUncommittedChanges, log, currentBranch, statusSummary, commitsLimitDisplayed)
|
LogStatus.Loaded(hasUncommittedChanges, log, currentBranch, statusSummary, commitsLimitDisplayed)
|
||||||
|
|
Loading…
Reference in a new issue