Skip to content

Commit 205244f

Browse files
committed
sha1-file: create shared-cache directory if it doesn't exist
The config variable `gvfs.sharedCache` contains the pathname to an alternate <odb> that will be used by `gvfs-helper` to store dynamically-fetched missing objects. If this directory does not exist on disk, `prepare_alt_odb()` omits this directory from the in-memory list of alternates. This causes `git` commands (and `gvfs-helper` in particular) to fall-back to `.git/objects` for storage of these objects. This disables the shared-cache and leads to poorer performance. Teach `alt_obj_usable()` and `prepare_alt_odb()`, match up the directory named in `gvfs.sharedCache` with an entry in `.git/objects/info/alternates` and force-create the `<odb>` root directory (and the associated `<odb>/pack` directory) if necessary. If the value of `gvfs.sharedCache` refers to a directory that is NOT listed as an alternate, create an in-memory alternate entry in the odb-list. (This is similar to how GIT_ALTERNATE_OBJECT_DIRECTORIES works.) This work happens the first time that `prepare_alt_odb()` is called. Furthermore, teach the `--shared-cache=<odb>` command line option in `gvfs-helper` (which is runs after the first call to `prepare_alt_odb()`) to override the inherited shared-cache (and again, create the ODB directory if necessary). Signed-off-by: Jeff Hostetler <[email protected]>
1 parent 5d39b08 commit 205244f

File tree

6 files changed

+181
-45
lines changed

6 files changed

+181
-45
lines changed

cache.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -897,7 +897,7 @@ extern int protect_ntfs;
897897
extern const char *core_fsmonitor;
898898
extern int core_use_gvfs_helper;
899899
extern const char *gvfs_cache_server_url;
900-
extern const char *gvfs_shared_cache_pathname;
900+
extern struct strbuf gvfs_shared_cache_pathname;
901901

902902
int core_apply_sparse_checkout;
903903
int core_sparse_checkout_cone;

config.c

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1513,19 +1513,17 @@ static int git_default_gvfs_config(const char *var, const char *value)
15131513
}
15141514

15151515
if (!strcmp(var, "gvfs.sharedcache") && value && *value) {
1516-
struct strbuf buf = STRBUF_INIT;
1517-
strbuf_addstr(&buf, value);
1518-
if (strbuf_normalize_path(&buf) < 0) {
1516+
strbuf_setlen(&gvfs_shared_cache_pathname, 0);
1517+
strbuf_addstr(&gvfs_shared_cache_pathname, value);
1518+
if (strbuf_normalize_path(&gvfs_shared_cache_pathname) < 0) {
15191519
/*
15201520
* Pretend it wasn't set. This will cause us to
15211521
* fallback to ".git/objects" effectively.
15221522
*/
1523-
strbuf_release(&buf);
1523+
strbuf_release(&gvfs_shared_cache_pathname);
15241524
return 0;
15251525
}
1526-
strbuf_trim_trailing_dir_sep(&buf);
1527-
1528-
gvfs_shared_cache_pathname = strbuf_detach(&buf, NULL);
1526+
strbuf_trim_trailing_dir_sep(&gvfs_shared_cache_pathname);
15291527
return 0;
15301528
}
15311529

environment.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ int protect_ntfs = PROTECT_NTFS_DEFAULT;
9090
const char *core_fsmonitor;
9191
int core_use_gvfs_helper;
9292
const char *gvfs_cache_server_url;
93-
const char *gvfs_shared_cache_pathname;
93+
struct strbuf gvfs_shared_cache_pathname = STRBUF_INIT;
9494

9595
/*
9696
* The character that begins a commented line in user-editable file

gvfs-helper-client.c

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -224,29 +224,33 @@ static int ghc__get__receive_response(struct child_process *process,
224224
return err;
225225
}
226226

227+
/*
228+
* Select the preferred ODB for fetching missing objects.
229+
* This should be the alternate with the same directory
230+
* name as set in `gvfs.sharedCache`.
231+
*
232+
* Fallback to .git/objects if necessary.
233+
*/
227234
static void ghc__choose_odb(void)
228235
{
229236
struct object_directory *odb;
230237

231238
if (ghs__chosen_odb)
232239
return;
233240

241+
ghs__chosen_odb = the_repository->objects->odb;
242+
234243
prepare_alt_odb(the_repository);
235244

236-
if (gvfs_shared_cache_pathname && *gvfs_shared_cache_pathname) {
237-
for (odb = the_repository->objects->odb; odb; odb = odb->next) {
238-
if (!strcmp(odb->path, gvfs_shared_cache_pathname)) {
239-
ghs__chosen_odb = odb;
240-
return;
241-
}
245+
if (!gvfs_shared_cache_pathname.len)
246+
return;
247+
248+
for (odb = the_repository->objects->odb->next; odb; odb = odb->next) {
249+
if (!strcmp(odb->path, gvfs_shared_cache_pathname.buf)) {
250+
ghs__chosen_odb = odb;
251+
return;
242252
}
243253
}
244-
245-
/*
246-
* Use .git/objects if "gvfs.sharedcache" not set or set to an
247-
* unknown pathname.
248-
*/
249-
ghs__chosen_odb = the_repository->objects->odb;
250254
}
251255

252256
static int ghc__get(enum ghc__created *p_ghc)

gvfs-helper.c

Lines changed: 83 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,11 @@
8181
//
8282
// Fetch 1 or more objects. If a cache-server is configured,
8383
// try it first. Optionally fallback to the main Git server.
84+
//
8485
// Create 1 or more loose objects and/or packfiles in the
85-
// requested shared-cache directory (given on the command
86-
// line and which is reported at the beginning of the
87-
// response).
86+
// shared-cache ODB. (The pathname of the selected ODB is
87+
// reported at the beginning of the response; this should
88+
// match the pathname given on the command line).
8889
//
8990
// git> get
9091
// git> <oid>
@@ -632,26 +633,88 @@ static int option_parse_cache_server_mode(const struct option *opt,
632633
}
633634

634635
/*
635-
* Let command line args override "gvfs.sharedcache" config setting.
636+
* Let command line args override "gvfs.sharedcache" config setting
637+
* and override the value set by git_default_config().
638+
*
639+
* The command line is parsed *AFTER* the config is loaded, so
640+
* prepared_alt_odb() has already been called any default or inherited
641+
* shared-cache has already been set.
636642
*
637-
* It would be nice to move this to parse-options.c as an
638-
* OPTION_PATHNAME handler. And maybe have flags for exists()
639-
* and is_directory().
643+
* We have a chance to override it here.
640644
*/
641645
static int option_parse_shared_cache_directory(const struct option *opt,
642646
const char *arg, int unset)
643647
{
648+
struct strbuf buf_arg = STRBUF_INIT;
649+
644650
if (unset) /* should not happen */
645651
return error(_("missing value for switch '%s'"),
646652
opt->long_name);
647653

648-
if (!is_directory(arg))
649-
return error(_("value for switch '%s' is not a directory: '%s'"),
650-
opt->long_name, arg);
654+
strbuf_addstr(&buf_arg, arg);
655+
if (strbuf_normalize_path(&buf_arg) < 0) {
656+
/*
657+
* Pretend command line wasn't given. Use whatever
658+
* settings we already have from the config.
659+
*/
660+
strbuf_release(&buf_arg);
661+
return 0;
662+
}
663+
strbuf_trim_trailing_dir_sep(&buf_arg);
664+
665+
if (!strbuf_cmp(&buf_arg, &gvfs_shared_cache_pathname)) {
666+
/*
667+
* The command line argument matches what we got from
668+
* the config, so we're already setup correctly. (And
669+
* we have already verified that the directory exists
670+
* on disk.)
671+
*/
672+
strbuf_release(&buf_arg);
673+
return 0;
674+
}
675+
676+
else if (!gvfs_shared_cache_pathname.len) {
677+
/*
678+
* A shared-cache was requested and we did not inherit one.
679+
* Try it, but let alt_odb_usabe() secretly disable it if
680+
* it cannot create the directory on disk.
681+
*/
682+
strbuf_addbuf(&gvfs_shared_cache_pathname, &buf_arg);
651683

652-
gvfs_shared_cache_pathname = arg;
684+
add_to_alternates_memory(buf_arg.buf);
653685

654-
return 0;
686+
strbuf_release(&buf_arg);
687+
return 0;
688+
}
689+
690+
else {
691+
/*
692+
* The requested shared-cache is different from the one
693+
* we inherited. Replace the inherited value with this
694+
* one, but smartly fallback if necessary.
695+
*/
696+
struct strbuf buf_prev = STRBUF_INIT;
697+
698+
strbuf_addbuf(&buf_prev, &gvfs_shared_cache_pathname);
699+
700+
strbuf_setlen(&gvfs_shared_cache_pathname, 0);
701+
strbuf_addbuf(&gvfs_shared_cache_pathname, &buf_arg);
702+
703+
add_to_alternates_memory(buf_arg.buf);
704+
705+
/*
706+
* alt_odb_usabe() releases gvfs_shared_cache_pathname
707+
* if it cannot create the directory on disk, so fallback
708+
* to the previous choice when it fails.
709+
*/
710+
if (!gvfs_shared_cache_pathname.len)
711+
strbuf_addbuf(&gvfs_shared_cache_pathname,
712+
&buf_prev);
713+
714+
strbuf_release(&buf_arg);
715+
strbuf_release(&buf_prev);
716+
return 0;
717+
}
655718
}
656719

657720
/*
@@ -949,24 +1012,20 @@ static void approve_cache_server_creds(void)
9491012
}
9501013

9511014
/*
952-
* Select the ODB directory where we will write objects that we
953-
* download. If was given on the command line or define in the
954-
* config, use the local ODB (in ".git/objects").
1015+
* Get the pathname to the ODB where we write objects that we download.
9551016
*/
9561017
static void select_odb(void)
9571018
{
958-
const char *odb_path = NULL;
1019+
prepare_alt_odb(the_repository);
9591020

9601021
strbuf_init(&gh__global.buf_odb_path, 0);
9611022

962-
if (gvfs_shared_cache_pathname && *gvfs_shared_cache_pathname)
963-
odb_path = gvfs_shared_cache_pathname;
964-
else {
965-
prepare_alt_odb(the_repository);
966-
odb_path = the_repository->objects->odb->path;
967-
}
968-
969-
strbuf_addstr(&gh__global.buf_odb_path, odb_path);
1023+
if (gvfs_shared_cache_pathname.len)
1024+
strbuf_addbuf(&gh__global.buf_odb_path,
1025+
&gvfs_shared_cache_pathname);
1026+
else
1027+
strbuf_addstr(&gh__global.buf_odb_path,
1028+
the_repository->objects->odb->path);
9701029
}
9711030

9721031
/*

sha1-file.c

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,8 @@ const char *loose_object_path(struct repository *r, struct strbuf *buf,
443443
return odb_loose_path(r->objects->odb, buf, oid);
444444
}
445445

446+
static int gvfs_matched_shared_cache_to_alternate;
447+
446448
/*
447449
* Return non-zero iff the path is usable as an alternate object database.
448450
*/
@@ -452,6 +454,52 @@ static int alt_odb_usable(struct raw_object_store *o,
452454
{
453455
struct object_directory *odb;
454456

457+
if (!strbuf_cmp(path, &gvfs_shared_cache_pathname)) {
458+
/*
459+
* `gvfs.sharedCache` is the preferred alternate that we
460+
* will use with `gvfs-helper.exe` to dynamically fetch
461+
* missing objects. It is set during git_default_config().
462+
*
463+
* Make sure the directory exists on disk before we let the
464+
* stock code discredit it.
465+
*/
466+
struct strbuf buf_pack_foo = STRBUF_INIT;
467+
enum scld_error scld;
468+
469+
/*
470+
* Force create the "<odb>" and "<odb>/pack" directories, if
471+
* not present on disk. Append an extra bogus directory to
472+
* get safe_create_leading_directories() to see "<odb>/pack"
473+
* as a leading directory of something deeper (which it
474+
* won't create).
475+
*/
476+
strbuf_addf(&buf_pack_foo, "%s/pack/foo", path->buf);
477+
478+
scld = safe_create_leading_directories(buf_pack_foo.buf);
479+
if (scld != SCLD_OK && scld != SCLD_EXISTS) {
480+
error_errno(_("could not create shared-cache ODB '%s'"),
481+
gvfs_shared_cache_pathname.buf);
482+
483+
strbuf_release(&buf_pack_foo);
484+
485+
/*
486+
* Pretend no shared-cache was requested and
487+
* effectively fallback to ".git/objects" for
488+
* fetching missing objects.
489+
*/
490+
strbuf_release(&gvfs_shared_cache_pathname);
491+
return 0;
492+
}
493+
494+
/*
495+
* We know that there is an alternate (either from
496+
* .git/objects/info/alternates or from a memory-only
497+
* entry) associated with the shared-cache directory.
498+
*/
499+
gvfs_matched_shared_cache_to_alternate++;
500+
strbuf_release(&buf_pack_foo);
501+
}
502+
455503
/* Detect cases where alternate disappeared */
456504
if (!is_directory(path->buf)) {
457505
error(_("object directory %s does not exist; "
@@ -867,6 +915,33 @@ void prepare_alt_odb(struct repository *r)
867915
link_alt_odb_entries(r, r->objects->alternate_db, PATH_SEP, NULL, 0);
868916

869917
read_info_alternates(r, r->objects->odb->path, 0);
918+
919+
if (gvfs_shared_cache_pathname.len &&
920+
!gvfs_matched_shared_cache_to_alternate) {
921+
/*
922+
* There is no entry in .git/objects/info/alternates for
923+
* the requested shared-cache directory. Therefore, the
924+
* odb-list does not contain this directory.
925+
*
926+
* Force this directory into the odb-list as an in-memory
927+
* alternate. Implicitly create the directory on disk, if
928+
* necessary.
929+
*
930+
* See GIT_ALTERNATE_OBJECT_DIRECTORIES for another example
931+
* of this kind of usage.
932+
*
933+
* Note: This has the net-effect of allowing Git to treat
934+
* `gvfs.sharedCache` as an unofficial alternate. This
935+
* usage should be discouraged for compatbility reasons
936+
* with other tools in the overall Git ecosystem (that
937+
* won't know about this trick). It would be much better
938+
* for us to update .git/objects/info/alternates instead.
939+
* The code here is considered a backstop.
940+
*/
941+
link_alt_odb_entries(r, gvfs_shared_cache_pathname.buf,
942+
'\n', NULL, 0);
943+
}
944+
870945
r->objects->loaded_alternates = 1;
871946
}
872947

0 commit comments

Comments
 (0)