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 32KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979
  1. /*
  2. * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>,
  3. * Copyright (C) 2010-2012, Matthias Sohn <matthias.sohn@sap.com>
  4. * Copyright (C) 2012, Research In Motion Limited
  5. * and other copyright owners as documented in the project's IP log.
  6. *
  7. * This program and the accompanying materials are made available
  8. * under the terms of the Eclipse Distribution License v1.0 which
  9. * accompanies this distribution, is reproduced below, and is
  10. * available at http://www.eclipse.org/org/documents/edl-v10.php
  11. *
  12. * All rights reserved.
  13. *
  14. * Redistribution and use in source and binary forms, with or
  15. * without modification, are permitted provided that the following
  16. * conditions are met:
  17. *
  18. * - Redistributions of source code must retain the above copyright
  19. * notice, this list of conditions and the following disclaimer.
  20. *
  21. * - Redistributions in binary form must reproduce the above
  22. * copyright notice, this list of conditions and the following
  23. * disclaimer in the documentation and/or other materials provided
  24. * with the distribution.
  25. *
  26. * - Neither the name of the Eclipse Foundation, Inc. nor the
  27. * names of its contributors may be used to endorse or promote
  28. * products derived from this software without specific prior
  29. * written permission.
  30. *
  31. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  32. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  33. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  34. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  35. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  36. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  37. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  38. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  39. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  40. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  41. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  42. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  43. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  44. */
  45. package org.eclipse.jgit.merge;
  46. import java.io.File;
  47. import java.io.FileInputStream;
  48. import java.io.FileNotFoundException;
  49. import java.io.FileOutputStream;
  50. import java.io.IOException;
  51. import java.io.InputStream;
  52. import java.util.ArrayList;
  53. import java.util.Arrays;
  54. import java.util.Collections;
  55. import java.util.HashMap;
  56. import java.util.Iterator;
  57. import java.util.LinkedList;
  58. import java.util.List;
  59. import java.util.Map;
  60. import org.eclipse.jgit.diff.DiffAlgorithm;
  61. import org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm;
  62. import org.eclipse.jgit.diff.RawText;
  63. import org.eclipse.jgit.diff.RawTextComparator;
  64. import org.eclipse.jgit.diff.Sequence;
  65. import org.eclipse.jgit.dircache.DirCache;
  66. import org.eclipse.jgit.dircache.DirCacheBuildIterator;
  67. import org.eclipse.jgit.dircache.DirCacheBuilder;
  68. import org.eclipse.jgit.dircache.DirCacheCheckout;
  69. import org.eclipse.jgit.dircache.DirCacheEntry;
  70. import org.eclipse.jgit.errors.CorruptObjectException;
  71. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  72. import org.eclipse.jgit.errors.IndexWriteException;
  73. import org.eclipse.jgit.errors.MissingObjectException;
  74. import org.eclipse.jgit.errors.NoWorkTreeException;
  75. import org.eclipse.jgit.internal.JGitText;
  76. import org.eclipse.jgit.lib.ConfigConstants;
  77. import org.eclipse.jgit.lib.Constants;
  78. import org.eclipse.jgit.lib.FileMode;
  79. import org.eclipse.jgit.lib.ObjectId;
  80. import org.eclipse.jgit.lib.ObjectReader;
  81. import org.eclipse.jgit.lib.Repository;
  82. import org.eclipse.jgit.revwalk.RevTree;
  83. import org.eclipse.jgit.treewalk.AbstractTreeIterator;
  84. import org.eclipse.jgit.treewalk.CanonicalTreeParser;
  85. import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
  86. import org.eclipse.jgit.treewalk.WorkingTreeIterator;
  87. import org.eclipse.jgit.util.FS;
  88. import org.eclipse.jgit.util.FileUtils;
  89. /**
  90. * A three-way merger performing a content-merge if necessary
  91. */
  92. public class ResolveMerger extends ThreeWayMerger {
  93. /**
  94. * If the merge fails (means: not stopped because of unresolved conflicts)
  95. * this enum is used to explain why it failed
  96. */
  97. public enum MergeFailureReason {
  98. /** the merge failed because of a dirty index */
  99. DIRTY_INDEX,
  100. /** the merge failed because of a dirty workingtree */
  101. DIRTY_WORKTREE,
  102. /** the merge failed because of a file could not be deleted */
  103. COULD_NOT_DELETE
  104. }
  105. private NameConflictTreeWalk tw;
  106. /**
  107. * string versions of a list of commit SHA1s
  108. *
  109. * @since 3.0
  110. */
  111. protected String commitNames[];
  112. private static final int T_BASE = 0;
  113. private static final int T_OURS = 1;
  114. private static final int T_THEIRS = 2;
  115. private static final int T_INDEX = 3;
  116. private static final int T_FILE = 4;
  117. private DirCacheBuilder builder;
  118. /**
  119. * merge result as tree
  120. *
  121. * @since 3.0
  122. */
  123. protected ObjectId resultTree;
  124. private List<String> unmergedPaths = new ArrayList<String>();
  125. private List<String> modifiedFiles = new LinkedList<String>();
  126. private Map<String, DirCacheEntry> toBeCheckedOut = new HashMap<String, DirCacheEntry>();
  127. private List<String> toBeDeleted = new ArrayList<String>();
  128. private Map<String, MergeResult<? extends Sequence>> mergeResults = new HashMap<String, MergeResult<? extends Sequence>>();
  129. private Map<String, MergeFailureReason> failingPaths = new HashMap<String, MergeFailureReason>();
  130. private boolean enterSubtree;
  131. /**
  132. * Set to true if this merge should work in-memory. The repos dircache and
  133. * workingtree are not touched by this method. Eventually needed files are
  134. * created as temporary files and a new empty, in-memory dircache will be
  135. * used instead the repo's one. Often used for bare repos where the repo
  136. * doesn't even have a workingtree and dircache.
  137. * @since 3.0
  138. */
  139. protected boolean inCore;
  140. /**
  141. * Set to true if this merger should use the default dircache of the
  142. * repository and should handle locking and unlocking of the dircache. If
  143. * this merger should work in-core or if an explicit dircache was specified
  144. * during construction then this field is set to false.
  145. * @since 3.0
  146. */
  147. protected boolean implicitDirCache;
  148. /**
  149. * Directory cache
  150. * @since 3.0
  151. */
  152. protected DirCache dircache;
  153. /**
  154. * The iterator to access the working tree. If set to <code>null</code> this
  155. * merger will not touch the working tree.
  156. * @since 3.0
  157. */
  158. protected WorkingTreeIterator workingTreeIterator;
  159. /**
  160. * our merge algorithm
  161. * @since 3.0
  162. */
  163. protected MergeAlgorithm mergeAlgorithm;
  164. /**
  165. * @param local
  166. * @param inCore
  167. */
  168. protected ResolveMerger(Repository local, boolean inCore) {
  169. super(local);
  170. SupportedAlgorithm diffAlg = local.getConfig().getEnum(
  171. ConfigConstants.CONFIG_DIFF_SECTION, null,
  172. ConfigConstants.CONFIG_KEY_ALGORITHM,
  173. SupportedAlgorithm.HISTOGRAM);
  174. mergeAlgorithm = new MergeAlgorithm(DiffAlgorithm.getAlgorithm(diffAlg));
  175. commitNames = new String[] { "BASE", "OURS", "THEIRS" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
  176. this.inCore = inCore;
  177. if (inCore) {
  178. implicitDirCache = false;
  179. dircache = DirCache.newInCore();
  180. } else {
  181. implicitDirCache = true;
  182. }
  183. }
  184. /**
  185. * @param local
  186. */
  187. protected ResolveMerger(Repository local) {
  188. this(local, false);
  189. }
  190. @Override
  191. protected boolean mergeImpl() throws IOException {
  192. if (implicitDirCache)
  193. dircache = getRepository().lockDirCache();
  194. try {
  195. return mergeTrees(mergeBase(), sourceTrees[0], sourceTrees[1]);
  196. } finally {
  197. if (implicitDirCache)
  198. dircache.unlock();
  199. }
  200. }
  201. private void checkout() throws NoWorkTreeException, IOException {
  202. ObjectReader r = db.getObjectDatabase().newReader();
  203. try {
  204. for (Map.Entry<String, DirCacheEntry> entry : toBeCheckedOut
  205. .entrySet()) {
  206. File f = new File(db.getWorkTree(), entry.getKey());
  207. createDir(f.getParentFile());
  208. DirCacheCheckout.checkoutEntry(db, f, entry.getValue(), r);
  209. modifiedFiles.add(entry.getKey());
  210. }
  211. // Iterate in reverse so that "folder/file" is deleted before
  212. // "folder". Otherwise this could result in a failing path because
  213. // of a non-empty directory, for which delete() would fail.
  214. for (int i = toBeDeleted.size() - 1; i >= 0; i--) {
  215. String fileName = toBeDeleted.get(i);
  216. File f = new File(db.getWorkTree(), fileName);
  217. if (!f.delete())
  218. if (!f.isDirectory())
  219. failingPaths.put(fileName,
  220. MergeFailureReason.COULD_NOT_DELETE);
  221. modifiedFiles.add(fileName);
  222. }
  223. } finally {
  224. r.release();
  225. }
  226. }
  227. private void createDir(File f) throws IOException {
  228. if (!db.getFS().isDirectory(f) && !f.mkdirs()) {
  229. File p = f;
  230. while (p != null && !db.getFS().exists(p))
  231. p = p.getParentFile();
  232. if (p == null || db.getFS().isDirectory(p))
  233. throw new IOException(JGitText.get().cannotCreateDirectory);
  234. FileUtils.delete(p);
  235. if (!f.mkdirs())
  236. throw new IOException(JGitText.get().cannotCreateDirectory);
  237. }
  238. }
  239. /**
  240. * Reverts the worktree after an unsuccessful merge. We know that for all
  241. * modified files the old content was in the old index and the index
  242. * contained only stage 0. In case if inCore operation just clear the
  243. * history of modified files.
  244. *
  245. * @throws IOException
  246. * @throws CorruptObjectException
  247. * @throws NoWorkTreeException
  248. */
  249. private void cleanUp() throws NoWorkTreeException, CorruptObjectException,
  250. IOException {
  251. if (inCore) {
  252. modifiedFiles.clear();
  253. return;
  254. }
  255. DirCache dc = db.readDirCache();
  256. ObjectReader or = db.getObjectDatabase().newReader();
  257. Iterator<String> mpathsIt=modifiedFiles.iterator();
  258. while(mpathsIt.hasNext()) {
  259. String mpath=mpathsIt.next();
  260. DirCacheEntry entry = dc.getEntry(mpath);
  261. if (entry == null)
  262. continue;
  263. FileOutputStream fos = new FileOutputStream(new File(
  264. db.getWorkTree(), mpath));
  265. try {
  266. or.open(entry.getObjectId()).copyTo(fos);
  267. } finally {
  268. fos.close();
  269. }
  270. mpathsIt.remove();
  271. }
  272. }
  273. /**
  274. * adds a new path with the specified stage to the index builder
  275. *
  276. * @param path
  277. * @param p
  278. * @param stage
  279. * @param lastMod
  280. * @param len
  281. * @return the entry which was added to the index
  282. */
  283. private DirCacheEntry add(byte[] path, CanonicalTreeParser p, int stage,
  284. long lastMod, long len) {
  285. if (p != null && !p.getEntryFileMode().equals(FileMode.TREE)) {
  286. DirCacheEntry e = new DirCacheEntry(path, stage);
  287. e.setFileMode(p.getEntryFileMode());
  288. e.setObjectId(p.getEntryObjectId());
  289. e.setLastModified(lastMod);
  290. e.setLength(len);
  291. builder.add(e);
  292. return e;
  293. }
  294. return null;
  295. }
  296. /**
  297. * adds a entry to the index builder which is a copy of the specified
  298. * DirCacheEntry
  299. *
  300. * @param e
  301. * the entry which should be copied
  302. *
  303. * @return the entry which was added to the index
  304. */
  305. private DirCacheEntry keep(DirCacheEntry e) {
  306. DirCacheEntry newEntry = new DirCacheEntry(e.getPathString(),
  307. e.getStage());
  308. newEntry.setFileMode(e.getFileMode());
  309. newEntry.setObjectId(e.getObjectId());
  310. newEntry.setLastModified(e.getLastModified());
  311. newEntry.setLength(e.getLength());
  312. builder.add(newEntry);
  313. return newEntry;
  314. }
  315. /**
  316. * Processes one path and tries to merge. This method will do all do all
  317. * trivial (not content) merges and will also detect if a merge will fail.
  318. * The merge will fail when one of the following is true
  319. * <ul>
  320. * <li>the index entry does not match the entry in ours. When merging one
  321. * branch into the current HEAD, ours will point to HEAD and theirs will
  322. * point to the other branch. It is assumed that the index matches the HEAD
  323. * because it will only not match HEAD if it was populated before the merge
  324. * operation. But the merge commit should not accidentally contain
  325. * modifications done before the merge. Check the <a href=
  326. * "http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html#_3_way_merge"
  327. * >git read-tree</a> documentation for further explanations.</li>
  328. * <li>A conflict was detected and the working-tree file is dirty. When a
  329. * conflict is detected the content-merge algorithm will try to write a
  330. * merged version into the working-tree. If the file is dirty we would
  331. * override unsaved data.</li>
  332. *
  333. * @param base
  334. * the common base for ours and theirs
  335. * @param ours
  336. * the ours side of the merge. When merging a branch into the
  337. * HEAD ours will point to HEAD
  338. * @param theirs
  339. * the theirs side of the merge. When merging a branch into the
  340. * current HEAD theirs will point to the branch which is merged
  341. * into HEAD.
  342. * @param index
  343. * the index entry
  344. * @param work
  345. * the file in the working tree
  346. * @return <code>false</code> if the merge will fail because the index entry
  347. * didn't match ours or the working-dir file was dirty and a
  348. * conflict occurred
  349. * @throws MissingObjectException
  350. * @throws IncorrectObjectTypeException
  351. * @throws CorruptObjectException
  352. * @throws IOException
  353. */
  354. private boolean processEntry(CanonicalTreeParser base,
  355. CanonicalTreeParser ours, CanonicalTreeParser theirs,
  356. DirCacheBuildIterator index, WorkingTreeIterator work)
  357. throws MissingObjectException, IncorrectObjectTypeException,
  358. CorruptObjectException, IOException {
  359. enterSubtree = true;
  360. final int modeO = tw.getRawMode(T_OURS);
  361. final int modeT = tw.getRawMode(T_THEIRS);
  362. final int modeB = tw.getRawMode(T_BASE);
  363. if (modeO == 0 && modeT == 0 && modeB == 0)
  364. // File is either untracked or new, staged but uncommitted
  365. return true;
  366. if (isIndexDirty())
  367. return false;
  368. DirCacheEntry ourDce = null;
  369. if (index == null || index.getDirCacheEntry() == null) {
  370. // create a fake DCE, but only if ours is valid. ours is kept only
  371. // in case it is valid, so a null ourDce is ok in all other cases.
  372. if (nonTree(modeO)) {
  373. ourDce = new DirCacheEntry(tw.getRawPath());
  374. ourDce.setObjectId(tw.getObjectId(T_OURS));
  375. ourDce.setFileMode(tw.getFileMode(T_OURS));
  376. }
  377. } else {
  378. ourDce = index.getDirCacheEntry();
  379. }
  380. if (nonTree(modeO) && nonTree(modeT) && tw.idEqual(T_OURS, T_THEIRS)) {
  381. // OURS and THEIRS have equal content. Check the file mode
  382. if (modeO == modeT) {
  383. // content and mode of OURS and THEIRS are equal: it doesn't
  384. // matter which one we choose. OURS is chosen. Since the index
  385. // is clean (the index matches already OURS) we can keep the existing one
  386. keep(ourDce);
  387. // no checkout needed!
  388. return true;
  389. } else {
  390. // same content but different mode on OURS and THEIRS.
  391. // Try to merge the mode and report an error if this is
  392. // not possible.
  393. int newMode = mergeFileModes(modeB, modeO, modeT);
  394. if (newMode != FileMode.MISSING.getBits()) {
  395. if (newMode == modeO)
  396. // ours version is preferred
  397. keep(ourDce);
  398. else {
  399. // the preferred version THEIRS has a different mode
  400. // than ours. Check it out!
  401. if (isWorktreeDirty(work, ourDce))
  402. return false;
  403. // we know about length and lastMod only after we have written the new content.
  404. // This will happen later. Set these values to 0 for know.
  405. DirCacheEntry e = add(tw.getRawPath(), theirs,
  406. DirCacheEntry.STAGE_0, 0, 0);
  407. toBeCheckedOut.put(tw.getPathString(), e);
  408. }
  409. return true;
  410. } else {
  411. // FileModes are not mergeable. We found a conflict on modes.
  412. // For conflicting entries we don't know lastModified and length.
  413. add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
  414. add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
  415. add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0);
  416. unmergedPaths.add(tw.getPathString());
  417. mergeResults.put(
  418. tw.getPathString(),
  419. new MergeResult<RawText>(Collections
  420. .<RawText> emptyList()));
  421. }
  422. return true;
  423. }
  424. }
  425. if (nonTree(modeO) && modeB == modeT && tw.idEqual(T_BASE, T_THEIRS)) {
  426. // THEIRS was not changed compared to BASE. All changes must be in
  427. // OURS. OURS is chosen. We can keep the existing entry.
  428. keep(ourDce);
  429. // no checkout needed!
  430. return true;
  431. }
  432. if (modeB == modeO && tw.idEqual(T_BASE, T_OURS)) {
  433. // OURS was not changed compared to BASE. All changes must be in
  434. // THEIRS. THEIRS is chosen.
  435. // Check worktree before checking out THEIRS
  436. if (isWorktreeDirty(work, ourDce))
  437. return false;
  438. if (nonTree(modeT)) {
  439. // we know about length and lastMod only after we have written
  440. // the new content.
  441. // This will happen later. Set these values to 0 for know.
  442. DirCacheEntry e = add(tw.getRawPath(), theirs,
  443. DirCacheEntry.STAGE_0, 0, 0);
  444. if (e != null)
  445. toBeCheckedOut.put(tw.getPathString(), e);
  446. return true;
  447. } else if (modeT == 0 && modeB != 0) {
  448. // we want THEIRS ... but THEIRS contains the deletion of the
  449. // file. Also, do not complain if the file is already deleted
  450. // locally. This complements the test in isWorktreeDirty() for
  451. // the same case.
  452. if (tw.getTreeCount() > T_FILE && tw.getRawMode(T_FILE) == 0)
  453. return true;
  454. toBeDeleted.add(tw.getPathString());
  455. return true;
  456. }
  457. }
  458. if (tw.isSubtree()) {
  459. // file/folder conflicts: here I want to detect only file/folder
  460. // conflict between ours and theirs. file/folder conflicts between
  461. // base/index/workingTree and something else are not relevant or
  462. // detected later
  463. if (nonTree(modeO) && !nonTree(modeT)) {
  464. if (nonTree(modeB))
  465. add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
  466. add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
  467. unmergedPaths.add(tw.getPathString());
  468. enterSubtree = false;
  469. return true;
  470. }
  471. if (nonTree(modeT) && !nonTree(modeO)) {
  472. if (nonTree(modeB))
  473. add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
  474. add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0);
  475. unmergedPaths.add(tw.getPathString());
  476. enterSubtree = false;
  477. return true;
  478. }
  479. // ours and theirs are both folders or both files (and treewalk
  480. // tells us we are in a subtree because of index or working-dir).
  481. // If they are both folders no content-merge is required - we can
  482. // return here.
  483. if (!nonTree(modeO))
  484. return true;
  485. // ours and theirs are both files, just fall out of the if block
  486. // and do the content merge
  487. }
  488. if (nonTree(modeO) && nonTree(modeT)) {
  489. // Check worktree before modifying files
  490. if (isWorktreeDirty(work, ourDce))
  491. return false;
  492. // Don't attempt to resolve submodule link conflicts
  493. if (isGitLink(modeO) || isGitLink(modeT)) {
  494. add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
  495. add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
  496. add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0);
  497. unmergedPaths.add(tw.getPathString());
  498. return true;
  499. }
  500. MergeResult<RawText> result = contentMerge(base, ours, theirs);
  501. File of = writeMergedFile(result);
  502. updateIndex(base, ours, theirs, result, of);
  503. if (result.containsConflicts())
  504. unmergedPaths.add(tw.getPathString());
  505. modifiedFiles.add(tw.getPathString());
  506. } else if (modeO != modeT) {
  507. // OURS or THEIRS has been deleted
  508. if (((modeO != 0 && !tw.idEqual(T_BASE, T_OURS)) || (modeT != 0 && !tw
  509. .idEqual(T_BASE, T_THEIRS)))) {
  510. add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
  511. add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
  512. DirCacheEntry e = add(tw.getRawPath(), theirs,
  513. DirCacheEntry.STAGE_3, 0, 0);
  514. // OURS was deleted checkout THEIRS
  515. if (modeO == 0) {
  516. // Check worktree before checking out THEIRS
  517. if (isWorktreeDirty(work, ourDce))
  518. return false;
  519. if (nonTree(modeT)) {
  520. if (e != null)
  521. toBeCheckedOut.put(tw.getPathString(), e);
  522. }
  523. }
  524. unmergedPaths.add(tw.getPathString());
  525. // generate a MergeResult for the deleted file
  526. mergeResults.put(tw.getPathString(),
  527. contentMerge(base, ours, theirs));
  528. }
  529. }
  530. return true;
  531. }
  532. /**
  533. * Does the content merge. The three texts base, ours and theirs are
  534. * specified with {@link CanonicalTreeParser}. If any of the parsers is
  535. * specified as <code>null</code> then an empty text will be used instead.
  536. *
  537. * @param base
  538. * @param ours
  539. * @param theirs
  540. *
  541. * @return the result of the content merge
  542. * @throws IOException
  543. */
  544. private MergeResult<RawText> contentMerge(CanonicalTreeParser base,
  545. CanonicalTreeParser ours, CanonicalTreeParser theirs)
  546. throws IOException {
  547. RawText baseText = base == null ? RawText.EMPTY_TEXT : getRawText(
  548. base.getEntryObjectId(), db);
  549. RawText ourText = ours == null ? RawText.EMPTY_TEXT : getRawText(
  550. ours.getEntryObjectId(), db);
  551. RawText theirsText = theirs == null ? RawText.EMPTY_TEXT : getRawText(
  552. theirs.getEntryObjectId(), db);
  553. return (mergeAlgorithm.merge(RawTextComparator.DEFAULT, baseText,
  554. ourText, theirsText));
  555. }
  556. private boolean isIndexDirty() {
  557. if (inCore)
  558. return false;
  559. final int modeI = tw.getRawMode(T_INDEX);
  560. final int modeO = tw.getRawMode(T_OURS);
  561. // Index entry has to match ours to be considered clean
  562. final boolean isDirty = nonTree(modeI)
  563. && !(modeO == modeI && tw.idEqual(T_INDEX, T_OURS));
  564. if (isDirty)
  565. failingPaths
  566. .put(tw.getPathString(), MergeFailureReason.DIRTY_INDEX);
  567. return isDirty;
  568. }
  569. private boolean isWorktreeDirty(WorkingTreeIterator work,
  570. DirCacheEntry ourDce) throws IOException {
  571. if (work == null)
  572. return false;
  573. final int modeF = tw.getRawMode(T_FILE);
  574. final int modeO = tw.getRawMode(T_OURS);
  575. // Worktree entry has to match ours to be considered clean
  576. boolean isDirty;
  577. if (ourDce != null)
  578. isDirty = work.isModified(ourDce, true, reader);
  579. else {
  580. isDirty = work.isModeDifferent(modeO);
  581. if (!isDirty && nonTree(modeF))
  582. isDirty = !tw.idEqual(T_FILE, T_OURS);
  583. }
  584. // Ignore existing empty directories
  585. if (isDirty && modeF == FileMode.TYPE_TREE
  586. && modeO == FileMode.TYPE_MISSING)
  587. isDirty = false;
  588. if (isDirty)
  589. failingPaths.put(tw.getPathString(),
  590. MergeFailureReason.DIRTY_WORKTREE);
  591. return isDirty;
  592. }
  593. /**
  594. * Updates the index after a content merge has happened. If no conflict has
  595. * occurred this includes persisting the merged content to the object
  596. * database. In case of conflicts this method takes care to write the
  597. * correct stages to the index.
  598. *
  599. * @param base
  600. * @param ours
  601. * @param theirs
  602. * @param result
  603. * @param of
  604. * @throws FileNotFoundException
  605. * @throws IOException
  606. */
  607. private void updateIndex(CanonicalTreeParser base,
  608. CanonicalTreeParser ours, CanonicalTreeParser theirs,
  609. MergeResult<RawText> result, File of) throws FileNotFoundException,
  610. IOException {
  611. if (result.containsConflicts()) {
  612. // a conflict occurred, the file will contain conflict markers
  613. // the index will be populated with the three stages and only the
  614. // workdir (if used) contains the halfways merged content
  615. add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
  616. add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
  617. add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0);
  618. mergeResults.put(tw.getPathString(), result);
  619. } else {
  620. // no conflict occurred, the file will contain fully merged content.
  621. // the index will be populated with the new merged version
  622. DirCacheEntry dce = new DirCacheEntry(tw.getPathString());
  623. int newMode = mergeFileModes(tw.getRawMode(0), tw.getRawMode(1),
  624. tw.getRawMode(2));
  625. // set the mode for the new content. Fall back to REGULAR_FILE if
  626. // you can't merge modes of OURS and THEIRS
  627. dce.setFileMode((newMode == FileMode.MISSING.getBits()) ? FileMode.REGULAR_FILE
  628. : FileMode.fromBits(newMode));
  629. dce.setLastModified(of.lastModified());
  630. dce.setLength((int) of.length());
  631. InputStream is = new FileInputStream(of);
  632. try {
  633. dce.setObjectId(getObjectInserter().insert(
  634. Constants.OBJ_BLOB, of.length(), is));
  635. } finally {
  636. is.close();
  637. if (inCore)
  638. FileUtils.delete(of);
  639. }
  640. builder.add(dce);
  641. }
  642. }
  643. /**
  644. * Writes merged file content to the working tree. In case {@link #inCore}
  645. * is set and we don't have a working tree the content is written to a
  646. * temporary file
  647. *
  648. * @param result
  649. * the result of the content merge
  650. * @return the file to which the merged content was written
  651. * @throws FileNotFoundException
  652. * @throws IOException
  653. */
  654. private File writeMergedFile(MergeResult<RawText> result)
  655. throws FileNotFoundException, IOException {
  656. MergeFormatter fmt = new MergeFormatter();
  657. File of = null;
  658. FileOutputStream fos;
  659. if (!inCore) {
  660. File workTree = db.getWorkTree();
  661. if (workTree == null)
  662. // TODO: This should be handled by WorkingTreeIterators which
  663. // support write operations
  664. throw new UnsupportedOperationException();
  665. FS fs = db.getFS();
  666. of = new File(workTree, tw.getPathString());
  667. File parentFolder = of.getParentFile();
  668. if (!fs.exists(parentFolder))
  669. parentFolder.mkdirs();
  670. fos = new FileOutputStream(of);
  671. try {
  672. fmt.formatMerge(fos, result, Arrays.asList(commitNames),
  673. Constants.CHARACTER_ENCODING);
  674. } finally {
  675. fos.close();
  676. }
  677. } else if (!result.containsConflicts()) {
  678. // When working inCore, only trivial merges can be handled,
  679. // so we generate objects only in conflict free cases
  680. of = File.createTempFile("merge_", "_temp", null); //$NON-NLS-1$ //$NON-NLS-2$
  681. fos = new FileOutputStream(of);
  682. try {
  683. fmt.formatMerge(fos, result, Arrays.asList(commitNames),
  684. Constants.CHARACTER_ENCODING);
  685. } finally {
  686. fos.close();
  687. }
  688. }
  689. return of;
  690. }
  691. /**
  692. * Try to merge filemodes. If only ours or theirs have changed the mode
  693. * (compared to base) we choose that one. If ours and theirs have equal
  694. * modes return that one. If also that is not the case the modes are not
  695. * mergeable. Return {@link FileMode#MISSING} int that case.
  696. *
  697. * @param modeB
  698. * filemode found in BASE
  699. * @param modeO
  700. * filemode found in OURS
  701. * @param modeT
  702. * filemode found in THEIRS
  703. *
  704. * @return the merged filemode or {@link FileMode#MISSING} in case of a
  705. * conflict
  706. */
  707. private int mergeFileModes(int modeB, int modeO, int modeT) {
  708. if (modeO == modeT)
  709. return modeO;
  710. if (modeB == modeO)
  711. // Base equal to Ours -> chooses Theirs if that is not missing
  712. return (modeT == FileMode.MISSING.getBits()) ? modeO : modeT;
  713. if (modeB == modeT)
  714. // Base equal to Theirs -> chooses Ours if that is not missing
  715. return (modeO == FileMode.MISSING.getBits()) ? modeT : modeO;
  716. return FileMode.MISSING.getBits();
  717. }
  718. private static RawText getRawText(ObjectId id, Repository db)
  719. throws IOException {
  720. if (id.equals(ObjectId.zeroId()))
  721. return new RawText(new byte[] {});
  722. return new RawText(db.open(id, Constants.OBJ_BLOB).getCachedBytes());
  723. }
  724. private static boolean nonTree(final int mode) {
  725. return mode != 0 && !FileMode.TREE.equals(mode);
  726. }
  727. private static boolean isGitLink(final int mode) {
  728. return FileMode.GITLINK.equals(mode);
  729. }
  730. @Override
  731. public ObjectId getResultTreeId() {
  732. return (resultTree == null) ? null : resultTree.toObjectId();
  733. }
  734. /**
  735. * @param commitNames
  736. * the names of the commits as they would appear in conflict
  737. * markers
  738. */
  739. public void setCommitNames(String[] commitNames) {
  740. this.commitNames = commitNames;
  741. }
  742. /**
  743. * @return the names of the commits as they would appear in conflict
  744. * markers.
  745. */
  746. public String[] getCommitNames() {
  747. return commitNames;
  748. }
  749. /**
  750. * @return the paths with conflicts. This is a subset of the files listed
  751. * by {@link #getModifiedFiles()}
  752. */
  753. public List<String> getUnmergedPaths() {
  754. return unmergedPaths;
  755. }
  756. /**
  757. * @return the paths of files which have been modified by this merge. A
  758. * file will be modified if a content-merge works on this path or if
  759. * the merge algorithm decides to take the theirs-version. This is a
  760. * superset of the files listed by {@link #getUnmergedPaths()}.
  761. */
  762. public List<String> getModifiedFiles() {
  763. return modifiedFiles;
  764. }
  765. /**
  766. * @return a map which maps the paths of files which have to be checked out
  767. * because the merge created new fully-merged content for this file
  768. * into the index. This means: the merge wrote a new stage 0 entry
  769. * for this path.
  770. */
  771. public Map<String, DirCacheEntry> getToBeCheckedOut() {
  772. return toBeCheckedOut;
  773. }
  774. /**
  775. * @return the mergeResults
  776. */
  777. public Map<String, MergeResult<? extends Sequence>> getMergeResults() {
  778. return mergeResults;
  779. }
  780. /**
  781. * @return lists paths causing this merge to fail (not stopped because of a
  782. * conflict). <code>null</code> is returned if this merge didn't
  783. * fail.
  784. */
  785. public Map<String, MergeFailureReason> getFailingPaths() {
  786. return (failingPaths.size() == 0) ? null : failingPaths;
  787. }
  788. /**
  789. * Returns whether this merge failed (i.e. not stopped because of a
  790. * conflict)
  791. *
  792. * @return <code>true</code> if a failure occurred, <code>false</code>
  793. * otherwise
  794. */
  795. public boolean failed() {
  796. return failingPaths.size() > 0;
  797. }
  798. /**
  799. * Sets the DirCache which shall be used by this merger. If the DirCache is
  800. * not set explicitly and if this merger doesn't work in-core, this merger
  801. * will implicitly get and lock a default DirCache. If the DirCache is
  802. * explicitly set the caller is responsible to lock it in advance. Finally
  803. * the merger will call {@link DirCache#commit()} which requires that the
  804. * DirCache is locked. If the {@link #mergeImpl()} returns without throwing
  805. * an exception the lock will be released. In case of exceptions the caller
  806. * is responsible to release the lock.
  807. *
  808. * @param dc
  809. * the DirCache to set
  810. */
  811. public void setDirCache(DirCache dc) {
  812. this.dircache = dc;
  813. implicitDirCache = false;
  814. }
  815. /**
  816. * Sets the WorkingTreeIterator to be used by this merger. If no
  817. * WorkingTreeIterator is set this merger will ignore the working tree and
  818. * fail if a content merge is necessary.
  819. * <p>
  820. * TODO: enhance WorkingTreeIterator to support write operations. Then this
  821. * merger will be able to merge with a different working tree abstraction.
  822. *
  823. * @param workingTreeIterator
  824. * the workingTreeIt to set
  825. */
  826. public void setWorkingTreeIterator(WorkingTreeIterator workingTreeIterator) {
  827. this.workingTreeIterator = workingTreeIterator;
  828. }
  829. /**
  830. * The resolve conflict way of three way merging
  831. *
  832. * @param baseTree
  833. * @param headTree
  834. * @param mergeTree
  835. * @return whether the trees merged cleanly
  836. * @throws IOException
  837. * @since 3.0
  838. */
  839. protected boolean mergeTrees(AbstractTreeIterator baseTree,
  840. RevTree headTree, RevTree mergeTree) throws IOException {
  841. builder = dircache.builder();
  842. DirCacheBuildIterator buildIt = new DirCacheBuildIterator(builder);
  843. tw = new NameConflictTreeWalk(db);
  844. tw.addTree(baseTree);
  845. tw.addTree(headTree);
  846. tw.addTree(mergeTree);
  847. tw.addTree(buildIt);
  848. if (workingTreeIterator != null)
  849. tw.addTree(workingTreeIterator);
  850. while (tw.next()) {
  851. if (!processEntry(
  852. tw.getTree(T_BASE, CanonicalTreeParser.class),
  853. tw.getTree(T_OURS, CanonicalTreeParser.class),
  854. tw.getTree(T_THEIRS, CanonicalTreeParser.class),
  855. tw.getTree(T_INDEX, DirCacheBuildIterator.class),
  856. (workingTreeIterator == null) ? null : tw.getTree(T_FILE,
  857. WorkingTreeIterator.class))) {
  858. cleanUp();
  859. return false;
  860. }
  861. if (tw.isSubtree() && enterSubtree)
  862. tw.enterSubtree();
  863. }
  864. if (!inCore) {
  865. // No problem found. The only thing left to be done is to
  866. // checkout all files from "theirs" which have been selected to
  867. // go into the new index.
  868. checkout();
  869. // All content-merges are successfully done. If we can now write the
  870. // new index we are on quite safe ground. Even if the checkout of
  871. // files coming from "theirs" fails the user can work around such
  872. // failures by checking out the index again.
  873. if (!builder.commit()) {
  874. cleanUp();
  875. throw new IndexWriteException();
  876. }
  877. builder = null;
  878. } else {
  879. builder.finish();
  880. builder = null;
  881. }
  882. if (getUnmergedPaths().isEmpty() && !failed()) {
  883. resultTree = dircache.writeTree(getObjectInserter());
  884. return true;
  885. } else {
  886. resultTree = null;
  887. return false;
  888. }
  889. }
  890. }