summaryrefslogtreecommitdiff
path: root/builtin-verify-pack.c
blob: b6079ae6cb03c7f3112c6eebc8c9a012d690a125 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#include "builtin.h"
#include "cache.h"
#include "pack.h"
#include "pack-revindex.h"
#include "parse-options.h"

#define MAX_CHAIN 50

#define VERIFY_PACK_VERBOSE 01
#define VERIFY_PACK_STAT_ONLY 02

static void show_pack_info(struct packed_git *p, unsigned int flags)
{
	uint32_t nr_objects, i;
	int cnt;
	int stat_only = flags & VERIFY_PACK_STAT_ONLY;
	unsigned long chain_histogram[MAX_CHAIN+1], baseobjects;

	nr_objects = p->num_objects;
	memset(chain_histogram, 0, sizeof(chain_histogram));
	baseobjects = 0;

	for (i = 0; i < nr_objects; i++) {
		const unsigned char *sha1;
		unsigned char base_sha1[20];
		const char *type;
		unsigned long size;
		unsigned long store_size;
		off_t offset;
		unsigned int delta_chain_length;

		sha1 = nth_packed_object_sha1(p, i);
		if (!sha1)
			die("internal error pack-check nth-packed-object");
		offset = nth_packed_object_offset(p, i);
		type = packed_object_info_detail(p, offset, &size, &store_size,
						 &delta_chain_length,
						 base_sha1);
		if (!stat_only)
			printf("%s ", sha1_to_hex(sha1));
		if (!delta_chain_length) {
			if (!stat_only)
				printf("%-6s %lu %lu %"PRIuMAX"\n",
				       type, size, store_size, (uintmax_t)offset);
			baseobjects++;
		}
		else {
			if (!stat_only)
				printf("%-6s %lu %lu %"PRIuMAX" %u %s\n",
				       type, size, store_size, (uintmax_t)offset,
				       delta_chain_length, sha1_to_hex(base_sha1));
			if (delta_chain_length <= MAX_CHAIN)
				chain_histogram[delta_chain_length]++;
			else
				chain_histogram[0]++;
		}
	}

	if (baseobjects)
		printf("non delta: %lu object%s\n",
		       baseobjects, baseobjects > 1 ? "s" : "");

	for (cnt = 1; cnt <= MAX_CHAIN; cnt++) {
		if (!chain_histogram[cnt])
			continue;
		printf("chain length = %d: %lu object%s\n", cnt,
		       chain_histogram[cnt],
		       chain_histogram[cnt] > 1 ? "s" : "");
	}
	if (chain_histogram[0])
		printf("chain length > %d: %lu object%s\n", MAX_CHAIN,
		       chain_histogram[0],
		       chain_histogram[0] > 1 ? "s" : "");
}

static int verify_one_pack(const char *path, unsigned int flags)
{
	char arg[PATH_MAX];
	int len;
	int verbose = flags & VERIFY_PACK_VERBOSE;
	int stat_only = flags & VERIFY_PACK_STAT_ONLY;
	struct packed_git *pack;
	int err;

	len = strlcpy(arg, path, PATH_MAX);
	if (len >= PATH_MAX)
		return error("name too long: %s", path);

	/*
	 * In addition to "foo.idx" we accept "foo.pack" and "foo";
	 * normalize these forms to "foo.idx" for add_packed_git().
	 */
	if (has_extension(arg, ".pack")) {
		strcpy(arg + len - 5, ".idx");
		len--;
	} else if (!has_extension(arg, ".idx")) {
		if (len + 4 >= PATH_MAX)
			return error("name too long: %s.idx", arg);
		strcpy(arg + len, ".idx");
		len += 4;
	}

	/*
	 * add_packed_git() uses our buffer (containing "foo.idx") to
	 * build the pack filename ("foo.pack").  Make sure it fits.
	 */
	if (len + 1 >= PATH_MAX) {
		arg[len - 4] = '\0';
		return error("name too long: %s.pack", arg);
	}

	pack = add_packed_git(arg, len, 1);
	if (!pack)
		return error("packfile %s not found.", arg);

	install_packed_git(pack);

	if (!stat_only)
		err = verify_pack(pack);
	else
		err = open_pack_index(pack);

	if (verbose || stat_only) {
		if (err)
			printf("%s: bad\n", pack->pack_name);
		else {
			show_pack_info(pack, flags);
			if (!stat_only)
				printf("%s: ok\n", pack->pack_name);
		}
	}

	return err;
}

static const char * const verify_pack_usage[] = {
	"git verify-pack [-v|--verbose] [-s|--stat-only] <pack>...",
	NULL
};

int cmd_verify_pack(int argc, const char **argv, const char *prefix)
{
	int err = 0;
	unsigned int flags = 0;
	int i;
	const struct option verify_pack_options[] = {
		OPT_BIT('v', "verbose", &flags, "verbose",
			VERIFY_PACK_VERBOSE),
		OPT_BIT('s', "stat-only", &flags, "show statistics only",
			VERIFY_PACK_STAT_ONLY),
		OPT_END()
	};

	git_config(git_default_config, NULL);
	argc = parse_options(argc, argv, prefix, verify_pack_options,
			     verify_pack_usage, 0);
	if (argc < 1)
		usage_with_options(verify_pack_usage, verify_pack_options);
	for (i = 0; i < argc; i++) {
		if (verify_one_pack(argv[i], flags))
			err = 1;
		discard_revindex();
	}

	return err;
}