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