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