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.assertTrue;
49 import static org.junit.Assert.fail;
50
51 import java.io.File;
52 import java.io.IOException;
53 import java.util.Iterator;
54
55 import org.eclipse.jgit.api.CherryPickResult.CherryPickStatus;
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.api.errors.MultipleParentsNotAllowedException;
60 import org.eclipse.jgit.dircache.DirCache;
61 import org.eclipse.jgit.junit.RepositoryTestCase;
62 import org.eclipse.jgit.lib.ConfigConstants;
63 import org.eclipse.jgit.lib.Constants;
64 import org.eclipse.jgit.lib.FileMode;
65 import org.eclipse.jgit.lib.ObjectId;
66 import org.eclipse.jgit.lib.ReflogReader;
67 import org.eclipse.jgit.lib.RepositoryState;
68 import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason;
69 import org.eclipse.jgit.revwalk.RevCommit;
70 import org.junit.Test;
71
72
73
74
75 public class CherryPickCommandTest extends RepositoryTestCase {
76 @Test
77 public void testCherryPick() throws IOException, JGitInternalException,
78 GitAPIException {
79 doTestCherryPick(false);
80 }
81
82 @Test
83 public void testCherryPickNoCommit() throws IOException,
84 JGitInternalException, GitAPIException {
85 doTestCherryPick(true);
86 }
87
88 private void doTestCherryPick(boolean noCommit) throws IOException,
89 JGitInternalException,
90 GitAPIException {
91 Git git = new Git(db);
92
93 writeTrashFile("a", "first line\nsec. line\nthird line\n");
94 git.add().addFilepattern("a").call();
95 RevCommit firstCommit = git.commit().setMessage("create a").call();
96
97 writeTrashFile("b", "content\n");
98 git.add().addFilepattern("b").call();
99 git.commit().setMessage("create b").call();
100
101 writeTrashFile("a", "first line\nsec. line\nthird line\nfourth line\n");
102 git.add().addFilepattern("a").call();
103 git.commit().setMessage("enlarged a").call();
104
105 writeTrashFile("a",
106 "first line\nsecond line\nthird line\nfourth line\n");
107 git.add().addFilepattern("a").call();
108 RevCommit fixingA = git.commit().setMessage("fixed a").call();
109
110 git.branchCreate().setName("side").setStartPoint(firstCommit).call();
111 checkoutBranch("refs/heads/side");
112
113 writeTrashFile("a", "first line\nsec. line\nthird line\nfeature++\n");
114 git.add().addFilepattern("a").call();
115 git.commit().setMessage("enhanced a").call();
116
117 CherryPickResult pickResult = git.cherryPick().include(fixingA)
118 .setNoCommit(noCommit).call();
119
120 assertEquals(CherryPickStatus.OK, pickResult.getStatus());
121 assertFalse(new File(db.getWorkTree(), "b").exists());
122 checkFile(new File(db.getWorkTree(), "a"),
123 "first line\nsecond line\nthird line\nfeature++\n");
124 Iterator<RevCommit> history = git.log().call().iterator();
125 if (!noCommit)
126 assertEquals("fixed a", history.next().getFullMessage());
127 assertEquals("enhanced a", history.next().getFullMessage());
128 assertEquals("create a", history.next().getFullMessage());
129 assertFalse(history.hasNext());
130 }
131
132 @Test
133 public void testSequentialCherryPick() throws IOException, JGitInternalException,
134 GitAPIException {
135 Git git = new Git(db);
136
137 writeTrashFile("a", "first line\nsec. line\nthird line\n");
138 git.add().addFilepattern("a").call();
139 RevCommit firstCommit = git.commit().setMessage("create a").call();
140
141 writeTrashFile("a", "first line\nsec. line\nthird line\nfourth line\n");
142 git.add().addFilepattern("a").call();
143 RevCommit enlargingA = git.commit().setMessage("enlarged a").call();
144
145 writeTrashFile("a",
146 "first line\nsecond line\nthird line\nfourth line\n");
147 git.add().addFilepattern("a").call();
148 RevCommit fixingA = git.commit().setMessage("fixed a").call();
149
150 git.branchCreate().setName("side").setStartPoint(firstCommit).call();
151 checkoutBranch("refs/heads/side");
152
153 writeTrashFile("b", "nothing to do with a");
154 git.add().addFilepattern("b").call();
155 git.commit().setMessage("create b").call();
156
157 CherryPickResult result = git.cherryPick().include(enlargingA).include(fixingA).call();
158 assertEquals(CherryPickResult.CherryPickStatus.OK, result.getStatus());
159
160 Iterator<RevCommit> history = git.log().call().iterator();
161 assertEquals("fixed a", history.next().getFullMessage());
162 assertEquals("enlarged a", history.next().getFullMessage());
163 assertEquals("create b", history.next().getFullMessage());
164 assertEquals("create a", history.next().getFullMessage());
165 assertFalse(history.hasNext());
166 }
167
168 @Test
169 public void testCherryPickDirtyIndex() throws Exception {
170 Git git = new Git(db);
171 RevCommit sideCommit = prepareCherryPick(git);
172
173
174 writeTrashFile("a", "a(modified)");
175 git.add().addFilepattern("a").call();
176
177
178 doCherryPickAndCheckResult(git, sideCommit,
179 MergeFailureReason.DIRTY_INDEX);
180 }
181
182 @Test
183 public void testCherryPickDirtyWorktree() throws Exception {
184 Git git = new Git(db);
185 RevCommit sideCommit = prepareCherryPick(git);
186
187
188 writeTrashFile("a", "a(modified)");
189
190
191 doCherryPickAndCheckResult(git, sideCommit,
192 MergeFailureReason.DIRTY_WORKTREE);
193 }
194
195 @Test
196 public void testCherryPickConflictResolution() throws Exception {
197 Git git = new Git(db);
198 RevCommit sideCommit = prepareCherryPick(git);
199
200 CherryPickResult result = git.cherryPick().include(sideCommit.getId())
201 .call();
202
203 assertEquals(CherryPickStatus.CONFLICTING, result.getStatus());
204 assertTrue(new File(db.getDirectory(), Constants.MERGE_MSG).exists());
205 assertEquals("side\n\nConflicts:\n\ta\n", db.readMergeCommitMsg());
206 assertTrue(new File(db.getDirectory(), Constants.CHERRY_PICK_HEAD)
207 .exists());
208 assertEquals(sideCommit.getId(), db.readCherryPickHead());
209 assertEquals(RepositoryState.CHERRY_PICKING, db.getRepositoryState());
210
211
212 writeTrashFile("a", "a");
213 git.add().addFilepattern("a").call();
214
215 assertEquals(RepositoryState.CHERRY_PICKING_RESOLVED,
216 db.getRepositoryState());
217
218 git.commit().setOnly("a").setMessage("resolve").call();
219
220 assertEquals(RepositoryState.SAFE, db.getRepositoryState());
221 }
222
223 @Test
224 public void testCherryPickConflictResolutionNoCOmmit() throws Exception {
225 Git git = new Git(db);
226 RevCommit sideCommit = prepareCherryPick(git);
227
228 CherryPickResult result = git.cherryPick().include(sideCommit.getId())
229 .setNoCommit(true).call();
230
231 assertEquals(CherryPickStatus.CONFLICTING, result.getStatus());
232 assertTrue(db.readDirCache().hasUnmergedPaths());
233 String expected = "<<<<<<< master\na(master)\n=======\na(side)\n>>>>>>> 527460a side\n";
234 assertEquals(expected, read("a"));
235 assertTrue(new File(db.getDirectory(), Constants.MERGE_MSG).exists());
236 assertEquals("side\n\nConflicts:\n\ta\n", db.readMergeCommitMsg());
237 assertFalse(new File(db.getDirectory(), Constants.CHERRY_PICK_HEAD)
238 .exists());
239 assertEquals(RepositoryState.SAFE, db.getRepositoryState());
240
241
242 writeTrashFile("a", "a");
243 git.add().addFilepattern("a").call();
244
245 assertEquals(RepositoryState.SAFE, db.getRepositoryState());
246
247 git.commit().setOnly("a").setMessage("resolve").call();
248
249 assertEquals(RepositoryState.SAFE, db.getRepositoryState());
250 }
251
252 @Test
253 public void testCherryPickConflictReset() throws Exception {
254 Git git = new Git(db);
255
256 RevCommit sideCommit = prepareCherryPick(git);
257
258 CherryPickResult result = git.cherryPick().include(sideCommit.getId())
259 .call();
260
261 assertEquals(CherryPickStatus.CONFLICTING, result.getStatus());
262 assertEquals(RepositoryState.CHERRY_PICKING, db.getRepositoryState());
263 assertTrue(new File(db.getDirectory(), Constants.CHERRY_PICK_HEAD)
264 .exists());
265
266 git.reset().setMode(ResetType.MIXED).setRef("HEAD").call();
267
268 assertEquals(RepositoryState.SAFE, db.getRepositoryState());
269 assertFalse(new File(db.getDirectory(), Constants.CHERRY_PICK_HEAD)
270 .exists());
271 }
272
273 @Test
274 public void testCherryPickOverExecutableChangeOnNonExectuableFileSystem()
275 throws Exception {
276 Git git = new Git(db);
277 File file = writeTrashFile("test.txt", "a");
278 assertNotNull(git.add().addFilepattern("test.txt").call());
279 assertNotNull(git.commit().setMessage("commit1").call());
280
281 assertNotNull(git.checkout().setCreateBranch(true).setName("a").call());
282
283 writeTrashFile("test.txt", "b");
284 assertNotNull(git.add().addFilepattern("test.txt").call());
285 RevCommit commit2 = git.commit().setMessage("commit2").call();
286 assertNotNull(commit2);
287
288 assertNotNull(git.checkout().setName(Constants.MASTER).call());
289
290 DirCache cache = db.lockDirCache();
291 cache.getEntry("test.txt").setFileMode(FileMode.EXECUTABLE_FILE);
292 cache.write();
293 assertTrue(cache.commit());
294 cache.unlock();
295
296 assertNotNull(git.commit().setMessage("commit3").call());
297
298 db.getFS().setExecute(file, false);
299 git.getRepository()
300 .getConfig()
301 .setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
302 ConfigConstants.CONFIG_KEY_FILEMODE, false);
303
304 CherryPickResult result = git.cherryPick().include(commit2).call();
305 assertNotNull(result);
306 assertEquals(CherryPickStatus.OK, result.getStatus());
307 }
308
309 @Test
310 public void testCherryPickConflictMarkers() throws Exception {
311 Git git = new Git(db);
312 RevCommit sideCommit = prepareCherryPick(git);
313
314 CherryPickResult result = git.cherryPick().include(sideCommit.getId())
315 .call();
316 assertEquals(CherryPickStatus.CONFLICTING, result.getStatus());
317
318 String expected = "<<<<<<< master\na(master)\n=======\na(side)\n>>>>>>> 527460a side\n";
319 checkFile(new File(db.getWorkTree(), "a"), expected);
320 }
321
322 @Test
323 public void testCherryPickOurCommitName() throws Exception {
324 Git git = new Git(db);
325 RevCommit sideCommit = prepareCherryPick(git);
326
327 CherryPickResult result = git.cherryPick().include(sideCommit.getId())
328 .setOurCommitName("custom name").call();
329 assertEquals(CherryPickStatus.CONFLICTING, result.getStatus());
330
331 String expected = "<<<<<<< custom name\na(master)\n=======\na(side)\n>>>>>>> 527460a side\n";
332 checkFile(new File(db.getWorkTree(), "a"), expected);
333 }
334
335 private RevCommit prepareCherryPick(final Git git) throws Exception {
336
337 writeTrashFile("a", "a");
338 git.add().addFilepattern("a").call();
339 RevCommit firstMasterCommit = git.commit().setMessage("first master")
340 .call();
341
342
343 createBranch(firstMasterCommit, "refs/heads/side");
344 checkoutBranch("refs/heads/side");
345
346 writeTrashFile("a", "a(side)");
347 git.add().addFilepattern("a").call();
348 RevCommit sideCommit = git.commit().setMessage("side").call();
349
350
351 checkoutBranch("refs/heads/master");
352
353 writeTrashFile("a", "a(master)");
354 git.add().addFilepattern("a").call();
355 git.commit().setMessage("second master").call();
356 return sideCommit;
357 }
358
359 private void doCherryPickAndCheckResult(final Git git,
360 final RevCommit sideCommit, final MergeFailureReason reason)
361 throws Exception {
362
363 String indexState = indexState(CONTENT);
364
365
366 CherryPickResult result = git.cherryPick().include(sideCommit.getId())
367 .call();
368 assertEquals(CherryPickStatus.FAILED, result.getStatus());
369
370 assertEquals(1, result.getFailingPaths().size());
371 assertEquals(reason, result.getFailingPaths().get("a"));
372 assertEquals("a(modified)", read(new File(db.getWorkTree(), "a")));
373
374 assertEquals(indexState, indexState(CONTENT));
375 assertEquals(RepositoryState.SAFE, db.getRepositoryState());
376
377 if (reason == null) {
378 ReflogReader reader = db.getReflogReader(Constants.HEAD);
379 assertTrue(reader.getLastEntry().getComment()
380 .startsWith("cherry-pick: "));
381 reader = db.getReflogReader(db.getBranch());
382 assertTrue(reader.getLastEntry().getComment()
383 .startsWith("cherry-pick: "));
384 }
385 }
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400 @Test
401 public void testCherryPickMerge() throws Exception {
402 Git git = new Git(db);
403
404 commitFile("file", "1\n2\n3\n", "master");
405 commitFile("file", "1\n2\n3\n", "side");
406 checkoutBranch("refs/heads/side");
407 RevCommit commitD = commitFile("file", "1\n2\n3\n4\n5\n", "side2");
408 commitFile("file", "a\n2\n3\n", "side");
409 MergeResult mergeResult = git.merge().include(commitD).call();
410 ObjectId commitM = mergeResult.getNewHead();
411 checkoutBranch("refs/heads/master");
412 RevCommit commitT = commitFile("another", "t", "master");
413
414 try {
415 git.cherryPick().include(commitM).call();
416 fail("merges should not be cherry-picked by default");
417 } catch (MultipleParentsNotAllowedException e) {
418
419 }
420 try {
421 git.cherryPick().include(commitM).setMainlineParentNumber(3).call();
422 fail("specifying a non-existent parent should fail");
423 } catch (JGitInternalException e) {
424
425 assertTrue(e.getMessage().endsWith(
426 "does not have a parent number 3."));
427 }
428
429 CherryPickResult result = git.cherryPick().include(commitM)
430 .setMainlineParentNumber(1).call();
431 assertEquals(CherryPickStatus.OK, result.getStatus());
432 checkFile(new File(db.getWorkTree(), "file"), "1\n2\n3\n4\n5\n");
433
434 git.reset().setMode(ResetType.HARD).setRef(commitT.getName()).call();
435
436 CherryPickResult result2 = git.cherryPick().include(commitM)
437 .setMainlineParentNumber(2).call();
438 assertEquals(CherryPickStatus.OK, result2.getStatus());
439 checkFile(new File(db.getWorkTree(), "file"), "a\n2\n3\n");
440 }
441 }