Add git-update-cache --replace option.
[git.git] / read-cache.c
index 327888b..47aa2d4 100644 (file)
@@ -127,10 +127,15 @@ int same_name(struct cache_entry *a, struct cache_entry *b)
  * is being added, or we already have path and path/file is being
  * added.  Either one would result in a nonsense tree that has path
  * twice when git-write-tree tries to write it out.  Prevent it.
+ * 
+ * If ok-to-replace is specified, we remove the conflicting entries
+ * from the cache so the caller should recompute the insert position.
+ * When this happens, we return non-zero.
  */
-static int check_file_directory_conflict(const struct cache_entry *ce)
+static int check_file_directory_conflict(const struct cache_entry *ce,
+                                        int ok_to_replace)
 {
-       int pos;
+       int pos, replaced = 0;
        const char *path = ce->name;
        int namelen = strlen(path);
        int stage = ce_stage(ce);
@@ -157,8 +162,13 @@ static int check_file_directory_conflict(const struct cache_entry *ce)
                         * and we are trying to make it a directory.  This is
                         * bad.
                         */
-                       free(pathbuf);
-                       return -1;
+                       if (!ok_to_replace) {
+                               free(pathbuf);
+                               return -1;
+                       }
+                       fprintf(stderr, "removing file '%s' to replace it with a directory to create '%s'.\n", pathbuf, path);
+                       remove_entry_at(pos);
+                       replaced = 1;
                }
                *ep = '/';  /* then restore it and go downwards */
                cp = ep + 1;
@@ -185,30 +195,40 @@ static int check_file_directory_conflict(const struct cache_entry *ce)
         *
         *        1 path
         * pos->  3 path
-        *        2 path/file
-        *        3 path/file
+        *        2 path/file1
+        *        3 path/file1
+        *        2 path/file2
+        *        2 patho
         *
         * We need to examine pos, ignore it because it is at different
         * stage, examine next to find the path/file at stage 2, and
-        * complain.
+        * complain.  We need to do this until we are not the leading
+        * path of an existing entry anymore.
         */
 
        while (pos < active_nr) {
                struct cache_entry *other = active_cache[pos];
                if (strncmp(other->name, path, namelen))
                        break; /* it is not our "subdirectory" anymore */
-               if ((ce_stage(other) == stage) && other->name[namelen] == '/')
-                       return -1;
+               if ((ce_stage(other) == stage) &&
+                   other->name[namelen] == '/') {
+                       if (!ok_to_replace)
+                               return -1;
+                       fprintf(stderr, "removing file '%s' under '%s' to be replaced with a file\n", other->name, path);
+                       remove_entry_at(pos);
+                       replaced = 1;
+                       continue; /* cycle without updating pos */
+               }
                pos++;
        }
-
-       return 0;
+       return replaced;
 }
 
-int add_cache_entry(struct cache_entry *ce, int ok_to_add)
+int add_cache_entry(struct cache_entry *ce, int option)
 {
        int pos;
-
+       int ok_to_add = option & ADD_CACHE_OK_TO_ADD;
+       int ok_to_replace = option & ADD_CACHE_OK_TO_REPLACE;
        pos = cache_name_pos(ce->name, htons(ce->ce_flags));
 
        /* existing match? Just replace it */
@@ -234,8 +254,12 @@ int add_cache_entry(struct cache_entry *ce, int ok_to_add)
        if (!ok_to_add)
                return -1;
 
-       if (check_file_directory_conflict(ce))
-               return -1;
+       if (check_file_directory_conflict(ce, ok_to_replace)) {
+               if (!ok_to_replace)
+                       return -1;
+               pos = cache_name_pos(ce->name, htons(ce->ce_flags));
+               pos = -pos-1;
+       }
 
        /* Make sure the array is big enough .. */
        if (active_nr == active_alloc) {