Coverage Report

Created: 2024-09-08 06:23

/src/git/builtin/bugreport.c
Line
Count
Source (jump to first uncovered line)
1
#include "builtin.h"
2
#include "abspath.h"
3
#include "editor.h"
4
#include "gettext.h"
5
#include "parse-options.h"
6
#include "strbuf.h"
7
#include "help.h"
8
#include "compat/compiler.h"
9
#include "hook.h"
10
#include "hook-list.h"
11
#include "diagnose.h"
12
#include "object-file.h"
13
#include "setup.h"
14
15
static void get_system_info(struct strbuf *sys_info)
16
0
{
17
0
  struct utsname uname_info;
18
0
  char *shell = NULL;
19
20
  /* get git version from native cmd */
21
0
  strbuf_addstr(sys_info, _("git version:\n"));
22
0
  get_version_info(sys_info, 1);
23
24
  /* system call for other version info */
25
0
  strbuf_addstr(sys_info, "uname: ");
26
0
  if (uname(&uname_info))
27
0
    strbuf_addf(sys_info, _("uname() failed with error '%s' (%d)\n"),
28
0
          strerror(errno),
29
0
          errno);
30
0
  else
31
0
    strbuf_addf(sys_info, "%s %s %s %s\n",
32
0
          uname_info.sysname,
33
0
          uname_info.release,
34
0
          uname_info.version,
35
0
          uname_info.machine);
36
37
0
  strbuf_addstr(sys_info, _("compiler info: "));
38
0
  get_compiler_info(sys_info);
39
40
0
  strbuf_addstr(sys_info, _("libc info: "));
41
0
  get_libc_info(sys_info);
42
43
0
  shell = getenv("SHELL");
44
0
  strbuf_addf(sys_info, "$SHELL (typically, interactive shell): %s\n",
45
0
        shell ? shell : "<unset>");
46
0
}
47
48
static void get_populated_hooks(struct strbuf *hook_info, int nongit)
49
0
{
50
0
  const char **p;
51
52
0
  if (nongit) {
53
0
    strbuf_addstr(hook_info,
54
0
      _("not run from a git repository - no hooks to show\n"));
55
0
    return;
56
0
  }
57
58
0
  for (p = hook_name_list; *p; p++) {
59
0
    const char *hook = *p;
60
61
0
    if (hook_exists(the_repository, hook))
62
0
      strbuf_addf(hook_info, "%s\n", hook);
63
0
  }
64
0
}
65
66
static const char * const bugreport_usage[] = {
67
  N_("git bugreport [(-o | --output-directory) <path>]\n"
68
     "              [(-s | --suffix) <format> | --no-suffix]\n"
69
     "              [--diagnose[=<mode>]]"),
70
  NULL
71
};
72
73
static int get_bug_template(struct strbuf *template)
74
0
{
75
0
  const char template_text[] = N_(
76
0
"Thank you for filling out a Git bug report!\n"
77
0
"Please answer the following questions to help us understand your issue.\n"
78
0
"\n"
79
0
"What did you do before the bug happened? (Steps to reproduce your issue)\n"
80
0
"\n"
81
0
"What did you expect to happen? (Expected behavior)\n"
82
0
"\n"
83
0
"What happened instead? (Actual behavior)\n"
84
0
"\n"
85
0
"What's different between what you expected and what actually happened?\n"
86
0
"\n"
87
0
"Anything else you want to add:\n"
88
0
"\n"
89
0
"Please review the rest of the bug report below.\n"
90
0
"You can delete any lines you don't wish to share.\n");
91
92
0
  strbuf_addstr(template, _(template_text));
93
0
  return 0;
94
0
}
95
96
static void get_header(struct strbuf *buf, const char *title)
97
0
{
98
0
  strbuf_addf(buf, "\n\n[%s]\n", title);
99
0
}
100
101
int cmd_bugreport(int argc, const char **argv, const char *prefix)
102
0
{
103
0
  struct strbuf buffer = STRBUF_INIT;
104
0
  struct strbuf report_path = STRBUF_INIT;
105
0
  int report = -1;
106
0
  time_t now = time(NULL);
107
0
  struct tm tm;
108
0
  enum diagnose_mode diagnose = DIAGNOSE_NONE;
109
0
  char *option_output = NULL;
110
0
  const char *option_suffix = "%Y-%m-%d-%H%M";
111
0
  const char *user_relative_path = NULL;
112
0
  char *prefixed_filename;
113
0
  size_t output_path_len;
114
0
  int ret;
115
116
0
  const struct option bugreport_options[] = {
117
0
    OPT_CALLBACK_F(0, "diagnose", &diagnose, N_("mode"),
118
0
             N_("create an additional zip archive of detailed diagnostics (default 'stats')"),
119
0
             PARSE_OPT_OPTARG, option_parse_diagnose),
120
0
    OPT_STRING('o', "output-directory", &option_output, N_("path"),
121
0
         N_("specify a destination for the bugreport file(s)")),
122
0
    OPT_STRING('s', "suffix", &option_suffix, N_("format"),
123
0
         N_("specify a strftime format suffix for the filename(s)")),
124
0
    OPT_END()
125
0
  };
126
127
0
  argc = parse_options(argc, argv, prefix, bugreport_options,
128
0
           bugreport_usage, 0);
129
130
0
  if (argc) {
131
0
    error(_("unknown argument `%s'"), argv[0]);
132
0
    usage(bugreport_usage[0]);
133
0
  }
134
135
  /* Prepare the path to put the result */
136
0
  prefixed_filename = prefix_filename(prefix,
137
0
              option_output ? option_output : "");
138
0
  strbuf_addstr(&report_path, prefixed_filename);
139
0
  strbuf_complete(&report_path, '/');
140
0
  output_path_len = report_path.len;
141
142
0
  strbuf_addstr(&report_path, "git-bugreport");
143
0
  if (option_suffix) {
144
0
    strbuf_addch(&report_path, '-');
145
0
    strbuf_addftime(&report_path, option_suffix, localtime_r(&now, &tm), 0, 0);
146
0
  }
147
0
  strbuf_addstr(&report_path, ".txt");
148
149
0
  switch (safe_create_leading_directories(report_path.buf)) {
150
0
  case SCLD_OK:
151
0
  case SCLD_EXISTS:
152
0
    break;
153
0
  default:
154
0
    die(_("could not create leading directories for '%s'"),
155
0
        report_path.buf);
156
0
  }
157
158
  /* Prepare diagnostics, if requested */
159
0
  if (diagnose != DIAGNOSE_NONE) {
160
0
    struct strbuf zip_path = STRBUF_INIT;
161
0
    strbuf_add(&zip_path, report_path.buf, output_path_len);
162
0
    strbuf_addstr(&zip_path, "git-diagnostics-");
163
0
    strbuf_addftime(&zip_path, option_suffix, localtime_r(&now, &tm), 0, 0);
164
0
    strbuf_addstr(&zip_path, ".zip");
165
166
0
    if (create_diagnostics_archive(&zip_path, diagnose))
167
0
      die_errno(_("unable to create diagnostics archive %s"), zip_path.buf);
168
169
0
    strbuf_release(&zip_path);
170
0
  }
171
172
  /* Prepare the report contents */
173
0
  get_bug_template(&buffer);
174
175
0
  get_header(&buffer, _("System Info"));
176
0
  get_system_info(&buffer);
177
178
0
  get_header(&buffer, _("Enabled Hooks"));
179
0
  get_populated_hooks(&buffer, !startup_info->have_repository);
180
181
  /* fopen doesn't offer us an O_EXCL alternative, except with glibc. */
182
0
  report = xopen(report_path.buf, O_CREAT | O_EXCL | O_WRONLY, 0666);
183
184
0
  if (write_in_full(report, buffer.buf, buffer.len) < 0)
185
0
    die_errno(_("unable to write to %s"), report_path.buf);
186
187
0
  close(report);
188
189
  /*
190
   * We want to print the path relative to the user, but we still need the
191
   * path relative to us to give to the editor.
192
   */
193
0
  if (!(prefix && skip_prefix(report_path.buf, prefix, &user_relative_path)))
194
0
    user_relative_path = report_path.buf;
195
0
  fprintf(stderr, _("Created new report at '%s'.\n"),
196
0
    user_relative_path);
197
198
0
  free(prefixed_filename);
199
0
  strbuf_release(&buffer);
200
201
0
  ret = !!launch_editor(report_path.buf, NULL, NULL);
202
0
  strbuf_release(&report_path);
203
0
  return ret;
204
0
}