Skip to content

Commit 27da720

Browse files
nasamuffingitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent dd763e6 commit 27da720

File tree

8 files changed

+224
-0
lines changed

8 files changed

+224
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
/git-bisect--helper
2626
/git-blame
2727
/git-branch
28+
/git-bugreport
2829
/git-bundle
2930
/git-cat-file
3031
/git-check-attr

Documentation/git-bugreport.txt

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,7 @@ EXTRA_PROGRAMS =
681681
# ... and all the rest that could be moved out of bindir to gitexecdir
682682
PROGRAMS += $(EXTRA_PROGRAMS)
683683

684+
PROGRAM_OBJS += bugreport.o
684685
PROGRAM_OBJS += credential-store.o
685686
PROGRAM_OBJS += daemon.o
686687
PROGRAM_OBJS += fast-import.o
@@ -2457,6 +2458,10 @@ endif
24572458
git-%$X: %.o GIT-LDFLAGS $(GITLIBS)
24582459
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
24592460

2461+
git-bugreport$X: bugreport.o GIT-LDFLAGS $(GITLIBS)
2462+
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
2463+
$(LIBS)
2464+
24602465
git-imap-send$X: imap-send.o $(IMAP_SEND_BUILDDEPS) GIT-LDFLAGS $(GITLIBS)
24612466
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
24622467
$(IMAP_SEND_LDFLAGS) $(LIBS)

bugreport.c

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ git-archive mainporcelain
5454
git-bisect mainporcelain info
5555
git-blame ancillaryinterrogators complete
5656
git-branch mainporcelain history
57+
git-bugreport ancillaryinterrogators
5758
git-bundle mainporcelain
5859
git-cat-file plumbinginterrogators
5960
git-check-attr purehelpers

strbuf.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,10 @@ ssize_t strbuf_write(struct strbuf *sb, FILE *f)
539539
return sb->len ? fwrite(sb->buf, 1, sb->len, f) : 0;
540540
}
541541

542+
ssize_t strbuf_write_fd(struct strbuf *sb, int fd)
543+
{
544+
return sb->len ? write(fd, sb->buf, sb->len) : 0;
545+
}
542546

543547
#define STRBUF_MAXLINK (2*PATH_MAX)
544548

strbuf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
450450
* NUL bytes.
451451
*/
452452
ssize_t strbuf_write(struct strbuf *sb, FILE *stream);
453+
ssize_t strbuf_write_fd(struct strbuf *sb, int fd);
453454

454455
/**
455456
* Read a line from a FILE *, overwriting the existing contents of

t/t0091-bugreport.sh

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

0 commit comments

Comments
 (0)