Skip to content

Commit 4f36eb0

Browse files
author
Oleksandr Dzhychko
committed
feat(model-datastructure): optimize merge algorithm
1 parent e66ac56 commit 4f36eb0

File tree

6 files changed

+21
-21
lines changed

6 files changed

+21
-21
lines changed

model-datastructure/src/commonMain/kotlin/org/modelix/model/LinearHistory.kt

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class LinearHistory(val baseVersionHash: String?) {
99
* This means that a version must come after all its descendants.
1010
* Returns the ordered versions starting with the earliest version.
1111
*/
12-
fun loadLazy(vararg fromVersions: CLVersion) = sequence {
12+
fun computeHistoryLazy(vararg fromVersions: CLVersion) = sequence {
1313
// The algorithm sorts the versions topologically.
1414
// It performs a depth-first search.
1515
// It is implemented as an iterative algorithm with a stack.
@@ -31,9 +31,7 @@ class LinearHistory(val baseVersionHash: String?) {
3131
val versionWasVisited = !visited.add(version)
3232
if (versionWasVisited) {
3333
stack.removeLast()
34-
if (!version.isMerge()) {
35-
yield(version)
36-
}
34+
yield(version)
3735
}
3836
val descendants = if (version.isMerge()) {
3937
// Put version 1 last, so that is processed first.
@@ -52,9 +50,9 @@ class LinearHistory(val baseVersionHash: String?) {
5250
}
5351

5452
/**
55-
* Same as [[loadLazy]], but returning as a list instead of a lazy sequence.
53+
* Same as [[computeHistoryLazy]], but returning as a list instead of a lazy sequence and omitting merge versions.
5654
*/
57-
fun load(vararg fromVersions: CLVersion): List<CLVersion> {
58-
return loadLazy(*fromVersions).toList()
55+
fun computeHistoryWithoutMerges(vararg fromVersions: CLVersion): List<CLVersion> {
56+
return computeHistoryLazy(*fromVersions).filterNot { it.isMerge() }.toList()
5957
}
6058
}

model-datastructure/src/commonMain/kotlin/org/modelix/model/VersionMerger.kt

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,23 +51,25 @@ class VersionMerger(private val storeCache: IDeserializingKeyValueStore, private
5151
if (commonBase?.hash == leftVersion.hash) return rightVersion
5252
if (commonBase?.hash == rightVersion.hash) return leftVersion
5353

54-
val leftNonMerges = LinearHistory(commonBase?.hash).loadLazy(leftVersion).toSet()
55-
val rightNonMerges = LinearHistory(commonBase?.hash).loadLazy(rightVersion).toSet()
56-
if (leftNonMerges == rightNonMerges) {
57-
// If there is no actual change on both sides, but they just did the same merge, we have to pick one
58-
// of them, otherwise both sides will continue creating merges forever.
59-
return if (leftVersion.id < rightVersion.id) leftVersion else rightVersion
60-
}
61-
if (leftNonMerges.containsAll(rightNonMerges)) {
54+
val leftVersions = LinearHistory(commonBase?.hash).computeHistoryLazy(leftVersion)
55+
if (leftVersions.contains(rightVersion)) {
6256
// No merge needed, fast-forward to the left version
6357
return leftVersion
6458
}
65-
if (rightNonMerges.containsAll(leftNonMerges)) {
59+
val rightVersions = LinearHistory(commonBase?.hash).computeHistoryLazy(rightVersion)
60+
if (rightVersions.contains(leftVersion)) {
6661
// No merge needed, fast-forward to the right version
6762
return rightVersion
6863
}
64+
val leftNonMerges = leftVersions.filterNot { it.isMerge() }.toSet()
65+
val rightNonMerges = rightVersions.filterNot { it.isMerge() }.toSet()
66+
if (leftNonMerges == rightNonMerges) {
67+
// If there is no actual change on both sides, but they just did the same merge, we have to pick one
68+
// of them, otherwise both sides will continue creating merges forever.
69+
return if (leftVersion.id < rightVersion.id) leftVersion else rightVersion
70+
}
6971

70-
val versionsToApply = LinearHistory(commonBase?.hash).load(leftVersion, rightVersion)
72+
val versionsToApply = LinearHistory(commonBase?.hash).computeHistoryWithoutMerges(leftVersion, rightVersion)
7173

7274
val operationsToApply = versionsToApply.flatMap { captureIntend(it) }
7375
var mergedVersion: CLVersion? = null

model-datastructure/src/commonMain/kotlin/org/modelix/model/lazy/CLVersion.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ private fun computeDelta(keyValueStore: IKeyValueStore, versionHash: String, bas
324324
// VersionMerger(store, IdGenerator.newInstance(0)).mergeChange(version, baseVersion)
325325
// }
326326

327-
val history = LinearHistory(baseVersionHash).load(version)
327+
val history = LinearHistory(baseVersionHash).computeHistoryWithoutMerges(version)
328328
val bulkQuery = BulkQuery(store)
329329
var v1 = baseVersion
330330
for (v2 in history) {

model-datastructure/src/commonTest/kotlin/LinearHistoryTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ class LinearHistoryTest {
197197

198198
private fun history(v1: CLVersion, v2: CLVersion): List<CLVersion> {
199199
val base = VersionMerger.commonBaseVersion(v1, v2)
200-
val history = LinearHistory(base?.getContentHash()).load(v1, v2)
200+
val history = LinearHistory(base?.getContentHash()).computeHistoryWithoutMerges(v1, v2)
201201
assertHistoryIsCorrect(history)
202202
return history
203203
}

model-datastructure/src/commonTest/kotlin/UndoTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ class UndoTest {
140140
}
141141

142142
fun printHistory(version: CLVersion, store: IDeserializingKeyValueStore) {
143-
LinearHistory(null).load(version).forEach {
143+
LinearHistory(null).computeHistoryWithoutMerges(version).forEach {
144144
println("Version ${it.id.toString(16)} ${it.hash} ${it.author}")
145145
for (op in it.operations) {
146146
println(" $op")

model-server/src/main/kotlin/org/modelix/model/server/handlers/HistoryHandler.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ class HistoryHandler(val client: IModelClient, private val repositoriesManager:
221221
if (rowIndex >= skip) {
222222
createTableRow(version, latestVersion)
223223
if (version.isMerge()) {
224-
for (v in LinearHistory(version.baseVersion!!.getContentHash()).load(version.getMergedVersion1()!!, version.getMergedVersion2()!!)) {
224+
for (v in LinearHistory(version.baseVersion!!.getContentHash()).computeHistoryWithoutMerges(version.getMergedVersion1()!!, version.getMergedVersion2()!!)) {
225225
createTableRow(v, latestVersion)
226226
rowIndex++
227227
if (rowIndex >= skip + limit) {

0 commit comments

Comments
 (0)