Remove System.out.println from RevWalkFilterTest
[egit/chris.git] / org.spearce.jgit / src / org / spearce / jgit / lib / WorkDirCheckout.java
blob843522390570c41ac0c08d679fd855cb2f348b84
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>
7 * All rights reserved.
9 * Redistribution and use in source and binary forms, with or
10 * without modification, are permitted provided that the following
11 * conditions are met:
13 * - Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
16 * - Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials provided
19 * with the distribution.
21 * - Neither the name of the Git Development Community nor the
22 * names of its contributors may be used to endorse or promote
23 * products derived from this software without specific prior
24 * written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
27 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
28 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
29 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
31 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
33 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
34 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
35 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
36 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
38 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 package org.spearce.jgit.lib;
43 import java.io.File;
44 import java.io.FileNotFoundException;
45 import java.io.IOException;
46 import java.util.ArrayList;
47 import java.util.HashMap;
49 import org.spearce.jgit.errors.CheckoutConflictException;
50 import org.spearce.jgit.lib.GitIndex.Entry;
52 /**
53 * This class handles checking out one or two trees merging
54 * with the index (actually a tree too).
56 * Three-way merges are no performed. See {@link #setFailOnConflict(boolean)}.
58 public class WorkDirCheckout {
59 Repository repo;
61 File root;
63 GitIndex index;
65 private boolean failOnConflict = true;
67 Tree merge;
70 /**
71 * If <code>true</code>, will scan first to see if it's possible to check out,
72 * otherwise throw {@link CheckoutConflictException}. If <code>false</code>,
73 * it will silently deal with the problem.
74 * @param failOnConflict
76 public void setFailOnConflict(boolean failOnConflict) {
77 this.failOnConflict = failOnConflict;
80 WorkDirCheckout(Repository repo, File workDir,
81 GitIndex oldIndex, GitIndex newIndex) throws IOException {
82 this.repo = repo;
83 this.root = workDir;
84 this.index = oldIndex;
85 this.merge = repo.mapTree(newIndex.writeTree());
88 /**
89 * Create a checkout class for checking out one tree, merging with the index
91 * @param repo
92 * @param root workdir
93 * @param index current index
94 * @param merge tree to check out
96 public WorkDirCheckout(Repository repo, File root,
97 GitIndex index, Tree merge) {
98 this.repo = repo;
99 this.root = root;
100 this.index = index;
101 this.merge = merge;
105 * Create a checkout class for merging and checking our two trees and the index.
107 * @param repo
108 * @param root workdir
109 * @param head
110 * @param index
111 * @param merge
113 public WorkDirCheckout(Repository repo, File root, Tree head, GitIndex index, Tree merge) {
114 this(repo, root, index, merge);
115 this.head = head;
119 * Execute this checkout
121 * @throws IOException
123 public void checkout() throws IOException {
124 if (head == null)
125 prescanOneTree();
126 else prescanTwoTrees();
127 if (!conflicts.isEmpty()) {
128 if (failOnConflict) {
129 String[] entries = conflicts.toArray(new String[0]);
130 throw new CheckoutConflictException(entries);
134 cleanUpConflicts();
135 if (head == null)
136 checkoutOutIndexNoHead();
137 else checkoutTwoTrees();
140 private void checkoutTwoTrees() throws FileNotFoundException, IOException {
141 for (String path : removed) {
142 index.remove(root, new File(root, path));
145 for (java.util.Map.Entry<String, ObjectId> entry : updated.entrySet()) {
146 Entry newEntry = index.addEntry(merge.findBlobMember(entry.getKey()));
147 index.checkoutEntry(root, newEntry);
151 ArrayList<String> conflicts = new ArrayList<String>();
152 ArrayList<String> removed = new ArrayList<String>();
154 Tree head = null;
156 HashMap<String, ObjectId> updated = new HashMap<String, ObjectId>();
158 private void checkoutOutIndexNoHead() throws IOException {
159 new IndexTreeWalker(index, merge, root, new AbstractIndexTreeVisitor() {
160 public void visitEntry(TreeEntry m, Entry i, File f) throws IOException {
161 if (m == null) {
162 index.remove(root, f);
163 return;
166 boolean needsCheckout = false;
167 if (i == null)
168 needsCheckout = true;
169 else if (i.getObjectId().equals(m.getId())) {
170 if (i.isModified(root, true))
171 needsCheckout = true;
172 } else needsCheckout = true;
174 if (needsCheckout) {
175 Entry newEntry = index.addEntry(m);
176 index.checkoutEntry(root, newEntry);
179 }).walk();
182 private void cleanUpConflicts() throws CheckoutConflictException {
183 for (String c : conflicts) {
184 File conflict = new File(root, c);
185 if (!conflict.delete())
186 throw new CheckoutConflictException("Cannot delete file: " + c);
187 removeEmptyParents(conflict);
189 for (String r : removed) {
190 File file = new File(root, r);
191 file.delete();
192 removeEmptyParents(file);
196 private void removeEmptyParents(File f) {
197 File parentFile = f.getParentFile();
198 while (!parentFile.equals(root)) {
199 if (parentFile.list().length == 0)
200 parentFile.delete();
201 else break;
203 parentFile = parentFile.getParentFile();
207 void prescanOneTree() throws IOException {
208 new IndexTreeWalker(index, merge, root, new AbstractIndexTreeVisitor() {
209 public void visitEntry(TreeEntry m, Entry i, File file) throws IOException {
210 if (m != null) {
211 if (!file.isFile()) {
212 checkConflictsWithFile(file);
214 } else {
215 if (file.exists()) {
216 removed.add(i.getName());
217 conflicts.remove(i.getName());
221 }).walk();
222 conflicts.removeAll(removed);
225 private ArrayList<String> listFiles(File file) {
226 ArrayList<String> list = new ArrayList<String>();
227 listFiles(file, list);
228 return list;
231 private void listFiles(File dir, ArrayList<String> list) {
232 for (File f : dir.listFiles()) {
233 if (f.isDirectory())
234 listFiles(f, list);
235 else {
236 list.add(Repository.stripWorkDir(root, f));
242 * @return a list of conflicts created by this checkout
244 public ArrayList<String> getConflicts() {
245 return conflicts;
249 * @return a list of all files removed by this checkout
251 public ArrayList<String> getRemoved() {
252 return removed;
255 void prescanTwoTrees() throws IOException {
256 new IndexTreeWalker(index, head, merge, root, new AbstractIndexTreeVisitor() {
257 public void visitEntry(TreeEntry treeEntry, TreeEntry auxEntry,
258 Entry indexEntry, File file) throws IOException {
259 if (treeEntry instanceof Tree || auxEntry instanceof Tree) {
260 throw new IllegalArgumentException("Can't pass me a tree!");
262 processEntry(treeEntry, auxEntry, indexEntry);
265 @Override
266 public void finishVisitTree(Tree tree, Tree auxTree, String curDir) throws IOException {
267 if (curDir.length() == 0) return;
269 if (auxTree != null) {
270 if (index.getEntry(curDir) != null)
271 removed.add(curDir);
275 }).walk();
277 // if there's a conflict, don't list it under
278 // to-be-removed, since that messed up our next
279 // section
280 removed.removeAll(conflicts);
282 for (String path : updated.keySet()) {
283 if (index.getEntry(path) == null) {
284 File file = new File(root, path);
285 if (file.isFile())
286 conflicts.add(path);
287 else if (file.isDirectory()) {
288 checkConflictsWithFile(file);
294 conflicts.removeAll(removed);
297 void processEntry(TreeEntry h, TreeEntry m, Entry i) throws IOException {
298 ObjectId iId = (i == null ? null : i.getObjectId());
299 ObjectId mId = (m == null ? null : m.getId());
300 ObjectId hId = (h == null ? null : h.getId());
302 String name = (i != null ? i.getName() :
303 (h != null ? h.getFullName() :
304 m.getFullName()));
306 if (i == null) {
308 I (index) H M Result
309 -------------------------------------------------------
310 0 nothing nothing nothing (does not happen)
311 1 nothing nothing exists use M
312 2 nothing exists nothing remove path from index
313 3 nothing exists exists use M */
315 if (h == null) {
316 updated.put(name,mId);
317 } else if (m == null) {
318 removed.add(name);
319 } else {
320 updated.put(name, mId);
322 } else if (h == null) {
324 clean I==H I==M H M Result
325 -----------------------------------------------------
326 4 yes N/A N/A nothing nothing keep index
327 5 no N/A N/A nothing nothing keep index
329 6 yes N/A yes nothing exists keep index
330 7 no N/A yes nothing exists keep index
331 8 yes N/A no nothing exists fail
332 9 no N/A no nothing exists fail */
334 if (m == null || mId.equals(iId)) {
335 if (hasParentBlob(merge, name)) {
336 if (i.isModified(root, true)) {
337 conflicts.add(name);
338 } else {
339 removed.add(name);
342 } else {
343 conflicts.add(name);
345 } else if (m == null) {
347 10 yes yes N/A exists nothing remove path from index
348 11 no yes N/A exists nothing fail
349 12 yes no N/A exists nothing fail
350 13 no no N/A exists nothing fail
353 if (hId.equals(iId)) {
354 if (i.isModified(root, true)) {
355 conflicts.add(name);
356 } else {
357 removed.add(name);
359 } else {
360 conflicts.add(name);
362 } else {
363 if (!hId.equals(mId) && !hId.equals(iId)
364 && !mId.equals(iId)) {
365 conflicts.add(name);
366 } else if (hId.equals(iId) && !mId.equals(iId)) {
367 if (i.isModified(root, true))
368 conflicts.add(name);
369 else updated.put(name, mId);
374 private boolean hasParentBlob(Tree t, String name) throws IOException {
375 if (name.indexOf("/") == -1) return false;
377 String parent = name.substring(0, name.lastIndexOf("/"));
378 if (t.findBlobMember(parent) != null)
379 return true;
380 return hasParentBlob(t, parent);
383 private void checkConflictsWithFile(File file) {
384 if (file.isDirectory()) {
385 ArrayList<String> childFiles = listFiles(file);
386 conflicts.addAll(childFiles);
387 } else {
388 File parent = file.getParentFile();
389 while (!parent.equals(root)) {
390 if (parent.isDirectory())
391 break;
392 if (parent.isFile()) {
393 conflicts.add(Repository.stripWorkDir(root, parent));
394 break;
396 parent = parent.getParentFile();