summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <junkio@cox.net>2005-11-26 00:35:20 (GMT)
committerJunio C Hamano <junkio@cox.net>2005-11-26 00:35:20 (GMT)
commit93dcab2937624ebb97f91807576cddb242a55a46 (patch)
tree8d16464f16866a7f215401a95f8bdaa124799d49
parent52b6536c624b4998b3730b55b9bc3d7c4e1071a4 (diff)
parent2a1ddc58e4d2220feea7eba4623dac07a0f83f94 (diff)
downloadgit-0.99.9k.zip
git-0.99.9k.tar.gz
git-0.99.9k.tar.bz2
GIT 0.99.9kv0.99.9k
This is not 1.0rc4 yet, but to push the recent fixes out. Signed-off-by: Junio C Hamano <junkio@cox.net>
-rw-r--r--.gitignore1
-rw-r--r--Documentation/git-daemon.txt18
-rw-r--r--Documentation/git-repo-config.txt170
-rw-r--r--Documentation/git-reset.txt19
-rw-r--r--Documentation/git.txt3
-rw-r--r--Documentation/howto/rebase-from-internal-branch.txt7
-rw-r--r--Documentation/howto/update-hook-example.txt105
-rw-r--r--Documentation/pull-fetch-param.txt30
-rw-r--r--Documentation/tutorial.txt5
-rw-r--r--Makefile69
-rw-r--r--cache.h8
-rw-r--r--config.c340
-rw-r--r--connect.c168
-rw-r--r--daemon.c142
-rw-r--r--debian/changelog6
-rw-r--r--diff-files.c2
-rw-r--r--diff-index.c2
-rw-r--r--diff-stages.c3
-rw-r--r--diff-tree.c56
-rw-r--r--diff.c39
-rw-r--r--diff.h1
-rw-r--r--diffcore-rename.c3
-rwxr-xr-xgit-am.sh2
-rwxr-xr-xgit-applymbox.sh2
-rwxr-xr-xgit-applypatch.sh2
-rwxr-xr-xgit-bisect.sh2
-rwxr-xr-xgit-branch.sh2
-rwxr-xr-xgit-checkout.sh3
-rwxr-xr-xgit-cherry.sh2
-rwxr-xr-xgit-commit.sh7
-rwxr-xr-xgit-count-objects.sh18
-rwxr-xr-xgit-cvsimport.perl2
-rwxr-xr-xgit-fetch.sh2
-rwxr-xr-xgit-format-patch.sh7
-rwxr-xr-xgit-lost-found.sh2
-rwxr-xr-xgit-ls-remote.sh1
-rwxr-xr-xgit-merge-one-file.sh3
-rwxr-xr-xgit-merge-recursive.py6
-rwxr-xr-xgit-merge.sh22
-rwxr-xr-xgit-mv.perl25
-rwxr-xr-xgit-octopus.sh2
-rwxr-xr-xgit-parse-remote.sh4
-rwxr-xr-xgit-prune.sh2
-rwxr-xr-xgit-pull.sh2
-rwxr-xr-xgit-push.sh2
-rwxr-xr-xgit-rebase.sh2
-rwxr-xr-xgit-repack.sh39
-rwxr-xr-xgit-reset.sh2
-rwxr-xr-xgit-resolve.sh2
-rwxr-xr-xgit-revert.sh14
-rwxr-xr-xgit-sh-setup.sh20
-rwxr-xr-xgit-status.sh2
-rwxr-xr-xgit-tag.sh2
-rwxr-xr-xgit-verify-tag.sh2
-rw-r--r--git.c2
-rwxr-xr-xgitk265
-rw-r--r--http-fetch.c821
-rw-r--r--http-push.c809
-rw-r--r--http.c442
-rw-r--r--http.h95
-rw-r--r--ident.c15
-rw-r--r--name-rev.c2
-rw-r--r--pack-objects.c2
-rw-r--r--pack-redundant.c128
-rw-r--r--path.c115
-rw-r--r--receive-pack.c17
-rw-r--r--refs.c40
-rw-r--r--repo-config.c116
-rw-r--r--rev-list.c5
-rw-r--r--setup.c42
-rw-r--r--sha1_name.c17
-rw-r--r--show-branch.c13
-rw-r--r--t/t1300-repo-config.sh271
-rw-r--r--templates/hooks--update7
-rw-r--r--update-index.c13
-rw-r--r--upload-pack.c15
-rw-r--r--var.c4
77 files changed, 2856 insertions, 1804 deletions
diff --git a/.gitignore b/.gitignore
index 0dd7b9c..8a6bd02 100644
--- a/.gitignore
+++ b/.gitignore
@@ -75,6 +75,7 @@ git-rebase
git-receive-pack
git-relink
git-repack
+git-repo-config
git-request-pull
git-reset
git-resolve
diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt
index 3783858..2a8f371 100644
--- a/Documentation/git-daemon.txt
+++ b/Documentation/git-daemon.txt
@@ -8,7 +8,7 @@ git-daemon - A really simple server for git repositories.
SYNOPSIS
--------
'git-daemon' [--verbose] [--syslog] [--inetd | --port=n] [--export-all]
- [--timeout=n] [--init-timeout=n] [directory...]
+ [--timeout=n] [--init-timeout=n] [--strict-paths] [directory...]
DESCRIPTION
-----------
@@ -29,9 +29,15 @@ This is ideally suited for read-only updates, ie pulling from git repositories.
OPTIONS
-------
+--strict-paths::
+ Match paths exactly (i.e. don't allow "/foo/repo" when the real path is
+ "/foo/repo.git" or "/foo/repo/.git") and don't do user-relative paths.
+ git-daemon will refuse to start when this option is enabled and no
+ whitelist is specified.
+
--export-all::
Allow pulling from all directories that look like GIT repositories
- (have the 'objects' subdirectory and a 'HEAD' file), even if they
+ (have the 'objects' and 'refs' subdirectories), even if they
do not have the 'git-daemon-export-ok' file.
--inetd::
@@ -57,9 +63,15 @@ OPTIONS
--verbose::
Log details about the incoming connections and requested files.
+<directory>::
+ A directory to add to the whitelist of allowed directories. Unless
+ --strict-paths is specified this will also include subdirectories
+ of each named directory.
+
Author
------
-Written by Linus Torvalds <torvalds@osdl.org> and YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
+Written by Linus Torvalds <torvalds@osdl.org>, YOSHIFUJI Hideaki
+<yoshfuji@linux-ipv6.org> and the git-list <git@vger.kernel.org>
Documentation
--------------
diff --git a/Documentation/git-repo-config.txt b/Documentation/git-repo-config.txt
new file mode 100644
index 0000000..5eefe02
--- /dev/null
+++ b/Documentation/git-repo-config.txt
@@ -0,0 +1,170 @@
+git-repo-config(1)
+==================
+
+NAME
+----
+git-repo-config - Get and set options in .git/config.
+
+
+SYNOPSIS
+--------
+'git-repo-config' name [value [value_regex]]
+'git-repo-config' --replace-all name [value [value_regex]]
+'git-repo-config' --get name [value_regex]
+'git-repo-config' --get-all name [value_regex]
+'git-repo-config' --unset name [value_regex]
+'git-repo-config' --unset-all name [value_regex]
+
+DESCRIPTION
+-----------
+You can query/set/replace/unset options with this command. The name is
+actually the section and the key separated by a dot, and the value will be
+escaped.
+
+If you want to set/unset an option which can occor on multiple lines, you
+should provide a POSIX regex for the value. If you want to handle the lines
+*not* matching the regex, just prepend a single exlamation mark in front
+(see EXAMPLES).
+
+This command will fail if
+
+. .git/config is invalid,
+. .git/config can not be written to,
+. no section was provided,
+. the section or key is invalid,
+. you try to unset an option which does not exist, or
+. you try to unset/set an option for which multiple lines match.
+
+
+OPTIONS
+-------
+
+--replace-all::
+ Default behaviour is to replace at most one line. This replaces
+ all lines matching the key (and optionally the value_regex)
+
+--get::
+ Get the value for a given key (optionally filtered by a regex
+ matching the value).
+
+--get-all::
+ Like get, but does not fail if the number of values for the key
+ is not exactly one.
+
+--unset::
+ Remove the line matching the key from .git/config.
+
+--unset-all::
+ Remove all matching lines from .git/config.
+
+
+EXAMPLE
+-------
+
+Given a .git/config like this:
+
+ #
+ # This is the config file, and
+ # a '#' or ';' character indicates
+ # a comment
+ #
+
+ ; core variables
+ [core]
+ ; Don't trust file modes
+ filemode = false
+
+ ; Our diff algorithm
+ [diff]
+ external = "/usr/local/bin/gnu-diff -u"
+ renames = true
+
+ ; Proxy settings
+ [proxy]
+ command="ssh" for "ssh://kernel.org/"
+ command="proxy-command" for kernel.org
+ command="myprotocol-command" for "my://"
+ command=default-proxy ; for all the rest
+
+you can set the filemode to true with
+
+------------
+% git repo-config core.filemode true
+------------
+
+The hypothetic proxy command entries actually have a postfix to discern
+to what URL they apply. Here is how to change the entry for kernel.org
+to "ssh".
+
+------------
+% git repo-config proxy.command '"ssh" for kernel.org' 'for kernel.org$'
+------------
+
+This makes sure that only the key/value pair for kernel.org is replaced.
+
+To delete the entry for renames, do
+
+------------
+% git repo-config --unset diff.renames
+------------
+
+If you want to delete an entry for a multivar (like proxy.command above),
+you have to provide a regex matching the value of exactly one line.
+
+To query the value for a given key, do
+
+------------
+% git repo-config --get core.filemode
+------------
+
+or
+
+------------
+% git repo-config core.filemode
+------------
+
+or, to query a multivar:
+
+------------
+% git repo-config --get proxy.command "for kernel.org$"
+------------
+
+If you want to know all the values for a multivar, do:
+
+------------
+% git repo-config --get-all proxy.command
+------------
+
+If you like to live dangerous, you can replace *all* proxy.commands by a
+new one with
+
+------------
+% git repo-config --replace-all proxy.command ssh
+------------
+
+However, if you really only want to replace the line for the default proxy,
+i.e. the one without a "for ..." postfix, do something like this:
+
+------------
+% git repo-config proxy.command ssh '! for '
+------------
+
+To actually match only values with an exclamation mark, you have to
+
+------------
+% git repo-config section.key value '[!]'
+------------
+
+
+Author
+------
+Written by Johannes Schindelin <Johannes.Schindelin@gmx.de>
+
+Documentation
+--------------
+Documentation by Johannes Schindelin.
+
+GIT
+---
+Part of the gitlink:git[7] suite
+
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 31ec207..6af3a4f 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -14,19 +14,30 @@ DESCRIPTION
Sets the current head to the specified commit and optionally resets the
index and working tree to match.
+This command is useful if you notice some small error in a recent
+commit (or set of commits) and want to redo that part without showing
+the undo in the history.
+
+If you want to undo a commit other than the latest on a branch,
+gitlink:git-revert[1] is your friend.
+
OPTIONS
-------
--mixed::
- Like --soft but reports what has not been updated. This is the
- default action.
+ Resets the index but not the working tree (ie, the changed files
+ are preserved but not marked for commit) and reports what has not
+ been updated. This is the default action.
--soft::
Does not touch the index file nor the working tree at all, but
- requires them in a good order.
+ requires them to be in a good order. This leaves all your changed
+ files "Updated but not checked in", as gitlink:git-status[1] would
+ put it.
--hard::
Matches the working tree and index to that of the tree being
- switched to.
+ switched to. Any changes to tracked files in the working tree
+ since <commit-ish> are lost.
<commit-ish>::
Commit to make the current HEAD.
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 338e5ac..a518249 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -108,6 +108,9 @@ gitlink:git-prune-packed[1]::
gitlink:git-read-tree[1]::
Reads tree information into the directory index
+gitlink:git-repo-config[1]::
+ Get and set options in .git/config.
+
gitlink:git-unpack-objects[1]::
Unpacks objects out of a packed archive.
diff --git a/Documentation/howto/rebase-from-internal-branch.txt b/Documentation/howto/rebase-from-internal-branch.txt
index b2c021d..c2d4a91 100644
--- a/Documentation/howto/rebase-from-internal-branch.txt
+++ b/Documentation/howto/rebase-from-internal-branch.txt
@@ -40,10 +40,7 @@ So I started from master, made a bunch of edits, and committed:
$ git checkout master
$ cd Documentation; ed git.txt ...
$ cd ..; git add Documentation/*.txt
- $ git commit -s -v
-
-NOTE. The -v flag to commit is a handy way to make sure that
-your additions are not introducing bogusly formatted lines.
+ $ git commit -s
After the commit, the ancestry graph would look like this:
@@ -98,7 +95,7 @@ to do cherrypicking using only the core GIT tools.
Let's go back to the earlier picture, with different labels.
You, as an individual developer, cloned upstream repository and
-amde a couple of commits on top of it.
+made a couple of commits on top of it.
*your "master" head
upstream --> #1 --> #2 --> #3
diff --git a/Documentation/howto/update-hook-example.txt b/Documentation/howto/update-hook-example.txt
new file mode 100644
index 0000000..dacaf17
--- /dev/null
+++ b/Documentation/howto/update-hook-example.txt
@@ -0,0 +1,105 @@
+From: Junio C Hamano <junkio@cox.net>
+Subject: control access to branches.
+Date: Thu, 17 Nov 2005 23:55:32 -0800
+Message-ID: <7vfypumlu3.fsf@assigned-by-dhcp.cox.net>
+Abstract: An example hooks/update script is presented to
+ implement repository maintenance policies, such as who can push
+ into which branch and who can make a tag.
+
+When your developer runs git-push into the repository,
+git-receive-pack is run (either locally or over ssh) as that
+developer, so is hooks/update script. Quoting from the relevant
+section of the documentation:
+
+ Before each ref is updated, if $GIT_DIR/hooks/update file exists
+ and executable, it is called with three parameters:
+
+ $GIT_DIR/hooks/update refname sha1-old sha1-new
+
+ The refname parameter is relative to $GIT_DIR; e.g. for the
+ master head this is "refs/heads/master". Two sha1 are the
+ object names for the refname before and after the update. Note
+ that the hook is called before the refname is updated, so either
+ sha1-old is 0{40} (meaning there is no such ref yet), or it
+ should match what is recorded in refname.
+
+So if your policy is (1) always require fast-forward push
+(i.e. never allow "git-push repo +branch:branch"), (2) you
+have a list of users allowed to update each branch, and (3) you
+do not let tags to be overwritten, then:
+
+ #!/bin/sh
+ # This is a sample hooks/update script, written by JC
+ # in his e-mail buffer, so naturally it is not tested
+ # but hopefully would convey the idea.
+
+ umask 002
+ case "$1" in
+ refs/tags/*)
+ # No overwriting an existing tag
+ if test -f "$GIT_DIR/$1"
+ then
+ exit 1
+ fi
+ ;;
+ refs/heads/*)
+ # No rebasing or rewinding
+ if expr "$2" : '0*$' >/dev/null
+ then
+ # creating a new branch
+ ;
+ else
+ # updating -- make sure it is a fast forward
+ mb=`git-merge-base "$2" "$3"`
+ case "$mb,$2" in
+ "$2,$mb")
+ ;; # fast forward -- happy
+ *)
+ exit 1 ;; # unhappy
+ esac
+ fi
+ ;;
+ *)
+ # No funny refs allowed
+ exit 1
+ ;;
+ esac
+
+ # Is the user allowed to update it?
+ me=`id -u -n` ;# e.g. "junio"
+ while read head_pattern users
+ do
+ if expr "$1" : "$head_pattern" >/dev/null
+ then
+ case " $users " in
+ *" $me "*)
+ exit 0 ;; # happy
+ ' * ')
+ exit 0 ;; # anybody
+ esac
+ fi
+ done
+ exit 1
+
+For the sake of simplicity, I assumed that you keep something
+like this in $GIT_DIR/info/allowed-pushers file:
+
+ refs/heads/master junio
+ refs/heads/cogito$ pasky
+ refs/heads/bw/ linus
+ refs/heads/tmp/ *
+ refs/tags/v[0-9]* junio
+
+With this, Linus can push or create "bw/penguin" or "bw/zebra"
+or "bw/panda" branches, Pasky can do only "cogito", and I can do
+master branch and make versioned tags. And anybody can do
+tmp/blah branches. This assumes all the users are in a single
+group that can write into $GIT_DIR/ and underneath.
+
+
+
+
+
+
+
+
diff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt
index ddd5823..6413d52 100644
--- a/Documentation/pull-fetch-param.txt
+++ b/Documentation/pull-fetch-param.txt
@@ -5,11 +5,31 @@
to name the remote repository:
+
===============================================================
-- Rsync URL: rsync://remote.machine/path/to/repo.git/
-- HTTP(s) URL: http://remote.machine/path/to/repo.git/
-- git URL: git://remote.machine/path/to/repo.git/
-- ssh URL: remote.machine:/path/to/repo.git/
-- Local directory: /path/to/repo.git/
+- rsync://host.xz/path/to/repo.git/
+- http://host.xz/path/to/repo.git/
+- https://host.xz/path/to/repo.git/
+- git://host.xz/path/to/repo.git/
+- git://host.xz/~user/path/to/repo.git/
+- ssh://host.xz/path/to/repo.git/
+- ssh://host.xz/~user/path/to/repo.git/
+- ssh://host.xz/~/path/to/repo.git
+===============================================================
++
+ SSH Is the default transport protocol and also supports an
+ scp-like syntax. Both syntaxes support username expansion,
+ as does the native git protocol. The following three are
+ identical to the last three above, respectively:
++
+===============================================================
+- host.xz:/path/to/repo.git/
+- host.xz:~user/path/to/repo.git/
+- host.xz:path/to/repo.git
+===============================================================
++
+ To sync with a local directory, use:
+
+===============================================================
+- /path/to/repo.git/
===============================================================
+
In addition to the above, as a short-hand, the name of a
diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt
index 03eb421..e2dfb00 100644
--- a/Documentation/tutorial.txt
+++ b/Documentation/tutorial.txt
@@ -1534,7 +1534,10 @@ on that project and has an own "public repository" goes like this:
the "project lead" person does.
3. Copy over the packed files from "project lead" public
- repository to your public repository.
+ repository to your public repository, unless the "project
+ lead" repository lives on the same machine as yours. In the
+ latter case, you can use `objects/info/alternates` file to
+ point at the repository you are borrowing from.
4. Push into the public repository from your primary
repository. Run `git repack`, and possibly `git prune` if the
diff --git a/Makefile b/Makefile
index 2d8853d..adff025 100644
--- a/Makefile
+++ b/Makefile
@@ -50,7 +50,7 @@
# 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.
-GIT_VERSION = 0.99.9j
+GIT_VERSION = 0.99.9k
# CFLAGS and LDFLAGS are for the users to override from the command line.
@@ -102,6 +102,11 @@ SCRIPT_PERL = \
SCRIPT_PYTHON = \
git-merge-recursive.py
+SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
+ $(patsubst %.perl,%,$(SCRIPT_PERL)) \
+ $(patsubst %.py,%,$(SCRIPT_PYTHON)) \
+ gitk git-cherry-pick
+
# The ones that do not have to link with lcrypto nor lz.
SIMPLE_PROGRAMS = \
git-get-tar-commit-id$X git-mailinfo$X git-mailsplit$X \
@@ -125,18 +130,36 @@ PROGRAMS = \
git-unpack-objects$X git-update-index$X git-update-server-info$X \
git-upload-pack$X git-verify-pack$X git-write-tree$X \
git-update-ref$X git-symbolic-ref$X git-check-ref-format$X \
- git-name-rev$X git-pack-redundant$X git-var$X $(SIMPLE_PROGRAMS)
+ git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X
+
+# what 'all' will build and 'install' will install.
+ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) git$X
# Backward compatibility -- to be removed after 1.0
PROGRAMS += git-ssh-pull$X git-ssh-push$X
GIT_LIST_TWEAK =
+# Set paths to tools early so that they can be used for version tests.
+ifndef SHELL_PATH
+ SHELL_PATH = /bin/sh
+endif
+ifndef PERL_PATH
+ PERL_PATH = /usr/bin/perl
+endif
+ifndef PYTHON_PATH
+ PYTHON_PATH = /usr/bin/python
+endif
+
PYMODULES = \
gitMergeCommon.py
ifdef WITH_OWN_SUBPROCESS_PY
PYMODULES += compat/subprocess.py
+else
+ ifneq ($(shell $(PYTHON_PATH) -c 'import subprocess;print"OK"' 2>/dev/null),OK)
+ PYMODULES += compat/subprocess.py
+ endif
endif
ifdef WITH_SEND_EMAIL
@@ -242,22 +265,15 @@ ifndef NO_CURL
CURL_LIBCURL = -lcurl
endif
PROGRAMS += git-http-fetch$X
- ifndef NO_EXPAT
- EXPAT_LIBEXPAT = -lexpat
- PROGRAMS += git-http-push$X
+ curl_check := $(shell (echo 070908; curl-config --vernum) | sort -r | sed -ne 2p)
+ ifeq "$(curl_check)" "070908"
+ ifndef NO_EXPAT
+ EXPAT_LIBEXPAT = -lexpat
+ PROGRAMS += git-http-push$X
+ endif
endif
endif
-ifndef SHELL_PATH
- SHELL_PATH = /bin/sh
-endif
-ifndef PERL_PATH
- PERL_PATH = /usr/bin/perl
-endif
-ifndef PYTHON_PATH
- PYTHON_PATH = /usr/bin/python
-endif
-
ifndef NO_OPENSSL
LIB_OBJS += epoch.o
OPENSSL_LIBSSL = -lssl
@@ -330,25 +346,20 @@ endif
ALL_CFLAGS += -DSHA1_HEADER=$(call shellquote,$(SHA1_HEADER))
-SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
- $(patsubst %.perl,%,$(SCRIPT_PERL)) \
- $(patsubst %.py,%,$(SCRIPT_PYTHON)) \
- gitk git-cherry-pick
-
export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
### Build rules
-all: $(PROGRAMS) $(SCRIPTS) git
+all: $(ALL_PROGRAMS)
all:
$(MAKE) -C templates
# Only use $(CFLAGS). We don't need anything else.
-git: git.c Makefile
+git$(X): git.c Makefile
$(CC) -DGIT_EXEC_PATH='"$(bindir)"' -DGIT_VERSION='"$(GIT_VERSION)"' \
- $(CFLAGS) $@.c -o $@
+ $(CFLAGS) $< -o $@
-$(filter-out git,$(patsubst %.sh,%,$(SCRIPT_SH))) : % : %.sh
+$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
rm -f $@
sed -e '1s|#!.*/sh|#!$(call shq,$(SHELL_PATH))|' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
@@ -387,7 +398,8 @@ $(SIMPLE_PROGRAMS) : git-%$X : %.o
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(LIB_FILE) $(SIMPLE_LIB)
-git-http-fetch$X: fetch.o
+git-http-fetch$X: fetch.o http.o
+git-http-push$X: http.o
git-local-fetch$X: fetch.o
git-ssh-fetch$X: rsh.o fetch.o
git-ssh-upload$X: rsh.o
@@ -431,9 +443,9 @@ check:
### Installation rules
-install: $(PROGRAMS) $(SCRIPTS) git
+install: all
$(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(bindir))
- $(INSTALL) git $(PROGRAMS) $(SCRIPTS) $(call shellquote,$(DESTDIR)$(bindir))
+ $(INSTALL) $(ALL_PROGRAMS) $(call shellquote,$(DESTDIR)$(bindir))
$(MAKE) -C templates install
$(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR))
$(INSTALL) $(PYMODULES) $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR))
@@ -470,7 +482,8 @@ deb: dist
### Cleaning rules
clean:
- rm -f *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o git $(PROGRAMS) $(LIB_FILE)
+ rm -f *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o $(LIB_FILE)
+ rm -f $(PROGRAMS) $(SIMPLE_PROGRAMS) git$X
rm -f $(filter-out gitk,$(SCRIPTS))
rm -f *.spec *.pyc *.pyo
rm -rf $(GIT_TARNAME)
diff --git a/cache.h b/cache.h
index 99afa2c..6ac94c5 100644
--- a/cache.h
+++ b/cache.h
@@ -203,6 +203,7 @@ int git_mkstemp(char *path, size_t n, const char *template);
int safe_create_leading_directories(char *path);
char *safe_strncpy(char *, const char *, size_t);
+char *enter_repo(char *path, int strict);
/* Read and unpack a sha1 file into memory, write memory to a sha1 file */
extern int unpack_sha1_header(z_stream *stream, void *map, unsigned long mapsize, void *buffer, unsigned long size);
@@ -262,9 +263,8 @@ void datestamp(char *buf, int bufsize);
unsigned long approxidate(const char *);
extern int setup_ident(void);
-extern char *get_ident(const char *name, const char *email, const char *date_str);
-extern char *git_author_info(void);
-extern char *git_committer_info(void);
+extern const char *git_author_info(void);
+extern const char *git_committer_info(void);
static inline void *xmalloc(size_t size)
{
@@ -386,6 +386,8 @@ extern int git_default_config(const char *, const char *);
extern int git_config(config_fn_t fn);
extern int git_config_int(const char *, const char *);
extern int git_config_bool(const char *, const char *);
+extern int git_config_set(const char *, const char *);
+extern int git_config_set_multivar(const char *, const char *, const char *, int);
#define MAX_GITNAME (1000)
extern char git_default_email[MAX_GITNAME];
diff --git a/config.c b/config.c
index 915bb97..5cc8535 100644
--- a/config.c
+++ b/config.c
@@ -1,5 +1,12 @@
-
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ * Copyright (C) Johannes Schindelin, 2005
+ *
+ */
#include "cache.h"
+#include <regex.h>
#define MAXNAME (256)
@@ -136,7 +143,7 @@ static int get_base_var(char *name)
return -1;
if (c == ']')
return baselen;
- if (!isalnum(c))
+ if (!isalnum(c) && c != '.')
return -1;
if (baselen > MAXNAME / 2)
return -1;
@@ -229,11 +236,6 @@ int git_default_config(const char *var, const char *value)
return 0;
}
- if (!strcmp(var, "diff.renamelimit")) {
- diff_rename_limit_default = git_config_int(var, value);
- return 0;
- }
-
/* Add other config variables here.. */
return 0;
}
@@ -252,3 +254,327 @@ int git_config(config_fn_t fn)
}
return ret;
}
+
+/*
+ * Find all the stuff for git_config_set() below.
+ */
+
+#define MAX_MATCHES 512
+
+static struct {
+ int baselen;
+ char* key;
+ int do_not_match;
+ regex_t* value_regex;
+ int multi_replace;
+ off_t offset[MAX_MATCHES];
+ enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
+ int seen;
+} store;
+
+static int matches(const char* key, const char* value)
+{
+ return !strcmp(key, store.key) &&
+ (store.value_regex == NULL ||
+ (store.do_not_match ^
+ !regexec(store.value_regex, value, 0, NULL, 0)));
+}
+
+static int store_aux(const char* key, const char* value)
+{
+ switch (store.state) {
+ case KEY_SEEN:
+ if (matches(key, value)) {
+ if (store.seen == 1 && store.multi_replace == 0) {
+ fprintf(stderr,
+ "Warning: %s has multiple values\n",
+ key);
+ } else if (store.seen >= MAX_MATCHES) {
+ fprintf(stderr, "Too many matches\n");
+ return 1;
+ }
+
+ store.offset[store.seen] = ftell(config_file);
+ store.seen++;
+ }
+ break;
+ case SECTION_SEEN:
+ if (strncmp(key, store.key, store.baselen+1)) {
+ store.state = SECTION_END_SEEN;
+ break;
+ } else
+ /* do not increment matches: this is no match */
+ store.offset[store.seen] = ftell(config_file);
+ /* fallthru */
+ case SECTION_END_SEEN:
+ case START:
+ if (matches(key, value)) {
+ store.offset[store.seen] = ftell(config_file);
+ store.state = KEY_SEEN;
+ store.seen++;
+ } else if(!strncmp(key, store.key, store.baselen))
+ store.state = SECTION_SEEN;
+ }
+ return 0;
+}
+
+static void store_write_section(int fd, const char* key)
+{
+ write(fd, "[", 1);
+ write(fd, key, store.baselen);
+ write(fd, "]\n", 2);
+}
+
+static void store_write_pair(int fd, const char* key, const char* value)
+{
+ int i;
+
+ write(fd, "\t", 1);
+ write(fd, key+store.baselen+1,
+ strlen(key+store.baselen+1));
+ write(fd, " = ", 3);
+ for (i = 0; value[i]; i++)
+ switch (value[i]) {
+ case '\n': write(fd, "\\n", 2); break;
+ case '\t': write(fd, "\\t", 2); break;
+ case '"': case '\\': write(fd, "\\", 1);
+ default: write(fd, value+i, 1);
+ }
+ write(fd, "\n", 1);
+}
+
+static int find_beginning_of_line(const char* contents, int size,
+ int offset_, int* found_bracket)
+{
+ int equal_offset = size, bracket_offset = size;
+ int offset;
+
+ for (offset = offset_-2; offset > 0
+ && contents[offset] != '\n'; offset--)
+ switch (contents[offset]) {
+ case '=': equal_offset = offset; break;
+ case ']': bracket_offset = offset; break;
+ }
+ if (bracket_offset < equal_offset) {
+ *found_bracket = 1;
+ offset = bracket_offset+1;
+ } else
+ offset++;
+
+ return offset;
+}
+
+int git_config_set(const char* key, const char* value)
+{
+ return git_config_set_multivar(key, value, NULL, 0);
+}
+
+/*
+ * If value==NULL, unset in (remove from) config,
+ * if value_regex!=NULL, disregard key/value pairs where value does not match.
+ * if multi_replace==0, nothing, or only one matching key/value is replaced,
+ * else all matching key/values (regardless how many) are removed,
+ * before the new pair is written.
+ *
+ * Returns 0 on success.
+ *
+ * This function does this:
+ *
+ * - it locks the config file by creating ".git/config.lock"
+ *
+ * - it then parses the config using store_aux() as validator to find
+ * the position on the key/value pair to replace. If it is to be unset,
+ * it must be found exactly once.
+ *
+ * - the config file is mmap()ed and the part before the match (if any) is
+ * written to the lock file, then the changed part and the rest.
+ *
+ * - the config file is removed and the lock file rename()d to it.
+ *
+ */
+int git_config_set_multivar(const char* key, const char* value,
+ const char* value_regex, int multi_replace)
+{
+ int i;
+ struct stat st;
+ int fd;
+ char* config_filename = strdup(git_path("config"));
+ char* lock_file = strdup(git_path("config.lock"));
+ const char* last_dot = strrchr(key, '.');
+
+ /*
+ * Since "key" actually contains the section name and the real
+ * key name separated by a dot, we have to know where the dot is.
+ */
+
+ if (last_dot == NULL) {
+ fprintf(stderr, "key does not contain a section: %s\n", key);
+ return 2;
+ }
+ store.baselen = last_dot - key;
+
+ store.multi_replace = multi_replace;
+
+ /*
+ * Validate the key and while at it, lower case it for matching.
+ */
+ store.key = (char*)malloc(strlen(key)+1);
+ for (i = 0; key[i]; i++)
+ if (i != store.baselen &&
+ ((!isalnum(key[i]) && key[i] != '.') ||
+ (i == store.baselen+1 && !isalpha(key[i])))) {
+ fprintf(stderr, "invalid key: %s\n", key);
+ free(store.key);
+ return 1;
+ } else
+ store.key[i] = tolower(key[i]);
+ store.key[i] = 0;
+
+ /*
+ * The lock_file serves a purpose in addition to locking: the new
+ * contents of .git/config will be written into it.
+ */
+ fd = open(lock_file, O_WRONLY | O_CREAT | O_EXCL, 0666);
+ if (fd < 0) {
+ fprintf(stderr, "could not lock config file\n");
+ free(store.key);
+ return -1;
+ }
+
+ /*
+ * If .git/config does not exist yet, write a minimal version.
+ */
+ if (stat(config_filename, &st)) {
+ static const char contents[] =
+ "#\n"
+ "# This is the config file\n"
+ "#\n"
+ "\n";
+
+ free(store.key);
+
+ /* if nothing to unset, error out */
+ if (value == NULL) {
+ close(fd);
+ unlink(lock_file);
+ return 5;
+ }
+
+ store.key = (char*)key;
+
+ write(fd, contents, sizeof(contents)-1);
+ store_write_section(fd, key);
+ store_write_pair(fd, key, value);
+ } else{
+ int in_fd;
+ char* contents;
+ int i, copy_begin, copy_end, new_line = 0;
+
+ if (value_regex == NULL)
+ store.value_regex = NULL;
+ else {
+ if (value_regex[0] == '!') {
+ store.do_not_match = 1;
+ value_regex++;
+ } else
+ store.do_not_match = 0;
+
+ store.value_regex = (regex_t*)malloc(sizeof(regex_t));
+ if (regcomp(store.value_regex, value_regex,
+ REG_EXTENDED)) {
+ fprintf(stderr, "Invalid pattern: %s",
+ value_regex);
+ free(store.value_regex);
+ return 6;
+ }
+ }
+
+ store.offset[0] = 0;
+ store.state = START;
+ store.seen = 0;
+
+ /*
+ * After this, store.offset will contain the *end* offset
+ * of the last match, or remain at 0 if no match was found.
+ * As a side effect, we make sure to transform only a valid
+ * existing config file.
+ */
+ if (git_config(store_aux)) {
+ fprintf(stderr, "invalid config file\n");
+ free(store.key);
+ if (store.value_regex != NULL) {
+ regfree(store.value_regex);
+ free(store.value_regex);
+ }
+ return 3;
+ }
+
+ free(store.key);
+ if (store.value_regex != NULL) {
+ regfree(store.value_regex);
+ free(store.value_regex);
+ }
+
+ /* if nothing to unset, or too many matches, error out */
+ if ((store.seen == 0 && value == NULL) ||
+ (store.seen > 1 && multi_replace == 0)) {
+ close(fd);
+ unlink(lock_file);
+ return 5;
+ }
+
+ in_fd = open(config_filename, O_RDONLY, 0666);
+ contents = mmap(NULL, st.st_size, PROT_READ,
+ MAP_PRIVATE, in_fd, 0);
+ close(in_fd);
+
+ if (store.seen == 0)
+ store.seen = 1;
+
+ for (i = 0, copy_begin = 0; i < store.seen; i++) {
+ if (store.offset[i] == 0) {
+ store.offset[i] = copy_end = st.st_size;
+ } else if (store.state != KEY_SEEN) {
+ copy_end = store.offset[i];
+ } else
+ copy_end = find_beginning_of_line(
+ contents, st.st_size,
+ store.offset[i]-2, &new_line);
+
+ /* write the first part of the config */
+ if (copy_end > copy_begin) {
+ write(fd, contents + copy_begin,
+ copy_end - copy_begin);
+ if (new_line)
+ write(fd, "\n", 1);
+ }
+ copy_begin = store.offset[i];
+ }
+
+ /* write the pair (value == NULL means unset) */
+ if (value != NULL) {
+ if (store.state == START)
+ store_write_section(fd, key);
+ store_write_pair(fd, key, value);
+ }
+
+ /* write the rest of the config */
+ if (copy_begin < st.st_size)
+ write(fd, contents + copy_begin,
+ st.st_size - copy_begin);
+
+ munmap(contents, st.st_size);
+ unlink(config_filename);
+ }
+
+ close(fd);
+
+ if (rename(lock_file, config_filename) < 0) {
+ fprintf(stderr, "Could not rename the lock file?\n");
+ return 4;
+ }
+
+ return 0;
+}
+
+
diff --git a/connect.c b/connect.c
index c2badc7..93f6f80 100644
--- a/connect.c
+++ b/connect.c
@@ -427,7 +427,7 @@ static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path)
memset(&sa, 0, sizeof sa);
sa.sin_family = he->h_addrtype;
sa.sin_port = htons(nport);
- memcpy(&sa.sin_addr, ap, he->h_length);
+ memcpy(&sa.sin_addr, *ap, he->h_length);
if (connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) {
close(sockfd);
@@ -448,42 +448,162 @@ static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path)
#endif /* NO_IPV6 */
+static char *git_proxy_command = NULL;
+static const char *rhost_name = NULL;
+static int rhost_len;
+
+static int git_proxy_command_options(const char *var, const char *value)
+{
+ if (!strcmp(var, "core.gitproxy")) {
+ const char *for_pos;
+ int matchlen = -1;
+ int hostlen;
+
+ if (git_proxy_command)
+ return 0;
+ /* [core]
+ * ;# matches www.kernel.org as well
+ * gitproxy = netcatter-1 for kernel.org
+ * gitproxy = netcatter-2 for sample.xz
+ * gitproxy = netcatter-default
+ */
+ for_pos = strstr(value, " for ");
+ if (!for_pos)
+ /* matches everybody */
+ matchlen = strlen(value);
+ else {
+ hostlen = strlen(for_pos + 5);
+ if (rhost_len < hostlen)
+ matchlen = -1;
+ else if (!strncmp(for_pos + 5,
+ rhost_name + rhost_len - hostlen,
+ hostlen) &&
+ ((rhost_len == hostlen) ||
+ rhost_name[rhost_len - hostlen -1] == '.'))
+ matchlen = for_pos - value;
+ else
+ matchlen = -1;
+ }
+ if (0 <= matchlen) {
+ /* core.gitproxy = none for kernel.org */
+ if (matchlen == 4 &&
+ !memcmp(value, "none", 4))
+ matchlen = 0;
+ git_proxy_command = xmalloc(matchlen + 1);
+ memcpy(git_proxy_command, value, matchlen);
+ git_proxy_command[matchlen] = 0;
+ }
+ return 0;
+ }
+
+ return git_default_config(var, value);
+}
+
+static int git_use_proxy(const char *host)
+{
+ rhost_name = host;
+ rhost_len = strlen(host);
+ git_proxy_command = getenv("GIT_PROXY_COMMAND");
+ git_config(git_proxy_command_options);
+ rhost_name = NULL;
+ return (git_proxy_command && *git_proxy_command);
+}
+
+static int git_proxy_connect(int fd[2], const char *prog, char *host, char *path)
+{
+ char *port = STR(DEFAULT_GIT_PORT);
+ char *colon, *end;
+ int pipefd[2][2];
+ pid_t pid;
+
+ if (host[0] == '[') {
+ end = strchr(host + 1, ']');
+ if (end) {
+ *end = 0;
+ end++;
+ host++;
+ } else
+ end = host;
+ } else
+ end = host;
+ colon = strchr(end, ':');
+
+ if (colon) {
+ *colon = 0;
+ port = colon + 1;
+ }
+
+ if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0)
+ die("unable to create pipe pair for communication");
+ pid = fork();
+ if (!pid) {
+ dup2(pipefd[1][0], 0);
+ dup2(pipefd[0][1], 1);
+ close(pipefd[0][0]);
+ close(pipefd[0][1]);
+ close(pipefd[1][0]);
+ close(pipefd[1][1]);
+ execlp(git_proxy_command, git_proxy_command, host, port, NULL);
+ die("exec failed");
+ }
+ fd[0] = pipefd[0][0];
+ fd[1] = pipefd[1][1];
+ close(pipefd[0][1]);
+ close(pipefd[1][0]);
+ packet_write(fd[1], "%s %s\n", prog, path);
+ return pid;
+}
+
/*
* Yeah, yeah, fixme. Need to pass in the heads etc.
*/
int git_connect(int fd[2], char *url, const char *prog)
{
char command[1024];
- char *host, *path;
- char *colon;
+ char *host, *path = url;
+ char *colon = NULL;
int pipefd[2][2];
pid_t pid;
- enum protocol protocol;
-
- host = NULL;
- path = url;
- colon = strchr(url, ':');
- protocol = PROTO_LOCAL;
- if (colon) {
- *colon = 0;
+ enum protocol protocol = PROTO_LOCAL;
+
+ host = strstr(url, "://");
+ if(host) {
+ *host = '\0';
+ protocol = get_protocol(url);
+ host += 3;
+ path = strchr(host, '/');
+ }
+ else {
host = url;
- path = colon+1;
- protocol = PROTO_SSH;
- if (!memcmp(path, "//", 2)) {
- char *slash = strchr(path + 2, '/');
- if (slash) {
- int nr = slash - path - 2;
- memmove(path, path+2, nr);
- path[nr] = 0;
- protocol = get_protocol(url);
- host = path;
- path = slash;
- }
+ if ((colon = strchr(host, ':'))) {
+ protocol = PROTO_SSH;
+ *colon = '\0';
+ path = colon + 1;
}
}
- if (protocol == PROTO_GIT)
+ if (!path || !*path)
+ die("No path specified. See 'man git-pull' for valid url syntax");
+
+ /*
+ * null-terminate hostname and point path to ~ for URL's like this:
+ * ssh://host.xz/~user/repo
+ */
+ if (protocol != PROTO_LOCAL && host != url) {
+ char *ptr = path;
+ if (path[1] == '~')
+ path++;
+ else
+ path = strdup(ptr);
+
+ *ptr = '\0';
+ }
+
+ if (protocol == PROTO_GIT) {
+ if (git_use_proxy(host))
+ return git_proxy_connect(fd, prog, host, path);
return git_tcp_connect(fd, prog, host, path);
+ }
if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0)
die("unable to create pipe pair for communication");
diff --git a/daemon.c b/daemon.c
index 2b81152..91b9656 100644
--- a/daemon.c
+++ b/daemon.c
@@ -15,10 +15,11 @@ static int verbose;
static const char daemon_usage[] =
"git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\n"
-" [--timeout=n] [--init-timeout=n] [directory...]";
+" [--timeout=n] [--init-timeout=n] [--strict-paths] [directory...]";
/* List of acceptable pathname prefixes */
static char **ok_paths = NULL;
+static int strict_paths = 0;
/* If this is set, git-daemon-export-ok is not required */
static int export_all_trees = 0;
@@ -81,69 +82,52 @@ static void loginfo(const char *err, ...)
va_end(params);
}
-static int path_ok(const char *dir)
+static char *path_ok(char *dir)
{
- const char *p = dir;
- char **pp;
- int sl, ndot;
+ char *path = enter_repo(dir, strict_paths);
- /* The pathname here should be an absolute path. */
- if ( *p++ != '/' )
- return 0;
-
- sl = 1; ndot = 0;
-
- for (;;) {
- if ( *p == '.' ) {
- ndot++;
- } else if ( *p == '\0' ) {
- /* Reject "." and ".." at the end of the path */
- if ( sl && ndot > 0 && ndot < 3 )
- return 0;
-
- /* Otherwise OK */
- break;
- } else if ( *p == '/' ) {
- /* Refuse "", "." or ".." */
- if ( sl && ndot < 3 )
- return 0;
- sl = 1;
- ndot = 0;
- } else {
- sl = ndot = 0;
- }
- p++;
+ if (!path) {
+ logerror("'%s': unable to chdir or not a git archive", dir);
+ return NULL;
}
if ( ok_paths && *ok_paths ) {
- int ok = 0;
- int dirlen = strlen(dir);
-
+ char **pp;
+ int pathlen = strlen(path);
+
+ /* The validation is done on the paths after enter_repo
+ * canonicalization, so whitelist should be written in
+ * terms of real pathnames (i.e. after ~user is expanded
+ * and symlinks resolved).
+ */
for ( pp = ok_paths ; *pp ; pp++ ) {
int len = strlen(*pp);
- if ( len <= dirlen &&
- !strncmp(*pp, dir, len) &&
- (dir[len] == '/' || dir[len] == '\0') ) {
- ok = 1;
- break;
- }
+ if (len <= pathlen &&
+ !memcmp(*pp, path, len) &&
+ (path[len] == '\0' ||
+ (!strict_paths && path[len] == '/')))
+ return path;
}
-
- if ( !ok )
- return 0; /* Path not in whitelist */
+ }
+ else {
+ /* be backwards compatible */
+ if (!strict_paths)
+ return path;
}
- return 1; /* Path acceptable */
+ logerror("'%s': not in whitelist", path);
+ return NULL; /* Fallthrough. Deny by default */
}
-static int set_dir(const char *dir)
+static int upload(char *dir)
{
- if (!path_ok(dir)) {
- errno = EACCES;
- return -1;
- }
+ /* Timeout as string */
+ char timeout_buf[64];
+ const char *path;
+
+ loginfo("Request for '%s'", dir);
- if ( chdir(dir) )
+ if (!(path = path_ok(dir)))
return -1;
/*
@@ -152,45 +136,17 @@ static int set_dir(const char *dir)
* We want a readable HEAD, usable "objects" directory, and
* a "git-daemon-export-ok" flag that says that the other side
* is ok with us doing this.
+ *
+ * path_ok() uses enter_repo() and does whitelist checking.
+ * We only need to make sure the repository is exported.
*/
+
if (!export_all_trees && access("git-daemon-export-ok", F_OK)) {
+ logerror("'%s': repository not exported.", path);
errno = EACCES;
return -1;
}
- if (access("objects/", X_OK) || access("HEAD", R_OK)) {
- errno = EINVAL;
- return -1;
- }
-
- /* If all this passed, we're OK */
- return 0;
-}
-
-static int upload(char *dir)
-{
- /* Try paths in this order */
- static const char *paths[] = { "%s", "%s/.git", "%s.git", "%s.git/.git", NULL };
- const char **pp;
- /* Enough for the longest path above including final null */
- int buflen = strlen(dir)+10;
- char *dirbuf = xmalloc(buflen);
- /* Timeout as string */
- char timeout_buf[64];
-
- loginfo("Request for '%s'", dir);
-
- for ( pp = paths ; *pp ; pp++ ) {
- snprintf(dirbuf, buflen, *pp, dir);
- if ( !set_dir(dirbuf) )
- break;
- }
-
- if ( !*pp ) {
- logerror("Cannot set directory '%s': %s", dir, strerror(errno));
- return -1;
- }
-
/*
* We'll ignore SIGTERM from now on, we have a
* good client.
@@ -216,7 +172,7 @@ static int execute(void)
if (len && line[len-1] == '\n')
line[--len] = 0;
- if (!strncmp("git-upload-pack /", line, 17))
+ if (!strncmp("git-upload-pack ", line, 16))
return upload(line+16);
logerror("Protocol error: '%s'", line);
@@ -510,8 +466,14 @@ static int socksetup(int port, int **socklist_p)
return 0;
}
+ if (listen(sockfd, 5) < 0) {
+ close(sockfd);
+ return 0;
+ }
+
*socklist_p = xmalloc(sizeof(int));
**socklist_p = sockfd;
+ return 1;
}
#endif
@@ -617,6 +579,10 @@ int main(int argc, char **argv)
init_timeout = atoi(arg+15);
continue;
}
+ if (!strcmp(arg, "--strict-paths")) {
+ strict_paths = 1;
+ continue;
+ }
if (!strcmp(arg, "--")) {
ok_paths = &argv[i+1];
break;
@@ -631,6 +597,14 @@ int main(int argc, char **argv)
if (log_syslog)
openlog("git-daemon", 0, LOG_DAEMON);
+ if (strict_paths && (!ok_paths || !*ok_paths)) {
+ if (!inetd_mode)
+ die("git-daemon: option --strict-paths requires a whitelist");
+
+ logerror("option --strict-paths requires a whitelist");
+ exit (1);
+ }
+
if (inetd_mode) {
fclose(stderr); //FIXME: workaround
return execute();
diff --git a/debian/changelog b/debian/changelog
index 1eda61f..7356fe7 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+git-core (0.99.9k-0) unstable; urgency=low
+
+ * GIT 0.99.9k but not 1.0rc yet.
+
+ -- Junio C Hamano <junkio@cox.net> Fri, 25 Nov 2005 16:33:11 -0800
+
git-core (0.99.9j-0) unstable; urgency=low
* GIT 0.99.9j aka 1.0rc3
diff --git a/diff-files.c b/diff-files.c
index 1789939..38599b5 100644
--- a/diff-files.c
+++ b/diff-files.c
@@ -38,7 +38,7 @@ int main(int argc, const char **argv)
const char *prefix = setup_git_directory();
int entries, i;
- git_config(git_default_config);
+ git_config(git_diff_config);
diff_setup(&diff_options);
while (1 < argc && argv[1][0] == '-') {
if (!strcmp(argv[1], "--")) {
diff --git a/diff-index.c b/diff-index.c
index c9a9f4c..0054883 100644
--- a/diff-index.c
+++ b/diff-index.c
@@ -180,7 +180,7 @@ int main(int argc, const char **argv)
int allow_options = 1;
int i;
- git_config(git_default_config);
+ git_config(git_diff_config);
diff_setup(&diff_options);
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
diff --git a/diff-stages.c b/diff-stages.c
index 85170b2..9968d6c 100644
--- a/diff-stages.c
+++ b/diff-stages.c
@@ -55,6 +55,9 @@ int main(int ac, const char **av)
{
int stage1, stage2;
+ setup_git_directory();
+
+ git_config(git_diff_config);
read_cache();
diff_setup(&diff_options);
while (1 < ac && av[1][0] == '-') {
diff --git a/diff-tree.c b/diff-tree.c
index 09d16ad..d56d921 100644
--- a/diff-tree.c
+++ b/diff-tree.c
@@ -69,52 +69,50 @@ static int diff_root_tree(const unsigned char *new, const char *base)
return retval;
}
-static const char *generate_header(const char *commit, const char *parent, const char *msg, unsigned long len)
+static const char *generate_header(const char *commit, const char *parent, const char *msg)
{
static char this_header[16384];
int offset;
+ unsigned long len;
if (!verbose_header)
return commit;
+ len = strlen(msg);
offset = sprintf(this_header, "%s%s (from %s)\n", header_prefix, commit, parent);
offset += pretty_print_commit(commit_format, msg, len, this_header + offset, sizeof(this_header) - offset);
return this_header;
}
-static int diff_tree_commit(const unsigned char *commit, const char *name)
+static int diff_tree_commit(const unsigned char *commit_sha1)
{
- unsigned long size, offset;
- char *buf = read_object_with_reference(commit, "commit", &size, NULL);
+ struct commit *commit;
+ struct commit_list *parents;
+ char name[50];
+ unsigned char sha1[20];
- if (!buf)
+ sprintf(name, "%s^0", sha1_to_hex(commit_sha1));
+ if (get_sha1(name, sha1))
return -1;
-
- if (!name) {
- static char commit_name[60];
- strcpy(commit_name, sha1_to_hex(commit));
- name = commit_name;
- }
-
+ name[40] = 0;
+ commit = lookup_commit(sha1);
+
/* Root commit? */
- if (show_root_diff && memcmp(buf + 46, "parent ", 7)) {
- header = generate_header(name, "root", buf, size);
- diff_root_tree(commit, "");
+ if (show_root_diff && !commit->parents) {
+ header = generate_header(name, "root", commit->buffer);
+ diff_root_tree(commit_sha1, "");
}
/* More than one parent? */
- if (ignore_merges) {
- if (!memcmp(buf + 46 + 48, "parent ", 7))
+ if (ignore_merges && commit->parents && commit->parents->next)
return 0;
- }
- offset = 46;
- while (offset + 48 < size && !memcmp(buf + offset, "parent ", 7)) {
- unsigned char parent[20];
- if (get_sha1_hex(buf + offset + 7, parent))
- return -1;
- header = generate_header(name, sha1_to_hex(parent), buf, size);
- diff_tree_sha1_top(parent, commit, "");
+ for (parents = commit->parents; parents; parents = parents->next) {
+ struct commit *parent = parents->item;
+ header = generate_header(name,
+ sha1_to_hex(parent->object.sha1),
+ commit->buffer);
+ diff_tree_sha1_top(parent->object.sha1, commit_sha1, "");
if (!header && verbose_header) {
header_prefix = "\ndiff-tree ";
/*
@@ -122,9 +120,7 @@ static int diff_tree_commit(const unsigned char *commit, const char *name)
* don't print the diffs.
*/
}
- offset += 48;
}
- free(buf);
return 0;
}
@@ -147,7 +143,7 @@ static int diff_tree_stdin(char *line)
return diff_tree_sha1_top(parent, commit, "");
}
line[40] = 0;
- return diff_tree_commit(commit, line);
+ return diff_tree_commit(commit);
}
static const char diff_tree_usage[] =
@@ -164,7 +160,7 @@ int main(int argc, const char **argv)
unsigned char sha1[2][20];
const char *prefix = setup_git_directory();
- git_config(git_default_config);
+ git_config(git_diff_config);
nr_sha1 = 0;
diff_setup(&diff_options);
@@ -250,7 +246,7 @@ int main(int argc, const char **argv)
usage(diff_tree_usage);
break;
case 1:
- diff_tree_commit(sha1[0], NULL);
+ diff_tree_commit(sha1[0]);
break;
case 2:
diff_tree_sha1_top(sha1[0], sha1[1], "");
diff --git a/diff.c b/diff.c
index 0391e8c..2e0797b 100644
--- a/diff.c
+++ b/diff.c
@@ -15,6 +15,16 @@ static int use_size_cache;
int diff_rename_limit_default = -1;
+int git_diff_config(const char *var, const char *value)
+{
+ if (!strcmp(var, "diff.renamelimit")) {
+ diff_rename_limit_default = git_config_int(var, value);
+ return 0;
+ }
+
+ return git_default_config(var, value);
+}
+
static char *quote_one(const char *str)
{
int needlen;
@@ -838,16 +848,29 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
static int parse_num(const char **cp_p)
{
- int num, scale, ch, cnt;
+ unsigned long num, scale;
+ int ch, dot;
const char *cp = *cp_p;
- cnt = num = 0;
+ num = 0;
scale = 1;
- while ('0' <= (ch = *cp) && ch <= '9') {
- if (cnt++ < 5) {
- /* We simply ignore more than 5 digits precision. */
- scale *= 10;
- num = num * 10 + ch - '0';
+ dot = 0;
+ for(;;) {
+ ch = *cp;
+ if ( !dot && ch == '.' ) {
+ scale = 1;
+ dot = 1;
+ } else if ( ch == '%' ) {
+ scale = dot ? scale*100 : 100;
+ cp++; /* % is always at the end */
+ break;
+ } else if ( ch >= '0' && ch <= '9' ) {
+ if ( scale < 100000 ) {
+ scale *= 10;
+ num = (num*10) + (ch-'0');
+ }
+ } else {
+ break;
}
cp++;
}
@@ -856,7 +879,7 @@ static int parse_num(const char **cp_p)
/* user says num divided by scale and we say internally that
* is MAX_SCORE * num / scale.
*/
- return (MAX_SCORE * num / scale);
+ return (num >= scale) ? MAX_SCORE : (MAX_SCORE * num / scale);
}
int diff_scoreopt_parse(const char *opt)
diff --git a/diff.h b/diff.h
index 9b2e1e6..32b4780 100644
--- a/diff.h
+++ b/diff.h
@@ -77,6 +77,7 @@ extern int diff_scoreopt_parse(const char *opt);
#define DIFF_SETUP_USE_CACHE 2
#define DIFF_SETUP_USE_SIZE_CACHE 4
+extern int git_diff_config(const char *var, const char *value);
extern void diff_setup(struct diff_options *);
extern int diff_opt_parse(struct diff_options *, const char **, int);
extern int diff_setup_done(struct diff_options *);
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 6a9d95d..dba965c 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -307,6 +307,9 @@ void diffcore_rename(struct diff_options *options)
if (rename_count == rename_dst_nr)
goto cleanup;
+ if (minimum_score == MAX_SCORE)
+ goto cleanup;
+
num_create = (rename_dst_nr - rename_count);
num_src = rename_src_nr;
mx = xmalloc(sizeof(*mx) * num_create * num_src);
diff --git a/git-am.sh b/git-am.sh
index 8f073c9..660b3a4 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -1,7 +1,7 @@
#!/bin/sh
#
#
-. git-sh-setup || die "Not a git archive"
+. git-sh-setup
usage () {
echo >&2 "usage: $0 [--signoff] [--dotest=<dir>] [--utf8] [--binary] [--3way] <mbox>"
diff --git a/git-applymbox.sh b/git-applymbox.sh
index 6de6932..24d4a8c 100755
--- a/git-applymbox.sh
+++ b/git-applymbox.sh
@@ -18,7 +18,7 @@
##
## git-am is supposed to be the newer and better tool for this job.
-. git-sh-setup || die "Not a git archive"
+. git-sh-setup
usage () {
echo >&2 "applymbox [-u] [-k] [-q] [-m] (-c .dotest/<num> | mbox) [signoff]"
diff --git a/git-applypatch.sh b/git-applypatch.sh
index 66fd19a..f0549960 100755
--- a/git-applypatch.sh
+++ b/git-applypatch.sh
@@ -10,7 +10,7 @@
## $3 - "info" file with Author, email and subject
## $4 - optional file containing signoff to add
##
-. git-sh-setup || die "Not a git archive."
+. git-sh-setup
final=.dotest/final-commit
##
diff --git a/git-bisect.sh b/git-bisect.sh
index 1ab2f18..d92993b 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -1,5 +1,5 @@
#!/bin/sh
-. git-sh-setup || dir "Not a git archive"
+. git-sh-setup
usage() {
echo >&2 'usage: git bisect [start|bad|good|next|reset|visualize]
diff --git a/git-branch.sh b/git-branch.sh
index 2594518..4cd5da1 100755
--- a/git-branch.sh
+++ b/git-branch.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-. git-sh-setup || die "Not a git archive"
+. git-sh-setup
usage () {
echo >&2 "usage: $(basename $0)"' [-d <branch>] | [[-f] <branch> [start-point]]
diff --git a/git-checkout.sh b/git-checkout.sh
index 4c08f36..4cf30e2 100755
--- a/git-checkout.sh
+++ b/git-checkout.sh
@@ -1,5 +1,5 @@
#!/bin/sh
-. git-sh-setup || die "Not a git archive"
+. git-sh-setup
usage () {
die "usage: git checkout [-f] [-b <new_branch>] [<branch>] [<paths>...]"
@@ -82,7 +82,6 @@ then
# rescuing paths and is never meant to remove what
# is not in the named tree-ish.
git-ls-tree -r "$new" "$@" |
- sed -ne 's/^\([0-7]*\) blob \(.*\)$/\1 \2/p' |
git-update-index --index-info || exit $?
fi
git-checkout-index -f -u -- "$@"
diff --git a/git-cherry.sh b/git-cherry.sh
index aad2e61..867522b 100755
--- a/git-cherry.sh
+++ b/git-cherry.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano.
#
-. git-sh-setup || die "Not a git archive."
+. git-sh-setup
usage="usage: $0 "'[-v] <upstream> [<head>]
diff --git a/git-commit.sh b/git-commit.sh
index 41955e8..3d250ec 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Linus Torvalds
#
-. git-sh-setup || die "Not a git archive"
+. git-sh-setup
usage () {
die 'git commit [-a] [-s] [-v | --no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit>] [-e] [<path>...]'
@@ -92,10 +92,13 @@ tt*)
esac
case "$all,$#" in
-t,*)
+t,0)
git-diff-files --name-only -z |
git-update-index --remove -z --stdin
;;
+t,*)
+ die "Cannot use -a and explicit files at the same time."
+ ;;
,0)
;;
*)
diff --git a/git-count-objects.sh b/git-count-objects.sh
index 843d2fd..d6e9a32 100755
--- a/git-count-objects.sh
+++ b/git-count-objects.sh
@@ -1,7 +1,25 @@
#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
. git-sh-setup
+dc </dev/null 2>/dev/null || {
+ # This is not a real DC at all -- it just knows how
+ # this script feeds DC and does the computation itself.
+ dc () {
+ while read a b
+ do
+ case $a,$b in
+ 0,) acc=0 ;;
+ *,+) acc=$(($acc + $a)) ;;
+ p,) echo "$acc" ;;
+ esac
+ done
+ }
+}
+
echo $(find "$GIT_DIR/objects"/?? -type f -print 2>/dev/null | wc -l) objects, \
$({
echo 0
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index efe1934..08a890c 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -502,7 +502,7 @@ unless($pid) {
if ($opt_P) {
exec("cat", $opt_P);
} else {
- exec("cvsps",@opt,"-u","-A",'--root',$opt_d,$cvs_tree);
+ exec("cvsps","--norc",@opt,"-u","-A",'--root',$opt_d,$cvs_tree);
die "Could not start cvsps: $!\n";
}
}
diff --git a/git-fetch.sh b/git-fetch.sh
index 6586e77..14ea295 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -1,6 +1,6 @@
#!/bin/sh
#
-. git-sh-setup || die "Not a git archive"
+. git-sh-setup
. git-parse-remote
_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
diff --git a/git-format-patch.sh b/git-format-patch.sh
index 7ee5d32..bc56876 100755
--- a/git-format-patch.sh
+++ b/git-format-patch.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano
#
-. git-sh-setup || die "Not a git archive."
+. git-sh-setup
usage () {
echo >&2 "usage: $0"' [-n] [-o dir | --stdout] [--keep-subject] [--mbox]
@@ -99,7 +99,7 @@ filelist=$tmp-files
# Also, "rev1.." should mean "rev1..HEAD"; git-diff users are
# familiar with that syntax.
-case "$#,$1" in
+case "$#,$1$2" in
1,?*..?*)
# single "rev1..rev2"
;;
@@ -131,7 +131,8 @@ do
rev2=`expr "$revpair" : '.*\.\.\(.*\)'`
;;
*)
- usage
+ rev1="$revpair^"
+ rev2="$revpair"
;;
esac
git-rev-parse --verify "$rev1^0" >/dev/null 2>&1 ||
diff --git a/git-lost-found.sh b/git-lost-found.sh
index 3892f52..9dd7430 100755
--- a/git-lost-found.sh
+++ b/git-lost-found.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-. git-sh-setup || die "Not a git archive."
+. git-sh-setup
laf="$GIT_DIR/lost-found"
rm -fr "$laf" && mkdir -p "$laf/commit" "$laf/other" || exit
diff --git a/git-ls-remote.sh b/git-ls-remote.sh
index f0f0b07..dc6a775 100755
--- a/git-ls-remote.sh
+++ b/git-ls-remote.sh
@@ -1,6 +1,5 @@
#!/bin/sh
#
-. git-sh-setup
usage () {
echo >&2 "usage: $0 [--heads] [--tags] <repository> <refs>..."
diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh
index b08597d..c3eca8b 100755
--- a/git-merge-one-file.sh
+++ b/git-merge-one-file.sh
@@ -25,7 +25,8 @@ case "${1:-.}${2:-.}${3:-.}" in
echo "Removing $4"
fi
if test -f "$4"; then
- rm -f -- "$4"
+ rm -f -- "$4" &&
+ rmdir -p "$(expr "$4" : '\(.*\)/')" 2>/dev/null
fi &&
exec git-update-index --remove -- "$4"
;;
diff --git a/git-merge-recursive.py b/git-merge-recursive.py
index d7d36aa..0129233 100755
--- a/git-merge-recursive.py
+++ b/git-merge-recursive.py
@@ -245,7 +245,7 @@ def updateFileExt(sha, mode, path, updateCache, updateWd):
try:
createDir = not stat.S_ISDIR(os.lstat(p).st_mode)
- except:
+ except OSError:
createDir = True
if createDir:
@@ -293,6 +293,10 @@ def removeFile(clean, path):
except OSError, e:
if e.errno != errno.ENOENT and e.errno != errno.EISDIR:
raise
+ try:
+ os.removedirs(os.path.dirname(path))
+ except OSError:
+ pass
def uniquePath(path, branch):
def fileExists(path):
diff --git a/git-merge.sh b/git-merge.sh
index 7f481e4..d352a3c 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano
#
-. git-sh-setup || die "Not a git archive"
+. git-sh-setup
LF='
'
@@ -12,10 +12,8 @@ usage () {
die "git-merge [-n] [--no-commit] [-s <strategy>]... <merge-message> <head> <remote>+"
}
-# all_strategies='resolve recursive stupid octopus'
-
all_strategies='recursive octopus resolve stupid ours'
-default_strategies='resolve octopus'
+default_strategies='recursive'
use_strategies=
dropsave() {
@@ -90,11 +88,6 @@ do
shift
done
-case "$use_strategies" in
-'')
- use_strategies=$default_strategies
- ;;
-esac
test "$#" -le 2 && usage ;# we need at least two heads.
merge_msg="$1"
@@ -185,6 +178,17 @@ case "$#,$common,$no_commit" in
;;
esac
+case "$use_strategies" in
+'')
+ case "$#" in
+ 1)
+ use_strategies="$default_strategies" ;;
+ *)
+ use_strategies=octopus ;;
+ esac
+ ;;
+esac
+
# At this point, we need a real merge. No matter what strategy
# we use, it would operate on the index, possibly affecting the
# working tree, and when resolved cleanly, have the desired tree
diff --git a/git-mv.perl b/git-mv.perl
index a21d87e..bf54c38 100755
--- a/git-mv.perl
+++ b/git-mv.perl
@@ -103,13 +103,22 @@ while(scalar @srcArgs > 0) {
$bad = "bad source '$src'";
}
+ $safesrc = quotemeta($src);
+ @srcfiles = grep /^$safesrc(\/|$)/, @allfiles;
+
$overwritten{$dst} = 0;
if (($bad eq "") && -e $dst) {
$bad = "destination '$dst' already exists";
- if (-f $dst && $opt_f) {
- print "Warning: $bad; will overwrite!\n";
- $bad = "";
- $overwritten{$dst} = 1;
+ if ($opt_f) {
+ # only files can overwrite each other: check both source and destination
+ if (-f $dst && (scalar @srcfiles == 1)) {
+ print "Warning: $bad; will overwrite!\n";
+ $bad = "";
+ $overwritten{$dst} = 1;
+ }
+ else {
+ $bad = "Can not overwrite '$src' with '$dst'";
+ }
}
}
@@ -118,8 +127,6 @@ while(scalar @srcArgs > 0) {
}
if ($bad eq "") {
- $safesrc = quotemeta($src);
- @srcfiles = grep /^$safesrc(\/|$)/, @allfiles;
if (scalar @srcfiles == 0) {
$bad = "'$src' not under version control";
}
@@ -166,10 +173,12 @@ while(scalar @srcs > 0) {
push @deletedfiles, @srcfiles;
if (scalar @srcfiles == 1) {
+ # $dst can be a directory with 1 file inside
if ($overwritten{$dst} ==1) {
- push @changedfiles, $dst;
+ push @changedfiles, $dstfiles[0];
+
} else {
- push @addedfiles, $dst;
+ push @addedfiles, $dstfiles[0];
}
}
else {
diff --git a/git-octopus.sh b/git-octopus.sh
index d2471af..2edbf52 100755
--- a/git-octopus.sh
+++ b/git-octopus.sh
@@ -4,7 +4,7 @@
#
# Resolve two or more trees recorded in $GIT_DIR/FETCH_HEAD.
#
-. git-sh-setup || die "Not a git archive"
+. git-sh-setup
usage () {
die "usage: git octopus"
diff --git a/git-parse-remote.sh b/git-parse-remote.sh
index aea7b0e..5f158c6 100755
--- a/git-parse-remote.sh
+++ b/git-parse-remote.sh
@@ -1,6 +1,8 @@
#!/bin/sh
-. git-sh-setup
+# git-ls-remote could be called from outside a git managed repository;
+# this would fail in that case and would issue an error message.
+GIT_DIR=$(git-rev-parse --git-dir 2>/dev/null) || :;
get_data_source () {
case "$1" in
diff --git a/git-prune.sh b/git-prune.sh
index c4de7f5..1fd8c73 100755
--- a/git-prune.sh
+++ b/git-prune.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-. git-sh-setup || die "Not a git archive"
+. git-sh-setup
dryrun=
echo=
diff --git a/git-pull.sh b/git-pull.sh
index 3b875ad..3a13984 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -4,7 +4,7 @@
#
# Fetch one or more remote refs and merge it/them into the current HEAD.
-. git-sh-setup || die "Not a git archive"
+. git-sh-setup
usage () {
echo >&2 "usage: $0"' [-n] [--no-commit] [--no-summary] [--help]
diff --git a/git-push.sh b/git-push.sh
index edc0b83..140c8f8 100755
--- a/git-push.sh
+++ b/git-push.sh
@@ -1,5 +1,5 @@
#!/bin/sh
-. git-sh-setup || die "Not a git archive"
+. git-sh-setup
usage () {
die "Usage: git push [--all] [--force] <repository> [<refspec>]"
diff --git a/git-rebase.sh b/git-rebase.sh
index 5289762..2bc3a12 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano.
#
-. git-sh-setup || die "Not a git archive."
+. git-sh-setup
# The other head is given
other=$(git-rev-parse --verify "$1^0") || exit
diff --git a/git-repack.sh b/git-repack.sh
index 55a7b27..430ddc5 100755
--- a/git-repack.sh
+++ b/git-repack.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Linus Torvalds
#
-. git-sh-setup || die "Not a git archive"
+. git-sh-setup
no_update_info= all_into_one= remove_redundant= local=
while case "$#" in 0) break ;; esac
@@ -32,24 +32,20 @@ case ",$all_into_one," in
rev_list=
rev_parse='--all'
pack_objects=
+
+ # Redundancy check in all-into-one case is trivial.
+ existing=`cd "$PACKDIR" && \
+ find . -type f \( -name '*.pack' -o -name '*.idx' \) -print`
;;
esac
if [ "$local" ]; then
pack_objects="$pack_objects --local"
fi
-name=$(git-rev-list --objects $rev_list $(git-rev-parse $rev_parse) |
+name=$(git-rev-list --objects $rev_list $(git-rev-parse $rev_parse) 2>&1 |
git-pack-objects --non-empty $pack_objects .tmp-pack) ||
exit 1
if [ -z "$name" ]; then
echo Nothing new to pack.
- if test "$remove_redundant" = t ; then
- echo "Removing redundant packs."
- sync
- redundant=$(git-pack-redundant --all)
- if test "$redundant" != "" ; then
- echo $redundant | xargs rm
- fi
- fi
exit 0
fi
echo "Pack pack-$name created."
@@ -62,23 +58,20 @@ exit
if test "$remove_redundant" = t
then
- sync
- if test "$all_into_one" = t
+ # We know $existing are all redundant only when
+ # all-into-one is used.
+ if test "$all_into_one" != '' && test "$existing" != ''
then
- cd "$PACKDIR"
- existing=`find . -type f \( -name '*.pack' -o -name '*.idx' \) -print`
- for e in $existing
- do
+ sync
+ ( cd "$PACKDIR" &&
+ for e in $existing
+ do
case "$e" in
./pack-$name.pack | ./pack-$name.idx) ;;
- *) rm -f $e ;;
+ *) rm -f $e ;;
esac
- done
- else
- redundant=$(git-pack-redundant --all)
- if test "$redundant" != "" ; then
- echo $redundant | xargs rm
- fi
+ done
+ )
fi
fi
diff --git a/git-reset.sh b/git-reset.sh
index 2086d26..72ef303 100755
--- a/git-reset.sh
+++ b/git-reset.sh
@@ -1,5 +1,5 @@
#!/bin/sh
-. git-sh-setup || die "Not a git archive"
+. git-sh-setup
usage () {
die 'Usage: git reset [--mixed | --soft | --hard] [<commit-ish>]'
diff --git a/git-resolve.sh b/git-resolve.sh
index 7d8fb54..fcc5ad7 100755
--- a/git-resolve.sh
+++ b/git-resolve.sh
@@ -4,7 +4,7 @@
#
# Resolve two trees.
#
-. git-sh-setup || die "Not a git archive"
+. git-sh-setup
usage () {
die "git-resolve <head> <remote> <merge-message>"
diff --git a/git-revert.sh b/git-revert.sh
index 4154fe0..c1aebb1 100755
--- a/git-revert.sh
+++ b/git-revert.sh
@@ -3,15 +3,17 @@
# Copyright (c) 2005 Linus Torvalds
# Copyright (c) 2005 Junio C Hamano
#
-. git-sh-setup || die "Not a git archive"
+. git-sh-setup
case "$0" in
*-revert* )
+ test -t 0 && edit=-e
me=revert ;;
*-cherry-pick* )
+ edit=
me=cherry-pick ;;
* )
- die "What are ou talking about?" ;;
+ die "What are you talking about?" ;;
esac
usage () {
@@ -33,6 +35,12 @@ do
--no-commi|--no-commit)
no_commit=t
;;
+ -e|--e|--ed|--edi|--edit)
+ edit=-e
+ ;;
+ -n|--n|--no|--no-|--no-e|--no-ed|--no-edi|--no-edit)
+ edit=
+ ;;
-r|--r|--re|--rep|--repl|--repla|--replay)
replay=t
;;
@@ -163,7 +171,7 @@ echo >&2 "Finished one $me."
case "$no_commit" in
'')
- git-commit -n -F .msg
+ git-commit -n -F .msg $edit
rm -f .msg
;;
esac
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index dbb9884..b4f1022 100755
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -1,10 +1,9 @@
#!/bin/sh
#
-# Set up GIT_DIR and GIT_OBJECT_DIRECTORY
-# and return true if everything looks ok
-#
-: ${GIT_DIR=.git}
-: ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"}
+# This is included in commands that either have to be run from the toplevel
+# of the repository, or with GIT_DIR environment variable properly.
+# If the GIT_DIR does not look like the right correct git-repository,
+# it dies.
# Having this variable in your environment would break scripts because
# you would cause "cd" to be be taken to unexpected places. If you
@@ -12,14 +11,13 @@
# exporting it.
unset CDPATH
+: ${GIT_DIR=.git}
+: ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"}
+
die() {
echo >&2 "$@"
exit 1
}
-case "$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD 2>/dev/null)" in
-refs/*) : ;;
-*) false ;;
-esac &&
-[ -d "$GIT_DIR/refs" ] &&
-[ -d "$GIT_OBJECT_DIRECTORY/" ]
+# Make sure we are in a valid repository of a vintage we understand.
+GIT_DIR="$GIT_DIR" git-var GIT_AUTHOR_IDENT >/dev/null || exit
diff --git a/git-status.sh b/git-status.sh
index 837f334..b90ffc1 100755
--- a/git-status.sh
+++ b/git-status.sh
@@ -2,7 +2,7 @@
#
# Copyright (c) 2005 Linus Torvalds
#
-. git-sh-setup || die "Not a git archive"
+GIT_DIR=$(git-rev-parse --git-dir) || exit
report () {
header="#
diff --git a/git-tag.sh b/git-tag.sh
index 1375945..16efc5b 100755
--- a/git-tag.sh
+++ b/git-tag.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Copyright (c) 2005 Linus Torvalds
-. git-sh-setup || die "Not a git archive"
+. git-sh-setup
usage () {
echo >&2 "Usage: git-tag [-a | -s | -u <key-id>] [-f | -d] [-m <msg>] <tagname> [<head>]"
diff --git a/git-verify-tag.sh b/git-verify-tag.sh
index ed4c893..3c65f4a 100755
--- a/git-verify-tag.sh
+++ b/git-verify-tag.sh
@@ -1,5 +1,5 @@
#!/bin/sh
-. git-sh-setup || die "Not a git archive"
+. git-sh-setup
type="$(git-cat-file -t "$1" 2>/dev/null)" ||
die "$1: no such object."
diff --git a/git.c b/git.c
index bdd3f8d..0b10b6e 100644
--- a/git.c
+++ b/git.c
@@ -273,7 +273,7 @@ int main(int argc, char **argv, char **envp)
while (!strncmp(exec_path, "./", 2)) {
exec_path += 2;
while (*exec_path == '/')
- *exec_path++;
+ exec_path++;
}
snprintf(git_command + len, sizeof(git_command) - len,
"/%s", exec_path);
diff --git a/gitk b/gitk
index a9d37d9..3dd97e2 100755
--- a/gitk
+++ b/gitk
@@ -60,7 +60,7 @@ proc getcommits {rargs} {
proc getcommitlines {commfd} {
global commits parents cdate children
- global commitlisted phase commitinfo nextupdate
+ global commitlisted phase nextupdate
global stopped redisplaying leftover
set stuff [read $commfd]
@@ -196,42 +196,44 @@ proc parsecommit {id contents listed olds} {
incr ncleft($p)
}
}
- foreach line [split $contents "\n"] {
- if {$inhdr} {
- if {$line == {}} {
- set inhdr 0
- } else {
- set tag [lindex $line 0]
- if {$tag == "author"} {
- set x [expr {[llength $line] - 2}]
- set audate [lindex $line $x]
- set auname [lrange $line 1 [expr {$x - 1}]]
- } elseif {$tag == "committer"} {
- set x [expr {[llength $line] - 2}]
- set comdate [lindex $line $x]
- set comname [lrange $line 1 [expr {$x - 1}]]
- }
- }
- } else {
- if {$comment == {}} {
- set headline [string trim $line]
- } else {
- append comment "\n"
- }
- if {!$listed} {
- # git-rev-list indents the comment by 4 spaces;
- # if we got this via git-cat-file, add the indentation
- append comment " "
- }
- append comment $line
+ set hdrend [string first "\n\n" $contents]
+ if {$hdrend < 0} {
+ # should never happen...
+ set hdrend [string length $contents]
+ }
+ set header [string range $contents 0 [expr {$hdrend - 1}]]
+ set comment [string range $contents [expr {$hdrend + 2}] end]
+ foreach line [split $header "\n"] {
+ set tag [lindex $line 0]
+ if {$tag == "author"} {
+ set audate [lindex $line end-1]
+ set auname [lrange $line 1 end-2]
+ } elseif {$tag == "committer"} {
+ set comdate [lindex $line end-1]
+ set comname [lrange $line 1 end-2]
}
}
- if {$audate != {}} {
- set audate [clock format $audate -format "%Y-%m-%d %H:%M:%S"]
+ set headline {}
+ # take the first line of the comment as the headline
+ set i [string first "\n" $comment]
+ if {$i >= 0} {
+ set headline [string trim [string range $comment 0 $i]]
+ } else {
+ set headline $comment
+ }
+ if {!$listed} {
+ # git-rev-list indents the comment by 4 spaces;
+ # if we got this via git-cat-file, add the indentation
+ set newcomment {}
+ foreach line [split $comment "\n"] {
+ append newcomment " "
+ append newcomment $line
+ append newcomment "\n"
+ }
+ set comment $newcomment
}
if {$comdate != {}} {
set cdate($id) $comdate
- set comdate [clock format $comdate -format "%Y-%m-%d %H:%M:%S"]
}
set commitinfo($id) [list $headline $auname $audate \
$comname $comdate $comment]
@@ -239,77 +241,43 @@ proc parsecommit {id contents listed olds} {
proc readrefs {} {
global tagids idtags headids idheads tagcontents
-
- set tags [glob -nocomplain -types f [gitdir]/refs/tags/*]
- foreach f $tags {
- catch {
- set fd [open $f r]
- set line [read $fd]
- if {[regexp {^[0-9a-f]{40}} $line id]} {
- set direct [file tail $f]
- set tagids($direct) $id
- lappend idtags($id) $direct
- set tagblob [exec git-cat-file tag $id]
- set contents [split $tagblob "\n"]
- set obj {}
- set type {}
- set tag {}
- foreach l $contents {
- if {$l == {}} break
- switch -- [lindex $l 0] {
- "object" {set obj [lindex $l 1]}
- "type" {set type [lindex $l 1]}
- "tag" {set tag [string range $l 4 end]}
- }
- }
- if {$obj != {} && $type == "commit" && $tag != {}} {
- set tagids($tag) $obj
- lappend idtags($obj) $tag
- set tagcontents($tag) $tagblob
- }
- }
- close $fd
- }
- }
- set heads [glob -nocomplain -types f [gitdir]/refs/heads/*]
- foreach f $heads {
- catch {
- set fd [open $f r]
- set line [read $fd 40]
- if {[regexp {^[0-9a-f]{40}} $line id]} {
- set head [file tail $f]
- set headids($head) $line
- lappend idheads($line) $head
- }
- close $fd
- }
- }
- readotherrefs refs {} {tags heads}
-}
-
-proc readotherrefs {base dname excl} {
global otherrefids idotherrefs
- set git [gitdir]
- set files [glob -nocomplain -types f [file join $git $base *]]
- foreach f $files {
- catch {
- set fd [open $f r]
- set line [read $fd 40]
- if {[regexp {^[0-9a-f]{40}} $line id]} {
- set name "$dname[file tail $f]"
- set otherrefids($name) $id
- lappend idotherrefs($id) $name
+ set refd [open [list | git-ls-remote [gitdir]] r]
+ while {0 <= [set n [gets $refd line]]} {
+ if {![regexp {^([0-9a-f]{40}) refs/([^^]*)$} $line \
+ match id path]} {
+ continue
+ }
+ if {![regexp {^(tags|heads)/(.*)$} $path match type name]} {
+ set type others
+ set name $path
+ }
+ if {$type == "tags"} {
+ set tagids($name) $id
+ lappend idtags($id) $name
+ set obj {}
+ set type {}
+ set tag {}
+ catch {
+ set commit [exec git-rev-parse "$id^0"]
+ if {"$commit" != "$id"} {
+ set tagids($name) $commit
+ lappend idtags($commit) $name
+ }
+ }
+ catch {
+ set tagcontents($name) [exec git-cat-file tag "$id"]
}
- close $fd
+ } elseif { $type == "heads" } {
+ set headids($name) $id
+ lappend idheads($id) $name
+ } else {
+ set otherrefids($name) $id
+ lappend idotherrefs($id) $name
}
}
- set dirs [glob -nocomplain -types d [file join $git $base *]]
- foreach d $dirs {
- set dir [file tail $d]
- if {[lsearch -exact $excl $dir] >= 0} continue
- readotherrefs [file join $base $dir] "$dname$dir/" {}
- }
+ close $refd
}
proc error_popup msg {
@@ -683,7 +651,7 @@ Use and redistribute under the terms of the GNU General Public License} \
}
proc assigncolor {id} {
- global commitinfo colormap commcolors colors nextcolor
+ global colormap commcolors colors nextcolor
global parents nparents children nchildren
global cornercrossings crossings
@@ -783,10 +751,12 @@ proc bindline {t id} {
$canv bind $t <Button-1> "lineclick %x %y $id 1"
}
-proc drawlines {id xtra} {
+proc drawlines {id xtra delold} {
global mainline mainlinearrow sidelines lthickness colormap canv
- $canv delete lines.$id
+ if {$delold} {
+ $canv delete lines.$id
+ }
if {[info exists mainline($id)]} {
set t [$canv create line $mainline($id) \
-width [expr {($xtra + 1) * $lthickness}] \
@@ -858,7 +828,7 @@ proc drawcommitline {level} {
set mainline($id) [trimdiagstart $mainline($id)]
}
}
- drawlines $id 0
+ drawlines $id 0 0
set orad [expr {$linespc / 3}]
set t [$canv create oval [expr $x - $orad] [expr $y1 - $orad] \
[expr $x + $orad - 1] [expr $y1 + $orad - 1] \
@@ -878,6 +848,7 @@ proc drawcommitline {level} {
set headline [lindex $commitinfo($id) 0]
set name [lindex $commitinfo($id) 1]
set date [lindex $commitinfo($id) 2]
+ set date [formatdate $date]
set linehtag($lineno) [$canv create text $xt $y1 -anchor w \
-text $headline -font $mainfont ]
$canv bind $linehtag($lineno) <Button-3> "rowmenu %X %Y $id"
@@ -1446,8 +1417,8 @@ proc decidenext {{noread 0}} {
}
proc drawcommit {id} {
- global phase todo nchildren datemode nextupdate
- global numcommits ncmupdate displayorder todo onscreen
+ global phase todo nchildren datemode nextupdate revlistorder
+ global numcommits ncmupdate displayorder todo onscreen parents
if {$phase != "incrdraw"} {
set phase incrdraw
@@ -1459,19 +1430,29 @@ proc drawcommit {id} {
lappend todo $id
set onscreen($id) 0
}
- set level [decidenext 1]
- if {$level == {} || $id != [lindex $todo $level]} {
- return
- }
- while 1 {
- lappend displayorder [lindex $todo $level]
- if {[updatetodo $level $datemode]} {
- set level [decidenext 1]
- if {$level == {}} break
+ if {$revlistorder} {
+ set level [lsearch -exact $todo $id]
+ if {$level < 0} {
+ error_popup "oops, $id isn't in todo"
+ return
+ }
+ lappend displayorder $id
+ updatetodo $level 0
+ } else {
+ set level [decidenext 1]
+ if {$level == {} || $id != [lindex $todo $level]} {
+ return
}
- set id [lindex $todo $level]
- if {![info exists commitlisted($id)]} {
- break
+ while 1 {
+ lappend displayorder [lindex $todo $level]
+ if {[updatetodo $level $datemode]} {
+ set level [decidenext 1]
+ if {$level == {}} break
+ }
+ set id [lindex $todo $level]
+ if {![info exists commitlisted($id)]} {
+ break
+ }
}
}
drawmore 1
@@ -1523,7 +1504,7 @@ proc drawrest {} {
global phase stopped redisplaying selectedline
global datemode todo displayorder
global numcommits ncmupdate
- global nextupdate startmsecs
+ global nextupdate startmsecs revlistorder
set level [decidenext]
if {$level >= 0} {
@@ -1536,8 +1517,8 @@ proc drawrest {} {
if {$level < 0} break
}
}
- drawmore 0
}
+ drawmore 0
set phase {}
set drawmsecs [expr [clock clicks -milliseconds] - $startmsecs]
#puts "overall $drawmsecs ms for $numcommits commits"
@@ -2146,8 +2127,10 @@ proc selectline {l isnew} {
$ctext mark set fmark.0 0.0
$ctext mark gravity fmark.0 left
set info $commitinfo($id)
- $ctext insert end "Author: [lindex $info 1] [lindex $info 2]\n"
- $ctext insert end "Committer: [lindex $info 3] [lindex $info 4]\n"
+ set date [formatdate [lindex $info 2]]
+ $ctext insert end "Author: [lindex $info 1] $date\n"
+ set date [formatdate [lindex $info 4]]
+ $ctext insert end "Committer: [lindex $info 3] $date\n"
if {[info exists idtags($id)]} {
$ctext insert end "Tags:"
foreach tag $idtags($id) {
@@ -2805,8 +2788,7 @@ proc gettreediffs {ids} {
set treepending $ids
set treediff {}
set id [lindex $ids 0]
- set p [lindex $ids 1]
- if [catch {set gdtf [open "|git-diff-tree -r $id" r]}] return
+ if [catch {set gdtf [open "|git-diff-tree --no-commit-id -r $id" r]}] return
fconfigure $gdtf -blocking 0
fileevent $gdtf readable [list gettreediffline $gdtf $ids]
}
@@ -2840,9 +2822,8 @@ proc getblobdiffs {ids} {
global difffilestart nextupdate diffinhdr treediffs
set id [lindex $ids 0]
- set p [lindex $ids 1]
set env(GIT_DIFF_OPTS) $diffopts
- set cmd [list | git-diff-tree -r -p -C $id]
+ set cmd [list | git-diff-tree --no-commit-id -r -p -C $id]
if {[catch {set bdf [open $cmd r]} err]} {
puts "error getting diffs: $err"
return
@@ -3143,7 +3124,7 @@ proc linehover {} {
set t [$canv create rectangle $x0 $y0 $x1 $y1 \
-fill \#ffff80 -outline black -width 1 -tags hover]
$canv raise $t
- set t [$canv create text $x $y -anchor nw -text $text -tags hover]
+ set t [$canv create text $x $y -anchor nw -text $text -tags hover -font $mainfont]
$canv raise $t
}
@@ -3178,7 +3159,7 @@ proc clickisonarrow {id y} {
}
proc arrowjump {id dirn y} {
- global mainline sidelines canv
+ global mainline sidelines canv canv2 canv3
set yt {}
if {$dirn eq "down"} {
@@ -3216,6 +3197,8 @@ proc arrowjump {id dirn y} {
set yfrac 0
}
$canv yview moveto $yfrac
+ $canv2 yview moveto $yfrac
+ $canv3 yview moveto $yfrac
}
proc lineclick {x y id isnew} {
@@ -3226,7 +3209,7 @@ proc lineclick {x y id isnew} {
normalline
$canv delete hover
# draw this line thicker than normal
- drawlines $id 1
+ drawlines $id 1 1
set thickerline $id
if {$isnew} {
set ymax [lindex [$canv cget -scrollregion] 3]
@@ -3255,7 +3238,8 @@ proc lineclick {x y id isnew} {
set info $commitinfo($id)
$ctext insert end "\n\t[lindex $info 0]\n"
$ctext insert end "\tAuthor:\t[lindex $info 1]\n"
- $ctext insert end "\tDate:\t[lindex $info 2]\n"
+ set date [formatdate [lindex $info 2]]
+ $ctext insert end "\tDate:\t$date\n"
if {[info exists children($id)]} {
$ctext insert end "\nChildren:"
set i 0
@@ -3267,7 +3251,8 @@ proc lineclick {x y id isnew} {
$ctext tag bind link$i <1> [list selbyid $child]
$ctext insert end "\n\t[lindex $info 0]"
$ctext insert end "\n\tAuthor:\t[lindex $info 1]"
- $ctext insert end "\n\tDate:\t[lindex $info 2]\n"
+ set date [formatdate [lindex $info 2]]
+ $ctext insert end "\n\tDate:\t$date\n"
}
}
$ctext conf -state disabled
@@ -3278,7 +3263,7 @@ proc lineclick {x y id isnew} {
proc normalline {} {
global thickerline
if {[info exists thickerline]} {
- drawlines $thickerline 0
+ drawlines $thickerline 0 1
unset thickerline
}
}
@@ -3650,6 +3635,23 @@ proc doquit {} {
destroy .
}
+proc formatdate {d} {
+ global hours nhours tfd fastdate
+
+ if {!$fastdate} {
+ return [clock format $d -format "%Y-%m-%d %H:%M:%S"]
+ }
+ set hr [expr {$d / 3600}]
+ set ms [expr {$d % 3600}]
+ if {![info exists hours($hr)]} {
+ set hours($hr) [clock format $d -format "%Y-%m-%d %H"]
+ set nhours($hr) 0
+ }
+ incr nhours($hr)
+ set minsec [format "%.2d:%.2d" [expr {$ms/60}] [expr {$ms%60}]]
+ return "$hours($hr):$minsec"
+}
+
# defaults...
set datemode 0
set boldnames 0
@@ -3662,6 +3664,8 @@ set findmergefiles 0
set gaudydiff 0
set maxgraphpct 50
set maxwidth 16
+set revlistorder 0
+set fastdate 0
set colors {green red blue magenta darkgrey brown orange}
@@ -3678,6 +3682,7 @@ foreach arg $argv {
"^$" { }
"^-b" { set boldnames 1 }
"^-d" { set datemode 1 }
+ "^-r" { set revlistorder 1 }
default {
lappend revtreeargs $arg
}
diff --git a/http-fetch.c b/http-fetch.c
index 21cc1b9..4353173 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -2,44 +2,14 @@
#include "commit.h"
#include "pack.h"
#include "fetch.h"
-
-#include <curl/curl.h>
-#include <curl/easy.h>
-
-#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
+#include "http.h"
#define PREV_BUF_SIZE 4096
#define RANGE_HEADER_SIZE 30
static int got_alternates = -1;
-static int active_requests = 0;
-static int data_received;
-#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 *pragma_header;
static struct curl_slist *no_pragma_header;
-static struct curl_slist *no_range_header;
-static char curl_errorstr[CURL_ERROR_SIZE];
struct alt_base
{
@@ -51,14 +21,14 @@ struct alt_base
static struct alt_base *alt = NULL;
-enum transfer_state {
+enum object_request_state {
WAITING,
ABORTED,
ACTIVE,
COMPLETE,
};
-struct transfer_request
+struct object_request
{
unsigned char sha1[20];
struct alt_base *repo;
@@ -66,7 +36,7 @@ struct transfer_request
char filename[PATH_MAX];
char tmpfile[PATH_MAX];
int local;
- enum transfer_state state;
+ enum object_request_state state;
CURLcode curl_result;
char errorstr[CURL_ERROR_SIZE];
long http_code;
@@ -76,23 +46,10 @@ struct transfer_request
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;
- void *callback_data;
- void (*callback_func)(void *data);
- struct active_request_slot *next;
+ struct object_request *next;
};
-struct alt_request {
+struct alternates_request {
char *base;
char *url;
struct buffer *buffer;
@@ -100,120 +57,7 @@ struct alt_request {
int http_specific;
};
-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 buffer
-{
- size_t posn;
- size_t size;
- void *buffer;
-};
-
-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 fwrite_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(buffer->buffer + buffer->posn, ptr, size);
- buffer->posn += size;
- data_received++;
- 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 struct object_request *object_queue_head = NULL;
static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
void *data)
@@ -221,194 +65,35 @@ static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
unsigned char expn[4096];
size_t size = eltsize * nmemb;
int posn = 0;
- struct transfer_request *request = (struct transfer_request *)data;
+ struct object_request *obj_req = (struct object_request *)data;
do {
- ssize_t retval = write(request->local,
+ ssize_t retval = write(obj_req->local,
ptr + posn, size - posn);
if (retval < 0)
return posn;
posn += retval;
} while (posn < size);
- request->stream.avail_in = size;
- request->stream.next_in = ptr;
+ obj_req->stream.avail_in = size;
+ obj_req->stream.next_in = ptr;
do {
- request->stream.next_out = expn;
- request->stream.avail_out = sizeof(expn);
- request->zret = inflate(&request->stream, Z_SYNC_FLUSH);
- SHA1_Update(&request->c, expn,
- sizeof(expn) - request->stream.avail_out);
- } while (request->stream.avail_in && request->zret == Z_OK);
+ obj_req->stream.next_out = expn;
+ obj_req->stream.avail_out = sizeof(expn);
+ obj_req->zret = inflate(&obj_req->stream, Z_SYNC_FLUSH);
+ SHA1_Update(&obj_req->c, expn,
+ sizeof(expn) - obj_req->stream.avail_out);
+ } while (obj_req->stream.avail_in && obj_req->zret == Z_OK);
data_received++;
return size;
}
-#ifdef USE_CURL_MULTI
-static void process_curl_messages(void);
-static void process_request_queue(void);
-#endif
static void fetch_alternates(char *base);
-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);
- }
-
- curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1);
-
- return result;
-}
-
-static struct active_request_slot *get_active_slot(void)
-{
- struct active_request_slot *slot = active_queue_head;
- struct active_request_slot *newslot;
+static void process_object_response(void *callback_data);
-#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;
- slot->callback_data = NULL;
- slot->callback_func = NULL;
- curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);
- curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_range_header);
- curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);
-
- return slot;
-}
-
-static int start_active_slot(struct active_request_slot *slot)
+static void start_object_request(struct object_request *obj_req)
{
-#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_request(struct transfer_request *request)
-{
- char *hex = sha1_to_hex(request->sha1);
+ char *hex = sha1_to_hex(obj_req->sha1);
char prevfile[PATH_MAX];
char *url;
char *posn;
@@ -420,53 +105,53 @@ static void start_request(struct transfer_request *request)
struct curl_slist *range_header = NULL;
struct active_request_slot *slot;
- snprintf(prevfile, sizeof(prevfile), "%s.prev", request->filename);
+ snprintf(prevfile, sizeof(prevfile), "%s.prev", obj_req->filename);
unlink(prevfile);
- rename(request->tmpfile, prevfile);
- unlink(request->tmpfile);
+ rename(obj_req->tmpfile, prevfile);
+ unlink(obj_req->tmpfile);
- if (request->local != -1)
- error("fd leakage in start: %d", request->local);
- request->local = open(request->tmpfile,
+ if (obj_req->local != -1)
+ error("fd leakage in start: %d", obj_req->local);
+ obj_req->local = open(obj_req->tmpfile,
O_WRONLY | O_CREAT | O_EXCL, 0666);
/* This could have failed due to the "lazy directory creation";
* try to mkdir the last path component.
*/
- if (request->local < 0 && errno == ENOENT) {
- char *dir = strrchr(request->tmpfile, '/');
+ if (obj_req->local < 0 && errno == ENOENT) {
+ char *dir = strrchr(obj_req->tmpfile, '/');
if (dir) {
*dir = 0;
- mkdir(request->tmpfile, 0777);
+ mkdir(obj_req->tmpfile, 0777);
*dir = '/';
}
- request->local = open(request->tmpfile,
+ obj_req->local = open(obj_req->tmpfile,
O_WRONLY | O_CREAT | O_EXCL, 0666);
}
- if (request->local < 0) {
- request->state = ABORTED;
+ if (obj_req->local < 0) {
+ obj_req->state = ABORTED;
error("Couldn't create temporary file %s for %s: %s\n",
- request->tmpfile, request->filename, strerror(errno));
+ obj_req->tmpfile, obj_req->filename, strerror(errno));
return;
}
- memset(&request->stream, 0, sizeof(request->stream));
+ memset(&obj_req->stream, 0, sizeof(obj_req->stream));
- inflateInit(&request->stream);
+ inflateInit(&obj_req->stream);
- SHA1_Init(&request->c);
+ SHA1_Init(&obj_req->c);
- url = xmalloc(strlen(request->repo->base) + 50);
- request->url = xmalloc(strlen(request->repo->base) + 50);
- strcpy(url, request->repo->base);
- posn = url + strlen(request->repo->base);
+ url = xmalloc(strlen(obj_req->repo->base) + 50);
+ obj_req->url = xmalloc(strlen(obj_req->repo->base) + 50);
+ strcpy(url, obj_req->repo->base);
+ posn = url + strlen(obj_req->repo->base);
strcpy(posn, "objects/");
posn += 8;
memcpy(posn, hex, 2);
posn += 2;
*(posn++) = '/';
strcpy(posn, hex + 2);
- strcpy(request->url, url);
+ strcpy(obj_req->url, url);
/* If a previous temp file is present, process what was already
fetched. */
@@ -478,7 +163,7 @@ static void start_request(struct transfer_request *request)
if (fwrite_sha1_file(prev_buf,
1,
prev_read,
- request) == prev_read) {
+ obj_req) == prev_read) {
prev_posn += prev_read;
} else {
prev_read = -1;
@@ -492,20 +177,24 @@ static void start_request(struct transfer_request *request)
/* Reset inflate/SHA1 if there was an error reading the previous temp
file; also rewind to the beginning of the local file. */
if (prev_read == -1) {
- memset(&request->stream, 0, sizeof(request->stream));
- inflateInit(&request->stream);
- SHA1_Init(&request->c);
+ memset(&obj_req->stream, 0, sizeof(obj_req->stream));
+ inflateInit(&obj_req->stream);
+ SHA1_Init(&obj_req->c);
if (prev_posn>0) {
prev_posn = 0;
- lseek(request->local, SEEK_SET, 0);
- ftruncate(request->local, 0);
+ lseek(obj_req->local, SEEK_SET, 0);
+ ftruncate(obj_req->local, 0);
}
}
slot = get_active_slot();
- curl_easy_setopt(slot->curl, CURLOPT_FILE, request);
+ slot->callback_func = process_object_response;
+ slot->callback_data = obj_req;
+ obj_req->slot = slot;
+
+ curl_easy_setopt(slot->curl, CURLOPT_FILE, obj_req);
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
- curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr);
+ curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, obj_req->errorstr);
curl_easy_setopt(slot->curl, CURLOPT_URL, url);
curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
@@ -523,151 +212,111 @@ static void start_request(struct transfer_request *request)
}
/* Try to get the request started, abort the request on error */
+ obj_req->state = ACTIVE;
if (!start_active_slot(slot)) {
- request->state = ABORTED;
- close(request->local); request->local = -1;
- free(request->url);
+ obj_req->state = ABORTED;
+ obj_req->slot = NULL;
+ close(obj_req->local); obj_req->local = -1;
+ free(obj_req->url);
return;
}
- request->slot = slot;
- request->state = ACTIVE;
}
-static void finish_request(struct transfer_request *request)
+static void finish_object_request(struct object_request *obj_req)
{
struct stat st;
- fchmod(request->local, 0444);
- close(request->local); request->local = -1;
+ fchmod(obj_req->local, 0444);
+ close(obj_req->local); obj_req->local = -1;
- if (request->http_code == 416) {
+ if (obj_req->http_code == 416) {
fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n");
- } else if (request->curl_result != CURLE_OK) {
- if (stat(request->tmpfile, &st) == 0)
+ } else if (obj_req->curl_result != CURLE_OK) {
+ if (stat(obj_req->tmpfile, &st) == 0)
if (st.st_size == 0)
- unlink(request->tmpfile);
+ unlink(obj_req->tmpfile);
return;
}
- inflateEnd(&request->stream);
- SHA1_Final(request->real_sha1, &request->c);
- if (request->zret != Z_STREAM_END) {
- unlink(request->tmpfile);
+ inflateEnd(&obj_req->stream);
+ SHA1_Final(obj_req->real_sha1, &obj_req->c);
+ if (obj_req->zret != Z_STREAM_END) {
+ unlink(obj_req->tmpfile);
return;
}
- if (memcmp(request->sha1, request->real_sha1, 20)) {
- unlink(request->tmpfile);
+ if (memcmp(obj_req->sha1, obj_req->real_sha1, 20)) {
+ unlink(obj_req->tmpfile);
return;
}
- request->rename =
- move_temp_to_file(request->tmpfile, request->filename);
+ obj_req->rename =
+ move_temp_to_file(obj_req->tmpfile, obj_req->filename);
- if (request->rename == 0)
- pull_say("got %s\n", sha1_to_hex(request->sha1));
+ if (obj_req->rename == 0)
+ pull_say("got %s\n", sha1_to_hex(obj_req->sha1));
}
-static void release_request(struct transfer_request *request)
+static void process_object_response(void *callback_data)
{
- struct transfer_request *entry = request_queue_head;
+ struct object_request *obj_req =
+ (struct object_request *)callback_data;
- if (request->local != -1)
- error("fd leakage in release: %d", request->local);
- 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;
+ obj_req->curl_result = obj_req->slot->curl_result;
+ obj_req->http_code = obj_req->slot->http_code;
+ obj_req->slot = NULL;
+ obj_req->state = COMPLETE;
+
+ /* Use alternates if necessary */
+ if (obj_req->http_code == 404) {
+ fetch_alternates(alt->base);
+ if (obj_req->repo->next != NULL) {
+ obj_req->repo =
+ obj_req->repo->next;
+ close(obj_req->local);
+ obj_req->local = -1;
+ start_object_request(obj_req);
+ return;
+ }
}
- free(request->url);
- free(request);
+ finish_object_request(obj_req);
}
-#ifdef USE_CURL_MULTI
-static void process_curl_messages(void)
+static void release_object_request(struct object_request *obj_req)
{
- 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) {
- int curl_result = curl_message->data.result;
- 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_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;
- } else {
- fprintf(stderr, "Received DONE message for unknown request!\n");
- }
+ struct object_request *entry = object_queue_head;
- /* Process slot callback if appropriate */
- if (slot->callback_func != NULL) {
- slot->callback_func(slot->callback_data);
- }
-
- if (request != NULL) {
- request->curl_result = curl_result;
- request->http_code = slot->http_code;
- request->slot = NULL;
- request->state = COMPLETE;
-
- /* Use alternates if necessary */
- if (request->http_code == 404) {
- fetch_alternates(alt->base);
- if (request->repo->next != NULL) {
- request->repo =
- request->repo->next;
- close(request->local);
- request->local = -1;
- start_request(request);
- } else {
- finish_request(request);
- }
- } else {
- finish_request(request);
- }
- }
- } else {
- fprintf(stderr, "Unknown CURL message received: %d\n",
- (int)curl_message->msg);
- }
- curl_message = curl_multi_info_read(curlm, &num_messages);
+ if (obj_req->local != -1)
+ error("fd leakage in release: %d", obj_req->local);
+ if (obj_req == object_queue_head) {
+ object_queue_head = obj_req->next;
+ } else {
+ while (entry->next != NULL && entry->next != obj_req)
+ entry = entry->next;
+ if (entry->next == obj_req)
+ entry->next = entry->next->next;
}
+
+ free(obj_req->url);
+ free(obj_req);
}
-static void process_request_queue(void)
+#ifdef USE_CURL_MULTI
+void fill_active_slots(void)
{
- struct transfer_request *request = request_queue_head;
+ struct object_request *obj_req = object_queue_head;
struct active_request_slot *slot = active_queue_head;
int num_transfers;
- while (active_requests < max_requests && request != NULL) {
- if (request->state == WAITING) {
- if (has_sha1_file(request->sha1))
- release_request(request);
+ while (active_requests < max_requests && obj_req != NULL) {
+ if (obj_req->state == WAITING) {
+ if (has_sha1_file(obj_req->sha1))
+ release_object_request(obj_req);
else
- start_request(request);
+ start_object_request(obj_req);
curl_multi_perform(curlm, &num_transfers);
}
- request = request->next;
+ obj_req = obj_req->next;
}
while (slot != NULL) {
@@ -682,8 +331,8 @@ static void process_request_queue(void)
void prefetch(unsigned char *sha1)
{
- struct transfer_request *newreq;
- struct transfer_request *tail;
+ struct object_request *newreq;
+ struct object_request *tail;
char *filename = sha1_file_name(sha1);
newreq = xmalloc(sizeof(*newreq));
@@ -697,18 +346,19 @@ void prefetch(unsigned char *sha1)
"%s.temp", filename);
newreq->next = NULL;
- if (request_queue_head == NULL) {
- request_queue_head = newreq;
+ if (object_queue_head == NULL) {
+ object_queue_head = newreq;
} else {
- tail = request_queue_head;
+ tail = object_queue_head;
while (tail->next != NULL) {
tail = tail->next;
}
tail->next = newreq;
}
+
#ifdef USE_CURL_MULTI
- process_request_queue();
- process_curl_messages();
+ fill_active_slots();
+ step_active_slots();
#endif
}
@@ -793,9 +443,10 @@ static int setup_index(struct alt_base *repo, unsigned char *sha1)
return 0;
}
-static void process_alternates(void *callback_data)
+static void process_alternates_response(void *callback_data)
{
- struct alt_request *alt_req = (struct alt_request *)callback_data;
+ struct alternates_request *alt_req =
+ (struct alternates_request *)callback_data;
struct active_request_slot *slot = alt_req->slot;
struct alt_base *tail = alt;
char *base = alt_req->base;
@@ -815,12 +466,11 @@ static void process_alternates(void *callback_data)
alt_req->url);
active_requests++;
slot->in_use = 1;
- slot->done = 0;
if (start_active_slot(slot)) {
return;
} else {
got_alternates = -1;
- slot->done = 1;
+ slot->in_use = 0;
return;
}
}
@@ -831,7 +481,7 @@ static void process_alternates(void *callback_data)
}
}
- fwrite_buffer_dynamic(&null_byte, 1, 1, alt_req->buffer);
+ fwrite_buffer(&null_byte, 1, 1, alt_req->buffer);
alt_req->buffer->posn--;
data = alt_req->buffer->buffer;
@@ -901,17 +551,16 @@ static void fetch_alternates(char *base)
char *url;
char *data;
struct active_request_slot *slot;
- static struct alt_request alt_req;
- int num_transfers;
+ static struct alternates_request alt_req;
/* If another request has already started fetching alternates,
wait for them to arrive and return to processing this request's
curl message */
+#ifdef USE_CURL_MULTI
while (got_alternates == 0) {
- curl_multi_perform(curlm, &num_transfers);
- process_curl_messages();
- process_request_queue();
+ step_active_slots();
}
+#endif
/* Nothing to do if they've already been fetched */
if (got_alternates == 1)
@@ -934,12 +583,11 @@ static void fetch_alternates(char *base)
/* Use a callback to process the result, since another request
may fail and need to have alternates loaded before continuing */
slot = get_active_slot();
- slot->callback_func = process_alternates;
+ slot->callback_func = process_alternates_response;
slot->callback_data = &alt_req;
curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
- curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
- fwrite_buffer_dynamic);
+ curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
curl_easy_setopt(slot->curl, CURLOPT_URL, url);
alt_req.base = base;
@@ -983,17 +631,24 @@ static int fetch_indices(struct alt_base *repo)
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_WRITEFUNCTION, fwrite_buffer);
curl_easy_setopt(slot->curl, CURLOPT_URL, url);
curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
if (start_active_slot(slot)) {
run_active_slot(slot);
if (slot->curl_result != CURLE_OK) {
- free(buffer.buffer);
- return error("%s", curl_errorstr);
+ if (slot->http_code == 404) {
+ repo->got_indices = 1;
+ free(buffer.buffer);
+ return 0;
+ } else {
+ repo->got_indices = 0;
+ free(buffer.buffer);
+ return error("%s", curl_errorstr);
+ }
}
} else {
+ repo->got_indices = 0;
free(buffer.buffer);
return error("Unable to start request");
}
@@ -1115,94 +770,56 @@ static int fetch_pack(struct alt_base *repo, unsigned char *sha1)
static int fetch_object(struct alt_base *repo, unsigned char *sha1)
{
char *hex = sha1_to_hex(sha1);
- int ret;
- struct transfer_request *request = request_queue_head;
+ int ret = 0;
+ struct object_request *obj_req = object_queue_head;
- while (request != NULL && memcmp(request->sha1, sha1, 20))
- request = request->next;
- if (request == NULL)
+ while (obj_req != NULL && memcmp(obj_req->sha1, sha1, 20))
+ obj_req = obj_req->next;
+ if (obj_req == NULL)
return error("Couldn't find request for %s in the queue", hex);
- if (has_sha1_file(request->sha1)) {
- release_request(request);
+ if (has_sha1_file(obj_req->sha1)) {
+ release_object_request(obj_req);
return 0;
}
#ifdef USE_CURL_MULTI
- while (request->state == WAITING) {
- int num_transfers;
- curl_multi_perform(curlm, &num_transfers);
- if (num_transfers < active_requests) {
- process_curl_messages();
- process_request_queue();
- }
+ while (obj_req->state == WAITING) {
+ step_active_slots();
}
#else
- start_request(request);
+ start_object_request(obj_req);
#endif
- while (request->state == ACTIVE) {
- run_active_slot(request->slot);
-#ifndef USE_CURL_MULTI
- request->curl_result = request->slot->curl_result;
- request->http_code = request->slot->http_code;
- request->slot = NULL;
-
- /* Use alternates if necessary */
- if (request->http_code == 404) {
- fetch_alternates(alt->base);
- if (request->repo->next != NULL) {
- request->repo = request->repo->next;
- close(request->local); request->local = -1;
- start_request(request);
- }
- } else {
- finish_request(request);
- request->state = COMPLETE;
- }
-#endif
+ while (obj_req->state == ACTIVE) {
+ run_active_slot(obj_req->slot);
}
- if (request->local != -1) {
- close(request->local); request->local = -1;
+ if (obj_req->local != -1) {
+ close(obj_req->local); obj_req->local = -1;
}
- if (request->state == ABORTED) {
- release_request(request);
- return error("Request for %s aborted", hex);
- }
-
- if (request->curl_result != CURLE_OK && request->http_code != 416) {
- if (request->http_code == 404)
+ if (obj_req->state == ABORTED) {
+ ret = error("Request for %s aborted", hex);
+ } else if (obj_req->curl_result != CURLE_OK &&
+ obj_req->http_code != 416) {
+ if (obj_req->http_code == 404)
ret = -1; /* Be silent, it is probably in a pack. */
else
ret = error("%s (curl_result = %d, http_code = %ld, sha1 = %s)",
- request->errorstr, request->curl_result,
- request->http_code, hex);
- release_request(request);
- return ret;
- }
-
- if (request->zret != Z_STREAM_END) {
- ret = error("File %s (%s) corrupt\n", hex, request->url);
- release_request(request);
- return ret;
- }
-
- if (memcmp(request->sha1, request->real_sha1, 20)) {
- release_request(request);
- return error("File %s has bad hash\n", hex);
- }
-
- if (request->rename < 0) {
+ obj_req->errorstr, obj_req->curl_result,
+ obj_req->http_code, hex);
+ } else if (obj_req->zret != Z_STREAM_END) {
+ ret = error("File %s (%s) corrupt\n", hex, obj_req->url);
+ } else if (memcmp(obj_req->sha1, obj_req->real_sha1, 20)) {
+ ret = error("File %s has bad hash\n", hex);
+ } else if (obj_req->rename < 0) {
ret = error("unable to write sha1 filename %s: %s",
- request->filename,
- strerror(request->rename));
- release_request(request);
- return ret;
+ obj_req->filename,
+ strerror(obj_req->rename));
}
- release_request(request);
- return 0;
+ release_object_request(obj_req);
+ return ret;
}
int fetch(unsigned char *sha1)
@@ -1303,10 +920,6 @@ int main(int argc, char **argv)
char *commit_id;
char *url;
int arg = 1;
- struct active_request_slot *slot;
- char *low_speed_limit;
- char *low_speed_time;
- char *wait_url;
int rc = 0;
while (arg < argc && argv[arg][0] == '-') {
@@ -1335,58 +948,9 @@ int main(int argc, char **argv)
commit_id = argv[arg];
url = argv[arg + 1];
- 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);
+ http_init();
- 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
-
- pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache");
no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
- no_range_header = curl_slist_append(no_range_header, "Range:");
-
-#ifndef NO_CURL_EASY_DUPHANDLE
- curl_default = get_curl_handle();
-#endif
alt = xmalloc(sizeof(*alt));
alt->base = url;
@@ -1397,30 +961,9 @@ int main(int argc, char **argv)
if (pull(commit_id))
rc = 1;
- curl_slist_free_all(pragma_header);
curl_slist_free_all(no_pragma_header);
- curl_slist_free_all(no_range_header);
-#ifndef NO_CURL_EASY_DUPHANDLE
- curl_easy_cleanup(curl_default);
-#endif
- slot = active_queue_head;
- while (slot != NULL) {
- if (slot->in_use) {
- if (get_verbosely) {
- curl_easy_getinfo(slot->curl,
- CURLINFO_EFFECTIVE_URL,
- &wait_url);
- fprintf(stderr, "Waiting for %s\n", wait_url);
- }
- run_active_slot(slot);
- }
- if (slot->curl != NULL)
- curl_easy_cleanup(slot->curl);
- slot = slot->next;
- }
-#ifdef USE_CURL_MULTI
- curl_multi_cleanup(curlm);
-#endif
- curl_global_cleanup();
+
+ http_cleanup();
+
return rc;
}
diff --git a/http-push.c b/http-push.c
index 8866189..76c7886 100644
--- a/http-push.c
+++ b/http-push.c
@@ -4,30 +4,13 @@
#include "fetch.h"
#include "tag.h"
#include "blob.h"
+#include "http.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
-
#ifndef XML_STATUS_OK
enum XML_Status {
XML_STATUS_OK = 1,
@@ -39,47 +22,45 @@ enum XML_Status {
#define RANGE_HEADER_SIZE 30
-/* DAV method names and request body templates */
+/* DAV methods */
#define DAV_LOCK "LOCK"
#define DAV_MKCOL "MKCOL"
#define DAV_MOVE "MOVE"
#define DAV_PROPFIND "PROPFIND"
#define DAV_PUT "PUT"
#define DAV_UNLOCK "UNLOCK"
+
+/* DAV lock flags */
+#define DAV_PROP_LOCKWR (1u << 0)
+#define DAV_PROP_LOCKEX (1u << 1)
+#define DAV_LOCK_OK (1u << 2)
+
+/* DAV XML properties */
+#define DAV_CTX_LOCKENTRY ".multistatus.response.propstat.prop.supportedlock.lockentry"
+#define DAV_CTX_LOCKTYPE_WRITE ".multistatus.response.propstat.prop.supportedlock.lockentry.locktype.write"
+#define DAV_CTX_LOCKTYPE_EXCLUSIVE ".multistatus.response.propstat.prop.supportedlock.lockentry.lockscope.exclusive"
+#define DAV_ACTIVELOCK_OWNER ".prop.lockdiscovery.activelock.owner.href"
+#define DAV_ACTIVELOCK_TIMEOUT ".prop.lockdiscovery.activelock.timeout"
+#define DAV_ACTIVELOCK_TOKEN ".prop.lockdiscovery.activelock.locktoken.href"
+
+/* DAV request body templates */
#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;
static char remote_dir_exists[256];
-#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;
@@ -122,40 +103,19 @@ struct transfer_request
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 xml_ctx
+{
+ char *name;
+ int len;
+ char *cdata;
+ void (*userFunc)(struct xml_ctx *ctx, int tag_closed);
+ void *userData;
+};
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;
@@ -164,270 +124,14 @@ struct active_lock
int refreshing;
};
-struct lockprop
-{
- int supported_lock;
- int lock_entry;
- int lock_scope;
- int lock_type;
- int lock_exclusive;
- int lock_exclusive_write;
-};
+static void finish_request(struct transfer_request *request);
-static int http_options(const char *var, const char *value)
+static void process_response(void *callback_data)
{
- if (!strcmp("http.sslverify", var)) {
- if (curl_ssl_verify == -1) {
- curl_ssl_verify = git_config_bool(var, value);
- }
- return 0;
- }
+ struct transfer_request *request =
+ (struct transfer_request *)callback_data;
- 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
+ finish_request(request);
}
static void start_check(struct transfer_request *request)
@@ -447,6 +151,8 @@ static void start_check(struct transfer_request *request)
strcpy(posn, hex + 2);
slot = get_active_slot();
+ slot->callback_func = process_response;
+ slot->callback_data = request;
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);
@@ -457,6 +163,7 @@ static void start_check(struct transfer_request *request)
} else {
request->state = ABORTED;
free(request->url);
+ request->url = NULL;
}
}
@@ -476,6 +183,8 @@ static void start_mkcol(struct transfer_request *request)
strcpy(posn, "/");
slot = get_active_slot();
+ slot->callback_func = process_response;
+ slot->callback_data = request;
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);
@@ -488,6 +197,7 @@ static void start_mkcol(struct transfer_request *request)
} else {
request->state = ABORTED;
free(request->url);
+ request->url = NULL;
}
}
@@ -534,8 +244,6 @@ static void start_put(struct transfer_request *request)
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);
@@ -553,6 +261,8 @@ static void start_put(struct transfer_request *request)
strcpy(posn, request->lock->token);
slot = get_active_slot();
+ slot->callback_func = process_response;
+ slot->callback_data = request;
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);
@@ -569,6 +279,7 @@ static void start_put(struct transfer_request *request)
} else {
request->state = ABORTED;
free(request->url);
+ request->url = NULL;
}
}
@@ -578,6 +289,8 @@ static void start_move(struct transfer_request *request)
struct curl_slist *dav_headers = NULL;
slot = get_active_slot();
+ slot->callback_func = process_response;
+ slot->callback_data = request;
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);
@@ -592,6 +305,7 @@ static void start_move(struct transfer_request *request)
} else {
request->state = ABORTED;
free(request->url);
+ request->url = NULL;
}
}
@@ -656,6 +370,13 @@ static void finish_request(struct transfer_request *request)
if (request->headers != NULL)
curl_slist_free_all(request->headers);
+
+ /* URL is reused for MOVE after PUT */
+ if (request->state != RUN_PUT) {
+ free(request->url);
+ request->url = NULL;
+ }
+
if (request->state == RUN_HEAD) {
if (request->http_code == 404) {
request->state = NEED_PUSH;
@@ -721,52 +442,12 @@ static void release_request(struct transfer_request *request)
entry->next = entry->next->next;
}
- free(request->url);
+ if (request->url != NULL)
+ free(request->url);
free(request);
}
-#ifdef USE_CURL_MULTI
-static 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) {
- int curl_result = curl_message->data.result;
- curl_multi_remove_handle(curlm, slot->curl);
- active_requests--;
- slot->done = 1;
- slot->in_use = 0;
- slot->curl_result = curl_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);
- }
-}
-
-static void process_request_queue(void)
+void fill_active_slots(void)
{
struct transfer_request *request = request_queue_head;
struct active_request_slot *slot = active_queue_head;
@@ -797,20 +478,6 @@ static void process_request_queue(void)
slot = slot->next;
}
}
-#endif
-
-static 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;
- }
-}
static void add_request(unsigned char *sha1, struct active_lock *lock)
{
@@ -834,10 +501,9 @@ static void add_request(unsigned char *sha1, struct active_lock *lock)
request->state = NEED_CHECK;
request->next = request_queue_head;
request_queue_head = request;
-#ifdef USE_CURL_MULTI
- process_request_queue();
- process_curl_messages();
-#endif
+
+ fill_active_slots();
+ step_active_slots();
}
static int fetch_index(unsigned char *sha1)
@@ -917,6 +583,7 @@ static int fetch_index(unsigned char *sha1)
}
} else {
free(url);
+ fclose(indexfile);
return error("Unable to start request");
}
@@ -963,8 +630,7 @@ static int fetch_indices(void)
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_WRITEFUNCTION, fwrite_buffer);
curl_easy_setopt(slot->curl, CURLOPT_URL, url);
curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
if (start_active_slot(slot)) {
@@ -1068,8 +734,7 @@ int fetch_ref(char *ref, unsigned char *sha1)
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_WRITEFUNCTION, fwrite_buffer);
curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
curl_easy_setopt(slot->curl, CURLOPT_URL, url);
if (start_active_slot(slot)) {
@@ -1086,107 +751,101 @@ int fetch_ref(char *ref, unsigned char *sha1)
return 0;
}
-static void
-start_activelock_element(void *userData, const char *name, const char **atts)
+static void handle_lockprop_ctx(struct xml_ctx *ctx, int tag_closed)
{
- 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;
+ int *lock_flags = (int *)ctx->userData;
+
+ if (tag_closed) {
+ if (!strcmp(ctx->name, DAV_CTX_LOCKENTRY)) {
+ if ((*lock_flags & DAV_PROP_LOCKEX) &&
+ (*lock_flags & DAV_PROP_LOCKWR)) {
+ *lock_flags |= DAV_LOCK_OK;
+ }
+ *lock_flags &= DAV_LOCK_OK;
+ } else if (!strcmp(ctx->name, DAV_CTX_LOCKTYPE_WRITE)) {
+ *lock_flags |= DAV_PROP_LOCKWR;
+ } else if (!strcmp(ctx->name, DAV_CTX_LOCKTYPE_EXCLUSIVE)) {
+ *lock_flags |= DAV_PROP_LOCKEX;
+ }
+ }
}
-static void
-end_activelock_element(void *userData, const char *name)
+static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed)
{
- 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;
+ struct active_lock *lock = (struct active_lock *)ctx->userData;
+
+ if (tag_closed && ctx->cdata) {
+ if (!strcmp(ctx->name, DAV_ACTIVELOCK_OWNER)) {
+ lock->owner = xmalloc(strlen(ctx->cdata) + 1);
+ strcpy(lock->owner, ctx->cdata);
+ } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TIMEOUT)) {
+ if (!strncmp(ctx->cdata, "Second-", 7))
+ lock->timeout =
+ strtol(ctx->cdata + 7, NULL, 10);
+ } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TOKEN)) {
+ if (!strncmp(ctx->cdata, "opaquelocktoken:", 16)) {
+ lock->token = xmalloc(strlen(ctx->cdata - 15));
+ strcpy(lock->token, ctx->cdata + 16);
+ }
+ }
}
}
static void
-activelock_cdata(void *userData, const XML_Char *s, int len)
+xml_start_tag(void *userData, const char *name, const char **atts)
{
- 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);
+ struct xml_ctx *ctx = (struct xml_ctx *)userData;
+ const char *c = index(name, ':');
+ int new_len;
+
+ if (c == NULL)
+ c = name;
+ else
+ c++;
+
+ new_len = strlen(ctx->name) + strlen(c) + 2;
+
+ if (new_len > ctx->len) {
+ ctx->name = xrealloc(ctx->name, new_len);
+ ctx->len = new_len;
+ }
+ strcat(ctx->name, ".");
+ strcat(ctx->name, c);
+
+ if (ctx->cdata) {
+ free(ctx->cdata);
+ ctx->cdata = NULL;
}
- free(this);
+ ctx->userFunc(ctx, 0);
}
static void
-start_lockprop_element(void *userData, const char *name, const char **atts)
+xml_end_tag(void *userData, const char *name)
{
- struct lockprop *prop = (struct lockprop *)userData;
+ struct xml_ctx *ctx = (struct xml_ctx *)userData;
+ const char *c = index(name, ':');
+ char *ep;
- 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;
- }
+ ctx->userFunc(ctx, 1);
+
+ if (c == NULL)
+ c = name;
+ else
+ c++;
+
+ ep = ctx->name + strlen(ctx->name) - strlen(c) - 1;
+ *ep = 0;
}
static void
-end_lockprop_element(void *userData, const char *name)
+xml_cdata(void *userData, const XML_Char *s, int len)
{
- 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 xml_ctx *ctx = (struct xml_ctx *)userData;
+ if (ctx->cdata)
+ free(ctx->cdata);
+ ctx->cdata = xcalloc(len+1, 1);
+ strncpy(ctx->cdata, s, len);
}
static struct active_lock *lock_remote(char *file, long timeout)
@@ -1199,10 +858,11 @@ static struct active_lock *lock_remote(char *file, long timeout)
char *url;
char *ep;
char timeout_header[25];
- struct active_lock *new_lock;
+ struct active_lock *new_lock = NULL;
XML_Parser parser = XML_ParserCreate(NULL);
enum XML_Status result;
struct curl_slist *dav_headers = NULL;
+ struct xml_ctx ctx;
url = xmalloc(strlen(remote->url) + strlen(file) + 1);
sprintf(url, "%s%s", remote->url, file);
@@ -1246,12 +906,6 @@ static struct active_lock *lock_remote(char *file, long timeout)
in_buffer.posn = 0;
in_buffer.buffer = in_data;
- new_lock = xcalloc(1, 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");
@@ -1261,47 +915,47 @@ static struct active_lock *lock_remote(char *file, long timeout)
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_WRITEFUNCTION, fwrite_buffer);
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);
+ new_lock = xcalloc(1, sizeof(*new_lock));
+ new_lock->owner = NULL;
+ new_lock->token = NULL;
+ new_lock->timeout = -1;
+ new_lock->refreshing = 0;
+
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;
+ if (slot->curl_result == CURLE_OK) {
+ ctx.name = xcalloc(10, 1);
+ ctx.len = 0;
+ ctx.cdata = NULL;
+ ctx.userFunc = handle_new_lock_ctx;
+ ctx.userData = new_lock;
+ XML_SetUserData(parser, &ctx);
+ XML_SetElementHandler(parser, xml_start_tag,
+ xml_end_tag);
+ XML_SetCharacterDataHandler(parser, xml_cdata);
+ result = XML_Parse(parser, in_buffer.buffer,
+ in_buffer.posn, 1);
+ free(ctx.name);
+ if (result != XML_STATUS_OK) {
+ fprintf(stderr, "XML error: %s\n",
+ XML_ErrorString(
+ XML_GetErrorCode(parser)));
+ new_lock->timeout = -1;
+ }
}
} else {
- free(new_lock);
- free(url);
- free(out_data);
- free(in_data);
fprintf(stderr, "Unable to start request\n");
- return NULL;
}
+ curl_slist_free_all(dav_headers);
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)
@@ -1310,11 +964,12 @@ static struct active_lock *lock_remote(char *file, long timeout)
free(new_lock->owner);
free(url);
free(new_lock);
- return NULL;
+ new_lock = NULL;
+ } else {
+ new_lock->url = url;
+ new_lock->start_time = time(NULL);
}
- new_lock->url = url;
- new_lock->start_time = time(NULL);
return new_lock;
}
@@ -1353,13 +1008,15 @@ static int unlock_remote(struct active_lock *lock)
if (lock->owner != NULL)
free(lock->owner);
free(lock->url);
+/* Freeing the token causes a segfault...
free(lock->token);
+*/
free(lock);
return rc;
}
-static int check_locking(void)
+static int locking_available(void)
{
struct active_request_slot *slot;
struct buffer in_buffer;
@@ -1368,8 +1025,9 @@ static int check_locking(void)
char *out_data;
XML_Parser parser = XML_ParserCreate(NULL);
enum XML_Status result;
- struct lockprop supported_lock;
struct curl_slist *dav_headers = NULL;
+ struct xml_ctx ctx;
+ int lock_flags = 0;
out_buffer.size = strlen(PROPFIND_REQUEST) + strlen(remote->url) - 2;
out_data = xmalloc(out_buffer.size + 1);
@@ -1390,8 +1048,7 @@ static int check_locking(void)
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_WRITEFUNCTION, fwrite_buffer);
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);
@@ -1399,30 +1056,35 @@ static int check_locking(void)
if (start_active_slot(slot)) {
run_active_slot(slot);
- free(out_data);
- if (slot->curl_result != CURLE_OK) {
- free(in_buffer.buffer);
- return -1;
+ if (slot->curl_result == CURLE_OK) {
+ ctx.name = xcalloc(10, 1);
+ ctx.len = 0;
+ ctx.cdata = NULL;
+ ctx.userFunc = handle_lockprop_ctx;
+ ctx.userData = &lock_flags;
+ XML_SetUserData(parser, &ctx);
+ XML_SetElementHandler(parser, xml_start_tag,
+ xml_end_tag);
+ result = XML_Parse(parser, in_buffer.buffer,
+ in_buffer.posn, 1);
+ free(ctx.name);
+
+ if (result != XML_STATUS_OK) {
+ fprintf(stderr, "XML error: %s\n",
+ XML_ErrorString(
+ XML_GetErrorCode(parser)));
+ lock_flags = 0;
+ }
}
-
- 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");
+ fprintf(stderr, "Unable to start request\n");
}
- if (supported_lock.lock_exclusive_write)
- return 0;
- else
- return 1;
+ free(out_data);
+ free(in_buffer.buffer);
+ curl_slist_free_all(dav_headers);
+
+ return lock_flags;
}
static int is_ancestor(unsigned char *sha1, struct commit *commit)
@@ -1560,8 +1222,6 @@ static int update_remote(unsigned char *sha1, struct active_lock *lock)
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;
@@ -1576,8 +1236,6 @@ int main(int argc, char **argv)
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;
@@ -1617,50 +1275,7 @@ int main(int argc, char **argv)
memset(remote_dir_exists, 0, 256);
- 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
+ http_init();
no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
default_headers = curl_slist_append(default_headers, "Range:");
@@ -1669,12 +1284,8 @@ int main(int argc, char **argv)
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) {
+ if (!locking_available()) {
fprintf(stderr, "Error: no DAV locking support on remote repo %s\n", remote->url);
rc = 1;
goto cleanup;
@@ -1766,13 +1377,13 @@ int main(int argc, char **argv)
fetch_indices();
get_delta(push_all ? NULL : remote_sha1,
local_object, remote_lock);
- process_waiting_requests();
+ finish_all_active_slots();
/* 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();
+ fill_active_slots();
+ finish_all_active_slots();
/* Update the remote branch if all went well */
if (do_remote_update) {
@@ -1802,14 +1413,7 @@ int main(int argc, char **argv)
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;
- }
+ http_cleanup();
request = request_queue_head;
while (request != NULL) {
@@ -1818,12 +1422,5 @@ int main(int argc, char **argv)
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;
}
diff --git a/http.c b/http.c
new file mode 100644
index 0000000..75e6717
--- /dev/null
+++ b/http.c
@@ -0,0 +1,442 @@
+#include "http.h"
+
+int data_received;
+int active_requests = 0;
+
+#ifdef USE_CURL_MULTI
+int max_requests = -1;
+CURLM *curlm;
+#endif
+#ifndef NO_CURL_EASY_DUPHANDLE
+CURL *curl_default;
+#endif
+char curl_errorstr[CURL_ERROR_SIZE];
+
+int curl_ssl_verify = -1;
+char *ssl_cert = NULL;
+#if LIBCURL_VERSION_NUM >= 0x070902
+char *ssl_key = NULL;
+#endif
+#if LIBCURL_VERSION_NUM >= 0x070908
+char *ssl_capath = NULL;
+#endif
+char *ssl_cainfo = NULL;
+long curl_low_speed_limit = -1;
+long curl_low_speed_time = -1;
+
+struct curl_slist *pragma_header;
+struct curl_slist *no_range_header;
+
+struct active_request_slot *active_queue_head = NULL;
+
+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;
+}
+
+size_t fwrite_buffer(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;
+}
+
+size_t fwrite_null(const void *ptr, size_t eltsize,
+ size_t nmemb, struct buffer *buffer)
+{
+ data_received++;
+ return eltsize * nmemb;
+}
+
+static void finish_active_slot(struct active_request_slot *slot);
+
+#ifdef USE_CURL_MULTI
+static void process_curl_messages(void)
+{
+ int num_messages;
+ struct active_request_slot *slot;
+ CURLMsg *curl_message = curl_multi_info_read(curlm, &num_messages);
+
+ 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)
+ slot = slot->next;
+ if (slot != NULL) {
+ curl_multi_remove_handle(curlm, slot->curl);
+ slot->curl_result = curl_result;
+ finish_active_slot(slot);
+ } 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);
+ }
+}
+#endif
+
+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 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);
+ }
+
+ curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1);
+
+ return result;
+}
+
+void http_init(void)
+{
+ char *low_speed_limit;
+ char *low_speed_time;
+
+ curl_global_init(CURL_GLOBAL_ALL);
+
+ pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache");
+ no_range_header = curl_slist_append(no_range_header, "Range:");
+
+#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");
+ exit(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
+
+#ifndef NO_CURL_EASY_DUPHANDLE
+ curl_default = get_curl_handle();
+#endif
+}
+
+void http_cleanup(void)
+{
+ struct active_request_slot *slot = active_queue_head;
+#ifdef USE_CURL_MULTI
+ char *wait_url;
+#endif
+
+ while (slot != NULL) {
+#ifdef USE_CURL_MULTI
+ if (slot->in_use) {
+ curl_easy_getinfo(slot->curl,
+ CURLINFO_EFFECTIVE_URL,
+ &wait_url);
+ fprintf(stderr, "Waiting for %s\n", wait_url);
+ run_active_slot(slot);
+ }
+#endif
+ if (slot->curl != NULL)
+ curl_easy_cleanup(slot->curl);
+ slot = slot->next;
+ }
+
+#ifndef NO_CURL_EASY_DUPHANDLE
+ curl_easy_cleanup(curl_default);
+#endif
+
+#ifdef USE_CURL_MULTI
+ curl_multi_cleanup(curlm);
+#endif
+ curl_global_cleanup();
+
+}
+
+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->local = NULL;
+ slot->callback_data = NULL;
+ slot->callback_func = NULL;
+ curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);
+ curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_range_header);
+ curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);
+
+ return slot;
+}
+
+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;
+}
+
+#ifdef USE_CURL_MULTI
+void step_active_slots(void)
+{
+ int num_transfers;
+ CURLMcode curlm_result;
+
+ do {
+ curlm_result = curl_multi_perform(curlm, &num_transfers);
+ } while (curlm_result == CURLM_CALL_MULTI_PERFORM);
+ if (num_transfers < active_requests) {
+ process_curl_messages();
+ fill_active_slots();
+ }
+}
+#endif
+
+void run_active_slot(struct active_request_slot *slot)
+{
+#ifdef USE_CURL_MULTI
+ long last_pos = 0;
+ long current_pos;
+ fd_set readfds;
+ fd_set writefds;
+ fd_set excfds;
+ int max_fd;
+ struct timeval select_timeout;
+
+ while (slot->in_use) {
+ data_received = 0;
+ step_active_slots();
+
+ if (!data_received && slot->local != NULL) {
+ current_pos = ftell(slot->local);
+ if (current_pos > last_pos)
+ data_received++;
+ last_pos = current_pos;
+ }
+
+ if (slot->in_use && !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
+ while (slot->in_use) {
+ slot->curl_result = curl_easy_perform(slot->curl);
+ finish_active_slot(slot);
+ }
+#endif
+}
+
+static void finish_active_slot(struct active_request_slot *slot)
+{
+ active_requests--;
+ slot->in_use = 0;
+ curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code);
+
+ /* Run callback if appropriate */
+ if (slot->callback_func != NULL) {
+ slot->callback_func(slot->callback_data);
+ }
+}
+
+void finish_all_active_slots(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;
+ }
+}
diff --git a/http.h b/http.h
new file mode 100644
index 0000000..ed4ea33
--- /dev/null
+++ b/http.h
@@ -0,0 +1,95 @@
+#ifndef HTTP_H
+#define HTTP_H
+
+#include "cache.h"
+
+#include <curl/curl.h>
+#include <curl/easy.h>
+
+#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
+
+struct active_request_slot
+{
+ CURL *curl;
+ FILE *local;
+ int in_use;
+ CURLcode curl_result;
+ long http_code;
+ void *callback_data;
+ void (*callback_func)(void *data);
+ struct active_request_slot *next;
+};
+
+struct buffer
+{
+ size_t posn;
+ size_t size;
+ void *buffer;
+};
+
+/* Curl request read/write callbacks */
+extern size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb,
+ struct buffer *buffer);
+extern size_t fwrite_buffer(const void *ptr, size_t eltsize,
+ size_t nmemb, struct buffer *buffer);
+extern size_t fwrite_null(const void *ptr, size_t eltsize,
+ size_t nmemb, struct buffer *buffer);
+
+/* Slot lifecycle functions */
+extern struct active_request_slot *get_active_slot(void);
+extern int start_active_slot(struct active_request_slot *slot);
+extern void run_active_slot(struct active_request_slot *slot);
+extern void finish_all_active_slots(void);
+
+#ifdef USE_CURL_MULTI
+extern void fill_active_slots(void);
+extern void step_active_slots(void);
+#endif
+
+extern void http_init(void);
+extern void http_cleanup(void);
+
+extern int data_received;
+extern int active_requests;
+
+#ifdef USE_CURL_MULTI
+extern int max_requests;
+extern CURLM *curlm;
+#endif
+#ifndef NO_CURL_EASY_DUPHANDLE
+extern CURL *curl_default;
+#endif
+extern char curl_errorstr[CURL_ERROR_SIZE];
+
+extern int curl_ssl_verify;
+extern char *ssl_cert;
+#if LIBCURL_VERSION_NUM >= 0x070902
+extern char *ssl_key;
+#endif
+#if LIBCURL_VERSION_NUM >= 0x070908
+extern char *ssl_capath;
+#endif
+extern char *ssl_cainfo;
+extern long curl_low_speed_limit;
+extern long curl_low_speed_time;
+
+extern struct curl_slist *pragma_header;
+extern struct curl_slist *no_range_header;
+
+extern struct active_request_slot *active_queue_head;
+
+#endif /* HTTP_H */
diff --git a/ident.c b/ident.c
index bc89e1d..ac1c27f 100644
--- a/ident.c
+++ b/ident.c
@@ -156,7 +156,8 @@ static int copy(char *buf, int size, int offset, const char *src)
return offset;
}
-char *get_ident(const char *name, const char *email, const char *date_str)
+static const char *get_ident(const char *name, const char *email,
+ const char *date_str)
{
static char buffer[1000];
char date[50];
@@ -181,12 +182,16 @@ char *get_ident(const char *name, const char *email, const char *date_str)
return buffer;
}
-char *git_author_info(void)
+const char *git_author_info(void)
{
- return get_ident(getenv("GIT_AUTHOR_NAME"), getenv("GIT_AUTHOR_EMAIL"), getenv("GIT_AUTHOR_DATE"));
+ return get_ident(getenv("GIT_AUTHOR_NAME"),
+ getenv("GIT_AUTHOR_EMAIL"),
+ getenv("GIT_AUTHOR_DATE"));
}
-char *git_committer_info(void)
+const char *git_committer_info(void)
{
- return get_ident(getenv("GIT_COMMITTER_NAME"), getenv("GIT_COMMITTER_EMAIL"), getenv("GIT_COMMITTER_DATE"));
+ return get_ident(getenv("GIT_COMMITTER_NAME"),
+ getenv("GIT_COMMITTER_EMAIL"),
+ getenv("GIT_COMMITTER_DATE"));
}
diff --git a/name-rev.c b/name-rev.c
index 59194f1..817e36b 100644
--- a/name-rev.c
+++ b/name-rev.c
@@ -230,8 +230,6 @@ int main(int argc, char **argv)
fwrite(p_start, p - p_start, 1, stdout);
}
} else if (all) {
- extern struct object **objs;
- extern int nr_objs;
int i;
for (i = 0; i < nr_objs; i++)
diff --git a/pack-objects.c b/pack-objects.c
index 4e941e7..8864a31 100644
--- a/pack-objects.c
+++ b/pack-objects.c
@@ -524,7 +524,7 @@ int main(int argc, char **argv)
unsigned char sha1[20];
if (get_sha1_hex(line, sha1))
- die("expected sha1, got garbage");
+ die("expected sha1, got garbage:\n %s", line);
hash = 0;
p = line+40;
while (*p) {
diff --git a/pack-redundant.c b/pack-redundant.c
index fb6cb48..793fa08 100644
--- a/pack-redundant.c
+++ b/pack-redundant.c
@@ -11,19 +11,19 @@
static const char pack_redundant_usage[] =
"git-pack-redundant [ --verbose ] [ --alt-odb ] < --all | <.pack filename> ...>";
-int load_all_packs = 0, verbose = 0, alt_odb = 0;
+static int load_all_packs = 0, verbose = 0, alt_odb = 0;
struct llist_item {
struct llist_item *next;
- char *sha1;
+ unsigned char *sha1;
};
-struct llist {
+static struct llist {
struct llist_item *front;
struct llist_item *back;
size_t size;
} *all_objects; /* all objects which must be present in local packfiles */
-struct pack_list {
+static struct pack_list {
struct pack_list *next;
struct packed_git *pack;
struct llist *unique_objects;
@@ -36,23 +36,43 @@ struct pll {
size_t pl_size;
};
-inline void llist_free(struct llist *list)
+static struct llist_item *free_nodes = NULL;
+
+static inline struct llist_item *llist_item_get()
+{
+ struct llist_item *new;
+ if ( free_nodes ) {
+ new = free_nodes;
+ free_nodes = free_nodes->next;
+ } else
+ new = xmalloc(sizeof(struct llist_item));
+
+ return new;
+}
+
+static inline void llist_item_put(struct llist_item *item)
+{
+ item->next = free_nodes;
+ free_nodes = item;
+}
+
+static void llist_free(struct llist *list)
{
while((list->back = list->front)) {
list->front = list->front->next;
- free(list->back);
+ llist_item_put(list->back);
}
free(list);
}
-inline void llist_init(struct llist **list)
+static inline void llist_init(struct llist **list)
{
*list = xmalloc(sizeof(struct llist));
(*list)->front = (*list)->back = NULL;
(*list)->size = 0;
}
-struct llist * llist_copy(struct llist *list)
+static struct llist * llist_copy(struct llist *list)
{
struct llist *ret;
struct llist_item *new, *old, *prev;
@@ -62,13 +82,13 @@ struct llist * llist_copy(struct llist *list)
if ((ret->size = list->size) == 0)
return ret;
- new = ret->front = xmalloc(sizeof(struct llist_item));
+ new = ret->front = llist_item_get();
new->sha1 = list->front->sha1;
old = list->front->next;
while (old) {
prev = new;
- new = xmalloc(sizeof(struct llist_item));
+ new = llist_item_get();
prev->next = new;
new->sha1 = old->sha1;
old = old->next;
@@ -79,10 +99,11 @@ struct llist * llist_copy(struct llist *list)
return ret;
}
-inline struct llist_item * llist_insert(struct llist *list,
- struct llist_item *after, char *sha1)
+static inline struct llist_item * llist_insert(struct llist *list,
+ struct llist_item *after,
+ unsigned char *sha1)
{
- struct llist_item *new = xmalloc(sizeof(struct llist_item));
+ struct llist_item *new = llist_item_get();
new->sha1 = sha1;
new->next = NULL;
@@ -102,13 +123,12 @@ inline struct llist_item * llist_insert(struct llist *list,
return new;
}
-inline struct llist_item * llist_insert_back(struct llist *list, char *sha1)
+static inline struct llist_item *llist_insert_back(struct llist *list, unsigned char *sha1)
{
return llist_insert(list, list->back, sha1);
}
-inline struct llist_item * llist_insert_sorted_unique(struct llist *list,
- char *sha1, struct llist_item *hint)
+static inline struct llist_item *llist_insert_sorted_unique(struct llist *list, unsigned char *sha1, struct llist_item *hint)
{
struct llist_item *prev = NULL, *l;
@@ -129,8 +149,7 @@ inline struct llist_item * llist_insert_sorted_unique(struct llist *list,
}
/* returns a pointer to an item in front of sha1 */
-inline struct llist_item * llist_sorted_remove(struct llist *list, char *sha1,
- struct llist_item *hint)
+static inline struct llist_item * llist_sorted_remove(struct llist *list, const unsigned char *sha1, struct llist_item *hint)
{
struct llist_item *prev, *l;
@@ -153,7 +172,7 @@ redo_from_start:
prev->next = l->next;
if (l == list->back)
list->back = prev;
- free(l);
+ llist_item_put(l);
list->size--;
return prev;
}
@@ -164,7 +183,7 @@ redo_from_start:
}
/* computes A\B */
-void llist_sorted_difference_inplace(struct llist *A,
+static void llist_sorted_difference_inplace(struct llist *A,
struct llist *B)
{
struct llist_item *hint, *b;
@@ -178,7 +197,7 @@ void llist_sorted_difference_inplace(struct llist *A,
}
}
-inline struct pack_list * pack_list_insert(struct pack_list **pl,
+static inline struct pack_list * pack_list_insert(struct pack_list **pl,
struct pack_list *entry)
{
struct pack_list *p = xmalloc(sizeof(struct pack_list));
@@ -188,7 +207,7 @@ inline struct pack_list * pack_list_insert(struct pack_list **pl,
return p;
}
-inline size_t pack_list_size(struct pack_list *pl)
+static inline size_t pack_list_size(struct pack_list *pl)
{
size_t ret = 0;
while(pl) {
@@ -198,10 +217,11 @@ inline size_t pack_list_size(struct pack_list *pl)
return ret;
}
-struct pack_list * pack_list_difference(struct pack_list *A,
- struct pack_list *B)
+static struct pack_list * pack_list_difference(const struct pack_list *A,
+ const struct pack_list *B)
{
- struct pack_list *ret, *pl;
+ struct pack_list *ret;
+ const struct pack_list *pl;
if (A == NULL)
return NULL;
@@ -218,7 +238,7 @@ struct pack_list * pack_list_difference(struct pack_list *A,
return ret;
}
-void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
+static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
{
int p1_off, p2_off;
void *p1_base, *p2_base;
@@ -250,7 +270,7 @@ void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
}
}
-void pll_insert(struct pll **pll, struct pll **hint_table)
+static void pll_insert(struct pll **pll, struct pll **hint_table)
{
struct pll *prev;
int i = (*pll)->pl_size - 1;
@@ -276,7 +296,7 @@ void pll_insert(struct pll **pll, struct pll **hint_table)
/* all the permutations have to be free()d at the same time,
* since they refer to each other
*/
-struct pll * get_all_permutations(struct pack_list *list)
+static struct pll * get_all_permutations(struct pack_list *list)
{
struct pll *subset, *pll, *new_pll = NULL; /*silence warning*/
static struct pll **hint = NULL;
@@ -323,15 +343,14 @@ struct pll * get_all_permutations(struct pack_list *list)
return hint[0];
}
-int is_superset(struct pack_list *pl, struct llist *list)
+static int is_superset(struct pack_list *pl, struct llist *list)
{
struct llist *diff;
diff = llist_copy(list);
while (pl) {
- llist_sorted_difference_inplace(diff,
- pl->all_objects);
+ llist_sorted_difference_inplace(diff, pl->all_objects);
if (diff->size == 0) { /* we're done */
llist_free(diff);
return 1;
@@ -342,7 +361,7 @@ int is_superset(struct pack_list *pl, struct llist *list)
return 0;
}
-size_t sizeof_union(struct packed_git *p1, struct packed_git *p2)
+static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2)
{
size_t ret = 0;
int p1_off, p2_off;
@@ -373,14 +392,14 @@ size_t sizeof_union(struct packed_git *p1, struct packed_git *p2)
}
/* another O(n^2) function ... */
-size_t get_pack_redundancy(struct pack_list *pl)
+static size_t get_pack_redundancy(struct pack_list *pl)
{
struct pack_list *subset;
+ size_t ret = 0;
if (pl == NULL)
return 0;
- size_t ret = 0;
while ((subset = pl->next)) {
while(subset) {
ret += sizeof_union(pl->pack, subset->pack);
@@ -391,7 +410,7 @@ size_t get_pack_redundancy(struct pack_list *pl)
return ret;
}
-inline size_t pack_set_bytecount(struct pack_list *pl)
+static inline size_t pack_set_bytecount(struct pack_list *pl)
{
size_t ret = 0;
while (pl) {
@@ -402,7 +421,7 @@ inline size_t pack_set_bytecount(struct pack_list *pl)
return ret;
}
-void minimize(struct pack_list **min)
+static void minimize(struct pack_list **min)
{
struct pack_list *pl, *unique = NULL,
*non_unique = NULL, *min_perm = NULL;
@@ -469,16 +488,14 @@ void minimize(struct pack_list **min)
}
}
-void load_all_objects()
+static void load_all_objects(void)
{
struct pack_list *pl = local_packs;
struct llist_item *hint, *l;
- int i;
llist_init(&all_objects);
while (pl) {
- i = 0;
hint = NULL;
l = pl->all_objects->front;
while (l) {
@@ -497,7 +514,7 @@ void load_all_objects()
}
/* this scales like O(n^2) */
-void cmp_local_packs()
+static void cmp_local_packs(void)
{
struct pack_list *subset, *pl = local_packs;
@@ -508,7 +525,7 @@ void cmp_local_packs()
}
}
-void scan_alt_odb_packs()
+static void scan_alt_odb_packs(void)
{
struct pack_list *local, *alt;
@@ -524,7 +541,7 @@ void scan_alt_odb_packs()
}
}
-struct pack_list * add_pack(struct packed_git *p)
+static struct pack_list * add_pack(struct packed_git *p)
{
struct pack_list l;
size_t off;
@@ -550,7 +567,7 @@ struct pack_list * add_pack(struct packed_git *p)
return pack_list_insert(&altodb_packs, &l);
}
-struct pack_list * add_pack_file(char *filename)
+static struct pack_list * add_pack_file(char *filename)
{
struct packed_git *p = packed_git;
@@ -565,7 +582,7 @@ struct pack_list * add_pack_file(char *filename)
die("Filename %s not found in packed_git\n", filename);
}
-void load_all()
+static void load_all(void)
{
struct packed_git *p = packed_git;
@@ -579,6 +596,9 @@ int main(int argc, char **argv)
{
int i;
struct pack_list *min, *red, *pl;
+ struct llist *ignore;
+ unsigned char *sha1;
+ char buf[42]; /* 40 byte sha1 + \n + \0 */
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
@@ -621,6 +641,23 @@ int main(int argc, char **argv)
if (alt_odb)
scan_alt_odb_packs();
+ /* ignore objects given on stdin */
+ llist_init(&ignore);
+ if (!isatty(0)) {
+ while (fgets(buf, sizeof(buf), stdin)) {
+ sha1 = xmalloc(20);
+ if (get_sha1_hex(buf, sha1))
+ die("Bad sha1 on stdin: %s", buf);
+ llist_insert_sorted_unique(ignore, sha1, NULL);
+ }
+ }
+ llist_sorted_difference_inplace(all_objects, ignore);
+ pl = local_packs;
+ while (pl) {
+ llist_sorted_difference_inplace(pl->unique_objects, ignore);
+ pl = pl->next;
+ }
+
minimize(&min);
if (verbose) {
@@ -647,6 +684,9 @@ int main(int argc, char **argv)
pl->pack->pack_name);
pl = pl->next;
}
+ if (verbose)
+ fprintf(stderr, "%luMB of redundant packs in total.\n",
+ (unsigned long)pack_set_bytecount(red)/(1024*1024));
return 0;
}
diff --git a/path.c b/path.c
index 495d17c..4d88947 100644
--- a/path.c
+++ b/path.c
@@ -11,6 +11,7 @@
* which is what it's designed for.
*/
#include "cache.h"
+#include <pwd.h>
static char pathname[PATH_MAX];
static char bad_path[] = "/bad-path/";
@@ -89,3 +90,117 @@ char *safe_strncpy(char *dest, const char *src, size_t n)
return dest;
}
+
+int validate_symref(const char *path)
+{
+ struct stat st;
+ char *buf, buffer[256];
+ int len, fd;
+
+ if (lstat(path, &st) < 0)
+ return -1;
+
+ /* Make sure it is a "refs/.." symlink */
+ if (S_ISLNK(st.st_mode)) {
+ len = readlink(path, buffer, sizeof(buffer)-1);
+ if (len >= 5 && !memcmp("refs/", buffer, 5))
+ return 0;
+ return -1;
+ }
+
+ /*
+ * Anything else, just open it and try to see if it is a symbolic ref.
+ */
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -1;
+ len = read(fd, buffer, sizeof(buffer)-1);
+ close(fd);
+
+ /*
+ * Is it a symbolic ref?
+ */
+ if (len < 4 || memcmp("ref:", buffer, 4))
+ return -1;
+ buf = buffer + 4;
+ len -= 4;
+ while (len && isspace(*buf))
+ buf++, len--;
+ if (len >= 5 && !memcmp("refs/", buf, 5))
+ return 0;
+ return -1;
+}
+
+static char *current_dir(void)
+{
+ return getcwd(pathname, sizeof(pathname));
+}
+
+static int user_chdir(char *path)
+{
+ char *dir = path;
+
+ if(*dir == '~') { /* user-relative path */
+ struct passwd *pw;
+ char *slash = strchr(dir, '/');
+
+ dir++;
+ /* '~/' and '~' (no slash) means users own home-dir */
+ if(!*dir || *dir == '/')
+ pw = getpwuid(getuid());
+ else {
+ if (slash) {
+ *slash = '\0';
+ pw = getpwnam(dir);
+ *slash = '/';
+ }
+ else
+ pw = getpwnam(dir);
+ }
+
+ /* make sure we got something back that we can chdir() to */
+ if(!pw || chdir(pw->pw_dir) < 0)
+ return -1;
+
+ if(!slash || !slash[1]) /* no path following username */
+ return 0;
+
+ dir = slash + 1;
+ }
+
+ /* ~foo/path/to/repo is now path/to/repo and we're in foo's homedir */
+ if(chdir(dir) < 0)
+ return -1;
+
+ return 0;
+}
+
+char *enter_repo(char *path, int strict)
+{
+ if(!path)
+ return NULL;
+
+ if (strict) {
+ if (chdir(path) < 0)
+ return NULL;
+ }
+ else {
+ if (!*path)
+ ; /* happy -- no chdir */
+ else if (!user_chdir(path))
+ ; /* happy -- as given */
+ else if (!user_chdir(mkpath("%s.git", path)))
+ ; /* happy -- uemacs --> uemacs.git */
+ else
+ return NULL;
+ (void)chdir(".git");
+ }
+
+ if(access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
+ validate_symref("HEAD") == 0) {
+ putenv("GIT_DIR=.");
+ return current_dir();
+ }
+
+ return NULL;
+}
diff --git a/receive-pack.c b/receive-pack.c
index 8f157bc..1873506 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -248,11 +248,11 @@ static void unpack(void)
int main(int argc, char **argv)
{
int i;
- const char *dir = NULL;
+ char *dir = NULL;
argv++;
for (i = 1; i < argc; i++) {
- const char *arg = *argv++;
+ char *arg = *argv++;
if (*arg == '-') {
/* Do flag handling here */
@@ -265,18 +265,9 @@ int main(int argc, char **argv)
if (!dir)
usage(receive_pack_usage);
- /* chdir to the directory. If that fails, try appending ".git" */
- if (chdir(dir) < 0) {
- if (chdir(mkpath("%s.git", dir)) < 0)
- die("unable to cd to %s", dir);
- }
-
- /* If we have a ".git" directory, chdir to it */
- chdir(".git");
- putenv("GIT_DIR=.");
+ if(!enter_repo(dir, 0))
+ die("'%s': unable to chdir or not a git archive", dir);
- if (access("objects", X_OK) < 0 || access("refs/heads", X_OK) < 0)
- die("%s doesn't appear to be a git directory", dir);
write_head_info();
/* EOF */
diff --git a/refs.c b/refs.c
index f324be5..ac26198 100644
--- a/refs.c
+++ b/refs.c
@@ -10,46 +10,6 @@
#define USE_SYMLINK_HEAD 1
#endif
-int validate_symref(const char *path)
-{
- struct stat st;
- char *buf, buffer[256];
- int len, fd;
-
- if (lstat(path, &st) < 0)
- return -1;
-
- /* Make sure it is a "refs/.." symlink */
- if (S_ISLNK(st.st_mode)) {
- len = readlink(path, buffer, sizeof(buffer)-1);
- if (len >= 5 && !memcmp("refs/", buffer, 5))
- return 0;
- return -1;
- }
-
- /*
- * Anything else, just open it and try to see if it is a symbolic ref.
- */
- fd = open(path, O_RDONLY);
- if (fd < 0)
- return -1;
- len = read(fd, buffer, sizeof(buffer)-1);
- close(fd);
-
- /*
- * Is it a symbolic ref?
- */
- if (len < 4 || memcmp("ref:", buffer, 4))
- return -1;
- buf = buffer + 4;
- len -= 4;
- while (len && isspace(*buf))
- buf++, len--;
- if (len >= 5 && !memcmp("refs/", buf, 5))
- return 0;
- return -1;
-}
-
const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
{
int depth = MAXDEPTH, len;
diff --git a/repo-config.c b/repo-config.c
new file mode 100644
index 0000000..b2569b7
--- /dev/null
+++ b/repo-config.c
@@ -0,0 +1,116 @@
+#include "cache.h"
+#include <regex.h>
+
+static const char git_config_set_usage[] =
+"git-repo-config [--get | --get-all | --replace-all | --unset | --unset-all] name [value [value_regex]]";
+
+static char* key = NULL;
+static char* value = NULL;
+static regex_t* regex = NULL;
+static int do_all = 0;
+static int do_not_match = 0;
+static int seen = 0;
+
+static int show_config(const char* key_, const char* value_)
+{
+ if (!strcmp(key_, key) &&
+ (regex == NULL ||
+ (do_not_match ^
+ !regexec(regex, value_, 0, NULL, 0)))) {
+ if (do_all) {
+ printf("%s\n", value_);
+ return 0;
+ }
+ if (seen > 0) {
+ fprintf(stderr, "More than one value: %s\n", value);
+ free(value);
+ }
+ value = strdup(value_);
+ seen++;
+ }
+ return 0;
+}
+
+static int get_value(const char* key_, const char* regex_)
+{
+ int i;
+
+ key = malloc(strlen(key_)+1);
+ for (i = 0; key_[i]; i++)
+ key[i] = tolower(key_[i]);
+ key[i] = 0;
+
+ if (regex_) {
+ if (regex_[0] == '!') {
+ do_not_match = 1;
+ regex_++;
+ }
+
+ regex = (regex_t*)malloc(sizeof(regex_t));
+ if (regcomp(regex, regex_, REG_EXTENDED)) {
+ fprintf(stderr, "Invalid pattern: %s\n", regex_);
+ return -1;
+ }
+ }
+
+ i = git_config(show_config);
+ if (value) {
+ printf("%s\n", value);
+ free(value);
+ }
+ free(key);
+ if (regex) {
+ regfree(regex);
+ free(regex);
+ }
+
+ if (do_all)
+ return 0;
+
+ return seen == 1 ? 0 : 1;
+}
+
+int main(int argc, const char **argv)
+{
+ setup_git_directory();
+ switch (argc) {
+ case 2:
+ return get_value(argv[1], NULL);
+ case 3:
+ if (!strcmp(argv[1], "--unset"))
+ return git_config_set(argv[2], NULL);
+ else if (!strcmp(argv[1], "--unset-all"))
+ return git_config_set_multivar(argv[2], NULL, NULL, 1);
+ else if (!strcmp(argv[1], "--get"))
+ return get_value(argv[2], NULL);
+ else if (!strcmp(argv[1], "--get-all")) {
+ do_all = 1;
+ return get_value(argv[2], NULL);
+ } else
+
+ return git_config_set(argv[1], argv[2]);
+ case 4:
+ if (!strcmp(argv[1], "--unset"))
+ return git_config_set_multivar(argv[2], NULL, argv[3], 0);
+ else if (!strcmp(argv[1], "--unset-all"))
+ return git_config_set_multivar(argv[2], NULL, argv[3], 1);
+ else if (!strcmp(argv[1], "--get"))
+ return get_value(argv[2], argv[3]);
+ else if (!strcmp(argv[1], "--get-all")) {
+ do_all = 1;
+ return get_value(argv[2], argv[3]);
+ } else if (!strcmp(argv[1], "--replace-all"))
+
+ return git_config_set_multivar(argv[2], argv[3], NULL, 1);
+ else
+
+ return git_config_set_multivar(argv[1], argv[2], argv[3], 0);
+ case 5:
+ if (!strcmp(argv[1], "--replace-all"))
+ return git_config_set_multivar(argv[2], argv[3], argv[4], 1);
+ case 1:
+ default:
+ usage(git_config_set_usage);
+ }
+ return 0;
+}
diff --git a/rev-list.c b/rev-list.c
index 6e6ffde..e17f928 100644
--- a/rev-list.c
+++ b/rev-list.c
@@ -124,8 +124,6 @@ static int filter_commit(struct commit * commit)
stop_traversal=1;
return CONTINUE;
}
- if (max_count != -1 && !max_count--)
- return STOP;
if (no_merges && (commit->parents && commit->parents->next))
return CONTINUE;
if (paths && dense) {
@@ -148,6 +146,9 @@ static int process_commit(struct commit * commit)
return CONTINUE;
}
+ if (max_count != -1 && !max_count--)
+ return STOP;
+
show_commit(commit);
return CONTINUE;
diff --git a/setup.c b/setup.c
index c487d7e..ab3c778 100644
--- a/setup.c
+++ b/setup.c
@@ -73,8 +73,8 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
}
/*
- * Test it it looks like we're at the top
- * level git directory. We want to see a
+ * Test if it looks like we're at the top level git directory.
+ * We want to see:
*
* - either a .git/objects/ directory _or_ the proper
* GIT_OBJECT_DIRECTORY environment variable
@@ -92,17 +92,43 @@ static int is_toplevel_directory(void)
return 1;
}
-const char *setup_git_directory(void)
+static const char *setup_git_directory_1(void)
{
static char cwd[PATH_MAX+1];
int len, offset;
/*
* If GIT_DIR is set explicitly, we're not going
- * to do any discovery
+ * to do any discovery, but we still do repository
+ * validation.
*/
- if (getenv(GIT_DIR_ENVIRONMENT))
+ if (getenv(GIT_DIR_ENVIRONMENT)) {
+ char path[PATH_MAX];
+ int len = strlen(getenv(GIT_DIR_ENVIRONMENT));
+ if (sizeof(path) - 40 < len)
+ die("'$%s' too big", GIT_DIR_ENVIRONMENT);
+ memcpy(path, getenv(GIT_DIR_ENVIRONMENT), len);
+
+ strcpy(path + len, "/refs");
+ if (access(path, X_OK))
+ goto bad_dir_environ;
+ strcpy(path + len, "/HEAD");
+ if (validate_symref(path))
+ goto bad_dir_environ;
+ if (getenv(DB_ENVIRONMENT)) {
+ if (access(DB_ENVIRONMENT, X_OK))
+ goto bad_dir_environ;
+ }
+ else {
+ strcpy(path + len, "/objects");
+ if (access(path, X_OK))
+ goto bad_dir_environ;
+ }
return NULL;
+ bad_dir_environ:
+ path[len] = 0;
+ die("Not a git repository: '%s'", path);
+ }
if (!getcwd(cwd, sizeof(cwd)) || cwd[0] != '/')
die("Unable to read current working directory");
@@ -127,3 +153,9 @@ const char *setup_git_directory(void)
cwd[len] = 0;
return cwd + offset;
}
+
+const char *setup_git_directory(void)
+{
+ const char *retval = setup_git_directory_1();
+ return retval;
+}
diff --git a/sha1_name.c b/sha1_name.c
index be1755a..faac158 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -236,6 +236,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
NULL
};
const char **p;
+ int found = 0;
if (len == 40 && !get_sha1_hex(str, sha1))
return 0;
@@ -246,10 +247,20 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
for (p = prefix; *p; p++) {
char *pathname = git_path("%s/%.*s", *p, len, str);
- if (!read_ref(pathname, sha1))
- return 0;
+ if (!read_ref(pathname, sha1)) {
+ /* Must be unique; i.e. when heads/foo and
+ * tags/foo are both present, reject "foo".
+ * Note that read_ref() eventually calls
+ * get_sha1_hex() which can smudge initial
+ * part of the buffer even if what is read
+ * is found to be invalid halfway.
+ */
+ if (1 < found++)
+ return -1;
+ }
}
-
+ if (found == 1)
+ return 0;
return -1;
}
diff --git a/show-branch.c b/show-branch.c
index 631336c..d8808ee 100644
--- a/show-branch.c
+++ b/show-branch.c
@@ -313,9 +313,16 @@ static int append_ref(const char *refname, const unsigned char *sha1)
static int append_head_ref(const char *refname, const unsigned char *sha1)
{
- if (strncmp(refname, "refs/heads/", 11))
+ unsigned char tmp[20];
+ int ofs = 11;
+ if (strncmp(refname, "refs/heads/", ofs))
return 0;
- return append_ref(refname + 11, sha1);
+ /* If both heads/foo and tags/foo exists, get_sha1 would
+ * get confused.
+ */
+ if (get_sha1(refname + ofs, tmp) || memcmp(tmp, sha1, 20))
+ ofs = 5;
+ return append_ref(refname + ofs, sha1);
}
static int append_tag_ref(const char *refname, const unsigned char *sha1)
@@ -470,7 +477,7 @@ int main(int ac, char **av)
if (MAX_REVS <= num_rev)
die("cannot handle more than %d revs.", MAX_REVS);
if (get_sha1(ref_name[num_rev], revkey))
- usage(show_branch_usage);
+ die("'%s' is not a valid ref.\n", ref_name[num_rev]);
commit = lookup_commit_reference(revkey);
if (!commit)
die("cannot find commit %s (%s)",
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
new file mode 100644
index 0000000..5e994ff
--- /dev/null
+++ b/t/t1300-repo-config.sh
@@ -0,0 +1,271 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Johannes Schindelin
+#
+
+test_description='Test git-repo-config in different settings'
+
+. ./test-lib.sh
+
+test -f .git/config && rm .git/config
+
+git-repo-config core.penguin "little blue"
+
+cat > expect << EOF
+#
+# This is the config file
+#
+
+[core]
+ penguin = little blue
+EOF
+
+test_expect_success 'initial' 'cmp .git/config expect'
+
+git-repo-config Core.Movie BadPhysics
+
+cat > expect << EOF
+#
+# This is the config file
+#
+
+[core]
+ penguin = little blue
+ Movie = BadPhysics
+EOF
+
+test_expect_success 'mixed case' 'cmp .git/config expect'
+
+git-repo-config Cores.WhatEver Second
+
+cat > expect << EOF
+#
+# This is the config file
+#
+
+[core]
+ penguin = little blue
+ Movie = BadPhysics
+[Cores]
+ WhatEver = Second
+EOF
+
+test_expect_success 'similar section' 'cmp .git/config expect'
+
+git-repo-config CORE.UPPERCASE true
+
+cat > expect << EOF
+#
+# This is the config file
+#
+
+[core]
+ penguin = little blue
+ Movie = BadPhysics
+ UPPERCASE = true
+[Cores]
+ WhatEver = Second
+EOF
+
+test_expect_success 'similar section' 'cmp .git/config expect'
+
+test_expect_success 'replace with non-match' \
+ 'git-repo-config core.penguin kingpin !blue'
+
+test_expect_success 'replace with non-match (actually matching)' \
+ 'git-repo-config core.penguin "very blue" !kingpin'
+
+cat > expect << EOF
+#
+# This is the config file
+#
+
+[core]
+ penguin = very blue
+ Movie = BadPhysics
+ UPPERCASE = true
+ penguin = kingpin
+[Cores]
+ WhatEver = Second
+EOF
+
+test_expect_success 'non-match result' 'cmp .git/config expect'
+
+cat > .git/config << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+ haha ="beta" # last silly comment
+haha = hello
+ haha = bello
+[nextSection] noNewline = ouch
+EOF
+
+cp .git/config .git/config2
+
+test_expect_success 'multiple unset' \
+ 'git-repo-config --unset-all beta.haha'
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+[nextSection] noNewline = ouch
+EOF
+
+test_expect_success 'multiple unset is correct' 'cmp .git/config expect'
+
+mv .git/config2 .git/config
+
+test_expect_success '--replace-all' \
+ 'git-repo-config --replace-all beta.haha gamma'
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+ haha = gamma
+[nextSection] noNewline = ouch
+EOF
+
+test_expect_success 'all replaced' 'cmp .git/config expect'
+
+git-repo-config beta.haha alpha
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+ haha = alpha
+[nextSection] noNewline = ouch
+EOF
+
+test_expect_success 'really mean test' 'cmp .git/config expect'
+
+git-repo-config nextsection.nonewline wow
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+ haha = alpha
+[nextSection]
+ nonewline = wow
+EOF
+
+test_expect_success 'really really mean test' 'cmp .git/config expect'
+
+test_expect_success 'get value' 'test alpha = $(git-repo-config beta.haha)'
+git-repo-config --unset beta.haha
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+[nextSection]
+ nonewline = wow
+EOF
+
+test_expect_success 'unset' 'cmp .git/config expect'
+
+git-repo-config nextsection.NoNewLine "wow2 for me" "for me$"
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+[nextSection]
+ nonewline = wow
+ NoNewLine = wow2 for me
+EOF
+
+test_expect_success 'multivar' 'cmp .git/config expect'
+
+test_expect_success 'non-match' \
+ 'git-repo-config --get nextsection.nonewline !for'
+
+test_expect_success 'non-match value' \
+ 'test wow = $(git-repo-config --get nextsection.nonewline !for)'
+
+test_expect_failure 'ambiguous get' \
+ 'git-repo-config --get nextsection.nonewline'
+
+test_expect_success 'get multivar' \
+ 'git-repo-config --get-all nextsection.nonewline'
+
+git-repo-config nextsection.nonewline "wow3" "wow$"
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+[nextSection]
+ nonewline = wow3
+ NoNewLine = wow2 for me
+EOF
+
+test_expect_success 'multivar replace' 'cmp .git/config expect'
+
+test_expect_failure 'ambiguous value' 'git-repo-config nextsection.nonewline'
+
+test_expect_failure 'ambiguous unset' \
+ 'git-repo-config --unset nextsection.nonewline'
+
+test_expect_failure 'invalid unset' \
+ 'git-repo-config --unset somesection.nonewline'
+
+git-repo-config --unset nextsection.nonewline "wow3$"
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+[nextSection]
+ NoNewLine = wow2 for me
+EOF
+
+test_expect_success 'multivar unset' 'cmp .git/config expect'
+
+test_expect_failure 'invalid key' 'git-repo-config inval.2key blabla'
+
+test_expect_success 'correct key' 'git-repo-config 123456.a123 987'
+
+test_expect_success 'hierarchical section' \
+ 'git-repo-config 1.2.3.alpha beta'
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+[nextSection]
+ NoNewLine = wow2 for me
+[123456]
+ a123 = 987
+[1.2.3]
+ alpha = beta
+EOF
+
+test_expect_success 'hierarchical section value' 'cmp .git/config expect'
+
+test_done
+
diff --git a/templates/hooks--update b/templates/hooks--update
index 3f38b82..6db555f 100644
--- a/templates/hooks--update
+++ b/templates/hooks--update
@@ -8,14 +8,14 @@
# (2) make this file executable by "chmod +x update".
#
-recipient="commit-list@mydomain.xz"
+recipient="commit-list@example.com"
if expr "$2" : '0*$' >/dev/null
then
echo "Created a new ref, with the following commits:"
git-rev-list --pretty "$3"
else
- $base=$(git-merge-base "$2" "$3")
+ base=$(git-merge-base "$2" "$3")
case "$base" in
"$2")
echo "New commits:"
@@ -24,8 +24,7 @@ else
echo "Rebased ref, commits from common ancestor:"
;;
esac
-fi
-git-rev-list --pretty "$3" "^$base"
+ git-rev-list --pretty "$3" "^$base"
fi |
mail -s "Changes to ref $1" "$recipient"
exit 0
diff --git a/update-index.c b/update-index.c
index 5bbc3de..11b7f6a 100644
--- a/update-index.c
+++ b/update-index.c
@@ -338,7 +338,7 @@ static void read_index_info(int line_termination)
struct strbuf buf;
strbuf_init(&buf);
while (1) {
- char *ptr;
+ char *ptr, *tab;
char *path_name;
unsigned char sha1[20];
unsigned int mode;
@@ -348,12 +348,15 @@ static void read_index_info(int line_termination)
break;
mode = strtoul(buf.buf, &ptr, 8);
- if (ptr == buf.buf || *ptr != ' ' ||
- get_sha1_hex(ptr + 1, sha1) ||
- ptr[41] != '\t')
+ if (ptr == buf.buf || *ptr != ' ')
goto bad_line;
- ptr += 42;
+ tab = strchr(ptr, '\t');
+ if (!tab || tab - ptr < 41)
+ goto bad_line;
+ if (get_sha1_hex(tab - 40, sha1) || tab[-41] != ' ')
+ goto bad_line;
+ ptr = tab + 1;
if (line_termination && ptr[0] == '"')
path_name = unquote_c_style(ptr, NULL);
diff --git a/upload-pack.c b/upload-pack.c
index be63132..1834b6b 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -248,7 +248,7 @@ static int upload_pack(void)
int main(int argc, char **argv)
{
- const char *dir;
+ char *dir;
int i;
int strict = 0;
@@ -275,18 +275,9 @@ int main(int argc, char **argv)
usage(upload_pack_usage);
dir = argv[i];
- /* chdir to the directory. If that fails, try appending ".git" */
- if (chdir(dir) < 0) {
- if (strict || chdir(mkpath("%s.git", dir)) < 0)
- die("git-upload-pack unable to chdir to %s", dir);
- }
- if (!strict)
- chdir(".git");
-
- if (access("objects", X_OK) || access("refs", X_OK))
- die("git-upload-pack: %s doesn't seem to be a git archive", dir);
+ if (!enter_repo(dir, strict))
+ die("'%s': unable to chdir or not a git archive", dir);
- putenv("GIT_DIR=.");
upload_pack();
return 0;
}
diff --git a/var.c b/var.c
index 51cf86a..59da56d 100644
--- a/var.c
+++ b/var.c
@@ -12,7 +12,7 @@ static const char var_usage[] = "git-var [-l | <variable>]";
struct git_var {
const char *name;
- char *(*read)(void);
+ const char *(*read)(void);
};
static struct git_var git_vars[] = {
{ "GIT_COMMITTER_IDENT", git_committer_info },
@@ -57,6 +57,8 @@ int main(int argc, char **argv)
if (argc != 2) {
usage(var_usage);
}
+
+ setup_git_directory();
setup_ident();
val = NULL;