1717
1818static struct oidset gh_client__oidset_queued = OIDSET_INIT ;
1919static unsigned long gh_client__oidset_count ;
20- static int gh_client__includes_immediate ;
2120
2221struct gh_server__process {
2322 struct subprocess_entry subprocess ; /* must be first */
@@ -28,13 +27,20 @@ static int gh_server__subprocess_map_initialized;
2827static struct hashmap gh_server__subprocess_map ;
2928static struct odb_source * gh_client__chosen_odb ;
3029
31- #define CAP_GET (1u<<1)
30+ /*
31+ * The "objects" capability has 2 verbs: "get" and "post".
32+ */
33+ #define CAP_OBJECTS (1u<<1)
34+ #define CAP_OBJECTS_NAME "objects"
35+
36+ #define CAP_OBJECTS__VERB_GET1_NAME "get"
37+ #define CAP_OBJECTS__VERB_POST_NAME "post"
3238
3339static int gh_client__start_fn (struct subprocess_entry * subprocess )
3440{
3541 static int versions [] = {1 , 0 };
3642 static struct subprocess_capability capabilities [] = {
37- { "get" , CAP_GET },
43+ { CAP_OBJECTS_NAME , CAP_OBJECTS },
3844 { NULL , 0 }
3945 };
4046
@@ -46,14 +52,16 @@ static int gh_client__start_fn(struct subprocess_entry *subprocess)
4652}
4753
4854/*
49- * Send:
55+ * Send the queued OIDs in the OIDSET to gvfs-helper for it to
56+ * fetch from the cache-server or main Git server using "/gvfs/objects"
57+ * POST semantics.
5058 *
51- * get LF
59+ * objects.post LF
5260 * (<hex-oid> LF)*
5361 * <flush>
5462 *
5563 */
56- static int gh_client__get__send_command (struct child_process * process )
64+ static int gh_client__send__objects_post (struct child_process * process )
5765{
5866 struct oidset_iter iter ;
5967 struct object_id * oid ;
@@ -64,7 +72,9 @@ static int gh_client__get__send_command(struct child_process *process)
6472 * so that we don't have to.
6573 */
6674
67- err = packet_write_fmt_gently (process -> in , "get\n" );
75+ err = packet_write_fmt_gently (
76+ process -> in ,
77+ (CAP_OBJECTS_NAME "." CAP_OBJECTS__VERB_POST_NAME "\n" ));
6878 if (err )
6979 return err ;
7080
@@ -83,6 +93,46 @@ static int gh_client__get__send_command(struct child_process *process)
8393 return 0 ;
8494}
8595
96+ /*
97+ * Send the given OID to gvfs-helper for it to fetch from the
98+ * cache-server or main Git server using "/gvfs/objects" GET
99+ * semantics.
100+ *
101+ * This ignores any queued OIDs.
102+ *
103+ * objects.get LF
104+ * <hex-oid> LF
105+ * <flush>
106+ *
107+ */
108+ static int gh_client__send__objects_get (struct child_process * process ,
109+ const struct object_id * oid )
110+ {
111+ int err ;
112+
113+ /*
114+ * We assume that all of the packet_ routines call error()
115+ * so that we don't have to.
116+ */
117+
118+ err = packet_write_fmt_gently (
119+ process -> in ,
120+ (CAP_OBJECTS_NAME "." CAP_OBJECTS__VERB_GET1_NAME "\n" ));
121+ if (err )
122+ return err ;
123+
124+ err = packet_write_fmt_gently (process -> in , "%s\n" ,
125+ oid_to_hex (oid ));
126+ if (err )
127+ return err ;
128+
129+ err = packet_flush_gently (process -> in );
130+ if (err )
131+ return err ;
132+
133+ return 0 ;
134+ }
135+
86136/*
87137 * Update the loose object cache to include the newly created
88138 * object.
@@ -131,7 +181,7 @@ static void gh_client__update_packed_git(const char *line)
131181}
132182
133183/*
134- * We expect :
184+ * Both CAP_OBJECTS verbs return the same format response :
135185 *
136186 * <odb>
137187 * <data>*
@@ -162,7 +212,7 @@ static void gh_client__update_packed_git(const char *line)
162212 * grouped with a queued request for a blob. The tree-walk *might* be
163213 * able to continue and let the 404 blob be handled later.
164214 */
165- static int gh_client__get__receive_response (
215+ static int gh_client__objects__receive_response (
166216 struct child_process * process ,
167217 enum gh_client__created * p_ghc ,
168218 int * p_nr_loose , int * p_nr_packfile )
@@ -241,17 +291,12 @@ static void gh_client__choose_odb(void)
241291 }
242292}
243293
244- static int gh_client__get (enum gh_client__created * p_ghc )
294+ static struct gh_server__process * gh_client__find_long_running_process (
295+ unsigned int cap_needed )
245296{
246297 struct gh_server__process * entry ;
247- struct child_process * process ;
248298 struct strvec argv = STRVEC_INIT ;
249299 struct strbuf quoted = STRBUF_INIT ;
250- int nr_loose = 0 ;
251- int nr_packfile = 0 ;
252- int err = 0 ;
253-
254- trace2_region_enter ("gh-client" , "get" , the_repository );
255300
256301 gh_client__choose_odb ();
257302
@@ -267,6 +312,11 @@ static int gh_client__get(enum gh_client__created *p_ghc)
267312
268313 sq_quote_argv_pretty (& quoted , argv .v );
269314
315+ /*
316+ * Find an existing long-running process with the above command
317+ * line -or- create a new long-running process for this and
318+ * subsequent 'get' requests.
319+ */
270320 if (!gh_server__subprocess_map_initialized ) {
271321 gh_server__subprocess_map_initialized = 1 ;
272322 hashmap_init (& gh_server__subprocess_map ,
@@ -280,70 +330,24 @@ static int gh_client__get(enum gh_client__created *p_ghc)
280330 entry = xmalloc (sizeof (* entry ));
281331 entry -> supported_capabilities = 0 ;
282332
283- err = subprocess_start_strvec (
284- & gh_server__subprocess_map , & entry -> subprocess , 1 ,
285- & argv , gh_client__start_fn );
286- if (err ) {
287- free (entry );
288- goto leave_region ;
289- }
333+ if (subprocess_start_strvec (& gh_server__subprocess_map ,
334+ & entry -> subprocess , 1 ,
335+ & argv , gh_client__start_fn ))
336+ FREE_AND_NULL (entry );
290337 }
291338
292- process = & entry -> subprocess .process ;
293-
294- if (!(CAP_GET & entry -> supported_capabilities )) {
295- error ("gvfs-helper: does not support GET" );
296- subprocess_stop (& gh_server__subprocess_map ,
297- (struct subprocess_entry * )entry );
298- free (entry );
299- err = -1 ;
300- goto leave_region ;
301- }
302-
303- sigchain_push (SIGPIPE , SIG_IGN );
304-
305- err = gh_client__get__send_command (process );
306- if (!err )
307- err = gh_client__get__receive_response (process , p_ghc ,
308- & nr_loose , & nr_packfile );
309-
310- sigchain_pop (SIGPIPE );
311-
312- if (err ) {
339+ if (entry &&
340+ (entry -> supported_capabilities & cap_needed ) != cap_needed ) {
341+ error ("gvfs-helper: does not support needed capabilities" );
313342 subprocess_stop (& gh_server__subprocess_map ,
314343 (struct subprocess_entry * )entry );
315- free (entry );
344+ FREE_AND_NULL (entry );
316345 }
317346
318- leave_region :
319347 strvec_clear (& argv );
320348 strbuf_release (& quoted );
321349
322- trace2_data_intmax ("gh-client" , the_repository ,
323- "get/immediate" , gh_client__includes_immediate );
324-
325- trace2_data_intmax ("gh-client" , the_repository ,
326- "get/nr_objects" , gh_client__oidset_count );
327-
328- if (nr_loose )
329- trace2_data_intmax ("gh-client" , the_repository ,
330- "get/nr_loose" , nr_loose );
331-
332- if (nr_packfile )
333- trace2_data_intmax ("gh-client" , the_repository ,
334- "get/nr_packfile" , nr_packfile );
335-
336- if (err )
337- trace2_data_intmax ("gh-client" , the_repository ,
338- "get/error" , err );
339-
340- trace2_region_leave ("gh-client" , "get" , the_repository );
341-
342- oidset_clear (& gh_client__oidset_queued );
343- gh_client__oidset_count = 0 ;
344- gh_client__includes_immediate = 0 ;
345-
346- return err ;
350+ return entry ;
347351}
348352
349353void gh_client__queue_oid (const struct object_id * oid )
@@ -370,27 +374,97 @@ void gh_client__queue_oid_array(const struct object_id *oids, int oid_nr)
370374 gh_client__queue_oid (& oids [k ]);
371375}
372376
377+ /*
378+ * Bulk fetch all of the queued OIDs in the OIDSET.
379+ */
373380int gh_client__drain_queue (enum gh_client__created * p_ghc )
374381{
382+ struct gh_server__process * entry ;
383+ struct child_process * process ;
384+ int nr_loose = 0 ;
385+ int nr_packfile = 0 ;
386+ int err = 0 ;
387+
375388 * p_ghc = GHC__CREATED__NOTHING ;
376389
377390 if (!gh_client__oidset_count )
378391 return 0 ;
379392
380- return gh_client__get (p_ghc );
393+ entry = gh_client__find_long_running_process (CAP_OBJECTS );
394+ if (!entry )
395+ return -1 ;
396+
397+ trace2_region_enter ("gh-client" , "objects/post" , the_repository );
398+
399+ process = & entry -> subprocess .process ;
400+
401+ sigchain_push (SIGPIPE , SIG_IGN );
402+
403+ err = gh_client__send__objects_post (process );
404+ if (!err )
405+ err = gh_client__objects__receive_response (
406+ process , p_ghc , & nr_loose , & nr_packfile );
407+
408+ sigchain_pop (SIGPIPE );
409+
410+ if (err ) {
411+ subprocess_stop (& gh_server__subprocess_map ,
412+ (struct subprocess_entry * )entry );
413+ FREE_AND_NULL (entry );
414+ }
415+
416+ trace2_data_intmax ("gh-client" , the_repository ,
417+ "objects/post/nr_objects" , gh_client__oidset_count );
418+ trace2_region_leave ("gh-client" , "objects/post" , the_repository );
419+
420+ oidset_clear (& gh_client__oidset_queued );
421+ gh_client__oidset_count = 0 ;
422+
423+ return err ;
381424}
425+
426+ /*
427+ * Get exactly 1 object immediately.
428+ * Ignore any queued objects.
429+ */
382430int gh_client__get_immediate (const struct object_id * oid ,
383431 enum gh_client__created * p_ghc )
384432{
385- gh_client__includes_immediate = 1 ;
433+ struct gh_server__process * entry ;
434+ struct child_process * process ;
435+ int nr_loose = 0 ;
436+ int nr_packfile = 0 ;
437+ int err = 0 ;
386438
387439 // TODO consider removing this trace2. it is useful for interactive
388440 // TODO debugging, but may generate way too much noise for a data
389441 // TODO event.
390442 trace2_printf ("gh_client__get_immediate: %s" , oid_to_hex (oid ));
391443
392- if (!oidset_insert (& gh_client__oidset_queued , oid ))
393- gh_client__oidset_count ++ ;
444+ entry = gh_client__find_long_running_process (CAP_OBJECTS );
445+ if (!entry )
446+ return -1 ;
447+
448+ trace2_region_enter ("gh-client" , "objects/get" , the_repository );
394449
395- return gh_client__drain_queue (p_ghc );
450+ process = & entry -> subprocess .process ;
451+
452+ sigchain_push (SIGPIPE , SIG_IGN );
453+
454+ err = gh_client__send__objects_get (process , oid );
455+ if (!err )
456+ err = gh_client__objects__receive_response (
457+ process , p_ghc , & nr_loose , & nr_packfile );
458+
459+ sigchain_pop (SIGPIPE );
460+
461+ if (err ) {
462+ subprocess_stop (& gh_server__subprocess_map ,
463+ (struct subprocess_entry * )entry );
464+ FREE_AND_NULL (entry );
465+ }
466+
467+ trace2_region_leave ("gh-client" , "objects/get" , the_repository );
468+
469+ return err ;
396470}
0 commit comments