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