added mrged blocks and README.md
This commit is contained in:
parent
3f6a16224e
commit
2262c66b63
31
README.md
31
README.md
@ -2,6 +2,37 @@
|
||||
|
||||
Tho tools to make smart merge of two versions of changed original data. See [merge3] function online docs.
|
||||
|
||||
## Usage
|
||||
|
||||
Add dependency:
|
||||
|
||||
~~~kotlin
|
||||
repositories {
|
||||
//...
|
||||
maven("https://gitea.sergeych.net/api/packages/SergeychWorks/maven")
|
||||
}
|
||||
~~~
|
||||
|
||||
and in the source set dependencies something like
|
||||
|
||||
~~~kotlin
|
||||
dependencies {
|
||||
// ...
|
||||
implementation("net.sergeych:mp_diff:0.0.1-SNAPSHOT")
|
||||
}
|
||||
~~~
|
||||
|
||||
Now call it in the code
|
||||
|
||||
~~~kotlin
|
||||
val src = "Hello world".toList()
|
||||
val a = "Hello friend".toList()
|
||||
val b = "Bye world".toList()
|
||||
|
||||
val m = merge3(src, b, a)
|
||||
assertEquals("Bye friend", m.merged.joinToString(""))
|
||||
println(m.blocks)
|
||||
~~~
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
plugins {
|
||||
kotlin("multiplatform") version "1.8.0"
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
group = "net.sergeych"
|
||||
@ -57,4 +58,23 @@ kotlin {
|
||||
val nativeMain by getting
|
||||
val nativeTest by getting
|
||||
}
|
||||
|
||||
publishing {
|
||||
val mavenToken by lazy {
|
||||
File("${System.getProperty("user.home")}/.gitea_token").readText()
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
credentials(HttpHeaderCredentials::class) {
|
||||
name = "Authorization"
|
||||
value = mavenToken
|
||||
}
|
||||
url = uri("https://gitea.sergeych.net/api/packages/cloudoc.ru/maven")
|
||||
authentication {
|
||||
create("Authorization", HttpHeaderAuthentication::class)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
1944
kotlin-js-store/yarn.lock
Normal file
1944
kotlin-js-store/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -11,14 +11,68 @@ import net.sergeych.mp_logger.debug
|
||||
import net.sergeych.sprintf.sprintf
|
||||
|
||||
/**
|
||||
* Merge result. This version does not report (yet) conflicts, just merges it all.
|
||||
* @param merged the best merged resut. In the case of the conflict usually has both variants concatennated in place
|
||||
* Merge result. This version does not report (yet) conflicts, just merges it all. See also [blocks] for
|
||||
* another representation of the merged data.
|
||||
*
|
||||
* @param merged the best merged data. In the case of the conflict usually has both variants concatennated in place
|
||||
* @param changedAreas ranges where data were altered. could be used to highlight changes
|
||||
*/
|
||||
class MergeResult<T>(
|
||||
val merged: List<T>,
|
||||
val changedAreas: List<ClosedRange<Int>>,
|
||||
)
|
||||
val changedAreas: List<IntRange>,
|
||||
) {
|
||||
val blocks: List<MergedBlock<T>> by lazy {
|
||||
if (changedAreas.isEmpty())
|
||||
listOf(MergedBlock.Unchanged(merged))
|
||||
else {
|
||||
val result = mutableListOf<MergedBlock<T>>()
|
||||
var start = 0
|
||||
for (r in changedAreas) {
|
||||
if (start != r.start) {
|
||||
result.add(MergedBlock.Unchanged(merged.slice(start until r.start)))
|
||||
}
|
||||
if (!r.isEmpty()) {
|
||||
result.add(MergedBlock.Resolved(merged.slice(r)))
|
||||
}
|
||||
start = r.endInclusive + 1
|
||||
}
|
||||
if (start < merged.size) {
|
||||
result.add(MergedBlock.Unchanged(merged.slice(start until merged.size)))
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge3 sequential result block.
|
||||
*/
|
||||
sealed class MergedBlock<T> {
|
||||
/**
|
||||
* Merged data. Int the case of the conflict, the concatenated conflicting data
|
||||
*/
|
||||
abstract val data: List<T>
|
||||
|
||||
/**
|
||||
* Data that are equals in both variants and therefore was not altered
|
||||
*/
|
||||
data class Unchanged<T>(override val data: List<T>) : MergedBlock<T>()
|
||||
|
||||
/**
|
||||
* The portion of data that was merged without conflicts
|
||||
*/
|
||||
data class Resolved<T>(override val data: List<T>) : MergedBlock<T>()
|
||||
|
||||
/**
|
||||
* The portion of data that can't be merged due to the conflicting changes.
|
||||
* __Note it is not yet used.__
|
||||
*/
|
||||
@Suppress("unused")
|
||||
data class Conflict<T>(val variantA: List<T>, val variantB: List<T>) : MergedBlock<T>() {
|
||||
override val data = variantA + variantB
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Perform 3-way merge. See [merge3] for details.
|
||||
@ -98,8 +152,7 @@ private class Merge3<T>(val source: List<T>, val variantA: List<T>, val variantB
|
||||
}
|
||||
}
|
||||
|
||||
private val conflicts = mutableListOf<ClosedRange<Int>>()
|
||||
private val updates = mutableListOf<ClosedRange<Int>>()
|
||||
private val updates = mutableListOf<IntRange>()
|
||||
|
||||
private fun applyDelta(delta: AbstractDelta<T>) {
|
||||
when (delta) {
|
||||
@ -149,12 +202,12 @@ private class Merge3<T>(val source: List<T>, val variantA: List<T>, val variantB
|
||||
// detect ranges
|
||||
var conflictStart = -1
|
||||
var changeStart = -1
|
||||
fun closeConflict(index: Int) {
|
||||
if (conflictStart >= 0) {
|
||||
conflicts.add(conflictStart until index)
|
||||
conflictStart = -1
|
||||
}
|
||||
}
|
||||
// fun closeConflict(index: Int) {
|
||||
// if (conflictStart >= 0) {
|
||||
// conflicts.add(conflictStart until index)
|
||||
// conflictStart = -1
|
||||
// }
|
||||
// }
|
||||
|
||||
fun closeUpdate(index: Int) {
|
||||
if (changeStart >= 0) {
|
||||
@ -167,13 +220,13 @@ private class Merge3<T>(val source: List<T>, val variantA: List<T>, val variantB
|
||||
when (count) {
|
||||
0 -> {
|
||||
// close conflict or update if was open
|
||||
closeConflict(index)
|
||||
// closeConflict(index)
|
||||
closeUpdate(index)
|
||||
}
|
||||
|
||||
1 -> {
|
||||
// could be end of the conflict
|
||||
closeConflict(index)
|
||||
// closeConflict(index)
|
||||
// open change if not opened
|
||||
if (changeStart < 0) changeStart = index
|
||||
}
|
||||
@ -191,7 +244,7 @@ private class Merge3<T>(val source: List<T>, val variantA: List<T>, val variantB
|
||||
}
|
||||
}
|
||||
}
|
||||
closeConflict(changeCount.size)
|
||||
// closeConflict(changeCount.size)
|
||||
closeUpdate(changeCount.size)
|
||||
|
||||
return MergeResult<T>(result, updates)
|
||||
@ -205,5 +258,5 @@ private class Merge3<T>(val source: List<T>, val variantA: List<T>, val variantB
|
||||
*/
|
||||
fun <T> merge3(source: List<T>, a: List<T>, b: List<T>, showDebug: Boolean = false): MergeResult<T> =
|
||||
Merge3(source, a, b).also {
|
||||
if( showDebug ) it.logLevel = Log.Level.DEBUG
|
||||
if (showDebug) it.logLevel = Log.Level.DEBUG
|
||||
}.perform()
|
@ -32,6 +32,7 @@ class BasicTest {
|
||||
println(m.changedAreas)
|
||||
assertEquals("Bye friend", m.merged.str)
|
||||
// assertTrue(m.noConflicts)
|
||||
println(m.blocks)
|
||||
}
|
||||
@Test
|
||||
fun testMergeWithConflicts() {
|
Loading…
Reference in New Issue
Block a user