receive-pack.con commit Merge with gitk. (89ab859)
   1#include "cache.h"
   2#include "refs.h"
   3#include "pkt-line.h"
   4#include <sys/wait.h>
   5
   6static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
   7
   8static const char *unpacker = "git-unpack-objects";
   9
  10static int show_ref(const char *path, const unsigned char *sha1)
  11{
  12        packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
  13        return 0;
  14}
  15
  16static void write_head_info(void)
  17{
  18        for_each_ref(show_ref);
  19}
  20
  21struct command {
  22        struct command *next;
  23        unsigned char old_sha1[20];
  24        unsigned char new_sha1[20];
  25        char ref_name[0];
  26};
  27
  28static struct command *commands = NULL;
  29
  30static int is_all_zeroes(const char *hex)
  31{
  32        int i;
  33        for (i = 0; i < 40; i++)
  34                if (*hex++ != '0')
  35                        return 0;
  36        return 1;
  37}
  38
  39static int verify_old_ref(const char *name, char *hex_contents)
  40{
  41        int fd, ret;
  42        char buffer[60];
  43
  44        if (is_all_zeroes(hex_contents))
  45                return 0;
  46        fd = open(name, O_RDONLY);
  47        if (fd < 0)
  48                return -1;
  49        ret = read(fd, buffer, 40);
  50        close(fd);
  51        if (ret != 40)
  52                return -1;
  53        if (memcmp(buffer, hex_contents, 40))
  54                return -1;
  55        return 0;
  56}
  57
  58static void update(const char *name, unsigned char *old_sha1, unsigned char *new_sha1)
  59{
  60        char new_hex[60], *old_hex, *lock_name;
  61        int newfd, namelen, written;
  62
  63        namelen = strlen(name);
  64        lock_name = xmalloc(namelen + 10);
  65        memcpy(lock_name, name, namelen);
  66        memcpy(lock_name + namelen, ".lock", 6);
  67
  68        strcpy(new_hex, sha1_to_hex(new_sha1));
  69        old_hex = sha1_to_hex(old_sha1);
  70        if (!has_sha1_file(new_sha1))
  71                die("unpack should have generated %s, but I can't find it!", new_hex);
  72
  73        newfd = open(lock_name, O_CREAT | O_EXCL | O_WRONLY, 0666);
  74        if (newfd < 0)
  75                die("unable to create %s (%s)", lock_name, strerror(errno));
  76
  77        /* Write the ref with an ending '\n' */
  78        new_hex[40] = '\n';
  79        new_hex[41] = 0;
  80        written = write(newfd, new_hex, 41);
  81        /* Remove the '\n' again */
  82        new_hex[40] = 0;
  83
  84        close(newfd);
  85        if (written != 41) {
  86                unlink(lock_name);
  87                die("unable to write %s", lock_name);
  88        }
  89        if (verify_old_ref(name, old_hex) < 0) {
  90                unlink(lock_name);
  91                die("%s changed during push", name);
  92        }
  93        if (rename(lock_name, name) < 0) {
  94                unlink(lock_name);
  95                die("unable to replace %s", name);
  96        }
  97        fprintf(stderr, "%s: %s -> %s\n", name, old_hex, new_hex);
  98}
  99
 100
 101/*
 102 * This gets called after(if) we've successfully
 103 * unpacked the data payload.
 104 */
 105static void execute_commands(void)
 106{
 107        struct command *cmd = commands;
 108
 109        while (cmd) {
 110                update(cmd->ref_name, cmd->old_sha1, cmd->new_sha1);
 111                cmd = cmd->next;
 112        }
 113        update_server_info(0);
 114}
 115
 116static void read_head_info(void)
 117{
 118        struct command **p = &commands;
 119        for (;;) {
 120                static char line[1000];
 121                unsigned char old_sha1[20], new_sha1[20];
 122                struct command *cmd;
 123                int len;
 124
 125                len = packet_read_line(0, line, sizeof(line));
 126                if (!len)
 127                        break;
 128                if (line[len-1] == '\n')
 129                        line[--len] = 0;
 130                if (len < 83 ||
 131                    line[40] != ' ' ||
 132                    line[81] != ' ' ||
 133                    get_sha1_hex(line, old_sha1) ||
 134                    get_sha1_hex(line + 41, new_sha1))
 135                        die("protocol error: expected old/new/ref, got '%s'", line);
 136                cmd = xmalloc(sizeof(struct command) + len - 80);
 137                memcpy(cmd->old_sha1, old_sha1, 20);
 138                memcpy(cmd->new_sha1, new_sha1, 20);
 139                memcpy(cmd->ref_name, line + 82, len - 81);
 140                cmd->next = NULL;
 141                *p = cmd;
 142                p = &cmd->next;
 143        }
 144}
 145
 146static void unpack(void)
 147{
 148        pid_t pid = fork();
 149
 150        if (pid < 0)
 151                die("unpack fork failed");
 152        if (!pid) {
 153                execlp(unpacker, unpacker, NULL);
 154                die("unpack execute failed");
 155        }
 156
 157        for (;;) {
 158                int status, code;
 159                int retval = waitpid(pid, &status, 0);
 160
 161                if (retval < 0) {
 162                        if (errno == EINTR)
 163                                continue;
 164                        die("waitpid failed (%s)", strerror(retval));
 165                }
 166                if (retval != pid)
 167                        die("waitpid is confused");
 168                if (WIFSIGNALED(status))
 169                        die("%s died of signal %d", unpacker, WTERMSIG(status));
 170                if (!WIFEXITED(status))
 171                        die("%s died out of really strange complications", unpacker);
 172                code = WEXITSTATUS(status);
 173                if (code)
 174                        die("%s exited with error code %d", unpacker, code);
 175                return;
 176        }
 177}
 178
 179int main(int argc, char **argv)
 180{
 181        int i;
 182        const char *dir = NULL;
 183
 184        argv++;
 185        for (i = 1; i < argc; i++) {
 186                const char *arg = *argv++;
 187
 188                if (*arg == '-') {
 189                        /* Do flag handling here */
 190                        usage(receive_pack_usage);
 191                }
 192                if (dir)
 193                        usage(receive_pack_usage);
 194                dir = arg;
 195        }
 196        if (!dir)
 197                usage(receive_pack_usage);
 198
 199        /* chdir to the directory. If that fails, try appending ".git" */
 200        if (chdir(dir) < 0) {
 201                if (chdir(mkpath("%s.git", dir)) < 0)
 202                        die("unable to cd to %s", dir);
 203        }
 204
 205        /* If we have a ".git" directory, chdir to it */
 206        chdir(".git");
 207        setenv("GIT_DIR", ".", 1);
 208
 209        if (access("objects", X_OK) < 0 || access("refs/heads", X_OK) < 0)
 210                die("%s doesn't appear to be a git directory", dir);
 211        write_head_info();
 212
 213        /* EOF */
 214        packet_flush(1);
 215
 216        read_head_info();
 217        if (commands) {
 218                unpack();
 219                execute_commands();
 220        }
 221        return 0;
 222}