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