[PATCH 0/2] Support for transferring pack files in git-ssh-*

DORMANTno replies

3 messages, 1 author, 2016-06-15 · open the first message on its own page

[PATCH 0/2] Support for transferring pack files in git-ssh-*

From: Daniel Barkalow <hidden>
Date: 2016-06-15 22:42:01

This series allows git-ssh-* to transfer objects packed into pack files in
the case of updating a ref file. It is a proof-of-concept for transferring
pack files in any situation where it's useful.

The general method is that the fetch() method has the option of
additionally getting other objects in addition to the one
specified; objects which aren't needed are specified with
dont_fetch() (when it makes sense to exclude them). In this version, it
only excludes an object when it is the current value of a ref file that is
being updated, but further exclusions are clearly possible.

In the case of git-ssh-*, the target specifies objects to exclude, and the
source responds (asynchronously) with whether or not it knows how to
exclude them (i.e., whether or not it has them). If the target has gotten
an object excluded, it requests a pack file instead of a single object,
and the source provides all objects referenced from the given hash,
excluding those specified for exclusion.

	-Daniel
*This .sig left intentionally blank*

[PATCH 1/2] Specify object not useful to pull

From: Daniel Barkalow <hidden>
Date: 2016-06-15 22:42:01

Add support for the pull common code to specify to a pull implementation
hashes which wouldn't be useful to fetch implicitly. This can be used to
infer (possibly) what hashes would be useful to fetch implicitly, such that
a later call to fetch can also fetch extra stuff.

Signed-off-by: Daniel Barkalow <redacted>

---
commit 9bc0256d2e834e101d7bd3f4867a330c92104929
tree a2b4cfdaac954c46babffd585a61f3faf68969e1
parent d0efc8a71da1855c705fd2074b219bcb158b6dbd
author Daniel Barkalow [off-list ref] 1120411439 -0400
committer Daniel Barkalow <barkalow@silva-tulga.(none)> 1120411439 -0400

Index: http-pull.c
===================================================================
--- f26b700095ec30154fede14638a099f49744981d/http-pull.c  (mode:100644 sha1:1f9d60b9b1d5eed85b24d96c240666bbfc5a22ed)
+++ a2b4cfdaac954c46babffd585a61f3faf68969e1/http-pull.c  (mode:100644 sha1:f252a4b9d5448afa7af8a62176808a631429b9cd)
@@ -139,6 +139,10 @@
         return 0;
 }
 
+void dont_fetch(const unsigned char *sha1)
+{
+}
+
 int main(int argc, char **argv)
 {
 	char *commit_id;
Index: local-pull.c
===================================================================
--- f26b700095ec30154fede14638a099f49744981d/local-pull.c  (mode:100644 sha1:2f06fbee8b840a7ae642f5a22e2cb993687f3470)
+++ a2b4cfdaac954c46babffd585a61f3faf68969e1/local-pull.c  (mode:100644 sha1:270e3a0b8405793cd70e6efa70ec6aa4b1674141)
@@ -73,6 +73,10 @@
 	return -1;
 }
 
+void dont_fetch(const unsigned char *sha1)
+{
+}
+
 int fetch_ref(char *ref, unsigned char *sha1)
 {
 	static int ref_name_start = -1;
Index: pull.c
===================================================================
--- f26b700095ec30154fede14638a099f49744981d/pull.c  (mode:100644 sha1:ed3078e3b27c62c07558fd94f339801cbd685593)
+++ a2b4cfdaac954c46babffd585a61f3faf68969e1/pull.c  (mode:100644 sha1:f7f5a89aef36ffc2436dbd30170e4c8dbb2ba3a3)
@@ -155,6 +155,10 @@
 	unsigned char sha1[20];
 	int fd = -1;
 
+	if (current_ref) {
+		dont_fetch(current_ref);
+	}
+
 	if (write_ref && current_ref) {
 		fd = lock_ref_sha1(write_ref, current_ref);
 		if (fd < 0)
Index: pull.h
===================================================================
--- f26b700095ec30154fede14638a099f49744981d/pull.h  (mode:100644 sha1:e173ae3337c4465da87d849f4e5c9da203fdf01d)
+++ a2b4cfdaac954c46babffd585a61f3faf68969e1/pull.h  (mode:100644 sha1:6a35d39fd69bb884faa2d5e70c79e5c40b3ba436)
@@ -15,6 +15,12 @@
  */
 extern int fetch_ref(char *ref, unsigned char *sha1);
 
+/*
+ * Specify that the given SHA1, and everything it references, need not
+ * be fetched.  To be provided by the particular implementation. 
+ */
+extern void dont_fetch(const unsigned char *sha1);
+
 /* If set, the ref filename to write the target value to. */
 extern const char *write_ref;
 
Index: ssh-pull.c
===================================================================
--- f26b700095ec30154fede14638a099f49744981d/ssh-pull.c  (mode:100644 sha1:87d523899a83d8c0d3c5ff721208ded30c1a38f4)
+++ a2b4cfdaac954c46babffd585a61f3faf68969e1/ssh-pull.c  (mode:100644 sha1:362318071333420a7cf2450ada7269a94ec2cc7c)
@@ -53,6 +53,10 @@
 	return 0;
 }
 
+void dont_fetch(const unsigned char *sha1)
+{
+}
+
 int main(int argc, char **argv)
 {
 	char *commit_id;

[PATCH 2/2] Pull well-bounded pack files over ssh

From: Daniel Barkalow <hidden>
Date: 2016-06-15 22:42:01

Initial version of support for transferring pack files over ssh. This works
by sending the source hashes that the target already has, and checking (in
bulk) whether the source has them as well. If the source has some hash that
the target has, the target will request a pack for the fetch, instead of
requesting individual objects, and the source will provide a pack that has
the requested object and all references, excluding those mentioned previously
and any they refer to.

Signed-off-by: Daniel Barkalow <redacted>

---
commit 2045e6098dfa0f8760b6a4a65227a6ea51de990d
tree 3d3e671c0ac57ea66434768c2b3432352b7c20ae
parent a3d68b56cd5efaa06a7493aca61febb4f376fb49
author Daniel Barkalow [off-list ref] 1120501001 -0400
committer Daniel Barkalow <barkalow@silva-tulga.(none)> 1120501001 -0400

Index: ssh-pull.c
===================================================================
--- 8c3f58421885a9c1472f141bb32b668c2e65a5fc/ssh-pull.c  (mode:100644 sha1:0f2c3b5b9db324e20eb6ed0c52cc884b86349a08)
+++ 3d3e671c0ac57ea66434768c2b3432352b7c20ae/ssh-pull.c  (mode:100644 sha1:f792135e7afba6a8d68946a7f14aa339c4fc02e7)
@@ -4,11 +4,86 @@
 #include "pull.h"
 #include "refs.h"
 
+#include "pkt-line.h"
+#include <sys/wait.h>
+
+static const char *unpacker = "git-unpack-objects";
+
 static int fd_in;
 static int fd_out;
 
 static unsigned char remote_version = 0;
-static unsigned char local_version = 1;
+static unsigned char local_version = 2;
+
+static int sent_excludes = 0;
+static int accepted_excludes = 0;
+
+static int write_pack_from_fd(int fd)
+{
+	pid_t pid = fork();
+
+	if (pid < 0)
+		die("unpack fork failed");
+	if (!pid) {
+		execlp(unpacker, unpacker, NULL);
+		die("unpack execute failed");
+	}
+
+	for (;;) {
+		int status, code;
+		int retval = waitpid(pid, &status, 0);
+
+		if (retval < 0) {
+			if (errno == EINTR)
+				continue;
+			die("waitpid failed (%s)", strerror(retval));
+		}
+		if (retval != pid)
+			die("waitpid is confused");
+		if (WIFSIGNALED(status))
+			die("%s died of signal %d", unpacker, WTERMSIG(status));
+		if (!WIFEXITED(status))
+			die("%s died out of really strange complications", unpacker);
+		code = WEXITSTATUS(status);
+		if (code)
+			die("%s exited with error code %d", unpacker, code);
+		return 0;
+	}
+}
+
+static int handle_deferred_reads()
+{
+	signed char remote;
+	while (sent_excludes) {
+		if (read(fd_in, &remote, 1) < 1)
+			return -1;
+		if (!remote) {
+			if (get_verbosely) {
+				fprintf(stderr, "Exclude accepted\n");
+			}
+			accepted_excludes++;
+		}
+		sent_excludes--;
+	}
+	return 0;
+}
+
+static int fetch_pack(unsigned char *sha1)
+{
+	int ret;
+	char type = 'p';
+	signed char remote;
+	write(fd_out, &type, 1);
+	write(fd_out, sha1, 20);
+	if (read(fd_in, &remote, 1) < 1)
+		return -1;
+	if (remote < 0)
+		return remote;
+	if (get_verbosely)
+		fprintf(stderr, "Getting pack\n");
+	ret = write_pack_from_fd(fd_in);
+	return ret;
+}
 
 int fetch(unsigned char *sha1)
 {
@@ -17,6 +92,10 @@
 	char type = 'o';
 	if (has_sha1_file(sha1))
 		return 0;
+	if (handle_deferred_reads())
+		return -1;
+	if (accepted_excludes)
+		return fetch_pack(sha1);
 	write(fd_out, &type, 1);
 	write(fd_out, sha1, 20);
 	if (read(fd_in, &remote, 1) < 1)
@@ -44,6 +123,8 @@
 {
 	signed char remote;
 	char type = 'r';
+	if (handle_deferred_reads())
+		return -1;
 	write(fd_out, &type, 1);
 	write(fd_out, ref, strlen(ref) + 1);
 	read(fd_in, &remote, 1);
@@ -55,6 +136,15 @@
 
 void dont_fetch(const unsigned char *sha1)
 {
+	char type = 'd';
+	if (remote_version < 2)
+		return;
+	write(fd_out, &type, 1);
+	write(fd_out, sha1, 20);
+	sent_excludes++;
+	if (get_verbosely) {
+		fprintf(stderr, "Won't get %s\n", sha1_to_hex(sha1));
+	}
 }
 
 int main(int argc, char **argv)
@@ -76,7 +166,12 @@
 		} else if (argv[arg][1] == 'v') {
 			get_verbosely = 1;
 		} else if (argv[arg][1] == 'w') {
+			unsigned char *current = malloc(20);
 			write_ref = argv[arg + 1];
+			if (get_ref_sha1(write_ref, current))
+				free(current);
+			else
+				current_ref = current;
 			arg++;
 		}
 		arg++;
Index: ssh-push.c
===================================================================
--- 8c3f58421885a9c1472f141bb32b668c2e65a5fc/ssh-push.c  (mode:100644 sha1:090d6f9f8fbde2d736ac5bf563415b0fa402b5aa)
+++ 3d3e671c0ac57ea66434768c2b3432352b7c20ae/ssh-push.c  (mode:100644 sha1:10390948efacfa06f4f6fc6b2f3631cec6fcb876)
@@ -4,16 +4,88 @@
 
 #include <string.h>
 
-static unsigned char local_version = 1;
+static unsigned char local_version = 2;
 static unsigned char remote_version = 0;
 
-static int serve_object(int fd_in, int fd_out) {
+static void exec_pack_objects(void)
+{
+	static char *args[] = {
+		"git-pack-objects",
+		"--stdout",
+		NULL
+	};
+	execvp("git-pack-objects", args);
+	die("git-pack-objects exec failed (%s)", strerror(errno));
+}
+
+static void exec_rev_list(unsigned char *incl, unsigned char *excl)
+{
+	static char *args[1000];
+	char *buf = malloc(100);
+	int i = 0;
+
+	args[i++] = "git-rev-list";	/* 0 */
+	args[i++] = "--objects";	/* 1 */
+
+	args[i++] = buf;
+	snprintf(buf, 50, "^%s", sha1_to_hex(excl));
+	buf += 50;
+	args[i++] = buf;
+	snprintf(buf, 50, "%s", sha1_to_hex(incl));
+
+	args[i] = NULL;
+	execvp("git-rev-list", args);
+	die("git-rev-list exec failed (%s)", strerror(errno));
+}
+
+static void rev_list(int fd, unsigned char *incl, unsigned char *excl)
+{
+	int pipe_fd[2];
+	pid_t pack_objects_pid;
+
+	if (pipe(pipe_fd) < 0)
+		die("rev-list setup: pipe failed");
+	pack_objects_pid = fork();
+	if (!pack_objects_pid) {
+		dup2(pipe_fd[0], 0);
+		dup2(fd, 1);
+		close(pipe_fd[0]);
+		close(pipe_fd[1]);
+		close(fd);
+		exec_pack_objects();
+		die("pack-objects setup failed");
+	}
+	if (pack_objects_pid < 0)
+		die("pack-objects fork failed");
+	dup2(pipe_fd[1], 1);
+	close(pipe_fd[0]);
+	close(pipe_fd[1]);
+	close(fd);
+	exec_rev_list(incl, excl);
+}
+
+static int pack_objects(int fd, unsigned char *incl, unsigned char *excl)
+{
+	pid_t rev_list_pid;
+
+	rev_list_pid = fork();
+	if (!rev_list_pid) {
+		rev_list(fd, incl, excl);
+		die("rev-list setup failed");
+	}
+	if (rev_list_pid < 0)
+		die("rev-list fork failed");
+	/*
+	 * We don't wait for the rev-list pipeline in the parent:
+	 * we end up waiting for the other end instead
+	 */
+	return 0;
+}
+
+static int read_sha1(int fd_in, unsigned char *sha1)
+{
 	ssize_t size;
 	int posn = 0;
-	unsigned char sha1[20];
-	unsigned long objsize;
-	void *buf;
-	signed char remote;
 	do {
 		size = read(fd_in, sha1 + posn, 20 - posn);
 		if (size < 0) {
@@ -24,6 +96,18 @@
 			return -1;
 		posn += size;
 	} while (posn < 20);
+	return 0;
+}
+
+static int serve_object(int fd_in, int fd_out) {
+	unsigned char sha1[20];
+	unsigned long objsize;
+	void *buf;
+	signed char remote;
+	int posn;
+	ssize_t size;
+	if (read_sha1(fd_in, sha1))
+		return -1;
 	
 	/* fprintf(stderr, "Serving %s\n", sha1_to_hex(sha1)); */
 	remote = 0;
@@ -85,6 +169,48 @@
         return 0;
 }
 
+static unsigned char *excluded = NULL;
+
+static int serve_exclude(int fd_in, int fd_out)
+{
+	unsigned char sha1[20];
+	signed char remote;
+	if (read_sha1(fd_in, sha1))
+		return -1;
+
+	if (has_sha1_file(sha1)) {
+		remote = 0;
+		if (!excluded) {
+			excluded = malloc(20);
+			memcpy(excluded, sha1, 20);
+		}
+	} else {
+		remote = -1;
+	}
+
+	write(fd_out, &remote, 1);
+	
+	return 0;
+}
+
+static int serve_pack(int fd_in, int fd_out)
+{
+	unsigned char sha1[20];
+	signed char remote = 0;
+	if (read_sha1(fd_in, sha1))
+		return -1;
+
+	if (!has_sha1_file(sha1))
+		remote = -1;
+
+	write(fd_out, &remote, 1);
+
+	if (remote)
+		return 0;
+	if (pack_objects(fd_out, sha1, excluded))
+		return -1;
+	return 0;
+}
 
 static void service(int fd_in, int fd_out) {
 	char type;
@@ -102,6 +228,10 @@
 			return;
 		if (type == 'r' && serve_ref(fd_in, fd_out))
 			return;
+		if (type == 'd' && serve_exclude(fd_in, fd_out))
+			return;
+		if (type == 'p' && serve_pack(fd_in, fd_out))
+			return;
 	} while (1);
 }
 
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help