ResolveMerger.java

  1. /*
  2.  * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>,
  3.  * Copyright (C) 2010-2012, Matthias Sohn <matthias.sohn@sap.com>
  4.  * Copyright (C) 2012, Research In Motion Limited
  5.  * Copyright (C) 2017, Obeo (mathieu.cartaud@obeo.fr)
  6.  * Copyright (C) 2018, 2022 Thomas Wolf <twolf@apache.org>
  7.  * Copyright (C) 2022, Google Inc. and others
  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.merge;

  16. import static java.nio.charset.StandardCharsets.UTF_8;
  17. import static java.time.Instant.EPOCH;
  18. import static org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm.HISTOGRAM;
  19. import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DIFF_SECTION;
  20. import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_ALGORITHM;
  21. import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;

  22. import java.io.Closeable;
  23. import java.io.File;
  24. import java.io.FileOutputStream;
  25. import java.io.IOException;
  26. import java.io.InputStream;
  27. import java.io.OutputStream;
  28. import java.time.Instant;
  29. import java.util.ArrayList;
  30. import java.util.Arrays;
  31. import java.util.Collections;
  32. import java.util.HashMap;
  33. import java.util.LinkedList;
  34. import java.util.List;
  35. import java.util.Map;
  36. import java.util.Objects;
  37. import java.util.TreeMap;

  38. import org.eclipse.jgit.annotations.NonNull;
  39. import org.eclipse.jgit.annotations.Nullable;
  40. import org.eclipse.jgit.attributes.Attribute;
  41. import org.eclipse.jgit.attributes.Attributes;
  42. import org.eclipse.jgit.diff.DiffAlgorithm;
  43. import org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm;
  44. import org.eclipse.jgit.diff.RawText;
  45. import org.eclipse.jgit.diff.RawTextComparator;
  46. import org.eclipse.jgit.diff.Sequence;
  47. import org.eclipse.jgit.dircache.DirCache;
  48. import org.eclipse.jgit.dircache.DirCacheBuildIterator;
  49. import org.eclipse.jgit.dircache.DirCacheBuilder;
  50. import org.eclipse.jgit.dircache.DirCacheCheckout;
  51. import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata;
  52. import org.eclipse.jgit.dircache.DirCacheCheckout.StreamSupplier;
  53. import org.eclipse.jgit.dircache.DirCacheEntry;
  54. import org.eclipse.jgit.errors.BinaryBlobException;
  55. import org.eclipse.jgit.errors.IndexWriteException;
  56. import org.eclipse.jgit.errors.NoWorkTreeException;
  57. import org.eclipse.jgit.internal.JGitText;
  58. import org.eclipse.jgit.lib.Config;
  59. import org.eclipse.jgit.lib.ConfigConstants;
  60. import org.eclipse.jgit.lib.Constants;
  61. import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
  62. import org.eclipse.jgit.lib.FileMode;
  63. import org.eclipse.jgit.lib.ObjectId;
  64. import org.eclipse.jgit.lib.ObjectInserter;
  65. import org.eclipse.jgit.lib.ObjectLoader;
  66. import org.eclipse.jgit.lib.ObjectReader;
  67. import org.eclipse.jgit.lib.Repository;
  68. import org.eclipse.jgit.revwalk.RevTree;
  69. import org.eclipse.jgit.storage.pack.PackConfig;
  70. import org.eclipse.jgit.submodule.SubmoduleConflict;
  71. import org.eclipse.jgit.treewalk.AbstractTreeIterator;
  72. import org.eclipse.jgit.treewalk.CanonicalTreeParser;
  73. import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
  74. import org.eclipse.jgit.treewalk.TreeWalk;
  75. import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
  76. import org.eclipse.jgit.treewalk.WorkingTreeIterator;
  77. import org.eclipse.jgit.treewalk.WorkingTreeOptions;
  78. import org.eclipse.jgit.treewalk.filter.TreeFilter;
  79. import org.eclipse.jgit.util.FS;
  80. import org.eclipse.jgit.util.LfsFactory;
  81. import org.eclipse.jgit.util.LfsFactory.LfsInputStream;
  82. import org.eclipse.jgit.util.TemporaryBuffer;
  83. import org.eclipse.jgit.util.io.EolStreamTypeUtil;

  84. /**
  85.  * A three-way merger performing a content-merge if necessary
  86.  */
  87. public class ResolveMerger extends ThreeWayMerger {

  88.     /**
  89.      * Handles work tree updates on both the checkout and the index.
  90.      * <p>
  91.      * You should use a single instance for all of your file changes. In case of
  92.      * an error, make sure your instance is released, and initiate a new one if
  93.      * necessary.
  94.      *
  95.      * @since 6.3.1
  96.      */
  97.     protected static class WorkTreeUpdater implements Closeable {

  98.         /**
  99.          * The result of writing the index changes.
  100.          */
  101.         public static class Result {

  102.             private final List<String> modifiedFiles = new LinkedList<>();

  103.             private final List<String> failedToDelete = new LinkedList<>();

  104.             private ObjectId treeId = null;

  105.             /**
  106.              * @return Modified tree ID if any, or null otherwise.
  107.              */
  108.             public ObjectId getTreeId() {
  109.                 return treeId;
  110.             }

  111.             /**
  112.              * @return Files that couldn't be deleted.
  113.              */
  114.             public List<String> getFailedToDelete() {
  115.                 return failedToDelete;
  116.             }

  117.             /**
  118.              * @return Files modified during this operation.
  119.              */
  120.             public List<String> getModifiedFiles() {
  121.                 return modifiedFiles;
  122.             }
  123.         }

  124.         Result result = new Result();

  125.         /**
  126.          * The repository this handler operates on.
  127.          */
  128.         @Nullable
  129.         private final Repository repo;

  130.         /**
  131.          * Set to true if this operation should work in-memory. The repo's
  132.          * dircache and workingtree are not touched by this method. Eventually
  133.          * needed files are created as temporary files and a new empty,
  134.          * in-memory dircache will be used instead the repo's one. Often used
  135.          * for bare repos where the repo doesn't even have a workingtree and
  136.          * dircache.
  137.          */
  138.         private final boolean inCore;

  139.         private final ObjectInserter inserter;

  140.         private final ObjectReader reader;

  141.         private DirCache dirCache;

  142.         private boolean implicitDirCache = false;

  143.         /**
  144.          * Builder to update the dir cache during this operation.
  145.          */
  146.         private DirCacheBuilder builder;

  147.         /**
  148.          * The {@link WorkingTreeOptions} are needed to determine line endings
  149.          * for affected files.
  150.          */
  151.         private WorkingTreeOptions workingTreeOptions;

  152.         /**
  153.          * The size limit (bytes) which controls a file to be stored in
  154.          * {@code Heap} or {@code LocalFile} during the operation.
  155.          */
  156.         private int inCoreFileSizeLimit;

  157.         /**
  158.          * If the operation has nothing to do for a file but check it out at the
  159.          * end of the operation, it can be added here.
  160.          */
  161.         private final Map<String, DirCacheEntry> toBeCheckedOut = new HashMap<>();

  162.         /**
  163.          * Files in this list will be deleted from the local copy at the end of
  164.          * the operation.
  165.          */
  166.         private final TreeMap<String, File> toBeDeleted = new TreeMap<>();

  167.         /**
  168.          * Keeps {@link CheckoutMetadata} for {@link #checkout()}.
  169.          */
  170.         private Map<String, CheckoutMetadata> checkoutMetadataByPath;

  171.         /**
  172.          * Keeps {@link CheckoutMetadata} for {@link #revertModifiedFiles()}.
  173.          */
  174.         private Map<String, CheckoutMetadata> cleanupMetadataByPath;

  175.         /**
  176.          * Whether the changes were successfully written.
  177.          */
  178.         private boolean indexChangesWritten;

  179.         /**
  180.          * @param repo
  181.          *            the {@link Repository}.
  182.          * @param dirCache
  183.          *            if set, use the provided dir cache. Otherwise, use the
  184.          *            default repository one
  185.          */
  186.         private WorkTreeUpdater(Repository repo, DirCache dirCache) {
  187.             this.repo = repo;
  188.             this.dirCache = dirCache;

  189.             this.inCore = false;
  190.             this.inserter = repo.newObjectInserter();
  191.             this.reader = inserter.newReader();
  192.             Config config = repo.getConfig();
  193.             this.workingTreeOptions = config.get(WorkingTreeOptions.KEY);
  194.             this.inCoreFileSizeLimit = getInCoreFileSizeLimit(config);
  195.             this.checkoutMetadataByPath = new HashMap<>();
  196.             this.cleanupMetadataByPath = new HashMap<>();
  197.         }

  198.         /**
  199.          * Creates a new {@link WorkTreeUpdater} for the given repository.
  200.          *
  201.          * @param repo
  202.          *            the {@link Repository}.
  203.          * @param dirCache
  204.          *            if set, use the provided dir cache. Otherwise, use the
  205.          *            default repository one
  206.          * @return the {@link WorkTreeUpdater}.
  207.          */
  208.         public static WorkTreeUpdater createWorkTreeUpdater(Repository repo,
  209.                 DirCache dirCache) {
  210.             return new WorkTreeUpdater(repo, dirCache);
  211.         }

  212.         /**
  213.          * @param repo
  214.          *            the {@link Repository}.
  215.          * @param dirCache
  216.          *            if set, use the provided dir cache. Otherwise, creates a
  217.          *            new one
  218.          * @param oi
  219.          *            to use for writing the modified objects with.
  220.          */
  221.         private WorkTreeUpdater(Repository repo, DirCache dirCache,
  222.                 ObjectInserter oi) {
  223.             this.repo = repo;
  224.             this.dirCache = dirCache;
  225.             this.inserter = oi;

  226.             this.inCore = true;
  227.             this.reader = oi.newReader();
  228.             if (repo != null) {
  229.                 this.inCoreFileSizeLimit = getInCoreFileSizeLimit(
  230.                         repo.getConfig());
  231.             }
  232.         }

  233.         /**
  234.          * Creates a new {@link WorkTreeUpdater} that works in memory only.
  235.          *
  236.          * @param repo
  237.          *            the {@link Repository}.
  238.          * @param dirCache
  239.          *            if set, use the provided dir cache. Otherwise, creates a
  240.          *            new one
  241.          * @param oi
  242.          *            to use for writing the modified objects with.
  243.          * @return the {@link WorkTreeUpdater}
  244.          */
  245.         public static WorkTreeUpdater createInCoreWorkTreeUpdater(
  246.                 Repository repo, DirCache dirCache, ObjectInserter oi) {
  247.             return new WorkTreeUpdater(repo, dirCache, oi);
  248.         }

  249.         private static int getInCoreFileSizeLimit(Config config) {
  250.             return config.getInt(ConfigConstants.CONFIG_MERGE_SECTION,
  251.                     ConfigConstants.CONFIG_KEY_IN_CORE_LIMIT, 10 << 20);
  252.         }

  253.         /**
  254.          * Gets the size limit for in-core files in this config.
  255.          *
  256.          * @return the size
  257.          */
  258.         public int getInCoreFileSizeLimit() {
  259.             return inCoreFileSizeLimit;
  260.         }

  261.         /**
  262.          * Gets dir cache for the repo. Locked if not inCore.
  263.          *
  264.          * @return the result dir cache
  265.          * @throws IOException
  266.          *             is case the dir cache cannot be read
  267.          */
  268.         public DirCache getLockedDirCache() throws IOException {
  269.             if (dirCache == null) {
  270.                 implicitDirCache = true;
  271.                 if (inCore) {
  272.                     dirCache = DirCache.newInCore();
  273.                 } else {
  274.                     dirCache = nonNullRepo().lockDirCache();
  275.                 }
  276.             }
  277.             if (builder == null) {
  278.                 builder = dirCache.builder();
  279.             }
  280.             return dirCache;
  281.         }

  282.         /**
  283.          * Creates a {@link DirCacheBuildIterator} for the builder of this
  284.          * {@link WorkTreeUpdater}.
  285.          *
  286.          * @return the {@link DirCacheBuildIterator}
  287.          */
  288.         public DirCacheBuildIterator createDirCacheBuildIterator() {
  289.             return new DirCacheBuildIterator(builder);
  290.         }

  291.         /**
  292.          * Writes the changes to the working tree (but not to the index).
  293.          *
  294.          * @param shouldCheckoutTheirs
  295.          *            before committing the changes
  296.          * @throws IOException
  297.          *             if any of the writes fail
  298.          */
  299.         public void writeWorkTreeChanges(boolean shouldCheckoutTheirs)
  300.                 throws IOException {
  301.             handleDeletedFiles();

  302.             if (inCore) {
  303.                 builder.finish();
  304.                 return;
  305.             }
  306.             if (shouldCheckoutTheirs) {
  307.                 // No problem found. The only thing left to be done is to
  308.                 // check out all files from "theirs" which have been selected to
  309.                 // go into the new index.
  310.                 checkout();
  311.             }

  312.             // All content operations are successfully done. If we can now write
  313.             // the
  314.             // new index we are on quite safe ground. Even if the checkout of
  315.             // files coming from "theirs" fails the user can work around such
  316.             // failures by checking out the index again.
  317.             if (!builder.commit()) {
  318.                 revertModifiedFiles();
  319.                 throw new IndexWriteException();
  320.             }
  321.         }

  322.         /**
  323.          * Writes the changes to the index.
  324.          *
  325.          * @return the {@link Result} of the operation.
  326.          * @throws IOException
  327.          *             if any of the writes fail
  328.          */
  329.         public Result writeIndexChanges() throws IOException {
  330.             result.treeId = getLockedDirCache().writeTree(inserter);
  331.             indexChangesWritten = true;
  332.             return result;
  333.         }

  334.         /**
  335.          * Adds a {@link DirCacheEntry} for direct checkout and remembers its
  336.          * {@link CheckoutMetadata}.
  337.          *
  338.          * @param path
  339.          *            of the entry
  340.          * @param entry
  341.          *            to add
  342.          * @param cleanupStreamType
  343.          *            to use for the cleanup metadata
  344.          * @param cleanupSmudgeCommand
  345.          *            to use for the cleanup metadata
  346.          * @param checkoutStreamType
  347.          *            to use for the checkout metadata
  348.          * @param checkoutSmudgeCommand
  349.          *            to use for the checkout metadata
  350.          */
  351.         public void addToCheckout(String path, DirCacheEntry entry,
  352.                 EolStreamType cleanupStreamType, String cleanupSmudgeCommand,
  353.                 EolStreamType checkoutStreamType,
  354.                 String checkoutSmudgeCommand) {
  355.             if (entry != null) {
  356.                 // In some cases, we just want to add the metadata.
  357.                 toBeCheckedOut.put(path, entry);
  358.             }
  359.             addCheckoutMetadata(cleanupMetadataByPath, path, cleanupStreamType,
  360.                     cleanupSmudgeCommand);
  361.             addCheckoutMetadata(checkoutMetadataByPath, path,
  362.                     checkoutStreamType, checkoutSmudgeCommand);
  363.         }

  364.         /**
  365.          * Gets a map which maps the paths of files which have to be checked out
  366.          * because the operation created new fully-merged content for this file
  367.          * into the index.
  368.          * <p>
  369.          * This means: the operation wrote a new stage 0 entry for this path.
  370.          * </p>
  371.          *
  372.          * @return the map
  373.          */
  374.         public Map<String, DirCacheEntry> getToBeCheckedOut() {
  375.             return toBeCheckedOut;
  376.         }

  377.         /**
  378.          * Remembers the given file to be deleted.
  379.          * <p>
  380.          * Note the actual deletion is only done in
  381.          * {@link #writeWorkTreeChanges}.
  382.          *
  383.          * @param path
  384.          *            of the file to be deleted
  385.          * @param file
  386.          *            to be deleted
  387.          * @param streamType
  388.          *            to use for cleanup metadata
  389.          * @param smudgeCommand
  390.          *            to use for cleanup metadata
  391.          */
  392.         public void deleteFile(String path, File file, EolStreamType streamType,
  393.                 String smudgeCommand) {
  394.             toBeDeleted.put(path, file);
  395.             if (file != null && file.isFile()) {
  396.                 addCheckoutMetadata(cleanupMetadataByPath, path, streamType,
  397.                         smudgeCommand);
  398.             }
  399.         }

  400.         /**
  401.          * Remembers the {@link CheckoutMetadata} for the given path; it may be
  402.          * needed in {@link #checkout()} or in {@link #revertModifiedFiles()}.
  403.          *
  404.          * @param map
  405.          *            to add the metadata to
  406.          * @param path
  407.          *            of the current node
  408.          * @param streamType
  409.          *            to use for the metadata
  410.          * @param smudgeCommand
  411.          *            to use for the metadata
  412.          */
  413.         private void addCheckoutMetadata(Map<String, CheckoutMetadata> map,
  414.                 String path, EolStreamType streamType, String smudgeCommand) {
  415.             if (inCore || map == null) {
  416.                 return;
  417.             }
  418.             map.put(path, new CheckoutMetadata(streamType, smudgeCommand));
  419.         }

  420.         /**
  421.          * Detects if CRLF conversion has been configured.
  422.          * <p>
  423.          * </p>
  424.          * See {@link EolStreamTypeUtil#detectStreamType} for more info.
  425.          *
  426.          * @param attributes
  427.          *            of the file for which the type is to be detected
  428.          * @return the detected type
  429.          */
  430.         public EolStreamType detectCheckoutStreamType(Attributes attributes) {
  431.             if (inCore) {
  432.                 return null;
  433.             }
  434.             return EolStreamTypeUtil.detectStreamType(OperationType.CHECKOUT_OP,
  435.                     workingTreeOptions, attributes);
  436.         }

  437.         private void handleDeletedFiles() {
  438.             // Iterate in reverse so that "folder/file" is deleted before
  439.             // "folder". Otherwise, this could result in a failing path because
  440.             // of a non-empty directory, for which delete() would fail.
  441.             for (String path : toBeDeleted.descendingKeySet()) {
  442.                 File file = inCore ? null : toBeDeleted.get(path);
  443.                 if (file != null && !file.delete()) {
  444.                     if (!file.isDirectory()) {
  445.                         result.failedToDelete.add(path);
  446.                     }
  447.                 }
  448.             }
  449.         }

  450.         /**
  451.          * Marks the given path as modified in the operation.
  452.          *
  453.          * @param path
  454.          *            to mark as modified
  455.          */
  456.         public void markAsModified(String path) {
  457.             result.modifiedFiles.add(path);
  458.         }

  459.         /**
  460.          * Gets the list of files which were modified in this operation.
  461.          *
  462.          * @return the list
  463.          */
  464.         public List<String> getModifiedFiles() {
  465.             return result.modifiedFiles;
  466.         }

  467.         private void checkout() throws NoWorkTreeException, IOException {
  468.             for (Map.Entry<String, DirCacheEntry> entry : toBeCheckedOut
  469.                     .entrySet()) {
  470.                 DirCacheEntry dirCacheEntry = entry.getValue();
  471.                 if (dirCacheEntry.getFileMode() == FileMode.GITLINK) {
  472.                     new File(nonNullRepo().getWorkTree(), entry.getKey())
  473.                             .mkdirs();
  474.                 } else {
  475.                     DirCacheCheckout.checkoutEntry(repo, dirCacheEntry, reader,
  476.                             false, checkoutMetadataByPath.get(entry.getKey()),
  477.                             workingTreeOptions);
  478.                     result.modifiedFiles.add(entry.getKey());
  479.                 }
  480.             }
  481.         }

  482.         /**
  483.          * Reverts any uncommitted changes in the worktree. We know that for all
  484.          * modified files the old content was in the old index and the index
  485.          * contained only stage 0. In case of inCore operation just clear the
  486.          * history of modified files.
  487.          *
  488.          * @throws IOException
  489.          *             in case the cleaning up failed
  490.          */
  491.         public void revertModifiedFiles() throws IOException {
  492.             if (inCore) {
  493.                 result.modifiedFiles.clear();
  494.                 return;
  495.             }
  496.             if (indexChangesWritten) {
  497.                 return;
  498.             }
  499.             for (String path : result.modifiedFiles) {
  500.                 DirCacheEntry entry = dirCache.getEntry(path);
  501.                 if (entry != null) {
  502.                     DirCacheCheckout.checkoutEntry(repo, entry, reader, false,
  503.                             cleanupMetadataByPath.get(path),
  504.                             workingTreeOptions);
  505.                 }
  506.             }
  507.         }

  508.         @Override
  509.         public void close() throws IOException {
  510.             if (implicitDirCache) {
  511.                 dirCache.unlock();
  512.             }
  513.         }

  514.         /**
  515.          * Updates the file in the checkout with the given content.
  516.          *
  517.          * @param inputStream
  518.          *            the content to be updated
  519.          * @param streamType
  520.          *            for parsing the content
  521.          * @param smudgeCommand
  522.          *            for formatting the content
  523.          * @param path
  524.          *            of the file to be updated
  525.          * @param file
  526.          *            to be updated
  527.          * @throws IOException
  528.          *             if the file cannot be updated
  529.          */
  530.         public void updateFileWithContent(StreamSupplier inputStream,
  531.                 EolStreamType streamType, String smudgeCommand, String path,
  532.                 File file) throws IOException {
  533.             if (inCore) {
  534.                 return;
  535.             }
  536.             CheckoutMetadata metadata = new CheckoutMetadata(streamType,
  537.                     smudgeCommand);

  538.             try (OutputStream outputStream = new FileOutputStream(file)) {
  539.                 DirCacheCheckout.getContent(repo, path, metadata, inputStream,
  540.                         workingTreeOptions, outputStream);
  541.             }
  542.         }

  543.         /**
  544.          * Creates a path with the given content, and adds it to the specified
  545.          * stage to the index builder.
  546.          *
  547.          * @param input
  548.          *            the content to be updated
  549.          * @param path
  550.          *            of the file to be updated
  551.          * @param fileMode
  552.          *            of the modified file
  553.          * @param entryStage
  554.          *            of the new entry
  555.          * @param lastModified
  556.          *            instant of the modified file
  557.          * @param len
  558.          *            of the content
  559.          * @param lfsAttribute
  560.          *            for checking for LFS enablement
  561.          * @return the entry which was added to the index
  562.          * @throws IOException
  563.          *             if inserting the content fails
  564.          */
  565.         public DirCacheEntry insertToIndex(InputStream input, byte[] path,
  566.                 FileMode fileMode, int entryStage, Instant lastModified,
  567.                 int len, Attribute lfsAttribute) throws IOException {
  568.             return addExistingToIndex(insertResult(input, lfsAttribute, len),
  569.                     path, fileMode, entryStage, lastModified, len);
  570.         }

  571.         /**
  572.          * Adds a path with the specified stage to the index builder.
  573.          *
  574.          * @param objectId
  575.          *            of the existing object to add
  576.          * @param path
  577.          *            of the modified file
  578.          * @param fileMode
  579.          *            of the modified file
  580.          * @param entryStage
  581.          *            of the new entry
  582.          * @param lastModified
  583.          *            instant of the modified file
  584.          * @param len
  585.          *            of the modified file content
  586.          * @return the entry which was added to the index
  587.          */
  588.         public DirCacheEntry addExistingToIndex(ObjectId objectId, byte[] path,
  589.                 FileMode fileMode, int entryStage, Instant lastModified,
  590.                 int len) {
  591.             DirCacheEntry dce = new DirCacheEntry(path, entryStage);
  592.             dce.setFileMode(fileMode);
  593.             if (lastModified != null) {
  594.                 dce.setLastModified(lastModified);
  595.             }
  596.             dce.setLength(inCore ? 0 : len);
  597.             dce.setObjectId(objectId);
  598.             builder.add(dce);
  599.             return dce;
  600.         }

  601.         private ObjectId insertResult(InputStream input, Attribute lfsAttribute,
  602.                 long length) throws IOException {
  603.             try (LfsInputStream is = LfsFactory.getInstance()
  604.                     .applyCleanFilter(repo, input, length, lfsAttribute)) {
  605.                 return inserter.insert(OBJ_BLOB, is.getLength(), is);
  606.             }
  607.         }

  608.         /**
  609.          * Gets the non-null repository instance of this
  610.          * {@link WorkTreeUpdater}.
  611.          *
  612.          * @return non-null repository instance
  613.          * @throws NullPointerException
  614.          *             if the handler was constructed without a repository.
  615.          */
  616.         @NonNull
  617.         private Repository nonNullRepo() throws NullPointerException {
  618.             return Objects.requireNonNull(repo,
  619.                     () -> JGitText.get().repositoryIsRequired);
  620.         }
  621.     }

  622.     /**
  623.      * If the merge fails (means: not stopped because of unresolved conflicts)
  624.      * this enum is used to explain why it failed
  625.      */
  626.     public enum MergeFailureReason {
  627.         /** the merge failed because of a dirty index */
  628.         DIRTY_INDEX,
  629.         /** the merge failed because of a dirty workingtree */
  630.         DIRTY_WORKTREE,
  631.         /** the merge failed because of a file could not be deleted */
  632.         COULD_NOT_DELETE
  633.     }

  634.     /**
  635.      * The tree walk which we'll iterate over to merge entries.
  636.      *
  637.      * @since 3.4
  638.      */
  639.     protected NameConflictTreeWalk tw;

  640.     /**
  641.      * string versions of a list of commit SHA1s
  642.      *
  643.      * @since 3.0
  644.      */
  645.     protected String[] commitNames;

  646.     /**
  647.      * Index of the base tree within the {@link #tw tree walk}.
  648.      *
  649.      * @since 3.4
  650.      */
  651.     protected static final int T_BASE = 0;

  652.     /**
  653.      * Index of our tree in withthe {@link #tw tree walk}.
  654.      *
  655.      * @since 3.4
  656.      */
  657.     protected static final int T_OURS = 1;

  658.     /**
  659.      * Index of their tree within the {@link #tw tree walk}.
  660.      *
  661.      * @since 3.4
  662.      */
  663.     protected static final int T_THEIRS = 2;

  664.     /**
  665.      * Index of the index tree within the {@link #tw tree walk}.
  666.      *
  667.      * @since 3.4
  668.      */
  669.     protected static final int T_INDEX = 3;

  670.     /**
  671.      * Index of the working directory tree within the {@link #tw tree walk}.
  672.      *
  673.      * @since 3.4
  674.      */
  675.     protected static final int T_FILE = 4;

  676.     /**
  677.      * Handler for repository I/O actions.
  678.      *
  679.      * @since 6.3
  680.      */
  681.     protected WorkTreeUpdater workTreeUpdater;

  682.     /**
  683.      * merge result as tree
  684.      *
  685.      * @since 3.0
  686.      */
  687.     protected ObjectId resultTree;

  688.     /**
  689.      * Files modified during this operation. Note this list is only updated after a successful write.
  690.      */
  691.     protected List<String> modifiedFiles = new ArrayList<>();

  692.     /**
  693.      * Paths that could not be merged by this merger because of an unsolvable
  694.      * conflict.
  695.      *
  696.      * @since 3.4
  697.      */
  698.     protected List<String> unmergedPaths = new ArrayList<>();

  699.     /**
  700.      * Low-level textual merge results. Will be passed on to the callers in case
  701.      * of conflicts.
  702.      *
  703.      * @since 3.4
  704.      */
  705.     protected Map<String, MergeResult<? extends Sequence>> mergeResults = new HashMap<>();

  706.     /**
  707.      * Paths for which the merge failed altogether.
  708.      *
  709.      * @since 3.4
  710.      */
  711.     protected Map<String, MergeFailureReason> failingPaths = new HashMap<>();

  712.     /**
  713.      * Updated as we merge entries of the tree walk. Tells us whether we should
  714.      * recurse into the entry if it is a subtree.
  715.      *
  716.      * @since 3.4
  717.      */
  718.     protected boolean enterSubtree;

  719.     /**
  720.      * Set to true if this merge should work in-memory. The repos dircache and
  721.      * workingtree are not touched by this method. Eventually needed files are
  722.      * created as temporary files and a new empty, in-memory dircache will be
  723.      * used instead the repo's one. Often used for bare repos where the repo
  724.      * doesn't even have a workingtree and dircache.
  725.      * @since 3.0
  726.      */
  727.     protected boolean inCore;

  728.     /**
  729.      * Directory cache
  730.      * @since 3.0
  731.      */
  732.     protected DirCache dircache;

  733.     /**
  734.      * The iterator to access the working tree. If set to <code>null</code> this
  735.      * merger will not touch the working tree.
  736.      * @since 3.0
  737.      */
  738.     protected WorkingTreeIterator workingTreeIterator;

  739.     /**
  740.      * our merge algorithm
  741.      * @since 3.0
  742.      */
  743.     protected MergeAlgorithm mergeAlgorithm;

  744.     /**
  745.      * The {@link ContentMergeStrategy} to use for "resolve" and "recursive"
  746.      * merges.
  747.      */
  748.     @NonNull
  749.     private ContentMergeStrategy contentStrategy = ContentMergeStrategy.CONFLICT;

  750.     private static MergeAlgorithm getMergeAlgorithm(Config config) {
  751.         SupportedAlgorithm diffAlg = config.getEnum(
  752.                 CONFIG_DIFF_SECTION, null, CONFIG_KEY_ALGORITHM,
  753.                 HISTOGRAM);
  754.         return new MergeAlgorithm(DiffAlgorithm.getAlgorithm(diffAlg));
  755.     }

  756.     private static String[] defaultCommitNames() {
  757.         return new String[]{"BASE", "OURS", "THEIRS"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
  758.     }

  759.     private static final Attributes NO_ATTRIBUTES = new Attributes();

  760.     /**
  761.      * Constructor for ResolveMerger.
  762.      *
  763.      * @param local
  764.      *            the {@link org.eclipse.jgit.lib.Repository}.
  765.      * @param inCore
  766.      *            a boolean.
  767.      */
  768.     protected ResolveMerger(Repository local, boolean inCore) {
  769.         super(local);
  770.         Config config = local.getConfig();
  771.         mergeAlgorithm = getMergeAlgorithm(config);
  772.         commitNames = defaultCommitNames();
  773.         this.inCore = inCore;
  774.     }

  775.     /**
  776.      * Constructor for ResolveMerger.
  777.      *
  778.      * @param local
  779.      *            the {@link org.eclipse.jgit.lib.Repository}.
  780.      */
  781.     protected ResolveMerger(Repository local) {
  782.         this(local, false);
  783.     }

  784.     /**
  785.      * Constructor for ResolveMerger.
  786.      *
  787.      * @param inserter
  788.      *            an {@link org.eclipse.jgit.lib.ObjectInserter} object.
  789.      * @param config
  790.      *            the repository configuration
  791.      * @since 4.8
  792.      */
  793.     protected ResolveMerger(ObjectInserter inserter, Config config) {
  794.         super(inserter);
  795.         mergeAlgorithm = getMergeAlgorithm(config);
  796.         commitNames = defaultCommitNames();
  797.         inCore = true;
  798.     }

  799.     /**
  800.      * Retrieves the content merge strategy for content conflicts.
  801.      *
  802.      * @return the {@link ContentMergeStrategy} in effect
  803.      * @since 5.12
  804.      */
  805.     @NonNull
  806.     public ContentMergeStrategy getContentMergeStrategy() {
  807.         return contentStrategy;
  808.     }

  809.     /**
  810.      * Sets the content merge strategy for content conflicts.
  811.      *
  812.      * @param strategy
  813.      *            {@link ContentMergeStrategy} to use
  814.      * @since 5.12
  815.      */
  816.     public void setContentMergeStrategy(ContentMergeStrategy strategy) {
  817.         contentStrategy = strategy == null ? ContentMergeStrategy.CONFLICT
  818.                 : strategy;
  819.     }

  820.     /** {@inheritDoc} */
  821.     @Override
  822.     protected boolean mergeImpl() throws IOException {
  823.         return mergeTrees(mergeBase(), sourceTrees[0], sourceTrees[1],
  824.                 false);
  825.     }

  826.     /**
  827.      * adds a new path with the specified stage to the index builder
  828.      *
  829.      * @param path
  830.      * @param p
  831.      * @param stage
  832.      * @param lastMod
  833.      * @param len
  834.      * @return the entry which was added to the index
  835.      */
  836.     private DirCacheEntry add(byte[] path, CanonicalTreeParser p, int stage,
  837.             Instant lastMod, long len) {
  838.         if (p != null && !p.getEntryFileMode().equals(FileMode.TREE)) {
  839.             return workTreeUpdater.addExistingToIndex(p.getEntryObjectId(), path,
  840.                     p.getEntryFileMode(), stage,
  841.                     lastMod, (int) len);
  842.         }
  843.         return null;
  844.     }

  845.     /**
  846.      * Adds the conflict stages for the current path of {@link #tw} to the index
  847.      * builder and returns the "theirs" stage; if present.
  848.      *
  849.      * @param base
  850.      *            of the conflict
  851.      * @param ours
  852.      *            of the conflict
  853.      * @param theirs
  854.      *            of the conflict
  855.      * @return the {@link DirCacheEntry} for the "theirs" stage, or {@code null}
  856.      */
  857.     private DirCacheEntry addConflict(CanonicalTreeParser base,
  858.             CanonicalTreeParser ours, CanonicalTreeParser theirs) {
  859.         add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0);
  860.         add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
  861.         return add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 0);
  862.     }

  863.     /**
  864.      * adds a entry to the index builder which is a copy of the specified
  865.      * DirCacheEntry
  866.      *
  867.      * @param e
  868.      *            the entry which should be copied
  869.      *
  870.      * @return the entry which was added to the index
  871.      */
  872.     private DirCacheEntry keep(DirCacheEntry e) {
  873.         return workTreeUpdater.addExistingToIndex(e.getObjectId(), e.getRawPath(), e.getFileMode(),
  874.                 e.getStage(), e.getLastModifiedInstant(), e.getLength());
  875.     }

  876.     /**
  877.      * Adds a {@link DirCacheEntry} for direct checkout and remembers its
  878.      * {@link CheckoutMetadata}.
  879.      *
  880.      * @param path
  881.      *            of the entry
  882.      * @param entry
  883.      *            to add
  884.      * @param attributes
  885.      *            the {@link Attributes} of the trees
  886.      * @throws IOException
  887.      *             if the {@link CheckoutMetadata} cannot be determined
  888.      * @since 6.1
  889.      */
  890.     protected void addToCheckout(String path, DirCacheEntry entry,
  891.             Attributes[] attributes)
  892.             throws IOException {
  893.         EolStreamType cleanupStreamType = workTreeUpdater.detectCheckoutStreamType(attributes[T_OURS]);
  894.         String cleanupSmudgeCommand = tw.getSmudgeCommand(attributes[T_OURS]);
  895.         EolStreamType checkoutStreamType = workTreeUpdater.detectCheckoutStreamType(attributes[T_THEIRS]);
  896.         String checkoutSmudgeCommand = tw.getSmudgeCommand(attributes[T_THEIRS]);
  897.         workTreeUpdater.addToCheckout(path, entry, cleanupStreamType, cleanupSmudgeCommand,
  898.                 checkoutStreamType, checkoutSmudgeCommand);
  899.     }

  900.     /**
  901.      * Remember a path for deletion, and remember its {@link CheckoutMetadata}
  902.      * in case it has to be restored in the cleanUp.
  903.      *
  904.      * @param path
  905.      *            of the entry
  906.      * @param isFile
  907.      *            whether it is a file
  908.      * @param attributes
  909.      *            to use for determining the {@link CheckoutMetadata}
  910.      * @throws IOException
  911.      *             if the {@link CheckoutMetadata} cannot be determined
  912.      * @since 5.1
  913.      */
  914.     protected void addDeletion(String path, boolean isFile,
  915.             Attributes attributes) throws IOException {
  916.         if (db == null || nonNullRepo().isBare() || !isFile)
  917.             return;

  918.         File file = new File(nonNullRepo().getWorkTree(), path);
  919.         EolStreamType streamType = workTreeUpdater.detectCheckoutStreamType(attributes);
  920.         String smudgeCommand = tw.getSmudgeCommand(attributes);
  921.         workTreeUpdater.deleteFile(path, file, streamType, smudgeCommand);
  922.     }

  923.     /**
  924.      * Processes one path and tries to merge taking git attributes in account.
  925.      * This method will do all trivial (not content) merges and will also detect
  926.      * if a merge will fail. The merge will fail when one of the following is
  927.      * true
  928.      * <ul>
  929.      * <li>the index entry does not match the entry in ours. When merging one
  930.      * branch into the current HEAD, ours will point to HEAD and theirs will
  931.      * point to the other branch. It is assumed that the index matches the HEAD
  932.      * because it will only not match HEAD if it was populated before the merge
  933.      * operation. But the merge commit should not accidentally contain
  934.      * modifications done before the merge. Check the <a href=
  935.      * "http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html#_3_way_merge"
  936.      * >git read-tree</a> documentation for further explanations.</li>
  937.      * <li>A conflict was detected and the working-tree file is dirty. When a
  938.      * conflict is detected the content-merge algorithm will try to write a
  939.      * merged version into the working-tree. If the file is dirty we would
  940.      * override unsaved data.</li>
  941.      * </ul>
  942.      *
  943.      * @param base
  944.      *            the common base for ours and theirs
  945.      * @param ours
  946.      *            the ours side of the merge. When merging a branch into the
  947.      *            HEAD ours will point to HEAD
  948.      * @param theirs
  949.      *            the theirs side of the merge. When merging a branch into the
  950.      *            current HEAD theirs will point to the branch which is merged
  951.      *            into HEAD.
  952.      * @param index
  953.      *            the index entry
  954.      * @param work
  955.      *            the file in the working tree
  956.      * @param ignoreConflicts
  957.      *            see
  958.      *            {@link org.eclipse.jgit.merge.ResolveMerger#mergeTrees(AbstractTreeIterator, RevTree, RevTree, boolean)}
  959.      * @param attributes
  960.      *            the {@link Attributes} for the three trees
  961.      * @return <code>false</code> if the merge will fail because the index entry
  962.      *         didn't match ours or the working-dir file was dirty and a
  963.      *         conflict occurred
  964.      * @throws java.io.IOException
  965.      * @since 6.1
  966.      */
  967.     protected boolean processEntry(CanonicalTreeParser base,
  968.             CanonicalTreeParser ours, CanonicalTreeParser theirs,
  969.             DirCacheBuildIterator index, WorkingTreeIterator work,
  970.             boolean ignoreConflicts, Attributes[] attributes)
  971.             throws IOException {
  972.         enterSubtree = true;
  973.         final int modeO = tw.getRawMode(T_OURS);
  974.         final int modeT = tw.getRawMode(T_THEIRS);
  975.         final int modeB = tw.getRawMode(T_BASE);
  976.         boolean gitLinkMerging = isGitLink(modeO) || isGitLink(modeT)
  977.                 || isGitLink(modeB);
  978.         if (modeO == 0 && modeT == 0 && modeB == 0) {
  979.             // File is either untracked or new, staged but uncommitted
  980.             return true;
  981.         }

  982.         if (isIndexDirty()) {
  983.             return false;
  984.         }

  985.         DirCacheEntry ourDce = null;

  986.         if (index == null || index.getDirCacheEntry() == null) {
  987.             // create a fake DCE, but only if ours is valid. ours is kept only
  988.             // in case it is valid, so a null ourDce is ok in all other cases.
  989.             if (nonTree(modeO)) {
  990.                 ourDce = new DirCacheEntry(tw.getRawPath());
  991.                 ourDce.setObjectId(tw.getObjectId(T_OURS));
  992.                 ourDce.setFileMode(tw.getFileMode(T_OURS));
  993.             }
  994.         } else {
  995.             ourDce = index.getDirCacheEntry();
  996.         }

  997.         if (nonTree(modeO) && nonTree(modeT) && tw.idEqual(T_OURS, T_THEIRS)) {
  998.             // OURS and THEIRS have equal content. Check the file mode
  999.             if (modeO == modeT) {
  1000.                 // content and mode of OURS and THEIRS are equal: it doesn't
  1001.                 // matter which one we choose. OURS is chosen. Since the index
  1002.                 // is clean (the index matches already OURS) we can keep the existing one
  1003.                 keep(ourDce);
  1004.                 // no checkout needed!
  1005.                 return true;
  1006.             }
  1007.             // same content but different mode on OURS and THEIRS.
  1008.             // Try to merge the mode and report an error if this is
  1009.             // not possible.
  1010.             int newMode = mergeFileModes(modeB, modeO, modeT);
  1011.             if (newMode != FileMode.MISSING.getBits()) {
  1012.                 if (newMode == modeO) {
  1013.                     // ours version is preferred
  1014.                     keep(ourDce);
  1015.                 } else {
  1016.                     // the preferred version THEIRS has a different mode
  1017.                     // than ours. Check it out!
  1018.                     if (isWorktreeDirty(work, ourDce)) {
  1019.                         return false;
  1020.                     }
  1021.                     // we know about length and lastMod only after we have
  1022.                     // written the new content.
  1023.                     // This will happen later. Set these values to 0 for know.
  1024.                     DirCacheEntry e = add(tw.getRawPath(), theirs,
  1025.                             DirCacheEntry.STAGE_0, EPOCH, 0);
  1026.                     addToCheckout(tw.getPathString(), e, attributes);
  1027.                 }
  1028.                 return true;
  1029.             }
  1030.             if (!ignoreConflicts) {
  1031.                 // FileModes are not mergeable. We found a conflict on modes.
  1032.                 // For conflicting entries we don't know lastModified and
  1033.                 // length.
  1034.                 // This path can be skipped on ignoreConflicts, so the caller
  1035.                 // could use virtual commit.
  1036.                 addConflict(base, ours, theirs);
  1037.                 unmergedPaths.add(tw.getPathString());
  1038.                 mergeResults.put(tw.getPathString(),
  1039.                         new MergeResult<>(Collections.emptyList()));
  1040.             }
  1041.             return true;
  1042.         }

  1043.         if (modeB == modeT && tw.idEqual(T_BASE, T_THEIRS)) {
  1044.             // THEIRS was not changed compared to BASE. All changes must be in
  1045.             // OURS. OURS is chosen. We can keep the existing entry.
  1046.             if (ourDce != null) {
  1047.                 keep(ourDce);
  1048.             }
  1049.             // no checkout needed!
  1050.             return true;
  1051.         }

  1052.         if (modeB == modeO && tw.idEqual(T_BASE, T_OURS)) {
  1053.             // OURS was not changed compared to BASE. All changes must be in
  1054.             // THEIRS. THEIRS is chosen.

  1055.             // Check worktree before checking out THEIRS
  1056.             if (isWorktreeDirty(work, ourDce)) {
  1057.                 return false;
  1058.             }
  1059.             if (nonTree(modeT)) {
  1060.                 // we know about length and lastMod only after we have written
  1061.                 // the new content.
  1062.                 // This will happen later. Set these values to 0 for know.
  1063.                 DirCacheEntry e = add(tw.getRawPath(), theirs,
  1064.                         DirCacheEntry.STAGE_0, EPOCH, 0);
  1065.                 if (e != null) {
  1066.                     addToCheckout(tw.getPathString(), e, attributes);
  1067.                 }
  1068.                 return true;
  1069.             }
  1070.             // we want THEIRS ... but THEIRS contains a folder or the
  1071.             // deletion of the path. Delete what's in the working tree,
  1072.             // which we know to be clean.
  1073.             if (tw.getTreeCount() > T_FILE && tw.getRawMode(T_FILE) == 0) {
  1074.                 // Not present in working tree, so nothing to delete
  1075.                 return true;
  1076.             }
  1077.             if (modeT != 0 && modeT == modeB) {
  1078.                 // Base, ours, and theirs all contain a folder: don't delete
  1079.                 return true;
  1080.             }
  1081.             addDeletion(tw.getPathString(), nonTree(modeO), attributes[T_OURS]);
  1082.             return true;
  1083.         }

  1084.         if (tw.isSubtree()) {
  1085.             // file/folder conflicts: here I want to detect only file/folder
  1086.             // conflict between ours and theirs. file/folder conflicts between
  1087.             // base/index/workingTree and something else are not relevant or
  1088.             // detected later
  1089.             if (nonTree(modeO) != nonTree(modeT)) {
  1090.                 if (ignoreConflicts) {
  1091.                     // In case of merge failures, ignore this path instead of reporting unmerged, so
  1092.                     // a caller can use virtual commit. This will not result in files with conflict
  1093.                     // markers in the index/working tree. The actual diff on the path will be
  1094.                     // computed directly on children.
  1095.                     enterSubtree = false;
  1096.                     return true;
  1097.                 }
  1098.                 if (nonTree(modeB)) {
  1099.                     add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0);
  1100.                 }
  1101.                 if (nonTree(modeO)) {
  1102.                     add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
  1103.                 }
  1104.                 if (nonTree(modeT)) {
  1105.                     add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 0);
  1106.                 }
  1107.                 unmergedPaths.add(tw.getPathString());
  1108.                 enterSubtree = false;
  1109.                 return true;
  1110.             }

  1111.             // ours and theirs are both folders or both files (and treewalk
  1112.             // tells us we are in a subtree because of index or working-dir).
  1113.             // If they are both folders no content-merge is required - we can
  1114.             // return here.
  1115.             if (!nonTree(modeO)) {
  1116.                 return true;
  1117.             }

  1118.             // ours and theirs are both files, just fall out of the if block
  1119.             // and do the content merge
  1120.         }

  1121.         if (nonTree(modeO) && nonTree(modeT)) {
  1122.             // Check worktree before modifying files
  1123.             boolean worktreeDirty = isWorktreeDirty(work, ourDce);
  1124.             if (!attributes[T_OURS].canBeContentMerged() && worktreeDirty) {
  1125.                 return false;
  1126.             }

  1127.             if (gitLinkMerging && ignoreConflicts) {
  1128.                 // Always select 'ours' in case of GITLINK merge failures so
  1129.                 // a caller can use virtual commit.
  1130.                 add(tw.getRawPath(), ours, DirCacheEntry.STAGE_0, EPOCH, 0);
  1131.                 return true;
  1132.             } else if (gitLinkMerging) {
  1133.                 addConflict(base, ours, theirs);
  1134.                 MergeResult<SubmoduleConflict> result = createGitLinksMergeResult(
  1135.                         base, ours, theirs);
  1136.                 result.setContainsConflicts(true);
  1137.                 mergeResults.put(tw.getPathString(), result);
  1138.                 unmergedPaths.add(tw.getPathString());
  1139.                 return true;
  1140.             } else if (!attributes[T_OURS].canBeContentMerged()) {
  1141.                 // File marked as binary
  1142.                 switch (getContentMergeStrategy()) {
  1143.                     case OURS:
  1144.                         keep(ourDce);
  1145.                         return true;
  1146.                     case THEIRS:
  1147.                         DirCacheEntry theirEntry = add(tw.getRawPath(), theirs,
  1148.                                 DirCacheEntry.STAGE_0, EPOCH, 0);
  1149.                         addToCheckout(tw.getPathString(), theirEntry, attributes);
  1150.                         return true;
  1151.                     default:
  1152.                         break;
  1153.                 }
  1154.                 addConflict(base, ours, theirs);

  1155.                 // attribute merge issues are conflicts but not failures
  1156.                 unmergedPaths.add(tw.getPathString());
  1157.                 return true;
  1158.             }

  1159.             // Check worktree before modifying files
  1160.             if (worktreeDirty) {
  1161.                 return false;
  1162.             }

  1163.             MergeResult<RawText> result = null;
  1164.             boolean hasSymlink = FileMode.SYMLINK.equals(modeO)
  1165.                     || FileMode.SYMLINK.equals(modeT);
  1166.             if (!hasSymlink) {
  1167.                 try {
  1168.                     result = contentMerge(base, ours, theirs, attributes,
  1169.                             getContentMergeStrategy());
  1170.                 } catch (BinaryBlobException e) {
  1171.                     // result == null
  1172.                 }
  1173.             }
  1174.             if (result == null) {
  1175.                 switch (getContentMergeStrategy()) {
  1176.                 case OURS:
  1177.                     keep(ourDce);
  1178.                     return true;
  1179.                 case THEIRS:
  1180.                     DirCacheEntry e = add(tw.getRawPath(), theirs,
  1181.                             DirCacheEntry.STAGE_0, EPOCH, 0);
  1182.                     if (e != null) {
  1183.                         addToCheckout(tw.getPathString(), e, attributes);
  1184.                     }
  1185.                     return true;
  1186.                 default:
  1187.                     result = new MergeResult<>(Collections.emptyList());
  1188.                     result.setContainsConflicts(true);
  1189.                     break;
  1190.                 }
  1191.             }
  1192.             if (ignoreConflicts) {
  1193.                 result.setContainsConflicts(false);
  1194.             }
  1195.             String currentPath = tw.getPathString();
  1196.             if (hasSymlink) {
  1197.                 if (ignoreConflicts) {
  1198.                     if (((modeT & FileMode.TYPE_MASK) == FileMode.TYPE_FILE)) {
  1199.                         DirCacheEntry e = add(tw.getRawPath(), theirs,
  1200.                                 DirCacheEntry.STAGE_0, EPOCH, 0);
  1201.                         addToCheckout(currentPath, e, attributes);
  1202.                     } else {
  1203.                         keep(ourDce);
  1204.                     }
  1205.                 } else {
  1206.                     // Record the conflict
  1207.                     DirCacheEntry e = addConflict(base, ours, theirs);
  1208.                     mergeResults.put(currentPath, result);
  1209.                     // If theirs is a file, check it out. In link/file
  1210.                     // conflicts, C git prefers the file.
  1211.                     if (((modeT & FileMode.TYPE_MASK) == FileMode.TYPE_FILE)
  1212.                             && e != null) {
  1213.                         addToCheckout(currentPath, e, attributes);
  1214.                     }
  1215.                 }
  1216.             } else {
  1217.                 updateIndex(base, ours, theirs, result, attributes[T_OURS]);
  1218.             }
  1219.             if (result.containsConflicts() && !ignoreConflicts) {
  1220.                 unmergedPaths.add(currentPath);
  1221.             }
  1222.             workTreeUpdater.markAsModified(currentPath);
  1223.             // Entry is null - only adds the metadata.
  1224.             addToCheckout(currentPath, null, attributes);
  1225.         } else if (modeO != modeT) {
  1226.             // OURS or THEIRS has been deleted
  1227.             if (((modeO != 0 && !tw.idEqual(T_BASE, T_OURS)) || (modeT != 0 && !tw
  1228.                     .idEqual(T_BASE, T_THEIRS)))) {
  1229.                 if (gitLinkMerging && ignoreConflicts) {
  1230.                     add(tw.getRawPath(), ours, DirCacheEntry.STAGE_0, EPOCH, 0);
  1231.                 } else if (gitLinkMerging) {
  1232.                     addConflict(base, ours, theirs);
  1233.                     MergeResult<SubmoduleConflict> result = createGitLinksMergeResult(
  1234.                             base, ours, theirs);
  1235.                     result.setContainsConflicts(true);
  1236.                     mergeResults.put(tw.getPathString(), result);
  1237.                     unmergedPaths.add(tw.getPathString());
  1238.                 } else {
  1239.                     boolean isSymLink = ((modeO | modeT)
  1240.                             & FileMode.TYPE_MASK) == FileMode.TYPE_SYMLINK;
  1241.                     // Content merge strategy does not apply to delete-modify
  1242.                     // conflicts!
  1243.                     MergeResult<RawText> result;
  1244.                     if (isSymLink) {
  1245.                         // No need to do a content merge
  1246.                         result = new MergeResult<>(Collections.emptyList());
  1247.                         result.setContainsConflicts(true);
  1248.                     } else {
  1249.                         try {
  1250.                             result = contentMerge(base, ours, theirs,
  1251.                                     attributes, ContentMergeStrategy.CONFLICT);
  1252.                         } catch (BinaryBlobException e) {
  1253.                             result = new MergeResult<>(Collections.emptyList());
  1254.                             result.setContainsConflicts(true);
  1255.                         }
  1256.                     }
  1257.                     if (ignoreConflicts) {
  1258.                         result.setContainsConflicts(false);
  1259.                         if (isSymLink) {
  1260.                             if (modeO != 0) {
  1261.                                 keep(ourDce);
  1262.                             } else {
  1263.                                 // Check out theirs
  1264.                                 if (isWorktreeDirty(work, ourDce)) {
  1265.                                     return false;
  1266.                                 }
  1267.                                 DirCacheEntry e = add(tw.getRawPath(), theirs,
  1268.                                         DirCacheEntry.STAGE_0, EPOCH, 0);
  1269.                                 if (e != null) {
  1270.                                     addToCheckout(tw.getPathString(), e,
  1271.                                             attributes);
  1272.                                 }
  1273.                             }
  1274.                         } else {
  1275.                             // In case a conflict is detected the working tree
  1276.                             // file is again filled with new content (containing
  1277.                             // conflict markers). But also stage 0 of the index
  1278.                             // is filled with that content.
  1279.                             updateIndex(base, ours, theirs, result,
  1280.                                     attributes[T_OURS]);
  1281.                         }
  1282.                     } else {
  1283.                         DirCacheEntry e = addConflict(base, ours, theirs);

  1284.                         // OURS was deleted checkout THEIRS
  1285.                         if (modeO == 0) {
  1286.                             // Check worktree before checking out THEIRS
  1287.                             if (isWorktreeDirty(work, ourDce)) {
  1288.                                 return false;
  1289.                             }
  1290.                             if (nonTree(modeT) && e != null) {
  1291.                                 addToCheckout(tw.getPathString(), e,
  1292.                                         attributes);
  1293.                             }
  1294.                         }

  1295.                         unmergedPaths.add(tw.getPathString());

  1296.                         // generate a MergeResult for the deleted file
  1297.                         mergeResults.put(tw.getPathString(), result);
  1298.                     }
  1299.                 }
  1300.             }
  1301.         }
  1302.         return true;
  1303.     }

  1304.     private static MergeResult<SubmoduleConflict> createGitLinksMergeResult(
  1305.             CanonicalTreeParser base, CanonicalTreeParser ours,
  1306.             CanonicalTreeParser theirs) {
  1307.         return new MergeResult<>(Arrays.asList(
  1308.                 new SubmoduleConflict(
  1309.                         base == null ? null : base.getEntryObjectId()),
  1310.                 new SubmoduleConflict(
  1311.                         ours == null ? null : ours.getEntryObjectId()),
  1312.                 new SubmoduleConflict(
  1313.                         theirs == null ? null : theirs.getEntryObjectId())));
  1314.     }

  1315.     /**
  1316.      * Does the content merge. The three texts base, ours and theirs are
  1317.      * specified with {@link CanonicalTreeParser}. If any of the parsers is
  1318.      * specified as <code>null</code> then an empty text will be used instead.
  1319.      *
  1320.      * @param base
  1321.      * @param ours
  1322.      * @param theirs
  1323.      * @param attributes
  1324.      * @param strategy
  1325.      *
  1326.      * @return the result of the content merge
  1327.      * @throws BinaryBlobException
  1328.      *             if any of the blobs looks like a binary blob
  1329.      * @throws IOException
  1330.      */
  1331.     private MergeResult<RawText> contentMerge(CanonicalTreeParser base,
  1332.             CanonicalTreeParser ours, CanonicalTreeParser theirs,
  1333.             Attributes[] attributes, ContentMergeStrategy strategy)
  1334.             throws BinaryBlobException, IOException {
  1335.         // TW: The attributes here are used to determine the LFS smudge filter.
  1336.         // Is doing a content merge on LFS items really a good idea??
  1337.         RawText baseText = base == null ? RawText.EMPTY_TEXT
  1338.                 : getRawText(base.getEntryObjectId(), attributes[T_BASE]);
  1339.         RawText ourText = ours == null ? RawText.EMPTY_TEXT
  1340.                 : getRawText(ours.getEntryObjectId(), attributes[T_OURS]);
  1341.         RawText theirsText = theirs == null ? RawText.EMPTY_TEXT
  1342.                 : getRawText(theirs.getEntryObjectId(), attributes[T_THEIRS]);
  1343.         mergeAlgorithm.setContentMergeStrategy(strategy);
  1344.         return mergeAlgorithm.merge(RawTextComparator.DEFAULT, baseText,
  1345.                 ourText, theirsText);
  1346.     }

  1347.     private boolean isIndexDirty() {
  1348.         if (inCore) {
  1349.             return false;
  1350.         }

  1351.         final int modeI = tw.getRawMode(T_INDEX);
  1352.         final int modeO = tw.getRawMode(T_OURS);

  1353.         // Index entry has to match ours to be considered clean
  1354.         final boolean isDirty = nonTree(modeI)
  1355.                 && !(modeO == modeI && tw.idEqual(T_INDEX, T_OURS));
  1356.         if (isDirty) {
  1357.             failingPaths
  1358.                     .put(tw.getPathString(), MergeFailureReason.DIRTY_INDEX);
  1359.         }
  1360.         return isDirty;
  1361.     }

  1362.     private boolean isWorktreeDirty(WorkingTreeIterator work,
  1363.             DirCacheEntry ourDce) throws IOException {
  1364.         if (work == null) {
  1365.             return false;
  1366.         }

  1367.         final int modeF = tw.getRawMode(T_FILE);
  1368.         final int modeO = tw.getRawMode(T_OURS);

  1369.         // Worktree entry has to match ours to be considered clean
  1370.         boolean isDirty;
  1371.         if (ourDce != null) {
  1372.             isDirty = work.isModified(ourDce, true, reader);
  1373.         } else {
  1374.             isDirty = work.isModeDifferent(modeO);
  1375.             if (!isDirty && nonTree(modeF)) {
  1376.                 isDirty = !tw.idEqual(T_FILE, T_OURS);
  1377.             }
  1378.         }

  1379.         // Ignore existing empty directories
  1380.         if (isDirty && modeF == FileMode.TYPE_TREE
  1381.                 && modeO == FileMode.TYPE_MISSING) {
  1382.             isDirty = false;
  1383.         }
  1384.         if (isDirty) {
  1385.             failingPaths.put(tw.getPathString(),
  1386.                     MergeFailureReason.DIRTY_WORKTREE);
  1387.         }
  1388.         return isDirty;
  1389.     }

  1390.     /**
  1391.      * Updates the index after a content merge has happened. If no conflict has
  1392.      * occurred this includes persisting the merged content to the object
  1393.      * database. In case of conflicts this method takes care to write the
  1394.      * correct stages to the index.
  1395.      *
  1396.      * @param base
  1397.      * @param ours
  1398.      * @param theirs
  1399.      * @param result
  1400.      * @param attributes
  1401.      * @throws IOException
  1402.      */
  1403.     private void updateIndex(CanonicalTreeParser base,
  1404.             CanonicalTreeParser ours, CanonicalTreeParser theirs,
  1405.             MergeResult<RawText> result, Attributes attributes)
  1406.             throws IOException {
  1407.         TemporaryBuffer rawMerged = null;
  1408.         try {
  1409.             rawMerged = doMerge(result);
  1410.             File mergedFile = inCore ? null
  1411.                     : writeMergedFile(rawMerged, attributes);
  1412.             if (result.containsConflicts()) {
  1413.                 // A conflict occurred, the file will contain conflict markers
  1414.                 // the index will be populated with the three stages and the
  1415.                 // workdir (if used) contains the halfway merged content.
  1416.                 addConflict(base, ours, theirs);
  1417.                 mergeResults.put(tw.getPathString(), result);
  1418.                 return;
  1419.             }

  1420.             // No conflict occurred, the file will contain fully merged content.
  1421.             // The index will be populated with the new merged version.
  1422.             Instant lastModified = mergedFile == null ? null
  1423.                     : nonNullRepo().getFS().lastModifiedInstant(mergedFile);
  1424.             // Set the mode for the new content. Fall back to REGULAR_FILE if
  1425.             // we can't merge modes of OURS and THEIRS.
  1426.             int newMode = mergeFileModes(tw.getRawMode(0), tw.getRawMode(1),
  1427.                     tw.getRawMode(2));
  1428.             FileMode mode = newMode == FileMode.MISSING.getBits()
  1429.                     ? FileMode.REGULAR_FILE : FileMode.fromBits(newMode);
  1430.             workTreeUpdater.insertToIndex(rawMerged.openInputStream(),
  1431.                     tw.getPathString().getBytes(UTF_8), mode,
  1432.                     DirCacheEntry.STAGE_0, lastModified,
  1433.                     (int) rawMerged.length(),
  1434.                     attributes.get(Constants.ATTR_MERGE));
  1435.         } finally {
  1436.             if (rawMerged != null) {
  1437.                 rawMerged.destroy();
  1438.             }
  1439.         }
  1440.     }

  1441.     /**
  1442.      * Writes merged file content to the working tree.
  1443.      *
  1444.      * @param rawMerged
  1445.      *            the raw merged content
  1446.      * @param attributes
  1447.      *            the files .gitattributes entries
  1448.      * @return the working tree file to which the merged content was written.
  1449.      * @throws IOException
  1450.      */
  1451.     private File writeMergedFile(TemporaryBuffer rawMerged,
  1452.             Attributes attributes)
  1453.             throws IOException {
  1454.         File workTree = nonNullRepo().getWorkTree();
  1455.         FS fs = nonNullRepo().getFS();
  1456.         File of = new File(workTree, tw.getPathString());
  1457.         File parentFolder = of.getParentFile();
  1458.         EolStreamType eol = workTreeUpdater.detectCheckoutStreamType(attributes);
  1459.         if (!fs.exists(parentFolder)) {
  1460.             parentFolder.mkdirs();
  1461.         }
  1462.         workTreeUpdater.updateFileWithContent(rawMerged::openInputStream,
  1463.                 eol, tw.getSmudgeCommand(attributes), of.getPath(), of);
  1464.         return of;
  1465.     }

  1466.     private TemporaryBuffer doMerge(MergeResult<RawText> result)
  1467.             throws IOException {
  1468.         TemporaryBuffer.LocalFile buf = new TemporaryBuffer.LocalFile(
  1469.                 db != null ? nonNullRepo().getDirectory() : null, workTreeUpdater.getInCoreFileSizeLimit());
  1470.         boolean success = false;
  1471.         try {
  1472.             new MergeFormatter().formatMerge(buf, result,
  1473.                     Arrays.asList(commitNames), UTF_8);
  1474.             buf.close();
  1475.             success = true;
  1476.         } finally {
  1477.             if (!success) {
  1478.                 buf.destroy();
  1479.             }
  1480.         }
  1481.         return buf;
  1482.     }

  1483.     /**
  1484.      * Try to merge filemodes. If only ours or theirs have changed the mode
  1485.      * (compared to base) we choose that one. If ours and theirs have equal
  1486.      * modes return that one. If also that is not the case the modes are not
  1487.      * mergeable. Return {@link FileMode#MISSING} int that case.
  1488.      *
  1489.      * @param modeB
  1490.      *            filemode found in BASE
  1491.      * @param modeO
  1492.      *            filemode found in OURS
  1493.      * @param modeT
  1494.      *            filemode found in THEIRS
  1495.      *
  1496.      * @return the merged filemode or {@link FileMode#MISSING} in case of a
  1497.      *         conflict
  1498.      */
  1499.     private int mergeFileModes(int modeB, int modeO, int modeT) {
  1500.         if (modeO == modeT) {
  1501.             return modeO;
  1502.         }
  1503.         if (modeB == modeO) {
  1504.             // Base equal to Ours -> chooses Theirs if that is not missing
  1505.             return (modeT == FileMode.MISSING.getBits()) ? modeO : modeT;
  1506.         }
  1507.         if (modeB == modeT) {
  1508.             // Base equal to Theirs -> chooses Ours if that is not missing
  1509.             return (modeO == FileMode.MISSING.getBits()) ? modeT : modeO;
  1510.         }
  1511.         return FileMode.MISSING.getBits();
  1512.     }

  1513.     private RawText getRawText(ObjectId id,
  1514.             Attributes attributes)
  1515.             throws IOException, BinaryBlobException {
  1516.         if (id.equals(ObjectId.zeroId())) {
  1517.             return new RawText(new byte[]{});
  1518.         }

  1519.         ObjectLoader loader = LfsFactory.getInstance().applySmudgeFilter(
  1520.                 getRepository(), reader.open(id, OBJ_BLOB),
  1521.                 attributes.get(Constants.ATTR_MERGE));
  1522.         int threshold = PackConfig.DEFAULT_BIG_FILE_THRESHOLD;
  1523.         return RawText.load(loader, threshold);
  1524.     }

  1525.     private static boolean nonTree(int mode) {
  1526.         return mode != 0 && !FileMode.TREE.equals(mode);
  1527.     }

  1528.     private static boolean isGitLink(int mode) {
  1529.         return FileMode.GITLINK.equals(mode);
  1530.     }

  1531.     /** {@inheritDoc} */
  1532.     @Override
  1533.     public ObjectId getResultTreeId() {
  1534.         return (resultTree == null) ? null : resultTree.toObjectId();
  1535.     }

  1536.     /**
  1537.      * Set the names of the commits as they would appear in conflict markers
  1538.      *
  1539.      * @param commitNames
  1540.      *            the names of the commits as they would appear in conflict
  1541.      *            markers
  1542.      */
  1543.     public void setCommitNames(String[] commitNames) {
  1544.         this.commitNames = commitNames;
  1545.     }

  1546.     /**
  1547.      * Get the names of the commits as they would appear in conflict markers.
  1548.      *
  1549.      * @return the names of the commits as they would appear in conflict
  1550.      *         markers.
  1551.      */
  1552.     public String[] getCommitNames() {
  1553.         return commitNames;
  1554.     }

  1555.     /**
  1556.      * Get the paths with conflicts. This is a subset of the files listed by
  1557.      * {@link #getModifiedFiles()}
  1558.      *
  1559.      * @return the paths with conflicts. This is a subset of the files listed by
  1560.      *         {@link #getModifiedFiles()}
  1561.      */
  1562.     public List<String> getUnmergedPaths() {
  1563.         return unmergedPaths;
  1564.     }

  1565.     /**
  1566.      * Get the paths of files which have been modified by this merge.
  1567.      *
  1568.      * @return the paths of files which have been modified by this merge. A file
  1569.      *         will be modified if a content-merge works on this path or if the
  1570.      *         merge algorithm decides to take the theirs-version. This is a
  1571.      *         superset of the files listed by {@link #getUnmergedPaths()}.
  1572.      */
  1573.     public List<String> getModifiedFiles() {
  1574.         return workTreeUpdater != null ? workTreeUpdater.getModifiedFiles() : modifiedFiles;
  1575.     }

  1576.     /**
  1577.      * Get a map which maps the paths of files which have to be checked out
  1578.      * because the merge created new fully-merged content for this file into the
  1579.      * index.
  1580.      *
  1581.      * @return a map which maps the paths of files which have to be checked out
  1582.      *         because the merge created new fully-merged content for this file
  1583.      *         into the index. This means: the merge wrote a new stage 0 entry
  1584.      *         for this path.
  1585.      */
  1586.     public Map<String, DirCacheEntry> getToBeCheckedOut() {
  1587.         return workTreeUpdater.getToBeCheckedOut();
  1588.     }

  1589.     /**
  1590.      * Get the mergeResults
  1591.      *
  1592.      * @return the mergeResults
  1593.      */
  1594.     public Map<String, MergeResult<? extends Sequence>> getMergeResults() {
  1595.         return mergeResults;
  1596.     }

  1597.     /**
  1598.      * Get list of paths causing this merge to fail (not stopped because of a
  1599.      * conflict).
  1600.      *
  1601.      * @return lists paths causing this merge to fail (not stopped because of a
  1602.      *         conflict). <code>null</code> is returned if this merge didn't
  1603.      *         fail.
  1604.      */
  1605.     public Map<String, MergeFailureReason> getFailingPaths() {
  1606.         return failingPaths.isEmpty() ? null : failingPaths;
  1607.     }

  1608.     /**
  1609.      * Returns whether this merge failed (i.e. not stopped because of a
  1610.      * conflict)
  1611.      *
  1612.      * @return <code>true</code> if a failure occurred, <code>false</code>
  1613.      *         otherwise
  1614.      */
  1615.     public boolean failed() {
  1616.         return !failingPaths.isEmpty();
  1617.     }

  1618.     /**
  1619.      * Sets the DirCache which shall be used by this merger. If the DirCache is
  1620.      * not set explicitly and if this merger doesn't work in-core, this merger
  1621.      * will implicitly get and lock a default DirCache. If the DirCache is
  1622.      * explicitly set the caller is responsible to lock it in advance. Finally
  1623.      * the merger will call {@link org.eclipse.jgit.dircache.DirCache#commit()}
  1624.      * which requires that the DirCache is locked. If the {@link #mergeImpl()}
  1625.      * returns without throwing an exception the lock will be released. In case
  1626.      * of exceptions the caller is responsible to release the lock.
  1627.      *
  1628.      * @param dc
  1629.      *            the DirCache to set
  1630.      */
  1631.     public void setDirCache(DirCache dc) {
  1632.         this.dircache = dc;
  1633.     }

  1634.     /**
  1635.      * Sets the WorkingTreeIterator to be used by this merger. If no
  1636.      * WorkingTreeIterator is set this merger will ignore the working tree and
  1637.      * fail if a content merge is necessary.
  1638.      * <p>
  1639.      * TODO: enhance WorkingTreeIterator to support write operations. Then this
  1640.      * merger will be able to merge with a different working tree abstraction.
  1641.      *
  1642.      * @param workingTreeIterator
  1643.      *            the workingTreeIt to set
  1644.      */
  1645.     public void setWorkingTreeIterator(WorkingTreeIterator workingTreeIterator) {
  1646.         this.workingTreeIterator = workingTreeIterator;
  1647.     }


  1648.     /**
  1649.      * The resolve conflict way of three way merging
  1650.      *
  1651.      * @param baseTree
  1652.      *            a {@link org.eclipse.jgit.treewalk.AbstractTreeIterator}
  1653.      *            object.
  1654.      * @param headTree
  1655.      *            a {@link org.eclipse.jgit.revwalk.RevTree} object.
  1656.      * @param mergeTree
  1657.      *            a {@link org.eclipse.jgit.revwalk.RevTree} object.
  1658.      * @param ignoreConflicts
  1659.      *            Controls what to do in case a content-merge is done and a
  1660.      *            conflict is detected. The default setting for this should be
  1661.      *            <code>false</code>. In this case the working tree file is
  1662.      *            filled with new content (containing conflict markers) and the
  1663.      *            index is filled with multiple stages containing BASE, OURS and
  1664.      *            THEIRS content. Having such non-0 stages is the sign to git
  1665.      *            tools that there are still conflicts for that path.
  1666.      *            <p>
  1667.      *            If <code>true</code> is specified the behavior is different.
  1668.      *            In case a conflict is detected the working tree file is again
  1669.      *            filled with new content (containing conflict markers). But
  1670.      *            also stage 0 of the index is filled with that content. No
  1671.      *            other stages are filled. Means: there is no conflict on that
  1672.      *            path but the new content (including conflict markers) is
  1673.      *            stored as successful merge result. This is needed in the
  1674.      *            context of {@link org.eclipse.jgit.merge.RecursiveMerger}
  1675.      *            where when determining merge bases we don't want to deal with
  1676.      *            content-merge conflicts.
  1677.      * @return whether the trees merged cleanly
  1678.      * @throws java.io.IOException
  1679.      * @since 3.5
  1680.      */
  1681.     protected boolean mergeTrees(AbstractTreeIterator baseTree,
  1682.             RevTree headTree, RevTree mergeTree, boolean ignoreConflicts)
  1683.             throws IOException {
  1684.         try {
  1685.             workTreeUpdater = inCore ?
  1686.                     WorkTreeUpdater.createInCoreWorkTreeUpdater(db, dircache, getObjectInserter()) :
  1687.                     WorkTreeUpdater.createWorkTreeUpdater(db, dircache);
  1688.             dircache = workTreeUpdater.getLockedDirCache();
  1689.             tw = new NameConflictTreeWalk(db, reader);

  1690.             tw.addTree(baseTree);
  1691.             tw.setHead(tw.addTree(headTree));
  1692.             tw.addTree(mergeTree);
  1693.             DirCacheBuildIterator buildIt = workTreeUpdater.createDirCacheBuildIterator();
  1694.             int dciPos = tw.addTree(buildIt);
  1695.             if (workingTreeIterator != null) {
  1696.                 tw.addTree(workingTreeIterator);
  1697.                 workingTreeIterator.setDirCacheIterator(tw, dciPos);
  1698.             } else {
  1699.                 tw.setFilter(TreeFilter.ANY_DIFF);
  1700.             }

  1701.             if (!mergeTreeWalk(tw, ignoreConflicts)) {
  1702.                 return false;
  1703.             }

  1704.             workTreeUpdater.writeWorkTreeChanges(true);
  1705.             if (getUnmergedPaths().isEmpty() && !failed()) {
  1706.                 WorkTreeUpdater.Result result = workTreeUpdater.writeIndexChanges();
  1707.                 resultTree = result.getTreeId();
  1708.                 modifiedFiles = result.getModifiedFiles();
  1709.                 for (String f : result.getFailedToDelete()) {
  1710.                     failingPaths.put(f, MergeFailureReason.COULD_NOT_DELETE);
  1711.                 }
  1712.                 return result.getFailedToDelete().isEmpty();
  1713.             }
  1714.             resultTree = null;
  1715.             return false;
  1716.         } finally {
  1717.             if(modifiedFiles.isEmpty()) {
  1718.                 modifiedFiles = workTreeUpdater.getModifiedFiles();
  1719.             }
  1720.             workTreeUpdater.close();
  1721.             workTreeUpdater = null;
  1722.         }
  1723.     }

  1724.     /**
  1725.      * Process the given TreeWalk's entries.
  1726.      *
  1727.      * @param treeWalk
  1728.      *            The walk to iterate over.
  1729.      * @param ignoreConflicts
  1730.      *            see
  1731.      *            {@link org.eclipse.jgit.merge.ResolveMerger#mergeTrees(AbstractTreeIterator, RevTree, RevTree, boolean)}
  1732.      * @return Whether the trees merged cleanly.
  1733.      * @throws java.io.IOException
  1734.      * @since 3.5
  1735.      */
  1736.     protected boolean mergeTreeWalk(TreeWalk treeWalk, boolean ignoreConflicts)
  1737.             throws IOException {
  1738.         boolean hasWorkingTreeIterator = tw.getTreeCount() > T_FILE;
  1739.         boolean hasAttributeNodeProvider = treeWalk
  1740.                 .getAttributesNodeProvider() != null;
  1741.         while (treeWalk.next()) {
  1742.             Attributes[] attributes = {NO_ATTRIBUTES, NO_ATTRIBUTES,
  1743.                     NO_ATTRIBUTES};
  1744.             if (hasAttributeNodeProvider) {
  1745.                 attributes[T_BASE] = treeWalk.getAttributes(T_BASE);
  1746.                 attributes[T_OURS] = treeWalk.getAttributes(T_OURS);
  1747.                 attributes[T_THEIRS] = treeWalk.getAttributes(T_THEIRS);
  1748.             }
  1749.             if (!processEntry(
  1750.                     treeWalk.getTree(T_BASE, CanonicalTreeParser.class),
  1751.                     treeWalk.getTree(T_OURS, CanonicalTreeParser.class),
  1752.                     treeWalk.getTree(T_THEIRS, CanonicalTreeParser.class),
  1753.                     treeWalk.getTree(T_INDEX, DirCacheBuildIterator.class),
  1754.                     hasWorkingTreeIterator ? treeWalk.getTree(T_FILE,
  1755.                             WorkingTreeIterator.class) : null,
  1756.                     ignoreConflicts, attributes)) {
  1757.                 workTreeUpdater.revertModifiedFiles();
  1758.                 return false;
  1759.             }
  1760.             if (treeWalk.isSubtree() && enterSubtree) {
  1761.                 treeWalk.enterSubtree();
  1762.             }
  1763.         }
  1764.         return true;
  1765.     }
  1766. }