@@ -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}
@@ -333,7 +376,7 @@ static struct gh_server__process *gh_client__find_long_running_process(
333376 /*
334377 * Find an existing long-running process with the above command
335378 * line -or- create a new long-running process for this and
336- * subsequent 'get' requests.
379+ * subsequent requests.
337380 */
338381 if (!gh_server__subprocess_map_initialized ) {
339382 gh_server__subprocess_map_initialized = 1 ;
@@ -370,10 +413,14 @@ static struct gh_server__process *gh_client__find_long_running_process(
370413
371414void gh_client__queue_oid (const struct object_id * oid )
372415{
373- // TODO consider removing this trace2. it is useful for interactive
374- // TODO debugging, but may generate way too much noise for a data
375- // TODO event.
376- trace2_printf ("gh_client__queue_oid: %s" , oid_to_hex (oid ));
416+ /*
417+ * Keep this trace as a printf only, so that it goes to the
418+ * perf log, but not the event log. It is useful for interactive
419+ * debugging, but generates way too much (unuseful) noise for the
420+ * database.
421+ */
422+ if (trace2_is_enabled ())
423+ trace2_printf ("gh_client__queue_oid: %s" , oid_to_hex (oid ));
377424
378425 if (!oidset_insert (& gh_client__oidset_queued , oid ))
379426 gh_client__oidset_count ++ ;
@@ -454,10 +501,14 @@ int gh_client__get_immediate(const struct object_id *oid,
454501 int nr_packfile = 0 ;
455502 int err = 0 ;
456503
457- // TODO consider removing this trace2. it is useful for interactive
458- // TODO debugging, but may generate way too much noise for a data
459- // TODO event.
460- trace2_printf ("gh_client__get_immediate: %s" , oid_to_hex (oid ));
504+ /*
505+ * Keep this trace as a printf only, so that it goes to the
506+ * perf log, but not the event log. It is useful for interactive
507+ * debugging, but generates way too much (unuseful) noise for the
508+ * database.
509+ */
510+ if (trace2_is_enabled ())
511+ trace2_printf ("gh_client__get_immediate: %s" , oid_to_hex (oid ));
461512
462513 entry = gh_client__find_long_running_process (CAP_OBJECTS );
463514 if (!entry )
@@ -486,3 +537,55 @@ int gh_client__get_immediate(const struct object_id *oid,
486537
487538 return err ;
488539}
540+
541+ /*
542+ * Ask gvfs-helper to prefetch commits-and-trees packfiles since a
543+ * given timestamp.
544+ *
545+ * If seconds_since_epoch is zero, gvfs-helper will scan the ODB for
546+ * the last received prefetch and ask for ones newer than that.
547+ */
548+ int gh_client__prefetch (timestamp_t seconds_since_epoch ,
549+ int * nr_packfiles_received )
550+ {
551+ struct gh_server__process * entry ;
552+ struct child_process * process ;
553+ enum gh_client__created ghc ;
554+ int nr_loose = 0 ;
555+ int nr_packfile = 0 ;
556+ int err = 0 ;
557+
558+ entry = gh_client__find_long_running_process (CAP_OBJECTS );
559+ if (!entry )
560+ return -1 ;
561+
562+ trace2_region_enter ("gh-client" , "objects/prefetch" , the_repository );
563+ trace2_data_intmax ("gh-client" , the_repository , "prefetch/since" ,
564+ seconds_since_epoch );
565+
566+ process = & entry -> subprocess .process ;
567+
568+ sigchain_push (SIGPIPE , SIG_IGN );
569+
570+ err = gh_client__send__objects_prefetch (process , seconds_since_epoch );
571+ if (!err )
572+ err = gh_client__objects__receive_response (
573+ process , & ghc , & nr_loose , & nr_packfile );
574+
575+ sigchain_pop (SIGPIPE );
576+
577+ if (err ) {
578+ subprocess_stop (& gh_server__subprocess_map ,
579+ (struct subprocess_entry * )entry );
580+ FREE_AND_NULL (entry );
581+ }
582+
583+ trace2_data_intmax ("gh-client" , the_repository ,
584+ "prefetch/packfile_count" , nr_packfile );
585+ trace2_region_leave ("gh-client" , "objects/prefetch" , the_repository );
586+
587+ if (nr_packfiles_received )
588+ * nr_packfiles_received = nr_packfile ;
589+
590+ return err ;
591+ }
0 commit comments