View Javadoc
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  
17  import static java.nio.charset.StandardCharsets.UTF_8;
18  import static java.time.Instant.EPOCH;
19  import static org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm.HISTOGRAM;
20  import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DIFF_SECTION;
21  import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_ALGORITHM;
22  import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
23  
24  import java.io.Closeable;
25  import java.io.File;
26  import java.io.FileOutputStream;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.io.OutputStream;
30  import java.time.Instant;
31  import java.util.ArrayList;
32  import java.util.Arrays;
33  import java.util.Collections;
34  import java.util.HashMap;
35  import java.util.LinkedList;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.Objects;
39  import java.util.TreeMap;
40  
41  import org.eclipse.jgit.annotations.NonNull;
42  import org.eclipse.jgit.annotations.Nullable;
43  import org.eclipse.jgit.attributes.Attribute;
44  import org.eclipse.jgit.attributes.Attributes;
45  import org.eclipse.jgit.diff.DiffAlgorithm;
46  import org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm;
47  import org.eclipse.jgit.diff.RawText;
48  import org.eclipse.jgit.diff.RawTextComparator;
49  import org.eclipse.jgit.diff.Sequence;
50  import org.eclipse.jgit.dircache.DirCache;
51  import org.eclipse.jgit.dircache.DirCacheBuildIterator;
52  import org.eclipse.jgit.dircache.DirCacheBuilder;
53  import org.eclipse.jgit.dircache.DirCacheCheckout;
54  import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata;
55  import org.eclipse.jgit.dircache.DirCacheCheckout.StreamSupplier;
56  import org.eclipse.jgit.dircache.DirCacheEntry;
57  import org.eclipse.jgit.errors.BinaryBlobException;
58  import org.eclipse.jgit.errors.IndexWriteException;
59  import org.eclipse.jgit.errors.NoWorkTreeException;
60  import org.eclipse.jgit.internal.JGitText;
61  import org.eclipse.jgit.lib.Config;
62  import org.eclipse.jgit.lib.ConfigConstants;
63  import org.eclipse.jgit.lib.Constants;
64  import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
65  import org.eclipse.jgit.lib.FileMode;
66  import org.eclipse.jgit.lib.ObjectId;
67  import org.eclipse.jgit.lib.ObjectInserter;
68  import org.eclipse.jgit.lib.ObjectLoader;
69  import org.eclipse.jgit.lib.ObjectReader;
70  import org.eclipse.jgit.lib.Repository;
71  import org.eclipse.jgit.revwalk.RevTree;
72  import org.eclipse.jgit.storage.pack.PackConfig;
73  import org.eclipse.jgit.submodule.SubmoduleConflict;
74  import org.eclipse.jgit.treewalk.AbstractTreeIterator;
75  import org.eclipse.jgit.treewalk.CanonicalTreeParser;
76  import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
77  import org.eclipse.jgit.treewalk.TreeWalk;
78  import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
79  import org.eclipse.jgit.treewalk.WorkingTreeIterator;
80  import org.eclipse.jgit.treewalk.WorkingTreeOptions;
81  import org.eclipse.jgit.treewalk.filter.TreeFilter;
82  import org.eclipse.jgit.util.FS;
83  import org.eclipse.jgit.util.LfsFactory;
84  import org.eclipse.jgit.util.LfsFactory.LfsInputStream;
85  import org.eclipse.jgit.util.TemporaryBuffer;
86  import org.eclipse.jgit.util.io.EolStreamTypeUtil;
87  
88  /**
89   * A three-way merger performing a content-merge if necessary
90   */
91  public class ResolveMerger extends ThreeWayMerger {
92  
93  	/**
94  	 * Handles work tree updates on both the checkout and the index.
95  	 * <p>
96  	 * You should use a single instance for all of your file changes. In case of
97  	 * an error, make sure your instance is released, and initiate a new one if
98  	 * necessary.
99  	 *
100 	 * @since 6.3.1
101 	 */
102 	protected static class WorkTreeUpdater implements Closeable {
103 
104 		/**
105 		 * The result of writing the index changes.
106 		 */
107 		public static class Result {
108 
109 			private final List<String> modifiedFiles = new LinkedList<>();
110 
111 			private final List<String> failedToDelete = new LinkedList<>();
112 
113 			private ObjectId treeId = null;
114 
115 			/**
116 			 * @return Modified tree ID if any, or null otherwise.
117 			 */
118 			public ObjectId getTreeId() {
119 				return treeId;
120 			}
121 
122 			/**
123 			 * @return Files that couldn't be deleted.
124 			 */
125 			public List<String> getFailedToDelete() {
126 				return failedToDelete;
127 			}
128 
129 			/**
130 			 * @return Files modified during this operation.
131 			 */
132 			public List<String> getModifiedFiles() {
133 				return modifiedFiles;
134 			}
135 		}
136 
137 		Result result = new Result();
138 
139 		/**
140 		 * The repository this handler operates on.
141 		 */
142 		@Nullable
143 		private final Repository repo;
144 
145 		/**
146 		 * Set to true if this operation should work in-memory. The repo's
147 		 * dircache and workingtree are not touched by this method. Eventually
148 		 * needed files are created as temporary files and a new empty,
149 		 * in-memory dircache will be used instead the repo's one. Often used
150 		 * for bare repos where the repo doesn't even have a workingtree and
151 		 * dircache.
152 		 */
153 		private final boolean inCore;
154 
155 		private final ObjectInserter inserter;
156 
157 		private final ObjectReader reader;
158 
159 		private DirCache dirCache;
160 
161 		private boolean implicitDirCache = false;
162 
163 		/**
164 		 * Builder to update the dir cache during this operation.
165 		 */
166 		private DirCacheBuilder builder;
167 
168 		/**
169 		 * The {@link WorkingTreeOptions} are needed to determine line endings
170 		 * for affected files.
171 		 */
172 		private WorkingTreeOptions workingTreeOptions;
173 
174 		/**
175 		 * The size limit (bytes) which controls a file to be stored in
176 		 * {@code Heap} or {@code LocalFile} during the operation.
177 		 */
178 		private int inCoreFileSizeLimit;
179 
180 		/**
181 		 * If the operation has nothing to do for a file but check it out at the
182 		 * end of the operation, it can be added here.
183 		 */
184 		private final Map<String, DirCacheEntry> toBeCheckedOut = new HashMap<>();
185 
186 		/**
187 		 * Files in this list will be deleted from the local copy at the end of
188 		 * the operation.
189 		 */
190 		private final TreeMap<String, File> toBeDeleted = new TreeMap<>();
191 
192 		/**
193 		 * Keeps {@link CheckoutMetadata} for {@link #checkout()}.
194 		 */
195 		private Map<String, CheckoutMetadata> checkoutMetadataByPath;
196 
197 		/**
198 		 * Keeps {@link CheckoutMetadata} for {@link #revertModifiedFiles()}.
199 		 */
200 		private Map<String, CheckoutMetadata> cleanupMetadataByPath;
201 
202 		/**
203 		 * Whether the changes were successfully written.
204 		 */
205 		private boolean indexChangesWritten;
206 
207 		/**
208 		 * @param repo
209 		 *            the {@link Repository}.
210 		 * @param dirCache
211 		 *            if set, use the provided dir cache. Otherwise, use the
212 		 *            default repository one
213 		 */
214 		private WorkTreeUpdater(Repository repo, DirCache dirCache) {
215 			this.repo = repo;
216 			this.dirCache = dirCache;
217 
218 			this.inCore = false;
219 			this.inserter = repo.newObjectInserter();
220 			this.reader = inserter.newReader();
221 			Config config = repo.getConfig();
222 			this.workingTreeOptions = config.get(WorkingTreeOptions.KEY);
223 			this.inCoreFileSizeLimit = getInCoreFileSizeLimit(config);
224 			this.checkoutMetadataByPath = new HashMap<>();
225 			this.cleanupMetadataByPath = new HashMap<>();
226 		}
227 
228 		/**
229 		 * Creates a new {@link WorkTreeUpdater} for the given repository.
230 		 *
231 		 * @param repo
232 		 *            the {@link Repository}.
233 		 * @param dirCache
234 		 *            if set, use the provided dir cache. Otherwise, use the
235 		 *            default repository one
236 		 * @return the {@link WorkTreeUpdater}.
237 		 */
238 		public static WorkTreeUpdater createWorkTreeUpdater(Repository repo,
239 				DirCache dirCache) {
240 			return new WorkTreeUpdater(repo, dirCache);
241 		}
242 
243 		/**
244 		 * @param repo
245 		 *            the {@link Repository}.
246 		 * @param dirCache
247 		 *            if set, use the provided dir cache. Otherwise, creates a
248 		 *            new one
249 		 * @param oi
250 		 *            to use for writing the modified objects with.
251 		 */
252 		private WorkTreeUpdater(Repository repo, DirCache dirCache,
253 				ObjectInserter oi) {
254 			this.repo = repo;
255 			this.dirCache = dirCache;
256 			this.inserter = oi;
257 
258 			this.inCore = true;
259 			this.reader = oi.newReader();
260 			if (repo != null) {
261 				this.inCoreFileSizeLimit = getInCoreFileSizeLimit(
262 						repo.getConfig());
263 			}
264 		}
265 
266 		/**
267 		 * Creates a new {@link WorkTreeUpdater} that works in memory only.
268 		 *
269 		 * @param repo
270 		 *            the {@link Repository}.
271 		 * @param dirCache
272 		 *            if set, use the provided dir cache. Otherwise, creates a
273 		 *            new one
274 		 * @param oi
275 		 *            to use for writing the modified objects with.
276 		 * @return the {@link WorkTreeUpdater}
277 		 */
278 		public static WorkTreeUpdater createInCoreWorkTreeUpdater(
279 				Repository repo, DirCache dirCache, ObjectInserter oi) {
280 			return new WorkTreeUpdater(repo, dirCache, oi);
281 		}
282 
283 		private static int getInCoreFileSizeLimit(Config config) {
284 			return config.getInt(ConfigConstants.CONFIG_MERGE_SECTION,
285 					ConfigConstants.CONFIG_KEY_IN_CORE_LIMIT, 10 << 20);
286 		}
287 
288 		/**
289 		 * Gets the size limit for in-core files in this config.
290 		 *
291 		 * @return the size
292 		 */
293 		public int getInCoreFileSizeLimit() {
294 			return inCoreFileSizeLimit;
295 		}
296 
297 		/**
298 		 * Gets dir cache for the repo. Locked if not inCore.
299 		 *
300 		 * @return the result dir cache
301 		 * @throws IOException
302 		 *             is case the dir cache cannot be read
303 		 */
304 		public DirCache getLockedDirCache() throws IOException {
305 			if (dirCache == null) {
306 				implicitDirCache = true;
307 				if (inCore) {
308 					dirCache = DirCache.newInCore();
309 				} else {
310 					dirCache = nonNullRepo().lockDirCache();
311 				}
312 			}
313 			if (builder == null) {
314 				builder = dirCache.builder();
315 			}
316 			return dirCache;
317 		}
318 
319 		/**
320 		 * Creates a {@link DirCacheBuildIterator} for the builder of this
321 		 * {@link WorkTreeUpdater}.
322 		 *
323 		 * @return the {@link DirCacheBuildIterator}
324 		 */
325 		public DirCacheBuildIterator createDirCacheBuildIterator() {
326 			return new DirCacheBuildIterator(builder);
327 		}
328 
329 		/**
330 		 * Writes the changes to the working tree (but not to the index).
331 		 *
332 		 * @param shouldCheckoutTheirs
333 		 *            before committing the changes
334 		 * @throws IOException
335 		 *             if any of the writes fail
336 		 */
337 		public void writeWorkTreeChanges(boolean shouldCheckoutTheirs)
338 				throws IOException {
339 			handleDeletedFiles();
340 
341 			if (inCore) {
342 				builder.finish();
343 				return;
344 			}
345 			if (shouldCheckoutTheirs) {
346 				// No problem found. The only thing left to be done is to
347 				// check out all files from "theirs" which have been selected to
348 				// go into the new index.
349 				checkout();
350 			}
351 
352 			// All content operations are successfully done. If we can now write
353 			// the
354 			// new index we are on quite safe ground. Even if the checkout of
355 			// files coming from "theirs" fails the user can work around such
356 			// failures by checking out the index again.
357 			if (!builder.commit()) {
358 				revertModifiedFiles();
359 				throw new IndexWriteException();
360 			}
361 		}
362 
363 		/**
364 		 * Writes the changes to the index.
365 		 *
366 		 * @return the {@link Result} of the operation.
367 		 * @throws IOException
368 		 *             if any of the writes fail
369 		 */
370 		public Result writeIndexChanges() throws IOException {
371 			result.treeId = getLockedDirCache().writeTree(inserter);
372 			indexChangesWritten = true;
373 			return result;
374 		}
375 
376 		/**
377 		 * Adds a {@link DirCacheEntry} for direct checkout and remembers its
378 		 * {@link CheckoutMetadata}.
379 		 *
380 		 * @param path
381 		 *            of the entry
382 		 * @param entry
383 		 *            to add
384 		 * @param cleanupStreamType
385 		 *            to use for the cleanup metadata
386 		 * @param cleanupSmudgeCommand
387 		 *            to use for the cleanup metadata
388 		 * @param checkoutStreamType
389 		 *            to use for the checkout metadata
390 		 * @param checkoutSmudgeCommand
391 		 *            to use for the checkout metadata
392 		 */
393 		public void addToCheckout(String path, DirCacheEntry entry,
394 				EolStreamType cleanupStreamType, String cleanupSmudgeCommand,
395 				EolStreamType checkoutStreamType,
396 				String checkoutSmudgeCommand) {
397 			if (entry != null) {
398 				// In some cases, we just want to add the metadata.
399 				toBeCheckedOut.put(path, entry);
400 			}
401 			addCheckoutMetadata(cleanupMetadataByPath, path, cleanupStreamType,
402 					cleanupSmudgeCommand);
403 			addCheckoutMetadata(checkoutMetadataByPath, path,
404 					checkoutStreamType, checkoutSmudgeCommand);
405 		}
406 
407 		/**
408 		 * Gets a map which maps the paths of files which have to be checked out
409 		 * because the operation created new fully-merged content for this file
410 		 * into the index.
411 		 * <p>
412 		 * This means: the operation wrote a new stage 0 entry for this path.
413 		 * </p>
414 		 *
415 		 * @return the map
416 		 */
417 		public Map<String, DirCacheEntry> getToBeCheckedOut() {
418 			return toBeCheckedOut;
419 		}
420 
421 		/**
422 		 * Remembers the given file to be deleted.
423 		 * <p>
424 		 * Note the actual deletion is only done in
425 		 * {@link #writeWorkTreeChanges}.
426 		 *
427 		 * @param path
428 		 *            of the file to be deleted
429 		 * @param file
430 		 *            to be deleted
431 		 * @param streamType
432 		 *            to use for cleanup metadata
433 		 * @param smudgeCommand
434 		 *            to use for cleanup metadata
435 		 */
436 		public void deleteFile(String path, File file, EolStreamType streamType,
437 				String smudgeCommand) {
438 			toBeDeleted.put(path, file);
439 			if (file != null && file.isFile()) {
440 				addCheckoutMetadata(cleanupMetadataByPath, path, streamType,
441 						smudgeCommand);
442 			}
443 		}
444 
445 		/**
446 		 * Remembers the {@link CheckoutMetadata} for the given path; it may be
447 		 * needed in {@link #checkout()} or in {@link #revertModifiedFiles()}.
448 		 *
449 		 * @param map
450 		 *            to add the metadata to
451 		 * @param path
452 		 *            of the current node
453 		 * @param streamType
454 		 *            to use for the metadata
455 		 * @param smudgeCommand
456 		 *            to use for the metadata
457 		 */
458 		private void addCheckoutMetadata(Map<String, CheckoutMetadata> map,
459 				String path, EolStreamType streamType, String smudgeCommand) {
460 			if (inCore || map == null) {
461 				return;
462 			}
463 			map.put(path, new CheckoutMetadata(streamType, smudgeCommand));
464 		}
465 
466 		/**
467 		 * Detects if CRLF conversion has been configured.
468 		 * <p>
469 		 * </p>
470 		 * See {@link EolStreamTypeUtil#detectStreamType} for more info.
471 		 *
472 		 * @param attributes
473 		 *            of the file for which the type is to be detected
474 		 * @return the detected type
475 		 */
476 		public EolStreamType detectCheckoutStreamType(Attributes attributes) {
477 			if (inCore) {
478 				return null;
479 			}
480 			return EolStreamTypeUtil.detectStreamType(OperationType.CHECKOUT_OP,
481 					workingTreeOptions, attributes);
482 		}
483 
484 		private void handleDeletedFiles() {
485 			// Iterate in reverse so that "folder/file" is deleted before
486 			// "folder". Otherwise, this could result in a failing path because
487 			// of a non-empty directory, for which delete() would fail.
488 			for (String path : toBeDeleted.descendingKeySet()) {
489 				File file = inCore ? null : toBeDeleted.get(path);
490 				if (file != null && !file.delete()) {
491 					if (!file.isDirectory()) {
492 						result.failedToDelete.add(path);
493 					}
494 				}
495 			}
496 		}
497 
498 		/**
499 		 * Marks the given path as modified in the operation.
500 		 *
501 		 * @param path
502 		 *            to mark as modified
503 		 */
504 		public void markAsModified(String path) {
505 			result.modifiedFiles.add(path);
506 		}
507 
508 		/**
509 		 * Gets the list of files which were modified in this operation.
510 		 *
511 		 * @return the list
512 		 */
513 		public List<String> getModifiedFiles() {
514 			return result.modifiedFiles;
515 		}
516 
517 		private void checkout() throws NoWorkTreeException, IOException {
518 			for (Map.Entry<String, DirCacheEntry> entry : toBeCheckedOut
519 					.entrySet()) {
520 				DirCacheEntry dirCacheEntry = entry.getValue();
521 				if (dirCacheEntry.getFileMode() == FileMode.GITLINK) {
522 					new File(nonNullRepo().getWorkTree(), entry.getKey())
523 							.mkdirs();
524 				} else {
525 					DirCacheCheckout.checkoutEntry(repo, dirCacheEntry, reader,
526 							false, checkoutMetadataByPath.get(entry.getKey()),
527 							workingTreeOptions);
528 					result.modifiedFiles.add(entry.getKey());
529 				}
530 			}
531 		}
532 
533 		/**
534 		 * Reverts any uncommitted changes in the worktree. We know that for all
535 		 * modified files the old content was in the old index and the index
536 		 * contained only stage 0. In case of inCore operation just clear the
537 		 * history of modified files.
538 		 *
539 		 * @throws IOException
540 		 *             in case the cleaning up failed
541 		 */
542 		public void revertModifiedFiles() throws IOException {
543 			if (inCore) {
544 				result.modifiedFiles.clear();
545 				return;
546 			}
547 			if (indexChangesWritten) {
548 				return;
549 			}
550 			for (String path : result.modifiedFiles) {
551 				DirCacheEntry entry = dirCache.getEntry(path);
552 				if (entry != null) {
553 					DirCacheCheckout.checkoutEntry(repo, entry, reader, false,
554 							cleanupMetadataByPath.get(path),
555 							workingTreeOptions);
556 				}
557 			}
558 		}
559 
560 		@Override
561 		public void close() throws IOException {
562 			if (implicitDirCache) {
563 				dirCache.unlock();
564 			}
565 		}
566 
567 		/**
568 		 * Updates the file in the checkout with the given content.
569 		 *
570 		 * @param inputStream
571 		 *            the content to be updated
572 		 * @param streamType
573 		 *            for parsing the content
574 		 * @param smudgeCommand
575 		 *            for formatting the content
576 		 * @param path
577 		 *            of the file to be updated
578 		 * @param file
579 		 *            to be updated
580 		 * @throws IOException
581 		 *             if the file cannot be updated
582 		 */
583 		public void updateFileWithContent(StreamSupplier inputStream,
584 				EolStreamType streamType, String smudgeCommand, String path,
585 				File file) throws IOException {
586 			if (inCore) {
587 				return;
588 			}
589 			CheckoutMetadata metadata = new CheckoutMetadata(streamType,
590 					smudgeCommand);
591 
592 			try (OutputStream outputStream = new FileOutputStream(file)) {
593 				DirCacheCheckout.getContent(repo, path, metadata, inputStream,
594 						workingTreeOptions, outputStream);
595 			}
596 		}
597 
598 		/**
599 		 * Creates a path with the given content, and adds it to the specified
600 		 * stage to the index builder.
601 		 *
602 		 * @param input
603 		 *            the content to be updated
604 		 * @param path
605 		 *            of the file to be updated
606 		 * @param fileMode
607 		 *            of the modified file
608 		 * @param entryStage
609 		 *            of the new entry
610 		 * @param lastModified
611 		 *            instant of the modified file
612 		 * @param len
613 		 *            of the content
614 		 * @param lfsAttribute
615 		 *            for checking for LFS enablement
616 		 * @return the entry which was added to the index
617 		 * @throws IOException
618 		 *             if inserting the content fails
619 		 */
620 		public DirCacheEntry insertToIndex(InputStream input, byte[] path,
621 				FileMode fileMode, int entryStage, Instant lastModified,
622 				int len, Attribute lfsAttribute) throws IOException {
623 			return addExistingToIndex(insertResult(input, lfsAttribute, len),
624 					path, fileMode, entryStage, lastModified, len);
625 		}
626 
627 		/**
628 		 * Adds a path with the specified stage to the index builder.
629 		 *
630 		 * @param objectId
631 		 *            of the existing object to add
632 		 * @param path
633 		 *            of the modified file
634 		 * @param fileMode
635 		 *            of the modified file
636 		 * @param entryStage
637 		 *            of the new entry
638 		 * @param lastModified
639 		 *            instant of the modified file
640 		 * @param len
641 		 *            of the modified file content
642 		 * @return the entry which was added to the index
643 		 */
644 		public DirCacheEntry addExistingToIndex(ObjectId objectId, byte[] path,
645 				FileMode fileMode, int entryStage, Instant lastModified,
646 				int len) {
647 			DirCacheEntry dce = new DirCacheEntry(path, entryStage);
648 			dce.setFileMode(fileMode);
649 			if (lastModified != null) {
650 				dce.setLastModified(lastModified);
651 			}
652 			dce.setLength(inCore ? 0 : len);
653 			dce.setObjectId(objectId);
654 			builder.add(dce);
655 			return dce;
656 		}
657 
658 		private ObjectId insertResult(InputStream input, Attribute lfsAttribute,
659 				long length) throws IOException {
660 			try (LfsInputStream is = LfsFactory.getInstance()
661 					.applyCleanFilter(repo, input, length, lfsAttribute)) {
662 				return inserter.insert(OBJ_BLOB, is.getLength(), is);
663 			}
664 		}
665 
666 		/**
667 		 * Gets the non-null repository instance of this
668 		 * {@link WorkTreeUpdater}.
669 		 *
670 		 * @return non-null repository instance
671 		 * @throws NullPointerException
672 		 *             if the handler was constructed without a repository.
673 		 */
674 		@NonNull
675 		private Repository nonNullRepo() throws NullPointerException {
676 			return Objects.requireNonNull(repo,
677 					() -> JGitText.get().repositoryIsRequired);
678 		}
679 	}
680 
681 	/**
682 	 * If the merge fails (means: not stopped because of unresolved conflicts)
683 	 * this enum is used to explain why it failed
684 	 */
685 	public enum MergeFailureReason {
686 		/** the merge failed because of a dirty index */
687 		DIRTY_INDEX,
688 		/** the merge failed because of a dirty workingtree */
689 		DIRTY_WORKTREE,
690 		/** the merge failed because of a file could not be deleted */
691 		COULD_NOT_DELETE
692 	}
693 
694 	/**
695 	 * The tree walk which we'll iterate over to merge entries.
696 	 *
697 	 * @since 3.4
698 	 */
699 	protected NameConflictTreeWalk tw;
700 
701 	/**
702 	 * string versions of a list of commit SHA1s
703 	 *
704 	 * @since 3.0
705 	 */
706 	protected String[] commitNames;
707 
708 	/**
709 	 * Index of the base tree within the {@link #tw tree walk}.
710 	 *
711 	 * @since 3.4
712 	 */
713 	protected static final int T_BASE = 0;
714 
715 	/**
716 	 * Index of our tree in withthe {@link #tw tree walk}.
717 	 *
718 	 * @since 3.4
719 	 */
720 	protected static final int T_OURS = 1;
721 
722 	/**
723 	 * Index of their tree within the {@link #tw tree walk}.
724 	 *
725 	 * @since 3.4
726 	 */
727 	protected static final int T_THEIRS = 2;
728 
729 	/**
730 	 * Index of the index tree within the {@link #tw tree walk}.
731 	 *
732 	 * @since 3.4
733 	 */
734 	protected static final int T_INDEX = 3;
735 
736 	/**
737 	 * Index of the working directory tree within the {@link #tw tree walk}.
738 	 *
739 	 * @since 3.4
740 	 */
741 	protected static final int T_FILE = 4;
742 
743 	/**
744 	 * Handler for repository I/O actions.
745 	 *
746 	 * @since 6.3
747 	 */
748 	protected WorkTreeUpdater workTreeUpdater;
749 
750 	/**
751 	 * merge result as tree
752 	 *
753 	 * @since 3.0
754 	 */
755 	protected ObjectId resultTree;
756 
757 	/**
758 	 * Files modified during this operation. Note this list is only updated after a successful write.
759 	 */
760 	protected List<String> modifiedFiles = new ArrayList<>();
761 
762 	/**
763 	 * Paths that could not be merged by this merger because of an unsolvable
764 	 * conflict.
765 	 *
766 	 * @since 3.4
767 	 */
768 	protected List<String> unmergedPaths = new ArrayList<>();
769 
770 	/**
771 	 * Low-level textual merge results. Will be passed on to the callers in case
772 	 * of conflicts.
773 	 *
774 	 * @since 3.4
775 	 */
776 	protected Map<String, MergeResult<? extends Sequence>> mergeResults = new HashMap<>();
777 
778 	/**
779 	 * Paths for which the merge failed altogether.
780 	 *
781 	 * @since 3.4
782 	 */
783 	protected Map<String, MergeFailureReason> failingPaths = new HashMap<>();
784 
785 	/**
786 	 * Updated as we merge entries of the tree walk. Tells us whether we should
787 	 * recurse into the entry if it is a subtree.
788 	 *
789 	 * @since 3.4
790 	 */
791 	protected boolean enterSubtree;
792 
793 	/**
794 	 * Set to true if this merge should work in-memory. The repos dircache and
795 	 * workingtree are not touched by this method. Eventually needed files are
796 	 * created as temporary files and a new empty, in-memory dircache will be
797 	 * used instead the repo's one. Often used for bare repos where the repo
798 	 * doesn't even have a workingtree and dircache.
799 	 * @since 3.0
800 	 */
801 	protected boolean inCore;
802 
803 	/**
804 	 * Directory cache
805 	 * @since 3.0
806 	 */
807 	protected DirCache dircache;
808 
809 	/**
810 	 * The iterator to access the working tree. If set to <code>null</code> this
811 	 * merger will not touch the working tree.
812 	 * @since 3.0
813 	 */
814 	protected WorkingTreeIterator workingTreeIterator;
815 
816 	/**
817 	 * our merge algorithm
818 	 * @since 3.0
819 	 */
820 	protected MergeAlgorithm mergeAlgorithm;
821 
822 	/**
823 	 * The {@link ContentMergeStrategy} to use for "resolve" and "recursive"
824 	 * merges.
825 	 */
826 	@NonNull
827 	private ContentMergeStrategy contentStrategy = ContentMergeStrategy.CONFLICT;
828 
829 	private static MergeAlgorithm getMergeAlgorithm(Config config) {
830 		SupportedAlgorithm diffAlg = config.getEnum(
831 				CONFIG_DIFF_SECTION, null, CONFIG_KEY_ALGORITHM,
832 				HISTOGRAM);
833 		return new MergeAlgorithm(DiffAlgorithm.getAlgorithm(diffAlg));
834 	}
835 
836 	private static String[] defaultCommitNames() {
837 		return new String[]{"BASE", "OURS", "THEIRS"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
838 	}
839 
840 	private static final Attributes NO_ATTRIBUTES = new Attributes();
841 
842 	/**
843 	 * Constructor for ResolveMerger.
844 	 *
845 	 * @param local
846 	 *            the {@link org.eclipse.jgit.lib.Repository}.
847 	 * @param inCore
848 	 *            a boolean.
849 	 */
850 	protected ResolveMerger(Repository local, boolean inCore) {
851 		super(local);
852 		Config config = local.getConfig();
853 		mergeAlgorithm = getMergeAlgorithm(config);
854 		commitNames = defaultCommitNames();
855 		this.inCore = inCore;
856 	}
857 
858 	/**
859 	 * Constructor for ResolveMerger.
860 	 *
861 	 * @param local
862 	 *            the {@link org.eclipse.jgit.lib.Repository}.
863 	 */
864 	protected ResolveMerger(Repository local) {
865 		this(local, false);
866 	}
867 
868 	/**
869 	 * Constructor for ResolveMerger.
870 	 *
871 	 * @param inserter
872 	 *            an {@link org.eclipse.jgit.lib.ObjectInserter} object.
873 	 * @param config
874 	 *            the repository configuration
875 	 * @since 4.8
876 	 */
877 	protected ResolveMerger(ObjectInserter inserter, Config config) {
878 		super(inserter);
879 		mergeAlgorithm = getMergeAlgorithm(config);
880 		commitNames = defaultCommitNames();
881 		inCore = true;
882 	}
883 
884 	/**
885 	 * Retrieves the content merge strategy for content conflicts.
886 	 *
887 	 * @return the {@link ContentMergeStrategy} in effect
888 	 * @since 5.12
889 	 */
890 	@NonNull
891 	public ContentMergeStrategy getContentMergeStrategy() {
892 		return contentStrategy;
893 	}
894 
895 	/**
896 	 * Sets the content merge strategy for content conflicts.
897 	 *
898 	 * @param strategy
899 	 *            {@link ContentMergeStrategy} to use
900 	 * @since 5.12
901 	 */
902 	public void setContentMergeStrategy(ContentMergeStrategy strategy) {
903 		contentStrategy = strategy == null ? ContentMergeStrategy.CONFLICT
904 				: strategy;
905 	}
906 
907 	/** {@inheritDoc} */
908 	@Override
909 	protected boolean mergeImpl() throws IOException {
910 		return mergeTrees(mergeBase(), sourceTrees[0], sourceTrees[1],
911 				false);
912 	}
913 
914 	/**
915 	 * adds a new path with the specified stage to the index builder
916 	 *
917 	 * @param path
918 	 * @param p
919 	 * @param stage
920 	 * @param lastMod
921 	 * @param len
922 	 * @return the entry which was added to the index
923 	 */
924 	private DirCacheEntry add(byte[] path, CanonicalTreeParser p, int stage,
925 			Instant lastMod, long len) {
926 		if (p != null && !p.getEntryFileMode().equals(FileMode.TREE)) {
927 			return workTreeUpdater.addExistingToIndex(p.getEntryObjectId(), path,
928 					p.getEntryFileMode(), stage,
929 					lastMod, (int) len);
930 		}
931 		return null;
932 	}
933 
934 	/**
935 	 * Adds the conflict stages for the current path of {@link #tw} to the index
936 	 * builder and returns the "theirs" stage; if present.
937 	 *
938 	 * @param base
939 	 *            of the conflict
940 	 * @param ours
941 	 *            of the conflict
942 	 * @param theirs
943 	 *            of the conflict
944 	 * @return the {@link DirCacheEntry} for the "theirs" stage, or {@code null}
945 	 */
946 	private DirCacheEntry addConflict(CanonicalTreeParser base,
947 			CanonicalTreeParser ours, CanonicalTreeParser theirs) {
948 		add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0);
949 		add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
950 		return add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 0);
951 	}
952 
953 	/**
954 	 * adds a entry to the index builder which is a copy of the specified
955 	 * DirCacheEntry
956 	 *
957 	 * @param e
958 	 *            the entry which should be copied
959 	 *
960 	 * @return the entry which was added to the index
961 	 */
962 	private DirCacheEntry keep(DirCacheEntry e) {
963 		return workTreeUpdater.addExistingToIndex(e.getObjectId(), e.getRawPath(), e.getFileMode(),
964 				e.getStage(), e.getLastModifiedInstant(), e.getLength());
965 	}
966 
967 	/**
968 	 * Adds a {@link DirCacheEntry} for direct checkout and remembers its
969 	 * {@link CheckoutMetadata}.
970 	 *
971 	 * @param path
972 	 *            of the entry
973 	 * @param entry
974 	 *            to add
975 	 * @param attributes
976 	 *            the {@link Attributes} of the trees
977 	 * @throws IOException
978 	 *             if the {@link CheckoutMetadata} cannot be determined
979 	 * @since 6.1
980 	 */
981 	protected void addToCheckout(String path, DirCacheEntry entry,
982 			Attributes[] attributes)
983 			throws IOException {
984 		EolStreamType cleanupStreamType = workTreeUpdater.detectCheckoutStreamType(attributes[T_OURS]);
985 		String cleanupSmudgeCommand = tw.getSmudgeCommand(attributes[T_OURS]);
986 		EolStreamType checkoutStreamType = workTreeUpdater.detectCheckoutStreamType(attributes[T_THEIRS]);
987 		String checkoutSmudgeCommand = tw.getSmudgeCommand(attributes[T_THEIRS]);
988 		workTreeUpdater.addToCheckout(path, entry, cleanupStreamType, cleanupSmudgeCommand,
989 				checkoutStreamType, checkoutSmudgeCommand);
990 	}
991 
992 	/**
993 	 * Remember a path for deletion, and remember its {@link CheckoutMetadata}
994 	 * in case it has to be restored in the cleanUp.
995 	 *
996 	 * @param path
997 	 *            of the entry
998 	 * @param isFile
999 	 *            whether it is a file
1000 	 * @param attributes
1001 	 *            to use for determining the {@link CheckoutMetadata}
1002 	 * @throws IOException
1003 	 *             if the {@link CheckoutMetadata} cannot be determined
1004 	 * @since 5.1
1005 	 */
1006 	protected void addDeletion(String path, boolean isFile,
1007 			Attributes attributes) throws IOException {
1008 		if (db == null || nonNullRepo().isBare() || !isFile)
1009 			return;
1010 
1011 		File file = new File(nonNullRepo().getWorkTree(), path);
1012 		EolStreamType streamType = workTreeUpdater.detectCheckoutStreamType(attributes);
1013 		String smudgeCommand = tw.getSmudgeCommand(attributes);
1014 		workTreeUpdater.deleteFile(path, file, streamType, smudgeCommand);
1015 	}
1016 
1017 	/**
1018 	 * Processes one path and tries to merge taking git attributes in account.
1019 	 * This method will do all trivial (not content) merges and will also detect
1020 	 * if a merge will fail. The merge will fail when one of the following is
1021 	 * true
1022 	 * <ul>
1023 	 * <li>the index entry does not match the entry in ours. When merging one
1024 	 * branch into the current HEAD, ours will point to HEAD and theirs will
1025 	 * point to the other branch. It is assumed that the index matches the HEAD
1026 	 * because it will only not match HEAD if it was populated before the merge
1027 	 * operation. But the merge commit should not accidentally contain
1028 	 * modifications done before the merge. Check the <a href=
1029 	 * "http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html#_3_way_merge"
1030 	 * >git read-tree</a> documentation for further explanations.</li>
1031 	 * <li>A conflict was detected and the working-tree file is dirty. When a
1032 	 * conflict is detected the content-merge algorithm will try to write a
1033 	 * merged version into the working-tree. If the file is dirty we would
1034 	 * override unsaved data.</li>
1035 	 * </ul>
1036 	 *
1037 	 * @param base
1038 	 *            the common base for ours and theirs
1039 	 * @param ours
1040 	 *            the ours side of the merge. When merging a branch into the
1041 	 *            HEAD ours will point to HEAD
1042 	 * @param theirs
1043 	 *            the theirs side of the merge. When merging a branch into the
1044 	 *            current HEAD theirs will point to the branch which is merged
1045 	 *            into HEAD.
1046 	 * @param index
1047 	 *            the index entry
1048 	 * @param work
1049 	 *            the file in the working tree
1050 	 * @param ignoreConflicts
1051 	 *            see
1052 	 *            {@link org.eclipse.jgit.merge.ResolveMerger#mergeTrees(AbstractTreeIterator, RevTree, RevTree, boolean)}
1053 	 * @param attributes
1054 	 *            the {@link Attributes} for the three trees
1055 	 * @return <code>false</code> if the merge will fail because the index entry
1056 	 *         didn't match ours or the working-dir file was dirty and a
1057 	 *         conflict occurred
1058 	 * @throws java.io.IOException
1059 	 * @since 6.1
1060 	 */
1061 	protected boolean processEntry(CanonicalTreeParser base,
1062 			CanonicalTreeParser ours, CanonicalTreeParser theirs,
1063 			DirCacheBuildIterator index, WorkingTreeIterator work,
1064 			boolean ignoreConflicts, Attributes[] attributes)
1065 			throws IOException {
1066 		enterSubtree = true;
1067 		final int modeO = tw.getRawMode(T_OURS);
1068 		final int modeT = tw.getRawMode(T_THEIRS);
1069 		final int modeB = tw.getRawMode(T_BASE);
1070 		boolean gitLinkMerging = isGitLink(modeO) || isGitLink(modeT)
1071 				|| isGitLink(modeB);
1072 		if (modeO == 0 && modeT == 0 && modeB == 0) {
1073 			// File is either untracked or new, staged but uncommitted
1074 			return true;
1075 		}
1076 
1077 		if (isIndexDirty()) {
1078 			return false;
1079 		}
1080 
1081 		DirCacheEntry ourDce = null;
1082 
1083 		if (index == null || index.getDirCacheEntry() == null) {
1084 			// create a fake DCE, but only if ours is valid. ours is kept only
1085 			// in case it is valid, so a null ourDce is ok in all other cases.
1086 			if (nonTree(modeO)) {
1087 				ourDce = new DirCacheEntry(tw.getRawPath());
1088 				ourDce.setObjectId(tw.getObjectId(T_OURS));
1089 				ourDce.setFileMode(tw.getFileMode(T_OURS));
1090 			}
1091 		} else {
1092 			ourDce = index.getDirCacheEntry();
1093 		}
1094 
1095 		if (nonTree(modeO) && nonTree(modeT) && tw.idEqual(T_OURS, T_THEIRS)) {
1096 			// OURS and THEIRS have equal content. Check the file mode
1097 			if (modeO == modeT) {
1098 				// content and mode of OURS and THEIRS are equal: it doesn't
1099 				// matter which one we choose. OURS is chosen. Since the index
1100 				// is clean (the index matches already OURS) we can keep the existing one
1101 				keep(ourDce);
1102 				// no checkout needed!
1103 				return true;
1104 			}
1105 			// same content but different mode on OURS and THEIRS.
1106 			// Try to merge the mode and report an error if this is
1107 			// not possible.
1108 			int newMode = mergeFileModes(modeB, modeO, modeT);
1109 			if (newMode != FileMode.MISSING.getBits()) {
1110 				if (newMode == modeO) {
1111 					// ours version is preferred
1112 					keep(ourDce);
1113 				} else {
1114 					// the preferred version THEIRS has a different mode
1115 					// than ours. Check it out!
1116 					if (isWorktreeDirty(work, ourDce)) {
1117 						return false;
1118 					}
1119 					// we know about length and lastMod only after we have
1120 					// written the new content.
1121 					// This will happen later. Set these values to 0 for know.
1122 					DirCacheEntry e = add(tw.getRawPath(), theirs,
1123 							DirCacheEntry.STAGE_0, EPOCH, 0);
1124 					addToCheckout(tw.getPathString(), e, attributes);
1125 				}
1126 				return true;
1127 			}
1128 			if (!ignoreConflicts) {
1129 				// FileModes are not mergeable. We found a conflict on modes.
1130 				// For conflicting entries we don't know lastModified and
1131 				// length.
1132 				// This path can be skipped on ignoreConflicts, so the caller
1133 				// could use virtual commit.
1134 				addConflict(base, ours, theirs);
1135 				unmergedPaths.add(tw.getPathString());
1136 				mergeResults.put(tw.getPathString(),
1137 						new MergeResult<>(Collections.emptyList()));
1138 			}
1139 			return true;
1140 		}
1141 
1142 		if (modeB == modeT && tw.idEqual(T_BASE, T_THEIRS)) {
1143 			// THEIRS was not changed compared to BASE. All changes must be in
1144 			// OURS. OURS is chosen. We can keep the existing entry.
1145 			if (ourDce != null) {
1146 				keep(ourDce);
1147 			}
1148 			// no checkout needed!
1149 			return true;
1150 		}
1151 
1152 		if (modeB == modeO && tw.idEqual(T_BASE, T_OURS)) {
1153 			// OURS was not changed compared to BASE. All changes must be in
1154 			// THEIRS. THEIRS is chosen.
1155 
1156 			// Check worktree before checking out THEIRS
1157 			if (isWorktreeDirty(work, ourDce)) {
1158 				return false;
1159 			}
1160 			if (nonTree(modeT)) {
1161 				// we know about length and lastMod only after we have written
1162 				// the new content.
1163 				// This will happen later. Set these values to 0 for know.
1164 				DirCacheEntry e = add(tw.getRawPath(), theirs,
1165 						DirCacheEntry.STAGE_0, EPOCH, 0);
1166 				if (e != null) {
1167 					addToCheckout(tw.getPathString(), e, attributes);
1168 				}
1169 				return true;
1170 			}
1171 			// we want THEIRS ... but THEIRS contains a folder or the
1172 			// deletion of the path. Delete what's in the working tree,
1173 			// which we know to be clean.
1174 			if (tw.getTreeCount() > T_FILE && tw.getRawMode(T_FILE) == 0) {
1175 				// Not present in working tree, so nothing to delete
1176 				return true;
1177 			}
1178 			if (modeT != 0 && modeT == modeB) {
1179 				// Base, ours, and theirs all contain a folder: don't delete
1180 				return true;
1181 			}
1182 			addDeletion(tw.getPathString(), nonTree(modeO), attributes[T_OURS]);
1183 			return true;
1184 		}
1185 
1186 		if (tw.isSubtree()) {
1187 			// file/folder conflicts: here I want to detect only file/folder
1188 			// conflict between ours and theirs. file/folder conflicts between
1189 			// base/index/workingTree and something else are not relevant or
1190 			// detected later
1191 			if (nonTree(modeO) != nonTree(modeT)) {
1192 				if (ignoreConflicts) {
1193 					// In case of merge failures, ignore this path instead of reporting unmerged, so
1194 					// a caller can use virtual commit. This will not result in files with conflict
1195 					// markers in the index/working tree. The actual diff on the path will be
1196 					// computed directly on children.
1197 					enterSubtree = false;
1198 					return true;
1199 				}
1200 				if (nonTree(modeB)) {
1201 					add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0);
1202 				}
1203 				if (nonTree(modeO)) {
1204 					add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
1205 				}
1206 				if (nonTree(modeT)) {
1207 					add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 0);
1208 				}
1209 				unmergedPaths.add(tw.getPathString());
1210 				enterSubtree = false;
1211 				return true;
1212 			}
1213 
1214 			// ours and theirs are both folders or both files (and treewalk
1215 			// tells us we are in a subtree because of index or working-dir).
1216 			// If they are both folders no content-merge is required - we can
1217 			// return here.
1218 			if (!nonTree(modeO)) {
1219 				return true;
1220 			}
1221 
1222 			// ours and theirs are both files, just fall out of the if block
1223 			// and do the content merge
1224 		}
1225 
1226 		if (nonTree(modeO) && nonTree(modeT)) {
1227 			// Check worktree before modifying files
1228 			boolean worktreeDirty = isWorktreeDirty(work, ourDce);
1229 			if (!attributes[T_OURS].canBeContentMerged() && worktreeDirty) {
1230 				return false;
1231 			}
1232 
1233 			if (gitLinkMerging && ignoreConflicts) {
1234 				// Always select 'ours' in case of GITLINK merge failures so
1235 				// a caller can use virtual commit.
1236 				add(tw.getRawPath(), ours, DirCacheEntry.STAGE_0, EPOCH, 0);
1237 				return true;
1238 			} else if (gitLinkMerging) {
1239 				addConflict(base, ours, theirs);
1240 				MergeResult<SubmoduleConflict> result = createGitLinksMergeResult(
1241 						base, ours, theirs);
1242 				result.setContainsConflicts(true);
1243 				mergeResults.put(tw.getPathString(), result);
1244 				unmergedPaths.add(tw.getPathString());
1245 				return true;
1246 			} else if (!attributes[T_OURS].canBeContentMerged()) {
1247 				// File marked as binary
1248 				switch (getContentMergeStrategy()) {
1249 					case OURS:
1250 						keep(ourDce);
1251 						return true;
1252 					case THEIRS:
1253 						DirCacheEntry theirEntry = add(tw.getRawPath(), theirs,
1254 								DirCacheEntry.STAGE_0, EPOCH, 0);
1255 						addToCheckout(tw.getPathString(), theirEntry, attributes);
1256 						return true;
1257 					default:
1258 						break;
1259 				}
1260 				addConflict(base, ours, theirs);
1261 
1262 				// attribute merge issues are conflicts but not failures
1263 				unmergedPaths.add(tw.getPathString());
1264 				return true;
1265 			}
1266 
1267 			// Check worktree before modifying files
1268 			if (worktreeDirty) {
1269 				return false;
1270 			}
1271 
1272 			MergeResult<RawText> result = null;
1273 			boolean hasSymlink = FileMode.SYMLINK.equals(modeO)
1274 					|| FileMode.SYMLINK.equals(modeT);
1275 			if (!hasSymlink) {
1276 				try {
1277 					result = contentMerge(base, ours, theirs, attributes,
1278 							getContentMergeStrategy());
1279 				} catch (BinaryBlobException e) {
1280 					// result == null
1281 				}
1282 			}
1283 			if (result == null) {
1284 				switch (getContentMergeStrategy()) {
1285 				case OURS:
1286 					keep(ourDce);
1287 					return true;
1288 				case THEIRS:
1289 					DirCacheEntry e = add(tw.getRawPath(), theirs,
1290 							DirCacheEntry.STAGE_0, EPOCH, 0);
1291 					if (e != null) {
1292 						addToCheckout(tw.getPathString(), e, attributes);
1293 					}
1294 					return true;
1295 				default:
1296 					result = new MergeResult<>(Collections.emptyList());
1297 					result.setContainsConflicts(true);
1298 					break;
1299 				}
1300 			}
1301 			if (ignoreConflicts) {
1302 				result.setContainsConflicts(false);
1303 			}
1304 			String currentPath = tw.getPathString();
1305 			if (hasSymlink) {
1306 				if (ignoreConflicts) {
1307 					if (((modeT & FileMode.TYPE_MASK) == FileMode.TYPE_FILE)) {
1308 						DirCacheEntry e = add(tw.getRawPath(), theirs,
1309 								DirCacheEntry.STAGE_0, EPOCH, 0);
1310 						addToCheckout(currentPath, e, attributes);
1311 					} else {
1312 						keep(ourDce);
1313 					}
1314 				} else {
1315 					// Record the conflict
1316 					DirCacheEntry e = addConflict(base, ours, theirs);
1317 					mergeResults.put(currentPath, result);
1318 					// If theirs is a file, check it out. In link/file
1319 					// conflicts, C git prefers the file.
1320 					if (((modeT & FileMode.TYPE_MASK) == FileMode.TYPE_FILE)
1321 							&& e != null) {
1322 						addToCheckout(currentPath, e, attributes);
1323 					}
1324 				}
1325 			} else {
1326 				updateIndex(base, ours, theirs, result, attributes[T_OURS]);
1327 			}
1328 			if (result.containsConflicts() && !ignoreConflicts) {
1329 				unmergedPaths.add(currentPath);
1330 			}
1331 			workTreeUpdater.markAsModified(currentPath);
1332 			// Entry is null - only adds the metadata.
1333 			addToCheckout(currentPath, null, attributes);
1334 		} else if (modeO != modeT) {
1335 			// OURS or THEIRS has been deleted
1336 			if (((modeO != 0 && !tw.idEqual(T_BASE, T_OURS)) || (modeT != 0 && !tw
1337 					.idEqual(T_BASE, T_THEIRS)))) {
1338 				if (gitLinkMerging && ignoreConflicts) {
1339 					add(tw.getRawPath(), ours, DirCacheEntry.STAGE_0, EPOCH, 0);
1340 				} else if (gitLinkMerging) {
1341 					addConflict(base, ours, theirs);
1342 					MergeResult<SubmoduleConflict> result = createGitLinksMergeResult(
1343 							base, ours, theirs);
1344 					result.setContainsConflicts(true);
1345 					mergeResults.put(tw.getPathString(), result);
1346 					unmergedPaths.add(tw.getPathString());
1347 				} else {
1348 					boolean isSymLink = ((modeO | modeT)
1349 							& FileMode.TYPE_MASK) == FileMode.TYPE_SYMLINK;
1350 					// Content merge strategy does not apply to delete-modify
1351 					// conflicts!
1352 					MergeResult<RawText> result;
1353 					if (isSymLink) {
1354 						// No need to do a content merge
1355 						result = new MergeResult<>(Collections.emptyList());
1356 						result.setContainsConflicts(true);
1357 					} else {
1358 						try {
1359 							result = contentMerge(base, ours, theirs,
1360 									attributes, ContentMergeStrategy.CONFLICT);
1361 						} catch (BinaryBlobException e) {
1362 							result = new MergeResult<>(Collections.emptyList());
1363 							result.setContainsConflicts(true);
1364 						}
1365 					}
1366 					if (ignoreConflicts) {
1367 						result.setContainsConflicts(false);
1368 						if (isSymLink) {
1369 							if (modeO != 0) {
1370 								keep(ourDce);
1371 							} else {
1372 								// Check out theirs
1373 								if (isWorktreeDirty(work, ourDce)) {
1374 									return false;
1375 								}
1376 								DirCacheEntry e = add(tw.getRawPath(), theirs,
1377 										DirCacheEntry.STAGE_0, EPOCH, 0);
1378 								if (e != null) {
1379 									addToCheckout(tw.getPathString(), e,
1380 											attributes);
1381 								}
1382 							}
1383 						} else {
1384 							// In case a conflict is detected the working tree
1385 							// file is again filled with new content (containing
1386 							// conflict markers). But also stage 0 of the index
1387 							// is filled with that content.
1388 							updateIndex(base, ours, theirs, result,
1389 									attributes[T_OURS]);
1390 						}
1391 					} else {
1392 						DirCacheEntry e = addConflict(base, ours, theirs);
1393 
1394 						// OURS was deleted checkout THEIRS
1395 						if (modeO == 0) {
1396 							// Check worktree before checking out THEIRS
1397 							if (isWorktreeDirty(work, ourDce)) {
1398 								return false;
1399 							}
1400 							if (nonTree(modeT) && e != null) {
1401 								addToCheckout(tw.getPathString(), e,
1402 										attributes);
1403 							}
1404 						}
1405 
1406 						unmergedPaths.add(tw.getPathString());
1407 
1408 						// generate a MergeResult for the deleted file
1409 						mergeResults.put(tw.getPathString(), result);
1410 					}
1411 				}
1412 			}
1413 		}
1414 		return true;
1415 	}
1416 
1417 	private static MergeResult<SubmoduleConflict> createGitLinksMergeResult(
1418 			CanonicalTreeParser base, CanonicalTreeParser ours,
1419 			CanonicalTreeParser theirs) {
1420 		return new MergeResult<>(Arrays.asList(
1421 				new SubmoduleConflict(
1422 						base == null ? null : base.getEntryObjectId()),
1423 				new SubmoduleConflict(
1424 						ours == null ? null : ours.getEntryObjectId()),
1425 				new SubmoduleConflict(
1426 						theirs == null ? null : theirs.getEntryObjectId())));
1427 	}
1428 
1429 	/**
1430 	 * Does the content merge. The three texts base, ours and theirs are
1431 	 * specified with {@link CanonicalTreeParser}. If any of the parsers is
1432 	 * specified as <code>null</code> then an empty text will be used instead.
1433 	 *
1434 	 * @param base
1435 	 * @param ours
1436 	 * @param theirs
1437 	 * @param attributes
1438 	 * @param strategy
1439 	 *
1440 	 * @return the result of the content merge
1441 	 * @throws BinaryBlobException
1442 	 *             if any of the blobs looks like a binary blob
1443 	 * @throws IOException
1444 	 */
1445 	private MergeResult<RawText> contentMerge(CanonicalTreeParser base,
1446 			CanonicalTreeParser ours, CanonicalTreeParser theirs,
1447 			Attributes[] attributes, ContentMergeStrategy strategy)
1448 			throws BinaryBlobException, IOException {
1449 		// TW: The attributes here are used to determine the LFS smudge filter.
1450 		// Is doing a content merge on LFS items really a good idea??
1451 		RawText baseText = base == null ? RawText.EMPTY_TEXT
1452 				: getRawText(base.getEntryObjectId(), attributes[T_BASE]);
1453 		RawText ourText = ours == null ? RawText.EMPTY_TEXT
1454 				: getRawText(ours.getEntryObjectId(), attributes[T_OURS]);
1455 		RawText theirsText = theirs == null ? RawText.EMPTY_TEXT
1456 				: getRawText(theirs.getEntryObjectId(), attributes[T_THEIRS]);
1457 		mergeAlgorithm.setContentMergeStrategy(strategy);
1458 		return mergeAlgorithm.merge(RawTextComparator.DEFAULT, baseText,
1459 				ourText, theirsText);
1460 	}
1461 
1462 	private boolean isIndexDirty() {
1463 		if (inCore) {
1464 			return false;
1465 		}
1466 
1467 		final int modeI = tw.getRawMode(T_INDEX);
1468 		final int modeO = tw.getRawMode(T_OURS);
1469 
1470 		// Index entry has to match ours to be considered clean
1471 		final boolean isDirty = nonTree(modeI)
1472 				&& !(modeO == modeI && tw.idEqual(T_INDEX, T_OURS));
1473 		if (isDirty) {
1474 			failingPaths
1475 					.put(tw.getPathString(), MergeFailureReason.DIRTY_INDEX);
1476 		}
1477 		return isDirty;
1478 	}
1479 
1480 	private boolean isWorktreeDirty(WorkingTreeIterator work,
1481 			DirCacheEntry ourDce) throws IOException {
1482 		if (work == null) {
1483 			return false;
1484 		}
1485 
1486 		final int modeF = tw.getRawMode(T_FILE);
1487 		final int modeO = tw.getRawMode(T_OURS);
1488 
1489 		// Worktree entry has to match ours to be considered clean
1490 		boolean isDirty;
1491 		if (ourDce != null) {
1492 			isDirty = work.isModified(ourDce, true, reader);
1493 		} else {
1494 			isDirty = work.isModeDifferent(modeO);
1495 			if (!isDirty && nonTree(modeF)) {
1496 				isDirty = !tw.idEqual(T_FILE, T_OURS);
1497 			}
1498 		}
1499 
1500 		// Ignore existing empty directories
1501 		if (isDirty && modeF == FileMode.TYPE_TREE
1502 				&& modeO == FileMode.TYPE_MISSING) {
1503 			isDirty = false;
1504 		}
1505 		if (isDirty) {
1506 			failingPaths.put(tw.getPathString(),
1507 					MergeFailureReason.DIRTY_WORKTREE);
1508 		}
1509 		return isDirty;
1510 	}
1511 
1512 	/**
1513 	 * Updates the index after a content merge has happened. If no conflict has
1514 	 * occurred this includes persisting the merged content to the object
1515 	 * database. In case of conflicts this method takes care to write the
1516 	 * correct stages to the index.
1517 	 *
1518 	 * @param base
1519 	 * @param ours
1520 	 * @param theirs
1521 	 * @param result
1522 	 * @param attributes
1523 	 * @throws IOException
1524 	 */
1525 	private void updateIndex(CanonicalTreeParser base,
1526 			CanonicalTreeParser ours, CanonicalTreeParser theirs,
1527 			MergeResult<RawText> result, Attributes attributes)
1528 			throws IOException {
1529 		TemporaryBuffer rawMerged = null;
1530 		try {
1531 			rawMerged = doMerge(result);
1532 			File mergedFile = inCore ? null
1533 					: writeMergedFile(rawMerged, attributes);
1534 			if (result.containsConflicts()) {
1535 				// A conflict occurred, the file will contain conflict markers
1536 				// the index will be populated with the three stages and the
1537 				// workdir (if used) contains the halfway merged content.
1538 				addConflict(base, ours, theirs);
1539 				mergeResults.put(tw.getPathString(), result);
1540 				return;
1541 			}
1542 
1543 			// No conflict occurred, the file will contain fully merged content.
1544 			// The index will be populated with the new merged version.
1545 			Instant lastModified = mergedFile == null ? null
1546 					: nonNullRepo().getFS().lastModifiedInstant(mergedFile);
1547 			// Set the mode for the new content. Fall back to REGULAR_FILE if
1548 			// we can't merge modes of OURS and THEIRS.
1549 			int newMode = mergeFileModes(tw.getRawMode(0), tw.getRawMode(1),
1550 					tw.getRawMode(2));
1551 			FileMode mode = newMode == FileMode.MISSING.getBits()
1552 					? FileMode.REGULAR_FILE : FileMode.fromBits(newMode);
1553 			workTreeUpdater.insertToIndex(rawMerged.openInputStream(),
1554 					tw.getPathString().getBytes(UTF_8), mode,
1555 					DirCacheEntry.STAGE_0, lastModified,
1556 					(int) rawMerged.length(),
1557 					attributes.get(Constants.ATTR_MERGE));
1558 		} finally {
1559 			if (rawMerged != null) {
1560 				rawMerged.destroy();
1561 			}
1562 		}
1563 	}
1564 
1565 	/**
1566 	 * Writes merged file content to the working tree.
1567 	 *
1568 	 * @param rawMerged
1569 	 *            the raw merged content
1570 	 * @param attributes
1571 	 *            the files .gitattributes entries
1572 	 * @return the working tree file to which the merged content was written.
1573 	 * @throws IOException
1574 	 */
1575 	private File writeMergedFile(TemporaryBuffer rawMerged,
1576 			Attributes attributes)
1577 			throws IOException {
1578 		File workTree = nonNullRepo().getWorkTree();
1579 		FS fs = nonNullRepo().getFS();
1580 		File of = new File(workTree, tw.getPathString());
1581 		File parentFolder = of.getParentFile();
1582 		EolStreamType eol = workTreeUpdater.detectCheckoutStreamType(attributes);
1583 		if (!fs.exists(parentFolder)) {
1584 			parentFolder.mkdirs();
1585 		}
1586 		workTreeUpdater.updateFileWithContent(rawMerged::openInputStream,
1587 				eol, tw.getSmudgeCommand(attributes), of.getPath(), of);
1588 		return of;
1589 	}
1590 
1591 	private TemporaryBuffer doMerge(MergeResult<RawText> result)
1592 			throws IOException {
1593 		TemporaryBuffer.LocalFile buf = new TemporaryBuffer.LocalFile(
1594 				db != null ? nonNullRepo().getDirectory() : null, workTreeUpdater.getInCoreFileSizeLimit());
1595 		boolean success = false;
1596 		try {
1597 			new MergeFormatter().formatMerge(buf, result,
1598 					Arrays.asList(commitNames), UTF_8);
1599 			buf.close();
1600 			success = true;
1601 		} finally {
1602 			if (!success) {
1603 				buf.destroy();
1604 			}
1605 		}
1606 		return buf;
1607 	}
1608 
1609 	/**
1610 	 * Try to merge filemodes. If only ours or theirs have changed the mode
1611 	 * (compared to base) we choose that one. If ours and theirs have equal
1612 	 * modes return that one. If also that is not the case the modes are not
1613 	 * mergeable. Return {@link FileMode#MISSING} int that case.
1614 	 *
1615 	 * @param modeB
1616 	 *            filemode found in BASE
1617 	 * @param modeO
1618 	 *            filemode found in OURS
1619 	 * @param modeT
1620 	 *            filemode found in THEIRS
1621 	 *
1622 	 * @return the merged filemode or {@link FileMode#MISSING} in case of a
1623 	 *         conflict
1624 	 */
1625 	private int mergeFileModes(int modeB, int modeO, int modeT) {
1626 		if (modeO == modeT) {
1627 			return modeO;
1628 		}
1629 		if (modeB == modeO) {
1630 			// Base equal to Ours -> chooses Theirs if that is not missing
1631 			return (modeT == FileMode.MISSING.getBits()) ? modeO : modeT;
1632 		}
1633 		if (modeB == modeT) {
1634 			// Base equal to Theirs -> chooses Ours if that is not missing
1635 			return (modeO == FileMode.MISSING.getBits()) ? modeT : modeO;
1636 		}
1637 		return FileMode.MISSING.getBits();
1638 	}
1639 
1640 	private RawText getRawText(ObjectId id,
1641 			Attributes attributes)
1642 			throws IOException, BinaryBlobException {
1643 		if (id.equals(ObjectId.zeroId())) {
1644 			return new RawText(new byte[]{});
1645 		}
1646 
1647 		ObjectLoader loader = LfsFactory.getInstance().applySmudgeFilter(
1648 				getRepository(), reader.open(id, OBJ_BLOB),
1649 				attributes.get(Constants.ATTR_MERGE));
1650 		int threshold = PackConfig.DEFAULT_BIG_FILE_THRESHOLD;
1651 		return RawText.load(loader, threshold);
1652 	}
1653 
1654 	private static boolean nonTree(int mode) {
1655 		return mode != 0 && !FileMode.TREE.equals(mode);
1656 	}
1657 
1658 	private static boolean isGitLink(int mode) {
1659 		return FileMode.GITLINK.equals(mode);
1660 	}
1661 
1662 	/** {@inheritDoc} */
1663 	@Override
1664 	public ObjectId getResultTreeId() {
1665 		return (resultTree == null) ? null : resultTree.toObjectId();
1666 	}
1667 
1668 	/**
1669 	 * Set the names of the commits as they would appear in conflict markers
1670 	 *
1671 	 * @param commitNames
1672 	 *            the names of the commits as they would appear in conflict
1673 	 *            markers
1674 	 */
1675 	public void setCommitNames(String[] commitNames) {
1676 		this.commitNames = commitNames;
1677 	}
1678 
1679 	/**
1680 	 * Get the names of the commits as they would appear in conflict markers.
1681 	 *
1682 	 * @return the names of the commits as they would appear in conflict
1683 	 *         markers.
1684 	 */
1685 	public String[] getCommitNames() {
1686 		return commitNames;
1687 	}
1688 
1689 	/**
1690 	 * Get the paths with conflicts. This is a subset of the files listed by
1691 	 * {@link #getModifiedFiles()}
1692 	 *
1693 	 * @return the paths with conflicts. This is a subset of the files listed by
1694 	 *         {@link #getModifiedFiles()}
1695 	 */
1696 	public List<String> getUnmergedPaths() {
1697 		return unmergedPaths;
1698 	}
1699 
1700 	/**
1701 	 * Get the paths of files which have been modified by this merge.
1702 	 *
1703 	 * @return the paths of files which have been modified by this merge. A file
1704 	 *         will be modified if a content-merge works on this path or if the
1705 	 *         merge algorithm decides to take the theirs-version. This is a
1706 	 *         superset of the files listed by {@link #getUnmergedPaths()}.
1707 	 */
1708 	public List<String> getModifiedFiles() {
1709 		return workTreeUpdater != null ? workTreeUpdater.getModifiedFiles() : modifiedFiles;
1710 	}
1711 
1712 	/**
1713 	 * Get a map which maps the paths of files which have to be checked out
1714 	 * because the merge created new fully-merged content for this file into the
1715 	 * index.
1716 	 *
1717 	 * @return a map which maps the paths of files which have to be checked out
1718 	 *         because the merge created new fully-merged content for this file
1719 	 *         into the index. This means: the merge wrote a new stage 0 entry
1720 	 *         for this path.
1721 	 */
1722 	public Map<String, DirCacheEntry> getToBeCheckedOut() {
1723 		return workTreeUpdater.getToBeCheckedOut();
1724 	}
1725 
1726 	/**
1727 	 * Get the mergeResults
1728 	 *
1729 	 * @return the mergeResults
1730 	 */
1731 	public Map<String, MergeResult<? extends Sequence>> getMergeResults() {
1732 		return mergeResults;
1733 	}
1734 
1735 	/**
1736 	 * Get list of paths causing this merge to fail (not stopped because of a
1737 	 * conflict).
1738 	 *
1739 	 * @return lists paths causing this merge to fail (not stopped because of a
1740 	 *         conflict). <code>null</code> is returned if this merge didn't
1741 	 *         fail.
1742 	 */
1743 	public Map<String, MergeFailureReason> getFailingPaths() {
1744 		return failingPaths.isEmpty() ? null : failingPaths;
1745 	}
1746 
1747 	/**
1748 	 * Returns whether this merge failed (i.e. not stopped because of a
1749 	 * conflict)
1750 	 *
1751 	 * @return <code>true</code> if a failure occurred, <code>false</code>
1752 	 *         otherwise
1753 	 */
1754 	public boolean failed() {
1755 		return !failingPaths.isEmpty();
1756 	}
1757 
1758 	/**
1759 	 * Sets the DirCache which shall be used by this merger. If the DirCache is
1760 	 * not set explicitly and if this merger doesn't work in-core, this merger
1761 	 * will implicitly get and lock a default DirCache. If the DirCache is
1762 	 * explicitly set the caller is responsible to lock it in advance. Finally
1763 	 * the merger will call {@link org.eclipse.jgit.dircache.DirCache#commit()}
1764 	 * which requires that the DirCache is locked. If the {@link #mergeImpl()}
1765 	 * returns without throwing an exception the lock will be released. In case
1766 	 * of exceptions the caller is responsible to release the lock.
1767 	 *
1768 	 * @param dc
1769 	 *            the DirCache to set
1770 	 */
1771 	public void setDirCache(DirCache dc) {
1772 		this.dircache = dc;
1773 	}
1774 
1775 	/**
1776 	 * Sets the WorkingTreeIterator to be used by this merger. If no
1777 	 * WorkingTreeIterator is set this merger will ignore the working tree and
1778 	 * fail if a content merge is necessary.
1779 	 * <p>
1780 	 * TODO: enhance WorkingTreeIterator to support write operations. Then this
1781 	 * merger will be able to merge with a different working tree abstraction.
1782 	 *
1783 	 * @param workingTreeIterator
1784 	 *            the workingTreeIt to set
1785 	 */
1786 	public void setWorkingTreeIterator(WorkingTreeIterator workingTreeIterator) {
1787 		this.workingTreeIterator = workingTreeIterator;
1788 	}
1789 
1790 
1791 	/**
1792 	 * The resolve conflict way of three way merging
1793 	 *
1794 	 * @param baseTree
1795 	 *            a {@link org.eclipse.jgit.treewalk.AbstractTreeIterator}
1796 	 *            object.
1797 	 * @param headTree
1798 	 *            a {@link org.eclipse.jgit.revwalk.RevTree} object.
1799 	 * @param mergeTree
1800 	 *            a {@link org.eclipse.jgit.revwalk.RevTree} object.
1801 	 * @param ignoreConflicts
1802 	 *            Controls what to do in case a content-merge is done and a
1803 	 *            conflict is detected. The default setting for this should be
1804 	 *            <code>false</code>. In this case the working tree file is
1805 	 *            filled with new content (containing conflict markers) and the
1806 	 *            index is filled with multiple stages containing BASE, OURS and
1807 	 *            THEIRS content. Having such non-0 stages is the sign to git
1808 	 *            tools that there are still conflicts for that path.
1809 	 *            <p>
1810 	 *            If <code>true</code> is specified the behavior is different.
1811 	 *            In case a conflict is detected the working tree file is again
1812 	 *            filled with new content (containing conflict markers). But
1813 	 *            also stage 0 of the index is filled with that content. No
1814 	 *            other stages are filled. Means: there is no conflict on that
1815 	 *            path but the new content (including conflict markers) is
1816 	 *            stored as successful merge result. This is needed in the
1817 	 *            context of {@link org.eclipse.jgit.merge.RecursiveMerger}
1818 	 *            where when determining merge bases we don't want to deal with
1819 	 *            content-merge conflicts.
1820 	 * @return whether the trees merged cleanly
1821 	 * @throws java.io.IOException
1822 	 * @since 3.5
1823 	 */
1824 	protected boolean mergeTrees(AbstractTreeIterator baseTree,
1825 			RevTree headTree, RevTree mergeTree, boolean ignoreConflicts)
1826 			throws IOException {
1827 		try {
1828 			workTreeUpdater = inCore ?
1829 					WorkTreeUpdater.createInCoreWorkTreeUpdater(db, dircache, getObjectInserter()) :
1830 					WorkTreeUpdater.createWorkTreeUpdater(db, dircache);
1831 			dircache = workTreeUpdater.getLockedDirCache();
1832 			tw = new NameConflictTreeWalk(db, reader);
1833 
1834 			tw.addTree(baseTree);
1835 			tw.setHead(tw.addTree(headTree));
1836 			tw.addTree(mergeTree);
1837 			DirCacheBuildIterator buildIt = workTreeUpdater.createDirCacheBuildIterator();
1838 			int dciPos = tw.addTree(buildIt);
1839 			if (workingTreeIterator != null) {
1840 				tw.addTree(workingTreeIterator);
1841 				workingTreeIterator.setDirCacheIterator(tw, dciPos);
1842 			} else {
1843 				tw.setFilter(TreeFilter.ANY_DIFF);
1844 			}
1845 
1846 			if (!mergeTreeWalk(tw, ignoreConflicts)) {
1847 				return false;
1848 			}
1849 
1850 			workTreeUpdater.writeWorkTreeChanges(true);
1851 			if (getUnmergedPaths().isEmpty() && !failed()) {
1852 				WorkTreeUpdater.Result result = workTreeUpdater.writeIndexChanges();
1853 				resultTree = result.getTreeId();
1854 				modifiedFiles = result.getModifiedFiles();
1855 				for (String f : result.getFailedToDelete()) {
1856 					failingPaths.put(f, MergeFailureReason.COULD_NOT_DELETE);
1857 				}
1858 				return result.getFailedToDelete().isEmpty();
1859 			}
1860 			resultTree = null;
1861 			return false;
1862 		} finally {
1863 			if(modifiedFiles.isEmpty()) {
1864 				modifiedFiles = workTreeUpdater.getModifiedFiles();
1865 			}
1866 			workTreeUpdater.close();
1867 			workTreeUpdater = null;
1868 		}
1869 	}
1870 
1871 	/**
1872 	 * Process the given TreeWalk's entries.
1873 	 *
1874 	 * @param treeWalk
1875 	 *            The walk to iterate over.
1876 	 * @param ignoreConflicts
1877 	 *            see
1878 	 *            {@link org.eclipse.jgit.merge.ResolveMerger#mergeTrees(AbstractTreeIterator, RevTree, RevTree, boolean)}
1879 	 * @return Whether the trees merged cleanly.
1880 	 * @throws java.io.IOException
1881 	 * @since 3.5
1882 	 */
1883 	protected boolean mergeTreeWalk(TreeWalk treeWalk, boolean ignoreConflicts)
1884 			throws IOException {
1885 		boolean hasWorkingTreeIterator = tw.getTreeCount() > T_FILE;
1886 		boolean hasAttributeNodeProvider = treeWalk
1887 				.getAttributesNodeProvider() != null;
1888 		while (treeWalk.next()) {
1889 			Attributes[] attributes = {NO_ATTRIBUTES, NO_ATTRIBUTES,
1890 					NO_ATTRIBUTES};
1891 			if (hasAttributeNodeProvider) {
1892 				attributes[T_BASE] = treeWalk.getAttributes(T_BASE);
1893 				attributes[T_OURS] = treeWalk.getAttributes(T_OURS);
1894 				attributes[T_THEIRS] = treeWalk.getAttributes(T_THEIRS);
1895 			}
1896 			if (!processEntry(
1897 					treeWalk.getTree(T_BASE, CanonicalTreeParser.class),
1898 					treeWalk.getTree(T_OURS, CanonicalTreeParser.class),
1899 					treeWalk.getTree(T_THEIRS, CanonicalTreeParser.class),
1900 					treeWalk.getTree(T_INDEX, DirCacheBuildIterator.class),
1901 					hasWorkingTreeIterator ? treeWalk.getTree(T_FILE,
1902 							WorkingTreeIterator.class) : null,
1903 					ignoreConflicts, attributes)) {
1904 				workTreeUpdater.revertModifiedFiles();
1905 				return false;
1906 			}
1907 			if (treeWalk.isSubtree() && enterSubtree) {
1908 				treeWalk.enterSubtree();
1909 			}
1910 		}
1911 		return true;
1912 	}
1913 }