Merge branch 'fixes'
[git.git] / upload-pack.c
1 #include "cache.h"
2 #include "refs.h"
3 #include "pkt-line.h"
4
5 static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] <dir>";
6
7 #define MAX_HAS (16)
8 #define MAX_NEEDS (256)
9 static int nr_has = 0, nr_needs = 0;
10 static unsigned char has_sha1[MAX_HAS][20];
11 static unsigned char needs_sha1[MAX_NEEDS][20];
12 static unsigned int timeout = 0;
13
14 static void reset_timeout(void)
15 {
16         alarm(timeout);
17 }
18
19 static int strip(char *line, int len)
20 {
21         if (len && line[len-1] == '\n')
22                 line[--len] = 0;
23         return len;
24 }
25
26 static void create_pack_file(void)
27 {
28         int fd[2];
29         pid_t pid;
30
31         if (pipe(fd) < 0)
32                 die("git-upload-pack: unable to create pipe");
33         pid = fork();
34         if (pid < 0)
35                 die("git-upload-pack: unable to fork git-rev-list");
36
37         if (!pid) {
38                 int i;
39                 int args;
40                 char **argv;
41                 char *buf;
42                 char **p;
43
44                 if (MAX_NEEDS <= nr_needs)
45                         args = nr_has + 10;
46                 else
47                         args = nr_has + nr_needs + 5;
48                 argv = xmalloc(args * sizeof(char *));
49                 buf = xmalloc(args * 45);
50                 p = argv;
51
52                 dup2(fd[1], 1);
53                 close(0);
54                 close(fd[0]);
55                 close(fd[1]);
56                 *p++ = "git-rev-list";
57                 *p++ = "--objects";
58                 if (MAX_NEEDS <= nr_needs)
59                         *p++ = "--all";
60                 else {
61                         for (i = 0; i < nr_needs; i++) {
62                                 *p++ = buf;
63                                 memcpy(buf, sha1_to_hex(needs_sha1[i]), 41);
64                                 buf += 41;
65                         }
66                 }
67                 for (i = 0; i < nr_has; i++) {
68                         *p++ = buf;
69                         *buf++ = '^';
70                         memcpy(buf, sha1_to_hex(has_sha1[i]), 41);
71                         buf += 41;
72                 }
73                 *p++ = NULL;
74                 execvp("git-rev-list", argv);
75                 die("git-upload-pack: unable to exec git-rev-list");
76         }
77         dup2(fd[0], 0);
78         close(fd[0]);
79         close(fd[1]);
80         execlp("git-pack-objects", "git-pack-objects", "--stdout", NULL);
81         die("git-upload-pack: unable to exec git-pack-objects");
82 }
83
84 static int got_sha1(char *hex, unsigned char *sha1)
85 {
86         int nr;
87         if (get_sha1_hex(hex, sha1))
88                 die("git-upload-pack: expected SHA1 object, got '%s'", hex);
89         if (!has_sha1_file(sha1))
90                 return 0;
91         nr = nr_has;
92         if (nr < MAX_HAS) {
93                 memcpy(has_sha1[nr], sha1, 20);
94                 nr_has = nr+1;
95         }
96         return 1;
97 }
98
99 static int get_common_commits(void)
100 {
101         static char line[1000];
102         unsigned char sha1[20];
103         int len;
104
105         for(;;) {
106                 len = packet_read_line(0, line, sizeof(line));
107                 reset_timeout();
108
109                 if (!len) {
110                         packet_write(1, "NAK\n");
111                         continue;
112                 }
113                 len = strip(line, len);
114                 if (!strncmp(line, "have ", 5)) {
115                         if (got_sha1(line+5, sha1)) {
116                                 packet_write(1, "ACK %s\n", sha1_to_hex(sha1));
117                                 break;
118                         }
119                         continue;
120                 }
121                 if (!strcmp(line, "done")) {
122                         packet_write(1, "NAK\n");
123                         return -1;
124                 }
125                 die("git-upload-pack: expected SHA1 list, got '%s'", line);
126         }
127
128         for (;;) {
129                 len = packet_read_line(0, line, sizeof(line));
130                 reset_timeout();
131                 if (!len)
132                         continue;
133                 len = strip(line, len);
134                 if (!strncmp(line, "have ", 5)) {
135                         got_sha1(line+5, sha1);
136                         continue;
137                 }
138                 if (!strcmp(line, "done"))
139                         break;
140                 die("git-upload-pack: expected SHA1 list, got '%s'", line);
141         }
142         return 0;
143 }
144
145 static int receive_needs(void)
146 {
147         static char line[1000];
148         int len, needs;
149
150         needs = 0;
151         for (;;) {
152                 unsigned char dummy[20], *sha1_buf;
153                 len = packet_read_line(0, line, sizeof(line));
154                 reset_timeout();
155                 if (!len)
156                         return needs;
157
158                 sha1_buf = dummy;
159                 if (needs == MAX_NEEDS) {
160                         fprintf(stderr,
161                                 "warning: supporting only a max of %d requests. "
162                                 "sending everything instead.\n",
163                                 MAX_NEEDS);
164                 }
165                 else if (needs < MAX_NEEDS)
166                         sha1_buf = needs_sha1[needs];
167
168                 if (strncmp("want ", line, 5) || get_sha1_hex(line+5, sha1_buf))
169                         die("git-upload-pack: protocol error, "
170                             "expected to get sha, not '%s'", line);
171                 needs++;
172         }
173 }
174
175 static int send_ref(const char *refname, const unsigned char *sha1)
176 {
177         packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname);
178         return 0;
179 }
180
181 static int upload_pack(void)
182 {
183         reset_timeout();
184         head_ref(send_ref);
185         for_each_ref(send_ref);
186         packet_flush(1);
187         nr_needs = receive_needs();
188         if (!nr_needs)
189                 return 0;
190         get_common_commits();
191         create_pack_file();
192         return 0;
193 }
194
195 int main(int argc, char **argv)
196 {
197         const char *dir;
198         int i;
199         int strict = 0;
200
201         for (i = 1; i < argc; i++) {
202                 char *arg = argv[i];
203
204                 if (arg[0] != '-')
205                         break;
206                 if (!strcmp(arg, "--strict")) {
207                         strict = 1;
208                         continue;
209                 }
210                 if (!strncmp(arg, "--timeout=", 10)) {
211                         timeout = atoi(arg+10);
212                         continue;
213                 }
214                 if (!strcmp(arg, "--")) {
215                         i++;
216                         break;
217                 }
218         }
219         
220         if (i != argc-1)
221                 usage(upload_pack_usage);
222         dir = argv[i];
223
224         /* chdir to the directory. If that fails, try appending ".git" */
225         if (chdir(dir) < 0) {
226                 if (strict || chdir(mkpath("%s.git", dir)) < 0)
227                         die("git-upload-pack unable to chdir to %s", dir);
228         }
229         if (!strict)
230                 chdir(".git");
231
232         if (access("objects", X_OK) || access("refs", X_OK))
233                 die("git-upload-pack: %s doesn't seem to be a git archive", dir);
234
235         putenv("GIT_DIR=.");
236         upload_pack();
237         return 0;
238 }