diff options
Diffstat (limited to 'read-cache.c')
| -rw-r--r-- | read-cache.c | 660 |
1 files changed, 399 insertions, 261 deletions
diff --git a/read-cache.c b/read-cache.c index 3202402927..a6db25a16d 100644 --- a/read-cache.c +++ b/read-cache.c @@ -3,24 +3,38 @@ * * Copyright (C) Linus Torvalds, 2005 */ -#include "cache.h" +#include "git-compat-util.h" +#include "bulk-checkin.h" #include "config.h" +#include "date.h" #include "diff.h" #include "diffcore.h" +#include "hex.h" #include "tempfile.h" #include "lockfile.h" #include "cache-tree.h" #include "refs.h" #include "dir.h" -#include "object-store.h" +#include "object-file.h" +#include "object-store-ll.h" +#include "oid-array.h" #include "tree.h" #include "commit.h" -#include "blob.h" +#include "environment.h" +#include "gettext.h" +#include "mem-pool.h" +#include "name-hash.h" +#include "object-name.h" +#include "path.h" +#include "preload-index.h" +#include "read-cache.h" #include "resolve-undo.h" -#include "run-command.h" +#include "revision.h" #include "strbuf.h" +#include "trace2.h" #include "varint.h" #include "split-index.h" +#include "symlinks.h" #include "utf8.h" #include "fsmonitor.h" #include "thread-utils.h" @@ -163,61 +177,6 @@ void rename_index_entry_at(struct index_state *istate, int nr, const char *new_n add_index_entry(istate, new_entry, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE); } -void fill_stat_data(struct stat_data *sd, struct stat *st) -{ - sd->sd_ctime.sec = (unsigned int)st->st_ctime; - sd->sd_mtime.sec = (unsigned int)st->st_mtime; - sd->sd_ctime.nsec = ST_CTIME_NSEC(*st); - sd->sd_mtime.nsec = ST_MTIME_NSEC(*st); - sd->sd_dev = st->st_dev; - sd->sd_ino = st->st_ino; - sd->sd_uid = st->st_uid; - sd->sd_gid = st->st_gid; - sd->sd_size = st->st_size; -} - -int match_stat_data(const struct stat_data *sd, struct stat *st) -{ - int changed = 0; - - if (sd->sd_mtime.sec != (unsigned int)st->st_mtime) - changed |= MTIME_CHANGED; - if (trust_ctime && check_stat && - sd->sd_ctime.sec != (unsigned int)st->st_ctime) - changed |= CTIME_CHANGED; - -#ifdef USE_NSEC - if (check_stat && sd->sd_mtime.nsec != ST_MTIME_NSEC(*st)) - changed |= MTIME_CHANGED; - if (trust_ctime && check_stat && - sd->sd_ctime.nsec != ST_CTIME_NSEC(*st)) - changed |= CTIME_CHANGED; -#endif - - if (check_stat) { - if (sd->sd_uid != (unsigned int) st->st_uid || - sd->sd_gid != (unsigned int) st->st_gid) - changed |= OWNER_CHANGED; - if (sd->sd_ino != (unsigned int) st->st_ino) - changed |= INODE_CHANGED; - } - -#ifdef USE_STDEV - /* - * st_dev breaks on network filesystems where different - * clients will have different views of what "device" - * the filesystem is on - */ - if (check_stat && sd->sd_dev != (unsigned int) st->st_dev) - changed |= INODE_CHANGED; -#endif - - if (sd->sd_size != (unsigned int) st->st_size) - changed |= DATA_CHANGED; - - return changed; -} - /* * This only updates the "non-critical" parts of the directory * cache, ie the parts that aren't tracked by GIT, and only used @@ -236,6 +195,33 @@ void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, st } } +static unsigned int st_mode_from_ce(const struct cache_entry *ce) +{ + extern int trust_executable_bit, has_symlinks; + + switch (ce->ce_mode & S_IFMT) { + case S_IFLNK: + return has_symlinks ? S_IFLNK : (S_IFREG | 0644); + case S_IFREG: + return (ce->ce_mode & (trust_executable_bit ? 0755 : 0644)) | S_IFREG; + case S_IFGITLINK: + return S_IFDIR | 0755; + case S_IFDIR: + return ce->ce_mode; + default: + BUG("unsupported ce_mode: %o", ce->ce_mode); + } +} + +int fake_lstat(const struct cache_entry *ce, struct stat *st) +{ + fake_lstat_data(&ce->ce_stat_data, st); + st->st_mode = st_mode_from_ce(ce); + + /* always succeed as lstat() replacement */ + return 0; +} + static int ce_compare_data(struct index_state *istate, const struct cache_entry *ce, struct stat *st) @@ -263,7 +249,7 @@ static int ce_compare_link(const struct cache_entry *ce, size_t expected_size) if (strbuf_readlink(&sb, ce->name, expected_size)) return -1; - buffer = read_object_file(&ce->oid, &type, &size); + buffer = repo_read_object_file(the_repository, &ce->oid, &type, &size); if (buffer) { if (size == sb.len) match = memcmp(buffer, sb.buf, size); @@ -488,86 +474,30 @@ int ie_modified(struct index_state *istate, return 0; } -int base_name_compare(const char *name1, int len1, int mode1, - const char *name2, int len2, int mode2) +static int cache_name_stage_compare(const char *name1, int len1, int stage1, + const char *name2, int len2, int stage2) { - unsigned char c1, c2; - int len = len1 < len2 ? len1 : len2; int cmp; - cmp = memcmp(name1, name2, len); - if (cmp) - return cmp; - c1 = name1[len]; - c2 = name2[len]; - if (!c1 && S_ISDIR(mode1)) - c1 = '/'; - if (!c2 && S_ISDIR(mode2)) - c2 = '/'; - return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0; -} - -/* - * df_name_compare() is identical to base_name_compare(), except it - * compares conflicting directory/file entries as equal. Note that - * while a directory name compares as equal to a regular file, they - * then individually compare _differently_ to a filename that has - * a dot after the basename (because '\0' < '.' < '/'). - * - * This is used by routines that want to traverse the git namespace - * but then handle conflicting entries together when possible. - */ -int df_name_compare(const char *name1, int len1, int mode1, - const char *name2, int len2, int mode2) -{ - int len = len1 < len2 ? len1 : len2, cmp; - unsigned char c1, c2; - - cmp = memcmp(name1, name2, len); + cmp = name_compare(name1, len1, name2, len2); if (cmp) return cmp; - /* Directories and files compare equal (same length, same name) */ - if (len1 == len2) - return 0; - c1 = name1[len]; - if (!c1 && S_ISDIR(mode1)) - c1 = '/'; - c2 = name2[len]; - if (!c2 && S_ISDIR(mode2)) - c2 = '/'; - if (c1 == '/' && !c2) - return 0; - if (c2 == '/' && !c1) - return 0; - return c1 - c2; -} -int name_compare(const char *name1, size_t len1, const char *name2, size_t len2) -{ - size_t min_len = (len1 < len2) ? len1 : len2; - int cmp = memcmp(name1, name2, min_len); - if (cmp) - return cmp; - if (len1 < len2) + if (stage1 < stage2) return -1; - if (len1 > len2) + if (stage1 > stage2) return 1; return 0; } -int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2) +int cmp_cache_name_compare(const void *a_, const void *b_) { - int cmp; + const struct cache_entry *ce1, *ce2; - cmp = name_compare(name1, len1, name2, len2); - if (cmp) - return cmp; - - if (stage1 < stage2) - return -1; - if (stage1 > stage2) - return 1; - return 0; + ce1 = *((const struct cache_entry **)a_); + ce2 = *((const struct cache_entry **)b_); + return cache_name_stage_compare(ce1->name, ce1->ce_namelen, ce_stage(ce1), + ce2->name, ce2->ce_namelen, ce_stage(ce2)); } static int index_name_stage_pos(struct index_state *istate, @@ -1186,19 +1116,32 @@ static int has_dir_name(struct index_state *istate, istate->cache[istate->cache_nr - 1]->name, &len_eq_last); if (cmp_last > 0) { - if (len_eq_last == 0) { + if (name[len_eq_last] != '/') { /* * The entry sorts AFTER the last one in the - * index and their paths have no common prefix, - * so there cannot be a F/D conflict. + * index. + * + * If there were a conflict with "file", then our + * name would start with "file/" and the last index + * entry would start with "file" but not "file/". + * + * The next character after common prefix is + * not '/', so there can be no conflict. */ return retval; } else { /* * The entry sorts AFTER the last one in the - * index, but has a common prefix. Fall through - * to the loop below to disect the entry's path - * and see where the difference is. + * index, and the next character after common + * prefix is '/'. + * + * Either the last index entry is a file in + * conflict with this entry, or it has a name + * which sorts between this entry and the + * potential conflicting file. + * + * In both cases, we fall through to the loop + * below and let the regular search code handle it. */ } } else if (cmp_last == 0) { @@ -1222,53 +1165,6 @@ static int has_dir_name(struct index_state *istate, } len = slash - name; - if (cmp_last > 0) { - /* - * (len + 1) is a directory boundary (including - * the trailing slash). And since the loop is - * decrementing "slash", the first iteration is - * the longest directory prefix; subsequent - * iterations consider parent directories. - */ - - if (len + 1 <= len_eq_last) { - /* - * The directory prefix (including the trailing - * slash) also appears as a prefix in the last - * entry, so the remainder cannot collide (because - * strcmp said the whole path was greater). - * - * EQ: last: xxx/A - * this: xxx/B - * - * LT: last: xxx/file_A - * this: xxx/file_B - */ - return retval; - } - - if (len > len_eq_last) { - /* - * This part of the directory prefix (excluding - * the trailing slash) is longer than the known - * equal portions, so this sub-directory cannot - * collide with a file. - * - * GT: last: xxxA - * this: xxxB/file - */ - return retval; - } - - /* - * This is a possible collision. Fall through and - * let the regular search code handle it. - * - * last: xxx - * this: xxx/file - */ - } - pos = index_name_stage_pos(istate, name, len, stage, EXPAND_SPARSE); if (pos >= 0) { /* @@ -1817,6 +1713,8 @@ static int verify_hdr(const struct cache_header *hdr, unsigned long size) git_hash_ctx c; unsigned char hash[GIT_MAX_RAWSZ]; int hdr_version; + unsigned char *start, *end; + struct object_id oid; if (hdr->hdr_signature != htonl(CACHE_SIGNATURE)) return error(_("bad signature 0x%08x"), hdr->hdr_signature); @@ -1827,10 +1725,16 @@ static int verify_hdr(const struct cache_header *hdr, unsigned long size) if (!verify_index_checksum) return 0; + end = (unsigned char *)hdr + size; + start = end - the_hash_algo->rawsz; + oidread(&oid, start); + if (oideq(&oid, null_oid())) + return 0; + the_hash_algo->init_fn(&c); the_hash_algo->update_fn(&c, hdr, size - the_hash_algo->rawsz); the_hash_algo->final_fn(hash, &c); - if (!hasheq(hash, (unsigned char *)hdr + size - the_hash_algo->rawsz)) + if (!hasheq(hash, start)) return error(_("bad index file sha1 signature")); return 0; } @@ -2292,12 +2196,10 @@ static void set_new_index_sparsity(struct index_state *istate) * If the index's repo exists, mark it sparse according to * repo settings. */ - if (istate->repo) { - prepare_repo_settings(istate->repo); - if (!istate->repo->settings.command_requires_full_index && - is_sparse_index_allowed(istate, 0)) - istate->sparse_index = 1; - } + prepare_repo_settings(istate->repo); + if (!istate->repo->settings.command_requires_full_index && + is_sparse_index_allowed(istate, 0)) + istate->sparse_index = 1; } /* remember to discard_cache() before reading a different cache! */ @@ -2322,9 +2224,8 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist) fd = open(path, O_RDONLY); if (fd < 0) { if (!must_exist && errno == ENOENT) { - if (!istate->repo) - istate->repo = the_repository; set_new_index_sparsity(istate); + istate->initialized = 1; return 0; } die_errno(_("%s: index file open failed"), path); @@ -2425,9 +2326,6 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist) trace2_data_intmax("index", the_repository, "read/cache_nr", istate->cache_nr); - if (!istate->repo) - istate->repo = the_repository; - /* * If the command explicitly requires a full index, force it * to be full. Otherwise, correct the sparsity based on repository @@ -2490,18 +2388,21 @@ int read_index_from(struct index_state *istate, const char *path, trace_performance_enter(); if (split_index->base) - discard_index(split_index->base); + release_index(split_index->base); else - CALLOC_ARRAY(split_index->base, 1); + ALLOC_ARRAY(split_index->base, 1); + index_state_init(split_index->base, istate->repo); base_oid_hex = oid_to_hex(&split_index->base_oid); base_path = xstrfmt("%s/sharedindex.%s", gitdir, base_oid_hex); - trace2_region_enter_printf("index", "shared/do_read_index", - the_repository, "%s", base_path); - ret = do_read_index(split_index->base, base_path, 0); - trace2_region_leave_printf("index", "shared/do_read_index", - the_repository, "%s", base_path); - if (!ret) { + if (file_exists(base_path)) { + trace2_region_enter_printf("index", "shared/do_read_index", + the_repository, "%s", base_path); + + ret = do_read_index(split_index->base, base_path, 0); + trace2_region_leave_printf("index", "shared/do_read_index", + the_repository, "%s", base_path); + } else { char *path_copy = xstrdup(path); char *base_path2 = xstrfmt("%s/sharedindex.%s", dirname(path_copy), base_oid_hex); @@ -2531,7 +2432,13 @@ int is_index_unborn(struct index_state *istate) return (!istate->cache_nr && !istate->timestamp.sec); } -int discard_index(struct index_state *istate) +void index_state_init(struct index_state *istate, struct repository *r) +{ + struct index_state blank = INDEX_STATE_INIT(r); + memcpy(istate, &blank, sizeof(*istate)); +} + +void release_index(struct index_state *istate) { /* * Cache entries in istate->cache[] should have been allocated @@ -2543,27 +2450,28 @@ int discard_index(struct index_state *istate) validate_cache_entries(istate); resolve_undo_clear_index(istate); - istate->cache_nr = 0; - istate->cache_changed = 0; - istate->timestamp.sec = 0; - istate->timestamp.nsec = 0; free_name_hash(istate); cache_tree_free(&(istate->cache_tree)); - istate->initialized = 0; - istate->fsmonitor_has_run_once = 0; - FREE_AND_NULL(istate->fsmonitor_last_update); - FREE_AND_NULL(istate->cache); - istate->cache_alloc = 0; + free(istate->fsmonitor_last_update); + free(istate->cache); discard_split_index(istate); free_untracked_cache(istate->untracked); - istate->untracked = NULL; + + if (istate->sparse_checkout_patterns) { + clear_pattern_list(istate->sparse_checkout_patterns); + FREE_AND_NULL(istate->sparse_checkout_patterns); + } if (istate->ce_mem_pool) { mem_pool_discard(istate->ce_mem_pool, should_validate_cache_entries()); FREE_AND_NULL(istate->ce_mem_pool); } +} - return 0; +void discard_index(struct index_state *istate) +{ + release_index(istate); + index_state_init(istate, istate->repo); } /* @@ -2618,7 +2526,7 @@ int repo_index_has_changes(struct repository *repo, if (tree) cmp = tree->object.oid; - if (tree || !get_oid_tree("HEAD", &cmp)) { + if (tree || !repo_get_oid_tree(repo, "HEAD", &cmp)) { struct diff_options opt; repo_diff_setup(repo, &opt); @@ -2891,6 +2799,16 @@ static int record_ieot(void) return !git_config_get_index_threads(&val) && val != 1; } +enum write_extensions { + WRITE_NO_EXTENSION = 0, + WRITE_SPLIT_INDEX_EXTENSION = 1<<0, + WRITE_CACHE_TREE_EXTENSION = 1<<1, + WRITE_RESOLVE_UNDO_EXTENSION = 1<<2, + WRITE_UNTRACKED_CACHE_EXTENSION = 1<<3, + WRITE_FSMONITOR_EXTENSION = 1<<4, +}; +#define WRITE_ALL_EXTENSIONS ((enum write_extensions)-1) + /* * On success, `tempfile` is closed. If it is the temporary file * of a `struct lock_file`, we will therefore effectively perform @@ -2899,7 +2817,7 @@ static int record_ieot(void) * rely on it. */ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, - int strip_extensions, unsigned flags) + enum write_extensions write_extensions, unsigned flags) { uint64_t start = getnanotime(); struct hashfile *f; @@ -2917,9 +2835,13 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, int ieot_entries = 1; struct index_entry_offset_table *ieot = NULL; int nr, nr_threads; + struct repository *r = istate->repo; f = hashfd(tempfile->fd, tempfile->filename.buf); + prepare_repo_settings(r); + f->skip_hash = r->settings.index_skip_hash; + for (i = removed = extended = 0; i < entries; i++) { if (cache[i]->ce_flags & CE_REMOVE) removed++; @@ -2933,7 +2855,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, } if (!istate->version) - istate->version = get_index_format_default(the_repository); + istate->version = get_index_format_default(r); /* demote version 3 to version 2 when the latter suffices */ if (istate->version == 3 || istate->version == 2) @@ -3068,8 +2990,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, return -1; } - if (!strip_extensions && istate->split_index && - !is_null_oid(&istate->split_index->base_oid)) { + if (write_extensions & WRITE_SPLIT_INDEX_EXTENSION && + istate->split_index) { struct strbuf sb = STRBUF_INIT; if (istate->sparse_index) @@ -3083,7 +3005,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, if (err) return -1; } - if (!strip_extensions && !drop_cache_tree && istate->cache_tree) { + if (write_extensions & WRITE_CACHE_TREE_EXTENSION && + !drop_cache_tree && istate->cache_tree) { struct strbuf sb = STRBUF_INIT; cache_tree_write(&sb, istate->cache_tree); @@ -3093,7 +3016,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, if (err) return -1; } - if (!strip_extensions && istate->resolve_undo) { + if (write_extensions & WRITE_RESOLVE_UNDO_EXTENSION && + istate->resolve_undo) { struct strbuf sb = STRBUF_INIT; resolve_undo_write(&sb, istate->resolve_undo); @@ -3104,7 +3028,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, if (err) return -1; } - if (!strip_extensions && istate->untracked) { + if (write_extensions & WRITE_UNTRACKED_CACHE_EXTENSION && + istate->untracked) { struct strbuf sb = STRBUF_INIT; write_untracked_extension(&sb, istate->untracked); @@ -3115,7 +3040,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, if (err) return -1; } - if (!strip_extensions && istate->fsmonitor_last_update) { + if (write_extensions & WRITE_FSMONITOR_EXTENSION && + istate->fsmonitor_last_update) { struct strbuf sb = STRBUF_INIT; write_fsmonitor_extension(&sb, istate); @@ -3189,8 +3115,10 @@ static int commit_locked_index(struct lock_file *lk) return commit_lock_file(lk); } -static int do_write_locked_index(struct index_state *istate, struct lock_file *lock, - unsigned flags) +static int do_write_locked_index(struct index_state *istate, + struct lock_file *lock, + unsigned flags, + enum write_extensions write_extensions) { int ret; int was_full = istate->sparse_index == INDEX_EXPANDED; @@ -3208,7 +3136,7 @@ static int do_write_locked_index(struct index_state *istate, struct lock_file *l */ trace2_region_enter_printf("index", "do_write_index", the_repository, "%s", get_lock_file_path(lock)); - ret = do_write_index(istate, lock->tempfile, 0, flags); + ret = do_write_index(istate, lock->tempfile, write_extensions, flags); trace2_region_leave_printf("index", "do_write_index", the_repository, "%s", get_lock_file_path(lock)); @@ -3237,7 +3165,7 @@ static int write_split_index(struct index_state *istate, { int ret; prepare_to_write_split_index(istate); - ret = do_write_locked_index(istate, lock, flags); + ret = do_write_locked_index(istate, lock, flags, WRITE_ALL_EXTENSIONS); finish_writing_split_index(istate); return ret; } @@ -3312,7 +3240,7 @@ static int write_shared_index(struct index_state *istate, trace2_region_enter_printf("index", "shared/do_write_index", the_repository, "%s", get_tempfile_path(*temp)); - ret = do_write_index(si->base, *temp, 1, flags); + ret = do_write_index(si->base, *temp, WRITE_NO_EXTENSION, flags); trace2_region_leave_printf("index", "shared/do_write_index", the_repository, "%s", get_tempfile_path(*temp)); @@ -3389,9 +3317,8 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock, if ((!si && !test_split_index_env) || alternate_index_output || (istate->cache_changed & ~EXTMASK)) { - if (si) - oidclr(&si->base_oid); - ret = do_write_locked_index(istate, lock, flags); + ret = do_write_locked_index(istate, lock, flags, + ~WRITE_SPLIT_INDEX_EXTENSION); goto out; } @@ -3417,8 +3344,8 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock, /* Same initial permissions as the main .git/index file */ temp = mks_tempfile_sm(git_path("sharedindex_XXXXXX"), 0, 0666); if (!temp) { - oidclr(&si->base_oid); - ret = do_write_locked_index(istate, lock, flags); + ret = do_write_locked_index(istate, lock, flags, + ~WRITE_SPLIT_INDEX_EXTENSION); goto out; } ret = write_shared_index(istate, &temp, flags); @@ -3539,7 +3466,8 @@ void *read_blob_data_from_index(struct index_state *istate, } if (pos < 0) return NULL; - data = read_object_file(&istate->cache[pos]->oid, &type, &sz); + data = repo_read_object_file(the_repository, &istate->cache[pos]->oid, + &type, &sz); if (!data || type != OBJ_BLOB) { free(data); return NULL; @@ -3549,35 +3477,6 @@ void *read_blob_data_from_index(struct index_state *istate, return data; } -void stat_validity_clear(struct stat_validity *sv) -{ - FREE_AND_NULL(sv->sd); -} - -int stat_validity_check(struct stat_validity *sv, const char *path) -{ - struct stat st; - - if (stat(path, &st) < 0) - return sv->sd == NULL; - if (!sv->sd) - return 0; - return S_ISREG(st.st_mode) && !match_stat_data(sv->sd, &st); -} - -void stat_validity_update(struct stat_validity *sv, int fd) -{ - struct stat st; - - if (fstat(fd, &st) < 0 || !S_ISREG(st.st_mode)) - stat_validity_clear(sv); - else { - if (!sv->sd) - CALLOC_ARRAY(sv->sd, 1); - fill_stat_data(sv->sd, &st); - } -} - void move_index_extensions(struct index_state *dst, struct index_state *src) { dst->untracked = src->untracked; @@ -3821,3 +3720,242 @@ void prefetch_cache_entries(const struct index_state *istate, to_fetch.oid, to_fetch.nr); oid_array_clear(&to_fetch); } + +static int read_one_entry_opt(struct index_state *istate, + const struct object_id *oid, + struct strbuf *base, + const char *pathname, + unsigned mode, int opt) +{ + int len; + struct cache_entry *ce; + + if (S_ISDIR(mode)) + return READ_TREE_RECURSIVE; + + len = strlen(pathname); + ce = make_empty_cache_entry(istate, base->len + len); + + ce->ce_mode = create_ce_mode(mode); + ce->ce_flags = create_ce_flags(1); + ce->ce_namelen = base->len + len; + memcpy(ce->name, base->buf, base->len); + memcpy(ce->name + base->len, pathname, len+1); + oidcpy(&ce->oid, oid); + return add_index_entry(istate, ce, opt); +} + +static int read_one_entry(const struct object_id *oid, struct strbuf *base, + const char *pathname, unsigned mode, + void *context) +{ + struct index_state *istate = context; + return read_one_entry_opt(istate, oid, base, pathname, + mode, + ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK); +} + +/* + * This is used when the caller knows there is no existing entries at + * the stage that will conflict with the entry being added. + */ +static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base, + const char *pathname, unsigned mode, + void *context) +{ + struct index_state *istate = context; + return read_one_entry_opt(istate, oid, base, pathname, + mode, ADD_CACHE_JUST_APPEND); +} + +/* + * Read the tree specified with --with-tree option + * (typically, HEAD) into stage #1 and then + * squash them down to stage #0. This is used for + * --error-unmatch to list and check the path patterns + * that were given from the command line. We are not + * going to write this index out. + */ +void overlay_tree_on_index(struct index_state *istate, + const char *tree_name, const char *prefix) +{ + struct tree *tree; + struct object_id oid; + struct pathspec pathspec; + struct cache_entry *last_stage0 = NULL; + int i; + read_tree_fn_t fn = NULL; + int err; + + if (repo_get_oid(the_repository, tree_name, &oid)) + die("tree-ish %s not found.", tree_name); + tree = parse_tree_indirect(&oid); + if (!tree) + die("bad tree-ish %s", tree_name); + + /* Hoist the unmerged entries up to stage #3 to make room */ + /* TODO: audit for interaction with sparse-index. */ + ensure_full_index(istate); + for (i = 0; i < istate->cache_nr; i++) { + struct cache_entry *ce = istate->cache[i]; + if (!ce_stage(ce)) + continue; + ce->ce_flags |= CE_STAGEMASK; + } + + if (prefix) { + static const char *(matchbuf[1]); + matchbuf[0] = NULL; + parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC, + PATHSPEC_PREFER_CWD, prefix, matchbuf); + } else + memset(&pathspec, 0, sizeof(pathspec)); + + /* + * See if we have cache entry at the stage. If so, + * do it the original slow way, otherwise, append and then + * sort at the end. + */ + for (i = 0; !fn && i < istate->cache_nr; i++) { + const struct cache_entry *ce = istate->cache[i]; + if (ce_stage(ce) == 1) + fn = read_one_entry; + } + + if (!fn) + fn = read_one_entry_quick; + err = read_tree(the_repository, tree, &pathspec, fn, istate); + clear_pathspec(&pathspec); + if (err) + die("unable to read tree entries %s", tree_name); + + /* + * Sort the cache entry -- we need to nuke the cache tree, though. + */ + if (fn == read_one_entry_quick) { + cache_tree_free(&istate->cache_tree); + QSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare); + } + + for (i = 0; i < istate->cache_nr; i++) { + struct cache_entry *ce = istate->cache[i]; + switch (ce_stage(ce)) { + case 0: + last_stage0 = ce; + /* fallthru */ + default: + continue; + case 1: + /* + * If there is stage #0 entry for this, we do not + * need to show it. We use CE_UPDATE bit to mark + * such an entry. + */ + if (last_stage0 && + !strcmp(last_stage0->name, ce->name)) + ce->ce_flags |= CE_UPDATE; + } + } +} + +struct update_callback_data { + struct index_state *index; + int include_sparse; + int flags; + int add_errors; +}; + +static int fix_unmerged_status(struct diff_filepair *p, + struct update_callback_data *data) +{ + if (p->status != DIFF_STATUS_UNMERGED) + return p->status; + if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL) && !p->two->mode) + /* + * This is not an explicit add request, and the + * path is missing from the working tree (deleted) + */ + return DIFF_STATUS_DELETED; + else + /* + * Either an explicit add request, or path exists + * in the working tree. An attempt to explicitly + * add a path that does not exist in the working tree + * will be caught as an error by the caller immediately. + */ + return DIFF_STATUS_MODIFIED; +} + +static void update_callback(struct diff_queue_struct *q, + struct diff_options *opt UNUSED, 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; + + if (!data->include_sparse && + !path_in_sparse_checkout(path, data->index)) + continue; + + switch (fix_unmerged_status(p, data)) { + default: + die(_("unexpected diff status %c"), p->status); + case DIFF_STATUS_MODIFIED: + case DIFF_STATUS_TYPE_CHANGED: + if (add_file_to_index(data->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(data->index, path); + if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE)) + printf(_("remove '%s'\n"), path); + break; + } + } +} + +int add_files_to_cache(struct repository *repo, const char *prefix, + const struct pathspec *pathspec, char *ps_matched, + int include_sparse, int flags) +{ + struct update_callback_data data; + struct rev_info rev; + + memset(&data, 0, sizeof(data)); + data.index = repo->index; + data.include_sparse = include_sparse; + data.flags = flags; + + repo_init_revisions(repo, &rev, prefix); + setup_revisions(0, NULL, &rev, NULL); + if (pathspec) { + copy_pathspec(&rev.prune_data, pathspec); + rev.ps_matched = ps_matched; + } + rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; + rev.diffopt.format_callback = update_callback; + rev.diffopt.format_callback_data = &data; + rev.diffopt.flags.override_submodule_config = 1; + rev.max_count = 0; /* do not compare unmerged paths with stage #2 */ + + /* + * Use an ODB transaction to optimize adding multiple objects. + * This function is invoked from commands other than 'add', which + * may not have their own transaction active. + */ + begin_odb_transaction(); + run_diff_files(&rev, DIFF_RACY_IS_MODIFIED); + end_odb_transaction(); + + release_revisions(&rev); + return !!data.add_errors; +} |
