Skip to content

Commit 71151db

Browse files
jeffhostetlerdscho
authored andcommitted
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 <jeffhost@microsoft.com>
1 parent 2d8fe50 commit 71151db

File tree

6 files changed

+185
-33
lines changed

6 files changed

+185
-33
lines changed

config.c

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

19391939
if (!strcmp(var, "gvfs.sharedcache") && value && *value) {
1940-
struct strbuf buf = STRBUF_INIT;
1941-
strbuf_addstr(&buf, value);
1942-
if (strbuf_normalize_path(&buf) < 0) {
1940+
strbuf_setlen(&gvfs_shared_cache_pathname, 0);
1941+
strbuf_addstr(&gvfs_shared_cache_pathname, value);
1942+
if (strbuf_normalize_path(&gvfs_shared_cache_pathname) < 0) {
19431943
/*
19441944
* Pretend it wasn't set. This will cause us to
19451945
* fallback to ".git/objects" effectively.
19461946
*/
1947-
strbuf_release(&buf);
1947+
strbuf_release(&gvfs_shared_cache_pathname);
19481948
return 0;
19491949
}
1950-
strbuf_trim_trailing_dir_sep(&buf);
1951-
1952-
gvfs_shared_cache_pathname = strbuf_detach(&buf, NULL);
1950+
strbuf_trim_trailing_dir_sep(&gvfs_shared_cache_pathname);
19531951
return 0;
19541952
}
19551953

environment.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ int protect_hfs = PROTECT_HFS_DEFAULT;
9696
int protect_ntfs = PROTECT_NTFS_DEFAULT;
9797
int core_use_gvfs_helper;
9898
const char *gvfs_cache_server_url;
99-
const char *gvfs_shared_cache_pathname;
99+
struct strbuf gvfs_shared_cache_pathname = STRBUF_INIT;
100100

101101
/*
102102
* The character that begins a commented line in user-editable file

environment.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ extern int protect_hfs;
153153
extern int protect_ntfs;
154154
extern int core_use_gvfs_helper;
155155
extern const char *gvfs_cache_server_url;
156-
extern const char *gvfs_shared_cache_pathname;
156+
extern struct strbuf gvfs_shared_cache_pathname;
157157

158158
extern int core_apply_sparse_checkout;
159159
extern int core_sparse_checkout_cone;

gvfs-helper-client.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "git-compat-util.h"
2+
#include "environment.h"
23
#include "hex.h"
34
#include "strvec.h"
45
#include "trace2.h"
@@ -206,13 +207,32 @@ static int gh_client__get__receive_response(
206207
return err;
207208
}
208209

210+
/*
211+
* Select the preferred ODB for fetching missing objects.
212+
* This should be the alternate with the same directory
213+
* name as set in `gvfs.sharedCache`.
214+
*
215+
* Fallback to .git/objects if necessary.
216+
*/
209217
static void gh_client__choose_odb(void)
210218
{
219+
struct object_directory *odb;
220+
211221
if (gh_client__chosen_odb)
212222
return;
213223

214224
prepare_alt_odb(the_repository);
215225
gh_client__chosen_odb = the_repository->objects->odb;
226+
227+
if (!gvfs_shared_cache_pathname.len)
228+
return;
229+
230+
for (odb = the_repository->objects->odb->next; odb; odb = odb->next) {
231+
if (!strcmp(odb->path, gvfs_shared_cache_pathname.buf)) {
232+
gh_client__chosen_odb = odb;
233+
return;
234+
}
235+
}
216236
}
217237

218238
static int gh_client__get(enum gh_client__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>
@@ -639,26 +640,88 @@ static int option_parse_cache_server_mode(const struct option *opt,
639640
}
640641

641642
/*
642-
* Let command line args override "gvfs.sharedcache" config setting.
643+
* Let command line args override "gvfs.sharedcache" config setting
644+
* and override the value set by git_default_config().
645+
*
646+
* The command line is parsed *AFTER* the config is loaded, so
647+
* prepared_alt_odb() has already been called any default or inherited
648+
* shared-cache has already been set.
643649
*
644-
* It would be nice to move this to parse-options.c as an
645-
* OPTION_PATHNAME handler. And maybe have flags for exists()
646-
* and is_directory().
650+
* We have a chance to override it here.
647651
*/
648652
static int option_parse_shared_cache_directory(const struct option *opt,
649653
const char *arg, int unset)
650654
{
655+
struct strbuf buf_arg = STRBUF_INIT;
656+
651657
if (unset) /* should not happen */
652658
return error(_("missing value for switch '%s'"),
653659
opt->long_name);
654660

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

659-
gvfs_shared_cache_pathname = arg;
691+
add_to_alternates_memory(buf_arg.buf);
660692

661-
return 0;
693+
strbuf_release(&buf_arg);
694+
return 0;
695+
}
696+
697+
else {
698+
/*
699+
* The requested shared-cache is different from the one
700+
* we inherited. Replace the inherited value with this
701+
* one, but smartly fallback if necessary.
702+
*/
703+
struct strbuf buf_prev = STRBUF_INIT;
704+
705+
strbuf_addbuf(&buf_prev, &gvfs_shared_cache_pathname);
706+
707+
strbuf_setlen(&gvfs_shared_cache_pathname, 0);
708+
strbuf_addbuf(&gvfs_shared_cache_pathname, &buf_arg);
709+
710+
add_to_alternates_memory(buf_arg.buf);
711+
712+
/*
713+
* alt_odb_usable() releases gvfs_shared_cache_pathname
714+
* if it cannot create the directory on disk, so fallback
715+
* to the previous choice when it fails.
716+
*/
717+
if (!gvfs_shared_cache_pathname.len)
718+
strbuf_addbuf(&gvfs_shared_cache_pathname,
719+
&buf_prev);
720+
721+
strbuf_release(&buf_arg);
722+
strbuf_release(&buf_prev);
723+
return 0;
724+
}
662725
}
663726

664727
/*
@@ -956,24 +1019,20 @@ static void approve_cache_server_creds(void)
9561019
}
9571020

9581021
/*
959-
* Select the ODB directory where we will write objects that we
960-
* download. If was given on the command line or define in the
961-
* config, use the local ODB (in ".git/objects").
1022+
* Get the pathname to the ODB where we write objects that we download.
9621023
*/
9631024
static void select_odb(void)
9641025
{
965-
const char *odb_path = NULL;
1026+
prepare_alt_odb(the_repository);
9661027

9671028
strbuf_init(&gh__global.buf_odb_path, 0);
9681029

969-
if (gvfs_shared_cache_pathname && *gvfs_shared_cache_pathname)
970-
odb_path = gvfs_shared_cache_pathname;
971-
else {
972-
prepare_alt_odb(the_repository);
973-
odb_path = the_repository->objects->odb->path;
974-
}
975-
976-
strbuf_addstr(&gh__global.buf_odb_path, odb_path);
1030+
if (gvfs_shared_cache_pathname.len)
1031+
strbuf_addbuf(&gh__global.buf_odb_path,
1032+
&gvfs_shared_cache_pathname);
1033+
else
1034+
strbuf_addstr(&gh__global.buf_odb_path,
1035+
the_repository->objects->odb->path);
9771036
}
9781037

9791038
/*

object-file.c

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

468+
static int gvfs_matched_shared_cache_to_alternate;
469+
468470
/*
469471
* Return non-zero iff the path is usable as an alternate object database.
470472
*/
@@ -474,6 +476,52 @@ static int alt_odb_usable(struct raw_object_store *o,
474476
{
475477
int r;
476478

479+
if (!strbuf_cmp(path, &gvfs_shared_cache_pathname)) {
480+
/*
481+
* `gvfs.sharedCache` is the preferred alternate that we
482+
* will use with `gvfs-helper.exe` to dynamically fetch
483+
* missing objects. It is set during git_default_config().
484+
*
485+
* Make sure the directory exists on disk before we let the
486+
* stock code discredit it.
487+
*/
488+
struct strbuf buf_pack_foo = STRBUF_INIT;
489+
enum scld_error scld;
490+
491+
/*
492+
* Force create the "<odb>" and "<odb>/pack" directories, if
493+
* not present on disk. Append an extra bogus directory to
494+
* get safe_create_leading_directories() to see "<odb>/pack"
495+
* as a leading directory of something deeper (which it
496+
* won't create).
497+
*/
498+
strbuf_addf(&buf_pack_foo, "%s/pack/foo", path->buf);
499+
500+
scld = safe_create_leading_directories(buf_pack_foo.buf);
501+
if (scld != SCLD_OK && scld != SCLD_EXISTS) {
502+
error_errno(_("could not create shared-cache ODB '%s'"),
503+
gvfs_shared_cache_pathname.buf);
504+
505+
strbuf_release(&buf_pack_foo);
506+
507+
/*
508+
* Pretend no shared-cache was requested and
509+
* effectively fallback to ".git/objects" for
510+
* fetching missing objects.
511+
*/
512+
strbuf_release(&gvfs_shared_cache_pathname);
513+
return 0;
514+
}
515+
516+
/*
517+
* We know that there is an alternate (either from
518+
* .git/objects/info/alternates or from a memory-only
519+
* entry) associated with the shared-cache directory.
520+
*/
521+
gvfs_matched_shared_cache_to_alternate++;
522+
strbuf_release(&buf_pack_foo);
523+
}
524+
477525
/* Detect cases where alternate disappeared */
478526
if (!is_directory(path->buf)) {
479527
error(_("object directory %s does not exist; "
@@ -957,6 +1005,33 @@ void prepare_alt_odb(struct repository *r)
9571005
link_alt_odb_entries(r, r->objects->alternate_db, PATH_SEP, NULL, 0);
9581006

9591007
read_info_alternates(r, r->objects->odb->path, 0);
1008+
1009+
if (gvfs_shared_cache_pathname.len &&
1010+
!gvfs_matched_shared_cache_to_alternate) {
1011+
/*
1012+
* There is no entry in .git/objects/info/alternates for
1013+
* the requested shared-cache directory. Therefore, the
1014+
* odb-list does not contain this directory.
1015+
*
1016+
* Force this directory into the odb-list as an in-memory
1017+
* alternate. Implicitly create the directory on disk, if
1018+
* necessary.
1019+
*
1020+
* See GIT_ALTERNATE_OBJECT_DIRECTORIES for another example
1021+
* of this kind of usage.
1022+
*
1023+
* Note: This has the net-effect of allowing Git to treat
1024+
* `gvfs.sharedCache` as an unofficial alternate. This
1025+
* usage should be discouraged for compatbility reasons
1026+
* with other tools in the overall Git ecosystem (that
1027+
* won't know about this trick). It would be much better
1028+
* for us to update .git/objects/info/alternates instead.
1029+
* The code here is considered a backstop.
1030+
*/
1031+
link_alt_odb_entries(r, gvfs_shared_cache_pathname.buf,
1032+
'\n', NULL, 0);
1033+
}
1034+
9601035
r->objects->loaded_alternates = 1;
9611036
}
9621037

0 commit comments

Comments
 (0)