]> source.dussan.org Git - jgit.git/commitdiff
Add "Pull" command 96/1696/4
authorMathias Kinzler <mathias.kinzler@sap.com>
Thu, 7 Oct 2010 09:37:16 +0000 (11:37 +0200)
committerChris Aniszczyk <caniszczyk@gmail.com>
Fri, 8 Oct 2010 13:57:28 +0000 (08:57 -0500)
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>
org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java [new file with mode: 0644]
org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java [new file with mode: 0644]
org.eclipse.jgit/src/org/eclipse/jgit/api/PullResult.java [new file with mode: 0644]
org.eclipse.jgit/src/org/eclipse/jgit/api/errors/CanceledException.java [new file with mode: 0644]
org.eclipse.jgit/src/org/eclipse/jgit/api/errors/DetachedHeadException.java [new file with mode: 0644]
org.eclipse.jgit/src/org/eclipse/jgit/api/errors/InvalidConfigurationException.java [new file with mode: 0644]
org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.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 (file)
index 0000000..de75fd5
--- /dev/null
@@ -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();
+               }
+       }
+}
index 7c5c60860d9ce62ccfaecdb46f8af1b688eb772d..2b0bf515d26a61572dcc4f83d6f18aaabe7a22ff 100644 (file)
@@ -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
index 502823ad5096d3ed7c2e485f801020a9e3c6b8b9..6019b6a5d4c8f9c668556d2003fb168444c724fa 100644 (file)
@@ -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;
index 480178b9ce86d1f2351ec9bc2b6f28c71c1ed08a..493019a152879ecb4aca2cf478444c447ded93db 100644 (file)
@@ -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 (file)
index 0000000..b7f09d9
--- /dev/null
@@ -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 (file)
index 0000000..105b76f
--- /dev/null
@@ -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 (file)
index 0000000..3ad2597
--- /dev/null
@@ -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 (file)
index 0000000..705b27e
--- /dev/null
@@ -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 (file)
index 0000000..cb89e46
--- /dev/null
@@ -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);
+       }
+}
index 45e77b1c17dccfc8b8a9de6d972ab6f755bfea89..e63f4e9d08341ea5441b359630a7ae8c732fc09f 100644 (file)
@@ -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";
+
 }