aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit
diff options
context:
space:
mode:
authorHaamed Gheibi <gheibi@gmail.com>2023-07-24 17:50:34 -0700
committerHaamed Gheibi <gheibi@gmail.com>2023-07-31 11:57:28 -0700
commit462c57ec8d94b23e21479394ee7880e0fc1bb832 (patch)
tree68fde3035945ae30cdc090658eec6549d99efab9 /org.eclipse.jgit
parent0f4af2bc3642b419b4e1b10b84624e9a3ed8a443 (diff)
downloadjgit-462c57ec8d94b23e21479394ee7880e0fc1bb832.tar.gz
jgit-462c57ec8d94b23e21479394ee7880e0fc1bb832.zip
Merge: Add diff3 style merge conflict formatter.
Add base section to the merge conflict hunks. Bug: 442284 Change-Id: I977b43e7dd8119d6b72d11f09c4e8ec241750383
Diffstat (limited to 'org.eclipse.jgit')
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeChunk.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java64
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatterPass.java72
4 files changed, 147 insertions, 15 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java
index abca813c11..b902492366 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java
@@ -127,6 +127,8 @@ public final class MergeAlgorithm {
// Let their complete content conflict with empty text
result.add(1, 0, 0,
ConflictState.FIRST_CONFLICTING_RANGE);
+ result.add(0, 0, base.size(),
+ ConflictState.BASE_CONFLICTING_RANGE);
result.add(2, 0, theirs.size(),
ConflictState.NEXT_CONFLICTING_RANGE);
break;
@@ -155,6 +157,8 @@ public final class MergeAlgorithm {
// Let our complete content conflict with empty text
result.add(1, 0, ours.size(),
ConflictState.FIRST_CONFLICTING_RANGE);
+ result.add(0, 0, base.size(),
+ ConflictState.BASE_CONFLICTING_RANGE);
result.add(2, 0, 0, ConflictState.NEXT_CONFLICTING_RANGE);
break;
}
@@ -324,6 +328,14 @@ public final class MergeAlgorithm {
result.add(1, oursBeginB + commonPrefix,
oursEndB - commonSuffix,
ConflictState.FIRST_CONFLICTING_RANGE);
+
+ int baseBegin = Math.min(oursBeginB, theirsBeginB)
+ + commonPrefix;
+ int baseEnd = Math.min(base.size(),
+ Math.max(oursEndB, theirsEndB)) - commonSuffix;
+ result.add(0, baseBegin, baseEnd,
+ ConflictState.BASE_CONFLICTING_RANGE);
+
result.add(2, theirsBeginB + commonPrefix,
theirsEndB - commonSuffix,
ConflictState.NEXT_CONFLICTING_RANGE);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeChunk.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeChunk.java
index ca998e30bc..7102aa6998 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeChunk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeChunk.java
@@ -29,14 +29,22 @@ public class MergeChunk {
NO_CONFLICT,
/**
- * This chunk does belong to a conflict and is the first one of the
+ * This chunk does belong to a conflict and is the ours section of the
* conflicting chunks
*/
FIRST_CONFLICTING_RANGE,
/**
- * This chunk does belong to a conflict but is not the first one of the
- * conflicting chunks. It's a subsequent one.
+ * This chunk does belong to a conflict and is the base section of the
+ * conflicting chunks
+ *
+ * @since 6.7
+ */
+ BASE_CONFLICTING_RANGE,
+
+ /**
+ * This chunk does belong to a conflict and is the theirs section of
+ * the conflicting chunks. It's a subsequent one.
*/
NEXT_CONFLICTING_RANGE
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java
index baf5d27714..a35b30eb01 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java
@@ -83,6 +83,35 @@ public class MergeFormatter {
}
/**
+ * Formats the results of a merge of {@link org.eclipse.jgit.diff.RawText}
+ * objects in a Git conformant way using diff3 style. This method also
+ * assumes that the {@link org.eclipse.jgit.diff.RawText} objects being
+ * merged are line oriented files which use LF as delimiter. This method
+ * will also use LF to separate chunks and conflict metadata, therefore it
+ * fits only to texts that are LF-separated lines.
+ *
+ * @param out
+ * the output stream where to write the textual presentation
+ * @param res
+ * the merge result which should be presented
+ * @param seqName
+ * When a conflict is reported each conflicting range will get a
+ * name. This name is following the "&lt;&lt;&lt;&lt;&lt;&lt;&lt;
+ * ", "|||||||" or "&gt;&gt;&gt;&gt;&gt;&gt;&gt; " conflict
+ * markers. The names for the sequences are given in this list
+ * @param charset
+ * the character set used when writing conflict metadata
+ * @throws java.io.IOException
+ * if an IO error occurred
+ * @since 6.7
+ */
+ public void formatMergeDiff3(OutputStream out,
+ MergeResult<RawText> res, List<String> seqName, Charset charset)
+ throws IOException {
+ new MergeFormatterPass(out, res, seqName, charset, true).formatMerge();
+ }
+
+ /**
* Formats the results of a merge of exactly two
* {@link org.eclipse.jgit.diff.RawText} objects in a Git conformant way.
* This convenience method accepts the names for the three sequences (base
@@ -150,4 +179,39 @@ public class MergeFormatter {
names.add(theirsName);
formatMerge(out, res, names, charset);
}
+
+ /**
+ * Formats the results of a merge of three
+ * {@link org.eclipse.jgit.diff.RawText} objects in a Git conformant way,
+ * using diff-3 style. This convenience method accepts the names for the
+ * three sequences (base and the two merged sequences) as explicit
+ * parameters and doesn't require the caller to specify a List
+ *
+ * @param out
+ * the {@link java.io.OutputStream} where to write the textual
+ * presentation
+ * @param res
+ * the merge result which should be presented
+ * @param baseName
+ * the name ranges from the base should get
+ * @param oursName
+ * the name ranges from ours should get
+ * @param theirsName
+ * the name ranges from theirs should get
+ * @param charset
+ * the character set used when writing conflict metadata
+ * @throws java.io.IOException
+ * if an IO error occurred
+ * @since 6.7
+ */
+ @SuppressWarnings("unchecked")
+ public void formatMergeDiff3(OutputStream out,
+ MergeResult res, String baseName, String oursName,
+ String theirsName, Charset charset) throws IOException {
+ List<String> names = new ArrayList<>(3);
+ names.add(baseName);
+ names.add(oursName);
+ names.add(theirsName);
+ formatMergeDiff3(out, res, names, charset);
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatterPass.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatterPass.java
index f09b343007..5e80b5fb23 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatterPass.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatterPass.java
@@ -31,6 +31,8 @@ class MergeFormatterPass {
private final boolean threeWayMerge;
+ private final boolean writeBase; // diff3-style requested
+
private String lastConflictingName; // is set to non-null whenever we are in
// a conflict
@@ -50,22 +52,47 @@ class MergeFormatterPass {
*/
MergeFormatterPass(OutputStream out, MergeResult<RawText> res,
List<String> seqName, Charset charset) {
+ this(out, res, seqName, charset, false);
+ }
+
+ /**
+ * @param out
+ * the {@link java.io.OutputStream} where to write the textual
+ * presentation
+ * @param res
+ * the merge result which should be presented
+ * @param seqName
+ * When a conflict is reported each conflicting range will get a
+ * name. This name is following the "&lt;&lt;&lt;&lt;&lt;&lt;&lt;
+ * ", "|||||||" or "&gt;&gt;&gt;&gt;&gt;&gt;&gt; " conflict
+ * markers. The names for the sequences are given in this list
+ * @param charset
+ * the character set used when writing conflict metadata
+ * @param writeBase
+ * base's contribution should be written in conflicts
+ */
+ MergeFormatterPass(OutputStream out, MergeResult<RawText> res,
+ List<String> seqName, Charset charset, boolean writeBase) {
this.out = new EolAwareOutputStream(out);
this.res = res;
this.seqName = seqName;
this.charset = charset;
this.threeWayMerge = (res.getSequences().size() == 3);
+ this.writeBase = writeBase;
}
void formatMerge() throws IOException {
boolean missingNewlineAtEnd = false;
for (MergeChunk chunk : res) {
- RawText seq = res.getSequences().get(chunk.getSequenceIndex());
- writeConflictMetadata(chunk);
- // the lines with conflict-metadata are written. Now write the chunk
- for (int i = chunk.getBegin(); i < chunk.getEnd(); i++)
- writeLine(seq, i);
- missingNewlineAtEnd = seq.isMissingNewlineAtEnd();
+ if (!isBase(chunk) || writeBase) {
+ RawText seq = res.getSequences().get(chunk.getSequenceIndex());
+ writeConflictMetadata(chunk);
+ // the lines with conflict-metadata are written. Now write the
+ // chunk
+ for (int i = chunk.getBegin(); i < chunk.getEnd(); i++)
+ writeLine(seq, i);
+ missingNewlineAtEnd = seq.isMissingNewlineAtEnd();
+ }
}
// one possible leftover: if the merge result ended with a conflict we
// have to close the last conflict here
@@ -77,16 +104,19 @@ class MergeFormatterPass {
private void writeConflictMetadata(MergeChunk chunk) throws IOException {
if (lastConflictingName != null
- && chunk.getConflictState() != ConflictState.NEXT_CONFLICTING_RANGE) {
- // found the end of an conflict
+ && !isTheirs(chunk) && !isBase(chunk)) {
+ // found the end of a conflict
writeConflictEnd();
}
- if (chunk.getConflictState() == ConflictState.FIRST_CONFLICTING_RANGE) {
- // found the start of an conflict
+ if (isOurs(chunk)) {
+ // found the start of a conflict
writeConflictStart(chunk);
- } else if (chunk.getConflictState() == ConflictState.NEXT_CONFLICTING_RANGE) {
- // found another conflicting chunk
+ } else if (isTheirs(chunk)) {
+ // found the theirs conflicting chunk
writeConflictChange(chunk);
+ } else if (isBase(chunk)) {
+ // found the base conflicting chunk
+ writeConflictBase(chunk);
}
}
@@ -113,6 +143,11 @@ class MergeFormatterPass {
+ lastConflictingName);
}
+ private void writeConflictBase(MergeChunk chunk) throws IOException {
+ lastConflictingName = seqName.get(chunk.getSequenceIndex());
+ writeln("||||||| " + lastConflictingName); //$NON-NLS-1$
+ }
+
private void writeln(String s) throws IOException {
out.beginln();
out.write((s + "\n").getBytes(charset)); //$NON-NLS-1$
@@ -125,4 +160,17 @@ class MergeFormatterPass {
if (out.isBeginln())
out.write('\n');
}
+
+ private boolean isBase(MergeChunk chunk) {
+ return chunk.getConflictState() == ConflictState.BASE_CONFLICTING_RANGE;
+ }
+
+ private boolean isOurs(MergeChunk chunk) {
+ return chunk
+ .getConflictState() == ConflictState.FIRST_CONFLICTING_RANGE;
+ }
+
+ private boolean isTheirs(MergeChunk chunk) {
+ return chunk.getConflictState() == ConflictState.NEXT_CONFLICTING_RANGE;
+ }
}