]> git.ipfire.org Git - thirdparty/git.git/blame - builtin/bugreport.c
Merge branch 'jc/diffcore-rotate'
[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"
788a7760
ES
6#include "run-command.h"
7
617d5719
ES
8
9static void get_system_info(struct strbuf *sys_info)
10{
1411914a 11 struct utsname uname_info;
4a4804ed 12 char *shell = NULL;
1411914a 13
617d5719
ES
14 /* get git version from native cmd */
15 strbuf_addstr(sys_info, _("git version:\n"));
16 get_version_info(sys_info, 1);
1411914a
ES
17
18 /* system call for other version info */
19 strbuf_addstr(sys_info, "uname: ");
20 if (uname(&uname_info))
21 strbuf_addf(sys_info, _("uname() failed with error '%s' (%d)\n"),
22 strerror(errno),
23 errno);
24 else
25 strbuf_addf(sys_info, "%s %s %s %s\n",
26 uname_info.sysname,
27 uname_info.release,
28 uname_info.version,
29 uname_info.machine);
69bcbbce
ES
30
31 strbuf_addstr(sys_info, _("compiler info: "));
32 get_compiler_info(sys_info);
4a4804ed 33
69bcbbce
ES
34 strbuf_addstr(sys_info, _("libc info: "));
35 get_libc_info(sys_info);
4a4804ed
ES
36
37 shell = getenv("SHELL");
38 strbuf_addf(sys_info, "$SHELL (typically, interactive shell): %s\n",
39 shell ? shell : "<unset>");
617d5719 40}
238b439d 41
788a7760
ES
42static void get_populated_hooks(struct strbuf *hook_info, int nongit)
43{
44 /*
45 * NEEDSWORK: Doesn't look like there is a list of all possible hooks;
46 * so below is a transcription of `git help hooks`. Later, this should
47 * be replaced with some programmatically generated list (generated from
48 * doc or else taken from some library which tells us about all the
49 * hooks)
50 */
51 static const char *hook[] = {
52 "applypatch-msg",
53 "pre-applypatch",
54 "post-applypatch",
55 "pre-commit",
56 "pre-merge-commit",
57 "prepare-commit-msg",
58 "commit-msg",
59 "post-commit",
60 "pre-rebase",
61 "post-checkout",
62 "post-merge",
63 "pre-push",
64 "pre-receive",
65 "update",
66 "post-receive",
67 "post-update",
68 "push-to-checkout",
69 "pre-auto-gc",
70 "post-rewrite",
71 "sendemail-validate",
72 "fsmonitor-watchman",
73 "p4-pre-submit",
74 "post-index-change",
75 };
76 int i;
77
78 if (nongit) {
79 strbuf_addstr(hook_info,
80 _("not run from a git repository - no hooks to show\n"));
81 return;
82 }
83
84 for (i = 0; i < ARRAY_SIZE(hook); i++)
85 if (find_hook(hook[i]))
86 strbuf_addf(hook_info, "%s\n", hook[i]);
87}
88
238b439d
ES
89static const char * const bugreport_usage[] = {
90 N_("git bugreport [-o|--output-directory <file>] [-s|--suffix <format>]"),
91 NULL
92};
93
94static int get_bug_template(struct strbuf *template)
95{
96 const char template_text[] = N_(
97"Thank you for filling out a Git bug report!\n"
98"Please answer the following questions to help us understand your issue.\n"
99"\n"
100"What did you do before the bug happened? (Steps to reproduce your issue)\n"
101"\n"
102"What did you expect to happen? (Expected behavior)\n"
103"\n"
104"What happened instead? (Actual behavior)\n"
105"\n"
106"What's different between what you expected and what actually happened?\n"
107"\n"
108"Anything else you want to add:\n"
109"\n"
110"Please review the rest of the bug report below.\n"
111"You can delete any lines you don't wish to share.\n");
112
113 strbuf_addstr(template, _(template_text));
114 return 0;
115}
116
617d5719
ES
117static void get_header(struct strbuf *buf, const char *title)
118{
119 strbuf_addf(buf, "\n\n[%s]\n", title);
120}
121
d7a5649c 122int cmd_bugreport(int argc, const char **argv, const char *prefix)
238b439d
ES
123{
124 struct strbuf buffer = STRBUF_INIT;
125 struct strbuf report_path = STRBUF_INIT;
126 int report = -1;
127 time_t now = time(NULL);
4f6460df 128 struct tm tm;
238b439d
ES
129 char *option_output = NULL;
130 char *option_suffix = "%Y-%m-%d-%H%M";
238b439d
ES
131 const char *user_relative_path = NULL;
132
133 const struct option bugreport_options[] = {
134 OPT_STRING('o', "output-directory", &option_output, N_("path"),
135 N_("specify a destination for the bugreport file")),
136 OPT_STRING('s', "suffix", &option_suffix, N_("format"),
137 N_("specify a strftime format suffix for the filename")),
138 OPT_END()
139 };
140
238b439d
ES
141 argc = parse_options(argc, argv, prefix, bugreport_options,
142 bugreport_usage, 0);
143
144 /* Prepare the path to put the result */
145 strbuf_addstr(&report_path,
146 prefix_filename(prefix,
147 option_output ? option_output : ""));
148 strbuf_complete(&report_path, '/');
149
150 strbuf_addstr(&report_path, "git-bugreport-");
4f6460df 151 strbuf_addftime(&report_path, option_suffix, localtime_r(&now, &tm), 0, 0);
238b439d
ES
152 strbuf_addstr(&report_path, ".txt");
153
154 switch (safe_create_leading_directories(report_path.buf)) {
155 case SCLD_OK:
156 case SCLD_EXISTS:
157 break;
158 default:
159 die(_("could not create leading directories for '%s'"),
160 report_path.buf);
161 }
162
163 /* Prepare the report contents */
164 get_bug_template(&buffer);
165
617d5719
ES
166 get_header(&buffer, _("System Info"));
167 get_system_info(&buffer);
168
788a7760 169 get_header(&buffer, _("Enabled Hooks"));
d7a5649c 170 get_populated_hooks(&buffer, !startup_info->have_repository);
788a7760 171
238b439d
ES
172 /* fopen doesn't offer us an O_EXCL alternative, except with glibc. */
173 report = open(report_path.buf, O_CREAT | O_EXCL | O_WRONLY, 0666);
174
d5e1961c 175 if (report < 0)
238b439d 176 die(_("couldn't create a new file at '%s'"), report_path.buf);
238b439d 177
f64b6a1f
RB
178 if (write_in_full(report, buffer.buf, buffer.len) < 0)
179 die_errno(_("unable to write to %s"), report_path.buf);
180
238b439d
ES
181 close(report);
182
183 /*
184 * We want to print the path relative to the user, but we still need the
185 * path relative to us to give to the editor.
186 */
187 if (!(prefix && skip_prefix(report_path.buf, prefix, &user_relative_path)))
188 user_relative_path = report_path.buf;
189 fprintf(stderr, _("Created new report at '%s'.\n"),
190 user_relative_path);
191
192 UNLEAK(buffer);
193 UNLEAK(report_path);
194 return !!launch_editor(report_path.buf, NULL, NULL);
195}