Bundle-Vendor: %provider_name
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Import-Package: org.eclipse.jgit.api;version="[3.7.0,3.8.0)",
+ org.eclipse.jgit.api.errors;version="[3.7.0,3.8.0)",
org.eclipse.jgit.diff;version="[3.7.0,3.8.0)",
org.eclipse.jgit.dircache;version="[3.7.0,3.8.0)",
org.eclipse.jgit.internal.storage.file;version="3.7.0",
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.RejectCommitException;
import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.junit.Assume;
res.getStatus());
}
+ @Test
+ public void testPreCommitHook() throws Exception {
+ assumeSupportedPlatform();
+
+ Hook h = Hook.PRE_COMMIT;
+ writeHookFile(h.getName(),
+ "#!/bin/sh\necho \"test\"\n\necho 1>&2 \"stderr\"\nexit 1");
+ Git git = Git.wrap(db);
+ String path = "a.txt";
+ writeTrashFile(path, "content");
+ git.add().addFilepattern(path).call();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try {
+ git.commit().setMessage("commit")
+ .setHookOutputStream(new PrintStream(out)).call();
+ fail("expected pre-commit hook to abort commit");
+ } catch (RejectCommitException e) {
+ assertEquals("unexpected error message from pre-commit hook",
+ "Commit rejected by \"pre-commit\" hook.\nstderr\n",
+ e.getMessage());
+ assertEquals("unexpected output from pre-commit hook", "test\n",
+ out.toString());
+ } catch (Throwable e) {
+ fail("unexpected exception thrown by pre-commit hook: " + e);
+ }
+ }
+
private File writeHookFile(final String name, final String data)
throws IOException {
File path = new File(db.getWorkTree() + "/.git/hooks/", name);
commitMessageNotSpecified=commit message not specified
commitOnRepoWithoutHEADCurrentlyNotSupported=Commit on repo without HEAD currently not supported
commitAmendOnInitialNotPossible=Amending is not possible on initial commit.
+commitRejectedByHook=Commit rejected by "{0}" hook.\n{1}
compressingObjects=Compressing objects
connectionFailed=connection failed
connectionTimeOut=Connection time out: {0}
.setMessage(srcCommit.getFullMessage())
.setReflogComment(reflogPrefix + " " //$NON-NLS-1$
+ srcCommit.getShortMessage())
- .setAuthor(srcCommit.getAuthorIdent()).call();
+ .setAuthor(srcCommit.getAuthorIdent())
+ .setNoVerify(true).call();
cherryPickedRefs.add(src);
} else {
if (merger.failed())
*/
package org.eclipse.jgit.api;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.PrintStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import org.eclipse.jgit.api.errors.NoFilepatternException;
import org.eclipse.jgit.api.errors.NoHeadException;
import org.eclipse.jgit.api.errors.NoMessageException;
+import org.eclipse.jgit.api.errors.RejectCommitException;
import org.eclipse.jgit.api.errors.UnmergedPathsException;
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.ChangeIdUtil;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.Hook;
+import org.eclipse.jgit.util.ProcessResult;
/**
* A class used to execute a {@code Commit} command. It has setters for all
private String reflogComment;
+ /**
+ * Setting this option bypasses the {@link Hook#PRE_COMMIT pre-commit} and
+ * {@link Hook#COMMIT_MSG commit-msg} hooks.
+ */
+ private boolean noVerify;
+
+ private PrintStream hookOutRedirect;
+
/**
* @param repo
*/
protected CommitCommand(Repository repo) {
super(repo);
+ hookOutRedirect = System.out;
}
/**
* else
* @throws WrongRepositoryStateException
* when repository is not in the right state for committing
+ * @throws RejectCommitException
+ * if there are either pre-commit or commit-msg hooks present in
+ * the repository and at least one of them rejects the commit.
*/
public RevCommit call() throws GitAPIException, NoHeadException,
NoMessageException, UnmergedPathsException,
- ConcurrentRefUpdateException,
- WrongRepositoryStateException {
+ ConcurrentRefUpdateException, WrongRepositoryStateException,
+ RejectCommitException {
checkCallable();
Collections.sort(only);
throw new WrongRepositoryStateException(MessageFormat.format(
JGitText.get().cannotCommitOnARepoWithState,
state.name()));
+
+ if (!noVerify) {
+ final ByteArrayOutputStream errorByteArray = new ByteArrayOutputStream();
+ final PrintStream hookErrRedirect = new PrintStream(
+ errorByteArray);
+ ProcessResult preCommitHookResult = FS.DETECTED.runIfPresent(
+ repo, Hook.PRE_COMMIT, new String[0], hookOutRedirect,
+ hookErrRedirect, null);
+ if (preCommitHookResult.getStatus() == ProcessResult.Status.OK
+ && preCommitHookResult.getExitCode() != 0) {
+ String errorMessage = MessageFormat.format(
+ JGitText.get().commitRejectedByHook, Hook.PRE_COMMIT.getName(),
+ errorByteArray.toString());
+ throw new RejectCommitException(errorMessage);
+ }
+ }
+
processOptions(state, rw);
if (all && !repo.isBare() && repo.getWorkTree() != null) {
return this;
}
+ /**
+ * Sets the {@link #noVerify} option on this commit command.
+ * <p>
+ * Both the {@link Hook#PRE_COMMIT pre-commit} and {@link Hook#COMMIT_MSG
+ * commit-msg} hooks can block a commit by their return value; setting this
+ * option to <code>true</code> will bypass these two hooks.
+ * </p>
+ *
+ * @param noVerify
+ * Whether this commit should be verified by the pre-commit and
+ * commit-msg hooks.
+ * @return {@code this}
+ * @since 3.7
+ */
+ public CommitCommand setNoVerify(boolean noVerify) {
+ this.noVerify = noVerify;
+ return this;
+ }
+
+ /**
+ * Set the output stream for hook scripts executed by this command. If not
+ * set it defaults to {@code System.out}.
+ *
+ * @param hookStdOut
+ * the output stream for hook scripts executed by this command
+ * @return {@code this}
+ * @since 3.7
+ */
+ public CommitCommand setHookOutputStream(PrintStream hookStdOut) {
+ this.hookOutRedirect = hookStdOut;
+ return this;
+ }
}
String newMessage = interactiveHandler
.modifyCommitMessage(oldMessage);
newHead = new Git(repo).commit().setMessage(newMessage)
- .setAmend(true).call();
+ .setAmend(true).setNoVerify(true).call();
return null;
case EDIT:
rebaseState.createFile(AMEND, commitToPick.name());
}
retNewHead = new Git(repo).commit()
.setMessage(stripCommentLines(commitMessage))
- .setAmend(true).call();
+ .setAmend(true).setNoVerify(true).call();
rebaseState.getFile(MESSAGE_SQUASH).delete();
rebaseState.getFile(MESSAGE_FIXUP).delete();
} else {
// Next step is either Squash or Fixup
- retNewHead = new Git(repo).commit()
- .setMessage(commitMessage).setAmend(true)
- .call();
+ retNewHead = new Git(repo).commit().setMessage(commitMessage)
+ .setAmend(true).setNoVerify(true).call();
}
return retNewHead;
}
--- /dev/null
+/*
+ * Copyright (C) 2015 Obeo.
+ * 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 commit is rejected by a hook (either
+ * {@link org.eclipse.jgit.util.Hook#PRE_COMMIT pre-commit} or
+ * {@link org.eclipse.jgit.util.Hook#COMMIT_MSG commit-msg}).
+ *
+ * @since 3.7
+ */
+public class RejectCommitException extends GitAPIException {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * @param message
+ */
+ public RejectCommitException(String message) {
+ super(message);
+ }
+}
/***/ public String commitMessageNotSpecified;
/***/ public String commitOnRepoWithoutHEADCurrentlyNotSupported;
/***/ public String commitAmendOnInitialNotPossible;
+ /***/ public String commitRejectedByHook;
/***/ public String compressingObjects;
/***/ public String connectionFailed;
/***/ public String connectionTimeOut;