diff options
author | Haamed Gheibi <gheibi@gmail.com> | 2023-07-24 17:50:34 -0700 |
---|---|---|
committer | Haamed Gheibi <gheibi@gmail.com> | 2023-07-31 11:57:28 -0700 |
commit | 462c57ec8d94b23e21479394ee7880e0fc1bb832 (patch) | |
tree | 68fde3035945ae30cdc090658eec6549d99efab9 /org.eclipse.jgit | |
parent | 0f4af2bc3642b419b4e1b10b84624e9a3ed8a443 (diff) | |
download | jgit-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')
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 "<<<<<<< + * ", "|||||||" or ">>>>>>> " 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 "<<<<<<< + * ", "|||||||" or ">>>>>>> " 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; + } } |