Hooks are now obtained via a convenient API like git commands, and callers don't have to check for their existence. The pre-commit hook has been updated accordingly. Change-Id: I3383ffb10e2f3b588d7367b9139b606ec7f62758 Signed-off-by: Laurent Delaigue <laurent.delaigue@obeo.fr> Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>tags/v4.0.0.201503231230-m1
@@ -9,6 +9,7 @@ Import-Package: org.eclipse.jgit.api;version="[4.0.0,4.1.0)", | |||
org.eclipse.jgit.api.errors;version="[4.0.0,4.1.0)", | |||
org.eclipse.jgit.diff;version="[4.0.0,4.1.0)", | |||
org.eclipse.jgit.dircache;version="[4.0.0,4.1.0)", | |||
org.eclipse.jgit.hooks;version="[4.0.0,4.1.0)", | |||
org.eclipse.jgit.internal.storage.file;version="4.0.0", | |||
org.eclipse.jgit.junit;version="[4.0.0,4.1.0)", | |||
org.eclipse.jgit.lib;version="[4.0.0,4.1.0)", |
@@ -52,7 +52,8 @@ 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.api.errors.AbortedByHookException; | |||
import org.eclipse.jgit.hooks.PreCommitHook; | |||
import org.eclipse.jgit.junit.JGitTestUtil; | |||
import org.eclipse.jgit.junit.RepositoryTestCase; | |||
import org.junit.Assume; | |||
@@ -64,25 +65,25 @@ public class HookTest extends RepositoryTestCase { | |||
public void testFindHook() throws Exception { | |||
assumeSupportedPlatform(); | |||
Hook h = Hook.PRE_COMMIT; | |||
assertNull("no hook should be installed", FS.DETECTED.findHook(db, h)); | |||
File hookFile = writeHookFile(h.getName(), | |||
assertNull("no hook should be installed", | |||
FS.DETECTED.findHook(db, PreCommitHook.NAME)); | |||
File hookFile = writeHookFile(PreCommitHook.NAME, | |||
"#!/bin/bash\necho \"test $1 $2\""); | |||
assertEquals("exected to find pre-commit hook", hookFile, | |||
FS.DETECTED.findHook(db, h)); | |||
assertEquals("expected to find pre-commit hook", hookFile, | |||
FS.DETECTED.findHook(db, PreCommitHook.NAME)); | |||
} | |||
@Test | |||
public void testRunHook() throws Exception { | |||
assumeSupportedPlatform(); | |||
Hook h = Hook.PRE_COMMIT; | |||
writeHookFile( | |||
h.getName(), | |||
writeHookFile(PreCommitHook.NAME, | |||
"#!/bin/sh\necho \"test $1 $2\"\nread INPUT\necho $INPUT\necho 1>&2 \"stderr\""); | |||
ByteArrayOutputStream out = new ByteArrayOutputStream(); | |||
ByteArrayOutputStream err = new ByteArrayOutputStream(); | |||
ProcessResult res = FS.DETECTED.runIfPresent(db, h, new String[] { | |||
ProcessResult res = FS.DETECTED.runHookIfPresent(db, | |||
PreCommitHook.NAME, | |||
new String[] { | |||
"arg1", "arg2" }, | |||
new PrintStream(out), new PrintStream(err), "stdin"); | |||
assertEquals("unexpected hook output", "test arg1 arg2\nstdin\n", | |||
@@ -95,11 +96,10 @@ public class HookTest extends RepositoryTestCase { | |||
} | |||
@Test | |||
public void testPreCommitHook() throws Exception { | |||
public void testFailedPreCommitHookBlockCommit() throws Exception { | |||
assumeSupportedPlatform(); | |||
Hook h = Hook.PRE_COMMIT; | |||
writeHookFile(h.getName(), | |||
writeHookFile(PreCommitHook.NAME, | |||
"#!/bin/sh\necho \"test\"\n\necho 1>&2 \"stderr\"\nexit 1"); | |||
Git git = Git.wrap(db); | |||
String path = "a.txt"; | |||
@@ -110,14 +110,12 @@ public class HookTest extends RepositoryTestCase { | |||
git.commit().setMessage("commit") | |||
.setHookOutputStream(new PrintStream(out)).call(); | |||
fail("expected pre-commit hook to abort commit"); | |||
} catch (RejectCommitException e) { | |||
} catch (AbortedByHookException e) { | |||
assertEquals("unexpected error message from pre-commit hook", | |||
"Commit rejected by \"pre-commit\" hook.\nstderr\n", | |||
"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); | |||
} | |||
} | |||
@@ -352,10 +352,10 @@ public class FS_POSIX_Java7 extends FS_POSIX { | |||
* @since 3.7 | |||
*/ | |||
@Override | |||
public File findHook(Repository repository, Hook hook) { | |||
public File findHook(Repository repository, String hookName) { | |||
final File gitdir = repository.getDirectory(); | |||
final Path hookPath = gitdir.toPath().resolve(Constants.HOOKS) | |||
.resolve(hook.getName()); | |||
.resolve(hookName); | |||
if (Files.isExecutable(hookPath)) | |||
return hookPath.toFile(); | |||
return null; |
@@ -145,10 +145,10 @@ public class FS_Win32_Java7Cygwin extends FS_Win32_Cygwin { | |||
* @since 3.7 | |||
*/ | |||
@Override | |||
public File findHook(Repository repository, Hook hook) { | |||
public File findHook(Repository repository, String hookName) { | |||
final File gitdir = repository.getDirectory(); | |||
final Path hookPath = gitdir.toPath().resolve(Constants.HOOKS) | |||
.resolve(hook.getName()); | |||
.resolve(hookName); | |||
if (Files.isExecutable(hookPath)) | |||
return hookPath.toFile(); | |||
return null; |
@@ -53,6 +53,7 @@ Export-Package: org.eclipse.jgit.api;version="4.0.0"; | |||
org.eclipse.jgit.lib, | |||
org.eclipse.jgit.revwalk", | |||
org.eclipse.jgit.gitrepo.internal;version="4.0.0";x-internal:=true, | |||
org.eclipse.jgit.hooks;version="4.0.0", | |||
org.eclipse.jgit.ignore;version="4.0.0", | |||
org.eclipse.jgit.ignore.internal;version="4.0.0";x-friends:="org.eclipse.jgit.test", | |||
org.eclipse.jgit.internal;version="4.0.0";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test", |
@@ -99,12 +99,12 @@ checkoutUnexpectedResult=Checkout returned unexpected result {0} | |||
classCastNotA=Not a {0} | |||
cloneNonEmptyDirectory=Destination path "{0}" already exists and is not an empty directory | |||
collisionOn=Collision on {0} | |||
commandRejectedByHook=Rejected by "{0}" hook.\n{1} | |||
commandWasCalledInTheWrongState=Command {0} was called in the wrong state | |||
commitAlreadyExists=exists {0} | |||
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} |
@@ -42,7 +42,6 @@ | |||
*/ | |||
package org.eclipse.jgit.api; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.PrintStream; | |||
@@ -52,13 +51,13 @@ import java.util.Collections; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import org.eclipse.jgit.api.errors.AbortedByHookException; | |||
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException; | |||
import org.eclipse.jgit.api.errors.GitAPIException; | |||
import org.eclipse.jgit.api.errors.JGitInternalException; | |||
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; | |||
@@ -67,6 +66,7 @@ import org.eclipse.jgit.dircache.DirCacheBuilder; | |||
import org.eclipse.jgit.dircache.DirCacheEntry; | |||
import org.eclipse.jgit.dircache.DirCacheIterator; | |||
import org.eclipse.jgit.errors.UnmergedPathException; | |||
import org.eclipse.jgit.hooks.Hooks; | |||
import org.eclipse.jgit.internal.JGitText; | |||
import org.eclipse.jgit.lib.CommitBuilder; | |||
import org.eclipse.jgit.lib.Constants; | |||
@@ -87,9 +87,6 @@ import org.eclipse.jgit.treewalk.CanonicalTreeParser; | |||
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 | |||
@@ -126,8 +123,7 @@ public class CommitCommand extends GitCommand<RevCommit> { | |||
private String reflogComment; | |||
/** | |||
* Setting this option bypasses the {@link Hook#PRE_COMMIT pre-commit} and | |||
* {@link Hook#COMMIT_MSG commit-msg} hooks. | |||
* Setting this option bypasses the pre-commit and commit-msg hooks. | |||
*/ | |||
private boolean noVerify; | |||
@@ -138,7 +134,6 @@ public class CommitCommand extends GitCommand<RevCommit> { | |||
*/ | |||
protected CommitCommand(Repository repo) { | |||
super(repo); | |||
hookOutRedirect = System.out; | |||
} | |||
/** | |||
@@ -159,14 +154,14 @@ public class CommitCommand extends GitCommand<RevCommit> { | |||
* else | |||
* @throws WrongRepositoryStateException | |||
* when repository is not in the right state for committing | |||
* @throws RejectCommitException | |||
* @throws AbortedByHookException | |||
* if there are either pre-commit or commit-msg hooks present in | |||
* the repository and at least one of them rejects the commit. | |||
* the repository and one of them rejects the commit. | |||
*/ | |||
public RevCommit call() throws GitAPIException, NoHeadException, | |||
NoMessageException, UnmergedPathsException, | |||
ConcurrentRefUpdateException, WrongRepositoryStateException, | |||
RejectCommitException { | |||
AbortedByHookException { | |||
checkCallable(); | |||
Collections.sort(only); | |||
@@ -180,19 +175,7 @@ public class CommitCommand extends GitCommand<RevCommit> { | |||
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); | |||
} | |||
Hooks.preCommit(repo, hookOutRedirect).call(); | |||
} | |||
processOptions(state, rw); | |||
@@ -771,9 +754,9 @@ public class CommitCommand extends GitCommand<RevCommit> { | |||
/** | |||
* 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. | |||
* Both the pre-commit and 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 |
@@ -0,0 +1,104 @@ | |||
/* | |||
* 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; | |||
import java.text.MessageFormat; | |||
import org.eclipse.jgit.internal.JGitText; | |||
/** | |||
* Exception thrown when a hook returns a process result with a value different | |||
* from 0. It is up to the caller to decide whether this should block execution | |||
* or not. | |||
* | |||
* @since 4.0 | |||
*/ | |||
public class AbortedByHookException extends GitAPIException { | |||
private static final long serialVersionUID = 1L; | |||
/** | |||
* The hook that caused this exception. | |||
*/ | |||
private final String hookName; | |||
/** | |||
* The process result. | |||
*/ | |||
private final int returnCode; | |||
/** | |||
* @param message | |||
* The error details. | |||
* @param hookName | |||
* The name of the hook that interrupted the command, must not be | |||
* null. | |||
* @param returnCode | |||
* The return code of the hook process that has been run. | |||
*/ | |||
public AbortedByHookException(String message, String hookName, | |||
int returnCode) { | |||
super(message); | |||
this.hookName = hookName; | |||
this.returnCode = returnCode; | |||
} | |||
/** | |||
* @return the type of the hook that interrupted the git command. | |||
*/ | |||
public String getHookName() { | |||
return hookName; | |||
} | |||
/** | |||
* @return the hook process result. | |||
*/ | |||
public int getReturnCode() { | |||
return returnCode; | |||
} | |||
@Override | |||
public String getMessage() { | |||
return MessageFormat.format(JGitText.get().commandRejectedByHook, | |||
hookName, super.getMessage()); | |||
} | |||
} |
@@ -0,0 +1,159 @@ | |||
/* | |||
* 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.hooks; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
import java.io.PrintStream; | |||
import java.util.concurrent.Callable; | |||
import org.eclipse.jgit.api.errors.AbortedByHookException; | |||
import org.eclipse.jgit.lib.Repository; | |||
import org.eclipse.jgit.util.FS; | |||
import org.eclipse.jgit.util.ProcessResult; | |||
/** | |||
* Git can fire off custom scripts when certain important actions occur. These | |||
* custom scripts are called "hooks". There are two groups of hooks: client-side | |||
* (that run on local operations such as committing and merging), and | |||
* server-side (that run on network operations such as receiving pushed | |||
* commits). This is the abstract super-class of the different hook | |||
* implementations in JGit. | |||
* | |||
* @param <T> | |||
* the return type which is expected from {@link #call()} | |||
* @see <a href="http://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks">Git | |||
* Hooks on the git-scm official site</a> | |||
* @since 4.0 | |||
*/ | |||
abstract class GitHook<T> implements Callable<T> { | |||
private final Repository repo; | |||
/** | |||
* The output stream to be used by the hook. | |||
*/ | |||
protected final PrintStream outputStream; | |||
/** | |||
* @param repo | |||
* @param outputStream | |||
* The output stream the hook must use. {@code null} is allowed, | |||
* in which case the hook will use {@code System.out}. | |||
*/ | |||
protected GitHook(Repository repo, PrintStream outputStream) { | |||
this.repo = repo; | |||
this.outputStream = outputStream; | |||
} | |||
/** | |||
* Run the hook. | |||
* | |||
* @throws IOException | |||
* if IO goes wrong. | |||
* @throws AbortedByHookException | |||
* If the hook has been run and a returned an exit code | |||
* different from zero. | |||
*/ | |||
public abstract T call() throws IOException, AbortedByHookException; | |||
/** | |||
* @return The name of the hook, which must not be {@code null}. | |||
*/ | |||
public abstract String getHookName(); | |||
/** | |||
* @return The repository. | |||
*/ | |||
protected Repository getRepository() { | |||
return repo; | |||
} | |||
/** | |||
* Override this method when needed to provide relevant parameters to the | |||
* underlying hook script. The default implementation returns an empty | |||
* array. | |||
* | |||
* @return The parameters the hook receives. | |||
*/ | |||
protected String[] getParameters() { | |||
return new String[0]; | |||
} | |||
/** | |||
* Override to provide relevant arguments via stdin to the underlying hook | |||
* script. The default implementation returns {@code null}. | |||
* | |||
* @return The parameters the hook receives. | |||
*/ | |||
protected String getStdinArgs() { | |||
return null; | |||
} | |||
/** | |||
* @return The output stream the hook must use. Never {@code null}, | |||
* {@code System.out} is returned by default. | |||
*/ | |||
protected PrintStream getOutputStream() { | |||
return outputStream == null ? System.out : outputStream; | |||
} | |||
/** | |||
* Runs the hook, without performing any validity checks. | |||
* | |||
* @throws AbortedByHookException | |||
* If the underlying hook script exited with non-zero. | |||
*/ | |||
protected void doRun() throws AbortedByHookException { | |||
final ByteArrayOutputStream errorByteArray = new ByteArrayOutputStream(); | |||
final PrintStream hookErrRedirect = new PrintStream(errorByteArray); | |||
ProcessResult result = FS.DETECTED.runHookIfPresent(getRepository(), | |||
getHookName(), getParameters(), getOutputStream(), | |||
hookErrRedirect, getStdinArgs()); | |||
if (result.isExecutedWithError()) { | |||
throw new AbortedByHookException(errorByteArray.toString(), | |||
getHookName(), result.getExitCode()); | |||
} | |||
} | |||
} |
@@ -40,22 +40,27 @@ | |||
* 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; | |||
package org.eclipse.jgit.hooks; | |||
import java.io.PrintStream; | |||
import org.eclipse.jgit.lib.Repository; | |||
/** | |||
* 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}). | |||
* Factory class for instantiating supported hooks. | |||
* | |||
* @since 3.7 | |||
* @since 4.0 | |||
*/ | |||
public class RejectCommitException extends GitAPIException { | |||
private static final long serialVersionUID = 1L; | |||
public class Hooks { | |||
/** | |||
* @param message | |||
* @param repo | |||
* @param outputStream | |||
* The output stream, or {@code null} to use {@code System.out} | |||
* @return The pre-commit hook for the given repository. | |||
*/ | |||
public RejectCommitException(String message) { | |||
super(message); | |||
public static PreCommitHook preCommit(Repository repo, | |||
PrintStream outputStream) { | |||
return new PreCommitHook(repo, outputStream); | |||
} | |||
} |
@@ -0,0 +1,84 @@ | |||
/* | |||
* 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.hooks; | |||
import java.io.IOException; | |||
import java.io.PrintStream; | |||
import org.eclipse.jgit.api.errors.AbortedByHookException; | |||
import org.eclipse.jgit.lib.Repository; | |||
/** | |||
* The <code>pre-commit</code> hook implementation. This hook is run before the | |||
* commit and can reject the commit. | |||
* | |||
* @since 4.0 | |||
*/ | |||
public class PreCommitHook extends GitHook<Void> { | |||
/** The pre-commit hook name. */ | |||
public static final String NAME = "pre-commit"; //$NON-NLS-1$ | |||
/** | |||
* @param repo | |||
* The repository | |||
* @param outputStream | |||
* The output stream the hook must use. {@code null} is allowed, | |||
* in which case the hook will use {@code System.out}. | |||
*/ | |||
protected PreCommitHook(Repository repo, PrintStream outputStream) { | |||
super(repo, outputStream); | |||
} | |||
@Override | |||
public Void call() throws IOException, AbortedByHookException { | |||
doRun(); | |||
return null; | |||
} | |||
@Override | |||
public String getHookName() { | |||
return NAME; | |||
} | |||
} |
@@ -158,12 +158,12 @@ public class JGitText extends TranslationBundle { | |||
/***/ public String classCastNotA; | |||
/***/ public String cloneNonEmptyDirectory; | |||
/***/ public String collisionOn; | |||
/***/ public String commandRejectedByHook; | |||
/***/ public String commandWasCalledInTheWrongState; | |||
/***/ public String commitAlreadyExists; | |||
/***/ public String commitMessageNotSpecified; | |||
/***/ public String commitOnRepoWithoutHEADCurrentlyNotSupported; | |||
/***/ public String commitAmendOnInitialNotPossible; | |||
/***/ public String commitRejectedByHook; | |||
/***/ public String compressingObjects; | |||
/***/ public String connectionFailed; | |||
/***/ public String connectionTimeOut; |
@@ -660,8 +660,8 @@ public abstract class FS { | |||
* | |||
* @param repository | |||
* The repository for which a hook should be run. | |||
* @param hook | |||
* The hook to be executed. | |||
* @param hookName | |||
* The name of the hook to be executed. | |||
* @param args | |||
* Arguments to pass to this hook. Cannot be <code>null</code>, | |||
* but can be an empty array. | |||
@@ -669,11 +669,12 @@ public abstract class FS { | |||
* @throws JGitInternalException | |||
* if we fail to run the hook somehow. Causes may include an | |||
* interrupted process or I/O errors. | |||
* @since 3.7 | |||
* @since 4.0 | |||
*/ | |||
public ProcessResult runIfPresent(Repository repository, final Hook hook, | |||
public ProcessResult runHookIfPresent(Repository repository, | |||
final String hookName, | |||
String[] args) throws JGitInternalException { | |||
return runIfPresent(repository, hook, args, System.out, System.err, | |||
return runHookIfPresent(repository, hookName, args, System.out, System.err, | |||
null); | |||
} | |||
@@ -683,8 +684,8 @@ public abstract class FS { | |||
* | |||
* @param repository | |||
* The repository for which a hook should be run. | |||
* @param hook | |||
* The hook to be executed. | |||
* @param hookName | |||
* The name of the hook to be executed. | |||
* @param args | |||
* Arguments to pass to this hook. Cannot be <code>null</code>, | |||
* but can be an empty array. | |||
@@ -703,9 +704,10 @@ public abstract class FS { | |||
* @throws JGitInternalException | |||
* if we fail to run the hook somehow. Causes may include an | |||
* interrupted process or I/O errors. | |||
* @since 3.7 | |||
* @since 4.0 | |||
*/ | |||
public ProcessResult runIfPresent(Repository repository, final Hook hook, | |||
public ProcessResult runHookIfPresent(Repository repository, | |||
final String hookName, | |||
String[] args, PrintStream outRedirect, PrintStream errRedirect, | |||
String stdinArgs) throws JGitInternalException { | |||
return new ProcessResult(Status.NOT_SUPPORTED); | |||
@@ -713,13 +715,13 @@ public abstract class FS { | |||
/** | |||
* See | |||
* {@link #runIfPresent(Repository, Hook, String[], PrintStream, PrintStream, String)} | |||
* {@link #runHookIfPresent(Repository, String, String[], PrintStream, PrintStream, String)} | |||
* . Should only be called by FS supporting shell scripts execution. | |||
* | |||
* @param repository | |||
* The repository for which a hook should be run. | |||
* @param hook | |||
* The hook to be executed. | |||
* @param hookName | |||
* The name of the hook to be executed. | |||
* @param args | |||
* Arguments to pass to this hook. Cannot be <code>null</code>, | |||
* but can be an empty array. | |||
@@ -738,13 +740,13 @@ public abstract class FS { | |||
* @throws JGitInternalException | |||
* if we fail to run the hook somehow. Causes may include an | |||
* interrupted process or I/O errors. | |||
* @since 3.7 | |||
* @since 4.0 | |||
*/ | |||
protected ProcessResult internalRunIfPresent(Repository repository, | |||
final Hook hook, String[] args, PrintStream outRedirect, | |||
protected ProcessResult internalRunHookIfPresent(Repository repository, | |||
final String hookName, String[] args, PrintStream outRedirect, | |||
PrintStream errRedirect, String stdinArgs) | |||
throws JGitInternalException { | |||
final File hookFile = findHook(repository, hook); | |||
final File hookFile = findHook(repository, hookName); | |||
if (hookFile == null) | |||
return new ProcessResult(Status.NOT_PRESENT); | |||
@@ -764,11 +766,11 @@ public abstract class FS { | |||
} catch (IOException e) { | |||
throw new JGitInternalException(MessageFormat.format( | |||
JGitText.get().exceptionCaughtDuringExecutionOfHook, | |||
hook.getName()), e); | |||
hookName), e); | |||
} catch (InterruptedException e) { | |||
throw new JGitInternalException(MessageFormat.format( | |||
JGitText.get().exceptionHookExecutionInterrupted, | |||
hook.getName()), e); | |||
hookName), e); | |||
} | |||
} | |||
@@ -778,15 +780,15 @@ public abstract class FS { | |||
* | |||
* @param repository | |||
* The repository within which to find a hook. | |||
* @param hook | |||
* The hook we're trying to find. | |||
* @param hookName | |||
* The name of the hook we're trying to find. | |||
* @return The {@link File} containing this particular hook if it exists in | |||
* the given repository, <code>null</code> otherwise. | |||
* @since 3.7 | |||
* @since 4.0 | |||
*/ | |||
public File findHook(Repository repository, final Hook hook) { | |||
public File findHook(Repository repository, final String hookName) { | |||
final File hookFile = new File(new File(repository.getDirectory(), | |||
Constants.HOOKS), hook.getName()); | |||
Constants.HOOKS), hookName); | |||
return hookFile.isFile() ? hookFile : null; | |||
} | |||
@@ -126,13 +126,13 @@ public abstract class FS_POSIX extends FS { | |||
} | |||
/** | |||
* @since 3.7 | |||
* @since 4.0 | |||
*/ | |||
@Override | |||
public ProcessResult runIfPresent(Repository repository, Hook hook, | |||
public ProcessResult runHookIfPresent(Repository repository, String hookName, | |||
String[] args, PrintStream outRedirect, PrintStream errRedirect, | |||
String stdinArgs) throws JGitInternalException { | |||
return internalRunIfPresent(repository, hook, args, outRedirect, | |||
return internalRunHookIfPresent(repository, hookName, args, outRedirect, | |||
errRedirect, stdinArgs); | |||
} | |||
} |
@@ -149,13 +149,13 @@ public class FS_Win32_Cygwin extends FS_Win32 { | |||
} | |||
/** | |||
* @since 3.7 | |||
* @since 4.0 | |||
*/ | |||
@Override | |||
public ProcessResult runIfPresent(Repository repository, Hook hook, | |||
public ProcessResult runHookIfPresent(Repository repository, String hookName, | |||
String[] args, PrintStream outRedirect, PrintStream errRedirect, | |||
String stdinArgs) throws JGitInternalException { | |||
return internalRunIfPresent(repository, hook, args, outRedirect, | |||
return internalRunHookIfPresent(repository, hookName, args, outRedirect, | |||
errRedirect, stdinArgs); | |||
} | |||
} |
@@ -1,149 +0,0 @@ | |||
/* | |||
* Copyright (C) 2014 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.util; | |||
/** | |||
* An enum describing the different hooks a user can implement to customize his | |||
* repositories. | |||
* | |||
* @since 3.7 | |||
*/ | |||
public enum Hook { | |||
/** | |||
* Literal for the "pre-commit" git hook. | |||
* <p> | |||
* This hook is invoked by git commit, and can be bypassed with the | |||
* "no-verify" option. It takes no parameter, and is invoked before | |||
* obtaining the proposed commit log message and making a commit. | |||
* </p> | |||
* <p> | |||
* A non-zero exit code from the called hook means that the commit should be | |||
* aborted. | |||
* </p> | |||
*/ | |||
PRE_COMMIT("pre-commit"), //$NON-NLS-1$ | |||
/** | |||
* Literal for the "prepare-commit-msg" git hook. | |||
* <p> | |||
* This hook is invoked by git commit right after preparing the default | |||
* message, and before any editing possibility is displayed to the user. | |||
* </p> | |||
* <p> | |||
* A non-zero exit code from the called hook means that the commit should be | |||
* aborted. | |||
* </p> | |||
*/ | |||
PREPARE_COMMIT_MSG("prepare-commit-msg"), //$NON-NLS-1$ | |||
/** | |||
* Literal for the "commit-msg" git hook. | |||
* <p> | |||
* This hook is invoked by git commit, and can be bypassed with the | |||
* "no-verify" option. Its single parameter is the path to the file | |||
* containing the prepared commit message (typically | |||
* "<gitdir>/COMMIT-EDITMSG"). | |||
* </p> | |||
* <p> | |||
* A non-zero exit code from the called hook means that the commit should be | |||
* aborted. | |||
* </p> | |||
*/ | |||
COMMIT_MSG("commit-msg"), //$NON-NLS-1$ | |||
/** | |||
* Literal for the "post-commit" git hook. | |||
* <p> | |||
* This hook is invoked by git commit. It takes no parameter and is invoked | |||
* after a commit has been made. | |||
* </p> | |||
* <p> | |||
* The exit code of this hook has no significance. | |||
* </p> | |||
*/ | |||
POST_COMMIT("post-commit"), //$NON-NLS-1$ | |||
/** | |||
* Literal for the "post-rewrite" git hook. | |||
* <p> | |||
* This hook is invoked after commands that rewrite commits (currently, only | |||
* "git rebase" and "git commit --amend"). It a single argument denoting the | |||
* source of the call (one of <code>rebase</code> or <code>amend</code>). It | |||
* then accepts a list of rewritten commits through stdin, in the form | |||
* <code><old SHA-1> <new SHA-1>LF</code>. | |||
* </p> | |||
* <p> | |||
* The exit code of this hook has no significance. | |||
* </p> | |||
*/ | |||
POST_REWRITE("post-rewrite"), //$NON-NLS-1$ | |||
/** | |||
* Literal for the "pre-rebase" git hook. | |||
* <p> | |||
* </p> | |||
* This hook is invoked right before the rebase operation runs. It accepts | |||
* up to two parameters, the first being the upstream from which the branch | |||
* to rebase has been forked. If the tip of the series of commits to rebase | |||
* is HEAD, the other parameter is unset. Otherwise, that tip is passed as | |||
* the second parameter of the script. | |||
* <p> | |||
* A non-zero exit code from the called hook means that the rebase should be | |||
* aborted. | |||
* </p> | |||
*/ | |||
PRE_REBASE("pre-rebase"); //$NON-NLS-1$ | |||
private final String name; | |||
private Hook(String name) { | |||
this.name = name; | |||
} | |||
/** | |||
* @return The name of this hook. | |||
*/ | |||
public String getName() { | |||
return name; | |||
} | |||
} |
@@ -109,4 +109,13 @@ public class ProcessResult { | |||
public Status getStatus() { | |||
return status; | |||
} | |||
/** | |||
* @return <code>true</code> if the execution occurred and resulted in a | |||
* return code different from 0, <code>false</code> otherwise. | |||
* @since 4.0 | |||
*/ | |||
public boolean isExecutedWithError() { | |||
return getStatus() == ProcessResult.Status.OK && getExitCode() != 0; | |||
} | |||
} |