Commit first cut at "git-fetch-pack"
authorLinus Torvalds <torvalds@g5.osdl.org>
Mon, 4 Jul 2005 20:26:53 +0000 (13:26 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Mon, 4 Jul 2005 20:26:53 +0000 (13:26 -0700)
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.

Makefile
fetch-pack.c [new file with mode: 0644]
upload-pack.c [new file with mode: 0644]

index e7a2644..a833a85 100644 (file)
--- 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 (file)
index 0000000..e9035a1
--- /dev/null
@@ -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 (file)
index 0000000..18b8e1a
--- /dev/null
@@ -0,0 +1,86 @@
+#include "cache.h"
+#include "refs.h"
+#include "pkt-line.h"
+
+static const char upload_pack_usage[] = "git-upload-pack <dir>";
+
+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;
+}