@@ -24,13 +24,14 @@ static struct hashmap gh_server__subprocess_map;
2424static 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
3536static 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
348391void 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+ }
0 commit comments