]> git.ipfire.org Git - thirdparty/git.git/blob - builtin/bugreport.c
Merge branch 'ea/blame-use-oideq'
[thirdparty/git.git] / builtin / bugreport.c
1 #include "builtin.h"
2 #include "parse-options.h"
3 #include "strbuf.h"
4 #include "help.h"
5 #include "compat/compiler.h"
6 #include "run-command.h"
7
8
9 static void get_system_info(struct strbuf *sys_info)
10 {
11 struct utsname uname_info;
12 char *shell = NULL;
13
14 /* get git version from native cmd */
15 strbuf_addstr(sys_info, _("git version:\n"));
16 get_version_info(sys_info, 1);
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);
30
31 strbuf_addstr(sys_info, _("compiler info: "));
32 get_compiler_info(sys_info);
33
34 strbuf_addstr(sys_info, _("libc info: "));
35 get_libc_info(sys_info);
36
37 shell = getenv("SHELL");
38 strbuf_addf(sys_info, "$SHELL (typically, interactive shell): %s\n",
39 shell ? shell : "<unset>");
40 }
41
42 static 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
89 static const char * const bugreport_usage[] = {
90 N_("git bugreport [-o|--output-directory <file>] [-s|--suffix <format>]"),
91 NULL
92 };
93
94 static 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
117 static void get_header(struct strbuf *buf, const char *title)
118 {
119 strbuf_addf(buf, "\n\n[%s]\n", title);
120 }
121
122 int cmd_bugreport(int argc, const char **argv, const char *prefix)
123 {
124 struct strbuf buffer = STRBUF_INIT;
125 struct strbuf report_path = STRBUF_INIT;
126 int report = -1;
127 time_t now = time(NULL);
128 char *option_output = NULL;
129 char *option_suffix = "%Y-%m-%d-%H%M";
130 const char *user_relative_path = NULL;
131
132 const struct option bugreport_options[] = {
133 OPT_STRING('o', "output-directory", &option_output, N_("path"),
134 N_("specify a destination for the bugreport file")),
135 OPT_STRING('s', "suffix", &option_suffix, N_("format"),
136 N_("specify a strftime format suffix for the filename")),
137 OPT_END()
138 };
139
140 argc = parse_options(argc, argv, prefix, bugreport_options,
141 bugreport_usage, 0);
142
143 /* Prepare the path to put the result */
144 strbuf_addstr(&report_path,
145 prefix_filename(prefix,
146 option_output ? option_output : ""));
147 strbuf_complete(&report_path, '/');
148
149 strbuf_addstr(&report_path, "git-bugreport-");
150 strbuf_addftime(&report_path, option_suffix, localtime(&now), 0, 0);
151 strbuf_addstr(&report_path, ".txt");
152
153 switch (safe_create_leading_directories(report_path.buf)) {
154 case SCLD_OK:
155 case SCLD_EXISTS:
156 break;
157 default:
158 die(_("could not create leading directories for '%s'"),
159 report_path.buf);
160 }
161
162 /* Prepare the report contents */
163 get_bug_template(&buffer);
164
165 get_header(&buffer, _("System Info"));
166 get_system_info(&buffer);
167
168 get_header(&buffer, _("Enabled Hooks"));
169 get_populated_hooks(&buffer, !startup_info->have_repository);
170
171 /* fopen doesn't offer us an O_EXCL alternative, except with glibc. */
172 report = open(report_path.buf, O_CREAT | O_EXCL | O_WRONLY, 0666);
173
174 if (report < 0)
175 die(_("couldn't create a new file at '%s'"), report_path.buf);
176
177 if (write_in_full(report, buffer.buf, buffer.len) < 0)
178 die_errno(_("unable to write to %s"), report_path.buf);
179
180 close(report);
181
182 /*
183 * We want to print the path relative to the user, but we still need the
184 * path relative to us to give to the editor.
185 */
186 if (!(prefix && skip_prefix(report_path.buf, prefix, &user_relative_path)))
187 user_relative_path = report_path.buf;
188 fprintf(stderr, _("Created new report at '%s'.\n"),
189 user_relative_path);
190
191 UNLEAK(buffer);
192 UNLEAK(report_path);
193 return !!launch_editor(report_path.buf, NULL, NULL);
194 }