Commit 003808b3 by Vicent Marti

Merge branch 'config-refresh' into development

parents a9db123b c1f61af6
......@@ -56,6 +56,7 @@ struct git_config_file {
int (*set_multivar)(git_config_file *cfg, const char *name, const char *regexp, const char *value);
int (*del)(struct git_config_file *, const char *key);
int (*foreach)(struct git_config_file *, const char *, int (*fn)(const git_config_entry *, void *), void *data);
int (*refresh)(struct git_config_file *);
void (*free)(struct git_config_file *);
};
......@@ -243,6 +244,19 @@ GIT_EXTERN(int) git_config_open_level(
unsigned int level);
/**
* Reload changed config files
*
* A config file may be changed on disk out from under the in-memory
* config object. This function causes us to look for files that have
* been modified since we last loaded them and refresh the config with
* the latest information.
*
* @param cfg The configuration to refresh
* @return 0 or an error code
*/
GIT_EXTERN(int) git_config_refresh(git_config *cfg);
/**
* Free the configuration and its associated memory and files
*
* @param cfg the configuration to free
......
......@@ -261,32 +261,26 @@ bool git_attr_cache__is_cached(
static int load_attr_file(
const char **data,
git_attr_file_stat_sig *sig,
git_futils_filestamp *stamp,
const char *filename)
{
int error;
git_buf content = GIT_BUF_INIT;
struct stat st;
if (p_stat(filename, &st) < 0)
return GIT_ENOTFOUND;
error = git_futils_filestamp_check(stamp, filename);
if (error < 0)
return error;
if (sig != NULL &&
(git_time_t)st.st_mtime == sig->seconds &&
(git_off_t)st.st_size == sig->size &&
(unsigned int)st.st_ino == sig->ino)
/* if error == 0, then file is up to date. By returning GIT_ENOTFOUND,
* we tell the caller not to reparse this file...
*/
if (!error)
return GIT_ENOTFOUND;
error = git_futils_readbuffer_updated(&content, filename, NULL, NULL);
error = git_futils_readbuffer(&content, filename);
if (error < 0)
return error;
if (sig != NULL) {
sig->seconds = (git_time_t)st.st_mtime;
sig->size = (git_off_t)st.st_size;
sig->ino = (unsigned int)st.st_ino;
}
*data = git_buf_detach(&content);
return 0;
......@@ -386,7 +380,7 @@ int git_attr_cache__push_file(
git_attr_cache *cache = git_repository_attr_cache(repo);
git_attr_file *file = NULL;
git_blob *blob = NULL;
git_attr_file_stat_sig st;
git_futils_filestamp stamp;
assert(filename && stack);
......@@ -408,12 +402,10 @@ int git_attr_cache__push_file(
/* if not in cache, load data, parse, and cache */
if (source == GIT_ATTR_FILE_FROM_FILE) {
if (file)
memcpy(&st, &file->cache_data.st, sizeof(st));
else
memset(&st, 0, sizeof(st));
git_futils_filestamp_set(
&stamp, file ? &file->cache_data.stamp : NULL);
error = load_attr_file(&content, &st, filename);
error = load_attr_file(&content, &stamp, filename);
} else {
error = load_attr_blob_from_index(&content, &blob,
repo, file ? &file->cache_data.oid : NULL, relfile);
......@@ -448,7 +440,7 @@ int git_attr_cache__push_file(
if (blob)
git_oid_cpy(&file->cache_data.oid, git_object_id((git_object *)blob));
else
memcpy(&file->cache_data.st, &st, sizeof(st));
git_futils_filestamp_set(&file->cache_data.stamp, &stamp);
finish:
/* push file onto vector if we found one*/
......
......@@ -11,6 +11,7 @@
#include "vector.h"
#include "pool.h"
#include "buffer.h"
#include "fileops.h"
#define GIT_ATTR_FILE ".gitattributes"
#define GIT_ATTR_FILE_INREPO "info/attributes"
......@@ -54,19 +55,13 @@ typedef struct {
} git_attr_assignment;
typedef struct {
git_time_t seconds;
git_off_t size;
unsigned int ino;
} git_attr_file_stat_sig;
typedef struct {
char *key; /* cache "source#path" this was loaded from */
git_vector rules; /* vector of <rule*> or <fnmatch*> */
git_pool *pool;
bool pool_is_allocated;
union {
git_oid oid;
git_attr_file_stat_sig st;
git_futils_filestamp stamp;
} cache_data;
} git_attr_file;
......
......@@ -267,6 +267,20 @@ int git_config_add_file(
return 0;
}
int git_config_refresh(git_config *cfg)
{
int error = 0;
unsigned int i;
for (i = 0; i < cfg->files.length && !error; ++i) {
file_internal *internal = git_vector_get(&cfg->files, i);
git_config_file *file = internal->file;
error = file->refresh(file);
}
return error;
}
/*
* Loop over all the variables
*/
......
......@@ -76,6 +76,10 @@ typedef struct {
} reader;
char *file_path;
time_t file_mtime;
size_t file_size;
unsigned int level;
} diskfile_backend;
static int config_parse(diskfile_backend *cfg_file, unsigned int level);
......@@ -150,25 +154,53 @@ static int config_open(git_config_file *cfg, unsigned int level)
int res;
diskfile_backend *b = (diskfile_backend *)cfg;
b->level = level;
b->values = git_strmap_alloc();
GITERR_CHECK_ALLOC(b->values);
git_buf_init(&b->reader.buffer, 0);
res = git_futils_readbuffer(&b->reader.buffer, b->file_path);
res = git_futils_readbuffer_updated(
&b->reader.buffer, b->file_path, &b->file_mtime, &b->file_size, NULL);
/* It's fine if the file doesn't exist */
if (res == GIT_ENOTFOUND)
return 0;
if (res < 0 || config_parse(b, level) < 0) {
if (res < 0 || (res = config_parse(b, level)) < 0) {
free_vars(b->values);
b->values = NULL;
}
git_buf_free(&b->reader.buffer);
return -1;
return res;
}
static int config_refresh(git_config_file *cfg)
{
int res, updated = 0;
diskfile_backend *b = (diskfile_backend *)cfg;
git_strmap *old_values;
res = git_futils_readbuffer_updated(
&b->reader.buffer, b->file_path, &b->file_mtime, &b->file_size, &updated);
if (res < 0 || !updated)
return (res == GIT_ENOTFOUND) ? 0 : res;
/* need to reload - store old values and prep for reload */
old_values = b->values;
b->values = git_strmap_alloc();
GITERR_CHECK_ALLOC(b->values);
if ((res = config_parse(b, b->level)) < 0) {
free_vars(b->values);
b->values = old_values;
} else {
free_vars(old_values);
}
git_buf_free(&b->reader.buffer);
return 0;
return res;
}
static void backend_free(git_config_file *_backend)
......@@ -527,6 +559,7 @@ int git_config_file__ondisk(git_config_file **out, const char *path)
backend->parent.set_multivar = config_set_multivar;
backend->parent.del = config_delete;
backend->parent.foreach = file_foreach;
backend->parent.refresh = config_refresh;
backend->parent.free = backend_free;
*out = (git_config_file *)backend;
......@@ -1197,8 +1230,12 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
git__free(section);
git__free(current_section);
/* refresh stats - if this errors, then commit will error too */
(void)git_filebuf_stats(&cfg->file_mtime, &cfg->file_size, &file);
result = git_filebuf_commit(&file, GIT_CONFIG_FILE_MODE);
git_buf_free(&cfg->reader.buffer);
return result;
rewrite_fail:
......
......@@ -466,3 +466,26 @@ int git_filebuf_printf(git_filebuf *file, const char *format, ...)
return res;
}
int git_filebuf_stats(time_t *mtime, size_t *size, git_filebuf *file)
{
int res;
struct stat st;
if (file->fd_is_open)
res = p_fstat(file->fd, &st);
else
res = p_stat(file->path_original, &st);
if (res < 0) {
giterr_set(GITERR_OS, "Could not get stat info for '%s'",
file->path_original);
return res;
}
if (mtime)
*mtime = st.st_mtime;
if (size)
*size = (size_t)st.st_size;
return 0;
}
......@@ -82,5 +82,6 @@ int git_filebuf_commit_at(git_filebuf *lock, const char *path, mode_t mode);
void git_filebuf_cleanup(git_filebuf *lock);
int git_filebuf_hash(git_oid *oid, git_filebuf *file);
int git_filebuf_flush(git_filebuf *file);
int git_filebuf_stats(time_t *mtime, size_t *size, git_filebuf *file);
#endif
......@@ -142,10 +142,11 @@ int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len)
}
int git_futils_readbuffer_updated(
git_buf *buf, const char *path, time_t *mtime, int *updated)
git_buf *buf, const char *path, time_t *mtime, size_t *size, int *updated)
{
git_file fd;
struct stat st;
bool changed = false;
assert(buf && path && *path);
......@@ -162,16 +163,25 @@ int git_futils_readbuffer_updated(
}
/*
* If we were given a time, we only want to read the file if it
* has been modified.
* If we were given a time and/or a size, we only want to read the file
* if it has been modified.
*/
if (mtime != NULL && *mtime >= st.st_mtime) {
if (size && *size != (size_t)st.st_size)
changed = true;
if (mtime && *mtime != st.st_mtime)
changed = true;
if (!size && !mtime)
changed = true;
if (!changed) {
p_close(fd);
return 0;
}
if (mtime != NULL)
*mtime = st.st_mtime;
if (size != NULL)
*size = (size_t)st.st_size;
if (git_futils_readbuffer_fd(buf, fd, (size_t)st.st_size) < 0) {
p_close(fd);
......@@ -188,7 +198,7 @@ int git_futils_readbuffer_updated(
int git_futils_readbuffer(git_buf *buf, const char *path)
{
return git_futils_readbuffer_updated(buf, path, NULL, NULL);
return git_futils_readbuffer_updated(buf, path, NULL, NULL, NULL);
}
int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode)
......@@ -660,3 +670,38 @@ int git_futils_cp_r(
return error;
}
int git_futils_filestamp_check(
git_futils_filestamp *stamp, const char *path)
{
struct stat st;
/* if the stamp is NULL, then always reload */
if (stamp == NULL)
return 1;
if (p_stat(path, &st) < 0)
return GIT_ENOTFOUND;
if (stamp->mtime == (git_time_t)st.st_mtime &&
stamp->size == (git_off_t)st.st_size &&
stamp->ino == (unsigned int)st.st_ino)
return 0;
stamp->mtime = (git_time_t)st.st_mtime;
stamp->size = (git_off_t)st.st_size;
stamp->ino = (unsigned int)st.st_ino;
return 1;
}
void git_futils_filestamp_set(
git_futils_filestamp *target, const git_futils_filestamp *source)
{
assert(target);
if (source)
memcpy(target, source, sizeof(*target));
else
memset(target, 0, sizeof(*target));
}
......@@ -18,7 +18,8 @@
* Read whole files into an in-memory buffer for processing
*/
extern int git_futils_readbuffer(git_buf *obj, const char *path);
extern int git_futils_readbuffer_updated(git_buf *obj, const char *path, time_t *mtime, int *updated);
extern int git_futils_readbuffer_updated(
git_buf *obj, const char *path, time_t *mtime, size_t *size, int *updated);
extern int git_futils_readbuffer_fd(git_buf *obj, git_file fd, size_t len);
/**
......@@ -266,4 +267,44 @@ extern int git_futils_find_system_file(git_buf *path, const char *filename);
*/
extern int git_futils_fake_symlink(const char *new, const char *old);
/**
* A file stamp represents a snapshot of information about a file that can
* be used to test if the file changes. This portable implementation is
* based on stat data about that file, but it is possible that OS specific
* versions could be implemented in the future.
*/
typedef struct {
git_time_t mtime;
git_off_t size;
unsigned int ino;
} git_futils_filestamp;
/**
* Compare stat information for file with reference info.
*
* This function updates the file stamp to current data for the given path
* and returns 0 if the file is up-to-date relative to the prior setting or
* 1 if the file has been changed. (This also may return GIT_ENOTFOUND if
* the file doesn't exist.)
*
* @param stamp File stamp to be checked
* @param path Path to stat and check if changed
* @return 0 if up-to-date, 1 if out-of-date, <0 on error
*/
extern int git_futils_filestamp_check(
git_futils_filestamp *stamp, const char *path);
/**
* Set or reset file stamp data
*
* This writes the target file stamp. If the source is NULL, this will set
* the target stamp to values that will definitely be out of date. If the
* source is not NULL, this copies the source values to the target.
*
* @param tgt File stamp to write to
* @param src File stamp to copy from or NULL to clear the target
*/
extern void git_futils_filestamp_set(
git_futils_filestamp *tgt, const git_futils_filestamp *src);
#endif /* INCLUDE_fileops_h__ */
......@@ -405,7 +405,7 @@ int git_index_read(git_index *index)
/* We don't want to update the mtime if we fail to parse the index */
mtime = index->last_modified;
error = git_futils_readbuffer_updated(
&buffer, index->index_file_path, &mtime, &updated);
&buffer, index->index_file_path, &mtime, NULL, &updated);
if (error < 0)
return error;
......
......@@ -123,7 +123,8 @@ static int reference_read(
if (git_buf_joinpath(&path, repo_path, ref_name) < 0)
return -1;
result = git_futils_readbuffer_updated(file_content, path.ptr, mtime, updated);
result = git_futils_readbuffer_updated(
file_content, path.ptr, mtime, NULL, updated);
git_buf_free(&path);
return result;
......
#include "clar_libgit2.h"
#define TEST_FILE "config.refresh"
void test_config_refresh__initialize(void)
{
}
void test_config_refresh__cleanup(void)
{
cl_fixture_cleanup(TEST_FILE);
}
void test_config_refresh__update_value(void)
{
git_config *cfg;
int32_t v;
cl_git_mkfile(TEST_FILE, "[section]\n\tvalue = 1\n\n");
/* By freeing the config, we make sure we flush the values */
cl_git_pass(git_config_open_ondisk(&cfg, TEST_FILE));
cl_git_pass(git_config_get_int32(&v, cfg, "section.value"));
cl_assert_equal_i(1, v);
cl_git_rewritefile(TEST_FILE, "[section]\n\tvalue = 10\n\n");
cl_git_pass(git_config_get_int32(&v, cfg, "section.value"));
cl_assert_equal_i(1, v);
cl_git_pass(git_config_refresh(cfg));
cl_git_pass(git_config_get_int32(&v, cfg, "section.value"));
cl_assert_equal_i(10, v);
git_config_free(cfg);
}
void test_config_refresh__delete_value(void)
{
git_config *cfg;
int32_t v;
cl_git_mkfile(TEST_FILE, "[section]\n\tvalue = 1\n\n");
/* By freeing the config, we make sure we flush the values */
cl_git_pass(git_config_open_ondisk(&cfg, TEST_FILE));
cl_git_pass(git_config_get_int32(&v, cfg, "section.value"));
cl_assert_equal_i(1, v);
cl_git_fail(git_config_get_int32(&v, cfg, "section.newval"));
cl_git_rewritefile(TEST_FILE, "[section]\n\tnewval = 10\n\n");
cl_git_pass(git_config_get_int32(&v, cfg, "section.value"));
cl_assert_equal_i(1, v);
cl_git_fail(git_config_get_int32(&v, cfg, "section.newval"));
cl_git_pass(git_config_refresh(cfg));
cl_git_fail(git_config_get_int32(&v, cfg, "section.value"));
cl_git_pass(git_config_get_int32(&v, cfg, "section.newval"));
cl_assert_equal_i(10, v);
git_config_free(cfg);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment