merge-recursive: Avoid working directory changes during recursive case
[gitweb.git] / merge-recursive.c
index 42d52cb5bc17e9bb289a6b6c3e90c97d241660a2..e6a6a81ec28b531031aa50ee5374a05c0a890137 100644 (file)
@@ -135,7 +135,6 @@ static void flush_output(struct merge_options *o)
 __attribute__((format (printf, 3, 4)))
 static void output(struct merge_options *o, int v, const char *fmt, ...)
 {
-       int len;
        va_list ap;
 
        if (!show(o, v))
@@ -146,21 +145,9 @@ static void output(struct merge_options *o, int v, const char *fmt, ...)
        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);
+       strbuf_vaddf(&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");
-               }
-       }
-       strbuf_setlen(&o->obuf, o->obuf.len + len);
        strbuf_add(&o->obuf, "\n", 1);
        if (!o->buffer_output)
                flush_output(o);
@@ -243,7 +230,7 @@ struct tree *write_tree_from_memory(struct merge_options *o)
                for (i = 0; i < active_nr; i++) {
                        struct cache_entry *ce = active_cache[i];
                        if (ce_stage(ce))
-                               fprintf(stderr, "BUG: %d %.*s", ce_stage(ce),
+                               fprintf(stderr, "BUG: %d %.*s\n", ce_stage(ce),
                                        (int)ce_namelen(ce), ce->name);
                }
                die("Bug in merge-recursive.c");
@@ -364,9 +351,15 @@ static void make_room_for_directories_of_df_conflicts(struct merge_options *o,
         */
        const char *last_file = NULL;
        int last_len = 0;
-       struct stage_data *last_e;
        int i;
 
+       /*
+        * If we're merging merge-bases, we don't want to bother with
+        * any working directory changes.
+        */
+       if (o->call_depth)
+               return;
+
        for (i = 0; i < entries->nr; i++) {
                const char *path = entries->items[i].string;
                int len = strlen(path);
@@ -394,7 +387,6 @@ static void make_room_for_directories_of_df_conflicts(struct merge_options *o,
                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;
                }
@@ -431,14 +423,16 @@ static struct string_list *get_renames(struct merge_options *o,
        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;
+                           1000;
        opts.rename_score = o->rename_score;
-       opts.warn_on_too_large_rename = 1;
+       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 string_list_item *item;
                struct rename *re;
@@ -472,10 +466,12 @@ static struct string_list *get_renames(struct merge_options *o,
        return renames;
 }
 
-static int update_stages_options(const char *path, struct diff_filespec *o,
-                        struct diff_filespec *a, struct diff_filespec *b,
-                        int clear, int options)
+static int update_stages(const char *path, const struct diff_filespec *o,
+                        const struct diff_filespec *a,
+                        const struct diff_filespec *b)
 {
+       int clear = 1;
+       int options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_SKIP_DFCHECK;
        if (clear)
                if (remove_file_from_cache(path))
                        return -1;
@@ -491,14 +487,6 @@ static int update_stages_options(const char *path, struct diff_filespec *o,
        return 0;
 }
 
-static int update_stages(const char *path, struct diff_filespec *o,
-                        struct diff_filespec *a, struct diff_filespec *b,
-                        int clear)
-{
-       int options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE;
-       return update_stages_options(path, o, a, b, clear, options);
-}
-
 static int update_stages_and_entry(const char *path,
                                   struct stage_data *entry,
                                   struct diff_filespec *o,
@@ -515,8 +503,7 @@ static int update_stages_and_entry(const char *path,
        hashcpy(entry->stages[1].sha, o->sha1);
        hashcpy(entry->stages[2].sha, a->sha1);
        hashcpy(entry->stages[3].sha, b->sha1);
-       options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_SKIP_DFCHECK;
-       return update_stages_options(path, o, a, b, clear, options);
+       return update_stages(path, o, a, b);
 }
 
 static int remove_file(struct merge_options *o, int clean,
@@ -723,9 +710,9 @@ struct merge_file_info {
 
 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 struct diff_filespec *one,
+                     const struct diff_filespec *a,
+                     const struct diff_filespec *b,
                      const char *branch1,
                      const char *branch2)
 {
@@ -783,9 +770,9 @@ static int merge_3way(struct merge_options *o,
 }
 
 static struct merge_file_info merge_file(struct merge_options *o,
-                                        struct diff_filespec *one,
-                                        struct diff_filespec *a,
-                                        struct diff_filespec *b,
+                                        const struct diff_filespec *one,
+                                        const struct diff_filespec *a,
+                                        const struct diff_filespec *b,
                                         const char *branch1,
                                         const char *branch2)
 {
@@ -872,8 +859,7 @@ static void conflict_rename_delete(struct merge_options *o,
        if (!o->call_depth)
                update_stages(dest_name, NULL,
                              rename_branch == o->branch1 ? pair->two : NULL,
-                             rename_branch == o->branch1 ? NULL : pair->two,
-                             1);
+                             rename_branch == o->branch1 ? NULL : pair->two);
        if (lstat(dest_name, &st) == 0 && S_ISDIR(st.st_mode)) {
                dest_name = unique_path(o, dest_name, rename_branch);
                df_conflict = 1;
@@ -917,8 +903,8 @@ static void conflict_rename_rename_1to2(struct merge_options *o,
                 * update_file(o, 0, pair2->two->sha1, pair2->two->mode, dst_name2);
                 */
        } else {
-               update_stages(ren1_dst, NULL, pair1->two, NULL, 1);
-               update_stages(ren2_dst, NULL, NULL, pair2->two, 1);
+               update_stages(ren1_dst, NULL, pair1->two, NULL);
+               update_stages(ren2_dst, NULL, NULL, pair2->two);
 
                update_file(o, 0, pair1->two->sha1, pair1->two->mode, dst_name1);
                update_file(o, 0, pair2->two->sha1, pair2->two->mode, dst_name2);
@@ -967,7 +953,6 @@ static int process_renames(struct merge_options *o,
        }
 
        for (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) {
-               char *src;
                struct string_list *renames1, *renames2Dst;
                struct rename *ren1 = NULL, *ren2 = NULL;
                const char *branch1, *branch2;
@@ -1002,7 +987,6 @@ static int process_renames(struct merge_options *o,
                        ren2 = ren1;
                        ren1 = tmp;
                }
-               src = ren1->pair->one->path;
 
                ren1->dst_entry->processed = 1;
                ren1->src_entry->processed = 1;
@@ -1084,7 +1068,6 @@ static int process_renames(struct merge_options *o,
                                   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;
                                try_merge = 1;
                                output(o, 1, "CONFLICT (rename/add): Rename %s->%s in %s. "
@@ -1113,9 +1096,10 @@ static int process_renames(struct merge_options *o,
                                                    ren1_dst);
                                        try_merge = 0;
                                } else {
-                                       new_path = unique_path(o, ren1_dst, branch2);
+                                       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 if ((item = string_list_lookup(renames2Dst, ren1_dst))) {
                                ren2 = item->util;
@@ -1283,13 +1267,14 @@ static int merge_content(struct merge_options *o,
        }
 
        if (df_conflict_remains) {
-               const char *new_path;
+               char *new_path;
                update_file_flags(o, mfi.sha, mfi.mode, path,
                                  o->call_depth || mfi.clean, 0);
                new_path = unique_path(o, path, df_rename_conflict_branch);
                mfi.clean = 0;
                output(o, 1, "Adding as %s instead", new_path);
                update_file_flags(o, mfi.sha, mfi.mode, new_path, 0, 1);
+               free(new_path);
        } else {
                update_file(o, mfi.clean, mfi.sha, mfi.mode, path);
        }
@@ -1445,14 +1430,16 @@ static int process_df_entry(struct merge_options *o,
                }
        } else if (o_sha && (!a_sha || !b_sha)) {
                /* Modify/delete; deleted side may have put a directory in the way */
-               const char *new_path = path;
-               if (lstat(path, &st) == 0 && S_ISDIR(st.st_mode))
-                       new_path = unique_path(o, path, a_sha ? o->branch1 : o->branch2);
+               char *renamed = NULL;
+               if (lstat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
+                       renamed = unique_path(o, path, a_sha ? o->branch1 : o->branch2);
+               }
                clean_merge = 0;
-               handle_delete_modify(o, path, new_path,
+               handle_delete_modify(o, path, renamed ? renamed : path,
                                     a_sha, a_mode, b_sha, b_mode);
+               free(renamed);
        } else if (!o_sha && !!a_sha != !!b_sha) {
-               /* directory -> (directory, file) */
+               /* directory -> (directory, file) or <nothing> -> (directory, file) */
                const char *add_branch;
                const char *other_branch;
                unsigned mode;
@@ -1473,12 +1460,13 @@ static int process_df_entry(struct merge_options *o,
                        conf = "directory/file";
                }
                if (lstat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
-                       const char *new_path = unique_path(o, path, add_branch);
+                       char *new_path = unique_path(o, path, add_branch);
                        clean_merge = 0;
                        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);
                        update_file(o, 0, sha, mode, new_path);
+                       free(new_path);
                } else {
                        output(o, 2, "Adding %s", path);
                        update_file(o, 1, sha, mode, path);
@@ -1662,6 +1650,9 @@ int merge_recursive(struct merge_options *o,
                commit_list_insert(h2, &(*result)->parents->next);
        }
        flush_output(o);
+       if (show(o, 2))
+               diff_warn_rename_limit("merge.renamelimit",
+                                      o->needed_rename_limit, 0);
        return clean;
 }
 
@@ -1719,15 +1710,15 @@ int merge_recursive_generic(struct merge_options *o,
 static int merge_recursive_config(const char *var, const char *value, void *cb)
 {
        struct merge_options *o = cb;
-       if (!strcasecmp(var, "merge.verbosity")) {
+       if (!strcmp(var, "merge.verbosity")) {
                o->verbosity = git_config_int(var, value);
                return 0;
        }
-       if (!strcasecmp(var, "diff.renamelimit")) {
+       if (!strcmp(var, "diff.renamelimit")) {
                o->diff_rename_limit = git_config_int(var, value);
                return 0;
        }
-       if (!strcasecmp(var, "merge.renamelimit")) {
+       if (!strcmp(var, "merge.renamelimit")) {
                o->merge_rename_limit = git_config_int(var, value);
                return 0;
        }