/* * The merge_rr list is meant to hold outstanding conflicted paths * that rerere could handle. Abuse the list by adding other types of * entries to allow the caller to show "rerere remaining". * * - Conflicted paths that rerere does not handle are added * - Conflicted paths that have been resolved are marked as such * by storing RERERE_RESOLVED to .util field (where conflict ID * is expected to be stored). * * Do *not* write MERGE_RR file out after calling this function. * * NEEDSWORK: we may want to fix the caller that implements "rerere * remaining" to do this without abusing merge_rr. */ 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(_("index file corrupt")); for (i = 0; i < active_nr;) { int conflict_type; const struct cache_entry *e = active_cache[i]; i = check_one_conflict(i, &conflict_type); if (conflict_type == PUNTED) string_list_insert(merge_rr, (const char *)e->name); else if (conflict_type == RESOLVED) { struct string_list_item *it; it = string_list_lookup(merge_rr, (const char *)e->name); if (it != NULL) { free_rerere_id(it); it->util = RERERE_RESOLVED; } } } return 0; }
int rerere_forget(struct pathspec *pathspec) { int i, fd; struct string_list conflict = STRING_LIST_INIT_DUP; struct string_list merge_rr = STRING_LIST_INIT_DUP; if (read_cache() < 0) return error(_("index file corrupt")); fd = setup_rerere(&merge_rr, RERERE_NOAUTOUPDATE); if (fd < 0) return 0; /* * The paths may have been resolved (incorrectly); * recover the original conflicted state and then * find the conflicted paths. */ unmerge_cache(pathspec); find_conflict(&conflict); for (i = 0; i < conflict.nr; i++) { struct string_list_item *it = &conflict.items[i]; if (!match_pathspec(&the_index, pathspec, it->string, strlen(it->string), 0, NULL, 0)) continue; rerere_forget_one_path(it->string, &merge_rr); } return write_rr(&merge_rr, fd); }
int rerere(int flags) { struct string_list merge_rr = STRING_LIST_INIT_DUP; int fd; fd = setup_rerere(&merge_rr, flags); if (fd < 0) return 0; return do_plain_rerere(&merge_rr, fd); }
int rerere(void) { struct string_list merge_rr = { NULL, 0, 0, 1 }; int fd; fd = setup_rerere(&merge_rr); if (fd < 0) return 0; return do_plain_rerere(&merge_rr, fd); }
/* * The main entry point that is called internally from codepaths that * perform mergy operations, possibly leaving conflicted index entries * and working tree files. */ int rerere(int flags) { struct string_list merge_rr = STRING_LIST_INIT_DUP; int fd, status; fd = setup_rerere(&merge_rr, flags); if (fd < 0) return 0; status = do_plain_rerere(&merge_rr, fd); free_rerere_dirs(); return status; }
void rerere_gc(struct string_list *rr) { struct string_list to_remove = STRING_LIST_INIT_DUP; DIR *dir; struct dirent *e; int i; timestamp_t now = time(NULL); timestamp_t cutoff_noresolve = now - 15 * 86400; timestamp_t cutoff_resolve = now - 60 * 86400; if (setup_rerere(rr, 0) < 0) return; git_config_get_expiry_in_days("gc.rerereresolved", &cutoff_resolve, now); git_config_get_expiry_in_days("gc.rerereunresolved", &cutoff_noresolve, now); git_config(git_default_config, NULL); dir = opendir(git_path("rr-cache")); if (!dir) die_errno(_("unable to open rr-cache directory")); /* Collect stale conflict IDs ... */ while ((e = readdir(dir))) { struct rerere_dir *rr_dir; struct rerere_id id; int now_empty; if (is_dot_or_dotdot(e->d_name)) continue; rr_dir = find_rerere_dir(e->d_name); if (!rr_dir) continue; /* or should we remove 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); if (id.collection->status[id.variant]) now_empty = 0; } if (now_empty) string_list_append(&to_remove, e->d_name); } closedir(dir); /* ... and then remove the empty directories */ for (i = 0; i < to_remove.nr; i++) rmdir(git_path("rr-cache/%s", to_remove.items[i].string)); string_list_clear(&to_remove, 0); rollback_lock_file(&write_lock); }
/* * During a conflict resolution, after "rerere" recorded the * preimages, abandon them if the user did not resolve them or * record their resolutions. And drop $GIT_DIR/MERGE_RR. * * NEEDSWORK: shouldn't we be calling this from "reset --hard"? */ void rerere_clear(struct string_list *merge_rr) { int i; if (setup_rerere(merge_rr, 0) < 0) return; for (i = 0; i < merge_rr->nr; i++) { 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()); rollback_lock_file(&write_lock); }
static int sequencer_rollback(struct replay_opts *opts) { const char *filename; FILE *f; unsigned char sha1[20]; struct strbuf buf = STRBUF_INIT; struct string_list merge_rr = STRING_LIST_INIT_DUP; if (setup_rerere(&merge_rr, 0) >= 0) { rerere_clear(&merge_rr); string_list_clear(&merge_rr, 1); } filename = git_path(SEQ_HEAD_FILE); f = fopen(filename, "r"); if (!f && errno == ENOENT) { /* * There is no multiple-cherry-pick in progress. * If CHERRY_PICK_HEAD or REVERT_HEAD indicates * a single-cherry-pick in progress, abort that. */ return rollback_single_pick(); } if (!f) return error(_("cannot open %s: %s"), filename, strerror(errno)); if (strbuf_getline(&buf, f, '\n')) { error(_("cannot read %s: %s"), filename, ferror(f) ? strerror(errno) : _("unexpected end of file")); fclose(f); goto fail; } fclose(f); if (get_sha1_hex(buf.buf, sha1) || buf.buf[40] != '\0') { error(_("stored pre-cherry-pick HEAD file '%s' is corrupt"), filename); goto fail; } if (reset_for_rollback(sha1)) goto fail; remove_sequencer_state(); strbuf_release(&buf); return 0; fail: strbuf_release(&buf); return -1; }
void rerere_gc(struct string_list *rr) { struct string_list to_remove = STRING_LIST_INIT_DUP; DIR *dir; struct dirent *e; int i, cutoff; time_t now = time(NULL), then; 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); dir = opendir(git_path("rr-cache")); if (!dir) die_errno("unable to open rr-cache directory"); /* Collect stale conflict IDs ... */ while ((e = readdir(dir))) { if (is_dot_or_dotdot(e->d_name)) continue; then = rerere_last_used_at(e->d_name); if (then) { cutoff = cutoff_resolve; } else { then = rerere_created_at(e->d_name); if (!then) continue; cutoff = cutoff_noresolve; } if (then < now - cutoff * 86400) string_list_append(&to_remove, e->d_name); } closedir(dir); /* ... and then remove them one-by-one */ for (i = 0; i < to_remove.nr; i++) unlink_rr_item(dirname_to_id(to_remove.items[i].string)); string_list_clear(&to_remove, 0); rollback_lock_file(&write_lock); }
int rerere_forget(struct pathspec *pathspec) { int i, fd; struct string_list conflict = STRING_LIST_INIT_DUP; struct string_list merge_rr = STRING_LIST_INIT_DUP; if (read_cache() < 0) return error("Could not read index"); fd = setup_rerere(&merge_rr, RERERE_NOAUTOUPDATE); unmerge_cache(pathspec); find_conflict(&conflict); for (i = 0; i < conflict.nr; i++) { struct string_list_item *it = &conflict.items[i]; if (!match_pathspec(pathspec, it->string, strlen(it->string), 0, NULL, 0)) continue; rerere_forget_one_path(it->string, &merge_rr); } return write_rr(&merge_rr, fd); }
static int sequencer_skip(struct replay_opts *opts) { const char *argv[3]; /* reset --hard + NULL */ struct string_list merge_rr = STRING_LIST_INIT_DUP; int ret; if (setup_rerere(&merge_rr, 0) >= 0) { rerere_clear(&merge_rr); string_list_clear(&merge_rr, 1); } argv[0] = "reset"; argv[1] = "--hard"; argv[2] = NULL; ret = run_command_v_opt(argv, RUN_GIT_CMD); if (ret) return ret; discard_cache(); read_cache(); return sequencer_continue(opts, 1); }
int cmd_rerere(int argc, const char **argv, const char *prefix) { struct string_list merge_rr = { NULL, 0, 0, 1 }; int i, fd; if (argc < 2) return rerere(); fd = setup_rerere(&merge_rr); if (fd < 0) return 0; if (!strcmp(argv[1], "clear")) { for (i = 0; i < merge_rr.nr; i++) { const char *name = (const char *)merge_rr.items[i].util; if (!has_resolution(name)) unlink_rr_item(name); } unlink(git_path("rr-cache/MERGE_RR")); } else if (!strcmp(argv[1], "gc")) garbage_collect(&merge_rr); else if (!strcmp(argv[1], "status")) for (i = 0; i < merge_rr.nr; i++) printf("%s\n", merge_rr.items[i].string); else if (!strcmp(argv[1], "diff")) for (i = 0; i < merge_rr.nr; i++) { const char *path = merge_rr.items[i].string; const char *name = (const char *)merge_rr.items[i].util; diff_two(rr_path(name, "preimage"), path, path, path); } else usage(git_rerere_usage); string_list_clear(&merge_rr, 1); return 0; }
int cmd_rerere(int argc, const char **argv, const char *prefix) { struct string_list merge_rr = STRING_LIST_INIT_DUP; int i, fd, autoupdate = -1, flags = 0; struct option options[] = { OPT_SET_INT(0, "rerere-autoupdate", &autoupdate, N_("register clean resolutions in index"), 1), OPT_END(), }; argc = parse_options(argc, argv, prefix, options, rerere_usage, 0); git_config(git_xmerge_config, NULL); if (autoupdate == 1) flags = RERERE_AUTOUPDATE; if (autoupdate == 0) flags = RERERE_NOAUTOUPDATE; if (argc < 1) return rerere(flags); if (!strcmp(argv[0], "forget")) { struct pathspec pathspec; if (argc < 2) warning("'git rerere forget' without paths is deprecated"); parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD, prefix, argv + 1); return rerere_forget(&pathspec); } fd = setup_rerere(&merge_rr, flags); if (fd < 0) return 0; if (!strcmp(argv[0], "clear")) { rerere_clear(&merge_rr); } else if (!strcmp(argv[0], "gc")) rerere_gc(&merge_rr); else if (!strcmp(argv[0], "status")) for (i = 0; i < merge_rr.nr; i++) printf("%s\n", merge_rr.items[i].string); else if (!strcmp(argv[0], "remaining")) { rerere_remaining(&merge_rr); for (i = 0; i < merge_rr.nr; i++) { if (merge_rr.items[i].util != RERERE_RESOLVED) printf("%s\n", merge_rr.items[i].string); else /* prepare for later call to * string_list_clear() */ merge_rr.items[i].util = NULL; } } else if (!strcmp(argv[0], "diff")) for (i = 0; i < merge_rr.nr; i++) { const char *path = merge_rr.items[i].string; const char *name = (const char *)merge_rr.items[i].util; diff_two(rerere_path(name, "preimage"), path, path, path); } else usage_with_options(rerere_usage, options); string_list_clear(&merge_rr, 1); return 0; }