From: Junio C Hamano Date: Wed, 17 Dec 2014 19:20:31 +0000 (-0800) Subject: Sync with v1.8.5.6 X-Git-Tag: v1.9.5~1 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/6898b797218ca1f25818d813318b387d965dc1bb?ds=inline;hp=-c Sync with v1.8.5.6 * maint-1.8.5: Git 1.8.5.6 fsck: complain about NTFS ".git" aliases in trees read-cache: optionally disallow NTFS .git variants path: add is_ntfs_dotgit() helper fsck: complain about HFS+ ".git" aliases in trees read-cache: optionally disallow HFS+ .git variants utf8: add is_hfs_dotgit() helper fsck: notice .git case-insensitively t1450: refactor ".", "..", and ".git" fsck tests verify_dotfile(): reject .git case-insensitively read-tree: add tests for confusing paths like ".." and ".git" unpack-trees: propagate errors adding entries to the index --- 6898b797218ca1f25818d813318b387d965dc1bb diff --combined Documentation/config.txt index c26a7c8469,097fdd47e1..7076aa9282 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@@ -78,8 -78,8 +78,8 @@@ be escaped: use `\"` for `"` and `\\` f The following escape sequences (beside `\"` and `\\`) are recognized: `\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB) -and `\b` for backspace (BS). No other char escape sequence, nor octal -char sequences are valid. +and `\b` for backspace (BS). Other char escape sequences (including octal +escape sequences) are invalid. Variable values ending in a `\` are continued on the next line in the customary UNIX fashion. @@@ -234,6 -234,17 +234,17 @@@ core.precomposeunicode: When false, file names are handled fully transparent by Git, which is backward compatible with older versions of Git. + core.protectHFS:: + If set to true, do not allow checkout of paths that would + be considered equivalent to `.git` on an HFS+ filesystem. + Defaults to `true` on Mac OS, and `false` elsewhere. + + core.protectNTFS:: + If set to true, do not allow checkout of paths that would + cause problems with the NTFS filesystem, e.g. conflict with + 8.3 "short" names. + Defaults to `true` on Windows, and `false` elsewhere. + core.trustctime:: If false, the ctime differences between the index and the working tree are ignored; useful when the inode change time @@@ -567,10 -578,6 +578,10 @@@ be passed to the shell by Git, which wi command to `LESS=FRSX less -+S`. The environment tells the command to set the `S` option to chop long lines but the command line resets it to the default to fold long lines. ++ +Likewise, when the `LV` environment variable is unset, Git sets it +to `-c`. You can override this setting by exporting `LV` with +another value or setting `core.pager` to `lv +c`. core.whitespace:: A comma separated list of common whitespace problems to @@@ -827,7 -834,7 +838,7 @@@ color.diff: commands will only use color when output is to the terminal. Defaults to false. + -This does not affect linkgit:git-format-patch[1] nor the +This does not affect linkgit:git-format-patch[1] or the 'git-diff-{asterisk}' plumbing commands. Can be overridden on the command line with the `--color[=]` option. @@@ -2030,10 -2037,6 +2041,10 @@@ receive.updateserverinfo: If set to true, git-receive-pack will run git-update-server-info after receiving data from git-push and updating refs. +receive.shallowupdate:: + If set to true, .git/shallow can be updated when new refs + require new shallow roots. Otherwise those refs are rejected. + remote.pushdefault:: The remote to push to by default. Overrides `branch..remote` for all branches, and is overridden by @@@ -2095,8 -2098,8 +2106,8 @@@ remote..vcs: remote..prune:: When set to true, fetching from this remote by default will also - remove any remote-tracking branches which no longer exist on the - remote (as if the `--prune` option was give on the command line). + remove any remote-tracking references that no longer exist on the + remote (as if the `--prune` option was given on the command line). Overrides `fetch.prune` settings, if any. remotes.:: diff --combined Documentation/git.txt index 3d54378f27,2ff62c54ee..7297fe1ea2 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@@ -43,18 -43,10 +43,19 @@@ unreleased) version of Git, that is ava branch of the `git.git` repository. Documentation for older releases are available here: +* link:v1.9.4/git.html[documentation for release 1.9.4] + +* release notes for + link:RelNotes/1.9.4.txt[1.9.4], + link:RelNotes/1.9.3.txt[1.9.3], + link:RelNotes/1.9.2.txt[1.9.2], + link:RelNotes/1.9.1.txt[1.9.1], + link:RelNotes/1.9.0.txt[1.9.0]. + - * link:v1.8.5.5/git.html[documentation for release 1.8.5.5] + * link:v1.8.5.6/git.html[documentation for release 1.8.5.6] * release notes for + link:RelNotes/1.8.5.6.txt[1.8.5.6], link:RelNotes/1.8.5.5.txt[1.8.5.5], link:RelNotes/1.8.5.4.txt[1.8.5.4], link:RelNotes/1.8.5.3.txt[1.8.5.3], @@@ -819,15 -811,6 +820,15 @@@ temporary file --- it is removed when ' + For a path that is unmerged, 'GIT_EXTERNAL_DIFF' is called with 1 parameter, . ++ +For each path 'GIT_EXTERNAL_DIFF' is called, two environment variables, +'GIT_DIFF_PATH_COUNTER' and 'GIT_DIFF_PATH_TOTAL' are set. + +'GIT_DIFF_PATH_COUNTER':: + A 1-based counter incremented by one for every path. + +'GIT_DIFF_PATH_TOTAL':: + The total number of paths. other ~~~~~ diff --combined cache.h index ebe9a405d8,29ed24b802..017c487609 --- a/cache.h +++ b/cache.h @@@ -354,7 -354,6 +354,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" @@@ -487,7 -486,7 +487,7 @@@ extern int remove_file_from_index(struc #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 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 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,13 -497,11 +498,13 @@@ #define CE_MATCH_RACY_IS_DIRTY 02 /* do stat comparison even if CE_SKIP_WORKTREE is true */ #define CE_MATCH_IGNORE_SKIP_WORKTREE 04 +/* 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); -extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec); - #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); @@@ -587,6 -584,8 +587,8 @@@ extern int fsync_object_files extern int core_preload_index; extern int core_apply_sparse_checkout; extern int precomposed_unicode; + extern int protect_hfs; + extern int protect_ntfs; /* * The character that begins a commented line in user-editable file @@@ -739,29 -738,8 +741,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); @@@ -782,13 -760,14 +784,14 @@@ int longest_ancestor_length(const char char *strip_path_suffix(const char *path, const char *suffix); int daemon_avoid_alias(const char *path); int offset_1st_component(const char *path); + extern int is_ntfs_dotgit(const char *name); /* 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 -776,6 +800,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 *); @@@ -917,12 -890,9 +920,12 @@@ extern int dwim_log(const char *str, in 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); @@@ -961,7 -931,6 +964,7 @@@ void datestamp(char *buf, int bufsize) unsigned long approxidate_careful(const char *, int *); unsigned long approxidate_relative(const char *date, const struct timeval *now); enum date_mode parse_date_format(const char *format); +int date_overflows(unsigned long date); #define IDENT_STRICT 1 #define IDENT_NO_DATE 2 @@@ -1108,7 -1077,6 +1111,7 @@@ struct object_info enum object_type *typep; unsigned long *sizep; unsigned long *disk_sizep; + unsigned char *delta_base_sha1; /* Response */ enum { @@@ -1133,7 -1101,7 +1136,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); @@@ -1271,8 -1239,6 +1274,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); diff --combined config.c index 314d8ee740,2cd64b6e3a..8984ad20c7 --- a/config.c +++ b/config.c @@@ -84,12 -84,8 +84,12 @@@ static int handle_path_include(const ch { int ret = 0; struct strbuf buf = STRBUF_INIT; - char *expanded = expand_user_path(path); + char *expanded; + if (!path) + return config_error_nonbool("include.path"); + + expanded = expand_user_path(path); if (!expanded) return error("Could not expand include path '%s'", path); path = expanded; @@@ -885,6 -881,16 +885,16 @@@ static int git_default_core_config(cons return 0; } + if (!strcmp(var, "core.protecthfs")) { + protect_hfs = git_config_bool(var, value); + return 0; + } + + if (!strcmp(var, "core.protectntfs")) { + protect_ntfs = git_config_bool(var, value); + return 0; + } + /* Add other config variables here and to Documentation/config.txt. */ return 0; } @@@ -973,25 -979,25 +983,25 @@@ static int git_default_mailmap_config(c int git_default_config(const char *var, const char *value, void *dummy) { - if (!prefixcmp(var, "core.")) + if (starts_with(var, "core.")) return git_default_core_config(var, value); - if (!prefixcmp(var, "user.")) + if (starts_with(var, "user.")) return git_ident_config(var, value, dummy); - if (!prefixcmp(var, "i18n.")) + if (starts_with(var, "i18n.")) return git_default_i18n_config(var, value); - if (!prefixcmp(var, "branch.")) + if (starts_with(var, "branch.")) return git_default_branch_config(var, value); - if (!prefixcmp(var, "push.")) + if (starts_with(var, "push.")) return git_default_push_config(var, value); - if (!prefixcmp(var, "mailmap.")) + if (starts_with(var, "mailmap.")) return git_default_mailmap_config(var, value); - if (!prefixcmp(var, "advice.")) + if (starts_with(var, "advice.")) return git_default_advice_config(var, value); if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) { @@@ -1214,14 -1220,15 +1224,14 @@@ int git_config(config_fn_t fn, void *da * Find all the stuff for git_config_set() below. */ -#define MAX_MATCHES 512 - static struct { int baselen; char *key; int do_not_match; regex_t *value_regex; int multi_replace; - size_t offset[MAX_MATCHES]; + size_t *offset; + unsigned int offset_alloc; enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state; int seen; } store; @@@ -1244,11 -1251,11 +1254,11 @@@ static int store_aux(const char *key, c if (matches(key, value)) { if (store.seen == 1 && store.multi_replace == 0) { warning("%s has multiple values", key); - } else if (store.seen >= MAX_MATCHES) { - error("too many matches for %s", key); - return 1; } + ALLOC_GROW(store.offset, store.seen + 1, + store.offset_alloc); + store.offset[store.seen] = cf->do_ftell(cf); store.seen++; } @@@ -1276,15 -1283,11 +1286,15 @@@ * Do not increment matches: this is no match, but we * just made sure we are in the desired section. */ + ALLOC_GROW(store.offset, store.seen + 1, + store.offset_alloc); store.offset[store.seen] = cf->do_ftell(cf); /* fallthru */ case SECTION_END_SEEN: case START: if (matches(key, value)) { + ALLOC_GROW(store.offset, store.seen + 1, + store.offset_alloc); store.offset[store.seen] = cf->do_ftell(cf); store.state = KEY_SEEN; store.seen++; @@@ -1292,9 -1295,6 +1302,9 @@@ if (strrchr(key, '.') - key == store.baselen && !strncmp(key, store.key, store.baselen)) { store.state = SECTION_SEEN; + ALLOC_GROW(store.offset, + store.seen + 1, + store.offset_alloc); store.offset[store.seen] = cf->do_ftell(cf); } } @@@ -1593,7 -1593,6 +1603,7 @@@ int git_config_set_multivar_in_file(con } } + ALLOC_GROW(store.offset, 1, store.offset_alloc); store.offset[0] = 0; store.state = START; store.seen = 0; @@@ -1883,7 -1882,7 +1893,7 @@@ int parse_config_key(const char *var const char *dot; /* Does it start with "section." ? */ - if (prefixcmp(var, section) || var[section_len] != '.') + if (!starts_with(var, section) || var[section_len] != '.') return -1; /* diff --combined config.mak.uname index efaed94d5d,ec7ed7ac3b..f3cdcbcddf --- a/config.mak.uname +++ b/config.mak.uname @@@ -97,6 -97,7 +97,7 @@@ ifeq ($(uname_S),Darwin HAVE_DEV_TTY = YesPlease COMPAT_OBJS += compat/precompose_utf8.o BASIC_CFLAGS += -DPRECOMPOSE_UNICODE + BASIC_CFLAGS += -DPROTECT_HFS_DEFAULT=1 endif ifeq ($(uname_S),SunOS) NEEDS_SOCKET = YesPlease @@@ -188,7 -189,6 +189,7 @@@ ifeq ($(uname_S),FreeBSD endif PYTHON_PATH = /usr/local/bin/python HAVE_PATHS_H = YesPlease + GMTIME_UNRELIABLE_ERRORS = UnfortunatelyYes endif ifeq ($(uname_S),OpenBSD) NO_STRCASESTR = YesPlease @@@ -199,13 -199,6 +200,13 @@@ BASIC_LDFLAGS += -L/usr/local/lib HAVE_PATHS_H = YesPlease endif +ifeq ($(uname_S),MirBSD) + NO_STRCASESTR = YesPlease + NO_MEMMEM = YesPlease + USE_ST_TIMESPEC = YesPlease + NEEDS_LIBICONV = YesPlease + HAVE_PATHS_H = YesPlease +endif ifeq ($(uname_S),NetBSD) ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2) NEEDS_LIBICONV = YesPlease @@@ -369,6 -362,7 +370,7 @@@ ifeq ($(uname_S),Windows EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib PTHREAD_LIBS = lib = + BASIC_CFLAGS += -DPROTECT_NTFS_DEFAULT=1 ifndef DEBUG BASIC_CFLAGS += -GL -Os -MT BASIC_LDFLAGS += -LTCG @@@ -513,6 -507,7 +515,7 @@@ ifneq (,$(findstring MINGW,$(uname_S)) COMPAT_OBJS += compat/mingw.o compat/winansi.o \ compat/win32/pthread.o compat/win32/syslog.o \ compat/win32/dirent.o + BASIC_CFLAGS += -DPROTECT_NTFS_DEFAULT=1 BASIC_LDFLAGS += -Wl,--large-address-aware EXTLIBS += -lws2_32 GITLIBS += git.res diff --combined environment.c index 4a3437d8a6,184748da3e..39a8c6c343 --- a/environment.c +++ b/environment.c @@@ -10,7 -10,6 +10,7 @@@ #include "cache.h" #include "refs.h" #include "fmt-merge-msg.h" +#include "commit.h" int trust_executable_bit = 1; int trust_ctime = 1; @@@ -64,6 -63,16 +64,16 @@@ int precomposed_unicode = -1; /* see pr struct startup_info *startup_info; unsigned long pack_size_limit_cfg; + #ifndef PROTECT_HFS_DEFAULT + #define PROTECT_HFS_DEFAULT 0 + #endif + int protect_hfs = PROTECT_HFS_DEFAULT; + + #ifndef PROTECT_NTFS_DEFAULT + #define PROTECT_NTFS_DEFAULT 0 + #endif + int protect_ntfs = PROTECT_NTFS_DEFAULT; + /* * The character that begins a commented line in user-editable file * that is subject to stripspace. @@@ -98,7 -107,6 +108,7 @@@ const char * const local_repo_env[] = INDEX_ENVIRONMENT, NO_REPLACE_OBJECTS_ENVIRONMENT, GIT_PREFIX_ENVIRONMENT, + GIT_SHALLOW_FILE_ENVIRONMENT, NULL }; @@@ -126,7 -134,6 +136,7 @@@ static char *expand_namespace(const cha static void setup_git_env(void) { const char *gitfile; + const char *shallow_file; git_dir = getenv(GIT_DIR_ENVIRONMENT); if (!git_dir) @@@ -150,9 -157,6 +160,9 @@@ read_replace_refs = 0; namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT)); namespace_len = strlen(namespace); + shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT); + if (shallow_file) + set_alternate_shallow_file(shallow_file, 0); } int is_bare_repository(void) @@@ -177,7 -181,7 +187,7 @@@ const char *get_git_namespace(void const char *strip_namespace(const char *namespaced_ref) { - if (prefixcmp(namespaced_ref, get_git_namespace()) != 0) + if (!starts_with(namespaced_ref, get_git_namespace())) return NULL; return namespaced_ref + namespace_len; } diff --combined fsck.c index 64bf279fd7,0b76de6f68..ee7f531e36 --- a/fsck.c +++ b/fsck.c @@@ -6,6 -6,7 +6,7 @@@ #include "commit.h" #include "tag.h" #include "fsck.h" + #include "utf8.h" static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data) { @@@ -175,7 -176,8 +176,8 @@@ static int fsck_tree(struct tree *item has_dot = 1; if (!strcmp(name, "..")) has_dotdot = 1; - if (!strcmp(name, ".git")) + if (!strcasecmp(name, ".git") || is_hfs_dotgit(name) || + is_ntfs_dotgit(name)) has_dotgit = 1; has_zero_pad |= *(char *)desc.buffer == '0'; update_tree_entry(&desc); @@@ -245,8 -247,6 +247,8 @@@ static int fsck_ident(char **ident, struct object *obj, fsck_error error_func) { + char *end; + if (**ident == '<') return error_func(obj, FSCK_ERROR, "invalid author/committer line - missing space before email"); *ident += strcspn(*ident, "<>\n"); @@@ -266,11 -266,10 +268,11 @@@ (*ident)++; if (**ident == '0' && (*ident)[1] != ' ') return error_func(obj, FSCK_ERROR, "invalid author/committer line - zero-padded date"); - *ident += strspn(*ident, "0123456789"); - if (**ident != ' ') + if (date_overflows(strtoul(*ident, &end, 10))) + return error_func(obj, FSCK_ERROR, "invalid author/committer line - date causes integer overflow"); + if (end == *ident || *end != ' ') return error_func(obj, FSCK_ERROR, "invalid author/committer line - bad date"); - (*ident)++; + *ident = end + 1; if ((**ident != '+' && **ident != '-') || !isdigit((*ident)[1]) || !isdigit((*ident)[2]) || @@@ -290,6 -289,9 +292,6 @@@ static int fsck_commit(struct commit *c int parents = 0; int err; - if (commit->date == ULONG_MAX) - return error_func(&commit->object, FSCK_ERROR, "invalid author/committer line"); - if (memcmp(buffer, "tree ", 5)) return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'tree' line"); if (get_sha1_hex(buffer+5, tree_sha1) || buffer[45] != '\n') diff --combined path.c index f9c5062427,4ef1b01e05..dfd58f4367 --- a/path.c +++ b/path.c @@@ -265,12 -265,12 +265,12 @@@ static struct passwd *getpw_str(const c char *expand_user_path(const char *path) { struct strbuf user_path = STRBUF_INIT; - const char *first_slash = strchrnul(path, '/'); const char *to_copy = path; if (path == NULL) goto return_null; if (path[0] == '~') { + const char *first_slash = strchrnul(path, '/'); const char *username = path + 1; size_t username_len = first_slash - username; if (username_len == 0) { @@@ -830,3 -830,36 +830,36 @@@ int offset_1st_component(const char *pa return 2 + is_dir_sep(path[2]); return is_dir_sep(path[0]); } + + static int only_spaces_and_periods(const char *path, size_t len, size_t skip) + { + if (len < skip) + return 0; + len -= skip; + path += skip; + while (len-- > 0) { + char c = *(path++); + if (c != ' ' && c != '.') + return 0; + } + return 1; + } + + int is_ntfs_dotgit(const char *name) + { + int len; + + for (len = 0; ; len++) + if (!name[len] || name[len] == '\\' || is_dir_sep(name[len])) { + if (only_spaces_and_periods(name, len, 4) && + !strncasecmp(name, ".git", 4)) + return 1; + if (only_spaces_and_periods(name, len, 5) && + !strncasecmp(name, "git~1", 5)) + return 1; + if (name[len] != '\\') + return 0; + name += len + 1; + len = -1; + } + } diff --combined read-cache.c index 4b4effd64b,4fa208b662..ee07cd610a --- a/read-cache.c +++ b/read-cache.c @@@ -14,9 -14,9 +14,10 @@@ #include "resolve-undo.h" #include "strbuf.h" #include "varint.h" + #include "utf8.h" -static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really); +static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, + unsigned int options); /* Mask for the name length in ce_flags in the on-disk index */ @@@ -697,7 -697,7 +698,7 @@@ int add_file_to_index(struct index_stat struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, - int refresh) + unsigned int refresh_options) { int size, len; struct cache_entry *ce; @@@ -717,7 -717,10 +718,7 @@@ ce->ce_namelen = len; ce->ce_mode = create_ce_mode(mode); - if (refresh) - return refresh_cache_entry(ce, 0); - - return ce; + return refresh_cache_entry(ce, refresh_options); } int ce_same_name(const struct cache_entry *a, const struct cache_entry *b) @@@ -726,6 -729,11 +727,6 @@@ return ce_namelen(b) == len && !memcmp(a->name, b->name, len); } -int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec) -{ - return match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL); -} - /* * We fundamentally don't like some paths: we don't want * dot or dot-dot anywhere, and for obvious reasons don't @@@ -752,9 -760,10 +753,10 @@@ static int verify_dotfile(const char *r * shares the path end test with the ".." case. */ case 'g': - if (rest[1] != 'i') + case 'G': + if (rest[1] != 'i' && rest[1] != 'I') break; - if (rest[2] != 't') + if (rest[2] != 't' && rest[2] != 'T') break; rest += 2; /* fallthrough */ @@@ -778,6 -787,10 +780,10 @@@ int verify_path(const char *path 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; c = *path++; if ((c == '.' && !verify_dotfile(path)) || is_dir_sep(c) || c == '\0') @@@ -1022,12 -1035,10 +1028,12 @@@ static struct cache_entry *refresh_cach struct stat st; struct cache_entry *updated; int changed, size; + int refresh = options & CE_MATCH_REFRESH; 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; - if (ce_uptodate(ce)) + if (!refresh || ce_uptodate(ce)) return ce; /* @@@ -1045,8 -1056,6 +1051,8 @@@ } if (lstat(ce->name, &st) < 0) { + if (ignore_missing && errno == ENOENT) + return ce; if (err) *err = errno; return NULL; @@@ -1124,9 -1133,7 +1130,9 @@@ int refresh_index(struct index_state *i int ignore_submodules = (flags & REFRESH_IGNORE_SUBMODULES) != 0; int first = 1; int in_porcelain = (flags & REFRESH_IN_PORCELAIN); - unsigned int options = really ? CE_MATCH_IGNORE_VALID : 0; + unsigned int options = (CE_MATCH_REFRESH | + (really ? CE_MATCH_IGNORE_VALID : 0) | + (not_new ? CE_MATCH_IGNORE_MISSING : 0)); const char *modified_fmt; const char *deleted_fmt; const char *typechange_fmt; @@@ -1148,7 -1155,8 +1154,7 @@@ if (ignore_submodules && S_ISGITLINK(ce->ce_mode)) continue; - if (pathspec && - !match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, seen)) + if (pathspec && !ce_path_match(ce, pathspec, seen)) filtered = 1; if (ce_stage(ce)) { @@@ -1174,6 -1182,8 +1180,6 @@@ if (!new) { const char *fmt; - if (not_new && cache_errno == ENOENT) - continue; if (really && cache_errno == EINVAL) { /* If we are doing --really-refresh that * means the index is not valid anymore. @@@ -1203,10 -1213,9 +1209,10 @@@ return has_errors; } -static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really) +static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, + unsigned int options) { - return refresh_cache_ent(&the_index, ce, really, NULL, NULL); + return refresh_cache_ent(&the_index, ce, options, NULL, NULL); } diff --combined t/t1450-fsck.sh index 8c739c9613,6edd99a81e..983568a4b9 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh @@@ -142,20 -142,6 +142,20 @@@ test_expect_success '> in name is repor grep "error in commit $new" out ' +# date is 2^64 + 1 +test_expect_success 'integer overflow in timestamps is reported' ' + git cat-file commit HEAD >basis && + sed "s/^\\(author .*>\\) [0-9]*/\\1 18446744073709551617/" \ + bad-timestamp && + new=$(git hash-object -t commit -w --stdin out && + cat out && + grep "error in commit $new.*integer overflow" out +' + test_expect_success 'tag pointing to nonexistent' ' cat >invalid-tag <<-\EOF && object ffffffffffffffffffffffffffffffffffffffff @@@ -251,35 -237,40 +251,40 @@@ test_expect_success 'fsck notices submo ) ' - test_expect_success 'fsck notices "." and ".." in trees' ' - ( - git init dots && - cd dots && - blob=$(echo foo | git hash-object -w --stdin) && - tab=$(printf "\\t") && - git mktree <<-EOF && - 100644 blob $blob$tab. - 100644 blob $blob$tab.. - EOF - git fsck 2>out && - cat out && - grep "warning.*\\." out - ) - ' - - test_expect_success 'fsck notices ".git" in trees' ' - ( - git init dotgit && - cd dotgit && - blob=$(echo foo | git hash-object -w --stdin) && - tab=$(printf "\\t") && - git mktree <<-EOF && - 100644 blob $blob$tab.git - EOF - git fsck 2>out && - cat out && - grep "warning.*\\.git" out - ) - ' + while read name path pretty; do + while read mode type; do + : ${pretty:=$path} + test_expect_success "fsck notices $pretty as $type" ' + ( + git init $name-$type && + cd $name-$type && + echo content >file && + git add file && + git commit -m base && + blob=$(git rev-parse :file) && + tree=$(git rev-parse HEAD^{tree}) && + value=$(eval "echo \$$type") && + printf "$mode $type %s\t%s" "$value" "$path" >bad && + bad_tree=$(git mktree out && + cat out && + grep "warning.*tree $bad_tree" out + )' + done <<-\EOF + 100644 blob + 040000 tree + EOF + done <<-EOF + dot . + dotdot .. + dotgit .git + dotgit-case .GIT + dotgit-unicode .gI${u200c}T .gI{u200c}T + dotgit-case2 .Git + git-tilde1 git~1 + dotgitdot .git. + dot-backslash-case .\\\\.GIT\\\\foobar + dotgit-case-backslash .git\\\\foobar + EOF test_done diff --combined t/test-lib.sh index 3c7cb1d774,d4569f8df0..afa411e128 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@@ -1,4 -1,4 +1,4 @@@ -#!/bin/sh +# Test framework for git. See t/README for usage. # # Copyright (c) 2005 Junio C Hamano # @@@ -26,10 -26,6 +26,10 @@@ the # outside of t/, e.g. for running tests on the test library # itself. TEST_DIRECTORY=$(pwd) +else + # ensure that TEST_DIRECTORY is an absolute path so that it + # is valid even if the current working directory is changed + TEST_DIRECTORY=$(cd "$TEST_DIRECTORY" && pwd) || exit 1 fi if test -z "$TEST_OUTPUT_DIRECTORY" then @@@ -158,7 -154,11 +158,11 @@@ _z40=0000000000000000000000000000000000 LF=' ' - export _x05 _x40 _z40 LF + # UTF-8 ZERO WIDTH NON-JOINER, which HFS+ ignores + # when case-folding filenames + u200c=$(printf '\342\200\214') + + export _x05 _x40 _z40 LF u200c # Each test should start with something like this, after copyright notices: # @@@ -277,7 -277,7 +281,7 @@@ error "Test script did not set test_des if test "$help" = "t" then - echo "$test_description" + printf '%s\n' "$test_description" exit 0 fi @@@ -328,7 -328,7 +332,7 @@@ test_failure_ () test_failure=$(($test_failure + 1)) say_color error "not ok $test_count - $1" shift - echo "$@" | sed -e 's/^/# /' + printf '%s\n' "$*" | sed -e 's/^/# /' test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; } } @@@ -481,6 -481,8 +485,6 @@@ test_at_end_hook_ () test_done () { GIT_EXIT_OK=t - # Note: t0000 relies on $HARNESS_ACTIVE disabling the .counts - # output file if test -z "$HARNESS_ACTIVE" then test_results_dir="$TEST_OUTPUT_DIRECTORY/test-results" @@@ -575,9 -577,11 +579,9 @@@ the make_valgrind_symlink () { # handle only executables, unless they are shell libraries that - # need to be in the exec-path. We will just use "#!" as a - # guess for a shell-script, since we have no idea what the user - # may have configured as the shell path. + # need to be in the exec-path. test -x "$1" || - test "#!" = "$(head -c 2 <"$1")" || + test "# " = "$(head -c 2 <"$1")" || return; base=$(basename "$1") @@@ -830,10 -834,6 +834,10 @@@ test_lazy_prereq SYMLINKS ln -s x y && test -h y ' +test_lazy_prereq FILEMODE ' + test "$(git config --bool core.filemode)" = true +' + test_lazy_prereq CASE_INSENSITIVE_FS ' echo good >CamelCase && echo bad >camelcase && diff --combined unpack-trees.c index 164354dad7,648180256e..ca7dd0fa5f --- a/unpack-trees.c +++ b/unpack-trees.c @@@ -102,7 -102,7 +102,7 @@@ void setup_unpack_trees_porcelain(struc opts->unpack_rejects[i].strdup_strings = 1; } - static void do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce, + static int do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce, unsigned int set, unsigned int clear) { clear |= CE_HASHED | CE_UNHASHED; @@@ -112,8 -112,8 +112,8 @@@ ce->next = NULL; ce->ce_flags = (ce->ce_flags & ~clear) | set; - add_index_entry(&o->result, ce, - ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE); + return add_index_entry(&o->result, ce, + ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE); } static struct cache_entry *dup_entry(const struct cache_entry *ce) @@@ -608,7 -608,9 +608,9 @@@ static int unpack_nondirectories(int n for (i = 0; i < n; i++) if (src[i] && src[i] != o->df_conflict_entry) - do_add_entry(o, src[i], 0, 0); + if (do_add_entry(o, src[i], 0, 0)) + return -1; + return 0; } @@@ -830,24 -832,23 +832,24 @@@ static int unpack_callback(int n, unsig } static int clear_ce_flags_1(struct cache_entry **cache, int nr, - char *prefix, int prefix_len, + struct strbuf *prefix, int select_mask, int clear_mask, struct exclude_list *el, int defval); /* Whole directory matching */ static int clear_ce_flags_dir(struct cache_entry **cache, int nr, - char *prefix, int prefix_len, + struct strbuf *prefix, char *basename, int select_mask, int clear_mask, struct exclude_list *el, int defval) { struct cache_entry **cache_end; int dtype = DT_DIR; - int ret = is_excluded_from_list(prefix, prefix_len, + int ret = is_excluded_from_list(prefix->buf, prefix->len, basename, &dtype, el); + int rc; - prefix[prefix_len++] = '/'; + strbuf_addch(prefix, '/'); /* If undecided, use matching result of parent dir in defval */ if (ret < 0) @@@ -855,7 -856,7 +857,7 @@@ for (cache_end = cache; cache_end != cache + nr; cache_end++) { struct cache_entry *ce = *cache_end; - if (strncmp(ce->name, prefix, prefix_len)) + if (strncmp(ce->name, prefix->buf, prefix->len)) break; } @@@ -866,12 -867,10 +868,12 @@@ * calling clear_ce_flags_1(). That function will call * the expensive is_excluded_from_list() on every entry. */ - return clear_ce_flags_1(cache, cache_end - cache, - prefix, prefix_len, - select_mask, clear_mask, - el, ret); + rc = clear_ce_flags_1(cache, cache_end - cache, + prefix, + select_mask, clear_mask, + el, ret); + strbuf_setlen(prefix, prefix->len - 1); + return rc; } /* @@@ -890,7 -889,7 +892,7 @@@ * Top level path has prefix_len zero. */ static int clear_ce_flags_1(struct cache_entry **cache, int nr, - char *prefix, int prefix_len, + struct strbuf *prefix, int select_mask, int clear_mask, struct exclude_list *el, int defval) { @@@ -910,10 -909,10 +912,10 @@@ continue; } - if (prefix_len && strncmp(ce->name, prefix, prefix_len)) + if (prefix->len && strncmp(ce->name, prefix->buf, prefix->len)) break; - name = ce->name + prefix_len; + name = ce->name + prefix->len; slash = strchr(name, '/'); /* If it's a directory, try whole directory match first */ @@@ -921,26 -920,29 +923,26 @@@ int processed; len = slash - name; - memcpy(prefix + prefix_len, name, len); + strbuf_add(prefix, name, len); - /* - * terminate the string (no trailing slash), - * clear_c_f_dir needs it - */ - prefix[prefix_len + len] = '\0'; processed = clear_ce_flags_dir(cache, cache_end - cache, - prefix, prefix_len + len, - prefix + prefix_len, + prefix, + prefix->buf + prefix->len - len, select_mask, clear_mask, el, defval); /* clear_c_f_dir eats a whole dir already? */ if (processed) { cache += processed; + strbuf_setlen(prefix, prefix->len - len); continue; } - prefix[prefix_len + len++] = '/'; + strbuf_addch(prefix, '/'); cache += clear_ce_flags_1(cache, cache_end - cache, - prefix, prefix_len + len, + prefix, select_mask, clear_mask, el, defval); + strbuf_setlen(prefix, prefix->len - len - 1); continue; } @@@ -961,12 -963,9 +963,12 @@@ static int clear_ce_flags(struct cache_ int select_mask, int clear_mask, struct exclude_list *el) { - char prefix[PATH_MAX]; + static struct strbuf prefix = STRBUF_INIT; + + strbuf_reset(&prefix); + return clear_ce_flags_1(cache, nr, - prefix, 0, + &prefix, select_mask, clear_mask, el, 0); } diff --combined utf8.c index 536a9c83e1,2c6442cc11..015c8157ed --- a/utf8.c +++ b/utf8.c @@@ -84,10 -84,11 +84,10 @@@ static int git_wcwidth(ucs_char_t ch * "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c". */ static const struct interval combining[] = { - { 0x0300, 0x0357 }, { 0x035D, 0x036F }, { 0x0483, 0x0486 }, - { 0x0488, 0x0489 }, { 0x0591, 0x05A1 }, { 0x05A3, 0x05B9 }, - { 0x05BB, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, - { 0x05C4, 0x05C4 }, { 0x0600, 0x0603 }, { 0x0610, 0x0615 }, - { 0x064B, 0x0658 }, { 0x0670, 0x0670 }, { 0x06D6, 0x06E4 }, + { 0x0300, 0x036F }, { 0x0483, 0x0489 }, { 0x0591, 0x05BD }, + { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, { 0x05C4, 0x05C5 }, + { 0x05C7, 0x05C7 }, { 0x0600, 0x0604 }, { 0x0610, 0x061A }, + { 0x064B, 0x065F }, { 0x0670, 0x0670 }, { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, { 0x07A6, 0x07B0 }, { 0x0901, 0x0902 }, { 0x093C, 0x093C }, { 0x0941, 0x0948 }, @@@ -627,3 -628,67 +627,67 @@@ int mbs_chrlen(const char **text, size_ return chrlen; } + + /* + * Pick the next char from the stream, folding as an HFS+ filename comparison + * would. Note that this is _not_ complete by any means. It's just enough + * to make is_hfs_dotgit() work, and should not be used otherwise. + */ + static ucs_char_t next_hfs_char(const char **in) + { + while (1) { + ucs_char_t out = pick_one_utf8_char(in, NULL); + /* + * check for malformed utf8. Technically this + * gets converted to a percent-sequence, but + * returning 0 is good enough for is_hfs_dotgit + * to realize it cannot be .git + */ + if (!*in) + return 0; + + /* these code points are ignored completely */ + switch (out) { + case 0x200c: /* ZERO WIDTH NON-JOINER */ + case 0x200d: /* ZERO WIDTH JOINER */ + case 0x200e: /* LEFT-TO-RIGHT MARK */ + case 0x200f: /* RIGHT-TO-LEFT MARK */ + case 0x202a: /* LEFT-TO-RIGHT EMBEDDING */ + case 0x202b: /* RIGHT-TO-LEFT EMBEDDING */ + case 0x202c: /* POP DIRECTIONAL FORMATTING */ + case 0x202d: /* LEFT-TO-RIGHT OVERRIDE */ + case 0x202e: /* RIGHT-TO-LEFT OVERRIDE */ + case 0x206a: /* INHIBIT SYMMETRIC SWAPPING */ + case 0x206b: /* ACTIVATE SYMMETRIC SWAPPING */ + case 0x206c: /* INHIBIT ARABIC FORM SHAPING */ + case 0x206d: /* ACTIVATE ARABIC FORM SHAPING */ + case 0x206e: /* NATIONAL DIGIT SHAPES */ + case 0x206f: /* NOMINAL DIGIT SHAPES */ + case 0xfeff: /* ZERO WIDTH NO-BREAK SPACE */ + continue; + } + + /* + * there's a great deal of other case-folding that occurs, + * but this is enough to catch anything that will convert + * to ".git" + */ + return tolower(out); + } + } + + int is_hfs_dotgit(const char *path) + { + ucs_char_t c; + + if (next_hfs_char(&path) != '.' || + next_hfs_char(&path) != 'g' || + next_hfs_char(&path) != 'i' || + next_hfs_char(&path) != 't') + return 0; + c = next_hfs_char(&path); + if (c && !is_dir_sep(c)) + return 0; + + return 1; + }