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