7 #include "run-command.h"
10 static struct remote *remote;
11 static const char *url;
12 static struct walker *walker;
17 unsigned progress : 1,
21 static struct options options;
23 static void init_walker(void)
26 walker = get_http_walker(url, remote);
29 static int set_option(const char *name, const char *value)
31 if (!strcmp(name, "verbosity")) {
33 int v = strtol(value, &end, 10);
34 if (value == end || *end)
36 options.verbosity = v;
39 else if (!strcmp(name, "progress")) {
40 if (!strcmp(value, "true"))
42 else if (!strcmp(value, "false"))
46 return 1 /* TODO implement later */;
48 else if (!strcmp(name, "depth")) {
50 unsigned long v = strtoul(value, &end, 10);
51 if (value == end || *end)
54 return 1 /* TODO implement later */;
56 else if (!strcmp(name, "followtags")) {
57 if (!strcmp(value, "true"))
58 options.followtags = 1;
59 else if (!strcmp(value, "false"))
60 options.followtags = 0;
63 return 1 /* TODO implement later */;
65 else if (!strcmp(name, "dry-run")) {
66 if (!strcmp(value, "true"))
68 else if (!strcmp(value, "false"))
75 return 1 /* unsupported */;
84 unsigned proto_git : 1;
86 static struct discovery *last_discovery;
88 static void free_discovery(struct discovery *d)
91 if (d == last_discovery)
92 last_discovery = NULL;
98 static struct discovery* discover_refs(const char *service)
100 struct strbuf buffer = STRBUF_INIT;
101 struct discovery *last = last_discovery;
103 int http_ret, is_http = 0;
105 if (last && !strcmp(service, last->service))
107 free_discovery(last);
109 strbuf_addf(&buffer, "%s/info/refs", url);
110 if (!prefixcmp(url, "http://") || !prefixcmp(url, "https://")) {
112 if (!strchr(url, '?'))
113 strbuf_addch(&buffer, '?');
115 strbuf_addch(&buffer, '&');
116 strbuf_addf(&buffer, "service=%s", service);
118 refs_url = strbuf_detach(&buffer, NULL);
121 http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
125 case HTTP_MISSING_TARGET:
126 die("%s not found: did you run git update-server-info on the"
127 " server?", refs_url);
129 http_error(refs_url, http_ret);
130 die("HTTP request failed");
133 last= xcalloc(1, sizeof(*last_discovery));
134 last->service = service;
135 last->buf_alloc = strbuf_detach(&buffer, &last->len);
136 last->buf = last->buf_alloc;
138 if (is_http && 5 <= last->len && last->buf[4] == '#') {
139 /* smart HTTP response; validate that the service
140 * pkt-line matches our request.
142 struct strbuf exp = STRBUF_INIT;
144 if (packet_get_line(&buffer, &last->buf, &last->len) <= 0)
145 die("%s has invalid packet header", refs_url);
146 if (buffer.len && buffer.buf[buffer.len - 1] == '\n')
147 strbuf_setlen(&buffer, buffer.len - 1);
149 strbuf_addf(&exp, "# service=%s", service);
150 if (strbuf_cmp(&exp, &buffer))
151 die("invalid server response; got '%s'", buffer.buf);
152 strbuf_release(&exp);
154 /* The header can include additional metadata lines, up
155 * until a packet flush marker. Ignore these now, but
156 * in the future we might start to scan them.
158 strbuf_reset(&buffer);
159 while (packet_get_line(&buffer, &last->buf, &last->len) > 0)
160 strbuf_reset(&buffer);
166 strbuf_release(&buffer);
167 last_discovery = last;
171 static int write_discovery(int fd, void *data)
173 struct discovery *heads = data;
175 if (write_in_full(fd, heads->buf, heads->len) != heads->len)
181 static struct ref *parse_git_refs(struct discovery *heads)
183 struct ref *list = NULL;
186 memset(&async, 0, sizeof(async));
187 async.proc = write_discovery;
190 if (start_async(&async))
191 die("cannot start thread to parse advertised refs");
192 get_remote_heads(async.out, &list, 0, NULL, 0, NULL);
194 if (finish_async(&async))
195 die("ref parsing thread failed");
199 static struct ref *parse_info_refs(struct discovery *heads)
201 char *data, *start, *mid;
205 struct ref *refs = NULL;
206 struct ref *ref = NULL;
207 struct ref *last_ref = NULL;
212 while (i < heads->len) {
218 if (data[i] == '\n') {
221 ref = xmalloc(sizeof(struct ref) +
222 strlen(ref_name) + 1);
223 memset(ref, 0, sizeof(struct ref));
224 strcpy(ref->name, ref_name);
225 get_sha1_hex(start, ref->old_sha1);
229 last_ref->next = ref;
237 ref = alloc_ref("HEAD");
238 if (!walker->fetch_ref(walker, ref) &&
239 !resolve_remote_symref(ref, refs)) {
249 static struct ref *get_refs(int for_push)
251 struct discovery *heads;
254 heads = discover_refs("git-receive-pack");
256 heads = discover_refs("git-upload-pack");
258 if (heads->proto_git)
259 return parse_git_refs(heads);
260 return parse_info_refs(heads);
263 static void output_refs(struct ref *refs)
266 for (posn = refs; posn; posn = posn->next) {
268 printf("@%s %s\n", posn->symref, posn->name);
270 printf("%s %s\n", sha1_to_hex(posn->old_sha1), posn->name);
277 static int fetch_dumb(int nr_heads, struct ref **to_fetch)
279 char **targets = xmalloc(nr_heads * sizeof(char*));
282 for (i = 0; i < nr_heads; i++)
283 targets[i] = xstrdup(sha1_to_hex(to_fetch[i]->old_sha1));
287 walker->get_tree = 1;
288 walker->get_history = 1;
289 walker->get_verbosely = options.verbosity >= 3;
290 walker->get_recover = 0;
291 ret = walker_fetch(walker, nr_heads, targets, NULL, NULL);
293 for (i = 0; i < nr_heads; i++)
297 return ret ? error("Fetch failed.") : 0;
300 static void parse_fetch(struct strbuf *buf)
302 struct ref **to_fetch = NULL;
303 struct ref *list_head = NULL;
304 struct ref **list = &list_head;
305 int alloc_heads = 0, nr_heads = 0;
308 if (!prefixcmp(buf->buf, "fetch ")) {
309 char *p = buf->buf + strlen("fetch ");
312 unsigned char old_sha1[20];
314 if (strlen(p) < 40 || get_sha1_hex(p, old_sha1))
315 die("protocol error: expected sha/ref, got %s'", p);
321 die("protocol error: expected sha/ref, got %s'", p);
323 ref = alloc_ref(name);
324 hashcpy(ref->old_sha1, old_sha1);
329 ALLOC_GROW(to_fetch, nr_heads + 1, alloc_heads);
330 to_fetch[nr_heads++] = ref;
333 die("http transport does not support %s", buf->buf);
336 if (strbuf_getline(buf, stdin, '\n') == EOF)
342 if (fetch_dumb(nr_heads, to_fetch))
343 exit(128); /* error already reported */
344 free_refs(list_head);
352 static int push_dav(int nr_spec, char **specs)
354 const char **argv = xmalloc((10 + nr_spec) * sizeof(char*));
357 argv[argc++] = "http-push";
358 argv[argc++] = "--helper-status";
360 argv[argc++] = "--dry-run";
361 if (options.verbosity > 1)
362 argv[argc++] = "--verbose";
364 for (i = 0; i < nr_spec; i++)
365 argv[argc++] = specs[i];
368 if (run_command_v_opt(argv, RUN_GIT_CMD))
369 die("git-%s failed", argv[0]);
374 static void parse_push(struct strbuf *buf)
377 int alloc_spec = 0, nr_spec = 0, i;
380 if (!prefixcmp(buf->buf, "push ")) {
381 ALLOC_GROW(specs, nr_spec + 1, alloc_spec);
382 specs[nr_spec++] = xstrdup(buf->buf + 5);
385 die("http transport does not support %s", buf->buf);
388 if (strbuf_getline(buf, stdin, '\n') == EOF)
394 if (push_dav(nr_spec, specs))
395 exit(128); /* error already reported */
396 for (i = 0; i < nr_spec; i++)
404 int main(int argc, const char **argv)
406 struct strbuf buf = STRBUF_INIT;
408 git_extract_argv0_path(argv[0]);
409 setup_git_directory();
411 fprintf(stderr, "Remote needed\n");
415 options.verbosity = 1;
416 options.progress = !!isatty(2);
418 remote = remote_get(argv[1]);
423 url = remote->url[0];
427 if (strbuf_getline(&buf, stdin, '\n') == EOF)
429 if (!prefixcmp(buf.buf, "fetch ")) {
432 } else if (!strcmp(buf.buf, "list") || !prefixcmp(buf.buf, "list ")) {
433 int for_push = !!strstr(buf.buf + 4, "for-push");
434 output_refs(get_refs(for_push));
436 } else if (!prefixcmp(buf.buf, "push ")) {
439 } else if (!prefixcmp(buf.buf, "option ")) {
440 char *name = buf.buf + strlen("option ");
441 char *value = strchr(name, ' ');
449 result = set_option(name, value);
453 printf("error invalid value\n");
455 printf("unsupported\n");
458 } else if (!strcmp(buf.buf, "capabilities")) {