Skip to content
Merged
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/RelNotes/2.50.0.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ UI, Workflows & Features
delta chains from forming in a corner case even when there is no
such cycle.

* Make repository clean-up tasks "gc" can do available to "git
maintenance" front-end.


Performance, Internal Implementation, Development Support etc.
--------------------------------------------------------------
Expand Down Expand Up @@ -286,6 +289,11 @@ Fixes since v2.49
also existed on the working tree, which has been corrected.
(merge ec727e189c kj/glob-path-with-special-char later to maint).

* The fallback implementation of open_nofollow() depended on
open("symlink", O_NOFOLLOW) to set errno to ELOOP, but a few BSD
derived systems use different errno, which has been worked around.
(merge f47bcc3413 cf/wrapper-bsd-eloop later to maint).

* Other code cleanup, docfix, build fix, etc.
(merge 227c4f33a0 ja/doc-block-delimiter-markup-fix later to maint).
(merge 2bfd3b3685 ab/decorate-code-cleanup later to maint).
Expand Down
17 changes: 17 additions & 0 deletions Documentation/config/maintenance.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,20 @@ maintenance.reflog-expire.auto::
positive value implies the command should run when the number of
expired reflog entries in the "HEAD" reflog is at least the value of
`maintenance.loose-objects.auto`. The default value is 100.

maintenance.rerere-gc.auto::
This integer config option controls how often the `rerere-gc` task
should be run as part of `git maintenance run --auto`. If zero, then
the `rerere-gc` task will not run with the `--auto` option. A negative
value will force the task to run every time. Otherwise, any positive
value implies the command will run when the "rr-cache" directory exists
and has at least one entry, regardless of whether it is stale or not.
This heuristic may be refined in the future. The default value is 1.

maintenance.worktree-prune.auto::
This integer config option controls how often the `worktree-prune` task
should be run as part of `git maintenance run --auto`. If zero, then
the `worktree-prune` task will not run with the `--auto` option. A
negative value will force the task to run every time. Otherwise, a
positive value implies the command should run when the number of
prunable worktrees exceeds the value. The default value is 1.
8 changes: 8 additions & 0 deletions Documentation/git-maintenance.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,14 @@ reflog-expire::
The `reflog-expire` task deletes any entries in the reflog older than the
expiry threshold. See linkgit:git-reflog[1] for more information.

rerere-gc::
The `rerere-gc` task invokes garbage collection for stale entries in
the rerere cache. See linkgit:git-rerere[1] for more information.

worktree-prune::
The `worktree-prune` task deletes stale or broken worktrees. See
linkit:git-worktree[1] for more information.

OPTIONS
-------
--auto::
Expand Down
148 changes: 117 additions & 31 deletions builtin/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "builtin.h"
#include "abspath.h"
#include "date.h"
#include "dir.h"
#include "environment.h"
#include "hex.h"
#include "config.h"
Expand All @@ -33,6 +34,7 @@
#include "pack-objects.h"
#include "path.h"
#include "reflog.h"
#include "rerere.h"
#include "blob.h"
#include "tree.h"
#include "promisor-remote.h"
Expand All @@ -43,6 +45,7 @@
#include "hook.h"
#include "setup.h"
#include "trace2.h"
#include "worktree.h"

#define FAILED_RUN "failed to run %s"

Expand All @@ -52,15 +55,9 @@ static const char * const builtin_gc_usage[] = {
};

static timestamp_t gc_log_expire_time;

static struct strvec repack = STRVEC_INIT;
static struct strvec prune = STRVEC_INIT;
static struct strvec prune_worktrees = STRVEC_INIT;
static struct strvec rerere = STRVEC_INIT;

static struct tempfile *pidfile;
static struct lock_file log_lock;

static struct string_list pack_garbage = STRING_LIST_INIT_DUP;

static void clean_pack_garbage(void)
Expand Down Expand Up @@ -339,6 +336,94 @@ static int maintenance_task_reflog_expire(struct maintenance_run_opts *opts UNUS
return run_command(&cmd);
}

static int maintenance_task_worktree_prune(struct maintenance_run_opts *opts UNUSED,
struct gc_config *cfg)
{
struct child_process prune_worktrees_cmd = CHILD_PROCESS_INIT;

prune_worktrees_cmd.git_cmd = 1;
strvec_pushl(&prune_worktrees_cmd.args, "worktree", "prune", "--expire", NULL);
strvec_push(&prune_worktrees_cmd.args, cfg->prune_worktrees_expire);

return run_command(&prune_worktrees_cmd);
}

static int worktree_prune_condition(struct gc_config *cfg)
{
struct strbuf buf = STRBUF_INIT;
int should_prune = 0, limit = 1;
timestamp_t expiry_date;
struct dirent *d;
DIR *dir = NULL;

git_config_get_int("maintenance.worktree-prune.auto", &limit);
if (limit <= 0) {
should_prune = limit < 0;
goto out;
}

if (parse_expiry_date(cfg->prune_worktrees_expire, &expiry_date))
goto out;

dir = opendir(repo_git_path_replace(the_repository, &buf, "worktrees"));
if (!dir)
goto out;

while (limit && (d = readdir_skip_dot_and_dotdot(dir))) {
char *wtpath;
strbuf_reset(&buf);
if (should_prune_worktree(d->d_name, &buf, &wtpath, expiry_date))
limit--;
free(wtpath);
}

should_prune = !limit;

out:
if (dir)
closedir(dir);
strbuf_release(&buf);
return should_prune;
}

static int maintenance_task_rerere_gc(struct maintenance_run_opts *opts UNUSED,
struct gc_config *cfg UNUSED)
{
struct child_process rerere_cmd = CHILD_PROCESS_INIT;
rerere_cmd.git_cmd = 1;
strvec_pushl(&rerere_cmd.args, "rerere", "gc", NULL);
return run_command(&rerere_cmd);
}

static int rerere_gc_condition(struct gc_config *cfg UNUSED)
{
struct strbuf path = STRBUF_INIT;
int should_gc = 0, limit = 1;
DIR *dir = NULL;

git_config_get_int("maintenance.rerere-gc.auto", &limit);
if (limit <= 0) {
should_gc = limit < 0;
goto out;
}

/*
* We skip garbage collection in case we either have no "rr-cache"
* directory or when it doesn't contain at least one entry.
*/
repo_git_path_replace(the_repository, &path, "rr-cache");
dir = opendir(path.buf);
if (!dir)
goto out;
should_gc = !!readdir_skip_dot_and_dotdot(dir);

out:
strbuf_release(&path);
if (dir)
closedir(dir);
return should_gc;
}

static int too_many_loose_objects(struct gc_config *cfg)
{
/*
Expand Down Expand Up @@ -728,9 +813,9 @@ static void gc_before_repack(struct maintenance_run_opts *opts,
}

int cmd_gc(int argc,
const char **argv,
const char *prefix,
struct repository *repo UNUSED)
const char **argv,
const char *prefix,
struct repository *repo UNUSED)
{
int aggressive = 0;
int quiet = 0;
Expand All @@ -740,7 +825,6 @@ struct repository *repo UNUSED)
int daemonized = 0;
int keep_largest_pack = -1;
timestamp_t dummy;
struct child_process rerere_cmd = CHILD_PROCESS_INIT;
struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT;
struct gc_config cfg = GC_CONFIG_INIT;
const char *prune_expire_sentinel = "sentinel";
Expand Down Expand Up @@ -779,9 +863,6 @@ struct repository *repo UNUSED)
builtin_gc_usage, builtin_gc_options);

strvec_pushl(&repack, "repack", "-d", "-l", NULL);
strvec_pushl(&prune, "prune", "--expire", NULL);
strvec_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL);
strvec_pushl(&rerere, "rerere", "gc", NULL);

gc_config(&cfg);

Expand Down Expand Up @@ -907,34 +988,27 @@ struct repository *repo UNUSED)
if (cfg.prune_expire) {
struct child_process prune_cmd = CHILD_PROCESS_INIT;

strvec_pushl(&prune_cmd.args, "prune", "--expire", NULL);
/* run `git prune` even if using cruft packs */
strvec_push(&prune, cfg.prune_expire);
strvec_push(&prune_cmd.args, cfg.prune_expire);
if (quiet)
strvec_push(&prune, "--no-progress");
strvec_push(&prune_cmd.args, "--no-progress");
if (repo_has_promisor_remote(the_repository))
strvec_push(&prune,
strvec_push(&prune_cmd.args,
"--exclude-promisor-objects");
prune_cmd.git_cmd = 1;
strvec_pushv(&prune_cmd.args, prune.v);

if (run_command(&prune_cmd))
die(FAILED_RUN, prune.v[0]);
die(FAILED_RUN, prune_cmd.args.v[0]);
}
}

if (cfg.prune_worktrees_expire) {
struct child_process prune_worktrees_cmd = CHILD_PROCESS_INIT;

strvec_push(&prune_worktrees, cfg.prune_worktrees_expire);
prune_worktrees_cmd.git_cmd = 1;
strvec_pushv(&prune_worktrees_cmd.args, prune_worktrees.v);
if (run_command(&prune_worktrees_cmd))
die(FAILED_RUN, prune_worktrees.v[0]);
}
if (cfg.prune_worktrees_expire &&
maintenance_task_worktree_prune(&opts, &cfg))
die(FAILED_RUN, "worktree");

rerere_cmd.git_cmd = 1;
strvec_pushv(&rerere_cmd.args, rerere.v);
if (run_command(&rerere_cmd))
die(FAILED_RUN, rerere.v[0]);
if (maintenance_task_rerere_gc(&opts, &cfg))
die(FAILED_RUN, "rerere");

report_garbage = report_pack_garbage;
reprepare_packed_git(the_repository);
Expand Down Expand Up @@ -1467,6 +1541,8 @@ enum maintenance_task_label {
TASK_COMMIT_GRAPH,
TASK_PACK_REFS,
TASK_REFLOG_EXPIRE,
TASK_WORKTREE_PRUNE,
TASK_RERERE_GC,

/* Leave as final value */
TASK__COUNT
Expand Down Expand Up @@ -1508,6 +1584,16 @@ static struct maintenance_task tasks[] = {
maintenance_task_reflog_expire,
reflog_expire_condition,
},
[TASK_WORKTREE_PRUNE] = {
"worktree-prune",
maintenance_task_worktree_prune,
worktree_prune_condition,
},
[TASK_RERERE_GC] = {
"rerere-gc",
maintenance_task_rerere_gc,
rerere_gc_condition,
},
};

static int compare_tasks_by_selection(const void *a_, const void *b_)
Expand Down
2 changes: 1 addition & 1 deletion compiler-tricks/not-constant.c
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#include <git-compat-util.h>
int false_but_the_compiler_does_not_know_it_;
int false_but_the_compiler_does_not_know_it_ = 0;
9 changes: 7 additions & 2 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -1904,14 +1904,19 @@ if perl_features_enabled
perl_header_template = 'perl/header_templates/runtime_prefix.template.pl'
endif

perllibdir = get_option('perllibdir')
if perllibdir == ''
perllibdir = get_option('datadir') / 'perl5'
endif

perl_header = configure_file(
input: perl_header_template,
output: 'GIT-PERL-HEADER',
configuration: {
'GITEXECDIR_REL': get_option('libexecdir') / 'git-core',
'PERLLIBDIR_REL': get_option('datadir') / 'perl5',
'PERLLIBDIR_REL': perllibdir,
'LOCALEDIR_REL': get_option('datadir') / 'locale',
'INSTLIBDIR': get_option('datadir') / 'perl5',
'INSTLIBDIR': perllibdir,
'PATHSEP': pathsep,
},
)
Expand Down
4 changes: 4 additions & 0 deletions meson_options.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Configuration for Git installation
option('perllibdir', type: 'string', value: '',
description: 'Directory to install perl lib to. Defaults to <datadir>/perl5')

# Configuration for how Git behaves at runtime.
option('default_pager', type: 'string', value: 'less',
description: 'Fall-back pager.')
Expand Down
2 changes: 1 addition & 1 deletion perl/FromCPAN/Mail/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ test_dependencies += custom_target(
output: 'Address.pm',
command: generate_perl_command,
install: true,
install_dir: get_option('datadir') / 'perl5/FromCPAN/Mail',
install_dir: perllibdir / 'FromCPAN/Mail',
depends: [git_version_file],
)
2 changes: 1 addition & 1 deletion perl/FromCPAN/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ test_dependencies += custom_target(
output: 'Error.pm',
command: generate_perl_command,
install: true,
install_dir: get_option('datadir') / 'perl5/FromCPAN',
install_dir: perllibdir / 'FromCPAN',
depends: [git_version_file],
)

Expand Down
2 changes: 1 addition & 1 deletion perl/Git/LoadCPAN/Mail/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ test_dependencies += custom_target(
output: 'Address.pm',
command: generate_perl_command,
install: true,
install_dir: get_option('datadir') / 'perl5/Git/LoadCPAN/Mail',
install_dir: perllibdir / 'Git/LoadCPAN/Mail',
depends: [git_version_file],
)
2 changes: 1 addition & 1 deletion perl/Git/LoadCPAN/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ test_dependencies += custom_target(
output: 'Error.pm',
command: generate_perl_command,
install: true,
install_dir: get_option('datadir') / 'perl5/Git/LoadCPAN',
install_dir: perllibdir / 'Git/LoadCPAN',
depends: [git_version_file],
)

Expand Down
2 changes: 1 addition & 1 deletion perl/Git/SVN/Memoize/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ test_dependencies += custom_target(
output: 'YAML.pm',
command: generate_perl_command,
install: true,
install_dir: get_option('datadir') / 'perl5/Git/SVN',
install_dir: perllibdir / 'Git/SVN',
depends: [git_version_file],
)
2 changes: 1 addition & 1 deletion perl/Git/SVN/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ foreach source : [
output: source,
command: generate_perl_command,
install: true,
install_dir: get_option('datadir') / 'perl5/Git/SVN',
install_dir: perllibdir / 'Git/SVN',
depends: [git_version_file],
)
endforeach
Expand Down
2 changes: 1 addition & 1 deletion perl/Git/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ foreach source : [
output: source,
command: generate_perl_command,
install: true,
install_dir: get_option('datadir') / 'perl5/Git',
install_dir: perllibdir / 'Git',
depends: [git_version_file],
)
endforeach
Expand Down
2 changes: 1 addition & 1 deletion perl/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ test_dependencies += custom_target(
output: 'Git.pm',
command: generate_perl_command,
install: true,
install_dir: get_option('datadir') / 'perl5',
install_dir: perllibdir,
depends: [git_version_file],
)

Expand Down
Loading