diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandWithRebaseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandWithRebaseTest.java index 2c1eb768b..2f2faf822 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandWithRebaseTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandWithRebaseTest.java @@ -44,6 +44,7 @@ package org.eclipse.jgit.api; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -58,11 +59,14 @@ import org.eclipse.jgit.api.MergeResult.MergeStatus; import org.eclipse.jgit.api.RebaseResult.Status; import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryState; import org.eclipse.jgit.lib.RepositoryTestCase; import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.merge.MergeStrategy; import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.storage.file.FileRepository; import org.eclipse.jgit.transport.RefSpec; import org.eclipse.jgit.transport.RemoteConfig; @@ -226,6 +230,72 @@ public class PullCommandWithRebaseTest extends RepositoryTestCase { .getRepository().getRepositoryState()); } + @Test + public void testPullFastForwardWithLocalCommitAndRebaseFlagSet() throws Exception { + final String SOURCE_COMMIT_MESSAGE = "Source commit message for rebase flag test"; + final String TARGET_COMMIT_MESSAGE = "Target commit message for rebase flag test"; + + assertFalse(SOURCE_COMMIT_MESSAGE.equals(TARGET_COMMIT_MESSAGE)); + + final String SOURCE_FILE_CONTENTS = "Source change"; + final String NEW_FILE_CONTENTS = "New file from target"; + + // make sure the config for target says we should pull with merge + // we will override this later with the setRebase method + StoredConfig targetConfig = dbTarget.getConfig(); + targetConfig.setBoolean("branch", "master", "rebase", false); + targetConfig.save(); + + // create commit in source + writeToFile(sourceFile, SOURCE_FILE_CONTENTS); + source.add().addFilepattern(sourceFile.getName()).call(); + source.commit().setMessage(SOURCE_COMMIT_MESSAGE).call(); + + // create commit in target, not conflicting with the new commit in source + File newFile = new File(dbTarget.getWorkTree().getPath() + "/newFile.txt"); + writeToFile(newFile, NEW_FILE_CONTENTS); + target.add().addFilepattern(newFile.getName()).call(); + target.commit().setMessage(TARGET_COMMIT_MESSAGE).call(); + + // verify that rebase is set to false in the config + assertFalse(targetConfig.getBoolean("branch", "master", "rebase", true)); + + // pull with rebase - local commit in target should be on top + PullResult pullResult = target.pull().setRebase(true).call(); + + // make sure pull is considered successful + assertTrue(pullResult.isSuccessful()); + + // verify rebase result is ok + RebaseResult rebaseResult = pullResult.getRebaseResult(); + assertNotNull(rebaseResult); + assertNull(rebaseResult.getFailingPaths()); + assertEquals(Status.OK, rebaseResult.getStatus()); + + // Get the HEAD and HEAD~1 commits + Repository targetRepo = target.getRepository(); + RevWalk revWalk = new RevWalk(targetRepo); + ObjectId headId = targetRepo.resolve(Constants.HEAD); + RevCommit root = revWalk.parseCommit(headId); + revWalk.markStart(root); + // HEAD + RevCommit head = revWalk.next(); + // HEAD~1 + RevCommit beforeHead = revWalk.next(); + + // verify the commit message on the HEAD commit + assertEquals(TARGET_COMMIT_MESSAGE, head.getFullMessage()); + // verify the commit just before HEAD + assertEquals(SOURCE_COMMIT_MESSAGE, beforeHead.getFullMessage()); + + // verify file states + assertFileContentsEqual(sourceFile, SOURCE_FILE_CONTENTS); + assertFileContentsEqual(newFile, NEW_FILE_CONTENTS); + // verify repository state + assertEquals(RepositoryState.SAFE, target + .getRepository().getRepositoryState()); + } + @Override @Before public void setUp() throws Exception { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java index 5b8c776fd..817909888 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java @@ -80,6 +80,14 @@ public class PullCommand extends TransportCommand { private ProgressMonitor monitor = NullProgressMonitor.INSTANCE; + private PullRebaseMode pullRebaseMode = PullRebaseMode.USE_CONFIG; + + private enum PullRebaseMode { + USE_CONFIG, + REBASE, + NO_REBASE + } + /** * @param repo */ @@ -97,6 +105,28 @@ public class PullCommand extends TransportCommand { return this; } + /** + * Set if rebase should be used after fetching. If set to true, rebase is + * used instead of merge. This is equivalent to --rebase on the command line. + *

+ * If set to false, merge is used after fetching, overriding the configuration + * file. This is equivalent to --no-rebase on the command line. + *

+ * This setting overrides the settings in the configuration file. + * By default, the setting in the repository configuration file is used. + *

+ * A branch can be configured to use rebase by default. + * See branch.[name].rebase and branch.autosetuprebase. + * + * @param useRebase + * @return {@code this} + */ + public PullCommand setRebase(boolean useRebase) { + checkCallable(); + pullRebaseMode = useRebase ? PullRebaseMode.REBASE : PullRebaseMode.NO_REBASE; + return this; + } + /** * Executes the {@code Pull} command with all the options and parameters * collected by the setter methods (e.g. @@ -162,10 +192,24 @@ public class PullCommand extends TransportCommand { String remoteBranchName = repoConfig.getString( ConfigConstants.CONFIG_BRANCH_SECTION, branchName, ConfigConstants.CONFIG_KEY_MERGE); - // check if the branch is configured for pull-rebase - boolean doRebase = repoConfig.getBoolean( - ConfigConstants.CONFIG_BRANCH_SECTION, branchName, - ConfigConstants.CONFIG_KEY_REBASE, false); + + // determines whether rebase should be used after fetching + boolean doRebase = false; + switch (pullRebaseMode) { + case REBASE: + doRebase = true; + break; + case NO_REBASE: + doRebase = false; + break; + case USE_CONFIG: + default: + // check if the branch is configured for pull-rebase + doRebase = repoConfig.getBoolean( + ConfigConstants.CONFIG_BRANCH_SECTION, branchName, + ConfigConstants.CONFIG_KEY_REBASE, false); + break; + } if (remoteBranchName == null) { String missingKey = ConfigConstants.CONFIG_BRANCH_SECTION + DOT