1 /*
2 * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
3 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
4 * Copyright (C) 2008, Roger C. Soares <rogersoares@intelinet.com.br>
5 * Copyright (C) 2006, Shawn O. Pearce <spearce@spearce.org>
6 * Copyright (C) 2010, Chrisian Halstrick <christian.halstrick@sap.com> and
7 * other copyright owners as documented in the project's IP log.
8 *
9 * This program and the accompanying materials are made available under the
10 * terms of the Eclipse Distribution License v1.0 which accompanies this
11 * distribution, is reproduced below, and is available at
12 * http://www.eclipse.org/org/documents/edl-v10.php
13 *
14 * All rights reserved.
15 *
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions are met:
18 *
19 * - Redistributions of source code must retain the above copyright notice, this
20 * list of conditions and the following disclaimer.
21 *
22 * - Redistributions in binary form must reproduce the above copyright notice,
23 * this list of conditions and the following disclaimer in the documentation
24 * and/or other materials provided with the distribution.
25 *
26 * - Neither the name of the Eclipse Foundation, Inc. nor the names of its
27 * contributors may be used to endorse or promote products derived from this
28 * software without specific prior written permission.
29 *
30 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
31 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
34 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
35 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
36 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
37 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
38 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
39 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
40 * POSSIBILITY OF SUCH DAMAGE.
41 */
42
43 package org.eclipse.jgit.dircache;
44
45 import java.io.File;
46 import java.io.FileOutputStream;
47 import java.io.IOException;
48 import java.io.OutputStream;
49 import java.text.MessageFormat;
50 import java.util.ArrayList;
51 import java.util.HashMap;
52 import java.util.List;
53 import java.util.Map;
54
55 import org.eclipse.jgit.errors.CheckoutConflictException;
56 import org.eclipse.jgit.errors.CorruptObjectException;
57 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
58 import org.eclipse.jgit.errors.IndexWriteException;
59 import org.eclipse.jgit.errors.MissingObjectException;
60 import org.eclipse.jgit.internal.JGitText;
61 import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
62 import org.eclipse.jgit.lib.CoreConfig.SymLinks;
63 import org.eclipse.jgit.lib.FileMode;
64 import org.eclipse.jgit.lib.ObjectChecker;
65 import org.eclipse.jgit.lib.ObjectId;
66 import org.eclipse.jgit.lib.ObjectLoader;
67 import org.eclipse.jgit.lib.ObjectReader;
68 import org.eclipse.jgit.lib.Repository;
69 import org.eclipse.jgit.treewalk.AbstractTreeIterator;
70 import org.eclipse.jgit.treewalk.CanonicalTreeParser;
71 import org.eclipse.jgit.treewalk.EmptyTreeIterator;
72 import org.eclipse.jgit.treewalk.FileTreeIterator;
73 import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
74 import org.eclipse.jgit.treewalk.TreeWalk;
75 import org.eclipse.jgit.treewalk.WorkingTreeIterator;
76 import org.eclipse.jgit.treewalk.WorkingTreeOptions;
77 import org.eclipse.jgit.treewalk.filter.PathFilter;
78 import org.eclipse.jgit.util.FS;
79 import org.eclipse.jgit.util.FileUtils;
80 import org.eclipse.jgit.util.RawParseUtils;
81 import org.eclipse.jgit.util.SystemReader;
82 import org.eclipse.jgit.util.io.AutoCRLFOutputStream;
83
84 /**
85 * This class handles checking out one or two trees merging with the index.
86 */
87 public class DirCacheCheckout {
88 private Repository repo;
89
90 private HashMap<String, ObjectId> updated = new HashMap<String, ObjectId>();
91
92 private ArrayList<String> conflicts = new ArrayList<String>();
93
94 private ArrayList<String> removed = new ArrayList<String>();
95
96 private ObjectId mergeCommitTree;
97
98 private DirCache dc;
99
100 private DirCacheBuilder builder;
101
102 private NameConflictTreeWalk walk;
103
104 private ObjectId headCommitTree;
105
106 private WorkingTreeIterator workingTree;
107
108 private boolean failOnConflict = true;
109
110 private ArrayList<String> toBeDeleted = new ArrayList<String>();
111
112 private boolean emptyDirCache;
113
114 /**
115 * @return a list of updated paths and objectIds
116 */
117 public Map<String, ObjectId> getUpdated() {
118 return updated;
119 }
120
121 /**
122 * @return a list of conflicts created by this checkout
123 */
124 public List<String> getConflicts() {
125 return conflicts;
126 }
127
128 /**
129 * @return a list of paths (relative to the start of the working tree) of
130 * files which couldn't be deleted during last call to
131 * {@link #checkout()} . {@link #checkout()} detected that these
132 * files should be deleted but the deletion in the filesystem failed
133 * (e.g. because a file was locked). To have a consistent state of
134 * the working tree these files have to be deleted by the callers of
135 * {@link DirCacheCheckout}.
136 */
137 public List<String> getToBeDeleted() {
138 return toBeDeleted;
139 }
140
141 /**
142 * @return a list of all files removed by this checkout
143 */
144 public List<String> getRemoved() {
145 return removed;
146 }
147
148 /**
149 * Constructs a DirCacheCeckout for merging and checking out two trees (HEAD
150 * and mergeCommitTree) and the index.
151 *
152 * @param repo
153 * the repository in which we do the checkout
154 * @param headCommitTree
155 * the id of the tree of the head commit
156 * @param dc
157 * the (already locked) Dircache for this repo
158 * @param mergeCommitTree
159 * the id of the tree we want to fast-forward to
160 * @param workingTree
161 * an iterator over the repositories Working Tree
162 * @throws IOException
163 */
164 public DirCacheCheckout(Repository repo, ObjectId headCommitTree, DirCache dc,
165 ObjectId mergeCommitTree, WorkingTreeIterator workingTree)
166 throws IOException {
167 this.repo = repo;
168 this.dc = dc;
169 this.headCommitTree = headCommitTree;
170 this.mergeCommitTree = mergeCommitTree;
171 this.workingTree = workingTree;
172 this.emptyDirCache = (dc == null) || (dc.getEntryCount() == 0);
173 }
174
175 /**
176 * Constructs a DirCacheCeckout for merging and checking out two trees (HEAD
177 * and mergeCommitTree) and the index. As iterator over the working tree
178 * this constructor creates a standard {@link FileTreeIterator}
179 *
180 * @param repo
181 * the repository in which we do the checkout
182 * @param headCommitTree
183 * the id of the tree of the head commit
184 * @param dc
185 * the (already locked) Dircache for this repo
186 * @param mergeCommitTree
187 * the id of the tree we want to fast-forward to
188 * @throws IOException
189 */
190 public DirCacheCheckout(Repository repo, ObjectId headCommitTree,
191 DirCache dc, ObjectId mergeCommitTree) throws IOException {
192 this(repo, headCommitTree, dc, mergeCommitTree, new FileTreeIterator(repo));
193 }
194
195 /**
196 * Constructs a DirCacheCeckout for checking out one tree, merging with the
197 * index.
198 *
199 * @param repo
200 * the repository in which we do the checkout
201 * @param dc
202 * the (already locked) Dircache for this repo
203 * @param mergeCommitTree
204 * the id of the tree we want to fast-forward to
205 * @param workingTree
206 * an iterator over the repositories Working Tree
207 * @throws IOException
208 */
209 public DirCacheCheckout(Repository repo, DirCache dc,
210 ObjectId mergeCommitTree, WorkingTreeIterator workingTree)
211 throws IOException {
212 this(repo, null, dc, mergeCommitTree, workingTree);
213 }
214
215 /**
216 * Constructs a DirCacheCeckout for checking out one tree, merging with the
217 * index. As iterator over the working tree this constructor creates a
218 * standard {@link FileTreeIterator}
219 *
220 * @param repo
221 * the repository in which we do the checkout
222 * @param dc
223 * the (already locked) Dircache for this repo
224 * @param mergeCommitTree
225 * the id of the tree of the
226 * @throws IOException
227 */
228 public DirCacheCheckout(Repository repo, DirCache dc,
229 ObjectId mergeCommitTree) throws IOException {
230 this(repo, null, dc, mergeCommitTree, new FileTreeIterator(repo));
231 }
232
233 /**
234 * Scan head, index and merge tree. Used during normal checkout or merge
235 * operations.
236 *
237 * @throws CorruptObjectException
238 * @throws IOException
239 */
240 public void preScanTwoTrees() throws CorruptObjectException, IOException {
241 removed.clear();
242 updated.clear();
243 conflicts.clear();
244 walk = new NameConflictTreeWalk(repo);
245 builder = dc.builder();
246
247 addTree(walk, headCommitTree);
248 addTree(walk, mergeCommitTree);
249 walk.addTree(new DirCacheBuildIterator(builder));
250 walk.addTree(workingTree);
251
252 while (walk.next()) {
253 processEntry(walk.getTree(0, CanonicalTreeParser.class),
254 walk.getTree(1, CanonicalTreeParser.class),
255 walk.getTree(2, DirCacheBuildIterator.class),
256 walk.getTree(3, WorkingTreeIterator.class));
257 if (walk.isSubtree())
258 walk.enterSubtree();
259 }
260 }
261
262 private void addTree(TreeWalk tw, ObjectId id) throws MissingObjectException, IncorrectObjectTypeException, IOException {
263 if (id == null)
264 tw.addTree(new EmptyTreeIterator());
265 else
266 tw.addTree(id);
267 }
268
269 /**
270 * Scan index and merge tree (no HEAD). Used e.g. for initial checkout when
271 * there is no head yet.
272 *
273 * @throws MissingObjectException
274 * @throws IncorrectObjectTypeException
275 * @throws CorruptObjectException
276 * @throws IOException
277 */
278 public void prescanOneTree()
279 throws MissingObjectException, IncorrectObjectTypeException,
280 CorruptObjectException, IOException {
281 removed.clear();
282 updated.clear();
283 conflicts.clear();
284
285 builder = dc.builder();
286
287 walk = new NameConflictTreeWalk(repo);
288 addTree(walk, mergeCommitTree);
289 walk.addTree(new DirCacheBuildIterator(builder));
290 walk.addTree(workingTree);
291
292 while (walk.next()) {
293 processEntry(walk.getTree(0, CanonicalTreeParser.class),
294 walk.getTree(1, DirCacheBuildIterator.class),
295 walk.getTree(2, WorkingTreeIterator.class));
296 if (walk.isSubtree())
297 walk.enterSubtree();
298 }
299 conflicts.removeAll(removed);
300 }
301
302 /**
303 * Processing an entry in the context of {@link #prescanOneTree()} when only
304 * one tree is given
305 *
306 * @param m the tree to merge
307 * @param i the index
308 * @param f the working tree
309 * @throws IOException
310 */
311 void processEntry(CanonicalTreeParser m, DirCacheBuildIterator i,
312 WorkingTreeIterator f) throws IOException {
313 if (m != null) {
314 checkValidPath(m);
315 // There is an entry in the merge commit. Means: we want to update
316 // what's currently in the index and working-tree to that one
317 if (i == null) {
318 // The index entry is missing
319 if (f != null && !FileMode.TREE.equals(f.getEntryFileMode())
320 && !f.isEntryIgnored()) {
321 // don't overwrite an untracked and not ignored file
322 conflicts.add(walk.getPathString());
323 } else
324 update(m.getEntryPathString(), m.getEntryObjectId(),
325 m.getEntryFileMode());
326 } else if (f == null || !m.idEqual(i)) {
327 // The working tree file is missing or the merge content differs
328 // from index content
329 update(m.getEntryPathString(), m.getEntryObjectId(),
330 m.getEntryFileMode());
331 } else if (i.getDirCacheEntry() != null) {
332 // The index contains a file (and not a folder)
333 if (f.isModified(i.getDirCacheEntry(), true,
334 this.walk.getObjectReader())
335 || i.getDirCacheEntry().getStage() != 0)
336 // The working tree file is dirty or the index contains a
337 // conflict
338 update(m.getEntryPathString(), m.getEntryObjectId(),
339 m.getEntryFileMode());
340 else {
341 // update the timestamp of the index with the one from the
342 // file if not set, as we are sure to be in sync here.
343 DirCacheEntry entry = i.getDirCacheEntry();
344 if (entry.getLastModified() == 0)
345 entry.setLastModified(f.getEntryLastModified());
346 keep(entry);
347 }
348 } else
349 // The index contains a folder
350 keep(i.getDirCacheEntry());
351 } else {
352 // There is no entry in the merge commit. Means: we want to delete
353 // what's currently in the index and working tree
354 if (f != null) {
355 // There is a file/folder for that path in the working tree
356 if (walk.isDirectoryFileConflict()) {
357 conflicts.add(walk.getPathString());
358 } else {
359 // No file/folder conflict exists. All entries are files or
360 // all entries are folders
361 if (i != null) {
362 // ... and the working tree contained a file or folder
363 // -> add it to the removed set and remove it from
364 // conflicts set
365 remove(i.getEntryPathString());
366 conflicts.remove(i.getEntryPathString());
367 } else {
368 // untracked file, neither contained in tree to merge
369 // nor in index
370 }
371 }
372 } else {
373 // There is no file/folder for that path in the working tree,
374 // nor in the merge head.
375 // The only entry we have is the index entry. Like the case
376 // where there is a file with the same name, remove it,
377 }
378 }
379 }
380
381 /**
382 * Execute this checkout
383 *
384 * @return <code>false</code> if this method could not delete all the files
385 * which should be deleted (e.g. because of of the files was
386 * locked). In this case {@link #getToBeDeleted()} lists the files
387 * which should be tried to be deleted outside of this method.
388 * Although <code>false</code> is returned the checkout was
389 * successful and the working tree was updated for all other files.
390 * <code>true</code> is returned when no such problem occurred
391 *
392 * @throws IOException
393 */
394 public boolean checkout() throws IOException {
395 try {
396 return doCheckout();
397 } finally {
398 dc.unlock();
399 }
400 }
401
402 private boolean doCheckout() throws CorruptObjectException, IOException,
403 MissingObjectException, IncorrectObjectTypeException,
404 CheckoutConflictException, IndexWriteException {
405 toBeDeleted.clear();
406 try (ObjectReader objectReader = repo.getObjectDatabase().newReader()) {
407 if (headCommitTree != null)
408 preScanTwoTrees();
409 else
410 prescanOneTree();
411
412 if (!conflicts.isEmpty()) {
413 if (failOnConflict)
414 throw new CheckoutConflictException(conflicts.toArray(new String[conflicts.size()]));
415 else
416 cleanUpConflicts();
417 }
418
419 // update our index
420 builder.finish();
421
422 File file = null;
423 String last = null;
424 // when deleting files process them in the opposite order as they have
425 // been reported. This ensures the files are deleted before we delete
426 // their parent folders
427 for (int i = removed.size() - 1; i >= 0; i--) {
428 String r = removed.get(i);
429 file = new File(repo.getWorkTree(), r);
430 if (!file.delete() && repo.getFS().exists(file)) {
431 // The list of stuff to delete comes from the index
432 // which will only contain a directory if it is
433 // a submodule, in which case we shall not attempt
434 // to delete it. A submodule is not empty, so it
435 // is safe to check this after a failed delete.
436 if (!repo.getFS().isDirectory(file))
437 toBeDeleted.add(r);
438 } else {
439 if (last != null && !isSamePrefix(r, last))
440 removeEmptyParents(new File(repo.getWorkTree(), last));
441 last = r;
442 }
443 }
444 if (file != null)
445 removeEmptyParents(file);
446
447 for (String path : updated.keySet()) {
448 DirCacheEntry entry = dc.getEntry(path);
449 if (!FileMode.GITLINK.equals(entry.getRawMode()))
450 checkoutEntry(repo, entry, objectReader);
451 }
452
453 // commit the index builder - a new index is persisted
454 if (!builder.commit())
455 throw new IndexWriteException();
456 }
457 return toBeDeleted.size() == 0;
458 }
459
460 private static boolean isSamePrefix(String a, String b) {
461 int as = a.lastIndexOf('/');
462 int bs = b.lastIndexOf('/');
463 return a.substring(0, as + 1).equals(b.substring(0, bs + 1));
464 }
465
466 private void removeEmptyParents(File f) {
467 File parentFile = f.getParentFile();
468
469 while (parentFile != null && !parentFile.equals(repo.getWorkTree())) {
470 if (!parentFile.delete())
471 break;
472 parentFile = parentFile.getParentFile();
473 }
474 }
475
476 /**
477 * Compares whether two pairs of ObjectId and FileMode are equal.
478 *
479 * @param id1
480 * @param mode1
481 * @param id2
482 * @param mode2
483 * @return <code>true</code> if FileModes and ObjectIds are equal.
484 * <code>false</code> otherwise
485 */
486 private boolean equalIdAndMode(ObjectId id1, FileMode mode1, ObjectId id2,
487 FileMode mode2) {
488 if (!mode1.equals(mode2))
489 return false;
490 return id1 != null ? id1.equals(id2) : id2 == null;
491 }
492
493 /**
494 * Here the main work is done. This method is called for each existing path
495 * in head, index and merge. This method decides what to do with the
496 * corresponding index entry: keep it, update it, remove it or mark a
497 * conflict.
498 *
499 * @param h
500 * the entry for the head
501 * @param m
502 * the entry for the merge
503 * @param i
504 * the entry for the index
505 * @param f
506 * the file in the working tree
507 * @throws IOException
508 */
509
510 void processEntry(CanonicalTreeParser h, CanonicalTreeParser m,
511 DirCacheBuildIterator i, WorkingTreeIterator f) throws IOException {
512 DirCacheEntry dce = i != null ? i.getDirCacheEntry() : null;
513
514 String name = walk.getPathString();
515
516 if (m != null)
517 checkValidPath(m);
518
519 if (i == null && m == null && h == null) {
520 // File/Directory conflict case #20
521 if (walk.isDirectoryFileConflict())
522 // TODO: check whether it is always correct to report a conflict here
523 conflict(name, null, null, null);
524
525 // file only exists in working tree -> ignore it
526 return;
527 }
528
529 ObjectId iId = (i == null ? null : i.getEntryObjectId());
530 ObjectId mId = (m == null ? null : m.getEntryObjectId());
531 ObjectId hId = (h == null ? null : h.getEntryObjectId());
532 FileMode iMode = (i == null ? null : i.getEntryFileMode());
533 FileMode mMode = (m == null ? null : m.getEntryFileMode());
534 FileMode hMode = (h == null ? null : h.getEntryFileMode());
535
536 /**
537 * <pre>
538 * File/Directory conflicts:
539 * the following table from ReadTreeTest tells what to do in case of directory/file
540 * conflicts. I give comments here
541 *
542 * H I M Clean H==M H==I I==M Result
543 * ------------------------------------------------------------------
544 * 1 D D F Y N Y N Update
545 * 2 D D F N N Y N Conflict
546 * 3 D F D Y N N Keep
547 * 4 D F D N N N Conflict
548 * 5 D F F Y N N Y Keep
549 * 5b D F F Y N N N Conflict
550 * 6 D F F N N N Y Keep
551 * 6b D F F N N N N Conflict
552 * 7 F D F Y Y N N Update
553 * 8 F D F N Y N N Conflict
554 * 9 F D F N N N Conflict
555 * 10 F D D N N Y Keep
556 * 11 F D D N N N Conflict
557 * 12 F F D Y N Y N Update
558 * 13 F F D N N Y N Conflict
559 * 14 F F D N N N Conflict
560 * 15 0 F D N N N Conflict
561 * 16 0 D F Y N N N Update
562 * 17 0 D F N N N Conflict
563 * 18 F 0 D Update
564 * 19 D 0 F Update
565 * 20 0 0 F N (worktree=dir) Conflict
566 * </pre>
567 */
568
569 // The information whether head,index,merge iterators are currently
570 // pointing to file/folder/non-existing is encoded into this variable.
571 //
572 // To decode write down ffMask in hexadecimal form. The last digit
573 // represents the state for the merge iterator, the second last the
574 // state for the index iterator and the third last represents the state
575 // for the head iterator. The hexadecimal constant "F" stands for
576 // "file", a "D" stands for "directory" (tree), and a "0" stands for
577 // non-existing. Symbolic links and git links are treated as File here.
578 //
579 // Examples:
580 // ffMask == 0xFFD -> Head=File, Index=File, Merge=Tree
581 // ffMask == 0xDD0 -> Head=Tree, Index=Tree, Merge=Non-Existing
582
583 int ffMask = 0;
584 if (h != null)
585 ffMask = FileMode.TREE.equals(hMode) ? 0xD00 : 0xF00;
586 if (i != null)
587 ffMask |= FileMode.TREE.equals(iMode) ? 0x0D0 : 0x0F0;
588 if (m != null)
589 ffMask |= FileMode.TREE.equals(mMode) ? 0x00D : 0x00F;
590
591 // Check whether we have a possible file/folder conflict. Therefore we
592 // need a least one file and one folder.
593 if (((ffMask & 0x222) != 0x000)
594 && (((ffMask & 0x00F) == 0x00D) || ((ffMask & 0x0F0) == 0x0D0) || ((ffMask & 0xF00) == 0xD00))) {
595
596 // There are 3*3*3=27 possible combinations of file/folder
597 // conflicts. Some of them are not-relevant because
598 // they represent no conflict, e.g. 0xFFF, 0xDDD, ... The following
599 // switch processes all relevant cases.
600 switch (ffMask) {
601 case 0xDDF: // 1 2
602 if (f != null && isModifiedSubtree_IndexWorkingtree(name)) {
603 conflict(name, dce, h, m); // 1
604 } else {
605 update(name, mId, mMode); // 2
606 }
607
608 break;
609 case 0xDFD: // 3 4
610 keep(dce);
611 break;
612 case 0xF0D: // 18
613 remove(name);
614 break;
615 case 0xDFF: // 5 5b 6 6b
616 if (equalIdAndMode(iId, iMode, mId, mMode))
617 keep(dce); // 5 6
618 else
619 conflict(name, dce, h, m); // 5b 6b
620 break;
621 case 0xFDD: // 10 11
622 // TODO: make use of tree extension as soon as available in jgit
623 // we would like to do something like
624 // if (!equalIdAndMode(iId, iMode, mId, mMode)
625 // conflict(name, i.getDirCacheEntry(), h, m);
626 // But since we don't know the id of a tree in the index we do
627 // nothing here and wait that conflicts between index and merge
628 // are found later
629 break;
630 case 0xD0F: // 19
631 update(name, mId, mMode);
632 break;
633 case 0xDF0: // conflict without a rule
634 case 0x0FD: // 15
635 conflict(name, dce, h, m);
636 break;
637 case 0xFDF: // 7 8 9
638 if (equalIdAndMode(hId, hMode, mId, mMode)) {
639 if (isModifiedSubtree_IndexWorkingtree(name))
640 conflict(name, dce, h, m); // 8
641 else
642 update(name, mId, mMode); // 7
643 } else
644 conflict(name, dce, h, m); // 9
645 break;
646 case 0xFD0: // keep without a rule
647 keep(dce);
648 break;
649 case 0xFFD: // 12 13 14
650 if (equalIdAndMode(hId, hMode, iId, iMode))
651 if (f != null
652 && f.isModified(dce, true,
653 this.walk.getObjectReader()))
654 conflict(name, dce, h, m); // 13
655 else
656 remove(name); // 12
657 else
658 conflict(name, dce, h, m); // 14
659 break;
660 case 0x0DF: // 16 17
661 if (!isModifiedSubtree_IndexWorkingtree(name))
662 update(name, mId, mMode);
663 else
664 conflict(name, dce, h, m);
665 break;
666 default:
667 keep(dce);
668 }
669 return;
670 }
671
672 // if we have no file at all then there is nothing to do
673 if ((ffMask & 0x222) == 0
674 && (f == null || FileMode.TREE.equals(f.getEntryFileMode())))
675 return;
676
677 if ((ffMask == 0x00F) && f != null && FileMode.TREE.equals(f.getEntryFileMode())) {
678 // File/Directory conflict case #20
679 conflict(name, null, h, m);
680 return;
681 }
682
683 if (i == null) {
684 // Nothing in Index
685 // At least one of Head, Index, Merge is not empty
686 // make sure not to overwrite untracked files
687 if (f != null && !f.isEntryIgnored()) {
688 // A submodule is not a file. We should ignore it
689 if (!FileMode.GITLINK.equals(mMode)) {
690 // a dirty worktree: the index is empty but we have a
691 // workingtree-file
692 if (mId == null
693 || !equalIdAndMode(mId, mMode,
694 f.getEntryObjectId(), f.getEntryFileMode())) {
695 conflict(name, null, h, m);
696 return;
697 }
698 }
699 }
700
701 /**
702 * <pre>
703 * I (index) H M H==M Result
704 * -------------------------------------------
705 * 0 nothing nothing nothing (does not happen)
706 * 1 nothing nothing exists use M
707 * 2 nothing exists nothing remove path from index
708 * 3 nothing exists exists yes keep index if not in initial checkout
709 * , otherwise use M
710 * nothing exists exists no fail
711 * </pre>
712 */
713
714 if (h == null)
715 // Nothing in Head
716 // Nothing in Index
717 // At least one of Head, Index, Merge is not empty
718 // -> only Merge contains something for this path. Use it!
719 // Potentially update the file
720 update(name, mId, mMode); // 1
721 else if (m == null)
722 // Nothing in Merge
723 // Something in Head
724 // Nothing in Index
725 // -> only Head contains something for this path and it should
726 // be deleted. Potentially removes the file!
727 remove(name); // 2
728 else { // 3
729 // Something in Merge
730 // Something in Head
731 // Nothing in Index
732 // -> Head and Merge contain something (maybe not the same) and
733 // in the index there is nothing (e.g. 'git rm ...' was
734 // called before). Ignore the cached deletion and use what we
735 // find in Merge. Potentially updates the file.
736 if (equalIdAndMode(hId, hMode, mId, mMode)) {
737 if (emptyDirCache)
738 update(name, mId, mMode);
739 else
740 keep(dce);
741 } else
742 conflict(name, dce, h, m);
743 }
744 } else {
745 // Something in Index
746 if (h == null) {
747 // Nothing in Head
748 // Something in Index
749 /**
750 * <pre>
751 * clean I==H I==M H M Result
752 * -----------------------------------------------------
753 * 4 yes N/A N/A nothing nothing keep index
754 * 5 no N/A N/A nothing nothing keep index
755 *
756 * 6 yes N/A yes nothing exists keep index
757 * 7 no N/A yes nothing exists keep index
758 * 8 yes N/A no nothing exists fail
759 * 9 no N/A no nothing exists fail
760 * </pre>
761 */
762
763 if (m == null
764 || !isModified_IndexTree(name, iId, iMode, mId, mMode,
765 mergeCommitTree)) {
766 // Merge contains nothing or the same as Index
767 // Nothing in Head
768 // Something in Index
769 if (m==null && walk.isDirectoryFileConflict()) {
770 // Nothing in Merge and current path is part of
771 // File/Folder conflict
772 // Nothing in Head
773 // Something in Index
774 if (dce != null
775 && (f == null || f.isModified(dce, true,
776 this.walk.getObjectReader())))
777 // No file or file is dirty
778 // Nothing in Merge and current path is part of
779 // File/Folder conflict
780 // Nothing in Head
781 // Something in Index
782 // -> File folder conflict and Merge wants this
783 // path to be removed. Since the file is dirty
784 // report a conflict
785 conflict(name, dce, h, m);
786 else
787 // A file is present and file is not dirty
788 // Nothing in Merge and current path is part of
789 // File/Folder conflict
790 // Nothing in Head
791 // Something in Index
792 // -> File folder conflict and Merge wants this path
793 // to be removed. Since the file is not dirty remove
794 // file and index entry
795 remove(name);
796 } else
797 // Something in Merge or current path is not part of
798 // File/Folder conflict
799 // Merge contains nothing or the same as Index
800 // Nothing in Head
801 // Something in Index
802 // -> Merge contains nothing new. Keep the index.
803 keep(dce);
804 } else
805 // Merge contains something and it is not the same as Index
806 // Nothing in Head
807 // Something in Index
808 // -> Index contains something new (different from Head)
809 // and Merge is different from Index. Report a conflict
810 conflict(name, dce, h, m);
811 } else if (m == null) {
812 // Nothing in Merge
813 // Something in Head
814 // Something in Index
815
816 /**
817 * <pre>
818 * clean I==H I==M H M Result
819 * -----------------------------------------------------
820 * 10 yes yes N/A exists nothing remove path from index
821 * 11 no yes N/A exists nothing keep file
822 * 12 yes no N/A exists nothing fail
823 * 13 no no N/A exists nothing fail
824 * </pre>
825 */
826
827 if (iMode == FileMode.GITLINK) {
828 // A submodule in Index
829 // Nothing in Merge
830 // Something in Head
831 // Submodules that disappear from the checkout must
832 // be removed from the index, but not deleted from disk.
833 remove(name);
834 } else {
835 // Something different from a submodule in Index
836 // Nothing in Merge
837 // Something in Head
838 if (!isModified_IndexTree(name, iId, iMode, hId, hMode,
839 headCommitTree)) {
840 // Index contains the same as Head
841 // Something different from a submodule in Index
842 // Nothing in Merge
843 // Something in Head
844 if (f != null
845 && f.isModified(dce, true,
846 this.walk.getObjectReader())) {
847 // file is dirty
848 // Index contains the same as Head
849 // Something different from a submodule in Index
850 // Nothing in Merge
851 // Something in Head
852
853 if (!FileMode.TREE.equals(f.getEntryFileMode())
854 && FileMode.TREE.equals(iMode))
855 // The workingtree contains a file and the index semantically contains a folder.
856 // Git considers the workingtree file as untracked. Just keep the untracked file.
857 return;
858 else
859 // -> file is dirty and tracked but is should be
860 // removed. That's a conflict
861 conflict(name, dce, h, m);
862 } else
863 // file doesn't exist or is clean
864 // Index contains the same as Head
865 // Something different from a submodule in Index
866 // Nothing in Merge
867 // Something in Head
868 // -> Remove from index and delete the file
869 remove(name);
870 } else
871 // Index contains something different from Head
872 // Something different from a submodule in Index
873 // Nothing in Merge
874 // Something in Head
875 // -> Something new is in index (and maybe even on the
876 // filesystem). But Merge wants the path to be removed.
877 // Report a conflict
878 conflict(name, dce, h, m);
879 }
880 } else {
881 // Something in Merge
882 // Something in Head
883 // Something in Index
884 if (!equalIdAndMode(hId, hMode, mId, mMode)
885 && isModified_IndexTree(name, iId, iMode, hId, hMode,
886 headCommitTree)
887 && isModified_IndexTree(name, iId, iMode, mId, mMode,
888 mergeCommitTree))
889 // All three contents in Head, Merge, Index differ from each
890 // other
891 // -> All contents differ. Report a conflict.
892 conflict(name, dce, h, m);
893 else
894 // At least two of the contents of Head, Index, Merge
895 // are the same
896 // Something in Merge
897 // Something in Head
898 // Something in Index
899
900 if (!isModified_IndexTree(name, iId, iMode, hId, hMode,
901 headCommitTree)
902 && isModified_IndexTree(name, iId, iMode, mId, mMode,
903 mergeCommitTree)) {
904 // Head contains the same as Index. Merge differs
905 // Something in Merge
906
907 // For submodules just update the index with the new SHA-1
908 if (dce != null
909 && FileMode.GITLINK.equals(dce.getFileMode())) {
910 // Index and Head contain the same submodule. Merge
911 // differs
912 // Something in Merge
913 // -> Nothing new in index. Move to merge.
914 // Potentially updates the file
915
916 // TODO check that we don't overwrite some unsaved
917 // file content
918 update(name, mId, mMode);
919 } else if (dce != null
920 && (f != null && f.isModified(dce, true,
921 this.walk.getObjectReader()))) {
922 // File exists and is dirty
923 // Head and Index don't contain a submodule
924 // Head contains the same as Index. Merge differs
925 // Something in Merge
926 // -> Merge wants the index and file to be updated
927 // but the file is dirty. Report a conflict
928 conflict(name, dce, h, m);
929 } else {
930 // File doesn't exist or is clean
931 // Head and Index don't contain a submodule
932 // Head contains the same as Index. Merge differs
933 // Something in Merge
934 // -> Standard case when switching between branches:
935 // Nothing new in index but something different in
936 // Merge. Update index and file
937 update(name, mId, mMode);
938 }
939 } else {
940 // Head differs from index or merge is same as index
941 // At least two of the contents of Head, Index, Merge
942 // are the same
943 // Something in Merge
944 // Something in Head
945 // Something in Index
946
947 // Can be formulated as: Either all three states are
948 // equal or Merge is equal to Head or Index and differs
949 // to the other one.
950 // -> In all three cases we don't touch index and file.
951
952 keep(dce);
953 }
954 }
955 }
956 }
957
958 /**
959 * A conflict is detected - add the three different stages to the index
960 * @param path the path of the conflicting entry
961 * @param e the previous index entry
962 * @param h the first tree you want to merge (the HEAD)
963 * @param m the second tree you want to merge
964 */
965 private void conflict(String path, DirCacheEntry e, AbstractTreeIterator h, AbstractTreeIterator m) {
966 conflicts.add(path);
967
968 DirCacheEntry entry;
969 if (e != null) {
970 entry = new DirCacheEntry(e.getPathString(), DirCacheEntry.STAGE_1);
971 entry.copyMetaData(e, true);
972 builder.add(entry);
973 }
974
975 if (h != null && !FileMode.TREE.equals(h.getEntryFileMode())) {
976 entry = new DirCacheEntry(h.getEntryPathString(), DirCacheEntry.STAGE_2);
977 entry.setFileMode(h.getEntryFileMode());
978 entry.setObjectId(h.getEntryObjectId());
979 builder.add(entry);
980 }
981
982 if (m != null && !FileMode.TREE.equals(m.getEntryFileMode())) {
983 entry = new DirCacheEntry(m.getEntryPathString(), DirCacheEntry.STAGE_3);
984 entry.setFileMode(m.getEntryFileMode());
985 entry.setObjectId(m.getEntryObjectId());
986 builder.add(entry);
987 }
988 }
989
990 private void keep(DirCacheEntry e) {
991 if (e != null && !FileMode.TREE.equals(e.getFileMode()))
992 builder.add(e);
993 }
994
995 private void remove(String path) {
996 removed.add(path);
997 }
998
999 private void update(String path, ObjectId mId, FileMode mode) {
1000 if (!FileMode.TREE.equals(mode)) {
1001 updated.put(path, mId);
1002 DirCacheEntry entry = new DirCacheEntry(path, DirCacheEntry.STAGE_0);
1003 entry.setObjectId(mId);
1004 entry.setFileMode(mode);
1005 builder.add(entry);
1006 }
1007 }
1008
1009 /**
1010 * If <code>true</code>, will scan first to see if it's possible to check
1011 * out, otherwise throw {@link CheckoutConflictException}. If
1012 * <code>false</code>, it will silently deal with the problem.
1013 *
1014 * @param failOnConflict
1015 */
1016 public void setFailOnConflict(boolean failOnConflict) {
1017 this.failOnConflict = failOnConflict;
1018 }
1019
1020 /**
1021 * This method implements how to handle conflicts when
1022 * {@link #failOnConflict} is false
1023 *
1024 * @throws CheckoutConflictException
1025 */
1026 private void cleanUpConflicts() throws CheckoutConflictException {
1027 // TODO: couldn't we delete unsaved worktree content here?
1028 for (String c : conflicts) {
1029 File conflict = new File(repo.getWorkTree(), c);
1030 if (!conflict.delete())
1031 throw new CheckoutConflictException(MessageFormat.format(
1032 JGitText.get().cannotDeleteFile, c));
1033 removeEmptyParents(conflict);
1034 }
1035 for (String r : removed) {
1036 File file = new File(repo.getWorkTree(), r);
1037 if (!file.delete())
1038 throw new CheckoutConflictException(
1039 MessageFormat.format(JGitText.get().cannotDeleteFile,
1040 file.getAbsolutePath()));
1041 removeEmptyParents(file);
1042 }
1043 }
1044
1045 /**
1046 * Checks whether the subtree starting at a given path differs between Index and
1047 * workingtree.
1048 *
1049 * @param path
1050 * @return true if the subtrees differ
1051 * @throws CorruptObjectException
1052 * @throws IOException
1053 */
1054 private boolean isModifiedSubtree_IndexWorkingtree(String path)
1055 throws CorruptObjectException, IOException {
1056 try (NameConflictTreeWalk tw = new NameConflictTreeWalk(repo)) {
1057 tw.addTree(new DirCacheIterator(dc));
1058 tw.addTree(new FileTreeIterator(repo));
1059 tw.setRecursive(true);
1060 tw.setFilter(PathFilter.create(path));
1061 DirCacheIterator dcIt;
1062 WorkingTreeIterator wtIt;
1063 while (tw.next()) {
1064 dcIt = tw.getTree(0, DirCacheIterator.class);
1065 wtIt = tw.getTree(1, WorkingTreeIterator.class);
1066 if (dcIt == null || wtIt == null)
1067 return true;
1068 if (wtIt.isModified(dcIt.getDirCacheEntry(), true,
1069 this.walk.getObjectReader())) {
1070 return true;
1071 }
1072 }
1073 return false;
1074 }
1075 }
1076
1077 private boolean isModified_IndexTree(String path, ObjectId iId,
1078 FileMode iMode, ObjectId tId, FileMode tMode, ObjectId rootTree)
1079 throws CorruptObjectException, IOException {
1080 if (iMode != tMode)
1081 return true;
1082 if (FileMode.TREE.equals(iMode)
1083 && (iId == null || ObjectId.zeroId().equals(iId)))
1084 return isModifiedSubtree_IndexTree(path, rootTree);
1085 else
1086 return !equalIdAndMode(iId, iMode, tId, tMode);
1087 }
1088
1089 /**
1090 * Checks whether the subtree starting at a given path differs between Index and
1091 * some tree.
1092 *
1093 * @param path
1094 * @param tree
1095 * the tree to compare
1096 * @return true if the subtrees differ
1097 * @throws CorruptObjectException
1098 * @throws IOException
1099 */
1100 private boolean isModifiedSubtree_IndexTree(String path, ObjectId tree)
1101 throws CorruptObjectException, IOException {
1102 try (NameConflictTreeWalk tw = new NameConflictTreeWalk(repo)) {
1103 tw.addTree(new DirCacheIterator(dc));
1104 tw.addTree(tree);
1105 tw.setRecursive(true);
1106 tw.setFilter(PathFilter.create(path));
1107 while (tw.next()) {
1108 AbstractTreeIterator dcIt = tw.getTree(0,
1109 DirCacheIterator.class);
1110 AbstractTreeIterator treeIt = tw.getTree(1,
1111 AbstractTreeIterator.class);
1112 if (dcIt == null || treeIt == null)
1113 return true;
1114 if (dcIt.getEntryRawMode() != treeIt.getEntryRawMode())
1115 return true;
1116 if (!dcIt.getEntryObjectId().equals(treeIt.getEntryObjectId()))
1117 return true;
1118 }
1119 return false;
1120 }
1121 }
1122
1123 /**
1124 * Updates the file in the working tree with content and mode from an entry
1125 * in the index. The new content is first written to a new temporary file in
1126 * the same directory as the real file. Then that new file is renamed to the
1127 * final filename.
1128 *
1129 * <p>
1130 * TODO: this method works directly on File IO, we may need another
1131 * abstraction (like WorkingTreeIterator). This way we could tell e.g.
1132 * Eclipse that Files in the workspace got changed
1133 * </p>
1134 *
1135 * @param repo
1136 * repository managing the destination work tree.
1137 * @param entry
1138 * the entry containing new mode and content
1139 * @param or
1140 * object reader to use for checkout
1141 * @throws IOException
1142 * @since 3.6
1143 */
1144 public static void checkoutEntry(Repository repo, DirCacheEntry entry,
1145 ObjectReader or) throws IOException {
1146 ObjectLoader ol = or.open(entry.getObjectId());
1147 File f = new File(repo.getWorkTree(), entry.getPathString());
1148 File parentDir = f.getParentFile();
1149 FileUtils.mkdirs(parentDir, true);
1150 FS fs = repo.getFS();
1151 WorkingTreeOptions opt = repo.getConfig().get(WorkingTreeOptions.KEY);
1152 if (entry.getFileMode() == FileMode.SYMLINK
1153 && opt.getSymLinks() == SymLinks.TRUE) {
1154 byte[] bytes = ol.getBytes();
1155 String target = RawParseUtils.decode(bytes);
1156 fs.createSymLink(f, target);
1157 entry.setLength(bytes.length);
1158 entry.setLastModified(fs.lastModified(f));
1159 return;
1160 }
1161
1162 File tmpFile = File.createTempFile(
1163 "._" + f.getName(), null, parentDir); //$NON-NLS-1$
1164 OutputStream channel = new FileOutputStream(tmpFile);
1165 if (opt.getAutoCRLF() == AutoCRLF.TRUE)
1166 channel = new AutoCRLFOutputStream(channel);
1167 try {
1168 ol.copyTo(channel);
1169 } finally {
1170 channel.close();
1171 }
1172 entry.setLength(opt.getAutoCRLF() == AutoCRLF.TRUE ? //
1173 tmpFile.length() // AutoCRLF wants on-disk-size
1174 : (int) ol.getSize());
1175
1176 if (opt.isFileMode() && fs.supportsExecute()) {
1177 if (FileMode.EXECUTABLE_FILE.equals(entry.getRawMode())) {
1178 if (!fs.canExecute(tmpFile))
1179 fs.setExecute(tmpFile, true);
1180 } else {
1181 if (fs.canExecute(tmpFile))
1182 fs.setExecute(tmpFile, false);
1183 }
1184 }
1185 try {
1186 FileUtils.rename(tmpFile, f);
1187 } catch (IOException e) {
1188 throw new IOException(MessageFormat.format(
1189 JGitText.get().renameFileFailed, tmpFile.getPath(),
1190 f.getPath()));
1191 }
1192 entry.setLastModified(f.lastModified());
1193 }
1194
1195 @SuppressWarnings("deprecation")
1196 private static void checkValidPath(CanonicalTreeParser t)
1197 throws InvalidPathException {
1198 ObjectChecker chk = new ObjectChecker()
1199 .setSafeForWindows(SystemReader.getInstance().isWindows())
1200 .setSafeForMacOS(SystemReader.getInstance().isMacOS());
1201 for (CanonicalTreeParser i = t; i != null; i = i.getParent())
1202 checkValidPathSegment(chk, i);
1203 }
1204
1205 /**
1206 * Check if path is a valid path for a checked out file name or ref name.
1207 *
1208 * @param path
1209 * @throws InvalidPathException
1210 * if the path is invalid
1211 * @since 3.3
1212 */
1213 static void checkValidPath(String path) throws InvalidPathException {
1214 try {
1215 SystemReader.getInstance().checkPath(path);
1216 } catch (CorruptObjectException e) {
1217 InvalidPathException p = new InvalidPathException(path);
1218 p.initCause(e);
1219 throw p;
1220 }
1221 }
1222
1223 private static void checkValidPathSegment(ObjectChecker chk,
1224 CanonicalTreeParser t) throws InvalidPathException {
1225 try {
1226 int ptr = t.getNameOffset();
1227 int end = ptr + t.getNameLength();
1228 chk.checkPathSegment(t.getEntryPathBuffer(), ptr, end);
1229 } catch (CorruptObjectException err) {
1230 String path = t.getEntryPathString();
1231 InvalidPathException i = new InvalidPathException(path);
1232 i.initCause(err);
1233 throw i;
1234 }
1235 }
1236 }