Merge branch 'jc/1.7.0-push-safety'
authorJunio C Hamano <gitster@pobox.com>
Sat, 26 Dec 2009 22:03:17 +0000 (14:03 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sat, 26 Dec 2009 22:03:17 +0000 (14:03 -0800)
* jc/1.7.0-push-safety:
Refuse deleting the current branch via push
Refuse updating the current branch in a non-bare repository via push

1  2 
builtin-receive-pack.c
t/t5516-fetch-push.sh
diff --combined builtin-receive-pack.c
index 78c0e69cdc9fe2d21e83947932cb353c0553452e,db12b813acbe9bade2b854b3a94fed13f6210499..4320c93e700a08911e42e3e949656af67b675244
@@@ -28,8 -28,6 +28,8 @@@ static int transfer_unpack_limit = -1
  static int unpack_limit = 100;
  static int report_status;
  static int prefer_ofs_delta = 1;
 +static int auto_update_server_info;
 +static int auto_gc = 1;
  static const char *head_name;
  static char *capabilities_to_send;
  
@@@ -90,16 -88,6 +90,16 @@@ static int receive_pack_config(const ch
                return 0;
        }
  
 +      if (strcmp(var, "receive.updateserverinfo") == 0) {
 +              auto_update_server_info = git_config_bool(var, value);
 +              return 0;
 +      }
 +
 +      if (strcmp(var, "receive.autogc") == 0) {
 +              auto_gc = git_config_bool(var, value);
 +              return 0;
 +      }
 +
        return git_default_config(var, value, cb);
  }
  
@@@ -135,6 -123,31 +135,6 @@@ static struct command *commands
  static const char pre_receive_hook[] = "hooks/pre-receive";
  static const char post_receive_hook[] = "hooks/post-receive";
  
 -static int run_status(int code, const char *cmd_name)
 -{
 -      switch (code) {
 -      case 0:
 -              return 0;
 -      case -ERR_RUN_COMMAND_FORK:
 -              return error("fork of %s failed", cmd_name);
 -      case -ERR_RUN_COMMAND_EXEC:
 -              return error("execute of %s failed", cmd_name);
 -      case -ERR_RUN_COMMAND_PIPE:
 -              return error("pipe failed");
 -      case -ERR_RUN_COMMAND_WAITPID:
 -              return error("waitpid failed");
 -      case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
 -              return error("waitpid is confused");
 -      case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
 -              return error("%s died of signal", cmd_name);
 -      case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
 -              return error("%s died strangely", cmd_name);
 -      default:
 -              error("%s exited with error code %d", cmd_name, -code);
 -              return -code;
 -      }
 -}
 -
  static int run_receive_hook(const char *hook_name)
  {
        static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];
  
        code = start_command(&proc);
        if (code)
 -              return run_status(code, hook_name);
 +              return code;
        for (cmd = commands; cmd; cmd = cmd->next) {
                if (!cmd->error_string) {
                        size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n",
                }
        }
        close(proc.in);
 -      return run_status(finish_command(&proc), hook_name);
 +      return finish_command(&proc);
  }
  
  static int run_update_hook(struct command *cmd)
        argv[3] = sha1_to_hex(cmd->new_sha1);
        argv[4] = NULL;
  
 -      return run_status(run_command_v_opt(argv, RUN_COMMAND_NO_STDIN |
 -                                      RUN_COMMAND_STDOUT_TO_STDERR),
 -                      update_hook);
 +      return run_command_v_opt(argv, RUN_COMMAND_NO_STDIN |
 +                                      RUN_COMMAND_STDOUT_TO_STDERR);
  }
  
  static int is_ref_checked_out(const char *ref)
        return !strcmp(head_name, ref);
  }
  
- static char *warn_unconfigured_deny_msg[] = {
-       "Updating the currently checked out branch may cause confusion,",
-       "as the index and work tree do not reflect changes that are in HEAD.",
-       "As a result, you may see the changes you just pushed into it",
-       "reverted when you run 'git diff' over there, and you may want",
-       "to run 'git reset --hard' before starting to work to recover.",
+ 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",
-       "'refuse' in the remote repository to forbid pushing into its",
-       "current branch."
+       "'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 allow pushing into the current branch, you can set it to 'ignore';",
-       "but 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, you can set it to 'warn'.",
-       "",
-       "Note that the default will change in a future version of git",
-       "to refuse updating the current branch unless you have the",
-       "configuration variable set to either 'ignore' or 'warn'."
+       "To squelch this message and still keep the default behaviour, set",
+       "'receive.denyCurrentBranch' configuration variable to 'refuse'."
  };
  
- static void warn_unconfigured_deny(void)
+ static void refuse_unconfigured_deny(void)
  {
        int i;
-       for (i = 0; i < ARRAY_SIZE(warn_unconfigured_deny_msg); i++)
-               warning("%s", warn_unconfigured_deny_msg[i]);
+       for (i = 0; i < ARRAY_SIZE(refuse_unconfigured_deny_msg); i++)
+               error("%s", refuse_unconfigured_deny_msg[i]);
  }
  
- static char *warn_unconfigured_deny_delete_current_msg[] = {
-       "Deleting the current branch can cause confusion by making the next",
-       "'git clone' not check out any file.",
+ 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",
-       "'refuse' in the remote repository to disallow deleting the current",
-       "branch.",
-       "",
-       "You can set it to 'ignore' to allow such a delete without a warning.",
+       "'warn' or 'ignore' in the remote repository to allow deleting the",
+       "current branch, with or without a warning message.",
        "",
-       "To make this warning message less loud, you can set it to 'warn'.",
-       "",
-       "Note that the default will change in a future version of git",
-       "to refuse deleting the current branch unless you have the",
-       "configuration variable set to either 'ignore' or 'warn'."
+       "To squelch this message, you can set it to 'refuse'."
  };
  
- static void warn_unconfigured_deny_delete_current(void)
+ static void refuse_unconfigured_deny_delete_current(void)
  {
        int i;
        for (i = 0;
-            i < ARRAY_SIZE(warn_unconfigured_deny_delete_current_msg);
+            i < ARRAY_SIZE(refuse_unconfigured_deny_delete_current_msg);
             i++)
-               warning("%s", warn_unconfigured_deny_delete_current_msg[i]);
+               error("%s", refuse_unconfigured_deny_delete_current_msg[i]);
  }
  
  static const char *update(struct command *cmd)
                switch (deny_current_branch) {
                case DENY_IGNORE:
                        break;
-               case DENY_UNCONFIGURED:
                case DENY_WARN:
                        warning("updating the current branch");
-                       if (deny_current_branch == DENY_UNCONFIGURED)
-                               warn_unconfigured_deny();
                        break;
                case DENY_REFUSE:
+               case DENY_UNCONFIGURED:
                        error("refusing to update checked out branch: %s", name);
+                       if (deny_current_branch == DENY_UNCONFIGURED)
+                               refuse_unconfigured_deny();
                        return "branch is currently checked out";
                }
        }
                        case DENY_IGNORE:
                                break;
                        case DENY_WARN:
-                       case DENY_UNCONFIGURED:
-                               if (deny_delete_current == DENY_UNCONFIGURED)
-                                       warn_unconfigured_deny_delete_current();
                                warning("deleting the current branch");
                                break;
                        case DENY_REFUSE:
+                       case DENY_UNCONFIGURED:
+                               if (deny_delete_current == DENY_UNCONFIGURED)
+                                       refuse_unconfigured_deny_delete_current();
                                error("refusing to delete the current branch: %s", name);
                                return "deletion of the current branch prohibited";
                        }
                                break;
                free_commit_list(bases);
                if (!ent) {
 -                      error("denying non-fast forward %s"
 +                      error("denying non-fast-forward %s"
                              " (you should pull first)", name);
 -                      return "non-fast forward";
 +                      return "non-fast-forward";
                }
        }
        if (run_update_hook(cmd)) {
@@@ -405,6 -407,7 +393,6 @@@ static void run_update_post_hook(struc
        argv[argc] = NULL;
        status = run_command_v_opt(argv, RUN_COMMAND_NO_STDIN
                        | RUN_COMMAND_STDOUT_TO_STDERR);
 -      run_status(status, update_post_hook);
  }
  
  static void execute_commands(const char *unpacker_error)
@@@ -522,6 -525,7 +510,6 @@@ static const char *unpack(void
                code = run_command_v_opt(unpacker, RUN_GIT_CMD);
                if (!code)
                        return NULL;
 -              run_status(code, unpacker[0]);
                return "unpack-objects abnormal exit";
        } else {
                const char *keeper[7];
                ip.git_cmd = 1;
                status = start_command(&ip);
                if (status) {
 -                      run_status(status, keeper[0]);
                        return "index-pack fork failed";
                }
                pack_lockfile = index_pack_lockfile(ip.out);
                        reprepare_packed_git();
                        return NULL;
                }
 -              run_status(status, keeper[0]);
                return "index-pack abnormal exit";
        }
  }
@@@ -627,8 -633,6 +615,8 @@@ static void add_alternate_refs(void
  
  int cmd_receive_pack(int argc, const char **argv, const char *prefix)
  {
 +      int advertise_refs = 0;
 +      int stateless_rpc = 0;
        int i;
        char *dir = NULL;
  
                const char *arg = *argv++;
  
                if (*arg == '-') {
 -                      /* Do flag handling here */
 +                      if (!strcmp(arg, "--advertise-refs")) {
 +                              advertise_refs = 1;
 +                              continue;
 +                      }
 +                      if (!strcmp(arg, "--stateless-rpc")) {
 +                              stateless_rpc = 1;
 +                              continue;
 +                      }
 +
                        usage(receive_pack_usage);
                }
                if (dir)
                " report-status delete-refs ofs-delta " :
                " report-status delete-refs ";
  
 -      add_alternate_refs();
 -      write_head_info();
 -      clear_extra_refs();
 +      if (advertise_refs || !stateless_rpc) {
 +              add_alternate_refs();
 +              write_head_info();
 +              clear_extra_refs();
  
 -      /* EOF */
 -      packet_flush(1);
 +              /* EOF */
 +              packet_flush(1);
 +      }
 +      if (advertise_refs)
 +              return 0;
  
        read_head_info();
        if (commands) {
                        report(unpack_status);
                run_receive_hook(post_receive_hook);
                run_update_post_hook(commands);
 +              if (auto_gc) {
 +                      const char *argv_gc_auto[] = {
 +                              "gc", "--auto", "--quiet", NULL,
 +                      };
 +                      run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
 +              }
 +              if (auto_update_server_info)
 +                      update_server_info(0);
        }
        return 0;
  }
diff --combined t/t5516-fetch-push.sh
index 6889a53cf9bdea0aff88789f954ddf31d1eec010,6529d97dc007a438f5507cb427a17106113a168f..516127b5390bafbd725564e91fb24ead224cf3a7
@@@ -12,6 -12,7 +12,7 @@@ mk_empty () 
        (
                cd testrepo &&
                git init &&
+               git config receive.denyCurrentBranch warn &&
                mv .git/hooks .git/hooks-disabled
        )
  }
@@@ -122,23 -123,6 +123,23 @@@ test_expect_success 'fetch with instead
        )
  '
  
 +test_expect_success 'fetch with pushInsteadOf (should not rewrite)' '
 +      mk_empty &&
 +      (
 +              TRASH=$(pwd)/ &&
 +              cd testrepo &&
 +              git config "url.trash/.pushInsteadOf" "$TRASH" &&
 +              git config remote.up.url "$TRASH." &&
 +              git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
 +              git fetch up &&
 +
 +              r=$(git show-ref -s --verify refs/remotes/origin/master) &&
 +              test "z$r" = "z$the_commit" &&
 +
 +              test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
 +      )
 +'
 +
  test_expect_success 'push without wildcard' '
        mk_empty &&
  
@@@ -179,36 -163,6 +180,36 @@@ test_expect_success 'push with insteadO
        )
  '
  
 +test_expect_success 'push with pushInsteadOf' '
 +      mk_empty &&
 +      TRASH="$(pwd)/" &&
 +      git config "url.$TRASH.pushInsteadOf" trash/ &&
 +      git push trash/testrepo refs/heads/master:refs/remotes/origin/master &&
 +      (
 +              cd testrepo &&
 +              r=$(git show-ref -s --verify refs/remotes/origin/master) &&
 +              test "z$r" = "z$the_commit" &&
 +
 +              test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
 +      )
 +'
 +
 +test_expect_success 'push with pushInsteadOf and explicit pushurl (pushInsteadOf should not rewrite)' '
 +      mk_empty &&
 +      TRASH="$(pwd)/" &&
 +      git config "url.trash2/.pushInsteadOf" trash/ &&
 +      git config remote.r.url trash/wrong &&
 +      git config remote.r.pushurl "$TRASH/testrepo" &&
 +      git push r refs/heads/master:refs/remotes/origin/master &&
 +      (
 +              cd testrepo &&
 +              r=$(git show-ref -s --verify refs/remotes/origin/master) &&
 +              test "z$r" = "z$the_commit" &&
 +
 +              test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
 +      )
 +'
 +
  test_expect_success 'push with matching heads' '
  
        mk_test heads/master &&