/* automatically update cleanly resolved paths to the index */
static int rerere_autoupdate;
-static char *merge_rr_path;
+static void free_rerere_id(struct string_list_item *item)
+{
+ free(item->util);
+}
-const char *rerere_path(const char *hex, const char *file)
+static const char *rerere_id_hex(const struct rerere_id *id)
{
- return git_path("rr-cache/%s/%s", hex, file);
+ return id->hex;
+}
+
+const char *rerere_path(const struct rerere_id *id, const char *file)
+{
+ if (!file)
+ return git_path("rr-cache/%s", rerere_id_hex(id));
+
+ return git_path("rr-cache/%s/%s", rerere_id_hex(id), file);
}
-static int has_rerere_resolution(const char *hex)
+static int has_rerere_resolution(const struct rerere_id *id)
{
struct stat st;
- return !stat(rerere_path(hex, "postimage"), &st);
+
+ return !stat(rerere_path(id, "postimage"), &st);
+}
+
+static struct rerere_id *new_rerere_id_hex(char *hex)
+{
+ struct rerere_id *id = xmalloc(sizeof(*id));
+ xsnprintf(id->hex, sizeof(id->hex), "%s", hex);
+ return id;
+}
+
+static struct rerere_id *new_rerere_id(unsigned char *sha1)
+{
+ return new_rerere_id_hex(sha1_to_hex(sha1));
}
/*
static void read_rr(struct string_list *rr)
{
struct strbuf buf = STRBUF_INIT;
- FILE *in = fopen(merge_rr_path, "r");
+ FILE *in = fopen(git_path_merge_rr(), "r");
if (!in)
return;
while (!strbuf_getwholeline(&buf, in, '\0')) {
char *path;
unsigned char sha1[20];
+ struct rerere_id *id;
/* There has to be the hash, tab, path and then NUL */
if (buf.len < 42 || get_sha1_hex(buf.buf, sha1))
die("corrupt MERGE_RR");
buf.buf[40] = '\0';
path = buf.buf + 41;
-
- string_list_insert(rr, path)->util = xstrdup(buf.buf);
+ id = new_rerere_id_hex(buf.buf);
+ string_list_insert(rr, path)->util = id;
}
strbuf_release(&buf);
fclose(in);
int i;
for (i = 0; i < rr->nr; i++) {
struct strbuf buf = STRBUF_INIT;
+ struct rerere_id *id;
assert(rr->items[i].util != RERERE_RESOLVED);
- if (!rr->items[i].util)
+
+ id = rr->items[i].util;
+ if (!id)
continue;
strbuf_addf(&buf, "%s\t%s%c",
- (char *)rr->items[i].util,
+ rerere_id_hex(id),
rr->items[i].string, 0);
if (write_in_full(out_fd, buf.buf, buf.len) != buf.len)
die("unable to write rerere record");
int rerere_remaining(struct string_list *merge_rr)
{
int i;
+ if (setup_rerere(merge_rr, RERERE_READONLY))
+ return 0;
if (read_cache() < 0)
return error("Could not read index");
struct string_list_item *it;
it = string_list_lookup(merge_rr, (const char *)e->name);
if (it != NULL) {
- free(it->util);
+ free_rerere_id(it);
it->util = RERERE_RESOLVED;
}
}
}
/*
- * Find the conflict identified by "name"; the change between its
+ * Find the conflict identified by "id"; the change between its
* "preimage" (i.e. a previous contents with conflict markers) and its
* "postimage" (i.e. the corresponding contents with conflicts
* resolved) may apply cleanly to the contents stored in "path", i.e.
* Returns 0 for successful replay of recorded resolution, or non-zero
* for failure.
*/
-static int merge(const char *name, const char *path)
+static int merge(const struct rerere_id *id, const char *path)
{
+ FILE *f;
int ret;
mmfile_t cur = {NULL, 0}, base = {NULL, 0}, other = {NULL, 0};
mmbuffer_t result = {NULL, 0};
* Normalize the conflicts in path and write it out to
* "thisimage" temporary file.
*/
- if (handle_file(path, NULL, rerere_path(name, "thisimage")) < 0)
- return 1;
+ if (handle_file(path, NULL, rerere_path(id, "thisimage")) < 0) {
+ ret = 1;
+ goto out;
+ }
- if (read_mmfile(&cur, rerere_path(name, "thisimage")) ||
- read_mmfile(&base, rerere_path(name, "preimage")) ||
- read_mmfile(&other, rerere_path(name, "postimage"))) {
+ if (read_mmfile(&cur, rerere_path(id, "thisimage")) ||
+ read_mmfile(&base, rerere_path(id, "preimage")) ||
+ read_mmfile(&other, rerere_path(id, "postimage"))) {
ret = 1;
goto out;
}
* low-level merge driver settings.
*/
ret = ll_merge(&result, path, &base, NULL, &cur, "", &other, "", NULL);
- if (!ret) {
- FILE *f;
+ if (ret)
+ goto out;
- /*
- * A successful replay of recorded resolution.
- * Mark that "postimage" was used to help gc.
- */
- if (utime(rerere_path(name, "postimage"), NULL) < 0)
- warning("failed utime() on %s: %s",
- rerere_path(name, "postimage"),
- strerror(errno));
-
- /* Update "path" with the resolution */
- f = fopen(path, "w");
- if (!f)
- return error("Could not open %s: %s", path,
- strerror(errno));
- if (fwrite(result.ptr, result.size, 1, f) != 1)
- error("Could not write %s: %s", path, strerror(errno));
- if (fclose(f))
- return error("Writing %s failed: %s", path,
- strerror(errno));
- }
+ /*
+ * A successful replay of recorded resolution.
+ * Mark that "postimage" was used to help gc.
+ */
+ if (utime(rerere_path(id, "postimage"), NULL) < 0)
+ warning("failed utime() on %s: %s",
+ rerere_path(id, "postimage"),
+ strerror(errno));
+
+ /* Update "path" with the resolution */
+ f = fopen(path, "w");
+ if (!f)
+ return error("Could not open %s: %s", path,
+ strerror(errno));
+ if (fwrite(result.ptr, result.size, 1, f) != 1)
+ error("Could not write %s: %s", path, strerror(errno));
+ if (fclose(f))
+ return error("Writing %s failed: %s", path,
+ strerror(errno));
out:
free(cur.ptr);
struct string_list *update)
{
const char *path = rr_item->string;
- const char *name = (const char *)rr_item->util;
+ const struct rerere_id *id = rr_item->util;
/* Is there a recorded resolution we could attempt to apply? */
- if (has_rerere_resolution(name)) {
- if (merge(name, path))
+ if (has_rerere_resolution(id)) {
+ if (merge(id, path))
return; /* failed to replay */
if (rerere_autoupdate)
fprintf(stderr,
"Resolved '%s' using previous resolution.\n",
path);
- goto mark_resolved;
+ } else if (!handle_file(path, NULL, NULL)) {
+ /* The user has resolved it. */
+ copy_file(rerere_path(id, "postimage"), path, 0666);
+ fprintf(stderr, "Recorded resolution for '%s'.\n", path);
+ } else {
+ return;
}
-
- /* Let's see if the user has resolved it. */
- if (handle_file(path, NULL, NULL))
- return; /* not yet resolved */
-
- copy_file(rerere_path(name, "postimage"), path, 0666);
- fprintf(stderr, "Recorded resolution for '%s'.\n", path);
-mark_resolved:
- free(rr_item->util);
+ free_rerere_id(rr_item);
rr_item->util = NULL;
}
* initial run would catch all and register their preimages.
*/
for (i = 0; i < conflict.nr; i++) {
+ struct rerere_id *id;
unsigned char sha1[20];
- char *hex;
- int ret;
const char *path = conflict.items[i].string;
+ int ret;
if (string_list_has_string(rr, path))
continue;
ret = handle_file(path, sha1, NULL);
if (ret < 1)
continue;
- hex = xstrdup(sha1_to_hex(sha1));
- string_list_insert(rr, path)->util = hex;
+
+ id = new_rerere_id(sha1);
+ string_list_insert(rr, path)->util = id;
/*
* If the directory does not exist, create
* NEEDSWORK: make sure "gc" does not remove
* preimage without removing the directory.
*/
- if (mkdir_in_gitdir(git_path("rr-cache/%s", hex)))
+ if (mkdir_in_gitdir(rerere_path(id, NULL)))
continue;
/*
* conflict. Ask handle_file() to write the
* normalized contents to the "preimage" file.
*/
- handle_file(path, NULL, rerere_path(hex, "preimage"));
+ handle_file(path, NULL, rerere_path(id, "preimage"));
fprintf(stderr, "Recorded preimage for '%s'\n", path);
}
git_config(git_default_config, NULL);
}
+static GIT_PATH_FUNC(git_path_rr_cache, "rr-cache")
+
static int is_rerere_enabled(void)
{
- const char *rr_cache;
int rr_cache_exists;
if (!rerere_enabled)
return 0;
- rr_cache = git_path("rr-cache");
- rr_cache_exists = is_directory(rr_cache);
+ rr_cache_exists = is_directory(git_path_rr_cache());
if (rerere_enabled < 0)
return rr_cache_exists;
- if (!rr_cache_exists && mkdir_in_gitdir(rr_cache))
- die("Could not create directory %s", rr_cache);
+ if (!rr_cache_exists && mkdir_in_gitdir(git_path_rr_cache()))
+ die("Could not create directory %s", git_path_rr_cache());
return 1;
}
if (flags & (RERERE_AUTOUPDATE|RERERE_NOAUTOUPDATE))
rerere_autoupdate = !!(flags & RERERE_AUTOUPDATE);
- merge_rr_path = git_pathdup("MERGE_RR");
- fd = hold_lock_file_for_update(&write_lock, merge_rr_path,
- LOCK_DIE_ON_ERROR);
+ if (flags & RERERE_READONLY)
+ fd = 0;
+ else
+ fd = hold_lock_file_for_update(&write_lock, git_path_merge_rr(),
+ LOCK_DIE_ON_ERROR);
read_rr(merge_rr);
return fd;
}
static int rerere_forget_one_path(const char *path, struct string_list *rr)
{
const char *filename;
- char *hex;
+ struct rerere_id *id;
unsigned char sha1[20];
int ret;
struct string_list_item *item;
return error("Could not parse conflict hunks in '%s'", path);
/* Nuke the recorded resolution for the conflict */
- hex = xstrdup(sha1_to_hex(sha1));
- filename = rerere_path(hex, "postimage");
+ id = new_rerere_id(sha1);
+ filename = rerere_path(id, "postimage");
if (unlink(filename))
return (errno == ENOENT
? error("no remembered resolution for %s", path)
* conflict in the working tree, run us again to record
* the postimage.
*/
- handle_cache(path, sha1, rerere_path(hex, "preimage"));
+ handle_cache(path, sha1, rerere_path(id, "preimage"));
fprintf(stderr, "Updated preimage for '%s'\n", path);
/*
* conflict when the user is done.
*/
item = string_list_insert(rr, path);
- free(item->util);
- item->util = hex;
+ free_rerere_id(item);
+ item->util = id;
fprintf(stderr, "Forgot resolution for %s\n", path);
return 0;
}
return error("Could not read index");
fd = setup_rerere(&merge_rr, RERERE_NOAUTOUPDATE);
+ if (fd < 0)
+ return 0;
/*
* The paths may have been resolved (incorrectly);
/*
* Garbage collection support
*/
-static time_t rerere_created_at(const char *name)
+
+/*
+ * Note that this is not reentrant but is used only one-at-a-time
+ * so it does not matter right now.
+ */
+static struct rerere_id *dirname_to_id(const char *name)
+{
+ static struct rerere_id id;
+ xsnprintf(id.hex, sizeof(id.hex), "%s", name);
+ return &id;
+}
+
+static time_t rerere_created_at(const char *dir_name)
{
struct stat st;
- return stat(rerere_path(name, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
+ struct rerere_id *id = dirname_to_id(dir_name);
+
+ return stat(rerere_path(id, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
}
-static time_t rerere_last_used_at(const char *name)
+static time_t rerere_last_used_at(const char *dir_name)
{
struct stat st;
- return stat(rerere_path(name, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
+ struct rerere_id *id = dirname_to_id(dir_name);
+
+ return stat(rerere_path(id, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
}
/*
* Remove the recorded resolution for a given conflict ID
*/
-static void unlink_rr_item(const char *name)
+static void unlink_rr_item(struct rerere_id *id)
{
- unlink(rerere_path(name, "thisimage"));
- unlink(rerere_path(name, "preimage"));
- unlink(rerere_path(name, "postimage"));
+ unlink(rerere_path(id, "thisimage"));
+ unlink(rerere_path(id, "preimage"));
+ unlink(rerere_path(id, "postimage"));
/*
* NEEDSWORK: what if this rmdir() fails? Wouldn't we then
* assume that we already have preimage recorded in
* do_plain_rerere()?
*/
- rmdir(git_path("rr-cache/%s", name));
+ rmdir(rerere_path(id, NULL));
}
void rerere_gc(struct string_list *rr)
int cutoff_noresolve = 15;
int cutoff_resolve = 60;
+ if (setup_rerere(rr, 0) < 0)
+ return;
+
git_config_get_int("gc.rerereresolved", &cutoff_resolve);
git_config_get_int("gc.rerereunresolved", &cutoff_noresolve);
git_config(git_default_config, NULL);
closedir(dir);
/* ... and then remove them one-by-one */
for (i = 0; i < to_remove.nr; i++)
- unlink_rr_item(to_remove.items[i].string);
+ unlink_rr_item(dirname_to_id(to_remove.items[i].string));
string_list_clear(&to_remove, 0);
+ rollback_lock_file(&write_lock);
}
/*
{
int i;
+ if (setup_rerere(merge_rr, 0) < 0)
+ return;
+
for (i = 0; i < merge_rr->nr; i++) {
- const char *name = (const char *)merge_rr->items[i].util;
- if (!has_rerere_resolution(name))
- unlink_rr_item(name);
+ struct rerere_id *id = merge_rr->items[i].util;
+ if (!has_rerere_resolution(id))
+ unlink_rr_item(id);
}
- unlink_or_warn(git_path("MERGE_RR"));
+ unlink_or_warn(git_path_merge_rr());
+ rollback_lock_file(&write_lock);
}