| /* |
| * Copyright (C) 2010, Stefan Lay <stefan.lay@sap.com> |
| * Copyright (C) 2010-2014, Christian Halstrick <christian.halstrick@sap.com> and others |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Distribution License v. 1.0 which is available at |
| * https://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| package org.eclipse.jgit.api; |
| |
| import static org.eclipse.jgit.lib.Constants.MASTER; |
| import static org.eclipse.jgit.lib.Constants.R_HEADS; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| import static org.junit.Assume.assumeTrue; |
| |
| import java.io.File; |
| import java.util.Iterator; |
| import java.util.regex.Pattern; |
| |
| import org.eclipse.jgit.api.MergeCommand.FastForwardMode; |
| import org.eclipse.jgit.api.MergeResult.MergeStatus; |
| import org.eclipse.jgit.api.errors.InvalidMergeHeadsException; |
| import org.eclipse.jgit.junit.RepositoryTestCase; |
| import org.eclipse.jgit.junit.TestRepository; |
| import org.eclipse.jgit.junit.TestRepository.BranchBuilder; |
| import org.eclipse.jgit.lib.Constants; |
| import org.eclipse.jgit.lib.Ref; |
| import org.eclipse.jgit.lib.Repository; |
| import org.eclipse.jgit.lib.RepositoryState; |
| import org.eclipse.jgit.lib.Sets; |
| import org.eclipse.jgit.merge.MergeStrategy; |
| import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason; |
| import org.eclipse.jgit.revwalk.RevCommit; |
| import org.eclipse.jgit.util.FS; |
| import org.eclipse.jgit.util.FileUtils; |
| import org.eclipse.jgit.util.GitDateFormatter; |
| import org.eclipse.jgit.util.GitDateFormatter.Format; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.experimental.theories.DataPoints; |
| import org.junit.experimental.theories.Theories; |
| import org.junit.experimental.theories.Theory; |
| import org.junit.runner.RunWith; |
| |
| @RunWith(Theories.class) |
| public class MergeCommandTest extends RepositoryTestCase { |
| |
| public static @DataPoints |
| MergeStrategy[] mergeStrategies = MergeStrategy.get(); |
| |
| private GitDateFormatter dateFormatter; |
| |
| @Override |
| @Before |
| public void setUp() throws Exception { |
| super.setUp(); |
| dateFormatter = new GitDateFormatter(Format.DEFAULT); |
| } |
| |
| @Test |
| public void testMergeInItself() throws Exception { |
| try (Git git = new Git(db)) { |
| git.commit().setMessage("initial commit").call(); |
| |
| MergeResult result = git.merge().include(db.exactRef(Constants.HEAD)).call(); |
| assertEquals(MergeResult.MergeStatus.ALREADY_UP_TO_DATE, result.getMergeStatus()); |
| } |
| // no reflog entry written by merge |
| assertEquals("commit (initial): initial commit", |
| db |
| .getReflogReader(Constants.HEAD).getLastEntry().getComment()); |
| assertEquals("commit (initial): initial commit", |
| db |
| .getReflogReader(db.getBranch()).getLastEntry().getComment()); |
| } |
| |
| @Test |
| public void testAlreadyUpToDate() throws Exception { |
| try (Git git = new Git(db)) { |
| RevCommit first = git.commit().setMessage("initial commit").call(); |
| createBranch(first, "refs/heads/branch1"); |
| |
| RevCommit second = git.commit().setMessage("second commit").call(); |
| MergeResult result = git.merge().include(db.exactRef("refs/heads/branch1")).call(); |
| assertEquals(MergeResult.MergeStatus.ALREADY_UP_TO_DATE, result.getMergeStatus()); |
| assertEquals(second, result.getNewHead()); |
| } |
| // no reflog entry written by merge |
| assertEquals("commit: second commit", db |
| .getReflogReader(Constants.HEAD).getLastEntry().getComment()); |
| assertEquals("commit: second commit", db |
| .getReflogReader(db.getBranch()).getLastEntry().getComment()); |
| } |
| |
| @Test |
| public void testFastForward() throws Exception { |
| try (Git git = new Git(db)) { |
| RevCommit first = git.commit().setMessage("initial commit").call(); |
| createBranch(first, "refs/heads/branch1"); |
| |
| RevCommit second = git.commit().setMessage("second commit").call(); |
| |
| checkoutBranch("refs/heads/branch1"); |
| |
| MergeResult result = git.merge().include(db.exactRef(R_HEADS + MASTER)).call(); |
| |
| assertEquals(MergeResult.MergeStatus.FAST_FORWARD, result.getMergeStatus()); |
| assertEquals(second, result.getNewHead()); |
| } |
| assertEquals("merge refs/heads/master: Fast-forward", |
| db.getReflogReader(Constants.HEAD).getLastEntry().getComment()); |
| assertEquals("merge refs/heads/master: Fast-forward", |
| db.getReflogReader(db.getBranch()).getLastEntry().getComment()); |
| } |
| |
| @Test |
| public void testFastForwardNoCommit() throws Exception { |
| try (Git git = new Git(db)) { |
| RevCommit first = git.commit().setMessage("initial commit").call(); |
| createBranch(first, "refs/heads/branch1"); |
| |
| RevCommit second = git.commit().setMessage("second commit").call(); |
| |
| checkoutBranch("refs/heads/branch1"); |
| |
| MergeResult result = git.merge().include(db.exactRef(R_HEADS + MASTER)) |
| .setCommit(false).call(); |
| |
| assertEquals(MergeResult.MergeStatus.FAST_FORWARD, |
| result.getMergeStatus()); |
| assertEquals(second, result.getNewHead()); |
| } |
| assertEquals("merge refs/heads/master: Fast-forward", db |
| .getReflogReader(Constants.HEAD).getLastEntry().getComment()); |
| assertEquals("merge refs/heads/master: Fast-forward", db |
| .getReflogReader(db.getBranch()).getLastEntry().getComment()); |
| } |
| |
| @Test |
| public void testFastForwardWithFiles() throws Exception { |
| try (Git git = new Git(db)) { |
| writeTrashFile("file1", "file1"); |
| git.add().addFilepattern("file1").call(); |
| RevCommit first = git.commit().setMessage("initial commit").call(); |
| |
| assertTrue(new File(db.getWorkTree(), "file1").exists()); |
| createBranch(first, "refs/heads/branch1"); |
| |
| writeTrashFile("file2", "file2"); |
| git.add().addFilepattern("file2").call(); |
| RevCommit second = git.commit().setMessage("second commit").call(); |
| assertTrue(new File(db.getWorkTree(), "file2").exists()); |
| |
| checkoutBranch("refs/heads/branch1"); |
| assertFalse(new File(db.getWorkTree(), "file2").exists()); |
| |
| MergeResult result = git.merge().include(db.exactRef(R_HEADS + MASTER)).call(); |
| |
| assertTrue(new File(db.getWorkTree(), "file1").exists()); |
| assertTrue(new File(db.getWorkTree(), "file2").exists()); |
| assertEquals(MergeResult.MergeStatus.FAST_FORWARD, result.getMergeStatus()); |
| assertEquals(second, result.getNewHead()); |
| } |
| assertEquals("merge refs/heads/master: Fast-forward", |
| db.getReflogReader(Constants.HEAD).getLastEntry().getComment()); |
| assertEquals("merge refs/heads/master: Fast-forward", |
| db.getReflogReader(db.getBranch()).getLastEntry().getComment()); |
| } |
| |
| @Test |
| public void testMultipleHeads() throws Exception { |
| try (Git git = new Git(db)) { |
| writeTrashFile("file1", "file1"); |
| git.add().addFilepattern("file1").call(); |
| RevCommit first = git.commit().setMessage("initial commit").call(); |
| createBranch(first, "refs/heads/branch1"); |
| |
| writeTrashFile("file2", "file2"); |
| git.add().addFilepattern("file2").call(); |
| RevCommit second = git.commit().setMessage("second commit").call(); |
| |
| writeTrashFile("file3", "file3"); |
| git.add().addFilepattern("file3").call(); |
| git.commit().setMessage("third commit").call(); |
| |
| checkoutBranch("refs/heads/branch1"); |
| assertFalse(new File(db.getWorkTree(), "file2").exists()); |
| assertFalse(new File(db.getWorkTree(), "file3").exists()); |
| |
| MergeCommand merge = git.merge(); |
| merge.include(second.getId()); |
| merge.include(db.exactRef(R_HEADS + MASTER)); |
| try { |
| merge.call(); |
| fail("Expected exception not thrown when merging multiple heads"); |
| } catch (InvalidMergeHeadsException e) { |
| // expected this exception |
| } |
| } |
| } |
| |
| @Theory |
| public void testMergeSuccessAllStrategies(MergeStrategy mergeStrategy) |
| throws Exception { |
| try (Git git = new Git(db)) { |
| RevCommit first = git.commit().setMessage("first").call(); |
| createBranch(first, "refs/heads/side"); |
| |
| writeTrashFile("a", "a"); |
| git.add().addFilepattern("a").call(); |
| git.commit().setMessage("second").call(); |
| |
| checkoutBranch("refs/heads/side"); |
| writeTrashFile("b", "b"); |
| git.add().addFilepattern("b").call(); |
| git.commit().setMessage("third").call(); |
| |
| MergeResult result = git.merge().setStrategy(mergeStrategy) |
| .include(db.exactRef(R_HEADS + MASTER)).call(); |
| assertEquals(MergeStatus.MERGED, result.getMergeStatus()); |
| } |
| assertEquals( |
| "merge refs/heads/master: Merge made by " |
| + mergeStrategy.getName() + ".", |
| db.getReflogReader(Constants.HEAD).getLastEntry().getComment()); |
| assertEquals( |
| "merge refs/heads/master: Merge made by " |
| + mergeStrategy.getName() + ".", |
| db.getReflogReader(db.getBranch()).getLastEntry().getComment()); |
| } |
| |
| @Theory |
| public void testMergeSuccessAllStrategiesNoCommit( |
| MergeStrategy mergeStrategy) throws Exception { |
| try (Git git = new Git(db)) { |
| RevCommit first = git.commit().setMessage("first").call(); |
| createBranch(first, "refs/heads/side"); |
| |
| writeTrashFile("a", "a"); |
| git.add().addFilepattern("a").call(); |
| git.commit().setMessage("second").call(); |
| |
| checkoutBranch("refs/heads/side"); |
| writeTrashFile("b", "b"); |
| git.add().addFilepattern("b").call(); |
| RevCommit thirdCommit = git.commit().setMessage("third").call(); |
| |
| MergeResult result = git.merge().setStrategy(mergeStrategy) |
| .setCommit(false) |
| .include(db.exactRef(R_HEADS + MASTER)).call(); |
| assertEquals(MergeStatus.MERGED_NOT_COMMITTED, result.getMergeStatus()); |
| assertEquals(db.exactRef(Constants.HEAD).getTarget().getObjectId(), |
| thirdCommit.getId()); |
| } |
| } |
| |
| @Test |
| public void testContentMerge() throws Exception { |
| try (Git git = new Git(db)) { |
| writeTrashFile("a", "1\na\n3\n"); |
| writeTrashFile("b", "1\nb\n3\n"); |
| writeTrashFile("c/c/c", "1\nc\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("b") |
| .addFilepattern("c/c/c").call(); |
| RevCommit initialCommit = git.commit().setMessage("initial").call(); |
| |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| |
| writeTrashFile("a", "1\na(side)\n3\n"); |
| writeTrashFile("b", "1\nb(side)\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("b").call(); |
| RevCommit secondCommit = git.commit().setMessage("side").call(); |
| |
| assertEquals("1\nb(side)\n3\n", read(new File(db.getWorkTree(), "b"))); |
| checkoutBranch("refs/heads/master"); |
| assertEquals("1\nb\n3\n", read(new File(db.getWorkTree(), "b"))); |
| |
| writeTrashFile("a", "1\na(main)\n3\n"); |
| writeTrashFile("c/c/c", "1\nc(main)\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("c/c/c").call(); |
| git.commit().setMessage("main").call(); |
| |
| MergeResult result = git.merge().include(secondCommit.getId()) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus()); |
| |
| assertEquals( |
| "1\n<<<<<<< HEAD\na(main)\n=======\na(side)\n>>>>>>> 86503e7e397465588cc267b65d778538bffccb83\n3\n", |
| read(new File(db.getWorkTree(), "a"))); |
| assertEquals("1\nb(side)\n3\n", read(new File(db.getWorkTree(), "b"))); |
| assertEquals("1\nc(main)\n3\n", |
| read(new File(db.getWorkTree(), "c/c/c"))); |
| |
| assertEquals(1, result.getConflicts().size()); |
| assertEquals(3, result.getConflicts().get("a")[0].length); |
| |
| assertEquals(RepositoryState.MERGING, db.getRepositoryState()); |
| } |
| } |
| |
| @Test |
| public void testMergeTag() throws Exception { |
| try (Git git = new Git(db)) { |
| writeTrashFile("a", "a"); |
| git.add().addFilepattern("a").call(); |
| RevCommit initialCommit = git.commit().setMessage("initial").call(); |
| |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| |
| writeTrashFile("b", "b"); |
| git.add().addFilepattern("b").call(); |
| RevCommit secondCommit = git.commit().setMessage("side").call(); |
| Ref tag = git.tag().setAnnotated(true).setMessage("my tag 01") |
| .setName("tag01").setObjectId(secondCommit).call(); |
| |
| checkoutBranch("refs/heads/master"); |
| |
| writeTrashFile("a", "a2"); |
| git.add().addFilepattern("a").call(); |
| git.commit().setMessage("main").call(); |
| |
| MergeResult result = git.merge().include(tag).setStrategy(MergeStrategy.RESOLVE).call(); |
| assertEquals(MergeStatus.MERGED, result.getMergeStatus()); |
| } |
| } |
| |
| @Test |
| public void testMergeMessage() throws Exception { |
| try (Git git = new Git(db)) { |
| writeTrashFile("a", "1\na\n3\n"); |
| git.add().addFilepattern("a").call(); |
| RevCommit initialCommit = git.commit().setMessage("initial").call(); |
| |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| |
| writeTrashFile("a", "1\na(side)\n3\n"); |
| git.add().addFilepattern("a").call(); |
| git.commit().setMessage("side").call(); |
| |
| checkoutBranch("refs/heads/master"); |
| |
| writeTrashFile("a", "1\na(main)\n3\n"); |
| git.add().addFilepattern("a").call(); |
| git.commit().setMessage("main").call(); |
| |
| Ref sideBranch = db.exactRef("refs/heads/side"); |
| |
| git.merge().include(sideBranch) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| |
| assertEquals("Merge branch 'side'\n\nConflicts:\n\ta\n", |
| db.readMergeCommitMsg()); |
| } |
| |
| } |
| |
| @Test |
| public void testMergeNonVersionedPaths() throws Exception { |
| try (Git git = new Git(db)) { |
| writeTrashFile("a", "1\na\n3\n"); |
| writeTrashFile("b", "1\nb\n3\n"); |
| writeTrashFile("c/c/c", "1\nc\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("b") |
| .addFilepattern("c/c/c").call(); |
| RevCommit initialCommit = git.commit().setMessage("initial").call(); |
| |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| |
| writeTrashFile("a", "1\na(side)\n3\n"); |
| writeTrashFile("b", "1\nb(side)\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("b").call(); |
| RevCommit secondCommit = git.commit().setMessage("side").call(); |
| |
| assertEquals("1\nb(side)\n3\n", read(new File(db.getWorkTree(), "b"))); |
| checkoutBranch("refs/heads/master"); |
| assertEquals("1\nb\n3\n", read(new File(db.getWorkTree(), "b"))); |
| |
| writeTrashFile("a", "1\na(main)\n3\n"); |
| writeTrashFile("c/c/c", "1\nc(main)\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("c/c/c").call(); |
| git.commit().setMessage("main").call(); |
| |
| writeTrashFile("d", "1\nd\n3\n"); |
| assertTrue(new File(db.getWorkTree(), "e").mkdir()); |
| |
| MergeResult result = git.merge().include(secondCommit.getId()) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus()); |
| |
| assertEquals( |
| "1\n<<<<<<< HEAD\na(main)\n=======\na(side)\n>>>>>>> 86503e7e397465588cc267b65d778538bffccb83\n3\n", |
| read(new File(db.getWorkTree(), "a"))); |
| assertEquals("1\nb(side)\n3\n", read(new File(db.getWorkTree(), "b"))); |
| assertEquals("1\nc(main)\n3\n", |
| read(new File(db.getWorkTree(), "c/c/c"))); |
| assertEquals("1\nd\n3\n", read(new File(db.getWorkTree(), "d"))); |
| File dir = new File(db.getWorkTree(), "e"); |
| assertTrue(dir.isDirectory()); |
| |
| assertEquals(1, result.getConflicts().size()); |
| assertEquals(3, result.getConflicts().get("a")[0].length); |
| |
| assertEquals(RepositoryState.MERGING, db.getRepositoryState()); |
| } |
| } |
| |
| @Test |
| public void testMultipleCreations() throws Exception { |
| try (Git git = new Git(db)) { |
| writeTrashFile("a", "1\na\n3\n"); |
| git.add().addFilepattern("a").call(); |
| RevCommit initialCommit = git.commit().setMessage("initial").call(); |
| |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| |
| writeTrashFile("b", "1\nb(side)\n3\n"); |
| git.add().addFilepattern("b").call(); |
| RevCommit secondCommit = git.commit().setMessage("side").call(); |
| |
| checkoutBranch("refs/heads/master"); |
| |
| writeTrashFile("b", "1\nb(main)\n3\n"); |
| git.add().addFilepattern("b").call(); |
| git.commit().setMessage("main").call(); |
| |
| MergeResult result = git.merge().include(secondCommit.getId()) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus()); |
| } |
| } |
| |
| @Test |
| public void testMultipleCreationsSameContent() throws Exception { |
| try (Git git = new Git(db)) { |
| writeTrashFile("a", "1\na\n3\n"); |
| git.add().addFilepattern("a").call(); |
| RevCommit initialCommit = git.commit().setMessage("initial").call(); |
| |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| |
| writeTrashFile("b", "1\nb(1)\n3\n"); |
| git.add().addFilepattern("b").call(); |
| RevCommit secondCommit = git.commit().setMessage("side").call(); |
| |
| checkoutBranch("refs/heads/master"); |
| |
| writeTrashFile("b", "1\nb(1)\n3\n"); |
| git.add().addFilepattern("b").call(); |
| git.commit().setMessage("main").call(); |
| |
| MergeResult result = git.merge().include(secondCommit.getId()) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| assertEquals(MergeStatus.MERGED, result.getMergeStatus()); |
| assertEquals("1\nb(1)\n3\n", read(new File(db.getWorkTree(), "b"))); |
| assertEquals("merge " + secondCommit.getId().getName() |
| + ": Merge made by resolve.", db |
| .getReflogReader(Constants.HEAD) |
| .getLastEntry().getComment()); |
| assertEquals("merge " + secondCommit.getId().getName() |
| + ": Merge made by resolve.", db |
| .getReflogReader(db.getBranch()) |
| .getLastEntry().getComment()); |
| } |
| } |
| |
| @Test |
| public void testSuccessfulContentMerge() throws Exception { |
| try (Git git = new Git(db)) { |
| writeTrashFile("a", "1\na\n3\n"); |
| writeTrashFile("b", "1\nb\n3\n"); |
| writeTrashFile("c/c/c", "1\nc\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("b") |
| .addFilepattern("c/c/c").call(); |
| RevCommit initialCommit = git.commit().setMessage("initial").call(); |
| |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| |
| writeTrashFile("a", "1(side)\na\n3\n"); |
| writeTrashFile("b", "1\nb(side)\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("b").call(); |
| RevCommit secondCommit = git.commit().setMessage("side").call(); |
| |
| assertEquals("1\nb(side)\n3\n", read(new File(db.getWorkTree(), "b"))); |
| checkoutBranch("refs/heads/master"); |
| assertEquals("1\nb\n3\n", read(new File(db.getWorkTree(), "b"))); |
| |
| writeTrashFile("a", "1\na\n3(main)\n"); |
| writeTrashFile("c/c/c", "1\nc(main)\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("c/c/c").call(); |
| RevCommit thirdCommit = git.commit().setMessage("main").call(); |
| |
| MergeResult result = git.merge().include(secondCommit.getId()) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| assertEquals(MergeStatus.MERGED, result.getMergeStatus()); |
| |
| assertEquals("1(side)\na\n3(main)\n", read(new File(db.getWorkTree(), |
| "a"))); |
| assertEquals("1\nb(side)\n3\n", read(new File(db.getWorkTree(), "b"))); |
| assertEquals("1\nc(main)\n3\n", read(new File(db.getWorkTree(), |
| "c/c/c"))); |
| |
| assertEquals(null, result.getConflicts()); |
| |
| assertEquals(2, result.getMergedCommits().length); |
| assertEquals(thirdCommit, result.getMergedCommits()[0]); |
| assertEquals(secondCommit, result.getMergedCommits()[1]); |
| |
| Iterator<RevCommit> it = git.log().call().iterator(); |
| RevCommit newHead = it.next(); |
| assertEquals(newHead, result.getNewHead()); |
| assertEquals(2, newHead.getParentCount()); |
| assertEquals(thirdCommit, newHead.getParent(0)); |
| assertEquals(secondCommit, newHead.getParent(1)); |
| assertEquals( |
| "Merge commit '3fa334456d236a92db020289fe0bf481d91777b4'", |
| newHead.getFullMessage()); |
| // @TODO fix me |
| assertEquals(RepositoryState.SAFE, db.getRepositoryState()); |
| // test index state |
| } |
| } |
| |
| @Test |
| public void testSuccessfulContentMergeNoCommit() throws Exception { |
| try (Git git = new Git(db)) { |
| writeTrashFile("a", "1\na\n3\n"); |
| writeTrashFile("b", "1\nb\n3\n"); |
| writeTrashFile("c/c/c", "1\nc\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("b") |
| .addFilepattern("c/c/c").call(); |
| RevCommit initialCommit = git.commit().setMessage("initial").call(); |
| |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| |
| writeTrashFile("a", "1(side)\na\n3\n"); |
| writeTrashFile("b", "1\nb(side)\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("b").call(); |
| RevCommit secondCommit = git.commit().setMessage("side").call(); |
| |
| assertEquals("1\nb(side)\n3\n", read(new File(db.getWorkTree(), "b"))); |
| checkoutBranch("refs/heads/master"); |
| assertEquals("1\nb\n3\n", read(new File(db.getWorkTree(), "b"))); |
| |
| writeTrashFile("a", "1\na\n3(main)\n"); |
| writeTrashFile("c/c/c", "1\nc(main)\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("c/c/c").call(); |
| RevCommit thirdCommit = git.commit().setMessage("main").call(); |
| |
| MergeResult result = git.merge().include(secondCommit.getId()) |
| .setCommit(false) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| assertEquals(MergeStatus.MERGED_NOT_COMMITTED, result.getMergeStatus()); |
| assertEquals(db.exactRef(Constants.HEAD).getTarget().getObjectId(), |
| thirdCommit.getId()); |
| |
| assertEquals("1(side)\na\n3(main)\n", read(new File(db.getWorkTree(), |
| "a"))); |
| assertEquals("1\nb(side)\n3\n", read(new File(db.getWorkTree(), "b"))); |
| assertEquals("1\nc(main)\n3\n", |
| read(new File(db.getWorkTree(), "c/c/c"))); |
| |
| assertEquals(null, result.getConflicts()); |
| |
| assertEquals(2, result.getMergedCommits().length); |
| assertEquals(thirdCommit, result.getMergedCommits()[0]); |
| assertEquals(secondCommit, result.getMergedCommits()[1]); |
| assertNull(result.getNewHead()); |
| assertEquals(RepositoryState.MERGING_RESOLVED, db.getRepositoryState()); |
| } |
| } |
| |
| @Test |
| public void testSuccessfulContentMergeAndDirtyworkingTree() |
| throws Exception { |
| try (Git git = new Git(db)) { |
| writeTrashFile("a", "1\na\n3\n"); |
| writeTrashFile("b", "1\nb\n3\n"); |
| writeTrashFile("d", "1\nd\n3\n"); |
| writeTrashFile("c/c/c", "1\nc\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("b") |
| .addFilepattern("c/c/c").addFilepattern("d").call(); |
| RevCommit initialCommit = git.commit().setMessage("initial").call(); |
| |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| |
| writeTrashFile("a", "1(side)\na\n3\n"); |
| writeTrashFile("b", "1\nb(side)\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("b").call(); |
| RevCommit secondCommit = git.commit().setMessage("side").call(); |
| |
| assertEquals("1\nb(side)\n3\n", read(new File(db.getWorkTree(), "b"))); |
| checkoutBranch("refs/heads/master"); |
| assertEquals("1\nb\n3\n", read(new File(db.getWorkTree(), "b"))); |
| |
| writeTrashFile("a", "1\na\n3(main)\n"); |
| writeTrashFile("c/c/c", "1\nc(main)\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("c/c/c").call(); |
| RevCommit thirdCommit = git.commit().setMessage("main").call(); |
| |
| writeTrashFile("d", "--- dirty ---"); |
| MergeResult result = git.merge().include(secondCommit.getId()) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| assertEquals(MergeStatus.MERGED, result.getMergeStatus()); |
| |
| assertEquals("1(side)\na\n3(main)\n", read(new File(db.getWorkTree(), |
| "a"))); |
| assertEquals("1\nb(side)\n3\n", read(new File(db.getWorkTree(), "b"))); |
| assertEquals("1\nc(main)\n3\n", read(new File(db.getWorkTree(), |
| "c/c/c"))); |
| assertEquals("--- dirty ---", read(new File(db.getWorkTree(), "d"))); |
| |
| assertEquals(null, result.getConflicts()); |
| |
| assertEquals(2, result.getMergedCommits().length); |
| assertEquals(thirdCommit, result.getMergedCommits()[0]); |
| assertEquals(secondCommit, result.getMergedCommits()[1]); |
| |
| Iterator<RevCommit> it = git.log().call().iterator(); |
| RevCommit newHead = it.next(); |
| assertEquals(newHead, result.getNewHead()); |
| assertEquals(2, newHead.getParentCount()); |
| assertEquals(thirdCommit, newHead.getParent(0)); |
| assertEquals(secondCommit, newHead.getParent(1)); |
| assertEquals( |
| "Merge commit '064d54d98a4cdb0fed1802a21c656bfda67fe879'", |
| newHead.getFullMessage()); |
| |
| assertEquals(RepositoryState.SAFE, db.getRepositoryState()); |
| } |
| } |
| |
| @Test |
| public void testSingleDeletion() throws Exception { |
| try (Git git = new Git(db)) { |
| writeTrashFile("a", "1\na\n3\n"); |
| writeTrashFile("b", "1\nb\n3\n"); |
| writeTrashFile("d", "1\nd\n3\n"); |
| writeTrashFile("c/c/c", "1\nc\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("b") |
| .addFilepattern("c/c/c").addFilepattern("d").call(); |
| RevCommit initialCommit = git.commit().setMessage("initial").call(); |
| |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| |
| assertTrue(new File(db.getWorkTree(), "b").delete()); |
| git.add().addFilepattern("b").setUpdate(true).call(); |
| RevCommit secondCommit = git.commit().setMessage("side").call(); |
| |
| assertFalse(new File(db.getWorkTree(), "b").exists()); |
| checkoutBranch("refs/heads/master"); |
| assertTrue(new File(db.getWorkTree(), "b").exists()); |
| |
| writeTrashFile("a", "1\na\n3(main)\n"); |
| writeTrashFile("c/c/c", "1\nc(main)\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("c/c/c").call(); |
| RevCommit thirdCommit = git.commit().setMessage("main").call(); |
| |
| // We are merging a deletion into our branch |
| MergeResult result = git.merge().include(secondCommit.getId()) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| assertEquals(MergeStatus.MERGED, result.getMergeStatus()); |
| |
| assertEquals("1\na\n3(main)\n", read(new File(db.getWorkTree(), "a"))); |
| assertFalse(new File(db.getWorkTree(), "b").exists()); |
| assertEquals("1\nc(main)\n3\n", |
| read(new File(db.getWorkTree(), "c/c/c"))); |
| assertEquals("1\nd\n3\n", read(new File(db.getWorkTree(), "d"))); |
| |
| // Do the opposite, be on a branch where we have deleted a file and |
| // merge in a old commit where this file was not deleted |
| checkoutBranch("refs/heads/side"); |
| assertFalse(new File(db.getWorkTree(), "b").exists()); |
| |
| result = git.merge().include(thirdCommit.getId()) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| assertEquals(MergeStatus.MERGED, result.getMergeStatus()); |
| |
| assertEquals("1\na\n3(main)\n", read(new File(db.getWorkTree(), "a"))); |
| assertFalse(new File(db.getWorkTree(), "b").exists()); |
| assertEquals("1\nc(main)\n3\n", |
| read(new File(db.getWorkTree(), "c/c/c"))); |
| assertEquals("1\nd\n3\n", read(new File(db.getWorkTree(), "d"))); |
| } |
| } |
| |
| @Test |
| public void testMultipleDeletions() throws Exception { |
| try (Git git = new Git(db)) { |
| writeTrashFile("a", "1\na\n3\n"); |
| git.add().addFilepattern("a").call(); |
| RevCommit initialCommit = git.commit().setMessage("initial").call(); |
| |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| |
| assertTrue(new File(db.getWorkTree(), "a").delete()); |
| git.add().addFilepattern("a").setUpdate(true).call(); |
| RevCommit secondCommit = git.commit().setMessage("side").call(); |
| |
| assertFalse(new File(db.getWorkTree(), "a").exists()); |
| checkoutBranch("refs/heads/master"); |
| assertTrue(new File(db.getWorkTree(), "a").exists()); |
| |
| assertTrue(new File(db.getWorkTree(), "a").delete()); |
| git.add().addFilepattern("a").setUpdate(true).call(); |
| git.commit().setMessage("main").call(); |
| |
| // We are merging a deletion into our branch |
| MergeResult result = git.merge().include(secondCommit.getId()) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| assertEquals(MergeStatus.MERGED, result.getMergeStatus()); |
| } |
| } |
| |
| @Test |
| public void testDeletionAndConflict() throws Exception { |
| try (Git git = new Git(db)) { |
| writeTrashFile("a", "1\na\n3\n"); |
| writeTrashFile("b", "1\nb\n3\n"); |
| writeTrashFile("d", "1\nd\n3\n"); |
| writeTrashFile("c/c/c", "1\nc\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("b") |
| .addFilepattern("c/c/c").addFilepattern("d").call(); |
| RevCommit initialCommit = git.commit().setMessage("initial").call(); |
| |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| |
| assertTrue(new File(db.getWorkTree(), "b").delete()); |
| writeTrashFile("a", "1\na\n3(side)\n"); |
| git.add().addFilepattern("b").setUpdate(true).call(); |
| git.add().addFilepattern("a").setUpdate(true).call(); |
| RevCommit secondCommit = git.commit().setMessage("side").call(); |
| |
| assertFalse(new File(db.getWorkTree(), "b").exists()); |
| checkoutBranch("refs/heads/master"); |
| assertTrue(new File(db.getWorkTree(), "b").exists()); |
| |
| writeTrashFile("a", "1\na\n3(main)\n"); |
| writeTrashFile("c/c/c", "1\nc(main)\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("c/c/c").call(); |
| git.commit().setMessage("main").call(); |
| |
| // We are merging a deletion into our branch |
| MergeResult result = git.merge().include(secondCommit.getId()) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus()); |
| |
| assertEquals( |
| "1\na\n<<<<<<< HEAD\n3(main)\n=======\n3(side)\n>>>>>>> 54ffed45d62d252715fc20e41da92d44c48fb0ff\n", |
| read(new File(db.getWorkTree(), "a"))); |
| assertFalse(new File(db.getWorkTree(), "b").exists()); |
| assertEquals("1\nc(main)\n3\n", |
| read(new File(db.getWorkTree(), "c/c/c"))); |
| assertEquals("1\nd\n3\n", read(new File(db.getWorkTree(), "d"))); |
| } |
| } |
| |
| @Test |
| public void testDeletionOnMasterConflict() throws Exception { |
| try (Git git = new Git(db)) { |
| writeTrashFile("a", "1\na\n3\n"); |
| writeTrashFile("b", "1\nb\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("b").call(); |
| RevCommit initialCommit = git.commit().setMessage("initial").call(); |
| |
| // create side branch and modify "a" |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| writeTrashFile("a", "1\na(side)\n3\n"); |
| git.add().addFilepattern("a").call(); |
| RevCommit secondCommit = git.commit().setMessage("side").call(); |
| |
| // delete a on master to generate conflict |
| checkoutBranch("refs/heads/master"); |
| git.rm().addFilepattern("a").call(); |
| git.commit().setMessage("main").call(); |
| |
| // merge side with master |
| MergeResult result = git.merge().include(secondCommit.getId()) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus()); |
| |
| // result should be 'a' conflicting with workspace content from side |
| assertTrue(new File(db.getWorkTree(), "a").exists()); |
| assertEquals("1\na(side)\n3\n", read(new File(db.getWorkTree(), "a"))); |
| assertEquals("1\nb\n3\n", read(new File(db.getWorkTree(), "b"))); |
| } |
| } |
| |
| @Test |
| public void testDeletionOnSideConflict() throws Exception { |
| try (Git git = new Git(db)) { |
| writeTrashFile("a", "1\na\n3\n"); |
| writeTrashFile("b", "1\nb\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("b").call(); |
| RevCommit initialCommit = git.commit().setMessage("initial").call(); |
| |
| // create side branch and delete "a" |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| git.rm().addFilepattern("a").call(); |
| RevCommit secondCommit = git.commit().setMessage("side").call(); |
| |
| // update a on master to generate conflict |
| checkoutBranch("refs/heads/master"); |
| writeTrashFile("a", "1\na(main)\n3\n"); |
| git.add().addFilepattern("a").call(); |
| git.commit().setMessage("main").call(); |
| |
| // merge side with master |
| MergeResult result = git.merge().include(secondCommit.getId()) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus()); |
| |
| assertTrue(new File(db.getWorkTree(), "a").exists()); |
| assertEquals("1\na(main)\n3\n", read(new File(db.getWorkTree(), "a"))); |
| assertEquals("1\nb\n3\n", read(new File(db.getWorkTree(), "b"))); |
| |
| assertEquals(1, result.getConflicts().size()); |
| assertEquals(3, result.getConflicts().get("a")[0].length); |
| } |
| } |
| |
| @Test |
| public void testModifiedAndRenamed() throws Exception { |
| // this test is essentially the same as testDeletionOnSideConflict, |
| // however if once rename support is added this test should result in a |
| // successful merge instead of a conflict |
| try (Git git = new Git(db)) { |
| writeTrashFile("x", "add x"); |
| git.add().addFilepattern("x").call(); |
| RevCommit initial = git.commit().setMessage("add x").call(); |
| |
| createBranch(initial, "refs/heads/d1"); |
| createBranch(initial, "refs/heads/d2"); |
| |
| // rename x to y on d1 |
| checkoutBranch("refs/heads/d1"); |
| new File(db.getWorkTree(), "x") |
| .renameTo(new File(db.getWorkTree(), "y")); |
| git.rm().addFilepattern("x").call(); |
| git.add().addFilepattern("y").call(); |
| RevCommit d1Commit = git.commit().setMessage("d1 rename x -> y").call(); |
| |
| checkoutBranch("refs/heads/d2"); |
| writeTrashFile("x", "d2 change"); |
| git.add().addFilepattern("x").call(); |
| RevCommit d2Commit = git.commit().setMessage("d2 change in x").call(); |
| |
| checkoutBranch("refs/heads/master"); |
| MergeResult d1Merge = git.merge().include(d1Commit).call(); |
| assertEquals(MergeResult.MergeStatus.FAST_FORWARD, |
| d1Merge.getMergeStatus()); |
| |
| MergeResult d2Merge = git.merge().include(d2Commit).call(); |
| assertEquals(MergeResult.MergeStatus.CONFLICTING, |
| d2Merge.getMergeStatus()); |
| assertEquals(1, d2Merge.getConflicts().size()); |
| assertEquals(3, d2Merge.getConflicts().get("x")[0].length); |
| } |
| } |
| |
| @Test |
| public void testMergeFailingWithDirtyWorkingTree() throws Exception { |
| try (Git git = new Git(db)) { |
| writeTrashFile("a", "1\na\n3\n"); |
| writeTrashFile("b", "1\nb\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("b").call(); |
| RevCommit initialCommit = git.commit().setMessage("initial").call(); |
| |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| |
| writeTrashFile("a", "1(side)\na\n3\n"); |
| writeTrashFile("b", "1\nb(side)\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("b").call(); |
| RevCommit secondCommit = git.commit().setMessage("side").call(); |
| |
| assertEquals("1\nb(side)\n3\n", read(new File(db.getWorkTree(), "b"))); |
| checkoutBranch("refs/heads/master"); |
| assertEquals("1\nb\n3\n", read(new File(db.getWorkTree(), "b"))); |
| |
| writeTrashFile("a", "1\na\n3(main)\n"); |
| git.add().addFilepattern("a").call(); |
| git.commit().setMessage("main").call(); |
| |
| writeTrashFile("a", "--- dirty ---"); |
| MergeResult result = git.merge().include(secondCommit.getId()) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| |
| assertEquals(MergeStatus.FAILED, result.getMergeStatus()); |
| |
| assertEquals("--- dirty ---", read(new File(db.getWorkTree(), "a"))); |
| assertEquals("1\nb\n3\n", read(new File(db.getWorkTree(), "b"))); |
| |
| assertEquals(null, result.getConflicts()); |
| |
| assertEquals(RepositoryState.SAFE, db.getRepositoryState()); |
| } |
| } |
| |
| @Test |
| public void testMergeConflictFileFolder() throws Exception { |
| try (Git git = new Git(db)) { |
| writeTrashFile("a", "1\na\n3\n"); |
| writeTrashFile("b", "1\nb\n3\n"); |
| git.add().addFilepattern("a").addFilepattern("b").call(); |
| RevCommit initialCommit = git.commit().setMessage("initial").call(); |
| |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| |
| writeTrashFile("c/c/c", "1\nc(side)\n3\n"); |
| writeTrashFile("d", "1\nd(side)\n3\n"); |
| git.add().addFilepattern("c/c/c").addFilepattern("d").call(); |
| RevCommit secondCommit = git.commit().setMessage("side").call(); |
| |
| checkoutBranch("refs/heads/master"); |
| |
| writeTrashFile("c", "1\nc(main)\n3\n"); |
| writeTrashFile("d/d/d", "1\nd(main)\n3\n"); |
| git.add().addFilepattern("c").addFilepattern("d/d/d").call(); |
| git.commit().setMessage("main").call(); |
| |
| MergeResult result = git.merge().include(secondCommit.getId()) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| |
| assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus()); |
| |
| assertEquals("1\na\n3\n", read(new File(db.getWorkTree(), "a"))); |
| assertEquals("1\nb\n3\n", read(new File(db.getWorkTree(), "b"))); |
| assertEquals("1\nc(main)\n3\n", read(new File(db.getWorkTree(), "c"))); |
| assertEquals("1\nd(main)\n3\n", read(new File(db.getWorkTree(), "d/d/d"))); |
| |
| assertEquals(null, result.getConflicts()); |
| |
| assertEquals(RepositoryState.MERGING, db.getRepositoryState()); |
| } |
| } |
| |
| @Test |
| public void testSuccessfulMergeFailsDueToDirtyIndex() throws Exception { |
| try (Git git = new Git(db)) { |
| File fileA = writeTrashFile("a", "a"); |
| RevCommit initialCommit = addAllAndCommit(git); |
| |
| // switch branch |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| // modify file a |
| write(fileA, "a(side)"); |
| writeTrashFile("b", "b"); |
| RevCommit sideCommit = addAllAndCommit(git); |
| |
| // switch branch |
| checkoutBranch("refs/heads/master"); |
| writeTrashFile("c", "c"); |
| addAllAndCommit(git); |
| |
| // modify and add file a |
| write(fileA, "a(modified)"); |
| git.add().addFilepattern("a").call(); |
| // do not commit |
| |
| // get current index state |
| String indexState = indexState(CONTENT); |
| |
| // merge |
| MergeResult result = git.merge().include(sideCommit.getId()) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| |
| checkMergeFailedResult(result, MergeFailureReason.DIRTY_INDEX, |
| indexState, fileA); |
| } |
| } |
| |
| @Test |
| public void testConflictingMergeFailsDueToDirtyIndex() throws Exception { |
| try (Git git = new Git(db)) { |
| File fileA = writeTrashFile("a", "a"); |
| RevCommit initialCommit = addAllAndCommit(git); |
| |
| // switch branch |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| // modify file a |
| write(fileA, "a(side)"); |
| writeTrashFile("b", "b"); |
| RevCommit sideCommit = addAllAndCommit(git); |
| |
| // switch branch |
| checkoutBranch("refs/heads/master"); |
| // modify file a - this will cause a conflict during merge |
| write(fileA, "a(master)"); |
| writeTrashFile("c", "c"); |
| addAllAndCommit(git); |
| |
| // modify and add file a |
| write(fileA, "a(modified)"); |
| git.add().addFilepattern("a").call(); |
| // do not commit |
| |
| // get current index state |
| String indexState = indexState(CONTENT); |
| |
| // merge |
| MergeResult result = git.merge().include(sideCommit.getId()) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| |
| checkMergeFailedResult(result, MergeFailureReason.DIRTY_INDEX, |
| indexState, fileA); |
| } |
| } |
| |
| @Test |
| public void testSuccessfulMergeFailsDueToDirtyWorktree() throws Exception { |
| try (Git git = new Git(db)) { |
| File fileA = writeTrashFile("a", "a"); |
| RevCommit initialCommit = addAllAndCommit(git); |
| |
| // switch branch |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| // modify file a |
| write(fileA, "a(side)"); |
| writeTrashFile("b", "b"); |
| RevCommit sideCommit = addAllAndCommit(git); |
| |
| // switch branch |
| checkoutBranch("refs/heads/master"); |
| writeTrashFile("c", "c"); |
| addAllAndCommit(git); |
| |
| // modify file a |
| write(fileA, "a(modified)"); |
| // do not add and commit |
| |
| // get current index state |
| String indexState = indexState(CONTENT); |
| |
| // merge |
| MergeResult result = git.merge().include(sideCommit.getId()) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| |
| checkMergeFailedResult(result, MergeFailureReason.DIRTY_WORKTREE, |
| indexState, fileA); |
| } |
| } |
| |
| @Test |
| public void testConflictingMergeFailsDueToDirtyWorktree() throws Exception { |
| try (Git git = new Git(db)) { |
| File fileA = writeTrashFile("a", "a"); |
| RevCommit initialCommit = addAllAndCommit(git); |
| |
| // switch branch |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| // modify file a |
| write(fileA, "a(side)"); |
| writeTrashFile("b", "b"); |
| RevCommit sideCommit = addAllAndCommit(git); |
| |
| // switch branch |
| checkoutBranch("refs/heads/master"); |
| // modify file a - this will cause a conflict during merge |
| write(fileA, "a(master)"); |
| writeTrashFile("c", "c"); |
| addAllAndCommit(git); |
| |
| // modify file a |
| write(fileA, "a(modified)"); |
| // do not add and commit |
| |
| // get current index state |
| String indexState = indexState(CONTENT); |
| |
| // merge |
| MergeResult result = git.merge().include(sideCommit.getId()) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| |
| checkMergeFailedResult(result, MergeFailureReason.DIRTY_WORKTREE, |
| indexState, fileA); |
| } |
| } |
| |
| @Test |
| public void testMergeRemovingFolders() throws Exception { |
| File folder1 = new File(db.getWorkTree(), "folder1"); |
| File folder2 = new File(db.getWorkTree(), "folder2"); |
| FileUtils.mkdir(folder1); |
| FileUtils.mkdir(folder2); |
| File file = new File(folder1, "file1.txt"); |
| write(file, "folder1--file1.txt"); |
| file = new File(folder1, "file2.txt"); |
| write(file, "folder1--file2.txt"); |
| file = new File(folder2, "file1.txt"); |
| write(file, "folder--file1.txt"); |
| file = new File(folder2, "file2.txt"); |
| write(file, "folder2--file2.txt"); |
| |
| try (Git git = new Git(db)) { |
| git.add().addFilepattern(folder1.getName()) |
| .addFilepattern(folder2.getName()).call(); |
| RevCommit commit1 = git.commit().setMessage("adding folders").call(); |
| |
| recursiveDelete(folder1); |
| recursiveDelete(folder2); |
| git.rm().addFilepattern("folder1/file1.txt") |
| .addFilepattern("folder1/file2.txt") |
| .addFilepattern("folder2/file1.txt") |
| .addFilepattern("folder2/file2.txt").call(); |
| RevCommit commit2 = git.commit() |
| .setMessage("removing folders on 'branch'").call(); |
| |
| git.checkout().setName(commit1.name()).call(); |
| |
| MergeResult result = git.merge().include(commit2.getId()) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| assertEquals(MergeResult.MergeStatus.FAST_FORWARD, |
| result.getMergeStatus()); |
| assertEquals(commit2, result.getNewHead()); |
| assertFalse(folder1.exists()); |
| assertFalse(folder2.exists()); |
| } |
| } |
| |
| @Test |
| public void testMergeRemovingFoldersWithoutFastForward() throws Exception { |
| File folder1 = new File(db.getWorkTree(), "folder1"); |
| File folder2 = new File(db.getWorkTree(), "folder2"); |
| FileUtils.mkdir(folder1); |
| FileUtils.mkdir(folder2); |
| File file = new File(folder1, "file1.txt"); |
| write(file, "folder1--file1.txt"); |
| file = new File(folder1, "file2.txt"); |
| write(file, "folder1--file2.txt"); |
| file = new File(folder2, "file1.txt"); |
| write(file, "folder--file1.txt"); |
| file = new File(folder2, "file2.txt"); |
| write(file, "folder2--file2.txt"); |
| |
| try (Git git = new Git(db)) { |
| git.add().addFilepattern(folder1.getName()) |
| .addFilepattern(folder2.getName()).call(); |
| RevCommit base = git.commit().setMessage("adding folders").call(); |
| |
| recursiveDelete(folder1); |
| recursiveDelete(folder2); |
| git.rm().addFilepattern("folder1/file1.txt") |
| .addFilepattern("folder1/file2.txt") |
| .addFilepattern("folder2/file1.txt") |
| .addFilepattern("folder2/file2.txt").call(); |
| RevCommit other = git.commit() |
| .setMessage("removing folders on 'branch'").call(); |
| |
| git.checkout().setName(base.name()).call(); |
| |
| file = new File(folder2, "file3.txt"); |
| write(file, "folder2--file3.txt"); |
| |
| git.add().addFilepattern(folder2.getName()).call(); |
| git.commit().setMessage("adding another file").call(); |
| |
| MergeResult result = git.merge().include(other.getId()) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| |
| assertEquals(MergeResult.MergeStatus.MERGED, |
| result.getMergeStatus()); |
| assertFalse(folder1.exists()); |
| } |
| } |
| |
| @Test |
| public void testFileModeMerge() throws Exception { |
| // Only Java6 |
| assumeTrue(FS.DETECTED.supportsExecute()); |
| try (Git git = new Git(db)) { |
| writeTrashFile("mergeableMode", "a"); |
| setExecutable(git, "mergeableMode", false); |
| writeTrashFile("conflictingModeWithBase", "a"); |
| setExecutable(git, "conflictingModeWithBase", false); |
| RevCommit initialCommit = addAllAndCommit(git); |
| |
| // switch branch |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| setExecutable(git, "mergeableMode", true); |
| writeTrashFile("conflictingModeNoBase", "b"); |
| setExecutable(git, "conflictingModeNoBase", true); |
| RevCommit sideCommit = addAllAndCommit(git); |
| |
| // switch branch |
| createBranch(initialCommit, "refs/heads/side2"); |
| checkoutBranch("refs/heads/side2"); |
| setExecutable(git, "mergeableMode", false); |
| assertFalse(new File(git.getRepository().getWorkTree(), |
| "conflictingModeNoBase").exists()); |
| writeTrashFile("conflictingModeNoBase", "b"); |
| setExecutable(git, "conflictingModeNoBase", false); |
| addAllAndCommit(git); |
| |
| // merge |
| MergeResult result = git.merge().include(sideCommit.getId()) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus()); |
| assertTrue(canExecute(git, "mergeableMode")); |
| assertFalse(canExecute(git, "conflictingModeNoBase")); |
| } |
| } |
| |
| @Test |
| public void testFileModeMergeWithDirtyWorkTree() throws Exception { |
| // Only Java6 (or set x bit in index) |
| assumeTrue(FS.DETECTED.supportsExecute()); |
| |
| try (Git git = new Git(db)) { |
| writeTrashFile("mergeableButDirty", "a"); |
| setExecutable(git, "mergeableButDirty", false); |
| RevCommit initialCommit = addAllAndCommit(git); |
| |
| // switch branch |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| setExecutable(git, "mergeableButDirty", true); |
| RevCommit sideCommit = addAllAndCommit(git); |
| |
| // switch branch |
| createBranch(initialCommit, "refs/heads/side2"); |
| checkoutBranch("refs/heads/side2"); |
| setExecutable(git, "mergeableButDirty", false); |
| addAllAndCommit(git); |
| |
| writeTrashFile("mergeableButDirty", "b"); |
| |
| // merge |
| MergeResult result = git.merge().include(sideCommit.getId()) |
| .setStrategy(MergeStrategy.RESOLVE).call(); |
| assertEquals(MergeStatus.FAILED, result.getMergeStatus()); |
| assertFalse(canExecute(git, "mergeableButDirty")); |
| } |
| } |
| |
| @Test |
| public void testSquashFastForward() throws Exception { |
| try (Git git = new Git(db)) { |
| writeTrashFile("file1", "file1"); |
| git.add().addFilepattern("file1").call(); |
| RevCommit first = git.commit().setMessage("initial commit").call(); |
| |
| assertTrue(new File(db.getWorkTree(), "file1").exists()); |
| createBranch(first, "refs/heads/branch1"); |
| checkoutBranch("refs/heads/branch1"); |
| |
| writeTrashFile("file2", "file2"); |
| git.add().addFilepattern("file2").call(); |
| RevCommit second = git.commit().setMessage("second commit").call(); |
| assertTrue(new File(db.getWorkTree(), "file2").exists()); |
| |
| writeTrashFile("file3", "file3"); |
| git.add().addFilepattern("file3").call(); |
| RevCommit third = git.commit().setMessage("third commit").call(); |
| assertTrue(new File(db.getWorkTree(), "file3").exists()); |
| |
| checkoutBranch("refs/heads/master"); |
| assertTrue(new File(db.getWorkTree(), "file1").exists()); |
| assertFalse(new File(db.getWorkTree(), "file2").exists()); |
| assertFalse(new File(db.getWorkTree(), "file3").exists()); |
| |
| MergeResult result = git.merge() |
| .include(db.exactRef("refs/heads/branch1")) |
| .setSquash(true) |
| .call(); |
| |
| assertTrue(new File(db.getWorkTree(), "file1").exists()); |
| assertTrue(new File(db.getWorkTree(), "file2").exists()); |
| assertTrue(new File(db.getWorkTree(), "file3").exists()); |
| assertEquals(MergeResult.MergeStatus.FAST_FORWARD_SQUASHED, |
| result.getMergeStatus()); |
| assertEquals(first, result.getNewHead()); // HEAD didn't move |
| assertEquals(first, db.resolve(Constants.HEAD + "^{commit}")); |
| |
| assertEquals( |
| "Squashed commit of the following:\n\ncommit " |
| + third.getName() |
| + "\nAuthor: " |
| + third.getAuthorIdent().getName() |
| + " <" |
| + third.getAuthorIdent().getEmailAddress() |
| + ">\nDate: " |
| + dateFormatter.formatDate(third |
| .getAuthorIdent()) |
| + "\n\n\tthird commit\n\ncommit " |
| + second.getName() |
| + "\nAuthor: " |
| + second.getAuthorIdent().getName() |
| + " <" |
| + second.getAuthorIdent().getEmailAddress() |
| + ">\nDate: " |
| + dateFormatter.formatDate(second |
| .getAuthorIdent()) + "\n\n\tsecond commit\n", |
| db.readSquashCommitMsg()); |
| assertNull(db.readMergeCommitMsg()); |
| |
| Status stat = git.status().call(); |
| assertEquals(Sets.of("file2", "file3"), stat.getAdded()); |
| } |
| } |
| |
| @Test |
| public void testSquashMerge() throws Exception { |
| try (Git git = new Git(db)) { |
| writeTrashFile("file1", "file1"); |
| git.add().addFilepattern("file1").call(); |
| RevCommit first = git.commit().setMessage("initial commit").call(); |
| |
| assertTrue(new File(db.getWorkTree(), "file1").exists()); |
| createBranch(first, "refs/heads/branch1"); |
| |
| writeTrashFile("file2", "file2"); |
| git.add().addFilepattern("file2").call(); |
| RevCommit second = git.commit().setMessage("second commit").call(); |
| assertTrue(new File(db.getWorkTree(), "file2").exists()); |
| |
| checkoutBranch("refs/heads/branch1"); |
| |
| writeTrashFile("file3", "file3"); |
| git.add().addFilepattern("file3").call(); |
| RevCommit third = git.commit().setMessage("third commit").call(); |
| assertTrue(new File(db.getWorkTree(), "file3").exists()); |
| |
| checkoutBranch("refs/heads/master"); |
| assertTrue(new File(db.getWorkTree(), "file1").exists()); |
| assertTrue(new File(db.getWorkTree(), "file2").exists()); |
| assertFalse(new File(db.getWorkTree(), "file3").exists()); |
| |
| MergeResult result = git.merge() |
| .include(db.exactRef("refs/heads/branch1")) |
| .setSquash(true) |
| .call(); |
| |
| assertTrue(new File(db.getWorkTree(), "file1").exists()); |
| assertTrue(new File(db.getWorkTree(), "file2").exists()); |
| assertTrue(new File(db.getWorkTree(), "file3").exists()); |
| assertEquals(MergeResult.MergeStatus.MERGED_SQUASHED, |
| result.getMergeStatus()); |
| assertEquals(second, result.getNewHead()); // HEAD didn't move |
| assertEquals(second, db.resolve(Constants.HEAD + "^{commit}")); |
| |
| assertEquals( |
| "Squashed commit of the following:\n\ncommit " |
| + third.getName() |
| + "\nAuthor: " |
| + third.getAuthorIdent().getName() |
| + " <" |
| + third.getAuthorIdent().getEmailAddress() |
| + ">\nDate: " |
| + dateFormatter.formatDate(third |
| .getAuthorIdent()) + "\n\n\tthird commit\n", |
| db.readSquashCommitMsg()); |
| assertNull(db.readMergeCommitMsg()); |
| |
| Status stat = git.status().call(); |
| assertEquals(Sets.of("file3"), stat.getAdded()); |
| } |
| } |
| |
| @Test |
| public void testSquashMergeConflict() throws Exception { |
| try (Git git = new Git(db)) { |
| writeTrashFile("file1", "file1"); |
| git.add().addFilepattern("file1").call(); |
| RevCommit first = git.commit().setMessage("initial commit").call(); |
| |
| assertTrue(new File(db.getWorkTree(), "file1").exists()); |
| createBranch(first, "refs/heads/branch1"); |
| |
| writeTrashFile("file2", "master"); |
| git.add().addFilepattern("file2").call(); |
| RevCommit second = git.commit().setMessage("second commit").call(); |
| assertTrue(new File(db.getWorkTree(), "file2").exists()); |
| |
| checkoutBranch("refs/heads/branch1"); |
| |
| writeTrashFile("file2", "branch"); |
| git.add().addFilepattern("file2").call(); |
| RevCommit third = git.commit().setMessage("third commit").call(); |
| assertTrue(new File(db.getWorkTree(), "file2").exists()); |
| |
| checkoutBranch("refs/heads/master"); |
| assertTrue(new File(db.getWorkTree(), "file1").exists()); |
| assertTrue(new File(db.getWorkTree(), "file2").exists()); |
| |
| MergeResult result = git.merge() |
| .include(db.exactRef("refs/heads/branch1")) |
| .setSquash(true) |
| .call(); |
| |
| assertTrue(new File(db.getWorkTree(), "file1").exists()); |
| assertTrue(new File(db.getWorkTree(), "file2").exists()); |
| assertEquals(MergeResult.MergeStatus.CONFLICTING, |
| result.getMergeStatus()); |
| assertNull(result.getNewHead()); |
| assertEquals(second, db.resolve(Constants.HEAD + "^{commit}")); |
| |
| assertEquals( |
| "Squashed commit of the following:\n\ncommit " |
| + third.getName() |
| + "\nAuthor: " |
| + third.getAuthorIdent().getName() |
| + " <" |
| + third.getAuthorIdent().getEmailAddress() |
| + ">\nDate: " |
| + dateFormatter.formatDate(third |
| .getAuthorIdent()) + "\n\n\tthird commit\n", |
| db.readSquashCommitMsg()); |
| assertEquals("\nConflicts:\n\tfile2\n", db.readMergeCommitMsg()); |
| |
| Status stat = git.status().call(); |
| assertEquals(Sets.of("file2"), stat.getConflicting()); |
| } |
| } |
| |
| @Test |
| public void testFastForwardOnly() throws Exception { |
| try (Git git = new Git(db)) { |
| RevCommit initialCommit = git.commit().setMessage("initial commit") |
| .call(); |
| createBranch(initialCommit, "refs/heads/branch1"); |
| git.commit().setMessage("second commit").call(); |
| checkoutBranch("refs/heads/branch1"); |
| |
| MergeCommand merge = git.merge(); |
| merge.setFastForward(FastForwardMode.FF_ONLY); |
| merge.include(db.exactRef(R_HEADS + MASTER)); |
| MergeResult result = merge.call(); |
| |
| assertEquals(MergeStatus.FAST_FORWARD, result.getMergeStatus()); |
| } |
| } |
| |
| @Test |
| public void testNoFastForward() throws Exception { |
| try (Git git = new Git(db)) { |
| RevCommit initialCommit = git.commit().setMessage("initial commit") |
| .call(); |
| createBranch(initialCommit, "refs/heads/branch1"); |
| git.commit().setMessage("second commit").call(); |
| checkoutBranch("refs/heads/branch1"); |
| |
| MergeCommand merge = git.merge(); |
| merge.setFastForward(FastForwardMode.NO_FF); |
| merge.include(db.exactRef(R_HEADS + MASTER)); |
| MergeResult result = merge.call(); |
| |
| assertEquals(MergeStatus.MERGED, result.getMergeStatus()); |
| } |
| } |
| |
| @Test |
| public void testNoFastForwardNoCommit() throws Exception { |
| // given |
| try (Git git = new Git(db)) { |
| RevCommit initialCommit = git.commit().setMessage("initial commit") |
| .call(); |
| createBranch(initialCommit, "refs/heads/branch1"); |
| RevCommit secondCommit = git.commit().setMessage("second commit") |
| .call(); |
| checkoutBranch("refs/heads/branch1"); |
| |
| // when |
| MergeCommand merge = git.merge(); |
| merge.setFastForward(FastForwardMode.NO_FF); |
| merge.include(db.exactRef(R_HEADS + MASTER)); |
| merge.setCommit(false); |
| MergeResult result = merge.call(); |
| |
| // then |
| assertEquals(MergeStatus.MERGED_NOT_COMMITTED, result.getMergeStatus()); |
| assertEquals(2, result.getMergedCommits().length); |
| assertEquals(initialCommit, result.getMergedCommits()[0]); |
| assertEquals(secondCommit, result.getMergedCommits()[1]); |
| assertNull(result.getNewHead()); |
| assertEquals(RepositoryState.MERGING_RESOLVED, db.getRepositoryState()); |
| } |
| } |
| |
| @Test |
| public void testFastForwardOnlyNotPossible() throws Exception { |
| try (Git git = new Git(db)) { |
| RevCommit initialCommit = git.commit().setMessage("initial commit") |
| .call(); |
| createBranch(initialCommit, "refs/heads/branch1"); |
| git.commit().setMessage("second commit").call(); |
| checkoutBranch("refs/heads/branch1"); |
| writeTrashFile("file1", "branch1"); |
| git.add().addFilepattern("file").call(); |
| git.commit().setMessage("second commit on branch1").call(); |
| MergeCommand merge = git.merge(); |
| merge.setFastForward(FastForwardMode.FF_ONLY); |
| merge.include(db.exactRef(R_HEADS + MASTER)); |
| MergeResult result = merge.call(); |
| |
| assertEquals(MergeStatus.ABORTED, result.getMergeStatus()); |
| } |
| } |
| |
| @Test |
| public void testRecursiveMergeWithConflict() throws Exception { |
| try (TestRepository<Repository> db_t = new TestRepository<>(db)) { |
| BranchBuilder master = db_t.branch("master"); |
| RevCommit m0 = master.commit() |
| .add("f", "1\n2\n3\n4\n5\n6\n7\n8\n9\n").message("m0") |
| .create(); |
| RevCommit m1 = master.commit() |
| .add("f", "1-master\n2\n3\n4\n5\n6\n7\n8\n9\n") |
| .message("m1").create(); |
| db_t.getRevWalk().parseCommit(m1); |
| |
| BranchBuilder side = db_t.branch("side"); |
| RevCommit s1 = side.commit().parent(m0) |
| .add("f", "1\n2\n3\n4\n5\n6\n7\n8\n9-side\n").message("s1") |
| .create(); |
| RevCommit s2 = side.commit().parent(m1) |
| .add("f", |
| "1-master\n2\n3\n4\n5\n6\n7-res(side)\n8\n9-side\n") |
| .message("s2(merge)").create(); |
| master.commit().parent(s1) |
| .add("f", |
| "1-master\n2\n3\n4\n5\n6\n7-conflict\n8\n9-side\n") |
| .message("m2(merge)").create(); |
| |
| Git git = Git.wrap(db); |
| git.checkout().setName("master").call(); |
| |
| MergeResult result = git.merge() |
| .setStrategy(MergeStrategy.RECURSIVE).include("side", s2) |
| .call(); |
| assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus()); |
| } |
| } |
| |
| private Ref prepareSuccessfulMerge(Git git) throws Exception { |
| writeTrashFile("a", "1\na\n3\n"); |
| git.add().addFilepattern("a").call(); |
| RevCommit initialCommit = git.commit().setMessage("initial").call(); |
| |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| |
| writeTrashFile("b", "1\nb\n3\n"); |
| git.add().addFilepattern("b").call(); |
| git.commit().setMessage("side").call(); |
| |
| checkoutBranch("refs/heads/master"); |
| |
| writeTrashFile("c", "1\nc\n3\n"); |
| git.add().addFilepattern("c").call(); |
| git.commit().setMessage("main").call(); |
| |
| return db.exactRef("refs/heads/side"); |
| } |
| |
| @Test |
| public void testMergeWithMessageOption() throws Exception { |
| try (Git git = new Git(db)) { |
| Ref sideBranch = prepareSuccessfulMerge(git); |
| |
| git.merge().include(sideBranch).setStrategy(MergeStrategy.RESOLVE) |
| .setMessage("user message").call(); |
| |
| assertNull(db.readMergeCommitMsg()); |
| |
| Iterator<RevCommit> it = git.log().call().iterator(); |
| RevCommit newHead = it.next(); |
| assertEquals("user message", newHead.getFullMessage()); |
| } |
| } |
| |
| @Test |
| public void testMergeWithChangeId() throws Exception { |
| try (Git git = new Git(db)) { |
| Ref sideBranch = prepareSuccessfulMerge(git); |
| |
| git.merge().include(sideBranch).setStrategy(MergeStrategy.RESOLVE) |
| .setInsertChangeId(true).call(); |
| |
| assertNull(db.readMergeCommitMsg()); |
| |
| Iterator<RevCommit> it = git.log().call().iterator(); |
| RevCommit newHead = it.next(); |
| String commitMessage = newHead.getFullMessage(); |
| assertTrue(Pattern.compile("\nChange-Id: I[0-9a-fA-F]{40}\n") |
| .matcher(commitMessage).find()); |
| } |
| } |
| |
| @Test |
| public void testMergeWithMessageAndChangeId() throws Exception { |
| try (Git git = new Git(db)) { |
| Ref sideBranch = prepareSuccessfulMerge(git); |
| |
| git.merge().include(sideBranch).setStrategy(MergeStrategy.RESOLVE) |
| .setMessage("user message").setInsertChangeId(true).call(); |
| |
| assertNull(db.readMergeCommitMsg()); |
| |
| Iterator<RevCommit> it = git.log().call().iterator(); |
| RevCommit newHead = it.next(); |
| String commitMessage = newHead.getFullMessage(); |
| assertTrue(commitMessage.startsWith("user message\n\n")); |
| assertTrue(Pattern.compile("\nChange-Id: I[0-9a-fA-F]{40}\n") |
| .matcher(commitMessage).find()); |
| } |
| } |
| |
| @Test |
| public void testMergeConflictWithMessageOption() throws Exception { |
| try (Git git = new Git(db)) { |
| writeTrashFile("a", "1\na\n3\n"); |
| git.add().addFilepattern("a").call(); |
| RevCommit initialCommit = git.commit().setMessage("initial").call(); |
| |
| createBranch(initialCommit, "refs/heads/side"); |
| checkoutBranch("refs/heads/side"); |
| |
| writeTrashFile("a", "1\na(side)\n3\n"); |
| git.add().addFilepattern("a").call(); |
| git.commit().setMessage("side").call(); |
| |
| checkoutBranch("refs/heads/master"); |
| |
| writeTrashFile("a", "1\na(main)\n3\n"); |
| git.add().addFilepattern("a").call(); |
| git.commit().setMessage("main").call(); |
| |
| Ref sideBranch = db.exactRef("refs/heads/side"); |
| |
| git.merge().include(sideBranch).setStrategy(MergeStrategy.RESOLVE) |
| .setMessage("user message").call(); |
| |
| assertEquals("user message\n\nConflicts:\n\ta\n", |
| db.readMergeCommitMsg()); |
| } |
| } |
| |
| private static void setExecutable(Git git, String path, boolean executable) { |
| FS.DETECTED.setExecute( |
| new File(git.getRepository().getWorkTree(), path), executable); |
| } |
| |
| private static boolean canExecute(Git git, String path) { |
| return FS.DETECTED.canExecute(new File(git.getRepository() |
| .getWorkTree(), path)); |
| } |
| |
| private static RevCommit addAllAndCommit(Git git) throws Exception { |
| git.add().addFilepattern(".").call(); |
| return git.commit().setMessage("message").call(); |
| } |
| |
| private void checkMergeFailedResult(final MergeResult result, |
| final MergeFailureReason reason, |
| final String indexState, final File fileA) throws Exception { |
| assertEquals(MergeStatus.FAILED, result.getMergeStatus()); |
| assertEquals(reason, result.getFailingPaths().get("a")); |
| assertEquals("a(modified)", read(fileA)); |
| assertFalse(new File(db.getWorkTree(), "b").exists()); |
| assertEquals("c", read(new File(db.getWorkTree(), "c"))); |
| assertEquals(indexState, indexState(CONTENT)); |
| assertEquals(null, result.getConflicts()); |
| assertEquals(RepositoryState.SAFE, db.getRepositoryState()); |
| } |
| } |