View Javadoc
1   /*
2    * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
3    * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
4    * Copyright (C) 2008, Roger C. Soares <rogersoares@intelinet.com.br>
5    * Copyright (C) 2006, Shawn O. Pearce <spearce@spearce.org>
6    * Copyright (C) 2010, Chrisian Halstrick <christian.halstrick@sap.com> and
7    * other copyright owners as documented in the project's IP log.
8    *
9    * This program and the accompanying materials are made available under the
10   * terms of the Eclipse Distribution License v1.0 which accompanies this
11   * distribution, is reproduced below, and is available at
12   * http://www.eclipse.org/org/documents/edl-v10.php
13   *
14   * All rights reserved.
15   *
16   * Redistribution and use in source and binary forms, with or without
17   * modification, are permitted provided that the following conditions are met:
18   *
19   * - Redistributions of source code must retain the above copyright notice, this
20   * list of conditions and the following disclaimer.
21   *
22   * - Redistributions in binary form must reproduce the above copyright notice,
23   * this list of conditions and the following disclaimer in the documentation
24   * and/or other materials provided with the distribution.
25   *
26   * - Neither the name of the Eclipse Foundation, Inc. nor the names of its
27   * contributors may be used to endorse or promote products derived from this
28   * software without specific prior written permission.
29   *
30   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
31   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
34   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
35   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
36   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
37   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
38   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
39   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
40   * POSSIBILITY OF SUCH DAMAGE.
41   */
42  
43  package org.eclipse.jgit.dircache;
44  
45  import java.io.File;
46  import java.io.FileOutputStream;
47  import java.io.IOException;
48  import java.io.OutputStream;
49  import java.text.MessageFormat;
50  import java.util.ArrayList;
51  import java.util.HashMap;
52  import java.util.List;
53  import java.util.Map;
54  
55  import org.eclipse.jgit.errors.CheckoutConflictException;
56  import org.eclipse.jgit.errors.CorruptObjectException;
57  import org.eclipse.jgit.errors.IncorrectObjectTypeException;
58  import org.eclipse.jgit.errors.IndexWriteException;
59  import org.eclipse.jgit.errors.MissingObjectException;
60  import org.eclipse.jgit.internal.JGitText;
61  import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
62  import org.eclipse.jgit.lib.CoreConfig.SymLinks;
63  import org.eclipse.jgit.lib.FileMode;
64  import org.eclipse.jgit.lib.ObjectChecker;
65  import org.eclipse.jgit.lib.ObjectId;
66  import org.eclipse.jgit.lib.ObjectLoader;
67  import org.eclipse.jgit.lib.ObjectReader;
68  import org.eclipse.jgit.lib.Repository;
69  import org.eclipse.jgit.treewalk.AbstractTreeIterator;
70  import org.eclipse.jgit.treewalk.CanonicalTreeParser;
71  import org.eclipse.jgit.treewalk.EmptyTreeIterator;
72  import org.eclipse.jgit.treewalk.FileTreeIterator;
73  import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
74  import org.eclipse.jgit.treewalk.TreeWalk;
75  import org.eclipse.jgit.treewalk.WorkingTreeIterator;
76  import org.eclipse.jgit.treewalk.WorkingTreeOptions;
77  import org.eclipse.jgit.treewalk.filter.PathFilter;
78  import org.eclipse.jgit.util.FS;
79  import org.eclipse.jgit.util.FileUtils;
80  import org.eclipse.jgit.util.RawParseUtils;
81  import org.eclipse.jgit.util.SystemReader;
82  import org.eclipse.jgit.util.io.AutoCRLFOutputStream;
83  
84  /**
85   * This class handles checking out one or two trees merging with the index.
86   */
87  public class DirCacheCheckout {
88  	private Repository repo;
89  
90  	private HashMap<String, ObjectId> updated = new HashMap<String, ObjectId>();
91  
92  	private ArrayList<String> conflicts = new ArrayList<String>();
93  
94  	private ArrayList<String> removed = new ArrayList<String>();
95  
96  	private ObjectId mergeCommitTree;
97  
98  	private DirCache dc;
99  
100 	private DirCacheBuilder builder;
101 
102 	private NameConflictTreeWalk walk;
103 
104 	private ObjectId headCommitTree;
105 
106 	private WorkingTreeIterator workingTree;
107 
108 	private boolean failOnConflict = true;
109 
110 	private ArrayList<String> toBeDeleted = new ArrayList<String>();
111 
112 	private boolean emptyDirCache;
113 
114 	/**
115 	 * @return a list of updated paths and objectIds
116 	 */
117 	public Map<String, ObjectId> getUpdated() {
118 		return updated;
119 	}
120 
121 	/**
122 	 * @return a list of conflicts created by this checkout
123 	 */
124 	public List<String> getConflicts() {
125 		return conflicts;
126 	}
127 
128 	/**
129 	 * @return a list of paths (relative to the start of the working tree) of
130 	 *         files which couldn't be deleted during last call to
131 	 *         {@link #checkout()} . {@link #checkout()} detected that these
132 	 *         files should be deleted but the deletion in the filesystem failed
133 	 *         (e.g. because a file was locked). To have a consistent state of
134 	 *         the working tree these files have to be deleted by the callers of
135 	 *         {@link DirCacheCheckout}.
136 	 */
137 	public List<String> getToBeDeleted() {
138 		return toBeDeleted;
139 	}
140 
141 	/**
142 	 * @return a list of all files removed by this checkout
143 	 */
144 	public List<String> getRemoved() {
145 		return removed;
146 	}
147 
148 	/**
149 	 * Constructs a DirCacheCeckout for merging and checking out two trees (HEAD
150 	 * and mergeCommitTree) and the index.
151 	 *
152 	 * @param repo
153 	 *            the repository in which we do the checkout
154 	 * @param headCommitTree
155 	 *            the id of the tree of the head commit
156 	 * @param dc
157 	 *            the (already locked) Dircache for this repo
158 	 * @param mergeCommitTree
159 	 *            the id of the tree we want to fast-forward to
160 	 * @param workingTree
161 	 *            an iterator over the repositories Working Tree
162 	 * @throws IOException
163 	 */
164 	public DirCacheCheckout(Repository repo, ObjectId headCommitTree, DirCache dc,
165 			ObjectId mergeCommitTree, WorkingTreeIterator workingTree)
166 			throws IOException {
167 		this.repo = repo;
168 		this.dc = dc;
169 		this.headCommitTree = headCommitTree;
170 		this.mergeCommitTree = mergeCommitTree;
171 		this.workingTree = workingTree;
172 		this.emptyDirCache = (dc == null) || (dc.getEntryCount() == 0);
173 	}
174 
175 	/**
176 	 * Constructs a DirCacheCeckout for merging and checking out two trees (HEAD
177 	 * and mergeCommitTree) and the index. As iterator over the working tree
178 	 * this constructor creates a standard {@link FileTreeIterator}
179 	 *
180 	 * @param repo
181 	 *            the repository in which we do the checkout
182 	 * @param headCommitTree
183 	 *            the id of the tree of the head commit
184 	 * @param dc
185 	 *            the (already locked) Dircache for this repo
186 	 * @param mergeCommitTree
187 	 *            the id of the tree we want to fast-forward to
188 	 * @throws IOException
189 	 */
190 	public DirCacheCheckout(Repository repo, ObjectId headCommitTree,
191 			DirCache dc, ObjectId mergeCommitTree) throws IOException {
192 		this(repo, headCommitTree, dc, mergeCommitTree, new FileTreeIterator(repo));
193 	}
194 
195 	/**
196 	 * Constructs a DirCacheCeckout for checking out one tree, merging with the
197 	 * index.
198 	 *
199 	 * @param repo
200 	 *            the repository in which we do the checkout
201 	 * @param dc
202 	 *            the (already locked) Dircache for this repo
203 	 * @param mergeCommitTree
204 	 *            the id of the tree we want to fast-forward to
205 	 * @param workingTree
206 	 *            an iterator over the repositories Working Tree
207 	 * @throws IOException
208 	 */
209 	public DirCacheCheckout(Repository repo, DirCache dc,
210 			ObjectId mergeCommitTree, WorkingTreeIterator workingTree)
211 			throws IOException {
212 		this(repo, null, dc, mergeCommitTree, workingTree);
213 	}
214 
215 	/**
216 	 * Constructs a DirCacheCeckout for checking out one tree, merging with the
217 	 * index. As iterator over the working tree this constructor creates a
218 	 * standard {@link FileTreeIterator}
219 	 *
220 	 * @param repo
221 	 *            the repository in which we do the checkout
222 	 * @param dc
223 	 *            the (already locked) Dircache for this repo
224 	 * @param mergeCommitTree
225 	 *            the id of the tree of the
226 	 * @throws IOException
227 	 */
228 	public DirCacheCheckout(Repository repo, DirCache dc,
229 			ObjectId mergeCommitTree) throws IOException {
230 		this(repo, null, dc, mergeCommitTree, new FileTreeIterator(repo));
231 	}
232 
233 	/**
234 	 * Scan head, index and merge tree. Used during normal checkout or merge
235 	 * operations.
236 	 *
237 	 * @throws CorruptObjectException
238 	 * @throws IOException
239 	 */
240 	public void preScanTwoTrees() throws CorruptObjectException, IOException {
241 		removed.clear();
242 		updated.clear();
243 		conflicts.clear();
244 		walk = new NameConflictTreeWalk(repo);
245 		builder = dc.builder();
246 
247 		addTree(walk, headCommitTree);
248 		addTree(walk, mergeCommitTree);
249 		walk.addTree(new DirCacheBuildIterator(builder));
250 		walk.addTree(workingTree);
251 
252 		while (walk.next()) {
253 			processEntry(walk.getTree(0, CanonicalTreeParser.class),
254 					walk.getTree(1, CanonicalTreeParser.class),
255 					walk.getTree(2, DirCacheBuildIterator.class),
256 					walk.getTree(3, WorkingTreeIterator.class));
257 			if (walk.isSubtree())
258 				walk.enterSubtree();
259 		}
260 	}
261 
262 	private void addTree(TreeWalk tw, ObjectId id) throws MissingObjectException, IncorrectObjectTypeException, IOException {
263 		if (id == null)
264 			tw.addTree(new EmptyTreeIterator());
265 		else
266 			tw.addTree(id);
267 	}
268 
269 	/**
270 	 * Scan index and merge tree (no HEAD). Used e.g. for initial checkout when
271 	 * there is no head yet.
272 	 *
273 	 * @throws MissingObjectException
274 	 * @throws IncorrectObjectTypeException
275 	 * @throws CorruptObjectException
276 	 * @throws IOException
277 	 */
278 	public void prescanOneTree()
279 			throws MissingObjectException, IncorrectObjectTypeException,
280 			CorruptObjectException, IOException {
281 		removed.clear();
282 		updated.clear();
283 		conflicts.clear();
284 
285 		builder = dc.builder();
286 
287 		walk = new NameConflictTreeWalk(repo);
288 		addTree(walk, mergeCommitTree);
289 		walk.addTree(new DirCacheBuildIterator(builder));
290 		walk.addTree(workingTree);
291 
292 		while (walk.next()) {
293 			processEntry(walk.getTree(0, CanonicalTreeParser.class),
294 					walk.getTree(1, DirCacheBuildIterator.class),
295 					walk.getTree(2, WorkingTreeIterator.class));
296 			if (walk.isSubtree())
297 				walk.enterSubtree();
298 		}
299 		conflicts.removeAll(removed);
300 	}
301 
302 	/**
303 	 * Processing an entry in the context of {@link #prescanOneTree()} when only
304 	 * one tree is given
305 	 *
306 	 * @param m the tree to merge
307 	 * @param i the index
308 	 * @param f the working tree
309 	 * @throws IOException
310 	 */
311 	void processEntry(CanonicalTreeParser m, DirCacheBuildIterator i,
312 			WorkingTreeIterator f) throws IOException {
313 		if (m != null) {
314 			checkValidPath(m);
315 			// There is an entry in the merge commit. Means: we want to update
316 			// what's currently in the index and working-tree to that one
317 			if (i == null) {
318 				// The index entry is missing
319 				if (f != null && !FileMode.TREE.equals(f.getEntryFileMode())
320 						&& !f.isEntryIgnored()) {
321 					// don't overwrite an untracked and not ignored file
322 					conflicts.add(walk.getPathString());
323 				} else
324 					update(m.getEntryPathString(), m.getEntryObjectId(),
325 						m.getEntryFileMode());
326 			} else if (f == null || !m.idEqual(i)) {
327 				// The working tree file is missing or the merge content differs
328 				// from index content
329 				update(m.getEntryPathString(), m.getEntryObjectId(),
330 						m.getEntryFileMode());
331 			} else if (i.getDirCacheEntry() != null) {
332 				// The index contains a file (and not a folder)
333 				if (f.isModified(i.getDirCacheEntry(), true,
334 						this.walk.getObjectReader())
335 						|| i.getDirCacheEntry().getStage() != 0)
336 					// The working tree file is dirty or the index contains a
337 					// conflict
338 					update(m.getEntryPathString(), m.getEntryObjectId(),
339 							m.getEntryFileMode());
340 				else {
341 					// update the timestamp of the index with the one from the
342 					// file if not set, as we are sure to be in sync here.
343 					DirCacheEntry entry = i.getDirCacheEntry();
344 					if (entry.getLastModified() == 0)
345 						entry.setLastModified(f.getEntryLastModified());
346 					keep(entry);
347 				}
348 			} else
349 				// The index contains a folder
350 				keep(i.getDirCacheEntry());
351 		} else {
352 			// There is no entry in the merge commit. Means: we want to delete
353 			// what's currently in the index and working tree
354 			if (f != null) {
355 				// There is a file/folder for that path in the working tree
356 				if (walk.isDirectoryFileConflict()) {
357 					conflicts.add(walk.getPathString());
358 				} else {
359 					// No file/folder conflict exists. All entries are files or
360 					// all entries are folders
361 					if (i != null) {
362 						// ... and the working tree contained a file or folder
363 						// -> add it to the removed set and remove it from
364 						// conflicts set
365 						remove(i.getEntryPathString());
366 						conflicts.remove(i.getEntryPathString());
367 					} else {
368 						// untracked file, neither contained in tree to merge
369 						// nor in index
370 					}
371 				}
372 			} else {
373 				// There is no file/folder for that path in the working tree,
374 				// nor in the merge head.
375 				// The only entry we have is the index entry. Like the case
376 				// where there is a file with the same name, remove it,
377 			}
378 		}
379 	}
380 
381 	/**
382 	 * Execute this checkout
383 	 *
384 	 * @return <code>false</code> if this method could not delete all the files
385 	 *         which should be deleted (e.g. because of of the files was
386 	 *         locked). In this case {@link #getToBeDeleted()} lists the files
387 	 *         which should be tried to be deleted outside of this method.
388 	 *         Although <code>false</code> is returned the checkout was
389 	 *         successful and the working tree was updated for all other files.
390 	 *         <code>true</code> is returned when no such problem occurred
391 	 *
392 	 * @throws IOException
393 	 */
394 	public boolean checkout() throws IOException {
395 		try {
396 			return doCheckout();
397 		} finally {
398 			dc.unlock();
399 		}
400 	}
401 
402 	private boolean doCheckout() throws CorruptObjectException, IOException,
403 			MissingObjectException, IncorrectObjectTypeException,
404 			CheckoutConflictException, IndexWriteException {
405 		toBeDeleted.clear();
406 		try (ObjectReader objectReader = repo.getObjectDatabase().newReader()) {
407 			if (headCommitTree != null)
408 				preScanTwoTrees();
409 			else
410 				prescanOneTree();
411 
412 			if (!conflicts.isEmpty()) {
413 				if (failOnConflict)
414 					throw new CheckoutConflictException(conflicts.toArray(new String[conflicts.size()]));
415 				else
416 					cleanUpConflicts();
417 			}
418 
419 			// update our index
420 			builder.finish();
421 
422 			File file = null;
423 			String last = null;
424 			// when deleting files process them in the opposite order as they have
425 			// been reported. This ensures the files are deleted before we delete
426 			// their parent folders
427 			for (int i = removed.size() - 1; i >= 0; i--) {
428 				String r = removed.get(i);
429 				file = new File(repo.getWorkTree(), r);
430 				if (!file.delete() && repo.getFS().exists(file)) {
431 					// The list of stuff to delete comes from the index
432 					// which will only contain a directory if it is
433 					// a submodule, in which case we shall not attempt
434 					// to delete it. A submodule is not empty, so it
435 					// is safe to check this after a failed delete.
436 					if (!repo.getFS().isDirectory(file))
437 						toBeDeleted.add(r);
438 				} else {
439 					if (last != null && !isSamePrefix(r, last))
440 						removeEmptyParents(new File(repo.getWorkTree(), last));
441 					last = r;
442 				}
443 			}
444 			if (file != null)
445 				removeEmptyParents(file);
446 
447 			for (String path : updated.keySet()) {
448 				DirCacheEntry entry = dc.getEntry(path);
449 				if (!FileMode.GITLINK.equals(entry.getRawMode()))
450 					checkoutEntry(repo, entry, objectReader);
451 			}
452 
453 			// commit the index builder - a new index is persisted
454 			if (!builder.commit())
455 				throw new IndexWriteException();
456 		}
457 		return toBeDeleted.size() == 0;
458 	}
459 
460 	private static boolean isSamePrefix(String a, String b) {
461 		int as = a.lastIndexOf('/');
462 		int bs = b.lastIndexOf('/');
463 		return a.substring(0, as + 1).equals(b.substring(0, bs + 1));
464 	}
465 
466 	 private void removeEmptyParents(File f) {
467 		File parentFile = f.getParentFile();
468 
469 		while (parentFile != null && !parentFile.equals(repo.getWorkTree())) {
470 			if (!parentFile.delete())
471 				break;
472 			parentFile = parentFile.getParentFile();
473 		}
474 	}
475 
476 	/**
477 	 * Compares whether two pairs of ObjectId and FileMode are equal.
478 	 *
479 	 * @param id1
480 	 * @param mode1
481 	 * @param id2
482 	 * @param mode2
483 	 * @return <code>true</code> if FileModes and ObjectIds are equal.
484 	 *         <code>false</code> otherwise
485 	 */
486 	private boolean equalIdAndMode(ObjectId id1, FileMode mode1, ObjectId id2,
487 			FileMode mode2) {
488 		if (!mode1.equals(mode2))
489 			return false;
490 		return id1 != null ? id1.equals(id2) : id2 == null;
491 	}
492 
493 	/**
494 	 * Here the main work is done. This method is called for each existing path
495 	 * in head, index and merge. This method decides what to do with the
496 	 * corresponding index entry: keep it, update it, remove it or mark a
497 	 * conflict.
498 	 *
499 	 * @param h
500 	 *            the entry for the head
501 	 * @param m
502 	 *            the entry for the merge
503 	 * @param i
504 	 *            the entry for the index
505 	 * @param f
506 	 *            the file in the working tree
507 	 * @throws IOException
508 	 */
509 
510 	void processEntry(CanonicalTreeParser h, CanonicalTreeParser m,
511 			DirCacheBuildIterator i, WorkingTreeIterator f) throws IOException {
512 		DirCacheEntry dce = i != null ? i.getDirCacheEntry() : null;
513 
514 		String name = walk.getPathString();
515 
516 		if (m != null)
517 			checkValidPath(m);
518 
519 		if (i == null && m == null && h == null) {
520 			// File/Directory conflict case #20
521 			if (walk.isDirectoryFileConflict())
522 				// TODO: check whether it is always correct to report a conflict here
523 				conflict(name, null, null, null);
524 
525 			// file only exists in working tree -> ignore it
526 			return;
527 		}
528 
529 		ObjectId iId = (i == null ? null : i.getEntryObjectId());
530 		ObjectId mId = (m == null ? null : m.getEntryObjectId());
531 		ObjectId hId = (h == null ? null : h.getEntryObjectId());
532 		FileMode iMode = (i == null ? null : i.getEntryFileMode());
533 		FileMode mMode = (m == null ? null : m.getEntryFileMode());
534 		FileMode hMode = (h == null ? null : h.getEntryFileMode());
535 
536 		/**
537 		 * <pre>
538 		 *  File/Directory conflicts:
539 		 *  the following table from ReadTreeTest tells what to do in case of directory/file
540 		 *  conflicts. I give comments here
541 		 *
542 		 *      H        I       M     Clean     H==M     H==I    I==M         Result
543 		 *      ------------------------------------------------------------------
544 		 * 1    D        D       F       Y         N       Y       N           Update
545 		 * 2    D        D       F       N         N       Y       N           Conflict
546 		 * 3    D        F       D                 Y       N       N           Keep
547 		 * 4    D        F       D                 N       N       N           Conflict
548 		 * 5    D        F       F       Y         N       N       Y           Keep
549 		 * 5b   D        F       F       Y         N       N       N           Conflict
550 		 * 6    D        F       F       N         N       N       Y           Keep
551 		 * 6b   D        F       F       N         N       N       N           Conflict
552 		 * 7    F        D       F       Y         Y       N       N           Update
553 		 * 8    F        D       F       N         Y       N       N           Conflict
554 		 * 9    F        D       F                 N       N       N           Conflict
555 		 * 10   F        D       D                 N       N       Y           Keep
556 		 * 11   F        D       D                 N       N       N           Conflict
557 		 * 12   F        F       D       Y         N       Y       N           Update
558 		 * 13   F        F       D       N         N       Y       N           Conflict
559 		 * 14   F        F       D                 N       N       N           Conflict
560 		 * 15   0        F       D                 N       N       N           Conflict
561 		 * 16   0        D       F       Y         N       N       N           Update
562 		 * 17   0        D       F                 N       N       N           Conflict
563 		 * 18   F        0       D                                             Update
564 		 * 19   D        0       F                                             Update
565 		 * 20   0        0       F       N (worktree=dir)                      Conflict
566 		 * </pre>
567 		 */
568 
569 		// The information whether head,index,merge iterators are currently
570 		// pointing to file/folder/non-existing is encoded into this variable.
571 		//
572 		// To decode write down ffMask in hexadecimal form. The last digit
573 		// represents the state for the merge iterator, the second last the
574 		// state for the index iterator and the third last represents the state
575 		// for the head iterator. The hexadecimal constant "F" stands for
576 		// "file", a "D" stands for "directory" (tree), and a "0" stands for
577 		// non-existing. Symbolic links and git links are treated as File here.
578 		//
579 		// Examples:
580 		// ffMask == 0xFFD -> Head=File, Index=File, Merge=Tree
581 		// ffMask == 0xDD0 -> Head=Tree, Index=Tree, Merge=Non-Existing
582 
583 		int ffMask = 0;
584 		if (h != null)
585 			ffMask = FileMode.TREE.equals(hMode) ? 0xD00 : 0xF00;
586 		if (i != null)
587 			ffMask |= FileMode.TREE.equals(iMode) ? 0x0D0 : 0x0F0;
588 		if (m != null)
589 			ffMask |= FileMode.TREE.equals(mMode) ? 0x00D : 0x00F;
590 
591 		// Check whether we have a possible file/folder conflict. Therefore we
592 		// need a least one file and one folder.
593 		if (((ffMask & 0x222) != 0x000)
594 				&& (((ffMask & 0x00F) == 0x00D) || ((ffMask & 0x0F0) == 0x0D0) || ((ffMask & 0xF00) == 0xD00))) {
595 
596 			// There are 3*3*3=27 possible combinations of file/folder
597 			// conflicts. Some of them are not-relevant because
598 			// they represent no conflict, e.g. 0xFFF, 0xDDD, ... The following
599 			// switch processes all relevant cases.
600 			switch (ffMask) {
601 			case 0xDDF: // 1 2
602 				if (f != null && isModifiedSubtree_IndexWorkingtree(name)) {
603 					conflict(name, dce, h, m); // 1
604 				} else {
605 					update(name, mId, mMode); // 2
606 				}
607 
608 				break;
609 			case 0xDFD: // 3 4
610 				keep(dce);
611 				break;
612 			case 0xF0D: // 18
613 				remove(name);
614 				break;
615 			case 0xDFF: // 5 5b 6 6b
616 				if (equalIdAndMode(iId, iMode, mId, mMode))
617 					keep(dce); // 5 6
618 				else
619 					conflict(name, dce, h, m); // 5b 6b
620 				break;
621 			case 0xFDD: // 10 11
622 				// TODO: make use of tree extension as soon as available in jgit
623 				// we would like to do something like
624 				// if (!equalIdAndMode(iId, iMode, mId, mMode)
625 				//   conflict(name, i.getDirCacheEntry(), h, m);
626 				// But since we don't know the id of a tree in the index we do
627 				// nothing here and wait that conflicts between index and merge
628 				// are found later
629 				break;
630 			case 0xD0F: // 19
631 				update(name, mId, mMode);
632 				break;
633 			case 0xDF0: // conflict without a rule
634 			case 0x0FD: // 15
635 				conflict(name, dce, h, m);
636 				break;
637 			case 0xFDF: // 7 8 9
638 				if (equalIdAndMode(hId, hMode, mId, mMode)) {
639 					if (isModifiedSubtree_IndexWorkingtree(name))
640 						conflict(name, dce, h, m); // 8
641 					else
642 						update(name, mId, mMode); // 7
643 				} else
644 					conflict(name, dce, h, m); // 9
645 				break;
646 			case 0xFD0: // keep without a rule
647 				keep(dce);
648 				break;
649 			case 0xFFD: // 12 13 14
650 				if (equalIdAndMode(hId, hMode, iId, iMode))
651 					if (f != null
652 							&& f.isModified(dce, true,
653 									this.walk.getObjectReader()))
654 						conflict(name, dce, h, m); // 13
655 					else
656 						remove(name); // 12
657 				else
658 					conflict(name, dce, h, m); // 14
659 				break;
660 			case 0x0DF: // 16 17
661 				if (!isModifiedSubtree_IndexWorkingtree(name))
662 					update(name, mId, mMode);
663 				else
664 					conflict(name, dce, h, m);
665 				break;
666 			default:
667 				keep(dce);
668 			}
669 			return;
670 		}
671 
672 		// if we have no file at all then there is nothing to do
673 		if ((ffMask & 0x222) == 0
674 				&& (f == null || FileMode.TREE.equals(f.getEntryFileMode())))
675 			return;
676 
677 		if ((ffMask == 0x00F) && f != null && FileMode.TREE.equals(f.getEntryFileMode())) {
678 			// File/Directory conflict case #20
679 			conflict(name, null, h, m);
680 			return;
681 		}
682 
683 		if (i == null) {
684 			// Nothing in Index
685 			// At least one of Head, Index, Merge is not empty
686 			// make sure not to overwrite untracked files
687 			if (f != null && !f.isEntryIgnored()) {
688 				// A submodule is not a file. We should ignore it
689 				if (!FileMode.GITLINK.equals(mMode)) {
690 					// a dirty worktree: the index is empty but we have a
691 					// workingtree-file
692 					if (mId == null
693 							|| !equalIdAndMode(mId, mMode,
694 									f.getEntryObjectId(), f.getEntryFileMode())) {
695 						conflict(name, null, h, m);
696 						return;
697 					}
698 				}
699 			}
700 
701 			/**
702 			 * <pre>
703 			 * 	          I (index)     H        M     H==M  Result
704 			 * 	        -------------------------------------------
705 			 * 	        0 nothing    nothing  nothing        (does not happen)
706 			 * 	        1 nothing    nothing  exists         use M
707 			 * 	        2 nothing    exists   nothing        remove path from index
708 			 * 	        3 nothing    exists   exists   yes   keep index if not in initial checkout
709 			 *                                               , otherwise use M
710 			 * 	          nothing    exists   exists   no    fail
711 			 * </pre>
712 			 */
713 
714 			if (h == null)
715 				// Nothing in Head
716 				// Nothing in Index
717 				// At least one of Head, Index, Merge is not empty
718 				// -> only Merge contains something for this path. Use it!
719 				// Potentially update the file
720 				update(name, mId, mMode); // 1
721 			else if (m == null)
722 				// Nothing in Merge
723 				// Something in Head
724 				// Nothing in Index
725 				// -> only Head contains something for this path and it should
726 				// be deleted. Potentially removes the file!
727 				remove(name); // 2
728 			else { // 3
729 				// Something in Merge
730 				// Something in Head
731 				// Nothing in Index
732 				// -> Head and Merge contain something (maybe not the same) and
733 				// in the index there is nothing (e.g. 'git rm ...' was
734 				// called before). Ignore the cached deletion and use what we
735 				// find in Merge. Potentially updates the file.
736 				if (equalIdAndMode(hId, hMode, mId, mMode)) {
737 					if (emptyDirCache)
738 						update(name, mId, mMode);
739 					else
740 						keep(dce);
741 				} else
742 					conflict(name, dce, h, m);
743 			}
744 		} else {
745 			// Something in Index
746 			if (h == null) {
747 				// Nothing in Head
748 				// Something in Index
749 				/**
750 				 * <pre>
751 				 * 	          clean I==H  I==M       H        M        Result
752 				 * 	         -----------------------------------------------------
753 				 * 	        4 yes   N/A   N/A     nothing  nothing  keep index
754 				 * 	        5 no    N/A   N/A     nothing  nothing  keep index
755 				 *
756 				 * 	        6 yes   N/A   yes     nothing  exists   keep index
757 				 * 	        7 no    N/A   yes     nothing  exists   keep index
758 				 * 	        8 yes   N/A   no      nothing  exists   fail
759 				 * 	        9 no    N/A   no      nothing  exists   fail
760 				 * </pre>
761 				 */
762 
763 				if (m == null
764 						|| !isModified_IndexTree(name, iId, iMode, mId, mMode,
765 								mergeCommitTree)) {
766 					// Merge contains nothing or the same as Index
767 					// Nothing in Head
768 					// Something in Index
769 					if (m==null && walk.isDirectoryFileConflict()) {
770 						// Nothing in Merge and current path is part of
771 						// File/Folder conflict
772 						// Nothing in Head
773 						// Something in Index
774 						if (dce != null
775 								&& (f == null || f.isModified(dce, true,
776 										this.walk.getObjectReader())))
777 							// No file or file is dirty
778 							// Nothing in Merge and current path is part of
779 							// File/Folder conflict
780 							// Nothing in Head
781 							// Something in Index
782 							// -> File folder conflict and Merge wants this
783 							// path to be removed. Since the file is dirty
784 							// report a conflict
785 							conflict(name, dce, h, m);
786 						else
787 							// A file is present and file is not dirty
788 							// Nothing in Merge and current path is part of
789 							// File/Folder conflict
790 							// Nothing in Head
791 							// Something in Index
792 							// -> File folder conflict and Merge wants this path
793 							// to be removed. Since the file is not dirty remove
794 							// file and index entry
795 							remove(name);
796 					} else
797 						// Something in Merge or current path is not part of
798 						// File/Folder conflict
799 						// Merge contains nothing or the same as Index
800 						// Nothing in Head
801 						// Something in Index
802 						// -> Merge contains nothing new. Keep the index.
803 						keep(dce);
804 				} else
805 					// Merge contains something and it is not the same as Index
806 					// Nothing in Head
807 					// Something in Index
808 					// -> Index contains something new (different from Head)
809 					// and Merge is different from Index. Report a conflict
810 					conflict(name, dce, h, m);
811 			} else if (m == null) {
812 				// Nothing in Merge
813 				// Something in Head
814 				// Something in Index
815 
816 				/**
817 				 * <pre>
818 				 * 	           clean I==H  I==M       H        M        Result
819 				 * 	         -----------------------------------------------------
820 				 * 	        10 yes   yes   N/A     exists   nothing  remove path from index
821 				 * 	        11 no    yes   N/A     exists   nothing  keep file
822 				 * 	        12 yes   no    N/A     exists   nothing  fail
823 				 * 	        13 no    no    N/A     exists   nothing  fail
824 				 * </pre>
825 				 */
826 
827 				if (iMode == FileMode.GITLINK) {
828 					// A submodule in Index
829 					// Nothing in Merge
830 					// Something in Head
831 					// Submodules that disappear from the checkout must
832 					// be removed from the index, but not deleted from disk.
833 					remove(name);
834 				} else {
835 					// Something different from a submodule in Index
836 					// Nothing in Merge
837 					// Something in Head
838 					if (!isModified_IndexTree(name, iId, iMode, hId, hMode,
839 							headCommitTree)) {
840 						// Index contains the same as Head
841 						// Something different from a submodule in Index
842 						// Nothing in Merge
843 						// Something in Head
844 						if (f != null
845 								&& f.isModified(dce, true,
846 										this.walk.getObjectReader())) {
847 							// file is dirty
848 							// Index contains the same as Head
849 							// Something different from a submodule in Index
850 							// Nothing in Merge
851 							// Something in Head
852 
853 							if (!FileMode.TREE.equals(f.getEntryFileMode())
854 									&& FileMode.TREE.equals(iMode))
855 								// The workingtree contains a file and the index semantically contains a folder.
856 								// Git considers the workingtree file as untracked. Just keep the untracked file.
857 								return;
858 							else
859 								// -> file is dirty and tracked but is should be
860 								// removed. That's a conflict
861 								conflict(name, dce, h, m);
862 						} else
863 							// file doesn't exist or is clean
864 							// Index contains the same as Head
865 							// Something different from a submodule in Index
866 							// Nothing in Merge
867 							// Something in Head
868 							// -> Remove from index and delete the file
869 							remove(name);
870 					} else
871 						// Index contains something different from Head
872 						// Something different from a submodule in Index
873 						// Nothing in Merge
874 						// Something in Head
875 						// -> Something new is in index (and maybe even on the
876 						// filesystem). But Merge wants the path to be removed.
877 						// Report a conflict
878 						conflict(name, dce, h, m);
879 				}
880 			} else {
881 				// Something in Merge
882 				// Something in Head
883 				// Something in Index
884 				if (!equalIdAndMode(hId, hMode, mId, mMode)
885 						&& isModified_IndexTree(name, iId, iMode, hId, hMode,
886 								headCommitTree)
887 						&& isModified_IndexTree(name, iId, iMode, mId, mMode,
888 								mergeCommitTree))
889 					// All three contents in Head, Merge, Index differ from each
890 					// other
891 					// -> All contents differ. Report a conflict.
892 					conflict(name, dce, h, m);
893 				else
894 					// At least two of the contents of Head, Index, Merge
895 					// are the same
896 					// Something in Merge
897 					// Something in Head
898 					// Something in Index
899 
900 				if (!isModified_IndexTree(name, iId, iMode, hId, hMode,
901 						headCommitTree)
902 						&& isModified_IndexTree(name, iId, iMode, mId, mMode,
903 								mergeCommitTree)) {
904 						// Head contains the same as Index. Merge differs
905 						// Something in Merge
906 
907 					// For submodules just update the index with the new SHA-1
908 					if (dce != null
909 							&& FileMode.GITLINK.equals(dce.getFileMode())) {
910 						// Index and Head contain the same submodule. Merge
911 						// differs
912 						// Something in Merge
913 						// -> Nothing new in index. Move to merge.
914 						// Potentially updates the file
915 
916 						// TODO check that we don't overwrite some unsaved
917 						// file content
918 						update(name, mId, mMode);
919 					} else if (dce != null
920 							&& (f != null && f.isModified(dce, true,
921 									this.walk.getObjectReader()))) {
922 						// File exists and is dirty
923 						// Head and Index don't contain a submodule
924 						// Head contains the same as Index. Merge differs
925 						// Something in Merge
926 						// -> Merge wants the index and file to be updated
927 						// but the file is dirty. Report a conflict
928 						conflict(name, dce, h, m);
929 					} else {
930 						// File doesn't exist or is clean
931 						// Head and Index don't contain a submodule
932 						// Head contains the same as Index. Merge differs
933 						// Something in Merge
934 						// -> Standard case when switching between branches:
935 						// Nothing new in index but something different in
936 						// Merge. Update index and file
937 						update(name, mId, mMode);
938 					}
939 				} else {
940 					// Head differs from index or merge is same as index
941 					// At least two of the contents of Head, Index, Merge
942 					// are the same
943 					// Something in Merge
944 					// Something in Head
945 					// Something in Index
946 
947 					// Can be formulated as: Either all three states are
948 					// equal or Merge is equal to Head or Index and differs
949 					// to the other one.
950 					// -> In all three cases we don't touch index and file.
951 
952 					keep(dce);
953 				}
954 			}
955 		}
956 	}
957 
958 	/**
959 	 * A conflict is detected - add the three different stages to the index
960 	 * @param path the path of the conflicting entry
961 	 * @param e the previous index entry
962 	 * @param h the first tree you want to merge (the HEAD)
963 	 * @param m the second tree you want to merge
964 	 */
965 	private void conflict(String path, DirCacheEntry e, AbstractTreeIterator h, AbstractTreeIterator m) {
966 		conflicts.add(path);
967 
968 		DirCacheEntry entry;
969 		if (e != null) {
970 			entry = new DirCacheEntry(e.getPathString(), DirCacheEntry.STAGE_1);
971 			entry.copyMetaData(e, true);
972 			builder.add(entry);
973 		}
974 
975 		if (h != null && !FileMode.TREE.equals(h.getEntryFileMode())) {
976 			entry = new DirCacheEntry(h.getEntryPathString(), DirCacheEntry.STAGE_2);
977 			entry.setFileMode(h.getEntryFileMode());
978 			entry.setObjectId(h.getEntryObjectId());
979 			builder.add(entry);
980 		}
981 
982 		if (m != null && !FileMode.TREE.equals(m.getEntryFileMode())) {
983 			entry = new DirCacheEntry(m.getEntryPathString(), DirCacheEntry.STAGE_3);
984 			entry.setFileMode(m.getEntryFileMode());
985 			entry.setObjectId(m.getEntryObjectId());
986 			builder.add(entry);
987 		}
988 	}
989 
990 	private void keep(DirCacheEntry e) {
991 		if (e != null && !FileMode.TREE.equals(e.getFileMode()))
992 			builder.add(e);
993 	}
994 
995 	private void remove(String path) {
996 		removed.add(path);
997 	}
998 
999 	private void update(String path, ObjectId mId, FileMode mode) {
1000 		if (!FileMode.TREE.equals(mode)) {
1001 			updated.put(path, mId);
1002 			DirCacheEntry entry = new DirCacheEntry(path, DirCacheEntry.STAGE_0);
1003 			entry.setObjectId(mId);
1004 			entry.setFileMode(mode);
1005 			builder.add(entry);
1006 		}
1007 	}
1008 
1009 	/**
1010 	 * If <code>true</code>, will scan first to see if it's possible to check
1011 	 * out, otherwise throw {@link CheckoutConflictException}. If
1012 	 * <code>false</code>, it will silently deal with the problem.
1013 	 *
1014 	 * @param failOnConflict
1015 	 */
1016 	public void setFailOnConflict(boolean failOnConflict) {
1017 		this.failOnConflict = failOnConflict;
1018 	}
1019 
1020 	/**
1021 	 * This method implements how to handle conflicts when
1022 	 * {@link #failOnConflict} is false
1023 	 *
1024 	 * @throws CheckoutConflictException
1025 	 */
1026 	private void cleanUpConflicts() throws CheckoutConflictException {
1027 		// TODO: couldn't we delete unsaved worktree content here?
1028 		for (String c : conflicts) {
1029 			File conflict = new File(repo.getWorkTree(), c);
1030 			if (!conflict.delete())
1031 				throw new CheckoutConflictException(MessageFormat.format(
1032 						JGitText.get().cannotDeleteFile, c));
1033 			removeEmptyParents(conflict);
1034 		}
1035 		for (String r : removed) {
1036 			File file = new File(repo.getWorkTree(), r);
1037 			if (!file.delete())
1038 				throw new CheckoutConflictException(
1039 						MessageFormat.format(JGitText.get().cannotDeleteFile,
1040 								file.getAbsolutePath()));
1041 			removeEmptyParents(file);
1042 		}
1043 	}
1044 
1045 	/**
1046 	 * Checks whether the subtree starting at a given path differs between Index and
1047 	 * workingtree.
1048 	 *
1049 	 * @param path
1050 	 * @return true if the subtrees differ
1051 	 * @throws CorruptObjectException
1052 	 * @throws IOException
1053 	 */
1054 	private boolean isModifiedSubtree_IndexWorkingtree(String path)
1055 			throws CorruptObjectException, IOException {
1056 		try (NameConflictTreeWalk tw = new NameConflictTreeWalk(repo)) {
1057 			tw.addTree(new DirCacheIterator(dc));
1058 			tw.addTree(new FileTreeIterator(repo));
1059 			tw.setRecursive(true);
1060 			tw.setFilter(PathFilter.create(path));
1061 			DirCacheIterator dcIt;
1062 			WorkingTreeIterator wtIt;
1063 			while (tw.next()) {
1064 				dcIt = tw.getTree(0, DirCacheIterator.class);
1065 				wtIt = tw.getTree(1, WorkingTreeIterator.class);
1066 				if (dcIt == null || wtIt == null)
1067 					return true;
1068 				if (wtIt.isModified(dcIt.getDirCacheEntry(), true,
1069 						this.walk.getObjectReader())) {
1070 					return true;
1071 				}
1072 			}
1073 			return false;
1074 		}
1075 	}
1076 
1077 	private boolean isModified_IndexTree(String path, ObjectId iId,
1078 			FileMode iMode, ObjectId tId, FileMode tMode, ObjectId rootTree)
1079 			throws CorruptObjectException, IOException {
1080 		if (iMode != tMode)
1081 			return true;
1082 		if (FileMode.TREE.equals(iMode)
1083 				&& (iId == null || ObjectId.zeroId().equals(iId)))
1084 			return isModifiedSubtree_IndexTree(path, rootTree);
1085 		else
1086 			return !equalIdAndMode(iId, iMode, tId, tMode);
1087 	}
1088 
1089 	/**
1090 	 * Checks whether the subtree starting at a given path differs between Index and
1091 	 * some tree.
1092 	 *
1093 	 * @param path
1094 	 * @param tree
1095 	 *            the tree to compare
1096 	 * @return true if the subtrees differ
1097 	 * @throws CorruptObjectException
1098 	 * @throws IOException
1099 	 */
1100 	private boolean isModifiedSubtree_IndexTree(String path, ObjectId tree)
1101 			throws CorruptObjectException, IOException {
1102 		try (NameConflictTreeWalk tw = new NameConflictTreeWalk(repo)) {
1103 			tw.addTree(new DirCacheIterator(dc));
1104 			tw.addTree(tree);
1105 			tw.setRecursive(true);
1106 			tw.setFilter(PathFilter.create(path));
1107 			while (tw.next()) {
1108 				AbstractTreeIterator dcIt = tw.getTree(0,
1109 						DirCacheIterator.class);
1110 				AbstractTreeIterator treeIt = tw.getTree(1,
1111 						AbstractTreeIterator.class);
1112 				if (dcIt == null || treeIt == null)
1113 					return true;
1114 				if (dcIt.getEntryRawMode() != treeIt.getEntryRawMode())
1115 					return true;
1116 				if (!dcIt.getEntryObjectId().equals(treeIt.getEntryObjectId()))
1117 					return true;
1118 			}
1119 			return false;
1120 		}
1121 	}
1122 
1123 	/**
1124 	 * Updates the file in the working tree with content and mode from an entry
1125 	 * in the index. The new content is first written to a new temporary file in
1126 	 * the same directory as the real file. Then that new file is renamed to the
1127 	 * final filename.
1128 	 *
1129 	 * <p>
1130 	 * TODO: this method works directly on File IO, we may need another
1131 	 * abstraction (like WorkingTreeIterator). This way we could tell e.g.
1132 	 * Eclipse that Files in the workspace got changed
1133 	 * </p>
1134 	 *
1135 	 * @param repo
1136 	 *            repository managing the destination work tree.
1137 	 * @param entry
1138 	 *            the entry containing new mode and content
1139 	 * @param or
1140 	 *            object reader to use for checkout
1141 	 * @throws IOException
1142 	 * @since 3.6
1143 	 */
1144 	public static void checkoutEntry(Repository repo, DirCacheEntry entry,
1145 			ObjectReader or) throws IOException {
1146 		ObjectLoader ol = or.open(entry.getObjectId());
1147 		File f = new File(repo.getWorkTree(), entry.getPathString());
1148 		File parentDir = f.getParentFile();
1149 		FileUtils.mkdirs(parentDir, true);
1150 		FS fs = repo.getFS();
1151 		WorkingTreeOptions opt = repo.getConfig().get(WorkingTreeOptions.KEY);
1152 		if (entry.getFileMode() == FileMode.SYMLINK
1153 				&& opt.getSymLinks() == SymLinks.TRUE) {
1154 			byte[] bytes = ol.getBytes();
1155 			String target = RawParseUtils.decode(bytes);
1156 			fs.createSymLink(f, target);
1157 			entry.setLength(bytes.length);
1158 			entry.setLastModified(fs.lastModified(f));
1159 			return;
1160 		}
1161 
1162 		File tmpFile = File.createTempFile(
1163 				"._" + f.getName(), null, parentDir); //$NON-NLS-1$
1164 		OutputStream channel = new FileOutputStream(tmpFile);
1165 		if (opt.getAutoCRLF() == AutoCRLF.TRUE)
1166 			channel = new AutoCRLFOutputStream(channel);
1167 		try {
1168 			ol.copyTo(channel);
1169 		} finally {
1170 			channel.close();
1171 		}
1172 		entry.setLength(opt.getAutoCRLF() == AutoCRLF.TRUE ? //
1173 				tmpFile.length() // AutoCRLF wants on-disk-size
1174 				: (int) ol.getSize());
1175 
1176 		if (opt.isFileMode() && fs.supportsExecute()) {
1177 			if (FileMode.EXECUTABLE_FILE.equals(entry.getRawMode())) {
1178 				if (!fs.canExecute(tmpFile))
1179 					fs.setExecute(tmpFile, true);
1180 			} else {
1181 				if (fs.canExecute(tmpFile))
1182 					fs.setExecute(tmpFile, false);
1183 			}
1184 		}
1185 		try {
1186 			FileUtils.rename(tmpFile, f);
1187 		} catch (IOException e) {
1188 			throw new IOException(MessageFormat.format(
1189 					JGitText.get().renameFileFailed, tmpFile.getPath(),
1190 					f.getPath()));
1191 		}
1192 		entry.setLastModified(f.lastModified());
1193 	}
1194 
1195 	@SuppressWarnings("deprecation")
1196 	private static void checkValidPath(CanonicalTreeParser t)
1197 			throws InvalidPathException {
1198 		ObjectChecker chk = new ObjectChecker()
1199 			.setSafeForWindows(SystemReader.getInstance().isWindows())
1200 			.setSafeForMacOS(SystemReader.getInstance().isMacOS());
1201 		for (CanonicalTreeParser i = t; i != null; i = i.getParent())
1202 			checkValidPathSegment(chk, i);
1203 	}
1204 
1205 	/**
1206 	 * Check if path is a valid path for a checked out file name or ref name.
1207 	 *
1208 	 * @param path
1209 	 * @throws InvalidPathException
1210 	 *             if the path is invalid
1211 	 * @since 3.3
1212 	 */
1213 	static void checkValidPath(String path) throws InvalidPathException {
1214 		try {
1215 			SystemReader.getInstance().checkPath(path);
1216 		} catch (CorruptObjectException e) {
1217 			InvalidPathException p = new InvalidPathException(path);
1218 			p.initCause(e);
1219 			throw p;
1220 		}
1221 	}
1222 
1223 	private static void checkValidPathSegment(ObjectChecker chk,
1224 			CanonicalTreeParser t) throws InvalidPathException {
1225 		try {
1226 			int ptr = t.getNameOffset();
1227 			int end = ptr + t.getNameLength();
1228 			chk.checkPathSegment(t.getEntryPathBuffer(), ptr, end);
1229 		} catch (CorruptObjectException err) {
1230 			String path = t.getEntryPathString();
1231 			InvalidPathException i = new InvalidPathException(path);
1232 			i.initCause(err);
1233 			throw i;
1234 		}
1235 	}
1236 }