From db55d13f5f4b641a9390076ced6b1775bcfcd6f6 Mon Sep 17 00:00:00 2001 From: Mathias Kinzler Date: Thu, 7 Oct 2010 11:37:16 +0200 Subject: [PATCH] Add "Pull" command This is the minimal implementation of a "Pull" command. It does not have any parameters besides the generic progress monitor and timeout. It works on the currently checked-out branch and assumes that the configuration contains the keys "branch..remote" and "branch..merge" to determine the remote configuration for the fetch and the remote branch name for the merge. Bug: 303404 Change-Id: I7fe09029996d0cfc09a7d8f097b5d6af1488fa93 Signed-off-by: Mathias Kinzler Signed-off-by: Chris Aniszczyk --- .../org/eclipse/jgit/api/PullCommandTest.java | 200 ++++++++++++++ .../org/eclipse/jgit/JGitText.properties | 6 + .../src/org/eclipse/jgit/JGitText.java | 6 + .../src/org/eclipse/jgit/api/Git.java | 9 + .../src/org/eclipse/jgit/api/PullCommand.java | 260 ++++++++++++++++++ .../src/org/eclipse/jgit/api/PullResult.java | 100 +++++++ .../jgit/api/errors/CanceledException.java | 53 ++++ .../api/errors/DetachedHeadException.java | 71 +++++ .../errors/InvalidConfigurationException.java | 61 ++++ .../org/eclipse/jgit/lib/ConfigConstants.java | 6 + 10 files changed, 772 insertions(+) create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/api/PullResult.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/api/errors/CanceledException.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/api/errors/DetachedHeadException.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/api/errors/InvalidConfigurationException.java diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java new file mode 100644 index 000000000..de75fd5b6 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2010, Mathias Kinzler + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.api; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import org.eclipse.jgit.api.MergeResult.MergeStatus; +import org.eclipse.jgit.lib.RefUpdate; +import org.eclipse.jgit.lib.RepositoryTestCase; +import org.eclipse.jgit.lib.StoredConfig; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.storage.file.FileRepository; +import org.eclipse.jgit.transport.RefSpec; +import org.eclipse.jgit.transport.RemoteConfig; +import org.eclipse.jgit.transport.URIish; + +public class PullCommandTest extends RepositoryTestCase { + /** Second Test repository */ + protected FileRepository dbTarget; + + private Git source; + + private Git target; + + private File sourceFile; + + private File targetFile; + + public void testPullFastForward() throws Exception { + PullResult res = target.pull().call(); + // nothing to update since we don't have different data yet + assertTrue(res.getFetchResult().getTrackingRefUpdates().isEmpty()); + assertTrue(res.getMergeResult().getMergeStatus().equals( + MergeStatus.ALREADY_UP_TO_DATE)); + + assertFileContentsEqual(targetFile, "Hello world"); + + // change the source file + writeToFile(sourceFile, "Another change"); + source.add().addFilepattern("SomeFile.txt").call(); + source.commit().setMessage("Some change in remote").call(); + + res = target.pull().call(); + + assertFalse(res.getFetchResult().getTrackingRefUpdates().isEmpty()); + assertEquals(res.getMergeResult().getMergeStatus(), + MergeStatus.FAST_FORWARD); + assertFileContentsEqual(targetFile, "Another change"); + } + + public void testPullConflict() throws Exception { + PullResult res = target.pull().call(); + // nothing to update since we don't have different data yet + assertTrue(res.getFetchResult().getTrackingRefUpdates().isEmpty()); + assertTrue(res.getMergeResult().getMergeStatus().equals( + MergeStatus.ALREADY_UP_TO_DATE)); + + assertFileContentsEqual(targetFile, "Hello world"); + + // change the source file + writeToFile(sourceFile, "Source change"); + source.add().addFilepattern("SomeFile.txt").call(); + source.commit().setMessage("Source change in remote").call(); + + // change the target file + writeToFile(targetFile, "Target change"); + target.add().addFilepattern("SomeFile.txt").call(); + target.commit().setMessage("Target change in local").call(); + + res = target.pull().call(); + + String sourceChangeString = "Source change\n>>>>>>> branch 'refs/heads/master' of " + + target.getRepository().getConfig().getString("remote", + "origin", "url"); + + assertFalse(res.getFetchResult().getTrackingRefUpdates().isEmpty()); + assertEquals(res.getMergeResult().getMergeStatus(), + MergeStatus.CONFLICTING); + String result = "<<<<<<< HEAD\nTarget change\n=======\n" + + sourceChangeString + "\n"; + assertFileContentsEqual(targetFile, result); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + dbTarget = createWorkRepository(); + source = new Git(db); + target = new Git(dbTarget); + + // put some file in the source repo + sourceFile = new File(db.getWorkTree(), "SomeFile.txt"); + writeToFile(sourceFile, "Hello world"); + // and commit it + source.add().addFilepattern("SomeFile.txt").call(); + RevCommit commit = source.commit().setMessage( + "Initial commit for source").call(); + + // point the master branch to the new commit + RefUpdate upd = dbTarget.updateRef("refs/heads/master"); + upd.setNewObjectId(commit.getId()); + upd.update(); + + // configure the target repo to connect to the source via "origin" + StoredConfig targetConfig = dbTarget.getConfig(); + targetConfig.setString("branch", "master", "remote", "origin"); + targetConfig + .setString("branch", "master", "merge", "refs/heads/master"); + RemoteConfig config = new RemoteConfig(targetConfig, "origin"); + + config + .addURI(new URIish(source.getRepository().getWorkTree() + .getPath())); + config.addFetchRefSpec(new RefSpec( + "+refs/heads/*:refs/remotes/origin/*")); + targetConfig.save(); + config.update(targetConfig); + + targetFile = new File(dbTarget.getWorkTree(), "SomeFile.txt"); + writeToFile(targetFile, "Hello world"); + // make sure we have the same content + target.pull().call(); + } + + private void writeToFile(File actFile, String string) throws IOException { + FileOutputStream fos = null; + try { + fos = new FileOutputStream(actFile); + fos.write(string.getBytes("UTF-8")); + fos.close(); + } finally { + if (fos != null) + fos.close(); + } + } + + private void assertFileContentsEqual(File actFile, String string) + throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + FileInputStream fis = null; + byte[] buffer = new byte[100]; + try { + fis = new FileInputStream(actFile); + int read = fis.read(buffer); + while (read > 0) { + bos.write(buffer, 0, read); + read = fis.read(buffer); + } + String content = new String(bos.toByteArray(), "UTF-8"); + assertEquals(string, content); + } finally { + if (fis != null) + fis.close(); + } + } +} diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties index 7c5c60860..2b0bf515d 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties @@ -56,6 +56,7 @@ cannotMoveIndexTo=Cannot move index to {0} cannotMovePackTo=Cannot move pack to {0} cannotOpenService=cannot open {0} cannotParseGitURIish=Cannot parse Git URI-ish +cannotPullOnARepoWithState=Cannot pull into a repository with state: {0} cannotRead=Cannot read {0} cannotReadBlob=Cannot read blob {0} cannotReadCommit=Cannot read commit {0} @@ -121,6 +122,7 @@ creatingDeltasIsNotImplemented=creating deltas is not implemented daemonAlreadyRunning=Daemon already running deletingNotSupported=Deleting {0} not supported. destinationIsNotAWildcard=Destination is not a wildcard. +detachedHeadDetected=HEAD is detached dirCacheDoesNotHaveABackingFile=DirCache does not have a backing file dirCacheFileIsNotLocked=DirCache {0} not locked dirCacheIsNotLocked=DirCache is not locked @@ -153,6 +155,7 @@ exceptionCaughtDuringExecutionOfCommitCommand=Exception caught during execution exceptionCaughtDuringExecutionOfFetchCommand=Exception caught during execution of fetch command exceptionCaughtDuringExecutionOfMergeCommand=Exception caught during execution of merge command. {0} exceptionCaughtDuringExecutionOfPushCommand=Exception caught during execution of push command +exceptionCaughtDuringExecutionOfPullCommand=Exception caught during execution of pull command exceptionCaughtDuringExecutionOfTagCommand=Exception caught during execution of tag command exceptionOccuredDuringAddingOfOptionToALogCommand=Exception occured during adding of {0} as option to a Log command exceptionOccuredDuringReadingOfGIT_DIR=Exception occured during reading of $GIT_DIR/{0}. {1} @@ -241,6 +244,7 @@ mergeStrategyAlreadyExistsAsDefault=Merge strategy "{0}" already exists as a def mergeStrategyDoesNotSupportHeads=merge strategy {0} does not support {1} heads to be merged into HEAD mergeUsingStrategyResultedInDescription=Merge of revisions {0} with base {1} using strategy {2} resulted in: {3}. {4} missingAccesskey=Missing accesskey. +missingConfigurationForKey=No value for key {0} found in configuration missingDeltaBase=delta base missingForwardImageInGITBinaryPatch=Missing forward-image in GIT binary patch missingObject=Missing {0} {1} @@ -284,6 +288,7 @@ onlyOneFetchSupported=Only one fetch supported onlyOneOperationCallPerConnectionIsSupported=Only one operation call per connection is supported. openFilesMustBeAtLeast1=Open files must be >= 1 openingConnection=Opening connection +operationCanceled=Operation {0} was canceled outputHasAlreadyBeenStarted=Output has already been started. packChecksumMismatch=Pack checksum mismatch packCorruptedWhileWritingToFilesystem=Pack corrupted while writing to filesystem @@ -304,6 +309,7 @@ prefixRemote=remote: problemWithResolvingPushRefSpecsLocally=Problem with resolving push ref specs locally: {0} progressMonUploading=Uploading {0} propertyIsAlreadyNonNull=Property is already non null +pullTaskName=Pull pushCancelled=push cancelled pushIsNotSupportedForBundleTransport=Push is not supported for bundle transport pushNotPermitted=push not permitted diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java index 502823ad5..6019b6a5d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java @@ -116,6 +116,7 @@ public class JGitText extends TranslationBundle { /***/ public String cannotMovePackTo; /***/ public String cannotOpenService; /***/ public String cannotParseGitURIish; + /***/ public String cannotPullOnARepoWithState; /***/ public String cannotRead; /***/ public String cannotReadBlob; /***/ public String cannotReadCommit; @@ -181,6 +182,7 @@ public class JGitText extends TranslationBundle { /***/ public String daemonAlreadyRunning; /***/ public String deletingNotSupported; /***/ public String destinationIsNotAWildcard; + /***/ public String detachedHeadDetected; /***/ public String dirCacheDoesNotHaveABackingFile; /***/ public String dirCacheFileIsNotLocked; /***/ public String dirCacheIsNotLocked; @@ -213,6 +215,7 @@ public class JGitText extends TranslationBundle { /***/ public String exceptionCaughtDuringExecutionOfFetchCommand; /***/ public String exceptionCaughtDuringExecutionOfMergeCommand; /***/ public String exceptionCaughtDuringExecutionOfPushCommand; + /***/ public String exceptionCaughtDuringExecutionOfPullCommand; /***/ public String exceptionCaughtDuringExecutionOfTagCommand; /***/ public String exceptionOccuredDuringAddingOfOptionToALogCommand; /***/ public String exceptionOccuredDuringReadingOfGIT_DIR; @@ -301,6 +304,7 @@ public class JGitText extends TranslationBundle { /***/ public String mergeStrategyDoesNotSupportHeads; /***/ public String mergeUsingStrategyResultedInDescription; /***/ public String missingAccesskey; + /***/ public String missingConfigurationForKey; /***/ public String missingDeltaBase; /***/ public String missingForwardImageInGITBinaryPatch; /***/ public String missingObject; @@ -344,6 +348,7 @@ public class JGitText extends TranslationBundle { /***/ public String onlyOneOperationCallPerConnectionIsSupported; /***/ public String openFilesMustBeAtLeast1; /***/ public String openingConnection; + /***/ public String operationCanceled; /***/ public String outputHasAlreadyBeenStarted; /***/ public String packChecksumMismatch; /***/ public String packCorruptedWhileWritingToFilesystem; @@ -364,6 +369,7 @@ public class JGitText extends TranslationBundle { /***/ public String problemWithResolvingPushRefSpecsLocally; /***/ public String progressMonUploading; /***/ public String propertyIsAlreadyNonNull; + /***/ public String pullTaskName; /***/ public String pushCancelled; /***/ public String pushIsNotSupportedForBundleTransport; /***/ public String pushNotPermitted; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java index 480178b9c..493019a15 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java @@ -133,6 +133,15 @@ public class Git { return new MergeCommand(repo); } + /** + * Returns a command object to execute a {@code Pull} command + * + * @return a {@link PullCommand} + */ + public PullCommand pull() { + return new PullCommand(repo); + } + /** * Returns a command object to execute a {@code Add} command * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java new file mode 100644 index 000000000..b7f09d9a7 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2010, Christian Halstrick + * Copyright (C) 2010, Mathias Kinzler + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.api; + +import java.io.IOException; +import java.text.MessageFormat; + +import org.eclipse.jgit.JGitText; +import org.eclipse.jgit.api.errors.CanceledException; +import org.eclipse.jgit.api.errors.CheckoutConflictException; +import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException; +import org.eclipse.jgit.api.errors.DetachedHeadException; +import org.eclipse.jgit.api.errors.InvalidConfigurationException; +import org.eclipse.jgit.api.errors.InvalidMergeHeadsException; +import org.eclipse.jgit.api.errors.InvalidRemoteException; +import org.eclipse.jgit.api.errors.JGitInternalException; +import org.eclipse.jgit.api.errors.NoHeadException; +import org.eclipse.jgit.api.errors.NoMessageException; +import org.eclipse.jgit.api.errors.WrongRepositoryStateException; +import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.ConfigConstants; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.NullProgressMonitor; +import org.eclipse.jgit.lib.ProgressMonitor; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.RepositoryState; +import org.eclipse.jgit.transport.FetchResult; + +/** + * The Pull command + * + * @see Git documentation about Pull + */ +public class PullCommand extends GitCommand { + private int timeout = 0; + + private final static String DOT = "."; + + private ProgressMonitor monitor = NullProgressMonitor.INSTANCE; + + /** + * @param repo + */ + protected PullCommand(Repository repo) { + super(repo); + } + + /** + * @param timeout + * in seconds + * @return this instance + */ + public PullCommand setTimeout(int timeout) { + this.timeout = timeout; + return this; + } + + /** + * @param monitor + * a progress monitor + * @return this instance + */ + public PullCommand setProgressMonitor(ProgressMonitor monitor) { + this.monitor = monitor; + return this; + } + + /** + * Executes the {@code Pull} command with all the options and parameters + * collected by the setter methods (e.g. + * {@link #setProgressMonitor(ProgressMonitor)}) of this class. Each + * instance of this class should only be used for one invocation of the + * command. Don't call this method twice on an instance. + * + * @return the result of the pull + */ + public PullResult call() throws WrongRepositoryStateException, + InvalidConfigurationException, DetachedHeadException, + InvalidRemoteException, CanceledException { + checkCallable(); + + monitor.beginTask(JGitText.get().pullTaskName, 2); + + String branchName; + try { + String fullBranch = repo.getFullBranch(); + if (!fullBranch.startsWith(Constants.R_HEADS)) { + // we can not pull if HEAD is detached and branch is not + // specified explicitly + throw new DetachedHeadException(); + } + branchName = fullBranch.substring(Constants.R_HEADS.length()); + } catch (IOException e) { + throw new JGitInternalException( + JGitText.get().exceptionCaughtDuringExecutionOfPullCommand, + e); + } + + if (!repo.getRepositoryState().equals(RepositoryState.SAFE)) + throw new WrongRepositoryStateException(MessageFormat.format( + JGitText.get().cannotPullOnARepoWithState, repo + .getRepositoryState().name())); + + // get the configured remote for the currently checked out branch + // stored in configuration key branch..remote + Config repoConfig = repo.getConfig(); + final String remote = repoConfig.getString( + ConfigConstants.CONFIG_BRANCH_SECTION, branchName, + ConfigConstants.CONFIG_KEY_REMOTE); + if (remote == null) { + String missingKey = ConfigConstants.CONFIG_BRANCH_SECTION + DOT + + branchName + DOT + ConfigConstants.CONFIG_KEY_REMOTE; + throw new InvalidConfigurationException(MessageFormat.format( + JGitText.get().missingConfigurationForKey, missingKey)); + } + final String remoteUri = repo.getConfig().getString("remote", remote, + ConfigConstants.CONFIG_KEY_URL); + if (remoteUri == null) { + String missingKey = ConfigConstants.CONFIG_REMOTE_SECTION + DOT + + remote + DOT + ConfigConstants.CONFIG_KEY_URL; + throw new InvalidConfigurationException(MessageFormat.format( + JGitText.get().missingConfigurationForKey, missingKey)); + } + + // get the name of the branch in the remote repository + // stored in configuration key branch..merge + String remoteBranchName = repoConfig.getString( + ConfigConstants.CONFIG_BRANCH_SECTION, branchName, + ConfigConstants.CONFIG_KEY_MERGE); + if (remoteBranchName == null) { + // check if the branch is configured for pull-rebase + remoteBranchName = repoConfig.getString( + ConfigConstants.CONFIG_BRANCH_SECTION, branchName, + ConfigConstants.CONFIG_KEY_MERGE); + if (remoteBranchName != null) { + // TODO implement pull-rebase + throw new JGitInternalException( + "Pull with rebase is not yet supported"); + } + } + if (remoteBranchName == null) { + String missingKey = ConfigConstants.CONFIG_BRANCH_SECTION + DOT + + branchName + DOT + ConfigConstants.CONFIG_KEY_MERGE; + throw new InvalidConfigurationException(MessageFormat.format( + JGitText.get().missingConfigurationForKey, missingKey)); + } + + if (monitor.isCancelled()) + throw new CanceledException(MessageFormat.format( + JGitText.get().operationCanceled, + JGitText.get().pullTaskName)); + + FetchCommand fetch = new FetchCommand(repo); + fetch.setRemote(remote); + if (monitor != null) + fetch.setProgressMonitor(monitor); + fetch.setTimeout(this.timeout); + + FetchResult fetchRes = fetch.call(); + + monitor.update(1); + + // we check the updates to see which of the updated branches corresponds + // to the remote branch name + + AnyObjectId commitToMerge = null; + + Ref r = fetchRes.getAdvertisedRef(remoteBranchName); + if (r == null) + r = fetchRes.getAdvertisedRef(Constants.R_HEADS + remoteBranchName); + if (r == null) { + // TODO: we should be able to get the mapping also if nothing was + // updated by the fetch; for the time being, use the naming + // convention as fall back + String remoteTrackingBranch = Constants.R_REMOTES + remote + '/' + + branchName; + try { + commitToMerge = repo.resolve(remoteTrackingBranch); + } catch (IOException e) { + throw new JGitInternalException( + JGitText.get().exceptionCaughtDuringExecutionOfPullCommand, + e); + } + + } else + commitToMerge = r.getObjectId(); + + if (monitor.isCancelled()) + throw new CanceledException(MessageFormat.format( + JGitText.get().operationCanceled, + JGitText.get().pullTaskName)); + + MergeCommand merge = new MergeCommand(repo); + merge.include("branch \'" + remoteBranchName + "\' of " + remoteUri, + commitToMerge); + MergeResult mergeRes; + try { + mergeRes = merge.call(); + monitor.update(1); + } catch (NoHeadException e) { + throw new JGitInternalException(e.getMessage(), e); + } catch (ConcurrentRefUpdateException e) { + throw new JGitInternalException(e.getMessage(), e); + } catch (CheckoutConflictException e) { + throw new JGitInternalException(e.getMessage(), e); + } catch (InvalidMergeHeadsException e) { + throw new JGitInternalException(e.getMessage(), e); + } catch (WrongRepositoryStateException e) { + throw new JGitInternalException(e.getMessage(), e); + } catch (NoMessageException e) { + throw new JGitInternalException(e.getMessage(), e); + } + monitor.endTask(); + return new PullResult(fetchRes, remote, mergeRes); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullResult.java new file mode 100644 index 000000000..105b76f81 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullResult.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2010, Mathias Kinzler + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.api; + +import org.eclipse.jgit.transport.FetchResult; + +/** + * Encapsulates the result of a {@link PullCommand} + */ +public class PullResult { + private final FetchResult fetchResult; + + private final MergeResult mergeResult; + + private final String fetchedFrom; + + PullResult(FetchResult fetchResult, String fetchedFrom, + MergeResult mergeResult) { + this.fetchResult = fetchResult; + this.fetchedFrom = fetchedFrom; + this.mergeResult = mergeResult; + } + + /** + * @return the fetch result, or null + */ + public FetchResult getFetchResult() { + return this.fetchResult; + } + + /** + * @return the merge result, or null + */ + public MergeResult getMergeResult() { + return this.mergeResult; + } + + /** + * @return the name of the remote configuration from which fetch was tried, + * or null + */ + public String getFetchedFrom() { + return this.fetchedFrom; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (fetchResult != null) + sb.append(fetchResult.toString()); + else + sb.append("No fetch result"); + sb.append("\n"); + if (mergeResult != null) + sb.append(mergeResult.toString()); + else + sb.append("No merge result"); + return sb.toString(); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/CanceledException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/CanceledException.java new file mode 100644 index 000000000..3ad259783 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/CanceledException.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010, Christian Halstrick + * Copyright (C) 2010, Mathias Kinzler + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v1.0 which accompanies this + * distribution, is reproduced below, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.api.errors; + +/** + * Exception thrown when an operation was canceled + */ +public class CanceledException extends GitAPIException { + private static final long serialVersionUID = 1L; + + /** + * @param message + */ + public CanceledException(String message) { + super(message); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/DetachedHeadException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/DetachedHeadException.java new file mode 100644 index 000000000..705b27eca --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/DetachedHeadException.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2010, Christian Halstrick + * Copyright (C) 2010, Mathias Kinzler + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v1.0 which accompanies this + * distribution, is reproduced below, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.api.errors; + +import org.eclipse.jgit.JGitText; + +/** + * Exception thrown when a command expected a non-detached {@code HEAD} + * reference + */ +public class DetachedHeadException extends GitAPIException { + private static final long serialVersionUID = 1L; + + /** + * The default constructor with a default message + */ + public DetachedHeadException() { + this(JGitText.get().detachedHeadDetected); + } + + /** + * @param message + * @param cause + */ + public DetachedHeadException(String message, Throwable cause) { + super(message, cause); + } + + /** + * @param message + */ + public DetachedHeadException(String message) { + super(message); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/InvalidConfigurationException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/InvalidConfigurationException.java new file mode 100644 index 000000000..cb89e469b --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/InvalidConfigurationException.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010, Christian Halstrick + * Copyright (C) 2010, Mathias Kinzler + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v1.0 which accompanies this + * distribution, is reproduced below, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.api.errors; + +/** + * Exception thrown when a command fails due to an invalid configuration + */ +public class InvalidConfigurationException extends GitAPIException { + private static final long serialVersionUID = 1L; + + /** + * @param message + * @param cause + */ + public InvalidConfigurationException(String message, Throwable cause) { + super(message, cause); + } + + /** + * @param message + */ + public InvalidConfigurationException(String message) { + super(message); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java index 45e77b1c1..e63f4e9d0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java @@ -54,6 +54,9 @@ public class ConfigConstants { /** The "branch" section */ public static final String CONFIG_BRANCH_SECTION = "branch"; + /** The "remote" section */ + public static final String CONFIG_REMOTE_SECTION = "remote"; + /** The "autocrlf" key */ public static final String CONFIG_KEY_AUTOCRLF = "autocrlf"; @@ -81,4 +84,7 @@ public class ConfigConstants { /** The "rebase" key */ public static final String CONFIG_KEY_REBASE = "rebase"; + /** The "url" key */ + public static final String CONFIG_KEY_URL = "url"; + }