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 testResetToNonexistingHEAD() throws JGitInternalException,
198 AmbiguousObjectException, IOException, GitAPIException {
199
200
201 git = new Git(db);
202 writeTrashFile("f", "content");
203
204 try {
205 git.reset().setRef(Constants.HEAD).call();
206 fail("Expected JGitInternalException didn't occur");
207 } catch (JGitInternalException e) {
208
209 }
210 }
211
212 @Test
213 public void testSoftReset() throws JGitInternalException,
214 AmbiguousObjectException, IOException, GitAPIException {
215 setupRepository();
216 ObjectId prevHead = db.resolve(Constants.HEAD);
217 assertSameAsHead(git.reset().setMode(ResetType.SOFT)
218 .setRef(initialCommit.getName()).call());
219
220 ObjectId head = db.resolve(Constants.HEAD);
221 assertEquals(initialCommit, head);
222
223 assertTrue(untrackedFile.exists());
224 assertTrue(indexFile.exists());
225
226 String fileInIndexPath = indexFile.getAbsolutePath();
227 assertFalse(inHead(fileInIndexPath));
228 assertTrue(inIndex(indexFile.getName()));
229 assertReflog(prevHead, head);
230 assertEquals(prevHead, db.readOrigHead());
231 }
232
233 @Test
234 public void testMixedReset() throws JGitInternalException,
235 AmbiguousObjectException, IOException, GitAPIException {
236 setupRepository();
237 ObjectId prevHead = db.resolve(Constants.HEAD);
238 assertSameAsHead(git.reset().setMode(ResetType.MIXED)
239 .setRef(initialCommit.getName()).call());
240
241 ObjectId head = db.resolve(Constants.HEAD);
242 assertEquals(initialCommit, head);
243
244 assertTrue(untrackedFile.exists());
245 assertTrue(indexFile.exists());
246
247 String fileInIndexPath = indexFile.getAbsolutePath();
248 assertFalse(inHead(fileInIndexPath));
249 assertFalse(inIndex(indexFile.getName()));
250
251 assertReflog(prevHead, head);
252 assertEquals(prevHead, db.readOrigHead());
253 }
254
255 @Test
256 public void testMixedResetRetainsSizeAndModifiedTime() throws Exception {
257 git = new Git(db);
258
259 Files.setLastModifiedTime(writeTrashFile("a.txt", "a").toPath(),
260 FileTime.from(Instant.now().minusSeconds(60)));
261 assertNotNull(git.add().addFilepattern("a.txt").call());
262 assertNotNull(git.commit().setMessage("a commit").call());
263
264 Files.setLastModifiedTime(writeTrashFile("b.txt", "b").toPath(),
265 FileTime.from(Instant.now().minusSeconds(60)));
266 assertNotNull(git.add().addFilepattern("b.txt").call());
267 RevCommit commit2 = git.commit().setMessage("b commit").call();
268 assertNotNull(commit2);
269
270 DirCache cache = db.readDirCache();
271
272 DirCacheEntry aEntry = cache.getEntry("a.txt");
273 assertNotNull(aEntry);
274 assertTrue(aEntry.getLength() > 0);
275 assertTrue(aEntry.getLastModifiedInstant().compareTo(EPOCH) > 0);
276
277 DirCacheEntry bEntry = cache.getEntry("b.txt");
278 assertNotNull(bEntry);
279 assertTrue(bEntry.getLength() > 0);
280 assertTrue(bEntry.getLastModifiedInstant().compareTo(EPOCH) > 0);
281
282 assertSameAsHead(git.reset().setMode(ResetType.MIXED)
283 .setRef(commit2.getName()).call());
284
285 cache = db.readDirCache();
286
287 DirCacheEntry mixedAEntry = cache.getEntry("a.txt");
288 assertNotNull(mixedAEntry);
289 assertEquals(aEntry.getLastModifiedInstant(),
290 mixedAEntry.getLastModifiedInstant());
291 assertEquals(aEntry.getLastModifiedInstant(),
292 mixedAEntry.getLastModifiedInstant());
293
294 DirCacheEntry mixedBEntry = cache.getEntry("b.txt");
295 assertNotNull(mixedBEntry);
296 assertEquals(bEntry.getLastModifiedInstant(),
297 mixedBEntry.getLastModifiedInstant());
298 assertEquals(bEntry.getLastModifiedInstant(),
299 mixedBEntry.getLastModifiedInstant());
300 }
301
302 @Test
303 public void testMixedResetWithUnmerged() throws Exception {
304 git = new Git(db);
305
306 String file = "a.txt";
307 writeTrashFile(file, "data");
308 String file2 = "b.txt";
309 writeTrashFile(file2, "data");
310
311 git.add().addFilepattern(file).addFilepattern(file2).call();
312 git.commit().setMessage("commit").call();
313
314 DirCache index = db.lockDirCache();
315 DirCacheBuilder builder = index.builder();
316 builder.add(createEntry(file, FileMode.REGULAR_FILE, 1, ""));
317 builder.add(createEntry(file, FileMode.REGULAR_FILE, 2, ""));
318 builder.add(createEntry(file, FileMode.REGULAR_FILE, 3, ""));
319 assertTrue(builder.commit());
320
321 assertEquals("[a.txt, mode:100644, stage:1]"
322 + "[a.txt, mode:100644, stage:2]"
323 + "[a.txt, mode:100644, stage:3]",
324 indexState(0));
325
326 assertSameAsHead(git.reset().setMode(ResetType.MIXED).call());
327
328 assertEquals("[a.txt, mode:100644]" + "[b.txt, mode:100644]",
329 indexState(0));
330 }
331
332 @Test
333 public void testPathsReset() throws Exception {
334 setupRepository();
335
336 DirCacheEntry preReset = DirCache.read(db.getIndexFile(), db.getFS())
337 .getEntry(indexFile.getName());
338 assertNotNull(preReset);
339
340 git.add().addFilepattern(untrackedFile.getName()).call();
341
342
343
344 assertSameAsHead(git.reset().addPath(indexFile.getName())
345 .addPath(untrackedFile.getName()).call());
346
347 DirCacheEntry postReset = DirCache.read(db.getIndexFile(), db.getFS())
348 .getEntry(indexFile.getName());
349 assertNotNull(postReset);
350 Assert.assertNotSame(preReset.getObjectId(), postReset.getObjectId());
351 Assert.assertEquals(prestage.getObjectId(), postReset.getObjectId());
352
353
354 ObjectId head = db.resolve(Constants.HEAD);
355 assertEquals(secondCommit, head);
356
357 assertTrue(untrackedFile.exists());
358 assertTrue(indexFile.exists());
359 assertTrue(inHead(indexFile.getName()));
360 assertTrue(inIndex(indexFile.getName()));
361 assertFalse(inIndex(untrackedFile.getName()));
362 }
363
364 @Test
365 public void testPathsResetOnDirs() throws Exception {
366 setupRepository();
367
368 DirCacheEntry preReset = DirCache.read(db.getIndexFile(), db.getFS())
369 .getEntry("dir/b.txt");
370 assertNotNull(preReset);
371
372 git.add().addFilepattern(untrackedFile.getName()).call();
373
374
375 assertSameAsHead(git.reset().addPath("dir").call());
376
377 DirCacheEntry postReset = DirCache.read(db.getIndexFile(), db.getFS())
378 .getEntry("dir/b.txt");
379 assertNotNull(postReset);
380 Assert.assertNotSame(preReset.getObjectId(), postReset.getObjectId());
381
382
383 ObjectId head = db.resolve(Constants.HEAD);
384 assertEquals(secondCommit, head);
385
386 assertTrue(untrackedFile.exists());
387 assertTrue(inHead("dir/b.txt"));
388 assertTrue(inIndex("dir/b.txt"));
389 }
390
391 @Test
392 public void testPathsResetWithRef() throws Exception {
393 setupRepository();
394
395 DirCacheEntry preReset = DirCache.read(db.getIndexFile(), db.getFS())
396 .getEntry(indexFile.getName());
397 assertNotNull(preReset);
398
399 git.add().addFilepattern(untrackedFile.getName()).call();
400
401
402
403
404 assertSameAsHead(git.reset().setRef(initialCommit.getName())
405 .addPath(indexFile.getName()).addPath(untrackedFile.getName())
406 .call());
407
408
409 ObjectId head = db.resolve(Constants.HEAD);
410 assertEquals(secondCommit, head);
411
412 assertTrue(untrackedFile.exists());
413 assertTrue(indexFile.exists());
414 assertTrue(inHead(indexFile.getName()));
415 assertFalse(inIndex(indexFile.getName()));
416 assertFalse(inIndex(untrackedFile.getName()));
417 }
418
419 @Test
420 public void testPathsResetWithUnmerged() throws Exception {
421 setupRepository();
422
423 String file = "a.txt";
424 writeTrashFile(file, "data");
425
426 git.add().addFilepattern(file).call();
427 git.commit().setMessage("commit").call();
428
429 DirCache index = db.lockDirCache();
430 DirCacheBuilder builder = index.builder();
431 builder.add(createEntry(file, FileMode.REGULAR_FILE, 1, ""));
432 builder.add(createEntry(file, FileMode.REGULAR_FILE, 2, ""));
433 builder.add(createEntry(file, FileMode.REGULAR_FILE, 3, ""));
434 builder.add(createEntry("b.txt", FileMode.REGULAR_FILE));
435 assertTrue(builder.commit());
436
437 assertEquals("[a.txt, mode:100644, stage:1]"
438 + "[a.txt, mode:100644, stage:2]"
439 + "[a.txt, mode:100644, stage:3]"
440 + "[b.txt, mode:100644]",
441 indexState(0));
442
443 assertSameAsHead(git.reset().addPath(file).call());
444
445 assertEquals("[a.txt, mode:100644]" + "[b.txt, mode:100644]",
446 indexState(0));
447 }
448
449 @Test
450 public void testPathsResetOnUnbornBranch() throws Exception {
451 git = new Git(db);
452 writeTrashFile("a.txt", "content");
453 git.add().addFilepattern("a.txt").call();
454
455 assertSameAsHead(git.reset().addPath("a.txt").call());
456
457 DirCache cache = db.readDirCache();
458 DirCacheEntry aEntry = cache.getEntry("a.txt");
459 assertNull(aEntry);
460 }
461
462 @Test(expected = JGitInternalException.class)
463 public void testPathsResetToNonexistingRef() throws Exception {
464 git = new Git(db);
465 writeTrashFile("a.txt", "content");
466 git.add().addFilepattern("a.txt").call();
467 assertSameAsHead(
468 git.reset().setRef("doesnotexist").addPath("a.txt").call());
469 }
470
471 @Test
472 public void testResetDefaultMode() throws Exception {
473 git = new Git(db);
474 writeTrashFile("a.txt", "content");
475 git.add().addFilepattern("a.txt").call();
476 writeTrashFile("a.txt", "modified");
477
478 assertSameAsHead(git.reset().call());
479
480 DirCache cache = db.readDirCache();
481 DirCacheEntry aEntry = cache.getEntry("a.txt");
482 assertNull(aEntry);
483 assertEquals("modified", read("a.txt"));
484 }
485
486 @Test
487 public void testHardResetOnTag() throws Exception {
488 setupRepository();
489 String tagName = "initialtag";
490 git.tag().setName(tagName).setObjectId(secondCommit)
491 .setMessage("message").call();
492
493 DirCacheEntry preReset = DirCache.read(db.getIndexFile(), db.getFS())
494 .getEntry(indexFile.getName());
495 assertNotNull(preReset);
496
497 git.add().addFilepattern(untrackedFile.getName()).call();
498
499 assertSameAsHead(git.reset().setRef(tagName).setMode(HARD).call());
500
501 ObjectId head = db.resolve(Constants.HEAD);
502 assertEquals(secondCommit, head);
503 }
504
505 @Test
506 public void testHardResetAfterSquashMerge() throws Exception {
507 git = new Git(db);
508
509 writeTrashFile("file1", "file1");
510 git.add().addFilepattern("file1").call();
511 RevCommit first = git.commit().setMessage("initial commit").call();
512
513 assertTrue(new File(db.getWorkTree(), "file1").exists());
514 createBranch(first, "refs/heads/branch1");
515 checkoutBranch("refs/heads/branch1");
516
517 writeTrashFile("file2", "file2");
518 git.add().addFilepattern("file2").call();
519 git.commit().setMessage("second commit").call();
520 assertTrue(new File(db.getWorkTree(), "file2").exists());
521
522 checkoutBranch("refs/heads/master");
523
524 MergeResult result = git.merge()
525 .include(db.exactRef("refs/heads/branch1"))
526 .setSquash(true)
527 .call();
528
529 assertEquals(MergeResult.MergeStatus.FAST_FORWARD_SQUASHED,
530 result.getMergeStatus());
531 assertNotNull(db.readSquashCommitMsg());
532
533 assertSameAsHead(git.reset().setMode(ResetType.HARD)
534 .setRef(first.getName()).call());
535
536 assertNull(db.readSquashCommitMsg());
537 }
538
539 @Test
540 public void testHardResetOnUnbornBranch() throws Exception {
541 git = new Git(db);
542 File fileA = writeTrashFile("a.txt", "content");
543 git.add().addFilepattern("a.txt").call();
544
545 assertSameAsHead(git.reset().setMode(ResetType.HARD).call());
546
547 DirCache cache = db.readDirCache();
548 DirCacheEntry aEntry = cache.getEntry("a.txt");
549 assertNull(aEntry);
550 assertFalse(fileA.exists());
551 assertNull(db.resolve(Constants.HEAD));
552 }
553
554 private void assertReflog(ObjectId prevHead, ObjectId head)
555 throws IOException {
556
557 String actualHeadMessage = db.getReflogReader(Constants.HEAD)
558 .getLastEntry().getComment();
559 String expectedHeadMessage = head.getName() + ": updating HEAD";
560 assertEquals(expectedHeadMessage, actualHeadMessage);
561 assertEquals(head.getName(), db.getReflogReader(Constants.HEAD)
562 .getLastEntry().getNewId().getName());
563 assertEquals(prevHead.getName(), db.getReflogReader(Constants.HEAD)
564 .getLastEntry().getOldId().getName());
565
566
567 String actualMasterMessage = db.getReflogReader("refs/heads/master")
568 .getLastEntry().getComment();
569 String expectedMasterMessage = head.getName() + ": updating HEAD";
570 assertEquals(expectedMasterMessage, actualMasterMessage);
571 assertEquals(head.getName(), db.getReflogReader(Constants.HEAD)
572 .getLastEntry().getNewId().getName());
573 assertEquals(prevHead.getName(), db
574 .getReflogReader("refs/heads/master").getLastEntry().getOldId()
575 .getName());
576 }
577
578 private void assertReflogDisabled(ObjectId head)
579 throws IOException {
580
581 String actualHeadMessage = db.getReflogReader(Constants.HEAD)
582 .getLastEntry().getComment();
583 String expectedHeadMessage = "commit: adding a.txt and dir/b.txt";
584 assertEquals(expectedHeadMessage, actualHeadMessage);
585 assertEquals(head.getName(), db.getReflogReader(Constants.HEAD)
586 .getLastEntry().getOldId().getName());
587
588
589 String actualMasterMessage = db.getReflogReader("refs/heads/master")
590 .getLastEntry().getComment();
591 String expectedMasterMessage = "commit: adding a.txt and dir/b.txt";
592 assertEquals(expectedMasterMessage, actualMasterMessage);
593 assertEquals(head.getName(), db.getReflogReader(Constants.HEAD)
594 .getLastEntry().getOldId().getName());
595 }
596
597
598
599
600
601
602
603 private boolean inHead(String path) throws IOException {
604 ObjectId headId = db.resolve(Constants.HEAD);
605 try (RevWalk rw = new RevWalk(db);
606 TreeWalk tw = TreeWalk.forPath(db, path,
607 rw.parseTree(headId))) {
608 return tw != null;
609 }
610 }
611
612
613
614
615
616
617
618
619 private boolean inIndex(String path) throws IOException {
620 DirCache dc = DirCache.read(db.getIndexFile(), db.getFS());
621 return dc.getEntry(path) != null;
622 }
623
624
625
626
627
628
629 private void assertSameAsHead(Ref ref) throws IOException {
630 Ref headRef = db.exactRef(Constants.HEAD);
631 assertEquals(headRef.getName(), ref.getName());
632 assertEquals(headRef.getObjectId(), ref.getObjectId());
633 }
634 }