Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

ResolveMerger.java 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. /*
  2. * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>,
  3. * Copyright (C) 2010, Matthias Sohn <matthias.sohn@sap.com>
  4. * and other copyright owners as documented in the project's IP log.
  5. *
  6. * This program and the accompanying materials are made available
  7. * under the terms of the Eclipse Distribution License v1.0 which
  8. * accompanies this distribution, is reproduced below, and is
  9. * available at http://www.eclipse.org/org/documents/edl-v10.php
  10. *
  11. * All rights reserved.
  12. *
  13. * Redistribution and use in source and binary forms, with or
  14. * without modification, are permitted provided that the following
  15. * conditions are met:
  16. *
  17. * - Redistributions of source code must retain the above copyright
  18. * notice, this list of conditions and the following disclaimer.
  19. *
  20. * - Redistributions in binary form must reproduce the above
  21. * copyright notice, this list of conditions and the following
  22. * disclaimer in the documentation and/or other materials provided
  23. * with the distribution.
  24. *
  25. * - Neither the name of the Eclipse Foundation, Inc. nor the
  26. * names of its contributors may be used to endorse or promote
  27. * products derived from this software without specific prior
  28. * written permission.
  29. *
  30. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  31. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  32. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  33. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  34. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  35. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  36. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  37. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  38. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  39. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  40. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  41. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  42. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  43. */
  44. package org.eclipse.jgit.merge;
  45. import java.io.File;
  46. import java.io.FileInputStream;
  47. import java.io.FileNotFoundException;
  48. import java.io.FileOutputStream;
  49. import java.io.IOException;
  50. import java.io.InputStream;
  51. import java.util.ArrayList;
  52. import java.util.Arrays;
  53. import java.util.HashMap;
  54. import java.util.Iterator;
  55. import java.util.LinkedList;
  56. import java.util.List;
  57. import java.util.Map;
  58. import org.eclipse.jgit.JGitText;
  59. import org.eclipse.jgit.diff.DiffAlgorithm;
  60. import org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm;
  61. import org.eclipse.jgit.diff.RawText;
  62. import org.eclipse.jgit.diff.RawTextComparator;
  63. import org.eclipse.jgit.diff.Sequence;
  64. import org.eclipse.jgit.dircache.DirCache;
  65. import org.eclipse.jgit.dircache.DirCacheBuildIterator;
  66. import org.eclipse.jgit.dircache.DirCacheBuilder;
  67. import org.eclipse.jgit.dircache.DirCacheCheckout;
  68. import org.eclipse.jgit.dircache.DirCacheEntry;
  69. import org.eclipse.jgit.errors.CorruptObjectException;
  70. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  71. import org.eclipse.jgit.errors.IndexWriteException;
  72. import org.eclipse.jgit.errors.MissingObjectException;
  73. import org.eclipse.jgit.errors.NoWorkTreeException;
  74. import org.eclipse.jgit.lib.ConfigConstants;
  75. import org.eclipse.jgit.lib.Constants;
  76. import org.eclipse.jgit.lib.FileMode;
  77. import org.eclipse.jgit.lib.ObjectId;
  78. import org.eclipse.jgit.lib.ObjectInserter;
  79. import org.eclipse.jgit.lib.ObjectReader;
  80. import org.eclipse.jgit.lib.Repository;
  81. import org.eclipse.jgit.treewalk.CanonicalTreeParser;
  82. import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
  83. import org.eclipse.jgit.treewalk.WorkingTreeIterator;
  84. import org.eclipse.jgit.util.FileUtils;
  85. /**
  86. * A three-way merger performing a content-merge if necessary
  87. */
  88. public class ResolveMerger extends ThreeWayMerger {
  89. /**
  90. * If the merge fails (means: not stopped because of unresolved conflicts)
  91. * this enum is used to explain why it failed
  92. */
  93. public enum MergeFailureReason {
  94. /** the merge failed because of a dirty index */
  95. DIRTY_INDEX,
  96. /** the merge failed because of a dirty workingtree */
  97. DIRTY_WORKTREE,
  98. /** the merge failed because of a file could not be deleted */
  99. COULD_NOT_DELETE
  100. }
  101. private NameConflictTreeWalk tw;
  102. private String commitNames[];
  103. private static final int T_BASE = 0;
  104. private static final int T_OURS = 1;
  105. private static final int T_THEIRS = 2;
  106. private static final int T_INDEX = 3;
  107. private static final int T_FILE = 4;
  108. private DirCacheBuilder builder;
  109. private ObjectId resultTree;
  110. private List<String> unmergedPaths = new ArrayList<String>();
  111. private List<String> modifiedFiles = new LinkedList<String>();
  112. private Map<String, DirCacheEntry> toBeCheckedOut = new HashMap<String, DirCacheEntry>();
  113. private Map<String, MergeResult<? extends Sequence>> mergeResults = new HashMap<String, MergeResult<? extends Sequence>>();
  114. private Map<String, MergeFailureReason> failingPaths = new HashMap<String, MergeFailureReason>();
  115. private ObjectInserter oi;
  116. private boolean enterSubtree;
  117. private boolean inCore;
  118. private DirCache dircache;
  119. private WorkingTreeIterator workingTreeIterator;
  120. private MergeAlgorithm mergeAlgorithm;
  121. /**
  122. * @param local
  123. * @param inCore
  124. */
  125. protected ResolveMerger(Repository local, boolean inCore) {
  126. super(local);
  127. SupportedAlgorithm diffAlg = local.getConfig().getEnum(
  128. ConfigConstants.CONFIG_DIFF_SECTION, null,
  129. ConfigConstants.CONFIG_KEY_ALGORITHM,
  130. SupportedAlgorithm.HISTOGRAM);
  131. mergeAlgorithm = new MergeAlgorithm(DiffAlgorithm.getAlgorithm(diffAlg));
  132. commitNames = new String[] { "BASE", "OURS", "THEIRS" };
  133. oi = getObjectInserter();
  134. this.inCore = inCore;
  135. if (inCore) {
  136. dircache = DirCache.newInCore();
  137. }
  138. }
  139. /**
  140. * @param local
  141. */
  142. protected ResolveMerger(Repository local) {
  143. this(local, false);
  144. }
  145. @Override
  146. protected boolean mergeImpl() throws IOException {
  147. boolean implicitDirCache = false;
  148. if (dircache == null) {
  149. dircache = getRepository().lockDirCache();
  150. implicitDirCache = true;
  151. }
  152. try {
  153. builder = dircache.builder();
  154. DirCacheBuildIterator buildIt = new DirCacheBuildIterator(builder);
  155. tw = new NameConflictTreeWalk(db);
  156. tw.addTree(mergeBase());
  157. tw.addTree(sourceTrees[0]);
  158. tw.addTree(sourceTrees[1]);
  159. tw.addTree(buildIt);
  160. if (workingTreeIterator != null)
  161. tw.addTree(workingTreeIterator);
  162. while (tw.next()) {
  163. if (!processEntry(
  164. tw.getTree(T_BASE, CanonicalTreeParser.class),
  165. tw.getTree(T_OURS, CanonicalTreeParser.class),
  166. tw.getTree(T_THEIRS, CanonicalTreeParser.class),
  167. tw.getTree(T_INDEX, DirCacheBuildIterator.class),
  168. (workingTreeIterator == null) ? null : tw.getTree(T_FILE, WorkingTreeIterator.class))) {
  169. cleanUp();
  170. return false;
  171. }
  172. if (tw.isSubtree() && enterSubtree)
  173. tw.enterSubtree();
  174. }
  175. if (!inCore) {
  176. // All content-merges are successfully done. If we can now write the
  177. // new index we are on quite safe ground. Even if the checkout of
  178. // files coming from "theirs" fails the user can work around such
  179. // failures by checking out the index again.
  180. if (!builder.commit()) {
  181. cleanUp();
  182. throw new IndexWriteException();
  183. }
  184. builder = null;
  185. // No problem found. The only thing left to be done is to checkout
  186. // all files from "theirs" which have been selected to go into the
  187. // new index.
  188. checkout();
  189. } else {
  190. builder.finish();
  191. builder = null;
  192. }
  193. if (getUnmergedPaths().isEmpty()) {
  194. resultTree = dircache.writeTree(oi);
  195. return true;
  196. } else {
  197. resultTree = null;
  198. return false;
  199. }
  200. } finally {
  201. if (implicitDirCache)
  202. dircache.unlock();
  203. }
  204. }
  205. private void checkout() throws NoWorkTreeException, IOException {
  206. for (Map.Entry<String, DirCacheEntry> entry : toBeCheckedOut.entrySet()) {
  207. File f = new File(db.getWorkTree(), entry.getKey());
  208. if (entry.getValue() != null) {
  209. createDir(f.getParentFile());
  210. DirCacheCheckout.checkoutEntry(db,
  211. f,
  212. entry.getValue());
  213. } else {
  214. if (!f.delete())
  215. failingPaths.put(entry.getKey(),
  216. MergeFailureReason.COULD_NOT_DELETE);
  217. }
  218. modifiedFiles.add(entry.getKey());
  219. }
  220. }
  221. private void createDir(File f) throws IOException {
  222. if (!f.isDirectory() && !f.mkdirs()) {
  223. File p = f;
  224. while (p != null && !p.exists())
  225. p = p.getParentFile();
  226. if (p == null || p.isDirectory())
  227. throw new IOException(JGitText.get().cannotCreateDirectory);
  228. FileUtils.delete(p);
  229. if (!f.mkdirs())
  230. throw new IOException(JGitText.get().cannotCreateDirectory);
  231. }
  232. }
  233. /**
  234. * Reverts the worktree after an unsuccessful merge. We know that for all
  235. * modified files the old content was in the old index and the index
  236. * contained only stage 0. In case if inCore operation just clear
  237. * the history of modified files.
  238. *
  239. * @throws IOException
  240. * @throws CorruptObjectException
  241. * @throws NoWorkTreeException
  242. */
  243. private void cleanUp() throws NoWorkTreeException, CorruptObjectException, IOException {
  244. if (inCore) {
  245. modifiedFiles.clear();
  246. return;
  247. }
  248. DirCache dc = db.readDirCache();
  249. ObjectReader or = db.getObjectDatabase().newReader();
  250. Iterator<String> mpathsIt=modifiedFiles.iterator();
  251. while(mpathsIt.hasNext()) {
  252. String mpath=mpathsIt.next();
  253. DirCacheEntry entry = dc.getEntry(mpath);
  254. FileOutputStream fos = new FileOutputStream(new File(db.getWorkTree(), mpath));
  255. try {
  256. or.open(entry.getObjectId()).copyTo(fos);
  257. } finally {
  258. fos.close();
  259. }
  260. mpathsIt.remove();
  261. }
  262. }
  263. /**
  264. * adds a new path with the specified stage to the index builder
  265. *
  266. * @param path
  267. * @param p
  268. * @param stage
  269. * @return the entry which was added to the index
  270. */
  271. private DirCacheEntry add(byte[] path, CanonicalTreeParser p, int stage) {
  272. if (p != null && !p.getEntryFileMode().equals(FileMode.TREE)) {
  273. DirCacheEntry e = new DirCacheEntry(path, stage);
  274. e.setFileMode(p.getEntryFileMode());
  275. e.setObjectId(p.getEntryObjectId());
  276. builder.add(e);
  277. return e;
  278. }
  279. return null;
  280. }
  281. /**
  282. * Processes one path and tries to merge. This method will do all do all
  283. * trivial (not content) merges and will also detect if a merge will fail.
  284. * The merge will fail when one of the following is true
  285. * <ul>
  286. * <li>the index entry does not match the entry in ours. When merging one
  287. * branch into the current HEAD, ours will point to HEAD and theirs will
  288. * point to the other branch. It is assumed that the index matches the HEAD
  289. * because it will only not match HEAD if it was populated before the merge
  290. * operation. But the merge commit should not accidentally contain
  291. * modifications done before the merge. Check the <a href=
  292. * "http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html#_3_way_merge"
  293. * >git read-tree</a> documentation for further explanations.</li>
  294. * <li>A conflict was detected and the working-tree file is dirty. When a
  295. * conflict is detected the content-merge algorithm will try to write a
  296. * merged version into the working-tree. If the file is dirty we would
  297. * override unsaved data.</li>
  298. *
  299. * @param base
  300. * the common base for ours and theirs
  301. * @param ours
  302. * the ours side of the merge. When merging a branch into the
  303. * HEAD ours will point to HEAD
  304. * @param theirs
  305. * the theirs side of the merge. When merging a branch into the
  306. * current HEAD theirs will point to the branch which is merged
  307. * into HEAD.
  308. * @param index
  309. * the index entry
  310. * @param work
  311. * the file in the working tree
  312. * @return <code>false</code> if the merge will fail because the index entry
  313. * didn't match ours or the working-dir file was dirty and a
  314. * conflict occurred
  315. * @throws MissingObjectException
  316. * @throws IncorrectObjectTypeException
  317. * @throws CorruptObjectException
  318. * @throws IOException
  319. */
  320. private boolean processEntry(CanonicalTreeParser base,
  321. CanonicalTreeParser ours, CanonicalTreeParser theirs,
  322. DirCacheBuildIterator index, WorkingTreeIterator work)
  323. throws MissingObjectException, IncorrectObjectTypeException,
  324. CorruptObjectException, IOException {
  325. enterSubtree = true;
  326. final int modeO = tw.getRawMode(T_OURS);
  327. final int modeT = tw.getRawMode(T_THEIRS);
  328. final int modeB = tw.getRawMode(T_BASE);
  329. if (modeO == 0 && modeT == 0 && modeB == 0)
  330. // File is either untracked or new, staged but uncommitted
  331. return true;
  332. if (isIndexDirty())
  333. return false;
  334. if (nonTree(modeO) && modeO == modeT && tw.idEqual(T_OURS, T_THEIRS)) {
  335. // OURS and THEIRS are equal: it doesn't matter which one we choose.
  336. // OURS is chosen.
  337. add(tw.getRawPath(), ours, DirCacheEntry.STAGE_0);
  338. // no checkout needed!
  339. return true;
  340. }
  341. if (nonTree(modeO) && modeB == modeT && tw.idEqual(T_BASE, T_THEIRS)) {
  342. // THEIRS was not changed compared to BASE. All changes must be in
  343. // OURS. OURS is chosen.
  344. add(tw.getRawPath(), ours, DirCacheEntry.STAGE_0);
  345. // no checkout needed!
  346. return true;
  347. }
  348. if (modeB == modeO && tw.idEqual(T_BASE, T_OURS)) {
  349. // OURS was not changed compared to BASE. All changes must be in
  350. // THEIRS. THEIRS is chosen.
  351. // Check worktree before checking out THEIRS
  352. if (isWorktreeDirty())
  353. return false;
  354. if (nonTree(modeT)) {
  355. DirCacheEntry e = add(tw.getRawPath(), theirs,
  356. DirCacheEntry.STAGE_0);
  357. if (e != null)
  358. toBeCheckedOut.put(tw.getPathString(), e);
  359. return true;
  360. } else if (modeT == 0 && modeB != 0) {
  361. // we want THEIRS ... but THEIRS contains the deletion of the
  362. // file
  363. toBeCheckedOut.put(tw.getPathString(), null);
  364. return true;
  365. }
  366. }
  367. if (tw.isSubtree()) {
  368. // file/folder conflicts: here I want to detect only file/folder
  369. // conflict between ours and theirs. file/folder conflicts between
  370. // base/index/workingTree and something else are not relevant or
  371. // detected later
  372. if (nonTree(modeO) && !nonTree(modeT)) {
  373. if (nonTree(modeB))
  374. add(tw.getRawPath(), base, DirCacheEntry.STAGE_1);
  375. add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2);
  376. unmergedPaths.add(tw.getPathString());
  377. enterSubtree = false;
  378. return true;
  379. }
  380. if (nonTree(modeT) && !nonTree(modeO)) {
  381. if (nonTree(modeB))
  382. add(tw.getRawPath(), base, DirCacheEntry.STAGE_1);
  383. add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3);
  384. unmergedPaths.add(tw.getPathString());
  385. enterSubtree = false;
  386. return true;
  387. }
  388. // ours and theirs are both folders or both files (and treewalk
  389. // tells us we are in a subtree because of index or working-dir).
  390. // If they are both folders no content-merge is required - we can
  391. // return here.
  392. if (!nonTree(modeO))
  393. return true;
  394. // ours and theirs are both files, just fall out of the if block
  395. // and do the content merge
  396. }
  397. if (nonTree(modeO) && nonTree(modeT)) {
  398. // Check worktree before modifying files
  399. if (isWorktreeDirty())
  400. return false;
  401. if (!contentMerge(base, ours, theirs)) {
  402. unmergedPaths.add(tw.getPathString());
  403. }
  404. modifiedFiles.add(tw.getPathString());
  405. }
  406. return true;
  407. }
  408. private boolean isIndexDirty() {
  409. final int modeI = tw.getRawMode(T_INDEX);
  410. final int modeO = tw.getRawMode(T_OURS);
  411. // Index entry has to match ours to be considered clean
  412. final boolean isDirty = nonTree(modeI)
  413. && !(tw.idEqual(T_INDEX, T_OURS) && modeO == modeI);
  414. if (isDirty)
  415. failingPaths
  416. .put(tw.getPathString(), MergeFailureReason.DIRTY_INDEX);
  417. return isDirty;
  418. }
  419. private boolean isWorktreeDirty() {
  420. if (inCore)
  421. return false;
  422. final int modeF = tw.getRawMode(T_FILE);
  423. final int modeO = tw.getRawMode(T_OURS);
  424. // Worktree entry has to match ours to be considered clean
  425. final boolean isDirty = nonTree(modeF)
  426. && !(tw.idEqual(T_FILE, T_OURS) && modeO == modeF);
  427. if (isDirty)
  428. failingPaths.put(tw.getPathString(),
  429. MergeFailureReason.DIRTY_WORKTREE);
  430. return isDirty;
  431. }
  432. private boolean contentMerge(CanonicalTreeParser base,
  433. CanonicalTreeParser ours, CanonicalTreeParser theirs)
  434. throws FileNotFoundException, IllegalStateException, IOException {
  435. MergeFormatter fmt = new MergeFormatter();
  436. RawText baseText = base == null ? RawText.EMPTY_TEXT : getRawText(
  437. base.getEntryObjectId(), db);
  438. // do the merge
  439. MergeResult<RawText> result = mergeAlgorithm.merge(
  440. RawTextComparator.DEFAULT, baseText,
  441. getRawText(ours.getEntryObjectId(), db),
  442. getRawText(theirs.getEntryObjectId(), db));
  443. File of = null;
  444. FileOutputStream fos;
  445. if (!inCore) {
  446. File workTree = db.getWorkTree();
  447. if (workTree == null)
  448. // TODO: This should be handled by WorkingTreeIterators which
  449. // support write operations
  450. throw new UnsupportedOperationException();
  451. of = new File(workTree, tw.getPathString());
  452. fos = new FileOutputStream(of);
  453. try {
  454. fmt.formatMerge(fos, result, Arrays.asList(commitNames),
  455. Constants.CHARACTER_ENCODING);
  456. } finally {
  457. fos.close();
  458. }
  459. }
  460. else if (!result.containsConflicts()) {
  461. // When working inCore, only trivial merges can be handled,
  462. // so we generate objects only in conflict free cases
  463. of = File.createTempFile("merge_", "_temp", null);
  464. fos = new FileOutputStream(of);
  465. try {
  466. fmt.formatMerge(fos, result, Arrays.asList(commitNames),
  467. Constants.CHARACTER_ENCODING);
  468. } finally {
  469. fos.close();
  470. }
  471. }
  472. if (result.containsConflicts()) {
  473. // a conflict occurred, the file will contain conflict markers
  474. // the index will be populated with the three stages and only the
  475. // workdir (if used) contains the halfways merged content
  476. add(tw.getRawPath(), base, DirCacheEntry.STAGE_1);
  477. add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2);
  478. add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3);
  479. mergeResults.put(tw.getPathString(), result);
  480. return false;
  481. } else {
  482. // no conflict occurred, the file will contain fully merged content.
  483. // the index will be populated with the new merged version
  484. DirCacheEntry dce = new DirCacheEntry(tw.getPathString());
  485. dce.setFileMode(tw.getFileMode(0));
  486. dce.setLastModified(of.lastModified());
  487. dce.setLength((int) of.length());
  488. InputStream is = new FileInputStream(of);
  489. try {
  490. dce.setObjectId(oi.insert(Constants.OBJ_BLOB, of.length(),
  491. is));
  492. } finally {
  493. is.close();
  494. if (inCore)
  495. FileUtils.delete(of);
  496. }
  497. builder.add(dce);
  498. return true;
  499. }
  500. }
  501. private static RawText getRawText(ObjectId id, Repository db)
  502. throws IOException {
  503. if (id.equals(ObjectId.zeroId()))
  504. return new RawText(new byte[] {});
  505. return new RawText(db.open(id, Constants.OBJ_BLOB).getCachedBytes());
  506. }
  507. private static boolean nonTree(final int mode) {
  508. return mode != 0 && !FileMode.TREE.equals(mode);
  509. }
  510. @Override
  511. public ObjectId getResultTreeId() {
  512. return (resultTree == null) ? null : resultTree.toObjectId();
  513. }
  514. /**
  515. * @param commitNames
  516. * the names of the commits as they would appear in conflict
  517. * markers
  518. */
  519. public void setCommitNames(String[] commitNames) {
  520. this.commitNames = commitNames;
  521. }
  522. /**
  523. * @return the names of the commits as they would appear in conflict
  524. * markers.
  525. */
  526. public String[] getCommitNames() {
  527. return commitNames;
  528. }
  529. /**
  530. * @return the paths with conflicts. This is a subset of the files listed
  531. * by {@link #getModifiedFiles()}
  532. */
  533. public List<String> getUnmergedPaths() {
  534. return unmergedPaths;
  535. }
  536. /**
  537. * @return the paths of files which have been modified by this merge. A
  538. * file will be modified if a content-merge works on this path or if
  539. * the merge algorithm decides to take the theirs-version. This is a
  540. * superset of the files listed by {@link #getUnmergedPaths()}.
  541. */
  542. public List<String> getModifiedFiles() {
  543. return modifiedFiles;
  544. }
  545. /**
  546. * @return a map which maps the paths of files which have to be checked out
  547. * because the merge created new fully-merged content for this file
  548. * into the index. This means: the merge wrote a new stage 0 entry
  549. * for this path.
  550. */
  551. public Map<String, DirCacheEntry> getToBeCheckedOut() {
  552. return toBeCheckedOut;
  553. }
  554. /**
  555. * @return the mergeResults
  556. */
  557. public Map<String, MergeResult<? extends Sequence>> getMergeResults() {
  558. return mergeResults;
  559. }
  560. /**
  561. * @return lists paths causing this merge to fail (not stopped because of a
  562. * conflict). <code>null</code> is returned if this merge didn't
  563. * fail.
  564. */
  565. public Map<String, MergeFailureReason> getFailingPaths() {
  566. return (failingPaths.size() == 0) ? null : failingPaths;
  567. }
  568. /**
  569. * Returns whether this merge failed (i.e. not stopped because of a
  570. * conflict)
  571. *
  572. * @return <code>true</code> if a failure occurred, <code>false</code>
  573. * otherwise
  574. */
  575. public boolean failed() {
  576. return failingPaths.size() > 0;
  577. }
  578. /**
  579. * Sets the DirCache which shall be used by this merger. If the DirCache is
  580. * not set explicitly this merger will implicitly get and lock a default
  581. * DirCache. If the DirCache is explicitly set the caller is responsible to
  582. * lock it in advance. Finally the merger will call
  583. * {@link DirCache#commit()} which requires that the DirCache is locked. If
  584. * the {@link #mergeImpl()} returns without throwing an exception the lock
  585. * will be released. In case of exceptions the caller is responsible to
  586. * release the lock.
  587. *
  588. * @param dc
  589. * the DirCache to set
  590. */
  591. public void setDirCache(DirCache dc) {
  592. this.dircache = dc;
  593. }
  594. /**
  595. * Sets the WorkingTreeIterator to be used by this merger. If no
  596. * WorkingTreeIterator is set this merger will ignore the working tree and
  597. * fail if a content merge is necessary.
  598. * <p>
  599. * TODO: enhance WorkingTreeIterator to support write operations. Then this
  600. * merger will be able to merge with a different working tree abstraction.
  601. *
  602. * @param workingTreeIterator
  603. * the workingTreeIt to set
  604. */
  605. public void setWorkingTreeIterator(WorkingTreeIterator workingTreeIterator) {
  606. this.workingTreeIterator = workingTreeIterator;
  607. }
  608. }