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.
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
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
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[=<when>]` option.
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.<name>.remote` for all branches, and is overridden by
remote.<name>.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.<group>::
branch of the `git.git` repository.
Documentation for older releases are available here:
- * link:v1.8.5.5/git.html[documentation for release 1.8.5.5]
+* 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.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],
+
For a path that is unmerged, 'GIT_EXTERNAL_DIFF' is called with 1
parameter, <path>.
++
+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
~~~~~
#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"
#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 *);
#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);
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
};
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);
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)
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 *);
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);
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
enum object_type *typep;
unsigned long *sizep;
unsigned long *disk_sizep;
+ unsigned char *delta_base_sha1;
/* Response */
enum {
} 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);
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);
{
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;
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;
}
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")) {
* 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;
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++;
}
* 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++;
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);
}
}
}
}
+ ALLOC_GROW(store.offset, 1, store.offset_alloc);
store.offset[0] = 0;
store.state = START;
store.seen = 0;
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;
/*
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
endif
PYTHON_PATH = /usr/local/bin/python
HAVE_PATHS_H = YesPlease
+ GMTIME_UNRELIABLE_ERRORS = UnfortunatelyYes
endif
ifeq ($(uname_S),OpenBSD)
NO_STRCASESTR = YesPlease
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
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
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
#include "cache.h"
#include "refs.h"
#include "fmt-merge-msg.h"
+#include "commit.h"
int trust_executable_bit = 1;
int trust_ctime = 1;
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.
INDEX_ENVIRONMENT,
NO_REPLACE_OBJECTS_ENVIRONMENT,
GIT_PREFIX_ENVIRONMENT,
+ GIT_SHALLOW_FILE_ENVIRONMENT,
NULL
};
static void setup_git_env(void)
{
const char *gitfile;
+ const char *shallow_file;
git_dir = getenv(GIT_DIR_ENVIRONMENT);
if (!git_dir)
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)
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;
}
#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)
{
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);
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");
(*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]) ||
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')
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) {
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;
+ }
+ }
#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 */
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;
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)
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
* 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 */
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')
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;
/*
}
if (lstat(ce->name, &st) < 0) {
+ if (ignore_missing && errno == ENOENT)
+ return ce;
if (err)
*err = errno;
return NULL;
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;
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)) {
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.
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);
}
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/" \
+ <basis >bad-timestamp &&
+ new=$(git hash-object -t commit -w --stdin <bad-timestamp) &&
+ test_when_finished "remove_object $new" &&
+ git update-ref refs/heads/bogus "$new" &&
+ test_when_finished "git update-ref -d refs/heads/bogus" &&
+ git fsck 2>out &&
+ cat out &&
+ grep "error in commit $new.*integer overflow" out
+'
+
test_expect_success 'tag pointing to nonexistent' '
cat >invalid-tag <<-\EOF &&
object ffffffffffffffffffffffffffffffffffffffff
)
'
- 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 <bad) &&
+ git fsck 2>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
-#!/bin/sh
+# Test framework for git. See t/README for usage.
#
# Copyright (c) 2005 Junio C Hamano
#
# 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
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:
#
if test "$help" = "t"
then
- echo "$test_description"
+ printf '%s\n' "$test_description"
exit 0
fi
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; }
}
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"
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")
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 &&
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;
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)
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;
}
}
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)
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;
}
* 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;
}
/*
* 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)
{
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 */
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;
}
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);
}
* "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 },
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;
+ }