]>
Commit | Line | Data |
---|---|---|
def88e9a | 1 | #include "cache.h" |
fb9040cc | 2 | #include "refs.h" |
def88e9a | 3 | #include "pkt-line.h" |
49bb805e JH |
4 | #include "commit.h" |
5 | #include "tag.h" | |
6 | #include <time.h> | |
75bfc6c2 | 7 | #include <sys/wait.h> |
def88e9a | 8 | |
8b3d9dc0 | 9 | static int quiet; |
33b83034 JH |
10 | static int verbose; |
11 | static const char fetch_pack_usage[] = | |
12 | "git-fetch-pack [-q] [-v] [--exec=upload-pack] [host:]directory <refs>..."; | |
def88e9a LT |
13 | static const char *exec = "git-upload-pack"; |
14 | ||
0a8944dd JS |
15 | #define COMPLETE (1U << 0) |
16 | ||
33b83034 JH |
17 | static int find_common(int fd[2], unsigned char *result_sha1, |
18 | struct ref *refs) | |
def88e9a | 19 | { |
2759cbc7 | 20 | int fetching; |
def88e9a | 21 | static char line[1000]; |
0a8944dd JS |
22 | static char rev_command[1024]; |
23 | int count = 0, flushes = 0, retval, rev_command_len; | |
75bfc6c2 | 24 | FILE *revs; |
def88e9a | 25 | |
0a8944dd JS |
26 | strcpy(rev_command, "git-rev-list $(git-rev-parse --all)"); |
27 | rev_command_len = strlen(rev_command); | |
2759cbc7 LT |
28 | fetching = 0; |
29 | for ( ; refs ; refs = refs->next) { | |
33b83034 | 30 | unsigned char *remote = refs->old_sha1; |
4dab94d5 | 31 | struct object *o; |
2759cbc7 | 32 | |
0a8944dd | 33 | /* |
4dab94d5 JH |
34 | * If that object is complete (i.e. it is an ancestor of a |
35 | * local ref), we tell them we have it but do not have to | |
36 | * tell them about its ancestors, which they already know | |
37 | * about. | |
38 | */ | |
39 | if (has_sha1_file(remote) && | |
40 | ((o = parse_object(remote)) != NULL) && | |
41 | (o->flags & COMPLETE)) { | |
42 | struct commit_list *p; | |
43 | struct commit *commit = | |
44 | (struct commit *) (o = deref_tag(o)); | |
45 | if (!o) | |
46 | goto repair; | |
47 | if (o->type != commit_type) | |
48 | continue; | |
49 | p = commit->parents; | |
50 | while (p && | |
51 | rev_command_len + 44 < sizeof(rev_command)) { | |
0a8944dd | 52 | snprintf(rev_command + rev_command_len, 44, |
4dab94d5 JH |
53 | " ^%s", |
54 | sha1_to_hex(p->item->object.sha1)); | |
0a8944dd | 55 | rev_command_len += 43; |
4dab94d5 | 56 | p = p->next; |
0a8944dd | 57 | } |
2759cbc7 | 58 | continue; |
0a8944dd | 59 | } |
4dab94d5 | 60 | repair: |
33b83034 | 61 | packet_write(fd[1], "want %s\n", sha1_to_hex(remote)); |
2759cbc7 | 62 | fetching++; |
33b83034 | 63 | } |
fb9040cc | 64 | packet_flush(fd[1]); |
2759cbc7 LT |
65 | if (!fetching) |
66 | return 1; | |
0a8944dd JS |
67 | |
68 | revs = popen(rev_command, "r"); | |
69 | if (!revs) | |
70 | die("unable to run 'git-rev-list'"); | |
71 | ||
75bfc6c2 LT |
72 | flushes = 1; |
73 | retval = -1; | |
74 | while (fgets(line, sizeof(line), revs) != NULL) { | |
def88e9a LT |
75 | unsigned char sha1[20]; |
76 | if (get_sha1_hex(line, sha1)) | |
77 | die("git-fetch-pack: expected object name, got crud"); | |
78 | packet_write(fd[1], "have %s\n", sha1_to_hex(sha1)); | |
33b83034 JH |
79 | if (verbose) |
80 | fprintf(stderr, "have %s\n", sha1_to_hex(sha1)); | |
def88e9a LT |
81 | if (!(31 & ++count)) { |
82 | packet_flush(fd[1]); | |
83 | flushes++; | |
84 | ||
85 | /* | |
86 | * We keep one window "ahead" of the other side, and | |
87 | * will wait for an ACK only on the next one | |
88 | */ | |
89 | if (count == 32) | |
90 | continue; | |
75bfc6c2 LT |
91 | if (get_ack(fd[0], result_sha1)) { |
92 | flushes = 0; | |
93 | retval = 0; | |
33b83034 JH |
94 | if (verbose) |
95 | fprintf(stderr, "got ack\n"); | |
75bfc6c2 LT |
96 | break; |
97 | } | |
def88e9a LT |
98 | flushes--; |
99 | } | |
100 | } | |
75bfc6c2 LT |
101 | pclose(revs); |
102 | packet_write(fd[1], "done\n"); | |
33b83034 JH |
103 | if (verbose) |
104 | fprintf(stderr, "done\n"); | |
def88e9a LT |
105 | while (flushes) { |
106 | flushes--; | |
33b83034 JH |
107 | if (get_ack(fd[0], result_sha1)) { |
108 | if (verbose) | |
109 | fprintf(stderr, "got ack\n"); | |
def88e9a | 110 | return 0; |
33b83034 | 111 | } |
def88e9a | 112 | } |
75bfc6c2 | 113 | return retval; |
def88e9a LT |
114 | } |
115 | ||
49bb805e JH |
116 | static struct commit_list *complete = NULL; |
117 | ||
118 | static int mark_complete(const char *path, const unsigned char *sha1) | |
119 | { | |
120 | struct object *o = parse_object(sha1); | |
121 | ||
122 | while (o && o->type == tag_type) { | |
123 | o->flags |= COMPLETE; | |
124 | o = parse_object(((struct tag *)o)->tagged->sha1); | |
125 | } | |
126 | if (o->type == commit_type) { | |
127 | struct commit *commit = (struct commit *)o; | |
128 | commit->object.flags |= COMPLETE; | |
129 | insert_by_date(commit, &complete); | |
130 | } | |
131 | return 0; | |
132 | } | |
133 | ||
134 | static void mark_recent_complete_commits(unsigned long cutoff) | |
135 | { | |
136 | while (complete && cutoff <= complete->item->date) { | |
137 | if (verbose) | |
138 | fprintf(stderr, "Marking %s as complete\n", | |
139 | sha1_to_hex(complete->item->object.sha1)); | |
140 | pop_most_recent_commit(&complete, COMPLETE); | |
141 | } | |
142 | } | |
143 | ||
2759cbc7 LT |
144 | static int everything_local(struct ref *refs) |
145 | { | |
49bb805e | 146 | struct ref *ref; |
2759cbc7 | 147 | int retval; |
49bb805e JH |
148 | unsigned long cutoff = 0; |
149 | ||
150 | track_object_refs = 0; | |
151 | save_commit_buffer = 0; | |
152 | ||
153 | for (ref = refs; ref; ref = ref->next) { | |
154 | struct object *o; | |
155 | ||
156 | o = parse_object(ref->old_sha1); | |
157 | if (!o) | |
158 | continue; | |
159 | ||
160 | /* We already have it -- which may mean that we were | |
161 | * in sync with the other side at some time after | |
162 | * that (it is OK if we guess wrong here). | |
163 | */ | |
164 | if (o->type == commit_type) { | |
165 | struct commit *commit = (struct commit *)o; | |
166 | if (!cutoff || cutoff < commit->date) | |
167 | cutoff = commit->date; | |
168 | } | |
169 | } | |
170 | ||
171 | for_each_ref(mark_complete); | |
172 | if (cutoff) | |
173 | mark_recent_complete_commits(cutoff); | |
2759cbc7 LT |
174 | |
175 | for (retval = 1; refs ; refs = refs->next) { | |
176 | const unsigned char *remote = refs->old_sha1; | |
177 | unsigned char local[20]; | |
49bb805e | 178 | struct object *o; |
2759cbc7 | 179 | |
49bb805e JH |
180 | o = parse_object(remote); |
181 | if (!o || !(o->flags & COMPLETE)) { | |
2759cbc7 LT |
182 | retval = 0; |
183 | if (!verbose) | |
184 | continue; | |
185 | fprintf(stderr, | |
186 | "want %s (%s)\n", sha1_to_hex(remote), | |
187 | refs->name); | |
188 | continue; | |
189 | } | |
190 | ||
191 | memcpy(refs->new_sha1, local, 20); | |
192 | if (!verbose) | |
193 | continue; | |
194 | fprintf(stderr, | |
195 | "already have %s (%s)\n", sha1_to_hex(remote), | |
196 | refs->name); | |
197 | } | |
198 | return retval; | |
199 | } | |
200 | ||
def88e9a LT |
201 | static int fetch_pack(int fd[2], int nr_match, char **match) |
202 | { | |
d1c133f5 LT |
203 | struct ref *ref; |
204 | unsigned char sha1[20]; | |
205 | int status; | |
75bfc6c2 | 206 | pid_t pid; |
def88e9a | 207 | |
1a7141ff | 208 | get_remote_heads(fd[0], &ref, nr_match, match, 1); |
d1c133f5 LT |
209 | if (!ref) { |
210 | packet_flush(fd[1]); | |
211 | die("no matching remote head"); | |
212 | } | |
2759cbc7 LT |
213 | if (everything_local(ref)) { |
214 | packet_flush(fd[1]); | |
215 | goto all_done; | |
216 | } | |
33b83034 JH |
217 | if (find_common(fd, sha1, ref) < 0) |
218 | fprintf(stderr, "warning: no common commits\n"); | |
75bfc6c2 LT |
219 | pid = fork(); |
220 | if (pid < 0) | |
221 | die("git-fetch-pack: unable to fork off git-unpack-objects"); | |
222 | if (!pid) { | |
75bfc6c2 LT |
223 | dup2(fd[0], 0); |
224 | close(fd[0]); | |
85c414b5 | 225 | close(fd[1]); |
8b3d9dc0 JH |
226 | execlp("git-unpack-objects", "git-unpack-objects", |
227 | quiet ? "-q" : NULL, NULL); | |
75bfc6c2 LT |
228 | die("git-unpack-objects exec failed"); |
229 | } | |
fb9040cc | 230 | close(fd[0]); |
75bfc6c2 LT |
231 | close(fd[1]); |
232 | while (waitpid(pid, &status, 0) < 0) { | |
233 | if (errno != EINTR) | |
234 | die("waiting for git-unpack-objects: %s", strerror(errno)); | |
235 | } | |
236 | if (WIFEXITED(status)) { | |
237 | int code = WEXITSTATUS(status); | |
238 | if (code) | |
239 | die("git-unpack-objects died with error code %d", code); | |
2759cbc7 | 240 | all_done: |
33b83034 JH |
241 | while (ref) { |
242 | printf("%s %s\n", | |
243 | sha1_to_hex(ref->old_sha1), ref->name); | |
244 | ref = ref->next; | |
245 | } | |
75bfc6c2 LT |
246 | return 0; |
247 | } | |
248 | if (WIFSIGNALED(status)) { | |
249 | int sig = WTERMSIG(status); | |
250 | die("git-unpack-objects died of signal %d", sig); | |
251 | } | |
252 | die("Sherlock Holmes! git-unpack-objects died of unnatural causes %d!", status); | |
def88e9a LT |
253 | } |
254 | ||
255 | int main(int argc, char **argv) | |
256 | { | |
257 | int i, ret, nr_heads; | |
258 | char *dest = NULL, **heads; | |
259 | int fd[2]; | |
260 | pid_t pid; | |
261 | ||
262 | nr_heads = 0; | |
263 | heads = NULL; | |
264 | for (i = 1; i < argc; i++) { | |
265 | char *arg = argv[i]; | |
266 | ||
267 | if (*arg == '-') { | |
8b3d9dc0 JH |
268 | if (!strncmp("--exec=", arg, 7)) { |
269 | exec = arg + 7; | |
270 | continue; | |
271 | } | |
33b83034 JH |
272 | if (!strcmp("-q", arg)) { |
273 | quiet = 1; | |
274 | continue; | |
275 | } | |
276 | if (!strcmp("-v", arg)) { | |
277 | verbose = 1; | |
278 | continue; | |
279 | } | |
def88e9a LT |
280 | usage(fetch_pack_usage); |
281 | } | |
282 | dest = arg; | |
283 | heads = argv + i + 1; | |
284 | nr_heads = argc - i - 1; | |
285 | break; | |
286 | } | |
287 | if (!dest) | |
288 | usage(fetch_pack_usage); | |
289 | pid = git_connect(fd, dest, exec); | |
290 | if (pid < 0) | |
291 | return 1; | |
292 | ret = fetch_pack(fd, nr_heads, heads); | |
293 | close(fd[0]); | |
294 | close(fd[1]); | |
295 | finish_connect(pid); | |
296 | return ret; | |
297 | } |