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