diff options
Diffstat (limited to 'merge-recursive.c')
-rw-r--r-- | merge-recursive.c | 2384 |
1 files changed, 1346 insertions, 1038 deletions
diff --git a/merge-recursive.c b/merge-recursive.c index 16f6a0f98b..680937c39e 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -3,59 +3,50 @@ * Fredrik Kuivinen. * The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006 */ +#include "advice.h" #include "cache.h" #include "cache-tree.h" #include "commit.h" #include "blob.h" +#include "builtin.h" #include "tree-walk.h" #include "diff.h" #include "diffcore.h" -#include "run-command.h" #include "tag.h" #include "unpack-trees.h" -#include "path-list.h" +#include "string-list.h" #include "xdiff-interface.h" -#include "interpolate.h" +#include "ll-merge.h" #include "attr.h" +#include "merge-recursive.h" +#include "dir.h" +#include "submodule.h" -static int subtree_merge; - -static struct tree *shift_tree_object(struct tree *one, struct tree *two) +static struct tree *shift_tree_object(struct tree *one, struct tree *two, + const char *subtree_shift) { unsigned char shifted[20]; - /* - * NEEDSWORK: this limits the recursion depth to hardcoded - * value '2' to avoid excessive overhead. - */ - shift_tree(one->object.sha1, two->object.sha1, shifted, 2); + if (!*subtree_shift) { + shift_tree(one->object.sha1, two->object.sha1, shifted, 0); + } else { + shift_tree_by(one->object.sha1, two->object.sha1, shifted, + subtree_shift); + } if (!hashcmp(two->object.sha1, shifted)) return two; return lookup_tree(shifted); } -/* - * A virtual commit has - * - (const char *)commit->util set to the name, and - * - *(int *)commit->object.sha1 set to the virtual id. - */ - -static unsigned commit_list_count(const struct commit_list *l) -{ - unsigned c = 0; - for (; l; l = l->next ) - c++; - return c; -} - static struct commit *make_virtual_commit(struct tree *tree, const char *comment) { struct commit *commit = xcalloc(1, sizeof(struct commit)); - static unsigned virtual_id = 1; + struct merge_remote_desc *desc = xmalloc(sizeof(*desc)); + + desc->name = comment; + desc->obj = (struct object *)commit; commit->tree = tree; - commit->util = (void*)comment; - *(int*)commit->object.sha1 = virtual_id++; - /* avoid warnings */ + commit->util = desc; commit->object.parsed = 1; return commit; } @@ -71,130 +62,141 @@ static int sha_eq(const unsigned char *a, const unsigned char *b) return a && b && hashcmp(a, b) == 0; } +enum rename_type { + RENAME_NORMAL = 0, + RENAME_DELETE, + RENAME_ONE_FILE_TO_ONE, + RENAME_ONE_FILE_TO_TWO, + RENAME_TWO_FILES_TO_ONE +}; + +struct rename_conflict_info { + enum rename_type rename_type; + struct diff_filepair *pair1; + struct diff_filepair *pair2; + const char *branch1; + const char *branch2; + struct stage_data *dst_entry1; + struct stage_data *dst_entry2; + struct diff_filespec ren1_other; + struct diff_filespec ren2_other; +}; + /* * Since we want to write the index eventually, we cannot reuse the index * for these (temporary) data. */ -struct stage_data -{ - struct - { +struct stage_data { + struct { unsigned mode; unsigned char sha[20]; } stages[4]; + struct rename_conflict_info *rename_conflict_info; unsigned processed:1; }; -struct output_buffer +static inline void setup_rename_conflict_info(enum rename_type rename_type, + struct diff_filepair *pair1, + struct diff_filepair *pair2, + const char *branch1, + const char *branch2, + struct stage_data *dst_entry1, + struct stage_data *dst_entry2, + struct merge_options *o, + struct stage_data *src_entry1, + struct stage_data *src_entry2) { - struct output_buffer *next; - char *str; -}; + struct rename_conflict_info *ci = xcalloc(1, sizeof(struct rename_conflict_info)); + ci->rename_type = rename_type; + ci->pair1 = pair1; + ci->branch1 = branch1; + ci->branch2 = branch2; + + ci->dst_entry1 = dst_entry1; + dst_entry1->rename_conflict_info = ci; + dst_entry1->processed = 0; + + assert(!pair2 == !dst_entry2); + if (dst_entry2) { + ci->dst_entry2 = dst_entry2; + ci->pair2 = pair2; + dst_entry2->rename_conflict_info = ci; + } + + if (rename_type == RENAME_TWO_FILES_TO_ONE) { + /* + * For each rename, there could have been + * modifications on the side of history where that + * file was not renamed. + */ + int ostage1 = o->branch1 == branch1 ? 3 : 2; + int ostage2 = ostage1 ^ 1; -static struct path_list current_file_set = {NULL, 0, 0, 1}; -static struct path_list current_directory_set = {NULL, 0, 0, 1}; + ci->ren1_other.path = pair1->one->path; + hashcpy(ci->ren1_other.sha1, src_entry1->stages[ostage1].sha); + ci->ren1_other.mode = src_entry1->stages[ostage1].mode; -static int call_depth = 0; -static int verbosity = 2; -static int buffer_output = 1; -static struct output_buffer *output_list, *output_end; + ci->ren2_other.path = pair2->one->path; + hashcpy(ci->ren2_other.sha1, src_entry2->stages[ostage2].sha); + ci->ren2_other.mode = src_entry2->stages[ostage2].mode; + } +} -static int show (int v) +static int show(struct merge_options *o, int v) { - return (!call_depth && verbosity >= v) || verbosity >= 5; + return (!o->call_depth && o->verbosity >= v) || o->verbosity >= 5; } -static void output(int v, const char *fmt, ...) +static void flush_output(struct merge_options *o) { - va_list args; - va_start(args, fmt); - if (buffer_output && show(v)) { - struct output_buffer *b = xmalloc(sizeof(*b)); - nfvasprintf(&b->str, fmt, args); - b->next = NULL; - if (output_end) - output_end->next = b; - else - output_list = b; - output_end = b; - } else if (show(v)) { - int i; - for (i = call_depth; i--;) - fputs(" ", stdout); - vfprintf(stdout, fmt, args); - fputc('\n', stdout); + if (o->obuf.len) { + fputs(o->obuf.buf, stdout); + strbuf_reset(&o->obuf); } - va_end(args); } -static void flush_output(void) +__attribute__((format (printf, 3, 4))) +static void output(struct merge_options *o, int v, const char *fmt, ...) { - struct output_buffer *b, *n; - for (b = output_list; b; b = n) { - int i; - for (i = call_depth; i--;) - fputs(" ", stdout); - fputs(b->str, stdout); - fputc('\n', stdout); - n = b->next; - free(b->str); - free(b); - } - output_list = NULL; - output_end = NULL; + va_list ap; + + if (!show(o, v)) + return; + + strbuf_grow(&o->obuf, o->call_depth * 2 + 2); + memset(o->obuf.buf + o->obuf.len, ' ', o->call_depth * 2); + strbuf_setlen(&o->obuf, o->obuf.len + o->call_depth * 2); + + va_start(ap, fmt); + strbuf_vaddf(&o->obuf, fmt, ap); + va_end(ap); + + strbuf_add(&o->obuf, "\n", 1); + if (!o->buffer_output) + flush_output(o); } -static void output_commit_title(struct commit *commit) +static void output_commit_title(struct merge_options *o, struct commit *commit) { int i; - flush_output(); - for (i = call_depth; i--;) + flush_output(o); + for (i = o->call_depth; i--;) fputs(" ", stdout); if (commit->util) - printf("virtual %s\n", (char *)commit->util); + printf("virtual %s\n", merge_remote_util(commit)->name); else { printf("%s ", find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV)); if (parse_commit(commit) != 0) printf("(bad commit)\n"); else { - const char *s; - int len; - for (s = commit->buffer; *s; s++) - if (*s == '\n' && s[1] == '\n') { - s += 2; - break; - } - for (len = 0; s[len] && '\n' != s[len]; len++) - ; /* do nothing */ - printf("%.*s\n", len, s); + const char *title; + int len = find_commit_subject(commit->buffer, &title); + if (len) + printf("%.*s\n", len, title); } } } -static struct cache_entry *make_cache_entry(unsigned int mode, - const unsigned char *sha1, const char *path, int stage, int refresh) -{ - int size, len; - struct cache_entry *ce; - - if (!verify_path(path)) - return NULL; - - len = strlen(path); - size = cache_entry_size(len); - ce = xcalloc(1, size); - - hashcpy(ce->sha1, sha1); - memcpy(ce->name, path, len); - ce->ce_flags = create_ce_flags(len, stage); - ce->ce_mode = create_ce_mode(mode); - - if (refresh) - return refresh_cache_entry(ce, 0); - - return ce; -} - static int add_cacheinfo(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh, int options) { @@ -205,17 +207,6 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1, return add_cache_entry(ce, options); } -/* - * This is a global variable which is used in a number of places but - * only written to in the 'merge' function. - * - * index_only == 1 => Don't leave any non-stage 0 entries in the cache and - * don't update the working directory. - * 0 => Leave unmerged entries in the cache and update - * the working directory. - */ -static int index_only = 0; - static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree) { parse_tree(tree); @@ -239,6 +230,9 @@ static int git_merge_trees(int index_only, opts.merge = 1; opts.head_idx = 2; opts.fn = threeway_merge; + opts.src_index = &the_index; + opts.dst_index = &the_index; + setup_unpack_trees_porcelain(&opts, "merge"); init_tree_desc_from_tree(t+0, common); init_tree_desc_from_tree(t+1, head); @@ -249,30 +243,20 @@ static int git_merge_trees(int index_only, return rc; } -static int unmerged_index(void) -{ - int i; - for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; - if (ce_stage(ce)) - return 1; - } - return 0; -} - -static struct tree *git_write_tree(void) +struct tree *write_tree_from_memory(struct merge_options *o) { struct tree *result = NULL; - if (unmerged_index()) { + if (unmerged_cache()) { int i; - output(0, "There are unmerged index entries:"); + fprintf(stderr, "BUG: There are unmerged index entries:\n"); for (i = 0; i < active_nr; i++) { struct cache_entry *ce = active_cache[i]; if (ce_stage(ce)) - output(0, "%d %.*s", ce_stage(ce), ce_namelen(ce), ce->name); + fprintf(stderr, "BUG: %d %.*s\n", ce_stage(ce), + (int)ce_namelen(ce), ce->name); } - return NULL; + die("Bug in merge-recursive.c"); } if (!active_cache_tree) @@ -280,7 +264,7 @@ static struct tree *git_write_tree(void) if (!cache_tree_fully_valid(active_cache_tree) && cache_tree_update(active_cache_tree, - active_cache, active_nr, 0, 0) < 0) + active_cache, active_nr, 0) < 0) die("error building trees"); result = lookup_tree(active_cache_tree->sha1); @@ -290,41 +274,45 @@ static struct tree *git_write_tree(void) static int save_files_dirs(const unsigned char *sha1, const char *base, int baselen, const char *path, - unsigned int mode, int stage) + unsigned int mode, int stage, void *context) { int len = strlen(path); char *newpath = xmalloc(baselen + len + 1); + struct merge_options *o = context; + memcpy(newpath, base, baselen); memcpy(newpath + baselen, path, len); newpath[baselen + len] = '\0'; if (S_ISDIR(mode)) - path_list_insert(newpath, ¤t_directory_set); + string_list_insert(&o->current_directory_set, newpath); else - path_list_insert(newpath, ¤t_file_set); + string_list_insert(&o->current_file_set, newpath); free(newpath); - return READ_TREE_RECURSIVE; + return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0); } -static int get_files_dirs(struct tree *tree) +static int get_files_dirs(struct merge_options *o, struct tree *tree) { int n; - if (read_tree_recursive(tree, "", 0, 0, NULL, save_files_dirs) != 0) + struct pathspec match_all; + init_pathspec(&match_all, NULL); + if (read_tree_recursive(tree, "", 0, 0, &match_all, save_files_dirs, o)) return 0; - n = current_file_set.nr + current_directory_set.nr; + n = o->current_file_set.nr + o->current_directory_set.nr; return n; } /* - * Returns a index_entry instance which doesn't have to correspond to + * Returns an index_entry instance which doesn't have to correspond to * a real cache entry in Git's index. */ static struct stage_data *insert_stage_data(const char *path, struct tree *o, struct tree *a, struct tree *b, - struct path_list *entries) + struct string_list *entries) { - struct path_list_item *item; + struct string_list_item *item; struct stage_data *e = xcalloc(1, sizeof(struct stage_data)); get_tree_entry(o->object.sha1, path, e->stages[1].sha, &e->stages[1].mode); @@ -332,7 +320,7 @@ static struct stage_data *insert_stage_data(const char *path, e->stages[2].sha, &e->stages[2].mode); get_tree_entry(b->object.sha1, path, e->stages[3].sha, &e->stages[3].mode); - item = path_list_insert(path, entries); + item = string_list_insert(entries, path); item->util = e; return e; } @@ -341,35 +329,136 @@ static struct stage_data *insert_stage_data(const char *path, * Create a dictionary mapping file names to stage_data objects. The * dictionary contains one entry for every path with a non-zero stage entry. */ -static struct path_list *get_unmerged(void) +static struct string_list *get_unmerged(void) { - struct path_list *unmerged = xcalloc(1, sizeof(struct path_list)); + struct string_list *unmerged = xcalloc(1, sizeof(struct string_list)); int i; - unmerged->strdup_paths = 1; + unmerged->strdup_strings = 1; for (i = 0; i < active_nr; i++) { - struct path_list_item *item; + struct string_list_item *item; struct stage_data *e; struct cache_entry *ce = active_cache[i]; if (!ce_stage(ce)) continue; - item = path_list_lookup(ce->name, unmerged); + item = string_list_lookup(unmerged, ce->name); if (!item) { - item = path_list_insert(ce->name, unmerged); + item = string_list_insert(unmerged, ce->name); item->util = xcalloc(1, sizeof(struct stage_data)); } e = item->util; - e->stages[ce_stage(ce)].mode = ntohl(ce->ce_mode); + e->stages[ce_stage(ce)].mode = ce->ce_mode; hashcpy(e->stages[ce_stage(ce)].sha, ce->sha1); } return unmerged; } -struct rename +static int string_list_df_name_compare(const void *a, const void *b) +{ + const struct string_list_item *one = a; + const struct string_list_item *two = b; + int onelen = strlen(one->string); + int twolen = strlen(two->string); + /* + * Here we only care that entries for D/F conflicts are + * adjacent, in particular with the file of the D/F conflict + * appearing before files below the corresponding directory. + * The order of the rest of the list is irrelevant for us. + * + * To achieve this, we sort with df_name_compare and provide + * the mode S_IFDIR so that D/F conflicts will sort correctly. + * We use the mode S_IFDIR for everything else for simplicity, + * since in other cases any changes in their order due to + * sorting cause no problems for us. + */ + int cmp = df_name_compare(one->string, onelen, S_IFDIR, + two->string, twolen, S_IFDIR); + /* + * Now that 'foo' and 'foo/bar' compare equal, we have to make sure + * that 'foo' comes before 'foo/bar'. + */ + if (cmp) + return cmp; + return onelen - twolen; +} + +static void record_df_conflict_files(struct merge_options *o, + struct string_list *entries) { + /* If there is a D/F conflict and the file for such a conflict + * currently exist in the working tree, we want to allow it to be + * removed to make room for the corresponding directory if needed. + * The files underneath the directories of such D/F conflicts will + * be processed before the corresponding file involved in the D/F + * conflict. If the D/F directory ends up being removed by the + * merge, then we won't have to touch the D/F file. If the D/F + * directory needs to be written to the working copy, then the D/F + * file will simply be removed (in make_room_for_path()) to make + * room for the necessary paths. Note that if both the directory + * and the file need to be present, then the D/F file will be + * reinstated with a new unique name at the time it is processed. + */ + struct string_list df_sorted_entries; + const char *last_file = NULL; + int last_len = 0; + int i; + + /* + * If we're merging merge-bases, we don't want to bother with + * any working directory changes. + */ + if (o->call_depth) + return; + + /* Ensure D/F conflicts are adjacent in the entries list. */ + memset(&df_sorted_entries, 0, sizeof(struct string_list)); + for (i = 0; i < entries->nr; i++) { + struct string_list_item *next = &entries->items[i]; + string_list_append(&df_sorted_entries, next->string)->util = + next->util; + } + qsort(df_sorted_entries.items, entries->nr, sizeof(*entries->items), + string_list_df_name_compare); + + string_list_clear(&o->df_conflict_file_set, 1); + for (i = 0; i < df_sorted_entries.nr; i++) { + const char *path = df_sorted_entries.items[i].string; + int len = strlen(path); + struct stage_data *e = df_sorted_entries.items[i].util; + + /* + * Check if last_file & path correspond to a D/F conflict; + * i.e. whether path is last_file+'/'+<something>. + * If so, record that it's okay to remove last_file to make + * room for path and friends if needed. + */ + if (last_file && + len > last_len && + memcmp(path, last_file, last_len) == 0 && + path[last_len] == '/') { + string_list_insert(&o->df_conflict_file_set, last_file); + } + + /* + * Determine whether path could exist as a file in the + * working directory as a possible D/F conflict. This + * will only occur when it exists in stage 2 as a + * file. + */ + if (S_ISREG(e->stages[2].mode) || S_ISLNK(e->stages[2].mode)) { + last_file = path; + last_len = len; + } else { + last_file = NULL; + } + } + string_list_clear(&df_sorted_entries, 0); +} + +struct rename { struct diff_filepair *pair; struct stage_data *src_entry; struct stage_data *dst_entry; @@ -382,27 +471,36 @@ struct rename * 'b_tree') to be able to associate the correct cache entries with * the rename information. 'tree' is always equal to either a_tree or b_tree. */ -static struct path_list *get_renames(struct tree *tree, - struct tree *o_tree, - struct tree *a_tree, - struct tree *b_tree, - struct path_list *entries) +static struct string_list *get_renames(struct merge_options *o, + struct tree *tree, + struct tree *o_tree, + struct tree *a_tree, + struct tree *b_tree, + struct string_list *entries) { int i; - struct path_list *renames; + struct string_list *renames; struct diff_options opts; - renames = xcalloc(1, sizeof(struct path_list)); + renames = xcalloc(1, sizeof(struct string_list)); diff_setup(&opts); - opts.recursive = 1; + DIFF_OPT_SET(&opts, RECURSIVE); + DIFF_OPT_CLR(&opts, RENAME_EMPTY); opts.detect_rename = DIFF_DETECT_RENAME; + opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit : + o->diff_rename_limit >= 0 ? o->diff_rename_limit : + 1000; + opts.rename_score = o->rename_score; + opts.show_rename_progress = o->show_rename_progress; opts.output_format = DIFF_FORMAT_NO_OUTPUT; if (diff_setup_done(&opts) < 0) die("diff setup failed"); diff_tree_sha1(o_tree->object.sha1, tree->object.sha1, "", &opts); diffcore_std(&opts); + if (opts.needed_rename_limit > o->needed_rename_limit) + o->needed_rename_limit = opts.needed_rename_limit; for (i = 0; i < diff_queued_diff.nr; ++i) { - struct path_list_item *item; + struct string_list_item *item; struct rename *re; struct diff_filepair *pair = diff_queued_diff.queue[i]; if (pair->status != 'R') { @@ -412,20 +510,20 @@ static struct path_list *get_renames(struct tree *tree, re = xmalloc(sizeof(*re)); re->processed = 0; re->pair = pair; - item = path_list_lookup(re->pair->one->path, entries); + item = string_list_lookup(entries, re->pair->one->path); if (!item) re->src_entry = insert_stage_data(re->pair->one->path, o_tree, a_tree, b_tree, entries); else re->src_entry = item->util; - item = path_list_lookup(re->pair->two->path, entries); + item = string_list_lookup(entries, re->pair->two->path); if (!item) re->dst_entry = insert_stage_data(re->pair->two->path, o_tree, a_tree, b_tree, entries); else re->dst_entry = item->util; - item = path_list_insert(pair->one->path, renames); + item = string_list_insert(renames, pair->one->path); item->util = re; } opts.output_format = DIFF_FORMAT_NO_OUTPUT; @@ -434,11 +532,21 @@ static struct path_list *get_renames(struct tree *tree, return renames; } -static int update_stages(const char *path, struct diff_filespec *o, - struct diff_filespec *a, struct diff_filespec *b, - int clear) +static int update_stages(const char *path, const struct diff_filespec *o, + const struct diff_filespec *a, + const struct diff_filespec *b) { - int options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE; + + /* + * NOTE: It is usually a bad idea to call update_stages on a path + * before calling update_file on that same path, since it can + * sometimes lead to spurious "refusing to lose untracked file..." + * messages from update_file (via make_room_for path via + * would_lose_untracked). Instead, reverse the order of the calls + * (executing update_file first and then update_stages). + */ + int clear = 1; + int options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_SKIP_DFCHECK; if (clear) if (remove_file_from_cache(path)) return -1; @@ -454,47 +562,38 @@ static int update_stages(const char *path, struct diff_filespec *o, return 0; } -static int remove_path(const char *name) +static void update_entry(struct stage_data *entry, + struct diff_filespec *o, + struct diff_filespec *a, + struct diff_filespec *b) { - int ret, len; - char *slash, *dirs; - - ret = unlink(name); - if (ret) - return ret; - len = strlen(name); - dirs = xmalloc(len+1); - memcpy(dirs, name, len); - dirs[len] = '\0'; - while ((slash = strrchr(name, '/'))) { - *slash = '\0'; - len = slash - name; - if (rmdir(name) != 0) - break; - } - free(dirs); - return ret; + entry->processed = 0; + entry->stages[1].mode = o->mode; + entry->stages[2].mode = a->mode; + entry->stages[3].mode = b->mode; + hashcpy(entry->stages[1].sha, o->sha1); + hashcpy(entry->stages[2].sha, a->sha1); + hashcpy(entry->stages[3].sha, b->sha1); } -static int remove_file(int clean, const char *path, int no_wd) +static int remove_file(struct merge_options *o, int clean, + const char *path, int no_wd) { - int update_cache = index_only || clean; - int update_working_directory = !index_only && !no_wd; + int update_cache = o->call_depth || clean; + int update_working_directory = !o->call_depth && !no_wd; if (update_cache) { if (remove_file_from_cache(path)) return -1; } if (update_working_directory) { - unlink(path); - if (errno != ENOENT || errno != EISDIR) + if (remove_path(path)) return -1; - remove_path(path); } return 0; } -static char *unique_path(const char *path, const char *branch) +static char *unique_path(struct merge_options *o, const char *path, const char *branch) { char *newpath = xmalloc(strlen(path) + 1 + strlen(branch) + 8 + 1); int suffix = 0; @@ -506,24 +605,15 @@ static char *unique_path(const char *path, const char *branch) for (; *p; ++p) if ('/' == *p) *p = '_'; - while (path_list_has_path(¤t_file_set, newpath) || - path_list_has_path(¤t_directory_set, newpath) || + while (string_list_has_string(&o->current_file_set, newpath) || + string_list_has_string(&o->current_directory_set, newpath) || lstat(newpath, &st) == 0) sprintf(p, "_%d", suffix++); - path_list_insert(newpath, ¤t_file_set); + string_list_insert(&o->current_file_set, newpath); return newpath; } -static int mkdir_p(const char *path, unsigned long mode) -{ - /* path points to cache entries, so xstrdup before messing with it */ - char *buf = xstrdup(path); - int result = safe_create_leading_directories(buf); - free(buf); - return result; -} - static void flush_buffer(int fd, const char *buf, unsigned long size) { while (size > 0) { @@ -532,7 +622,7 @@ static void flush_buffer(int fd, const char *buf, unsigned long size) /* Ignore epipe */ if (errno == EPIPE) break; - die("merge-recursive: %s", strerror(errno)); + die_errno("merge-recursive"); } else if (!ret) { die("merge-recursive: disk full?"); } @@ -541,12 +631,84 @@ static void flush_buffer(int fd, const char *buf, unsigned long size) } } -static int make_room_for_path(const char *path) +static int dir_in_way(const char *path, int check_working_copy) +{ + int pos, pathlen = strlen(path); + char *dirpath = xmalloc(pathlen + 2); + struct stat st; + + strcpy(dirpath, path); + dirpath[pathlen] = '/'; + dirpath[pathlen+1] = '\0'; + + pos = cache_name_pos(dirpath, pathlen+1); + + if (pos < 0) + pos = -1 - pos; + if (pos < active_nr && + !strncmp(dirpath, active_cache[pos]->name, pathlen+1)) { + free(dirpath); + return 1; + } + + free(dirpath); + return check_working_copy && !lstat(path, &st) && S_ISDIR(st.st_mode); +} + +static int was_tracked(const char *path) +{ + int pos = cache_name_pos(path, strlen(path)); + + if (pos < 0) + pos = -1 - pos; + while (pos < active_nr && + !strcmp(path, active_cache[pos]->name)) { + /* + * If stage #0, it is definitely tracked. + * If it has stage #2 then it was tracked + * before this merge started. All other + * cases the path was not tracked. + */ + switch (ce_stage(active_cache[pos])) { + case 0: + case 2: + return 1; + } + pos++; + } + return 0; +} + +static int would_lose_untracked(const char *path) +{ + return !was_tracked(path) && file_exists(path); +} + +static int make_room_for_path(struct merge_options *o, const char *path) { - int status; + int status, i; const char *msg = "failed to create path '%s'%s"; - status = mkdir_p(path, 0777); + /* Unlink any D/F conflict files that are in the way */ + for (i = 0; i < o->df_conflict_file_set.nr; i++) { + const char *df_path = o->df_conflict_file_set.items[i].string; + size_t pathlen = strlen(path); + size_t df_pathlen = strlen(df_path); + if (df_pathlen < pathlen && + path[df_pathlen] == '/' && + strncmp(path, df_path, df_pathlen) == 0) { + output(o, 3, + "Removing %s to make room for subdirectory\n", + df_path); + unlink(df_path); + unsorted_string_list_delete_item(&o->df_conflict_file_set, + i, 0); + break; + } + } + + /* Make sure leading directories are created */ + status = safe_create_leading_directories_const(path); if (status) { if (status == -3) { /* something else exists */ @@ -556,6 +718,14 @@ static int make_room_for_path(const char *path) die(msg, path, ""); } + /* + * Do not unlink a file in the work tree if we are not + * tracking it. + */ + if (would_lose_untracked(path)) + return error("refusing to lose untracked file at '%s'", + path); + /* Successful unlink is good.. */ if (!unlink(path)) return 0; @@ -566,13 +736,14 @@ static int make_room_for_path(const char *path) return error(msg, path, ": perhaps a D/F conflict?"); } -static void update_file_flags(const unsigned char *sha, +static void update_file_flags(struct merge_options *o, + const unsigned char *sha, unsigned mode, const char *path, int update_cache, int update_wd) { - if (index_only) + if (o->call_depth) update_wd = 0; if (update_wd) { @@ -580,14 +751,33 @@ static void update_file_flags(const unsigned char *sha, void *buf; unsigned long size; + if (S_ISGITLINK(mode)) { + /* + * We may later decide to recursively descend into + * the submodule directory and update its index + * and/or work tree, but we do not do that now. + */ + update_wd = 0; + goto update_index; + } + buf = read_sha1_file(sha, &type, &size); if (!buf) die("cannot read object %s '%s'", sha1_to_hex(sha), path); if (type != OBJ_BLOB) die("blob expected for %s '%s'", sha1_to_hex(sha), path); + if (S_ISREG(mode)) { + struct strbuf strbuf = STRBUF_INIT; + if (convert_to_working_tree(path, buf, size, &strbuf)) { + free(buf); + size = strbuf.len; + buf = strbuf_detach(&strbuf, NULL); + } + } - if (make_room_for_path(path) < 0) { + if (make_room_for_path(o, path) < 0) { update_wd = 0; + free(buf); goto update_index; } if (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) { @@ -598,439 +788,96 @@ static void update_file_flags(const unsigned char *sha, mode = 0666; fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode); if (fd < 0) - die("failed to open %s: %s", path, strerror(errno)); + die_errno("failed to open '%s'", path); flush_buffer(fd, buf, size); close(fd); } else if (S_ISLNK(mode)) { - char *lnk = xmalloc(size + 1); - memcpy(lnk, buf, size); - lnk[size] = '\0'; - mkdir_p(path, 0777); + char *lnk = xmemdupz(buf, size); + safe_create_leading_directories_const(path); unlink(path); - symlink(lnk, path); + if (symlink(lnk, path)) + die_errno("failed to symlink '%s'", path); free(lnk); } else die("do not know what to do with %06o %s '%s'", mode, sha1_to_hex(sha), path); + free(buf); } update_index: if (update_cache) add_cacheinfo(mode, sha, path, 0, update_wd, ADD_CACHE_OK_TO_ADD); } -static void update_file(int clean, +static void update_file(struct merge_options *o, + int clean, const unsigned char *sha, unsigned mode, const char *path) { - update_file_flags(sha, mode, path, index_only || clean, !index_only); + update_file_flags(o, sha, mode, path, o->call_depth || clean, !o->call_depth); } /* Low level file merging, update and removal */ -struct merge_file_info -{ +struct merge_file_info { unsigned char sha[20]; unsigned mode; unsigned clean:1, merge:1; }; -static void fill_mm(const unsigned char *sha1, mmfile_t *mm) -{ - unsigned long size; - enum object_type type; - - if (!hashcmp(sha1, null_sha1)) { - mm->ptr = xstrdup(""); - mm->size = 0; - return; - } - - mm->ptr = read_sha1_file(sha1, &type, &size); - if (!mm->ptr || type != OBJ_BLOB) - die("unable to read blob object %s", sha1_to_hex(sha1)); - mm->size = size; -} - -/* - * Customizable low-level merge drivers support. - */ - -struct ll_merge_driver; -typedef int (*ll_merge_fn)(const struct ll_merge_driver *, - const char *path, - mmfile_t *orig, - mmfile_t *src1, const char *name1, - mmfile_t *src2, const char *name2, - mmbuffer_t *result); - -struct ll_merge_driver { - const char *name; - const char *description; - ll_merge_fn fn; - const char *recursive; - struct ll_merge_driver *next; - char *cmdline; -}; - -/* - * Built-in low-levels - */ -static int ll_binary_merge(const struct ll_merge_driver *drv_unused, - const char *path_unused, - mmfile_t *orig, - mmfile_t *src1, const char *name1, - mmfile_t *src2, const char *name2, - mmbuffer_t *result) -{ - /* - * The tentative merge result is "ours" for the final round, - * or common ancestor for an internal merge. Still return - * "conflicted merge" status. - */ - mmfile_t *stolen = index_only ? orig : src1; - - result->ptr = stolen->ptr; - result->size = stolen->size; - stolen->ptr = NULL; - return 1; -} - -static int ll_xdl_merge(const struct ll_merge_driver *drv_unused, - const char *path_unused, - mmfile_t *orig, - mmfile_t *src1, const char *name1, - mmfile_t *src2, const char *name2, - mmbuffer_t *result) -{ - xpparam_t xpp; - - if (buffer_is_binary(orig->ptr, orig->size) || - buffer_is_binary(src1->ptr, src1->size) || - buffer_is_binary(src2->ptr, src2->size)) { - warning("Cannot merge binary files: %s vs. %s\n", - name1, name2); - return ll_binary_merge(drv_unused, path_unused, - orig, src1, name1, - src2, name2, - result); - } - - memset(&xpp, 0, sizeof(xpp)); - return xdl_merge(orig, - src1, name1, - src2, name2, - &xpp, XDL_MERGE_ZEALOUS, - result); -} - -static int ll_union_merge(const struct ll_merge_driver *drv_unused, - const char *path_unused, - mmfile_t *orig, - mmfile_t *src1, const char *name1, - mmfile_t *src2, const char *name2, - mmbuffer_t *result) -{ - char *src, *dst; - long size; - const int marker_size = 7; - - int status = ll_xdl_merge(drv_unused, path_unused, - orig, src1, NULL, src2, NULL, result); - if (status <= 0) - return status; - size = result->size; - src = dst = result->ptr; - while (size) { - char ch; - if ((marker_size < size) && - (*src == '<' || *src == '=' || *src == '>')) { - int i; - ch = *src; - for (i = 0; i < marker_size; i++) - if (src[i] != ch) - goto not_a_marker; - if (src[marker_size] != '\n') - goto not_a_marker; - src += marker_size + 1; - size -= marker_size + 1; - continue; - } - not_a_marker: - do { - ch = *src++; - *dst++ = ch; - size--; - } while (ch != '\n' && size); - } - result->size = dst - result->ptr; - return 0; -} - -#define LL_BINARY_MERGE 0 -#define LL_TEXT_MERGE 1 -#define LL_UNION_MERGE 2 -static struct ll_merge_driver ll_merge_drv[] = { - { "binary", "built-in binary merge", ll_binary_merge }, - { "text", "built-in 3-way text merge", ll_xdl_merge }, - { "union", "built-in union merge", ll_union_merge }, -}; - -static void create_temp(mmfile_t *src, char *path) -{ - int fd; - - strcpy(path, ".merge_file_XXXXXX"); - fd = xmkstemp(path); - if (write_in_full(fd, src->ptr, src->size) != src->size) - die("unable to write temp-file"); - close(fd); -} - -/* - * User defined low-level merge driver support. - */ -static int ll_ext_merge(const struct ll_merge_driver *fn, - const char *path, - mmfile_t *orig, - mmfile_t *src1, const char *name1, - mmfile_t *src2, const char *name2, - mmbuffer_t *result) -{ - char temp[3][50]; - char cmdbuf[2048]; - struct interp table[] = { - { "%O" }, - { "%A" }, - { "%B" }, - }; - struct child_process child; - const char *args[20]; - int status, fd, i; - struct stat st; - - if (fn->cmdline == NULL) - die("custom merge driver %s lacks command line.", fn->name); - - result->ptr = NULL; - result->size = 0; - create_temp(orig, temp[0]); - create_temp(src1, temp[1]); - create_temp(src2, temp[2]); - - interp_set_entry(table, 0, temp[0]); - interp_set_entry(table, 1, temp[1]); - interp_set_entry(table, 2, temp[2]); - - output(1, "merging %s using %s", path, - fn->description ? fn->description : fn->name); - - interpolate(cmdbuf, sizeof(cmdbuf), fn->cmdline, table, 3); - - memset(&child, 0, sizeof(child)); - child.argv = args; - args[0] = "sh"; - args[1] = "-c"; - args[2] = cmdbuf; - args[3] = NULL; - - status = run_command(&child); - if (status < -ERR_RUN_COMMAND_FORK) - ; /* failure in run-command */ - else - status = -status; - fd = open(temp[1], O_RDONLY); - if (fd < 0) - goto bad; - if (fstat(fd, &st)) - goto close_bad; - result->size = st.st_size; - result->ptr = xmalloc(result->size + 1); - if (read_in_full(fd, result->ptr, result->size) != result->size) { - free(result->ptr); - result->ptr = NULL; - result->size = 0; - } - close_bad: - close(fd); - bad: - for (i = 0; i < 3; i++) - unlink(temp[i]); - return status; -} - -/* - * merge.default and merge.driver configuration items - */ -static struct ll_merge_driver *ll_user_merge, **ll_user_merge_tail; -static const char *default_ll_merge; - -static int read_merge_config(const char *var, const char *value) +static int merge_3way(struct merge_options *o, + mmbuffer_t *result_buf, + const struct diff_filespec *one, + const struct diff_filespec *a, + const struct diff_filespec *b, + const char *branch1, + const char *branch2) { - struct ll_merge_driver *fn; - const char *ep, *name; - int namelen; - - if (!strcmp(var, "merge.default")) { - if (value) - default_ll_merge = strdup(value); - return 0; - } + mmfile_t orig, src1, src2; + struct ll_merge_options ll_opts = {0}; + char *base_name, *name1, *name2; + int merge_status; - /* - * We are not interested in anything but "merge.<name>.variable"; - * especially, we do not want to look at variables such as - * "merge.summary", "merge.tool", and "merge.verbosity". - */ - if (prefixcmp(var, "merge.") || (ep = strrchr(var, '.')) == var + 5) - return 0; + ll_opts.renormalize = o->renormalize; + ll_opts.xdl_opts = o->xdl_opts; - /* - * Find existing one as we might be processing merge.<name>.var2 - * after seeing merge.<name>.var1. - */ - name = var + 6; - namelen = ep - name; - for (fn = ll_user_merge; fn; fn = fn->next) - if (!strncmp(fn->name, name, namelen) && !fn->name[namelen]) + if (o->call_depth) { + ll_opts.virtual_ancestor = 1; + ll_opts.variant = 0; + } else { + switch (o->recursive_variant) { + case MERGE_RECURSIVE_OURS: + ll_opts.variant = XDL_MERGE_FAVOR_OURS; break; - if (!fn) { - char *namebuf; - fn = xcalloc(1, sizeof(struct ll_merge_driver)); - namebuf = xmalloc(namelen + 1); - memcpy(namebuf, name, namelen); - namebuf[namelen] = 0; - fn->name = namebuf; - fn->fn = ll_ext_merge; - fn->next = NULL; - *ll_user_merge_tail = fn; - ll_user_merge_tail = &(fn->next); - } - - ep++; - - if (!strcmp("name", ep)) { - if (!value) - return error("%s: lacks value", var); - fn->description = strdup(value); - return 0; - } - - if (!strcmp("driver", ep)) { - if (!value) - return error("%s: lacks value", var); - /* - * merge.<name>.driver specifies the command line: - * - * command-line - * - * The command-line will be interpolated with the following - * tokens and is given to the shell: - * - * %O - temporary file name for the merge base. - * %A - temporary file name for our version. - * %B - temporary file name for the other branches' version. - * - * The external merge driver should write the results in the - * file named by %A, and signal that it has done with zero exit - * status. - */ - fn->cmdline = strdup(value); - return 0; - } - - if (!strcmp("recursive", ep)) { - if (!value) - return error("%s: lacks value", var); - fn->recursive = strdup(value); - return 0; + case MERGE_RECURSIVE_THEIRS: + ll_opts.variant = XDL_MERGE_FAVOR_THEIRS; + break; + default: + ll_opts.variant = 0; + break; + } } - return 0; -} - -static void initialize_ll_merge(void) -{ - if (ll_user_merge_tail) - return; - ll_user_merge_tail = &ll_user_merge; - git_config(read_merge_config); -} - -static const struct ll_merge_driver *find_ll_merge_driver(const char *merge_attr) -{ - struct ll_merge_driver *fn; - const char *name; - int i; - - initialize_ll_merge(); - - if (ATTR_TRUE(merge_attr)) - return &ll_merge_drv[LL_TEXT_MERGE]; - else if (ATTR_FALSE(merge_attr)) - return &ll_merge_drv[LL_BINARY_MERGE]; - else if (ATTR_UNSET(merge_attr)) { - if (!default_ll_merge) - return &ll_merge_drv[LL_TEXT_MERGE]; - else - name = default_ll_merge; + if (strcmp(a->path, b->path) || + (o->ancestor != NULL && strcmp(a->path, one->path) != 0)) { + base_name = o->ancestor == NULL ? NULL : + xstrdup(mkpath("%s:%s", o->ancestor, one->path)); + name1 = xstrdup(mkpath("%s:%s", branch1, a->path)); + name2 = xstrdup(mkpath("%s:%s", branch2, b->path)); + } else { + base_name = o->ancestor == NULL ? NULL : + xstrdup(mkpath("%s", o->ancestor)); + name1 = xstrdup(mkpath("%s", branch1)); + name2 = xstrdup(mkpath("%s", branch2)); } - else - name = merge_attr; - - for (fn = ll_user_merge; fn; fn = fn->next) - if (!strcmp(fn->name, name)) - return fn; - - for (i = 0; i < ARRAY_SIZE(ll_merge_drv); i++) - if (!strcmp(ll_merge_drv[i].name, name)) - return &ll_merge_drv[i]; - - /* default to the 3-way */ - return &ll_merge_drv[LL_TEXT_MERGE]; -} - -static const char *git_path_check_merge(const char *path) -{ - static struct git_attr_check attr_merge_check; - if (!attr_merge_check.attr) - attr_merge_check.attr = git_attr("merge", 5); + read_mmblob(&orig, one->sha1); + read_mmblob(&src1, a->sha1); + read_mmblob(&src2, b->sha1); - if (git_checkattr(path, 1, &attr_merge_check)) - return NULL; - return attr_merge_check.value; -} - -static int ll_merge(mmbuffer_t *result_buf, - struct diff_filespec *o, - struct diff_filespec *a, - struct diff_filespec *b, - const char *branch1, - const char *branch2) -{ - mmfile_t orig, src1, src2; - char *name1, *name2; - int merge_status; - const char *ll_driver_name; - const struct ll_merge_driver *driver; - - name1 = xstrdup(mkpath("%s:%s", branch1, a->path)); - name2 = xstrdup(mkpath("%s:%s", branch2, b->path)); - - fill_mm(o->sha1, &orig); - fill_mm(a->sha1, &src1); - fill_mm(b->sha1, &src2); - - ll_driver_name = git_path_check_merge(a->path); - driver = find_ll_merge_driver(ll_driver_name); - - if (index_only && driver->recursive) - driver = find_ll_merge_driver(driver->recursive); - merge_status = driver->fn(driver, a->path, - &orig, &src1, name1, &src2, name2, - result_buf); + merge_status = ll_merge(result_buf, a->path, &orig, base_name, + &src1, name1, &src2, name2, &ll_opts); free(name1); free(name2); @@ -1040,9 +887,12 @@ static int ll_merge(mmbuffer_t *result_buf, return merge_status; } -static struct merge_file_info merge_file(struct diff_filespec *o, - struct diff_filespec *a, struct diff_filespec *b, - const char *branch1, const char *branch2) +static struct merge_file_info merge_file_1(struct merge_options *o, + const struct diff_filespec *one, + const struct diff_filespec *a, + const struct diff_filespec *b, + const char *branch1, + const char *branch2) { struct merge_file_info result; result.merge = 0; @@ -1058,21 +908,32 @@ static struct merge_file_info merge_file(struct diff_filespec *o, hashcpy(result.sha, b->sha1); } } else { - if (!sha_eq(a->sha1, o->sha1) && !sha_eq(b->sha1, o->sha1)) + if (!sha_eq(a->sha1, one->sha1) && !sha_eq(b->sha1, one->sha1)) result.merge = 1; - result.mode = a->mode == o->mode ? b->mode: a->mode; + /* + * Merge modes + */ + if (a->mode == b->mode || a->mode == one->mode) + result.mode = b->mode; + else { + result.mode = a->mode; + if (b->mode != one->mode) { + result.clean = 0; + result.merge = 1; + } + } - if (sha_eq(a->sha1, o->sha1)) + if (sha_eq(a->sha1, b->sha1) || sha_eq(a->sha1, one->sha1)) hashcpy(result.sha, b->sha1); - else if (sha_eq(b->sha1, o->sha1)) + else if (sha_eq(b->sha1, one->sha1)) hashcpy(result.sha, a->sha1); else if (S_ISREG(a->mode)) { mmbuffer_t result_buf; int merge_status; - merge_status = ll_merge(&result_buf, o, a, b, - branch1, branch2); + merge_status = merge_3way(o, &result_buf, one, a, b, + branch1, branch2); if ((merge_status < 0) || !result_buf.ptr) die("Failed to execute internal merge"); @@ -1084,124 +945,357 @@ static struct merge_file_info merge_file(struct diff_filespec *o, free(result_buf.ptr); result.clean = (merge_status == 0); - } else { - if (!(S_ISLNK(a->mode) || S_ISLNK(b->mode))) - die("cannot merge modes?"); - + } else if (S_ISGITLINK(a->mode)) { + result.clean = merge_submodule(result.sha, + one->path, one->sha1, + a->sha1, b->sha1, + !o->call_depth); + } else if (S_ISLNK(a->mode)) { hashcpy(result.sha, a->sha1); if (!sha_eq(a->sha1, b->sha1)) result.clean = 0; + } else { + die("unsupported object type in the tree"); } } return result; } -static void conflict_rename_rename(struct rename *ren1, - const char *branch1, - struct rename *ren2, - const char *branch2) +static struct merge_file_info +merge_file_special_markers(struct merge_options *o, + const struct diff_filespec *one, + const struct diff_filespec *a, + const struct diff_filespec *b, + const char *branch1, + const char *filename1, + const char *branch2, + const char *filename2) { - char *del[2]; - int delp = 0; - const char *ren1_dst = ren1->pair->two->path; - const char *ren2_dst = ren2->pair->two->path; - const char *dst_name1 = ren1_dst; - const char *dst_name2 = ren2_dst; - if (path_list_has_path(¤t_directory_set, ren1_dst)) { - dst_name1 = del[delp++] = unique_path(ren1_dst, branch1); - output(1, "%s is a directory in %s added as %s instead", - ren1_dst, branch2, dst_name1); - remove_file(0, ren1_dst, 0); + char *side1 = NULL; + char *side2 = NULL; + struct merge_file_info mfi; + + if (filename1) { + side1 = xmalloc(strlen(branch1) + strlen(filename1) + 2); + sprintf(side1, "%s:%s", branch1, filename1); + } + if (filename2) { + side2 = xmalloc(strlen(branch2) + strlen(filename2) + 2); + sprintf(side2, "%s:%s", branch2, filename2); } - if (path_list_has_path(¤t_directory_set, ren2_dst)) { - dst_name2 = del[delp++] = unique_path(ren2_dst, branch2); - output(1, "%s is a directory in %s added as %s instead", - ren2_dst, branch1, dst_name2); - remove_file(0, ren2_dst, 0); + + mfi = merge_file_1(o, one, a, b, + side1 ? side1 : branch1, side2 ? side2 : branch2); + free(side1); + free(side2); + return mfi; +} + +static struct merge_file_info merge_file(struct merge_options *o, + const char *path, + const unsigned char *o_sha, int o_mode, + const unsigned char *a_sha, int a_mode, + const unsigned char *b_sha, int b_mode, + const char *branch1, + const char *branch2) +{ + struct diff_filespec one, a, b; + + one.path = a.path = b.path = (char *)path; + hashcpy(one.sha1, o_sha); + one.mode = o_mode; + hashcpy(a.sha1, a_sha); + a.mode = a_mode; + hashcpy(b.sha1, b_sha); + b.mode = b_mode; + return merge_file_1(o, &one, &a, &b, branch1, branch2); +} + +static void handle_change_delete(struct merge_options *o, + const char *path, + const unsigned char *o_sha, int o_mode, + const unsigned char *a_sha, int a_mode, + const unsigned char *b_sha, int b_mode, + const char *change, const char *change_past) +{ + char *renamed = NULL; + if (dir_in_way(path, !o->call_depth)) { + renamed = unique_path(o, path, a_sha ? o->branch1 : o->branch2); } - if (index_only) { - remove_file_from_cache(dst_name1); - remove_file_from_cache(dst_name2); + + if (o->call_depth) { /* - * Uncomment to leave the conflicting names in the resulting tree - * - * update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, dst_name1); - * update_file(0, ren2->pair->two->sha1, ren2->pair->two->mode, dst_name2); + * We cannot arbitrarily accept either a_sha or b_sha as + * correct; since there is no true "middle point" between + * them, simply reuse the base version for virtual merge base. */ + remove_file_from_cache(path); + update_file(o, 0, o_sha, o_mode, renamed ? renamed : path); + } else if (!a_sha) { + output(o, 1, "CONFLICT (%s/delete): %s deleted in %s " + "and %s in %s. Version %s of %s left in tree%s%s.", + change, path, o->branch1, + change_past, o->branch2, o->branch2, path, + NULL == renamed ? "" : " at ", + NULL == renamed ? "" : renamed); + update_file(o, 0, b_sha, b_mode, renamed ? renamed : path); + } else { + output(o, 1, "CONFLICT (%s/delete): %s deleted in %s " + "and %s in %s. Version %s of %s left in tree%s%s.", + change, path, o->branch2, + change_past, o->branch1, o->branch1, path, + NULL == renamed ? "" : " at ", + NULL == renamed ? "" : renamed); + if (renamed) + update_file(o, 0, a_sha, a_mode, renamed); + /* + * No need to call update_file() on path when !renamed, since + * that would needlessly touch path. We could call + * update_file_flags() with update_cache=0 and update_wd=0, + * but that's a no-op. + */ + } + free(renamed); +} + +static void conflict_rename_delete(struct merge_options *o, + struct diff_filepair *pair, + const char *rename_branch, + const char *other_branch) +{ + const struct diff_filespec *orig = pair->one; + const struct diff_filespec *dest = pair->two; + const unsigned char *a_sha = NULL; + const unsigned char *b_sha = NULL; + int a_mode = 0; + int b_mode = 0; + + if (rename_branch == o->branch1) { + a_sha = dest->sha1; + a_mode = dest->mode; + } else { + b_sha = dest->sha1; + b_mode = dest->mode; + } + + handle_change_delete(o, + o->call_depth ? orig->path : dest->path, + orig->sha1, orig->mode, + a_sha, a_mode, + b_sha, b_mode, + "rename", "renamed"); + + if (o->call_depth) { + remove_file_from_cache(dest->path); } else { - update_stages(dst_name1, NULL, ren1->pair->two, NULL, 1); - update_stages(dst_name2, NULL, NULL, ren2->pair->two, 1); + update_stages(dest->path, NULL, + rename_branch == o->branch1 ? dest : NULL, + rename_branch == o->branch1 ? NULL : dest); } - while (delp--) - free(del[delp]); + +} + +static struct diff_filespec *filespec_from_entry(struct diff_filespec *target, + struct stage_data *entry, + int stage) +{ + unsigned char *sha = entry->stages[stage].sha; + unsigned mode = entry->stages[stage].mode; + if (mode == 0 || is_null_sha1(sha)) + return NULL; + hashcpy(target->sha1, sha); + target->mode = mode; + return target; +} + +static void handle_file(struct merge_options *o, + struct diff_filespec *rename, + int stage, + struct rename_conflict_info *ci) +{ + char *dst_name = rename->path; + struct stage_data *dst_entry; + const char *cur_branch, *other_branch; + struct diff_filespec other; + struct diff_filespec *add; + + if (stage == 2) { + dst_entry = ci->dst_entry1; + cur_branch = ci->branch1; + other_branch = ci->branch2; + } else { + dst_entry = ci->dst_entry2; + cur_branch = ci->branch2; + other_branch = ci->branch1; + } + + add = filespec_from_entry(&other, dst_entry, stage ^ 1); + if (add) { + char *add_name = unique_path(o, rename->path, other_branch); + update_file(o, 0, add->sha1, add->mode, add_name); + + remove_file(o, 0, rename->path, 0); + dst_name = unique_path(o, rename->path, cur_branch); + } else { + if (dir_in_way(rename->path, !o->call_depth)) { + dst_name = unique_path(o, rename->path, cur_branch); + output(o, 1, "%s is a directory in %s adding as %s instead", + rename->path, other_branch, dst_name); + } + } + update_file(o, 0, rename->sha1, rename->mode, dst_name); + if (stage == 2) + update_stages(rename->path, NULL, rename, add); + else + update_stages(rename->path, NULL, add, rename); + + if (dst_name != rename->path) + free(dst_name); } -static void conflict_rename_dir(struct rename *ren1, - const char *branch1) +static void conflict_rename_rename_1to2(struct merge_options *o, + struct rename_conflict_info *ci) { - char *new_path = unique_path(ren1->pair->two->path, branch1); - output(1, "Renamed %s to %s instead", ren1->pair->one->path, new_path); - remove_file(0, ren1->pair->two->path, 0); - update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path); - free(new_path); + /* One file was renamed in both branches, but to different names. */ + struct diff_filespec *one = ci->pair1->one; + struct diff_filespec *a = ci->pair1->two; + struct diff_filespec *b = ci->pair2->two; + + output(o, 1, "CONFLICT (rename/rename): " + "Rename \"%s\"->\"%s\" in branch \"%s\" " + "rename \"%s\"->\"%s\" in \"%s\"%s", + one->path, a->path, ci->branch1, + one->path, b->path, ci->branch2, + o->call_depth ? " (left unresolved)" : ""); + if (o->call_depth) { + struct merge_file_info mfi; + struct diff_filespec other; + struct diff_filespec *add; + mfi = merge_file(o, one->path, + one->sha1, one->mode, + a->sha1, a->mode, + b->sha1, b->mode, + ci->branch1, ci->branch2); + /* + * FIXME: For rename/add-source conflicts (if we could detect + * such), this is wrong. We should instead find a unique + * pathname and then either rename the add-source file to that + * unique path, or use that unique path instead of src here. + */ + update_file(o, 0, mfi.sha, mfi.mode, one->path); + + /* + * Above, we put the merged content at the merge-base's + * path. Now we usually need to delete both a->path and + * b->path. However, the rename on each side of the merge + * could also be involved in a rename/add conflict. In + * such cases, we should keep the added file around, + * resolving the conflict at that path in its favor. + */ + add = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1); + if (add) + update_file(o, 0, add->sha1, add->mode, a->path); + else + remove_file_from_cache(a->path); + add = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1); + if (add) + update_file(o, 0, add->sha1, add->mode, b->path); + else + remove_file_from_cache(b->path); + } else { + handle_file(o, a, 2, ci); + handle_file(o, b, 3, ci); + } } -static void conflict_rename_rename_2(struct rename *ren1, - const char *branch1, - struct rename *ren2, - const char *branch2) +static void conflict_rename_rename_2to1(struct merge_options *o, + struct rename_conflict_info *ci) { - char *new_path1 = unique_path(ren1->pair->two->path, branch1); - char *new_path2 = unique_path(ren2->pair->two->path, branch2); - output(1, "Renamed %s to %s and %s to %s instead", - ren1->pair->one->path, new_path1, - ren2->pair->one->path, new_path2); - remove_file(0, ren1->pair->two->path, 0); - update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path1); - update_file(0, ren2->pair->two->sha1, ren2->pair->two->mode, new_path2); - free(new_path2); - free(new_path1); + /* Two files, a & b, were renamed to the same thing, c. */ + struct diff_filespec *a = ci->pair1->one; + struct diff_filespec *b = ci->pair2->one; + struct diff_filespec *c1 = ci->pair1->two; + struct diff_filespec *c2 = ci->pair2->two; + char *path = c1->path; /* == c2->path */ + struct merge_file_info mfi_c1; + struct merge_file_info mfi_c2; + + output(o, 1, "CONFLICT (rename/rename): " + "Rename %s->%s in %s. " + "Rename %s->%s in %s", + a->path, c1->path, ci->branch1, + b->path, c2->path, ci->branch2); + + remove_file(o, 1, a->path, would_lose_untracked(a->path)); + remove_file(o, 1, b->path, would_lose_untracked(b->path)); + + mfi_c1 = merge_file_special_markers(o, a, c1, &ci->ren1_other, + o->branch1, c1->path, + o->branch2, ci->ren1_other.path); + mfi_c2 = merge_file_special_markers(o, b, &ci->ren2_other, c2, + o->branch1, ci->ren2_other.path, + o->branch2, c2->path); + + if (o->call_depth) { + /* + * If mfi_c1.clean && mfi_c2.clean, then it might make + * sense to do a two-way merge of those results. But, I + * think in all cases, it makes sense to have the virtual + * merge base just undo the renames; they can be detected + * again later for the non-recursive merge. + */ + remove_file(o, 0, path, 0); + update_file(o, 0, mfi_c1.sha, mfi_c1.mode, a->path); + update_file(o, 0, mfi_c2.sha, mfi_c2.mode, b->path); + } else { + char *new_path1 = unique_path(o, path, ci->branch1); + char *new_path2 = unique_path(o, path, ci->branch2); + output(o, 1, "Renaming %s to %s and %s to %s instead", + a->path, new_path1, b->path, new_path2); + remove_file(o, 0, path, 0); + update_file(o, 0, mfi_c1.sha, mfi_c1.mode, new_path1); + update_file(o, 0, mfi_c2.sha, mfi_c2.mode, new_path2); + free(new_path2); + free(new_path1); + } } -static int process_renames(struct path_list *a_renames, - struct path_list *b_renames, - const char *a_branch, - const char *b_branch) +static int process_renames(struct merge_options *o, + struct string_list *a_renames, + struct string_list *b_renames) { int clean_merge = 1, i, j; - struct path_list a_by_dst = {NULL, 0, 0, 0}, b_by_dst = {NULL, 0, 0, 0}; + struct string_list a_by_dst = STRING_LIST_INIT_NODUP; + struct string_list b_by_dst = STRING_LIST_INIT_NODUP; const struct rename *sre; for (i = 0; i < a_renames->nr; i++) { sre = a_renames->items[i].util; - path_list_insert(sre->pair->two->path, &a_by_dst)->util - = sre->dst_entry; + string_list_insert(&a_by_dst, sre->pair->two->path)->util + = (void *)sre; } for (i = 0; i < b_renames->nr; i++) { sre = b_renames->items[i].util; - path_list_insert(sre->pair->two->path, &b_by_dst)->util - = sre->dst_entry; + string_list_insert(&b_by_dst, sre->pair->two->path)->util + = (void *)sre; } for (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) { - int compare; - char *src; - struct path_list *renames1, *renames2, *renames2Dst; + struct string_list *renames1, *renames2Dst; struct rename *ren1 = NULL, *ren2 = NULL; const char *branch1, *branch2; const char *ren1_src, *ren1_dst; + struct string_list_item *lookup; if (i >= a_renames->nr) { - compare = 1; ren2 = b_renames->items[j++].util; } else if (j >= b_renames->nr) { - compare = -1; ren1 = a_renames->items[i++].util; } else { - compare = strcmp(a_renames->items[i].path, - b_renames->items[j].path); + int compare = strcmp(a_renames->items[i].string, + b_renames->items[j].string); if (compare <= 0) ren1 = a_renames->items[i++].util; if (compare >= 0) @@ -1211,142 +1305,179 @@ static int process_renames(struct path_list *a_renames, /* TODO: refactor, so that 1/2 are not needed */ if (ren1) { renames1 = a_renames; - renames2 = b_renames; renames2Dst = &b_by_dst; - branch1 = a_branch; - branch2 = b_branch; + branch1 = o->branch1; + branch2 = o->branch2; } else { struct rename *tmp; renames1 = b_renames; - renames2 = a_renames; renames2Dst = &a_by_dst; - branch1 = b_branch; - branch2 = a_branch; + branch1 = o->branch2; + branch2 = o->branch1; tmp = ren2; ren2 = ren1; ren1 = tmp; } - src = ren1->pair->one->path; - - ren1->dst_entry->processed = 1; - ren1->src_entry->processed = 1; if (ren1->processed) continue; ren1->processed = 1; + ren1->dst_entry->processed = 1; + /* BUG: We should only mark src_entry as processed if we + * are not dealing with a rename + add-source case. + */ + ren1->src_entry->processed = 1; ren1_src = ren1->pair->one->path; ren1_dst = ren1->pair->two->path; if (ren2) { + /* One file renamed on both sides */ const char *ren2_src = ren2->pair->one->path; const char *ren2_dst = ren2->pair->two->path; - /* Renamed in 1 and renamed in 2 */ + enum rename_type rename_type; if (strcmp(ren1_src, ren2_src) != 0) - die("ren1.src != ren2.src"); + die("ren1_src != ren2_src"); ren2->dst_entry->processed = 1; ren2->processed = 1; if (strcmp(ren1_dst, ren2_dst) != 0) { + rename_type = RENAME_ONE_FILE_TO_TWO; clean_merge = 0; - output(1, "CONFLICT (rename/rename): " - "Rename \"%s\"->\"%s\" in branch \"%s\" " - "rename \"%s\"->\"%s\" in \"%s\"%s", - src, ren1_dst, branch1, - src, ren2_dst, branch2, - index_only ? " (left unresolved)": ""); - if (index_only) { - remove_file_from_cache(src); - update_file(0, ren1->pair->one->sha1, - ren1->pair->one->mode, src); - } - conflict_rename_rename(ren1, branch1, ren2, branch2); } else { - struct merge_file_info mfi; - remove_file(1, ren1_src, 1); - mfi = merge_file(ren1->pair->one, - ren1->pair->two, - ren2->pair->two, - branch1, - branch2); - if (mfi.merge || !mfi.clean) - output(1, "Renamed %s->%s", src, ren1_dst); - - if (mfi.merge) - output(2, "Auto-merged %s", ren1_dst); - - if (!mfi.clean) { - output(1, "CONFLICT (content): merge conflict in %s", - ren1_dst); - clean_merge = 0; - - if (!index_only) - update_stages(ren1_dst, - ren1->pair->one, - ren1->pair->two, - ren2->pair->two, - 1 /* clear */); - } - update_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst); + rename_type = RENAME_ONE_FILE_TO_ONE; + /* BUG: We should only remove ren1_src in + * the base stage (think of rename + + * add-source cases). + */ + remove_file(o, 1, ren1_src, 1); + update_entry(ren1->dst_entry, + ren1->pair->one, + ren1->pair->two, + ren2->pair->two); } + setup_rename_conflict_info(rename_type, + ren1->pair, + ren2->pair, + branch1, + branch2, + ren1->dst_entry, + ren2->dst_entry, + o, + NULL, + NULL); + } else if ((lookup = string_list_lookup(renames2Dst, ren1_dst))) { + /* Two different files renamed to the same thing */ + char *ren2_dst; + ren2 = lookup->util; + ren2_dst = ren2->pair->two->path; + if (strcmp(ren1_dst, ren2_dst) != 0) + die("ren1_dst != ren2_dst"); + + clean_merge = 0; + ren2->processed = 1; + /* + * BUG: We should only mark src_entry as processed + * if we are not dealing with a rename + add-source + * case. + */ + ren2->src_entry->processed = 1; + + setup_rename_conflict_info(RENAME_TWO_FILES_TO_ONE, + ren1->pair, + ren2->pair, + branch1, + branch2, + ren1->dst_entry, + ren2->dst_entry, + o, + ren1->src_entry, + ren2->src_entry); + } else { /* Renamed in 1, maybe changed in 2 */ - struct path_list_item *item; /* we only use sha1 and mode of these */ struct diff_filespec src_other, dst_other; - int try_merge, stage = a_renames == renames1 ? 3: 2; - - remove_file(1, ren1_src, index_only || stage == 3); - - hashcpy(src_other.sha1, ren1->src_entry->stages[stage].sha); - src_other.mode = ren1->src_entry->stages[stage].mode; - hashcpy(dst_other.sha1, ren1->dst_entry->stages[stage].sha); - dst_other.mode = ren1->dst_entry->stages[stage].mode; - + int try_merge; + + /* + * unpack_trees loads entries from common-commit + * into stage 1, from head-commit into stage 2, and + * from merge-commit into stage 3. We keep track + * of which side corresponds to the rename. + */ + int renamed_stage = a_renames == renames1 ? 2 : 3; + int other_stage = a_renames == renames1 ? 3 : 2; + + /* BUG: We should only remove ren1_src in the base + * stage and in other_stage (think of rename + + * add-source case). + */ + remove_file(o, 1, ren1_src, + renamed_stage == 2 || !was_tracked(ren1_src)); + + hashcpy(src_other.sha1, ren1->src_entry->stages[other_stage].sha); + src_other.mode = ren1->src_entry->stages[other_stage].mode; + hashcpy(dst_other.sha1, ren1->dst_entry->stages[other_stage].sha); + dst_other.mode = ren1->dst_entry->stages[other_stage].mode; try_merge = 0; - if (path_list_has_path(¤t_directory_set, ren1_dst)) { - clean_merge = 0; - output(1, "CONFLICT (rename/directory): Renamed %s->%s in %s " - " directory %s added in %s", - ren1_src, ren1_dst, branch1, - ren1_dst, branch2); - conflict_rename_dir(ren1, branch1); - } else if (sha_eq(src_other.sha1, null_sha1)) { - clean_merge = 0; - output(1, "CONFLICT (rename/delete): Renamed %s->%s in %s " - "and deleted in %s", - ren1_src, ren1_dst, branch1, - branch2); - update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst); + if (sha_eq(src_other.sha1, null_sha1)) { + setup_rename_conflict_info(RENAME_DELETE, + ren1->pair, + NULL, + branch1, + branch2, + ren1->dst_entry, + NULL, + o, + NULL, + NULL); + } else if ((dst_other.mode == ren1->pair->two->mode) && + sha_eq(dst_other.sha1, ren1->pair->two->sha1)) { + /* + * Added file on the other side identical to + * the file being renamed: clean merge. + * Also, there is no need to overwrite the + * file already in the working copy, so call + * update_file_flags() instead of + * update_file(). + */ + update_file_flags(o, + ren1->pair->two->sha1, + ren1->pair->two->mode, + ren1_dst, + 1, /* update_cache */ + 0 /* update_wd */); } else if (!sha_eq(dst_other.sha1, null_sha1)) { - const char *new_path; clean_merge = 0; try_merge = 1; - output(1, "CONFLICT (rename/add): Renamed %s->%s in %s. " + output(o, 1, "CONFLICT (rename/add): Rename %s->%s in %s. " "%s added in %s", ren1_src, ren1_dst, branch1, ren1_dst, branch2); - new_path = unique_path(ren1_dst, branch2); - output(1, "Added as %s instead", new_path); - update_file(0, dst_other.sha1, dst_other.mode, new_path); - } else if ((item = path_list_lookup(ren1_dst, renames2Dst))) { - ren2 = item->util; - clean_merge = 0; - ren2->processed = 1; - output(1, "CONFLICT (rename/rename): Renamed %s->%s in %s. " - "Renamed %s->%s in %s", - ren1_src, ren1_dst, branch1, - ren2->pair->one->path, ren2->pair->two->path, branch2); - conflict_rename_rename_2(ren1, branch1, ren2, branch2); + if (o->call_depth) { + struct merge_file_info mfi; + mfi = merge_file(o, ren1_dst, null_sha1, 0, + ren1->pair->two->sha1, ren1->pair->two->mode, + dst_other.sha1, dst_other.mode, + branch1, branch2); + output(o, 1, "Adding merged %s", ren1_dst); + update_file(o, 0, mfi.sha, mfi.mode, ren1_dst); + try_merge = 0; + } else { + char *new_path = unique_path(o, ren1_dst, branch2); + output(o, 1, "Adding as %s instead", new_path); + update_file(o, 0, dst_other.sha1, dst_other.mode, new_path); + free(new_path); + } } else try_merge = 1; if (try_merge) { - struct diff_filespec *o, *a, *b; - struct merge_file_info mfi; + struct diff_filespec *one, *a, *b; src_other.path = (char *)ren1_src; - o = ren1->pair->one; + one = ren1->pair->one; if (a_renames == renames1) { a = ren1->pair->two; b = &src_other; @@ -1354,39 +1485,22 @@ static int process_renames(struct path_list *a_renames, b = ren1->pair->two; a = &src_other; } - mfi = merge_file(o, a, b, - a_branch, b_branch); - - if (mfi.clean && - sha_eq(mfi.sha, ren1->pair->two->sha1) && - mfi.mode == ren1->pair->two->mode) - /* - * This messaged is part of - * t6022 test. If you change - * it update the test too. - */ - output(3, "Skipped %s (merged same as existing)", ren1_dst); - else { - if (mfi.merge || !mfi.clean) - output(1, "Renamed %s => %s", ren1_src, ren1_dst); - if (mfi.merge) - output(2, "Auto-merged %s", ren1_dst); - if (!mfi.clean) { - output(1, "CONFLICT (rename/modify): Merge conflict in %s", - ren1_dst); - clean_merge = 0; - - if (!index_only) - update_stages(ren1_dst, - o, a, b, 1); - } - update_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst); - } + update_entry(ren1->dst_entry, one, a, b); + setup_rename_conflict_info(RENAME_NORMAL, + ren1->pair, + NULL, + branch1, + NULL, + ren1->dst_entry, + NULL, + o, + NULL, + NULL); } } } - path_list_clear(&a_by_dst, 0); - path_list_clear(&b_by_dst, 0); + string_list_clear(&a_by_dst, 0); + string_list_clear(&b_by_dst, 0); return clean_merge; } @@ -1396,16 +1510,181 @@ static unsigned char *stage_sha(const unsigned char *sha, unsigned mode) return (is_null_sha1(sha) || mode == 0) ? NULL: (unsigned char *)sha; } +static int read_sha1_strbuf(const unsigned char *sha1, struct strbuf *dst) +{ + void *buf; + enum object_type type; + unsigned long size; + buf = read_sha1_file(sha1, &type, &size); + if (!buf) + return error("cannot read object %s", sha1_to_hex(sha1)); + if (type != OBJ_BLOB) { + free(buf); + return error("object %s is not a blob", sha1_to_hex(sha1)); + } + strbuf_attach(dst, buf, size, size + 1); + return 0; +} + +static int blob_unchanged(const unsigned char *o_sha, + const unsigned char *a_sha, + int renormalize, const char *path) +{ + struct strbuf o = STRBUF_INIT; + struct strbuf a = STRBUF_INIT; + int ret = 0; /* assume changed for safety */ + + if (sha_eq(o_sha, a_sha)) + return 1; + if (!renormalize) + return 0; + + assert(o_sha && a_sha); + if (read_sha1_strbuf(o_sha, &o) || read_sha1_strbuf(a_sha, &a)) + goto error_return; + /* + * Note: binary | is used so that both renormalizations are + * performed. Comparison can be skipped if both files are + * unchanged since their sha1s have already been compared. + */ + if (renormalize_buffer(path, o.buf, o.len, &o) | + renormalize_buffer(path, a.buf, o.len, &a)) + ret = (o.len == a.len && !memcmp(o.buf, a.buf, o.len)); + +error_return: + strbuf_release(&o); + strbuf_release(&a); + return ret; +} + +static void handle_modify_delete(struct merge_options *o, + const char *path, + unsigned char *o_sha, int o_mode, + unsigned char *a_sha, int a_mode, + unsigned char *b_sha, int b_mode) +{ + handle_change_delete(o, + path, + o_sha, o_mode, + a_sha, a_mode, + b_sha, b_mode, + "modify", "modified"); +} + +static int merge_content(struct merge_options *o, + const char *path, + unsigned char *o_sha, int o_mode, + unsigned char *a_sha, int a_mode, + unsigned char *b_sha, int b_mode, + struct rename_conflict_info *rename_conflict_info) +{ + const char *reason = "content"; + const char *path1 = NULL, *path2 = NULL; + struct merge_file_info mfi; + struct diff_filespec one, a, b; + unsigned df_conflict_remains = 0; + + if (!o_sha) { + reason = "add/add"; + o_sha = (unsigned char *)null_sha1; + } + one.path = a.path = b.path = (char *)path; + hashcpy(one.sha1, o_sha); + one.mode = o_mode; + hashcpy(a.sha1, a_sha); + a.mode = a_mode; + hashcpy(b.sha1, b_sha); + b.mode = b_mode; + + if (rename_conflict_info) { + struct diff_filepair *pair1 = rename_conflict_info->pair1; + + path1 = (o->branch1 == rename_conflict_info->branch1) ? + pair1->two->path : pair1->one->path; + /* If rename_conflict_info->pair2 != NULL, we are in + * RENAME_ONE_FILE_TO_ONE case. Otherwise, we have a + * normal rename. + */ + path2 = (rename_conflict_info->pair2 || + o->branch2 == rename_conflict_info->branch1) ? + pair1->two->path : pair1->one->path; + + if (dir_in_way(path, !o->call_depth)) + df_conflict_remains = 1; + } + mfi = merge_file_special_markers(o, &one, &a, &b, + o->branch1, path1, + o->branch2, path2); + + if (mfi.clean && !df_conflict_remains && + sha_eq(mfi.sha, a_sha) && mfi.mode == a_mode) { + int path_renamed_outside_HEAD; + output(o, 3, "Skipped %s (merged same as existing)", path); + /* + * The content merge resulted in the same file contents we + * already had. We can return early if those file contents + * are recorded at the correct path (which may not be true + * if the merge involves a rename). + */ + path_renamed_outside_HEAD = !path2 || !strcmp(path, path2); + if (!path_renamed_outside_HEAD) { + add_cacheinfo(mfi.mode, mfi.sha, path, + 0, (!o->call_depth), 0); + return mfi.clean; + } + } else + output(o, 2, "Auto-merging %s", path); + + if (!mfi.clean) { + if (S_ISGITLINK(mfi.mode)) + reason = "submodule"; + output(o, 1, "CONFLICT (%s): Merge conflict in %s", + reason, path); + if (rename_conflict_info && !df_conflict_remains) + update_stages(path, &one, &a, &b); + } + + if (df_conflict_remains) { + char *new_path; + if (o->call_depth) { + remove_file_from_cache(path); + } else { + if (!mfi.clean) + update_stages(path, &one, &a, &b); + else { + int file_from_stage2 = was_tracked(path); + struct diff_filespec merged; + hashcpy(merged.sha1, mfi.sha); + merged.mode = mfi.mode; + + update_stages(path, NULL, + file_from_stage2 ? &merged : NULL, + file_from_stage2 ? NULL : &merged); + } + + } + new_path = unique_path(o, path, rename_conflict_info->branch1); + output(o, 1, "Adding as %s instead", new_path); + update_file(o, 0, mfi.sha, mfi.mode, new_path); + free(new_path); + mfi.clean = 0; + } else { + update_file(o, mfi.clean, mfi.sha, mfi.mode, path); + } + return mfi.clean; + +} + /* Per entry merge function */ -static int process_entry(const char *path, struct stage_data *entry, - const char *branch1, - const char *branch2) +static int process_entry(struct merge_options *o, + const char *path, struct stage_data *entry) { /* printf("processing entry, clean cache: %s\n", index_only ? "yes": "no"); print_index_entry("\tpath: ", entry); */ int clean_merge = 1; + int normalize = o->renormalize; unsigned o_mode = entry->stages[1].mode; unsigned a_mode = entry->stages[2].mode; unsigned b_mode = entry->stages[3].mode; @@ -1413,38 +1692,56 @@ static int process_entry(const char *path, struct stage_data *entry, unsigned char *a_sha = stage_sha(entry->stages[2].sha, a_mode); unsigned char *b_sha = stage_sha(entry->stages[3].sha, b_mode); - if (o_sha && (!a_sha || !b_sha)) { + entry->processed = 1; + if (entry->rename_conflict_info) { + struct rename_conflict_info *conflict_info = entry->rename_conflict_info; + switch (conflict_info->rename_type) { + case RENAME_NORMAL: + case RENAME_ONE_FILE_TO_ONE: + clean_merge = merge_content(o, path, + o_sha, o_mode, a_sha, a_mode, b_sha, b_mode, + conflict_info); + break; + case RENAME_DELETE: + clean_merge = 0; + conflict_rename_delete(o, conflict_info->pair1, + conflict_info->branch1, + conflict_info->branch2); + break; + case RENAME_ONE_FILE_TO_TWO: + clean_merge = 0; + conflict_rename_rename_1to2(o, conflict_info); + break; + case RENAME_TWO_FILES_TO_ONE: + clean_merge = 0; + conflict_rename_rename_2to1(o, conflict_info); + break; + default: + entry->processed = 0; + break; + } + } else if (o_sha && (!a_sha || !b_sha)) { /* Case A: Deleted in one */ if ((!a_sha && !b_sha) || - (sha_eq(a_sha, o_sha) && !b_sha) || - (!a_sha && sha_eq(b_sha, o_sha))) { + (!b_sha && blob_unchanged(o_sha, a_sha, normalize, path)) || + (!a_sha && blob_unchanged(o_sha, b_sha, normalize, path))) { /* Deleted in both or deleted in one and * unchanged in the other */ if (a_sha) - output(2, "Removed %s", path); + output(o, 2, "Removing %s", path); /* do not touch working file if it did not exist */ - remove_file(1, path, !a_sha); + remove_file(o, 1, path, !a_sha); } else { - /* Deleted in one and changed in the other */ + /* Modify/delete; deleted side may have put a directory in the way */ clean_merge = 0; - if (!a_sha) { - output(1, "CONFLICT (delete/modify): %s deleted in %s " - "and modified in %s. Version %s of %s left in tree.", - path, branch1, - branch2, branch2, path); - update_file(0, b_sha, b_mode, path); - } else { - output(1, "CONFLICT (delete/modify): %s deleted in %s " - "and modified in %s. Version %s of %s left in tree.", - path, branch2, - branch1, branch1, path); - update_file(0, a_sha, a_mode, path); - } + handle_modify_delete(o, path, o_sha, o_mode, + a_sha, a_mode, b_sha, b_mode); } - } else if ((!o_sha && a_sha && !b_sha) || (!o_sha && !a_sha && b_sha)) { /* Case B: Added in one. */ + /* [nothing|directory] -> ([nothing|directory], file) */ + const char *add_branch; const char *other_branch; unsigned mode; @@ -1452,136 +1749,120 @@ static int process_entry(const char *path, struct stage_data *entry, const char *conf; if (a_sha) { - add_branch = branch1; - other_branch = branch2; + add_branch = o->branch1; + other_branch = o->branch2; mode = a_mode; sha = a_sha; conf = "file/directory"; } else { - add_branch = branch2; - other_branch = branch1; + add_branch = o->branch2; + other_branch = o->branch1; mode = b_mode; sha = b_sha; conf = "directory/file"; } - if (path_list_has_path(¤t_directory_set, path)) { - const char *new_path = unique_path(path, add_branch); + if (dir_in_way(path, !o->call_depth)) { + char *new_path = unique_path(o, path, add_branch); clean_merge = 0; - output(1, "CONFLICT (%s): There is a directory with name %s in %s. " - "Added %s as %s", + output(o, 1, "CONFLICT (%s): There is a directory with name %s in %s. " + "Adding %s as %s", conf, path, other_branch, path, new_path); - remove_file(0, path, 0); - update_file(0, sha, mode, new_path); + if (o->call_depth) + remove_file_from_cache(path); + update_file(o, 0, sha, mode, new_path); + if (o->call_depth) + remove_file_from_cache(path); + free(new_path); } else { - output(2, "Added %s", path); - update_file(1, sha, mode, path); + output(o, 2, "Adding %s", path); + /* do not overwrite file if already present */ + update_file_flags(o, sha, mode, path, 1, !a_sha); } } else if (a_sha && b_sha) { /* Case C: Added in both (check for same permissions) and */ /* case D: Modified in both, but differently. */ - const char *reason = "content"; - struct merge_file_info mfi; - struct diff_filespec o, a, b; - - if (!o_sha) { - reason = "add/add"; - o_sha = (unsigned char *)null_sha1; - } - output(2, "Auto-merged %s", path); - o.path = a.path = b.path = (char *)path; - hashcpy(o.sha1, o_sha); - o.mode = o_mode; - hashcpy(a.sha1, a_sha); - a.mode = a_mode; - hashcpy(b.sha1, b_sha); - b.mode = b_mode; - - mfi = merge_file(&o, &a, &b, - branch1, branch2); - - if (mfi.clean) - update_file(1, mfi.sha, mfi.mode, path); - else { - clean_merge = 0; - output(1, "CONFLICT (%s): Merge conflict in %s", - reason, path); - - if (index_only) - update_file(0, mfi.sha, mfi.mode, path); - else - update_file_flags(mfi.sha, mfi.mode, path, - 0 /* update_cache */, 1 /* update_working_directory */); - } + clean_merge = merge_content(o, path, + o_sha, o_mode, a_sha, a_mode, b_sha, b_mode, + NULL); } else if (!o_sha && !a_sha && !b_sha) { /* * this entry was deleted altogether. a_mode == 0 means * we had that path and want to actively remove it. */ - remove_file(1, path, !a_mode); + remove_file(o, 1, path, !a_mode); } else die("Fatal merge failure, shouldn't happen."); return clean_merge; } -static int merge_trees(struct tree *head, - struct tree *merge, - struct tree *common, - const char *branch1, - const char *branch2, - struct tree **result) +int merge_trees(struct merge_options *o, + struct tree *head, + struct tree *merge, + struct tree *common, + struct tree **result) { int code, clean; - if (subtree_merge) { - merge = shift_tree_object(head, merge); - common = shift_tree_object(head, common); + if (o->subtree_shift) { + merge = shift_tree_object(head, merge, o->subtree_shift); + common = shift_tree_object(head, common, o->subtree_shift); } if (sha_eq(common->object.sha1, merge->object.sha1)) { - output(0, "Already uptodate!"); + output(o, 0, "Already up-to-date!"); *result = head; return 1; } - code = git_merge_trees(index_only, common, head, merge); + code = git_merge_trees(o->call_depth, common, head, merge); - if (code != 0) - die("merging of trees %s and %s failed", - sha1_to_hex(head->object.sha1), - sha1_to_hex(merge->object.sha1)); + if (code != 0) { + if (show(o, 4) || o->call_depth) + die("merging of trees %s and %s failed", + sha1_to_hex(head->object.sha1), + sha1_to_hex(merge->object.sha1)); + else + exit(128); + } - if (unmerged_index()) { - struct path_list *entries, *re_head, *re_merge; + if (unmerged_cache()) { + struct string_list *entries, *re_head, *re_merge; int i; - path_list_clear(¤t_file_set, 1); - path_list_clear(¤t_directory_set, 1); - get_files_dirs(head); - get_files_dirs(merge); + string_list_clear(&o->current_file_set, 1); + string_list_clear(&o->current_directory_set, 1); + get_files_dirs(o, head); + get_files_dirs(o, merge); entries = get_unmerged(); - re_head = get_renames(head, common, head, merge, entries); - re_merge = get_renames(merge, common, head, merge, entries); - clean = process_renames(re_head, re_merge, - branch1, branch2); - for (i = 0; i < entries->nr; i++) { - const char *path = entries->items[i].path; + record_df_conflict_files(o, entries); + re_head = get_renames(o, head, common, head, merge, entries); + re_merge = get_renames(o, merge, common, head, merge, entries); + clean = process_renames(o, re_head, re_merge); + for (i = entries->nr-1; 0 <= i; i--) { + const char *path = entries->items[i].string; struct stage_data *e = entries->items[i].util; if (!e->processed - && !process_entry(path, e, branch1, branch2)) + && !process_entry(o, path, e)) clean = 0; } + for (i = 0; i < entries->nr; i++) { + struct stage_data *e = entries->items[i].util; + if (!e->processed) + die("Unprocessed path??? %s", + entries->items[i].string); + } - path_list_clear(re_merge, 0); - path_list_clear(re_head, 0); - path_list_clear(entries, 1); + string_list_clear(re_merge, 0); + string_list_clear(re_head, 0); + string_list_clear(entries, 1); } else clean = 1; - if (index_only) - *result = git_write_tree(); + if (o->call_depth) + *result = write_tree_from_memory(o); return clean; } @@ -1601,22 +1882,21 @@ static struct commit_list *reverse_commit_list(struct commit_list *list) * Merge the commits h1 and h2, return the resulting virtual * commit object and a flag indicating the cleanness of the merge. */ -static int merge(struct commit *h1, - struct commit *h2, - const char *branch1, - const char *branch2, - struct commit_list *ca, - struct commit **result) +int merge_recursive(struct merge_options *o, + struct commit *h1, + struct commit *h2, + struct commit_list *ca, + struct commit **result) { struct commit_list *iter; struct commit *merged_common_ancestors; - struct tree *mrtree; + struct tree *mrtree = mrtree; int clean; - if (show(4)) { - output(4, "Merging:"); - output_commit_title(h1); - output_commit_title(h2); + if (show(o, 4)) { + output(o, 4, "Merging:"); + output_commit_title(o, h1); + output_commit_title(o, h2); } if (!ca) { @@ -1624,25 +1904,24 @@ static int merge(struct commit *h1, ca = reverse_commit_list(ca); } - if (show(5)) { - output(5, "found %u common ancestor(s):", commit_list_count(ca)); + if (show(o, 5)) { + output(o, 5, "found %u common ancestor(s):", commit_list_count(ca)); for (iter = ca; iter; iter = iter->next) - output_commit_title(iter->item); + output_commit_title(o, iter->item); } merged_common_ancestors = pop_commit(&ca); if (merged_common_ancestors == NULL) { - /* if there is no common ancestor, make an empty tree */ - struct tree *tree = xcalloc(1, sizeof(struct tree)); + /* if there is no common ancestor, use an empty tree */ + struct tree *tree; - tree->object.parsed = 1; - tree->object.type = OBJ_TREE; - pretend_sha1_file(NULL, 0, OBJ_TREE, tree->object.sha1); + tree = lookup_tree(EMPTY_TREE_SHA1_BIN); merged_common_ancestors = make_virtual_commit(tree, "ancestor"); } for (iter = ca; iter; iter = iter->next) { - call_depth++; + const char *saved_b1, *saved_b2; + o->call_depth++; /* * When the merge fails, the result contains files * with conflict markers. The cleanness flag is @@ -1651,135 +1930,164 @@ static int merge(struct commit *h1, * "conflicts" were already resolved. */ discard_cache(); - merge(merged_common_ancestors, iter->item, - "Temporary merge branch 1", - "Temporary merge branch 2", - NULL, - &merged_common_ancestors); - call_depth--; + saved_b1 = o->branch1; + saved_b2 = o->branch2; + o->branch1 = "Temporary merge branch 1"; + o->branch2 = "Temporary merge branch 2"; + merge_recursive(o, merged_common_ancestors, iter->item, + NULL, &merged_common_ancestors); + o->branch1 = saved_b1; + o->branch2 = saved_b2; + o->call_depth--; if (!merged_common_ancestors) die("merge returned no commit"); } discard_cache(); - if (!call_depth) { + if (!o->call_depth) read_cache(); - index_only = 0; - } else - index_only = 1; - clean = merge_trees(h1->tree, h2->tree, merged_common_ancestors->tree, - branch1, branch2, &mrtree); + o->ancestor = "merged common ancestors"; + clean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree, + &mrtree); - if (index_only) { + if (o->call_depth) { *result = make_virtual_commit(mrtree, "merged tree"); commit_list_insert(h1, &(*result)->parents); commit_list_insert(h2, &(*result)->parents->next); } - flush_output(); + flush_output(o); + if (show(o, 2)) + diff_warn_rename_limit("merge.renamelimit", + o->needed_rename_limit, 0); return clean; } -static const char *better_branch_name(const char *branch) -{ - static char githead_env[8 + 40 + 1]; - char *name; - - if (strlen(branch) != 40) - return branch; - sprintf(githead_env, "GITHEAD_%s", branch); - name = getenv(githead_env); - return name ? name : branch; -} - -static struct commit *get_ref(const char *ref) +static struct commit *get_ref(const unsigned char *sha1, const char *name) { - unsigned char sha1[20]; struct object *object; - if (get_sha1(ref, sha1)) - die("Could not resolve ref '%s'", ref); - object = deref_tag(parse_object(sha1), ref, strlen(ref)); + object = deref_tag(parse_object(sha1), name, strlen(name)); + if (!object) + return NULL; if (object->type == OBJ_TREE) - return make_virtual_commit((struct tree*)object, - better_branch_name(ref)); + return make_virtual_commit((struct tree*)object, name); if (object->type != OBJ_COMMIT) return NULL; if (parse_commit((struct commit *)object)) - die("Could not parse commit '%s'", sha1_to_hex(object->sha1)); + return NULL; return (struct commit *)object; } -static int merge_config(const char *var, const char *value) -{ - if (!strcasecmp(var, "merge.verbosity")) { - verbosity = git_config_int(var, value); - return 0; - } - return git_default_config(var, value); -} - -int main(int argc, char *argv[]) +int merge_recursive_generic(struct merge_options *o, + const unsigned char *head, + const unsigned char *merge, + int num_base_list, + const unsigned char **base_list, + struct commit **result) { - static const char *bases[20]; - static unsigned bases_count = 0; - int i, clean; - const char *branch1, *branch2; - struct commit *result, *h1, *h2; - struct commit_list *ca = NULL; + int clean, index_fd; struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); - int index_fd; + struct commit *head_commit = get_ref(head, o->branch1); + struct commit *next_commit = get_ref(merge, o->branch2); + struct commit_list *ca = NULL; - if (argv[0]) { - int namelen = strlen(argv[0]); - if (8 < namelen && - !strcmp(argv[0] + namelen - 8, "-subtree")) - subtree_merge = 1; + if (base_list) { + int i; + for (i = 0; i < num_base_list; ++i) { + struct commit *base; + if (!(base = get_ref(base_list[i], sha1_to_hex(base_list[i])))) + return error("Could not parse object '%s'", + sha1_to_hex(base_list[i])); + commit_list_insert(base, &ca); + } } - git_config(merge_config); - if (getenv("GIT_MERGE_VERBOSITY")) - verbosity = strtol(getenv("GIT_MERGE_VERBOSITY"), NULL, 10); + index_fd = hold_locked_index(lock, 1); + clean = merge_recursive(o, head_commit, next_commit, ca, + result); + if (active_cache_changed && + (write_cache(index_fd, active_cache, active_nr) || + commit_locked_index(lock))) + return error("Unable to write index."); - if (argc < 4) - die("Usage: %s <base>... -- <head> <remote> ...\n", argv[0]); + return clean ? 0 : 1; +} - for (i = 1; i < argc; ++i) { - if (!strcmp(argv[i], "--")) - break; - if (bases_count < sizeof(bases)/sizeof(*bases)) - bases[bases_count++] = argv[i]; +static int merge_recursive_config(const char *var, const char *value, void *cb) +{ + struct merge_options *o = cb; + if (!strcmp(var, "merge.verbosity")) { + o->verbosity = git_config_int(var, value); + return 0; } - if (argc - i != 3) /* "--" "<head>" "<remote>" */ - die("Not handling anything other than two heads merge."); - if (verbosity >= 5) - buffer_output = 0; - - branch1 = argv[++i]; - branch2 = argv[++i]; - - h1 = get_ref(branch1); - h2 = get_ref(branch2); - - branch1 = better_branch_name(branch1); - branch2 = better_branch_name(branch2); - - if (show(3)) - printf("Merging %s with %s\n", branch1, branch2); - - index_fd = hold_locked_index(lock, 1); - - for (i = 0; i < bases_count; i++) { - struct commit *ancestor = get_ref(bases[i]); - ca = commit_list_insert(ancestor, &ca); + if (!strcmp(var, "diff.renamelimit")) { + o->diff_rename_limit = git_config_int(var, value); + return 0; + } + if (!strcmp(var, "merge.renamelimit")) { + o->merge_rename_limit = git_config_int(var, value); + return 0; } - clean = merge(h1, h2, branch1, branch2, ca, &result); + return git_xmerge_config(var, value, cb); +} - if (active_cache_changed && - (write_cache(index_fd, active_cache, active_nr) || - close(index_fd) || commit_locked_index(lock))) - die ("unable to write %s", get_index_file()); +void init_merge_options(struct merge_options *o) +{ + memset(o, 0, sizeof(struct merge_options)); + o->verbosity = 2; + o->buffer_output = 1; + o->diff_rename_limit = -1; + o->merge_rename_limit = -1; + o->renormalize = 0; + git_config(merge_recursive_config, o); + if (getenv("GIT_MERGE_VERBOSITY")) + o->verbosity = + strtol(getenv("GIT_MERGE_VERBOSITY"), NULL, 10); + if (o->verbosity >= 5) + o->buffer_output = 0; + strbuf_init(&o->obuf, 0); + memset(&o->current_file_set, 0, sizeof(struct string_list)); + o->current_file_set.strdup_strings = 1; + memset(&o->current_directory_set, 0, sizeof(struct string_list)); + o->current_directory_set.strdup_strings = 1; + memset(&o->df_conflict_file_set, 0, sizeof(struct string_list)); + o->df_conflict_file_set.strdup_strings = 1; +} - return clean ? 0: 1; +int parse_merge_opt(struct merge_options *o, const char *s) +{ + if (!s || !*s) + return -1; + if (!strcmp(s, "ours")) + o->recursive_variant = MERGE_RECURSIVE_OURS; + else if (!strcmp(s, "theirs")) + o->recursive_variant = MERGE_RECURSIVE_THEIRS; + else if (!strcmp(s, "subtree")) + o->subtree_shift = ""; + else if (!prefixcmp(s, "subtree=")) + o->subtree_shift = s + strlen("subtree="); + else if (!strcmp(s, "patience")) + o->xdl_opts = DIFF_WITH_ALG(o, PATIENCE_DIFF); + else if (!strcmp(s, "histogram")) + o->xdl_opts = DIFF_WITH_ALG(o, HISTOGRAM_DIFF); + else if (!strcmp(s, "ignore-space-change")) + o->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE; + else if (!strcmp(s, "ignore-all-space")) + o->xdl_opts |= XDF_IGNORE_WHITESPACE; + else if (!strcmp(s, "ignore-space-at-eol")) + o->xdl_opts |= XDF_IGNORE_WHITESPACE_AT_EOL; + else if (!strcmp(s, "renormalize")) + o->renormalize = 1; + else if (!strcmp(s, "no-renormalize")) + o->renormalize = 0; + else if (!prefixcmp(s, "rename-threshold=")) { + const char *score = s + strlen("rename-threshold="); + if ((o->rename_score = parse_rename_score(&score)) == -1 || *score != 0) + return -1; + } + else + return -1; + return 0; } |