summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Wolf <thomas.wolf@paranor.ch>2019-09-20 16:41:39 +0200
committerMatthias Sohn <matthias.sohn@sap.com>2019-10-21 18:05:31 -0400
commit385b503ae8635ddb4233c9ae1f2d472a51609162 (patch)
treeb828b829579027704eb0600bb289e737a8ca125c
parentb138f169458e254cd5d8cb768b447e24273741b5 (diff)
downloadjgit-385b503ae8635ddb4233c9ae1f2d472a51609162.tar.gz
jgit-385b503ae8635ddb4233c9ae1f2d472a51609162.zip
Support for core.hooksPath
Support the core.hooksPath git config. This can be an absolute or relative path of a directory where to find git hooks; a relative path is resolved relative to the directory the hook will run in. Bug: 500266 Change-Id: I671999a6386a837e897c31718583c91d8035f3ba Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java71
-rw-r--r--org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java17
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java91
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java15
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java17
8 files changed, 167 insertions, 52 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java
index e5fcbf9d7c..70a2dbbfe8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java
@@ -58,6 +58,8 @@ import org.eclipse.jgit.hooks.PostCommitHook;
import org.eclipse.jgit.hooks.PreCommitHook;
import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Assume;
import org.junit.Test;
@@ -221,6 +223,75 @@ public class HookTest extends RepositoryTestCase {
}
@Test
+ public void testRunHookHooksPathRelative() throws Exception {
+ assumeSupportedPlatform();
+
+ writeHookFile(PreCommitHook.NAME,
+ "#!/bin/sh\necho \"Wrong hook $1 $2\"\nread INPUT\necho $INPUT\n"
+ + "echo $GIT_DIR\necho $GIT_WORK_TREE\necho 1>&2 \"stderr\"");
+ writeHookFile("../../" + PreCommitHook.NAME,
+ "#!/bin/sh\necho \"test $1 $2\"\nread INPUT\necho $INPUT\n"
+ + "echo $GIT_DIR\necho $GIT_WORK_TREE\necho 1>&2 \"stderr\"");
+ StoredConfig cfg = db.getConfig();
+ cfg.load();
+ cfg.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_HOOKS_PATH, ".");
+ cfg.save();
+ try (ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayOutputStream err = new ByteArrayOutputStream()) {
+ 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"
+ + db.getDirectory().getAbsolutePath() + '\n'
+ + db.getWorkTree().getAbsolutePath() + '\n',
+ out.toString("UTF-8"));
+ assertEquals("unexpected output on stderr stream", "stderr\n",
+ err.toString("UTF-8"));
+ assertEquals("unexpected exit code", 0, res.getExitCode());
+ assertEquals("unexpected process status", ProcessResult.Status.OK,
+ res.getStatus());
+ }
+ }
+
+ @Test
+ public void testRunHookHooksPathAbsolute() throws Exception {
+ assumeSupportedPlatform();
+
+ writeHookFile(PreCommitHook.NAME,
+ "#!/bin/sh\necho \"Wrong hook $1 $2\"\nread INPUT\necho $INPUT\n"
+ + "echo $GIT_DIR\necho $GIT_WORK_TREE\necho 1>&2 \"stderr\"");
+ writeHookFile("../../" + PreCommitHook.NAME,
+ "#!/bin/sh\necho \"test $1 $2\"\nread INPUT\necho $INPUT\n"
+ + "echo $GIT_DIR\necho $GIT_WORK_TREE\necho 1>&2 \"stderr\"");
+ StoredConfig cfg = db.getConfig();
+ cfg.load();
+ cfg.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_HOOKS_PATH,
+ db.getWorkTree().getAbsolutePath());
+ cfg.save();
+ try (ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayOutputStream err = new ByteArrayOutputStream()) {
+ 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"
+ + db.getDirectory().getAbsolutePath() + '\n'
+ + db.getWorkTree().getAbsolutePath() + '\n',
+ out.toString("UTF-8"));
+ assertEquals("unexpected output on stderr stream", "stderr\n",
+ err.toString("UTF-8"));
+ assertEquals("unexpected exit code", 0, res.getExitCode());
+ assertEquals("unexpected process status", ProcessResult.Status.OK,
+ res.getStatus());
+ }
+ }
+
+ @Test
public void testFailedPreCommitHookBlockCommit() throws Exception {
assumeSupportedPlatform();
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
index 280d95a9f5..4607f9ee45 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -348,6 +348,7 @@ invalidFilter=Invalid filter: {0}
invalidGitdirRef = Invalid .git reference in file ''{0}''
invalidGitModules=Invalid .gitmodules file
invalidGitType=invalid git type: {0}
+invalidHooksPath=Invalid git config core.hooksPath = {0}
invalidId=Invalid id: {0}
invalidId0=Invalid id
invalidIdLength=Invalid id length {0}; should be {1}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
index ad43e2ca83..6bb5bfc4c4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
@@ -162,9 +162,14 @@ abstract class GitHook<T> implements Callable<T> {
} catch (UnsupportedEncodingException e) {
// UTF-8 is guaranteed to be available
}
- ProcessResult result = FS.DETECTED.runHookIfPresent(getRepository(),
- getHookName(), getParameters(), getOutputStream(),
- hookErrRedirect, getStdinArgs());
+ Repository repository = getRepository();
+ FS fs = repository.getFS();
+ if (fs == null) {
+ fs = FS.DETECTED;
+ }
+ ProcessResult result = fs.runHookIfPresent(repository, getHookName(),
+ getParameters(), getOutputStream(), hookErrRedirect,
+ getStdinArgs());
if (result.isExecutedWithError()) {
throw new AbortedByHookException(
new String(errorByteArray.toByteArray(), UTF_8),
@@ -180,7 +185,11 @@ abstract class GitHook<T> implements Callable<T> {
* @since 4.11
*/
public boolean isNativeHookPresent() {
- return FS.DETECTED.findHook(getRepository(), getHookName()) != null;
+ FS fs = getRepository().getFS();
+ if (fs == null) {
+ fs = FS.DETECTED;
+ }
+ return fs.findHook(getRepository(), getHookName()) != null;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
index 1aae4f4be5..2357e8ee99 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -409,6 +409,7 @@ public class JGitText extends TranslationBundle {
/***/ public String invalidGitdirRef;
/***/ public String invalidGitModules;
/***/ public String invalidGitType;
+ /***/ public String invalidHooksPath;
/***/ public String invalidId;
/***/ public String invalidId0;
/***/ public String invalidIdLength;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
index 8f40db626a..f2f1d5a48f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -149,6 +149,12 @@ public final class ConfigConstants {
*/
public static final String CONFIG_KEY_GPGSIGN = "gpgSign";
+ /**
+ * The "hooksPath" key.
+ * @since 5.6
+ */
+ public static final String CONFIG_KEY_HOOKS_PATH = "hooksPath";
+
/** The "algorithm" key */
public static final String CONFIG_KEY_ALGORITHM = "algorithm";
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
index 068e6450fe..68e19ce722 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -61,6 +61,7 @@ import java.nio.charset.Charset;
import java.nio.file.AccessDeniedException;
import java.nio.file.FileStore;
import java.nio.file.Files;
+import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
@@ -98,6 +99,7 @@ import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.LockFailedException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.file.FileSnapshot;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
@@ -1730,20 +1732,18 @@ public abstract class FS {
final String hookName, String[] args, PrintStream outRedirect,
PrintStream errRedirect, String stdinArgs)
throws JGitInternalException {
- final File hookFile = findHook(repository, hookName);
- if (hookFile == null)
+ File hookFile = findHook(repository, hookName);
+ if (hookFile == null || hookName == 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);
+ File runDirectory = getRunDirectory(repository, hookName);
+ if (runDirectory == null) {
+ return new ProcessResult(Status.NOT_PRESENT);
+ }
+ String cmd = hookFile.getAbsolutePath();
ProcessBuilder hookProcess = runInShell(cmd, args);
- hookProcess.directory(runDirectory);
+ hookProcess.directory(runDirectory.getAbsoluteFile());
Map<String, String> environment = hookProcess.environment();
environment.put(Constants.GIT_DIR_KEY,
repository.getDirectory().getAbsolutePath());
@@ -1778,12 +1778,71 @@ public abstract class FS {
* @since 4.0
*/
public File findHook(Repository repository, String hookName) {
- File gitDir = repository.getDirectory();
- if (gitDir == null)
+ if (hookName == null) {
+ return null;
+ }
+ File hookDir = getHooksDirectory(repository);
+ if (hookDir == null) {
return null;
- final File hookFile = new File(new File(gitDir,
- Constants.HOOKS), hookName);
- return hookFile.isFile() ? hookFile : null;
+ }
+ File hookFile = new File(hookDir, hookName);
+ if (hookFile.isAbsolute()) {
+ if (!hookFile.exists() || FS.DETECTED.supportsExecute()
+ && !FS.DETECTED.canExecute(hookFile)) {
+ return null;
+ }
+ } else {
+ try {
+ File runDirectory = getRunDirectory(repository, hookName);
+ if (runDirectory == null) {
+ return null;
+ }
+ Path hookPath = runDirectory.getAbsoluteFile().toPath()
+ .resolve(hookFile.toPath());
+ FS fs = repository.getFS();
+ if (fs == null) {
+ fs = FS.DETECTED;
+ }
+ if (!Files.exists(hookPath) || fs.supportsExecute()
+ && !fs.canExecute(hookPath.toFile())) {
+ return null;
+ }
+ hookFile = hookPath.toFile();
+ } catch (InvalidPathException e) {
+ LOG.warn(MessageFormat.format(JGitText.get().invalidHooksPath,
+ hookFile));
+ return null;
+ }
+ }
+ return hookFile;
+ }
+
+ private File getRunDirectory(Repository repository,
+ @NonNull String hookName) {
+ if (repository.isBare()) {
+ return repository.getDirectory();
+ }
+ switch (hookName) {
+ case "pre-receive": //$NON-NLS-1$
+ case "update": //$NON-NLS-1$
+ case "post-receive": //$NON-NLS-1$
+ case "post-update": //$NON-NLS-1$
+ case "push-to-checkout": //$NON-NLS-1$
+ return repository.getDirectory();
+ default:
+ return repository.getWorkTree();
+ }
+ }
+
+ private File getHooksDirectory(Repository repository) {
+ Config config = repository.getConfig();
+ String hooksDir = config.getString(ConfigConstants.CONFIG_CORE_SECTION,
+ null, ConfigConstants.CONFIG_KEY_HOOKS_PATH);
+ if (hooksDir != null) {
+ return new File(hooksDir);
+ }
+ File dir = repository.getDirectory();
+ return dir == null ? null : new File(dir, Constants.HOOKS);
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
index 6a1eef2d66..7c170ac4eb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
@@ -74,7 +74,6 @@ import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.errors.CommandFailedException;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
import org.slf4j.Logger;
@@ -311,20 +310,6 @@ public class FS_POSIX extends FS {
/** {@inheritDoc} */
@Override
- public File findHook(Repository repository, String hookName) {
- final File gitdir = repository.getDirectory();
- if (gitdir == null) {
- return null;
- }
- final Path hookPath = gitdir.toPath().resolve(Constants.HOOKS)
- .resolve(hookName);
- if (Files.isExecutable(hookPath))
- return hookPath.toFile();
- return null;
- }
-
- /** {@inheritDoc} */
- @Override
public boolean supportsAtomicCreateNewFile() {
if (supportsAtomicFileCreation == AtomicFileCreation.UNDEFINED) {
try {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
index 9a163e8e38..4efa9888f7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
@@ -47,8 +47,6 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.File;
import java.io.PrintStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
@@ -57,7 +55,6 @@ import java.util.List;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.errors.CommandFailedException;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -175,18 +172,4 @@ public class FS_Win32_Cygwin extends FS_Win32 {
return internalRunHookIfPresent(repository, hookName, args, outRedirect,
errRedirect, stdinArgs);
}
-
- /** {@inheritDoc} */
- @Override
- public File findHook(Repository repository, String hookName) {
- final File gitdir = repository.getDirectory();
- if (gitdir == null) {
- return null;
- }
- final Path hookPath = gitdir.toPath().resolve(Constants.HOOKS)
- .resolve(hookName);
- if (Files.isExecutable(hookPath))
- return hookPath.toFile();
- return null;
- }
}