@matheusbsbs / matheus-bagliano / commits / 238b439d69
Fork 0

bugreport: add tool to generate debugging info

Teach Git how to prompt the user for a good bug report: reproduction steps, expected behavior, and actual behavior. Later, Git can learn how to collect some diagnostic information from the repository. If users can send us a well-written bug report which contains diagnostic information we would otherwise need to ask the user for, we can reduce the number of question-and-answer round trips between the reporter and the Git contributor. Users may also wish to send a report like this to their local "Git expert" if they have put their repository into a state they are confused by. Signed-off-by: Emily Shaffer <emilyshaffer@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Emily Shaffer committed Apr 16, 2020 at 14:18 UTC 238b439d69890980dafc5154895d425cb4cf4a5e
8 files changed +224
.gitignore
+1
@@ -25,6 +25,7 @@
25 /git-bisect--helper
26 /git-blame
27 /git-branch
28 +/git-bugreport
29 /git-bundle
30 /git-cat-file
31 /git-check-attr
Documentation/git-bugreport.txt new
+46
@@ -0,0 +1,46 @@
1 +git-bugreport(1)
2 +================
3 +
4 +NAME
5 +----
6 +git-bugreport - Collect information for user to file a bug report
7 +
8 +SYNOPSIS
9 +--------
10 +[verse]
11 +'git bugreport' [(-o | --output-directory) <path>] [(-s | --suffix) <format>]
12 +
13 +DESCRIPTION
14 +-----------
15 +Captures information about the user's machine, Git client, and repository state,
16 +as well as a form requesting information about the behavior the user observed,
17 +into a single text file which the user can then share, for example to the Git
18 +mailing list, in order to report an observed bug.
19 +
20 +The following information is requested from the user:
21 +
22 + - Reproduction steps
23 + - Expected behavior
24 + - Actual behavior
25 +
26 +This tool is invoked via the typical Git setup process, which means that in some
27 +cases, it might not be able to launch - for example, if a relevant config file
28 +is unreadable. In this kind of scenario, it may be helpful to manually gather
29 +the kind of information listed above when manually asking for help.
30 +
31 +OPTIONS
32 +-------
33 +-o <path>::
34 +--output-directory <path>::
35 + Place the resulting bug report file in `<path>` instead of the root of
36 + the Git repository.
37 +
38 +-s <format>::
39 +--suffix <format>::
40 + Specify an alternate suffix for the bugreport name, to create a file
41 + named 'git-bugreport-<formatted suffix>'. This should take the form of a
42 + link:strftime[3] format string; the current local time will be used.
43 +
44 +GIT
45 +---
46 +Part of the linkgit:git[1] suite
Makefile
+5
@@ -681,6 +681,7 @@ EXTRA_PROGRAMS =
681 # ... and all the rest that could be moved out of bindir to gitexecdir
682 PROGRAMS += $(EXTRA_PROGRAMS)
683
684 +PROGRAM_OBJS += bugreport.o
685 PROGRAM_OBJS += credential-store.o
686 PROGRAM_OBJS += daemon.o
687 PROGRAM_OBJS += fast-import.o
@@ -2457,6 +2458,10 @@ endif
2458 git-%$X: %.o GIT-LDFLAGS $(GITLIBS)
2459 $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
2460
2461 +git-bugreport$X: bugreport.o GIT-LDFLAGS $(GITLIBS)
2462 + $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
2463 + $(LIBS)
2464 +
2465 git-imap-send$X: imap-send.o $(IMAP_SEND_BUILDDEPS) GIT-LDFLAGS $(GITLIBS)
2466 $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
2467 $(IMAP_SEND_LDFLAGS) $(LIBS)
bugreport.c new
+105
@@ -0,0 +1,105 @@
1 +#include "builtin.h"
2 +#include "parse-options.h"
3 +#include "stdio.h"
4 +#include "strbuf.h"
5 +#include "time.h"
6 +
7 +static const char * const bugreport_usage[] = {
8 + N_("git bugreport [-o|--output-directory <file>] [-s|--suffix <format>]"),
9 + NULL
10 +};
11 +
12 +static int get_bug_template(struct strbuf *template)
13 +{
14 + const char template_text[] = N_(
15 +"Thank you for filling out a Git bug report!\n"
16 +"Please answer the following questions to help us understand your issue.\n"
17 +"\n"
18 +"What did you do before the bug happened? (Steps to reproduce your issue)\n"
19 +"\n"
20 +"What did you expect to happen? (Expected behavior)\n"
21 +"\n"
22 +"What happened instead? (Actual behavior)\n"
23 +"\n"
24 +"What's different between what you expected and what actually happened?\n"
25 +"\n"
26 +"Anything else you want to add:\n"
27 +"\n"
28 +"Please review the rest of the bug report below.\n"
29 +"You can delete any lines you don't wish to share.\n");
30 +
31 + strbuf_addstr(template, _(template_text));
32 + return 0;
33 +}
34 +
35 +int cmd_main(int argc, const char **argv)
36 +{
37 + struct strbuf buffer = STRBUF_INIT;
38 + struct strbuf report_path = STRBUF_INIT;
39 + int report = -1;
40 + time_t now = time(NULL);
41 + char *option_output = NULL;
42 + char *option_suffix = "%Y-%m-%d-%H%M";
43 + int nongit_ok = 0;
44 + const char *prefix = NULL;
45 + const char *user_relative_path = NULL;
46 +
47 + const struct option bugreport_options[] = {
48 + OPT_STRING('o', "output-directory", &option_output, N_("path"),
49 + N_("specify a destination for the bugreport file")),
50 + OPT_STRING('s', "suffix", &option_suffix, N_("format"),
51 + N_("specify a strftime format suffix for the filename")),
52 + OPT_END()
53 + };
54 +
55 + prefix = setup_git_directory_gently(&nongit_ok);
56 +
57 + argc = parse_options(argc, argv, prefix, bugreport_options,
58 + bugreport_usage, 0);
59 +
60 + /* Prepare the path to put the result */
61 + strbuf_addstr(&report_path,
62 + prefix_filename(prefix,
63 + option_output ? option_output : ""));
64 + strbuf_complete(&report_path, '/');
65 +
66 + strbuf_addstr(&report_path, "git-bugreport-");
67 + strbuf_addftime(&report_path, option_suffix, localtime(&now), 0, 0);
68 + strbuf_addstr(&report_path, ".txt");
69 +
70 + switch (safe_create_leading_directories(report_path.buf)) {
71 + case SCLD_OK:
72 + case SCLD_EXISTS:
73 + break;
74 + default:
75 + die(_("could not create leading directories for '%s'"),
76 + report_path.buf);
77 + }
78 +
79 + /* Prepare the report contents */
80 + get_bug_template(&buffer);
81 +
82 + /* fopen doesn't offer us an O_EXCL alternative, except with glibc. */
83 + report = open(report_path.buf, O_CREAT | O_EXCL | O_WRONLY, 0666);
84 +
85 + if (report < 0) {
86 + UNLEAK(report_path);
87 + die(_("couldn't create a new file at '%s'"), report_path.buf);
88 + }
89 +
90 + strbuf_write_fd(&buffer, report);
91 + close(report);
92 +
93 + /*
94 + * We want to print the path relative to the user, but we still need the
95 + * path relative to us to give to the editor.
96 + */
97 + if (!(prefix && skip_prefix(report_path.buf, prefix, &user_relative_path)))
98 + user_relative_path = report_path.buf;
99 + fprintf(stderr, _("Created new report at '%s'.\n"),
100 + user_relative_path);
101 +
102 + UNLEAK(buffer);
103 + UNLEAK(report_path);
104 + return !!launch_editor(report_path.buf, NULL, NULL);
105 +}
command-list.txt
+1
@@ -54,6 +54,7 @@ git-archive mainporcelain
54 git-bisect mainporcelain info
55 git-blame ancillaryinterrogators complete
56 git-branch mainporcelain history
57 +git-bugreport ancillaryinterrogators
58 git-bundle mainporcelain
59 git-cat-file plumbinginterrogators
60 git-check-attr purehelpers
strbuf.c
+4
@@ -539,6 +539,10 @@ ssize_t strbuf_write(struct strbuf *sb, FILE *f)
539 return sb->len ? fwrite(sb->buf, 1, sb->len, f) : 0;
540 }
541
542 +ssize_t strbuf_write_fd(struct strbuf *sb, int fd)
543 +{
544 + return sb->len ? write(fd, sb->buf, sb->len) : 0;
545 +}
546
547 #define STRBUF_MAXLINK (2*PATH_MAX)
548
strbuf.h
+1
@@ -450,6 +450,7 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
450 * NUL bytes.
451 */
452 ssize_t strbuf_write(struct strbuf *sb, FILE *stream);
453 +ssize_t strbuf_write_fd(struct strbuf *sb, int fd);
454
455 /**
456 * Read a line from a FILE *, overwriting the existing contents of
t/t0091-bugreport.sh new
+61
@@ -0,0 +1,61 @@
1 +#!/bin/sh
2 +
3 +test_description='git bugreport'
4 +
5 +. ./test-lib.sh
6 +
7 +# Headers "[System Info]" will be followed by a non-empty line if we put some
8 +# information there; we can make sure all our headers were followed by some
9 +# information to check if the command was successful.
10 +HEADER_PATTERN="^\[.*\]$"
11 +
12 +check_all_headers_populated () {
13 + while read -r line
14 + do
15 + if test "$(grep "$HEADER_PATTERN" "$line")"
16 + then
17 + echo "$line"
18 + read -r nextline
19 + if test -z "$nextline"; then
20 + return 1;
21 + fi
22 + fi
23 + done
24 +}
25 +
26 +test_expect_success 'creates a report with content in the right places' '
27 + test_when_finished rm git-bugreport-check-headers.txt &&
28 + git bugreport -s check-headers &&
29 + check_all_headers_populated <git-bugreport-check-headers.txt
30 +'
31 +
32 +test_expect_success 'dies if file with same name as report already exists' '
33 + test_when_finished rm git-bugreport-duplicate.txt &&
34 + >>git-bugreport-duplicate.txt &&
35 + test_must_fail git bugreport --suffix duplicate
36 +'
37 +
38 +test_expect_success '--output-directory puts the report in the provided dir' '
39 + test_when_finished rm -fr foo/ &&
40 + git bugreport -o foo/ &&
41 + test_path_is_file foo/git-bugreport-*
42 +'
43 +
44 +test_expect_success 'incorrect arguments abort with usage' '
45 + test_must_fail git bugreport --false 2>output &&
46 + test_i18ngrep usage output &&
47 + test_path_is_missing git-bugreport-*
48 +'
49 +
50 +test_expect_success 'runs outside of a git dir' '
51 + test_when_finished rm non-repo/git-bugreport-* &&
52 + nongit git bugreport
53 +'
54 +
55 +test_expect_success 'can create leading directories outside of a git dir' '
56 + test_when_finished rm -fr foo/bar/baz &&
57 + nongit git bugreport -o foo/bar/baz
58 +'
59 +
60 +
61 +test_done