builtin-apply: keep information about files to be deleted
[gitweb.git] / merge-recursive.c
index a0c804c8171d021780680f11901680d4c7e89e0b..9bf5cc71754df1df29057822cf16bbfe51579d37 100644 (file)
@@ -237,7 +237,7 @@ static int save_files_dirs(const unsigned char *sha1,
                string_list_insert(newpath, &o->current_file_set);
        free(newpath);
 
-       return READ_TREE_RECURSIVE;
+       return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
 }
 
 static int get_files_dirs(struct merge_options *o, struct tree *tree)
@@ -447,6 +447,30 @@ static void flush_buffer(int fd, const char *buf, unsigned long size)
        }
 }
 
+static int would_lose_untracked(const char *path)
+{
+       int pos = cache_name_pos(path, strlen(path));
+
+       if (pos < 0)
+               pos = -1 - pos;
+       while (pos < active_nr &&
+              !strcmp(path, active_cache[pos]->name)) {
+               /*
+                * If stage #0, it is definitely tracked.
+                * If it has stage #2 then it was tracked
+                * before this merge started.  All other
+                * cases the path was not tracked.
+                */
+               switch (ce_stage(active_cache[pos])) {
+               case 0:
+               case 2:
+                       return 0;
+               }
+               pos++;
+       }
+       return file_exists(path);
+}
+
 static int make_room_for_path(const char *path)
 {
        int status;
@@ -462,6 +486,14 @@ static int make_room_for_path(const char *path)
                die(msg, path, "");
        }
 
+       /*
+        * Do not unlink a file in the work tree if we are not
+        * tracking it.
+        */
+       if (would_lose_untracked(path))
+               return error("refusing to lose untracked file at '%s'",
+                            path);
+
        /* Successful unlink is good.. */
        if (!unlink(path))
                return 0;
@@ -902,6 +934,11 @@ static int process_renames(struct merge_options *o,
                                       ren1_src, ren1_dst, branch1,
                                       branch2);
                                update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst);
+                               update_stages(ren1_dst, NULL,
+                                               branch1 == o->branch1 ?
+                                               ren1->pair->two : NULL,
+                                               branch1 == o->branch1 ?
+                                               NULL : ren1->pair->two, 1);
                        } else if (!sha_eq(dst_other.sha1, null_sha1)) {
                                const char *new_path;
                                clean_merge = 0;
@@ -1084,21 +1121,13 @@ static int process_entry(struct merge_options *o,
                                 o->branch1, o->branch2);
 
                clean_merge = mfi.clean;
-               if (mfi.clean)
-                       update_file(o, 1, mfi.sha, mfi.mode, path);
-               else if (S_ISGITLINK(mfi.mode))
-                       output(o, 1, "CONFLICT (submodule): Merge conflict in %s "
-                              "- needs %s", path, sha1_to_hex(b.sha1));
-               else {
+               if (!mfi.clean) {
+                       if (S_ISGITLINK(mfi.mode))
+                               reason = "submodule";
                        output(o, 1, "CONFLICT (%s): Merge conflict in %s",
                                        reason, path);
-
-                       if (o->call_depth)
-                               update_file(o, 0, mfi.sha, mfi.mode, path);
-                       else
-                               update_file_flags(o, mfi.sha, mfi.mode, path,
-                                             0 /* update_cache */, 1 /* update_working_directory */);
                }
+               update_file(o, mfi.clean, mfi.sha, mfi.mode, path);
        } else if (!o_sha && !a_sha && !b_sha) {
                /*
                 * this entry was deleted altogether. a_mode == 0 means