Git development
 help / color / mirror / Atom feed
* [PATCH 4/4] Modularize commit-walker
From: Daniel Barkalow @ 2007-07-22 22:09 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

This turns the extern functions to be provided by the backend into a
struct of pointers, renames the functions to be more
namespace-friendly, and updates http-fetch to this interface. It
removes the unused include from http-push.c. It makes git-http-fetch a
builtin (with the implementation a separate file, accessible
directly).

Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
---
 Makefile             |   17 +-
 builtin-http-fetch.c |   77 ++++
 builtin.h            |    1 +
 fetch.c              |  317 ---------------
 fetch.h              |   54 ---
 git.c                |    3 +
 http-fetch.c         | 1059 --------------------------------------------------
 http-push.c          |    1 -
 http-walker.c        | 1035 ++++++++++++++++++++++++++++++++++++++++++++++++
 walker.c             |  318 +++++++++++++++
 walker.h             |   37 ++
 11 files changed, 1480 insertions(+), 1439 deletions(-)
 create mode 100644 builtin-http-fetch.c
 delete mode 100644 fetch.c
 delete mode 100644 fetch.h
 delete mode 100644 http-fetch.c
 create mode 100644 http-walker.c
 create mode 100644 walker.c
 create mode 100644 walker.h

diff --git a/Makefile b/Makefile
index 705dde5..5f880c6 100644
--- a/Makefile
+++ b/Makefile
@@ -508,7 +508,9 @@ else
 	CC_LD_DYNPATH = -R
 endif
 
-ifndef NO_CURL
+ifdef NO_CURL
+	BASIC_CFLAGS += -DNO_CURL
+else
 	ifdef CURLDIR
 		# Try "-Wl,-rpath=$(CURLDIR)/lib" in such a case.
 		BASIC_CFLAGS += -I$(CURLDIR)/include
@@ -516,7 +518,9 @@ ifndef NO_CURL
 	else
 		CURL_LIBCURL = -lcurl
 	endif
-	PROGRAMS += git-http-fetch$X
+	BUILTIN_OBJS += builtin-http-fetch.o
+	EXTLIBS += $(CURL_LIBCURL)
+	LIB_OBJS += http.o walker.o http-walker.o
 	curl_check := $(shell (echo 070908; curl-config --vernum) | sort -r | sed -ne 2p)
 	ifeq "$(curl_check)" "070908"
 		ifndef NO_EXPAT
@@ -874,7 +878,7 @@ http.o: http.c GIT-CFLAGS
 	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DGIT_USER_AGENT='"git/$(GIT_VERSION)"' $<
 
 ifdef NO_EXPAT
-http-fetch.o: http-fetch.c http.h GIT-CFLAGS
+http-walker.o: http-walker.c http.h GIT-CFLAGS
 	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DNO_EXPAT $<
 endif
 
@@ -883,16 +887,13 @@ git-%$X: %.o $(GITLIBS)
 
 git-imap-send$X: imap-send.o $(LIB_FILE)
 
-http.o http-fetch.o http-push.o: http.h
-git-http-fetch$X: fetch.o http.o http-fetch.o $(GITLIBS)
-	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
-		$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
+http.o http-walker.o http-push.o: http.h
 
 git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 		$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 
-$(LIB_OBJS) $(BUILTIN_OBJS) fetch.o: $(LIB_H)
+$(LIB_OBJS) $(BUILTIN_OBJS) walker.o: $(LIB_H)
 $(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
 $(DIFF_OBJS): diffcore.h
 
diff --git a/builtin-http-fetch.c b/builtin-http-fetch.c
new file mode 100644
index 0000000..4a50dbd
--- /dev/null
+++ b/builtin-http-fetch.c
@@ -0,0 +1,77 @@
+#include "cache.h"
+#include "walker.h"
+
+int cmd_http_fetch(int argc, const char **argv, const char *prefix)
+{
+	struct walker *walker;
+	int commits_on_stdin = 0;
+	int commits;
+	const char **write_ref = NULL;
+	char **commit_id;
+	const char *url;
+	int arg = 1;
+	int rc = 0;
+	int get_tree = 0;
+	int get_history = 0;
+	int get_all = 0;
+	int get_verbosely = 0;
+	int get_recover = 0;
+
+	git_config(git_default_config);
+
+	while (arg < argc && argv[arg][0] == '-') {
+		if (argv[arg][1] == 't') {
+			get_tree = 1;
+		} else if (argv[arg][1] == 'c') {
+			get_history = 1;
+		} else if (argv[arg][1] == 'a') {
+			get_all = 1;
+			get_tree = 1;
+			get_history = 1;
+		} else if (argv[arg][1] == 'v') {
+			get_verbosely = 1;
+		} else if (argv[arg][1] == 'w') {
+			write_ref = &argv[arg + 1];
+			arg++;
+		} else if (!strcmp(argv[arg], "--recover")) {
+			get_recover = 1;
+		} else if (!strcmp(argv[arg], "--stdin")) {
+			commits_on_stdin = 1;
+		}
+		arg++;
+	}
+	if (argc < arg + 2 - commits_on_stdin) {
+		usage("git-http-fetch [-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin] commit-id url");
+		return 1;
+	}
+	if (commits_on_stdin) {
+		commits = walker_targets_stdin(&commit_id, &write_ref);
+	} else {
+		commit_id = (char **) &argv[arg++];
+		commits = 1;
+	}
+	url = argv[arg];
+
+	walker = get_http_walker(url);
+	walker->get_tree = get_tree;
+	walker->get_history = get_history;
+	walker->get_all = get_all;
+	walker->get_verbosely = get_verbosely;
+	walker->get_recover = get_recover;
+
+	rc = walker_fetch(walker, commits, commit_id, write_ref, url);
+
+	if (commits_on_stdin)
+		walker_targets_free(commits, commit_id, write_ref);
+
+	if (walker->corrupt_object_found) {
+		fprintf(stderr,
+"Some loose object were found to be corrupt, but they might be just\n"
+"a false '404 Not Found' error message sent with incorrect HTTP\n"
+"status code.  Suggest running git-fsck.\n");
+	}
+
+	walker_free(walker);
+
+	return rc;
+}
diff --git a/builtin.h b/builtin.h
index 661a92f..bd06af7 100644
--- a/builtin.h
+++ b/builtin.h
@@ -40,6 +40,7 @@ extern int cmd_gc(int argc, const char **argv, const char *prefix);
 extern int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix);
 extern int cmd_grep(int argc, const char **argv, const char *prefix);
 extern int cmd_help(int argc, const char **argv, const char *prefix);
+extern int cmd_http_fetch(int argc, const char **argv, const char *prefix);
 extern int cmd_init_db(int argc, const char **argv, const char *prefix);
 extern int cmd_log(int argc, const char **argv, const char *prefix);
 extern int cmd_log_reflog(int argc, const char **argv, const char *prefix);
diff --git a/fetch.c b/fetch.c
deleted file mode 100644
index 811be87..0000000
--- a/fetch.c
+++ /dev/null
@@ -1,317 +0,0 @@
-#include "cache.h"
-#include "fetch.h"
-#include "commit.h"
-#include "tree.h"
-#include "tree-walk.h"
-#include "tag.h"
-#include "blob.h"
-#include "refs.h"
-#include "strbuf.h"
-
-int get_tree = 0;
-int get_history = 0;
-int get_all = 0;
-int get_verbosely = 0;
-int get_recover = 0;
-static unsigned char current_commit_sha1[20];
-
-void pull_say(const char *fmt, const char *hex)
-{
-	if (get_verbosely)
-		fprintf(stderr, fmt, hex);
-}
-
-static void report_missing(const struct object *obj)
-{
-	char missing_hex[41];
-	strcpy(missing_hex, sha1_to_hex(obj->sha1));;
-	fprintf(stderr, "Cannot obtain needed %s %s\n",
-		obj->type ? typename(obj->type): "object", missing_hex);
-	if (!is_null_sha1(current_commit_sha1))
-		fprintf(stderr, "while processing commit %s.\n",
-			sha1_to_hex(current_commit_sha1));
-}
-
-static int process(struct object *obj);
-
-static int process_tree(struct tree *tree)
-{
-	struct tree_desc desc;
-	struct name_entry entry;
-
-	if (parse_tree(tree))
-		return -1;
-
-	init_tree_desc(&desc, tree->buffer, tree->size);
-	while (tree_entry(&desc, &entry)) {
-		struct object *obj = NULL;
-
-		/* submodule commits are not stored in the superproject */
-		if (S_ISGITLINK(entry.mode))
-			continue;
-		if (S_ISDIR(entry.mode)) {
-			struct tree *tree = lookup_tree(entry.sha1);
-			if (tree)
-				obj = &tree->object;
-		}
-		else {
-			struct blob *blob = lookup_blob(entry.sha1);
-			if (blob)
-				obj = &blob->object;
-		}
-		if (!obj || process(obj))
-			return -1;
-	}
-	free(tree->buffer);
-	tree->buffer = NULL;
-	tree->size = 0;
-	return 0;
-}
-
-#define COMPLETE	(1U << 0)
-#define SEEN		(1U << 1)
-#define TO_SCAN		(1U << 2)
-
-static struct commit_list *complete = NULL;
-
-static int process_commit(struct commit *commit)
-{
-	if (parse_commit(commit))
-		return -1;
-
-	while (complete && complete->item->date >= commit->date) {
-		pop_most_recent_commit(&complete, COMPLETE);
-	}
-
-	if (commit->object.flags & COMPLETE)
-		return 0;
-
-	hashcpy(current_commit_sha1, commit->object.sha1);
-
-	pull_say("walk %s\n", sha1_to_hex(commit->object.sha1));
-
-	if (get_tree) {
-		if (process(&commit->tree->object))
-			return -1;
-		if (!get_all)
-			get_tree = 0;
-	}
-	if (get_history) {
-		struct commit_list *parents = commit->parents;
-		for (; parents; parents = parents->next) {
-			if (process(&parents->item->object))
-				return -1;
-		}
-	}
-	return 0;
-}
-
-static int process_tag(struct tag *tag)
-{
-	if (parse_tag(tag))
-		return -1;
-	return process(tag->tagged);
-}
-
-static struct object_list *process_queue = NULL;
-static struct object_list **process_queue_end = &process_queue;
-
-static int process_object(struct object *obj)
-{
-	if (obj->type == OBJ_COMMIT) {
-		if (process_commit((struct commit *)obj))
-			return -1;
-		return 0;
-	}
-	if (obj->type == OBJ_TREE) {
-		if (process_tree((struct tree *)obj))
-			return -1;
-		return 0;
-	}
-	if (obj->type == OBJ_BLOB) {
-		return 0;
-	}
-	if (obj->type == OBJ_TAG) {
-		if (process_tag((struct tag *)obj))
-			return -1;
-		return 0;
-	}
-	return error("Unable to determine requirements "
-		     "of type %s for %s",
-		     typename(obj->type), sha1_to_hex(obj->sha1));
-}
-
-static int process(struct object *obj)
-{
-	if (obj->flags & SEEN)
-		return 0;
-	obj->flags |= SEEN;
-
-	if (has_sha1_file(obj->sha1)) {
-		/* We already have it, so we should scan it now. */
-		obj->flags |= TO_SCAN;
-	}
-	else {
-		if (obj->flags & COMPLETE)
-			return 0;
-		prefetch(obj->sha1);
-	}
-
-	object_list_insert(obj, process_queue_end);
-	process_queue_end = &(*process_queue_end)->next;
-	return 0;
-}
-
-static int loop(void)
-{
-	struct object_list *elem;
-
-	while (process_queue) {
-		struct object *obj = process_queue->item;
-		elem = process_queue;
-		process_queue = elem->next;
-		free(elem);
-		if (!process_queue)
-			process_queue_end = &process_queue;
-
-		/* If we are not scanning this object, we placed it in
-		 * the queue because we needed to fetch it first.
-		 */
-		if (! (obj->flags & TO_SCAN)) {
-			if (fetch(obj->sha1)) {
-				report_missing(obj);
-				return -1;
-			}
-		}
-		if (!obj->type)
-			parse_object(obj->sha1);
-		if (process_object(obj))
-			return -1;
-	}
-	return 0;
-}
-
-static int interpret_target(char *target, unsigned char *sha1)
-{
-	if (!get_sha1_hex(target, sha1))
-		return 0;
-	if (!check_ref_format(target)) {
-		if (!fetch_ref(target, sha1)) {
-			return 0;
-		}
-	}
-	return -1;
-}
-
-static int mark_complete(const char *path, const unsigned char *sha1, int flag, void *cb_data)
-{
-	struct commit *commit = lookup_commit_reference_gently(sha1, 1);
-	if (commit) {
-		commit->object.flags |= COMPLETE;
-		insert_by_date(commit, &complete);
-	}
-	return 0;
-}
-
-int pull_targets_stdin(char ***target, const char ***write_ref)
-{
-	int targets = 0, targets_alloc = 0;
-	struct strbuf buf;
-	*target = NULL; *write_ref = NULL;
-	strbuf_init(&buf);
-	while (1) {
-		char *rf_one = NULL;
-		char *tg_one;
-
-		read_line(&buf, stdin, '\n');
-		if (buf.eof)
-			break;
-		tg_one = buf.buf;
-		rf_one = strchr(tg_one, '\t');
-		if (rf_one)
-			*rf_one++ = 0;
-
-		if (targets >= targets_alloc) {
-			targets_alloc = targets_alloc ? targets_alloc * 2 : 64;
-			*target = xrealloc(*target, targets_alloc * sizeof(**target));
-			*write_ref = xrealloc(*write_ref, targets_alloc * sizeof(**write_ref));
-		}
-		(*target)[targets] = xstrdup(tg_one);
-		(*write_ref)[targets] = rf_one ? xstrdup(rf_one) : NULL;
-		targets++;
-	}
-	return targets;
-}
-
-void pull_targets_free(int targets, char **target, const char **write_ref)
-{
-	while (targets--) {
-		free(target[targets]);
-		if (write_ref && write_ref[targets])
-			free((char *) write_ref[targets]);
-	}
-}
-
-int pull(int targets, char **target, const char **write_ref,
-         const char *write_ref_log_details)
-{
-	struct ref_lock **lock = xcalloc(targets, sizeof(struct ref_lock *));
-	unsigned char *sha1 = xmalloc(targets * 20);
-	char *msg;
-	int ret;
-	int i;
-
-	save_commit_buffer = 0;
-	track_object_refs = 0;
-
-	for (i = 0; i < targets; i++) {
-		if (!write_ref || !write_ref[i])
-			continue;
-
-		lock[i] = lock_ref_sha1(write_ref[i], NULL);
-		if (!lock[i]) {
-			error("Can't lock ref %s", write_ref[i]);
-			goto unlock_and_fail;
-		}
-	}
-
-	if (!get_recover)
-		for_each_ref(mark_complete, NULL);
-
-	for (i = 0; i < targets; i++) {
-		if (interpret_target(target[i], &sha1[20 * i])) {
-			error("Could not interpret %s as something to pull", target[i]);
-			goto unlock_and_fail;
-		}
-		if (process(lookup_unknown_object(&sha1[20 * i])))
-			goto unlock_and_fail;
-	}
-
-	if (loop())
-		goto unlock_and_fail;
-
-	if (write_ref_log_details) {
-		msg = xmalloc(strlen(write_ref_log_details) + 12);
-		sprintf(msg, "fetch from %s", write_ref_log_details);
-	} else {
-		msg = NULL;
-	}
-	for (i = 0; i < targets; i++) {
-		if (!write_ref || !write_ref[i])
-			continue;
-		ret = write_ref_sha1(lock[i], &sha1[20 * i], msg ? msg : "fetch (unknown)");
-		lock[i] = NULL;
-		if (ret)
-			goto unlock_and_fail;
-	}
-	free(msg);
-
-	return 0;
-
-
-unlock_and_fail:
-	for (i = 0; i < targets; i++)
-		if (lock[i])
-			unlock_ref(lock[i]);
-	return -1;
-}
diff --git a/fetch.h b/fetch.h
deleted file mode 100644
index be48c6f..0000000
--- a/fetch.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef PULL_H
-#define PULL_H
-
-/*
- * Fetch object given SHA1 from the remote, and store it locally under
- * GIT_OBJECT_DIRECTORY.  Return 0 on success, -1 on failure.  To be
- * provided by the particular implementation.
- */
-extern int fetch(unsigned char *sha1);
-
-/*
- * Fetch the specified object and store it locally; fetch() will be
- * called later to determine success. To be provided by the particular
- * implementation.
- */
-extern void prefetch(unsigned char *sha1);
-
-/*
- * Fetch ref (relative to $GIT_DIR/refs) from the remote, and store
- * the 20-byte SHA1 in sha1.  Return 0 on success, -1 on failure.  To
- * be provided by the particular implementation.
- */
-extern int fetch_ref(char *ref, unsigned char *sha1);
-
-/* Set to fetch the target tree. */
-extern int get_tree;
-
-/* Set to fetch the commit history. */
-extern int get_history;
-
-/* Set to fetch the trees in the commit history. */
-extern int get_all;
-
-/* Set to be verbose */
-extern int get_verbosely;
-
-/* Set to check on all reachable objects. */
-extern int get_recover;
-
-/* Report what we got under get_verbosely */
-extern void pull_say(const char *, const char *);
-
-/* Load pull targets from stdin */
-extern int pull_targets_stdin(char ***target, const char ***write_ref);
-
-/* Free up loaded targets */
-extern void pull_targets_free(int targets, char **target, const char **write_ref);
-
-/* If write_ref is set, the ref filename to write the target value to. */
-/* If write_ref_log_details is set, additional text will appear in the ref log. */
-extern int pull(int targets, char **target, const char **write_ref,
-		const char *write_ref_log_details);
-
-#endif /* PULL_H */
diff --git a/git.c b/git.c
index b949cbb..4c2d6da 100644
--- a/git.c
+++ b/git.c
@@ -317,6 +317,9 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "get-tar-commit-id", cmd_get_tar_commit_id },
 		{ "grep", cmd_grep, RUN_SETUP | USE_PAGER },
 		{ "help", cmd_help },
+#ifndef NO_CURL
+		{ "http-fetch", cmd_http_fetch, RUN_SETUP },
+#endif
 		{ "init", cmd_init_db },
 		{ "init-db", cmd_init_db },
 		{ "log", cmd_log, RUN_SETUP | USE_PAGER },
diff --git a/http-fetch.c b/http-fetch.c
deleted file mode 100644
index 7786110..0000000
--- a/http-fetch.c
+++ /dev/null
@@ -1,1059 +0,0 @@
-#include "cache.h"
-#include "commit.h"
-#include "pack.h"
-#include "fetch.h"
-#include "http.h"
-
-#define PREV_BUF_SIZE 4096
-#define RANGE_HEADER_SIZE 30
-
-static int commits_on_stdin;
-
-static int got_alternates = -1;
-static int corrupt_object_found;
-
-static struct curl_slist *no_pragma_header;
-
-struct alt_base
-{
-	char *base;
-	int got_indices;
-	struct packed_git *packs;
-	struct alt_base *next;
-};
-
-static struct alt_base *alt;
-
-enum object_request_state {
-	WAITING,
-	ABORTED,
-	ACTIVE,
-	COMPLETE,
-};
-
-struct object_request
-{
-	unsigned char sha1[20];
-	struct alt_base *repo;
-	char *url;
-	char filename[PATH_MAX];
-	char tmpfile[PATH_MAX];
-	int local;
-	enum object_request_state state;
-	CURLcode curl_result;
-	char errorstr[CURL_ERROR_SIZE];
-	long http_code;
-	unsigned char real_sha1[20];
-	SHA_CTX c;
-	z_stream stream;
-	int zret;
-	int rename;
-	struct active_request_slot *slot;
-	struct object_request *next;
-};
-
-struct alternates_request {
-	const char *base;
-	char *url;
-	struct buffer *buffer;
-	struct active_request_slot *slot;
-	int http_specific;
-};
-
-static struct object_request *object_queue_head;
-
-static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
-			       void *data)
-{
-	unsigned char expn[4096];
-	size_t size = eltsize * nmemb;
-	int posn = 0;
-	struct object_request *obj_req = (struct object_request *)data;
-	do {
-		ssize_t retval = xwrite(obj_req->local,
-				       (char *) ptr + posn, size - posn);
-		if (retval < 0)
-			return posn;
-		posn += retval;
-	} while (posn < size);
-
-	obj_req->stream.avail_in = size;
-	obj_req->stream.next_in = ptr;
-	do {
-		obj_req->stream.next_out = expn;
-		obj_req->stream.avail_out = sizeof(expn);
-		obj_req->zret = inflate(&obj_req->stream, Z_SYNC_FLUSH);
-		SHA1_Update(&obj_req->c, expn,
-			    sizeof(expn) - obj_req->stream.avail_out);
-	} while (obj_req->stream.avail_in && obj_req->zret == Z_OK);
-	data_received++;
-	return size;
-}
-
-static int missing__target(int code, int result)
-{
-	return	/* file:// URL -- do we ever use one??? */
-		(result == CURLE_FILE_COULDNT_READ_FILE) ||
-		/* http:// and https:// URL */
-		(code == 404 && result == CURLE_HTTP_RETURNED_ERROR) ||
-		/* ftp:// URL */
-		(code == 550 && result == CURLE_FTP_COULDNT_RETR_FILE)
-		;
-}
-
-#define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
-
-static void fetch_alternates(const char *base);
-
-static void process_object_response(void *callback_data);
-
-static void start_object_request(struct object_request *obj_req)
-{
-	char *hex = sha1_to_hex(obj_req->sha1);
-	char prevfile[PATH_MAX];
-	char *url;
-	char *posn;
-	int prevlocal;
-	unsigned char prev_buf[PREV_BUF_SIZE];
-	ssize_t prev_read = 0;
-	long prev_posn = 0;
-	char range[RANGE_HEADER_SIZE];
-	struct curl_slist *range_header = NULL;
-	struct active_request_slot *slot;
-
-	snprintf(prevfile, sizeof(prevfile), "%s.prev", obj_req->filename);
-	unlink(prevfile);
-	rename(obj_req->tmpfile, prevfile);
-	unlink(obj_req->tmpfile);
-
-	if (obj_req->local != -1)
-		error("fd leakage in start: %d", obj_req->local);
-	obj_req->local = open(obj_req->tmpfile,
-			      O_WRONLY | O_CREAT | O_EXCL, 0666);
-	/* This could have failed due to the "lazy directory creation";
-	 * try to mkdir the last path component.
-	 */
-	if (obj_req->local < 0 && errno == ENOENT) {
-		char *dir = strrchr(obj_req->tmpfile, '/');
-		if (dir) {
-			*dir = 0;
-			mkdir(obj_req->tmpfile, 0777);
-			*dir = '/';
-		}
-		obj_req->local = open(obj_req->tmpfile,
-				      O_WRONLY | O_CREAT | O_EXCL, 0666);
-	}
-
-	if (obj_req->local < 0) {
-		obj_req->state = ABORTED;
-		error("Couldn't create temporary file %s for %s: %s",
-		      obj_req->tmpfile, obj_req->filename, strerror(errno));
-		return;
-	}
-
-	memset(&obj_req->stream, 0, sizeof(obj_req->stream));
-
-	inflateInit(&obj_req->stream);
-
-	SHA1_Init(&obj_req->c);
-
-	url = xmalloc(strlen(obj_req->repo->base) + 51);
-	obj_req->url = xmalloc(strlen(obj_req->repo->base) + 51);
-	strcpy(url, obj_req->repo->base);
-	posn = url + strlen(obj_req->repo->base);
-	strcpy(posn, "/objects/");
-	posn += 9;
-	memcpy(posn, hex, 2);
-	posn += 2;
-	*(posn++) = '/';
-	strcpy(posn, hex + 2);
-	strcpy(obj_req->url, url);
-
-	/* If a previous temp file is present, process what was already
-	   fetched. */
-	prevlocal = open(prevfile, O_RDONLY);
-	if (prevlocal != -1) {
-		do {
-			prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE);
-			if (prev_read>0) {
-				if (fwrite_sha1_file(prev_buf,
-						     1,
-						     prev_read,
-						     obj_req) == prev_read) {
-					prev_posn += prev_read;
-				} else {
-					prev_read = -1;
-				}
-			}
-		} while (prev_read > 0);
-		close(prevlocal);
-	}
-	unlink(prevfile);
-
-	/* Reset inflate/SHA1 if there was an error reading the previous temp
-	   file; also rewind to the beginning of the local file. */
-	if (prev_read == -1) {
-		memset(&obj_req->stream, 0, sizeof(obj_req->stream));
-		inflateInit(&obj_req->stream);
-		SHA1_Init(&obj_req->c);
-		if (prev_posn>0) {
-			prev_posn = 0;
-			lseek(obj_req->local, 0, SEEK_SET);
-			ftruncate(obj_req->local, 0);
-		}
-	}
-
-	slot = get_active_slot();
-	slot->callback_func = process_object_response;
-	slot->callback_data = obj_req;
-	obj_req->slot = slot;
-
-	curl_easy_setopt(slot->curl, CURLOPT_FILE, obj_req);
-	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
-	curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, obj_req->errorstr);
-	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
-	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
-
-	/* If we have successfully processed data from a previous fetch
-	   attempt, only fetch the data we don't already have. */
-	if (prev_posn>0) {
-		if (get_verbosely)
-			fprintf(stderr,
-				"Resuming fetch of object %s at byte %ld\n",
-				hex, prev_posn);
-		sprintf(range, "Range: bytes=%ld-", prev_posn);
-		range_header = curl_slist_append(range_header, range);
-		curl_easy_setopt(slot->curl,
-				 CURLOPT_HTTPHEADER, range_header);
-	}
-
-	/* Try to get the request started, abort the request on error */
-	obj_req->state = ACTIVE;
-	if (!start_active_slot(slot)) {
-		obj_req->state = ABORTED;
-		obj_req->slot = NULL;
-		close(obj_req->local); obj_req->local = -1;
-		free(obj_req->url);
-		return;
-	}
-}
-
-static void finish_object_request(struct object_request *obj_req)
-{
-	struct stat st;
-
-	fchmod(obj_req->local, 0444);
-	close(obj_req->local); obj_req->local = -1;
-
-	if (obj_req->http_code == 416) {
-		fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n");
-	} else if (obj_req->curl_result != CURLE_OK) {
-		if (stat(obj_req->tmpfile, &st) == 0)
-			if (st.st_size == 0)
-				unlink(obj_req->tmpfile);
-		return;
-	}
-
-	inflateEnd(&obj_req->stream);
-	SHA1_Final(obj_req->real_sha1, &obj_req->c);
-	if (obj_req->zret != Z_STREAM_END) {
-		unlink(obj_req->tmpfile);
-		return;
-	}
-	if (hashcmp(obj_req->sha1, obj_req->real_sha1)) {
-		unlink(obj_req->tmpfile);
-		return;
-	}
-	obj_req->rename =
-		move_temp_to_file(obj_req->tmpfile, obj_req->filename);
-
-	if (obj_req->rename == 0)
-		pull_say("got %s\n", sha1_to_hex(obj_req->sha1));
-}
-
-static void process_object_response(void *callback_data)
-{
-	struct object_request *obj_req =
-		(struct object_request *)callback_data;
-
-	obj_req->curl_result = obj_req->slot->curl_result;
-	obj_req->http_code = obj_req->slot->http_code;
-	obj_req->slot = NULL;
-	obj_req->state = COMPLETE;
-
-	/* Use alternates if necessary */
-	if (missing_target(obj_req)) {
-		fetch_alternates(alt->base);
-		if (obj_req->repo->next != NULL) {
-			obj_req->repo =
-				obj_req->repo->next;
-			close(obj_req->local);
-			obj_req->local = -1;
-			start_object_request(obj_req);
-			return;
-		}
-	}
-
-	finish_object_request(obj_req);
-}
-
-static void release_object_request(struct object_request *obj_req)
-{
-	struct object_request *entry = object_queue_head;
-
-	if (obj_req->local != -1)
-		error("fd leakage in release: %d", obj_req->local);
-	if (obj_req == object_queue_head) {
-		object_queue_head = obj_req->next;
-	} else {
-		while (entry->next != NULL && entry->next != obj_req)
-			entry = entry->next;
-		if (entry->next == obj_req)
-			entry->next = entry->next->next;
-	}
-
-	free(obj_req->url);
-	free(obj_req);
-}
-
-#ifdef USE_CURL_MULTI
-static int fill_active_slot(void *unused)
-{
-	struct object_request *obj_req;
-
-	for (obj_req = object_queue_head; obj_req; obj_req = obj_req->next) {
-		if (obj_req->state == WAITING) {
-			if (has_sha1_file(obj_req->sha1))
-				obj_req->state = COMPLETE;
-			else {
-				start_object_request(obj_req);
-				return 1;
-			}
-		}
-	}
-	return 0;
-}
-#endif
-
-void prefetch(unsigned char *sha1)
-{
-	struct object_request *newreq;
-	struct object_request *tail;
-	char *filename = sha1_file_name(sha1);
-
-	newreq = xmalloc(sizeof(*newreq));
-	hashcpy(newreq->sha1, sha1);
-	newreq->repo = alt;
-	newreq->url = NULL;
-	newreq->local = -1;
-	newreq->state = WAITING;
-	snprintf(newreq->filename, sizeof(newreq->filename), "%s", filename);
-	snprintf(newreq->tmpfile, sizeof(newreq->tmpfile),
-		 "%s.temp", filename);
-	newreq->slot = NULL;
-	newreq->next = NULL;
-
-	if (object_queue_head == NULL) {
-		object_queue_head = newreq;
-	} else {
-		tail = object_queue_head;
-		while (tail->next != NULL) {
-			tail = tail->next;
-		}
-		tail->next = newreq;
-	}
-
-#ifdef USE_CURL_MULTI
-	fill_active_slots();
-	step_active_slots();
-#endif
-}
-
-static int fetch_index(struct alt_base *repo, unsigned char *sha1)
-{
-	char *hex = sha1_to_hex(sha1);
-	char *filename;
-	char *url;
-	char tmpfile[PATH_MAX];
-	long prev_posn = 0;
-	char range[RANGE_HEADER_SIZE];
-	struct curl_slist *range_header = NULL;
-
-	FILE *indexfile;
-	struct active_request_slot *slot;
-	struct slot_results results;
-
-	if (has_pack_index(sha1))
-		return 0;
-
-	if (get_verbosely)
-		fprintf(stderr, "Getting index for pack %s\n", hex);
-
-	url = xmalloc(strlen(repo->base) + 64);
-	sprintf(url, "%s/objects/pack/pack-%s.idx", repo->base, hex);
-
-	filename = sha1_pack_index_name(sha1);
-	snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
-	indexfile = fopen(tmpfile, "a");
-	if (!indexfile)
-		return error("Unable to open local file %s for pack index",
-			     filename);
-
-	slot = get_active_slot();
-	slot->results = &results;
-	curl_easy_setopt(slot->curl, CURLOPT_FILE, indexfile);
-	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
-	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
-	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
-	slot->local = indexfile;
-
-	/* If there is data present from a previous transfer attempt,
-	   resume where it left off */
-	prev_posn = ftell(indexfile);
-	if (prev_posn>0) {
-		if (get_verbosely)
-			fprintf(stderr,
-				"Resuming fetch of index for pack %s at byte %ld\n",
-				hex, prev_posn);
-		sprintf(range, "Range: bytes=%ld-", prev_posn);
-		range_header = curl_slist_append(range_header, range);
-		curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, range_header);
-	}
-
-	if (start_active_slot(slot)) {
-		run_active_slot(slot);
-		if (results.curl_result != CURLE_OK) {
-			fclose(indexfile);
-			return error("Unable to get pack index %s\n%s", url,
-				     curl_errorstr);
-		}
-	} else {
-		fclose(indexfile);
-		return error("Unable to start request");
-	}
-
-	fclose(indexfile);
-
-	return move_temp_to_file(tmpfile, filename);
-}
-
-static int setup_index(struct alt_base *repo, unsigned char *sha1)
-{
-	struct packed_git *new_pack;
-	if (has_pack_file(sha1))
-		return 0; /* don't list this as something we can get */
-
-	if (fetch_index(repo, sha1))
-		return -1;
-
-	new_pack = parse_pack_index(sha1);
-	new_pack->next = repo->packs;
-	repo->packs = new_pack;
-	return 0;
-}
-
-static void process_alternates_response(void *callback_data)
-{
-	struct alternates_request *alt_req =
-		(struct alternates_request *)callback_data;
-	struct active_request_slot *slot = alt_req->slot;
-	struct alt_base *tail = alt;
-	const char *base = alt_req->base;
-	static const char null_byte = '\0';
-	char *data;
-	int i = 0;
-
-	if (alt_req->http_specific) {
-		if (slot->curl_result != CURLE_OK ||
-		    !alt_req->buffer->posn) {
-
-			/* Try reusing the slot to get non-http alternates */
-			alt_req->http_specific = 0;
-			sprintf(alt_req->url, "%s/objects/info/alternates",
-				base);
-			curl_easy_setopt(slot->curl, CURLOPT_URL,
-					 alt_req->url);
-			active_requests++;
-			slot->in_use = 1;
-			if (slot->finished != NULL)
-				(*slot->finished) = 0;
-			if (!start_active_slot(slot)) {
-				got_alternates = -1;
-				slot->in_use = 0;
-				if (slot->finished != NULL)
-					(*slot->finished) = 1;
-			}
-			return;
-		}
-	} else if (slot->curl_result != CURLE_OK) {
-		if (!missing_target(slot)) {
-			got_alternates = -1;
-			return;
-		}
-	}
-
-	fwrite_buffer(&null_byte, 1, 1, alt_req->buffer);
-	alt_req->buffer->posn--;
-	data = alt_req->buffer->buffer;
-
-	while (i < alt_req->buffer->posn) {
-		int posn = i;
-		while (posn < alt_req->buffer->posn && data[posn] != '\n')
-			posn++;
-		if (data[posn] == '\n') {
-			int okay = 0;
-			int serverlen = 0;
-			struct alt_base *newalt;
-			char *target = NULL;
-			if (data[i] == '/') {
-				/* This counts
-				 * http://git.host/pub/scm/linux.git/
-				 * -----------here^
-				 * so memcpy(dst, base, serverlen) will
-				 * copy up to "...git.host".
-				 */
-				const char *colon_ss = strstr(base,"://");
-				if (colon_ss) {
-					serverlen = (strchr(colon_ss + 3, '/')
-						     - base);
-					okay = 1;
-				}
-			} else if (!memcmp(data + i, "../", 3)) {
-				/* Relative URL; chop the corresponding
-				 * number of subpath from base (and ../
-				 * from data), and concatenate the result.
-				 *
-				 * The code first drops ../ from data, and
-				 * then drops one ../ from data and one path
-				 * from base.  IOW, one extra ../ is dropped
-				 * from data than path is dropped from base.
-				 *
-				 * This is not wrong.  The alternate in
-				 *     http://git.host/pub/scm/linux.git/
-				 * to borrow from
-				 *     http://git.host/pub/scm/linus.git/
-				 * is ../../linus.git/objects/.  You need
-				 * two ../../ to borrow from your direct
-				 * neighbour.
-				 */
-				i += 3;
-				serverlen = strlen(base);
-				while (i + 2 < posn &&
-				       !memcmp(data + i, "../", 3)) {
-					do {
-						serverlen--;
-					} while (serverlen &&
-						 base[serverlen - 1] != '/');
-					i += 3;
-				}
-				/* If the server got removed, give up. */
-				okay = strchr(base, ':') - base + 3 <
-					serverlen;
-			} else if (alt_req->http_specific) {
-				char *colon = strchr(data + i, ':');
-				char *slash = strchr(data + i, '/');
-				if (colon && slash && colon < data + posn &&
-				    slash < data + posn && colon < slash) {
-					okay = 1;
-				}
-			}
-			/* skip "objects\n" at end */
-			if (okay) {
-				target = xmalloc(serverlen + posn - i - 6);
-				memcpy(target, base, serverlen);
-				memcpy(target + serverlen, data + i,
-				       posn - i - 7);
-				target[serverlen + posn - i - 7] = 0;
-				if (get_verbosely)
-					fprintf(stderr,
-						"Also look at %s\n", target);
-				newalt = xmalloc(sizeof(*newalt));
-				newalt->next = NULL;
-				newalt->base = target;
-				newalt->got_indices = 0;
-				newalt->packs = NULL;
-
-				while (tail->next != NULL)
-					tail = tail->next;
-				tail->next = newalt;
-			}
-		}
-		i = posn + 1;
-	}
-
-	got_alternates = 1;
-}
-
-static void fetch_alternates(const char *base)
-{
-	struct buffer buffer;
-	char *url;
-	char *data;
-	struct active_request_slot *slot;
-	struct alternates_request alt_req;
-
-	/* If another request has already started fetching alternates,
-	   wait for them to arrive and return to processing this request's
-	   curl message */
-#ifdef USE_CURL_MULTI
-	while (got_alternates == 0) {
-		step_active_slots();
-	}
-#endif
-
-	/* Nothing to do if they've already been fetched */
-	if (got_alternates == 1)
-		return;
-
-	/* Start the fetch */
-	got_alternates = 0;
-
-	data = xmalloc(4096);
-	buffer.size = 4096;
-	buffer.posn = 0;
-	buffer.buffer = data;
-
-	if (get_verbosely)
-		fprintf(stderr, "Getting alternates list for %s\n", base);
-
-	url = xmalloc(strlen(base) + 31);
-	sprintf(url, "%s/objects/info/http-alternates", base);
-
-	/* Use a callback to process the result, since another request
-	   may fail and need to have alternates loaded before continuing */
-	slot = get_active_slot();
-	slot->callback_func = process_alternates_response;
-	slot->callback_data = &alt_req;
-
-	curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
-	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
-	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
-
-	alt_req.base = base;
-	alt_req.url = url;
-	alt_req.buffer = &buffer;
-	alt_req.http_specific = 1;
-	alt_req.slot = slot;
-
-	if (start_active_slot(slot))
-		run_active_slot(slot);
-	else
-		got_alternates = -1;
-
-	free(data);
-	free(url);
-}
-
-static int fetch_indices(struct alt_base *repo)
-{
-	unsigned char sha1[20];
-	char *url;
-	struct buffer buffer;
-	char *data;
-	int i = 0;
-
-	struct active_request_slot *slot;
-	struct slot_results results;
-
-	if (repo->got_indices)
-		return 0;
-
-	data = xmalloc(4096);
-	buffer.size = 4096;
-	buffer.posn = 0;
-	buffer.buffer = data;
-
-	if (get_verbosely)
-		fprintf(stderr, "Getting pack list for %s\n", repo->base);
-
-	url = xmalloc(strlen(repo->base) + 21);
-	sprintf(url, "%s/objects/info/packs", repo->base);
-
-	slot = get_active_slot();
-	slot->results = &results;
-	curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
-	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
-	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
-	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
-	if (start_active_slot(slot)) {
-		run_active_slot(slot);
-		if (results.curl_result != CURLE_OK) {
-			if (missing_target(&results)) {
-				repo->got_indices = 1;
-				free(buffer.buffer);
-				return 0;
-			} else {
-				repo->got_indices = 0;
-				free(buffer.buffer);
-				return error("%s", curl_errorstr);
-			}
-		}
-	} else {
-		repo->got_indices = 0;
-		free(buffer.buffer);
-		return error("Unable to start request");
-	}
-
-	data = buffer.buffer;
-	while (i < buffer.posn) {
-		switch (data[i]) {
-		case 'P':
-			i++;
-			if (i + 52 <= buffer.posn &&
-			    !prefixcmp(data + i, " pack-") &&
-			    !prefixcmp(data + i + 46, ".pack\n")) {
-				get_sha1_hex(data + i + 6, sha1);
-				setup_index(repo, sha1);
-				i += 51;
-				break;
-			}
-		default:
-			while (i < buffer.posn && data[i] != '\n')
-				i++;
-		}
-		i++;
-	}
-
-	free(buffer.buffer);
-	repo->got_indices = 1;
-	return 0;
-}
-
-static int fetch_pack(struct alt_base *repo, unsigned char *sha1)
-{
-	char *url;
-	struct packed_git *target;
-	struct packed_git **lst;
-	FILE *packfile;
-	char *filename;
-	char tmpfile[PATH_MAX];
-	int ret;
-	long prev_posn = 0;
-	char range[RANGE_HEADER_SIZE];
-	struct curl_slist *range_header = NULL;
-
-	struct active_request_slot *slot;
-	struct slot_results results;
-
-	if (fetch_indices(repo))
-		return -1;
-	target = find_sha1_pack(sha1, repo->packs);
-	if (!target)
-		return -1;
-
-	if (get_verbosely) {
-		fprintf(stderr, "Getting pack %s\n",
-			sha1_to_hex(target->sha1));
-		fprintf(stderr, " which contains %s\n",
-			sha1_to_hex(sha1));
-	}
-
-	url = xmalloc(strlen(repo->base) + 65);
-	sprintf(url, "%s/objects/pack/pack-%s.pack",
-		repo->base, sha1_to_hex(target->sha1));
-
-	filename = sha1_pack_name(target->sha1);
-	snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
-	packfile = fopen(tmpfile, "a");
-	if (!packfile)
-		return error("Unable to open local file %s for pack",
-			     filename);
-
-	slot = get_active_slot();
-	slot->results = &results;
-	curl_easy_setopt(slot->curl, CURLOPT_FILE, packfile);
-	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
-	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
-	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
-	slot->local = packfile;
-
-	/* If there is data present from a previous transfer attempt,
-	   resume where it left off */
-	prev_posn = ftell(packfile);
-	if (prev_posn>0) {
-		if (get_verbosely)
-			fprintf(stderr,
-				"Resuming fetch of pack %s at byte %ld\n",
-				sha1_to_hex(target->sha1), prev_posn);
-		sprintf(range, "Range: bytes=%ld-", prev_posn);
-		range_header = curl_slist_append(range_header, range);
-		curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, range_header);
-	}
-
-	if (start_active_slot(slot)) {
-		run_active_slot(slot);
-		if (results.curl_result != CURLE_OK) {
-			fclose(packfile);
-			return error("Unable to get pack file %s\n%s", url,
-				     curl_errorstr);
-		}
-	} else {
-		fclose(packfile);
-		return error("Unable to start request");
-	}
-
-	target->pack_size = ftell(packfile);
-	fclose(packfile);
-
-	ret = move_temp_to_file(tmpfile, filename);
-	if (ret)
-		return ret;
-
-	lst = &repo->packs;
-	while (*lst != target)
-		lst = &((*lst)->next);
-	*lst = (*lst)->next;
-
-	if (verify_pack(target, 0))
-		return -1;
-	install_packed_git(target);
-
-	return 0;
-}
-
-static void abort_object_request(struct object_request *obj_req)
-{
-	if (obj_req->local >= 0) {
-		close(obj_req->local);
-		obj_req->local = -1;
-	}
-	unlink(obj_req->tmpfile);
-	if (obj_req->slot) {
-		release_active_slot(obj_req->slot);
-		obj_req->slot = NULL;
-	}
-	release_object_request(obj_req);
-}
-
-static int fetch_object(struct alt_base *repo, unsigned char *sha1)
-{
-	char *hex = sha1_to_hex(sha1);
-	int ret = 0;
-	struct object_request *obj_req = object_queue_head;
-
-	while (obj_req != NULL && hashcmp(obj_req->sha1, sha1))
-		obj_req = obj_req->next;
-	if (obj_req == NULL)
-		return error("Couldn't find request for %s in the queue", hex);
-
-	if (has_sha1_file(obj_req->sha1)) {
-		abort_object_request(obj_req);
-		return 0;
-	}
-
-#ifdef USE_CURL_MULTI
-	while (obj_req->state == WAITING) {
-		step_active_slots();
-	}
-#else
-	start_object_request(obj_req);
-#endif
-
-	while (obj_req->state == ACTIVE) {
-		run_active_slot(obj_req->slot);
-	}
-	if (obj_req->local != -1) {
-		close(obj_req->local); obj_req->local = -1;
-	}
-
-	if (obj_req->state == ABORTED) {
-		ret = error("Request for %s aborted", hex);
-	} else if (obj_req->curl_result != CURLE_OK &&
-		   obj_req->http_code != 416) {
-		if (missing_target(obj_req))
-			ret = -1; /* Be silent, it is probably in a pack. */
-		else
-			ret = error("%s (curl_result = %d, http_code = %ld, sha1 = %s)",
-				    obj_req->errorstr, obj_req->curl_result,
-				    obj_req->http_code, hex);
-	} else if (obj_req->zret != Z_STREAM_END) {
-		corrupt_object_found++;
-		ret = error("File %s (%s) corrupt", hex, obj_req->url);
-	} else if (hashcmp(obj_req->sha1, obj_req->real_sha1)) {
-		ret = error("File %s has bad hash", hex);
-	} else if (obj_req->rename < 0) {
-		ret = error("unable to write sha1 filename %s",
-			    obj_req->filename);
-	}
-
-	release_object_request(obj_req);
-	return ret;
-}
-
-int fetch(unsigned char *sha1)
-{
-	struct alt_base *altbase = alt;
-
-	if (!fetch_object(altbase, sha1))
-		return 0;
-	while (altbase) {
-		if (!fetch_pack(altbase, sha1))
-			return 0;
-		fetch_alternates(alt->base);
-		altbase = altbase->next;
-	}
-	return error("Unable to find %s under %s", sha1_to_hex(sha1),
-		     alt->base);
-}
-
-static inline int needs_quote(int ch)
-{
-	if (((ch >= 'A') && (ch <= 'Z'))
-			|| ((ch >= 'a') && (ch <= 'z'))
-			|| ((ch >= '0') && (ch <= '9'))
-			|| (ch == '/')
-			|| (ch == '-')
-			|| (ch == '.'))
-		return 0;
-	return 1;
-}
-
-static inline int hex(int v)
-{
-	if (v < 10) return '0' + v;
-	else return 'A' + v - 10;
-}
-
-static char *quote_ref_url(const char *base, const char *ref)
-{
-	const char *cp;
-	char *dp, *qref;
-	int len, baselen, ch;
-
-	baselen = strlen(base);
-	len = baselen + 7; /* "/refs/" + NUL */
-	for (cp = ref; (ch = *cp) != 0; cp++, len++)
-		if (needs_quote(ch))
-			len += 2; /* extra two hex plus replacement % */
-	qref = xmalloc(len);
-	memcpy(qref, base, baselen);
-	memcpy(qref + baselen, "/refs/", 6);
-	for (cp = ref, dp = qref + baselen + 6; (ch = *cp) != 0; cp++) {
-		if (needs_quote(ch)) {
-			*dp++ = '%';
-			*dp++ = hex((ch >> 4) & 0xF);
-			*dp++ = hex(ch & 0xF);
-		}
-		else
-			*dp++ = ch;
-	}
-	*dp = 0;
-
-	return qref;
-}
-
-int fetch_ref(char *ref, unsigned char *sha1)
-{
-        char *url;
-        char hex[42];
-        struct buffer buffer;
-	const char *base = alt->base;
-	struct active_request_slot *slot;
-	struct slot_results results;
-        buffer.size = 41;
-        buffer.posn = 0;
-        buffer.buffer = hex;
-        hex[41] = '\0';
-
-	url = quote_ref_url(base, ref);
-	slot = get_active_slot();
-	slot->results = &results;
-	curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
-	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
-	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
-	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
-	if (start_active_slot(slot)) {
-		run_active_slot(slot);
-		if (results.curl_result != CURLE_OK)
-			return error("Couldn't get %s for %s\n%s",
-				     url, ref, curl_errorstr);
-	} else {
-		return error("Unable to start request");
-	}
-
-        hex[40] = '\0';
-        get_sha1_hex(hex, sha1);
-        return 0;
-}
-
-int main(int argc, const char **argv)
-{
-	int commits;
-	const char **write_ref = NULL;
-	char **commit_id;
-	const char *url;
-	char *s;
-	int arg = 1;
-	int rc = 0;
-
-	setup_git_directory();
-	git_config(git_default_config);
-
-	while (arg < argc && argv[arg][0] == '-') {
-		if (argv[arg][1] == 't') {
-			get_tree = 1;
-		} else if (argv[arg][1] == 'c') {
-			get_history = 1;
-		} else if (argv[arg][1] == 'a') {
-			get_all = 1;
-			get_tree = 1;
-			get_history = 1;
-		} else if (argv[arg][1] == 'v') {
-			get_verbosely = 1;
-		} else if (argv[arg][1] == 'w') {
-			write_ref = &argv[arg + 1];
-			arg++;
-		} else if (!strcmp(argv[arg], "--recover")) {
-			get_recover = 1;
-		} else if (!strcmp(argv[arg], "--stdin")) {
-			commits_on_stdin = 1;
-		}
-		arg++;
-	}
-	if (argc < arg + 2 - commits_on_stdin) {
-		usage("git-http-fetch [-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin] commit-id url");
-		return 1;
-	}
-	if (commits_on_stdin) {
-		commits = pull_targets_stdin(&commit_id, &write_ref);
-	} else {
-		commit_id = (char **) &argv[arg++];
-		commits = 1;
-	}
-	url = argv[arg];
-
-	http_init();
-
-	no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
-
-	alt = xmalloc(sizeof(*alt));
-	alt->base = xmalloc(strlen(url) + 1);
-	strcpy(alt->base, url);
-	for (s = alt->base + strlen(alt->base) - 1; *s == '/'; --s)
-		*s = 0;
-	alt->got_indices = 0;
-	alt->packs = NULL;
-	alt->next = NULL;
-
-#ifdef USE_CURL_MULTI
-	add_fill_function(NULL, fill_active_slot);
-#endif
-
-	if (pull(commits, commit_id, write_ref, url))
-		rc = 1;
-
-	http_cleanup();
-
-	curl_slist_free_all(no_pragma_header);
-
-	if (commits_on_stdin)
-		pull_targets_free(commits, commit_id, write_ref);
-
-	if (corrupt_object_found) {
-		fprintf(stderr,
-"Some loose object were found to be corrupt, but they might be just\n"
-"a false '404 Not Found' error message sent with incorrect HTTP\n"
-"status code.  Suggest running git-fsck.\n");
-	}
-	return rc;
-}
diff --git a/http-push.c b/http-push.c
index c54230b..8e1fdfd 100644
--- a/http-push.c
+++ b/http-push.c
@@ -1,7 +1,6 @@
 #include "cache.h"
 #include "commit.h"
 #include "pack.h"
-#include "fetch.h"
 #include "tag.h"
 #include "blob.h"
 #include "http.h"
diff --git a/http-walker.c b/http-walker.c
new file mode 100644
index 0000000..1069a57
--- /dev/null
+++ b/http-walker.c
@@ -0,0 +1,1035 @@
+#include "cache.h"
+#include "commit.h"
+#include "pack.h"
+#include "walker.h"
+#include "http.h"
+
+#define PREV_BUF_SIZE 4096
+#define RANGE_HEADER_SIZE 30
+
+struct alt_base
+{
+	char *base;
+	int got_indices;
+	struct packed_git *packs;
+	struct alt_base *next;
+};
+
+enum object_request_state {
+	WAITING,
+	ABORTED,
+	ACTIVE,
+	COMPLETE,
+};
+
+struct object_request
+{
+	struct walker *walker;
+	unsigned char sha1[20];
+	struct alt_base *repo;
+	char *url;
+	char filename[PATH_MAX];
+	char tmpfile[PATH_MAX];
+	int local;
+	enum object_request_state state;
+	CURLcode curl_result;
+	char errorstr[CURL_ERROR_SIZE];
+	long http_code;
+	unsigned char real_sha1[20];
+	SHA_CTX c;
+	z_stream stream;
+	int zret;
+	int rename;
+	struct active_request_slot *slot;
+	struct object_request *next;
+};
+
+struct alternates_request {
+	struct walker *walker;
+	const char *base;
+	char *url;
+	struct buffer *buffer;
+	struct active_request_slot *slot;
+	int http_specific;
+};
+
+struct walker_data {
+	const char *url;
+	int got_alternates;
+	struct alt_base *alt;
+	struct curl_slist *no_pragma_header;
+};
+
+static struct object_request *object_queue_head;
+
+static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
+			       void *data)
+{
+	unsigned char expn[4096];
+	size_t size = eltsize * nmemb;
+	int posn = 0;
+	struct object_request *obj_req = (struct object_request *)data;
+	do {
+		ssize_t retval = xwrite(obj_req->local,
+				       (char *) ptr + posn, size - posn);
+		if (retval < 0)
+			return posn;
+		posn += retval;
+	} while (posn < size);
+
+	obj_req->stream.avail_in = size;
+	obj_req->stream.next_in = ptr;
+	do {
+		obj_req->stream.next_out = expn;
+		obj_req->stream.avail_out = sizeof(expn);
+		obj_req->zret = inflate(&obj_req->stream, Z_SYNC_FLUSH);
+		SHA1_Update(&obj_req->c, expn,
+			    sizeof(expn) - obj_req->stream.avail_out);
+	} while (obj_req->stream.avail_in && obj_req->zret == Z_OK);
+	data_received++;
+	return size;
+}
+
+static int missing__target(int code, int result)
+{
+	return	/* file:// URL -- do we ever use one??? */
+		(result == CURLE_FILE_COULDNT_READ_FILE) ||
+		/* http:// and https:// URL */
+		(code == 404 && result == CURLE_HTTP_RETURNED_ERROR) ||
+		/* ftp:// URL */
+		(code == 550 && result == CURLE_FTP_COULDNT_RETR_FILE)
+		;
+}
+
+#define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
+
+static void fetch_alternates(struct walker *walker, const char *base);
+
+static void process_object_response(void *callback_data);
+
+static void start_object_request(struct walker *walker,
+				 struct object_request *obj_req)
+{
+	char *hex = sha1_to_hex(obj_req->sha1);
+	char prevfile[PATH_MAX];
+	char *url;
+	char *posn;
+	int prevlocal;
+	unsigned char prev_buf[PREV_BUF_SIZE];
+	ssize_t prev_read = 0;
+	long prev_posn = 0;
+	char range[RANGE_HEADER_SIZE];
+	struct curl_slist *range_header = NULL;
+	struct active_request_slot *slot;
+	struct walker_data *data = walker->data;
+
+	snprintf(prevfile, sizeof(prevfile), "%s.prev", obj_req->filename);
+	unlink(prevfile);
+	rename(obj_req->tmpfile, prevfile);
+	unlink(obj_req->tmpfile);
+
+	if (obj_req->local != -1)
+		error("fd leakage in start: %d", obj_req->local);
+	obj_req->local = open(obj_req->tmpfile,
+			      O_WRONLY | O_CREAT | O_EXCL, 0666);
+	/* This could have failed due to the "lazy directory creation";
+	 * try to mkdir the last path component.
+	 */
+	if (obj_req->local < 0 && errno == ENOENT) {
+		char *dir = strrchr(obj_req->tmpfile, '/');
+		if (dir) {
+			*dir = 0;
+			mkdir(obj_req->tmpfile, 0777);
+			*dir = '/';
+		}
+		obj_req->local = open(obj_req->tmpfile,
+				      O_WRONLY | O_CREAT | O_EXCL, 0666);
+	}
+
+	if (obj_req->local < 0) {
+		obj_req->state = ABORTED;
+		error("Couldn't create temporary file %s for %s: %s",
+		      obj_req->tmpfile, obj_req->filename, strerror(errno));
+		return;
+	}
+
+	memset(&obj_req->stream, 0, sizeof(obj_req->stream));
+
+	inflateInit(&obj_req->stream);
+
+	SHA1_Init(&obj_req->c);
+
+	url = xmalloc(strlen(obj_req->repo->base) + 51);
+	obj_req->url = xmalloc(strlen(obj_req->repo->base) + 51);
+	strcpy(url, obj_req->repo->base);
+	posn = url + strlen(obj_req->repo->base);
+	strcpy(posn, "/objects/");
+	posn += 9;
+	memcpy(posn, hex, 2);
+	posn += 2;
+	*(posn++) = '/';
+	strcpy(posn, hex + 2);
+	strcpy(obj_req->url, url);
+
+	/* If a previous temp file is present, process what was already
+	   fetched. */
+	prevlocal = open(prevfile, O_RDONLY);
+	if (prevlocal != -1) {
+		do {
+			prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE);
+			if (prev_read>0) {
+				if (fwrite_sha1_file(prev_buf,
+						     1,
+						     prev_read,
+						     obj_req) == prev_read) {
+					prev_posn += prev_read;
+				} else {
+					prev_read = -1;
+				}
+			}
+		} while (prev_read > 0);
+		close(prevlocal);
+	}
+	unlink(prevfile);
+
+	/* Reset inflate/SHA1 if there was an error reading the previous temp
+	   file; also rewind to the beginning of the local file. */
+	if (prev_read == -1) {
+		memset(&obj_req->stream, 0, sizeof(obj_req->stream));
+		inflateInit(&obj_req->stream);
+		SHA1_Init(&obj_req->c);
+		if (prev_posn>0) {
+			prev_posn = 0;
+			lseek(obj_req->local, 0, SEEK_SET);
+			ftruncate(obj_req->local, 0);
+		}
+	}
+
+	slot = get_active_slot();
+	slot->callback_func = process_object_response;
+	slot->callback_data = obj_req;
+	obj_req->slot = slot;
+
+	curl_easy_setopt(slot->curl, CURLOPT_FILE, obj_req);
+	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
+	curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, obj_req->errorstr);
+	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, data->no_pragma_header);
+
+	/* If we have successfully processed data from a previous fetch
+	   attempt, only fetch the data we don't already have. */
+	if (prev_posn>0) {
+		if (walker->get_verbosely)
+			fprintf(stderr,
+				"Resuming fetch of object %s at byte %ld\n",
+				hex, prev_posn);
+		sprintf(range, "Range: bytes=%ld-", prev_posn);
+		range_header = curl_slist_append(range_header, range);
+		curl_easy_setopt(slot->curl,
+				 CURLOPT_HTTPHEADER, range_header);
+	}
+
+	/* Try to get the request started, abort the request on error */
+	obj_req->state = ACTIVE;
+	if (!start_active_slot(slot)) {
+		obj_req->state = ABORTED;
+		obj_req->slot = NULL;
+		close(obj_req->local); obj_req->local = -1;
+		free(obj_req->url);
+		return;
+	}
+}
+
+static void finish_object_request(struct object_request *obj_req)
+{
+	struct stat st;
+
+	fchmod(obj_req->local, 0444);
+	close(obj_req->local); obj_req->local = -1;
+
+	if (obj_req->http_code == 416) {
+		fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n");
+	} else if (obj_req->curl_result != CURLE_OK) {
+		if (stat(obj_req->tmpfile, &st) == 0)
+			if (st.st_size == 0)
+				unlink(obj_req->tmpfile);
+		return;
+	}
+
+	inflateEnd(&obj_req->stream);
+	SHA1_Final(obj_req->real_sha1, &obj_req->c);
+	if (obj_req->zret != Z_STREAM_END) {
+		unlink(obj_req->tmpfile);
+		return;
+	}
+	if (hashcmp(obj_req->sha1, obj_req->real_sha1)) {
+		unlink(obj_req->tmpfile);
+		return;
+	}
+	obj_req->rename =
+		move_temp_to_file(obj_req->tmpfile, obj_req->filename);
+
+	if (obj_req->rename == 0)
+		walker_say(obj_req->walker, "got %s\n", sha1_to_hex(obj_req->sha1));
+}
+
+static void process_object_response(void *callback_data)
+{
+	struct object_request *obj_req =
+		(struct object_request *)callback_data;
+	struct walker *walker = obj_req->walker;
+	struct walker_data *data = walker->data;
+	struct alt_base *alt = data->alt;
+
+	obj_req->curl_result = obj_req->slot->curl_result;
+	obj_req->http_code = obj_req->slot->http_code;
+	obj_req->slot = NULL;
+	obj_req->state = COMPLETE;
+
+	/* Use alternates if necessary */
+	if (missing_target(obj_req)) {
+		fetch_alternates(walker, alt->base);
+		if (obj_req->repo->next != NULL) {
+			obj_req->repo =
+				obj_req->repo->next;
+			close(obj_req->local);
+			obj_req->local = -1;
+			start_object_request(walker, obj_req);
+			return;
+		}
+	}
+
+	finish_object_request(obj_req);
+}
+
+static void release_object_request(struct object_request *obj_req)
+{
+	struct object_request *entry = object_queue_head;
+
+	if (obj_req->local != -1)
+		error("fd leakage in release: %d", obj_req->local);
+	if (obj_req == object_queue_head) {
+		object_queue_head = obj_req->next;
+	} else {
+		while (entry->next != NULL && entry->next != obj_req)
+			entry = entry->next;
+		if (entry->next == obj_req)
+			entry->next = entry->next->next;
+	}
+
+	free(obj_req->url);
+	free(obj_req);
+}
+
+#ifdef USE_CURL_MULTI
+static int fill_active_slot(struct walker *walker)
+{
+	struct object_request *obj_req;
+
+	for (obj_req = object_queue_head; obj_req; obj_req = obj_req->next) {
+		if (obj_req->state == WAITING) {
+			if (has_sha1_file(obj_req->sha1))
+				obj_req->state = COMPLETE;
+			else {
+				start_object_request(walker, obj_req);
+				return 1;
+			}
+		}
+	}
+	return 0;
+}
+#endif
+
+static void prefetch(struct walker *walker, unsigned char *sha1)
+{
+	struct object_request *newreq;
+	struct object_request *tail;
+	struct walker_data *data = walker->data;
+	char *filename = sha1_file_name(sha1);
+
+	newreq = xmalloc(sizeof(*newreq));
+	newreq->walker = walker;
+	hashcpy(newreq->sha1, sha1);
+	newreq->repo = data->alt;
+	newreq->url = NULL;
+	newreq->local = -1;
+	newreq->state = WAITING;
+	snprintf(newreq->filename, sizeof(newreq->filename), "%s", filename);
+	snprintf(newreq->tmpfile, sizeof(newreq->tmpfile),
+		 "%s.temp", filename);
+	newreq->slot = NULL;
+	newreq->next = NULL;
+
+	if (object_queue_head == NULL) {
+		object_queue_head = newreq;
+	} else {
+		tail = object_queue_head;
+		while (tail->next != NULL) {
+			tail = tail->next;
+		}
+		tail->next = newreq;
+	}
+
+#ifdef USE_CURL_MULTI
+	fill_active_slots();
+	step_active_slots();
+#endif
+}
+
+static int fetch_index(struct walker *walker, struct alt_base *repo, unsigned char *sha1)
+{
+	char *hex = sha1_to_hex(sha1);
+	char *filename;
+	char *url;
+	char tmpfile[PATH_MAX];
+	long prev_posn = 0;
+	char range[RANGE_HEADER_SIZE];
+	struct curl_slist *range_header = NULL;
+	struct walker_data *data = walker->data;
+
+	FILE *indexfile;
+	struct active_request_slot *slot;
+	struct slot_results results;
+
+	if (has_pack_index(sha1))
+		return 0;
+
+	if (walker->get_verbosely)
+		fprintf(stderr, "Getting index for pack %s\n", hex);
+
+	url = xmalloc(strlen(repo->base) + 64);
+	sprintf(url, "%s/objects/pack/pack-%s.idx", repo->base, hex);
+
+	filename = sha1_pack_index_name(sha1);
+	snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
+	indexfile = fopen(tmpfile, "a");
+	if (!indexfile)
+		return error("Unable to open local file %s for pack index",
+			     filename);
+
+	slot = get_active_slot();
+	slot->results = &results;
+	curl_easy_setopt(slot->curl, CURLOPT_FILE, indexfile);
+	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
+	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, data->no_pragma_header);
+	slot->local = indexfile;
+
+	/* If there is data present from a previous transfer attempt,
+	   resume where it left off */
+	prev_posn = ftell(indexfile);
+	if (prev_posn>0) {
+		if (walker->get_verbosely)
+			fprintf(stderr,
+				"Resuming fetch of index for pack %s at byte %ld\n",
+				hex, prev_posn);
+		sprintf(range, "Range: bytes=%ld-", prev_posn);
+		range_header = curl_slist_append(range_header, range);
+		curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, range_header);
+	}
+
+	if (start_active_slot(slot)) {
+		run_active_slot(slot);
+		if (results.curl_result != CURLE_OK) {
+			fclose(indexfile);
+			return error("Unable to get pack index %s\n%s", url,
+				     curl_errorstr);
+		}
+	} else {
+		fclose(indexfile);
+		return error("Unable to start request");
+	}
+
+	fclose(indexfile);
+
+	return move_temp_to_file(tmpfile, filename);
+}
+
+static int setup_index(struct walker *walker, struct alt_base *repo, unsigned char *sha1)
+{
+	struct packed_git *new_pack;
+	if (has_pack_file(sha1))
+		return 0; /* don't list this as something we can get */
+
+	if (fetch_index(walker, repo, sha1))
+		return -1;
+
+	new_pack = parse_pack_index(sha1);
+	new_pack->next = repo->packs;
+	repo->packs = new_pack;
+	return 0;
+}
+
+static void process_alternates_response(void *callback_data)
+{
+	struct alternates_request *alt_req =
+		(struct alternates_request *)callback_data;
+	struct walker *walker = alt_req->walker;
+	struct walker_data *cdata = walker->data;
+	struct active_request_slot *slot = alt_req->slot;
+	struct alt_base *tail = cdata->alt;
+	const char *base = alt_req->base;
+	static const char null_byte = '\0';
+	char *data;
+	int i = 0;
+
+	if (alt_req->http_specific) {
+		if (slot->curl_result != CURLE_OK ||
+		    !alt_req->buffer->posn) {
+
+			/* Try reusing the slot to get non-http alternates */
+			alt_req->http_specific = 0;
+			sprintf(alt_req->url, "%s/objects/info/alternates",
+				base);
+			curl_easy_setopt(slot->curl, CURLOPT_URL,
+					 alt_req->url);
+			active_requests++;
+			slot->in_use = 1;
+			if (slot->finished != NULL)
+				(*slot->finished) = 0;
+			if (!start_active_slot(slot)) {
+				cdata->got_alternates = -1;
+				slot->in_use = 0;
+				if (slot->finished != NULL)
+					(*slot->finished) = 1;
+			}
+			return;
+		}
+	} else if (slot->curl_result != CURLE_OK) {
+		if (!missing_target(slot)) {
+			cdata->got_alternates = -1;
+			return;
+		}
+	}
+
+	fwrite_buffer(&null_byte, 1, 1, alt_req->buffer);
+	alt_req->buffer->posn--;
+	data = alt_req->buffer->buffer;
+
+	while (i < alt_req->buffer->posn) {
+		int posn = i;
+		while (posn < alt_req->buffer->posn && data[posn] != '\n')
+			posn++;
+		if (data[posn] == '\n') {
+			int okay = 0;
+			int serverlen = 0;
+			struct alt_base *newalt;
+			char *target = NULL;
+			if (data[i] == '/') {
+				/* This counts
+				 * http://git.host/pub/scm/linux.git/
+				 * -----------here^
+				 * so memcpy(dst, base, serverlen) will
+				 * copy up to "...git.host".
+				 */
+				const char *colon_ss = strstr(base,"://");
+				if (colon_ss) {
+					serverlen = (strchr(colon_ss + 3, '/')
+						     - base);
+					okay = 1;
+				}
+			} else if (!memcmp(data + i, "../", 3)) {
+				/* Relative URL; chop the corresponding
+				 * number of subpath from base (and ../
+				 * from data), and concatenate the result.
+				 *
+				 * The code first drops ../ from data, and
+				 * then drops one ../ from data and one path
+				 * from base.  IOW, one extra ../ is dropped
+				 * from data than path is dropped from base.
+				 *
+				 * This is not wrong.  The alternate in
+				 *     http://git.host/pub/scm/linux.git/
+				 * to borrow from
+				 *     http://git.host/pub/scm/linus.git/
+				 * is ../../linus.git/objects/.  You need
+				 * two ../../ to borrow from your direct
+				 * neighbour.
+				 */
+				i += 3;
+				serverlen = strlen(base);
+				while (i + 2 < posn &&
+				       !memcmp(data + i, "../", 3)) {
+					do {
+						serverlen--;
+					} while (serverlen &&
+						 base[serverlen - 1] != '/');
+					i += 3;
+				}
+				/* If the server got removed, give up. */
+				okay = strchr(base, ':') - base + 3 <
+					serverlen;
+			} else if (alt_req->http_specific) {
+				char *colon = strchr(data + i, ':');
+				char *slash = strchr(data + i, '/');
+				if (colon && slash && colon < data + posn &&
+				    slash < data + posn && colon < slash) {
+					okay = 1;
+				}
+			}
+			/* skip "objects\n" at end */
+			if (okay) {
+				target = xmalloc(serverlen + posn - i - 6);
+				memcpy(target, base, serverlen);
+				memcpy(target + serverlen, data + i,
+				       posn - i - 7);
+				target[serverlen + posn - i - 7] = 0;
+				if (walker->get_verbosely)
+					fprintf(stderr,
+						"Also look at %s\n", target);
+				newalt = xmalloc(sizeof(*newalt));
+				newalt->next = NULL;
+				newalt->base = target;
+				newalt->got_indices = 0;
+				newalt->packs = NULL;
+
+				while (tail->next != NULL)
+					tail = tail->next;
+				tail->next = newalt;
+			}
+		}
+		i = posn + 1;
+	}
+
+	cdata->got_alternates = 1;
+}
+
+static void fetch_alternates(struct walker *walker, const char *base)
+{
+	struct buffer buffer;
+	char *url;
+	char *data;
+	struct active_request_slot *slot;
+	struct alternates_request alt_req;
+	struct walker_data *cdata = walker->data;
+
+	/* If another request has already started fetching alternates,
+	   wait for them to arrive and return to processing this request's
+	   curl message */
+#ifdef USE_CURL_MULTI
+	while (cdata->got_alternates == 0) {
+		step_active_slots();
+	}
+#endif
+
+	/* Nothing to do if they've already been fetched */
+	if (cdata->got_alternates == 1)
+		return;
+
+	/* Start the fetch */
+	cdata->got_alternates = 0;
+
+	data = xmalloc(4096);
+	buffer.size = 4096;
+	buffer.posn = 0;
+	buffer.buffer = data;
+
+	if (walker->get_verbosely)
+		fprintf(stderr, "Getting alternates list for %s\n", base);
+
+	url = xmalloc(strlen(base) + 31);
+	sprintf(url, "%s/objects/info/http-alternates", base);
+
+	/* Use a callback to process the result, since another request
+	   may fail and need to have alternates loaded before continuing */
+	slot = get_active_slot();
+	slot->callback_func = process_alternates_response;
+	alt_req.walker = walker;
+	slot->callback_data = &alt_req;
+
+	curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+
+	alt_req.base = base;
+	alt_req.url = url;
+	alt_req.buffer = &buffer;
+	alt_req.http_specific = 1;
+	alt_req.slot = slot;
+
+	if (start_active_slot(slot))
+		run_active_slot(slot);
+	else
+		cdata->got_alternates = -1;
+
+	free(data);
+	free(url);
+}
+
+static int fetch_indices(struct walker *walker, struct alt_base *repo)
+{
+	unsigned char sha1[20];
+	char *url;
+	struct buffer buffer;
+	char *data;
+	int i = 0;
+
+	struct active_request_slot *slot;
+	struct slot_results results;
+
+	if (repo->got_indices)
+		return 0;
+
+	data = xmalloc(4096);
+	buffer.size = 4096;
+	buffer.posn = 0;
+	buffer.buffer = data;
+
+	if (walker->get_verbosely)
+		fprintf(stderr, "Getting pack list for %s\n", repo->base);
+
+	url = xmalloc(strlen(repo->base) + 21);
+	sprintf(url, "%s/objects/info/packs", repo->base);
+
+	slot = get_active_slot();
+	slot->results = &results;
+	curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
+	if (start_active_slot(slot)) {
+		run_active_slot(slot);
+		if (results.curl_result != CURLE_OK) {
+			if (missing_target(&results)) {
+				repo->got_indices = 1;
+				free(buffer.buffer);
+				return 0;
+			} else {
+				repo->got_indices = 0;
+				free(buffer.buffer);
+				return error("%s", curl_errorstr);
+			}
+		}
+	} else {
+		repo->got_indices = 0;
+		free(buffer.buffer);
+		return error("Unable to start request");
+	}
+
+	data = buffer.buffer;
+	while (i < buffer.posn) {
+		switch (data[i]) {
+		case 'P':
+			i++;
+			if (i + 52 <= buffer.posn &&
+			    !prefixcmp(data + i, " pack-") &&
+			    !prefixcmp(data + i + 46, ".pack\n")) {
+				get_sha1_hex(data + i + 6, sha1);
+				setup_index(walker, repo, sha1);
+				i += 51;
+				break;
+			}
+		default:
+			while (i < buffer.posn && data[i] != '\n')
+				i++;
+		}
+		i++;
+	}
+
+	free(buffer.buffer);
+	repo->got_indices = 1;
+	return 0;
+}
+
+static int fetch_pack(struct walker *walker, struct alt_base *repo, unsigned char *sha1)
+{
+	char *url;
+	struct packed_git *target;
+	struct packed_git **lst;
+	FILE *packfile;
+	char *filename;
+	char tmpfile[PATH_MAX];
+	int ret;
+	long prev_posn = 0;
+	char range[RANGE_HEADER_SIZE];
+	struct curl_slist *range_header = NULL;
+	struct walker_data *data = walker->data;
+
+	struct active_request_slot *slot;
+	struct slot_results results;
+
+	if (fetch_indices(walker, repo))
+		return -1;
+	target = find_sha1_pack(sha1, repo->packs);
+	if (!target)
+		return -1;
+
+	if (walker->get_verbosely) {
+		fprintf(stderr, "Getting pack %s\n",
+			sha1_to_hex(target->sha1));
+		fprintf(stderr, " which contains %s\n",
+			sha1_to_hex(sha1));
+	}
+
+	url = xmalloc(strlen(repo->base) + 65);
+	sprintf(url, "%s/objects/pack/pack-%s.pack",
+		repo->base, sha1_to_hex(target->sha1));
+
+	filename = sha1_pack_name(target->sha1);
+	snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
+	packfile = fopen(tmpfile, "a");
+	if (!packfile)
+		return error("Unable to open local file %s for pack",
+			     filename);
+
+	slot = get_active_slot();
+	slot->results = &results;
+	curl_easy_setopt(slot->curl, CURLOPT_FILE, packfile);
+	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
+	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, data->no_pragma_header);
+	slot->local = packfile;
+
+	/* If there is data present from a previous transfer attempt,
+	   resume where it left off */
+	prev_posn = ftell(packfile);
+	if (prev_posn>0) {
+		if (walker->get_verbosely)
+			fprintf(stderr,
+				"Resuming fetch of pack %s at byte %ld\n",
+				sha1_to_hex(target->sha1), prev_posn);
+		sprintf(range, "Range: bytes=%ld-", prev_posn);
+		range_header = curl_slist_append(range_header, range);
+		curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, range_header);
+	}
+
+	if (start_active_slot(slot)) {
+		run_active_slot(slot);
+		if (results.curl_result != CURLE_OK) {
+			fclose(packfile);
+			return error("Unable to get pack file %s\n%s", url,
+				     curl_errorstr);
+		}
+	} else {
+		fclose(packfile);
+		return error("Unable to start request");
+	}
+
+	target->pack_size = ftell(packfile);
+	fclose(packfile);
+
+	ret = move_temp_to_file(tmpfile, filename);
+	if (ret)
+		return ret;
+
+	lst = &repo->packs;
+	while (*lst != target)
+		lst = &((*lst)->next);
+	*lst = (*lst)->next;
+
+	if (verify_pack(target, 0))
+		return -1;
+	install_packed_git(target);
+
+	return 0;
+}
+
+static void abort_object_request(struct object_request *obj_req)
+{
+	if (obj_req->local >= 0) {
+		close(obj_req->local);
+		obj_req->local = -1;
+	}
+	unlink(obj_req->tmpfile);
+	if (obj_req->slot) {
+		release_active_slot(obj_req->slot);
+		obj_req->slot = NULL;
+	}
+	release_object_request(obj_req);
+}
+
+static int fetch_object(struct walker *walker, struct alt_base *repo, unsigned char *sha1)
+{
+	char *hex = sha1_to_hex(sha1);
+	int ret = 0;
+	struct object_request *obj_req = object_queue_head;
+
+	while (obj_req != NULL && hashcmp(obj_req->sha1, sha1))
+		obj_req = obj_req->next;
+	if (obj_req == NULL)
+		return error("Couldn't find request for %s in the queue", hex);
+
+	if (has_sha1_file(obj_req->sha1)) {
+		abort_object_request(obj_req);
+		return 0;
+	}
+
+#ifdef USE_CURL_MULTI
+	while (obj_req->state == WAITING) {
+		step_active_slots();
+	}
+#else
+	start_object_request(walker, obj_req);
+#endif
+
+	while (obj_req->state == ACTIVE) {
+		run_active_slot(obj_req->slot);
+	}
+	if (obj_req->local != -1) {
+		close(obj_req->local); obj_req->local = -1;
+	}
+
+	if (obj_req->state == ABORTED) {
+		ret = error("Request for %s aborted", hex);
+	} else if (obj_req->curl_result != CURLE_OK &&
+		   obj_req->http_code != 416) {
+		if (missing_target(obj_req))
+			ret = -1; /* Be silent, it is probably in a pack. */
+		else
+			ret = error("%s (curl_result = %d, http_code = %ld, sha1 = %s)",
+				    obj_req->errorstr, obj_req->curl_result,
+				    obj_req->http_code, hex);
+	} else if (obj_req->zret != Z_STREAM_END) {
+		walker->corrupt_object_found++;
+		ret = error("File %s (%s) corrupt", hex, obj_req->url);
+	} else if (hashcmp(obj_req->sha1, obj_req->real_sha1)) {
+		ret = error("File %s has bad hash", hex);
+	} else if (obj_req->rename < 0) {
+		ret = error("unable to write sha1 filename %s",
+			    obj_req->filename);
+	}
+
+	release_object_request(obj_req);
+	return ret;
+}
+
+static int fetch(struct walker *walker, unsigned char *sha1)
+{
+	struct walker_data *data = walker->data;
+	struct alt_base *altbase = data->alt;
+
+	if (!fetch_object(walker, altbase, sha1))
+		return 0;
+	while (altbase) {
+		if (!fetch_pack(walker, altbase, sha1))
+			return 0;
+		fetch_alternates(walker, data->alt->base);
+		altbase = altbase->next;
+	}
+	return error("Unable to find %s under %s", sha1_to_hex(sha1),
+		     data->alt->base);
+}
+
+static inline int needs_quote(int ch)
+{
+	if (((ch >= 'A') && (ch <= 'Z'))
+			|| ((ch >= 'a') && (ch <= 'z'))
+			|| ((ch >= '0') && (ch <= '9'))
+			|| (ch == '/')
+			|| (ch == '-')
+			|| (ch == '.'))
+		return 0;
+	return 1;
+}
+
+static inline int hex(int v)
+{
+	if (v < 10) return '0' + v;
+	else return 'A' + v - 10;
+}
+
+static char *quote_ref_url(const char *base, const char *ref)
+{
+	const char *cp;
+	char *dp, *qref;
+	int len, baselen, ch;
+
+	baselen = strlen(base);
+	len = baselen + 7; /* "/refs/" + NUL */
+	for (cp = ref; (ch = *cp) != 0; cp++, len++)
+		if (needs_quote(ch))
+			len += 2; /* extra two hex plus replacement % */
+	qref = xmalloc(len);
+	memcpy(qref, base, baselen);
+	memcpy(qref + baselen, "/refs/", 6);
+	for (cp = ref, dp = qref + baselen + 6; (ch = *cp) != 0; cp++) {
+		if (needs_quote(ch)) {
+			*dp++ = '%';
+			*dp++ = hex((ch >> 4) & 0xF);
+			*dp++ = hex(ch & 0xF);
+		}
+		else
+			*dp++ = ch;
+	}
+	*dp = 0;
+
+	return qref;
+}
+
+static int fetch_ref(struct walker *walker, char *ref, unsigned char *sha1)
+{
+        char *url;
+        char hex[42];
+        struct buffer buffer;
+	struct walker_data *data = walker->data;
+	const char *base = data->alt->base;
+	struct active_request_slot *slot;
+	struct slot_results results;
+        buffer.size = 41;
+        buffer.posn = 0;
+        buffer.buffer = hex;
+        hex[41] = '\0';
+
+	url = quote_ref_url(base, ref);
+	slot = get_active_slot();
+	slot->results = &results;
+	curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
+	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+	if (start_active_slot(slot)) {
+		run_active_slot(slot);
+		if (results.curl_result != CURLE_OK)
+			return error("Couldn't get %s for %s\n%s",
+				     url, ref, curl_errorstr);
+	} else {
+		return error("Unable to start request");
+	}
+
+        hex[40] = '\0';
+        get_sha1_hex(hex, sha1);
+        return 0;
+}
+
+static void cleanup(struct walker *walker)
+{
+	struct walker_data *data = walker->data;
+	http_cleanup();
+
+	curl_slist_free_all(data->no_pragma_header);
+}
+
+struct walker *get_http_walker(const char *url)
+{
+	char *s;
+	struct walker_data *data = xmalloc(sizeof(struct walker_data));
+	struct walker *walker = xmalloc(sizeof(struct walker));
+
+	http_init();
+
+	data->no_pragma_header = curl_slist_append(NULL, "Pragma:");
+
+	data->alt = xmalloc(sizeof(*data->alt));
+	data->alt->base = xmalloc(strlen(url) + 1);
+	strcpy(data->alt->base, url);
+	for (s = data->alt->base + strlen(data->alt->base) - 1; *s == '/'; --s)
+		*s = 0;
+
+	data->alt->got_indices = 0;
+	data->alt->packs = NULL;
+	data->alt->next = NULL;
+	data->got_alternates = -1;
+
+	walker->corrupt_object_found = 0;
+	walker->fetch = fetch;
+	walker->fetch_ref = fetch_ref;
+	walker->prefetch = prefetch;
+	walker->cleanup = cleanup;
+	walker->data = data;
+
+#ifdef USE_CURL_MULTI
+	add_fill_function(walker, fill_active_slot);
+#endif
+
+	return walker;
+}
diff --git a/walker.c b/walker.c
new file mode 100644
index 0000000..5c65ea4
--- /dev/null
+++ b/walker.c
@@ -0,0 +1,318 @@
+#include "cache.h"
+#include "walker.h"
+#include "commit.h"
+#include "tree.h"
+#include "tree-walk.h"
+#include "tag.h"
+#include "blob.h"
+#include "refs.h"
+#include "strbuf.h"
+
+static unsigned char current_commit_sha1[20];
+
+void walker_say(struct walker *walker, const char *fmt, const char *hex)
+{
+	if (walker->get_verbosely)
+		fprintf(stderr, fmt, hex);
+}
+
+static void report_missing(const struct object *obj)
+{
+	char missing_hex[41];
+	strcpy(missing_hex, sha1_to_hex(obj->sha1));;
+	fprintf(stderr, "Cannot obtain needed %s %s\n",
+		obj->type ? typename(obj->type): "object", missing_hex);
+	if (!is_null_sha1(current_commit_sha1))
+		fprintf(stderr, "while processing commit %s.\n",
+			sha1_to_hex(current_commit_sha1));
+}
+
+static int process(struct walker *walker, struct object *obj);
+
+static int process_tree(struct walker *walker, struct tree *tree)
+{
+	struct tree_desc desc;
+	struct name_entry entry;
+
+	if (parse_tree(tree))
+		return -1;
+
+	init_tree_desc(&desc, tree->buffer, tree->size);
+	while (tree_entry(&desc, &entry)) {
+		struct object *obj = NULL;
+
+		/* submodule commits are not stored in the superproject */
+		if (S_ISGITLINK(entry.mode))
+			continue;
+		if (S_ISDIR(entry.mode)) {
+			struct tree *tree = lookup_tree(entry.sha1);
+			if (tree)
+				obj = &tree->object;
+		}
+		else {
+			struct blob *blob = lookup_blob(entry.sha1);
+			if (blob)
+				obj = &blob->object;
+		}
+		if (!obj || process(walker, obj))
+			return -1;
+	}
+	free(tree->buffer);
+	tree->buffer = NULL;
+	tree->size = 0;
+	return 0;
+}
+
+#define COMPLETE	(1U << 0)
+#define SEEN		(1U << 1)
+#define TO_SCAN		(1U << 2)
+
+static struct commit_list *complete = NULL;
+
+static int process_commit(struct walker *walker, struct commit *commit)
+{
+	if (parse_commit(commit))
+		return -1;
+
+	while (complete && complete->item->date >= commit->date) {
+		pop_most_recent_commit(&complete, COMPLETE);
+	}
+
+	if (commit->object.flags & COMPLETE)
+		return 0;
+
+	hashcpy(current_commit_sha1, commit->object.sha1);
+
+	walker_say(walker, "walk %s\n", sha1_to_hex(commit->object.sha1));
+
+	if (walker->get_tree) {
+		if (process(walker, &commit->tree->object))
+			return -1;
+		if (!walker->get_all)
+			walker->get_tree = 0;
+	}
+	if (walker->get_history) {
+		struct commit_list *parents = commit->parents;
+		for (; parents; parents = parents->next) {
+			if (process(walker, &parents->item->object))
+				return -1;
+		}
+	}
+	return 0;
+}
+
+static int process_tag(struct walker *walker, struct tag *tag)
+{
+	if (parse_tag(tag))
+		return -1;
+	return process(walker, tag->tagged);
+}
+
+static struct object_list *process_queue = NULL;
+static struct object_list **process_queue_end = &process_queue;
+
+static int process_object(struct walker *walker, struct object *obj)
+{
+	if (obj->type == OBJ_COMMIT) {
+		if (process_commit(walker, (struct commit *)obj))
+			return -1;
+		return 0;
+	}
+	if (obj->type == OBJ_TREE) {
+		if (process_tree(walker, (struct tree *)obj))
+			return -1;
+		return 0;
+	}
+	if (obj->type == OBJ_BLOB) {
+		return 0;
+	}
+	if (obj->type == OBJ_TAG) {
+		if (process_tag(walker, (struct tag *)obj))
+			return -1;
+		return 0;
+	}
+	return error("Unable to determine requirements "
+		     "of type %s for %s",
+		     typename(obj->type), sha1_to_hex(obj->sha1));
+}
+
+static int process(struct walker *walker, struct object *obj)
+{
+	if (obj->flags & SEEN)
+		return 0;
+	obj->flags |= SEEN;
+
+	if (has_sha1_file(obj->sha1)) {
+		/* We already have it, so we should scan it now. */
+		obj->flags |= TO_SCAN;
+	}
+	else {
+		if (obj->flags & COMPLETE)
+			return 0;
+		walker->prefetch(walker, obj->sha1);
+	}
+
+	object_list_insert(obj, process_queue_end);
+	process_queue_end = &(*process_queue_end)->next;
+	return 0;
+}
+
+static int loop(struct walker *walker)
+{
+	struct object_list *elem;
+
+	while (process_queue) {
+		struct object *obj = process_queue->item;
+		elem = process_queue;
+		process_queue = elem->next;
+		free(elem);
+		if (!process_queue)
+			process_queue_end = &process_queue;
+
+		/* If we are not scanning this object, we placed it in
+		 * the queue because we needed to fetch it first.
+		 */
+		if (! (obj->flags & TO_SCAN)) {
+			if (walker->fetch(walker, obj->sha1)) {
+				report_missing(obj);
+				return -1;
+			}
+		}
+		if (!obj->type)
+			parse_object(obj->sha1);
+		if (process_object(walker, obj))
+			return -1;
+	}
+	return 0;
+}
+
+static int interpret_target(struct walker *walker, char *target, unsigned char *sha1)
+{
+	if (!get_sha1_hex(target, sha1))
+		return 0;
+	if (!check_ref_format(target)) {
+		if (!walker->fetch_ref(walker, target, sha1)) {
+			return 0;
+		}
+	}
+	return -1;
+}
+
+static int mark_complete(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+{
+	struct commit *commit = lookup_commit_reference_gently(sha1, 1);
+	if (commit) {
+		commit->object.flags |= COMPLETE;
+		insert_by_date(commit, &complete);
+	}
+	return 0;
+}
+
+int walker_targets_stdin(char ***target, const char ***write_ref)
+{
+	int targets = 0, targets_alloc = 0;
+	struct strbuf buf;
+	*target = NULL; *write_ref = NULL;
+	strbuf_init(&buf);
+	while (1) {
+		char *rf_one = NULL;
+		char *tg_one;
+
+		read_line(&buf, stdin, '\n');
+		if (buf.eof)
+			break;
+		tg_one = buf.buf;
+		rf_one = strchr(tg_one, '\t');
+		if (rf_one)
+			*rf_one++ = 0;
+
+		if (targets >= targets_alloc) {
+			targets_alloc = targets_alloc ? targets_alloc * 2 : 64;
+			*target = xrealloc(*target, targets_alloc * sizeof(**target));
+			*write_ref = xrealloc(*write_ref, targets_alloc * sizeof(**write_ref));
+		}
+		(*target)[targets] = xstrdup(tg_one);
+		(*write_ref)[targets] = rf_one ? xstrdup(rf_one) : NULL;
+		targets++;
+	}
+	return targets;
+}
+
+void walker_targets_free(int targets, char **target, const char **write_ref)
+{
+	while (targets--) {
+		free(target[targets]);
+		if (write_ref && write_ref[targets])
+			free((char *) write_ref[targets]);
+	}
+}
+
+int walker_fetch(struct walker *walker, int targets, char **target,
+		 const char **write_ref, const char *write_ref_log_details)
+{
+	struct ref_lock **lock = xcalloc(targets, sizeof(struct ref_lock *));
+	unsigned char *sha1 = xmalloc(targets * 20);
+	char *msg;
+	int ret;
+	int i;
+
+	save_commit_buffer = 0;
+	track_object_refs = 0;
+
+	for (i = 0; i < targets; i++) {
+		if (!write_ref || !write_ref[i])
+			continue;
+
+		lock[i] = lock_ref_sha1(write_ref[i], NULL);
+		if (!lock[i]) {
+			error("Can't lock ref %s", write_ref[i]);
+			goto unlock_and_fail;
+		}
+	}
+
+	if (!walker->get_recover)
+		for_each_ref(mark_complete, NULL);
+
+	for (i = 0; i < targets; i++) {
+		if (interpret_target(walker, target[i], &sha1[20 * i])) {
+			error("Could not interpret %s as something to pull", target[i]);
+			goto unlock_and_fail;
+		}
+		if (process(walker, lookup_unknown_object(&sha1[20 * i])))
+			goto unlock_and_fail;
+	}
+
+	if (loop(walker))
+		goto unlock_and_fail;
+
+	if (write_ref_log_details) {
+		msg = xmalloc(strlen(write_ref_log_details) + 12);
+		sprintf(msg, "fetch from %s", write_ref_log_details);
+	} else {
+		msg = NULL;
+	}
+	for (i = 0; i < targets; i++) {
+		if (!write_ref || !write_ref[i])
+			continue;
+		ret = write_ref_sha1(lock[i], &sha1[20 * i], msg ? msg : "fetch (unknown)");
+		lock[i] = NULL;
+		if (ret)
+			goto unlock_and_fail;
+	}
+	free(msg);
+
+	return 0;
+
+unlock_and_fail:
+	for (i = 0; i < targets; i++)
+		if (lock[i])
+			unlock_ref(lock[i]);
+
+	return -1;
+}
+
+void walker_free(struct walker *walker)
+{
+	walker->cleanup(walker);
+	free(walker);
+}
diff --git a/walker.h b/walker.h
new file mode 100644
index 0000000..ea2c363
--- /dev/null
+++ b/walker.h
@@ -0,0 +1,37 @@
+#ifndef WALKER_H
+#define WALKER_H
+
+struct walker {
+	void *data;
+	int (*fetch_ref)(struct walker *, char *ref, unsigned char *sha1);
+	void (*prefetch)(struct walker *, unsigned char *sha1);
+	int (*fetch)(struct walker *, unsigned char *sha1);
+	void (*cleanup)(struct walker *);
+	int get_tree;
+	int get_history;
+	int get_all;
+	int get_verbosely;
+	int get_recover;
+
+	int corrupt_object_found;
+};
+
+/* Report what we got under get_verbosely */
+void walker_say(struct walker *walker, const char *, const char *);
+
+/* Load pull targets from stdin */
+int walker_targets_stdin(char ***target, const char ***write_ref);
+
+/* Free up loaded targets */
+void walker_targets_free(int targets, char **target, const char **write_ref);
+
+/* If write_ref is set, the ref filename to write the target value to. */
+/* If write_ref_log_details is set, additional text will appear in the ref log. */
+int walker_fetch(struct walker *impl, int targets, char **target,
+		 const char **write_ref, const char *write_ref_log_details);
+
+void walker_free(struct walker *walker);
+
+struct walker *get_http_walker(const char *url);
+
+#endif /* WALKER_H */
-- 
1.5.3.rc1.818.g84b7

^ permalink raw reply related

* [PATCH 3/4] Remove obsolete commit-walkers
From: Daniel Barkalow @ 2007-07-22 22:09 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

Removes the commit-walkers that are no longer useful, as well as
library code that was only used by ssh-fetch/push.

Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
---
 Makefile      |   20 +----
 local-fetch.c |  254 ---------------------------------------------------------
 rsh.c         |   83 -------------------
 rsh.h         |    7 --
 ssh-fetch.c   |  166 -------------------------------------
 ssh-pull.c    |    4 -
 ssh-push.c    |    4 -
 ssh-upload.c  |  143 --------------------------------
 8 files changed, 4 insertions(+), 677 deletions(-)
 delete mode 100644 local-fetch.c
 delete mode 100644 rsh.c
 delete mode 100644 rsh.h
 delete mode 100644 ssh-fetch.c
 delete mode 100644 ssh-pull.c
 delete mode 100644 ssh-push.c
 delete mode 100644 ssh-upload.c

diff --git a/Makefile b/Makefile
index 4ea5e45..705dde5 100644
--- a/Makefile
+++ b/Makefile
@@ -239,14 +239,14 @@ endif
 # ... and all the rest that could be moved out of bindir to gitexecdir
 PROGRAMS = \
 	git-convert-objects$X git-fetch-pack$X \
-	git-hash-object$X git-index-pack$X git-local-fetch$X \
+	git-hash-object$X git-index-pack$X \
 	git-fast-import$X \
 	git-daemon$X \
 	git-merge-index$X git-mktag$X git-mktree$X git-patch-id$X \
 	git-peek-remote$X git-receive-pack$X \
 	git-send-pack$X git-shell$X \
-	git-show-index$X git-ssh-fetch$X \
-	git-ssh-upload$X git-unpack-file$X \
+	git-show-index$X \
+	git-unpack-file$X \
 	git-update-server-info$X \
 	git-upload-pack$X \
 	git-pack-redundant$X git-var$X \
@@ -274,9 +274,6 @@ ifndef NO_TCLTK
 OTHER_PROGRAMS += gitk-wish
 endif
 
-# Backward compatibility -- to be removed after 1.0
-PROGRAMS += git-ssh-pull$X git-ssh-push$X
-
 # Set paths to tools early so that they can be used for version tests.
 ifndef SHELL_PATH
 	SHELL_PATH = /bin/sh
@@ -884,14 +881,6 @@ endif
 git-%$X: %.o $(GITLIBS)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
 
-ssh-pull.o: ssh-fetch.c
-ssh-push.o: ssh-upload.c
-git-local-fetch$X: fetch.o
-git-ssh-fetch$X: rsh.o fetch.o
-git-ssh-upload$X: rsh.o
-git-ssh-pull$X: rsh.o fetch.o
-git-ssh-push$X: rsh.o
-
 git-imap-send$X: imap-send.o $(LIB_FILE)
 
 http.o http-fetch.o http-push.o: http.h
@@ -1103,8 +1092,7 @@ check-docs::
 		git-merge-octopus | git-merge-ours | git-merge-recursive | \
 		git-merge-resolve | git-merge-stupid | \
 		git-add--interactive | git-fsck-objects | git-init-db | \
-		git-repo-config | git-fetch--tool | \
-		git-ssh-pull | git-ssh-push ) continue ;; \
+		git-repo-config | git-fetch--tool ) continue ;; \
 		esac ; \
 		test -f "Documentation/$$v.txt" || \
 		echo "no doc: $$v"; \
diff --git a/local-fetch.c b/local-fetch.c
deleted file mode 100644
index bf7ec6c..0000000
--- a/local-fetch.c
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * Copyright (C) 2005 Junio C Hamano
- */
-#include "cache.h"
-#include "commit.h"
-#include "fetch.h"
-
-static int use_link;
-static int use_symlink;
-static int use_filecopy = 1;
-static int commits_on_stdin;
-
-static const char *path; /* "Remote" git repository */
-
-void prefetch(unsigned char *sha1)
-{
-}
-
-static struct packed_git *packs;
-
-static void setup_index(unsigned char *sha1)
-{
-	struct packed_git *new_pack;
-	char filename[PATH_MAX];
-	strcpy(filename, path);
-	strcat(filename, "/objects/pack/pack-");
-	strcat(filename, sha1_to_hex(sha1));
-	strcat(filename, ".idx");
-	new_pack = parse_pack_index_file(sha1, filename);
-	new_pack->next = packs;
-	packs = new_pack;
-}
-
-static int setup_indices(void)
-{
-	DIR *dir;
-	struct dirent *de;
-	char filename[PATH_MAX];
-	unsigned char sha1[20];
-	sprintf(filename, "%s/objects/pack/", path);
-	dir = opendir(filename);
-	if (!dir)
-		return -1;
-	while ((de = readdir(dir)) != NULL) {
-		int namelen = strlen(de->d_name);
-		if (namelen != 50 ||
-		    !has_extension(de->d_name, ".pack"))
-			continue;
-		get_sha1_hex(de->d_name + 5, sha1);
-		setup_index(sha1);
-	}
-	closedir(dir);
-	return 0;
-}
-
-static int copy_file(const char *source, char *dest, const char *hex,
-		     int warn_if_not_exists)
-{
-	safe_create_leading_directories(dest);
-	if (use_link) {
-		if (!link(source, dest)) {
-			pull_say("link %s\n", hex);
-			return 0;
-		}
-		/* If we got ENOENT there is no point continuing. */
-		if (errno == ENOENT) {
-			if (!warn_if_not_exists)
-				return -1;
-			return error("does not exist %s", source);
-		}
-	}
-	if (use_symlink) {
-		struct stat st;
-		if (stat(source, &st)) {
-			if (!warn_if_not_exists && errno == ENOENT)
-				return -1;
-			return error("cannot stat %s: %s", source,
-				     strerror(errno));
-		}
-		if (!symlink(source, dest)) {
-			pull_say("symlink %s\n", hex);
-			return 0;
-		}
-	}
-	if (use_filecopy) {
-		int ifd, ofd, status = 0;
-
-		ifd = open(source, O_RDONLY);
-		if (ifd < 0) {
-			if (!warn_if_not_exists && errno == ENOENT)
-				return -1;
-			return error("cannot open %s", source);
-		}
-		ofd = open(dest, O_WRONLY | O_CREAT | O_EXCL, 0666);
-		if (ofd < 0) {
-			close(ifd);
-			return error("cannot open %s", dest);
-		}
-		status = copy_fd(ifd, ofd);
-		close(ofd);
-		if (status)
-			return error("cannot write %s", dest);
-		pull_say("copy %s\n", hex);
-		return 0;
-	}
-	return error("failed to copy %s with given copy methods.", hex);
-}
-
-static int fetch_pack(const unsigned char *sha1)
-{
-	struct packed_git *target;
-	char filename[PATH_MAX];
-	if (setup_indices())
-		return -1;
-	target = find_sha1_pack(sha1, packs);
-	if (!target)
-		return error("Couldn't find %s: not separate or in any pack",
-			     sha1_to_hex(sha1));
-	if (get_verbosely) {
-		fprintf(stderr, "Getting pack %s\n",
-			sha1_to_hex(target->sha1));
-		fprintf(stderr, " which contains %s\n",
-			sha1_to_hex(sha1));
-	}
-	sprintf(filename, "%s/objects/pack/pack-%s.pack",
-		path, sha1_to_hex(target->sha1));
-	copy_file(filename, sha1_pack_name(target->sha1),
-		  sha1_to_hex(target->sha1), 1);
-	sprintf(filename, "%s/objects/pack/pack-%s.idx",
-		path, sha1_to_hex(target->sha1));
-	copy_file(filename, sha1_pack_index_name(target->sha1),
-		  sha1_to_hex(target->sha1), 1);
-	install_packed_git(target);
-	return 0;
-}
-
-static int fetch_file(const unsigned char *sha1)
-{
-	static int object_name_start = -1;
-	static char filename[PATH_MAX];
-	char *hex = sha1_to_hex(sha1);
-	char *dest_filename = sha1_file_name(sha1);
-
-	if (object_name_start < 0) {
-		strcpy(filename, path); /* e.g. git.git */
-		strcat(filename, "/objects/");
-		object_name_start = strlen(filename);
-	}
-	filename[object_name_start+0] = hex[0];
-	filename[object_name_start+1] = hex[1];
-	filename[object_name_start+2] = '/';
-	strcpy(filename + object_name_start + 3, hex + 2);
-	return copy_file(filename, dest_filename, hex, 0);
-}
-
-int fetch(unsigned char *sha1)
-{
-	if (has_sha1_file(sha1))
-		return 0;
-	else
-		return fetch_file(sha1) && fetch_pack(sha1);
-}
-
-int fetch_ref(char *ref, unsigned char *sha1)
-{
-	static int ref_name_start = -1;
-	static char filename[PATH_MAX];
-	static char hex[41];
-	int ifd;
-
-	if (ref_name_start < 0) {
-		sprintf(filename, "%s/refs/", path);
-		ref_name_start = strlen(filename);
-	}
-	strcpy(filename + ref_name_start, ref);
-	ifd = open(filename, O_RDONLY);
-	if (ifd < 0) {
-		close(ifd);
-		return error("cannot open %s", filename);
-	}
-	if (read_in_full(ifd, hex, 40) != 40 || get_sha1_hex(hex, sha1)) {
-		close(ifd);
-		return error("cannot read from %s", filename);
-	}
-	close(ifd);
-	pull_say("ref %s\n", sha1_to_hex(sha1));
-	return 0;
-}
-
-static const char local_pull_usage[] =
-"git-local-fetch [-c] [-t] [-a] [-v] [-w filename] [--recover] [-l] [-s] [-n] [--stdin] commit-id path";
-
-/*
- * By default we only use file copy.
- * If -l is specified, a hard link is attempted.
- * If -s is specified, then a symlink is attempted.
- * If -n is _not_ specified, then a regular file-to-file copy is done.
- */
-int main(int argc, const char **argv)
-{
-	int commits;
-	const char **write_ref = NULL;
-	char **commit_id;
-	int arg = 1;
-
-	setup_git_directory();
-	git_config(git_default_config);
-
-	while (arg < argc && argv[arg][0] == '-') {
-		if (argv[arg][1] == 't')
-			get_tree = 1;
-		else if (argv[arg][1] == 'c')
-			get_history = 1;
-		else if (argv[arg][1] == 'a') {
-			get_all = 1;
-			get_tree = 1;
-			get_history = 1;
-		}
-		else if (argv[arg][1] == 'l')
-			use_link = 1;
-		else if (argv[arg][1] == 's')
-			use_symlink = 1;
-		else if (argv[arg][1] == 'n')
-			use_filecopy = 0;
-		else if (argv[arg][1] == 'v')
-			get_verbosely = 1;
-		else if (argv[arg][1] == 'w')
-			write_ref = &argv[++arg];
-		else if (!strcmp(argv[arg], "--recover"))
-			get_recover = 1;
-		else if (!strcmp(argv[arg], "--stdin"))
-			commits_on_stdin = 1;
-		else
-			usage(local_pull_usage);
-		arg++;
-	}
-	if (argc < arg + 2 - commits_on_stdin)
-		usage(local_pull_usage);
-	if (commits_on_stdin) {
-		commits = pull_targets_stdin(&commit_id, &write_ref);
-	} else {
-		commit_id = (char **) &argv[arg++];
-		commits = 1;
-	}
-	path = argv[arg];
-
-	if (pull(commits, commit_id, write_ref, path))
-		return 1;
-
-	if (commits_on_stdin)
-		pull_targets_free(commits, commit_id, write_ref);
-
-	return 0;
-}
diff --git a/rsh.c b/rsh.c
deleted file mode 100644
index 5754a23..0000000
--- a/rsh.c
+++ /dev/null
@@ -1,83 +0,0 @@
-#include "cache.h"
-#include "rsh.h"
-#include "quote.h"
-
-#define COMMAND_SIZE 4096
-
-int setup_connection(int *fd_in, int *fd_out, const char *remote_prog,
-		     char *url, int rmt_argc, char **rmt_argv)
-{
-	char *host;
-	char *path;
-	int sv[2];
-	char command[COMMAND_SIZE];
-	char *posn;
-	int sizen;
-	int of;
-	int i;
-	pid_t pid;
-
-	if (!strcmp(url, "-")) {
-		*fd_in = 0;
-		*fd_out = 1;
-		return 0;
-	}
-
-	host = strstr(url, "//");
-	if (host) {
-		host += 2;
-		path = strchr(host, '/');
-	} else {
-		host = url;
-		path = strchr(host, ':');
-		if (path)
-			*(path++) = '\0';
-	}
-	if (!path) {
-		return error("Bad URL: %s", url);
-	}
-	/* $GIT_RSH <host> "env GIT_DIR=<path> <remote_prog> <args...>" */
-	sizen = COMMAND_SIZE;
-	posn = command;
-	of = 0;
-	of |= add_to_string(&posn, &sizen, "env ", 0);
-	of |= add_to_string(&posn, &sizen, GIT_DIR_ENVIRONMENT "=", 0);
-	of |= add_to_string(&posn, &sizen, path, 1);
-	of |= add_to_string(&posn, &sizen, " ", 0);
-	of |= add_to_string(&posn, &sizen, remote_prog, 1);
-
-	for ( i = 0 ; i < rmt_argc ; i++ ) {
-		of |= add_to_string(&posn, &sizen, " ", 0);
-		of |= add_to_string(&posn, &sizen, rmt_argv[i], 1);
-	}
-
-	of |= add_to_string(&posn, &sizen, " -", 0);
-
-	if ( of )
-		return error("Command line too long");
-
-	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv))
-		return error("Couldn't create socket");
-
-	pid = fork();
-	if (pid < 0)
-		return error("Couldn't fork");
-	if (!pid) {
-		const char *ssh, *ssh_basename;
-		ssh = getenv("GIT_SSH");
-		if (!ssh) ssh = "ssh";
-		ssh_basename = strrchr(ssh, '/');
-		if (!ssh_basename)
-			ssh_basename = ssh;
-		else
-			ssh_basename++;
-		close(sv[1]);
-		dup2(sv[0], 0);
-		dup2(sv[0], 1);
-		execlp(ssh, ssh_basename, host, command, NULL);
-	}
-	close(sv[0]);
-	*fd_in = sv[1];
-	*fd_out = sv[1];
-	return 0;
-}
diff --git a/rsh.h b/rsh.h
deleted file mode 100644
index ee2f499..0000000
--- a/rsh.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef RSH_H
-#define RSH_H
-
-int setup_connection(int *fd_in, int *fd_out, const char *remote_prog,
-		     char *url, int rmt_argc, char **rmt_argv);
-
-#endif
diff --git a/ssh-fetch.c b/ssh-fetch.c
deleted file mode 100644
index bdf51a7..0000000
--- a/ssh-fetch.c
+++ /dev/null
@@ -1,166 +0,0 @@
-#ifndef COUNTERPART_ENV_NAME
-#define COUNTERPART_ENV_NAME "GIT_SSH_UPLOAD"
-#endif
-#ifndef COUNTERPART_PROGRAM_NAME
-#define COUNTERPART_PROGRAM_NAME "git-ssh-upload"
-#endif
-#ifndef MY_PROGRAM_NAME
-#define MY_PROGRAM_NAME "git-ssh-fetch"
-#endif
-
-#include "cache.h"
-#include "commit.h"
-#include "rsh.h"
-#include "fetch.h"
-#include "refs.h"
-
-static int fd_in;
-static int fd_out;
-
-static unsigned char remote_version;
-static unsigned char local_version = 1;
-
-static int prefetches;
-
-static struct object_list *in_transit;
-static struct object_list **end_of_transit = &in_transit;
-
-void prefetch(unsigned char *sha1)
-{
-	char type = 'o';
-	struct object_list *node;
-	if (prefetches > 100) {
-		fetch(in_transit->item->sha1);
-	}
-	node = xmalloc(sizeof(struct object_list));
-	node->next = NULL;
-	node->item = lookup_unknown_object(sha1);
-	*end_of_transit = node;
-	end_of_transit = &node->next;
-	/* XXX: what if these writes fail? */
-	write_in_full(fd_out, &type, 1);
-	write_in_full(fd_out, sha1, 20);
-	prefetches++;
-}
-
-static char conn_buf[4096];
-static size_t conn_buf_posn;
-
-int fetch(unsigned char *sha1)
-{
-	int ret;
-	signed char remote;
-	struct object_list *temp;
-
-	if (hashcmp(sha1, in_transit->item->sha1)) {
-		/* we must have already fetched it to clean the queue */
-		return has_sha1_file(sha1) ? 0 : -1;
-	}
-	prefetches--;
-	temp = in_transit;
-	in_transit = in_transit->next;
-	if (!in_transit)
-		end_of_transit = &in_transit;
-	free(temp);
-
-	if (conn_buf_posn) {
-		remote = conn_buf[0];
-		memmove(conn_buf, conn_buf + 1, --conn_buf_posn);
-	} else {
-		if (xread(fd_in, &remote, 1) < 1)
-			return -1;
-	}
-	/* fprintf(stderr, "Got %d\n", remote); */
-	if (remote < 0)
-		return remote;
-	ret = write_sha1_from_fd(sha1, fd_in, conn_buf, 4096, &conn_buf_posn);
-	if (!ret)
-		pull_say("got %s\n", sha1_to_hex(sha1));
-	return ret;
-}
-
-static int get_version(void)
-{
-	char type = 'v';
-	if (write_in_full(fd_out, &type, 1) != 1 ||
-	    write_in_full(fd_out, &local_version, 1)) {
-		return error("Couldn't request version from remote end");
-	}
-	if (xread(fd_in, &remote_version, 1) < 1) {
-		return error("Couldn't read version from remote end");
-	}
-	return 0;
-}
-
-int fetch_ref(char *ref, unsigned char *sha1)
-{
-	signed char remote;
-	char type = 'r';
-	int length = strlen(ref) + 1;
-	if (write_in_full(fd_out, &type, 1) != 1 ||
-	    write_in_full(fd_out, ref, length) != length)
-		return -1;
-
-	if (read_in_full(fd_in, &remote, 1) != 1)
-		return -1;
-	if (remote < 0)
-		return remote;
-	if (read_in_full(fd_in, sha1, 20) != 20)
-		return -1;
-	return 0;
-}
-
-static const char ssh_fetch_usage[] =
-  MY_PROGRAM_NAME
-  " [-c] [-t] [-a] [-v] [--recover] [-w ref] commit-id url";
-int main(int argc, char **argv)
-{
-	const char *write_ref = NULL;
-	char *commit_id;
-	char *url;
-	int arg = 1;
-	const char *prog;
-
-	prog = getenv("GIT_SSH_PUSH");
-	if (!prog) prog = "git-ssh-upload";
-
-	setup_git_directory();
-	git_config(git_default_config);
-
-	while (arg < argc && argv[arg][0] == '-') {
-		if (argv[arg][1] == 't') {
-			get_tree = 1;
-		} else if (argv[arg][1] == 'c') {
-			get_history = 1;
-		} else if (argv[arg][1] == 'a') {
-			get_all = 1;
-			get_tree = 1;
-			get_history = 1;
-		} else if (argv[arg][1] == 'v') {
-			get_verbosely = 1;
-		} else if (argv[arg][1] == 'w') {
-			write_ref = argv[arg + 1];
-			arg++;
-		} else if (!strcmp(argv[arg], "--recover")) {
-			get_recover = 1;
-		}
-		arg++;
-	}
-	if (argc < arg + 2) {
-		usage(ssh_fetch_usage);
-		return 1;
-	}
-	commit_id = argv[arg];
-	url = argv[arg + 1];
-
-	if (setup_connection(&fd_in, &fd_out, prog, url, arg, argv + 1))
-		return 1;
-
-	if (get_version())
-		return 1;
-
-	if (pull(1, &commit_id, &write_ref, url))
-		return 1;
-
-	return 0;
-}
diff --git a/ssh-pull.c b/ssh-pull.c
deleted file mode 100644
index 868ce4d..0000000
--- a/ssh-pull.c
+++ /dev/null
@@ -1,4 +0,0 @@
-#define COUNTERPART_ENV_NAME "GIT_SSH_PUSH"
-#define COUNTERPART_PROGRAM_NAME "git-ssh-push"
-#define MY_PROGRAM_NAME "git-ssh-pull"
-#include "ssh-fetch.c"
diff --git a/ssh-push.c b/ssh-push.c
deleted file mode 100644
index a562df1..0000000
--- a/ssh-push.c
+++ /dev/null
@@ -1,4 +0,0 @@
-#define COUNTERPART_ENV_NAME "GIT_SSH_PULL"
-#define COUNTERPART_PROGRAM_NAME "git-ssh-pull"
-#define MY_PROGRAM_NAME "git-ssh-push"
-#include "ssh-upload.c"
diff --git a/ssh-upload.c b/ssh-upload.c
deleted file mode 100644
index 20c35f0..0000000
--- a/ssh-upload.c
+++ /dev/null
@@ -1,143 +0,0 @@
-#ifndef COUNTERPART_ENV_NAME
-#define COUNTERPART_ENV_NAME "GIT_SSH_FETCH"
-#endif
-#ifndef COUNTERPART_PROGRAM_NAME
-#define COUNTERPART_PROGRAM_NAME "git-ssh-fetch"
-#endif
-#ifndef MY_PROGRAM_NAME
-#define MY_PROGRAM_NAME "git-ssh-upload"
-#endif
-
-#include "cache.h"
-#include "rsh.h"
-#include "refs.h"
-
-static unsigned char local_version = 1;
-static unsigned char remote_version;
-
-static int verbose;
-
-static int serve_object(int fd_in, int fd_out) {
-	ssize_t size;
-	unsigned char sha1[20];
-	signed char remote;
-
-	size = read_in_full(fd_in, sha1, 20);
-	if (size < 0) {
-		perror("git-ssh-upload: read ");
-		return -1;
-	}
-	if (!size)
-		return -1;
-
-	if (verbose)
-		fprintf(stderr, "Serving %s\n", sha1_to_hex(sha1));
-
-	remote = 0;
-
-	if (!has_sha1_file(sha1)) {
-		fprintf(stderr, "git-ssh-upload: could not find %s\n",
-			sha1_to_hex(sha1));
-		remote = -1;
-	}
-
-	if (write_in_full(fd_out, &remote, 1) != 1)
-		return 0;
-
-	if (remote < 0)
-		return 0;
-
-	return write_sha1_to_fd(fd_out, sha1);
-}
-
-static int serve_version(int fd_in, int fd_out)
-{
-	if (xread(fd_in, &remote_version, 1) < 1)
-		return -1;
-	write_in_full(fd_out, &local_version, 1);
-	return 0;
-}
-
-static int serve_ref(int fd_in, int fd_out)
-{
-	char ref[PATH_MAX];
-	unsigned char sha1[20];
-	int posn = 0;
-	signed char remote = 0;
-	do {
-		if (posn >= PATH_MAX || xread(fd_in, ref + posn, 1) < 1)
-			return -1;
-		posn++;
-	} while (ref[posn - 1]);
-
-	if (verbose)
-		fprintf(stderr, "Serving %s\n", ref);
-
-	if (get_ref_sha1(ref, sha1))
-		remote = -1;
-	if (write_in_full(fd_out, &remote, 1) != 1)
-		return 0;
-	if (remote)
-		return 0;
-	write_in_full(fd_out, sha1, 20);
-        return 0;
-}
-
-
-static void service(int fd_in, int fd_out) {
-	char type;
-	ssize_t retval;
-	do {
-		retval = xread(fd_in, &type, 1);
-		if (retval < 1) {
-			if (retval < 0)
-				perror("git-ssh-upload: read ");
-			return;
-		}
-		if (type == 'v' && serve_version(fd_in, fd_out))
-			return;
-		if (type == 'o' && serve_object(fd_in, fd_out))
-			return;
-		if (type == 'r' && serve_ref(fd_in, fd_out))
-			return;
-	} while (1);
-}
-
-static const char ssh_push_usage[] =
-	MY_PROGRAM_NAME " [-c] [-t] [-a] [-w ref] commit-id url";
-
-int main(int argc, char **argv)
-{
-	int arg = 1;
-        char *commit_id;
-        char *url;
-	int fd_in, fd_out;
-	const char *prog;
-	unsigned char sha1[20];
-	char hex[41];
-
-	prog = getenv(COUNTERPART_ENV_NAME);
-	if (!prog) prog = COUNTERPART_PROGRAM_NAME;
-
-	setup_git_directory();
-
-	while (arg < argc && argv[arg][0] == '-') {
-		if (argv[arg][1] == 'w')
-			arg++;
-                arg++;
-        }
-	if (argc < arg + 2)
-		usage(ssh_push_usage);
-	commit_id = argv[arg];
-	url = argv[arg + 1];
-	if (get_sha1(commit_id, sha1))
-		die("Not a valid object name %s", commit_id);
-	memcpy(hex, sha1_to_hex(sha1), sizeof(hex));
-	argv[arg] = hex;
-
-	if (setup_connection(&fd_in, &fd_out, prog, url, arg, argv + 1))
-		return 1;
-
-	service(fd_in, fd_out);
-	return 0;
-}
-- 
1.5.3.rc1.818.g84b7

^ permalink raw reply related

* [PATCH 2/4] Make function to refill http queue a callback
From: Daniel Barkalow @ 2007-07-22 22:09 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

This eliminates the last function provided by the code using http.h as
a global symbol, so it should be possible to have multiple programs
using http.h in the same executable, and it also adds an argument to
that callback, so that info can be passed into the callback without
being global.

Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
---
 http-fetch.c |    6 +++++-
 http-push.c  |    3 ++-
 http.c       |   30 ++++++++++++++++++++++++++++--
 http.h       |    4 +---
 4 files changed, 36 insertions(+), 7 deletions(-)

diff --git a/http-fetch.c b/http-fetch.c
index e9b9f30..7786110 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -317,7 +317,7 @@ static void release_object_request(struct object_request *obj_req)
 }
 
 #ifdef USE_CURL_MULTI
-int fill_active_slot(void)
+static int fill_active_slot(void *unused)
 {
 	struct object_request *obj_req;
 
@@ -1035,6 +1035,10 @@ int main(int argc, const char **argv)
 	alt->packs = NULL;
 	alt->next = NULL;
 
+#ifdef USE_CURL_MULTI
+	add_fill_function(NULL, fill_active_slot);
+#endif
+
 	if (pull(commits, commit_id, write_ref, url))
 		rc = 1;
 
diff --git a/http-push.c b/http-push.c
index c7471fa..c54230b 100644
--- a/http-push.c
+++ b/http-push.c
@@ -795,7 +795,7 @@ static void finish_request(struct transfer_request *request)
 }
 
 #ifdef USE_CURL_MULTI
-int fill_active_slot(void)
+static int fill_active_slot(void *unused)
 {
 	struct transfer_request *request = request_queue_head;
 
@@ -2496,6 +2496,7 @@ int main(int argc, char **argv)
 				objects_to_send);
 #ifdef USE_CURL_MULTI
 		fill_active_slots();
+		add_fill_function(NULL, fill_active_slot);
 #endif
 		finish_all_active_slots();
 
diff --git a/http.c b/http.c
index 1f305bd..a95483b 100644
--- a/http.c
+++ b/http.c
@@ -391,13 +391,39 @@ int start_active_slot(struct active_request_slot *slot)
 }
 
 #ifdef USE_CURL_MULTI
+struct fill_chain {
+	void *data;
+	int (*fill)(void *);
+	struct fill_chain *next;
+};
+
+static struct fill_chain *fill_cfg = NULL;
+
+void add_fill_function(void *data, int (*fill)(void *))
+{
+	struct fill_chain *new = malloc(sizeof(*new));
+	struct fill_chain **linkp = &fill_cfg;
+	new->data = data;
+	new->fill = fill;
+	new->next = NULL;
+	while (*linkp)
+		linkp = &(*linkp)->next;
+	*linkp = new;
+}
+
 void fill_active_slots(void)
 {
 	struct active_request_slot *slot = active_queue_head;
 
-	while (active_requests < max_requests)
-		if (!fill_active_slot())
+	while (active_requests < max_requests) {
+		struct fill_chain *fill;
+		for (fill = fill_cfg; fill; fill = fill->next)
+			if (fill->fill(fill->data))
+				break;
+
+		if (!fill)
 			break;
+	}
 
 	while (slot != NULL) {
 		if (!slot->in_use && slot->curl != NULL) {
diff --git a/http.h b/http.h
index 559105c..72abac2 100644
--- a/http.h
+++ b/http.h
@@ -70,10 +70,8 @@ extern void release_active_slot(struct active_request_slot *slot);
 
 #ifdef USE_CURL_MULTI
 extern void fill_active_slots(void);
+extern void add_fill_function(void *data, int (*fill)(void *));
 extern void step_active_slots(void);
-
-/* Provided by the program using http. */
-extern int fill_active_slot(void);
 #endif
 
 extern void http_init(void);
-- 
1.5.3.rc1.818.g84b7

^ permalink raw reply related

* [PATCH 1/4] Refactor http.h USE_CURL_MULTI fill_active_slots().
From: Daniel Barkalow @ 2007-07-22 22:09 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

This removes all of the boilerplate and http-internal stuff from
fill_active_slots() and makes it easy to turn into a callback.

Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
---
 http-fetch.c |   23 +++++++----------------
 http-push.c  |   23 ++++++-----------------
 http.c       |   24 ++++++++++++++++++++++++
 http.h       |    9 +++------
 4 files changed, 40 insertions(+), 39 deletions(-)

diff --git a/http-fetch.c b/http-fetch.c
index 202fae0..e9b9f30 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -317,30 +317,21 @@ static void release_object_request(struct object_request *obj_req)
 }
 
 #ifdef USE_CURL_MULTI
-void fill_active_slots(void)
+int fill_active_slot(void)
 {
-	struct object_request *obj_req = object_queue_head;
-	struct active_request_slot *slot = active_queue_head;
-	int num_transfers;
+	struct object_request *obj_req;
 
-	while (active_requests < max_requests && obj_req != NULL) {
+	for (obj_req = object_queue_head; obj_req; obj_req = obj_req->next) {
 		if (obj_req->state == WAITING) {
 			if (has_sha1_file(obj_req->sha1))
 				obj_req->state = COMPLETE;
-			else
+			else {
 				start_object_request(obj_req);
-			curl_multi_perform(curlm, &num_transfers);
-		}
-		obj_req = obj_req->next;
-	}
-
-	while (slot != NULL) {
-		if (!slot->in_use && slot->curl != NULL) {
-			curl_easy_cleanup(slot->curl);
-			slot->curl = NULL;
+				return 1;
+			}
 		}
-		slot = slot->next;
 	}
+	return 0;
 }
 #endif
 
diff --git a/http-push.c b/http-push.c
index 7c3720f..c7471fa 100644
--- a/http-push.c
+++ b/http-push.c
@@ -795,38 +795,27 @@ static void finish_request(struct transfer_request *request)
 }
 
 #ifdef USE_CURL_MULTI
-void fill_active_slots(void)
+int fill_active_slot(void)
 {
 	struct transfer_request *request = request_queue_head;
-	struct transfer_request *next;
-	struct active_request_slot *slot = active_queue_head;
-	int num_transfers;
 
 	if (aborted)
-		return;
+		return 0;
 
-	while (active_requests < max_requests && request != NULL) {
-		next = request->next;
+	for (request = request_queue_head; request; request = request->next) {
 		if (request->state == NEED_FETCH) {
 			start_fetch_loose(request);
+			return 1;
 		} else if (pushing && request->state == NEED_PUSH) {
 			if (remote_dir_exists[request->obj->sha1[0]] == 1) {
 				start_put(request);
 			} else {
 				start_mkcol(request);
 			}
-			curl_multi_perform(curlm, &num_transfers);
-		}
-		request = next;
-	}
-
-	while (slot != NULL) {
-		if (!slot->in_use && slot->curl != NULL) {
-			curl_easy_cleanup(slot->curl);
-			slot->curl = NULL;
+			return 1;
 		}
-		slot = slot->next;
 	}
+	return 0;
 }
 #endif
 
diff --git a/http.c b/http.c
index c6fb8ac..1f305bd 100644
--- a/http.c
+++ b/http.c
@@ -372,6 +372,7 @@ int start_active_slot(struct active_request_slot *slot)
 {
 #ifdef USE_CURL_MULTI
 	CURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl);
+	int num_transfers;
 
 	if (curlm_result != CURLM_OK &&
 	    curlm_result != CURLM_CALL_MULTI_PERFORM) {
@@ -379,11 +380,34 @@ int start_active_slot(struct active_request_slot *slot)
 		slot->in_use = 0;
 		return 0;
 	}
+
+	/*
+	 * We know there must be something to do, since we just added
+	 * something.
+	 */
+	curl_multi_perform(curlm, &num_transfers);
 #endif
 	return 1;
 }
 
 #ifdef USE_CURL_MULTI
+void fill_active_slots(void)
+{
+	struct active_request_slot *slot = active_queue_head;
+
+	while (active_requests < max_requests)
+		if (!fill_active_slot())
+			break;
+
+	while (slot != NULL) {
+		if (!slot->in_use && slot->curl != NULL) {
+			curl_easy_cleanup(slot->curl);
+			slot->curl = NULL;
+		}
+		slot = slot->next;
+	}
+}
+
 void step_active_slots(void)
 {
 	int num_transfers;
diff --git a/http.h b/http.h
index 69b6b66..559105c 100644
--- a/http.h
+++ b/http.h
@@ -71,6 +71,9 @@ extern void release_active_slot(struct active_request_slot *slot);
 #ifdef USE_CURL_MULTI
 extern void fill_active_slots(void);
 extern void step_active_slots(void);
+
+/* Provided by the program using http. */
+extern int fill_active_slot(void);
 #endif
 
 extern void http_init(void);
@@ -79,10 +82,6 @@ extern void http_cleanup(void);
 extern int data_received;
 extern int active_requests;
 
-#ifdef USE_CURL_MULTI
-extern int max_requests;
-extern CURLM *curlm;
-#endif
 #ifndef NO_CURL_EASY_DUPHANDLE
 extern CURL *curl_default;
 #endif
@@ -103,6 +102,4 @@ extern long curl_low_speed_time;
 extern struct curl_slist *pragma_header;
 extern struct curl_slist *no_range_header;
 
-extern struct active_request_slot *active_queue_head;
-
 #endif /* HTTP_H */
-- 
1.5.3.rc1.818.g84b7

^ permalink raw reply related

* [PATCH 0/4] Modularize http, commit-walker, http-fetch
From: Daniel Barkalow @ 2007-07-22 22:09 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

This series cleans up a whole lot of flow control and makes it possible to 
extend commit-walkers and http (each individually) in more useful 
directions.

Due to the limitations on what I can test, it could use a bunch more 
testing before it goes in. In particular, http-push is completely 
untested, and I haven't really beaten on USE_CURL_MULTI, although it 
compiles each way.

The first patch just makes the http library code easier to work with.

The second patch turns the caller-provided global function for making 
delayed requests into a callback in a chain.

The third patch (posted previously) removes obsolete commit-walkers.

The fourth patch (posted previously) reworks the commit-walker framework, 
and makes http-fetch into a commit-walker implementation which can be used 
from library code. It also turns http-fetch into a (trivial) builtin which 
calls the walker code with the library walker.

 Makefile                      |   37 ++----
 builtin-http-fetch.c          |   77 +++++++++++++
 builtin.h                     |    1 +
 fetch.h                       |   54 ---------
 git.c                         |    3 +
 http-push.c                   |   25 +---
 http-fetch.c => http-walker.c |  245 +++++++++++++++++----------------------
 http.c                        |   50 ++++++++
 http.h                        |    7 +-
 local-fetch.c                 |  254 -----------------------------------------
 rsh.c                         |   83 -------------
 rsh.h                         |    7 -
 ssh-fetch.c                   |  166 ---------------------------
 ssh-pull.c                    |    4 -
 ssh-push.c                    |    4 -
 ssh-upload.c                  |  143 -----------------------
 fetch.c => walker.c           |   83 +++++++-------
 walker.h                      |   37 ++++++
 18 files changed, 339 insertions(+), 941 deletions(-)

	-Daniel
*This .sig left intentionally blank*

^ permalink raw reply

* Re: [PATCH] some feedbacks from the Italian Free Translation Project.
From: Paolo Ciarrocchi @ 2007-07-22 22:06 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git
In-Reply-To: <Pine.LNX.4.64.0707222034500.14781@racer.site>

On 7/22/07, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:
> Hi,
>
> On Sun, 22 Jul 2007, Paolo Ciarrocchi wrote:
>
> > Many thanks to the Italian Free Translation Project
> >
> > More patches in the following days.
> >
> > Signed-off-by: Paolo Ciarrocchi <paolo.ciarrocchi@gmail.com>
>
> Thanks.  Both of your patches applied, although I had to fix non-UTF-8
> encoding.  This could well be a problem on my side.

Odd.
Is there anything I should check in my box?

Ciao,
-- 
Paolo
"Tutto cio' che merita di essere fatto,merita di essere fatto bene"
Philip Stanhope IV conte di Chesterfield

^ permalink raw reply

* [PATCH] More cleanup and grammatical fixes to it.po
From: Paolo Ciarrocchi @ 2007-07-22 22:06 UTC (permalink / raw)
  To: git, Johannes.Schindelin

Thanks to Daniele Forsi from the Italian translation project for the suggestions.
The translation is now in line with other already translated application.

Signed-off-by: Paolo Ciarrocchi <paolo.ciarrocchi@gmail.com>
---
 po/it.po |   32 ++++++++++++++++----------------
 1 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/po/it.po b/po/it.po
index 80365b4..9899a78 100644
--- a/po/it.po
+++ b/po/it.po
@@ -97,7 +97,7 @@ msgstr "Incolla"
 
 #: git-gui.sh:1756 git-gui.sh:2271
 msgid "Delete"
-msgstr "Cancella"
+msgstr "Elimina"
 
 #: git-gui.sh:1760 git-gui.sh:2275 git-gui.sh:2412
 msgid "Select All"
@@ -121,7 +121,7 @@ msgstr "Cancella"
 
 #: git-gui.sh:1791
 msgid "Reset..."
-msgstr "Resetta..."
+msgstr "Reimposta..."
 
 #: git-gui.sh:1803 git-gui.sh:2209
 msgid "New Commit"
@@ -161,7 +161,7 @@ msgstr "Incorporazione locale"
 
 #: git-gui.sh:1869
 msgid "Abort Merge..."
-msgstr "Annulla incorporazione"
+msgstr "Interrompi incorporazione"
 
 #: git-gui.sh:1881
 msgid "Push..."
@@ -186,11 +186,11 @@ msgstr "Aiuto"
 
 #: git-gui.sh:1978
 msgid "Online Documentation"
-msgstr "Documentazione Online"
+msgstr "Documentazione in linea"
 
 #: git-gui.sh:2094
 msgid "Current Branch:"
-msgstr "Ramo corrente:"
+msgstr "Ramo attuale:"
 
 #: git-gui.sh:2115
 msgid "Staged Changes (Will Be Committed)"
@@ -206,19 +206,19 @@ msgstr "Aggiungi esistente"
 
 #: git-gui.sh:2228
 msgid "Initial Commit Message:"
-msgstr "Messaggio iniziale del Commit"
+msgstr "Messaggio iniziale del commit:"
 
 #: git-gui.sh:2229
 msgid "Amended Commit Message:"
-msgstr "Annulla messaggio del Commit"
+msgstr "Annulla messaggio del commit:"
 
 #: git-gui.sh:2230
 msgid "Amended Initial Commit Message:"
-msgstr "Annulla messaggio iniziale del Commit"
+msgstr "Annulla messaggio iniziale del commit:"
 
 #: git-gui.sh:2231
 msgid "Amended Merge Commit Message:"
-msgstr "Annulla messaggio di incorporamento"
+msgstr "Annulla messaggio di incorporamento:"
 
 #: git-gui.sh:2232
 msgid "Merge Commit Message:"
@@ -226,7 +226,7 @@ msgstr "Messaggio di incorporamento"
 
 #: git-gui.sh:2233
 msgid "Commit Message:"
-msgstr "Messaggio di Commit"
+msgstr "Messaggio di commit:"
 
 #: git-gui.sh:2278 git-gui.sh:2416
 msgid "Copy All"
@@ -234,24 +234,24 @@ msgstr "Copia tutto"
 
 #: git-gui.sh:2404
 msgid "Refresh"
-msgstr "Rinfresca"
+msgstr "Aggiorna"
 
 #: git-gui.sh:2425
 msgid "Apply/Reverse Hunk"
-msgstr "Applica/Inverti Hunk"
+msgstr "Applica/Inverti sezione"
 
 #: git-gui.sh:2431
 msgid "Decrease Font Size"
-msgstr "Diminuisci dimensione Font"
+msgstr "Diminuisci dimensione font"
 
 #: git-gui.sh:2435
 msgid "Increase Font Size"
-msgstr "Aumenta dimensione Font"
+msgstr "Aumenta dimensione font"
 
 #: git-gui.sh:2440
 msgid "Show Less Context"
-msgstr "Mostra meno contenuto"
+msgstr "Mostra meno contesto"
 
 #: git-gui.sh:2447
 msgid "Show More Context"
-msgstr "Mostra più contenuto"
+msgstr "Mostra più contesto"
-- 
1.5.3.rc2.29.gc4640f

^ permalink raw reply related

* Re: [PATCH] Avoid to duplicate commit message when is not encoded
From: Marco Costalba @ 2007-07-22 22:04 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Alex Riesen, Git Mailing List
In-Reply-To: <7v4pjwssp0.fsf@assigned-by-dhcp.cox.net>

On 7/22/07, Junio C Hamano <gitster@pobox.com> wrote:
> Alex Riesen <raa.lkml@gmail.com> writes:
>
> > Marco Costalba, Sun, Jul 22, 2007 10:23:05 +0200:
> >> +            if (encoding) // we'll strip encoding header later
> >
> > You better avoid using C++ comments, not all C compilers support them.
>
> I've "fixed" them.
>

Sorry, I wrote them automatically without thinking it is better to
avoid them in C. BTW I would think it's more about coding style then
compiler support.

> >> +            else
> >> +                    return NULL; // nothing to do
> >
> > And you better avoid such useless comments too
>
> I think this one is probably a good-to-have.  It really needs a
> bit of thinking why there is nothing to do in this case.  At
> least for me it did ;-)
>

Me too ;-)

^ permalink raw reply

* Re: Git tree for old kernels from before the current tree
From: Jan Engelhardt @ 2007-07-22 22:00 UTC (permalink / raw)
  To: Paul Mundt; +Cc: Jon Smirl, Git Mailing List, lkml
In-Reply-To: <Pine.LNX.4.64.0707222344540.32367@fbirervta.pbzchgretzou.qr>


On Jul 22 2007 23:46, Jan Engelhardt wrote:
>On Jul 23 2007 06:13, Paul Mundt wrote:
>>> 
>>Err, that's crap. Have you even looked at gitweb? There's at least:
>>
>>git://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git
>>This has trees all the way back to 2.5.0.

Actually back to 2.4.0, including history. That I'd call mission done then :)


	Jan
-- 

^ permalink raw reply

* Re: [PATCH 3/3] Teach "git branch" about --new-workdir
From: Johannes Schindelin @ 2007-07-22 21:59 UTC (permalink / raw)
  To: Julian Phillips; +Cc: git, gitster
In-Reply-To: <Pine.LNX.4.64.0707222234020.5382@reaper.quantumfyre.co.uk>

Hi,

On Sun, 22 Jul 2007, Julian Phillips wrote:

> On Sun, 22 Jul 2007, Johannes Schindelin wrote:
> 
> > On Sun, 22 Jul 2007, Julian Phillips wrote:
> > 
> > > On Sun, 22 Jul 2007, Johannes Schindelin wrote:
> > > 
> > > > 	IMHO this is a better syntax than what is in contrib/, and "git
> > > > 	branch" is probably the right place for such a thing, from a
> > > > 	user's perspective.
> > > 
> > > Surely checkout would make more sense than branch?  You are effectively
> > > checking out into a new directory ... also you may want to get an
> > > existing branch (certainly most of my usage of new-workdir is checking
> > > out existing branches, e.g. to look at - as in build and play with - an
> > > interesting branch that someone else has pushed out).
> > 
> > My rationale here was:
> > 
> > - to make sure that the user cannot check out the same branch as in the
> >  current repo, _or some other workdir of it_, and
> 
> Since you can checkout any branch you like once you have the workdir, 
> this is really an artificial limitation - you are protected when you 
> create the workdir, but not after.

Well, it is not really an artificial limitation.  IMHO it is much more 
likely that you keep in mind what you should not do, when you have to work 
around such a limitation if you really want to do.

> If you want to have a workdir for an exisiting branch then you have to create
> a new one, and then switch it over.  That seems like a really big usability
> wart to me ... certainly it would make the option pretty much useless to me.
> My original motivation for the new-workdir script was to give me the ability
> to flatten out branches from a single repo for when I'm working on multiple
> branches at the same time.

Nowadays, we have separate remotes layout by default.  (Indeed, you cannot 
even disable it, as I found out recently).  Which means that you already 
have to branch off your local branch.  So the consequences are lesser.

> > - to have finer grained lock control, as well as respecting has_symlinks.
> 
> Not really sure what this means, since I am too tired to have read the 
> actual patch - is it referring to the fact that checkout is shell rather 
> than C?  If so, surely that is not really a good justification for 
> putting the option in the "wrong" command?

Well, I am really not interested in shooting myself in the foot, and 
having that option in checkout would make that much more likely.  So I 
really, really want to have this in git-branch.

Once git-checkout is builtin, we can still come back and add this option 
to git-checkout (with a big fat red warning, to be sure); it is not like 
we have git-branch and git-checkout functionality well separated...

Ciao,
Dscho

^ permalink raw reply

* Re: [PATCH] fsck --lost-found: write blob's contents, not their SHA-1
From: Johannes Schindelin @ 2007-07-22 21:52 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vodi4qfnx.fsf@assigned-by-dhcp.cox.net>

Hi,

On Sun, 22 Jul 2007, Junio C Hamano wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > When looking for a lost blob, it is much nicer to be able to grep
> > through .git/lost-found/other/* than to write an inefficient loop
> > over the file names.  So write the contents of the dangling blobs,
> > not their object names.
> 
> I think this is an idea to solve a good problem, but if we go
> this route, the need for us to worry about expiring lost-found
> entries would become more urgent, I suspect.

Why?  AFAICT lost+found/ has to be cleaned by people.  So if you look for 
something, you say "git fsck --lost-found", and once you found it, it's 
time for "rm -rf .git/lost-found".

> And when you think about expiring lost-found entries, another
> possible solution emerges.  If we teach git-prune to remove the
> corresponding entry from lost-found/other when it removes a
> loose blob from the object store, then we can easily and safely
> do this instead:
> 
> 	$ cat .git/lost-found/other/* |
> 	  xargs -r git grep 'the word to look for'

Well, it is not only for grepping.  In my case, I could get away by this:

$ ls -lrt $(grep -l filter_subdir .git/lost-found/other/* |
	sed "s/^.*other\/\(..\)/.git\/objects\/\1\//") 

IOW I found the loose dangling objects which matched a keyword, and sorted 
them by time.

In other cases, though, I wanted to see the size.

But what the whole thing boils down to: After finding dangling objects, 
you are much more likely using git tools on non-blobs than on blobs, and 
vice versa.

Ciao,
Dscho

P.S.: I fully forgot to mention that happily, I did "git add -u" sometime 
before "git reset --hard", otherwise I would have been lost.

^ permalink raw reply

* Re: [PATCH 3/3] Teach "git branch" about --new-workdir
From: Julian Phillips @ 2007-07-22 21:50 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, gitster
In-Reply-To: <Pine.LNX.4.64.0707222223460.14781@racer.site>

On Sun, 22 Jul 2007, Johannes Schindelin wrote:

> Hi,
>
> On Sun, 22 Jul 2007, Julian Phillips wrote:
>
>> On Sun, 22 Jul 2007, Johannes Schindelin wrote:
>>
>>> 	IMHO this is a better syntax than what is in contrib/, and "git
>>> 	branch" is probably the right place for such a thing, from a
>>> 	user's perspective.
>>
>> Surely checkout would make more sense than branch?  You are effectively
>> checking out into a new directory ... also you may want to get an
>> existing branch (certainly most of my usage of new-workdir is checking
>> out existing branches, e.g. to look at - as in build and play with - an
>> interesting branch that someone else has pushed out).
>
> My rationale here was:
>
> - to make sure that the user cannot check out the same branch as in the
>  current repo, _or some other workdir of it_, and

Since you can checkout any branch you like once you have the workdir, 
this is really an artificial limitation - you are protected when you 
create the workdir, but not after.  This seems more likely to bite 
someone than just making them aware of the pitfalls before hand.  One of 
the main reasons that I aimed for contrib rather than proper git 
originally was the ability to do bad things with realising.

If you want to have a workdir for an exisiting branch then you have to 
create a new one, and then switch it over.  That seems like a really big 
usability wart to me ... certainly it would make the option pretty much 
useless to me.  My original motivation for the new-workdir script was to 
give me the ability to flatten out branches from a single repo for when 
I'm working on multiple branches at the same time.

> - to have finer grained lock control, as well as respecting has_symlinks.

Not really sure what this means, since I am too tired to have read the 
actual patch - is it referring to the fact that checkout is shell rather 
than C?  If so, surely that is not really a good justification for putting 
the option in the "wrong" command?

-- 
Julian

  ---
Thud. Thud. Thud. Splat.
         -- (Terry Pratchett & Neil Gaiman, Good Omens)

^ permalink raw reply

* Re: Git tree for old kernels from before the current tree
From: Jan Engelhardt @ 2007-07-22 21:46 UTC (permalink / raw)
  To: Paul Mundt; +Cc: Jon Smirl, Git Mailing List, lkml
In-Reply-To: <20070722211314.GA13850@linux-sh.org>


On Jul 23 2007 06:13, Paul Mundt wrote:
>> 
>Err, that's crap. Have you even looked at gitweb? There's at least:
>
>git://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git
>This has trees all the way back to 2.5.0.
>
>and Linus also has:
>
>git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/old-2.6-bkcvs.git
>
>for the BK history.

Oh I did not know that. I don't glance too much around in git://git.kernel.org/
because there are sooo many :)



	Jan
-- 

^ permalink raw reply

* Re: A simpler approach to tracking directories
From: J. Bruce Fields @ 2007-07-22 21:45 UTC (permalink / raw)
  To: David Kastrup; +Cc: Theodore Tso, git
In-Reply-To: <85myxoi8fi.fsf@lola.goethe.zz>

On Sun, Jul 22, 2007 at 08:45:37PM +0200, David Kastrup wrote:
> Theodore Tso <tytso@mit.edu> writes:
> > One of the fundamental things which falls out of the "Git Tracks
> > Contents" mantra is that information which you expect to be pushed
> > forward future revisions (as opposed to metadata which is specific
> > to a commit, such as the Author and Committer of a patch, the Commit
> > log, etc.) *MUST* be information which is realized in the working
> > tree.
> 
> For every _file_ in the working tree, there is _one_ bit of
> information in the repository that is not in the working tree: namely
> whether git is tracking this file at all.

Actually, no.  The index just stores data for a given set of files.  You
can think of the set of files for which data is stored in the index as
the set that is "tracked", but there's no independent
"tracked/untracked" bit, and no way of marking a path as tracked without
also adding content at that path.

--b.

^ permalink raw reply

* Re: Test #7 in t9200-git-cvsexportcommit fails
From: Alex Riesen @ 2007-07-22 21:42 UTC (permalink / raw)
  To: Jason Sewall; +Cc: git, Junio C Hamano
In-Reply-To: <31e9dd080707221349g40ff050bue72733f270822603@mail.gmail.com>

Jason Sewall, Sun, Jul 22, 2007 22:49:19 +0200:
> >It'll never work, cvs does not commit changes made during one second,
> >as they have the same timestamp.
> 
> Why not add a delay in there, like this:
> 
> id=$(git rev-list --max-count=1 HEAD) && sleep 2
> 
> It makes the test work for me.

It looks just like another reason to disable it. It is not like the
_git_ functionality which is used by git-cvsexportcommit is not
already tested elsewhere.

Note that I do _NOT_ suggest disabling it by default, but a bit of
warning would probably do some good:

diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index 4efa0c9..e5e9e36 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -6,6 +6,12 @@ test_description='CVS export comit. '
 
 . ./test-lib.sh
 
+echo >&2
+echo >&2 "This test is very timing dependent and may fail."
+echo >&2 "If you don't need git-cvsexportcommit you're better off"
+echo >&2 "disabling it: make test GIT_SKIP_TESTS=t9200"
+echo >&2
+
 cvs >/dev/null 2>&1
 if test $? -ne 1
 then

^ permalink raw reply related

* Re: [PATCH] fsck --lost-found: write blob's contents, not their SHA-1
From: Junio C Hamano @ 2007-07-22 21:42 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git
In-Reply-To: <Pine.LNX.4.64.0707222120100.14781@racer.site>

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> When looking for a lost blob, it is much nicer to be able to grep
> through .git/lost-found/other/* than to write an inefficient loop
> over the file names.  So write the contents of the dangling blobs,
> not their object names.

I think this is an idea to solve a good problem, but if we go
this route, the need for us to worry about expiring lost-found
entries would become more urgent, I suspect.

And when you think about expiring lost-found entries, another
possible solution emerges.  If we teach git-prune to remove the
corresponding entry from lost-found/other when it removes a
loose blob from the object store, then we can easily and safely
do this instead:

	$ cat .git/lost-found/other/* |
	  xargs -r git grep 'the word to look for'

^ permalink raw reply

* Re: Git help for kernel archeology, suppress diffs caused by CVS keyword expansion
From: Johannes Schindelin @ 2007-07-22 21:39 UTC (permalink / raw)
  To: Jon Smirl; +Cc: Git Mailing List
In-Reply-To: <9e4733910707221248q45fb3aaala9c79afd4b09830e@mail.gmail.com>

Hi,

On Sun, 22 Jul 2007, Jon Smirl wrote:

> On 7/22/07, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:
> > But still, I think that it would be much better not to put this into Git.
> > We do have diff gitattributes now, so that you can roll your own diff for
> > specific files, but I still think that this is more a task for a
> > standalone perl script.  Possibly being called from filter-branch to be
> > done with the conversion once and for all times.
> 
> I can provide sample diffs if you want something to play with.

As you already did, this is my attempt at a perl script...  Feel free to 
bash my Perl capabilities, or to correct it...

Ciao,
Dscho

-- snipsnap --
#!/usr/bin/perl

sub init_hunk {
	$_ = $_[0];
	$current_hunk = "";
	$current_hunk_header = $_;
	($start_minus, $dummy, $start_plus, $dummy) =
		/^\@\@ -(\d+)(,\d+|) \+(\d+)(,\d+|) \@\@/;
	$plus = $minus = $space = 0;
	$skip_logs = 0;
}

sub flush_hunk {
	if ($plus > 0 || $minus > 0) {
		if ($current_file ne "") {
			print $current_file;
			$current_file = "";
		}
		$minus += $space;
		$plus += $space;
		print "\@\@ -$start_minus,$minus "
			. "+$start_plus,$plus \@\@\n";
		print $current_hunk;
	}
}

sub check_file {
	$_ = $_[0];
	$current_file = $_;
	while (<>) {
		if (/^\@\@/) {
			last;
		}
		$current_file .= $_;
	}

	init_hunk $_;

	# check hunks
	while (<>) {
		if ($skip_logs && /^\+ *\*/) {
			# do nothing
		} elsif (/^\@\@.*/) {
			flush_hunk;
			init_hunk $_;
		} elsif (/^diff/) {
			flush_hunk;
			return;
		} elsif (/^-.*\$(Id|Revision|Author|Date).*\$/) {
			$key = $1;
			s/^-/ /;
			$current_hunk .= $_;
			$space++;
			$_ = <>;
			if (!/\+.*\$Id.*\$/) {
				die "Expected some changed \$$key line: $_";
			}
			$skip_logs = 0;
		} elsif (/^ .*\$Log.*\$/) {
			$current_hunk .= $_;
			$space++;
			$skip_logs++;
		} elsif (/^ /) {
			$current_hunk .= $_;
			$space++;
			$skip_logs = 0;
		} elsif (/^\+/) {
			$current_hunk .= $_;
			$plus++;
		} elsif (/^-/) {
			$current_hunk .= $_;
			$minus++;
			$skip_logs = 0;
		} else {
			die "Unexpected line: $_";
		}
	}
}

while (<>) {
	if (/^diff/) {
		do {
			check_file $_;
		} while(/^diff/);
	} else {
		printf $_;
	}
}

^ permalink raw reply

* Re: Empty directories...
From: David Kastrup @ 2007-07-22 21:35 UTC (permalink / raw)
  To: git
In-Reply-To: <7vhco28aoq.fsf@assigned-by-dhcp.cox.net>


Coming full circle...

Junio C Hamano <gitster@pobox.com> writes:

> The right approach to take probably would be to allow entries of
> mode 040000 in the index.  Traditionally, we allowed only 100644
> (blobs as regular files) and 120000 (blobs as symlinks).  We
> recently added 160000 (commit from outer space, aka subproject).
>
> And we do that for all directories, not just empty ones.  So if
> you have fileA, empty/, sub/fileB tracked, your index would
> probably have these four entries, immediately after read-tree
> of an existing tree object:
>
> 	100644 15db6f1f27ef7a... 0	fileA
> 	040000 4b825dc642cb6e... 0	empty
> 	040000 e125e11d3b63e3... 0	sub
> 	100644 52054201c2a872... 0	sub/fileB

This would be very much what I am proposing now, except that instead
of 040000 we would have 040755 usually, so that when the index makes
it into the repository where 040000 already has a meaning (a
disappear-when-empty tree) we get the right information.  Also note
that the above comes about when doing
git-add *
but not when doing
git-add fileA empty sub/fileB (in the latter case, the entry for sub
                               would be missing)

> If you add sub/fileC, with "update-index" (and "add"), you
> invalidate the SHA-1 object name you stored for "sub" (because
> there is no point recomputing the tree object until you know you
> need a subtree for "sub" part, which does not happen until the
> next "write-tree"), and end up with something like:
>
> 	100644 15db6f1f27ef7a... 0	fileA
> 	040000 4b825dc642cb6e... 0	empty
> 	040000 00000000000000... 0	sub
> 	100644 52054201c2a872... 0	sub/fileB
> 	100644 705bf16c546f32... 0	sub/fileC
>
> These "missing" SHA-1 would need to be recomputed on-demand.

Ah, ok.  Does it even make sense to compute the SHA-1 values in the
index in advance?  What would they be useful for?

> We have had necessary infrastructure to do this "keeping
> untouched tree object names in the index" for quite some time,
> but it is not a part of the index proper (it is stored in an
> extension section in the index file, to keep the index
> compatible with older versions of git).

What is the application for which this is being used?

> Having made it sound so easy, here are the issues I would expect
> to be nontrivial (but probably not rocket surgery either).
>
>  * unpack-trees, which is the workhorse for twoway merge (aka
>    "switching branches") and threeway merge, has a convoluted
>    logic to avoid D/F conflicts; it can probably be cleaned up
>    once we do the above conversion so that the index starts
>    saying "Hey, I have a directory here" more explicitly.  The
>    end result would probably be a code easier to follow.

I am afraid that this is unlikely to happen, and that is because
directory tracking remains optional at a fundamental level as long as
we want to support the current behavior as an option.  However, one
could conceivably add 040000 entries (rather than 040755) for
directories that have not been passed into tracking but are required
by git, if this simplifies matters.  But it sounds like something that
might complicate working with several different git versions on the
same index.

>  * status, update-index --refresh, and diff-files cares about
>    the information cached in the index from the last time
>    lstat(2) is run on each entry.  What we should store there
>    for "tree" entries is very unclear to me, but probably we
>    should teach them to ignore the stat-matching logic for
>    these entries.

At the current point of time, git tracks just the u+x bit for normal
files, and for directories, there is really nothing worth tracking as
long as no attempt of restoring more mode bits is done.  Modification
times are probably a bit too risky to pay attention to.

>  * diff-index walks the index and a tree in parallel but does
>    not currently expect to see a tree object in the index.  It
>    needs to be taught to ignore these "tree" entries.

Or do something sensible when comparing.  Understood.

>  * merge-recursive and merge-index walk the index, coming up
>    with the merge results one path at a time.  They also need to
>    be taught to ignore these "tree" entries.

Same here.

>  * diff-index and "read-tree -m" should be taught to take
>    advantage of the "tree" entries in the index.  For example,
>    if diff-index finds the "tree" entry in the index and the
>    subtree found from the tree object exactly match, it does not
>    even have to descend into the tree, which would be a huge
>    performance win (because you do not have to open the subtree
>    and its subtrees from the tree side; you already have read
>    everything on the index side, and still have to skip the
>    entries in the directory).  "read-tree -m" also should be
>    able to optimize two identical subtrees in the 2 or 3 trees
>    involved.
>
>    Even if we follow the "lazy invalidate" strategy to maintain
>    the "tree" entries in the normal codepath, we could have a
>    special operation that says "now update all the tree entries
>    by recomputing the tree object names as needed".  Perhaps we
>    might want to initiate such an operation before "read-tree
>    -m" automatically.

Over my head, but it would appear that it can safely left for later.

-- 
David Kastrup, Kriemhildstr. 15, 44793 Bochum

^ permalink raw reply

* Re: diffutils: C labels misdetected as functions
From: Junio C Hamano @ 2007-07-22 21:30 UTC (permalink / raw)
  To: Simon Arlott
  Cc: Paul Eggert, Matthew Wilcox, Denis Cheng, kernel-janitors,
	linux-kernel, git
In-Reply-To: <46A3AD07.1070504@simon.arlott.org.uk>

Simon Arlott <simon@fire.lp0.eu> writes:

> On 22/07/07 19:38, Paul Eggert wrote:
>> 
>> Anyway, to work around your problem without changing "diff", you can
>> use "diff -u -F '^[[:alpha:]$_](|.*[^:])$'" instead of "diff -u -p".
>
> Perhaps git could do this (or similar) as a workaround? (Cc:ed)
>
>> Or you can put a single space before the labels in question: they'll
>> still stand out plenty.
>
> The code should definitely not need to be modified to work around a
> diff bug.

I think the word "bug" is just about documentation.  Promising
"show C function" and including labels and/or not showing all
functions would be a bug, but given that diff is about showing
textual differences between two text files with arbitrary kind
of contents, I do not think it is reasonable to take that "show
C function" promise too literally.

To avoid this, the description of the feature could be reworded
to "shows lines that match pattern '^[[:alpha:]$_]' on context
lines, intent of the option being that the pattern tends to
match the beginning of function in C language and help the
reader to locate the context more easily, with a caveat that the
pattern matching is not meant to capture all functions nor only
functions --- there will be false matches."

But that would be too long.  And as Paul showed, you can narrow
or widen the set of lines that are shown with the -F option.  I
do not think this deserves to be called a "bug".

You can define your own customized "function name" pattern in
your .git/config (or $HOME/.gitconfig) file to be used by the
git-diff family with recent enough git via the attributes
mechanism, but I do not think we should change the default,
unless GNU diff changes its default.  Personally I'd rather not
see it change, though.

^ permalink raw reply

* Re: [PATCH 3/3] Teach "git branch" about --new-workdir
From: Johannes Schindelin @ 2007-07-22 21:25 UTC (permalink / raw)
  To: Julian Phillips; +Cc: git, gitster
In-Reply-To: <Pine.LNX.4.64.0707222205050.23426@reaper.quantumfyre.co.uk>

Hi,

On Sun, 22 Jul 2007, Julian Phillips wrote:

> On Sun, 22 Jul 2007, Johannes Schindelin wrote:
> 
> > 	IMHO this is a better syntax than what is in contrib/, and "git
> > 	branch" is probably the right place for such a thing, from a
> > 	user's perspective.
> 
> Surely checkout would make more sense than branch?  You are effectively 
> checking out into a new directory ... also you may want to get an 
> existing branch (certainly most of my usage of new-workdir is checking 
> out existing branches, e.g. to look at - as in build and play with - an 
> interesting branch that someone else has pushed out).

My rationale here was:

- to make sure that the user cannot check out the same branch as in the 
  current repo, _or some other workdir of it_, and

- to have finer grained lock control, as well as respecting has_symlinks.

Ciao,
Dscho

^ permalink raw reply

* Re: [RFC PATCH] Re: Empty directories...
From: David Kastrup @ 2007-07-22 21:08 UTC (permalink / raw)
  To: git
In-Reply-To: <?= =?ISO-8859-1?Q?alpine.LFD.0.999?= =?ISO-8859-1?Q?.070718=041710271.?= =?ISO-8859-1?Q?27353@woody.linu?= =?ISO-8859-1?Q?x-foundation.org?= =?ISO-8859-1?Q?>


Well, coming back to this posting in order to focus on some points
that were at a level more relevant to the implementation.  And I'll go
through the questions assuming my permissions-based proposal.

Linus Torvalds <torvalds@linux-foundation.org> writes:

> On Thu, 19 Jul 2007, David Kastrup wrote:
>> 
>> Well, kudos.  Together with the analysis from Junio, this seems like a
>> good start.  Would you have any recommendations about what stuff one
>> should really read in order to get up to scratch about git internals?
>
> Well, you do need to understand the index. That's where all the new 
> subtlety happens.
>
> The data structures themselves are trivial, and we've supported
> empty trees (at the top level) from the beginning, so that part is
> not anything new.
>
> However, now having a new entry type in the index (S_IFDIR) means
> that anything that interacts with the index needs to think
> twice. But a lot of that is just testing what happens, and so the
> first thing to do is to have a test-suite.

Yes.

> There's also the question about how to show an empty tree in a
> diff.

Well, there are two possibilities involved here, a more and a less
chatty one.  Assuming that we want to do as little work as possible,
the transition between a tracked and a non-tracked directory will be
given in one of the following manners:

Either:
a) xxx: old mode 000000
   xxx: new mode 040755

when a directory gets tracked and

   xxx: new mode 040755
   xxx: old mode 000000

when it gets untracked again.

or
b)
   xxx: new directory mode 040755

when a directory gets tracked and

   xxx: deleted directory mode 040755

when it gets untracked again.  Note that "new" does not mean that git
did not previously have had files that absolutely have required a
directory for placing.  It just means that it has now actively gained
knowledge about the directory.

In a similar vein, "deleted" means that git is just deleting its
knowledge about the directory, _scheduling_ it for a single deletion
attempt at the earliest (and actually also latest) opportunity: when
git happens to know about no more files that require keeping the
directory around.  So perhaps the following would be more readable:

   xxx: tracking directory mode 040755

   xxx: forgetting directory mode 040755

Now in order to cut down on the verbiage, it might be an option to
transmit those strings only when something happens that can't be
deduced from other data.  Because _if_ it can be deduced from other
data (like a directory being present when files in it are), then at
least the working copies are identical as long as both persons don't
start deleting files from the repository.  If they do so, when a
directory becomes empty, the other side needs to know whether the
directory is being tracked or not if it still wants to maintain the
same state in the working tree.  But if we really want to have not
just the working tree but also the repositories in SHA1-lockstep, we
can't delay transmitting this information.

> We've never had that: the only time we had empty trees was when we
> compared a totally empty "root" tree against another tree, and then
> it was obvious.  But what if the empty tree is a subdirectory of
> another tree - how do you express that in a diff? Do you care? Right
> now, since we always recurse into the tree (and then not find
> anything), empty trees will simply not show up _at_all_ in any
> diffs.

One would still recurse.

> And what about usability issues elsewhere? With my patch, doing something 
> like a
>
> 	git add directory/
>
> still won't do anything, because the behaviour of "git add" has always 
> been to recurse into directories.

This will remain the same, but the directory itself will be added if
and only if the corresponding preference variable is set, regardless
of whether the directory is empty.

> So to add a new empty directory, you'd have to do
>
> 	git update-index --add directory
>
> and that's not exactly user-friendly.

Presumably one could, if one really wanted an explicit way, have
git add --directory directory
in analogy to the --directory option of the ls command.  But I think
that in most cases one would not want to treat one directory different
from the whole tree, so the implicit behavior regulated by a
project-wide preference should be sufficient in general.

> So do you add a "-n" flag to "git add" to tell it to not recurse? Or
> do you always recurse, but then if you notice that the end result is
> empty, you add it as a directory?

I always recurse (unless there is a --directory option and I have some
strange desire to actually use it).  I add it as a directory,
regardless of whether it is empty or not, if my preference setting (or
gitignore or whatever) is set to tracking directories.

-- 
David Kastrup, Kriemhildstr. 15, 44793 Bochum

^ permalink raw reply

* Re: Git tree for old kernels from before the current tree
From: Paul Mundt @ 2007-07-22 21:13 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Jon Smirl, Git Mailing List, lkml
In-Reply-To: <Pine.LNX.4.64.0707222257540.32367@fbirervta.pbzchgretzou.qr>

On Sun, Jul 22, 2007 at 11:00:15PM +0200, Jan Engelhardt wrote:
> On Jul 22 2007 16:49, Jon Smirl wrote:
> > Continuing on with kernel archeology for embedded systems, any
> > interest in making a git tree with all of the kernel versions from the
> > beginning up to the start of the current git tree?
> 
> Well, it would be cool if history was somehow available (I recognize
> this would be a lot of work). One currently has to go through the
> bk repo if something is to be searched for in that timeframe.
> 
Err, that's crap. Have you even looked at gitweb? There's at least:

git://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git

This has trees all the way back to 2.5.0.

and Linus also has:

git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/old-2.6-bkcvs.git

for the BK history.

That's already more history than most people have any immediate use for,
going beyond that starts getting in to general completeness before
practical application territory.

Anyone still sending 2.4 patches with the intent of them being moved
forward and applied to a current kernel needs to be killfiled.

^ permalink raw reply

* Re: [PATCH 3/3] Teach "git branch" about --new-workdir
From: Julian Phillips @ 2007-07-22 21:09 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, gitster
In-Reply-To: <Pine.LNX.4.64.0707221956210.14781@racer.site>

On Sun, 22 Jul 2007, Johannes Schindelin wrote:

>
> Inspired by contrib/workdir/git-new-workdir, the flag --new-workdir (or
> its shortcut, -n) can be used to create a new working directory for the
> newly created branch.  All information, except which branch is checked
> out (and therefore also the index), will be symlinked from the current
> repository.
>
> Example:
>
> 	$ git branch -n ~/my-new-topic xy-problem
>
> will create a branch called "xy-problem", which is initially identical
> to the current branch, and check out the new branch in ~/my-new-topic/.
> You will be able to cherry-pick from, log and diff with the branch
> "xy-problem" in the current repository, since most of the metadata is
> shared.
>
> Conversely, you can access all the branches in the current repository
> from ~/my-new-topic/, too.
>
> A word of warning: switching to _same_ branch that is checked out in
> the other repository is asking for trouble.  You are really working not
> only on the same object database, but also the same (i.e. not copied)
> refs namespace.
>
> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
> ---
> 	IMHO this is a better syntax than what is in contrib/, and "git
> 	branch" is probably the right place for such a thing, from a
> 	user's perspective.

Surely checkout would make more sense than branch?  You are effectively 
checking out into a new directory ... also you may want to get an existing 
branch (certainly most of my usage of new-workdir is checking out existing 
branches, e.g. to look at - as in build and play with - an interesting 
branch that someone else has pushed out).

Definitely in favour of moving this into git proper though.

-- 
Julian

  ---
Lack of capability is usually disguised by lack of interest.

^ permalink raw reply

* Re: Git tree for old kernels from before the current tree
From: Jan Engelhardt @ 2007-07-22 21:00 UTC (permalink / raw)
  To: Jon Smirl; +Cc: Git Mailing List, lkml
In-Reply-To: <9e4733910707221349s462aa11bj714956f7cdc72aac@mail.gmail.com>


On Jul 22 2007 16:49, Jon Smirl wrote:
>
> Continuing on with kernel archeology for embedded systems, any
> interest in making a git tree with all of the kernel versions from the
> beginning up to the start of the current git tree?

Well, it would be cool if history was somehow available (I recognize
this would be a lot of work). One currently has to go through the
bk repo if something is to be searched for in that timeframe.

> No history in the
> tree, just a simple way to quickly fetch and select a copy of all the
> various old releases. I know they are all available ftp, git tree
> would organize them all in one place and let you fetch them all at
> once.

In fact, we could bisect on it. Though not sure how useful that is
with really old versions :)

> If this is small enough you could add it to the current kernel tree.
> Git's super diffing performance might make this fairly small.
>

	Jan
-- 

^ permalink raw reply

* Git tree for old kernels from before the current tree
From: Jon Smirl @ 2007-07-22 20:49 UTC (permalink / raw)
  To: Git Mailing List, lkml

Continuing on with kernel archeology for embedded systems, any
interest in making a git tree with all of the kernel versions from the
beginning up to the start of the current git tree? No history in the
tree, just a simple way to quickly fetch and select a copy of all the
various old releases. I know they are all available ftp, git tree
would organize them all in one place and let you fetch them all at
once.

If this is small enough you could add it to the current kernel tree.
Git's super diffing performance might make this fairly small.

My dream system lets me checkout an ancient version, apply the diff
from the vendor (or expand a tarball), use git status to see what
files changed, and git diff to get detailed changes (all minus CVS
keyword expansion noise). Major bonus points if it can detect detect a
subsystem that has been backported.

-- 
Jon Smirl
jonsmirl@gmail.com

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox