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 b405f6ad7..6f6b1158e 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 @@ -61,6 +61,7 @@ import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryState; import org.eclipse.jgit.lib.StoredConfig; @@ -147,6 +148,57 @@ public void testPullFastForwardWithBranchInSource() throws Exception { } + @Test + public void testPullFastForwardDetachedHead() throws Exception { + Repository repository = source.getRepository(); + writeToFile(sourceFile, "2nd commit"); + source.add().addFilepattern("SomeFile.txt").call(); + source.commit().setMessage("2nd commit").call(); + + try (RevWalk revWalk = new RevWalk(repository)) { + // git checkout HEAD^ + String initialBranch = repository.getBranch(); + Ref initialRef = repository.findRef(Constants.HEAD); + RevCommit initialCommit = revWalk + .parseCommit(initialRef.getObjectId()); + assertEquals("this test need linear history", 1, + initialCommit.getParentCount()); + source.checkout().setName(initialCommit.getParent(0).getName()) + .call(); + assertFalse("expected detached HEAD", + repository.getFullBranch().startsWith(Constants.R_HEADS)); + + // change and commit another file + File otherFile = new File(sourceFile.getParentFile(), + System.currentTimeMillis() + ".tst"); + writeToFile(otherFile, "other 2nd commit"); + source.add().addFilepattern(otherFile.getName()).call(); + RevCommit newCommit = source.commit().setMessage("other 2nd commit") + .call(); + + // git pull --rebase initialBranch + source.pull().setRebase(true).setRemote(".") + .setRemoteBranchName(initialBranch) + .call(); + + assertEquals(RepositoryState.SAFE, + source.getRepository().getRepositoryState()); + Ref head = source.getRepository().findRef(Constants.HEAD); + RevCommit headCommit = revWalk.parseCommit(head.getObjectId()); + + // HEAD^ == initialCommit, no merge commit + assertEquals(1, headCommit.getParentCount()); + assertEquals(initialCommit, headCommit.getParent(0)); + + // both contributions for both commits are available + assertFileContentsEqual(sourceFile, "2nd commit"); + assertFileContentsEqual(otherFile, "other 2nd commit"); + // HEAD has same message as rebased commit + assertEquals(newCommit.getShortMessage(), + headCommit.getShortMessage()); + } + } + @Test public void testPullConflict() throws Exception { PullResult res = target.pull().call(); 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 ae822da56..9c5ae432e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java @@ -203,62 +203,63 @@ public PullCommand setRebase(BranchRebaseMode rebaseMode) { @Override public PullResult call() throws GitAPIException, WrongRepositoryStateException, InvalidConfigurationException, - DetachedHeadException, InvalidRemoteException, CanceledException, + InvalidRemoteException, CanceledException, RefNotFoundException, RefNotAdvertisedException, NoHeadException, org.eclipse.jgit.api.errors.TransportException { checkCallable(); monitor.beginTask(JGitText.get().pullTaskName, 2); + Config repoConfig = repo.getConfig(); - String branchName; + String branchName = null; try { String fullBranch = repo.getFullBranch(); - if (fullBranch == null) - throw new NoHeadException( - JGitText.get().pullOnRepoWithoutHEADCurrentlyNotSupported); - if (!fullBranch.startsWith(Constants.R_HEADS)) { - // we can not pull if HEAD is detached and branch is not - // specified explicitly - throw new DetachedHeadException(); + if (fullBranch != null + && fullBranch.startsWith(Constants.R_HEADS)) { + branchName = fullBranch.substring(Constants.R_HEADS.length()); } - branchName = fullBranch.substring(Constants.R_HEADS.length()); } catch (IOException e) { throw new JGitInternalException( JGitText.get().exceptionCaughtDuringExecutionOfPullCommand, e); } + if (remoteBranchName == null && branchName != null) { + // get the name of the branch in the remote repository + // stored in configuration key branch..merge + remoteBranchName = repoConfig.getString( + ConfigConstants.CONFIG_BRANCH_SECTION, branchName, + ConfigConstants.CONFIG_KEY_MERGE); + } + if (remoteBranchName == null) { + remoteBranchName = branchName; + } + if (remoteBranchName == null) { + throw new NoHeadException( + JGitText.get().cannotCheckoutFromUnbornBranch); + } if (!repo.getRepositoryState().equals(RepositoryState.SAFE)) throw new WrongRepositoryStateException(MessageFormat.format( JGitText.get().cannotPullOnARepoWithState, repo .getRepositoryState().name())); - Config repoConfig = repo.getConfig(); - if (remote == null) { + if (remote == null && branchName != null) { // get the configured remote for the currently checked out branch // stored in configuration key branch..remote remote = repoConfig.getString( ConfigConstants.CONFIG_BRANCH_SECTION, branchName, ConfigConstants.CONFIG_KEY_REMOTE); } - if (remote == null) + if (remote == null) { // fall back to default remote remote = Constants.DEFAULT_REMOTE_NAME; - - if (remoteBranchName == null) - // get the name of the branch in the remote repository - // stored in configuration key branch..merge - remoteBranchName = repoConfig.getString( - ConfigConstants.CONFIG_BRANCH_SECTION, branchName, - ConfigConstants.CONFIG_KEY_MERGE); + } // determines whether rebase should be used after fetching - if (pullRebaseMode == null) { + if (pullRebaseMode == null && branchName != null) { pullRebaseMode = getRebaseMode(branchName, repoConfig); } - if (remoteBranchName == null) - remoteBranchName = branchName; final boolean isRemote = !remote.equals("."); //$NON-NLS-1$ String remoteUri;