Merge branch 'maint'
authorJunio C Hamano <junkio@cox.net>
Mon, 2 Oct 2006 07:47:32 +0000 (00:47 -0700)
committerJunio C Hamano <junkio@cox.net>
Mon, 2 Oct 2006 07:47:32 +0000 (00:47 -0700)
* maint:
  git-push: .git/remotes/ file does not require SP after colon
  git-mv: invalidate the removed path properly in cache-tree

224 files changed:
.gitignore
Documentation/Makefile
Documentation/config.txt
Documentation/core-tutorial.txt
Documentation/cvs-migration.txt
Documentation/diff-options.txt
Documentation/git-apply.txt
Documentation/git-archive.txt [new file with mode: 0644]
Documentation/git-blame.txt
Documentation/git-daemon.txt
Documentation/git-grep.txt
Documentation/git-init-db.txt
Documentation/git-ls-remote.txt
Documentation/git-pack-objects.txt
Documentation/git-receive-pack.txt
Documentation/git-repack.txt
Documentation/git-repo-config.txt
Documentation/git-rev-list.txt
Documentation/git-svn.txt
Documentation/git-tar-tree.txt
Documentation/git-unpack-objects.txt
Documentation/git-update-index.txt
Documentation/git-upload-archive.txt [new file with mode: 0644]
Documentation/git-upload-tar.txt [deleted file]
Documentation/git.txt
Documentation/gitk.txt
Documentation/hooks.txt
Documentation/technical/racy-git.txt [new file with mode: 0644]
Documentation/tutorial-2.txt
GIT-VERSION-GEN
INSTALL
Makefile
archive-tar.c [new file with mode: 0644]
archive-zip.c [new file with mode: 0644]
archive.h [new file with mode: 0644]
blame.c
builtin-apply.c
builtin-archive.c [new file with mode: 0644]
builtin-cat-file.c
builtin-checkout-index.c [moved from checkout-index.c with 92% similarity]
builtin-commit-tree.c
builtin-count-objects.c [moved from builtin-count.c with 98% similarity]
builtin-diff-files.c
builtin-diff-stages.c
builtin-diff.c
builtin-fmt-merge-msg.c
builtin-grep.c
builtin-init-db.c
builtin-log.c
builtin-ls-files.c
builtin-ls-tree.c
builtin-mailinfo.c
builtin-mv.c
builtin-name-rev.c [moved from name-rev.c with 97% similarity]
builtin-pack-objects.c [moved from pack-objects.c with 81% similarity]
builtin-prune-packed.c
builtin-prune.c
builtin-push.c
builtin-read-tree.c
builtin-repo-config.c
builtin-rev-list.c
builtin-rev-parse.c
builtin-rm.c
builtin-runstatus.c [new file with mode: 0644]
builtin-show-branch.c
builtin-symbolic-ref.c [moved from symbolic-ref.c with 74% similarity]
builtin-tar-tree.c
builtin-unpack-objects.c [moved from unpack-objects.c with 82% similarity]
builtin-update-index.c
builtin-update-ref.c
builtin-upload-archive.c [new file with mode: 0644]
builtin-upload-tar.c [deleted file]
builtin-verify-pack.c [moved from verify-pack.c with 83% similarity]
builtin-write-tree.c
builtin.h
cache-tree.c
cache.h
check-racy.c [new file with mode: 0644]
color.c [new file with mode: 0644]
color.h [new file with mode: 0644]
combine-diff.c
commit.c
commit.h
compat/inet_pton.c [new file with mode: 0644]
config.c
config.mak.in
configure.ac
connect.c
contrib/completion/git-completion.bash [new file with mode: 0755]
contrib/emacs/vc-git.el
contrib/gitview/gitview.txt
contrib/vim/README [new file with mode: 0644]
contrib/vim/syntax/gitcommit.vim [new file with mode: 0644]
convert-objects.c
csum-file.c
daemon.c
date.c
describe.c
diff-delta.c
diff-lib.c
diff.c
diff.h
diffcore-break.c
diffcore-rename.c
dir.c
dir.h
dump-cache-tree.c
entry.c
environment.c
exec_cmd.c
fetch-clone.c
fetch-pack.c
fetch.c
fsck-objects.c
generate-cmdlist.sh
git-branch.sh
git-checkout.sh
git-cherry.sh
git-clone.sh
git-commit.sh
git-compat-util.h
git-cvsexportcommit.perl
git-fetch.sh
git-ls-remote.sh
git-merge-recursive-old.py [moved from git-merge-recursive.py with 100% similarity]
git-merge.sh
git-parse-remote.sh
git-rebase.sh
git-repack.sh
git-reset.sh
git-resolve.sh
git-send-email.perl
git-svn.perl
git-svnimport.perl
git.c
git.spec.in
gitk
gitweb/README
gitweb/git-favicon.png [new file with mode: 0644]
gitweb/git-logo.png [new file with mode: 0644]
gitweb/gitweb.cgi [deleted file]
gitweb/gitweb.css
gitweb/gitweb.perl [new file with mode: 0755]
grep.c [new file with mode: 0644]
grep.h [new file with mode: 0644]
help.c [moved from builtin-help.c with 98% similarity]
http-fetch.c
http-push.c
http.c
http.h
imap-send.c
index-pack.c
interpolate.c [new file with mode: 0644]
interpolate.h [new file with mode: 0644]
list-objects.c [new file with mode: 0644]
list-objects.h [new file with mode: 0644]
local-fetch.c
log-tree.c
merge-base.c
merge-file.c
merge-index.c
merge-recursive.c [new file with mode: 0644]
merge-tree.c
mktag.c
mktree.c
object-refs.c
object.c
object.h
pack-check.c
pack-redundant.c
pack.h
pager.c
patch-id.c
path-list.c
path.c
peek-remote.c
perl/.gitignore [new file with mode: 0644]
perl/Git.pm [new file with mode: 0644]
perl/Makefile.PL [new file with mode: 0644]
perl/private-Error.pm [new file with mode: 0644]
quote.c
quote.h
read-cache.c
receive-pack.c
refs.c
revision.c
revision.h
rsh.c
run-command.c
send-pack.c
server-info.c
setup.c
sha1_file.c
sha1_name.c
sideband.c [new file with mode: 0644]
sideband.h [new file with mode: 0644]
ssh-fetch.c
ssh-upload.c
t/t1400-update-ref.sh
t/t3200-branch.sh
t/t3403-rebase-skip.sh
t/t3700-add.sh
t/t4103-apply-binary.sh
t/t4104-apply-boundary.sh [new file with mode: 0755]
t/t4116-apply-reverse.sh [new file with mode: 0755]
t/t4117-apply-reject.sh [new file with mode: 0755]
t/t5400-send-pack.sh
t/t5510-fetch.sh [new file with mode: 0755]
t/t5710-info-alternate.sh
t/t6001-rev-list-graft.sh [new file with mode: 0755]
t/t7002-grep.sh
t/t7201-co.sh
t/test-lib.sh
trace.c [new file with mode: 0644]
tree-diff.c
tree-walk.c
tree.c
unpack-trees.c [new file with mode: 0644]
unpack-trees.h [new file with mode: 0644]
upload-pack.c
write_or_die.c [new file with mode: 0644]
wt-status.c [new file with mode: 0644]
wt-status.h [new file with mode: 0644]
xdiff-interface.c

index fb0fa3f..25eb463 100644 (file)
@@ -8,6 +8,7 @@ git-apply
 git-applymbox
 git-applypatch
 git-archimport
+git-archive
 git-bisect
 git-branch
 git-cat-file
@@ -62,7 +63,9 @@ git-merge-tree
 git-merge-octopus
 git-merge-one-file
 git-merge-ours
+git-merge-recur
 git-merge-recursive
+git-merge-recursive-old
 git-merge-resolve
 git-merge-stupid
 git-mktag
@@ -93,6 +96,7 @@ git-rev-list
 git-rev-parse
 git-revert
 git-rm
+git-runstatus
 git-send-email
 git-send-pack
 git-sh-setup
@@ -117,14 +121,15 @@ git-unpack-objects
 git-update-index
 git-update-ref
 git-update-server-info
+git-upload-archive
 git-upload-pack
-git-upload-tar
 git-var
 git-verify-pack
 git-verify-tag
 git-whatchanged
 git-write-tree
 git-core-*/?*
+gitweb/gitweb.cgi
 test-date
 test-delta
 test-dump-cache-tree
@@ -138,9 +143,10 @@ git-core.spec
 *.py[co]
 config.mak
 autom4te.cache
+config.cache
 config.log
 config.status
-config.mak.in
 config.mak.autogen
+config.mak.append
 configure
 git-blame
index 0d9ffb4..c00f5f6 100644 (file)
@@ -33,6 +33,8 @@ man7dir=$(mandir)/man7
 
 INSTALL?=install
 
+-include ../config.mak.autogen
+
 #
 # Please note that there is a minor bug in asciidoc.
 # The version after 6.0.3 _will_ include the patch found here:
@@ -105,7 +107,7 @@ WEBDOC_DEST = /pub/software/scm/git/docs
 
 $(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt
        rm -f $@+ $@
-       sed -e '1,/^$$/d' $? | asciidoc -b xhtml11 - >$@+
+       sed -e '1,/^$$/d' $< | asciidoc -b xhtml11 - >$@+
        mv $@+ $@
 
 install-webdoc : html
index ce722a2..84e3891 100644 (file)
@@ -119,6 +119,13 @@ apply.whitespace::
        Tells `git-apply` how to handle whitespaces, in the same way
        as the '--whitespace' option. See gitlink:git-apply[1].
 
+branch.<name>.remote::
+       When in branch <name>, it tells `git fetch` which remote to fetch.
+
+branch.<name>.merge::
+       When in branch <name>, it tells `git fetch` the default remote branch
+       to be merged.
+
 pager.color::
        A boolean to enable/disable colored output when the pager is in
        use (default is true).
@@ -195,6 +202,12 @@ http.lowSpeedLimit, http.lowSpeedTime::
        Can be overridden by the 'GIT_HTTP_LOW_SPEED_LIMIT' and
        'GIT_HTTP_LOW_SPEED_TIME' environment variables.
 
+http.noEPSV::
+       A boolean which disables using of EPSV ftp command by curl.
+       This can helpful with some "poor" ftp servers which doesn't
+       support EPSV mode. Can be overridden by the 'GIT_CURL_FTP_NO_EPSV'
+       environment variable. Default is false (curl will use EPSV).
+
 i18n.commitEncoding::
        Character encoding the commit messages are stored in; git itself
        does not care per se, but this information is necessary e.g. when
@@ -225,6 +238,20 @@ showbranch.default::
        The default set of branches for gitlink:git-show-branch[1].
        See gitlink:git-show-branch[1].
 
+status.color::
+       A boolean to enable/disable color in the output of
+       gitlink:git-status[1]. May be set to `true` (or `always`),
+       `false` (or `never`) or `auto`, in which case colors are used
+       only when the output is to a terminal. Defaults to false.
+
+status.color.<slot>::
+       Use customized color for status colorization. `<slot>` is
+       one of `header` (the header text of the status message),
+       `updated` (files which are updated but not committed),
+       `changed` (files which are changed but not updated in the index),
+       or `untracked` (files which are not tracked by git). The values of
+       these variables may be specified as in diff.color.<slot>.
+
 tar.umask::
        By default, gitlink:git-tar-tree[1] sets file and directories modes
        to 0666 or 0777. While this is both useful and acceptable for projects
@@ -253,3 +280,10 @@ whatchanged.difftree::
 imap::
        The configuration variables in the 'imap' section are described
        in gitlink:git-imap-send[1].
+
+receive.denyNonFastforwads::
+       If set to true, git-receive-pack will deny a ref update which is
+       not a fast forward. Use this to prevent such an update via a push,
+       even if that push is forced. This configuration variable is
+       set when initializing a shared repository.
+
index 1185897..47505aa 100644 (file)
@@ -1620,7 +1620,7 @@ suggested in the previous section may be new to you. You do not
 have to worry. git supports "shared public repository" style of
 cooperation you are probably more familiar with as well.
 
-See link:cvs-migration.txt[git for CVS users] for the details.
+See link:cvs-migration.html[git for CVS users] for the details.
 
 Bundling your work together
 ---------------------------
index d2b0bd3..6812683 100644 (file)
@@ -172,7 +172,7 @@ Advanced Shared Repository Management
 
 Git allows you to specify scripts called "hooks" to be run at certain
 points.  You can use these, for example, to send all commits to the shared
-repository to a mailing list.  See link:hooks.txt[Hooks used by git].
+repository to a mailing list.  See link:hooks.html[Hooks used by git].
 
 You can enforce finer grained permissions using update hooks.  See
 link:howto/update-hook-example.txt[Controlling access to branches using
index 47ba9a4..7b7b9e8 100644 (file)
 --patch-with-raw::
        Synonym for "-p --raw".
 
---stat::
-       Generate a diffstat.
+--stat[=width[,name-width]]::
+       Generate a diffstat.  You can override the default
+       output width for 80-column terminal by "--stat=width".
+       The width of the filename part can be controlled by
+       giving another width to it separated by a comma.
 
 --summary::
        Output a condensed summary of extended header information
@@ -36,6 +39,9 @@
        Turn off colored diff, even when the configuration file
        gives the default to color output.
 
+--color-words::
+       Show colored word diff, i.e. color words which have changed.
+
 --no-renames::
        Turn off rename detection, even when the configuration
        file gives the default to do so.
index 2ff7494..d9137c7 100644 (file)
@@ -10,9 +10,10 @@ SYNOPSIS
 --------
 [verse]
 'git-apply' [--stat] [--numstat] [--summary] [--check] [--index] [--apply]
-         [--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM]
-         [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>]
-         [<patch>...]
+         [--no-add] [--index-info] [--allow-binary-replacement | --binary]
+         [-R | --reverse] [--reject] [-z] [-pNUM] [-CNUM] [--inaccurate-eof]
+         [--whitespace=<nowarn|warn|error|error-all|strip>] [--exclude=PATH]
+         [--cached] [--verbose] [<patch>...]
 
 DESCRIPTION
 -----------
@@ -55,6 +56,11 @@ OPTIONS
        up-to-date, it is flagged as an error.  This flag also
        causes the index file to be updated.
 
+--cached::
+       Apply a patch without touching the working tree. Instead, take the
+       cached data, apply the patch, and store the result in the index,
+       without using the working tree. This implies '--index'.
+
 --index-info::
        Newer git-diff output has embedded 'index information'
        for each blob to help identify the original version that
@@ -62,6 +68,16 @@ OPTIONS
        the original version of the blob is available locally,
        outputs information about them to the standard output.
 
+-R, --reverse::
+       Apply the patch in reverse.
+
+--reject::
+       For atomicity, gitlink:git-apply[1] by default fails the whole patch and
+       does not touch the working tree when some of the hunks
+       do not apply.  This option makes it apply
+       the parts of the patch that are applicable, and leave the
+       rejected hunks in corresponding *.rej files.
+
 -z::
        When showing the index information, do not munge paths,
        but use NUL terminated machine readable format.  Without
@@ -79,9 +95,19 @@ OPTIONS
        context exist they all must match.  By default no context is
        ever ignored.
 
+--unidiff-zero::
+       By default, gitlink:git-apply[1] expects that the patch being
+       applied is a unified diff with at least one line of context.
+       This provides good safety measures, but breaks down when
+       applying a diff generated with --unified=0. To bypass these
+       checks use '--unidiff-zero'.
++
+Note, for the reasons stated above usage of context-free patches are
+discouraged.
+
 --apply::
-       If you use any of the options marked ``Turns off
-       "apply"'' above, git-apply reads and outputs the
+       If you use any of the options marked "Turns off
+       'apply'" above, gitlink:git-apply[1] reads and outputs the
        information you asked without actually applying the
        patch.  Give this flag after those flags to also apply
        the patch.
@@ -93,16 +119,16 @@ OPTIONS
        the result with this option, which would apply the
        deletion part but not addition part.
 
---allow-binary-replacement::
-       When applying a patch, which is a git-enhanced patch
-       that was prepared to record the pre- and post-image object
-       name in full, and the path being patched exactly matches
-       the object the patch applies to (i.e. "index" line's
-       pre-image object name is what is in the working tree),
-       and the post-image object is available in the object
-       database, use the post-image object as the patch
-       result.  This allows binary files to be patched in a
-       very limited way.
+--allow-binary-replacement, --binary::
+       Historically we did not allow binary patch applied
+       without an explicit permission from the user, and this
+       flag was the way to do so.  Currently we always allow binary
+       patch application, so this is a no-op.
+
+--exclude=<path-pattern>::
+       Don't apply changes to files matching the given path pattern. This can
+       be useful when importing patchsets, where you want to exclude certain
+       files or directories.
 
 --whitespace=<option>::
        When applying a patch, detect a new or modified line
@@ -110,7 +136,7 @@ OPTIONS
        line that solely consists of whitespaces).  By default,
        the command outputs warning messages and applies the
        patch.
-       When `git-apply` is used for statistics and not applying a
+       When gitlink:git-apply[1] is used for statistics and not applying a
        patch, it defaults to `nowarn`.
        You can use different `<option>` to control this
        behavior:
@@ -124,6 +150,17 @@ OPTIONS
 * `strip` outputs warnings for a few such errors, strips out the
   trailing whitespaces and applies the patch.
 
+--inacurate-eof::
+       Under certain circumstances, some versions of diff do not correctly
+       detect a missing new-line at the end of the file. As a result, patches
+       created by such diff programs do not record incomplete lines
+       correctly. This option adds support for applying such patches by
+       working around this bug.
+
+--verbose::
+       Report progress to stderr. By default, only a message about the
+       current patch being applied will be printed. This option will cause
+       additional information to be reported.
 
 Configuration
 -------------
diff --git a/Documentation/git-archive.txt b/Documentation/git-archive.txt
new file mode 100644 (file)
index 0000000..031fcd5
--- /dev/null
@@ -0,0 +1,113 @@
+git-archive(1)
+==============
+
+NAME
+----
+git-archive - Creates a archive of the files in the named tree
+
+
+SYNOPSIS
+--------
+'git-archive' --format=<fmt> [--list] [--prefix=<prefix>/] [<extra>]
+             [--remote=<repo>] <tree-ish> [path...]
+
+DESCRIPTION
+-----------
+Creates an archive of the specified format containing the tree
+structure for the named tree.  If <prefix> is specified it is
+prepended to the filenames in the archive.
+
+'git-archive' behaves differently when given a tree ID versus when
+given a commit ID or tag ID.  In the first case the current time is
+used as modification time of each file in the archive.  In the latter
+case the commit time as recorded in the referenced commit object is
+used instead.  Additionally the commit ID is stored in a global
+extended pax header if the tar format is used; it can be extracted
+using 'git-get-tar-commit-id'. In ZIP files it is stored as a file
+comment.
+
+OPTIONS
+-------
+
+--format=<fmt>::
+       Format of the resulting archive: 'tar', 'zip'...
+
+--list::
+       Show all available formats.
+
+--prefix=<prefix>/::
+       Prepend <prefix>/ to each filename in the archive.
+
+<extra>::
+       This can be any options that the archiver backend understand.
+       See next section.
+
+--remote=<repo>::
+       Instead of making a tar archive from local repository,
+       retrieve a tar archive from a remote repository.
+
+<tree-ish>::
+       The tree or commit to produce an archive for.
+
+path::
+       If one or more paths are specified, include only these in the
+       archive, otherwise include all files and subdirectories.
+
+BACKEND EXTRA OPTIONS
+---------------------
+
+zip
+~~~
+-0::
+       Store the files instead of deflating them.
+-9::
+       Highest and slowest compression level.  You can specify any
+       number from 1 to 9 to adjust compression speed and ratio.
+
+
+CONFIGURATION
+-------------
+By default, file and directories modes are set to 0666 or 0777 in tar
+archives.  It is possible to change this by setting the "umask" variable
+in the repository configuration as follows :
+
+[tar]
+        umask = 002    ;# group friendly
+
+The special umask value "user" indicates that the user's current umask
+will be used instead. The default value remains 0, which means world
+readable/writable files and directories.
+
+EXAMPLES
+--------
+git archive --format=tar --prefix=junk/ HEAD | (cd /var/tmp/ && tar xf -)::
+
+       Create a tar archive that contains the contents of the
+       latest commit on the current branch, and extracts it in
+       `/var/tmp/junk` directory.
+
+git archive --format=tar --prefix=git-1.4.0/ v1.4.0 | gzip >git-1.4.0.tar.gz::
+
+       Create a compressed tarball for v1.4.0 release.
+
+git archive --format=tar --prefix=git-1.4.0/ v1.4.0{caret}\{tree\} | gzip >git-1.4.0.tar.gz::
+
+       Create a compressed tarball for v1.4.0 release, but without a
+       global extended pax header.
+
+git archive --format=zip --prefix=git-docs/ HEAD:Documentation/ > git-1.4.0-docs.zip::
+
+       Put everything in the current head's Documentation/ directory
+       into 'git-1.4.0-docs.zip', with the prefix 'git-docs/'.
+
+Author
+------
+Written by Franck Bui-Huu and Rene Scharfe.
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the gitlink:git[7] suite
index bfed945..e1f8944 100644 (file)
@@ -3,21 +3,38 @@ git-blame(1)
 
 NAME
 ----
-git-blame - Blame file lines on commits
+git-blame - Show what revision and author last modified each line of a file
 
 SYNOPSIS
 --------
-git-blame file [options] file [revision]
+'git-blame' [-c] [-l] [-t] [-S <revs-file>] [--] <file> [<rev>]
 
 DESCRIPTION
 -----------
-Annotates each line in the given file with information from the commit
-which introduced the line. Start annotation from the given revision.
+
+Annotates each line in the given file with information from the revision which
+last modified the line. Optionally, start annotating from the given revision.
+
+This report doesn't tell you anything about lines which have been deleted or
+replaced; you need to use a tool such as gitlink:git-diff[1] or the "pickaxe"
+interface briefly mentioned in the following paragraph.
+
+Apart from supporting file annotation, git also supports searching the
+development history for when a code snippet occured in a change. This makes it
+possible to track when a code snippet was added to a file, moved or copied
+between files, and eventually deleted or replaced. It works by searching for
+a text string in the diff. A small example:
+
+-----------------------------------------------------------------------------
+$ git log --pretty=oneline -S'blame_usage'
+5040f17eba15504bad66b14a645bddd9b015ebb7 blame -S <ancestry-file>
+ea4c7f9bf69e781dd0cd88d2bccb2bf5cc15c9a7 git-blame: Make the output
+-----------------------------------------------------------------------------
 
 OPTIONS
 -------
 -c, --compatibility::
-       Use the same output mode as git-annotate (Default: off).
+       Use the same output mode as gitlink:git-annotate[1] (Default: off).
 
 -l, --long::
        Show long rev (Default: off).
@@ -26,7 +43,7 @@ OPTIONS
        Show raw timestamp (Default: off).
 
 -S, --rev-file <revs-file>::
-       Use revs from revs-file instead of calling git-rev-list.
+       Use revs from revs-file instead of calling gitlink:git-rev-list[1].
 
 -h, --help::
        Show help message.
index 0f7d274..d562232 100644 (file)
@@ -8,19 +8,21 @@ git-daemon - A really simple server for git repositories
 SYNOPSIS
 --------
 [verse]
-'git-daemon' [--verbose] [--syslog] [--inetd | --port=n] [--export-all]
+'git-daemon' [--verbose] [--syslog] [--export-all]
              [--timeout=n] [--init-timeout=n] [--strict-paths]
              [--base-path=path] [--user-path | --user-path=path]
-            [--reuseaddr] [--detach] [--pid-file=file] [directory...]
+             [--interpolated-path=pathtemplate]
+             [--reuseaddr] [--detach] [--pid-file=file]
+             [--enable=service] [--disable=service]
+            [--allow-override=service] [--forbid-override=service]
+            [--inetd | [--listen=host_or_ipaddr] [--port=n] [--user=user [--group=group]]
+            [directory...]
 
 DESCRIPTION
 -----------
 A really simple TCP git daemon that normally listens on port "DEFAULT_GIT_PORT"
-aka 9418. It waits for a connection, and will just execute "git-upload-pack"
-when it gets one.
-
-It's careful in that there's a magic request-line that gives the command and
-what directory to upload, and it verifies that the directory is OK.
+aka 9418.  It waits for a connection asking for a service, and will serve
+that service if it is enabled.
 
 It verifies that the directory has the magic file "git-daemon-export-ok", and
 it will refuse to export any git directory that hasn't explicitly been marked
@@ -28,7 +30,12 @@ for export this way (unless the '--export-all' parameter is specified). If you
 pass some directory paths as 'git-daemon' arguments, you can further restrict
 the offers to a whitelist comprising of those.
 
-This is ideally suited for read-only updates, i.e., pulling from git repositories.
+By default, only `upload-pack` service is enabled, which serves
+`git-fetch-pack` and `git-peek-remote` clients that are invoked
+from `git-fetch`, `git-ls-remote`, and `git-clone`.
+
+This is ideally suited for read-only updates, i.e., pulling from
+git repositories.
 
 OPTIONS
 -------
@@ -45,6 +52,16 @@ OPTIONS
        'git://example.com/hello.git', `git-daemon` will interpret the path
        as '/srv/git/hello.git'.
 
+--interpolated-path=pathtemplate::
+       To support virtual hosting, an interpolated path template can be
+       used to dynamically construct alternate paths.  The template
+       supports %H for the target hostname as supplied by the client but
+       converted to all lowercase, %CH for the canonical hostname,
+       %IP for the server's IP address, %P for the port number,
+       and %D for the absolute path of the named repository.
+       After interpolation, the path is validated against the directory
+       whitelist.
+
 --export-all::
        Allow pulling from all directories that look like GIT repositories
        (have the 'objects' and 'refs' subdirectories), even if they
@@ -52,9 +69,17 @@ OPTIONS
 
 --inetd::
        Have the server run as an inetd service. Implies --syslog.
+       Incompatible with --port, --listen, --user and --group options.
 
---port::
-       Listen on an alternative port.
+--listen=host_or_ipaddr::
+       Listen on an a specific IP address or hostname.  IP addresses can
+       be either an IPv4 address or an IPV6 address if supported.  If IPv6
+       is not supported, then --listen=hostname is also not supported and
+       --listen must be given an IPv4 address.
+       Incompatible with '--inetd' option.
+
+--port=n::
+       Listen on an alternative port.  Incompatible with '--inetd' option.
 
 --init-timeout::
        Timeout between the moment the connection is established and the
@@ -93,11 +118,101 @@ OPTIONS
 --pid-file=file::
        Save the process id in 'file'.
 
+--user=user, --group=group::
+       Change daemon's uid and gid before entering the service loop.
+       When only `--user` is given without `--group`, the
+       primary group ID for the user is used.  The values of
+       the option are given to `getpwnam(3)` and `getgrnam(3)`
+       and numeric IDs are not supported.
++
+Giving these options is an error when used with `--inetd`; use
+the facility of inet daemon to achieve the same before spawning
+`git-daemon` if needed.
+
+--enable-service, --disable-service::
+       Enable/disable the service site-wide per default.  Note
+       that a service disabled site-wide can still be enabled
+       per repository if it is marked overridable and the
+       repository enables the service with an configuration
+       item.
+
+--allow-override, --forbid-override::
+       Allow/forbid overriding the site-wide default with per
+       repository configuration.  By default, all the services
+       are overridable.
+
 <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.
 
+SERVICES
+--------
+
+upload-pack::
+       This serves `git-fetch-pack` and `git-peek-remote`
+       clients.  It is enabled by default, but a repository can
+       disable it by setting `daemon.uploadpack` configuration
+       item to `false`.
+
+EXAMPLES
+--------
+git-daemon as inetd server::
+       To set up `git-daemon` as an inetd service that handles any
+       repository under the whitelisted set of directories, /pub/foo
+       and /pub/bar, place an entry like the following into
+       /etc/inetd all on one line:
++
+------------------------------------------------
+       git stream tcp nowait nobody  /usr/bin/git-daemon
+               git-daemon --inetd --verbose
+               --syslog --export-all
+               /pub/foo /pub/bar
+------------------------------------------------
+
+
+git-daemon as inetd server for virtual hosts::
+       To set up `git-daemon` as an inetd service that handles
+       repositories for different virtual hosts, `www.example.com`
+       and `www.example.org`, place an entry like the following into
+       `/etc/inetd` all on one line:
++
+------------------------------------------------
+       git stream tcp nowait nobody /usr/bin/git-daemon
+               git-daemon --inetd --verbose
+               --syslog --export-all
+               --interpolated-path=/pub/%H%D
+               /pub/www.example.org/software
+               /pub/www.example.com/software
+               /software
+------------------------------------------------
++
+In this example, the root-level directory `/pub` will contain
+a subdirectory for each virtual host name supported.
+Further, both hosts advertise repositories simply as
+`git://www.example.com/software/repo.git`.  For pre-1.4.0
+clients, a symlink from `/software` into the appropriate
+default repository could be made as well.
+
+
+git-daemon as regular daemon for virtual hosts::
+       To set up `git-daemon` as a regular, non-inetd service that
+       handles repositories for multiple virtual hosts based on
+       their IP addresses, start the daemon like this:
++
+------------------------------------------------
+       git-daemon --verbose --export-all
+               --interpolated-path=/pub/%IP/%D
+               /pub/192.168.1.200/software
+               /pub/10.10.220.23/software
+------------------------------------------------
++
+In this example, the root-level directory `/pub` will contain
+a subdirectory for each virtual host IP address supported.
+Repositories can still be accessed by hostname though, assuming
+they correspond to these IP addresses.
+
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>, YOSHIFUJI Hideaki
index dc76833..d8af4d9 100644 (file)
@@ -11,7 +11,7 @@ SYNOPSIS
 [verse]
 'git-grep' [--cached]
           [-a | --text] [-I] [-i | --ignore-case] [-w | --word-regexp]
-          [-v | --invert-match]
+          [-v | --invert-match] [-h|-H] [--full-name]
           [-E | --extended-regexp] [-G | --basic-regexp] [-F | --fixed-strings]
           [-n] [-l | --files-with-matches] [-L | --files-without-match]
           [-c | --count]
@@ -47,6 +47,19 @@ OPTIONS
 -v | --invert-match::
        Select non-matching lines.
 
+-h | -H::
+       By default, the command shows the filename for each
+       match.  `-h` option is used to suppress this output.
+       `-H` is there for completeness and does not do anything
+       except it overrides `-h` given earlier on the command
+       line.
+
+--full-name::
+       When run from a subdirectory, the command usually
+       outputs paths relative to the current directory.  This
+       option forces paths to be output relative to the project
+       top directory.
+
 -E | --extended-regexp | -G | --basic-regexp::
        Use POSIX extended/basic regexp for patterns.  Default
        is to use basic regexp.
index 63cd5da..ca7d09d 100644 (file)
@@ -48,6 +48,10 @@ is given:
  - 'all' (or 'world' or 'everybody'): Same as 'group', but make the repository
    readable by all users.
 
+By default, the configuration flag receive.denyNonFastforward is enabled
+in shared repositories, so that you cannot force a non fast-forwarding push
+into it.
+
 --
 
 
index ae4c1a2..c8a4c5a 100644 (file)
@@ -3,16 +3,19 @@ git-ls-remote(1)
 
 NAME
 ----
-git-ls-remote - Look at references other repository has
+git-ls-remote - List references in a remote repository
 
 
 SYNOPSIS
 --------
-'git-ls-remote' [--heads] [--tags] <repository> <refs>...
+[verse]
+'git-ls-remote' [--heads] [--tags]  [-u <exec> | --upload-pack <exec>]
+             <repository> <refs>...
 
 DESCRIPTION
 -----------
-Displays the references other repository has.
+Displays references available in a remote repository along with the associated
+commit IDs.
 
 
 OPTIONS
@@ -23,9 +26,16 @@ OPTIONS
        both, references stored in refs/heads and refs/tags are
        displayed.
 
+-u <exec>, --upload-pack=<exec>::
+       Specify the full path of gitlink:git-upload-pack[1] on the remote
+       host. This allows listing references from repositories accessed via
+       SSH and where the SSH deamon does not use the PATH configured by the
+       user. Also see the '--exec' option for gitlink:git-peek-remote[1].
+
 <repository>::
        Location of the repository.  The shorthand defined in
-       $GIT_DIR/branches/ can be used.
+       $GIT_DIR/branches/ can be used. Use "." (dot) to list references in
+       the local repository.
 
 <refs>...::
        When unspecified, all references, after filtering done
index 4991f88..d4661dd 100644 (file)
@@ -11,7 +11,7 @@ SYNOPSIS
 [verse]
 'git-pack-objects' [-q] [--no-reuse-delta] [--non-empty]
        [--local] [--incremental] [--window=N] [--depth=N]
-       {--stdout | base-name} < object-list
+       [--revs [--unpacked | --all]*] [--stdout | base-name] < object-list
 
 
 DESCRIPTION
@@ -56,6 +56,24 @@ base-name::
        Write the pack contents (what would have been written to
        .pack file) out to the standard output.
 
+--revs::
+       Read the revision arguments from the standard input, instead of
+       individual object names.  The revision arguments are processed
+       the same way as gitlink:git-rev-list[1] with `--objects` flag
+       uses its `commit` arguments to build the list of objects it
+       outputs.  The objects on the resulting list are packed.
+
+--unpacked::
+       This implies `--revs`.  When processing the list of
+       revision arguments read from the standard input, limit
+       the objects packed to those that are not already packed.
+
+--all::
+       This implies `--revs`.  In addition to the list of
+       revision arguments read from the standard input, pretend
+       as if all refs under `$GIT_DIR/refs` are specifed to be
+       included.
+
 --window and --depth::
        These two options affects how the objects contained in
        the pack are stored using delta compression.  The
@@ -103,6 +121,7 @@ Documentation by Junio C Hamano
 
 See Also
 --------
+gitlink:git-rev-list[1]
 gitlink:git-repack[1]
 gitlink:git-prune-packed[1]
 
index f9457d4..0dfadc2 100644 (file)
@@ -73,6 +73,8 @@ packed and is served via a dumb transport.
 There are other real-world examples of using update and
 post-update hooks found in the Documentation/howto directory.
 
+git-receive-pack honours the receive.denyNonFastforwards flag, which
+tells it if updates to a ref should be denied if they are not fast-forwards.
 
 OPTIONS
 -------
index 9516227..49f7e0a 100644 (file)
@@ -9,7 +9,7 @@ objects into pack files.
 
 SYNOPSIS
 --------
-'git-repack' [-a] [-d] [-f] [-l] [-n] [-q]
+'git-repack' [-a] [-d] [-f] [-l] [-n] [-q] [--window=N] [--depth=N]
 
 DESCRIPTION
 -----------
@@ -56,6 +56,16 @@ OPTIONS
         Do not update the server information with
         `git update-server-info`.
 
+--window=[N], --depth=[N]::
+       These two options affects how the objects contained in the pack are
+       stored using delta compression. The objects are first internally
+       sorted by type, size and optionally names and compared against the
+       other objects within `--window` to see if using delta compression saves
+       space. `--depth` limits the maximum delta depth; making it too deep
+       affects the performance on the unpacker side, because delta data needs
+       to be applied that many times to get to the necessary object.
+
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>
index b03d66f..8a1ab61 100644 (file)
@@ -54,7 +54,8 @@ OPTIONS
 
 --get::
        Get the value for a given key (optionally filtered by a regex
-       matching the value).
+       matching the value). Returns error code 1 if the key was not
+       found and error code 2 if multiple key values were found.
 
 --get-all::
        Like get, but does not fail if the number of values for the key
index dd9fff1..00a95e2 100644 (file)
@@ -17,8 +17,10 @@ SYNOPSIS
             [ \--remove-empty ]
             [ \--not ]
             [ \--all ]
+            [ \--stdin ]
             [ \--topo-order ]
             [ \--parents ]
+            [ \--(author|committer|grep)=<pattern> ]
             [ [\--objects | \--objects-edge] [ \--unpacked ] ]
             [ \--pretty | \--header ]
             [ \--bisect ]
@@ -27,116 +29,258 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
+
 Lists commit objects in reverse chronological order starting at the
 given commit(s), taking ancestry relationship into account.  This is
 useful to produce human-readable log output.
 
-Commits which are stated with a preceding '{caret}' cause listing to stop at
-that point. Their parents are implied. "git-rev-list foo bar {caret}baz" thus
+Commits which are stated with a preceding '{caret}' cause listing to
+stop at that point. Their parents are implied. Thus the following
+command:
+
+-----------------------------------------------------------------------
+       $ git-rev-list foo bar ^baz
+-----------------------------------------------------------------------
+
 means "list all the commits which are included in 'foo' and 'bar', but
 not in 'baz'".
 
-A special notation <commit1>..<commit2> can be used as a
-short-hand for {caret}<commit1> <commit2>.
+A special notation "'<commit1>'..'<commit2>'" can be used as a
+short-hand for "{caret}'<commit1>' '<commit2>'". For example, either of
+the following may be used interchangeably:
 
-Another special notation is <commit1>...<commit2> which is useful for
-merges.  The resulting set of commits is the symmetric difference
+-----------------------------------------------------------------------
+       $ git-rev-list origin..HEAD
+       $ git-rev-list HEAD ^origin
+-----------------------------------------------------------------------
+
+Another special notation is "'<commit1>'...'<commit2>'" which is useful
+for merges.  The resulting set of commits is the symmetric difference
 between the two operands.  The following two commands are equivalent:
 
-------------
-$ git-rev-list A B --not $(git-merge-base --all A B)
-$ git-rev-list A...B
-------------
+-----------------------------------------------------------------------
+       $ git-rev-list A B --not $(git-merge-base --all A B)
+       $ git-rev-list A...B
+-----------------------------------------------------------------------
+
+gitlink:git-rev-list[1] is a very essential git program, since it
+provides the ability to build and traverse commit ancestry graphs. For
+this reason, it has a lot of different options that enables it to be
+used by commands as different as gitlink:git-bisect[1] and
+gitlink:git-repack[1].
 
 OPTIONS
 -------
---pretty::
-       Print the contents of the commit changesets in human-readable form.
+
+Commit Formatting
+~~~~~~~~~~~~~~~~~
+
+Using these options, gitlink:git-rev-list[1] will act similar to the
+more specialized family of commit log tools: gitlink:git-log[1],
+gitlink:git-show[1], and gitlink:git-whatchanged[1]
+
+--pretty[='<format>']::
+
+       Pretty print the contents of the commit logs in a given format,
+       where '<format>' can be one of 'raw', 'medium', 'short', 'full',
+       and 'oneline'. When left out the format default to 'medium'.
+
+--relative-date::
+
+       Show dates relative to the current time, e.g. "2 hours ago".
+       Only takes effect for dates shown in human-readable format, such
+       as when using "--pretty".
 
 --header::
-       Print the contents of the commit in raw-format; each
-       record is separated with a NUL character.
+
+       Print the contents of the commit in raw-format; each record is
+       separated with a NUL character.
 
 --parents::
+
        Print the parents of the commit.
 
---objects::
-       Print the object IDs of any object referenced by the listed commits.
-       'git-rev-list --objects foo ^bar' thus means "send me all object IDs
-       which I need to download if I have the commit object 'bar', but
-       not 'foo'".
+Diff Formatting
+~~~~~~~~~~~~~~~
 
---objects-edge::
-       Similar to `--objects`, but also print the IDs of
-       excluded commits prefixed with a `-` character.  This is
-       used by `git-pack-objects` to build 'thin' pack, which
-       records objects in deltified form based on objects
-       contained in these excluded commits to reduce network
-       traffic.
+Below are listed options that control the formatting of diff output.
+Some of them are specific to gitlink:git-rev-list[1], however other diff
+options may be given. See gitlink:git-diff-files[1] for more options.
 
---unpacked::
-       Only useful with `--objects`; print the object IDs that
-       are not in packs.
+-c::
+
+       This flag changes the way a merge commit is displayed.  It shows
+       the differences from each of the parents to the merge result
+       simultaneously instead of showing pairwise diff between a parent
+       and the result one at a time. Furthermore, it lists only files
+       which were modified from all parents.
+
+--cc::
+
+       This flag implies the '-c' options and further compresses the
+       patch output by omitting hunks that show differences from only
+       one parent, or show the same change from all but one parent for
+       an Octopus merge.
+
+-r::
+
+       Show recursive diffs.
+
+-t::
+
+       Show the tree objects in the diff output. This implies '-r'.
+
+Commit Limiting
+~~~~~~~~~~~~~~~
+
+Besides specifying a range of commits that should be listed using the
+special notations explained in the description, additional commit
+limiting may be applied.
+
+--
+
+-n 'number', --max-count='number'::
 
---bisect::
-       Limit output to the one commit object which is roughly halfway
-       between the included and excluded commits. Thus, if 'git-rev-list
-       --bisect foo {caret}bar {caret}baz' outputs 'midpoint', the output
-       of 'git-rev-list foo {caret}midpoint' and 'git-rev-list midpoint
-       {caret}bar {caret}baz' would be of roughly the same length.
-       Finding the change
-       which introduces a regression is thus reduced to a binary search:
-       repeatedly generate and test new 'midpoint's until the commit chain
-       is of length one.
-
---max-count::
        Limit the number of commits output.
 
---max-age=timestamp, --min-age=timestamp::
+--since='date', --after='date'::
+
+       Show commits more recent than a specific date.
+
+--until='date', --before='date'::
+
+       Show commits older than a specific date.
+
+--max-age='timestamp', --min-age='timestamp'::
+
        Limit the commits output to specified time range.
 
---sparse::
-       When optional paths are given, the command outputs only
-       the commits that changes at least one of them, and also
-       ignores merges that do not touch the given paths.  This
-       flag makes the command output all eligible commits
-       (still subject to count and age limitation), but apply
-       merge simplification nevertheless.
+--author='pattern', --committer='pattern'::
+
+       Limit the commits output to ones with author/committer
+       header lines that match the specified pattern.
+
+--grep='pattern'::
+
+       Limit the commits output to ones with log message that
+       matches the specified pattern.
 
 --remove-empty::
+
        Stop when a given path disappears from the tree.
 
 --no-merges::
+
        Do not print commits with more than one parent.
 
 --not::
-       Reverses the meaning of the '{caret}' prefix (or lack
-       thereof) for all following revision specifiers, up to
-       the next `--not`.
+
+       Reverses the meaning of the '{caret}' prefix (or lack thereof)
+       for all following revision specifiers, up to the next '--not'.
 
 --all::
-       Pretend as if all the refs in `$GIT_DIR/refs/` are
-       listed on the command line as <commit>.
 
---topo-order::
-       By default, the commits are shown in reverse
-       chronological order.  This option makes them appear in
-       topological order (i.e. descendant commits are shown
-       before their parents).
+       Pretend as if all the refs in `$GIT_DIR/refs/` are listed on the
+       command line as '<commit>'.
+
+--stdin::
+
+       In addition to the '<commit>' listed on the command
+       line, read them from the standard input.
 
 --merge::
+
        After a failed merge, show refs that touch files having a
        conflict and don't exist on all heads to merge.
 
+--boundary::
+
+       Output uninteresting commits at the boundary, which are usually
+       not shown.
+
+--dense, --sparse::
+
+When optional paths are given, the default behaviour ('--dense') is to
+only output commits that changes at least one of them, and also ignore
+merges that do not touch the given paths.
+
+Use the '--sparse' flag to makes the command output all eligible commits
+(still subject to count and age limitation), but apply merge
+simplification nevertheless.
+
+--bisect::
+
+Limit output to the one commit object which is roughly halfway between
+the included and excluded commits. Thus, if
+
+-----------------------------------------------------------------------
+       $ git-rev-list --bisect foo ^bar ^baz
+-----------------------------------------------------------------------
+
+outputs 'midpoint', the output of the two commands
+
+-----------------------------------------------------------------------
+       $ git-rev-list foo ^midpoint
+       $ git-rev-list midpoint ^bar ^baz
+-----------------------------------------------------------------------
+
+would be of roughly the same length.  Finding the change which
+introduces a regression is thus reduced to a binary search: repeatedly
+generate and test new 'midpoint's until the commit chain is of length
+one.
+
+--
+
+Commit Ordering
+~~~~~~~~~~~~~~~
+
+By default, the commits are shown in reverse chronological order.
+
+--topo-order::
+
+       This option makes them appear in topological order (i.e.
+       descendant commits are shown before their parents).
+
+--date-order::
+
+       This option is similar to '--topo-order' in the sense that no
+       parent comes before all of its children, but otherwise things
+       are still ordered in the commit timestamp order.
+
+Object Traversal
+~~~~~~~~~~~~~~~~
+
+These options are mostly targeted for packing of git repositories.
+
+--objects::
+
+       Print the object IDs of any object referenced by the listed
+       commits.  'git-rev-list --objects foo ^bar' thus means "send me
+       all object IDs which I need to download if I have the commit
+       object 'bar', but not 'foo'".
+
+--objects-edge::
+
+       Similar to '--objects', but also print the IDs of excluded
+       commits prefixed with a "-" character.  This is used by
+       gitlink:git-pack-objects[1] to build "thin" pack, which records
+       objects in deltified form based on objects contained in these
+       excluded commits to reduce network traffic.
+
+--unpacked::
+
+       Only useful with '--objects'; print the object IDs that are not
+       in packs.
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>
 
 Documentation
 --------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+Documentation by David Greaves, Junio C Hamano, Jonas Fonseca
+and the git-list <git@vger.kernel.org>.
 
 GIT
 ---
 Part of the gitlink:git[7] suite
-
index 7d86809..1cfa3e3 100644 (file)
@@ -12,10 +12,8 @@ SYNOPSIS
 DESCRIPTION
 -----------
 git-svn is a simple conduit for changesets between a single Subversion
-branch and git.
-
-git-svn is not to be confused with git-svnimport.  The were designed
-with very different goals in mind.
+branch and git. It is not to be confused with gitlink:git-svnimport[1].
+They were designed with very different goals in mind.
 
 git-svn is designed for an individual developer who wants a
 bidirectional flow of changesets between a single branch in Subversion
@@ -34,26 +32,38 @@ git-svnimport is designed for.
 
 COMMANDS
 --------
-init::
+--
+
+'init'::
        Creates an empty git repository with additional metadata
        directories for git-svn.  The Subversion URL must be specified
        as a command-line argument.
 
-fetch::
-       Fetch unfetched revisions from the Subversion URL we are
-       tracking.  refs/remotes/git-svn will be updated to the
-       latest revision.
+'fetch'::
+
+Fetch unfetched revisions from the Subversion URL we are
+tracking.  refs/remotes/git-svn will be updated to the
+latest revision.
+
+Note: You should never attempt to modify the remotes/git-svn
+branch outside of git-svn.  Instead, create a branch from
+remotes/git-svn and work on that branch.  Use the 'commit'
+command (see below) to write git commits back to
+remotes/git-svn.
 
-       Note: You should never attempt to modify the remotes/git-svn
-       branch outside of git-svn.  Instead, create a branch from
-       remotes/git-svn and work on that branch.  Use the 'commit'
-       command (see below) to write git commits back to
-       remotes/git-svn.
+See '<<fetch-args,Additional Fetch Arguments>>' if you are interested in
+manually joining branches on commit.
 
-       See 'Additional Fetch Arguments' if you are interested in
-       manually joining branches on commit.
+'dcommit'::
+       Commit all diffs from the current HEAD directly to the SVN
+       repository, and then rebase or reset (depending on whether or
+       not there is a diff between SVN and HEAD).  It is recommended
+       that you run git-svn fetch and rebase (not pull) your commits
+       against the latest changes in the SVN repository.
+       This is advantageous over 'commit' (below) because it produces
+       cleaner, more linear history.
 
-commit::
+'commit'::
        Commit specified commit or tree objects to SVN.  This relies on
        your imported fetch data being up-to-date.  This makes
        absolutely no attempts to do patching when committing to SVN, it
@@ -61,9 +71,9 @@ commit::
        commit.  All merging is assumed to have taken place
        independently of git-svn functions.
 
-rebuild::
+'rebuild'::
        Not a part of daily usage, but this is a useful command if
-       you've just cloned a repository (using git-clone) that was
+       you've just cloned a repository (using gitlink:git-clone[1]) that was
        tracked with git-svn.  Unfortunately, git-clone does not clone
        git-svn metadata and the svn working tree that git-svn uses for
        its operations.  This rebuilds the metadata so git-svn can
@@ -71,130 +81,182 @@ rebuild::
        specified at the command-line if the directory/repository you're
        tracking has moved or changed protocols.
 
-show-ignore::
+'show-ignore'::
        Recursively finds and lists the svn:ignore property on
        directories.  The output is suitable for appending to
        the $GIT_DIR/info/exclude file.
 
+--
+
 OPTIONS
 -------
+--
+
 -r <ARG>::
 --revision <ARG>::
-       Only used with the 'fetch' command.
 
-       Takes any valid -r<argument> svn would accept and passes it
-       directly to svn. -r<ARG1>:<ARG2> ranges and "{" DATE "}" syntax
-       is also supported.  This is passed directly to svn, see svn
-       documentation for more details.
+Only used with the 'fetch' command.
 
-       This can allow you to make partial mirrors when running fetch.
+Takes any valid -r<argument> svn would accept and passes it
+directly to svn. -r<ARG1>:<ARG2> ranges and "{" DATE "}" syntax
+is also supported.  This is passed directly to svn, see svn
+documentation for more details.
+
+This can allow you to make partial mirrors when running fetch.
 
 -::
 --stdin::
-       Only used with the 'commit' command.
 
-       Read a list of commits from stdin and commit them in reverse
-       order.  Only the leading sha1 is read from each line, so
-       git-rev-list --pretty=oneline output can be used.
+Only used with the 'commit' command.
+
+Read a list of commits from stdin and commit them in reverse
+order.  Only the leading sha1 is read from each line, so
+git-rev-list --pretty=oneline output can be used.
 
 --rmdir::
-       Only used with the 'commit' command.
 
-       Remove directories from the SVN tree if there are no files left
-       behind.  SVN can version empty directories, and they are not
-       removed by default if there are no files left in them.  git
-       cannot version empty directories.  Enabling this flag will make
-       the commit to SVN act like git.
+Only used with the 'commit' command.
 
-       repo-config key: svn.rmdir
+Remove directories from the SVN tree if there are no files left
+behind.  SVN can version empty directories, and they are not
+removed by default if there are no files left in them.  git
+cannot version empty directories.  Enabling this flag will make
+the commit to SVN act like git.
+
+repo-config key: svn.rmdir
 
 -e::
 --edit::
-       Only used with the 'commit' command.
 
-       Edit the commit message before committing to SVN.  This is off by
-       default for objects that are commits, and forced on when committing
-       tree objects.
+Only used with the 'commit' command.
+
+Edit the commit message before committing to SVN.  This is off by
+default for objects that are commits, and forced on when committing
+tree objects.
 
-       repo-config key: svn.edit
+repo-config key: svn.edit
 
 -l<num>::
 --find-copies-harder::
-       Both of these are only used with the 'commit' command.
 
-       They are both passed directly to git-diff-tree see
-       git-diff-tree(1) for more information.
+Both of these are only used with the 'commit' command.
 
-       repo-config key: svn.l
-       repo-config key: svn.findcopiesharder
+They are both passed directly to git-diff-tree see
+gitlink:git-diff-tree[1] for more information.
+
+[verse]
+repo-config key: svn.l
+repo-config key: svn.findcopiesharder
 
 -A<filename>::
 --authors-file=<filename>::
 
-       Syntax is compatible with the files used by git-svnimport and
-       git-cvsimport:
+Syntax is compatible with the files used by git-svnimport and
+git-cvsimport:
 
 ------------------------------------------------------------------------
-loginname = Joe User <user@example.com>
+       loginname = Joe User <user@example.com>
 ------------------------------------------------------------------------
 
-       If this option is specified and git-svn encounters an SVN
-       committer name that does not exist in the authors-file, git-svn
-       will abort operation. The user will then have to add the
-       appropriate entry.  Re-running the previous git-svn command
-       after the authors-file is modified should continue operation.
+If this option is specified and git-svn encounters an SVN
+committer name that does not exist in the authors-file, git-svn
+will abort operation. The user will then have to add the
+appropriate entry.  Re-running the previous git-svn command
+after the authors-file is modified should continue operation.
+
+repo-config key: svn.authors-file
+
+-m::
+--merge::
+-s<strategy>::
+--strategy=<strategy>::
+
+These are only used with the 'dcommit' command.
+
+Passed directly to git-rebase when using 'dcommit' if a
+'git-reset' cannot be used (see dcommit).
+
+-n::
+--dry-run::
 
-       repo-config key: svn.authors-file
+This is only used with the 'dcommit' command.
+
+Print out the series of git arguments that would show
+which diffs would be committed to SVN.
+
+--
 
 ADVANCED OPTIONS
 ----------------
+--
+
 -b<refname>::
 --branch <refname>::
-       Used with 'fetch' or 'commit'.
+Used with 'fetch' or 'commit'.
 
-       This can be used to join arbitrary git branches to remotes/git-svn
-       on new commits where the tree object is equivalent.
+This can be used to join arbitrary git branches to remotes/git-svn
+on new commits where the tree object is equivalent.
 
-       When used with different GIT_SVN_ID values, tags and branches in
-       SVN can be tracked this way, as can some merges where the heads
-       end up having completely equivalent content.  This can even be
-       used to track branches across multiple SVN _repositories_.
+When used with different GIT_SVN_ID values, tags and branches in
+SVN can be tracked this way, as can some merges where the heads
+end up having completely equivalent content.  This can even be
+used to track branches across multiple SVN _repositories_.
 
-       This option may be specified multiple times, once for each
-       branch.
+This option may be specified multiple times, once for each
+branch.
 
-       repo-config key: svn.branch
+repo-config key: svn.branch
 
 -i<GIT_SVN_ID>::
 --id <GIT_SVN_ID>::
-       This sets GIT_SVN_ID (instead of using the environment).  See
-       the section on "Tracking Multiple Repositories or Branches" for
-       more information on using GIT_SVN_ID.
+
+This sets GIT_SVN_ID (instead of using the environment).  See the
+section on
+'<<tracking-multiple-repos,Tracking Multiple Repositories or Branches>>'
+for more information on using GIT_SVN_ID.
+
+--
 
 COMPATIBILITY OPTIONS
 ---------------------
+--
+
 --upgrade::
-       Only used with the 'rebuild' command.
+Only used with the 'rebuild' command.
 
-       Run this if you used an old version of git-svn that used
-       "git-svn-HEAD" instead of "remotes/git-svn" as the branch
-       for tracking the remote.
+Run this if you used an old version of git-svn that used
+"git-svn-HEAD" instead of "remotes/git-svn" as the branch
+for tracking the remote.
 
 --no-ignore-externals::
-       Only used with the 'fetch' and 'rebuild' command.
+Only used with the 'fetch' and 'rebuild' command.
+
+By default, git-svn passes --ignore-externals to svn to avoid
+fetching svn:external trees into git.  Pass this flag to enable
+externals tracking directly via git.
+
+Versions of svn that do not support --ignore-externals are
+automatically detected and this flag will be automatically
+enabled for them.
 
-       By default, git-svn passes --ignore-externals to svn to avoid
-       fetching svn:external trees into git.  Pass this flag to enable
-       externals tracking directly via git.
+Otherwise, do not enable this flag unless you know what you're
+doing.
 
-       Versions of svn that do not support --ignore-externals are
-       automatically detected and this flag will be automatically
-       enabled for them.
+repo-config key: svn.noignoreexternals
 
-       Otherwise, do not enable this flag unless you know what you're
-       doing.
+--ignore-nodate::
+Only used with the 'fetch' command.
 
-       repo-config key: svn.noignoreexternals
+By default git-svn will crash if it tries to import a revision
+from SVN which has '(no date)' listed as the date of the revision.
+This is repository corruption on SVN's part, plain and simple.
+But sometimes you really need those revisions anyway.
+
+If supplied git-svn will convert '(no date)' entries to the UNIX
+epoch (midnight on Jan. 1, 1970).  Yes, that's probably very wrong.
+SVN was very wrong.
+
+--
 
 Basic Examples
 ~~~~~~~~~~~~~~
@@ -212,12 +274,26 @@ Tracking and contributing to an Subversion managed-project:
        git-svn commit <tree-ish> [<tree-ish_2> ...]
 # Commit all the git commits from my-branch that don't exist in SVN:
        git-svn commit remotes/git-svn..my-branch
-# Something is committed to SVN, pull the latest into your branch:
-       git-svn fetch && git pull . remotes/git-svn
+# Something is committed to SVN, rebase the latest into your branch:
+       git-svn fetch && git rebase remotes/git-svn
 # Append svn:ignore settings to the default git exclude file:
        git-svn show-ignore >> .git/info/exclude
 ------------------------------------------------------------------------
 
+REBASE VS. PULL
+---------------
+
+Originally, git-svn recommended that the remotes/git-svn branch be
+pulled from.  This is because the author favored 'git-svn commit B'
+to commit a single head rather than the 'git-svn commit A..B' notation
+to commit multiple commits.
+
+If you use 'git-svn commit A..B' to commit several diffs and you do not
+have the latest remotes/git-svn merged into my-branch, you should use
+'git rebase' to update your work branch instead of 'git pull'.  'pull'
+can cause non-linear history to be flattened when committing into SVN,
+which can lead to merge commits reversing previous commits in SVN.
+
 DESIGN PHILOSOPHY
 -----------------
 Merge tracking in Subversion is lacking and doing branched development
@@ -226,6 +302,7 @@ any automated merge/branch tracking on the Subversion side and leaves it
 entirely up to the user on the git side.  It's simply not worth it to do
 a useful translation when the original signal is weak.
 
+[[tracking-multiple-repos]]
 TRACKING MULTIPLE REPOSITORIES OR BRANCHES
 ------------------------------------------
 This is for advanced users, most users should ignore this section.
@@ -241,6 +318,7 @@ invocation.  The interface branch will be remotes/$GIT_SVN_ID, instead of
 remotes/git-svn.  Any remotes/$GIT_SVN_ID branch should never be modified
 by the user outside of git-svn commands.
 
+[[fetch-args]]
 ADDITIONAL FETCH ARGUMENTS
 --------------------------
 This is for advanced users, most users should ignore this section.
@@ -251,11 +329,15 @@ optionally be specified in the form of sha1 hex sums at the
 command-line.  Unfetched SVN revisions may also be tied to particular
 git commits with the following syntax:
 
+------------------------------------------------
        svn_revision_number=git_commit_sha1
+------------------------------------------------
 
-This allows you to tie unfetched SVN revision 375 to your current HEAD::
+This allows you to tie unfetched SVN revision 375 to your current HEAD:
 
-       `git-svn fetch 375=$(git-rev-parse HEAD)`
+------------------------------------------------
+       git-svn fetch 375=$(git-rev-parse HEAD)
+------------------------------------------------
 
 Advanced Example: Tracking a Reorganized Repository
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -310,6 +392,10 @@ the possible corner cases (git doesn't do it, either).  Renamed and
 copied files are fully supported if they're similar enough for git to
 detect them.
 
+SEE ALSO
+--------
+gitlink:git-rebase[1]
+
 Author
 ------
 Written by Eric Wong <normalperson@yhbt.net>.
index 1e1c7fa..74a6fdd 100644 (file)
@@ -12,6 +12,9 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
+THIS COMMAND IS DEPRECATED.  Use `git-archive` with `--format=tar`
+option instead.
+
 Creates a tar archive containing the tree structure for the named tree.
 When <base> is specified it is added as a leading path to the files in the
 generated tar archive.
index c20b38b..ff6184b 100644 (file)
@@ -8,7 +8,7 @@ git-unpack-objects - Unpack objects from a packed archive
 
 SYNOPSIS
 --------
-'git-unpack-objects' [-n] [-q] <pack-file
+'git-unpack-objects' [-n] [-q] [-r] <pack-file
 
 
 DESCRIPTION
@@ -34,6 +34,12 @@ OPTIONS
        The command usually shows percentage progress.  This
        flag suppresses it.
 
+-r::
+       When unpacking a corrupt packfile, the command dies at
+       the first corruption.  This flag tells it to keep going
+       and make the best effort to recover as many objects as
+       possible.
+
 
 Author
 ------
index 3ae6e74..41bb7e1 100644 (file)
@@ -15,7 +15,7 @@ SYNOPSIS
             [--cacheinfo <mode> <object> <file>]\*
             [--chmod=(+|-)x]
             [--assume-unchanged | --no-assume-unchanged]
-            [--really-refresh] [--unresolve] [--again]
+            [--really-refresh] [--unresolve] [--again | -g]
             [--info-only] [--index-info]
             [-z] [--stdin]
             [--verbose]
@@ -80,7 +80,7 @@ OPTIONS
        filesystem that has very slow lstat(2) system call
        (e.g. cifs).
 
---again::
+--again, -g::
        Runs `git-update-index` itself on the paths whose index
        entries are different from those from the `HEAD` commit.
 
diff --git a/Documentation/git-upload-archive.txt b/Documentation/git-upload-archive.txt
new file mode 100644 (file)
index 0000000..388bb53
--- /dev/null
@@ -0,0 +1,37 @@
+git-upload-archive(1)
+====================
+
+NAME
+----
+git-upload-archive - Send archive
+
+
+SYNOPSIS
+--------
+'git-upload-archive' <directory>
+
+DESCRIPTION
+-----------
+Invoked by 'git-archive --remote' and sends a generated archive to the
+other end over the git protocol.
+
+This command is usually not invoked directly by the end user.  The UI
+for the protocol is on the 'git-archive' side, and the program pair
+is meant to be used to get an archive from a remote repository.
+
+OPTIONS
+-------
+<directory>::
+       The repository to get a tar archive from.
+
+Author
+------
+Written by Franck Bui-Huu.
+
+Documentation
+--------------
+Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the gitlink:git[7] suite
diff --git a/Documentation/git-upload-tar.txt b/Documentation/git-upload-tar.txt
deleted file mode 100644 (file)
index 394af62..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-git-upload-tar(1)
-=================
-
-NAME
-----
-git-upload-tar - Send tar archive
-
-
-SYNOPSIS
---------
-'git-upload-tar' <directory>
-
-DESCRIPTION
------------
-Invoked by 'git-tar-tree --remote' and sends a generated tar archive
-to the other end over the git protocol.
-
-This command is usually not invoked directly by the end user.
-The UI for the protocol is on the 'git-tar-tree' side, and the
-program pair is meant to be used to get a tar archive from a
-remote repository.
-
-
-OPTIONS
--------
-<directory>::
-       The repository to get a tar archive from.
-
-Author
-------
-Written by Junio C Hamano <junio@kernel.org>
-
-Documentation
---------------
-Documentation by Junio C Hamano.
-
-GIT
----
-Part of the gitlink:git[7] suite
index bcf187a..2135b65 100644 (file)
@@ -8,8 +8,9 @@ git - the stupid content tracker
 
 SYNOPSIS
 --------
+[verse]
 'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate]
-       [--bare] [--git-dir=GIT_DIR] [--help] COMMAND [ARGS]
+    [--bare] [--git-dir=GIT_DIR] [--help] COMMAND [ARGS]
 
 DESCRIPTION
 -----------
@@ -246,10 +247,6 @@ gitlink:git-upload-pack[1]::
        Invoked by 'git-fetch-pack' to push
        what are asked for.
 
-gitlink:git-upload-tar[1]::
-       Invoked by 'git-tar-tree --remote' to return the tar
-       archive the other end asked for.
-
 
 High-level commands (porcelain)
 -------------------------------
@@ -269,6 +266,9 @@ gitlink:git-am[1]::
 gitlink:git-applymbox[1]::
        Apply patches from a mailbox, original version by Linus.
 
+gitlink:git-archive[1]::
+       Creates an archive of files from a named tree.
+
 gitlink:git-bisect[1]::
        Find the change that introduced a bug by binary search.
 
@@ -302,6 +302,9 @@ gitlink:git-format-patch[1]::
 gitlink:git-grep[1]::
        Print lines matching a pattern.
 
+gitlink:gitk[1]::
+       The git repository browser.
+
 gitlink:git-log[1]::
        Shows commit logs.
 
@@ -482,13 +485,6 @@ gitlink:git-stripspace[1]::
        Filter out empty lines.
 
 
-Commands not yet documented
----------------------------
-
-gitlink:gitk[1]::
-       The gitk repository browser.
-
-
 Configuration Mechanism
 -----------------------
 
@@ -633,10 +629,22 @@ git Diffs
 
 other
 ~~~~~
+'GIT_PAGER'::
+       This environment variable overrides `$PAGER`.
+
 'GIT_TRACE'::
-       If this variable is set git will print `trace:` messages on
+       If this variable is set to "1", "2" or "true" (comparison
+       is case insensitive), git will print `trace:` messages on
        stderr telling about alias expansion, built-in command
        execution and external command execution.
+       If this variable is set to an integer value greater than 1
+       and lower than 10 (strictly) then git will interpret this
+       value as an open file descriptor and will try to write the
+       trace messages into this file descriptor.
+       Alternatively, if this variable is set to an absolute path
+       (starting with a '/' character), git will interpret this
+       as a file path and will try to write the trace messages
+       into it.
 
 Discussion[[Discussion]]
 ------------------------
index cb482bf..f1aeb07 100644 (file)
@@ -3,26 +3,56 @@ gitk(1)
 
 NAME
 ----
-gitk - Some git command not yet documented.
-
+gitk - git repository browser
 
 SYNOPSIS
 --------
-'gitk' [ --option ] <args>...
+'gitk' [<option>...] [<revs>] [--] [<path>...]
 
 DESCRIPTION
 -----------
-Does something not yet documented.
+Displays changes in a repository or a selected set of commits. This includes
+visualizing the commit graph, showing information related to each commit, and
+the files in the trees of each revision.
 
+Historically, gitk was the first repository browser. It's written in tcl/tk
+and started off in a separate repository but was later merged into the main
+git repository.
 
 OPTIONS
 -------
---option::
-       Some option not yet documented.
+To control which revisions to shown, the command takes options applicable to
+the gitlink:git-rev-list[1] command. This manual page describes only the most
+frequently used options.
+
+-n <number>, --max-count=<number>::
+
+       Limits the number of commits to show.
+
+--since=<date>::
+
+       Show commits more recent than a specific date.
+
+--until=<date>::
+
+       Show commits older than a specific date.
+
+--all::
+
+       Show all branches.
 
-<args>...::
-       Some argument not yet documented.
+<revs>::
 
+       Limit the revisions to show. This can be either a single revision
+       meaning show from the given revision and back, or it can be a range in
+       the form "'<from>'..'<to>'" to show all revisions between '<from>' and
+       back to '<to>'. Note, more advanced revision selection can be applied.
+
+<path>::
+
+       Limit commits to the ones touching files in the given paths. Note, to
+       avoid ambiguity wrt. revision names use "--" to separate the paths
+       from any preceeding options.
 
 Examples
 --------
@@ -37,13 +67,32 @@ gitk --since="2 weeks ago" \-- gitk::
        The "--" is necessary to avoid confusion with the *branch* named
        'gitk'
 
+gitk --max-count=100 --all -- Makefile::
+
+       Show at most 100 changes made to the file 'Makefile'. Instead of only
+       looking for changes in the current branch look in all branches.
+
+See Also
+--------
+'qgit(1)'::
+       A repository browser written in C++ using Qt.
+
+'gitview(1)'::
+       A repository browser written in Python using Gtk. It's based on
+       'bzrk(1)' and distributed in the contrib area of the git repository.
+
+'tig(1)'::
+       A minimal repository browser and git tool output highlighter written
+       in C using Ncurses.
+
 Author
 ------
-Written by Paul Mackerras <paulus@samba.org>
+Written by Paul Mackerras <paulus@samba.org>.
 
 Documentation
 --------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
+Documentation by Junio C Hamano, Jonas Fonseca, and the git-list
+<git@vger.kernel.org>.
 
 GIT
 ---
index 898b4aa..517f49b 100644 (file)
@@ -5,8 +5,7 @@ Hooks are little scripts you can place in `$GIT_DIR/hooks`
 directory to trigger action at certain points.  When
 `git-init-db` is run, a handful example hooks are copied in the
 `hooks` directory of the new repository, but by default they are
-all disabled.  To enable a hook, make it executable with `chmod
-+x`.
+all disabled.  To enable a hook, make it executable with `chmod +x`.
 
 This document describes the currently defined hooks.
 
@@ -16,16 +15,16 @@ applypatch-msg
 This hook is invoked by `git-applypatch` script, which is
 typically invoked by `git-applymbox`.  It takes a single
 parameter, the name of the file that holds the proposed commit
-log message.  Exiting with non-zero status causes the
-'git-applypatch' to abort before applying the patch.
+log message.  Exiting with non-zero status causes
+`git-applypatch` to abort before applying the patch.
 
 The hook is allowed to edit the message file in place, and can
 be used to normalize the message into some project standard
 format (if the project has one). It can also be used to refuse
 the commit after inspecting the message file.
 
-The default applypatch-msg hook, when enabled, runs the
-commit-msg hook, if the latter is enabled.
+The default 'applypatch-msg' hook, when enabled, runs the
+'commit-msg' hook, if the latter is enabled.
 
 pre-applypatch
 --------------
@@ -39,8 +38,8 @@ after application of the patch not committed.
 It can be used to inspect the current working tree and refuse to
 make a commit if it does not pass certain test.
 
-The default pre-applypatch hook, when enabled, runs the
-pre-commit hook, if the latter is enabled.
+The default 'pre-applypatch' hook, when enabled, runs the
+'pre-commit' hook, if the latter is enabled.
 
 post-applypatch
 ---------------
@@ -61,9 +60,9 @@ invoked before obtaining the proposed commit log message and
 making a commit.  Exiting with non-zero status from this script
 causes the `git-commit` to abort.
 
-The default pre-commit hook, when enabled, catches introduction
+The default 'pre-commit' hook, when enabled, catches introduction
 of lines with trailing whitespaces and aborts the commit when
-a such line is found.
+such a line is found.
 
 commit-msg
 ----------
@@ -79,8 +78,8 @@ be used to normalize the message into some project standard
 format (if the project has one). It can also be used to refuse
 the commit after inspecting the message file.
 
-The default commit-msg hook, when enabled, detects duplicate
-Signed-off-by: lines, and aborts the commit when one is found.
+The default 'commit-msg' hook, when enabled, detects duplicate
+"Signed-off-by" lines, and aborts the commit if one is found.
 
 post-commit
 -----------
@@ -91,23 +90,24 @@ parameter, and is invoked after a commit is made.
 This hook is meant primarily for notification, and cannot affect
 the outcome of `git-commit`.
 
-The default post-commit hook, when enabled, demonstrates how to
+The default 'post-commit' hook, when enabled, demonstrates how to
 send out a commit notification e-mail.
 
 update
 ------
 
 This hook is invoked by `git-receive-pack` on the remote repository,
-which is happens when a `git push` is done on a local repository.
+which happens when a `git push` is done on a local repository.
 Just before updating the ref on the remote repository, the update hook
 is invoked.  Its exit status determines the success or failure of
 the ref update.
 
 The hook executes once for each ref to be updated, and takes
 three parameters:
-    - the name of the ref being updated,
-    - the old object name stored in the ref,
-    - and the new objectname to be stored in the ref.
+
+ - the name of the ref being updated,
+ - the old object name stored in the ref,
+ - and the new objectname to be stored in the ref.
 
 A zero exit from the update hook allows the ref to be updated.
 Exiting with a non-zero status prevents `git-receive-pack`
@@ -126,16 +126,16 @@ Another use suggested on the mailing list is to use this hook to
 implement access control which is finer grained than the one
 based on filesystem group.
 
-The standard output of this hook is sent to /dev/null; if you
-want to report something to the git-send-pack on the other end,
-you can redirect your output to your stderr.
+The standard output of this hook is sent to `/dev/null`; if you
+want to report something to the `git-send-pack` on the other end,
+you can redirect your output to your `stderr`.
 
 
 post-update
 -----------
 
 This hook is invoked by `git-receive-pack` on the remote repository,
-which is happens when a `git push` is done on a local repository.
+which happens when a `git push` is done on a local repository.
 It executes on the remote repository once after all the refs have
 been updated.
 
@@ -145,16 +145,16 @@ name of ref that was actually updated.
 This hook is meant primarily for notification, and cannot affect
 the outcome of `git-receive-pack`.
 
-The post-update hook can tell what are the heads that were pushed,
+The 'post-update' hook can tell what are the heads that were pushed,
 but it does not know what their original and updated values are,
 so it is a poor place to do log old..new.
 
-The default post-update hook, when enabled, runs
+When enabled, the default 'post-update' hook runs
 `git-update-server-info` to keep the information used by dumb
-transports (e.g., http) up-to-date.  If you are publishing
-a git repository that is accessible via http, you should
+transports (e.g., HTTP) up-to-date.  If you are publishing
+a git repository that is accessible via HTTP, you should
 probably enable this hook.
 
-The standard output of this hook is sent to /dev/null; if you
-want to report something to the git-send-pack on the other end,
-you can redirect your output to your stderr.
+The standard output of this hook is sent to `/dev/null`; if you
+want to report something to the `git-send-pack` on the other end,
+you can redirect your output to your `stderr`.
diff --git a/Documentation/technical/racy-git.txt b/Documentation/technical/racy-git.txt
new file mode 100644 (file)
index 0000000..7597d04
--- /dev/null
@@ -0,0 +1,193 @@
+Use of index and Racy git problem
+=================================
+
+Background
+----------
+
+The index is one of the most important data structure in git.
+It represents a virtual working tree state by recording list of
+paths and their object names and serves as a staging area to
+write out the next tree object to be committed.  The state is
+"virtual" in the sense that it does not necessarily have to, and
+often does not, match the files in the working tree.
+
+There are cases git needs to examine the differences between the
+virtual working tree state in the index and the files in the
+working tree.  The most obvious case is when the user asks `git
+diff` (or its low level implementation, `git diff-files`) or
+`git-ls-files --modified`.  In addition, git internally checks
+if the files in the working tree is different from what are
+recorded in the index to avoid stomping on local changes in them
+during patch application, switching branches, and merging.
+
+In order to speed up this comparison between the files in the
+working tree and the index entries, the index entries record the
+information obtained from the filesystem via `lstat(2)` system
+call when they were last updated.  When checking if they differ,
+git first runs `lstat(2)` on the files and compare the result
+with this information (this is what was originally done by the
+`ce_match_stat()` function, which the current code does in
+`ce_match_stat_basic()` function).  If some of these "cached
+stat information" fields do not match, git can tell that the
+files are modified without even looking at their contents.
+
+Note: not all members in `struct stat` obtained via `lstat(2)`
+are used for this comparison.  For example, `st_atime` obviously
+is not useful.  Currently, git compares the file type (regular
+files vs symbolic links) and executable bits (only for regular
+files) from `st_mode` member, `st_mtime` and `st_ctime`
+timestamps, `st_uid`, `st_gid`, `st_ino`, and `st_size` members.
+With a `USE_STDEV` compile-time option, `st_dev` is also
+compared, but this is not enabled by default because this member
+is not stable on network filesystems.  With `USE_NSEC`
+compile-time option, `st_mtim.tv_nsec` and `st_ctim.tv_nsec`
+members are also compared, but this is not enabled by default
+because the value of this member becomes meaningless once the
+inode is evicted from the inode cache on filesystems that do not
+store it on disk.
+
+
+Racy git
+--------
+
+There is one slight problem with the optimization based on the
+cached stat information.  Consider this sequence:
+
+  $ git update-index 'foo'
+  : modify 'foo' in-place without changing its size
+
+The first `update-index` computes the object name of the
+contents of file `foo` and updates the index entry for `foo`
+along with the `struct stat` information.  If the modification
+that follows it happens very fast so that the file's `st_mtime`
+timestamp does not change, after this sequence, the cached stat
+information the index entry records still exactly match what you
+can obtain from the filesystem, but the file `foo` is modified.
+This way, git can incorrectly think files in the working tree
+are unmodified even though they actually are.  This is called
+the "racy git" problem (discovered by Pasky), and the entries
+that appear clean when they may not be because of this problem
+are called "racily clean".
+
+To avoid this problem, git does two things:
+
+. When the cached stat information says the file has not been
+  modified, and the `st_mtime` is the same as (or newer than)
+  the timestamp of the index file itself (which is the time `git
+  update-index foo` finished running in the above example), it
+  also compares the contents with the object registered in the
+  index entry to make sure they match.
+
+. When the index file is updated that contains racily clean
+  entries, cached `st_size` information is truncated to zero
+  before writing a new version of the index file.
+
+Because the index file itself is written after collecting all
+the stat information from updated paths, `st_mtime` timestamp of
+it is usually the same as or newer than any of the paths the
+index contains.  And no matter how quick the modification that
+follows `git update-index foo` finishes, the resulting
+`st_mtime` timestamp on `foo` cannot get the timestamp earlier
+than the index file.  Therefore, index entries that can be
+racily clean are limited to the ones that have the same
+timestamp as the index file itself.
+
+The callers that want to check if an index entry matches the
+corresponding file in the working tree continue to call
+`ce_match_stat()`, but with this change, `ce_match_stat()` uses
+`ce_modified_check_fs()` to see if racily clean ones are
+actually clean after comparing the cached stat information using
+`ce_match_stat_basic()`.
+
+The problem the latter solves is this sequence:
+
+  $ git update-index 'foo'
+  : modify 'foo' in-place without changing its size
+  : wait for enough time
+  $ git update-index 'bar'
+
+Without the latter, the timestamp of the index file gets a newer
+value, and falsely clean entry `foo` would not be caught by the
+timestamp comparison check done with the former logic anymore.
+The latter makes sure that the cached stat information for `foo`
+would never match with the file in the working tree, so later
+checks by `ce_match_stat_basic()` would report the index entry
+does not match the file and git does not have to fall back on more
+expensive `ce_modified_check_fs()`.
+
+
+Runtime penalty
+---------------
+
+The runtime penalty of falling back to `ce_modified_check_fs()`
+from `ce_match_stat()` can be very expensive when there are many
+racily clean entries.  An obvious way to artificially create
+this situation is to give the same timestamp to all the files in
+the working tree in a large project, run `git update-index` on
+them, and give the same timestamp to the index file:
+
+  $ date >.datestamp
+  $ git ls-files | xargs touch -r .datestamp
+  $ git ls-files | git update-index --stdin
+  $ touch -r .datestamp .git/index
+
+This will make all index entries racily clean.  The linux-2.6
+project, for example, there are over 20,000 files in the working
+tree.  On my Athron 64X2 3800+, after the above:
+
+  $ /usr/bin/time git diff-files
+  1.68user 0.54system 0:02.22elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k
+  0inputs+0outputs (0major+67111minor)pagefaults 0swaps
+  $ git update-index MAINTAINERS
+  $ /usr/bin/time git diff-files
+  0.02user 0.12system 0:00.14elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k
+  0inputs+0outputs (0major+935minor)pagefaults 0swaps
+
+Running `git update-index` in the middle checked the racily
+clean entries, and left the cached `st_mtime` for all the paths
+intact because they were actually clean (so this step took about
+the same amount of time as the first `git diff-files`).  After
+that, they are not racily clean anymore but are truly clean, so
+the second invocation of `git diff-files` fully took advantage
+of the cached stat information.
+
+
+Avoiding runtime penalty
+------------------------
+
+In order to avoid the above runtime penalty, the recent "master"
+branch (post 1.4.2) has a code that makes sure the index file
+gets timestamp newer than the youngest files in the index when
+there are many young files with the same timestamp as the
+resulting index file would otherwise would have by waiting
+before finishing writing the index file out.
+
+I suspect that in practice the situation where many paths in the
+index are all racily clean is quite rare.  The only code paths
+that can record recent timestamp for large number of paths I
+know of are:
+
+. Initial `git add .` of a large project.
+
+. `git checkout` of a large project from an empty index into an
+  unpopulated working tree.
+
+Note: switching branches with `git checkout` keeps the cached
+stat information of existing working tree files that are the
+same between the current branch and the new branch, which are
+all older than the resulting index file, and they will not
+become racily clean.  Only the files that are actually checked
+out can become racily clean.
+
+In a large project where raciness avoidance cost really matters,
+however, the initial computation of all object names in the
+index takes more than one second, and the index file is written
+out after all that happens.  Therefore the timestamp of the
+index file will be more than one seconds later than the the
+youngest file in the working tree.  This means that in these
+cases there actually will not be any racily clean entry in
+the resulting index.
+
+So in summary I think we should not worry about avoiding the
+runtime penalty and get rid of the "wait before finishing
+writing" code out.
index 2f4fe12..42b6e7d 100644 (file)
@@ -368,7 +368,7 @@ in the index file is identical to the one in the working directory.
 In addition to being the staging area for new commits, the index file
 is also populated from the object database when checking out a
 branch, and is used to hold the trees involved in a merge operation.
-See the link:core-tutorial.txt[core tutorial] and the relevant man
+See the link:core-tutorial.html[core tutorial] and the relevant man
 pages for details.
 
 What next?
index 14923c9..0cacac3 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.4.2.GIT
+DEF_VER=v1.4.3.GIT
 
 LF='
 '
diff --git a/INSTALL b/INSTALL
index ba9778c..fce6bc3 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -16,7 +16,7 @@ install" would not work.
 Alternatively you can use autoconf generated ./configure script to
 set up install paths (via config.mak.autogen), so you can write instead
 
-       $ autoconf ;# as yourself if ./configure doesn't exist yet
+       $ make configure ;# as yourself
        $ ./configure --prefix=/usr ;# as yourself
        $ make all doc ;# as yourself
        # make install install-doc ;# as root
@@ -38,6 +38,19 @@ Issues of note:
    has been actively developed since 1997, and people have moved over to
    graphical file managers.
 
+ - You can use git after building but without installing if you
+   wanted to.  Various git commands need to find other git
+   commands and scripts to do their work, so you would need to
+   arrange a few environment variables to tell them that their
+   friends will be found in your built source area instead of at
+   their standard installation area.  Something like this works
+   for me:
+
+       GIT_EXEC_PATH=`pwd`
+       PATH=`pwd`:$PATH
+       GITPERLLIB=`pwd`/perl/blib/lib
+       export GIT_EXEC_PATH PATH GITPERLLIB
+
  - Git is reasonably self-sufficient, but does depend on a few external
    programs and libraries:
 
index 02a036b..401b893 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,11 +1,6 @@
 # The default target of this Makefile is...
 all:
 
-# Define MOZILLA_SHA1 environment variable when running make to make use of
-# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast
-# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default
-# choice) has very fast version optimized for i586.
-#
 # Define NO_OPENSSL environment variable if you do not have OpenSSL.
 # This also implies MOZILLA_SHA1.
 #
@@ -27,7 +22,7 @@ all:
 # Define NO_C99_FORMAT if your formatted IO functions (printf/scanf et.al.)
 # do not support the 'size specifiers' introduced by C99, namely ll, hh,
 # j, z, t. (representing long long int, char, intmax_t, size_t, ptrdiff_t).
-# some c compilers supported these specifiers prior to C99 as an extension.
+# some C compilers supported these specifiers prior to C99 as an extension.
 #
 # Define NO_STRCASESTR if you don't have strcasestr.
 #
@@ -60,6 +55,11 @@ all:
 # Define ARM_SHA1 environment variable when running make to make use of
 # a bundled SHA1 routine optimized for ARM.
 #
+# Define MOZILLA_SHA1 environment variable when running make to make use of
+# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast
+# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default
+# choice) has very fast version optimized for i586.
+#
 # Define NEEDS_SSL_WITH_CRYPTO if you need -lcrypto with -lssl (Darwin).
 #
 # Define NEEDS_LIBICONV if linking with libc is not enough (Darwin).
@@ -81,18 +81,16 @@ all:
 # Define NO_ACCURATE_DIFF if your diff program at least sometimes misses
 # a missing newline at the end of the file.
 #
-# Define NO_PYTHON if you want to lose all benefits of the recursive merge.
-#
 # Define COLLISION_CHECK below if you believe that SHA1's
 # 1461501637330902918203684832716283019655932542976 hashes do not give you
 # sufficient guarantee that no collisions between objects will ever happen.
-
+#
 # Define USE_NSEC below if you want git to care about sub-second file mtimes
 # and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and
 # it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely
 # randomly break unless your underlying filesystem supports those sub-second
 # times (my ext3 doesn't).
-
+#
 # 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.
 
@@ -121,6 +119,20 @@ template_dir = $(prefix)/share/git-core/templates/
 GIT_PYTHON_DIR = $(prefix)/share/git-core/python
 # DESTDIR=
 
+# default configuration for gitweb
+GITWEB_CONFIG = gitweb_config.perl
+GITWEB_HOME_LINK_STR = projects
+GITWEB_SITENAME =
+GITWEB_PROJECTROOT = /pub/git
+GITWEB_EXPORT_OK =
+GITWEB_STRICT_EXPORT =
+GITWEB_BASE_URL =
+GITWEB_LIST =
+GITWEB_HOMETEXT = indextext.html
+GITWEB_CSS = gitweb.css
+GITWEB_LOGO = git-logo.png
+GITWEB_FAVICON = git-favicon.png
+
 export prefix bindir gitexecdir template_dir GIT_PYTHON_DIR
 
 CC = gcc
@@ -137,6 +149,12 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__
 
 ### --- END CONFIGURATION SECTION ---
 
+# Those must not be GNU-specific; they are shared with perl/ which may
+# be built by a different compiler. (Note that this is an artifact now
+# but it still might be nice to keep that distinction.)
+BASIC_CFLAGS =
+BASIC_LDFLAGS =
+
 SCRIPT_SH = \
        git-bisect.sh git-branch.sh git-checkout.sh \
        git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \
@@ -160,7 +178,7 @@ SCRIPT_PERL = \
        git-send-email.perl git-svn.perl
 
 SCRIPT_PYTHON = \
-       git-merge-recursive.py
+       git-merge-recursive-old.py
 
 SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
          $(patsubst %.perl,%,$(SCRIPT_PERL)) \
@@ -173,35 +191,32 @@ SIMPLE_PROGRAMS = \
 
 # ... and all the rest that could be moved out of bindir to gitexecdir
 PROGRAMS = \
-       git-checkout-index$X \
        git-convert-objects$X git-fetch-pack$X git-fsck-objects$X \
        git-hash-object$X git-index-pack$X git-local-fetch$X \
        git-merge-base$X \
-       git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \
+       git-merge-index$X git-mktag$X git-mktree$X git-patch-id$X \
        git-peek-remote$X git-receive-pack$X \
        git-send-pack$X git-shell$X \
        git-show-index$X git-ssh-fetch$X \
        git-ssh-upload$X git-unpack-file$X \
-       git-unpack-objects$X git-update-server-info$X \
+       git-update-server-info$X \
        git-upload-pack$X git-verify-pack$X \
-       git-symbolic-ref$X \
-       git-name-rev$X git-pack-redundant$X git-var$X \
-       git-describe$X git-merge-tree$X git-blame$X git-imap-send$X
-
-BUILT_INS = git-log$X git-whatchanged$X git-show$X git-update-ref$X \
-       git-count-objects$X git-diff$X git-push$X git-mailsplit$X \
-       git-grep$X git-add$X git-rm$X git-rev-list$X git-stripspace$X \
-       git-check-ref-format$X git-rev-parse$X git-mailinfo$X \
-       git-init-db$X git-tar-tree$X git-upload-tar$X git-format-patch$X \
-       git-ls-files$X git-ls-tree$X git-get-tar-commit-id$X \
-       git-read-tree$X git-commit-tree$X git-write-tree$X \
-       git-apply$X git-show-branch$X git-diff-files$X git-update-index$X \
-       git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X \
-       git-fmt-merge-msg$X git-prune$X git-mv$X git-prune-packed$X \
-       git-repo-config$X
+       git-pack-redundant$X git-var$X \
+       git-describe$X git-merge-tree$X git-blame$X git-imap-send$X \
+       git-merge-recursive$X \
+       $(EXTRA_PROGRAMS)
+
+# Empty...
+EXTRA_PROGRAMS =
+
+BUILT_INS = \
+       git-format-patch$X git-show$X git-whatchanged$X \
+       git-get-tar-commit-id$X \
+       $(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
 
 # what 'all' will build and 'install' will install, in gitexecdir
-ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)
+ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) \
+       git-merge-recur$X
 
 # Backward compatibility -- to be removed after 1.0
 PROGRAMS += git-ssh-pull$X git-ssh-push$X
@@ -224,10 +239,10 @@ LIB_FILE=libgit.a
 XDIFF_LIB=xdiff/lib.a
 
 LIB_H = \
-       blob.h cache.h commit.h csum-file.h delta.h \
-       diff.h object.h pack.h pkt-line.h quote.h refs.h \
+       archive.h blob.h cache.h commit.h csum-file.h delta.h grep.h \
+       diff.h object.h pack.h pkt-line.h quote.h refs.h list-objects.h sideband.h \
        run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
-       tree-walk.h log-tree.h dir.h path-list.h
+       tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h
 
 DIFF_OBJS = \
        diff.o diff-lib.o diffcore-break.o diffcore-order.o \
@@ -236,29 +251,65 @@ DIFF_OBJS = \
 
 LIB_OBJS = \
        blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \
-       date.o diff-delta.o entry.o exec_cmd.o ident.o lockfile.o \
-       object.o pack-check.o patch-delta.o path.o pkt-line.o \
+       date.o diff-delta.o entry.o exec_cmd.o ident.o \
+       interpolate.o \
+       lockfile.o \
+       object.o pack-check.o patch-delta.o path.o pkt-line.o sideband.o \
        quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \
        server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
        tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
        fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \
-       alloc.o merge-file.o path-list.o $(DIFF_OBJS)
+       write_or_die.o trace.o list-objects.o grep.o \
+       alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
+       color.o wt-status.o archive-zip.o archive-tar.o
 
 BUILTIN_OBJS = \
-       builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \
-       builtin-grep.o builtin-add.o builtin-rev-list.o builtin-check-ref-format.o \
-       builtin-rm.o builtin-init-db.o builtin-rev-parse.o \
-       builtin-tar-tree.o builtin-upload-tar.o builtin-update-index.o \
-       builtin-ls-files.o builtin-ls-tree.o builtin-write-tree.o \
-       builtin-read-tree.o builtin-commit-tree.o builtin-mailinfo.o \
-       builtin-apply.o builtin-show-branch.o builtin-diff-files.o \
-       builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \
-       builtin-cat-file.o builtin-mailsplit.o builtin-stripspace.o \
-       builtin-update-ref.o builtin-fmt-merge-msg.o builtin-prune.o \
-       builtin-mv.o builtin-prune-packed.o builtin-repo-config.o
+       builtin-add.o \
+       builtin-apply.o \
+       builtin-archive.o \
+       builtin-cat-file.o \
+       builtin-checkout-index.o \
+       builtin-check-ref-format.o \
+       builtin-commit-tree.o \
+       builtin-count-objects.o \
+       builtin-diff.o \
+       builtin-diff-files.o \
+       builtin-diff-index.o \
+       builtin-diff-stages.o \
+       builtin-diff-tree.o \
+       builtin-fmt-merge-msg.o \
+       builtin-grep.o \
+       builtin-init-db.o \
+       builtin-log.o \
+       builtin-ls-files.o \
+       builtin-ls-tree.o \
+       builtin-mailinfo.o \
+       builtin-mailsplit.o \
+       builtin-mv.o \
+       builtin-name-rev.o \
+       builtin-pack-objects.o \
+       builtin-prune.o \
+       builtin-prune-packed.o \
+       builtin-push.o \
+       builtin-read-tree.o \
+       builtin-repo-config.o \
+       builtin-rev-list.o \
+       builtin-rev-parse.o \
+       builtin-rm.o \
+       builtin-runstatus.o \
+       builtin-show-branch.o \
+       builtin-stripspace.o \
+       builtin-symbolic-ref.o \
+       builtin-tar-tree.o \
+       builtin-unpack-objects.o \
+       builtin-update-index.o \
+       builtin-update-ref.o \
+       builtin-upload-archive.o \
+       builtin-verify-pack.o \
+       builtin-write-tree.o
 
 GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
-LIBS = $(GITLIBS) -lz
+EXTLIBS = -lz
 
 #
 # Platform specific tweaks
@@ -280,14 +331,14 @@ ifeq ($(uname_S),Darwin)
        NO_STRLCPY = YesPlease
        ifndef NO_FINK
                ifeq ($(shell test -d /sw/lib && echo y),y)
-                       ALL_CFLAGS += -I/sw/include
-                       ALL_LDFLAGS += -L/sw/lib
+                       BASIC_CFLAGS += -I/sw/include
+                       BASIC_LDFLAGS += -L/sw/lib
                endif
        endif
        ifndef NO_DARWIN_PORTS
                ifeq ($(shell test -d /opt/local/lib && echo y),y)
-                       ALL_CFLAGS += -I/opt/local/include
-                       ALL_LDFLAGS += -L/opt/local/lib
+                       BASIC_CFLAGS += -I/opt/local/include
+                       BASIC_LDFLAGS += -L/opt/local/lib
                endif
        endif
 endif
@@ -296,7 +347,6 @@ ifeq ($(uname_S),SunOS)
        NEEDS_NSL = YesPlease
        SHELL_PATH = /bin/bash
        NO_STRCASESTR = YesPlease
-       NO_STRLCPY = YesPlease
        ifeq ($(uname_R),5.8)
                NEEDS_LIBICONV = YesPlease
                NO_UNSETENV = YesPlease
@@ -310,7 +360,7 @@ ifeq ($(uname_S),SunOS)
        endif
        INSTALL = ginstall
        TAR = gtar
-       ALL_CFLAGS += -D__EXTENSIONS__
+       BASIC_CFLAGS += -D__EXTENSIONS__
 endif
 ifeq ($(uname_O),Cygwin)
        NO_D_TYPE_IN_DIRENT = YesPlease
@@ -328,21 +378,22 @@ ifeq ($(uname_O),Cygwin)
 endif
 ifeq ($(uname_S),FreeBSD)
        NEEDS_LIBICONV = YesPlease
-       ALL_CFLAGS += -I/usr/local/include
-       ALL_LDFLAGS += -L/usr/local/lib
+       BASIC_CFLAGS += -I/usr/local/include
+       BASIC_LDFLAGS += -L/usr/local/lib
 endif
 ifeq ($(uname_S),OpenBSD)
        NO_STRCASESTR = YesPlease
        NEEDS_LIBICONV = YesPlease
-       ALL_CFLAGS += -I/usr/local/include
-       ALL_LDFLAGS += -L/usr/local/lib
+       BASIC_CFLAGS += -I/usr/local/include
+       BASIC_LDFLAGS += -L/usr/local/lib
 endif
 ifeq ($(uname_S),NetBSD)
        ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2)
                NEEDS_LIBICONV = YesPlease
        endif
-       ALL_CFLAGS += -I/usr/pkg/include
-       ALL_LDFLAGS += -L/usr/pkg/lib -Wl,-rpath,/usr/pkg/lib
+       BASIC_CFLAGS += -I/usr/pkg/include
+       BASIC_LDFLAGS += -L/usr/pkg/lib
+       ALL_LDFLAGS += -Wl,-rpath,/usr/pkg/lib
 endif
 ifeq ($(uname_S),AIX)
        NO_STRCASESTR=YesPlease
@@ -356,9 +407,9 @@ ifeq ($(uname_S),IRIX64)
        NO_STRLCPY = YesPlease
        NO_SOCKADDR_STORAGE=YesPlease
        SHELL_PATH=/usr/gnu/bin/bash
-       ALL_CFLAGS += -DPATH_MAX=1024
+       BASIC_CFLAGS += -DPATH_MAX=1024
        # for now, build 32-bit version
-       ALL_LDFLAGS += -L/usr/lib32
+       BASIC_LDFLAGS += -L/usr/lib32
 endif
 ifneq (,$(findstring arm,$(uname_M)))
        ARM_SHA1 = YesPlease
@@ -380,7 +431,7 @@ endif
 ifndef NO_CURL
        ifdef CURLDIR
                # This is still problematic -- gcc does not always want -R.
-               ALL_CFLAGS += -I$(CURLDIR)/include
+               BASIC_CFLAGS += -I$(CURLDIR)/include
                CURL_LIBCURL = -L$(CURLDIR)/lib -R$(CURLDIR)/lib -lcurl
        else
                CURL_LIBCURL = -lcurl
@@ -401,13 +452,13 @@ ifndef NO_OPENSSL
        OPENSSL_LIBSSL = -lssl
        ifdef OPENSSLDIR
                # Again this may be problematic -- gcc does not always want -R.
-               ALL_CFLAGS += -I$(OPENSSLDIR)/include
+               BASIC_CFLAGS += -I$(OPENSSLDIR)/include
                OPENSSL_LINK = -L$(OPENSSLDIR)/lib -R$(OPENSSLDIR)/lib
        else
                OPENSSL_LINK =
        endif
 else
-       ALL_CFLAGS += -DNO_OPENSSL
+       BASIC_CFLAGS += -DNO_OPENSSL
        MOZILLA_SHA1 = 1
        OPENSSL_LIBSSL =
 endif
@@ -419,32 +470,32 @@ endif
 ifdef NEEDS_LIBICONV
        ifdef ICONVDIR
                # Again this may be problematic -- gcc does not always want -R.
-               ALL_CFLAGS += -I$(ICONVDIR)/include
+               BASIC_CFLAGS += -I$(ICONVDIR)/include
                ICONV_LINK = -L$(ICONVDIR)/lib -R$(ICONVDIR)/lib
        else
                ICONV_LINK =
        endif
-       LIBS += $(ICONV_LINK) -liconv
+       EXTLIBS += $(ICONV_LINK) -liconv
 endif
 ifdef NEEDS_SOCKET
-       LIBS += -lsocket
+       EXTLIBS += -lsocket
        SIMPLE_LIB += -lsocket
 endif
 ifdef NEEDS_NSL
-       LIBS += -lnsl
+       EXTLIBS += -lnsl
        SIMPLE_LIB += -lnsl
 endif
 ifdef NO_D_TYPE_IN_DIRENT
-       ALL_CFLAGS += -DNO_D_TYPE_IN_DIRENT
+       BASIC_CFLAGS += -DNO_D_TYPE_IN_DIRENT
 endif
 ifdef NO_D_INO_IN_DIRENT
-       ALL_CFLAGS += -DNO_D_INO_IN_DIRENT
+       BASIC_CFLAGS += -DNO_D_INO_IN_DIRENT
 endif
 ifdef NO_C99_FORMAT
        ALL_CFLAGS += -DNO_C99_FORMAT
 endif
 ifdef NO_SYMLINK_HEAD
-       ALL_CFLAGS += -DNO_SYMLINK_HEAD
+       BASIC_CFLAGS += -DNO_SYMLINK_HEAD
 endif
 ifdef NO_STRCASESTR
        COMPAT_CFLAGS += -DNO_STRCASESTR
@@ -458,7 +509,7 @@ ifdef NO_SETENV
        COMPAT_CFLAGS += -DNO_SETENV
        COMPAT_OBJS += compat/setenv.o
 endif
-ifdef NO_SETENV
+ifdef NO_UNSETENV
        COMPAT_CFLAGS += -DNO_UNSETENV
        COMPAT_OBJS += compat/unsetenv.o
 endif
@@ -467,21 +518,24 @@ ifdef NO_MMAP
        COMPAT_OBJS += compat/mmap.o
 endif
 ifdef NO_IPV6
-       ALL_CFLAGS += -DNO_IPV6
+       BASIC_CFLAGS += -DNO_IPV6
 endif
 ifdef NO_SOCKADDR_STORAGE
 ifdef NO_IPV6
-       ALL_CFLAGS += -Dsockaddr_storage=sockaddr_in
+       BASIC_CFLAGS += -Dsockaddr_storage=sockaddr_in
 else
-       ALL_CFLAGS += -Dsockaddr_storage=sockaddr_in6
+       BASIC_CFLAGS += -Dsockaddr_storage=sockaddr_in6
 endif
 endif
 ifdef NO_INET_NTOP
        LIB_OBJS += compat/inet_ntop.o
 endif
+ifdef NO_INET_PTON
+       LIB_OBJS += compat/inet_pton.o
+endif
 
 ifdef NO_ICONV
-       ALL_CFLAGS += -DNO_ICONV
+       BASIC_CFLAGS += -DNO_ICONV
 endif
 
 ifdef PPC_SHA1
@@ -497,12 +551,12 @@ ifdef MOZILLA_SHA1
        LIB_OBJS += mozilla-sha1/sha1.o
 else
        SHA1_HEADER = <openssl/sha.h>
-       LIBS += $(LIB_4_CRYPTO)
+       EXTLIBS += $(LIB_4_CRYPTO)
 endif
 endif
 endif
 ifdef NO_ACCURATE_DIFF
-       ALL_CFLAGS += -DNO_ACCURATE_DIFF
+       BASIC_CFLAGS += -DNO_ACCURATE_DIFF
 endif
 
 # Shell quote (do not use $(call) to accommodate ancient setups);
@@ -520,14 +574,23 @@ PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
 PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
 GIT_PYTHON_DIR_SQ = $(subst ','\'',$(GIT_PYTHON_DIR))
 
-ALL_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' $(COMPAT_CFLAGS)
+LIBS = $(GITLIBS) $(EXTLIBS)
+
+BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' $(COMPAT_CFLAGS)
 LIB_OBJS += $(COMPAT_OBJS)
+
+ALL_CFLAGS += $(BASIC_CFLAGS)
+ALL_LDFLAGS += $(BASIC_LDFLAGS)
+
 export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
+
+
 ### Build rules
 
-all: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk
+all: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk gitweb/gitweb.cgi
 
-all:
+all: perl/Makefile
+       $(MAKE) -C perl
        $(MAKE) -C templates
 
 strip: $(PROGRAMS) git$X
@@ -538,7 +601,10 @@ git$X: git.c common-cmds.h $(BUILTIN_OBJS) $(GITLIBS) GIT-CFLAGS
                $(ALL_CFLAGS) -o $@ $(filter %.c,$^) \
                $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
 
-builtin-help.o: common-cmds.h
+help.o: common-cmds.h
+
+git-merge-recur$X: git-merge-recursive$X
+       rm -f $@ && ln git-merge-recursive$X $@
 
 $(BUILT_INS): git$X
        rm -f $@ && ln git$X $@
@@ -558,9 +624,18 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
        chmod +x $@+
        mv $@+ $@
 
-$(patsubst %.perl,%,$(SCRIPT_PERL)) : % : %.perl
+$(patsubst %.perl,%,$(SCRIPT_PERL)): perl/Makefile
+$(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
        rm -f $@ $@+
-       sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
+       INSTLIBDIR=`$(MAKE) -C perl -s --no-print-directory instlibdir` && \
+       sed -e '1{' \
+           -e '        s|#!.*perl|#!$(PERL_PATH_SQ)|' \
+           -e '        h' \
+           -e '        s=.*=use lib (split(/:/, $$ENV{GITPERLLIB} || "@@INSTLIBDIR@@"));=' \
+           -e '        H' \
+           -e '        x' \
+           -e '}' \
+           -e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR"'|g' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
            $@.perl >$@+
        chmod +x $@+
@@ -583,6 +658,27 @@ git-status: git-commit
        cp $< $@+
        mv $@+ $@
 
+gitweb/gitweb.cgi: gitweb/gitweb.perl
+       rm -f $@ $@+
+       sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
+           -e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \
+           -e 's|++GIT_BINDIR++|$(bindir)|g' \
+           -e 's|++GITWEB_CONFIG++|$(GITWEB_CONFIG)|g' \
+           -e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \
+           -e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \
+           -e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \
+           -e 's|++GITWEB_EXPORT_OK++|$(GITWEB_EXPORT_OK)|g' \
+           -e 's|++GITWEB_STRICT_EXPORT++|$(GITWEB_STRICT_EXPORT)|g' \
+           -e 's|++GITWEB_BASE_URL++|$(GITWEB_BASE_URL)|g' \
+           -e 's|++GITWEB_LIST++|$(GITWEB_LIST)|g' \
+           -e 's|++GITWEB_HOMETEXT++|$(GITWEB_HOMETEXT)|g' \
+           -e 's|++GITWEB_CSS++|$(GITWEB_CSS)|g' \
+           -e 's|++GITWEB_LOGO++|$(GITWEB_LOGO)|g' \
+           -e 's|++GITWEB_FAVICON++|$(GITWEB_FAVICON)|g' \
+           $< >$@+
+       chmod +x $@+
+       mv $@+ $@
+
 git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css
        rm -f $@ $@+
        sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
@@ -593,10 +689,17 @@ git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css
            -e '/@@GITWEB_CGI@@/d' \
            -e '/@@GITWEB_CSS@@/r gitweb/gitweb.css' \
            -e '/@@GITWEB_CSS@@/d' \
-           $@.sh | sed "s|/usr/bin/git|$(bindir)/git|" > $@+
+           $@.sh > $@+
        chmod +x $@+
        mv $@+ $@
 
+configure: configure.ac
+       rm -f $@ $<+
+       sed -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
+           $< > $<+
+       autoconf -o $@ $<+
+       rm -f $<+
+
 # These can record GIT_VERSION
 git$X git.spec \
        $(patsubst %.sh,%,$(SCRIPT_SH)) \
@@ -662,6 +765,10 @@ $(XDIFF_LIB): $(XDIFF_OBJS)
        rm -f $@ && $(AR) rcs $@ $(XDIFF_OBJS)
 
 
+perl/Makefile: perl/Git.pm perl/Makefile.PL GIT-CFLAGS
+       (cd perl && $(PERL_PATH) Makefile.PL \
+               PREFIX='$(prefix_SQ)')
+
 doc:
        $(MAKE) -C Documentation all
 
@@ -724,6 +831,7 @@ install: all
        $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
        $(INSTALL) git$X gitk '$(DESTDIR_SQ)$(bindir_SQ)'
        $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
+       $(MAKE) -C perl install
        $(INSTALL) -d -m755 '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)'
        $(INSTALL) $(PYMODULES) '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)'
        if test 'z$(bindir_SQ)' != 'z$(gitexecdir_SQ)'; \
@@ -788,12 +896,15 @@ clean:
        rm -f $(ALL_PROGRAMS) $(BUILT_INS) git$X
        rm -f *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags
        rm -rf autom4te.cache
-       rm -f config.log config.mak.autogen configure config.status config.cache
+       rm -f configure config.log config.mak.autogen config.mak.append config.status config.cache
        rm -rf $(GIT_TARNAME) .doc-tmp-dir
        rm -f $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
        rm -f $(htmldocs).tar.gz $(manpages).tar.gz
+       rm -f gitweb/gitweb.cgi
        $(MAKE) -C Documentation/ clean
-       $(MAKE) -C templates clean
+       [ ! -f perl/Makefile ] || $(MAKE) -C perl/ clean || $(MAKE) -C perl/ clean
+       rm -f perl/ppport.h perl/Makefile.old
+       $(MAKE) -C templates/ clean
        $(MAKE) -C t/ clean
        rm -f GIT-VERSION-FILE GIT-CFLAGS
 
@@ -807,7 +918,8 @@ check-docs::
        do \
                case "$$v" in \
                git-merge-octopus | git-merge-ours | git-merge-recursive | \
-               git-merge-resolve | git-merge-stupid | \
+               git-merge-resolve | git-merge-stupid | git-merge-recur | \
+               git-merge-recursive-old | \
                git-ssh-pull | git-ssh-push ) continue ;; \
                esac ; \
                test -f "Documentation/$$v.txt" || \
diff --git a/archive-tar.c b/archive-tar.c
new file mode 100644 (file)
index 0000000..ff0f6e2
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2005, 2006 Rene Scharfe
+ */
+#include <time.h>
+#include "cache.h"
+#include "commit.h"
+#include "strbuf.h"
+#include "tar.h"
+#include "builtin.h"
+#include "archive.h"
+
+#define RECORDSIZE     (512)
+#define BLOCKSIZE      (RECORDSIZE * 20)
+
+static char block[BLOCKSIZE];
+static unsigned long offset;
+
+static time_t archive_time;
+static int tar_umask;
+static int verbose;
+
+/* writes out the whole block, but only if it is full */
+static void write_if_needed(void)
+{
+       if (offset == BLOCKSIZE) {
+               write_or_die(1, block, BLOCKSIZE);
+               offset = 0;
+       }
+}
+
+/*
+ * queues up writes, so that all our write(2) calls write exactly one
+ * full block; pads writes to RECORDSIZE
+ */
+static void write_blocked(const void *data, unsigned long size)
+{
+       const char *buf = data;
+       unsigned long tail;
+
+       if (offset) {
+               unsigned long chunk = BLOCKSIZE - offset;
+               if (size < chunk)
+                       chunk = size;
+               memcpy(block + offset, buf, chunk);
+               size -= chunk;
+               offset += chunk;
+               buf += chunk;
+               write_if_needed();
+       }
+       while (size >= BLOCKSIZE) {
+               write_or_die(1, buf, BLOCKSIZE);
+               size -= BLOCKSIZE;
+               buf += BLOCKSIZE;
+       }
+       if (size) {
+               memcpy(block + offset, buf, size);
+               offset += size;
+       }
+       tail = offset % RECORDSIZE;
+       if (tail)  {
+               memset(block + offset, 0, RECORDSIZE - tail);
+               offset += RECORDSIZE - tail;
+       }
+       write_if_needed();
+}
+
+/*
+ * The end of tar archives is marked by 2*512 nul bytes and after that
+ * follows the rest of the block (if any).
+ */
+static void write_trailer(void)
+{
+       int tail = BLOCKSIZE - offset;
+       memset(block + offset, 0, tail);
+       write_or_die(1, block, BLOCKSIZE);
+       if (tail < 2 * RECORDSIZE) {
+               memset(block, 0, offset);
+               write_or_die(1, block, BLOCKSIZE);
+       }
+}
+
+static void strbuf_append_string(struct strbuf *sb, const char *s)
+{
+       int slen = strlen(s);
+       int total = sb->len + slen;
+       if (total > sb->alloc) {
+               sb->buf = xrealloc(sb->buf, total);
+               sb->alloc = total;
+       }
+       memcpy(sb->buf + sb->len, s, slen);
+       sb->len = total;
+}
+
+/*
+ * pax extended header records have the format "%u %s=%s\n".  %u contains
+ * the size of the whole string (including the %u), the first %s is the
+ * keyword, the second one is the value.  This function constructs such a
+ * string and appends it to a struct strbuf.
+ */
+static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword,
+                                     const char *value, unsigned int valuelen)
+{
+       char *p;
+       int len, total, tmp;
+
+       /* "%u %s=%s\n" */
+       len = 1 + 1 + strlen(keyword) + 1 + valuelen + 1;
+       for (tmp = len; tmp > 9; tmp /= 10)
+               len++;
+
+       total = sb->len + len;
+       if (total > sb->alloc) {
+               sb->buf = xrealloc(sb->buf, total);
+               sb->alloc = total;
+       }
+
+       p = sb->buf;
+       p += sprintf(p, "%u %s=", len, keyword);
+       memcpy(p, value, valuelen);
+       p += valuelen;
+       *p = '\n';
+       sb->len = total;
+}
+
+static unsigned int ustar_header_chksum(const struct ustar_header *header)
+{
+       char *p = (char *)header;
+       unsigned int chksum = 0;
+       while (p < header->chksum)
+               chksum += *p++;
+       chksum += sizeof(header->chksum) * ' ';
+       p += sizeof(header->chksum);
+       while (p < (char *)header + sizeof(struct ustar_header))
+               chksum += *p++;
+       return chksum;
+}
+
+static int get_path_prefix(const struct strbuf *path, int maxlen)
+{
+       int i = path->len;
+       if (i > maxlen)
+               i = maxlen;
+       do {
+               i--;
+       } while (i > 0 && path->buf[i] != '/');
+       return i;
+}
+
+static void write_entry(const unsigned char *sha1, struct strbuf *path,
+                        unsigned int mode, void *buffer, unsigned long size)
+{
+       struct ustar_header header;
+       struct strbuf ext_header;
+
+       memset(&header, 0, sizeof(header));
+       ext_header.buf = NULL;
+       ext_header.len = ext_header.alloc = 0;
+
+       if (!sha1) {
+               *header.typeflag = TYPEFLAG_GLOBAL_HEADER;
+               mode = 0100666;
+               strcpy(header.name, "pax_global_header");
+       } else if (!path) {
+               *header.typeflag = TYPEFLAG_EXT_HEADER;
+               mode = 0100666;
+               sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
+       } else {
+               if (verbose)
+                       fprintf(stderr, "%.*s\n", path->len, path->buf);
+               if (S_ISDIR(mode)) {
+                       *header.typeflag = TYPEFLAG_DIR;
+                       mode = (mode | 0777) & ~tar_umask;
+               } else if (S_ISLNK(mode)) {
+                       *header.typeflag = TYPEFLAG_LNK;
+                       mode |= 0777;
+               } else if (S_ISREG(mode)) {
+                       *header.typeflag = TYPEFLAG_REG;
+                       mode = (mode | ((mode & 0100) ? 0777 : 0666)) & ~tar_umask;
+               } else {
+                       error("unsupported file mode: 0%o (SHA1: %s)",
+                             mode, sha1_to_hex(sha1));
+                       return;
+               }
+               if (path->len > sizeof(header.name)) {
+                       int plen = get_path_prefix(path, sizeof(header.prefix));
+                       int rest = path->len - plen - 1;
+                       if (plen > 0 && rest <= sizeof(header.name)) {
+                               memcpy(header.prefix, path->buf, plen);
+                               memcpy(header.name, path->buf + plen + 1, rest);
+                       } else {
+                               sprintf(header.name, "%s.data",
+                                       sha1_to_hex(sha1));
+                               strbuf_append_ext_header(&ext_header, "path",
+                                                        path->buf, path->len);
+                       }
+               } else
+                       memcpy(header.name, path->buf, path->len);
+       }
+
+       if (S_ISLNK(mode) && buffer) {
+               if (size > sizeof(header.linkname)) {
+                       sprintf(header.linkname, "see %s.paxheader",
+                               sha1_to_hex(sha1));
+                       strbuf_append_ext_header(&ext_header, "linkpath",
+                                                buffer, size);
+               } else
+                       memcpy(header.linkname, buffer, size);
+       }
+
+       sprintf(header.mode, "%07o", mode & 07777);
+       sprintf(header.size, "%011lo", S_ISREG(mode) ? size : 0);
+       sprintf(header.mtime, "%011lo", archive_time);
+
+       /* XXX: should we provide more meaningful info here? */
+       sprintf(header.uid, "%07o", 0);
+       sprintf(header.gid, "%07o", 0);
+       strlcpy(header.uname, "git", sizeof(header.uname));
+       strlcpy(header.gname, "git", sizeof(header.gname));
+       sprintf(header.devmajor, "%07o", 0);
+       sprintf(header.devminor, "%07o", 0);
+
+       memcpy(header.magic, "ustar", 6);
+       memcpy(header.version, "00", 2);
+
+       sprintf(header.chksum, "%07o", ustar_header_chksum(&header));
+
+       if (ext_header.len > 0) {
+               write_entry(sha1, NULL, 0, ext_header.buf, ext_header.len);
+               free(ext_header.buf);
+       }
+       write_blocked(&header, sizeof(header));
+       if (S_ISREG(mode) && buffer && size > 0)
+               write_blocked(buffer, size);
+}
+
+static void write_global_extended_header(const unsigned char *sha1)
+{
+       struct strbuf ext_header;
+       ext_header.buf = NULL;
+       ext_header.len = ext_header.alloc = 0;
+       strbuf_append_ext_header(&ext_header, "comment", sha1_to_hex(sha1), 40);
+       write_entry(NULL, NULL, 0, ext_header.buf, ext_header.len);
+       free(ext_header.buf);
+}
+
+static int git_tar_config(const char *var, const char *value)
+{
+       if (!strcmp(var, "tar.umask")) {
+               if (!strcmp(value, "user")) {
+                       tar_umask = umask(0);
+                       umask(tar_umask);
+               } else {
+                       tar_umask = git_config_int(var, value);
+               }
+               return 0;
+       }
+       return git_default_config(var, value);
+}
+
+static int write_tar_entry(const unsigned char *sha1,
+                           const char *base, int baselen,
+                           const char *filename, unsigned mode, int stage)
+{
+       static struct strbuf path;
+       int filenamelen = strlen(filename);
+       void *buffer;
+       char type[20];
+       unsigned long size;
+
+       if (!path.alloc) {
+               path.buf = xmalloc(PATH_MAX);
+               path.alloc = PATH_MAX;
+               path.len = path.eof = 0;
+       }
+       if (path.alloc < baselen + filenamelen) {
+               free(path.buf);
+               path.buf = xmalloc(baselen + filenamelen);
+               path.alloc = baselen + filenamelen;
+       }
+       memcpy(path.buf, base, baselen);
+       memcpy(path.buf + baselen, filename, filenamelen);
+       path.len = baselen + filenamelen;
+       if (S_ISDIR(mode)) {
+               strbuf_append_string(&path, "/");
+               buffer = NULL;
+               size = 0;
+       } else {
+               buffer = read_sha1_file(sha1, type, &size);
+               if (!buffer)
+                       die("cannot read %s", sha1_to_hex(sha1));
+       }
+
+       write_entry(sha1, &path, mode, buffer, size);
+       free(buffer);
+
+       return READ_TREE_RECURSIVE;
+}
+
+int write_tar_archive(struct archiver_args *args)
+{
+       int plen = args->base ? strlen(args->base) : 0;
+
+       git_config(git_tar_config);
+
+       archive_time = args->time;
+       verbose = args->verbose;
+
+       if (args->commit_sha1)
+               write_global_extended_header(args->commit_sha1);
+
+       if (args->base && plen > 0 && args->base[plen - 1] == '/') {
+               char *base = xstrdup(args->base);
+               int baselen = strlen(base);
+
+               while (baselen > 0 && base[baselen - 1] == '/')
+                       base[--baselen] = '\0';
+               write_tar_entry(args->tree->object.sha1, "", 0, base, 040777, 0);
+               free(base);
+       }
+       read_tree_recursive(args->tree, args->base, plen, 0,
+                           args->pathspec, write_tar_entry);
+       write_trailer();
+
+       return 0;
+}
diff --git a/archive-zip.c b/archive-zip.c
new file mode 100644 (file)
index 0000000..3ffdad6
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2006 Rene Scharfe
+ */
+#include <time.h>
+#include "cache.h"
+#include "commit.h"
+#include "blob.h"
+#include "tree.h"
+#include "quote.h"
+#include "builtin.h"
+#include "archive.h"
+
+static int verbose;
+static int zip_date;
+static int zip_time;
+
+static unsigned char *zip_dir;
+static unsigned int zip_dir_size;
+
+static unsigned int zip_offset;
+static unsigned int zip_dir_offset;
+static unsigned int zip_dir_entries;
+
+#define ZIP_DIRECTORY_MIN_SIZE (1024 * 1024)
+
+struct zip_local_header {
+       unsigned char magic[4];
+       unsigned char version[2];
+       unsigned char flags[2];
+       unsigned char compression_method[2];
+       unsigned char mtime[2];
+       unsigned char mdate[2];
+       unsigned char crc32[4];
+       unsigned char compressed_size[4];
+       unsigned char size[4];
+       unsigned char filename_length[2];
+       unsigned char extra_length[2];
+};
+
+struct zip_dir_header {
+       unsigned char magic[4];
+       unsigned char creator_version[2];
+       unsigned char version[2];
+       unsigned char flags[2];
+       unsigned char compression_method[2];
+       unsigned char mtime[2];
+       unsigned char mdate[2];
+       unsigned char crc32[4];
+       unsigned char compressed_size[4];
+       unsigned char size[4];
+       unsigned char filename_length[2];
+       unsigned char extra_length[2];
+       unsigned char comment_length[2];
+       unsigned char disk[2];
+       unsigned char attr1[2];
+       unsigned char attr2[4];
+       unsigned char offset[4];
+};
+
+struct zip_dir_trailer {
+       unsigned char magic[4];
+       unsigned char disk[2];
+       unsigned char directory_start_disk[2];
+       unsigned char entries_on_this_disk[2];
+       unsigned char entries[2];
+       unsigned char size[4];
+       unsigned char offset[4];
+       unsigned char comment_length[2];
+};
+
+static void copy_le16(unsigned char *dest, unsigned int n)
+{
+       dest[0] = 0xff & n;
+       dest[1] = 0xff & (n >> 010);
+}
+
+static void copy_le32(unsigned char *dest, unsigned int n)
+{
+       dest[0] = 0xff & n;
+       dest[1] = 0xff & (n >> 010);
+       dest[2] = 0xff & (n >> 020);
+       dest[3] = 0xff & (n >> 030);
+}
+
+static void *zlib_deflate(void *data, unsigned long size,
+                          unsigned long *compressed_size)
+{
+       z_stream stream;
+       unsigned long maxsize;
+       void *buffer;
+       int result;
+
+       memset(&stream, 0, sizeof(stream));
+       deflateInit(&stream, zlib_compression_level);
+       maxsize = deflateBound(&stream, size);
+       buffer = xmalloc(maxsize);
+
+       stream.next_in = data;
+       stream.avail_in = size;
+       stream.next_out = buffer;
+       stream.avail_out = maxsize;
+
+       do {
+               result = deflate(&stream, Z_FINISH);
+       } while (result == Z_OK);
+
+       if (result != Z_STREAM_END) {
+               free(buffer);
+               return NULL;
+       }
+
+       deflateEnd(&stream);
+       *compressed_size = stream.total_out;
+
+       return buffer;
+}
+
+static char *construct_path(const char *base, int baselen,
+                            const char *filename, int isdir, int *pathlen)
+{
+       int filenamelen = strlen(filename);
+       int len = baselen + filenamelen;
+       char *path, *p;
+
+       if (isdir)
+               len++;
+       p = path = xmalloc(len + 1);
+
+       memcpy(p, base, baselen);
+       p += baselen;
+       memcpy(p, filename, filenamelen);
+       p += filenamelen;
+       if (isdir)
+               *p++ = '/';
+       *p = '\0';
+
+       *pathlen = len;
+
+       return path;
+}
+
+static int write_zip_entry(const unsigned char *sha1,
+                           const char *base, int baselen,
+                           const char *filename, unsigned mode, int stage)
+{
+       struct zip_local_header header;
+       struct zip_dir_header dirent;
+       unsigned long compressed_size;
+       unsigned long uncompressed_size;
+       unsigned long crc;
+       unsigned long direntsize;
+       unsigned long size;
+       int method;
+       int result = -1;
+       int pathlen;
+       unsigned char *out;
+       char *path;
+       char type[20];
+       void *buffer = NULL;
+       void *deflated = NULL;
+
+       crc = crc32(0, Z_NULL, 0);
+
+       path = construct_path(base, baselen, filename, S_ISDIR(mode), &pathlen);
+       if (verbose)
+               fprintf(stderr, "%s\n", path);
+       if (pathlen > 0xffff) {
+               error("path too long (%d chars, SHA1: %s): %s", pathlen,
+                     sha1_to_hex(sha1), path);
+               goto out;
+       }
+
+       if (S_ISDIR(mode)) {
+               method = 0;
+               result = READ_TREE_RECURSIVE;
+               out = NULL;
+               uncompressed_size = 0;
+               compressed_size = 0;
+       } else if (S_ISREG(mode)) {
+               method = zlib_compression_level == 0 ? 0 : 8;
+               result = 0;
+               buffer = read_sha1_file(sha1, type, &size);
+               if (!buffer)
+                       die("cannot read %s", sha1_to_hex(sha1));
+               crc = crc32(crc, buffer, size);
+               out = buffer;
+               uncompressed_size = size;
+               compressed_size = size;
+       } else {
+               error("unsupported file mode: 0%o (SHA1: %s)", mode,
+                     sha1_to_hex(sha1));
+               goto out;
+       }
+
+       if (method == 8) {
+               deflated = zlib_deflate(buffer, size, &compressed_size);
+               if (deflated && compressed_size - 6 < size) {
+                       /* ZLIB --> raw compressed data (see RFC 1950) */
+                       /* CMF and FLG ... */
+                       out = (unsigned char *)deflated + 2;
+                       compressed_size -= 6;   /* ... and ADLER32 */
+               } else {
+                       method = 0;
+                       compressed_size = size;
+               }
+       }
+
+       /* make sure we have enough free space in the dictionary */
+       direntsize = sizeof(struct zip_dir_header) + pathlen;
+       while (zip_dir_size < zip_dir_offset + direntsize) {
+               zip_dir_size += ZIP_DIRECTORY_MIN_SIZE;
+               zip_dir = xrealloc(zip_dir, zip_dir_size);
+       }
+
+       copy_le32(dirent.magic, 0x02014b50);
+       copy_le16(dirent.creator_version, 0);
+       copy_le16(dirent.version, 20);
+       copy_le16(dirent.flags, 0);
+       copy_le16(dirent.compression_method, method);
+       copy_le16(dirent.mtime, zip_time);
+       copy_le16(dirent.mdate, zip_date);
+       copy_le32(dirent.crc32, crc);
+       copy_le32(dirent.compressed_size, compressed_size);
+       copy_le32(dirent.size, uncompressed_size);
+       copy_le16(dirent.filename_length, pathlen);
+       copy_le16(dirent.extra_length, 0);
+       copy_le16(dirent.comment_length, 0);
+       copy_le16(dirent.disk, 0);
+       copy_le16(dirent.attr1, 0);
+       copy_le32(dirent.attr2, 0);
+       copy_le32(dirent.offset, zip_offset);
+       memcpy(zip_dir + zip_dir_offset, &dirent, sizeof(struct zip_dir_header));
+       zip_dir_offset += sizeof(struct zip_dir_header);
+       memcpy(zip_dir + zip_dir_offset, path, pathlen);
+       zip_dir_offset += pathlen;
+       zip_dir_entries++;
+
+       copy_le32(header.magic, 0x04034b50);
+       copy_le16(header.version, 20);
+       copy_le16(header.flags, 0);
+       copy_le16(header.compression_method, method);
+       copy_le16(header.mtime, zip_time);
+       copy_le16(header.mdate, zip_date);
+       copy_le32(header.crc32, crc);
+       copy_le32(header.compressed_size, compressed_size);
+       copy_le32(header.size, uncompressed_size);
+       copy_le16(header.filename_length, pathlen);
+       copy_le16(header.extra_length, 0);
+       write_or_die(1, &header, sizeof(struct zip_local_header));
+       zip_offset += sizeof(struct zip_local_header);
+       write_or_die(1, path, pathlen);
+       zip_offset += pathlen;
+       if (compressed_size > 0) {
+               write_or_die(1, out, compressed_size);
+               zip_offset += compressed_size;
+       }
+
+out:
+       free(buffer);
+       free(deflated);
+       free(path);
+
+       return result;
+}
+
+static void write_zip_trailer(const unsigned char *sha1)
+{
+       struct zip_dir_trailer trailer;
+
+       copy_le32(trailer.magic, 0x06054b50);
+       copy_le16(trailer.disk, 0);
+       copy_le16(trailer.directory_start_disk, 0);
+       copy_le16(trailer.entries_on_this_disk, zip_dir_entries);
+       copy_le16(trailer.entries, zip_dir_entries);
+       copy_le32(trailer.size, zip_dir_offset);
+       copy_le32(trailer.offset, zip_offset);
+       copy_le16(trailer.comment_length, sha1 ? 40 : 0);
+
+       write_or_die(1, zip_dir, zip_dir_offset);
+       write_or_die(1, &trailer, sizeof(struct zip_dir_trailer));
+       if (sha1)
+               write_or_die(1, sha1_to_hex(sha1), 40);
+}
+
+static void dos_time(time_t *time, int *dos_date, int *dos_time)
+{
+       struct tm *t = localtime(time);
+
+       *dos_date = t->tm_mday + (t->tm_mon + 1) * 32 +
+                   (t->tm_year + 1900 - 1980) * 512;
+       *dos_time = t->tm_sec / 2 + t->tm_min * 32 + t->tm_hour * 2048;
+}
+
+int write_zip_archive(struct archiver_args *args)
+{
+       int plen = strlen(args->base);
+
+       dos_time(&args->time, &zip_date, &zip_time);
+
+       zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE);
+       zip_dir_size = ZIP_DIRECTORY_MIN_SIZE;
+       verbose = args->verbose;
+
+       if (args->base && plen > 0 && args->base[plen - 1] == '/') {
+               char *base = xstrdup(args->base);
+               int baselen = strlen(base);
+
+               while (baselen > 0 && base[baselen - 1] == '/')
+                       base[--baselen] = '\0';
+               write_zip_entry(args->tree->object.sha1, "", 0, base, 040777, 0);
+               free(base);
+       }
+       read_tree_recursive(args->tree, args->base, plen, 0,
+                           args->pathspec, write_zip_entry);
+       write_zip_trailer(args->commit_sha1);
+
+       free(zip_dir);
+
+       return 0;
+}
+
+void *parse_extra_zip_args(int argc, const char **argv)
+{
+       for (; argc > 0; argc--, argv++) {
+               const char *arg = argv[0];
+
+               if (arg[0] == '-' && isdigit(arg[1]) && arg[2] == '\0')
+                       zlib_compression_level = arg[1] - '0';
+               else
+                       die("Unknown argument for zip format: %s", arg);
+       }
+       return NULL;
+}
diff --git a/archive.h b/archive.h
new file mode 100644 (file)
index 0000000..16dcdb8
--- /dev/null
+++ b/archive.h
@@ -0,0 +1,47 @@
+#ifndef ARCHIVE_H
+#define ARCHIVE_H
+
+#define MAX_EXTRA_ARGS 32
+#define MAX_ARGS       (MAX_EXTRA_ARGS + 32)
+
+struct archiver_args {
+       const char *base;
+       struct tree *tree;
+       const unsigned char *commit_sha1;
+       time_t time;
+       const char **pathspec;
+       unsigned int verbose : 1;
+       void *extra;
+};
+
+typedef int (*write_archive_fn_t)(struct archiver_args *);
+
+typedef void *(*parse_extra_args_fn_t)(int argc, const char **argv);
+
+struct archiver {
+       const char *name;
+       struct archiver_args args;
+       write_archive_fn_t write_archive;
+       parse_extra_args_fn_t parse_extra;
+};
+
+extern struct archiver archivers[];
+
+extern int parse_archive_args(int argc,
+                             const char **argv,
+                             struct archiver *ar);
+
+extern void parse_treeish_arg(const char **treeish,
+                             struct archiver_args *ar_args,
+                             const char *prefix);
+
+extern void parse_pathspec_arg(const char **pathspec,
+                              struct archiver_args *args);
+/*
+ * Archive-format specific backends.
+ */
+extern int write_tar_archive(struct archiver_args *);
+extern int write_zip_archive(struct archiver_args *);
+extern void *parse_extra_zip_args(int argc, const char **argv);
+
+#endif /* ARCHIVE_H */
diff --git a/blame.c b/blame.c
index 7099b53..8cfd5d9 100644 (file)
--- a/blame.c
+++ b/blame.c
@@ -56,9 +56,9 @@ struct patch {
 static void get_blob(struct commit *commit);
 
 /* Only used for statistics */
-static int num_get_patch = 0;
-static int num_commits = 0;
-static int patch_time = 0;
+static int num_get_patch;
+static int num_commits;
+static int patch_time;
 
 struct blame_diff_state {
        struct xdiff_emit_state xm;
@@ -165,7 +165,7 @@ static int get_blob_sha1(struct tree *t, const char *pathname,
        blame_file = pathname;
        pathspec[0] = pathname;
        pathspec[1] = NULL;
-       memset(blob_sha1, 0, sizeof(blob_sha1));
+       hashclr(blob_sha1);
        read_tree_recursive(t, "", 0, 0, pathspec, get_blob_sha1_internal);
 
        for (i = 0; i < 20; i++) {
@@ -176,7 +176,7 @@ static int get_blob_sha1(struct tree *t, const char *pathname,
        if (i == 20)
                return -1;
 
-       memcpy(sha1, blob_sha1, 20);
+       hashcpy(sha1, blob_sha1);
        return 0;
 }
 
@@ -191,7 +191,7 @@ static int get_blob_sha1_internal(const unsigned char *sha1, const char *base,
            strcmp(blame_file + baselen, pathname))
                return -1;
 
-       memcpy(blob_sha1, sha1, 20);
+       hashcpy(blob_sha1, sha1);
        return -1;
 }
 
@@ -351,10 +351,7 @@ static int fill_util_info(struct commit *commit)
        assert(util);
        assert(util->pathname);
 
-       if (get_blob_sha1(commit->tree, util->pathname, util->sha1))
-               return 1;
-       else
-               return 0;
+       return !!get_blob_sha1(commit->tree, util->pathname, util->sha1);
 }
 
 static void alloc_line_map(struct commit *commit)
@@ -620,7 +617,7 @@ static void simplify_commit(struct rev_info *revs, struct commit *commit)
                                if (new_name) {
                                        struct util_info* putil = get_util(p);
                                        if (!putil->pathname)
-                                               putil->pathname = strdup(new_name);
+                                               putil->pathname = xstrdup(new_name);
                                } else {
                                        *pp = parent->next;
                                        continue;
index 9cf477c..de5f855 100644 (file)
@@ -27,22 +27,25 @@ static const char *prefix;
 static int prefix_length = -1;
 static int newfd = -1;
 
+static int unidiff_zero;
 static int p_value = 1;
-static int allow_binary_replacement = 0;
-static int check_index = 0;
-static int write_index = 0;
-static int cached = 0;
-static int diffstat = 0;
-static int numstat = 0;
-static int summary = 0;
-static int check = 0;
+static int check_index;
+static int write_index;
+static int cached;
+static int diffstat;
+static int numstat;
+static int summary;
+static int check;
 static int apply = 1;
-static int no_add = 0;
-static int show_index_info = 0;
+static int apply_in_reverse;
+static int apply_with_reject;
+static int apply_verbosely;
+static int no_add;
+static int show_index_info;
 static int line_termination = '\n';
 static unsigned long p_context = -1;
 static const char apply_usage[] =
-"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...";
+"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [--reverse] [--reject] [--verbose] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...";
 
 static enum whitespace_eol {
        nowarn_whitespace,
@@ -50,10 +53,10 @@ static enum whitespace_eol {
        error_on_whitespace,
        strip_whitespace,
 } new_whitespace = warn_on_whitespace;
-static int whitespace_error = 0;
+static int whitespace_error;
 static int squelch_whitespace_errors = 5;
-static int applied_after_stripping = 0;
-static const char *patch_input_file = NULL;
+static int applied_after_stripping;
+static const char *patch_input_file;
 
 static void parse_whitespace_option(const char *option)
 {
@@ -108,21 +111,37 @@ static int max_change, max_len;
  */
 static int linenr = 1;
 
+/*
+ * This represents one "hunk" from a patch, starting with
+ * "@@ -oldpos,oldlines +newpos,newlines @@" marker.  The
+ * patch text is pointed at by patch, and its byte length
+ * is stored in size.  leading and trailing are the number
+ * of context lines.
+ */
 struct fragment {
        unsigned long leading, trailing;
        unsigned long oldpos, oldlines;
        unsigned long newpos, newlines;
        const char *patch;
        int size;
+       int rejected;
        struct fragment *next;
 };
 
+/*
+ * When dealing with a binary patch, we reuse "leading" field
+ * to store the type of the binary hunk, either deflated "delta"
+ * or deflated "literal".
+ */
+#define binary_patch_method leading
+#define BINARY_DELTA_DEFLATED  1
+#define BINARY_LITERAL_DEFLATED 2
+
 struct patch {
        char *new_name, *old_name, *def_name;
        unsigned int old_mode, new_mode;
-       int is_rename, is_copy, is_new, is_delete, is_binary, is_reverse;
-#define BINARY_DELTA_DEFLATED 1
-#define BINARY_LITERAL_DEFLATED 2
+       int is_rename, is_copy, is_new, is_delete, is_binary;
+       int rejected;
        unsigned long deflate_origlen;
        int lines_added, lines_deleted;
        int score;
@@ -135,6 +154,24 @@ struct patch {
        struct patch *next;
 };
 
+static void say_patch_name(FILE *output, const char *pre, struct patch *patch, const char *post)
+{
+       fputs(pre, output);
+       if (patch->old_name && patch->new_name &&
+           strcmp(patch->old_name, patch->new_name)) {
+               write_name_quoted(NULL, 0, patch->old_name, 1, output);
+               fputs(" => ", output);
+               write_name_quoted(NULL, 0, patch->new_name, 1, output);
+       }
+       else {
+               const char *n = patch->new_name;
+               if (!n)
+                       n = patch->old_name;
+               write_name_quoted(NULL, 0, n, 1, output);
+       }
+       fputs(post, output);
+}
+
 #define CHUNKSIZE (8192)
 #define SLOP (16)
 
@@ -591,9 +628,7 @@ static char *git_header_name(char *line, int llen)
         * form.
         */
        for (len = 0 ; ; len++) {
-               char c = name[len];
-
-               switch (c) {
+               switch (name[len]) {
                default:
                        continue;
                case '\n':
@@ -819,12 +854,54 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
        return -1;
 }
 
+static void check_whitespace(const char *line, int len)
+{
+       const char *err = "Adds trailing whitespace";
+       int seen_space = 0;
+       int i;
+
+       /*
+        * We know len is at least two, since we have a '+' and we
+        * checked that the last character was a '\n' before calling
+        * this function.  That is, an addition of an empty line would
+        * check the '+' here.  Sneaky...
+        */
+       if (isspace(line[len-2]))
+               goto error;
+
+       /*
+        * Make sure that there is no space followed by a tab in
+        * indentation.
+        */
+       err = "Space in indent is followed by a tab";
+       for (i = 1; i < len; i++) {
+               if (line[i] == '\t') {
+                       if (seen_space)
+                               goto error;
+               }
+               else if (line[i] == ' ')
+                       seen_space = 1;
+               else
+                       break;
+       }
+       return;
+
+ error:
+       whitespace_error++;
+       if (squelch_whitespace_errors &&
+           squelch_whitespace_errors < whitespace_error)
+               ;
+       else
+               fprintf(stderr, "%s.\n%s:%d:%.*s\n",
+                       err, patch_input_file, linenr, len-2, line+1);
+}
+
+
 /*
- * Parse a unified diff. Note that this really needs
- * to parse each fragment separately, since the only
- * way to know the difference between a "---" that is
- * part of a patch, and a "---" that starts the next
- * patch is to look at the line counts..
+ * Parse a unified diff. Note that this really needs to parse each
+ * fragment separately, since the only way to know the difference
+ * between a "---" that is part of a patch, and a "---" that starts
+ * the next patch is to look at the line counts..
  */
 static int parse_fragment(char *line, unsigned long size, struct patch *patch, struct fragment *fragment)
 {
@@ -841,31 +918,14 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
        leading = 0;
        trailing = 0;
 
-       if (patch->is_new < 0) {
-               patch->is_new =  !oldlines;
-               if (!oldlines)
-                       patch->old_name = NULL;
-       }
-       if (patch->is_delete < 0) {
-               patch->is_delete = !newlines;
-               if (!newlines)
-                       patch->new_name = NULL;
-       }
-
-       if (patch->is_new && oldlines)
-               return error("new file depends on old contents");
-       if (patch->is_delete != !newlines) {
-               if (newlines)
-                       return error("deleted file still has contents");
-               fprintf(stderr, "** warning: file %s becomes empty but is not deleted\n", patch->new_name);
-       }
-
        /* Parse the thing.. */
        line += len;
        size -= len;
        linenr++;
        added = deleted = 0;
-       for (offset = len; size > 0; offset += len, size -= len, line += len, linenr++) {
+       for (offset = len;
+            0 < size;
+            offset += len, size -= len, line += len, linenr++) {
                if (!oldlines && !newlines)
                        break;
                len = linelen(line, size);
@@ -887,25 +947,8 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
                        trailing = 0;
                        break;
                case '+':
-                       /*
-                        * We know len is at least two, since we have a '+' and
-                        * we checked that the last character was a '\n' above.
-                        * That is, an addition of an empty line would check
-                        * the '+' here.  Sneaky...
-                        */
-                       if ((new_whitespace != nowarn_whitespace) &&
-                           isspace(line[len-2])) {
-                               whitespace_error++;
-                               if (squelch_whitespace_errors &&
-                                   squelch_whitespace_errors <
-                                   whitespace_error)
-                                       ;
-                               else {
-                                       fprintf(stderr, "Adds trailing whitespace.\n%s:%d:%.*s\n",
-                                               patch_input_file,
-                                               linenr, len-2, line+1);
-                               }
-                       }
+                       if (new_whitespace != nowarn_whitespace)
+                               check_whitespace(line, len);
                        added++;
                        newlines--;
                        trailing = 0;
@@ -938,12 +981,18 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
 
        patch->lines_added += added;
        patch->lines_deleted += deleted;
+
+       if (0 < patch->is_new && oldlines)
+               return error("new file depends on old contents");
+       if (0 < patch->is_delete && newlines)
+               return error("deleted file still has contents");
        return offset;
 }
 
 static int parse_single_patch(char *line, unsigned long size, struct patch *patch)
 {
        unsigned long offset = 0;
+       unsigned long oldlines = 0, newlines = 0, context = 0;
        struct fragment **fragp = &patch->fragments;
 
        while (size > 4 && !memcmp(line, "@@ -", 4)) {
@@ -954,9 +1003,11 @@ static int parse_single_patch(char *line, unsigned long size, struct patch *patc
                len = parse_fragment(line, size, patch, fragment);
                if (len <= 0)
                        die("corrupt patch at line %d", linenr);
-
                fragment->patch = line;
                fragment->size = len;
+               oldlines += fragment->oldlines;
+               newlines += fragment->newlines;
+               context += fragment->leading + fragment->trailing;
 
                *fragp = fragment;
                fragp = &fragment->next;
@@ -965,6 +1016,46 @@ static int parse_single_patch(char *line, unsigned long size, struct patch *patc
                line += len;
                size -= len;
        }
+
+       /*
+        * If something was removed (i.e. we have old-lines) it cannot
+        * be creation, and if something was added it cannot be
+        * deletion.  However, the reverse is not true; --unified=0
+        * patches that only add are not necessarily creation even
+        * though they do not have any old lines, and ones that only
+        * delete are not necessarily deletion.
+        *
+        * Unfortunately, a real creation/deletion patch do _not_ have
+        * any context line by definition, so we cannot safely tell it
+        * apart with --unified=0 insanity.  At least if the patch has
+        * more than one hunk it is not creation or deletion.
+        */
+       if (patch->is_new < 0 &&
+           (oldlines || (patch->fragments && patch->fragments->next)))
+               patch->is_new = 0;
+       if (patch->is_delete < 0 &&
+           (newlines || (patch->fragments && patch->fragments->next)))
+               patch->is_delete = 0;
+       if (!unidiff_zero || context) {
+               /* If the user says the patch is not generated with
+                * --unified=0, or if we have seen context lines,
+                * then not having oldlines means the patch is creation,
+                * and not having newlines means the patch is deletion.
+                */
+               if (patch->is_new < 0 && !oldlines)
+                       patch->is_new = 1;
+               if (patch->is_delete < 0 && !newlines)
+                       patch->is_delete = 1;
+       }
+
+       if (0 < patch->is_new && oldlines)
+               die("new file %s depends on old contents", patch->new_name);
+       if (0 < patch->is_delete && newlines)
+               die("deleted file %s still has contents", patch->old_name);
+       if (!patch->is_delete && !newlines && context)
+               fprintf(stderr, "** warning: file %s becomes empty but "
+                       "is not deleted\n", patch->new_name);
+
        return offset;
 }
 
@@ -978,51 +1069,82 @@ static inline int metadata_changes(struct patch *patch)
                 patch->old_mode != patch->new_mode);
 }
 
-static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
+static char *inflate_it(const void *data, unsigned long size,
+                       unsigned long inflated_size)
 {
-       /* We have read "GIT binary patch\n"; what follows is a line
-        * that says the patch method (currently, either "deflated
-        * literal" or "deflated delta") and the length of data before
-        * deflating; a sequence of 'length-byte' followed by base-85
-        * encoded data follows.
+       z_stream stream;
+       void *out;
+       int st;
+
+       memset(&stream, 0, sizeof(stream));
+
+       stream.next_in = (unsigned char *)data;
+       stream.avail_in = size;
+       stream.next_out = out = xmalloc(inflated_size);
+       stream.avail_out = inflated_size;
+       inflateInit(&stream);
+       st = inflate(&stream, Z_FINISH);
+       if ((st != Z_STREAM_END) || stream.total_out != inflated_size) {
+               free(out);
+               return NULL;
+       }
+       return out;
+}
+
+static struct fragment *parse_binary_hunk(char **buf_p,
+                                         unsigned long *sz_p,
+                                         int *status_p,
+                                         int *used_p)
+{
+       /* Expect a line that begins with binary patch method ("literal"
+        * or "delta"), followed by the length of data before deflating.
+        * a sequence of 'length-byte' followed by base-85 encoded data
+        * should follow, terminated by a newline.
         *
         * Each 5-byte sequence of base-85 encodes up to 4 bytes,
         * and we would limit the patch line to 66 characters,
         * so one line can fit up to 13 groups that would decode
         * to 52 bytes max.  The length byte 'A'-'Z' corresponds
         * to 1-26 bytes, and 'a'-'z' corresponds to 27-52 bytes.
-        * The end of binary is signaled with an empty line.
         */
        int llen, used;
-       struct fragment *fragment;
+       unsigned long size = *sz_p;
+       char *buffer = *buf_p;
+       int patch_method;
+       unsigned long origlen;
        char *data = NULL;
+       int hunk_size = 0;
+       struct fragment *frag;
 
-       patch->fragments = fragment = xcalloc(1, sizeof(*fragment));
-
-       /* Grab the type of patch */
        llen = linelen(buffer, size);
        used = llen;
-       linenr++;
+
+       *status_p = 0;
 
        if (!strncmp(buffer, "delta ", 6)) {
-               patch->is_binary = BINARY_DELTA_DEFLATED;
-               patch->deflate_origlen = strtoul(buffer + 6, NULL, 10);
+               patch_method = BINARY_DELTA_DEFLATED;
+               origlen = strtoul(buffer + 6, NULL, 10);
        }
        else if (!strncmp(buffer, "literal ", 8)) {
-               patch->is_binary = BINARY_LITERAL_DEFLATED;
-               patch->deflate_origlen = strtoul(buffer + 8, NULL, 10);
+               patch_method = BINARY_LITERAL_DEFLATED;
+               origlen = strtoul(buffer + 8, NULL, 10);
        }
        else
-               return error("unrecognized binary patch at line %d: %.*s",
-                            linenr-1, llen-1, buffer);
+               return NULL;
+
+       linenr++;
        buffer += llen;
        while (1) {
                int byte_length, max_byte_length, newsize;
                llen = linelen(buffer, size);
                used += llen;
                linenr++;
-               if (llen == 1)
+               if (llen == 1) {
+                       /* consume the blank line */
+                       buffer++;
+                       size--;
                        break;
+               }
                /* Minimum line is "A00000\n" which is 7-byte long,
                 * and the line length must be multiple of 5 plus 2.
                 */
@@ -1043,21 +1165,78 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
                if (max_byte_length < byte_length ||
                    byte_length <= max_byte_length - 4)
                        goto corrupt;
-               newsize = fragment->size + byte_length;
+               newsize = hunk_size + byte_length;
                data = xrealloc(data, newsize);
-               if (decode_85(data + fragment->size,
-                             buffer + 1,
-                             byte_length))
+               if (decode_85(data + hunk_size, buffer + 1, byte_length))
                        goto corrupt;
-               fragment->size = newsize;
+               hunk_size = newsize;
                buffer += llen;
                size -= llen;
        }
-       fragment->patch = data;
-       return used;
+
+       frag = xcalloc(1, sizeof(*frag));
+       frag->patch = inflate_it(data, hunk_size, origlen);
+       if (!frag->patch)
+               goto corrupt;
+       free(data);
+       frag->size = origlen;
+       *buf_p = buffer;
+       *sz_p = size;
+       *used_p = used;
+       frag->binary_patch_method = patch_method;
+       return frag;
+
  corrupt:
-       return error("corrupt binary patch at line %d: %.*s",
-                    linenr-1, llen-1, buffer);
+       free(data);
+       *status_p = -1;
+       error("corrupt binary patch at line %d: %.*s",
+             linenr-1, llen-1, buffer);
+       return NULL;
+}
+
+static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
+{
+       /* We have read "GIT binary patch\n"; what follows is a line
+        * that says the patch method (currently, either "literal" or
+        * "delta") and the length of data before deflating; a
+        * sequence of 'length-byte' followed by base-85 encoded data
+        * follows.
+        *
+        * When a binary patch is reversible, there is another binary
+        * hunk in the same format, starting with patch method (either
+        * "literal" or "delta") with the length of data, and a sequence
+        * of length-byte + base-85 encoded data, terminated with another
+        * empty line.  This data, when applied to the postimage, produces
+        * the preimage.
+        */
+       struct fragment *forward;
+       struct fragment *reverse;
+       int status;
+       int used, used_1;
+
+       forward = parse_binary_hunk(&buffer, &size, &status, &used);
+       if (!forward && !status)
+               /* there has to be one hunk (forward hunk) */
+               return error("unrecognized binary patch at line %d", linenr-1);
+       if (status)
+               /* otherwise we already gave an error message */
+               return status;
+
+       reverse = parse_binary_hunk(&buffer, &size, &status, &used_1);
+       if (reverse)
+               used += used_1;
+       else if (status) {
+               /* not having reverse hunk is not an error, but having
+                * a corrupt reverse hunk is.
+                */
+               free((void*) forward->patch);
+               free(forward);
+               return status;
+       }
+       forward->next = reverse;
+       patch->fragments = forward;
+       patch->is_binary = 1;
+       return used;
 }
 
 static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
@@ -1105,14 +1284,12 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
                        }
                }
 
-               /* Empty patch cannot be applied if:
-                * - it is a binary patch and we do not do binary_replace, or
-                * - text patch without metadata change
+               /* Empty patch cannot be applied if it is a text patch
+                * without metadata change.  A binary patch appears
+                * empty to us here.
                 */
                if ((apply || check) &&
-                   (patch->is_binary
-                    ? !allow_binary_replacement
-                    : !metadata_changes(patch)))
+                   (!patch->is_binary && !metadata_changes(patch)))
                        die("patch with only garbage at line %d", linenr);
        }
 
@@ -1143,7 +1320,6 @@ static void reverse_patches(struct patch *p)
                        swap(frag->newpos, frag->oldpos);
                        swap(frag->newlines, frag->oldlines);
                }
-               p->is_reverse = !p->is_reverse;
        }
 }
 
@@ -1206,8 +1382,7 @@ static void show_stats(struct patch *patch)
                printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
                       len, name, patch->lines_added + patch->lines_deleted,
                       add, pluses, del, minuses);
-       if (qname)
-               free(qname);
+       free(qname);
 }
 
 static int read_old_data(struct stat *st, const char *path, void *buf, unsigned long size)
@@ -1345,26 +1520,71 @@ static int apply_line(char *output, const char *patch, int plen)
 {
        /* plen is number of bytes to be copied from patch,
         * starting at patch+1 (patch[0] is '+').  Typically
-        * patch[plen] is '\n'.
+        * patch[plen] is '\n', unless this is the incomplete
+        * last line.
         */
+       int i;
        int add_nl_to_tail = 0;
-       if ((new_whitespace == strip_whitespace) &&
-           1 < plen && isspace(patch[plen-1])) {
+       int fixed = 0;
+       int last_tab_in_indent = -1;
+       int last_space_in_indent = -1;
+       int need_fix_leading_space = 0;
+       char *buf;
+
+       if ((new_whitespace != strip_whitespace) || !whitespace_error) {
+               memcpy(output, patch + 1, plen);
+               return plen;
+       }
+
+       if (1 < plen && isspace(patch[plen-1])) {
                if (patch[plen] == '\n')
                        add_nl_to_tail = 1;
                plen--;
                while (0 < plen && isspace(patch[plen]))
                        plen--;
-               applied_after_stripping++;
+               fixed = 1;
        }
-       memcpy(output, patch + 1, plen);
+
+       for (i = 1; i < plen; i++) {
+               char ch = patch[i];
+               if (ch == '\t') {
+                       last_tab_in_indent = i;
+                       if (0 <= last_space_in_indent)
+                               need_fix_leading_space = 1;
+               }
+               else if (ch == ' ')
+                       last_space_in_indent = i;
+               else
+                       break;
+       }
+
+       buf = output;
+       if (need_fix_leading_space) {
+               /* between patch[1..last_tab_in_indent] strip the
+                * funny spaces, updating them to tab as needed.
+                */
+               for (i = 1; i < last_tab_in_indent; i++, plen--) {
+                       char ch = patch[i];
+                       if (ch != ' ')
+                               *output++ = ch;
+                       else if ((i % 8) == 0)
+                               *output++ = '\t';
+               }
+               fixed = 1;
+               i = last_tab_in_indent;
+       }
+       else
+               i = 1;
+
+       memcpy(output, patch + i, plen);
        if (add_nl_to_tail)
                output[plen++] = '\n';
-       return plen;
+       if (fixed)
+               applied_after_stripping++;
+       return output + plen - buf;
 }
 
-static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
-       int reverse, int inaccurate_eof)
+static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, int inaccurate_eof)
 {
        int match_beginning, match_end;
        char *buf = desc->buffer;
@@ -1396,7 +1616,7 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
                if (len < size && patch[len] == '\\')
                        plen--;
                first = *patch;
-               if (reverse) {
+               if (apply_in_reverse) {
                        if (first == '-')
                                first = '+';
                        else if (first == '+')
@@ -1439,14 +1659,25 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
        /*
         * If we don't have any leading/trailing data in the patch,
         * we want it to match at the beginning/end of the file.
+        *
+        * But that would break if the patch is generated with
+        * --unified=0; sane people wouldn't do that to cause us
+        * trouble, but we try to please not so sane ones as well.
         */
-       match_beginning = !leading && (frag->oldpos == 1);
-       match_end = !trailing;
+       if (unidiff_zero) {
+               match_beginning = (!leading && !frag->oldpos);
+               match_end = 0;
+       }
+       else {
+               match_beginning = !leading && (frag->oldpos == 1);
+               match_end = !trailing;
+       }
 
        lines = 0;
        pos = frag->newpos;
        for (;;) {
-               offset = find_offset(buf, desc->size, oldlines, oldsize, pos, &lines);
+               offset = find_offset(buf, desc->size,
+                                    oldlines, oldsize, pos, &lines);
                if (match_end && offset + oldsize != desc->size)
                        offset = -1;
                if (match_beginning && offset)
@@ -1459,8 +1690,10 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
                        /* Warn if it was necessary to reduce the number
                         * of context lines.
                         */
-                       if ((leading != frag->leading) || (trailing != frag->trailing))
-                               fprintf(stderr, "Context reduced to (%ld/%ld) to apply fragment at %d\n",
+                       if ((leading != frag->leading) ||
+                           (trailing != frag->trailing))
+                               fprintf(stderr, "Context reduced to (%ld/%ld)"
+                                       " to apply fragment at %d\n",
                                        leading, trailing, pos + lines);
 
                        if (size > alloc) {
@@ -1470,7 +1703,9 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
                                desc->buffer = buf;
                        }
                        desc->size = size;
-                       memmove(buf + offset + newsize, buf + offset + oldsize, size - offset - newsize);
+                       memmove(buf + offset + newsize,
+                               buf + offset + oldsize,
+                               size - offset - newsize);
                        memcpy(buf + offset, newlines, newsize);
                        offset = 0;
 
@@ -1506,28 +1741,6 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
        return offset;
 }
 
-static char *inflate_it(const void *data, unsigned long size,
-                       unsigned long inflated_size)
-{
-       z_stream stream;
-       void *out;
-       int st;
-
-       memset(&stream, 0, sizeof(stream));
-
-       stream.next_in = (unsigned char *)data;
-       stream.avail_in = size;
-       stream.next_out = out = xmalloc(inflated_size);
-       stream.avail_out = inflated_size;
-       inflateInit(&stream);
-       st = inflate(&stream, Z_FINISH);
-       if ((st != Z_STREAM_END) || stream.total_out != inflated_size) {
-               free(out);
-               return NULL;
-       }
-       return out;
-}
-
 static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch)
 {
        unsigned long dst_size;
@@ -1535,30 +1748,29 @@ static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch)
        void *data;
        void *result;
 
-       /* Binary patch is irreversible */
-       if (patch->is_reverse)
-               return error("cannot reverse-apply a binary patch to '%s'",
-                            patch->new_name
-                            ? patch->new_name : patch->old_name);
-
-       data = inflate_it(fragment->patch, fragment->size,
-                         patch->deflate_origlen);
-       if (!data)
-               return error("corrupt patch data");
-       switch (patch->is_binary) {
+       /* Binary patch is irreversible without the optional second hunk */
+       if (apply_in_reverse) {
+               if (!fragment->next)
+                       return error("cannot reverse-apply a binary patch "
+                                    "without the reverse hunk to '%s'",
+                                    patch->new_name
+                                    ? patch->new_name : patch->old_name);
+               fragment = fragment->next;
+       }
+       data = (void*) fragment->patch;
+       switch (fragment->binary_patch_method) {
        case BINARY_DELTA_DEFLATED:
                result = patch_delta(desc->buffer, desc->size,
                                     data,
-                                    patch->deflate_origlen,
+                                    fragment->size,
                                     &dst_size);
                free(desc->buffer);
                desc->buffer = result;
-               free(data);
                break;
        case BINARY_LITERAL_DEFLATED:
                free(desc->buffer);
                desc->buffer = data;
-               dst_size = patch->deflate_origlen;
+               dst_size = fragment->size;
                break;
        }
        if (!desc->buffer)
@@ -1574,11 +1786,6 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
        unsigned char hdr[50];
        int hdrlen;
 
-       if (!allow_binary_replacement)
-               return error("cannot apply binary patch to '%s' "
-                            "without --allow-binary-replacement",
-                            name);
-
        /* For safety, we require patch index line to contain
         * full 40-byte textual SHA1 for old and new, at least for now.
         */
@@ -1609,7 +1816,7 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
        }
 
        get_sha1_hex(patch->new_sha1_prefix, sha1);
-       if (!memcmp(sha1, null_sha1, 20)) {
+       if (is_null_sha1(sha1)) {
                free(desc->buffer);
                desc->alloc = desc->size = 0;
                desc->buffer = NULL;
@@ -1642,7 +1849,7 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
                write_sha1_file_prepare(desc->buffer, desc->size, blob_type,
                                        sha1, hdr, &hdrlen);
                if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
-                       return error("binary patch to '%s' creates incorrect result", name);
+                       return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)", name, patch->new_sha1_prefix, sha1_to_hex(sha1));
        }
 
        return 0;
@@ -1657,10 +1864,12 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
                return apply_binary(desc, patch);
 
        while (frag) {
-               if (apply_one_fragment(desc, frag, patch->is_reverse,
-                                       patch->inaccurate_eof) < 0)
-                       return error("patch failed: %s:%ld",
-                                    name, frag->oldpos);
+               if (apply_one_fragment(desc, frag, patch->inaccurate_eof)) {
+                       error("patch failed: %s:%ld", name, frag->oldpos);
+                       if (!apply_with_reject)
+                               return -1;
+                       frag->rejected = 1;
+               }
                frag = frag->next;
        }
        return 0;
@@ -1696,8 +1905,9 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
        desc.size = size;
        desc.alloc = alloc;
        desc.buffer = buf;
+
        if (apply_fragments(&desc, patch) < 0)
-               return -1;
+               return -1; /* note with --reject this succeeds. */
 
        /* NUL terminate the result */
        if (desc.alloc <= desc.size)
@@ -1707,7 +1917,7 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
        patch->result = desc.buffer;
        patch->resultsize = desc.size;
 
-       if (patch->is_delete && patch->resultsize)
+       if (0 < patch->is_delete && patch->resultsize)
                return error("removal patch leaves file contents");
 
        return 0;
@@ -1722,6 +1932,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
        struct cache_entry *ce = NULL;
        int ok_if_exists;
 
+       patch->rejected = 1; /* we will drop this after we succeed */
        if (old_name) {
                int changed = 0;
                int stat_ret = 0;
@@ -1778,7 +1989,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
                                old_name, st_mode, patch->old_mode);
        }
 
-       if (new_name && prev_patch && prev_patch->is_delete &&
+       if (new_name && prev_patch && 0 < prev_patch->is_delete &&
            !strcmp(prev_patch->old_name, new_name))
                /* A type-change diff is always split into a patch to
                 * delete old, immediately followed by a patch to
@@ -1791,7 +2002,8 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
        else
                ok_if_exists = 0;
 
-       if (new_name && (patch->is_new | patch->is_rename | patch->is_copy)) {
+       if (new_name &&
+           ((0 < patch->is_new) | (0 < patch->is_rename) | patch->is_copy)) {
                if (check_index &&
                    cache_name_pos(new_name, strlen(new_name)) >= 0 &&
                    !ok_if_exists)
@@ -1808,7 +2020,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
                                return error("%s: %s", new_name, strerror(errno));
                }
                if (!patch->new_mode) {
-                       if (patch->is_new)
+                       if (0 < patch->is_new)
                                patch->new_mode = S_IFREG | 0644;
                        else
                                patch->new_mode = patch->old_mode;
@@ -1827,24 +2039,23 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
 
        if (apply_data(patch, &st, ce) < 0)
                return error("%s: patch does not apply", name);
+       patch->rejected = 0;
        return 0;
 }
 
 static int check_patch_list(struct patch *patch)
 {
        struct patch *prev_patch = NULL;
-       int error = 0;
+       int err = 0;
 
        for (prev_patch = NULL; patch ; patch = patch->next) {
-               error |= check_patch(patch, prev_patch);
+               if (apply_verbosely)
+                       say_patch_name(stderr,
+                                      "Checking patch ", patch, "...\n");
+               err |= check_patch(patch, prev_patch);
                prev_patch = patch;
        }
-       return error;
-}
-
-static inline int is_null_sha1(const unsigned char *sha1)
-{
-       return !memcmp(sha1, null_sha1, 20);
+       return err;
 }
 
 static void show_index_list(struct patch *list)
@@ -1860,7 +2071,7 @@ static void show_index_list(struct patch *list)
                const char *name;
 
                name = patch->old_name ? patch->old_name : patch->new_name;
-               if (patch->is_new)
+               if (0 < patch->is_new)
                        sha1_ptr = null_sha1;
                else if (get_sha1(patch->old_sha1_prefix, sha1))
                        die("sha1 information is lacking or useless (%s).",
@@ -2150,23 +2361,99 @@ static void write_out_one_result(struct patch *patch, int phase)
        if (phase == 0)
                remove_file(patch);
        if (phase == 1)
-       create_file(patch);
+               create_file(patch);
+}
+
+static int write_out_one_reject(struct patch *patch)
+{
+       FILE *rej;
+       char namebuf[PATH_MAX];
+       struct fragment *frag;
+       int cnt = 0;
+
+       for (cnt = 0, frag = patch->fragments; frag; frag = frag->next) {
+               if (!frag->rejected)
+                       continue;
+               cnt++;
+       }
+
+       if (!cnt) {
+               if (apply_verbosely)
+                       say_patch_name(stderr,
+                                      "Applied patch ", patch, " cleanly.\n");
+               return 0;
+       }
+
+       /* This should not happen, because a removal patch that leaves
+        * contents are marked "rejected" at the patch level.
+        */
+       if (!patch->new_name)
+               die("internal error");
+
+       /* Say this even without --verbose */
+       say_patch_name(stderr, "Applying patch ", patch, " with");
+       fprintf(stderr, " %d rejects...\n", cnt);
+
+       cnt = strlen(patch->new_name);
+       if (ARRAY_SIZE(namebuf) <= cnt + 5) {
+               cnt = ARRAY_SIZE(namebuf) - 5;
+               fprintf(stderr,
+                       "warning: truncating .rej filename to %.*s.rej",
+                       cnt - 1, patch->new_name);
+       }
+       memcpy(namebuf, patch->new_name, cnt);
+       memcpy(namebuf + cnt, ".rej", 5);
+
+       rej = fopen(namebuf, "w");
+       if (!rej)
+               return error("cannot open %s: %s", namebuf, strerror(errno));
+
+       /* Normal git tools never deal with .rej, so do not pretend
+        * this is a git patch by saying --git nor give extended
+        * headers.  While at it, maybe please "kompare" that wants
+        * the trailing TAB and some garbage at the end of line ;-).
+        */
+       fprintf(rej, "diff a/%s b/%s\t(rejected hunks)\n",
+               patch->new_name, patch->new_name);
+       for (cnt = 1, frag = patch->fragments;
+            frag;
+            cnt++, frag = frag->next) {
+               if (!frag->rejected) {
+                       fprintf(stderr, "Hunk #%d applied cleanly.\n", cnt);
+                       continue;
+               }
+               fprintf(stderr, "Rejected hunk #%d.\n", cnt);
+               fprintf(rej, "%.*s", frag->size, frag->patch);
+               if (frag->patch[frag->size-1] != '\n')
+                       fputc('\n', rej);
+       }
+       fclose(rej);
+       return -1;
 }
 
-static void write_out_results(struct patch *list, int skipped_patch)
+static int write_out_results(struct patch *list, int skipped_patch)
 {
        int phase;
+       int errs = 0;
+       struct patch *l;
 
        if (!list && !skipped_patch)
-               die("No changes");
+               return error("No changes");
 
        for (phase = 0; phase < 2; phase++) {
-               struct patch *l = list;
+               l = list;
                while (l) {
-                       write_out_one_result(l, phase);
+                       if (l->rejected)
+                               errs = 1;
+                       else {
+                               write_out_one_result(l, phase);
+                               if (phase == 1 && write_out_one_reject(l))
+                                       errs = 1;
+                       }
                        l = l->next;
                }
        }
+       return errs;
 }
 
 static struct lock_file lock_file;
@@ -2194,8 +2481,7 @@ static int use_patch(struct patch *p)
        return 1;
 }
 
-static int apply_patch(int fd, const char *filename,
-               int reverse, int inaccurate_eof)
+static int apply_patch(int fd, const char *filename, int inaccurate_eof)
 {
        unsigned long offset, size;
        char *buffer = read_patch_file(fd, &size);
@@ -2215,7 +2501,7 @@ static int apply_patch(int fd, const char *filename,
                nr = parse_chunk(buffer + offset, size, patch);
                if (nr < 0)
                        break;
-               if (reverse)
+               if (apply_in_reverse)
                        reverse_patches(patch);
                if (use_patch(patch)) {
                        patch_stats(patch);
@@ -2242,11 +2528,13 @@ static int apply_patch(int fd, const char *filename,
                        die("unable to read index file");
        }
 
-       if ((check || apply) && check_patch_list(list) < 0)
+       if ((check || apply) &&
+           check_patch_list(list) < 0 &&
+           !apply_with_reject)
                exit(1);
 
-       if (apply)
-               write_out_results(list, skipped_patch);
+       if (apply && write_out_results(list, skipped_patch))
+               exit(1);
 
        if (show_index_info)
                show_index_list(list);
@@ -2267,7 +2555,7 @@ static int apply_patch(int fd, const char *filename,
 static int git_apply_config(const char *var, const char *value)
 {
        if (!strcmp(var, "apply.whitespace")) {
-               apply_default_whitespace = strdup(value);
+               apply_default_whitespace = xstrdup(value);
                return 0;
        }
        return git_default_config(var, value);
@@ -2278,8 +2566,8 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
 {
        int i;
        int read_stdin = 1;
-       int reverse = 0;
        int inaccurate_eof = 0;
+       int errs = 0;
 
        const char *whitespace_option = NULL;
 
@@ -2289,7 +2577,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                int fd;
 
                if (!strcmp(arg, "-")) {
-                       apply_patch(0, "<stdin>", reverse, inaccurate_eof);
+                       errs |= apply_patch(0, "<stdin>", inaccurate_eof);
                        read_stdin = 0;
                        continue;
                }
@@ -2315,8 +2603,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                }
                if (!strcmp(arg, "--allow-binary-replacement") ||
                    !strcmp(arg, "--binary")) {
-                       allow_binary_replacement = 1;
-                       continue;
+                       continue; /* now no-op */
                }
                if (!strcmp(arg, "--numstat")) {
                        apply = 0;
@@ -2367,7 +2654,19 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                        continue;
                }
                if (!strcmp(arg, "-R") || !strcmp(arg, "--reverse")) {
-                       reverse = 1;
+                       apply_in_reverse = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--unidiff-zero")) {
+                       unidiff_zero = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--reject")) {
+                       apply = apply_with_reject = apply_verbosely = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--verbose")) {
+                       apply_verbosely = 1;
                        continue;
                }
                if (!strcmp(arg, "--inaccurate-eof")) {
@@ -2390,18 +2689,19 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                        usage(apply_usage);
                read_stdin = 0;
                set_default_whitespace_mode(whitespace_option);
-               apply_patch(fd, arg, reverse, inaccurate_eof);
+               errs |= apply_patch(fd, arg, inaccurate_eof);
                close(fd);
        }
        set_default_whitespace_mode(whitespace_option);
        if (read_stdin)
-               apply_patch(0, "<stdin>", reverse, inaccurate_eof);
+               errs |= apply_patch(0, "<stdin>", inaccurate_eof);
        if (whitespace_error) {
                if (squelch_whitespace_errors &&
                    squelch_whitespace_errors < whitespace_error) {
                        int squelched =
                                whitespace_error - squelch_whitespace_errors;
-                       fprintf(stderr, "warning: squelched %d whitespace error%s\n",
+                       fprintf(stderr, "warning: squelched %d "
+                               "whitespace error%s\n",
                                squelched,
                                squelched == 1 ? "" : "s");
                }
@@ -2429,5 +2729,5 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                        die("Unable to write new index file");
        }
 
-       return 0;
+       return !!errs;
 }
diff --git a/builtin-archive.c b/builtin-archive.c
new file mode 100644 (file)
index 0000000..6dabdee
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2006 Franck Bui-Huu
+ * Copyright (c) 2006 Rene Scharfe
+ */
+#include <time.h>
+#include "cache.h"
+#include "builtin.h"
+#include "archive.h"
+#include "commit.h"
+#include "tree-walk.h"
+#include "exec_cmd.h"
+#include "pkt-line.h"
+#include "sideband.h"
+
+static const char archive_usage[] = \
+"git-archive --format=<fmt> [--prefix=<prefix>/] [--verbose] [<extra>] <tree-ish> [path...]";
+
+struct archiver archivers[] = {
+       {
+               .name           = "tar",
+               .write_archive  = write_tar_archive,
+       },
+       {
+               .name           = "zip",
+               .write_archive  = write_zip_archive,
+               .parse_extra    = parse_extra_zip_args,
+       },
+};
+
+static int run_remote_archiver(const char *remote, int argc,
+                              const char **argv)
+{
+       char *url, buf[LARGE_PACKET_MAX];
+       int fd[2], i, len, rv;
+       pid_t pid;
+       const char *exec = "git-upload-archive";
+       int exec_at = 0;
+
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+               if (!strncmp("--exec=", arg, 7)) {
+                       if (exec_at)
+                               die("multiple --exec specified");
+                       exec = arg + 7;
+                       exec_at = i;
+                       break;
+               }
+       }
+
+       url = xstrdup(remote);
+       pid = git_connect(fd, url, exec);
+       if (pid < 0)
+               return pid;
+
+       for (i = 1; i < argc; i++) {
+               if (i == exec_at)
+                       continue;
+               packet_write(fd[1], "argument %s\n", argv[i]);
+       }
+       packet_flush(fd[1]);
+
+       len = packet_read_line(fd[0], buf, sizeof(buf));
+       if (!len)
+               die("git-archive: expected ACK/NAK, got EOF");
+       if (buf[len-1] == '\n')
+               buf[--len] = 0;
+       if (strcmp(buf, "ACK")) {
+               if (len > 5 && !strncmp(buf, "NACK ", 5))
+                       die("git-archive: NACK %s", buf + 5);
+               die("git-archive: protocol error");
+       }
+
+       len = packet_read_line(fd[0], buf, sizeof(buf));
+       if (len)
+               die("git-archive: expected a flush");
+
+       /* Now, start reading from fd[0] and spit it out to stdout */
+       rv = recv_sideband("archive", fd[0], 1, 2, buf, sizeof(buf));
+       close(fd[0]);
+       rv |= finish_connect(pid);
+
+       return !!rv;
+}
+
+static int init_archiver(const char *name, struct archiver *ar)
+{
+       int rv = -1, i;
+
+       for (i = 0; i < ARRAY_SIZE(archivers); i++) {
+               if (!strcmp(name, archivers[i].name)) {
+                       memcpy(ar, &archivers[i], sizeof(struct archiver));
+                       rv = 0;
+                       break;
+               }
+       }
+       return rv;
+}
+
+void parse_pathspec_arg(const char **pathspec, struct archiver_args *ar_args)
+{
+       ar_args->pathspec = get_pathspec(ar_args->base, pathspec);
+}
+
+void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,
+                      const char *prefix)
+{
+       const char *name = argv[0];
+       const unsigned char *commit_sha1;
+       time_t archive_time;
+       struct tree *tree;
+       struct commit *commit;
+       unsigned char sha1[20];
+
+       if (get_sha1(name, sha1))
+               die("Not a valid object name");
+
+       commit = lookup_commit_reference_gently(sha1, 1);
+       if (commit) {
+               commit_sha1 = commit->object.sha1;
+               archive_time = commit->date;
+       } else {
+               commit_sha1 = NULL;
+               archive_time = time(NULL);
+       }
+
+       tree = parse_tree_indirect(sha1);
+       if (tree == NULL)
+               die("not a tree object");
+
+       if (prefix) {
+               unsigned char tree_sha1[20];
+               unsigned int mode;
+               int err;
+
+               err = get_tree_entry(tree->object.sha1, prefix,
+                                    tree_sha1, &mode);
+               if (err || !S_ISDIR(mode))
+                       die("current working directory is untracked");
+
+               free(tree);
+               tree = parse_tree_indirect(tree_sha1);
+       }
+       ar_args->tree = tree;
+       ar_args->commit_sha1 = commit_sha1;
+       ar_args->time = archive_time;
+}
+
+int parse_archive_args(int argc, const char **argv, struct archiver *ar)
+{
+       const char *extra_argv[MAX_EXTRA_ARGS];
+       int extra_argc = 0;
+       const char *format = NULL; /* might want to default to "tar" */
+       const char *base = "";
+       int verbose = 0;
+       int i;
+
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+
+               if (!strcmp(arg, "--list") || !strcmp(arg, "-l")) {
+                       for (i = 0; i < ARRAY_SIZE(archivers); i++)
+                               printf("%s\n", archivers[i].name);
+                       exit(0);
+               }
+               if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) {
+                       verbose = 1;
+                       continue;
+               }
+               if (!strncmp(arg, "--format=", 9)) {
+                       format = arg + 9;
+                       continue;
+               }
+               if (!strncmp(arg, "--prefix=", 9)) {
+                       base = arg + 9;
+                       continue;
+               }
+               if (!strcmp(arg, "--")) {
+                       i++;
+                       break;
+               }
+               if (arg[0] == '-') {
+                       if (extra_argc > MAX_EXTRA_ARGS - 1)
+                               die("Too many extra options");
+                       extra_argv[extra_argc++] = arg;
+                       continue;
+               }
+               break;
+       }
+
+       /* We need at least one parameter -- tree-ish */
+       if (argc - 1 < i)
+               usage(archive_usage);
+       if (!format)
+               die("You must specify an archive format");
+       if (init_archiver(format, ar) < 0)
+               die("Unknown archive format '%s'", format);
+
+       if (extra_argc) {
+               if (!ar->parse_extra)
+                       die("'%s' format does not handle %s",
+                           ar->name, extra_argv[0]);
+               ar->args.extra = ar->parse_extra(extra_argc, extra_argv);
+       }
+       ar->args.verbose = verbose;
+       ar->args.base = base;
+
+       return i;
+}
+
+static const char *extract_remote_arg(int *ac, const char **av)
+{
+       int ix, iy, cnt = *ac;
+       int no_more_options = 0;
+       const char *remote = NULL;
+
+       for (ix = iy = 1; ix < cnt; ix++) {
+               const char *arg = av[ix];
+               if (!strcmp(arg, "--"))
+                       no_more_options = 1;
+               if (!no_more_options) {
+                       if (!strncmp(arg, "--remote=", 9)) {
+                               if (remote)
+                                       die("Multiple --remote specified");
+                               remote = arg + 9;
+                               continue;
+                       }
+                       if (arg[0] != '-')
+                               no_more_options = 1;
+               }
+               if (ix != iy)
+                       av[iy] = arg;
+               iy++;
+       }
+       if (remote) {
+               av[--cnt] = NULL;
+               *ac = cnt;
+       }
+       return remote;
+}
+
+int cmd_archive(int argc, const char **argv, const char *prefix)
+{
+       struct archiver ar;
+       int tree_idx;
+       const char *remote = NULL;
+
+       remote = extract_remote_arg(&argc, argv);
+       if (remote)
+               return run_remote_archiver(remote, argc, argv);
+
+       setlinebuf(stderr);
+
+       memset(&ar, 0, sizeof(ar));
+       tree_idx = parse_archive_args(argc, argv, &ar);
+       if (prefix == NULL)
+               prefix = setup_git_directory();
+
+       argv += tree_idx;
+       parse_treeish_arg(argv, &ar.args, prefix);
+       parse_pathspec_arg(argv + 1, &ar.args);
+
+       return ar.write_archive(&ar.args);
+}
index 814fb07..6c16bfa 100644 (file)
@@ -9,24 +9,7 @@
 #include "tree.h"
 #include "builtin.h"
 
-static void flush_buffer(const char *buf, unsigned long size)
-{
-       while (size > 0) {
-               long ret = xwrite(1, buf, size);
-               if (ret < 0) {
-                       /* Ignore epipe */
-                       if (errno == EPIPE)
-                               break;
-                       die("git-cat-file: %s", strerror(errno));
-               } else if (!ret) {
-                       die("git-cat-file: disk full?");
-               }
-               size -= ret;
-               buf += ret;
-       }
-}
-
-static int pprint_tag(const unsigned char *sha1, const char *buf, unsigned long size)
+static void pprint_tag(const unsigned char *sha1, const char *buf, unsigned long size)
 {
        /* the parser in tag.c is useless here. */
        const char *endp = buf + size;
@@ -42,7 +25,7 @@ static int pprint_tag(const unsigned char *sha1, const char *buf, unsigned long
                        /* Found the tagger line.  Copy out the contents
                         * of the buffer so far.
                         */
-                       flush_buffer(buf, cp - buf);
+                       write_or_die(1, buf, cp - buf);
 
                        /*
                         * Do something intelligent, like pretty-printing
@@ -61,18 +44,18 @@ static int pprint_tag(const unsigned char *sha1, const char *buf, unsigned long
                                                sp++;
                                        if (sp == cp) {
                                                /* give up */
-                                               flush_buffer(tagger,
+                                               write_or_die(1, tagger,
                                                             cp - tagger);
                                                break;
                                        }
                                        while (sp < cp &&
                                               !('0' <= *sp && *sp <= '9'))
                                                sp++;
-                                       flush_buffer(tagger, sp - tagger);
+                                       write_or_die(1, tagger, sp - tagger);
                                        date = strtoul(sp, &ep, 10);
                                        tz = strtol(ep, NULL, 10);
-                                       sp = show_date(date, tz);
-                                       flush_buffer(sp, strlen(sp));
+                                       sp = show_date(date, tz, 0);
+                                       write_or_die(1, sp, strlen(sp));
                                        xwrite(1, "\n", 1);
                                        break;
                                }
@@ -90,8 +73,7 @@ static int pprint_tag(const unsigned char *sha1, const char *buf, unsigned long
         * remainder as is.
         */
        if (cp < endp)
-               flush_buffer(cp, endp - cp);
-       return 0;
+               write_or_die(1, cp, endp - cp);
 }
 
 int cmd_cat_file(int argc, const char **argv, const char *prefix)
@@ -145,8 +127,10 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
                buf = read_sha1_file(sha1, type, &size);
                if (!buf)
                        die("Cannot read object %s", argv[2]);
-               if (!strcmp(type, tag_type))
-                       return pprint_tag(sha1, buf, size);
+               if (!strcmp(type, tag_type)) {
+                       pprint_tag(sha1, buf, size);
+                       return 0;
+               }
 
                /* otherwise just spit out the data */
                break;
@@ -161,6 +145,6 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
        if (!buf)
                die("git-cat-file %s: bad file", argv[2]);
 
-       flush_buffer(buf, size);
+       write_or_die(1, buf, size);
        return 0;
 }
similarity index 92%
rename from checkout-index.c
rename to builtin-checkout-index.c
index dfb1c44..b097c88 100644 (file)
 #include "cache-tree.h"
 
 #define CHECKOUT_ALL 4
-static const char *prefix;
-static int prefix_length;
 static int line_termination = '\n';
 static int checkout_stage; /* default to checkout stage0 */
 static int to_tempfile;
-static char topath[4][MAXPATHLEN+1];
+static char topath[4][PATH_MAX + 1];
 
 static struct checkout state;
 
-static void write_tempfile_record (const char *name)
+static void write_tempfile_record(const char *name, int prefix_length)
 {
        int i;
 
@@ -77,7 +75,7 @@ static void write_tempfile_record (const char *name)
        }
 }
 
-static int checkout_file(const char *name)
+static int checkout_file(const char *name, int prefix_length)
 {
        int namelen = strlen(name);
        int pos = cache_name_pos(name, namelen);
@@ -106,7 +104,7 @@ static int checkout_file(const char *name)
 
        if (did_checkout) {
                if (to_tempfile)
-                       write_tempfile_record(name);
+                       write_tempfile_record(name, prefix_length);
                return errs > 0 ? -1 : 0;
        }
 
@@ -124,7 +122,7 @@ static int checkout_file(const char *name)
        return -1;
 }
 
-static int checkout_all(void)
+static void checkout_all(const char *prefix, int prefix_length)
 {
        int i, errs = 0;
        struct cache_entry* last_ce = NULL;
@@ -141,7 +139,7 @@ static int checkout_all(void)
                if (last_ce && to_tempfile) {
                        if (ce_namelen(last_ce) != ce_namelen(ce)
                            || memcmp(last_ce->name, ce->name, ce_namelen(ce)))
-                               write_tempfile_record(last_ce->name);
+                               write_tempfile_record(last_ce->name, prefix_length);
                }
                if (checkout_entry(ce, &state,
                    to_tempfile ? topath[ce_stage(ce)] : NULL) < 0)
@@ -149,13 +147,12 @@ static int checkout_all(void)
                last_ce = ce;
        }
        if (last_ce && to_tempfile)
-               write_tempfile_record(last_ce->name);
+               write_tempfile_record(last_ce->name, prefix_length);
        if (errs)
                /* we have already done our error reporting.
                 * exit with the same code as die().
                 */
                exit(128);
-       return 0;
 }
 
 static const char checkout_cache_usage[] =
@@ -163,16 +160,16 @@ static const char checkout_cache_usage[] =
 
 static struct lock_file lock_file;
 
-int main(int argc, char **argv)
+int cmd_checkout_index(int argc, const char **argv, const char *prefix)
 {
        int i;
        int newfd = -1;
        int all = 0;
        int read_from_stdin = 0;
+       int prefix_length;
 
-       state.base_dir = "";
-       prefix = setup_git_directory();
        git_config(git_default_config);
+       state.base_dir = "";
        prefix_length = prefix ? strlen(prefix) : 0;
 
        if (read_cache() < 0) {
@@ -270,7 +267,7 @@ int main(int argc, char **argv)
                if (read_from_stdin)
                        die("git-checkout-index: don't mix '--stdin' and explicit filenames");
                p = prefix_path(prefix, prefix_length, arg);
-               checkout_file(p);
+               checkout_file(p, prefix_length);
                if (p < arg || p > arg + strlen(arg))
                        free((char*)p);
        }
@@ -292,7 +289,7 @@ int main(int argc, char **argv)
                        else
                                path_name = buf.buf;
                        p = prefix_path(prefix, prefix_length, path_name);
-                       checkout_file(p);
+                       checkout_file(p, prefix_length);
                        if (p < path_name || p > path_name + strlen(path_name))
                                free((char *)p);
                        if (path_name != buf.buf)
@@ -301,7 +298,7 @@ int main(int argc, char **argv)
        }
 
        if (all)
-               checkout_all();
+               checkout_all(prefix, prefix_length);
 
        if (0 <= newfd &&
            (write_cache(newfd, active_cache, active_nr) ||
index 9c98796..e2e690a 100644 (file)
@@ -69,7 +69,7 @@ static int new_parent(int idx)
        int i;
        unsigned char *sha1 = parent_sha1[idx];
        for (i = 0; i < idx; i++) {
-               if (!memcmp(parent_sha1[i], sha1, 20)) {
+               if (!hashcmp(parent_sha1[i], sha1)) {
                        error("duplicate parent %s ignored", sha1_to_hex(sha1));
                        return 0;
                }
similarity index 98%
rename from builtin-count.c
rename to builtin-count-objects.c
index 1d3729a..73c5982 100644 (file)
@@ -62,7 +62,7 @@ static void count_objects(DIR *d, char *path, int len, int verbose,
                hex[40] = 0;
                if (get_sha1_hex(hex, sha1))
                        die("internal error");
-               if (has_sha1_pack(sha1))
+               if (has_sha1_pack(sha1, NULL))
                        (*packed_loose)++;
        }
 }
index ac13db7..5d4a5c5 100644 (file)
@@ -47,12 +47,5 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
        if (rev.pending.nr ||
            rev.min_age != -1 || rev.max_age != -1)
                usage(diff_files_usage);
-       /*
-        * Backward compatibility wart - "diff-files -s" used to
-        * defeat the common diff option "-s" which asked for
-        * DIFF_FORMAT_NO_OUTPUT.
-        */
-       if (rev.diffopt.output_format == DIFF_FORMAT_NO_OUTPUT)
-               rev.diffopt.output_format = DIFF_FORMAT_RAW;
        return run_diff_files(&rev, silent);
 }
index 5960e08..70bb898 100644 (file)
@@ -46,7 +46,7 @@ static void diff_stages(int stage1, int stage2, const char **pathspec)
                else if (!two)
                        diff_addremove(&diff_options, '-', ntohl(one->ce_mode),
                                       one->sha1, name, NULL);
-               else if (memcmp(one->sha1, two->sha1, 20) ||
+               else if (hashcmp(one->sha1, two->sha1) ||
                         (one->ce_mode != two->ce_mode) ||
                         diff_options.find_copies_harder)
                        diff_change(&diff_options,
index a090e29..a659020 100644 (file)
@@ -56,13 +56,6 @@ static int builtin_diff_files(struct rev_info *revs,
        if (revs->max_count < 0 &&
            (revs->diffopt.output_format & DIFF_FORMAT_PATCH))
                revs->combine_merges = revs->dense_combined_merges = 1;
-       /*
-        * Backward compatibility wart - "diff-files -s" used to
-        * defeat the common diff option "-s" which asked for
-        * DIFF_FORMAT_NO_OUTPUT.
-        */
-       if (revs->diffopt.output_format == DIFF_FORMAT_NO_OUTPUT)
-               revs->diffopt.output_format = DIFF_FORMAT_RAW;
        return run_diff_files(revs, silent);
 }
 
@@ -75,9 +68,8 @@ static void stuff_change(struct diff_options *opt,
 {
        struct diff_filespec *one, *two;
 
-       if (memcmp(null_sha1, old_sha1, 20) &&
-           memcmp(null_sha1, new_sha1, 20) &&
-           !memcmp(old_sha1, new_sha1, 20))
+       if (!is_null_sha1(old_sha1) && !is_null_sha1(new_sha1) &&
+           !hashcmp(old_sha1, new_sha1))
                return;
 
        if (opt->reverse_diff) {
@@ -200,7 +192,7 @@ static int builtin_diff_combined(struct rev_info *revs,
        parent = xmalloc(ents * sizeof(*parent));
        /* Again, the revs are all reverse */
        for (i = 0; i < ents; i++)
-               memcpy(parent + i, ent[ents - 1 - i].item->sha1, 20);
+               hashcpy((unsigned char*)parent + i, ent[ents - 1 - i].item->sha1);
        diff_tree_combined(parent[0], parent + 1, ents - 1,
                           revs->dense_combined_merges, revs);
        return 0;
@@ -298,7 +290,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
                if (obj->type == OBJ_BLOB) {
                        if (2 <= blobs)
                                die("more than two blobs given: '%s'", name);
-                       memcpy(blob[blobs].sha1, obj->sha1, 20);
+                       hashcpy(blob[blobs].sha1, obj->sha1);
                        blob[blobs].name = name;
                        blobs++;
                        continue;
index 485ede7..c407c03 100644 (file)
@@ -8,7 +8,7 @@
 static const char *fmt_merge_msg_usage =
        "git-fmt-merge-msg [--summary] [--no-summary] [--file <file>]";
 
-static int merge_summary = 0;
+static int merge_summary;
 
 static int fmt_merge_msg_config(const char *key, const char *value)
 {
@@ -27,8 +27,8 @@ static void append_to_list(struct list *list, char *value, void *payload)
 {
        if (list->nr == list->alloc) {
                list->alloc += 32;
-               list->list = realloc(list->list, sizeof(char *) * list->alloc);
-               list->payload = realloc(list->payload,
+               list->list = xrealloc(list->list, sizeof(char *) * list->alloc);
+               list->payload = xrealloc(list->payload,
                                sizeof(char *) * list->alloc);
        }
        list->payload[list->nr] = payload;
@@ -55,8 +55,7 @@ static void free_list(struct list *list)
 
        for (i = 0; i < list->nr; i++) {
                free(list->list[i]);
-               if (list->payload[i])
-                       free(list->payload[i]);
+               free(list->payload[i]);
        }
        free(list->list);
        free(list->payload);
@@ -112,43 +111,43 @@ static int handle_line(char *line)
        i = find_in_list(&srcs, src);
        if (i < 0) {
                i = srcs.nr;
-               append_to_list(&srcs, strdup(src),
+               append_to_list(&srcs, xstrdup(src),
                                xcalloc(1, sizeof(struct src_data)));
        }
        src_data = srcs.payload[i];
 
        if (pulling_head) {
-               origin = strdup(src);
+               origin = xstrdup(src);
                src_data->head_status |= 1;
        } else if (!strncmp(line, "branch ", 7)) {
-               origin = strdup(line + 7);
+               origin = xstrdup(line + 7);
                append_to_list(&src_data->branch, origin, NULL);
                src_data->head_status |= 2;
        } else if (!strncmp(line, "tag ", 4)) {
                origin = line;
-               append_to_list(&src_data->tag, strdup(origin + 4), NULL);
+               append_to_list(&src_data->tag, xstrdup(origin + 4), NULL);
                src_data->head_status |= 2;
        } else if (!strncmp(line, "remote branch ", 14)) {
-               origin = strdup(line + 14);
+               origin = xstrdup(line + 14);
                append_to_list(&src_data->r_branch, origin, NULL);
                src_data->head_status |= 2;
        } else {
-               origin = strdup(src);
-               append_to_list(&src_data->generic, strdup(line), NULL);
+               origin = xstrdup(src);
+               append_to_list(&src_data->generic, xstrdup(line), NULL);
                src_data->head_status |= 2;
        }
 
        if (!strcmp(".", src) || !strcmp(src, origin)) {
                int len = strlen(origin);
                if (origin[0] == '\'' && origin[len - 1] == '\'') {
-                       char *new_origin = malloc(len - 1);
+                       char *new_origin = xmalloc(len - 1);
                        memcpy(new_origin, origin + 1, len - 2);
-                       new_origin[len - 1] = 0;
+                       new_origin[len - 2] = 0;
                        origin = new_origin;
                } else
-                       origin = strdup(origin);
+                       origin = xstrdup(origin);
        } else {
-               char *new_origin = malloc(strlen(origin) + strlen(src) + 5);
+               char *new_origin = xmalloc(strlen(origin) + strlen(src) + 5);
                sprintf(new_origin, "%s of %s", origin, src);
                origin = new_origin;
        }
@@ -204,7 +203,7 @@ static void shortlog(const char *name, unsigned char *sha1,
 
                bol = strstr(commit->buffer, "\n\n");
                if (!bol) {
-                       append_to_list(&subjects, strdup(sha1_to_hex(
+                       append_to_list(&subjects, xstrdup(sha1_to_hex(
                                                        commit->object.sha1)),
                                        NULL);
                        continue;
@@ -215,11 +214,11 @@ static void shortlog(const char *name, unsigned char *sha1,
 
                if (eol) {
                        int len = eol - bol;
-                       oneline = malloc(len + 1);
+                       oneline = xmalloc(len + 1);
                        memcpy(oneline, bol, len);
                        oneline[len] = 0;
                } else
-                       oneline = strdup(bol);
+                       oneline = xstrdup(bol);
                append_to_list(&subjects, oneline, NULL);
        }
 
@@ -278,7 +277,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
                usage(fmt_merge_msg_usage);
 
        /* get current branch */
-       head = strdup(git_path("HEAD"));
+       head = xstrdup(git_path("HEAD"));
        current_branch = resolve_ref(head, head_sha1, 1);
        current_branch += strlen(head) - 4;
        free((char *)head);
index 93b7e07..4205e5d 100644 (file)
@@ -11,6 +11,7 @@
 #include "tree-walk.h"
 #include "builtin.h"
 #include <regex.h>
+#include "grep.h"
 #include <fnmatch.h>
 #include <sys/wait.h>
 
@@ -82,569 +83,40 @@ static int pathspec_matches(const char **paths, const char *name)
        return 0;
 }
 
-enum grep_pat_token {
-       GREP_PATTERN,
-       GREP_AND,
-       GREP_OPEN_PAREN,
-       GREP_CLOSE_PAREN,
-       GREP_NOT,
-       GREP_OR,
-};
-
-struct grep_pat {
-       struct grep_pat *next;
-       const char *origin;
-       int no;
-       enum grep_pat_token token;
-       const char *pattern;
-       regex_t regexp;
-};
-
-enum grep_expr_node {
-       GREP_NODE_ATOM,
-       GREP_NODE_NOT,
-       GREP_NODE_AND,
-       GREP_NODE_OR,
-};
-
-struct grep_expr {
-       enum grep_expr_node node;
-       union {
-               struct grep_pat *atom;
-               struct grep_expr *unary;
-               struct {
-                       struct grep_expr *left;
-                       struct grep_expr *right;
-               } binary;
-       } u;
-};
-
-struct grep_opt {
-       struct grep_pat *pattern_list;
-       struct grep_pat **pattern_tail;
-       struct grep_expr *pattern_expression;
-       regex_t regexp;
-       unsigned linenum:1;
-       unsigned invert:1;
-       unsigned name_only:1;
-       unsigned unmatch_name_only:1;
-       unsigned count:1;
-       unsigned word_regexp:1;
-       unsigned fixed:1;
-#define GREP_BINARY_DEFAULT    0
-#define GREP_BINARY_NOMATCH    1
-#define GREP_BINARY_TEXT       2
-       unsigned binary:2;
-       unsigned extended:1;
-       int regflags;
-       unsigned pre_context;
-       unsigned post_context;
-};
-
-static void add_pattern(struct grep_opt *opt, const char *pat,
-                       const char *origin, int no, enum grep_pat_token t)
-{
-       struct grep_pat *p = xcalloc(1, sizeof(*p));
-       p->pattern = pat;
-       p->origin = origin;
-       p->no = no;
-       p->token = t;
-       *opt->pattern_tail = p;
-       opt->pattern_tail = &p->next;
-       p->next = NULL;
-}
-
-static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
-{
-       int err = regcomp(&p->regexp, p->pattern, opt->regflags);
-       if (err) {
-               char errbuf[1024];
-               char where[1024];
-               if (p->no)
-                       sprintf(where, "In '%s' at %d, ",
-                               p->origin, p->no);
-               else if (p->origin)
-                       sprintf(where, "%s, ", p->origin);
-               else
-                       where[0] = 0;
-               regerror(err, &p->regexp, errbuf, 1024);
-               regfree(&p->regexp);
-               die("%s'%s': %s", where, p->pattern, errbuf);
-       }
-}
-
-#if DEBUG
-static inline void indent(int in)
-{
-       int i;
-       for (i = 0; i < in; i++) putchar(' ');
-}
-
-static void dump_pattern_exp(struct grep_expr *x, int in)
-{
-       switch (x->node) {
-       case GREP_NODE_ATOM:
-               indent(in);
-               puts(x->u.atom->pattern);
-               break;
-       case GREP_NODE_NOT:
-               indent(in);
-               puts("--not");
-               dump_pattern_exp(x->u.unary, in+1);
-               break;
-       case GREP_NODE_AND:
-               dump_pattern_exp(x->u.binary.left, in+1);
-               indent(in);
-               puts("--and");
-               dump_pattern_exp(x->u.binary.right, in+1);
-               break;
-       case GREP_NODE_OR:
-               dump_pattern_exp(x->u.binary.left, in+1);
-               indent(in);
-               puts("--or");
-               dump_pattern_exp(x->u.binary.right, in+1);
-               break;
-       }
-}
-
-static void looking_at(const char *msg, struct grep_pat **list)
-{
-       struct grep_pat *p = *list;
-       fprintf(stderr, "%s: looking at ", msg);
-       if (!p)
-               fprintf(stderr, "empty\n");
-       else
-               fprintf(stderr, "<%s>\n", p->pattern);
-}
-#else
-#define looking_at(a,b) do {} while(0)
-#endif
-
-static struct grep_expr *compile_pattern_expr(struct grep_pat **);
-static struct grep_expr *compile_pattern_atom(struct grep_pat **list)
-{
-       struct grep_pat *p;
-       struct grep_expr *x;
-
-       looking_at("atom", list);
-
-       p = *list;
-       switch (p->token) {
-       case GREP_PATTERN: /* atom */
-               x = xcalloc(1, sizeof (struct grep_expr));
-               x->node = GREP_NODE_ATOM;
-               x->u.atom = p;
-               *list = p->next;
-               return x;
-       case GREP_OPEN_PAREN:
-               *list = p->next;
-               x = compile_pattern_expr(list);
-               if (!x)
-                       return NULL;
-               if (!*list || (*list)->token != GREP_CLOSE_PAREN)
-                       die("unmatched parenthesis");
-               *list = (*list)->next;
-               return x;
-       default:
-               return NULL;
-       }
-}
-
-static struct grep_expr *compile_pattern_not(struct grep_pat **list)
-{
-       struct grep_pat *p;
-       struct grep_expr *x;
-
-       looking_at("not", list);
-
-       p = *list;
-       switch (p->token) {
-       case GREP_NOT:
-               if (!p->next)
-                       die("--not not followed by pattern expression");
-               *list = p->next;
-               x = xcalloc(1, sizeof (struct grep_expr));
-               x->node = GREP_NODE_NOT;
-               x->u.unary = compile_pattern_not(list);
-               if (!x->u.unary)
-                       die("--not followed by non pattern expression");
-               return x;
-       default:
-               return compile_pattern_atom(list);
-       }
-}
-
-static struct grep_expr *compile_pattern_and(struct grep_pat **list)
-{
-       struct grep_pat *p;
-       struct grep_expr *x, *y, *z;
-
-       looking_at("and", list);
-
-       x = compile_pattern_not(list);
-       p = *list;
-       if (p && p->token == GREP_AND) {
-               if (!p->next)
-                       die("--and not followed by pattern expression");
-               *list = p->next;
-               y = compile_pattern_and(list);
-               if (!y)
-                       die("--and not followed by pattern expression");
-               z = xcalloc(1, sizeof (struct grep_expr));
-               z->node = GREP_NODE_AND;
-               z->u.binary.left = x;
-               z->u.binary.right = y;
-               return z;
-       }
-       return x;
-}
-
-static struct grep_expr *compile_pattern_or(struct grep_pat **list)
-{
-       struct grep_pat *p;
-       struct grep_expr *x, *y, *z;
-
-       looking_at("or", list);
-
-       x = compile_pattern_and(list);
-       p = *list;
-       if (x && p && p->token != GREP_CLOSE_PAREN) {
-               y = compile_pattern_or(list);
-               if (!y)
-                       die("not a pattern expression %s", p->pattern);
-               z = xcalloc(1, sizeof (struct grep_expr));
-               z->node = GREP_NODE_OR;
-               z->u.binary.left = x;
-               z->u.binary.right = y;
-               return z;
-       }
-       return x;
-}
-
-static struct grep_expr *compile_pattern_expr(struct grep_pat **list)
-{
-       looking_at("expr", list);
-
-       return compile_pattern_or(list);
-}
-
-static void compile_patterns(struct grep_opt *opt)
-{
-       struct grep_pat *p;
-
-       /* First compile regexps */
-       for (p = opt->pattern_list; p; p = p->next) {
-               if (p->token == GREP_PATTERN)
-                       compile_regexp(p, opt);
-               else
-                       opt->extended = 1;
-       }
-
-       if (!opt->extended)
-               return;
-
-       /* Then bundle them up in an expression.
-        * A classic recursive descent parser would do.
-        */
-       p = opt->pattern_list;
-       opt->pattern_expression = compile_pattern_expr(&p);
-#if DEBUG
-       dump_pattern_exp(opt->pattern_expression, 0);
-#endif
-       if (p)
-               die("incomplete pattern expression: %s", p->pattern);
-}
-
-static char *end_of_line(char *cp, unsigned long *left)
-{
-       unsigned long l = *left;
-       while (l && *cp != '\n') {
-               l--;
-               cp++;
-       }
-       *left = l;
-       return cp;
-}
-
-static int word_char(char ch)
-{
-       return isalnum(ch) || ch == '_';
-}
-
-static void show_line(struct grep_opt *opt, const char *bol, const char *eol,
-                     const char *name, unsigned lno, char sign)
-{
-       printf("%s%c", name, sign);
-       if (opt->linenum)
-               printf("%d%c", lno, sign);
-       printf("%.*s\n", (int)(eol-bol), bol);
-}
-
-/*
- * NEEDSWORK: share code with diff.c
- */
-#define FIRST_FEW_BYTES 8000
-static int buffer_is_binary(const char *ptr, unsigned long size)
-{
-       if (FIRST_FEW_BYTES < size)
-               size = FIRST_FEW_BYTES;
-       if (memchr(ptr, 0, size))
-               return 1;
-       return 0;
-}
-
-static int fixmatch(const char *pattern, char *line, regmatch_t *match)
-{
-       char *hit = strstr(line, pattern);
-       if (!hit) {
-               match->rm_so = match->rm_eo = -1;
-               return REG_NOMATCH;
-       }
-       else {
-               match->rm_so = hit - line;
-               match->rm_eo = match->rm_so + strlen(pattern);
-               return 0;
-       }
-}
-
-static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol, char *eol)
-{
-       int hit = 0;
-       int at_true_bol = 1;
-       regmatch_t pmatch[10];
-
- again:
-       if (!opt->fixed) {
-               regex_t *exp = &p->regexp;
-               hit = !regexec(exp, bol, ARRAY_SIZE(pmatch),
-                              pmatch, 0);
-       }
-       else {
-               hit = !fixmatch(p->pattern, bol, pmatch);
-       }
-
-       if (hit && opt->word_regexp) {
-               if ((pmatch[0].rm_so < 0) ||
-                   (eol - bol) <= pmatch[0].rm_so ||
-                   (pmatch[0].rm_eo < 0) ||
-                   (eol - bol) < pmatch[0].rm_eo)
-                       die("regexp returned nonsense");
-
-               /* Match beginning must be either beginning of the
-                * line, or at word boundary (i.e. the last char must
-                * not be a word char).  Similarly, match end must be
-                * either end of the line, or at word boundary
-                * (i.e. the next char must not be a word char).
-                */
-               if ( ((pmatch[0].rm_so == 0 && at_true_bol) ||
-                     !word_char(bol[pmatch[0].rm_so-1])) &&
-                    ((pmatch[0].rm_eo == (eol-bol)) ||
-                     !word_char(bol[pmatch[0].rm_eo])) )
-                       ;
-               else
-                       hit = 0;
-
-               if (!hit && pmatch[0].rm_so + bol + 1 < eol) {
-                       /* There could be more than one match on the
-                        * line, and the first match might not be
-                        * strict word match.  But later ones could be!
-                        */
-                       bol = pmatch[0].rm_so + bol + 1;
-                       at_true_bol = 0;
-                       goto again;
-               }
-       }
-       return hit;
-}
-
-static int match_expr_eval(struct grep_opt *opt,
-                          struct grep_expr *x,
-                          char *bol, char *eol)
-{
-       switch (x->node) {
-       case GREP_NODE_ATOM:
-               return match_one_pattern(opt, x->u.atom, bol, eol);
-               break;
-       case GREP_NODE_NOT:
-               return !match_expr_eval(opt, x->u.unary, bol, eol);
-       case GREP_NODE_AND:
-               return (match_expr_eval(opt, x->u.binary.left, bol, eol) &&
-                       match_expr_eval(opt, x->u.binary.right, bol, eol));
-       case GREP_NODE_OR:
-               return (match_expr_eval(opt, x->u.binary.left, bol, eol) ||
-                       match_expr_eval(opt, x->u.binary.right, bol, eol));
-       }
-       die("Unexpected node type (internal error) %d\n", x->node);
-}
-
-static int match_expr(struct grep_opt *opt, char *bol, char *eol)
-{
-       struct grep_expr *x = opt->pattern_expression;
-       return match_expr_eval(opt, x, bol, eol);
-}
-
-static int match_line(struct grep_opt *opt, char *bol, char *eol)
-{
-       struct grep_pat *p;
-       if (opt->extended)
-               return match_expr(opt, bol, eol);
-       for (p = opt->pattern_list; p; p = p->next) {
-               if (match_one_pattern(opt, p, bol, eol))
-                       return 1;
-       }
-       return 0;
-}
-
-static int grep_buffer(struct grep_opt *opt, const char *name,
-                      char *buf, unsigned long size)
-{
-       char *bol = buf;
-       unsigned long left = size;
-       unsigned lno = 1;
-       struct pre_context_line {
-               char *bol;
-               char *eol;
-       } *prev = NULL, *pcl;
-       unsigned last_hit = 0;
-       unsigned last_shown = 0;
-       int binary_match_only = 0;
-       const char *hunk_mark = "";
-       unsigned count = 0;
-
-       if (buffer_is_binary(buf, size)) {
-               switch (opt->binary) {
-               case GREP_BINARY_DEFAULT:
-                       binary_match_only = 1;
-                       break;
-               case GREP_BINARY_NOMATCH:
-                       return 0; /* Assume unmatch */
-                       break;
-               default:
-                       break;
-               }
-       }
-
-       if (opt->pre_context)
-               prev = xcalloc(opt->pre_context, sizeof(*prev));
-       if (opt->pre_context || opt->post_context)
-               hunk_mark = "--\n";
-
-       while (left) {
-               char *eol, ch;
-               int hit = 0;
-
-               eol = end_of_line(bol, &left);
-               ch = *eol;
-               *eol = 0;
-
-               hit = match_line(opt, bol, eol);
-
-               /* "grep -v -e foo -e bla" should list lines
-                * that do not have either, so inversion should
-                * be done outside.
-                */
-               if (opt->invert)
-                       hit = !hit;
-               if (opt->unmatch_name_only) {
-                       if (hit)
-                               return 0;
-                       goto next_line;
-               }
-               if (hit) {
-                       count++;
-                       if (binary_match_only) {
-                               printf("Binary file %s matches\n", name);
-                               return 1;
-                       }
-                       if (opt->name_only) {
-                               printf("%s\n", name);
-                               return 1;
-                       }
-                       /* Hit at this line.  If we haven't shown the
-                        * pre-context lines, we would need to show them.
-                        * When asked to do "count", this still show
-                        * the context which is nonsense, but the user
-                        * deserves to get that ;-).
-                        */
-                       if (opt->pre_context) {
-                               unsigned from;
-                               if (opt->pre_context < lno)
-                                       from = lno - opt->pre_context;
-                               else
-                                       from = 1;
-                               if (from <= last_shown)
-                                       from = last_shown + 1;
-                               if (last_shown && from != last_shown + 1)
-                                       printf(hunk_mark);
-                               while (from < lno) {
-                                       pcl = &prev[lno-from-1];
-                                       show_line(opt, pcl->bol, pcl->eol,
-                                                 name, from, '-');
-                                       from++;
-                               }
-                               last_shown = lno-1;
-                       }
-                       if (last_shown && lno != last_shown + 1)
-                               printf(hunk_mark);
-                       if (!opt->count)
-                               show_line(opt, bol, eol, name, lno, ':');
-                       last_shown = last_hit = lno;
-               }
-               else if (last_hit &&
-                        lno <= last_hit + opt->post_context) {
-                       /* If the last hit is within the post context,
-                        * we need to show this line.
-                        */
-                       if (last_shown && lno != last_shown + 1)
-                               printf(hunk_mark);
-                       show_line(opt, bol, eol, name, lno, '-');
-                       last_shown = lno;
-               }
-               if (opt->pre_context) {
-                       memmove(prev+1, prev,
-                               (opt->pre_context-1) * sizeof(*prev));
-                       prev->bol = bol;
-                       prev->eol = eol;
-               }
-
-       next_line:
-               *eol = ch;
-               bol = eol + 1;
-               if (!left)
-                       break;
-               left--;
-               lno++;
-       }
-
-       if (opt->unmatch_name_only) {
-               /* We did not see any hit, so we want to show this */
-               printf("%s\n", name);
-               return 1;
-       }
-
-       /* NEEDSWORK:
-        * The real "grep -c foo *.c" gives many "bar.c:0" lines,
-        * which feels mostly useless but sometimes useful.  Maybe
-        * make it another option?  For now suppress them.
-        */
-       if (opt->count && count)
-               printf("%s:%u\n", name, count);
-       return !!last_hit;
-}
-
-static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char *name)
+static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char *name, int tree_name_len)
 {
        unsigned long size;
        char *data;
        char type[20];
+       char *to_free = NULL;
        int hit;
+
        data = read_sha1_file(sha1, type, &size);
        if (!data) {
                error("'%s': unable to read %s", name, sha1_to_hex(sha1));
                return 0;
        }
+       if (opt->relative && opt->prefix_length) {
+               static char name_buf[PATH_MAX];
+               char *cp;
+               int name_len = strlen(name) - opt->prefix_length + 1;
+
+               if (!tree_name_len)
+                       name += opt->prefix_length;
+               else {
+                       if (ARRAY_SIZE(name_buf) <= name_len)
+                               cp = to_free = xmalloc(name_len);
+                       else
+                               cp = name_buf;
+                       memcpy(cp, name, tree_name_len);
+                       strcpy(cp + tree_name_len,
+                              name + tree_name_len + opt->prefix_length);
+                       name = cp;
+               }
+       }
        hit = grep_buffer(opt, name, data, size);
        free(data);
+       free(to_free);
        return hit;
 }
 
@@ -674,6 +146,8 @@ static int grep_file(struct grep_opt *opt, const char *filename)
                return 0;
        }
        close(i);
+       if (opt->relative && opt->prefix_length)
+               filename += opt->prefix_length;
        i = grep_buffer(opt, filename, data, st.st_size);
        free(data);
        return i;
@@ -720,7 +194,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
        char *argptr = randarg;
        struct grep_pat *p;
 
-       if (opt->extended)
+       if (opt->extended || (opt->relative && opt->prefix_length))
                return -1;
        len = nr = 0;
        push_arg("grep");
@@ -728,6 +202,8 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
                push_arg("-F");
        if (opt->linenum)
                push_arg("-n");
+       if (!opt->pathname)
+               push_arg("-h");
        if (opt->regflags & REG_EXTENDED)
                push_arg("-E");
        if (opt->regflags & REG_ICASE)
@@ -845,10 +321,11 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
                if (!pathspec_matches(paths, ce->name))
                        continue;
                if (cached)
-                       hit |= grep_sha1(opt, ce->sha1, ce->name);
+                       hit |= grep_sha1(opt, ce->sha1, ce->name, 0);
                else
                        hit |= grep_file(opt, ce->name);
        }
+       free_grep_patterns(opt);
        return hit;
 }
 
@@ -860,11 +337,12 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
        int hit = 0;
        struct name_entry entry;
        char *down;
-       char *path_buf = xmalloc(PATH_MAX + strlen(tree_name) + 100);
+       int tn_len = strlen(tree_name);
+       char *path_buf = xmalloc(PATH_MAX + tn_len + 100);
 
-       if (tree_name[0]) {
-               int offset = sprintf(path_buf, "%s:", tree_name);
-               down = path_buf + offset;
+       if (tn_len) {
+               tn_len = sprintf(path_buf, "%s:", tree_name);
+               down = path_buf + tn_len;
                strcat(down, base);
        }
        else {
@@ -886,7 +364,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
                if (!pathspec_matches(paths, down))
                        ;
                else if (S_ISREG(entry.mode))
-                       hit |= grep_sha1(opt, entry.sha1, path_buf);
+                       hit |= grep_sha1(opt, entry.sha1, path_buf, tn_len);
                else if (S_ISDIR(entry.mode)) {
                        char type[20];
                        struct tree_desc sub;
@@ -907,7 +385,7 @@ static int grep_object(struct grep_opt *opt, const char **paths,
                       struct object *obj, const char *name)
 {
        if (obj->type == OBJ_BLOB)
-               return grep_sha1(opt, obj->sha1, name);
+               return grep_sha1(opt, obj->sha1, name, 0);
        if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) {
                struct tree_desc tree;
                void *data;
@@ -945,6 +423,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        int i;
 
        memset(&opt, 0, sizeof(opt));
+       opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0;
+       opt.relative = 1;
+       opt.pathname = 1;
        opt.pattern_tail = &opt.pattern_list;
        opt.regflags = REG_NEWLINE;
 
@@ -1004,10 +485,12 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        opt.linenum = 1;
                        continue;
                }
+               if (!strcmp("-h", arg)) {
+                       opt.pathname = 0;
+                       continue;
+               }
                if (!strcmp("-H", arg)) {
-                       /* We always show the pathname, so this
-                        * is a noop.
-                        */
+                       opt.pathname = 1;
                        continue;
                }
                if (!strcmp("-l", arg) ||
@@ -1082,8 +565,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                                /* ignore empty line like grep does */
                                if (!buf[0])
                                        continue;
-                               add_pattern(&opt, strdup(buf), argv[1], ++lno,
-                                           GREP_PATTERN);
+                               append_grep_pattern(&opt, xstrdup(buf),
+                                                   argv[1], ++lno,
+                                                   GREP_PATTERN);
                        }
                        fclose(patterns);
                        argv++;
@@ -1091,33 +575,42 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        continue;
                }
                if (!strcmp("--not", arg)) {
-                       add_pattern(&opt, arg, "command line", 0, GREP_NOT);
+                       append_grep_pattern(&opt, arg, "command line", 0,
+                                           GREP_NOT);
                        continue;
                }
                if (!strcmp("--and", arg)) {
-                       add_pattern(&opt, arg, "command line", 0, GREP_AND);
+                       append_grep_pattern(&opt, arg, "command line", 0,
+                                           GREP_AND);
                        continue;
                }
                if (!strcmp("--or", arg))
                        continue; /* no-op */
                if (!strcmp("(", arg)) {
-                       add_pattern(&opt, arg, "command line", 0, GREP_OPEN_PAREN);
+                       append_grep_pattern(&opt, arg, "command line", 0,
+                                           GREP_OPEN_PAREN);
                        continue;
                }
                if (!strcmp(")", arg)) {
-                       add_pattern(&opt, arg, "command line", 0, GREP_CLOSE_PAREN);
+                       append_grep_pattern(&opt, arg, "command line", 0,
+                                           GREP_CLOSE_PAREN);
                        continue;
                }
                if (!strcmp("-e", arg)) {
                        if (1 < argc) {
-                               add_pattern(&opt, argv[1], "-e option", 0,
-                                           GREP_PATTERN);
+                               append_grep_pattern(&opt, argv[1],
+                                                   "-e option", 0,
+                                                   GREP_PATTERN);
                                argv++;
                                argc--;
                                continue;
                        }
                        die(emsg_missing_argument, arg);
                }
+               if (!strcmp("--full-name", arg)) {
+                       opt.relative = 0;
+                       continue;
+               }
                if (!strcmp("--", arg)) {
                        /* later processing wants to have this at argv[1] */
                        argv--;
@@ -1129,8 +622,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
 
                /* First unrecognized non-option token */
                if (!opt.pattern_list) {
-                       add_pattern(&opt, arg, "command line", 0,
-                                   GREP_PATTERN);
+                       append_grep_pattern(&opt, arg, "command line", 0,
+                                           GREP_PATTERN);
                        break;
                }
                else {
@@ -1147,8 +640,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                die("no pattern given.");
        if ((opt.regflags != REG_NEWLINE) && opt.fixed)
                die("cannot mix --fixed-strings and regexp");
-       if (!opt.fixed)
-               compile_patterns(&opt);
+       compile_grep_patterns(&opt);
 
        /* Check revs and then paths */
        for (i = 1; i < argc; i++) {
@@ -1176,8 +668,15 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        verify_filename(prefix, argv[j]);
        }
 
-       if (i < argc)
+       if (i < argc) {
                paths = get_pathspec(prefix, argv + i);
+               if (opt.prefix_length && opt.relative) {
+                       /* Make sure we do not get outside of paths */
+                       for (i = 0; paths[i]; i++)
+                               if (strncmp(prefix, paths[i], opt.prefix_length))
+                                       die("git-grep: cannot generate relative filenames containing '..'");
+               }
+       }
        else if (prefix) {
                paths = xcalloc(2, sizeof(const char *));
                paths[0] = prefix;
@@ -1196,5 +695,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                if (grep_object(&opt, paths, real_obj, list.objects[i].name))
                        hit = 1;
        }
+       free_grep_patterns(&opt);
        return !hit;
 }
index 5085018..c3ed1ce 100644 (file)
@@ -311,6 +311,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
                 */
                sprintf(buf, "%d", shared_repository);
                git_config_set("core.sharedrepository", buf);
+               git_config_set("receive.denyNonFastforwards", "true");
        }
 
        return 0;
index 691cf3a..9d1ceae 100644 (file)
@@ -101,7 +101,7 @@ static int git_format_config(const char *var, const char *value)
        if (!strcmp(var, "format.headers")) {
                int len = strlen(value);
                extra_headers_size += len + 1;
-               extra_headers = realloc(extra_headers, extra_headers_size);
+               extra_headers = xrealloc(extra_headers, extra_headers_size);
                extra_headers[extra_headers_size - len - 1] = 0;
                strcat(extra_headers, value);
                return 0;
@@ -348,6 +348,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        if (!rev.diffopt.output_format)
                rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH;
 
+       if (!output_directory)
+               output_directory = prefix;
+
        if (output_directory) {
                if (use_stdout)
                        die("standard output, or directory, which one?");
@@ -381,7 +384,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        continue;
 
                nr++;
-               list = realloc(list, nr * sizeof(list[0]));
+               list = xrealloc(list, nr * sizeof(list[0]));
                list[nr - 1] = commit;
        }
        total = nr;
index 11386c4..ad8c41e 100644 (file)
 #include "dir.h"
 #include "builtin.h"
 
-static int abbrev = 0;
-static int show_deleted = 0;
-static int show_cached = 0;
-static int show_others = 0;
-static int show_stage = 0;
-static int show_unmerged = 0;
-static int show_modified = 0;
-static int show_killed = 0;
-static int show_valid_bit = 0;
+static int abbrev;
+static int show_deleted;
+static int show_cached;
+static int show_others;
+static int show_stage;
+static int show_unmerged;
+static int show_modified;
+static int show_killed;
+static int show_valid_bit;
 static int line_terminator = '\n';
 
-static int prefix_len = 0, prefix_offset = 0;
-static const char **pathspec = NULL;
-static int error_unmatch = 0;
-static char *ps_matched = NULL;
+static int prefix_len;
+static int prefix_offset;
+static const char **pathspec;
+static int error_unmatch;
+static char *ps_matched;
 
 static const char *tag_cached = "";
 static const char *tag_unmerged = "";
index 261147f..201defd 100644 (file)
@@ -14,10 +14,10 @@ static int line_termination = '\n';
 #define LS_TREE_ONLY 2
 #define LS_SHOW_TREES 4
 #define LS_NAME_ONLY 8
-static int abbrev = 0;
-static int ls_options = 0;
+static int abbrev;
+static int ls_options;
 static const char **pathspec;
-static int chomp_prefix = 0;
+static int chomp_prefix;
 static const char *ls_tree_prefix;
 
 static const char ls_tree_usage[] =
index 24a4fc6..b8d7dbc 100644 (file)
@@ -16,8 +16,8 @@
 
 static FILE *cmitmsg, *patchfile, *fin, *fout;
 
-static int keep_subject = 0;
-static const char *metainfo_charset = NULL;
+static int keep_subject;
+static const char *metainfo_charset;
 static char line[1000];
 static char date[1000];
 static char name[1000];
@@ -31,7 +31,7 @@ static char charset[256];
 
 static char multipart_boundary[1000];
 static int multipart_boundary_len;
-static int patch_lines = 0;
+static int patch_lines;
 
 static char *sanity_check(char *name, char *email)
 {
@@ -451,17 +451,6 @@ static int read_one_header_line(char *line, int sz, FILE *in)
        return ofs;
 }
 
-static unsigned hexval(int c)
-{
-       if (c >= '0' && c <= '9')
-               return c - '0';
-       if (c >= 'a' && c <= 'f')
-               return c - 'a' + 10;
-       if (c >= 'A' && c <= 'F')
-               return c - 'A' + 10;
-       return ~0;
-}
-
 static int decode_q_segment(char *in, char *ot, char *ep, int rfc2047)
 {
        int c;
index 6b0ab8a..54dd3bf 100644 (file)
@@ -168,13 +168,13 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
                                int j, dst_len;
 
                                if (last - first > 0) {
-                                       source = realloc(source,
+                                       source = xrealloc(source,
                                                        (count + last - first)
                                                        * sizeof(char *));
-                                       destination = realloc(destination,
+                                       destination = xrealloc(destination,
                                                        (count + last - first)
                                                        * sizeof(char *));
-                                       modes = realloc(modes,
+                                       modes = xrealloc(modes,
                                                        (count + last - first)
                                                        * sizeof(enum update_mode));
                                }
@@ -262,10 +262,10 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
        } else {
                for (i = 0; i < changed.nr; i++) {
                        const char *path = changed.items[i].path;
-                       int i = cache_name_pos(path, strlen(path));
-                       struct cache_entry *ce = active_cache[i];
+                       int j = cache_name_pos(path, strlen(path));
+                       struct cache_entry *ce = active_cache[j];
 
-                       if (i < 0)
+                       if (j < 0)
                                die ("Huh? Cache entry for %s unknown?", path);
                        refresh_cache_entry(ce, 0);
                }
similarity index 97%
rename from name-rev.c
rename to builtin-name-rev.c
index f92f14e..52886b6 100644 (file)
@@ -1,4 +1,5 @@
 #include <stdlib.h>
+#include "builtin.h"
 #include "cache.h"
 #include "commit.h"
 #include "tag.h"
@@ -74,7 +75,7 @@ copy_data:
        }
 }
 
-static int tags_only = 0;
+static int tags_only;
 
 static int name_ref(const char *path, const unsigned char *sha1)
 {
@@ -99,7 +100,7 @@ static int name_ref(const char *path, const unsigned char *sha1)
                else if (!strncmp(path, "refs/", 5))
                        path = path + 5;
 
-               name_rev(commit, strdup(path), 0, 0, deref);
+               name_rev(commit, xstrdup(path), 0, 0, deref);
        }
        return 0;
 }
@@ -126,12 +127,11 @@ static const char* get_rev_name(struct object *o)
        return buffer;
 }
 
-int main(int argc, char **argv)
+int cmd_name_rev(int argc, const char **argv, const char *prefix)
 {
        struct object_array revs = { 0, 0, NULL };
        int as_is = 0, all = 0, transform_stdin = 0;
 
-       setup_git_directory();
        git_config(git_default_config);
 
        if (argc < 2)
similarity index 81%
rename from pack-objects.c
rename to builtin-pack-objects.c
index 861c7f0..96c069a 100644 (file)
@@ -1,3 +1,4 @@
+#include "builtin.h"
 #include "cache.h"
 #include "object.h"
 #include "blob.h"
@@ -8,10 +9,13 @@
 #include "pack.h"
 #include "csum-file.h"
 #include "tree-walk.h"
+#include "diff.h"
+#include "revision.h"
+#include "list-objects.h"
 #include <sys/time.h>
 #include <signal.h>
 
-static const char pack_usage[] = "git-pack-objects [-q] [--no-reuse-delta] [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] {--stdout | base-name} < object-list";
+static const char pack_usage[] = "git-pack-objects [-q] [--no-reuse-delta] [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] [--revs [--unpacked | --all]*] [--stdout | base-name] <ref-list | <object-list]";
 
 struct object_entry {
        unsigned char sha1[20];
@@ -52,18 +56,20 @@ struct object_entry {
  */
 
 static unsigned char object_list_sha1[20];
-static int non_empty = 0;
-static int no_reuse_delta = 0;
-static int local = 0;
-static int incremental = 0;
+static int non_empty;
+static int no_reuse_delta;
+static int local;
+static int incremental;
 static struct object_entry **sorted_by_sha, **sorted_by_type;
-static struct object_entry *objects = NULL;
-static int nr_objects = 0, nr_alloc = 0, nr_result = 0;
+static struct object_entry *objects;
+static int nr_objects, nr_alloc, nr_result;
 static const char *base_name;
 static unsigned char pack_file_sha1[20];
 static int progress = 1;
-static volatile sig_atomic_t progress_update = 0;
+static volatile sig_atomic_t progress_update;
 static int window = 10;
+static int pack_to_stdout;
+static int num_preferred_base;
 
 /*
  * The object names in objects array are hashed with this hashtable,
@@ -71,8 +77,8 @@ static int window = 10;
  * sorted_by_sha is also possible but this was easier to code and faster.
  * This hashtable is built after all the objects are seen.
  */
-static int *object_ix = NULL;
-static int object_ix_hashsz = 0;
+static int *object_ix;
+static int object_ix_hashsz;
 
 /*
  * Pack index for existing packs give us easy access to the offsets into
@@ -89,15 +95,15 @@ struct pack_revindex {
        struct packed_git *p;
        unsigned long *revindex;
 } *pack_revindex = NULL;
-static int pack_revindex_hashsz = 0;
+static int pack_revindex_hashsz;
 
 /*
  * stats
  */
-static int written = 0;
-static int written_delta = 0;
-static int reused = 0;
-static int reused_delta = 0;
+static int written;
+static int written_delta;
+static int reused;
+static int reused_delta;
 
 static int pack_revindex_ix(struct packed_git *p)
 {
@@ -241,6 +247,82 @@ static int encode_header(enum object_type type, unsigned long size, unsigned cha
        return n;
 }
 
+static int check_inflate(unsigned char *data, unsigned long len, unsigned long expect)
+{
+       z_stream stream;
+       unsigned char fakebuf[4096];
+       int st;
+
+       memset(&stream, 0, sizeof(stream));
+       stream.next_in = data;
+       stream.avail_in = len;
+       stream.next_out = fakebuf;
+       stream.avail_out = sizeof(fakebuf);
+       inflateInit(&stream);
+
+       while (1) {
+               st = inflate(&stream, Z_FINISH);
+               if (st == Z_STREAM_END || st == Z_OK) {
+                       st = (stream.total_out == expect &&
+                             stream.total_in == len) ? 0 : -1;
+                       break;
+               }
+               if (st != Z_BUF_ERROR) {
+                       st = -1;
+                       break;
+               }
+               stream.next_out = fakebuf;
+               stream.avail_out = sizeof(fakebuf);
+       }
+       inflateEnd(&stream);
+       return st;
+}
+
+/*
+ * we are going to reuse the existing pack entry data.  make
+ * sure it is not corrupt.
+ */
+static int revalidate_pack_entry(struct object_entry *entry, unsigned char *data, unsigned long len)
+{
+       enum object_type type;
+       unsigned long size, used;
+
+       if (pack_to_stdout)
+               return 0;
+
+       /* the caller has already called use_packed_git() for us,
+        * so it is safe to access the pack data from mmapped location.
+        * make sure the entry inflates correctly.
+        */
+       used = unpack_object_header_gently(data, len, &type, &size);
+       if (!used)
+               return -1;
+       if (type == OBJ_DELTA)
+               used += 20; /* skip base object name */
+       data += used;
+       len -= used;
+       return check_inflate(data, len, entry->size);
+}
+
+static int revalidate_loose_object(struct object_entry *entry,