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
18 package org
.spearce
.jgit
.lib
;
20 import java
.io
.ByteArrayInputStream
;
22 import java
.io
.IOException
;
23 import java
.io
.InputStream
;
24 import java
.util
.HashMap
;
26 import org
.spearce
.jgit
.errors
.CheckoutConflictException
;
28 public class ReadTreeTest
extends RepositoryTestCase
{
32 private GitIndex index
;
33 private WorkDirCheckout readTree
;
34 // Each of these rules are from the read-tree manpage
35 // go there to see what they mean.
36 // Rule 0 is left out for obvious reasons :)
37 public void testRules1thru3_NoIndexEntry() throws IOException
{
38 GitIndex index
= new GitIndex(db
);
40 Tree head
= new Tree(db
);
41 FileTreeEntry headFile
= head
.addFile("foo");
42 ObjectId objectId
= new ObjectId("ba78e065e2c261d4f7b8f42107588051e87e18e9");
43 headFile
.setId(objectId
);
44 Tree merge
= new Tree(db
);
46 WorkDirCheckout readTree
= new WorkDirCheckout(db
, trash
, head
, index
, merge
);
47 readTree
.prescanTwoTrees();
49 assertTrue(readTree
.removed
.contains("foo"));
51 readTree
= new WorkDirCheckout(db
, trash
, merge
, index
, head
);
52 readTree
.prescanTwoTrees();
54 assertEquals(objectId
, readTree
.updated
.get("foo"));
56 ObjectId anotherId
= new ObjectId("ba78e065e2c261d4f7b8f42107588051e87e18ee");
57 merge
.addFile("foo").setId(anotherId
);
59 readTree
= new WorkDirCheckout(db
, trash
, head
, index
, merge
);
60 readTree
.prescanTwoTrees();
62 assertEquals(anotherId
, readTree
.updated
.get("foo"));
65 void setupCase(HashMap
<String
, String
> headEntries
,
66 HashMap
<String
, String
> mergeEntries
,
67 HashMap
<String
, String
> indexEntries
) throws IOException
{
68 head
= buildTree(headEntries
);
69 merge
= buildTree(mergeEntries
);
70 index
= buildIndex(indexEntries
);
73 private GitIndex
buildIndex(HashMap
<String
, String
> indexEntries
) throws IOException
{
74 GitIndex index
= new GitIndex(db
);
76 if (indexEntries
== null)
78 for (java
.util
.Map
.Entry
<String
,String
> e
: indexEntries
.entrySet()) {
79 index
.add(trash
, writeTrashFile(e
.getKey(), e
.getValue())).forceRecheck();
85 private Tree
buildTree(HashMap
<String
, String
> headEntries
) throws IOException
{
86 Tree tree
= new Tree(db
);
88 if (headEntries
== null)
90 for (java
.util
.Map
.Entry
<String
,String
> e
: headEntries
.entrySet()) {
91 tree
.addFile(e
.getKey()).setId(genSha1(e
.getValue()));
97 ObjectId
genSha1(String data
) {
98 InputStream is
= new ByteArrayInputStream(data
.getBytes());
99 ObjectWriter objectWriter
= new ObjectWriter(db
);
101 return objectWriter
.writeObject(Constants
.OBJ_BLOB
, Constants
.TYPE_BLOB
, data
.getBytes().length
, is
,
103 } catch (IOException e
) {
109 private WorkDirCheckout
go() throws IOException
{
110 readTree
= new WorkDirCheckout(db
, trash
, head
, index
, merge
);
111 readTree
.prescanTwoTrees();
115 // for these rules, they all have clean yes/no options
116 // but it doesn't matter if the entry is clean or not
117 // so we can just ignore the state in the filesystem entirely
118 public void testRules4thru13_IndexEntryNotInHead() throws IOException
{
120 HashMap
<String
, String
> idxMap
;
122 idxMap
= new HashMap
<String
, String
>();
123 idxMap
.put("foo", "foo");
124 setupCase(null, null, idxMap
);
127 assertTrue(readTree
.updated
.isEmpty());
128 assertTrue(readTree
.removed
.isEmpty());
129 assertTrue(readTree
.conflicts
.isEmpty());
132 idxMap
= new HashMap
<String
, String
>();
133 idxMap
.put("foo", "foo");
134 setupCase(null, idxMap
, idxMap
);
140 HashMap
<String
, String
> mergeMap
;
141 mergeMap
= new HashMap
<String
, String
>();
143 mergeMap
.put("foo", "merge");
144 setupCase(null, mergeMap
, idxMap
);
147 assertTrue(readTree
.updated
.isEmpty());
148 assertTrue(readTree
.removed
.isEmpty());
149 assertTrue(readTree
.conflicts
.contains("foo"));
153 HashMap
<String
, String
> headMap
= new HashMap
<String
, String
>();
154 headMap
.put("foo", "foo");
155 setupCase(headMap
, null, idxMap
);
158 assertTrue(readTree
.removed
.contains("foo"));
159 assertTrue(readTree
.updated
.isEmpty());
160 assertTrue(readTree
.conflicts
.isEmpty());
163 setupCase(headMap
, null, idxMap
);
164 new File(trash
, "foo").delete();
165 writeTrashFile("foo", "bar");
166 index
.getMembers()[0].forceRecheck();
169 assertTrue(readTree
.removed
.isEmpty());
170 assertTrue(readTree
.updated
.isEmpty());
171 assertTrue(readTree
.conflicts
.contains("foo"));
174 headMap
.put("foo", "head");
175 setupCase(headMap
, null, idxMap
);
178 assertTrue(readTree
.removed
.isEmpty());
179 assertTrue(readTree
.updated
.isEmpty());
180 assertTrue(readTree
.conflicts
.contains("foo"));
183 setupCase(headMap
, headMap
, idxMap
);
189 setupCase(headMap
, mergeMap
, idxMap
); go();
190 assertTrue(readTree
.conflicts
.contains("foo"));
193 setupCase(headMap
, idxMap
, idxMap
); go();
197 setupCase(idxMap
, mergeMap
, idxMap
); go();
198 assertTrue(readTree
.updated
.containsKey("foo"));
201 setupCase(idxMap
, mergeMap
, idxMap
);
202 new File(trash
, "foo").delete();
203 writeTrashFile("foo", "bar");
204 index
.getMembers()[0].forceRecheck();
206 assertTrue(readTree
.conflicts
.contains("foo"));
209 private void assertAllEmpty() {
210 assertTrue(readTree
.removed
.isEmpty());
211 assertTrue(readTree
.updated
.isEmpty());
212 assertTrue(readTree
.conflicts
.isEmpty());
215 public void testDirectoryFileSimple() throws IOException
{
216 index
= new GitIndex(db
);
217 index
.add(trash
, writeTrashFile("DF", "DF"));
218 Tree treeDF
= db
.mapTree(index
.writeTree());
220 recursiveDelete(new File(trash
, "DF"));
221 index
= new GitIndex(db
);
222 index
.add(trash
, writeTrashFile("DF/DF", "DF/DF"));
223 Tree treeDFDF
= db
.mapTree(index
.writeTree());
225 index
= new GitIndex(db
);
226 recursiveDelete(new File(trash
, "DF"));
228 index
.add(trash
, writeTrashFile("DF", "DF"));
229 readTree
= new WorkDirCheckout(db
, trash
, treeDF
, index
, treeDFDF
);
230 readTree
.prescanTwoTrees();
232 assertTrue(readTree
.removed
.contains("DF"));
233 assertTrue(readTree
.updated
.containsKey("DF/DF"));
235 recursiveDelete(new File(trash
, "DF"));
236 index
= new GitIndex(db
);
237 index
.add(trash
, writeTrashFile("DF/DF", "DF/DF"));
239 readTree
= new WorkDirCheckout(db
, trash
, treeDFDF
, index
, treeDF
);
240 readTree
.prescanTwoTrees();
241 assertTrue(readTree
.removed
.contains("DF/DF"));
242 assertTrue(readTree
.updated
.containsKey("DF"));
246 * Directory/File Conflict cases:
247 * It's entirely possible that in practice a number of these may be equivalent
248 * to the cases described in git-read-tree.txt. As long as it does the right thing,
249 * that's all I care about. These are basically reverse-engineered from
250 * what git currently does. If there are tests for these in git, it's kind of
251 * hard to track them all down...
253 * H I M Clean H==M H==I I==M Result
254 * ------------------------------------------------------------------
255 *1 D D F Y N Y N Update
256 *2 D D F N N Y N Conflict
257 *3 D F D Y N N Update
258 *4 D F D N N N Update
259 *5 D F F Y N N Y Keep
260 *6 D F F N N N Y Keep
261 *7 F D F Y Y N N Update
262 *8 F D F N Y N N Conflict
263 *9 F D F Y N N N Update
265 *11 F D D N N N Conflict
266 *12 F F D Y N Y N Update
267 *13 F F D N N Y N Conflict
268 *14 F F D N N N Conflict
269 *15 0 F D N N N Conflict
270 *16 0 D F Y N N N Update
271 *17 0 D F N N N Conflict
276 public void testDirectoryFileConflicts_1() throws Exception
{
278 doit(mk("DF/DF"), mk("DF"), mk("DF/DF"));
281 assertRemoved("DF/DF");
284 public void testDirectoryFileConflicts_2() throws Exception
{
286 setupCase(mk("DF/DF"), mk("DF"), mk("DF/DF"));
287 writeTrashFile("DF/DF", "different");
289 assertConflict("DF/DF");
293 public void testDirectoryFileConflicts_3() throws Exception
{
294 // 3 - the first to break!
295 doit(mk("DF/DF"), mk("DF/DF"), mk("DF"));
296 assertUpdated("DF/DF");
300 public void testDirectoryFileConflicts_4() throws Exception
{
301 // 4 (basically same as 3, just with H and M different)
302 doit(mk("DF/DF"), mkmap("DF/DF", "foo"), mk("DF"));
303 assertUpdated("DF/DF");
308 public void testDirectoryFileConflicts_5() throws Exception
{
310 doit(mk("DF/DF"), mk("DF"), mk("DF"));
311 assertRemoved("DF/DF");
315 public void testDirectoryFileConflicts_6() throws Exception
{
317 setupCase(mk("DF/DF"), mk("DF"), mk("DF"));
318 writeTrashFile("DF", "different");
320 assertRemoved("DF/DF");
323 public void testDirectoryFileConflicts_7() throws Exception
{
325 doit(mk("DF"), mk("DF"), mk("DF/DF"));
327 assertRemoved("DF/DF");
330 setupCase(mk("DF/DF"), mk("DF/DF"), mk("DF/DF/DF/DF/DF"));
332 assertRemoved("DF/DF/DF/DF/DF");
333 assertUpdated("DF/DF");
336 setupCase(mk("DF/DF"), mk("DF/DF"), mk("DF/DF/DF/DF/DF"));
337 writeTrashFile("DF/DF/DF/DF/DF", "diff");
339 assertConflict("DF/DF/DF/DF/DF");
340 assertUpdated("DF/DF");
346 public void testDirectoryFileConflicts_9() throws Exception
{
348 doit(mk("DF"), mkmap("DF", "QP"), mk("DF/DF"));
349 assertRemoved("DF/DF");
353 public void testDirectoryFileConflicts_10() throws Exception
{
356 doit(mk("DF"), mk("DF/DF"), mk("DF/DF"));
361 public void testDirectoryFileConflicts_11() throws Exception
{
363 doit(mk("DF"), mk("DF/DF"), mkmap("DF/DF", "asdf"));
364 assertConflict("DF/DF");
367 public void testDirectoryFileConflicts_12() throws Exception
{
370 doit(mk("DF"), mk("DF/DF"), mk("DF"));
372 assertUpdated("DF/DF");
375 public void testDirectoryFileConflicts_13() throws Exception
{
378 setupCase(mk("DF"), mk("DF/DF"), mk("DF"));
379 writeTrashFile("DF", "asdfsdf");
381 assertConflict("DF");
382 assertUpdated("DF/DF");
385 public void testDirectoryFileConflicts_14() throws Exception
{
388 doit(mk("DF"), mk("DF/DF"), mkmap("DF", "Foo"));
389 assertConflict("DF");
390 assertUpdated("DF/DF");
393 public void testDirectoryFileConflicts_15() throws Exception
{
395 doit(mkmap(), mk("DF/DF"), mk("DF"));
397 assertUpdated("DF/DF");
400 public void testDirectoryFileConflicts_15b() throws Exception
{
401 // 15, take 2, just to check multi-leveled
402 doit(mkmap(), mk("DF/DF/DF/DF"), mk("DF"));
404 assertUpdated("DF/DF/DF/DF");
407 public void testDirectoryFileConflicts_16() throws Exception
{
410 doit(mkmap(), mk("DF"), mk("DF/DF/DF"));
411 assertRemoved("DF/DF/DF");
415 public void testDirectoryFileConflicts_17() throws Exception
{
418 setupCase(mkmap(), mk("DF"), mk("DF/DF/DF"));
419 writeTrashFile("DF/DF/DF", "asdf");
421 assertConflict("DF/DF/DF");
425 public void testDirectoryFileConflicts_18() throws Exception
{
428 doit(mk("DF/DF"), mk("DF/DF/DF/DF"), null);
429 assertRemoved("DF/DF");
430 assertUpdated("DF/DF/DF/DF");
433 public void testDirectoryFileConflicts_19() throws Exception
{
436 doit(mk("DF/DF/DF/DF"), mk("DF/DF/DF"), null);
437 assertRemoved("DF/DF/DF/DF");
438 assertUpdated("DF/DF/DF");
441 private void cleanUpDF() throws Exception
{
444 recursiveDelete(new File(trash
, "DF"));
447 private void assertConflict(String s
) {
448 assertTrue(readTree
.conflicts
.contains(s
));
451 private void assertUpdated(String s
) {
452 assertTrue(readTree
.updated
.containsKey(s
));
455 private void assertRemoved(String s
) {
456 assertTrue(readTree
.removed
.contains(s
));
459 private void assertNoConflicts() {
460 assertTrue(readTree
.conflicts
.isEmpty());
463 private void doit(HashMap
<String
, String
> h
, HashMap
<String
, String
>m
,
464 HashMap
<String
, String
> i
) throws IOException
{
469 private static HashMap
<String
, String
> mk(String a
) {
473 private static HashMap
<String
, String
> mkmap(String
... args
) {
474 if ((args
.length
% 2) > 0)
475 throw new IllegalArgumentException("needs to be pairs");
477 HashMap
<String
, String
> map
= new HashMap
<String
, String
>();
478 for (int i
= 0; i
< args
.length
; i
+= 2) {
479 map
.put(args
[i
], args
[i
+1]);
485 public void testUntrackedConflicts() throws IOException
{
486 setupCase(null, mk("foo"), null);
487 writeTrashFile("foo", "foo");
490 assertConflict("foo");
492 recursiveDelete(new File(trash
, "foo"));
493 setupCase(null, mk("foo"), null);
494 writeTrashFile("foo/bar/baz", "");
495 writeTrashFile("foo/blahblah", "");
498 assertConflict("foo/bar/baz");
499 assertConflict("foo/blahblah");
501 recursiveDelete(new File(trash
, "foo"));
503 setupCase(mkmap("foo/bar", "", "foo/baz", ""),
504 mk("foo"), mkmap("foo/bar", "", "foo/baz", ""));
505 assertTrue(new File(trash
, "foo/bar").exists());
511 public void testCloseNameConflictsX0() throws IOException
{
512 setupCase(mkmap("a/a", "a/a-c"), mkmap("a/a","a/a", "b.b/b.b","b.b/b.bs"), mkmap("a/a", "a/a-c") );
518 public void testCloseNameConflicts1() throws IOException
{
519 setupCase(mkmap("a/a", "a/a-c"), mkmap("a/a","a/a", "a.a/a.a","a.a/a.a"), mkmap("a/a", "a/a-c") );
525 private void checkout() throws IOException
{
526 readTree
= new WorkDirCheckout(db
, trash
, head
, index
, merge
);
530 public void testCheckoutOutChanges() throws IOException
{
531 setupCase(mk("foo"), mk("foo/bar"), mk("foo"));
534 assertFalse(new File(trash
, "foo").isFile());
535 assertTrue(new File(trash
, "foo/bar").isFile());
536 recursiveDelete(new File(trash
, "foo"));
538 setupCase(mk("foo/bar"), mk("foo"), mk("foo/bar"));
541 assertFalse(new File(trash
, "foo/bar").isFile());
542 assertTrue(new File(trash
, "foo").isFile());
544 setupCase(mk("foo"), mkmap("foo", "qux"), mkmap("foo", "bar"));
548 fail("did not throw exception");
549 } catch (CheckoutConflictException e
) {
550 // should have thrown