Merge branch 'ps/rerere-remove-global-state'

The 'rerere' subsystem has been libified to remove its dependency on
the global 'the_repository' state and global tracking variables,
allowing it to operate correctly in multi-repository contexts.

* ps/rerere-remove-global-state:
  rerere: get rid of global rerere cache state
  rerere: bundle global state into a struct
  rerere: stop using `the_repository`
  rerere: cache result of `is_rerere_enabled()`
  rerere: remove redundant calls to `repo_config()`
  rerere: stop using `the_repository` in internal helpers
  rerere: stop using `the_repository` in `rerere_path()`
This commit is contained in:
Junio C Hamano committed 2026-11-01 09:25:24 -08:00
commit b1bf38c452
7 files changed
+216 -151

No files matched your search

+15 -6
View File
@@ -106,11 +106,15 @@ int cmd_rerere(int argc,
rerere_gc(the_repository, &merge_rr,
skip_locked ? RERERE_NOWAIT : 0);
else if (!strcmp(argv[0], "status")) {
if (setup_rerere(the_repository, &merge_rr,
flags | RERERE_READONLY) < 0)
struct rerere_cache cache;
if (rerere_cache_setup(&cache, the_repository, &merge_rr,
flags | RERERE_READONLY) < 0)
return 0;
for (size_t i = 0; i < merge_rr.nr; i++)
printf("%s\n", merge_rr.items[i].string);
rerere_cache_release(&cache);
} else if (!strcmp(argv[0], "remaining")) {
rerere_remaining(the_repository, &merge_rr);
for (size_t i = 0; i < merge_rr.nr; i++) {
@@ -123,16 +127,21 @@ int cmd_rerere(int argc,
}
} else if (!strcmp(argv[0], "diff")) {
struct strbuf buf = STRBUF_INIT;
if (setup_rerere(the_repository, &merge_rr,
flags | RERERE_READONLY) < 0)
struct rerere_cache cache;
if (rerere_cache_setup(&cache, the_repository, &merge_rr,
flags | RERERE_READONLY) < 0)
return 0;
for (size_t i = 0; i < merge_rr.nr; i++) {
const char *path = merge_rr.items[i].string;
const struct rerere_id *id = merge_rr.items[i].util;
if (diff_two(rerere_path(&buf, id, "preimage"), path, path, path))
die(_("unable to generate diff for '%s'"), rerere_path(&buf, id, NULL));
if (diff_two(rerere_path(the_repository, &buf, id, "preimage"),
path, path, path))
die(_("unable to generate diff for '%s'"),
rerere_path(the_repository, &buf, id, NULL));
}
rerere_cache_release(&cache);
strbuf_release(&buf);
} else
usage_with_options(rerere_usage, options);
+2 -2
View File
@@ -5,8 +5,8 @@
#include "hex.h"
#include "strbuf.h"
static int get_hash_hex_algop(const char *hex, unsigned char *hash,
const struct git_hash_algo *algop)
int get_hash_hex_algop(const char *hex, unsigned char *hash,
const struct git_hash_algo *algop)
{
for (size_t i = 0; i < algop->rawsz; i++) {
int val = hex2chr(hex);
+3
View File
@@ -59,6 +59,9 @@ int parse_oid_hex_algop_impl(const char *hex, struct object_id *oid, const char
int get_oid_hex_any(const char *hex, struct object_id *oid);
int parse_oid_hex_any(const char *hex, struct object_id *oid, const char **end);
int get_hash_hex_algop(const char *hex, unsigned char *hash,
const struct git_hash_algo *algop);
#ifdef USE_THE_REPOSITORY_VARIABLE
/* Like get_oid_hex_algop, but for `the_hash_algo`. */
+1
View File
@@ -76,6 +76,7 @@ void initialize_repository(struct repository *repo)
repo->bare_cfg = -1;
repo->fetch_if_missing = 1;
repo_config_values_init(&repo->config_values_private_);
repo->rerere_cache_enabled = -1;
/*
* When a command runs inside a repository, it learns what
+6
View File
@@ -185,6 +185,12 @@ struct repository {
/* True if commit-graph has been disabled within this process. */
int commit_graph_disabled;
/*
* Indicates whether the rerere-cache has been enabled. -1 (the default
* value) causes us to perform auto-detection via ".git/rr-cache".
*/
int rerere_cache_enabled;
/*
* Controls whether the repository should lazily fetch missing
* objects from promisor remotes. Defaults to 1.
+168 -141
View File
@@ -1,4 +1,3 @@
#define USE_THE_REPOSITORY_VARIABLE
#define DISABLE_SIGN_COMPARE_WARNINGS
#include "git-compat-util.h"
@@ -27,15 +26,6 @@
#define THREE_STAGED 2
void *RERERE_RESOLVED = &RERERE_RESOLVED;
/* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
static int rerere_enabled = -1;
/* automatically update cleanly resolved paths to the index */
static int rerere_autoupdate;
/* how long to wait for MERGE_RR.lock, in milliseconds */
static int rerere_lock_timeout_ms = 1000;
#define RR_HAS_POSTIMAGE 1
#define RR_HAS_PREIMAGE 2
struct rerere_dir {
@@ -44,19 +34,19 @@ struct rerere_dir {
char name[FLEX_ARRAY];
};
static struct strmap rerere_dirs = STRMAP_INIT;
static void free_rerere_dirs(void)
void rerere_cache_release(struct rerere_cache *cache)
{
struct hashmap_iter iter;
struct strmap_entry *ent;
strmap_for_each_entry(&rerere_dirs, &iter, ent) {
strmap_for_each_entry(&cache->dirs, &iter, ent) {
struct rerere_dir *rr_dir = ent->value;
free(rr_dir->status);
free(rr_dir);
}
strmap_clear(&rerere_dirs, 0);
strmap_clear(&cache->dirs, 0);
rollback_lock_file(&cache->write_lock);
}
static void free_rerere_id(struct string_list_item *item)
@@ -95,17 +85,18 @@ static void assign_variant(struct rerere_id *id)
id->variant = variant;
}
const char *rerere_path(struct strbuf *buf, const struct rerere_id *id, const char *file)
const char *rerere_path(struct repository *repo,
struct strbuf *buf, const struct rerere_id *id, const char *file)
{
if (!file)
return repo_git_path_replace(the_repository, buf, "rr-cache/%s",
return repo_git_path_replace(repo, buf, "rr-cache/%s",
rerere_id_hex(id));
if (id->variant <= 0)
return repo_git_path_replace(the_repository, buf, "rr-cache/%s/%s",
return repo_git_path_replace(repo, buf, "rr-cache/%s/%s",
rerere_id_hex(id), file);
return repo_git_path_replace(the_repository, buf, "rr-cache/%s/%s.%d",
return repo_git_path_replace(repo, buf, "rr-cache/%s/%s.%d",
rerere_id_hex(id), file, id->variant);
}
@@ -128,13 +119,13 @@ static int is_rr_file(const char *name, const char *filename, int *variant)
return 1;
}
static void scan_rerere_dir(struct rerere_dir *rr_dir)
static void scan_rerere_dir(struct rerere_cache *cache, struct rerere_dir *rr_dir)
{
struct dirent *de;
char *path;
DIR *dir;
path = repo_git_path(the_repository, "rr-cache/%s", rr_dir->name);
path = repo_git_path(cache->repo, "rr-cache/%s", rr_dir->name);
dir = opendir(path);
free(path);
if (!dir)
@@ -153,19 +144,20 @@ static void scan_rerere_dir(struct rerere_dir *rr_dir)
closedir(dir);
}
static struct rerere_dir *find_rerere_dir(const char *hex)
static struct rerere_dir *find_rerere_dir(struct rerere_cache *cache,
const char *hex)
{
struct rerere_dir *rr_dir;
rr_dir = strmap_get(&rerere_dirs, hex);
rr_dir = strmap_get(&cache->dirs, hex);
if (!rr_dir) {
FLEX_ALLOC_STR(rr_dir, name, hex);
rr_dir->status = NULL;
rr_dir->status_nr = 0;
rr_dir->status_alloc = 0;
strmap_put(&rerere_dirs, hex, rr_dir);
strmap_put(&cache->dirs, hex, rr_dir);
scan_rerere_dir(rr_dir);
scan_rerere_dir(cache, rr_dir);
}
return rr_dir;
}
@@ -180,17 +172,18 @@ static int has_rerere_resolution(const struct rerere_id *id)
return ((id->collection->status[variant] & both) == both);
}
static struct rerere_id *new_rerere_id_hex(char *hex)
static struct rerere_id *new_rerere_id_hex(struct rerere_cache *cache,
char *hex)
{
struct rerere_id *id = xmalloc(sizeof(*id));
id->collection = find_rerere_dir(hex);
id->collection = find_rerere_dir(cache, hex);
id->variant = -1; /* not known yet */
return id;
}
static struct rerere_id *new_rerere_id(unsigned char *hash)
static struct rerere_id *new_rerere_id(struct rerere_cache *cache, unsigned char *hash)
{
return new_rerere_id_hex(hash_to_hex(hash));
return new_rerere_id_hex(cache, hash_to_hex_algop(hash, cache->repo->hash_algo));
}
/*
@@ -200,10 +193,10 @@ static struct rerere_id *new_rerere_id(unsigned char *hash)
* work on (i.e. what is left by the previous invocation of "git
* rerere" during the current conflict resolution session).
*/
static void read_rr(struct repository *r, struct string_list *rr)
static void read_rr(struct rerere_cache *cache, struct string_list *rr)
{
struct strbuf buf = STRBUF_INIT;
FILE *in = fopen_or_warn(git_path_merge_rr(r), "r");
FILE *in = fopen_or_warn(git_path_merge_rr(cache->repo), "r");
if (!in)
return;
@@ -212,10 +205,11 @@ static void read_rr(struct repository *r, struct string_list *rr)
unsigned char hash[GIT_MAX_RAWSZ];
struct rerere_id *id;
int variant;
const unsigned hexsz = the_hash_algo->hexsz;
const unsigned hexsz = cache->repo->hash_algo->hexsz;
/* There has to be the hash, tab, path and then NUL */
if (buf.len < hexsz + 2 || get_hash_hex(buf.buf, hash))
if (buf.len < hexsz + 2 ||
get_hash_hex_algop(buf.buf, hash, cache->repo->hash_algo))
die(_("corrupt MERGE_RR"));
if (buf.buf[hexsz] != '.') {
@@ -230,7 +224,7 @@ static void read_rr(struct repository *r, struct string_list *rr)
if (*(path++) != '\t')
die(_("corrupt MERGE_RR"));
buf.buf[hexsz] = '\0';
id = new_rerere_id_hex(buf.buf);
id = new_rerere_id_hex(cache, buf.buf);
id->variant = variant;
/*
* make sure id->collection->status has enough space
@@ -243,9 +237,8 @@ static void read_rr(struct repository *r, struct string_list *rr)
fclose(in);
}
static struct lock_file write_lock;
static int write_rr(struct string_list *rr, int out_fd)
static int write_rr(struct rerere_cache *cache,
struct string_list *rr, int out_fd)
{
int i;
for (i = 0; i < rr->nr; i++) {
@@ -272,7 +265,7 @@ static int write_rr(struct string_list *rr, int out_fd)
strbuf_release(&buf);
}
if (commit_lock_file(&write_lock) != 0)
if (commit_lock_file(&cache->write_lock) != 0)
die(_("unable to write rerere record"));
return 0;
}
@@ -410,13 +403,14 @@ static int handle_conflict(struct strbuf *out, struct rerere_io *io,
* Return 1 if conflict hunks are found, 0 if there are no conflict
* hunks and -1 if an error occurred.
*/
static int handle_path(unsigned char *hash, struct rerere_io *io, int marker_size)
static int handle_path(struct repository *r,
unsigned char *hash, struct rerere_io *io, int marker_size)
{
struct git_hash_ctx ctx;
struct strbuf buf = STRBUF_INIT, out = STRBUF_INIT;
int has_conflicts = 0;
if (hash)
git_hash_init(&ctx, the_hash_algo);
git_hash_init(&ctx, r->hash_algo);
while (!io->getline(&buf, io)) {
if (is_conflict_marker_line(buf.buf, buf.len, marker_size) == '<') {
@@ -465,7 +459,7 @@ static int handle_file(struct index_state *istate,
}
}
has_conflicts = handle_path(hash, (struct rerere_io *)&io, marker_size);
has_conflicts = handle_path(istate->repo, hash, (struct rerere_io *)&io, marker_size);
fclose(io.input);
if (io.io.wrerror)
@@ -571,12 +565,15 @@ static int find_conflict(struct repository *r, struct string_list *conflict)
*/
int rerere_remaining(struct repository *r, struct string_list *merge_rr)
{
int i;
struct rerere_cache cache;
int ret, i;
if (setup_rerere(r, merge_rr, RERERE_READONLY))
if (rerere_cache_setup(&cache, r, merge_rr, RERERE_READONLY))
return 0;
if (repo_read_index(r) < 0)
return error(_("index file corrupt"));
if (repo_read_index(r) < 0) {
ret = error(_("index file corrupt"));
goto out;
}
for (i = 0; i < r->index->cache_nr;) {
int conflict_type;
@@ -593,7 +590,12 @@ int rerere_remaining(struct repository *r, struct string_list *merge_rr)
}
}
}
return 0;
ret = 0;
out:
rerere_cache_release(&cache);
return ret;
}
/*
@@ -609,8 +611,8 @@ static int try_merge(struct index_state *istate,
mmfile_t base = {NULL, 0}, other = {NULL, 0};
struct strbuf buf = STRBUF_INIT;
if (read_mmfile(&base, rerere_path(&buf, id, "preimage")) ||
read_mmfile(&other, rerere_path(&buf, id, "postimage"))) {
if (read_mmfile(&base, rerere_path(istate->repo, &buf, id, "preimage")) ||
read_mmfile(&other, rerere_path(istate->repo, &buf, id, "postimage"))) {
ret = LL_MERGE_CONFLICT;
} else {
/*
@@ -650,8 +652,8 @@ static int merge(struct index_state *istate, const struct rerere_id *id, const c
* Normalize the conflicts in path and write it out to
* "thisimage" temporary file.
*/
if ((handle_file(istate, path, NULL, rerere_path(&buf, id, "thisimage")) < 0) ||
read_mmfile(&cur, rerere_path(&buf, id, "thisimage"))) {
if ((handle_file(istate, path, NULL, rerere_path(istate->repo, &buf, id, "thisimage")) < 0) ||
read_mmfile(&cur, rerere_path(istate->repo, &buf, id, "thisimage"))) {
ret = 1;
goto out;
}
@@ -664,9 +666,9 @@ static int merge(struct index_state *istate, const struct rerere_id *id, const c
* A successful replay of recorded resolution.
* Mark that "postimage" was used to help gc.
*/
if (utime(rerere_path(&buf, id, "postimage"), NULL) < 0)
if (utime(rerere_path(istate->repo, &buf, id, "postimage"), NULL) < 0)
warning_errno(_("failed utime() on '%s'"),
rerere_path(&buf, id, "postimage"));
rerere_path(istate->repo, &buf, id, "postimage"));
/* Update "path" with the resolution */
f = fopen(path, "w");
@@ -705,11 +707,11 @@ static void update_paths(struct repository *r, struct string_list *update)
die(_("unable to write new index file"));
}
static void remove_variant(struct rerere_id *id)
static void remove_variant(struct repository *r, struct rerere_id *id)
{
struct strbuf buf = STRBUF_INIT;
unlink_or_warn(rerere_path(&buf, id, "postimage"));
unlink_or_warn(rerere_path(&buf, id, "preimage"));
unlink_or_warn(rerere_path(r, &buf, id, "postimage"));
unlink_or_warn(rerere_path(r, &buf, id, "preimage"));
id->collection->status[id->variant] = 0;
strbuf_release(&buf);
}
@@ -721,10 +723,11 @@ static void remove_variant(struct rerere_id *id)
* only have the preimage for that conflict, in which case the result
* needs to be recorded as a resolution in a postimage file.
*/
static void do_rerere_one_path(struct index_state *istate,
static void do_rerere_one_path(struct rerere_cache *cache,
struct string_list_item *rr_item,
struct string_list *update)
{
struct index_state *istate = cache->repo->index;
const char *path = rr_item->string;
struct rerere_id *id = rr_item->util;
struct rerere_dir *rr_dir = id->collection;
@@ -743,9 +746,9 @@ static void do_rerere_one_path(struct index_state *istate,
const int had_postimage =
id->collection->status[variant] & RR_HAS_POSTIMAGE;
const char *postimage =
rerere_path(&buf, id, "postimage");
rerere_path(istate->repo, &buf, id, "postimage");
if (copy_file(the_repository,
if (copy_file(istate->repo,
postimage,
path, 0666)) {
if (!had_postimage)
@@ -784,9 +787,9 @@ static void do_rerere_one_path(struct index_state *istate,
* cleanly, there is no point maintaining our own variant.
*/
if (0 <= id->variant && id->variant != variant)
remove_variant(id);
remove_variant(istate->repo, id);
if (rerere_autoupdate)
if (cache->autoupdate)
string_list_insert(update, path);
else
fprintf_ln(stderr,
@@ -802,10 +805,10 @@ static void do_rerere_one_path(struct index_state *istate,
variant = id->variant;
if (handle_file(istate, path, NULL,
rerere_path(&buf, id, "preimage")) < 0)
rerere_path(istate->repo, &buf, id, "preimage")) < 0)
goto out;
if (id->collection->status[variant] & RR_HAS_POSTIMAGE) {
const char *path = rerere_path(&buf, id, "postimage");
const char *path = rerere_path(istate->repo, &buf, id, "postimage");
if (unlink(path))
die_errno(_("cannot unlink stray '%s'"), path);
id->collection->status[variant] &= ~RR_HAS_POSTIMAGE;
@@ -817,12 +820,13 @@ out:
strbuf_release(&buf);
}
static int do_plain_rerere(struct repository *r,
static int do_plain_rerere(struct rerere_cache *cache,
struct string_list *rr, int fd)
{
struct string_list conflict = STRING_LIST_INIT_DUP;
struct string_list update = STRING_LIST_INIT_DUP;
struct strbuf buf = STRBUF_INIT;
struct repository *r = cache->repo;
int i;
find_conflict(r, &conflict);
@@ -846,21 +850,21 @@ static int do_plain_rerere(struct repository *r,
*/
ret = handle_file(r->index, path, hash, NULL);
if (ret != 0 && string_list_has_string(rr, path)) {
remove_variant(string_list_lookup(rr, path)->util);
remove_variant(r, string_list_lookup(rr, path)->util);
string_list_remove(rr, path, 1);
}
if (ret < 1)
continue;
id = new_rerere_id(hash);
id = new_rerere_id(cache, hash);
string_list_insert(rr, path)->util = id;
/* Ensure that the directory exists. */
safe_create_dir_in_gitdir(the_repository, rerere_path(&buf, id, NULL));
safe_create_dir_in_gitdir(r, rerere_path(r, &buf, id, NULL));
}
for (i = 0; i < rr->nr; i++)
do_rerere_one_path(r->index, &rr->items[i], &update);
do_rerere_one_path(cache, &rr->items[i], &update);
if (update.nr)
update_paths(r, &update);
@@ -868,58 +872,68 @@ static int do_plain_rerere(struct repository *r,
string_list_clear(&conflict, 0);
string_list_clear(&update, 0);
strbuf_release(&buf);
return write_rr(rr, fd);
return write_rr(cache, rr, fd);
}
static void git_rerere_config(void)
{
repo_config_get_bool(the_repository, "rerere.enabled", &rerere_enabled);
repo_config_get_bool(the_repository, "rerere.autoupdate", &rerere_autoupdate);
repo_config_get_int(the_repository, "rerere.locktimeout",
&rerere_lock_timeout_ms);
repo_config(the_repository, git_default_config, NULL);
}
static GIT_PATH_FUNC(git_path_rr_cache, "rr-cache")
static int is_rerere_enabled(void)
static int is_rerere_enabled(struct repository *r)
{
int rr_cache_exists;
char *dir;
if (!rerere_enabled)
if (r->rerere_cache_enabled != -1)
return r->rerere_cache_enabled;
repo_config_get_bool(r, "rerere.enabled", &r->rerere_cache_enabled);
if (!r->rerere_cache_enabled)
return 0;
rr_cache_exists = is_directory(git_path_rr_cache());
if (rerere_enabled < 0)
return rr_cache_exists;
dir = repo_git_path(r, "rr-cache");
rr_cache_exists = is_directory(dir);
if (r->rerere_cache_enabled < 0) {
r->rerere_cache_enabled = rr_cache_exists;
goto out;
}
if (!rr_cache_exists &&
safe_create_dir_in_gitdir(the_repository, git_path_rr_cache()))
die(_("could not create directory '%s'"), git_path_rr_cache());
return 1;
safe_create_dir_in_gitdir(r, dir))
die(_("could not create directory '%s'"), dir);
r->rerere_cache_enabled = 1;
out:
free(dir);
return r->rerere_cache_enabled;
}
int setup_rerere(struct repository *r, struct string_list *merge_rr, int flags)
int rerere_cache_setup(struct rerere_cache *cache,
struct repository *r,
struct string_list *merge_rr,
int flags)
{
int lock_timeout_ms = 1000;
int fd;
git_rerere_config();
if (!is_rerere_enabled())
if (!is_rerere_enabled(r))
return -1;
memset(cache, 0, sizeof(*cache));
cache->repo = r;
strmap_init(&cache->dirs);
repo_config_get_bool(r, "rerere.autoupdate", &cache->autoupdate);
repo_config_get_int(r, "rerere.locktimeout", &lock_timeout_ms);
if (flags & (RERERE_AUTOUPDATE|RERERE_NOAUTOUPDATE))
rerere_autoupdate = !!(flags & RERERE_AUTOUPDATE);
cache->autoupdate = !!(flags & RERERE_AUTOUPDATE);
if ((flags & RERERE_READONLY) && (flags & RERERE_NOWAIT))
BUG("RERERE_NOWAIT does not apply with RERERE_READONLY");
if (flags & RERERE_READONLY) {
fd = 0;
} else {
int lock_flags = LOCK_DIE_ON_ERROR;
int timeout_ms = rerere_lock_timeout_ms;
if (flags & RERERE_NOWAIT) {
lock_flags = 0;
timeout_ms = 0;
lock_timeout_ms = 0;
}
/*
@@ -927,13 +941,13 @@ int setup_rerere(struct repository *r, struct string_list *merge_rr, int flags)
* "git rerere gc" while it prunes rr-cache, so by default
* wait for it instead of dying right away.
*/
fd = repo_hold_lock_file_for_update_timeout(r, &write_lock,
fd = repo_hold_lock_file_for_update_timeout(r, &cache->write_lock,
git_path_merge_rr(r),
lock_flags, timeout_ms);
lock_flags, lock_timeout_ms);
if (fd < 0)
return -1;
}
read_rr(r, merge_rr);
read_rr(cache, merge_rr);
return fd;
}
@@ -945,13 +959,15 @@ int setup_rerere(struct repository *r, struct string_list *merge_rr, int flags)
int repo_rerere(struct repository *r, int flags)
{
struct string_list merge_rr = STRING_LIST_INIT_DUP;
struct rerere_cache cache;
int fd, status;
fd = setup_rerere(r, &merge_rr, flags);
fd = rerere_cache_setup(&cache, r, &merge_rr, flags);
if (fd < 0)
return 0;
status = do_plain_rerere(r, &merge_rr, fd);
free_rerere_dirs();
status = do_plain_rerere(&cache, &merge_rr, fd);
rerere_cache_release(&cache);
string_list_clear(&merge_rr, 1);
return status;
}
@@ -1016,7 +1032,7 @@ static int handle_cache(struct index_state *istate,
break;
i = ce_stage(ce) - 1;
if (!mmfile[i].ptr) {
mmfile[i].ptr = odb_read_object(the_repository->objects,
mmfile[i].ptr = odb_read_object(istate->repo->objects,
&ce->oid, &type, &size);
if (!mmfile[i].ptr)
die(_("unable to read %s"),
@@ -1052,17 +1068,19 @@ static int handle_cache(struct index_state *istate,
* Grab the conflict ID and optionally write the original
* contents with conflict markers out.
*/
has_conflicts = handle_path(hash, (struct rerere_io *)&io, marker_size);
has_conflicts = handle_path(istate->repo, hash,
(struct rerere_io *)&io, marker_size);
strbuf_release(&io.input);
if (io.io.output)
fclose(io.io.output);
return has_conflicts;
}
static int rerere_forget_one_path(struct index_state *istate,
static int rerere_forget_one_path(struct rerere_cache *cache,
const char *path,
struct string_list *rr)
{
struct index_state *istate = cache->repo->index;
const char *filename;
struct rerere_id *id;
unsigned char hash[GIT_MAX_RAWSZ];
@@ -1079,7 +1097,7 @@ static int rerere_forget_one_path(struct index_state *istate,
return error(_("could not parse conflict hunks in '%s'"), path);
/* Nuke the recorded resolution for the conflict */
id = new_rerere_id(hash);
id = new_rerere_id(cache, hash);
for (id->variant = 0;
id->variant < id->collection->status_nr;
@@ -1091,8 +1109,8 @@ static int rerere_forget_one_path(struct index_state *istate,
if (!has_rerere_resolution(id))
continue;
handle_cache(istate, path, hash, rerere_path(&buf, id, "thisimage"));
if (read_mmfile(&cur, rerere_path(&buf, id, "thisimage"))) {
handle_cache(istate, path, hash, rerere_path(istate->repo, &buf, id, "thisimage"));
if (read_mmfile(&cur, rerere_path(istate->repo, &buf, id, "thisimage"))) {
error(_("failed to update conflicted state in '%s'"), path);
goto fail_exit;
}
@@ -1108,7 +1126,7 @@ static int rerere_forget_one_path(struct index_state *istate,
goto fail_exit;
}
filename = rerere_path(&buf, id, "postimage");
filename = rerere_path(istate->repo, &buf, id, "postimage");
if (unlink(filename)) {
if (errno == ENOENT)
error(_("no remembered resolution for '%s'"), path);
@@ -1122,7 +1140,7 @@ static int rerere_forget_one_path(struct index_state *istate,
* conflict in the working tree, run us again to record
* the postimage.
*/
handle_cache(istate, path, hash, rerere_path(&buf, id, "preimage"));
handle_cache(istate, path, hash, rerere_path(istate->repo, &buf, id, "preimage"));
fprintf_ln(stderr, _("Updated preimage for '%s'"), path);
/*
@@ -1147,11 +1165,12 @@ int rerere_forget(struct repository *r, struct pathspec *pathspec)
int i, fd, ret;
struct string_list conflict = STRING_LIST_INIT_DUP;
struct string_list merge_rr = STRING_LIST_INIT_DUP;
struct rerere_cache cache;
if (repo_read_index(r) < 0)
return error(_("index file corrupt"));
fd = setup_rerere(r, &merge_rr, RERERE_NOAUTOUPDATE);
fd = rerere_cache_setup(&cache, r, &merge_rr, RERERE_NOAUTOUPDATE);
if (fd < 0)
return 0;
@@ -1167,13 +1186,14 @@ int rerere_forget(struct repository *r, struct pathspec *pathspec)
if (!match_pathspec(r->index, pathspec, it->string,
strlen(it->string), 0, NULL, 0))
continue;
rerere_forget_one_path(r->index, it->string, &merge_rr);
rerere_forget_one_path(&cache, it->string, &merge_rr);
}
ret = write_rr(&merge_rr, fd);
ret = write_rr(&cache, &merge_rr, fd);
string_list_clear(&conflict, 0);
string_list_clear(&merge_rr, 1);
rerere_cache_release(&cache);
return ret;
}
@@ -1181,25 +1201,25 @@ int rerere_forget(struct repository *r, struct pathspec *pathspec)
* Garbage collection support
*/
static timestamp_t rerere_created_at(struct rerere_id *id)
static timestamp_t rerere_created_at(struct repository *r, struct rerere_id *id)
{
struct strbuf buf = STRBUF_INIT;
struct stat st;
timestamp_t ret;
ret = stat(rerere_path(&buf, id, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
ret = stat(rerere_path(r, &buf, id, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
strbuf_release(&buf);
return ret;
}
static timestamp_t rerere_last_used_at(struct rerere_id *id)
static timestamp_t rerere_last_used_at(struct repository *r, struct rerere_id *id)
{
struct strbuf buf = STRBUF_INIT;
struct stat st;
timestamp_t ret;
ret = stat(rerere_path(&buf, id, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
ret = stat(rerere_path(r, &buf, id, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
strbuf_release(&buf);
return ret;
@@ -1208,11 +1228,11 @@ static timestamp_t rerere_last_used_at(struct rerere_id *id)
/*
* Remove the recorded resolution for a given conflict ID
*/
static void unlink_rr_item(struct rerere_id *id)
static void unlink_rr_item(struct repository *r, struct rerere_id *id)
{
struct strbuf buf = STRBUF_INIT;
unlink_or_warn(rerere_path(&buf, id, "thisimage"));
remove_variant(id);
unlink_or_warn(rerere_path(r, &buf, id, "thisimage"));
remove_variant(r, id);
id->collection->status[id->variant] = 0;
strbuf_release(&buf);
}
@@ -1231,18 +1251,19 @@ static void rerere_gc_cutoffs(struct repository *r,
*cutoff_noresolve = now - 15 * 86400;
}
static bool rerere_id_is_stale(struct rerere_id *id,
static bool rerere_id_is_stale(struct repository *r,
struct rerere_id *id,
timestamp_t cutoff_resolve,
timestamp_t cutoff_noresolve)
{
timestamp_t then;
timestamp_t cutoff;
then = rerere_last_used_at(id);
then = rerere_last_used_at(r, id);
if (then) {
cutoff = cutoff_resolve;
} else {
then = rerere_created_at(id);
then = rerere_created_at(r, id);
if (!then)
return false;
cutoff = cutoff_noresolve;
@@ -1251,23 +1272,27 @@ static bool rerere_id_is_stale(struct rerere_id *id,
return then < cutoff;
}
static void prune_one(struct rerere_id *id,
static void prune_one(struct repository *r, struct rerere_id *id,
timestamp_t cutoff_resolve, timestamp_t cutoff_noresolve)
{
if (rerere_id_is_stale(id, cutoff_resolve, cutoff_noresolve))
unlink_rr_item(id);
if (rerere_id_is_stale(r, id, cutoff_resolve, cutoff_noresolve))
unlink_rr_item(r, id);
}
/* Does the basename in "path" look plausibly like an rr-cache entry? */
static int is_rr_cache_dirname(const char *path)
static int is_rr_cache_dirname(struct repository *r, const char *path)
{
struct object_id oid;
const char *end;
return !parse_oid_hex(path, &oid, &end) && !*end;
return !parse_oid_hex_algop(path, &oid, &end, r->hash_algo) && !*end;
}
bool rerere_gc_needed(struct repository *r, size_t limit)
{
struct rerere_cache cache = {
.repo = r,
.dirs = STRMAP_INIT,
};
timestamp_t cutoff_resolve, cutoff_noresolve;
struct strbuf buf = STRBUF_INIT;
bool needed = false;
@@ -1290,14 +1315,14 @@ bool rerere_gc_needed(struct repository *r, size_t limit)
* use for estimating the number of loose objects.
*/
if (!starts_with(e->d_name, "17") ||
!is_rr_cache_dirname(e->d_name))
!is_rr_cache_dirname(r, e->d_name))
continue;
id.collection = find_rerere_dir(e->d_name);
id.collection = find_rerere_dir(&cache, e->d_name);
for (id.variant = 0;
id.variant < id.collection->status_nr;
id.variant++) {
if (rerere_id_is_stale(&id, cutoff_resolve,
if (rerere_id_is_stale(r, &id, cutoff_resolve,
cutoff_noresolve)) {
count += 256;
if (count >= limit) {
@@ -1311,13 +1336,14 @@ bool rerere_gc_needed(struct repository *r, size_t limit)
out:
if (dir)
closedir(dir);
free_rerere_dirs();
rerere_cache_release(&cache);
strbuf_release(&buf);
return needed;
}
void rerere_gc(struct repository *r, struct string_list *rr, int flags)
{
struct rerere_cache cache;
struct string_list to_remove = STRING_LIST_INIT_DUP;
DIR *dir;
struct dirent *e;
@@ -1326,12 +1352,12 @@ void rerere_gc(struct repository *r, struct string_list *rr, int flags)
timestamp_t cutoff_resolve;
struct strbuf buf = STRBUF_INIT;
if (setup_rerere(r, rr, flags) < 0)
if (rerere_cache_setup(&cache, r, rr, flags) < 0)
return;
rerere_gc_cutoffs(r, &cutoff_resolve, &cutoff_noresolve);
repo_config(the_repository, git_default_config, NULL);
dir = opendir(repo_git_path_replace(the_repository, &buf, "rr-cache"));
dir = opendir(repo_git_path_replace(r, &buf, "rr-cache"));
if (!dir)
die_errno(_("unable to open rr-cache directory"));
/* Collect stale conflict IDs ... */
@@ -1340,16 +1366,16 @@ void rerere_gc(struct repository *r, struct string_list *rr, int flags)
struct rerere_id id;
int now_empty;
if (!is_rr_cache_dirname(e->d_name))
if (!is_rr_cache_dirname(r, e->d_name))
continue; /* or should we remove e->d_name? */
rr_dir = find_rerere_dir(e->d_name);
rr_dir = find_rerere_dir(&cache, e->d_name);
now_empty = 1;
for (id.variant = 0, id.collection = rr_dir;
id.variant < id.collection->status_nr;
id.variant++) {
prune_one(&id, cutoff_resolve, cutoff_noresolve);
prune_one(r, &id, cutoff_resolve, cutoff_noresolve);
if (id.collection->status[id.variant])
now_empty = 0;
}
@@ -1360,11 +1386,11 @@ void rerere_gc(struct repository *r, struct string_list *rr, int flags)
/* ... and then remove the empty directories */
for (i = 0; i < to_remove.nr; i++)
rmdir(repo_git_path_replace(the_repository, &buf,
rmdir(repo_git_path_replace(r, &buf,
"rr-cache/%s", to_remove.items[i].string));
string_list_clear(&to_remove, 0);
rollback_lock_file(&write_lock);
rerere_cache_release(&cache);
strbuf_release(&buf);
}
@@ -1377,9 +1403,10 @@ void rerere_gc(struct repository *r, struct string_list *rr, int flags)
*/
void rerere_clear(struct repository *r, struct string_list *merge_rr)
{
struct rerere_cache cache;
int i;
if (setup_rerere(r, merge_rr, 0) < 0)
if (rerere_cache_setup(&cache, r, merge_rr, 0) < 0)
return;
for (i = 0; i < merge_rr->nr; i++) {
@@ -1387,12 +1414,12 @@ void rerere_clear(struct repository *r, struct string_list *merge_rr)
struct strbuf buf = STRBUF_INIT;
if (!has_rerere_resolution(id)) {
unlink_rr_item(id);
rmdir(rerere_path(&buf, id, NULL));
unlink_rr_item(r, id);
rmdir(rerere_path(r, &buf, id, NULL));
}
strbuf_release(&buf);
}
unlink_or_warn(git_path_merge_rr(r));
rollback_lock_file(&write_lock);
rerere_cache_release(&cache);
}
+21 -2
View File
@@ -2,7 +2,9 @@
#define RERERE_H
#include "gettext.h"
#include "lockfile.h"
#include "string-list.h"
#include "strmap.h"
struct pathspec;
struct repository;
@@ -20,13 +22,29 @@ struct repository;
*/
extern void *RERERE_RESOLVED;
struct rerere_cache {
struct repository *repo;
/* Automatically update cleanly resolved paths to the index. */
int autoupdate;
struct strmap dirs;
struct lock_file write_lock;
};
struct rerere_dir;
struct rerere_id {
struct rerere_dir *collection;
int variant;
};
int setup_rerere(struct repository *,struct string_list *, int);
int rerere_cache_setup(struct rerere_cache *cache,
struct repository *r,
struct string_list *merge_rr,
int flags);
void rerere_cache_release(struct rerere_cache *cache);
int repo_rerere(struct repository *, int);
/*
* Given the conflict ID and the name of a "file" used for replaying
@@ -34,7 +52,8 @@ int repo_rerere(struct repository *, int);
* path to that filesystem entity. With "file" specified with NULL,
* return the path to the directory that houses these files.
*/
const char *rerere_path(struct strbuf *buf, const struct rerere_id *,
const char *rerere_path(struct repository *,
struct strbuf *buf, const struct rerere_id *,
const char *file);
int rerere_forget(struct repository *, struct pathspec *);
int rerere_remaining(struct repository *, struct string_list *);