receive-pack.con commit Merge branch 'rs/cherry' (56532fa)
   1#include "cache.h"
   2#include "refs.h"
   3#include "pkt-line.h"
   4#include "run-command.h"
   5#include "commit.h"
   6#include "object.h"
   7
   8static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
   9
  10static const char *unpacker[] = { "unpack-objects", NULL };
  11
  12static int deny_non_fast_forwards = 0;
  13static int report_status;
  14
  15static char capabilities[] = "report-status";
  16static int capabilities_sent;
  17
  18static int receive_pack_config(const char *var, const char *value)
  19{
  20        git_default_config(var, value);
  21
  22        if (strcmp(var, "receive.denynonfastforwards") == 0)
  23        {
  24                deny_non_fast_forwards = git_config_bool(var, value);
  25                return 0;
  26        }
  27
  28        return 0;
  29}
  30
  31static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
  32{
  33        if (capabilities_sent)
  34                packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
  35        else
  36                packet_write(1, "%s %s%c%s\n",
  37                             sha1_to_hex(sha1), path, 0, capabilities);
  38        capabilities_sent = 1;
  39        return 0;
  40}
  41
  42static void write_head_info(void)
  43{
  44        for_each_ref(show_ref, NULL);
  45        if (!capabilities_sent)
  46                show_ref("capabilities^{}", null_sha1, 0, NULL);
  47
  48}
  49
  50struct command {
  51        struct command *next;
  52        const char *error_string;
  53        unsigned char old_sha1[20];
  54        unsigned char new_sha1[20];
  55        char ref_name[FLEX_ARRAY]; /* more */
  56};
  57
  58static struct command *commands;
  59
  60static char update_hook[] = "hooks/update";
  61
  62static int run_update_hook(const char *refname,
  63                           char *old_hex, char *new_hex)
  64{
  65        int code;
  66
  67        if (access(update_hook, X_OK) < 0)
  68                return 0;
  69        code = run_command(update_hook, refname, old_hex, new_hex, NULL);
  70        switch (code) {
  71        case 0:
  72                return 0;
  73        case -ERR_RUN_COMMAND_FORK:
  74                return error("hook fork failed");
  75        case -ERR_RUN_COMMAND_EXEC:
  76                return error("hook execute failed");
  77        case -ERR_RUN_COMMAND_WAITPID:
  78                return error("waitpid failed");
  79        case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
  80                return error("waitpid is confused");
  81        case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
  82                return error("%s died of signal", update_hook);
  83        case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
  84                return error("%s died strangely", update_hook);
  85        default:
  86                error("%s exited with error code %d", update_hook, -code);
  87                return -code;
  88        }
  89}
  90
  91static int update(struct command *cmd)
  92{
  93        const char *name = cmd->ref_name;
  94        unsigned char *old_sha1 = cmd->old_sha1;
  95        unsigned char *new_sha1 = cmd->new_sha1;
  96        char new_hex[41], old_hex[41];
  97        struct ref_lock *lock;
  98
  99        cmd->error_string = NULL;
 100        if (!strncmp(name, "refs/", 5) && check_ref_format(name + 5)) {
 101                cmd->error_string = "funny refname";
 102                return error("refusing to create funny ref '%s' locally",
 103                             name);
 104        }
 105
 106        strcpy(new_hex, sha1_to_hex(new_sha1));
 107        strcpy(old_hex, sha1_to_hex(old_sha1));
 108        if (!has_sha1_file(new_sha1)) {
 109                cmd->error_string = "bad pack";
 110                return error("unpack should have generated %s, "
 111                             "but I can't find it!", new_hex);
 112        }
 113        if (deny_non_fast_forwards && !is_null_sha1(old_sha1)) {
 114                struct commit *old_commit, *new_commit;
 115                struct commit_list *bases, *ent;
 116
 117                old_commit = (struct commit *)parse_object(old_sha1);
 118                new_commit = (struct commit *)parse_object(new_sha1);
 119                bases = get_merge_bases(old_commit, new_commit, 1);
 120                for (ent = bases; ent; ent = ent->next)
 121                        if (!hashcmp(old_sha1, ent->item->object.sha1))
 122                                break;
 123                free_commit_list(bases);
 124                if (!ent)
 125                        return error("denying non-fast forward;"
 126                                     " you should pull first");
 127        }
 128        if (run_update_hook(name, old_hex, new_hex)) {
 129                cmd->error_string = "hook declined";
 130                return error("hook declined to update %s", name);
 131        }
 132
 133        lock = lock_any_ref_for_update(name, old_sha1);
 134        if (!lock) {
 135                cmd->error_string = "failed to lock";
 136                return error("failed to lock %s", name);
 137        }
 138        write_ref_sha1(lock, new_sha1, "push");
 139
 140        fprintf(stderr, "%s: %s -> %s\n", name, old_hex, new_hex);
 141        return 0;
 142}
 143
 144static char update_post_hook[] = "hooks/post-update";
 145
 146static void run_update_post_hook(struct command *cmd)
 147{
 148        struct command *cmd_p;
 149        int argc;
 150        const char **argv;
 151
 152        if (access(update_post_hook, X_OK) < 0)
 153                return;
 154        for (argc = 1, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {
 155                if (cmd_p->error_string)
 156                        continue;
 157                argc++;
 158        }
 159        argv = xmalloc(sizeof(*argv) * (1 + argc));
 160        argv[0] = update_post_hook;
 161
 162        for (argc = 1, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {
 163                char *p;
 164                if (cmd_p->error_string)
 165                        continue;
 166                p = xmalloc(strlen(cmd_p->ref_name) + 1);
 167                strcpy(p, cmd_p->ref_name);
 168                argv[argc] = p;
 169                argc++;
 170        }
 171        argv[argc] = NULL;
 172        run_command_v_opt(argc, argv, RUN_COMMAND_NO_STDIO);
 173}
 174
 175/*
 176 * This gets called after(if) we've successfully
 177 * unpacked the data payload.
 178 */
 179static void execute_commands(void)
 180{
 181        struct command *cmd = commands;
 182
 183        while (cmd) {
 184                update(cmd);
 185                cmd = cmd->next;
 186        }
 187        run_update_post_hook(commands);
 188}
 189
 190static void read_head_info(void)
 191{
 192        struct command **p = &commands;
 193        for (;;) {
 194                static char line[1000];
 195                unsigned char old_sha1[20], new_sha1[20];
 196                struct command *cmd;
 197                char *refname;
 198                int len, reflen;
 199
 200                len = packet_read_line(0, line, sizeof(line));
 201                if (!len)
 202                        break;
 203                if (line[len-1] == '\n')
 204                        line[--len] = 0;
 205                if (len < 83 ||
 206                    line[40] != ' ' ||
 207                    line[81] != ' ' ||
 208                    get_sha1_hex(line, old_sha1) ||
 209                    get_sha1_hex(line + 41, new_sha1))
 210                        die("protocol error: expected old/new/ref, got '%s'",
 211                            line);
 212
 213                refname = line + 82;
 214                reflen = strlen(refname);
 215                if (reflen + 82 < len) {
 216                        if (strstr(refname + reflen + 1, "report-status"))
 217                                report_status = 1;
 218                }
 219                cmd = xmalloc(sizeof(struct command) + len - 80);
 220                hashcpy(cmd->old_sha1, old_sha1);
 221                hashcpy(cmd->new_sha1, new_sha1);
 222                memcpy(cmd->ref_name, line + 82, len - 81);
 223                cmd->error_string = "n/a (unpacker error)";
 224                cmd->next = NULL;
 225                *p = cmd;
 226                p = &cmd->next;
 227        }
 228}
 229
 230static const char *unpack(int *error_code)
 231{
 232        int code = run_command_v_opt(1, unpacker, RUN_GIT_CMD);
 233
 234        *error_code = 0;
 235        switch (code) {
 236        case 0:
 237                return NULL;
 238        case -ERR_RUN_COMMAND_FORK:
 239                return "unpack fork failed";
 240        case -ERR_RUN_COMMAND_EXEC:
 241                return "unpack execute failed";
 242        case -ERR_RUN_COMMAND_WAITPID:
 243                return "waitpid failed";
 244        case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
 245                return "waitpid is confused";
 246        case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
 247                return "unpacker died of signal";
 248        case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
 249                return "unpacker died strangely";
 250        default:
 251                *error_code = -code;
 252                return "unpacker exited with error code";
 253        }
 254}
 255
 256static void report(const char *unpack_status)
 257{
 258        struct command *cmd;
 259        packet_write(1, "unpack %s\n",
 260                     unpack_status ? unpack_status : "ok");
 261        for (cmd = commands; cmd; cmd = cmd->next) {
 262                if (!cmd->error_string)
 263                        packet_write(1, "ok %s\n",
 264                                     cmd->ref_name);
 265                else
 266                        packet_write(1, "ng %s %s\n",
 267                                     cmd->ref_name, cmd->error_string);
 268        }
 269        packet_flush(1);
 270}
 271
 272int main(int argc, char **argv)
 273{
 274        int i;
 275        char *dir = NULL;
 276
 277        argv++;
 278        for (i = 1; i < argc; i++) {
 279                char *arg = *argv++;
 280
 281                if (*arg == '-') {
 282                        /* Do flag handling here */
 283                        usage(receive_pack_usage);
 284                }
 285                if (dir)
 286                        usage(receive_pack_usage);
 287                dir = arg;
 288        }
 289        if (!dir)
 290                usage(receive_pack_usage);
 291
 292        if (!enter_repo(dir, 0))
 293                die("'%s': unable to chdir or not a git archive", dir);
 294
 295        setup_ident();
 296        git_config(receive_pack_config);
 297
 298        write_head_info();
 299
 300        /* EOF */
 301        packet_flush(1);
 302
 303        read_head_info();
 304        if (commands) {
 305                int code;
 306                const char *unpack_status = unpack(&code);
 307                if (!unpack_status)
 308                        execute_commands();
 309                if (report_status)
 310                        report(unpack_status);
 311        }
 312        return 0;
 313}