Skip to content

Commit 61088c4

Browse files
committed
Added stashes visualitzation in the graph
Fixes #134
1 parent b0ca80b commit 61088c4

File tree

5 files changed

+103
-76
lines changed

5 files changed

+103
-76
lines changed

src/main/kotlin/com/jetpackduba/gitnuro/git/graph/GraphCommitList.kt

Lines changed: 63 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
package com.jetpackduba.gitnuro.git.graph
22

3-
import org.eclipse.jgit.internal.JGitText
43
import org.eclipse.jgit.lib.AnyObjectId
54
import org.eclipse.jgit.revwalk.RevCommit
6-
import org.eclipse.jgit.revwalk.RevCommitList
7-
import org.eclipse.jgit.revwalk.RevWalk
8-
import java.text.MessageFormat
95
import java.util.*
106

117
/**
@@ -21,11 +17,18 @@ import java.util.*
2117
*
2218
* type of lane used by the application.
2319
</L> */
24-
class GraphCommitList : RevCommitList<GraphNode>() {
20+
class GraphCommitList(
21+
private val commits: MutableList<GraphNode> = mutableListOf(),
22+
) : MutableList<GraphNode> by commits {
23+
var walker: GraphWalk? = null
2524
private var positionsAllocated = 0
2625
private val freePositions = TreeSet<Int>()
2726
private val activeLanes = HashSet<GraphLane>(32)
2827

28+
private var parentId: AnyObjectId? = null
29+
30+
private val graphCommit = UncommittedChangesGraphNode()
31+
2932
var maxLine = 0
3033
private set
3134

@@ -35,34 +38,48 @@ class GraphCommitList : RevCommitList<GraphNode>() {
3538
)
3639

3740
override fun clear() {
38-
super.clear()
3941
positionsAllocated = 0
4042
freePositions.clear()
4143
activeLanes.clear()
4244
laneLength.clear()
45+
commits.clear()
4346
}
4447

45-
override fun source(revWalk: RevWalk) {
46-
if (revWalk !is GraphWalk) throw ClassCastException(
47-
MessageFormat.format(
48-
JGitText.get().classCastNotA,
49-
GraphWalk::class.java.name
50-
)
51-
)
48+
fun fillTo(highMark: Int) {
49+
if (this.size <= highMark) {
50+
var c: GraphNode? = null
51+
52+
do {
53+
c = this.walker?.next()
54+
55+
if (c != null) {
56+
val stashChild = c.children.firstOrNull { it.isStash }
57+
58+
if (
59+
stashChild == null ||
60+
(
61+
stashChild.parentCount > 0 &&
62+
stashChild.parents[0] == c
63+
)
64+
) {
65+
this.enter(this.size, c)
66+
this.add(c)
67+
}
68+
}
69+
} while (c != null && size <= highMark)
5270

53-
super.source(revWalk)
71+
if (c == null) {
72+
this.walker = null
73+
}
74+
}
5475
}
5576

56-
private var parentId: AnyObjectId? = null
57-
58-
private val graphCommit = UncommittedChangesGraphNode()
59-
6077
fun addUncommittedChangesGraphCommit(parent: RevCommit) {
6178
parentId = parent.id
6279
graphCommit.lane = nextFreeLane()
6380
}
6481

65-
override fun enter(index: Int, currCommit: GraphNode) {
82+
fun enter(index: Int, currCommit: GraphNode) {
6683
var isUncommittedChangesNodeParent = false
6784
if (currCommit.id == parentId) {
6885
graphCommit.graphParent = currCommit
@@ -113,19 +130,18 @@ class GraphCommitList : RevCommitList<GraphNode>() {
113130
}
114131
} else {
115132
val children = currCommit.children.sortedBy { it.lane.position }
116-
for (i in 0 until nChildren) {
117-
val c: GraphNode = children[i]
118-
if (c.getGraphParent(0) === currCommit) {
119-
if (c.lane.position < 0)
120-
println("c.lane.position is invalid (${c.lane.position})")
133+
for (child in children) {
134+
if (child.getGraphParent(0) === currCommit) {
135+
if (child.lane.position < 0)
136+
println("child.lane.position is invalid (${child.lane.position})")
121137

122-
val length = laneLength[c.lane]
138+
val length = laneLength[child.lane]
123139

124140
// we may be the first parent for multiple lines of
125141
// development, try to continue the longest one
126142
if (length != null && length > lengthOfReservedLane) {
127-
reservedLane = c.lane
128-
childOnReservedLane = c
143+
reservedLane = child.lane
144+
childOnReservedLane = child
129145
lengthOfReservedLane = length
130146

131147
break
@@ -156,9 +172,17 @@ class GraphCommitList : RevCommitList<GraphNode>() {
156172

157173
continueActiveLanes(currCommit)
158174

159-
if (currCommit.parentCount == 0 && currCommit.lane.position == INVALID_LANE_POSITION)
175+
if (currCommit.parentCount == 0 && currCommit.lane.position == INVALID_LANE_POSITION) {
160176
closeLane(currCommit.lane)
177+
}
161178

179+
if (
180+
currCommit.childCount == 1 &&
181+
currCommit.children.first().isStash &&
182+
currCommit.parentCount == 0
183+
) {
184+
closeLane(currCommit.lane)
185+
}
162186
}
163187

164188
private fun continueActiveLanes(currCommit: GraphNode) {
@@ -182,7 +206,8 @@ class GraphCommitList : RevCommitList<GraphNode>() {
182206
* the lane
183207
*/
184208
private fun handleBlockedLanes(
185-
index: Int, currentNode: GraphNode,
209+
index: Int,
210+
currentNode: GraphNode,
186211
childOnLane: GraphNode?,
187212
) {
188213
for (child in currentNode.children) {
@@ -211,8 +236,11 @@ class GraphCommitList : RevCommitList<GraphNode>() {
211236

212237
// Handles the case where currCommit is a non-first parent of the child
213238
private fun handleMerge(
214-
index: Int, currCommit: GraphNode,
215-
childOnLane: GraphNode?, child: GraphNode, laneToUse: GraphLane,
239+
index: Int,
240+
currCommit: GraphNode,
241+
childOnLane: GraphNode?,
242+
child: GraphNode,
243+
laneToUse: GraphLane,
216244
): GraphLane {
217245

218246
// find all blocked positions between currCommit and this child
@@ -302,8 +330,9 @@ class GraphCommitList : RevCommitList<GraphNode>() {
302330
}
303331

304332
private fun setupChildren(currCommit: GraphNode) {
305-
val nParents = currCommit.parentCount
306-
for (i in 0 until nParents) (currCommit.getParent(i) as GraphNode).addChild(currCommit)
333+
for (parent in currCommit.parents) {
334+
(parent as GraphNode).addChild(currCommit)
335+
}
307336
}
308337

309338
private fun nextFreeLane(blockedPositions: BitSet? = null): GraphLane {
@@ -363,4 +392,4 @@ class GraphCommitList : RevCommitList<GraphNode>() {
363392
maxLine = this.maxOf { it.lane.position }
364393
}
365394
}
366-
}
395+
}

src/main/kotlin/com/jetpackduba/gitnuro/git/graph/GraphWalk.kt

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import java.io.IOException
1515
class GraphWalk(private var repository: Repository?) : RevWalk(repository) {
1616
private var additionalRefMap: MutableMap<AnyObjectId, Set<Ref>>? = HashMap()
1717
private var reverseRefMap: MutableMap<AnyObjectId, Set<Ref>>? = null
18+
private var stashes = emptySet<AnyObjectId>()
1819

1920
init {
2021
super.sort(RevSort.TOPO, true)
@@ -45,17 +46,18 @@ class GraphWalk(private var repository: Repository?) : RevWalk(repository) {
4546
return GraphNode(id)
4647
}
4748

48-
override fun next(): RevCommit? {
49+
override fun next(): GraphNode? {
4950
val graphNode = super.next() as GraphNode?
5051

5152
if (graphNode != null) {
5253
val refs = getRefs(graphNode)
53-
54-
graphNode.isStash = refs.count() == 1 && refs.firstOrNull()?.name == "refs/stash"
5554
graphNode.refs = refs
55+
56+
graphNode.isStash = stashes.contains(graphNode)
5657
}
5758

5859
return graphNode
60+
5961
}
6062

6163
private fun getRefs(commitId: AnyObjectId): List<Ref> {
@@ -102,14 +104,14 @@ class GraphWalk(private var repository: Repository?) : RevWalk(repository) {
102104
repository?.let { repo ->
103105
for (ref in repo.refDatabase.getRefsByPrefix(prefix)) {
104106
if (ref.isSymbolic) continue
105-
markStartRef(ref)
107+
markStartObjectId(ref.leaf.objectId)
106108
}
107109
}
108110
}
109111

110-
private fun markStartRef(ref: Ref) {
112+
private fun markStartObjectId(objectId: AnyObjectId) {
111113
try {
112-
val refTarget = parseAny(ref.leaf.objectId)
114+
val refTarget = parseAny(objectId)
113115

114116
when (refTarget) {
115117
is RevCommit -> markStart(refTarget)
@@ -128,6 +130,17 @@ class GraphWalk(private var repository: Repository?) : RevWalk(repository) {
128130
}
129131
}
130132

133+
fun markStartStashes(stashList: List<RevCommit>) {
134+
for (stash in stashList) {
135+
markStartObjectId(stash)
136+
}
137+
138+
this.stashes = stashList
139+
.asSequence()
140+
.map { it.toObjectId() }
141+
.toSet()
142+
}
143+
131144
internal inner class GraphRefComparator : Comparator<Ref> {
132145
override fun compare(o1: Ref, o2: Ref): Int {
133146
try {

src/main/kotlin/com/jetpackduba/gitnuro/git/log/GetLogUseCase.kt

Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@ package com.jetpackduba.gitnuro.git.log
33

44
import com.jetpackduba.gitnuro.git.graph.GraphCommitList
55
import com.jetpackduba.gitnuro.git.graph.GraphWalk
6+
import com.jetpackduba.gitnuro.git.stash.GetStashListUseCase
67
import kotlinx.coroutines.Dispatchers
78
import kotlinx.coroutines.ensureActive
89
import kotlinx.coroutines.withContext
910
import org.eclipse.jgit.api.Git
1011
import org.eclipse.jgit.lib.Constants
1112
import org.eclipse.jgit.lib.Ref
12-
import org.eclipse.jgit.lib.Repository
1313
import javax.inject.Inject
1414

15-
class GetLogUseCase @Inject constructor() {
16-
private var graphWalkCached: GraphWalk? = null
17-
15+
class GetLogUseCase @Inject constructor(
16+
private val getStashListUseCase: GetStashListUseCase,
17+
) {
1818
suspend operator fun invoke(
1919
git: Git,
2020
currentBranch: Ref?,
@@ -23,50 +23,38 @@ class GetLogUseCase @Inject constructor() {
2323
cachedCommitList: GraphCommitList? = null,
2424
) = withContext(Dispatchers.IO) {
2525
val repositoryState = git.repository.repositoryState
26-
val commitList: GraphCommitList = cachedCommitList ?: GraphCommitList()
27-
26+
val commitList = cachedCommitList ?: GraphCommitList()
2827
if (currentBranch != null || repositoryState.isRebasing) { // Current branch is null when there is no log (new repo) or rebasing
2928
val logList = git.log().setMaxCount(1).call().toList()
3029

31-
val walk = GraphWalk(git.repository)
30+
if (cachedCommitList == null) {
31+
val walk = GraphWalk(git.repository)
3232

33-
walk.use {
34-
// Without this, during rebase conflicts the graph won't show the HEAD commits (new commits created
35-
// by the rebase)
36-
if (cachedCommitList == null) {
33+
walk.use {
34+
// Without this, during rebase conflicts the graph won't show the HEAD commits (new commits created
35+
// by the rebase)
3736
walk.markStart(walk.lookupCommit(logList.first()))
3837

3938
walk.markStartAllRefs(Constants.R_HEADS)
4039
walk.markStartAllRefs(Constants.R_REMOTES)
4140
walk.markStartAllRefs(Constants.R_TAGS)
41+
walk.markStartStashes(getStashListUseCase(git))
4242

43-
if (hasUncommittedChanges)
43+
if (hasUncommittedChanges) {
4444
commitList.addUncommittedChangesGraphCommit(logList.first())
45-
// val count = walk.count()
46-
// println("Commits list count is $count")
47-
commitList.source(walk)
45+
}
46+
47+
commitList.walker = walk
4848
}
49-
commitList.fillTo(commitsLimit)
5049
}
5150

51+
commitList.fillTo(commitsLimit)
52+
5253
ensureActive()
5354
}
5455

5556
commitList.calcMaxLine()
5657

5758
return@withContext commitList
5859
}
59-
60-
private fun cachedGraphWalk(repository: Repository): GraphWalk {
61-
val graphWalkCached = this.graphWalkCached
62-
63-
return if (graphWalkCached != null) {
64-
graphWalkCached
65-
} else {
66-
val newGraphWalk = GraphWalk(repository)
67-
this.graphWalkCached = newGraphWalk
68-
69-
newGraphWalk
70-
}
71-
}
7260
}

src/main/kotlin/com/jetpackduba/gitnuro/ui/log/Log.kt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,7 @@ import com.jetpackduba.gitnuro.ui.dialogs.NewTagDialog
6060
import com.jetpackduba.gitnuro.ui.dialogs.ResetBranchDialog
6161
import com.jetpackduba.gitnuro.ui.dialogs.SetDefaultUpstreamBranchDialog
6262
import com.jetpackduba.gitnuro.ui.resizePointerIconEast
63-
import com.jetpackduba.gitnuro.viewmodels.ChangeUpstreamBranchDialogViewModel
64-
import com.jetpackduba.gitnuro.viewmodels.LogSearch
65-
import com.jetpackduba.gitnuro.viewmodels.LogStatus
66-
import com.jetpackduba.gitnuro.viewmodels.LogViewModel
63+
import com.jetpackduba.gitnuro.viewmodels.*
6764
import kotlinx.coroutines.flow.distinctUntilChanged
6865
import kotlinx.coroutines.launch
6966
import org.eclipse.jgit.lib.Ref
@@ -96,7 +93,7 @@ private const val DIVIDER_WIDTH = 8
9693

9794
private const val LOG_BOTTOM_PADDING = 80
9895

99-
private const val MIN_COMMITS_BEFORE_REQUESTING_MORE = 100
96+
private const val MIN_COMMITS_BEFORE_REQUESTING_MORE = INCREMENTAL_COMMITS_LOAD
10097

10198
private const val TAG = "LogView"
10299

src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/LogViewModel.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ private const val FIRST_INDEX = 1
5050

5151
private const val LOG_MIN_TIME_IN_MS_TO_SHOW_LOAD = 500L
5252

53-
private const val INITIAL_COMMITS_LOAD = 4000
54-
private const val INCREMENTAL_COMMITS_LOAD = 1000
53+
private const val INITIAL_COMMITS_LOAD = 2000
54+
const val INCREMENTAL_COMMITS_LOAD = 1000
5555

5656
class LogViewModel @Inject constructor(
5757
private val getLogUseCase: GetLogUseCase,

0 commit comments

Comments
 (0)