Browse Source

Refactored pre-commit hook to make it less invasive.

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
Laurent Delaigue 9 years ago
parent
commit
26fd56f167

+ 1
- 0
org.eclipse.jgit.java7.test/META-INF/MANIFEST.MF View File

@@ -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)",

+ 15
- 17
org.eclipse.jgit.java7.test/src/org/eclipse/jgit/util/HookTest.java View File

@@ -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);
}
}


+ 2
- 2
org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_POSIX_Java7.java View File

@@ -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;

+ 2
- 2
org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_Win32_Java7Cygwin.java View File

@@ -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;

+ 1
- 0
org.eclipse.jgit/META-INF/MANIFEST.MF View File

@@ -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",

+ 1
- 1
org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties View File

@@ -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}

+ 10
- 27
org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java View File

@@ -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

+ 104
- 0
org.eclipse.jgit/src/org/eclipse/jgit/api/errors/AbortedByHookException.java View File

@@ -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());
}
}

+ 159
- 0
org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java View File

@@ -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());
}
}

}

org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RejectCommitException.java → org.eclipse.jgit/src/org/eclipse/jgit/hooks/Hooks.java View File

@@ -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);
}
}

+ 84
- 0
org.eclipse.jgit/src/org/eclipse/jgit/hooks/PreCommitHook.java View File

@@ -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;
}

}

+ 1
- 1
org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java View File

@@ -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;

+ 25
- 23
org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java View File

@@ -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;
}


+ 3
- 3
org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java View File

@@ -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);
}
}

+ 3
- 3
org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java View File

@@ -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);
}
}

+ 0
- 149
org.eclipse.jgit/src/org/eclipse/jgit/util/Hook.java View File

@@ -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
* "&lt;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>&lt;old SHA-1> &lt;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;
}
}

+ 9
- 0
org.eclipse.jgit/src/org/eclipse/jgit/util/ProcessResult.java View File

@@ -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;
}
}

Loading…
Cancel
Save