From: Junio C Hamano Date: Sun, 11 May 2008 01:14:28 +0000 (-0700) Subject: Merge branch 'lt/case-insensitive' X-Git-Tag: v1.5.6-rc0~68^2~3 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/380a7426794dcad369dd48519cd01d6e0246cde5?ds=inline;hp=-c Merge branch 'lt/case-insensitive' * lt/case-insensitive: Make git-add behave more sensibly in a case-insensitive environment When adding files to the index, add support for case-independent matches Make unpack-tree update removed files before any updated files Make branch merging aware of underlying case-insensitive filsystems Add 'core.ignorecase' option Make hash_name_lookup able to do case-independent lookups Make "index_name_exists()" return the cache_entry it found Move name hashing functions into a file of its own Make unpack_trees_options bit flags actual bitfields --- 380a7426794dcad369dd48519cd01d6e0246cde5 diff --combined Makefile index 9d84c8d799,390b37b941..649ee56c96 --- a/Makefile +++ b/Makefile @@@ -366,7 -366,6 +366,7 @@@ LIB_H += refs. LIB_H += remote.h LIB_H += revision.h LIB_H += run-command.h +LIB_H += sha1-lookup.h LIB_H += sideband.h LIB_H += strbuf.h LIB_H += tag.h @@@ -423,6 -422,7 +423,7 @@@ LIB_OBJS += log-tree. LIB_OBJS += mailmap.o LIB_OBJS += match-trees.o LIB_OBJS += merge-file.o + LIB_OBJS += name-hash.o LIB_OBJS += object.o LIB_OBJS += pack-check.o LIB_OBJS += pack-revindex.o @@@ -447,7 -447,6 +448,7 @@@ LIB_OBJS += run-command. LIB_OBJS += server-info.o LIB_OBJS += setup.o LIB_OBJS += sha1_file.o +LIB_OBJS += sha1-lookup.o LIB_OBJS += sha1_name.o LIB_OBJS += shallow.o LIB_OBJS += sideband.o diff --combined cache.h index 7fb8f3359d,81727e4afe..80a8842db4 --- a/cache.h +++ b/cache.h @@@ -133,6 -133,7 +133,7 @@@ struct cache_entry #define CE_UPDATE (0x10000) #define CE_REMOVE (0x20000) #define CE_UPTODATE (0x40000) + #define CE_ADDED (0x80000) #define CE_HASHED (0x100000) #define CE_UNHASHED (0x200000) @@@ -153,20 -154,6 +154,6 @@@ static inline void copy_cache_entry(str dst->ce_flags = (dst->ce_flags & ~CE_STATE_MASK) | state; } - /* - * We don't actually *remove* it, we can just mark it invalid so that - * we won't find it in lookups. - * - * Not only would we have to search the lists (simple enough), but - * we'd also have to rehash other hash buckets in case this makes the - * hash bucket empty (common). So it's much better to just mark - * it. - */ - static inline void remove_index_entry(struct cache_entry *ce) - { - ce->ce_flags |= CE_UNHASHED; - } - static inline unsigned create_ce_flags(size_t len, unsigned stage) { if (len >= CE_NAMEMASK) @@@ -241,6 -228,23 +228,23 @@@ struct index_state extern struct index_state the_index; + /* Name hashing */ + extern void add_name_hash(struct index_state *istate, struct cache_entry *ce); + /* + * We don't actually *remove* it, we can just mark it invalid so that + * we won't find it in lookups. + * + * Not only would we have to search the lists (simple enough), but + * we'd also have to rehash other hash buckets in case this makes the + * hash bucket empty (common). So it's much better to just mark + * it. + */ + static inline void remove_name_hash(struct cache_entry *ce) + { + ce->ce_flags |= CE_UNHASHED; + } + + #ifndef NO_THE_INDEX_COMPATIBILITY_MACROS #define active_cache (the_index.cache) #define active_nr (the_index.cache_nr) @@@ -261,7 -265,7 +265,7 @@@ #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL) #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options)) #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options)) - #define cache_name_exists(name, namelen) index_name_exists(&the_index, (name), (namelen)) + #define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase)) #endif enum object_type { @@@ -311,7 -315,6 +315,7 @@@ extern char *get_index_file(void) extern char *get_graft_file(void); extern int set_git_dir(const char *path); extern const char *get_git_work_tree(void); +extern const char *read_gitfile_gently(const char *path); #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES" @@@ -351,7 -354,7 +355,7 @@@ extern int write_index(const struct ind extern int discard_index(struct index_state *); extern int unmerged_index(const struct index_state *); extern int verify_path(const char *path); - extern int index_name_exists(struct index_state *istate, const char *name, int namelen); + extern struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int igncase); extern int index_name_pos(const struct index_state *, const char *name, int namelen); #define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */ #define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */ @@@ -405,6 -408,7 +409,7 @@@ extern int delete_ref(const char *, con extern int trust_executable_bit; extern int quote_path_fully; extern int has_symlinks; + extern int ignore_case; extern int assume_unchanged; extern int prefer_symlink_refs; extern int log_all_ref_updates; @@@ -475,20 -479,10 +480,20 @@@ static inline void hashclr(unsigned cha int git_mkstemp(char *path, size_t n, const char *template); +/* + * NOTE NOTE NOTE!! + * + * PERM_UMASK, OLD_PERM_GROUP and OLD_PERM_EVERYBODY enumerations must + * not be changed. Old repositories have core.sharedrepository written in + * numeric format, and therefore these values are preserved for compatibility + * reasons. + */ enum sharedrepo { - PERM_UMASK = 0, - PERM_GROUP, - PERM_EVERYBODY + PERM_UMASK = 0, + OLD_PERM_GROUP = 1, + OLD_PERM_EVERYBODY = 2, + PERM_GROUP = 0660, + PERM_EVERYBODY = 0664, }; int git_config_perm(const char *var, const char *value); int adjust_shared_perm(const char *path); @@@ -635,7 -629,6 +640,7 @@@ struct ref struct ref *next; unsigned char old_sha1[20]; unsigned char new_sha1[20]; + char *symref; unsigned int force:1, merge:1, nonfastforward:1, @@@ -704,7 -697,6 +709,7 @@@ extern int git_parse_long(const char * extern int git_parse_ulong(const char *, unsigned long *); extern int git_config_int(const char *, const char *); extern unsigned long git_config_ulong(const char *, const char *); +extern int git_config_bool_or_int(const char *, const char *, int *); extern int git_config_bool(const char *, const char *); extern int git_config_string(const char **, const char *, const char *); extern int git_config_set(const char *, const char *); @@@ -728,8 -720,8 +733,8 @@@ extern const char *git_log_output_encod extern void maybe_flush_or_die(FILE *, const char *); extern int copy_fd(int ifd, int ofd); extern int copy_file(const char *dst, const char *src, int mode); -extern int read_in_full(int fd, void *buf, size_t count); -extern int write_in_full(int fd, const void *buf, size_t count); +extern ssize_t read_in_full(int fd, void *buf, size_t count); +extern ssize_t write_in_full(int fd, const void *buf, size_t count); extern void write_or_die(int fd, const void *buf, size_t count); extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg); extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg); diff --combined config.c index b0ada515b9,3d51868f2b..8fcb5db94f --- a/config.c +++ b/config.c @@@ -303,9 -303,8 +303,9 @@@ unsigned long git_config_ulong(const ch return ret; } -int git_config_bool(const char *name, const char *value) +int git_config_bool_or_int(const char *name, const char *value, int *is_bool) { + *is_bool = 1; if (!value) return 1; if (!*value) @@@ -314,14 -313,7 +314,14 @@@ return 1; if (!strcasecmp(value, "false") || !strcasecmp(value, "no")) return 0; - return git_config_int(name, value) != 0; + *is_bool = 0; + return git_config_int(name, value); +} + +int git_config_bool(const char *name, const char *value) +{ + int discard; + return !!git_config_bool_or_int(name, value, &discard); } int git_config_string(const char **dest, const char *var, const char *value) @@@ -350,6 -342,11 +350,11 @@@ int git_default_config(const char *var return 0; } + if (!strcmp(var, "core.ignorecase")) { + ignore_case = git_config_bool(var, value); + return 0; + } + if (!strcmp(var, "core.bare")) { is_bare_repository_cfg = git_config_bool(var, value); return 0; diff --combined dir.c index 9501476ecd,b5bfbcaac7..29d1d5ba31 --- a/dir.c +++ b/dir.c @@@ -52,11 -52,6 +52,11 @@@ int common_prefix(const char **pathspec return prefix; } +static inline int special_char(unsigned char c1) +{ + return !c1 || c1 == '*' || c1 == '[' || c1 == '?'; +} + /* * Does 'match' matches the given name? * A match is found if @@@ -74,31 -69,18 +74,31 @@@ static int match_one(const char *match int matchlen; /* If the match was just the prefix, we matched */ - matchlen = strlen(match); - if (!matchlen) + if (!*match) return MATCHED_RECURSIVELY; + for (;;) { + unsigned char c1 = *match; + unsigned char c2 = *name; + if (special_char(c1)) + break; + if (c1 != c2) + return 0; + match++; + name++; + namelen--; + } + + /* * If we don't match the matchstring exactly, * we need to match by fnmatch */ + matchlen = strlen(match); if (strncmp(match, name, matchlen)) return !fnmatch(match, name, 0) ? MATCHED_FNMATCH : 0; - if (!name[matchlen]) + if (namelen == matchlen) return MATCHED_EXACTLY; if (match[matchlen-1] == '/' || name[matchlen] == '/') return MATCHED_RECURSIVELY; @@@ -389,7 -371,7 +389,7 @@@ static struct dir_entry *dir_entry_new( struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len) { - if (cache_name_exists(pathname, len)) + if (cache_name_exists(pathname, len, ignore_case)) return NULL; ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc); diff --combined environment.c index fcd1ee5ef8,3c81682429..945574169b --- a/environment.c +++ b/environment.c @@@ -14,6 -14,7 +14,7 @@@ char git_default_name[MAX_GITNAME] int trust_executable_bit = 1; int quote_path_fully = 1; int has_symlinks = 1; + int ignore_case; int assume_unchanged; int prefer_symlink_refs; int is_bare_repository_cfg = -1; /* unspecified */ @@@ -49,8 -50,6 +50,8 @@@ static char *git_object_dir, *git_index static void setup_git_env(void) { git_dir = getenv(GIT_DIR_ENVIRONMENT); + if (!git_dir) + git_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT); if (!git_dir) git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; git_object_dir = getenv(DB_ENVIRONMENT); diff --combined read-cache.c index c3692f41ad,6b7d16c554..3b20a142ea --- a/read-cache.c +++ b/read-cache.c @@@ -23,80 -23,21 +23,21 @@@ struct index_state the_index; - static unsigned int hash_name(const char *name, int namelen) - { - unsigned int hash = 0x123; - - do { - unsigned char c = *name++; - hash = hash*101 + c; - } while (--namelen); - return hash; - } - - static void hash_index_entry(struct index_state *istate, struct cache_entry *ce) - { - void **pos; - unsigned int hash; - - if (ce->ce_flags & CE_HASHED) - return; - ce->ce_flags |= CE_HASHED; - ce->next = NULL; - hash = hash_name(ce->name, ce_namelen(ce)); - pos = insert_hash(hash, ce, &istate->name_hash); - if (pos) { - ce->next = *pos; - *pos = ce; - } - } - - static void lazy_init_name_hash(struct index_state *istate) - { - int nr; - - if (istate->name_hash_initialized) - return; - for (nr = 0; nr < istate->cache_nr; nr++) - hash_index_entry(istate, istate->cache[nr]); - istate->name_hash_initialized = 1; - } - static void set_index_entry(struct index_state *istate, int nr, struct cache_entry *ce) { - ce->ce_flags &= ~CE_UNHASHED; istate->cache[nr] = ce; - if (istate->name_hash_initialized) - hash_index_entry(istate, ce); + add_name_hash(istate, ce); } static void replace_index_entry(struct index_state *istate, int nr, struct cache_entry *ce) { struct cache_entry *old = istate->cache[nr]; - remove_index_entry(old); + remove_name_hash(old); set_index_entry(istate, nr, ce); istate->cache_changed = 1; } - int index_name_exists(struct index_state *istate, const char *name, int namelen) - { - unsigned int hash = hash_name(name, namelen); - struct cache_entry *ce; - - lazy_init_name_hash(istate); - ce = lookup_hash(hash, &istate->name_hash); - - while (ce) { - if (!(ce->ce_flags & CE_UNHASHED)) { - if (!cache_name_compare(name, namelen, ce->name, ce->ce_flags)) - return 1; - } - ce = ce->next; - } - return 0; - } - /* * This only updates the "non-critical" parts of the directory * cache, ie the parts that aren't tracked by GIT, and only used @@@ -438,7 -379,7 +379,7 @@@ int remove_index_entry_at(struct index_ { struct cache_entry *ce = istate->cache[pos]; - remove_index_entry(ce); + remove_name_hash(ce); istate->cache_changed = 1; istate->cache_nr--; if (pos >= istate->cache_nr) @@@ -488,11 -429,43 +429,43 @@@ static int index_name_pos_also_unmerged return pos; } + static int different_name(struct cache_entry *ce, struct cache_entry *alias) + { + int len = ce_namelen(ce); + return ce_namelen(alias) != len || memcmp(ce->name, alias->name, len); + } + + /* + * If we add a filename that aliases in the cache, we will use the + * name that we already have - but we don't want to update the same + * alias twice, because that implies that there were actually two + * different files with aliasing names! + * + * So we use the CE_ADDED flag to verify that the alias was an old + * one before we accept it as + */ + static struct cache_entry *create_alias_ce(struct cache_entry *ce, struct cache_entry *alias) + { + int len; + struct cache_entry *new; + + if (alias->ce_flags & CE_ADDED) + die("Will not add file alias '%s' ('%s' already exists in index)", ce->name, alias->name); + + /* Ok, create the new entry using the name of the existing alias */ + len = ce_namelen(alias); + new = xcalloc(1, cache_entry_size(len)); + memcpy(new->name, alias->name, len); + copy_cache_entry(new, ce); + free(ce); + return new; + } + int add_file_to_index(struct index_state *istate, const char *path, int verbose) { - int size, namelen, pos; + int size, namelen; struct stat st; - struct cache_entry *ce; + struct cache_entry *ce, *alias; unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY; if (lstat(path, &st)) @@@ -525,18 -498,19 +498,19 @@@ ce->ce_mode = ce_mode_from_stat(ent, st.st_mode); } - pos = index_name_pos(istate, ce->name, namelen); - if (0 <= pos && - !ce_stage(istate->cache[pos]) && - !ie_match_stat(istate, istate->cache[pos], &st, ce_option)) { + alias = index_name_exists(istate, ce->name, ce_namelen(ce), ignore_case); + if (alias && !ce_stage(alias) && !ie_match_stat(istate, alias, &st, ce_option)) { /* Nothing changed, really */ free(ce); - ce_mark_uptodate(istate->cache[pos]); + ce_mark_uptodate(alias); + alias->ce_flags |= CE_ADDED; return 0; } - if (index_path(ce->sha1, path, &st, 1)) die("unable to index file %s", path); + if (ignore_case && alias && different_name(ce, alias)) + ce = create_alias_ce(ce, alias); + ce->ce_flags |= CE_ADDED; if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE)) die("unable to add %s to index",path); if (verbose) @@@ -1370,7 -1344,7 +1344,7 @@@ int write_index(const struct index_stat struct cache_entry *ce = cache[i]; if (ce->ce_flags & CE_REMOVE) continue; - if (is_racy_timestamp(istate, ce)) + if (!ce_uptodate(ce) && is_racy_timestamp(istate, ce)) ce_smudge_racily_clean_entry(ce); if (ce_write_entry(&c, newfd, ce) < 0) return -1;