Skip to content
Open
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
8 changes: 8 additions & 0 deletions Documentation/git-pack-objects.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ SYNOPSIS
[--no-reuse-delta] [--delta-base-offset] [--non-empty]
[--local] [--incremental] [--window=<n>] [--depth=<n>]
[--revs [--unpacked | --all]] [--keep-pack=<pack-name>]
[--keep-pack-from-file=<file>]
[--cruft] [--cruft-expiration=<time>]
[--stdout [--filter=<filter-spec>] | <base-name>]
[--shallow] [--keep-true-parents] [--[no-]sparse]
Expand Down Expand Up @@ -193,6 +194,13 @@ depth is 4095.
leading directory (e.g. `pack-123.pack`). The option could be
specified multiple times to keep multiple packs.

--keep-pack-from-file=<file>::
Read names of packs to keep from `<file>`, one per line, and
treat each of them as if it had been given with `--keep-pack`.
Empty lines are ignored. This is meant for callers such as
linkgit:git-repack[1] that may have to name more packs than fit
on a command line.

--incremental::
This flag causes an object already in a pack to be ignored
even if it would have otherwise been packed.
Expand Down
71 changes: 58 additions & 13 deletions builtin/pack-objects.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ static const char *const pack_usage[] = {
" [--no-reuse-delta] [--delta-base-offset] [--non-empty]\n"
" [--local] [--incremental] [--window=<n>] [--depth=<n>]\n"
" [--revs [--unpacked | --all]] [--keep-pack=<pack-name>]\n"
" [--keep-pack-from-file=<file>]\n"
" [--cruft] [--cruft-expiration=<time>]\n"
" [--stdout [--filter=<filter-spec>] | <base-name>]\n"
" [--shallow] [--keep-true-parents] [--[no-]sparse]\n"
Expand Down Expand Up @@ -4274,6 +4275,7 @@ static void enumerate_cruft_objects(void)
static void enumerate_and_traverse_cruft_objects(struct string_list *fresh_packs)
{
struct packed_git *p;
struct odb_source *source;
struct rev_info revs;
int ret;

Expand Down Expand Up @@ -4301,10 +4303,17 @@ static void enumerate_and_traverse_cruft_objects(struct string_list *fresh_packs
/*
* Re-mark only the fresh packs as kept so that objects in
* unknown packs do not halt the reachability traversal early.
* The kept-pack cache was built while those packs were still
* marked, so drop it too.
*/
repo_for_each_pack(the_repository, p)
p->pack_keep_in_core = 0;
mark_pack_kept_in_core(fresh_packs, 1);
for (source = the_repository->objects->sources; source;
source = source->next) {
struct odb_source_files *files = odb_source_files_downcast(source);
packfile_store_invalidate_kept_pack_cache(files->packed);
}

if (prepare_revision_walk(&revs))
die(_("revision walk setup failed"));
Expand Down Expand Up @@ -4999,27 +5008,54 @@ static void get_object_list(struct rev_info *revs, struct strvec *argv)
oid_array_clear(&recent_objects);
}

static void add_extra_kept_packs(const struct string_list *names)
/*
* Read pack names from the file, one per line, as if each of them had
* been given with "--keep-pack".
*/
static void read_keep_pack_list(struct string_list *names, const char *path)
{
struct strbuf buf = STRBUF_INIT;
FILE *fp = xfopen(path, "r");

while (strbuf_getline(&buf, fp) != EOF) {
if (!buf.len)
continue;
string_list_append(names, buf.buf);
}
if (ferror(fp))
die_errno(_("could not read '%s'"), path);
fclose(fp);
strbuf_release(&buf);
}

static void add_extra_kept_packs(struct string_list *names,
enum stdin_packs_mode stdin_packs)
{
struct packed_git *p;

if (!names->nr)
return;

repo_for_each_pack(the_repository, p) {
const char *name = basename(p->pack_name);
int i;
string_list_sort(names);

repo_for_each_pack(the_repository, p) {
if (!p->pack_local)
continue;

for (i = 0; i < names->nr; i++)
if (!fspathcmp(name, names->items[i].string))
break;

if (i < names->nr) {
p->pack_keep_in_core = 1;
ignore_packed_keep_in_core = 1;
if (string_list_has_string(names, basename(p->pack_name))) {
/*
* When following, treat the pack like a "!" pack, not
* a "^" one: nobody said it is closed under
* reachability, so the traversal must be able to go
* through it.
*/
if (stdin_packs == STDIN_PACKS_MODE_FOLLOW) {
p->pack_keep_in_core_open = 1;
ignore_packed_keep_in_core_open = 1;
} else {
p->pack_keep_in_core = 1;
ignore_packed_keep_in_core = 1;
}
continue;
}
}
Expand Down Expand Up @@ -5131,7 +5167,11 @@ int cmd_pack_objects(int argc,
int rev_list_unpacked = 0, rev_list_all = 0, rev_list_reflog = 0;
int rev_list_index = 0;
enum stdin_packs_mode stdin_packs = STDIN_PACKS_MODE_NONE;
struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
struct string_list keep_pack_list = {
.strdup_strings = 1,
.cmp = fspathcmp,
};
char *keep_pack_from_file = NULL;
struct list_objects_filter_options filter_options =
LIST_OBJECTS_FILTER_INIT;
struct repo_config_values *cfg = repo_config_values(the_repository);
Expand Down Expand Up @@ -5216,6 +5256,8 @@ int cmd_pack_objects(int argc,
N_("ignore packs that have companion .keep file")),
OPT_STRING_LIST(0, "keep-pack", &keep_pack_list, N_("name"),
N_("ignore this pack")),
OPT_FILENAME(0, "keep-pack-from-file", &keep_pack_from_file,
N_("ignore the packs named in <file>")),
OPT_INTEGER(0, "compression", &cfg->pack_compression_level,
N_("pack compression level")),
OPT_BOOL(0, "keep-true-parents", &grafts_keep_true_parents,
Expand Down Expand Up @@ -5443,7 +5485,9 @@ int cmd_pack_objects(int argc,
if (progress && all_progress_implied)
progress = 2;

add_extra_kept_packs(&keep_pack_list);
if (keep_pack_from_file)
read_keep_pack_list(&keep_pack_list, keep_pack_from_file);
add_extra_kept_packs(&keep_pack_list, stdin_packs);
if (ignore_packed_keep_on_disk) {
struct packed_git *p;

Expand Down Expand Up @@ -5537,6 +5581,7 @@ int cmd_pack_objects(int argc,
clear_packing_data(&to_pack);
list_objects_filter_release(&filter_options);
string_list_clear(&keep_pack_list, 0);
free(keep_pack_from_file);
strvec_clear(&rp);

return 0;
Expand Down
15 changes: 15 additions & 0 deletions builtin/repack.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ int cmd_repack(int argc,
struct oidset drop_oids = OIDSET_INIT;
struct pack_geometry geometry = { 0 };
struct tempfile *refs_snapshot = NULL;
struct tempfile *kept_packs_snapshot = NULL;
int i, ret;
int show_progress;

Expand Down Expand Up @@ -456,6 +457,19 @@ int cmd_repack(int argc,

existing.repo = repo;
existing_packs_collect(&existing, &keep_pack_list);
if (existing.kept_packs.nr) {
struct strbuf path = STRBUF_INIT;

strbuf_addf(&path, "%s/%s_XXXXXX",
repo_get_object_directory(repo), "kept-packs");

kept_packs_snapshot = xmks_tempfile(path.buf);
existing_packs_snapshot_kept(&existing, kept_packs_snapshot);
po_args.kept_packs_snapshot =
get_tempfile_path(kept_packs_snapshot);

strbuf_release(&path);
}

if (geometry.split_factor) {
if (pack_everything)
Expand Down Expand Up @@ -644,6 +658,7 @@ int cmd_repack(int argc,
cruft_po_args.quiet = po_args.quiet;
cruft_po_args.delta_base_offset = po_args.delta_base_offset;
cruft_po_args.pack_kept_objects = 0;
cruft_po_args.kept_packs_snapshot = po_args.kept_packs_snapshot;

ret = write_cruft_pack(&opts, cruft_expiration,
combine_cruft_below_size, &names,
Expand Down
95 changes: 74 additions & 21 deletions object-file.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "read-cache-ll.h"
#include "run-command.h"
#include "setup.h"
#include "string-list.h"
#include "strvec.h"
#include "tempfile.h"
#include "tmp-objdir.h"
Expand Down Expand Up @@ -492,9 +493,13 @@ struct odb_transaction_files {
struct transaction_packfile packfile;
const char *prefix;

struct tempfile **pack_lockfiles;
size_t pack_lockfiles_nr;
size_t pack_lockfiles_alloc;
/*
* The message index-pack writes into its ".keep" files, and where
* those files end up once the quarantine is migrated. Each "util"
* holds a tempfile for as long as we consider that file ours.
*/
char *keep_msg;
struct string_list pack_lockfiles;
};

int odb_transaction_files_prepare(struct odb_transaction *base)
Expand Down Expand Up @@ -1256,6 +1261,45 @@ int read_loose_object(struct repository *repo,
return ret;
}

/*
* Track the ".keep" files before the migration moves them into place, so
* that a signal in the middle of it removes ours.
*/
static void register_pack_lockfiles(struct odb_transaction_files *transaction)
{
struct string_list_item *item;

for_each_string_list_item(item, &transaction->pack_lockfiles)
item->util = register_tempfile(item->string);
}

/*
* The migration stops at the first file that differs from what is already
* at its destination, and a ".keep" left by somebody else's push is one
* such file. Rather than work out what got installed, read the files
* back: one that does not carry our message is not ours to remove.
*/
static void disown_foreign_pack_lockfiles(struct odb_transaction_files *transaction)
{
struct strbuf buf = STRBUF_INIT;
struct string_list_item *item;

for_each_string_list_item(item, &transaction->pack_lockfiles) {
struct tempfile *lockfile = item->util;

strbuf_reset(&buf);
if (strbuf_read_file(&buf, item->string, 0) >= 0) {
strbuf_trim_trailing_newline(&buf);
if (!strcmp(buf.buf, transaction->keep_msg))
continue;
}
unregister_tempfile(&lockfile);
item->util = NULL;
}

strbuf_release(&buf);
}

static int odb_transaction_files_commit(struct odb_transaction *base)
{
struct odb_transaction_files *transaction =
Expand All @@ -1264,6 +1308,7 @@ static int odb_transaction_files_commit(struct odb_transaction *base)
if (transaction->objdir) {
struct strbuf temp_path = STRBUF_INIT;
struct tempfile *temp;
int ret;

/*
* Issue a full hardware flush against a temporary file to ensure
Expand All @@ -1285,7 +1330,10 @@ static int odb_transaction_files_commit(struct odb_transaction *base)
* Make the object files visible in the primary ODB after their data is
* fully durable.
*/
if (tmp_objdir_migrate(transaction->objdir))
register_pack_lockfiles(transaction);
ret = tmp_objdir_migrate(transaction->objdir);
disown_foreign_pack_lockfiles(transaction);
if (ret)
return error(_("unable to migrate temporary objects"));

transaction->objdir = NULL;
Expand Down Expand Up @@ -1393,10 +1441,10 @@ static int odb_transaction_files_write_pack(struct odb_transaction *base,

if (xgethostname(hostname, sizeof(hostname)))
xsnprintf(hostname, sizeof(hostname), "localhost");
strvec_pushf(&child.args,
"--keep=receive-pack %"PRIuMAX" on %s",
(uintmax_t)getpid(),
hostname);
free(transaction->keep_msg);
transaction->keep_msg = xstrfmt("receive-pack %"PRIuMAX" on %s",
(uintmax_t)getpid(), hostname);
strvec_pushf(&child.args, "--keep=%s", transaction->keep_msg);

if (!opts->quiet && err_fd)
strvec_push(&child.args, "--show-resolving-progress");
Expand All @@ -1423,18 +1471,13 @@ static int odb_transaction_files_write_pack(struct odb_transaction *base,
/*
* The lockfile filepath is expected to be the final location of
* the ".keep" file after being migrated to the main ODB source.
* This ensures the lockfile can be found and removed later
* after the ODB transaction has been committed.
* We start tracking it right before that migration; see
* odb_transaction_files_commit().
*/
lockfile = index_pack_lockfile(base->source, child.out, NULL);
if (lockfile) {
ALLOC_GROW(transaction->pack_lockfiles,
transaction->pack_lockfiles_nr + 1,
transaction->pack_lockfiles_alloc);
transaction->pack_lockfiles[transaction->pack_lockfiles_nr++] =
register_tempfile(lockfile);
free(lockfile);
}
if (lockfile)
string_list_append_nodup(&transaction->pack_lockfiles,
lockfile);
close(child.out);

status = finish_command(&child);
Expand All @@ -1454,12 +1497,21 @@ static int odb_transaction_files_finalize(struct odb_transaction *base)
{
struct odb_transaction_files *transaction =
container_of(base, struct odb_transaction_files, base);
struct string_list_item *item;
int ret = 0;

for (size_t i = 0; i < transaction->pack_lockfiles_nr; i++)
ret |= delete_tempfile(&transaction->pack_lockfiles[i]);
/*
* Only the ".keep" files that turned out to be ours still have a
* tempfile attached; delete_tempfile() does nothing for the rest.
*/
for_each_string_list_item(item, &transaction->pack_lockfiles) {
struct tempfile *lockfile = item->util;

ret |= delete_tempfile(&lockfile);
}

free(transaction->pack_lockfiles);
string_list_clear(&transaction->pack_lockfiles, 0);
FREE_AND_NULL(transaction->keep_msg);

return ret;
}
Expand Down Expand Up @@ -1492,6 +1544,7 @@ int odb_transaction_files_begin(struct odb_source *source,
transaction->base.write_pack = odb_transaction_files_write_pack;
transaction->base.env = odb_transaction_files_env;
transaction->flags = flags;
string_list_init_dup(&transaction->pack_lockfiles);

transaction->prefix = "bulk-fsync";
if (flags & ODB_TRANSACTION_RECEIVE) {
Expand Down
3 changes: 2 additions & 1 deletion odb/source-packed.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ struct odb_source_packed {
* Should not be accessed directly, but via
* `packfile_store_get_kept_pack_cache()`. The list of packs gets
* invalidated when the stored flags and the flags passed to
* `packfile_store_get_kept_pack_cache()` mismatch.
* `packfile_store_get_kept_pack_cache()` mismatch, or explicitly via
* `packfile_store_invalidate_kept_pack_cache()`.
*/
struct {
struct packed_git **packs;
Expand Down
9 changes: 7 additions & 2 deletions packfile.c
Original file line number Diff line number Diff line change
Expand Up @@ -1870,15 +1870,20 @@ int packfile_fill_entry(struct packed_git *p,
return 1;
}

void packfile_store_invalidate_kept_pack_cache(struct odb_source_packed *store)
{
FREE_AND_NULL(store->kept_cache.packs);
store->kept_cache.flags = 0;
}

static void maybe_invalidate_kept_pack_cache(struct odb_source_packed *store,
unsigned flags)
{
if (!store->kept_cache.packs)
return;
if (store->kept_cache.flags == flags)
return;
FREE_AND_NULL(store->kept_cache.packs);
store->kept_cache.flags = 0;
packfile_store_invalidate_kept_pack_cache(store);
}

struct packed_git **packfile_store_get_kept_pack_cache(struct odb_source_packed *store,
Expand Down
Loading
Loading