Merge branch 'jc/fetch-verify'
authorJunio C Hamano <gitster@pobox.com>
Wed, 5 Oct 2011 19:36:20 +0000 (12:36 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 5 Oct 2011 19:36:20 +0000 (12:36 -0700)
* jc/fetch-verify:
fetch: verify we have everything we need before updating our ref
rev-list --verify-object
list-objects: pass callback data to show_objects()

1  2 
builtin/fetch.c
builtin/pack-objects.c
revision.c
revision.h
t/t5504-fetch-receive-strict.sh
upload-pack.c
diff --combined builtin/fetch.c
index e422ced9299521bb2e7f3c5ff59a434c8403a4ea,bdb03ff54c51426f4c814dc7326e62b0bc3576c5..ead8c9d5c37411da0fefd7a4f2272cb94652ffdb
@@@ -345,6 -345,64 +345,64 @@@ static int update_local_ref(struct ref 
        }
  }
  
+ /*
+  * The ref_map records the tips of the refs we are fetching. If
+  *
+  *  $ git rev-list --verify-objects --stdin --not --all
+  *
+  * (feeding all the refs in ref_map on its standard input) does not
+  * error out, that means everything reachable from these updated refs
+  * locally exists and is connected to some of our existing refs.
+  *
+  * Returns 0 if everything is connected, non-zero otherwise.
+  */
+ static int check_everything_connected(struct ref *ref_map, int quiet)
+ {
+       struct child_process rev_list;
+       const char *argv[] = {"rev-list", "--verify-objects",
+                             "--stdin", "--not", "--all", NULL, NULL};
+       char commit[41];
+       struct ref *ref;
+       int err = 0;
+       if (!ref_map)
+               return 0;
+       if (quiet)
+               argv[5] = "--quiet";
+       memset(&rev_list, 0, sizeof(rev_list));
+       rev_list.argv = argv;
+       rev_list.git_cmd = 1;
+       rev_list.in = -1;
+       rev_list.no_stdout = 1;
+       rev_list.no_stderr = quiet;
+       if (start_command(&rev_list))
+               return error(_("Could not run 'git rev-list'"));
+       sigchain_push(SIGPIPE, SIG_IGN);
+       memcpy(commit + 40, "\n", 2);
+       for (ref = ref_map; ref; ref = ref->next) {
+               memcpy(commit, sha1_to_hex(ref->old_sha1), 40);
+               if (write_in_full(rev_list.in, commit, 41) < 0) {
+                       if (errno != EPIPE && errno != EINVAL)
+                               error(_("failed write to rev-list: %s"),
+                                     strerror(errno));
+                       err = -1;
+                       break;
+               }
+       }
+       if (close(rev_list.in)) {
+               error(_("failed to close rev-list's stdin: %s"), strerror(errno));
+               err = -1;
+       }
+       sigchain_pop(SIGPIPE);
+       return finish_command(&rev_list) || err;
+ }
  static int store_updated_refs(const char *raw_url, const char *remote_name,
                struct ref *ref_map)
  {
                url = transport_anonymize_url(raw_url);
        else
                url = xstrdup("foreign");
+       if (check_everything_connected(ref_map, 0))
+               return error(_("%s did not send all necessary objects\n"), url);
        for (rm = ref_map; rm; rm = rm->next) {
                struct ref *ref = NULL;
  
   * We would want to bypass the object transfer altogether if
   * everything we are going to fetch already exists and is connected
   * locally.
-  *
-  * The refs we are going to fetch are in ref_map.  If running
-  *
-  *  $ git rev-list --objects --stdin --not --all
-  *
-  * (feeding all the refs in ref_map on its standard input)
-  * does not error out, that means everything reachable from the
-  * refs we are going to fetch exists and is connected to some of
-  * our existing refs.
   */
  static int quickfetch(struct ref *ref_map)
  {
-       struct child_process revlist;
-       struct ref *ref;
-       int err;
-       const char *argv[] = {"rev-list",
-               "--quiet", "--objects", "--stdin", "--not", "--all", NULL};
        /*
         * If we are deepening a shallow clone we already have these
         * objects reachable.  Running rev-list here will return with
         */
        if (depth)
                return -1;
-       if (!ref_map)
-               return 0;
-       memset(&revlist, 0, sizeof(revlist));
-       revlist.argv = argv;
-       revlist.git_cmd = 1;
-       revlist.no_stdout = 1;
-       revlist.no_stderr = 1;
-       revlist.in = -1;
-       err = start_command(&revlist);
-       if (err) {
-               error(_("could not run rev-list"));
-               return err;
-       }
-       /*
-        * If rev-list --stdin encounters an unknown commit, it terminates,
-        * which will cause SIGPIPE in the write loop below.
-        */
-       sigchain_push(SIGPIPE, SIG_IGN);
-       for (ref = ref_map; ref; ref = ref->next) {
-               if (write_in_full(revlist.in, sha1_to_hex(ref->old_sha1), 40) < 0 ||
-                   write_str_in_full(revlist.in, "\n") < 0) {
-                       if (errno != EPIPE && errno != EINVAL)
-                               error(_("failed write to rev-list: %s"), strerror(errno));
-                       err = -1;
-                       break;
-               }
-       }
-       if (close(revlist.in)) {
-               error(_("failed to close rev-list's stdin: %s"), strerror(errno));
-               err = -1;
-       }
-       sigchain_pop(SIGPIPE);
-       return finish_command(&revlist) || err;
+       return check_everything_connected(ref_map, 1);
  }
  
  static int fetch_refs(struct transport *transport, struct ref *ref_map)
@@@ -875,7 -882,6 +882,7 @@@ static int fetch_one(struct remote *rem
  {
        int i;
        static const char **refs = NULL;
 +      struct refspec *refspec;
        int ref_nr = 0;
        int exit_code;
  
  
        sigchain_push_common(unlock_pack_on_signal);
        atexit(unlock_pack);
 -      exit_code = do_fetch(transport,
 -                      parse_fetch_refspec(ref_nr, refs), ref_nr);
 +      refspec = parse_fetch_refspec(ref_nr, refs);
 +      exit_code = do_fetch(transport, refspec, ref_nr);
 +      free(refspec);
        transport_disconnect(transport);
        transport = NULL;
        return exit_code;
@@@ -941,15 -946,6 +948,15 @@@ int cmd_fetch(int argc, const char **ar
        argc = parse_options(argc, argv, prefix,
                             builtin_fetch_options, builtin_fetch_usage, 0);
  
 +      if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
 +              if (recurse_submodules_default) {
 +                      int arg = parse_fetch_recurse_submodules_arg("--recurse-submodules-default", recurse_submodules_default);
 +                      set_config_fetch_recurse_submodules(arg);
 +              }
 +              gitmodules_config();
 +              git_config(submodule_config, NULL);
 +      }
 +
        if (all) {
                if (argc == 1)
                        die(_("fetch --all does not take a repository argument"));
        if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) {
                const char *options[10];
                int num_options = 0;
 -              if (recurse_submodules_default) {
 -                      int arg = parse_fetch_recurse_submodules_arg("--recurse-submodules-default", recurse_submodules_default);
 -                      set_config_fetch_recurse_submodules(arg);
 -              }
 -              gitmodules_config();
 -              git_config(submodule_config, NULL);
                add_options_to_argv(&num_options, options);
                result = fetch_populated_submodules(num_options, options,
                                                    submodule_prefix,
diff --combined builtin/pack-objects.c
index a9c67c18ba159c8f04fa6bfff52ed9718965190a,fca6cb56358170ef0d2682c7882372c435248de0..2b18de5dc37bf849dbdbc892e4e9f3a34893dd9d
@@@ -51,8 -51,6 +51,8 @@@ struct object_entry 
                                       * objects against.
                                       */
        unsigned char no_try_delta;
 +      unsigned char tagged; /* near the very tip of refs */
 +      unsigned char filled; /* assigned write-order */
  };
  
  /*
@@@ -72,7 -70,6 +72,7 @@@ static int local
  static int incremental;
  static int ignore_packed_keep;
  static int allow_ofs_delta;
 +static struct pack_idx_option pack_idx_opts;
  static const char *base_name;
  static int progress = 1;
  static int window = 10;
@@@ -98,7 -95,6 +98,7 @@@ static unsigned long window_memory_limi
   */
  static int *object_ix;
  static int object_ix_hashsz;
 +static struct object_entry *locate_object_entry(const unsigned char *sha1);
  
  /*
   * stats
@@@ -130,13 -126,13 +130,13 @@@ static void *get_delta(struct object_en
  
  static unsigned long do_compress(void **pptr, unsigned long size)
  {
 -      z_stream stream;
 +      git_zstream stream;
        void *in, *out;
        unsigned long maxsize;
  
        memset(&stream, 0, sizeof(stream));
 -      deflateInit(&stream, pack_compression_level);
 -      maxsize = deflateBound(&stream, size);
 +      git_deflate_init(&stream, pack_compression_level);
 +      maxsize = git_deflate_bound(&stream, size);
  
        in = *pptr;
        out = xmalloc(maxsize);
        stream.avail_in = size;
        stream.next_out = out;
        stream.avail_out = maxsize;
 -      while (deflate(&stream, Z_FINISH) == Z_OK)
 +      while (git_deflate(&stream, Z_FINISH) == Z_OK)
                ; /* nothing */
 -      deflateEnd(&stream);
 +      git_deflate_end(&stream);
  
        free(in);
        return stream.total_out;
@@@ -164,7 -160,7 +164,7 @@@ static int check_pack_inflate(struct pa
                off_t len,
                unsigned long expect)
  {
 -      z_stream stream;
 +      git_zstream stream;
        unsigned char fakebuf[4096], *in;
        int st;
  
@@@ -191,19 -187,18 +191,19 @@@ static void copy_pack_data(struct sha1f
                off_t len)
  {
        unsigned char *in;
 -      unsigned int avail;
 +      unsigned long avail;
  
        while (len) {
                in = use_pack(p, w_curs, offset, &avail);
                if (avail > len)
 -                      avail = (unsigned int)len;
 +                      avail = (unsigned long)len;
                sha1write(f, in, avail);
                offset += avail;
                len -= avail;
        }
  }
  
 +/* Return 0 if we will bust the pack-size limit */
  static unsigned long write_object(struct sha1file *f,
                                  struct object_entry *entry,
                                  off_t write_offset)
@@@ -438,134 -433,6 +438,134 @@@ static int write_one(struct sha1file *f
        return 1;
  }
  
 +static int mark_tagged(const char *path, const unsigned char *sha1, int flag,
 +                     void *cb_data)
 +{
 +      unsigned char peeled[20];
 +      struct object_entry *entry = locate_object_entry(sha1);
 +
 +      if (entry)
 +              entry->tagged = 1;
 +      if (!peel_ref(path, peeled)) {
 +              entry = locate_object_entry(peeled);
 +              if (entry)
 +                      entry->tagged = 1;
 +      }
 +      return 0;
 +}
 +
 +static void add_to_write_order(struct object_entry **wo,
 +                             int *endp,
 +                             struct object_entry *e)
 +{
 +      if (e->filled)
 +              return;
 +      wo[(*endp)++] = e;
 +      e->filled = 1;
 +}
 +
 +static void add_descendants_to_write_order(struct object_entry **wo,
 +                                         int *endp,
 +                                         struct object_entry *e)
 +{
 +      struct object_entry *child;
 +
 +      for (child = e->delta_child; child; child = child->delta_sibling)
 +              add_to_write_order(wo, endp, child);
 +      for (child = e->delta_child; child; child = child->delta_sibling)
 +              add_descendants_to_write_order(wo, endp, child);
 +}
 +
 +static void add_family_to_write_order(struct object_entry **wo,
 +                                    int *endp,
 +                                    struct object_entry *e)
 +{
 +      struct object_entry *root;
 +
 +      for (root = e; root->delta; root = root->delta)
 +              ; /* nothing */
 +      add_to_write_order(wo, endp, root);
 +      add_descendants_to_write_order(wo, endp, root);
 +}
 +
 +static struct object_entry **compute_write_order(void)
 +{
 +      int i, wo_end;
 +
 +      struct object_entry **wo = xmalloc(nr_objects * sizeof(*wo));
 +
 +      for (i = 0; i < nr_objects; i++) {
 +              objects[i].tagged = 0;
 +              objects[i].filled = 0;
 +              objects[i].delta_child = NULL;
 +              objects[i].delta_sibling = NULL;
 +      }
 +
 +      /*
 +       * Fully connect delta_child/delta_sibling network.
 +       * Make sure delta_sibling is sorted in the original
 +       * recency order.
 +       */
 +      for (i = nr_objects - 1; 0 <= i; i--) {
 +              struct object_entry *e = &objects[i];
 +              if (!e->delta)
 +                      continue;
 +              /* Mark me as the first child */
 +              e->delta_sibling = e->delta->delta_child;
 +              e->delta->delta_child = e;
 +      }
 +
 +      /*
 +       * Mark objects that are at the tip of tags.
 +       */
 +      for_each_tag_ref(mark_tagged, NULL);
 +
 +      /*
 +       * Give the commits in the original recency order until
 +       * we see a tagged tip.
 +       */
 +      for (i = wo_end = 0; i < nr_objects; i++) {
 +              if (objects[i].tagged)
 +                      break;
 +              add_to_write_order(wo, &wo_end, &objects[i]);
 +      }
 +
 +      /*
 +       * Then fill all the tagged tips.
 +       */
 +      for (; i < nr_objects; i++) {
 +              if (objects[i].tagged)
 +                      add_to_write_order(wo, &wo_end, &objects[i]);
 +      }
 +
 +      /*
 +       * And then all remaining commits and tags.
 +       */
 +      for (i = 0; i < nr_objects; i++) {
 +              if (objects[i].type != OBJ_COMMIT &&
 +                  objects[i].type != OBJ_TAG)
 +                      continue;
 +              add_to_write_order(wo, &wo_end, &objects[i]);
 +      }
 +
 +      /*
 +       * And then all the trees.
 +       */
 +      for (i = 0; i < nr_objects; i++) {
 +              if (objects[i].type != OBJ_TREE)
 +                      continue;
 +              add_to_write_order(wo, &wo_end, &objects[i]);
 +      }
 +
 +      /*
 +       * Finally all the rest in really tight order
 +       */
 +      for (i = 0; i < nr_objects; i++)
 +              add_family_to_write_order(wo, &wo_end, &objects[i]);
 +
 +      return wo;
 +}
 +
  static void write_pack_file(void)
  {
        uint32_t i = 0, j;
        struct pack_header hdr;
        uint32_t nr_remaining = nr_result;
        time_t last_mtime = 0;
 +      struct object_entry **write_order;
  
        if (progress > pack_to_stdout)
                progress_state = start_progress("Writing objects", nr_result);
        written_list = xmalloc(nr_objects * sizeof(*written_list));
 +      write_order = compute_write_order();
  
        do {
                unsigned char sha1[20];
                offset = sizeof(hdr);
                nr_written = 0;
                for (; i < nr_objects; i++) {
 -                      if (!write_one(f, objects + i, &offset))
 +                      struct object_entry *e = write_order[i];
 +                      if (!write_one(f, e, &offset))
                                break;
                        display_progress(progress_state, written);
                }
                        const char *idx_tmp_name;
                        char tmpname[PATH_MAX];
  
 -                      idx_tmp_name = write_idx_file(NULL, written_list,
 -                                                    nr_written, sha1);
 +                      idx_tmp_name = write_idx_file(NULL, written_list, nr_written,
 +                                                    &pack_idx_opts, sha1);
  
                        snprintf(tmpname, sizeof(tmpname), "%s-%s.pack",
                                 base_name, sha1_to_hex(sha1));
        } while (nr_remaining && i < nr_objects);
  
        free(written_list);
 +      free(write_order);
        stop_progress(&progress_state);
        if (written != nr_result)
                die("wrote %"PRIu32" objects while expecting %"PRIu32,
@@@ -770,7 -633,7 +770,7 @@@ static int no_try_delta(const char *pat
        struct git_attr_check check[1];
  
        setup_delta_attr_check(check);
 -      if (git_checkattr(path, ARRAY_SIZE(check), check))
 +      if (git_check_attr(path, ARRAY_SIZE(check), check))
                return 0;
        if (ATTR_FALSE(check->value))
                return 1;
@@@ -1131,7 -994,7 +1131,7 @@@ static void check_object(struct object_
                const unsigned char *base_ref = NULL;
                struct object_entry *base_entry;
                unsigned long used, used_0;
 -              unsigned int avail;
 +              unsigned long avail;
                off_t ofs;
                unsigned char *buf, c;
  
@@@ -2021,10 -1884,10 +2021,10 @@@ static int git_pack_config(const char *
                return 0;
        }
        if (!strcmp(k, "pack.indexversion")) {
 -              pack_idx_default_version = git_config_int(k, v);
 -              if (pack_idx_default_version > 2)
 +              pack_idx_opts.version = git_config_int(k, v);
 +              if (pack_idx_opts.version > 2)
                        die("bad pack.indexversion=%"PRIu32,
 -                              pack_idx_default_version);
 +                          pack_idx_opts.version);
                return 0;
        }
        if (!strcmp(k, "pack.packsizelimit")) {
@@@ -2073,7 -1936,9 +2073,9 @@@ static void show_commit(struct commit *
        commit->object.flags |= OBJECT_ADDED;
  }
  
- static void show_object(struct object *obj, const struct name_path *path, const char *last)
+ static void show_object(struct object *obj,
+                       const struct name_path *path, const char *last,
+                       void *data)
  {
        char *name = path_name(path, last);
  
@@@ -2271,7 -2136,6 +2273,7 @@@ int cmd_pack_objects(int argc, const ch
        rp_av[1] = "--objects"; /* --thin will make it --objects-edge */
        rp_ac = 2;
  
 +      reset_pack_idx_option(&pack_idx_opts);
        git_config(git_pack_config, NULL);
        if (!pack_compression_seen && core_compression_seen)
                pack_compression_level = core_compression_level;
                }
                if (!prefixcmp(arg, "--index-version=")) {
                        char *c;
 -                      pack_idx_default_version = strtoul(arg + 16, &c, 10);
 -                      if (pack_idx_default_version > 2)
 +                      pack_idx_opts.version = strtoul(arg + 16, &c, 10);
 +                      if (pack_idx_opts.version > 2)
                                die("bad %s", arg);
                        if (*c == ',')
 -                              pack_idx_off32_limit = strtoul(c+1, &c, 0);
 -                      if (*c || pack_idx_off32_limit & 0x80000000)
 +                              pack_idx_opts.off32_limit = strtoul(c+1, &c, 0);
 +                      if (*c || pack_idx_opts.off32_limit & 0x80000000)
                                die("bad %s", arg);
                        continue;
                }
diff --combined revision.c
index 19a493bf609bf3ecbe9602de0f2ce54af2ae828a,5ef498b304aeb1bf4a80750331923164c64f4371..9bae329c153f33b5aa39f2dab2748dc56b2c3824
@@@ -770,16 -770,12 +770,16 @@@ static void limit_to_ancestry(struct co
   * to filter the result of "A..B" further to the ones that can actually
   * reach A.
   */
 -static struct commit_list *collect_bottom_commits(struct commit_list *list)
 +static struct commit_list *collect_bottom_commits(struct rev_info *revs)
  {
 -      struct commit_list *elem, *bottom = NULL;
 -      for (elem = list; elem; elem = elem->next)
 -              if (elem->item->object.flags & UNINTERESTING)
 -                      commit_list_insert(elem->item, &bottom);
 +      struct commit_list *bottom = NULL;
 +      int i;
 +      for (i = 0; i < revs->cmdline.nr; i++) {
 +              struct rev_cmdline_entry *elem = &revs->cmdline.rev[i];
 +              if ((elem->flags & UNINTERESTING) &&
 +                  elem->item->type == OBJ_COMMIT)
 +                      commit_list_insert((struct commit *)elem->item, &bottom);
 +      }
        return bottom;
  }
  
@@@ -810,7 -806,7 +810,7 @@@ static int limit_list(struct rev_info *
        struct commit_list *bottom = NULL;
  
        if (revs->ancestry_path) {
 -              bottom = collect_bottom_commits(list);
 +              bottom = collect_bottom_commits(revs);
                if (!bottom)
                        die("--ancestry-path given but there are no bottom commits");
        }
        return 0;
  }
  
 +static void add_rev_cmdline(struct rev_info *revs,
 +                          struct object *item,
 +                          const char *name,
 +                          int whence,
 +                          unsigned flags)
 +{
 +      struct rev_cmdline_info *info = &revs->cmdline;
 +      int nr = info->nr;
 +
 +      ALLOC_GROW(info->rev, nr + 1, info->alloc);
 +      info->rev[nr].item = item;
 +      info->rev[nr].name = name;
 +      info->rev[nr].whence = whence;
 +      info->rev[nr].flags = flags;
 +      info->nr++;
 +}
 +
  struct all_refs_cb {
        int all_flags;
        int warned_bad_reflog;
@@@ -896,7 -875,6 +896,7 @@@ static int handle_one_ref(const char *p
        struct all_refs_cb *cb = cb_data;
        struct object *object = get_reference(cb->all_revs, path, sha1,
                                              cb->all_flags);
 +      add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags);
        add_pending_object(cb->all_revs, object, path);
        return 0;
  }
@@@ -923,7 -901,6 +923,7 @@@ static void handle_one_reflog_commit(un
                struct object *o = parse_object(sha1);
                if (o) {
                        o->flags |= cb->all_flags;
 +                      /* ??? CMDLINEFLAGS ??? */
                        add_pending_object(cb->all_revs, o, "");
                }
                else if (!cb->warned_bad_reflog) {
@@@ -960,13 -937,12 +960,13 @@@ static void handle_reflog(struct rev_in
        for_each_reflog(handle_one_reflog, &cb);
  }
  
 -static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
 +static int add_parents_only(struct rev_info *revs, const char *arg_, int flags)
  {
        unsigned char sha1[20];
        struct object *it;
        struct commit *commit;
        struct commit_list *parents;
 +      const char *arg = arg_;
  
        if (*arg == '^') {
                flags ^= UNINTERESTING;
        for (parents = commit->parents; parents; parents = parents->next) {
                it = &parents->item->object;
                it->flags |= flags;
 +              add_rev_cmdline(revs, it, arg_, REV_CMD_PARENTS_ONLY, flags);
                add_pending_object(revs, it, arg);
        }
        return 1;
@@@ -1084,7 -1059,7 +1084,7 @@@ static void prepare_show_merge(struct r
        revs->limited = 1;
  }
  
 -int handle_revision_arg(const char *arg, struct rev_info *revs,
 +int handle_revision_arg(const char *arg_, struct rev_info *revs,
                        int flags,
                        int cant_be_filename)
  {
        struct object *object;
        unsigned char sha1[20];
        int local_flags;
 +      const char *arg = arg_;
  
        dotdot = strstr(arg, "..");
        if (dotdot) {
                const char *this = arg;
                int symmetric = *next == '.';
                unsigned int flags_exclude = flags ^ UNINTERESTING;
 +              unsigned int a_flags;
  
                *dotdot = 0;
                next += symmetric;
                                add_pending_commit_list(revs, exclude,
                                                        flags_exclude);
                                free_commit_list(exclude);
 -                              a->object.flags |= flags | SYMMETRIC_LEFT;
 +                              a_flags = flags | SYMMETRIC_LEFT;
                        } else
 -                              a->object.flags |= flags_exclude;
 +                              a_flags = flags_exclude;
 +                      a->object.flags |= a_flags;
                        b->object.flags |= flags;
 +                      add_rev_cmdline(revs, &a->object, this,
 +                                      REV_CMD_LEFT, a_flags);
 +                      add_rev_cmdline(revs, &b->object, next,
 +                                      REV_CMD_RIGHT, flags);
                        add_pending_object(revs, &a->object, this);
                        add_pending_object(revs, &b->object, next);
                        return 0;
        if (!cant_be_filename)
                verify_non_filename(revs->prefix, arg);
        object = get_reference(revs, arg, sha1, flags ^ local_flags);
 +      add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
        add_pending_object_with_mode(revs, object, arg, mode);
        return 0;
  }
@@@ -1416,6 -1383,11 +1416,11 @@@ static int handle_revision_opt(struct r
                revs->tree_objects = 1;
                revs->blob_objects = 1;
                revs->edge_hint = 1;
+       } else if (!strcmp(arg, "--verify-objects")) {
+               revs->tag_objects = 1;
+               revs->tree_objects = 1;
+               revs->blob_objects = 1;
+               revs->verify_objects = 1;
        } else if (!strcmp(arg, "--unpacked")) {
                revs->unpacked = 1;
        } else if (!prefixcmp(arg, "--unpacked=")) {
diff --combined revision.h
index 8eeb542dfbbfa70eb17cce7d0e2075f43d51c161,648876b35d8cfc273fecf39fc52feb7affeb6d5d..754f31b1cda81c474f71ab56f8d82e260adee968
@@@ -24,23 -24,6 +24,23 @@@ struct rev_info
  struct log_info;
  struct string_list;
  
 +struct rev_cmdline_info {
 +      unsigned int nr;
 +      unsigned int alloc;
 +      struct rev_cmdline_entry {
 +              struct object *item;
 +              const char *name;
 +              enum {
 +                      REV_CMD_REF,
 +                      REV_CMD_PARENTS_ONLY,
 +                      REV_CMD_LEFT,
 +                      REV_CMD_RIGHT,
 +                      REV_CMD_REV
 +              } whence;
 +              unsigned flags;
 +      } *rev;
 +};
 +
  struct rev_info {
        /* Starting list */
        struct commit_list *commits;
@@@ -49,9 -32,6 +49,9 @@@
        /* Parents of shown commits */
        struct object_array boundary_commits;
  
 +      /* The end-points specified by the end user */
 +      struct rev_cmdline_info cmdline;
 +
        /* Basic information */
        const char *prefix;
        const char *def;
@@@ -73,6 -53,7 +73,7 @@@
                        tag_objects:1,
                        tree_objects:1,
                        blob_objects:1,
+                       verify_objects:1,
                        edge_hint:1,
                        limited:1,
                        unpacked:1,
index 66100124a94779940d4057f958ebc400168ad1c9,0000000000000000000000000000000000000000..8341fc4d154f6d50bf9055b206810dea4e1b807b
mode 100755,000000..100755
--- /dev/null
@@@ -1,104 -1,0 +1,104 @@@
-               git fetch ../.git master
 +#!/bin/sh
 +
 +test_description='fetch/receive strict mode'
 +. ./test-lib.sh
 +
 +test_expect_success setup '
 +      echo hello >greetings &&
 +      git add greetings &&
 +      git commit -m greetings &&
 +
 +      S=$(git rev-parse :greetings | sed -e "s|^..|&/|") &&
 +      X=$(echo bye | git hash-object -w --stdin | sed -e "s|^..|&/|") &&
 +      mv -f .git/objects/$X .git/objects/$S &&
 +
 +      test_must_fail git fsck
 +'
 +
 +test_expect_success 'fetch without strict' '
 +      rm -rf dst &&
 +      git init dst &&
 +      (
 +              cd dst &&
 +              git config fetch.fsckobjects false &&
 +              git config transfer.fsckobjects false &&
-               git fetch ../.git master
++              test_must_fail git fetch ../.git master
 +      )
 +'
 +
 +test_expect_success 'fetch with !fetch.fsckobjects' '
 +      rm -rf dst &&
 +      git init dst &&
 +      (
 +              cd dst &&
 +              git config fetch.fsckobjects false &&
 +              git config transfer.fsckobjects true &&
++              test_must_fail git fetch ../.git master
 +      )
 +'
 +
 +test_expect_success 'fetch with fetch.fsckobjects' '
 +      rm -rf dst &&
 +      git init dst &&
 +      (
 +              cd dst &&
 +              git config fetch.fsckobjects true &&
 +              git config transfer.fsckobjects false &&
 +              test_must_fail git fetch ../.git master
 +      )
 +'
 +
 +test_expect_success 'fetch with transfer.fsckobjects' '
 +      rm -rf dst &&
 +      git init dst &&
 +      (
 +              cd dst &&
 +              git config transfer.fsckobjects true &&
 +              test_must_fail git fetch ../.git master
 +      )
 +'
 +
 +test_expect_success 'push without strict' '
 +      rm -rf dst &&
 +      git init dst &&
 +      (
 +              cd dst &&
 +              git config fetch.fsckobjects false &&
 +              git config transfer.fsckobjects false
 +      ) &&
 +      git push dst master:refs/heads/test
 +'
 +
 +test_expect_success 'push with !receive.fsckobjects' '
 +      rm -rf dst &&
 +      git init dst &&
 +      (
 +              cd dst &&
 +              git config receive.fsckobjects false &&
 +              git config transfer.fsckobjects true
 +      ) &&
 +      git push dst master:refs/heads/test
 +'
 +
 +test_expect_success 'push with receive.fsckobjects' '
 +      rm -rf dst &&
 +      git init dst &&
 +      (
 +              cd dst &&
 +              git config receive.fsckobjects true &&
 +              git config transfer.fsckobjects false
 +      ) &&
 +      test_must_fail git push dst master:refs/heads/test
 +'
 +
 +test_expect_success 'push with transfer.fsckobjects' '
 +      rm -rf dst &&
 +      git init dst &&
 +      (
 +              cd dst &&
 +              git config transfer.fsckobjects true
 +      ) &&
 +      test_must_fail git push dst master:refs/heads/test
 +'
 +
 +test_done
diff --combined upload-pack.c
index 31686712c77ba8229870a224fafcc8008c3af4ff,6be625995df74df0865853a1bc4dbc1c1bbf4505..470cffd7c14a9f28010423a44327084219598a35
@@@ -10,7 -10,6 +10,7 @@@
  #include "revision.h"
  #include "list-objects.h"
  #include "run-command.h"
 +#include "sigchain.h"
  
  static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=<n>] <dir>";
  
@@@ -84,7 -83,9 +84,9 @@@ static void show_commit(struct commit *
        commit->buffer = NULL;
  }
  
- static void show_object(struct object *obj, const struct name_path *path, const char *component)
+ static void show_object(struct object *obj,
+                       const struct name_path *path, const char *component,
+                       void *cb_data)
  {
        show_object_with_name(pack_pipe, obj, path, component);
  }
@@@ -486,97 -487,11 +488,97 @@@ static int get_common_commits(void
        }
  }
  
 +static void check_non_tip(void)
 +{
 +      static const char *argv[] = {
 +              "rev-list", "--stdin", NULL,
 +      };
 +      static struct child_process cmd;
 +      struct object *o;
 +      char namebuf[42]; /* ^ + SHA-1 + LF */
 +      int i;
 +
 +      /* In the normal in-process case non-tip request can never happen */
 +      if (!stateless_rpc)
 +              goto error;
 +
 +      cmd.argv = argv;
 +      cmd.git_cmd = 1;
 +      cmd.no_stderr = 1;
 +      cmd.in = -1;
 +      cmd.out = -1;
 +
 +      if (start_command(&cmd))
 +              goto error;
 +
 +      /*
 +       * If rev-list --stdin encounters an unknown commit, it
 +       * terminates, which will cause SIGPIPE in the write loop
 +       * below.
 +       */
 +      sigchain_push(SIGPIPE, SIG_IGN);
 +
 +      namebuf[0] = '^';
 +      namebuf[41] = '\n';
 +      for (i = get_max_object_index(); 0 < i; ) {
 +              o = get_indexed_object(--i);
 +              if (!o)
 +                      continue;
 +              if (!(o->flags & OUR_REF))
 +                      continue;
 +              memcpy(namebuf + 1, sha1_to_hex(o->sha1), 40);
 +              if (write_in_full(cmd.in, namebuf, 42) < 0)
 +                      goto error;
 +      }
 +      namebuf[40] = '\n';
 +      for (i = 0; i < want_obj.nr; i++) {
 +              o = want_obj.objects[i].item;
 +              if (o->flags & OUR_REF)
 +                      continue;
 +              memcpy(namebuf, sha1_to_hex(o->sha1), 40);
 +              if (write_in_full(cmd.in, namebuf, 41) < 0)
 +                      goto error;
 +      }
 +      close(cmd.in);
 +
 +      sigchain_pop(SIGPIPE);
 +
 +      /*
 +       * The commits out of the rev-list are not ancestors of
 +       * our ref.
 +       */
 +      i = read_in_full(cmd.out, namebuf, 1);
 +      if (i)
 +              goto error;
 +      close(cmd.out);
 +
 +      /*
 +       * rev-list may have died by encountering a bad commit
 +       * in the history, in which case we do want to bail out
 +       * even when it showed no commit.
 +       */
 +      if (finish_command(&cmd))
 +              goto error;
 +
 +      /* All the non-tip ones are ancestors of what we advertised */
 +      return;
 +
 +error:
 +      /* Pick one of them (we know there at least is one) */
 +      for (i = 0; i < want_obj.nr; i++) {
 +              o = want_obj.objects[i].item;
 +              if (!(o->flags & OUR_REF))
 +                      die("git upload-pack: not our ref %s",
 +                          sha1_to_hex(o->sha1));
 +      }
 +}
 +
  static void receive_needs(void)
  {
        struct object_array shallows = OBJECT_ARRAY_INIT;
        static char line[1000];
        int len, depth = 0;
 +      int has_non_tip = 0;
  
        shallow_nr = 0;
        if (debug_fd)
                if (strstr(line+45, "include-tag"))
                        use_include_tag = 1;
  
 -              /* We have sent all our refs already, and the other end
 -               * should have chosen out of them; otherwise they are
 -               * asking for nonsense.
 -               *
 -               * Hmph.  We may later want to allow "want" line that
 -               * asks for something like "master~10" (symbolic)...
 -               * would it make sense?  I don't know.
 -               */
                o = lookup_object(sha1_buf);
 -              if (!o || !(o->flags & OUR_REF))
 +              if (!o)
                        die("git upload-pack: not our ref %s",
                            sha1_to_hex(sha1_buf));
                if (!(o->flags & WANTED)) {
                        o->flags |= WANTED;
 +                      if (!(o->flags & OUR_REF))
 +                              has_non_tip = 1;
                        add_object_array(o, NULL, &want_obj);
                }
        }
        if (debug_fd)
                write_str_in_full(debug_fd, "#E\n");
  
 +      /*
 +       * We have sent all our refs already, and the other end
 +       * should have chosen out of them. When we are operating
 +       * in the stateless RPC mode, however, their choice may
 +       * have been based on the set of older refs advertised
 +       * by another process that handled the initial request.
 +       */
 +      if (has_non_tip)
 +              check_non_tip();
 +
        if (!use_sideband && daemon_mode)
                no_progress = 1;
  
@@@ -719,17 -630,16 +721,17 @@@ static int send_ref(const char *refname
                " side-band-64k ofs-delta shallow no-progress"
                " include-tag multi_ack_detailed";
        struct object *o = parse_object(sha1);
 +      const char *refname_nons = strip_namespace(refname);
  
        if (!o)
                die("git upload-pack: cannot find object %s:", sha1_to_hex(sha1));
  
        if (capabilities)
 -              packet_write(1, "%s %s%c%s%s\n", sha1_to_hex(sha1), refname,
 +              packet_write(1, "%s %s%c%s%s\n", sha1_to_hex(sha1), refname_nons,
                             0, capabilities,
                             stateless_rpc ? " no-done" : "");
        else
 -              packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname);
 +              packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname_nons);
        capabilities = NULL;
        if (!(o->flags & OUR_REF)) {
                o->flags |= OUR_REF;
        if (o->type == OBJ_TAG) {
                o = deref_tag(o, refname, 0);
                if (o)
 -                      packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname);
 +                      packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname_nons);
        }
        return 0;
  }
@@@ -759,12 -669,12 +761,12 @@@ static void upload_pack(void
  {
        if (advertise_refs || !stateless_rpc) {
                reset_timeout();
 -              head_ref(send_ref, NULL);
 -              for_each_ref(send_ref, NULL);
 +              head_ref_namespaced(send_ref, NULL);
 +              for_each_namespaced_ref(send_ref, NULL);
                packet_flush(1);
        } else {
 -              head_ref(mark_our_ref, NULL);
 -              for_each_ref(mark_our_ref, NULL);
 +              head_ref_namespaced(mark_our_ref, NULL);
 +              for_each_namespaced_ref(mark_our_ref, NULL);
        }
        if (advertise_refs)
                return;