import java.nio.file.attribute.PosixFilePermission;
import java.util.Set;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+
/**
* FS implementation for Java7 on unix like systems
*/
public String normalize(String name) {
return FileUtil.normalize(name);
}
+
+ /**
+ * @since 3.7
+ */
+ @Override
+ public File findHook(Repository repository, Hook hook) {
+ final File gitdir = repository.getDirectory();
+ final Path hookPath = gitdir.toPath().resolve(Constants.HOOKS)
+ .resolve(hook.getName());
+ if (Files.isExecutable(hookPath))
+ return hookPath.toFile();
+ return null;
+ }
}
import java.io.File;
import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
/**
* FS for Java7 on Windows with Cygwin
public Attributes getAttributes(File path) {
return FileUtil.getFileAttributesBasic(this, path);
}
+
+ /**
+ * @since 3.7
+ */
+ @Override
+ public File findHook(Repository repository, Hook hook) {
+ final File gitdir = repository.getDirectory();
+ final Path hookPath = gitdir.toPath().resolve(Constants.HOOKS)
+ .resolve(hook.getName());
+ if (Files.isExecutable(hookPath))
+ return hookPath.toFile();
+ return null;
+ }
}
import java.io.File;
import java.io.IOException;
+import java.util.regex.Matcher;
import org.eclipse.jgit.junit.JGitTestUtil;
import org.junit.After;
String target = fs.readSymLink(new File(trash, "x"));
assertEquals("y", target);
}
+
+ @Test
+ public void testRelativize_doc() {
+ // This is the javadoc example
+ String base = toOSPathString("c:\\Users\\jdoe\\eclipse\\git\\project");
+ String other = toOSPathString("c:\\Users\\jdoe\\eclipse\\git\\another_project\\pom.xml");
+ String expected = toOSPathString("..\\another_project\\pom.xml");
+
+ String actual = FileUtils.relativize(base, other);
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testRelativize_mixedCase() {
+ SystemReader systemReader = SystemReader.getInstance();
+ String oldOSName = null;
+ String base = toOSPathString("C:\\git\\jgit");
+ String other = toOSPathString("C:\\Git\\test\\d\\f.txt");
+ String expectedWindows = toOSPathString("..\\test\\d\\f.txt");
+ String expectedUnix = toOSPathString("..\\..\\Git\\test\\d\\f.txt");
+
+ if (!systemReader.isWindows()) {
+ String actual = FileUtils.relativize(base, other);
+ assertEquals(expectedUnix, actual);
+
+ // FS_POSIX#isCaseSensitive will return "false" for mac OS X.
+ // Use this to test both behaviors.
+ oldOSName = System.getProperty("os.name");
+ try {
+ System.setProperty("os.name", "Mac OS X");
+
+ actual = FileUtils.relativize(base, other);
+ assertEquals(expectedWindows, actual);
+ } finally {
+ if (oldOSName != null)
+ System.setProperty("os.name", oldOSName);
+ }
+ } else {
+ String actual = FileUtils.relativize(base, other);
+ assertEquals(expectedWindows, actual);
+ }
+ }
+
+ @Test
+ public void testRelativize_scheme() {
+ String base = toOSPathString("file:/home/eclipse/runtime-New_configuration/project_1/file.java");
+ String other = toOSPathString("file:/home/eclipse/runtime-New_configuration/project");
+ // 'file.java' is treated as a folder
+ String expected = toOSPathString("../../project");
+
+ String actual = FileUtils.relativize(base, other);
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testRelativize_equalPaths() {
+ String base = toOSPathString("file:/home/eclipse/runtime-New_configuration/project_1");
+ String other = toOSPathString("file:/home/eclipse/runtime-New_configuration/project_1");
+ String expected = "";
+
+ String actual = FileUtils.relativize(base, other);
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testRelativize_whitespaces() {
+ String base = toOSPathString("/home/eclipse 3.4/runtime New_configuration/project_1");
+ String other = toOSPathString("/home/eclipse 3.4/runtime New_configuration/project_1/file");
+ String expected = "file";
+
+ String actual = FileUtils.relativize(base, other);
+ assertEquals(expected, actual);
+ }
+
+ private String toOSPathString(String path) {
+ return path.replaceAll("/|\\\\",
+ Matcher.quoteReplacement(File.separator));
+ }
}
errorOccurredDuringUnpackingOnTheRemoteEnd=error occurred during unpacking on the remote end: {0}
errorReadingInfoRefs=error reading info/refs
errorSymlinksNotSupported=Symlinks are not supported with this OS/JRE
+exceptionCaughtDuringExecutionOfHook=Exception caught during execution of "{0}" hook.
exceptionCaughtDuringExecutionOfAddCommand=Exception caught during execution of add command
exceptionCaughtDuringExecutionOfArchiveCommand=Exception caught during execution of archive command
exceptionCaughtDuringExecutionOfCherryPickCommand=Exception caught during execution of cherry-pick command. {0}
exceptionCaughtDuringExecutionOfRevertCommand=Exception caught during execution of revert command. {0}
exceptionCaughtDuringExecutionOfRmCommand=Exception caught during execution of rm command
exceptionCaughtDuringExecutionOfTagCommand=Exception caught during execution of tag command
+exceptionHookExecutionInterrupted=Execution of "{0}" hook interrupted.
exceptionOccurredDuringAddingOfOptionToALogCommand=Exception occurred during adding of {0} as option to a Log command
exceptionOccurredDuringReadingOfGIT_DIR=Exception occurred during reading of $GIT_DIR/{0}. {1}
exceptionWhileReadingPack=ERROR: Exception caught while accessing pack file {0}, the pack file might be corrupt
/***/ public String errorOccurredDuringUnpackingOnTheRemoteEnd;
/***/ public String errorReadingInfoRefs;
/***/ public String errorSymlinksNotSupported;
+ /***/ public String exceptionCaughtDuringExecutionOfHook;
/***/ public String exceptionCaughtDuringExecutionOfAddCommand;
/***/ public String exceptionCaughtDuringExecutionOfArchiveCommand;
/***/ public String exceptionCaughtDuringExecutionOfCherryPickCommand;
/***/ public String exceptionCaughtDuringExecutionOfRevertCommand;
/***/ public String exceptionCaughtDuringExecutionOfRmCommand;
/***/ public String exceptionCaughtDuringExecutionOfTagCommand;
+ /***/ public String exceptionHookExecutionInterrupted;
/***/ public String exceptionOccurredDuringAddingOfOptionToALogCommand;
/***/ public String exceptionOccurredDuringReadingOfGIT_DIR;
/***/ public String exceptionWhileReadingPack;
*/
public static final String MODULES = "modules";
+ /**
+ * Name of the folder (inside gitDir) where the hooks are stored.
+ *
+ * @since 3.7
+ */
+ public static final String HOOKS = "hooks";
+
/**
* Create a new digest function for objects.
*
package org.eclipse.jgit.util;
import java.io.BufferedReader;
+import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.io.PrintWriter;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.text.MessageFormat;
import java.util.Arrays;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
+import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.errors.SymlinksNotSupportedException;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.util.ProcessResult.Status;
/** Abstraction to support various file system operations not in Java. */
public abstract class FS {
JGitText.get().errorSymlinksNotSupported);
}
+ /**
+ * See {@link FileUtils#relativize(String, String)}.
+ *
+ * @param base
+ * The path against which <code>other</code> should be
+ * relativized.
+ * @param other
+ * The path that will be made relative to <code>base</code>.
+ * @return A relative path that, when resolved against <code>base</code>,
+ * will yield the original <code>other</code>.
+ * @see FileUtils#relativize(String, String)
+ * @since 3.7
+ */
+ public String relativize(String base, String other) {
+ return FileUtils.relativize(base, other);
+ }
+
+ /**
+ * Checks whether the given hook is defined for the given repository, then
+ * runs it with the given arguments.
+ * <p>
+ * The hook's standard output and error streams will be redirected to
+ * <code>System.out</code> and <code>System.err</code> respectively. The
+ * hook will have no stdin.
+ * </p>
+ *
+ * @param repository
+ * The repository for which a hook should be run.
+ * @param hook
+ * The hook to be executed.
+ * @param args
+ * Arguments to pass to this hook. Cannot be <code>null</code>,
+ * but can be an empty array.
+ * @return The ProcessResult describing this hook's execution.
+ * @throws JGitInternalException
+ * if we fail to run the hook somehow. Causes may include an
+ * interrupted process or I/O errors.
+ * @since 3.7
+ */
+ public ProcessResult runIfPresent(Repository repository, final Hook hook,
+ String[] args) throws JGitInternalException {
+ return runIfPresent(repository, hook, args, System.out, System.err,
+ null);
+ }
+
+ /**
+ * Checks whether the given hook is defined for the given repository, then
+ * runs it with the given arguments.
+ *
+ * @param repository
+ * The repository for which a hook should be run.
+ * @param hook
+ * The hook to be executed.
+ * @param args
+ * Arguments to pass to this hook. Cannot be <code>null</code>,
+ * but can be an empty array.
+ * @param outRedirect
+ * A print stream on which to redirect the hook's stdout. Can be
+ * <code>null</code>, in which case the hook's standard output
+ * will be lost.
+ * @param errRedirect
+ * A print stream on which to redirect the hook's stderr. Can be
+ * <code>null</code>, in which case the hook's standard error
+ * will be lost.
+ * @param stdinArgs
+ * A string to pass on to the standard input of the hook. May be
+ * <code>null</code>.
+ * @return The ProcessResult describing this hook's execution.
+ * @throws JGitInternalException
+ * if we fail to run the hook somehow. Causes may include an
+ * interrupted process or I/O errors.
+ * @since 3.7
+ */
+ public ProcessResult runIfPresent(Repository repository, final Hook hook,
+ String[] args, PrintStream outRedirect, PrintStream errRedirect,
+ String stdinArgs) throws JGitInternalException {
+ return new ProcessResult(Status.NOT_SUPPORTED);
+ }
+
+ /**
+ * See
+ * {@link #runIfPresent(Repository, Hook, 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 args
+ * Arguments to pass to this hook. Cannot be <code>null</code>,
+ * but can be an empty array.
+ * @param outRedirect
+ * A print stream on which to redirect the hook's stdout. Can be
+ * <code>null</code>, in which case the hook's standard output
+ * will be lost.
+ * @param errRedirect
+ * A print stream on which to redirect the hook's stderr. Can be
+ * <code>null</code>, in which case the hook's standard error
+ * will be lost.
+ * @param stdinArgs
+ * A string to pass on to the standard input of the hook. May be
+ * <code>null</code>.
+ * @return The ProcessResult describing this hook's execution.
+ * @throws JGitInternalException
+ * if we fail to run the hook somehow. Causes may include an
+ * interrupted process or I/O errors.
+ * @since 3.7
+ */
+ protected ProcessResult internalRunIfPresent(Repository repository,
+ final Hook hook, String[] args, PrintStream outRedirect,
+ PrintStream errRedirect, String stdinArgs)
+ throws JGitInternalException {
+ final File hookFile = findHook(repository, hook);
+ if (hookFile == null)
+ return new ProcessResult(Status.NOT_PRESENT);
+
+ final String hookPath = hookFile.getAbsolutePath();
+ final File runDirectory;
+ if (repository.isBare())
+ runDirectory = repository.getDirectory();
+ else
+ runDirectory = repository.getWorkTree();
+ final String cmd = relativize(runDirectory.getAbsolutePath(),
+ hookPath);
+ ProcessBuilder hookProcess = runInShell(cmd, args);
+ hookProcess.directory(runDirectory);
+ try {
+ return new ProcessResult(runProcess(hookProcess, outRedirect,
+ errRedirect, stdinArgs), Status.OK);
+ } catch (IOException e) {
+ throw new JGitInternalException(MessageFormat.format(
+ JGitText.get().exceptionCaughtDuringExecutionOfHook,
+ hook.getName()), e);
+ } catch (InterruptedException e) {
+ throw new JGitInternalException(MessageFormat.format(
+ JGitText.get().exceptionHookExecutionInterrupted,
+ hook.getName()), e);
+ }
+ }
+
+
+ /**
+ * Tries to find a hook matching the given one in the given repository.
+ *
+ * @param repository
+ * The repository within which to find a hook.
+ * @param hook
+ * 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
+ */
+ public File findHook(Repository repository, final Hook hook) {
+ final File hookFile = new File(new File(repository.getDirectory(),
+ Constants.HOOKS), hook.getName());
+ return hookFile.isFile() ? hookFile : null;
+ }
+
+ /**
+ * Runs the given process until termination, clearing its stdout and stderr
+ * streams on-the-fly.
+ *
+ * @param hookProcessBuilder
+ * The process builder configured for this hook.
+ * @param outRedirect
+ * A print stream on which to redirect the hook's stdout. Can be
+ * <code>null</code>, in which case the hook's standard output
+ * will be lost.
+ * @param errRedirect
+ * A print stream on which to redirect the hook's stderr. Can be
+ * <code>null</code>, in which case the hook's standard error
+ * will be lost.
+ * @param stdinArgs
+ * A string to pass on to the standard input of the hook. Can be
+ * <code>null</code>.
+ * @return the exit value of this hook.
+ * @throws IOException
+ * if an I/O error occurs while executing this hook.
+ * @throws InterruptedException
+ * if the current thread is interrupted while waiting for the
+ * process to end.
+ * @since 3.7
+ */
+ protected int runProcess(ProcessBuilder hookProcessBuilder,
+ OutputStream outRedirect, OutputStream errRedirect, String stdinArgs)
+ throws IOException, InterruptedException {
+ final ExecutorService executor = Executors.newFixedThreadPool(2);
+ Process process = null;
+ // We'll record the first I/O exception that occurs, but keep on trying
+ // to dispose of our open streams and file handles
+ IOException ioException = null;
+ try {
+ process = hookProcessBuilder.start();
+ final Callable<Void> errorGobbler = new StreamGobbler(
+ process.getErrorStream(), errRedirect);
+ final Callable<Void> outputGobbler = new StreamGobbler(
+ process.getInputStream(), outRedirect);
+ executor.submit(errorGobbler);
+ executor.submit(outputGobbler);
+ if (stdinArgs != null) {
+ final PrintWriter stdinWriter = new PrintWriter(
+ process.getOutputStream());
+ stdinWriter.print(stdinArgs);
+ stdinWriter.flush();
+ // We are done with this hook's input. Explicitly close its
+ // stdin now to kick off any blocking read the hook might have.
+ stdinWriter.close();
+ }
+ return process.waitFor();
+ } catch (IOException e) {
+ ioException = e;
+ } finally {
+ shutdownAndAwaitTermination(executor);
+ if (process != null) {
+ try {
+ process.waitFor();
+ } catch (InterruptedException e) {
+ // Thrown by the outer try.
+ // Swallow this one to carry on our cleanup, and clear the
+ // interrupted flag (processes throw the exception without
+ // clearing the flag).
+ Thread.interrupted();
+ }
+ // A process doesn't clean its own resources even when destroyed
+ // Explicitly try and close all three streams, preserving the
+ // outer I/O exception if any.
+ try {
+ process.getErrorStream().close();
+ } catch (IOException e) {
+ ioException = ioException != null ? ioException : e;
+ }
+ try {
+ process.getInputStream().close();
+ } catch (IOException e) {
+ ioException = ioException != null ? ioException : e;
+ }
+ try {
+ process.getOutputStream().close();
+ } catch (IOException e) {
+ ioException = ioException != null ? ioException : e;
+ }
+ process.destroy();
+ }
+ }
+ // We can only be here if the outer try threw an IOException.
+ throw ioException;
+ }
+
+ /**
+ * Shuts down an {@link ExecutorService} in two phases, first by calling
+ * {@link ExecutorService#shutdown() shutdown} to reject incoming tasks, and
+ * then calling {@link ExecutorService#shutdownNow() shutdownNow}, if
+ * necessary, to cancel any lingering tasks. Returns true if the pool has
+ * been properly shutdown, false otherwise.
+ * <p>
+ *
+ * @param pool
+ * the pool to shutdown
+ * @return <code>true</code> if the pool has been properly shutdown,
+ * <code>false</code> otherwise.
+ */
+ private static boolean shutdownAndAwaitTermination(ExecutorService pool) {
+ boolean hasShutdown = true;
+ pool.shutdown(); // Disable new tasks from being submitted
+ try {
+ // Wait a while for existing tasks to terminate
+ if (!pool.awaitTermination(5, TimeUnit.SECONDS)) {
+ pool.shutdownNow(); // Cancel currently executing tasks
+ // Wait a while for tasks to respond to being canceled
+ if (!pool.awaitTermination(5, TimeUnit.SECONDS))
+ hasShutdown = false;
+ }
+ } catch (InterruptedException ie) {
+ // (Re-)Cancel if current thread also interrupted
+ pool.shutdownNow();
+ // Preserve interrupt status
+ Thread.currentThread().interrupt();
+ hasShutdown = false;
+ }
+ return hasShutdown;
+ }
+
/**
* Initialize a ProcesssBuilder to run a command using the system shell.
*
public String normalize(String name) {
return name;
}
+
+ /**
+ * This runnable will consume an input stream's content into an output
+ * stream as soon as it gets available.
+ * <p>
+ * Typically used to empty processes' standard output and error, preventing
+ * them to choke.
+ * </p>
+ * <p>
+ * <b>Note</b> that a {@link StreamGobbler} will never close either of its
+ * streams.
+ * </p>
+ */
+ private static class StreamGobbler implements Callable<Void> {
+ private final BufferedReader reader;
+
+ private final BufferedWriter writer;
+
+ public StreamGobbler(InputStream stream, OutputStream output) {
+ this.reader = new BufferedReader(new InputStreamReader(stream));
+ if (output == null)
+ this.writer = null;
+ else
+ this.writer = new BufferedWriter(new OutputStreamWriter(output));
+ }
+
+ public Void call() throws IOException {
+ boolean writeFailure = false;
+
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ // Do not try to write again after a failure, but keep reading
+ // as long as possible to prevent the input stream from choking.
+ if (!writeFailure && writer != null) {
+ try {
+ writer.write(line);
+ writer.newLine();
+ writer.flush();
+ } catch (IOException e) {
+ writeFailure = true;
+ }
+ }
+ }
+ return null;
+ }
+ }
}
import java.io.File;
import java.io.IOException;
+import java.io.PrintStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.lib.Repository;
/**
* Base FS for POSIX based systems
proc.command(argv);
return proc;
}
+
+ /**
+ * @since 3.7
+ */
+ @Override
+ public ProcessResult runIfPresent(Repository repository, Hook hook,
+ String[] args, PrintStream outRedirect, PrintStream errRedirect,
+ String stdinArgs) throws JGitInternalException {
+ return internalRunIfPresent(repository, hook, args, outRedirect,
+ errRedirect, stdinArgs);
+ }
}
package org.eclipse.jgit.util;
import java.io.File;
+import java.io.PrintStream;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.lib.Repository;
/**
* FS implementation for Cygwin on Windows
proc.command(argv);
return proc;
}
+
+ /**
+ * @since 3.7
+ */
+ @Override
+ public String relativize(String base, String other) {
+ final String relativized = super.relativize(base, other);
+ return relativized.replace(File.separatorChar, '/');
+ }
+
+ /**
+ * @since 3.7
+ */
+ @Override
+ public ProcessResult runIfPresent(Repository repository, Hook hook,
+ String[] args, PrintStream outRedirect, PrintStream errRedirect,
+ String stdinArgs) throws JGitInternalException {
+ return internalRunIfPresent(repository, hook, args, outRedirect,
+ errRedirect, stdinArgs);
+ }
}
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
+import java.util.regex.Pattern;
import org.eclipse.jgit.internal.JGitText;
}
throw new IOException(JGitText.get().cannotCreateTempDir);
}
+
+ /**
+ * This will try and make a given path relative to another.
+ * <p>
+ * For example, if this is called with the two following paths :
+ *
+ * <pre>
+ * <code>base = "c:\\Users\\jdoe\\eclipse\\git\\project"</code>
+ * <code>other = "c:\\Users\\jdoe\\eclipse\\git\\another_project\\pom.xml"</code>
+ * </pre>
+ *
+ * This will return "..\\another_project\\pom.xml".
+ * </p>
+ * <p>
+ * This method uses {@link File#separator} to split the paths into segments.
+ * </p>
+ * <p>
+ * <b>Note</b> that this will return the empty String if <code>base</code>
+ * and <code>other</code> are equal.
+ * </p>
+ *
+ * @param base
+ * The path against which <code>other</code> should be
+ * relativized. This will be assumed to denote the path to a
+ * folder and not a file.
+ * @param other
+ * The path that will be made relative to <code>base</code>.
+ * @return A relative path that, when resolved against <code>base</code>,
+ * will yield the original <code>other</code>.
+ * @since 3.7
+ */
+ public static String relativize(String base, String other) {
+ if (base.equals(other))
+ return ""; //$NON-NLS-1$
+
+ final boolean ignoreCase = !FS.DETECTED.isCaseSensitive();
+ final String[] baseSegments = base.split(Pattern.quote(File.separator));
+ final String[] otherSegments = other.split(Pattern
+ .quote(File.separator));
+
+ int commonPrefix = 0;
+ while (commonPrefix < baseSegments.length
+ && commonPrefix < otherSegments.length) {
+ if (ignoreCase
+ && baseSegments[commonPrefix]
+ .equalsIgnoreCase(otherSegments[commonPrefix]))
+ commonPrefix++;
+ else if (!ignoreCase
+ && baseSegments[commonPrefix]
+ .equals(otherSegments[commonPrefix]))
+ commonPrefix++;
+ else
+ break;
+ }
+
+ final StringBuilder builder = new StringBuilder();
+ for (int i = commonPrefix; i < baseSegments.length; i++)
+ builder.append("..").append(File.separator); //$NON-NLS-1$
+ for (int i = commonPrefix; i < otherSegments.length; i++) {
+ builder.append(otherSegments[i]);
+ if (i < otherSegments.length - 1)
+ builder.append(File.separator);
+ }
+ return builder.toString();
+ }
}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+
+/**
+ * Describes the result of running an external process.
+ *
+ * @since 3.7
+ */
+public class ProcessResult {
+ /**
+ * Status of a process' execution.
+ */
+ public static enum Status {
+ /**
+ * The script was found and launched properly. It may still have exited
+ * with a non-zero {@link #exitCode}.
+ */
+ OK,
+
+ /** The script was not found on disk and thus could not be launched. */
+ NOT_PRESENT,
+
+ /**
+ * The script was found but could not be launched since it was not
+ * supported by the current {@link FS}.
+ */
+ NOT_SUPPORTED;
+ }
+
+ /** The exit code of the process. */
+ private final int exitCode;
+
+ /** Status of the process' execution. */
+ private final Status status;
+
+ /**
+ * Instantiates a process result with the given status and an exit code of
+ * <code>-1</code>.
+ *
+ * @param status
+ * Status describing the execution of the external process.
+ */
+ public ProcessResult(Status status) {
+ this(-1, status);
+ }
+
+ /**
+ * @param exitCode
+ * Exit code of the process.
+ * @param status
+ * Status describing the execution of the external process.
+ */
+ public ProcessResult(int exitCode, Status status) {
+ this.exitCode = exitCode;
+ this.status = status;
+ }
+
+ /**
+ * @return The exit code of the process.
+ */
+ public int getExitCode() {
+ return exitCode;
+ }
+
+ /**
+ * @return The status of the process' execution.
+ */
+ public Status getStatus() {
+ return status;
+ }
+}