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