aboutsummaryrefslogtreecommitdiffstats
path: root/builtin/pack-objects.c
diff options
context:
space:
mode:
Diffstat (limited to 'builtin/pack-objects.c')
-rw-r--r--builtin/pack-objects.c246
1 files changed, 165 insertions, 81 deletions
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index c6e2852d3c..58a9b16126 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -1,8 +1,10 @@
+#define USE_THE_REPOSITORY_VARIABLE
+#define DISABLE_SIGN_COMPARE_WARNINGS
+
#include "builtin.h"
#include "environment.h"
#include "gettext.h"
#include "hex.h"
-#include "repository.h"
#include "config.h"
#include "attr.h"
#include "object.h"
@@ -239,6 +241,7 @@ static enum {
static uint16_t write_bitmap_options = BITMAP_OPT_HASH_CACHE;
static int exclude_promisor_objects;
+static int exclude_promisor_objects_best_effort;
static int use_delta_islands;
@@ -266,6 +269,43 @@ struct configured_exclusion {
static struct oidmap configured_exclusions;
static struct oidset excluded_by_config;
+static int name_hash_version = -1;
+
+/**
+ * Check whether the name_hash_version chosen by user input is appropriate,
+ * and also validate whether it is compatible with other features.
+ */
+static void validate_name_hash_version(void)
+{
+ if (name_hash_version < 1 || name_hash_version > 2)
+ die(_("invalid --name-hash-version option: %d"), name_hash_version);
+ if (write_bitmap_index && name_hash_version != 1) {
+ warning(_("currently, --write-bitmap-index requires --name-hash-version=1"));
+ name_hash_version = 1;
+ }
+}
+
+static inline uint32_t pack_name_hash_fn(const char *name)
+{
+ static int seen_version = -1;
+
+ if (seen_version < 0)
+ seen_version = name_hash_version;
+ else if (seen_version != name_hash_version)
+ BUG("name hash version changed from %d to %d mid-process",
+ seen_version, name_hash_version);
+
+ switch (name_hash_version) {
+ case 1:
+ return pack_name_hash(name);
+
+ case 2:
+ return pack_name_hash_v2((const unsigned char *)name);
+
+ default:
+ BUG("invalid name-hash version: %d", name_hash_version);
+ }
+}
/*
* stats
@@ -1100,78 +1140,64 @@ static void write_reused_pack_one(struct packed_git *reuse_packfile,
static size_t write_reused_pack_verbatim(struct bitmapped_pack *reuse_packfile,
struct hashfile *out,
- off_t pack_start,
struct pack_window **w_curs)
{
- size_t pos = reuse_packfile->bitmap_pos;
+ size_t pos = 0;
size_t end;
- if (pos % BITS_IN_EWORD) {
- size_t word_pos = (pos / BITS_IN_EWORD);
- size_t offset = pos % BITS_IN_EWORD;
- size_t last;
- eword_t word = reuse_packfile_bitmap->words[word_pos];
-
- if (offset + reuse_packfile->bitmap_nr < BITS_IN_EWORD)
- last = offset + reuse_packfile->bitmap_nr;
- else
- last = BITS_IN_EWORD;
-
- for (; offset < last; offset++) {
- if (word >> offset == 0)
- return word_pos;
- if (!bitmap_get(reuse_packfile_bitmap,
- word_pos * BITS_IN_EWORD + offset))
- return word_pos;
- }
-
- pos += BITS_IN_EWORD - (pos % BITS_IN_EWORD);
+ if (reuse_packfile->bitmap_pos) {
+ /*
+ * We can't reuse whole chunks verbatim out of
+ * non-preferred packs since we can't guarantee that
+ * all duplicate objects were resolved in favor of
+ * that pack.
+ *
+ * Even if we have a whole eword_t worth of bits that
+ * could be reused, there may be objects between the
+ * objects corresponding to the first and last bit of
+ * that word which were selected from a different
+ * pack, causing us to send duplicate or unwanted
+ * objects.
+ *
+ * Handle non-preferred packs from within
+ * write_reused_pack(), which inspects and reuses
+ * individual bits.
+ */
+ return reuse_packfile->bitmap_pos / BITS_IN_EWORD;
}
/*
- * Now we're going to copy as many whole eword_t's as possible.
- * "end" is the index of the last whole eword_t we copy, but
- * there may be additional bits to process. Those are handled
- * individually by write_reused_pack().
+ * Only read through the last word whose bits all correspond
+ * to objects in the given packfile, since we must stop at a
+ * word boundary.
*
- * Begin by advancing to the first word boundary in range of the
- * bit positions occupied by objects in "reuse_packfile". Then
- * pick the last word boundary in the same range. If we have at
- * least one word's worth of bits to process, continue on.
+ * If there is no whole word to read (i.e. the packfile
+ * contains fewer than BITS_IN_EWORD objects), then we'll
+ * inspect bits one-by-one in write_reused_pack().
*/
- end = reuse_packfile->bitmap_pos + reuse_packfile->bitmap_nr;
- if (end % BITS_IN_EWORD)
- end -= end % BITS_IN_EWORD;
- if (pos >= end)
- return reuse_packfile->bitmap_pos / BITS_IN_EWORD;
+ end = reuse_packfile->bitmap_nr / BITS_IN_EWORD;
+ if (reuse_packfile_bitmap->word_alloc < end)
+ BUG("fewer words than expected in reuse_packfile_bitmap");
- while (pos < end &&
- reuse_packfile_bitmap->words[pos / BITS_IN_EWORD] == (eword_t)~0)
- pos += BITS_IN_EWORD;
+ while (pos < end && reuse_packfile_bitmap->words[pos] == (eword_t)~0)
+ pos++;
- if (pos > end)
- pos = end;
+ if (pos) {
+ off_t to_write;
- if (reuse_packfile->bitmap_pos < pos) {
- off_t pack_start_off = pack_pos_to_offset(reuse_packfile->p, 0);
- off_t pack_end_off = pack_pos_to_offset(reuse_packfile->p,
- pos - reuse_packfile->bitmap_pos);
-
- written += pos - reuse_packfile->bitmap_pos;
+ written = (pos * BITS_IN_EWORD);
+ to_write = pack_pos_to_offset(reuse_packfile->p, written)
+ - sizeof(struct pack_header);
/* We're recording one chunk, not one object. */
- record_reused_object(pack_start_off,
- pack_start_off - (hashfile_total(out) - pack_start));
+ record_reused_object(sizeof(struct pack_header), 0);
hashflush(out);
copy_pack_data(out, reuse_packfile->p, w_curs,
- pack_start_off, pack_end_off - pack_start_off);
+ sizeof(struct pack_header), to_write);
display_progress(progress_state, written);
}
- if (pos % BITS_IN_EWORD)
- BUG("attempted to jump past a word boundary to %"PRIuMAX,
- (uintmax_t)pos);
- return pos / BITS_IN_EWORD;
+ return pos;
}
static void write_reused_pack(struct bitmapped_pack *reuse_packfile,
@@ -1183,8 +1209,7 @@ static void write_reused_pack(struct bitmapped_pack *reuse_packfile,
struct pack_window *w_curs = NULL;
if (allow_ofs_delta)
- i = write_reused_pack_verbatim(reuse_packfile, f, pack_start,
- &w_curs);
+ i = write_reused_pack_verbatim(reuse_packfile, f, &w_curs);
for (; i < reuse_packfile_bitmap->word_alloc; ++i) {
eword_t word = reuse_packfile_bitmap->words[i];
@@ -1276,7 +1301,8 @@ static void write_pack_file(void)
struct object_entry **write_order;
if (progress > pack_to_stdout)
- progress_state = start_progress(_("Writing objects"), nr_result);
+ progress_state = start_progress(the_repository,
+ _("Writing objects"), nr_result);
ALLOC_ARRAY(written_list, to_pack.nr_objects);
write_order = compute_write_order();
@@ -1330,8 +1356,9 @@ static void write_pack_file(void)
*/
int fd = finalize_hashfile(f, hash, FSYNC_COMPONENT_PACK, 0);
- fixup_pack_header_footer(fd, hash, pack_tmp_name,
- nr_written, hash, offset);
+ fixup_pack_header_footer(the_hash_algo, fd, hash,
+ pack_tmp_name, nr_written,
+ hash, offset);
close(fd);
if (write_bitmap_index) {
if (write_bitmap_index != WRITE_BITMAP_QUIET)
@@ -1379,9 +1406,10 @@ static void write_pack_file(void)
if (cruft)
pack_idx_opts.flags |= WRITE_MTIMES;
- stage_tmp_packfiles(&tmpname, pack_tmp_name,
- written_list, nr_written,
- &to_pack, &pack_idx_opts, hash,
+ stage_tmp_packfiles(the_hash_algo, &tmpname,
+ pack_tmp_name, written_list,
+ nr_written, &to_pack,
+ &pack_idx_opts, hash,
&idx_tmp_name);
if (write_bitmap_index) {
@@ -1529,7 +1557,7 @@ static int want_found_object(const struct object_id *oid, int exclude,
return 0;
if (ignore_packed_keep_in_core && p->pack_keep_in_core)
return 0;
- if (has_object_kept_pack(oid, flags))
+ if (has_object_kept_pack(p->repo, oid, flags))
return 0;
}
@@ -1556,7 +1584,7 @@ static int want_object_in_pack_one(struct packed_git *p,
if (p == *found_pack)
offset = *found_offset;
else
- offset = find_pack_entry_one(oid->hash, p);
+ offset = find_pack_entry_one(oid, p);
if (offset) {
if (!*found_pack) {
@@ -1698,7 +1726,7 @@ static int add_object_entry(const struct object_id *oid, enum object_type type,
return 0;
}
- create_object_entry(oid, type, pack_name_hash(name),
+ create_object_entry(oid, type, pack_name_hash_fn(name),
exclude, name && no_try_delta(name),
found_pack, found_offset);
return 1;
@@ -1912,7 +1940,7 @@ static void add_preferred_base_object(const char *name)
{
struct pbase_tree *it;
size_t cmplen;
- unsigned hash = pack_name_hash(name);
+ unsigned hash = pack_name_hash_fn(name);
if (!num_preferred_base || check_pbase_path(hash))
return;
@@ -2412,7 +2440,8 @@ static void get_object_details(void)
struct object_entry **sorted_by_offset;
if (progress)
- progress_state = start_progress(_("Counting objects"),
+ progress_state = start_progress(the_repository,
+ _("Counting objects"),
to_pack.nr_objects);
CALLOC_ARRAY(sorted_by_offset, to_pack.nr_objects);
@@ -3232,7 +3261,8 @@ static void prepare_pack(int window, int depth)
unsigned nr_done = 0;
if (progress)
- progress_state = start_progress(_("Compressing objects"),
+ progress_state = start_progress(the_repository,
+ _("Compressing objects"),
nr_deltas);
QSORT(delta_list, n, type_size_sort);
ll_find_deltas(delta_list, n, window+1, depth, &nr_done);
@@ -3422,7 +3452,7 @@ static void show_object_pack_hint(struct object *object, const char *name,
* here using a now in order to perhaps improve the delta selection
* process.
*/
- oe->hash = pack_name_hash(name);
+ oe->hash = pack_name_hash_fn(name);
oe->no_try_delta = name && no_try_delta(name);
stdin_packs_hints_nr++;
@@ -3572,7 +3602,7 @@ static void add_cruft_object_entry(const struct object_id *oid, enum object_type
entry = packlist_find(&to_pack, oid);
if (entry) {
if (name) {
- entry->hash = pack_name_hash(name);
+ entry->hash = pack_name_hash_fn(name);
entry->no_try_delta = no_try_delta(name);
}
} else {
@@ -3595,7 +3625,7 @@ static void add_cruft_object_entry(const struct object_id *oid, enum object_type
return;
}
- entry = create_object_entry(oid, type, pack_name_hash(name),
+ entry = create_object_entry(oid, type, pack_name_hash_fn(name),
0, name && no_try_delta(name),
pack, offset);
}
@@ -3627,7 +3657,7 @@ static void show_cruft_commit(struct commit *commit, void *data)
static int cruft_include_check_obj(struct object *obj, void *data UNUSED)
{
- return !has_object_kept_pack(&obj->oid, IN_CORE_KEEP_PACKS);
+ return !has_object_kept_pack(to_pack.repo, &obj->oid, IN_CORE_KEEP_PACKS);
}
static int cruft_include_check(struct commit *commit, void *data)
@@ -3660,7 +3690,8 @@ static void add_objects_in_unpacked_packs(void);
static void enumerate_cruft_objects(void)
{
if (progress)
- progress_state = start_progress(_("Enumerating cruft objects"), 0);
+ progress_state = start_progress(the_repository,
+ _("Enumerating cruft objects"), 0);
add_objects_in_unpacked_packs();
add_unreachable_loose_objects();
@@ -3686,7 +3717,8 @@ static void enumerate_and_traverse_cruft_objects(struct string_list *fresh_packs
revs.ignore_missing_links = 1;
if (progress)
- progress_state = start_progress(_("Enumerating cruft objects"), 0);
+ progress_state = start_progress(the_repository,
+ _("Enumerating cruft objects"), 0);
ret = add_unseen_recent_objects_to_traversal(&revs, cruft_expiration,
set_cruft_mtime, 1);
stop_progress(&progress_state);
@@ -3705,7 +3737,8 @@ static void enumerate_and_traverse_cruft_objects(struct string_list *fresh_packs
if (prepare_revision_walk(&revs))
die(_("revision walk setup failed"));
if (progress)
- progress_state = start_progress(_("Traversing cruft objects"), 0);
+ progress_state = start_progress(the_repository,
+ _("Traversing cruft objects"), 0);
nr_seen = 0;
traverse_commit_list(&revs, show_cruft_commit, show_cruft_object, NULL);
@@ -3858,7 +3891,8 @@ static void show_object__ma_allow_promisor(struct object *obj, const char *name,
* Quietly ignore EXPECTED missing objects. This avoids problems with
* staging them now and getting an odd error later.
*/
- if (!has_object(the_repository, &obj->oid, 0) && is_promisor_object(&obj->oid))
+ if (!has_object(the_repository, &obj->oid, 0) &&
+ is_promisor_object(to_pack.repo, &obj->oid))
return;
show_object(obj, name, data);
@@ -3927,7 +3961,9 @@ static int add_object_in_unpacked_pack(const struct object_id *oid,
static void add_objects_in_unpacked_packs(void)
{
- if (for_each_packed_object(add_object_in_unpacked_pack, NULL,
+ if (for_each_packed_object(to_pack.repo,
+ add_object_in_unpacked_pack,
+ NULL,
FOR_EACH_OBJECT_PACK_ORDER |
FOR_EACH_OBJECT_LOCAL_ONLY |
FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS |
@@ -3968,7 +4004,7 @@ static int add_loose_object(const struct object_id *oid, const char *path,
*/
static void add_unreachable_loose_objects(void)
{
- for_each_loose_file_in_objdir(get_object_directory(),
+ for_each_loose_file_in_objdir(repo_get_object_directory(the_repository),
add_loose_object,
NULL, NULL, NULL);
}
@@ -3984,7 +4020,7 @@ static int has_sha1_pack_kept_or_nonlocal(const struct object_id *oid)
while (p) {
if ((!p->pack_local || p->pack_keep ||
p->pack_keep_in_core) &&
- find_pack_entry_one(oid->hash, p)) {
+ find_pack_entry_one(oid, p)) {
last_found = p;
return 1;
}
@@ -4069,6 +4105,15 @@ static int get_object_list_from_bitmap(struct rev_info *revs)
if (!(bitmap_git = prepare_bitmap_walk(revs, 0)))
return -1;
+ /*
+ * For now, force the name-hash version to be 1 since that
+ * is the version implied by the bitmap format. Later, the
+ * format can include this version explicitly in its format,
+ * allowing readers to know the version that was used during
+ * the bitmap write.
+ */
+ name_hash_version = 1;
+
if (pack_options_allow_reuse())
reuse_partial_packfile_from_bitmap(bitmap_git,
&reuse_packfiles,
@@ -4312,7 +4357,22 @@ static int option_parse_cruft_expiration(const struct option *opt UNUSED,
return 0;
}
-int cmd_pack_objects(int argc, const char **argv, const char *prefix)
+static int is_not_in_promisor_pack_obj(struct object *obj, void *data UNUSED)
+{
+ struct object_info info = OBJECT_INFO_INIT;
+ if (oid_object_info_extended(the_repository, &obj->oid, &info, 0))
+ BUG("should_include_obj should only be called on existing objects");
+ return info.whence != OI_PACKED || !info.u.packed.pack->pack_promisor;
+}
+
+static int is_not_in_promisor_pack(struct commit *commit, void *data) {
+ return is_not_in_promisor_pack_obj((struct object *) commit, data);
+}
+
+int cmd_pack_objects(int argc,
+ const char **argv,
+ const char *prefix,
+ struct repository *repo UNUSED)
{
int use_internal_rev_list = 0;
int shallow = 0;
@@ -4421,11 +4481,16 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
option_parse_missing_action),
OPT_BOOL(0, "exclude-promisor-objects", &exclude_promisor_objects,
N_("do not pack objects in promisor packfiles")),
+ OPT_BOOL(0, "exclude-promisor-objects-best-effort",
+ &exclude_promisor_objects_best_effort,
+ N_("implies --missing=allow-any")),
OPT_BOOL(0, "delta-islands", &use_delta_islands,
N_("respect islands during delta compression")),
OPT_STRING_LIST(0, "uri-protocol", &uri_protocols,
N_("protocol"),
N_("exclude any configured uploadpack.blobpackfileuri with this protocol")),
+ OPT_INTEGER(0, "name-hash-version", &name_hash_version,
+ N_("use the specified name-hash function to group similar objects")),
OPT_END(),
};
@@ -4501,10 +4566,18 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
strvec_push(&rp, "--unpacked");
}
+ if (exclude_promisor_objects && exclude_promisor_objects_best_effort)
+ die(_("options '%s' and '%s' cannot be used together"),
+ "--exclude-promisor-objects", "--exclude-promisor-objects-best-effort");
if (exclude_promisor_objects) {
use_internal_rev_list = 1;
fetch_if_missing = 0;
strvec_push(&rp, "--exclude-promisor-objects");
+ } else if (exclude_promisor_objects_best_effort) {
+ use_internal_rev_list = 1;
+ fetch_if_missing = 0;
+ option_parse_missing_action(NULL, "allow-any", 0);
+ /* revs configured below */
}
if (unpack_unreachable || keep_unreachable || pack_loose_unreachable)
use_internal_rev_list = 1;
@@ -4573,6 +4646,11 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
if (pack_to_stdout || !rev_list_all)
write_bitmap_index = 0;
+ if (name_hash_version < 0)
+ name_hash_version = (int)git_env_ulong("GIT_TEST_NAME_HASH_VERSION", 1);
+
+ validate_name_hash_version();
+
if (use_delta_islands)
strvec_push(&rp, "--topo-order");
@@ -4608,7 +4686,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
prepare_packing_data(the_repository, &to_pack);
if (progress && !cruft)
- progress_state = start_progress(_("Enumerating objects"), 0);
+ progress_state = start_progress(the_repository,
+ _("Enumerating objects"), 0);
if (stdin_packs) {
/* avoids adding objects in excluded packs */
ignore_packed_keep_in_core = 1;
@@ -4624,6 +4703,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
repo_init_revisions(the_repository, &revs, NULL);
list_objects_filter_copy(&revs.filter, &filter_options);
+ if (exclude_promisor_objects_best_effort) {
+ revs.include_check = is_not_in_promisor_pack;
+ revs.include_check_obj = is_not_in_promisor_pack_obj;
+ }
get_object_list(&revs, rp.nr, rp.v);
release_revisions(&revs);
}
@@ -4669,6 +4752,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
cleanup:
clear_packing_data(&to_pack);
list_objects_filter_release(&filter_options);
+ string_list_clear(&keep_pack_list, 0);
strvec_clear(&rp);
return 0;