Git development
 help / color / mirror / Atom feed
* Re: [PATCH] Add 'ours' merge strategy.
From: Johannes Schindelin @ 2005-11-02 10:35 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vll071ug5.fsf@assigned-by-dhcp.cox.net>

Hi,

On Tue, 1 Nov 2005, Junio C Hamano wrote:

>  This can be used to terminate an old maintenance branch without
>  leaving people's repositories behind.

How about optionally do something similar when git-rebase'ing? Especially 
"pu"?

Ciao,
Dscho

^ permalink raw reply

* Re: [PATCH gitweb] Visually indicating patch size with horizontal bars
From: Johannes Schindelin @ 2005-11-02 10:37 UTC (permalink / raw)
  To: Andreas Ericsson; +Cc: git
In-Reply-To: <43687414.1030702@op5.se>

Hi,

On Wed, 2 Nov 2005, Andreas Ericsson wrote:

> Color-blindness doesn't work like that. There are no "red-color-blind" 
> people.

I do exist. I have problems focusing on red text or objects. Agreed, it is 
no "blindness", but it is not too seldom either.

Ciao,
Dscho

^ permalink raw reply

* Re: [PATCH gitweb] Visually indicating patch size with horizontal bars
From: Andreas Ericsson @ 2005-11-02 12:19 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git
In-Reply-To: <Pine.LNX.4.63.0511021135450.6501@wbgn013.biozentrum.uni-wuerzburg.de>

Johannes Schindelin wrote:
> Hi,
> 
> On Wed, 2 Nov 2005, Andreas Ericsson wrote:
> 
> 
>>Color-blindness doesn't work like that. There are no "red-color-blind" 
>>people.
> 
> 
> I do exist. I have problems focusing on red text or objects. Agreed, it is 
> no "blindness", but it is not too seldom either.
> 

Is that irrespective of background color?

-- 
Andreas Ericsson                   andreas.ericsson@op5.se
OP5 AB                             www.op5.se
Tel: +46 8-230225                  Fax: +46 8-230231

^ permalink raw reply

* Re: [PATCH gitweb] Visually indicating patch size with horizontal bars
From: Johannes Schindelin @ 2005-11-02 12:43 UTC (permalink / raw)
  To: Andreas Ericsson; +Cc: git
In-Reply-To: <4368AEBB.6080609@op5.se>

Hi,

On Wed, 2 Nov 2005, Andreas Ericsson wrote:

> Johannes Schindelin wrote:
> > 
> > On Wed, 2 Nov 2005, Andreas Ericsson wrote:
> > 
> > 
> > > Color-blindness doesn't work like that. There are no "red-color-blind"
> > > people.
> > 
> > 
> > I do exist. I have problems focusing on red text or objects. Agreed, it is
> > no "blindness", but it is not too seldom either.
> > 
> 
> Is that irrespective of background color?

Mostly. (I don't remember the exact outcome of the test, but I am 
definitely not color blind).

Ciao,
Dscho

^ permalink raw reply

* remove CR/LF from .gitignore
From: Alex Riesen @ 2005-11-02 13:05 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 397 bytes --]

For everyone cursed by dos/windows line endings (aka CRLF):

The code reading the .gitignore files (excludes and excludes per
directory) leaves \r in the patterns, which causes fnmatch to fail for
no obvious reason. Just remove a "\r" preceding a "\n"
unconditionally.

---

Junio, sorry for attachments. Have no other way to it on gmail here at
work. The other patch is just the test.

[-- Attachment #2: msdos-excludes.patch --]
[-- Type: application/xxxxx, Size: 401 bytes --]

[-- Attachment #3: msdos-excludes-test.patch --]
[-- Type: application/xxxxx, Size: 648 bytes --]

^ permalink raw reply

* Re: hgmq vs. StGIT
From: Chris Mason @ 2005-11-02 15:41 UTC (permalink / raw)
  To: Catalin Marinas
  Cc: Chuck Lever, Petr Baudis, Theodore Ts'o, Joel Becker,
	Junio C Hamano, git
In-Reply-To: <b0943d9e0511011330v7045c568u@mail.gmail.com>

On Tue, Nov 01, 2005 at 09:30:06PM +0000, Catalin Marinas wrote:
> On 01/11/05, Chris Mason <mason@suse.com> wrote:
> > I would suggest just putting the .git/patches directory under revision
> > control.
> 
> Putting them under a separate revisions repository, i.e. having a
> .git/patches/,git directory? Otherwise there would be some problems
> with the modified files automatically included in a patch.
> 
> > More importantly, you can reuse gitk and all of the other history
> > functionality in the SCM.
> 
> Doing it the above way wouldn't be of much help with gitk. You would
> get files like .git/patches/master/patchX/top etc. under revision
> control which only contain some hash strings, not meaningful. With GIT
> you have the advantage of being able to specify the DAG structure. It
> is pretty simple to just link the commit objects corresponding to a
> patch into a DAG and using gitk would allow you to navigate through
> the history and also look at the diff itself.

I think we're talking past each other a little, partially because
I'm not sure exactly what features you want from revision control on the
patches.

But, my suggestion is to remember that once you add some sort of
revision control, people are going to want all of the features they are
used to with git/hg/their favorite SCM.  You'll probably get better
results if you patch git to your needs then if you try to reimplement
things all over again. 

-chris

^ permalink raw reply

* Re: remove CR/LF from .gitignore
From: Linus Torvalds @ 2005-11-02 16:02 UTC (permalink / raw)
  To: Alex Riesen; +Cc: git, Junio C Hamano
In-Reply-To: <81b0412b0511020505u1b6b7649l924e252d561b8e9e@mail.gmail.com>



On Wed, 2 Nov 2005, Alex Riesen wrote:
>
> For everyone cursed by dos/windows line endings (aka CRLF):

Btw, it would be good if somebody verified that the .git/config file also 
works with CR/LF. 

I'm pretty sure "\" at the end of line will break, but I didn't care 
enough. And maybe nobody does. Normal config files _should_ work, but 
you can guess how eager I'm to test it ;)

		Linus

^ permalink raw reply

* git 0.99.9b installed on kernel.org
From: H. Peter Anvin @ 2005-11-02 16:52 UTC (permalink / raw)
  To: Git Mailing List

git 0.99.9b is now installed on all kernel.org machines.

	-hpa

^ permalink raw reply

* [PATCH] rev-list: make --max- and --min-age a bit more usable.
From: Junio C Hamano @ 2005-11-02 19:02 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: git
In-Reply-To: <Pine.LNX.4.64.0510301838110.27915@g5.osdl.org>

Linus Torvalds <torvalds@osdl.org> writes:

> I've several times been surprised to see people not realize that
> "git-whatchanged" takes a file list to limit the files it is interested 
> in. I also suspect people don't realize that you can limit it by time and 
> version and file list, all at the same time.

The current "time based limiting" is not very user friendly, so
people not knowing the limit-by-time is not a surprise.

> 	git-whatchanged -p --pretty=short --since="2 weeks ago" v0.99.8..v0.99.9 Makefile
>
> is a valid query

Well, it is not a valid query ;-) Nobody implemented --since
yet, but you could spell it --max-age.  It would not grok "2
weeks ago" though.

With the attached patch, you could at least do:

	git log --max-age='2005-10-25' v0.99.8..v0.99.9 Makefile

There are still a couple of things that bothers me.

(1) The underlying workhorse, rev-list, takes max-age and
    min-age.  While these names are logically correct, it feels
    a bit hard and counterintuitive when deciding which one to
    use in order to ask "what are the ones that happened after
    Wednesday last week?".  The query talks about the commits
    being young, so --max-age=2005-10-26 is the right query
    (i.e. "I want to discard things that are older than that
    time"), but as soon as I type "max", my mind starts
    comparing date strings, and surely 2005-10-21 is smaller
    than 2005-10-26 and I am saying 2005-10-26 is the max, which
    confuses me to think that 2005-10-21 would be included in
    the result (it would not be -- we are talking about age, so
    2005-10-21 one is older, which means its age is greater than
    specified). It takes some mental effort to do this, at least
    for me.

    I *hate* to suggest this change at this late stage of the
    game, but maybe they should be renamed or at least acquire 
    less confusing synonyms, perhaps?

	--max-age	= --min-timestamp, --since
        --min-age	= --max-timestamp, --until

(2) If I run the above --max-age query, the last commit
    displayed is this one:

        commit f3123c4ab3d3698262e59561ac084de45b10365a
        Author: Junio C Hamano <junkio@cox.net>
        Date:   Sat Oct 22 01:28:13 2005 -0700

    This is because the age limit uses commit date (which is a
    sensible thing to do) while the display shows author date.
    To an uninitiated, this takes some explanation and
    justification (i.e. counterintuitive again).

    Incidenally, I have not found a way to prettyprint the
    commit date; --pretty=raw gives that information, but in
    really raw format.  --pretty=full does not even give any
    timestamp.

    Again I *hate* to suggest this, but maybe --pretty=full
    should show both author and commit timestamp as well, like
    this?

        commit f3123c4ab3d3698262e59561ac084de45b10365a
        Author: Junio C Hamano <junkio@cox.net>
        A-Date: Sat Oct 22 01:28:13 2005 -0700
        Commit: Junio C Hamano <junkio@cox.net>
        C-Date: Wed Oct 26 12:37:49 2005 -0700


-- >8 -- cut here -- >8 --

Earlier we just did atoi to accept these parameters, which meant
that the user needed to specify the raw UNIX time format to use
them.

This still does not make it accept '2 weeks ago', but at least
it now can take --max-age='2005-10-15', which is a start.

Signed-off-by: Junio C Hamano <junkio@cox.net>

---

 rev-list.c |   19 +++++++++++++++++--
 1 files changed, 17 insertions(+), 2 deletions(-)

applies-to: 0c9683fe37dbc43713faaa15fcce14bfe5621bba
9dc13af3f916803d270d3387032eb0e91b940417
diff --git a/rev-list.c b/rev-list.c
index 6e6ffde..7a73703 100644
--- a/rev-list.c
+++ b/rev-list.c
@@ -712,6 +712,21 @@ static void handle_all(struct commit_lis
 	global_lst = NULL;
 }
 
+static unsigned long getdate(const char *arg, const char *label)
+{
+	char text[80];
+	unsigned long date;
+
+	if (parse_date(arg, text, sizeof(text)) < 0) {
+		/* Maybe handle "4 days ago" and the like here... */
+		die("bad time specification for %s: %s", label, arg);
+	}
+	date = strtoul(text, NULL, 10);
+	if (date == ULONG_MAX)
+		die("unparsable time specification for %s: %s", label, arg);
+	return date;
+}
+
 int main(int argc, const char **argv)
 {
 	const char *prefix = setup_git_directory();
@@ -730,12 +745,12 @@ int main(int argc, const char **argv)
 			continue;
 		}
 		if (!strncmp(arg, "--max-age=", 10)) {
-			max_age = atoi(arg + 10);
+			max_age = getdate(arg + 10, "max-age");
 			limited = 1;
 			continue;
 		}
 		if (!strncmp(arg, "--min-age=", 10)) {
-			min_age = atoi(arg + 10);
+			min_age = getdate(arg + 10, "min-age");
 			limited = 1;
 			continue;
 		}
---
0.99.9.GIT

^ permalink raw reply related

* [PATCH 2/2] Add support for git-http-push to git-push script
From: Nick Hengeveld @ 2005-11-02 19:19 UTC (permalink / raw)
  To: git

Add support for git-http-push to git-push script

Signed-off-by: Nick Hengeveld <nickh@reactrix.com>


---

This probably doesn't make sense until git-http-push has undergone some
testing.

 git-push.sh |    9 +++++++--
 1 files changed, 7 insertions(+), 2 deletions(-)

applies-to: e7d656bb2608ae924a78750bbd3d01bf4f6c2e9a
a374b962104899cfe39b21fc2fbef64d1e1fdbd8
diff --git a/git-push.sh b/git-push.sh
index 5aa6531..edc0b83 100755
--- a/git-push.sh
+++ b/git-push.sh
@@ -46,7 +46,7 @@ esac
 shift
 
 case "$remote" in
-http://* | https://* | git://*)
+git://*)
 	die "Cannot use READ-ONLY transport to push to $remote" ;;
 rsync://*)
         die "Pushing with rsync transport is deprecated" ;;
@@ -57,4 +57,9 @@ test "$has_all" && set x "$has_all" "$@"
 test "$has_force" && set x "$has_force" "$@" && shift
 test "$has_exec" && set x "$has_exec" "$@" && shift
 
-exec git-send-pack "$@"
+case "$remote" in
+http://* | https://*)
+	exec git-http-push "$@";;
+*)
+	exec git-send-pack "$@";;
+esac
---
0.99.9.GIT

^ permalink raw reply related

* [PATCH 1/2] Add support for pushing to a remote repository using HTTP/DAV
From: Nick Hengeveld @ 2005-11-02 19:19 UTC (permalink / raw)
  To: git

Add support for pushing to a remote repository using HTTP/DAV

Signed-off-by: Nick Hengeveld <nickh@reactrix.com>


---

There is still some cleanup to be done here, but it is usable.

To enable support in Apache, load the dav and dav_fs modules, set
DAVLockDB and add "Dav on" to the GIT directory.  The Apache user will
need write permissions in the GIT repository - at least to the refs and
objects directory - so it is probably a good idea to turn on mod_ssl and
the appropriate mod_auth_* module as well.

By default, the push will trust that if a remote has an object, it also
has that object's full history.  This behavior can be changed using the
--complete arg, but it may be appropriate to either never trust the
remote or to change the default behavior.

The push currently sends everything as loose objects, even if they are
stored in a pack in the local repository.  It may make sense to pack all
the necessary objects prior to sending, although there doesn't seem to
be a way to resume a PUT so a failed transfer could prove much more
annoying.

There is an advantage to keeping objects packed on the remote end, as the
remote's pack indexes will be fetched and stored locally and objects stored
in a remote pack will not require a separate HEAD request to check whether
they exist in the remote repository.


 Documentation/git-http-push.txt |   89 ++
 Makefile                        |   13 
 http-push.c                     | 1625 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 1725 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/git-http-push.txt
 create mode 100644 http-push.c

applies-to: b774cd64105bda561bba3c528a7fea19b87755a1
cb4a5d4a797002739ece1ed67204520749324e2d
diff --git a/Documentation/git-http-push.txt b/Documentation/git-http-push.txt
new file mode 100644
index 0000000..c7066d6
--- /dev/null
+++ b/Documentation/git-http-push.txt
@@ -0,0 +1,89 @@
+git-http-push(1)
+================
+
+NAME
+----
+git-http-push - Push missing objects using HTTP/DAV.
+
+
+SYNOPSIS
+--------
+'git-http-push' [--complete] [--force] [--verbose] <url> <ref> [<ref>...]
+
+DESCRIPTION
+-----------
+Sends missing objects to remote repository, and updates the
+remote branch.
+
+
+OPTIONS
+-------
+--complete::
+	Do not assume that the remote repository is complete in its
+	current state, and verify all objects in the entire local
+	ref's history exist in the remote repository.
+
+--force::
+	Usually, the command refuses to update a remote ref that
+	is not an ancestor of the local ref used to overwrite it.
+	This flag disables the check.  What this means is that
+	the remote repository can lose commits; use it with
+	care.
+
+--verbose::
+	Report the list of objects being walked locally and the
+	list of objects successfully sent to the remote repository.
+
+<ref>...:
+	The remote refs to update.
+
+
+Specifying the Refs
+-------------------
+
+A '<ref>' specification can be either a single pattern, or a pair
+of such patterns separated by a colon ":" (this means that a ref name
+cannot have a colon in it).  A single pattern '<name>' is just a 
+shorthand for '<name>:<name>'.
+
+Each pattern pair consists of the source side (before the colon)
+and the destination side (after the colon).  The ref to be
+pushed is determined by finding a match that matches the source
+side, and where it is pushed is determined by using the
+destination side.
+
+ - It is an error if <src> does not match exactly one of the
+   local refs.
+
+ - If <dst> does not match any remote ref, either
+
+   * it has to start with "refs/"; <dst> is used as the
+     destination literally in this case.
+
+   * <src> == <dst> and the ref that matched the <src> must not
+     exist in the set of remote refs; the ref matched <src>
+     locally is used as the name of the destination.
+
+Without '--force', the <src> ref is stored at the remote only if
+<dst> does not exist, or <dst> is a proper subset (i.e. an
+ancestor) of <src>.  This check, known as "fast forward check",
+is performed in order to avoid accidentally overwriting the
+remote ref and lose other peoples' commits from there.
+
+With '--force', the fast forward check is disabled for all refs.
+
+Optionally, a <ref> parameter can be prefixed with a plus '+' sign
+to disable the fast-forward check only on that ref.
+
+
+Author
+------
+Written by Nick Hengeveld <nickh@reactrix.com>
+
+Documentation
+--------------
+Documentation by Nick Hengeveld
+
+GIT
+---
+Part of the gitlink:git[7] suite
diff --git a/Makefile b/Makefile
index 357cb3e..d441b9e 100644
--- a/Makefile
+++ b/Makefile
@@ -6,12 +6,16 @@
 # Define NO_OPENSSL environment variable if you do not have OpenSSL. You will
 # miss out git-rev-list --merge-order. This also implies MOZILLA_SHA1.
 #
-# Define NO_CURL if you do not have curl installed.  git-http-pull is not
-# built, and you cannot use http:// and https:// transports.
+# Define NO_CURL if you do not have curl installed.  git-http-pull and
+# git-http-push are not built, and you cannot use http:// and https://
+# transports.
 #
 # Define CURLDIR=/foo/bar if your curl header and library files are in
 # /foo/bar/include and /foo/bar/lib directories.
 #
+# Define NO_EXPAT if you do not have expat installed.  git-http-push is
+# not built, and you cannot push using http:// and https:// transports.
+#
 # Define NO_STRCASESTR if you don't have strcasestr.
 #
 # Define PPC_SHA1 environment variable when running make to make use of
@@ -223,6 +227,10 @@ ifndef NO_CURL
 		CURL_LIBCURL = -lcurl
 	endif
 	PROGRAMS += git-http-fetch$X
+	ifndef NO_EXPAT
+		EXPAT_LIBEXPAT = -lexpat
+		PROGRAMS += git-http-push$X
+	endif
 endif
 
 ifndef SHELL_PATH
@@ -375,6 +383,7 @@ git-ssh-pull$X: rsh.o fetch.o
 git-ssh-push$X: rsh.o
 
 git-http-fetch$X: LIBS += $(CURL_LIBCURL)
+git-http-push$X: LIBS += $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 git-rev-list$X: LIBS += $(OPENSSL_LIBSSL)
 
 init-db.o: init-db.c
diff --git a/http-push.c b/http-push.c
new file mode 100644
index 0000000..e85f1c1
--- /dev/null
+++ b/http-push.c
@@ -0,0 +1,1625 @@
+#include "cache.h"
+#include "commit.h"
+#include "pack.h"
+#include "fetch.h"
+#include "tag.h"
+#include "blob.h"
+
+#include <curl/curl.h>
+#include <curl/easy.h>
+#include "expat.h"
+
+static const char http_push_usage[] =
+"git-http-push [--complete] [--force] [--verbose] <url> <ref> [<ref>...]\n";
+
+#if LIBCURL_VERSION_NUM >= 0x070908
+#define USE_CURL_MULTI
+#define DEFAULT_MAX_REQUESTS 5
+#endif
+
+#if LIBCURL_VERSION_NUM < 0x070704
+#define curl_global_cleanup() do { /* nothing */ } while(0)
+#endif
+#if LIBCURL_VERSION_NUM < 0x070800
+#define curl_global_init(a) do { /* nothing */ } while(0)
+#endif
+
+#if LIBCURL_VERSION_NUM < 0x070c04
+#define NO_CURL_EASY_DUPHANDLE
+#endif
+
+#define RANGE_HEADER_SIZE 30
+
+/* DAV method names and request body templates */
+#define DAV_LOCK "LOCK"
+#define DAV_MKCOL "MKCOL"
+#define DAV_MOVE "MOVE"
+#define DAV_PROPFIND "PROPFIND"
+#define DAV_PUT "PUT"
+#define DAV_UNLOCK "UNLOCK"
+#define PROPFIND_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:prop xmlns:R=\"%s\">\n<D:supportedlock/>\n</D:prop>\n</D:propfind>"
+#define LOCK_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:lockinfo xmlns:D=\"DAV:\">\n<D:lockscope><D:exclusive/></D:lockscope>\n<D:locktype><D:write/></D:locktype>\n<D:owner>\n<D:href>mailto:%s</D:href>\n</D:owner>\n</D:lockinfo>"
+
+static int active_requests = 0;
+static int data_received;
+static int pushing = 0;
+static int aborted = 0;
+
+#ifdef USE_CURL_MULTI
+static int max_requests = -1;
+static CURLM *curlm;
+#endif
+#ifndef NO_CURL_EASY_DUPHANDLE
+static CURL *curl_default;
+#endif
+static struct curl_slist *no_pragma_header;
+static struct curl_slist *default_headers;
+static char curl_errorstr[CURL_ERROR_SIZE];
+static char *lock_token = NULL;
+
+static int push_verbosely = 0;
+static int push_all = 0;
+static int force_all = 0;
+
+struct buffer
+{
+        size_t posn;
+        size_t size;
+        void *buffer;
+};
+
+struct repo
+{
+	char *url;
+	struct packed_git *packs;
+};
+
+static struct repo *remote = NULL;
+
+enum transfer_state {
+	NEED_CHECK,
+	RUN_HEAD,
+	NEED_PUSH,
+	RUN_MKCOL,
+	RUN_PUT,
+	RUN_MOVE,
+	ABORTED,
+	COMPLETE,
+};
+
+struct transfer_request
+{
+	unsigned char sha1[20];
+	char *url;
+	char *dest;
+	char *lock_token;
+	struct curl_slist *headers;
+	struct buffer buffer;
+	char filename[PATH_MAX];
+	char tmpfile[PATH_MAX];
+	enum transfer_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 transfer_request *next;
+};
+
+struct active_request_slot
+{
+	CURL *curl;
+	FILE *local;
+	int in_use;
+	int done;
+	CURLcode curl_result;
+	long http_code;
+	struct active_request_slot *next;
+};
+
+static struct transfer_request *request_queue_head = NULL;
+static struct active_request_slot *active_queue_head = NULL;
+
+static int curl_ssl_verify = -1;
+static char *ssl_cert = NULL;
+#if LIBCURL_VERSION_NUM >= 0x070902
+static char *ssl_key = NULL;
+#endif
+#if LIBCURL_VERSION_NUM >= 0x070908
+static char *ssl_capath = NULL;
+#endif
+static char *ssl_cainfo = NULL;
+static long curl_low_speed_limit = -1;
+static long curl_low_speed_time = -1;
+
+struct lockprop
+{
+	int supported_lock;
+	int lock_entry;
+	int lock_scope;
+	int lock_type;
+	int lock_exclusive;
+	int lock_exclusive_write;
+};
+
+static int http_options(const char *var, const char *value)
+{
+	if (!strcmp("http.sslverify", var)) {
+		if (curl_ssl_verify == -1) {
+			curl_ssl_verify = git_config_bool(var, value);
+		}
+		return 0;
+	}
+
+	if (!strcmp("http.sslcert", var)) {
+		if (ssl_cert == NULL) {
+			ssl_cert = xmalloc(strlen(value)+1);
+			strcpy(ssl_cert, value);
+		}
+		return 0;
+	}
+#if LIBCURL_VERSION_NUM >= 0x070902
+	if (!strcmp("http.sslkey", var)) {
+		if (ssl_key == NULL) {
+			ssl_key = xmalloc(strlen(value)+1);
+			strcpy(ssl_key, value);
+		}
+		return 0;
+	}
+#endif
+#if LIBCURL_VERSION_NUM >= 0x070908
+	if (!strcmp("http.sslcapath", var)) {
+		if (ssl_capath == NULL) {
+			ssl_capath = xmalloc(strlen(value)+1);
+			strcpy(ssl_capath, value);
+		}
+		return 0;
+	}
+#endif
+	if (!strcmp("http.sslcainfo", var)) {
+		if (ssl_cainfo == NULL) {
+			ssl_cainfo = xmalloc(strlen(value)+1);
+			strcpy(ssl_cainfo, value);
+		}
+		return 0;
+	}
+
+#ifdef USE_CURL_MULTI	
+	if (!strcmp("http.maxrequests", var)) {
+		if (max_requests == -1)
+			max_requests = git_config_int(var, value);
+		return 0;
+	}
+#endif
+
+	if (!strcmp("http.lowspeedlimit", var)) {
+		if (curl_low_speed_limit == -1)
+			curl_low_speed_limit = (long)git_config_int(var, value);
+		return 0;
+	}
+	if (!strcmp("http.lowspeedtime", var)) {
+		if (curl_low_speed_time == -1)
+			curl_low_speed_time = (long)git_config_int(var, value);
+		return 0;
+	}
+
+	/* Fall back on the default ones */
+	return git_default_config(var, value);
+}
+
+static size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb,
+			   struct buffer *buffer)
+{
+	size_t size = eltsize * nmemb;
+	if (size > buffer->size - buffer->posn)
+		size = buffer->size - buffer->posn;
+	memcpy(ptr, buffer->buffer + buffer->posn, size);
+	buffer->posn += size;
+	return size;
+}
+
+static size_t fwrite_buffer_dynamic(const void *ptr, size_t eltsize,
+				    size_t nmemb, struct buffer *buffer)
+{
+	size_t size = eltsize * nmemb;
+	if (size > buffer->size - buffer->posn) {
+		buffer->size = buffer->size * 3 / 2;
+		if (buffer->size < buffer->posn + size)
+			buffer->size = buffer->posn + size;
+		buffer->buffer = xrealloc(buffer->buffer, buffer->size);
+	}
+	memcpy(buffer->buffer + buffer->posn, ptr, size);
+	buffer->posn += size;
+	data_received++;
+	return size;
+}
+
+static size_t fwrite_null(const void *ptr, size_t eltsize,
+			  size_t nmemb, struct buffer *buffer)
+{
+	data_received++;
+	return eltsize * nmemb;
+}
+
+#ifdef USE_CURL_MULTI
+static void process_curl_messages(void);
+static void process_request_queue(void);
+#endif
+
+static CURL* get_curl_handle(void)
+{
+	CURL* result = curl_easy_init();
+
+	curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, curl_ssl_verify);
+#if LIBCURL_VERSION_NUM >= 0x070907
+	curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
+#endif
+
+	if (ssl_cert != NULL)
+		curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
+#if LIBCURL_VERSION_NUM >= 0x070902
+	if (ssl_key != NULL)
+		curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);
+#endif
+#if LIBCURL_VERSION_NUM >= 0x070908
+	if (ssl_capath != NULL)
+		curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath);
+#endif
+	if (ssl_cainfo != NULL)
+		curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
+	curl_easy_setopt(result, CURLOPT_FAILONERROR, 1);
+
+	if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {
+		curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT,
+				 curl_low_speed_limit);
+		curl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME,
+				 curl_low_speed_time);
+	}
+
+	return result;
+}
+
+static struct active_request_slot *get_active_slot(void)
+{
+	struct active_request_slot *slot = active_queue_head;
+	struct active_request_slot *newslot;
+
+#ifdef USE_CURL_MULTI
+	int num_transfers;
+
+	/* Wait for a slot to open up if the queue is full */
+	while (active_requests >= max_requests) {
+		curl_multi_perform(curlm, &num_transfers);
+		if (num_transfers < active_requests) {
+			process_curl_messages();
+		}
+	}
+#endif
+
+	while (slot != NULL && slot->in_use) {
+		slot = slot->next;
+	}
+	if (slot == NULL) {
+		newslot = xmalloc(sizeof(*newslot));
+		newslot->curl = NULL;
+		newslot->in_use = 0;
+		newslot->next = NULL;
+
+		slot = active_queue_head;
+		if (slot == NULL) {
+			active_queue_head = newslot;
+		} else {
+			while (slot->next != NULL) {
+				slot = slot->next;
+			}
+			slot->next = newslot;
+		}
+		slot = newslot;
+	}
+
+	if (slot->curl == NULL) {
+#ifdef NO_CURL_EASY_DUPHANDLE
+		slot->curl = get_curl_handle();
+#else
+		slot->curl = curl_easy_duphandle(curl_default);
+#endif
+	}
+
+	active_requests++;
+	slot->in_use = 1;
+	slot->done = 0;
+	slot->local = NULL;
+	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, default_headers);
+	curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);
+
+	return slot;
+}
+
+static int start_active_slot(struct active_request_slot *slot)
+{
+#ifdef USE_CURL_MULTI
+	CURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl);
+
+	if (curlm_result != CURLM_OK &&
+	    curlm_result != CURLM_CALL_MULTI_PERFORM) {
+		active_requests--;
+		slot->in_use = 0;
+		return 0;
+	}
+#endif
+	return 1;
+}
+
+static void run_active_slot(struct active_request_slot *slot)
+{
+#ifdef USE_CURL_MULTI
+	int num_transfers;
+	long last_pos = 0;
+	long current_pos;
+	fd_set readfds;
+	fd_set writefds;
+	fd_set excfds;
+	int max_fd;
+	struct timeval select_timeout;
+	CURLMcode curlm_result;
+
+	while (!slot->done) {
+		data_received = 0;
+		do {
+			curlm_result = curl_multi_perform(curlm,
+							  &num_transfers);
+		} while (curlm_result == CURLM_CALL_MULTI_PERFORM);
+		if (num_transfers < active_requests) {
+			process_curl_messages();
+			process_request_queue();
+		}
+
+		if (!data_received && slot->local != NULL) {
+			current_pos = ftell(slot->local);
+			if (current_pos > last_pos)
+				data_received++;
+			last_pos = current_pos;
+		}
+
+		if (!slot->done && !data_received) {
+			max_fd = 0;
+			FD_ZERO(&readfds);
+			FD_ZERO(&writefds);
+			FD_ZERO(&excfds);
+			select_timeout.tv_sec = 0;
+			select_timeout.tv_usec = 50000;
+			select(max_fd, &readfds, &writefds,
+			       &excfds, &select_timeout);
+		}
+	}
+#else
+	slot->curl_result = curl_easy_perform(slot->curl);
+	active_requests--;
+#endif
+}
+
+static void start_check(struct transfer_request *request)
+{
+	char *hex = sha1_to_hex(request->sha1);
+	struct active_request_slot *slot;
+	char *posn;
+
+	request->url = xmalloc(strlen(remote->url) + 55);
+	strcpy(request->url, remote->url);
+	posn = request->url + strlen(remote->url);
+	strcpy(posn, "objects/");
+	posn += 8;
+	memcpy(posn, hex, 2);
+	posn += 2;
+	*(posn++) = '/';
+	strcpy(posn, hex + 2);
+
+	slot = get_active_slot();
+	curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr);
+	curl_easy_setopt(slot->curl, CURLOPT_URL, request->url);
+	curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);
+
+	if (start_active_slot(slot)) {
+		request->slot = slot;
+		request->state = RUN_HEAD;
+	} else {
+		request->state = ABORTED;
+		free(request->url);
+	}
+}
+
+static void start_mkcol(struct transfer_request *request)
+{
+	char *hex = sha1_to_hex(request->sha1);
+	struct active_request_slot *slot;
+	char *posn;
+
+	request->url = xmalloc(strlen(remote->url) + 13);
+	strcpy(request->url, remote->url);
+	posn = request->url + strlen(remote->url);
+	strcpy(posn, "objects/");
+	posn += 8;
+	memcpy(posn, hex, 2);
+	posn += 2;
+	strcpy(posn, "/");
+
+	slot = get_active_slot();
+	curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); /* undo PUT setup */
+	curl_easy_setopt(slot->curl, CURLOPT_URL, request->url);
+	curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr);
+	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_MKCOL);
+	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
+
+	if (start_active_slot(slot)) {
+		request->slot = slot;
+		request->state = RUN_MKCOL;
+	} else {
+		request->state = ABORTED;
+		free(request->url);
+	}
+}
+
+static void start_put(struct transfer_request *request)
+{
+	char *hex = sha1_to_hex(request->sha1);
+	struct active_request_slot *slot;
+	char *posn;
+	char type[20];
+	char hdr[50];
+	void *unpacked;
+	unsigned long len;
+	int hdrlen;
+	ssize_t size;
+	z_stream stream;
+
+	unpacked = read_sha1_file(request->sha1, type, &len);
+	hdrlen = sprintf(hdr, "%s %lu", type, len) + 1;
+
+	/* Set it up */
+	memset(&stream, 0, sizeof(stream));
+	deflateInit(&stream, Z_BEST_COMPRESSION);
+	size = deflateBound(&stream, len + hdrlen);
+	request->buffer.buffer = xmalloc(size);
+
+	/* Compress it */
+	stream.next_out = request->buffer.buffer;
+	stream.avail_out = size;
+
+	/* First header.. */
+	stream.next_in = (void *)hdr;
+	stream.avail_in = hdrlen;
+	while (deflate(&stream, 0) == Z_OK)
+		/* nothing */;
+
+	/* Then the data itself.. */
+	stream.next_in = unpacked;
+	stream.avail_in = len;
+	while (deflate(&stream, Z_FINISH) == Z_OK)
+		/* nothing */;
+	deflateEnd(&stream);
+	free(unpacked);
+
+	request->buffer.size = stream.total_out;
+	request->buffer.posn = 0;
+
+	if (request->url != NULL)
+		free(request->url);
+	request->url = xmalloc(strlen(remote->url) + 
+			       strlen(request->lock_token) + 51);
+	strcpy(request->url, remote->url);
+	posn = request->url + strlen(remote->url);
+	strcpy(posn, "objects/");
+	posn += 8;
+	memcpy(posn, hex, 2);
+	posn += 2;
+	*(posn++) = '/';
+	strcpy(posn, hex + 2);
+	request->dest = xmalloc(strlen(request->url) + 14);
+	sprintf(request->dest, "Destination: %s", request->url);
+	posn += 38;
+	*(posn++) = '.';
+	strcpy(posn, request->lock_token);
+
+	slot = get_active_slot();
+	curl_easy_setopt(slot->curl, CURLOPT_INFILE, &request->buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, request->buffer.size);
+	curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
+	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);
+	curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
+	curl_easy_setopt(slot->curl, CURLOPT_PUT, 1);
+	curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
+	curl_easy_setopt(slot->curl, CURLOPT_URL, request->url);
+
+	if (start_active_slot(slot)) {
+		request->slot = slot;
+		request->state = RUN_PUT;
+	} else {
+		request->state = ABORTED;
+		free(request->url);
+	}
+}
+
+static void start_move(struct transfer_request *request)
+{
+	struct active_request_slot *slot;
+	struct curl_slist *dav_headers = NULL;
+
+	slot = get_active_slot();
+	curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); /* undo PUT setup */
+	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_MOVE);
+	dav_headers = curl_slist_append(dav_headers, request->dest);
+	dav_headers = curl_slist_append(dav_headers, "Overwrite: T");
+	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
+	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
+	curl_easy_setopt(slot->curl, CURLOPT_URL, request->url);
+
+	if (start_active_slot(slot)) {
+		request->slot = slot;
+		request->state = RUN_MOVE;
+	} else {
+		request->state = ABORTED;
+		free(request->url);
+	}
+}
+
+static void finish_request(struct transfer_request *request)
+{
+	request->curl_result =	request->slot->curl_result;
+	request->http_code = request->slot->http_code;
+	request->slot = NULL;
+	if (request->headers != NULL)
+		curl_slist_free_all(request->headers);
+	if (request->state == RUN_HEAD) {
+		if (request->http_code == 404) {
+			request->state = NEED_PUSH;
+		} else if (request->curl_result == CURLE_OK) {
+			request->state = COMPLETE;
+		} else {
+			fprintf(stderr, "HEAD %s failed, aborting (%d/%ld)\n",
+				sha1_to_hex(request->sha1),
+				request->curl_result, request->http_code);
+			request->state = ABORTED;
+			aborted = 1;
+		}
+	} else if (request->state == RUN_MKCOL) {
+		if (request->curl_result == CURLE_OK ||
+		    request->http_code == 405) {
+			start_put(request);
+		} else {
+			fprintf(stderr, "MKCOL %s failed, aborting (%d/%ld)\n",
+				sha1_to_hex(request->sha1),
+				request->curl_result, request->http_code);
+			request->state = ABORTED;
+			aborted = 1;
+		}
+	} else if (request->state == RUN_PUT) {
+		if (request->curl_result == CURLE_OK) {
+			start_move(request);
+		} else {
+			fprintf(stderr,	"PUT %s failed, aborting (%d/%ld)\n",
+				sha1_to_hex(request->sha1),
+				request->curl_result, request->http_code);
+			request->state = ABORTED;
+			aborted = 1;
+		}
+	} else if (request->state == RUN_MOVE) {
+		if (request->curl_result == CURLE_OK) {
+			if (push_verbosely)
+				fprintf(stderr,
+					"sent %s\n",
+					sha1_to_hex(request->sha1));
+			request->state = COMPLETE;
+		} else {
+			fprintf(stderr, "MOVE %s failed, aborting (%d/%ld)\n",
+				sha1_to_hex(request->sha1),
+				request->curl_result, request->http_code);
+			request->state = ABORTED;
+			aborted = 1;
+		}
+	}
+}
+
+static void release_request(struct transfer_request *request)
+{
+	struct transfer_request *entry = request_queue_head;
+
+	if (request == request_queue_head) {
+		request_queue_head = request->next;
+	} else {
+		while (entry->next != NULL && entry->next != request)
+			entry = entry->next;
+		if (entry->next == request)
+			entry->next = entry->next->next;
+	}
+
+	free(request->url);
+	free(request);
+}
+
+#ifdef USE_CURL_MULTI
+void process_curl_messages(void)
+{
+	int num_messages;
+	struct active_request_slot *slot;
+	struct transfer_request *request = NULL;
+	CURLMsg *curl_message = curl_multi_info_read(curlm, &num_messages);
+
+	while (curl_message != NULL) {
+		if (curl_message->msg == CURLMSG_DONE) {
+			slot = active_queue_head;
+			while (slot != NULL &&
+			       slot->curl != curl_message->easy_handle)
+				slot = slot->next;
+			if (slot != NULL) {
+				curl_multi_remove_handle(curlm, slot->curl);
+				active_requests--;
+				slot->done = 1;
+				slot->in_use = 0;
+				slot->curl_result = curl_message->data.result;
+				curl_easy_getinfo(slot->curl,
+						  CURLINFO_HTTP_CODE,
+						  &slot->http_code);
+				request = request_queue_head;
+				while (request != NULL &&
+				       request->slot != slot)
+					request = request->next;
+				if (request != NULL)
+					finish_request(request);
+			} else {
+				fprintf(stderr, "Received DONE message for unknown request!\n");
+			}
+		} else {
+			fprintf(stderr, "Unknown CURL message received: %d\n",
+				(int)curl_message->msg);
+		}
+		curl_message = curl_multi_info_read(curlm, &num_messages);
+	}
+}
+
+void process_request_queue(void)
+{
+	struct transfer_request *request = request_queue_head;
+	struct active_request_slot *slot = active_queue_head;
+	int num_transfers;
+
+	if (aborted)
+		return;
+
+	while (active_requests < max_requests && request != NULL) {
+		if (!pushing && request->state == NEED_CHECK) {
+			start_check(request);
+			curl_multi_perform(curlm, &num_transfers);
+		} else if (pushing && request->state == NEED_PUSH) {
+			start_mkcol(request);
+			curl_multi_perform(curlm, &num_transfers);
+		}
+		request = request->next;
+	}
+
+	while (slot != NULL) {
+		if (!slot->in_use && slot->curl != NULL) {
+			curl_easy_cleanup(slot->curl);
+			slot->curl = NULL;
+		}
+		slot = slot->next;
+	}				
+}
+#endif
+
+void process_waiting_requests(void)
+{
+	struct active_request_slot *slot = active_queue_head;
+
+	while (slot != NULL)
+		if (slot->in_use) {
+			run_active_slot(slot);
+			slot = active_queue_head;
+		} else {
+			slot = slot->next;
+		}
+}
+
+void add_request(unsigned char *sha1, char *lock_token)
+{
+	struct transfer_request *request = request_queue_head;
+	struct transfer_request *tail;
+	struct packed_git *target;
+	
+	while (request != NULL && memcmp(request->sha1, sha1, 20))
+		request = request->next;
+	if (request != NULL)
+		return;
+
+	target = find_sha1_pack(sha1, remote->packs);
+	if (target)
+		return;
+
+	request = xmalloc(sizeof(*request));
+	memcpy(request->sha1, sha1, 20);
+	request->url = NULL;
+	request->lock_token = lock_token;
+	request->headers = NULL;
+	request->state = NEED_CHECK;
+	request->next = NULL;
+
+	if (request_queue_head == NULL) {
+		request_queue_head = request;
+	} else {
+		tail = request_queue_head;
+		while (tail->next != NULL) {
+			tail = tail->next;
+		}
+		tail->next = request;
+	}
+#ifdef USE_CURL_MULTI
+	process_request_queue();
+	process_curl_messages();
+#endif
+}
+
+static int fetch_index(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;
+
+	if (has_pack_index(sha1))
+		return 0;
+
+	if (push_verbosely)
+		fprintf(stderr, "Getting index for pack %s\n", hex);
+	
+	url = xmalloc(strlen(remote->url) + 64);
+	sprintf(url, "%s/objects/pack/pack-%s.idx", remote->url, 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();
+	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 (push_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 (slot->curl_result != CURLE_OK) {
+			free(url);
+			fclose(indexfile);
+			return error("Unable to get pack index %s\n%s", url,
+				     curl_errorstr);
+		}
+	} else {
+		free(url);
+		return error("Unable to start request");
+	}
+
+	free(url);
+	fclose(indexfile);
+
+	return move_temp_to_file(tmpfile, filename);
+}
+
+static int setup_index(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(sha1))
+		return -1;
+
+	new_pack = parse_pack_index(sha1);
+	new_pack->next = remote->packs;
+	remote->packs = new_pack;
+	return 0;
+}
+
+static int fetch_indices()
+{
+	unsigned char sha1[20];
+	char *url;
+	struct buffer buffer;
+	char *data;
+	int i = 0;
+
+	struct active_request_slot *slot;
+
+	data = xmalloc(4096);
+	memset(data, 0, 4096);
+	buffer.size = 4096;
+	buffer.posn = 0;
+	buffer.buffer = data;
+
+	if (push_verbosely)
+		fprintf(stderr, "Getting pack list\n");
+	
+	url = xmalloc(strlen(remote->url) + 21);
+	sprintf(url, "%s/objects/info/packs", remote->url);
+
+	slot = get_active_slot();
+	curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
+			 fwrite_buffer_dynamic);
+	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 (slot->curl_result != CURLE_OK) {
+			free(buffer.buffer);
+			free(url);
+			if (slot->http_code == 404)
+				return 0;
+			else
+				return error("%s", curl_errorstr);
+		}
+	} else {
+		free(buffer.buffer);
+		free(url);
+		return error("Unable to start request");
+	}
+	free(url);
+
+	data = buffer.buffer;
+	while (i < buffer.posn) {
+		switch (data[i]) {
+		case 'P':
+			i++;
+			if (i + 52 < buffer.posn &&
+			    !strncmp(data + i, " pack-", 6) &&
+			    !strncmp(data + i + 46, ".pack\n", 6)) {
+				get_sha1_hex(data + i + 6, sha1);
+				setup_index(sha1);
+				i += 51;
+				break;
+			}
+		default:
+			while (data[i] != '\n')
+				i++;
+		}
+		i++;
+	}
+
+	free(buffer.buffer);
+	return 0;
+}
+
+static inline int needs_quote(int ch)
+{
+	switch (ch) {
+	case '/': case '-': case '.':
+	case 'A'...'Z':	case 'a'...'z':	case '0'...'9':
+		return 0;
+	default:
+		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 + 12; /* "refs/heads/" + 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/heads/", 11);
+	for (cp = ref, dp = qref + baselen + 11; (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;
+	char *base = remote->url;
+	struct active_request_slot *slot;
+        buffer.size = 41;
+        buffer.posn = 0;
+        buffer.buffer = hex;
+        hex[41] = '\0';
+        
+	url = quote_ref_url(base, ref);
+	slot = get_active_slot();
+	curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
+			 fwrite_buffer_dynamic);
+	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 (slot->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
+start_lockprop_element(void *userData, const char *name, const char **atts)
+{
+	struct lockprop *prop = (struct lockprop *)userData;
+
+	if (prop->lock_type && !strcmp(name, "D:write")) {
+		if (prop->lock_exclusive) {
+			prop->lock_exclusive_write = 1;
+		}
+	} else if (prop->lock_scope && !strcmp(name, "D:exclusive")) {
+		prop->lock_exclusive = 1;
+	} else if (prop->lock_entry) {
+		if (!strcmp(name, "D:lockscope")) {
+			prop->lock_scope = 1;
+		} else if (!strcmp(name, "D:locktype")) {
+			prop->lock_type = 1;
+		}
+	} else if (prop->supported_lock) {
+		if (!strcmp(name, "D:lockentry")) {
+			prop->lock_entry = 1;
+		}
+	} else if (!strcmp(name, "D:supportedlock")) {
+		prop->supported_lock = 1;
+	}
+}
+
+static void
+end_lockprop_element(void *userData, const char *name)
+{
+	struct lockprop *prop = (struct lockprop *)userData;
+
+	if (!strcmp(name, "D:lockentry")) {
+		prop->lock_entry = 0;
+		prop->lock_scope = 0;
+		prop->lock_type = 0;
+		prop->lock_exclusive = 0;
+	} else if (!strcmp(name, "D:supportedlock")) {
+		prop->supported_lock = 0;
+	}
+}
+
+size_t process_lock_header( void *ptr, size_t size, size_t nmemb, void *stream)
+{
+	size_t header_size = size*nmemb;
+	char *start;
+	char *end;
+
+	if (!strncmp(ptr, "Lock-Token: <opaquelocktoken:", 29)) {
+		start = ptr + 29;
+		for (end = ptr + header_size;
+		     *(end - 1) == '\r' || *(end - 1) == '\n' || *(end - 1) == '>';
+		     end--) {}
+		if (end > start) {
+			lock_token = xmalloc(end - start + 1);
+			memcpy(lock_token, start, end - start);
+			lock_token[end - start] = 0;
+		}
+	}
+
+	return header_size;
+}
+
+char *lock_remote(char *file, int timeout)
+{
+	struct active_request_slot *slot;
+	struct buffer out_buffer;
+	char *out_data;
+	char *url;
+	char timeout_header[25];
+	struct curl_slist *dav_headers = NULL;
+
+	if (lock_token != NULL)
+		free(lock_token);
+
+	out_buffer.size = strlen(LOCK_REQUEST) + strlen(git_default_email) - 2;
+	out_data = xmalloc(out_buffer.size + 1);
+	snprintf(out_data, out_buffer.size + 1, LOCK_REQUEST, git_default_email);
+	out_buffer.posn = 0;
+	out_buffer.buffer = out_data;
+
+	sprintf(timeout_header, "Timeout: Second-%d", timeout);
+	url = xmalloc(strlen(remote->url) + strlen(file) + 1);
+	sprintf(url, "%s%s", remote->url, file);
+	dav_headers = curl_slist_append(dav_headers, timeout_header);
+	dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
+
+	slot = get_active_slot();
+	curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
+	curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
+	curl_easy_setopt(slot->curl, CURLOPT_HEADERFUNCTION,
+		process_lock_header);
+	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+	curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
+	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK);
+	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
+
+	if (start_active_slot(slot)) {
+		run_active_slot(slot);
+		free(out_data);
+		if (slot->curl_result != CURLE_OK) {
+			fprintf(stderr, "Got HTTP error %ld\n", slot->http_code);
+			return NULL;
+		}
+	} else {
+		free(out_data);
+		fprintf(stderr, "Unable to start request\n");
+	}
+
+	return strdup(lock_token);
+}
+
+int unlock_remote(char *file, char *lock_token)
+{
+	struct active_request_slot *slot;
+	char *url;
+	char *lock_token_header;
+	struct curl_slist *dav_headers = NULL;
+	int rc = 0;
+
+	if (lock_token == NULL) {
+		fprintf(stderr, "Unable to unlock, no lock token");
+		return 0;
+	}
+
+	lock_token_header = xmalloc(strlen(lock_token) + 31);
+	sprintf(lock_token_header, "Lock-Token: <opaquelocktoken:%s>",
+		lock_token);
+	url = xmalloc(strlen(remote->url) + strlen(file) + 1);
+	sprintf(url, "%s%s", remote->url, file);
+	dav_headers = curl_slist_append(dav_headers, lock_token_header);
+
+	slot = get_active_slot();
+	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
+	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_UNLOCK);
+	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
+
+	if (start_active_slot(slot)) {
+		run_active_slot(slot);
+		if (slot->curl_result == CURLE_OK)
+			rc = 1;
+		else
+			fprintf(stderr, "Got HTTP error %ld\n",
+				slot->http_code);
+	} else {
+		fprintf(stderr, "Unable to start request\n");
+	}
+
+	curl_slist_free_all(dav_headers);
+	free(lock_token_header);
+	free(url);
+
+	return rc;
+}
+
+int check_locking()
+{
+	struct active_request_slot *slot;
+	struct buffer in_buffer;
+	struct buffer out_buffer;
+	char *in_data;
+	char *out_data;
+	XML_Parser parser = XML_ParserCreate(NULL);
+	enum XML_Status result;
+	struct lockprop supported_lock;
+	struct curl_slist *dav_headers = NULL;
+
+	out_buffer.size = strlen(PROPFIND_REQUEST) + strlen(remote->url) - 2;
+	out_data = xmalloc(out_buffer.size + 1);
+	snprintf(out_data, out_buffer.size + 1, PROPFIND_REQUEST, remote->url);
+	out_buffer.posn = 0;
+	out_buffer.buffer = out_data;
+
+	in_buffer.size = 4096;
+	in_data = xmalloc(in_buffer.size);
+	in_buffer.posn = 0;
+	in_buffer.buffer = in_data;
+
+	dav_headers = curl_slist_append(dav_headers, "Depth: 0");
+	dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
+	
+	slot = get_active_slot();
+	curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
+	curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
+			 fwrite_buffer_dynamic);
+	curl_easy_setopt(slot->curl, CURLOPT_URL, remote->url);
+	curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
+	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND);
+	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
+
+	if (start_active_slot(slot)) {
+		run_active_slot(slot);
+		free(out_data);
+		if (slot->curl_result != CURLE_OK) {
+			free(in_buffer.buffer);
+			return -1;
+		}
+
+		XML_SetUserData(parser, &supported_lock);
+		XML_SetElementHandler(parser, start_lockprop_element,
+				      end_lockprop_element);
+		result = XML_Parse(parser, in_buffer.buffer, in_buffer.posn, 1);
+		free(in_buffer.buffer);
+		if (result != XML_STATUS_OK)
+			return error("%s", XML_ErrorString(
+					     XML_GetErrorCode(parser)));
+	} else {
+		free(out_data);
+		free(in_buffer.buffer);
+		return error("Unable to start request");
+	}
+
+	if (supported_lock.lock_exclusive_write)
+		return 0;
+	else
+		return 1;
+}
+
+int is_ancestor(unsigned char *sha1, struct commit *commit)
+{
+	struct commit_list *parents;
+
+	if (parse_commit(commit))
+		return 0;
+	parents = commit->parents;
+	for (; parents; parents = parents->next) {
+		if (!memcmp(sha1, parents->item->object.sha1, 20)) {
+			return 1;
+		} else if (parents->item->object.type == commit_type) {
+			if (is_ancestor(
+				    sha1,
+				    (struct commit *)&parents->item->object
+				    ))
+				return 1;
+		}
+	}
+	return 0;
+}
+
+void get_delta(unsigned char *sha1, struct object *obj, char *lock_token)
+{
+	struct commit *commit;
+	struct commit_list *parents;
+	struct tree *tree;
+	struct tree_entry_list *entry;
+
+	if (sha1 && !memcmp(sha1, obj->sha1, 20))
+		return;
+
+	if (aborted)
+		return;
+
+	if (obj->type == commit_type) {
+		if (push_verbosely)
+			fprintf(stderr, "walk %s\n", sha1_to_hex(obj->sha1));
+		add_request(obj->sha1, lock_token);
+		commit = (struct commit *)obj;
+		if (parse_commit(commit)) {
+			fprintf(stderr, "Error parsing commit %s\n",
+				sha1_to_hex(obj->sha1));
+			aborted = 1;
+			return;
+		}
+		parents = commit->parents;
+		for (; parents; parents = parents->next)
+			if (sha1 == NULL ||
+			    memcmp(sha1, parents->item->object.sha1, 20))
+				get_delta(sha1, &parents->item->object,
+					  lock_token);
+		get_delta(sha1, &commit->tree->object, lock_token);
+	} else if (obj->type == tree_type) {
+		if (push_verbosely)
+			fprintf(stderr, "walk %s\n", sha1_to_hex(obj->sha1));
+		add_request(obj->sha1, lock_token);
+		tree = (struct tree *)obj;
+		if (parse_tree(tree)) {
+			fprintf(stderr, "Error parsing tree %s\n",
+				sha1_to_hex(obj->sha1));
+			aborted = 1;
+			return;
+		}
+		entry = tree->entries;
+		tree->entries = NULL;
+		while (entry) {
+			struct tree_entry_list *next = entry->next;
+			get_delta(sha1, entry->item.any, lock_token);
+			free(entry->name);
+			free(entry);
+			entry = next;
+		}
+	} else if (obj->type == blob_type || obj->type == tag_type) {
+		add_request(obj->sha1, lock_token);
+	}
+}
+
+int update_remote(char *remote_path, unsigned char *sha1, char *lock_token)
+{
+	struct active_request_slot *slot;
+	char *url;
+	char *out_data;
+	char *if_header;
+	struct buffer out_buffer;
+	struct curl_slist *dav_headers = NULL;
+	int i;
+
+	url = xmalloc(strlen(remote->url) + strlen(remote_path) + 1);
+	sprintf(url, "%s%s", remote->url, remote_path);
+
+	if_header = xmalloc(strlen(lock_token) + 25);
+	sprintf(if_header, "If: (<opaquelocktoken:%s>)", lock_token);
+	dav_headers = curl_slist_append(dav_headers, if_header);
+
+	out_buffer.size = 41;
+	out_data = xmalloc(out_buffer.size + 1);
+	i = snprintf(out_data, out_buffer.size + 1, "%s\n", sha1_to_hex(sha1));
+	if (i != out_buffer.size) {
+		fprintf(stderr, "Unable to initialize PUT request body\n");
+		return 0;
+	}
+	out_buffer.posn = 0;
+	out_buffer.buffer = out_data;
+
+	slot = get_active_slot();
+	curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
+	curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
+	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);
+	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
+	curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
+	curl_easy_setopt(slot->curl, CURLOPT_PUT, 1);
+	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+
+	if (start_active_slot(slot)) {
+		run_active_slot(slot);
+		free(out_data);
+		free(if_header);
+		free(url);
+		if (slot->curl_result != CURLE_OK) {
+			fprintf(stderr,
+				"PUT error: curl result=%d, HTTP code=%ld\n",
+				slot->curl_result, slot->http_code);
+			/* We should attempt recovery? */
+			return 0;
+		}
+	} else {
+		free(out_data);
+		free(if_header);
+		free(url);
+		fprintf(stderr, "Unable to start PUT request\n");
+		return 0;
+	}
+
+	return 1;
+}
+
+int main(int argc, char **argv)
+{
+	struct active_request_slot *slot;
+	struct active_request_slot *next_slot;
+	struct transfer_request *request;
+	struct transfer_request *next_request;
+	int nr_refspec = 0;
+	char **refspec = NULL;
+	int do_remote_update;
+	int new_branch;
+	int force_this;
+	char *local_ref;
+	unsigned char local_sha1[20];
+	struct object *local_object = NULL;
+	char *remote_ref = NULL;
+	unsigned char remote_sha1[20];
+	char *remote_lock = NULL;
+	char *remote_path = NULL;
+	char *low_speed_limit;
+	char *low_speed_time;
+	int rc = 0;
+	int i;
+
+	setup_ident();
+
+	remote = xmalloc(sizeof(*remote));
+	remote->url = NULL;
+	remote->packs = NULL;
+
+	argv++;
+	for (i = 1; i < argc; i++, argv++) {
+		char *arg = *argv;
+
+		if (*arg == '-') {
+			if (!strcmp(arg, "--complete")) {
+				push_all = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--force")) {
+				force_all = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--verbose")) {
+				push_verbosely = 1;
+				continue;
+			}
+			usage(http_push_usage);
+		}
+		if (!remote->url) {
+			remote->url = arg;
+			continue;
+		}
+		refspec = argv;
+		nr_refspec = argc - i;
+		break;
+	}
+
+	curl_global_init(CURL_GLOBAL_ALL);
+
+#ifdef USE_CURL_MULTI
+	{
+		char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS");
+		if (http_max_requests != NULL)
+			max_requests = atoi(http_max_requests);
+	}
+
+	curlm = curl_multi_init();
+	if (curlm == NULL) {
+		fprintf(stderr, "Error creating curl multi handle.\n");
+		return 1;
+	}
+#endif
+
+	if (getenv("GIT_SSL_NO_VERIFY"))
+		curl_ssl_verify = 0;
+
+	ssl_cert = getenv("GIT_SSL_CERT");
+#if LIBCURL_VERSION_NUM >= 0x070902
+	ssl_key = getenv("GIT_SSL_KEY");
+#endif
+#if LIBCURL_VERSION_NUM >= 0x070908
+	ssl_capath = getenv("GIT_SSL_CAPATH");
+#endif
+	ssl_cainfo = getenv("GIT_SSL_CAINFO");
+
+	low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT");
+	if (low_speed_limit != NULL)
+		curl_low_speed_limit = strtol(low_speed_limit, NULL, 10);
+	low_speed_time = getenv("GIT_HTTP_LOW_SPEED_TIME");
+	if (low_speed_time != NULL)
+		curl_low_speed_time = strtol(low_speed_time, NULL, 10);
+
+	git_config(http_options);
+
+	if (curl_ssl_verify == -1)
+		curl_ssl_verify = 1;
+
+#ifdef USE_CURL_MULTI
+	if (max_requests < 1)
+		max_requests = DEFAULT_MAX_REQUESTS;
+#endif
+
+	no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
+	default_headers = curl_slist_append(default_headers, "Range:");
+	default_headers = curl_slist_append(default_headers, "Destination:");
+	default_headers = curl_slist_append(default_headers, "If:");
+	default_headers = curl_slist_append(default_headers,
+					    "Pragma: no-cache");
+
+#ifndef NO_CURL_EASY_DUPHANDLE
+	curl_default = get_curl_handle();
+#endif
+
+	/* Verify DAV compliance/lock support */
+	if (check_locking() != 0) {
+		fprintf(stderr, "Error: no DAV locking support on remote repo %s\n", remote->url);
+		rc = 1;
+		goto cleanup;
+	}
+
+	/* Process each refspec */
+	for (i = 0; i < nr_refspec; i++) {
+		char *ep;
+		force_this = 0;
+		do_remote_update = 0;
+		new_branch = 0;
+		local_ref = refspec[i];
+		if (*local_ref == '+') {
+			force_this = 1;
+			local_ref++;
+		}
+		ep = strchr(local_ref, ':');
+		if (ep) {
+			remote_ref = ep + 1;
+			*ep = 0;
+		}
+		else
+			remote_ref = local_ref;
+
+		/* Lock remote branch ref */
+		if (remote_path)
+			free(remote_path);
+		remote_path = xmalloc(strlen(remote_ref) + 12);
+		sprintf(remote_path, "refs/heads/%s", remote_ref);
+		remote_lock = lock_remote(remote_path, 3600);
+		if (remote_lock == NULL) {
+			fprintf(stderr, "Unable to lock remote branch %s\n",
+				remote_ref);
+			rc = 1;
+			continue;
+		}
+
+		/* Resolve local and remote refs */
+		if (fetch_ref(remote_ref, remote_sha1) != 0) {
+			fprintf(stderr,
+				"Remote branch %s does not exist on %s\n",
+				remote_ref, remote->url);
+			new_branch = 1;
+		}
+		if (get_sha1(local_ref, local_sha1) != 0) {
+			fprintf(stderr, "Error resolving local branch %s\n",
+				local_ref);
+			rc = 1;
+			goto unlock;
+		}
+	
+		/* Find relationship between local and remote */
+		local_object = parse_object(local_sha1);
+		if (!local_object) {
+			fprintf(stderr, "Unable to parse local object %s\n",
+				sha1_to_hex(local_sha1));
+			rc = 1;
+			goto unlock;
+		} else if (new_branch) {
+			do_remote_update = 1;
+		} else {
+			if (!memcmp(local_sha1, remote_sha1, 20)) {
+				fprintf(stderr,
+					"* %s: same as branch '%s' of %s\n",
+					local_ref, remote_ref, remote->url);
+			} else if (is_ancestor(remote_sha1,
+					       (struct commit *)local_object)) {
+				fprintf(stderr,
+					"Remote %s will fast-forward to local %s\n",
+					remote_ref, local_ref);
+				do_remote_update = 1;
+			} else if (force_all || force_this) {
+				fprintf(stderr,
+					"* %s on %s does not fast forward to local branch '%s', overwriting\n",
+					remote_ref, remote->url, local_ref);
+				do_remote_update = 1;
+			} else {
+				fprintf(stderr,
+					"* %s on %s does not fast forward to local branch '%s'\n",
+					remote_ref, remote->url, local_ref);
+				rc = 1;
+				goto unlock;
+			}
+		}
+
+		/* Generate and check list of required objects */
+		pushing = 0;
+		if (do_remote_update || push_all)
+			fetch_indices();
+		get_delta(push_all ? NULL : remote_sha1,
+			  local_object, remote_lock);
+		process_waiting_requests();
+
+		/* Push missing objects to remote, this would be a
+		   convenient time to pack them first if appropriate. */
+		pushing = 1;
+		process_request_queue();
+		process_waiting_requests();
+
+		/* Update the remote branch if all went well */
+		if (do_remote_update) {
+			if (!aborted && update_remote(remote_path,
+						      local_sha1,
+						      remote_lock)) {
+				fprintf(stderr, "%s remote branch %s\n",
+					new_branch ? "Created" : "Updated",
+					remote_ref);
+			} else {
+				fprintf(stderr,
+					"Unable to %s remote branch %s\n",
+					new_branch ? "create" : "update",
+					remote_ref);
+				rc = 1;
+				goto unlock;
+			}
+		}
+
+	unlock:
+		unlock_remote(remote_path, remote_lock);
+		free(remote_path);
+		free(remote_lock);
+	}
+
+ cleanup:
+	free(remote);
+
+	curl_slist_free_all(no_pragma_header);
+	curl_slist_free_all(default_headers);
+
+	slot = active_queue_head;
+	while (slot != NULL) {
+		next_slot = slot->next;
+		if (slot->curl != NULL)
+			curl_easy_cleanup(slot->curl);
+		free(slot);
+		slot = next_slot;
+	}
+
+	request = request_queue_head;
+	while (request != NULL) {
+		next_request = request->next;
+		release_request(request);
+		free(request);
+		request = next_request;
+	}
+
+#ifndef NO_CURL_EASY_DUPHANDLE
+	curl_easy_cleanup(curl_default);
+#endif
+#ifdef USE_CURL_MULTI
+	curl_multi_cleanup(curlm);
+#endif
+	curl_global_cleanup();
+	return rc;
+}
---
0.99.9.GIT

^ permalink raw reply related

* [PATCH] Clean up the SunOS Makefile rule
From: Peter Eriksen @ 2005-11-02 19:27 UTC (permalink / raw)
  To: git

Don't set a non-standard CURLDIR as default, and fix an error
in Solaris 10 by setting NEEDS_LIBICONV.

---

I hope this is ok, since I was the one suggesting the
stupid non-standard /opt/sfw path in the first place.
Further more Solaris 10 seems to need NEEDS_LIBICONV.

I hope other people who know more about Solaris 
than me could point out, what will work on any 
Solaris system and not just the specific one I'm using.

Regards,

Peter
 
 Makefile |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

applies-to: b774cd64105bda561bba3c528a7fea19b87755a1
5f5bb71e10f8f3e92df2475a456d73d5ee7ca55a
diff --git a/Makefile b/Makefile
index 357cb3e..cdd19c3 100644
--- a/Makefile
+++ b/Makefile
@@ -189,9 +189,9 @@ endif
 ifeq ($(uname_S),SunOS)
 	NEEDS_SOCKET = YesPlease
 	NEEDS_NSL = YesPlease
+	NEEDS_LIBICONV = YesPlease
 	SHELL_PATH = /bin/bash
 	NO_STRCASESTR = YesPlease
-	CURLDIR = /opt/sfw
 	INSTALL = ginstall
 	TAR = gtar
 	PLATFORM_DEFINES += -D__EXTENSIONS__
---
0.99.9.GIT

^ permalink raw reply related

* Re: [PATCH 1/2] Add support for pushing to a remote repository using HTTP/DAV
From: Johannes Schindelin @ 2005-11-02 19:43 UTC (permalink / raw)
  To: Nick Hengeveld; +Cc: git
In-Reply-To: <20051102191924.GC3928@reactrix.com>

Hi,

On Wed, 2 Nov 2005, Nick Hengeveld wrote:

> Add support for pushing to a remote repository using HTTP/DAV

Great! I will try to test it in the next few days.

Ciao,
Dscho

^ permalink raw reply

* Re: [PATCH] Clean up the SunOS Makefile rule
From: Peter Eriksen @ 2005-11-02 19:49 UTC (permalink / raw)
  To: git
In-Reply-To: <20051102192730.GA17706@ebar091.ebar.dtu.dk>

On Wed, Nov 02, 2005 at 08:27:31PM +0100, Peter Eriksen wrote:
> Don't set a non-standard CURLDIR as default, and fix an error
> in Solaris 10 by setting NEEDS_LIBICONV.

Ups, I forgot to say, that this is

Signed-off-by: Peter Eriksen <s022018@student.dtu.dk>

Regards,

Peter

^ permalink raw reply

* [PATCH] Fix segfaults caused by invalid tags
From: Petr Baudis @ 2005-11-02 20:07 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On many places, we didn't bother checking deref_tag() return value against
NULL, causing GIT segfaulting e.g. on some old Cogito checkouts containing
residual tags from GIT.

Signed-off-by: Petr Baudis <pasky@suse.cz>
---

 fetch-pack.c  |    2 +-
 server-info.c |    5 +++--
 upload-pack.c |    3 ++-
 3 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/fetch-pack.c b/fetch-pack.c
index 3df9911..1fae211 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -40,7 +40,7 @@ static int rev_list_insert_ref(const cha
 {
 	struct object *o = deref_tag(parse_object(sha1));
 
-	if (o->type == commit_type)
+	if (o && o->type == commit_type)
 		rev_list_push((struct commit *)o, SEEN);
 
 	return 0;
diff --git a/server-info.c b/server-info.c
index ba53591..8e13c94 100644
--- a/server-info.c
+++ b/server-info.c
@@ -14,8 +14,9 @@ static int add_info_ref(const char *path
 	fprintf(info_ref_fp, "%s	%s\n", sha1_to_hex(sha1), path);
 	if (o->type == tag_type) {
 		o = deref_tag(o);
-		fprintf(info_ref_fp, "%s	%s^{}\n",
-			sha1_to_hex(o->sha1), path);
+		if (o)
+			fprintf(info_ref_fp, "%s	%s^{}\n",
+				sha1_to_hex(o->sha1), path);
 	}
 	return 0;
 }
diff --git a/upload-pack.c b/upload-pack.c
index c5eff21..f0b8e63 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -227,7 +227,8 @@ static int send_ref(const char *refname,
 	}
 	if (o->type == tag_type) {
 		o = deref_tag(o);
-		packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname);
+		if (o)
+			packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname);
 	}
 	return 0;
 }

^ permalink raw reply related

* Re: [PATCH] Fix segfaults caused by invalid tags
From: Johannes Schindelin @ 2005-11-02 20:16 UTC (permalink / raw)
  To: Petr Baudis; +Cc: Junio C Hamano, git
In-Reply-To: <20051102200751.26904.5780.stgit@machine.or.cz>

Hi,

On Wed, 2 Nov 2005, Petr Baudis wrote:

> On many places, we didn't bother checking deref_tag() return value against
> NULL, causing GIT segfaulting e.g. on some old Cogito checkouts containing
> residual tags from GIT.

Shouldn't a warning be issued in every of those cases? I don't know, but 
those tags pointing nowhere don't seem useful to me.

Ciao,
Dscho

^ permalink raw reply

* Re: remove CR/LF from .gitignore
From: Junio C Hamano @ 2005-11-02 20:27 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: git
In-Reply-To: <Pine.LNX.4.64.0511020759500.27915@g5.osdl.org>

Linus Torvalds <torvalds@osdl.org> writes:

> On Wed, 2 Nov 2005, Alex Riesen wrote:
>>
>> For everyone cursed by dos/windows line endings (aka CRLF):
>
> Btw, it would be good if somebody verified that the .git/config file also 
> works with CR/LF. 
>
> I'm pretty sure "\" at the end of line will break, but I didn't care 
> enough. And maybe nobody does. Normal config files _should_ work, but 
> you can guess how eager I'm to test it ;)

Something like this?

---
cd /opt/packrat/playpen/public/in-place/git/git.junio/
git diff
diff --git a/config.c b/config.c
index 519fecf..e89bab2 100644
--- a/config.c
+++ b/config.c
@@ -13,6 +13,14 @@ static int get_next_char(void)
 	c = '\n';
 	if ((f = config_file) != NULL) {
 		c = fgetc(f);
+		if (c == '\r') {
+			/* DOS like systems */
+			c = fgetc(f);
+			if (c != '\n') {
+				ungetc(c, f);
+				c = '\r';
+			}
+		}
 		if (c == '\n')
 			config_linenr++;
 		if (c == EOF) {

Compilation finished at Wed Nov  2 12:24:27

^ permalink raw reply related

* Re: [PATCH] Fix segfaults caused by invalid tags
From: Junio C Hamano @ 2005-11-02 20:28 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Petr Baudis, git
In-Reply-To: <Pine.LNX.4.63.0511022115250.13746@wbgn013.biozentrum.uni-wuerzburg.de>

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

> Shouldn't a warning be issued in every of those cases? I don't know, but 
> those tags pointing nowhere don't seem useful to me.

Yes they should warn.

It is not just "don't seem useful"; it is a corrupt repository.

^ permalink raw reply

* [PATCH] Warn when calling deref_tag() on broken tags
From: Petr Baudis @ 2005-11-02 20:41 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Junio C Hamano, git
In-Reply-To: <Pine.LNX.4.63.0511022115250.13746@wbgn013.biozentrum.uni-wuerzburg.de>

Dear diary, on Wed, Nov 02, 2005 at 09:16:13PM CET, I got a letter
where Johannes Schindelin <Johannes.Schindelin@gmx.de> told me that...
> On Wed, 2 Nov 2005, Petr Baudis wrote:
> 
> > On many places, we didn't bother checking deref_tag() return value against
> > NULL, causing GIT segfaulting e.g. on some old Cogito checkouts containing
> > residual tags from GIT.
> 
> Shouldn't a warning be issued in every of those cases? I don't know, but 
> those tags pointing nowhere don't seem useful to me.

Good idea. Not that I would be excited by the awing elegancy of the
patch...

---

When we hit a broken tag by deref_tag(), warn the user.

Signed-off-by: Petr Baudis <pasky@suse.cz>
---

 commit.c      |    2 +-
 fetch-pack.c  |    4 ++--
 name-rev.c    |    2 +-
 send-pack.c   |    4 ++--
 server-info.c |    2 +-
 sha1_name.c   |    2 +-
 tag.c         |   12 ++++++++++--
 tag.h         |    2 +-
 upload-pack.c |    2 +-
 9 files changed, 20 insertions(+), 12 deletions(-)

diff --git a/commit.c b/commit.c
index 8f40318..78aac42 100644
--- a/commit.c
+++ b/commit.c
@@ -55,7 +55,7 @@ static struct commit *check_commit(struc
 struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
 					      int quiet)
 {
-	struct object *obj = deref_tag(parse_object(sha1));
+	struct object *obj = deref_tag(parse_object(sha1), NULL);
 
 	if (!obj)
 		return NULL;
diff --git a/fetch-pack.c b/fetch-pack.c
index 1fae211..14b5918 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -38,7 +38,7 @@ static void rev_list_push(struct commit 
 
 static int rev_list_insert_ref(const char *path, const unsigned char *sha1)
 {
-	struct object *o = deref_tag(parse_object(sha1));
+	struct object *o = deref_tag(parse_object(sha1), path);
 
 	if (o && o->type == commit_type)
 		rev_list_push((struct commit *)o, SEEN);
@@ -317,7 +317,7 @@ static int everything_local(struct ref *
 	 * Don't mark them common yet; the server has to be told so first.
 	 */
 	for (ref = *refs; ref; ref = ref->next) {
-		struct object *o = deref_tag(lookup_object(ref->old_sha1));
+		struct object *o = deref_tag(lookup_object(ref->old_sha1), ref->name);
 
 		if (!o || o->type != commit_type || !(o->flags & COMPLETE))
 			continue;
diff --git a/name-rev.c b/name-rev.c
index 21fecdf..8ac2d72 100644
--- a/name-rev.c
+++ b/name-rev.c
@@ -164,7 +164,7 @@ int main(int argc, char **argv)
 			continue;
 		}
 
-		o = deref_tag(parse_object(sha1));
+		o = deref_tag(parse_object(sha1), *argv);
 		if (!o || o->type != commit_type) {
 			fprintf(stderr, "Could not get commit for %s. Skipping.\n",
 					*argv);
diff --git a/send-pack.c b/send-pack.c
index 9f9a6e7..3ee6794 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -126,12 +126,12 @@ static int ref_newer(const unsigned char
 	/* Both new and old must be commit-ish and new is descendant of
 	 * old.  Otherwise we require --force.
 	 */
-	o = deref_tag(parse_object(old_sha1));
+	o = deref_tag(parse_object(old_sha1), NULL);
 	if (!o || o->type != commit_type)
 		return 0;
 	old = (struct commit *) o;
 
-	o = deref_tag(parse_object(new_sha1));
+	o = deref_tag(parse_object(new_sha1), NULL);
 	if (!o || o->type != commit_type)
 		return 0;
 	new = (struct commit *) o;
diff --git a/server-info.c b/server-info.c
index 8e13c94..23d89fb 100644
--- a/server-info.c
+++ b/server-info.c
@@ -13,7 +13,7 @@ static int add_info_ref(const char *path
 
 	fprintf(info_ref_fp, "%s	%s\n", sha1_to_hex(sha1), path);
 	if (o->type == tag_type) {
-		o = deref_tag(o);
+		o = deref_tag(o, path);
 		if (o)
 			fprintf(info_ref_fp, "%s	%s^{}\n",
 				sha1_to_hex(o->sha1), path);
diff --git a/sha1_name.c b/sha1_name.c
index fe409fb..ae4c801 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -349,7 +349,7 @@ static int peel_onion(const char *name, 
 	if (!o)
 		return -1;
 	if (!type_string) {
-		o = deref_tag(o);
+		o = deref_tag(o, NULL);
 		if (!o || (!o->parsed && !parse_object(o->sha1)))
 			return -1;
 		memcpy(sha1, o->sha1, 20);
diff --git a/tag.c b/tag.c
index b1ab75f..9cd09cf 100644
--- a/tag.c
+++ b/tag.c
@@ -3,10 +3,18 @@
 
 const char *tag_type = "tag";
 
-struct object *deref_tag(struct object *o)
+/* The refname argument is optional and suitable to use only if
+ * we use this to resolve an actual tag ref. */
+struct object *deref_tag(struct object *o, const char *refname)
 {
+	struct object *o2 = o;
 	while (o && o->type == tag_type)
-		o = parse_object(((struct tag *)o)->tagged->sha1);
+		o = parse_object(((struct tag *)(o2 = o))->tagged->sha1);
+	if (!o)
+		fprintf(stderr, "warning: invalid tag %s\n",
+		        refname ? refname
+		        : (o2 ? sha1_to_hex(o2->sha1)
+		           : "<unknown> (this is probably internal GIT error)"));
 	return o;
 }
 
diff --git a/tag.h b/tag.h
index 36e5324..2b60b45 100644
--- a/tag.h
+++ b/tag.h
@@ -15,6 +15,6 @@ struct tag {
 extern struct tag *lookup_tag(const unsigned char *sha1);
 extern int parse_tag_buffer(struct tag *item, void *data, unsigned long size);
 extern int parse_tag(struct tag *item);
-extern struct object *deref_tag(struct object *);
+extern struct object *deref_tag(struct object *, const char *refname);
 
 #endif /* TAG_H */
diff --git a/upload-pack.c b/upload-pack.c
index f0b8e63..cc5b3e3 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -226,7 +226,7 @@ static int send_ref(const char *refname,
 		nr_our_refs++;
 	}
 	if (o->type == tag_type) {
-		o = deref_tag(o);
+		o = deref_tag(o, refname);
 		if (o)
 			packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname);
 	}

-- 
				Petr "Pasky" Baudis
Stuff: http://pasky.or.cz/
VI has two modes: the one in which it beeps and the one in which
it doesn't.

^ permalink raw reply related

* [PATCH] gitk: fix for empty line after Comments
From: Pavel Roskin @ 2005-11-02 20:42 UTC (permalink / raw)
  To: git

git-diff-tree called with one tree-ish shows the commit ID, which should
be ignored by gitk.  getblobdiffs() and gettreediffline() recognize and
ignore commit ID now.

Also, the p variable that was used as the other argument git-diff-tree
is no longer used, so it's dropped in both places.

Signed-off-by: Pavel Roskin <proski@gnu.org>

diff --git a/gitk b/gitk
index a9d37d9..e935484 100755
--- a/gitk
+++ b/gitk
@@ -2805,7 +2805,6 @@ proc gettreediffs {ids} {
     set treepending $ids
     set treediff {}
     set id [lindex $ids 0]
-    set p [lindex $ids 1]
     if [catch {set gdtf [open "|git-diff-tree -r $id" r]}] return
     fconfigure $gdtf -blocking 0
     fileevent $gdtf readable [list gettreediffline $gdtf $ids]
@@ -2830,6 +2829,8 @@ proc gettreediffline {gdtf ids} {
 	    }
 	}
 	return
+    } elseif {$n == 40} {
+	return
     }
     set file [lindex $line 5]
     lappend treediff $file
@@ -2840,7 +2841,6 @@ proc getblobdiffs {ids} {
     global difffilestart nextupdate diffinhdr treediffs
 
     set id [lindex $ids 0]
-    set p [lindex $ids 1]
     set env(GIT_DIFF_OPTS) $diffopts
     set cmd [list | git-diff-tree -r -p -C $id]
     if {[catch {set bdf [open $cmd r]} err]} {
@@ -2934,7 +2934,7 @@ proc getblobdiffline {bdf ids} {
 	} elseif {$diffinhdr || $x == "\\"} {
 	    # e.g. "\ No newline at end of file"
 	    $ctext insert end "$line\n" filesep
-	} else {
+	} elseif {![regexp {^[0-9a-f]{40}$} $line]} {
 	    # Something else we don't recognize
 	    if {$curdifftag != "Comments"} {
 		$ctext insert end "\n"


-- 
Regards,
Pavel Roskin

^ permalink raw reply related

* Re: [PATCH] Warn when calling deref_tag() on broken tags
From: Johannes Schindelin @ 2005-11-02 20:47 UTC (permalink / raw)
  To: Petr Baudis; +Cc: Junio C Hamano, git
In-Reply-To: <20051102204101.GE1431@pasky.or.cz>

Hi,

On Wed, 2 Nov 2005, Petr Baudis wrote:

> diff --git a/tag.c b/tag.c
> index b1ab75f..9cd09cf 100644
> --- a/tag.c
> +++ b/tag.c
> @@ -3,10 +3,18 @@
>  
>  const char *tag_type = "tag";
>  
> -struct object *deref_tag(struct object *o)
> +/* The refname argument is optional and suitable to use only if
> + * we use this to resolve an actual tag ref. */
> +struct object *deref_tag(struct object *o, const char *refname)

How about leaving dereg_tag's signature alone, and warn something like 

	"You have a broken tag (sha1=%s).\n"
	"To find out which name(s) it corresponds to, execute\n"
	"\tgit-name-rev --tags %s\n"

What do you think?

Ciao,
Dscho

^ permalink raw reply

* Re: [PATCH] Warn when calling deref_tag() on broken tags
From: Petr Baudis @ 2005-11-02 20:50 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Junio C Hamano, git
In-Reply-To: <Pine.LNX.4.63.0511022145010.13989@wbgn013.biozentrum.uni-wuerzburg.de>

Dear diary, on Wed, Nov 02, 2005 at 09:47:46PM CET, I got a letter
where Johannes Schindelin <Johannes.Schindelin@gmx.de> told me that...
> On Wed, 2 Nov 2005, Petr Baudis wrote:
> > diff --git a/tag.c b/tag.c
> > index b1ab75f..9cd09cf 100644
> > --- a/tag.c
> > +++ b/tag.c
> > @@ -3,10 +3,18 @@
> >  
> >  const char *tag_type = "tag";
> >  
> > -struct object *deref_tag(struct object *o)
> > +/* The refname argument is optional and suitable to use only if
> > + * we use this to resolve an actual tag ref. */
> > +struct object *deref_tag(struct object *o, const char *refname)
> 
> How about leaving dereg_tag's signature alone, and warn something like 
> 
> 	"You have a broken tag (sha1=%s).\n"
> 	"To find out which name(s) it corresponds to, execute\n"
> 	"\tgit-name-rev --tags %s\n"
> 
> What do you think?

I don't know. For multiple broken tags, or multiple invocations of
deref_tag() on the same tag, this will fill a lot of screen estate. And
if we mostly actually have this information, why not show it to the user
right aay?

-- 
				Petr "Pasky" Baudis
Stuff: http://pasky.or.cz/
VI has two modes: the one in which it beeps and the one in which
it doesn't.

^ permalink raw reply

* Re: [PATCH] Clean up the SunOS Makefile rule
From: Junio C Hamano @ 2005-11-02 20:56 UTC (permalink / raw)
  To: git; +Cc: Peter Eriksen
In-Reply-To: <20051102192730.GA17706@ebar091.ebar.dtu.dk>

"Peter Eriksen" <s022018@student.dtu.dk> writes:

> Don't set a non-standard CURLDIR as default, and fix an error
> in Solaris 10 by setting NEEDS_LIBICONV.

Just to make it clear to everybody, these platform defines are
just to give default that is intended to help majority of the
users.  You do not have to cover everybody on that platform.
Giving *one* default CURLDIR, as long as it helps major portion
of the user base, would be helpful.  In other words, it is OK as
long as the user can say:

	solaris$ gmake CURLDIR=/I/have/my/curl/here

to override what you chose, and /opt/sfw/ is where *many* (if
not most) of the Solaris installations have curl.  I do not have
access to many different flavours of Solaris boxes, but one
machine I have at work (5.9) seems to have it installed there.

Could Solaris users on the list help us out, as Peter asks?  How
many of you have curl in /opt/sfw?  How many others have curl in
somewhere else and think that somewhere else would be more
appropriate default?  If this user poll results in either a
default location better than /opt/sfw, or diverse locations with
no clear majority, then it would make sense to remove the
current default, but otherwise I would say we do not need to
drop it.

People who built curl library and installed at random places
themselves do not count -- they know what they are doing and are
perfectly capable of overriding whatever we say in our Makefile
from the comand line.

Although I think always requiring LIBICONV is OK there, and it
probably is needed on *all* Solaris boxes, but in principle,
NEEDS_LIBICONV is similar.  If the user cannot say:

	solaris$ gmake NEEDS_LIBICONV= ;# No thanks, on my Solaris

to disable -liconv, and if some Solaris installations do not
want -liconv, then that is a problem.  But GNU make seems to do
the right thing; ifdef NEEDS_LIBICONV seems to evaluate to false
if your user overrides it to be empty from the command line.

^ permalink raw reply

* Re: [PATCH] Warn when calling deref_tag() on broken tags
From: Junio C Hamano @ 2005-11-02 21:18 UTC (permalink / raw)
  To: Petr Baudis; +Cc: git
In-Reply-To: <20051102204101.GE1431@pasky.or.cz>

Petr Baudis <pasky@suse.cz> writes:

> +struct object *deref_tag(struct object *o, const char *refname)
>  {
> +	struct object *o2 = o;
>  	while (o && o->type == tag_type)
> -		o = parse_object(((struct tag *)o)->tagged->sha1);
> +		o = parse_object(((struct tag *)(o2 = o))->tagged->sha1);
> +	if (!o)
> +		fprintf(stderr, "warning: invalid tag %s\n",
> +		        refname ? refname
> +		        : (o2 ? sha1_to_hex(o2->sha1)
> +		           : "<unknown> (this is probably internal GIT error)"));
>  	return o;
>  }

I wonder if it would make more sense to keep the root of the
traversal for this function (i.e. the original value of o) for
error reporting purposes, like this:

        struct object *deref_tag(struct object *o, const char *refname)
        {
                struct object *o2 = o;
                while (o && o->type == tag_type)
                        o = parse_object(((struct tag *)o)->tagged->sha1);
                if (!o)
                        fprintf(stderr, "warning: invalid tag %s\n",
                                refname ? refname
                                : (o2 ? sha1_to_hex(o2->sha1)
                                   : "<unknown> (this is probably internal GIT error)"));
                return o;
        }

What do you think?

Other than that, the patch is OK by me.  Thanks.

^ permalink raw reply

* [PATCH][Cogito] Let cg-push default to git+ssh on a host:path syntax
From: Josef Weidendorfer @ 2005-11-02 21:27 UTC (permalink / raw)
  To: Petr Baudis; +Cc: git

Let cg-push default to git+ssh on a host:path syntax

cg-update/cg-fetch fall back to git+ssh protocol for host:path
syntax. cg-push should do the same.

Signed-off-by: Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
---

 cg-push |    6 +++++-
 1 files changed, 5 insertions(+), 1 deletions(-)

applies-to: 9294c00b9e87de048c0d29f8ad99c0a6d2733b16
1f6693e09a8aa4c02297472764b972a07d1c0d02
diff --git a/cg-push b/cg-push
index 4c29b33..d63fb65 100755
--- a/cg-push
+++ b/cg-push
@@ -51,8 +51,12 @@ if echo "$uri" | grep -q "^http://"; the
 	die "pushing over HTTP not supported yet"
 elif echo "$uri" | grep -q "^git+ssh://"; then
 	git-send-pack "$(echo "$uri" | sed 's#^git+ssh://\([^/]*\)\(/.*\)$#\1:\2#')" $_git_head$sprembranch "${tags[@]}"
+elif echo "$uri" | grep -q "^rsync://"; then
+        die "pushing over rsync not supported"
 elif echo "$uri" | grep -q ":"; then
-	die "pushing over rsync not supported"
+	echo "WARNING: I guessed the host:path syntax was used and fell back to the git+ssh protocol."
+	echo "WARNING: The host:path syntax is evil because it is implicit. Please just use a URI."
+	git-send-pack "$uri" $_git_head$sprembranch "${tags[@]}"
 else
 	remgit="$uri"; [ -d "$remgit/.git" ] && remgit="$remgit/.git"
 	if is_same_repo "$_git_objects" "$remgit/objects"; then
---
0.99.9

^ permalink raw reply related


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