2013-10-24 20:01:06 +02:00
|
|
|
#include "cache.h"
|
|
|
|
#include "object.h"
|
|
|
|
#include "pack.h"
|
|
|
|
#include "pack-objects.h"
|
2018-04-14 17:35:05 +02:00
|
|
|
#include "packfile.h"
|
|
|
|
#include "config.h"
|
2013-10-24 20:01:06 +02:00
|
|
|
|
|
|
|
static uint32_t locate_object_entry_hash(struct packing_data *pdata,
|
2019-06-20 09:41:07 +02:00
|
|
|
const struct object_id *oid,
|
2013-10-24 20:01:06 +02:00
|
|
|
int *found)
|
|
|
|
{
|
2014-07-03 00:20:20 +02:00
|
|
|
uint32_t i, mask = (pdata->index_size - 1);
|
2013-10-24 20:01:06 +02:00
|
|
|
|
2019-06-20 09:41:49 +02:00
|
|
|
i = oidhash(oid) & mask;
|
2013-10-24 20:01:06 +02:00
|
|
|
|
|
|
|
while (pdata->index[i] > 0) {
|
|
|
|
uint32_t pos = pdata->index[i] - 1;
|
|
|
|
|
2019-06-20 09:41:07 +02:00
|
|
|
if (oideq(oid, &pdata->objects[pos].idx.oid)) {
|
2013-10-24 20:01:06 +02:00
|
|
|
*found = 1;
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
i = (i + 1) & mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
*found = 0;
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline uint32_t closest_pow2(uint32_t v)
|
|
|
|
{
|
|
|
|
v = v - 1;
|
|
|
|
v |= v >> 1;
|
|
|
|
v |= v >> 2;
|
|
|
|
v |= v >> 4;
|
|
|
|
v |= v >> 8;
|
|
|
|
v |= v >> 16;
|
|
|
|
return v + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rehash_objects(struct packing_data *pdata)
|
|
|
|
{
|
|
|
|
uint32_t i;
|
|
|
|
struct object_entry *entry;
|
|
|
|
|
|
|
|
pdata->index_size = closest_pow2(pdata->nr_objects * 3);
|
|
|
|
if (pdata->index_size < 1024)
|
|
|
|
pdata->index_size = 1024;
|
|
|
|
|
2014-06-01 13:07:21 +02:00
|
|
|
free(pdata->index);
|
|
|
|
pdata->index = xcalloc(pdata->index_size, sizeof(*pdata->index));
|
2013-10-24 20:01:06 +02:00
|
|
|
|
|
|
|
entry = pdata->objects;
|
|
|
|
|
|
|
|
for (i = 0; i < pdata->nr_objects; i++) {
|
|
|
|
int found;
|
2017-05-07 00:10:11 +02:00
|
|
|
uint32_t ix = locate_object_entry_hash(pdata,
|
2019-06-20 09:41:07 +02:00
|
|
|
&entry->idx.oid,
|
2017-05-07 00:10:11 +02:00
|
|
|
&found);
|
2013-10-24 20:01:06 +02:00
|
|
|
|
|
|
|
if (found)
|
2018-05-02 11:38:39 +02:00
|
|
|
BUG("Duplicate object in hash");
|
2013-10-24 20:01:06 +02:00
|
|
|
|
|
|
|
pdata->index[ix] = i + 1;
|
|
|
|
entry++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct object_entry *packlist_find(struct packing_data *pdata,
|
2019-06-20 09:41:03 +02:00
|
|
|
const struct object_id *oid,
|
2013-10-24 20:01:06 +02:00
|
|
|
uint32_t *index_pos)
|
|
|
|
{
|
|
|
|
uint32_t i;
|
|
|
|
int found;
|
|
|
|
|
|
|
|
if (!pdata->index_size)
|
|
|
|
return NULL;
|
|
|
|
|
2019-06-20 09:41:07 +02:00
|
|
|
i = locate_object_entry_hash(pdata, oid, &found);
|
2013-10-24 20:01:06 +02:00
|
|
|
|
|
|
|
if (index_pos)
|
|
|
|
*index_pos = i;
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return &pdata->objects[pdata->index[i] - 1];
|
|
|
|
}
|
|
|
|
|
2018-04-14 17:35:05 +02:00
|
|
|
static void prepare_in_pack_by_idx(struct packing_data *pdata)
|
|
|
|
{
|
|
|
|
struct packed_git **mapping, *p;
|
|
|
|
int cnt = 0, nr = 1U << OE_IN_PACK_BITS;
|
|
|
|
|
|
|
|
ALLOC_ARRAY(mapping, nr);
|
|
|
|
/*
|
|
|
|
* oe_in_pack() on an all-zero'd object_entry
|
|
|
|
* (i.e. in_pack_idx also zero) should return NULL.
|
|
|
|
*/
|
|
|
|
mapping[cnt++] = NULL;
|
2018-11-10 06:49:08 +01:00
|
|
|
for (p = get_all_packs(pdata->repo); p; p = p->next, cnt++) {
|
2018-04-14 17:35:05 +02:00
|
|
|
if (cnt == nr) {
|
|
|
|
free(mapping);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
p->index = cnt;
|
|
|
|
mapping[cnt] = p;
|
|
|
|
}
|
|
|
|
pdata->in_pack_by_idx = mapping;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A new pack appears after prepare_in_pack_by_idx() has been
|
|
|
|
* run. This is likely a race.
|
|
|
|
*
|
|
|
|
* We could map this new pack to in_pack_by_idx[] array, but then we
|
|
|
|
* have to deal with full array anyway. And since it's hard to test
|
|
|
|
* this fall back code, just stay simple and fall back to using
|
|
|
|
* in_pack[] array.
|
|
|
|
*/
|
2019-02-14 06:50:32 +01:00
|
|
|
void oe_map_new_pack(struct packing_data *pack)
|
2018-04-14 17:35:05 +02:00
|
|
|
{
|
|
|
|
uint32_t i;
|
|
|
|
|
|
|
|
REALLOC_ARRAY(pack->in_pack, pack->nr_alloc);
|
|
|
|
|
|
|
|
for (i = 0; i < pack->nr_objects; i++)
|
|
|
|
pack->in_pack[i] = oe_in_pack(pack, pack->objects + i);
|
|
|
|
|
|
|
|
FREE_AND_NULL(pack->in_pack_by_idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* assume pdata is already zero'd by caller */
|
2018-11-10 06:49:08 +01:00
|
|
|
void prepare_packing_data(struct repository *r, struct packing_data *pdata)
|
2018-04-14 17:35:05 +02:00
|
|
|
{
|
2018-11-10 06:49:08 +01:00
|
|
|
pdata->repo = r;
|
|
|
|
|
2018-04-14 17:35:05 +02:00
|
|
|
if (git_env_bool("GIT_TEST_FULL_IN_PACK_ARRAY", 0)) {
|
|
|
|
/*
|
|
|
|
* do not initialize in_pack_by_idx[] to force the
|
|
|
|
* slow path in oe_in_pack()
|
|
|
|
*/
|
|
|
|
} else {
|
|
|
|
prepare_in_pack_by_idx(pdata);
|
|
|
|
}
|
2018-04-14 17:35:10 +02:00
|
|
|
|
|
|
|
pdata->oe_size_limit = git_env_ulong("GIT_TEST_OE_SIZE",
|
|
|
|
1U << OE_SIZE_BITS);
|
pack-objects: fix performance issues on packing large deltas
Let's start with some background about oe_delta_size() and
oe_set_delta_size(). If you already know, skip the next paragraph.
These two are added in 0aca34e826 (pack-objects: shrink delta_size
field in struct object_entry - 2018-04-14) to help reduce 'struct
object_entry' size. The delta size field in this struct is reduced to
only contain max 1MB. So if any new delta is produced and larger than
1MB, it's dropped because we can't really save such a large size
anywhere. Fallback is provided in case existing packfiles already have
large deltas, then we can retrieve it from the pack.
While this should help small machines repacking large repos without
large deltas (i.e. less memory pressure), dropping large deltas during
the delta selection process could end up with worse pack files. And if
existing packfiles already have >1MB delta and pack-objects is
instructed to not reuse deltas, all of them will be dropped on the
floor, and the resulting pack would be definitely bigger.
There is also a regression in terms of CPU/IO if we have large on-disk
deltas because fallback code needs to parse the pack every time the
delta size is needed and just access to the mmap'd pack data is enough
for extra page faults when memory is under pressure.
Both of these issues were reported on the mailing list. Here's some
numbers for comparison.
Version Pack (MB) MaxRSS(kB) Time (s)
------- --------- ---------- --------
2.17.0 5498 43513628 2494.85
2.18.0 10531 40449596 4168.94
This patch provides a better fallback that is
- cheaper in terms of cpu and io because we won't have to read
existing pack files as much
- better in terms of pack size because the pack heuristics is back to
2.17.0 time, we do not drop large deltas at all
If we encounter any delta (on-disk or created during try_delta phase)
that is larger than the 1MB limit, we stop using delta_size_ field for
this because it can't contain such size anyway. A new array of delta
size is dynamically allocated and can hold all the deltas that 2.17.0
can. This array only contains delta sizes that delta_size_ can't
contain.
With this, we do not have to drop deltas in try_delta() anymore. Of
course the downside is we use slightly more memory, even compared to
2.17.0. But since this is considered an uncommon case, a bit more
memory consumption should not be a problem.
Delta size limit is also raised from 1MB to 16MB to better cover
common case and avoid that extra memory consumption (99.999% deltas in
this reported repo are under 12MB; Jeff noted binary artifacts topped
out at about 3MB in some other private repos). Other fields are
shuffled around to keep this struct packed tight. We don't use more
memory in common case even with this limit update.
A note about thread synchronization. Since this code can be run in
parallel during delta searching phase, we need a mutex. The realloc
part in packlist_alloc() is not protected because it only happens
during the object counting phase, which is always single-threaded.
Access to e->delta_size_ (and by extension
pack->delta_size[e - pack->objects]) is unprotected as before, the
thread scheduler in pack-objects must make sure "e" is never updated
by two different threads.
The area under the new lock is as small as possible, avoiding locking
at all in common case, since lock contention with high thread count
could be expensive (most blobs are small enough that delta compute
time is short and we end up taking the lock very often). The previous
attempt to always hold a lock in oe_delta_size() and
oe_set_delta_size() increases execution time by 33% when repacking
linux.git with with 40 threads.
Reported-by: Elijah Newren <newren@gmail.com>
Helped-by: Elijah Newren <newren@gmail.com>
Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-07-22 10:04:21 +02:00
|
|
|
pdata->oe_delta_size_limit = git_env_ulong("GIT_TEST_OE_DELTA_SIZE",
|
|
|
|
1UL << OE_DELTA_SIZE_BITS);
|
2019-01-25 01:22:05 +01:00
|
|
|
init_recursive_mutex(&pdata->odb_lock);
|
2018-04-14 17:35:05 +02:00
|
|
|
}
|
|
|
|
|
2013-10-24 20:01:06 +02:00
|
|
|
struct object_entry *packlist_alloc(struct packing_data *pdata,
|
|
|
|
const unsigned char *sha1,
|
|
|
|
uint32_t index_pos)
|
|
|
|
{
|
|
|
|
struct object_entry *new_entry;
|
|
|
|
|
|
|
|
if (pdata->nr_objects >= pdata->nr_alloc) {
|
|
|
|
pdata->nr_alloc = (pdata->nr_alloc + 1024) * 3 / 2;
|
2014-09-16 20:56:57 +02:00
|
|
|
REALLOC_ARRAY(pdata->objects, pdata->nr_alloc);
|
2018-04-14 17:35:05 +02:00
|
|
|
|
|
|
|
if (!pdata->in_pack_by_idx)
|
|
|
|
REALLOC_ARRAY(pdata->in_pack, pdata->nr_alloc);
|
pack-objects: fix performance issues on packing large deltas
Let's start with some background about oe_delta_size() and
oe_set_delta_size(). If you already know, skip the next paragraph.
These two are added in 0aca34e826 (pack-objects: shrink delta_size
field in struct object_entry - 2018-04-14) to help reduce 'struct
object_entry' size. The delta size field in this struct is reduced to
only contain max 1MB. So if any new delta is produced and larger than
1MB, it's dropped because we can't really save such a large size
anywhere. Fallback is provided in case existing packfiles already have
large deltas, then we can retrieve it from the pack.
While this should help small machines repacking large repos without
large deltas (i.e. less memory pressure), dropping large deltas during
the delta selection process could end up with worse pack files. And if
existing packfiles already have >1MB delta and pack-objects is
instructed to not reuse deltas, all of them will be dropped on the
floor, and the resulting pack would be definitely bigger.
There is also a regression in terms of CPU/IO if we have large on-disk
deltas because fallback code needs to parse the pack every time the
delta size is needed and just access to the mmap'd pack data is enough
for extra page faults when memory is under pressure.
Both of these issues were reported on the mailing list. Here's some
numbers for comparison.
Version Pack (MB) MaxRSS(kB) Time (s)
------- --------- ---------- --------
2.17.0 5498 43513628 2494.85
2.18.0 10531 40449596 4168.94
This patch provides a better fallback that is
- cheaper in terms of cpu and io because we won't have to read
existing pack files as much
- better in terms of pack size because the pack heuristics is back to
2.17.0 time, we do not drop large deltas at all
If we encounter any delta (on-disk or created during try_delta phase)
that is larger than the 1MB limit, we stop using delta_size_ field for
this because it can't contain such size anyway. A new array of delta
size is dynamically allocated and can hold all the deltas that 2.17.0
can. This array only contains delta sizes that delta_size_ can't
contain.
With this, we do not have to drop deltas in try_delta() anymore. Of
course the downside is we use slightly more memory, even compared to
2.17.0. But since this is considered an uncommon case, a bit more
memory consumption should not be a problem.
Delta size limit is also raised from 1MB to 16MB to better cover
common case and avoid that extra memory consumption (99.999% deltas in
this reported repo are under 12MB; Jeff noted binary artifacts topped
out at about 3MB in some other private repos). Other fields are
shuffled around to keep this struct packed tight. We don't use more
memory in common case even with this limit update.
A note about thread synchronization. Since this code can be run in
parallel during delta searching phase, we need a mutex. The realloc
part in packlist_alloc() is not protected because it only happens
during the object counting phase, which is always single-threaded.
Access to e->delta_size_ (and by extension
pack->delta_size[e - pack->objects]) is unprotected as before, the
thread scheduler in pack-objects must make sure "e" is never updated
by two different threads.
The area under the new lock is as small as possible, avoiding locking
at all in common case, since lock contention with high thread count
could be expensive (most blobs are small enough that delta compute
time is short and we end up taking the lock very often). The previous
attempt to always hold a lock in oe_delta_size() and
oe_set_delta_size() increases execution time by 33% when repacking
linux.git with with 40 threads.
Reported-by: Elijah Newren <newren@gmail.com>
Helped-by: Elijah Newren <newren@gmail.com>
Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-07-22 10:04:21 +02:00
|
|
|
if (pdata->delta_size)
|
|
|
|
REALLOC_ARRAY(pdata->delta_size, pdata->nr_alloc);
|
2018-08-16 08:13:12 +02:00
|
|
|
|
|
|
|
if (pdata->tree_depth)
|
|
|
|
REALLOC_ARRAY(pdata->tree_depth, pdata->nr_alloc);
|
2018-08-16 08:13:13 +02:00
|
|
|
|
|
|
|
if (pdata->layer)
|
|
|
|
REALLOC_ARRAY(pdata->layer, pdata->nr_alloc);
|
2013-10-24 20:01:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
new_entry = pdata->objects + pdata->nr_objects++;
|
|
|
|
|
|
|
|
memset(new_entry, 0, sizeof(*new_entry));
|
2017-05-07 00:10:11 +02:00
|
|
|
hashcpy(new_entry->idx.oid.hash, sha1);
|
2013-10-24 20:01:06 +02:00
|
|
|
|
|
|
|
if (pdata->index_size * 3 <= pdata->nr_objects * 4)
|
|
|
|
rehash_objects(pdata);
|
|
|
|
else
|
|
|
|
pdata->index[index_pos] = pdata->nr_objects;
|
|
|
|
|
2018-04-14 17:35:05 +02:00
|
|
|
if (pdata->in_pack)
|
|
|
|
pdata->in_pack[pdata->nr_objects - 1] = NULL;
|
|
|
|
|
2018-08-16 08:13:12 +02:00
|
|
|
if (pdata->tree_depth)
|
|
|
|
pdata->tree_depth[pdata->nr_objects - 1] = 0;
|
|
|
|
|
2018-08-16 08:13:13 +02:00
|
|
|
if (pdata->layer)
|
|
|
|
pdata->layer[pdata->nr_objects - 1] = 0;
|
|
|
|
|
2013-10-24 20:01:06 +02:00
|
|
|
return new_entry;
|
|
|
|
}
|
pack-objects: reuse on-disk deltas for thin "have" objects
When we serve a fetch, we pass the "wants" and "haves" from
the fetch negotiation to pack-objects. That tells us not
only which objects we need to send, but we also use the
boundary commits as "preferred bases": their trees and blobs
are candidates for delta bases, both for reusing on-disk
deltas and for finding new ones.
However, this misses some opportunities. Modulo some special
cases like shallow or partial clones, we know that every
object reachable from the "haves" could be a preferred base.
We don't use all of them for two reasons:
1. It's expensive to traverse the whole history and
enumerate all of the objects the other side has.
2. The delta search is expensive, so we want to keep the
number of candidate bases sane. The boundary commits
are the most likely to work.
When we have reachability bitmaps, though, reason 1 no
longer applies. We can efficiently compute the set of
reachable objects on the other side (and in fact already did
so as part of the bitmap set-difference to get the list of
interesting objects). And using this set conveniently
covers the shallow and partial cases, since we have to
disable the use of bitmaps for those anyway.
The second reason argues against using these bases in the
search for new deltas. But there's one case where we can use
this information for free: when we have an existing on-disk
delta that we're considering reusing, we can do so if we
know the other side has the base object. This in fact saves
time during the delta search, because it's one less delta we
have to compute.
And that's exactly what this patch does: when we're
considering whether to reuse an on-disk delta, if bitmaps
tell us the other side has the object (and we're making a
thin-pack), then we reuse it.
Here are the results on p5311 using linux.git, which
simulates a client fetching after `N` days since their last
fetch:
Test origin HEAD
--------------------------------------------------------------------------
5311.3: server (1 days) 0.27(0.27+0.04) 0.12(0.09+0.03) -55.6%
5311.4: size (1 days) 0.9M 237.0K -73.7%
5311.5: client (1 days) 0.04(0.05+0.00) 0.10(0.10+0.00) +150.0%
5311.7: server (2 days) 0.34(0.42+0.04) 0.13(0.10+0.03) -61.8%
5311.8: size (2 days) 1.5M 347.7K -76.5%
5311.9: client (2 days) 0.07(0.08+0.00) 0.16(0.15+0.01) +128.6%
5311.11: server (4 days) 0.56(0.77+0.08) 0.13(0.10+0.02) -76.8%
5311.12: size (4 days) 2.8M 566.6K -79.8%
5311.13: client (4 days) 0.13(0.15+0.00) 0.34(0.31+0.02) +161.5%
5311.15: server (8 days) 0.97(1.39+0.11) 0.30(0.25+0.05) -69.1%
5311.16: size (8 days) 4.3M 1.0M -76.0%
5311.17: client (8 days) 0.20(0.22+0.01) 0.53(0.52+0.01) +165.0%
5311.19: server (16 days) 1.52(2.51+0.12) 0.30(0.26+0.03) -80.3%
5311.20: size (16 days) 8.0M 2.0M -74.5%
5311.21: client (16 days) 0.40(0.47+0.03) 1.01(0.98+0.04) +152.5%
5311.23: server (32 days) 2.40(4.44+0.20) 0.31(0.26+0.04) -87.1%
5311.24: size (32 days) 14.1M 4.1M -70.9%
5311.25: client (32 days) 0.70(0.90+0.03) 1.81(1.75+0.06) +158.6%
5311.27: server (64 days) 11.76(26.57+0.29) 0.55(0.50+0.08) -95.3%
5311.28: size (64 days) 89.4M 47.4M -47.0%
5311.29: client (64 days) 5.71(9.31+0.27) 15.20(15.20+0.32) +166.2%
5311.31: server (128 days) 16.15(36.87+0.40) 0.91(0.82+0.14) -94.4%
5311.32: size (128 days) 134.8M 100.4M -25.5%
5311.33: client (128 days) 9.42(16.86+0.49) 25.34(25.80+0.46) +169.0%
In all cases we save CPU time on the server (sometimes
significant) and the resulting pack is smaller. We do spend
more CPU time on the client side, because it has to
reconstruct more deltas. But that's the right tradeoff to
make, since clients tend to outnumber servers. It just means
the thin pack mechanism is doing its job.
From the user's perspective, the end-to-end time of the
operation will generally be faster. E.g., in the 128-day
case, we saved 15s on the server at a cost of 16s on the
client. Since the resulting pack is 34MB smaller, this is a
net win if the network speed is less than 270Mbit/s. And
that's actually the worst case. The 64-day case saves just
over 11s at a cost of just under 11s. So it's a slight win
at any network speed, and the 40MB saved is pure bonus. That
trend continues for the smaller fetches.
The implementation itself is mostly straightforward, with
the new logic going into check_object(). But there are two
tricky bits.
The first is that check_object() needs access to the
relevant information (the thin flag and bitmap result). We
can do this by pushing these into program-lifetime globals.
The second is that the rest of the code assumes that any
reused delta will point to another "struct object_entry" as
its base. But of course the case we are interested in here
is the one where don't have such an entry!
I looked at a number of options that didn't quite work:
- we could use a flag to signal a reused delta, but it's
not a single bit. We have to actually store the oid of
the base, which is normally done by pointing to the
existing object_entry. And we'd have to modify all the
code which looks at deltas.
- we could add the reused bases to the end of the existing
object_entry array. While this does create some extra
work as later stages consider the extra entries, it's
actually not too bad (we're not sending them, so they
don't cost much in the delta search, and at most we'd
have 2*N of them).
But there's a more subtle problem. Adding to the existing
array means we might need to grow it with realloc, which
could move the earlier entries around. While many of the
references to other entries are done by integer index,
some (including ones on the stack) use pointers, which
would become invalidated.
This isn't insurmountable, but it would require quite a
bit of refactoring (and it's hard to know that you've got
it all, since it may work _most_ of the time and then
fail subtly based on memory allocation patterns).
- we could allocate a new one-off entry for the base. In
fact, this is what an earlier version of this patch did.
However, since the refactoring brought in by ad635e82d6
(Merge branch 'nd/pack-objects-pack-struct', 2018-05-23),
the delta_idx code requires that both entries be in the
main packing list.
So taking all of those options into account, what I ended up
with is a separate list of "external bases" that are not
part of the main packing list. Each delta entry that points
to an external base has a single-bit flag to do so; we have a
little breathing room in the bitfield section of
object_entry.
This lets us limit the change primarily to the oe_delta()
and oe_set_delta_ext() functions. And as a bonus, most of
the rest of the code does not consider these dummy entries
at all, saving both runtime CPU and code complexity.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-21 21:07:05 +02:00
|
|
|
|
|
|
|
void oe_set_delta_ext(struct packing_data *pdata,
|
|
|
|
struct object_entry *delta,
|
|
|
|
const unsigned char *sha1)
|
|
|
|
{
|
|
|
|
struct object_entry *base;
|
|
|
|
|
|
|
|
ALLOC_GROW(pdata->ext_bases, pdata->nr_ext + 1, pdata->alloc_ext);
|
|
|
|
base = &pdata->ext_bases[pdata->nr_ext++];
|
|
|
|
memset(base, 0, sizeof(*base));
|
|
|
|
hashcpy(base->idx.oid.hash, sha1);
|
|
|
|
|
|
|
|
/* These flags mark that we are not part of the actual pack output. */
|
|
|
|
base->preferred_base = 1;
|
|
|
|
base->filled = 1;
|
|
|
|
|
|
|
|
delta->ext_base = 1;
|
|
|
|
delta->delta_idx = base - pdata->ext_bases + 1;
|
|
|
|
}
|