Merge branch 'me/fetch-into-shallow-safety'
authorJunio C Hamano <gitster@pobox.com>
Wed, 1 Jul 2015 21:02:33 +0000 (14:02 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 1 Jul 2015 21:02:33 +0000 (14:02 -0700)
"git fetch --depth=<depth>" and "git clone --depth=<depth>" issued
a shallow transfer request even to an upload-pack that does not
support the capability.

* me/fetch-into-shallow-safety:
fetch-pack: check for shallow if depth given

1  2 
fetch-pack.c
diff --combined fetch-pack.c
index a912935a63c225b49eb67174d4142de41758dbe8,e3f4ef2c7a5320e959079de12023b0d343e93592..a1367721527a6a354e80da76088c341eb1273bd5
@@@ -1,5 -1,4 +1,5 @@@
  #include "cache.h"
 +#include "lockfile.h"
  #include "refs.h"
  #include "pkt-line.h"
  #include "commit.h"
@@@ -43,12 -42,7 +43,12 @@@ static int marked
  #define MAX_IN_VAIN 256
  
  static struct prio_queue rev_list = { compare_commits_by_commit_date };
 -static int non_common_revs, multi_ack, use_sideband, allow_tip_sha1_in_want;
 +static int non_common_revs, multi_ack, use_sideband;
 +/* Allow specifying sha1 if it is a ref tip. */
 +#define ALLOW_TIP_SHA1        01
 +/* Allow request of a sha1 if it is reachable from a ref (possibly hidden ref). */
 +#define ALLOW_REACHABLE_SHA1  02
 +static unsigned int allow_unadvertised_object_request;
  
  static void rev_list_push(struct commit *commit, int mark)
  {
@@@ -65,7 -59,7 +65,7 @@@
        }
  }
  
 -static int rev_list_insert_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 +static int rev_list_insert_ref(const char *refname, const unsigned char *sha1)
  {
        struct object *o = deref_tag(parse_object(sha1), refname, 0);
  
        return 0;
  }
  
 -static int clear_marks(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 +static int rev_list_insert_ref_oid(const char *refname, const struct object_id *oid,
 +                                 int flag, void *cb_data)
  {
 -      struct object *o = deref_tag(parse_object(sha1), refname, 0);
 +      return rev_list_insert_ref(refname, oid->hash);
 +}
 +
 +static int clear_marks(const char *refname, const struct object_id *oid,
 +                     int flag, void *cb_data)
 +{
 +      struct object *o = deref_tag(parse_object(oid->hash), refname, 0);
  
        if (o && o->type == OBJ_COMMIT)
                clear_commit_marks((struct commit *)o,
@@@ -202,23 -189,20 +202,23 @@@ static enum ack_type get_ack(int fd, un
  {
        int len;
        char *line = packet_read_line(fd, &len);
 +      const char *arg;
  
        if (!len)
                die("git fetch-pack: expected ACK/NAK, got EOF");
        if (!strcmp(line, "NAK"))
                return NAK;
 -      if (starts_with(line, "ACK ")) {
 -              if (!get_sha1_hex(line+4, result_sha1)) {
 -                      if (len < 45)
 +      if (skip_prefix(line, "ACK ", &arg)) {
 +              if (!get_sha1_hex(arg, result_sha1)) {
 +                      arg += 40;
 +                      len -= arg - line;
 +                      if (len < 1)
                                return ACK;
 -                      if (strstr(line+45, "continue"))
 +                      if (strstr(arg, "continue"))
                                return ACK_continue;
 -                      if (strstr(line+45, "common"))
 +                      if (strstr(arg, "common"))
                                return ACK_common;
 -                      if (strstr(line+45, "ready"))
 +                      if (strstr(arg, "ready"))
                                return ACK_ready;
                        return ACK;
                }
@@@ -238,7 -222,7 +238,7 @@@ static void send_request(struct fetch_p
  
  static void insert_one_alternate_ref(const struct ref *ref, void *unused)
  {
 -      rev_list_insert_ref(NULL, ref->old_sha1, 0, NULL);
 +      rev_list_insert_ref(NULL, ref->old_sha1);
  }
  
  #define INITIAL_FLUSH 16
@@@ -275,7 -259,7 +275,7 @@@ static int find_common(struct fetch_pac
                for_each_ref(clear_marks, NULL);
        marked = 1;
  
 -      for_each_ref(rev_list_insert_ref, NULL);
 +      for_each_ref(rev_list_insert_ref_oid, NULL);
        for_each_alternate_ref(insert_one_alternate_ref, NULL);
  
        fetching = 0;
  
        if (args->depth > 0) {
                char *line;
 +              const char *arg;
                unsigned char sha1[20];
  
                send_request(args, fd[1], &req_buf);
                while ((line = packet_read_line(fd[0], NULL))) {
 -                      if (starts_with(line, "shallow ")) {
 -                              if (get_sha1_hex(line + 8, sha1))
 +                      if (skip_prefix(line, "shallow ", &arg)) {
 +                              if (get_sha1_hex(arg, sha1))
                                        die("invalid shallow line: %s", line);
                                register_shallow(sha1);
                                continue;
                        }
 -                      if (starts_with(line, "unshallow ")) {
 -                              if (get_sha1_hex(line + 10, sha1))
 +                      if (skip_prefix(line, "unshallow ", &arg)) {
 +                              if (get_sha1_hex(arg, sha1))
                                        die("invalid unshallow line: %s", line);
                                if (!lookup_object(sha1))
                                        die("object not found: %s", line);
@@@ -478,7 -461,7 +478,7 @@@ done
  
  static struct commit_list *complete;
  
 -static int mark_complete(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 +static int mark_complete(const unsigned char *sha1)
  {
        struct object *o = parse_object(sha1);
  
        return 0;
  }
  
 +static int mark_complete_oid(const char *refname, const struct object_id *oid,
 +                           int flag, void *cb_data)
 +{
 +      return mark_complete(oid->hash);
 +}
 +
  static void mark_recent_complete_commits(struct fetch_pack_args *args,
                                         unsigned long cutoff)
  {
@@@ -530,7 -507,7 +530,7 @@@ static void filter_refs(struct fetch_pa
                int keep = 0;
                next = ref->next;
  
 -              if (!memcmp(ref->name, "refs/", 5) &&
 +              if (starts_with(ref->name, "refs/") &&
                    check_refname_format(ref->name, 0))
                        ; /* trash */
                else {
        }
  
        /* Append unmatched requests to the list */
 -      if (allow_tip_sha1_in_want) {
 +      if ((allow_unadvertised_object_request &
 +          (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1))) {
                for (i = 0; i < nr_sought; i++) {
 +                      unsigned char sha1[20];
 +
                        ref = sought[i];
                        if (ref->matched)
                                continue;
 -                      if (get_sha1_hex(ref->name, ref->old_sha1))
 +                      if (get_sha1_hex(ref->name, sha1) ||
 +                          ref->name[40] != '\0' ||
 +                          hashcmp(sha1, ref->old_sha1))
                                continue;
  
                        ref->matched = 1;
 -                      *newtail = ref;
 -                      ref->next = NULL;
 -                      newtail = &ref->next;
 +                      *newtail = copy_ref(ref);
 +                      newtail = &(*newtail)->next;
                }
        }
        *refs = newlist;
  
  static void mark_alternate_complete(const struct ref *ref, void *unused)
  {
 -      mark_complete(NULL, ref->old_sha1, 0, NULL);
 +      mark_complete(ref->old_sha1);
  }
  
  static int everything_local(struct fetch_pack_args *args,
        }
  
        if (!args->depth) {
 -              for_each_ref(mark_complete, NULL);
 +              for_each_ref(mark_complete_oid, NULL);
                for_each_alternate_ref(mark_alternate_complete, NULL);
                commit_list_sort_by_date(&complete);
                if (cutoff)
  
        for (retval = 1, ref = *refs; ref ; ref = ref->next) {
                const unsigned char *remote = ref->old_sha1;
 -              unsigned char local[20];
                struct object *o;
  
                o = lookup_object(remote);
                                ref->name);
                        continue;
                }
 -
 -              hashcpy(ref->new_sha1, local);
                if (!args->verbose)
                        continue;
                fprintf(stderr,
@@@ -686,7 -662,7 +686,7 @@@ static int get_pack(struct fetch_pack_a
        char hdr_arg[256];
        const char **av, *cmd_name;
        int do_keep = args->keep_pack;
 -      struct child_process cmd;
 +      struct child_process cmd = CHILD_PROCESS_INIT;
        int ret;
  
        memset(&demux, 0, sizeof(demux));
        else
                demux.out = xd[0];
  
 -      memset(&cmd, 0, sizeof(cmd));
        cmd.argv = argv;
        av = argv;
        *hdr_arg = 0;
@@@ -809,7 -786,7 +809,7 @@@ static struct ref *do_fetch_pack(struc
        sort_ref_list(&ref, ref_compare_name);
        qsort(sought, nr_sought, sizeof(*sought), cmp_ref_by_name);
  
-       if (is_repository_shallow() && !server_supports("shallow"))
+       if ((args->depth > 0 || is_repository_shallow()) && !server_supports("shallow"))
                die("Server does not support shallow clients");
        if (server_supports("multi_ack_detailed")) {
                if (args->verbose)
        if (server_supports("allow-tip-sha1-in-want")) {
                if (args->verbose)
                        fprintf(stderr, "Server supports allow-tip-sha1-in-want\n");
 -              allow_tip_sha1_in_want = 1;
 +              allow_unadvertised_object_request |= ALLOW_TIP_SHA1;
 +      }
 +      if (server_supports("allow-reachable-sha1-in-want")) {
 +              if (args->verbose)
 +                      fprintf(stderr, "Server supports allow-reachable-sha1-in-want\n");
 +              allow_unadvertised_object_request |= ALLOW_REACHABLE_SHA1;
        }
        if (!server_supports("thin-pack"))
                args->use_thin_pack = 0;
        return ref;
  }
  
 -static int fetch_pack_config(const char *var, const char *value, void *cb)
 +static void fetch_pack_config(void)
  {
 -      if (strcmp(var, "fetch.unpacklimit") == 0) {
 -              fetch_unpack_limit = git_config_int(var, value);
 -              return 0;
 -      }
 -
 -      if (strcmp(var, "transfer.unpacklimit") == 0) {
 -              transfer_unpack_limit = git_config_int(var, value);
 -              return 0;
 -      }
 -
 -      if (strcmp(var, "repack.usedeltabaseoffset") == 0) {
 -              prefer_ofs_delta = git_config_bool(var, value);
 -              return 0;
 -      }
 -
 -      if (!strcmp(var, "fetch.fsckobjects")) {
 -              fetch_fsck_objects = git_config_bool(var, value);
 -              return 0;
 -      }
 -
 -      if (!strcmp(var, "transfer.fsckobjects")) {
 -              transfer_fsck_objects = git_config_bool(var, value);
 -              return 0;
 -      }
 +      git_config_get_int("fetch.unpacklimit", &fetch_unpack_limit);
 +      git_config_get_int("transfer.unpacklimit", &transfer_unpack_limit);
 +      git_config_get_bool("repack.usedeltabaseoffset", &prefer_ofs_delta);
 +      git_config_get_bool("fetch.fsckobjects", &fetch_fsck_objects);
 +      git_config_get_bool("transfer.fsckobjects", &transfer_fsck_objects);
  
 -      return git_default_config(var, value, cb);
 +      git_config(git_default_config, NULL);
  }
  
  static void fetch_pack_setup(void)
        static int did_setup;
        if (did_setup)
                return;
 -      git_config(fetch_pack_config, NULL);
 +      fetch_pack_config();
        if (0 <= transfer_unpack_limit)
                unpack_limit = transfer_unpack_limit;
        else if (0 <= fetch_unpack_limit)