Coverage Report

Created: 2026-02-14 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/git/hook.c
Line
Count
Source
1
#include "git-compat-util.h"
2
#include "abspath.h"
3
#include "advice.h"
4
#include "gettext.h"
5
#include "hook.h"
6
#include "path.h"
7
#include "run-command.h"
8
#include "config.h"
9
#include "strbuf.h"
10
#include "environment.h"
11
#include "setup.h"
12
13
const char *find_hook(struct repository *r, const char *name)
14
0
{
15
0
  static struct strbuf path = STRBUF_INIT;
16
17
0
  int found_hook;
18
19
0
  repo_git_path_replace(r, &path, "hooks/%s", name);
20
0
  found_hook = access(path.buf, X_OK) >= 0;
21
#ifdef STRIP_EXTENSION
22
  if (!found_hook) {
23
    int err = errno;
24
25
    strbuf_addstr(&path, STRIP_EXTENSION);
26
    found_hook = access(path.buf, X_OK) >= 0;
27
    if (!found_hook)
28
      errno = err;
29
  }
30
#endif
31
32
0
  if (!found_hook) {
33
0
    if (errno == EACCES && advice_enabled(ADVICE_IGNORED_HOOK)) {
34
0
      static struct string_list advise_given = STRING_LIST_INIT_DUP;
35
36
0
      if (!string_list_lookup(&advise_given, name)) {
37
0
        string_list_insert(&advise_given, name);
38
0
        advise(_("The '%s' hook was ignored because "
39
0
           "it's not set as executable.\n"
40
0
           "You can disable this warning with "
41
0
           "`git config set advice.ignoredHook false`."),
42
0
               path.buf);
43
0
      }
44
0
    }
45
0
    return NULL;
46
0
  }
47
0
  return path.buf;
48
0
}
49
50
int hook_exists(struct repository *r, const char *name)
51
0
{
52
0
  return !!find_hook(r, name);
53
0
}
54
55
static int pick_next_hook(struct child_process *cp,
56
        struct strbuf *out UNUSED,
57
        void *pp_cb,
58
        void **pp_task_cb UNUSED)
59
0
{
60
0
  struct hook_cb_data *hook_cb = pp_cb;
61
0
  const char *hook_path = hook_cb->hook_path;
62
63
0
  if (!hook_path)
64
0
    return 0;
65
66
0
  cp->no_stdin = 1;
67
0
  strvec_pushv(&cp->env, hook_cb->options->env.v);
68
  /* reopen the file for stdin; run_command closes it. */
69
0
  if (hook_cb->options->path_to_stdin) {
70
0
    cp->no_stdin = 0;
71
0
    cp->in = xopen(hook_cb->options->path_to_stdin, O_RDONLY);
72
0
  }
73
0
  cp->stdout_to_stderr = 1;
74
0
  cp->trace2_hook_name = hook_cb->hook_name;
75
0
  cp->dir = hook_cb->options->dir;
76
77
0
  strvec_push(&cp->args, hook_path);
78
0
  strvec_pushv(&cp->args, hook_cb->options->args.v);
79
80
  /*
81
   * This pick_next_hook() will be called again, we're only
82
   * running one hook, so indicate that no more work will be
83
   * done.
84
   */
85
0
  hook_cb->hook_path = NULL;
86
87
0
  return 1;
88
0
}
89
90
static int notify_start_failure(struct strbuf *out UNUSED,
91
        void *pp_cb,
92
        void *pp_task_cp UNUSED)
93
0
{
94
0
  struct hook_cb_data *hook_cb = pp_cb;
95
96
0
  hook_cb->rc |= 1;
97
98
0
  return 1;
99
0
}
100
101
static int notify_hook_finished(int result,
102
        struct strbuf *out UNUSED,
103
        void *pp_cb,
104
        void *pp_task_cb UNUSED)
105
0
{
106
0
  struct hook_cb_data *hook_cb = pp_cb;
107
0
  struct run_hooks_opt *opt = hook_cb->options;
108
109
0
  hook_cb->rc |= result;
110
111
0
  if (opt->invoked_hook)
112
0
    *opt->invoked_hook = 1;
113
114
0
  return 0;
115
0
}
116
117
static void run_hooks_opt_clear(struct run_hooks_opt *options)
118
0
{
119
0
  strvec_clear(&options->env);
120
0
  strvec_clear(&options->args);
121
0
}
122
123
int run_hooks_opt(struct repository *r, const char *hook_name,
124
      struct run_hooks_opt *options)
125
0
{
126
0
  struct strbuf abs_path = STRBUF_INIT;
127
0
  struct hook_cb_data cb_data = {
128
0
    .rc = 0,
129
0
    .hook_name = hook_name,
130
0
    .options = options,
131
0
  };
132
0
  const char *const hook_path = find_hook(r, hook_name);
133
0
  int ret = 0;
134
0
  const struct run_process_parallel_opts opts = {
135
0
    .tr2_category = "hook",
136
0
    .tr2_label = hook_name,
137
138
0
    .processes = 1,
139
0
    .ungroup = 1,
140
141
0
    .get_next_task = pick_next_hook,
142
0
    .start_failure = notify_start_failure,
143
0
    .task_finished = notify_hook_finished,
144
145
0
    .data = &cb_data,
146
0
  };
147
148
0
  if (!options)
149
0
    BUG("a struct run_hooks_opt must be provided to run_hooks");
150
151
0
  if (options->invoked_hook)
152
0
    *options->invoked_hook = 0;
153
154
0
  if (!hook_path && !options->error_if_missing)
155
0
    goto cleanup;
156
157
0
  if (!hook_path) {
158
0
    ret = error("cannot find a hook named %s", hook_name);
159
0
    goto cleanup;
160
0
  }
161
162
0
  cb_data.hook_path = hook_path;
163
0
  if (options->dir) {
164
0
    strbuf_add_absolute_path(&abs_path, hook_path);
165
0
    cb_data.hook_path = abs_path.buf;
166
0
  }
167
168
0
  run_processes_parallel(&opts);
169
0
  ret = cb_data.rc;
170
0
cleanup:
171
0
  strbuf_release(&abs_path);
172
0
  run_hooks_opt_clear(options);
173
0
  return ret;
174
0
}
175
176
int run_hooks(struct repository *r, const char *hook_name)
177
0
{
178
0
  struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
179
180
0
  return run_hooks_opt(r, hook_name, &opt);
181
0
}
182
183
int run_hooks_l(struct repository *r, const char *hook_name, ...)
184
0
{
185
0
  struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
186
0
  va_list ap;
187
0
  const char *arg;
188
189
0
  va_start(ap, hook_name);
190
0
  while ((arg = va_arg(ap, const char *)))
191
0
    strvec_push(&opt.args, arg);
192
0
  va_end(ap);
193
194
0
  return run_hooks_opt(r, hook_name, &opt);
195
0
}