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