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