Merge branch 'bc/http-push-flagsfix'
[git] / bugreport.c
1 #include "cache.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_main(int argc, const char **argv)
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         int nongit_ok = 0;
131         const char *prefix = NULL;
132         const char *user_relative_path = NULL;
133
134         const struct option bugreport_options[] = {
135                 OPT_STRING('o', "output-directory", &option_output, N_("path"),
136                            N_("specify a destination for the bugreport file")),
137                 OPT_STRING('s', "suffix", &option_suffix, N_("format"),
138                            N_("specify a strftime format suffix for the filename")),
139                 OPT_END()
140         };
141
142         prefix = setup_git_directory_gently(&nongit_ok);
143
144         argc = parse_options(argc, argv, prefix, bugreport_options,
145                              bugreport_usage, 0);
146
147         /* Prepare the path to put the result */
148         strbuf_addstr(&report_path,
149                       prefix_filename(prefix,
150                                       option_output ? option_output : ""));
151         strbuf_complete(&report_path, '/');
152
153         strbuf_addstr(&report_path, "git-bugreport-");
154         strbuf_addftime(&report_path, option_suffix, localtime(&now), 0, 0);
155         strbuf_addstr(&report_path, ".txt");
156
157         switch (safe_create_leading_directories(report_path.buf)) {
158         case SCLD_OK:
159         case SCLD_EXISTS:
160                 break;
161         default:
162                 die(_("could not create leading directories for '%s'"),
163                     report_path.buf);
164         }
165
166         /* Prepare the report contents */
167         get_bug_template(&buffer);
168
169         get_header(&buffer, _("System Info"));
170         get_system_info(&buffer);
171
172         get_header(&buffer, _("Enabled Hooks"));
173         get_populated_hooks(&buffer, nongit_ok);
174
175         /* fopen doesn't offer us an O_EXCL alternative, except with glibc. */
176         report = open(report_path.buf, O_CREAT | O_EXCL | O_WRONLY, 0666);
177
178         if (report < 0) {
179                 UNLEAK(report_path);
180                 die(_("couldn't create a new file at '%s'"), report_path.buf);
181         }
182
183         if (write_in_full(report, buffer.buf, buffer.len) < 0)
184                 die_errno(_("unable to write to %s"), report_path.buf);
185
186         close(report);
187
188         /*
189          * We want to print the path relative to the user, but we still need the
190          * path relative to us to give to the editor.
191          */
192         if (!(prefix && skip_prefix(report_path.buf, prefix, &user_relative_path)))
193                 user_relative_path = report_path.buf;
194         fprintf(stderr, _("Created new report at '%s'.\n"),
195                 user_relative_path);
196
197         UNLEAK(buffer);
198         UNLEAK(report_path);
199         return !!launch_editor(report_path.buf, NULL, NULL);
200 }