Merge branch 'jc/blame'
[gitweb.git] / merge-recursive.c
index dbdb9ac2c41d700f7d8521f25a5d45aefd0829c7..ee853b990d8bfb15e0058fefbbd267bd58ed40fc 100644 (file)
@@ -16,9 +16,9 @@
 #include "string-list.h"
 #include "xdiff-interface.h"
 #include "ll-merge.h"
-#include "interpolate.h"
 #include "attr.h"
 #include "merge-recursive.h"
+#include "dir.h"
 
 static struct tree *shift_tree_object(struct tree *one, struct tree *two)
 {
@@ -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)
@@ -392,24 +392,6 @@ static int update_stages(const char *path, struct diff_filespec *o,
        return 0;
 }
 
-static int remove_path(const char *name)
-{
-       int ret;
-       char *slash, *dirs;
-
-       ret = unlink(name);
-       if (ret)
-               return ret;
-       dirs = xstrdup(name);
-       while ((slash = strrchr(name, '/'))) {
-               *slash = '\0';
-               if (rmdir(name) != 0)
-                       break;
-       }
-       free(dirs);
-       return ret;
-}
-
 static int remove_file(struct merge_options *o, int clean,
                       const char *path, int no_wd)
 {
@@ -421,10 +403,8 @@ static int remove_file(struct merge_options *o, int clean,
                        return -1;
        }
        if (update_working_directory) {
-               unlink(path);
-               if (errno != ENOENT || errno != EISDIR)
+               if (remove_path(path) && errno != ENOENT)
                        return -1;
-               remove_path(path);
        }
        return 0;
 }
@@ -467,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;
@@ -482,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;
@@ -517,8 +529,7 @@ static void update_file_flags(struct merge_options *o,
                if (type != OBJ_BLOB)
                        die("blob expected for %s '%s'", sha1_to_hex(sha), path);
                if (S_ISREG(mode)) {
-                       struct strbuf strbuf;
-                       strbuf_init(&strbuf, 0);
+                       struct strbuf strbuf = STRBUF_INIT;
                        if (convert_to_working_tree(path, buf, size, &strbuf)) {
                                free(buf);
                                size = strbuf.len;
@@ -546,7 +557,8 @@ static void update_file_flags(struct merge_options *o,
                        char *lnk = xmemdupz(buf, size);
                        safe_create_leading_directories_const(path);
                        unlink(path);
-                       symlink(lnk, path);
+                       if (symlink(lnk, path))
+                               die("failed to symlink %s: %s", path, strerror(errno));
                        free(lnk);
                } else
                        die("do not know what to do with %06o %s '%s'",
@@ -922,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;
@@ -1352,7 +1369,7 @@ static int merge_recursive_config(const char *var, const char *value, void *cb)
                o->merge_rename_limit = git_config_int(var, value);
                return 0;
        }
-       return git_default_config(var, value, cb);
+       return git_xmerge_config(var, value, cb);
 }
 
 void init_merge_options(struct merge_options *o)