git-receive-pack: start parsing ref update commands
[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 (nr && !path_match(path, nr, match))
81                                 continue;
82                         show_ref(path, sha1);
83                 }
84                 free(path);
85                 closedir(dir);
86         }
87 }
88
89 struct command {
90         struct command *next;
91         unsigned char old_sha1[20];
92         unsigned char new_sha1[20];
93         char ref_name[0];
94 };
95
96 struct command *commands = NULL;
97
98 /*
99  * This gets called after(if) we've successfully
100  * unpacked the data payload.
101  */
102 static void execute_commands(void)
103 {
104         struct command *cmd = commands;
105
106         while (cmd) {
107                 char old_hex[60], *new_hex;
108                 strcpy(old_hex, sha1_to_hex(cmd->old_sha1));
109                 new_hex = sha1_to_hex(cmd->new_sha1);
110                 fprintf(stderr, "%s: %s -> %s\n", cmd->ref_name, old_hex, new_hex);
111                 cmd = cmd->next;
112         }
113 }
114
115 static void read_head_info(void)
116 {
117         struct command **p = &commands;
118         for (;;) {
119                 static char line[1000];
120                 unsigned char old_sha1[20], new_sha1[20];
121                 struct command *cmd;
122                 int len;
123
124                 len = packet_read_line(0, line, sizeof(line));
125                 if (!len)
126                         break;
127                 if (line[len-1] == '\n')
128                         line[--len] = 0;
129                 if (len < 83 ||
130                     line[40] != ' ' ||
131                     line[81] != ' ' ||
132                     get_sha1_hex(line, old_sha1) ||
133                     get_sha1_hex(line + 41, new_sha1))
134                         die("protocol error: expected old/new/ref, got '%s'", line);
135                 cmd = xmalloc(sizeof(struct command) + len - 80);
136                 memcpy(cmd->old_sha1, old_sha1, 20);
137                 memcpy(cmd->new_sha1, new_sha1, 20);
138                 memcpy(cmd->ref_name, line + 82, len - 81);
139                 cmd->next = NULL;
140                 *p = cmd;
141                 p = &cmd->next;
142         }
143 }
144
145 static void unpack(void)
146 {
147         pid_t pid = fork();
148
149         if (pid < 0)
150                 die("unpack fork failed");
151         if (!pid) {
152                 setenv("GIT_DIR", ".", 1);
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
179 int main(int argc, char **argv)
180 {
181         int i, nr_heads = 0;
182         const char *dir = NULL;
183         char **heads = NULL;
184
185         argv++;
186         for (i = 1; i < argc; i++) {
187                 const char *arg = *argv++;
188
189                 if (*arg == '-') {
190                         if (!strncmp(arg, "--unpack=", 9)) {
191                                 unpacker = arg+9;
192                                 continue;
193                         }
194                         /* Do flag handling here */
195                         usage(receive_pack_usage);
196                 }
197                 dir = arg;
198                 heads = argv;
199                 nr_heads = argc - i - 1;
200                 break;
201         }
202         if (!dir)
203                 usage(receive_pack_usage);
204
205         /* chdir to the directory. If that fails, try appending ".git" */
206         if (chdir(dir) < 0) {
207                 static char path[PATH_MAX];
208                 snprintf(path, sizeof(path), "%s.git", dir);
209                 if (chdir(path) < 0)
210                         die("unable to cd to %s", dir);
211         }
212
213         /* If we have a ".git" directory, chdir to it */
214         chdir(".git");
215
216         if (access("objects", X_OK) < 0 || access("refs/heads", X_OK) < 0)
217                 die("%s doesn't appear to be a git directory", dir);
218         write_head_info("refs/", nr_heads, heads);
219
220         /* EOF */
221         packet_flush(1);
222
223         read_head_info();
224         if (commands) {
225                 unpack();
226                 execute_commands();
227         }
228         return 0;
229 }