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