Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Documentation/config.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,8 @@ include::config/pack.adoc[]

include::config/pager.adoc[]

include::config/postcommand.adoc[]

include::config/pretty.adoc[]

include::config/promisor.adoc[]
Expand Down
13 changes: 13 additions & 0 deletions Documentation/config/postcommand.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
postCommand.strategy::
The `post-command` hook is run on every Git process by default. This
config option allows running the hook only conditionally, according
to these values:
+
----
`always`;;
run the `post-command` hook on every process (default).

`post-index-change`;;
run the `post-command` hook only if the current process wrote to
the index.
----
70 changes: 69 additions & 1 deletion hook.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#define USE_THE_REPOSITORY_VARIABLE

#include "git-compat-util.h"
#include "trace2/tr2_sid.h"
#include "abspath.h"
#include "environment.h"
#include "advice.h"
Expand Down Expand Up @@ -176,6 +177,58 @@ static void run_hooks_opt_clear(struct run_hooks_opt *options)
strvec_clear(&options->args);
}

static char *get_post_index_change_sentinel_name(struct repository *r)
{
struct strbuf path = STRBUF_INIT;
const char *sid = tr2_sid_get();
char *slash = strchr(sid, '/');

/*
* Name is based on top-level SID, so children can indicate that
* the top-level process should run the post-command hook.
*/
if (slash)
*slash = 0;

repo_git_path_replace(r, &path, "hooks/index-change-%s.snt", sid);

return strbuf_detach(&path, NULL);
}

static int write_post_index_change_sentinel(struct repository *r)
{
char *path = get_post_index_change_sentinel_name(r);
FILE *fp = xfopen(path, "w");

if (fp) {
fprintf(fp, "run post-command hook");
fclose(fp);
}

free(path);
return fp ? 0 : -1;
}

/**
* Try to delete the sentinel file for this repository. If that succeeds, then
* return 1.
*/
static int post_index_change_sentinel_exists(struct repository *r)
{
char *path = get_post_index_change_sentinel_name(r);
int res = 1;

if (unlink(path)) {
if (is_missing_file_error(errno))
res = 0;
else
warning_errno("failed to remove index-change sentinel file '%s'", path);
}

free(path);
return res;
}

int run_hooks_opt(struct repository *r, const char *hook_name,
struct run_hooks_opt *options)
{
Expand All @@ -185,7 +238,7 @@ int run_hooks_opt(struct repository *r, const char *hook_name,
.hook_name = hook_name,
.options = options,
};
const char *hook_path = find_hook(r, hook_name);
const char *hook_path;
int ret = 0;
const struct run_process_parallel_opts opts = {
.tr2_category = "hook",
Expand All @@ -201,6 +254,21 @@ int run_hooks_opt(struct repository *r, const char *hook_name,
.data = &cb_data,
};

/* Interject hook behavior depending on strategy. */
if (r && r->gitdir) {
const char *strval;
if (!repo_config_get_string_tmp(r, "postcommand.strategy", &strval) &&
Comment thread
derrickstolee marked this conversation as resolved.
!strcasecmp(strval, "post-index-change")) {
if (!strcmp(hook_name, "post-index-change"))
return write_post_index_change_sentinel(r);
if (!strcmp(hook_name, "post-command") &&
!post_index_change_sentinel_exists(r))
return 0;
Comment thread
dscho marked this conversation as resolved.
}
}

hook_path = find_hook(r, hook_name);

/*
* Backwards compatibility hack in VFS for Git: when originally
* introduced (and used!), it was called `post-indexchanged`, but this
Expand Down
42 changes: 42 additions & 0 deletions t/t0401-post-command-hook.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ test_expect_success 'with succeeding hook' '
'

test_expect_success 'with failing pre-command hook' '
test_when_finished rm -f .git/hooks/pre-command &&
write_script .git/hooks/pre-command <<-EOF &&
exit 1
EOF
Expand All @@ -30,4 +31,45 @@ test_expect_success 'with failing pre-command hook' '
test_path_is_missing "$(cat .git/post-command.out)"
'

test_expect_success 'with post-index-change config' '
mkdir -p .git/hooks &&
write_script .git/hooks/post-command <<-EOF &&
echo ran >post-command.out
EOF
write_script .git/hooks/post-index-change <<-EOF &&
echo ran >post-index-change.out
EOF

# First, show expected behavior.
echo ran >expect &&
rm -f post-command.out post-index-change.out &&

# rev-parse leaves index intact, but runs post-command.
git rev-parse HEAD &&
test_path_is_missing post-index-change.out &&
test_cmp expect post-command.out &&
rm -f post-command.out &&

echo stuff >>file &&
# add updates the index and runs post-command.
git add file &&
test_cmp expect post-index-change.out &&
test_cmp expect post-command.out &&

# Now, show configured behavior
git config postCommand.strategy post-index-change &&
rm -f post-command.out post-index-change.out &&

# rev-parse leaves index intact and thus skips post-command.
git rev-parse HEAD &&
test_path_is_missing post-index-change.out &&
test_path_is_missing post-command.out &&

echo stuff >>file &&
# add updates the index and runs post-command.
git add file &&
test_path_is_missing post-index-change.out &&
test_cmp expect post-command.out
'

test_done