Code

GIT 0.99.9e v0.99.9e
authorJunio C Hamano <junkio@cox.net>
Mon, 7 Nov 2005 02:57:40 +0000 (18:57 -0800)
committerJunio C Hamano <junkio@cox.net>
Mon, 7 Nov 2005 02:57:40 +0000 (18:57 -0800)
Signed-off-by: Junio C Hamano <junkio@cox.net>
26 files changed:
Documentation/git-clone.txt
Documentation/git-http-push.txt [new file with mode: 0644]
Documentation/git-merge.txt
Documentation/git-pull.txt
Documentation/install-webdoc.sh
Documentation/merge-strategies.txt [new file with mode: 0644]
Documentation/pull-fetch-param.txt
INSTALL
Makefile
debian/changelog
debian/control
debian/git-arch.files [new file with mode: 0644]
debian/git-cvs.files [new file with mode: 0644]
debian/git-doc.files [new file with mode: 0644]
debian/git-email.files [new file with mode: 0644]
debian/git-svn.files [new file with mode: 0644]
debian/rules
fetch-pack.c
git-clone.sh
git-format-patch.sh
git-status.sh
http-fetch.c
http-push.c [new file with mode: 0644]
ls-files.c
sha1_file.c
t/t4102-apply-rename.sh

index dd92cdefc2a2c504aea8ca23ce0e01746020036f..fefd2985f3f1424ddaff24fed6b57a85b86f8acc 100644 (file)
@@ -12,7 +12,22 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Clones a repository into a newly created directory.
+Clones a repository into a newly created directory.  All remote
+branch heads are copied under `$GIT_DIR/refs/heads/`, except
+that the remote `master` is also copied to `origin` branch.
+
+In addition, `$GIT_DIR/remotes/origin` file is set up to have
+this line:
+
+       Pull: master:origin
+
+This is to help the typical workflow of working off of the
+remote `master` branch.  Every time `git pull` without argument
+is run, the progress on the remote `master` branch is tracked by
+copying it into the local `origin` branch, and merged into the
+branch you are currently working on.  Remote branches other than
+`master` are also added there to be tracked.
+
 
 OPTIONS
 -------
@@ -28,9 +43,10 @@ OPTIONS
 --shared::
 -s::
        When the repository to clone is on the local machine,
-       instead of using hard links automatically setup
+       instead of using hard links, automatically setup
        .git/objects/info/alternatives to share the objects
-       with the source repository
+       with the source repository.  The resulting repository
+       starts out without any object of its own.
 
 --quiet::
 -q::
@@ -49,14 +65,13 @@ OPTIONS
 
 <repository>::
        The (possibly remote) repository to clone from.  It can
-       be an "rsync://host/dir" URL, an "http://host/dir" URL,
-       or [<host>:]/dir notation that is used by 'git-clone-pack'.
-       Currently http transport is not supported.
+       be any URL git-fetch supports.
 
 <directory>::
        The name of a new directory to be cloned into.  It is an
        error to specify an existing directory.
 
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>
diff --git a/Documentation/git-http-push.txt b/Documentation/git-http-push.txt
new file mode 100644 (file)
index 0000000..c7066d6
--- /dev/null
@@ -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
index 3e058db996cbb1200fc304b3a090cbd4e13fce80..b3ef19bae44268a3a2159f9ccfbce51314312ab7 100644 (file)
@@ -34,6 +34,8 @@ include::merge-pull-opts.txt[]
        least one <remote>.  Specifying more than one <remote>
        obviously means you are trying an Octopus.
 
+include::merge-strategies.txt[]
+
 
 SEE ALSO
 --------
index ec10a2f409a6cd3d7340cfc934c53ccd5a00f5f0..7ebb08da0c7c9065b1d26398601f69584fc867ef 100644 (file)
@@ -31,42 +31,8 @@ include::pull-fetch-param.txt[]
 
 include::merge-pull-opts.txt[]
 
+include::merge-strategies.txt[]
 
-MERGE STRATEGIES
-----------------
-
-resolve::
-       This can only resolve two heads (i.e. the current branch
-       and another branch you pulled from) using 3-way merge
-       algorithm.  It tries to carefully detect criss-cross
-       merge ambiguities and is considered generally safe and
-       fast.  This is the default merge strategy when pulling
-       one branch.
-
-recursive::
-       This can only resolve two heads using 3-way merge
-       algorithm.  When there are more than one common
-       ancestors that can be used for 3-way merge, it creates a
-       merged tree of the common ancestores and uses that as
-       the reference tree for the 3-way merge.  This has been
-       reported to result in fewer merge conflicts without
-       causing mis-merges by tests done on actual merge commits
-       taken from Linux 2.6 kernel development history.
-       Additionally this can detect and handle merges involving
-       renames.
-
-octopus::
-       This resolves more than two-head case, but refuses to do
-       complex merge that needs manual resolution.  It is
-       primarily meant to be used for bundling topic branch
-       heads together.  This is the default merge strategy when
-       pulling more than one branch.
-
-ours::
-       This resolves any number of heads, but the result of the
-       merge is always the current branch head.  It is meant to
-       be used to supersede old development history of side
-       branches.
 
 
 EXAMPLES
index d593ab988be48ba9f9d910e9fdbc73e2837241cc..50638c78d5a4067320db4519131d224c55b3925a 100755 (executable)
@@ -2,7 +2,7 @@
 
 T="$1"
 
-for h in *.html howto/*.txt howto/*.html
+for h in *.html *.txt howto/*.txt howto/*.html
 do
        diff -u -I'Last updated [0-9][0-9]-[A-Z][a-z][a-z]-' "$T/$h" "$h" || {
                echo >&2 "# install $h $T/$h"
@@ -12,7 +12,7 @@ do
        }
 done
 strip_leading=`echo "$T/" | sed -e 's|.|.|g'`
-for th in "$T"/*.html "$T"/howto/*.txt "$T"/howto/*.html
+for th in "$T"/*.html "$T"/*.txt "$T"/howto/*.txt "$T"/howto/*.html
 do
        h=`expr "$th" : "$strip_leading"'\(.*\)'`
        case "$h" in
diff --git a/Documentation/merge-strategies.txt b/Documentation/merge-strategies.txt
new file mode 100644 (file)
index 0000000..3ec56d2
--- /dev/null
@@ -0,0 +1,35 @@
+MERGE STRATEGIES
+----------------
+
+resolve::
+       This can only resolve two heads (i.e. the current branch
+       and another branch you pulled from) using 3-way merge
+       algorithm.  It tries to carefully detect criss-cross
+       merge ambiguities and is considered generally safe and
+       fast.  This is the default merge strategy when pulling
+       one branch.
+
+recursive::
+       This can only resolve two heads using 3-way merge
+       algorithm.  When there are more than one common
+       ancestors that can be used for 3-way merge, it creates a
+       merged tree of the common ancestores and uses that as
+       the reference tree for the 3-way merge.  This has been
+       reported to result in fewer merge conflicts without
+       causing mis-merges by tests done on actual merge commits
+       taken from Linux 2.6 kernel development history.
+       Additionally this can detect and handle merges involving
+       renames.
+
+octopus::
+       This resolves more than two-head case, but refuses to do
+       complex merge that needs manual resolution.  It is
+       primarily meant to be used for bundling topic branch
+       heads together.  This is the default merge strategy when
+       pulling more than one branch.
+
+ours::
+       This resolves any number of heads, but the result of the
+       merge is always the current branch head.  It is meant to
+       be used to supersede old development history of side
+       branches.
index 57e9ddf0d605e5536ce21901494c0058ef4ce857..5c2888e16362798a494b146036e721a19f622517 100644 (file)
@@ -82,14 +82,19 @@ must know this is the expected usage pattern for a branch.
 [NOTE]
 You never do your own development on branches that appear
 on the right hand side of a <refspec> colon on `Pull:` lines;
-they are to be updated by `git-fetch`.  The corollary is that
-a local branch should be introduced and named on a <refspec>
-right-hand-side if you intend to do development derived from
-that branch.
-This leads to the common `Pull: master:origin` mapping of a
-remote `master` branch to a local `origin` branch, which
-is then merged to a local development branch, again typically
-named `master`.
+they are to be updated by `git-fetch`.  If you intend to do
+development derived from a remote branch `B`, have a `Pull:`
+line to track it (i.e. `Pull: B:remote-B`), and have a separate
+branch `my-B` to do your development on top of it.  The latter
+is created by `git branch my-B remote-B` (or its equivalent `git
+checkout -b my-B remote-B`).  Run `git fetch` to keep track of
+the progress of the remote side, and when you see something new
+on the remote branch, merge it into your development branch with
+`git pull . remote-B`, while you are on `my-B` branch.
+The common `Pull: master:origin` mapping of a remote `master`
+branch to a local `origin` branch, which is then merged to a
+ocal development branch, again typically named `master`, is made
+when you run `git clone` for you to follow this pattern.
 +
 [NOTE]
 There is a difference between listing multiple <refspec>
diff --git a/INSTALL b/INSTALL
index 6e336d7c204ccb503fd7499ec9d9674da68e8318..bbb13f3fd98f96af5521226a8cdaf74f8c6732a2 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -46,6 +46,9 @@ Issues of note:
          transfer, you are probabaly OK if you do not have
          them.
 
+       - expat library; git-http-push uses it for remote lock
+         management over DAV.  Similar to "curl" above, this is optional.
+
        - "GNU diff" to generate patches.  Of course, you don't _have_ to
          generate patches if you don't want to, but let's face it, you'll
          be wanting to. Or why did you get git in the first place?
index 2ae69e3aa157b2c630ed3a7683ea75153d1460d0..56e0fb1ba60713c7d7559ffb3817c2d4ed074630 100644 (file)
--- 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
 # 1461501637330902918203684832716283019655932542976 hashes do not give you
 # sufficient guarantee that no collisions between objects will ever happen.
 
-# DEFINES += -DCOLLISION_CHECK
-
 # Define USE_NSEC below if you want git to care about sub-second file mtimes
 # and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and
 # it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely
 # randomly break unless your underlying filesystem supports those sub-second
 # times (my ext3 doesn't).
 
-# DEFINES += -DUSE_NSEC
-
 # Define USE_STDEV below if you want git to care about the underlying device
 # change being considered an inode change from the update-cache perspective.
 
-# DEFINES += -DUSE_STDEV
-
-GIT_VERSION = 0.99.9d
+GIT_VERSION = 0.99.9e
 
+# CFLAGS is for the users to override from the command line.
 CFLAGS = -g -O2 -Wall
-ALL_CFLAGS = $(CFLAGS) $(PLATFORM_DEFINES) $(DEFINES)
+ALL_CFLAGS = $(CFLAGS)
 
 prefix = $(HOME)
 bindir = $(prefix)/bin
@@ -185,6 +184,10 @@ uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not')
 ifeq ($(uname_S),Darwin)
        NEEDS_SSL_WITH_CRYPTO = YesPlease
        NEEDS_LIBICONV = YesPlease
+       ## fink
+       ALL_CFLAGS += -I/sw/include -L/sw/lib
+       ## darwinports
+       ALL_CFLAGS += -I/opt/local/include -L/opt/local/lib
 endif
 ifeq ($(uname_S),SunOS)
        NEEDS_SOCKET = YesPlease
@@ -194,19 +197,19 @@ ifeq ($(uname_S),SunOS)
        NO_STRCASESTR = YesPlease
        INSTALL = ginstall
        TAR = gtar
-       PLATFORM_DEFINES += -D__EXTENSIONS__
+       ALL_CFLAGS += -D__EXTENSIONS__
 endif
 ifeq ($(uname_O),Cygwin)
        NO_STRCASESTR = YesPlease
        NEEDS_LIBICONV = YesPlease
        NO_IPV6 = YesPlease
        X = .exe
-       PLATFORM_DEFINES += -DUSE_SYMLINK_HEAD=0
+       ALL_CFLAGS += -DUSE_SYMLINK_HEAD=0
 endif
 ifeq ($(uname_S),OpenBSD)
        NO_STRCASESTR = YesPlease
        NEEDS_LIBICONV = YesPlease
-       PLATFORM_DEFINES += -I/usr/local/include -L/usr/local/lib
+       ALL_CFLAGS += -I/usr/local/include -L/usr/local/lib
 endif
 ifneq (,$(findstring arm,$(uname_M)))
        ARM_SHA1 = YesPlease
@@ -217,12 +220,16 @@ endif
 ifndef NO_CURL
        ifdef CURLDIR
                # This is still problematic -- gcc does not want -R.
-               CFLAGS += -I$(CURLDIR)/include
+               ALL_CFLAGS += -I$(CURLDIR)/include
                CURL_LIBCURL = -L$(CURLDIR)/lib -R$(CURLDIR)/lib -lcurl
        else
                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
@@ -240,13 +247,13 @@ ifndef NO_OPENSSL
        OPENSSL_LIBSSL = -lssl
        ifdef OPENSSLDIR
                # Again this may be problematic -- gcc does not always want -R.
-               CFLAGS += -I$(OPENSSLDIR)/include
+               ALL_CFLAGS += -I$(OPENSSLDIR)/include
                OPENSSL_LINK = -L$(OPENSSLDIR)/lib -R$(OPENSSLDIR)/lib
        else
                OPENSSL_LINK =
        endif
 else
-       DEFINES += -DNO_OPENSSL
+       ALL_CFLAGS += -DNO_OPENSSL
        MOZILLA_SHA1 = 1
        OPENSSL_LIBSSL =
 endif
@@ -258,7 +265,7 @@ endif
 ifdef NEEDS_LIBICONV
        ifdef ICONVDIR
                # Again this may be problematic -- gcc does not always want -R.
-               CFLAGS += -I$(ICONVDIR)/include
+               ALL_CFLAGS += -I$(ICONVDIR)/include
                ICONV_LINK = -L$(ICONVDIR)/lib -R$(ICONVDIR)/lib
        else
                ICONV_LINK =
@@ -276,15 +283,15 @@ ifdef NEEDS_NSL
        SIMPLE_LIB += -lnsl
 endif
 ifdef NO_STRCASESTR
-       DEFINES += -Dstrcasestr=gitstrcasestr -DNO_STRCASESTR=1
+       ALL_CFLAGS += -Dstrcasestr=gitstrcasestr -DNO_STRCASESTR=1
        LIB_OBJS += compat/strcasestr.o
 endif
 ifdef NO_MMAP
-       DEFINES += -Dmmap=gitfakemmap -Dmunmap=gitfakemunmap -DNO_MMAP
+       ALL_CFLAGS += -Dmmap=gitfakemmap -Dmunmap=gitfakemunmap -DNO_MMAP
        LIB_OBJS += compat/mmap.o
 endif
 ifdef NO_IPV6
-       DEFINES += -DNO_IPV6 -Dsockaddr_storage=sockaddr_in
+       ALL_CFLAGS += -DNO_IPV6 -Dsockaddr_storage=sockaddr_in
 endif
 
 ifdef PPC_SHA1
@@ -305,7 +312,7 @@ endif
 endif
 endif
 
-DEFINES += -DSHA1_HEADER=$(call shellquote,$(SHA1_HEADER))
+ALL_CFLAGS += -DSHA1_HEADER=$(call shellquote,$(SHA1_HEADER))
 
 SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
          $(patsubst %.perl,%,$(SCRIPT_PERL)) \
@@ -375,6 +382,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
@@ -454,8 +462,8 @@ clean:
        rm -f git-core.spec *.pyc *.pyo
        rm -rf $(GIT_TARNAME)
        rm -f $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
-       rm -f git-core_$(GIT_VERSION)-*.deb git-core_$(GIT_VERSION)-*.dsc
-       rm -f git-tk_$(GIT_VERSION)-*.deb
+       rm -f git-core_$(GIT_VERSION)-*.dsc
+       rm -f git-*_$(GIT_VERSION)-*.deb
        $(MAKE) -C Documentation/ clean
        $(MAKE) -C templates clean
        $(MAKE) -C t/ clean
index c643015a7dd0debf6772fd2fe6aba104efa9d16b..9cf900fb0892210206b134c65713d9dea33df1fa 100644 (file)
@@ -1,3 +1,9 @@
+git-core (0.99.9e-0) unstable; urgency=low
+
+  * GIT 0.99.9e
+
+ -- Junio C Hamano <junkio@cox.net>  Sun,  6 Nov 2005 18:37:18 -0800
+
 git-core (0.99.9d-0) unstable; urgency=low
 
   * GIT 0.99.9d
@@ -22,6 +28,19 @@ git-core (0.99.9a-0) unstable; urgency=low
 
  -- Junio C Hamano <junkio@cox.net>  Sun, 30 Oct 2005 15:03:32 -0800
 
+git-core (0.99.9.GIT-0) unstable; urgency=low
+
+  * Test Build.
+
+ -- Junio C Hamano <junkio@cox.net>  Sat,  5 Nov 2005 11:18:13 -0800
+
+git-core (0.99.9-1) unstable; urgency=low
+
+  * Split the git-core binary package into core, doc, and foreign SCM
+    interoperability modules.
+
+ -- Junio C Hamano <junkio@cox.net>  Sat,  5 Nov 2005 11:18:13 -0800
+
 git-core (0.99.9-0) unstable; urgency=low
 
   * GIT 0.99.9
index 1f45f93b365ee11d743105582d1d29eea0fb0232..8cc527e2daf7888ec60f2f0fe2fa8910ef770002 100644 (file)
@@ -8,7 +8,7 @@ Standards-Version: 3.6.1
 Package: git-core
 Architecture: any
 Depends: ${shlibs:Depends}, ${perl:Depends}, ${misc:Depends}, rcs
-Recommends: rsync, curl, ssh, libmail-sendmail-perl, libemail-valid-perl, libsvn-core-perl (>= 1.2.1), python (>= 2.4.0), less
+Recommends: rsync, curl, ssh, python (>= 2.4.0), less
 Suggests: cogito, patch
 Conflicts: git, cogito (<< 0.13)
 Description: The git content addressable filesystem
@@ -18,9 +18,46 @@ Description: The git content addressable filesystem
  enables human beings to work with the database in a manner to a degree
  similar to other SCM tools.
 
+Package: git-doc
+Architecture: all
+Depends: ${shlibs:Depends}, ${misc:Depends}, git-core
+Description: The git content addressable filesystem, Documentation
+ This package contains documentation for GIT.
+
 Package: git-tk
 Architecture: all
 Depends: ${shlibs:Depends}, ${misc:Depends}, git-core, tk8.4
 Description: The git content addressable filesystem, GUI add-on
  This package contains 'gitk', the git revision tree visualizer.
 
+Package: git-svn
+Architecture: all
+Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, git-core, libsvn-core-perl (>= 1.2.1)
+Suggests: subversion
+Description: The git content addressable filesystem, SVN interoperability
+ This package contains 'git-svnimport', to import development history from
+ SVN repositories.
+
+Package: git-arch
+Architecture: all
+Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, git-core
+Suggests: tla, bazaar
+Description: The git content addressable filesystem, GNUArch interoperability
+ This package contains 'git-archimport', to import development history from
+ GNUArch repositories.
+
+Package: git-cvs
+Architecture: all
+Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, git-core
+Suggests: cvs
+Description: The git content addressable filesystem, CVS interoperability
+ This package contains 'git-cvsimport', to import development history from
+ CVS repositories.
+
+Package: git-email
+Architecture: all
+Depends: ${shlibs:Depends}, ${misc:Depends}, git-core, libmail-sendmail-perl, libemail-valid-perl
+Description: The git content addressable filesystem, e-mail add-on
+ This package contains 'git-send-email', to send a series of patch e-mails.
+
+
diff --git a/debian/git-arch.files b/debian/git-arch.files
new file mode 100644 (file)
index 0000000..1ad4656
--- /dev/null
@@ -0,0 +1,2 @@
+/usr/bin/git-archimport
+/usr/share/doc/git-core/git-archimport.*
diff --git a/debian/git-cvs.files b/debian/git-cvs.files
new file mode 100644 (file)
index 0000000..8bf5090
--- /dev/null
@@ -0,0 +1,2 @@
+/usr/bin/git-cvsimport
+/usr/share/doc/git-core/git-cvsimport.*
diff --git a/debian/git-doc.files b/debian/git-doc.files
new file mode 100644 (file)
index 0000000..567f5d7
--- /dev/null
@@ -0,0 +1,7 @@
+/usr/share/doc/git-core/*.txt
+/usr/share/doc/git-core/*.html
+/usr/share/doc/git-core/*/*.html
+/usr/share/doc/git-core/*/*.txt
+
+
+
diff --git a/debian/git-email.files b/debian/git-email.files
new file mode 100644 (file)
index 0000000..236754c
--- /dev/null
@@ -0,0 +1,2 @@
+/usr/bin/git-send-email
+/usr/share/doc/git-core/git-send-email.*
diff --git a/debian/git-svn.files b/debian/git-svn.files
new file mode 100644 (file)
index 0000000..317b12a
--- /dev/null
@@ -0,0 +1,2 @@
+/usr/bin/git-svnimport
+/usr/share/doc/git-core/git-svnimport.*
index 568d430932bf96efd9c6a448adbb42507c821a0f..4ab221ce9ea5a24201b1bf37c3c9d07eff243770 100755 (executable)
@@ -41,7 +41,7 @@ MAN_DESTDIR := $(DESTDIR)/$(MANDIR)
 build: debian/build-stamp
 debian/build-stamp:
        dh_testdir
-       $(MAKE) prefix=$(PREFIX) PYTHON_PATH=/usr/bin/python2.4 all doc test
+       $(MAKE) prefix=$(PREFIX) PYTHON_PATH=/usr/bin/python2.4 all test doc
        touch debian/build-stamp
 
 debian-clean:
@@ -62,10 +62,15 @@ install: build
        make DESTDIR=$(DESTDIR) prefix=$(PREFIX) mandir=$(MANDIR) \
                install install-doc
 
-       mkdir -p $(DOC_DESTDIR)
-       find $(DOC) '(' -name '*.txt' -o -name '*.html' ')' -exec install {} $(DOC_DESTDIR) ';'
+       make -C Documentation DESTDIR=$(DESTDIR) prefix=$(PREFIX) \
+               WEBDOC_DEST=$(DOC_DESTDIR) install-webdoc
 
+       dh_movefiles -p git-arch
+       dh_movefiles -p git-cvs
+       dh_movefiles -p git-svn
        dh_movefiles -p git-tk
+       dh_movefiles -p git-email
+       dh_movefiles -p git-doc
        dh_movefiles -p git-core
        find debian/tmp -type d -o -print | sed -e 's/^/? /'
 
index cb2171523c6f30911b1d80d622d709b7f39fdb97..65659826601eb9c850266539f6c9e7807a0b467e 100644 (file)
@@ -458,5 +458,19 @@ int main(int argc, char **argv)
        close(fd[0]);
        close(fd[1]);
        finish_connect(pid);
+
+       if (!ret && nr_heads) {
+               /* If the heads to pull were given, we should have
+                * consumed all of them by matching the remote.
+                * Otherwise, 'git-fetch remote no-such-ref' would
+                * silently succeed without issuing an error.
+                */
+               for (i = 0; i < nr_heads; i++)
+                       if (heads[i] && heads[i][0]) {
+                               error("no such remote ref %s", heads[i]);
+                               ret = 1;
+                       }
+       }
+
        return ret;
 }
index 1adf604172d5131222e152509c750f1e5a4ed9b0..4fdd6525148d50bd3a014bfc0f60eefd0f16efb9 100755 (executable)
@@ -202,8 +202,16 @@ then
                mkdir -p .git/remotes &&
                echo >.git/remotes/origin \
                "URL: $repo
-Pull: $head_points_at:origin"
-               cp ".git/refs/heads/$head_points_at" .git/refs/heads/origin
+Pull: $head_points_at:origin" &&
+               cp ".git/refs/heads/$head_points_at" .git/refs/heads/origin &&
+               find .git/refs/heads -type f -print |
+               while read ref
+               do
+                       head=`expr "$ref" : '.git/refs/heads/\(.*\)'` &&
+                       test "$head_points_at" = "$head" ||
+                       test "origin" = "$head" ||
+                       echo "Pull: ${head}:${head}"
+               done >>.git/remotes/origin
        esac
 
        case "$no_checkout" in
index 5b93ff80a06f385c11cdaa6b5a41eaee5ae9cbc1..b43ba3909c200b84e23d678408a0bf876fd3f3c6 100755 (executable)
@@ -101,7 +101,7 @@ case "$#,$1" in
        ;;
 1,?*..)
        # single "rev1.." should mean "rev1..HEAD"
-       set x "$1"HEAD"
+       set x "$1"HEAD
        shift
        ;;
 1,*)
index 62a24a9b03a259fa94f61b3cb060c2b99f55e40e..837f334d8760c9ff4af4de5d057e97d7af3cfc61 100755 (executable)
@@ -41,7 +41,7 @@ git-update-index -q --unmerged --refresh || exit
 
 if GIT_DIR="$GIT_DIR" git-rev-parse --verify HEAD >/dev/null 2>&1
 then
-       git-diff-index -M --cached --name-status HEAD |
+       git-diff-index -M --cached --name-status --diff-filter=MDTCRA HEAD |
        sed -e '
                s/\\/\\\\/g
                s/ /\\ /g
index b12779dcb4a1ce92db94f545e1b78d7ea8b098e0..ea8af1b2defaf7e11dff0a09952234f3c7626376 100644 (file)
@@ -578,6 +578,7 @@ void process_curl_messages(void)
 
        while (curl_message != NULL) {
                if (curl_message->msg == CURLMSG_DONE) {
+                       int curl_result = curl_message->data.result;
                        slot = active_queue_head;
                        while (slot != NULL &&
                               slot->curl != curl_message->easy_handle)
@@ -587,7 +588,7 @@ void process_curl_messages(void)
                                active_requests--;
                                slot->done = 1;
                                slot->in_use = 0;
-                               slot->curl_result = curl_message->data.result;
+                               slot->curl_result = curl_result;
                                curl_easy_getinfo(slot->curl,
                                                  CURLINFO_HTTP_CODE,
                                                  &slot->http_code);
@@ -599,8 +600,7 @@ void process_curl_messages(void)
                                fprintf(stderr, "Received DONE message for unknown request!\n");
                        }
                        if (request != NULL) {
-                               request->curl_result =
-                                       curl_message->data.result;
+                               request->curl_result = curl_result;
                                request->http_code = slot->http_code;
                                request->slot = NULL;
                                request->state = COMPLETE;
diff --git a/http-push.c b/http-push.c
new file mode 100644 (file)
index 0000000..89fda42
--- /dev/null
@@ -0,0 +1,1811 @@
+#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>"
+
+#define LOCK_TIME 600
+#define LOCK_REFRESH 30
+
+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 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;
+       struct active_lock *lock;
+       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 active_lock
+{
+       int ctx_activelock;
+       int ctx_owner;
+       int ctx_owner_href;
+       int ctx_timeout;
+       int ctx_locktoken;
+       int ctx_locktoken_href;
+       char *url;
+       char *owner;
+       char *token;
+       time_t start_time;
+       long timeout;
+       int refreshing;
+};
+
+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);
+       }
+}
+
+int refresh_lock(struct active_lock *lock)
+{
+       struct active_request_slot *slot;
+       char *if_header;
+       char timeout_header[25];
+       struct curl_slist *dav_headers = NULL;
+       int rc = 0;
+
+       lock->refreshing = 1;
+
+       if_header = xmalloc(strlen(lock->token) + 25);
+       sprintf(if_header, "If: (<opaquelocktoken:%s>)", lock->token);
+       sprintf(timeout_header, "Timeout: Second-%ld", lock->timeout);
+       dav_headers = curl_slist_append(dav_headers, if_header);
+       dav_headers = curl_slist_append(dav_headers, timeout_header);
+
+       slot = get_active_slot();
+       curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
+       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
+       curl_easy_setopt(slot->curl, CURLOPT_URL, lock->url);
+       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);
+               if (slot->curl_result != CURLE_OK) {
+                       fprintf(stderr, "Got HTTP error %ld\n", slot->http_code);
+               } else {
+                       lock->start_time = time(NULL);
+                       rc = 1;
+               }
+       }
+
+       lock->refreshing = 0;
+       curl_slist_free_all(dav_headers);
+       free(if_header);
+
+       return rc;
+}
+
+static void finish_request(struct transfer_request *request)
+{
+       time_t current_time = time(NULL);
+       int time_remaining;
+
+       request->curl_result =  request->slot->curl_result;
+       request->http_code = request->slot->http_code;
+       request->slot = NULL;
+
+       /* Refresh the lock if it is close to timing out */
+       time_remaining = request->lock->start_time + request->lock->timeout
+               - current_time;
+       if (time_remaining < LOCK_REFRESH && !request->lock->refreshing) {
+               if (!refresh_lock(request->lock)) {
+                       fprintf(stderr, "Unable to refresh remote lock\n");
+                       aborted = 1;
+               }
+       }
+
+       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, struct active_lock *lock)
+{
+       struct transfer_request *request = request_queue_head;
+       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 = lock;
+       request->headers = NULL;
+       request->state = NEED_CHECK;
+       request->next = request_queue_head;
+       request_queue_head = 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;
+
+       /* Don't use the index if the pack isn't there */
+       url = xmalloc(strlen(remote->url) + 65);
+       sprintf(url, "%s/objects/pack/pack-%s.pack", remote->url, hex);
+       slot = get_active_slot();
+       curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+       curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);
+       if (start_active_slot(slot)) {
+               run_active_slot(slot);
+               if (slot->curl_result != CURLE_OK) {
+                       free(url);
+                       return error("Unable to verify pack %s is available",
+                                    hex);
+               }
+       } else {
+               return error("Unable to start request");
+       }
+
+       if (has_pack_index(sha1))
+               return 0;
+
+       if (push_verbosely)
+               fprintf(stderr, "Getting index for pack %s\n", hex);
+       
+       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_NOBODY, 0);
+       curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
+       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 (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_activelock_element(void *userData, const char *name, const char **atts)
+{
+       struct active_lock *lock = (struct active_lock *)userData;
+
+       if (lock->ctx_activelock && !strcmp(name, "D:timeout"))
+               lock->ctx_timeout = 1;
+       else if (lock->ctx_owner && strstr(name, "href"))
+               lock->ctx_owner_href = 1;
+       else if (lock->ctx_activelock && strstr(name, "owner"))
+               lock->ctx_owner = 1;
+       else if (lock->ctx_locktoken && !strcmp(name, "D:href"))
+               lock->ctx_locktoken_href = 1;
+       else if (lock->ctx_activelock && !strcmp(name, "D:locktoken"))
+               lock->ctx_locktoken = 1;
+       else if (!strcmp(name, "D:activelock"))
+               lock->ctx_activelock = 1;
+}
+
+static void
+end_activelock_element(void *userData, const char *name)
+{
+       struct active_lock *lock = (struct active_lock *)userData;
+
+       if (lock->ctx_timeout && !strcmp(name, "D:timeout")) {
+               lock->ctx_timeout = 0;
+       } else if (lock->ctx_owner_href && strstr(name, "href")) {
+               lock->ctx_owner_href = 0;
+       } else if (lock->ctx_owner && strstr(name, "owner")) {
+               lock->ctx_owner = 0;
+       } else if (lock->ctx_locktoken_href && !strcmp(name, "D:href")) {
+               lock->ctx_locktoken_href = 0;
+       } else if (lock->ctx_locktoken && !strcmp(name, "D:locktoken")) {
+               lock->ctx_locktoken = 0;
+       } else if (lock->ctx_activelock && !strcmp(name, "D:activelock")) {
+               lock->ctx_activelock = 0;
+       }
+}
+
+static void
+activelock_cdata(void *userData, const XML_Char *s, int len)
+{
+       struct active_lock *lock = (struct active_lock *)userData;
+       char *this = malloc(len+1);
+       strncpy(this, s, len);
+
+       if (lock->ctx_owner_href) {
+               lock->owner = malloc(len+1);
+               strcpy(lock->owner, this);
+       } else if (lock->ctx_locktoken_href) {
+               if (!strncmp(this, "opaquelocktoken:", 16)) {
+                       lock->token = malloc(len-15);
+                       strcpy(lock->token, this+16);
+               }
+       } else if (lock->ctx_timeout) {
+               if (!strncmp(this, "Second-", 7))
+                       lock->timeout = strtol(this+7, NULL, 10);
+       }
+
+       free(this);
+}
+
+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;
+       }
+}
+
+struct active_lock *lock_remote(char *file, long timeout)
+{
+       struct active_request_slot *slot;
+       struct buffer out_buffer;
+       struct buffer in_buffer;
+       char *out_data;
+       char *in_data;
+       char *url;
+       char *ep;
+       char timeout_header[25];
+       struct active_lock *new_lock;
+       XML_Parser parser = XML_ParserCreate(NULL);
+       enum XML_Status result;
+       struct curl_slist *dav_headers = NULL;
+
+       url = xmalloc(strlen(remote->url) + strlen(file) + 1);
+       sprintf(url, "%s%s", remote->url, file);
+
+       /* Make sure leading directories exist for the remote ref */
+       ep = strchr(url + strlen(remote->url) + 11, '/');
+       while (ep) {
+               *ep = 0;
+               slot = get_active_slot();
+               curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
+               curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+               curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_MKCOL);
+               curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
+               if (start_active_slot(slot)) {
+                       run_active_slot(slot);
+                       if (slot->curl_result != CURLE_OK &&
+                           slot->http_code != 405) {
+                               fprintf(stderr,
+                                       "Unable to create branch path %s\n",
+                                       url);
+                               free(url);
+                               return NULL;
+                       }
+               } else {
+                       fprintf(stderr, "Unable to start request\n");
+                       free(url);
+                       return NULL;
+               }
+               *ep = '/';
+               ep = strchr(ep + 1, '/');
+       }
+
+       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;
+
+       in_buffer.size = 4096;
+       in_data = xmalloc(in_buffer.size);
+       in_buffer.posn = 0;
+       in_buffer.buffer = in_data;
+
+       new_lock = xmalloc(sizeof(*new_lock));
+       new_lock->owner = NULL;
+       new_lock->token = NULL;
+       new_lock->timeout = -1;
+       new_lock->refreshing = 0;
+
+       sprintf(timeout_header, "Timeout: Second-%ld", timeout);
+       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_FILE, &in_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_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);
+               if (slot->curl_result != CURLE_OK) {
+                       fprintf(stderr, "Got HTTP error %ld\n", slot->http_code);
+                       free(new_lock);
+                       free(url);
+                       free(out_data);
+                       free(in_data);
+                       return NULL;
+               }
+       } else {
+               free(new_lock);
+               free(url);
+               free(out_data);
+               free(in_data);
+               fprintf(stderr, "Unable to start request\n");
+               return NULL;
+       }
+
+       free(out_data);
+
+       XML_SetUserData(parser, new_lock);
+       XML_SetElementHandler(parser, start_activelock_element,
+                                     end_activelock_element);
+       XML_SetCharacterDataHandler(parser, activelock_cdata);
+       result = XML_Parse(parser, in_buffer.buffer, in_buffer.posn, 1);
+       free(in_data);
+       if (result != XML_STATUS_OK) {
+               fprintf(stderr, "%s", XML_ErrorString(
+                               XML_GetErrorCode(parser)));
+               free(url);
+               free(new_lock);
+               return NULL;
+       }
+
+       if (new_lock->token == NULL || new_lock->timeout <= 0) {
+               if (new_lock->token != NULL)
+                       free(new_lock->token);
+               if (new_lock->owner != NULL)
+                       free(new_lock->owner);
+               free(url);
+               free(new_lock);
+               return NULL;
+       }
+
+       new_lock->url = url;
+       new_lock->start_time = time(NULL);
+       return new_lock;
+}
+
+int unlock_remote(struct active_lock *lock)
+{
+       struct active_request_slot *slot;
+       char *lock_token_header;
+       struct curl_slist *dav_headers = NULL;
+       int rc = 0;
+
+       lock_token_header = xmalloc(strlen(lock->token) + 31);
+       sprintf(lock_token_header, "Lock-Token: <opaquelocktoken:%s>",
+               lock->token);
+       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, lock->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);
+
+       if (lock->owner != NULL)
+               free(lock->owner);
+       free(lock->url);
+       free(lock->token);
+       free(lock);
+
+       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,
+              struct active_lock *lock)
+{
+       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);
+               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);
+               get_delta(sha1, &commit->tree->object, lock);
+       } else if (obj->type == tree_type) {
+               if (push_verbosely)
+                       fprintf(stderr, "walk %s\n", sha1_to_hex(obj->sha1));
+               add_request(obj->sha1, lock);
+               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);
+                       free(entry->name);
+                       free(entry);
+                       entry = next;
+               }
+       } else if (obj->type == blob_type || obj->type == tag_type) {
+               add_request(obj->sha1, lock);
+       }
+}
+
+int update_remote(unsigned char *sha1, struct active_lock *lock)
+{
+       struct active_request_slot *slot;
+       char *out_data;
+       char *if_header;
+       struct buffer out_buffer;
+       struct curl_slist *dav_headers = NULL;
+       int i;
+
+       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, lock->url);
+
+       if (start_active_slot(slot)) {
+               run_active_slot(slot);
+               free(out_data);
+               free(if_header);
+               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);
+               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];
+       struct active_lock *remote_lock;
+       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, LOCK_TIME);
+               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(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_lock);
+               free(remote_path);
+       }
+
+ 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);
+               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;
+}
index d9c8b215f1dcc48ebd7de9f92713f43a09a80e66..f7653e7d32aa6fa72d9c0e3fb1ad618b9536538c 100644 (file)
@@ -348,6 +348,29 @@ static void show_dir_entry(const char *tag, struct nond_on_fs *ent)
        putchar(line_terminator);
 }
 
+static void show_other_files(void)
+{
+       int i;
+       for (i = 0; i < nr_dir; i++) {
+               /* We should not have a matching entry, but we
+                * may have an unmerged entry for this path.
+                */
+               struct nond_on_fs *ent = dir[i];
+               int pos = cache_name_pos(ent->name, ent->len);
+               struct cache_entry *ce;
+               if (0 <= pos)
+                       die("bug in show-other-files");
+               pos = -pos - 1;
+               if (pos < active_nr) { 
+                       ce = active_cache[pos];
+                       if (ce_namelen(ce) == ent->len &&
+                           !memcmp(ce->name, ent->name, ent->len))
+                               continue; /* Yup, this one exists unmerged */
+               }
+               show_dir_entry(tag_other, ent);
+       }
+}
+
 static void show_killed_files(void)
 {
        int i;
@@ -438,8 +461,7 @@ static void show_files(void)
                read_directory(path, base, baselen);
                qsort(dir, nr_dir, sizeof(struct nond_on_fs *), cmp_name);
                if (show_others)
-                       for (i = 0; i < nr_dir; i++)
-                               show_dir_entry(tag_other, dir[i]);
+                       show_other_files();
                if (show_killed)
                        show_killed_files();
        }
index 642f00d3d47c3d782c3dfe522d21819c1a67de30..946a35346bdaf21432b7f463712f5bf8881fee39 100644 (file)
@@ -51,6 +51,8 @@ int get_sha1_hex(const char *hex, unsigned char *sha1)
 int safe_create_leading_directories(char *path)
 {
        char *pos = path;
+       if (*pos == '/')
+               pos++;
 
        while (pos) {
                pos = strchr(pos, '/');
index 530cc4d2a38c6f4af92097dfe524029ef6a3a6bf..0401d7bbc6e416d2d44b8315ffd70125edca55c0 100755 (executable)
@@ -32,6 +32,6 @@ test_expect_success apply \
     'git-apply --index --stat --summary --apply test-patch'
 
 test_expect_success validate \
-    'test -f bar && ls -l bar | grep "^-..x..x..x"'
+    'test -f bar && ls -l bar | grep "^-..x......"'
 
 test_done