* Copyright (C) Linus Torvalds, 2005
*/
#include "cache.h"
+#include "alloc.h"
#include "config.h"
#include "diff.h"
#include "diffcore.h"
+#include "hex.h"
#include "tempfile.h"
#include "lockfile.h"
#include "cache-tree.h"
#include "tree.h"
#include "commit.h"
#include "blob.h"
+#include "environment.h"
+#include "gettext.h"
#include "resolve-undo.h"
#include "run-command.h"
#include "strbuf.h"
+#include "trace2.h"
#include "varint.h"
#include "split-index.h"
#include "utf8.h"
#include "sparse-index.h"
#include "csum-file.h"
#include "promisor-remote.h"
+#include "hook.h"
+#include "wrapper.h"
/* Mask for the name length in ce_flags in the on-disk index */
static void set_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)
{
if (S_ISSPARSEDIR(ce->ce_mode))
- istate->sparse_index = 1;
+ istate->sparse_index = INDEX_COLLAPSED;
istate->cache[nr] = ce;
add_name_hash(istate, ce);
void rename_index_entry_at(struct index_state *istate, int nr, const char *new_name)
{
- struct cache_entry *old_entry = istate->cache[nr], *new_entry;
+ struct cache_entry *old_entry = istate->cache[nr], *new_entry, *refreshed;
int namelen = strlen(new_name);
new_entry = make_empty_cache_entry(istate, namelen);
cache_tree_invalidate_path(istate, old_entry->name);
untracked_cache_remove_from_index(istate, old_entry->name);
remove_index_entry_at(istate, nr);
- add_index_entry(istate, new_entry, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
+
+ /*
+ * Refresh the new index entry. Using 'refresh_cache_entry' ensures
+ * we only update stat info if the entry is otherwise up-to-date (i.e.,
+ * the contents/mode haven't changed). This ensures that we reflect the
+ * 'ctime' of the rename in the index without (incorrectly) updating
+ * the cached stat info to reflect unstaged changes on disk.
+ */
+ refreshed = refresh_cache_entry(istate, new_entry, CE_MATCH_REFRESH);
+ if (refreshed && refreshed != new_entry) {
+ add_index_entry(istate, refreshed, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
+ discard_cache_entry(new_entry);
+ } else
+ 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)
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);
return 0;
}
-int base_name_compare(const char *name1, int len1, int mode1,
- const char *name2, int len2, int mode2)
+int base_name_compare(const char *name1, size_t len1, int mode1,
+ const char *name2, size_t len2, int mode2)
{
unsigned char c1, c2;
- int len = len1 < len2 ? len1 : len2;
+ size_t len = len1 < len2 ? len1 : len2;
int cmp;
cmp = memcmp(name1, name2, len);
* 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 df_name_compare(const char *name1, size_t len1, int mode1,
+ const char *name2, size_t len2, int mode2)
{
- int len = len1 < len2 ? len1 : len2, cmp;
unsigned char c1, c2;
+ size_t len = len1 < len2 ? len1 : len2;
+ int cmp;
cmp = memcmp(name1, name2, len);
if (cmp)
return index_name_stage_pos(istate, name, namelen, 0, EXPAND_SPARSE);
}
+int index_name_pos_sparse(struct index_state *istate, const char *name, int namelen)
+{
+ return index_name_stage_pos(istate, name, namelen, 0, NO_EXPAND_SPARSE);
+}
+
int index_entry_exists(struct index_state *istate, const char *name, int namelen)
{
return index_name_stage_pos(istate, name, namelen, 0, NO_EXPAND_SPARSE) >= 0;
void set_object_name_for_intent_to_add_entry(struct cache_entry *ce)
{
struct object_id oid;
- if (write_object_file("", 0, blob_type, &oid))
+ if (write_object_file("", 0, OBJ_BLOB, &oid))
die(_("cannot create an empty blob in the object database"));
oidcpy(&ce->oid, &oid);
}
int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK;
int new_only = option & ADD_CACHE_NEW_ONLY;
- if (!(option & ADD_CACHE_KEEP_CACHE_TREE))
- cache_tree_invalidate_path(istate, ce->name);
-
/*
* If this entry's path sorts after the last entry in the index,
* we can avoid searching for it.
else
pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce), EXPAND_SPARSE);
+ /*
+ * Cache tree path should be invalidated only after index_name_stage_pos,
+ * in case it expands a sparse index.
+ */
+ if (!(option & ADD_CACHE_KEEP_CACHE_TREE))
+ cache_tree_invalidate_path(istate, ce->name);
+
/* existing match? Just replace it. */
if (pos >= 0) {
if (!new_only)
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);
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;
}
break;
case CACHE_EXT_SPARSE_DIRECTORIES:
/* no content, only an indicator */
- istate->sparse_index = 1;
+ istate->sparse_index = INDEX_COLLAPSED;
break;
default:
if (*ext < 'A' || 'Z' < *ext)
return 0;
}
+/*
+ * Parses the contents of the cache entry contained within the 'ondisk' buffer
+ * into a new incore 'cache_entry'.
+ *
+ * Note that 'char *ondisk' may not be aligned to a 4-byte address interval in
+ * index v4, so we cannot cast it to 'struct ondisk_cache_entry *' and access
+ * its members. Instead, we use the byte offsets of members within the struct to
+ * identify where 'get_be16()', 'get_be32()', and 'oidread()' (which can all
+ * read from an unaligned memory buffer) should read from the 'ondisk' buffer
+ * into the corresponding incore 'cache_entry' members.
+ */
static struct cache_entry *create_from_disk(struct mem_pool *ce_mem_pool,
unsigned int version,
- struct ondisk_cache_entry *ondisk,
+ const char *ondisk,
unsigned long *ent_size,
const struct cache_entry *previous_ce)
{
size_t len;
const char *name;
const unsigned hashsz = the_hash_algo->rawsz;
- const uint16_t *flagsp = (const uint16_t *)(ondisk->data + hashsz);
+ const char *flagsp = ondisk + offsetof(struct ondisk_cache_entry, data) + hashsz;
unsigned int flags;
size_t copy_len = 0;
/*
if (flags & CE_EXTENDED) {
int extended_flags;
- extended_flags = get_be16(flagsp + 1) << 16;
+ extended_flags = get_be16(flagsp + sizeof(uint16_t)) << 16;
/* We do not yet understand any bit out of CE_EXTENDED_FLAGS */
if (extended_flags & ~CE_EXTENDED_FLAGS)
die(_("unknown index entry format 0x%08x"), extended_flags);
flags |= extended_flags;
- name = (const char *)(flagsp + 2);
+ name = (const char *)(flagsp + 2 * sizeof(uint16_t));
}
else
- name = (const char *)(flagsp + 1);
+ name = (const char *)(flagsp + sizeof(uint16_t));
if (expand_name_field) {
const unsigned char *cp = (const unsigned char *)name;
ce = mem_pool__ce_alloc(ce_mem_pool, len);
- ce->ce_stat_data.sd_ctime.sec = get_be32(&ondisk->ctime.sec);
- ce->ce_stat_data.sd_mtime.sec = get_be32(&ondisk->mtime.sec);
- ce->ce_stat_data.sd_ctime.nsec = get_be32(&ondisk->ctime.nsec);
- ce->ce_stat_data.sd_mtime.nsec = get_be32(&ondisk->mtime.nsec);
- ce->ce_stat_data.sd_dev = get_be32(&ondisk->dev);
- ce->ce_stat_data.sd_ino = get_be32(&ondisk->ino);
- ce->ce_mode = get_be32(&ondisk->mode);
- ce->ce_stat_data.sd_uid = get_be32(&ondisk->uid);
- ce->ce_stat_data.sd_gid = get_be32(&ondisk->gid);
- ce->ce_stat_data.sd_size = get_be32(&ondisk->size);
+ /*
+ * NEEDSWORK: using 'offsetof()' is cumbersome and should be replaced
+ * with something more akin to 'load_bitmap_entries_v1()'s use of
+ * 'read_be16'/'read_be32'. For consistency with the corresponding
+ * ondisk entry write function ('copy_cache_entry_to_ondisk()'), this
+ * should be done at the same time as removing references to
+ * 'ondisk_cache_entry' there.
+ */
+ ce->ce_stat_data.sd_ctime.sec = get_be32(ondisk + offsetof(struct ondisk_cache_entry, ctime)
+ + offsetof(struct cache_time, sec));
+ ce->ce_stat_data.sd_mtime.sec = get_be32(ondisk + offsetof(struct ondisk_cache_entry, mtime)
+ + offsetof(struct cache_time, sec));
+ ce->ce_stat_data.sd_ctime.nsec = get_be32(ondisk + offsetof(struct ondisk_cache_entry, ctime)
+ + offsetof(struct cache_time, nsec));
+ ce->ce_stat_data.sd_mtime.nsec = get_be32(ondisk + offsetof(struct ondisk_cache_entry, mtime)
+ + offsetof(struct cache_time, nsec));
+ ce->ce_stat_data.sd_dev = get_be32(ondisk + offsetof(struct ondisk_cache_entry, dev));
+ ce->ce_stat_data.sd_ino = get_be32(ondisk + offsetof(struct ondisk_cache_entry, ino));
+ ce->ce_mode = get_be32(ondisk + offsetof(struct ondisk_cache_entry, mode));
+ ce->ce_stat_data.sd_uid = get_be32(ondisk + offsetof(struct ondisk_cache_entry, uid));
+ ce->ce_stat_data.sd_gid = get_be32(ondisk + offsetof(struct ondisk_cache_entry, gid));
+ ce->ce_stat_data.sd_size = get_be32(ondisk + offsetof(struct ondisk_cache_entry, size));
ce->ce_flags = flags & ~CE_NAMEMASK;
ce->ce_namelen = len;
ce->index = 0;
- oidread(&ce->oid, ondisk->data);
- memcpy(ce->name, name, len);
- ce->name[len] = '\0';
+ oidread(&ce->oid, (const unsigned char *)ondisk + offsetof(struct ondisk_cache_entry, data));
if (expand_name_field) {
if (copy_len)
unsigned long src_offset = start_offset;
for (i = offset; i < offset + nr; i++) {
- struct ondisk_cache_entry *disk_ce;
struct cache_entry *ce;
unsigned long consumed;
- disk_ce = (struct ondisk_cache_entry *)(mmap + src_offset);
- ce = create_from_disk(ce_mem_pool, istate->version, disk_ce, &consumed, previous_ce);
+ ce = create_from_disk(ce_mem_pool, istate->version,
+ mmap + src_offset,
+ &consumed, previous_ce);
set_index_entry(istate, i, ce);
src_offset += consumed;
return consumed;
}
+static void set_new_index_sparsity(struct index_state *istate)
+{
+ /*
+ * If the index's repo exists, mark it sparse according to
+ * repo settings.
+ */
+ 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! */
int do_read_index(struct index_state *istate, const char *path, int must_exist)
{
istate->timestamp.nsec = 0;
fd = open(path, O_RDONLY);
if (fd < 0) {
- if (!must_exist && errno == ENOENT)
+ if (!must_exist && errno == ENOENT) {
+ set_new_index_sparsity(istate);
return 0;
+ }
die_errno(_("%s: index file open failed"), path);
}
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
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);
the_repository, "%s", base_path);
if (!ret) {
char *path_copy = xstrdup(path);
- const char *base_path2 = xstrfmt("%s/sharedindex.%s",
- dirname(path_copy),
- base_oid_hex);
+ char *base_path2 = xstrfmt("%s/sharedindex.%s",
+ dirname(path_copy), base_oid_hex);
free(path_copy);
trace2_region_enter_printf("index", "shared/do_read_index",
the_repository, "%s", base_path2);
ret = do_read_index(split_index->base, base_path2, 1);
trace2_region_leave_printf("index", "shared/do_read_index",
the_repository, "%s", base_path2);
+ free(base_path2);
}
if (!oideq(&split_index->base_oid, &split_index->base->oid))
die(_("broken index, expect %s in %s, got %s"),
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
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);
}
/*
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);
return verify_index_from(repo->index, repo->index_file);
}
-static int has_racy_timestamp(struct index_state *istate)
+int has_racy_timestamp(struct index_state *istate)
{
int entries = istate->cache_nr;
int i;
* rely on it.
*/
static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
- int strip_extensions)
+ int strip_extensions, unsigned flags)
{
uint64_t start = getnanotime();
struct hashfile *f;
struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
int drop_cache_tree = istate->drop_cache_tree;
off_t offset;
+ int csum_fsync_flag;
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++;
}
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)
!is_null_oid(&istate->split_index->base_oid)) {
struct strbuf sb = STRBUF_INIT;
+ if (istate->sparse_index)
+ die(_("cannot write split index for a sparse index"));
+
err = write_link_extension(&sb, istate) < 0 ||
write_index_ext_header(f, eoie_c, CACHE_EXT_LINK,
sb.len) < 0;
return -1;
}
- finalize_hashfile(f, istate->oid.hash, CSUM_HASH_IN_STREAM);
+ csum_fsync_flag = 0;
+ if (!alternate_index_output && (flags & COMMIT_LOCK))
+ csum_fsync_flag = CSUM_FSYNC;
+
+ finalize_hashfile(f, istate->oid.hash, FSYNC_COMPONENT_INDEX,
+ CSUM_HASH_IN_STREAM | csum_fsync_flag);
+
if (close_tempfile_gently(tempfile)) {
error(_("could not close '%s'"), get_tempfile_path(tempfile));
return -1;
unsigned flags)
{
int ret;
- int was_full = !istate->sparse_index;
+ int was_full = istate->sparse_index == INDEX_EXPANDED;
ret = convert_to_sparse(istate, 0);
*/
trace2_region_enter_printf("index", "do_write_index", the_repository,
"%s", get_lock_file_path(lock));
- ret = do_write_index(istate, lock->tempfile, 0);
+ ret = do_write_index(istate, lock->tempfile, 0, flags);
trace2_region_leave_printf("index", "do_write_index", the_repository,
"%s", get_lock_file_path(lock));
else
ret = close_lock_file_gently(lock);
- run_hook_le(NULL, "post-index-change",
+ run_hooks_l("post-index-change",
istate->updated_workdir ? "1" : "0",
istate->updated_skipworktree ? "1" : "0", NULL);
istate->updated_workdir = 0;
}
static int write_shared_index(struct index_state *istate,
- struct tempfile **temp)
+ struct tempfile **temp, unsigned flags)
{
struct split_index *si = istate->split_index;
int ret, was_full = !istate->sparse_index;
trace2_region_enter_printf("index", "shared/do_write_index",
the_repository, "%s", get_tempfile_path(*temp));
- ret = do_write_index(si->base, *temp, 1);
+ ret = do_write_index(si->base, *temp, 1, flags);
trace2_region_leave_printf("index", "shared/do_write_index",
the_repository, "%s", get_tempfile_path(*temp));
ret = do_write_locked_index(istate, lock, flags);
goto out;
}
- ret = write_shared_index(istate, &temp);
+ ret = write_shared_index(istate, &temp, flags);
saved_errno = errno;
if (is_tempfile_active(temp))
}
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;