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