You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MergeAlgorithm.java 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. /*
  2. * Copyright (C) 2009, Christian Halstrick <christian.halstrick@sap.com> and others
  3. *
  4. * This program and the accompanying materials are made available under the
  5. * terms of the Eclipse Distribution License v. 1.0 which is available at
  6. * https://www.eclipse.org/org/documents/edl-v10.php.
  7. *
  8. * SPDX-License-Identifier: BSD-3-Clause
  9. */
  10. package org.eclipse.jgit.merge;
  11. import java.util.ArrayList;
  12. import java.util.Iterator;
  13. import java.util.List;
  14. import org.eclipse.jgit.diff.DiffAlgorithm;
  15. import org.eclipse.jgit.diff.Edit;
  16. import org.eclipse.jgit.diff.EditList;
  17. import org.eclipse.jgit.diff.HistogramDiff;
  18. import org.eclipse.jgit.diff.Sequence;
  19. import org.eclipse.jgit.diff.SequenceComparator;
  20. import org.eclipse.jgit.merge.MergeChunk.ConflictState;
  21. /**
  22. * Provides the merge algorithm which does a three-way merge on content provided
  23. * as RawText. By default {@link org.eclipse.jgit.diff.HistogramDiff} is used as
  24. * diff algorithm.
  25. */
  26. public final class MergeAlgorithm {
  27. private final DiffAlgorithm diffAlg;
  28. /**
  29. * Creates a new MergeAlgorithm which uses
  30. * {@link org.eclipse.jgit.diff.HistogramDiff} as diff algorithm
  31. */
  32. public MergeAlgorithm() {
  33. this(new HistogramDiff());
  34. }
  35. /**
  36. * Creates a new MergeAlgorithm
  37. *
  38. * @param diff
  39. * the diff algorithm used by this merge
  40. */
  41. public MergeAlgorithm(DiffAlgorithm diff) {
  42. this.diffAlg = diff;
  43. }
  44. // An special edit which acts as a sentinel value by marking the end the
  45. // list of edits
  46. private static final Edit END_EDIT = new Edit(Integer.MAX_VALUE,
  47. Integer.MAX_VALUE);
  48. @SuppressWarnings("ReferenceEquality")
  49. private static boolean isEndEdit(Edit edit) {
  50. return edit == END_EDIT;
  51. }
  52. /**
  53. * Does the three way merge between a common base and two sequences.
  54. *
  55. * @param cmp comparison method for this execution.
  56. * @param base the common base sequence
  57. * @param ours the first sequence to be merged
  58. * @param theirs the second sequence to be merged
  59. * @return the resulting content
  60. */
  61. public <S extends Sequence> MergeResult<S> merge(
  62. SequenceComparator<S> cmp, S base, S ours, S theirs) {
  63. List<S> sequences = new ArrayList<>(3);
  64. sequences.add(base);
  65. sequences.add(ours);
  66. sequences.add(theirs);
  67. MergeResult<S> result = new MergeResult<>(sequences);
  68. if (ours.size() == 0) {
  69. if (theirs.size() != 0) {
  70. EditList theirsEdits = diffAlg.diff(cmp, base, theirs);
  71. if (!theirsEdits.isEmpty()) {
  72. // we deleted, they modified -> Let their complete content
  73. // conflict with empty text
  74. result.add(1, 0, 0, ConflictState.FIRST_CONFLICTING_RANGE);
  75. result.add(2, 0, theirs.size(),
  76. ConflictState.NEXT_CONFLICTING_RANGE);
  77. } else
  78. // we deleted, they didn't modify -> Let our deletion win
  79. result.add(1, 0, 0, ConflictState.NO_CONFLICT);
  80. } else
  81. // we and they deleted -> return a single chunk of nothing
  82. result.add(1, 0, 0, ConflictState.NO_CONFLICT);
  83. return result;
  84. } else if (theirs.size() == 0) {
  85. EditList oursEdits = diffAlg.diff(cmp, base, ours);
  86. if (!oursEdits.isEmpty()) {
  87. // we modified, they deleted -> Let our complete content
  88. // conflict with empty text
  89. result.add(1, 0, ours.size(),
  90. ConflictState.FIRST_CONFLICTING_RANGE);
  91. result.add(2, 0, 0, ConflictState.NEXT_CONFLICTING_RANGE);
  92. } else
  93. // they deleted, we didn't modify -> Let their deletion win
  94. result.add(2, 0, 0, ConflictState.NO_CONFLICT);
  95. return result;
  96. }
  97. EditList oursEdits = diffAlg.diff(cmp, base, ours);
  98. Iterator<Edit> baseToOurs = oursEdits.iterator();
  99. EditList theirsEdits = diffAlg.diff(cmp, base, theirs);
  100. Iterator<Edit> baseToTheirs = theirsEdits.iterator();
  101. int current = 0; // points to the next line (first line is 0) of base
  102. // which was not handled yet
  103. Edit oursEdit = nextEdit(baseToOurs);
  104. Edit theirsEdit = nextEdit(baseToTheirs);
  105. // iterate over all edits from base to ours and from base to theirs
  106. // leave the loop when there are no edits more for ours or for theirs
  107. // (or both)
  108. while (!isEndEdit(theirsEdit) || !isEndEdit(oursEdit)) {
  109. if (oursEdit.getEndA() < theirsEdit.getBeginA()) {
  110. // something was changed in ours not overlapping with any change
  111. // from theirs. First add the common part in front of the edit
  112. // then the edit.
  113. if (current != oursEdit.getBeginA()) {
  114. result.add(0, current, oursEdit.getBeginA(),
  115. ConflictState.NO_CONFLICT);
  116. }
  117. result.add(1, oursEdit.getBeginB(), oursEdit.getEndB(),
  118. ConflictState.NO_CONFLICT);
  119. current = oursEdit.getEndA();
  120. oursEdit = nextEdit(baseToOurs);
  121. } else if (theirsEdit.getEndA() < oursEdit.getBeginA()) {
  122. // something was changed in theirs not overlapping with any
  123. // from ours. First add the common part in front of the edit
  124. // then the edit.
  125. if (current != theirsEdit.getBeginA()) {
  126. result.add(0, current, theirsEdit.getBeginA(),
  127. ConflictState.NO_CONFLICT);
  128. }
  129. result.add(2, theirsEdit.getBeginB(), theirsEdit.getEndB(),
  130. ConflictState.NO_CONFLICT);
  131. current = theirsEdit.getEndA();
  132. theirsEdit = nextEdit(baseToTheirs);
  133. } else {
  134. // here we found a real overlapping modification
  135. // if there is a common part in front of the conflict add it
  136. if (oursEdit.getBeginA() != current
  137. && theirsEdit.getBeginA() != current) {
  138. result.add(0, current, Math.min(oursEdit.getBeginA(),
  139. theirsEdit.getBeginA()), ConflictState.NO_CONFLICT);
  140. }
  141. // set some initial values for the ranges in A and B which we
  142. // want to handle
  143. int oursBeginB = oursEdit.getBeginB();
  144. int theirsBeginB = theirsEdit.getBeginB();
  145. // harmonize the start of the ranges in A and B
  146. if (oursEdit.getBeginA() < theirsEdit.getBeginA()) {
  147. theirsBeginB -= theirsEdit.getBeginA()
  148. - oursEdit.getBeginA();
  149. } else {
  150. oursBeginB -= oursEdit.getBeginA() - theirsEdit.getBeginA();
  151. }
  152. // combine edits:
  153. // Maybe an Edit on one side corresponds to multiple Edits on
  154. // the other side. Then we have to combine the Edits of the
  155. // other side - so in the end we can merge together two single
  156. // edits.
  157. //
  158. // It is important to notice that this combining will extend the
  159. // ranges of our conflict always downwards (towards the end of
  160. // the content). The starts of the conflicting ranges in ours
  161. // and theirs are not touched here.
  162. //
  163. // This combining is an iterative process: after we have
  164. // combined some edits we have to do the check again. The
  165. // combined edits could now correspond to multiple edits on the
  166. // other side.
  167. //
  168. // Example: when this combining algorithm works on the following
  169. // edits
  170. // oursEdits=((0-5,0-5),(6-8,6-8),(10-11,10-11)) and
  171. // theirsEdits=((0-1,0-1),(2-3,2-3),(5-7,5-7))
  172. // it will merge them into
  173. // oursEdits=((0-8,0-8),(10-11,10-11)) and
  174. // theirsEdits=((0-7,0-7))
  175. //
  176. // Since the only interesting thing to us is how in ours and
  177. // theirs the end of the conflicting range is changing we let
  178. // oursEdit and theirsEdit point to the last conflicting edit
  179. Edit nextOursEdit = nextEdit(baseToOurs);
  180. Edit nextTheirsEdit = nextEdit(baseToTheirs);
  181. for (;;) {
  182. if (oursEdit.getEndA() >= nextTheirsEdit.getBeginA()) {
  183. theirsEdit = nextTheirsEdit;
  184. nextTheirsEdit = nextEdit(baseToTheirs);
  185. } else if (theirsEdit.getEndA() >= nextOursEdit.getBeginA()) {
  186. oursEdit = nextOursEdit;
  187. nextOursEdit = nextEdit(baseToOurs);
  188. } else {
  189. break;
  190. }
  191. }
  192. // harmonize the end of the ranges in A and B
  193. int oursEndB = oursEdit.getEndB();
  194. int theirsEndB = theirsEdit.getEndB();
  195. if (oursEdit.getEndA() < theirsEdit.getEndA()) {
  196. oursEndB += theirsEdit.getEndA() - oursEdit.getEndA();
  197. } else {
  198. theirsEndB += oursEdit.getEndA() - theirsEdit.getEndA();
  199. }
  200. // A conflicting region is found. Strip off common lines in
  201. // in the beginning and the end of the conflicting region
  202. // Determine the minimum length of the conflicting areas in OURS
  203. // and THEIRS. Also determine how much bigger the conflicting
  204. // area in THEIRS is compared to OURS. All that is needed to
  205. // limit the search for common areas at the beginning or end
  206. // (the common areas cannot be bigger then the smaller
  207. // conflicting area. The delta is needed to know whether the
  208. // complete conflicting area is common in OURS and THEIRS.
  209. int minBSize = oursEndB - oursBeginB;
  210. int BSizeDelta = minBSize - (theirsEndB - theirsBeginB);
  211. if (BSizeDelta > 0)
  212. minBSize -= BSizeDelta;
  213. int commonPrefix = 0;
  214. while (commonPrefix < minBSize
  215. && cmp.equals(ours, oursBeginB + commonPrefix, theirs,
  216. theirsBeginB + commonPrefix))
  217. commonPrefix++;
  218. minBSize -= commonPrefix;
  219. int commonSuffix = 0;
  220. while (commonSuffix < minBSize
  221. && cmp.equals(ours, oursEndB - commonSuffix - 1, theirs,
  222. theirsEndB - commonSuffix - 1))
  223. commonSuffix++;
  224. minBSize -= commonSuffix;
  225. // Add the common lines at start of conflict
  226. if (commonPrefix > 0)
  227. result.add(1, oursBeginB, oursBeginB + commonPrefix,
  228. ConflictState.NO_CONFLICT);
  229. // Add the conflict (Only if there is a conflict left to report)
  230. if (minBSize > 0 || BSizeDelta != 0) {
  231. result.add(1, oursBeginB + commonPrefix, oursEndB
  232. - commonSuffix,
  233. ConflictState.FIRST_CONFLICTING_RANGE);
  234. result.add(2, theirsBeginB + commonPrefix, theirsEndB
  235. - commonSuffix,
  236. ConflictState.NEXT_CONFLICTING_RANGE);
  237. }
  238. // Add the common lines at end of conflict
  239. if (commonSuffix > 0)
  240. result.add(1, oursEndB - commonSuffix, oursEndB,
  241. ConflictState.NO_CONFLICT);
  242. current = Math.max(oursEdit.getEndA(), theirsEdit.getEndA());
  243. oursEdit = nextOursEdit;
  244. theirsEdit = nextTheirsEdit;
  245. }
  246. }
  247. // maybe we have a common part behind the last edit: copy it to the
  248. // result
  249. if (current < base.size()) {
  250. result.add(0, current, base.size(), ConflictState.NO_CONFLICT);
  251. }
  252. return result;
  253. }
  254. /**
  255. * Helper method which returns the next Edit for an Iterator over Edits.
  256. * When there are no more edits left this method will return the constant
  257. * END_EDIT.
  258. *
  259. * @param it
  260. * the iterator for which the next edit should be returned
  261. * @return the next edit from the iterator or END_EDIT if there no more
  262. * edits
  263. */
  264. private static Edit nextEdit(Iterator<Edit> it) {
  265. return (it.hasNext() ? it.next() : END_EDIT);
  266. }
  267. }