Code

Merge branch 'maint-1.7.6' into maint-1.7.7
[git.git] / merge-recursive.c
index 2bebc9721c3c494c888ef8fac12bed05094d0698..c34a4f148b65cf81f28e2aed6c35e141e175b324 100644 (file)
@@ -297,7 +297,9 @@ static int save_files_dirs(const unsigned char *sha1,
 static int get_files_dirs(struct merge_options *o, struct tree *tree)
 {
        int n;
-       if (read_tree_recursive(tree, "", 0, 0, NULL, save_files_dirs, o))
+       struct pathspec match_all;
+       init_pathspec(&match_all, NULL);
+       if (read_tree_recursive(tree, "", 0, 0, &match_all, save_files_dirs, o))
                return 0;
        n = o->current_file_set.nr + o->current_directory_set.nr;
        return n;
@@ -388,7 +390,7 @@ static void record_df_conflict_files(struct merge_options *o,
                                     struct string_list *entries)
 {
        /* If there is a D/F conflict and the file for such a conflict
-        * currently exist in the working copy, we want to allow it to be
+        * currently exist in the working tree, we want to allow it to be
         * removed to make room for the corresponding directory if needed.
         * The files underneath the directories of such D/F conflicts will
         * be processed before the corresponding file involved in the D/F
@@ -400,6 +402,7 @@ static void record_df_conflict_files(struct merge_options *o,
         * and the file need to be present, then the D/F file will be
         * reinstated with a new unique name at the time it is processed.
         */
+       struct string_list df_sorted_entries;
        const char *last_file = NULL;
        int last_len = 0;
        int i;
@@ -412,14 +415,20 @@ static void record_df_conflict_files(struct merge_options *o,
                return;
 
        /* Ensure D/F conflicts are adjacent in the entries list. */
-       qsort(entries->items, entries->nr, sizeof(*entries->items),
+       memset(&df_sorted_entries, 0, sizeof(struct string_list));
+       for (i = 0; i < entries->nr; i++) {
+               struct string_list_item *next = &entries->items[i];
+               string_list_append(&df_sorted_entries, next->string)->util =
+                                  next->util;
+       }
+       qsort(df_sorted_entries.items, entries->nr, sizeof(*entries->items),
              string_list_df_name_compare);
 
        string_list_clear(&o->df_conflict_file_set, 1);
-       for (i = 0; i < entries->nr; i++) {
-               const char *path = entries->items[i].string;
+       for (i = 0; i < df_sorted_entries.nr; i++) {
+               const char *path = df_sorted_entries.items[i].string;
                int len = strlen(path);
-               struct stage_data *e = entries->items[i].util;
+               struct stage_data *e = df_sorted_entries.items[i].util;
 
                /*
                 * Check if last_file & path correspond to a D/F conflict;
@@ -447,6 +456,7 @@ static void record_df_conflict_files(struct merge_options *o,
                        last_file = NULL;
                }
        }
+       string_list_clear(&df_sorted_entries, 0);
 }
 
 struct rename {
@@ -1036,7 +1046,14 @@ static void handle_change_delete(struct merge_options *o,
                       change_past, o->branch1, o->branch1, path,
                       NULL == renamed ? "" : " at ",
                       NULL == renamed ? "" : renamed);
-               update_file(o, 0, a_sha, a_mode, renamed ? renamed : path);
+               if (renamed)
+                       update_file(o, 0, a_sha, a_mode, renamed);
+               /*
+                * No need to call update_file() on path when !renamed, since
+                * that would needlessly touch path.  We could call
+                * update_file_flags() with update_cache=0 and update_wd=0,
+                * but that's a no-op.
+                */
        }
        free(renamed);
 }
@@ -1152,6 +1169,8 @@ static void conflict_rename_rename_1to2(struct merge_options *o,
               o->call_depth ? " (left unresolved)" : "");
        if (o->call_depth) {
                struct merge_file_info mfi;
+               struct diff_filespec other;
+               struct diff_filespec *add;
                mfi = merge_file(o, one->path,
                                 one->sha1, one->mode,
                                 a->sha1, a->mode,
@@ -1164,8 +1183,25 @@ static void conflict_rename_rename_1to2(struct merge_options *o,
                 * unique path, or use that unique path instead of src here.
                 */
                update_file(o, 0, mfi.sha, mfi.mode, one->path);
-               remove_file_from_cache(a->path);
-               remove_file_from_cache(b->path);
+
+               /*
+                * Above, we put the merged content at the merge-base's
+                * path.  Now we usually need to delete both a->path and
+                * b->path.  However, the rename on each side of the merge
+                * could also be involved in a rename/add conflict.  In
+                * such cases, we should keep the added file around,
+                * resolving the conflict at that path in its favor.
+                */
+               add = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);
+               if (add)
+                       update_file(o, 0, add->sha1, add->mode, a->path);
+               else
+                       remove_file_from_cache(a->path);
+               add = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);
+               if (add)
+                       update_file(o, 0, add->sha1, add->mode, b->path);
+               else
+                       remove_file_from_cache(b->path);
        } else {
                handle_file(o, a, 2, ci);
                handle_file(o, b, 3, ci);
@@ -1396,10 +1432,20 @@ static int process_renames(struct merge_options *o,
                                                           NULL);
                        } 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);
+                               /*
+                                * Added file on the other side identical to
+                                * the file being renamed: clean merge.
+                                * Also, there is no need to overwrite the
+                                * file already in the working copy, so call
+                                * update_file_flags() instead of
+                                * update_file().
+                                */
+                               update_file_flags(o,
+                                                 ren1->pair->two->sha1,
+                                                 ren1->pair->two->mode,
+                                                 ren1_dst,
+                                                 1, /* update_cache */
+                                                 0  /* update_wd    */);
                        } else if (!sha_eq(dst_other.sha1, null_sha1)) {
                                clean_merge = 0;
                                try_merge = 1;
@@ -1581,7 +1627,7 @@ static int merge_content(struct merge_options *o,
                path_renamed_outside_HEAD = !path2 || !strcmp(path, path2);
                if (!path_renamed_outside_HEAD) {
                        add_cacheinfo(mfi.mode, mfi.sha, path,
-                                     0 /*stage*/, 1 /*refresh*/, 0 /*options*/);
+                                     0, (!o->call_depth), 0);
                        return mfi.clean;
                }
        } else
@@ -1727,7 +1773,8 @@ static int process_entry(struct merge_options *o,
                        free(new_path);
                } else {
                        output(o, 2, "Adding %s", path);
-                       update_file(o, 1, sha, mode, path);
+                       /* do not overwrite file if already present */
+                       update_file_flags(o, sha, mode, path, 1, !a_sha);
                }
        } else if (a_sha && b_sha) {
                /* Case C: Added in both (check for same permissions) and */
@@ -1863,12 +1910,10 @@ int merge_recursive(struct merge_options *o,
 
        merged_common_ancestors = pop_commit(&ca);
        if (merged_common_ancestors == NULL) {
-               /* if there is no common ancestor, make an empty tree */
-               struct tree *tree = xcalloc(1, sizeof(struct tree));
+               /* if there is no common ancestor, use an empty tree */
+               struct tree *tree;
 
-               tree->object.parsed = 1;
-               tree->object.type = OBJ_TREE;
-               pretend_sha1_file(NULL, 0, OBJ_TREE, tree->object.sha1);
+               tree = lookup_tree((const unsigned char *)EMPTY_TREE_SHA1_BIN);
                merged_common_ancestors = make_virtual_commit(tree, "ancestor");
        }
 
@@ -2023,6 +2068,8 @@ int parse_merge_opt(struct merge_options *o, const char *s)
                o->subtree_shift = s + strlen("subtree=");
        else if (!strcmp(s, "patience"))
                o->xdl_opts |= XDF_PATIENCE_DIFF;
+       else if (!strcmp(s, "histogram"))
+               o->xdl_opts |= XDF_HISTOGRAM_DIFF;
        else if (!strcmp(s, "ignore-space-change"))
                o->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE;
        else if (!strcmp(s, "ignore-all-space"))