From 192268c197c0f143989662f648b1d900014e7d89 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 7 May 2005 21:55:21 -0700 Subject: [PATCH] Add git-update-cache --replace option. When "path" exists as a file or a symlink in the index, an attempt to add "path/file" is refused because it results in file vs directory conflict. Similarly when "path/file1", "path/file2", etc. exist, an attempt to add "path" as a file or a symlink is refused. With git-update-cache --replace, these existing entries that conflict with the entry being added are automatically removed from the cache, with warning messages. Signed-off-by: Junio C Hamano --- Documentation/core-git.txt | 10 ++++++- cache.h | 4 ++- read-cache.c | 54 +++++++++++++++++++++++++++----------- tree.c | 2 +- update-cache.c | 18 +++++++++---- 5 files changed, 65 insertions(+), 23 deletions(-) diff --git a/Documentation/core-git.txt b/Documentation/core-git.txt index 014b97964..5e702fd83 100644 --- a/Documentation/core-git.txt +++ b/Documentation/core-git.txt @@ -1098,7 +1098,7 @@ returns the name of the temporary file in the following format: ################################################################ git-update-cache git-update-cache - [--add] [--remove] [--refresh] + [--add] [--remove] [--refresh] [--replace] [--ignore-missing] [--force-remove ] [--cacheinfo ]* @@ -1135,6 +1135,14 @@ using the various options: Remove the file from the index even when the working directory still has such a file. +--replace + By default, when a file "path" exists in the index, + git-update-cache refuses an attempt to add "path/file". + Similarly if a file "path/file" exists, a file "path" + cannot be added. With --replace flag, existing entries + that conflicts with the entry being added are + automatically removed with warning messages. + -- Do not interpret any more arguments as options. diff --git a/cache.h b/cache.h index 314ee0dd0..7a656c70d 100644 --- a/cache.h +++ b/cache.h @@ -116,7 +116,9 @@ unsigned int active_nr, active_alloc, active_cache_changed; extern int read_cache(void); extern int write_cache(int newfd, struct cache_entry **cache, int entries); extern int cache_name_pos(const char *name, int namelen); -extern int add_cache_entry(struct cache_entry *ce, int ok_to_add); +#define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */ +#define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */ +extern int add_cache_entry(struct cache_entry *ce, int option); extern int remove_entry_at(int pos); extern int remove_file_from_cache(char *path); extern int same_name(struct cache_entry *a, struct cache_entry *b); diff --git a/read-cache.c b/read-cache.c index 327888b8a..47aa2d446 100644 --- a/read-cache.c +++ b/read-cache.c @@ -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) { diff --git a/tree.c b/tree.c index d9777bf81..a978c53a3 100644 --- a/tree.c +++ b/tree.c @@ -18,7 +18,7 @@ static int read_one_entry(unsigned char *sha1, const char *base, int baselen, co memcpy(ce->name, base, baselen); memcpy(ce->name + baselen, pathname, len+1); memcpy(ce->sha1, sha1, 20); - return add_cache_entry(ce, 1); + return add_cache_entry(ce, ADD_CACHE_OK_TO_ADD); } static int read_tree_recursive(void *buffer, unsigned long size, diff --git a/update-cache.c b/update-cache.c index 735d19920..1e4e62cc4 100644 --- a/update-cache.c +++ b/update-cache.c @@ -13,7 +13,7 @@ * like "update-cache *" and suddenly having all the object * files be revision controlled. */ -static int allow_add = 0, allow_remove = 0, not_new = 0; +static int allow_add = 0, allow_remove = 0, allow_replace = 0, not_new = 0; /* Three functions to allow overloaded pointer return; see linux/err.h */ static inline void *ERR_PTR(long error) @@ -53,7 +53,7 @@ static void fill_stat_cache_info(struct cache_entry *ce, struct stat *st) static int add_file_to_cache(char *path) { - int size, namelen; + int size, namelen, option; struct cache_entry *ce; struct stat st; int fd; @@ -95,7 +95,9 @@ static int add_file_to_cache(char *path) default: return -1; } - return add_cache_entry(ce, allow_add); + option = allow_add ? ADD_CACHE_OK_TO_ADD : 0; + option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0; + return add_cache_entry(ce, option); } static int match_data(int fd, void *buffer, unsigned long size) @@ -273,7 +275,7 @@ inside: static int add_cacheinfo(char *arg1, char *arg2, char *arg3) { - int size, len; + int size, len, option; unsigned int mode; unsigned char sha1[20]; struct cache_entry *ce; @@ -294,7 +296,9 @@ static int add_cacheinfo(char *arg1, char *arg2, char *arg3) memcpy(ce->name, arg3, len); ce->ce_flags = htons(len); ce->ce_mode = create_ce_mode(mode); - return add_cache_entry(ce, allow_add); + option = allow_add ? ADD_CACHE_OK_TO_ADD : 0; + option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0; + return add_cache_entry(ce, option); } static const char *lockfile_name = NULL; @@ -343,6 +347,10 @@ int main(int argc, char **argv) allow_add = 1; continue; } + if (!strcmp(path, "--replace")) { + allow_replace = 1; + continue; + } if (!strcmp(path, "--remove")) { allow_remove = 1; continue; -- 2.26.2