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