@@ -189,7 +189,9 @@
/gitweb/gitweb.cgi
/gitweb/static/gitweb.js
/gitweb/static/gitweb.min.*
+/config-list.h
/command-list.h
+/bugreport-config-safelist.h
*.tar.gz
*.dsc
*.deb
@@ -6,9 +6,13 @@
#
# Show Git link as: <command>(<section>); if section is defined, else just show
# the command.
+#
+# The bugreport macro does nothing as far as rendering is
+# concerned -- we just grep for it in the sources.
[macros]
(?su)[\\]?(?P<name>linkgit):(?P<target>\S*?)\[(?P<attrlist>.*?)\]=
+(?su)[\\]?(?P<name>bugreport):(?P<action>\S*?)\[(?P<attrlist>.*?)\]=
[attributes]
asterisk=*
@@ -28,6 +32,8 @@ ifdef::backend-docbook[]
{0#<citerefentry>}
{0#<refentrytitle>{target}</refentrytitle><manvolnum>{0}</manvolnum>}
{0#</citerefentry>}
+[bugreport-inlinemacro]
+{0#}
endif::backend-docbook[]
ifdef::backend-docbook[]
@@ -94,4 +100,6 @@ ifdef::backend-xhtml11[]
git-relative-html-prefix=
[linkgit-inlinemacro]
<a href="{git-relative-html-prefix}{target}.html">{target}{0?({0})}</a>
+[bugreport-inlinemacro]
+<!-- -->
endif::backend-xhtml11[]
@@ -37,6 +37,10 @@ module Git
output = output.sub(/<\/refmeta>/, new_tags + "</refmeta>")
end
output
+
+ class BugReportProcessor < Asciidoctor::Extensions::InlineMacroProcessor
+ def process(parent, action, attrs)
+ ""
end
end
end
@@ -45,4 +49,7 @@ end
Asciidoctor::Extensions.register do
inline_macro Git::Documentation::LinkGitProcessor, :linkgit
postprocessor Git::Documentation::DocumentPostProcessor
+ # The bugreport macro does nothing as far as rendering is
+ # concerned -- we just grep for it in the sources.
+ inline_macro Git::Documentation::BugReportProcessor, :bugreport
end
@@ -1,63 +1,63 @@
-sendemail.identity:: // bugreport-exclude
+sendemail.identity bugreport:exclude[x] ::
A configuration identity. When given, causes values in the
'sendemail.<identity>' subsection to take precedence over
values in the 'sendemail' section. The default identity is
the value of `sendemail.identity`.
-sendemail.smtpEncryption:: // bugreport-include
+sendemail.smtpEncryption bugreport:include[x] ::
See linkgit:git-send-email[1] for description. Note that this
setting is not subject to the 'identity' mechanism.
-sendemail.smtpssl (deprecated):: // bugreport-exclude
+sendemail.smtpssl (deprecated) bugreport:exclude[x] ::
Deprecated alias for 'sendemail.smtpEncryption = ssl'.
-sendemail.smtpsslcertpath:: // bugreport-exclude
+sendemail.smtpsslcertpath bugreport:exclude[x] ::
Path to ca-certificates (either a directory or a single file).
Set it to an empty string to disable certificate verification.
-sendemail.<identity>.*:: // bugreport-exclude
+sendemail.<identity>.* bugreport:exclude[x] ::
Identity-specific versions of the 'sendemail.*' parameters
found below, taking precedence over those when this
identity is selected, through either the command-line or
`sendemail.identity`.
-sendemail.aliasesFile:: // bugreport-exclude
-sendemail.aliasFileType:: // bugreport-exclude
-sendemail.annotate:: // bugreport-include
-sendemail.bcc:: // bugreport-include
-sendemail.cc:: // bugreport-include
-sendemail.ccCmd:: // bugreport-include
-sendemail.chainReplyTo:: // bugreport-include
-sendemail.confirm:: // bugreport-include
-sendemail.envelopeSender:: // bugreport-include
-sendemail.from:: // bugreport-include
-sendemail.multiEdit:: // bugreport-include
-sendemail.signedoffbycc:: // bugreport-include
-sendemail.smtpPass:: // bugreport-exclude
-sendemail.suppresscc:: // bugreport-include
-sendemail.suppressFrom:: // bugreport-include
-sendemail.to:: // bugreport-include
-sendemail.tocmd:: // bugreport-include
-sendemail.smtpDomain:: // bugreport-include
-sendemail.smtpServer:: // bugreport-include
-sendemail.smtpServerPort:: // bugreport-include
-sendemail.smtpServerOption:: // bugreport-include
-sendemail.smtpUser:: // bugreport-exclude
-sendemail.thread:: // bugreport-include
-sendemail.transferEncoding:: // bugreport-include
-sendemail.validate:: // bugreport-include
-sendemail.xmailer:: // bugreport-include
+sendemail.aliasesFile bugreport:exclude[x] ::
+sendemail.aliasFileType bugreport:exclude[x] ::
+sendemail.annotate bugreport:include[x] ::
+sendemail.bcc bugreport:include[x] ::
+sendemail.cc bugreport:include[x] ::
+sendemail.ccCmd bugreport:include[x] ::
+sendemail.chainReplyTo bugreport:include[x] ::
+sendemail.confirm bugreport:include[x] ::
+sendemail.envelopeSender bugreport:include[x] ::
+sendemail.from bugreport:include[x] ::
+sendemail.multiEdit bugreport:include[x] ::
+sendemail.signedoffbycc bugreport:include[x] ::
+sendemail.smtpPass bugreport:exclude[x] ::
+sendemail.suppresscc bugreport:include[x] ::
+sendemail.suppressFrom bugreport:include[x] ::
+sendemail.to bugreport:include[x] ::
+sendemail.tocmd bugreport:include[x] ::
+sendemail.smtpDomain bugreport:include[x] ::
+sendemail.smtpServer bugreport:include[x] ::
+sendemail.smtpServerPort bugreport:include[x] ::
+sendemail.smtpServerOption bugreport:include[x] ::
+sendemail.smtpUser bugreport:exclude[x] ::
+sendemail.thread bugreport:include[x] ::
+sendemail.transferEncoding bugreport:include[x] ::
+sendemail.validate bugreport:include[x] ::
+sendemail.xmailer bugreport:include[x] ::
See linkgit:git-send-email[1] for description.
-sendemail.signedoffcc (deprecated):: // bugreport-exclude
+sendemail.signedoffcc (deprecated) bugreport:exclude[x] ::
Deprecated alias for `sendemail.signedoffbycc`.
-sendemail.smtpBatchSize:: // bugreport-include
+sendemail.smtpBatchSize bugreport:include[x] ::
Number of messages to be sent per connection, after that a relogin
will happen. If the value is 0 or undefined, send all messages in
one connection.
See also the `--batch-size` option of linkgit:git-send-email[1].
-sendemail.smtpReloginDelay:: // bugreport-include
+sendemail.smtpReloginDelay bugreport:include[x] ::
Seconds wait before reconnecting to smtp server.
See also the `--relogin-delay` option of linkgit:git-send-email[1].
new file mode 100644
@@ -0,0 +1,43 @@
+git-bugreport(1)
+================
+
+NAME
+----
+git-bugreport - Collect information for user to file a bug report
+
+SYNOPSIS
+--------
+[verse]
+'git bugreport' [-o | --output <path>]
+
+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 (`git version --build-options`)
+ - Machine information (`uname -a`)
+ - Versions of various dependencies
+ - Git config contents (`git config --show-origin --list`)
+ - The names of all configured git-hooks in `.git/hooks/`
+
+OPTIONS
+-------
+-o [<path>]::
+--output [<path>]::
+ Place the resulting bug report file in <path> instead of the root of the
+ Git repository.
+
+GIT
+---
+Part of the linkgit:git[1] suite
@@ -10,6 +10,7 @@ SYNOPSIS
--------
[verse]
'git http-fetch' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] [--stdin] <commit> <url>
+'git http-fetch' [-V]
DESCRIPTION
-----------
@@ -30,6 +31,10 @@ commit-id::
-v::
Report what is downloaded.
+-V::
+ Report information about the version of git-http-fetch, including the
+ versions of its dependencies.
+
-w <filename>::
Writes the commit-id into the filename under $GIT_DIR/refs/<filename> on
the local end after the transfer is complete.
@@ -634,10 +634,6 @@ SCRIPT_PYTHON += git-p4.py
SCRIPT_SH_GEN = $(patsubst %.sh,%,$(SCRIPT_SH))
SCRIPT_PERL_GEN = $(patsubst %.perl,%,$(SCRIPT_PERL))
SCRIPT_PYTHON_GEN = $(patsubst %.py,%,$(SCRIPT_PYTHON))
-SCRIPT_DEPENDENCIES = git-bugreport-config-whitelist
-
-$(SCRIPT_DEPENDENCIES): Documentation/config/*.txt
- sh bugreport-generate-config-whitelist.sh
# Individual rules to allow e.g.
# "make -C ../.. SCRIPT_PERL=contrib/foo/bar.perl build-perl-script"
@@ -662,13 +658,10 @@ clean-perl-script:
$(RM) $(SCRIPT_PERL_GEN)
clean-python-script:
$(RM) $(SCRIPT_PYTHON_GEN)
-clean-script-dependencies:
- $(RM) $(SCRIPT_DEPENDENCIES)
SCRIPTS = $(SCRIPT_SH_GEN) \
$(SCRIPT_PERL_GEN) \
$(SCRIPT_PYTHON_GEN) \
- $(SCRIPT_DEPENDENCIES) \
git-instaweb
ETAGS_TARGET = TAGS
@@ -688,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
@@ -821,7 +815,9 @@ 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
+GENERATED_H += bugreport-config-safelist.h
LIB_H := $(sort $(patsubst ./%,%,$(shell git ls-files '*.h' ':!t/' ':!Documentation/' 2>/dev/null || \
$(FIND) . \
@@ -846,7 +842,6 @@ LIB_OBJS += bisect.o
LIB_OBJS += blame.o
LIB_OBJS += blob.o
LIB_OBJS += branch.o
-LIB_OBJS += bugreport.o
LIB_OBJS += bulk-checkin.o
LIB_OBJS += bundle.o
LIB_OBJS += cache-tree.o
@@ -1049,7 +1044,6 @@ BUILTIN_OBJS += builtin/archive.o
BUILTIN_OBJS += builtin/bisect--helper.o
BUILTIN_OBJS += builtin/blame.o
BUILTIN_OBJS += builtin/branch.o
-BUILTIN_OBJS += builtin/bugreport.o
BUILTIN_OBJS += builtin/bundle.o
BUILTIN_OBJS += builtin/cat-file.o
BUILTIN_OBJS += builtin/check-attr.o
@@ -2135,7 +2129,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)"' \
@@ -2155,6 +2149,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
@@ -2162,6 +2162,12 @@ command-list.h: $(wildcard Documentation/git*.txt) Documentation/*config.txt Doc
$(patsubst %,--exclude-program %,$(EXCLUDED_PROGRAMS)) \
command-list.txt >$@+ && mv $@+ $@
+bugreport-config-safelist.h: generate-bugreport-config-safelist.sh
+
+bugreport-config-safelist.h: Documentation/config/*.txt
+ $(QUIET_GEN)$(SHELL_PATH) ./generate-bugreport-config-safelist.sh \
+ >$@+ && mv $@+ $@
+
SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\
$(localedir_SQ):$(NO_CURL):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\
$(gitwebdir_SQ):$(PERL_PATH_SQ):$(SANE_TEXT_GREP):$(PAGER_ENV):\
@@ -2457,6 +2463,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)
@@ -2788,7 +2798,7 @@ $(SP_OBJ): %.sp: %.c GIT-CFLAGS FORCE
.PHONY: sparse $(SP_OBJ)
sparse: $(SP_OBJ)
-GEN_HDRS := command-list.h unicode-width.h
+GEN_HDRS := config-list.h command-list.h unicode-width.h bugreport-config-safelist.h
EXCEPT_HDRS := $(GEN_HDRS) compat/% xdiff/%
ifndef GCRYPT_SHA256
EXCEPT_HDRS += sha256/gcrypt.h
@@ -2811,7 +2821,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"; \
@@ -3114,7 +3124,9 @@ clean: profile-clean coverage-clean cocciclean
$(RM) $(HCC)
$(RM) -r bin-wrappers $(dep_dirs)
$(RM) -r po/build/
- $(RM) *.pyc *.pyo */*.pyc */*.pyo command-list.h $(ETAGS_TARGET) tags cscope*
+ $(RM) *.pyc *.pyo */*.pyc */*.pyo
+ $(RM) config-list.h command-list.h bugreport-config-safelist.h
+ $(RM) $(ETAGS_TARGET) tags cscope*
$(RM) -r $(GIT_TARNAME) .doc-tmp-dir
$(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
$(RM) $(htmldocs).tar.gz $(manpages).tar.gz
deleted file mode 100755
@@ -1,4 +0,0 @@
-#!/bin/sh
-
-grep -RhPo ".*(?=:: \/\/ bugreport-include)" Documentation/config \
- >git-bugreport-config-whitelist
@@ -1,114 +1,128 @@
#include "cache.h"
-
-#include "bugreport.h"
-#include "config.h"
-#include "exec-cmd.h"
+#include "parse-options.h"
+#include "stdio.h"
+#include "strbuf.h"
+#include "time.h"
#include "help.h"
+#include <gnu/libc-version.h>
#include "run-command.h"
-#include "strbuf.h"
-#include "string-list.h"
-#include "version.h"
-
-#include "dirent.h"
-
-/**
- * A sorted list of config options which we will add to the bugreport. Managed
- * by 'gather_whitelist(...)'.
- */
-struct string_list whitelist = STRING_LIST_INIT_DUP;
-struct strbuf configs_and_values = STRBUF_INIT;
-
-// git version --build-options
-// uname -a
-// curl-config --version
-// ldd --version
-// echo $SHELL
-void get_system_info(struct strbuf *sys_info)
+#include "config.h"
+#include "bugreport-config-safelist.h"
+#include "khash.h"
+#include "run-command.h"
+
+static void get_http_version_info(struct strbuf *http_info)
{
struct child_process cp = CHILD_PROCESS_INIT;
- struct strbuf std_out = STRBUF_INIT;
- strbuf_reset(sys_info);
+ argv_array_push(&cp.args, "git");
+ argv_array_push(&cp.args, "http-fetch");
+ argv_array_push(&cp.args, "-V");
+ if (capture_command(&cp, http_info, 0))
+ strbuf_addstr(http_info, "'git-http-fetch -V' not supported\n");
+}
- // get git version from native cmd
- strbuf_addstr(sys_info, "git version: ");
- strbuf_addstr(sys_info, git_version_string);
- strbuf_complete_line(sys_info);
+KHASH_INIT(cfg_set, const char*, int, 0, kh_str_hash_func, kh_str_hash_equal);
- // system call for other version info
- argv_array_push(&cp.args, "uname");
- argv_array_push(&cp.args, "-a");
- capture_command(&cp, &std_out, 0);
+struct cfgset {
+ kh_cfg_set_t set;
+};
- strbuf_addstr(sys_info, "uname -a: ");
- strbuf_addbuf(sys_info, &std_out);
- strbuf_complete_line(sys_info);
-
- argv_array_clear(&cp.args);
- strbuf_reset(&std_out);
+struct cfgset safelist;
+static void cfgset_init(struct cfgset *set, size_t initial_size)
+{
+ memset(&set->set, 0, sizeof(set->set));
+ if (initial_size)
+ kh_resize_cfg_set(&set->set, initial_size);
+}
- argv_array_push(&cp.args, "curl-config");
- argv_array_push(&cp.args, "--version");
- capture_command(&cp, &std_out, 0);
+static int cfgset_insert(struct cfgset *set, const char *cfg_key)
+{
+ int added;
+ kh_put_cfg_set(&set->set, cfg_key, &added);
+ printf("ESS: added %s\n", cfg_key);
+ return !added;
+}
- strbuf_addstr(sys_info, "curl-config --version: ");
- strbuf_addbuf(sys_info, &std_out);
- strbuf_complete_line(sys_info);
+static int cfgset_contains(struct cfgset *set, const char *cfg_key)
+{
+ khiter_t pos = kh_get_cfg_set(&set->set, cfg_key);
+ return pos != kh_end(&set->set);
+}
- argv_array_clear(&cp.args);
- strbuf_reset(&std_out);
+static void cfgset_clear(struct cfgset *set)
+{
+ kh_release_cfg_set(&set->set);
+ cfgset_init(set, 0);
+}
+static void get_system_info(struct strbuf *sys_info)
+{
+ struct strbuf version_info = STRBUF_INIT;
+ struct utsname uname_info;
- argv_array_push(&cp.args, "ldd");
- argv_array_push(&cp.args, "--version");
- capture_command(&cp, &std_out, 0);
+ /* get git version from native cmd */
+ strbuf_addstr(sys_info, "git version:\n");
+ list_version_info(&version_info, 1);
+ strbuf_addbuf(sys_info, &version_info);
+ strbuf_complete_line(sys_info);
- strbuf_addstr(sys_info, "ldd --version: ");
- strbuf_addbuf(sys_info, &std_out);
+ /* system call for other version info */
+ strbuf_addstr(sys_info, "uname -a: ");
+ if (uname(&uname_info))
+ strbuf_addf(sys_info, "uname() failed with code %d\n", errno);
+ else
+ strbuf_addf(sys_info, "%s %s %s %s %s\n",
+ uname_info.sysname,
+ uname_info.nodename,
+ uname_info.release,
+ uname_info.version,
+ uname_info.machine);
+
+ strbuf_addstr(sys_info, "glibc version: ");
+ strbuf_addstr(sys_info, gnu_get_libc_version());
strbuf_complete_line(sys_info);
- argv_array_clear(&cp.args);
- strbuf_reset(&std_out);
-}
+ strbuf_addf(sys_info, "$SHELL (typically, interactive shell): %s\n",
+ getenv("SHELL"));
-void gather_whitelist(struct strbuf *path)
-{
- struct strbuf tmp = STRBUF_INIT;
- strbuf_read_file(&tmp, path->buf, 0);
- string_list_init(&whitelist, 1);
- string_list_split(&whitelist, tmp.buf, '\n', -1);
- string_list_sort(&whitelist);
+ strbuf_addstr(sys_info, "git-http-fetch -V:\n");
+ get_http_version_info(sys_info);
+ strbuf_complete_line(sys_info);
}
-int git_config_bugreport(const char *var, const char *value, void *cb)
+static void gather_safelist()
{
- if (string_list_has_string(&whitelist, var)) {
- strbuf_addf(&configs_and_values,
- "%s : %s\n",
- var, value);
- }
+ int index;
+ int safelist_len = sizeof(bugreport_config_safelist) / sizeof(const char *);
+ cfgset_init(&safelist, safelist_len);
+ for (index = 0; index < safelist_len; index++)
+ cfgset_insert(&safelist, bugreport_config_safelist[index]);
- return 0;
}
-void get_whitelisted_config(struct strbuf *config_info)
+static int git_config_bugreport(const char *var, const char *value, void *cb)
{
- struct strbuf path = STRBUF_INIT;
+ struct strbuf *config_info = (struct strbuf *)cb;
- strbuf_addstr(&path, git_exec_path());
- strbuf_addstr(&path, "/git-bugreport-config-whitelist");
+ if (cfgset_contains(&safelist, var))
+ strbuf_addf(config_info,
+ "%s (%s) : %s\n",
+ var, config_scope_to_string(current_config_scope()),
+ value);
- gather_whitelist(&path);
- strbuf_init(&configs_and_values, whitelist.nr);
-
- git_config(git_config_bugreport, NULL);
+ return 0;
+}
- strbuf_reset(config_info);
- strbuf_addbuf(config_info, &configs_and_values);
+static void get_safelisted_config(struct strbuf *config_info)
+{
+ gather_safelist();
+ git_config(git_config_bugreport, config_info);
+ cfgset_clear(&safelist);
}
-void get_populated_hooks(struct strbuf *hook_info)
+static void get_populated_hooks(struct strbuf *hook_info)
{
/*
* Doesn't look like there is a list of all possible hooks; so below is
@@ -139,8 +153,15 @@ void get_populated_hooks(struct strbuf *hook_info)
"post-index-changex";
struct string_list hooks_list = STRING_LIST_INIT_DUP;
struct string_list_item *iter = NULL;
+ int nongit_ok;
+
+ setup_git_directory_gently(&nongit_ok);
- strbuf_reset(hook_info);
+ if (nongit_ok) {
+ strbuf_addstr(hook_info,
+ "not run from a git repository - no hooks to show\n");
+ return;
+ }
string_list_split(&hooks_list, hooks, ',', -1);
@@ -152,163 +173,262 @@ void get_populated_hooks(struct strbuf *hook_info)
}
}
-/**
- * Fill 'contents' with the contents of the dir at 'dirpath'.
- * If 'filter' is nonzero, the contents are filtered on d_type as 'type' - see
- * 'man readdir'. opendir() doesn't take string length as an arg, so don't
- * bother passing it in.
- */
-void list_contents_of_dir(struct string_list *contents, struct strbuf *dirpath,
- int filter, unsigned char type)
+static int is_hex(const char *string, size_t count)
{
- struct dirent *dir = NULL;
- DIR *dh = NULL;
-
- dh = opendir(dirpath->buf);
- while (dh && (dir = readdir(dh))) {
- if (!filter || type == dir->d_type) {
- string_list_append(contents, dir->d_name);
- }
+ for (; count; string++, count--) {
+ if (!isxdigit(*string))
+ return 0;
}
+ return 1;
}
-/**
- * Fills 'contents' with a list of all directories within the provided
- * directory, recursing into each directory.
- */
-void list_contents_of_dir_recursively(struct string_list *contents,
- struct strbuf *dirpath)
-{
- struct string_list current_contents = STRING_LIST_INIT_DUP;
- struct string_list current_subdirs = STRING_LIST_INIT_DUP;
- struct string_list_item *it;
- struct strbuf buf = STRBUF_INIT;
-
- list_contents_of_dir(¤t_contents, dirpath, 0, 0);
- for_each_string_list_item(it, ¤t_contents) {
- strbuf_reset(&buf);
- strbuf_addbuf(&buf, dirpath);
- strbuf_complete(&buf, '/');
- strbuf_addstr(&buf, it->string);
-
- string_list_append(contents, buf.buf);
- }
+static void get_loose_object_summary(struct strbuf *obj_info) {
+ struct dirent *d = NULL;
+ DIR *dir, *subdir = NULL;
+ size_t dir_len;
+ struct strbuf dirpath = STRBUF_INIT;
- list_contents_of_dir(¤t_subdirs, dirpath, 1, DT_DIR);
- for_each_string_list_item(it, ¤t_subdirs) {
- if (strcmp(it->string, ".") == 0
- || strcmp(it->string, "..") == 0)
- continue;
- strbuf_reset(&buf);
- strbuf_addbuf(&buf, dirpath);
- strbuf_complete(&buf, '/');
- strbuf_addstr(&buf, it->string);
+ strbuf_addstr(&dirpath, get_object_directory());
+ strbuf_complete(&dirpath, '/');
- list_contents_of_dir_recursively(contents, &buf);
+ dir = opendir(dirpath.buf);
+ if (!dir) {
+ strbuf_addf(obj_info, "could not open object directory '%s'\n",
+ dirpath.buf);
+ strbuf_release(&dirpath);
+ return;
}
-}
-void get_object_counts(struct strbuf *obj_info)
-{
- struct child_process cp = CHILD_PROCESS_INIT;
- struct strbuf std_out = STRBUF_INIT;
-
- argv_array_push(&cp.args, "count-objects");
- argv_array_push(&cp.args, "-vH");
- cp.git_cmd = 1;
- capture_command(&cp, &std_out, 0);
+ dir_len = dirpath.len;
- strbuf_reset(obj_info);
- strbuf_addstr(obj_info, "git-count-objects -vH:\n");
- strbuf_addbuf(obj_info, &std_out);
-}
+ while ((d = readdir(dir))) {
+ int object_count = 0;
+ char subdir_name[3];
-void get_loose_object_summary(struct strbuf *obj_info)
-{
- struct strbuf dirpath = STRBUF_INIT;
- struct string_list subdirs = STRING_LIST_INIT_DUP;
- struct string_list_item *subdir;
-
- strbuf_reset(obj_info);
+ if (d->d_type != DT_DIR)
+ continue;
- strbuf_addstr(&dirpath, get_object_directory());
- strbuf_complete(&dirpath, '/');
+ if ((strlen(d->d_name) != 2) || (!is_hex(d->d_name, 2)))
+ continue;
- list_contents_of_dir(&subdirs, &dirpath, 1, DT_DIR);
+ /* copy directory name + \0 */
+ memcpy(subdir_name, d->d_name, 3);
- for_each_string_list_item(subdir, &subdirs)
- {
- struct strbuf subdir_buf = STRBUF_INIT;
- struct string_list objects = STRING_LIST_INIT_DUP;
+ strbuf_setlen(&dirpath, dir_len);
+ strbuf_addstr(&dirpath, d->d_name);
- /*
- * Only interested in loose objects - so dirs named with the
- * first byte of the object ID
- */
- if (strlen(subdir->string) != 2 || !strcmp(subdir->string, ".."))
+ subdir = opendir(dirpath.buf);
+ if (!subdir)
continue;
+ while ((d = readdir(subdir)))
+ if (d->d_type == DT_REG)
+ object_count++;
+
+ closedir(subdir);
- strbuf_addbuf(&subdir_buf, &dirpath);
- strbuf_addstr(&subdir_buf, subdir->string);
- list_contents_of_dir(&objects, &subdir_buf, 0, 0);
- strbuf_addf(obj_info, "%s: %d objects\n", subdir->string,
- objects.nr);
+ strbuf_addf(obj_info, "%s: %d\n", subdir_name, object_count);
}
+
+
+ closedir(dir);
+ strbuf_release(&dirpath);
}
-void get_packed_object_summary(struct strbuf *obj_info)
+static void get_packed_object_summary(struct strbuf *obj_info)
{
struct strbuf dirpath = STRBUF_INIT;
- struct string_list contents = STRING_LIST_INIT_DUP;
- struct string_list_item *entry;
-
- strbuf_reset(obj_info);
+ struct dirent *d;
+ DIR *dir = NULL;
strbuf_addstr(&dirpath, get_object_directory());
strbuf_complete(&dirpath, '/');
strbuf_addstr(&dirpath, "pack/");
- list_contents_of_dir(&contents, &dirpath, 0, 0);
- // list full contents of $GIT_OBJECT_DIRECTORY/pack/
- for_each_string_list_item(entry, &contents) {
+ dir = opendir(dirpath.buf);
+ if (!dir) {
+ strbuf_addf(obj_info, "could not open packed object directory '%s'\n",
+ dirpath.buf);
+ strbuf_release(&dirpath);
+ return;
+ }
+
+ while ((d = readdir(dir))) {
strbuf_addbuf(obj_info, &dirpath);
- strbuf_addstr(obj_info, entry->string);
+ strbuf_addstr(obj_info, d->d_name);
strbuf_complete_line(obj_info);
}
+
+ closedir(dir);
+ strbuf_release(&dirpath);
}
-void get_object_info_summary(struct strbuf *obj_info)
+static void list_contents_of_dir_recursively(struct strbuf *contents,
+ struct strbuf *dirpath)
{
- // strbuf += GITDIR/info/:
- // recursively list contents of $GIT_OBJECT_DIRECTORY/info
- struct strbuf dirpath = STRBUF_INIT;
- struct string_list contents = STRING_LIST_INIT_DUP;
- struct string_list_item *entry;
+ struct dirent *d;
+ DIR *dir;
+ size_t path_len;
- strbuf_reset(obj_info);
+ dir = opendir(dirpath->buf);
+ if (!dir)
+ return;
+
+ strbuf_complete(dirpath, '/');
+ path_len = dirpath->len;
+
+ while ((d = readdir(dir))) {
+ if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+ continue;
+
+ strbuf_addbuf(contents, dirpath);
+ strbuf_addstr(contents, d->d_name);
+ strbuf_complete_line(contents);
+
+ if (d->d_type == DT_DIR) {
+ strbuf_addstr(dirpath, d->d_name);
+ list_contents_of_dir_recursively(contents, dirpath);
+ }
+ strbuf_setlen(dirpath, path_len);
+ }
+
+ closedir(dir);
+}
+
+static void get_object_info_summary(struct strbuf *obj_info)
+{
+ struct strbuf dirpath = STRBUF_INIT;
strbuf_addstr(&dirpath, get_object_directory());
strbuf_complete(&dirpath, '/');
strbuf_addstr(&dirpath, "info/");
- list_contents_of_dir_recursively(&contents, &dirpath);
+ list_contents_of_dir_recursively(obj_info, &dirpath);
- for_each_string_list_item(entry, &contents) {
- strbuf_addstr(obj_info, entry->string);
- strbuf_complete_line(obj_info);
- }
+ strbuf_release(&dirpath);
}
-void get_alternates_file(struct strbuf *alternates_info)
+static void get_alternates_summary(struct strbuf *alternates_info)
{
struct strbuf alternates_path = STRBUF_INIT;
+ struct strbuf alternate = STRBUF_INIT;
+ FILE *file;
+ size_t exists = 0, broken = 0;
strbuf_addstr(&alternates_path, get_object_directory());
strbuf_complete(&alternates_path, '/');
strbuf_addstr(&alternates_path, "info/alternates");
- strbuf_reset(alternates_info);
- strbuf_addbuf(alternates_info, &alternates_path);
- strbuf_complete_line(alternates_info);
- strbuf_read_file(alternates_info, alternates_path.buf, 0);
+ file = fopen(alternates_path.buf, "r");
+ if (!file) {
+ strbuf_addstr(alternates_info, "No alternates file found.\n");
+ strbuf_release(&alternates_path);
+ return;
+ }
+
+ while (strbuf_getline(&alternate, file) != EOF) {
+ if (!access(alternate.buf, F_OK))
+ exists++;
+ else
+ broken++;
+ }
+
+ strbuf_addf(alternates_info,
+ "%zd alternates found (%zd working, %zd broken)\n",
+ exists + broken,
+ exists,
+ broken);
+
+ fclose(file);
+ strbuf_release(&alternate);
+ strbuf_release(&alternates_path);
+}
+
+static const char * const bugreport_usage[] = {
+ N_("git bugreport [-o|--output <file>]"),
+ 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 send.\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;
+ FILE *report;
+ time_t now = time(NULL);
+ char *option_output = NULL;
+
+ const struct option bugreport_options[] = {
+ OPT_STRING('o', "output", &option_output, N_("path"),
+ N_("specify a destination for the bugreport file")),
+ OPT_END()
+ };
+ argc = parse_options(argc, argv, "", bugreport_options,
+ bugreport_usage, 0);
+
+ if (option_output) {
+ strbuf_addstr(&report_path, option_output);
+ strbuf_complete(&report_path, '/');
+ }
+
+ strbuf_addstr(&report_path, "git-bugreport-");
+ strbuf_addftime(&report_path, "%F", gmtime(&now), 0, 0);
+ strbuf_addstr(&report_path, ".txt");
+
+
+ get_bug_template(&buffer);
+
+ get_header(&buffer, "System Info");
+ get_system_info(&buffer);
+
+ get_header(&buffer, "Safelisted Config Info");
+ get_safelisted_config(&buffer);
+
+ get_header(&buffer, "Configured Hooks");
+ get_populated_hooks(&buffer);
+
+ get_header(&buffer, "Loose Object Counts");
+ get_loose_object_summary(&buffer);
+
+ get_header(&buffer, "Packed Object Summary");
+ get_packed_object_summary(&buffer);
+
+ get_header(&buffer, "Object Info Summary");
+ get_object_info_summary(&buffer);
+
+ get_header(&buffer, "Alternates");
+ get_alternates_summary(&buffer);
+
+ report = fopen_for_writing(report_path.buf);
+ strbuf_write(&buffer, report);
+ fclose(report);
+
+ launch_editor(report_path.buf, NULL, NULL);
+ return 0;
}
deleted file mode 100644
@@ -1,44 +0,0 @@
-#include "strbuf.h"
-
-/**
- * Adds the Git version, `uname -a`, and `curl-config --version` to sys_info.
- * The previous contents of sys_info will be discarded.
- */
-void get_system_info(struct strbuf *sys_info);
-
-/**
- * Adds the values of the config items listed in
- * 'git-bugreport-config-whitelist' to config_info. The previous contents of
- * config_info will be discarded.
- */
-void get_whitelisted_config(struct strbuf *sys_info);
-
-/**
- * Adds the paths to all configured hooks (but not their contents). The previous
- * contents of hook_info will be discarded.
- */
-void get_populated_hooks(struct strbuf *hook_info);
-
-/**
- * Adds the output of `git count-object -vH`. The previous contents of hook_info
- * will be discarded.
- */
-void get_loose_object_summary(struct strbuf *obj_info);
-
-/**
- * Adds a list of the contents of '.git/objects/pack'. The previous contents of
- * hook_info will be discarded.
- */
-void get_packed_object_summary(struct strbuf *obj_info);
-
-/**
- * Adds a list of all contents (recursively) of '.git/objects/info'. The
- * previous contents of hook_info will be discarded.
- */
-void get_object_info_summary(struct strbuf *obj_info);
-
-/**
- * Adds the contents of '.git/info/alternates'. The previous contents of
- * alternates_info will be discarded.
- */
-void get_alternates_file(struct strbuf *alt_info);
@@ -135,7 +135,6 @@ int cmd_archive(int argc, const char **argv, const char *prefix);
int cmd_bisect__helper(int argc, const char **argv, const char *prefix);
int cmd_blame(int argc, const char **argv, const char *prefix);
int cmd_branch(int argc, const char **argv, const char *prefix);
-int cmd_bugreport(int argc, const char **argv, const char *prefix);
int cmd_bundle(int argc, const char **argv, const char *prefix);
int cmd_cat_file(int argc, const char **argv, const char *prefix);
int cmd_checkout(int argc, const char **argv, const char *prefix);
deleted file mode 100644
@@ -1,90 +0,0 @@
-#include "builtin.h"
-#include "bugreport.h"
-#include "stdio.h"
-#include "strbuf.h"
-#include "time.h"
-
-int get_bug_template(struct strbuf *template)
-{
- const char template_text[] =
-"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 send.\n";
-
- strbuf_reset(template);
- strbuf_add(template, template_text, strlen(template_text));
- return 0;
-}
-
-void add_header(FILE *report, const char *title)
-{
- struct strbuf buffer = STRBUF_INIT;
- strbuf_addf(&buffer, "\n\n[%s]\n", title);
- strbuf_write(&buffer, report);
-}
-
-int cmd_bugreport(int argc, const char **argv, const char *prefix)
-{
- struct strbuf buffer = STRBUF_INIT;
- struct strbuf report_path = STRBUF_INIT;
- FILE *report;
- time_t now = time(NULL);
-
- strbuf_addstr(&report_path, "git-bugreport-");
- strbuf_addftime(&report_path, "%F", gmtime(&now), 0, 0);
- strbuf_addstr(&report_path, ".txt");
-
- report = fopen_for_writing(report_path.buf);
-
- get_bug_template(&buffer);
- strbuf_write(&buffer, report);
-
- // add other contents
- add_header(report, "System Info");
- get_system_info(&buffer);
- strbuf_write(&buffer, report);
-
- add_header(report, "Whitelisted Config");
- get_whitelisted_config(&buffer);
- strbuf_write(&buffer, report);
-
- add_header(report, "Populated Hooks");
- get_populated_hooks(&buffer);
- strbuf_write(&buffer, report);
-
- add_header(report, "Object Counts");
- get_loose_object_summary(&buffer);
- strbuf_write(&buffer, report);
-
- add_header(report, "Packed Object Summary");
- get_packed_object_summary(&buffer);
- strbuf_write(&buffer, report);
-
- add_header(report, "Object Info Data");
- get_object_info_summary(&buffer);
- strbuf_write(&buffer, report);
-
- add_header(report, "Alternates File");
- get_alternates_file(&buffer);
- strbuf_write(&buffer, report);
-
- // Close file
- // open file in editor
- launch_editor(report_path, NULL, NULL);
- fclose(report);
-
- launch_editor(report_path.buf, NULL, NULL);
- return 0;
-}
@@ -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"))
@@ -3312,6 +3312,23 @@ enum config_scope current_config_scope(void)
return current_parsing_scope;
}
+const char *config_scope_to_string(enum config_scope scope)
+{
+ switch (scope) {
+ case CONFIG_SCOPE_SYSTEM:
+ return "system";
+ case CONFIG_SCOPE_GLOBAL:
+ return "global";
+ case CONFIG_SCOPE_REPO:
+ return "repo";
+ case CONFIG_SCOPE_CMDLINE:
+ return "cmdline";
+ case CONFIG_SCOPE_UNKNOWN:
+ default:
+ return "unknown";
+ }
+}
+
int lookup_config(const char **mapping, int nr_mapping, const char *var)
{
int i;
@@ -303,6 +303,7 @@ enum config_scope {
};
enum config_scope current_config_scope(void);
+const char *config_scope_to_string(enum config_scope);
const char *current_config_origin_type(void);
const char *current_config_name(void);
new file mode 100755
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+cat <<EOF
+/* Automatically generated by bugreport-generate-config-safelist.sh */
+
+
+static const char *bugreport_config_safelist[] = {
+EOF
+
+# cat all regular files in Documentation/config
+find Documentation/config -type f -exec cat {} \; |
+# print the command name which matches the bugreport-include macro
+sed -n 's/^\(.*\) \+bugreport:include.* ::$/\1/p' |
+sort |
+while read line
+do
+ echo " \"$line\","
+done
+
+cat <<EOF
+};
+EOF
@@ -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
new file mode 100755
@@ -0,0 +1,24 @@
+#!/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 |
+ while read line
+ do
+ echo " \"$line\","
+ done
+ cat <<EOF
+ NULL,
+};
+EOF
+}
+
+echo
+print_config_list
@@ -473,7 +473,6 @@ static struct cmd_struct commands[] = {
{ "bisect--helper", cmd_bisect__helper, RUN_SETUP },
{ "blame", cmd_blame, RUN_SETUP },
{ "branch", cmd_branch, RUN_SETUP | DELAY_PAGER_CONFIG },
- { "bugreport", cmd_bugreport, RUN_SETUP },
{ "bundle", cmd_bundle, RUN_SETUP_GENTLY | NO_PARSEOPT },
{ "cat-file", cmd_cat_file, RUN_SETUP },
{ "check-attr", cmd_check_attr, RUN_SETUP },
@@ -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,34 @@ const char *help_unknown_cmd(const char *cmd)
exit(1);
}
+void list_version_info(struct strbuf *buf, int build_options)
+{
+ strbuf_reset(buf);
+ /*
+ * 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 (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_addf(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));
+ strbuf_addf(buf, "shell-path: %s\n", SHELL_PATH);
+ /* 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 +663,9 @@ 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);
+ list_version_info(&buf, build_options);
+ printf("%s", buf.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;
}
@@ -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 list_version_info(struct strbuf *buf, int build_options);
/*
* call this to die(), when it is suspected that the user mistyped a
@@ -3,9 +3,18 @@
#include "exec-cmd.h"
#include "http.h"
#include "walker.h"
+#include "version.h"
static const char http_fetch_usage[] = "git http-fetch "
-"[-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin] commit-id url";
+"[-c] [-t] [-a] [-v] [-V] [--recover] [-w ref] [--stdin] commit-id url";
+
+void NORETURN version_info()
+{
+ printf("git-http-fetch version: %s\n", git_version_string);
+ printf("built from commit: %s\n", git_built_from_commit_string);
+ printf("curl version: %s\n", curl_version());
+ exit(0);
+}
int cmd_main(int argc, const char **argv)
{
@@ -26,6 +35,8 @@ int cmd_main(int argc, const char **argv)
} else if (argv[arg][1] == 'a') {
} else if (argv[arg][1] == 'v') {
get_verbosely = 1;
+ } else if (argv[arg][1] == 'V') {
+ version_info();
} else if (argv[arg][1] == 'w') {
write_ref = &argv[arg + 1];
arg++;
new file mode 100755
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+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 [$(grep $HEADER_PATTERN $line)]; then
+ read -r nextline
+ if [-z $nextline]; then
+ return 1;
+ fi
+ fi
+ done
+}
+
+test_expect_success 'creates a report with content in the right places' '
+ git bugreport &&
+ check_all_headers_populated <git-bugreport-* &&
+ rm git-bugreport-*
+'
+
+test_expect_success '--output puts the report in the provided dir' '
+ mkdir foo/ &&
+ git bugreport -o foo/ &&
+ test -f foo/git-bugreport-* &&
+ rm -fr foo/
+'
+
+test_expect_success 'incorrect arguments abort with usage' '
+ test_must_fail git bugreport --false 2>output &&
+ grep usage output &&
+ test ! -f git-bugreport-*
+'
+
+test_done