From: Junio C Hamano Date: Tue, 5 Feb 2019 22:26:13 +0000 (-0800) Subject: Merge branch 'jk/add-ignore-errors-bit-assignment-fix' X-Git-Tag: v2.21.0-rc0~47 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/1c418243a525328b7261ba2c1f2cd93fd52191a2?ds=inline;hp=-c Merge branch 'jk/add-ignore-errors-bit-assignment-fix' "git add --ignore-errors" did not work as advertised and instead worked as an unintended synonym for "git add --renormalize", which has been fixed. * jk/add-ignore-errors-bit-assignment-fix: add: use separate ADD_CACHE_RENORMALIZE flag --- 1c418243a525328b7261ba2c1f2cd93fd52191a2 diff --combined builtin/add.c index d461ba08b9,95a7ec126e..7c2a7c5a4d --- a/builtin/add.c +++ b/builtin/add.c @@@ -110,7 -110,7 +110,7 @@@ int add_files_to_cache(const char *pref memset(&data, 0, sizeof(data)); data.flags = flags; - init_revisions(&rev, prefix); + repo_init_revisions(the_repository, &rev, prefix); setup_revisions(0, NULL, &rev, NULL); if (pathspec) copy_pathspec(&rev.prune_data, pathspec); @@@ -137,7 -137,7 +137,7 @@@ static int renormalize_tracked_files(co continue; /* do not touch non blobs */ if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL)) continue; - retval |= add_file_to_cache(ce->name, flags | HASH_RENORMALIZE); + retval |= add_file_to_cache(ce->name, flags | ADD_CACHE_RENORMALIZE); } return retval; @@@ -176,7 -176,7 +176,7 @@@ static void refresh(int verbose, const die(_("pathspec '%s' did not match any files"), pathspec->items[i].match); } - free(seen); + free(seen); } int run_add_interactive(const char *revision, const char *patch_mode, @@@ -232,14 -232,14 +232,14 @@@ static int edit_patch(int argc, const c if (read_cache() < 0) die(_("Could not read the index")); - init_revisions(&rev, prefix); + repo_init_revisions(the_repository, &rev, prefix); rev.diffopt.context = 7; argc = setup_revisions(argc, argv, &rev, NULL); rev.diffopt.output_format = DIFF_FORMAT_PATCH; rev.diffopt.use_color = 0; rev.diffopt.flags.ignore_dirty_submodules = 1; - out = open(file, O_CREAT | O_WRONLY, 0666); + out = open(file, O_CREAT | O_WRONLY | O_TRUNC, 0666); if (out < 0) die(_("Could not open '%s' for writing."), file); rev.diffopt.file = xfdopen(out, "w"); @@@ -445,6 -445,11 +445,6 @@@ int cmd_add(int argc, const char **argv return 0; } - if (read_cache() < 0) - die(_("index file corrupt")); - - die_in_unpopulated_submodule(&the_index, prefix); - /* * Check the "pathspec '%s' did not match any files" block * below before enabling new magic. @@@ -454,10 -459,6 +454,10 @@@ PATHSPEC_SYMLINK_LEADING_PATH, prefix, argv); + if (read_cache_preload(&pathspec) < 0) + die(_("index file corrupt")); + + die_in_unpopulated_submodule(&the_index, prefix); die_path_inside_submodule(&the_index, &pathspec); if (add_new_files) { diff --combined cache.h index 009e8b3b15,18ecbe7104..038e3764a9 --- a/cache.h +++ b/cache.h @@@ -45,20 -45,10 +45,20 @@@ unsigned long git_deflate_bound(git_zst /* The length in bytes and in hex digits of an object name (SHA-1 value). */ #define GIT_SHA1_RAWSZ 20 #define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ) +/* The block size of SHA-1. */ +#define GIT_SHA1_BLKSZ 64 + +/* The length in bytes and in hex digits of an object name (SHA-256 value). */ +#define GIT_SHA256_RAWSZ 32 +#define GIT_SHA256_HEXSZ (2 * GIT_SHA256_RAWSZ) +/* The block size of SHA-256. */ +#define GIT_SHA256_BLKSZ 64 /* The length in byte and in hex digits of the largest possible hash value. */ -#define GIT_MAX_RAWSZ GIT_SHA1_RAWSZ -#define GIT_MAX_HEXSZ GIT_SHA1_HEXSZ +#define GIT_MAX_RAWSZ GIT_SHA256_RAWSZ +#define GIT_MAX_HEXSZ GIT_SHA256_HEXSZ +/* The largest possible block size for any supported hash. */ +#define GIT_MAX_BLKSZ GIT_SHA256_BLKSZ struct object_id { unsigned char hash[GIT_MAX_RAWSZ]; @@@ -420,7 -410,7 +420,7 @@@ void validate_cache_entries(const struc #define read_cache() read_index(&the_index) #define read_cache_from(path) read_index_from(&the_index, (path), (get_git_dir())) -#define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec)) +#define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec), 0) #define is_cache_unborn() is_index_unborn(&the_index) #define read_cache_unmerged() read_index_unmerged(&the_index) #define discard_cache() discard_index(&the_index) @@@ -496,8 -486,6 +496,8 @@@ static inline enum object_type object_t #define INFOATTRIBUTES_FILE "info/attributes" #define ATTRIBUTE_MACRO_PREFIX "[attr]" #define GITMODULES_FILE ".gitmodules" +#define GITMODULES_INDEX ":.gitmodules" +#define GITMODULES_HEAD "HEAD:.gitmodules" #define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF" #define GIT_NOTES_DEFAULT_REF "refs/notes/commits" #define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF" @@@ -671,12 -659,7 +671,12 @@@ extern int daemonize(void) /* Initialize and use the cache information */ struct lock_file; extern int read_index(struct index_state *); -extern int read_index_preload(struct index_state *, const struct pathspec *pathspec); +extern void preload_index(struct index_state *index, + const struct pathspec *pathspec, + unsigned int refresh_flags); +extern int read_index_preload(struct index_state *, + const struct pathspec *pathspec, + unsigned int refresh_flags); extern int do_read_index(struct index_state *istate, const char *path, int must_exist); /* for testting only! */ extern int read_index_from(struct index_state *, const char *path, @@@ -720,7 -703,7 +720,7 @@@ extern int unmerged_index(const struct * provided, the space-separated list of files that differ will be appended * to it. */ -extern int index_has_changes(const struct index_state *istate, +extern int index_has_changes(struct index_state *istate, struct tree *tree, struct strbuf *sb); @@@ -755,6 -738,7 +755,7 @@@ extern int index_name_pos(const struct #define ADD_CACHE_JUST_APPEND 8 /* Append only; tree.c::read_tree() */ #define ADD_CACHE_NEW_ONLY 16 /* Do not replace existing ones */ #define ADD_CACHE_KEEP_CACHE_TREE 32 /* Do not invalidate cache-tree */ + #define ADD_CACHE_RENORMALIZE 64 /* Pass along HASH_RENORMALIZE */ extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option); extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name); @@@ -806,8 -790,8 +807,8 @@@ extern int ie_modified(struct index_sta #define HASH_WRITE_OBJECT 1 #define HASH_FORMAT_CHECK 2 #define HASH_RENORMALIZE 4 -extern int index_fd(struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags); -extern int index_path(struct object_id *oid, const char *path, struct stat *st, unsigned flags); +extern int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags); +extern int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags); /* * Record to sd the data from st that we use to check whether a file @@@ -833,7 -817,6 +834,7 @@@ extern void fill_stat_cache_info(struc #define REFRESH_IGNORE_MISSING 0x0008 /* ignore non-existent */ #define REFRESH_IGNORE_SUBMODULES 0x0010 /* ignore submodules */ #define REFRESH_IN_PORCELAIN 0x0020 /* user friendly output, not "needs update" */ +#define REFRESH_PROGRESS 0x0040 /* show progress bar if stderr is tty */ extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg); extern struct cache_entry *refresh_cache_entry(struct index_state *, struct cache_entry *, unsigned int); @@@ -921,6 -904,14 +922,6 @@@ int use_optional_locks(void) extern char comment_line_char; extern int auto_comment_line_char; -/* Windows only */ -enum hide_dotfiles_type { - HIDE_DOTFILES_FALSE = 0, - HIDE_DOTFILES_TRUE, - HIDE_DOTFILES_DOTGITONLY -}; -extern enum hide_dotfiles_type hide_dotfiles; - enum log_refs_config { LOG_REFS_UNSET = -1, LOG_REFS_NONE = 0, @@@ -969,13 -960,11 +970,13 @@@ extern int grafts_replace_parents extern int repository_format_precious_objects; extern char *repository_format_partial_clone; extern const char *core_partial_clone_filter_default; +extern int repository_format_worktree_config; struct repository_format { int version; int precious_objects; char *partial_clone; /* value of extensions.partialclone */ + int worktree_config; int is_bare; int hash_algo; char *work_tree; @@@ -1038,12 -1027,16 +1039,12 @@@ extern const struct object_id null_oid static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2) { /* - * This is a temporary optimization hack. By asserting the size here, - * we let the compiler know that it's always going to be 20, which lets - * it turn this fixed-size memcmp into a few inline instructions. - * - * This will need to be extended or ripped out when we learn about - * hashes of different sizes. + * Teach the compiler that there are only two possibilities of hash size + * here, so that it can optimize for this case as much as possible. */ - if (the_hash_algo->rawsz != 20) - BUG("hash size not yet supported by hashcmp"); - return memcmp(sha1, sha2, the_hash_algo->rawsz); + if (the_hash_algo->rawsz == GIT_MAX_RAWSZ) + return memcmp(sha1, sha2, GIT_MAX_RAWSZ); + return memcmp(sha1, sha2, GIT_SHA1_RAWSZ); } static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2) @@@ -1051,30 -1044,14 +1052,30 @@@ return hashcmp(oid1->hash, oid2->hash); } +static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2) +{ + /* + * We write this here instead of deferring to hashcmp so that the + * compiler can properly inline it and avoid calling memcmp. + */ + if (the_hash_algo->rawsz == GIT_MAX_RAWSZ) + return !memcmp(sha1, sha2, GIT_MAX_RAWSZ); + return !memcmp(sha1, sha2, GIT_SHA1_RAWSZ); +} + +static inline int oideq(const struct object_id *oid1, const struct object_id *oid2) +{ + return hasheq(oid1->hash, oid2->hash); +} + static inline int is_null_sha1(const unsigned char *sha1) { - return !hashcmp(sha1, null_sha1); + return hasheq(sha1, null_sha1); } static inline int is_null_oid(const struct object_id *oid) { - return !hashcmp(oid->hash, null_sha1); + return hasheq(oid->hash, null_sha1); } static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src) @@@ -1084,7 -1061,7 +1085,7 @@@ static inline void oidcpy(struct object_id *dst, const struct object_id *src) { - hashcpy(dst->hash, src->hash); + memcpy(dst->hash, src->hash, GIT_MAX_RAWSZ); } static inline struct object_id *oiddup(const struct object_id *src) @@@ -1111,22 -1088,22 +1112,22 @@@ static inline void oidread(struct objec static inline int is_empty_blob_sha1(const unsigned char *sha1) { - return !hashcmp(sha1, the_hash_algo->empty_blob->hash); + return hasheq(sha1, the_hash_algo->empty_blob->hash); } static inline int is_empty_blob_oid(const struct object_id *oid) { - return !oidcmp(oid, the_hash_algo->empty_blob); + return oideq(oid, the_hash_algo->empty_blob); } static inline int is_empty_tree_sha1(const unsigned char *sha1) { - return !hashcmp(sha1, the_hash_algo->empty_tree->hash); + return hasheq(sha1, the_hash_algo->empty_tree->hash); } static inline int is_empty_tree_oid(const struct object_id *oid) { - return !oidcmp(oid, the_hash_algo->empty_tree); + return oideq(oid, the_hash_algo->empty_tree); } const char *empty_tree_oid_hex(void); @@@ -1377,9 -1354,9 +1378,9 @@@ extern int get_oid_hex(const char *hex extern int hex_to_bytes(unsigned char *binary, const char *hex, size_t len); /* - * Convert a binary sha1 to its hex equivalent. The `_r` variant is reentrant, + * Convert a binary hash to its hex equivalent. The `_r` variant is reentrant, * and writes the NUL-terminated output to the buffer `out`, which must be at - * least `GIT_SHA1_HEXSZ + 1` bytes, and returns a pointer to out for + * least `GIT_MAX_HEXSZ + 1` bytes, and returns a pointer to out for * convenience. * * The non-`_r` variant returns a static buffer, but uses a ring of 4 @@@ -1387,13 -1364,10 +1388,13 @@@ * * printf("%s -> %s", sha1_to_hex(one), sha1_to_hex(two)); */ -extern char *sha1_to_hex_r(char *out, const unsigned char *sha1); -extern char *oid_to_hex_r(char *out, const struct object_id *oid); -extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */ -extern char *oid_to_hex(const struct object_id *oid); /* same static buffer as sha1_to_hex */ +char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash, const struct git_hash_algo *); +char *sha1_to_hex_r(char *out, const unsigned char *sha1); +char *oid_to_hex_r(char *out, const struct object_id *oid); +char *hash_to_hex_algop(const unsigned char *hash, const struct git_hash_algo *); /* static buffer result! */ +char *sha1_to_hex(const unsigned char *sha1); /* same static buffer */ +char *hash_to_hex(const unsigned char *hash); /* same static buffer */ +char *oid_to_hex(const struct object_id *oid); /* same static buffer */ /* * Parse a 40-character hexadecimal object ID starting from hex, updating the @@@ -1501,7 -1475,6 +1502,7 @@@ extern const char *fmt_name(const char extern const char *ident_default_name(void); extern const char *ident_default_email(void); extern const char *git_editor(void); +extern const char *git_sequence_editor(void); extern const char *git_pager(int stdout_is_tty); extern int is_terminal_dumb(void); extern int git_ident_config(const char *, const char *, void *); @@@ -1548,15 -1521,14 +1549,15 @@@ struct checkout unsigned force:1, quiet:1, not_new:1, + clone:1, refresh_cache:1; }; #define CHECKOUT_INIT { NULL, "" } #define TEMPORARY_FILENAME_LENGTH 25 -extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath); +extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath, int *nr_checkouts); extern void enable_delayed_checkout(struct checkout *state); -extern int finish_delayed_checkout(struct checkout *state); +extern int finish_delayed_checkout(struct checkout *state, int *nr_checkouts); struct cache_def { struct strbuf path; @@@ -1725,7 -1697,7 +1726,7 @@@ void shift_tree_by(const struct object_ /* All WS_* -- when extended, adapt diff.c emit_symbol */ #define WS_RULE_MASK 07777 extern unsigned whitespace_rule_cfg; -extern unsigned whitespace_rule(const char *); +extern unsigned whitespace_rule(struct index_state *, const char *); extern unsigned parse_whitespace_rule(const char *); extern unsigned ws_check(const char *line, int len, unsigned ws_rule); extern void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws); @@@ -1747,12 -1719,10 +1748,12 @@@ extern struct startup_info *startup_inf /* merge.c */ struct commit_list; -int try_merge_command(const char *strategy, size_t xopts_nr, +int try_merge_command(struct repository *r, + const char *strategy, size_t xopts_nr, const char **xopts, struct commit_list *common, const char *head_arg, struct commit_list *remotes); -int checkout_fast_forward(const struct object_id *from, +int checkout_fast_forward(struct repository *r, + const struct object_id *from, const struct object_id *to, int overwrite_ignore); diff --combined read-cache.c index bfff271a3d,ce679c1799..9783c493a3 --- a/read-cache.c +++ b/read-cache.c @@@ -23,8 -23,6 +23,8 @@@ #include "split-index.h" #include "utf8.h" #include "fsmonitor.h" +#include "thread-utils.h" +#include "progress.h" /* Mask for the name length in ce_flags in the on-disk index */ @@@ -45,8 -43,6 +45,8 @@@ #define CACHE_EXT_LINK 0x6c696e6b /* "link" */ #define CACHE_EXT_UNTRACKED 0x554E5452 /* "UNTR" */ #define CACHE_EXT_FSMONITOR 0x46534D4E /* "FSMN" */ +#define CACHE_EXT_ENDOFINDEXENTRIES 0x454F4945 /* "EOIE" */ +#define CACHE_EXT_INDEXENTRYOFFSETTABLE 0x49454F54 /* "IEOT" */ /* changes that can be kept in $GIT_DIR/index (basically all extensions) */ #define EXTMASK (RESOLVE_UNDO_CHANGED | CACHE_TREE_CHANGED | \ @@@ -209,17 -205,15 +209,17 @@@ void fill_stat_cache_info(struct cache_ } } -static int ce_compare_data(const struct cache_entry *ce, struct stat *st) +static int ce_compare_data(struct index_state *istate, + const struct cache_entry *ce, + struct stat *st) { int match = -1; int fd = git_open_cloexec(ce->name, O_RDONLY); if (fd >= 0) { struct object_id oid; - if (!index_fd(&oid, fd, st, OBJ_BLOB, ce->name, 0)) - match = oidcmp(&oid, &ce->oid); + if (!index_fd(istate, &oid, fd, st, OBJ_BLOB, ce->name, 0)) + match = !oideq(&oid, &ce->oid); /* index_fd() closed the file descriptor already */ } return match; @@@ -260,16 -254,14 +260,16 @@@ static int ce_compare_gitlink(const str */ if (resolve_gitlink_ref(ce->name, "HEAD", &oid) < 0) return 0; - return oidcmp(&oid, &ce->oid); + return !oideq(&oid, &ce->oid); } -static int ce_modified_check_fs(const struct cache_entry *ce, struct stat *st) +static int ce_modified_check_fs(struct index_state *istate, + const struct cache_entry *ce, + struct stat *st) { switch (st->st_mode & S_IFMT) { case S_IFREG: - if (ce_compare_data(ce, st)) + if (ce_compare_data(istate, ce, st)) return DATA_CHANGED; break; case S_IFLNK: @@@ -316,7 -308,7 +316,7 @@@ static int ce_match_stat_basic(const st changed |= DATA_CHANGED; return changed; default: - die("internal error: ce_mode is %o", ce->ce_mode); + BUG("unsupported ce_mode: %o", ce->ce_mode); } changed |= match_stat_data(&ce->ce_stat_data, st); @@@ -415,7 -407,7 +415,7 @@@ int ie_match_stat(struct index_state *i if (assume_racy_is_modified) changed |= DATA_CHANGED; else - changed |= ce_modified_check_fs(ce, st); + changed |= ce_modified_check_fs(istate, ce, st); } return changed; @@@ -455,7 -447,7 +455,7 @@@ int ie_modified(struct index_state *ist (S_ISGITLINK(ce->ce_mode) || ce->ce_stat_data.sd_size != 0)) return changed; - changed_fs = ce_modified_check_fs(ce, st); + changed_fs = ce_modified_check_fs(istate, ce, st); if (changed_fs) return changed | changed_fs; return 0; @@@ -672,8 -664,7 +672,8 @@@ static struct cache_entry *create_alias struct cache_entry *new_entry; if (alias->ce_flags & CE_ADDED) - die("Will not add file alias '%s' ('%s' already exists in index)", ce->name, alias->name); + 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); @@@ -688,7 -679,7 +688,7 @@@ void set_object_name_for_intent_to_add_ { struct object_id oid; if (write_object_file("", 0, blob_type, &oid)) - die("cannot create an empty blob in the object database"); + die(_("cannot create an empty blob in the object database")); oidcpy(&ce->oid, &oid); } @@@ -703,13 -694,13 +703,13 @@@ int add_to_index(struct index_state *is int intent_only = flags & ADD_CACHE_INTENT; int add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE| (intent_only ? ADD_CACHE_NEW_ONLY : 0)); - int newflags = HASH_WRITE_OBJECT; + int hash_flags = HASH_WRITE_OBJECT; - if (flags & HASH_RENORMALIZE) - newflags |= HASH_RENORMALIZE; + if (flags & ADD_CACHE_RENORMALIZE) + hash_flags |= HASH_RENORMALIZE; if (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode)) - return error("%s: can only add regular files, symbolic links or git-directories", path); + return error(_("%s: can only add regular files, symbolic links or git-directories"), path); namelen = strlen(path); if (S_ISDIR(st_mode)) { @@@ -762,9 -753,9 +762,9 @@@ } } if (!intent_only) { - if (index_path(istate, &ce->oid, path, st, newflags)) { - if (index_path(&ce->oid, path, st, hash_flags)) { ++ if (index_path(istate, &ce->oid, path, st, hash_flags)) { discard_cache_entry(ce); - return error("unable to index file %s", path); + return error(_("unable to index file '%s'"), path); } } else set_object_name_for_intent_to_add_entry(ce); @@@ -776,14 -767,14 +776,14 @@@ /* It was suspected to be racily clean, but it turns out to be Ok */ was_same = (alias && !ce_stage(alias) && - !oidcmp(&alias->oid, &ce->oid) && + oideq(&alias->oid, &ce->oid) && ce->ce_mode == alias->ce_mode); if (pretend) discard_cache_entry(ce); else if (add_index_entry(istate, ce, add_option)) { discard_cache_entry(ce); - return error("unable to add %s to index", path); + return error(_("unable to add '%s' to index"), path); } if (verbose && !was_same) printf("add '%s'\n", path); @@@ -794,7 -785,7 +794,7 @@@ int add_file_to_index(struct index_stat { struct stat st; if (lstat(path, &st)) - die_errno("unable to stat '%s'", path); + die_errno(_("unable to stat '%s'"), path); return add_to_index(istate, path, &st, flags); } @@@ -819,7 -810,7 +819,7 @@@ struct cache_entry *make_cache_entry(st int len; if (!verify_path(path, mode)) { - error("Invalid path '%s'", path); + error(_("invalid path '%s'"), path); return NULL; } @@@ -832,7 -823,7 +832,7 @@@ ce->ce_namelen = len; ce->ce_mode = create_ce_mode(mode); - ret = refresh_cache_entry(&the_index, ce, refresh_options); + ret = refresh_cache_entry(istate, ce, refresh_options); if (ret != ce) discard_cache_entry(ce); return ret; @@@ -845,7 -836,7 +845,7 @@@ struct cache_entry *make_transient_cach int len; if (!verify_path(path, mode)) { - error("Invalid path '%s'", path); + error(_("invalid path '%s'"), path); return NULL; } @@@ -1298,12 -1289,12 +1298,12 @@@ static int add_index_entry_with_check(s if (!ok_to_add) return -1; if (!verify_path(ce->name, ce->ce_mode)) - return error("Invalid path '%s'", ce->name); + return error(_("invalid path '%s'"), ce->name); if (!skip_df_check && check_file_directory_conflict(istate, ce, pos, ok_to_replace)) { if (!ok_to_replace) - return error("'%s' appears as both a file and as a directory", + return error(_("'%s' appears as both a file and as a directory"), ce->name); pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce)); pos = -pos-1; @@@ -1485,24 -1476,13 +1485,24 @@@ int refresh_index(struct index_state *i const char *typechange_fmt; const char *added_fmt; const char *unmerged_fmt; - uint64_t start = getnanotime(); - - modified_fmt = (in_porcelain ? "M\t%s\n" : "%s: needs update\n"); - deleted_fmt = (in_porcelain ? "D\t%s\n" : "%s: needs update\n"); - typechange_fmt = (in_porcelain ? "T\t%s\n" : "%s needs update\n"); - added_fmt = (in_porcelain ? "A\t%s\n" : "%s needs update\n"); - unmerged_fmt = (in_porcelain ? "U\t%s\n" : "%s: needs merge\n"); + struct progress *progress = NULL; + + if (flags & REFRESH_PROGRESS && isatty(2)) + progress = start_delayed_progress(_("Refresh index"), + istate->cache_nr); + + trace_performance_enter(); + modified_fmt = in_porcelain ? "M\t%s\n" : "%s: needs update\n"; + deleted_fmt = in_porcelain ? "D\t%s\n" : "%s: needs update\n"; + typechange_fmt = in_porcelain ? "T\t%s\n" : "%s: needs update\n"; + added_fmt = in_porcelain ? "A\t%s\n" : "%s: needs update\n"; + unmerged_fmt = in_porcelain ? "U\t%s\n" : "%s: needs merge\n"; + /* + * Use the multi-threaded preload_index() to refresh most of the + * cache entries quickly then in the single threaded loop below, + * we only have to do the special cases that are left. + */ + preload_index(istate, pathspec, 0); for (i = 0; i < istate->cache_nr; i++) { struct cache_entry *ce, *new_entry; int cache_errno = 0; @@@ -1513,7 -1493,7 +1513,7 @@@ if (ignore_submodules && S_ISGITLINK(ce->ce_mode)) continue; - if (pathspec && !ce_path_match(&the_index, ce, pathspec, seen)) + if (pathspec && !ce_path_match(istate, ce, pathspec, seen)) filtered = 1; if (ce_stage(ce)) { @@@ -1536,8 -1516,6 +1536,8 @@@ new_entry = refresh_cache_ent(istate, ce, options, &cache_errno, &changed); if (new_entry == ce) continue; + if (progress) + display_progress(progress, i); if (!new_entry) { const char *fmt; @@@ -1569,11 -1547,7 +1569,11 @@@ replace_index_entry(istate, i, new_entry); } - trace_performance_since(start, "refresh index"); + if (progress) { + display_progress(progress, istate->cache_nr); + stop_progress(&progress); + } + trace_performance_leave("refresh index"); return has_errors; } @@@ -1676,17 -1650,17 +1676,17 @@@ int verify_index_checksum /* Allow fsck to force verification of the cache entry order. */ int verify_ce_order; -static int verify_hdr(struct cache_header *hdr, unsigned long size) +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; if (hdr->hdr_signature != htonl(CACHE_SIGNATURE)) - return error("bad signature"); + return error(_("bad signature 0x%08x"), hdr->hdr_signature); hdr_version = ntohl(hdr->hdr_version); if (hdr_version < INDEX_FORMAT_LB || INDEX_FORMAT_UB < hdr_version) - return error("bad index version %d", hdr_version); + return error(_("bad index version %d"), hdr_version); if (!verify_index_checksum) return 0; @@@ -1694,13 -1668,13 +1694,13 @@@ 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 (hashcmp(hash, (unsigned char *)hdr + size - the_hash_algo->rawsz)) - return error("bad index file sha1 signature"); + if (!hasheq(hash, (unsigned char *)hdr + size - the_hash_algo->rawsz)) + return error(_("bad index file sha1 signature")); return 0; } static int read_index_extension(struct index_state *istate, - const char *ext, void *data, unsigned long sz) + const char *ext, const char *data, unsigned long sz) { switch (CACHE_EXT(ext)) { case CACHE_EXT_TREE: @@@ -1719,15 -1693,11 +1719,15 @@@ case CACHE_EXT_FSMONITOR: read_fsmonitor_extension(istate, data, sz); break; + case CACHE_EXT_ENDOFINDEXENTRIES: + case CACHE_EXT_INDEXENTRYOFFSETTABLE: + /* already handled in do_read_index() */ + break; default: if (*ext < 'A' || 'Z' < *ext) - return error("index uses %.4s extension, which we do not understand", + return error(_("index uses %.4s extension, which we do not understand"), ext); - fprintf(stderr, "ignoring %.4s extension\n", ext); + fprintf_ln(stderr, _("ignoring %.4s extension"), ext); break; } return 0; @@@ -1743,25 -1713,63 +1743,25 @@@ int read_index(struct index_state *ista return read_index_from(istate, get_index_file(), get_git_dir()); } -static struct cache_entry *cache_entry_from_ondisk(struct mem_pool *mem_pool, - struct ondisk_cache_entry *ondisk, - unsigned int flags, - const char *name, - size_t len) -{ - struct cache_entry *ce = mem_pool__ce_alloc(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); - ce->ce_flags = flags & ~CE_NAMEMASK; - ce->ce_namelen = len; - ce->index = 0; - hashcpy(ce->oid.hash, ondisk->sha1); - memcpy(ce->name, name, len); - ce->name[len] = '\0'; - return ce; -} - -/* - * Adjacent cache entries tend to share the leading paths, so it makes - * sense to only store the differences in later entries. In the v4 - * on-disk format of the index, each on-disk cache entry stores the - * number of bytes to be stripped from the end of the previous name, - * and the bytes to append to the result, to come up with its name. - */ -static unsigned long expand_name_field(struct strbuf *name, const char *cp_) -{ - const unsigned char *ep, *cp = (const unsigned char *)cp_; - size_t len = decode_varint(&cp); - - if (name->len < len) - die("malformed name field in the index"); - strbuf_remove(name, name->len - len, len); - for (ep = cp; *ep; ep++) - ; /* find the end */ - strbuf_add(name, cp, ep - cp); - return (const char *)ep + 1 - cp_; -} - -static struct cache_entry *create_from_disk(struct mem_pool *mem_pool, +static struct cache_entry *create_from_disk(struct mem_pool *ce_mem_pool, + unsigned int version, struct ondisk_cache_entry *ondisk, unsigned long *ent_size, - struct strbuf *previous_name) + const struct cache_entry *previous_ce) { struct cache_entry *ce; size_t len; const char *name; unsigned int flags; + size_t copy_len = 0; + /* + * Adjacent cache entries tend to share the leading paths, so it makes + * sense to only store the differences in later entries. In the v4 + * on-disk format of the index, each on-disk cache entry stores the + * number of bytes to be stripped from the end of the previous name, + * and the bytes to append to the result, to come up with its name. + */ + int expand_name_field = version == 4; /* On-disk flags are just 16 bits */ flags = get_be16(&ondisk->flags); @@@ -1774,60 -1782,28 +1774,60 @@@ extended_flags = get_be16(&ondisk2->flags2) << 16; /* We do not yet understand any bit out of CE_EXTENDED_FLAGS */ if (extended_flags & ~CE_EXTENDED_FLAGS) - die("Unknown index entry format %08x", extended_flags); + die(_("unknown index entry format 0x%08x"), extended_flags); flags |= extended_flags; name = ondisk2->name; } else name = ondisk->name; - if (!previous_name) { - /* v3 and earlier */ - if (len == CE_NAMEMASK) - len = strlen(name); - ce = cache_entry_from_ondisk(mem_pool, ondisk, flags, name, len); + if (expand_name_field) { + const unsigned char *cp = (const unsigned char *)name; + size_t strip_len, previous_len; + + /* If we're at the begining of a block, ignore the previous name */ + strip_len = decode_varint(&cp); + if (previous_ce) { + previous_len = previous_ce->ce_namelen; + if (previous_len < strip_len) + die(_("malformed name field in the index, near path '%s'"), + previous_ce->name); + copy_len = previous_len - strip_len; + } + name = (const char *)cp; + } - *ent_size = ondisk_ce_size(ce); - } else { - unsigned long consumed; - consumed = expand_name_field(previous_name, name); - ce = cache_entry_from_ondisk(mem_pool, ondisk, flags, - previous_name->buf, - previous_name->len); + if (len == CE_NAMEMASK) { + len = strlen(name); + if (expand_name_field) + len += copy_len; + } - *ent_size = (name - ((char *)ondisk)) + consumed; + 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); + ce->ce_flags = flags & ~CE_NAMEMASK; + ce->ce_namelen = len; + ce->index = 0; + hashcpy(ce->oid.hash, ondisk->sha1); + + if (expand_name_field) { + if (copy_len) + memcpy(ce->name, previous_ce->name, copy_len); + memcpy(ce->name + copy_len, name, len + 1 - copy_len); + *ent_size = (name - ((char *)ondisk)) + len + 1 - copy_len; + } else { + memcpy(ce->name, name, len + 1); + *ent_size = ondisk_ce_size(ce); } return ce; } @@@ -1845,13 -1821,13 +1845,13 @@@ static void check_ce_order(struct index int name_compare = strcmp(ce->name, next_ce->name); if (0 < name_compare) - die("unordered stage entries in index"); + die(_("unordered stage entries in index")); if (!name_compare) { if (!ce_stage(ce)) - die("multiple stage entries for merged file '%s'", + die(_("multiple stage entries for merged file '%s'"), ce->name); if (ce_stage(ce) > ce_stage(next_ce)) - die("unordered stage entries for '%s'", + die(_("unordered stage entries for '%s'"), ce->name); } } @@@ -1913,228 -1889,16 +1913,228 @@@ static size_t estimate_cache_size(size_ return ondisk_size + entries * per_entry; } +struct index_entry_offset +{ + /* starting byte offset into index file, count of index entries in this block */ + int offset, nr; +}; + +struct index_entry_offset_table +{ + int nr; + struct index_entry_offset entries[FLEX_ARRAY]; +}; + +static struct index_entry_offset_table *read_ieot_extension(const char *mmap, size_t mmap_size, size_t offset); +static void write_ieot_extension(struct strbuf *sb, struct index_entry_offset_table *ieot); + +static size_t read_eoie_extension(const char *mmap, size_t mmap_size); +static void write_eoie_extension(struct strbuf *sb, git_hash_ctx *eoie_context, size_t offset); + +struct load_index_extensions +{ + pthread_t pthread; + struct index_state *istate; + const char *mmap; + size_t mmap_size; + unsigned long src_offset; +}; + +static void *load_index_extensions(void *_data) +{ + struct load_index_extensions *p = _data; + unsigned long src_offset = p->src_offset; + + while (src_offset <= p->mmap_size - the_hash_algo->rawsz - 8) { + /* After an array of active_nr index entries, + * there can be arbitrary number of extended + * sections, each of which is prefixed with + * extension name (4-byte) and section length + * in 4-byte network byte order. + */ + uint32_t extsize = get_be32(p->mmap + src_offset + 4); + if (read_index_extension(p->istate, + p->mmap + src_offset, + p->mmap + src_offset + 8, + extsize) < 0) { + munmap((void *)p->mmap, p->mmap_size); + die(_("index file corrupt")); + } + src_offset += 8; + src_offset += extsize; + } + + return NULL; +} + +/* + * A helper function that will load the specified range of cache entries + * from the memory mapped file and add them to the given index. + */ +static unsigned long load_cache_entry_block(struct index_state *istate, + struct mem_pool *ce_mem_pool, int offset, int nr, const char *mmap, + unsigned long start_offset, const struct cache_entry *previous_ce) +{ + int i; + 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); + set_index_entry(istate, i, ce); + + src_offset += consumed; + previous_ce = ce; + } + return src_offset - start_offset; +} + +static unsigned long load_all_cache_entries(struct index_state *istate, + const char *mmap, size_t mmap_size, unsigned long src_offset) +{ + unsigned long consumed; + + if (istate->version == 4) { + mem_pool_init(&istate->ce_mem_pool, + estimate_cache_size_from_compressed(istate->cache_nr)); + } else { + mem_pool_init(&istate->ce_mem_pool, + estimate_cache_size(mmap_size, istate->cache_nr)); + } + + consumed = load_cache_entry_block(istate, istate->ce_mem_pool, + 0, istate->cache_nr, mmap, src_offset, NULL); + return consumed; +} + +/* + * Mostly randomly chosen maximum thread counts: we + * cap the parallelism to online_cpus() threads, and we want + * to have at least 10000 cache entries per thread for it to + * be worth starting a thread. + */ + +#define THREAD_COST (10000) + +struct load_cache_entries_thread_data +{ + pthread_t pthread; + struct index_state *istate; + struct mem_pool *ce_mem_pool; + int offset; + const char *mmap; + struct index_entry_offset_table *ieot; + int ieot_start; /* starting index into the ieot array */ + int ieot_blocks; /* count of ieot entries to process */ + unsigned long consumed; /* return # of bytes in index file processed */ +}; + +/* + * A thread proc to run the load_cache_entries() computation + * across multiple background threads. + */ +static void *load_cache_entries_thread(void *_data) +{ + struct load_cache_entries_thread_data *p = _data; + int i; + + /* iterate across all ieot blocks assigned to this thread */ + for (i = p->ieot_start; i < p->ieot_start + p->ieot_blocks; i++) { + p->consumed += load_cache_entry_block(p->istate, p->ce_mem_pool, + p->offset, p->ieot->entries[i].nr, p->mmap, p->ieot->entries[i].offset, NULL); + p->offset += p->ieot->entries[i].nr; + } + return NULL; +} + +static unsigned long load_cache_entries_threaded(struct index_state *istate, const char *mmap, size_t mmap_size, + unsigned long src_offset, int nr_threads, struct index_entry_offset_table *ieot) +{ + int i, offset, ieot_blocks, ieot_start, err; + struct load_cache_entries_thread_data *data; + unsigned long consumed = 0; + + /* a little sanity checking */ + if (istate->name_hash_initialized) + BUG("the name hash isn't thread safe"); + + mem_pool_init(&istate->ce_mem_pool, 0); + + /* ensure we have no more threads than we have blocks to process */ + if (nr_threads > ieot->nr) + nr_threads = ieot->nr; + data = xcalloc(nr_threads, sizeof(*data)); + + offset = ieot_start = 0; + ieot_blocks = DIV_ROUND_UP(ieot->nr, nr_threads); + for (i = 0; i < nr_threads; i++) { + struct load_cache_entries_thread_data *p = &data[i]; + int nr, j; + + if (ieot_start + ieot_blocks > ieot->nr) + ieot_blocks = ieot->nr - ieot_start; + + p->istate = istate; + p->offset = offset; + p->mmap = mmap; + p->ieot = ieot; + p->ieot_start = ieot_start; + p->ieot_blocks = ieot_blocks; + + /* create a mem_pool for each thread */ + nr = 0; + for (j = p->ieot_start; j < p->ieot_start + p->ieot_blocks; j++) + nr += p->ieot->entries[j].nr; + if (istate->version == 4) { + mem_pool_init(&p->ce_mem_pool, + estimate_cache_size_from_compressed(nr)); + } else { + mem_pool_init(&p->ce_mem_pool, + estimate_cache_size(mmap_size, nr)); + } + + err = pthread_create(&p->pthread, NULL, load_cache_entries_thread, p); + if (err) + die(_("unable to create load_cache_entries thread: %s"), strerror(err)); + + /* increment by the number of cache entries in the ieot block being processed */ + for (j = 0; j < ieot_blocks; j++) + offset += ieot->entries[ieot_start + j].nr; + ieot_start += ieot_blocks; + } + + for (i = 0; i < nr_threads; i++) { + struct load_cache_entries_thread_data *p = &data[i]; + + err = pthread_join(p->pthread, NULL); + if (err) + die(_("unable to join load_cache_entries thread: %s"), strerror(err)); + mem_pool_combine(istate->ce_mem_pool, p->ce_mem_pool); + consumed += p->consumed; + } + + free(data); + + return consumed; +} + /* remember to discard_cache() before reading a different cache! */ int do_read_index(struct index_state *istate, const char *path, int must_exist) { - int fd, i; + int fd; struct stat st; unsigned long src_offset; - struct cache_header *hdr; - void *mmap; + const struct cache_header *hdr; + const char *mmap; size_t mmap_size; - struct strbuf previous_name_buf = STRBUF_INIT, *previous_name; + struct load_index_extensions p; + size_t extension_offset = 0; + int nr_threads, cpus; + struct index_entry_offset_table *ieot = NULL; if (istate->initialized) return istate->cache_nr; @@@ -2145,22 -1909,22 +2145,22 @@@ if (fd < 0) { if (!must_exist && errno == ENOENT) return 0; - die_errno("%s: index file open failed", path); + die_errno(_("%s: index file open failed"), path); } if (fstat(fd, &st)) - die_errno("cannot stat the open index"); + die_errno(_("%s: cannot stat the open index"), path); mmap_size = xsize_t(st.st_size); if (mmap_size < sizeof(struct cache_header) + the_hash_algo->rawsz) - die("index file smaller than expected"); + die(_("%s: index file smaller than expected"), path); mmap = xmmap(NULL, mmap_size, PROT_READ, MAP_PRIVATE, fd, 0); if (mmap == MAP_FAILED) - die_errno("unable to map index file"); + die_errno(_("%s: unable to map index file"), path); close(fd); - hdr = mmap; + hdr = (const struct cache_header *)mmap; if (verify_hdr(hdr, mmap_size) < 0) goto unmap; @@@ -2171,72 -1935,56 +2171,72 @@@ istate->cache = xcalloc(istate->cache_alloc, sizeof(*istate->cache)); istate->initialized = 1; - if (istate->version == 4) { - previous_name = &previous_name_buf; - mem_pool_init(&istate->ce_mem_pool, - estimate_cache_size_from_compressed(istate->cache_nr)); - } else { - previous_name = NULL; - mem_pool_init(&istate->ce_mem_pool, - estimate_cache_size(mmap_size, istate->cache_nr)); - } + p.istate = istate; + p.mmap = mmap; + p.mmap_size = mmap_size; src_offset = sizeof(*hdr); - for (i = 0; i < istate->cache_nr; i++) { - struct ondisk_cache_entry *disk_ce; - struct cache_entry *ce; - unsigned long consumed; - disk_ce = (struct ondisk_cache_entry *)((char *)mmap + src_offset); - ce = create_from_disk(istate->ce_mem_pool, disk_ce, &consumed, previous_name); - set_index_entry(istate, i, ce); + if (git_config_get_index_threads(&nr_threads)) + nr_threads = 1; - src_offset += consumed; + /* TODO: does creating more threads than cores help? */ + if (!nr_threads) { + nr_threads = istate->cache_nr / THREAD_COST; + cpus = online_cpus(); + if (nr_threads > cpus) + nr_threads = cpus; } - strbuf_release(&previous_name_buf); + + if (!HAVE_THREADS) + nr_threads = 1; + + if (nr_threads > 1) { + extension_offset = read_eoie_extension(mmap, mmap_size); + if (extension_offset) { + int err; + + p.src_offset = extension_offset; + err = pthread_create(&p.pthread, NULL, load_index_extensions, &p); + if (err) + die(_("unable to create load_index_extensions thread: %s"), strerror(err)); + + nr_threads--; + } + } + + /* + * Locate and read the index entry offset table so that we can use it + * to multi-thread the reading of the cache entries. + */ + if (extension_offset && nr_threads > 1) + ieot = read_ieot_extension(mmap, mmap_size, extension_offset); + + if (ieot) { + src_offset += load_cache_entries_threaded(istate, mmap, mmap_size, src_offset, nr_threads, ieot); + free(ieot); + } else { + src_offset += load_all_cache_entries(istate, mmap, mmap_size, src_offset); + } + istate->timestamp.sec = st.st_mtime; istate->timestamp.nsec = ST_MTIME_NSEC(st); - while (src_offset <= mmap_size - the_hash_algo->rawsz - 8) { - /* After an array of active_nr index entries, - * there can be arbitrary number of extended - * sections, each of which is prefixed with - * extension name (4-byte) and section length - * in 4-byte network byte order. - */ - uint32_t extsize; - memcpy(&extsize, (char *)mmap + src_offset + 4, 4); - extsize = ntohl(extsize); - if (read_index_extension(istate, - (const char *) mmap + src_offset, - (char *) mmap + src_offset + 8, - extsize) < 0) - goto unmap; - src_offset += 8; - src_offset += extsize; + /* if we created a thread, join it otherwise load the extensions on the primary thread */ + if (extension_offset) { + int ret = pthread_join(p.pthread, NULL); + if (ret) + die(_("unable to join load_index_extensions thread: %s"), strerror(ret)); + } else { + p.src_offset = src_offset; + load_index_extensions(&p); } - munmap(mmap, mmap_size); + munmap((void *)mmap, mmap_size); return istate->cache_nr; unmap: - munmap(mmap, mmap_size); - die("index file corrupt"); + munmap((void *)mmap, mmap_size); + die(_("index file corrupt")); } /* @@@ -2248,12 -1996,13 +2248,12 @@@ static void freshen_shared_index(const char *shared_index, int warn) { if (!check_and_freshen_file(shared_index, 1) && warn) - warning("could not freshen shared index '%s'", shared_index); + warning(_("could not freshen shared index '%s'"), shared_index); } int read_index_from(struct index_state *istate, const char *path, const char *gitdir) { - uint64_t start = getnanotime(); struct split_index *split_index; int ret; char *base_oid_hex; @@@ -2263,9 -2012,8 +2263,9 @@@ if (istate->initialized) return istate->cache_nr; + trace_performance_enter(); ret = do_read_index(istate, path, 0); - trace_performance_since(start, "read cache %s", path); + trace_performance_leave("read cache %s", path); split_index = istate->split_index; if (!split_index || is_null_oid(&split_index->base_oid)) { @@@ -2273,7 -2021,6 +2273,7 @@@ return ret; } + trace_performance_enter(); if (split_index->base) discard_index(split_index->base); else @@@ -2282,15 -2029,15 +2282,15 @@@ base_oid_hex = oid_to_hex(&split_index->base_oid); base_path = xstrfmt("%s/sharedindex.%s", gitdir, base_oid_hex); ret = do_read_index(split_index->base, base_path, 1); - if (oidcmp(&split_index->base_oid, &split_index->base->oid)) - die("broken index, expect %s in %s, got %s", + if (!oideq(&split_index->base_oid, &split_index->base->oid)) + die(_("broken index, expect %s in %s, got %s"), base_oid_hex, base_path, oid_to_hex(&split_index->base->oid)); freshen_shared_index(base_path, 0); merge_base_index(istate); post_read_index_from(istate); - trace_performance_since(start, "read cache %s", base_path); + trace_performance_leave("read cache %s", base_path); free(base_path); return ret; } @@@ -2349,14 -2096,14 +2349,14 @@@ void validate_cache_entries(const struc for (i = 0; i < istate->cache_nr; i++) { if (!istate) { - die("internal error: cache entry is not allocated from expected memory pool"); + BUG("cache entry is not allocated from expected memory pool"); } else if (!istate->ce_mem_pool || !mem_pool_contains(istate->ce_mem_pool, istate->cache[i])) { if (!istate->split_index || !istate->split_index->base || !istate->split_index->base->ce_mem_pool || !mem_pool_contains(istate->split_index->base->ce_mem_pool, istate->cache[i])) { - die("internal error: cache entry is not allocated from expected memory pool"); + BUG("cache entry is not allocated from expected memory pool"); } } } @@@ -2375,7 -2122,7 +2375,7 @@@ int unmerged_index(const struct index_s return 0; } -int index_has_changes(const struct index_state *istate, +int index_has_changes(struct index_state *istate, struct tree *tree, struct strbuf *sb) { @@@ -2390,7 -2137,7 +2390,7 @@@ if (tree || !get_oid_tree("HEAD", &cmp)) { struct diff_options opt; - diff_setup(&opt); + repo_diff_setup(the_repository, &opt); opt.flags.exit_with_status = 1; if (!sb) opt.flags.quick = 1; @@@ -2451,15 -2198,11 +2451,15 @@@ static int ce_write(git_hash_ctx *conte return 0; } -static int write_index_ext_header(git_hash_ctx *context, int fd, - unsigned int ext, unsigned int sz) +static int write_index_ext_header(git_hash_ctx *context, git_hash_ctx *eoie_context, + int fd, unsigned int ext, unsigned int sz) { ext = htonl(ext); sz = htonl(sz); + if (eoie_context) { + the_hash_algo->update_fn(eoie_context, &ext, 4); + the_hash_algo->update_fn(eoie_context, &sz, 4); + } return ((ce_write(context, fd, &ext, 4) < 0) || (ce_write(context, fd, &sz, 4) < 0)) ? -1 : 0; } @@@ -2487,8 -2230,7 +2487,8 @@@ static int ce_flush(git_hash_ctx *conte return (write_in_full(fd, write_buffer, left) < 0) ? -1 : 0; } -static void ce_smudge_racily_clean_entry(struct cache_entry *ce) +static void ce_smudge_racily_clean_entry(struct index_state *istate, + struct cache_entry *ce) { /* * The only thing we care about in this function is to smudge the @@@ -2507,7 -2249,7 +2507,7 @@@ return; if (ce_match_stat_basic(ce, &st)) return; - if (ce_modified_check_fs(ce, &st)) { + if (ce_modified_check_fs(istate, ce, &st)) { /* This is "racily clean"; smudge it. Note that this * is a tricky code. At first glance, it may appear * that it can break with this sequence: @@@ -2653,7 -2395,7 +2653,7 @@@ static int verify_index_from(const stru if (n != the_hash_algo->rawsz) goto out; - if (hashcmp(istate->oid.hash, hash)) + if (!hasheq(istate->oid.hash, hash)) goto out; close(fd); @@@ -2691,36 -2433,6 +2691,36 @@@ void update_index_if_able(struct index_ rollback_lock_file(lockfile); } +static int record_eoie(void) +{ + int val; + + if (!git_config_get_bool("index.recordendofindexentries", &val)) + return val; + + /* + * As a convenience, the end of index entries extension + * used for threading is written by default if the user + * explicitly requested threaded index reads. + */ + return !git_config_get_index_threads(&val) && val != 1; +} + +static int record_ieot(void) +{ + int val; + + if (!git_config_get_bool("index.recordoffsettable", &val)) + return val; + + /* + * As a convenience, the offset table used for threading is + * written by default if the user explicitly requested + * threaded index reads. + */ + return !git_config_get_index_threads(&val) && val != 1; +} + /* * On success, `tempfile` is closed. If it is the temporary file * of a `struct lock_file`, we will therefore effectively perform @@@ -2733,7 -2445,7 +2733,7 @@@ static int do_write_index(struct index_ { uint64_t start = getnanotime(); int newfd = tempfile->fd; - git_hash_ctx c; + git_hash_ctx c, eoie_c; struct cache_header hdr; int i, err = 0, removed, extended, hdr_version; struct cache_entry **cache = istate->cache; @@@ -2742,10 -2454,6 +2742,10 @@@ struct ondisk_cache_entry_extended ondisk; struct strbuf previous_name_buf = STRBUF_INIT, *previous_name; int drop_cache_tree = istate->drop_cache_tree; + off_t offset; + int ieot_entries = 1; + struct index_entry_offset_table *ieot = NULL; + int nr, nr_threads; for (i = removed = extended = 0; i < entries; i++) { if (cache[i]->ce_flags & CE_REMOVE) @@@ -2779,46 -2487,6 +2779,46 @@@ if (ce_write(&c, newfd, &hdr, sizeof(hdr)) < 0) return -1; + if (!HAVE_THREADS || git_config_get_index_threads(&nr_threads)) + nr_threads = 1; + + if (nr_threads != 1 && record_ieot()) { + int ieot_blocks, cpus; + + /* + * ensure default number of ieot blocks maps evenly to the + * default number of threads that will process them leaving + * room for the thread to load the index extensions. + */ + if (!nr_threads) { + ieot_blocks = istate->cache_nr / THREAD_COST; + cpus = online_cpus(); + if (ieot_blocks > cpus - 1) + ieot_blocks = cpus - 1; + } else { + ieot_blocks = nr_threads; + if (ieot_blocks > istate->cache_nr) + ieot_blocks = istate->cache_nr; + } + + /* + * no reason to write out the IEOT extension if we don't + * have enough blocks to utilize multi-threading + */ + if (ieot_blocks > 1) { + ieot = xcalloc(1, sizeof(struct index_entry_offset_table) + + (ieot_blocks * sizeof(struct index_entry_offset))); + ieot_entries = DIV_ROUND_UP(entries, ieot_blocks); + } + } + + offset = lseek(newfd, 0, SEEK_CUR); + if (offset < 0) { + free(ieot); + return -1; + } + offset += write_buffer_len; + nr = 0; previous_name = (hdr_version == 4) ? &previous_name_buf : NULL; for (i = 0; i < entries; i++) { @@@ -2826,7 -2494,7 +2826,7 @@@ if (ce->ce_flags & CE_REMOVE) continue; if (!ce_uptodate(ce) && is_racy_timestamp(istate, ce)) - ce_smudge_racily_clean_entry(ce); + ce_smudge_racily_clean_entry(istate, ce); if (is_null_oid(&ce->oid)) { static const char msg[] = "cache entry has null sha1: %s"; static int allow = -1; @@@ -2840,77 -2508,23 +2840,77 @@@ drop_cache_tree = 1; } + if (ieot && i && (i % ieot_entries == 0)) { + ieot->entries[ieot->nr].nr = nr; + ieot->entries[ieot->nr].offset = offset; + ieot->nr++; + /* + * If we have a V4 index, set the first byte to an invalid + * character to ensure there is nothing common with the previous + * entry + */ + if (previous_name) + previous_name->buf[0] = 0; + nr = 0; + offset = lseek(newfd, 0, SEEK_CUR); + if (offset < 0) { + free(ieot); + return -1; + } + offset += write_buffer_len; + } if (ce_write_entry(&c, newfd, ce, previous_name, (struct ondisk_cache_entry *)&ondisk) < 0) err = -1; if (err) break; + nr++; + } + if (ieot && nr) { + ieot->entries[ieot->nr].nr = nr; + ieot->entries[ieot->nr].offset = offset; + ieot->nr++; } strbuf_release(&previous_name_buf); - if (err) + if (err) { + free(ieot); return err; + } /* Write extension data here */ + offset = lseek(newfd, 0, SEEK_CUR); + if (offset < 0) { + free(ieot); + return -1; + } + offset += write_buffer_len; + the_hash_algo->init_fn(&eoie_c); + + /* + * Lets write out CACHE_EXT_INDEXENTRYOFFSETTABLE first so that we + * can minimize the number of extensions we have to scan through to + * find it during load. Write it out regardless of the + * strip_extensions parameter as we need it when loading the shared + * index. + */ + if (ieot) { + struct strbuf sb = STRBUF_INIT; + + write_ieot_extension(&sb, ieot); + err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_INDEXENTRYOFFSETTABLE, sb.len) < 0 + || ce_write(&c, newfd, sb.buf, sb.len) < 0; + strbuf_release(&sb); + free(ieot); + if (err) + return -1; + } + if (!strip_extensions && istate->split_index) { struct strbuf sb = STRBUF_INIT; err = write_link_extension(&sb, istate) < 0 || - write_index_ext_header(&c, newfd, CACHE_EXT_LINK, + write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_LINK, sb.len) < 0 || ce_write(&c, newfd, sb.buf, sb.len) < 0; strbuf_release(&sb); @@@ -2921,7 -2535,7 +2921,7 @@@ struct strbuf sb = STRBUF_INIT; cache_tree_write(&sb, istate->cache_tree); - err = write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sb.len) < 0 + err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_TREE, sb.len) < 0 || ce_write(&c, newfd, sb.buf, sb.len) < 0; strbuf_release(&sb); if (err) @@@ -2931,7 -2545,7 +2931,7 @@@ struct strbuf sb = STRBUF_INIT; resolve_undo_write(&sb, istate->resolve_undo); - err = write_index_ext_header(&c, newfd, CACHE_EXT_RESOLVE_UNDO, + err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_RESOLVE_UNDO, sb.len) < 0 || ce_write(&c, newfd, sb.buf, sb.len) < 0; strbuf_release(&sb); @@@ -2942,7 -2556,7 +2942,7 @@@ struct strbuf sb = STRBUF_INIT; write_untracked_extension(&sb, istate->untracked); - err = write_index_ext_header(&c, newfd, CACHE_EXT_UNTRACKED, + err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_UNTRACKED, sb.len) < 0 || ce_write(&c, newfd, sb.buf, sb.len) < 0; strbuf_release(&sb); @@@ -2953,24 -2567,7 +2953,24 @@@ struct strbuf sb = STRBUF_INIT; write_fsmonitor_extension(&sb, istate); - err = write_index_ext_header(&c, newfd, CACHE_EXT_FSMONITOR, sb.len) < 0 + err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_FSMONITOR, sb.len) < 0 + || ce_write(&c, newfd, sb.buf, sb.len) < 0; + strbuf_release(&sb); + if (err) + return -1; + } + + /* + * CACHE_EXT_ENDOFINDEXENTRIES must be written as the last entry before the SHA1 + * so that it can be found and processed before all the index entries are + * read. Write it out regardless of the strip_extensions parameter as we need it + * when loading the shared index. + */ + if (offset && record_eoie()) { + struct strbuf sb = STRBUF_INIT; + + write_eoie_extension(&sb, &eoie_c, offset); + err = write_index_ext_header(&c, NULL, newfd, CACHE_EXT_ENDOFINDEXENTRIES, sb.len) < 0 || ce_write(&c, newfd, sb.buf, sb.len) < 0; strbuf_release(&sb); if (err) @@@ -3097,7 -2694,7 +3097,7 @@@ static int write_shared_index(struct in return ret; ret = adjust_shared_perm(get_tempfile_path(*temp)); if (ret) { - error("cannot fix permission bits on %s", get_tempfile_path(*temp)); + error(_("cannot fix permission bits on '%s'"), get_tempfile_path(*temp)); return ret; } ret = rename_tempfile(temp, @@@ -3146,9 -2743,6 +3146,9 @@@ int write_locked_index(struct index_sta int new_shared_index, ret; struct split_index *si = istate->split_index; + if (git_env_bool("GIT_TEST_CHECK_CACHE_TREE", 0)) + cache_tree_verify(the_repository, istate); + if ((flags & SKIP_IF_UNCHANGED) && !istate->cache_changed) { if (flags & COMMIT_LOCK) rollback_lock_file(lock); @@@ -3180,8 -2774,7 +3180,8 @@@ struct tempfile *temp; int saved_errno; - temp = mks_tempfile(git_path("sharedindex_XXXXXX")); + /* 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); @@@ -3244,7 -2837,7 +3244,7 @@@ int read_index_unmerged(struct index_st new_ce->ce_namelen = len; new_ce->ce_mode = ce->ce_mode; if (add_index_entry(istate, new_ce, ADD_CACHE_SKIP_DFCHECK)) - return error("%s: cannot drop to stage #0", + return error(_("%s: cannot drop to stage #0"), new_ce->name); } return unmerged; @@@ -3346,8 -2939,6 +3346,8 @@@ void move_index_extensions(struct index { dst->untracked = src->untracked; src->untracked = NULL; + dst->cache_tree = src->cache_tree; + src->cache_tree = NULL; } struct cache_entry *dup_cache_entry(const struct cache_entry *ce, @@@ -3387,179 -2978,3 +3387,179 @@@ int should_validate_cache_entries(void return validate_index_cache_entries; } + +#define EOIE_SIZE (4 + GIT_SHA1_RAWSZ) /* <4-byte offset> + <20-byte hash> */ +#define EOIE_SIZE_WITH_HEADER (4 + 4 + EOIE_SIZE) /* <4-byte signature> + <4-byte length> + EOIE_SIZE */ + +static size_t read_eoie_extension(const char *mmap, size_t mmap_size) +{ + /* + * The end of index entries (EOIE) extension is guaranteed to be last + * so that it can be found by scanning backwards from the EOF. + * + * "EOIE" + * <4-byte length> + * <4-byte offset> + * <20-byte hash> + */ + const char *index, *eoie; + uint32_t extsize; + size_t offset, src_offset; + unsigned char hash[GIT_MAX_RAWSZ]; + git_hash_ctx c; + + /* ensure we have an index big enough to contain an EOIE extension */ + if (mmap_size < sizeof(struct cache_header) + EOIE_SIZE_WITH_HEADER + the_hash_algo->rawsz) + return 0; + + /* validate the extension signature */ + index = eoie = mmap + mmap_size - EOIE_SIZE_WITH_HEADER - the_hash_algo->rawsz; + if (CACHE_EXT(index) != CACHE_EXT_ENDOFINDEXENTRIES) + return 0; + index += sizeof(uint32_t); + + /* validate the extension size */ + extsize = get_be32(index); + if (extsize != EOIE_SIZE) + return 0; + index += sizeof(uint32_t); + + /* + * Validate the offset we're going to look for the first extension + * signature is after the index header and before the eoie extension. + */ + offset = get_be32(index); + if (mmap + offset < mmap + sizeof(struct cache_header)) + return 0; + if (mmap + offset >= eoie) + return 0; + index += sizeof(uint32_t); + + /* + * The hash is computed over extension types and their sizes (but not + * their contents). E.g. if we have "TREE" extension that is N-bytes + * long, "REUC" extension that is M-bytes long, followed by "EOIE", + * then the hash would be: + * + * SHA-1("TREE" + + + * "REUC" + ) + */ + src_offset = offset; + the_hash_algo->init_fn(&c); + while (src_offset < mmap_size - the_hash_algo->rawsz - EOIE_SIZE_WITH_HEADER) { + /* After an array of active_nr index entries, + * there can be arbitrary number of extended + * sections, each of which is prefixed with + * extension name (4-byte) and section length + * in 4-byte network byte order. + */ + uint32_t extsize; + memcpy(&extsize, mmap + src_offset + 4, 4); + extsize = ntohl(extsize); + + /* verify the extension size isn't so large it will wrap around */ + if (src_offset + 8 + extsize < src_offset) + return 0; + + the_hash_algo->update_fn(&c, mmap + src_offset, 8); + + src_offset += 8; + src_offset += extsize; + } + the_hash_algo->final_fn(hash, &c); + if (!hasheq(hash, (const unsigned char *)index)) + return 0; + + /* Validate that the extension offsets returned us back to the eoie extension. */ + if (src_offset != mmap_size - the_hash_algo->rawsz - EOIE_SIZE_WITH_HEADER) + return 0; + + return offset; +} + +static void write_eoie_extension(struct strbuf *sb, git_hash_ctx *eoie_context, size_t offset) +{ + uint32_t buffer; + unsigned char hash[GIT_MAX_RAWSZ]; + + /* offset */ + put_be32(&buffer, offset); + strbuf_add(sb, &buffer, sizeof(uint32_t)); + + /* hash */ + the_hash_algo->final_fn(hash, eoie_context); + strbuf_add(sb, hash, the_hash_algo->rawsz); +} + +#define IEOT_VERSION (1) + +static struct index_entry_offset_table *read_ieot_extension(const char *mmap, size_t mmap_size, size_t offset) +{ + const char *index = NULL; + uint32_t extsize, ext_version; + struct index_entry_offset_table *ieot; + int i, nr; + + /* find the IEOT extension */ + if (!offset) + return NULL; + while (offset <= mmap_size - the_hash_algo->rawsz - 8) { + extsize = get_be32(mmap + offset + 4); + if (CACHE_EXT((mmap + offset)) == CACHE_EXT_INDEXENTRYOFFSETTABLE) { + index = mmap + offset + 4 + 4; + break; + } + offset += 8; + offset += extsize; + } + if (!index) + return NULL; + + /* validate the version is IEOT_VERSION */ + ext_version = get_be32(index); + if (ext_version != IEOT_VERSION) { + error("invalid IEOT version %d", ext_version); + return NULL; + } + index += sizeof(uint32_t); + + /* extension size - version bytes / bytes per entry */ + nr = (extsize - sizeof(uint32_t)) / (sizeof(uint32_t) + sizeof(uint32_t)); + if (!nr) { + error("invalid number of IEOT entries %d", nr); + return NULL; + } + ieot = xmalloc(sizeof(struct index_entry_offset_table) + + (nr * sizeof(struct index_entry_offset))); + ieot->nr = nr; + for (i = 0; i < nr; i++) { + ieot->entries[i].offset = get_be32(index); + index += sizeof(uint32_t); + ieot->entries[i].nr = get_be32(index); + index += sizeof(uint32_t); + } + + return ieot; +} + +static void write_ieot_extension(struct strbuf *sb, struct index_entry_offset_table *ieot) +{ + uint32_t buffer; + int i; + + /* version */ + put_be32(&buffer, IEOT_VERSION); + strbuf_add(sb, &buffer, sizeof(uint32_t)); + + /* ieot */ + for (i = 0; i < ieot->nr; i++) { + + /* offset */ + put_be32(&buffer, ieot->entries[i].offset); + strbuf_add(sb, &buffer, sizeof(uint32_t)); + + /* count */ + put_be32(&buffer, ieot->entries[i].nr); + strbuf_add(sb, &buffer, sizeof(uint32_t)); + } +}