@@ -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 * Verify that the pathname found in the "odb" response line matches
134173 * what we requested.
@@ -198,7 +237,7 @@ static void gh_client__update_packed_git(const char *line)
198237}
199238
200239/*
201- * Both CAP_OBJECTS verbs return the same format response:
240+ * CAP_OBJECTS verbs return the same format response:
202241 *
203242 * <odb>
204243 * <data>*
@@ -238,6 +277,8 @@ static int gh_client__objects__receive_response(
238277 const char * v1 ;
239278 char * line ;
240279 int len ;
280+ int nr_loose = 0 ;
281+ int nr_packfile = 0 ;
241282 int err = 0 ;
242283
243284 while (1 ) {
@@ -256,13 +297,13 @@ static int gh_client__objects__receive_response(
256297 else if (starts_with (line , "packfile" )) {
257298 gh_client__update_packed_git (line );
258299 ghc |= GHC__CREATED__PACKFILE ;
259- * p_nr_packfile += 1 ;
300+ nr_packfile ++ ;
260301 }
261302
262303 else if (starts_with (line , "loose" )) {
263304 gh_client__update_loose_cache (line );
264305 ghc |= GHC__CREATED__LOOSE ;
265- * p_nr_loose += 1 ;
306+ nr_loose ++ ;
266307 }
267308
268309 else if (starts_with (line , "ok" ))
@@ -276,6 +317,8 @@ static int gh_client__objects__receive_response(
276317 }
277318
278319 * p_ghc = ghc ;
320+ * p_nr_loose = nr_loose ;
321+ * p_nr_packfile = nr_packfile ;
279322
280323 return err ;
281324}
@@ -332,7 +375,7 @@ static struct gh_server__process *gh_client__find_long_running_process(
332375 /*
333376 * Find an existing long-running process with the above command
334377 * line -or- create a new long-running process for this and
335- * subsequent 'get' requests.
378+ * subsequent requests.
336379 */
337380 if (!gh_server__subprocess_map_initialized ) {
338381 gh_server__subprocess_map_initialized = 1 ;
@@ -369,10 +412,14 @@ static struct gh_server__process *gh_client__find_long_running_process(
369412
370413void gh_client__queue_oid (const struct object_id * oid )
371414{
372- // TODO consider removing this trace2. it is useful for interactive
373- // TODO debugging, but may generate way too much noise for a data
374- // TODO event.
375- trace2_printf ("gh_client__queue_oid: %s" , oid_to_hex (oid ));
415+ /*
416+ * Keep this trace as a printf only, so that it goes to the
417+ * perf log, but not the event log. It is useful for interactive
418+ * debugging, but generates way too much (unuseful) noise for the
419+ * database.
420+ */
421+ if (trace2_is_enabled ())
422+ trace2_printf ("gh_client__queue_oid: %s" , oid_to_hex (oid ));
376423
377424 if (!oidset_insert (& gh_client__oidset_queued , oid ))
378425 gh_client__oidset_count ++ ;
@@ -453,10 +500,14 @@ int gh_client__get_immediate(const struct object_id *oid,
453500 int nr_packfile = 0 ;
454501 int err = 0 ;
455502
456- // TODO consider removing this trace2. it is useful for interactive
457- // TODO debugging, but may generate way too much noise for a data
458- // TODO event.
459- trace2_printf ("gh_client__get_immediate: %s" , oid_to_hex (oid ));
503+ /*
504+ * Keep this trace as a printf only, so that it goes to the
505+ * perf log, but not the event log. It is useful for interactive
506+ * debugging, but generates way too much (unuseful) noise for the
507+ * database.
508+ */
509+ if (trace2_is_enabled ())
510+ trace2_printf ("gh_client__get_immediate: %s" , oid_to_hex (oid ));
460511
461512 entry = gh_client__find_long_running_process (CAP_OBJECTS );
462513 if (!entry )
@@ -485,3 +536,55 @@ int gh_client__get_immediate(const struct object_id *oid,
485536
486537 return err ;
487538}
539+
540+ /*
541+ * Ask gvfs-helper to prefetch commits-and-trees packfiles since a
542+ * given timestamp.
543+ *
544+ * If seconds_since_epoch is zero, gvfs-helper will scan the ODB for
545+ * the last received prefetch and ask for ones newer than that.
546+ */
547+ int gh_client__prefetch (timestamp_t seconds_since_epoch ,
548+ int * nr_packfiles_received )
549+ {
550+ struct gh_server__process * entry ;
551+ struct child_process * process ;
552+ enum gh_client__created ghc ;
553+ int nr_loose = 0 ;
554+ int nr_packfile = 0 ;
555+ int err = 0 ;
556+
557+ entry = gh_client__find_long_running_process (CAP_OBJECTS );
558+ if (!entry )
559+ return -1 ;
560+
561+ trace2_region_enter ("gh-client" , "objects/prefetch" , the_repository );
562+ trace2_data_intmax ("gh-client" , the_repository , "prefetch/since" ,
563+ seconds_since_epoch );
564+
565+ process = & entry -> subprocess .process ;
566+
567+ sigchain_push (SIGPIPE , SIG_IGN );
568+
569+ err = gh_client__send__objects_prefetch (process , seconds_since_epoch );
570+ if (!err )
571+ err = gh_client__objects__receive_response (
572+ process , & ghc , & nr_loose , & nr_packfile );
573+
574+ sigchain_pop (SIGPIPE );
575+
576+ if (err ) {
577+ subprocess_stop (& gh_server__subprocess_map ,
578+ (struct subprocess_entry * )entry );
579+ FREE_AND_NULL (entry );
580+ }
581+
582+ trace2_data_intmax ("gh-client" , the_repository ,
583+ "prefetch/packfile_count" , nr_packfile );
584+ trace2_region_leave ("gh-client" , "objects/prefetch" , the_repository );
585+
586+ if (nr_packfiles_received )
587+ * nr_packfiles_received = nr_packfile ;
588+
589+ return err ;
590+ }
0 commit comments