127ef99eeda03de4ef84a2cfbe473df8f896e3f4
   1#include "cache.h"
   2#include "refs.h"
   3
   4static const char git_update_ref_usage[] = "git-update-ref <refname> <value> [<oldval>]";
   5
   6#define MAXDEPTH 5
   7
   8const char *resolve_ref(const char *path, unsigned char *sha1)
   9{
  10        int depth = MAXDEPTH, len;
  11        char buffer[256];
  12
  13        for (;;) {
  14                struct stat st;
  15                int fd;
  16
  17                if (--depth < 0)
  18                        return NULL;
  19
  20                /* Special case: non-existing file */
  21                if (lstat(path, &st) < 0) {
  22                        if (errno != ENOENT)
  23                                return NULL;
  24                        memset(sha1, 0, 20);
  25                        return path;
  26                }
  27
  28                /* Follow "normalized" - ie "refs/.." symlinks by hand */
  29                if (S_ISLNK(st.st_mode)) {
  30                        len = readlink(path, buffer, sizeof(buffer)-1);
  31                        if (len >= 5 && !memcmp("refs/", buffer, 5)) {
  32                                path = git_path("%.*s", len, buffer);
  33                                continue;
  34                        }
  35                }
  36
  37                /*
  38                 * Anything else, just open it and try to use it as
  39                 * a ref
  40                 */
  41                fd = open(path, O_RDONLY);
  42                if (fd < 0)
  43                        return NULL;
  44                len = read(fd, buffer, sizeof(buffer)-1);
  45                close(fd);
  46                break;
  47        }
  48        if (len < 40 || get_sha1_hex(buffer, sha1))
  49                return NULL;
  50        return path;
  51}
  52
  53int main(int argc, char **argv)
  54{
  55        char *hex;
  56        const char *refname, *value, *oldval, *path, *lockpath;
  57        unsigned char sha1[20], oldsha1[20], currsha1[20];
  58        int fd, written;
  59
  60        setup_git_directory();
  61        if (argc < 3 || argc > 4)
  62                usage(git_update_ref_usage);
  63
  64        refname = argv[1];
  65        value = argv[2];
  66        oldval = argv[3];
  67        if (get_sha1(value, sha1) < 0)
  68                die("%s: not a valid SHA1", value);
  69        memset(oldsha1, 0, 20);
  70        if (oldval && get_sha1(oldval, oldsha1) < 0)
  71                die("%s: not a valid old SHA1", oldval);
  72
  73        path = resolve_ref(git_path("%s", refname), currsha1);
  74        if (!path)
  75                die("No such ref: %s", refname);
  76
  77        if (oldval) {
  78                if (memcmp(currsha1, oldsha1, 20))
  79                        die("Ref %s changed to %s", refname, sha1_to_hex(currsha1));
  80                /* Nothing to do? */
  81                if (!memcmp(oldsha1, sha1, 20))
  82                        exit(0);
  83        }
  84        path = strdup(path);
  85        lockpath = mkpath("%s.lock", path);
  86
  87        fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666);
  88        if (fd < 0)
  89                die("Unable to create %s", lockpath);
  90        hex = sha1_to_hex(sha1);
  91        hex[40] = '\n';
  92        written = write(fd, hex, 41);
  93        close(fd);
  94        if (written != 41) {
  95                unlink(lockpath);
  96                die("Unable to write to %s", lockpath);
  97        }
  98
  99        /*
 100         * FIXME!
 101         *
 102         * We should re-read the old ref here, and re-verify that it
 103         * matches "oldsha1". Otherwise there's a small race.
 104         */
 105
 106        if (rename(lockpath, path) < 0) {
 107                unlink(lockpath);
 108                die("Unable to create %s", path);
 109        }
 110        return 0;
 111}