Merge branch 'kb/checkout-optim'
* kb/checkout-optim: Revert "lstat_cache(): print a warning if doing ping-pong between cache types" checkout bugfix: use stat.mtime instead of stat.ctime in two places Makefile: Set compiler switch for USE_NSEC Create USE_ST_TIMESPEC and turn it on for Darwin Not all systems use st_[cm]tim field for ns resolution file timestamp Record ns-timestamps if possible, but do not use it without USE_NSEC write_index(): update index_state->timestamp after flushing to disk verify_uptodate(): add ce_uptodate(ce) test make USE_NSEC work as expected fix compile error when USE_NSEC is defined check_updates(): effective removal of cache entries marked CE_REMOVE lstat_cache(): print a warning if doing ping-pong between cache types show_patch_diff(): remove a call to fstat() write_entry(): use fstat() instead of lstat() when file is open write_entry(): cleanup of some duplicated code create_directories(): remove some memcpy() and strchr() calls unlink_entry(): introduce schedule_dir_for_removal() lstat_cache(): swap func(length, string) into func(string, length) lstat_cache(): generalise longest_match_lstat_cache() lstat_cache(): small cleanup and optimisationmaint
commit
a9bfe81309
|
@ -129,3 +129,6 @@ For C programs:
|
||||||
used in the git core command set (unless your command is clearly
|
used in the git core command set (unless your command is clearly
|
||||||
separate from it, such as an importer to convert random-scm-X
|
separate from it, such as an importer to convert random-scm-X
|
||||||
repositories to git).
|
repositories to git).
|
||||||
|
|
||||||
|
- When we pass <string, length> pair to functions, we should try to
|
||||||
|
pass them in that order.
|
||||||
|
|
18
Makefile
18
Makefile
|
@ -126,6 +126,12 @@ all::
|
||||||
# randomly break unless your underlying filesystem supports those sub-second
|
# randomly break unless your underlying filesystem supports those sub-second
|
||||||
# times (my ext3 doesn't).
|
# times (my ext3 doesn't).
|
||||||
#
|
#
|
||||||
|
# Define USE_ST_TIMESPEC if your "struct stat" uses "st_ctimespec" instead of
|
||||||
|
# "st_ctim"
|
||||||
|
#
|
||||||
|
# Define NO_NSEC if your "struct stat" does not have "st_ctim.tv_nsec"
|
||||||
|
# available. This automatically turns USE_NSEC off.
|
||||||
|
#
|
||||||
# Define USE_STDEV below if you want git to care about the underlying device
|
# Define USE_STDEV below if you want git to care about the underlying device
|
||||||
# change being considered an inode change from the update-index perspective.
|
# change being considered an inode change from the update-index perspective.
|
||||||
#
|
#
|
||||||
|
@ -657,6 +663,7 @@ ifeq ($(uname_S),Darwin)
|
||||||
endif
|
endif
|
||||||
NO_MEMMEM = YesPlease
|
NO_MEMMEM = YesPlease
|
||||||
THREADED_DELTA_SEARCH = YesPlease
|
THREADED_DELTA_SEARCH = YesPlease
|
||||||
|
USE_ST_TIMESPEC = YesPlease
|
||||||
endif
|
endif
|
||||||
ifeq ($(uname_S),SunOS)
|
ifeq ($(uname_S),SunOS)
|
||||||
NEEDS_SOCKET = YesPlease
|
NEEDS_SOCKET = YesPlease
|
||||||
|
@ -734,6 +741,7 @@ ifeq ($(uname_S),AIX)
|
||||||
NO_MEMMEM = YesPlease
|
NO_MEMMEM = YesPlease
|
||||||
NO_MKDTEMP = YesPlease
|
NO_MKDTEMP = YesPlease
|
||||||
NO_STRLCPY = YesPlease
|
NO_STRLCPY = YesPlease
|
||||||
|
NO_NSEC = YesPlease
|
||||||
FREAD_READS_DIRECTORIES = UnfortunatelyYes
|
FREAD_READS_DIRECTORIES = UnfortunatelyYes
|
||||||
INTERNAL_QSORT = UnfortunatelyYes
|
INTERNAL_QSORT = UnfortunatelyYes
|
||||||
NEEDS_LIBICONV=YesPlease
|
NEEDS_LIBICONV=YesPlease
|
||||||
|
@ -799,6 +807,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
|
||||||
RUNTIME_PREFIX = YesPlease
|
RUNTIME_PREFIX = YesPlease
|
||||||
NO_POSIX_ONLY_PROGRAMS = YesPlease
|
NO_POSIX_ONLY_PROGRAMS = YesPlease
|
||||||
NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
|
NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
|
||||||
|
NO_NSEC = YesPlease
|
||||||
COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/regex -Icompat/fnmatch
|
COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/regex -Icompat/fnmatch
|
||||||
COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1
|
COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1
|
||||||
COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
|
COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
|
||||||
|
@ -920,6 +929,15 @@ endif
|
||||||
ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
|
ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
|
||||||
BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT
|
BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT
|
||||||
endif
|
endif
|
||||||
|
ifdef USE_NSEC
|
||||||
|
BASIC_CFLAGS += -DUSE_NSEC
|
||||||
|
endif
|
||||||
|
ifdef USE_ST_TIMESPEC
|
||||||
|
BASIC_CFLAGS += -DUSE_ST_TIMESPEC
|
||||||
|
endif
|
||||||
|
ifdef NO_NSEC
|
||||||
|
BASIC_CFLAGS += -DNO_NSEC
|
||||||
|
endif
|
||||||
ifdef NO_C99_FORMAT
|
ifdef NO_C99_FORMAT
|
||||||
BASIC_CFLAGS += -DNO_C99_FORMAT
|
BASIC_CFLAGS += -DNO_C99_FORMAT
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -148,7 +148,7 @@ static const char **validate_pathspec(int argc, const char **argv, const char *p
|
||||||
if (pathspec) {
|
if (pathspec) {
|
||||||
const char **p;
|
const char **p;
|
||||||
for (p = pathspec; *p; p++) {
|
for (p = pathspec; *p; p++) {
|
||||||
if (has_symlink_leading_path(strlen(*p), *p)) {
|
if (has_symlink_leading_path(*p, strlen(*p))) {
|
||||||
int len = prefix ? strlen(prefix) : 0;
|
int len = prefix ? strlen(prefix) : 0;
|
||||||
die("'%s' is beyond a symbolic link", *p + len);
|
die("'%s' is beyond a symbolic link", *p + len);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2360,7 +2360,7 @@ static int check_to_create_blob(const char *new_name, int ok_if_exists)
|
||||||
* In such a case, path "new_name" does not exist as
|
* In such a case, path "new_name" does not exist as
|
||||||
* far as git is concerned.
|
* far as git is concerned.
|
||||||
*/
|
*/
|
||||||
if (has_symlink_leading_path(strlen(new_name), new_name))
|
if (has_symlink_leading_path(new_name, strlen(new_name)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return error("%s: already exists in working directory", new_name);
|
return error("%s: already exists in working directory", new_name);
|
||||||
|
|
|
@ -800,15 +800,13 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
mtime.sec = st.st_mtime;
|
mtime.sec = st.st_mtime;
|
||||||
#ifdef USE_NSEC
|
mtime.nsec = ST_MTIME_NSEC(st);
|
||||||
mtime.usec = st.st_mtim.usec;
|
|
||||||
#endif
|
|
||||||
if (stat(shallow, &st)) {
|
if (stat(shallow, &st)) {
|
||||||
if (mtime.sec)
|
if (mtime.sec)
|
||||||
die("shallow file was removed during fetch");
|
die("shallow file was removed during fetch");
|
||||||
} else if (st.st_mtime != mtime.sec
|
} else if (st.st_mtime != mtime.sec
|
||||||
#ifdef USE_NSEC
|
#ifdef USE_NSEC
|
||||||
|| st.st_mtim.usec != mtime.usec
|
|| ST_MTIME_NSEC(st) != mtime.nsec
|
||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
die("shallow file was changed during fetch");
|
die("shallow file was changed during fetch");
|
||||||
|
|
|
@ -195,7 +195,7 @@ static int process_path(const char *path)
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
len = strlen(path);
|
len = strlen(path);
|
||||||
if (has_symlink_leading_path(len, path))
|
if (has_symlink_leading_path(path, len))
|
||||||
return error("'%s' is beyond a symbolic link", path);
|
return error("'%s' is beyond a symbolic link", path);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
19
cache.h
19
cache.h
|
@ -140,8 +140,8 @@ struct ondisk_cache_entry_extended {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cache_entry {
|
struct cache_entry {
|
||||||
unsigned int ce_ctime;
|
struct cache_time ce_ctime;
|
||||||
unsigned int ce_mtime;
|
struct cache_time ce_mtime;
|
||||||
unsigned int ce_dev;
|
unsigned int ce_dev;
|
||||||
unsigned int ce_ino;
|
unsigned int ce_ino;
|
||||||
unsigned int ce_mode;
|
unsigned int ce_mode;
|
||||||
|
@ -282,7 +282,7 @@ struct index_state {
|
||||||
struct cache_entry **cache;
|
struct cache_entry **cache;
|
||||||
unsigned int cache_nr, cache_alloc, cache_changed;
|
unsigned int cache_nr, cache_alloc, cache_changed;
|
||||||
struct cache_tree *cache_tree;
|
struct cache_tree *cache_tree;
|
||||||
time_t timestamp;
|
struct cache_time timestamp;
|
||||||
void *alloc;
|
void *alloc;
|
||||||
unsigned name_hash_initialized : 1,
|
unsigned name_hash_initialized : 1,
|
||||||
initialized : 1;
|
initialized : 1;
|
||||||
|
@ -428,7 +428,7 @@ extern int read_index_preload(struct index_state *, const char **pathspec);
|
||||||
extern int read_index_from(struct index_state *, const char *path);
|
extern int read_index_from(struct index_state *, const char *path);
|
||||||
extern int is_index_unborn(struct index_state *);
|
extern int is_index_unborn(struct index_state *);
|
||||||
extern int read_index_unmerged(struct index_state *);
|
extern int read_index_unmerged(struct index_state *);
|
||||||
extern int write_index(const struct index_state *, int newfd);
|
extern int write_index(struct index_state *, int newfd);
|
||||||
extern int discard_index(struct index_state *);
|
extern int discard_index(struct index_state *);
|
||||||
extern int unmerged_index(const struct index_state *);
|
extern int unmerged_index(const struct index_state *);
|
||||||
extern int verify_path(const char *path);
|
extern int verify_path(const char *path);
|
||||||
|
@ -443,6 +443,7 @@ extern int add_index_entry(struct index_state *, struct cache_entry *ce, int opt
|
||||||
extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
|
extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
|
||||||
extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
|
extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
|
||||||
extern int remove_index_entry_at(struct index_state *, int pos);
|
extern int remove_index_entry_at(struct index_state *, int pos);
|
||||||
|
extern void remove_marked_cache_entries(struct index_state *istate);
|
||||||
extern int remove_file_from_index(struct index_state *, const char *path);
|
extern int remove_file_from_index(struct index_state *, const char *path);
|
||||||
#define ADD_CACHE_VERBOSE 1
|
#define ADD_CACHE_VERBOSE 1
|
||||||
#define ADD_CACHE_PRETEND 2
|
#define ADD_CACHE_PRETEND 2
|
||||||
|
@ -725,11 +726,13 @@ struct checkout {
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
|
extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
|
||||||
extern int has_symlink_leading_path(int len, const char *name);
|
extern int has_symlink_leading_path(const char *name, int len);
|
||||||
extern int has_symlink_or_noent_leading_path(int len, const char *name);
|
extern int has_symlink_or_noent_leading_path(const char *name, int len);
|
||||||
extern int has_dirs_only_path(int len, const char *name, int prefix_len);
|
extern int has_dirs_only_path(const char *name, int len, int prefix_len);
|
||||||
extern void invalidate_lstat_cache(int len, const char *name);
|
extern void invalidate_lstat_cache(const char *name, int len);
|
||||||
extern void clear_lstat_cache(void);
|
extern void clear_lstat_cache(void);
|
||||||
|
extern void schedule_dir_for_removal(const char *name, int len);
|
||||||
|
extern void remove_scheduled_dirs(void);
|
||||||
|
|
||||||
extern struct alternate_object_database {
|
extern struct alternate_object_database {
|
||||||
struct alternate_object_database *next;
|
struct alternate_object_database *next;
|
||||||
|
|
|
@ -712,9 +712,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
|
||||||
result_size = buf.len;
|
result_size = buf.len;
|
||||||
result = strbuf_detach(&buf, NULL);
|
result = strbuf_detach(&buf, NULL);
|
||||||
elem->mode = canon_mode(st.st_mode);
|
elem->mode = canon_mode(st.st_mode);
|
||||||
}
|
} else if (0 <= (fd = open(elem->path, O_RDONLY))) {
|
||||||
else if (0 <= (fd = open(elem->path, O_RDONLY)) &&
|
|
||||||
!fstat(fd, &st)) {
|
|
||||||
size_t len = xsize_t(st.st_size);
|
size_t len = xsize_t(st.st_size);
|
||||||
ssize_t done;
|
ssize_t done;
|
||||||
int is_file, i;
|
int is_file, i;
|
||||||
|
|
|
@ -31,7 +31,7 @@ static int check_removed(const struct cache_entry *ce, struct stat *st)
|
||||||
return -1;
|
return -1;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (has_symlink_leading_path(ce_namelen(ce), ce->name))
|
if (has_symlink_leading_path(ce->name, ce_namelen(ce)))
|
||||||
return 1;
|
return 1;
|
||||||
if (S_ISDIR(st->st_mode)) {
|
if (S_ISDIR(st->st_mode)) {
|
||||||
unsigned char sub[20];
|
unsigned char sub[20];
|
||||||
|
|
2
dir.c
2
dir.c
|
@ -720,7 +720,7 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i
|
||||||
{
|
{
|
||||||
struct path_simplify *simplify;
|
struct path_simplify *simplify;
|
||||||
|
|
||||||
if (has_symlink_leading_path(strlen(path), path))
|
if (has_symlink_leading_path(path, strlen(path)))
|
||||||
return dir->nr;
|
return dir->nr;
|
||||||
|
|
||||||
simplify = create_simplify(pathspec);
|
simplify = create_simplify(pathspec);
|
||||||
|
|
106
entry.c
106
entry.c
|
@ -2,15 +2,19 @@
|
||||||
#include "blob.h"
|
#include "blob.h"
|
||||||
#include "dir.h"
|
#include "dir.h"
|
||||||
|
|
||||||
static void create_directories(const char *path, const struct checkout *state)
|
static void create_directories(const char *path, int path_len,
|
||||||
|
const struct checkout *state)
|
||||||
{
|
{
|
||||||
int len = strlen(path);
|
char *buf = xmalloc(path_len + 1);
|
||||||
char *buf = xmalloc(len + 1);
|
int len = 0;
|
||||||
const char *slash = path;
|
|
||||||
|
|
||||||
while ((slash = strchr(slash+1, '/')) != NULL) {
|
while (len < path_len) {
|
||||||
len = slash - path;
|
do {
|
||||||
memcpy(buf, path, len);
|
buf[len] = path[len];
|
||||||
|
len++;
|
||||||
|
} while (len < path_len && path[len] != '/');
|
||||||
|
if (len >= path_len)
|
||||||
|
break;
|
||||||
buf[len] = 0;
|
buf[len] = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -20,7 +24,7 @@ static void create_directories(const char *path, const struct checkout *state)
|
||||||
* we test the path components of the prefix with the
|
* we test the path components of the prefix with the
|
||||||
* stat() function instead of the lstat() function.
|
* stat() function instead of the lstat() function.
|
||||||
*/
|
*/
|
||||||
if (has_dirs_only_path(len, buf, state->base_dir_len))
|
if (has_dirs_only_path(buf, len, state->base_dir_len))
|
||||||
continue; /* ok, it is already a directory. */
|
continue; /* ok, it is already a directory. */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -74,7 +78,7 @@ static int create_file(const char *path, unsigned int mode)
|
||||||
return open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
|
return open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned long *size)
|
static void *read_blob_entry(struct cache_entry *ce, unsigned long *size)
|
||||||
{
|
{
|
||||||
enum object_type type;
|
enum object_type type;
|
||||||
void *new = read_sha1_file(ce->sha1, &type, size);
|
void *new = read_sha1_file(ce->sha1, &type, size);
|
||||||
|
@ -89,36 +93,52 @@ static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned
|
||||||
|
|
||||||
static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile)
|
static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile)
|
||||||
{
|
{
|
||||||
int fd;
|
unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT;
|
||||||
long wrote;
|
int fd, ret, fstat_done = 0;
|
||||||
|
char *new;
|
||||||
switch (ce->ce_mode & S_IFMT) {
|
struct strbuf buf = STRBUF_INIT;
|
||||||
char *new;
|
unsigned long size;
|
||||||
struct strbuf buf;
|
size_t wrote, newsize = 0;
|
||||||
unsigned long size;
|
struct stat st;
|
||||||
|
|
||||||
|
switch (ce_mode_s_ifmt) {
|
||||||
case S_IFREG:
|
case S_IFREG:
|
||||||
new = read_blob_entry(ce, path, &size);
|
case S_IFLNK:
|
||||||
|
new = read_blob_entry(ce, &size);
|
||||||
if (!new)
|
if (!new)
|
||||||
return error("git checkout-index: unable to read sha1 file of %s (%s)",
|
return error("git checkout-index: unable to read sha1 file of %s (%s)",
|
||||||
path, sha1_to_hex(ce->sha1));
|
path, sha1_to_hex(ce->sha1));
|
||||||
|
|
||||||
|
if (ce_mode_s_ifmt == S_IFLNK && has_symlinks && !to_tempfile) {
|
||||||
|
ret = symlink(new, path);
|
||||||
|
free(new);
|
||||||
|
if (ret)
|
||||||
|
return error("git checkout-index: unable to create symlink %s (%s)",
|
||||||
|
path, strerror(errno));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert from git internal format to working tree format
|
* Convert from git internal format to working tree format
|
||||||
*/
|
*/
|
||||||
strbuf_init(&buf, 0);
|
if (ce_mode_s_ifmt == S_IFREG &&
|
||||||
if (convert_to_working_tree(ce->name, new, size, &buf)) {
|
convert_to_working_tree(ce->name, new, size, &buf)) {
|
||||||
size_t newsize = 0;
|
|
||||||
free(new);
|
free(new);
|
||||||
new = strbuf_detach(&buf, &newsize);
|
new = strbuf_detach(&buf, &newsize);
|
||||||
size = newsize;
|
size = newsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (to_tempfile) {
|
if (to_tempfile) {
|
||||||
strcpy(path, ".merge_file_XXXXXX");
|
if (ce_mode_s_ifmt == S_IFREG)
|
||||||
|
strcpy(path, ".merge_file_XXXXXX");
|
||||||
|
else
|
||||||
|
strcpy(path, ".merge_link_XXXXXX");
|
||||||
fd = mkstemp(path);
|
fd = mkstemp(path);
|
||||||
} else
|
} else if (ce_mode_s_ifmt == S_IFREG) {
|
||||||
fd = create_file(path, ce->ce_mode);
|
fd = create_file(path, ce->ce_mode);
|
||||||
|
} else {
|
||||||
|
fd = create_file(path, 0666);
|
||||||
|
}
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
free(new);
|
free(new);
|
||||||
return error("git checkout-index: unable to create file %s (%s)",
|
return error("git checkout-index: unable to create file %s (%s)",
|
||||||
|
@ -126,41 +146,16 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
|
||||||
}
|
}
|
||||||
|
|
||||||
wrote = write_in_full(fd, new, size);
|
wrote = write_in_full(fd, new, size);
|
||||||
|
/* use fstat() only when path == ce->name */
|
||||||
|
if (state->refresh_cache && !to_tempfile && !state->base_dir_len) {
|
||||||
|
fstat(fd, &st);
|
||||||
|
fstat_done = 1;
|
||||||
|
}
|
||||||
close(fd);
|
close(fd);
|
||||||
free(new);
|
free(new);
|
||||||
if (wrote != size)
|
if (wrote != size)
|
||||||
return error("git checkout-index: unable to write file %s", path);
|
return error("git checkout-index: unable to write file %s", path);
|
||||||
break;
|
break;
|
||||||
case S_IFLNK:
|
|
||||||
new = read_blob_entry(ce, path, &size);
|
|
||||||
if (!new)
|
|
||||||
return error("git checkout-index: unable to read sha1 file of %s (%s)",
|
|
||||||
path, sha1_to_hex(ce->sha1));
|
|
||||||
if (to_tempfile || !has_symlinks) {
|
|
||||||
if (to_tempfile) {
|
|
||||||
strcpy(path, ".merge_link_XXXXXX");
|
|
||||||
fd = mkstemp(path);
|
|
||||||
} else
|
|
||||||
fd = create_file(path, 0666);
|
|
||||||
if (fd < 0) {
|
|
||||||
free(new);
|
|
||||||
return error("git checkout-index: unable to create "
|
|
||||||
"file %s (%s)", path, strerror(errno));
|
|
||||||
}
|
|
||||||
wrote = write_in_full(fd, new, size);
|
|
||||||
close(fd);
|
|
||||||
free(new);
|
|
||||||
if (wrote != size)
|
|
||||||
return error("git checkout-index: unable to write file %s",
|
|
||||||
path);
|
|
||||||
} else {
|
|
||||||
wrote = symlink(new, path);
|
|
||||||
free(new);
|
|
||||||
if (wrote)
|
|
||||||
return error("git checkout-index: unable to create "
|
|
||||||
"symlink %s (%s)", path, strerror(errno));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case S_IFGITLINK:
|
case S_IFGITLINK:
|
||||||
if (to_tempfile)
|
if (to_tempfile)
|
||||||
return error("git checkout-index: cannot create temporary subproject %s", path);
|
return error("git checkout-index: cannot create temporary subproject %s", path);
|
||||||
|
@ -172,8 +167,8 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->refresh_cache) {
|
if (state->refresh_cache) {
|
||||||
struct stat st;
|
if (!fstat_done)
|
||||||
lstat(ce->name, &st);
|
lstat(ce->name, &st);
|
||||||
fill_stat_cache_info(ce, &st);
|
fill_stat_cache_info(ce, &st);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -190,6 +185,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t
|
||||||
|
|
||||||
memcpy(path, state->base_dir, len);
|
memcpy(path, state->base_dir, len);
|
||||||
strcpy(path + len, ce->name);
|
strcpy(path + len, ce->name);
|
||||||
|
len += ce_namelen(ce);
|
||||||
|
|
||||||
if (!lstat(path, &st)) {
|
if (!lstat(path, &st)) {
|
||||||
unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);
|
unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);
|
||||||
|
@ -218,6 +214,6 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t
|
||||||
return error("unable to unlink old '%s' (%s)", path, strerror(errno));
|
return error("unable to unlink old '%s' (%s)", path, strerror(errno));
|
||||||
} else if (state->not_new)
|
} else if (state->not_new)
|
||||||
return 0;
|
return 0;
|
||||||
create_directories(path, state);
|
create_directories(path, len, state);
|
||||||
return write_entry(ce, path, state, 0);
|
return write_entry(ce, path, state, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -388,4 +388,18 @@ void git_qsort(void *base, size_t nmemb, size_t size,
|
||||||
# define FORCE_DIR_SET_GID 0
|
# define FORCE_DIR_SET_GID 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef NO_NSEC
|
||||||
|
#undef USE_NSEC
|
||||||
|
#define ST_CTIME_NSEC(st) 0
|
||||||
|
#define ST_MTIME_NSEC(st) 0
|
||||||
|
#else
|
||||||
|
#ifdef USE_ST_TIMESPEC
|
||||||
|
#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctimespec.tv_nsec))
|
||||||
|
#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtimespec.tv_nsec))
|
||||||
|
#else
|
||||||
|
#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctim.tv_nsec))
|
||||||
|
#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtim.tv_nsec))
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
85
read-cache.c
85
read-cache.c
|
@ -67,8 +67,10 @@ void rename_index_entry_at(struct index_state *istate, int nr, const char *new_n
|
||||||
*/
|
*/
|
||||||
void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
|
void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
|
||||||
{
|
{
|
||||||
ce->ce_ctime = st->st_ctime;
|
ce->ce_ctime.sec = (unsigned int)st->st_ctime;
|
||||||
ce->ce_mtime = st->st_mtime;
|
ce->ce_mtime.sec = (unsigned int)st->st_mtime;
|
||||||
|
ce->ce_ctime.nsec = ST_CTIME_NSEC(*st);
|
||||||
|
ce->ce_mtime.nsec = ST_MTIME_NSEC(*st);
|
||||||
ce->ce_dev = st->st_dev;
|
ce->ce_dev = st->st_dev;
|
||||||
ce->ce_ino = st->st_ino;
|
ce->ce_ino = st->st_ino;
|
||||||
ce->ce_uid = st->st_uid;
|
ce->ce_uid = st->st_uid;
|
||||||
|
@ -196,11 +198,18 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
|
||||||
default:
|
default:
|
||||||
die("internal error: ce_mode is %o", ce->ce_mode);
|
die("internal error: ce_mode is %o", ce->ce_mode);
|
||||||
}
|
}
|
||||||
if (ce->ce_mtime != (unsigned int) st->st_mtime)
|
if (ce->ce_mtime.sec != (unsigned int)st->st_mtime)
|
||||||
changed |= MTIME_CHANGED;
|
changed |= MTIME_CHANGED;
|
||||||
if (trust_ctime && ce->ce_ctime != (unsigned int) st->st_ctime)
|
if (trust_ctime && ce->ce_ctime.sec != (unsigned int)st->st_ctime)
|
||||||
changed |= CTIME_CHANGED;
|
changed |= CTIME_CHANGED;
|
||||||
|
|
||||||
|
#ifdef USE_NSEC
|
||||||
|
if (ce->ce_mtime.nsec != ST_MTIME_NSEC(*st))
|
||||||
|
changed |= MTIME_CHANGED;
|
||||||
|
if (trust_ctime && ce->ce_ctime.nsec != ST_CTIME_NSEC(*st))
|
||||||
|
changed |= CTIME_CHANGED;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (ce->ce_uid != (unsigned int) st->st_uid ||
|
if (ce->ce_uid != (unsigned int) st->st_uid ||
|
||||||
ce->ce_gid != (unsigned int) st->st_gid)
|
ce->ce_gid != (unsigned int) st->st_gid)
|
||||||
changed |= OWNER_CHANGED;
|
changed |= OWNER_CHANGED;
|
||||||
|
@ -232,8 +241,16 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
|
||||||
static int is_racy_timestamp(const struct index_state *istate, struct cache_entry *ce)
|
static int is_racy_timestamp(const struct index_state *istate, struct cache_entry *ce)
|
||||||
{
|
{
|
||||||
return (!S_ISGITLINK(ce->ce_mode) &&
|
return (!S_ISGITLINK(ce->ce_mode) &&
|
||||||
istate->timestamp &&
|
istate->timestamp.sec &&
|
||||||
((unsigned int)istate->timestamp) <= ce->ce_mtime);
|
#ifdef USE_NSEC
|
||||||
|
/* nanosecond timestamped files can also be racy! */
|
||||||
|
(istate->timestamp.sec < ce->ce_mtime.sec ||
|
||||||
|
(istate->timestamp.sec == ce->ce_mtime.sec &&
|
||||||
|
istate->timestamp.nsec <= ce->ce_mtime.nsec))
|
||||||
|
#else
|
||||||
|
istate->timestamp.sec <= ce->ce_mtime.sec
|
||||||
|
#endif
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ie_match_stat(const struct index_state *istate,
|
int ie_match_stat(const struct index_state *istate,
|
||||||
|
@ -443,6 +460,26 @@ int remove_index_entry_at(struct index_state *istate, int pos)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove all cache ententries marked for removal, that is where
|
||||||
|
* CE_REMOVE is set in ce_flags. This is much more effective than
|
||||||
|
* calling remove_index_entry_at() for each entry to be removed.
|
||||||
|
*/
|
||||||
|
void remove_marked_cache_entries(struct index_state *istate)
|
||||||
|
{
|
||||||
|
struct cache_entry **ce_array = istate->cache;
|
||||||
|
unsigned int i, j;
|
||||||
|
|
||||||
|
for (i = j = 0; i < istate->cache_nr; i++) {
|
||||||
|
if (ce_array[i]->ce_flags & CE_REMOVE)
|
||||||
|
remove_name_hash(ce_array[i]);
|
||||||
|
else
|
||||||
|
ce_array[j++] = ce_array[i];
|
||||||
|
}
|
||||||
|
istate->cache_changed = 1;
|
||||||
|
istate->cache_nr = j;
|
||||||
|
}
|
||||||
|
|
||||||
int remove_file_from_index(struct index_state *istate, const char *path)
|
int remove_file_from_index(struct index_state *istate, const char *path)
|
||||||
{
|
{
|
||||||
int pos = index_name_pos(istate, path, strlen(path));
|
int pos = index_name_pos(istate, path, strlen(path));
|
||||||
|
@ -1139,8 +1176,10 @@ static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_en
|
||||||
size_t len;
|
size_t len;
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
||||||
ce->ce_ctime = ntohl(ondisk->ctime.sec);
|
ce->ce_ctime.sec = ntohl(ondisk->ctime.sec);
|
||||||
ce->ce_mtime = ntohl(ondisk->mtime.sec);
|
ce->ce_mtime.sec = ntohl(ondisk->mtime.sec);
|
||||||
|
ce->ce_ctime.nsec = ntohl(ondisk->ctime.nsec);
|
||||||
|
ce->ce_mtime.nsec = ntohl(ondisk->mtime.nsec);
|
||||||
ce->ce_dev = ntohl(ondisk->dev);
|
ce->ce_dev = ntohl(ondisk->dev);
|
||||||
ce->ce_ino = ntohl(ondisk->ino);
|
ce->ce_ino = ntohl(ondisk->ino);
|
||||||
ce->ce_mode = ntohl(ondisk->mode);
|
ce->ce_mode = ntohl(ondisk->mode);
|
||||||
|
@ -1206,7 +1245,8 @@ int read_index_from(struct index_state *istate, const char *path)
|
||||||
return istate->cache_nr;
|
return istate->cache_nr;
|
||||||
|
|
||||||
errno = ENOENT;
|
errno = ENOENT;
|
||||||
istate->timestamp = 0;
|
istate->timestamp.sec = 0;
|
||||||
|
istate->timestamp.nsec = 0;
|
||||||
fd = open(path, O_RDONLY);
|
fd = open(path, O_RDONLY);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
if (errno == ENOENT)
|
if (errno == ENOENT)
|
||||||
|
@ -1258,7 +1298,9 @@ int read_index_from(struct index_state *istate, const char *path)
|
||||||
src_offset += ondisk_ce_size(ce);
|
src_offset += ondisk_ce_size(ce);
|
||||||
dst_offset += ce_size(ce);
|
dst_offset += ce_size(ce);
|
||||||
}
|
}
|
||||||
istate->timestamp = st.st_mtime;
|
istate->timestamp.sec = st.st_mtime;
|
||||||
|
istate->timestamp.nsec = ST_MTIME_NSEC(st);
|
||||||
|
|
||||||
while (src_offset <= mmap_size - 20 - 8) {
|
while (src_offset <= mmap_size - 20 - 8) {
|
||||||
/* After an array of active_nr index entries,
|
/* After an array of active_nr index entries,
|
||||||
* there can be arbitrary number of extended
|
* there can be arbitrary number of extended
|
||||||
|
@ -1288,14 +1330,15 @@ unmap:
|
||||||
|
|
||||||
int is_index_unborn(struct index_state *istate)
|
int is_index_unborn(struct index_state *istate)
|
||||||
{
|
{
|
||||||
return (!istate->cache_nr && !istate->alloc && !istate->timestamp);
|
return (!istate->cache_nr && !istate->alloc && !istate->timestamp.sec);
|
||||||
}
|
}
|
||||||
|
|
||||||
int discard_index(struct index_state *istate)
|
int discard_index(struct index_state *istate)
|
||||||
{
|
{
|
||||||
istate->cache_nr = 0;
|
istate->cache_nr = 0;
|
||||||
istate->cache_changed = 0;
|
istate->cache_changed = 0;
|
||||||
istate->timestamp = 0;
|
istate->timestamp.sec = 0;
|
||||||
|
istate->timestamp.nsec = 0;
|
||||||
istate->name_hash_initialized = 0;
|
istate->name_hash_initialized = 0;
|
||||||
free_hash(&istate->name_hash);
|
free_hash(&istate->name_hash);
|
||||||
cache_tree_free(&(istate->cache_tree));
|
cache_tree_free(&(istate->cache_tree));
|
||||||
|
@ -1441,10 +1484,10 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce)
|
||||||
struct ondisk_cache_entry *ondisk = xcalloc(1, size);
|
struct ondisk_cache_entry *ondisk = xcalloc(1, size);
|
||||||
char *name;
|
char *name;
|
||||||
|
|
||||||
ondisk->ctime.sec = htonl(ce->ce_ctime);
|
ondisk->ctime.sec = htonl(ce->ce_ctime.sec);
|
||||||
ondisk->ctime.nsec = 0;
|
ondisk->mtime.sec = htonl(ce->ce_mtime.sec);
|
||||||
ondisk->mtime.sec = htonl(ce->ce_mtime);
|
ondisk->ctime.nsec = htonl(ce->ce_ctime.nsec);
|
||||||
ondisk->mtime.nsec = 0;
|
ondisk->mtime.nsec = htonl(ce->ce_mtime.nsec);
|
||||||
ondisk->dev = htonl(ce->ce_dev);
|
ondisk->dev = htonl(ce->ce_dev);
|
||||||
ondisk->ino = htonl(ce->ce_ino);
|
ondisk->ino = htonl(ce->ce_ino);
|
||||||
ondisk->mode = htonl(ce->ce_mode);
|
ondisk->mode = htonl(ce->ce_mode);
|
||||||
|
@ -1466,13 +1509,14 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce)
|
||||||
return ce_write(c, fd, ondisk, size);
|
return ce_write(c, fd, ondisk, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
int write_index(const struct index_state *istate, int newfd)
|
int write_index(struct index_state *istate, int newfd)
|
||||||
{
|
{
|
||||||
git_SHA_CTX c;
|
git_SHA_CTX c;
|
||||||
struct cache_header hdr;
|
struct cache_header hdr;
|
||||||
int i, err, removed, extended;
|
int i, err, removed, extended;
|
||||||
struct cache_entry **cache = istate->cache;
|
struct cache_entry **cache = istate->cache;
|
||||||
int entries = istate->cache_nr;
|
int entries = istate->cache_nr;
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
for (i = removed = extended = 0; i < entries; i++) {
|
for (i = removed = extended = 0; i < entries; i++) {
|
||||||
if (cache[i]->ce_flags & CE_REMOVE)
|
if (cache[i]->ce_flags & CE_REMOVE)
|
||||||
|
@ -1516,7 +1560,12 @@ int write_index(const struct index_state *istate, int newfd)
|
||||||
if (err)
|
if (err)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return ce_flush(&c, newfd);
|
|
||||||
|
if (ce_flush(&c, newfd) || fstat(newfd, &st))
|
||||||
|
return -1;
|
||||||
|
istate->timestamp.sec = (unsigned int)st.st_mtime;
|
||||||
|
istate->timestamp.nsec = ST_MTIME_NSEC(st);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
171
symlinks.c
171
symlinks.c
|
@ -1,5 +1,37 @@
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the length (on a path component basis) of the longest
|
||||||
|
* common prefix match of 'name_a' and 'name_b'.
|
||||||
|
*/
|
||||||
|
static int longest_path_match(const char *name_a, int len_a,
|
||||||
|
const char *name_b, int len_b,
|
||||||
|
int *previous_slash)
|
||||||
|
{
|
||||||
|
int max_len, match_len = 0, match_len_prev = 0, i = 0;
|
||||||
|
|
||||||
|
max_len = len_a < len_b ? len_a : len_b;
|
||||||
|
while (i < max_len && name_a[i] == name_b[i]) {
|
||||||
|
if (name_a[i] == '/') {
|
||||||
|
match_len_prev = match_len;
|
||||||
|
match_len = i;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Is 'name_b' a substring of 'name_a', the other way around,
|
||||||
|
* or is 'name_a' and 'name_b' the exact same string?
|
||||||
|
*/
|
||||||
|
if (i >= max_len && ((len_a > len_b && name_a[len_b] == '/') ||
|
||||||
|
(len_a < len_b && name_b[len_a] == '/') ||
|
||||||
|
(len_a == len_b))) {
|
||||||
|
match_len_prev = match_len;
|
||||||
|
match_len = i;
|
||||||
|
}
|
||||||
|
*previous_slash = match_len_prev;
|
||||||
|
return match_len;
|
||||||
|
}
|
||||||
|
|
||||||
static struct cache_def {
|
static struct cache_def {
|
||||||
char path[PATH_MAX + 1];
|
char path[PATH_MAX + 1];
|
||||||
int len;
|
int len;
|
||||||
|
@ -8,44 +40,15 @@ static struct cache_def {
|
||||||
int prefix_len_stat_func;
|
int prefix_len_stat_func;
|
||||||
} cache;
|
} cache;
|
||||||
|
|
||||||
/*
|
static inline void reset_lstat_cache(void)
|
||||||
* Returns the length (on a path component basis) of the longest
|
|
||||||
* common prefix match of 'name' and the cached path string.
|
|
||||||
*/
|
|
||||||
static inline int longest_match_lstat_cache(int len, const char *name,
|
|
||||||
int *previous_slash)
|
|
||||||
{
|
|
||||||
int max_len, match_len = 0, match_len_prev = 0, i = 0;
|
|
||||||
|
|
||||||
max_len = len < cache.len ? len : cache.len;
|
|
||||||
while (i < max_len && name[i] == cache.path[i]) {
|
|
||||||
if (name[i] == '/') {
|
|
||||||
match_len_prev = match_len;
|
|
||||||
match_len = i;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
/* Is the cached path string a substring of 'name'? */
|
|
||||||
if (i == cache.len && cache.len < len && name[cache.len] == '/') {
|
|
||||||
match_len_prev = match_len;
|
|
||||||
match_len = cache.len;
|
|
||||||
/* Is 'name' a substring of the cached path string? */
|
|
||||||
} else if ((i == len && len < cache.len && cache.path[len] == '/') ||
|
|
||||||
(i == len && len == cache.len)) {
|
|
||||||
match_len_prev = match_len;
|
|
||||||
match_len = len;
|
|
||||||
}
|
|
||||||
*previous_slash = match_len_prev;
|
|
||||||
return match_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void reset_lstat_cache(int track_flags, int prefix_len_stat_func)
|
|
||||||
{
|
{
|
||||||
cache.path[0] = '\0';
|
cache.path[0] = '\0';
|
||||||
cache.len = 0;
|
cache.len = 0;
|
||||||
cache.flags = 0;
|
cache.flags = 0;
|
||||||
cache.track_flags = track_flags;
|
/*
|
||||||
cache.prefix_len_stat_func = prefix_len_stat_func;
|
* The track_flags and prefix_len_stat_func members is only
|
||||||
|
* set by the safeguard rule inside lstat_cache()
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
#define FL_DIR (1 << 0)
|
#define FL_DIR (1 << 0)
|
||||||
|
@ -67,7 +70,7 @@ static inline void reset_lstat_cache(int track_flags, int prefix_len_stat_func)
|
||||||
* of the prefix, where the cache should use the stat() function
|
* of the prefix, where the cache should use the stat() function
|
||||||
* instead of the lstat() function to test each path component.
|
* instead of the lstat() function to test each path component.
|
||||||
*/
|
*/
|
||||||
static int lstat_cache(int len, const char *name,
|
static int lstat_cache(const char *name, int len,
|
||||||
int track_flags, int prefix_len_stat_func)
|
int track_flags, int prefix_len_stat_func)
|
||||||
{
|
{
|
||||||
int match_len, last_slash, last_slash_dir, previous_slash;
|
int match_len, last_slash, last_slash_dir, previous_slash;
|
||||||
|
@ -77,11 +80,13 @@ static int lstat_cache(int len, const char *name,
|
||||||
if (cache.track_flags != track_flags ||
|
if (cache.track_flags != track_flags ||
|
||||||
cache.prefix_len_stat_func != prefix_len_stat_func) {
|
cache.prefix_len_stat_func != prefix_len_stat_func) {
|
||||||
/*
|
/*
|
||||||
* As a safeguard we clear the cache if the values of
|
* As a safeguard rule we clear the cache if the
|
||||||
* track_flags and/or prefix_len_stat_func does not
|
* values of track_flags and/or prefix_len_stat_func
|
||||||
* match with the last supplied values.
|
* does not match with the last supplied values.
|
||||||
*/
|
*/
|
||||||
reset_lstat_cache(track_flags, prefix_len_stat_func);
|
reset_lstat_cache();
|
||||||
|
cache.track_flags = track_flags;
|
||||||
|
cache.prefix_len_stat_func = prefix_len_stat_func;
|
||||||
match_len = last_slash = 0;
|
match_len = last_slash = 0;
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
|
@ -89,7 +94,8 @@ static int lstat_cache(int len, const char *name,
|
||||||
* the 2 "excluding" path types.
|
* the 2 "excluding" path types.
|
||||||
*/
|
*/
|
||||||
match_len = last_slash =
|
match_len = last_slash =
|
||||||
longest_match_lstat_cache(len, name, &previous_slash);
|
longest_path_match(name, len, cache.path, cache.len,
|
||||||
|
&previous_slash);
|
||||||
match_flags = cache.flags & track_flags & (FL_NOENT|FL_SYMLINK);
|
match_flags = cache.flags & track_flags & (FL_NOENT|FL_SYMLINK);
|
||||||
if (match_flags && match_len == cache.len)
|
if (match_flags && match_len == cache.len)
|
||||||
return match_flags;
|
return match_flags;
|
||||||
|
@ -153,7 +159,7 @@ static int lstat_cache(int len, const char *name,
|
||||||
cache.path[last_slash] = '\0';
|
cache.path[last_slash] = '\0';
|
||||||
cache.len = last_slash;
|
cache.len = last_slash;
|
||||||
cache.flags = save_flags;
|
cache.flags = save_flags;
|
||||||
} else if (track_flags & FL_DIR &&
|
} else if ((track_flags & FL_DIR) &&
|
||||||
last_slash_dir > 0 && last_slash_dir <= PATH_MAX) {
|
last_slash_dir > 0 && last_slash_dir <= PATH_MAX) {
|
||||||
/*
|
/*
|
||||||
* We have a separate test for the directory case,
|
* We have a separate test for the directory case,
|
||||||
|
@ -170,7 +176,7 @@ static int lstat_cache(int len, const char *name,
|
||||||
cache.len = last_slash_dir;
|
cache.len = last_slash_dir;
|
||||||
cache.flags = FL_DIR;
|
cache.flags = FL_DIR;
|
||||||
} else {
|
} else {
|
||||||
reset_lstat_cache(track_flags, prefix_len_stat_func);
|
reset_lstat_cache();
|
||||||
}
|
}
|
||||||
return ret_flags;
|
return ret_flags;
|
||||||
}
|
}
|
||||||
|
@ -179,19 +185,19 @@ static int lstat_cache(int len, const char *name,
|
||||||
* Invalidate the given 'name' from the cache, if 'name' matches
|
* Invalidate the given 'name' from the cache, if 'name' matches
|
||||||
* completely with the cache.
|
* completely with the cache.
|
||||||
*/
|
*/
|
||||||
void invalidate_lstat_cache(int len, const char *name)
|
void invalidate_lstat_cache(const char *name, int len)
|
||||||
{
|
{
|
||||||
int match_len, previous_slash;
|
int match_len, previous_slash;
|
||||||
|
|
||||||
match_len = longest_match_lstat_cache(len, name, &previous_slash);
|
match_len = longest_path_match(name, len, cache.path, cache.len,
|
||||||
|
&previous_slash);
|
||||||
if (len == match_len) {
|
if (len == match_len) {
|
||||||
if ((cache.track_flags & FL_DIR) && previous_slash > 0) {
|
if ((cache.track_flags & FL_DIR) && previous_slash > 0) {
|
||||||
cache.path[previous_slash] = '\0';
|
cache.path[previous_slash] = '\0';
|
||||||
cache.len = previous_slash;
|
cache.len = previous_slash;
|
||||||
cache.flags = FL_DIR;
|
cache.flags = FL_DIR;
|
||||||
} else
|
} else
|
||||||
reset_lstat_cache(cache.track_flags,
|
reset_lstat_cache();
|
||||||
cache.prefix_len_stat_func);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +206,7 @@ void invalidate_lstat_cache(int len, const char *name)
|
||||||
*/
|
*/
|
||||||
void clear_lstat_cache(void)
|
void clear_lstat_cache(void)
|
||||||
{
|
{
|
||||||
reset_lstat_cache(0, 0);
|
reset_lstat_cache();
|
||||||
}
|
}
|
||||||
|
|
||||||
#define USE_ONLY_LSTAT 0
|
#define USE_ONLY_LSTAT 0
|
||||||
|
@ -208,9 +214,9 @@ void clear_lstat_cache(void)
|
||||||
/*
|
/*
|
||||||
* Return non-zero if path 'name' has a leading symlink component
|
* Return non-zero if path 'name' has a leading symlink component
|
||||||
*/
|
*/
|
||||||
int has_symlink_leading_path(int len, const char *name)
|
int has_symlink_leading_path(const char *name, int len)
|
||||||
{
|
{
|
||||||
return lstat_cache(len, name,
|
return lstat_cache(name, len,
|
||||||
FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) &
|
FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) &
|
||||||
FL_SYMLINK;
|
FL_SYMLINK;
|
||||||
}
|
}
|
||||||
|
@ -219,9 +225,9 @@ int has_symlink_leading_path(int len, const char *name)
|
||||||
* Return non-zero if path 'name' has a leading symlink component or
|
* Return non-zero if path 'name' has a leading symlink component or
|
||||||
* if some leading path component does not exists.
|
* if some leading path component does not exists.
|
||||||
*/
|
*/
|
||||||
int has_symlink_or_noent_leading_path(int len, const char *name)
|
int has_symlink_or_noent_leading_path(const char *name, int len)
|
||||||
{
|
{
|
||||||
return lstat_cache(len, name,
|
return lstat_cache(name, len,
|
||||||
FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) &
|
FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) &
|
||||||
(FL_SYMLINK|FL_NOENT);
|
(FL_SYMLINK|FL_NOENT);
|
||||||
}
|
}
|
||||||
|
@ -233,9 +239,68 @@ int has_symlink_or_noent_leading_path(int len, const char *name)
|
||||||
* 'prefix_len', thus we then allow for symlinks in the prefix part as
|
* 'prefix_len', thus we then allow for symlinks in the prefix part as
|
||||||
* long as those points to real existing directories.
|
* long as those points to real existing directories.
|
||||||
*/
|
*/
|
||||||
int has_dirs_only_path(int len, const char *name, int prefix_len)
|
int has_dirs_only_path(const char *name, int len, int prefix_len)
|
||||||
{
|
{
|
||||||
return lstat_cache(len, name,
|
return lstat_cache(name, len,
|
||||||
FL_DIR|FL_FULLPATH, prefix_len) &
|
FL_DIR|FL_FULLPATH, prefix_len) &
|
||||||
FL_DIR;
|
FL_DIR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct removal_def {
|
||||||
|
char path[PATH_MAX];
|
||||||
|
int len;
|
||||||
|
} removal;
|
||||||
|
|
||||||
|
static void do_remove_scheduled_dirs(int new_len)
|
||||||
|
{
|
||||||
|
while (removal.len > new_len) {
|
||||||
|
removal.path[removal.len] = '\0';
|
||||||
|
if (rmdir(removal.path))
|
||||||
|
break;
|
||||||
|
do {
|
||||||
|
removal.len--;
|
||||||
|
} while (removal.len > new_len &&
|
||||||
|
removal.path[removal.len] != '/');
|
||||||
|
}
|
||||||
|
removal.len = new_len;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void schedule_dir_for_removal(const char *name, int len)
|
||||||
|
{
|
||||||
|
int match_len, last_slash, i, previous_slash;
|
||||||
|
|
||||||
|
match_len = last_slash = i =
|
||||||
|
longest_path_match(name, len, removal.path, removal.len,
|
||||||
|
&previous_slash);
|
||||||
|
/* Find last slash inside 'name' */
|
||||||
|
while (i < len) {
|
||||||
|
if (name[i] == '/')
|
||||||
|
last_slash = i;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we are about to go down the directory tree, we check if
|
||||||
|
* we must first go upwards the tree, such that we then can
|
||||||
|
* remove possible empty directories as we go upwards.
|
||||||
|
*/
|
||||||
|
if (match_len < last_slash && match_len < removal.len)
|
||||||
|
do_remove_scheduled_dirs(match_len);
|
||||||
|
/*
|
||||||
|
* If we go deeper down the directory tree, we only need to
|
||||||
|
* save the new path components as we go down.
|
||||||
|
*/
|
||||||
|
if (match_len < last_slash) {
|
||||||
|
memcpy(&removal.path[match_len], &name[match_len],
|
||||||
|
last_slash - match_len);
|
||||||
|
removal.len = last_slash;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_scheduled_dirs(void)
|
||||||
|
{
|
||||||
|
do_remove_scheduled_dirs(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
|
@ -52,36 +52,17 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
|
||||||
add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|ADD_CACHE_SKIP_DFCHECK);
|
add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|ADD_CACHE_SKIP_DFCHECK);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Unlink the last component and attempt to remove leading
|
/*
|
||||||
* directories, in case this unlink is the removal of the
|
* Unlink the last component and schedule the leading directories for
|
||||||
* last entry in the directory -- empty directories are removed.
|
* removal, such that empty directories get removed.
|
||||||
*/
|
*/
|
||||||
static void unlink_entry(struct cache_entry *ce)
|
static void unlink_entry(struct cache_entry *ce)
|
||||||
{
|
{
|
||||||
char *cp, *prev;
|
if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce)))
|
||||||
char *name = ce->name;
|
|
||||||
|
|
||||||
if (has_symlink_or_noent_leading_path(ce_namelen(ce), ce->name))
|
|
||||||
return;
|
return;
|
||||||
if (unlink(name))
|
if (unlink(ce->name))
|
||||||
return;
|
return;
|
||||||
prev = NULL;
|
schedule_dir_for_removal(ce->name, ce_namelen(ce));
|
||||||
while (1) {
|
|
||||||
int status;
|
|
||||||
cp = strrchr(name, '/');
|
|
||||||
if (prev)
|
|
||||||
*prev = '/';
|
|
||||||
if (!cp)
|
|
||||||
break;
|
|
||||||
|
|
||||||
*cp = 0;
|
|
||||||
status = rmdir(name);
|
|
||||||
if (status) {
|
|
||||||
*cp = '/';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
prev = cp;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct checkout state;
|
static struct checkout state;
|
||||||
|
@ -112,11 +93,10 @@ static int check_updates(struct unpack_trees_options *o)
|
||||||
display_progress(progress, ++cnt);
|
display_progress(progress, ++cnt);
|
||||||
if (o->update)
|
if (o->update)
|
||||||
unlink_entry(ce);
|
unlink_entry(ce);
|
||||||
remove_index_entry_at(&o->result, i);
|
|
||||||
i--;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
remove_marked_cache_entries(&o->result);
|
||||||
|
remove_scheduled_dirs();
|
||||||
|
|
||||||
for (i = 0; i < index->cache_nr; i++) {
|
for (i = 0; i < index->cache_nr; i++) {
|
||||||
struct cache_entry *ce = index->cache[i];
|
struct cache_entry *ce = index->cache[i];
|
||||||
|
@ -380,8 +360,10 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
|
||||||
|
|
||||||
memset(&o->result, 0, sizeof(o->result));
|
memset(&o->result, 0, sizeof(o->result));
|
||||||
o->result.initialized = 1;
|
o->result.initialized = 1;
|
||||||
if (o->src_index)
|
if (o->src_index) {
|
||||||
o->result.timestamp = o->src_index->timestamp;
|
o->result.timestamp.sec = o->src_index->timestamp.sec;
|
||||||
|
o->result.timestamp.nsec = o->src_index->timestamp.nsec;
|
||||||
|
}
|
||||||
o->merge_size = len;
|
o->merge_size = len;
|
||||||
|
|
||||||
if (!dfc)
|
if (!dfc)
|
||||||
|
@ -446,7 +428,7 @@ static int verify_uptodate(struct cache_entry *ce,
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
if (o->index_only || o->reset)
|
if (o->index_only || o->reset || ce_uptodate(ce))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!lstat(ce->name, &st)) {
|
if (!lstat(ce->name, &st)) {
|
||||||
|
@ -583,7 +565,7 @@ static int verify_absent(struct cache_entry *ce, const char *action,
|
||||||
if (o->index_only || o->reset || !o->update)
|
if (o->index_only || o->reset || !o->update)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (has_symlink_or_noent_leading_path(ce_namelen(ce), ce->name))
|
if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!lstat(ce->name, &st)) {
|
if (!lstat(ce->name, &st)) {
|
||||||
|
|
Loading…
Reference in New Issue