summaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit
diff options
context:
space:
mode:
authorChristian Halstrick <christian.halstrick@sap.com>2010-05-19 10:01:25 +0200
committerChristian Halstrick <christian.halstrick@sap.com>2010-05-21 01:49:46 +0200
commit6ca9843f3ebbea152969a8b795efce1d4ff15dbf (patch)
tree516f608960debe427e540912941b300ace2b033a /org.eclipse.jgit
parent3c667b328ae086dcbfe159a22b3c86779a4590e5 (diff)
downloadjgit-6ca9843f3ebbea152969a8b795efce1d4ff15dbf.tar.gz
jgit-6ca9843f3ebbea152969a8b795efce1d4ff15dbf.zip
Added merge support to CommitCommand
The CommitCommand should take care to create a merge commit if the file $GIT_DIR/MERGE_HEAD exists. It should then read the parents for the merge commit out of this file. It should also take care that when commiting a merge and no commit message was specified to read the message from $GIT_DIR/MERGE_MSG. Finally the CommitCommand should remove these files if the commit succeeded. Change-Id: I4e292115085099d5b86546d2021680cb1454266c Signed-off-by: Christian Halstrick <christian.halstrick@sap.com>
Diffstat (limited to 'org.eclipse.jgit')
-rw-r--r--org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java72
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/WrongRepositoryStateException.java55
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java56
6 files changed, 181 insertions, 12 deletions
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
index 4cdf914163..e9ed28a198 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
@@ -27,6 +27,7 @@ blobNotFound=Blob not found: {0}
blobNotFoundForPath=Blob not found: {0} for path: {1}
cannotBeCombined=Cannot be combined.
cannotCombineTreeFilterWithRevFilter=Cannot combine TreeFilter {0} with RefFilter {1}.
+cannotCommitOnARepoWithState=Cannot commit on a repo with state: {0}
cannotCommitWriteTo=Cannot commit write to {0}
cannotConnectPipes=cannot connect pipes
cannotConvertScriptToText=Cannot convert script to text
@@ -137,6 +138,7 @@ errorOccurredDuringUnpackingOnTheRemoteEnd=error occurred during unpacking on th
errorReadingInfoRefs=error reading info/refs
exceptionCaughtDuringExecutionOfCommitCommand=Exception caught during execution of commit 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}
expectedACKNAKFoundEOF=Expected ACK/NAK, found EOF
expectedACKNAKGot=Expected ACK/NAK, got: {0}
expectedBooleanStringValue=Expected boolean string value
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
index fbc17aad8e..a7c2e685c1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
@@ -87,6 +87,7 @@ public class JGitText extends TranslationBundle {
/***/ public String blobNotFoundForPath;
/***/ public String cannotBeCombined;
/***/ public String cannotCombineTreeFilterWithRevFilter;
+ /***/ public String cannotCommitOnARepoWithState;
/***/ public String cannotCommitWriteTo;
/***/ public String cannotConnectPipes;
/***/ public String cannotConvertScriptToText;
@@ -197,6 +198,7 @@ public class JGitText extends TranslationBundle {
/***/ public String errorReadingInfoRefs;
/***/ public String exceptionCaughtDuringExecutionOfCommitCommand;
/***/ public String exceptionOccuredDuringAddingOfOptionToALogCommand;
+ /***/ public String exceptionOccuredDuringReadingOfGIT_DIR;
/***/ public String expectedACKNAKFoundEOF;
/***/ public String expectedACKNAKGot;
/***/ public String expectedBooleanStringValue;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
index 542c821451..eef952e7cd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
@@ -42,8 +42,11 @@
*/
package org.eclipse.jgit.api;
+import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
+import java.util.LinkedList;
+import java.util.List;
import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.dircache.DirCache;
@@ -57,6 +60,7 @@ import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.RepositoryState;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -77,6 +81,12 @@ public class CommitCommand extends GitCommand<RevCommit> {
private String message;
/**
+ * parents this commit should have. The current HEAD will be in this list
+ * and also all commits mentioned in .git/MERGE_HEAD
+ */
+ private List<ObjectId> parents = new LinkedList<ObjectId>();
+
+ /**
* @param repo
*/
protected CommitCommand(Repository repo) {
@@ -96,6 +106,8 @@ public class CommitCommand extends GitCommand<RevCommit> {
* when called without specifying a commit message
* @throws UnmergedPathException
* when the current index contained unmerged pathes (conflicts)
+ * @throws WrongRepositoryStateException
+ * when repository is not in the right state for committing
* @throws JGitInternalException
* a low-level exception of JGit has occurred. The original
* exception can be retrieved by calling
@@ -106,9 +118,14 @@ public class CommitCommand extends GitCommand<RevCommit> {
*/
public RevCommit call() throws NoHeadException, NoMessageException,
UnmergedPathException, ConcurrentRefUpdateException,
- JGitInternalException {
+ JGitInternalException, WrongRepositoryStateException {
checkCallable();
- processOptions();
+
+ RepositoryState state = repo.getRepositoryState();
+ if (!state.canCommit())
+ throw new WrongRepositoryStateException(MessageFormat.format(
+ JGitText.get().cannotCommitOnARepoWithState, state.name()));
+ processOptions(state);
try {
Ref head = repo.getRef(Constants.HEAD);
@@ -117,7 +134,9 @@ public class CommitCommand extends GitCommand<RevCommit> {
JGitText.get().commitOnRepoWithoutHEADCurrentlyNotSupported);
// determine the current HEAD and the commit it is referring to
- ObjectId parentID = repo.resolve(Constants.HEAD + "^{commit}");
+ ObjectId headId = repo.resolve(Constants.HEAD + "^{commit}");
+ if (headId != null)
+ parents.add(0, headId);
// lock the index
DirCache index = DirCache.lock(repo);
@@ -134,8 +153,8 @@ public class CommitCommand extends GitCommand<RevCommit> {
commit.setCommitter(committer);
commit.setAuthor(author);
commit.setMessage(message);
- if (parentID != null)
- commit.setParentIds(new ObjectId[] { parentID });
+
+ commit.setParentIds(parents.toArray(new ObjectId[]{}));
commit.setTreeId(indexTreeId);
ObjectId commitId = repoWriter.writeCommit(commit);
@@ -145,12 +164,20 @@ public class CommitCommand extends GitCommand<RevCommit> {
ru.setRefLogMessage("commit : " + revCommit.getShortMessage(),
false);
- ru.setExpectedOldObjectId(parentID);
+ ru.setExpectedOldObjectId(headId);
Result rc = ru.update();
switch (rc) {
case NEW:
case FAST_FORWARD:
setCallable(false);
+ if (state == RepositoryState.MERGING_RESOLVED) {
+ // Commit was successful. Now delete the files
+ // used for merge commits
+ new File(repo.getDirectory(), Constants.MERGE_HEAD)
+ .delete();
+ new File(repo.getDirectory(), Constants.MERGE_MSG)
+ .delete();
+ }
return revCommit;
case REJECTED:
case LOCK_FAILURE:
@@ -179,18 +206,41 @@ public class CommitCommand extends GitCommand<RevCommit> {
* Sets default values for not explicitly specified options. Then validates
* that all required data has been provided.
*
+ * @param state
+ * the state of the repository we are working on
+ *
* @throws NoMessageException
* if the commit message has not been specified
*/
- private void processOptions() throws NoMessageException {
- if (message == null)
- // as long as we don't suppport -C option we have to have
- // an explicit message
- throw new NoMessageException(JGitText.get().commitMessageNotSpecified);
+ private void processOptions(RepositoryState state) throws NoMessageException {
if (committer == null)
committer = new PersonIdent(repo);
if (author == null)
author = committer;
+
+ // when doing a merge commit parse MERGE_HEAD and MERGE_MSG files
+ if (state == RepositoryState.MERGING_RESOLVED) {
+ try {
+ parents = repo.readMergeHeads();
+ } catch (IOException e) {
+ throw new JGitInternalException(MessageFormat.format(
+ JGitText.get().exceptionOccuredDuringReadingOfGIT_DIR,
+ Constants.MERGE_HEAD, e));
+ }
+ if (message == null) {
+ try {
+ message = repo.readMergeCommitMsg();
+ } catch (IOException e) {
+ throw new JGitInternalException(MessageFormat.format(
+ JGitText.get().exceptionOccuredDuringReadingOfGIT_DIR,
+ Constants.MERGE_MSG, e));
+ }
+ }
+ }
+ if (message == null)
+ // as long as we don't suppport -C option we have to have
+ // an explicit message
+ throw new NoMessageException(JGitText.get().commitMessageNotSpecified);
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/WrongRepositoryStateException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/WrongRepositoryStateException.java
new file mode 100644
index 0000000000..833cf8d6ed
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/WrongRepositoryStateException.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2010, Christian Halstrick <christian.halstrick@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;
+
+/**
+ * Exception thrown when the state of the repository doesn't allow the execution
+ * of a certain command. E.g. when a CommitCommand should be executed on a
+ * repository with unresolved conflicts this exception will be thrown.
+ */
+public class WrongRepositoryStateException extends GitAPIException {
+ private static final long serialVersionUID = 1L;
+
+ WrongRepositoryStateException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ WrongRepositoryStateException(String message) {
+ super(message);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
index 37836f3246..03ab629790 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
@@ -518,6 +518,12 @@ public final class Constants {
CHARSET = Charset.forName(CHARACTER_ENCODING);
}
+ /** name of the file containing the commit msg for a merge commit */
+ public static final String MERGE_MSG = "MERGE_MSG";
+
+ /** name of the file containing the IDs of the parents of a merge commit */
+ public static final String MERGE_HEAD = "MERGE_HEAD";
+
private Constants() {
// Hide the default constructor
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
index e4d857bf8d..233cecf310 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -47,6 +47,7 @@
package org.eclipse.jgit.lib;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
@@ -61,12 +62,14 @@ import java.util.Set;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;
-import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.RevisionSyntaxException;
import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.IO;
+import org.eclipse.jgit.util.RawParseUtils;
import org.eclipse.jgit.util.SystemReader;
/**
@@ -1338,4 +1341,55 @@ public class Repository {
return new ReflogReader(this, ref.getName());
return null;
}
+
+ /**
+ * Return the information stored in the file $GIT_DIR/MERGE_MSG. In this
+ * file operations triggering a merge will store a template for the commit
+ * message of the merge commit.
+ *
+ * @return a String containing the content of the MERGE_MSG file or
+ * {@code null} if this file doesn't exist
+ * @throws IOException
+ */
+ public String readMergeCommitMsg() throws IOException {
+ File mergeMsgFile = new File(gitDir, Constants.MERGE_MSG);
+ try {
+ return new String(IO.readFully(mergeMsgFile));
+ } catch (FileNotFoundException e) {
+ // MERGE_MSG file has disappeared in the meantime
+ // ignore it
+ return null;
+ }
+ }
+
+ /**
+ * Return the information stored in the file $GIT_DIR/MERGE_HEAD. In this
+ * file operations triggering a merge will store the IDs of all heads which
+ * should be merged together with HEAD.
+ *
+ * @return a list of {@link Commit}s which IDs are listed in the MERGE_HEAD
+ * file or {@code null} if this file doesn't exist. Also if the file
+ * exists but is empty {@code null} will be returned
+ * @throws IOException
+ */
+ public List<ObjectId> readMergeHeads() throws IOException {
+ File mergeHeadFile = new File(gitDir, Constants.MERGE_HEAD);
+ byte[] raw;
+ try {
+ raw = IO.readFully(mergeHeadFile);
+ } catch (FileNotFoundException notFound) {
+ return new LinkedList<ObjectId>();
+ }
+
+ if (raw.length == 0)
+ throw new IOException("MERGE_HEAD file empty: " + mergeHeadFile);
+
+ LinkedList<ObjectId> heads = new LinkedList<ObjectId>();
+ for (int p = 0; p < raw.length;) {
+ heads.add(ObjectId.fromString(raw, p));
+ p = RawParseUtils
+ .nextLF(raw, p + Constants.OBJECT_ID_STRING_LENGTH);
+ }
+ return heads;
+ }
}