Commit first cut at "git-fetch-pack"
[git/git.git] / fetch-pack.c
CommitLineData
def88e9a
LT
1#include "cache.h"
2#include "pkt-line.h"
3
4static const char fetch_pack_usage[] = "git-fetch-pack [host:]directory [heads]* < mycommitlist";
5static const char *exec = "git-upload-pack";
6
7static int get_ack(int fd, unsigned char *result_sha1)
8{
9 static char line[1000];
10 int len = packet_read_line(fd, line, sizeof(line));
11
12 if (!len)
13 die("git-fetch-pack: expected ACK/NAK, got EOF");
14 if (line[len-1] == '\n')
15 line[--len] = 0;
16 if (!strcmp(line, "NAK"))
17 return 0;
18 if (!strncmp(line, "ACK ", 3)) {
19 if (!get_sha1_hex(line+4, result_sha1))
20 return 1;
21 }
22 die("git-fetch_pack: expected ACK/NAK, got '%s'", line);
23}
24
25static int find_common(int fd[2], unsigned char *result_sha1)
26{
27 static char line[1000];
28 int count = 0, flushes = 0;
29
30 while (fgets(line, sizeof(line), stdin) != NULL) {
31 unsigned char sha1[20];
32 if (get_sha1_hex(line, sha1))
33 die("git-fetch-pack: expected object name, got crud");
34 packet_write(fd[1], "have %s\n", sha1_to_hex(sha1));
35 if (!(31 & ++count)) {
36 packet_flush(fd[1]);
37 flushes++;
38
39 /*
40 * We keep one window "ahead" of the other side, and
41 * will wait for an ACK only on the next one
42 */
43 if (count == 32)
44 continue;
45 if (get_ack(fd[0], result_sha1))
46 return 0;
47 flushes--;
48 }
49 }
50 flushes++;
51 packet_flush(fd[1]);
52 while (flushes) {
53 flushes--;
54 if (get_ack(fd[0], result_sha1))
55 return 0;
56 }
57 return -1;
58}
59
60static int get_remote_heads(int fd, int nr_match, char **match)
61{
62 for (;;) {
63 static char line[1000];
64 unsigned char sha1[20];
65 char *refname;
66 int len;
67
68 len = packet_read_line(fd, line, sizeof(line));
69 if (!len)
70 break;
71 if (line[len-1] == '\n')
72 line[--len] = 0;
73 if (len < 42 || get_sha1_hex(line, sha1))
74 die("git-fetch-pack: protocol error - expected ref descriptor, got '%sä'", line);
75 refname = line+41;
76 if (nr_match && !path_match(refname, nr_match, match))
77 continue;
78 printf("%s %s\n", sha1_to_hex(sha1), refname);
79 }
80 return 0;
81}
82
83static int fetch_pack(int fd[2], int nr_match, char **match)
84{
85 unsigned char sha1[20];
86
87 get_remote_heads(fd[0], nr_match, match);
88 if (find_common(fd, sha1) < 0)
89 die("git-fetch-pack: no common commits");
90 printf("common commit: %s\n", sha1_to_hex(sha1));
91 return 0;
92}
93
94int main(int argc, char **argv)
95{
96 int i, ret, nr_heads;
97 char *dest = NULL, **heads;
98 int fd[2];
99 pid_t pid;
100
101 nr_heads = 0;
102 heads = NULL;
103 for (i = 1; i < argc; i++) {
104 char *arg = argv[i];
105
106 if (*arg == '-') {
107 /* Arguments go here */
108 usage(fetch_pack_usage);
109 }
110 dest = arg;
111 heads = argv + i + 1;
112 nr_heads = argc - i - 1;
113 break;
114 }
115 if (!dest)
116 usage(fetch_pack_usage);
117 pid = git_connect(fd, dest, exec);
118 if (pid < 0)
119 return 1;
120 ret = fetch_pack(fd, nr_heads, heads);
121 close(fd[0]);
122 close(fd[1]);
123 finish_connect(pid);
124 return ret;
125}