summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Documentation/git-bugreport.txt52
-rw-r--r--Makefile18
-rw-r--r--bugreport.c140
-rw-r--r--builtin/help.c86
-rw-r--r--command-list.txt1
-rw-r--r--compat/compiler.h41
-rw-r--r--compat/vcbuild/README4
-rw-r--r--config.mak.uname6
-rwxr-xr-xgenerate-cmdlist.sh19
-rwxr-xr-xgenerate-configlist.sh21
-rw-r--r--help.c131
-rw-r--r--help.h2
-rw-r--r--strbuf.c4
-rw-r--r--strbuf.h1
-rwxr-xr-xt/t0091-bugreport.sh61
16 files changed, 458 insertions, 131 deletions
diff --git a/.gitignore b/.gitignore
index 188bd1c3de..ee509a2ad2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,6 +25,7 @@
/git-bisect--helper
/git-blame
/git-branch
+/git-bugreport
/git-bundle
/git-cat-file
/git-check-attr
@@ -188,6 +189,7 @@
/gitweb/gitweb.cgi
/gitweb/static/gitweb.js
/gitweb/static/gitweb.min.*
+/config-list.h
/command-list.h
*.tar.gz
*.dsc
diff --git a/Documentation/git-bugreport.txt b/Documentation/git-bugreport.txt
new file mode 100644
index 0000000000..643d1b2884
--- /dev/null
+++ b/Documentation/git-bugreport.txt
@@ -0,0 +1,52 @@
+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
+
+The following information is captured automatically:
+
+ - 'git version --build-options'
+ - uname sysname, release, version, and machine strings
+ - Compiler-specific info string
+
+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
diff --git a/Makefile b/Makefile
index 7abdb1d352..3d3a39fc19 100644
--- a/Makefile
+++ b/Makefile
@@ -674,6 +674,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
@@ -810,6 +811,7 @@ LIB_FILE = libgit.a
XDIFF_LIB = xdiff/lib.a
VCSSVN_LIB = vcs-svn/lib.a
+GENERATED_H += config-list.h
GENERATED_H += command-list.h
LIB_H := $(sort $(patsubst ./%,%,$(shell git ls-files '*.h' ':!t/' ':!Documentation/' 2>/dev/null || \
@@ -2137,7 +2139,7 @@ git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS)
help.sp help.s help.o: command-list.h
-builtin/help.sp builtin/help.s builtin/help.o: command-list.h GIT-PREFIX
+builtin/help.sp builtin/help.s builtin/help.o: config-list.h GIT-PREFIX
builtin/help.sp builtin/help.s builtin/help.o: EXTRA_CPPFLAGS = \
'-DGIT_HTML_PATH="$(htmldir_relative_SQ)"' \
'-DGIT_MAN_PATH="$(mandir_relative_SQ)"' \
@@ -2157,6 +2159,12 @@ $(BUILT_INS): git$X
ln -s $< $@ 2>/dev/null || \
cp $< $@
+config-list.h: generate-configlist.sh
+
+config-list.h:
+ $(QUIET_GEN)$(SHELL_PATH) ./generate-configlist.sh \
+ >$@+ && mv $@+ $@
+
command-list.h: generate-cmdlist.sh command-list.txt
command-list.h: $(wildcard Documentation/git*.txt) Documentation/*config.txt Documentation/config/*.txt
@@ -2459,6 +2467,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)
@@ -2790,7 +2802,7 @@ $(SP_OBJ): %.sp: %.c GIT-CFLAGS FORCE
.PHONY: sparse $(SP_OBJ)
sparse: $(SP_OBJ)
-EXCEPT_HDRS := command-list.h unicode-width.h compat/% xdiff/%
+EXCEPT_HDRS := command-list.h config-list.h unicode-width.h compat/% xdiff/%
ifndef GCRYPT_SHA256
EXCEPT_HDRS += sha256/gcrypt.h
endif
@@ -2812,7 +2824,7 @@ hdr-check: $(HCO)
style:
git clang-format --style file --diff --extensions c,h
-check: command-list.h
+check: config-list.h command-list.h
@if sparse; \
then \
echo >&2 "Use 'make sparse' instead"; \
diff --git a/bugreport.c b/bugreport.c
new file mode 100644
index 0000000000..acacca8fef
--- /dev/null
+++ b/bugreport.c
@@ -0,0 +1,140 @@
+#include "cache.h"
+#include "parse-options.h"
+#include "strbuf.h"
+#include "help.h"
+#include "compat/compiler.h"
+
+static void get_system_info(struct strbuf *sys_info)
+{
+ struct utsname uname_info;
+
+ /* get git version from native cmd */
+ strbuf_addstr(sys_info, _("git version:\n"));
+ get_version_info(sys_info, 1);
+
+ /* system call for other version info */
+ strbuf_addstr(sys_info, "uname: ");
+ if (uname(&uname_info))
+ strbuf_addf(sys_info, _("uname() failed with error '%s' (%d)\n"),
+ strerror(errno),
+ errno);
+ else
+ strbuf_addf(sys_info, "%s %s %s %s\n",
+ uname_info.sysname,
+ uname_info.release,
+ uname_info.version,
+ uname_info.machine);
+
+ strbuf_addstr(sys_info, _("compiler info: "));
+ get_compiler_info(sys_info);
+ strbuf_addstr(sys_info, _("libc info: "));
+ get_libc_info(sys_info);
+}
+
+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;
+}
+
+static void get_header(struct strbuf *buf, const char *title)
+{
+ strbuf_addf(buf, "\n\n[%s]\n", title);
+}
+
+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);
+
+ get_header(&buffer, _("System Info"));
+ get_system_info(&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);
+}
diff --git a/builtin/help.c b/builtin/help.c
index c024110531..299206eb57 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -8,6 +8,7 @@
#include "parse-options.h"
#include "run-command.h"
#include "column.h"
+#include "config-list.h"
#include "help.h"
#include "alias.h"
@@ -62,6 +63,91 @@ static const char * const builtin_help_usage[] = {
NULL
};
+struct slot_expansion {
+ const char *prefix;
+ const char *placeholder;
+ void (*fn)(struct string_list *list, const char *prefix);
+ int found;
+};
+
+static void list_config_help(int for_human)
+{
+ struct slot_expansion slot_expansions[] = {
+ { "advice", "*", list_config_advices },
+ { "color.branch", "<slot>", list_config_color_branch_slots },
+ { "color.decorate", "<slot>", list_config_color_decorate_slots },
+ { "color.diff", "<slot>", list_config_color_diff_slots },
+ { "color.grep", "<slot>", list_config_color_grep_slots },
+ { "color.interactive", "<slot>", list_config_color_interactive_slots },
+ { "color.remote", "<slot>", list_config_color_sideband_slots },
+ { "color.status", "<slot>", list_config_color_status_slots },
+ { "fsck", "<msg-id>", list_config_fsck_msg_ids },
+ { "receive.fsck", "<msg-id>", list_config_fsck_msg_ids },
+ { NULL, NULL, NULL }
+ };
+ const char **p;
+ struct slot_expansion *e;
+ struct string_list keys = STRING_LIST_INIT_DUP;
+ int i;
+
+ for (p = config_name_list; *p; p++) {
+ const char *var = *p;
+ struct strbuf sb = STRBUF_INIT;
+
+ for (e = slot_expansions; e->prefix; e++) {
+
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s.%s", e->prefix, e->placeholder);
+ if (!strcasecmp(var, sb.buf)) {
+ e->fn(&keys, e->prefix);
+ e->found++;
+ break;
+ }
+ }
+ strbuf_release(&sb);
+ if (!e->prefix)
+ string_list_append(&keys, var);
+ }
+
+ for (e = slot_expansions; e->prefix; e++)
+ if (!e->found)
+ BUG("slot_expansion %s.%s is not used",
+ e->prefix, e->placeholder);
+
+ string_list_sort(&keys);
+ for (i = 0; i < keys.nr; i++) {
+ const char *var = keys.items[i].string;
+ const char *wildcard, *tag, *cut;
+
+ if (for_human) {
+ puts(var);
+ continue;
+ }
+
+ wildcard = strchr(var, '*');
+ tag = strchr(var, '<');
+
+ if (!wildcard && !tag) {
+ puts(var);
+ continue;
+ }
+
+ if (wildcard && !tag)
+ cut = wildcard;
+ else if (!wildcard && tag)
+ cut = tag;
+ else
+ cut = wildcard < tag ? wildcard : tag;
+
+ /*
+ * We may produce duplicates, but that's up to
+ * git-completion.bash to handle
+ */
+ printf("%.*s\n", (int)(cut - var), var);
+ }
+ string_list_clear(&keys, 0);
+}
+
static enum help_format parse_help_format(const char *format)
{
if (!strcmp(format, "man"))
diff --git a/command-list.txt b/command-list.txt
index 2087894655..185e5e3f05 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -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
diff --git a/compat/compiler.h b/compat/compiler.h
new file mode 100644
index 0000000000..10dbb65937
--- /dev/null
+++ b/compat/compiler.h
@@ -0,0 +1,41 @@
+#ifndef COMPILER_H
+#define COMPILER_H
+
+#include "git-compat-util.h"
+#include "strbuf.h"
+
+#ifdef __GLIBC__
+#include <gnu/libc-version.h>
+#endif
+
+static inline void get_compiler_info(struct strbuf *info)
+{
+ int len = info->len;
+#ifdef __clang__
+ strbuf_addf(info, "clang: %s\n", __clang_version__);
+#elif defined(__GNUC__)
+ strbuf_addf(info, "gnuc: %d.%d\n", __GNUC__, __GNUC_MINOR__);
+#endif
+
+#ifdef _MSC_VER
+ strbuf_addf(info, "MSVC version: %02d.%02d.%05d\n",
+ _MSC_VER / 100, _MSC_VER % 100, _MSC_FULL_VER % 100000);
+#endif
+
+ if (len == info->len)
+ strbuf_addstr(info, _("no compiler information available\n"));
+}
+
+static inline void get_libc_info(struct strbuf *info)
+{
+ int len = info->len;
+
+#ifdef __GLIBC__
+ strbuf_addf(info, "glibc: %s\n", gnu_get_libc_version());
+#endif
+
+ if (len == info->len)
+ strbuf_addstr(info, _("no libc information available\n"));
+}
+
+#endif /* COMPILER_H */
diff --git a/compat/vcbuild/README b/compat/vcbuild/README
index 1b6dabf5a2..42292e7c09 100644
--- a/compat/vcbuild/README
+++ b/compat/vcbuild/README
@@ -92,8 +92,8 @@ The Steps of Build Git with VS2008
the git operations.
3. Inside Git's directory run the command:
- make command-list.h
- to generate the command-list.h file needed to compile git.
+ make command-list.h config-list.h
+ to generate the header file needed to compile git.
4. Then either build Git with the GNU Make Makefile in the Git projects
root
diff --git a/config.mak.uname b/config.mak.uname
index 87c7f8d76c..5ad43c80b1 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -731,9 +731,9 @@ vcxproj:
echo '</Project>') >git-remote-http/LinkOrCopyRemoteHttp.targets
git add -f git/LinkOrCopyBuiltins.targets git-remote-http/LinkOrCopyRemoteHttp.targets
- # Add command-list.h
- $(MAKE) MSVC=1 SKIP_VCPKG=1 prefix=/mingw64 command-list.h
- git add -f command-list.h
+ # Add command-list.h and config-list.h
+ $(MAKE) MSVC=1 SKIP_VCPKG=1 prefix=/mingw64 config-list.h command-list.h
+ git add -f config-list.h command-list.h
# Add scripts
rm -f perl/perl.mak
diff --git a/generate-cmdlist.sh b/generate-cmdlist.sh
index 71158f7d8b..45fecf8bdf 100755
--- a/generate-cmdlist.sh
+++ b/generate-cmdlist.sh
@@ -76,23 +76,6 @@ print_command_list () {
echo "};"
}
-print_config_list () {
- cat <<EOF
-static const char *config_name_list[] = {
-EOF
- grep -h '^[a-zA-Z].*\..*::$' Documentation/*config.txt Documentation/config/*.txt |
- sed '/deprecated/d; s/::$//; s/, */\n/g' |
- sort |
- while read line
- do
- echo " \"$line\","
- done
- cat <<EOF
- NULL,
-};
-EOF
-}
-
exclude_programs=
while test "--exclude-program" = "$1"
do
@@ -113,5 +96,3 @@ echo
define_category_names "$1"
echo
print_command_list "$1"
-echo
-print_config_list
diff --git a/generate-configlist.sh b/generate-configlist.sh
new file mode 100755
index 0000000000..8692fe5cf4
--- /dev/null
+++ b/generate-configlist.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+echo "/* Automatically generated by generate-configlist.sh */"
+echo
+
+print_config_list () {
+ cat <<EOF
+static const char *config_name_list[] = {
+EOF
+ grep -h '^[a-zA-Z].*\..*::$' Documentation/*config.txt Documentation/config/*.txt |
+ sed '/deprecated/d; s/::$//; s/, */\n/g' |
+ sort |
+ sed 's/^.*$/ "&",/'
+ cat <<EOF
+ NULL,
+};
+EOF
+}
+
+echo
+print_config_list
diff --git a/help.c b/help.c
index cf67624a94..1de9c0d589 100644
--- a/help.c
+++ b/help.c
@@ -407,91 +407,6 @@ void list_common_guides_help(void)
putchar('\n');
}
-struct slot_expansion {
- const char *prefix;
- const char *placeholder;
- void (*fn)(struct string_list *list, const char *prefix);
- int found;
-};
-
-void list_config_help(int for_human)
-{
- struct slot_expansion slot_expansions[] = {
- { "advice", "*", list_config_advices },
- { "color.branch", "<slot>", list_config_color_branch_slots },
- { "color.decorate", "<slot>", list_config_color_decorate_slots },
- { "color.diff", "<slot>", list_config_color_diff_slots },
- { "color.grep", "<slot>", list_config_color_grep_slots },
- { "color.interactive", "<slot>", list_config_color_interactive_slots },
- { "color.remote", "<slot>", list_config_color_sideband_slots },
- { "color.status", "<slot>", list_config_color_status_slots },
- { "fsck", "<msg-id>", list_config_fsck_msg_ids },
- { "receive.fsck", "<msg-id>", list_config_fsck_msg_ids },
- { NULL, NULL, NULL }
- };
- const char **p;
- struct slot_expansion *e;
- struct string_list keys = STRING_LIST_INIT_DUP;
- int i;
-
- for (p = config_name_list; *p; p++) {
- const char *var = *p;
- struct strbuf sb = STRBUF_INIT;
-
- for (e = slot_expansions; e->prefix; e++) {
-
- strbuf_reset(&sb);
- strbuf_addf(&sb, "%s.%s", e->prefix, e->placeholder);
- if (!strcasecmp(var, sb.buf)) {
- e->fn(&keys, e->prefix);
- e->found++;
- break;
- }
- }
- strbuf_release(&sb);
- if (!e->prefix)
- string_list_append(&keys, var);
- }
-
- for (e = slot_expansions; e->prefix; e++)
- if (!e->found)
- BUG("slot_expansion %s.%s is not used",
- e->prefix, e->placeholder);
-
- string_list_sort(&keys);
- for (i = 0; i < keys.nr; i++) {
- const char *var = keys.items[i].string;
- const char *wildcard, *tag, *cut;
-
- if (for_human) {
- puts(var);
- continue;
- }
-
- wildcard = strchr(var, '*');
- tag = strchr(var, '<');
-
- if (!wildcard && !tag) {
- puts(var);
- continue;
- }
-
- if (wildcard && !tag)
- cut = wildcard;
- else if (!wildcard && tag)
- cut = tag;
- else
- cut = wildcard < tag ? wildcard : tag;
-
- /*
- * We may produce duplicates, but that's up to
- * git-completion.bash to handle
- */
- printf("%.*s\n", (int)(cut - var), var);
- }
- string_list_clear(&keys, 0);
-}
-
static int get_alias(const char *var, const char *value, void *data)
{
struct string_list *list = data;
@@ -707,8 +622,32 @@ const char *help_unknown_cmd(const char *cmd)
exit(1);
}
+void get_version_info(struct strbuf *buf, int show_build_options)
+{
+ /*
+ * The format of this string should be kept stable for compatibility
+ * with external projects that rely on the output of "git version".
+ *
+ * Always show the version, even if other options are given.
+ */
+ strbuf_addf(buf, "git version %s\n", git_version_string);
+
+ if (show_build_options) {
+ strbuf_addf(buf, "cpu: %s\n", GIT_HOST_CPU);
+ if (git_built_from_commit_string[0])
+ strbuf_addf(buf, "built from commit: %s\n",
+ git_built_from_commit_string);
+ else
+ strbuf_addstr(buf, "no commit associated with this build\n");
+ strbuf_addf(buf, "sizeof-long: %d\n", (int)sizeof(long));
+ strbuf_addf(buf, "sizeof-size_t: %d\n", (int)sizeof(size_t));
+ /* NEEDSWORK: also save and output GIT-BUILD_OPTIONS? */
+ }
+}
+
int cmd_version(int argc, const char **argv, const char *prefix)
{
+ struct strbuf buf = STRBUF_INIT;
int build_options = 0;
const char * const usage[] = {
N_("git version [<options>]"),
@@ -722,25 +661,11 @@ int cmd_version(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options, usage, 0);
- /*
- * The format of this string should be kept stable for compatibility
- * with external projects that rely on the output of "git version".
- *
- * Always show the version, even if other options are given.
- */
- printf("git version %s\n", git_version_string);
+ get_version_info(&buf, build_options);
+ printf("%s", buf.buf);
+
+ strbuf_release(&buf);
- if (build_options) {
- printf("cpu: %s\n", GIT_HOST_CPU);
- if (git_built_from_commit_string[0])
- printf("built from commit: %s\n",
- git_built_from_commit_string);
- else
- printf("no commit associated with this build\n");
- printf("sizeof-long: %d\n", (int)sizeof(long));
- printf("sizeof-size_t: %d\n", (int)sizeof(size_t));
- /* NEEDSWORK: also save and output GIT-BUILD_OPTIONS? */
- }
return 0;
}
diff --git a/help.h b/help.h
index 7a455beeb7..500521b908 100644
--- a/help.h
+++ b/help.h
@@ -22,7 +22,6 @@ static inline void mput_char(char c, unsigned int num)
void list_common_cmds_help(void);
void list_all_cmds_help(void);
void list_common_guides_help(void);
-void list_config_help(int for_human);
void list_all_main_cmds(struct string_list *list);
void list_all_other_cmds(struct string_list *list);
@@ -38,6 +37,7 @@ void add_cmdname(struct cmdnames *cmds, const char *name, int len);
void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
int is_in_cmdlist(struct cmdnames *cmds, const char *name);
void list_commands(unsigned int colopts, struct cmdnames *main_cmds, struct cmdnames *other_cmds);
+void get_version_info(struct strbuf *buf, int show_build_options);
/*
* call this to die(), when it is suspected that the user mistyped a
diff --git a/strbuf.c b/strbuf.c
index 6e74901bfa..880daaaaa9 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -554,6 +554,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)
diff --git a/strbuf.h b/strbuf.h
index 411063ca76..16f47202eb 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -468,6 +468,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
diff --git a/t/t0091-bugreport.sh b/t/t0091-bugreport.sh
new file mode 100755
index 0000000000..2e73658a5c
--- /dev/null
+++ b/t/t0091-bugreport.sh
@@ -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