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.

Merger.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. /*
  2. * Copyright (C) 2008-2013, Google Inc.
  3. * Copyright (C) 2016, Laurent Delaigue <laurent.delaigue@obeo.fr> and others
  4. *
  5. * This program and the accompanying materials are made available under the
  6. * terms of the Eclipse Distribution License v. 1.0 which is available at
  7. * https://www.eclipse.org/org/documents/edl-v10.php.
  8. *
  9. * SPDX-License-Identifier: BSD-3-Clause
  10. */
  11. package org.eclipse.jgit.merge;
  12. import java.io.IOException;
  13. import java.text.MessageFormat;
  14. import org.eclipse.jgit.annotations.Nullable;
  15. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  16. import org.eclipse.jgit.errors.NoMergeBaseException;
  17. import org.eclipse.jgit.errors.NoMergeBaseException.MergeBaseFailureReason;
  18. import org.eclipse.jgit.internal.JGitText;
  19. import org.eclipse.jgit.lib.AnyObjectId;
  20. import org.eclipse.jgit.lib.NullProgressMonitor;
  21. import org.eclipse.jgit.lib.ObjectId;
  22. import org.eclipse.jgit.lib.ObjectInserter;
  23. import org.eclipse.jgit.lib.ObjectReader;
  24. import org.eclipse.jgit.lib.ProgressMonitor;
  25. import org.eclipse.jgit.lib.Repository;
  26. import org.eclipse.jgit.revwalk.RevCommit;
  27. import org.eclipse.jgit.revwalk.RevObject;
  28. import org.eclipse.jgit.revwalk.RevTree;
  29. import org.eclipse.jgit.revwalk.RevWalk;
  30. import org.eclipse.jgit.revwalk.filter.RevFilter;
  31. import org.eclipse.jgit.treewalk.AbstractTreeIterator;
  32. import org.eclipse.jgit.treewalk.CanonicalTreeParser;
  33. /**
  34. * Instance of a specific {@link org.eclipse.jgit.merge.MergeStrategy} for a
  35. * single {@link org.eclipse.jgit.lib.Repository}.
  36. */
  37. public abstract class Merger {
  38. /**
  39. * The repository this merger operates on.
  40. * <p>
  41. * Null if and only if the merger was constructed with {@link
  42. * #Merger(ObjectInserter)}. Callers that want to assume the repo is not null
  43. * (e.g. because of a previous check that the merger is not in-core) may use
  44. * {@link #nonNullRepo()}.
  45. */
  46. @Nullable
  47. protected final Repository db;
  48. /** Reader to support {@link #walk} and other object loading. */
  49. protected ObjectReader reader;
  50. /** A RevWalk for computing merge bases, or listing incoming commits. */
  51. protected RevWalk walk;
  52. private ObjectInserter inserter;
  53. /** The original objects supplied in the merge; this can be any tree-ish. */
  54. protected RevObject[] sourceObjects;
  55. /** If {@link #sourceObjects}[i] is a commit, this is the commit. */
  56. protected RevCommit[] sourceCommits;
  57. /** The trees matching every entry in {@link #sourceObjects}. */
  58. protected RevTree[] sourceTrees;
  59. /**
  60. * A progress monitor.
  61. *
  62. * @since 4.2
  63. */
  64. protected ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
  65. /**
  66. * Create a new merge instance for a repository.
  67. *
  68. * @param local
  69. * the repository this merger will read and write data on.
  70. */
  71. protected Merger(Repository local) {
  72. if (local == null) {
  73. throw new NullPointerException(JGitText.get().repositoryIsRequired);
  74. }
  75. db = local;
  76. inserter = local.newObjectInserter();
  77. reader = inserter.newReader();
  78. walk = new RevWalk(reader);
  79. }
  80. /**
  81. * Create a new in-core merge instance from an inserter.
  82. *
  83. * @param oi
  84. * the inserter to write objects to. Will be closed at the
  85. * conclusion of {@code merge}, unless {@code flush} is false.
  86. * @since 4.8
  87. */
  88. protected Merger(ObjectInserter oi) {
  89. db = null;
  90. inserter = oi;
  91. reader = oi.newReader();
  92. walk = new RevWalk(reader);
  93. }
  94. /**
  95. * Get the repository this merger operates on.
  96. *
  97. * @return the repository this merger operates on.
  98. */
  99. @Nullable
  100. public Repository getRepository() {
  101. return db;
  102. }
  103. /**
  104. * Get non-null repository instance
  105. *
  106. * @return non-null repository instance
  107. * @throws java.lang.NullPointerException
  108. * if the merger was constructed without a repository.
  109. * @since 4.8
  110. */
  111. protected Repository nonNullRepo() {
  112. if (db == null) {
  113. throw new NullPointerException(JGitText.get().repositoryIsRequired);
  114. }
  115. return db;
  116. }
  117. /**
  118. * Get an object writer to create objects, writing objects to
  119. * {@link #getRepository()}
  120. *
  121. * @return an object writer to create objects, writing objects to
  122. * {@link #getRepository()} (if a repository was provided).
  123. */
  124. public ObjectInserter getObjectInserter() {
  125. return inserter;
  126. }
  127. /**
  128. * Set the inserter this merger will use to create objects.
  129. * <p>
  130. * If an inserter was already set on this instance (such as by a prior set,
  131. * or a prior call to {@link #getObjectInserter()}), the prior inserter as
  132. * well as the in-progress walk will be released.
  133. *
  134. * @param oi
  135. * the inserter instance to use. Must be associated with the
  136. * repository instance returned by {@link #getRepository()} (if a
  137. * repository was provided). Will be closed at the conclusion of
  138. * {@code merge}, unless {@code flush} is false.
  139. */
  140. public void setObjectInserter(ObjectInserter oi) {
  141. walk.close();
  142. reader.close();
  143. inserter.close();
  144. inserter = oi;
  145. reader = oi.newReader();
  146. walk = new RevWalk(reader);
  147. }
  148. /**
  149. * Merge together two or more tree-ish objects.
  150. * <p>
  151. * Any tree-ish may be supplied as inputs. Commits and/or tags pointing at
  152. * trees or commits may be passed as input objects.
  153. *
  154. * @param tips
  155. * source trees to be combined together. The merge base is not
  156. * included in this set.
  157. * @return true if the merge was completed without conflicts; false if the
  158. * merge strategy cannot handle this merge or there were conflicts
  159. * preventing it from automatically resolving all paths.
  160. * @throws IncorrectObjectTypeException
  161. * one of the input objects is not a commit, but the strategy
  162. * requires it to be a commit.
  163. * @throws java.io.IOException
  164. * one or more sources could not be read, or outputs could not
  165. * be written to the Repository.
  166. */
  167. public boolean merge(AnyObjectId... tips) throws IOException {
  168. return merge(true, tips);
  169. }
  170. /**
  171. * Merge together two or more tree-ish objects.
  172. * <p>
  173. * Any tree-ish may be supplied as inputs. Commits and/or tags pointing at
  174. * trees or commits may be passed as input objects.
  175. *
  176. * @since 3.5
  177. * @param flush
  178. * whether to flush and close the underlying object inserter when
  179. * finished to store any content-merged blobs and virtual merged
  180. * bases; if false, callers are responsible for flushing.
  181. * @param tips
  182. * source trees to be combined together. The merge base is not
  183. * included in this set.
  184. * @return true if the merge was completed without conflicts; false if the
  185. * merge strategy cannot handle this merge or there were conflicts
  186. * preventing it from automatically resolving all paths.
  187. * @throws IncorrectObjectTypeException
  188. * one of the input objects is not a commit, but the strategy
  189. * requires it to be a commit.
  190. * @throws java.io.IOException
  191. * one or more sources could not be read, or outputs could not
  192. * be written to the Repository.
  193. */
  194. public boolean merge(boolean flush, AnyObjectId... tips)
  195. throws IOException {
  196. sourceObjects = new RevObject[tips.length];
  197. for (int i = 0; i < tips.length; i++)
  198. sourceObjects[i] = walk.parseAny(tips[i]);
  199. sourceCommits = new RevCommit[sourceObjects.length];
  200. for (int i = 0; i < sourceObjects.length; i++) {
  201. try {
  202. sourceCommits[i] = walk.parseCommit(sourceObjects[i]);
  203. } catch (IncorrectObjectTypeException err) {
  204. sourceCommits[i] = null;
  205. }
  206. }
  207. sourceTrees = new RevTree[sourceObjects.length];
  208. for (int i = 0; i < sourceObjects.length; i++)
  209. sourceTrees[i] = walk.parseTree(sourceObjects[i]);
  210. try {
  211. boolean ok = mergeImpl();
  212. if (ok && flush)
  213. inserter.flush();
  214. return ok;
  215. } finally {
  216. if (flush)
  217. inserter.close();
  218. reader.close();
  219. }
  220. }
  221. /**
  222. * Get the ID of the commit that was used as merge base for merging
  223. *
  224. * @return the ID of the commit that was used as merge base for merging, or
  225. * null if no merge base was used or it was set manually
  226. * @since 3.2
  227. */
  228. public abstract ObjectId getBaseCommitId();
  229. /**
  230. * Return the merge base of two commits.
  231. *
  232. * @param a
  233. * the first commit in {@link #sourceObjects}.
  234. * @param b
  235. * the second commit in {@link #sourceObjects}.
  236. * @return the merge base of two commits
  237. * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  238. * one of the input objects is not a commit.
  239. * @throws java.io.IOException
  240. * objects are missing or multiple merge bases were found.
  241. * @since 3.0
  242. */
  243. protected RevCommit getBaseCommit(RevCommit a, RevCommit b)
  244. throws IncorrectObjectTypeException, IOException {
  245. walk.reset();
  246. walk.setRevFilter(RevFilter.MERGE_BASE);
  247. walk.markStart(a);
  248. walk.markStart(b);
  249. final RevCommit base = walk.next();
  250. if (base == null)
  251. return null;
  252. final RevCommit base2 = walk.next();
  253. if (base2 != null) {
  254. throw new NoMergeBaseException(
  255. MergeBaseFailureReason.MULTIPLE_MERGE_BASES_NOT_SUPPORTED,
  256. MessageFormat.format(
  257. JGitText.get().multipleMergeBasesFor, a.name(), b.name(),
  258. base.name(), base2.name()));
  259. }
  260. return base;
  261. }
  262. /**
  263. * Open an iterator over a tree.
  264. *
  265. * @param treeId
  266. * the tree to scan; must be a tree (not a treeish).
  267. * @return an iterator for the tree.
  268. * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  269. * the input object is not a tree.
  270. * @throws java.io.IOException
  271. * the tree object is not found or cannot be read.
  272. */
  273. protected AbstractTreeIterator openTree(AnyObjectId treeId)
  274. throws IncorrectObjectTypeException, IOException {
  275. return new CanonicalTreeParser(null, reader, treeId);
  276. }
  277. /**
  278. * Execute the merge.
  279. * <p>
  280. * This method is called from {@link #merge(AnyObjectId[])} after the
  281. * {@link #sourceObjects}, {@link #sourceCommits} and {@link #sourceTrees}
  282. * have been populated.
  283. *
  284. * @return true if the merge was completed without conflicts; false if the
  285. * merge strategy cannot handle this merge or there were conflicts
  286. * preventing it from automatically resolving all paths.
  287. * @throws IncorrectObjectTypeException
  288. * one of the input objects is not a commit, but the strategy
  289. * requires it to be a commit.
  290. * @throws java.io.IOException
  291. * one or more sources could not be read, or outputs could not
  292. * be written to the Repository.
  293. */
  294. protected abstract boolean mergeImpl() throws IOException;
  295. /**
  296. * Get resulting tree.
  297. *
  298. * @return resulting tree, if {@link #merge(AnyObjectId[])} returned true.
  299. */
  300. public abstract ObjectId getResultTreeId();
  301. /**
  302. * Set a progress monitor.
  303. *
  304. * @param monitor
  305. * Monitor to use, can be null to indicate no progress reporting
  306. * is desired.
  307. * @since 4.2
  308. */
  309. public void setProgressMonitor(ProgressMonitor monitor) {
  310. if (monitor == null) {
  311. this.monitor = NullProgressMonitor.INSTANCE;
  312. } else {
  313. this.monitor = monitor;
  314. }
  315. }
  316. }