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.

ResolveMerger.java 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  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.RawText;
  61. import org.eclipse.jgit.diff.RawTextComparator;
  62. import org.eclipse.jgit.diff.Sequence;
  63. import org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm;
  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 abnormally (means: not because of unresolved
  91. * conflicts) 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> unmergedPathes = 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> failingPathes = 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 (getUnmergedPathes().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(), true);
  213. } else {
  214. if (!f.delete())
  215. failingPathes.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 occured
  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 modeI = tw.getRawMode(T_INDEX);
  328. // Each index entry has to match ours, means: it has to be clean
  329. if (nonTree(modeI)
  330. && !(tw.idEqual(T_INDEX, T_OURS) && modeO == modeI)) {
  331. failingPathes.put(tw.getPathString(), MergeFailureReason.DIRTY_INDEX);
  332. return false;
  333. }
  334. final int modeT = tw.getRawMode(T_THEIRS);
  335. if (nonTree(modeO) && modeO == modeT && tw.idEqual(T_OURS, T_THEIRS)) {
  336. // ours and theirs are equal: it doesn'nt matter
  337. // which one we choose. OURS is choosen here.
  338. add(tw.getRawPath(), ours, DirCacheEntry.STAGE_0);
  339. // no checkout needed!
  340. return true;
  341. }
  342. final int modeB = tw.getRawMode(T_BASE);
  343. if (nonTree(modeO) && modeB == modeT && tw.idEqual(T_BASE, T_THEIRS)) {
  344. // THEIRS was not changed compared to base. All changes must be in
  345. // OURS. Choose OURS.
  346. add(tw.getRawPath(), ours, DirCacheEntry.STAGE_0);
  347. return true;
  348. }
  349. if (modeB == modeO && tw.idEqual(T_BASE, T_OURS)) {
  350. // OURS was not changed compared to base. All changes must be in
  351. // THEIRS. Choose THEIRS.
  352. if (nonTree(modeT)) {
  353. DirCacheEntry e = add(tw.getRawPath(), theirs,
  354. DirCacheEntry.STAGE_0);
  355. if (e != null)
  356. toBeCheckedOut.put(tw.getPathString(), e);
  357. return true;
  358. } else if ((modeT == 0) && (modeB != 0)) {
  359. // we want THEIRS ... but THEIRS contains the deletion of the
  360. // file
  361. toBeCheckedOut.put(tw.getPathString(), null);
  362. return true;
  363. }
  364. }
  365. if (tw.isSubtree()) {
  366. // file/folder conflicts: here I want to detect only file/folder
  367. // conflict between ours and theirs. file/folder conflicts between
  368. // base/index/workingTree and something else are not relevant or
  369. // detected later
  370. if (nonTree(modeO) && !nonTree(modeT)) {
  371. if (nonTree(modeB))
  372. add(tw.getRawPath(), base, DirCacheEntry.STAGE_1);
  373. add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2);
  374. unmergedPathes.add(tw.getPathString());
  375. enterSubtree = false;
  376. return true;
  377. }
  378. if (nonTree(modeT) && !nonTree(modeO)) {
  379. if (nonTree(modeB))
  380. add(tw.getRawPath(), base, DirCacheEntry.STAGE_1);
  381. add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3);
  382. unmergedPathes.add(tw.getPathString());
  383. enterSubtree = false;
  384. return true;
  385. }
  386. // ours and theirs are both folders or both files (and treewalk
  387. // tells us we are in a subtree because of index or working-dir).
  388. // If they are both folders no content-merge is required - we can
  389. // return here.
  390. if (!nonTree(modeO))
  391. return true;
  392. // ours and theirs are both files, just fall out of the if block
  393. // and do the content merge
  394. }
  395. if (nonTree(modeO) && nonTree(modeT)) {
  396. if (!inCore) {
  397. // We are going to update the worktree. Make sure the worktree
  398. // is not modified
  399. if (work != null
  400. && (!nonTree(work.getEntryRawMode()) || work
  401. .isModified(index.getDirCacheEntry(), true,
  402. true, db.getFS()))) {
  403. failingPathes.put(tw.getPathString(),
  404. MergeFailureReason.DIRTY_WORKTREE);
  405. return false;
  406. }
  407. }
  408. if (!contentMerge(base, ours, theirs)) {
  409. unmergedPathes.add(tw.getPathString());
  410. }
  411. modifiedFiles.add(tw.getPathString());
  412. }
  413. return true;
  414. }
  415. private boolean contentMerge(CanonicalTreeParser base,
  416. CanonicalTreeParser ours, CanonicalTreeParser theirs)
  417. throws FileNotFoundException, IllegalStateException, IOException {
  418. MergeFormatter fmt = new MergeFormatter();
  419. RawText baseText = base == null ? RawText.EMPTY_TEXT : getRawText(
  420. base.getEntryObjectId(), db);
  421. // do the merge
  422. MergeResult<RawText> result = mergeAlgorithm.merge(
  423. RawTextComparator.DEFAULT, baseText,
  424. getRawText(ours.getEntryObjectId(), db),
  425. getRawText(theirs.getEntryObjectId(), db));
  426. File of = null;
  427. FileOutputStream fos;
  428. if (!inCore) {
  429. File workTree = db.getWorkTree();
  430. if (workTree == null)
  431. // TODO: This should be handled by WorkingTreeIterators which
  432. // support write operations
  433. throw new UnsupportedOperationException();
  434. of = new File(workTree, tw.getPathString());
  435. fos = new FileOutputStream(of);
  436. try {
  437. fmt.formatMerge(fos, result, Arrays.asList(commitNames),
  438. Constants.CHARACTER_ENCODING);
  439. } finally {
  440. fos.close();
  441. }
  442. }
  443. else if (!result.containsConflicts()) {
  444. // When working inCore, only trivial merges can be handled,
  445. // so we generate objects only in conflict free cases
  446. of = File.createTempFile("merge_", "_temp", null);
  447. fos = new FileOutputStream(of);
  448. try {
  449. fmt.formatMerge(fos, result, Arrays.asList(commitNames),
  450. Constants.CHARACTER_ENCODING);
  451. } finally {
  452. fos.close();
  453. }
  454. }
  455. if (result.containsConflicts()) {
  456. // a conflict occured, the file will contain conflict markers
  457. // the index will be populated with the three stages and only the
  458. // workdir (if used) contains the halfways merged content
  459. add(tw.getRawPath(), base, DirCacheEntry.STAGE_1);
  460. add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2);
  461. add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3);
  462. mergeResults.put(tw.getPathString(), result);
  463. return false;
  464. } else {
  465. // no conflict occured, the file will contain fully merged content.
  466. // the index will be populated with the new merged version
  467. DirCacheEntry dce = new DirCacheEntry(tw.getPathString());
  468. dce.setFileMode(tw.getFileMode(0));
  469. dce.setLastModified(of.lastModified());
  470. dce.setLength((int) of.length());
  471. InputStream is = new FileInputStream(of);
  472. try {
  473. dce.setObjectId(oi.insert(Constants.OBJ_BLOB, of.length(),
  474. is));
  475. } finally {
  476. is.close();
  477. if (inCore)
  478. FileUtils.delete(of);
  479. }
  480. builder.add(dce);
  481. return true;
  482. }
  483. }
  484. private static RawText getRawText(ObjectId id, Repository db)
  485. throws IOException {
  486. if (id.equals(ObjectId.zeroId()))
  487. return new RawText(new byte[] {});
  488. return new RawText(db.open(id, Constants.OBJ_BLOB).getCachedBytes());
  489. }
  490. private static boolean nonTree(final int mode) {
  491. return mode != 0 && !FileMode.TREE.equals(mode);
  492. }
  493. @Override
  494. public ObjectId getResultTreeId() {
  495. return (resultTree == null) ? null : resultTree.toObjectId();
  496. }
  497. /**
  498. * @param commitNames
  499. * the names of the commits as they would appear in conflict
  500. * markers
  501. */
  502. public void setCommitNames(String[] commitNames) {
  503. this.commitNames = commitNames;
  504. }
  505. /**
  506. * @return the names of the commits as they would appear in conflict
  507. * markers.
  508. */
  509. public String[] getCommitNames() {
  510. return commitNames;
  511. }
  512. /**
  513. * @return the paths with conflicts. This is a subset of the files listed
  514. * by {@link #getModifiedFiles()}
  515. */
  516. public List<String> getUnmergedPathes() {
  517. return unmergedPathes;
  518. }
  519. /**
  520. * @return the paths of files which have been modified by this merge. A
  521. * file will be modified if a content-merge works on this path or if
  522. * the merge algorithm decides to take the theirs-version. This is a
  523. * superset of the files listed by {@link #getUnmergedPathes()}.
  524. */
  525. public List<String> getModifiedFiles() {
  526. return modifiedFiles;
  527. }
  528. /**
  529. * @return a map which maps the paths of files which have to be checked out
  530. * because the merge created new fully-merged content for this file
  531. * into the index. This means: the merge wrote a new stage 0 entry
  532. * for this path.
  533. */
  534. public Map<String, DirCacheEntry> getToBeCheckedOut() {
  535. return toBeCheckedOut;
  536. }
  537. /**
  538. * @return the mergeResults
  539. */
  540. public Map<String, MergeResult<? extends Sequence>> getMergeResults() {
  541. return mergeResults;
  542. }
  543. /**
  544. * @return lists paths causing this merge to fail abnormally (not because of
  545. * a conflict). <code>null</code> is returned if this merge didn't
  546. * fail abnormally.
  547. */
  548. public Map<String, MergeFailureReason> getFailingPathes() {
  549. return (failingPathes.size() == 0) ? null : failingPathes;
  550. }
  551. /**
  552. * Sets the DirCache which shall be used by this merger. If the DirCache is
  553. * not set explicitly this merger will implicitly get and lock a default
  554. * DirCache. If the DirCache is explicitly set the caller is responsible to
  555. * lock it in advance. Finally the merger will call
  556. * {@link DirCache#commit()} which requires that the DirCache is locked. If
  557. * the {@link #mergeImpl()} returns without throwing an exception the lock
  558. * will be released. In case of exceptions the caller is responsible to
  559. * release the lock.
  560. *
  561. * @param dc
  562. * the DirCache to set
  563. */
  564. public void setDirCache(DirCache dc) {
  565. this.dircache = dc;
  566. }
  567. /**
  568. * Sets the WorkingTreeIterator to be used by this merger. If no
  569. * WorkingTreeIterator is set this merger will ignore the working tree and
  570. * fail if a content merge is necessary.
  571. * <p>
  572. * TODO: enhance WorkingTreeIterator to support write operations. Then this
  573. * merger will be able to merge with a different working tree abstraction.
  574. *
  575. * @param workingTreeIterator
  576. * the workingTreeIt to set
  577. */
  578. public void setWorkingTreeIterator(WorkingTreeIterator workingTreeIterator) {
  579. this.workingTreeIterator = workingTreeIterator;
  580. }
  581. }