Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
wetneb committed Apr 6, 2024
1 parent 066f78a commit 43e7b80
Show file tree
Hide file tree
Showing 17 changed files with 82 additions and 26 deletions.
3 changes: 2 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>6.3.0.202209071007-r</version>
<!-- <version>6.9.0.202403050737-r</version> -->
<version>6.10.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
Expand Down
15 changes: 8 additions & 7 deletions src/main/java/se/kth/spork/cli/Cli.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public static String prettyPrint(CtModule spoonRoot) {
.map(Object::toString)
.map(impStmt -> impStmt.substring("import ".length(), impStmt.length() - 1))
.collect(Collectors.toList());
new PrinterPreprocessor(importNames, activePackage.getQualifiedName()).scan(spoonRoot);
new PrinterPreprocessor(importNames, activePackage.getQualifiedName(), Spoon3dmMerge.INSTANCE.getDiff3()).scan(spoonRoot);

StringBuilder sb = new StringBuilder();

Expand Down Expand Up @@ -172,7 +172,7 @@ public Integer call() throws IOException {
rightPath.toFile().deleteOnExit();
}

Pair<String, Integer> merged = merge(basePath, leftPath, rightPath, exitOnError);
Pair<String, Integer> merged = merge(basePath, leftPath, rightPath, exitOnError, diff3);
String pretty = merged.getFirst();
int numConflicts = merged.getSecond();

Expand Down Expand Up @@ -205,10 +205,11 @@ public Integer call() throws IOException {
* @param right Path to right revision.
* @param exitOnError Disallow the use of line-based fallback if the structured merge encounters
* an error.
* @param diff3 whether to use the diff3 style one or the default one
* @return A pair on the form (prettyPrint, numConflicts)
*/
public static Pair<String, Integer> merge(
Path base, Path left, Path right, boolean exitOnError) {
Path base, Path left, Path right, boolean exitOnError, boolean diff3) {
try {
LOGGER.info(() -> "Parsing input files");
CtModule baseModule = Parser.INSTANCE.parse(base);
Expand All @@ -231,7 +232,7 @@ public static Pair<String, Integer> merge(
LOGGER.warn(
() ->
"Merge contains no types (i.e. classes, interfaces, etc), reverting to line-based merge");
return lineBasedMerge(base, left, right);
return lineBasedMerge(base, left, right, diff3);
}
} catch (Exception e) {
if (exitOnError) {
Expand All @@ -244,16 +245,16 @@ public static Pair<String, Integer> merge(
LOGGER.info(
() ->
"Spork encountered an error in structured merge. Falling back to line-based merge");
return lineBasedMerge(base, left, right);
return lineBasedMerge(base, left, right, diff3);
}
}
}

private static Pair<String, Integer> lineBasedMerge(Path base, Path left, Path right) {
private static Pair<String, Integer> lineBasedMerge(Path base, Path left, Path right, boolean diff3) {
String baseStr = Parser.INSTANCE.read(base);
String leftStr = Parser.INSTANCE.read(left);
String rightStr = Parser.INSTANCE.read(right);
return LineBasedMergeKt.lineBasedMerge(baseStr, leftStr, rightStr);
return LineBasedMergeKt.lineBasedMerge(baseStr, leftStr, rightStr, diff3);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public class PrinterPreprocessor extends CtScanner {
private final String activePackage;

private final Map<String, Set<CtPackageReference>> refToPack;
private final boolean diff3;

private int currentConflictId;

Expand All @@ -41,12 +42,13 @@ public class PrinterPreprocessor extends CtScanner {
// TODO improve the pretty-printer such that this hack is redundant
private final Map<String, Triple<String, String, String>> globalContentConflicts;

public PrinterPreprocessor(List<String> importStatements, String activePackage) {
public PrinterPreprocessor(List<String> importStatements, String activePackage, boolean diff3) {
this.importStatements = importStatements;
this.activePackage = activePackage;
refToPack = new HashMap<>();
currentConflictId = 0;
globalContentConflicts = new HashMap<>();
this.diff3 = diff3;
}

@Override
Expand Down Expand Up @@ -143,13 +145,13 @@ private void processConflict(ContentConflict conflict, CtElement element) {
String rawRight =
(String) conflict.getRight().getMetadata(RoledValue.Key.RAW_CONTENT);
String rawBase =
conflict.getBase() == null
conflict.getBase() != null
? (String)
conflict.getBase().getMetadata(RoledValue.Key.RAW_CONTENT)
: "";

Pair<String, Integer> rawConflict =
LineBasedMergeKt.lineBasedMerge(rawBase, rawLeft, rawRight);
LineBasedMergeKt.lineBasedMerge(rawBase, rawLeft, rawRight, diff3);
assert rawConflict.getSecond() > 0
: "Comments without conflict should already have been merged";

Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/se/kth/spork/spoon/Spoon3dmMerge.kt
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ object Spoon3dmMerge {
val baseComment = getCuComment(base)
val leftComment = getCuComment(left)
val rightComment = getCuComment(right)
return lineBasedMerge(baseComment, leftComment, rightComment)
return lineBasedMerge(baseComment, leftComment, rightComment, diff3)
}

private fun getCuComment(mod: CtElement): String = mod.getMetadata(Parser.COMPILATION_UNIT_COMMENT) as String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ class CommentContentHandler : ContentConflictHandler {
baseElem: CtElement?,
leftElem: CtElement,
rightElem: CtElement,
diff3: Boolean,
): Pair<Any?, Boolean> {
return Pair(mergeComments(baseVal ?: "", leftVal, rightVal), false)
return Pair(mergeComments(baseVal ?: "", leftVal, rightVal, diff3), false)
}

private fun mergeComments(base: Any, left: Any, right: Any): Any? {
val merge = lineBasedMerge(base.toString(), left.toString(), right.toString())
private fun mergeComments(base: Any, left: Any, right: Any, diff3: Boolean): Any? {
val merge = lineBasedMerge(base.toString(), left.toString(), right.toString(), diff3)
return if (merge.second > 0) {
null
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ interface ContentConflictHandler {
baseElem: CtElement?,
leftElem: CtElement,
rightElem: CtElement,
diff3: Boolean,
): Pair<Any?, Boolean>

/** @return The role that this conflict handler deals with.
Expand Down
2 changes: 2 additions & 0 deletions src/main/kotlin/se/kth/spork/spoon/conflict/ContentMerger.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class ContentMerger(conflictHandlers: List<ContentConflictHandler>) {
*/
fun mergedContent(
nodeContents: Set<Content<SpoonNode, RoledValues>>,
diff3: Boolean,
): Pair<RoledValues?, List<ContentConflict>> {
if (nodeContents.size == 1) {
return Pair(nodeContents.iterator().next().value, emptyList())
Expand Down Expand Up @@ -85,6 +86,7 @@ class ContentMerger(conflictHandlers: List<ContentConflictHandler>) {
baseRoledValues?.element,
leftRoledValues.element,
rightRoledValues.element,
diff3,
)
merged = result.first
conflictPresent = result.second
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class IsImplicitHandler : ContentConflictHandler {
baseElem: CtElement?,
leftElem: CtElement,
rightElem: CtElement,
diff3: Boolean,
): Pair<Any?, Boolean> {
val mergedValue = if (baseVal != null) {
// as there are only two possible values for a boolean, left and right disagreeing must
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class IsUpperHandler : ContentConflictHandler {
baseElem: CtElement?,
leftElem: CtElement,
rightElem: CtElement,
diff3: Boolean,
): Pair<Any?, Boolean> {
return Pair(mergeIsUpper(baseElem, leftElem, rightElem), false)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class ModifierHandler : ContentConflictHandler {
baseElem: CtElement?,
leftElem: CtElement,
rightElem: CtElement,
diff3: Boolean,
): Pair<Any?, Boolean> {
return mergeModifierKinds(
(baseVal ?: setOf<ModifierKind>()) as Set<ModifierKind>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fun fromMergedPcs(
contentConflictHandlers: List<ContentConflictHandler>,
diff3: Boolean
): Pair<CtElement?, Int> {
val sporkTreeBuilder = SporkTreeBuilder(base, delta, baseLeft, baseRight, structuralConflictHandlers)
val sporkTreeBuilder = SporkTreeBuilder(base, delta, baseLeft, baseRight, structuralConflictHandlers, diff3)
val sporkTreeRoot = sporkTreeBuilder.buildTree()

// this is a bit of a hack, get any used environment such that the SpoonTreeBuilder can copy environment details
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class SpoonTreeBuilder internal constructor(
mergeTree = originalTree.clone()
mergeTree.putMetadata<CtElement>(SINGLE_REVISION_KEY, sporkChild.singleRevision)
} else {
val mergedContent = contentMerger.mergedContent(sporkChild.content)
val mergedContent = contentMerger.mergedContent(sporkChild.content, diff3)
mergeTree = shallowCopyTree(originalTree, factory)
mergedContent
.first?.forEach { roledValue ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ internal class SporkTreeBuilder(
private val baseLeft: SpoonMapping,
private val baseRight: SpoonMapping,
private val conflictHandlers: List<StructuralConflictHandler>,
private val diff3: Boolean
) {
private val rootToChildren: Map<SpoonNode, Map<SpoonNode, Pcs<SpoonNode>>> = buildRootToChildren(delta.pcsSet)
private val baseRootToChildren: Map<SpoonNode, Map<SpoonNode, Pcs<SpoonNode>>> = buildRootToChildren(base.pcsSet)
Expand Down Expand Up @@ -130,7 +131,7 @@ internal class SporkTreeBuilder(
}

private fun build(start: SpoonNode, tree: SporkTree, children: Map<SpoonNode, Pcs<SpoonNode>>?, baseChildren: Map<SpoonNode, Pcs<SpoonNode>>?) {
if (children == null || baseChildren == null) {
if (children == null) {
// leaf node
return
}
Expand Down Expand Up @@ -209,6 +210,7 @@ internal class SporkTreeBuilder(
base.element,
left!!.element,
right!!.element,
diff3
)
numStructuralConflicts += second
return StructuralConflict(
Expand All @@ -224,7 +226,7 @@ internal class SporkTreeBuilder(
conflicting: Pcs<SpoonNode>,
baseSuccessor: Pcs<SpoonNode>?,
children: Map<SpoonNode, Pcs<SpoonNode>>,
baseChildren: Map<SpoonNode, Pcs<SpoonNode>>,
baseChildren: Map<SpoonNode, Pcs<SpoonNode>>?,
tree: SporkTree,
): SpoonNode {
remainingInconsistencies.remove(nextPcs)
Expand Down
17 changes: 14 additions & 3 deletions src/main/kotlin/se/kth/spork/util/LineBasedMerge.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import java.util.Objects
* @param right The right revision.
* @return A pair containing the merge and the amount of conflicts.
*/
fun lineBasedMerge(base: String, left: String, right: String): Pair<String, Int> {
fun lineBasedMerge(base: String, left: String, right: String, diff3: Boolean): Pair<String, Int> {
if (base.isEmpty() && (left.isEmpty() || right.isEmpty())) {
// For some reason, this merge implementation reports a conflict on pure additions.
// This is an easy fix for that. See #144 for details.
Expand All @@ -31,6 +31,7 @@ fun lineBasedMerge(base: String, left: String, right: String): Pair<String, Int>
val rightRaw = RawText(right.toByteArray())

val merge = MergeAlgorithm()

val res: MergeResult<RawText> = merge.merge(
object : SequenceComparator<RawText>() {
override fun equals(lhs: RawText, lhsIdx: Int, rhs: RawText, rhsIdx: Int) =
Expand Down Expand Up @@ -63,6 +64,16 @@ fun lineBasedMerge(base: String, left: String, right: String): Pair<String, Int>
inConflict = true
}
lines.add(SporkPrettyPrinter.MID_CONFLICT)
} else if (chunk.conflictState
== MergeChunk.ConflictState.BASE_CONFLICTING_RANGE) {
if (!diff3) {
continue;
}
if (!inConflict) {
lines.add(SporkPrettyPrinter.START_CONFLICT)
inConflict = true
}
lines.add(SporkPrettyPrinter.BASE_CONFLICT)
}
for (i in chunk.begin until chunk.end) {
lines.add(seq.getString(i))
Expand All @@ -89,9 +100,9 @@ fun lineBasedMerge(base: String, left: String, right: String): Pair<String, Int>
* @param right The right revision.
* @return A pair containing the merge and the amount of conflicts.
*/
fun lineBasedMerge(base: CtElement, left: CtElement, right: CtElement): Pair<String, Int> {
fun lineBasedMerge(base: CtElement, left: CtElement, right: CtElement, diff3: Boolean): Pair<String, Int> {
val baseSource = SourceExtractor.getOriginalSource(base)
val leftSource = SourceExtractor.getOriginalSource(left)
val rightSource = SourceExtractor.getOriginalSource(right)
return lineBasedMerge(baseSource, leftSource, rightSource)
return lineBasedMerge(baseSource, leftSource, rightSource, diff3)
}
24 changes: 24 additions & 0 deletions src/test/java/se/kth/spork/LineBasedMergeTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package se.kth.spork;

import kotlin.Pair;
import org.junit.jupiter.api.Test;
import se.kth.spork.util.LineBasedMergeKt;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class LineBasedMergeTest {
@Test
public void testSimpleMerge() {
Pair<String, Integer> result = LineBasedMergeKt.lineBasedMerge("hello world", "ola mundo", "bonjour le monde", true);

assertEquals(result.component1(),
"<<<<<<< LEFT\n" +
"ola mundo\n" +
"||||||| BASE\n" +
"hello world\n" +
"=======\n" +
"bonjour le monde\n" +
">>>>>>> RIGHT");
assertEquals(result.component2(), 1);
}
}
4 changes: 2 additions & 2 deletions src/test/java/se/kth/spork/cli/CliTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ void merge_shouldThrowOnMissingType_whenGlobalFallbackIsDisabled() {

assertThrows(
MergeException.class,
() -> Cli.merge(sources.base, sources.left, sources.right, /* exitOnError= */ true),
() -> Cli.merge(sources.base, sources.left, sources.right, /* exitOnError= */ true, /* diff3 = */ false),
"Merge contained no types and global line-based fallback is disabled");
}

Expand All @@ -64,7 +64,7 @@ void merge_shouldMexgeCorrectlyOnMissingType_whenGlobalFallbackIsEnabled() {
String expected = Parser.INSTANCE.read(sources.expected);

Pair<String, Integer> merge =
Cli.merge(sources.base, sources.left, sources.right, /* exitOnError= */ false);
Cli.merge(sources.base, sources.left, sources.right, /* exitOnError= */ false, /* diff3 = */ false);

assertEquals(0, merge.getSecond());
assertEquals(expected, merge.getFirst());
Expand Down
12 changes: 10 additions & 2 deletions src/test/resources/conflict/add_similar_fields/ExpectedDiff3.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,17 @@ public class Main {
Integer a = 2;
Integer b = 3;
<<<<<<< LEFT
Object field = Integer.valueOf(2);
Object
||||||| BASE
=======
Integer field = Integer.valueOf(3);
Integer
>>>>>>> RIGHT
field = Integer.valueOf(
<<<<<<< LEFT
2
||||||| BASE
=======
3
>>>>>>> RIGHT
);
}

0 comments on commit 43e7b80

Please sign in to comment.