Merge branches 'jc/rev-list' and 'jc/pack-thin'
[git] / upload-pack.c
1 #include "cache.h"
2 #include "refs.h"
3 #include "pkt-line.h"
4 #include "tag.h"
5 #include "object.h"
6 #include "commit.h"
7 #include "exec_cmd.h"
8
9 static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] <dir>";
10
11 #define THEY_HAVE (1U << 0)
12 #define OUR_REF (1U << 1)
13 #define WANTED (1U << 2)
14 #define MAX_HAS 256
15 #define MAX_NEEDS 256
16 static int nr_has = 0, nr_needs = 0, multi_ack = 0, nr_our_refs = 0;
17 static int use_thin_pack = 0;
18 static unsigned char has_sha1[MAX_HAS][20];
19 static unsigned char needs_sha1[MAX_NEEDS][20];
20 static unsigned int timeout = 0;
21
22 static void reset_timeout(void)
23 {
24         alarm(timeout);
25 }
26
27 static int strip(char *line, int len)
28 {
29         if (len && line[len-1] == '\n')
30                 line[--len] = 0;
31         return len;
32 }
33
34 static void create_pack_file(void)
35 {
36         int fd[2];
37         pid_t pid;
38         int create_full_pack = (nr_our_refs == nr_needs && !nr_has);
39
40         if (pipe(fd) < 0)
41                 die("git-upload-pack: unable to create pipe");
42         pid = fork();
43         if (pid < 0)
44                 die("git-upload-pack: unable to fork git-rev-list");
45
46         if (!pid) {
47                 int i;
48                 int args;
49                 char **argv;
50                 char *buf;
51                 char **p;
52
53                 if (create_full_pack) {
54                         args = 10;
55                         use_thin_pack = 0; /* no point doing it */
56                 }
57                 else
58                         args = nr_has + nr_needs + 5;
59                 argv = xmalloc(args * sizeof(char *));
60                 buf = xmalloc(args * 45);
61                 p = argv;
62
63                 dup2(fd[1], 1);
64                 close(0);
65                 close(fd[0]);
66                 close(fd[1]);
67                 *p++ = "rev-list";
68                 *p++ = use_thin_pack ? "--objects-edge" : "--objects";
69                 if (create_full_pack || MAX_NEEDS <= nr_needs)
70                         *p++ = "--all";
71                 else {
72                         for (i = 0; i < nr_needs; i++) {
73                                 *p++ = buf;
74                                 memcpy(buf, sha1_to_hex(needs_sha1[i]), 41);
75                                 buf += 41;
76                         }
77                 }
78                 if (!create_full_pack)
79                         for (i = 0; i < nr_has; i++) {
80                                 *p++ = buf;
81                                 *buf++ = '^';
82                                 memcpy(buf, sha1_to_hex(has_sha1[i]), 41);
83                                 buf += 41;
84                         }
85                 *p++ = NULL;
86                 execv_git_cmd(argv);
87                 die("git-upload-pack: unable to exec git-rev-list");
88         }
89         dup2(fd[0], 0);
90         close(fd[0]);
91         close(fd[1]);
92         execl_git_cmd("pack-objects", "--stdout", NULL);
93         die("git-upload-pack: unable to exec git-pack-objects");
94 }
95
96 static int got_sha1(char *hex, unsigned char *sha1)
97 {
98         if (get_sha1_hex(hex, sha1))
99                 die("git-upload-pack: expected SHA1 object, got '%s'", hex);
100         if (!has_sha1_file(sha1))
101                 return 0;
102         if (nr_has < MAX_HAS) {
103                 struct object *o = lookup_object(sha1);
104                 if (!(o && o->parsed))
105                         o = parse_object(sha1);
106                 if (!o)
107                         die("oops (%s)", sha1_to_hex(sha1));
108                 if (o->type == commit_type) {
109                         struct commit_list *parents;
110                         if (o->flags & THEY_HAVE)
111                                 return 0;
112                         o->flags |= THEY_HAVE;
113                         for (parents = ((struct commit*)o)->parents;
114                              parents;
115                              parents = parents->next)
116                                 parents->item->object.flags |= THEY_HAVE;
117                 }
118                 memcpy(has_sha1[nr_has++], sha1, 20);
119         }
120         return 1;
121 }
122
123 static int get_common_commits(void)
124 {
125         static char line[1000];
126         unsigned char sha1[20], last_sha1[20];
127         int len;
128
129         track_object_refs = 0;
130         save_commit_buffer = 0;
131
132         for(;;) {
133                 len = packet_read_line(0, line, sizeof(line));
134                 reset_timeout();
135
136                 if (!len) {
137                         if (nr_has == 0 || multi_ack)
138                                 packet_write(1, "NAK\n");
139                         continue;
140                 }
141                 len = strip(line, len);
142                 if (!strncmp(line, "have ", 5)) {
143                         if (got_sha1(line+5, sha1) &&
144                                         (multi_ack || nr_has == 1)) {
145                                 if (nr_has >= MAX_HAS)
146                                         multi_ack = 0;
147                                 packet_write(1, "ACK %s%s\n",
148                                         sha1_to_hex(sha1),
149                                         multi_ack ?  " continue" : "");
150                                 if (multi_ack)
151                                         memcpy(last_sha1, sha1, 20);
152                         }
153                         continue;
154                 }
155                 if (!strcmp(line, "done")) {
156                         if (nr_has > 0) {
157                                 if (multi_ack)
158                                         packet_write(1, "ACK %s\n",
159                                                         sha1_to_hex(last_sha1));
160                                 return 0;
161                         }
162                         packet_write(1, "NAK\n");
163                         return -1;
164                 }
165                 die("git-upload-pack: expected SHA1 list, got '%s'", line);
166         }
167 }
168
169 static int receive_needs(void)
170 {
171         static char line[1000];
172         int len, needs;
173
174         needs = 0;
175         for (;;) {
176                 struct object *o;
177                 unsigned char dummy[20], *sha1_buf;
178                 len = packet_read_line(0, line, sizeof(line));
179                 reset_timeout();
180                 if (!len)
181                         return needs;
182
183                 sha1_buf = dummy;
184                 if (needs == MAX_NEEDS) {
185                         fprintf(stderr,
186                                 "warning: supporting only a max of %d requests. "
187                                 "sending everything instead.\n",
188                                 MAX_NEEDS);
189                 }
190                 else if (needs < MAX_NEEDS)
191                         sha1_buf = needs_sha1[needs];
192
193                 if (strncmp("want ", line, 5) || get_sha1_hex(line+5, sha1_buf))
194                         die("git-upload-pack: protocol error, "
195                             "expected to get sha, not '%s'", line);
196                 if (strstr(line+45, "multi_ack"))
197                         multi_ack = 1;
198                 if (strstr(line+45, "thin-pack"))
199                         use_thin_pack = 1;
200
201                 /* We have sent all our refs already, and the other end
202                  * should have chosen out of them; otherwise they are
203                  * asking for nonsense.
204                  *
205                  * Hmph.  We may later want to allow "want" line that
206                  * asks for something like "master~10" (symbolic)...
207                  * would it make sense?  I don't know.
208                  */
209                 o = lookup_object(sha1_buf);
210                 if (!o || !(o->flags & OUR_REF))
211                         die("git-upload-pack: not our ref %s", line+5);
212                 if (!(o->flags & WANTED)) {
213                         o->flags |= WANTED;
214                         needs++;
215                 }
216         }
217 }
218
219 static int send_ref(const char *refname, const unsigned char *sha1)
220 {
221         static char *capabilities = "multi_ack thin-pack";
222         struct object *o = parse_object(sha1);
223
224         if (!o)
225                 die("git-upload-pack: cannot find object %s:", sha1_to_hex(sha1));
226
227         if (capabilities)
228                 packet_write(1, "%s %s%c%s\n", sha1_to_hex(sha1), refname,
229                         0, capabilities);
230         else
231                 packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname);
232         capabilities = NULL;
233         if (!(o->flags & OUR_REF)) {
234                 o->flags |= OUR_REF;
235                 nr_our_refs++;
236         }
237         if (o->type == tag_type) {
238                 o = deref_tag(o, refname, 0);
239                 packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname);
240         }
241         return 0;
242 }
243
244 static int upload_pack(void)
245 {
246         reset_timeout();
247         head_ref(send_ref);
248         for_each_ref(send_ref);
249         packet_flush(1);
250         nr_needs = receive_needs();
251         if (!nr_needs)
252                 return 0;
253         get_common_commits();
254         create_pack_file();
255         return 0;
256 }
257
258 int main(int argc, char **argv)
259 {
260         char *dir;
261         int i;
262         int strict = 0;
263
264         for (i = 1; i < argc; i++) {
265                 char *arg = argv[i];
266
267                 if (arg[0] != '-')
268                         break;
269                 if (!strcmp(arg, "--strict")) {
270                         strict = 1;
271                         continue;
272                 }
273                 if (!strncmp(arg, "--timeout=", 10)) {
274                         timeout = atoi(arg+10);
275                         continue;
276                 }
277                 if (!strcmp(arg, "--")) {
278                         i++;
279                         break;
280                 }
281         }
282         
283         if (i != argc-1)
284                 usage(upload_pack_usage);
285         dir = argv[i];
286
287         if (!enter_repo(dir, strict))
288                 die("'%s': unable to chdir or not a git archive", dir);
289
290         upload_pack();
291         return 0;
292 }