Skip to content

Commit 147b659

Browse files
authored
Merge pull request libgit2#5405 from lhchavez/multi-pack-index-odb-write
midx: Introduce git_odb_write_multi_pack_index()
2 parents 1829338 + c3512fe commit 147b659

File tree

5 files changed

+163
-5
lines changed

5 files changed

+163
-5
lines changed

include/git2/odb.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,20 @@ GIT_EXTERN(int) git_odb_write_pack(
390390
git_indexer_progress_cb progress_cb,
391391
void *progress_payload);
392392

393+
/**
394+
* Write a `multi-pack-index` file from all the `.pack` files in the ODB.
395+
*
396+
* If the ODB layer understands pack files, then this will create a file called
397+
* `multi-pack-index` next to the `.pack` and `.idx` files, which will contain
398+
* an index of all objects stored in `.pack` files. This will allow for
399+
* O(log n) lookup for n objects (regardless of how many packfiles there
400+
* exist).
401+
*
402+
* @param db object database where the `multi-pack-index` file will be written.
403+
*/
404+
GIT_EXTERN(int) git_odb_write_multi_pack_index(
405+
git_odb *db);
406+
393407
/**
394408
* Determine the object-ID (sha1 hash) of a data buffer
395409
*

include/git2/sys/odb_backend.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,13 @@ struct git_odb_backend {
8484
git_odb_writepack **, git_odb_backend *, git_odb *odb,
8585
git_indexer_progress_cb progress_cb, void *progress_payload);
8686

87+
/**
88+
* If the backend supports pack files, this will create a
89+
* `multi-pack-index` file which will contain an index of all objects
90+
* across all the `.pack` files.
91+
*/
92+
int GIT_CALLBACK(writemidx)(git_odb_backend *);
93+
8794
/**
8895
* "Freshens" an already existing object, updating its last-used
8996
* time. This occurs when `git_odb_write` was called, but the

src/odb.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1703,6 +1703,35 @@ int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_indexer_
17031703
return error;
17041704
}
17051705

1706+
int git_odb_write_multi_pack_index(git_odb *db)
1707+
{
1708+
size_t i, writes = 0;
1709+
int error = GIT_ERROR;
1710+
1711+
GIT_ASSERT_ARG(db);
1712+
1713+
for (i = 0; i < db->backends.length && error < 0; ++i) {
1714+
backend_internal *internal = git_vector_get(&db->backends, i);
1715+
git_odb_backend *b = internal->backend;
1716+
1717+
/* we don't write in alternates! */
1718+
if (internal->is_alternate)
1719+
continue;
1720+
1721+
if (b->writemidx != NULL) {
1722+
++writes;
1723+
error = b->writemidx(b);
1724+
}
1725+
}
1726+
1727+
if (error == GIT_PASSTHROUGH)
1728+
error = 0;
1729+
if (error < 0 && !writes)
1730+
error = git_odb__error_unsupported_in_backend("write multi-pack-index");
1731+
1732+
return error;
1733+
}
1734+
17061735
void *git_odb_backend_data_alloc(git_odb_backend *backend, size_t len)
17071736
{
17081737
GIT_UNUSED(backend);

src/odb_pack.c

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,6 @@ static int process_multi_pack_index_pack(
402402
const char *packfile_name)
403403
{
404404
int error;
405-
size_t cmp_len = strlen(packfile_name);
406405
struct git_pack_file *pack;
407406
size_t found_position;
408407
git_buf pack_path = GIT_BUF_INIT, index_prefix = GIT_BUF_INIT;
@@ -411,12 +410,11 @@ static int process_multi_pack_index_pack(
411410
if (error < 0)
412411
return error;
413412

414-
/* This is ensured by midx__parse_packfile_name() */
415-
if (cmp_len <= strlen(".idx") || git__suffixcmp(git_buf_cstr(&pack_path), ".idx") != 0)
413+
/* This is ensured by midx_parse_packfile_name() */
414+
if (git_buf_len(&pack_path) <= strlen(".idx") || git__suffixcmp(git_buf_cstr(&pack_path), ".idx") != 0)
416415
return git_odb__error_notfound("midx file contained a non-index", NULL, 0);
417416

418-
cmp_len -= strlen(".idx");
419-
git_buf_attach_notowned(&index_prefix, git_buf_cstr(&pack_path), cmp_len);
417+
git_buf_attach_notowned(&index_prefix, git_buf_cstr(&pack_path), git_buf_len(&pack_path) - strlen(".idx"));
420418

421419
if (git_vector_search2(&found_position, &backend->packs, packfile_byname_search_cmp, &index_prefix) == 0) {
422420
/* Pack was found in the packs list. Moving it to the midx_packs list. */
@@ -744,6 +742,81 @@ static int pack_backend__writepack(struct git_odb_writepack **out,
744742
return 0;
745743
}
746744

745+
static int get_idx_path(
746+
git_buf *idx_path,
747+
struct pack_backend *backend,
748+
struct git_pack_file *p)
749+
{
750+
size_t path_len;
751+
int error;
752+
753+
error = git_path_prettify(idx_path, p->pack_name, backend->pack_folder);
754+
if (error < 0)
755+
return error;
756+
path_len = git_buf_len(idx_path);
757+
if (path_len <= strlen(".pack") || git__suffixcmp(git_buf_cstr(idx_path), ".pack") != 0)
758+
return git_odb__error_notfound("packfile does not end in .pack", NULL, 0);
759+
path_len -= strlen(".pack");
760+
error = git_buf_splice(idx_path, path_len, strlen(".pack"), ".idx", strlen(".idx"));
761+
if (error < 0)
762+
return error;
763+
764+
return 0;
765+
}
766+
767+
static int pack_backend__writemidx(git_odb_backend *_backend)
768+
{
769+
struct pack_backend *backend;
770+
git_midx_writer *w = NULL;
771+
struct git_pack_file *p;
772+
size_t i;
773+
int error = 0;
774+
775+
GIT_ASSERT_ARG(_backend);
776+
777+
backend = (struct pack_backend *)_backend;
778+
779+
error = git_midx_writer_new(&w, backend->pack_folder);
780+
if (error < 0)
781+
return error;
782+
783+
git_vector_foreach(&backend->midx_packs, i, p) {
784+
git_buf idx_path = GIT_BUF_INIT;
785+
error = get_idx_path(&idx_path, backend, p);
786+
if (error < 0)
787+
goto cleanup;
788+
error = git_midx_writer_add(w, git_buf_cstr(&idx_path));
789+
git_buf_dispose(&idx_path);
790+
if (error < 0)
791+
goto cleanup;
792+
}
793+
git_vector_foreach(&backend->packs, i, p) {
794+
git_buf idx_path = GIT_BUF_INIT;
795+
error = get_idx_path(&idx_path, backend, p);
796+
if (error < 0)
797+
goto cleanup;
798+
error = git_midx_writer_add(w, git_buf_cstr(&idx_path));
799+
git_buf_dispose(&idx_path);
800+
if (error < 0)
801+
goto cleanup;
802+
}
803+
804+
/*
805+
* Invalidate the previous midx before writing the new one.
806+
*/
807+
error = remove_multi_pack_index(backend);
808+
if (error < 0)
809+
goto cleanup;
810+
error = git_midx_writer_commit(w);
811+
if (error < 0)
812+
goto cleanup;
813+
error = refresh_multi_pack_index(backend);
814+
815+
cleanup:
816+
git_midx_writer_free(w);
817+
return error;
818+
}
819+
747820
static void pack_backend__free(git_odb_backend *_backend)
748821
{
749822
struct pack_backend *backend;
@@ -792,6 +865,7 @@ static int pack_backend__alloc(struct pack_backend **out, size_t initial_size)
792865
backend->parent.refresh = &pack_backend__refresh;
793866
backend->parent.foreach = &pack_backend__foreach;
794867
backend->parent.writepack = &pack_backend__writepack;
868+
backend->parent.writemidx = &pack_backend__writemidx;
795869
backend->parent.freshen = &pack_backend__freshen;
796870
backend->parent.free = &pack_backend__free;
797871

tests/pack/midx.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <git2.h>
44
#include <git2/sys/midx.h>
55

6+
#include "futils.h"
67
#include "midx.h"
78

89
void test_pack_midx__parse(void)
@@ -74,3 +75,36 @@ void test_pack_midx__writer(void)
7475
git_midx_writer_free(w);
7576
git_repository_free(repo);
7677
}
78+
79+
void test_pack_midx__odb_create(void)
80+
{
81+
git_repository *repo;
82+
git_odb *odb;
83+
git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
84+
git_buf midx = GIT_BUF_INIT, expected_midx = GIT_BUF_INIT, midx_path = GIT_BUF_INIT;
85+
struct stat st;
86+
87+
opts.bare = true;
88+
opts.local = GIT_CLONE_LOCAL;
89+
cl_git_pass(git_clone(&repo, cl_fixture("testrepo/.gitted"), "./clone.git", &opts));
90+
cl_git_pass(git_buf_joinpath(&midx_path, git_repository_path(repo), "objects/pack/multi-pack-index"));
91+
cl_git_fail(p_stat(git_buf_cstr(&midx_path), &st));
92+
93+
cl_git_pass(git_repository_odb(&odb, repo));
94+
cl_git_pass(git_odb_write_multi_pack_index(odb));
95+
git_odb_free(odb);
96+
97+
cl_git_pass(p_stat(git_buf_cstr(&midx_path), &st));
98+
99+
cl_git_pass(git_futils_readbuffer(&expected_midx, cl_fixture("testrepo.git/objects/pack/multi-pack-index")));
100+
cl_git_pass(git_futils_readbuffer(&midx, git_buf_cstr(&midx_path)));
101+
cl_assert_equal_i(git_buf_len(&midx), git_buf_len(&expected_midx));
102+
cl_assert_equal_strn(git_buf_cstr(&midx), git_buf_cstr(&expected_midx), git_buf_len(&midx));
103+
104+
git_repository_free(repo);
105+
git_buf_dispose(&midx);
106+
git_buf_dispose(&midx_path);
107+
git_buf_dispose(&expected_midx);
108+
109+
cl_git_pass(git_futils_rmdir_r("./clone.git", NULL, GIT_RMDIR_REMOVE_FILES));
110+
}

0 commit comments

Comments
 (0)