8000 Add support for delta reuse by kempniu · Pull Request #7040 · libgit2/libgit2 · GitHub
[go: up one dir, main page]

Skip to content

Add support for delta reuse #7040

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
8000
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions include/git2/sys/odb_backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ struct git_odb_backend {
int GIT_CALLBACK(read_header)(
size_t *, git_object_t *, git_odb_backend *, const git_oid *);

/**
* If the OID given is stored as a delta, get the OID of its delta base
* and compressed delta data.
*/
int GIT_CALLBACK(get_delta)(
git_oid *, void **, size_t *, size_t *, git_odb_backend *, const git_oid *);

/**
* Write an object into the backend. The id of the object has
* already been calculated and is passed in.
Expand Down
63 changes: 63 additions & 0 deletions src/libgit2/odb.c
Original file line number Diff line number Diff line change
Expand Up @@ -1310,6 +1310,69 @@ int git_odb__read_header_or_object(
return error;
}

static int odb_get_delta_1(
git_oid *base_p,
void **z_data_p,
size_t *size_p,
size_t *z_size_p,
git_odb *db,
const git_oid *id)
{
size_t i;
bool passthrough = false;
int error;

if ((error = git_mutex_lock(&db->lock)) < 0) {
git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
return error;
}

for (i = 0; i < db->backends.length; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;

if (!b->get_delta) {
passthrough = true;
continue;
}

error = b->get_delta(base_p, z_data_p, size_p, z_size_p, b, id);

git_mutex_unlock(&db->lock);

return error;
}

git_mutex_unlock(&db->lock);

return passthrough ? GIT_PASSTHROUGH : GIT_ENOTFOUND;
}

int git_odb__get_delta(
git_oid *base_p,
void **z_data_p,
size_t *size_p,
size_t *z_size_p,
git_odb *db,
const git_oid *id)
{
int error;

GIT_ASSERT_ARG(db);
GIT_ASSERT_ARG(id);
GIT_ASSERT_ARG(base_p);
GIT_ASSERT_ARG(z_data_p);
GIT_ASSERT_ARG(size_p);
GIT_ASSERT_ARG(z_size_p);

error = odb_get_delta_1(base_p, z_data_p, size_p, z_size_p, db, id);

if (error == GIT_PASSTHROUGH)
error = GIT_ENOTFOUND;

return error;
}

static int odb_read_1(
git_odb_object **out,
git_odb *db,
Expand Down
12 changes: 12 additions & 0 deletions src/libgit2/odb.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,18 @@ int git_odb__read_header_or_object(
git_odb_object **out, size_t *len_p, git_object_t *type_p,
git_odb *db, const git_oid *id);

/*
* If `id` is stored as a delta, get the OID of its delta base and compressed
* delta data.
*/
int git_odb__get_delta(
git_oid *base_p,
void **z_data_p,
size_t *size_p,
size_t *z_size_p,
git_odb *db,
const git_oid *id);

/*
* Attempt to get the ODB's commit-graph file. This object is still owned by
* the ODB. If the repository does not contain a commit-graph, it will return
Expand Down
25 changes: 25 additions & 0 deletions src/libgit2/odb_pack.c
10000
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,30 @@ static int pack_backend__read_header(
return git_packfile_resolve_header(len_p, type_p, e.p, e.offset);
}

static int pack_backend__get_delta(
git_oid *base_p,
void **z_data_p,
size_t *size_p,
size_t *z_size_p,
struct git_odb_backend *backend,
const git_oid *id)
{
struct git_pack_entry e;
int error;

GIT_ASSERT_ARG(backend);
GIT_ASSERT_ARG(id);
GIT_ASSERT_ARG(base_p);
GIT_ASSERT_ARG(z_data_p);
GIT_ASSERT_ARG(size_p);
GIT_ASSERT_ARG(z_size_p);

if ((error = pack_entry_find(&e, (struct pack_backend *)backend, id)) < 0)
return error;

return git_packfile_get_delta(base_p, z_data_p, size_p, z_size_p, e.p, e.offset);
}

static int pack_backend__freshen(
git_odb_backend *backend, const git_oid *oid)
{
Expand Down Expand Up @@ -907,6 +931,7 @@ static int pack_backend__alloc(
backend->parent.read = &pack_backend__read;
backend->parent.read_prefix = &pack_backend__read_prefix;
backend->parent.read_header = &pack_backend__read_header;
backend->parent.get_delta = &pack_backend__get_delta;
backend->parent.exists = &pack_backend__exists;
backend->parent.exists_prefix = &pack_backend__exists_prefix;
backend->parent.refresh = &pack_backend__refresh;
Expand Down
43 changes: 41 additions & 2 deletions src/libgit2/pack-objects.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ static int packbuilder_config(git_packbuilder *pb)

#undef config_get

ret = git_config_get_bool(&pb->no_reuse_delta, config, "pack.noReuseDelta");
if (ret == GIT_ENOTFOUND)
ret = 0;

out:
git_config_free(config);

Expand Down Expand Up @@ -769,8 +773,6 @@ static int try_delta(git_packbuilder *pb, struct unpacked *trg,

*ret = 0;

/* TODO: support reuse-delta */

/* Let's not bust the allowed depth. */
if (src->depth >= max_depth)
return 0;
Expand Down Expand Up @@ -1333,6 +1335,40 @@ static int ll_find_deltas(git_packbuilder *pb, git_pobject **list,
#define ll_find_deltas(pb, l, ls, w, d) find_deltas(pb, l, &ls, w, d)
#endif

static bool reuse_delta(git_pobject *po, git_packbuilder *pb)
{
git_oid base_id;
void *z_data;
size_t size;
size_t z_size;
git_pobject *base_po;

if (pb->no_reuse_delta ||
git_odb__get_delta(&base_id, &z_data, &size, &z_size, pb->odb, &po->id) < 0)
return false;

if (git_packbuilder_pobjectmap_get(&base_po, &pb->object_ix, &base_id) != 0) {
git__free(z_data);
return false;
}

po->delta = base_po;
po->delta_data = z_data;
po->delta_size = size;
po->z_delta_size = z_size;

/*
* The base of this (reused) delta may be considered for deltification
* itself. Connect delta_child/delta_sibling network links to ensure
* find_deltas() accounts for the depth of the reused deltas when
* limiting depth for the deltas it creates itself.
*/
po->delta_sibling = po->delta->delta_child;
po->delta->delta_child = po;

return true;
}

int git_packbuilder__prepare(git_packbuilder *pb)
{
git_pobject **delta_list;
Expand All @@ -1357,6 +1393,9 @@ int git_packbuilder__prepare(git_packbuilder *pb)
for (i = 0; i < pb->nr_objects; ++i) {
git_pobject *po = pb->object_list + i;

if (reuse_delta(po, pb))
continue;

/* Make sure the item is within our size limits */
if (po->size < 50 || po->size > pb->big_file_threshold)
continue;
Expand Down
1 change: 1 addition & 0 deletions src/libgit2/pack-objects.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ struct git_packbuilder {
size_t cache_max_small_delta_size;
size_t big_file_threshold;
size_t window_memory_limit;
int no_reuse_delta;

unsigned int nr_threads; /* nr of threads to use */

Expand Down
Loading
0