| /* |
| * 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; |
| |
| import java.io.IOException; |
| |
| import org.eclipse.jgit.dircache.DirCache; |
| import org.eclipse.jgit.errors.UnmergedPathException; |
| import org.eclipse.jgit.lib.Commit; |
| import org.eclipse.jgit.lib.Constants; |
| import org.eclipse.jgit.lib.ObjectId; |
| import org.eclipse.jgit.lib.ObjectWriter; |
| import org.eclipse.jgit.lib.PersonIdent; |
| 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.revwalk.RevCommit; |
| import org.eclipse.jgit.revwalk.RevWalk; |
| |
| /** |
| * A class used to execute a {@code Commit} command. It has setters for all |
| * supported options and arguments of this command and a {@link #call()} method |
| * to finally execute the command. |
| * |
| * @see <a |
| * href="http://www.kernel.org/pub/software/scm/git/docs/git-commit.html" |
| * >Git documentation about Commit</a> |
| */ |
| public class CommitCommand extends GitCommand<RevCommit> { |
| private PersonIdent author; |
| |
| private PersonIdent committer; |
| |
| private String message; |
| |
| /** |
| * @param repo |
| */ |
| protected CommitCommand(Repository repo) { |
| super(repo); |
| } |
| |
| /** |
| * Executes the {@code commit} command with all the options and parameters |
| * collected by the setter methods of this class. Each instance of this |
| * class should only be used for one invocation of the command (means: one |
| * call to {@link #call()}) |
| * |
| * @return a {@link Commit} object representing the successful commit |
| * @throws NoHeadException |
| * when called on a git repo without a HEAD reference |
| * @throws NoMessageException |
| * when called without specifying a commit message |
| * @throws UnmergedPathException |
| * when the current index contained unmerged pathes (conflicts) |
| * @throws JGitInternalException |
| * a low-level exception of JGit has occurred. The original |
| * exception can be retrieved by calling |
| * {@link Exception#getCause()}. Expect only |
| * {@code IOException's} to be wrapped. Subclasses of |
| * {@link IOException} (e.g. {@link UnmergedPathException}) are |
| * typically not wrapped here but thrown as original exception |
| */ |
| public RevCommit call() throws NoHeadException, NoMessageException, |
| UnmergedPathException, ConcurrentRefUpdateException, |
| JGitInternalException { |
| checkCallable(); |
| processOptions(); |
| |
| try { |
| Ref head = repo.getRef(Constants.HEAD); |
| if (head == null) |
| throw new NoHeadException( |
| "Commit on repo without HEAD currently not supported"); |
| |
| // determine the current HEAD and the commit it is referring to |
| ObjectId parentID = repo.resolve(Constants.HEAD + "^{commit}"); |
| |
| // lock the index |
| DirCache index = DirCache.lock(repo); |
| try { |
| ObjectWriter repoWriter = new ObjectWriter(repo); |
| |
| // Write the index as tree to the object database. This may fail |
| // for example when the index contains unmerged pathes |
| // (unresolved conflicts) |
| ObjectId indexTreeId = index.writeTree(repoWriter); |
| |
| // Create a Commit object, populate it and write it |
| Commit commit = new Commit(repo); |
| commit.setCommitter(committer); |
| commit.setAuthor(author); |
| commit.setMessage(message); |
| if (parentID != null) |
| commit.setParentIds(new ObjectId[] { parentID }); |
| commit.setTreeId(indexTreeId); |
| ObjectId commitId = repoWriter.writeCommit(commit); |
| |
| RevCommit revCommit = new RevWalk(repo).parseCommit(commitId); |
| RefUpdate ru = repo.updateRef(Constants.HEAD); |
| ru.setNewObjectId(commitId); |
| ru.setRefLogMessage("commit : " + revCommit.getShortMessage(), |
| false); |
| |
| ru.setExpectedOldObjectId(parentID); |
| Result rc = ru.update(); |
| switch (rc) { |
| case NEW: |
| case FAST_FORWARD: |
| setCallable(false); |
| return revCommit; |
| case REJECTED: |
| case LOCK_FAILURE: |
| throw new ConcurrentRefUpdateException( |
| "Could lock HEAD during commit", ru.getRef(), rc); |
| default: |
| throw new JGitInternalException( |
| "Updating the ref " |
| + Constants.HEAD |
| + " to " |
| + commitId.toString() |
| + " failed. ReturnCode from RefUpdate.update() was " |
| + rc); |
| } |
| } finally { |
| index.unlock(); |
| } |
| } catch (UnmergedPathException e) { |
| // since UnmergedPathException is a subclass of IOException |
| // which should not be wrapped by a JGitInternalException we |
| // have to catch and re-throw it here |
| throw e; |
| } catch (IOException e) { |
| throw new JGitInternalException( |
| "Exception caught during execution of commit command", e); |
| } |
| } |
| |
| /** |
| * Sets default values for not explicitly specified options. Then validates |
| * that all required data has been provided. |
| * |
| * @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("commit message not specified"); |
| if (committer == null) |
| committer = new PersonIdent(repo); |
| if (author == null) |
| author = committer; |
| } |
| |
| /** |
| * @param message |
| * the commit message used for the {@code commit} |
| * @return {@code this} |
| */ |
| public CommitCommand setMessage(String message) { |
| checkCallable(); |
| this.message = message; |
| return this; |
| } |
| |
| /** |
| * @return the commit message used for the <code>commit</code> |
| */ |
| public String getMessage() { |
| return message; |
| } |
| |
| /** |
| * Sets the committer for this {@code commit}. If no committer is explicitly |
| * specified because this method is never called or called with {@code null} |
| * value then the committer will be deduced from config info in repository, |
| * with current time. |
| * |
| * @param committer |
| * the committer used for the {@code commit} |
| * @return {@code this} |
| */ |
| public CommitCommand setCommitter(PersonIdent committer) { |
| checkCallable(); |
| this.committer = committer; |
| return this; |
| } |
| |
| /** |
| * Sets the committer for this {@code commit}. If no committer is explicitly |
| * specified because this method is never called or called with {@code null} |
| * value then the committer will be deduced from config info in repository, |
| * with current time. |
| * |
| * @param name |
| * the name of the committer used for the {@code commit} |
| * @param email |
| * the email of the committer used for the {@code commit} |
| * @return {@code this} |
| */ |
| public CommitCommand setCommitter(String name, String email) { |
| checkCallable(); |
| return setCommitter(new PersonIdent(name, email)); |
| } |
| |
| /** |
| * @return the committer used for the {@code commit}. If no committer was |
| * specified {@code null} is returned and the default |
| * {@link PersonIdent} of this repo is used during execution of the |
| * command |
| */ |
| public PersonIdent getCommitter() { |
| return committer; |
| } |
| |
| /** |
| * Sets the author for this {@code commit}. If no author is explicitly |
| * specified because this method is never called or called with {@code null} |
| * value then the author will be set to the committer. |
| * |
| * @param author |
| * the author used for the {@code commit} |
| * @return {@code this} |
| */ |
| public CommitCommand setAuthor(PersonIdent author) { |
| checkCallable(); |
| this.author = author; |
| return this; |
| } |
| |
| /** |
| * Sets the author for this {@code commit}. If no author is explicitly |
| * specified because this method is never called or called with {@code null} |
| * value then the author will be set to the committer. |
| * |
| * @param name |
| * the name of the author used for the {@code commit} |
| * @param email |
| * the email of the author used for the {@code commit} |
| * @return {@code this} |
| */ |
| public CommitCommand setAuthor(String name, String email) { |
| checkCallable(); |
| return setAuthor(new PersonIdent(name, email)); |
| } |
| |
| /** |
| * @return the author used for the {@code commit}. If no author was |
| * specified {@code null} is returned and the default |
| * {@link PersonIdent} of this repo is used during execution of the |
| * command |
| */ |
| public PersonIdent getAuthor() { |
| return author; |
| } |
| } |