You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ResetCommandTest.java 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. /*
  2. * Copyright (C) 2011-2013, 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. import static org.eclipse.jgit.api.ResetCommand.ResetType.HARD;
  45. import static org.junit.Assert.assertEquals;
  46. import static org.junit.Assert.assertFalse;
  47. import static org.junit.Assert.assertNotNull;
  48. import static org.junit.Assert.assertNull;
  49. import static org.junit.Assert.assertTrue;
  50. import static org.junit.Assert.fail;
  51. import java.io.File;
  52. import java.io.IOException;
  53. import java.io.PrintWriter;
  54. import org.eclipse.jgit.api.ResetCommand.ResetType;
  55. import org.eclipse.jgit.api.errors.GitAPIException;
  56. import org.eclipse.jgit.api.errors.JGitInternalException;
  57. import org.eclipse.jgit.dircache.DirCache;
  58. import org.eclipse.jgit.dircache.DirCacheBuilder;
  59. import org.eclipse.jgit.dircache.DirCacheEntry;
  60. import org.eclipse.jgit.errors.AmbiguousObjectException;
  61. import org.eclipse.jgit.junit.RepositoryTestCase;
  62. import org.eclipse.jgit.lib.Constants;
  63. import org.eclipse.jgit.lib.FileMode;
  64. import org.eclipse.jgit.lib.ObjectId;
  65. import org.eclipse.jgit.revwalk.RevCommit;
  66. import org.eclipse.jgit.revwalk.RevWalk;
  67. import org.eclipse.jgit.treewalk.TreeWalk;
  68. import org.eclipse.jgit.util.FileUtils;
  69. import org.junit.Assert;
  70. import org.junit.Test;
  71. public class ResetCommandTest extends RepositoryTestCase {
  72. private Git git;
  73. private RevCommit initialCommit;
  74. private RevCommit secondCommit;
  75. private File indexFile;
  76. private File untrackedFile;
  77. private DirCacheEntry prestage;
  78. public void setupRepository() throws IOException, JGitInternalException,
  79. GitAPIException {
  80. // create initial commit
  81. git = new Git(db);
  82. initialCommit = git.commit().setMessage("initial commit").call();
  83. // create nested file
  84. File dir = new File(db.getWorkTree(), "dir");
  85. FileUtils.mkdir(dir);
  86. File nestedFile = new File(dir, "b.txt");
  87. FileUtils.createNewFile(nestedFile);
  88. PrintWriter nesterFileWriter = new PrintWriter(nestedFile);
  89. nesterFileWriter.print("content");
  90. nesterFileWriter.flush();
  91. // create file
  92. indexFile = new File(db.getWorkTree(), "a.txt");
  93. FileUtils.createNewFile(indexFile);
  94. PrintWriter writer = new PrintWriter(indexFile);
  95. writer.print("content");
  96. writer.flush();
  97. // add file and commit it
  98. git.add().addFilepattern("dir").addFilepattern("a.txt").call();
  99. secondCommit = git.commit().setMessage("adding a.txt and dir/b.txt")
  100. .call();
  101. prestage = DirCache.read(db.getIndexFile(), db.getFS()).getEntry(
  102. indexFile.getName());
  103. // modify file and add to index
  104. writer.print("new content");
  105. writer.close();
  106. nesterFileWriter.print("new content");
  107. nesterFileWriter.close();
  108. git.add().addFilepattern("a.txt").addFilepattern("dir").call();
  109. // create a file not added to the index
  110. untrackedFile = new File(db.getWorkTree(),
  111. "notAddedToIndex.txt");
  112. FileUtils.createNewFile(untrackedFile);
  113. PrintWriter writer2 = new PrintWriter(untrackedFile);
  114. writer2.print("content");
  115. writer2.close();
  116. }
  117. @Test
  118. public void testHardReset() throws JGitInternalException,
  119. AmbiguousObjectException, IOException, GitAPIException {
  120. setupRepository();
  121. ObjectId prevHead = db.resolve(Constants.HEAD);
  122. git.reset().setMode(ResetType.HARD).setRef(initialCommit.getName())
  123. .call();
  124. // check if HEAD points to initial commit now
  125. ObjectId head = db.resolve(Constants.HEAD);
  126. assertEquals(initialCommit, head);
  127. // check if files were removed
  128. assertFalse(indexFile.exists());
  129. assertTrue(untrackedFile.exists());
  130. // fileInIndex must no longer be in HEAD and in the index
  131. String fileInIndexPath = indexFile.getAbsolutePath();
  132. assertFalse(inHead(fileInIndexPath));
  133. assertFalse(inIndex(indexFile.getName()));
  134. assertReflog(prevHead, head);
  135. assertEquals(prevHead, db.readOrigHead());
  136. }
  137. @Test
  138. public void testResetToNonexistingHEAD() throws JGitInternalException,
  139. AmbiguousObjectException, IOException, GitAPIException {
  140. // create a file in the working tree of a fresh repo
  141. git = new Git(db);
  142. writeTrashFile("f", "content");
  143. try {
  144. git.reset().setRef(Constants.HEAD).call();
  145. fail("Expected JGitInternalException didn't occur");
  146. } catch (JGitInternalException e) {
  147. // got the expected exception
  148. }
  149. }
  150. @Test
  151. public void testSoftReset() throws JGitInternalException,
  152. AmbiguousObjectException, IOException, GitAPIException {
  153. setupRepository();
  154. ObjectId prevHead = db.resolve(Constants.HEAD);
  155. git.reset().setMode(ResetType.SOFT).setRef(initialCommit.getName())
  156. .call();
  157. // check if HEAD points to initial commit now
  158. ObjectId head = db.resolve(Constants.HEAD);
  159. assertEquals(initialCommit, head);
  160. // check if files still exist
  161. assertTrue(untrackedFile.exists());
  162. assertTrue(indexFile.exists());
  163. // fileInIndex must no longer be in HEAD but has to be in the index
  164. String fileInIndexPath = indexFile.getAbsolutePath();
  165. assertFalse(inHead(fileInIndexPath));
  166. assertTrue(inIndex(indexFile.getName()));
  167. assertReflog(prevHead, head);
  168. assertEquals(prevHead, db.readOrigHead());
  169. }
  170. @Test
  171. public void testMixedReset() throws JGitInternalException,
  172. AmbiguousObjectException, IOException, GitAPIException {
  173. setupRepository();
  174. ObjectId prevHead = db.resolve(Constants.HEAD);
  175. git.reset().setMode(ResetType.MIXED).setRef(initialCommit.getName())
  176. .call();
  177. // check if HEAD points to initial commit now
  178. ObjectId head = db.resolve(Constants.HEAD);
  179. assertEquals(initialCommit, head);
  180. // check if files still exist
  181. assertTrue(untrackedFile.exists());
  182. assertTrue(indexFile.exists());
  183. // fileInIndex must no longer be in HEAD and in the index
  184. String fileInIndexPath = indexFile.getAbsolutePath();
  185. assertFalse(inHead(fileInIndexPath));
  186. assertFalse(inIndex(indexFile.getName()));
  187. assertReflog(prevHead, head);
  188. assertEquals(prevHead, db.readOrigHead());
  189. }
  190. @Test
  191. public void testMixedResetRetainsSizeAndModifiedTime() throws Exception {
  192. git = new Git(db);
  193. writeTrashFile("a.txt", "a").setLastModified(
  194. System.currentTimeMillis() - 60 * 1000);
  195. assertNotNull(git.add().addFilepattern("a.txt").call());
  196. assertNotNull(git.commit().setMessage("a commit").call());
  197. writeTrashFile("b.txt", "b").setLastModified(
  198. System.currentTimeMillis() - 60 * 1000);
  199. assertNotNull(git.add().addFilepattern("b.txt").call());
  200. RevCommit commit2 = git.commit().setMessage("b commit").call();
  201. assertNotNull(commit2);
  202. DirCache cache = db.readDirCache();
  203. DirCacheEntry aEntry = cache.getEntry("a.txt");
  204. assertNotNull(aEntry);
  205. assertTrue(aEntry.getLength() > 0);
  206. assertTrue(aEntry.getLastModified() > 0);
  207. DirCacheEntry bEntry = cache.getEntry("b.txt");
  208. assertNotNull(bEntry);
  209. assertTrue(bEntry.getLength() > 0);
  210. assertTrue(bEntry.getLastModified() > 0);
  211. git.reset().setMode(ResetType.MIXED).setRef(commit2.getName()).call();
  212. cache = db.readDirCache();
  213. DirCacheEntry mixedAEntry = cache.getEntry("a.txt");
  214. assertNotNull(mixedAEntry);
  215. assertEquals(aEntry.getLastModified(), mixedAEntry.getLastModified());
  216. assertEquals(aEntry.getLastModified(), mixedAEntry.getLastModified());
  217. DirCacheEntry mixedBEntry = cache.getEntry("b.txt");
  218. assertNotNull(mixedBEntry);
  219. assertEquals(bEntry.getLastModified(), mixedBEntry.getLastModified());
  220. assertEquals(bEntry.getLastModified(), mixedBEntry.getLastModified());
  221. }
  222. @Test
  223. public void testMixedResetWithUnmerged() throws Exception {
  224. git = new Git(db);
  225. String file = "a.txt";
  226. writeTrashFile(file, "data");
  227. String file2 = "b.txt";
  228. writeTrashFile(file2, "data");
  229. git.add().addFilepattern(file).addFilepattern(file2).call();
  230. git.commit().setMessage("commit").call();
  231. DirCache index = db.lockDirCache();
  232. DirCacheBuilder builder = index.builder();
  233. builder.add(createEntry(file, FileMode.REGULAR_FILE, 1, ""));
  234. builder.add(createEntry(file, FileMode.REGULAR_FILE, 2, ""));
  235. builder.add(createEntry(file, FileMode.REGULAR_FILE, 3, ""));
  236. assertTrue(builder.commit());
  237. assertEquals("[a.txt, mode:100644, stage:1]"
  238. + "[a.txt, mode:100644, stage:2]"
  239. + "[a.txt, mode:100644, stage:3]",
  240. indexState(0));
  241. git.reset().setMode(ResetType.MIXED).call();
  242. assertEquals("[a.txt, mode:100644]" + "[b.txt, mode:100644]",
  243. indexState(0));
  244. }
  245. @Test
  246. public void testPathsReset() throws Exception {
  247. setupRepository();
  248. DirCacheEntry preReset = DirCache.read(db.getIndexFile(), db.getFS())
  249. .getEntry(indexFile.getName());
  250. assertNotNull(preReset);
  251. git.add().addFilepattern(untrackedFile.getName()).call();
  252. // 'a.txt' has already been modified in setupRepository
  253. // 'notAddedToIndex.txt' has been added to repository
  254. git.reset().addPath(indexFile.getName())
  255. .addPath(untrackedFile.getName()).call();
  256. DirCacheEntry postReset = DirCache.read(db.getIndexFile(), db.getFS())
  257. .getEntry(indexFile.getName());
  258. assertNotNull(postReset);
  259. Assert.assertNotSame(preReset.getObjectId(), postReset.getObjectId());
  260. Assert.assertEquals(prestage.getObjectId(), postReset.getObjectId());
  261. // check that HEAD hasn't moved
  262. ObjectId head = db.resolve(Constants.HEAD);
  263. assertEquals(secondCommit, head);
  264. // check if files still exist
  265. assertTrue(untrackedFile.exists());
  266. assertTrue(indexFile.exists());
  267. assertTrue(inHead(indexFile.getName()));
  268. assertTrue(inIndex(indexFile.getName()));
  269. assertFalse(inIndex(untrackedFile.getName()));
  270. }
  271. @Test
  272. public void testPathsResetOnDirs() throws Exception {
  273. setupRepository();
  274. DirCacheEntry preReset = DirCache.read(db.getIndexFile(), db.getFS())
  275. .getEntry("dir/b.txt");
  276. assertNotNull(preReset);
  277. git.add().addFilepattern(untrackedFile.getName()).call();
  278. // 'dir/b.txt' has already been modified in setupRepository
  279. git.reset().addPath("dir").call();
  280. DirCacheEntry postReset = DirCache.read(db.getIndexFile(), db.getFS())
  281. .getEntry("dir/b.txt");
  282. assertNotNull(postReset);
  283. Assert.assertNotSame(preReset.getObjectId(), postReset.getObjectId());
  284. // check that HEAD hasn't moved
  285. ObjectId head = db.resolve(Constants.HEAD);
  286. assertEquals(secondCommit, head);
  287. // check if files still exist
  288. assertTrue(untrackedFile.exists());
  289. assertTrue(inHead("dir/b.txt"));
  290. assertTrue(inIndex("dir/b.txt"));
  291. }
  292. @Test
  293. public void testPathsResetWithRef() throws Exception {
  294. setupRepository();
  295. DirCacheEntry preReset = DirCache.read(db.getIndexFile(), db.getFS())
  296. .getEntry(indexFile.getName());
  297. assertNotNull(preReset);
  298. git.add().addFilepattern(untrackedFile.getName()).call();
  299. // 'a.txt' has already been modified in setupRepository
  300. // 'notAddedToIndex.txt' has been added to repository
  301. // reset to the inital commit
  302. git.reset().setRef(initialCommit.getName())
  303. .addPath(indexFile.getName())
  304. .addPath(untrackedFile.getName()).call();
  305. // check that HEAD hasn't moved
  306. ObjectId head = db.resolve(Constants.HEAD);
  307. assertEquals(secondCommit, head);
  308. // check if files still exist
  309. assertTrue(untrackedFile.exists());
  310. assertTrue(indexFile.exists());
  311. assertTrue(inHead(indexFile.getName()));
  312. assertFalse(inIndex(indexFile.getName()));
  313. assertFalse(inIndex(untrackedFile.getName()));
  314. }
  315. @Test
  316. public void testPathsResetWithUnmerged() throws Exception {
  317. setupRepository();
  318. String file = "a.txt";
  319. writeTrashFile(file, "data");
  320. git.add().addFilepattern(file).call();
  321. git.commit().setMessage("commit").call();
  322. DirCache index = db.lockDirCache();
  323. DirCacheBuilder builder = index.builder();
  324. builder.add(createEntry(file, FileMode.REGULAR_FILE, 1, ""));
  325. builder.add(createEntry(file, FileMode.REGULAR_FILE, 2, ""));
  326. builder.add(createEntry(file, FileMode.REGULAR_FILE, 3, ""));
  327. builder.add(createEntry("b.txt", FileMode.REGULAR_FILE));
  328. assertTrue(builder.commit());
  329. assertEquals("[a.txt, mode:100644, stage:1]"
  330. + "[a.txt, mode:100644, stage:2]"
  331. + "[a.txt, mode:100644, stage:3]"
  332. + "[b.txt, mode:100644]",
  333. indexState(0));
  334. git.reset().addPath(file).call();
  335. assertEquals("[a.txt, mode:100644]" + "[b.txt, mode:100644]",
  336. indexState(0));
  337. }
  338. @Test
  339. public void testPathsResetOnUnbornBranch() throws Exception {
  340. git = new Git(db);
  341. writeTrashFile("a.txt", "content");
  342. git.add().addFilepattern("a.txt").call();
  343. // Should assume an empty tree, like in C Git 1.8.2
  344. git.reset().addPath("a.txt").call();
  345. DirCache cache = db.readDirCache();
  346. DirCacheEntry aEntry = cache.getEntry("a.txt");
  347. assertNull(aEntry);
  348. }
  349. @Test(expected = JGitInternalException.class)
  350. public void testPathsResetToNonexistingRef() throws Exception {
  351. git = new Git(db);
  352. writeTrashFile("a.txt", "content");
  353. git.add().addFilepattern("a.txt").call();
  354. git.reset().setRef("doesnotexist").addPath("a.txt").call();
  355. }
  356. @Test
  357. public void testHardResetOnTag() throws Exception {
  358. setupRepository();
  359. String tagName = "initialtag";
  360. git.tag().setName(tagName).setObjectId(secondCommit)
  361. .setMessage("message").call();
  362. DirCacheEntry preReset = DirCache.read(db.getIndexFile(), db.getFS())
  363. .getEntry(indexFile.getName());
  364. assertNotNull(preReset);
  365. git.add().addFilepattern(untrackedFile.getName()).call();
  366. git.reset().setRef(tagName).setMode(HARD).call();
  367. ObjectId head = db.resolve(Constants.HEAD);
  368. assertEquals(secondCommit, head);
  369. }
  370. @Test
  371. public void testHardResetAfterSquashMerge() throws Exception {
  372. Git g = new Git(db);
  373. writeTrashFile("file1", "file1");
  374. g.add().addFilepattern("file1").call();
  375. RevCommit first = g.commit().setMessage("initial commit").call();
  376. assertTrue(new File(db.getWorkTree(), "file1").exists());
  377. createBranch(first, "refs/heads/branch1");
  378. checkoutBranch("refs/heads/branch1");
  379. writeTrashFile("file2", "file2");
  380. g.add().addFilepattern("file2").call();
  381. g.commit().setMessage("second commit").call();
  382. assertTrue(new File(db.getWorkTree(), "file2").exists());
  383. checkoutBranch("refs/heads/master");
  384. MergeResult result = g.merge().include(db.getRef("branch1"))
  385. .setSquash(true).call();
  386. assertEquals(MergeResult.MergeStatus.FAST_FORWARD_SQUASHED,
  387. result.getMergeStatus());
  388. assertNotNull(db.readSquashCommitMsg());
  389. g.reset().setMode(ResetType.HARD).setRef(first.getName()).call();
  390. assertNull(db.readSquashCommitMsg());
  391. }
  392. @Test
  393. public void testHardResetOnUnbornBranch() throws Exception {
  394. git = new Git(db);
  395. File fileA = writeTrashFile("a.txt", "content");
  396. git.add().addFilepattern("a.txt").call();
  397. // Should assume an empty tree, like in C Git 1.8.2
  398. git.reset().setMode(ResetType.HARD).call();
  399. DirCache cache = db.readDirCache();
  400. DirCacheEntry aEntry = cache.getEntry("a.txt");
  401. assertNull(aEntry);
  402. assertFalse(fileA.exists());
  403. assertNull(db.resolve(Constants.HEAD));
  404. }
  405. private void assertReflog(ObjectId prevHead, ObjectId head)
  406. throws IOException {
  407. // Check the reflog for HEAD
  408. String actualHeadMessage = db.getReflogReader(Constants.HEAD)
  409. .getLastEntry().getComment();
  410. String expectedHeadMessage = head.getName() + ": updating HEAD";
  411. assertEquals(expectedHeadMessage, actualHeadMessage);
  412. assertEquals(head.getName(), db.getReflogReader(Constants.HEAD)
  413. .getLastEntry().getNewId().getName());
  414. assertEquals(prevHead.getName(), db.getReflogReader(Constants.HEAD)
  415. .getLastEntry().getOldId().getName());
  416. // The reflog for master contains the same as the one for HEAD
  417. String actualMasterMessage = db.getReflogReader("refs/heads/master")
  418. .getLastEntry().getComment();
  419. String expectedMasterMessage = head.getName() + ": updating HEAD"; // yes!
  420. assertEquals(expectedMasterMessage, actualMasterMessage);
  421. assertEquals(head.getName(), db.getReflogReader(Constants.HEAD)
  422. .getLastEntry().getNewId().getName());
  423. assertEquals(prevHead.getName(), db
  424. .getReflogReader("refs/heads/master").getLastEntry().getOldId()
  425. .getName());
  426. }
  427. /**
  428. * Checks if a file with the given path exists in the HEAD tree
  429. *
  430. * @param path
  431. * @return true if the file exists
  432. * @throws IOException
  433. */
  434. private boolean inHead(String path) throws IOException {
  435. ObjectId headId = db.resolve(Constants.HEAD);
  436. RevWalk rw = new RevWalk(db);
  437. TreeWalk tw = null;
  438. try {
  439. tw = TreeWalk.forPath(db, path, rw.parseTree(headId));
  440. return tw != null;
  441. } finally {
  442. rw.release();
  443. rw.dispose();
  444. if (tw != null)
  445. tw.release();
  446. }
  447. }
  448. /**
  449. * Checks if a file with the given path exists in the index
  450. *
  451. * @param path
  452. * @return true if the file exists
  453. * @throws IOException
  454. */
  455. private boolean inIndex(String path) throws IOException {
  456. DirCache dc = DirCache.read(db.getIndexFile(), db.getFS());
  457. return dc.getEntry(path) != null;
  458. }
  459. }