#define USE_THE_REPOSITORY_VARIABLE #include "git-compat-util.h" #include "abspath.h" #include "config.h" #include "copy.h" #include "gettext.h" #include "hex.h" #include "lockfile.h" #include "string-list.h" #include "read-cache-ll.h" #include "rerere.h" #include "xdiff-interface.h" #include "dir.h" #include "resolve-undo.h" #include "merge-ll.h" #include "path.h" #include "pathspec.h" #include "object-file.h" #include "object-store-ll.h" #include "strmap.h" #define RESOLVED … #define PUNTED … #define THREE_STAGED … void *RERERE_RESOLVED = …; /* if rerere_enabled == -1, fall back to detection of .git/rr-cache */ static int rerere_enabled = …; /* automatically update cleanly resolved paths to the index */ static int rerere_autoupdate; #define RR_HAS_POSTIMAGE … #define RR_HAS_PREIMAGE … struct rerere_dir { … }; static struct strmap rerere_dirs = …; static void free_rerere_dirs(void) { … } static void free_rerere_id(struct string_list_item *item) { … } static const char *rerere_id_hex(const struct rerere_id *id) { … } static void fit_variant(struct rerere_dir *rr_dir, int variant) { … } static void assign_variant(struct rerere_id *id) { … } const char *rerere_path(const struct rerere_id *id, const char *file) { … } static int is_rr_file(const char *name, const char *filename, int *variant) { … } static void scan_rerere_dir(struct rerere_dir *rr_dir) { … } static struct rerere_dir *find_rerere_dir(const char *hex) { … } static int has_rerere_resolution(const struct rerere_id *id) { … } static struct rerere_id *new_rerere_id_hex(char *hex) { … } static struct rerere_id *new_rerere_id(unsigned char *hash) { … } /* * $GIT_DIR/MERGE_RR file is a collection of records, each of which is * "conflict ID", a HT and pathname, terminated with a NUL, and is * used to keep track of the set of paths that "rerere" may need to * 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 struct lock_file write_lock; static int write_rr(struct string_list *rr, int out_fd) { … } /* * "rerere" interacts with conflicted file contents using this I/O * abstraction. It reads a conflicted contents from one place via * "getline()" method, and optionally can write it out after * normalizing the conflicted hunks to the "output". Subclasses of * rerere_io embed this structure at the beginning of their own * rerere_io object. */ struct rerere_io { … }; static void ferr_write(const void *p, size_t count, FILE *fp, int *err) { … } static inline void ferr_puts(const char *s, FILE *fp, int *err) { … } static void rerere_io_putstr(const char *str, struct rerere_io *io) { … } static void rerere_io_putmem(const char *mem, size_t sz, struct rerere_io *io) { … } /* * Subclass of rerere_io that reads from an on-disk file */ struct rerere_io_file { … }; /* * ... and its getline() method implementation */ static int rerere_file_getline(struct strbuf *sb, struct rerere_io *io_) { … } /* * Require the exact number of conflict marker letters, no more, no * less, followed by SP or any whitespace * (including LF). */ static int is_cmarker(char *buf, int marker_char, int marker_size) { … } static void rerere_strbuf_putconflict(struct strbuf *buf, int ch, size_t size) { … } static int handle_conflict(struct strbuf *out, struct rerere_io *io, int marker_size, git_hash_ctx *ctx) { … } /* * Read contents a file with conflicts, normalize the conflicts * by (1) discarding the common ancestor version in diff3-style, * (2) reordering our side and their side so that whichever sorts * alphabetically earlier comes before the other one, while * computing the "conflict ID", which is just an SHA-1 hash of * one side of the conflict, NUL, the other side of the conflict, * and NUL concatenated together. * * 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) { … } /* * Scan the path for conflicts, do the "handle_path()" thing above, and * return the number of conflict hunks found. */ static int handle_file(struct index_state *istate, const char *path, unsigned char *hash, const char *output) { … } /* * Look at a cache entry at "i" and see if it is not conflicting, * conflicting and we are willing to handle, or conflicting and * we are unable to handle, and return the determination in *type. * Return the cache index to be looked at next, by skipping the * stages we have already looked at in this invocation of this * function. */ static int check_one_conflict(struct index_state *istate, int i, int *type) { … } /* * Scan the index and find paths that have conflicts that rerere can * handle, i.e. the ones that has both stages #2 and #3. * * NEEDSWORK: we do not record or replay a previous "resolve by * deletion" for a delete-modify conflict, as that is inherently risky * without knowing what modification is being discarded. The only * safe case, i.e. both side doing the deletion and modification that * are identical to the previous round, might want to be handled, * though. */ static int find_conflict(struct repository *r, struct string_list *conflict) { … } /* * 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 repository *r, struct string_list *merge_rr) { … } /* * Try using the given conflict resolution "ID" to see * if that recorded conflict resolves cleanly what we * got in the "cur". */ static int try_merge(struct index_state *istate, const struct rerere_id *id, const char *path, mmfile_t *cur, mmbuffer_t *result) { … } /* * 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. * the conflict this time around. * * Returns 0 for successful replay of recorded resolution, or non-zero * for failure. */ static int merge(struct index_state *istate, const struct rerere_id *id, const char *path) { … } static void update_paths(struct repository *r, struct string_list *update) { … } static void remove_variant(struct rerere_id *id) { … } /* * The path indicated by rr_item may still have conflict for which we * have a recorded resolution, in which case replay it and optionally * update it. Or it may have been resolved by the user and we may * 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, struct string_list_item *rr_item, struct string_list *update) { … } static int do_plain_rerere(struct repository *r, struct string_list *rr, int fd) { … } static void git_rerere_config(void) { … } static GIT_PATH_FUNC(git_path_rr_cache, "rr-cache") static int is_rerere_enabled(void) { … } int setup_rerere(struct repository *r, struct string_list *merge_rr, int flags) { … } /* * The main entry point that is called internally from codepaths that * perform mergy operations, possibly leaving conflicted index entries * and working tree files. */ int repo_rerere(struct repository *r, int flags) { … } /* * Subclass of rerere_io that reads from an in-core buffer that is a * strbuf */ struct rerere_io_mem { … }; /* * ... and its getline() method implementation */ static int rerere_mem_getline(struct strbuf *sb, struct rerere_io *io_) { … } static int handle_cache(struct index_state *istate, const char *path, unsigned char *hash, const char *output) { … } static int rerere_forget_one_path(struct index_state *istate, const char *path, struct string_list *rr) { … } 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_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 prune_one(struct rerere_id *id, timestamp_t cutoff_resolve, timestamp_t cutoff_noresolve) { … } /* Does the basename in "path" look plausibly like an rr-cache entry? */ static int is_rr_cache_dirname(const char *path) { … } void rerere_gc(struct repository *r, struct string_list *rr) { … } /* * 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 repository *r, struct string_list *merge_rr) { … }