summaryrefslogtreecommitdiffstatsabout
diff options
context:
space:
mode:
authorMathias Kinzler2010-10-07 05:37:16 (EDT)
committer Chris Aniszczyk2010-10-08 09:57:28 (EDT)
commitdb55d13f5f4b641a9390076ced6b1775bcfcd6f6 (patch)
treea4b097b6ac803b9c032984bbdbfecd4d88a0d586
parent2160c09dd4f678c5f2f8e730945be637210b39de (diff)
downloadjgit-db55d13f5f4b641a9390076ced6b1775bcfcd6f6.zip
jgit-db55d13f5f4b641a9390076ced6b1775bcfcd6f6.tar.gz
jgit-db55d13f5f4b641a9390076ced6b1775bcfcd6f6.tar.bz2
Add "Pull" commandrefs/changes/96/1696/4
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.<branch name>.remote" and "branch.<branch name>.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 <mathias.kinzler@sap.com> Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java200
-rw-r--r--org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java9
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java260
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/PullResult.java100
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/errors/CanceledException.java53
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/errors/DetachedHeadException.java71
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/errors/InvalidConfigurationException.java61
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java6
10 files changed, 772 insertions, 0 deletions
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 0000000..de75fd5
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
+ * 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 7c5c608..2b0bf51 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 502823a..6019b6a 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 480178b..493019a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
@@ -134,6 +134,15 @@ public class Git {
}
/**
+ * 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
*
* @see <a
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 0000000..b7f09d9
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
+ * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
+ * 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 <a href="http://www.kernel.org/pub/software/scm/git/docs/git-pull.html"
+ * >Git documentation about Pull</a>
+ */
+public class PullCommand extends GitCommand<PullResult> {
+ 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.<branch name>.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.<branch name>.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 0000000..105b76f
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullResult.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
+ * 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 <code>null</code>
+ */
+ public FetchResult getFetchResult() {
+ return this.fetchResult;
+ }
+
+ /**
+ * @return the merge result, or <code>null</code>
+ */
+ public MergeResult getMergeResult() {
+ return this.mergeResult;
+ }
+
+ /**
+ * @return the name of the remote configuration from which fetch was tried,
+ * or <code>null</code>
+ */
+ 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 0000000..3ad2597
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/CanceledException.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
+ * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
+ * 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 0000000..705b27e
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/DetachedHeadException.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
+ * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
+ * 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 0000000..cb89e46
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/InvalidConfigurationException.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
+ * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
+ * 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 45e77b1..e63f4e9 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";
+
}