diff options
Diffstat (limited to 'merge-recursive.c')
-rw-r--r-- | merge-recursive.c | 1112 |
1 files changed, 539 insertions, 573 deletions
diff --git a/merge-recursive.c b/merge-recursive.c index 3096594b3..d415c4188 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -7,16 +7,18 @@ #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" - -static int subtree_merge; +#include "ll-merge.h" +#include "attr.h" +#include "merge-recursive.h" +#include "dir.h" static struct tree *shift_tree_object(struct tree *one, struct tree *two) { @@ -33,26 +35,14 @@ static struct tree *shift_tree_object(struct tree *one, struct tree *two) } /* - * A virtual commit has - * - (const char *)commit->util set to the name, and - * - *(int *)commit->object.sha1 set to the virtual id. + * A virtual commit has (const char *)commit->util set to the name. */ -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; commit->tree = tree; commit->util = (void*)comment; - *(int*)commit->object.sha1 = virtual_id++; /* avoid warnings */ commit->object.parsed = 1; return commit; @@ -83,75 +73,57 @@ struct stage_data unsigned processed:1; }; -struct output_buffer -{ - struct output_buffer *next; - char *str; -}; - -static struct path_list current_file_set = {NULL, 0, 0, 1}; -static struct path_list current_directory_set = {NULL, 0, 0, 1}; - -static int call_depth = 0; -static int verbosity = 2; -static int buffer_output = 1; -static int do_progress = 1; -static unsigned last_percent; -static unsigned merged_cnt; -static unsigned total_cnt; -static volatile sig_atomic_t progress_update; -static struct output_buffer *output_list, *output_end; - -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() +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); + int len; + 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); + len = vsnprintf(o->obuf.buf + o->obuf.len, strbuf_avail(&o->obuf), fmt, ap); + va_end(ap); + + if (len < 0) + len = 0; + if (len >= strbuf_avail(&o->obuf)) { + strbuf_grow(&o->obuf, len + 2); + va_start(ap, fmt); + len = vsnprintf(o->obuf.buf + o->obuf.len, strbuf_avail(&o->obuf), fmt, ap); + va_end(ap); + if (len >= strbuf_avail(&o->obuf)) { + die("this should not happen, your snprintf is broken"); + } } - output_list = NULL; - output_end = NULL; + strbuf_setlen(&o->obuf, o->obuf.len + len); + 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); @@ -174,63 +146,6 @@ static void output_commit_title(struct commit *commit) } } -static void progress_interval(int signum) -{ - progress_update = 1; -} - -static void setup_progress_signal(void) -{ - struct sigaction sa; - struct itimerval v; - - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = progress_interval; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - sigaction(SIGALRM, &sa, NULL); - - v.it_interval.tv_sec = 1; - v.it_interval.tv_usec = 0; - v.it_value = v.it_interval; - setitimer(ITIMER_REAL, &v, NULL); -} - -static void display_progress() -{ - unsigned percent = total_cnt ? merged_cnt * 100 / total_cnt : 0; - if (progress_update || percent != last_percent) { - fprintf(stderr, "%4u%% (%u/%u) done\r", - percent, merged_cnt, total_cnt); - progress_update = 0; - last_percent = percent; - } -} - -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) { @@ -241,16 +156,11 @@ 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); + init_tree_desc(desc, tree->buffer, tree->size); +} static int git_merge_trees(int index_only, struct tree *common, @@ -258,7 +168,7 @@ static int git_merge_trees(int index_only, struct tree *merge) { int rc; - struct object_list *trees = NULL; + struct tree_desc t[3]; struct unpack_trees_options opts; memset(&opts, 0, sizeof(opts)); @@ -269,38 +179,29 @@ 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; - object_list_append(&common->object, &trees); - object_list_append(&head->object, &trees); - object_list_append(&merge->object, &trees); + init_tree_desc_from_tree(t+0, common); + init_tree_desc_from_tree(t+1, head); + init_tree_desc_from_tree(t+2, merge); - rc = unpack_trees(trees, &opts); + rc = unpack_trees(3, t, &opts); cache_tree_free(&active_cache_tree); 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:"); + output(o, 0, "There are unmerged index entries:"); 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); + output(o, 0, "%d %.*s", ce_stage(ce), ce_namelen(ce), ce->name); } return NULL; } @@ -320,41 +221,43 @@ 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(newpath, &o->current_directory_set); else - path_list_insert(newpath, ¤t_file_set); + string_list_insert(newpath, &o->current_file_set); 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) + if (read_tree_recursive(tree, "", 0, 0, NULL, 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); @@ -362,7 +265,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(path, entries); item->util = e; return e; } @@ -371,30 +274,27 @@ 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; - total_cnt += active_nr; + unmerged->strdup_strings = 1; - for (i = 0; i < active_nr; i++, merged_cnt++) { - struct path_list_item *item; + for (i = 0; i < active_nr; i++) { + struct string_list_item *item; struct stage_data *e; struct cache_entry *ce = active_cache[i]; - if (do_progress) - display_progress(); if (!ce_stage(ce)) continue; - item = path_list_lookup(ce->name, unmerged); + item = string_list_lookup(ce->name, unmerged); if (!item) { - item = path_list_insert(ce->name, unmerged); + item = string_list_insert(ce->name, unmerged); 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); } @@ -415,27 +315,32 @@ 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); 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 : + 500; + opts.warn_on_too_large_rename = 1; 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); 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') { @@ -445,20 +350,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(re->pair->one->path, entries); 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(re->pair->two->path, entries); 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(pair->one->path, renames); item->util = re; } opts.output_format = DIFF_FORMAT_NO_OUTPUT; @@ -487,47 +392,24 @@ static int update_stages(const char *path, struct diff_filespec *o, return 0; } -static int remove_path(const char *name) +static int remove_file(struct merge_options *o, int clean, + const char *path, int no_wd) { - 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; -} - -static int remove_file(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) && errno != ENOENT) 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; @@ -539,24 +421,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(newpath, &o->current_file_set); 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) { @@ -565,7 +438,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?"); } @@ -574,13 +447,71 @@ static void flush_buffer(int fd, const char *buf, unsigned long size) } } -static void update_file_flags(const unsigned char *sha, +static int would_lose_untracked(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 0; + } + pos++; + } + return file_exists(path); +} + +static int make_room_for_path(const char *path) +{ + int status; + const char *msg = "failed to create path '%s'%s"; + + status = safe_create_leading_directories_const(path); + if (status) { + if (status == -3) { + /* something else exists */ + error(msg, path, ": perhaps a D/F conflict?"); + return -1; + } + 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; + /* .. and so is no existing file */ + if (errno == ENOENT) + return 0; + /* .. but not some other error (who really cares what?) */ + return error(msg, path, ": perhaps a D/F conflict?"); +} + +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) { @@ -588,48 +519,68 @@ 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. + */ + 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) { + update_wd = 0; + free(buf); + goto update_index; + } if (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) { int fd; - if (mkdir_p(path, 0777)) - die("failed to create path %s: %s", path, strerror(errno)); - unlink(path); if (mode & 0100) mode = 0777; else 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 */ @@ -659,9 +610,48 @@ static void fill_mm(const unsigned char *sha1, mmfile_t *mm) mm->size = size; } -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 int merge_3way(struct merge_options *o, + mmbuffer_t *result_buf, + struct diff_filespec *one, + 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; + + if (strcmp(a->path, b->path)) { + name1 = xstrdup(mkpath("%s:%s", branch1, a->path)); + name2 = xstrdup(mkpath("%s:%s", branch2, b->path)); + } else { + name1 = xstrdup(mkpath("%s", branch1)); + name2 = xstrdup(mkpath("%s", branch2)); + } + + fill_mm(one->sha1, &orig); + fill_mm(a->sha1, &src1); + fill_mm(b->sha1, &src2); + + merge_status = ll_merge(result_buf, a->path, &orig, + &src1, name1, &src2, name2, + o->call_depth); + + free(name1); + free(name2); + free(orig.ptr); + free(src1.ptr); + free(src2.ptr); + return merge_status; +} + +static struct merge_file_info merge_file(struct merge_options *o, + struct diff_filespec *one, + struct diff_filespec *a, + struct diff_filespec *b, + const char *branch1, + const char *branch2) { struct merge_file_info result; result.merge = 0; @@ -677,40 +667,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)) { - mmfile_t orig, src1, src2; mmbuffer_t result_buf; - xpparam_t xpp; - char *name1, *name2; int merge_status; - 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); - - memset(&xpp, 0, sizeof(xpp)); - merge_status = xdl_merge(&orig, - &src1, name1, - &src2, name2, - &xpp, XDL_MERGE_ZEALOUS, - &result_buf); - free(name1); - free(name2); - free(orig.ptr); - free(src1.ptr); - free(src2.ptr); + 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"); @@ -722,21 +704,24 @@ 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 = 0; + hashcpy(result.sha, a->sha1); + } 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, +static void conflict_rename_rename(struct merge_options *o, + struct rename *ren1, const char *branch1, struct rename *ren2, const char *branch2) @@ -747,26 +732,26 @@ static void conflict_rename_rename(struct rename *ren1, 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", + if (string_list_has_string(&o->current_directory_set, ren1_dst)) { + dst_name1 = del[delp++] = unique_path(o, ren1_dst, branch1); + output(o, 1, "%s is a directory in %s adding as %s instead", ren1_dst, branch2, dst_name1); - remove_file(0, ren1_dst, 0); + remove_file(o, 0, ren1_dst, 0); } - 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", + if (string_list_has_string(&o->current_directory_set, ren2_dst)) { + dst_name2 = del[delp++] = unique_path(o, ren2_dst, branch2); + output(o, 1, "%s is a directory in %s adding as %s instead", ren2_dst, branch1, dst_name2); - remove_file(0, ren2_dst, 0); + remove_file(o, 0, ren2_dst, 0); } - if (index_only) { + if (o->call_depth) { remove_file_from_cache(dst_name1); remove_file_from_cache(dst_name2); /* * 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); + * update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, dst_name1); + * update_file(o, 0, ren2->pair->two->sha1, ren2->pair->two->mode, dst_name2); */ } else { update_stages(dst_name1, NULL, ren1->pair->two, NULL, 1); @@ -776,70 +761,68 @@ static void conflict_rename_rename(struct rename *ren1, free(del[delp]); } -static void conflict_rename_dir(struct rename *ren1, +static void conflict_rename_dir(struct merge_options *o, + struct rename *ren1, const char *branch1) { - 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); + char *new_path = unique_path(o, ren1->pair->two->path, branch1); + output(o, 1, "Renaming %s to %s instead", ren1->pair->one->path, new_path); + remove_file(o, 0, ren1->pair->two->path, 0); + update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path); free(new_path); } -static void conflict_rename_rename_2(struct rename *ren1, +static void conflict_rename_rename_2(struct merge_options *o, + struct rename *ren1, const char *branch1, struct rename *ren2, const char *branch2) { - 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", + char *new_path1 = unique_path(o, ren1->pair->two->path, branch1); + char *new_path2 = unique_path(o, ren2->pair->two->path, branch2); + output(o, 1, "Renaming %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); + remove_file(o, 0, ren1->pair->two->path, 0); + update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path1); + update_file(o, 0, ren2->pair->two->sha1, ren2->pair->two->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 = {NULL, 0, 0, 0}, b_by_dst = {NULL, 0, 0, 0}; 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 + string_list_insert(sre->pair->two->path, &a_by_dst)->util = sre->dst_entry; } 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 + string_list_insert(sre->pair->two->path, &b_by_dst)->util = sre->dst_entry; } 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; 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) @@ -849,17 +832,15 @@ 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; @@ -886,54 +867,55 @@ static int process_renames(struct path_list *a_renames, ren2->processed = 1; if (strcmp(ren1_dst, ren2_dst) != 0) { clean_merge = 0; - output(1, "CONFLICT (rename/rename): " + output(o, 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) { + o->call_depth ? " (left unresolved)": ""); + if (o->call_depth) { remove_file_from_cache(src); - update_file(0, ren1->pair->one->sha1, + update_file(o, 0, ren1->pair->one->sha1, ren1->pair->one->mode, src); } - conflict_rename_rename(ren1, branch1, ren2, branch2); + conflict_rename_rename(o, ren1, branch1, ren2, branch2); } else { struct merge_file_info mfi; - remove_file(1, ren1_src, 1); - mfi = merge_file(ren1->pair->one, + remove_file(o, 1, ren1_src, 1); + mfi = merge_file(o, + ren1->pair->one, ren1->pair->two, ren2->pair->two, branch1, branch2); if (mfi.merge || !mfi.clean) - output(1, "Renamed %s->%s", src, ren1_dst); + output(o, 1, "Renaming %s->%s", src, ren1_dst); if (mfi.merge) - output(2, "Auto-merged %s", ren1_dst); + output(o, 2, "Auto-merging %s", ren1_dst); if (!mfi.clean) { - output(1, "CONFLICT (content): merge conflict in %s", + output(o, 1, "CONFLICT (content): merge conflict in %s", ren1_dst); clean_merge = 0; - if (!index_only) + if (!o->call_depth) 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); + update_file(o, mfi.clean, mfi.sha, mfi.mode, ren1_dst); } } else { /* Renamed in 1, maybe changed in 2 */ - struct path_list_item *item; + struct string_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); + remove_file(o, 1, ren1_src, o->call_depth || stage == 3); hashcpy(src_other.sha1, ren1->src_entry->stages[stage].sha); src_other.mode = ren1->src_entry->stages[stage].mode; @@ -942,49 +924,56 @@ static int process_renames(struct path_list *a_renames, try_merge = 0; - if (path_list_has_path(¤t_directory_set, ren1_dst)) { + if (string_list_has_string(&o->current_directory_set, ren1_dst)) { clean_merge = 0; - output(1, "CONFLICT (rename/directory): Renamed %s->%s in %s " + output(o, 1, "CONFLICT (rename/directory): Rename %s->%s in %s " " directory %s added in %s", ren1_src, ren1_dst, branch1, ren1_dst, branch2); - conflict_rename_dir(ren1, branch1); + conflict_rename_dir(o, ren1, branch1); } else if (sha_eq(src_other.sha1, null_sha1)) { clean_merge = 0; - output(1, "CONFLICT (rename/delete): Renamed %s->%s in %s " + output(o, 1, "CONFLICT (rename/delete): Rename %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); + update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst); + if (!o->call_depth) + update_stages(ren1_dst, NULL, + branch1 == o->branch1 ? + ren1->pair->two : NULL, + branch1 == o->branch1 ? + NULL : ren1->pair->two, 1); } 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))) { + 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); + } else if ((item = string_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", + output(o, 1, "CONFLICT (rename/rename): " + "Rename %s->%s in %s. " + "Rename %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); + conflict_rename_rename_2(o, ren1, branch1, ren2, branch2); } else try_merge = 1; if (try_merge) { - struct diff_filespec *o, *a, *b; + struct diff_filespec *one, *a, *b; struct merge_file_info mfi; 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; @@ -992,53 +981,63 @@ 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.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); + mfi = merge_file(o, one, a, b, + o->branch1, o->branch2); + + 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(o, 3, "Skipped %s (merged same as existing)", ren1_dst); + else { + if (mfi.merge || !mfi.clean) + output(o, 1, "Renaming %s => %s", ren1_src, ren1_dst); + if (mfi.merge) + output(o, 2, "Auto-merging %s", ren1_dst); + if (!mfi.clean) { + output(o, 1, "CONFLICT (rename/modify): Merge conflict in %s", + ren1_dst); + clean_merge = 0; + + if (!o->call_depth) + update_stages(ren1_dst, + one, a, b, 1); + } + update_file(o, mfi.clean, mfi.sha, mfi.mode, ren1_dst); } - update_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst); } } } - 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; } -static unsigned char *has_sha(const unsigned char *sha) +static unsigned char *stage_sha(const unsigned char *sha, unsigned mode) { - return is_null_sha1(sha) ? NULL: (unsigned char *)sha; + return (is_null_sha1(sha) || mode == 0) ? NULL: (unsigned char *)sha; } /* 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; - unsigned char *o_sha = has_sha(entry->stages[1].sha); - unsigned char *a_sha = has_sha(entry->stages[2].sha); - unsigned char *b_sha = has_sha(entry->stages[3].sha); unsigned o_mode = entry->stages[1].mode; unsigned a_mode = entry->stages[2].mode; unsigned b_mode = entry->stages[3].mode; + unsigned char *o_sha = stage_sha(entry->stages[1].sha, o_mode); + 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)) { /* Case A: Deleted in one */ @@ -1048,24 +1047,24 @@ static int process_entry(const char *path, struct stage_data *entry, /* 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 */ clean_merge = 0; if (!a_sha) { - output(1, "CONFLICT (delete/modify): %s deleted in %s " + output(o, 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); + path, o->branch1, + o->branch2, o->branch2, path); + update_file(o, 0, b_sha, b_mode, path); } else { - output(1, "CONFLICT (delete/modify): %s deleted in %s " + output(o, 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); + path, o->branch2, + o->branch1, o->branch1, path); + update_file(o, 0, a_sha, a_mode, path); } } @@ -1079,133 +1078,129 @@ 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 (string_list_has_string(&o->current_directory_set, path)) { + const 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); + remove_file(o, 0, path, 0); + update_file(o, 0, sha, mode, new_path); } else { - output(2, "Added %s", path); - update_file(1, sha, mode, path); + output(o, 2, "Adding %s", path); + update_file(o, 1, sha, mode, path); } } 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; + struct diff_filespec one, 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; + output(o, 2, "Auto-merging %s", path); + 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; - mfi = merge_file(&o, &a, &b, - branch1, branch2); + mfi = merge_file(o, &one, &a, &b, + o->branch1, o->branch2); - if (mfi.clean) - update_file(1, mfi.sha, mfi.mode, path); - else { - clean_merge = 0; - output(1, "CONFLICT (%s): Merge conflict in %s", + clean_merge = mfi.clean; + if (!mfi.clean) { + if (S_ISGITLINK(mfi.mode)) + reason = "submodule"; + output(o, 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 */); } + update_file(o, mfi.clean, mfi.sha, mfi.mode, path); + } 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(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) { + if (o->subtree_merge) { merge = shift_tree_object(head, merge); common = shift_tree_object(head, common); } if (sha_eq(common->object.sha1, merge->object.sha1)) { - output(0, "Already uptodate!"); + output(o, 0, "Already uptodate!"); *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 (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); - total_cnt += entries->nr; - for (i = 0; i < entries->nr; i++, merged_cnt++) { - const char *path = entries->items[i].path; + 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 = 0; i < entries->nr; 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; - if (do_progress) - display_progress(); } - 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; } @@ -1225,22 +1220,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) { @@ -1248,10 +1242,10 @@ 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); @@ -1266,7 +1260,8 @@ static int merge(struct commit *h1, } 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 @@ -1275,150 +1270,121 @@ 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); + 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); } - if (!call_depth && do_progress) { - /* Make sure we end at 100% */ - if (!total_cnt) - total_cnt = 1; - merged_cnt = total_cnt; - progress_update = 1; - display_progress(); - fputc('\n', stderr); - } - flush_output(); + flush_output(o); 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 (!strcasecmp(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; - do_progress = 0; + if (!strcasecmp(var, "diff.renamelimit")) { + o->diff_rename_limit = git_config_int(var, value); + return 0; } - else - do_progress = isatty(1); - - 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 (do_progress) - setup_progress_signal(); - 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 (!strcasecmp(var, "merge.renamelimit")) { + o->merge_rename_limit = git_config_int(var, value); + return 0; } - clean = merge(h1, h2, branch1, branch2, ca, &result); - - 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()); + return git_xmerge_config(var, value, cb); +} - return clean ? 0: 1; +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; + 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; } |