Merge branch 'jt/accept-capability-advertisement-when-fetching-from-void'
authorJunio C Hamano <gitster@pobox.com>
Wed, 21 Sep 2016 22:15:17 +0000 (15:15 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 21 Sep 2016 22:15:18 +0000 (15:15 -0700)
JGit can show a fake ref "capabilities^{}" to "git fetch" when it
does not advertise any refs, but "git fetch" was not prepared to
see such an advertisement.  When the other side disconnects without
giving any ref advertisement, we used to say "there may not be a
repository at that URL", but we may have seen other advertisement
like "shallow" and ".have" in which case we definitely know that a
repository is there.  The code to detect this case has also been
updated.

* jt/accept-capability-advertisement-when-fetching-from-void:
  connect: advertized capability is not a ref
  connect: tighten check for unexpected early hang up
  tests: move test_lazy_prereq JGIT to test-lib.sh

1  2 
connect.c
t/test-lib.sh

diff --combined connect.c
+++ b/connect.c
@@@ -43,9 -43,9 +43,9 @@@ int check_ref_type(const struct ref *re
        return check_ref(ref->name, flags);
  }
  
- static void die_initial_contact(int got_at_least_one_head)
+ static void die_initial_contact(int unexpected)
  {
-       if (got_at_least_one_head)
+       if (unexpected)
                die("The remote end hung up upon initial contact");
        else
                die("Could not read from remote repository.\n\n"
@@@ -115,10 -115,18 +115,18 @@@ struct ref **get_remote_heads(int in, c
                              struct sha1_array *shallow_points)
  {
        struct ref **orig_list = list;
-       int got_at_least_one_head = 0;
+       /*
+        * A hang-up after seeing some response from the other end
+        * means that it is unexpected, as we know the other end is
+        * willing to talk to us.  A hang-up before seeing any
+        * response does not necessarily mean an ACL problem, though.
+        */
+       int saw_response;
+       int got_dummy_ref_with_capabilities_declaration = 0;
  
        *list = NULL;
-       for (;;) {
+       for (saw_response = 0; ; saw_response = 1) {
                struct ref *ref;
                struct object_id old_oid;
                char *name;
                                  PACKET_READ_GENTLE_ON_EOF |
                                  PACKET_READ_CHOMP_NEWLINE);
                if (len < 0)
-                       die_initial_contact(got_at_least_one_head);
+                       die_initial_contact(saw_response);
  
                if (!len)
                        break;
                        continue;
                }
  
+               if (!strcmp(name, "capabilities^{}")) {
+                       if (saw_response)
+                               die("protocol error: unexpected capabilities^{}");
+                       if (got_dummy_ref_with_capabilities_declaration)
+                               die("protocol error: multiple capabilities^{}");
+                       got_dummy_ref_with_capabilities_declaration = 1;
+                       continue;
+               }
                if (!check_ref(name, flags))
                        continue;
+               if (got_dummy_ref_with_capabilities_declaration)
+                       die("protocol error: unexpected ref after capabilities^{}");
                ref = alloc_ref(buffer + GIT_SHA1_HEXSZ + 1);
                oidcpy(&ref->old_oid, &old_oid);
                *list = ref;
                list = &ref->next;
-               got_at_least_one_head = 1;
        }
  
        annotate_refs_with_symref_info(*orig_list);
@@@ -658,19 -678,6 +678,19 @@@ static enum protocol parse_connect_url(
  
  static struct child_process no_fork = CHILD_PROCESS_INIT;
  
 +static const char *get_ssh_command(void)
 +{
 +      const char *ssh;
 +
 +      if ((ssh = getenv("GIT_SSH_COMMAND")))
 +              return ssh;
 +
 +      if (!git_config_get_string_const("core.sshcommand", &ssh))
 +              return ssh;
 +
 +      return NULL;
 +}
 +
  /*
   * This returns a dummy child_process if the transport protocol does not
   * need fork(2), or a struct child_process object if it does.  Once done,
@@@ -771,7 -778,7 +791,7 @@@ struct child_process *git_connect(int f
                                return NULL;
                        }
  
 -                      ssh = getenv("GIT_SSH_COMMAND");
 +                      ssh = get_ssh_command();
                        if (!ssh) {
                                const char *base;
                                char *ssh_dup;
diff --combined t/test-lib.sh
@@@ -89,7 -89,6 +89,7 @@@ unset VISUAL EMAIL LANGUAGE COLUMNS $("
                UNZIP
                PERF_
                CURL_VERBOSE
 +              TRACE_CURL
        ));
        my @vars = grep(/^GIT_/ && !/^GIT_($ok)/o, @env);
        print join("\n", @vars);
@@@ -688,9 -687,9 +688,9 @@@ test_done () 
                test_results_dir="$TEST_OUTPUT_DIRECTORY/test-results"
                mkdir -p "$test_results_dir"
                base=${0##*/}
 -              test_results_path="$test_results_dir/${base%.sh}-$$.counts"
 +              test_results_path="$test_results_dir/${base%.sh}.counts"
  
 -              cat >>"$test_results_path" <<-EOF
 +              cat >"$test_results_path" <<-EOF
                total $test_count
                success $test_success
                fixed $test_fixed
@@@ -1073,6 -1072,10 +1073,10 @@@ test_lazy_prereq NOT_ROOT 
        test "$uid" != 0
  '
  
+ test_lazy_prereq JGIT '
+       type jgit
+ '
  # SANITY is about "can you correctly predict what the filesystem would
  # do by only looking at the permission bits of the files and
  # directories?"  A typical example of !SANITY is running the test