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>
This commit is contained in:
parent
709df95b78
commit
238b439d69
1
.gitignore
vendored
1
.gitignore
vendored
@ -25,6 +25,7 @@
|
||||
/git-bisect--helper
|
||||
/git-blame
|
||||
/git-branch
|
||||
/git-bugreport
|
||||
/git-bundle
|
||||
/git-cat-file
|
||||
/git-check-attr
|
||||
|
46
Documentation/git-bugreport.txt
Normal file
46
Documentation/git-bugreport.txt
Normal file
@ -0,0 +1,46 @@
|
||||
git-bugreport(1)
|
||||
================
|
||||
|
||||
NAME
|
||||
----
|
||||
git-bugreport - Collect information for user to file a bug report
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git bugreport' [(-o | --output-directory) <path>] [(-s | --suffix) <format>]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Captures information about the user's machine, Git client, and repository state,
|
||||
as well as a form requesting information about the behavior the user observed,
|
||||
into a single text file which the user can then share, for example to the Git
|
||||
mailing list, in order to report an observed bug.
|
||||
|
||||
The following information is requested from the user:
|
||||
|
||||
- Reproduction steps
|
||||
- Expected behavior
|
||||
- Actual behavior
|
||||
|
||||
This tool is invoked via the typical Git setup process, which means that in some
|
||||
cases, it might not be able to launch - for example, if a relevant config file
|
||||
is unreadable. In this kind of scenario, it may be helpful to manually gather
|
||||
the kind of information listed above when manually asking for help.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-o <path>::
|
||||
--output-directory <path>::
|
||||
Place the resulting bug report file in `<path>` instead of the root of
|
||||
the Git repository.
|
||||
|
||||
-s <format>::
|
||||
--suffix <format>::
|
||||
Specify an alternate suffix for the bugreport name, to create a file
|
||||
named 'git-bugreport-<formatted suffix>'. This should take the form of a
|
||||
link:strftime[3] format string; the current local time will be used.
|
||||
|
||||
GIT
|
||||
---
|
||||
Part of the linkgit:git[1] suite
|
5
Makefile
5
Makefile
@ -681,6 +681,7 @@ EXTRA_PROGRAMS =
|
||||
# ... and all the rest that could be moved out of bindir to gitexecdir
|
||||
PROGRAMS += $(EXTRA_PROGRAMS)
|
||||
|
||||
PROGRAM_OBJS += bugreport.o
|
||||
PROGRAM_OBJS += credential-store.o
|
||||
PROGRAM_OBJS += daemon.o
|
||||
PROGRAM_OBJS += fast-import.o
|
||||
@ -2457,6 +2458,10 @@ endif
|
||||
git-%$X: %.o GIT-LDFLAGS $(GITLIBS)
|
||||
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
|
||||
|
||||
git-bugreport$X: bugreport.o GIT-LDFLAGS $(GITLIBS)
|
||||
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
|
||||
$(LIBS)
|
||||
|
||||
git-imap-send$X: imap-send.o $(IMAP_SEND_BUILDDEPS) GIT-LDFLAGS $(GITLIBS)
|
||||
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
|
||||
$(IMAP_SEND_LDFLAGS) $(LIBS)
|
||||
|
105
bugreport.c
Normal file
105
bugreport.c
Normal file
@ -0,0 +1,105 @@
|
||||
#include "builtin.h"
|
||||
#include "parse-options.h"
|
||||
#include "stdio.h"
|
||||
#include "strbuf.h"
|
||||
#include "time.h"
|
||||
|
||||
static const char * const bugreport_usage[] = {
|
||||
N_("git bugreport [-o|--output-directory <file>] [-s|--suffix <format>]"),
|
||||
NULL
|
||||
};
|
||||
|
||||
static int get_bug_template(struct strbuf *template)
|
||||
{
|
||||
const char template_text[] = N_(
|
||||
"Thank you for filling out a Git bug report!\n"
|
||||
"Please answer the following questions to help us understand your issue.\n"
|
||||
"\n"
|
||||
"What did you do before the bug happened? (Steps to reproduce your issue)\n"
|
||||
"\n"
|
||||
"What did you expect to happen? (Expected behavior)\n"
|
||||
"\n"
|
||||
"What happened instead? (Actual behavior)\n"
|
||||
"\n"
|
||||
"What's different between what you expected and what actually happened?\n"
|
||||
"\n"
|
||||
"Anything else you want to add:\n"
|
||||
"\n"
|
||||
"Please review the rest of the bug report below.\n"
|
||||
"You can delete any lines you don't wish to share.\n");
|
||||
|
||||
strbuf_addstr(template, _(template_text));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_main(int argc, const char **argv)
|
||||
{
|
||||
struct strbuf buffer = STRBUF_INIT;
|
||||
struct strbuf report_path = STRBUF_INIT;
|
||||
int report = -1;
|
||||
time_t now = time(NULL);
|
||||
char *option_output = NULL;
|
||||
char *option_suffix = "%Y-%m-%d-%H%M";
|
||||
int nongit_ok = 0;
|
||||
const char *prefix = NULL;
|
||||
const char *user_relative_path = NULL;
|
||||
|
||||
const struct option bugreport_options[] = {
|
||||
OPT_STRING('o', "output-directory", &option_output, N_("path"),
|
||||
N_("specify a destination for the bugreport file")),
|
||||
OPT_STRING('s', "suffix", &option_suffix, N_("format"),
|
||||
N_("specify a strftime format suffix for the filename")),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
prefix = setup_git_directory_gently(&nongit_ok);
|
||||
|
||||
argc = parse_options(argc, argv, prefix, bugreport_options,
|
||||
bugreport_usage, 0);
|
||||
|
||||
/* Prepare the path to put the result */
|
||||
strbuf_addstr(&report_path,
|
||||
prefix_filename(prefix,
|
||||
option_output ? option_output : ""));
|
||||
strbuf_complete(&report_path, '/');
|
||||
|
||||
strbuf_addstr(&report_path, "git-bugreport-");
|
||||
strbuf_addftime(&report_path, option_suffix, localtime(&now), 0, 0);
|
||||
strbuf_addstr(&report_path, ".txt");
|
||||
|
||||
switch (safe_create_leading_directories(report_path.buf)) {
|
||||
case SCLD_OK:
|
||||
case SCLD_EXISTS:
|
||||
break;
|
||||
default:
|
||||
die(_("could not create leading directories for '%s'"),
|
||||
report_path.buf);
|
||||
}
|
||||
|
||||
/* Prepare the report contents */
|
||||
get_bug_template(&buffer);
|
||||
|
||||
/* fopen doesn't offer us an O_EXCL alternative, except with glibc. */
|
||||
report = open(report_path.buf, O_CREAT | O_EXCL | O_WRONLY, 0666);
|
||||
|
||||
if (report < 0) {
|
||||
UNLEAK(report_path);
|
||||
die(_("couldn't create a new file at '%s'"), report_path.buf);
|
||||
}
|
||||
|
||||
strbuf_write_fd(&buffer, report);
|
||||
close(report);
|
||||
|
||||
/*
|
||||
* We want to print the path relative to the user, but we still need the
|
||||
* path relative to us to give to the editor.
|
||||
*/
|
||||
if (!(prefix && skip_prefix(report_path.buf, prefix, &user_relative_path)))
|
||||
user_relative_path = report_path.buf;
|
||||
fprintf(stderr, _("Created new report at '%s'.\n"),
|
||||
user_relative_path);
|
||||
|
||||
UNLEAK(buffer);
|
||||
UNLEAK(report_path);
|
||||
return !!launch_editor(report_path.buf, NULL, NULL);
|
||||
}
|
@ -54,6 +54,7 @@ git-archive mainporcelain
|
||||
git-bisect mainporcelain info
|
||||
git-blame ancillaryinterrogators complete
|
||||
git-branch mainporcelain history
|
||||
git-bugreport ancillaryinterrogators
|
||||
git-bundle mainporcelain
|
||||
git-cat-file plumbinginterrogators
|
||||
git-check-attr purehelpers
|
||||
|
4
strbuf.c
4
strbuf.c
@ -539,6 +539,10 @@ ssize_t strbuf_write(struct strbuf *sb, FILE *f)
|
||||
return sb->len ? fwrite(sb->buf, 1, sb->len, f) : 0;
|
||||
}
|
||||
|
||||
ssize_t strbuf_write_fd(struct strbuf *sb, int fd)
|
||||
{
|
||||
return sb->len ? write(fd, sb->buf, sb->len) : 0;
|
||||
}
|
||||
|
||||
#define STRBUF_MAXLINK (2*PATH_MAX)
|
||||
|
||||
|
1
strbuf.h
1
strbuf.h
@ -450,6 +450,7 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
|
||||
* NUL bytes.
|
||||
*/
|
||||
ssize_t strbuf_write(struct strbuf *sb, FILE *stream);
|
||||
ssize_t strbuf_write_fd(struct strbuf *sb, int fd);
|
||||
|
||||
/**
|
||||
* Read a line from a FILE *, overwriting the existing contents of
|
||||
|
61
t/t0091-bugreport.sh
Executable file
61
t/t0091-bugreport.sh
Executable file
@ -0,0 +1,61 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='git bugreport'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
# Headers "[System Info]" will be followed by a non-empty line if we put some
|
||||
# information there; we can make sure all our headers were followed by some
|
||||
# information to check if the command was successful.
|
||||
HEADER_PATTERN="^\[.*\]$"
|
||||
|
||||
check_all_headers_populated () {
|
||||
while read -r line
|
||||
do
|
||||
if test "$(grep "$HEADER_PATTERN" "$line")"
|
||||
then
|
||||
echo "$line"
|
||||
read -r nextline
|
||||
if test -z "$nextline"; then
|
||||
return 1;
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
test_expect_success 'creates a report with content in the right places' '
|
||||
test_when_finished rm git-bugreport-check-headers.txt &&
|
||||
git bugreport -s check-headers &&
|
||||
check_all_headers_populated <git-bugreport-check-headers.txt
|
||||
'
|
||||
|
||||
test_expect_success 'dies if file with same name as report already exists' '
|
||||
test_when_finished rm git-bugreport-duplicate.txt &&
|
||||
>>git-bugreport-duplicate.txt &&
|
||||
test_must_fail git bugreport --suffix duplicate
|
||||
'
|
||||
|
||||
test_expect_success '--output-directory puts the report in the provided dir' '
|
||||
test_when_finished rm -fr foo/ &&
|
||||
git bugreport -o foo/ &&
|
||||
test_path_is_file foo/git-bugreport-*
|
||||
'
|
||||
|
||||
test_expect_success 'incorrect arguments abort with usage' '
|
||||
test_must_fail git bugreport --false 2>output &&
|
||||
test_i18ngrep usage output &&
|
||||
test_path_is_missing git-bugreport-*
|
||||
'
|
||||
|
||||
test_expect_success 'runs outside of a git dir' '
|
||||
test_when_finished rm non-repo/git-bugreport-* &&
|
||||
nongit git bugreport
|
||||
'
|
||||
|
||||
test_expect_success 'can create leading directories outside of a git dir' '
|
||||
test_when_finished rm -fr foo/bar/baz &&
|
||||
nongit git bugreport -o foo/bar/baz
|
||||
'
|
||||
|
||||
|
||||
test_done
|
Loading…
Reference in New Issue
Block a user