Browse Source

GIT 0.99.9k

This is not 1.0rc4 yet, but to push the recent fixes out.

Signed-off-by: Junio C Hamano <junkio@cox.net>
maint v0.99.9k
Junio C Hamano 19 years ago
parent
commit
93dcab2937
  1. 1
      .gitignore
  2. 18
      Documentation/git-daemon.txt
  3. 170
      Documentation/git-repo-config.txt
  4. 19
      Documentation/git-reset.txt
  5. 3
      Documentation/git.txt
  6. 7
      Documentation/howto/rebase-from-internal-branch.txt
  7. 105
      Documentation/howto/update-hook-example.txt
  8. 30
      Documentation/pull-fetch-param.txt
  9. 5
      Documentation/tutorial.txt
  10. 63
      Makefile
  11. 8
      cache.h
  12. 340
      config.c
  13. 162
      connect.c
  14. 138
      daemon.c
  15. 6
      debian/changelog
  16. 2
      diff-files.c
  17. 2
      diff-index.c
  18. 3
      diff-stages.c
  19. 54
      diff-tree.c
  20. 37
      diff.c
  21. 1
      diff.h
  22. 3
      diffcore-rename.c
  23. 2
      git-am.sh
  24. 2
      git-applymbox.sh
  25. 2
      git-applypatch.sh
  26. 2
      git-bisect.sh
  27. 2
      git-branch.sh
  28. 3
      git-checkout.sh
  29. 2
      git-cherry.sh
  30. 7
      git-commit.sh
  31. 18
      git-count-objects.sh
  32. 2
      git-cvsimport.perl
  33. 2
      git-fetch.sh
  34. 7
      git-format-patch.sh
  35. 2
      git-lost-found.sh
  36. 1
      git-ls-remote.sh
  37. 3
      git-merge-one-file.sh
  38. 6
      git-merge-recursive.py
  39. 22
      git-merge.sh
  40. 19
      git-mv.perl
  41. 2
      git-octopus.sh
  42. 4
      git-parse-remote.sh
  43. 2
      git-prune.sh
  44. 2
      git-pull.sh
  45. 2
      git-push.sh
  46. 2
      git-rebase.sh
  47. 31
      git-repack.sh
  48. 2
      git-reset.sh
  49. 2
      git-resolve.sh
  50. 14
      git-revert.sh
  51. 20
      git-sh-setup.sh
  52. 2
      git-status.sh
  53. 2
      git-tag.sh
  54. 2
      git-verify-tag.sh
  55. 2
      git.c
  56. 207
      gitk
  57. 815
      http-fetch.c
  58. 799
      http-push.c
  59. 442
      http.c
  60. 95
      http.h
  61. 15
      ident.c
  62. 2
      name-rev.c
  63. 2
      pack-objects.c
  64. 128
      pack-redundant.c
  65. 115
      path.c
  66. 17
      receive-pack.c
  67. 40
      refs.c
  68. 116
      repo-config.c
  69. 5
      rev-list.c
  70. 42
      setup.c
  71. 17
      sha1_name.c
  72. 13
      show-branch.c
  73. 271
      t/t1300-repo-config.sh
  74. 7
      templates/hooks--update
  75. 13
      update-index.c
  76. 15
      upload-pack.c
  77. 4
      var.c

1
.gitignore vendored

@ -75,6 +75,7 @@ git-rebase
git-receive-pack git-receive-pack
git-relink git-relink
git-repack git-repack
git-repo-config
git-request-pull git-request-pull
git-reset git-reset
git-resolve git-resolve

18
Documentation/git-daemon.txt

@ -8,7 +8,7 @@ git-daemon - A really simple server for git repositories.
SYNOPSIS SYNOPSIS
-------- --------
'git-daemon' [--verbose] [--syslog] [--inetd | --port=n] [--export-all] 'git-daemon' [--verbose] [--syslog] [--inetd | --port=n] [--export-all]
[--timeout=n] [--init-timeout=n] [directory...] [--timeout=n] [--init-timeout=n] [--strict-paths] [directory...]


DESCRIPTION DESCRIPTION
----------- -----------
@ -29,9 +29,15 @@ This is ideally suited for read-only updates, ie pulling from git repositories.


OPTIONS 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:: --export-all::
Allow pulling from all directories that look like GIT repositories 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. do not have the 'git-daemon-export-ok' file.


--inetd:: --inetd::
@ -57,9 +63,15 @@ OPTIONS
--verbose:: --verbose::
Log details about the incoming connections and requested files. 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 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 Documentation
-------------- --------------

170
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

19
Documentation/git-reset.txt

@ -14,19 +14,30 @@ DESCRIPTION
Sets the current head to the specified commit and optionally resets the Sets the current head to the specified commit and optionally resets the
index and working tree to match. 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 OPTIONS
------- -------
--mixed:: --mixed::
Like --soft but reports what has not been updated. This is the Resets the index but not the working tree (ie, the changed files
default action. are preserved but not marked for commit) and reports what has not
been updated. This is the default action.


--soft:: --soft::
Does not touch the index file nor the working tree at all, but 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:: --hard::
Matches the working tree and index to that of the tree being 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-ish>::
Commit to make the current HEAD. Commit to make the current HEAD.

3
Documentation/git.txt

@ -108,6 +108,9 @@ gitlink:git-prune-packed[1]::
gitlink:git-read-tree[1]:: gitlink:git-read-tree[1]::
Reads tree information into the directory index Reads tree information into the directory index


gitlink:git-repo-config[1]::
Get and set options in .git/config.

gitlink:git-unpack-objects[1]:: gitlink:git-unpack-objects[1]::
Unpacks objects out of a packed archive. Unpacks objects out of a packed archive.



7
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 $ git checkout master
$ cd Documentation; ed git.txt ... $ cd Documentation; ed git.txt ...
$ cd ..; git add Documentation/*.txt $ cd ..; git add Documentation/*.txt
$ git commit -s -v $ git commit -s

NOTE. The -v flag to commit is a handy way to make sure that
your additions are not introducing bogusly formatted lines.


After the commit, the ancestry graph would look like this: 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. Let's go back to the earlier picture, with different labels.


You, as an individual developer, cloned upstream repository and 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 *your "master" head
upstream --> #1 --> #2 --> #3 upstream --> #1 --> #2 --> #3

105
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.








30
Documentation/pull-fetch-param.txt

@ -5,11 +5,31 @@
to name the remote repository: to name the remote repository:
+ +
=============================================================== ===============================================================
- Rsync URL: rsync://remote.machine/path/to/repo.git/ - rsync://host.xz/path/to/repo.git/
- HTTP(s) URL: http://remote.machine/path/to/repo.git/ - http://host.xz/path/to/repo.git/
- git URL: git://remote.machine/path/to/repo.git/ - https://host.xz/path/to/repo.git/
- ssh URL: remote.machine:/path/to/repo.git/ - git://host.xz/path/to/repo.git/
- Local directory: /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 In addition to the above, as a short-hand, the name of a

5
Documentation/tutorial.txt

@ -1534,7 +1534,10 @@ on that project and has an own "public repository" goes like this:
the "project lead" person does. the "project lead" person does.


3. Copy over the packed files from "project lead" public 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 4. Push into the public repository from your primary
repository. Run `git repack`, and possibly `git prune` if the repository. Run `git repack`, and possibly `git prune` if the

63
Makefile

@ -50,7 +50,7 @@
# Define USE_STDEV below if you want git to care about the underlying device # 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. # 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. # CFLAGS and LDFLAGS are for the users to override from the command line.


@ -102,6 +102,11 @@ SCRIPT_PERL = \
SCRIPT_PYTHON = \ SCRIPT_PYTHON = \
git-merge-recursive.py 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. # The ones that do not have to link with lcrypto nor lz.
SIMPLE_PROGRAMS = \ SIMPLE_PROGRAMS = \
git-get-tar-commit-id$X git-mailinfo$X git-mailsplit$X \ 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-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-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-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 # Backward compatibility -- to be removed after 1.0
PROGRAMS += git-ssh-pull$X git-ssh-push$X PROGRAMS += git-ssh-pull$X git-ssh-push$X


GIT_LIST_TWEAK = 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 = \ PYMODULES = \
gitMergeCommon.py gitMergeCommon.py


ifdef WITH_OWN_SUBPROCESS_PY ifdef WITH_OWN_SUBPROCESS_PY
PYMODULES += compat/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 endif


ifdef WITH_SEND_EMAIL ifdef WITH_SEND_EMAIL
@ -242,20 +265,13 @@ ifndef NO_CURL
CURL_LIBCURL = -lcurl CURL_LIBCURL = -lcurl
endif endif
PROGRAMS += git-http-fetch$X PROGRAMS += git-http-fetch$X
curl_check := $(shell (echo 070908; curl-config --vernum) | sort -r | sed -ne 2p)
ifeq "$(curl_check)" "070908"
ifndef NO_EXPAT ifndef NO_EXPAT
EXPAT_LIBEXPAT = -lexpat EXPAT_LIBEXPAT = -lexpat
PROGRAMS += git-http-push$X PROGRAMS += git-http-push$X
endif 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 endif


ifndef NO_OPENSSL ifndef NO_OPENSSL
@ -330,25 +346,20 @@ endif


ALL_CFLAGS += -DSHA1_HEADER=$(call shellquote,$(SHA1_HEADER)) 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 export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
### Build rules ### Build rules


all: $(PROGRAMS) $(SCRIPTS) git all: $(ALL_PROGRAMS)


all: all:
$(MAKE) -C templates $(MAKE) -C templates


# Only use $(CFLAGS). We don't need anything else. # 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)"' \ $(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 $@ rm -f $@
sed -e '1s|#!.*/sh|#!$(call shq,$(SHELL_PATH))|' \ sed -e '1s|#!.*/sh|#!$(call shq,$(SHELL_PATH))|' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \ -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
@ -387,7 +398,8 @@ $(SIMPLE_PROGRAMS) : git-%$X : %.o
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(LIB_FILE) $(SIMPLE_LIB) $(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-local-fetch$X: fetch.o
git-ssh-fetch$X: rsh.o fetch.o git-ssh-fetch$X: rsh.o fetch.o
git-ssh-upload$X: rsh.o git-ssh-upload$X: rsh.o
@ -431,9 +443,9 @@ check:


### Installation rules ### Installation rules


install: $(PROGRAMS) $(SCRIPTS) git install: all
$(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(bindir)) $(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 $(MAKE) -C templates install
$(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR)) $(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR))
$(INSTALL) $(PYMODULES) $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR)) $(INSTALL) $(PYMODULES) $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR))
@ -470,7 +482,8 @@ deb: dist
### Cleaning rules ### Cleaning rules


clean: 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 $(filter-out gitk,$(SCRIPTS))
rm -f *.spec *.pyc *.pyo rm -f *.spec *.pyc *.pyo
rm -rf $(GIT_TARNAME) rm -rf $(GIT_TARNAME)

8
cache.h

@ -203,6 +203,7 @@ int git_mkstemp(char *path, size_t n, const char *template);


int safe_create_leading_directories(char *path); int safe_create_leading_directories(char *path);
char *safe_strncpy(char *, const char *, size_t); 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 */ /* 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); 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 *); unsigned long approxidate(const char *);


extern int setup_ident(void); extern int setup_ident(void);
extern char *get_ident(const char *name, const char *email, const char *date_str); extern const char *git_author_info(void);
extern char *git_author_info(void); extern const char *git_committer_info(void);
extern char *git_committer_info(void);


static inline void *xmalloc(size_t size) 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(config_fn_t fn);
extern int git_config_int(const char *, const char *); extern int git_config_int(const char *, const char *);
extern int git_config_bool(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) #define MAX_GITNAME (1000)
extern char git_default_email[MAX_GITNAME]; extern char git_default_email[MAX_GITNAME];

340
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 "cache.h"
#include <regex.h>


#define MAXNAME (256) #define MAXNAME (256)


@ -136,7 +143,7 @@ static int get_base_var(char *name)
return -1; return -1;
if (c == ']') if (c == ']')
return baselen; return baselen;
if (!isalnum(c)) if (!isalnum(c) && c != '.')
return -1; return -1;
if (baselen > MAXNAME / 2) if (baselen > MAXNAME / 2)
return -1; return -1;
@ -229,11 +236,6 @@ int git_default_config(const char *var, const char *value)
return 0; return 0;
} }


if (!strcmp(var, "diff.renamelimit")) {
diff_rename_limit_default = git_config_int(var, value);
return 0;
}

/* Add other config variables here.. */ /* Add other config variables here.. */
return 0; return 0;
} }
@ -252,3 +254,327 @@ int git_config(config_fn_t fn)
} }
return ret; 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;
}



162
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); memset(&sa, 0, sizeof sa);
sa.sin_family = he->h_addrtype; sa.sin_family = he->h_addrtype;
sa.sin_port = htons(nport); 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) { if (connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) {
close(sockfd); close(sockfd);
@ -448,42 +448,162 @@ static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path)


#endif /* NO_IPV6 */ #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. * Yeah, yeah, fixme. Need to pass in the heads etc.
*/ */
int git_connect(int fd[2], char *url, const char *prog) int git_connect(int fd[2], char *url, const char *prog)
{ {
char command[1024]; char command[1024];
char *host, *path; char *host, *path = url;
char *colon; char *colon = NULL;
int pipefd[2][2]; int pipefd[2][2];
pid_t pid; pid_t pid;
enum protocol protocol; enum protocol protocol = PROTO_LOCAL;


host = NULL; host = strstr(url, "://");
path = url; if(host) {
colon = strchr(url, ':'); *host = '\0';
protocol = PROTO_LOCAL; protocol = get_protocol(url);
if (colon) { host += 3;
*colon = 0; path = strchr(host, '/');
}
else {
host = url; host = url;
path = colon+1; if ((colon = strchr(host, ':'))) {
protocol = PROTO_SSH; protocol = PROTO_SSH;
if (!memcmp(path, "//", 2)) { *colon = '\0';
char *slash = strchr(path + 2, '/'); path = colon + 1;
if (slash) {
int nr = slash - path - 2;
memmove(path, path+2, nr);
path[nr] = 0;
protocol = get_protocol(url);
host = path;
path = slash;
} }
} }

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 (protocol == PROTO_GIT) {
if (git_use_proxy(host))
return git_proxy_connect(fd, prog, host, path);
return git_tcp_connect(fd, prog, host, path); return git_tcp_connect(fd, prog, host, path);
}


if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0) if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0)
die("unable to create pipe pair for communication"); die("unable to create pipe pair for communication");

138
daemon.c

@ -15,10 +15,11 @@ static int verbose;


static const char daemon_usage[] = static const char daemon_usage[] =
"git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\n" "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 */ /* List of acceptable pathname prefixes */
static char **ok_paths = NULL; static char **ok_paths = NULL;
static int strict_paths = 0;


/* If this is set, git-daemon-export-ok is not required */ /* If this is set, git-daemon-export-ok is not required */
static int export_all_trees = 0; static int export_all_trees = 0;
@ -81,69 +82,52 @@ static void loginfo(const char *err, ...)
va_end(params); va_end(params);
} }


static int path_ok(const char *dir) static char *path_ok(char *dir)
{ {
const char *p = dir; char *path = enter_repo(dir, strict_paths);
char **pp;
int sl, ndot;

/* 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 */ if (!path) {
break; logerror("'%s': unable to chdir or not a git archive", dir);
} else if ( *p == '/' ) { return NULL;
/* Refuse "", "." or ".." */
if ( sl && ndot < 3 )
return 0;
sl = 1;
ndot = 0;
} else {
sl = ndot = 0;
}
p++;
} }


if ( ok_paths && *ok_paths ) { if ( ok_paths && *ok_paths ) {
int ok = 0; char **pp;
int dirlen = strlen(dir); 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++ ) { for ( pp = ok_paths ; *pp ; pp++ ) {
int len = strlen(*pp); int len = strlen(*pp);
if ( len <= dirlen && if (len <= pathlen &&
!strncmp(*pp, dir, len) && !memcmp(*pp, path, len) &&
(dir[len] == '/' || dir[len] == '\0') ) { (path[len] == '\0' ||
ok = 1; (!strict_paths && path[len] == '/')))
break; return path;
} }
} }

else {
if ( !ok ) /* be backwards compatible */
return 0; /* Path not in whitelist */ 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)) { /* Timeout as string */
errno = EACCES; char timeout_buf[64];
return -1; const char *path;
}


if ( chdir(dir) ) loginfo("Request for '%s'", dir);

if (!(path = path_ok(dir)))
return -1; return -1;


/* /*
@ -152,45 +136,17 @@ static int set_dir(const char *dir)
* We want a readable HEAD, usable "objects" directory, and * We want a readable HEAD, usable "objects" directory, and
* a "git-daemon-export-ok" flag that says that the other side * a "git-daemon-export-ok" flag that says that the other side
* is ok with us doing this. * 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)) { if (!export_all_trees && access("git-daemon-export-ok", F_OK)) {
logerror("'%s': repository not exported.", path);
errno = EACCES; errno = EACCES;
return -1; 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 * We'll ignore SIGTERM from now on, we have a
* good client. * good client.
@ -216,7 +172,7 @@ static int execute(void)
if (len && line[len-1] == '\n') if (len && line[len-1] == '\n')
line[--len] = 0; line[--len] = 0;


if (!strncmp("git-upload-pack /", line, 17)) if (!strncmp("git-upload-pack ", line, 16))
return upload(line+16); return upload(line+16);


logerror("Protocol error: '%s'", line); logerror("Protocol error: '%s'", line);
@ -510,8 +466,14 @@ static int socksetup(int port, int **socklist_p)
return 0; return 0;
} }


if (listen(sockfd, 5) < 0) {
close(sockfd);
return 0;
}

*socklist_p = xmalloc(sizeof(int)); *socklist_p = xmalloc(sizeof(int));
**socklist_p = sockfd; **socklist_p = sockfd;
return 1;
} }


#endif #endif
@ -617,6 +579,10 @@ int main(int argc, char **argv)
init_timeout = atoi(arg+15); init_timeout = atoi(arg+15);
continue; continue;
} }
if (!strcmp(arg, "--strict-paths")) {
strict_paths = 1;
continue;
}
if (!strcmp(arg, "--")) { if (!strcmp(arg, "--")) {
ok_paths = &argv[i+1]; ok_paths = &argv[i+1];
break; break;
@ -631,6 +597,14 @@ int main(int argc, char **argv)
if (log_syslog) if (log_syslog)
openlog("git-daemon", 0, LOG_DAEMON); 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) { if (inetd_mode) {
fclose(stderr); //FIXME: workaround fclose(stderr); //FIXME: workaround
return execute(); return execute();

6
debian/changelog vendored

@ -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-core (0.99.9j-0) unstable; urgency=low


* GIT 0.99.9j aka 1.0rc3 * GIT 0.99.9j aka 1.0rc3

2
diff-files.c

@ -38,7 +38,7 @@ int main(int argc, const char **argv)
const char *prefix = setup_git_directory(); const char *prefix = setup_git_directory();
int entries, i; int entries, i;


git_config(git_default_config); git_config(git_diff_config);
diff_setup(&diff_options); diff_setup(&diff_options);
while (1 < argc && argv[1][0] == '-') { while (1 < argc && argv[1][0] == '-') {
if (!strcmp(argv[1], "--")) { if (!strcmp(argv[1], "--")) {

2
diff-index.c

@ -180,7 +180,7 @@ int main(int argc, const char **argv)
int allow_options = 1; int allow_options = 1;
int i; int i;


git_config(git_default_config); git_config(git_diff_config);
diff_setup(&diff_options); diff_setup(&diff_options);
for (i = 1; i < argc; i++) { for (i = 1; i < argc; i++) {
const char *arg = argv[i]; const char *arg = argv[i];

3
diff-stages.c

@ -55,6 +55,9 @@ int main(int ac, const char **av)
{ {
int stage1, stage2; int stage1, stage2;


setup_git_directory();

git_config(git_diff_config);
read_cache(); read_cache();
diff_setup(&diff_options); diff_setup(&diff_options);
while (1 < ac && av[1][0] == '-') { while (1 < ac && av[1][0] == '-') {

54
diff-tree.c

@ -69,52 +69,50 @@ static int diff_root_tree(const unsigned char *new, const char *base)
return retval; 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]; static char this_header[16384];
int offset; int offset;
unsigned long len;


if (!verbose_header) if (!verbose_header)
return commit; return commit;


len = strlen(msg);
offset = sprintf(this_header, "%s%s (from %s)\n", header_prefix, commit, parent); 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); offset += pretty_print_commit(commit_format, msg, len, this_header + offset, sizeof(this_header) - offset);
return this_header; 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; struct commit *commit;
char *buf = read_object_with_reference(commit, "commit", &size, NULL); 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; return -1;

name[40] = 0;
if (!name) { commit = lookup_commit(sha1);
static char commit_name[60];
strcpy(commit_name, sha1_to_hex(commit));
name = commit_name;
}
/* Root commit? */ /* Root commit? */
if (show_root_diff && memcmp(buf + 46, "parent ", 7)) { if (show_root_diff && !commit->parents) {
header = generate_header(name, "root", buf, size); header = generate_header(name, "root", commit->buffer);
diff_root_tree(commit, ""); diff_root_tree(commit_sha1, "");
} }


/* More than one parent? */ /* More than one parent? */
if (ignore_merges) { if (ignore_merges && commit->parents && commit->parents->next)
if (!memcmp(buf + 46 + 48, "parent ", 7))
return 0; return 0;
}


offset = 46; for (parents = commit->parents; parents; parents = parents->next) {
while (offset + 48 < size && !memcmp(buf + offset, "parent ", 7)) { struct commit *parent = parents->item;
unsigned char parent[20]; header = generate_header(name,
if (get_sha1_hex(buf + offset + 7, parent)) sha1_to_hex(parent->object.sha1),
return -1; commit->buffer);
header = generate_header(name, sha1_to_hex(parent), buf, size); diff_tree_sha1_top(parent->object.sha1, commit_sha1, "");
diff_tree_sha1_top(parent, commit, "");
if (!header && verbose_header) { if (!header && verbose_header) {
header_prefix = "\ndiff-tree "; 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. * don't print the diffs.
*/ */
} }
offset += 48;
} }
free(buf);
return 0; return 0;
} }


@ -147,7 +143,7 @@ static int diff_tree_stdin(char *line)
return diff_tree_sha1_top(parent, commit, ""); return diff_tree_sha1_top(parent, commit, "");
} }
line[40] = 0; line[40] = 0;
return diff_tree_commit(commit, line); return diff_tree_commit(commit);
} }


static const char diff_tree_usage[] = static const char diff_tree_usage[] =
@ -164,7 +160,7 @@ int main(int argc, const char **argv)
unsigned char sha1[2][20]; unsigned char sha1[2][20];
const char *prefix = setup_git_directory(); const char *prefix = setup_git_directory();


git_config(git_default_config); git_config(git_diff_config);
nr_sha1 = 0; nr_sha1 = 0;
diff_setup(&diff_options); diff_setup(&diff_options);


@ -250,7 +246,7 @@ int main(int argc, const char **argv)
usage(diff_tree_usage); usage(diff_tree_usage);
break; break;
case 1: case 1:
diff_tree_commit(sha1[0], NULL); diff_tree_commit(sha1[0]);
break; break;
case 2: case 2:
diff_tree_sha1_top(sha1[0], sha1[1], ""); diff_tree_sha1_top(sha1[0], sha1[1], "");

37
diff.c

@ -15,6 +15,16 @@ static int use_size_cache;


int diff_rename_limit_default = -1; 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) static char *quote_one(const char *str)
{ {
int needlen; 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) 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; const char *cp = *cp_p;


cnt = num = 0; num = 0;
scale = 1;
dot = 0;
for(;;) {
ch = *cp;
if ( !dot && ch == '.' ) {
scale = 1; scale = 1;
while ('0' <= (ch = *cp) && ch <= '9') { dot = 1;
if (cnt++ < 5) { } else if ( ch == '%' ) {
/* We simply ignore more than 5 digits precision. */ scale = dot ? scale*100 : 100;
cp++; /* % is always at the end */
break;
} else if ( ch >= '0' && ch <= '9' ) {
if ( scale < 100000 ) {
scale *= 10; scale *= 10;
num = num * 10 + ch - '0'; num = (num*10) + (ch-'0');
}
} else {
break;
} }
cp++; cp++;
} }
@ -856,7 +879,7 @@ static int parse_num(const char **cp_p)
/* user says num divided by scale and we say internally that /* user says num divided by scale and we say internally that
* is MAX_SCORE * num / scale. * 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) int diff_scoreopt_parse(const char *opt)

1
diff.h

@ -77,6 +77,7 @@ extern int diff_scoreopt_parse(const char *opt);
#define DIFF_SETUP_USE_CACHE 2 #define DIFF_SETUP_USE_CACHE 2
#define DIFF_SETUP_USE_SIZE_CACHE 4 #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 void diff_setup(struct diff_options *);
extern int diff_opt_parse(struct diff_options *, const char **, int); extern int diff_opt_parse(struct diff_options *, const char **, int);
extern int diff_setup_done(struct diff_options *); extern int diff_setup_done(struct diff_options *);

3
diffcore-rename.c

@ -307,6 +307,9 @@ void diffcore_rename(struct diff_options *options)
if (rename_count == rename_dst_nr) if (rename_count == rename_dst_nr)
goto cleanup; goto cleanup;


if (minimum_score == MAX_SCORE)
goto cleanup;

num_create = (rename_dst_nr - rename_count); num_create = (rename_dst_nr - rename_count);
num_src = rename_src_nr; num_src = rename_src_nr;
mx = xmalloc(sizeof(*mx) * num_create * num_src); mx = xmalloc(sizeof(*mx) * num_create * num_src);

2
git-am.sh

@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# #
# #
. git-sh-setup || die "Not a git archive" . git-sh-setup


usage () { usage () {
echo >&2 "usage: $0 [--signoff] [--dotest=<dir>] [--utf8] [--binary] [--3way] <mbox>" echo >&2 "usage: $0 [--signoff] [--dotest=<dir>] [--utf8] [--binary] [--3way] <mbox>"

2
git-applymbox.sh

@ -18,7 +18,7 @@
## ##
## git-am is supposed to be the newer and better tool for this job. ## 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 () { usage () {
echo >&2 "applymbox [-u] [-k] [-q] [-m] (-c .dotest/<num> | mbox) [signoff]" echo >&2 "applymbox [-u] [-k] [-q] [-m] (-c .dotest/<num> | mbox) [signoff]"

2
git-applypatch.sh

@ -10,7 +10,7 @@
## $3 - "info" file with Author, email and subject ## $3 - "info" file with Author, email and subject
## $4 - optional file containing signoff to add ## $4 - optional file containing signoff to add
## ##
. git-sh-setup || die "Not a git archive." . git-sh-setup


final=.dotest/final-commit final=.dotest/final-commit
## ##

2
git-bisect.sh

@ -1,5 +1,5 @@
#!/bin/sh #!/bin/sh
. git-sh-setup || dir "Not a git archive" . git-sh-setup


usage() { usage() {
echo >&2 'usage: git bisect [start|bad|good|next|reset|visualize] echo >&2 'usage: git bisect [start|bad|good|next|reset|visualize]

2
git-branch.sh

@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh


. git-sh-setup || die "Not a git archive" . git-sh-setup


usage () { usage () {
echo >&2 "usage: $(basename $0)"' [-d <branch>] | [[-f] <branch> [start-point]] echo >&2 "usage: $(basename $0)"' [-d <branch>] | [[-f] <branch> [start-point]]

3
git-checkout.sh

@ -1,5 +1,5 @@
#!/bin/sh #!/bin/sh
. git-sh-setup || die "Not a git archive" . git-sh-setup


usage () { usage () {
die "usage: git checkout [-f] [-b <new_branch>] [<branch>] [<paths>...]" die "usage: git checkout [-f] [-b <new_branch>] [<branch>] [<paths>...]"
@ -82,7 +82,6 @@ then
# rescuing paths and is never meant to remove what # rescuing paths and is never meant to remove what
# is not in the named tree-ish. # is not in the named tree-ish.
git-ls-tree -r "$new" "$@" | git-ls-tree -r "$new" "$@" |
sed -ne 's/^\([0-7]*\) blob \(.*\)$/\1 \2/p' |
git-update-index --index-info || exit $? git-update-index --index-info || exit $?
fi fi
git-checkout-index -f -u -- "$@" git-checkout-index -f -u -- "$@"

2
git-cherry.sh

@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano. # Copyright (c) 2005 Junio C Hamano.
# #


. git-sh-setup || die "Not a git archive." . git-sh-setup


usage="usage: $0 "'[-v] <upstream> [<head>] usage="usage: $0 "'[-v] <upstream> [<head>]



7
git-commit.sh

@ -3,7 +3,7 @@
# Copyright (c) 2005 Linus Torvalds # Copyright (c) 2005 Linus Torvalds
# #


. git-sh-setup || die "Not a git archive" . git-sh-setup


usage () { usage () {
die 'git commit [-a] [-s] [-v | --no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit>] [-e] [<path>...]' die 'git commit [-a] [-s] [-v | --no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit>] [-e] [<path>...]'
@ -92,10 +92,13 @@ tt*)
esac esac


case "$all,$#" in case "$all,$#" in
t,*) t,0)
git-diff-files --name-only -z | git-diff-files --name-only -z |
git-update-index --remove -z --stdin git-update-index --remove -z --stdin
;; ;;
t,*)
die "Cannot use -a and explicit files at the same time."
;;
,0) ,0)
;; ;;
*) *)

18
git-count-objects.sh

@ -1,7 +1,25 @@
#!/bin/sh #!/bin/sh
#
# Copyright (c) 2005 Junio C Hamano
#


. git-sh-setup . 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 $(find "$GIT_DIR/objects"/?? -type f -print 2>/dev/null | wc -l) objects, \
$({ $({
echo 0 echo 0

2
git-cvsimport.perl

@ -502,7 +502,7 @@ unless($pid) {
if ($opt_P) { if ($opt_P) {
exec("cat", $opt_P); exec("cat", $opt_P);
} else { } 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"; die "Could not start cvsps: $!\n";
} }
} }

2
git-fetch.sh

@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
# #
. git-sh-setup || die "Not a git archive" . git-sh-setup
. git-parse-remote . git-parse-remote
_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' _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" _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"

7
git-format-patch.sh

@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano # Copyright (c) 2005 Junio C Hamano
# #


. git-sh-setup || die "Not a git archive." . git-sh-setup


usage () { usage () {
echo >&2 "usage: $0"' [-n] [-o dir | --stdout] [--keep-subject] [--mbox] 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 # Also, "rev1.." should mean "rev1..HEAD"; git-diff users are
# familiar with that syntax. # familiar with that syntax.


case "$#,$1" in case "$#,$1$2" in
1,?*..?*) 1,?*..?*)
# single "rev1..rev2" # single "rev1..rev2"
;; ;;
@ -131,7 +131,8 @@ do
rev2=`expr "$revpair" : '.*\.\.\(.*\)'` rev2=`expr "$revpair" : '.*\.\.\(.*\)'`
;; ;;
*) *)
usage rev1="$revpair^"
rev2="$revpair"
;; ;;
esac esac
git-rev-parse --verify "$rev1^0" >/dev/null 2>&1 || git-rev-parse --verify "$rev1^0" >/dev/null 2>&1 ||

2
git-lost-found.sh

@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh


. git-sh-setup || die "Not a git archive." . git-sh-setup


laf="$GIT_DIR/lost-found" laf="$GIT_DIR/lost-found"
rm -fr "$laf" && mkdir -p "$laf/commit" "$laf/other" || exit rm -fr "$laf" && mkdir -p "$laf/commit" "$laf/other" || exit

1
git-ls-remote.sh

@ -1,6 +1,5 @@
#!/bin/sh #!/bin/sh
# #
. git-sh-setup


usage () { usage () {
echo >&2 "usage: $0 [--heads] [--tags] <repository> <refs>..." echo >&2 "usage: $0 [--heads] [--tags] <repository> <refs>..."

3
git-merge-one-file.sh

@ -25,7 +25,8 @@ case "${1:-.}${2:-.}${3:-.}" in
echo "Removing $4" echo "Removing $4"
fi fi
if test -f "$4"; then if test -f "$4"; then
rm -f -- "$4" rm -f -- "$4" &&
rmdir -p "$(expr "$4" : '\(.*\)/')" 2>/dev/null
fi && fi &&
exec git-update-index --remove -- "$4" exec git-update-index --remove -- "$4"
;; ;;

6
git-merge-recursive.py

@ -245,7 +245,7 @@ def updateFileExt(sha, mode, path, updateCache, updateWd):


try: try:
createDir = not stat.S_ISDIR(os.lstat(p).st_mode) createDir = not stat.S_ISDIR(os.lstat(p).st_mode)
except: except OSError:
createDir = True createDir = True
if createDir: if createDir:
@ -293,6 +293,10 @@ def removeFile(clean, path):
except OSError, e: except OSError, e:
if e.errno != errno.ENOENT and e.errno != errno.EISDIR: if e.errno != errno.ENOENT and e.errno != errno.EISDIR:
raise raise
try:
os.removedirs(os.path.dirname(path))
except OSError:
pass


def uniquePath(path, branch): def uniquePath(path, branch):
def fileExists(path): def fileExists(path):

22
git-merge.sh

@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano # Copyright (c) 2005 Junio C Hamano
# #


. git-sh-setup || die "Not a git archive" . git-sh-setup


LF=' LF='
' '
@ -12,10 +12,8 @@ usage () {
die "git-merge [-n] [--no-commit] [-s <strategy>]... <merge-message> <head> <remote>+" 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' all_strategies='recursive octopus resolve stupid ours'
default_strategies='resolve octopus' default_strategies='recursive'
use_strategies= use_strategies=


dropsave() { dropsave() {
@ -90,11 +88,6 @@ do
shift shift
done done


case "$use_strategies" in
'')
use_strategies=$default_strategies
;;
esac
test "$#" -le 2 && usage ;# we need at least two heads. test "$#" -le 2 && usage ;# we need at least two heads.


merge_msg="$1" merge_msg="$1"
@ -185,6 +178,17 @@ case "$#,$common,$no_commit" in
;; ;;
esac 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 # At this point, we need a real merge. No matter what strategy
# we use, it would operate on the index, possibly affecting the # we use, it would operate on the index, possibly affecting the
# working tree, and when resolved cleanly, have the desired tree # working tree, and when resolved cleanly, have the desired tree

19
git-mv.perl

@ -103,14 +103,23 @@ while(scalar @srcArgs > 0) {
$bad = "bad source '$src'"; $bad = "bad source '$src'";
} }


$safesrc = quotemeta($src);
@srcfiles = grep /^$safesrc(\/|$)/, @allfiles;

$overwritten{$dst} = 0; $overwritten{$dst} = 0;
if (($bad eq "") && -e $dst) { if (($bad eq "") && -e $dst) {
$bad = "destination '$dst' already exists"; $bad = "destination '$dst' already exists";
if (-f $dst && $opt_f) { 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"; print "Warning: $bad; will overwrite!\n";
$bad = ""; $bad = "";
$overwritten{$dst} = 1; $overwritten{$dst} = 1;
} }
else {
$bad = "Can not overwrite '$src' with '$dst'";
}
}
} }
if (($bad eq "") && ($src eq $dstDir)) { if (($bad eq "") && ($src eq $dstDir)) {
@ -118,8 +127,6 @@ while(scalar @srcArgs > 0) {
} }


if ($bad eq "") { if ($bad eq "") {
$safesrc = quotemeta($src);
@srcfiles = grep /^$safesrc(\/|$)/, @allfiles;
if (scalar @srcfiles == 0) { if (scalar @srcfiles == 0) {
$bad = "'$src' not under version control"; $bad = "'$src' not under version control";
} }
@ -166,10 +173,12 @@ while(scalar @srcs > 0) {


push @deletedfiles, @srcfiles; push @deletedfiles, @srcfiles;
if (scalar @srcfiles == 1) { if (scalar @srcfiles == 1) {
# $dst can be a directory with 1 file inside
if ($overwritten{$dst} ==1) { if ($overwritten{$dst} ==1) {
push @changedfiles, $dst; push @changedfiles, $dstfiles[0];

} else { } else {
push @addedfiles, $dst; push @addedfiles, $dstfiles[0];
} }
} }
else { else {

2
git-octopus.sh

@ -4,7 +4,7 @@
# #
# Resolve two or more trees recorded in $GIT_DIR/FETCH_HEAD. # Resolve two or more trees recorded in $GIT_DIR/FETCH_HEAD.
# #
. git-sh-setup || die "Not a git archive" . git-sh-setup


usage () { usage () {
die "usage: git octopus" die "usage: git octopus"

4
git-parse-remote.sh

@ -1,6 +1,8 @@
#!/bin/sh #!/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 () { get_data_source () {
case "$1" in case "$1" in

2
git-prune.sh

@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh


. git-sh-setup || die "Not a git archive" . git-sh-setup


dryrun= dryrun=
echo= echo=

2
git-pull.sh

@ -4,7 +4,7 @@
# #
# Fetch one or more remote refs and merge it/them into the current HEAD. # 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 () { usage () {
echo >&2 "usage: $0"' [-n] [--no-commit] [--no-summary] [--help] echo >&2 "usage: $0"' [-n] [--no-commit] [--no-summary] [--help]

2
git-push.sh

@ -1,5 +1,5 @@
#!/bin/sh #!/bin/sh
. git-sh-setup || die "Not a git archive" . git-sh-setup


usage () { usage () {
die "Usage: git push [--all] [--force] <repository> [<refspec>]" die "Usage: git push [--all] [--force] <repository> [<refspec>]"

2
git-rebase.sh

@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano. # Copyright (c) 2005 Junio C Hamano.
# #


. git-sh-setup || die "Not a git archive." . git-sh-setup


# The other head is given # The other head is given
other=$(git-rev-parse --verify "$1^0") || exit other=$(git-rev-parse --verify "$1^0") || exit

31
git-repack.sh

@ -3,7 +3,7 @@
# Copyright (c) 2005 Linus Torvalds # 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= no_update_info= all_into_one= remove_redundant= local=
while case "$#" in 0) break ;; esac while case "$#" in 0) break ;; esac
@ -32,24 +32,20 @@ case ",$all_into_one," in
rev_list= rev_list=
rev_parse='--all' rev_parse='--all'
pack_objects= pack_objects=

# Redundancy check in all-into-one case is trivial.
existing=`cd "$PACKDIR" && \
find . -type f \( -name '*.pack' -o -name '*.idx' \) -print`
;; ;;
esac esac
if [ "$local" ]; then if [ "$local" ]; then
pack_objects="$pack_objects --local" pack_objects="$pack_objects --local"
fi 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) || git-pack-objects --non-empty $pack_objects .tmp-pack) ||
exit 1 exit 1
if [ -z "$name" ]; then if [ -z "$name" ]; then
echo Nothing new to pack. 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 exit 0
fi fi
echo "Pack pack-$name created." echo "Pack pack-$name created."
@ -62,11 +58,12 @@ exit


if test "$remove_redundant" = t if test "$remove_redundant" = t
then then
sync # We know $existing are all redundant only when
if test "$all_into_one" = t # all-into-one is used.
if test "$all_into_one" != '' && test "$existing" != ''
then then
cd "$PACKDIR" sync
existing=`find . -type f \( -name '*.pack' -o -name '*.idx' \) -print` ( cd "$PACKDIR" &&
for e in $existing for e in $existing
do do
case "$e" in case "$e" in
@ -74,11 +71,7 @@ then
*) rm -f $e ;; *) rm -f $e ;;
esac esac
done done
else )
redundant=$(git-pack-redundant --all)
if test "$redundant" != "" ; then
echo $redundant | xargs rm
fi
fi fi
fi fi



2
git-reset.sh

@ -1,5 +1,5 @@
#!/bin/sh #!/bin/sh
. git-sh-setup || die "Not a git archive" . git-sh-setup


usage () { usage () {
die 'Usage: git reset [--mixed | --soft | --hard] [<commit-ish>]' die 'Usage: git reset [--mixed | --soft | --hard] [<commit-ish>]'

2
git-resolve.sh

@ -4,7 +4,7 @@
# #
# Resolve two trees. # Resolve two trees.
# #
. git-sh-setup || die "Not a git archive" . git-sh-setup


usage () { usage () {
die "git-resolve <head> <remote> <merge-message>" die "git-resolve <head> <remote> <merge-message>"

14
git-revert.sh

@ -3,15 +3,17 @@
# Copyright (c) 2005 Linus Torvalds # Copyright (c) 2005 Linus Torvalds
# Copyright (c) 2005 Junio C Hamano # Copyright (c) 2005 Junio C Hamano
# #
. git-sh-setup || die "Not a git archive" . git-sh-setup


case "$0" in case "$0" in
*-revert* ) *-revert* )
test -t 0 && edit=-e
me=revert ;; me=revert ;;
*-cherry-pick* ) *-cherry-pick* )
edit=
me=cherry-pick ;; me=cherry-pick ;;
* ) * )
die "What are ou talking about?" ;; die "What are you talking about?" ;;
esac esac


usage () { usage () {
@ -33,6 +35,12 @@ do
--no-commi|--no-commit) --no-commi|--no-commit)
no_commit=t 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) -r|--r|--re|--rep|--repl|--repla|--replay)
replay=t replay=t
;; ;;
@ -163,7 +171,7 @@ echo >&2 "Finished one $me."


case "$no_commit" in case "$no_commit" in
'') '')
git-commit -n -F .msg git-commit -n -F .msg $edit
rm -f .msg rm -f .msg
;; ;;
esac esac

20
git-sh-setup.sh

@ -1,10 +1,9 @@
#!/bin/sh #!/bin/sh
# #
# Set up GIT_DIR and GIT_OBJECT_DIRECTORY # This is included in commands that either have to be run from the toplevel
# and return true if everything looks ok # of the repository, or with GIT_DIR environment variable properly.
# # If the GIT_DIR does not look like the right correct git-repository,
: ${GIT_DIR=.git} # it dies.
: ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"}


# Having this variable in your environment would break scripts because # Having this variable in your environment would break scripts because
# you would cause "cd" to be be taken to unexpected places. If you # you would cause "cd" to be be taken to unexpected places. If you
@ -12,14 +11,13 @@
# exporting it. # exporting it.
unset CDPATH unset CDPATH


: ${GIT_DIR=.git}
: ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"}

die() { die() {
echo >&2 "$@" echo >&2 "$@"
exit 1 exit 1
} }


case "$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD 2>/dev/null)" in # Make sure we are in a valid repository of a vintage we understand.
refs/*) : ;; GIT_DIR="$GIT_DIR" git-var GIT_AUTHOR_IDENT >/dev/null || exit
*) false ;;
esac &&
[ -d "$GIT_DIR/refs" ] &&
[ -d "$GIT_OBJECT_DIRECTORY/" ]

2
git-status.sh

@ -2,7 +2,7 @@
# #
# Copyright (c) 2005 Linus Torvalds # Copyright (c) 2005 Linus Torvalds
# #
. git-sh-setup || die "Not a git archive" GIT_DIR=$(git-rev-parse --git-dir) || exit


report () { report () {
header="# header="#

2
git-tag.sh

@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# Copyright (c) 2005 Linus Torvalds # Copyright (c) 2005 Linus Torvalds


. git-sh-setup || die "Not a git archive" . git-sh-setup


usage () { usage () {
echo >&2 "Usage: git-tag [-a | -s | -u <key-id>] [-f | -d] [-m <msg>] <tagname> [<head>]" echo >&2 "Usage: git-tag [-a | -s | -u <key-id>] [-f | -d] [-m <msg>] <tagname> [<head>]"

2
git-verify-tag.sh

@ -1,5 +1,5 @@
#!/bin/sh #!/bin/sh
. git-sh-setup || die "Not a git archive" . git-sh-setup


type="$(git-cat-file -t "$1" 2>/dev/null)" || type="$(git-cat-file -t "$1" 2>/dev/null)" ||
die "$1: no such object." die "$1: no such object."

2
git.c

@ -273,7 +273,7 @@ int main(int argc, char **argv, char **envp)
while (!strncmp(exec_path, "./", 2)) { while (!strncmp(exec_path, "./", 2)) {
exec_path += 2; exec_path += 2;
while (*exec_path == '/') while (*exec_path == '/')
*exec_path++; exec_path++;
} }
snprintf(git_command + len, sizeof(git_command) - len, snprintf(git_command + len, sizeof(git_command) - len,
"/%s", exec_path); "/%s", exec_path);

207
gitk

@ -60,7 +60,7 @@ proc getcommits {rargs} {


proc getcommitlines {commfd} { proc getcommitlines {commfd} {
global commits parents cdate children global commits parents cdate children
global commitlisted phase commitinfo nextupdate global commitlisted phase nextupdate
global stopped redisplaying leftover global stopped redisplaying leftover


set stuff [read $commfd] set stuff [read $commfd]
@ -196,42 +196,44 @@ proc parsecommit {id contents listed olds} {
incr ncleft($p) incr ncleft($p)
} }
} }
foreach line [split $contents "\n"] { set hdrend [string first "\n\n" $contents]
if {$inhdr} { if {$hdrend < 0} {
if {$line == {}} { # should never happen...
set inhdr 0 set hdrend [string length $contents]
} else { }
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] set tag [lindex $line 0]
if {$tag == "author"} { if {$tag == "author"} {
set x [expr {[llength $line] - 2}] set audate [lindex $line end-1]
set audate [lindex $line $x] set auname [lrange $line 1 end-2]
set auname [lrange $line 1 [expr {$x - 1}]]
} elseif {$tag == "committer"} { } elseif {$tag == "committer"} {
set x [expr {[llength $line] - 2}] set comdate [lindex $line end-1]
set comdate [lindex $line $x] set comname [lrange $line 1 end-2]
set comname [lrange $line 1 [expr {$x - 1}]]
} }
} }
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 { } else {
if {$comment == {}} { set headline $comment
set headline [string trim $line]
} else {
append comment "\n"
} }
if {!$listed} { if {!$listed} {
# git-rev-list indents the comment by 4 spaces; # git-rev-list indents the comment by 4 spaces;
# if we got this via git-cat-file, add the indentation # if we got this via git-cat-file, add the indentation
append comment " " set newcomment {}
foreach line [split $comment "\n"] {
append newcomment " "
append newcomment $line
append newcomment "\n"
} }
append comment $line set comment $newcomment
}
}
if {$audate != {}} {
set audate [clock format $audate -format "%Y-%m-%d %H:%M:%S"]
} }
if {$comdate != {}} { if {$comdate != {}} {
set cdate($id) $comdate set cdate($id) $comdate
set comdate [clock format $comdate -format "%Y-%m-%d %H:%M:%S"]
} }
set commitinfo($id) [list $headline $auname $audate \ set commitinfo($id) [list $headline $auname $audate \
$comname $comdate $comment] $comname $comdate $comment]
@ -239,77 +241,43 @@ proc parsecommit {id contents listed olds} {


proc readrefs {} { proc readrefs {} {
global tagids idtags headids idheads tagcontents global tagids idtags headids idheads tagcontents
global otherrefids idotherrefs


set tags [glob -nocomplain -types f [gitdir]/refs/tags/*] set refd [open [list | git-ls-remote [gitdir]] r]
foreach f $tags { while {0 <= [set n [gets $refd line]]} {
catch { if {![regexp {^([0-9a-f]{40}) refs/([^^]*)$} $line \
set fd [open $f r] match id path]} {
set line [read $fd] continue
if {[regexp {^[0-9a-f]{40}} $line id]} { }
set direct [file tail $f] if {![regexp {^(tags|heads)/(.*)$} $path match type name]} {
set tagids($direct) $id set type others
lappend idtags($id) $direct set name $path
set tagblob [exec git-cat-file tag $id] }
set contents [split $tagblob "\n"] if {$type == "tags"} {
set tagids($name) $id
lappend idtags($id) $name
set obj {} set obj {}
set type {} set type {}
set tag {} 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 { catch {
set fd [open $f r] set commit [exec git-rev-parse "$id^0"]
set line [read $fd 40] if {"$commit" != "$id"} {
if {[regexp {^[0-9a-f]{40}} $line id]} { set tagids($name) $commit
set head [file tail $f] lappend idtags($commit) $name
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 { catch {
set fd [open $f r] set tagcontents($name) [exec git-cat-file tag "$id"]
set line [read $fd 40] }
if {[regexp {^[0-9a-f]{40}} $line id]} { } elseif { $type == "heads" } {
set name "$dname[file tail $f]" set headids($name) $id
lappend idheads($id) $name
} else {
set otherrefids($name) $id set otherrefids($name) $id
lappend idotherrefs($id) $name lappend idotherrefs($id) $name
} }
close $fd
}
}
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 { proc error_popup msg {
@ -683,7 +651,7 @@ Use and redistribute under the terms of the GNU General Public License} \
} }


proc assigncolor {id} { proc assigncolor {id} {
global commitinfo colormap commcolors colors nextcolor global colormap commcolors colors nextcolor
global parents nparents children nchildren global parents nparents children nchildren
global cornercrossings crossings global cornercrossings crossings


@ -783,10 +751,12 @@ proc bindline {t id} {
$canv bind $t <Button-1> "lineclick %x %y $id 1" $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 global mainline mainlinearrow sidelines lthickness colormap canv


if {$delold} {
$canv delete lines.$id $canv delete lines.$id
}
if {[info exists mainline($id)]} { if {[info exists mainline($id)]} {
set t [$canv create line $mainline($id) \ set t [$canv create line $mainline($id) \
-width [expr {($xtra + 1) * $lthickness}] \ -width [expr {($xtra + 1) * $lthickness}] \
@ -858,7 +828,7 @@ proc drawcommitline {level} {
set mainline($id) [trimdiagstart $mainline($id)] set mainline($id) [trimdiagstart $mainline($id)]
} }
} }
drawlines $id 0 drawlines $id 0 0
set orad [expr {$linespc / 3}] set orad [expr {$linespc / 3}]
set t [$canv create oval [expr $x - $orad] [expr $y1 - $orad] \ set t [$canv create oval [expr $x - $orad] [expr $y1 - $orad] \
[expr $x + $orad - 1] [expr $y1 + $orad - 1] \ [expr $x + $orad - 1] [expr $y1 + $orad - 1] \
@ -878,6 +848,7 @@ proc drawcommitline {level} {
set headline [lindex $commitinfo($id) 0] set headline [lindex $commitinfo($id) 0]
set name [lindex $commitinfo($id) 1] set name [lindex $commitinfo($id) 1]
set date [lindex $commitinfo($id) 2] set date [lindex $commitinfo($id) 2]
set date [formatdate $date]
set linehtag($lineno) [$canv create text $xt $y1 -anchor w \ set linehtag($lineno) [$canv create text $xt $y1 -anchor w \
-text $headline -font $mainfont ] -text $headline -font $mainfont ]
$canv bind $linehtag($lineno) <Button-3> "rowmenu %X %Y $id" $canv bind $linehtag($lineno) <Button-3> "rowmenu %X %Y $id"
@ -1446,8 +1417,8 @@ proc decidenext {{noread 0}} {
} }


proc drawcommit {id} { proc drawcommit {id} {
global phase todo nchildren datemode nextupdate global phase todo nchildren datemode nextupdate revlistorder
global numcommits ncmupdate displayorder todo onscreen global numcommits ncmupdate displayorder todo onscreen parents


if {$phase != "incrdraw"} { if {$phase != "incrdraw"} {
set phase incrdraw set phase incrdraw
@ -1459,6 +1430,15 @@ proc drawcommit {id} {
lappend todo $id lappend todo $id
set onscreen($id) 0 set onscreen($id) 0
} }
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] set level [decidenext 1]
if {$level == {} || $id != [lindex $todo $level]} { if {$level == {} || $id != [lindex $todo $level]} {
return return
@ -1474,6 +1454,7 @@ proc drawcommit {id} {
break break
} }
} }
}
drawmore 1 drawmore 1
} }


@ -1523,7 +1504,7 @@ proc drawrest {} {
global phase stopped redisplaying selectedline global phase stopped redisplaying selectedline
global datemode todo displayorder global datemode todo displayorder
global numcommits ncmupdate global numcommits ncmupdate
global nextupdate startmsecs global nextupdate startmsecs revlistorder


set level [decidenext] set level [decidenext]
if {$level >= 0} { if {$level >= 0} {
@ -1536,8 +1517,8 @@ proc drawrest {} {
if {$level < 0} break if {$level < 0} break
} }
} }
drawmore 0
} }
drawmore 0
set phase {} set phase {}
set drawmsecs [expr [clock clicks -milliseconds] - $startmsecs] set drawmsecs [expr [clock clicks -milliseconds] - $startmsecs]
#puts "overall $drawmsecs ms for $numcommits commits" #puts "overall $drawmsecs ms for $numcommits commits"
@ -2146,8 +2127,10 @@ proc selectline {l isnew} {
$ctext mark set fmark.0 0.0 $ctext mark set fmark.0 0.0
$ctext mark gravity fmark.0 left $ctext mark gravity fmark.0 left
set info $commitinfo($id) set info $commitinfo($id)
$ctext insert end "Author: [lindex $info 1] [lindex $info 2]\n" set date [formatdate [lindex $info 2]]
$ctext insert end "Committer: [lindex $info 3] [lindex $info 4]\n" $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)]} { if {[info exists idtags($id)]} {
$ctext insert end "Tags:" $ctext insert end "Tags:"
foreach tag $idtags($id) { foreach tag $idtags($id) {
@ -2805,8 +2788,7 @@ proc gettreediffs {ids} {
set treepending $ids set treepending $ids
set treediff {} set treediff {}
set id [lindex $ids 0] set id [lindex $ids 0]
set p [lindex $ids 1] if [catch {set gdtf [open "|git-diff-tree --no-commit-id -r $id" r]}] return
if [catch {set gdtf [open "|git-diff-tree -r $id" r]}] return
fconfigure $gdtf -blocking 0 fconfigure $gdtf -blocking 0
fileevent $gdtf readable [list gettreediffline $gdtf $ids] fileevent $gdtf readable [list gettreediffline $gdtf $ids]
} }
@ -2840,9 +2822,8 @@ proc getblobdiffs {ids} {
global difffilestart nextupdate diffinhdr treediffs global difffilestart nextupdate diffinhdr treediffs


set id [lindex $ids 0] set id [lindex $ids 0]
set p [lindex $ids 1]
set env(GIT_DIFF_OPTS) $diffopts 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]} { if {[catch {set bdf [open $cmd r]} err]} {
puts "error getting diffs: $err" puts "error getting diffs: $err"
return return
@ -3143,7 +3124,7 @@ proc linehover {} {
set t [$canv create rectangle $x0 $y0 $x1 $y1 \ set t [$canv create rectangle $x0 $y0 $x1 $y1 \
-fill \#ffff80 -outline black -width 1 -tags hover] -fill \#ffff80 -outline black -width 1 -tags hover]
$canv raise $t $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 $canv raise $t
} }


@ -3178,7 +3159,7 @@ proc clickisonarrow {id y} {
} }


proc arrowjump {id dirn y} { proc arrowjump {id dirn y} {
global mainline sidelines canv global mainline sidelines canv canv2 canv3


set yt {} set yt {}
if {$dirn eq "down"} { if {$dirn eq "down"} {
@ -3216,6 +3197,8 @@ proc arrowjump {id dirn y} {
set yfrac 0 set yfrac 0
} }
$canv yview moveto $yfrac $canv yview moveto $yfrac
$canv2 yview moveto $yfrac
$canv3 yview moveto $yfrac
} }


proc lineclick {x y id isnew} { proc lineclick {x y id isnew} {
@ -3226,7 +3209,7 @@ proc lineclick {x y id isnew} {
normalline normalline
$canv delete hover $canv delete hover
# draw this line thicker than normal # draw this line thicker than normal
drawlines $id 1 drawlines $id 1 1
set thickerline $id set thickerline $id
if {$isnew} { if {$isnew} {
set ymax [lindex [$canv cget -scrollregion] 3] set ymax [lindex [$canv cget -scrollregion] 3]
@ -3255,7 +3238,8 @@ proc lineclick {x y id isnew} {
set info $commitinfo($id) set info $commitinfo($id)
$ctext insert end "\n\t[lindex $info 0]\n" $ctext insert end "\n\t[lindex $info 0]\n"
$ctext insert end "\tAuthor:\t[lindex $info 1]\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)]} { if {[info exists children($id)]} {
$ctext insert end "\nChildren:" $ctext insert end "\nChildren:"
set i 0 set i 0
@ -3267,7 +3251,8 @@ proc lineclick {x y id isnew} {
$ctext tag bind link$i <1> [list selbyid $child] $ctext tag bind link$i <1> [list selbyid $child]
$ctext insert end "\n\t[lindex $info 0]" $ctext insert end "\n\t[lindex $info 0]"
$ctext insert end "\n\tAuthor:\t[lindex $info 1]" $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 $ctext conf -state disabled
@ -3278,7 +3263,7 @@ proc lineclick {x y id isnew} {
proc normalline {} { proc normalline {} {
global thickerline global thickerline
if {[info exists thickerline]} { if {[info exists thickerline]} {
drawlines $thickerline 0 drawlines $thickerline 0 1
unset thickerline unset thickerline
} }
} }
@ -3650,6 +3635,23 @@ proc doquit {} {
destroy . 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... # defaults...
set datemode 0 set datemode 0
set boldnames 0 set boldnames 0
@ -3662,6 +3664,8 @@ set findmergefiles 0
set gaudydiff 0 set gaudydiff 0
set maxgraphpct 50 set maxgraphpct 50
set maxwidth 16 set maxwidth 16
set revlistorder 0
set fastdate 0


set colors {green red blue magenta darkgrey brown orange} set colors {green red blue magenta darkgrey brown orange}


@ -3678,6 +3682,7 @@ foreach arg $argv {
"^$" { } "^$" { }
"^-b" { set boldnames 1 } "^-b" { set boldnames 1 }
"^-d" { set datemode 1 } "^-d" { set datemode 1 }
"^-r" { set revlistorder 1 }
default { default {
lappend revtreeargs $arg lappend revtreeargs $arg
} }

815
http-fetch.c

File diff suppressed because it is too large Load Diff

799
http-push.c

File diff suppressed because it is too large Load Diff

442
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;
}
}

95
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 */

15
ident.c

@ -156,7 +156,8 @@ static int copy(char *buf, int size, int offset, const char *src)
return offset; 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]; static char buffer[1000];
char date[50]; char date[50];
@ -181,12 +182,16 @@ char *get_ident(const char *name, const char *email, const char *date_str)
return buffer; 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"));
} }

2
name-rev.c

@ -230,8 +230,6 @@ int main(int argc, char **argv)
fwrite(p_start, p - p_start, 1, stdout); fwrite(p_start, p - p_start, 1, stdout);
} }
} else if (all) { } else if (all) {
extern struct object **objs;
extern int nr_objs;
int i; int i;


for (i = 0; i < nr_objs; i++) for (i = 0; i < nr_objs; i++)

2
pack-objects.c

@ -524,7 +524,7 @@ int main(int argc, char **argv)
unsigned char sha1[20]; unsigned char sha1[20];


if (get_sha1_hex(line, sha1)) if (get_sha1_hex(line, sha1))
die("expected sha1, got garbage"); die("expected sha1, got garbage:\n %s", line);
hash = 0; hash = 0;
p = line+40; p = line+40;
while (*p) { while (*p) {

128
pack-redundant.c

@ -11,19 +11,19 @@
static const char pack_redundant_usage[] = static const char pack_redundant_usage[] =
"git-pack-redundant [ --verbose ] [ --alt-odb ] < --all | <.pack filename> ...>"; "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 {
struct llist_item *next; struct llist_item *next;
char *sha1; unsigned char *sha1;
}; };
struct llist { static struct llist {
struct llist_item *front; struct llist_item *front;
struct llist_item *back; struct llist_item *back;
size_t size; size_t size;
} *all_objects; /* all objects which must be present in local packfiles */ } *all_objects; /* all objects which must be present in local packfiles */


struct pack_list { static struct pack_list {
struct pack_list *next; struct pack_list *next;
struct packed_git *pack; struct packed_git *pack;
struct llist *unique_objects; struct llist *unique_objects;
@ -36,23 +36,43 @@ struct pll {
size_t pl_size; 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)) { while((list->back = list->front)) {
list->front = list->front->next; list->front = list->front->next;
free(list->back); llist_item_put(list->back);
} }
free(list); free(list);
} }


inline void llist_init(struct llist **list) static inline void llist_init(struct llist **list)
{ {
*list = xmalloc(sizeof(struct llist)); *list = xmalloc(sizeof(struct llist));
(*list)->front = (*list)->back = NULL; (*list)->front = (*list)->back = NULL;
(*list)->size = 0; (*list)->size = 0;
} }


struct llist * llist_copy(struct llist *list) static struct llist * llist_copy(struct llist *list)
{ {
struct llist *ret; struct llist *ret;
struct llist_item *new, *old, *prev; struct llist_item *new, *old, *prev;
@ -62,13 +82,13 @@ struct llist * llist_copy(struct llist *list)
if ((ret->size = list->size) == 0) if ((ret->size = list->size) == 0)
return ret; return ret;


new = ret->front = xmalloc(sizeof(struct llist_item)); new = ret->front = llist_item_get();
new->sha1 = list->front->sha1; new->sha1 = list->front->sha1;


old = list->front->next; old = list->front->next;
while (old) { while (old) {
prev = new; prev = new;
new = xmalloc(sizeof(struct llist_item)); new = llist_item_get();
prev->next = new; prev->next = new;
new->sha1 = old->sha1; new->sha1 = old->sha1;
old = old->next; old = old->next;
@ -79,10 +99,11 @@ struct llist * llist_copy(struct llist *list)
return ret; return ret;
} }


inline struct llist_item * llist_insert(struct llist *list, static inline struct llist_item * llist_insert(struct llist *list,
struct llist_item *after, char *sha1) 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->sha1 = sha1;
new->next = NULL; new->next = NULL;


@ -102,13 +123,12 @@ inline struct llist_item * llist_insert(struct llist *list,
return new; 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); return llist_insert(list, list->back, sha1);
} }


inline struct llist_item * llist_insert_sorted_unique(struct llist *list, static inline struct llist_item *llist_insert_sorted_unique(struct llist *list, unsigned char *sha1, struct llist_item *hint)
char *sha1, struct llist_item *hint)
{ {
struct llist_item *prev = NULL, *l; 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 */ /* returns a pointer to an item in front of sha1 */
inline struct llist_item * llist_sorted_remove(struct llist *list, char *sha1, static inline struct llist_item * llist_sorted_remove(struct llist *list, const unsigned char *sha1, struct llist_item *hint)
struct llist_item *hint)
{ {
struct llist_item *prev, *l; struct llist_item *prev, *l;


@ -153,7 +172,7 @@ redo_from_start:
prev->next = l->next; prev->next = l->next;
if (l == list->back) if (l == list->back)
list->back = prev; list->back = prev;
free(l); llist_item_put(l);
list->size--; list->size--;
return prev; return prev;
} }
@ -164,7 +183,7 @@ redo_from_start:
} }


/* computes A\B */ /* 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 *B)
{ {
struct llist_item *hint, *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 *entry)
{ {
struct pack_list *p = xmalloc(sizeof(struct pack_list)); 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; 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; size_t ret = 0;
while(pl) { while(pl) {
@ -198,10 +217,11 @@ inline size_t pack_list_size(struct pack_list *pl)
return ret; return ret;
} }


struct pack_list * pack_list_difference(struct pack_list *A, static struct pack_list * pack_list_difference(const struct pack_list *A,
struct pack_list *B) const struct pack_list *B)
{ {
struct pack_list *ret, *pl; struct pack_list *ret;
const struct pack_list *pl;


if (A == NULL) if (A == NULL)
return NULL; return NULL;
@ -218,7 +238,7 @@ struct pack_list * pack_list_difference(struct pack_list *A,
return ret; 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; int p1_off, p2_off;
void *p1_base, *p2_base; 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; struct pll *prev;
int i = (*pll)->pl_size - 1; 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, /* all the permutations have to be free()d at the same time,
* since they refer to each other * 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*/ struct pll *subset, *pll, *new_pll = NULL; /*silence warning*/
static struct pll **hint = NULL; static struct pll **hint = NULL;
@ -323,15 +343,14 @@ struct pll * get_all_permutations(struct pack_list *list)
return hint[0]; 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; struct llist *diff;


diff = llist_copy(list); diff = llist_copy(list);


while (pl) { while (pl) {
llist_sorted_difference_inplace(diff, llist_sorted_difference_inplace(diff, pl->all_objects);
pl->all_objects);
if (diff->size == 0) { /* we're done */ if (diff->size == 0) { /* we're done */
llist_free(diff); llist_free(diff);
return 1; return 1;
@ -342,7 +361,7 @@ int is_superset(struct pack_list *pl, struct llist *list)
return 0; 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; size_t ret = 0;
int p1_off, p2_off; 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 ... */ /* 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; struct pack_list *subset;
size_t ret = 0;


if (pl == NULL) if (pl == NULL)
return 0; return 0;


size_t ret = 0;
while ((subset = pl->next)) { while ((subset = pl->next)) {
while(subset) { while(subset) {
ret += sizeof_union(pl->pack, subset->pack); ret += sizeof_union(pl->pack, subset->pack);
@ -391,7 +410,7 @@ size_t get_pack_redundancy(struct pack_list *pl)
return ret; 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; size_t ret = 0;
while (pl) { while (pl) {
@ -402,7 +421,7 @@ inline size_t pack_set_bytecount(struct pack_list *pl)
return ret; return ret;
} }


void minimize(struct pack_list **min) static void minimize(struct pack_list **min)
{ {
struct pack_list *pl, *unique = NULL, struct pack_list *pl, *unique = NULL,
*non_unique = NULL, *min_perm = 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 pack_list *pl = local_packs;
struct llist_item *hint, *l; struct llist_item *hint, *l;
int i;


llist_init(&all_objects); llist_init(&all_objects);


while (pl) { while (pl) {
i = 0;
hint = NULL; hint = NULL;
l = pl->all_objects->front; l = pl->all_objects->front;
while (l) { while (l) {
@ -497,7 +514,7 @@ void load_all_objects()
} }


/* this scales like O(n^2) */ /* this scales like O(n^2) */
void cmp_local_packs() static void cmp_local_packs(void)
{ {
struct pack_list *subset, *pl = local_packs; 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; 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; struct pack_list l;
size_t off; size_t off;
@ -550,7 +567,7 @@ struct pack_list * add_pack(struct packed_git *p)
return pack_list_insert(&altodb_packs, &l); 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; 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); die("Filename %s not found in packed_git\n", filename);
} }


void load_all() static void load_all(void)
{ {
struct packed_git *p = packed_git; struct packed_git *p = packed_git;


@ -579,6 +596,9 @@ int main(int argc, char **argv)
{ {
int i; int i;
struct pack_list *min, *red, *pl; 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++) { for (i = 1; i < argc; i++) {
const char *arg = argv[i]; const char *arg = argv[i];
@ -621,6 +641,23 @@ int main(int argc, char **argv)
if (alt_odb) if (alt_odb)
scan_alt_odb_packs(); 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); minimize(&min);


if (verbose) { if (verbose) {
@ -647,6 +684,9 @@ int main(int argc, char **argv)
pl->pack->pack_name); pl->pack->pack_name);
pl = pl->next; pl = pl->next;
} }
if (verbose)
fprintf(stderr, "%luMB of redundant packs in total.\n",
(unsigned long)pack_set_bytecount(red)/(1024*1024));


return 0; return 0;
} }

115
path.c

@ -11,6 +11,7 @@
* which is what it's designed for. * which is what it's designed for.
*/ */
#include "cache.h" #include "cache.h"
#include <pwd.h>


static char pathname[PATH_MAX]; static char pathname[PATH_MAX];
static char bad_path[] = "/bad-path/"; static char bad_path[] = "/bad-path/";
@ -89,3 +90,117 @@ char *safe_strncpy(char *dest, const char *src, size_t n)


return dest; 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;
}

17
receive-pack.c

@ -248,11 +248,11 @@ static void unpack(void)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int i; int i;
const char *dir = NULL; char *dir = NULL;


argv++; argv++;
for (i = 1; i < argc; i++) { for (i = 1; i < argc; i++) {
const char *arg = *argv++; char *arg = *argv++;


if (*arg == '-') { if (*arg == '-') {
/* Do flag handling here */ /* Do flag handling here */
@ -265,18 +265,9 @@ int main(int argc, char **argv)
if (!dir) if (!dir)
usage(receive_pack_usage); usage(receive_pack_usage);


/* chdir to the directory. If that fails, try appending ".git" */ if(!enter_repo(dir, 0))
if (chdir(dir) < 0) { die("'%s': unable to chdir or not a git archive", dir);
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 (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(); write_head_info();


/* EOF */ /* EOF */

40
refs.c

@ -10,46 +10,6 @@
#define USE_SYMLINK_HEAD 1 #define USE_SYMLINK_HEAD 1
#endif #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) const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
{ {
int depth = MAXDEPTH, len; int depth = MAXDEPTH, len;

116
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;
}

5
rev-list.c

@ -124,8 +124,6 @@ static int filter_commit(struct commit * commit)
stop_traversal=1; stop_traversal=1;
return CONTINUE; return CONTINUE;
} }
if (max_count != -1 && !max_count--)
return STOP;
if (no_merges && (commit->parents && commit->parents->next)) if (no_merges && (commit->parents && commit->parents->next))
return CONTINUE; return CONTINUE;
if (paths && dense) { if (paths && dense) {
@ -148,6 +146,9 @@ static int process_commit(struct commit * commit)
return CONTINUE; return CONTINUE;
} }


if (max_count != -1 && !max_count--)
return STOP;

show_commit(commit); show_commit(commit);


return CONTINUE; return CONTINUE;

42
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 * Test if it looks like we're at the top level git directory.
* level git directory. We want to see a * We want to see:
* *
* - either a .git/objects/ directory _or_ the proper * - either a .git/objects/ directory _or_ the proper
* GIT_OBJECT_DIRECTORY environment variable * GIT_OBJECT_DIRECTORY environment variable
@ -92,17 +92,43 @@ static int is_toplevel_directory(void)
return 1; return 1;
} }


const char *setup_git_directory(void) static const char *setup_git_directory_1(void)
{ {
static char cwd[PATH_MAX+1]; static char cwd[PATH_MAX+1];
int len, offset; int len, offset;


/* /*
* If GIT_DIR is set explicitly, we're not going * 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; return NULL;
bad_dir_environ:
path[len] = 0;
die("Not a git repository: '%s'", path);
}


if (!getcwd(cwd, sizeof(cwd)) || cwd[0] != '/') if (!getcwd(cwd, sizeof(cwd)) || cwd[0] != '/')
die("Unable to read current working directory"); die("Unable to read current working directory");
@ -127,3 +153,9 @@ const char *setup_git_directory(void)
cwd[len] = 0; cwd[len] = 0;
return cwd + offset; return cwd + offset;
} }

const char *setup_git_directory(void)
{
const char *retval = setup_git_directory_1();
return retval;
}

17
sha1_name.c

@ -236,6 +236,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
NULL NULL
}; };
const char **p; const char **p;
int found = 0;


if (len == 40 && !get_sha1_hex(str, sha1)) if (len == 40 && !get_sha1_hex(str, sha1))
return 0; return 0;
@ -246,10 +247,20 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)


for (p = prefix; *p; p++) { for (p = prefix; *p; p++) {
char *pathname = git_path("%s/%.*s", *p, len, str); char *pathname = git_path("%s/%.*s", *p, len, str);
if (!read_ref(pathname, sha1)) if (!read_ref(pathname, sha1)) {
return 0; /* 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; return -1;
} }



13
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) 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 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) 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) if (MAX_REVS <= num_rev)
die("cannot handle more than %d revs.", MAX_REVS); die("cannot handle more than %d revs.", MAX_REVS);
if (get_sha1(ref_name[num_rev], revkey)) 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); commit = lookup_commit_reference(revkey);
if (!commit) if (!commit)
die("cannot find commit %s (%s)", die("cannot find commit %s (%s)",

271
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

7
templates/hooks--update

@ -8,14 +8,14 @@
# (2) make this file executable by "chmod +x update". # (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 if expr "$2" : '0*$' >/dev/null
then then
echo "Created a new ref, with the following commits:" echo "Created a new ref, with the following commits:"
git-rev-list --pretty "$3" git-rev-list --pretty "$3"
else else
$base=$(git-merge-base "$2" "$3") base=$(git-merge-base "$2" "$3")
case "$base" in case "$base" in
"$2") "$2")
echo "New commits:" echo "New commits:"
@ -24,8 +24,7 @@ else
echo "Rebased ref, commits from common ancestor:" echo "Rebased ref, commits from common ancestor:"
;; ;;
esac esac
fi git-rev-list --pretty "$3" "^$base"
git-rev-list --pretty "$3" "^$base"
fi | fi |
mail -s "Changes to ref $1" "$recipient" mail -s "Changes to ref $1" "$recipient"
exit 0 exit 0

13
update-index.c

@ -338,7 +338,7 @@ static void read_index_info(int line_termination)
struct strbuf buf; struct strbuf buf;
strbuf_init(&buf); strbuf_init(&buf);
while (1) { while (1) {
char *ptr; char *ptr, *tab;
char *path_name; char *path_name;
unsigned char sha1[20]; unsigned char sha1[20];
unsigned int mode; unsigned int mode;
@ -348,12 +348,15 @@ static void read_index_info(int line_termination)
break; break;


mode = strtoul(buf.buf, &ptr, 8); mode = strtoul(buf.buf, &ptr, 8);
if (ptr == buf.buf || *ptr != ' ' || if (ptr == buf.buf || *ptr != ' ')
get_sha1_hex(ptr + 1, sha1) ||
ptr[41] != '\t')
goto bad_line; 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] == '"') if (line_termination && ptr[0] == '"')
path_name = unquote_c_style(ptr, NULL); path_name = unquote_c_style(ptr, NULL);

15
upload-pack.c

@ -248,7 +248,7 @@ static int upload_pack(void)


int main(int argc, char **argv) int main(int argc, char **argv)
{ {
const char *dir; char *dir;
int i; int i;
int strict = 0; int strict = 0;


@ -275,18 +275,9 @@ int main(int argc, char **argv)
usage(upload_pack_usage); usage(upload_pack_usage);
dir = argv[i]; dir = argv[i];


/* chdir to the directory. If that fails, try appending ".git" */ if (!enter_repo(dir, strict))
if (chdir(dir) < 0) { die("'%s': unable to chdir or not a git archive", dir);
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);


putenv("GIT_DIR=.");
upload_pack(); upload_pack();
return 0; return 0;
} }

4
var.c

@ -12,7 +12,7 @@ static const char var_usage[] = "git-var [-l | <variable>]";


struct git_var { struct git_var {
const char *name; const char *name;
char *(*read)(void); const char *(*read)(void);
}; };
static struct git_var git_vars[] = { static struct git_var git_vars[] = {
{ "GIT_COMMITTER_IDENT", git_committer_info }, { "GIT_COMMITTER_IDENT", git_committer_info },
@ -57,6 +57,8 @@ int main(int argc, char **argv)
if (argc != 2) { if (argc != 2) {
usage(var_usage); usage(var_usage);
} }

setup_git_directory();
setup_ident(); setup_ident();
val = NULL; val = NULL;



Loading…
Cancel
Save