Skip to content

Commit

Permalink
Merge branch 'ps/pack-refs-auto'
Browse files Browse the repository at this point in the history
"git pack-refs" learned the "--auto" option, which is a useful
addition to be triggered from "git gc --auto".

Acked-by: Karthik Nayak <[email protected]>
cf. <CAOLa=ZRAEA7rSUoYL0h-2qfEELdbPHbeGpgBJRqesyhHi9Q6WQ@mail.gmail.com>

* ps/pack-refs-auto:
  builtin/gc: pack refs when using `git maintenance run --auto`
  builtin/gc: forward git-gc(1)'s `--auto` flag when packing refs
  t6500: extract objects with "17" prefix
  builtin/gc: move `struct maintenance_run_opts`
  builtin/pack-refs: introduce new "--auto" flag
  builtin/pack-refs: release allocated memory
  refs/reftable: expose auto compaction via new flag
  refs: remove `PACK_REFS_ALL` flag
  refs: move `struct pack_refs_opts` to where it's used
  t/helper: drop pack-refs wrapper
  refs/reftable: print errors on compaction failure
  reftable/stack: gracefully handle failed auto-compaction due to locks
  reftable/stack: use error codes when locking fails during compaction
  reftable/error: discern locked/outdated errors
  reftable/stack: fix error handling in `reftable_stack_init_addition()`
  • Loading branch information
gitster committed Apr 9, 2024
2 parents a6abdda + 9f6714a commit eacfd58
Show file tree
Hide file tree
Showing 14 changed files with 308 additions and 125 deletions.
15 changes: 14 additions & 1 deletion Documentation/git-pack-refs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ git-pack-refs - Pack heads and tags for efficient repository access
SYNOPSIS
--------
[verse]
'git pack-refs' [--all] [--no-prune] [--include <pattern>] [--exclude <pattern>]
'git pack-refs' [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude <pattern>]

DESCRIPTION
-----------
Expand Down Expand Up @@ -60,6 +60,19 @@ with many branches of historical interests.
The command usually removes loose refs under `$GIT_DIR/refs`
hierarchy after packing them. This option tells it not to.

--auto::

Pack refs as needed depending on the current state of the ref database. The
behavior depends on the ref format used by the repository and may change in the
future.
+
- "files": No special handling for `--auto` has been implemented.
+
- "reftable": Tables are compacted such that they form a geometric
sequence. For two tables N and N+1, where N+1 is newer, this
maintains the property that N is at least twice as big as N+1. Only
tables that violate this property are compacted.

--include <pattern>::

Pack refs based on a `glob(7)` pattern. Repetitions of this option
Expand Down
86 changes: 49 additions & 37 deletions builtin/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -180,13 +180,51 @@ static void gc_config(void)
git_config(git_default_config, NULL);
}

struct maintenance_run_opts;
enum schedule_priority {
SCHEDULE_NONE = 0,
SCHEDULE_WEEKLY = 1,
SCHEDULE_DAILY = 2,
SCHEDULE_HOURLY = 3,
};

static enum schedule_priority parse_schedule(const char *value)
{
if (!value)
return SCHEDULE_NONE;
if (!strcasecmp(value, "hourly"))
return SCHEDULE_HOURLY;
if (!strcasecmp(value, "daily"))
return SCHEDULE_DAILY;
if (!strcasecmp(value, "weekly"))
return SCHEDULE_WEEKLY;
return SCHEDULE_NONE;
}

struct maintenance_run_opts {
int auto_flag;
int quiet;
enum schedule_priority schedule;
};

static int pack_refs_condition(void)
{
/*
* The auto-repacking logic for refs is handled by the ref backends and
* exposed via `git pack-refs --auto`. We thus always return truish
* here and let the backend decide for us.
*/
return 1;
}

static int maintenance_task_pack_refs(MAYBE_UNUSED struct maintenance_run_opts *opts)
{
struct child_process cmd = CHILD_PROCESS_INIT;

cmd.git_cmd = 1;
strvec_pushl(&cmd.args, "pack-refs", "--all", "--prune", NULL);
if (opts->auto_flag)
strvec_push(&cmd.args, "--auto");

return run_command(&cmd);
}

Expand Down Expand Up @@ -547,7 +585,7 @@ static int report_last_gc_error(void)
return ret;
}

static void gc_before_repack(void)
static void gc_before_repack(struct maintenance_run_opts *opts)
{
/*
* We may be called twice, as both the pre- and
Expand All @@ -558,7 +596,7 @@ static void gc_before_repack(void)
if (done++)
return;

if (pack_refs && maintenance_task_pack_refs(NULL))
if (pack_refs && maintenance_task_pack_refs(opts))
die(FAILED_RUN, "pack-refs");

if (prune_reflogs) {
Expand All @@ -574,7 +612,6 @@ static void gc_before_repack(void)
int cmd_gc(int argc, const char **argv, const char *prefix)
{
int aggressive = 0;
int auto_gc = 0;
int quiet = 0;
int force = 0;
const char *name;
Expand All @@ -583,6 +620,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
int keep_largest_pack = -1;
timestamp_t dummy;
struct child_process rerere_cmd = CHILD_PROCESS_INIT;
struct maintenance_run_opts opts = {0};

struct option builtin_gc_options[] = {
OPT__QUIET(&quiet, N_("suppress progress reporting")),
Expand All @@ -593,7 +631,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
OPT_MAGNITUDE(0, "max-cruft-size", &max_cruft_size,
N_("with --cruft, limit the size of new cruft packs")),
OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")),
OPT_BOOL_F(0, "auto", &auto_gc, N_("enable auto-gc mode"),
OPT_BOOL_F(0, "auto", &opts.auto_flag, N_("enable auto-gc mode"),
PARSE_OPT_NOCOMPLETE),
OPT_BOOL_F(0, "force", &force,
N_("force running gc even if there may be another gc running"),
Expand Down Expand Up @@ -638,7 +676,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
if (quiet)
strvec_push(&repack, "-q");

if (auto_gc) {
if (opts.auto_flag) {
/*
* Auto-gc should be least intrusive as possible.
*/
Expand All @@ -663,7 +701,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)

if (lock_repo_for_gc(force, &pid))
return 0;
gc_before_repack(); /* dies on failure */
gc_before_repack(&opts); /* dies on failure */
delete_tempfile(&pidfile);

/*
Expand All @@ -688,7 +726,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)

name = lock_repo_for_gc(force, &pid);
if (name) {
if (auto_gc)
if (opts.auto_flag)
return 0; /* be quiet on --auto */
die(_("gc is already running on machine '%s' pid %"PRIuMAX" (use --force if not)"),
name, (uintmax_t)pid);
Expand All @@ -703,7 +741,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
atexit(process_log_file_at_exit);
}

gc_before_repack();
gc_before_repack(&opts);

if (!repository_format_precious_objects) {
struct child_process repack_cmd = CHILD_PROCESS_INIT;
Expand Down Expand Up @@ -758,7 +796,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
!quiet && !daemonized ? COMMIT_GRAPH_WRITE_PROGRESS : 0,
NULL);

if (auto_gc && too_many_loose_objects())
if (opts.auto_flag && too_many_loose_objects())
warning(_("There are too many unreachable loose objects; "
"run 'git prune' to remove them."));

Expand All @@ -773,26 +811,6 @@ static const char *const builtin_maintenance_run_usage[] = {
NULL
};

enum schedule_priority {
SCHEDULE_NONE = 0,
SCHEDULE_WEEKLY = 1,
SCHEDULE_DAILY = 2,
SCHEDULE_HOURLY = 3,
};

static enum schedule_priority parse_schedule(const char *value)
{
if (!value)
return SCHEDULE_NONE;
if (!strcasecmp(value, "hourly"))
return SCHEDULE_HOURLY;
if (!strcasecmp(value, "daily"))
return SCHEDULE_DAILY;
if (!strcasecmp(value, "weekly"))
return SCHEDULE_WEEKLY;
return SCHEDULE_NONE;
}

static int maintenance_opt_schedule(const struct option *opt, const char *arg,
int unset)
{
Expand All @@ -809,12 +827,6 @@ static int maintenance_opt_schedule(const struct option *opt, const char *arg,
return 0;
}

struct maintenance_run_opts {
int auto_flag;
int quiet;
enum schedule_priority schedule;
};

/* Remember to update object flag allocation in object.h */
#define SEEN (1u<<0)

Expand Down Expand Up @@ -1296,7 +1308,7 @@ static struct maintenance_task tasks[] = {
[TASK_PACK_REFS] = {
"pack-refs",
maintenance_task_pack_refs,
NULL,
pack_refs_condition,
},
};

Expand Down
31 changes: 20 additions & 11 deletions builtin/pack-refs.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,28 @@
#include "revision.h"

static char const * const pack_refs_usage[] = {
N_("git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude <pattern>]"),
N_("git pack-refs [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude <pattern>]"),
NULL
};

int cmd_pack_refs(int argc, const char **argv, const char *prefix)
{
unsigned int flags = PACK_REFS_PRUNE;
static struct ref_exclusions excludes = REF_EXCLUSIONS_INIT;
static struct string_list included_refs = STRING_LIST_INIT_NODUP;
struct pack_refs_opts pack_refs_opts = { .exclusions = &excludes,
.includes = &included_refs,
.flags = flags };
static struct string_list option_excluded_refs = STRING_LIST_INIT_NODUP;
struct ref_exclusions excludes = REF_EXCLUSIONS_INIT;
struct string_list included_refs = STRING_LIST_INIT_NODUP;
struct pack_refs_opts pack_refs_opts = {
.exclusions = &excludes,
.includes = &included_refs,
.flags = PACK_REFS_PRUNE,
};
struct string_list option_excluded_refs = STRING_LIST_INIT_NODUP;
struct string_list_item *item;
int pack_all = 0;
int ret;

struct option opts[] = {
OPT_BIT(0, "all", &pack_refs_opts.flags, N_("pack everything"), PACK_REFS_ALL),
OPT_BOOL(0, "all", &pack_all, N_("pack everything")),
OPT_BIT(0, "prune", &pack_refs_opts.flags, N_("prune loose refs (default)"), PACK_REFS_PRUNE),
OPT_BIT(0, "auto", &pack_refs_opts.flags, N_("auto-pack refs as needed"), PACK_REFS_AUTO),
OPT_STRING_LIST(0, "include", pack_refs_opts.includes, N_("pattern"),
N_("references to include")),
OPT_STRING_LIST(0, "exclude", &option_excluded_refs, N_("pattern"),
Expand All @@ -38,11 +42,16 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix)
for_each_string_list_item(item, &option_excluded_refs)
add_ref_exclusion(pack_refs_opts.exclusions, item->string);

if (pack_refs_opts.flags & PACK_REFS_ALL)
if (pack_all)
string_list_append(pack_refs_opts.includes, "*");

if (!pack_refs_opts.includes->nr)
string_list_append(pack_refs_opts.includes, "refs/tags/*");

return refs_pack_refs(get_main_ref_store(the_repository), &pack_refs_opts);
ret = refs_pack_refs(get_main_ref_store(the_repository), &pack_refs_opts);

clear_ref_exclusions(&excludes);
string_list_clear(&included_refs, 0);
string_list_clear(&option_excluded_refs, 0);
return ret;
}
20 changes: 11 additions & 9 deletions refs.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,6 @@ const char *ref_storage_format_to_name(unsigned int ref_storage_format);
#define RESOLVE_REF_NO_RECURSE 0x02
#define RESOLVE_REF_ALLOW_BAD_NAME 0x04

struct pack_refs_opts {
unsigned int flags;
struct ref_exclusions *exclusions;
struct string_list *includes;
};

const char *refs_resolve_ref_unsafe(struct ref_store *refs,
const char *refname,
int resolve_flags,
Expand Down Expand Up @@ -428,10 +422,18 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt,
/*
* Flags for controlling behaviour of pack_refs()
* PACK_REFS_PRUNE: Prune loose refs after packing
* PACK_REFS_ALL: Pack _all_ refs, not just tags and already packed refs
* PACK_REFS_AUTO: Pack refs on a best effort basis. The heuristics and end
* result are decided by the ref backend. Backends may ignore
* this flag and fall back to a normal repack.
*/
#define PACK_REFS_PRUNE 0x0001
#define PACK_REFS_ALL 0x0002
#define PACK_REFS_PRUNE (1 << 0)
#define PACK_REFS_AUTO (1 << 1)

struct pack_refs_opts {
unsigned int flags;
struct ref_exclusions *exclusions;
struct string_list *includes;
};

/*
* Write a packed-refs file for the current repository.
Expand Down
11 changes: 9 additions & 2 deletions refs/reftable-backend.c
Original file line number Diff line number Diff line change
Expand Up @@ -1203,9 +1203,16 @@ static int reftable_be_pack_refs(struct ref_store *ref_store,
if (!stack)
stack = refs->main_stack;

ret = reftable_stack_compact_all(stack, NULL);
if (ret)
if (opts->flags & PACK_REFS_AUTO)
ret = reftable_stack_auto_compact(stack);
else
ret = reftable_stack_compact_all(stack, NULL);
if (ret < 0) {
ret = error(_("unable to compact stack: %s"),
reftable_error_str(ret));
goto out;
}

ret = reftable_stack_clean(stack);
if (ret)
goto out;
Expand Down
4 changes: 3 additions & 1 deletion reftable/error.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const char *reftable_error_str(int err)
case REFTABLE_NOT_EXIST_ERROR:
return "file does not exist";
case REFTABLE_LOCK_ERROR:
return "data is outdated";
return "data is locked";
case REFTABLE_API_ERROR:
return "misuse of the reftable API";
case REFTABLE_ZLIB_ERROR:
Expand All @@ -35,6 +35,8 @@ const char *reftable_error_str(int err)
return "invalid refname";
case REFTABLE_ENTRY_TOO_BIG_ERROR:
return "entry too large";
case REFTABLE_OUTDATED_ERROR:
return "data concurrently modified";
case -1:
return "general error";
default:
Expand Down
5 changes: 4 additions & 1 deletion reftable/reftable-error.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ enum reftable_error {
*/
REFTABLE_NOT_EXIST_ERROR = -4,

/* Trying to write out-of-date data. */
/* Trying to access locked data. */
REFTABLE_LOCK_ERROR = -5,

/* Misuse of the API:
Expand Down Expand Up @@ -57,6 +57,9 @@ enum reftable_error {
/* Entry does not fit. This can happen when writing outsize reflog
messages. */
REFTABLE_ENTRY_TOO_BIG_ERROR = -11,

/* Trying to write out-of-date data. */
REFTABLE_OUTDATED_ERROR = -12,
};

/* convert the numeric error code to a string. The string should not be
Expand Down
Loading

0 comments on commit eacfd58

Please sign in to comment.