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