X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=merge-recursive.c;h=875859f68efaab5113785896326140c8d0543f45;hb=2844923d62a4c408bd59ddb2caacca4aa7eb86bc;hp=df90be44a51f3af5ebf9eb65e95b7d4fc18cc2f9;hpb=2acf3658a31249d1a09573e2b6870871b5323121;p=git.git diff --git a/merge-recursive.c b/merge-recursive.c index df90be44a..875859f68 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -180,7 +180,7 @@ static int git_merge_trees(int index_only, opts.fn = threeway_merge; opts.src_index = &the_index; opts.dst_index = &the_index; - set_porcelain_error_msgs(opts.msgs, "merge"); + setup_unpack_trees_porcelain(&opts, "merge"); init_tree_desc_from_tree(t+0, common); init_tree_desc_from_tree(t+1, head); @@ -334,6 +334,7 @@ static struct string_list *get_renames(struct merge_options *o, opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit : o->diff_rename_limit >= 0 ? o->diff_rename_limit : 500; + opts.rename_score = o->rename_score; opts.warn_on_too_large_rename = 1; opts.output_format = DIFF_FORMAT_NO_OUTPUT; if (diff_setup_done(&opts) < 0) @@ -605,22 +606,26 @@ static int merge_3way(struct merge_options *o, const char *branch2) { mmfile_t orig, src1, src2; + struct ll_merge_options ll_opts = {0}; char *base_name, *name1, *name2; int merge_status; - int favor; - if (o->call_depth) - favor = 0; - else { + ll_opts.renormalize = o->renormalize; + ll_opts.xdl_opts = o->xdl_opts; + + if (o->call_depth) { + ll_opts.virtual_ancestor = 1; + ll_opts.variant = 0; + } else { switch (o->recursive_variant) { case MERGE_RECURSIVE_OURS: - favor = XDL_MERGE_FAVOR_OURS; + ll_opts.variant = XDL_MERGE_FAVOR_OURS; break; case MERGE_RECURSIVE_THEIRS: - favor = XDL_MERGE_FAVOR_THEIRS; + ll_opts.variant = XDL_MERGE_FAVOR_THEIRS; break; default: - favor = 0; + ll_opts.variant = 0; break; } } @@ -643,8 +648,7 @@ static int merge_3way(struct merge_options *o, read_mmblob(&src2, b->sha1); merge_status = ll_merge(result_buf, a->path, &orig, base_name, - &src1, name1, &src2, name2, - (!!o->call_depth) | (favor << 1)); + &src1, name1, &src2, name2, &ll_opts); free(name1); free(name2); @@ -953,6 +957,12 @@ static int process_renames(struct merge_options *o, ren1->pair->two : NULL, branch1 == o->branch1 ? NULL : ren1->pair->two, 1); + } 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 */ + update_file(o, 1, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst); } else if (!sha_eq(dst_other.sha1, null_sha1)) { const char *new_path; clean_merge = 0; @@ -1062,6 +1072,53 @@ 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; +} + /* Per entry merge function */ static int process_entry(struct merge_options *o, const char *path, struct stage_data *entry) @@ -1071,6 +1128,7 @@ static int process_entry(struct merge_options *o, 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; @@ -1082,8 +1140,8 @@ static int process_entry(struct merge_options *o, 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) @@ -1238,50 +1296,6 @@ static int process_df_entry(struct merge_options *o, return clean_merge; } -void set_porcelain_error_msgs(const char **msgs, const char *cmd) -{ - const char *msg; - char *tmp; - const char *cmd2 = strcmp(cmd, "checkout") ? cmd : "switch branches"; - if (advice_commit_before_merge) - msg = "Your local changes to the following files would be overwritten by %s:\n%%s" - "Please, commit your changes or stash them before you can %s."; - else - msg = "Your local changes to the following files would be overwritten by %s:\n%%s"; - tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 2); - sprintf(tmp, msg, cmd, cmd2); - msgs[ERROR_WOULD_OVERWRITE] = tmp; - msgs[ERROR_NOT_UPTODATE_FILE] = tmp; - - msgs[ERROR_NOT_UPTODATE_DIR] = - "Updating the following directories would lose untracked files in it:\n%s"; - - if (advice_commit_before_merge) - msg = "The following untracked working tree files would be %s by %s:\n%%s" - "Please move or remove them before you can %s."; - else - msg = "The following untracked working tree files would be %s by %s:\n%%s"; - tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("removed") + strlen(cmd2) - 4); - sprintf(tmp, msg, "removed", cmd, cmd2); - msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = tmp; - tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("overwritten") + strlen(cmd2) - 4); - sprintf(tmp, msg, "overwritten", cmd, cmd2); - msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = tmp; - - /* - * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we - * cannot easily display it as a list. - */ - msgs[ERROR_BIND_OVERLAP] = "Entry '%s' overlaps with '%s'. Cannot bind."; - - msgs[ERROR_SPARSE_NOT_UPTODATE_FILE] = - "Cannot update sparse checkout: the following entries are not up-to-date:\n%s"; - msgs[ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN] = - "The following Working tree files would be overwritten by sparse checkout update:\n%s"; - msgs[ERROR_WOULD_LOSE_ORPHANED_REMOVED] = - "The following Working tree files would be removed by sparse checkout update:\n%s"; -} - int merge_trees(struct merge_options *o, struct tree *head, struct tree *merge, @@ -1525,6 +1539,7 @@ void init_merge_options(struct merge_options *o) 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 = @@ -1537,3 +1552,37 @@ void init_merge_options(struct merge_options *o) memset(&o->current_directory_set, 0, sizeof(struct string_list)); o->current_directory_set.strdup_strings = 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 |= XDF_PATIENCE_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; +}