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 java.time.Instant.EPOCH;
46 import static org.eclipse.jgit.api.ResetCommand.ResetType.HARD;
47 import static org.junit.Assert.assertEquals;
48 import static org.junit.Assert.assertFalse;
49 import static org.junit.Assert.assertNotNull;
50 import static org.junit.Assert.assertNull;
51 import static org.junit.Assert.assertTrue;
52 import static org.junit.Assert.fail;
53
54 import java.io.File;
55 import java.io.IOException;
56 import java.nio.file.Files;
57 import java.nio.file.attribute.FileTime;
58 import java.time.Instant;
59
60 import org.eclipse.jgit.api.ResetCommand.ResetType;
61 import org.eclipse.jgit.api.errors.GitAPIException;
62 import org.eclipse.jgit.api.errors.JGitInternalException;
63 import org.eclipse.jgit.dircache.DirCache;
64 import org.eclipse.jgit.dircache.DirCacheBuilder;
65 import org.eclipse.jgit.dircache.DirCacheEntry;
66 import org.eclipse.jgit.errors.AmbiguousObjectException;
67 import org.eclipse.jgit.junit.RepositoryTestCase;
68 import org.eclipse.jgit.lib.Constants;
69 import org.eclipse.jgit.lib.FileMode;
70 import org.eclipse.jgit.lib.ObjectId;
71 import org.eclipse.jgit.lib.Ref;
72 import org.eclipse.jgit.revwalk.RevCommit;
73 import org.eclipse.jgit.revwalk.RevWalk;
74 import org.eclipse.jgit.treewalk.TreeWalk;
75 import org.eclipse.jgit.util.FileUtils;
76 import org.junit.Assert;
77 import org.junit.Test;
78
79 public class ResetCommandTest extends RepositoryTestCase {
80
81 private Git git;
82
83 private RevCommit initialCommit;
84
85 private RevCommit secondCommit;
86
87 private File indexFile;
88
89 private File indexNestedFile;
90
91 private File untrackedFile;
92
93 private DirCacheEntry prestage;
94
95 public void setupRepository() throws IOException, JGitInternalException,
96 GitAPIException {
97
98
99 git = new Git(db);
100 initialCommit = git.commit().setMessage("initial commit").call();
101
102
103 indexFile = writeTrashFile("a.txt", "content");
104
105
106 indexNestedFile = writeTrashFile("dir/b.txt", "content");
107
108
109 git.add().addFilepattern("a.txt").addFilepattern("dir/b.txt").call();
110 secondCommit = git.commit().setMessage("adding a.txt and dir/b.txt").call();
111
112 prestage = DirCache.read(db.getIndexFile(), db.getFS()).getEntry(indexFile.getName());
113
114
115 writeTrashFile("a.txt", "new content");
116 writeTrashFile("dir/b.txt", "new content");
117 git.add().addFilepattern("a.txt").addFilepattern("dir/b.txt").call();
118
119
120 untrackedFile = writeTrashFile("notAddedToIndex.txt", "content");
121 }
122
123 @Test
124 public void testHardReset() throws JGitInternalException,
125 AmbiguousObjectException, IOException, GitAPIException {
126 setupRepository();
127 ObjectId prevHead = db.resolve(Constants.HEAD);
128 ResetCommand reset = git.reset();
129 assertSameAsHead(reset.setMode(ResetType.HARD)
130 .setRef(initialCommit.getName()).call());
131 assertFalse("reflog should be enabled", reset.isReflogDisabled());
132
133 ObjectId head = db.resolve(Constants.HEAD);
134 assertEquals(initialCommit, head);
135
136 assertFalse(indexFile.exists());
137 assertFalse(indexNestedFile.exists());
138 assertTrue(untrackedFile.exists());
139
140 String fileInIndexPath = indexFile.getAbsolutePath();
141 assertFalse(inHead(fileInIndexPath));
142 assertFalse(inIndex(indexFile.getName()));
143 assertReflog(prevHead, head);
144 assertEquals(prevHead, db.readOrigHead());
145 }
146
147 @Test
148 public void testHardResetReflogDisabled() throws Exception {
149 setupRepository();
150 ObjectId prevHead = db.resolve(Constants.HEAD);
151 ResetCommand reset = git.reset();
152 assertSameAsHead(reset.setMode(ResetType.HARD)
153 .setRef(initialCommit.getName()).disableRefLog(true).call());
154 assertTrue("reflog should be disabled", reset.isReflogDisabled());
155
156 ObjectId head = db.resolve(Constants.HEAD);
157 assertEquals(initialCommit, head);
158
159 assertFalse(indexFile.exists());
160 assertFalse(indexNestedFile.exists());
161 assertTrue(untrackedFile.exists());
162
163 String fileInIndexPath = indexFile.getAbsolutePath();
164 assertFalse(inHead(fileInIndexPath));
165 assertFalse(inIndex(indexFile.getName()));
166 assertReflogDisabled(head);
167 assertEquals(prevHead, db.readOrigHead());
168 }
169
170 @Test
171 public void testHardResetWithConflicts_OverwriteUntrackedFile() throws Exception {
172 setupRepository();
173
174 git.rm().setCached(true).addFilepattern("a.txt").call();
175 assertTrue(new File(db.getWorkTree(), "a.txt").exists());
176
177 git.reset().setMode(ResetType.HARD).setRef(Constants.HEAD).call();
178 assertTrue(new File(db.getWorkTree(), "a.txt").exists());
179 assertEquals("content", read(new File(db.getWorkTree(), "a.txt")));
180 }
181
182 @Test
183 public void testHardResetWithConflicts_DeleteFileFolderConflict() throws Exception {
184 setupRepository();
185
186 writeTrashFile("dir-or-file/c.txt", "content");
187 git.add().addFilepattern("dir-or-file/c.txt").call();
188
189 FileUtils.delete(new File(db.getWorkTree(), "dir-or-file"), FileUtils.RECURSIVE);
190 writeTrashFile("dir-or-file", "content");
191
192 git.reset().setMode(ResetType.HARD).setRef(Constants.HEAD).call();
193 assertFalse(new File(db.getWorkTree(), "dir-or-file").exists());
194 }
195
196 @Test
197 public void testHardResetWithConflicts_CreateFolder_UnstagedChanges() throws Exception {
198 setupRepository();
199
200 writeTrashFile("dir-or-file/c.txt", "content");
201 git.add().addFilepattern("dir-or-file/c.txt").call();
202 git.commit().setMessage("adding dir-or-file/c.txt").call();
203
204 FileUtils.delete(new File(db.getWorkTree(), "dir-or-file"), FileUtils.RECURSIVE);
205 writeTrashFile("dir-or-file", "content");
206
207
208 git.reset().setMode(ResetType.HARD).setRef(Constants.HEAD).call();
209 assertTrue(new File(db.getWorkTree(), "dir-or-file/c.txt").exists());
210 }
211
212 @Test
213 public void testHardResetWithConflicts_DeleteFolder_UnstagedChanges() throws Exception {
214 setupRepository();
215 ObjectId prevHead = db.resolve(Constants.HEAD);
216
217 writeTrashFile("dir-or-file/c.txt", "content");
218 git.add().addFilepattern("dir-or-file/c.txt").call();
219 git.commit().setMessage("adding dir-or-file/c.txt").call();
220
221 writeTrashFile("dir-or-file-2/d.txt", "content");
222 git.add().addFilepattern("dir-or-file-2/d.txt").call();
223 FileUtils.delete(new File(db.getWorkTree(), "dir-or-file-2"), FileUtils.RECURSIVE);
224 writeTrashFile("dir-or-file-2", "content");
225
226
227 git.reset().setMode(ResetType.HARD).setRef(prevHead.getName()).call();
228 assertFalse(new File(db.getWorkTree(), "dir-or-file").exists());
229 assertFalse(new File(db.getWorkTree(), "dir-or-file-2").exists());
230 }
231
232 @Test
233 public void testResetToNonexistingHEAD() throws JGitInternalException,
234 AmbiguousObjectException, IOException, GitAPIException {
235
236
237 git = new Git(db);
238 writeTrashFile("f", "content");
239
240 try {
241 git.reset().setRef(Constants.HEAD).call();
242 fail("Expected JGitInternalException didn't occur");
243 } catch (JGitInternalException e) {
244
245 }
246 }
247
248 @Test
249 public void testSoftReset() throws JGitInternalException,
250 AmbiguousObjectException, IOException, GitAPIException {
251 setupRepository();
252 ObjectId prevHead = db.resolve(Constants.HEAD);
253 assertSameAsHead(git.reset().setMode(ResetType.SOFT)
254 .setRef(initialCommit.getName()).call());
255
256 ObjectId head = db.resolve(Constants.HEAD);
257 assertEquals(initialCommit, head);
258
259 assertTrue(untrackedFile.exists());
260 assertTrue(indexFile.exists());
261
262 String fileInIndexPath = indexFile.getAbsolutePath();
263 assertFalse(inHead(fileInIndexPath));
264 assertTrue(inIndex(indexFile.getName()));
265 assertReflog(prevHead, head);
266 assertEquals(prevHead, db.readOrigHead());
267 }
268
269 @Test
270 public void testMixedReset() throws JGitInternalException,
271 AmbiguousObjectException, IOException, GitAPIException {
272 setupRepository();
273 ObjectId prevHead = db.resolve(Constants.HEAD);
274 assertSameAsHead(git.reset().setMode(ResetType.MIXED)
275 .setRef(initialCommit.getName()).call());
276
277 ObjectId head = db.resolve(Constants.HEAD);
278 assertEquals(initialCommit, head);
279
280 assertTrue(untrackedFile.exists());
281 assertTrue(indexFile.exists());
282
283 String fileInIndexPath = indexFile.getAbsolutePath();
284 assertFalse(inHead(fileInIndexPath));
285 assertFalse(inIndex(indexFile.getName()));
286
287 assertReflog(prevHead, head);
288 assertEquals(prevHead, db.readOrigHead());
289 }
290
291 @Test
292 public void testMixedResetRetainsSizeAndModifiedTime() throws Exception {
293 git = new Git(db);
294
295 Files.setLastModifiedTime(writeTrashFile("a.txt", "a").toPath(),
296 FileTime.from(Instant.now().minusSeconds(60)));
297 assertNotNull(git.add().addFilepattern("a.txt").call());
298 assertNotNull(git.commit().setMessage("a commit").call());
299
300 Files.setLastModifiedTime(writeTrashFile("b.txt", "b").toPath(),
301 FileTime.from(Instant.now().minusSeconds(60)));
302 assertNotNull(git.add().addFilepattern("b.txt").call());
303 RevCommit commit2 = git.commit().setMessage("b commit").call();
304 assertNotNull(commit2);
305
306 DirCache cache = db.readDirCache();
307
308 DirCacheEntry aEntry = cache.getEntry("a.txt");
309 assertNotNull(aEntry);
310 assertTrue(aEntry.getLength() > 0);
311 assertTrue(aEntry.getLastModifiedInstant().compareTo(EPOCH) > 0);
312
313 DirCacheEntry bEntry = cache.getEntry("b.txt");
314 assertNotNull(bEntry);
315 assertTrue(bEntry.getLength() > 0);
316 assertTrue(bEntry.getLastModifiedInstant().compareTo(EPOCH) > 0);
317
318 assertSameAsHead(git.reset().setMode(ResetType.MIXED)
319 .setRef(commit2.getName()).call());
320
321 cache = db.readDirCache();
322
323 DirCacheEntry mixedAEntry = cache.getEntry("a.txt");
324 assertNotNull(mixedAEntry);
325 assertEquals(aEntry.getLastModifiedInstant(),
326 mixedAEntry.getLastModifiedInstant());
327 assertEquals(aEntry.getLastModifiedInstant(),
328 mixedAEntry.getLastModifiedInstant());
329
330 DirCacheEntry mixedBEntry = cache.getEntry("b.txt");
331 assertNotNull(mixedBEntry);
332 assertEquals(bEntry.getLastModifiedInstant(),
333 mixedBEntry.getLastModifiedInstant());
334 assertEquals(bEntry.getLastModifiedInstant(),
335 mixedBEntry.getLastModifiedInstant());
336 }
337
338 @Test
339 public void testMixedResetWithUnmerged() throws Exception {
340 git = new Git(db);
341
342 String file = "a.txt";
343 writeTrashFile(file, "data");
344 String file2 = "b.txt";
345 writeTrashFile(file2, "data");
346
347 git.add().addFilepattern(file).addFilepattern(file2).call();
348 git.commit().setMessage("commit").call();
349
350 DirCache index = db.lockDirCache();
351 DirCacheBuilder builder = index.builder();
352 builder.add(createEntry(file, FileMode.REGULAR_FILE, 1, ""));
353 builder.add(createEntry(file, FileMode.REGULAR_FILE, 2, ""));
354 builder.add(createEntry(file, FileMode.REGULAR_FILE, 3, ""));
355 assertTrue(builder.commit());
356
357 assertEquals("[a.txt, mode:100644, stage:1]"
358 + "[a.txt, mode:100644, stage:2]"
359 + "[a.txt, mode:100644, stage:3]",
360 indexState(0));
361
362 assertSameAsHead(git.reset().setMode(ResetType.MIXED).call());
363
364 assertEquals("[a.txt, mode:100644]" + "[b.txt, mode:100644]",
365 indexState(0));
366 }
367
368 @Test
369 public void testPathsReset() throws Exception {
370 setupRepository();
371
372 DirCacheEntry preReset = DirCache.read(db.getIndexFile(), db.getFS())
373 .getEntry(indexFile.getName());
374 assertNotNull(preReset);
375
376 git.add().addFilepattern(untrackedFile.getName()).call();
377
378
379
380 assertSameAsHead(git.reset().addPath(indexFile.getName())
381 .addPath(untrackedFile.getName()).call());
382
383 DirCacheEntry postReset = DirCache.read(db.getIndexFile(), db.getFS())
384 .getEntry(indexFile.getName());
385 assertNotNull(postReset);
386 Assert.assertNotSame(preReset.getObjectId(), postReset.getObjectId());
387 Assert.assertEquals(prestage.getObjectId(), postReset.getObjectId());
388
389
390 ObjectId head = db.resolve(Constants.HEAD);
391 assertEquals(secondCommit, head);
392
393 assertTrue(untrackedFile.exists());
394 assertTrue(indexFile.exists());
395 assertTrue(inHead(indexFile.getName()));
396 assertTrue(inIndex(indexFile.getName()));
397 assertFalse(inIndex(untrackedFile.getName()));
398 }
399
400 @Test
401 public void testPathsResetOnDirs() throws Exception {
402 setupRepository();
403
404 DirCacheEntry preReset = DirCache.read(db.getIndexFile(), db.getFS())
405 .getEntry("dir/b.txt");
406 assertNotNull(preReset);
407
408 git.add().addFilepattern(untrackedFile.getName()).call();
409
410
411 assertSameAsHead(git.reset().addPath("dir").call());
412
413 DirCacheEntry postReset = DirCache.read(db.getIndexFile(), db.getFS())
414 .getEntry("dir/b.txt");
415 assertNotNull(postReset);
416 Assert.assertNotSame(preReset.getObjectId(), postReset.getObjectId());
417
418
419 ObjectId head = db.resolve(Constants.HEAD);
420 assertEquals(secondCommit, head);
421
422 assertTrue(untrackedFile.exists());
423 assertTrue(inHead("dir/b.txt"));
424 assertTrue(inIndex("dir/b.txt"));
425 }
426
427 @Test
428 public void testPathsResetWithRef() throws Exception {
429 setupRepository();
430
431 DirCacheEntry preReset = DirCache.read(db.getIndexFile(), db.getFS())
432 .getEntry(indexFile.getName());
433 assertNotNull(preReset);
434
435 git.add().addFilepattern(untrackedFile.getName()).call();
436
437
438
439
440 assertSameAsHead(git.reset().setRef(initialCommit.getName())
441 .addPath(indexFile.getName()).addPath(untrackedFile.getName())
442 .call());
443
444
445 ObjectId head = db.resolve(Constants.HEAD);
446 assertEquals(secondCommit, head);
447
448 assertTrue(untrackedFile.exists());
449 assertTrue(indexFile.exists());
450 assertTrue(inHead(indexFile.getName()));
451 assertFalse(inIndex(indexFile.getName()));
452 assertFalse(inIndex(untrackedFile.getName()));
453 }
454
455 @Test
456 public void testPathsResetWithUnmerged() throws Exception {
457 setupRepository();
458
459 String file = "a.txt";
460 writeTrashFile(file, "data");
461
462 git.add().addFilepattern(file).call();
463 git.commit().setMessage("commit").call();
464
465 DirCache index = db.lockDirCache();
466 DirCacheBuilder builder = index.builder();
467 builder.add(createEntry(file, FileMode.REGULAR_FILE, 1, ""));
468 builder.add(createEntry(file, FileMode.REGULAR_FILE, 2, ""));
469 builder.add(createEntry(file, FileMode.REGULAR_FILE, 3, ""));
470 builder.add(createEntry("b.txt", FileMode.REGULAR_FILE));
471 assertTrue(builder.commit());
472
473 assertEquals("[a.txt, mode:100644, stage:1]"
474 + "[a.txt, mode:100644, stage:2]"
475 + "[a.txt, mode:100644, stage:3]"
476 + "[b.txt, mode:100644]",
477 indexState(0));
478
479 assertSameAsHead(git.reset().addPath(file).call());
480
481 assertEquals("[a.txt, mode:100644]" + "[b.txt, mode:100644]",
482 indexState(0));
483 }
484
485 @Test
486 public void testPathsResetOnUnbornBranch() throws Exception {
487 git = new Git(db);
488 writeTrashFile("a.txt", "content");
489 git.add().addFilepattern("a.txt").call();
490
491 assertSameAsHead(git.reset().addPath("a.txt").call());
492
493 DirCache cache = db.readDirCache();
494 DirCacheEntry aEntry = cache.getEntry("a.txt");
495 assertNull(aEntry);
496 }
497
498 @Test(expected = JGitInternalException.class)
499 public void testPathsResetToNonexistingRef() throws Exception {
500 git = new Git(db);
501 writeTrashFile("a.txt", "content");
502 git.add().addFilepattern("a.txt").call();
503 assertSameAsHead(
504 git.reset().setRef("doesnotexist").addPath("a.txt").call());
505 }
506
507 @Test
508 public void testResetDefaultMode() throws Exception {
509 git = new Git(db);
510 writeTrashFile("a.txt", "content");
511 git.add().addFilepattern("a.txt").call();
512 writeTrashFile("a.txt", "modified");
513
514 assertSameAsHead(git.reset().call());
515
516 DirCache cache = db.readDirCache();
517 DirCacheEntry aEntry = cache.getEntry("a.txt");
518 assertNull(aEntry);
519 assertEquals("modified", read("a.txt"));
520 }
521
522 @Test
523 public void testHardResetOnTag() throws Exception {
524 setupRepository();
525 String tagName = "initialtag";
526 git.tag().setName(tagName).setObjectId(secondCommit)
527 .setMessage("message").call();
528
529 DirCacheEntry preReset = DirCache.read(db.getIndexFile(), db.getFS())
530 .getEntry(indexFile.getName());
531 assertNotNull(preReset);
532
533 git.add().addFilepattern(untrackedFile.getName()).call();
534
535 assertSameAsHead(git.reset().setRef(tagName).setMode(HARD).call());
536
537 ObjectId head = db.resolve(Constants.HEAD);
538 assertEquals(secondCommit, head);
539 }
540
541 @Test
542 public void testHardResetAfterSquashMerge() throws Exception {
543 git = new Git(db);
544
545 writeTrashFile("file1", "file1");
546 git.add().addFilepattern("file1").call();
547 RevCommit first = git.commit().setMessage("initial commit").call();
548
549 assertTrue(new File(db.getWorkTree(), "file1").exists());
550 createBranch(first, "refs/heads/branch1");
551 checkoutBranch("refs/heads/branch1");
552
553 writeTrashFile("file2", "file2");
554 git.add().addFilepattern("file2").call();
555 git.commit().setMessage("second commit").call();
556 assertTrue(new File(db.getWorkTree(), "file2").exists());
557
558 checkoutBranch("refs/heads/master");
559
560 MergeResult result = git.merge()
561 .include(db.exactRef("refs/heads/branch1"))
562 .setSquash(true)
563 .call();
564
565 assertEquals(MergeResult.MergeStatus.FAST_FORWARD_SQUASHED,
566 result.getMergeStatus());
567 assertNotNull(db.readSquashCommitMsg());
568
569 assertSameAsHead(git.reset().setMode(ResetType.HARD)
570 .setRef(first.getName()).call());
571
572 assertNull(db.readSquashCommitMsg());
573 }
574
575 @Test
576 public void testHardResetOnUnbornBranch() throws Exception {
577 git = new Git(db);
578 File fileA = writeTrashFile("a.txt", "content");
579 git.add().addFilepattern("a.txt").call();
580
581 assertSameAsHead(git.reset().setMode(ResetType.HARD).call());
582
583 DirCache cache = db.readDirCache();
584 DirCacheEntry aEntry = cache.getEntry("a.txt");
585 assertNull(aEntry);
586 assertFalse(fileA.exists());
587 assertNull(db.resolve(Constants.HEAD));
588 }
589
590 private void assertReflog(ObjectId prevHead, ObjectId head)
591 throws IOException {
592
593 String actualHeadMessage = db.getReflogReader(Constants.HEAD)
594 .getLastEntry().getComment();
595 String expectedHeadMessage = head.getName() + ": updating HEAD";
596 assertEquals(expectedHeadMessage, actualHeadMessage);
597 assertEquals(head.getName(), db.getReflogReader(Constants.HEAD)
598 .getLastEntry().getNewId().getName());
599 assertEquals(prevHead.getName(), db.getReflogReader(Constants.HEAD)
600 .getLastEntry().getOldId().getName());
601
602
603 String actualMasterMessage = db.getReflogReader("refs/heads/master")
604 .getLastEntry().getComment();
605 String expectedMasterMessage = head.getName() + ": updating HEAD";
606 assertEquals(expectedMasterMessage, actualMasterMessage);
607 assertEquals(head.getName(), db.getReflogReader(Constants.HEAD)
608 .getLastEntry().getNewId().getName());
609 assertEquals(prevHead.getName(), db
610 .getReflogReader("refs/heads/master").getLastEntry().getOldId()
611 .getName());
612 }
613
614 private void assertReflogDisabled(ObjectId head)
615 throws IOException {
616
617 String actualHeadMessage = db.getReflogReader(Constants.HEAD)
618 .getLastEntry().getComment();
619 String expectedHeadMessage = "commit: adding a.txt and dir/b.txt";
620 assertEquals(expectedHeadMessage, actualHeadMessage);
621 assertEquals(head.getName(), db.getReflogReader(Constants.HEAD)
622 .getLastEntry().getOldId().getName());
623
624
625 String actualMasterMessage = db.getReflogReader("refs/heads/master")
626 .getLastEntry().getComment();
627 String expectedMasterMessage = "commit: adding a.txt and dir/b.txt";
628 assertEquals(expectedMasterMessage, actualMasterMessage);
629 assertEquals(head.getName(), db.getReflogReader(Constants.HEAD)
630 .getLastEntry().getOldId().getName());
631 }
632
633
634
635
636
637
638
639 private boolean inHead(String path) throws IOException {
640 ObjectId headId = db.resolve(Constants.HEAD);
641 try (RevWalk rw = new RevWalk(db);
642 TreeWalk tw = TreeWalk.forPath(db, path,
643 rw.parseTree(headId))) {
644 return tw != null;
645 }
646 }
647
648
649
650
651
652
653
654
655 private boolean inIndex(String path) throws IOException {
656 DirCache dc = DirCache.read(db.getIndexFile(), db.getFS());
657 return dc.getEntry(path) != null;
658 }
659
660
661
662
663
664
665 private void assertSameAsHead(Ref ref) throws IOException {
666 Ref headRef = db.exactRef(Constants.HEAD);
667 assertEquals(headRef.getName(), ref.getName());
668 assertEquals(headRef.getObjectId(), ref.getObjectId());
669 }
670 }