Skip to content

Commit 381d457

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 71074cc commit 381d457

5 files changed

Lines changed: 190 additions & 33 deletions

File tree

‎environment.c‎

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ int protect_hfs = PROTECT_HFS_DEFAULT;
125125
int protect_ntfs = PROTECT_NTFS_DEFAULT;
126126
int core_use_gvfs_helper;
127127
char *gvfs_cache_server_url;
128-
const char *gvfs_shared_cache_pathname;
128+
struct strbuf gvfs_shared_cache_pathname = STRBUF_INIT;
129129

130130
/*
131131
* The character that begins a commented line in user-editable file
@@ -721,19 +721,17 @@ static int git_default_gvfs_config(const char *var, const char *value)
721721
}
722722

723723
if (!strcmp(var, "gvfs.sharedcache") && value && *value) {
724-
struct strbuf buf = STRBUF_INIT;
725-
strbuf_addstr(&buf, value);
726-
if (strbuf_normalize_path(&buf) < 0) {
724+
strbuf_setlen(&gvfs_shared_cache_pathname, 0);
725+
strbuf_addstr(&gvfs_shared_cache_pathname, value);
726+
if (strbuf_normalize_path(&gvfs_shared_cache_pathname) < 0) {
727727
/*
728728
* Pretend it wasn't set. This will cause us to
729729
* fallback to ".git/objects" effectively.
730730
*/
731-
strbuf_release(&buf);
731+
strbuf_release(&gvfs_shared_cache_pathname);
732732
return 0;
733733
}
734-
strbuf_trim_trailing_dir_sep(&buf);
735-
736-
gvfs_shared_cache_pathname = strbuf_detach(&buf, NULL);
734+
strbuf_trim_trailing_dir_sep(&gvfs_shared_cache_pathname);
737735
return 0;
738736
}
739737

‎environment.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ extern int protect_hfs;
166166
extern int protect_ntfs;
167167
extern int core_use_gvfs_helper;
168168
extern char *gvfs_cache_server_url;
169-
extern const char *gvfs_shared_cache_pathname;
169+
extern struct strbuf gvfs_shared_cache_pathname;
170170

171171
extern int core_apply_sparse_checkout;
172172
extern int core_sparse_checkout_cone;

‎gvfs-helper-client.c‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#define USE_THE_REPOSITORY_VARIABLE
22
#include "git-compat-util.h"
3+
#include "dir.h"
4+
#include "environment.h"
35
#include "gvfs-helper-client.h"
46
#include "hex.h"
57
#include "object-file.h"
@@ -181,13 +183,32 @@ static int gh_client__get__receive_response(
181183
return err;
182184
}
183185

186+
/*
187+
* Select the preferred ODB for fetching missing objects.
188+
* This should be the alternate with the same directory
189+
* name as set in `gvfs.sharedCache`.
190+
*
191+
* Fallback to .git/objects if necessary.
192+
*/
184193
static void gh_client__choose_odb(void)
185194
{
195+
struct odb_source *odb;
196+
186197
if (gh_client__chosen_odb)
187198
return;
188199

189200
odb_prepare_alternates(the_repository->objects);
190201
gh_client__chosen_odb = the_repository->objects->sources;
202+
203+
if (!gvfs_shared_cache_pathname.len)
204+
return;
205+
206+
for (odb = the_repository->objects->sources->next; odb; odb = odb->next) {
207+
if (!fspathcmp(odb->path, gvfs_shared_cache_pathname.buf)) {
208+
gh_client__chosen_odb = odb;
209+
return;
210+
}
211+
}
191212
}
192213

193214
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>
@@ -641,26 +642,88 @@ static int option_parse_cache_server_mode(const struct option *opt,
641642
}
642643

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

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

661-
gvfs_shared_cache_pathname = arg;
693+
odb_add_to_alternates_memory(the_repository->objects, buf_arg.buf);
662694

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

666729
/*
@@ -961,24 +1024,20 @@ static void approve_cache_server_creds(void)
9611024
}
9621025

9631026
/*
964-
* Select the ODB directory where we will write objects that we
965-
* download. If was given on the command line or define in the
966-
* config, use the local ODB (in ".git/objects").
1027+
* Get the pathname to the ODB where we write objects that we download.
9671028
*/
9681029
static void select_odb(void)
9691030
{
970-
const char *odb_path = NULL;
1031+
odb_prepare_alternates(the_repository->objects);
9711032

9721033
strbuf_init(&gh__global.buf_odb_path, 0);
9731034

974-
if (gvfs_shared_cache_pathname && *gvfs_shared_cache_pathname)
975-
odb_path = gvfs_shared_cache_pathname;
976-
else {
977-
odb_prepare_alternates(the_repository->objects);
978-
odb_path = the_repository->objects->sources->path;
979-
}
980-
981-
strbuf_addstr(&gh__global.buf_odb_path, odb_path);
1035+
if (gvfs_shared_cache_pathname.len)
1036+
strbuf_addbuf(&gh__global.buf_odb_path,
1037+
&gvfs_shared_cache_pathname);
1038+
else
1039+
strbuf_addstr(&gh__global.buf_odb_path,
1040+
the_repository->objects->sources->path);
9821041
}
9831042

9841043
/*

‎odb.c‎

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,17 +94,66 @@ int odb_mkstemp(struct object_database *odb,
9494
return xmkstemp_mode(temp_filename->buf, mode);
9595
}
9696

97+
static int gvfs_matched_shared_cache_to_alternate;
98+
9799
/*
98100
* Return non-zero iff the path is usable as an alternate object database.
99101
*/
100102
static bool odb_is_source_usable(struct object_database *o, const char *path)
101103
{
104+
extern struct strbuf gvfs_shared_cache_pathname;
102105
int r;
103106
struct strbuf normalized_objdir = STRBUF_INIT;
104107
bool usable = false;
105108

106109
strbuf_realpath(&normalized_objdir, o->sources->path, 1);
107110

111+
if (!strcmp(path, gvfs_shared_cache_pathname.buf)) {
112+
/*
113+
* `gvfs.sharedCache` is the preferred alternate that we
114+
* will use with `gvfs-helper.exe` to dynamically fetch
115+
* missing objects. It is set during git_default_config().
116+
*
117+
* Make sure the directory exists on disk before we let the
118+
* stock code discredit it.
119+
*/
120+
struct strbuf buf_pack_foo = STRBUF_INIT;
121+
enum scld_error scld;
122+
123+
/*
124+
* Force create the "<odb>" and "<odb>/pack" directories, if
125+
* not present on disk. Append an extra bogus directory to
126+
* get safe_create_leading_directories() to see "<odb>/pack"
127+
* as a leading directory of something deeper (which it
128+
* won't create).
129+
*/
130+
strbuf_addf(&buf_pack_foo, "%s/pack/foo", path);
131+
132+
scld = safe_create_leading_directories(o->repo, buf_pack_foo.buf);
133+
if (scld != SCLD_OK && scld != SCLD_EXISTS) {
134+
error_errno(_("could not create shared-cache ODB '%s'"),
135+
gvfs_shared_cache_pathname.buf);
136+
137+
strbuf_release(&buf_pack_foo);
138+
139+
/*
140+
* Pretend no shared-cache was requested and
141+
* effectively fallback to ".git/objects" for
142+
* fetching missing objects.
143+
*/
144+
strbuf_release(&gvfs_shared_cache_pathname);
145+
return 0;
146+
}
147+
148+
/*
149+
* We know that there is an alternate (either from
150+
* .git/objects/info/alternates or from a memory-only
151+
* entry) associated with the shared-cache directory.
152+
*/
153+
gvfs_matched_shared_cache_to_alternate++;
154+
strbuf_release(&buf_pack_foo);
155+
}
156+
108157
/* Detect cases where alternate disappeared */
109158
if (!is_directory(path)) {
110159
error(_("object directory %s does not exist; "
@@ -622,6 +671,7 @@ int odb_for_each_alternate(struct object_database *odb,
622671

623672
void odb_prepare_alternates(struct object_database *odb)
624673
{
674+
extern struct strbuf gvfs_shared_cache_pathname;
625675
struct strvec sources = STRVEC_INIT;
626676

627677
if (odb->loaded_alternates)
@@ -632,6 +682,35 @@ void odb_prepare_alternates(struct object_database *odb)
632682
for (size_t i = 0; i < sources.nr; i++)
633683
odb_add_alternate_recursively(odb, sources.v[i], 0);
634684

685+
if (gvfs_shared_cache_pathname.len &&
686+
!gvfs_matched_shared_cache_to_alternate) {
687+
/*
688+
* There is no entry in .git/objects/info/alternates for
689+
* the requested shared-cache directory. Therefore, the
690+
* odb-list does not contain this directory.
691+
*
692+
* Force this directory into the odb-list as an in-memory
693+
* alternate. Implicitly create the directory on disk, if
694+
* necessary.
695+
*
696+
* See GIT_ALTERNATE_OBJECT_DIRECTORIES for another example
697+
* of this kind of usage.
698+
*
699+
* Note: This has the net-effect of allowing Git to treat
700+
* `gvfs.sharedCache` as an unofficial alternate. This
701+
* usage should be discouraged for compatbility reasons
702+
* with other tools in the overall Git ecosystem (that
703+
* won't know about this trick). It would be much better
704+
* for us to update .git/objects/info/alternates instead.
705+
* The code here is considered a backstop.
706+
*/
707+
parse_alternates(gvfs_shared_cache_pathname.buf, '\n', NULL, &sources);
708+
odb_source_read_alternates(odb->sources, &sources);
709+
for (size_t i = 0; i < sources.nr; i++)
710+
odb_add_alternate_recursively(odb, sources.v[i], 0);
711+
712+
}
713+
635714
odb->loaded_alternates = 1;
636715

637716
strvec_clear(&sources);

0 commit comments

Comments
 (0)