Merge branch 'jc/maint-svn-info-test-fix'
[gitweb.git] / merge-recursive.c
index 0271d16c66ba88219d37f6d523a5add4ce9f835a..16c2dbeab95a0f4099e3de8badf688bb4dee8189 100644 (file)
@@ -346,6 +346,63 @@ static struct string_list *get_unmerged(void)
        return unmerged;
 }
 
+static void make_room_for_directories_of_df_conflicts(struct merge_options *o,
+                                                     struct string_list *entries)
+{
+       /* If there are D/F conflicts, and the paths currently exist
+        * in the working copy as a file, we want to remove them to
+        * make room for the corresponding directory.  Such paths will
+        * later be processed in process_df_entry() at the end.  If
+        * the corresponding directory ends up being removed by the
+        * merge, then the file will be reinstated at that time;
+        * otherwise, if the file is not supposed to be removed by the
+        * merge, the contents of the file will be placed in another
+        * unique filename.
+        *
+        * NOTE: This function relies on the fact that entries for a
+        * D/F conflict will appear adjacent in the index, with the
+        * entries for the file appearing before entries for paths
+        * below the corresponding directory.
+        */
+       const char *last_file = NULL;
+       int last_len = 0;
+       struct stage_data *last_e;
+       int i;
+
+       for (i = 0; i < entries->nr; i++) {
+               const char *path = entries->items[i].string;
+               int len = strlen(path);
+               struct stage_data *e = 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, remove last_file to make room for path and friends.
+                */
+               if (last_file &&
+                   len > last_len &&
+                   memcmp(path, last_file, last_len) == 0 &&
+                   path[last_len] == '/') {
+                       output(o, 3, "Removing %s to make room for subdirectory; may re-add later.", last_file);
+                       unlink(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;
+                       last_e = e;
+               } else {
+                       last_file = NULL;
+               }
+       }
+}
+
 struct rename
 {
        struct diff_filepair *pair;
@@ -378,6 +435,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)
@@ -676,22 +734,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;
                }
        }
@@ -714,10 +776,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 ? LL_OPT_VIRTUAL_ANCESTOR : 0) |
-                                (o->renormalize ? LL_OPT_RENORMALIZE : 0) |
-                                create_ll_flag(favor)));
+                               &src1, name1, &src2, name2, &ll_opts);
 
        free(name1);
        free(name2);
@@ -975,8 +1034,6 @@ static int process_renames(struct merge_options *o,
                                                              branch2,
                                                              ren1->dst_entry,
                                                              ren2->dst_entry);
-                               remove_file(o, 0, ren1_dst, 0);
-                               /* ren2_dst not in head, so no need to delete */
                        } else {
                                remove_file(o, 1, ren1_src, 1);
                                update_stages_and_entry(ren1_dst,
@@ -1020,7 +1077,6 @@ static int process_renames(struct merge_options *o,
                                                                      branch2,
                                                                      ren1->dst_entry,
                                                                      NULL);
-                                       remove_file(o, 0, ren1_dst, 0);
                                } else {
                                        clean_merge = 0;
                                        conflict_rename_delete(o, ren1->pair, branch1, branch2);
@@ -1099,7 +1155,6 @@ static int process_renames(struct merge_options *o,
                                                                      NULL,
                                                                      ren1->dst_entry,
                                                                      NULL);
-                                       remove_file(o, 0, ren1_dst, 0);
                                }
                        }
                }
@@ -1281,7 +1336,7 @@ static int process_entry(struct merge_options *o,
                } else if (string_list_has_string(&o->current_directory_set,
                                                  path)) {
                        entry->processed = 0;
-                       return 1; /* Assume clean till processed */
+                       return 1; /* Assume clean until processed */
                } else {
                        /* Deleted in one and changed in the other */
                        clean_merge = 0;
@@ -1305,15 +1360,7 @@ static int process_entry(struct merge_options *o,
                if (string_list_has_string(&o->current_directory_set, path)) {
                        /* Handle D->F conflicts after all subfiles */
                        entry->processed = 0;
-                       /* But get any file out of the way now, so conflicted
-                        * entries below the directory of the same name can
-                        * be put in the working directory.
-                        */
-                       if (a_sha)
-                               output(o, 2, "Removing %s", path);
-                       /* do not touch working file if it did not exist */
-                       remove_file(o, 0, path, !a_sha);
-                       return 1; /* Assume clean till processed */
+                       return 1; /* Assume clean until processed */
                } else {
                        output(o, 2, "Adding %s", path);
                        update_file(o, 1, sha, mode, path);
@@ -1435,7 +1482,6 @@ static int process_df_entry(struct merge_options *o,
                        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(o, 0, path, 0);
                        update_file(o, 0, sha, mode, new_path);
                } else {
                        output(o, 2, "Adding %s", path);
@@ -1488,6 +1534,7 @@ int merge_trees(struct merge_options *o,
                get_files_dirs(o, merge);
 
                entries = get_unmerged();
+               make_room_for_directories_of_df_conflicts(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);
@@ -1711,3 +1758,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;
+}