Make send/receive-pack be closer to doing something interesting
[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 line {
90         struct line *next;
91         char data[0];
92 };
93
94 struct line *commands = NULL;
95
96 /*
97  * This gets called after(if) we've successfully
98  * unpacked the data payload.
99  */
100 static void execute_commands(void)
101 {
102         struct line *line = commands;
103
104         while (line) {
105                 fprintf(stderr, "%s", line->data);
106                 line = line->next;
107         }
108 }
109
110 static void read_head_info(void)
111 {
112         struct line **p = &commands;
113         for (;;) {
114                 static char line[1000];
115                 int len = packet_read_line(0, line, sizeof(line));
116                 struct line *n;
117                 if (!len)
118                         break;
119                 n = xmalloc(sizeof(struct line) + len);
120                 n->next = NULL;
121                 memcpy(n->data, line + 4, len - 3);
122                 *p = n;
123                 p = &n->next;
124         }
125 }
126
127 static void unpack(void)
128 {
129         pid_t pid = fork();
130
131         if (pid < 0)
132                 die("unpack fork failed");
133         if (!pid) {
134                 setenv("GIT_DIR", ".", 1);
135                 execlp(unpacker, unpacker, NULL);
136                 die("unpack execute failed");
137         }
138
139         for (;;) {
140                 int status, code;
141                 int retval = waitpid(pid, &status, 0);
142
143                 if (retval < 0) {
144                         if (errno == EINTR)
145                                 continue;
146                         die("waitpid failed (%s)", strerror(retval));
147                 }
148                 if (retval != pid)
149                         die("waitpid is confused");
150                 if (WIFSIGNALED(status))
151                         die("%s died of signal %d", unpacker, WTERMSIG(status));
152                 if (!WIFEXITED(status))
153                         die("%s died out of really strange complications", unpacker);
154                 code = WEXITSTATUS(status);
155                 if (code)
156                         die("%s exited with error code %d", unpacker, code);
157                 return;
158         }
159 }
160
161 int main(int argc, char **argv)
162 {
163         int i, nr_heads = 0;
164         const char *dir = NULL;
165         char **heads = NULL;
166
167         argv++;
168         for (i = 1; i < argc; i++) {
169                 const char *arg = *argv++;
170
171                 if (*arg == '-') {
172                         if (!strncmp(arg, "--unpack=", 9)) {
173                                 unpacker = arg+9;
174                                 continue;
175                         }
176                         /* Do flag handling here */
177                         usage(receive_pack_usage);
178                 }
179                 dir = arg;
180                 heads = argv;
181                 nr_heads = argc - i - 1;
182                 break;
183         }
184         if (!dir)
185                 usage(receive_pack_usage);
186
187         /* chdir to the directory. If that fails, try appending ".git" */
188         if (chdir(dir) < 0) {
189                 static char path[PATH_MAX];
190                 snprintf(path, sizeof(path), "%s.git", dir);
191                 if (chdir(path) < 0)
192                         die("unable to cd to %s", dir);
193         }
194
195         /* If we have a ".git" directory, chdir to it */
196         chdir(".git");
197
198         if (access("objects", X_OK) < 0 || access("refs/heads", X_OK) < 0)
199                 die("%s doesn't appear to be a git directory", dir);
200         write_head_info("refs/", nr_heads, heads);
201
202         /* EOF */
203         packet_flush(1);
204
205         read_head_info();
206         unpack();
207         execute_commands();
208         return 0;
209 }