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.

RevertCommandTest.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. /*
  2. * Copyright (C) 2011, Robin Rosenberg and others
  3. *
  4. * This program and the accompanying materials are made available under the
  5. * terms of the Eclipse Distribution License v. 1.0 which is available at
  6. * https://www.eclipse.org/org/documents/edl-v10.php.
  7. *
  8. * SPDX-License-Identifier: BSD-3-Clause
  9. */
  10. package org.eclipse.jgit.api;
  11. import static org.junit.Assert.assertEquals;
  12. import static org.junit.Assert.assertFalse;
  13. import static org.junit.Assert.assertNotNull;
  14. import static org.junit.Assert.assertNull;
  15. import static org.junit.Assert.assertTrue;
  16. import java.io.File;
  17. import java.io.IOException;
  18. import java.util.Iterator;
  19. import org.eclipse.jgit.api.MergeResult.MergeStatus;
  20. import org.eclipse.jgit.api.ResetCommand.ResetType;
  21. import org.eclipse.jgit.api.errors.GitAPIException;
  22. import org.eclipse.jgit.api.errors.JGitInternalException;
  23. import org.eclipse.jgit.dircache.DirCache;
  24. import org.eclipse.jgit.junit.RepositoryTestCase;
  25. import org.eclipse.jgit.lib.ConfigConstants;
  26. import org.eclipse.jgit.lib.Constants;
  27. import org.eclipse.jgit.lib.FileMode;
  28. import org.eclipse.jgit.lib.ReflogReader;
  29. import org.eclipse.jgit.lib.RepositoryState;
  30. import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason;
  31. import org.eclipse.jgit.revwalk.RevCommit;
  32. import org.junit.Test;
  33. /**
  34. * Test revert command
  35. */
  36. public class RevertCommandTest extends RepositoryTestCase {
  37. @Test
  38. public void testRevert() throws IOException, JGitInternalException,
  39. GitAPIException {
  40. try (Git git = new Git(db)) {
  41. writeTrashFile("a", "first line\nsec. line\nthird line\n");
  42. git.add().addFilepattern("a").call();
  43. git.commit().setMessage("create a").call();
  44. writeTrashFile("b", "content\n");
  45. git.add().addFilepattern("b").call();
  46. git.commit().setMessage("create b").call();
  47. writeTrashFile("a", "first line\nsec. line\nthird line\nfourth line\n");
  48. git.add().addFilepattern("a").call();
  49. git.commit().setMessage("enlarged a").call();
  50. writeTrashFile("a",
  51. "first line\nsecond line\nthird line\nfourth line\n");
  52. git.add().addFilepattern("a").call();
  53. RevCommit fixingA = git.commit().setMessage("fixed a").call();
  54. writeTrashFile("b", "first line\n");
  55. git.add().addFilepattern("b").call();
  56. git.commit().setMessage("fixed b").call();
  57. git.revert().include(fixingA).call();
  58. assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  59. assertTrue(new File(db.getWorkTree(), "b").exists());
  60. checkFile(new File(db.getWorkTree(), "a"),
  61. "first line\nsec. line\nthird line\nfourth line\n");
  62. Iterator<RevCommit> history = git.log().call().iterator();
  63. RevCommit revertCommit = history.next();
  64. String expectedMessage = "Revert \"fixed a\"\n\n"
  65. + "This reverts commit " + fixingA.getId().getName() + ".\n";
  66. assertEquals(expectedMessage, revertCommit.getFullMessage());
  67. assertEquals("fixed b", history.next().getFullMessage());
  68. assertEquals("fixed a", history.next().getFullMessage());
  69. assertEquals("enlarged a", history.next().getFullMessage());
  70. assertEquals("create b", history.next().getFullMessage());
  71. assertEquals("create a", history.next().getFullMessage());
  72. assertFalse(history.hasNext());
  73. ReflogReader reader = db.getReflogReader(Constants.HEAD);
  74. assertTrue(reader.getLastEntry().getComment()
  75. .startsWith("revert: Revert \""));
  76. reader = db.getReflogReader(db.getBranch());
  77. assertTrue(reader.getLastEntry().getComment()
  78. .startsWith("revert: Revert \""));
  79. }
  80. }
  81. @Test
  82. public void testRevertMultiple() throws IOException, JGitInternalException,
  83. GitAPIException {
  84. try (Git git = new Git(db)) {
  85. writeTrashFile("a", "first\n");
  86. git.add().addFilepattern("a").call();
  87. git.commit().setMessage("add first").call();
  88. writeTrashFile("a", "first\nsecond\n");
  89. git.add().addFilepattern("a").call();
  90. RevCommit secondCommit = git.commit().setMessage("add second").call();
  91. writeTrashFile("a", "first\nsecond\nthird\n");
  92. git.add().addFilepattern("a").call();
  93. RevCommit thirdCommit = git.commit().setMessage("add third").call();
  94. git.revert().include(thirdCommit).include(secondCommit).call();
  95. assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  96. checkFile(new File(db.getWorkTree(), "a"), "first\n");
  97. Iterator<RevCommit> history = git.log().call().iterator();
  98. RevCommit revertCommit = history.next();
  99. String expectedMessage = "Revert \"add second\"\n\n"
  100. + "This reverts commit "
  101. + secondCommit.getId().getName() + ".\n";
  102. assertEquals(expectedMessage, revertCommit.getFullMessage());
  103. revertCommit = history.next();
  104. expectedMessage = "Revert \"add third\"\n\n"
  105. + "This reverts commit " + thirdCommit.getId().getName()
  106. + ".\n";
  107. assertEquals(expectedMessage, revertCommit.getFullMessage());
  108. assertEquals("add third", history.next().getFullMessage());
  109. assertEquals("add second", history.next().getFullMessage());
  110. assertEquals("add first", history.next().getFullMessage());
  111. assertFalse(history.hasNext());
  112. ReflogReader reader = db.getReflogReader(Constants.HEAD);
  113. assertTrue(reader.getLastEntry().getComment()
  114. .startsWith("revert: Revert \""));
  115. reader = db.getReflogReader(db.getBranch());
  116. assertTrue(reader.getLastEntry().getComment()
  117. .startsWith("revert: Revert \""));
  118. }
  119. }
  120. @Test
  121. public void testRevertMultipleWithFail() throws IOException,
  122. JGitInternalException, GitAPIException {
  123. try (Git git = new Git(db)) {
  124. writeTrashFile("a", "first\n");
  125. git.add().addFilepattern("a").call();
  126. git.commit().setMessage("add first").call();
  127. writeTrashFile("a", "first\nsecond\n");
  128. git.add().addFilepattern("a").call();
  129. RevCommit secondCommit = git.commit().setMessage("add second").call();
  130. writeTrashFile("a", "first\nsecond\nthird\n");
  131. git.add().addFilepattern("a").call();
  132. git.commit().setMessage("add third").call();
  133. writeTrashFile("a", "first\nsecond\nthird\nfourth\n");
  134. git.add().addFilepattern("a").call();
  135. RevCommit fourthCommit = git.commit().setMessage("add fourth").call();
  136. git.revert().include(fourthCommit).include(secondCommit).call();
  137. // not SAFE because it failed
  138. assertEquals(RepositoryState.REVERTING, db.getRepositoryState());
  139. checkFile(new File(db.getWorkTree(), "a"), "first\n"
  140. + "<<<<<<< master\n" + "second\n" + "third\n" + "=======\n"
  141. + ">>>>>>> " + secondCommit.getId().abbreviate(7).name()
  142. + " add second\n");
  143. Iterator<RevCommit> history = git.log().call().iterator();
  144. RevCommit revertCommit = history.next();
  145. String expectedMessage = "Revert \"add fourth\"\n\n"
  146. + "This reverts commit " + fourthCommit.getId().getName()
  147. + ".\n";
  148. assertEquals(expectedMessage, revertCommit.getFullMessage());
  149. assertEquals("add fourth", history.next().getFullMessage());
  150. assertEquals("add third", history.next().getFullMessage());
  151. assertEquals("add second", history.next().getFullMessage());
  152. assertEquals("add first", history.next().getFullMessage());
  153. assertFalse(history.hasNext());
  154. ReflogReader reader = db.getReflogReader(Constants.HEAD);
  155. assertTrue(reader.getLastEntry().getComment()
  156. .startsWith("revert: Revert \""));
  157. reader = db.getReflogReader(db.getBranch());
  158. assertTrue(reader.getLastEntry().getComment()
  159. .startsWith("revert: Revert \""));
  160. }
  161. }
  162. @Test
  163. public void testRevertDirtyIndex() throws Exception {
  164. try (Git git = new Git(db)) {
  165. RevCommit sideCommit = prepareRevert(git);
  166. // modify and add file a
  167. writeTrashFile("a", "a(modified)");
  168. git.add().addFilepattern("a").call();
  169. // do not commit
  170. doRevertAndCheckResult(git, sideCommit,
  171. MergeFailureReason.DIRTY_INDEX);
  172. }
  173. }
  174. @Test
  175. public void testRevertDirtyWorktree() throws Exception {
  176. try (Git git = new Git(db)) {
  177. RevCommit sideCommit = prepareRevert(git);
  178. // modify file a
  179. writeTrashFile("a", "a(modified)");
  180. // do not add and commit
  181. doRevertAndCheckResult(git, sideCommit,
  182. MergeFailureReason.DIRTY_WORKTREE);
  183. }
  184. }
  185. @Test
  186. public void testRevertConflictResolution() throws Exception {
  187. try (Git git = new Git(db)) {
  188. RevCommit sideCommit = prepareRevert(git);
  189. RevertCommand revert = git.revert();
  190. RevCommit newHead = revert.include(sideCommit.getId()).call();
  191. assertNull(newHead);
  192. MergeResult result = revert.getFailingResult();
  193. assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
  194. assertTrue(new File(db.getDirectory(), Constants.MERGE_MSG).exists());
  195. assertEquals("Revert \"" + sideCommit.getShortMessage()
  196. + "\"\n\nThis reverts commit " + sideCommit.getId().getName()
  197. + ".\n\nConflicts:\n\ta\n",
  198. db.readMergeCommitMsg());
  199. assertTrue(new File(db.getDirectory(), Constants.REVERT_HEAD)
  200. .exists());
  201. assertEquals(sideCommit.getId(), db.readRevertHead());
  202. assertEquals(RepositoryState.REVERTING, db.getRepositoryState());
  203. // Resolve
  204. writeTrashFile("a", "a");
  205. git.add().addFilepattern("a").call();
  206. assertEquals(RepositoryState.REVERTING_RESOLVED,
  207. db.getRepositoryState());
  208. git.commit().setOnly("a").setMessage("resolve").call();
  209. assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  210. }
  211. }
  212. @Test
  213. public void testRevertConflictReset() throws Exception {
  214. try (Git git = new Git(db)) {
  215. RevCommit sideCommit = prepareRevert(git);
  216. RevertCommand revert = git.revert();
  217. RevCommit newHead = revert.include(sideCommit.getId()).call();
  218. assertNull(newHead);
  219. MergeResult result = revert.getFailingResult();
  220. assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
  221. assertEquals(RepositoryState.REVERTING, db.getRepositoryState());
  222. assertTrue(new File(db.getDirectory(), Constants.REVERT_HEAD)
  223. .exists());
  224. git.reset().setMode(ResetType.MIXED).setRef("HEAD").call();
  225. assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  226. assertFalse(new File(db.getDirectory(), Constants.REVERT_HEAD)
  227. .exists());
  228. }
  229. }
  230. @Test
  231. public void testRevertOverExecutableChangeOnNonExectuableFileSystem()
  232. throws Exception {
  233. try (Git git = new Git(db)) {
  234. File file = writeTrashFile("test.txt", "a");
  235. assertNotNull(git.add().addFilepattern("test.txt").call());
  236. assertNotNull(git.commit().setMessage("commit1").call());
  237. assertNotNull(git.checkout().setCreateBranch(true).setName("a").call());
  238. writeTrashFile("test.txt", "b");
  239. assertNotNull(git.add().addFilepattern("test.txt").call());
  240. RevCommit commit2 = git.commit().setMessage("commit2").call();
  241. assertNotNull(commit2);
  242. assertNotNull(git.checkout().setName(Constants.MASTER).call());
  243. DirCache cache = db.lockDirCache();
  244. cache.getEntry("test.txt").setFileMode(FileMode.EXECUTABLE_FILE);
  245. cache.write();
  246. assertTrue(cache.commit());
  247. cache.unlock();
  248. assertNotNull(git.commit().setMessage("commit3").call());
  249. db.getFS().setExecute(file, false);
  250. git.getRepository()
  251. .getConfig()
  252. .setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
  253. ConfigConstants.CONFIG_KEY_FILEMODE, false);
  254. RevertCommand revert = git.revert();
  255. RevCommit newHead = revert.include(commit2).call();
  256. assertNotNull(newHead);
  257. }
  258. }
  259. @Test
  260. public void testRevertConflictMarkers() throws Exception {
  261. try (Git git = new Git(db)) {
  262. RevCommit sideCommit = prepareRevert(git);
  263. RevertCommand revert = git.revert();
  264. RevCommit newHead = revert.include(sideCommit.getId())
  265. .call();
  266. assertNull(newHead);
  267. MergeResult result = revert.getFailingResult();
  268. assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
  269. String expected = "<<<<<<< master\na(latest)\n=======\na\n>>>>>>> ca96c31 second master\n";
  270. checkFile(new File(db.getWorkTree(), "a"), expected);
  271. }
  272. }
  273. @Test
  274. public void testRevertOurCommitName() throws Exception {
  275. try (Git git = new Git(db)) {
  276. RevCommit sideCommit = prepareRevert(git);
  277. RevertCommand revert = git.revert();
  278. RevCommit newHead = revert.include(sideCommit.getId())
  279. .setOurCommitName("custom name").call();
  280. assertNull(newHead);
  281. MergeResult result = revert.getFailingResult();
  282. assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
  283. String expected = "<<<<<<< custom name\na(latest)\n=======\na\n>>>>>>> ca96c31 second master\n";
  284. checkFile(new File(db.getWorkTree(), "a"), expected);
  285. }
  286. }
  287. private RevCommit prepareRevert(Git git) throws Exception {
  288. // create, add and commit file a
  289. writeTrashFile("a", "a");
  290. git.add().addFilepattern("a").call();
  291. git.commit().setMessage("first master").call();
  292. // First commit
  293. checkoutBranch("refs/heads/master");
  294. // modify, add and commit file a
  295. writeTrashFile("a", "a(previous)");
  296. git.add().addFilepattern("a").call();
  297. RevCommit oldCommit = git.commit().setMessage("second master").call();
  298. // modify, add and commit file a
  299. writeTrashFile("a", "a(latest)");
  300. git.add().addFilepattern("a").call();
  301. git.commit().setMessage("side").call();
  302. return oldCommit;
  303. }
  304. private void doRevertAndCheckResult(final Git git,
  305. final RevCommit sideCommit, final MergeFailureReason reason)
  306. throws Exception {
  307. // get current index state
  308. String indexState = indexState(CONTENT);
  309. // revert
  310. RevertCommand revert = git.revert();
  311. RevCommit resultCommit = revert.include(sideCommit.getId()).call();
  312. assertNull(resultCommit);
  313. MergeResult result = revert.getFailingResult();
  314. assertEquals(MergeStatus.FAILED, result.getMergeStatus());
  315. // staged file a causes DIRTY_INDEX
  316. assertEquals(1, result.getFailingPaths().size());
  317. assertEquals(reason, result.getFailingPaths().get("a"));
  318. assertEquals("a(modified)", read(new File(db.getWorkTree(), "a")));
  319. // index shall be unchanged
  320. assertEquals(indexState, indexState(CONTENT));
  321. assertEquals(RepositoryState.SAFE, db.getRepositoryState());
  322. if (reason == null) {
  323. ReflogReader reader = db.getReflogReader(Constants.HEAD);
  324. assertTrue(reader.getLastEntry().getComment()
  325. .startsWith("revert: "));
  326. reader = db.getReflogReader(db.getBranch());
  327. assertTrue(reader.getLastEntry().getComment()
  328. .startsWith("revert: "));
  329. }
  330. }
  331. }