View Javadoc
1   /*
2    * Copyright (C) 2012, IBM Corporation
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  package org.eclipse.jgit.pgm;
44  
45  import static org.junit.Assert.assertArrayEquals;
46  import static org.junit.Assert.assertEquals;
47  import static org.junit.Assert.assertFalse;
48  import static org.junit.Assert.assertNotNull;
49  import static org.junit.Assert.assertTrue;
50  import static org.junit.Assert.fail;
51  
52  import java.io.File;
53  import java.nio.file.Files;
54  import java.nio.file.Path;
55  import java.util.Arrays;
56  import java.util.List;
57  
58  import org.eclipse.jgit.api.Git;
59  import org.eclipse.jgit.api.errors.CheckoutConflictException;
60  import org.eclipse.jgit.diff.DiffEntry;
61  import org.eclipse.jgit.lib.CLIRepositoryTestCase;
62  import org.eclipse.jgit.lib.FileMode;
63  import org.eclipse.jgit.lib.Ref;
64  import org.eclipse.jgit.revwalk.RevCommit;
65  import org.eclipse.jgit.treewalk.FileTreeIterator;
66  import org.eclipse.jgit.treewalk.FileTreeIterator.FileEntry;
67  import org.eclipse.jgit.treewalk.TreeWalk;
68  import org.eclipse.jgit.util.FS;
69  import org.eclipse.jgit.util.FileUtils;
70  import org.junit.Assume;
71  import org.junit.Test;
72  
73  public class CheckoutTest extends CLIRepositoryTestCase {
74  	/**
75  	 * Executes specified git command (with arguments), captures exception and
76  	 * returns its message as an array of lines. Throws an AssertionError if no
77  	 * exception is thrown.
78  	 *
79  	 * @param command
80  	 *            a valid git command line, e.g. "git branch -h"
81  	 * @return message contained within the exception
82  	 */
83  	private String[] executeExpectingException(String command) {
84  		try {
85  			execute(command);
86  			throw new AssertionError("Expected Die");
87  		} catch (Exception e) {
88  			return e.getMessage().split(System.lineSeparator());
89  		}
90  	}
91  
92  	@Test
93  	public void testCheckoutSelf() throws Exception {
94  		try (Git git = new Git(db)) {
95  			git.commit().setMessage("initial commit").call();
96  
97  			assertStringArrayEquals("Already on 'master'",
98  					execute("git checkout master"));
99  		}
100 	}
101 
102 	@Test
103 	public void testCheckoutBranch() throws Exception {
104 		try (Git git = new Git(db)) {
105 			git.commit().setMessage("initial commit").call();
106 			git.branchCreate().setName("side").call();
107 
108 			assertStringArrayEquals("Switched to branch 'side'",
109 					execute("git checkout side"));
110 		}
111 	}
112 
113 	@Test
114 	public void testCheckoutNewBranch() throws Exception {
115 		try (Git git = new Git(db)) {
116 			git.commit().setMessage("initial commit").call();
117 
118 			assertStringArrayEquals("Switched to a new branch 'side'",
119 					execute("git checkout -b side"));
120 		}
121 	}
122 
123 	@Test
124 	public void testCheckoutNonExistingBranch() throws Exception {
125 		assertStringArrayEquals(
126 				"error: pathspec 'side' did not match any file(s) known to git.",
127 				executeExpectingException("git checkout side"));
128 	}
129 
130 	@Test
131 	public void testCheckoutNewBranchThatAlreadyExists() throws Exception {
132 		try (Git git = new Git(db)) {
133 			git.commit().setMessage("initial commit").call();
134 
135 			assertStringArrayEquals(
136 					"fatal: A branch named 'master' already exists.",
137 				executeUnchecked("git checkout -b master"));
138 		}
139 	}
140 
141 	@Test
142 	public void testCheckoutNewBranchOnBranchToBeBorn() throws Exception {
143 		assertStringArrayEquals("fatal: You are on a branch yet to be born",
144 				executeUnchecked("git checkout -b side"));
145 	}
146 
147 	@Test
148 	public void testCheckoutUnresolvedHead() throws Exception {
149 		assertStringArrayEquals(
150 				"error: pathspec 'HEAD' did not match any file(s) known to git.",
151 				executeExpectingException("git checkout HEAD"));
152 	}
153 
154 	@Test
155 	public void testCheckoutHead() throws Exception {
156 		try (Git git = new Git(db)) {
157 			git.commit().setMessage("initial commit").call();
158 
159 			assertStringArrayEquals("", execute("git checkout HEAD"));
160 		}
161 	}
162 
163 	@Test
164 	public void testCheckoutExistingBranchWithConflict() throws Exception {
165 		try (Git git = new Git(db)) {
166 			writeTrashFile("a", "Hello world a");
167 			git.add().addFilepattern(".").call();
168 			git.commit().setMessage("commit file a").call();
169 			git.branchCreate().setName("branch_1").call();
170 			git.rm().addFilepattern("a").call();
171 			FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
172 			writeTrashFile("a/b", "Hello world b");
173 			git.add().addFilepattern("a/b").call();
174 			git.commit().setMessage("commit folder a").call();
175 			git.rm().addFilepattern("a").call();
176 			writeTrashFile("a", "New Hello world a");
177 			git.add().addFilepattern(".").call();
178 
179 			String[] execute = executeExpectingException(
180 					"git checkout branch_1");
181 			assertEquals(
182 					"error: Your local changes to the following files would be overwritten by checkout:",
183 					execute[0]);
184 			assertEquals("\ta", execute[1]);
185 		}
186 	}
187 
188 	/**
189 	 * Steps:
190 	 * <ol>
191 	 * <li>Add file 'a' and 'b'
192 	 * <li>Commit
193 	 * <li>Create branch '1'
194 	 * <li>modify file 'a'
195 	 * <li>Commit
196 	 * <li>Delete file 'a' in the working tree
197 	 * <li>Checkout branch '1'
198 	 * </ol>
199 	 * <p>
200 	 * The working tree should contain 'a' with FileMode.REGULAR_FILE after the
201 	 * checkout.
202 	 *
203 	 * @throws Exception
204 	 */
205 	@Test
206 	public void testCheckoutWithMissingWorkingTreeFile() throws Exception {
207 		try (Git git = new Git(db)) {
208 			File fileA = writeTrashFile("a", "Hello world a");
209 			writeTrashFile("b", "Hello world b");
210 			git.add().addFilepattern(".").call();
211 			git.commit().setMessage("add files a & b").call();
212 			Ref branch_1 = git.branchCreate().setName("branch_1").call();
213 			writeTrashFile("a", "b");
214 			git.add().addFilepattern("a").call();
215 			git.commit().setMessage("modify file a").call();
216 
217 			FileEntry entry = new FileTreeIterator.FileEntry(new File(
218 					db.getWorkTree(), "a"), db.getFS());
219 			assertEquals(FileMode.REGULAR_FILE, entry.getMode());
220 
221 			FileUtils.delete(fileA);
222 
223 			git.checkout().setName(branch_1.getName()).call();
224 
225 			entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
226 					db.getFS());
227 			assertEquals(FileMode.REGULAR_FILE, entry.getMode());
228 			assertEquals("Hello world a", read(fileA));
229 		}
230 	}
231 
232 	@Test
233 	public void testCheckoutOrphan() throws Exception {
234 		try (Git git = new Git(db)) {
235 			git.commit().setMessage("initial commit").call();
236 
237 			assertStringArrayEquals("Switched to a new branch 'new_branch'",
238 					execute("git checkout --orphan new_branch"));
239 			assertEquals("refs/heads/new_branch",
240 					db.exactRef("HEAD").getTarget().getName());
241 			RevCommit commit = git.commit().setMessage("orphan commit").call();
242 			assertEquals(0, commit.getParentCount());
243 		}
244 	}
245 
246 	/**
247 	 * Steps:
248 	 * <ol>
249 	 * <li>Add file 'b'
250 	 * <li>Commit
251 	 * <li>Create branch '1'
252 	 * <li>Add folder 'a'
253 	 * <li>Commit
254 	 * <li>Replace folder 'a' by file 'a' in the working tree
255 	 * <li>Checkout branch '1'
256 	 * </ol>
257 	 * <p>
258 	 * The checkout has to delete folder but the workingtree contains a dirty
259 	 * file at this path. The checkout should fail like in native git.
260 	 *
261 	 * @throws Exception
262 	 */
263 	@Test
264 	public void fileModeTestMissingThenFolderWithFileInWorkingTree()
265 			throws Exception {
266 		try (Git git = new Git(db)) {
267 			writeTrashFile("b", "Hello world b");
268 			git.add().addFilepattern(".").call();
269 			git.commit().setMessage("add file b").call();
270 			Ref branch_1 = git.branchCreate().setName("branch_1").call();
271 			File folderA = new File(db.getWorkTree(), "a");
272 			FileUtils.mkdirs(folderA);
273 			writeTrashFile("a/c", "Hello world c");
274 			git.add().addFilepattern(".").call();
275 			git.commit().setMessage("add folder a").call();
276 
277 			FileEntry entry = new FileTreeIterator.FileEntry(new File(
278 					db.getWorkTree(), "a"), db.getFS());
279 			assertEquals(FileMode.TREE, entry.getMode());
280 
281 			FileUtils.delete(folderA, FileUtils.RECURSIVE);
282 			writeTrashFile("a", "b");
283 
284 			entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
285 					db.getFS());
286 			assertEquals(FileMode.REGULAR_FILE, entry.getMode());
287 
288 			try {
289 				git.checkout().setName(branch_1.getName()).call();
290 				fail("Don't get the expected conflict");
291 			} catch (CheckoutConflictException e) {
292 				assertEquals("[a]", e.getConflictingPaths().toString());
293 				entry = new FileTreeIterator.FileEntry(
294 						new File(db.getWorkTree(), "a"), db.getFS());
295 				assertEquals(FileMode.REGULAR_FILE, entry.getMode());
296 			}
297 		}
298 	}
299 
300 	/**
301 	 * Steps:
302 	 * <ol>
303 	 * <li>Add file 'a'
304 	 * <li>Commit
305 	 * <li>Create branch '1'
306 	 * <li>Replace file 'a' by folder 'a'
307 	 * <li>Commit
308 	 * <li>Delete folder 'a' in the working tree
309 	 * <li>Checkout branch '1'
310 	 * </ol>
311 	 * <p>
312 	 * The working tree should contain 'a' with FileMode.REGULAR_FILE after the
313 	 * checkout.
314 	 *
315 	 * @throws Exception
316 	 */
317 	@Test
318 	public void fileModeTestFolderWithMissingInWorkingTree() throws Exception {
319 		try (Git git = new Git(db)) {
320 			writeTrashFile("b", "Hello world b");
321 			writeTrashFile("a", "b");
322 			git.add().addFilepattern(".").call();
323 			git.commit().setMessage("add file b & file a").call();
324 			Ref branch_1 = git.branchCreate().setName("branch_1").call();
325 			git.rm().addFilepattern("a").call();
326 			File folderA = new File(db.getWorkTree(), "a");
327 			FileUtils.mkdirs(folderA);
328 			writeTrashFile("a/c", "Hello world c");
329 			git.add().addFilepattern(".").call();
330 			git.commit().setMessage("add folder a").call();
331 
332 			FileEntry entry = new FileTreeIterator.FileEntry(new File(
333 					db.getWorkTree(), "a"), db.getFS());
334 			assertEquals(FileMode.TREE, entry.getMode());
335 
336 			FileUtils.delete(folderA, FileUtils.RECURSIVE);
337 
338 			git.checkout().setName(branch_1.getName()).call();
339 
340 			entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
341 					db.getFS());
342 			assertEquals(FileMode.REGULAR_FILE, entry.getMode());
343 		}
344 	}
345 
346 	/**
347 	 * Steps:
348 	 * <ol>
349 	 * <li>Add file 'a'
350 	 * <li>Commit
351 	 * <li>Create branch '1'
352 	 * <li>Delete file 'a'
353 	 * <li>Commit
354 	 * <li>Add folder 'a' in the working tree
355 	 * <li>Checkout branch '1'
356 	 * </ol>
357 	 * <p>
358 	 * The checkout command should raise an error. The conflicting paths are 'a'
359 	 * and 'a/c'.
360 	 *
361 	 * @throws Exception
362 	 */
363 	@Test
364 	public void fileModeTestMissingWithFolderInWorkingTree() throws Exception {
365 		try (Git git = new Git(db)) {
366 			writeTrashFile("b", "Hello world b");
367 			writeTrashFile("a", "b");
368 			git.add().addFilepattern(".").call();
369 			git.commit().setMessage("add file b & file a").call();
370 			Ref branch_1 = git.branchCreate().setName("branch_1").call();
371 			git.rm().addFilepattern("a").call();
372 			git.commit().setMessage("delete file a").call();
373 
374 			FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
375 			writeTrashFile("a/c", "Hello world c");
376 
377 			FileEntry entry = new FileTreeIterator.FileEntry(new File(
378 					db.getWorkTree(), "a"), db.getFS());
379 			assertEquals(FileMode.TREE, entry.getMode());
380 
381 			CheckoutConflictException exception = null;
382 			try {
383 				git.checkout().setName(branch_1.getName()).call();
384 			} catch (CheckoutConflictException e) {
385 				exception = e;
386 			}
387 			assertNotNull(exception);
388 			assertEquals(2, exception.getConflictingPaths().size());
389 			assertEquals("a", exception.getConflictingPaths().get(0));
390 			assertEquals("a/c", exception.getConflictingPaths().get(1));
391 		}
392 	}
393 
394 	/**
395 	 * Steps:
396 	 * <ol>
397 	 * <li>Add folder 'a'
398 	 * <li>Commit
399 	 * <li>Create branch '1'
400 	 * <li>Delete folder 'a'
401 	 * <li>Commit
402 	 * <li>Add file 'a' in the working tree
403 	 * <li>Checkout branch '1'
404 	 * </ol>
405 	 * <p>
406 	 * The checkout command should raise an error. The conflicting path is 'a'.
407 	 *
408 	 * @throws Exception
409 	 */
410 	@Test
411 	public void fileModeTestFolderThenMissingWithFileInWorkingTree()
412 			throws Exception {
413 		try (Git git = new Git(db)) {
414 			FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
415 			writeTrashFile("a/c", "Hello world c");
416 			writeTrashFile("b", "Hello world b");
417 			git.add().addFilepattern(".").call();
418 			RevCommit commit1 = git.commit().setMessage("add folder a & file b")
419 					.call();
420 			Ref branch_1 = git.branchCreate().setName("branch_1").call();
421 			git.rm().addFilepattern("a").call();
422 			RevCommit commit2 = git.commit().setMessage("delete folder a").call();
423 
424 			TreeWalk tw = new TreeWalk(db);
425 			tw.addTree(commit1.getTree());
426 			tw.addTree(commit2.getTree());
427 			List<DiffEntry> scan = DiffEntry.scan(tw);
428 			assertEquals(1, scan.size());
429 			assertEquals(FileMode.MISSING, scan.get(0).getNewMode());
430 			assertEquals(FileMode.TREE, scan.get(0).getOldMode());
431 
432 			writeTrashFile("a", "b");
433 
434 			FileEntry entry = new FileTreeIterator.FileEntry(new File(
435 					db.getWorkTree(), "a"), db.getFS());
436 			assertEquals(FileMode.REGULAR_FILE, entry.getMode());
437 
438 			CheckoutConflictException exception = null;
439 			try {
440 				git.checkout().setName(branch_1.getName()).call();
441 			} catch (CheckoutConflictException e) {
442 				exception = e;
443 			}
444 			assertNotNull(exception);
445 			assertEquals(1, exception.getConflictingPaths().size());
446 			assertEquals("a", exception.getConflictingPaths().get(0));
447 		}
448 	}
449 
450 	/**
451 	 * Steps:
452 	 * <ol>
453 	 * <li>Add folder 'a'
454 	 * <li>Commit
455 	 * <li>Create branch '1'
456 	 * <li>Replace folder 'a'by file 'a'
457 	 * <li>Commit
458 	 * <li>Delete file 'a' in the working tree
459 	 * <li>Checkout branch '1'
460 	 * </ol>
461 	 * <p>
462 	 * The working tree should contain 'a' with FileMode.TREE after the
463 	 * checkout.
464 	 *
465 	 * @throws Exception
466 	 */
467 	@Test
468 	public void fileModeTestFolderThenFileWithMissingInWorkingTree()
469 			throws Exception {
470 		try (Git git = new Git(db)) {
471 			FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
472 			writeTrashFile("a/c", "Hello world c");
473 			writeTrashFile("b", "Hello world b");
474 			git.add().addFilepattern(".").call();
475 			git.commit().setMessage("add folder a & file b").call();
476 			Ref branch_1 = git.branchCreate().setName("branch_1").call();
477 			git.rm().addFilepattern("a").call();
478 			File fileA = new File(db.getWorkTree(), "a");
479 			writeTrashFile("a", "b");
480 			git.add().addFilepattern("a").call();
481 			git.commit().setMessage("add file a").call();
482 
483 			FileEntry entry = new FileTreeIterator.FileEntry(new File(
484 					db.getWorkTree(), "a"), db.getFS());
485 			assertEquals(FileMode.REGULAR_FILE, entry.getMode());
486 
487 			FileUtils.delete(fileA);
488 
489 			git.checkout().setName(branch_1.getName()).call();
490 
491 			entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
492 					db.getFS());
493 			assertEquals(FileMode.TREE, entry.getMode());
494 		}
495 	}
496 
497 	/**
498 	 * Steps:
499 	 * <ol>
500 	 * <li>Add file 'a'
501 	 * <li>Commit
502 	 * <li>Create branch '1'
503 	 * <li>Modify file 'a'
504 	 * <li>Commit
505 	 * <li>Delete file 'a' & replace by folder 'a' in the working tree & index
506 	 * <li>Checkout branch '1'
507 	 * </ol>
508 	 * <p>
509 	 * The checkout command should raise an error. The conflicting path is 'a'.
510 	 *
511 	 * @throws Exception
512 	 */
513 	@Test
514 	public void fileModeTestFileThenFileWithFolderInIndex() throws Exception {
515 		try (Git git = new Git(db)) {
516 			writeTrashFile("a", "Hello world a");
517 			writeTrashFile("b", "Hello world b");
518 			git.add().addFilepattern(".").call();
519 			git.commit().setMessage("add files a & b").call();
520 			Ref branch_1 = git.branchCreate().setName("branch_1").call();
521 			writeTrashFile("a", "b");
522 			git.add().addFilepattern("a").call();
523 			git.commit().setMessage("add file a").call();
524 
525 			FileEntry entry = new FileTreeIterator.FileEntry(new File(
526 					db.getWorkTree(), "a"), db.getFS());
527 			assertEquals(FileMode.REGULAR_FILE, entry.getMode());
528 
529 			git.rm().addFilepattern("a").call();
530 			FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
531 			writeTrashFile("a/c", "Hello world c");
532 			git.add().addFilepattern(".").call();
533 
534 			entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
535 					db.getFS());
536 			assertEquals(FileMode.TREE, entry.getMode());
537 
538 			CheckoutConflictException exception = null;
539 			try {
540 				git.checkout().setName(branch_1.getName()).call();
541 			} catch (CheckoutConflictException e) {
542 				exception = e;
543 			}
544 			assertNotNull(exception);
545 			assertEquals(1, exception.getConflictingPaths().size());
546 			assertEquals("a", exception.getConflictingPaths().get(0));
547 		}
548 	}
549 
550 	/**
551 	 * Steps:
552 	 * <ol>
553 	 * <li>Add file 'a'
554 	 * <li>Commit
555 	 * <li>Create branch '1'
556 	 * <li>Modify file 'a'
557 	 * <li>Commit
558 	 * <li>Delete file 'a' & replace by folder 'a' in the working tree & index
559 	 * <li>Checkout branch '1'
560 	 * </ol>
561 	 * <p>
562 	 * The checkout command should raise an error. The conflicting paths are 'a'
563 	 * and 'a/c'.
564 	 *
565 	 * @throws Exception
566 	 */
567 	@Test
568 	public void fileModeTestFileWithFolderInIndex() throws Exception {
569 		try (Git git = new Git(db)) {
570 			writeTrashFile("b", "Hello world b");
571 			writeTrashFile("a", "b");
572 			git.add().addFilepattern(".").call();
573 			git.commit().setMessage("add file b & file a").call();
574 			Ref branch_1 = git.branchCreate().setName("branch_1").call();
575 			git.rm().addFilepattern("a").call();
576 			writeTrashFile("a", "Hello world a");
577 			git.add().addFilepattern("a").call();
578 			git.commit().setMessage("add file a").call();
579 
580 			FileEntry entry = new FileTreeIterator.FileEntry(new File(
581 					db.getWorkTree(), "a"), db.getFS());
582 			assertEquals(FileMode.REGULAR_FILE, entry.getMode());
583 
584 			git.rm().addFilepattern("a").call();
585 			FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
586 			writeTrashFile("a/c", "Hello world c");
587 			git.add().addFilepattern(".").call();
588 
589 			entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
590 					db.getFS());
591 			assertEquals(FileMode.TREE, entry.getMode());
592 
593 			CheckoutConflictException exception = null;
594 			try {
595 				git.checkout().setName(branch_1.getName()).call();
596 			} catch (CheckoutConflictException e) {
597 				exception = e;
598 			}
599 			assertNotNull(exception);
600 			assertEquals(1, exception.getConflictingPaths().size());
601 			assertEquals("a", exception.getConflictingPaths().get(0));
602 
603 			// TODO: ideally we'd like to get two paths from this exception
604 			// assertEquals(2, exception.getConflictingPaths().size());
605 			// assertEquals("a", exception.getConflictingPaths().get(0));
606 			// assertEquals("a/c", exception.getConflictingPaths().get(1));
607 		}
608 	}
609 
610 	@Test
611 	public void testCheckoutPath() throws Exception {
612 		try (Git git = new Git(db)) {
613 			writeTrashFile("a", "Hello world a");
614 			git.add().addFilepattern(".").call();
615 			git.commit().setMessage("commit file a").call();
616 			git.branchCreate().setName("branch_1").call();
617 			git.checkout().setName("branch_1").call();
618 			File b = writeTrashFile("b", "Hello world b");
619 			git.add().addFilepattern("b").call();
620 			git.commit().setMessage("commit file b").call();
621 			File a = writeTrashFile("a", "New Hello world a");
622 			git.add().addFilepattern(".").call();
623 			git.commit().setMessage("modified a").call();
624 			assertArrayEquals(new String[] { "" },
625 					execute("git checkout HEAD~2 -- a"));
626 			assertEquals("Hello world a", read(a));
627 			assertArrayEquals(new String[] { "* branch_1", "  master", "" },
628 					execute("git branch"));
629 			assertEquals("Hello world b", read(b));
630 		}
631 	}
632 
633 	@Test
634 	public void testCheckoutAllPaths() throws Exception {
635 		try (Git git = new Git(db)) {
636 			writeTrashFile("a", "Hello world a");
637 			git.add().addFilepattern(".").call();
638 			git.commit().setMessage("commit file a").call();
639 			git.branchCreate().setName("branch_1").call();
640 			git.checkout().setName("branch_1").call();
641 			File b = writeTrashFile("b", "Hello world b");
642 			git.add().addFilepattern("b").call();
643 			git.commit().setMessage("commit file b").call();
644 			File a = writeTrashFile("a", "New Hello world a");
645 			git.add().addFilepattern(".").call();
646 			git.commit().setMessage("modified a").call();
647 			assertArrayEquals(new String[] { "" },
648 					execute("git checkout HEAD~2 -- ."));
649 			assertEquals("Hello world a", read(a));
650 			assertArrayEquals(new String[] { "* branch_1", "  master", "" },
651 					execute("git branch"));
652 			assertEquals("Hello world b", read(b));
653 		}
654 	}
655 
656 	@Test
657 	public void testCheckoutSingleFile() throws Exception {
658 		try (Git git = new Git(db)) {
659 			File a = writeTrashFile("a", "file a");
660 			git.add().addFilepattern(".").call();
661 			git.commit().setMessage("commit file a").call();
662 			writeTrashFile("a", "b");
663 			assertEquals("b", read(a));
664 			assertEquals("[]", Arrays.toString(execute("git checkout -- a")));
665 			assertEquals("file a", read(a));
666 		}
667 	}
668 
669 	@Test
670 	public void testCheckoutLink() throws Exception {
671 		Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
672 		try (Git git = new Git(db)) {
673 			Path path = writeLink("a", "link_a");
674 			assertTrue(Files.isSymbolicLink(path));
675 			git.add().addFilepattern(".").call();
676 			git.commit().setMessage("commit link a").call();
677 			deleteTrashFile("a");
678 			writeTrashFile("a", "Hello world a");
679 			assertFalse(Files.isSymbolicLink(path));
680 			assertEquals("[]", Arrays.toString(execute("git checkout -- a")));
681 			assertEquals("link_a", FileUtils.readSymLink(path.toFile()));
682 			assertTrue(Files.isSymbolicLink(path));
683 		}
684 	}
685 }