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.  * Copyright (C) 2017, 2022, Thomas Wolf <thomas.wolf@paranor.ch> and others
  9.  *
  10.  * This program and the accompanying materials are made available under the
  11.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  12.  * https://www.eclipse.org/org/documents/edl-v10.php.
  13.  *
  14.  * SPDX-License-Identifier: BSD-3-Clause
  15.  */

  16. package org.eclipse.jgit.dircache;

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

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

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

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

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

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

  90.         /** filter command to apply */
  91.         public final String smudgeFilterCommand;

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

  101.         static CheckoutMetadata EMPTY = new CheckoutMetadata(
  102.                 EolStreamType.DIRECT, null);
  103.     }

  104.     private Repository repo;

  105.     private Map<String, CheckoutMetadata> updated = new LinkedHashMap<>();

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

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

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

  109.     private ObjectId mergeCommitTree;

  110.     private DirCache dc;

  111.     private DirCacheBuilder builder;

  112.     private NameConflictTreeWalk walk;

  113.     private ObjectId headCommitTree;

  114.     private WorkingTreeIterator workingTree;

  115.     private boolean failOnConflict = true;

  116.     private boolean force = false;

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

  118.     private boolean initialCheckout;

  119.     private boolean performingCheckout;

  120.     private WorkingTreeOptions options;

  121.     private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;

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

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

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

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

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

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

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

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

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

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

  267.         walk.setHead(addTree(walk, headCommitTree));
  268.         addTree(walk, mergeCommitTree);
  269.         int dciPos = walk.addTree(new DirCacheBuildIterator(builder));
  270.         walk.addTree(workingTree);
  271.         workingTree.setDirCacheIterator(walk, dciPos);

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

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

  296.         builder = dc.builder();

  297.         walk = new NameConflictTreeWalk(repo);
  298.         walk.setHead(addTree(walk, mergeCommitTree));
  299.         int dciPos = walk.addTree(new DirCacheBuildIterator(builder));
  300.         walk.addTree(workingTree);
  301.         workingTree.setDirCacheIterator(walk, dciPos);

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

  311.     private int addTree(TreeWalk tw, ObjectId id) throws MissingObjectException,
  312.             IncorrectObjectTypeException, IOException {
  313.         if (id == null) {
  314.             return tw.addTree(new EmptyTreeIterator());
  315.         }
  316.         return tw.addTree(id);
  317.     }

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

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

  446.     private boolean doCheckout() throws CorruptObjectException, IOException,
  447.             MissingObjectException, IncorrectObjectTypeException,
  448.             CheckoutConflictException, IndexWriteException, CanceledException {
  449.         toBeDeleted.clear();
  450.         options = repo.getConfig()
  451.                 .get(WorkingTreeOptions.KEY);
  452.         try (ObjectReader objectReader = repo.getObjectDatabase().newReader()) {
  453.             if (headCommitTree != null)
  454.                 preScanTwoTrees();
  455.             else
  456.                 prescanOneTree();

  457.             if (!conflicts.isEmpty()) {
  458.                 if (failOnConflict) {
  459.                     throw new CheckoutConflictException(conflicts.toArray(new String[0]));
  460.                 }
  461.                 cleanUpConflicts();
  462.             }

  463.             // update our index
  464.             builder.finish();

  465.             // init progress reporting
  466.             int numTotal = removed.size() + updated.size() + conflicts.size();
  467.             monitor.beginTask(JGitText.get().checkingOutFiles, numTotal);

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

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

  559.                 monitor.update(1);
  560.                 if (monitor.isCancelled()) {
  561.                     throw new CanceledException(MessageFormat.format(
  562.                             JGitText.get().operationCanceled,
  563.                             JGitText.get().checkingOutFiles));
  564.                 }
  565.             }
  566.             monitor.endTask();

  567.             // commit the index builder - a new index is persisted
  568.             if (!builder.commit())
  569.                 throw new IndexWriteException();
  570.         }
  571.         return toBeDeleted.isEmpty();
  572.     }

  573.     private void checkoutGitlink(String path, DirCacheEntry entry)
  574.             throws IOException {
  575.         File gitlinkDir = new File(repo.getWorkTree(), path);
  576.         FileUtils.mkdirs(gitlinkDir, true);
  577.         FS fs = repo.getFS();
  578.         entry.setLastModified(fs.lastModifiedInstant(gitlinkDir));
  579.     }

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

  609.     private static boolean isSamePrefix(String a, String b) {
  610.         int as = a.lastIndexOf('/');
  611.         int bs = b.lastIndexOf('/');
  612.         return a.substring(0, as + 1).equals(b.substring(0, bs + 1));
  613.     }

  614.      private void removeEmptyParents(File f) {
  615.         File parentFile = f.getParentFile();

  616.         while (parentFile != null && !parentFile.equals(repo.getWorkTree())) {
  617.             if (!parentFile.delete())
  618.                 break;
  619.             parentFile = parentFile.getParentFile();
  620.         }
  621.     }

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

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

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

  657.         String name = walk.getPathString();

  658.         if (m != null)
  659.             checkValidPath(m);

  660.         if (i == null && m == null && h == null) {
  661.             // File/Directory conflict case #20
  662.             if (walk.isDirectoryFileConflict())
  663.                 // TODO: check whether it is always correct to report a conflict here
  664.                 conflict(name, null, null, null);

  665.             // file only exists in working tree -> ignore it
  666.             return;
  667.         }

  668.         ObjectId iId = (i == null ? null : i.getEntryObjectId());
  669.         ObjectId mId = (m == null ? null : m.getEntryObjectId());
  670.         ObjectId hId = (h == null ? null : h.getEntryObjectId());
  671.         FileMode iMode = (i == null ? null : i.getEntryFileMode());
  672.         FileMode mMode = (m == null ? null : m.getEntryFileMode());
  673.         FileMode hMode = (h == null ? null : h.getEntryFileMode());

  674.         /**
  675.          * <pre>
  676.          *  File/Directory conflicts:
  677.          *  the following table from ReadTreeTest tells what to do in case of directory/file
  678.          *  conflicts. I give comments here
  679.          *
  680.          *      H        I       M     Clean     H==M     H==I    I==M         Result
  681.          *      ------------------------------------------------------------------
  682.          * 1    D        D       F       Y         N       Y       N           Update
  683.          * 2    D        D       F       N         N       Y       N           Conflict
  684.          * 3    D        F       D                 Y       N       N           Keep
  685.          * 4    D        F       D                 N       N       N           Conflict
  686.          * 5    D        F       F       Y         N       N       Y           Keep
  687.          * 5b   D        F       F       Y         N       N       N           Conflict
  688.          * 6    D        F       F       N         N       N       Y           Keep
  689.          * 6b   D        F       F       N         N       N       N           Conflict
  690.          * 7    F        D       F       Y         Y       N       N           Update
  691.          * 8    F        D       F       N         Y       N       N           Conflict
  692.          * 9    F        D       F                 N       N       N           Conflict
  693.          * 10   F        D       D                 N       N       Y           Keep
  694.          * 11   F        D       D                 N       N       N           Conflict
  695.          * 12   F        F       D       Y         N       Y       N           Update
  696.          * 13   F        F       D       N         N       Y       N           Conflict
  697.          * 14   F        F       D                 N       N       N           Conflict
  698.          * 15   0        F       D                 N       N       N           Conflict
  699.          * 16   0        D       F       Y         N       N       N           Update
  700.          * 17   0        D       F                 N       N       N           Conflict
  701.          * 18   F        0       D                                             Update
  702.          * 19   D        0       F                                             Update
  703.          * 20   0        0       F       N (worktree=dir)                      Conflict
  704.          * </pre>
  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.         int ffMask = 0;
  720.         if (h != null)
  721.             ffMask = FileMode.TREE.equals(hMode) ? 0xD00 : 0xF00;
  722.         if (i != null)
  723.             ffMask |= FileMode.TREE.equals(iMode) ? 0x0D0 : 0x0F0;
  724.         if (m != null)
  725.             ffMask |= FileMode.TREE.equals(mMode) ? 0x00D : 0x00F;

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

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

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

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

  820.         if ((ffMask == 0x00F) && f != null && FileMode.TREE.equals(f.getEntryFileMode())) {
  821.             // File/Directory conflict case #20
  822.             conflict(name, null, h, m);
  823.             return;
  824.         }

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

  842.             /**
  843.              * <pre>
  844.              *            I (index)     H        M     H==M  Result
  845.              *          -------------------------------------------
  846.              *          0 nothing    nothing  nothing        (does not happen)
  847.              *          1 nothing    nothing  exists         use M
  848.              *          2 nothing    exists   nothing        remove path from index
  849.              *          3 nothing    exists   exists   yes   keep index if not in initial checkout
  850.              *                                               , otherwise use M
  851.              *            nothing    exists   exists   no    fail
  852.              * </pre>
  853.              */

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

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

  956.                 /**
  957.                  * <pre>
  958.                  *             clean I==H  I==M       H        M        Result
  959.                  *           -----------------------------------------------------
  960.                  *          10 yes   yes   N/A     exists   nothing  remove path from index
  961.                  *          11 no    yes   N/A     exists   nothing  keep file
  962.                  *          12 yes   no    N/A     exists   nothing  fail
  963.                  *          13 no    no    N/A     exists   nothing  fail
  964.                  * </pre>
  965.                  */

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

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

  1039.                 if (!isModified_IndexTree(name, iId, iMode, hId, hMode,
  1040.                         headCommitTree)
  1041.                         && isModified_IndexTree(name, iId, iMode, mId, mMode,
  1042.                                 mergeCommitTree)) {
  1043.                         // Head contains the same as Index. Merge differs
  1044.                         // Something in Merge

  1045.                     // For submodules just update the index with the new SHA-1
  1046.                     if (dce != null
  1047.                             && FileMode.GITLINK.equals(dce.getFileMode())) {
  1048.                         // Index and Head contain the same submodule. Merge
  1049.                         // differs
  1050.                         // Something in Merge
  1051.                         // -> Nothing new in index. Move to merge.
  1052.                         // Potentially updates the file

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

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

  1087.                     keep(name, dce, f);
  1088.                 }
  1089.             }
  1090.         }
  1091.     }

  1092.     private static boolean idEqual(AbstractTreeIterator a,
  1093.             AbstractTreeIterator b) {
  1094.         if (a == b) {
  1095.             return true;
  1096.         }
  1097.         if (a == null || b == null) {
  1098.             return false;
  1099.         }
  1100.         return a.getEntryObjectId().equals(b.getEntryObjectId());
  1101.     }

  1102.     /**
  1103.      * A conflict is detected - add the three different stages to the index
  1104.      * @param path the path of the conflicting entry
  1105.      * @param e the previous index entry
  1106.      * @param h the first tree you want to merge (the HEAD)
  1107.      * @param m the second tree you want to merge
  1108.      */
  1109.     private void conflict(String path, DirCacheEntry e, AbstractTreeIterator h, AbstractTreeIterator m) {
  1110.         conflicts.add(path);

  1111.         DirCacheEntry entry;
  1112.         if (e != null) {
  1113.             entry = new DirCacheEntry(e.getPathString(), DirCacheEntry.STAGE_1);
  1114.             entry.copyMetaData(e, true);
  1115.             builder.add(entry);
  1116.         }

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

  1123.         if (m != null && !FileMode.TREE.equals(m.getEntryFileMode())) {
  1124.             entry = new DirCacheEntry(m.getEntryPathString(), DirCacheEntry.STAGE_3);
  1125.             entry.setFileMode(m.getEntryFileMode());
  1126.             entry.setObjectId(m.getEntryObjectId());
  1127.             builder.add(entry);
  1128.         }
  1129.     }

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

  1148.     private void remove(String path) {
  1149.         removed.add(path);
  1150.     }

  1151.     private void update(CanonicalTreeParser tree) throws IOException {
  1152.         update(0, tree.getEntryPathString(), tree.getEntryObjectId(),
  1153.                 tree.getEntryFileMode());
  1154.     }

  1155.     private void update(int index, String path, ObjectId mId,
  1156.             FileMode mode) throws IOException {
  1157.         if (!FileMode.TREE.equals(mode)) {
  1158.             updated.put(path, new CheckoutMetadata(
  1159.                     walk.getCheckoutEolStreamType(index),
  1160.                     walk.getSmudgeCommand(index)));

  1161.             DirCacheEntry entry = new DirCacheEntry(path, DirCacheEntry.STAGE_0);
  1162.             entry.setObjectId(mId);
  1163.             entry.setFileMode(mode);
  1164.             builder.add(entry);
  1165.         }
  1166.     }

  1167.     /**
  1168.      * If <code>true</code>, will scan first to see if it's possible to check
  1169.      * out, otherwise throw
  1170.      * {@link org.eclipse.jgit.errors.CheckoutConflictException}. If
  1171.      * <code>false</code>, it will silently deal with the problem.
  1172.      *
  1173.      * @param failOnConflict
  1174.      *            a boolean.
  1175.      */
  1176.     public void setFailOnConflict(boolean failOnConflict) {
  1177.         this.failOnConflict = failOnConflict;
  1178.     }

  1179.     /**
  1180.      * If <code>true</code>, dirty worktree files may be overridden. If
  1181.      * <code>false</code> dirty worktree files will not be overridden in order
  1182.      * not to delete unsaved content. This corresponds to native git's 'git
  1183.      * checkout -f' option. By default this option is set to false.
  1184.      *
  1185.      * @param force
  1186.      *            a boolean.
  1187.      * @since 5.3
  1188.      */
  1189.     public void setForce(boolean force) {
  1190.         this.force = force;
  1191.     }

  1192.     /**
  1193.      * This method implements how to handle conflicts when
  1194.      * {@link #failOnConflict} is false
  1195.      *
  1196.      * @throws CheckoutConflictException
  1197.      */
  1198.     private void cleanUpConflicts() throws CheckoutConflictException {
  1199.         // TODO: couldn't we delete unsaved worktree content here?
  1200.         for (String c : conflicts) {
  1201.             File conflict = new File(repo.getWorkTree(), c);
  1202.             if (!conflict.delete())
  1203.                 throw new CheckoutConflictException(MessageFormat.format(
  1204.                         JGitText.get().cannotDeleteFile, c));
  1205.             removeEmptyParents(conflict);
  1206.         }
  1207.     }

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

  1241.     private boolean isModified_IndexTree(String path, ObjectId iId,
  1242.             FileMode iMode, ObjectId tId, FileMode tMode, ObjectId rootTree)
  1243.             throws CorruptObjectException, IOException {
  1244.         if (iMode != tMode) {
  1245.             return true;
  1246.         }
  1247.         if (FileMode.TREE.equals(iMode)
  1248.                 && (iId == null || ObjectId.zeroId().equals(iId))) {
  1249.             return isModifiedSubtree_IndexTree(path, rootTree);
  1250.         }
  1251.         return !equalIdAndMode(iId, iMode, tId, tMode);
  1252.     }

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

  1286.     /**
  1287.      * Updates the file in the working tree with content and mode from an entry
  1288.      * in the index. The new content is first written to a new temporary file in
  1289.      * the same directory as the real file. Then that new file is renamed to the
  1290.      * final filename.
  1291.      *
  1292.      * <p>
  1293.      * <b>Note:</b> if the entry path on local file system exists as a non-empty
  1294.      * directory, and the target entry type is a link or file, the checkout will
  1295.      * fail with {@link java.io.IOException} since existing non-empty directory
  1296.      * cannot be renamed to file or link without deleting it recursively.
  1297.      * </p>
  1298.      *
  1299.      * @param repo
  1300.      *            repository managing the destination work tree.
  1301.      * @param entry
  1302.      *            the entry containing new mode and content
  1303.      * @param or
  1304.      *            object reader to use for checkout
  1305.      * @throws java.io.IOException
  1306.      * @since 3.6
  1307.      * @deprecated since 5.1, use
  1308.      *             {@link #checkoutEntry(Repository, DirCacheEntry, ObjectReader, boolean, CheckoutMetadata, WorkingTreeOptions)}
  1309.      *             instead
  1310.      */
  1311.     @Deprecated
  1312.     public static void checkoutEntry(Repository repo, DirCacheEntry entry,
  1313.             ObjectReader or) throws IOException {
  1314.         checkoutEntry(repo, entry, or, false, null, null);
  1315.     }


  1316.     /**
  1317.      * Updates the file in the working tree with content and mode from an entry
  1318.      * in the index. The new content is first written to a new temporary file in
  1319.      * the same directory as the real file. Then that new file is renamed to the
  1320.      * final filename.
  1321.      *
  1322.      * <p>
  1323.      * <b>Note:</b> if the entry path on local file system exists as a file, it
  1324.      * will be deleted and if it exists as a directory, it will be deleted
  1325.      * recursively, independently if has any content.
  1326.      * </p>
  1327.      *
  1328.      * @param repo
  1329.      *            repository managing the destination work tree.
  1330.      * @param entry
  1331.      *            the entry containing new mode and content
  1332.      * @param or
  1333.      *            object reader to use for checkout
  1334.      * @param deleteRecursive
  1335.      *            true to recursively delete final path if it exists on the file
  1336.      *            system
  1337.      * @param checkoutMetadata
  1338.      *            containing
  1339.      *            <ul>
  1340.      *            <li>smudgeFilterCommand to be run for smudging the entry to be
  1341.      *            checked out</li>
  1342.      *            <li>eolStreamType used for stream conversion</li>
  1343.      *            </ul>
  1344.      * @throws java.io.IOException
  1345.      * @since 4.2
  1346.      * @deprecated since 6.3, use
  1347.      *             {@link #checkoutEntry(Repository, DirCacheEntry, ObjectReader, boolean, CheckoutMetadata, WorkingTreeOptions)}
  1348.      *             instead
  1349.      */
  1350.     @Deprecated
  1351.     public static void checkoutEntry(Repository repo, DirCacheEntry entry,
  1352.             ObjectReader or, boolean deleteRecursive,
  1353.             CheckoutMetadata checkoutMetadata) throws IOException {
  1354.         checkoutEntry(repo, entry, or, deleteRecursive, checkoutMetadata, null);
  1355.     }

  1356.     /**
  1357.      * Updates the file in the working tree with content and mode from an entry
  1358.      * in the index. The new content is first written to a new temporary file in
  1359.      * the same directory as the real file. Then that new file is renamed to the
  1360.      * final filename.
  1361.      *
  1362.      * <p>
  1363.      * <b>Note:</b> if the entry path on local file system exists as a file, it
  1364.      * will be deleted and if it exists as a directory, it will be deleted
  1365.      * recursively, independently if has any content.
  1366.      * </p>
  1367.      *
  1368.      * @param repo
  1369.      *            repository managing the destination work tree.
  1370.      * @param entry
  1371.      *            the entry containing new mode and content
  1372.      * @param or
  1373.      *            object reader to use for checkout
  1374.      * @param deleteRecursive
  1375.      *            true to recursively delete final path if it exists on the file
  1376.      *            system
  1377.      * @param checkoutMetadata
  1378.      *            containing
  1379.      *            <ul>
  1380.      *            <li>smudgeFilterCommand to be run for smudging the entry to be
  1381.      *            checked out</li>
  1382.      *            <li>eolStreamType used for stream conversion</li>
  1383.      *            </ul>
  1384.      * @param options
  1385.      *            {@link WorkingTreeOptions} that are effective; if {@code null}
  1386.      *            they are loaded from the repository config
  1387.      * @throws java.io.IOException
  1388.      * @since 6.3
  1389.      */
  1390.     public static void checkoutEntry(Repository repo, DirCacheEntry entry,
  1391.             ObjectReader or, boolean deleteRecursive,
  1392.             CheckoutMetadata checkoutMetadata, WorkingTreeOptions options)
  1393.             throws IOException {
  1394.         if (checkoutMetadata == null) {
  1395.             checkoutMetadata = CheckoutMetadata.EMPTY;
  1396.         }
  1397.         ObjectLoader ol = or.open(entry.getObjectId());
  1398.         File f = new File(repo.getWorkTree(), entry.getPathString());
  1399.         File parentDir = f.getParentFile();
  1400.         if (parentDir.isFile()) {
  1401.             FileUtils.delete(parentDir);
  1402.         }
  1403.         FileUtils.mkdirs(parentDir, true);
  1404.         FS fs = repo.getFS();
  1405.         WorkingTreeOptions opt = options != null ? options
  1406.                 : repo.getConfig().get(WorkingTreeOptions.KEY);
  1407.         if (entry.getFileMode() == FileMode.SYMLINK
  1408.                 && opt.getSymLinks() == SymLinks.TRUE) {
  1409.             byte[] bytes = ol.getBytes();
  1410.             String target = RawParseUtils.decode(bytes);
  1411.             if (deleteRecursive && f.isDirectory()) {
  1412.                 FileUtils.delete(f, FileUtils.RECURSIVE);
  1413.             }
  1414.             fs.createSymLink(f, target);
  1415.             entry.setLength(bytes.length);
  1416.             entry.setLastModified(fs.lastModifiedInstant(f));
  1417.             return;
  1418.         }

  1419.         String name = f.getName();
  1420.         if (name.length() > 200) {
  1421.             name = name.substring(0, 200);
  1422.         }
  1423.         File tmpFile = File.createTempFile(
  1424.                 "._" + name, null, parentDir); //$NON-NLS-1$

  1425.         getContent(repo, entry.getPathString(), checkoutMetadata, ol, opt,
  1426.                 new FileOutputStream(tmpFile));

  1427.         // The entry needs to correspond to the on-disk filesize. If the content
  1428.         // was filtered (either by autocrlf handling or smudge filters) ask the
  1429.         // filesystem again for the length. Otherwise the objectloader knows the
  1430.         // size
  1431.         if (checkoutMetadata.eolStreamType == EolStreamType.DIRECT
  1432.                 && checkoutMetadata.smudgeFilterCommand == null) {
  1433.             entry.setLength(ol.getSize());
  1434.         } else {
  1435.             entry.setLength(tmpFile.length());
  1436.         }

  1437.         if (opt.isFileMode() && fs.supportsExecute()) {
  1438.             if (FileMode.EXECUTABLE_FILE.equals(entry.getRawMode())) {
  1439.                 if (!fs.canExecute(tmpFile))
  1440.                     fs.setExecute(tmpFile, true);
  1441.             } else {
  1442.                 if (fs.canExecute(tmpFile))
  1443.                     fs.setExecute(tmpFile, false);
  1444.             }
  1445.         }
  1446.         try {
  1447.             if (deleteRecursive && f.isDirectory()) {
  1448.                 FileUtils.delete(f, FileUtils.RECURSIVE);
  1449.             }
  1450.             FileUtils.rename(tmpFile, f, StandardCopyOption.ATOMIC_MOVE);
  1451.         } catch (IOException e) {
  1452.             throw new IOException(
  1453.                     MessageFormat.format(JGitText.get().renameFileFailed,
  1454.                             tmpFile.getPath(), f.getPath()),
  1455.                     e);
  1456.         } finally {
  1457.             if (tmpFile.exists()) {
  1458.                 FileUtils.delete(tmpFile);
  1459.             }
  1460.         }
  1461.         entry.setLastModified(fs.lastModifiedInstant(f));
  1462.     }

  1463.     /**
  1464.      * Return filtered content for a specific object (blob). EOL handling and
  1465.      * smudge-filter handling are applied in the same way as it would be done
  1466.      * during a checkout.
  1467.      *
  1468.      * @param repo
  1469.      *            the repository
  1470.      * @param path
  1471.      *            the path used to determine the correct filters for the object
  1472.      * @param checkoutMetadata
  1473.      *            containing
  1474.      *            <ul>
  1475.      *            <li>smudgeFilterCommand to be run for smudging the object</li>
  1476.      *            <li>eolStreamType used for stream conversion (can be
  1477.      *            null)</li>
  1478.      *            </ul>
  1479.      * @param ol
  1480.      *            the object loader to read raw content of the object
  1481.      * @param opt
  1482.      *            the working tree options where only 'core.autocrlf' is used
  1483.      *            for EOL handling if 'checkoutMetadata.eolStreamType' is not
  1484.      *            valid
  1485.      * @param os
  1486.      *            the output stream the filtered content is written to. The
  1487.      *            caller is responsible to close the stream.
  1488.      * @throws IOException
  1489.      *
  1490.      * @since 5.7
  1491.      */
  1492.     public static void getContent(Repository repo, String path,
  1493.             CheckoutMetadata checkoutMetadata, ObjectLoader ol,
  1494.             WorkingTreeOptions opt, OutputStream os)
  1495.             throws IOException {
  1496.         getContent(repo, path, checkoutMetadata, ol::openStream, opt, os);
  1497.     }


  1498.     /**
  1499.      * Something that can supply an {@link InputStream}.
  1500.      *
  1501.      * @since 6.3
  1502.      */
  1503.     public interface StreamSupplier {

  1504.         /**
  1505.          * Loads the input stream.
  1506.          *
  1507.          * @return the loaded stream
  1508.          * @throws IOException
  1509.          *             if any reading error occurs
  1510.          */
  1511.         InputStream load() throws IOException;
  1512.     }

  1513.     /**
  1514.      * Return filtered content for blob contents. EOL handling and smudge-filter
  1515.      * handling are applied in the same way as it would be done during a
  1516.      * checkout.
  1517.      *
  1518.      * @param repo
  1519.      *            the repository
  1520.      * @param path
  1521.      *            the path used to determine the correct filters for the object
  1522.      * @param checkoutMetadata
  1523.      *            containing
  1524.      *            <ul>
  1525.      *            <li>smudgeFilterCommand to be run for smudging the object</li>
  1526.      *            <li>eolStreamType used for stream conversion (can be
  1527.      *            null)</li>
  1528.      *            </ul>
  1529.      * @param inputStream
  1530.      *            A supplier for the raw content of the object. Each call should
  1531.      *            yield a fresh stream of the same object.
  1532.      * @param opt
  1533.      *            the working tree options where only 'core.autocrlf' is used
  1534.      *            for EOL handling if 'checkoutMetadata.eolStreamType' is not
  1535.      *            valid
  1536.      * @param os
  1537.      *            the output stream the filtered content is written to. The
  1538.      *            caller is responsible to close the stream.
  1539.      * @throws IOException
  1540.      * @since 6.3
  1541.      */
  1542.     public static void getContent(Repository repo, String path,
  1543.             CheckoutMetadata checkoutMetadata, StreamSupplier inputStream,
  1544.             WorkingTreeOptions opt, OutputStream os)
  1545.             throws IOException {
  1546.         EolStreamType nonNullEolStreamType;
  1547.         if (checkoutMetadata.eolStreamType != null) {
  1548.             nonNullEolStreamType = checkoutMetadata.eolStreamType;
  1549.         } else if (opt.getAutoCRLF() == AutoCRLF.TRUE) {
  1550.             nonNullEolStreamType = EolStreamType.AUTO_CRLF;
  1551.         } else {
  1552.             nonNullEolStreamType = EolStreamType.DIRECT;
  1553.         }
  1554.         try (OutputStream channel = EolStreamTypeUtil.wrapOutputStream(
  1555.                 os, nonNullEolStreamType)) {
  1556.             if (checkoutMetadata.smudgeFilterCommand != null) {
  1557.                 if (FilterCommandRegistry
  1558.                         .isRegistered(checkoutMetadata.smudgeFilterCommand)) {
  1559.                     runBuiltinFilterCommand(repo, checkoutMetadata, inputStream,
  1560.                             channel);
  1561.                 } else {
  1562.                     runExternalFilterCommand(repo, path, checkoutMetadata, inputStream,
  1563.                             channel);
  1564.                 }
  1565.             } else {
  1566.                 try (InputStream in = inputStream.load()) {
  1567.                     in.transferTo(channel);
  1568.                 }
  1569.             }
  1570.         }
  1571.     }

  1572.     // Run an external filter command
  1573.     private static void runExternalFilterCommand(Repository repo, String path,
  1574.             CheckoutMetadata checkoutMetadata, StreamSupplier inputStream,
  1575.             OutputStream channel) throws IOException {
  1576.         FS fs = repo.getFS();
  1577.         ProcessBuilder filterProcessBuilder = fs.runInShell(
  1578.                 checkoutMetadata.smudgeFilterCommand, new String[0]);
  1579.         filterProcessBuilder.directory(repo.getWorkTree());
  1580.         filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY,
  1581.                 repo.getDirectory().getAbsolutePath());
  1582.         ExecutionResult result;
  1583.         int rc;
  1584.         try {
  1585.             // TODO: wire correctly with AUTOCRLF
  1586.             try (InputStream in = inputStream.load()) {
  1587.                 result = fs.execute(filterProcessBuilder, in);
  1588.             }
  1589.             rc = result.getRc();
  1590.             if (rc == 0) {
  1591.                 result.getStdout().writeTo(channel,
  1592.                         NullProgressMonitor.INSTANCE);
  1593.             }
  1594.         } catch (IOException | InterruptedException e) {
  1595.             throw new IOException(new FilterFailedException(e,
  1596.                     checkoutMetadata.smudgeFilterCommand,
  1597.                     path));
  1598.         }
  1599.         if (rc != 0) {
  1600.             throw new IOException(new FilterFailedException(rc,
  1601.                     checkoutMetadata.smudgeFilterCommand, path,
  1602.                     result.getStdout().toByteArray(MAX_EXCEPTION_TEXT_SIZE),
  1603.                     result.getStderr().toString(MAX_EXCEPTION_TEXT_SIZE)));
  1604.         }
  1605.     }

  1606.     // Run a builtin filter command
  1607.     private static void runBuiltinFilterCommand(Repository repo,
  1608.             CheckoutMetadata checkoutMetadata, StreamSupplier inputStream,
  1609.             OutputStream channel) throws MissingObjectException, IOException {
  1610.         boolean isMandatory = repo.getConfig().getBoolean(
  1611.                 ConfigConstants.CONFIG_FILTER_SECTION,
  1612.                 ConfigConstants.CONFIG_SECTION_LFS,
  1613.                 ConfigConstants.CONFIG_KEY_REQUIRED, false);
  1614.         FilterCommand command = null;
  1615.         try (InputStream in = inputStream.load()) {
  1616.             try {
  1617.                 command = FilterCommandRegistry.createFilterCommand(
  1618.                         checkoutMetadata.smudgeFilterCommand, repo, in,
  1619.                         channel);
  1620.             } catch (IOException e) {
  1621.                 LOG.error(JGitText.get().failedToDetermineFilterDefinition, e);
  1622.                 if (!isMandatory) {
  1623.                     // In case an IOException occurred during creating of the
  1624.                     // command then proceed as if there would not have been a
  1625.                     // builtin filter (only if the filter is not mandatory).
  1626.                     try (InputStream again = inputStream.load()) {
  1627.                         again.transferTo(channel);
  1628.                     }
  1629.                 } else {
  1630.                     throw e;
  1631.                 }
  1632.             }
  1633.             if (command != null) {
  1634.                 while (command.run() != -1) {
  1635.                     // loop as long as command.run() tells there is work to do
  1636.                 }
  1637.             }
  1638.         }
  1639.     }

  1640.     @SuppressWarnings("deprecation")
  1641.     private static void checkValidPath(CanonicalTreeParser t)
  1642.             throws InvalidPathException {
  1643.         ObjectChecker chk = new ObjectChecker()
  1644.             .setSafeForWindows(SystemReader.getInstance().isWindows())
  1645.             .setSafeForMacOS(SystemReader.getInstance().isMacOS());
  1646.         for (CanonicalTreeParser i = t; i != null; i = i.getParent())
  1647.             checkValidPathSegment(chk, i);
  1648.     }

  1649.     private static void checkValidPathSegment(ObjectChecker chk,
  1650.             CanonicalTreeParser t) throws InvalidPathException {
  1651.         try {
  1652.             int ptr = t.getNameOffset();
  1653.             int end = ptr + t.getNameLength();
  1654.             chk.checkPathSegment(t.getEntryPathBuffer(), ptr, end);
  1655.         } catch (CorruptObjectException err) {
  1656.             String path = t.getEntryPathString();
  1657.             InvalidPathException i = new InvalidPathException(path);
  1658.             i.initCause(err);
  1659.             throw i;
  1660.         }
  1661.     }
  1662. }