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>

Victoria Dye committed Aug 12, 2022 at 20:10 UTC 7ecf193f7d621f618b322498d884ee103a44522f
5 files changed +84 -4
Documentation/git-diagnose.txt
+16 -1
@@ -9,6 +9,7 @@ SYNOPSIS
9 --------
10 [verse]
11 'git diagnose' [(-o | --output-directory) <path>] [(-s | --suffix) <format>]
12 + [--mode=<mode>]
13
14 DESCRIPTION
15 -----------
@@ -17,7 +18,7 @@ repository state and packages that information into a zip archive. The
18 generated archive can then, for example, be shared with the Git mailing list to
19 help debug an issue or serve as a reference for independent debugging.
20
20 -The following information is captured in the archive:
21 +By default, the following information is captured in the archive:
22
23 * 'git version --build-options'
24 * The path to the repository root
@@ -27,6 +28,9 @@ The following information is captured in the archive:
28 * The total count of loose objects, as well as counts broken down by
29 `.git/objects` subdirectory
30
31 +Additional information can be collected by selecting a different diagnostic mode
32 +using the `--mode` option.
33 +
34 This tool differs from linkgit:git-bugreport[1] in that it collects much more
35 detailed information with a greater focus on reporting the size and data shape
36 of repository contents.
@@ -45,6 +49,17 @@ OPTIONS
49 form of a strftime(3) format string; the current local time will be
50 used.
51
52 +--mode=(stats|all)::
53 + Specify the type of diagnostics that should be collected. The default behavior
54 + of 'git diagnose' is equivalent to `--mode=stats`.
55 ++
56 +The `--mode=all` option collects everything included in `--mode=stats`, as well
57 +as copies of `.git`, `.git/hooks`, `.git/info`, `.git/logs`, and
58 +`.git/objects/info` directories. This additional information may be sensitive,
59 +as it can be used to reconstruct the full contents of the diagnosed repository.
60 +Users should exercise caution when sharing an archive generated with
61 +`--mode=all`.
62 +
63 GIT
64 ---
65 Part of the linkgit:git[1] suite
builtin/diagnose.c
+6 -2
@@ -3,7 +3,7 @@
3 #include "diagnose.h"
4
5 static const char * const diagnose_usage[] = {
6 - N_("git diagnose [-o|--output-directory <path>] [-s|--suffix <format>]"),
6 + N_("git diagnose [-o|--output-directory <path>] [-s|--suffix <format>] [--mode=<mode>]"),
7 NULL
8 };
9
@@ -12,6 +12,7 @@ int cmd_diagnose(int argc, const char **argv, const char *prefix)
12 struct strbuf zip_path = STRBUF_INIT;
13 time_t now = time(NULL);
14 struct tm tm;
15 + enum diagnose_mode mode = DIAGNOSE_STATS;
16 char *option_output = NULL;
17 char *option_suffix = "%Y-%m-%d-%H%M";
18 char *prefixed_filename;
@@ -21,6 +22,9 @@ int cmd_diagnose(int argc, const char **argv, const char *prefix)
22 N_("specify a destination for the diagnostics archive")),
23 OPT_STRING('s', "suffix", &option_suffix, N_("format"),
24 N_("specify a strftime format suffix for the filename")),
25 + OPT_CALLBACK_F(0, "mode", &mode, N_("(stats|all)"),
26 + N_("specify the content of the diagnostic archive"),
27 + PARSE_OPT_NONEG, option_parse_diagnose),
28 OPT_END()
29 };
30
@@ -47,7 +51,7 @@ int cmd_diagnose(int argc, const char **argv, const char *prefix)
51 }
52
53 /* Prepare diagnostics */
50 - if (create_diagnostics_archive(&zip_path, DIAGNOSE_STATS))
54 + if (create_diagnostics_archive(&zip_path, mode))
55 die_errno(_("unable to create diagnostics archive %s"),
56 zip_path.buf);
57
diagnose.c
+30
@@ -13,6 +13,36 @@ struct archive_dir {
13 int recursive;
14 };
15
16 +struct diagnose_option {
17 + enum diagnose_mode mode;
18 + const char *option_name;
19 +};
20 +
21 +static struct diagnose_option diagnose_options[] = {
22 + { DIAGNOSE_STATS, "stats" },
23 + { DIAGNOSE_ALL, "all" },
24 +};
25 +
26 +int option_parse_diagnose(const struct option *opt, const char *arg, int unset)
27 +{
28 + int i;
29 + enum diagnose_mode *diagnose = opt->value;
30 +
31 + if (!arg) {
32 + *diagnose = unset ? DIAGNOSE_NONE : DIAGNOSE_STATS;
33 + return 0;
34 + }
35 +
36 + for (i = 0; i < ARRAY_SIZE(diagnose_options); i++) {
37 + if (!strcmp(arg, diagnose_options[i].option_name)) {
38 + *diagnose = diagnose_options[i].mode;
39 + return 0;
40 + }
41 + }
42 +
43 + return error(_("invalid --%s value '%s'"), opt->long_name, arg);
44 +}
45 +
46 static void dir_file_stats_objects(const char *full_path, size_t full_path_len,
47 const char *file_name, void *data)
48 {
diagnose.h
+3
@@ -2,6 +2,7 @@
2 #define DIAGNOSE_H
3
4 #include "strbuf.h"
5 +#include "parse-options.h"
6
7 enum diagnose_mode {
8 DIAGNOSE_NONE,
@@ -9,6 +10,8 @@ enum diagnose_mode {
10 DIAGNOSE_ALL
11 };
12
13 +int option_parse_diagnose(const struct option *opt, const char *arg, int unset);
14 +
15 int create_diagnostics_archive(struct strbuf *zip_path, enum diagnose_mode mode);
16
17 #endif /* DIAGNOSE_H */
t/t0092-diagnose.sh
+29 -1
@@ -26,7 +26,35 @@ test_expect_success UNZIP 'creates diagnostics zip archive' '
26
27 # Should not include .git directory contents by default
28 ! "$GIT_UNZIP" -l "$zip_path" | grep ".git/"
29 - grep "^Total: [0-9][0-9]*" out
29 +'
30 +
31 +test_expect_success UNZIP '--mode=stats excludes .git dir contents' '
32 + test_when_finished rm -rf report &&
33 +
34 + git diagnose -o report -s test --mode=stats >out &&
35 +
36 + # Includes pack quantity/size info
37 + "$GIT_UNZIP" -p "$zip_path" packs-local.txt >out &&
38 + grep ".git/objects" out &&
39 +
40 + # Does not include .git directory contents
41 + ! "$GIT_UNZIP" -l "$zip_path" | grep ".git/"
42 +'
43 +
44 +test_expect_success UNZIP '--mode=all includes .git dir contents' '
45 + test_when_finished rm -rf report &&
46 +
47 + git diagnose -o report -s test --mode=all >out &&
48 +
49 + # Includes pack quantity/size info
50 + "$GIT_UNZIP" -p "$zip_path" packs-local.txt >out &&
51 + grep ".git/objects" out &&
52 +
53 + # Includes .git directory contents
54 + "$GIT_UNZIP" -l "$zip_path" | grep ".git/" &&
55 +
56 + "$GIT_UNZIP" -p "$zip_path" .git/HEAD >out &&
57 + test_file_not_empty out
58 '
59
60 test_done