361 lines
9.6 KiB
C
361 lines
9.6 KiB
C
#include "git-compat-util.h"
|
|
#include "dir.h"
|
|
#include "midx.h"
|
|
#include "odb.h"
|
|
#include "packfile.h"
|
|
#include "path.h"
|
|
#include "repack.h"
|
|
#include "repository.h"
|
|
#include "run-command.h"
|
|
#include "tempfile.h"
|
|
|
|
void prepare_pack_objects(struct child_process *cmd,
|
|
const struct pack_objects_args *args,
|
|
const char *out)
|
|
{
|
|
strvec_push(&cmd->args, "pack-objects");
|
|
if (args->window)
|
|
strvec_pushf(&cmd->args, "--window=%s", args->window);
|
|
if (args->window_memory)
|
|
strvec_pushf(&cmd->args, "--window-memory=%s", args->window_memory);
|
|
if (args->depth)
|
|
strvec_pushf(&cmd->args, "--depth=%s", args->depth);
|
|
if (args->threads)
|
|
strvec_pushf(&cmd->args, "--threads=%s", args->threads);
|
|
if (args->max_pack_size)
|
|
strvec_pushf(&cmd->args, "--max-pack-size=%lu", args->max_pack_size);
|
|
if (args->no_reuse_delta)
|
|
strvec_pushf(&cmd->args, "--no-reuse-delta");
|
|
if (args->no_reuse_object)
|
|
strvec_pushf(&cmd->args, "--no-reuse-object");
|
|
if (args->name_hash_version)
|
|
strvec_pushf(&cmd->args, "--name-hash-version=%d", args->name_hash_version);
|
|
if (args->path_walk)
|
|
strvec_pushf(&cmd->args, "--path-walk");
|
|
if (args->local)
|
|
strvec_push(&cmd->args, "--local");
|
|
if (args->quiet)
|
|
strvec_push(&cmd->args, "--quiet");
|
|
if (args->delta_base_offset)
|
|
strvec_push(&cmd->args, "--delta-base-offset");
|
|
if (!args->pack_kept_objects)
|
|
strvec_push(&cmd->args, "--honor-pack-keep");
|
|
strvec_push(&cmd->args, out);
|
|
cmd->git_cmd = 1;
|
|
cmd->out = -1;
|
|
}
|
|
|
|
void pack_objects_args_release(struct pack_objects_args *args)
|
|
{
|
|
free(args->window);
|
|
free(args->window_memory);
|
|
free(args->depth);
|
|
free(args->threads);
|
|
list_objects_filter_release(&args->filter_options);
|
|
}
|
|
|
|
void repack_remove_redundant_pack(struct repository *repo, const char *dir_name,
|
|
const char *base_name)
|
|
{
|
|
struct strbuf buf = STRBUF_INIT;
|
|
struct odb_source *source = repo->objects->sources;
|
|
struct multi_pack_index *m = get_multi_pack_index(source);
|
|
strbuf_addf(&buf, "%s.pack", base_name);
|
|
if (m && source->local && midx_contains_pack(m, buf.buf))
|
|
clear_midx_file(repo);
|
|
strbuf_insertf(&buf, 0, "%s/", dir_name);
|
|
unlink_pack_path(buf.buf, 1);
|
|
strbuf_release(&buf);
|
|
}
|
|
|
|
const char *write_pack_opts_pack_prefix(const struct write_pack_opts *opts)
|
|
{
|
|
const char *pack_prefix;
|
|
if (!skip_prefix(opts->packtmp, opts->packdir, &pack_prefix))
|
|
die(_("pack prefix %s does not begin with objdir %s"),
|
|
opts->packtmp, opts->packdir);
|
|
if (*pack_prefix == '/')
|
|
pack_prefix++;
|
|
return pack_prefix;
|
|
}
|
|
|
|
bool write_pack_opts_is_local(const struct write_pack_opts *opts)
|
|
{
|
|
return starts_with(opts->destination, opts->packdir);
|
|
}
|
|
|
|
int finish_pack_objects_cmd(const struct git_hash_algo *algop,
|
|
const struct write_pack_opts *opts,
|
|
struct child_process *cmd,
|
|
struct string_list *names)
|
|
{
|
|
FILE *out;
|
|
bool local = write_pack_opts_is_local(opts);
|
|
struct strbuf line = STRBUF_INIT;
|
|
|
|
out = xfdopen(cmd->out, "r");
|
|
while (strbuf_getline_lf(&line, out) != EOF) {
|
|
struct string_list_item *item;
|
|
|
|
if (line.len != algop->hexsz)
|
|
die(_("repack: Expecting full hex object ID lines only "
|
|
"from pack-objects."));
|
|
/*
|
|
* Avoid putting packs written outside of the repository in the
|
|
* list of names.
|
|
*/
|
|
if (local) {
|
|
item = string_list_append(names, line.buf);
|
|
item->util = generated_pack_populate(line.buf,
|
|
opts->packtmp);
|
|
}
|
|
}
|
|
fclose(out);
|
|
|
|
strbuf_release(&line);
|
|
|
|
return finish_command(cmd);
|
|
}
|
|
|
|
#define DELETE_PACK 1
|
|
#define RETAIN_PACK 2
|
|
|
|
void existing_packs_collect(struct existing_packs *existing,
|
|
const struct string_list *extra_keep)
|
|
{
|
|
struct packfile_store *packs = existing->repo->objects->packfiles;
|
|
struct packed_git *p;
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
for (p = packfile_store_get_all_packs(packs); p; p = p->next) {
|
|
size_t i;
|
|
const char *base;
|
|
|
|
if (p->multi_pack_index)
|
|
string_list_append(&existing->midx_packs,
|
|
pack_basename(p));
|
|
if (!p->pack_local)
|
|
continue;
|
|
|
|
base = pack_basename(p);
|
|
|
|
for (i = 0; i < extra_keep->nr; i++)
|
|
if (!fspathcmp(base, extra_keep->items[i].string))
|
|
break;
|
|
|
|
strbuf_reset(&buf);
|
|
strbuf_addstr(&buf, base);
|
|
strbuf_strip_suffix(&buf, ".pack");
|
|
|
|
if ((extra_keep->nr > 0 && i < extra_keep->nr) || p->pack_keep)
|
|
string_list_append(&existing->kept_packs, buf.buf);
|
|
else if (p->is_cruft)
|
|
string_list_append(&existing->cruft_packs, buf.buf);
|
|
else
|
|
string_list_append(&existing->non_kept_packs, buf.buf);
|
|
}
|
|
|
|
string_list_sort(&existing->kept_packs);
|
|
string_list_sort(&existing->non_kept_packs);
|
|
string_list_sort(&existing->cruft_packs);
|
|
string_list_sort(&existing->midx_packs);
|
|
strbuf_release(&buf);
|
|
}
|
|
|
|
int existing_packs_has_non_kept(const struct existing_packs *existing)
|
|
{
|
|
return existing->non_kept_packs.nr || existing->cruft_packs.nr;
|
|
}
|
|
|
|
static void existing_pack_mark_for_deletion(struct string_list_item *item)
|
|
{
|
|
item->util = (void*)((uintptr_t)item->util | DELETE_PACK);
|
|
}
|
|
|
|
static void existing_pack_unmark_for_deletion(struct string_list_item *item)
|
|
{
|
|
item->util = (void*)((uintptr_t)item->util & ~DELETE_PACK);
|
|
}
|
|
|
|
int existing_pack_is_marked_for_deletion(struct string_list_item *item)
|
|
{
|
|
return (uintptr_t)item->util & DELETE_PACK;
|
|
}
|
|
|
|
static void existing_packs_mark_retained(struct string_list_item *item)
|
|
{
|
|
item->util = (void*)((uintptr_t)item->util | RETAIN_PACK);
|
|
}
|
|
|
|
static int existing_pack_is_retained(struct string_list_item *item)
|
|
{
|
|
return (uintptr_t)item->util & RETAIN_PACK;
|
|
}
|
|
|
|
static void existing_packs_mark_for_deletion_1(const struct git_hash_algo *algop,
|
|
struct string_list *names,
|
|
struct string_list *list)
|
|
{
|
|
struct string_list_item *item;
|
|
const size_t hexsz = algop->hexsz;
|
|
|
|
for_each_string_list_item(item, list) {
|
|
char *sha1;
|
|
size_t len = strlen(item->string);
|
|
if (len < hexsz)
|
|
continue;
|
|
sha1 = item->string + len - hexsz;
|
|
|
|
if (existing_pack_is_retained(item)) {
|
|
existing_pack_unmark_for_deletion(item);
|
|
} else if (!string_list_has_string(names, sha1)) {
|
|
/*
|
|
* Mark this pack for deletion, which ensures
|
|
* that this pack won't be included in a MIDX
|
|
* (if `--write-midx` was given) and that we
|
|
* will actually delete this pack (if `-d` was
|
|
* given).
|
|
*/
|
|
existing_pack_mark_for_deletion(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
void existing_packs_retain_cruft(struct existing_packs *existing,
|
|
struct packed_git *cruft)
|
|
{
|
|
struct strbuf buf = STRBUF_INIT;
|
|
struct string_list_item *item;
|
|
|
|
strbuf_addstr(&buf, pack_basename(cruft));
|
|
strbuf_strip_suffix(&buf, ".pack");
|
|
|
|
item = string_list_lookup(&existing->cruft_packs, buf.buf);
|
|
if (!item)
|
|
BUG("could not find cruft pack '%s'", pack_basename(cruft));
|
|
|
|
existing_packs_mark_retained(item);
|
|
strbuf_release(&buf);
|
|
}
|
|
|
|
void existing_packs_mark_for_deletion(struct existing_packs *existing,
|
|
struct string_list *names)
|
|
|
|
{
|
|
const struct git_hash_algo *algop = existing->repo->hash_algo;
|
|
existing_packs_mark_for_deletion_1(algop, names,
|
|
&existing->non_kept_packs);
|
|
existing_packs_mark_for_deletion_1(algop, names,
|
|
&existing->cruft_packs);
|
|
}
|
|
|
|
static void remove_redundant_packs_1(struct repository *repo,
|
|
struct string_list *packs,
|
|
const char *packdir)
|
|
{
|
|
struct string_list_item *item;
|
|
for_each_string_list_item(item, packs) {
|
|
if (!existing_pack_is_marked_for_deletion(item))
|
|
continue;
|
|
repack_remove_redundant_pack(repo, packdir, item->string);
|
|
}
|
|
}
|
|
|
|
void existing_packs_remove_redundant(struct existing_packs *existing,
|
|
const char *packdir)
|
|
{
|
|
remove_redundant_packs_1(existing->repo, &existing->non_kept_packs,
|
|
packdir);
|
|
remove_redundant_packs_1(existing->repo, &existing->cruft_packs,
|
|
packdir);
|
|
}
|
|
|
|
void existing_packs_release(struct existing_packs *existing)
|
|
{
|
|
string_list_clear(&existing->kept_packs, 0);
|
|
string_list_clear(&existing->non_kept_packs, 0);
|
|
string_list_clear(&existing->cruft_packs, 0);
|
|
string_list_clear(&existing->midx_packs, 0);
|
|
}
|
|
|
|
static struct {
|
|
const char *name;
|
|
unsigned optional:1;
|
|
} exts[] = {
|
|
{".pack"},
|
|
{".rev", 1},
|
|
{".mtimes", 1},
|
|
{".bitmap", 1},
|
|
{".promisor", 1},
|
|
{".idx"},
|
|
};
|
|
|
|
struct generated_pack {
|
|
struct tempfile *tempfiles[ARRAY_SIZE(exts)];
|
|
};
|
|
|
|
struct generated_pack *generated_pack_populate(const char *name,
|
|
const char *packtmp)
|
|
{
|
|
struct stat statbuf;
|
|
struct strbuf path = STRBUF_INIT;
|
|
struct generated_pack *pack = xcalloc(1, sizeof(*pack));
|
|
size_t i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(exts); i++) {
|
|
strbuf_reset(&path);
|
|
strbuf_addf(&path, "%s-%s%s", packtmp, name, exts[i].name);
|
|
|
|
if (stat(path.buf, &statbuf))
|
|
continue;
|
|
|
|
pack->tempfiles[i] = register_tempfile(path.buf);
|
|
}
|
|
|
|
strbuf_release(&path);
|
|
return pack;
|
|
}
|
|
|
|
int generated_pack_has_ext(const struct generated_pack *pack, const char *ext)
|
|
{
|
|
size_t i;
|
|
for (i = 0; i < ARRAY_SIZE(exts); i++) {
|
|
if (strcmp(exts[i].name, ext))
|
|
continue;
|
|
return !!pack->tempfiles[i];
|
|
}
|
|
BUG("unknown pack extension: '%s'", ext);
|
|
}
|
|
|
|
void generated_pack_install(struct generated_pack *pack, const char *name,
|
|
const char *packdir, const char *packtmp)
|
|
{
|
|
size_t ext;
|
|
for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
|
|
char *fname;
|
|
|
|
fname = mkpathdup("%s/pack-%s%s", packdir, name,
|
|
exts[ext].name);
|
|
|
|
if (pack->tempfiles[ext]) {
|
|
const char *fname_old = get_tempfile_path(pack->tempfiles[ext]);
|
|
struct stat statbuffer;
|
|
|
|
if (!stat(fname_old, &statbuffer)) {
|
|
statbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
|
|
chmod(fname_old, statbuffer.st_mode);
|
|
}
|
|
|
|
if (rename_tempfile(&pack->tempfiles[ext], fname))
|
|
die_errno(_("renaming pack to '%s' failed"),
|
|
fname);
|
|
} else if (!exts[ext].optional)
|
|
die(_("pack-objects did not write a '%s' file for pack %s-%s"),
|
|
exts[ext].name, packtmp, name);
|
|
else if (unlink(fname) < 0 && errno != ENOENT)
|
|
die_errno(_("could not unlink: %s"), fname);
|
|
|
|
free(fname);
|
|
}
|
|
}
|