1313
1414static struct oidset gh_client__oidset_queued = OIDSET_INIT ;
1515static unsigned long gh_client__oidset_count ;
16- static int gh_client__includes_immediate ;
1716
1817struct gh_server__process {
1918 struct subprocess_entry subprocess ; /* must be first */
@@ -24,13 +23,20 @@ static int gh_server__subprocess_map_initialized;
2423static struct hashmap gh_server__subprocess_map ;
2524static struct object_directory * gh_client__chosen_odb ;
2625
27- #define CAP_GET (1u<<1)
26+ /*
27+ * The "objects" capability has 2 verbs: "get" and "post".
28+ */
29+ #define CAP_OBJECTS (1u<<1)
30+ #define CAP_OBJECTS_NAME "objects"
31+
32+ #define CAP_OBJECTS__VERB_GET1_NAME "get"
33+ #define CAP_OBJECTS__VERB_POST_NAME "post"
2834
2935static int gh_client__start_fn (struct subprocess_entry * subprocess )
3036{
3137 static int versions [] = {1 , 0 };
3238 static struct subprocess_capability capabilities [] = {
33- { "get" , CAP_GET },
39+ { CAP_OBJECTS_NAME , CAP_OBJECTS },
3440 { NULL , 0 }
3541 };
3642
@@ -42,14 +48,16 @@ static int gh_client__start_fn(struct subprocess_entry *subprocess)
4248}
4349
4450/*
45- * Send:
51+ * Send the queued OIDs in the OIDSET to gvfs-helper for it to
52+ * fetch from the cache-server or main Git server using "/gvfs/objects"
53+ * POST semantics.
4654 *
47- * get LF
55+ * objects.post LF
4856 * (<hex-oid> LF)*
4957 * <flush>
5058 *
5159 */
52- static int gh_client__get__send_command (struct child_process * process )
60+ static int gh_client__send__objects_post (struct child_process * process )
5361{
5462 struct oidset_iter iter ;
5563 struct object_id * oid ;
@@ -60,7 +68,9 @@ static int gh_client__get__send_command(struct child_process *process)
6068 * so that we don't have to.
6169 */
6270
63- err = packet_write_fmt_gently (process -> in , "get\n" );
71+ err = packet_write_fmt_gently (
72+ process -> in ,
73+ (CAP_OBJECTS_NAME "." CAP_OBJECTS__VERB_POST_NAME "\n" ));
6474 if (err )
6575 return err ;
6676
@@ -79,6 +89,46 @@ static int gh_client__get__send_command(struct child_process *process)
7989 return 0 ;
8090}
8191
92+ /*
93+ * Send the given OID to gvfs-helper for it to fetch from the
94+ * cache-server or main Git server using "/gvfs/objects" GET
95+ * semantics.
96+ *
97+ * This ignores any queued OIDs.
98+ *
99+ * objects.get LF
100+ * <hex-oid> LF
101+ * <flush>
102+ *
103+ */
104+ static int gh_client__send__objects_get (struct child_process * process ,
105+ const struct object_id * oid )
106+ {
107+ int err ;
108+
109+ /*
110+ * We assume that all of the packet_ routines call error()
111+ * so that we don't have to.
112+ */
113+
114+ err = packet_write_fmt_gently (
115+ process -> in ,
116+ (CAP_OBJECTS_NAME "." CAP_OBJECTS__VERB_GET1_NAME "\n" ));
117+ if (err )
118+ return err ;
119+
120+ err = packet_write_fmt_gently (process -> in , "%s\n" ,
121+ oid_to_hex (oid ));
122+ if (err )
123+ return err ;
124+
125+ err = packet_flush_gently (process -> in );
126+ if (err )
127+ return err ;
128+
129+ return 0 ;
130+ }
131+
82132/*
83133 * Verify that the pathname found in the "odb" response line matches
84134 * what we requested.
@@ -148,7 +198,7 @@ static void gh_client__update_packed_git(const char *line)
148198}
149199
150200/*
151- * We expect :
201+ * Both CAP_OBJECTS verbs return the same format response :
152202 *
153203 * <odb>
154204 * <data>*
@@ -179,7 +229,7 @@ static void gh_client__update_packed_git(const char *line)
179229 * grouped with a queued request for a blob. The tree-walk *might* be
180230 * able to continue and let the 404 blob be handled later.
181231 */
182- static int gh_client__get__receive_response (
232+ static int gh_client__objects__receive_response (
183233 struct child_process * process ,
184234 enum gh_client__created * p_ghc ,
185235 int * p_nr_loose , int * p_nr_packfile )
@@ -259,17 +309,12 @@ static void gh_client__choose_odb(void)
259309 }
260310}
261311
262- static int gh_client__get (enum gh_client__created * p_ghc )
312+ static struct gh_server__process * gh_client__find_long_running_process (
313+ unsigned int cap_needed )
263314{
264315 struct gh_server__process * entry ;
265- struct child_process * process ;
266316 struct argv_array argv = ARGV_ARRAY_INIT ;
267317 struct strbuf quoted = STRBUF_INIT ;
268- int nr_loose = 0 ;
269- int nr_packfile = 0 ;
270- int err = 0 ;
271-
272- trace2_region_enter ("gh-client" , "get" , the_repository );
273318
274319 gh_client__choose_odb ();
275320
@@ -285,6 +330,11 @@ static int gh_client__get(enum gh_client__created *p_ghc)
285330
286331 sq_quote_argv_pretty (& quoted , argv .argv );
287332
333+ /*
334+ * Find an existing long-running process with the above command
335+ * line -or- create a new long-running process for this and
336+ * subsequent 'get' requests.
337+ */
288338 if (!gh_server__subprocess_map_initialized ) {
289339 gh_server__subprocess_map_initialized = 1 ;
290340 hashmap_init (& gh_server__subprocess_map ,
@@ -298,70 +348,24 @@ static int gh_client__get(enum gh_client__created *p_ghc)
298348 entry = xmalloc (sizeof (* entry ));
299349 entry -> supported_capabilities = 0 ;
300350
301- err = subprocess_start_argv (
302- & gh_server__subprocess_map , & entry -> subprocess , 1 ,
303- & argv , gh_client__start_fn );
304- if (err ) {
305- free (entry );
306- goto leave_region ;
307- }
351+ if (subprocess_start_argv (& gh_server__subprocess_map ,
352+ & entry -> subprocess , 1 ,
353+ & argv , gh_client__start_fn ))
354+ FREE_AND_NULL (entry );
308355 }
309356
310- process = & entry -> subprocess .process ;
311-
312- if (!(CAP_GET & entry -> supported_capabilities )) {
313- error ("gvfs-helper: does not support GET" );
357+ if (entry &&
358+ (entry -> supported_capabilities & cap_needed ) != cap_needed ) {
359+ error ("gvfs-helper: does not support needed capabilities" );
314360 subprocess_stop (& gh_server__subprocess_map ,
315361 (struct subprocess_entry * )entry );
316- free (entry );
317- err = -1 ;
318- goto leave_region ;
362+ FREE_AND_NULL (entry );
319363 }
320364
321- sigchain_push (SIGPIPE , SIG_IGN );
322-
323- err = gh_client__get__send_command (process );
324- if (!err )
325- err = gh_client__get__receive_response (process , p_ghc ,
326- & nr_loose , & nr_packfile );
327-
328- sigchain_pop (SIGPIPE );
329-
330- if (err ) {
331- subprocess_stop (& gh_server__subprocess_map ,
332- (struct subprocess_entry * )entry );
333- free (entry );
334- }
335-
336- leave_region :
337365 argv_array_clear (& argv );
338366 strbuf_release (& quoted );
339367
340- trace2_data_intmax ("gh-client" , the_repository ,
341- "get/immediate" , gh_client__includes_immediate );
342-
343- trace2_data_intmax ("gh-client" , the_repository ,
344- "get/nr_objects" , gh_client__oidset_count );
345-
346- if (nr_loose )
347- trace2_data_intmax ("gh-client" , the_repository ,
348- "get/nr_loose" , nr_loose );
349-
350- if (nr_packfile )
351- trace2_data_intmax ("gh-client" , the_repository ,
352- "get/nr_packfile" , nr_packfile );
353-
354- if (err )
355- trace2_data_intmax ("gh-client" , the_repository ,
356- "get/error" , err );
357-
358- trace2_region_leave ("gh-client" , "get" , the_repository );
359-
360- oidset_clear (& gh_client__oidset_queued );
361- gh_client__oidset_count = 0 ;
362- gh_client__includes_immediate = 0 ;
363-
364- return err ;
368+ return entry ;
365369}
366370
367371void gh_client__queue_oid (const struct object_id * oid )
@@ -388,28 +392,97 @@ void gh_client__queue_oid_array(const struct object_id *oids, int oid_nr)
388392 gh_client__queue_oid (& oids [k ]);
389393}
390394
395+ /*
396+ * Bulk fetch all of the queued OIDs in the OIDSET.
397+ */
391398int gh_client__drain_queue (enum gh_client__created * p_ghc )
392399{
400+ struct gh_server__process * entry ;
401+ struct child_process * process ;
402+ int nr_loose = 0 ;
403+ int nr_packfile = 0 ;
404+ int err = 0 ;
405+
393406 * p_ghc = GHC__CREATED__NOTHING ;
394407
395408 if (!gh_client__oidset_count )
396409 return 0 ;
397410
398- return gh_client__get (p_ghc );
411+ entry = gh_client__find_long_running_process (CAP_OBJECTS );
412+ if (!entry )
413+ return -1 ;
414+
415+ trace2_region_enter ("gh-client" , "objects/post" , the_repository );
416+
417+ process = & entry -> subprocess .process ;
418+
419+ sigchain_push (SIGPIPE , SIG_IGN );
420+
421+ err = gh_client__send__objects_post (process );
422+ if (!err )
423+ err = gh_client__objects__receive_response (
424+ process , p_ghc , & nr_loose , & nr_packfile );
425+
426+ sigchain_pop (SIGPIPE );
427+
428+ if (err ) {
429+ subprocess_stop (& gh_server__subprocess_map ,
430+ (struct subprocess_entry * )entry );
431+ FREE_AND_NULL (entry );
432+ }
433+
434+ trace2_data_intmax ("gh-client" , the_repository ,
435+ "objects/post/nr_objects" , gh_client__oidset_count );
436+ trace2_region_leave ("gh-client" , "objects/post" , the_repository );
437+
438+ oidset_clear (& gh_client__oidset_queued );
439+ gh_client__oidset_count = 0 ;
440+
441+ return err ;
399442}
400443
444+ /*
445+ * Get exactly 1 object immediately.
446+ * Ignore any queued objects.
447+ */
401448int gh_client__get_immediate (const struct object_id * oid ,
402449 enum gh_client__created * p_ghc )
403450{
404- gh_client__includes_immediate = 1 ;
451+ struct gh_server__process * entry ;
452+ struct child_process * process ;
453+ int nr_loose = 0 ;
454+ int nr_packfile = 0 ;
455+ int err = 0 ;
405456
406457 // TODO consider removing this trace2. it is useful for interactive
407458 // TODO debugging, but may generate way too much noise for a data
408459 // TODO event.
409460 trace2_printf ("gh_client__get_immediate: %s" , oid_to_hex (oid ));
410461
411- if (!oidset_insert (& gh_client__oidset_queued , oid ))
412- gh_client__oidset_count ++ ;
462+ entry = gh_client__find_long_running_process (CAP_OBJECTS );
463+ if (!entry )
464+ return -1 ;
465+
466+ trace2_region_enter ("gh-client" , "objects/get" , the_repository );
413467
414- return gh_client__drain_queue (p_ghc );
468+ process = & entry -> subprocess .process ;
469+
470+ sigchain_push (SIGPIPE , SIG_IGN );
471+
472+ err = gh_client__send__objects_get (process , oid );
473+ if (!err )
474+ err = gh_client__objects__receive_response (
475+ process , p_ghc , & nr_loose , & nr_packfile );
476+
477+ sigchain_pop (SIGPIPE );
478+
479+ if (err ) {
480+ subprocess_stop (& gh_server__subprocess_map ,
481+ (struct subprocess_entry * )entry );
482+ FREE_AND_NULL (entry );
483+ }
484+
485+ trace2_region_leave ("gh-client" , "objects/get" , the_repository );
486+
487+ return err ;
415488}
0 commit comments