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