Merge branch 'rs/update-hook-optim'
authorJunio C Hamano <gitster@pobox.com>
Fri, 24 Mar 2017 20:07:37 +0000 (13:07 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 24 Mar 2017 20:07:37 +0000 (13:07 -0700)
Code clean-up.

* rs/update-hook-optim:
receive-pack: simplify run_update_post_hook()

1  2 
builtin/receive-pack.c
diff --combined builtin/receive-pack.c
index 83492af05f2bfd8ca1ec796b7ae8dccc84f0f31b,a5279e9df8739efa3665a158fe0667c86a26d209..fb2a090a0ce464cf6f663fa367c4859bd3a5f619
@@@ -20,8 -20,6 +20,8 @@@
  #include "gpg-interface.h"
  #include "sigchain.h"
  #include "fsck.h"
 +#include "tmp-objdir.h"
 +#include "oidset.h"
  
  static const char * const receive_pack_usage[] = {
        N_("git receive-pack <git-dir>"),
@@@ -48,7 -46,6 +48,7 @@@ static int transfer_unpack_limit = -1
  static int advertise_atomic_push = 1;
  static int advertise_push_options;
  static int unpack_limit = 100;
 +static off_t max_input_size;
  static int report_status;
  static int use_sideband;
  static int use_atomic;
@@@ -88,8 -85,6 +88,8 @@@ static enum 
  } use_keepalive;
  static int keepalive_in_sec = 5;
  
 +static struct tmp_objdir *tmp_objdir;
 +
  static enum deny_action parse_deny_action(const char *var, const char *value)
  {
        if (value) {
@@@ -217,18 -212,13 +217,18 @@@ static int receive_pack_config(const ch
                return 0;
        }
  
 +      if (strcmp(var, "receive.maxinputsize") == 0) {
 +              max_input_size = git_config_int64(var, value);
 +              return 0;
 +      }
 +
        return git_default_config(var, value, cb);
  }
  
  static void show_ref(const char *path, const unsigned char *sha1)
  {
        if (sent_capabilities) {
 -              packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
 +              packet_write_fmt(1, "%s %s\n", sha1_to_hex(sha1), path);
        } else {
                struct strbuf cap = STRBUF_INIT;
  
                if (advertise_push_options)
                        strbuf_addstr(&cap, " push-options");
                strbuf_addf(&cap, " agent=%s", git_user_agent_sanitized());
 -              packet_write(1, "%s %s%c%s\n",
 +              packet_write_fmt(1, "%s %s%c%s\n",
                             sha1_to_hex(sha1), path, 0, cap.buf);
                strbuf_release(&cap);
                sent_capabilities = 1;
  }
  
  static int show_ref_cb(const char *path_full, const struct object_id *oid,
 -                     int flag, void *unused)
 +                     int flag, void *data)
  {
 +      struct oidset *seen = data;
        const char *path = strip_namespace(path_full);
  
        if (ref_is_hidden(path, path_full))
        /*
         * Advertise refs outside our current namespace as ".have"
         * refs, so that the client can use them to minimize data
 -       * transfer but will otherwise ignore them. This happens to
 -       * cover ".have" that are thrown in by add_one_alternate_ref()
 -       * to mark histories that are complete in our alternates as
 -       * well.
 +       * transfer but will otherwise ignore them.
         */
 -      if (!path)
 +      if (!path) {
 +              if (oidset_insert(seen, oid))
 +                      return 0;
                path = ".have";
 +      } else {
 +              oidset_insert(seen, oid);
 +      }
        show_ref(path, oid->hash);
        return 0;
  }
  
 -static void show_one_alternate_sha1(const unsigned char sha1[20], void *unused)
 +static void show_one_alternate_ref(const char *refname,
 +                                 const struct object_id *oid,
 +                                 void *data)
  {
 -      show_ref(".have", sha1);
 -}
 +      struct oidset *seen = data;
  
 -static void collect_one_alternate_ref(const struct ref *ref, void *data)
 -{
 -      struct sha1_array *sa = data;
 -      sha1_array_append(sa, ref->old_oid.hash);
 +      if (oidset_insert(seen, oid))
 +              return;
 +
 +      show_ref(".have", oid->hash);
  }
  
  static void write_head_info(void)
  {
 -      struct sha1_array sa = SHA1_ARRAY_INIT;
 +      static struct oidset seen = OIDSET_INIT;
  
 -      for_each_alternate_ref(collect_one_alternate_ref, &sa);
 -      sha1_array_for_each_unique(&sa, show_one_alternate_sha1, NULL);
 -      sha1_array_clear(&sa);
 -      for_each_ref(show_ref_cb, NULL);
 +      for_each_ref(show_ref_cb, &seen);
 +      for_each_alternate_ref(show_one_alternate_ref, &seen);
 +      oidset_clear(&seen);
        if (!sent_capabilities)
                show_ref("capabilities^{}", null_sha1);
  
@@@ -670,9 -657,6 +670,9 @@@ static int run_and_feed_hook(const cha
        } else
                argv_array_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT");
  
 +      if (tmp_objdir)
 +              argv_array_pushv(&proc.env_array, tmp_objdir_env(tmp_objdir));
 +
        if (use_sideband) {
                memset(&muxer, 0, sizeof(muxer));
                muxer.proc = copy_to_sideband;
@@@ -772,7 -756,6 +772,7 @@@ static int run_update_hook(struct comma
        proc.stdout_to_stderr = 1;
        proc.err = use_sideband ? -1 : 0;
        proc.argv = argv;
 +      proc.env = tmp_objdir_env(tmp_objdir);
  
        code = start_command(&proc);
        if (code)
@@@ -792,39 -775,47 +792,39 @@@ static int is_ref_checked_out(const cha
        return !strcmp(head_name, ref);
  }
  
 -static char *refuse_unconfigured_deny_msg[] = {
 -      "By default, updating the current branch in a non-bare repository",
 -      "is denied, because it will make the index and work tree inconsistent",
 -      "with what you pushed, and will require 'git reset --hard' to match",
 -      "the work tree to HEAD.",
 -      "",
 -      "You can set 'receive.denyCurrentBranch' configuration variable to",
 -      "'ignore' or 'warn' in the remote repository to allow pushing into",
 -      "its current branch; however, this is not recommended unless you",
 -      "arranged to update its work tree to match what you pushed in some",
 -      "other way.",
 -      "",
 -      "To squelch this message and still keep the default behaviour, set",
 -      "'receive.denyCurrentBranch' configuration variable to 'refuse'."
 -};
 +static char *refuse_unconfigured_deny_msg =
 +      N_("By default, updating the current branch in a non-bare repository\n"
 +         "is denied, because it will make the index and work tree inconsistent\n"
 +         "with what you pushed, and will require 'git reset --hard' to match\n"
 +         "the work tree to HEAD.\n"
 +         "\n"
 +         "You can set the 'receive.denyCurrentBranch' configuration variable\n"
 +         "to 'ignore' or 'warn' in the remote repository to allow pushing into\n"
 +         "its current branch; however, this is not recommended unless you\n"
 +         "arranged to update its work tree to match what you pushed in some\n"
 +         "other way.\n"
 +         "\n"
 +         "To squelch this message and still keep the default behaviour, set\n"
 +         "'receive.denyCurrentBranch' configuration variable to 'refuse'.");
  
  static void refuse_unconfigured_deny(void)
  {
 -      int i;
 -      for (i = 0; i < ARRAY_SIZE(refuse_unconfigured_deny_msg); i++)
 -              rp_error("%s", refuse_unconfigured_deny_msg[i]);
 +      rp_error("%s", _(refuse_unconfigured_deny_msg));
  }
  
 -static char *refuse_unconfigured_deny_delete_current_msg[] = {
 -      "By default, deleting the current branch is denied, because the next",
 -      "'git clone' won't result in any file checked out, causing confusion.",
 -      "",
 -      "You can set 'receive.denyDeleteCurrent' configuration variable to",
 -      "'warn' or 'ignore' in the remote repository to allow deleting the",
 -      "current branch, with or without a warning message.",
 -      "",
 -      "To squelch this message, you can set it to 'refuse'."
 -};
 +static char *refuse_unconfigured_deny_delete_current_msg =
 +      N_("By default, deleting the current branch is denied, because the next\n"
 +         "'git clone' won't result in any file checked out, causing confusion.\n"
 +         "\n"
 +         "You can set 'receive.denyDeleteCurrent' configuration variable to\n"
 +         "'warn' or 'ignore' in the remote repository to allow deleting the\n"
 +         "current branch, with or without a warning message.\n"
 +         "\n"
 +         "To squelch this message, you can set it to 'refuse'.");
  
  static void refuse_unconfigured_deny_delete_current(void)
  {
 -      int i;
 -      for (i = 0;
 -           i < ARRAY_SIZE(refuse_unconfigured_deny_delete_current_msg);
 -           i++)
 -              rp_error("%s", refuse_unconfigured_deny_delete_current_msg[i]);
 +      rp_error("%s", _(refuse_unconfigured_deny_delete_current_msg));
  }
  
  static int command_singleton_iterator(void *cb_data, unsigned char sha1[20]);
@@@ -844,7 -835,6 +844,7 @@@ static int update_shallow_ref(struct co
                    !delayed_reachability_test(si, i))
                        sha1_array_append(&extra, si->shallow->sha1[i]);
  
 +      opt.env = tmp_objdir_env(tmp_objdir);
        setup_alternate_shallow(&shallow_lock, &opt.shallow_file, &extra);
        if (check_connected(command_singleton_iterator, cmd, &opt)) {
                rollback_lock_file(&shallow_lock);
@@@ -1128,25 -1118,22 +1128,22 @@@ static const char *update(struct comman
  static void run_update_post_hook(struct command *commands)
  {
        struct command *cmd;
-       int argc;
        struct child_process proc = CHILD_PROCESS_INIT;
        const char *hook;
  
        hook = find_hook("post-update");
-       for (argc = 0, cmd = commands; cmd; cmd = cmd->next) {
-               if (cmd->error_string || cmd->did_not_exist)
-                       continue;
-               argc++;
-       }
-       if (!argc || !hook)
+       if (!hook)
                return;
  
-       argv_array_push(&proc.args, hook);
        for (cmd = commands; cmd; cmd = cmd->next) {
                if (cmd->error_string || cmd->did_not_exist)
                        continue;
+               if (!proc.args.argc)
+                       argv_array_push(&proc.args, hook);
                argv_array_push(&proc.args, cmd->ref_name);
        }
+       if (!proc.args.argc)
+               return;
  
        proc.no_stdin = 1;
        proc.stdout_to_stderr = 1;
@@@ -1166,6 -1153,10 +1163,6 @@@ static void check_aliased_update(struc
        struct string_list_item *item;
        struct command *dst_cmd;
        unsigned char sha1[GIT_SHA1_RAWSZ];
 -      char cmd_oldh[GIT_SHA1_HEXSZ + 1],
 -           cmd_newh[GIT_SHA1_HEXSZ + 1],
 -           dst_oldh[GIT_SHA1_HEXSZ + 1],
 -           dst_newh[GIT_SHA1_HEXSZ + 1];
        int flag;
  
        strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
  
        dst_cmd->skip_update = 1;
  
 -      find_unique_abbrev_r(cmd_oldh, cmd->old_sha1, DEFAULT_ABBREV);
 -      find_unique_abbrev_r(cmd_newh, cmd->new_sha1, DEFAULT_ABBREV);
 -      find_unique_abbrev_r(dst_oldh, dst_cmd->old_sha1, DEFAULT_ABBREV);
 -      find_unique_abbrev_r(dst_newh, dst_cmd->new_sha1, DEFAULT_ABBREV);
        rp_error("refusing inconsistent update between symref '%s' (%s..%s) and"
                 " its target '%s' (%s..%s)",
 -               cmd->ref_name, cmd_oldh, cmd_newh,
 -               dst_cmd->ref_name, dst_oldh, dst_newh);
 +               cmd->ref_name,
 +               find_unique_abbrev(cmd->old_sha1, DEFAULT_ABBREV),
 +               find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV),
 +               dst_cmd->ref_name,
 +               find_unique_abbrev(dst_cmd->old_sha1, DEFAULT_ABBREV),
 +               find_unique_abbrev(dst_cmd->new_sha1, DEFAULT_ABBREV));
  
        cmd->error_string = dst_cmd->error_string =
                "inconsistent aliased update";
@@@ -1248,17 -1239,12 +1245,17 @@@ static void set_connectivity_errors(str
  
        for (cmd = commands; cmd; cmd = cmd->next) {
                struct command *singleton = cmd;
 +              struct check_connected_options opt = CHECK_CONNECTED_INIT;
 +
                if (shallow_update && si->shallow_ref[cmd->index])
                        /* to be checked in update_shallow_ref() */
                        continue;
 +
 +              opt.env = tmp_objdir_env(tmp_objdir);
                if (!check_connected(command_singleton_iterator, &singleton,
 -                                   NULL))
 +                                   &opt))
                        continue;
 +
                cmd->error_string = "missing necessary objects";
        }
  }
@@@ -1417,7 -1403,7 +1414,7 @@@ static void execute_commands(struct com
  {
        struct check_connected_options opt = CHECK_CONNECTED_INIT;
        struct command *cmd;
 -      unsigned char sha1[20];
 +      struct object_id oid;
        struct iterate_data data;
        struct async muxer;
        int err_fd = 0;
        data.si = si;
        opt.err_fd = err_fd;
        opt.progress = err_fd && !quiet;
 +      opt.env = tmp_objdir_env(tmp_objdir);
        if (check_connected(iterate_receive_command_list, &data, &opt))
                set_connectivity_errors(commands, si);
  
                return;
        }
  
 +      /*
 +       * Now we'll start writing out refs, which means the objects need
 +       * to be in their final positions so that other processes can see them.
 +       */
 +      if (tmp_objdir_migrate(tmp_objdir) < 0) {
 +              for (cmd = commands; cmd; cmd = cmd->next) {
 +                      if (!cmd->error_string)
 +                              cmd->error_string = "unable to migrate objects to permanent storage";
 +              }
 +              return;
 +      }
 +      tmp_objdir = NULL;
 +
        check_aliased_updates(commands);
  
        free(head_name_to_free);
 -      head_name = head_name_to_free = resolve_refdup("HEAD", 0, sha1, NULL);
 +      head_name = head_name_to_free = resolve_refdup("HEAD", 0, oid.hash, NULL);
  
        if (use_atomic)
                execute_commands_atomic(commands, si);
@@@ -1666,21 -1638,6 +1663,21 @@@ static const char *unpack(int err_fd, s
                argv_array_push(&child.args, alt_shallow_file);
        }
  
 +      tmp_objdir = tmp_objdir_create();
 +      if (!tmp_objdir) {
 +              if (err_fd > 0)
 +                      close(err_fd);
 +              return "unable to create temporary object directory";
 +      }
 +      child.env = tmp_objdir_env(tmp_objdir);
 +
 +      /*
 +       * Normally we just pass the tmp_objdir environment to the child
 +       * processes that do the heavy lifting, but we may need to see these
 +       * objects ourselves to set up shallow information.
 +       */
 +      tmp_objdir_add_as_alternate(tmp_objdir);
 +
        if (ntohl(hdr.hdr_entries) < unpack_limit) {
                argv_array_pushl(&child.args, "unpack-objects", hdr_arg, NULL);
                if (quiet)
                if (fsck_objects)
                        argv_array_pushf(&child.args, "--strict%s",
                                fsck_msg_types.buf);
 +              if (max_input_size)
 +                      argv_array_pushf(&child.args, "--max-input-size=%"PRIuMAX,
 +                              (uintmax_t)max_input_size);
                child.no_stdout = 1;
                child.err = err_fd;
                child.git_cmd = 1;
                                fsck_msg_types.buf);
                if (!reject_thin)
                        argv_array_push(&child.args, "--fix-thin");
 +              if (max_input_size)
 +                      argv_array_pushf(&child.args, "--max-input-size=%"PRIuMAX,
 +                              (uintmax_t)max_input_size);
                child.out = -1;
                child.err = err_fd;
                child.git_cmd = 1;
@@@ -1948,7 -1899,8 +1945,7 @@@ int cmd_receive_pack(int argc, const ch
                run_receive_hook(commands, "post-receive", 1,
                                 &push_options);
                run_update_post_hook(commands);
 -              if (push_options.nr)
 -                      string_list_clear(&push_options, 0);
 +              string_list_clear(&push_options, 0);
                if (auto_gc) {
                        const char *argv_gc_auto[] = {
                                "gc", "--auto", "--quiet", NULL,