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
;
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 public class WorkDirCheckout
{
29 protected Repository repo
;
33 protected GitIndex index
;
35 private boolean failOnConflict
= true;
41 * If <code>true</code>, will scan first to see if it's possible to check out,
42 * otherwise throw {@link CheckoutConflictException}. If <code>false</code>,
43 * it will silently deal with the problem.
44 * @param failOnConflict
46 public void setFailOnConflict(boolean failOnConflict
) {
47 this.failOnConflict
= failOnConflict
;
50 WorkDirCheckout(Repository repo
, File workDir
,
51 GitIndex oldIndex
, GitIndex newIndex
) throws IOException
{
54 this.index
= oldIndex
;
55 this.merge
= repo
.mapTree(newIndex
.writeTree());
58 public WorkDirCheckout(Repository repo
, File root
,
59 GitIndex index
, Tree merge
) {
66 public WorkDirCheckout(Repository repo
, File root
, Tree head
, GitIndex index
, Tree merge
) {
67 this(repo
, root
, index
, merge
);
71 public void checkout() throws IOException
{
74 else prescanTwoTrees();
75 if (!conflicts
.isEmpty()) {
77 String
[] entries
= conflicts
.toArray(new String
[0]);
78 throw new CheckoutConflictException(entries
);
84 checkoutOutIndexNoHead();
85 else checkoutTwoTrees();
88 private void checkoutTwoTrees() throws FileNotFoundException
, IOException
{
89 for (String path
: removed
) {
90 index
.remove(root
, new File(root
, path
));
93 for (java
.util
.Map
.Entry
<String
, ObjectId
> entry
: updated
.entrySet()) {
94 Entry newEntry
= index
.addEntry(merge
.findBlobMember(entry
.getKey()));
95 index
.checkoutEntry(root
, newEntry
);
99 ArrayList
<String
> conflicts
= new ArrayList
<String
>();;
100 ArrayList
<String
> removed
= new ArrayList
<String
>();
102 protected Tree head
= null;
104 protected HashMap
<String
, ObjectId
> updated
= new HashMap
<String
, ObjectId
>();
106 private void checkoutOutIndexNoHead() throws IOException
{
107 new IndexTreeWalker(index
, merge
, root
, new AbstractIndexTreeVisitor() {
108 public void visitEntry(TreeEntry m
, Entry i
, File f
) throws IOException
{
110 index
.remove(root
, f
);
114 boolean needsCheckout
= false;
116 needsCheckout
= true;
117 else if (i
.getObjectId().equals(m
.getId())) {
118 if (i
.isModified(root
, true))
119 needsCheckout
= true;
120 } else needsCheckout
= true;
123 Entry newEntry
= index
.addEntry(m
);
124 index
.checkoutEntry(root
, newEntry
);
130 private void cleanUpConflicts() throws CheckoutConflictException
{
131 for (String c
: conflicts
) {
132 File conflict
= new File(root
, c
);
133 if (!conflict
.delete())
134 throw new CheckoutConflictException("Cannot delete file: " + c
);
135 removeEmptyParents(conflict
);
137 for (String r
: removed
) {
138 File file
= new File(root
, r
);
140 removeEmptyParents(file
);
144 private void removeEmptyParents(File f
) {
145 File parentFile
= f
.getParentFile();
146 while (!parentFile
.equals(root
)) {
147 if (parentFile
.list().length
== 0)
151 parentFile
= parentFile
.getParentFile();
155 void prescanOneTree() throws IOException
{
156 new IndexTreeWalker(index
, merge
, root
, new AbstractIndexTreeVisitor() {
157 public void visitEntry(TreeEntry m
, Entry i
, File file
) throws IOException
{
159 if (!file
.isFile()) {
160 checkConflictsWithFile(file
);
164 removed
.add(i
.getName());
165 conflicts
.remove(i
.getName());
170 conflicts
.removeAll(removed
);
173 private ArrayList
<String
> listFiles(File file
) {
174 ArrayList
<String
> list
= new ArrayList
<String
>();
175 listFiles(file
, list
);
179 private void listFiles(File dir
, ArrayList
<String
> list
) {
180 for (File f
: dir
.listFiles()) {
184 list
.add(stripWorkdirFront(f
));
189 private String
stripWorkdirFront(File f
) {
190 return f
.getPath().substring(root
.getPath().length() + 1);
193 public ArrayList
<String
> getConflicts() {
197 public ArrayList
<String
> getRemoved() {
201 void prescanTwoTrees() throws IOException
{
202 new IndexTreeWalker(index
, head
, merge
, root
, new AbstractIndexTreeVisitor() {
203 public void visitEntry(TreeEntry treeEntry
, TreeEntry auxEntry
,
204 Entry indexEntry
, File file
) throws IOException
{
205 if (treeEntry
instanceof Tree
|| auxEntry
instanceof Tree
) {
206 throw new IllegalArgumentException("Can't pass me a tree!");
208 processEntry(treeEntry
, auxEntry
, indexEntry
, file
);
212 public void finishVisitTree(Tree tree
, Tree auxTree
, int i
,
213 String curDir
) throws IOException
{
214 if (curDir
.length() == 0) return;
216 if (auxTree
!= null && i
== 0) {
217 if (index
.getEntry(curDir
) != null)
224 // if there's a conflict, don't list it under
225 // to-be-removed, since that messed up our next
227 removed
.removeAll(conflicts
);
229 for (String path
: updated
.keySet()) {
230 if (index
.getEntry(path
) == null) {
231 File file
= new File(root
, path
);
234 else if (file
.isDirectory()) {
235 checkConflictsWithFile(file
);
241 conflicts
.removeAll(removed
);
244 protected void processEntry(TreeEntry h
, TreeEntry m
, Entry i
,
245 File file
) throws IOException
{
246 ObjectId iId
= (i
== null ?
null : i
.getObjectId());
247 ObjectId mId
= (m
== null ?
null : m
.getId());
248 ObjectId hId
= (h
== null ?
null : h
.getId());
250 String name
= (i
!= null ? i
.getName() :
251 (h
!= null ? h
.getFullName() :
257 -------------------------------------------------------
258 0 nothing nothing nothing (does not happen)
259 1 nothing nothing exists use M
260 2 nothing exists nothing remove path from index
261 3 nothing exists exists use M */
264 updated
.put(name
,mId
);
265 } else if (m
== null) {
268 updated
.put(name
, mId
);
270 } else if (h
== null) {
272 clean I==H I==M H M Result
273 -----------------------------------------------------
274 4 yes N/A N/A nothing nothing keep index
275 5 no N/A N/A nothing nothing keep index
277 6 yes N/A yes nothing exists keep index
278 7 no N/A yes nothing exists keep index
279 8 yes N/A no nothing exists fail
280 9 no N/A no nothing exists fail */
282 if (m
== null || mId
.equals(iId
)) {
283 if (hasParentBlob(merge
, name
)) {
284 if (i
.isModified(root
, true)) {
293 } else if (m
== null) {
295 10 yes yes N/A exists nothing remove path from index
296 11 no yes N/A exists nothing fail
297 12 yes no N/A exists nothing fail
298 13 no no N/A exists nothing fail
301 if (hId
.equals(iId
)) {
302 if (i
.isModified(root
, true)) {
311 if (!hId
.equals(mId
) && !hId
.equals(iId
)
312 && !mId
.equals(iId
)) {
314 } else if (hId
.equals(iId
) && !mId
.equals(iId
)) {
315 if (i
.isModified(root
, true))
317 else updated
.put(name
, mId
);
322 private boolean hasParentBlob(Tree t
, String name
) throws IOException
{
323 if (name
.indexOf("/") == -1) return false;
325 String parent
= name
.substring(0, name
.lastIndexOf("/"));
326 if (t
.findBlobMember(parent
) != null)
328 return hasParentBlob(t
, parent
);
331 private void checkConflictsWithFile(File file
) {
332 if (file
.isDirectory()) {
333 ArrayList
<String
> childFiles
= listFiles(file
);
334 conflicts
.addAll(childFiles
);
336 File parent
= file
.getParentFile();
337 while (!parent
.equals(root
)) {
338 if (parent
.isDirectory())
340 if (parent
.isFile()) {
341 conflicts
.add(stripWorkdirFront(parent
));
344 parent
= parent
.getParentFile();