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' and replace by folder 'a' in the working tree and
506 	 * index
507 	 * <li>Checkout branch '1'
508 	 * </ol>
509 	 * <p>
510 	 * The checkout command should raise an error. The conflicting path is 'a'.
511 	 *
512 	 * @throws Exception
513 	 */
514 	@Test
515 	public void fileModeTestFileThenFileWithFolderInIndex() throws Exception {
516 		try (Git git = new Git(db)) {
517 			writeTrashFile("a", "Hello world a");
518 			writeTrashFile("b", "Hello world b");
519 			git.add().addFilepattern(".").call();
520 			git.commit().setMessage("add files a & b").call();
521 			Ref branch_1 = git.branchCreate().setName("branch_1").call();
522 			writeTrashFile("a", "b");
523 			git.add().addFilepattern("a").call();
524 			git.commit().setMessage("add file a").call();
525 
526 			FileEntry entry = new FileTreeIterator.FileEntry(new File(
527 					db.getWorkTree(), "a"), db.getFS());
528 			assertEquals(FileMode.REGULAR_FILE, entry.getMode());
529 
530 			git.rm().addFilepattern("a").call();
531 			FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
532 			writeTrashFile("a/c", "Hello world c");
533 			git.add().addFilepattern(".").call();
534 
535 			entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
536 					db.getFS());
537 			assertEquals(FileMode.TREE, entry.getMode());
538 
539 			CheckoutConflictException exception = null;
540 			try {
541 				git.checkout().setName(branch_1.getName()).call();
542 			} catch (CheckoutConflictException e) {
543 				exception = e;
544 			}
545 			assertNotNull(exception);
546 			assertEquals(1, exception.getConflictingPaths().size());
547 			assertEquals("a", exception.getConflictingPaths().get(0));
548 		}
549 	}
550 
551 	/**
552 	 * Steps:
553 	 * <ol>
554 	 * <li>Add file 'a'
555 	 * <li>Commit
556 	 * <li>Create branch '1'
557 	 * <li>Modify file 'a'
558 	 * <li>Commit
559 	 * <li>Delete file 'a' and replace by folder 'a' in the working tree and
560 	 * index
561 	 * <li>Checkout branch '1'
562 	 * </ol>
563 	 * <p>
564 	 * The checkout command should raise an error. The conflicting paths are 'a'
565 	 * and 'a/c'.
566 	 *
567 	 * @throws Exception
568 	 */
569 	@Test
570 	public void fileModeTestFileWithFolderInIndex() throws Exception {
571 		try (Git git = new Git(db)) {
572 			writeTrashFile("b", "Hello world b");
573 			writeTrashFile("a", "b");
574 			git.add().addFilepattern(".").call();
575 			git.commit().setMessage("add file b & file a").call();
576 			Ref branch_1 = git.branchCreate().setName("branch_1").call();
577 			git.rm().addFilepattern("a").call();
578 			writeTrashFile("a", "Hello world a");
579 			git.add().addFilepattern("a").call();
580 			git.commit().setMessage("add file a").call();
581 
582 			FileEntry entry = new FileTreeIterator.FileEntry(new File(
583 					db.getWorkTree(), "a"), db.getFS());
584 			assertEquals(FileMode.REGULAR_FILE, entry.getMode());
585 
586 			git.rm().addFilepattern("a").call();
587 			FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
588 			writeTrashFile("a/c", "Hello world c");
589 			git.add().addFilepattern(".").call();
590 
591 			entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
592 					db.getFS());
593 			assertEquals(FileMode.TREE, entry.getMode());
594 
595 			CheckoutConflictException exception = null;
596 			try {
597 				git.checkout().setName(branch_1.getName()).call();
598 			} catch (CheckoutConflictException e) {
599 				exception = e;
600 			}
601 			assertNotNull(exception);
602 			assertEquals(1, exception.getConflictingPaths().size());
603 			assertEquals("a", exception.getConflictingPaths().get(0));
604 
605 			// TODO: ideally we'd like to get two paths from this exception
606 			// assertEquals(2, exception.getConflictingPaths().size());
607 			// assertEquals("a", exception.getConflictingPaths().get(0));
608 			// assertEquals("a/c", exception.getConflictingPaths().get(1));
609 		}
610 	}
611 
612 	@Test
613 	public void testCheckoutPath() throws Exception {
614 		try (Git git = new Git(db)) {
615 			writeTrashFile("a", "Hello world a");
616 			git.add().addFilepattern(".").call();
617 			git.commit().setMessage("commit file a").call();
618 			git.branchCreate().setName("branch_1").call();
619 			git.checkout().setName("branch_1").call();
620 			File b = writeTrashFile("b", "Hello world b");
621 			git.add().addFilepattern("b").call();
622 			git.commit().setMessage("commit file b").call();
623 			File a = writeTrashFile("a", "New Hello world a");
624 			git.add().addFilepattern(".").call();
625 			git.commit().setMessage("modified a").call();
626 			assertArrayEquals(new String[] { "" },
627 					execute("git checkout HEAD~2 -- a"));
628 			assertEquals("Hello world a", read(a));
629 			assertArrayEquals(new String[] { "* branch_1", "  master", "" },
630 					execute("git branch"));
631 			assertEquals("Hello world b", read(b));
632 		}
633 	}
634 
635 	@Test
636 	public void testCheckoutAllPaths() throws Exception {
637 		try (Git git = new Git(db)) {
638 			writeTrashFile("a", "Hello world a");
639 			git.add().addFilepattern(".").call();
640 			git.commit().setMessage("commit file a").call();
641 			git.branchCreate().setName("branch_1").call();
642 			git.checkout().setName("branch_1").call();
643 			File b = writeTrashFile("b", "Hello world b");
644 			git.add().addFilepattern("b").call();
645 			git.commit().setMessage("commit file b").call();
646 			File a = writeTrashFile("a", "New Hello world a");
647 			git.add().addFilepattern(".").call();
648 			git.commit().setMessage("modified a").call();
649 			assertArrayEquals(new String[] { "" },
650 					execute("git checkout HEAD~2 -- ."));
651 			assertEquals("Hello world a", read(a));
652 			assertArrayEquals(new String[] { "* branch_1", "  master", "" },
653 					execute("git branch"));
654 			assertEquals("Hello world b", read(b));
655 		}
656 	}
657 
658 	@Test
659 	public void testCheckoutSingleFile() throws Exception {
660 		try (Git git = new Git(db)) {
661 			File a = writeTrashFile("a", "file a");
662 			git.add().addFilepattern(".").call();
663 			git.commit().setMessage("commit file a").call();
664 			writeTrashFile("a", "b");
665 			assertEquals("b", read(a));
666 			assertEquals("[]", Arrays.toString(execute("git checkout -- a")));
667 			assertEquals("file a", read(a));
668 		}
669 	}
670 
671 	@Test
672 	public void testCheckoutLink() throws Exception {
673 		Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
674 		try (Git git = new Git(db)) {
675 			Path path = writeLink("a", "link_a");
676 			assertTrue(Files.isSymbolicLink(path));
677 			git.add().addFilepattern(".").call();
678 			git.commit().setMessage("commit link a").call();
679 			deleteTrashFile("a");
680 			writeTrashFile("a", "Hello world a");
681 			assertFalse(Files.isSymbolicLink(path));
682 			assertEquals("[]", Arrays.toString(execute("git checkout -- a")));
683 			assertEquals("link_a", FileUtils.readSymLink(path.toFile()));
684 			assertTrue(Files.isSymbolicLink(path));
685 		}
686 	}
687 }