View Javadoc
1   /*
2    * Copyright (C) 2012, GitHub Inc.
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.api;
44  
45  import static org.junit.Assert.assertEquals;
46  import static org.junit.Assert.assertFalse;
47  import static org.junit.Assert.assertNotNull;
48  import static org.junit.Assert.assertTrue;
49  import static org.junit.Assert.fail;
50  
51  import java.io.File;
52  import java.text.MessageFormat;
53  
54  import org.eclipse.jgit.api.errors.InvalidRefNameException;
55  import org.eclipse.jgit.api.errors.JGitInternalException;
56  import org.eclipse.jgit.api.errors.NoHeadException;
57  import org.eclipse.jgit.api.errors.StashApplyFailureException;
58  import org.eclipse.jgit.events.ChangeRecorder;
59  import org.eclipse.jgit.events.ListenerHandle;
60  import org.eclipse.jgit.internal.JGitText;
61  import org.eclipse.jgit.junit.RepositoryTestCase;
62  import org.eclipse.jgit.lib.ObjectId;
63  import org.eclipse.jgit.lib.Repository;
64  import org.eclipse.jgit.revwalk.RevCommit;
65  import org.eclipse.jgit.util.FileUtils;
66  import org.junit.After;
67  import org.junit.Before;
68  import org.junit.Test;
69  
70  /**
71   * Unit tests of {@link StashApplyCommand}
72   */
73  public class StashApplyCommandTest extends RepositoryTestCase {
74  
75  	private static final String PATH = "file.txt";
76  
77  	private RevCommit head;
78  
79  	private Git git;
80  
81  	private File committedFile;
82  
83  	private ChangeRecorder recorder;
84  
85  	private ListenerHandle handle;
86  
87  	@Override
88  	@Before
89  	public void setUp() throws Exception {
90  		super.setUp();
91  		git = Git.wrap(db);
92  		recorder = new ChangeRecorder();
93  		handle = db.getListenerList().addWorkingTreeModifiedListener(recorder);
94  		committedFile = writeTrashFile(PATH, "content");
95  		git.add().addFilepattern(PATH).call();
96  		head = git.commit().setMessage("add file").call();
97  		assertNotNull(head);
98  		recorder.assertNoEvent();
99  	}
100 
101 	@Override
102 	@After
103 	public void tearDown() throws Exception {
104 		if (handle != null) {
105 			handle.remove();
106 		}
107 		super.tearDown();
108 	}
109 
110 	@Test
111 	public void workingDirectoryDelete() throws Exception {
112 		deleteTrashFile(PATH);
113 		assertFalse(committedFile.exists());
114 		RevCommit stashed = git.stashCreate().call();
115 		assertNotNull(stashed);
116 		assertEquals("content", read(committedFile));
117 		recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
118 
119 		ObjectId unstashed = git.stashApply().call();
120 		assertEquals(stashed, unstashed);
121 		assertFalse(committedFile.exists());
122 		recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { PATH });
123 
124 		Status status = git.status().call();
125 		assertTrue(status.getAdded().isEmpty());
126 		assertTrue(status.getChanged().isEmpty());
127 		assertTrue(status.getConflicting().isEmpty());
128 		assertTrue(status.getModified().isEmpty());
129 		assertTrue(status.getUntracked().isEmpty());
130 		assertTrue(status.getRemoved().isEmpty());
131 
132 		assertEquals(1, status.getMissing().size());
133 		assertTrue(status.getMissing().contains(PATH));
134 	}
135 
136 	@Test
137 	public void indexAdd() throws Exception {
138 		String addedPath = "file2.txt";
139 		File addedFile = writeTrashFile(addedPath, "content2");
140 		git.add().addFilepattern(addedPath).call();
141 
142 		RevCommit stashed = git.stashCreate().call();
143 		assertNotNull(stashed);
144 		assertFalse(addedFile.exists());
145 		recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { addedPath });
146 
147 		ObjectId unstashed = git.stashApply().call();
148 		assertEquals(stashed, unstashed);
149 		assertTrue(addedFile.exists());
150 		assertEquals("content2", read(addedFile));
151 		recorder.assertEvent(new String[] { addedPath }, ChangeRecorder.EMPTY);
152 
153 		Status status = git.status().call();
154 		assertTrue(status.getChanged().isEmpty());
155 		assertTrue(status.getConflicting().isEmpty());
156 		assertTrue(status.getMissing().isEmpty());
157 		assertTrue(status.getModified().isEmpty());
158 		assertTrue(status.getRemoved().isEmpty());
159 		assertTrue(status.getUntracked().isEmpty());
160 
161 		assertEquals(1, status.getAdded().size());
162 		assertTrue(status.getAdded().contains(addedPath));
163 	}
164 
165 	@Test
166 	public void indexDelete() throws Exception {
167 		git.rm().addFilepattern("file.txt").call();
168 		recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { "file.txt" });
169 
170 		RevCommit stashed = git.stashCreate().call();
171 		assertNotNull(stashed);
172 		assertEquals("content", read(committedFile));
173 		recorder.assertEvent(new String[] { "file.txt" }, ChangeRecorder.EMPTY);
174 
175 		ObjectId unstashed = git.stashApply().call();
176 		assertEquals(stashed, unstashed);
177 		assertFalse(committedFile.exists());
178 		recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { "file.txt" });
179 
180 		Status status = git.status().call();
181 		assertTrue(status.getAdded().isEmpty());
182 		assertTrue(status.getChanged().isEmpty());
183 		assertTrue(status.getConflicting().isEmpty());
184 		assertTrue(status.getModified().isEmpty());
185 		assertTrue(status.getMissing().isEmpty());
186 		assertTrue(status.getUntracked().isEmpty());
187 
188 		assertEquals(1, status.getRemoved().size());
189 		assertTrue(status.getRemoved().contains(PATH));
190 	}
191 
192 	@Test
193 	public void workingDirectoryModify() throws Exception {
194 		writeTrashFile("file.txt", "content2");
195 
196 		RevCommit stashed = git.stashCreate().call();
197 		assertNotNull(stashed);
198 		assertEquals("content", read(committedFile));
199 		recorder.assertEvent(new String[] { "file.txt" }, ChangeRecorder.EMPTY);
200 
201 		ObjectId unstashed = git.stashApply().call();
202 		assertEquals(stashed, unstashed);
203 		assertEquals("content2", read(committedFile));
204 		recorder.assertEvent(new String[] { "file.txt" }, ChangeRecorder.EMPTY);
205 
206 		Status status = git.status().call();
207 		assertTrue(status.getAdded().isEmpty());
208 		assertTrue(status.getChanged().isEmpty());
209 		assertTrue(status.getConflicting().isEmpty());
210 		assertTrue(status.getMissing().isEmpty());
211 		assertTrue(status.getRemoved().isEmpty());
212 		assertTrue(status.getUntracked().isEmpty());
213 
214 		assertEquals(1, status.getModified().size());
215 		assertTrue(status.getModified().contains(PATH));
216 	}
217 
218 	@Test
219 	public void workingDirectoryModifyInSubfolder() throws Exception {
220 		String path = "d1/d2/f.txt";
221 		File subfolderFile = writeTrashFile(path, "content");
222 		git.add().addFilepattern(path).call();
223 		head = git.commit().setMessage("add file").call();
224 		recorder.assertNoEvent();
225 
226 		writeTrashFile(path, "content2");
227 
228 		RevCommit stashed = git.stashCreate().call();
229 		assertNotNull(stashed);
230 		assertEquals("content", read(subfolderFile));
231 		recorder.assertEvent(new String[] { "d1/d2/f.txt" },
232 				ChangeRecorder.EMPTY);
233 
234 		ObjectId unstashed = git.stashApply().call();
235 		assertEquals(stashed, unstashed);
236 		assertEquals("content2", read(subfolderFile));
237 		recorder.assertEvent(new String[] { "d1/d2/f.txt" },
238 				ChangeRecorder.EMPTY);
239 
240 		Status status = git.status().call();
241 		assertTrue(status.getAdded().isEmpty());
242 		assertTrue(status.getChanged().isEmpty());
243 		assertTrue(status.getConflicting().isEmpty());
244 		assertTrue(status.getMissing().isEmpty());
245 		assertTrue(status.getRemoved().isEmpty());
246 		assertTrue(status.getUntracked().isEmpty());
247 
248 		assertEquals(1, status.getModified().size());
249 		assertTrue(status.getModified().contains(path));
250 	}
251 
252 	@Test
253 	public void workingDirectoryModifyIndexChanged() throws Exception {
254 		writeTrashFile("file.txt", "content2");
255 		git.add().addFilepattern("file.txt").call();
256 		writeTrashFile("file.txt", "content3");
257 
258 		RevCommit stashed = git.stashCreate().call();
259 		assertNotNull(stashed);
260 		assertEquals("content", read(committedFile));
261 		recorder.assertEvent(new String[] { "file.txt" }, ChangeRecorder.EMPTY);
262 
263 		ObjectId unstashed = git.stashApply().call();
264 		assertEquals(stashed, unstashed);
265 		assertEquals("content3", read(committedFile));
266 		recorder.assertEvent(new String[] { "file.txt" }, ChangeRecorder.EMPTY);
267 
268 		Status status = git.status().call();
269 		assertTrue(status.getAdded().isEmpty());
270 		assertTrue(status.getConflicting().isEmpty());
271 		assertTrue(status.getMissing().isEmpty());
272 		assertTrue(status.getRemoved().isEmpty());
273 		assertTrue(status.getUntracked().isEmpty());
274 
275 		assertEquals(1, status.getChanged().size());
276 		assertTrue(status.getChanged().contains(PATH));
277 		assertEquals(1, status.getModified().size());
278 		assertTrue(status.getModified().contains(PATH));
279 	}
280 
281 	@Test
282 	public void workingDirectoryCleanIndexModify() throws Exception {
283 		writeTrashFile("file.txt", "content2");
284 		git.add().addFilepattern("file.txt").call();
285 		writeTrashFile("file.txt", "content");
286 
287 		RevCommit stashed = git.stashCreate().call();
288 		assertNotNull(stashed);
289 		assertEquals("content", read(committedFile));
290 		recorder.assertEvent(new String[] { "file.txt" }, ChangeRecorder.EMPTY);
291 
292 		ObjectId unstashed = git.stashApply().call();
293 		assertEquals(stashed, unstashed);
294 		assertEquals("content2", read(committedFile));
295 		recorder.assertEvent(new String[] { "file.txt" }, ChangeRecorder.EMPTY);
296 
297 		Status status = git.status().call();
298 		assertTrue(status.getAdded().isEmpty());
299 		assertTrue(status.getConflicting().isEmpty());
300 		assertTrue(status.getMissing().isEmpty());
301 		assertTrue(status.getModified().isEmpty());
302 		assertTrue(status.getRemoved().isEmpty());
303 		assertTrue(status.getUntracked().isEmpty());
304 
305 		assertEquals(1, status.getChanged().size());
306 		assertTrue(status.getChanged().contains(PATH));
307 	}
308 
309 	@Test
310 	public void workingDirectoryDeleteIndexAdd() throws Exception {
311 		String path = "file2.txt";
312 		File added = writeTrashFile(path, "content2");
313 		assertTrue(added.exists());
314 		git.add().addFilepattern(path).call();
315 		FileUtils.delete(added);
316 		assertFalse(added.exists());
317 
318 		RevCommit stashed = git.stashCreate().call();
319 		assertNotNull(stashed);
320 		assertFalse(added.exists());
321 		recorder.assertNoEvent();
322 
323 		ObjectId unstashed = git.stashApply().call();
324 		assertEquals(stashed, unstashed);
325 		assertEquals("content2", read(added));
326 		recorder.assertEvent(new String[] { path }, ChangeRecorder.EMPTY);
327 
328 		Status status = git.status().call();
329 		assertTrue(status.getChanged().isEmpty());
330 		assertTrue(status.getConflicting().isEmpty());
331 		assertTrue(status.getMissing().isEmpty());
332 		assertTrue(status.getModified().isEmpty());
333 		assertTrue(status.getRemoved().isEmpty());
334 		assertTrue(status.getUntracked().isEmpty());
335 
336 		assertEquals(1, status.getAdded().size());
337 		assertTrue(status.getAdded().contains(path));
338 	}
339 
340 	@Test
341 	public void workingDirectoryDeleteIndexEdit() throws Exception {
342 		writeTrashFile(PATH, "content2");
343 		git.add().addFilepattern(PATH).call();
344 		FileUtils.delete(committedFile);
345 		assertFalse(committedFile.exists());
346 
347 		RevCommit stashed = git.stashCreate().call();
348 		assertNotNull(stashed);
349 		assertEquals("content", read(committedFile));
350 		recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
351 
352 		ObjectId unstashed = git.stashApply().call();
353 		assertEquals(stashed, unstashed);
354 		assertFalse(committedFile.exists());
355 		recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { PATH });
356 
357 		Status status = git.status().call();
358 		assertTrue(status.getAdded().isEmpty());
359 		assertEquals(1, status.getChanged().size());
360 		assertTrue(status.getChanged().contains(PATH));
361 		assertTrue(status.getConflicting().isEmpty());
362 		assertEquals(1, status.getMissing().size());
363 		assertTrue(status.getMissing().contains(PATH));
364 		assertTrue(status.getModified().isEmpty());
365 		assertTrue(status.getUntracked().isEmpty());
366 
367 		assertTrue(status.getRemoved().isEmpty());
368 	}
369 
370 	@Test
371 	public void multipleEdits() throws Exception {
372 		String addedPath = "file2.txt";
373 		git.rm().addFilepattern(PATH).call();
374 		File addedFile = writeTrashFile(addedPath, "content2");
375 		git.add().addFilepattern(addedPath).call();
376 
377 		RevCommit stashed = git.stashCreate().call();
378 		assertNotNull(stashed);
379 		assertTrue(committedFile.exists());
380 		assertFalse(addedFile.exists());
381 		recorder.assertEvent(new String[] { PATH },
382 				new String[] { "file2.txt" });
383 
384 		ObjectId unstashed = git.stashApply().call();
385 		assertEquals(stashed, unstashed);
386 		recorder.assertEvent(new String[] { "file2.txt" },
387 				new String[] { PATH });
388 
389 		Status status = git.status().call();
390 		assertTrue(status.getChanged().isEmpty());
391 		assertTrue(status.getConflicting().isEmpty());
392 		assertTrue(status.getMissing().isEmpty());
393 		assertTrue(status.getModified().isEmpty());
394 		assertTrue(status.getUntracked().isEmpty());
395 
396 		assertEquals(1, status.getRemoved().size());
397 		assertTrue(status.getRemoved().contains(PATH));
398 		assertEquals(1, status.getAdded().size());
399 		assertTrue(status.getAdded().contains(addedPath));
400 	}
401 
402 	@Test
403 	public void workingDirectoryContentConflict() throws Exception {
404 		writeTrashFile(PATH, "content2");
405 
406 		RevCommit stashed = git.stashCreate().call();
407 		assertNotNull(stashed);
408 		assertEquals("content", read(committedFile));
409 		assertTrue(git.status().call().isClean());
410 		recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
411 
412 		writeTrashFile(PATH, "content3");
413 
414 		try {
415 			git.stashApply().call();
416 			fail("Exception not thrown");
417 		} catch (StashApplyFailureException e) {
418 			// expected
419  		}
420 		assertEquals("content3", read(PATH));
421 		recorder.assertNoEvent();
422 	}
423 
424 	@Test
425 	public void stashedContentMerge() throws Exception {
426 		writeTrashFile(PATH, "content\nmore content\n");
427 		git.add().addFilepattern(PATH).call();
428 		git.commit().setMessage("more content").call();
429 
430 		writeTrashFile(PATH, "content\nhead change\nmore content\n");
431 		git.add().addFilepattern(PATH).call();
432 		git.commit().setMessage("even content").call();
433 
434 		writeTrashFile(PATH, "content\nstashed change\nmore content\n");
435 
436 		RevCommit stashed = git.stashCreate().call();
437 		assertNotNull(stashed);
438 		assertEquals("content\nhead change\nmore content\n",
439 				read(committedFile));
440 		assertTrue(git.status().call().isClean());
441 		recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
442 
443 		writeTrashFile(PATH, "content\nmore content\ncommitted change\n");
444 		git.add().addFilepattern(PATH).call();
445 		git.commit().setMessage("committed change").call();
446 		recorder.assertNoEvent();
447 
448 		try {
449 			git.stashApply().call();
450 			fail("Expected conflict");
451 		} catch (StashApplyFailureException e) {
452 			// expected
453 		}
454 		recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
455 		Status status = new StatusCommand(db).call();
456 		assertEquals(1, status.getConflicting().size());
457 		assertEquals(
458 				"content\n<<<<<<< HEAD\n=======\nstashed change\n>>>>>>> stash\nmore content\ncommitted change\n",
459 				read(PATH));
460 	}
461 
462 	@Test
463 	public void stashedApplyOnOtherBranch() throws Exception {
464 		writeTrashFile(PATH, "content\nmore content\n");
465 		git.add().addFilepattern(PATH).call();
466 		git.commit().setMessage("more content").call();
467 		String path2 = "file2.txt";
468 		File file2 = writeTrashFile(path2, "content\nmore content\n");
469 		git.add().addFilepattern(PATH).call();
470 		git.add().addFilepattern(path2).call();
471 		git.commit().setMessage("even content").call();
472 
473 		String otherBranch = "otherBranch";
474 		git.branchCreate().setName(otherBranch).call();
475 
476 		writeTrashFile(PATH, "master content");
477 		git.add().addFilepattern(PATH).call();
478 		git.commit().setMessage("even content").call();
479 		recorder.assertNoEvent();
480 
481 		git.checkout().setName(otherBranch).call();
482 		recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
483 
484 		writeTrashFile(PATH, "otherBranch content");
485 		git.add().addFilepattern(PATH).call();
486 		git.commit().setMessage("even more content").call();
487 		recorder.assertNoEvent();
488 
489 		writeTrashFile(path2, "content\nstashed change\nmore content\n");
490 
491 		RevCommit stashed = git.stashCreate().call();
492 
493 		assertNotNull(stashed);
494 		assertEquals("content\nmore content\n", read(file2));
495 		assertEquals("otherBranch content",
496 				read(committedFile));
497 		assertTrue(git.status().call().isClean());
498 		recorder.assertEvent(new String[] { path2 }, ChangeRecorder.EMPTY);
499 
500 		git.checkout().setName("master").call();
501 		recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
502 		git.stashApply().call();
503 		assertEquals("content\nstashed change\nmore content\n", read(file2));
504 		assertEquals("master content",
505 				read(committedFile));
506 		recorder.assertEvent(new String[] { path2 }, ChangeRecorder.EMPTY);
507 	}
508 
509 	@Test
510 	public void stashedApplyOnOtherBranchWithStagedChange() throws Exception {
511 		writeTrashFile(PATH, "content\nmore content\n");
512 		git.add().addFilepattern(PATH).call();
513 		git.commit().setMessage("more content").call();
514 		String path2 = "file2.txt";
515 		File file2 = writeTrashFile(path2, "content\nmore content\n");
516 		git.add().addFilepattern(PATH).call();
517 		git.add().addFilepattern(path2).call();
518 		git.commit().setMessage("even content").call();
519 
520 		String otherBranch = "otherBranch";
521 		git.branchCreate().setName(otherBranch).call();
522 
523 		writeTrashFile(PATH, "master content");
524 		git.add().addFilepattern(PATH).call();
525 		git.commit().setMessage("even content").call();
526 		recorder.assertNoEvent();
527 
528 		git.checkout().setName(otherBranch).call();
529 		recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
530 
531 		writeTrashFile(PATH, "otherBranch content");
532 		git.add().addFilepattern(PATH).call();
533 		git.commit().setMessage("even more content").call();
534 		recorder.assertNoEvent();
535 
536 		writeTrashFile(path2,
537 				"content\nstashed change in index\nmore content\n");
538 		git.add().addFilepattern(path2).call();
539 		writeTrashFile(path2, "content\nstashed change\nmore content\n");
540 
541 		RevCommit stashed = git.stashCreate().call();
542 
543 		assertNotNull(stashed);
544 		assertEquals("content\nmore content\n", read(file2));
545 		assertEquals("otherBranch content", read(committedFile));
546 		assertTrue(git.status().call().isClean());
547 		recorder.assertEvent(new String[] { path2 }, ChangeRecorder.EMPTY);
548 
549 		git.checkout().setName("master").call();
550 		recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
551 		git.stashApply().call();
552 		assertEquals("content\nstashed change\nmore content\n", read(file2));
553 		assertEquals(
554 				"[file.txt, mode:100644, content:master content]"
555 						+ "[file2.txt, mode:100644, content:content\nstashed change in index\nmore content\n]",
556 				indexState(CONTENT));
557 		assertEquals("master content", read(committedFile));
558 		recorder.assertEvent(new String[] { path2 }, ChangeRecorder.EMPTY);
559 	}
560 
561 	@Test
562 	public void workingDirectoryContentMerge() throws Exception {
563 		writeTrashFile(PATH, "content\nmore content\n");
564 		git.add().addFilepattern(PATH).call();
565 		git.commit().setMessage("more content").call();
566 		recorder.assertNoEvent();
567 
568 		writeTrashFile(PATH, "content\nstashed change\nmore content\n");
569 
570 		RevCommit stashed = git.stashCreate().call();
571 		assertNotNull(stashed);
572 		assertEquals("content\nmore content\n", read(committedFile));
573 		assertTrue(git.status().call().isClean());
574 		recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
575 
576 		writeTrashFile(PATH, "content\nmore content\ncommitted change\n");
577 		git.add().addFilepattern(PATH).call();
578 		git.commit().setMessage("committed change").call();
579 		recorder.assertNoEvent();
580 
581 		git.stashApply().call();
582 		assertEquals(
583 				"content\nstashed change\nmore content\ncommitted change\n",
584 				read(committedFile));
585 		recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
586 	}
587 
588 	@Test
589 	public void indexContentConflict() throws Exception {
590 		writeTrashFile(PATH, "content2");
591 
592 		RevCommit stashed = git.stashCreate().call();
593 		assertNotNull(stashed);
594 		assertEquals("content", read(committedFile));
595 		assertTrue(git.status().call().isClean());
596 		recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
597 
598 		writeTrashFile(PATH, "content3");
599 		git.add().addFilepattern(PATH).call();
600 		writeTrashFile(PATH, "content2");
601 
602 		try {
603 			git.stashApply().call();
604 			fail("Exception not thrown");
605 		} catch (StashApplyFailureException e) {
606 			// expected
607 		}
608 		recorder.assertNoEvent();
609 		assertEquals("content2", read(PATH));
610 	}
611 
612 	@Test
613 	public void workingDirectoryEditPreCommit() throws Exception {
614 		writeTrashFile(PATH, "content2");
615 
616 		RevCommit stashed = git.stashCreate().call();
617 		assertNotNull(stashed);
618 		assertEquals("content", read(committedFile));
619 		assertTrue(git.status().call().isClean());
620 		recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
621 
622 		String path2 = "file2.txt";
623 		writeTrashFile(path2, "content3");
624 		git.add().addFilepattern(path2).call();
625 		assertNotNull(git.commit().setMessage("adding file").call());
626 
627 		ObjectId unstashed = git.stashApply().call();
628 		assertEquals(stashed, unstashed);
629 		recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
630 
631 		Status status = git.status().call();
632 		assertTrue(status.getAdded().isEmpty());
633 		assertTrue(status.getChanged().isEmpty());
634 		assertTrue(status.getConflicting().isEmpty());
635 		assertTrue(status.getMissing().isEmpty());
636 		assertTrue(status.getRemoved().isEmpty());
637 		assertTrue(status.getUntracked().isEmpty());
638 
639 		assertEquals(1, status.getModified().size());
640 		assertTrue(status.getModified().contains(PATH));
641 	}
642 
643 	@Test
644 	public void stashChangeInANewSubdirectory() throws Exception {
645 		String subdir = "subdir";
646 		String fname = "file2.txt";
647 		String path = subdir + "/" + fname;
648 		String otherBranch = "otherbranch";
649 
650 		writeTrashFile(subdir, fname, "content2");
651 
652 		git.add().addFilepattern(path).call();
653 		RevCommit stashed = git.stashCreate().call();
654 		assertNotNull(stashed);
655 		assertTrue(git.status().call().isClean());
656 		recorder.assertEvent(ChangeRecorder.EMPTY,
657 				new String[] { subdir, path });
658 
659 		git.branchCreate().setName(otherBranch).call();
660 		git.checkout().setName(otherBranch).call();
661 
662 		ObjectId unstashed = git.stashApply().call();
663 		assertEquals(stashed, unstashed);
664 		recorder.assertEvent(new String[] { path }, ChangeRecorder.EMPTY);
665 
666 		Status status = git.status().call();
667 		assertTrue(status.getChanged().isEmpty());
668 		assertTrue(status.getConflicting().isEmpty());
669 		assertTrue(status.getMissing().isEmpty());
670 		assertTrue(status.getRemoved().isEmpty());
671 		assertTrue(status.getModified().isEmpty());
672 		assertTrue(status.getUntracked().isEmpty());
673 
674 		assertEquals(1, status.getAdded().size());
675 		assertTrue(status.getAdded().contains(path));
676 	}
677 
678 	@Test
679 	public void unstashNonStashCommit() throws Exception {
680 		try {
681 			git.stashApply().setStashRef(head.name()).call();
682 			fail("Exception not thrown");
683 		} catch (JGitInternalException e) {
684 			assertEquals(MessageFormat.format(
685 					JGitText.get().stashCommitIncorrectNumberOfParents,
686 					head.name(), "0"),
687 					e.getMessage());
688 		}
689 	}
690 
691 	@Test
692 	public void unstashNoHead() throws Exception {
693 		Repository repo = createWorkRepository();
694 		try {
695 			Git.wrap(repo).stashApply().call();
696 			fail("Exception not thrown");
697 		} catch (NoHeadException e) {
698 			assertNotNull(e.getMessage());
699 		}
700 	}
701 
702 	@Test
703 	public void noStashedCommits() throws Exception {
704 		try {
705 			git.stashApply().call();
706 			fail("Exception not thrown");
707 		} catch (InvalidRefNameException e) {
708 			assertNotNull(e.getMessage());
709 		}
710 	}
711 
712 	@Test
713 	public void testApplyStashWithDeletedFile() throws Exception {
714 		File file = writeTrashFile("file", "content");
715 		git.add().addFilepattern("file").call();
716 		git.commit().setMessage("x").call();
717 		file.delete();
718 		git.rm().addFilepattern("file").call();
719 		recorder.assertNoEvent();
720 		git.stashCreate().call();
721 		recorder.assertEvent(new String[] { "file" }, ChangeRecorder.EMPTY);
722 		file.delete();
723 
724 		git.stashApply().setStashRef("stash@{0}").call();
725 
726 		assertFalse(file.exists());
727 		recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { "file" });
728 	}
729 
730 	@Test
731 	public void untrackedFileNotIncluded() throws Exception {
732 		String untrackedPath = "untracked.txt";
733 		File untrackedFile = writeTrashFile(untrackedPath, "content");
734 		// at least one modification needed
735 		writeTrashFile(PATH, "content2");
736 		git.add().addFilepattern(PATH).call();
737 		git.stashCreate().call();
738 		assertTrue(untrackedFile.exists());
739 		recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
740 
741 		git.stashApply().setStashRef("stash@{0}").call();
742 		assertTrue(untrackedFile.exists());
743 		recorder.assertEvent(new String[] { PATH }, ChangeRecorder.EMPTY);
744 
745 		Status status = git.status().call();
746 		assertEquals(1, status.getUntracked().size());
747 		assertTrue(status.getUntracked().contains(untrackedPath));
748 		assertEquals(1, status.getChanged().size());
749 		assertTrue(status.getChanged().contains(PATH));
750 		assertTrue(status.getAdded().isEmpty());
751 		assertTrue(status.getConflicting().isEmpty());
752 		assertTrue(status.getMissing().isEmpty());
753 		assertTrue(status.getRemoved().isEmpty());
754 		assertTrue(status.getModified().isEmpty());
755 	}
756 
757 	@Test
758 	public void untrackedFileIncluded() throws Exception {
759 		String path = "a/b/untracked.txt";
760 		File untrackedFile = writeTrashFile(path, "content");
761 		RevCommit stashedCommit = git.stashCreate().setIncludeUntracked(true)
762 				.call();
763 		assertNotNull(stashedCommit);
764 		assertFalse(untrackedFile.exists());
765 		recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { path });
766 
767 		deleteTrashFile("a/b"); // checkout should create parent dirs
768 
769 		git.stashApply().setStashRef("stash@{0}").call();
770 		assertTrue(untrackedFile.exists());
771 		assertEquals("content", read(path));
772 		recorder.assertEvent(new String[] { path }, ChangeRecorder.EMPTY);
773 
774 		Status status = git.status().call();
775 		assertEquals(1, status.getUntracked().size());
776 		assertTrue(status.getAdded().isEmpty());
777 		assertTrue(status.getChanged().isEmpty());
778 		assertTrue(status.getConflicting().isEmpty());
779 		assertTrue(status.getMissing().isEmpty());
780 		assertTrue(status.getRemoved().isEmpty());
781 		assertTrue(status.getModified().isEmpty());
782 		assertTrue(status.getUntracked().contains(path));
783 	}
784 
785 	@Test
786 	public void untrackedFileConflictsWithCommit() throws Exception {
787 		String path = "untracked.txt";
788 		writeTrashFile(path, "untracked");
789 		git.stashCreate().setIncludeUntracked(true).call();
790 		recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { path });
791 
792 		writeTrashFile(path, "committed");
793 		head = git.commit().setMessage("add file").call();
794 		git.add().addFilepattern(path).call();
795 		git.commit().setMessage("conflicting commit").call();
796 
797 		try {
798 			git.stashApply().setStashRef("stash@{0}").call();
799 			fail("StashApplyFailureException should be thrown.");
800 		} catch (StashApplyFailureException e) {
801 			assertEquals(e.getMessage(), JGitText.get().stashApplyConflict);
802 		}
803 		assertEquals("committed", read(path));
804 		recorder.assertNoEvent();
805 	}
806 
807 	@Test
808 	public void untrackedFileConflictsWithWorkingDirectory()
809 			throws Exception {
810 		String path = "untracked.txt";
811 		writeTrashFile(path, "untracked");
812 		git.stashCreate().setIncludeUntracked(true).call();
813 		recorder.assertEvent(ChangeRecorder.EMPTY, new String[] { path });
814 
815 		writeTrashFile(path, "working-directory");
816 		try {
817 			git.stashApply().setStashRef("stash@{0}").call();
818 			fail("StashApplyFailureException should be thrown.");
819 		} catch (StashApplyFailureException e) {
820 			assertEquals(e.getMessage(), JGitText.get().stashApplyConflict);
821 		}
822 		assertEquals("working-directory", read(path));
823 		recorder.assertNoEvent();
824 	}
825 
826 	@Test
827 	public void untrackedAndTrackedChanges() throws Exception {
828 		writeTrashFile(PATH, "changed");
829 		String path = "untracked.txt";
830 		writeTrashFile(path, "untracked");
831 		git.stashCreate().setIncludeUntracked(true).call();
832 		assertTrue(PATH + " should exist", check(PATH));
833 		assertEquals(PATH + " should have been reset", "content", read(PATH));
834 		assertFalse(path + " should not exist", check(path));
835 		recorder.assertEvent(new String[] { PATH }, new String[] { path });
836 		git.stashApply().setStashRef("stash@{0}").call();
837 		assertTrue(PATH + " should exist", check(PATH));
838 		assertEquals(PATH + " should have new content", "changed", read(PATH));
839 		assertTrue(path + " should exist", check(path));
840 		assertEquals(path + " should have new content", "untracked",
841 				read(path));
842 		recorder.assertEvent(new String[] { PATH, path }, ChangeRecorder.EMPTY);
843 	}
844 }