]> git.ipfire.org Git - thirdparty/git.git/blobdiff - merge-recursive.c
merge-recursive: fix overwriting dirty files involved in renames
[thirdparty/git.git] / merge-recursive.c
index 9c05eb7f700eed0dc3e20a4c22f3a60e0aa21488..4fb5f797dae15e90181e60acda6b6d302490a759 100644 (file)
@@ -427,14 +427,14 @@ static void get_files_dirs(struct merge_options *o, struct tree *tree)
        read_tree_recursive(tree, "", 0, 0, &match_all, save_files_dirs, o);
 }
 
-static int get_tree_entry_if_blob(struct tree *tree,
+static int get_tree_entry_if_blob(const struct object_id *tree,
                                  const char *path,
                                  struct object_id *hashy,
                                  unsigned int *mode_o)
 {
        int ret;
 
-       ret = get_tree_entry(&tree->object.oid, path, hashy, mode_o);
+       ret = get_tree_entry(tree, path, hashy, mode_o);
        if (S_ISDIR(*mode_o)) {
                oidcpy(hashy, &null_oid);
                *mode_o = 0;
@@ -452,11 +452,11 @@ static struct stage_data *insert_stage_data(const char *path,
 {
        struct string_list_item *item;
        struct stage_data *e = xcalloc(1, sizeof(struct stage_data));
-       get_tree_entry_if_blob(o, path,
+       get_tree_entry_if_blob(&o->object.oid, path,
                               &e->stages[1].oid, &e->stages[1].mode);
-       get_tree_entry_if_blob(a, path,
+       get_tree_entry_if_blob(&a->object.oid, path,
                               &e->stages[2].oid, &e->stages[2].mode);
-       get_tree_entry_if_blob(b, path,
+       get_tree_entry_if_blob(&b->object.oid, path,
                               &e->stages[3].oid, &e->stages[3].mode);
        item = string_list_insert(entries, path);
        item->util = e;
@@ -1324,22 +1324,11 @@ static int handle_file(struct merge_options *o,
 
        add = filespec_from_entry(&other, dst_entry, stage ^ 1);
        if (add) {
-               int ren_src_was_dirty = was_dirty(o, rename->path);
                char *add_name = unique_path(o, rename->path, other_branch);
                if (update_file(o, 0, &add->oid, add->mode, add_name))
                        return -1;
 
-               if (ren_src_was_dirty) {
-                       output(o, 1, _("Refusing to lose dirty file at %s"),
-                              rename->path);
-               }
-               /*
-                * Because the double negatives somehow keep confusing me...
-                *    1) update_wd iff !ren_src_was_dirty.
-                *    2) no_wd iff !update_wd
-                *    3) so, no_wd == !!ren_src_was_dirty == ren_src_was_dirty
-                */
-               remove_file(o, 0, rename->path, ren_src_was_dirty);
+               remove_file(o, 0, rename->path, 0);
                dst_name = unique_path(o, rename->path, cur_branch);
        } else {
                if (dir_in_way(rename->path, !o->call_depth, 0)) {
@@ -1477,10 +1466,7 @@ static int conflict_rename_rename_2to1(struct merge_options *o,
                char *new_path2 = unique_path(o, path, ci->branch2);
                output(o, 1, _("Renaming %s to %s and %s to %s instead"),
                       a->path, new_path1, b->path, new_path2);
-               if (was_dirty(o, path))
-                       output(o, 1, _("Refusing to lose dirty file at %s"),
-                              path);
-               else if (would_lose_untracked(path))
+               if (would_lose_untracked(path))
                        /*
                         * Only way we get here is if both renames were from
                         * a directory rename AND user had an untracked file
@@ -1861,7 +1847,7 @@ static struct hashmap *get_directory_renames(struct diff_queue_struct *pairs,
         * renames, finding out how often each directory rename pair
         * possibility occurs.
         */
-       dir_renames = xmalloc(sizeof(struct hashmap));
+       dir_renames = xmalloc(sizeof(*dir_renames));
        dir_rename_init(dir_renames);
        for (i = 0; i < pairs->nr; ++i) {
                struct string_list_item *item;
@@ -1881,7 +1867,7 @@ static struct hashmap *get_directory_renames(struct diff_queue_struct *pairs,
 
                entry = dir_rename_find_entry(dir_renames, old_dir);
                if (!entry) {
-                       entry = xmalloc(sizeof(struct dir_rename_entry));
+                       entry = xmalloc(sizeof(*entry));
                        dir_rename_entry_init(entry, old_dir);
                        hashmap_put(dir_renames, entry);
                } else {
@@ -1992,7 +1978,7 @@ static void compute_collisions(struct hashmap *collisions,
                char *new_path;
                struct diff_filepair *pair = pairs->queue[i];
 
-               if (pair->status != 'A' && pair->status != 'R')
+               if (pair->status == 'D')
                        continue;
                dir_rename_ent = check_dir_renamed(pair->two->path,
                                                   dir_renames);
@@ -2089,7 +2075,6 @@ static void apply_directory_rename_modifications(struct merge_options *o,
 {
        struct string_list_item *item;
        int stage = (tree == a_tree ? 2 : 3);
-       int update_wd;
 
        /*
         * In all cases where we can do directory rename detection,
@@ -2100,11 +2085,7 @@ static void apply_directory_rename_modifications(struct merge_options *o,
         * saying the file would have been overwritten), but it might
         * be dirty, though.
         */
-       update_wd = !was_dirty(o, pair->two->path);
-       if (!update_wd)
-               output(o, 1, _("Refusing to lose dirty file at %s"),
-                      pair->two->path);
-       remove_file(o, 1, pair->two->path, !update_wd);
+       remove_file(o, 1, pair->two->path, 0 /* no_wd */);
 
        /* Find or create a new re->dst_entry */
        item = string_list_lookup(entries, new_path);
@@ -2219,7 +2200,7 @@ static struct string_list *get_renames(struct merge_options *o,
                struct diff_filepair *pair = pairs->queue[i];
                char *new_path; /* non-NULL only with directory renames */
 
-               if (pair->status != 'A' && pair->status != 'R') {
+               if (pair->status == 'D') {
                        diff_free_filepair(pair);
                        continue;
                }
@@ -2773,6 +2754,7 @@ static int merge_content(struct merge_options *o,
 
        if (mfi.clean && !df_conflict_remains &&
            oid_eq(&mfi.oid, a_oid) && mfi.mode == a_mode) {
+               int path_renamed_outside_HEAD;
                output(o, 3, _("Skipped %s (merged same as existing)"), path);
                /*
                 * The content merge resulted in the same file contents we
@@ -2780,7 +2762,8 @@ static int merge_content(struct merge_options *o,
                 * are recorded at the correct path (which may not be true
                 * if the merge involves a rename).
                 */
-               if (was_tracked(path)) {
+               path_renamed_outside_HEAD = !path2 || !strcmp(path, path2);
+               if (!path_renamed_outside_HEAD) {
                        add_cacheinfo(o, mfi.mode, &mfi.oid, path,
                                      0, (!o->call_depth), 0);
                        return mfi.clean;