Add "skip_unmerged" option to unpack_trees.
[gitweb.git] / merge-recursive.c
index 14b56c2f20651711727169b6d1f28df66bfe775d..bdf03b1f1f7acacafebc0541309be097cc80336c 100644 (file)
@@ -85,63 +85,59 @@ 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 rename_limit = -1;
 static int buffer_output = 1;
-static struct output_buffer *output_list, *output_end;
+static struct strbuf obuf = STRBUF_INIT;
 
-static int show (int v)
+static int show(int v)
 {
        return (!call_depth && verbosity >= v) || verbosity >= 5;
 }
 
-static void output(int v, const char *fmt, ...)
+static void flush_output(void)
 {
-       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 (obuf.len) {
+               fputs(obuf.buf, stdout);
+               strbuf_reset(&obuf);
        }
-       va_end(args);
 }
 
-static void flush_output(void)
+static void output(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(v))
+               return;
+
+       strbuf_grow(&obuf, call_depth * 2 + 2);
+       memset(obuf.buf + obuf.len, ' ', call_depth * 2);
+       strbuf_setlen(&obuf, obuf.len + call_depth * 2);
+
+       va_start(ap, fmt);
+       len = vsnprintf(obuf.buf + obuf.len, strbuf_avail(&obuf), fmt, ap);
+       va_end(ap);
+
+       if (len < 0)
+               len = 0;
+       if (len >= strbuf_avail(&obuf)) {
+               strbuf_grow(&obuf, len + 2);
+               va_start(ap, fmt);
+               len = vsnprintf(obuf.buf + obuf.len, strbuf_avail(&obuf), fmt, ap);
+               va_end(ap);
+               if (len >= strbuf_avail(&obuf)) {
+                       die("this should not happen, your snprintf is broken");
+               }
        }
-       output_list = NULL;
-       output_end = NULL;
+       strbuf_setlen(&obuf, obuf.len + len);
+       strbuf_add(&obuf, "\n", 1);
+       if (!buffer_output)
+               flush_output();
 }
 
 static void output_commit_title(struct commit *commit)
@@ -293,7 +289,7 @@ static int get_files_dirs(struct tree *tree)
 }
 
 /*
- * 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,
@@ -337,7 +333,7 @@ static struct path_list *get_unmerged(void)
                        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);
        }
 
@@ -370,8 +366,9 @@ static struct path_list *get_renames(struct tree *tree,
 
        renames = xcalloc(1, sizeof(struct path_list));
        diff_setup(&opts);
-       opts.recursive = 1;
+       DIFF_OPT_SET(&opts, RECURSIVE);
        opts.detect_rename = DIFF_DETECT_RENAME;
+       opts.rename_limit = rename_limit;
        opts.output_format = DIFF_FORMAT_NO_OUTPUT;
        if (diff_setup_done(&opts) < 0)
                die("diff setup failed");
@@ -552,6 +549,10 @@ static void update_file_flags(const unsigned char *sha,
                void *buf;
                unsigned long size;
 
+               if (S_ISGITLINK(mode))
+                       die("cannot read object %s '%s': It is a submodule!",
+                           sha1_to_hex(sha), path);
+
                buf = read_sha1_file(sha, &type, &size);
                if (!buf)
                        die("cannot read object %s '%s'", sha1_to_hex(sha), path);
@@ -1049,14 +1050,16 @@ 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");
                }
        }
 
@@ -1464,10 +1467,13 @@ static int process_entry(const char *path, struct stage_data *entry,
                mfi = merge_file(&o, &a, &b,
                                 branch1, branch2);
 
+               clean_merge = mfi.clean;
                if (mfi.clean)
                        update_file(1, mfi.sha, mfi.mode, path);
+               else if (S_ISGITLINK(mfi.mode))
+                       output(1, "CONFLICT (submodule): Merge conflict in %s "
+                              "- needs %s", path, sha1_to_hex(b.sha1));
                else {
-                       clean_merge = 0;
                        output(1, "CONFLICT (%s): Merge conflict in %s",
                                        reason, path);
 
@@ -1575,7 +1581,7 @@ static int merge(struct commit *h1,
 {
        struct commit_list *iter;
        struct commit *merged_common_ancestors;
-       struct tree *mrtree;
+       struct tree *mrtree = mrtree;
        int clean;
 
        if (show(4)) {
@@ -1682,6 +1688,10 @@ static int merge_config(const char *var, const char *value)
                verbosity = git_config_int(var, value);
                return 0;
        }
+       if (!strcasecmp(var, "diff.renamelimit")) {
+               rename_limit = git_config_int(var, value);
+               return 0;
+       }
        return git_default_config(var, value);
 }
 
@@ -1743,7 +1753,7 @@ int main(int argc, char *argv[])
 
        if (active_cache_changed &&
            (write_cache(index_fd, active_cache, active_nr) ||
-            close(index_fd) || commit_locked_index(lock)))
+            commit_locked_index(lock)))
                        die ("unable to write %s", get_index_file());
 
        return clean ? 0: 1;