builtin/diagnose.c: create 'git diagnose' builtin
Create a 'git diagnose' builtin to generate a standalone zip archive of repository diagnostics. The "diagnose" functionality was originally implemented for Scalar in aa5c79a331 (scalar: implement `scalar diagnose`, 2022-05-28). However, the diagnostics gathered are not specific to Scalar-cloned repositories and can be useful when diagnosing issues in any Git repository. Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> 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
6783fd3cef0d6625e8a6d9d42d76042447078401
7 files changed
+143
.gitignore
+1
@@ -53,6 +53,7 @@
53
/git-cvsimport
54
/git-cvsserver
55
/git-daemon
56
+/git-diagnose
57
/git-diff
58
/git-diff-files
59
/git-diff-index
Documentation/git-diagnose.txt
new
+50
@@ -0,0 +1,50 @@
1
+git-diagnose(1)
2
+================
3
+
4
+NAME
5
+----
6
+git-diagnose - Generate a zip archive of diagnostic information
7
+
8
+SYNOPSIS
9
+--------
10
+[verse]
11
+'git diagnose' [(-o | --output-directory) <path>] [(-s | --suffix) <format>]
12
+
13
+DESCRIPTION
14
+-----------
15
+Collects detailed information about the user's machine, Git client, and
16
+repository state and packages that information into a zip archive. The
17
+generated archive can then, for example, be shared with the Git mailing list to
18
+help debug an issue or serve as a reference for independent debugging.
19
+
20
+The following information is captured in the archive:
21
+
22
+ * 'git version --build-options'
23
+ * The path to the repository root
24
+ * The available disk space on the filesystem
25
+ * The name and size of each packfile, including those in alternate object
26
+ stores
27
+ * The total count of loose objects, as well as counts broken down by
28
+ `.git/objects` subdirectory
29
+
30
+This tool differs from linkgit:git-bugreport[1] in that it collects much more
31
+detailed information with a greater focus on reporting the size and data shape
32
+of repository contents.
33
+
34
+OPTIONS
35
+-------
36
+-o <path>::
37
+--output-directory <path>::
38
+ Place the resulting diagnostics archive in `<path>` instead of the
39
+ current directory.
40
+
41
+-s <format>::
42
+--suffix <format>::
43
+ Specify an alternate suffix for the diagnostics archive name, to create
44
+ a file named 'git-diagnostics-<formatted suffix>'. This should take the
45
+ form of a strftime(3) format string; the current local time will be
46
+ used.
47
+
48
+GIT
49
+---
50
+Part of the linkgit:git[1] suite
Makefile
+1
@@ -1154,6 +1154,7 @@ BUILTIN_OBJS += builtin/credential-cache.o
1154
BUILTIN_OBJS += builtin/credential-store.o
1155
BUILTIN_OBJS += builtin/credential.o
1156
BUILTIN_OBJS += builtin/describe.o
1157
+BUILTIN_OBJS += builtin/diagnose.o
1158
BUILTIN_OBJS += builtin/diff-files.o
1159
BUILTIN_OBJS += builtin/diff-index.o
1160
BUILTIN_OBJS += builtin/diff-tree.o
builtin.h
+1
@@ -144,6 +144,7 @@ int cmd_credential_cache(int argc, const char **argv, const char *prefix);
144
int cmd_credential_cache_daemon(int argc, const char **argv, const char *prefix);
145
int cmd_credential_store(int argc, const char **argv, const char *prefix);
146
int cmd_describe(int argc, const char **argv, const char *prefix);
147
+int cmd_diagnose(int argc, const char **argv, const char *prefix);
148
int cmd_diff_files(int argc, const char **argv, const char *prefix);
149
int cmd_diff_index(int argc, const char **argv, const char *prefix);
150
int cmd_diff(int argc, const char **argv, const char *prefix);
builtin/diagnose.c
new
+57
@@ -0,0 +1,57 @@
1
+#include "builtin.h"
2
+#include "parse-options.h"
3
+#include "diagnose.h"
4
+
5
+static const char * const diagnose_usage[] = {
6
+ N_("git diagnose [-o|--output-directory <path>] [-s|--suffix <format>]"),
7
+ NULL
8
+};
9
+
10
+int cmd_diagnose(int argc, const char **argv, const char *prefix)
11
+{
12
+ struct strbuf zip_path = STRBUF_INIT;
13
+ time_t now = time(NULL);
14
+ struct tm tm;
15
+ char *option_output = NULL;
16
+ char *option_suffix = "%Y-%m-%d-%H%M";
17
+ char *prefixed_filename;
18
+
19
+ const struct option diagnose_options[] = {
20
+ OPT_STRING('o', "output-directory", &option_output, N_("path"),
21
+ N_("specify a destination for the diagnostics archive")),
22
+ OPT_STRING('s', "suffix", &option_suffix, N_("format"),
23
+ N_("specify a strftime format suffix for the filename")),
24
+ OPT_END()
25
+ };
26
+
27
+ argc = parse_options(argc, argv, prefix, diagnose_options,
28
+ diagnose_usage, 0);
29
+
30
+ /* Prepare the path to put the result */
31
+ prefixed_filename = prefix_filename(prefix,
32
+ option_output ? option_output : "");
33
+ strbuf_addstr(&zip_path, prefixed_filename);
34
+ strbuf_complete(&zip_path, '/');
35
+
36
+ strbuf_addstr(&zip_path, "git-diagnostics-");
37
+ strbuf_addftime(&zip_path, option_suffix, localtime_r(&now, &tm), 0, 0);
38
+ strbuf_addstr(&zip_path, ".zip");
39
+
40
+ switch (safe_create_leading_directories(zip_path.buf)) {
41
+ case SCLD_OK:
42
+ case SCLD_EXISTS:
43
+ break;
44
+ default:
45
+ die_errno(_("could not create leading directories for '%s'"),
46
+ zip_path.buf);
47
+ }
48
+
49
+ /* Prepare diagnostics */
50
+ if (create_diagnostics_archive(&zip_path, DIAGNOSE_STATS))
51
+ die_errno(_("unable to create diagnostics archive %s"),
52
+ zip_path.buf);
53
+
54
+ free(prefixed_filename);
55
+ strbuf_release(&zip_path);
56
+ return 0;
57
+}
git.c
+1
@@ -522,6 +522,7 @@ static struct cmd_struct commands[] = {
522
{ "credential-cache--daemon", cmd_credential_cache_daemon },
523
{ "credential-store", cmd_credential_store },
524
{ "describe", cmd_describe, RUN_SETUP },
525
+ { "diagnose", cmd_diagnose, RUN_SETUP_GENTLY },
526
{ "diff", cmd_diff, NO_PARSEOPT },
527
{ "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
528
{ "diff-index", cmd_diff_index, RUN_SETUP | NO_PARSEOPT },
t/t0092-diagnose.sh
new
+32
@@ -0,0 +1,32 @@
1
+#!/bin/sh
2
+
3
+test_description='git diagnose'
4
+
5
+TEST_PASSES_SANITIZE_LEAK=true
6
+. ./test-lib.sh
7
+
8
+test_expect_success UNZIP 'creates diagnostics zip archive' '
9
+ test_when_finished rm -rf report &&
10
+
11
+ git diagnose -o report -s test >out &&
12
+ grep "Available space" out &&
13
+
14
+ zip_path=report/git-diagnostics-test.zip &&
15
+ test_path_is_file "$zip_path" &&
16
+
17
+ # Check zipped archive content
18
+ "$GIT_UNZIP" -p "$zip_path" diagnostics.log >out &&
19
+ test_file_not_empty out &&
20
+
21
+ "$GIT_UNZIP" -p "$zip_path" packs-local.txt >out &&
22
+ grep ".git/objects" out &&
23
+
24
+ "$GIT_UNZIP" -p "$zip_path" objects-local.txt >out &&
25
+ grep "^Total: [0-9][0-9]*" out &&
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
30
+'
31
+
32
+test_done