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