Skip to content

Commit 91b34c4

Browse files
pks-tgitster
authored andcommitted
rerere: provide function to collect stale entries
We're about to add another task for git-maintenance(1) that prunes stale rerere entries via `git rerere gc`. The condition of when to run this subcommand will be configurable so that the subcommand is only executed when a certain number of stale rerere entries exists. This requires us to know about the number of stale rerere entries in the first place, which is non-trivial to figure out. Refactor `rerere_gc()` and `prune_one()` so that garbage collection is split into three phases: 1. We collect any stale rerere entries and directories that are about to become empty. 2. Prune all stale rerere entries. 3. Remove all directories that should have become empty in (2). By splitting out the collection of stale entries we can trivially expose this function to external callers and thus reuse it in later steps. This refactoring is not expected to result in a user-visible change in behaviour. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent eae6763 commit 91b34c4

File tree

2 files changed

+78
-28
lines changed

2 files changed

+78
-28
lines changed

rerere.c

Lines changed: 64 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,8 +1203,8 @@ static void unlink_rr_item(struct rerere_id *id)
12031203
strbuf_release(&buf);
12041204
}
12051205

1206-
static void prune_one(struct rerere_id *id,
1207-
timestamp_t cutoff_resolve, timestamp_t cutoff_noresolve)
1206+
static int is_stale(struct rerere_id *id,
1207+
timestamp_t cutoff_resolve, timestamp_t cutoff_noresolve)
12081208
{
12091209
timestamp_t then;
12101210
timestamp_t cutoff;
@@ -1215,11 +1215,11 @@ static void prune_one(struct rerere_id *id,
12151215
else {
12161216
then = rerere_created_at(id);
12171217
if (!then)
1218-
return;
1218+
return 0;
12191219
cutoff = cutoff_noresolve;
12201220
}
1221-
if (then < cutoff)
1222-
unlink_rr_item(id);
1221+
1222+
return then < cutoff;
12231223
}
12241224

12251225
/* Does the basename in "path" look plausibly like an rr-cache entry? */
@@ -1230,29 +1230,35 @@ static int is_rr_cache_dirname(const char *path)
12301230
return !parse_oid_hex(path, &oid, &end) && !*end;
12311231
}
12321232

1233-
void rerere_gc(struct repository *r, struct string_list *rr)
1233+
int rerere_collect_stale_entries(struct repository *r,
1234+
struct string_list *prunable_dirs,
1235+
struct rerere_id **prunable_entries,
1236+
size_t *prunable_entries_nr)
12341237
{
1235-
struct string_list to_remove = STRING_LIST_INIT_DUP;
1236-
DIR *dir;
1237-
struct dirent *e;
1238-
int i;
12391238
timestamp_t now = time(NULL);
12401239
timestamp_t cutoff_noresolve = now - 15 * 86400;
12411240
timestamp_t cutoff_resolve = now - 60 * 86400;
12421241
struct strbuf buf = STRBUF_INIT;
1242+
size_t prunable_entries_alloc;
1243+
struct dirent *e;
1244+
DIR *dir = NULL;
1245+
int ret;
12431246

1244-
if (setup_rerere(r, rr, 0) < 0)
1245-
return;
1247+
*prunable_entries = NULL;
1248+
*prunable_entries_nr = 0;
1249+
prunable_entries_alloc = 0;
12461250

1247-
repo_config_get_expiry_in_days(the_repository, "gc.rerereresolved",
1251+
repo_config_get_expiry_in_days(r, "gc.rerereresolved",
12481252
&cutoff_resolve, now);
1249-
repo_config_get_expiry_in_days(the_repository, "gc.rerereunresolved",
1253+
repo_config_get_expiry_in_days(r, "gc.rerereunresolved",
12501254
&cutoff_noresolve, now);
1251-
git_config(git_default_config, NULL);
1252-
dir = opendir(repo_git_path_replace(the_repository, &buf, "rr-cache"));
1253-
if (!dir)
1254-
die_errno(_("unable to open rr-cache directory"));
1255-
/* Collect stale conflict IDs ... */
1255+
1256+
dir = opendir(repo_git_path_replace(r, &buf, "rr-cache"));
1257+
if (!dir) {
1258+
ret = error_errno(_("unable to open rr-cache directory"));
1259+
goto out;
1260+
}
1261+
12561262
while ((e = readdir_skip_dot_and_dotdot(dir))) {
12571263
struct rerere_dir *rr_dir;
12581264
struct rerere_id id;
@@ -1267,23 +1273,53 @@ void rerere_gc(struct repository *r, struct string_list *rr)
12671273
for (id.variant = 0, id.collection = rr_dir;
12681274
id.variant < id.collection->status_nr;
12691275
id.variant++) {
1270-
prune_one(&id, cutoff_resolve, cutoff_noresolve);
1271-
if (id.collection->status[id.variant])
1276+
if (is_stale(&id, cutoff_resolve, cutoff_noresolve)) {
1277+
ALLOC_GROW(*prunable_entries, *prunable_entries_nr + 1,
1278+
prunable_entries_alloc);
1279+
(*prunable_entries)[(*prunable_entries_nr)++] = id;
1280+
} else {
12721281
now_empty = 0;
1282+
}
12731283
}
12741284
if (now_empty)
1275-
string_list_append(&to_remove, e->d_name);
1285+
string_list_append(prunable_dirs, e->d_name);
12761286
}
1277-
closedir(dir);
12781287

1279-
/* ... and then remove the empty directories */
1280-
for (i = 0; i < to_remove.nr; i++)
1281-
rmdir(repo_git_path_replace(the_repository, &buf,
1282-
"rr-cache/%s", to_remove.items[i].string));
1288+
ret = 0;
1289+
1290+
out:
1291+
strbuf_release(&buf);
1292+
if (dir)
1293+
closedir(dir);
1294+
return ret;
1295+
}
1296+
1297+
void rerere_gc(struct repository *r, struct string_list *rr)
1298+
{
1299+
struct string_list prunable_dirs = STRING_LIST_INIT_DUP;
1300+
struct rerere_id *prunable_entries;
1301+
struct strbuf buf = STRBUF_INIT;
1302+
size_t prunable_entries_nr;
1303+
1304+
if (setup_rerere(r, rr, 0) < 0)
1305+
return;
1306+
1307+
git_config(git_default_config, NULL);
1308+
1309+
if (rerere_collect_stale_entries(r, &prunable_dirs, &prunable_entries,
1310+
&prunable_entries_nr) < 0)
1311+
exit(127);
1312+
1313+
for (size_t i = 0; i < prunable_entries_nr; i++)
1314+
unlink_rr_item(&prunable_entries[i]);
1315+
for (size_t i = 0; i < prunable_dirs.nr; i++)
1316+
rmdir(repo_git_path_replace(r, &buf, "rr-cache/%s",
1317+
prunable_dirs.items[i].string));
12831318

1284-
string_list_clear(&to_remove, 0);
1319+
string_list_clear(&prunable_dirs, 0);
12851320
rollback_lock_file(&write_lock);
12861321
strbuf_release(&buf);
1322+
free(prunable_entries);
12871323
}
12881324

12891325
/*

rerere.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,20 @@ const char *rerere_path(struct strbuf *buf, const struct rerere_id *,
3737
int rerere_forget(struct repository *, struct pathspec *);
3838
int rerere_remaining(struct repository *, struct string_list *);
3939
void rerere_clear(struct repository *, struct string_list *);
40+
41+
/*
42+
* Collect prunable rerere entries that would be garbage collected via
43+
* `rerere_gc()`. Whether or not an entry is prunable depends on both
44+
* "gc.rerereResolved" and "gc.rerereUnresolved".
45+
*
46+
* Returns 0 on success, a negative error code in case entries could not be
47+
* collected.
48+
*/
49+
int rerere_collect_stale_entries(struct repository *r,
50+
struct string_list *prunable_dirs,
51+
struct rerere_id **prunable_entries,
52+
size_t *prunable_entries_nr);
53+
4054
void rerere_gc(struct repository *, struct string_list *);
4155

4256
#define OPT_RERERE_AUTOUPDATE(v) OPT_UYN(0, "rerere-autoupdate", (v), \

0 commit comments

Comments
 (0)