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