Skip to content

Commit b899fda

Browse files
committed
commit graph: support sha256
1 parent be484d3 commit b899fda

File tree

5 files changed

+159
-52
lines changed

5 files changed

+159
-52
lines changed

include/git2/sys/commit_graph.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,13 @@ GIT_BEGIN_DECL
2828
* @param objects_dir the path to a git objects directory.
2929
* @return Zero on success; -1 on failure.
3030
*/
31-
GIT_EXTERN(int) git_commit_graph_open(git_commit_graph **cgraph_out, const char *objects_dir);
31+
GIT_EXTERN(int) git_commit_graph_open(
32+
git_commit_graph **cgraph_out,
33+
const char *objects_dir
34+
#ifdef GIT_EXPERIMENTAL_SHA256
35+
, git_oid_t oid_type
36+
#endif
37+
);
3238

3339
/**
3440
* Frees commit-graph data. This should only be called when memory allocated
@@ -50,7 +56,11 @@ GIT_EXTERN(void) git_commit_graph_free(git_commit_graph *cgraph);
5056
*/
5157
GIT_EXTERN(int) git_commit_graph_writer_new(
5258
git_commit_graph_writer **out,
53-
const char *objects_info_dir);
59+
const char *objects_info_dir
60+
#ifdef GIT_EXPERIMENTAL_SHA256
61+
, git_oid_t oid_type
62+
#endif
63+
);
5464

5565
/**
5666
* Free the commit-graph writer and its resources.

src/libgit2/commit_graph.c

Lines changed: 110 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -138,19 +138,22 @@ static int commit_graph_parse_oid_lookup(
138138
struct git_commit_graph_chunk *chunk_oid_lookup)
139139
{
140140
uint32_t i;
141-
unsigned char *oid, *prev_oid, zero_oid[GIT_OID_SHA1_SIZE] = {0};
141+
unsigned char *oid, *prev_oid, zero_oid[GIT_OID_MAX_SIZE] = {0};
142+
size_t oid_size;
143+
144+
oid_size = git_oid_size(file->oid_type);
142145

143146
if (chunk_oid_lookup->offset == 0)
144147
return commit_graph_error("missing OID Lookup chunk");
145148
if (chunk_oid_lookup->length == 0)
146149
return commit_graph_error("empty OID Lookup chunk");
147-
if (chunk_oid_lookup->length != file->num_commits * GIT_OID_SHA1_SIZE)
150+
if (chunk_oid_lookup->length != file->num_commits * oid_size)
148151
return commit_graph_error("OID Lookup chunk has wrong length");
149152

150153
file->oid_lookup = oid = (unsigned char *)(data + chunk_oid_lookup->offset);
151154
prev_oid = zero_oid;
152-
for (i = 0; i < file->num_commits; ++i, oid += GIT_OID_SHA1_SIZE) {
153-
if (git_oid_raw_cmp(prev_oid, oid, GIT_OID_SHA1_SIZE) >= 0)
155+
for (i = 0; i < file->num_commits; ++i, oid += oid_size) {
156+
if (git_oid_raw_cmp(prev_oid, oid, oid_size) >= 0)
154157
return commit_graph_error("OID Lookup index is non-monotonic");
155158
prev_oid = oid;
156159
}
@@ -163,11 +166,13 @@ static int commit_graph_parse_commit_data(
163166
const unsigned char *data,
164167
struct git_commit_graph_chunk *chunk_commit_data)
165168
{
169+
size_t oid_size = git_oid_size(file->oid_type);
170+
166171
if (chunk_commit_data->offset == 0)
167172
return commit_graph_error("missing Commit Data chunk");
168173
if (chunk_commit_data->length == 0)
169174
return commit_graph_error("empty Commit Data chunk");
170-
if (chunk_commit_data->length != file->num_commits * (GIT_OID_SHA1_SIZE + 16))
175+
if (chunk_commit_data->length != file->num_commits * (oid_size + 16))
171176
return commit_graph_error("Commit Data chunk has wrong length");
172177

173178
file->commit_data = data + chunk_commit_data->offset;
@@ -209,7 +214,9 @@ int git_commit_graph_file_parse(
209214

210215
GIT_ASSERT_ARG(file);
211216

212-
if (size < sizeof(struct git_commit_graph_header) + GIT_OID_SHA1_SIZE)
217+
checksum_size = git_oid_size(file->oid_type);
218+
219+
if (size < sizeof(struct git_commit_graph_header) + checksum_size)
213220
return commit_graph_error("commit-graph is too short");
214221

215222
hdr = ((struct git_commit_graph_header *)data);
@@ -226,8 +233,7 @@ int git_commit_graph_file_parse(
226233
* headers, and a special zero chunk.
227234
*/
228235
last_chunk_offset = sizeof(struct git_commit_graph_header) + (1 + hdr->chunks) * 12;
229-
trailer_offset = size - GIT_OID_SHA1_SIZE;
230-
checksum_size = GIT_HASH_SHA1_SIZE;
236+
trailer_offset = size - checksum_size;
231237

232238
if (trailer_offset < last_chunk_offset)
233239
return commit_graph_error("wrong commit-graph size");
@@ -295,25 +301,35 @@ int git_commit_graph_file_parse(
295301
return 0;
296302
}
297303

298-
int git_commit_graph_new(git_commit_graph **cgraph_out, const char *objects_dir, bool open_file)
304+
int git_commit_graph_new(
305+
git_commit_graph **cgraph_out,
306+
const char *objects_dir,
307+
bool open_file,
308+
git_oid_t oid_type)
299309
{
300310
git_commit_graph *cgraph = NULL;
301311
int error = 0;
302312

303313
GIT_ASSERT_ARG(cgraph_out);
304314
GIT_ASSERT_ARG(objects_dir);
315+
GIT_ASSERT_ARG(oid_type);
305316

306317
cgraph = git__calloc(1, sizeof(git_commit_graph));
307318
GIT_ERROR_CHECK_ALLOC(cgraph);
308319

320+
cgraph->oid_type = oid_type;
321+
309322
error = git_str_joinpath(&cgraph->filename, objects_dir, "info/commit-graph");
310323
if (error < 0)
311324
goto error;
312325

313326
if (open_file) {
314-
error = git_commit_graph_file_open(&cgraph->file, git_str_cstr(&cgraph->filename));
327+
error = git_commit_graph_file_open(&cgraph->file,
328+
git_str_cstr(&cgraph->filename), oid_type);
329+
315330
if (error < 0)
316331
goto error;
332+
317333
cgraph->checked = 1;
318334
}
319335

@@ -326,31 +342,51 @@ int git_commit_graph_new(git_commit_graph **cgraph_out, const char *objects_dir,
326342
}
327343

328344
int git_commit_graph_validate(git_commit_graph *cgraph) {
329-
unsigned char checksum[GIT_HASH_SHA1_SIZE];
330-
size_t checksum_size = GIT_HASH_SHA1_SIZE;
331-
size_t trailer_offset = cgraph->file->graph_map.len - checksum_size;
345+
unsigned char checksum[GIT_HASH_MAX_SIZE];
346+
git_hash_algorithm_t checksum_type;
347+
size_t checksum_size, trailer_offset;
348+
349+
checksum_type = git_oid_algorithm(cgraph->oid_type);
350+
checksum_size = git_hash_size(checksum_type);
351+
trailer_offset = cgraph->file->graph_map.len - checksum_size;
332352

333353
if (cgraph->file->graph_map.len < checksum_size)
334354
return commit_graph_error("map length too small");
335355

336-
if (git_hash_buf(checksum, cgraph->file->graph_map.data, trailer_offset, GIT_HASH_ALGORITHM_SHA1) < 0)
356+
if (git_hash_buf(checksum, cgraph->file->graph_map.data, trailer_offset, checksum_type) < 0)
337357
return commit_graph_error("could not calculate signature");
338358
if (memcmp(checksum, cgraph->file->checksum, checksum_size) != 0)
339359
return commit_graph_error("index signature mismatch");
340360

341361
return 0;
342362
}
343363

344-
int git_commit_graph_open(git_commit_graph **cgraph_out, const char *objects_dir)
364+
int git_commit_graph_open(
365+
git_commit_graph **cgraph_out,
366+
const char *objects_dir
367+
#ifdef GIT_EXPERIMENTAL_SHA256
368+
, git_oid_t oid_type
369+
#endif
370+
)
345371
{
346-
int error = git_commit_graph_new(cgraph_out, objects_dir, true);
347-
if (!error) {
372+
#ifndef GIT_EXPERIMENTAL_SHA256
373+
git_oid_t oid_type = GIT_OID_SHA1;
374+
#endif
375+
int error;
376+
377+
error = git_commit_graph_new(cgraph_out, objects_dir, true,
378+
oid_type);
379+
380+
if (!error)
348381
return git_commit_graph_validate(*cgraph_out);
349-
}
382+
350383
return error;
351384
}
352385

353-
int git_commit_graph_file_open(git_commit_graph_file **file_out, const char *path)
386+
int git_commit_graph_file_open(
387+
git_commit_graph_file **file_out,
388+
const char *path,
389+
git_oid_t oid_type)
354390
{
355391
git_commit_graph_file *file;
356392
git_file fd = -1;
@@ -379,6 +415,8 @@ int git_commit_graph_file_open(git_commit_graph_file **file_out, const char *pat
379415
file = git__calloc(1, sizeof(git_commit_graph_file));
380416
GIT_ERROR_CHECK_ALLOC(file);
381417

418+
file->oid_type = oid_type;
419+
382420
error = git_futils_mmap_ro(&file->graph_map, fd, 0, cgraph_size);
383421
p_close(fd);
384422
if (error < 0) {
@@ -395,7 +433,9 @@ int git_commit_graph_file_open(git_commit_graph_file **file_out, const char *pat
395433
return 0;
396434
}
397435

398-
int git_commit_graph_get_file(git_commit_graph_file **file_out, git_commit_graph *cgraph)
436+
int git_commit_graph_get_file(
437+
git_commit_graph_file **file_out,
438+
git_commit_graph *cgraph)
399439
{
400440
if (!cgraph->checked) {
401441
int error = 0;
@@ -405,7 +445,8 @@ int git_commit_graph_get_file(git_commit_graph_file **file_out, git_commit_graph
405445
cgraph->checked = 1;
406446

407447
/* Best effort */
408-
error = git_commit_graph_file_open(&result, git_str_cstr(&cgraph->filename));
448+
error = git_commit_graph_file_open(&result,
449+
git_str_cstr(&cgraph->filename), cgraph->oid_type);
409450

410451
if (error < 0)
411452
return error;
@@ -441,6 +482,7 @@ static int git_commit_graph_entry_get_byindex(
441482
size_t pos)
442483
{
443484
const unsigned char *commit_data;
485+
size_t oid_size = git_oid_size(file->oid_type);
444486

445487
GIT_ASSERT_ARG(e);
446488
GIT_ASSERT_ARG(file);
@@ -450,15 +492,15 @@ static int git_commit_graph_entry_get_byindex(
450492
return GIT_ENOTFOUND;
451493
}
452494

453-
commit_data = file->commit_data + pos * (GIT_OID_SHA1_SIZE + 4 * sizeof(uint32_t));
454-
git_oid__fromraw(&e->tree_oid, commit_data, GIT_OID_SHA1);
455-
e->parent_indices[0] = ntohl(*((uint32_t *)(commit_data + GIT_OID_SHA1_SIZE)));
495+
commit_data = file->commit_data + pos * (oid_size + 4 * sizeof(uint32_t));
496+
git_oid__fromraw(&e->tree_oid, commit_data, file->oid_type);
497+
e->parent_indices[0] = ntohl(*((uint32_t *)(commit_data + oid_size)));
456498
e->parent_indices[1] = ntohl(
457-
*((uint32_t *)(commit_data + GIT_OID_SHA1_SIZE + sizeof(uint32_t))));
499+
*((uint32_t *)(commit_data + oid_size + sizeof(uint32_t))));
458500
e->parent_count = (e->parent_indices[0] != GIT_COMMIT_GRAPH_MISSING_PARENT)
459501
+ (e->parent_indices[1] != GIT_COMMIT_GRAPH_MISSING_PARENT);
460-
e->generation = ntohl(*((uint32_t *)(commit_data + GIT_OID_SHA1_SIZE + 2 * sizeof(uint32_t))));
461-
e->commit_time = ntohl(*((uint32_t *)(commit_data + GIT_OID_SHA1_SIZE + 3 * sizeof(uint32_t))));
502+
e->generation = ntohl(*((uint32_t *)(commit_data + oid_size + 2 * sizeof(uint32_t))));
503+
e->commit_time = ntohl(*((uint32_t *)(commit_data + oid_size + 3 * sizeof(uint32_t))));
462504

463505
e->commit_time |= (e->generation & UINT64_C(0x3)) << UINT64_C(32);
464506
e->generation >>= 2u;
@@ -485,7 +527,7 @@ static int git_commit_graph_entry_get_byindex(
485527
}
486528
}
487529

488-
git_oid__fromraw(&e->sha1, &file->oid_lookup[pos * GIT_OID_SHA1_SIZE], GIT_OID_SHA1);
530+
git_oid__fromraw(&e->sha1, &file->oid_lookup[pos * oid_size], file->oid_type);
489531
return 0;
490532
}
491533

@@ -494,8 +536,8 @@ bool git_commit_graph_file_needs_refresh(const git_commit_graph_file *file, cons
494536
git_file fd = -1;
495537
struct stat st;
496538
ssize_t bytes_read;
497-
unsigned char checksum[GIT_HASH_SHA1_SIZE];
498-
size_t checksum_size = GIT_HASH_SHA1_SIZE;
539+
unsigned char checksum[GIT_HASH_MAX_SIZE];
540+
size_t checksum_size = git_oid_size(file->oid_type);
499541

500542
/* TODO: properly open the file without access time using O_NOATIME */
501543
fd = git_futils_open_ro(path);
@@ -530,35 +572,40 @@ int git_commit_graph_entry_find(
530572
int pos, found = 0;
531573
uint32_t hi, lo;
532574
const unsigned char *current = NULL;
575+
size_t oid_size, oid_hexsize;
533576

534577
GIT_ASSERT_ARG(e);
535578
GIT_ASSERT_ARG(file);
536579
GIT_ASSERT_ARG(short_oid);
537580

581+
oid_size = git_oid_size(file->oid_type);
582+
oid_hexsize = git_oid_hexsize(file->oid_type);
583+
538584
hi = ntohl(file->oid_fanout[(int)short_oid->id[0]]);
539585
lo = ((short_oid->id[0] == 0x0) ? 0 : ntohl(file->oid_fanout[(int)short_oid->id[0] - 1]));
540586

541-
pos = git_pack__lookup_id(file->oid_lookup, GIT_OID_SHA1_SIZE, lo, hi, short_oid->id, GIT_OID_SHA1);
587+
pos = git_pack__lookup_id(file->oid_lookup, oid_size, lo, hi,
588+
short_oid->id, file->oid_type);
542589

543590
if (pos >= 0) {
544591
/* An object matching exactly the oid was found */
545592
found = 1;
546-
current = file->oid_lookup + (pos * GIT_OID_SHA1_SIZE);
593+
current = file->oid_lookup + (pos * oid_size);
547594
} else {
548595
/* No object was found */
549596
/* pos refers to the object with the "closest" oid to short_oid */
550597
pos = -1 - pos;
551598
if (pos < (int)file->num_commits) {
552-
current = file->oid_lookup + (pos * GIT_OID_SHA1_SIZE);
599+
current = file->oid_lookup + (pos * oid_size);
553600

554601
if (!git_oid_raw_ncmp(short_oid->id, current, len))
555602
found = 1;
556603
}
557604
}
558605

559-
if (found && len != GIT_OID_SHA1_HEXSIZE && pos + 1 < (int)file->num_commits) {
606+
if (found && len != oid_hexsize && pos + 1 < (int)file->num_commits) {
560607
/* Check for ambiguousity */
561-
const unsigned char *next = current + GIT_OID_SHA1_SIZE;
608+
const unsigned char *next = current + oid_size;
562609

563610
if (!git_oid_raw_ncmp(short_oid->id, next, len))
564611
found = 2;
@@ -637,11 +684,27 @@ static int packed_commit__cmp(const void *a_, const void *b_)
637684
return git_oid_cmp(&a->sha1, &b->sha1);
638685
}
639686

640-
int git_commit_graph_writer_new(git_commit_graph_writer **out, const char *objects_info_dir)
687+
int git_commit_graph_writer_new(
688+
git_commit_graph_writer **out,
689+
const char *objects_info_dir
690+
#ifdef GIT_EXPERIMENTAL_SHA256
691+
, git_oid_t oid_type
692+
#endif
693+
)
641694
{
642-
git_commit_graph_writer *w = git__calloc(1, sizeof(git_commit_graph_writer));
695+
git_commit_graph_writer *w;
696+
697+
#ifndef GIT_EXPERIMENTAL_SHA256
698+
git_oid_t oid_type = GIT_OID_SHA1;
699+
#endif
700+
701+
GIT_ASSERT_ARG(out && objects_info_dir && oid_type);
702+
703+
w = git__calloc(1, sizeof(git_commit_graph_writer));
643704
GIT_ERROR_CHECK_ALLOC(w);
644705

706+
w->oid_type = oid_type;
707+
645708
if (git_str_sets(&w->objects_info_dir, objects_info_dir) < 0) {
646709
git__free(w);
647710
return -1;
@@ -993,8 +1056,9 @@ static int commit_graph_write(
9931056
off64_t offset;
9941057
git_str oid_lookup = GIT_STR_INIT, commit_data = GIT_STR_INIT,
9951058
extra_edge_list = GIT_STR_INIT;
996-
unsigned char checksum[GIT_HASH_SHA1_SIZE];
997-
size_t checksum_size;
1059+
unsigned char checksum[GIT_HASH_MAX_SIZE];
1060+
git_hash_algorithm_t checksum_type;
1061+
size_t checksum_size, oid_size;
9981062
git_hash_ctx ctx;
9991063
struct commit_graph_write_hash_context hash_cb_data = {0};
10001064

@@ -1007,8 +1071,11 @@ static int commit_graph_write(
10071071
hash_cb_data.cb_data = cb_data;
10081072
hash_cb_data.ctx = &ctx;
10091073

1010-
checksum_size = GIT_HASH_SHA1_SIZE;
1011-
error = git_hash_ctx_init(&ctx, GIT_HASH_ALGORITHM_SHA1);
1074+
oid_size = git_oid_size(w->oid_type);
1075+
checksum_type = git_oid_algorithm(w->oid_type);
1076+
checksum_size = git_hash_size(checksum_type);
1077+
1078+
error = git_hash_ctx_init(&ctx, checksum_type);
10121079
if (error < 0)
10131080
return error;
10141081
cb_data = &hash_cb_data;
@@ -1035,7 +1102,7 @@ static int commit_graph_write(
10351102
git_vector_foreach (&w->commits, i, packed_commit) {
10361103
error = git_str_put(&oid_lookup,
10371104
(const char *)&packed_commit->sha1.id,
1038-
GIT_OID_SHA1_SIZE);
1105+
oid_size);
10391106

10401107
if (error < 0)
10411108
goto cleanup;
@@ -1052,7 +1119,7 @@ static int commit_graph_write(
10521119

10531120
error = git_str_put(&commit_data,
10541121
(const char *)&packed_commit->tree_oid.id,
1055-
GIT_OID_SHA1_SIZE);
1122+
oid_size);
10561123

10571124
if (error < 0)
10581125
goto cleanup;

0 commit comments

Comments
 (0)