From: Linus Torvalds Date: Mon, 4 Jul 2005 20:26:53 +0000 (-0700) Subject: Commit first cut at "git-fetch-pack" X-Git-Tag: v0.99~80 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=def88e9;p=git.git Commit first cut at "git-fetch-pack" It's meant to be used by "git fetch" for the local and ssh case. It doesn't actually do the fetching now, but it does discover the common commit point. --- diff --git a/Makefile b/Makefile index e7a26449b..a833a8561 100644 --- a/Makefile +++ b/Makefile @@ -40,10 +40,10 @@ PROG= git-update-cache git-diff-files git-init-db git-write-tree \ git-unpack-file git-export git-diff-cache git-convert-cache \ git-http-pull git-ssh-push git-ssh-pull git-rev-list git-mktag \ git-diff-helper git-tar-tree git-local-pull git-write-blob \ - git-get-tar-commit-id git-apply git-stripspace \ - git-cvs2git git-diff-stages git-rev-parse git-patch-id \ - git-pack-objects git-unpack-objects git-verify-pack \ - git-receive-pack git-send-pack git-prune-packed + git-get-tar-commit-id git-apply git-stripspace git-cvs2git \ + git-diff-stages git-rev-parse git-patch-id git-pack-objects \ + git-unpack-objects git-verify-pack git-receive-pack git-send-pack \ + git-prune-packed git-fetch-pack git-upload-pack all: $(PROG) @@ -139,6 +139,7 @@ git-verify-pack: verify-pack.c git-receive-pack: receive-pack.c git-send-pack: send-pack.c git-prune-packed: prune-packed.c +git-fetch-pack: fetch-pack.c git-http-pull: LIBS += -lcurl git-rev-list: LIBS += -lssl diff --git a/fetch-pack.c b/fetch-pack.c new file mode 100644 index 000000000..e9035a1dc --- /dev/null +++ b/fetch-pack.c @@ -0,0 +1,125 @@ +#include "cache.h" +#include "pkt-line.h" + +static const char fetch_pack_usage[] = "git-fetch-pack [host:]directory [heads]* < mycommitlist"; +static const char *exec = "git-upload-pack"; + +static int get_ack(int fd, unsigned char *result_sha1) +{ + static char line[1000]; + int len = packet_read_line(fd, line, sizeof(line)); + + if (!len) + die("git-fetch-pack: expected ACK/NAK, got EOF"); + if (line[len-1] == '\n') + line[--len] = 0; + if (!strcmp(line, "NAK")) + return 0; + if (!strncmp(line, "ACK ", 3)) { + if (!get_sha1_hex(line+4, result_sha1)) + return 1; + } + die("git-fetch_pack: expected ACK/NAK, got '%s'", line); +} + +static int find_common(int fd[2], unsigned char *result_sha1) +{ + static char line[1000]; + int count = 0, flushes = 0; + + while (fgets(line, sizeof(line), stdin) != NULL) { + unsigned char sha1[20]; + if (get_sha1_hex(line, sha1)) + die("git-fetch-pack: expected object name, got crud"); + packet_write(fd[1], "have %s\n", sha1_to_hex(sha1)); + if (!(31 & ++count)) { + packet_flush(fd[1]); + flushes++; + + /* + * We keep one window "ahead" of the other side, and + * will wait for an ACK only on the next one + */ + if (count == 32) + continue; + if (get_ack(fd[0], result_sha1)) + return 0; + flushes--; + } + } + flushes++; + packet_flush(fd[1]); + while (flushes) { + flushes--; + if (get_ack(fd[0], result_sha1)) + return 0; + } + return -1; +} + +static int get_remote_heads(int fd, int nr_match, char **match) +{ + for (;;) { + static char line[1000]; + unsigned char sha1[20]; + char *refname; + int len; + + len = packet_read_line(fd, line, sizeof(line)); + if (!len) + break; + if (line[len-1] == '\n') + line[--len] = 0; + if (len < 42 || get_sha1_hex(line, sha1)) + die("git-fetch-pack: protocol error - expected ref descriptor, got '%sä'", line); + refname = line+41; + if (nr_match && !path_match(refname, nr_match, match)) + continue; + printf("%s %s\n", sha1_to_hex(sha1), refname); + } + return 0; +} + +static int fetch_pack(int fd[2], int nr_match, char **match) +{ + unsigned char sha1[20]; + + get_remote_heads(fd[0], nr_match, match); + if (find_common(fd, sha1) < 0) + die("git-fetch-pack: no common commits"); + printf("common commit: %s\n", sha1_to_hex(sha1)); + return 0; +} + +int main(int argc, char **argv) +{ + int i, ret, nr_heads; + char *dest = NULL, **heads; + int fd[2]; + pid_t pid; + + nr_heads = 0; + heads = NULL; + for (i = 1; i < argc; i++) { + char *arg = argv[i]; + + if (*arg == '-') { + /* Arguments go here */ + usage(fetch_pack_usage); + } + dest = arg; + heads = argv + i + 1; + nr_heads = argc - i - 1; + break; + } + if (!dest) + usage(fetch_pack_usage); + pid = git_connect(fd, dest, exec); + if (pid < 0) + return 1; + ret = fetch_pack(fd, nr_heads, heads); + close(fd[0]); + close(fd[1]); + finish_connect(pid); + return ret; +} diff --git a/upload-pack.c b/upload-pack.c new file mode 100644 index 000000000..18b8e1a6c --- /dev/null +++ b/upload-pack.c @@ -0,0 +1,86 @@ +#include "cache.h" +#include "refs.h" +#include "pkt-line.h" + +static const char upload_pack_usage[] = "git-upload-pack "; + +static int got_sha1(char *hex, unsigned char *sha1) +{ + if (get_sha1_hex(hex, sha1)) + die("git-upload-pack: expected SHA1 object, got '%s'", hex); + return has_sha1_file(sha1); +} + +static int get_common_commits(void) +{ + static char line[1000]; + unsigned char sha1[20]; + int len; + + for(;;) { + len = packet_read_line(0, line, sizeof(line)); + + if (!len) { + packet_write(1, "NAK\n"); + continue; + } + if (line[len-1] == '\n') + line[--len] = 0; + if (!strncmp(line, "have ", 5)) { + if (got_sha1(line+5, sha1)) { + packet_write(1, "ACK %s\n", sha1_to_hex(sha1)); + break; + } + continue; + } + if (!strcmp(line, "done")) { + packet_write(1, "NAK\n"); + return -1; + } + die("git-upload-pack: expected SHA1 list, got '%s'", line); + } + + for (;;) { + len = packet_read_line(0, line, sizeof(line)); + if (!len) + break; + if (!strncmp(line, "have ", 5)) { + got_sha1(line+5, sha1); + continue; + } + if (!strcmp(line, "done")) + break; + die("git-upload-pack: expected SHA1 list, got '%s'", line); + } + return 0; +} + +static int send_ref(const char *refname, const unsigned char *sha1) +{ + packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname); + return 0; +} + +static int upload_pack(void) +{ + for_each_ref(send_ref); + packet_flush(1); + get_common_commits(); + return 0; +} + +int main(int argc, char **argv) +{ + const char *dir; + if (argc != 2) + usage(upload_pack_usage); + dir = argv[1]; + if (chdir(dir)) + die("git-upload-pack unable to chdir to %s", dir); + chdir(".git"); + if (access("objects", X_OK) || access("refs", X_OK)) + die("git-upload-pack: %s doesn't seem to be a git archive", dir); + setenv("GIT_DIR", ".", 1); + upload_pack(); + return 0; +}