Raw
1 #define USE_THE_REPOSITORY_VARIABLE
2
3 #include "test-tool.h"
4 #include "git-compat-util.h"
5 #include "delta.h"
6 #include "git-zlib.h"
7 #include "hash.h"
8 #include "hex.h"
9 #include "pack.h"
10 #include "packfile.h"
11 #include "pack-objects.h"
12 #include "parse-options.h"
13 #include "setup.h"
14 #include "strbuf.h"
15 #include "string-list.h"
16
17 static const char *usage_str[] = {
18 "test-tool pack-deltas --num-objects <num-objects>",
19 "test-tool pack-deltas --list-deltas <pack>.idx",
20 NULL
21 };
22
23 static unsigned long do_compress(void **pptr, unsigned long size)
24 {
25 git_zstream stream;
26 void *in, *out;
27 size_t maxsize;
28
29 git_deflate_init(&stream, 1);
30 maxsize = git_deflate_bound(&stream, size);
31
32 in = *pptr;
33 out = xmalloc(maxsize);
34 *pptr = out;
35
36 stream.next_in = in;
37 stream.avail_in = size;
38 stream.next_out = out;
39 stream.avail_out = maxsize;
40 while (git_deflate(&stream, Z_FINISH) == Z_OK)
41 ; /* nothing */
42 git_deflate_end(&stream);
43
44 free(in);
45 return stream.total_out;
46 }
47
48 static void write_ref_delta(struct hashfile *f,
49 struct object_id *oid,
50 struct object_id *base)
51 {
52 unsigned char header[MAX_PACK_OBJECT_HEADER];
53 unsigned long delta_size, compressed_size, hdrlen;
54 size_t size, base_size, delta_size_st = 0;
55 enum object_type type;
56 void *base_buf, *delta_buf;
57 void *buf = odb_read_object(the_repository->objects,
58 oid, &type, &size);
59
60 if (!buf)
61 die("unable to read %s", oid_to_hex(oid));
62
63 base_buf = odb_read_object(the_repository->objects,
64 base, &type, &base_size);
65
66 if (!base_buf)
67 die("unable to read %s", oid_to_hex(base));
68
69 delta_buf = diff_delta(base_buf, base_size,
70 buf, size, &delta_size_st, 0);
71 delta_size = cast_size_t_to_ulong(delta_size_st);
72
73 compressed_size = do_compress(&delta_buf, delta_size);
74
75 hdrlen = encode_in_pack_object_header(header, sizeof(header),
76 OBJ_REF_DELTA, delta_size);
77 hashwrite(f, header, hdrlen);
78 hashwrite(f, base->hash, the_repository->hash_algo->rawsz);
79 hashwrite(f, delta_buf, compressed_size);
80
81 free(buf);
82 free(base_buf);
83 free(delta_buf);
84 }
85
86 static int list_delta(const struct object_id *oid,
87 struct packed_git *p,
88 uint32_t pos,
89 void *_w_curs)
90 {
91 struct pack_window **w_curs = _w_curs;
92 off_t obj_offset = nth_packed_object_offset(p, pos);
93 off_t cur = obj_offset;
94 size_t size;
95 enum object_type type = unpack_object_header(p, w_curs, &cur,
96 &size);
97
98 if (type < 0)
99 die("unable to parse object at position %"PRIu32, pos);
100 if (type != OBJ_REF_DELTA && type != OBJ_OFS_DELTA)
101 return 0;
102
103 if (type == OBJ_REF_DELTA) {
104 struct object_id base_oid;
105 const unsigned char *base = use_pack(p, w_curs, cur,
106 NULL);
107
108 oidread(&base_oid, base, p->repo->hash_algo);
109 printf("%s REF_DELTA %s\n", oid_to_hex(oid),
110 oid_to_hex(&base_oid));
111 } else {
112 off_t base_offset = get_delta_base(p, w_curs, &cur,
113 type, obj_offset);
114
115 if (!base_offset)
116 die("unable to read base of object %s", oid_to_hex(oid));
117 printf("%s OFS_DELTA %"PRIuMAX"\n", oid_to_hex(oid),
118 (uintmax_t)base_offset);
119 }
120
121 return 0;
122 }
123
124 static void list_deltas(const char *idx_name)
125 {
126 struct packed_git *p;
127 struct pack_window *w_curs = NULL;
128
129 p = add_packed_git(the_repository, idx_name, strlen(idx_name), 1);
130 if (!p || open_pack_index(p))
131 die("unable to open pack index %s", idx_name);
132
133 if (for_each_object_in_pack(p, list_delta, &w_curs,
134 ODB_FOR_EACH_OBJECT_PACK_ORDER))
135 die("unable to iterate over objects in %s", idx_name);
136
137 unuse_pack(&w_curs);
138 close_pack(p);
139 free(p);
140 }
141
142 int cmd__pack_deltas(int argc, const char **argv)
143 {
144 int num_objects = -1;
145 int list_deltas_mode = 0;
146 struct hashfile *f;
147 struct strbuf line = STRBUF_INIT;
148 struct option options[] = {
149 OPT_INTEGER('n', "num-objects", &num_objects, N_("the number of objects to write")),
150 OPT_BOOL(0, "list-deltas", &list_deltas_mode,
151 N_("list REF_DELTA and OFS_DELTA entries")),
152 OPT_END()
153 };
154
155 argc = parse_options(argc, argv, NULL,
156 options, usage_str, 0);
157
158 if (list_deltas_mode) {
159 if (argc != 1 || num_objects >= 0)
160 usage_with_options(usage_str, options);
161 setup_git_directory(the_repository);
162 list_deltas(argv[0]);
163 return 0;
164 }
165
166 if (argc || num_objects < 0)
167 usage_with_options(usage_str, options);
168
169 setup_git_directory(the_repository);
170
171 f = hashfd(the_repository->hash_algo, 1, "<stdout>");
172 write_pack_header(f, num_objects);
173
174 /* Read each line from stdin into 'line' */
175 while (strbuf_getline_lf(&line, stdin) != EOF) {
176 const char *type_str, *content_oid_str, *base_oid_str = NULL;
177 struct object_id content_oid, base_oid;
178 struct string_list items = STRING_LIST_INIT_NODUP;
179 /*
180 * Tokenize into two or three parts:
181 * 1. REF_DELTA, OFS_DELTA, or FULL.
182 * 2. The object ID for the content object.
183 * 3. The object ID for the base object (optional).
184 */
185 if (string_list_split_in_place(&items, line.buf, " ", 3) < 0)
186 die("invalid input format: %s", line.buf);
187
188 if (items.nr < 2)
189 die("invalid input format: %s", line.buf);
190
191 type_str = items.items[0].string;
192 content_oid_str = items.items[1].string;
193
194 if (get_oid_hex(content_oid_str, &content_oid))
195 die("invalid object: %s", content_oid_str);
196 if (items.nr >= 3) {
197 base_oid_str = items.items[2].string;
198 if (get_oid_hex(base_oid_str, &base_oid))
199 die("invalid object: %s", base_oid_str);
200 }
201 string_list_clear(&items, 0);
202
203 if (!strcmp(type_str, "REF_DELTA"))
204 write_ref_delta(f, &content_oid, &base_oid);
205 else if (!strcmp(type_str, "OFS_DELTA"))
206 die("OFS_DELTA not implemented");
207 else if (!strcmp(type_str, "FULL"))
208 die("FULL not implemented");
209 else
210 die("unknown pack type: %s", type_str);
211 }
212
213 finalize_hashfile(f, NULL, FSYNC_COMPONENT_PACK,
214 CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE);
215 strbuf_release(&line);
216 return 0;
217 }