Skip to content

Commit db148b4

Browse files
derrickstoleemjcheetham
authored andcommitted
Merge gvfs-helper prefetch feature
Includes these pull requests: git-for-windows#227 git-for-windows#228 git-for-windows#229 git-for-windows#231 git-for-windows#240 Signed-off-by: Derrick Stolee <[email protected]>
2 parents 579c64d + 0c2f69b commit db148b4

File tree

9 files changed

+1778
-334
lines changed

9 files changed

+1778
-334
lines changed

Documentation/config/core.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,10 @@ core.gvfs::
761761
is first accessed and brought down to the client. Git.exe can't
762762
currently tell the first access vs subsequent accesses so this
763763
flag just blocks them from occurring at all.
764+
GVFS_PREFETCH_DURING_FETCH::
765+
Bit value 128
766+
While performing a `git fetch` command, use the gvfs-helper to
767+
perform a "prefetch" of commits and trees.
764768
--
765769

766770
core.useGvfsHelper::

builtin/fetch.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include "string-list.h"
1414
#include "remote.h"
1515
#include "transport.h"
16+
#include "gvfs.h"
17+
#include "gvfs-helper-client.h"
1618
#include "run-command.h"
1719
#include "parse-options.h"
1820
#include "sigchain.h"
@@ -1132,6 +1134,13 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
11321134
struct check_connected_options opt = CHECK_CONNECTED_INIT;
11331135

11341136
rm = ref_map;
1137+
1138+
/*
1139+
* Before checking connectivity, be really sure we have the
1140+
* latest pack-files loaded into memory.
1141+
*/
1142+
reprepare_packed_git(the_repository);
1143+
11351144
if (check_connected(iterate_ref_map, &rm, &opt)) {
11361145
rc = error(_("%s did not send all necessary objects\n"), url);
11371146
goto abort;
@@ -2217,6 +2226,9 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
22172226
}
22182227
}
22192228

2229+
if (core_gvfs & GVFS_PREFETCH_DURING_FETCH)
2230+
gh_client__prefetch(0, NULL);
2231+
22202232
if (negotiate_only) {
22212233
struct oidset acked_commits = OIDSET_INIT;
22222234
struct oidset_iter iter;

gvfs-helper-client.c

Lines changed: 116 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,14 @@ static struct hashmap gh_server__subprocess_map;
2424
static struct object_directory *gh_client__chosen_odb;
2525

2626
/*
27-
* The "objects" capability has 2 verbs: "get" and "post".
27+
* The "objects" capability has verbs: "get" and "post" and "prefetch".
2828
*/
2929
#define CAP_OBJECTS (1u<<1)
3030
#define CAP_OBJECTS_NAME "objects"
3131

3232
#define CAP_OBJECTS__VERB_GET1_NAME "get"
3333
#define CAP_OBJECTS__VERB_POST_NAME "post"
34+
#define CAP_OBJECTS__VERB_PREFETCH_NAME "prefetch"
3435

3536
static int gh_client__start_fn(struct subprocess_entry *subprocess)
3637
{
@@ -129,6 +130,44 @@ static int gh_client__send__objects_get(struct child_process *process,
129130
return 0;
130131
}
131132

133+
/*
134+
* Send a request to gvfs-helper to prefetch packfiles from either the
135+
* cache-server or the main Git server using "/gvfs/prefetch".
136+
*
137+
* objects.prefetch LF
138+
* [<seconds-since_epoch> LF]
139+
* <flush>
140+
*/
141+
static int gh_client__send__objects_prefetch(struct child_process *process,
142+
timestamp_t seconds_since_epoch)
143+
{
144+
int err;
145+
146+
/*
147+
* We assume that all of the packet_ routines call error()
148+
* so that we don't have to.
149+
*/
150+
151+
err = packet_write_fmt_gently(
152+
process->in,
153+
(CAP_OBJECTS_NAME "." CAP_OBJECTS__VERB_PREFETCH_NAME "\n"));
154+
if (err)
155+
return err;
156+
157+
if (seconds_since_epoch) {
158+
err = packet_write_fmt_gently(process->in, "%" PRItime "\n",
159+
seconds_since_epoch);
160+
if (err)
161+
return err;
162+
}
163+
164+
err = packet_flush_gently(process->in);
165+
if (err)
166+
return err;
167+
168+
return 0;
169+
}
170+
132171
/*
133172
* Update the loose object cache to include the newly created
134173
* object.
@@ -176,7 +215,7 @@ static void gh_client__update_packed_git(const char *line)
176215
}
177216

178217
/*
179-
* Both CAP_OBJECTS verbs return the same format response:
218+
* CAP_OBJECTS verbs return the same format response:
180219
*
181220
* <odb>
182221
* <data>*
@@ -216,6 +255,8 @@ static int gh_client__objects__receive_response(
216255
const char *v1;
217256
char *line;
218257
int len;
258+
int nr_loose = 0;
259+
int nr_packfile = 0;
219260
int err = 0;
220261

221262
while (1) {
@@ -234,13 +275,13 @@ static int gh_client__objects__receive_response(
234275
else if (starts_with(line, "packfile")) {
235276
gh_client__update_packed_git(line);
236277
ghc |= GHC__CREATED__PACKFILE;
237-
*p_nr_packfile += 1;
278+
nr_packfile++;
238279
}
239280

240281
else if (starts_with(line, "loose")) {
241282
gh_client__update_loose_cache(line);
242283
ghc |= GHC__CREATED__LOOSE;
243-
*p_nr_loose += 1;
284+
nr_loose++;
244285
}
245286

246287
else if (starts_with(line, "ok"))
@@ -254,6 +295,8 @@ static int gh_client__objects__receive_response(
254295
}
255296

256297
*p_ghc = ghc;
298+
*p_nr_loose = nr_loose;
299+
*p_nr_packfile = nr_packfile;
257300

258301
return err;
259302
}
@@ -310,7 +353,7 @@ static struct gh_server__process *gh_client__find_long_running_process(
310353
/*
311354
* Find an existing long-running process with the above command
312355
* line -or- create a new long-running process for this and
313-
* subsequent 'get' requests.
356+
* subsequent requests.
314357
*/
315358
if (!gh_server__subprocess_map_initialized) {
316359
gh_server__subprocess_map_initialized = 1;
@@ -347,10 +390,14 @@ static struct gh_server__process *gh_client__find_long_running_process(
347390

348391
void gh_client__queue_oid(const struct object_id *oid)
349392
{
350-
// TODO consider removing this trace2. it is useful for interactive
351-
// TODO debugging, but may generate way too much noise for a data
352-
// TODO event.
353-
trace2_printf("gh_client__queue_oid: %s", oid_to_hex(oid));
393+
/*
394+
* Keep this trace as a printf only, so that it goes to the
395+
* perf log, but not the event log. It is useful for interactive
396+
* debugging, but generates way too much (unuseful) noise for the
397+
* database.
398+
*/
399+
if (trace2_is_enabled())
400+
trace2_printf("gh_client__queue_oid: %s", oid_to_hex(oid));
354401

355402
if (!oidset_insert(&gh_client__oidset_queued, oid))
356403
gh_client__oidset_count++;
@@ -431,10 +478,14 @@ int gh_client__get_immediate(const struct object_id *oid,
431478
int nr_packfile = 0;
432479
int err = 0;
433480

434-
// TODO consider removing this trace2. it is useful for interactive
435-
// TODO debugging, but may generate way too much noise for a data
436-
// TODO event.
437-
trace2_printf("gh_client__get_immediate: %s", oid_to_hex(oid));
481+
/*
482+
* Keep this trace as a printf only, so that it goes to the
483+
* perf log, but not the event log. It is useful for interactive
484+
* debugging, but generates way too much (unuseful) noise for the
485+
* database.
486+
*/
487+
if (trace2_is_enabled())
488+
trace2_printf("gh_client__get_immediate: %s", oid_to_hex(oid));
438489

439490
entry = gh_client__find_long_running_process(CAP_OBJECTS);
440491
if (!entry)
@@ -463,3 +514,55 @@ int gh_client__get_immediate(const struct object_id *oid,
463514

464515
return err;
465516
}
517+
518+
/*
519+
* Ask gvfs-helper to prefetch commits-and-trees packfiles since a
520+
* given timestamp.
521+
*
522+
* If seconds_since_epoch is zero, gvfs-helper will scan the ODB for
523+
* the last received prefetch and ask for ones newer than that.
524+
*/
525+
int gh_client__prefetch(timestamp_t seconds_since_epoch,
526+
int *nr_packfiles_received)
527+
{
528+
struct gh_server__process *entry;
529+
struct child_process *process;
530+
enum gh_client__created ghc;
531+
int nr_loose = 0;
532+
int nr_packfile = 0;
533+
int err = 0;
534+
535+
entry = gh_client__find_long_running_process(CAP_OBJECTS);
536+
if (!entry)
537+
return -1;
538+
539+
trace2_region_enter("gh-client", "objects/prefetch", the_repository);
540+
trace2_data_intmax("gh-client", the_repository, "prefetch/since",
541+
seconds_since_epoch);
542+
543+
process = &entry->subprocess.process;
544+
545+
sigchain_push(SIGPIPE, SIG_IGN);
546+
547+
err = gh_client__send__objects_prefetch(process, seconds_since_epoch);
548+
if (!err)
549+
err = gh_client__objects__receive_response(
550+
process, &ghc, &nr_loose, &nr_packfile);
551+
552+
sigchain_pop(SIGPIPE);
553+
554+
if (err) {
555+
subprocess_stop(&gh_server__subprocess_map,
556+
(struct subprocess_entry *)entry);
557+
FREE_AND_NULL(entry);
558+
}
559+
560+
trace2_data_intmax("gh-client", the_repository,
561+
"prefetch/packfile_count", nr_packfile);
562+
trace2_region_leave("gh-client", "objects/prefetch", the_repository);
563+
564+
if (nr_packfiles_received)
565+
*nr_packfiles_received = nr_packfile;
566+
567+
return err;
568+
}

gvfs-helper-client.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,22 @@ void gh_client__queue_oid_array(const struct object_id *oids, int oid_nr);
6666
*/
6767
int gh_client__drain_queue(enum gh_client__created *p_ghc);
6868

69+
/*
70+
* Ask `gvfs-helper server` to fetch any "prefetch packs"
71+
* available on the server more recent than the requested time.
72+
*
73+
* If seconds_since_epoch is zero, gvfs-helper will scan the ODB for
74+
* the last received prefetch and ask for ones newer than that.
75+
*
76+
* A long-running background process is used to subsequent requests
77+
* (either prefetch or regular immediate/queued requests) more efficient.
78+
*
79+
* One or more packfiles will be created in the shared-cache ODB.
80+
*
81+
* Returns 0 on success, -1 on error. Optionally also returns the
82+
* number of prefetch packs received.
83+
*/
84+
int gh_client__prefetch(timestamp_t seconds_since_epoch,
85+
int *nr_packfiles_received);
86+
6987
#endif /* GVFS_HELPER_CLIENT_H */

0 commit comments

Comments
 (0)