builtin/diagnose.c: add '--mode' option

Create '--mode=<mode>' option in 'git diagnose' to allow users to optionally
select non-default diagnostic information to include in the output archive.
Additionally, document the currently-available modes, emphasizing the
importance of not sharing a '--mode=all' archive publicly due to the
presence of sensitive information.

Note that the option parsing callback - 'option_parse_diagnose()' - is added
to 'diagnose.c' rather than 'builtin/diagnose.c' so that it may be reused in
future callers configuring a diagnostics archive.

Helped-by: Derrick Stolee <derrickstolee@github.com>
Signed-off-by: Victoria Dye <vdye@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Victoria Dye 2022-08-12 20:10:16 +00:00 committed by Junio C Hamano
parent 6783fd3cef
commit 7ecf193f7d
5 changed files with 84 additions and 4 deletions

View File

@ -9,6 +9,7 @@ SYNOPSIS
-------- --------
[verse] [verse]
'git diagnose' [(-o | --output-directory) <path>] [(-s | --suffix) <format>] 'git diagnose' [(-o | --output-directory) <path>] [(-s | --suffix) <format>]
[--mode=<mode>]
DESCRIPTION DESCRIPTION
----------- -----------
@ -17,7 +18,7 @@ repository state and packages that information into a zip archive. The
generated archive can then, for example, be shared with the Git mailing list to generated archive can then, for example, be shared with the Git mailing list to
help debug an issue or serve as a reference for independent debugging. help debug an issue or serve as a reference for independent debugging.
The following information is captured in the archive: By default, the following information is captured in the archive:
* 'git version --build-options' * 'git version --build-options'
* The path to the repository root * The path to the repository root
@ -27,6 +28,9 @@ The following information is captured in the archive:
* The total count of loose objects, as well as counts broken down by * The total count of loose objects, as well as counts broken down by
`.git/objects` subdirectory `.git/objects` subdirectory
Additional information can be collected by selecting a different diagnostic mode
using the `--mode` option.
This tool differs from linkgit:git-bugreport[1] in that it collects much more This tool differs from linkgit:git-bugreport[1] in that it collects much more
detailed information with a greater focus on reporting the size and data shape detailed information with a greater focus on reporting the size and data shape
of repository contents. of repository contents.
@ -45,6 +49,17 @@ OPTIONS
form of a strftime(3) format string; the current local time will be form of a strftime(3) format string; the current local time will be
used. used.
--mode=(stats|all)::
Specify the type of diagnostics that should be collected. The default behavior
of 'git diagnose' is equivalent to `--mode=stats`.
+
The `--mode=all` option collects everything included in `--mode=stats`, as well
as copies of `.git`, `.git/hooks`, `.git/info`, `.git/logs`, and
`.git/objects/info` directories. This additional information may be sensitive,
as it can be used to reconstruct the full contents of the diagnosed repository.
Users should exercise caution when sharing an archive generated with
`--mode=all`.
GIT GIT
--- ---
Part of the linkgit:git[1] suite Part of the linkgit:git[1] suite

View File

@ -3,7 +3,7 @@
#include "diagnose.h" #include "diagnose.h"
static const char * const diagnose_usage[] = { static const char * const diagnose_usage[] = {
N_("git diagnose [-o|--output-directory <path>] [-s|--suffix <format>]"), N_("git diagnose [-o|--output-directory <path>] [-s|--suffix <format>] [--mode=<mode>]"),
NULL NULL
}; };
@ -12,6 +12,7 @@ int cmd_diagnose(int argc, const char **argv, const char *prefix)
struct strbuf zip_path = STRBUF_INIT; struct strbuf zip_path = STRBUF_INIT;
time_t now = time(NULL); time_t now = time(NULL);
struct tm tm; struct tm tm;
enum diagnose_mode mode = DIAGNOSE_STATS;
char *option_output = NULL; char *option_output = NULL;
char *option_suffix = "%Y-%m-%d-%H%M"; char *option_suffix = "%Y-%m-%d-%H%M";
char *prefixed_filename; char *prefixed_filename;
@ -21,6 +22,9 @@ int cmd_diagnose(int argc, const char **argv, const char *prefix)
N_("specify a destination for the diagnostics archive")), N_("specify a destination for the diagnostics archive")),
OPT_STRING('s', "suffix", &option_suffix, N_("format"), OPT_STRING('s', "suffix", &option_suffix, N_("format"),
N_("specify a strftime format suffix for the filename")), N_("specify a strftime format suffix for the filename")),
OPT_CALLBACK_F(0, "mode", &mode, N_("(stats|all)"),
N_("specify the content of the diagnostic archive"),
PARSE_OPT_NONEG, option_parse_diagnose),
OPT_END() OPT_END()
}; };
@ -47,7 +51,7 @@ int cmd_diagnose(int argc, const char **argv, const char *prefix)
} }
/* Prepare diagnostics */ /* Prepare diagnostics */
if (create_diagnostics_archive(&zip_path, DIAGNOSE_STATS)) if (create_diagnostics_archive(&zip_path, mode))
die_errno(_("unable to create diagnostics archive %s"), die_errno(_("unable to create diagnostics archive %s"),
zip_path.buf); zip_path.buf);

View File

@ -13,6 +13,36 @@ struct archive_dir {
int recursive; int recursive;
}; };
struct diagnose_option {
enum diagnose_mode mode;
const char *option_name;
};
static struct diagnose_option diagnose_options[] = {
{ DIAGNOSE_STATS, "stats" },
{ DIAGNOSE_ALL, "all" },
};
int option_parse_diagnose(const struct option *opt, const char *arg, int unset)
{
int i;
enum diagnose_mode *diagnose = opt->value;
if (!arg) {
*diagnose = unset ? DIAGNOSE_NONE : DIAGNOSE_STATS;
return 0;
}
for (i = 0; i < ARRAY_SIZE(diagnose_options); i++) {
if (!strcmp(arg, diagnose_options[i].option_name)) {
*diagnose = diagnose_options[i].mode;
return 0;
}
}
return error(_("invalid --%s value '%s'"), opt->long_name, arg);
}
static void dir_file_stats_objects(const char *full_path, size_t full_path_len, static void dir_file_stats_objects(const char *full_path, size_t full_path_len,
const char *file_name, void *data) const char *file_name, void *data)
{ {

View File

@ -2,6 +2,7 @@
#define DIAGNOSE_H #define DIAGNOSE_H
#include "strbuf.h" #include "strbuf.h"
#include "parse-options.h"
enum diagnose_mode { enum diagnose_mode {
DIAGNOSE_NONE, DIAGNOSE_NONE,
@ -9,6 +10,8 @@ enum diagnose_mode {
DIAGNOSE_ALL DIAGNOSE_ALL
}; };
int option_parse_diagnose(const struct option *opt, const char *arg, int unset);
int create_diagnostics_archive(struct strbuf *zip_path, enum diagnose_mode mode); int create_diagnostics_archive(struct strbuf *zip_path, enum diagnose_mode mode);
#endif /* DIAGNOSE_H */ #endif /* DIAGNOSE_H */

View File

@ -26,7 +26,35 @@ test_expect_success UNZIP 'creates diagnostics zip archive' '
# Should not include .git directory contents by default # Should not include .git directory contents by default
! "$GIT_UNZIP" -l "$zip_path" | grep ".git/" ! "$GIT_UNZIP" -l "$zip_path" | grep ".git/"
grep "^Total: [0-9][0-9]*" out '
test_expect_success UNZIP '--mode=stats excludes .git dir contents' '
test_when_finished rm -rf report &&
git diagnose -o report -s test --mode=stats >out &&
# Includes pack quantity/size info
"$GIT_UNZIP" -p "$zip_path" packs-local.txt >out &&
grep ".git/objects" out &&
# Does not include .git directory contents
! "$GIT_UNZIP" -l "$zip_path" | grep ".git/"
'
test_expect_success UNZIP '--mode=all includes .git dir contents' '
test_when_finished rm -rf report &&
git diagnose -o report -s test --mode=all >out &&
# Includes pack quantity/size info
"$GIT_UNZIP" -p "$zip_path" packs-local.txt >out &&
grep ".git/objects" out &&
# Includes .git directory contents
"$GIT_UNZIP" -l "$zip_path" | grep ".git/" &&
"$GIT_UNZIP" -p "$zip_path" .git/HEAD >out &&
test_file_not_empty out
' '
test_done test_done