1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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.assertNull;
49 import static org.junit.Assert.assertTrue;
50
51 import java.io.File;
52 import java.io.IOException;
53 import java.util.List;
54
55 import org.eclipse.jgit.api.errors.UnmergedPathsException;
56 import org.eclipse.jgit.diff.DiffEntry;
57 import org.eclipse.jgit.junit.RepositoryTestCase;
58 import org.eclipse.jgit.lib.Constants;
59 import org.eclipse.jgit.lib.ObjectId;
60 import org.eclipse.jgit.lib.PersonIdent;
61 import org.eclipse.jgit.lib.Ref;
62 import org.eclipse.jgit.lib.ReflogEntry;
63 import org.eclipse.jgit.lib.ReflogReader;
64 import org.eclipse.jgit.revwalk.RevCommit;
65 import org.eclipse.jgit.revwalk.RevWalk;
66 import org.eclipse.jgit.treewalk.TreeWalk;
67 import org.eclipse.jgit.treewalk.filter.TreeFilter;
68 import org.eclipse.jgit.util.FileUtils;
69 import org.junit.Before;
70 import org.junit.Test;
71
72
73
74
75 public class StashCreateCommandTest extends RepositoryTestCase {
76
77 private RevCommit head;
78
79 private Git git;
80
81 private File committedFile;
82
83 private File untrackedFile;
84
85 @Override
86 @Before
87 public void setUp() throws Exception {
88 super.setUp();
89 git = Git.wrap(db);
90 committedFile = writeTrashFile("file.txt", "content");
91 git.add().addFilepattern("file.txt").call();
92 head = git.commit().setMessage("add file").call();
93 assertNotNull(head);
94 untrackedFile = writeTrashFile("untracked.txt", "content");
95 }
96
97 private void validateStashedCommit(RevCommit commit)
98 throws IOException {
99 validateStashedCommit(commit, 2);
100 }
101
102
103
104
105
106
107
108
109
110 private void validateStashedCommit(final RevCommit commit,
111 int parentCount)
112 throws IOException {
113 assertNotNull(commit);
114 Ref stashRef = db.exactRef(Constants.R_STASH);
115 assertNotNull(stashRef);
116 assertEquals(commit, stashRef.getObjectId());
117 assertNotNull(commit.getAuthorIdent());
118 assertEquals(commit.getAuthorIdent(), commit.getCommitterIdent());
119 assertEquals(parentCount, commit.getParentCount());
120
121
122 try (RevWalk walk = new RevWalk(db)) {
123 for (RevCommit parent : commit.getParents())
124 walk.parseBody(parent);
125 }
126
127 assertEquals(1, commit.getParent(1).getParentCount());
128 assertEquals(head, commit.getParent(1).getParent(0));
129 assertFalse("Head tree matches stashed commit tree", commit.getTree()
130 .equals(head.getTree()));
131 assertEquals(head, commit.getParent(0));
132 assertFalse(commit.getFullMessage().equals(
133 commit.getParent(1).getFullMessage()));
134 }
135
136 private TreeWalk createTreeWalk() {
137 TreeWalk walk = new TreeWalk(db);
138 walk.setRecursive(true);
139 walk.setFilter(TreeFilter.ANY_DIFF);
140 return walk;
141 }
142
143 private List<DiffEntry> diffWorkingAgainstHead(RevCommit commit)
144 throws IOException {
145 try (TreeWalk walk = createTreeWalk()) {
146 walk.addTree(commit.getParent(0).getTree());
147 walk.addTree(commit.getTree());
148 return DiffEntry.scan(walk);
149 }
150 }
151
152 private List<DiffEntry> diffIndexAgainstHead(RevCommit commit)
153 throws IOException {
154 try (TreeWalk walk = createTreeWalk()) {
155 walk.addTree(commit.getParent(0).getTree());
156 walk.addTree(commit.getParent(1).getTree());
157 return DiffEntry.scan(walk);
158 }
159 }
160
161 private List<DiffEntry> diffIndexAgainstWorking(RevCommit commit)
162 throws IOException {
163 try (TreeWalk walk = createTreeWalk()) {
164 walk.addTree(commit.getParent(1).getTree());
165 walk.addTree(commit.getTree());
166 return DiffEntry.scan(walk);
167 }
168 }
169
170 @Test
171 public void noLocalChanges() throws Exception {
172 assertNull(git.stashCreate().call());
173 }
174
175 @Test
176 public void workingDirectoryDelete() throws Exception {
177 deleteTrashFile("file.txt");
178 RevCommit stashed = git.stashCreate().call();
179 assertNotNull(stashed);
180 assertEquals("content", read(committedFile));
181 validateStashedCommit(stashed);
182
183 assertEquals(head.getTree(), stashed.getParent(1).getTree());
184
185 List<DiffEntry> diffs = diffWorkingAgainstHead(stashed);
186 assertEquals(1, diffs.size());
187 assertEquals(DiffEntry.ChangeType.DELETE, diffs.get(0).getChangeType());
188 assertEquals("file.txt", diffs.get(0).getOldPath());
189 }
190
191 @Test
192 public void indexAdd() throws Exception {
193 File addedFile = writeTrashFile("file2.txt", "content2");
194 git.add().addFilepattern("file2.txt").call();
195
196 RevCommit stashed = Git.wrap(db).stashCreate().call();
197 assertNotNull(stashed);
198 assertFalse(addedFile.exists());
199 validateStashedCommit(stashed);
200
201 assertEquals(stashed.getTree(), stashed.getParent(1).getTree());
202
203 List<DiffEntry> diffs = diffWorkingAgainstHead(stashed);
204 assertEquals(1, diffs.size());
205 assertEquals(DiffEntry.ChangeType.ADD, diffs.get(0).getChangeType());
206 assertEquals("file2.txt", diffs.get(0).getNewPath());
207 }
208
209 @Test
210 public void newFileInIndexThenModifiedInWorkTree() throws Exception {
211 writeTrashFile("file", "content");
212 git.add().addFilepattern("file").call();
213 writeTrashFile("file", "content2");
214 RevCommit stashedWorkTree = Git.wrap(db).stashCreate().call();
215 validateStashedCommit(stashedWorkTree);
216 try (RevWalk walk = new RevWalk(db)) {
217 RevCommit stashedIndex = stashedWorkTree.getParent(1);
218 walk.parseBody(stashedIndex);
219 walk.parseBody(stashedIndex.getTree());
220 walk.parseBody(stashedIndex.getParent(0));
221 }
222 List<DiffEntry> workTreeStashAgainstWorkTree = diffWorkingAgainstHead(stashedWorkTree);
223 assertEquals(1, workTreeStashAgainstWorkTree.size());
224 List<DiffEntry> workIndexAgainstWorkTree = diffIndexAgainstHead(stashedWorkTree);
225 assertEquals(1, workIndexAgainstWorkTree.size());
226 List<DiffEntry> indexStashAgainstWorkTree = diffIndexAgainstWorking(stashedWorkTree);
227 assertEquals(1, indexStashAgainstWorkTree.size());
228 }
229
230 @Test
231 public void indexDelete() throws Exception {
232 git.rm().addFilepattern("file.txt").call();
233
234 RevCommit stashed = Git.wrap(db).stashCreate().call();
235 assertNotNull(stashed);
236 assertEquals("content", read(committedFile));
237 validateStashedCommit(stashed);
238
239 assertEquals(stashed.getTree(), stashed.getParent(1).getTree());
240
241 List<DiffEntry> diffs = diffWorkingAgainstHead(stashed);
242 assertEquals(1, diffs.size());
243 assertEquals(DiffEntry.ChangeType.DELETE, diffs.get(0).getChangeType());
244 assertEquals("file.txt", diffs.get(0).getOldPath());
245 }
246
247 @Test
248 public void workingDirectoryModify() throws Exception {
249 writeTrashFile("file.txt", "content2");
250
251 RevCommit stashed = Git.wrap(db).stashCreate().call();
252 assertNotNull(stashed);
253 assertEquals("content", read(committedFile));
254 validateStashedCommit(stashed);
255
256 assertEquals(head.getTree(), stashed.getParent(1).getTree());
257
258 List<DiffEntry> diffs = diffWorkingAgainstHead(stashed);
259 assertEquals(1, diffs.size());
260 assertEquals(DiffEntry.ChangeType.MODIFY, diffs.get(0).getChangeType());
261 assertEquals("file.txt", diffs.get(0).getNewPath());
262 }
263
264 @Test
265 public void workingDirectoryModifyInSubfolder() throws Exception {
266 String path = "d1/d2/f.txt";
267 File subfolderFile = writeTrashFile(path, "content");
268 git.add().addFilepattern(path).call();
269 head = git.commit().setMessage("add file").call();
270
271 writeTrashFile(path, "content2");
272
273 RevCommit stashed = Git.wrap(db).stashCreate().call();
274 assertNotNull(stashed);
275 assertEquals("content", read(subfolderFile));
276 validateStashedCommit(stashed);
277
278 assertEquals(head.getTree(), stashed.getParent(1).getTree());
279
280 List<DiffEntry> diffs = diffWorkingAgainstHead(stashed);
281 assertEquals(1, diffs.size());
282 assertEquals(DiffEntry.ChangeType.MODIFY, diffs.get(0).getChangeType());
283 assertEquals(path, diffs.get(0).getNewPath());
284 }
285
286 @Test
287 public void workingDirectoryModifyIndexChanged() throws Exception {
288 writeTrashFile("file.txt", "content2");
289 git.add().addFilepattern("file.txt").call();
290 writeTrashFile("file.txt", "content3");
291
292 RevCommit stashed = Git.wrap(db).stashCreate().call();
293 assertNotNull(stashed);
294 assertEquals("content", read(committedFile));
295 validateStashedCommit(stashed);
296
297 assertFalse(stashed.getTree().equals(stashed.getParent(1).getTree()));
298
299 List<DiffEntry> workingDiffs = diffWorkingAgainstHead(stashed);
300 assertEquals(1, workingDiffs.size());
301 assertEquals(DiffEntry.ChangeType.MODIFY, workingDiffs.get(0)
302 .getChangeType());
303 assertEquals("file.txt", workingDiffs.get(0).getNewPath());
304
305 List<DiffEntry> indexDiffs = diffIndexAgainstHead(stashed);
306 assertEquals(1, indexDiffs.size());
307 assertEquals(DiffEntry.ChangeType.MODIFY, indexDiffs.get(0)
308 .getChangeType());
309 assertEquals("file.txt", indexDiffs.get(0).getNewPath());
310
311 assertEquals(workingDiffs.get(0).getOldId(), indexDiffs.get(0)
312 .getOldId());
313 assertFalse(workingDiffs.get(0).getNewId()
314 .equals(indexDiffs.get(0).getNewId()));
315 }
316
317 @Test
318 public void workingDirectoryCleanIndexModify() throws Exception {
319 writeTrashFile("file.txt", "content2");
320 git.add().addFilepattern("file.txt").call();
321 writeTrashFile("file.txt", "content");
322
323 RevCommit stashed = Git.wrap(db).stashCreate().call();
324 assertNotNull(stashed);
325 assertEquals("content", read(committedFile));
326 validateStashedCommit(stashed);
327
328 assertEquals(stashed.getParent(1).getTree(), stashed.getTree());
329
330 List<DiffEntry> workingDiffs = diffWorkingAgainstHead(stashed);
331 assertEquals(1, workingDiffs.size());
332 assertEquals(DiffEntry.ChangeType.MODIFY, workingDiffs.get(0)
333 .getChangeType());
334 assertEquals("file.txt", workingDiffs.get(0).getNewPath());
335
336 List<DiffEntry> indexDiffs = diffIndexAgainstHead(stashed);
337 assertEquals(1, indexDiffs.size());
338 assertEquals(DiffEntry.ChangeType.MODIFY, indexDiffs.get(0)
339 .getChangeType());
340 assertEquals("file.txt", indexDiffs.get(0).getNewPath());
341
342 assertEquals(workingDiffs.get(0).getOldId(), indexDiffs.get(0)
343 .getOldId());
344 assertTrue(workingDiffs.get(0).getNewId()
345 .equals(indexDiffs.get(0).getNewId()));
346 }
347
348 @Test
349 public void workingDirectoryDeleteIndexAdd() throws Exception {
350 String path = "file2.txt";
351 File added = writeTrashFile(path, "content2");
352 assertTrue(added.exists());
353 git.add().addFilepattern(path).call();
354 FileUtils.delete(added);
355 assertFalse(added.exists());
356
357 RevCommit stashed = Git.wrap(db).stashCreate().call();
358 assertNotNull(stashed);
359 assertFalse(added.exists());
360
361 validateStashedCommit(stashed);
362
363 assertEquals(stashed.getParent(1).getTree(), stashed.getTree());
364
365 List<DiffEntry> workingDiffs = diffWorkingAgainstHead(stashed);
366 assertEquals(1, workingDiffs.size());
367 assertEquals(DiffEntry.ChangeType.ADD, workingDiffs.get(0)
368 .getChangeType());
369 assertEquals(path, workingDiffs.get(0).getNewPath());
370
371 List<DiffEntry> indexDiffs = diffIndexAgainstHead(stashed);
372 assertEquals(1, indexDiffs.size());
373 assertEquals(DiffEntry.ChangeType.ADD, indexDiffs.get(0)
374 .getChangeType());
375 assertEquals(path, indexDiffs.get(0).getNewPath());
376
377 assertEquals(workingDiffs.get(0).getOldId(), indexDiffs.get(0)
378 .getOldId());
379 assertTrue(workingDiffs.get(0).getNewId()
380 .equals(indexDiffs.get(0).getNewId()));
381 }
382
383 @Test
384 public void workingDirectoryDeleteIndexEdit() throws Exception {
385 File edited = writeTrashFile("file.txt", "content2");
386 git.add().addFilepattern("file.txt").call();
387 FileUtils.delete(edited);
388 assertFalse(edited.exists());
389
390 RevCommit stashed = Git.wrap(db).stashCreate().call();
391 assertNotNull(stashed);
392 assertEquals("content", read(committedFile));
393 validateStashedCommit(stashed);
394
395 assertFalse(stashed.getTree().equals(stashed.getParent(1).getTree()));
396
397 List<DiffEntry> workingDiffs = diffWorkingAgainstHead(stashed);
398 assertEquals(1, workingDiffs.size());
399 assertEquals(DiffEntry.ChangeType.DELETE, workingDiffs.get(0)
400 .getChangeType());
401 assertEquals("file.txt", workingDiffs.get(0).getOldPath());
402
403 List<DiffEntry> indexDiffs = diffIndexAgainstHead(stashed);
404 assertEquals(1, indexDiffs.size());
405 assertEquals(DiffEntry.ChangeType.MODIFY, indexDiffs.get(0)
406 .getChangeType());
407 assertEquals("file.txt", indexDiffs.get(0).getNewPath());
408
409 assertEquals(workingDiffs.get(0).getOldId(), indexDiffs.get(0)
410 .getOldId());
411 assertFalse(workingDiffs.get(0).getNewId()
412 .equals(indexDiffs.get(0).getNewId()));
413 }
414
415 @Test
416 public void multipleEdits() throws Exception {
417 git.rm().addFilepattern("file.txt").call();
418 File addedFile = writeTrashFile("file2.txt", "content2");
419 git.add().addFilepattern("file2.txt").call();
420
421 RevCommit stashed = Git.wrap(db).stashCreate().call();
422 assertNotNull(stashed);
423 assertFalse(addedFile.exists());
424 validateStashedCommit(stashed);
425
426 assertEquals(stashed.getTree(), stashed.getParent(1).getTree());
427
428 List<DiffEntry> diffs = diffWorkingAgainstHead(stashed);
429 assertEquals(2, diffs.size());
430 assertEquals(DiffEntry.ChangeType.DELETE, diffs.get(0).getChangeType());
431 assertEquals("file.txt", diffs.get(0).getOldPath());
432 assertEquals(DiffEntry.ChangeType.ADD, diffs.get(1).getChangeType());
433 assertEquals("file2.txt", diffs.get(1).getNewPath());
434 }
435
436 @Test
437 public void refLogIncludesCommitMessage() throws Exception {
438 PersonIdent who = new PersonIdent("user", "user@email.com");
439 deleteTrashFile("file.txt");
440 RevCommit stashed = git.stashCreate().setPerson(who).call();
441 assertNotNull(stashed);
442 assertEquals("content", read(committedFile));
443 validateStashedCommit(stashed);
444
445 ReflogReader reader = git.getRepository().getReflogReader(
446 Constants.R_STASH);
447 ReflogEntry entry = reader.getLastEntry();
448 assertNotNull(entry);
449 assertEquals(ObjectId.zeroId(), entry.getOldId());
450 assertEquals(stashed, entry.getNewId());
451 assertEquals(who, entry.getWho());
452 assertEquals(stashed.getFullMessage(), entry.getComment());
453 }
454
455 @Test(expected = UnmergedPathsException.class)
456 public void unmergedPathsShouldCauseException() throws Exception {
457 commitFile("file.txt", "master", "base");
458 RevCommit side = commitFile("file.txt", "side", "side");
459 commitFile("file.txt", "master", "master");
460 git.merge().include(side).call();
461
462 git.stashCreate().call();
463 }
464
465 @Test
466 public void untrackedFileIncluded() throws Exception {
467 String trackedPath = "tracked.txt";
468 writeTrashFile(trackedPath, "content2");
469 git.add().addFilepattern(trackedPath).call();
470
471 RevCommit stashed = git.stashCreate()
472 .setIncludeUntracked(true).call();
473 validateStashedCommit(stashed, 3);
474
475 assertEquals(
476 "Expected commits for workingDir,stashedIndex and untrackedFiles.",
477 3, stashed.getParentCount());
478 assertFalse("untracked file should be deleted.", untrackedFile.exists());
479 }
480
481 @Test
482 public void untrackedFileNotIncluded() throws Exception {
483 String trackedPath = "tracked.txt";
484
485 writeTrashFile(trackedPath, "content2");
486 git.add().addFilepattern(trackedPath).call();
487
488 RevCommit stashed = git.stashCreate().call();
489 validateStashedCommit(stashed);
490
491 assertTrue("untracked file should be left untouched.",
492 untrackedFile.exists());
493 assertEquals("content", read(untrackedFile));
494 }
495 }