fetch: ignore SIGPIPE during network operation
[gitweb.git] / fetch-pack.c
index 79007f996c899272b6ae9044a9029776181f3438..1dcb0f7cd79da277602774cb570043b156713101 100644 (file)
@@ -76,8 +76,7 @@ struct alternate_object_cache {
        size_t nr, alloc;
 };
 
-static void cache_one_alternate(const char *refname,
-                               const struct object_id *oid,
+static void cache_one_alternate(const struct object_id *oid,
                                void *vcache)
 {
        struct alternate_object_cache *cache = vcache;
@@ -190,8 +189,10 @@ static void send_request(struct fetch_pack_args *args,
        if (args->stateless_rpc) {
                send_sideband(fd, -1, buf->buf, buf->len, LARGE_PACKET_MAX);
                packet_flush(fd);
-       } else
-               write_or_die(fd, buf->buf, buf->len);
+       } else {
+               if (write_in_full(fd, buf->buf, buf->len) < 0)
+                       die_errno(_("unable to write to remote"));
+       }
 }
 
 static void insert_one_alternate_object(struct fetch_negotiator *negotiator,
@@ -532,21 +533,14 @@ static void add_refs_to_oidset(struct oidset *oids, struct ref *refs)
                oidset_insert(oids, &refs->old_oid);
 }
 
-static int tip_oids_contain(struct oidset *tip_oids,
-                           struct ref *unmatched, struct ref *newlist,
-                           const struct object_id *id)
+static int is_unmatched_ref(const struct ref *ref)
 {
-       /*
-        * Note that this only looks at the ref lists the first time it's
-        * called. This works out in filter_refs() because even though it may
-        * add to "newlist" between calls, the additions will always be for
-        * oids that are already in the set.
-        */
-       if (!tip_oids->map.map.tablesize) {
-               add_refs_to_oidset(tip_oids, unmatched);
-               add_refs_to_oidset(tip_oids, newlist);
-       }
-       return oidset_contains(tip_oids, id);
+       struct object_id oid;
+       const char *p;
+       return  ref->match_status == REF_NOT_MATCHED &&
+               !parse_oid_hex(ref->name, &oid, &p) &&
+               *p == '\0' &&
+               oideq(&oid, &ref->old_oid);
 }
 
 static void filter_refs(struct fetch_pack_args *args,
@@ -559,6 +553,8 @@ static void filter_refs(struct fetch_pack_args *args,
        struct ref *ref, *next;
        struct oidset tip_oids = OIDSET_INIT;
        int i;
+       int strict = !(allow_unadvertised_object_request &
+                      (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1));
 
        i = 0;
        for (ref = *refs; ref; ref = next) {
@@ -595,23 +591,25 @@ static void filter_refs(struct fetch_pack_args *args,
                }
        }
 
+       if (strict) {
+               for (i = 0; i < nr_sought; i++) {
+                       ref = sought[i];
+                       if (!is_unmatched_ref(ref))
+                               continue;
+
+                       add_refs_to_oidset(&tip_oids, unmatched);
+                       add_refs_to_oidset(&tip_oids, newlist);
+                       break;
+               }
+       }
+
        /* Append unmatched requests to the list */
        for (i = 0; i < nr_sought; i++) {
-               struct object_id oid;
-               const char *p;
-
                ref = sought[i];
-               if (ref->match_status != REF_NOT_MATCHED)
-                       continue;
-               if (parse_oid_hex(ref->name, &oid, &p) ||
-                   *p != '\0' ||
-                   oidcmp(&oid, &ref->old_oid))
+               if (!is_unmatched_ref(ref))
                        continue;
 
-               if ((allow_unadvertised_object_request &
-                    (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1)) ||
-                   tip_oids_contain(&tip_oids, unmatched, newlist,
-                                    &ref->old_oid)) {
+               if (!strict || oidset_contains(&tip_oids, &ref->old_oid)) {
                        ref->match_status = REF_MATCHED;
                        *newtail = copy_ref(ref);
                        newtail = &(*newtail)->next;
@@ -1182,7 +1180,8 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
 
        /* Send request */
        packet_buf_flush(&req_buf);
-       write_or_die(fd_out, req_buf.buf, req_buf.len);
+       if (write_in_full(fd_out, req_buf.buf, req_buf.len) < 0)
+               die_errno(_("unable to write request to remote"));
 
        strbuf_release(&req_buf);
        return ret;
@@ -1252,6 +1251,18 @@ static int process_acks(struct fetch_negotiator *negotiator,
            reader->status != PACKET_READ_DELIM)
                die(_("error processing acks: %d"), reader->status);
 
+       /*
+        * If an "acknowledgments" section is sent, a packfile is sent if and
+        * only if "ready" was sent in this section. The other sections
+        * ("shallow-info" and "wanted-refs") are sent only if a packfile is
+        * sent. Therefore, a DELIM is expected if "ready" is sent, and a FLUSH
+        * otherwise.
+        */
+       if (received_ready && reader->status != PACKET_READ_DELIM)
+               die(_("expected packfile to be sent after 'ready'"));
+       if (!received_ready && reader->status != PACKET_READ_FLUSH)
+               die(_("expected no other sections to be sent after no 'ready'"));
+
        /* return 0 if no common, 1 if there are common, or 2 if ready */
        return received_ready ? 2 : (received_ack ? 1 : 0);
 }
@@ -1629,7 +1640,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
                parse_list_objects_filter(&args->filter_options, "blob:none");
        }
 
-       if (!ref) {
+       if (version != protocol_v2 && !ref) {
                packet_flush(fd[1]);
                die(_("no matching remote head"));
        }