Always use a single WindowCache for the entire JVM
[egit/imyousuf.git] / org.spearce.jgit / src / org / spearce / jgit / lib / WorkDirCheckout.java
blob4c5152f0568ca310970ab3e6ab378a925ade011d
1 /*
2 * Copyright (C) 2007 Dave Watson <dwatson@mimvista.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License, version 2, as published by the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
17 package org.spearce.jgit.lib;
19 import java.io.File;
20 import java.io.FileNotFoundException;
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.HashMap;
25 import org.spearce.jgit.errors.CheckoutConflictException;
26 import org.spearce.jgit.lib.GitIndex.Entry;
28 /**
29 * This class handles checking out one or two trees merging
30 * with the index (actually a tree too).
32 * Three-way merges are no performed. See {@link #setFailOnConflict(boolean)}.
34 public class WorkDirCheckout {
35 protected Repository repo;
37 protected File root;
39 protected GitIndex index;
41 private boolean failOnConflict = true;
43 protected Tree merge;
46 /**
47 * If <code>true</code>, will scan first to see if it's possible to check out,
48 * otherwise throw {@link CheckoutConflictException}. If <code>false</code>,
49 * it will silently deal with the problem.
50 * @param failOnConflict
52 public void setFailOnConflict(boolean failOnConflict) {
53 this.failOnConflict = failOnConflict;
56 WorkDirCheckout(Repository repo, File workDir,
57 GitIndex oldIndex, GitIndex newIndex) throws IOException {
58 this.repo = repo;
59 this.root = workDir;
60 this.index = oldIndex;
61 this.merge = repo.mapTree(newIndex.writeTree());
64 /**
65 * Create a checkout class for checking out one tree, merging with the index
67 * @param repo
68 * @param root workdir
69 * @param index current index
70 * @param merge tree to check out
72 public WorkDirCheckout(Repository repo, File root,
73 GitIndex index, Tree merge) {
74 this.repo = repo;
75 this.root = root;
76 this.index = index;
77 this.merge = merge;
80 /**
81 * Create a checkout class for merging and checking our two trees and the index.
83 * @param repo
84 * @param root workdir
85 * @param head
86 * @param index
87 * @param merge
89 public WorkDirCheckout(Repository repo, File root, Tree head, GitIndex index, Tree merge) {
90 this(repo, root, index, merge);
91 this.head = head;
94 /**
95 * Execute this checkout
97 * @throws IOException
99 public void checkout() throws IOException {
100 if (head == null)
101 prescanOneTree();
102 else prescanTwoTrees();
103 if (!conflicts.isEmpty()) {
104 if (failOnConflict) {
105 String[] entries = conflicts.toArray(new String[0]);
106 throw new CheckoutConflictException(entries);
110 cleanUpConflicts();
111 if (head == null)
112 checkoutOutIndexNoHead();
113 else checkoutTwoTrees();
116 private void checkoutTwoTrees() throws FileNotFoundException, IOException {
117 for (String path : removed) {
118 index.remove(root, new File(root, path));
121 for (java.util.Map.Entry<String, ObjectId> entry : updated.entrySet()) {
122 Entry newEntry = index.addEntry(merge.findBlobMember(entry.getKey()));
123 index.checkoutEntry(root, newEntry);
127 ArrayList<String> conflicts = new ArrayList<String>();
128 ArrayList<String> removed = new ArrayList<String>();
130 protected Tree head = null;
132 protected HashMap<String, ObjectId> updated = new HashMap<String, ObjectId>();
134 private void checkoutOutIndexNoHead() throws IOException {
135 new IndexTreeWalker(index, merge, root, new AbstractIndexTreeVisitor() {
136 public void visitEntry(TreeEntry m, Entry i, File f) throws IOException {
137 if (m == null) {
138 index.remove(root, f);
139 return;
142 boolean needsCheckout = false;
143 if (i == null)
144 needsCheckout = true;
145 else if (i.getObjectId().equals(m.getId())) {
146 if (i.isModified(root, true))
147 needsCheckout = true;
148 } else needsCheckout = true;
150 if (needsCheckout) {
151 Entry newEntry = index.addEntry(m);
152 index.checkoutEntry(root, newEntry);
155 }).walk();
158 private void cleanUpConflicts() throws CheckoutConflictException {
159 for (String c : conflicts) {
160 File conflict = new File(root, c);
161 if (!conflict.delete())
162 throw new CheckoutConflictException("Cannot delete file: " + c);
163 removeEmptyParents(conflict);
165 for (String r : removed) {
166 File file = new File(root, r);
167 file.delete();
168 removeEmptyParents(file);
172 private void removeEmptyParents(File f) {
173 File parentFile = f.getParentFile();
174 while (!parentFile.equals(root)) {
175 if (parentFile.list().length == 0)
176 parentFile.delete();
177 else break;
179 parentFile = parentFile.getParentFile();
183 void prescanOneTree() throws IOException {
184 new IndexTreeWalker(index, merge, root, new AbstractIndexTreeVisitor() {
185 public void visitEntry(TreeEntry m, Entry i, File file) throws IOException {
186 if (m != null) {
187 if (!file.isFile()) {
188 checkConflictsWithFile(file);
190 } else {
191 if (file.exists()) {
192 removed.add(i.getName());
193 conflicts.remove(i.getName());
197 }).walk();
198 conflicts.removeAll(removed);
201 private ArrayList<String> listFiles(File file) {
202 ArrayList<String> list = new ArrayList<String>();
203 listFiles(file, list);
204 return list;
207 private void listFiles(File dir, ArrayList<String> list) {
208 for (File f : dir.listFiles()) {
209 if (f.isDirectory())
210 listFiles(f, list);
211 else {
212 list.add(Repository.stripWorkDir(root, f));
218 * @return a list of conflicts created by this checkout
220 public ArrayList<String> getConflicts() {
221 return conflicts;
225 * @return a list of all files removed by this checkout
227 public ArrayList<String> getRemoved() {
228 return removed;
231 void prescanTwoTrees() throws IOException {
232 new IndexTreeWalker(index, head, merge, root, new AbstractIndexTreeVisitor() {
233 public void visitEntry(TreeEntry treeEntry, TreeEntry auxEntry,
234 Entry indexEntry, File file) throws IOException {
235 if (treeEntry instanceof Tree || auxEntry instanceof Tree) {
236 throw new IllegalArgumentException("Can't pass me a tree!");
238 processEntry(treeEntry, auxEntry, indexEntry, file);
241 @Override
242 public void finishVisitTree(Tree tree, Tree auxTree, String curDir) throws IOException {
243 if (curDir.length() == 0) return;
245 if (auxTree != null) {
246 if (index.getEntry(curDir) != null)
247 removed.add(curDir);
251 }).walk();
253 // if there's a conflict, don't list it under
254 // to-be-removed, since that messed up our next
255 // section
256 removed.removeAll(conflicts);
258 for (String path : updated.keySet()) {
259 if (index.getEntry(path) == null) {
260 File file = new File(root, path);
261 if (file.isFile())
262 conflicts.add(path);
263 else if (file.isDirectory()) {
264 checkConflictsWithFile(file);
270 conflicts.removeAll(removed);
273 protected void processEntry(TreeEntry h, TreeEntry m, Entry i,
274 File file) throws IOException {
275 ObjectId iId = (i == null ? null : i.getObjectId());
276 ObjectId mId = (m == null ? null : m.getId());
277 ObjectId hId = (h == null ? null : h.getId());
279 String name = (i != null ? i.getName() :
280 (h != null ? h.getFullName() :
281 m.getFullName()));
283 if (i == null) {
285 I (index) H M Result
286 -------------------------------------------------------
287 0 nothing nothing nothing (does not happen)
288 1 nothing nothing exists use M
289 2 nothing exists nothing remove path from index
290 3 nothing exists exists use M */
292 if (h == null) {
293 updated.put(name,mId);
294 } else if (m == null) {
295 removed.add(name);
296 } else {
297 updated.put(name, mId);
299 } else if (h == null) {
301 clean I==H I==M H M Result
302 -----------------------------------------------------
303 4 yes N/A N/A nothing nothing keep index
304 5 no N/A N/A nothing nothing keep index
306 6 yes N/A yes nothing exists keep index
307 7 no N/A yes nothing exists keep index
308 8 yes N/A no nothing exists fail
309 9 no N/A no nothing exists fail */
311 if (m == null || mId.equals(iId)) {
312 if (hasParentBlob(merge, name)) {
313 if (i.isModified(root, true)) {
314 conflicts.add(name);
315 } else {
316 removed.add(name);
319 } else {
320 conflicts.add(name);
322 } else if (m == null) {
324 10 yes yes N/A exists nothing remove path from index
325 11 no yes N/A exists nothing fail
326 12 yes no N/A exists nothing fail
327 13 no no N/A exists nothing fail
330 if (hId.equals(iId)) {
331 if (i.isModified(root, true)) {
332 conflicts.add(name);
333 } else {
334 removed.add(name);
336 } else {
337 conflicts.add(name);
339 } else {
340 if (!hId.equals(mId) && !hId.equals(iId)
341 && !mId.equals(iId)) {
342 conflicts.add(name);
343 } else if (hId.equals(iId) && !mId.equals(iId)) {
344 if (i.isModified(root, true))
345 conflicts.add(name);
346 else updated.put(name, mId);
351 private boolean hasParentBlob(Tree t, String name) throws IOException {
352 if (name.indexOf("/") == -1) return false;
354 String parent = name.substring(0, name.lastIndexOf("/"));
355 if (t.findBlobMember(parent) != null)
356 return true;
357 return hasParentBlob(t, parent);
360 private void checkConflictsWithFile(File file) {
361 if (file.isDirectory()) {
362 ArrayList<String> childFiles = listFiles(file);
363 conflicts.addAll(childFiles);
364 } else {
365 File parent = file.getParentFile();
366 while (!parent.equals(root)) {
367 if (parent.isDirectory())
368 break;
369 if (parent.isFile()) {
370 conflicts.add(Repository.stripWorkDir(root, parent));
371 break;
373 parent = parent.getParentFile();