Skip to content

Commit ba9725a

Browse files
authored
Merge pull request libgit2#5132 from pks-t/pks/config-stat-cache
config_file: implement stat cache to avoid repeated rehashing
2 parents 398412c + 2ba7020 commit ba9725a

File tree

8 files changed

+184
-52
lines changed

8 files changed

+184
-52
lines changed

cmake/Modules/FindStatNsec.cmake

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
INCLUDE(FeatureSummary)
2+
13
CHECK_STRUCT_HAS_MEMBER ("struct stat" st_mtim "sys/types.h;sys/stat.h"
24
HAVE_STRUCT_STAT_ST_MTIM LANGUAGE C)
35
CHECK_STRUCT_HAS_MEMBER ("struct stat" st_mtimespec "sys/types.h;sys/stat.h"
@@ -17,4 +19,8 @@ ENDIF()
1719

1820
IF (HAVE_STRUCT_STAT_NSEC OR WIN32)
1921
OPTION( USE_NSEC "Care about sub-second file mtimes and ctimes" ON )
22+
ELSE()
23+
SET(USE_NSEC OFF)
2024
ENDIF()
25+
26+
ADD_FEATURE_INFO(nanoseconds USE_NSEC "whether to use sub-second file mtimes and ctimes")

examples/common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ extern int lg2_blame(git_repository *repo, int argc, char **argv);
3939
extern int lg2_cat_file(git_repository *repo, int argc, char **argv);
4040
extern int lg2_checkout(git_repository *repo, int argc, char **argv);
4141
extern int lg2_clone(git_repository *repo, int argc, char **argv);
42+
extern int lg2_config(git_repository *repo, int argc, char **argv);
4243
extern int lg2_describe(git_repository *repo, int argc, char **argv);
4344
extern int lg2_diff(git_repository *repo, int argc, char **argv);
4445
extern int lg2_fetch(git_repository *repo, int argc, char **argv);

examples/config.c

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* libgit2 "config" example - shows how to use the config API
3+
*
4+
* Written by the libgit2 contributors
5+
*
6+
* To the extent possible under law, the author(s) have dedicated all copyright
7+
* and related and neighboring rights to this software to the public domain
8+
* worldwide. This software is distributed without any warranty.
9+
*
10+
* You should have received a copy of the CC0 Public Domain Dedication along
11+
* with this software. If not, see
12+
* <http://creativecommons.org/publicdomain/zero/1.0/>.
13+
*/
14+
15+
#include "common.h"
16+
17+
static int config_get(git_config *cfg, const char *key)
18+
{
19+
git_config_entry *entry;
20+
int error;
21+
22+
if ((error = git_config_get_entry(&entry, cfg, key)) < 0) {
23+
if (error != GIT_ENOTFOUND)
24+
printf("Unable to get configuration: %s\n", git_error_last()->message);
25+
return 1;
26+
}
27+
28+
puts(entry->value);
29+
return 0;
30+
}
31+
32+
static int config_set(git_config *cfg, const char *key, const char *value)
33+
{
34+
if (git_config_set_string(cfg, key, value) < 0) {
35+
printf("Unable to set configuration: %s\n", git_error_last()->message);
36+
return 1;
37+
}
38+
return 0;
39+
}
40+
41+
int lg2_config(git_repository *repo, int argc, char **argv)
42+
{
43+
git_config *cfg;
44+
int error;
45+
46+
if ((error = git_repository_config(&cfg, repo)) < 0) {
47+
printf("Unable to obtain repository config: %s\n", git_error_last()->message);
48+
goto out;
49+
}
50+
51+
if (argc == 2) {
52+
error = config_get(cfg, argv[1]);
53+
} else if (argc == 3) {
54+
error = config_set(cfg, argv[1], argv[2]);
55+
} else {
56+
printf("USAGE: %s config <KEY> [<VALUE>]\n", argv[0]);
57+
error = 1;
58+
}
59+
60+
out:
61+
return error;
62+
}

examples/lg2.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ struct {
1515
{ "cat-file", lg2_cat_file, 1 },
1616
{ "checkout", lg2_checkout, 1 },
1717
{ "clone", lg2_clone, 0 },
18+
{ "config", lg2_config, 1 },
1819
{ "describe", lg2_describe, 1 },
1920
{ "diff", lg2_diff, 1 },
2021
{ "fetch", lg2_fetch, 1 },

src/config_file.c

Lines changed: 103 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ typedef struct {
4444
git_filebuf locked_buf;
4545
git_buf locked_content;
4646

47-
struct config_file file;
47+
git_config_file file;
4848
} diskfile_backend;
4949

5050
typedef struct {
@@ -62,6 +62,7 @@ typedef struct {
6262
} diskfile_parse_state;
6363

6464
static int config_read(git_config_entries *entries, const git_repository *repo, git_config_file *file, git_config_level_t level, int depth);
65+
static int config_read_buffer(git_config_entries *entries, const git_repository *repo, git_config_file *file, git_config_level_t level, int depth, const char *buf, size_t buflen);
6566
static int config_write(diskfile_backend *cfg, const char *orig_key, const char *key, const p_regex_t *preg, const char *value);
6667
static char *escape_value(const char *ptr);
6768

@@ -95,9 +96,9 @@ static git_config_entries *diskfile_entries_take(diskfile_header *h)
9596
return entries;
9697
}
9798

98-
static void config_file_clear(struct config_file *file)
99+
static void config_file_clear(git_config_file *file)
99100
{
100-
struct config_file *include;
101+
git_config_file *include;
101102
uint32_t i;
102103

103104
if (file == NULL)
@@ -133,7 +134,7 @@ static int config_open(git_config_backend *cfg, git_config_level_t level, const
133134
return res;
134135
}
135136

136-
static int config_is_modified(int *modified, struct config_file *file)
137+
static int config_is_modified(int *modified, git_config_file *file)
137138
{
138139
git_config_file *include;
139140
git_buf buf = GIT_BUF_INIT;
@@ -143,6 +144,9 @@ static int config_is_modified(int *modified, struct config_file *file)
143144

144145
*modified = 0;
145146

147+
if (!git_futils_filestamp_check(&file->stamp, file->path))
148+
goto check_includes;
149+
146150
if ((error = git_futils_readbuffer(&buf, file->path)) < 0)
147151
goto out;
148152

@@ -154,6 +158,7 @@ static int config_is_modified(int *modified, struct config_file *file)
154158
goto out;
155159
}
156160

161+
check_includes:
157162
git_array_foreach(file->includes, i, include) {
158163
if ((error = config_is_modified(modified, include)) < 0 || *modified)
159164
goto out;
@@ -165,47 +170,73 @@ static int config_is_modified(int *modified, struct config_file *file)
165170
return error;
166171
}
167172

168-
static int config_refresh(git_config_backend *cfg)
173+
static int config_set_entries(git_config_backend *cfg, git_config_entries *entries)
169174
{
170175
diskfile_backend *b = (diskfile_backend *)cfg;
171-
git_config_entries *entries = NULL, *tmp;
176+
git_config_entries *old = NULL;
172177
git_config_file *include;
173-
int error, modified;
174-
uint32_t i;
178+
int error;
179+
size_t i;
175180

176181
if (b->header.parent.readonly)
177182
return config_error_readonly();
178183

179-
error = config_is_modified(&modified, &b->file);
180-
if (error < 0 && error != GIT_ENOTFOUND)
181-
goto out;
182-
183-
if (!modified)
184-
return 0;
185-
186-
if ((error = git_config_entries_new(&entries)) < 0)
187-
goto out;
188-
189-
/* Reparse the current configuration */
190-
git_array_foreach(b->file.includes, i, include) {
184+
git_array_foreach(b->file.includes, i, include)
191185
config_file_clear(include);
192-
}
193186
git_array_clear(b->file.includes);
194187

195-
if ((error = config_read(entries, b->header.repo, &b->file, b->header.level, 0)) < 0)
196-
goto out;
197-
198188
if ((error = git_mutex_lock(&b->header.values_mutex)) < 0) {
199189
git_error_set(GIT_ERROR_OS, "failed to lock config backend");
200190
goto out;
201191
}
202192

203-
tmp = b->header.entries;
193+
old = b->header.entries;
204194
b->header.entries = entries;
205-
entries = tmp;
206195

207196
git_mutex_unlock(&b->header.values_mutex);
208197

198+
out:
199+
git_config_entries_free(old);
200+
return error;
201+
}
202+
203+
static int config_refresh_from_buffer(git_config_backend *cfg, const char *buf, size_t buflen)
204+
{
205+
diskfile_backend *b = GIT_CONTAINER_OF(cfg, diskfile_backend, header.parent);
206+
git_config_entries *entries = NULL;
207+
int error;
208+
209+
if ((error = git_config_entries_new(&entries)) < 0 ||
210+
(error = config_read_buffer(entries, b->header.repo, &b->file,
211+
b->header.level, 0, buf, buflen)) < 0 ||
212+
(error = config_set_entries(cfg, entries)) < 0)
213+
goto out;
214+
215+
entries = NULL;
216+
out:
217+
git_config_entries_free(entries);
218+
return error;
219+
}
220+
221+
static int config_refresh(git_config_backend *cfg)
222+
{
223+
diskfile_backend *b = GIT_CONTAINER_OF(cfg, diskfile_backend, header.parent);
224+
git_config_entries *entries = NULL;
225+
int error, modified;
226+
227+
error = config_is_modified(&modified, &b->file);
228+
if (error < 0 && error != GIT_ENOTFOUND)
229+
goto out;
230+
231+
if (!modified)
232+
return 0;
233+
234+
if ((error = git_config_entries_new(&entries)) < 0 ||
235+
(error = config_read(entries, b->header.repo, &b->file, b->header.level, 0)) < 0 ||
236+
(error = config_set_entries(cfg, entries)) < 0)
237+
goto out;
238+
239+
entries = NULL;
209240
out:
210241
git_config_entries_free(entries);
211242

@@ -280,8 +311,6 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val
280311
if ((error = config_write(b, name, key, NULL, esc_value)) < 0)
281312
goto out;
282313

283-
error = config_refresh(cfg);
284-
285314
out:
286315
git_config_entries_free(entries);
287316
git__free(esc_value);
@@ -348,8 +377,6 @@ static int config_set_multivar(
348377
if ((result = config_write(b, name, key, &preg, value)) < 0)
349378
goto out;
350379

351-
result = config_refresh(cfg);
352-
353380
out:
354381
git__free(key);
355382
p_regfree(&preg);
@@ -381,9 +408,6 @@ static int config_delete(git_config_backend *cfg, const char *name)
381408
if ((error = config_write(b, name, entry->name, NULL, NULL)) < 0)
382409
goto out;
383410

384-
if ((error = config_refresh(cfg)) < 0)
385-
goto out;
386-
387411
out:
388412
git_config_entries_free(entries);
389413
git__free(key);
@@ -422,9 +446,6 @@ static int config_delete_multivar(git_config_backend *cfg, const char *name, con
422446
if ((result = config_write(b, name, key, &preg, NULL)) < 0)
423447
goto out;
424448

425-
if ((result = config_refresh(cfg)) < 0)
426-
goto out;
427-
428449
out:
429450
git_config_entries_free(entries);
430451
git__free(key);
@@ -659,7 +680,7 @@ static char *escape_value(const char *ptr)
659680
static int parse_include(git_config_parser *reader,
660681
diskfile_parse_state *parse_data, const char *file)
661682
{
662-
struct config_file *include;
683+
git_config_file *include;
663684
git_buf path = GIT_BUF_INIT;
664685
char *dir;
665686
int result;
@@ -851,38 +872,33 @@ static int read_on_variable(
851872
return result;
852873
}
853874

854-
static int config_read(
875+
static int config_read_buffer(
855876
git_config_entries *entries,
856877
const git_repository *repo,
857878
git_config_file *file,
858879
git_config_level_t level,
859-
int depth)
880+
int depth,
881+
const char *buf,
882+
size_t buflen)
860883
{
861884
diskfile_parse_state parse_data;
862885
git_config_parser reader;
863-
git_buf contents = GIT_BUF_INIT;
864886
int error;
865887

866888
if (depth >= MAX_INCLUDE_DEPTH) {
867889
git_error_set(GIT_ERROR_CONFIG, "maximum config include depth reached");
868890
return -1;
869891
}
870892

871-
if ((error = git_futils_readbuffer(&contents, file->path)) < 0)
872-
goto out;
873-
874-
git_parse_ctx_init(&reader.ctx, contents.ptr, contents.size);
875-
876-
if ((error = git_hash_buf(&file->checksum, contents.ptr, contents.size)) < 0)
877-
goto out;
878-
879893
/* Initialize the reading position */
880894
reader.file = file;
881-
git_parse_ctx_init(&reader.ctx, contents.ptr, contents.size);
895+
git_parse_ctx_init(&reader.ctx, buf, buflen);
882896

883897
/* If the file is empty, there's nothing for us to do */
884-
if (!reader.ctx.content || *reader.ctx.content == '\0')
898+
if (!reader.ctx.content || *reader.ctx.content == '\0') {
899+
error = 0;
885900
goto out;
901+
}
886902

887903
parse_data.repo = repo;
888904
parse_data.file_path = file->path;
@@ -892,6 +908,37 @@ static int config_read(
892908

893909
error = git_config_parse(&reader, NULL, read_on_variable, NULL, NULL, &parse_data);
894910

911+
out:
912+
return error;
913+
}
914+
915+
static int config_read(
916+
git_config_entries *entries,
917+
const git_repository *repo,
918+
git_config_file *file,
919+
git_config_level_t level,
920+
int depth)
921+
{
922+
git_buf contents = GIT_BUF_INIT;
923+
struct stat st;
924+
int error;
925+
926+
if (p_stat(file->path, &st) < 0) {
927+
error = git_path_set_error(errno, file->path, "stat");
928+
goto out;
929+
}
930+
931+
if ((error = git_futils_readbuffer(&contents, file->path)) < 0)
932+
goto out;
933+
934+
git_futils_filestamp_set_from_stat(&file->stamp, &st);
935+
if ((error = git_hash_buf(&file->checksum, contents.ptr, contents.size)) < 0)
936+
goto out;
937+
938+
if ((error = config_read_buffer(entries, repo, file, level, depth,
939+
contents.ptr, contents.size)) < 0)
940+
goto out;
941+
895942
out:
896943
git_buf_dispose(&contents);
897944
return error;
@@ -1197,7 +1244,12 @@ static int config_write(diskfile_backend *cfg, const char *orig_key, const char
11971244
git_buf_attach(&cfg->locked_content, git_buf_detach(&buf), len);
11981245
} else {
11991246
git_filebuf_write(&file, git_buf_cstr(&buf), git_buf_len(&buf));
1200-
result = git_filebuf_commit(&file);
1247+
1248+
if ((result = git_filebuf_commit(&file)) < 0)
1249+
goto done;
1250+
1251+
if ((result = config_refresh_from_buffer(&cfg->header.parent, buf.ptr, buf.size)) < 0)
1252+
goto done;
12011253
}
12021254

12031255
done:

0 commit comments

Comments
 (0)