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 static org.eclipse.jgit.treewalk.TreeWalk.OperationType.CHECKOUT_OP;
46  
47  import java.io.File;
48  import java.io.FileOutputStream;
49  import java.io.IOException;
50  import java.io.OutputStream;
51  import java.nio.file.StandardCopyOption;
52  import java.text.MessageFormat;
53  import java.util.ArrayList;
54  import java.util.HashMap;
55  import java.util.Iterator;
56  import java.util.List;
57  import java.util.Map;
58  
59  import org.eclipse.jgit.api.errors.FilterFailedException;
60  import org.eclipse.jgit.attributes.FilterCommand;
61  import org.eclipse.jgit.attributes.FilterCommandRegistry;
62  import org.eclipse.jgit.errors.CheckoutConflictException;
63  import org.eclipse.jgit.errors.CorruptObjectException;
64  import org.eclipse.jgit.errors.IncorrectObjectTypeException;
65  import org.eclipse.jgit.errors.IndexWriteException;
66  import org.eclipse.jgit.errors.MissingObjectException;
67  import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
68  import org.eclipse.jgit.internal.JGitText;
69  import org.eclipse.jgit.lib.Constants;
70  import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
71  import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
72  import org.eclipse.jgit.lib.CoreConfig.SymLinks;
73  import org.eclipse.jgit.lib.FileMode;
74  import org.eclipse.jgit.lib.NullProgressMonitor;
75  import org.eclipse.jgit.lib.ObjectChecker;
76  import org.eclipse.jgit.lib.ObjectId;
77  import org.eclipse.jgit.lib.ObjectLoader;
78  import org.eclipse.jgit.lib.ObjectReader;
79  import org.eclipse.jgit.lib.Repository;
80  import org.eclipse.jgit.treewalk.AbstractTreeIterator;
81  import org.eclipse.jgit.treewalk.CanonicalTreeParser;
82  import org.eclipse.jgit.treewalk.EmptyTreeIterator;
83  import org.eclipse.jgit.treewalk.FileTreeIterator;
84  import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
85  import org.eclipse.jgit.treewalk.TreeWalk;
86  import org.eclipse.jgit.treewalk.WorkingTreeIterator;
87  import org.eclipse.jgit.treewalk.WorkingTreeOptions;
88  import org.eclipse.jgit.treewalk.filter.PathFilter;
89  import org.eclipse.jgit.util.FS;
90  import org.eclipse.jgit.util.FS.ExecutionResult;
91  import org.eclipse.jgit.util.FileUtils;
92  import org.eclipse.jgit.util.IntList;
93  import org.eclipse.jgit.util.RawParseUtils;
94  import org.eclipse.jgit.util.SystemReader;
95  import org.eclipse.jgit.util.io.EolStreamTypeUtil;
96  import org.slf4j.Logger;
97  import org.slf4j.LoggerFactory;
98  
99  /**
100  * This class handles checking out one or two trees merging with the index.
101  */
102 public class DirCacheCheckout {
103 	private static Logger LOG = LoggerFactory.getLogger(DirCacheCheckout.class);
104 
105 	private static final int MAX_EXCEPTION_TEXT_SIZE = 10 * 1024;
106 
107 	/**
108 	 * Metadata used in checkout process
109 	 *
110 	 * @since 4.3
111 	 */
112 	public static class CheckoutMetadata {
113 		/** git attributes */
114 		public final EolStreamType eolStreamType;
115 
116 		/** filter command to apply */
117 		public final String smudgeFilterCommand;
118 
119 		/**
120 		 * @param eolStreamType
121 		 * @param smudgeFilterCommand
122 		 */
123 		public CheckoutMetadata(EolStreamType eolStreamType,
124 				String smudgeFilterCommand) {
125 			this.eolStreamType = eolStreamType;
126 			this.smudgeFilterCommand = smudgeFilterCommand;
127 		}
128 
129 		static CheckoutMetadata EMPTY = new CheckoutMetadata(
130 				EolStreamType.DIRECT, null);
131 	}
132 
133 	private Repository repo;
134 
135 	private HashMap<String, CheckoutMetadata> updated = new HashMap<>();
136 
137 	private ArrayList<String> conflicts = new ArrayList<>();
138 
139 	private ArrayList<String> removed = new ArrayList<>();
140 
141 	private ObjectId mergeCommitTree;
142 
143 	private DirCache dc;
144 
145 	private DirCacheBuilder builder;
146 
147 	private NameConflictTreeWalk walk;
148 
149 	private ObjectId headCommitTree;
150 
151 	private WorkingTreeIterator workingTree;
152 
153 	private boolean failOnConflict = true;
154 
155 	private ArrayList<String> toBeDeleted = new ArrayList<>();
156 
157 	private boolean emptyDirCache;
158 
159 	private boolean performingCheckout;
160 
161 	/**
162 	 * Get list of updated paths and smudgeFilterCommands
163 	 *
164 	 * @return a list of updated paths and smudgeFilterCommands
165 	 */
166 	public Map<String, CheckoutMetadata> getUpdated() {
167 		return updated;
168 	}
169 
170 	/**
171 	 * Get a list of conflicts created by this checkout
172 	 *
173 	 * @return a list of conflicts created by this checkout
174 	 */
175 	public List<String> getConflicts() {
176 		return conflicts;
177 	}
178 
179 	/**
180 	 * Get list of paths of files which couldn't be deleted during last call to
181 	 * {@link #checkout()}
182 	 *
183 	 * @return a list of paths (relative to the start of the working tree) of
184 	 *         files which couldn't be deleted during last call to
185 	 *         {@link #checkout()} . {@link #checkout()} detected that these
186 	 *         files should be deleted but the deletion in the filesystem failed
187 	 *         (e.g. because a file was locked). To have a consistent state of
188 	 *         the working tree these files have to be deleted by the callers of
189 	 *         {@link org.eclipse.jgit.dircache.DirCacheCheckout}.
190 	 */
191 	public List<String> getToBeDeleted() {
192 		return toBeDeleted;
193 	}
194 
195 	/**
196 	 * Get list of all files removed by this checkout
197 	 *
198 	 * @return a list of all files removed by this checkout
199 	 */
200 	public List<String> getRemoved() {
201 		return removed;
202 	}
203 
204 	/**
205 	 * Constructs a DirCacheCeckout for merging and checking out two trees (HEAD
206 	 * and mergeCommitTree) and the index.
207 	 *
208 	 * @param repo
209 	 *            the repository in which we do the checkout
210 	 * @param headCommitTree
211 	 *            the id of the tree of the head commit
212 	 * @param dc
213 	 *            the (already locked) Dircache for this repo
214 	 * @param mergeCommitTree
215 	 *            the id of the tree we want to fast-forward to
216 	 * @param workingTree
217 	 *            an iterator over the repositories Working Tree
218 	 * @throws java.io.IOException
219 	 */
220 	public DirCacheCheckout(Repository repo, ObjectId headCommitTree, DirCache dc,
221 			ObjectId mergeCommitTree, WorkingTreeIterator workingTree)
222 			throws IOException {
223 		this.repo = repo;
224 		this.dc = dc;
225 		this.headCommitTree = headCommitTree;
226 		this.mergeCommitTree = mergeCommitTree;
227 		this.workingTree = workingTree;
228 		this.emptyDirCache = (dc == null) || (dc.getEntryCount() == 0);
229 	}
230 
231 	/**
232 	 * Constructs a DirCacheCeckout for merging and checking out two trees (HEAD
233 	 * and mergeCommitTree) and the index. As iterator over the working tree
234 	 * this constructor creates a standard
235 	 * {@link org.eclipse.jgit.treewalk.FileTreeIterator}
236 	 *
237 	 * @param repo
238 	 *            the repository in which we do the checkout
239 	 * @param headCommitTree
240 	 *            the id of the tree of the head commit
241 	 * @param dc
242 	 *            the (already locked) Dircache for this repo
243 	 * @param mergeCommitTree
244 	 *            the id of the tree we want to fast-forward to
245 	 * @throws java.io.IOException
246 	 */
247 	public DirCacheCheckout(Repository repo, ObjectId headCommitTree,
248 			DirCache dc, ObjectId mergeCommitTree) throws IOException {
249 		this(repo, headCommitTree, dc, mergeCommitTree, new FileTreeIterator(repo));
250 	}
251 
252 	/**
253 	 * Constructs a DirCacheCeckout for checking out one tree, merging with the
254 	 * index.
255 	 *
256 	 * @param repo
257 	 *            the repository in which we do the checkout
258 	 * @param dc
259 	 *            the (already locked) Dircache for this repo
260 	 * @param mergeCommitTree
261 	 *            the id of the tree we want to fast-forward to
262 	 * @param workingTree
263 	 *            an iterator over the repositories Working Tree
264 	 * @throws java.io.IOException
265 	 */
266 	public DirCacheCheckout(Repository repo, DirCache dc,
267 			ObjectId mergeCommitTree, WorkingTreeIterator workingTree)
268 			throws IOException {
269 		this(repo, null, dc, mergeCommitTree, workingTree);
270 	}
271 
272 	/**
273 	 * Constructs a DirCacheCeckout for checking out one tree, merging with the
274 	 * index. As iterator over the working tree this constructor creates a
275 	 * standard {@link org.eclipse.jgit.treewalk.FileTreeIterator}
276 	 *
277 	 * @param repo
278 	 *            the repository in which we do the checkout
279 	 * @param dc
280 	 *            the (already locked) Dircache for this repo
281 	 * @param mergeCommitTree
282 	 *            the id of the tree of the
283 	 * @throws java.io.IOException
284 	 */
285 	public DirCacheCheckout(Repository repo, DirCache dc,
286 			ObjectId mergeCommitTree) throws IOException {
287 		this(repo, null, dc, mergeCommitTree, new FileTreeIterator(repo));
288 	}
289 
290 	/**
291 	 * Scan head, index and merge tree. Used during normal checkout or merge
292 	 * operations.
293 	 *
294 	 * @throws org.eclipse.jgit.errors.CorruptObjectException
295 	 * @throws java.io.IOException
296 	 */
297 	public void preScanTwoTrees() throws CorruptObjectException, IOException {
298 		removed.clear();
299 		updated.clear();
300 		conflicts.clear();
301 		walk = new NameConflictTreeWalk(repo);
302 		builder = dc.builder();
303 
304 		addTree(walk, headCommitTree);
305 		addTree(walk, mergeCommitTree);
306 		int dciPos = walk.addTree(new DirCacheBuildIterator(builder));
307 		walk.addTree(workingTree);
308 		workingTree.setDirCacheIterator(walk, dciPos);
309 
310 		while (walk.next()) {
311 			processEntry(walk.getTree(0, CanonicalTreeParser.class),
312 					walk.getTree(1, CanonicalTreeParser.class),
313 					walk.getTree(2, DirCacheBuildIterator.class),
314 					walk.getTree(3, WorkingTreeIterator.class));
315 			if (walk.isSubtree())
316 				walk.enterSubtree();
317 		}
318 	}
319 
320 	private void addTree(TreeWalk tw, ObjectId id) throws MissingObjectException, IncorrectObjectTypeException, IOException {
321 		if (id == null)
322 			tw.addTree(new EmptyTreeIterator());
323 		else
324 			tw.addTree(id);
325 	}
326 
327 	/**
328 	 * Scan index and merge tree (no HEAD). Used e.g. for initial checkout when
329 	 * there is no head yet.
330 	 *
331 	 * @throws org.eclipse.jgit.errors.MissingObjectException
332 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
333 	 * @throws org.eclipse.jgit.errors.CorruptObjectException
334 	 * @throws java.io.IOException
335 	 */
336 	public void prescanOneTree()
337 			throws MissingObjectException, IncorrectObjectTypeException,
338 			CorruptObjectException, IOException {
339 		removed.clear();
340 		updated.clear();
341 		conflicts.clear();
342 
343 		builder = dc.builder();
344 
345 		walk = new NameConflictTreeWalk(repo);
346 		addTree(walk, mergeCommitTree);
347 		int dciPos = walk.addTree(new DirCacheBuildIterator(builder));
348 		walk.addTree(workingTree);
349 		workingTree.setDirCacheIterator(walk, dciPos);
350 
351 		while (walk.next()) {
352 			processEntry(walk.getTree(0, CanonicalTreeParser.class),
353 					walk.getTree(1, DirCacheBuildIterator.class),
354 					walk.getTree(2, WorkingTreeIterator.class));
355 			if (walk.isSubtree())
356 				walk.enterSubtree();
357 		}
358 		conflicts.removeAll(removed);
359 	}
360 
361 	/**
362 	 * Processing an entry in the context of {@link #prescanOneTree()} when only
363 	 * one tree is given
364 	 *
365 	 * @param m the tree to merge
366 	 * @param i the index
367 	 * @param f the working tree
368 	 * @throws IOException
369 	 */
370 	void processEntry(CanonicalTreeParser m, DirCacheBuildIterator i,
371 			WorkingTreeIterator f) throws IOException {
372 		if (m != null) {
373 			checkValidPath(m);
374 			// There is an entry in the merge commit. Means: we want to update
375 			// what's currently in the index and working-tree to that one
376 			if (i == null) {
377 				// The index entry is missing
378 				if (f != null && !FileMode.TREE.equals(f.getEntryFileMode())
379 						&& !f.isEntryIgnored()) {
380 					if (failOnConflict) {
381 						// don't overwrite an untracked and not ignored file
382 						conflicts.add(walk.getPathString());
383 					} else {
384 						// failOnConflict is false. Putting something to conflicts
385 						// would mean we delete it. Instead we want the mergeCommit
386 						// content to be checked out.
387 						update(m.getEntryPathString(), m.getEntryObjectId(),
388 								m.getEntryFileMode());
389 					}
390 				} else
391 					update(m.getEntryPathString(), m.getEntryObjectId(),
392 						m.getEntryFileMode());
393 			} else if (f == null || !m.idEqual(i)) {
394 				// The working tree file is missing or the merge content differs
395 				// from index content
396 				update(m.getEntryPathString(), m.getEntryObjectId(),
397 						m.getEntryFileMode());
398 			} else if (i.getDirCacheEntry() != null) {
399 				// The index contains a file (and not a folder)
400 				if (f.isModified(i.getDirCacheEntry(), true,
401 						this.walk.getObjectReader())
402 						|| i.getDirCacheEntry().getStage() != 0)
403 					// The working tree file is dirty or the index contains a
404 					// conflict
405 					update(m.getEntryPathString(), m.getEntryObjectId(),
406 							m.getEntryFileMode());
407 				else {
408 					// update the timestamp of the index with the one from the
409 					// file if not set, as we are sure to be in sync here.
410 					DirCacheEntry entry = i.getDirCacheEntry();
411 					if (entry.getLastModified() == 0)
412 						entry.setLastModified(f.getEntryLastModified());
413 					keep(entry);
414 				}
415 			} else
416 				// The index contains a folder
417 				keep(i.getDirCacheEntry());
418 		} else {
419 			// There is no entry in the merge commit. Means: we want to delete
420 			// what's currently in the index and working tree
421 			if (f != null) {
422 				// There is a file/folder for that path in the working tree
423 				if (walk.isDirectoryFileConflict()) {
424 					// We put it in conflicts. Even if failOnConflict is false
425 					// this would cause the path to be deleted. Thats exactly what
426 					// we want in this situation
427 					conflicts.add(walk.getPathString());
428 				} else {
429 					// No file/folder conflict exists. All entries are files or
430 					// all entries are folders
431 					if (i != null) {
432 						// ... and the working tree contained a file or folder
433 						// -> add it to the removed set and remove it from
434 						// conflicts set
435 						remove(i.getEntryPathString());
436 						conflicts.remove(i.getEntryPathString());
437 					} else {
438 						// untracked file, neither contained in tree to merge
439 						// nor in index
440 					}
441 				}
442 			} else {
443 				// There is no file/folder for that path in the working tree,
444 				// nor in the merge head.
445 				// The only entry we have is the index entry. Like the case
446 				// where there is a file with the same name, remove it,
447 			}
448 		}
449 	}
450 
451 	/**
452 	 * Execute this checkout. A
453 	 * {@link org.eclipse.jgit.events.WorkingTreeModifiedEvent} is fired if the
454 	 * working tree was modified; even if the checkout fails.
455 	 *
456 	 * @return <code>false</code> if this method could not delete all the files
457 	 *         which should be deleted (e.g. because one of the files was
458 	 *         locked). In this case {@link #getToBeDeleted()} lists the files
459 	 *         which should be tried to be deleted outside of this method.
460 	 *         Although <code>false</code> is returned the checkout was
461 	 *         successful and the working tree was updated for all other files.
462 	 *         <code>true</code> is returned when no such problem occurred
463 	 * @throws java.io.IOException
464 	 */
465 	public boolean checkout() throws IOException {
466 		try {
467 			return doCheckout();
468 		} finally {
469 			try {
470 				dc.unlock();
471 			} finally {
472 				if (performingCheckout) {
473 					WorkingTreeModifiedEvent event = new WorkingTreeModifiedEvent(
474 							getUpdated().keySet(), getRemoved());
475 					if (!event.isEmpty()) {
476 						repo.fireEvent(event);
477 					}
478 				}
479 			}
480 		}
481 	}
482 
483 	private boolean doCheckout() throws CorruptObjectException, IOException,
484 			MissingObjectException, IncorrectObjectTypeException,
485 			CheckoutConflictException, IndexWriteException {
486 		toBeDeleted.clear();
487 		try (ObjectReader objectReader = repo.getObjectDatabase().newReader()) {
488 			if (headCommitTree != null)
489 				preScanTwoTrees();
490 			else
491 				prescanOneTree();
492 
493 			if (!conflicts.isEmpty()) {
494 				if (failOnConflict)
495 					throw new CheckoutConflictException(conflicts.toArray(new String[conflicts.size()]));
496 				else
497 					cleanUpConflicts();
498 			}
499 
500 			// update our index
501 			builder.finish();
502 
503 			performingCheckout = true;
504 			File file = null;
505 			String last = null;
506 			// when deleting files process them in the opposite order as they have
507 			// been reported. This ensures the files are deleted before we delete
508 			// their parent folders
509 			IntList nonDeleted = new IntList();
510 			for (int i = removed.size() - 1; i >= 0; i--) {
511 				String r = removed.get(i);
512 				file = new File(repo.getWorkTree(), r);
513 				if (!file.delete() && repo.getFS().exists(file)) {
514 					// The list of stuff to delete comes from the index
515 					// which will only contain a directory if it is
516 					// a submodule, in which case we shall not attempt
517 					// to delete it. A submodule is not empty, so it
518 					// is safe to check this after a failed delete.
519 					if (!repo.getFS().isDirectory(file)) {
520 						nonDeleted.add(i);
521 						toBeDeleted.add(r);
522 					}
523 				} else {
524 					if (last != null && !isSamePrefix(r, last))
525 						removeEmptyParents(new File(repo.getWorkTree(), last));
526 					last = r;
527 				}
528 			}
529 			if (file != null) {
530 				removeEmptyParents(file);
531 			}
532 			removed = filterOut(removed, nonDeleted);
533 			nonDeleted = null;
534 			Iterator<Map.Entry<String, CheckoutMetadata>> toUpdate = updated
535 					.entrySet().iterator();
536 			Map.Entry<String, CheckoutMetadata> e = null;
537 			try {
538 				while (toUpdate.hasNext()) {
539 					e = toUpdate.next();
540 					String path = e.getKey();
541 					CheckoutMetadata meta = e.getValue();
542 					DirCacheEntry entry = dc.getEntry(path);
543 					if (!FileMode.GITLINK.equals(entry.getRawMode())) {
544 						checkoutEntry(repo, entry, objectReader, false, meta);
545 					}
546 					e = null;
547 				}
548 			} catch (Exception ex) {
549 				// We didn't actually modify the current entry nor any that
550 				// might follow.
551 				if (e != null) {
552 					toUpdate.remove();
553 				}
554 				while (toUpdate.hasNext()) {
555 					e = toUpdate.next();
556 					toUpdate.remove();
557 				}
558 				throw ex;
559 			}
560 			// commit the index builder - a new index is persisted
561 			if (!builder.commit())
562 				throw new IndexWriteException();
563 		}
564 		return toBeDeleted.size() == 0;
565 	}
566 
567 	private static ArrayList<String> filterOut(ArrayList<String> strings,
568 			IntList indicesToRemove) {
569 		int n = indicesToRemove.size();
570 		if (n == strings.size()) {
571 			return new ArrayList<>(0);
572 		}
573 		switch (n) {
574 		case 0:
575 			return strings;
576 		case 1:
577 			strings.remove(indicesToRemove.get(0));
578 			return strings;
579 		default:
580 			int length = strings.size();
581 			ArrayList<String> result = new ArrayList<>(length - n);
582 			// Process indicesToRemove from the back; we know that it
583 			// contains indices in descending order.
584 			int j = n - 1;
585 			int idx = indicesToRemove.get(j);
586 			for (int i = 0; i < length; i++) {
587 				if (i == idx) {
588 					idx = (--j >= 0) ? indicesToRemove.get(j) : -1;
589 				} else {
590 					result.add(strings.get(i));
591 				}
592 			}
593 			return result;
594 		}
595 	}
596 
597 	private static boolean isSamePrefix(String a, String b) {
598 		int as = a.lastIndexOf('/');
599 		int bs = b.lastIndexOf('/');
600 		return a.substring(0, as + 1).equals(b.substring(0, bs + 1));
601 	}
602 
603 	 private void removeEmptyParents(File f) {
604 		File parentFile = f.getParentFile();
605 
606 		while (parentFile != null && !parentFile.equals(repo.getWorkTree())) {
607 			if (!parentFile.delete())
608 				break;
609 			parentFile = parentFile.getParentFile();
610 		}
611 	}
612 
613 	/**
614 	 * Compares whether two pairs of ObjectId and FileMode are equal.
615 	 *
616 	 * @param id1
617 	 * @param mode1
618 	 * @param id2
619 	 * @param mode2
620 	 * @return <code>true</code> if FileModes and ObjectIds are equal.
621 	 *         <code>false</code> otherwise
622 	 */
623 	private boolean equalIdAndMode(ObjectId id1, FileMode mode1, ObjectId id2,
624 			FileMode mode2) {
625 		if (!mode1.equals(mode2))
626 			return false;
627 		return id1 != null ? id1.equals(id2) : id2 == null;
628 	}
629 
630 	/**
631 	 * Here the main work is done. This method is called for each existing path
632 	 * in head, index and merge. This method decides what to do with the
633 	 * corresponding index entry: keep it, update it, remove it or mark a
634 	 * conflict.
635 	 *
636 	 * @param h
637 	 *            the entry for the head
638 	 * @param m
639 	 *            the entry for the merge
640 	 * @param i
641 	 *            the entry for the index
642 	 * @param f
643 	 *            the file in the working tree
644 	 * @throws IOException
645 	 */
646 
647 	void processEntry(CanonicalTreeParser h, CanonicalTreeParser m,
648 			DirCacheBuildIterator i, WorkingTreeIterator f) throws IOException {
649 		DirCacheEntry dce = i != null ? i.getDirCacheEntry() : null;
650 
651 		String name = walk.getPathString();
652 
653 		if (m != null)
654 			checkValidPath(m);
655 
656 		if (i == null && m == null && h == null) {
657 			// File/Directory conflict case #20
658 			if (walk.isDirectoryFileConflict())
659 				// TODO: check whether it is always correct to report a conflict here
660 				conflict(name, null, null, null);
661 
662 			// file only exists in working tree -> ignore it
663 			return;
664 		}
665 
666 		ObjectId iId = (i == null ? null : i.getEntryObjectId());
667 		ObjectId mId = (m == null ? null : m.getEntryObjectId());
668 		ObjectId hId = (h == null ? null : h.getEntryObjectId());
669 		FileMode iMode = (i == null ? null : i.getEntryFileMode());
670 		FileMode mMode = (m == null ? null : m.getEntryFileMode());
671 		FileMode hMode = (h == null ? null : h.getEntryFileMode());
672 
673 		/**
674 		 * <pre>
675 		 *  File/Directory conflicts:
676 		 *  the following table from ReadTreeTest tells what to do in case of directory/file
677 		 *  conflicts. I give comments here
678 		 *
679 		 *      H        I       M     Clean     H==M     H==I    I==M         Result
680 		 *      ------------------------------------------------------------------
681 		 * 1    D        D       F       Y         N       Y       N           Update
682 		 * 2    D        D       F       N         N       Y       N           Conflict
683 		 * 3    D        F       D                 Y       N       N           Keep
684 		 * 4    D        F       D                 N       N       N           Conflict
685 		 * 5    D        F       F       Y         N       N       Y           Keep
686 		 * 5b   D        F       F       Y         N       N       N           Conflict
687 		 * 6    D        F       F       N         N       N       Y           Keep
688 		 * 6b   D        F       F       N         N       N       N           Conflict
689 		 * 7    F        D       F       Y         Y       N       N           Update
690 		 * 8    F        D       F       N         Y       N       N           Conflict
691 		 * 9    F        D       F                 N       N       N           Conflict
692 		 * 10   F        D       D                 N       N       Y           Keep
693 		 * 11   F        D       D                 N       N       N           Conflict
694 		 * 12   F        F       D       Y         N       Y       N           Update
695 		 * 13   F        F       D       N         N       Y       N           Conflict
696 		 * 14   F        F       D                 N       N       N           Conflict
697 		 * 15   0        F       D                 N       N       N           Conflict
698 		 * 16   0        D       F       Y         N       N       N           Update
699 		 * 17   0        D       F                 N       N       N           Conflict
700 		 * 18   F        0       D                                             Update
701 		 * 19   D        0       F                                             Update
702 		 * 20   0        0       F       N (worktree=dir)                      Conflict
703 		 * </pre>
704 		 */
705 
706 		// The information whether head,index,merge iterators are currently
707 		// pointing to file/folder/non-existing is encoded into this variable.
708 		//
709 		// To decode write down ffMask in hexadecimal form. The last digit
710 		// represents the state for the merge iterator, the second last the
711 		// state for the index iterator and the third last represents the state
712 		// for the head iterator. The hexadecimal constant "F" stands for
713 		// "file", a "D" stands for "directory" (tree), and a "0" stands for
714 		// non-existing. Symbolic links and git links are treated as File here.
715 		//
716 		// Examples:
717 		// ffMask == 0xFFD -> Head=File, Index=File, Merge=Tree
718 		// ffMask == 0xDD0 -> Head=Tree, Index=Tree, Merge=Non-Existing
719 
720 		int ffMask = 0;
721 		if (h != null)
722 			ffMask = FileMode.TREE.equals(hMode) ? 0xD00 : 0xF00;
723 		if (i != null)
724 			ffMask |= FileMode.TREE.equals(iMode) ? 0x0D0 : 0x0F0;
725 		if (m != null)
726 			ffMask |= FileMode.TREE.equals(mMode) ? 0x00D : 0x00F;
727 
728 		// Check whether we have a possible file/folder conflict. Therefore we
729 		// need a least one file and one folder.
730 		if (((ffMask & 0x222) != 0x000)
731 				&& (((ffMask & 0x00F) == 0x00D) || ((ffMask & 0x0F0) == 0x0D0) || ((ffMask & 0xF00) == 0xD00))) {
732 
733 			// There are 3*3*3=27 possible combinations of file/folder
734 			// conflicts. Some of them are not-relevant because
735 			// they represent no conflict, e.g. 0xFFF, 0xDDD, ... The following
736 			// switch processes all relevant cases.
737 			switch (ffMask) {
738 			case 0xDDF: // 1 2
739 				if (f != null && isModifiedSubtree_IndexWorkingtree(name)) {
740 					conflict(name, dce, h, m); // 1
741 				} else {
742 					update(name, mId, mMode); // 2
743 				}
744 
745 				break;
746 			case 0xDFD: // 3 4
747 				keep(dce);
748 				break;
749 			case 0xF0D: // 18
750 				remove(name);
751 				break;
752 			case 0xDFF: // 5 5b 6 6b
753 				if (equalIdAndMode(iId, iMode, mId, mMode))
754 					keep(dce); // 5 6
755 				else
756 					conflict(name, dce, h, m); // 5b 6b
757 				break;
758 			case 0xFDD: // 10 11
759 				// TODO: make use of tree extension as soon as available in jgit
760 				// we would like to do something like
761 				// if (!equalIdAndMode(iId, iMode, mId, mMode)
762 				//   conflict(name, i.getDirCacheEntry(), h, m);
763 				// But since we don't know the id of a tree in the index we do
764 				// nothing here and wait that conflicts between index and merge
765 				// are found later
766 				break;
767 			case 0xD0F: // 19
768 				update(name, mId, mMode);
769 				break;
770 			case 0xDF0: // conflict without a rule
771 			case 0x0FD: // 15
772 				conflict(name, dce, h, m);
773 				break;
774 			case 0xFDF: // 7 8 9
775 				if (equalIdAndMode(hId, hMode, mId, mMode)) {
776 					if (isModifiedSubtree_IndexWorkingtree(name))
777 						conflict(name, dce, h, m); // 8
778 					else
779 						update(name, mId, mMode); // 7
780 				} else
781 					conflict(name, dce, h, m); // 9
782 				break;
783 			case 0xFD0: // keep without a rule
784 				keep(dce);
785 				break;
786 			case 0xFFD: // 12 13 14
787 				if (equalIdAndMode(hId, hMode, iId, iMode))
788 					if (f != null
789 							&& f.isModified(dce, true,
790 									this.walk.getObjectReader()))
791 						conflict(name, dce, h, m); // 13
792 					else
793 						remove(name); // 12
794 				else
795 					conflict(name, dce, h, m); // 14
796 				break;
797 			case 0x0DF: // 16 17
798 				if (!isModifiedSubtree_IndexWorkingtree(name))
799 					update(name, mId, mMode);
800 				else
801 					conflict(name, dce, h, m);
802 				break;
803 			default:
804 				keep(dce);
805 			}
806 			return;
807 		}
808 
809 		if ((ffMask & 0x222) == 0) {
810 			// HEAD, MERGE and index don't contain a file (e.g. all contain a
811 			// folder)
812 			if (f == null || FileMode.TREE.equals(f.getEntryFileMode())) {
813 				// the workingtree entry doesn't exist or also contains a folder
814 				// -> no problem
815 				return;
816 			} else {
817 				// the workingtree entry exists and is not a folder
818 				if (!idEqual(h, m)) {
819 					// Because HEAD and MERGE differ we will try to update the
820 					// workingtree with a folder -> return a conflict
821 					conflict(name, null, null, null);
822 				}
823 				return;
824 			}
825 		}
826 
827 		if ((ffMask == 0x00F) && f != null && FileMode.TREE.equals(f.getEntryFileMode())) {
828 			// File/Directory conflict case #20
829 			conflict(name, null, h, m);
830 			return;
831 		}
832 
833 		if (i == null) {
834 			// Nothing in Index
835 			// At least one of Head, Index, Merge is not empty
836 			// make sure not to overwrite untracked files
837 			if (f != null && !f.isEntryIgnored()) {
838 				// A submodule is not a file. We should ignore it
839 				if (!FileMode.GITLINK.equals(mMode)) {
840 					// a dirty worktree: the index is empty but we have a
841 					// workingtree-file
842 					if (mId == null
843 							|| !equalIdAndMode(mId, mMode,
844 									f.getEntryObjectId(), f.getEntryFileMode())) {
845 						conflict(name, null, h, m);
846 						return;
847 					}
848 				}
849 			}
850 
851 			/**
852 			 * <pre>
853 			 * 	          I (index)     H        M     H==M  Result
854 			 * 	        -------------------------------------------
855 			 * 	        0 nothing    nothing  nothing        (does not happen)
856 			 * 	        1 nothing    nothing  exists         use M
857 			 * 	        2 nothing    exists   nothing        remove path from index
858 			 * 	        3 nothing    exists   exists   yes   keep index if not in initial checkout
859 			 *                                               , otherwise use M
860 			 * 	          nothing    exists   exists   no    fail
861 			 * </pre>
862 			 */
863 
864 			if (h == null)
865 				// Nothing in Head
866 				// Nothing in Index
867 				// At least one of Head, Index, Merge is not empty
868 				// -> only Merge contains something for this path. Use it!
869 				// Potentially update the file
870 				update(name, mId, mMode); // 1
871 			else if (m == null)
872 				// Nothing in Merge
873 				// Something in Head
874 				// Nothing in Index
875 				// -> only Head contains something for this path and it should
876 				// be deleted. Potentially removes the file!
877 				remove(name); // 2
878 			else { // 3
879 				// Something in Merge
880 				// Something in Head
881 				// Nothing in Index
882 				// -> Head and Merge contain something (maybe not the same) and
883 				// in the index there is nothing (e.g. 'git rm ...' was
884 				// called before). Ignore the cached deletion and use what we
885 				// find in Merge. Potentially updates the file.
886 				if (equalIdAndMode(hId, hMode, mId, mMode)) {
887 					if (emptyDirCache)
888 						update(name, mId, mMode);
889 					else
890 						keep(dce);
891 				} else
892 					conflict(name, dce, h, m);
893 			}
894 		} else {
895 			// Something in Index
896 			if (h == null) {
897 				// Nothing in Head
898 				// Something in Index
899 				/**
900 				 * <pre>
901 				 * 	          clean I==H  I==M       H        M        Result
902 				 * 	         -----------------------------------------------------
903 				 * 	        4 yes   N/A   N/A     nothing  nothing  keep index
904 				 * 	        5 no    N/A   N/A     nothing  nothing  keep index
905 				 *
906 				 * 	        6 yes   N/A   yes     nothing  exists   keep index
907 				 * 	        7 no    N/A   yes     nothing  exists   keep index
908 				 * 	        8 yes   N/A   no      nothing  exists   fail
909 				 * 	        9 no    N/A   no      nothing  exists   fail
910 				 * </pre>
911 				 */
912 
913 				if (m == null
914 						|| !isModified_IndexTree(name, iId, iMode, mId, mMode,
915 								mergeCommitTree)) {
916 					// Merge contains nothing or the same as Index
917 					// Nothing in Head
918 					// Something in Index
919 					if (m==null && walk.isDirectoryFileConflict()) {
920 						// Nothing in Merge and current path is part of
921 						// File/Folder conflict
922 						// Nothing in Head
923 						// Something in Index
924 						if (dce != null
925 								&& (f == null || f.isModified(dce, true,
926 										this.walk.getObjectReader())))
927 							// No file or file is dirty
928 							// Nothing in Merge and current path is part of
929 							// File/Folder conflict
930 							// Nothing in Head
931 							// Something in Index
932 							// -> File folder conflict and Merge wants this
933 							// path to be removed. Since the file is dirty
934 							// report a conflict
935 							conflict(name, dce, h, m);
936 						else
937 							// A file is present and file is not dirty
938 							// Nothing in Merge and current path is part of
939 							// File/Folder conflict
940 							// Nothing in Head
941 							// Something in Index
942 							// -> File folder conflict and Merge wants this path
943 							// to be removed. Since the file is not dirty remove
944 							// file and index entry
945 							remove(name);
946 					} else
947 						// Something in Merge or current path is not part of
948 						// File/Folder conflict
949 						// Merge contains nothing or the same as Index
950 						// Nothing in Head
951 						// Something in Index
952 						// -> Merge contains nothing new. Keep the index.
953 						keep(dce);
954 				} else
955 					// Merge contains something and it is not the same as Index
956 					// Nothing in Head
957 					// Something in Index
958 					// -> Index contains something new (different from Head)
959 					// and Merge is different from Index. Report a conflict
960 					conflict(name, dce, h, m);
961 			} else if (m == null) {
962 				// Nothing in Merge
963 				// Something in Head
964 				// Something in Index
965 
966 				/**
967 				 * <pre>
968 				 * 	           clean I==H  I==M       H        M        Result
969 				 * 	         -----------------------------------------------------
970 				 * 	        10 yes   yes   N/A     exists   nothing  remove path from index
971 				 * 	        11 no    yes   N/A     exists   nothing  keep file
972 				 * 	        12 yes   no    N/A     exists   nothing  fail
973 				 * 	        13 no    no    N/A     exists   nothing  fail
974 				 * </pre>
975 				 */
976 
977 				if (iMode == FileMode.GITLINK) {
978 					// A submodule in Index
979 					// Nothing in Merge
980 					// Something in Head
981 					// Submodules that disappear from the checkout must
982 					// be removed from the index, but not deleted from disk.
983 					remove(name);
984 				} else {
985 					// Something different from a submodule in Index
986 					// Nothing in Merge
987 					// Something in Head
988 					if (!isModified_IndexTree(name, iId, iMode, hId, hMode,
989 							headCommitTree)) {
990 						// Index contains the same as Head
991 						// Something different from a submodule in Index
992 						// Nothing in Merge
993 						// Something in Head
994 						if (f != null
995 								&& f.isModified(dce, true,
996 										this.walk.getObjectReader())) {
997 							// file is dirty
998 							// Index contains the same as Head
999 							// Something different from a submodule in Index
1000 							// Nothing in Merge
1001 							// Something in Head
1002 
1003 							if (!FileMode.TREE.equals(f.getEntryFileMode())
1004 									&& FileMode.TREE.equals(iMode))
1005 								// The workingtree contains a file and the index semantically contains a folder.
1006 								// Git considers the workingtree file as untracked. Just keep the untracked file.
1007 								return;
1008 							else
1009 								// -> file is dirty and tracked but is should be
1010 								// removed. That's a conflict
1011 								conflict(name, dce, h, m);
1012 						} else
1013 							// file doesn't exist or is clean
1014 							// Index contains the same as Head
1015 							// Something different from a submodule in Index
1016 							// Nothing in Merge
1017 							// Something in Head
1018 							// -> Remove from index and delete the file
1019 							remove(name);
1020 					} else
1021 						// Index contains something different from Head
1022 						// Something different from a submodule in Index
1023 						// Nothing in Merge
1024 						// Something in Head
1025 						// -> Something new is in index (and maybe even on the
1026 						// filesystem). But Merge wants the path to be removed.
1027 						// Report a conflict
1028 						conflict(name, dce, h, m);
1029 				}
1030 			} else {
1031 				// Something in Merge
1032 				// Something in Head
1033 				// Something in Index
1034 				if (!equalIdAndMode(hId, hMode, mId, mMode)
1035 						&& isModified_IndexTree(name, iId, iMode, hId, hMode,
1036 								headCommitTree)
1037 						&& isModified_IndexTree(name, iId, iMode, mId, mMode,
1038 								mergeCommitTree))
1039 					// All three contents in Head, Merge, Index differ from each
1040 					// other
1041 					// -> All contents differ. Report a conflict.
1042 					conflict(name, dce, h, m);
1043 				else
1044 					// At least two of the contents of Head, Index, Merge
1045 					// are the same
1046 					// Something in Merge
1047 					// Something in Head
1048 					// Something in Index
1049 
1050 				if (!isModified_IndexTree(name, iId, iMode, hId, hMode,
1051 						headCommitTree)
1052 						&& isModified_IndexTree(name, iId, iMode, mId, mMode,
1053 								mergeCommitTree)) {
1054 						// Head contains the same as Index. Merge differs
1055 						// Something in Merge
1056 
1057 					// For submodules just update the index with the new SHA-1
1058 					if (dce != null
1059 							&& FileMode.GITLINK.equals(dce.getFileMode())) {
1060 						// Index and Head contain the same submodule. Merge
1061 						// differs
1062 						// Something in Merge
1063 						// -> Nothing new in index. Move to merge.
1064 						// Potentially updates the file
1065 
1066 						// TODO check that we don't overwrite some unsaved
1067 						// file content
1068 						update(name, mId, mMode);
1069 					} else if (dce != null
1070 							&& (f != null && f.isModified(dce, true,
1071 									this.walk.getObjectReader()))) {
1072 						// File exists and is dirty
1073 						// Head and Index don't contain a submodule
1074 						// Head contains the same as Index. Merge differs
1075 						// Something in Merge
1076 						// -> Merge wants the index and file to be updated
1077 						// but the file is dirty. Report a conflict
1078 						conflict(name, dce, h, m);
1079 					} else {
1080 						// File doesn't exist or is clean
1081 						// Head and Index don't contain a submodule
1082 						// Head contains the same as Index. Merge differs
1083 						// Something in Merge
1084 						// -> Standard case when switching between branches:
1085 						// Nothing new in index but something different in
1086 						// Merge. Update index and file
1087 						update(name, mId, mMode);
1088 					}
1089 				} else {
1090 					// Head differs from index or merge is same as index
1091 					// At least two of the contents of Head, Index, Merge
1092 					// are the same
1093 					// Something in Merge
1094 					// Something in Head
1095 					// Something in Index
1096 
1097 					// Can be formulated as: Either all three states are
1098 					// equal or Merge is equal to Head or Index and differs
1099 					// to the other one.
1100 					// -> In all three cases we don't touch index and file.
1101 
1102 					keep(dce);
1103 				}
1104 			}
1105 		}
1106 	}
1107 
1108 	private static boolean idEqual(AbstractTreeIterator a,
1109 			AbstractTreeIterator b) {
1110 		if (a == b) {
1111 			return true;
1112 		}
1113 		if (a == null || b == null) {
1114 			return false;
1115 		}
1116 		return a.getEntryObjectId().equals(b.getEntryObjectId());
1117 	}
1118 
1119 	/**
1120 	 * A conflict is detected - add the three different stages to the index
1121 	 * @param path the path of the conflicting entry
1122 	 * @param e the previous index entry
1123 	 * @param h the first tree you want to merge (the HEAD)
1124 	 * @param m the second tree you want to merge
1125 	 */
1126 	private void conflict(String path, DirCacheEntry e, AbstractTreeIterator h, AbstractTreeIterator m) {
1127 		conflicts.add(path);
1128 
1129 		DirCacheEntry entry;
1130 		if (e != null) {
1131 			entry = new DirCacheEntry(e.getPathString(), DirCacheEntry.STAGE_1);
1132 			entry.copyMetaData(e, true);
1133 			builder.add(entry);
1134 		}
1135 
1136 		if (h != null && !FileMode.TREE.equals(h.getEntryFileMode())) {
1137 			entry = new DirCacheEntry(h.getEntryPathString(), DirCacheEntry.STAGE_2);
1138 			entry.setFileMode(h.getEntryFileMode());
1139 			entry.setObjectId(h.getEntryObjectId());
1140 			builder.add(entry);
1141 		}
1142 
1143 		if (m != null && !FileMode.TREE.equals(m.getEntryFileMode())) {
1144 			entry = new DirCacheEntry(m.getEntryPathString(), DirCacheEntry.STAGE_3);
1145 			entry.setFileMode(m.getEntryFileMode());
1146 			entry.setObjectId(m.getEntryObjectId());
1147 			builder.add(entry);
1148 		}
1149 	}
1150 
1151 	private void keep(DirCacheEntry e) {
1152 		if (e != null && !FileMode.TREE.equals(e.getFileMode()))
1153 			builder.add(e);
1154 	}
1155 
1156 	private void remove(String path) {
1157 		removed.add(path);
1158 	}
1159 
1160 	private void update(String path, ObjectId mId, FileMode mode)
1161 			throws IOException {
1162 		if (!FileMode.TREE.equals(mode)) {
1163 			updated.put(path, new CheckoutMetadata(
1164 					walk.getEolStreamType(CHECKOUT_OP),
1165 					walk.getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE)));
1166 
1167 			DirCacheEntry entry = new DirCacheEntry(path, DirCacheEntry.STAGE_0);
1168 			entry.setObjectId(mId);
1169 			entry.setFileMode(mode);
1170 			builder.add(entry);
1171 		}
1172 	}
1173 
1174 	/**
1175 	 * If <code>true</code>, will scan first to see if it's possible to check
1176 	 * out, otherwise throw
1177 	 * {@link org.eclipse.jgit.errors.CheckoutConflictException}. If
1178 	 * <code>false</code>, it will silently deal with the problem.
1179 	 *
1180 	 * @param failOnConflict
1181 	 *            a boolean.
1182 	 */
1183 	public void setFailOnConflict(boolean failOnConflict) {
1184 		this.failOnConflict = failOnConflict;
1185 	}
1186 
1187 	/**
1188 	 * This method implements how to handle conflicts when
1189 	 * {@link #failOnConflict} is false
1190 	 *
1191 	 * @throws CheckoutConflictException
1192 	 */
1193 	private void cleanUpConflicts() throws CheckoutConflictException {
1194 		// TODO: couldn't we delete unsaved worktree content here?
1195 		for (String c : conflicts) {
1196 			File conflict = new File(repo.getWorkTree(), c);
1197 			if (!conflict.delete())
1198 				throw new CheckoutConflictException(MessageFormat.format(
1199 						JGitText.get().cannotDeleteFile, c));
1200 			removeEmptyParents(conflict);
1201 		}
1202 		for (String r : removed) {
1203 			File file = new File(repo.getWorkTree(), r);
1204 			if (!file.delete())
1205 				throw new CheckoutConflictException(
1206 						MessageFormat.format(JGitText.get().cannotDeleteFile,
1207 								file.getAbsolutePath()));
1208 			removeEmptyParents(file);
1209 		}
1210 	}
1211 
1212 	/**
1213 	 * Checks whether the subtree starting at a given path differs between Index and
1214 	 * workingtree.
1215 	 *
1216 	 * @param path
1217 	 * @return true if the subtrees differ
1218 	 * @throws CorruptObjectException
1219 	 * @throws IOException
1220 	 */
1221 	private boolean isModifiedSubtree_IndexWorkingtree(String path)
1222 			throws CorruptObjectException, IOException {
1223 		try (NameConflictTreeWalk tw = new NameConflictTreeWalk(repo)) {
1224 			int dciPos = tw.addTree(new DirCacheIterator(dc));
1225 			FileTreeIterator fti = new FileTreeIterator(repo);
1226 			tw.addTree(fti);
1227 			fti.setDirCacheIterator(tw, dciPos);
1228 			tw.setRecursive(true);
1229 			tw.setFilter(PathFilter.create(path));
1230 			DirCacheIterator dcIt;
1231 			WorkingTreeIterator wtIt;
1232 			while (tw.next()) {
1233 				dcIt = tw.getTree(0, DirCacheIterator.class);
1234 				wtIt = tw.getTree(1, WorkingTreeIterator.class);
1235 				if (dcIt == null || wtIt == null)
1236 					return true;
1237 				if (wtIt.isModified(dcIt.getDirCacheEntry(), true,
1238 						this.walk.getObjectReader())) {
1239 					return true;
1240 				}
1241 			}
1242 			return false;
1243 		}
1244 	}
1245 
1246 	private boolean isModified_IndexTree(String path, ObjectId iId,
1247 			FileMode iMode, ObjectId tId, FileMode tMode, ObjectId rootTree)
1248 			throws CorruptObjectException, IOException {
1249 		if (iMode != tMode)
1250 			return true;
1251 		if (FileMode.TREE.equals(iMode)
1252 				&& (iId == null || ObjectId.zeroId().equals(iId)))
1253 			return isModifiedSubtree_IndexTree(path, rootTree);
1254 		else
1255 			return !equalIdAndMode(iId, iMode, tId, tMode);
1256 	}
1257 
1258 	/**
1259 	 * Checks whether the subtree starting at a given path differs between Index and
1260 	 * some tree.
1261 	 *
1262 	 * @param path
1263 	 * @param tree
1264 	 *            the tree to compare
1265 	 * @return true if the subtrees differ
1266 	 * @throws CorruptObjectException
1267 	 * @throws IOException
1268 	 */
1269 	private boolean isModifiedSubtree_IndexTree(String path, ObjectId tree)
1270 			throws CorruptObjectException, IOException {
1271 		try (NameConflictTreeWalk tw = new NameConflictTreeWalk(repo)) {
1272 			tw.addTree(new DirCacheIterator(dc));
1273 			tw.addTree(tree);
1274 			tw.setRecursive(true);
1275 			tw.setFilter(PathFilter.create(path));
1276 			while (tw.next()) {
1277 				AbstractTreeIterator dcIt = tw.getTree(0,
1278 						DirCacheIterator.class);
1279 				AbstractTreeIterator treeIt = tw.getTree(1,
1280 						AbstractTreeIterator.class);
1281 				if (dcIt == null || treeIt == null)
1282 					return true;
1283 				if (dcIt.getEntryRawMode() != treeIt.getEntryRawMode())
1284 					return true;
1285 				if (!dcIt.getEntryObjectId().equals(treeIt.getEntryObjectId()))
1286 					return true;
1287 			}
1288 			return false;
1289 		}
1290 	}
1291 
1292 	/**
1293 	 * Updates the file in the working tree with content and mode from an entry
1294 	 * in the index. The new content is first written to a new temporary file in
1295 	 * the same directory as the real file. Then that new file is renamed to the
1296 	 * final filename.
1297 	 *
1298 	 * <p>
1299 	 * <b>Note:</b> if the entry path on local file system exists as a non-empty
1300 	 * directory, and the target entry type is a link or file, the checkout will
1301 	 * fail with {@link java.io.IOException} since existing non-empty directory
1302 	 * cannot be renamed to file or link without deleting it recursively.
1303 	 * </p>
1304 	 *
1305 	 * <p>
1306 	 * TODO: this method works directly on File IO, we may need another
1307 	 * abstraction (like WorkingTreeIterator). This way we could tell e.g.
1308 	 * Eclipse that Files in the workspace got changed
1309 	 * </p>
1310 	 *
1311 	 * @param repo
1312 	 *            repository managing the destination work tree.
1313 	 * @param entry
1314 	 *            the entry containing new mode and content
1315 	 * @param or
1316 	 *            object reader to use for checkout
1317 	 * @throws java.io.IOException
1318 	 * @since 3.6
1319 	 */
1320 	public static void checkoutEntry(Repository repo, DirCacheEntry entry,
1321 			ObjectReader or) throws IOException {
1322 		checkoutEntry(repo, entry, or, false, null);
1323 	}
1324 
1325 	/**
1326 	 * Updates the file in the working tree with content and mode from an entry
1327 	 * in the index. The new content is first written to a new temporary file in
1328 	 * the same directory as the real file. Then that new file is renamed to the
1329 	 * final filename.
1330 	 *
1331 	 * <p>
1332 	 * <b>Note:</b> if the entry path on local file system exists as a file, it
1333 	 * will be deleted and if it exists as a directory, it will be deleted
1334 	 * recursively, independently if has any content.
1335 	 * </p>
1336 	 *
1337 	 * <p>
1338 	 * TODO: this method works directly on File IO, we may need another
1339 	 * abstraction (like WorkingTreeIterator). This way we could tell e.g.
1340 	 * Eclipse that Files in the workspace got changed
1341 	 * </p>
1342 	 *
1343 	 * @param repo
1344 	 *            repository managing the destination work tree.
1345 	 * @param entry
1346 	 *            the entry containing new mode and content
1347 	 * @param or
1348 	 *            object reader to use for checkout
1349 	 * @param deleteRecursive
1350 	 *            true to recursively delete final path if it exists on the file
1351 	 *            system
1352 	 * @param checkoutMetadata
1353 	 *            containing
1354 	 *            <ul>
1355 	 *            <li>smudgeFilterCommand to be run for smudging the entry to be
1356 	 *            checked out</li>
1357 	 *            <li>eolStreamType used for stream conversion</li>
1358 	 *            </ul>
1359 	 * @throws java.io.IOException
1360 	 * @since 4.2
1361 	 */
1362 	public static void checkoutEntry(Repository repo, DirCacheEntry entry,
1363 			ObjectReader or, boolean deleteRecursive,
1364 			CheckoutMetadata checkoutMetadata) throws IOException {
1365 		if (checkoutMetadata == null)
1366 			checkoutMetadata = CheckoutMetadata.EMPTY;
1367 		ObjectLoader ol = or.open(entry.getObjectId());
1368 		File f = new File(repo.getWorkTree(), entry.getPathString());
1369 		File parentDir = f.getParentFile();
1370 		FileUtils.mkdirs(parentDir, true);
1371 		FS fs = repo.getFS();
1372 		WorkingTreeOptions opt = repo.getConfig().get(WorkingTreeOptions.KEY);
1373 		if (entry.getFileMode() == FileMode.SYMLINK
1374 				&& opt.getSymLinks() == SymLinks.TRUE) {
1375 			byte[] bytes = ol.getBytes();
1376 			String target = RawParseUtils.decode(bytes);
1377 			if (deleteRecursive && f.isDirectory()) {
1378 				FileUtils.delete(f, FileUtils.RECURSIVE);
1379 			}
1380 			fs.createSymLink(f, target);
1381 			entry.setLength(bytes.length);
1382 			entry.setLastModified(fs.lastModified(f));
1383 			return;
1384 		}
1385 
1386 		String name = f.getName();
1387 		if (name.length() > 200) {
1388 			name = name.substring(0, 200);
1389 		}
1390 		File tmpFile = File.createTempFile(
1391 				"._" + name, null, parentDir); //$NON-NLS-1$
1392 
1393 		EolStreamType nonNullEolStreamType;
1394 		if (checkoutMetadata.eolStreamType != null) {
1395 			nonNullEolStreamType = checkoutMetadata.eolStreamType;
1396 		} else if (opt.getAutoCRLF() == AutoCRLF.TRUE) {
1397 			nonNullEolStreamType = EolStreamType.AUTO_CRLF;
1398 		} else {
1399 			nonNullEolStreamType = EolStreamType.DIRECT;
1400 		}
1401 		try (OutputStream channel = EolStreamTypeUtil.wrapOutputStream(
1402 				new FileOutputStream(tmpFile), nonNullEolStreamType)) {
1403 			if (checkoutMetadata.smudgeFilterCommand != null) {
1404 				if (FilterCommandRegistry
1405 						.isRegistered(checkoutMetadata.smudgeFilterCommand)) {
1406 					runBuiltinFilterCommand(repo, checkoutMetadata, ol,
1407 							channel);
1408 				} else {
1409 					runExternalFilterCommand(repo, entry, checkoutMetadata, ol,
1410 							fs, channel);
1411 				}
1412 			} else {
1413 				ol.copyTo(channel);
1414 			}
1415 		}
1416 		// The entry needs to correspond to the on-disk filesize. If the content
1417 		// was filtered (either by autocrlf handling or smudge filters) ask the
1418 		// filesystem again for the length. Otherwise the objectloader knows the
1419 		// size
1420 		if (checkoutMetadata.eolStreamType == EolStreamType.DIRECT
1421 				&& checkoutMetadata.smudgeFilterCommand == null) {
1422 			entry.setLength(ol.getSize());
1423 		} else {
1424 			entry.setLength(tmpFile.length());
1425 		}
1426 
1427 		if (opt.isFileMode() && fs.supportsExecute()) {
1428 			if (FileMode.EXECUTABLE_FILE.equals(entry.getRawMode())) {
1429 				if (!fs.canExecute(tmpFile))
1430 					fs.setExecute(tmpFile, true);
1431 			} else {
1432 				if (fs.canExecute(tmpFile))
1433 					fs.setExecute(tmpFile, false);
1434 			}
1435 		}
1436 		try {
1437 			if (deleteRecursive && f.isDirectory()) {
1438 				FileUtils.delete(f, FileUtils.RECURSIVE);
1439 			}
1440 			FileUtils.rename(tmpFile, f, StandardCopyOption.ATOMIC_MOVE);
1441 		} catch (IOException e) {
1442 			throw new IOException(
1443 					MessageFormat.format(JGitText.get().renameFileFailed,
1444 							tmpFile.getPath(), f.getPath()),
1445 					e);
1446 		} finally {
1447 			if (tmpFile.exists()) {
1448 				FileUtils.delete(tmpFile);
1449 			}
1450 		}
1451 		entry.setLastModified(fs.lastModified(f));
1452 	}
1453 
1454 	// Run an external filter command
1455 	private static void runExternalFilterCommand(Repository repo,
1456 			DirCacheEntry entry,
1457 			CheckoutMetadata checkoutMetadata, ObjectLoader ol, FS fs,
1458 			OutputStream channel) throws IOException {
1459 		ProcessBuilder filterProcessBuilder = fs.runInShell(
1460 				checkoutMetadata.smudgeFilterCommand, new String[0]);
1461 		filterProcessBuilder.directory(repo.getWorkTree());
1462 		filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY,
1463 				repo.getDirectory().getAbsolutePath());
1464 		ExecutionResult result;
1465 		int rc;
1466 		try {
1467 			// TODO: wire correctly with AUTOCRLF
1468 			result = fs.execute(filterProcessBuilder, ol.openStream());
1469 			rc = result.getRc();
1470 			if (rc == 0) {
1471 				result.getStdout().writeTo(channel,
1472 						NullProgressMonitor.INSTANCE);
1473 			}
1474 		} catch (IOException | InterruptedException e) {
1475 			throw new IOException(new FilterFailedException(e,
1476 					checkoutMetadata.smudgeFilterCommand,
1477 					entry.getPathString()));
1478 		}
1479 		if (rc != 0) {
1480 			throw new IOException(new FilterFailedException(rc,
1481 					checkoutMetadata.smudgeFilterCommand,
1482 					entry.getPathString(),
1483 					result.getStdout().toByteArray(MAX_EXCEPTION_TEXT_SIZE),
1484 					RawParseUtils.decode(result.getStderr()
1485 							.toByteArray(MAX_EXCEPTION_TEXT_SIZE))));
1486 		}
1487 	}
1488 
1489 	// Run a builtin filter command
1490 	private static void runBuiltinFilterCommand(Repository repo,
1491 			CheckoutMetadata checkoutMetadata, ObjectLoader ol,
1492 			OutputStream channel) throws MissingObjectException, IOException {
1493 		FilterCommand command = null;
1494 		try {
1495 			command = FilterCommandRegistry.createFilterCommand(
1496 					checkoutMetadata.smudgeFilterCommand, repo, ol.openStream(),
1497 					channel);
1498 		} catch (IOException e) {
1499 			LOG.error(JGitText.get().failedToDetermineFilterDefinition, e);
1500 			// In case an IOException occurred during creating of the command
1501 			// then proceed as if there would not have been a builtin filter.
1502 			ol.copyTo(channel);
1503 		}
1504 		if (command != null) {
1505 			while (command.run() != -1) {
1506 				// loop as long as command.run() tells there is work to do
1507 			}
1508 		}
1509 	}
1510 
1511 	@SuppressWarnings("deprecation")
1512 	private static void checkValidPath(CanonicalTreeParser t)
1513 			throws InvalidPathException {
1514 		ObjectChecker chk = new ObjectChecker()
1515 			.setSafeForWindows(SystemReader.getInstance().isWindows())
1516 			.setSafeForMacOS(SystemReader.getInstance().isMacOS());
1517 		for (CanonicalTreeParser i = t; i != null; i = i.getParent())
1518 			checkValidPathSegment(chk, i);
1519 	}
1520 
1521 	private static void checkValidPathSegment(ObjectChecker chk,
1522 			CanonicalTreeParser t) throws InvalidPathException {
1523 		try {
1524 			int ptr = t.getNameOffset();
1525 			int end = ptr + t.getNameLength();
1526 			chk.checkPathSegment(t.getEntryPathBuffer(), ptr, end);
1527 		} catch (CorruptObjectException err) {
1528 			String path = t.getEntryPathString();
1529 			InvalidPathException i = new InvalidPathException(path);
1530 			i.initCause(err);
1531 			throw i;
1532 		}
1533 	}
1534 }