From: Junio C Hamano Date: Tue, 22 May 2018 05:18:06 +0000 (+0900) Subject: Sync with Git 2.15.2 X-Git-Tag: v2.16.4~1 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/023020401d21c8c48d7f0a123c7ff50888e101c7?hp=-c Sync with Git 2.15.2 * maint-2.15: Git 2.15.2 Git 2.14.4 Git 2.13.7 verify_path: disallow symlinks in .gitmodules update-index: stat updated files earlier verify_dotfile: mention case-insensitivity in comment verify_path: drop clever fallthrough skip_prefix: add case-insensitive variant is_{hfs,ntfs}_dotgitmodules: add tests is_ntfs_dotgit: match other .git files is_hfs_dotgit: match other .git files is_ntfs_dotgit: use a size_t for traversing string submodule-config: verify submodule names as paths --- 023020401d21c8c48d7f0a123c7ff50888e101c7 diff --combined apply.c index 321a9fa68d,97f387540b..5a147ced30 --- a/apply.c +++ b/apply.c @@@ -75,10 -75,13 +75,10 @@@ static int parse_ignorewhitespace_optio } int init_apply_state(struct apply_state *state, - const char *prefix, - struct lock_file *lock_file) + const char *prefix) { memset(state, 0, sizeof(*state)); state->prefix = prefix; - state->lock_file = lock_file; - state->newfd = -1; state->apply = 1; state->line_termination = '\n'; state->p_value = 1; @@@ -143,6 -146,8 +143,6 @@@ int check_apply_state(struct apply_stat } if (state->check_index) state->unsafe_paths = 0; - if (!state->lock_file) - return error("BUG: state->lock_file should not be NULL"); if (state->apply_verbosity <= verbosity_silent) { state->saved_error_routine = get_error_routine(); @@@ -3860,9 -3865,9 +3860,9 @@@ static int check_unsafe_path(struct pat if (!patch->is_delete) new_name = patch->new_name; - if (old_name && !verify_path(old_name)) + if (old_name && !verify_path(old_name, patch->old_mode)) return error(_("invalid path '%s'"), old_name); - if (new_name && !verify_path(new_name)) + if (new_name && !verify_path(new_name, patch->new_mode)) return error(_("invalid path '%s'"), new_name); return 0; } @@@ -4687,13 -4692,13 +4687,13 @@@ static int apply_patch(struct apply_sta state->apply = 0; state->update_index = state->check_index && state->apply; - if (state->update_index && state->newfd < 0) { + if (state->update_index && !is_lock_file_locked(&state->lock_file)) { if (state->index_file) - state->newfd = hold_lock_file_for_update(state->lock_file, - state->index_file, - LOCK_DIE_ON_ERROR); + hold_lock_file_for_update(&state->lock_file, + state->index_file, + LOCK_DIE_ON_ERROR); else - state->newfd = hold_locked_index(state->lock_file, LOCK_DIE_ON_ERROR); + hold_locked_index(&state->lock_file, LOCK_DIE_ON_ERROR); } if (state->check_index && read_apply_cache(state) < 0) { @@@ -4889,18 -4894,22 +4889,18 @@@ int apply_all_patches(struct apply_stat } if (state->update_index) { - res = write_locked_index(&the_index, state->lock_file, COMMIT_LOCK); + res = write_locked_index(&the_index, &state->lock_file, COMMIT_LOCK); if (res) { error(_("Unable to write new index file")); res = -128; goto end; } - state->newfd = -1; } res = !!errs; end: - if (state->newfd >= 0) { - rollback_lock_file(state->lock_file); - state->newfd = -1; - } + rollback_lock_file(&state->lock_file); if (state->apply_verbosity <= verbosity_silent) { set_error_routine(state->saved_error_routine); diff --combined builtin/submodule--helper.c index a5c4a8a694,77940499b0..9f658a59a2 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@@ -13,16 -13,6 +13,16 @@@ #include "remote.h" #include "refs.h" #include "connect.h" +#include "revision.h" +#include "diffcore.h" +#include "diff.h" + +#define OPT_QUIET (1 << 0) +#define OPT_CACHED (1 << 1) +#define OPT_RECURSIVE (1 << 2) + +typedef void (*each_submodule_fn)(const struct cache_entry *list_item, + void *cb_data); static char *get_default_remote(void) { @@@ -229,64 -219,6 +229,64 @@@ static int resolve_relative_url_test(in return 0; } +/* the result should be freed by the caller. */ +static char *get_submodule_displaypath(const char *path, const char *prefix) +{ + const char *super_prefix = get_super_prefix(); + + if (prefix && super_prefix) { + BUG("cannot have prefix '%s' and superprefix '%s'", + prefix, super_prefix); + } else if (prefix) { + struct strbuf sb = STRBUF_INIT; + char *displaypath = xstrdup(relative_path(path, prefix, &sb)); + strbuf_release(&sb); + return displaypath; + } else if (super_prefix) { + return xstrfmt("%s%s", super_prefix, path); + } else { + return xstrdup(path); + } +} + +static char *compute_rev_name(const char *sub_path, const char* object_id) +{ + struct strbuf sb = STRBUF_INIT; + const char ***d; + + static const char *describe_bare[] = { NULL }; + + static const char *describe_tags[] = { "--tags", NULL }; + + static const char *describe_contains[] = { "--contains", NULL }; + + static const char *describe_all_always[] = { "--all", "--always", NULL }; + + static const char **describe_argv[] = { describe_bare, describe_tags, + describe_contains, + describe_all_always, NULL }; + + for (d = describe_argv; *d; d++) { + struct child_process cp = CHILD_PROCESS_INIT; + prepare_submodule_repo_env(&cp.env_array); + cp.dir = sub_path; + cp.git_cmd = 1; + cp.no_stderr = 1; + + argv_array_push(&cp.args, "describe"); + argv_array_pushv(&cp.args, *d); + argv_array_push(&cp.args, object_id); + + if (!capture_command(&cp, &sb, 0)) { + strbuf_strip_suffix(&sb, "\n"); + return strbuf_detach(&sb, NULL); + } + } + + strbuf_release(&sb); + return NULL; +} + struct module_list { const struct cache_entry **entries; int alloc, nr; @@@ -396,29 -328,21 +396,29 @@@ static int module_list(int argc, const return 0; } -static void init_submodule(const char *path, const char *prefix, int quiet) +static void for_each_listed_submodule(const struct module_list *list, + each_submodule_fn fn, void *cb_data) +{ + int i; + for (i = 0; i < list->nr; i++) + fn(list->entries[i], cb_data); +} + +struct init_cb { + const char *prefix; + unsigned int flags; +}; + +#define INIT_CB_INIT { NULL, 0 } + +static void init_submodule(const char *path, const char *prefix, + unsigned int flags) { const struct submodule *sub; struct strbuf sb = STRBUF_INIT; char *upd = NULL, *url = NULL, *displaypath; - if (prefix && get_super_prefix()) - die("BUG: cannot have prefix and superprefix"); - else if (prefix) - displaypath = xstrdup(relative_path(path, prefix, &sb)); - else if (get_super_prefix()) { - strbuf_addf(&sb, "%s%s", get_super_prefix(), path); - displaypath = strbuf_detach(&sb, NULL); - } else - displaypath = xstrdup(path); + displaypath = get_submodule_displaypath(path, prefix); sub = submodule_from_path(&null_oid, path); @@@ -433,9 -357,9 +433,9 @@@ * Set active flag for the submodule being initialized */ if (!is_submodule_active(the_repository, path)) { - strbuf_reset(&sb); strbuf_addf(&sb, "submodule.%s.active", sub->name); git_config_set_gently(sb.buf, "true"); + strbuf_reset(&sb); } /* @@@ -443,6 -367,7 +443,6 @@@ * To look up the url in .git/config, we must not fall back to * .gitmodules, so look it up directly. */ - strbuf_reset(&sb); strbuf_addf(&sb, "submodule.%s.url", sub->name); if (git_config_get_string(sb.buf, &url)) { if (!sub->url) @@@ -474,14 -399,14 +474,14 @@@ if (git_config_set_gently(sb.buf, url)) die(_("Failed to register url for submodule path '%s'"), displaypath); - if (!quiet) + if (!(flags & OPT_QUIET)) fprintf(stderr, _("Submodule '%s' (%s) registered for path '%s'\n"), sub->name, url, displaypath); } + strbuf_reset(&sb); /* Copy "update" setting when it is not set yet */ - strbuf_reset(&sb); strbuf_addf(&sb, "submodule.%s.update", sub->name); if (git_config_get_string(sb.buf, &upd) && sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) { @@@ -501,18 -426,12 +501,18 @@@ free(upd); } +static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data) +{ + struct init_cb *info = cb_data; + init_submodule(list_item->name, info->prefix, info->flags); +} + static int module_init(int argc, const char **argv, const char *prefix) { + struct init_cb info = INIT_CB_INIT; struct pathspec pathspec; struct module_list list = MODULE_LIST_INIT; int quiet = 0; - int i; struct option module_init_options[] = { OPT__QUIET(&quiet, N_("Suppress output for initializing a submodule")), @@@ -537,165 -456,8 +537,165 @@@ if (!argc && git_config_get_value_multi("submodule.active")) module_list_active(&list); - for (i = 0; i < list.nr; i++) - init_submodule(list.entries[i]->name, prefix, quiet); + info.prefix = prefix; + if (quiet) + info.flags |= OPT_QUIET; + + for_each_listed_submodule(&list, init_submodule_cb, &info); + + return 0; +} + +struct status_cb { + const char *prefix; + unsigned int flags; +}; + +#define STATUS_CB_INIT { NULL, 0 } + +static void print_status(unsigned int flags, char state, const char *path, + const struct object_id *oid, const char *displaypath) +{ + if (flags & OPT_QUIET) + return; + + printf("%c%s %s", state, oid_to_hex(oid), displaypath); + + if (state == ' ' || state == '+') + printf(" (%s)", compute_rev_name(path, oid_to_hex(oid))); + + printf("\n"); +} + +static int handle_submodule_head_ref(const char *refname, + const struct object_id *oid, int flags, + void *cb_data) +{ + struct object_id *output = cb_data; + if (oid) + oidcpy(output, oid); + + return 0; +} + +static void status_submodule(const char *path, const struct object_id *ce_oid, + unsigned int ce_flags, const char *prefix, + unsigned int flags) +{ + char *displaypath; + struct argv_array diff_files_args = ARGV_ARRAY_INIT; + struct rev_info rev; + int diff_files_result; + + if (!submodule_from_path(&null_oid, path)) + die(_("no submodule mapping found in .gitmodules for path '%s'"), + path); + + displaypath = get_submodule_displaypath(path, prefix); + + if ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) { + print_status(flags, 'U', path, &null_oid, displaypath); + goto cleanup; + } + + if (!is_submodule_active(the_repository, path)) { + print_status(flags, '-', path, ce_oid, displaypath); + goto cleanup; + } + + argv_array_pushl(&diff_files_args, "diff-files", + "--ignore-submodules=dirty", "--quiet", "--", + path, NULL); + + git_config(git_diff_basic_config, NULL); + init_revisions(&rev, prefix); + rev.abbrev = 0; + diff_files_args.argc = setup_revisions(diff_files_args.argc, + diff_files_args.argv, + &rev, NULL); + diff_files_result = run_diff_files(&rev, 0); + + if (!diff_result_code(&rev.diffopt, diff_files_result)) { + print_status(flags, ' ', path, ce_oid, + displaypath); + } else if (!(flags & OPT_CACHED)) { + struct object_id oid; + + if (refs_head_ref(get_submodule_ref_store(path), + handle_submodule_head_ref, &oid)) + die(_("could not resolve HEAD ref inside the " + "submodule '%s'"), path); + + print_status(flags, '+', path, &oid, displaypath); + } else { + print_status(flags, '+', path, ce_oid, displaypath); + } + + if (flags & OPT_RECURSIVE) { + struct child_process cpr = CHILD_PROCESS_INIT; + + cpr.git_cmd = 1; + cpr.dir = path; + prepare_submodule_repo_env(&cpr.env_array); + + argv_array_push(&cpr.args, "--super-prefix"); + argv_array_pushf(&cpr.args, "%s/", displaypath); + argv_array_pushl(&cpr.args, "submodule--helper", "status", + "--recursive", NULL); + + if (flags & OPT_CACHED) + argv_array_push(&cpr.args, "--cached"); + + if (flags & OPT_QUIET) + argv_array_push(&cpr.args, "--quiet"); + + if (run_command(&cpr)) + die(_("failed to recurse into submodule '%s'"), path); + } + +cleanup: + argv_array_clear(&diff_files_args); + free(displaypath); +} + +static void status_submodule_cb(const struct cache_entry *list_item, + void *cb_data) +{ + struct status_cb *info = cb_data; + status_submodule(list_item->name, &list_item->oid, list_item->ce_flags, + info->prefix, info->flags); +} + +static int module_status(int argc, const char **argv, const char *prefix) +{ + struct status_cb info = STATUS_CB_INIT; + struct pathspec pathspec; + struct module_list list = MODULE_LIST_INIT; + int quiet = 0; + + struct option module_status_options[] = { + OPT__QUIET(&quiet, N_("Suppress submodule status output")), + OPT_BIT(0, "cached", &info.flags, N_("Use commit stored in the index instead of the one stored in the submodule HEAD"), OPT_CACHED), + OPT_BIT(0, "recursive", &info.flags, N_("recurse into nested submodules"), OPT_RECURSIVE), + OPT_END() + }; + + const char *const git_submodule_helper_usage[] = { + N_("git submodule status [--quiet] [--cached] [--recursive] [...]"), + NULL + }; + + argc = parse_options(argc, argv, prefix, module_status_options, + git_submodule_helper_usage, 0); + + if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) + return 1; + + info.prefix = prefix; + if (quiet) + info.flags |= OPT_QUIET; + + for_each_listed_submodule(&list, status_submodule_cb, &info); return 0; } @@@ -1382,7 -1144,7 +1382,7 @@@ static int push_check(int argc, const c argv++; argc--; /* Get the submodule's head ref and determine if it is detached */ - head = resolve_refdup("HEAD", 0, head_oid.hash, NULL); + head = resolve_refdup("HEAD", 0, &head_oid, NULL); if (!head) die(_("Failed to resolve HEAD as a valid ref.")); if (!strcmp(head, "HEAD")) @@@ -1480,6 -1242,29 +1480,29 @@@ static int is_active(int argc, const ch return !is_submodule_active(the_repository, argv[1]); } + /* + * Exit non-zero if any of the submodule names given on the command line is + * invalid. If no names are given, filter stdin to print only valid names + * (which is primarily intended for testing). + */ + static int check_name(int argc, const char **argv, const char *prefix) + { + if (argc > 1) { + while (*++argv) { + if (check_submodule_name(*argv) < 0) + return 1; + } + } else { + struct strbuf buf = STRBUF_INIT; + while (strbuf_getline(&buf, stdin) != EOF) { + if (!check_submodule_name(buf.buf)) + printf("%s\n", buf.buf); + } + strbuf_release(&buf); + } + return 0; + } + #define SUPPORT_SUPER_PREFIX (1<<0) struct cmd_struct { @@@ -1497,11 -1282,11 +1520,12 @@@ static struct cmd_struct commands[] = {"resolve-relative-url", resolve_relative_url, 0}, {"resolve-relative-url-test", resolve_relative_url_test, 0}, {"init", module_init, SUPPORT_SUPER_PREFIX}, + {"status", module_status, SUPPORT_SUPER_PREFIX}, {"remote-branch", resolve_remote_submodule_branch, 0}, {"push-check", push_check, 0}, {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX}, {"is-active", is_active, 0}, + {"check-name", check_name, 0}, }; int cmd_submodule__helper(int argc, const char **argv, const char *prefix) diff --combined builtin/update-index.c index 58d1c2d282,a9fde000d4..52eaebae91 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@@ -16,7 -16,6 +16,7 @@@ #include "pathspec.h" #include "dir.h" #include "split-index.h" +#include "fsmonitor.h" /* * Default to not allowing changes to the list of files. The @@@ -33,7 -32,6 +33,7 @@@ static int force_remove static int verbose; static int mark_valid_only; static int mark_skip_worktree_only; +static int mark_fsmonitor_only; #define MARK_FLAG 1 #define UNMARK_FLAG 2 static struct strbuf mtime_dir = STRBUF_INIT; @@@ -230,7 -228,6 +230,7 @@@ static int mark_ce_flags(const char *pa int namelen = strlen(path); int pos = cache_name_pos(path, namelen); if (0 <= pos) { + mark_fsmonitor_invalid(&the_index, active_cache[pos]); if (mark) active_cache[pos]->ce_flags |= flag; else @@@ -331,7 -328,7 +331,7 @@@ static int process_directory(const cha if (S_ISGITLINK(ce->ce_mode)) { /* Do nothing to the index if there is no HEAD! */ - if (resolve_gitlink_ref(path, "HEAD", oid.hash) < 0) + if (resolve_gitlink_ref(path, "HEAD", &oid) < 0) return 0; return add_one_path(ce, path, len, st); @@@ -357,17 -354,16 +357,16 @@@ } /* No match - should we add it as a gitlink? */ - if (!resolve_gitlink_ref(path, "HEAD", oid.hash)) + if (!resolve_gitlink_ref(path, "HEAD", &oid)) return add_one_path(NULL, path, len, st); /* Error out. */ return error("%s: is a directory - add files inside instead", path); } - static int process_path(const char *path) + static int process_path(const char *path, struct stat *st, int stat_errno) { int pos, len; - struct stat st; const struct cache_entry *ce; len = strlen(path); @@@ -391,13 -387,13 +390,13 @@@ * First things first: get the stat information, to decide * what to do about the pathname! */ - if (lstat(path, &st) < 0) - return process_lstat_error(path, errno); + if (stat_errno) + return process_lstat_error(path, stat_errno); - if (S_ISDIR(st.st_mode)) - return process_directory(path, len, &st); + if (S_ISDIR(st->st_mode)) + return process_directory(path, len, st); - return add_one_path(ce, path, len, &st); + return add_one_path(ce, path, len, st); } static int add_cacheinfo(unsigned int mode, const struct object_id *oid, @@@ -406,7 -402,7 +405,7 @@@ int size, len, option; struct cache_entry *ce; - if (!verify_path(path)) + if (!verify_path(path, mode)) return error("Invalid path '%s'", path); len = strlen(path); @@@ -449,7 -445,17 +448,17 @@@ static void chmod_path(char flip, cons static void update_one(const char *path) { - if (!verify_path(path)) { + int stat_errno = 0; + struct stat st; + + if (mark_valid_only || mark_skip_worktree_only || force_remove) + st.st_mode = 0; + else if (lstat(path, &st) < 0) { + st.st_mode = 0; + stat_errno = errno; + } /* else stat is valid */ + + if (!verify_path(path, st.st_mode)) { fprintf(stderr, "Ignoring path %s\n", path); return; } @@@ -463,11 -469,6 +472,11 @@@ die("Unable to mark file %s", path); return; } + if (mark_fsmonitor_only) { + if (mark_ce_flags(path, CE_FSMONITOR_VALID, mark_fsmonitor_only == MARK_FLAG)) + die("Unable to mark file %s", path); + return; + } if (force_remove) { if (remove_file_from_cache(path)) @@@ -475,7 -476,7 +484,7 @@@ report("remove '%s'", path); return; } - if (process_path(path)) + if (process_path(path, &st, stat_errno)) die("Unable to process path %s", path); report("add '%s'", path); } @@@ -545,7 -546,7 +554,7 @@@ static void read_index_info(int nul_ter path_name = uq.buf; } - if (!verify_path(path_name)) { + if (!verify_path(path_name, mode)) { fprintf(stderr, "Ignoring path %s\n", path_name); continue; } @@@ -687,9 -688,9 +696,9 @@@ static int unresolve_one(const char *pa static void read_head_pointers(void) { - if (read_ref("HEAD", head_oid.hash)) + if (read_ref("HEAD", &head_oid)) die("No HEAD -- no initial commit yet?"); - if (read_ref("MERGE_HEAD", merge_head_oid.hash)) { + if (read_ref("MERGE_HEAD", &merge_head_oid)) { fprintf(stderr, "Not in the middle of a merge.\n"); exit(0); } @@@ -729,7 -730,7 +738,7 @@@ static int do_reupdate(int ac, const ch PATHSPEC_PREFER_CWD, prefix, av + 1); - if (read_ref("HEAD", head_oid.hash)) + if (read_ref("HEAD", &head_oid)) /* If there is no HEAD, that means it is an initial * commit. Update everything in the index. */ @@@ -925,8 -926,6 +934,8 @@@ int cmd_update_index(int argc, const ch struct refresh_params refresh_args = {0, &has_errors}; int lock_error = 0; int split_index = -1; + int force_write = 0; + int fsmonitor = -1; struct lock_file lock_file = LOCK_INIT; struct parse_opt_ctx_t ctx; strbuf_getline_fn getline_fn; @@@ -1018,16 -1017,6 +1027,16 @@@ N_("test if the filesystem supports untracked cache"), UC_TEST), OPT_SET_INT(0, "force-untracked-cache", &untracked_cache, N_("enable untracked cache without testing the filesystem"), UC_FORCE), + OPT_SET_INT(0, "force-write-index", &force_write, + N_("write out the index even if is not flagged as changed"), 1), + OPT_BOOL(0, "fsmonitor", &fsmonitor, + N_("enable or disable file system monitor")), + {OPTION_SET_INT, 0, "fsmonitor-valid", &mark_fsmonitor_only, NULL, + N_("mark files as fsmonitor valid"), + PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG}, + {OPTION_SET_INT, 0, "no-fsmonitor-valid", &mark_fsmonitor_only, NULL, + N_("clear fsmonitor valid bit"), + PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG}, OPT_END() }; @@@ -1166,23 -1155,7 +1175,23 @@@ die("BUG: bad untracked_cache value: %d", untracked_cache); } - if (active_cache_changed) { + if (fsmonitor > 0) { + if (git_config_get_fsmonitor() == 0) + warning(_("core.fsmonitor is unset; " + "set it if you really want to " + "enable fsmonitor")); + add_fsmonitor(&the_index); + report(_("fsmonitor enabled")); + } else if (!fsmonitor) { + if (git_config_get_fsmonitor() == 1) + warning(_("core.fsmonitor is set; " + "remove it if you really want to " + "disable fsmonitor")); + remove_fsmonitor(&the_index); + report(_("fsmonitor disabled")); + } + + if (active_cache_changed || force_write) { if (newfd < 0) { if (refresh_args.flags & REFRESH_QUIET) exit(128); diff --combined cache.h index fd755c32cf,803bd7094a..4c29dd02e8 --- a/cache.h +++ b/cache.h @@@ -14,7 -14,6 +14,7 @@@ #include "hash.h" #include "path.h" #include "sha1-array.h" +#include "repository.h" #ifndef platform_SHA_CTX /* @@@ -78,8 -77,6 +78,8 @@@ struct object_id unsigned char hash[GIT_MAX_RAWSZ]; }; +#define the_hash_algo the_repository->hash_algo + #if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT) #define DTYPE(de) ((de)->d_type) #else @@@ -207,7 -204,6 +207,7 @@@ struct cache_entry #define CE_ADDED (1 << 19) #define CE_HASHED (1 << 20) +#define CE_FSMONITOR_VALID (1 << 21) #define CE_WT_REMOVE (1 << 22) /* remove in work directory */ #define CE_CONFLICTED (1 << 23) @@@ -331,7 -327,6 +331,7 @@@ static inline unsigned int canon_mode(u #define CACHE_TREE_CHANGED (1 << 5) #define SPLIT_INDEX_ORDERED (1 << 6) #define UNTRACKED_CHANGED (1 << 7) +#define FSMONITOR_CHANGED (1 << 8) struct split_index; struct untracked_cache; @@@ -345,14 -340,11 +345,14 @@@ struct index_state struct split_index *split_index; struct cache_time timestamp; unsigned name_hash_initialized : 1, - initialized : 1; + initialized : 1, + drop_cache_tree : 1; struct hashmap name_hash; struct hashmap dir_hash; unsigned char sha1[20]; struct untracked_cache *untracked; + uint64_t fsmonitor_last_update; + struct ewah_bitmap *fsmonitor_dirty; }; extern struct index_state the_index; @@@ -372,7 -364,7 +372,7 @@@ extern void free_name_hash(struct index #define active_cache_tree (the_index.cache_tree) #define read_cache() read_index(&the_index) -#define read_cache_from(path) read_index_from(&the_index, (path)) +#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 is_cache_unborn() is_index_unborn(&the_index) #define read_cache_unmerged() read_index_unmerged(&the_index) @@@ -454,16 -446,6 +454,16 @@@ static inline enum object_type object_t #define GIT_QUARANTINE_ENVIRONMENT "GIT_QUARANTINE_PATH" #define GIT_OPTIONAL_LOCKS_ENVIRONMENT "GIT_OPTIONAL_LOCKS" +/* + * Environment variable used in handshaking the wire protocol. + * Contains a colon ':' separated list of keys with optional values + * 'key[=value]'. Presence of unknown keys and values must be + * ignored. + */ +#define GIT_PROTOCOL_ENVIRONMENT "GIT_PROTOCOL" +/* HTTP header used to handshake the wire protocol */ +#define GIT_PROTOCOL_HEADER "Git-Protocol" + /* * This environment variable is expected to contain a boolean indicating * whether we should or should not treat: @@@ -617,45 -599,16 +617,45 @@@ extern int read_index(struct index_stat extern int read_index_preload(struct index_state *, const struct pathspec *pathspec); 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); +extern int read_index_from(struct index_state *, const char *path, + const char *gitdir); extern int is_index_unborn(struct index_state *); extern int read_index_unmerged(struct index_state *); + +/* For use with `write_locked_index()`. */ #define COMMIT_LOCK (1 << 0) -#define CLOSE_LOCK (1 << 1) + +/* + * Write the index while holding an already-taken lock. Close the lock, + * and if `COMMIT_LOCK` is given, commit it. + * + * Unless a split index is in use, write the index into the lockfile. + * + * With a split index, write the shared index to a temporary file, + * adjust its permissions and rename it into place, then write the + * split index to the lockfile. If the temporary file for the shared + * index cannot be created, fall back to the behavior described in + * the previous paragraph. + * + * With `COMMIT_LOCK`, the lock is always committed or rolled back. + * Without it, the lock is closed, but neither committed nor rolled + * back. + */ extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags); + extern int discard_index(struct index_state *); extern void move_index_extensions(struct index_state *dst, struct index_state *src); extern int unmerged_index(const struct index_state *); + +/** + * Returns 1 if the index differs from HEAD, 0 otherwise. When on an unborn + * branch, returns 1 if there are entries in the index, 0 otherwise. If an + * strbuf is provided, the space-separated list of files that differ will be + * appended to it. + */ +extern int index_has_changes(struct strbuf *sb); + - extern int verify_path(const char *path); + extern int verify_path(const char *path, unsigned mode); extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change); extern int index_dir_exists(struct index_state *istate, const char *name, int namelen); extern void adjust_dirname_case(struct index_state *istate, char *name); @@@ -728,14 -681,11 +728,14 @@@ extern void *read_blob_data_from_index( #define CE_MATCH_IGNORE_MISSING 0x08 /* enable stat refresh */ #define CE_MATCH_REFRESH 0x10 -extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int); -extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int); +/* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */ +#define CE_MATCH_IGNORE_FSMONITOR 0X20 +extern int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int); +extern int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int); #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); @@@ -766,17 -716,12 +766,17 @@@ extern void fill_stat_cache_info(struc 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 cache_entry *, unsigned int); +/* + * Opportunistically update the index but do not complain if we can't. + * The lockfile is always committed or rolled back. + */ extern void update_index_if_able(struct index_state *, struct lock_file *); extern int hold_locked_index(struct lock_file *, int); extern void set_alternate_index_output(const char *); extern int verify_index_checksum; +extern int verify_ce_order; /* Environment bits from configuration mechanism */ extern int trust_executable_bit; @@@ -830,7 -775,6 +830,7 @@@ extern int core_apply_sparse_checkout extern int precomposed_unicode; extern int protect_hfs; extern int protect_ntfs; +extern const char *core_fsmonitor; /* * Include broken refs in all ref iterations, which will @@@ -921,7 -865,6 +921,7 @@@ struct repository_format int version; int precious_objects; int is_bare; + int hash_algo; char *work_tree; struct string_list unknown_extensions; }; @@@ -1054,22 -997,22 +1054,22 @@@ extern const struct object_id empty_blo static inline int is_empty_blob_sha1(const unsigned char *sha1) { - return !hashcmp(sha1, EMPTY_BLOB_SHA1_BIN); + return !hashcmp(sha1, the_hash_algo->empty_blob->hash); } static inline int is_empty_blob_oid(const struct object_id *oid) { - return !hashcmp(oid->hash, EMPTY_BLOB_SHA1_BIN); + return !oidcmp(oid, the_hash_algo->empty_blob); } static inline int is_empty_tree_sha1(const unsigned char *sha1) { - return !hashcmp(sha1, EMPTY_TREE_SHA1_BIN); + return !hashcmp(sha1, the_hash_algo->empty_tree->hash); } static inline int is_empty_tree_oid(const struct object_id *oid) { - return !hashcmp(oid->hash, EMPTY_TREE_SHA1_BIN); + return !oidcmp(oid, the_hash_algo->empty_tree); } /* set default permissions by passing mode arguments to open(2) */ @@@ -1187,7 -1130,15 +1187,15 @@@ int normalize_path_copy(char *dst, cons int longest_ancestor_length(const char *path, struct string_list *prefixes); char *strip_path_suffix(const char *path, const char *suffix); int daemon_avoid_alias(const char *path); - extern int is_ntfs_dotgit(const char *name); + + /* + * These functions match their is_hfs_dotgit() counterparts; see utf8.h for + * details. + */ + int is_ntfs_dotgit(const char *name); + int is_ntfs_dotgitmodules(const char *name); + int is_ntfs_dotgitignore(const char *name); + int is_ntfs_dotgitattributes(const char *name); /* * Returns true iff "str" could be confused as a command-line option when @@@ -1374,13 -1325,6 +1382,13 @@@ extern int set_disambiguate_hint_config extern int get_sha1_hex(const char *hex, unsigned char *sha1); extern int get_oid_hex(const char *hex, struct object_id *sha1); +/* + * Read `len` pairs of hexadecimal digits from `hex` and write the + * values to `binary` as `len` bytes. Return 0 on success, or -1 if + * the input does not consist of hex digits). + */ +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, * and writes the NUL-terminated output to the buffer `out`, which must be at @@@ -1502,7 -1446,6 +1510,7 @@@ extern const char *ident_default_name(v extern const char *ident_default_email(void); extern const char *git_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 *); extern void reset_ident_date(void); @@@ -1984,10 -1927,4 +1992,10 @@@ void sleep_millisec(int millisec) */ void safe_create_dir(const char *dir, int share); +/* + * Should we print an ellipsis after an abbreviated SHA-1 value + * when doing diff-raw output or indicating a detached HEAD? + */ +extern int print_sha1_ellipsis(void); + #endif /* CACHE_H */ diff --combined git-compat-util.h index 68b2ad531e,0d270ffc27..fc87cbb090 --- a/git-compat-util.h +++ b/git-compat-util.h @@@ -484,29 -484,6 +484,29 @@@ static inline int skip_prefix(const cha return 0; } +/* + * If the string "str" is the same as the string in "prefix", then the "arg" + * parameter is set to the "def" parameter and 1 is returned. + * If the string "str" begins with the string found in "prefix" and then a + * "=" sign, then the "arg" parameter is set to "str + strlen(prefix) + 1" + * (i.e., to the point in the string right after the prefix and the "=" sign), + * and 1 is returned. + * + * Otherwise, return 0 and leave "arg" untouched. + * + * When we accept both a "--key" and a "--key=" option, this function + * can be used instead of !strcmp(arg, "--key") and then + * skip_prefix(arg, "--key=", &arg) to parse such an option. + */ +int skip_to_optional_arg_default(const char *str, const char *prefix, + const char **arg, const char *def); + +static inline int skip_to_optional_arg(const char *str, const char *prefix, + const char **arg) +{ + return skip_to_optional_arg_default(str, prefix, arg, ""); +} + /* * Like skip_prefix, but promises never to read past "len" bytes of the input * buffer, and returns the remaining number of bytes in "out" via "outlen". @@@ -1001,6 -978,23 +1001,23 @@@ static inline int sane_iscase(int x, in return (x & 0x20) == 0; } + /* + * Like skip_prefix, but compare case-insensitively. Note that the comparison + * is done via tolower(), so it is strictly ASCII (no multi-byte characters or + * locale-specific conversions). + */ + static inline int skip_iprefix(const char *str, const char *prefix, + const char **out) + { + do { + if (!*prefix) { + *out = str; + return 1; + } + } while (tolower(*str++) == tolower(*prefix++)); + return 0; + } + static inline int strtoul_ui(char const *s, int base, unsigned int *result) { unsigned long ul; diff --combined git-submodule.sh index 156255a9e5,8f260fbd9c..f2e5bea715 --- a/git-submodule.sh +++ b/git-submodule.sh @@@ -229,6 -229,11 +229,11 @@@ Use -f if you really want to add it." > sm_name="$sm_path" fi + if ! git submodule--helper check-name "$sm_name" + then + die "$(eval_gettext "'$sm_name' is not a valid submodule name")" + fi + # perhaps the path exists and is already a git repo, else clone it if test -e "$sm_path" then @@@ -758,6 -763,18 +763,6 @@@ cmd_update( } } -set_name_rev () { - revname=$( ( - sanitize_submodule_env - cd "$1" && { - git describe "$2" 2>/dev/null || - git describe --tags "$2" 2>/dev/null || - git describe --contains "$2" 2>/dev/null || - git describe --all --always "$2" - } - ) ) - test -z "$revname" || revname=" ($revname)" -} # # Show commit summary for submodules in index or working tree # @@@ -1004,7 -1021,54 +1009,7 @@@ cmd_status( shift done - { - git submodule--helper list --prefix "$wt_prefix" "$@" || - echo "#unmatched" $? - } | - while read -r mode sha1 stage sm_path - do - die_if_unmatched "$mode" "$sha1" - name=$(git submodule--helper name "$sm_path") || exit - displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix") - if test "$stage" = U - then - say "U$sha1 $displaypath" - continue - fi - if ! git submodule--helper is-active "$sm_path" || - { - ! test -d "$sm_path"/.git && - ! test -f "$sm_path"/.git - } - then - say "-$sha1 $displaypath" - continue; - fi - if git diff-files --ignore-submodules=dirty --quiet -- "$sm_path" - then - set_name_rev "$sm_path" "$sha1" - say " $sha1 $displaypath$revname" - else - if test -z "$cached" - then - sha1=$(sanitize_submodule_env; cd "$sm_path" && git rev-parse --verify HEAD) - fi - set_name_rev "$sm_path" "$sha1" - say "+$sha1 $displaypath$revname" - fi - - if test -n "$recursive" - then - ( - prefix="$displaypath/" - sanitize_submodule_env - wt_prefix= - cd "$sm_path" && - eval cmd_status - ) || - die "$(eval_gettext "Failed to recurse into submodule path '\$sm_path'")" - fi - done + git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper status ${GIT_QUIET:+--quiet} ${cached:+--cached} ${recursive:+--recursive} "$@" } # # Sync remote urls for submodules diff --combined read-cache.c index 198e72b685,8cef7b6715..0dfc7269dc --- a/read-cache.c +++ b/read-cache.c @@@ -19,7 -19,6 +19,7 @@@ #include "varint.h" #include "split-index.h" #include "utf8.h" +#include "fsmonitor.h" /* Mask for the name length in ce_flags in the on-disk index */ @@@ -39,12 -38,11 +39,12 @@@ #define CACHE_EXT_RESOLVE_UNDO 0x52455543 /* "REUC" */ #define CACHE_EXT_LINK 0x6c696e6b /* "link" */ #define CACHE_EXT_UNTRACKED 0x554E5452 /* "UNTR" */ +#define CACHE_EXT_FSMONITOR 0x46534D4E /* "FSMN" */ /* changes that can be kept in $GIT_DIR/index (basically all extensions) */ #define EXTMASK (RESOLVE_UNDO_CHANGED | CACHE_TREE_CHANGED | \ CE_ENTRY_ADDED | CE_ENTRY_REMOVED | CE_ENTRY_CHANGED | \ - SPLIT_INDEX_ORDERED | UNTRACKED_CHANGED) + SPLIT_INDEX_ORDERED | UNTRACKED_CHANGED | FSMONITOR_CHANGED) struct index_state the_index; static const char *alternate_index_output; @@@ -64,7 -62,6 +64,7 @@@ static void replace_index_entry(struct free(old); set_index_entry(istate, nr, ce); ce->ce_flags |= CE_UPDATE_IN_BASE; + mark_fsmonitor_invalid(istate, ce); istate->cache_changed |= CE_ENTRY_CHANGED; } @@@ -153,10 -150,8 +153,10 @@@ void fill_stat_cache_info(struct cache_ if (assume_unchanged) ce->ce_flags |= CE_VALID; - if (S_ISREG(st->st_mode)) + if (S_ISREG(st->st_mode)) { ce_mark_uptodate(ce); + mark_fsmonitor_valid(ce); + } } static int ce_compare_data(const struct cache_entry *ce, struct stat *st) @@@ -196,7 -191,7 +196,7 @@@ static int ce_compare_link(const struc static int ce_compare_gitlink(const struct cache_entry *ce) { - unsigned char sha1[20]; + struct object_id oid; /* * We don't actually require that the .git directory @@@ -206,9 -201,9 +206,9 @@@ * * If so, we consider it always to match. */ - if (resolve_gitlink_ref(ce->name, "HEAD", sha1) < 0) + if (resolve_gitlink_ref(ce->name, "HEAD", &oid) < 0) return 0; - return hashcmp(sha1, ce->oid.hash); + return oidcmp(&oid, &ce->oid); } static int ce_modified_check_fs(const struct cache_entry *ce, struct stat *st) @@@ -306,7 -301,7 +306,7 @@@ int match_stat_data_racy(const struct i return match_stat_data(sd, st); } -int ie_match_stat(const struct index_state *istate, +int ie_match_stat(struct index_state *istate, const struct cache_entry *ce, struct stat *st, unsigned int options) { @@@ -314,10 -309,7 +314,10 @@@ int ignore_valid = options & CE_MATCH_IGNORE_VALID; int ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE; int assume_racy_is_modified = options & CE_MATCH_RACY_IS_DIRTY; + int ignore_fsmonitor = options & CE_MATCH_IGNORE_FSMONITOR; + if (!ignore_fsmonitor) + refresh_fsmonitor(istate); /* * If it's marked as always valid in the index, it's * valid whatever the checked-out copy says. @@@ -328,8 -320,6 +328,8 @@@ return 0; if (!ignore_valid && (ce->ce_flags & CE_VALID)) return 0; + if (!ignore_fsmonitor && (ce->ce_flags & CE_FSMONITOR_VALID)) + return 0; /* * Intent-to-add entries have not been added, so the index entry @@@ -367,7 -357,7 +367,7 @@@ return changed; } -int ie_modified(const struct index_state *istate, +int ie_modified(struct index_state *istate, const struct cache_entry *ce, struct stat *st, unsigned int options) { @@@ -641,17 -631,13 +641,17 @@@ int add_to_index(struct index_state *is { int size, namelen, was_same; mode_t st_mode = st->st_mode; - struct cache_entry *ce, *alias; + struct cache_entry *ce, *alias = NULL; unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE|CE_MATCH_RACY_IS_DIRTY; int verbose = flags & (ADD_CACHE_VERBOSE | ADD_CACHE_PRETEND); int pretend = flags & ADD_CACHE_PRETEND; 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; + + if (flags & HASH_RENORMALIZE) + newflags |= 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); @@@ -692,23 -678,19 +692,23 @@@ if (ignore_case) { adjust_dirname_case(istate, ce->name); } + if (!(flags & HASH_RENORMALIZE)) { + alias = index_file_exists(istate, ce->name, + ce_namelen(ce), ignore_case); + if (alias && + !ce_stage(alias) && + !ie_match_stat(istate, alias, st, ce_option)) { + /* Nothing changed, really */ + if (!S_ISGITLINK(alias->ce_mode)) + ce_mark_uptodate(alias); + alias->ce_flags |= CE_ADDED; - alias = index_file_exists(istate, ce->name, ce_namelen(ce), ignore_case); - if (alias && !ce_stage(alias) && !ie_match_stat(istate, alias, st, ce_option)) { - /* Nothing changed, really */ - if (!S_ISGITLINK(alias->ce_mode)) - ce_mark_uptodate(alias); - alias->ce_flags |= CE_ADDED; - - free(ce); - return 0; + free(ce); + return 0; + } } if (!intent_only) { - if (index_path(&ce->oid, path, st, HASH_WRITE_OBJECT)) { + if (index_path(&ce->oid, path, st, newflags)) { free(ce); return error("unable to index file %s", path); } @@@ -751,7 -733,7 +751,7 @@@ struct cache_entry *make_cache_entry(un int size, len; struct cache_entry *ce, *ret; - if (!verify_path(path)) { + if (!verify_path(path, mode)) { error("Invalid path '%s'", path); return NULL; } @@@ -796,7 -778,6 +796,7 @@@ int chmod_index_entry(struct index_stat } cache_tree_invalidate_path(istate, ce->name); ce->ce_flags |= CE_UPDATE_IN_BASE; + mark_fsmonitor_invalid(istate, ce); istate->cache_changed |= CE_ENTRY_CHANGED; return 0; @@@ -816,7 -797,7 +816,7 @@@ int ce_same_name(const struct cache_ent * Also, we don't want double slashes or slashes at the * end that can make pathnames ambiguous. */ - static int verify_dotfile(const char *rest) + static int verify_dotfile(const char *rest, unsigned mode) { /* * The first character was '.', but that @@@ -830,8 -811,13 +830,13 @@@ switch (*rest) { /* - * ".git" followed by NUL or slash is bad. This - * shares the path end test with the ".." case. + * ".git" followed by NUL or slash is bad. Note that we match + * case-insensitively here, even if ignore_case is not set. + * This outlaws ".GIT" everywhere out of an abundance of caution, + * since there's really no good reason to allow it. + * + * Once we've seen ".git", we can also find ".gitmodules", etc (also + * case-insensitively). */ case 'g': case 'G': @@@ -839,8 -825,15 +844,15 @@@ break; if (rest[2] != 't' && rest[2] != 'T') break; - rest += 2; - /* fallthrough */ + if (rest[3] == '\0' || is_dir_sep(rest[3])) + return 0; + if (S_ISLNK(mode)) { + rest += 3; + if (skip_iprefix(rest, "modules", &rest) && + (*rest == '\0' || is_dir_sep(*rest))) + return 0; + } + break; case '.': if (rest[1] == '\0' || is_dir_sep(rest[1])) return 0; @@@ -848,7 -841,7 +860,7 @@@ return 1; } - int verify_path(const char *path) + int verify_path(const char *path, unsigned mode) { char c; @@@ -861,12 -854,25 +873,25 @@@ return 1; if (is_dir_sep(c)) { inside: - if (protect_hfs && is_hfs_dotgit(path)) - return 0; - if (protect_ntfs && is_ntfs_dotgit(path)) - return 0; + if (protect_hfs) { + if (is_hfs_dotgit(path)) + return 0; + if (S_ISLNK(mode)) { + if (is_hfs_dotgitmodules(path)) + return 0; + } + } + if (protect_ntfs) { + if (is_ntfs_dotgit(path)) + return 0; + if (S_ISLNK(mode)) { + if (is_ntfs_dotgitmodules(path)) + return 0; + } + } + c = *path++; - if ((c == '.' && !verify_dotfile(path)) || + if ((c == '.' && !verify_dotfile(path, mode)) || is_dir_sep(c) || c == '\0') return 0; } @@@ -1183,7 -1189,7 +1208,7 @@@ static int add_index_entry_with_check(s if (!ok_to_add) return -1; - if (!verify_path(ce->name)) + if (!verify_path(ce->name, ce->ce_mode)) return error("Invalid path '%s'", ce->name); if (!skip_df_check && @@@ -1248,13 -1254,10 +1273,13 @@@ static struct cache_entry *refresh_cach int ignore_valid = options & CE_MATCH_IGNORE_VALID; int ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE; int ignore_missing = options & CE_MATCH_IGNORE_MISSING; + int ignore_fsmonitor = options & CE_MATCH_IGNORE_FSMONITOR; if (!refresh || ce_uptodate(ce)) return ce; + if (!ignore_fsmonitor) + refresh_fsmonitor(istate); /* * CE_VALID or CE_SKIP_WORKTREE means the user promised us * that the change to the work tree does not matter and told @@@ -1268,10 -1271,6 +1293,10 @@@ ce_mark_uptodate(ce); return ce; } + if (!ignore_fsmonitor && (ce->ce_flags & CE_FSMONITOR_VALID)) { + ce_mark_uptodate(ce); + return ce; + } if (has_symlink_leading_path(ce->name, ce_namelen(ce))) { if (ignore_missing) @@@ -1309,10 -1308,8 +1334,10 @@@ * because CE_UPTODATE flag is in-core only; * we are not going to write this change out. */ - if (!S_ISGITLINK(ce->ce_mode)) + if (!S_ISGITLINK(ce->ce_mode)) { ce_mark_uptodate(ce); + mark_fsmonitor_valid(ce); + } return ce; } } @@@ -1420,7 -1417,6 +1445,7 @@@ int refresh_index(struct index_state *i */ ce->ce_flags &= ~CE_VALID; ce->ce_flags |= CE_UPDATE_IN_BASE; + mark_fsmonitor_invalid(istate, ce); istate->cache_changed |= CE_ENTRY_CHANGED; } if (quiet) @@@ -1540,9 -1536,6 +1565,9 @@@ struct ondisk_cache_entry_extended /* Allow fsck to force verification of the index checksum. */ 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) { git_SHA_CTX c; @@@ -1583,9 -1576,6 +1608,9 @@@ static int read_index_extension(struct case CACHE_EXT_UNTRACKED: istate->untracked = read_untracked_extension(data, sz); break; + case CACHE_EXT_FSMONITOR: + read_fsmonitor_extension(istate, data, sz); + break; default: if (*ext < 'A' || 'Z' < *ext) return error("index uses %.4s extension, which we do not understand", @@@ -1603,7 -1593,7 +1628,7 @@@ int hold_locked_index(struct lock_file int read_index(struct index_state *istate) { - return read_index_from(istate, get_index_file()); + return read_index_from(istate, get_index_file(), get_git_dir()); } static struct cache_entry *cache_entry_from_ondisk(struct ondisk_cache_entry *ondisk, @@@ -1703,9 -1693,6 +1728,9 @@@ static void check_ce_order(struct index { unsigned int i; + if (!verify_ce_order) + return; + for (i = 1; i < istate->cache_nr; i++) { struct cache_entry *ce = istate->cache[i - 1]; struct cache_entry *next_ce = istate->cache[i]; @@@ -1761,7 -1748,6 +1786,7 @@@ static void post_read_index_from(struc check_ce_order(istate); tweak_untracked_cache(istate); tweak_split_index(istate); + tweak_fsmonitor(istate); } /* remember to discard_cache() before reading a different cache! */ @@@ -1863,19 -1849,20 +1888,19 @@@ unmap * This way, shared index can be removed if they have not been used * for some time. */ -static void freshen_shared_index(char *base_sha1_hex, int warn) +static void freshen_shared_index(const char *shared_index, int warn) { - char *shared_index = git_pathdup("sharedindex.%s", base_sha1_hex); if (!check_and_freshen_file(shared_index, 1) && warn) warning("could not freshen shared index '%s'", shared_index); - free(shared_index); } -int read_index_from(struct index_state *istate, const char *path) +int read_index_from(struct index_state *istate, const char *path, + const char *gitdir) { struct split_index *split_index; int ret; char *base_sha1_hex; - const char *base_path; + char *base_path; /* istate->initialized covers both .git/index and .git/sharedindex.xxx */ if (istate->initialized) @@@ -1895,17 -1882,16 +1920,17 @@@ split_index->base = xcalloc(1, sizeof(*split_index->base)); base_sha1_hex = sha1_to_hex(split_index->base_sha1); - base_path = git_path("sharedindex.%s", base_sha1_hex); + base_path = xstrfmt("%s/sharedindex.%s", gitdir, base_sha1_hex); ret = do_read_index(split_index->base, base_path, 1); if (hashcmp(split_index->base_sha1, split_index->base->sha1)) die("broken index, expect %s in %s, got %s", base_sha1_hex, base_path, sha1_to_hex(split_index->base->sha1)); - freshen_shared_index(base_sha1_hex, 0); + freshen_shared_index(base_path, 0); merge_base_index(istate); post_read_index_from(istate); + free(base_path); return ret; } @@@ -2215,22 -2201,17 +2240,22 @@@ static int has_racy_timestamp(struct in return 0; } -/* - * Opportunistically update the index but do not complain if we can't - */ void update_index_if_able(struct index_state *istate, struct lock_file *lockfile) { if ((istate->cache_changed || has_racy_timestamp(istate)) && - verify_index(istate) && - write_locked_index(istate, lockfile, COMMIT_LOCK)) + verify_index(istate)) + write_locked_index(istate, lockfile, COMMIT_LOCK); + else rollback_lock_file(lockfile); } +/* + * On success, `tempfile` is closed. If it is the temporary file + * of a `struct lock_file`, we will therefore effectively perform + * a 'close_lock_file_gently()`. Since that is an implementation + * detail of lockfiles, callers of `do_write_index()` should not + * rely on it. + */ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, int strip_extensions) { @@@ -2243,7 -2224,7 +2268,7 @@@ struct stat st; struct ondisk_cache_entry_extended ondisk; struct strbuf previous_name_buf = STRBUF_INIT, *previous_name; - int drop_cache_tree = 0; + int drop_cache_tree = istate->drop_cache_tree; for (i = removed = extended = 0; i < entries; i++) { if (cache[i]->ce_flags & CE_REMOVE) @@@ -2353,21 -2334,12 +2378,21 @@@ if (err) return -1; } + if (!strip_extensions && istate->fsmonitor_last_update) { + struct strbuf sb = STRBUF_INIT; + + write_fsmonitor_extension(&sb, istate); + err = write_index_ext_header(&c, newfd, CACHE_EXT_FSMONITOR, sb.len) < 0 + || ce_write(&c, newfd, sb.buf, sb.len) < 0; + strbuf_release(&sb); + if (err) + return -1; + } if (ce_flush(&c, newfd, istate->sha1)) return -1; if (close_tempfile_gently(tempfile)) { error(_("could not close '%s'"), tempfile->filename.buf); - delete_tempfile(&tempfile); return -1; } if (stat(tempfile->filename.buf, &st)) @@@ -2396,9 -2368,14 +2421,9 @@@ static int do_write_locked_index(struc int ret = do_write_index(istate, lock->tempfile, 0); if (ret) return ret; - assert((flags & (COMMIT_LOCK | CLOSE_LOCK)) != - (COMMIT_LOCK | CLOSE_LOCK)); if (flags & COMMIT_LOCK) return commit_locked_index(lock); - else if (flags & CLOSE_LOCK) - return close_lock_file_gently(lock); - else - return ret; + return close_lock_file_gently(lock); } static int write_split_index(struct index_state *istate, @@@ -2472,21 -2449,32 +2497,21 @@@ static int clean_shared_index_files(con } static int write_shared_index(struct index_state *istate, - struct lock_file *lock, unsigned flags) + struct tempfile **temp) { - struct tempfile *temp; struct split_index *si = istate->split_index; int ret; - temp = mks_tempfile(git_path("sharedindex_XXXXXX")); - if (!temp) { - hashclr(si->base_sha1); - return do_write_locked_index(istate, lock, flags); - } move_cache_to_base_index(istate); - ret = do_write_index(si->base, temp, 1); - if (ret) { - delete_tempfile(&temp); + ret = do_write_index(si->base, *temp, 1); + if (ret) return ret; - } - ret = adjust_shared_perm(get_tempfile_path(temp)); + ret = adjust_shared_perm(get_tempfile_path(*temp)); if (ret) { - int save_errno = errno; - error("cannot fix permission bits on %s", get_tempfile_path(temp)); - delete_tempfile(&temp); - errno = save_errno; + error("cannot fix permission bits on %s", get_tempfile_path(*temp)); return ret; } - ret = rename_tempfile(&temp, + ret = rename_tempfile(temp, git_path("sharedindex.%s", sha1_to_hex(si->base->sha1))); if (!ret) { hashcpy(si->base_sha1, si->base->sha1); @@@ -2532,15 -2520,11 +2557,15 @@@ int write_locked_index(struct index_sta int new_shared_index, ret; struct split_index *si = istate->split_index; + if (istate->fsmonitor_last_update) + fill_fsmonitor_bitmap(istate); + if (!si || alternate_index_output || (istate->cache_changed & ~EXTMASK)) { if (si) hashclr(si->base_sha1); - return do_write_locked_index(istate, lock, flags); + ret = do_write_locked_index(istate, lock, flags); + goto out; } if (getenv("GIT_TEST_SPLIT_INDEX")) { @@@ -2554,38 -2538,17 +2579,38 @@@ new_shared_index = istate->cache_changed & SPLIT_INDEX_ORDERED; if (new_shared_index) { - ret = write_shared_index(istate, lock, flags); + struct tempfile *temp; + int saved_errno; + + temp = mks_tempfile(git_path("sharedindex_XXXXXX")); + if (!temp) { + hashclr(si->base_sha1); + ret = do_write_locked_index(istate, lock, flags); + goto out; + } + ret = write_shared_index(istate, &temp); + + saved_errno = errno; + if (is_tempfile_active(temp)) + delete_tempfile(&temp); + errno = saved_errno; + if (ret) - return ret; + goto out; } ret = write_split_index(istate, lock, flags); /* Freshen the shared index only if the split-index was written */ - if (!ret && !new_shared_index) - freshen_shared_index(sha1_to_hex(si->base_sha1), 1); + if (!ret && !new_shared_index) { + const char *shared_index = git_path("sharedindex.%s", + sha1_to_hex(si->base_sha1)); + freshen_shared_index(shared_index, 1); + } +out: + if (flags & COMMIT_LOCK) + rollback_lock_file(lock); return ret; } diff --combined submodule-config.c index 602ba8ca8b,aa02641767..e5f4901212 --- a/submodule-config.c +++ b/submodule-config.c @@@ -9,7 -9,7 +9,7 @@@ /* * submodule cache lookup structure * There is one shared set of 'struct submodule' entries which can be - * looked up by their sha1 blob id of the .gitmodule file and either + * looked up by their sha1 blob id of the .gitmodules file and either * using path or name as key. * for_path stores submodule entries with path as key * for_name stores submodule entries with name as key @@@ -91,7 -91,7 +91,7 @@@ static void submodule_cache_clear(struc /* * We iterate over the name hash here to be symmetric with the * allocation of struct submodule entries. Each is allocated by - * their .gitmodule blob sha1 and submodule name. + * their .gitmodules blob sha1 and submodule name. */ hashmap_iter_init(&cache->for_name, &iter); while ((entry = hashmap_iter_next(&iter))) @@@ -190,6 -190,31 +190,31 @@@ static struct submodule *cache_lookup_n return NULL; } + int check_submodule_name(const char *name) + { + /* Disallow empty names */ + if (!*name) + return -1; + + /* + * Look for '..' as a path component. Check both '/' and '\\' as + * separators rather than is_dir_sep(), because we want the name rules + * to be consistent across platforms. + */ + goto in_component; /* always start inside component */ + while (*name) { + char c = *name++; + if (c == '/' || c == '\\') { + in_component: + if (name[0] == '.' && name[1] == '.' && + (!name[2] || name[2] == '/' || name[2] == '\\')) + return -1; + } + } + + return 0; + } + static int name_and_item_from_var(const char *var, struct strbuf *name, struct strbuf *item) { @@@ -201,6 -226,12 +226,12 @@@ return 0; strbuf_add(name, subsection, subsection_len); + if (check_submodule_name(name->buf) < 0) { + warning(_("ignoring suspicious submodule name: %s"), name->buf); + strbuf_release(name); + return 0; + } + strbuf_addstr(item, key); return 1; diff --combined submodule-config.h index a5503a5d17,93880cf021..17e2970223 --- a/submodule-config.h +++ b/submodule-config.h @@@ -22,9 -22,6 +22,9 @@@ struct submodule int recommend_shallow; }; +#define SUBMODULE_INIT { NULL, NULL, NULL, RECURSE_SUBMODULES_NONE, \ + NULL, NULL, SUBMODULE_UPDATE_STRATEGY_INIT, {0}, -1 }; + struct submodule_cache; struct repository; @@@ -48,4 -45,11 +48,11 @@@ extern const struct submodule *submodul const char *key); extern void submodule_free(void); + /* + * Returns 0 if the name is syntactically acceptable as a submodule "name" + * (e.g., that may be found in the subsection of a .gitmodules file) and -1 + * otherwise. + */ + int check_submodule_name(const char *name); + #endif /* SUBMODULE_CONFIG_H */