Skip to content

Commit 4a13c4d

Browse files
committed
Merge branch 'jc/maint-smart-http-race-upload-pack' into maint
* jc/maint-smart-http-race-upload-pack: helping smart-http/stateless-rpc fetch race
2 parents 02c2c60 + 051e400 commit 4a13c4d

File tree

1 file changed

+98
-9
lines changed

1 file changed

+98
-9
lines changed

upload-pack.c

Lines changed: 98 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "revision.h"
1111
#include "list-objects.h"
1212
#include "run-command.h"
13+
#include "sigchain.h"
1314

1415
static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=<n>] <dir>";
1516

@@ -498,11 +499,95 @@ static int get_common_commits(void)
498499
}
499500
}
500501

502+
static void check_non_tip(void)
503+
{
504+
static const char *argv[] = {
505+
"rev-list", "--stdin", NULL,
506+
};
507+
static struct child_process cmd;
508+
struct object *o;
509+
char namebuf[42]; /* ^ + SHA-1 + LF */
510+
int i;
511+
512+
/* In the normal in-process case non-tip request can never happen */
513+
if (!stateless_rpc)
514+
goto error;
515+
516+
cmd.argv = argv;
517+
cmd.git_cmd = 1;
518+
cmd.no_stderr = 1;
519+
cmd.in = -1;
520+
cmd.out = -1;
521+
522+
if (start_command(&cmd))
523+
goto error;
524+
525+
/*
526+
* If rev-list --stdin encounters an unknown commit, it
527+
* terminates, which will cause SIGPIPE in the write loop
528+
* below.
529+
*/
530+
sigchain_push(SIGPIPE, SIG_IGN);
531+
532+
namebuf[0] = '^';
533+
namebuf[41] = '\n';
534+
for (i = get_max_object_index(); 0 < i; ) {
535+
o = get_indexed_object(--i);
536+
if (!(o->flags & OUR_REF))
537+
continue;
538+
memcpy(namebuf + 1, sha1_to_hex(o->sha1), 40);
539+
if (write_in_full(cmd.in, namebuf, 42) < 0)
540+
goto error;
541+
}
542+
namebuf[40] = '\n';
543+
for (i = 0; i < want_obj.nr; i++) {
544+
o = want_obj.objects[i].item;
545+
if (o->flags & OUR_REF)
546+
continue;
547+
memcpy(namebuf, sha1_to_hex(o->sha1), 40);
548+
if (write_in_full(cmd.in, namebuf, 41) < 0)
549+
goto error;
550+
}
551+
close(cmd.in);
552+
553+
sigchain_pop(SIGPIPE);
554+
555+
/*
556+
* The commits out of the rev-list are not ancestors of
557+
* our ref.
558+
*/
559+
i = read_in_full(cmd.out, namebuf, 1);
560+
if (i)
561+
goto error;
562+
close(cmd.out);
563+
564+
/*
565+
* rev-list may have died by encountering a bad commit
566+
* in the history, in which case we do want to bail out
567+
* even when it showed no commit.
568+
*/
569+
if (finish_command(&cmd))
570+
goto error;
571+
572+
/* All the non-tip ones are ancestors of what we advertised */
573+
return;
574+
575+
error:
576+
/* Pick one of them (we know there at least is one) */
577+
for (i = 0; i < want_obj.nr; i++) {
578+
o = want_obj.objects[i].item;
579+
if (!(o->flags & OUR_REF))
580+
die("git upload-pack: not our ref %s",
581+
sha1_to_hex(o->sha1));
582+
}
583+
}
584+
501585
static void receive_needs(void)
502586
{
503587
struct object_array shallows = OBJECT_ARRAY_INIT;
504588
static char line[1000];
505589
int len, depth = 0;
590+
int has_non_tip = 0;
506591

507592
shallow_nr = 0;
508593
if (debug_fd)
@@ -559,26 +644,30 @@ static void receive_needs(void)
559644
if (strstr(line+45, "include-tag"))
560645
use_include_tag = 1;
561646

562-
/* We have sent all our refs already, and the other end
563-
* should have chosen out of them; otherwise they are
564-
* asking for nonsense.
565-
*
566-
* Hmph. We may later want to allow "want" line that
567-
* asks for something like "master~10" (symbolic)...
568-
* would it make sense? I don't know.
569-
*/
570647
o = lookup_object(sha1_buf);
571-
if (!o || !(o->flags & OUR_REF))
648+
if (!o)
572649
die("git upload-pack: not our ref %s",
573650
sha1_to_hex(sha1_buf));
574651
if (!(o->flags & WANTED)) {
575652
o->flags |= WANTED;
653+
if (!(o->flags & OUR_REF))
654+
has_non_tip = 1;
576655
add_object_array(o, NULL, &want_obj);
577656
}
578657
}
579658
if (debug_fd)
580659
write_str_in_full(debug_fd, "#E\n");
581660

661+
/*
662+
* We have sent all our refs already, and the other end
663+
* should have chosen out of them. When we are operating
664+
* in the stateless RPC mode, however, their choice may
665+
* have been based on the set of older refs advertised
666+
* by another process that handled the initial request.
667+
*/
668+
if (has_non_tip)
669+
check_non_tip();
670+
582671
if (!use_sideband && daemon_mode)
583672
no_progress = 1;
584673

0 commit comments

Comments
 (0)