Browse Source

Introduce hook support into the FS implementations

This introduces the background plumbing necessary to run git hooks from
JGit. This implementation will be OS-dependent as it aims to be
compatible with existing hooks, mostly written in Shell. It is
compatible with unix systems and windows as long as an Unix emulator
such as Cygwin is in its PATH.

Change-Id: I1f82a5205138fd8032614dd5b52aef14e02238ed
Signed-off-by: Laurent Goubet <laurent.goubet@obeo.fr>
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
tags/v3.7.0.201502031740-rc1
Laurent Goubet 9 years ago
parent
commit
6aed51e3ce

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

@@ -53,6 +53,9 @@ import java.nio.file.Path;
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
*/
@@ -344,4 +347,17 @@ public class FS_POSIX_Java7 extends FS_POSIX {
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;
}
}

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

@@ -45,6 +45,11 @@ package org.eclipse.jgit.util;

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
@@ -135,4 +140,17 @@ public class FS_Win32_Java7Cygwin extends FS_Win32_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;
}
}

+ 79
- 0
org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilTest.java View File

@@ -50,6 +50,7 @@ import static org.junit.Assert.fail;

import java.io.File;
import java.io.IOException;
import java.util.regex.Matcher;

import org.eclipse.jgit.junit.JGitTestUtil;
import org.junit.After;
@@ -434,4 +435,82 @@ public class FileUtilTest {
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));
}
}

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

@@ -193,6 +193,7 @@ errorListing=Error listing {0}
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}
@@ -206,6 +207,7 @@ exceptionCaughtDuringExecutionOfResetCommand=Exception caught during execution o
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

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

@@ -252,6 +252,7 @@ public class JGitText extends TranslationBundle {
/***/ public String errorOccurredDuringUnpackingOnTheRemoteEnd;
/***/ public String errorReadingInfoRefs;
/***/ public String errorSymlinksNotSupported;
/***/ public String exceptionCaughtDuringExecutionOfHook;
/***/ public String exceptionCaughtDuringExecutionOfAddCommand;
/***/ public String exceptionCaughtDuringExecutionOfArchiveCommand;
/***/ public String exceptionCaughtDuringExecutionOfCherryPickCommand;
@@ -265,6 +266,7 @@ public class JGitText extends TranslationBundle {
/***/ public String exceptionCaughtDuringExecutionOfRevertCommand;
/***/ public String exceptionCaughtDuringExecutionOfRmCommand;
/***/ public String exceptionCaughtDuringExecutionOfTagCommand;
/***/ public String exceptionHookExecutionInterrupted;
/***/ public String exceptionOccurredDuringAddingOfOptionToALogCommand;
/***/ public String exceptionOccurredDuringReadingOfGIT_DIR;
/***/ public String exceptionWhileReadingPack;

+ 7
- 0
org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java View File

@@ -386,6 +386,13 @@ public final class Constants {
*/
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.
*

+ 341
- 0
org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java View File

@@ -44,18 +44,31 @@
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 {
@@ -613,6 +626,288 @@ 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.
*
@@ -802,4 +1097,50 @@ public abstract class FS {
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;
}
}
}

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

@@ -44,11 +44,14 @@ package org.eclipse.jgit.util;

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
@@ -121,4 +124,15 @@ public abstract class FS_POSIX extends FS {
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);
}
}

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

@@ -44,12 +44,15 @@
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
@@ -135,4 +138,24 @@ public class FS_Win32_Cygwin extends FS_Win32 {
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);
}
}

+ 66
- 0
org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java View File

@@ -51,6 +51,7 @@ import java.nio.channels.FileLock;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import org.eclipse.jgit.internal.JGitText;

@@ -387,4 +388,69 @@ public class FileUtils {
}
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();
}
}

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

@@ -0,0 +1,149 @@
/*
* 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;
}
}

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

@@ -0,0 +1,112 @@
/*
* 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;
}
}

Loading…
Cancel
Save