From: Junio C Hamano Date: Fri, 7 Mar 2014 23:14:01 +0000 (-0800) Subject: Merge branch 'jn/add-2.0-u-A-sans-pathspec' X-Git-Tag: v2.0.0-rc0~141 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/053a6b18074e12876d60da952d8a668631b21a64?ds=inline;hp=-c Merge branch 'jn/add-2.0-u-A-sans-pathspec' "git add -u" and "git add -A" without any pathspec is a tree-wide operation now, even when they are run in a subdirectory of the working tree. --- 053a6b18074e12876d60da952d8a668631b21a64 diff --combined builtin/add.c index 672adc01ff,61f3cb49ff..de79f35efc --- a/builtin/add.c +++ b/builtin/add.c @@@ -26,55 -26,10 +26,10 @@@ static int take_worktree_changes struct update_callback_data { int flags; int add_errors; - const char *implicit_dot; - size_t implicit_dot_len; - /* only needed for 2.0 transition preparation */ int warn_add_would_remove; }; - static const char *option_with_implicit_dot; - static const char *short_option_with_implicit_dot; - - static void warn_pathless_add(void) - { - static int shown; - assert(option_with_implicit_dot && short_option_with_implicit_dot); - - if (shown) - return; - shown = 1; - - /* - * To be consistent with "git add -p" and most Git - * commands, we should default to being tree-wide, but - * this is not the original behavior and can't be - * changed until users trained themselves not to type - * "git add -u" or "git add -A". For now, we warn and - * keep the old behavior. Later, the behavior can be changed - * to tree-wide, keeping the warning for a while, and - * eventually we can drop the warning. - */ - warning(_("The behavior of 'git add %s (or %s)' with no path argument from a\n" - "subdirectory of the tree will change in Git 2.0 and should not be used anymore.\n" - "To add content for the whole tree, run:\n" - "\n" - " git add %s :/\n" - " (or git add %s :/)\n" - "\n" - "To restrict the command to the current directory, run:\n" - "\n" - " git add %s .\n" - " (or git add %s .)\n" - "\n" - "With the current Git version, the command is restricted to " - "the current directory.\n" - ""), - option_with_implicit_dot, short_option_with_implicit_dot, - option_with_implicit_dot, short_option_with_implicit_dot, - option_with_implicit_dot, short_option_with_implicit_dot); - } - static int fix_unmerged_status(struct diff_filepair *p, struct update_callback_data *data) { @@@ -119,26 -74,10 +74,10 @@@ static void update_callback(struct diff { int i; struct update_callback_data *data = cbdata; - const char *implicit_dot = data->implicit_dot; - size_t implicit_dot_len = data->implicit_dot_len; for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; const char *path = p->one->path; - /* - * Check if "git add -A" or "git add -u" was run from a - * subdirectory with a modified file outside that directory, - * and warn if so. - * - * "git add -u" will behave like "git add -u :/" instead of - * "git add -u ." in the future. This warning prepares for - * that change. - */ - if (implicit_dot && - strncmp_icase(path, implicit_dot, implicit_dot_len)) { - warn_pathless_add(); - continue; - } switch (fix_unmerged_status(p, data)) { default: die(_("unexpected diff status %c"), p->status); @@@ -166,16 -105,14 +105,16 @@@ } } -static void update_files_in_cache(const char *prefix, const char **pathspec, +static void update_files_in_cache(const char *prefix, + const struct pathspec *pathspec, struct update_callback_data *data) { struct rev_info rev; init_revisions(&rev, prefix); setup_revisions(0, NULL, &rev, NULL); - init_pathspec(&rev.prune_data, pathspec); + if (pathspec) + copy_pathspec(&rev.prune_data, pathspec); rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; rev.diffopt.format_callback = update_callback; rev.diffopt.format_callback_data = data; @@@ -183,8 -120,7 +122,8 @@@ run_diff_files(&rev, DIFF_RACY_IS_MODIFIED); } -int add_files_to_cache(const char *prefix, const char **pathspec, int flags) +int add_files_to_cache(const char *prefix, + const struct pathspec *pathspec, int flags) { struct update_callback_data data; @@@ -194,61 -130,91 +133,49 @@@ return !!data.add_errors; } - #define WARN_IMPLICIT_DOT (1u << 0) - static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec, - int prefix, unsigned flag) -static char *prune_directory(struct dir_struct *dir, const char **pathspec, int prefix) ++static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec, int prefix) { char *seen; - int i, specs; + int i; struct dir_entry **src, **dst; - for (specs = 0; pathspec[specs]; specs++) - /* nothing */; - seen = xcalloc(specs, 1); + seen = xcalloc(pathspec->nr, 1); src = dst = dir->entries; i = dir->nr; while (--i >= 0) { struct dir_entry *entry = *src++; - if (match_pathspec(pathspec, entry->name, entry->len, - prefix, seen)) + if (dir_path_match(entry, pathspec, prefix, seen)) *dst++ = entry; - else if (flag & WARN_IMPLICIT_DOT) - /* - * "git add -A" was run from a subdirectory with a - * new file outside that directory. - * - * "git add -A" will behave like "git add -A :/" - * instead of "git add -A ." in the future. - * Warn about the coming behavior change. - */ - warn_pathless_add(); } dir->nr = dst - dir->entries; - add_pathspec_matches_against_index(pathspec, seen, specs); + add_pathspec_matches_against_index(pathspec, seen); return seen; } -/* - * Checks the index to see whether any path in pathspec refers to - * something inside a submodule. If so, dies with an error message. - */ -static void treat_gitlinks(const char **pathspec) -{ - int i; - - if (!pathspec || !*pathspec) - return; - - for (i = 0; pathspec[i]; i++) - pathspec[i] = check_path_for_gitlink(pathspec[i]); -} - -static void refresh(int verbose, const char **pathspec) +static void refresh(int verbose, const struct pathspec *pathspec) { char *seen; - int i, specs; + int i; - for (specs = 0; pathspec[specs]; specs++) - /* nothing */; - seen = xcalloc(specs, 1); + seen = xcalloc(pathspec->nr, 1); refresh_index(&the_index, verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET, pathspec, seen, _("Unstaged changes after refreshing the index:")); - for (i = 0; i < specs; i++) { + for (i = 0; i < pathspec->nr; i++) { if (!seen[i]) - die(_("pathspec '%s' did not match any files"), pathspec[i]); + die(_("pathspec '%s' did not match any files"), + pathspec->items[i].match); } free(seen); } -/* - * Normalizes argv relative to prefix, via get_pathspec(), and then - * runs die_if_path_beyond_symlink() on each path in the normalized - * list. - */ -static const char **validate_pathspec(const char **argv, const char *prefix) -{ - const char **pathspec = get_pathspec(prefix, argv); - - if (pathspec) { - const char **p; - for (p = pathspec; *p; p++) { - die_if_path_beyond_symlink(*p, prefix); - } - } - - return pathspec; -} - int run_add_interactive(const char *revision, const char *patch_mode, - const char **pathspec) + const struct pathspec *pathspec) { - int status, ac, pc = 0; + int status, ac, i; const char **args; - if (pathspec) - while (pathspec[pc]) - pc++; - - args = xcalloc(sizeof(const char *), (pc + 5)); + args = xcalloc(sizeof(const char *), (pathspec->nr + 6)); ac = 0; args[ac++] = "add--interactive"; if (patch_mode) @@@ -256,9 -222,11 +183,9 @@@ if (revision) args[ac++] = revision; args[ac++] = "--"; - if (pc) { - memcpy(&(args[ac]), pathspec, sizeof(const char *) * pc); - ac += pc; - } - args[ac] = NULL; + for (i = 0; i < pathspec->nr; i++) + /* pass original pathspec, to be re-parsed */ + args[ac++] = pathspec->items[i].original; status = run_command_v_opt(args, RUN_GIT_CMD); free(args); @@@ -267,17 -235,17 +194,17 @@@ int interactive_add(int argc, const char **argv, const char *prefix, int patch) { - const char **pathspec = NULL; + struct pathspec pathspec; - if (argc) { - pathspec = validate_pathspec(argv, prefix); - if (!pathspec) - return -1; - } + parse_pathspec(&pathspec, 0, + PATHSPEC_PREFER_FULL | + PATHSPEC_SYMLINK_LEADING_PATH | + PATHSPEC_PREFIX_ORIGIN, + prefix, argv); return run_add_interactive(NULL, patch ? "--patch" : NULL, - pathspec); + &pathspec); } static int edit_patch(int argc, const char **argv, const char *prefix) @@@ -295,22 -263,21 +222,22 @@@ git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ if (read_cache() < 0) - die (_("Could not read the index")); + die(_("Could not read the index")); init_revisions(&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; DIFF_OPT_SET(&rev.diffopt, IGNORE_DIRTY_SUBMODULES); out = open(file, O_CREAT | O_WRONLY, 0666); if (out < 0) - die (_("Could not open '%s' for writing."), file); + die(_("Could not open '%s' for writing."), file); rev.diffopt.file = xfdopen(out, "w"); rev.diffopt.close_file = 1; if (run_diff_files(&rev, 0)) - die (_("Could not write patch")); + die(_("Could not write patch")); launch_editor(file, NULL, NULL); @@@ -323,7 -290,7 +250,7 @@@ child.git_cmd = 1; child.argv = apply_argv; if (run_command(&child)) - die (_("Could not apply '%s'"), file); + die(_("Could not apply '%s'"), file); unlink(file); free(file); @@@ -405,13 -372,12 +332,12 @@@ int cmd_add(int argc, const char **argv { int exit_status = 0; int newfd; - const char **pathspec; + struct pathspec pathspec; struct dir_struct dir; int flags; int add_new_files; int require_pathspec; char *seen = NULL; - int implicit_dot = 0; struct update_callback_data update_data; git_config(add_config, NULL); @@@ -453,19 -419,11 +379,11 @@@ if (!show_only && ignore_missing) die(_("Option --ignore-missing can only be used together with --dry-run")); - if (addremove) { - option_with_implicit_dot = "--all"; - short_option_with_implicit_dot = "-A"; - } - if (take_worktree_changes) { - option_with_implicit_dot = "--update"; - short_option_with_implicit_dot = "-u"; - } - if (option_with_implicit_dot && !argc) { - static const char *here[2] = { ".", NULL }; + + if ((addremove || take_worktree_changes) && !argc) { + static const char *whole[2] = { ":/", NULL }; argc = 1; - argv = here; - implicit_dot = 1; + argv = whole; } add_new_files = !take_worktree_changes && !refresh_only; @@@ -478,31 -436,21 +396,30 @@@ (intent_to_add ? ADD_CACHE_INTENT : 0) | (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) | (!(addremove || take_worktree_changes) - ? ADD_CACHE_IGNORE_REMOVAL : 0)) | - (implicit_dot ? ADD_CACHE_IMPLICIT_DOT : 0); + ? ADD_CACHE_IGNORE_REMOVAL : 0)); if (require_pathspec && argc == 0) { fprintf(stderr, _("Nothing specified, nothing added.\n")); fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n")); return 0; } - pathspec = validate_pathspec(argv, prefix); if (read_cache() < 0) die(_("index file corrupt")); - treat_gitlinks(pathspec); + + /* + * Check the "pathspec '%s' did not match any files" block + * below before enabling new magic. + */ + parse_pathspec(&pathspec, 0, + PATHSPEC_PREFER_FULL | + PATHSPEC_SYMLINK_LEADING_PATH | + PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE, + prefix, argv); if (add_new_files) { int baselen; + struct pathspec empty_pathspec; /* Set up the default git porcelain excludes */ memset(&dir, 0, sizeof(dir)); @@@ -511,52 -459,32 +428,49 @@@ setup_standard_excludes(&dir); } + memset(&empty_pathspec, 0, sizeof(empty_pathspec)); /* This picks up the paths that are not tracked */ - baselen = fill_directory(&dir, implicit_dot ? &empty_pathspec : &pathspec); - baselen = fill_directory(&dir, pathspec); - if (pathspec) - seen = prune_directory(&dir, pathspec, baselen); ++ baselen = fill_directory(&dir, &pathspec); + if (pathspec.nr) - seen = prune_directory(&dir, &pathspec, baselen, - implicit_dot ? WARN_IMPLICIT_DOT : 0); ++ seen = prune_directory(&dir, &pathspec, baselen); } if (refresh_only) { - refresh(verbose, pathspec); + refresh(verbose, &pathspec); goto finish; } - if (implicit_dot && prefix) - refresh_cache(REFRESH_QUIET); - if (pathspec) { + if (pathspec.nr) { int i; if (!seen) - seen = find_pathspecs_matching_against_index(pathspec); - for (i = 0; pathspec[i]; i++) { - if (!seen[i] && pathspec[i][0] - && !file_exists(pathspec[i])) { + seen = find_pathspecs_matching_against_index(&pathspec); + + /* + * file_exists() assumes exact match + */ + GUARD_PATHSPEC(&pathspec, + PATHSPEC_FROMTOP | + PATHSPEC_LITERAL | + PATHSPEC_GLOB | + PATHSPEC_ICASE | + PATHSPEC_EXCLUDE); + + for (i = 0; i < pathspec.nr; i++) { + const char *path = pathspec.items[i].match; + if (pathspec.items[i].magic & PATHSPEC_EXCLUDE) + continue; + if (!seen[i] && path[0] && + ((pathspec.items[i].magic & + (PATHSPEC_GLOB | PATHSPEC_ICASE)) || + !file_exists(path))) { if (ignore_missing) { int dtype = DT_UNKNOWN; - if (is_excluded(&dir, pathspec[i], &dtype)) - dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i])); + if (is_excluded(&dir, path, &dtype)) + dir_add_ignored(&dir, path, pathspec.items[i].len); } else die(_("pathspec '%s' did not match any files"), - pathspec[i]); + pathspec.items[i].original); } } free(seen); @@@ -564,19 -492,8 +478,8 @@@ plug_bulk_checkin(); - if ((flags & ADD_CACHE_IMPLICIT_DOT) && prefix) { - /* - * Check for modified files throughout the worktree so - * update_callback has a chance to warn about changes - * outside the cwd. - */ - update_data.implicit_dot = prefix; - update_data.implicit_dot_len = strlen(prefix); - free_pathspec(&pathspec); - memset(&pathspec, 0, sizeof(pathspec)); - } - update_data.flags = flags & ~ADD_CACHE_IMPLICIT_DOT; + update_data.flags = flags; - update_files_in_cache(prefix, pathspec, &update_data); + update_files_in_cache(prefix, &pathspec, &update_data); exit_status |= !!update_data.add_errors; if (add_new_files) @@@ -584,7 -501,7 +487,7 @@@ unplug_bulk_checkin(); - finish: +finish: if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(&lock_file)) diff --combined cache.h index b7d82e5d52,99acf80d2f..4c0043113c --- a/cache.h +++ b/cache.h @@@ -3,7 -3,7 +3,7 @@@ #include "git-compat-util.h" #include "strbuf.h" -#include "hash.h" +#include "hashmap.h" #include "advice.h" #include "gettext.h" #include "convert.h" @@@ -101,9 -101,9 +101,9 @@@ unsigned long git_deflate_bound(git_zst #define CACHE_SIGNATURE 0x44495243 /* "DIRC" */ struct cache_header { - unsigned int hdr_signature; - unsigned int hdr_version; - unsigned int hdr_entries; + uint32_t hdr_signature; + uint32_t hdr_version; + uint32_t hdr_entries; }; #define INDEX_FORMAT_LB 2 @@@ -115,27 -115,23 +115,27 @@@ * check it for equality in the 32 bits we save. */ struct cache_time { - unsigned int sec; - unsigned int nsec; + uint32_t sec; + uint32_t nsec; +}; + +struct stat_data { + struct cache_time sd_ctime; + struct cache_time sd_mtime; + unsigned int sd_dev; + unsigned int sd_ino; + unsigned int sd_uid; + unsigned int sd_gid; + unsigned int sd_size; }; struct cache_entry { - struct cache_time ce_ctime; - struct cache_time ce_mtime; - unsigned int ce_dev; - unsigned int ce_ino; + struct hashmap_entry ent; + struct stat_data ce_stat_data; unsigned int ce_mode; - unsigned int ce_uid; - unsigned int ce_gid; - unsigned int ce_size; unsigned int ce_flags; unsigned int ce_namelen; unsigned char sha1[20]; - struct cache_entry *next; char name[FLEX_ARRAY]; /* more */ }; @@@ -159,6 -155,7 +159,6 @@@ #define CE_ADDED (1 << 19) #define CE_HASHED (1 << 20) -#define CE_UNHASHED (1 << 21) #define CE_WT_REMOVE (1 << 22) /* remove in work directory */ #define CE_CONFLICTED (1 << 23) @@@ -188,24 -185,20 +188,24 @@@ #error "CE_EXTENDED_FLAGS out of range" #endif +struct pathspec; + /* * Copy the sha1 and stat state of a cache entry from one to * another. But we never change the name, or the hash state! */ -#define CE_STATE_MASK (CE_HASHED | CE_UNHASHED) -static inline void copy_cache_entry(struct cache_entry *dst, struct cache_entry *src) +static inline void copy_cache_entry(struct cache_entry *dst, + const struct cache_entry *src) { - unsigned int state = dst->ce_flags & CE_STATE_MASK; + unsigned int state = dst->ce_flags & CE_HASHED; /* Don't copy hash chain and name */ - memcpy(dst, src, offsetof(struct cache_entry, next)); + memcpy(&dst->ce_stat_data, &src->ce_stat_data, + offsetof(struct cache_entry, name) - + offsetof(struct cache_entry, ce_stat_data)); /* Restore the hash state */ - dst->ce_flags = (dst->ce_flags & ~CE_STATE_MASK) | state; + dst->ce_flags = (dst->ce_flags & ~CE_HASHED) | state; } static inline unsigned create_ce_flags(unsigned stage) @@@ -229,8 -222,7 +229,8 @@@ static inline unsigned int create_ce_mo return S_IFGITLINK; return S_IFREG | ce_permissions(mode); } -static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned int mode) +static inline unsigned int ce_mode_from_stat(const struct cache_entry *ce, + unsigned int mode) { extern int trust_executable_bit, has_symlinks; if (!has_symlinks && S_ISREG(mode) && @@@ -277,8 -269,8 +277,8 @@@ struct index_state struct cache_time timestamp; unsigned name_hash_initialized : 1, initialized : 1; - struct hash_table name_hash; - struct hash_table dir_hash; + struct hashmap name_hash; + struct hashmap dir_hash; }; extern struct index_state the_index; @@@ -314,8 -306,7 +314,8 @@@ extern void free_name_hash(struct index #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL, NULL) #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options)) #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options)) -#define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase)) +#define cache_dir_exists(name, namelen) index_dir_exists(&the_index, (name), (namelen)) +#define cache_file_exists(name, namelen, igncase) index_file_exists(&the_index, (name), (namelen), (igncase)) #define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen)) #define resolve_undo_clear() resolve_undo_clear_index(&the_index) #define unmerge_cache_entry_at(at) unmerge_index_entry_at(&the_index, at) @@@ -353,7 -344,6 +353,7 @@@ static inline enum object_type object_t #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY" #define INDEX_ENVIRONMENT "GIT_INDEX_FILE" #define GRAFT_ENVIRONMENT "GIT_GRAFT_FILE" +#define GIT_SHALLOW_FILE_ENVIRONMENT "GIT_SHALLOW_FILE" #define TEMPLATE_DIR_ENVIRONMENT "GIT_TEMPLATE_DIR" #define CONFIG_ENVIRONMENT "GIT_CONFIG" #define CONFIG_DATA_ENVIRONMENT "GIT_CONFIG_PARAMETERS" @@@ -369,9 -359,6 +369,9 @@@ #define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF" #define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE" #define GIT_LITERAL_PATHSPECS_ENVIRONMENT "GIT_LITERAL_PATHSPECS" +#define GIT_GLOB_PATHSPECS_ENVIRONMENT "GIT_GLOB_PATHSPECS" +#define GIT_NOGLOB_PATHSPECS_ENVIRONMENT "GIT_NOGLOB_PATHSPECS" +#define GIT_ICASE_PATHSPECS_ENVIRONMENT "GIT_ICASE_PATHSPECS" /* * This environment variable is expected to contain a boolean indicating @@@ -398,6 -385,7 +398,6 @@@ extern int is_bare_repository(void) extern int is_inside_git_dir(void); extern char *git_work_tree_cfg; extern int is_inside_work_tree(void); -extern int have_git_dir(void); extern const char *get_git_dir(void); extern int is_git_directory(const char *path); extern char *get_object_directory(void); @@@ -418,7 -406,6 +418,7 @@@ extern void setup_work_tree(void) extern const char *setup_git_directory_gently(int *); extern const char *setup_git_directory(void); extern char *prefix_path(const char *prefix, int len, const char *path); +extern char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path); extern const char *prefix_filename(const char *prefix, int len, const char *path); extern int check_filename(const char *prefix, const char *name); extern void verify_filename(const char *prefix, @@@ -432,9 -419,6 +432,9 @@@ extern int path_inside_repo(const char extern int set_git_dir_init(const char *git_dir, const char *real_git_dir, int); extern int init_db(const char *template_dir, unsigned int flags); +extern void sanitize_stdfds(void); +extern int daemonize(void); + #define alloc_nr(x) (((x)+16)*3/2) /* @@@ -457,7 -441,7 +457,7 @@@ /* Initialize and use the cache information */ extern int read_index(struct index_state *); -extern int read_index_preload(struct index_state *, const char **pathspec); +extern int read_index_preload(struct index_state *, const struct pathspec *pathspec); extern int read_index_from(struct index_state *, const char *path); extern int is_index_unborn(struct index_state *); extern int read_index_unmerged(struct index_state *); @@@ -465,8 -449,7 +465,8 @@@ extern int write_index(struct index_sta extern int discard_index(struct index_state *); extern int unmerged_index(const struct index_state *); extern int verify_path(const char *path); -extern struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int igncase); +extern struct cache_entry *index_dir_exists(struct index_state *istate, const char *name, int namelen); +extern struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase); extern int index_name_pos(const struct index_state *, const char *name, int namelen); #define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */ #define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */ @@@ -483,12 -466,10 +483,11 @@@ extern int remove_file_from_index(struc #define ADD_CACHE_IGNORE_ERRORS 4 #define ADD_CACHE_IGNORE_REMOVAL 8 #define ADD_CACHE_INTENT 16 - #define ADD_CACHE_IMPLICIT_DOT 32 /* internal to "git add -u/-A" */ extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags); extern int add_file_to_index(struct index_state *, const char *path, int flags); -extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh); -extern int ce_same_name(struct cache_entry *a, struct cache_entry *b); +extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options); +extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b); +extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce); extern int index_name_is_other(const struct index_state *, const char *, int); extern void *read_blob_data_from_index(struct index_state *, const char *, unsigned long *); @@@ -498,32 -479,35 +497,32 @@@ #define CE_MATCH_RACY_IS_DIRTY 02 /* do stat comparison even if CE_SKIP_WORKTREE is true */ #define CE_MATCH_IGNORE_SKIP_WORKTREE 04 -extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int); -extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int); - -#define PATHSPEC_ONESTAR 1 /* the pathspec pattern sastisfies GFNM_ONESTAR */ - -struct pathspec { - const char **raw; /* get_pathspec() result, not freed by free_pathspec() */ - int nr; - unsigned int has_wildcard:1; - unsigned int recursive:1; - int max_depth; - struct pathspec_item { - const char *match; - int len; - int nowildcard_len; - int flags; - } *items; -}; - -extern int init_pathspec(struct pathspec *, const char **); -extern void free_pathspec(struct pathspec *); -extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec); - -extern int limit_pathspec_to_literal(void); +/* ignore non-existent files during stat update */ +#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); #define HASH_WRITE_OBJECT 1 #define HASH_FORMAT_CHECK 2 extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags); extern int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned flags); + +/* + * Record to sd the data from st that we use to check whether a file + * might have changed. + */ +extern void fill_stat_data(struct stat_data *sd, struct stat *st); + +/* + * Return 0 if st is consistent with a file not having been changed + * since sd was filled. If there are differences, return a + * combination of MTIME_CHANGED, CTIME_CHANGED, OWNER_CHANGED, + * INODE_CHANGED, and DATA_CHANGED. + */ +extern int match_stat_data(const struct stat_data *sd, struct stat *st); + extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); #define REFRESH_REALLY 0x0001 /* ignore_valid */ @@@ -532,7 -516,7 +531,7 @@@ #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" */ -extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, const char *header_msg); +extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg); struct lock_file { struct lock_file *next; @@@ -569,7 -553,6 +568,7 @@@ extern int assume_unchanged extern int prefer_symlink_refs; extern int log_all_ref_updates; extern int warn_ambiguous_refs; +extern int warn_on_object_refname_ambiguity; extern int shared_repository; extern const char *apply_default_whitespace; extern const char *apply_default_ignorewhitespace; @@@ -739,29 -722,8 +738,29 @@@ enum sharedrepo }; int git_config_perm(const char *var, const char *value); int adjust_shared_perm(const char *path); -int safe_create_leading_directories(char *path); -int safe_create_leading_directories_const(const char *path); + +/* + * Create the directory containing the named path, using care to be + * somewhat safe against races. Return one of the scld_error values + * to indicate success/failure. + * + * SCLD_VANISHED indicates that one of the ancestor directories of the + * path existed at one point during the function call and then + * suddenly vanished, probably because another process pruned the + * directory while we were working. To be robust against this kind of + * race, callers might want to try invoking the function again when it + * returns SCLD_VANISHED. + */ +enum scld_error { + SCLD_OK = 0, + SCLD_FAILED = -1, + SCLD_PERMS = -2, + SCLD_EXISTS = -3, + SCLD_VANISHED = -4 +}; +enum scld_error safe_create_leading_directories(char *path); +enum scld_error safe_create_leading_directories_const(const char *path); + int mkdir_in_gitdir(const char *path); extern void home_config_paths(char **global, char **xdg, char *file); extern char *expand_user_path(const char *path); @@@ -774,9 -736,7 +773,9 @@@ int is_directory(const char *) const char *real_path(const char *path); const char *real_path_if_valid(const char *path); const char *absolute_path(const char *path); -const char *relative_path(const char *abs, const char *base); +const char *remove_leading_path(const char *in, const char *prefix); +const char *relative_path(const char *in, const char *prefix, struct strbuf *sb); +int normalize_path_copy_len(char *dst, const char *src, int *prefix_len); int normalize_path_copy(char *dst, const char *src); int longest_ancestor_length(const char *path, struct string_list *prefixes); char *strip_path_suffix(const char *path, const char *suffix); @@@ -784,11 -744,11 +783,11 @@@ int daemon_avoid_alias(const char *path int offset_1st_component(const char *path); /* object replacement */ -#define READ_SHA1_FILE_REPLACE 1 +#define LOOKUP_REPLACE_OBJECT 1 extern void *read_sha1_file_extended(const unsigned char *sha1, enum object_type *type, unsigned long *size, unsigned flag); static inline void *read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size) { - return read_sha1_file_extended(sha1, type, size, READ_SHA1_FILE_REPLACE); + return read_sha1_file_extended(sha1, type, size, LOOKUP_REPLACE_OBJECT); } extern const unsigned char *do_lookup_replace_object(const unsigned char *sha1); static inline const unsigned char *lookup_replace_object(const unsigned char *sha1) @@@ -797,12 -757,6 +796,12 @@@ return sha1; return do_lookup_replace_object(sha1); } +static inline const unsigned char *lookup_replace_object_extended(const unsigned char *sha1, unsigned flag) +{ + if (!(flag & LOOKUP_REPLACE_OBJECT)) + return sha1; + return lookup_replace_object(sha1); +} /* Read and unpack a sha1 file into memory, write memory to a sha1 file */ extern int sha1_object_info(const unsigned char *, unsigned long *); @@@ -810,7 -764,6 +809,7 @@@ extern int hash_sha1_file(const void *b extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1); extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *); extern int force_object_loose(const unsigned char *sha1, time_t mtime); +extern int git_open_noatime(const char *name); extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size); extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz); extern int parse_sha1_header(const char *hdr, unsigned long *sizep); @@@ -818,6 -771,9 +817,6 @@@ /* global flag to enable extra checks when accessing packed objects */ extern int do_check_packed_object_crc; -/* for development: log offset of pack access */ -extern const char *log_pack_access; - extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type); extern int move_temp_to_file(const char *tmpfile, const char *filename); @@@ -915,15 -871,12 +914,15 @@@ extern char *resolve_refdup(const char extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref); extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref); -extern int interpret_branch_name(const char *str, struct strbuf *); +extern int interpret_branch_name(const char *str, int len, struct strbuf *); extern int get_sha1_mb(const char *str, unsigned char *sha1); -extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules); -extern const char *ref_rev_parse_rules[]; -#define ref_fetch_rules ref_rev_parse_rules +/* + * Return true iff abbrev_name is a possible abbreviation for + * full_name according to the rules defined by ref_rev_parse_rules in + * refs.c. + */ +extern int refname_match(const char *abbrev_name, const char *full_name); extern int create_symref(const char *ref, const char *refs_heads_master, const char *logmsg); extern int validate_headref(const char *ref); @@@ -956,7 -909,6 +955,7 @@@ void show_date_relative(unsigned long t struct strbuf *timebuf); int parse_date(const char *date, char *buf, int bufsize); int parse_date_basic(const char *date, unsigned long *timestamp, int *offset); +int parse_expiry_date(const char *date, unsigned long *timestamp); void datestamp(char *buf, int bufsize); #define approxidate(s) approxidate_careful((s), NULL) unsigned long approxidate_careful(const char *, int *); @@@ -991,15 -943,6 +990,15 @@@ struct ident_split */ extern int split_ident_line(struct ident_split *, const char *, int); +/* + * Compare split idents for equality or strict ordering. Note that we + * compare only the ident part of the line, ignoring any timestamp. + * + * Because there are two fields, we must choose one as the primary key; we + * currently arbitrarily pick the email. + */ +extern int ident_cmp(const struct ident_split *, const struct ident_split *); + struct checkout { const char *base_dir; int base_dir_len; @@@ -1009,7 -952,6 +1008,7 @@@ refresh_cache:1; }; +#define TEMPORARY_FILENAME_LENGTH 25 extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath); struct cache_def { @@@ -1073,6 -1015,56 +1072,6 @@@ struct pack_entry struct packed_git *p; }; -struct ref { - struct ref *next; - unsigned char old_sha1[20]; - unsigned char new_sha1[20]; - char *symref; - unsigned int - force:1, - forced_update:1, - merge:1, - deletion:1, - matched:1; - enum { - REF_STATUS_NONE = 0, - REF_STATUS_OK, - REF_STATUS_REJECT_NONFASTFORWARD, - REF_STATUS_REJECT_ALREADY_EXISTS, - REF_STATUS_REJECT_NODELETE, - REF_STATUS_REJECT_FETCH_FIRST, - REF_STATUS_REJECT_NEEDS_FORCE, - REF_STATUS_UPTODATE, - REF_STATUS_REMOTE_REJECT, - REF_STATUS_EXPECTING_REPORT - } status; - char *remote_status; - struct ref *peer_ref; /* when renaming */ - char name[FLEX_ARRAY]; /* more */ -}; - -#define REF_NORMAL (1u << 0) -#define REF_HEADS (1u << 1) -#define REF_TAGS (1u << 2) - -extern struct ref *find_ref_by_name(const struct ref *list, const char *name); - -#define CONNECT_VERBOSE (1u << 0) -extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags); -extern int finish_connect(struct child_process *conn); -extern int git_connection_is_socket(struct child_process *conn); -struct extra_have_objects { - int nr, alloc; - unsigned char (*array)[20]; -}; -extern struct ref **get_remote_heads(int in, char *src_buf, size_t src_len, - struct ref **list, unsigned int flags, - struct extra_have_objects *); -extern int server_supports(const char *feature); -extern int parse_feature_request(const char *features, const char *feature); -extern const char *server_feature_value(const char *feature, int *len_ret); -extern const char *parse_feature_value(const char *feature_list, const char *feature, int *len_ret); - extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path); /* A hook for count-objects to report invalid files in pack directory */ @@@ -1105,10 -1097,7 +1104,10 @@@ extern int unpack_object_header(struct struct object_info { /* Request */ + enum object_type *typep; unsigned long *sizep; + unsigned long *disk_sizep; + unsigned char *delta_base_sha1; /* Response */ enum { @@@ -1133,7 -1122,7 +1132,7 @@@ } packed; } u; }; -extern int sha1_object_info_extended(const unsigned char *, struct object_info *); +extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags); /* Dumb servers support */ extern int update_server_info(int); @@@ -1152,19 -1141,14 +1151,19 @@@ typedef int (*config_fn_t)(const char *, const char *, void *); extern int git_default_config(const char *, const char *, void *); extern int git_config_from_file(config_fn_t fn, const char *, void *); +extern int git_config_from_buf(config_fn_t fn, const char *name, + const char *buf, size_t len, void *data); extern void git_config_push_parameter(const char *text); extern int git_config_from_parameters(config_fn_t fn, void *data); extern int git_config(config_fn_t fn, void *); extern int git_config_with_options(config_fn_t fn, void *, - const char *filename, int respect_includes); + const char *filename, + const char *blob_ref, + int respect_includes); extern int git_config_early(config_fn_t fn, void *, const char *repo_config); extern int git_parse_ulong(const char *, unsigned long *); extern int git_config_int(const char *, const char *); +extern int64_t git_config_int64(const char *, const char *); extern unsigned long git_config_ulong(const char *, const char *); extern int git_config_bool_or_int(const char *, const char *, int *); extern int git_config_bool(const char *, const char *); @@@ -1271,8 -1255,6 +1270,8 @@@ __attribute__((format (printf, 2, 3)) extern void trace_argv_printf(const char **argv, const char *format, ...); extern void trace_repo_setup(const char *prefix); extern int trace_want(const char *key); +__attribute__((format (printf, 2, 3))) +extern void trace_printf_key(const char *key, const char *fmt, ...); extern void trace_strbuf(const char *key, const struct strbuf *buf); void packet_trace_identity(const char *prog); @@@ -1282,7 -1264,7 +1281,7 @@@ * return 0 if success, 1 - if addition of a file failed and * ADD_FILES_IGNORE_ERRORS was specified in flags */ -int add_files_to_cache(const char *prefix, const char **pathspec, int flags); +int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags); /* diff.c */ extern int diff_auto_refresh_index; @@@ -1316,7 -1298,7 +1315,7 @@@ extern int ws_blank_line(const char *li #define ws_tab_width(rule) ((rule) & WS_TAB_WIDTH_MASK) /* ls-files */ -int report_path_error(const char *ps_matched, const char **pathspec, const char *prefix); +int report_path_error(const char *ps_matched, const struct pathspec *pathspec, const char *prefix); void overlay_tree_on_cache(const char *tree_name, const char *prefix); char *alias_lookup(const char *alias); @@@ -1343,31 -1325,4 +1342,31 @@@ int checkout_fast_forward(const unsigne int sane_execvp(const char *file, char *const argv[]); +/* + * A struct to encapsulate the concept of whether a file has changed + * since we last checked it. This uses criteria similar to those used + * for the index. + */ +struct stat_validity { + struct stat_data *sd; +}; + +void stat_validity_clear(struct stat_validity *sv); + +/* + * Returns 1 if the path is a regular file (or a symlink to a regular + * file) and matches the saved stat_validity, 0 otherwise. A missing + * or inaccessible file is considered a match if the struct was just + * initialized, or if the previous update found an inaccessible file. + */ +int stat_validity_check(struct stat_validity *sv, const char *path); + +/* + * Update the stat_validity from a file opened at descriptor fd. If + * the file is missing, inaccessible, or not a regular file, then + * future calls to stat_validity_check will match iff one of those + * conditions continues to be true. + */ +void stat_validity_update(struct stat_validity *sv, int fd); + #endif /* CACHE_H */ diff --combined t/t2200-add-update.sh index 9bf2bdffd2,9c7419f450..e16b15d3e5 --- a/t/t2200-add-update.sh +++ b/t/t2200-add-update.sh @@@ -80,26 -80,22 +80,21 @@@ test_expect_success 'change gets notice ' - # Note that this is scheduled to change in Git 2.0, when - # "git add -u" will become full-tree by default. - test_expect_success 'non-limited update in subdir leaves root alone' ' + test_expect_success 'non-qualified update in subdir updates from the root' ' ( cd dir1 && echo even more >>sub2 && git add -u ) && - cat >expect <<-\EOF && - check - top - EOF + : >expect && git diff-files --name-only >actual && test_cmp expect actual ' -test_expect_success SYMLINKS 'replace a file with a symlink' ' +test_expect_success 'replace a file with a symlink' ' rm foo && - ln -s top foo && - git add -u -- foo + test_ln_s_add top foo '