View Javadoc
1   /*
2    * Copyright (C) 2011-2018, Chris Aniszczyk <caniszczyk@gmail.com>
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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  		// create initial commit
99  		git = new Git(db);
100 		initialCommit = git.commit().setMessage("initial commit").call();
101 
102 		// create file
103 		indexFile = writeTrashFile("a.txt", "content");
104 
105 		// create nested file
106 		indexNestedFile = writeTrashFile("dir/b.txt", "content");
107 
108 		// add files and commit them
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 		// modify files and add to index
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 		// create a file not added to the index
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 		// check if HEAD points to initial commit now
133 		ObjectId head = db.resolve(Constants.HEAD);
134 		assertEquals(initialCommit, head);
135 		// check if files were removed
136 		assertFalse(indexFile.exists());
137 		assertFalse(indexNestedFile.exists());
138 		assertTrue(untrackedFile.exists());
139 		// fileInIndex must no longer be in HEAD and in the index
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 		// check if HEAD points to initial commit now
156 		ObjectId head = db.resolve(Constants.HEAD);
157 		assertEquals(initialCommit, head);
158 		// check if files were removed
159 		assertFalse(indexFile.exists());
160 		assertFalse(indexNestedFile.exists());
161 		assertTrue(untrackedFile.exists());
162 		// fileInIndex must no longer be in HEAD and in the index
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 		// bug 479266: cannot create folder "dir-or-file"
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 		// bug 479266: cannot delete folder "dir-or-file"
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 		// create a file in the working tree of a fresh repo
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 			// got the expected exception
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 		// check if HEAD points to initial commit now
256 		ObjectId head = db.resolve(Constants.HEAD);
257 		assertEquals(initialCommit, head);
258 		// check if files still exist
259 		assertTrue(untrackedFile.exists());
260 		assertTrue(indexFile.exists());
261 		// fileInIndex must no longer be in HEAD but has to be in the index
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 		// check if HEAD points to initial commit now
277 		ObjectId head = db.resolve(Constants.HEAD);
278 		assertEquals(initialCommit, head);
279 		// check if files still exist
280 		assertTrue(untrackedFile.exists());
281 		assertTrue(indexFile.exists());
282 		// fileInIndex must no longer be in HEAD and in the index
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 		// 'a.txt' has already been modified in setupRepository
379 		// 'notAddedToIndex.txt' has been added to repository
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 		// check that HEAD hasn't moved
390 		ObjectId head = db.resolve(Constants.HEAD);
391 		assertEquals(secondCommit, head);
392 		// check if files still exist
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 		// 'dir/b.txt' has already been modified in setupRepository
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 		// check that HEAD hasn't moved
419 		ObjectId head = db.resolve(Constants.HEAD);
420 		assertEquals(secondCommit, head);
421 		// check if files still exist
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 		// 'a.txt' has already been modified in setupRepository
438 		// 'notAddedToIndex.txt' has been added to repository
439 		// reset to the inital commit
440 		assertSameAsHead(git.reset().setRef(initialCommit.getName())
441 				.addPath(indexFile.getName()).addPath(untrackedFile.getName())
442 				.call());
443 
444 		// check that HEAD hasn't moved
445 		ObjectId head = db.resolve(Constants.HEAD);
446 		assertEquals(secondCommit, head);
447 		// check if files still exist
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 		// Should assume an empty tree, like in C Git 1.8.2
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 		// should use default mode MIXED
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 		// Should assume an empty tree, like in C Git 1.8.2
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 		// Check the reflog for HEAD
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 		// The reflog for master contains the same as the one for HEAD
603 		String actualMasterMessage = db.getReflogReader("refs/heads/master")
604 				.getLastEntry().getComment();
605 		String expectedMasterMessage = head.getName() + ": updating HEAD"; // yes!
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 		// Check the reflog for HEAD
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 		// The reflog for master contains the same as the one for HEAD
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 	 * Checks if a file with the given path exists in the HEAD tree
634 	 *
635 	 * @param path
636 	 * @return true if the file exists
637 	 * @throws IOException
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 	 * Checks if a file with the given path exists in the index
650 	 *
651 	 * @param path
652 	 * @return true if the file exists
653 	 * @throws IOException
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 	 * Asserts that a certain ref is similar to repos HEAD.
662 	 * @param ref
663 	 * @throws IOException
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 }