clone-pack: new option --keep tells it not to explode the pack.
[git.git] / clone-pack.c
1 #include "cache.h"
2 #include "refs.h"
3 #include "pkt-line.h"
4 #include <sys/wait.h>
5
6 static int quiet;
7 static int keep_pack;
8 static const char clone_pack_usage[] =
9 "git-clone-pack [-q] [--keep] [--exec=<git-upload-pack>] [<host>:]<directory> [<heads>]*";
10 static const char *exec = "git-upload-pack";
11
12 static void clone_handshake(int fd[2], struct ref *ref)
13 {
14         unsigned char sha1[20];
15
16         while (ref) {
17                 packet_write(fd[1], "want %s\n", sha1_to_hex(ref->old_sha1));
18                 ref = ref->next;
19         }
20         packet_flush(fd[1]);
21
22         /* We don't have nuttin' */
23         packet_write(fd[1], "done\n");
24         if (get_ack(fd[0], sha1))
25                 error("Huh! git-clone-pack got positive ack for %s", sha1_to_hex(sha1));
26 }
27
28 static int is_master(struct ref *ref)
29 {
30         return !strcmp(ref->name, "refs/heads/master");
31 }
32
33 static void write_one_ref(struct ref *ref)
34 {
35         char *path = git_path("%s", ref->name);
36         int fd;
37         char *hex;
38
39         if (safe_create_leading_directories(path))
40                 die("unable to create leading directory for %s", ref->name);
41         fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0666);
42         if (fd < 0)
43                 die("unable to create ref %s", ref->name);
44         hex = sha1_to_hex(ref->old_sha1);
45         hex[40] = '\n';
46         if (write(fd, hex, 41) != 41)
47                 die("unable to write ref %s", ref->name);
48         close(fd);
49 }
50
51 static void write_refs(struct ref *ref)
52 {
53         struct ref *head = NULL, *head_ptr, *master_ref;
54         char *head_path;
55
56         /* Upload-pack must report HEAD first */
57         if (!strcmp(ref->name, "HEAD")) {
58                 head = ref;
59                 ref = ref->next;
60         }
61         head_ptr = NULL;
62         master_ref = NULL;
63         while (ref) {
64                 if (is_master(ref))
65                         master_ref = ref;
66                 if (head &&
67                     !memcmp(ref->old_sha1, head->old_sha1, 20) &&
68                     !strncmp(ref->name, "refs/heads/",11) &&
69                     (!head_ptr || ref == master_ref))
70                         head_ptr = ref;
71
72                 write_one_ref(ref);
73                 ref = ref->next;
74         }
75         if (!head) {
76                 fprintf(stderr, "No HEAD in remote.\n");
77                 return;
78         }
79
80         head_path = strdup(git_path("HEAD"));
81         if (!head_ptr) {
82                 /*
83                  * If we had a master ref, and it wasn't HEAD, we need to undo the
84                  * symlink, and write a standalone HEAD. Give a warning, because that's
85                  * really really wrong.
86                  */
87                 if (master_ref) {
88                         error("HEAD doesn't point to any refs! Making standalone HEAD");
89                         unlink(head_path);
90                 }
91                 write_one_ref(head);
92                 free(head_path);
93                 return;
94         }
95
96         /* We reset to the master branch if it's available */
97         if (master_ref)
98                 return;
99
100         fprintf(stderr, "Setting HEAD to %s\n", head_ptr->name);
101
102         /*
103          * Uhhuh. Other end didn't have master. We start HEAD off with
104          * the first branch with the same value.
105          */
106         if (create_symref(head_path, head_ptr->name) < 0)
107                 die("unable to link HEAD to %s", head_ptr->name);
108         free(head_path);
109 }
110
111 static int clone_by_unpack(int fd[2])
112 {
113         int status;
114         pid_t pid;
115
116         pid = fork();
117         if (pid < 0)
118                 die("git-clone-pack: unable to fork off git-unpack-objects");
119         if (!pid) {
120                 dup2(fd[0], 0);
121                 close(fd[0]);
122                 close(fd[1]);
123                 execlp("git-unpack-objects", "git-unpack-objects",
124                         quiet ? "-q" : NULL, NULL);
125                 die("git-unpack-objects exec failed");
126         }
127         close(fd[0]);
128         close(fd[1]);
129         while (waitpid(pid, &status, 0) < 0) {
130                 if (errno != EINTR)
131                         die("waiting for git-unpack-objects: %s", strerror(errno));
132         }
133         if (WIFEXITED(status)) {
134                 int code = WEXITSTATUS(status);
135                 if (code)
136                         die("git-unpack-objects died with error code %d", code);
137                 return 0;
138         }
139         if (WIFSIGNALED(status)) {
140                 int sig = WTERMSIG(status);
141                 die("git-unpack-objects died of signal %d", sig);
142         }
143         die("Sherlock Holmes! git-unpack-objects died of unnatural causes %d!", status);
144 }
145
146 static int finish_pack(const char *pack_tmp_name)
147 {
148         int pipe_fd[2];
149         pid_t pid;
150         char idx[PATH_MAX];
151         char final[PATH_MAX];
152         char hash[41];
153         unsigned char sha1[20];
154         char *cp;
155         int err = 0;
156
157         if (pipe(pipe_fd) < 0)
158                 die("git-clone-pack: unable to set up pipe");
159
160         strcpy(idx, pack_tmp_name); /* ".git/objects/pack-XXXXXX" */
161         cp = strrchr(idx, '/');
162         memcpy(cp, "/pidx", 5);
163
164         pid = fork();
165         if (pid < 0)
166                 die("git-clone-pack: unable to fork off git-index-pack");
167         if (!pid) {
168                 close(0);
169                 dup2(pipe_fd[1], 1);
170                 close(pipe_fd[0]);
171                 close(pipe_fd[1]);
172                 execlp("git-index-pack","git-index-pack",
173                        "-o", idx, pack_tmp_name, NULL);
174                 error("cannot exec git-index-pack <%s> <%s>",
175                       idx, pack_tmp_name);
176                 exit(1);
177         }
178         close(pipe_fd[1]);
179         if (read(pipe_fd[0], hash, 40) != 40) {
180                 error("git-clone-pack: unable to read from git-index-pack");
181                 err = 1;
182         }
183         close(pipe_fd[0]);
184
185         for (;;) {
186                 int status, code;
187                 int retval = waitpid(pid, &status, 0);
188
189                 if (retval < 0) {
190                         if (errno == EINTR)
191                                 continue;
192                         error("waitpid failed (%s)", strerror(retval));
193                         goto error_die;
194                 }
195                 if (WIFSIGNALED(status)) {
196                         int sig = WTERMSIG(status);
197                         error("git-index-pack died of signal %d", sig);
198                         goto error_die;
199                 }
200                 if (!WIFEXITED(status)) {
201                         error("git-index-pack died of unnatural causes %d",
202                               status);
203                         goto error_die;
204                 }
205                 code = WEXITSTATUS(status);
206                 if (code) {
207                         error("git-index-pack died with error code %d", code);
208                         goto error_die;
209                 }
210                 if (err)
211                         goto error_die;
212                 break;
213         }
214         hash[40] = 0;
215         if (get_sha1_hex(hash, sha1)) {
216                 error("git-index-pack reported nonsense '%s'", hash);
217                 goto error_die;
218         }
219         /* Now we have pack in pack_tmp_name[], and
220          * idx in idx[]; rename them to their final names.
221          */
222         snprintf(final, sizeof(final),
223                  "%s/pack/pack-%s.pack", get_object_directory(), hash);
224         move_temp_to_file(pack_tmp_name, final);
225         chmod(final, 0444);
226         snprintf(final, sizeof(final),
227                  "%s/pack/pack-%s.idx", get_object_directory(), hash);
228         move_temp_to_file(idx, final);
229         chmod(final, 0444);
230         return 0;
231
232  error_die:
233         unlink(idx);
234         unlink(pack_tmp_name);
235         exit(1);
236 }
237
238 static int clone_without_unpack(int fd[2])
239 {
240         char tmpfile[PATH_MAX];
241         int ofd, ifd;
242
243         ifd = fd[0];
244         snprintf(tmpfile, sizeof(tmpfile),
245                  "%s/pack-XXXXXX", get_object_directory());
246         ofd = mkstemp(tmpfile);
247         if (ofd < 0)
248                 return error("unable to create temporary file %s", tmpfile);
249
250         while (1) {
251                 char buf[8192];
252                 ssize_t sz, wsz, pos;
253                 sz = read(ifd, buf, sizeof(buf));
254                 if (sz == 0)
255                         break;
256                 if (sz < 0) {
257                         error("error reading pack (%s)", strerror(errno));
258                         close(ofd);
259                         unlink(tmpfile);
260                         return -1;
261                 }
262                 pos = 0;
263                 while (pos < sz) {
264                         wsz = write(ofd, buf + pos, sz - pos);
265                         if (wsz < 0) {
266                                 error("error writing pack (%s)",
267                                       strerror(errno));
268                                 close(ofd);
269                                 unlink(tmpfile);
270                                 return -1;
271                         }
272                         pos += wsz;
273                 }
274         }
275         close(ofd);
276         return finish_pack(tmpfile);
277 }
278
279 static int clone_pack(int fd[2], int nr_match, char **match)
280 {
281         struct ref *refs;
282         int status;
283
284         get_remote_heads(fd[0], &refs, nr_match, match);
285         if (!refs) {
286                 packet_flush(fd[1]);
287                 die("no matching remote head");
288         }
289         clone_handshake(fd, refs);
290
291         if (keep_pack)
292                 status = clone_without_unpack(fd);
293         else
294                 status = clone_by_unpack(fd);
295
296         if (!status)
297                 write_refs(refs);
298         return status;
299 }
300
301 static int clone_options(const char *var, const char *value)
302 {
303         if (!strcmp("clone.keeppack", var)) {
304                 keep_pack = git_config_bool(var, value);
305                 return 0;
306         }
307         if (!strcmp("clone.quiet", var)) {
308                 quiet = git_config_bool(var, value);
309                 return 0;
310         }
311         /*
312          * Put other local option parsing for this program
313          * here ...
314          */
315
316         /* Fall back on the default ones */
317         return git_default_config(var, value);
318 }
319
320 int main(int argc, char **argv)
321 {
322         int i, ret, nr_heads;
323         char *dest = NULL, **heads;
324         int fd[2];
325         pid_t pid;
326
327         git_config(clone_options);
328         nr_heads = 0;
329         heads = NULL;
330         for (i = 1; i < argc; i++) {
331                 char *arg = argv[i];
332
333                 if (*arg == '-') {
334                         if (!strcmp("-q", arg)) {
335                                 quiet = 1;
336                                 continue;
337                         }
338                         if (!strncmp("--exec=", arg, 7)) {
339                                 exec = arg + 7;
340                                 continue;
341                         }
342                         if (!strcmp("--keep", arg)) {
343                                 keep_pack = 1;
344                                 continue;
345                         }
346                         usage(clone_pack_usage);
347                 }
348                 dest = arg;
349                 heads = argv + i + 1;
350                 nr_heads = argc - i - 1;
351                 break;
352         }
353         if (!dest)
354                 usage(clone_pack_usage);
355         pid = git_connect(fd, dest, exec);
356         if (pid < 0)
357                 return 1;
358         ret = clone_pack(fd, nr_heads, heads);
359         close(fd[0]);
360         close(fd[1]);
361         finish_connect(pid);
362         return ret;
363 }