Code

add support for the SUA layer (interix; windows)
[git.git] / merge-recursive.c
index 52936c35aec413ad8adf10cdd0a80f9bd282abd2..875859f68efaab5113785896326140c8d0543f45 100644 (file)
@@ -20,6 +20,7 @@
 #include "attr.h"
 #include "merge-recursive.h"
 #include "dir.h"
+#include "submodule.h"
 
 static struct tree *shift_tree_object(struct tree *one, struct tree *two,
                                      const char *subtree_shift)
@@ -136,16 +137,10 @@ static void output_commit_title(struct merge_options *o, struct commit *commit)
                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);
                }
        }
 }
@@ -185,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;
-       opts.msgs = get_porcelain_error_msgs();
+       setup_unpack_trees_porcelain(&opts, "merge");
 
        init_tree_desc_from_tree(t+0, common);
        init_tree_desc_from_tree(t+1, head);
@@ -238,9 +233,9 @@ static int save_files_dirs(const unsigned char *sha1,
        newpath[baselen + len] = '\0';
 
        if (S_ISDIR(mode))
-               string_list_insert(newpath, &o->current_directory_set);
+               string_list_insert(&o->current_directory_set, newpath);
        else
-               string_list_insert(newpath, &o->current_file_set);
+               string_list_insert(&o->current_file_set, newpath);
        free(newpath);
 
        return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
@@ -271,7 +266,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 = string_list_insert(path, entries);
+       item = string_list_insert(entries, path);
        item->util = e;
        return e;
 }
@@ -294,9 +289,9 @@ static struct string_list *get_unmerged(void)
                if (!ce_stage(ce))
                        continue;
 
-               item = string_list_lookup(ce->name, unmerged);
+               item = string_list_lookup(unmerged, ce->name);
                if (!item) {
-                       item = string_list_insert(ce->name, unmerged);
+                       item = string_list_insert(unmerged, ce->name);
                        item->util = xcalloc(1, sizeof(struct stage_data));
                }
                e = item->util;
@@ -339,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)
@@ -356,20 +352,20 @@ static struct string_list *get_renames(struct merge_options *o,
                re = xmalloc(sizeof(*re));
                re->processed = 0;
                re->pair = pair;
-               item = string_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 = string_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 = string_list_insert(pair->one->path, renames);
+               item = string_list_insert(renames, pair->one->path);
                item->util = re;
        }
        opts.output_format = DIFF_FORMAT_NO_OUTPUT;
@@ -409,7 +405,7 @@ static int remove_file(struct merge_options *o, int clean,
                        return -1;
        }
        if (update_working_directory) {
-               if (remove_path(path) && errno != ENOENT)
+               if (remove_path(path))
                        return -1;
        }
        return 0;
@@ -432,7 +428,7 @@ static char *unique_path(struct merge_options *o, const char *path, const char *
               lstat(newpath, &st) == 0)
                sprintf(p, "_%d", suffix++);
 
-       string_list_insert(newpath, &o->current_file_set);
+       string_list_insert(&o->current_file_set, newpath);
        return newpath;
 }
 
@@ -525,13 +521,15 @@ static void update_file_flags(struct merge_options *o,
                void *buf;
                unsigned long size;
 
-               if (S_ISGITLINK(mode))
+               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)
@@ -608,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;
                }
        }
@@ -646,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);
@@ -716,8 +717,8 @@ static struct merge_file_info merge_file(struct merge_options *o,
                        free(result_buf.ptr);
                        result.clean = (merge_status == 0);
                } else if (S_ISGITLINK(a->mode)) {
-                       result.clean = 0;
-                       hashcpy(result.sha, a->sha1);
+                       result.clean = merge_submodule(result.sha, one->path, one->sha1,
+                                                      a->sha1, b->sha1);
                } else if (S_ISLNK(a->mode)) {
                        hashcpy(result.sha, a->sha1);
 
@@ -806,17 +807,18 @@ static int process_renames(struct merge_options *o,
                           struct string_list *b_renames)
 {
        int clean_merge = 1, i, j;
-       struct string_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;
-               string_list_insert(sre->pair->two->path, &a_by_dst)->util
+               string_list_insert(&a_by_dst, sre->pair->two->path)->util
                        = sre->dst_entry;
        }
        for (i = 0; i < b_renames->nr; i++) {
                sre = b_renames->items[i].util;
-               string_list_insert(sre->pair->two->path, &b_by_dst)->util
+               string_list_insert(&b_by_dst, sre->pair->two->path)->util
                        = sre->dst_entry;
        }
 
@@ -955,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;
@@ -988,7 +996,7 @@ static int process_renames(struct merge_options *o,
                                        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))) {
+                       } else if ((item = string_list_lookup(renames2Dst, ren1_dst))) {
                                ren2 = item->util;
                                clean_merge = 0;
                                ren2->processed = 1;
@@ -1031,12 +1039,9 @@ static int process_renames(struct merge_options *o,
                                         * in the index (e.g. due to a D/F
                                         * conflict) that need to be resolved.
                                         */
-                                       for (i = 1; i <= 3; i++) {
-                                               if (!ren1->dst_entry->stages[i].mode)
-                                                       continue;
+                                       if (!ren1->dst_entry->stages[2].mode !=
+                                           !ren1->dst_entry->stages[3].mode)
                                                ren1->dst_entry->processed = 0;
-                                               break;
-                                       }
                                } else {
                                        if (mfi.merge || !mfi.clean)
                                                output(o, 1, "Renaming %s => %s", ren1_src, ren1_dst);
@@ -1067,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)
@@ -1076,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;
@@ -1087,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)
@@ -1243,28 +1296,6 @@ static int process_df_entry(struct merge_options *o,
        return clean_merge;
 }
 
-struct unpack_trees_error_msgs get_porcelain_error_msgs(void)
-{
-       struct unpack_trees_error_msgs msgs = {
-               /* would_overwrite */
-               "Your local changes to '%s' would be overwritten by merge.  Aborting.",
-               /* not_uptodate_file */
-               "Your local changes to '%s' would be overwritten by merge.  Aborting.",
-               /* not_uptodate_dir */
-               "Updating '%s' would lose untracked files in it.  Aborting.",
-               /* would_lose_untracked */
-               "Untracked working tree file '%s' would be %s by merge.  Aborting",
-               /* bind_overlap -- will not happen here */
-               NULL,
-       };
-       if (advice_commit_before_merge) {
-               msgs.would_overwrite = msgs.not_uptodate_file =
-                       "Your local changes to '%s' would be overwritten by merge.  Aborting.\n"
-                       "Please, commit your changes or stash them before you can merge.";
-       }
-       return msgs;
-}
-
 int merge_trees(struct merge_options *o,
                struct tree *head,
                struct tree *merge,
@@ -1279,7 +1310,7 @@ int merge_trees(struct merge_options *o,
        }
 
        if (sha_eq(common->object.sha1, merge->object.sha1)) {
-               output(o, 0, "Already uptodate!");
+               output(o, 0, "Already up-to-date!");
                *result = head;
                return 1;
        }
@@ -1508,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 =
@@ -1520,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;
+}