DirCacheCheckout.java

  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>
  7.  * Copyright (C) 2019-2020, Andre Bossert <andre.bossert@siemens.com>
  8.  *
  9.  * This program and the accompanying materials are made available under the
  10.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  11.  * https://www.eclipse.org/org/documents/edl-v10.php.
  12.  *
  13.  * SPDX-License-Identifier: BSD-3-Clause
  14.  */

  15. package org.eclipse.jgit.dircache;

  16. import static org.eclipse.jgit.treewalk.TreeWalk.OperationType.CHECKOUT_OP;

  17. import java.io.File;
  18. import java.io.FileOutputStream;
  19. import java.io.IOException;
  20. import java.io.OutputStream;
  21. import java.nio.file.StandardCopyOption;
  22. import java.text.MessageFormat;
  23. import java.time.Instant;
  24. import java.util.ArrayList;
  25. import java.util.HashMap;
  26. import java.util.HashSet;
  27. import java.util.Iterator;
  28. import java.util.List;
  29. import java.util.Map;
  30. import java.util.Set;

  31. import org.eclipse.jgit.api.errors.CanceledException;
  32. import org.eclipse.jgit.api.errors.FilterFailedException;
  33. import org.eclipse.jgit.attributes.FilterCommand;
  34. import org.eclipse.jgit.attributes.FilterCommandRegistry;
  35. import org.eclipse.jgit.errors.CheckoutConflictException;
  36. import org.eclipse.jgit.errors.CorruptObjectException;
  37. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  38. import org.eclipse.jgit.errors.IndexWriteException;
  39. import org.eclipse.jgit.errors.MissingObjectException;
  40. import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
  41. import org.eclipse.jgit.internal.JGitText;
  42. import org.eclipse.jgit.lib.ConfigConstants;
  43. import org.eclipse.jgit.lib.Constants;
  44. import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
  45. import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
  46. import org.eclipse.jgit.lib.CoreConfig.SymLinks;
  47. import org.eclipse.jgit.lib.FileMode;
  48. import org.eclipse.jgit.lib.NullProgressMonitor;
  49. import org.eclipse.jgit.lib.ObjectChecker;
  50. import org.eclipse.jgit.lib.ObjectId;
  51. import org.eclipse.jgit.lib.ObjectLoader;
  52. import org.eclipse.jgit.lib.ObjectReader;
  53. import org.eclipse.jgit.lib.ProgressMonitor;
  54. import org.eclipse.jgit.lib.Repository;
  55. import org.eclipse.jgit.treewalk.AbstractTreeIterator;
  56. import org.eclipse.jgit.treewalk.CanonicalTreeParser;
  57. import org.eclipse.jgit.treewalk.EmptyTreeIterator;
  58. import org.eclipse.jgit.treewalk.FileTreeIterator;
  59. import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
  60. import org.eclipse.jgit.treewalk.TreeWalk;
  61. import org.eclipse.jgit.treewalk.WorkingTreeIterator;
  62. import org.eclipse.jgit.treewalk.WorkingTreeOptions;
  63. import org.eclipse.jgit.treewalk.filter.PathFilter;
  64. import org.eclipse.jgit.util.FS;
  65. import org.eclipse.jgit.util.FS.ExecutionResult;
  66. import org.eclipse.jgit.util.FileUtils;
  67. import org.eclipse.jgit.util.IntList;
  68. import org.eclipse.jgit.util.RawParseUtils;
  69. import org.eclipse.jgit.util.SystemReader;
  70. import org.eclipse.jgit.util.io.EolStreamTypeUtil;
  71. import org.slf4j.Logger;
  72. import org.slf4j.LoggerFactory;

  73. /**
  74.  * This class handles checking out one or two trees merging with the index.
  75.  */
  76. public class DirCacheCheckout {
  77.     private static final Logger LOG = LoggerFactory
  78.             .getLogger(DirCacheCheckout.class);

  79.     private static final int MAX_EXCEPTION_TEXT_SIZE = 10 * 1024;

  80.     /**
  81.      * Metadata used in checkout process
  82.      *
  83.      * @since 4.3
  84.      */
  85.     public static class CheckoutMetadata {
  86.         /** git attributes */
  87.         public final EolStreamType eolStreamType;

  88.         /** filter command to apply */
  89.         public final String smudgeFilterCommand;

  90.         /**
  91.          * @param eolStreamType
  92.          * @param smudgeFilterCommand
  93.          */
  94.         public CheckoutMetadata(EolStreamType eolStreamType,
  95.                 String smudgeFilterCommand) {
  96.             this.eolStreamType = eolStreamType;
  97.             this.smudgeFilterCommand = smudgeFilterCommand;
  98.         }

  99.         static CheckoutMetadata EMPTY = new CheckoutMetadata(
  100.                 EolStreamType.DIRECT, null);
  101.     }

  102.     private Repository repo;

  103.     private HashMap<String, CheckoutMetadata> updated = new HashMap<>();

  104.     private ArrayList<String> conflicts = new ArrayList<>();

  105.     private ArrayList<String> removed = new ArrayList<>();

  106.     private ArrayList<String> kept = new ArrayList<>();

  107.     private ObjectId mergeCommitTree;

  108.     private DirCache dc;

  109.     private DirCacheBuilder builder;

  110.     private NameConflictTreeWalk walk;

  111.     private ObjectId headCommitTree;

  112.     private WorkingTreeIterator workingTree;

  113.     private boolean failOnConflict = true;

  114.     private boolean force = false;

  115.     private ArrayList<String> toBeDeleted = new ArrayList<>();

  116.     private boolean initialCheckout;

  117.     private boolean performingCheckout;

  118.     private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;

  119.     /**
  120.      * Get list of updated paths and smudgeFilterCommands
  121.      *
  122.      * @return a list of updated paths and smudgeFilterCommands
  123.      */
  124.     public Map<String, CheckoutMetadata> getUpdated() {
  125.         return updated;
  126.     }

  127.     /**
  128.      * Get a list of conflicts created by this checkout
  129.      *
  130.      * @return a list of conflicts created by this checkout
  131.      */
  132.     public List<String> getConflicts() {
  133.         return conflicts;
  134.     }

  135.     /**
  136.      * Get list of paths of files which couldn't be deleted during last call to
  137.      * {@link #checkout()}
  138.      *
  139.      * @return a list of paths (relative to the start of the working tree) of
  140.      *         files which couldn't be deleted during last call to
  141.      *         {@link #checkout()} . {@link #checkout()} detected that these
  142.      *         files should be deleted but the deletion in the filesystem failed
  143.      *         (e.g. because a file was locked). To have a consistent state of
  144.      *         the working tree these files have to be deleted by the callers of
  145.      *         {@link org.eclipse.jgit.dircache.DirCacheCheckout}.
  146.      */
  147.     public List<String> getToBeDeleted() {
  148.         return toBeDeleted;
  149.     }

  150.     /**
  151.      * Get list of all files removed by this checkout
  152.      *
  153.      * @return a list of all files removed by this checkout
  154.      */
  155.     public List<String> getRemoved() {
  156.         return removed;
  157.     }

  158.     /**
  159.      * Constructs a DirCacheCeckout for merging and checking out two trees (HEAD
  160.      * and mergeCommitTree) and the index.
  161.      *
  162.      * @param repo
  163.      *            the repository in which we do the checkout
  164.      * @param headCommitTree
  165.      *            the id of the tree of the head commit
  166.      * @param dc
  167.      *            the (already locked) Dircache for this repo
  168.      * @param mergeCommitTree
  169.      *            the id of the tree we want to fast-forward to
  170.      * @param workingTree
  171.      *            an iterator over the repositories Working Tree
  172.      * @throws java.io.IOException
  173.      */
  174.     public DirCacheCheckout(Repository repo, ObjectId headCommitTree, DirCache dc,
  175.             ObjectId mergeCommitTree, WorkingTreeIterator workingTree)
  176.             throws IOException {
  177.         this.repo = repo;
  178.         this.dc = dc;
  179.         this.headCommitTree = headCommitTree;
  180.         this.mergeCommitTree = mergeCommitTree;
  181.         this.workingTree = workingTree;
  182.         this.initialCheckout = !repo.isBare() && !repo.getIndexFile().exists();
  183.     }

  184.     /**
  185.      * Constructs a DirCacheCeckout for merging and checking out two trees (HEAD
  186.      * and mergeCommitTree) and the index. As iterator over the working tree
  187.      * this constructor creates a standard
  188.      * {@link org.eclipse.jgit.treewalk.FileTreeIterator}
  189.      *
  190.      * @param repo
  191.      *            the repository in which we do the checkout
  192.      * @param headCommitTree
  193.      *            the id of the tree of the head commit
  194.      * @param dc
  195.      *            the (already locked) Dircache for this repo
  196.      * @param mergeCommitTree
  197.      *            the id of the tree we want to fast-forward to
  198.      * @throws java.io.IOException
  199.      */
  200.     public DirCacheCheckout(Repository repo, ObjectId headCommitTree,
  201.             DirCache dc, ObjectId mergeCommitTree) throws IOException {
  202.         this(repo, headCommitTree, dc, mergeCommitTree, new FileTreeIterator(repo));
  203.     }

  204.     /**
  205.      * Constructs a DirCacheCeckout for checking out one tree, merging with the
  206.      * index.
  207.      *
  208.      * @param repo
  209.      *            the repository in which we do the checkout
  210.      * @param dc
  211.      *            the (already locked) Dircache for this repo
  212.      * @param mergeCommitTree
  213.      *            the id of the tree we want to fast-forward to
  214.      * @param workingTree
  215.      *            an iterator over the repositories Working Tree
  216.      * @throws java.io.IOException
  217.      */
  218.     public DirCacheCheckout(Repository repo, DirCache dc,
  219.             ObjectId mergeCommitTree, WorkingTreeIterator workingTree)
  220.             throws IOException {
  221.         this(repo, null, dc, mergeCommitTree, workingTree);
  222.     }

  223.     /**
  224.      * Constructs a DirCacheCeckout for checking out one tree, merging with the
  225.      * index. As iterator over the working tree this constructor creates a
  226.      * standard {@link org.eclipse.jgit.treewalk.FileTreeIterator}
  227.      *
  228.      * @param repo
  229.      *            the repository in which we do the checkout
  230.      * @param dc
  231.      *            the (already locked) Dircache for this repo
  232.      * @param mergeCommitTree
  233.      *            the id of the tree of the
  234.      * @throws java.io.IOException
  235.      */
  236.     public DirCacheCheckout(Repository repo, DirCache dc,
  237.             ObjectId mergeCommitTree) throws IOException {
  238.         this(repo, null, dc, mergeCommitTree, new FileTreeIterator(repo));
  239.     }

  240.     /**
  241.      * Set a progress monitor which can be passed to built-in filter commands,
  242.      * providing progress information for long running tasks.
  243.      *
  244.      * @param monitor
  245.      *            the {@link ProgressMonitor}
  246.      * @since 4.11
  247.      */
  248.     public void setProgressMonitor(ProgressMonitor monitor) {
  249.         this.monitor = monitor != null ? monitor : NullProgressMonitor.INSTANCE;
  250.     }

  251.     /**
  252.      * Scan head, index and merge tree. Used during normal checkout or merge
  253.      * operations.
  254.      *
  255.      * @throws org.eclipse.jgit.errors.CorruptObjectException
  256.      * @throws java.io.IOException
  257.      */
  258.     public void preScanTwoTrees() throws CorruptObjectException, IOException {
  259.         removed.clear();
  260.         updated.clear();
  261.         conflicts.clear();
  262.         walk = new NameConflictTreeWalk(repo);
  263.         builder = dc.builder();

  264.         addTree(walk, headCommitTree);
  265.         addTree(walk, mergeCommitTree);
  266.         int dciPos = walk.addTree(new DirCacheBuildIterator(builder));
  267.         walk.addTree(workingTree);
  268.         workingTree.setDirCacheIterator(walk, dciPos);

  269.         while (walk.next()) {
  270.             processEntry(walk.getTree(0, CanonicalTreeParser.class),
  271.                     walk.getTree(1, CanonicalTreeParser.class),
  272.                     walk.getTree(2, DirCacheBuildIterator.class),
  273.                     walk.getTree(3, WorkingTreeIterator.class));
  274.             if (walk.isSubtree())
  275.                 walk.enterSubtree();
  276.         }
  277.     }

  278.     private void addTree(TreeWalk tw, ObjectId id) throws MissingObjectException, IncorrectObjectTypeException, IOException {
  279.         if (id == null)
  280.             tw.addTree(new EmptyTreeIterator());
  281.         else
  282.             tw.addTree(id);
  283.     }

  284.     /**
  285.      * Scan index and merge tree (no HEAD). Used e.g. for initial checkout when
  286.      * there is no head yet.
  287.      *
  288.      * @throws org.eclipse.jgit.errors.MissingObjectException
  289.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  290.      * @throws org.eclipse.jgit.errors.CorruptObjectException
  291.      * @throws java.io.IOException
  292.      */
  293.     public void prescanOneTree()
  294.             throws MissingObjectException, IncorrectObjectTypeException,
  295.             CorruptObjectException, IOException {
  296.         removed.clear();
  297.         updated.clear();
  298.         conflicts.clear();

  299.         builder = dc.builder();

  300.         walk = new NameConflictTreeWalk(repo);
  301.         addTree(walk, mergeCommitTree);
  302.         int dciPos = walk.addTree(new DirCacheBuildIterator(builder));
  303.         walk.addTree(workingTree);
  304.         workingTree.setDirCacheIterator(walk, dciPos);

  305.         while (walk.next()) {
  306.             processEntry(walk.getTree(0, CanonicalTreeParser.class),
  307.                     walk.getTree(1, DirCacheBuildIterator.class),
  308.                     walk.getTree(2, WorkingTreeIterator.class));
  309.             if (walk.isSubtree())
  310.                 walk.enterSubtree();
  311.         }
  312.         conflicts.removeAll(removed);
  313.     }

  314.     /**
  315.      * Processing an entry in the context of {@link #prescanOneTree()} when only
  316.      * one tree is given
  317.      *
  318.      * @param m the tree to merge
  319.      * @param i the index
  320.      * @param f the working tree
  321.      * @throws IOException
  322.      */
  323.     void processEntry(CanonicalTreeParser m, DirCacheBuildIterator i,
  324.             WorkingTreeIterator f) throws IOException {
  325.         if (m != null) {
  326.             checkValidPath(m);
  327.             // There is an entry in the merge commit. Means: we want to update
  328.             // what's currently in the index and working-tree to that one
  329.             if (i == null) {
  330.                 // The index entry is missing
  331.                 if (f != null && !FileMode.TREE.equals(f.getEntryFileMode())
  332.                         && !f.isEntryIgnored()) {
  333.                     if (failOnConflict) {
  334.                         // don't overwrite an untracked and not ignored file
  335.                         conflicts.add(walk.getPathString());
  336.                     } else {
  337.                         // failOnConflict is false. Putting something to conflicts
  338.                         // would mean we delete it. Instead we want the mergeCommit
  339.                         // content to be checked out.
  340.                         update(m.getEntryPathString(), m.getEntryObjectId(),
  341.                                 m.getEntryFileMode());
  342.                     }
  343.                 } else
  344.                     update(m.getEntryPathString(), m.getEntryObjectId(),
  345.                         m.getEntryFileMode());
  346.             } else if (f == null || !m.idEqual(i)) {
  347.                 // The working tree file is missing or the merge content differs
  348.                 // from index content
  349.                 update(m.getEntryPathString(), m.getEntryObjectId(),
  350.                         m.getEntryFileMode());
  351.             } else if (i.getDirCacheEntry() != null) {
  352.                 // The index contains a file (and not a folder)
  353.                 if (f.isModified(i.getDirCacheEntry(), true,
  354.                         this.walk.getObjectReader())
  355.                         || i.getDirCacheEntry().getStage() != 0)
  356.                     // The working tree file is dirty or the index contains a
  357.                     // conflict
  358.                     update(m.getEntryPathString(), m.getEntryObjectId(),
  359.                             m.getEntryFileMode());
  360.                 else {
  361.                     // update the timestamp of the index with the one from the
  362.                     // file if not set, as we are sure to be in sync here.
  363.                     DirCacheEntry entry = i.getDirCacheEntry();
  364.                     Instant mtime = entry.getLastModifiedInstant();
  365.                     if (mtime == null || mtime.equals(Instant.EPOCH)) {
  366.                         entry.setLastModified(f.getEntryLastModifiedInstant());
  367.                     }
  368.                     keep(i.getEntryPathString(), entry, f);
  369.                 }
  370.             } else
  371.                 // The index contains a folder
  372.                 keep(i.getEntryPathString(), i.getDirCacheEntry(), f);
  373.         } else {
  374.             // There is no entry in the merge commit. Means: we want to delete
  375.             // what's currently in the index and working tree
  376.             if (f != null) {
  377.                 // There is a file/folder for that path in the working tree
  378.                 if (walk.isDirectoryFileConflict()) {
  379.                     // We put it in conflicts. Even if failOnConflict is false
  380.                     // this would cause the path to be deleted. Thats exactly what
  381.                     // we want in this situation
  382.                     conflicts.add(walk.getPathString());
  383.                 } else {
  384.                     // No file/folder conflict exists. All entries are files or
  385.                     // all entries are folders
  386.                     if (i != null) {
  387.                         // ... and the working tree contained a file or folder
  388.                         // -> add it to the removed set and remove it from
  389.                         // conflicts set
  390.                         remove(i.getEntryPathString());
  391.                         conflicts.remove(i.getEntryPathString());
  392.                     } else {
  393.                         // untracked file, neither contained in tree to merge
  394.                         // nor in index
  395.                     }
  396.                 }
  397.             } else {
  398.                 // There is no file/folder for that path in the working tree,
  399.                 // nor in the merge head.
  400.                 // The only entry we have is the index entry. Like the case
  401.                 // where there is a file with the same name, remove it,
  402.             }
  403.         }
  404.     }

  405.     /**
  406.      * Execute this checkout. A
  407.      * {@link org.eclipse.jgit.events.WorkingTreeModifiedEvent} is fired if the
  408.      * working tree was modified; even if the checkout fails.
  409.      *
  410.      * @return <code>false</code> if this method could not delete all the files
  411.      *         which should be deleted (e.g. because one of the files was
  412.      *         locked). In this case {@link #getToBeDeleted()} lists the files
  413.      *         which should be tried to be deleted outside of this method.
  414.      *         Although <code>false</code> is returned the checkout was
  415.      *         successful and the working tree was updated for all other files.
  416.      *         <code>true</code> is returned when no such problem occurred
  417.      * @throws java.io.IOException
  418.      */
  419.     public boolean checkout() throws IOException {
  420.         try {
  421.             return doCheckout();
  422.         } catch (CanceledException ce) {
  423.             // should actually be propagated, but this would change a LOT of
  424.             // APIs
  425.             throw new IOException(ce);
  426.         } finally {
  427.             try {
  428.                 dc.unlock();
  429.             } finally {
  430.                 if (performingCheckout) {
  431.                     Set<String> touched = new HashSet<>(conflicts);
  432.                     touched.addAll(getUpdated().keySet());
  433.                     touched.addAll(kept);
  434.                     WorkingTreeModifiedEvent event = new WorkingTreeModifiedEvent(
  435.                             touched, getRemoved());
  436.                     if (!event.isEmpty()) {
  437.                         repo.fireEvent(event);
  438.                     }
  439.                 }
  440.             }
  441.         }
  442.     }

  443.     private boolean doCheckout() throws CorruptObjectException, IOException,
  444.             MissingObjectException, IncorrectObjectTypeException,
  445.             CheckoutConflictException, IndexWriteException, CanceledException {
  446.         toBeDeleted.clear();
  447.         try (ObjectReader objectReader = repo.getObjectDatabase().newReader()) {
  448.             if (headCommitTree != null)
  449.                 preScanTwoTrees();
  450.             else
  451.                 prescanOneTree();

  452.             if (!conflicts.isEmpty()) {
  453.                 if (failOnConflict) {
  454.                     throw new CheckoutConflictException(conflicts.toArray(new String[0]));
  455.                 }
  456.                 cleanUpConflicts();
  457.             }

  458.             // update our index
  459.             builder.finish();

  460.             // init progress reporting
  461.             int numTotal = removed.size() + updated.size() + conflicts.size();
  462.             monitor.beginTask(JGitText.get().checkingOutFiles, numTotal);

  463.             performingCheckout = true;
  464.             File file = null;
  465.             String last = null;
  466.             // when deleting files process them in the opposite order as they have
  467.             // been reported. This ensures the files are deleted before we delete
  468.             // their parent folders
  469.             IntList nonDeleted = new IntList();
  470.             for (int i = removed.size() - 1; i >= 0; i--) {
  471.                 String r = removed.get(i);
  472.                 file = new File(repo.getWorkTree(), r);
  473.                 if (!file.delete() && repo.getFS().exists(file)) {
  474.                     // The list of stuff to delete comes from the index
  475.                     // which will only contain a directory if it is
  476.                     // a submodule, in which case we shall not attempt
  477.                     // to delete it. A submodule is not empty, so it
  478.                     // is safe to check this after a failed delete.
  479.                     if (!repo.getFS().isDirectory(file)) {
  480.                         nonDeleted.add(i);
  481.                         toBeDeleted.add(r);
  482.                     }
  483.                 } else {
  484.                     if (last != null && !isSamePrefix(r, last))
  485.                         removeEmptyParents(new File(repo.getWorkTree(), last));
  486.                     last = r;
  487.                 }
  488.                 monitor.update(1);
  489.                 if (monitor.isCancelled()) {
  490.                     throw new CanceledException(MessageFormat.format(
  491.                             JGitText.get().operationCanceled,
  492.                             JGitText.get().checkingOutFiles));
  493.                 }
  494.             }
  495.             if (file != null) {
  496.                 removeEmptyParents(file);
  497.             }
  498.             removed = filterOut(removed, nonDeleted);
  499.             nonDeleted = null;
  500.             Iterator<Map.Entry<String, CheckoutMetadata>> toUpdate = updated
  501.                     .entrySet().iterator();
  502.             Map.Entry<String, CheckoutMetadata> e = null;
  503.             try {
  504.                 while (toUpdate.hasNext()) {
  505.                     e = toUpdate.next();
  506.                     String path = e.getKey();
  507.                     CheckoutMetadata meta = e.getValue();
  508.                     DirCacheEntry entry = dc.getEntry(path);
  509.                     if (FileMode.GITLINK.equals(entry.getRawMode())) {
  510.                         checkoutGitlink(path, entry);
  511.                     } else {
  512.                         checkoutEntry(repo, entry, objectReader, false, meta);
  513.                     }
  514.                     e = null;

  515.                     monitor.update(1);
  516.                     if (monitor.isCancelled()) {
  517.                         throw new CanceledException(MessageFormat.format(
  518.                                 JGitText.get().operationCanceled,
  519.                                 JGitText.get().checkingOutFiles));
  520.                     }
  521.                 }
  522.             } catch (Exception ex) {
  523.                 // We didn't actually modify the current entry nor any that
  524.                 // might follow.
  525.                 if (e != null) {
  526.                     toUpdate.remove();
  527.                 }
  528.                 while (toUpdate.hasNext()) {
  529.                     e = toUpdate.next();
  530.                     toUpdate.remove();
  531.                 }
  532.                 throw ex;
  533.             }
  534.             for (String conflict : conflicts) {
  535.                 // the conflicts are likely to have multiple entries in the
  536.                 // dircache, we only want to check out the one for the "theirs"
  537.                 // tree
  538.                 int entryIdx = dc.findEntry(conflict);
  539.                 if (entryIdx >= 0) {
  540.                     while (entryIdx < dc.getEntryCount()) {
  541.                         DirCacheEntry entry = dc.getEntry(entryIdx);
  542.                         if (!entry.getPathString().equals(conflict)) {
  543.                             break;
  544.                         }
  545.                         if (entry.getStage() == DirCacheEntry.STAGE_3) {
  546.                             checkoutEntry(repo, entry, objectReader, false,
  547.                                     null);
  548.                             break;
  549.                         }
  550.                         ++entryIdx;
  551.                     }
  552.                 }

  553.                 monitor.update(1);
  554.                 if (monitor.isCancelled()) {
  555.                     throw new CanceledException(MessageFormat.format(
  556.                             JGitText.get().operationCanceled,
  557.                             JGitText.get().checkingOutFiles));
  558.                 }
  559.             }
  560.             monitor.endTask();

  561.             // commit the index builder - a new index is persisted
  562.             if (!builder.commit())
  563.                 throw new IndexWriteException();
  564.         }
  565.         return toBeDeleted.isEmpty();
  566.     }

  567.     private void checkoutGitlink(String path, DirCacheEntry entry)
  568.             throws IOException {
  569.         File gitlinkDir = new File(repo.getWorkTree(), path);
  570.         FileUtils.mkdirs(gitlinkDir, true);
  571.         FS fs = repo.getFS();
  572.         entry.setLastModified(fs.lastModifiedInstant(gitlinkDir));
  573.     }

  574.     private static ArrayList<String> filterOut(ArrayList<String> strings,
  575.             IntList indicesToRemove) {
  576.         int n = indicesToRemove.size();
  577.         if (n == strings.size()) {
  578.             return new ArrayList<>(0);
  579.         }
  580.         switch (n) {
  581.         case 0:
  582.             return strings;
  583.         case 1:
  584.             strings.remove(indicesToRemove.get(0));
  585.             return strings;
  586.         default:
  587.             int length = strings.size();
  588.             ArrayList<String> result = new ArrayList<>(length - n);
  589.             // Process indicesToRemove from the back; we know that it
  590.             // contains indices in descending order.
  591.             int j = n - 1;
  592.             int idx = indicesToRemove.get(j);
  593.             for (int i = 0; i < length; i++) {
  594.                 if (i == idx) {
  595.                     idx = (--j >= 0) ? indicesToRemove.get(j) : -1;
  596.                 } else {
  597.                     result.add(strings.get(i));
  598.                 }
  599.             }
  600.             return result;
  601.         }
  602.     }

  603.     private static boolean isSamePrefix(String a, String b) {
  604.         int as = a.lastIndexOf('/');
  605.         int bs = b.lastIndexOf('/');
  606.         return a.substring(0, as + 1).equals(b.substring(0, bs + 1));
  607.     }

  608.      private void removeEmptyParents(File f) {
  609.         File parentFile = f.getParentFile();

  610.         while (parentFile != null && !parentFile.equals(repo.getWorkTree())) {
  611.             if (!parentFile.delete())
  612.                 break;
  613.             parentFile = parentFile.getParentFile();
  614.         }
  615.     }

  616.     /**
  617.      * Compares whether two pairs of ObjectId and FileMode are equal.
  618.      *
  619.      * @param id1
  620.      * @param mode1
  621.      * @param id2
  622.      * @param mode2
  623.      * @return <code>true</code> if FileModes and ObjectIds are equal.
  624.      *         <code>false</code> otherwise
  625.      */
  626.     private boolean equalIdAndMode(ObjectId id1, FileMode mode1, ObjectId id2,
  627.             FileMode mode2) {
  628.         if (!mode1.equals(mode2))
  629.             return false;
  630.         return id1 != null ? id1.equals(id2) : id2 == null;
  631.     }

  632.     /**
  633.      * Here the main work is done. This method is called for each existing path
  634.      * in head, index and merge. This method decides what to do with the
  635.      * corresponding index entry: keep it, update it, remove it or mark a
  636.      * conflict.
  637.      *
  638.      * @param h
  639.      *            the entry for the head
  640.      * @param m
  641.      *            the entry for the merge
  642.      * @param i
  643.      *            the entry for the index
  644.      * @param f
  645.      *            the file in the working tree
  646.      * @throws IOException
  647.      */

  648.     void processEntry(CanonicalTreeParser h, CanonicalTreeParser m,
  649.             DirCacheBuildIterator i, WorkingTreeIterator f) throws IOException {
  650.         DirCacheEntry dce = i != null ? i.getDirCacheEntry() : null;

  651.         String name = walk.getPathString();

  652.         if (m != null)
  653.             checkValidPath(m);

  654.         if (i == null && m == null && h == null) {
  655.             // File/Directory conflict case #20
  656.             if (walk.isDirectoryFileConflict())
  657.                 // TODO: check whether it is always correct to report a conflict here
  658.                 conflict(name, null, null, null);

  659.             // file only exists in working tree -> ignore it
  660.             return;
  661.         }

  662.         ObjectId iId = (i == null ? null : i.getEntryObjectId());
  663.         ObjectId mId = (m == null ? null : m.getEntryObjectId());
  664.         ObjectId hId = (h == null ? null : h.getEntryObjectId());
  665.         FileMode iMode = (i == null ? null : i.getEntryFileMode());
  666.         FileMode mMode = (m == null ? null : m.getEntryFileMode());
  667.         FileMode hMode = (h == null ? null : h.getEntryFileMode());

  668.         /**
  669.          * <pre>
  670.          *  File/Directory conflicts:
  671.          *  the following table from ReadTreeTest tells what to do in case of directory/file
  672.          *  conflicts. I give comments here
  673.          *
  674.          *      H        I       M     Clean     H==M     H==I    I==M         Result
  675.          *      ------------------------------------------------------------------
  676.          * 1    D        D       F       Y         N       Y       N           Update
  677.          * 2    D        D       F       N         N       Y       N           Conflict
  678.          * 3    D        F       D                 Y       N       N           Keep
  679.          * 4    D        F       D                 N       N       N           Conflict
  680.          * 5    D        F       F       Y         N       N       Y           Keep
  681.          * 5b   D        F       F       Y         N       N       N           Conflict
  682.          * 6    D        F       F       N         N       N       Y           Keep
  683.          * 6b   D        F       F       N         N       N       N           Conflict
  684.          * 7    F        D       F       Y         Y       N       N           Update
  685.          * 8    F        D       F       N         Y       N       N           Conflict
  686.          * 9    F        D       F                 N       N       N           Conflict
  687.          * 10   F        D       D                 N       N       Y           Keep
  688.          * 11   F        D       D                 N       N       N           Conflict
  689.          * 12   F        F       D       Y         N       Y       N           Update
  690.          * 13   F        F       D       N         N       Y       N           Conflict
  691.          * 14   F        F       D                 N       N       N           Conflict
  692.          * 15   0        F       D                 N       N       N           Conflict
  693.          * 16   0        D       F       Y         N       N       N           Update
  694.          * 17   0        D       F                 N       N       N           Conflict
  695.          * 18   F        0       D                                             Update
  696.          * 19   D        0       F                                             Update
  697.          * 20   0        0       F       N (worktree=dir)                      Conflict
  698.          * </pre>
  699.          */

  700.         // The information whether head,index,merge iterators are currently
  701.         // pointing to file/folder/non-existing is encoded into this variable.
  702.         //
  703.         // To decode write down ffMask in hexadecimal form. The last digit
  704.         // represents the state for the merge iterator, the second last the
  705.         // state for the index iterator and the third last represents the state
  706.         // for the head iterator. The hexadecimal constant "F" stands for
  707.         // "file", a "D" stands for "directory" (tree), and a "0" stands for
  708.         // non-existing. Symbolic links and git links are treated as File here.
  709.         //
  710.         // Examples:
  711.         // ffMask == 0xFFD -> Head=File, Index=File, Merge=Tree
  712.         // ffMask == 0xDD0 -> Head=Tree, Index=Tree, Merge=Non-Existing

  713.         int ffMask = 0;
  714.         if (h != null)
  715.             ffMask = FileMode.TREE.equals(hMode) ? 0xD00 : 0xF00;
  716.         if (i != null)
  717.             ffMask |= FileMode.TREE.equals(iMode) ? 0x0D0 : 0x0F0;
  718.         if (m != null)
  719.             ffMask |= FileMode.TREE.equals(mMode) ? 0x00D : 0x00F;

  720.         // Check whether we have a possible file/folder conflict. Therefore we
  721.         // need a least one file and one folder.
  722.         if (((ffMask & 0x222) != 0x000)
  723.                 && (((ffMask & 0x00F) == 0x00D) || ((ffMask & 0x0F0) == 0x0D0) || ((ffMask & 0xF00) == 0xD00))) {

  724.             // There are 3*3*3=27 possible combinations of file/folder
  725.             // conflicts. Some of them are not-relevant because
  726.             // they represent no conflict, e.g. 0xFFF, 0xDDD, ... The following
  727.             // switch processes all relevant cases.
  728.             switch (ffMask) {
  729.             case 0xDDF: // 1 2
  730.                 if (f != null && isModifiedSubtree_IndexWorkingtree(name)) {
  731.                     conflict(name, dce, h, m); // 1
  732.                 } else {
  733.                     update(name, mId, mMode); // 2
  734.                 }

  735.                 break;
  736.             case 0xDFD: // 3 4
  737.                 keep(name, dce, f);
  738.                 break;
  739.             case 0xF0D: // 18
  740.                 remove(name);
  741.                 break;
  742.             case 0xDFF: // 5 5b 6 6b
  743.                 if (equalIdAndMode(iId, iMode, mId, mMode))
  744.                     keep(name, dce, f); // 5 6
  745.                 else
  746.                     conflict(name, dce, h, m); // 5b 6b
  747.                 break;
  748.             case 0xFDD: // 10 11
  749.                 // TODO: make use of tree extension as soon as available in jgit
  750.                 // we would like to do something like
  751.                 // if (!equalIdAndMode(iId, iMode, mId, mMode)
  752.                 //   conflict(name, i.getDirCacheEntry(), h, m);
  753.                 // But since we don't know the id of a tree in the index we do
  754.                 // nothing here and wait that conflicts between index and merge
  755.                 // are found later
  756.                 break;
  757.             case 0xD0F: // 19
  758.                 update(name, mId, mMode);
  759.                 break;
  760.             case 0xDF0: // conflict without a rule
  761.             case 0x0FD: // 15
  762.                 conflict(name, dce, h, m);
  763.                 break;
  764.             case 0xFDF: // 7 8 9
  765.                 if (equalIdAndMode(hId, hMode, mId, mMode)) {
  766.                     if (isModifiedSubtree_IndexWorkingtree(name))
  767.                         conflict(name, dce, h, m); // 8
  768.                     else
  769.                         update(name, mId, mMode); // 7
  770.                 } else
  771.                     conflict(name, dce, h, m); // 9
  772.                 break;
  773.             case 0xFD0: // keep without a rule
  774.                 keep(name, dce, f);
  775.                 break;
  776.             case 0xFFD: // 12 13 14
  777.                 if (equalIdAndMode(hId, hMode, iId, iMode))
  778.                     if (f != null
  779.                             && f.isModified(dce, true,
  780.                                     this.walk.getObjectReader()))
  781.                         conflict(name, dce, h, m); // 13
  782.                     else
  783.                         remove(name); // 12
  784.                 else
  785.                     conflict(name, dce, h, m); // 14
  786.                 break;
  787.             case 0x0DF: // 16 17
  788.                 if (!isModifiedSubtree_IndexWorkingtree(name))
  789.                     update(name, mId, mMode);
  790.                 else
  791.                     conflict(name, dce, h, m);
  792.                 break;
  793.             default:
  794.                 keep(name, dce, f);
  795.             }
  796.             return;
  797.         }

  798.         if ((ffMask & 0x222) == 0) {
  799.             // HEAD, MERGE and index don't contain a file (e.g. all contain a
  800.             // folder)
  801.             if (f == null || FileMode.TREE.equals(f.getEntryFileMode())) {
  802.                 // the workingtree entry doesn't exist or also contains a folder
  803.                 // -> no problem
  804.                 return;
  805.             }
  806.             // the workingtree entry exists and is not a folder
  807.             if (!idEqual(h, m)) {
  808.                 // Because HEAD and MERGE differ we will try to update the
  809.                 // workingtree with a folder -> return a conflict
  810.                 conflict(name, null, null, null);
  811.             }
  812.             return;
  813.         }

  814.         if ((ffMask == 0x00F) && f != null && FileMode.TREE.equals(f.getEntryFileMode())) {
  815.             // File/Directory conflict case #20
  816.             conflict(name, null, h, m);
  817.             return;
  818.         }

  819.         if (i == null) {
  820.             // Nothing in Index
  821.             // At least one of Head, Index, Merge is not empty
  822.             // make sure not to overwrite untracked files
  823.             if (f != null && !f.isEntryIgnored()) {
  824.                 // A submodule is not a file. We should ignore it
  825.                 if (!FileMode.GITLINK.equals(mMode)) {
  826.                     // a dirty worktree: the index is empty but we have a
  827.                     // workingtree-file
  828.                     if (mId == null
  829.                             || !equalIdAndMode(mId, mMode,
  830.                                     f.getEntryObjectId(), f.getEntryFileMode())) {
  831.                         conflict(name, null, h, m);
  832.                         return;
  833.                     }
  834.                 }
  835.             }

  836.             /**
  837.              * <pre>
  838.              *            I (index)     H        M     H==M  Result
  839.              *          -------------------------------------------
  840.              *          0 nothing    nothing  nothing        (does not happen)
  841.              *          1 nothing    nothing  exists         use M
  842.              *          2 nothing    exists   nothing        remove path from index
  843.              *          3 nothing    exists   exists   yes   keep index if not in initial checkout
  844.              *                                               , otherwise use M
  845.              *            nothing    exists   exists   no    fail
  846.              * </pre>
  847.              */

  848.             if (h == null)
  849.                 // Nothing in Head
  850.                 // Nothing in Index
  851.                 // At least one of Head, Index, Merge is not empty
  852.                 // -> only Merge contains something for this path. Use it!
  853.                 // Potentially update the file
  854.                 update(name, mId, mMode); // 1
  855.             else if (m == null)
  856.                 // Nothing in Merge
  857.                 // Something in Head
  858.                 // Nothing in Index
  859.                 // -> only Head contains something for this path and it should
  860.                 // be deleted. Potentially removes the file!
  861.                 remove(name); // 2
  862.             else { // 3
  863.                 // Something in Merge
  864.                 // Something in Head
  865.                 // Nothing in Index
  866.                 // -> Head and Merge contain something (maybe not the same) and
  867.                 // in the index there is nothing (e.g. 'git rm ...' was
  868.                 // called before). Ignore the cached deletion and use what we
  869.                 // find in Merge. Potentially updates the file.
  870.                 if (equalIdAndMode(hId, hMode, mId, mMode)) {
  871.                     if (initialCheckout || force) {
  872.                         update(name, mId, mMode);
  873.                     } else {
  874.                         keep(name, dce, f);
  875.                     }
  876.                 } else {
  877.                     conflict(name, dce, h, m);
  878.                 }
  879.             }
  880.         } else {
  881.             // Something in Index
  882.             if (h == null) {
  883.                 // Nothing in Head
  884.                 // Something in Index
  885.                 /**
  886.                  * <pre>
  887.                  *            clean I==H  I==M       H        M        Result
  888.                  *           -----------------------------------------------------
  889.                  *          4 yes   N/A   N/A     nothing  nothing  keep index
  890.                  *          5 no    N/A   N/A     nothing  nothing  keep index
  891.                  *
  892.                  *          6 yes   N/A   yes     nothing  exists   keep index
  893.                  *          7 no    N/A   yes     nothing  exists   keep index
  894.                  *          8 yes   N/A   no      nothing  exists   fail
  895.                  *          9 no    N/A   no      nothing  exists   fail
  896.                  * </pre>
  897.                  */

  898.                 if (m == null
  899.                         || !isModified_IndexTree(name, iId, iMode, mId, mMode,
  900.                                 mergeCommitTree)) {
  901.                     // Merge contains nothing or the same as Index
  902.                     // Nothing in Head
  903.                     // Something in Index
  904.                     if (m==null && walk.isDirectoryFileConflict()) {
  905.                         // Nothing in Merge and current path is part of
  906.                         // File/Folder conflict
  907.                         // Nothing in Head
  908.                         // Something in Index
  909.                         if (dce != null
  910.                                 && (f == null || f.isModified(dce, true,
  911.                                         this.walk.getObjectReader())))
  912.                             // No file or file is dirty
  913.                             // Nothing in Merge and current path is part of
  914.                             // File/Folder conflict
  915.                             // Nothing in Head
  916.                             // Something in Index
  917.                             // -> File folder conflict and Merge wants this
  918.                             // path to be removed. Since the file is dirty
  919.                             // report a conflict
  920.                             conflict(name, dce, h, m);
  921.                         else
  922.                             // A file is present and file is not dirty
  923.                             // Nothing in Merge and current path is part of
  924.                             // File/Folder conflict
  925.                             // Nothing in Head
  926.                             // Something in Index
  927.                             // -> File folder conflict and Merge wants this path
  928.                             // to be removed. Since the file is not dirty remove
  929.                             // file and index entry
  930.                             remove(name);
  931.                     } else
  932.                         // Something in Merge or current path is not part of
  933.                         // File/Folder conflict
  934.                         // Merge contains nothing or the same as Index
  935.                         // Nothing in Head
  936.                         // Something in Index
  937.                         // -> Merge contains nothing new. Keep the index.
  938.                         keep(name, dce, f);
  939.                 } else
  940.                     // Merge contains something and it is not the same as Index
  941.                     // Nothing in Head
  942.                     // Something in Index
  943.                     // -> Index contains something new (different from Head)
  944.                     // and Merge is different from Index. Report a conflict
  945.                     conflict(name, dce, h, m);
  946.             } else if (m == null) {
  947.                 // Nothing in Merge
  948.                 // Something in Head
  949.                 // Something in Index

  950.                 /**
  951.                  * <pre>
  952.                  *             clean I==H  I==M       H        M        Result
  953.                  *           -----------------------------------------------------
  954.                  *          10 yes   yes   N/A     exists   nothing  remove path from index
  955.                  *          11 no    yes   N/A     exists   nothing  keep file
  956.                  *          12 yes   no    N/A     exists   nothing  fail
  957.                  *          13 no    no    N/A     exists   nothing  fail
  958.                  * </pre>
  959.                  */

  960.                 if (iMode == FileMode.GITLINK) {
  961.                     // A submodule in Index
  962.                     // Nothing in Merge
  963.                     // Something in Head
  964.                     // Submodules that disappear from the checkout must
  965.                     // be removed from the index, but not deleted from disk.
  966.                     remove(name);
  967.                 } else {
  968.                     // Something different from a submodule in Index
  969.                     // Nothing in Merge
  970.                     // Something in Head
  971.                     if (!isModified_IndexTree(name, iId, iMode, hId, hMode,
  972.                             headCommitTree)) {
  973.                         // Index contains the same as Head
  974.                         // Something different from a submodule in Index
  975.                         // Nothing in Merge
  976.                         // Something in Head
  977.                         if (f != null
  978.                                 && f.isModified(dce, true,
  979.                                         this.walk.getObjectReader())) {
  980.                             // file is dirty
  981.                             // Index contains the same as Head
  982.                             // Something different from a submodule in Index
  983.                             // Nothing in Merge
  984.                             // Something in Head

  985.                             if (!FileMode.TREE.equals(f.getEntryFileMode())
  986.                                     && FileMode.TREE.equals(iMode)) {
  987.                                 // The workingtree contains a file and the index semantically contains a folder.
  988.                                 // Git considers the workingtree file as untracked. Just keep the untracked file.
  989.                                 return;
  990.                             }
  991.                             // -> file is dirty and tracked but is should be
  992.                             // removed. That's a conflict
  993.                             conflict(name, dce, h, m);
  994.                         } else {
  995.                             // file doesn't exist or is clean
  996.                             // Index contains the same as Head
  997.                             // Something different from a submodule in Index
  998.                             // Nothing in Merge
  999.                             // Something in Head
  1000.                             // -> Remove from index and delete the file
  1001.                             remove(name);
  1002.                         }
  1003.                     } else {
  1004.                         // Index contains something different from Head
  1005.                         // Something different from a submodule in Index
  1006.                         // Nothing in Merge
  1007.                         // Something in Head
  1008.                         // -> Something new is in index (and maybe even on the
  1009.                         // filesystem). But Merge wants the path to be removed.
  1010.                         // Report a conflict
  1011.                         conflict(name, dce, h, m);
  1012.                     }
  1013.                 }
  1014.             } else {
  1015.                 // Something in Merge
  1016.                 // Something in Head
  1017.                 // Something in Index
  1018.                 if (!equalIdAndMode(hId, hMode, mId, mMode)
  1019.                         && isModified_IndexTree(name, iId, iMode, hId, hMode,
  1020.                                 headCommitTree)
  1021.                         && isModified_IndexTree(name, iId, iMode, mId, mMode,
  1022.                                 mergeCommitTree))
  1023.                     // All three contents in Head, Merge, Index differ from each
  1024.                     // other
  1025.                     // -> All contents differ. Report a conflict.
  1026.                     conflict(name, dce, h, m);
  1027.                 else
  1028.                     // At least two of the contents of Head, Index, Merge
  1029.                     // are the same
  1030.                     // Something in Merge
  1031.                     // Something in Head
  1032.                     // Something in Index

  1033.                 if (!isModified_IndexTree(name, iId, iMode, hId, hMode,
  1034.                         headCommitTree)
  1035.                         && isModified_IndexTree(name, iId, iMode, mId, mMode,
  1036.                                 mergeCommitTree)) {
  1037.                         // Head contains the same as Index. Merge differs
  1038.                         // Something in Merge

  1039.                     // For submodules just update the index with the new SHA-1
  1040.                     if (dce != null
  1041.                             && FileMode.GITLINK.equals(dce.getFileMode())) {
  1042.                         // Index and Head contain the same submodule. Merge
  1043.                         // differs
  1044.                         // Something in Merge
  1045.                         // -> Nothing new in index. Move to merge.
  1046.                         // Potentially updates the file

  1047.                         // TODO check that we don't overwrite some unsaved
  1048.                         // file content
  1049.                         update(name, mId, mMode);
  1050.                     } else if (dce != null
  1051.                             && (f != null && f.isModified(dce, true,
  1052.                                     this.walk.getObjectReader()))) {
  1053.                         // File exists and is dirty
  1054.                         // Head and Index don't contain a submodule
  1055.                         // Head contains the same as Index. Merge differs
  1056.                         // Something in Merge
  1057.                         // -> Merge wants the index and file to be updated
  1058.                         // but the file is dirty. Report a conflict
  1059.                         conflict(name, dce, h, m);
  1060.                     } else {
  1061.                         // File doesn't exist or is clean
  1062.                         // Head and Index don't contain a submodule
  1063.                         // Head contains the same as Index. Merge differs
  1064.                         // Something in Merge
  1065.                         // -> Standard case when switching between branches:
  1066.                         // Nothing new in index but something different in
  1067.                         // Merge. Update index and file
  1068.                         update(name, mId, mMode);
  1069.                     }
  1070.                 } else {
  1071.                     // Head differs from index or merge is same as index
  1072.                     // At least two of the contents of Head, Index, Merge
  1073.                     // are the same
  1074.                     // Something in Merge
  1075.                     // Something in Head
  1076.                     // Something in Index

  1077.                     // Can be formulated as: Either all three states are
  1078.                     // equal or Merge is equal to Head or Index and differs
  1079.                     // to the other one.
  1080.                     // -> In all three cases we don't touch index and file.

  1081.                     keep(name, dce, f);
  1082.                 }
  1083.             }
  1084.         }
  1085.     }

  1086.     private static boolean idEqual(AbstractTreeIterator a,
  1087.             AbstractTreeIterator b) {
  1088.         if (a == b) {
  1089.             return true;
  1090.         }
  1091.         if (a == null || b == null) {
  1092.             return false;
  1093.         }
  1094.         return a.getEntryObjectId().equals(b.getEntryObjectId());
  1095.     }

  1096.     /**
  1097.      * A conflict is detected - add the three different stages to the index
  1098.      * @param path the path of the conflicting entry
  1099.      * @param e the previous index entry
  1100.      * @param h the first tree you want to merge (the HEAD)
  1101.      * @param m the second tree you want to merge
  1102.      */
  1103.     private void conflict(String path, DirCacheEntry e, AbstractTreeIterator h, AbstractTreeIterator m) {
  1104.         conflicts.add(path);

  1105.         DirCacheEntry entry;
  1106.         if (e != null) {
  1107.             entry = new DirCacheEntry(e.getPathString(), DirCacheEntry.STAGE_1);
  1108.             entry.copyMetaData(e, true);
  1109.             builder.add(entry);
  1110.         }

  1111.         if (h != null && !FileMode.TREE.equals(h.getEntryFileMode())) {
  1112.             entry = new DirCacheEntry(h.getEntryPathString(), DirCacheEntry.STAGE_2);
  1113.             entry.setFileMode(h.getEntryFileMode());
  1114.             entry.setObjectId(h.getEntryObjectId());
  1115.             builder.add(entry);
  1116.         }

  1117.         if (m != null && !FileMode.TREE.equals(m.getEntryFileMode())) {
  1118.             entry = new DirCacheEntry(m.getEntryPathString(), DirCacheEntry.STAGE_3);
  1119.             entry.setFileMode(m.getEntryFileMode());
  1120.             entry.setObjectId(m.getEntryObjectId());
  1121.             builder.add(entry);
  1122.         }
  1123.     }

  1124.     private void keep(String path, DirCacheEntry e, WorkingTreeIterator f)
  1125.             throws IOException {
  1126.         if (e == null) {
  1127.             return;
  1128.         }
  1129.         if (!FileMode.TREE.equals(e.getFileMode())) {
  1130.             builder.add(e);
  1131.         }
  1132.         if (force) {
  1133.             if (f == null || f.isModified(e, true, walk.getObjectReader())) {
  1134.                 kept.add(path);
  1135.                 checkoutEntry(repo, e, walk.getObjectReader(), false,
  1136.                         new CheckoutMetadata(walk.getEolStreamType(CHECKOUT_OP),
  1137.                                 walk.getFilterCommand(
  1138.                                         Constants.ATTR_FILTER_TYPE_SMUDGE)));
  1139.             }
  1140.         }
  1141.     }

  1142.     private void remove(String path) {
  1143.         removed.add(path);
  1144.     }

  1145.     private void update(String path, ObjectId mId, FileMode mode)
  1146.             throws IOException {
  1147.         if (!FileMode.TREE.equals(mode)) {
  1148.             updated.put(path, new CheckoutMetadata(
  1149.                     walk.getEolStreamType(CHECKOUT_OP),
  1150.                     walk.getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE)));

  1151.             DirCacheEntry entry = new DirCacheEntry(path, DirCacheEntry.STAGE_0);
  1152.             entry.setObjectId(mId);
  1153.             entry.setFileMode(mode);
  1154.             builder.add(entry);
  1155.         }
  1156.     }

  1157.     /**
  1158.      * If <code>true</code>, will scan first to see if it's possible to check
  1159.      * out, otherwise throw
  1160.      * {@link org.eclipse.jgit.errors.CheckoutConflictException}. If
  1161.      * <code>false</code>, it will silently deal with the problem.
  1162.      *
  1163.      * @param failOnConflict
  1164.      *            a boolean.
  1165.      */
  1166.     public void setFailOnConflict(boolean failOnConflict) {
  1167.         this.failOnConflict = failOnConflict;
  1168.     }

  1169.     /**
  1170.      * If <code>true</code>, dirty worktree files may be overridden. If
  1171.      * <code>false</code> dirty worktree files will not be overridden in order
  1172.      * not to delete unsaved content. This corresponds to native git's 'git
  1173.      * checkout -f' option. By default this option is set to false.
  1174.      *
  1175.      * @param force
  1176.      *            a boolean.
  1177.      * @since 5.3
  1178.      */
  1179.     public void setForce(boolean force) {
  1180.         this.force = force;
  1181.     }

  1182.     /**
  1183.      * This method implements how to handle conflicts when
  1184.      * {@link #failOnConflict} is false
  1185.      *
  1186.      * @throws CheckoutConflictException
  1187.      */
  1188.     private void cleanUpConflicts() throws CheckoutConflictException {
  1189.         // TODO: couldn't we delete unsaved worktree content here?
  1190.         for (String c : conflicts) {
  1191.             File conflict = new File(repo.getWorkTree(), c);
  1192.             if (!conflict.delete())
  1193.                 throw new CheckoutConflictException(MessageFormat.format(
  1194.                         JGitText.get().cannotDeleteFile, c));
  1195.             removeEmptyParents(conflict);
  1196.         }
  1197.     }

  1198.     /**
  1199.      * Checks whether the subtree starting at a given path differs between Index and
  1200.      * workingtree.
  1201.      *
  1202.      * @param path
  1203.      * @return true if the subtrees differ
  1204.      * @throws CorruptObjectException
  1205.      * @throws IOException
  1206.      */
  1207.     private boolean isModifiedSubtree_IndexWorkingtree(String path)
  1208.             throws CorruptObjectException, IOException {
  1209.         try (NameConflictTreeWalk tw = new NameConflictTreeWalk(repo)) {
  1210.             int dciPos = tw.addTree(new DirCacheIterator(dc));
  1211.             FileTreeIterator fti = new FileTreeIterator(repo);
  1212.             tw.addTree(fti);
  1213.             fti.setDirCacheIterator(tw, dciPos);
  1214.             tw.setRecursive(true);
  1215.             tw.setFilter(PathFilter.create(path));
  1216.             DirCacheIterator dcIt;
  1217.             WorkingTreeIterator wtIt;
  1218.             while (tw.next()) {
  1219.                 dcIt = tw.getTree(0, DirCacheIterator.class);
  1220.                 wtIt = tw.getTree(1, WorkingTreeIterator.class);
  1221.                 if (dcIt == null || wtIt == null)
  1222.                     return true;
  1223.                 if (wtIt.isModified(dcIt.getDirCacheEntry(), true,
  1224.                         this.walk.getObjectReader())) {
  1225.                     return true;
  1226.                 }
  1227.             }
  1228.             return false;
  1229.         }
  1230.     }

  1231.     private boolean isModified_IndexTree(String path, ObjectId iId,
  1232.             FileMode iMode, ObjectId tId, FileMode tMode, ObjectId rootTree)
  1233.             throws CorruptObjectException, IOException {
  1234.         if (iMode != tMode) {
  1235.             return true;
  1236.         }
  1237.         if (FileMode.TREE.equals(iMode)
  1238.                 && (iId == null || ObjectId.zeroId().equals(iId))) {
  1239.             return isModifiedSubtree_IndexTree(path, rootTree);
  1240.         }
  1241.         return !equalIdAndMode(iId, iMode, tId, tMode);
  1242.     }

  1243.     /**
  1244.      * Checks whether the subtree starting at a given path differs between Index and
  1245.      * some tree.
  1246.      *
  1247.      * @param path
  1248.      * @param tree
  1249.      *            the tree to compare
  1250.      * @return true if the subtrees differ
  1251.      * @throws CorruptObjectException
  1252.      * @throws IOException
  1253.      */
  1254.     private boolean isModifiedSubtree_IndexTree(String path, ObjectId tree)
  1255.             throws CorruptObjectException, IOException {
  1256.         try (NameConflictTreeWalk tw = new NameConflictTreeWalk(repo)) {
  1257.             tw.addTree(new DirCacheIterator(dc));
  1258.             tw.addTree(tree);
  1259.             tw.setRecursive(true);
  1260.             tw.setFilter(PathFilter.create(path));
  1261.             while (tw.next()) {
  1262.                 AbstractTreeIterator dcIt = tw.getTree(0,
  1263.                         DirCacheIterator.class);
  1264.                 AbstractTreeIterator treeIt = tw.getTree(1,
  1265.                         AbstractTreeIterator.class);
  1266.                 if (dcIt == null || treeIt == null)
  1267.                     return true;
  1268.                 if (dcIt.getEntryRawMode() != treeIt.getEntryRawMode())
  1269.                     return true;
  1270.                 if (!dcIt.getEntryObjectId().equals(treeIt.getEntryObjectId()))
  1271.                     return true;
  1272.             }
  1273.             return false;
  1274.         }
  1275.     }

  1276.     /**
  1277.      * Updates the file in the working tree with content and mode from an entry
  1278.      * in the index. The new content is first written to a new temporary file in
  1279.      * the same directory as the real file. Then that new file is renamed to the
  1280.      * final filename.
  1281.      *
  1282.      * <p>
  1283.      * <b>Note:</b> if the entry path on local file system exists as a non-empty
  1284.      * directory, and the target entry type is a link or file, the checkout will
  1285.      * fail with {@link java.io.IOException} since existing non-empty directory
  1286.      * cannot be renamed to file or link without deleting it recursively.
  1287.      * </p>
  1288.      *
  1289.      * <p>
  1290.      * TODO: this method works directly on File IO, we may need another
  1291.      * abstraction (like WorkingTreeIterator). This way we could tell e.g.
  1292.      * Eclipse that Files in the workspace got changed
  1293.      * </p>
  1294.      *
  1295.      * @param repo
  1296.      *            repository managing the destination work tree.
  1297.      * @param entry
  1298.      *            the entry containing new mode and content
  1299.      * @param or
  1300.      *            object reader to use for checkout
  1301.      * @throws java.io.IOException
  1302.      * @since 3.6
  1303.      * @deprecated since 5.1, use
  1304.      *             {@link #checkoutEntry(Repository, DirCacheEntry, ObjectReader, boolean, CheckoutMetadata)}
  1305.      *             instead
  1306.      */
  1307.     @Deprecated
  1308.     public static void checkoutEntry(Repository repo, DirCacheEntry entry,
  1309.             ObjectReader or) throws IOException {
  1310.         checkoutEntry(repo, entry, or, false, null);
  1311.     }

  1312.     /**
  1313.      * Updates the file in the working tree with content and mode from an entry
  1314.      * in the index. The new content is first written to a new temporary file in
  1315.      * the same directory as the real file. Then that new file is renamed to the
  1316.      * final filename.
  1317.      *
  1318.      * <p>
  1319.      * <b>Note:</b> if the entry path on local file system exists as a file, it
  1320.      * will be deleted and if it exists as a directory, it will be deleted
  1321.      * recursively, independently if has any content.
  1322.      * </p>
  1323.      *
  1324.      * <p>
  1325.      * TODO: this method works directly on File IO, we may need another
  1326.      * abstraction (like WorkingTreeIterator). This way we could tell e.g.
  1327.      * Eclipse that Files in the workspace got changed
  1328.      * </p>
  1329.      *
  1330.      * @param repo
  1331.      *            repository managing the destination work tree.
  1332.      * @param entry
  1333.      *            the entry containing new mode and content
  1334.      * @param or
  1335.      *            object reader to use for checkout
  1336.      * @param deleteRecursive
  1337.      *            true to recursively delete final path if it exists on the file
  1338.      *            system
  1339.      * @param checkoutMetadata
  1340.      *            containing
  1341.      *            <ul>
  1342.      *            <li>smudgeFilterCommand to be run for smudging the entry to be
  1343.      *            checked out</li>
  1344.      *            <li>eolStreamType used for stream conversion</li>
  1345.      *            </ul>
  1346.      * @throws java.io.IOException
  1347.      * @since 4.2
  1348.      */
  1349.     public static void checkoutEntry(Repository repo, DirCacheEntry entry,
  1350.             ObjectReader or, boolean deleteRecursive,
  1351.             CheckoutMetadata checkoutMetadata) throws IOException {
  1352.         if (checkoutMetadata == null)
  1353.             checkoutMetadata = CheckoutMetadata.EMPTY;
  1354.         ObjectLoader ol = or.open(entry.getObjectId());
  1355.         File f = new File(repo.getWorkTree(), entry.getPathString());
  1356.         File parentDir = f.getParentFile();
  1357.         if (parentDir.isFile()) {
  1358.             FileUtils.delete(parentDir);
  1359.         }
  1360.         FileUtils.mkdirs(parentDir, true);
  1361.         FS fs = repo.getFS();
  1362.         WorkingTreeOptions opt = repo.getConfig().get(WorkingTreeOptions.KEY);
  1363.         if (entry.getFileMode() == FileMode.SYMLINK
  1364.                 && opt.getSymLinks() == SymLinks.TRUE) {
  1365.             byte[] bytes = ol.getBytes();
  1366.             String target = RawParseUtils.decode(bytes);
  1367.             if (deleteRecursive && f.isDirectory()) {
  1368.                 FileUtils.delete(f, FileUtils.RECURSIVE);
  1369.             }
  1370.             fs.createSymLink(f, target);
  1371.             entry.setLength(bytes.length);
  1372.             entry.setLastModified(fs.lastModifiedInstant(f));
  1373.             return;
  1374.         }

  1375.         String name = f.getName();
  1376.         if (name.length() > 200) {
  1377.             name = name.substring(0, 200);
  1378.         }
  1379.         File tmpFile = File.createTempFile(
  1380.                 "._" + name, null, parentDir); //$NON-NLS-1$

  1381.         getContent(repo, entry.getPathString(), checkoutMetadata, ol, opt,
  1382.                 new FileOutputStream(tmpFile));

  1383.         // The entry needs to correspond to the on-disk filesize. If the content
  1384.         // was filtered (either by autocrlf handling or smudge filters) ask the
  1385.         // filesystem again for the length. Otherwise the objectloader knows the
  1386.         // size
  1387.         if (checkoutMetadata.eolStreamType == EolStreamType.DIRECT
  1388.                 && checkoutMetadata.smudgeFilterCommand == null) {
  1389.             entry.setLength(ol.getSize());
  1390.         } else {
  1391.             entry.setLength(tmpFile.length());
  1392.         }

  1393.         if (opt.isFileMode() && fs.supportsExecute()) {
  1394.             if (FileMode.EXECUTABLE_FILE.equals(entry.getRawMode())) {
  1395.                 if (!fs.canExecute(tmpFile))
  1396.                     fs.setExecute(tmpFile, true);
  1397.             } else {
  1398.                 if (fs.canExecute(tmpFile))
  1399.                     fs.setExecute(tmpFile, false);
  1400.             }
  1401.         }
  1402.         try {
  1403.             if (deleteRecursive && f.isDirectory()) {
  1404.                 FileUtils.delete(f, FileUtils.RECURSIVE);
  1405.             }
  1406.             FileUtils.rename(tmpFile, f, StandardCopyOption.ATOMIC_MOVE);
  1407.         } catch (IOException e) {
  1408.             throw new IOException(
  1409.                     MessageFormat.format(JGitText.get().renameFileFailed,
  1410.                             tmpFile.getPath(), f.getPath()),
  1411.                     e);
  1412.         } finally {
  1413.             if (tmpFile.exists()) {
  1414.                 FileUtils.delete(tmpFile);
  1415.             }
  1416.         }
  1417.         entry.setLastModified(fs.lastModifiedInstant(f));
  1418.     }

  1419.     /**
  1420.      * Return filtered content for a specific object (blob). EOL handling and
  1421.      * smudge-filter handling are applied in the same way as it would be done
  1422.      * during a checkout.
  1423.      *
  1424.      * @param repo
  1425.      *            the repository
  1426.      * @param path
  1427.      *            the path used to determine the correct filters for the object
  1428.      * @param checkoutMetadata
  1429.      *            containing
  1430.      *            <ul>
  1431.      *            <li>smudgeFilterCommand to be run for smudging the object</li>
  1432.      *            <li>eolStreamType used for stream conversion (can be
  1433.      *            null)</li>
  1434.      *            </ul>
  1435.      * @param ol
  1436.      *            the object loader to read raw content of the object
  1437.      * @param opt
  1438.      *            the working tree options where only 'core.autocrlf' is used
  1439.      *            for EOL handling if 'checkoutMetadata.eolStreamType' is not
  1440.      *            valid
  1441.      * @param os
  1442.      *            the output stream the filtered content is written to. The
  1443.      *            caller is responsible to close the stream.
  1444.      * @throws IOException
  1445.      *
  1446.      * @since 5.7
  1447.      */
  1448.     public static void getContent(Repository repo, String path,
  1449.             CheckoutMetadata checkoutMetadata, ObjectLoader ol,
  1450.             WorkingTreeOptions opt, OutputStream os)
  1451.             throws IOException {
  1452.         EolStreamType nonNullEolStreamType;
  1453.         if (checkoutMetadata.eolStreamType != null) {
  1454.             nonNullEolStreamType = checkoutMetadata.eolStreamType;
  1455.         } else if (opt.getAutoCRLF() == AutoCRLF.TRUE) {
  1456.             nonNullEolStreamType = EolStreamType.AUTO_CRLF;
  1457.         } else {
  1458.             nonNullEolStreamType = EolStreamType.DIRECT;
  1459.         }
  1460.         try (OutputStream channel = EolStreamTypeUtil.wrapOutputStream(
  1461.                 os, nonNullEolStreamType)) {
  1462.             if (checkoutMetadata.smudgeFilterCommand != null) {
  1463.                 if (FilterCommandRegistry
  1464.                         .isRegistered(checkoutMetadata.smudgeFilterCommand)) {
  1465.                     runBuiltinFilterCommand(repo, checkoutMetadata, ol,
  1466.                             channel);
  1467.                 } else {
  1468.                     runExternalFilterCommand(repo, path, checkoutMetadata, ol,
  1469.                             channel);
  1470.                 }
  1471.             } else {
  1472.                 ol.copyTo(channel);
  1473.             }
  1474.         }
  1475.     }

  1476.     // Run an external filter command
  1477.     private static void runExternalFilterCommand(Repository repo, String path,
  1478.             CheckoutMetadata checkoutMetadata, ObjectLoader ol,
  1479.             OutputStream channel) throws IOException {
  1480.         FS fs = repo.getFS();
  1481.         ProcessBuilder filterProcessBuilder = fs.runInShell(
  1482.                 checkoutMetadata.smudgeFilterCommand, new String[0]);
  1483.         filterProcessBuilder.directory(repo.getWorkTree());
  1484.         filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY,
  1485.                 repo.getDirectory().getAbsolutePath());
  1486.         ExecutionResult result;
  1487.         int rc;
  1488.         try {
  1489.             // TODO: wire correctly with AUTOCRLF
  1490.             result = fs.execute(filterProcessBuilder, ol.openStream());
  1491.             rc = result.getRc();
  1492.             if (rc == 0) {
  1493.                 result.getStdout().writeTo(channel,
  1494.                         NullProgressMonitor.INSTANCE);
  1495.             }
  1496.         } catch (IOException | InterruptedException e) {
  1497.             throw new IOException(new FilterFailedException(e,
  1498.                     checkoutMetadata.smudgeFilterCommand,
  1499.                     path));
  1500.         }
  1501.         if (rc != 0) {
  1502.             throw new IOException(new FilterFailedException(rc,
  1503.                     checkoutMetadata.smudgeFilterCommand,
  1504.                     path,
  1505.                     result.getStdout().toByteArray(MAX_EXCEPTION_TEXT_SIZE),
  1506.                     RawParseUtils.decode(result.getStderr()
  1507.                             .toByteArray(MAX_EXCEPTION_TEXT_SIZE))));
  1508.         }
  1509.     }

  1510.     // Run a builtin filter command
  1511.     private static void runBuiltinFilterCommand(Repository repo,
  1512.             CheckoutMetadata checkoutMetadata, ObjectLoader ol,
  1513.             OutputStream channel) throws MissingObjectException, IOException {
  1514.         boolean isMandatory = repo.getConfig().getBoolean(
  1515.                 ConfigConstants.CONFIG_FILTER_SECTION,
  1516.                 ConfigConstants.CONFIG_SECTION_LFS,
  1517.                 ConfigConstants.CONFIG_KEY_REQUIRED, false);
  1518.         FilterCommand command = null;
  1519.         try {
  1520.             command = FilterCommandRegistry.createFilterCommand(
  1521.                     checkoutMetadata.smudgeFilterCommand, repo, ol.openStream(),
  1522.                     channel);
  1523.         } catch (IOException e) {
  1524.             LOG.error(JGitText.get().failedToDetermineFilterDefinition, e);
  1525.             if (!isMandatory) {
  1526.                 // In case an IOException occurred during creating of the
  1527.                 // command then proceed as if there would not have been a
  1528.                 // builtin filter (only if the filter is not mandatory).
  1529.                 ol.copyTo(channel);
  1530.             } else {
  1531.                 throw e;
  1532.             }
  1533.         }
  1534.         if (command != null) {
  1535.             while (command.run() != -1) {
  1536.                 // loop as long as command.run() tells there is work to do
  1537.             }
  1538.         }
  1539.     }

  1540.     @SuppressWarnings("deprecation")
  1541.     private static void checkValidPath(CanonicalTreeParser t)
  1542.             throws InvalidPathException {
  1543.         ObjectChecker chk = new ObjectChecker()
  1544.             .setSafeForWindows(SystemReader.getInstance().isWindows())
  1545.             .setSafeForMacOS(SystemReader.getInstance().isMacOS());
  1546.         for (CanonicalTreeParser i = t; i != null; i = i.getParent())
  1547.             checkValidPathSegment(chk, i);
  1548.     }

  1549.     private static void checkValidPathSegment(ObjectChecker chk,
  1550.             CanonicalTreeParser t) throws InvalidPathException {
  1551.         try {
  1552.             int ptr = t.getNameOffset();
  1553.             int end = ptr + t.getNameLength();
  1554.             chk.checkPathSegment(t.getEntryPathBuffer(), ptr, end);
  1555.         } catch (CorruptObjectException err) {
  1556.             String path = t.getEntryPathString();
  1557.             InvalidPathException i = new InvalidPathException(path);
  1558.             i.initCause(err);
  1559.             throw i;
  1560.         }
  1561.     }
  1562. }