diff options
Diffstat (limited to 'read-cache.c')
-rw-r--r-- | read-cache.c | 227 |
1 files changed, 99 insertions, 128 deletions
diff --git a/read-cache.c b/read-cache.c index d214abab16..5790a91044 100644 --- a/read-cache.c +++ b/read-cache.c @@ -10,10 +10,10 @@ #include "dir.h" #include "tree.h" #include "commit.h" -#include "diff.h" -#include "diffcore.h" -#include "revision.h" #include "blob.h" +#include "resolve-undo.h" + +static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really); /* Index extensions. * @@ -26,6 +26,7 @@ #define CACHE_EXT(s) ( (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]) ) #define CACHE_EXT_TREE 0x54524545 /* "TREE" */ +#define CACHE_EXT_RESOLVE_UNDO 0x52455543 /* "REUC" */ struct index_state the_index; @@ -91,7 +92,7 @@ static int ce_compare_data(struct cache_entry *ce, struct stat *st) if (fd >= 0) { unsigned char sha1[20]; - if (!index_fd(sha1, fd, st, 0, OBJ_BLOB, ce->name)) + if (!index_fd(sha1, fd, st, OBJ_BLOB, ce->name, 0)) match = hashcmp(sha1, ce->sha1); /* index_fd() closed the file descriptor already */ } @@ -156,7 +157,7 @@ static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st) return 0; } -int is_empty_blob_sha1(const unsigned char *sha1) +static int is_empty_blob_sha1(const unsigned char *sha1) { static const unsigned char empty_blob_sha1[20] = { 0xe6,0x9d,0xe2,0x9b,0xb2,0xd1,0xd6,0x43,0x4b,0x8b, @@ -454,6 +455,7 @@ int remove_index_entry_at(struct index_state *istate, int pos) { struct cache_entry *ce = istate->cache[pos]; + record_resolve_undo(istate, ce); remove_name_hash(ce); istate->cache_changed = 1; istate->cache_nr--; @@ -606,16 +608,40 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st, ce->ce_mode = ce_mode_from_stat(ent, st_mode); } + /* When core.ignorecase=true, determine if a directory of the same name but differing + * case already exists within the Git repository. If it does, ensure the directory + * case of the file being added to the repository matches (is folded into) the existing + * entry's directory case. + */ + if (ignore_case) { + const char *startPtr = ce->name; + const char *ptr = startPtr; + while (*ptr) { + while (*ptr && *ptr != '/') + ++ptr; + if (*ptr == '/') { + struct cache_entry *foundce; + ++ptr; + foundce = index_name_exists(&the_index, ce->name, ptr - ce->name, ignore_case); + if (foundce) { + memcpy((void *)startPtr, foundce->name + (startPtr - ce->name), ptr - startPtr); + startPtr = ptr; + } + } + } + } + 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(alias); + if (!S_ISGITLINK(alias->ce_mode)) + ce_mark_uptodate(alias); alias->ce_flags |= CE_ADDED; return 0; } if (!intent_only) { - if (index_path(ce->sha1, path, st, 1)) + if (index_path(ce->sha1, path, st, HASH_WRITE_OBJECT)) return error("unable to index file %s", path); } else record_intent_to_add(ce); @@ -680,30 +706,9 @@ int ce_same_name(struct cache_entry *a, struct cache_entry *b) return ce_namelen(b) == len && !memcmp(a->name, b->name, len); } -int ce_path_match(const struct cache_entry *ce, const char **pathspec) +int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec) { - const char *match, *name; - int len; - - if (!pathspec) - return 1; - - len = ce_namelen(ce); - name = ce->name; - while ((match = *pathspec++) != NULL) { - int matchlen = strlen(match); - if (matchlen > len) - continue; - if (memcmp(name, match, matchlen)) - continue; - if (matchlen && name[matchlen-1] == '/') - return 1; - if (name[matchlen] == '/' || !name[matchlen]) - return 1; - if (!matchlen) - return 1; - } - return 0; + return match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL); } /* @@ -721,11 +726,12 @@ static int verify_dotfile(const char *rest) * has already been discarded, we now test * the rest. */ - switch (*rest) { + /* "." is not allowed */ - case '\0': case '/': + if (*rest == '\0' || is_dir_sep(*rest)) return 0; + switch (*rest) { /* * ".git" followed by NUL or slash is bad. This * shares the path end test with the ".." case. @@ -738,7 +744,7 @@ static int verify_dotfile(const char *rest) rest += 2; /* fallthrough */ case '.': - if (rest[1] == '\0' || rest[1] == '/') + if (rest[1] == '\0' || is_dir_sep(rest[1])) return 0; } return 1; @@ -748,23 +754,19 @@ int verify_path(const char *path) { char c; + if (has_dos_drive_prefix(path)) + return 0; + goto inside; for (;;) { if (!c) return 1; - if (c == '/') { + if (is_dir_sep(c)) { inside: c = *path++; - switch (c) { - default: - continue; - case '/': case '\0': - break; - case '.': - if (verify_dotfile(path)) - continue; - } - return 0; + if ((c == '.' && !verify_dotfile(path)) || + is_dir_sep(c) || c == '\0') + return 0; } c = *path++; } @@ -1048,7 +1050,8 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate, * because CE_UPTODATE flag is in-core only; * we are not going to write this change out. */ - ce_mark_uptodate(ce); + if (!S_ISGITLINK(ce->ce_mode)) + ce_mark_uptodate(ce); return ce; } } @@ -1077,17 +1080,17 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate, } static void show_file(const char * fmt, const char * name, int in_porcelain, - int * first, char *header_msg) + int * first, const char *header_msg) { if (in_porcelain && *first && header_msg) { printf("%s\n", header_msg); - *first=0; + *first = 0; } printf(fmt, name); } int refresh_index(struct index_state *istate, unsigned int flags, const char **pathspec, - char *seen, char *header_msg) + char *seen, const char *header_msg) { int i; int has_errors = 0; @@ -1152,7 +1155,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p return has_errors; } -struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really) +static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really) { return refresh_cache_ent(&the_index, ce, really, NULL); } @@ -1181,6 +1184,9 @@ static int read_index_extension(struct index_state *istate, case CACHE_EXT_TREE: istate->cache_tree = cache_tree_read(data, sz); break; + case CACHE_EXT_RESOLVE_UNDO: + istate->resolve_undo = resolve_undo_read(data, sz); + break; default: if (*ext < 'A' || 'Z' < *ext) return error("index uses %.4s extension, which we do not understand", @@ -1243,9 +1249,9 @@ static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_en static inline size_t estimate_cache_size(size_t ondisk_size, unsigned int entries) { - long per_entry; - - per_entry = sizeof(struct cache_entry) - sizeof(struct ondisk_cache_entry); + size_t fix_size_mem = offsetof(struct cache_entry, name); + size_t fix_size_dsk = offsetof(struct ondisk_cache_entry, name); + long per_entry = (fix_size_mem - fix_size_dsk + 7) & ~7; /* * Alignment can cause differences. This should be "alignof", but @@ -1360,6 +1366,7 @@ int is_index_unborn(struct index_state *istate) int discard_index(struct index_state *istate) { + resolve_undo_clear_index(istate); istate->cache_nr = 0; istate->cache_changed = 0; istate->timestamp.sec = 0; @@ -1508,6 +1515,7 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce) int size = ondisk_ce_size(ce); struct ondisk_cache_entry *ondisk = xcalloc(1, size); char *name; + int result; ondisk->ctime.sec = htonl(ce->ce_ctime.sec); ondisk->mtime.sec = htonl(ce->ce_mtime.sec); @@ -1531,7 +1539,34 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce) name = ondisk->name; memcpy(name, ce->name, ce_namelen(ce)); - return ce_write(c, fd, ondisk, size); + result = ce_write(c, fd, ondisk, size); + free(ondisk); + return result; +} + +static int has_racy_timestamp(struct index_state *istate) +{ + int entries = istate->cache_nr; + int i; + + for (i = 0; i < entries; i++) { + struct cache_entry *ce = istate->cache[i]; + if (is_racy_timestamp(istate, ce)) + return 1; + } + return 0; +} + +/* + * Opportunisticly update the index but do not complain if we can't + */ +void update_index_if_able(struct index_state *istate, struct lock_file *lockfile) +{ + if ((istate->cache_changed || has_racy_timestamp(istate)) && + !write_index(istate, lockfile->fd)) + commit_locked_index(lockfile); + else + rollback_lock_file(lockfile); } int write_index(struct index_state *istate, int newfd) @@ -1585,6 +1620,17 @@ int write_index(struct index_state *istate, int newfd) if (err) return -1; } + if (istate->resolve_undo) { + struct strbuf sb = STRBUF_INIT; + + resolve_undo_write(&sb, istate->resolve_undo); + err = write_index_ext_header(&c, newfd, CACHE_EXT_RESOLVE_UNDO, + sb.len) < 0 + || ce_write(&c, newfd, sb.buf, sb.len) < 0; + strbuf_release(&sb); + if (err) + return -1; + } if (ce_flush(&c, newfd) || fstat(newfd, &st)) return -1; @@ -1628,81 +1674,6 @@ int read_index_unmerged(struct index_state *istate) return unmerged; } -struct update_callback_data -{ - int flags; - int add_errors; -}; - -static void update_callback(struct diff_queue_struct *q, - struct diff_options *opt, void *cbdata) -{ - int i; - struct update_callback_data *data = cbdata; - - for (i = 0; i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - const char *path = p->one->path; - switch (p->status) { - default: - die("unexpected diff status %c", p->status); - case DIFF_STATUS_UNMERGED: - /* - * ADD_CACHE_IGNORE_REMOVAL is unset if "git - * add -u" is calling us, In such a case, a - * missing work tree file needs to be removed - * if there is an unmerged entry at stage #2, - * but such a diff record is followed by - * another with DIFF_STATUS_DELETED (and if - * there is no stage #2, we won't see DELETED - * nor MODIFIED). We can simply continue - * either way. - */ - if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL)) - continue; - /* - * Otherwise, it is "git add path" is asking - * to explicitly add it; we fall through. A - * missing work tree file is an error and is - * caught by add_file_to_index() in such a - * case. - */ - case DIFF_STATUS_MODIFIED: - case DIFF_STATUS_TYPE_CHANGED: - if (add_file_to_index(&the_index, path, data->flags)) { - if (!(data->flags & ADD_CACHE_IGNORE_ERRORS)) - die("updating files failed"); - data->add_errors++; - } - break; - case DIFF_STATUS_DELETED: - if (data->flags & ADD_CACHE_IGNORE_REMOVAL) - break; - if (!(data->flags & ADD_CACHE_PRETEND)) - remove_file_from_index(&the_index, path); - if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE)) - printf("remove '%s'\n", path); - break; - } - } -} - -int add_files_to_cache(const char *prefix, const char **pathspec, int flags) -{ - struct update_callback_data data; - struct rev_info rev; - init_revisions(&rev, prefix); - setup_revisions(0, NULL, &rev, NULL); - rev.prune_data = pathspec; - rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; - rev.diffopt.format_callback = update_callback; - data.flags = flags; - data.add_errors = 0; - rev.diffopt.format_callback_data = &data; - run_diff_files(&rev, DIFF_RACY_IS_MODIFIED); - return !!data.add_errors; -} - /* * Returns 1 if the path is an "other" path with respect to * the index; that is, the path is not mentioned in the index at all, |