Merge branch 'ds/maintenance-part-1'
A "git gc"'s big brother has been introduced to take care of more repository maintenance tasks, not limited to the object database cleaning. * ds/maintenance-part-1: maintenance: add trace2 regions for task execution maintenance: add auto condition for commit-graph task maintenance: use pointers to check --auto maintenance: create maintenance.<task>.enabled config maintenance: take a lock on the objects directory maintenance: add --task option maintenance: add commit-graph task maintenance: initialize task array maintenance: replace run_auto_gc() maintenance: add --quiet option maintenance: create basic maintenance runnermaint
commit
48794acc50
|
@ -90,6 +90,7 @@
|
||||||
/git-ls-tree
|
/git-ls-tree
|
||||||
/git-mailinfo
|
/git-mailinfo
|
||||||
/git-mailsplit
|
/git-mailsplit
|
||||||
|
/git-maintenance
|
||||||
/git-merge
|
/git-merge
|
||||||
/git-merge-base
|
/git-merge-base
|
||||||
/git-merge-index
|
/git-merge-index
|
||||||
|
|
|
@ -398,6 +398,8 @@ include::config/mailinfo.txt[]
|
||||||
|
|
||||||
include::config/mailmap.txt[]
|
include::config/mailmap.txt[]
|
||||||
|
|
||||||
|
include::config/maintenance.txt[]
|
||||||
|
|
||||||
include::config/man.txt[]
|
include::config/man.txt[]
|
||||||
|
|
||||||
include::config/merge.txt[]
|
include::config/merge.txt[]
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
maintenance.<task>.enabled::
|
||||||
|
This boolean config option controls whether the maintenance task
|
||||||
|
with name `<task>` is run when no `--task` option is specified to
|
||||||
|
`git maintenance run`. These config values are ignored if a
|
||||||
|
`--task` option exists. By default, only `maintenance.gc.enabled`
|
||||||
|
is true.
|
||||||
|
|
||||||
|
maintenance.commit-graph.auto::
|
||||||
|
This integer config option controls how often the `commit-graph` task
|
||||||
|
should be run as part of `git maintenance run --auto`. If zero, then
|
||||||
|
the `commit-graph` 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
|
||||||
|
reachable commits that are not in the commit-graph file is at least
|
||||||
|
the value of `maintenance.commit-graph.auto`. The default value is
|
||||||
|
100.
|
|
@ -95,9 +95,11 @@ ifndef::git-pull[]
|
||||||
Allow several <repository> and <group> arguments to be
|
Allow several <repository> and <group> arguments to be
|
||||||
specified. No <refspec>s may be specified.
|
specified. No <refspec>s may be specified.
|
||||||
|
|
||||||
|
--[no-]auto-maintenance::
|
||||||
--[no-]auto-gc::
|
--[no-]auto-gc::
|
||||||
Run `git gc --auto` at the end to perform garbage collection
|
Run `git maintenance run --auto` at the end to perform automatic
|
||||||
if needed. This is enabled by default.
|
repository maintenance if needed. (`--[no-]auto-gc` is a synonym.)
|
||||||
|
This is enabled by default.
|
||||||
|
|
||||||
--[no-]write-commit-graph::
|
--[no-]write-commit-graph::
|
||||||
Write a commit-graph after fetching. This overrides the config
|
Write a commit-graph after fetching. This overrides the config
|
||||||
|
|
|
@ -78,9 +78,9 @@ repository using this option and then delete branches (or use any
|
||||||
other Git command that makes any existing commit unreferenced) in the
|
other Git command that makes any existing commit unreferenced) in the
|
||||||
source repository, some objects may become unreferenced (or dangling).
|
source repository, some objects may become unreferenced (or dangling).
|
||||||
These objects may be removed by normal Git operations (such as `git commit`)
|
These objects may be removed by normal Git operations (such as `git commit`)
|
||||||
which automatically call `git gc --auto`. (See linkgit:git-gc[1].)
|
which automatically call `git maintenance run --auto`. (See
|
||||||
If these objects are removed and were referenced by the cloned repository,
|
linkgit:git-maintenance[1].) If these objects are removed and were referenced
|
||||||
then the cloned repository will become corrupt.
|
by the cloned repository, then the cloned repository will become corrupt.
|
||||||
+
|
+
|
||||||
Note that running `git repack` without the `--local` option in a repository
|
Note that running `git repack` without the `--local` option in a repository
|
||||||
cloned with `--shared` will copy objects from the source repository into a pack
|
cloned with `--shared` will copy objects from the source repository into a pack
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
git-maintenance(1)
|
||||||
|
==================
|
||||||
|
|
||||||
|
NAME
|
||||||
|
----
|
||||||
|
git-maintenance - Run tasks to optimize Git repository data
|
||||||
|
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
--------
|
||||||
|
[verse]
|
||||||
|
'git maintenance' run [<options>]
|
||||||
|
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
-----------
|
||||||
|
Run tasks to optimize Git repository data, speeding up other Git commands
|
||||||
|
and reducing storage requirements for the repository.
|
||||||
|
|
||||||
|
Git commands that add repository data, such as `git add` or `git fetch`,
|
||||||
|
are optimized for a responsive user experience. These commands do not take
|
||||||
|
time to optimize the Git data, since such optimizations scale with the full
|
||||||
|
size of the repository while these user commands each perform a relatively
|
||||||
|
small action.
|
||||||
|
|
||||||
|
The `git maintenance` command provides flexibility for how to optimize the
|
||||||
|
Git repository.
|
||||||
|
|
||||||
|
SUBCOMMANDS
|
||||||
|
-----------
|
||||||
|
|
||||||
|
run::
|
||||||
|
Run one or more maintenance tasks. If one or more `--task` options
|
||||||
|
are specified, then those tasks are run in that order. Otherwise,
|
||||||
|
the tasks are determined by which `maintenance.<task>.enabled`
|
||||||
|
config options are true. By default, only `maintenance.gc.enabled`
|
||||||
|
is true.
|
||||||
|
|
||||||
|
TASKS
|
||||||
|
-----
|
||||||
|
|
||||||
|
commit-graph::
|
||||||
|
The `commit-graph` job updates the `commit-graph` files incrementally,
|
||||||
|
then verifies that the written data is correct. The incremental
|
||||||
|
write is safe to run alongside concurrent Git processes since it
|
||||||
|
will not expire `.graph` files that were in the previous
|
||||||
|
`commit-graph-chain` file. They will be deleted by a later run based
|
||||||
|
on the expiration delay.
|
||||||
|
|
||||||
|
gc::
|
||||||
|
Clean up unnecessary files and optimize the local repository. "GC"
|
||||||
|
stands for "garbage collection," but this task performs many
|
||||||
|
smaller tasks. This task can be expensive for large repositories,
|
||||||
|
as it repacks all Git objects into a single pack-file. It can also
|
||||||
|
be disruptive in some situations, as it deletes stale data. See
|
||||||
|
linkgit:git-gc[1] for more details on garbage collection in Git.
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
-------
|
||||||
|
--auto::
|
||||||
|
When combined with the `run` subcommand, run maintenance tasks
|
||||||
|
only if certain thresholds are met. For example, the `gc` task
|
||||||
|
runs when the number of loose objects exceeds the number stored
|
||||||
|
in the `gc.auto` config setting, or when the number of pack-files
|
||||||
|
exceeds the `gc.autoPackLimit` config setting.
|
||||||
|
|
||||||
|
--quiet::
|
||||||
|
Do not report progress or other information over `stderr`.
|
||||||
|
|
||||||
|
--task=<task>::
|
||||||
|
If this option is specified one or more times, then only run the
|
||||||
|
specified tasks in the specified order. If no `--task=<task>`
|
||||||
|
arguments are specified, then only the tasks with
|
||||||
|
`maintenance.<task>.enabled` configured as `true` are considered.
|
||||||
|
See the 'TASKS' section for the list of accepted `<task>` values.
|
||||||
|
|
||||||
|
GIT
|
||||||
|
---
|
||||||
|
Part of the linkgit:git[1] suite
|
|
@ -172,6 +172,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix);
|
||||||
int cmd_ls_remote(int argc, const char **argv, const char *prefix);
|
int cmd_ls_remote(int argc, const char **argv, const char *prefix);
|
||||||
int cmd_mailinfo(int argc, const char **argv, const char *prefix);
|
int cmd_mailinfo(int argc, const char **argv, const char *prefix);
|
||||||
int cmd_mailsplit(int argc, const char **argv, const char *prefix);
|
int cmd_mailsplit(int argc, const char **argv, const char *prefix);
|
||||||
|
int cmd_maintenance(int argc, const char **argv, const char *prefix);
|
||||||
int cmd_merge(int argc, const char **argv, const char *prefix);
|
int cmd_merge(int argc, const char **argv, const char *prefix);
|
||||||
int cmd_merge_base(int argc, const char **argv, const char *prefix);
|
int cmd_merge_base(int argc, const char **argv, const char *prefix);
|
||||||
int cmd_merge_index(int argc, const char **argv, const char *prefix);
|
int cmd_merge_index(int argc, const char **argv, const char *prefix);
|
||||||
|
|
|
@ -1813,7 +1813,7 @@ next:
|
||||||
if (!state->rebasing) {
|
if (!state->rebasing) {
|
||||||
am_destroy(state);
|
am_destroy(state);
|
||||||
close_object_store(the_repository->objects);
|
close_object_store(the_repository->objects);
|
||||||
run_auto_gc(state->quiet);
|
run_auto_maintenance(state->quiet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1700,7 +1700,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
|
||||||
git_test_write_commit_graph_or_die();
|
git_test_write_commit_graph_or_die();
|
||||||
|
|
||||||
repo_rerere(the_repository, 0);
|
repo_rerere(the_repository, 0);
|
||||||
run_auto_gc(quiet);
|
run_auto_maintenance(quiet);
|
||||||
run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
|
run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
|
||||||
if (amend && !no_post_rewrite) {
|
if (amend && !no_post_rewrite) {
|
||||||
commit_post_rewrite(the_repository, current_head, &oid);
|
commit_post_rewrite(the_repository, current_head, &oid);
|
||||||
|
|
|
@ -200,8 +200,10 @@ static struct option builtin_fetch_options[] = {
|
||||||
OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
|
OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
|
||||||
N_("report that we have only objects reachable from this object")),
|
N_("report that we have only objects reachable from this object")),
|
||||||
OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
|
OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
|
||||||
|
OPT_BOOL(0, "auto-maintenance", &enable_auto_gc,
|
||||||
|
N_("run 'maintenance --auto' after fetching")),
|
||||||
OPT_BOOL(0, "auto-gc", &enable_auto_gc,
|
OPT_BOOL(0, "auto-gc", &enable_auto_gc,
|
||||||
N_("run 'gc --auto' after fetching")),
|
N_("run 'maintenance --auto' after fetching")),
|
||||||
OPT_BOOL(0, "show-forced-updates", &fetch_show_forced_updates,
|
OPT_BOOL(0, "show-forced-updates", &fetch_show_forced_updates,
|
||||||
N_("check for forced-updates on all updated branches")),
|
N_("check for forced-updates on all updated branches")),
|
||||||
OPT_BOOL(0, "write-commit-graph", &fetch_write_commit_graph,
|
OPT_BOOL(0, "write-commit-graph", &fetch_write_commit_graph,
|
||||||
|
@ -1925,7 +1927,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
|
||||||
close_object_store(the_repository->objects);
|
close_object_store(the_repository->objects);
|
||||||
|
|
||||||
if (enable_auto_gc)
|
if (enable_auto_gc)
|
||||||
run_auto_gc(verbosity < 0);
|
run_auto_maintenance(verbosity < 0);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
337
builtin/gc.c
337
builtin/gc.c
|
@ -28,6 +28,7 @@
|
||||||
#include "blob.h"
|
#include "blob.h"
|
||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
#include "promisor-remote.h"
|
#include "promisor-remote.h"
|
||||||
|
#include "refs.h"
|
||||||
|
|
||||||
#define FAILED_RUN "failed to run %s"
|
#define FAILED_RUN "failed to run %s"
|
||||||
|
|
||||||
|
@ -699,3 +700,339 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char * const builtin_maintenance_run_usage[] = {
|
||||||
|
N_("git maintenance run [--auto] [--[no-]quiet] [--task=<task>]"),
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
struct maintenance_run_opts {
|
||||||
|
int auto_flag;
|
||||||
|
int quiet;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Remember to update object flag allocation in object.h */
|
||||||
|
#define SEEN (1u<<0)
|
||||||
|
|
||||||
|
struct cg_auto_data {
|
||||||
|
int num_not_in_graph;
|
||||||
|
int limit;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int dfs_on_ref(const char *refname,
|
||||||
|
const struct object_id *oid, int flags,
|
||||||
|
void *cb_data)
|
||||||
|
{
|
||||||
|
struct cg_auto_data *data = (struct cg_auto_data *)cb_data;
|
||||||
|
int result = 0;
|
||||||
|
struct object_id peeled;
|
||||||
|
struct commit_list *stack = NULL;
|
||||||
|
struct commit *commit;
|
||||||
|
|
||||||
|
if (!peel_ref(refname, &peeled))
|
||||||
|
oid = &peeled;
|
||||||
|
if (oid_object_info(the_repository, oid, NULL) != OBJ_COMMIT)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
commit = lookup_commit(the_repository, oid);
|
||||||
|
if (!commit)
|
||||||
|
return 0;
|
||||||
|
if (parse_commit(commit))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
commit_list_append(commit, &stack);
|
||||||
|
|
||||||
|
while (!result && stack) {
|
||||||
|
struct commit_list *parent;
|
||||||
|
|
||||||
|
commit = pop_commit(&stack);
|
||||||
|
|
||||||
|
for (parent = commit->parents; parent; parent = parent->next) {
|
||||||
|
if (parse_commit(parent->item) ||
|
||||||
|
commit_graph_position(parent->item) != COMMIT_NOT_FROM_GRAPH ||
|
||||||
|
parent->item->object.flags & SEEN)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
parent->item->object.flags |= SEEN;
|
||||||
|
data->num_not_in_graph++;
|
||||||
|
|
||||||
|
if (data->num_not_in_graph >= data->limit) {
|
||||||
|
result = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
commit_list_append(parent->item, &stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free_commit_list(stack);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int should_write_commit_graph(void)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
struct cg_auto_data data;
|
||||||
|
|
||||||
|
data.num_not_in_graph = 0;
|
||||||
|
data.limit = 100;
|
||||||
|
git_config_get_int("maintenance.commit-graph.auto",
|
||||||
|
&data.limit);
|
||||||
|
|
||||||
|
if (!data.limit)
|
||||||
|
return 0;
|
||||||
|
if (data.limit < 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
result = for_each_ref(dfs_on_ref, &data);
|
||||||
|
|
||||||
|
clear_commit_marks_all(SEEN);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int run_write_commit_graph(struct maintenance_run_opts *opts)
|
||||||
|
{
|
||||||
|
struct child_process child = CHILD_PROCESS_INIT;
|
||||||
|
|
||||||
|
child.git_cmd = 1;
|
||||||
|
strvec_pushl(&child.args, "commit-graph", "write",
|
||||||
|
"--split", "--reachable", NULL);
|
||||||
|
|
||||||
|
if (opts->quiet)
|
||||||
|
strvec_push(&child.args, "--no-progress");
|
||||||
|
|
||||||
|
return !!run_command(&child);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int maintenance_task_commit_graph(struct maintenance_run_opts *opts)
|
||||||
|
{
|
||||||
|
close_object_store(the_repository->objects);
|
||||||
|
if (run_write_commit_graph(opts)) {
|
||||||
|
error(_("failed to write commit-graph"));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int maintenance_task_gc(struct maintenance_run_opts *opts)
|
||||||
|
{
|
||||||
|
struct child_process child = CHILD_PROCESS_INIT;
|
||||||
|
|
||||||
|
child.git_cmd = 1;
|
||||||
|
strvec_push(&child.args, "gc");
|
||||||
|
|
||||||
|
if (opts->auto_flag)
|
||||||
|
strvec_push(&child.args, "--auto");
|
||||||
|
if (opts->quiet)
|
||||||
|
strvec_push(&child.args, "--quiet");
|
||||||
|
else
|
||||||
|
strvec_push(&child.args, "--no-quiet");
|
||||||
|
|
||||||
|
close_object_store(the_repository->objects);
|
||||||
|
return run_command(&child);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int maintenance_task_fn(struct maintenance_run_opts *opts);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An auto condition function returns 1 if the task should run
|
||||||
|
* and 0 if the task should NOT run. See needs_to_gc() for an
|
||||||
|
* example.
|
||||||
|
*/
|
||||||
|
typedef int maintenance_auto_fn(void);
|
||||||
|
|
||||||
|
struct maintenance_task {
|
||||||
|
const char *name;
|
||||||
|
maintenance_task_fn *fn;
|
||||||
|
maintenance_auto_fn *auto_condition;
|
||||||
|
unsigned enabled:1;
|
||||||
|
|
||||||
|
/* -1 if not selected. */
|
||||||
|
int selected_order;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum maintenance_task_label {
|
||||||
|
TASK_GC,
|
||||||
|
TASK_COMMIT_GRAPH,
|
||||||
|
|
||||||
|
/* Leave as final value */
|
||||||
|
TASK__COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct maintenance_task tasks[] = {
|
||||||
|
[TASK_GC] = {
|
||||||
|
"gc",
|
||||||
|
maintenance_task_gc,
|
||||||
|
need_to_gc,
|
||||||
|
1,
|
||||||
|
},
|
||||||
|
[TASK_COMMIT_GRAPH] = {
|
||||||
|
"commit-graph",
|
||||||
|
maintenance_task_commit_graph,
|
||||||
|
should_write_commit_graph,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int compare_tasks_by_selection(const void *a_, const void *b_)
|
||||||
|
{
|
||||||
|
const struct maintenance_task *a, *b;
|
||||||
|
|
||||||
|
a = (const struct maintenance_task *)&a_;
|
||||||
|
b = (const struct maintenance_task *)&b_;
|
||||||
|
|
||||||
|
return b->selected_order - a->selected_order;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int maintenance_run_tasks(struct maintenance_run_opts *opts)
|
||||||
|
{
|
||||||
|
int i, found_selected = 0;
|
||||||
|
int result = 0;
|
||||||
|
struct lock_file lk;
|
||||||
|
struct repository *r = the_repository;
|
||||||
|
char *lock_path = xstrfmt("%s/maintenance", r->objects->odb->path);
|
||||||
|
|
||||||
|
if (hold_lock_file_for_update(&lk, lock_path, LOCK_NO_DEREF) < 0) {
|
||||||
|
/*
|
||||||
|
* Another maintenance command is running.
|
||||||
|
*
|
||||||
|
* If --auto was provided, then it is likely due to a
|
||||||
|
* recursive process stack. Do not report an error in
|
||||||
|
* that case.
|
||||||
|
*/
|
||||||
|
if (!opts->auto_flag && !opts->quiet)
|
||||||
|
warning(_("lock file '%s' exists, skipping maintenance"),
|
||||||
|
lock_path);
|
||||||
|
free(lock_path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
free(lock_path);
|
||||||
|
|
||||||
|
for (i = 0; !found_selected && i < TASK__COUNT; i++)
|
||||||
|
found_selected = tasks[i].selected_order >= 0;
|
||||||
|
|
||||||
|
if (found_selected)
|
||||||
|
QSORT(tasks, TASK__COUNT, compare_tasks_by_selection);
|
||||||
|
|
||||||
|
for (i = 0; i < TASK__COUNT; i++) {
|
||||||
|
if (found_selected && tasks[i].selected_order < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!found_selected && !tasks[i].enabled)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (opts->auto_flag &&
|
||||||
|
(!tasks[i].auto_condition ||
|
||||||
|
!tasks[i].auto_condition()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
trace2_region_enter("maintenance", tasks[i].name, r);
|
||||||
|
if (tasks[i].fn(opts)) {
|
||||||
|
error(_("task '%s' failed"), tasks[i].name);
|
||||||
|
result = 1;
|
||||||
|
}
|
||||||
|
trace2_region_leave("maintenance", tasks[i].name, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
rollback_lock_file(&lk);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initialize_task_config(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct strbuf config_name = STRBUF_INIT;
|
||||||
|
gc_config();
|
||||||
|
|
||||||
|
for (i = 0; i < TASK__COUNT; i++) {
|
||||||
|
int config_value;
|
||||||
|
|
||||||
|
strbuf_setlen(&config_name, 0);
|
||||||
|
strbuf_addf(&config_name, "maintenance.%s.enabled",
|
||||||
|
tasks[i].name);
|
||||||
|
|
||||||
|
if (!git_config_get_bool(config_name.buf, &config_value))
|
||||||
|
tasks[i].enabled = config_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
strbuf_release(&config_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int task_option_parse(const struct option *opt,
|
||||||
|
const char *arg, int unset)
|
||||||
|
{
|
||||||
|
int i, num_selected = 0;
|
||||||
|
struct maintenance_task *task = NULL;
|
||||||
|
|
||||||
|
BUG_ON_OPT_NEG(unset);
|
||||||
|
|
||||||
|
for (i = 0; i < TASK__COUNT; i++) {
|
||||||
|
if (tasks[i].selected_order >= 0)
|
||||||
|
num_selected++;
|
||||||
|
if (!strcasecmp(tasks[i].name, arg)) {
|
||||||
|
task = &tasks[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!task) {
|
||||||
|
error(_("'%s' is not a valid task"), arg);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task->selected_order >= 0) {
|
||||||
|
error(_("task '%s' cannot be selected multiple times"), arg);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
task->selected_order = num_selected + 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int maintenance_run(int argc, const char **argv, const char *prefix)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct maintenance_run_opts opts;
|
||||||
|
struct option builtin_maintenance_run_options[] = {
|
||||||
|
OPT_BOOL(0, "auto", &opts.auto_flag,
|
||||||
|
N_("run tasks based on the state of the repository")),
|
||||||
|
OPT_BOOL(0, "quiet", &opts.quiet,
|
||||||
|
N_("do not report progress or other information over stderr")),
|
||||||
|
OPT_CALLBACK_F(0, "task", NULL, N_("task"),
|
||||||
|
N_("run a specific task"),
|
||||||
|
PARSE_OPT_NONEG, task_option_parse),
|
||||||
|
OPT_END()
|
||||||
|
};
|
||||||
|
memset(&opts, 0, sizeof(opts));
|
||||||
|
|
||||||
|
opts.quiet = !isatty(2);
|
||||||
|
initialize_task_config();
|
||||||
|
|
||||||
|
for (i = 0; i < TASK__COUNT; i++)
|
||||||
|
tasks[i].selected_order = -1;
|
||||||
|
|
||||||
|
argc = parse_options(argc, argv, prefix,
|
||||||
|
builtin_maintenance_run_options,
|
||||||
|
builtin_maintenance_run_usage,
|
||||||
|
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||||
|
|
||||||
|
if (argc != 0)
|
||||||
|
usage_with_options(builtin_maintenance_run_usage,
|
||||||
|
builtin_maintenance_run_options);
|
||||||
|
return maintenance_run_tasks(&opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char builtin_maintenance_usage[] = N_("git maintenance run [<options>]");
|
||||||
|
|
||||||
|
int cmd_maintenance(int argc, const char **argv, const char *prefix)
|
||||||
|
{
|
||||||
|
if (argc < 2 ||
|
||||||
|
(argc == 2 && !strcmp(argv[1], "-h")))
|
||||||
|
usage(builtin_maintenance_usage);
|
||||||
|
|
||||||
|
if (!strcmp(argv[1], "run"))
|
||||||
|
return maintenance_run(argc - 1, argv + 1, prefix);
|
||||||
|
|
||||||
|
die(_("invalid subcommand: %s"), argv[1]);
|
||||||
|
}
|
||||||
|
|
|
@ -456,7 +456,7 @@ static void finish(struct commit *head_commit,
|
||||||
* user should see them.
|
* user should see them.
|
||||||
*/
|
*/
|
||||||
close_object_store(the_repository->objects);
|
close_object_store(the_repository->objects);
|
||||||
run_auto_gc(verbosity < 0);
|
run_auto_maintenance(verbosity < 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (new_head && show_diffstat) {
|
if (new_head && show_diffstat) {
|
||||||
|
|
|
@ -734,10 +734,10 @@ static int finish_rebase(struct rebase_options *opts)
|
||||||
apply_autostash(state_dir_path("autostash", opts));
|
apply_autostash(state_dir_path("autostash", opts));
|
||||||
close_object_store(the_repository->objects);
|
close_object_store(the_repository->objects);
|
||||||
/*
|
/*
|
||||||
* We ignore errors in 'gc --auto', since the
|
* We ignore errors in 'git maintenance run --auto', since the
|
||||||
* user should see them.
|
* user should see them.
|
||||||
*/
|
*/
|
||||||
run_auto_gc(!(opts->flags & (REBASE_NO_QUIET|REBASE_VERBOSE)));
|
run_auto_maintenance(!(opts->flags & (REBASE_NO_QUIET|REBASE_VERBOSE)));
|
||||||
if (opts->type == REBASE_MERGE) {
|
if (opts->type == REBASE_MERGE) {
|
||||||
struct replay_opts replay = REPLAY_OPTS_INIT;
|
struct replay_opts replay = REPLAY_OPTS_INIT;
|
||||||
|
|
||||||
|
|
|
@ -117,6 +117,7 @@ git-ls-remote plumbinginterrogators
|
||||||
git-ls-tree plumbinginterrogators
|
git-ls-tree plumbinginterrogators
|
||||||
git-mailinfo purehelpers
|
git-mailinfo purehelpers
|
||||||
git-mailsplit purehelpers
|
git-mailsplit purehelpers
|
||||||
|
git-maintenance mainporcelain
|
||||||
git-merge mainporcelain history
|
git-merge mainporcelain history
|
||||||
git-merge-base plumbinginterrogators
|
git-merge-base plumbinginterrogators
|
||||||
git-merge-file plumbingmanipulators
|
git-merge-file plumbingmanipulators
|
||||||
|
|
|
@ -172,7 +172,7 @@ static char *get_split_graph_filename(struct object_directory *odb,
|
||||||
oid_hex);
|
oid_hex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *get_chain_filename(struct object_directory *odb)
|
char *get_commit_graph_chain_filename(struct object_directory *odb)
|
||||||
{
|
{
|
||||||
return xstrfmt("%s/info/commit-graphs/commit-graph-chain", odb->path);
|
return xstrfmt("%s/info/commit-graphs/commit-graph-chain", odb->path);
|
||||||
}
|
}
|
||||||
|
@ -523,7 +523,7 @@ static struct commit_graph *load_commit_graph_chain(struct repository *r,
|
||||||
struct stat st;
|
struct stat st;
|
||||||
struct object_id *oids;
|
struct object_id *oids;
|
||||||
int i = 0, valid = 1, count;
|
int i = 0, valid = 1, count;
|
||||||
char *chain_name = get_chain_filename(odb);
|
char *chain_name = get_commit_graph_chain_filename(odb);
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
int stat_res;
|
int stat_res;
|
||||||
|
|
||||||
|
@ -1675,7 +1675,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->split) {
|
if (ctx->split) {
|
||||||
char *lock_name = get_chain_filename(ctx->odb);
|
char *lock_name = get_commit_graph_chain_filename(ctx->odb);
|
||||||
|
|
||||||
hold_lock_file_for_update_mode(&lk, lock_name,
|
hold_lock_file_for_update_mode(&lk, lock_name,
|
||||||
LOCK_DIE_ON_ERROR, 0444);
|
LOCK_DIE_ON_ERROR, 0444);
|
||||||
|
@ -2045,7 +2045,7 @@ static void expire_commit_graphs(struct write_commit_graph_context *ctx)
|
||||||
if (ctx->split_opts && ctx->split_opts->expire_time)
|
if (ctx->split_opts && ctx->split_opts->expire_time)
|
||||||
expire_time = ctx->split_opts->expire_time;
|
expire_time = ctx->split_opts->expire_time;
|
||||||
if (!ctx->split) {
|
if (!ctx->split) {
|
||||||
char *chain_file_name = get_chain_filename(ctx->odb);
|
char *chain_file_name = get_commit_graph_chain_filename(ctx->odb);
|
||||||
unlink(chain_file_name);
|
unlink(chain_file_name);
|
||||||
free(chain_file_name);
|
free(chain_file_name);
|
||||||
ctx->num_commit_graphs_after = 0;
|
ctx->num_commit_graphs_after = 0;
|
||||||
|
|
|
@ -25,6 +25,7 @@ struct raw_object_store;
|
||||||
struct string_list;
|
struct string_list;
|
||||||
|
|
||||||
char *get_commit_graph_filename(struct object_directory *odb);
|
char *get_commit_graph_filename(struct object_directory *odb);
|
||||||
|
char *get_commit_graph_chain_filename(struct object_directory *odb);
|
||||||
int open_commit_graph(const char *graph_file, int *fd, struct stat *st);
|
int open_commit_graph(const char *graph_file, int *fd, struct stat *st);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
1
git.c
1
git.c
|
@ -534,6 +534,7 @@ static struct cmd_struct commands[] = {
|
||||||
{ "ls-tree", cmd_ls_tree, RUN_SETUP },
|
{ "ls-tree", cmd_ls_tree, RUN_SETUP },
|
||||||
{ "mailinfo", cmd_mailinfo, RUN_SETUP_GENTLY | NO_PARSEOPT },
|
{ "mailinfo", cmd_mailinfo, RUN_SETUP_GENTLY | NO_PARSEOPT },
|
||||||
{ "mailsplit", cmd_mailsplit, NO_PARSEOPT },
|
{ "mailsplit", cmd_mailsplit, NO_PARSEOPT },
|
||||||
|
{ "maintenance", cmd_maintenance, RUN_SETUP_GENTLY | NO_PARSEOPT },
|
||||||
{ "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
|
{ "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
|
||||||
{ "merge-base", cmd_merge_base, RUN_SETUP },
|
{ "merge-base", cmd_merge_base, RUN_SETUP },
|
||||||
{ "merge-file", cmd_merge_file, RUN_SETUP_GENTLY },
|
{ "merge-file", cmd_merge_file, RUN_SETUP_GENTLY },
|
||||||
|
|
1
object.h
1
object.h
|
@ -73,6 +73,7 @@ struct object_array {
|
||||||
* sha1-name.c: 20
|
* sha1-name.c: 20
|
||||||
* list-objects-filter.c: 21
|
* list-objects-filter.c: 21
|
||||||
* builtin/fsck.c: 0--3
|
* builtin/fsck.c: 0--3
|
||||||
|
* builtin/gc.c: 0
|
||||||
* builtin/index-pack.c: 2021
|
* builtin/index-pack.c: 2021
|
||||||
* builtin/pack-objects.c: 20
|
* builtin/pack-objects.c: 20
|
||||||
* builtin/reflog.c: 10--12
|
* builtin/reflog.c: 10--12
|
||||||
|
|
|
@ -1866,15 +1866,13 @@ int run_processes_parallel_tr2(int n, get_next_task_fn get_next_task,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int run_auto_gc(int quiet)
|
int run_auto_maintenance(int quiet)
|
||||||
{
|
{
|
||||||
struct strvec argv_gc_auto = STRVEC_INIT;
|
struct child_process maint = CHILD_PROCESS_INIT;
|
||||||
int status;
|
|
||||||
|
|
||||||
strvec_pushl(&argv_gc_auto, "gc", "--auto", NULL);
|
maint.git_cmd = 1;
|
||||||
if (quiet)
|
strvec_pushl(&maint.args, "maintenance", "run", "--auto", NULL);
|
||||||
strvec_push(&argv_gc_auto, "--quiet");
|
strvec_push(&maint.args, quiet ? "--quiet" : "--no-quiet");
|
||||||
status = run_command_v_opt(argv_gc_auto.v, RUN_GIT_CMD);
|
|
||||||
strvec_clear(&argv_gc_auto);
|
return run_command(&maint);
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,7 +221,7 @@ int run_hook_ve(const char *const *env, const char *name, va_list args);
|
||||||
/*
|
/*
|
||||||
* Trigger an auto-gc
|
* Trigger an auto-gc
|
||||||
*/
|
*/
|
||||||
int run_auto_gc(int quiet);
|
int run_auto_maintenance(int quiet);
|
||||||
|
|
||||||
#define RUN_COMMAND_NO_STDIN 1
|
#define RUN_COMMAND_NO_STDIN 1
|
||||||
#define RUN_GIT_CMD 2 /*If this is to be git sub-command */
|
#define RUN_GIT_CMD 2 /*If this is to be git sub-command */
|
||||||
|
|
|
@ -936,7 +936,7 @@ test_expect_success 'fetching with auto-gc does not lock up' '
|
||||||
git config fetch.unpackLimit 1 &&
|
git config fetch.unpackLimit 1 &&
|
||||||
git config gc.autoPackLimit 1 &&
|
git config gc.autoPackLimit 1 &&
|
||||||
git config gc.autoDetach false &&
|
git config gc.autoDetach false &&
|
||||||
GIT_ASK_YESNO="$D/askyesno" git fetch >fetch.out 2>&1 &&
|
GIT_ASK_YESNO="$D/askyesno" git fetch --verbose >fetch.out 2>&1 &&
|
||||||
test_i18ngrep "Auto packing the repository" fetch.out &&
|
test_i18ngrep "Auto packing the repository" fetch.out &&
|
||||||
! grep "Should I try again" fetch.out
|
! grep "Should I try again" fetch.out
|
||||||
)
|
)
|
||||||
|
|
|
@ -108,7 +108,7 @@ test_expect_success 'git fetch --multiple (two remotes)' '
|
||||||
GIT_TRACE=1 git fetch --multiple one two 2>trace &&
|
GIT_TRACE=1 git fetch --multiple one two 2>trace &&
|
||||||
git branch -r > output &&
|
git branch -r > output &&
|
||||||
test_cmp ../expect output &&
|
test_cmp ../expect output &&
|
||||||
grep "built-in: git gc" trace >gc &&
|
grep "built-in: git maintenance" trace >gc &&
|
||||||
test_line_count = 1 gc
|
test_line_count = 1 gc
|
||||||
)
|
)
|
||||||
'
|
'
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
test_description='git maintenance builtin'
|
||||||
|
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
GIT_TEST_COMMIT_GRAPH=0
|
||||||
|
|
||||||
|
test_expect_success 'help text' '
|
||||||
|
test_expect_code 129 git maintenance -h 2>err &&
|
||||||
|
test_i18ngrep "usage: git maintenance run" err &&
|
||||||
|
test_expect_code 128 git maintenance barf 2>err &&
|
||||||
|
test_i18ngrep "invalid subcommand: barf" err &&
|
||||||
|
test_expect_code 129 git maintenance 2>err &&
|
||||||
|
test_i18ngrep "usage: git maintenance" err
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'run [--auto|--quiet]' '
|
||||||
|
GIT_TRACE2_EVENT="$(pwd)/run-no-auto.txt" \
|
||||||
|
git maintenance run 2>/dev/null &&
|
||||||
|
GIT_TRACE2_EVENT="$(pwd)/run-auto.txt" \
|
||||||
|
git maintenance run --auto 2>/dev/null &&
|
||||||
|
GIT_TRACE2_EVENT="$(pwd)/run-no-quiet.txt" \
|
||||||
|
git maintenance run --no-quiet 2>/dev/null &&
|
||||||
|
test_subcommand git gc --quiet <run-no-auto.txt &&
|
||||||
|
test_subcommand ! git gc --auto --quiet <run-auto.txt &&
|
||||||
|
test_subcommand git gc --no-quiet <run-no-quiet.txt
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'maintenance.<task>.enabled' '
|
||||||
|
git config maintenance.gc.enabled false &&
|
||||||
|
git config maintenance.commit-graph.enabled true &&
|
||||||
|
GIT_TRACE2_EVENT="$(pwd)/run-config.txt" git maintenance run 2>err &&
|
||||||
|
test_subcommand ! git gc --quiet <run-config.txt &&
|
||||||
|
test_subcommand git commit-graph write --split --reachable --no-progress <run-config.txt
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'run --task=<task>' '
|
||||||
|
GIT_TRACE2_EVENT="$(pwd)/run-commit-graph.txt" \
|
||||||
|
git maintenance run --task=commit-graph 2>/dev/null &&
|
||||||
|
GIT_TRACE2_EVENT="$(pwd)/run-gc.txt" \
|
||||||
|
git maintenance run --task=gc 2>/dev/null &&
|
||||||
|
GIT_TRACE2_EVENT="$(pwd)/run-commit-graph.txt" \
|
||||||
|
git maintenance run --task=commit-graph 2>/dev/null &&
|
||||||
|
GIT_TRACE2_EVENT="$(pwd)/run-both.txt" \
|
||||||
|
git maintenance run --task=commit-graph --task=gc 2>/dev/null &&
|
||||||
|
test_subcommand ! git gc --quiet <run-commit-graph.txt &&
|
||||||
|
test_subcommand git gc --quiet <run-gc.txt &&
|
||||||
|
test_subcommand git gc --quiet <run-both.txt &&
|
||||||
|
test_subcommand git commit-graph write --split --reachable --no-progress <run-commit-graph.txt &&
|
||||||
|
test_subcommand ! git commit-graph write --split --reachable --no-progress <run-gc.txt &&
|
||||||
|
test_subcommand git commit-graph write --split --reachable --no-progress <run-both.txt
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'run --task=bogus' '
|
||||||
|
test_must_fail git maintenance run --task=bogus 2>err &&
|
||||||
|
test_i18ngrep "is not a valid task" err
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'run --task duplicate' '
|
||||||
|
test_must_fail git maintenance run --task=gc --task=gc 2>err &&
|
||||||
|
test_i18ngrep "cannot be selected multiple times" err
|
||||||
|
'
|
||||||
|
|
||||||
|
test_done
|
|
@ -1628,3 +1628,36 @@ test_path_is_hidden () {
|
||||||
case "$("$SYSTEMROOT"/system32/attrib "$1")" in *H*?:*) return 0;; esac
|
case "$("$SYSTEMROOT"/system32/attrib "$1")" in *H*?:*) return 0;; esac
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Check that the given command was invoked as part of the
|
||||||
|
# trace2-format trace on stdin.
|
||||||
|
#
|
||||||
|
# test_subcommand [!] <command> <args>... < <trace>
|
||||||
|
#
|
||||||
|
# For example, to look for an invocation of "git upload-pack
|
||||||
|
# /path/to/repo"
|
||||||
|
#
|
||||||
|
# GIT_TRACE2_EVENT=event.log git fetch ... &&
|
||||||
|
# test_subcommand git upload-pack "$PATH" <event.log
|
||||||
|
#
|
||||||
|
# If the first parameter passed is !, this instead checks that
|
||||||
|
# the given command was not called.
|
||||||
|
#
|
||||||
|
test_subcommand () {
|
||||||
|
local negate=
|
||||||
|
if test "$1" = "!"
|
||||||
|
then
|
||||||
|
negate=t
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
|
||||||
|
local expr=$(printf '"%s",' "$@")
|
||||||
|
expr="${expr%,}"
|
||||||
|
|
||||||
|
if test -n "$negate"
|
||||||
|
then
|
||||||
|
! grep "\[$expr\]"
|
||||||
|
else
|
||||||
|
grep "\[$expr\]"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue