Require javadoc on protected methods in jgit core
[egit/imyousuf.git] / org.spearce.jgit / src / org / spearce / jgit / lib / WorkDirCheckout.java
blobd3da74a8122b0e6728ffdf1dd92079517df9b3a7
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, file);
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,
298 File file) throws IOException {
299 ObjectId iId = (i == null ? null : i.getObjectId());
300 ObjectId mId = (m == null ? null : m.getId());
301 ObjectId hId = (h == null ? null : h.getId());
303 String name = (i != null ? i.getName() :
304 (h != null ? h.getFullName() :
305 m.getFullName()));
307 if (i == null) {
309 I (index) H M Result
310 -------------------------------------------------------
311 0 nothing nothing nothing (does not happen)
312 1 nothing nothing exists use M
313 2 nothing exists nothing remove path from index
314 3 nothing exists exists use M */
316 if (h == null) {
317 updated.put(name,mId);
318 } else if (m == null) {
319 removed.add(name);
320 } else {
321 updated.put(name, mId);
323 } else if (h == null) {
325 clean I==H I==M H M Result
326 -----------------------------------------------------
327 4 yes N/A N/A nothing nothing keep index
328 5 no N/A N/A nothing nothing keep index
330 6 yes N/A yes nothing exists keep index
331 7 no N/A yes nothing exists keep index
332 8 yes N/A no nothing exists fail
333 9 no N/A no nothing exists fail */
335 if (m == null || mId.equals(iId)) {
336 if (hasParentBlob(merge, name)) {
337 if (i.isModified(root, true)) {
338 conflicts.add(name);
339 } else {
340 removed.add(name);
343 } else {
344 conflicts.add(name);
346 } else if (m == null) {
348 10 yes yes N/A exists nothing remove path from index
349 11 no yes N/A exists nothing fail
350 12 yes no N/A exists nothing fail
351 13 no no N/A exists nothing fail
354 if (hId.equals(iId)) {
355 if (i.isModified(root, true)) {
356 conflicts.add(name);
357 } else {
358 removed.add(name);
360 } else {
361 conflicts.add(name);
363 } else {
364 if (!hId.equals(mId) && !hId.equals(iId)
365 && !mId.equals(iId)) {
366 conflicts.add(name);
367 } else if (hId.equals(iId) && !mId.equals(iId)) {
368 if (i.isModified(root, true))
369 conflicts.add(name);
370 else updated.put(name, mId);
375 private boolean hasParentBlob(Tree t, String name) throws IOException {
376 if (name.indexOf("/") == -1) return false;
378 String parent = name.substring(0, name.lastIndexOf("/"));
379 if (t.findBlobMember(parent) != null)
380 return true;
381 return hasParentBlob(t, parent);
384 private void checkConflictsWithFile(File file) {
385 if (file.isDirectory()) {
386 ArrayList<String> childFiles = listFiles(file);
387 conflicts.addAll(childFiles);
388 } else {
389 File parent = file.getParentFile();
390 while (!parent.equals(root)) {
391 if (parent.isDirectory())
392 break;
393 if (parent.isFile()) {
394 conflicts.add(Repository.stripWorkDir(root, parent));
395 break;
397 parent = parent.getParentFile();