]>
Commit | Line | Data |
---|---|---|
66bf85a4 LT |
1 | #include "cache.h" |
2 | #include "refs.h" | |
3 | ||
4 | static const char git_update_ref_usage[] = "git-update-ref <refname> <value> [<oldval>]"; | |
5 | ||
6 | #define MAXDEPTH 5 | |
7 | ||
8 | const 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 | ||
53 | int 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 | } |