Merge branch 'js/branch-track'
authorJunio C Hamano <gitster@pobox.com>
Wed, 27 Feb 2008 21:02:57 +0000 (13:02 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 27 Feb 2008 21:02:57 +0000 (13:02 -0800)
* js/branch-track:
  doc: documentation update for the branch track changes
  branch: optionally setup branch.*.merge from upstream local branches

Conflicts:

Documentation/config.txt
Documentation/git-branch.txt
Documentation/git-checkout.txt
builtin-branch.c
cache.h
t/t7201-co.sh

311 files changed:
.gitattributes [new file with mode: 0644]
.gitignore
.mailmap
Documentation/.gitattributes [new file with mode: 0644]
Documentation/Makefile
Documentation/RelNotes-1.5.3.3.txt
Documentation/RelNotes-1.5.4.1.txt [new file with mode: 0644]
Documentation/RelNotes-1.5.4.2.txt [new file with mode: 0644]
Documentation/RelNotes-1.5.4.3.txt [new file with mode: 0644]
Documentation/RelNotes-1.5.4.txt
Documentation/RelNotes-1.5.5.txt [new file with mode: 0644]
Documentation/SubmittingPatches
Documentation/blame-options.txt
Documentation/config.txt
Documentation/diff-options.txt
Documentation/fetch-options.txt
Documentation/git-add.txt
Documentation/git-am.txt
Documentation/git-bundle.txt
Documentation/git-clone.txt
Documentation/git-commit.txt
Documentation/git-config.txt
Documentation/git-cvsimport.txt
Documentation/git-cvsserver.txt
Documentation/git-describe.txt
Documentation/git-fast-import.txt
Documentation/git-filter-branch.txt
Documentation/git-format-patch.txt
Documentation/git-grep.txt
Documentation/git-help.txt
Documentation/git-http-push.txt
Documentation/git-instaweb.txt
Documentation/git-merge-index.txt
Documentation/git-pack-objects.txt
Documentation/git-pull.txt
Documentation/git-push.txt
Documentation/git-remote.txt
Documentation/git-reset.txt
Documentation/git-rev-list.txt
Documentation/git-rev-parse.txt
Documentation/git-send-email.txt
Documentation/git-stash.txt
Documentation/git-submodule.txt
Documentation/git-svn.txt
Documentation/git-web--browse.txt [new file with mode: 0644]
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitignore.txt
Documentation/hooks.txt
Documentation/manpage-1.72.xsl [new file with mode: 0644]
Documentation/rev-list-options.txt
Documentation/technical/api-remote.txt [new file with mode: 0644]
Documentation/technical/api-run-command.txt
Documentation/technical/racy-git.txt
Documentation/urls.txt
GIT-VERSION-GEN
INSTALL
Makefile
RelNotes
alias.c [new file with mode: 0644]
archive-tar.c
attr.c
builtin-add.c
builtin-apply.c
builtin-blame.c
builtin-branch.c
builtin-checkout.c
builtin-clean.c
builtin-commit.c
builtin-config.c
builtin-describe.c
builtin-diff.c
builtin-fast-export.c
builtin-fetch-pack.c
builtin-fetch.c
builtin-fmt-merge-msg.c
builtin-for-each-ref.c
builtin-fsck.c
builtin-gc.c
builtin-grep.c
builtin-init-db.c
builtin-log.c
builtin-ls-files.c
builtin-ls-remote.c
builtin-mailinfo.c
builtin-merge-file.c
builtin-merge-recursive.c
builtin-mv.c
builtin-name-rev.c
builtin-pack-objects.c
builtin-prune.c
builtin-push.c
builtin-read-tree.c
builtin-reflog.c
builtin-rerere.c
builtin-rev-list.c
builtin-rev-parse.c
builtin-revert.c
builtin-send-pack.c
builtin-shortlog.c
builtin-show-branch.c
builtin-show-ref.c
builtin-tag.c
builtin-verify-tag.c
builtin-write-tree.c
builtin.h
bundle.c
cache-tree.c
cache-tree.h
cache.h
color.c
color.h
commit.c
commit.h
compat/fopen.c [new file with mode: 0644]
compat/qsort.c [new file with mode: 0644]
config.c
config.mak.in
configure.ac
connect.c
contrib/completion/git-completion.bash
contrib/emacs/git-blame.el
contrib/emacs/git.el
contrib/examples/git-checkout.sh
contrib/fast-import/git-p4
contrib/hg-to-git/hg-to-git.py
convert.c
copy.c
date.c
diff.c
diff.h
dir.c
dir.h
environment.c
fast-import.c
fetch-pack.h
git-add--interactive.perl
git-am.sh
git-bisect.sh
git-clone.sh
git-compat-util.h
git-cvsexportcommit.perl
git-cvsimport.perl
git-cvsserver.perl
git-filter-branch.sh
git-gui/Makefile
git-gui/git-gui.sh
git-gui/lib/about.tcl
git-gui/lib/checkout_op.tcl
git-gui/lib/choose_repository.tcl
git-gui/lib/choose_rev.tcl
git-gui/lib/commit.tcl
git-gui/lib/error.tcl
git-gui/lib/index.tcl
git-gui/lib/merge.tcl
git-gui/lib/option.tcl
git-gui/lib/spellcheck.tcl [new file with mode: 0644]
git-gui/macosx/Info.plist
git-gui/po/de.po
git-gui/po/git-gui.pot
git-gui/po/glossary/fr.po
git-gui/po/po2msg.sh
git-instaweb.sh
git-pull.sh
git-rebase--interactive.sh
git-relink.perl
git-remote.perl
git-send-email.perl
git-sh-setup.sh
git-submodule.sh
git-svn.perl
git-web--browse.sh [moved from git-help--browse.sh with 72% similarity]
git.c
git.spec.in
gitk-git/gitk
gitweb/README
gitweb/gitweb.perl
hash-object.c
hash.c
help.c
http-push.c
http.c
http.h
imap-send.c
list-objects.c
log-tree.c
log-tree.h
merge-index.c
object.c
pager.c
parse-options.c
pretty.c
reachable.c
read-cache.c
receive-pack.c
refs.c
remote.c
remote.h
revision.c
revision.h
run-command.c
run-command.h
setup.c
sha1_file.c
sha1_name.c
shallow.c
shortlog.h [new file with mode: 0644]
strbuf.c
strbuf.h
t/.gitattributes [new file with mode: 0644]
t/README
t/t0000-basic.sh
t/t0003-attributes.sh [new file with mode: 0755]
t/t0020-crlf.sh
t/t0030-stripspace.sh
t/t0040-parse-options.sh
t/t0050-filesystem.sh [new file with mode: 0755]
t/t1000-read-tree-m-3way.sh
t/t1200-tutorial.sh
t/t1300-repo-config.sh
t/t1302-repo-version.sh
t/t1400-update-ref.sh
t/t1500-rev-parse.sh
t/t1502-rev-parse-parseopt.sh [new file with mode: 0755]
t/t2000-checkout-cache-clash.sh
t/t2002-checkout-cache-u.sh
t/t2008-checkout-subdir.sh
t/t2100-update-cache-badpath.sh
t/t3001-ls-files-others-exclude.sh
t/t3020-ls-files-error-unmatch.sh
t/t3200-branch.sh
t/t3210-pack-refs.sh
t/t3400-rebase.sh
t/t3403-rebase-skip.sh
t/t3404-rebase-interactive.sh
t/t3600-rm.sh
t/t3701-add-interactive.sh [new file with mode: 0755]
t/t4013-diff-various.sh
t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^ [new file with mode: 0644]
t/t4014-format-patch.sh
t/t4015-diff-whitespace.sh
t/t4019-diff-wserror.sh
t/t4026-color.sh [new file with mode: 0755]
t/t4103-apply-binary.sh
t/t4105-apply-fuzz.sh [new file with mode: 0755]
t/t4113-apply-ending.sh
t/t4125-apply-ws-fuzz.sh [new file with mode: 0755]
t/t5100-mailinfo.sh
t/t5100/info0009 [new file with mode: 0644]
t/t5100/msg0009 [new file with mode: 0644]
t/t5100/patch0009 [new file with mode: 0644]
t/t5100/sample.mbox
t/t5300-pack-object.sh
t/t5302-pack-index.sh
t/t5303-hash-object.sh [new file with mode: 0755]
t/t5304-prune.sh [new file with mode: 0644]
t/t5401-update-hooks.sh
t/t5402-post-merge-hook.sh
t/t5500-fetch-pack.sh
t/t5510-fetch.sh
t/t5516-fetch-push.sh
t/t5520-pull.sh
t/t5530-upload-pack-error.sh
t/t5600-clone-fail-cleanup.sh
t/t5701-clone-local.sh
t/t5710-info-alternate.sh
t/t6009-rev-list-parent.sh [new file with mode: 0755]
t/t6023-merge-file.sh
t/t6024-recursive-merge.sh
t/t6025-merge-symlinks.sh
t/t6029-merge-subtree.sh [new file with mode: 0755]
t/t6030-bisect-porcelain.sh
t/t6101-rev-parse-parents.sh
t/t6300-for-each-ref.sh
t/t7001-mv.sh
t/t7002-grep.sh
t/t7003-filter-branch.sh
t/t7004-tag.sh
t/t7010-setup.sh [new file with mode: 0755]
t/t7101-reset.sh
t/t7104-reset.sh [new file with mode: 0755]
t/t7201-co.sh
t/t7300-clean.sh
t/t7400-submodule-basic.sh
t/t7501-commit.sh
t/t7502-commit.sh
t/t7502-status.sh
t/t7503-pre-commit-hook.sh
t/t7504-commit-msg-hook.sh
t/t7505-prepare-commit-msg-hook.sh [new file with mode: 0755]
t/t9001-send-email.sh
t/t9100-git-svn-basic.sh
t/t9106-git-svn-commit-diff-clobber.sh
t/t9106-git-svn-dcommit-clobber-series.sh
t/t9200-git-cvsexportcommit.sh
t/t9300-fast-import.sh
t/t9400-git-cvsserver-server.sh
t/test-lib.sh
tag.c
templates/hooks--commit-msg
templates/hooks--prepare-commit-msg [new file with mode: 0644]
test-parse-options.c
thread-utils.c [new file with mode: 0644]
thread-utils.h [new file with mode: 0644]
transport.c
unpack-trees.c
upload-pack.c
ws.c
wt-status.c
xdiff/xdiff.h
xdiff/xmerge.c

diff --git a/.gitattributes b/.gitattributes
new file mode 100644 (file)
index 0000000..6b9c715
--- /dev/null
@@ -0,0 +1,2 @@
+* whitespace=!indent,trail,space
+*.[ch] whitespace
index 7f8421d..165b256 100644 (file)
@@ -50,7 +50,6 @@ git-gc
 git-get-tar-commit-id
 git-grep
 git-hash-object
-git-help--browse
 git-http-fetch
 git-http-push
 git-imap-send
@@ -136,6 +135,7 @@ git-upload-pack
 git-var
 git-verify-pack
 git-verify-tag
+git-web--browse
 git-whatchanged
 git-write-tree
 git-core-*/?*
index 695739e..f88ae77 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -17,6 +17,7 @@ H. Peter Anvin <hpa@bonde.sc.orionmulti.com>
 H. Peter Anvin <hpa@tazenda.sc.orionmulti.com>
 H. Peter Anvin <hpa@trantor.hos.anvin.org>
 Horst H. von Brand <vonbrand@inf.utfsm.cl>
+Jay Soffian <jaysoffian+git@gmail.com>
 Joachim Berdal Haga <cjhaga@fys.uio.no>
 Jon Loeliger <jdl@freescale.com>
 Jon Seymour <jon@blackcubes.dyndns.org>
@@ -43,6 +44,7 @@ Steven Grimm <koreth@midwinter.com>
 Theodore Ts'o <tytso@mit.edu>
 Tony Luck <tony.luck@intel.com>
 Uwe Kleine-König <Uwe_Zeisberger@digi.com>
+Uwe Kleine-König <Uwe.Kleine-Koenig@digi.com>
 Uwe Kleine-König <ukleinek@informatik.uni-freiburg.de>
 Uwe Kleine-König <uzeisberger@io.fsforth.de>
 Uwe Kleine-König <zeisberg@informatik.uni-freiburg.de>
diff --git a/Documentation/.gitattributes b/Documentation/.gitattributes
new file mode 100644 (file)
index 0000000..ddb0301
--- /dev/null
@@ -0,0 +1 @@
+*.txt whitespace
index 7a32546..43781fb 100644 (file)
@@ -45,6 +45,7 @@ man7dir=$(mandir)/man7
 
 ASCIIDOC=asciidoc
 ASCIIDOC_EXTRA =
+MANPAGE_XSL = callouts.xsl
 INSTALL?=install
 RM ?= rm -f
 DOC_REF = origin/man
@@ -65,6 +66,7 @@ ASCIIDOC_EXTRA += -a asciidoc7compatible
 endif
 ifdef DOCBOOK_XSL_172
 ASCIIDOC_EXTRA += -a docbook-xsl-172
+MANPAGE_XSL = manpage-1.72.xsl
 endif
 
 #
@@ -159,7 +161,7 @@ $(MAN_HTML): %.html : %.txt
 
 %.1 %.5 %.7 : %.xml
        $(RM) $@
-       xmlto -m callouts.xsl man $<
+       xmlto -m $(MANPAGE_XSL) man $<
 
 %.xml : %.txt
        $(RM) $@+ $@
index 2a7bfdd..d213846 100644 (file)
@@ -12,7 +12,7 @@ Fixes since v1.5.3.2
  * The default shell on some FreeBSD did not execute the
    argument parsing code correctly and made git unusable.
 
- * git-svn incorrectly spawned pager even when the user user
+ * git-svn incorrectly spawned pager even when the user
    explicitly asked not to.
 
  * sample post-receive hook overquoted the envelope sender
diff --git a/Documentation/RelNotes-1.5.4.1.txt b/Documentation/RelNotes-1.5.4.1.txt
new file mode 100644 (file)
index 0000000..d4e44b8
--- /dev/null
@@ -0,0 +1,17 @@
+GIT v1.5.4.1 Release Notes
+==========================
+
+Fixes since v1.5.4
+------------------
+
+ * "git-commit -C $tag" used to work but rewrite in C done in
+   1.5.4 broke it.
+
+ * An entry in the .gitattributes file that names a pattern in a
+   subdirectory of the directory it is in did not match
+   correctly (e.g. pattern "b/*.c" in "a/.gitattributes" should
+   match "a/b/foo.c" but it didn't).
+
+ * Customized color specification was parsed incorrectly when
+   numeric color values are used.  This was fixed in 1.5.4.1.
+
diff --git a/Documentation/RelNotes-1.5.4.2.txt b/Documentation/RelNotes-1.5.4.2.txt
new file mode 100644 (file)
index 0000000..21d0df5
--- /dev/null
@@ -0,0 +1,43 @@
+GIT v1.5.4.2 Release Notes
+==========================
+
+Fixes since v1.5.4
+------------------
+
+ * The configuration parser was not prepared to see string
+   valued variables misspelled as boolean and segfaulted.
+
+ * Temporary files left behind due to interrupted object
+   transfers were not cleaned up with "git prune".
+
+ * "git config --unset" was confused when the unset variables
+   were spelled with continuation lines in the config file.
+
+ * The merge message detection in "git cvsimport" did not catch
+   a message that began with "Merge...".
+
+ * "git status" suggests "git rm --cached" for unstaging the
+   earlier "git add" before the initial commit.
+
+ * "git status" output was incorrect during a partial commit.
+
+ * "git bisect" refused to start when the HEAD was detached.
+
+ * "git bisect" allowed a wildcard character in the commit
+   message expanded while writing its log file.
+
+ * Manual pages were not formatted correctly with docbook xsl
+   1.72; added a workaround.
+
+ * "git-commit -C $tag" used to work but rewrite in C done in
+   1.5.4 broke it.  This was fixed in 1.5.4.1.
+
+ * An entry in the .gitattributes file that names a pattern in a
+   subdirectory of the directory it is in did not match
+   correctly (e.g. pattern "b/*.c" in "a/.gitattributes" should
+   match "a/b/foo.c" but it didn't).  This was fixed in 1.5.4.1.
+
+ * Customized color specification was parsed incorrectly when
+   numeric color values are used.  This was fixed in 1.5.4.1.
+
+ * http transport misbehaved when linked with curl-gnutls.
diff --git a/Documentation/RelNotes-1.5.4.3.txt b/Documentation/RelNotes-1.5.4.3.txt
new file mode 100644 (file)
index 0000000..b0fc67f
--- /dev/null
@@ -0,0 +1,27 @@
+GIT v1.5.4.3 Release Notes
+==========================
+
+Fixes since v1.5.4.2
+--------------------
+
+ * RPM spec used to pull in everything with 'git'.  This has been
+   changed so that 'git' package contains just the core parts,
+   and we now supply 'git-all' metapackage to slurp in everything.
+   This should match end user's expectation better.
+
+ * When some refs failed to update, git-push reported "failure"
+   which was unclear if some other refs were updated or all of
+   them failed atomically (the answer is the former).  Reworded
+   the message to clarify this.
+
+ * "git clone" from a repository whose HEAD was misconfigured
+   did not set up the remote properly.  Now it tries to do
+   better.
+
+ * Updated git-push documentation to clarify what "matching"
+   means, in order to reduce user confusion.
+
+ * Updated git-add documentation to clarify "add -u" operates in
+   the current subdirectory you are in, just like other commands.
+
+ * git-gui updates to work on OSX and Windows better.
index 9c864c9..f1323b6 100644 (file)
@@ -10,22 +10,32 @@ Removal
  * As git-commit and git-status have been rewritten, "git runstatus"
    helper script lost all its users and has been removed.
 
- * Curl library older than 7.10 is not supported by "git http-push",
-   as it does not work without CURLM.
+
+Temporarily disabled
+--------------------
+
+ * "git http-push" is known not to work well with cURL library older
+   than 7.16, and we had reports of repository corruption.  It is
+   disabled on such platforms for now.  Unfortunately, 1.5.3.8 shares
+   the same issue.  In other words, this does not mean you will be
+   fine if you stick to an older git release.  For now, please do not
+   use http-push from older git with cURL older than 7.16 if you
+   value your data. A proper fix will hopefully materialize in
+   later versions.
 
 
 Deprecation notices
 -------------------
 
- * The next feature release of git (this change is scheduled for v1.6.0)
-   will by default install dashed form of commands (e.g. "git-commit")
-   outside of users' normal $PATH, and will install only selected
-   commands ("git" itself, and "gitk") in $PATH.  This implies:
+ * From v1.6.0, git will by default install dashed form of commands
+   (e.g. "git-commit") outside of users' normal $PATH, and will install
+   only selected commands ("git" itself, and "gitk") in $PATH.  This
+   implies:
 
    - Using dashed forms of git commands (e.g. "git-commit") from the
      command line has been informally deprecated since early 2006, but
      now it officially is, and will be removed in the future.  Use
-     dashless forms (e.g. "git commit") instead.
+     dash-less forms (e.g. "git commit") instead.
 
    - Using dashed forms from your scripts, without first prepending the
      return value from "git --exec-path" to the scripts' PATH, has been
@@ -34,8 +44,8 @@ Deprecation notices
    - Use of dashed forms with "PATH=$(git --exec-path):$PATH; export
      PATH" early in your script is not deprecated with this change.
 
-  Users are strongly encouraged to adjust their habits and scripts now
-  to prepare for this.
+   Users are strongly encouraged to adjust their habits and scripts now
+   to prepare for this change.
 
  * The post-receive hook was introduced in March 2007 to supersede
    the post-update hook, primarily to overcome the command line length
@@ -70,7 +80,7 @@ Updates since v1.5.3
 
  * Comes with much improved gitk, with i18n.
 
- * Comes with "git gui" 0.9.1 with i18n.
+ * Comes with git-gui 0.9.2 with i18n.
 
  * gitk is now merged as a subdirectory of git.git project, in
    preparation for its i18n.
@@ -156,7 +166,7 @@ Updates since v1.5.3
    command line in the generated log message, when told to cherry-pick a
    commit by naming a tag that points at it.  It does not anymore.
 
- * "git for-each-ref" learned %(xxxdate:<dateformat>) syntax to show the
+ * "git for-each-ref" learned %(xxxdate:<date-format>) syntax to show the
    various date fields in different formats.
 
  * "git gc --auto" is a low-impact way to automatically run a variant of
@@ -211,7 +221,7 @@ Updates since v1.5.3
  * "git pull --rebase" is a different way to integrate what you fetched
    into your current branch.
 
- * "git fast-export" produces datastream that can be fed to fast-import
+ * "git fast-export" produces data-stream that can be fed to fast-import
    to reproduce the history recorded in a git repository.
 
  * "git add -i" takes pathspecs to limit the set of files to work on.
@@ -232,8 +242,8 @@ Updates since v1.5.3
    from its first parent.
 
  * "git commit" used to unconditionally strip comment lines that
-   began with '#' and removed excess blank lines.  This
-   behaviour has been made configurable.
+   began with '#' and removed excess blank lines.  This behavior has
+   been made configurable.
 
  * "git commit" has been rewritten in C.
 
@@ -317,7 +327,7 @@ Updates since v1.5.3
 
  * "git status" from a subdirectory now shows relative paths, which
    makes copy-and-pasting for git-checkout/git-add/git-rm easier.  The
-   traditional behaviour to show the full path relative to the top of
+   traditional behavior to show the full path relative to the top of
    the work tree can be had by setting status.relativepaths
    configuration variable to false.
 
@@ -332,7 +342,7 @@ Updates since v1.5.3
 
  * "git help" learned "-w" option to show documentation in browsers.
 
- * In addition there are quite a few internal clean-ups. Notably
+ * In addition there are quite a few internal clean-ups. Notably:
 
    - many fork/exec have been replaced with run-command API,
      brought from the msysgit effort.
@@ -341,6 +351,7 @@ Updates since v1.5.3
 
    - enhancement and more use of the strbuf API.
 
+ * Makefile tweaks to support HP-UX is in.
 
 Fixes since v1.5.3
 ------------------
@@ -354,7 +365,7 @@ series.
  * The way "git diff --check" behaves is much more consistent with the way
    "git apply --whitespace=warn" works.
 
- * "git svn" talking with the SVN over http will correctly quote branch
+ * "git svn" talking with the SVN over HTTP will correctly quote branch
    and project names.
 
  * "git config" did not work correctly on platforms that define
@@ -364,9 +375,3 @@ series.
    documentation; a workaround has been implemented.
 
  * "git diff --color-words" colored context lines in a wrong color.
-
---
-exec >/var/tmp/1
-O=v1.5.4-rc4
-echo O=`git describe refs/heads/master`
-git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
diff --git a/Documentation/RelNotes-1.5.5.txt b/Documentation/RelNotes-1.5.5.txt
new file mode 100644 (file)
index 0000000..c8b4f72
--- /dev/null
@@ -0,0 +1,78 @@
+GIT v1.5.5 Release Notes
+========================
+
+Updates since v1.5.4
+--------------------
+
+(performance)
+
+ * On platforms with suboptimal qsort(3) implementation, there
+   is an option to use more reasonable substitute we ship with
+   our software.
+
+ * New configuration variable "pack.packsizelimit" can be used
+   in place of command line option --max-pack-size.
+
+ * "git fetch" over the native git protocol used to make a
+   connection to find out the set of current remote refs and
+   another to actually download the pack data.  We now use only
+   one connection for these tasks.
+
+ * "git commit" does not run lstat(2) more than necessary
+   anymore.
+
+(usability, bells and whistles)
+
+ * You can be warned when core.autocrlf conversion is applied in
+   such a way that results in an irreversible conversion.
+
+ * A pattern "foo/" in .gitignore file now matches a directory
+   "foo".  Pattern "foo" also matches as before.
+
+ * "git describe" learned to limit the tags to be used for
+   naming with --match option.
+
+ * "git describe --contains" now barfs when the named commit
+   cannot be described.
+
+ * bash completion's prompt helper function can talk about
+   operation in-progress (e.g. merge, rebase, etc.).
+
+ * "git commit" learned a new hook "prepare-commit-msg" that can
+   inspect what is going to be committed and prepare the commit
+   log message template to be edited.
+
+ * "git gui" learned an auto-spell checking.
+
+ * "git send-email" learned to prompt for passwords
+   interactively.
+
+ * "git send-email" learned an easier way to suppress CC
+   recipients.
+
+ * Various "git cvsimport", "git cvsexportcommit", "git svn" and
+   "git p4" improvements.
+
+(internal)
+
+ * Duplicated code between git-help and git-instaweb that
+   launches user's preferred browser has been refactored.
+
+ * It is now easier to write test scripts that records known
+   breakages.
+
+
+Fixes since v1.5.4
+------------------
+
+All of the fixes in v1.5.4 maintenance series are included in
+this release, unless otherwise noted.
+
+
+---
+exec >/var/tmp/1
+O=v1.5.4
+O=v1.5.4.2-122-g7cb97da
+echo O=`git describe refs/heads/master`
+git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
+
index de08d09..0e155c9 100644 (file)
@@ -34,9 +34,9 @@ Checklist (and a short version for the impatient):
        - if your name is not writable in ASCII, make sure that
          you send off a message in the correct encoding.
        - send the patch to the list (git@vger.kernel.org) and the
-         maintainer (gitster@pobox.com). If you use
-         git-send-email(1), please test it first by sending
-         email to yourself.
+         maintainer (gitster@pobox.com) if (and only if) the patch
+         is ready for inclusion. If you use git-send-email(1),
+         please test it first by sending email to yourself.
 
 Long version:
 
@@ -112,7 +112,12 @@ lose tabs that way if you are not careful.
 
 It is a common convention to prefix your subject line with
 [PATCH].  This lets people easily distinguish patches from other
-e-mail discussions.
+e-mail discussions.  Use of additional markers after PATCH and
+the closing bracket to mark the nature of the patch is also
+encouraged.  E.g. [PATCH/RFC] is often used when the patch is
+not ready to be applied but it is for discussion, [PATCH v2],
+[PATCH v3] etc. are often seen when you are sending an update to
+what you have previously sent.
 
 "git format-patch" command follows the best current practice to
 format the body of an e-mail message.  At the beginning of the
@@ -157,7 +162,8 @@ Note that your maintainer does not necessarily read everything
 on the git mailing list.  If your patch is for discussion first,
 send it "To:" the mailing list, and optionally "cc:" him.  If it
 is trivially correct or after the list reached a consensus, send
-it "To:" the maintainer and optionally "cc:" the list.
+it "To:" the maintainer and optionally "cc:" the list for
+inclusion.
 
 Also note that your maintainer does not actively involve himself in
 maintaining what are in contrib/ hierarchy.  When you send fixes and
@@ -210,10 +216,53 @@ then you just add a line saying
 This line can be automatically added by git if you run the git-commit
 command with the -s option.
 
-Some people also put extra tags at the end.  They'll just be ignored for
-now, but you can do this to mark internal company procedures or just
-point out some special detail about the sign-off.
+Notice that you can place your own Signed-off-by: line when
+forwarding somebody else's patch with the above rules for
+D-C-O.  Indeed you are encouraged to do so.  Do not forget to
+place an in-body "From: " line at the beginning to properly attribute
+the change to its true author (see (2) above).
 
+Some people also put extra tags at the end.
+
+"Acked-by:" says that the patch was reviewed by the person who
+is more familiar with the issues and the area the patch attempts
+to modify.  "Tested-by:" says the patch was tested by the person
+and found to have the desired effect.
+
+------------------------------------------------
+An ideal patch flow
+
+Here is an ideal patch flow for this project the current maintainer
+suggests to the contributors:
+
+ (0) You come up with an itch.  You code it up.
+
+ (1) Send it to the list and cc people who may need to know about
+     the change.
+
+     The people who may need to know are the ones whose code you
+     are butchering.  These people happen to be the ones who are
+     most likely to be knowledgeable enough to help you, but
+     they have no obligation to help you (i.e. you ask for help,
+     don't demand).  "git log -p -- $area_you_are_modifying" would
+     help you find out who they are.
+
+ (2) You get comments and suggestions for improvements.  You may
+     even get them in a "on top of your change" patch form.
+
+ (3) Polish, refine, and re-send to the list and the people who
+     spend their time to improve your patch.  Go back to step (2).
+
+ (4) The list forms consensus that the last round of your patch is
+     good.  Send it to the list and cc the maintainer.
+
+ (5) A topic branch is created with the patch and is merged to 'next',
+     and cooked further and eventually graduates to 'master'.
+
+In any time between the (2)-(3) cycle, the maintainer may pick it up
+from the list and queue it to 'pu', in order to make it easier for
+people play with it without having to pick up and apply the patch to
+their trees themselves.
 
 ------------------------------------------------
 MUA specific hints
index ea1007b..c11bb7d 100644 (file)
@@ -52,7 +52,7 @@ of lines before or after the line given by <start>.
        When <rev> is not specified, the command annotates the
        changes starting backwards from the working tree copy.
        This flag makes the command pretend as if the working
-       tree copy has the contents of he named file (specify
+       tree copy has the contents of the named file (specify
        `-` to make the command read from the standard input).
 
 -M|<num>|::
index f5994bd..4027726 100644 (file)
@@ -139,6 +139,51 @@ core.autocrlf::
        "text" (i.e. be subjected to the autocrlf mechanism) is
        decided purely based on the contents.
 
+core.safecrlf::
+       If true, makes git check if converting `CRLF` as controlled by
+       `core.autocrlf` is reversible.  Git will verify if a command
+       modifies a file in the work tree either directly or indirectly.
+       For example, committing a file followed by checking out the
+       same file should yield the original file in the work tree.  If
+       this is not the case for the current setting of
+       `core.autocrlf`, git will reject the file.  The variable can
+       be set to "warn", in which case git will only warn about an
+       irreversible conversion but continue the operation.
++
+CRLF conversion bears a slight chance of corrupting data.
+autocrlf=true will convert CRLF to LF during commit and LF to
+CRLF during checkout.  A file that contains a mixture of LF and
+CRLF before the commit cannot be recreated by git.  For text
+files this is the right thing to do: it corrects line endings
+such that we have only LF line endings in the repository.
+But for binary files that are accidentally classified as text the
+conversion can corrupt data.
++
+If you recognize such corruption early you can easily fix it by
+setting the conversion type explicitly in .gitattributes.  Right
+after committing you still have the original file in your work
+tree and this file is not yet corrupted.  You can explicitly tell
+git that this file is binary and git will handle the file
+appropriately.
++
+Unfortunately, the desired effect of cleaning up text files with
+mixed line endings and the undesired effect of corrupting binary
+files cannot be distinguished.  In both cases CRLFs are removed
+in an irreversible way.  For text files this is the right thing
+to do because CRLFs are line endings, while for binary files
+converting CRLFs corrupts data.
++
+Note, this safety check does not mean that a checkout will generate a
+file identical to the original file for a different setting of
+`core.autocrlf`, but only for the current one.  For example, a text
+file with `LF` would be accepted with `core.autocrlf=input` and could
+later be checked out with `core.autocrlf=true`, in which case the
+resulting file would contain `CRLF`, although the original file
+contained `LF`.  However, in both work trees the line endings would be
+consistent, that is either all `LF` or all `CRLF`, but never mixed.  A
+file with mixed line endings would be reported by the `core.safecrlf`
+mechanism.
+
 core.symlinks::
        If false, symbolic links are checked out as small plain files that
        contain the link text. linkgit:git-update-index[1] and
@@ -308,6 +353,10 @@ core.whitespace::
   error (enabled by default).
 * `indent-with-non-tab` treats a line that is indented with 8 or more
   space characters as an error (not enabled by default).
+* `cr-at-eol` treats a carriage-return at the end of line as
+  part of the line terminator, i.e. with it, `trailing-space`
+  does not trigger if the character before such a carriage-return
+  is not a whitespace (not enabled by default).
 
 alias.*::
        Command aliases for the linkgit:git[1] command wrapper - e.g.
@@ -371,6 +420,11 @@ branch.<name>.rebase::
        it unless you understand the implications (see linkgit:git-rebase[1]
        for details).
 
+browser.<tool>.path::
+       Override the path for the given tool that may be used to
+       browse HTML help (see '-w' option in linkgit:git-help[1]) or a
+       working repository in gitweb (see linkgit:git-instaweb[1]).
+
 clean.requireForce::
        A boolean to make git-clean do nothing unless given -f
        or -n.   Defaults to true.
@@ -443,6 +497,13 @@ color.status.<slot>::
 commit.template::
        Specify a file to use as the template for new commit messages.
 
+color.ui::
+       When set to `always`, always use colors in all git commands which
+       are capable of colored output. When false (or `never`), never. When
+       set to `true` or `auto`, use colors only when the output is to the
+       terminal. When more specific variables of color.* are set, they always
+       take precedence over this setting. Defaults to false.
+
 diff.autorefreshindex::
        When using `git diff` to compare with work tree
        files, do not consider stat-only change as changed.
@@ -755,6 +816,8 @@ pack.threads::
        warning. This is meant to reduce packing time on multiprocessor
        machines. The required amount of memory for the delta search window
        is however multiplied by the number of threads.
+       Specifying 0 will cause git to auto-detect the number of CPU's
+       and set the number of threads accordingly.
 
 pack.indexVersion::
        Specify the default pack index version.  Valid values are 1 for
@@ -765,6 +828,12 @@ pack.indexVersion::
        whenever the corresponding pack is larger than 2 GB.  Otherwise
        the default is 1.
 
+pack.packSizeLimit:
+       The default maximum size of a pack.  This setting only affects
+       packing to a file, i.e. the git:// protocol is unaffected.  It
+       can be overridden by the `\--max-pack-size` option of
+       linkgit:git-repack[1].
+
 pull.octopus::
        The default merge strategy to use when pulling multiple branches
        at once.
@@ -834,6 +903,17 @@ tar.umask::
        archiving user's umask will be used instead.  See umask(2) and
        linkgit:git-archive[1].
 
+url.<base>.insteadOf::
+       Any URL that starts with this value will be rewritten to
+       start, instead, with <base>. In cases where some site serves a
+       large number of repositories, and serves them with multiple
+       access methods, and some users need to use different access
+       methods, this feature allows people to specify any of the
+       equivalent URLs and have git automatically rewrite the URL to
+       the best alternative for the particular user, even for a
+       never-before-seen repository on the site.  When more than one
+       insteadOf strings match a given URL, the longest match is used.
+
 user.email::
        Your email address to be recorded in any newly created commits.
        Can be overridden by the 'GIT_AUTHOR_EMAIL', 'GIT_COMMITTER_EMAIL', and
index 8d35cbd..8dc5b00 100644 (file)
@@ -170,6 +170,14 @@ endif::git-format-patch[]
        Swap two inputs; that is, show differences from index or
        on-disk file to tree contents.
 
+--relative[=<path>]::
+       When run from a subdirectory of the project, it can be
+       told to exclude changes outside the directory and show
+       pathnames relative to it with this option.  When you are
+       not in a subdirectory (e.g. in a bare repository), you
+       can name which subdirectory to make the output relative
+       to by giving a <path> as an argument.
+
 --text::
        Treat all files as text.
 
index 61e48cc..b675911 100644 (file)
        fetches is a descendant of `<lbranch>`.  This option
        overrides that check.
 
+ifdef::git-pull[]
+\--no-tags::
+endif::git-pull[]
+ifndef::git-pull[]
 -n, \--no-tags::
-       By default, `git-fetch` fetches tags that point at
-       objects that are downloaded from the remote repository
-       and stores them locally.  This option disables this
-       automatic tag following.
+endif::git-pull[]
+       By default, tags that point at objects that are downloaded
+       from the remote repository are fetched and stored locally.
+       This option disables this automatic tag following.
 
 -t, \--tags::
        Most of the tags are fetched automatically as branch
index 9d2ac86..4779909 100644 (file)
@@ -74,8 +74,8 @@ OPTIONS
        Update only files that git already knows about. This is similar
        to what "git commit -a" does in preparation for making a commit,
        except that the update is limited to paths specified on the
-       command line. If no paths are specified, all tracked files are
-       updated.
+       command line. If no paths are specified, all tracked files in the
+       current directory and its subdirectories are updated.
 
 \--refresh::
        Don't add the file(s), but only refresh their stat()
index 2ffba21..e640fc7 100644 (file)
@@ -138,7 +138,7 @@ aborts in the middle,.  You can recover from this in one of two ways:
 
 The command refuses to process new mailboxes while `.dotest`
 directory exists, so if you decide to start over from scratch,
-run `rm -f .dotest` before running the command with mailbox
+run `rm -f -r .dotest` before running the command with mailbox
 names.
 
 
index 72f080a..505ac05 100644 (file)
@@ -99,36 +99,62 @@ Assume two repositories exist as R1 on machine A, and R2 on machine B.
 For whatever reason, direct connection between A and B is not allowed,
 but we can move data from A to B via some mechanism (CD, email, etc).
 We want to update R2 with developments made on branch master in R1.
+
+To create the bundle you have to specify the basis. You have some options:
+
+- Without basis.
++
+This is useful when sending the whole history.
+
+------------
+$ git bundle create mybundle master
+------------
+
+- Using temporally tags.
++
 We set a tag in R1 (lastR2bundle) after the previous such transport,
 and move it afterwards to help build the bundle.
 
-in R1 on A:
-
 ------------
 $ git-bundle create mybundle master ^lastR2bundle
 $ git tag -f lastR2bundle master
 ------------
 
-(move mybundle from A to B by some mechanism)
+- Using a tag present in both repositories
+
+------------
+$ git bundle create mybundle master ^v1.0.0
+------------
+
+- A basis based on time.
+
+------------
+$ git bundle create mybundle master --since=10.days.ago
+------------
 
-in R2 on B:
+- With a limit on the number of commits
 
 ------------
-$ git-bundle verify mybundle
-$ git-fetch mybundle  refspec
+$ git bundle create mybundle master -n 10
 ------------
 
-where refspec is refInBundle:localRef
+Then you move mybundle from A to B, and in R2 on B:
 
+------------
+$ git-bundle verify mybundle
+$ git-fetch mybundle master:localRef
+------------
 
-Also, with something like this in your config:
+With something like this in the config in R2:
 
+------------------------
 [remote "bundle"]
     url = /home/me/tmp/file.bdl
     fetch = refs/heads/*:refs/remotes/origin/*
+------------------------
 
 You can first sneakernet the bundle file to ~/tmp/file.bdl and
-then these commands:
+then these commands on machine B:
 
 ------------
 $ git ls-remote bundle
index fdccbd4..9758243 100644 (file)
@@ -62,6 +62,15 @@ OPTIONS
        .git/objects/info/alternates to share the objects
        with the source repository.  The resulting repository
        starts out without any object of its own.
++
+*NOTE*: this is a possibly dangerous operation; do *not* use
+it unless you understand what it does. If you clone your
+repository using this option, then delete branches in the
+source repository and then run linkgit:git-gc[1] using the
+'--prune' option in the source repository, it may remove
+objects which are referenced by the cloned repository.
+
+
 
 --reference <repository>::
        If the reference repository is on the local machine
index c3725b2..b4ae61f 100644 (file)
@@ -280,8 +280,8 @@ order).
 
 HOOKS
 -----
-This command can run `commit-msg`, `pre-commit`, and
-`post-commit` hooks.  See link:hooks.html[hooks] for more
+This command can run `commit-msg`, `prepare-commit-msg`, `pre-commit`,
+and `post-commit` hooks.  See link:hooks.html[hooks] for more
 information.
 
 
index e4d0e47..fa16171 100644 (file)
@@ -130,7 +130,7 @@ See also <<FILES>>.
 
 -z, --null::
        For all options that output values and/or keys, always
-       end values with with the null character (instead of a
+       end values with the null character (instead of a
        newline). Use newline instead as a delimiter between
        key and value. This allows for secure parsing of the
        output without getting confused e.g. by values that
index dbce503..6f91b9e 100644 (file)
@@ -107,8 +107,8 @@ If you need to pass multiple options, separate them with a comma.
 
 -M <regex>::
        Attempt to detect merges based on the commit message with a custom
-       regex. It can be used with '-m' to also see the default regexes.
-       You must escape forward slashes.
+       regex. It can be used with '-m' to enable the default regexes
+       as well. You must escape forward slashes.
 
 -S <regex>::
        Skip paths matching the regex.
index d75e401..d3e9993 100644 (file)
@@ -183,7 +183,7 @@ access method and requested operation.
 That means that even if you offer only read access (e.g. by using
 the pserver method), git-cvsserver should have write access to
 the database to work reliably (otherwise you need to make sure
-that the database if up-to-date all the time git-cvsserver is run).
+that the database is up-to-date any time git-cvsserver is executed).
 
 By default it uses SQLite databases in the git directory, named
 `gitcvs.<module_name>.sqlite`. Note that the SQLite backend creates
@@ -204,7 +204,7 @@ about `DBI->connect()`.
 
 gitcvs.dbname::
        Database name. The exact meaning depends on the
-       used database driver, for SQLite this is a filename.
+       selected database driver, for SQLite this is a filename.
        Supports variable substitution (see below). May
        not contain semicolons (`;`).
        Default: '%Ggitcvs.%m.sqlite'
@@ -215,7 +215,7 @@ gitcvs.dbdriver::
        with 'DBD::SQLite', reported to work with
        'DBD::Pg', and reported *not* to work with 'DBD::mysql'.
        Please regard this as an experimental feature. May not
-       contain double colons (`:`).
+       contain colons (`:`).
        Default: 'SQLite'
 
 gitcvs.dbuser::
index 0742152..fbb40a2 100644 (file)
@@ -45,12 +45,21 @@ OPTIONS
        candidates to describe the input committish consider
        up to <n> candidates.  Increasing <n> above 10 will take
        slightly longer but may produce a more accurate result.
+       An <n> of 0 will cause only exact matches to be output.
+
+--exact-match::
+       Only output exact matches (a tag directly references the
+       supplied commit).  This is a synonym for --candidates=0.
 
 --debug::
        Verbosely display information about the searching strategy
        being employed to standard error.  The tag name will still
        be printed to standard out.
 
+--match <pattern>::
+       Only consider tags matching the given pattern (can be used to avoid
+       leaking private tags made from the repository).
+
 EXAMPLES
 --------
 
index bd625ab..96f6767 100644 (file)
@@ -805,6 +805,93 @@ Placing a `progress` command immediately after a `checkpoint` will
 inform the reader when the `checkpoint` has been completed and it
 can safely access the refs that fast-import updated.
 
+Crash Reports
+-------------
+If fast-import is supplied invalid input it will terminate with a
+non-zero exit status and create a crash report in the top level of
+the Git repository it was importing into.  Crash reports contain
+a snapshot of the internal fast-import state as well as the most
+recent commands that lead up to the crash.
+
+All recent commands (including stream comments, file changes and
+progress commands) are shown in the command history within the crash
+report, but raw file data and commit messages are excluded from the
+crash report.  This exclusion saves space within the report file
+and reduces the amount of buffering that fast-import must perform
+during execution.
+
+After writing a crash report fast-import will close the current
+packfile and export the marks table.  This allows the frontend
+developer to inspect the repository state and resume the import from
+the point where it crashed.  The modified branches and tags are not
+updated during a crash, as the import did not complete successfully.
+Branch and tag information can be found in the crash report and
+must be applied manually if the update is needed.
+
+An example crash:
+
+====
+       $ cat >in <<END_OF_INPUT
+       # my very first test commit
+       commit refs/heads/master
+       committer Shawn O. Pearce <spearce> 19283 -0400
+       # who is that guy anyway?
+       data <<EOF
+       this is my commit
+       EOF
+       M 644 inline .gitignore
+       data <<EOF
+       .gitignore
+       EOF
+       M 777 inline bob
+       END_OF_INPUT
+
+       $ git-fast-import <in
+       fatal: Corrupt mode: M 777 inline bob
+       fast-import: dumping crash report to .git/fast_import_crash_8434
+
+       $ cat .git/fast_import_crash_8434
+       fast-import crash report:
+           fast-import process: 8434
+           parent process     : 1391
+           at Sat Sep 1 00:58:12 2007
+
+       fatal: Corrupt mode: M 777 inline bob
+
+       Most Recent Commands Before Crash
+       ---------------------------------
+         # my very first test commit
+         commit refs/heads/master
+         committer Shawn O. Pearce <spearce> 19283 -0400
+         # who is that guy anyway?
+         data <<EOF
+         M 644 inline .gitignore
+         data <<EOF
+       * M 777 inline bob
+
+       Active Branch LRU
+       -----------------
+           active_branches = 1 cur, 5 max
+
+         pos  clock name
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+          1)      0 refs/heads/master
+
+       Inactive Branches
+       -----------------
+       refs/heads/master:
+         status      : active loaded dirty
+         tip commit  : 0000000000000000000000000000000000000000
+         old tree    : 0000000000000000000000000000000000000000
+         cur tree    : 0000000000000000000000000000000000000000
+         commit clock: 0
+         last pack   :
+
+
+       -------------------
+       END OF CRASH REPORT
+====
+
 Tips and Tricks
 ---------------
 The following tips and tricks have been collected from various
index e22dfa5..543a1cf 100644 (file)
@@ -56,7 +56,9 @@ notable exception of the commit filter, for technical reasons).
 Prior to that, the $GIT_COMMIT environment variable will be set to contain
 the id of the commit being rewritten.  Also, GIT_AUTHOR_NAME,
 GIT_AUTHOR_EMAIL, GIT_AUTHOR_DATE, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL,
-and GIT_COMMITTER_DATE are set according to the current commit.
+and GIT_COMMITTER_DATE are set according to the current commit. If any
+evaluation of <command> returns a non-zero exit status, the whole operation
+will be aborted.
 
 A 'map' function is available that takes an "original sha1 id" argument
 and outputs a "rewritten sha1 id" if the commit has been already
@@ -197,7 +199,7 @@ happened).  If this is not the case, use:
 
 --------------------------------------------------------------------------
 git filter-branch --parent-filter \
-       'cat; test $GIT_COMMIT = <commit-id> && echo "-p <graft-id>"' HEAD
+       'test $GIT_COMMIT = <commit-id> && echo "-p <graft-id>" || cat' HEAD
 --------------------------------------------------------------------------
 
 or even simpler:
@@ -240,6 +242,15 @@ committed a merge between P1 and P2, it will be propagated properly
 and all children of the merge will become merge commits with P1,P2
 as their parents instead of the merge commit.
 
+You can rewrite the commit log messages using `--message-filter`.  For
+example, `git-svn-id` strings in a repository created by `git-svn` can
+be removed this way:
+
+-------------------------------------------------------
+git filter-branch --message-filter '
+       sed -e "/^git-svn-id:/d"
+'
+-------------------------------------------------------
 
 To restrict rewriting to only part of the history, specify a revision
 range in addition to the new branch name.  The new branch name will
index 651efe6..b5207b7 100644 (file)
@@ -10,13 +10,15 @@ SYNOPSIS
 --------
 [verse]
 'git-format-patch' [-k] [-o <dir> | --stdout] [--thread]
-                   [--attach[=<boundary>] | --inline[=<boundary>]]
-                   [-s | --signoff] [<common diff options>]
-                   [-n | --numbered | -N | --no-numbered]
-                   [--start-number <n>] [--numbered-files]
-                   [--in-reply-to=Message-Id] [--suffix=.<sfx>]
-                   [--ignore-if-in-upstream]
-                   [--subject-prefix=Subject-Prefix]
+                  [--attach[=<boundary>] | --inline[=<boundary>]]
+                  [-s | --signoff] [<common diff options>]
+                  [-n | --numbered | -N | --no-numbered]
+                  [--start-number <n>] [--numbered-files]
+                  [--in-reply-to=Message-Id] [--suffix=.<sfx>]
+                  [--ignore-if-in-upstream]
+                  [--subject-prefix=Subject-Prefix]
+                  [--cc=<email>]
+                  [--cover-letter]
                   [ <since> | <revision range> ]
 
 DESCRIPTION
@@ -135,6 +137,15 @@ include::diff-options.txt[]
        allows for useful naming of a patch series, and can be
        combined with the --numbered option.
 
+--cc=<email>::
+       Add a "Cc:" header to the email headers. This is in addition
+       to any configured headers, and may be used multiple times.
+
+--cover-letter::
+       Generate a cover letter template.  You still have to fill in
+       a description, but the shortlog and the diffstat will be
+       generated for you.
+
 --suffix=.<sfx>::
        Instead of using `.patch` as the suffix for generated
        filenames, use specified suffix.  A common alternative is
index f3cb24f..71a7335 100644 (file)
@@ -75,9 +75,11 @@ OPTIONS
 -n::
        Prefix the line number to matching lines.
 
--l | --files-with-matches | -L | --files-without-match::
+-l | --files-with-matches | --name-only | -L | --files-without-match::
        Instead of showing every matched line, show only the
        names of files that contain (or do not contain) matches.
+       For better compatability with git-diff, --name-only is a
+       synonym for --files-with-matches.
 
 -c | --count::
        Instead of showing every matched line, show the number of
index 09904c7..0926dc1 100644 (file)
@@ -47,17 +47,9 @@ OPTIONS
 +
 The web browser can be specified using the configuration variable
 'help.browser', or 'web.browser' if the former is not set. If none of
-these config variables is set, the 'git-help--browse' helper script
-(called by 'git-help') will pick a suitable default.
-+
-You can explicitly provide a full path to your preferred browser by
-setting the configuration variable 'browser.<tool>.path'. For example,
-you can configure the absolute path to firefox by setting
-'browser.firefox.path'. Otherwise, 'git-help--browse' assumes the tool
-is available in PATH.
-+
-Note that the script tries, as much as possible, to display the HTML
-page in a new tab on an already opened browser.
+these config variables is set, the 'git-web--browse' helper script
+(called by 'git-help') will pick a suitable default. See
+linkgit:git-web--browse[1] for more information about this.
 
 CONFIGURATION VARIABLES
 -----------------------
@@ -74,7 +66,7 @@ line option:
 The 'help.browser', 'web.browser' and 'browser.<tool>.path' will also
 be checked if the 'web' format is chosen (either by command line
 option or configuration variable). See '-w|--web' in the OPTIONS
-section above.
+section above and linkgit:git-web--browse[1].
 
 Note that these configuration variables should probably be set using
 the '--global' flag, for example like this:
index cca77f1..0b82722 100644 (file)
@@ -15,6 +15,9 @@ DESCRIPTION
 Sends missing objects to remote repository, and updates the
 remote branch.
 
+*NOTE*: This command is temporarily disabled if your cURL
+library is older than 7.16, as the combination has been reported
+not to work and sometimes corrupts repository.
 
 OPTIONS
 -------
index 841e8fa..51f1532 100644 (file)
@@ -38,10 +38,11 @@ OPTIONS
        The port number to bind the httpd to.  (Default: 1234)
 
 -b|--browser::
-
-       The web browser command-line to execute to view the gitweb page.
-       If blank, the URL of the gitweb instance will be printed to
-       stdout.  (Default: 'firefox')
+       The web browser that should be used to view the gitweb
+       page. This will be passed to the 'git-web--browse' helper
+       script along with the URL of the gitweb instance. See
+       linkgit:git-web--browse[1] for more information about this. If
+       the script fails, the URL will be printed to stdout.
 
 --start::
        Start the httpd instance and exit.  This does not generate
@@ -72,7 +73,8 @@ You may specify configuration in your .git/config
 -----------------------------------------------------------------------
 
 If the configuration variable 'instaweb.browser' is not set,
-'web.browser' will be used instead if it is defined.
+'web.browser' will be used instead if it is defined. See
+linkgit:git-web--browse[1] for more information about this.
 
 Author
 ------
index 5d816d0..19ee017 100644 (file)
@@ -8,7 +8,7 @@ git-merge-index - Run a merge for files needing merging
 
 SYNOPSIS
 --------
-'git-merge-index' [-o] [-q] <merge-program> (-a | \-- | <file>\*)
+'git-merge-index' [-o] [-q] <merge-program> (-a | [--] <file>\*)
 
 DESCRIPTION
 -----------
index 74cc7c1..5c1bd3b 100644 (file)
@@ -99,7 +99,8 @@ base-name::
 --max-pack-size=<n>::
        Maximum size of each output packfile, expressed in MiB.
        If specified,  multiple packfiles may be created.
-       The default is unlimited.
+       The default is unlimited, unless the config variable
+       `pack.packSizeLimit` is set.
 
 --incremental::
        This flag causes an object already in a pack ignored
@@ -176,6 +177,8 @@ base-name::
        This is meant to reduce packing time on multiprocessor machines.
        The required amount of memory for the delta search window is
        however multiplied by the number of threads.
+       Specifying 0 will cause git to auto-detect the number of CPU's
+       and set the number of threads accordingly.
 
 --index-version=<version>[,<offset>]::
        This is intended to be used by the test suite only. It allows
index 77fdaf1..7378943 100644 (file)
@@ -15,6 +15,7 @@ DESCRIPTION
 -----------
 Runs `git-fetch` with the given parameters, and calls `git-merge`
 to merge the retrieved head(s) into the current branch.
+With `--rebase`, calls `git-rebase` instead of `git-merge`.
 
 Note that you can use `.` (current directory) as the
 <repository> to pull from the local repository -- this is useful
@@ -25,6 +26,24 @@ OPTIONS
 -------
 include::merge-options.txt[]
 
+:git-pull: 1
+
+\--rebase::
+       Instead of a merge, perform a rebase after fetching.  If
+       there is a remote ref for the upstream branch, and this branch
+       was rebased since last fetched, the rebase uses that information
+       to avoid rebasing non-local changes. To make this the default
+       for branch `<name>`, set configuration `branch.<name>.rebase`
+       to `true`.
++
+*NOTE:* This is a potentially _dangerous_ mode of operation.
+It rewrites history, which does not bode well when you
+published that history already.  Do *not* use this option
+unless you have read linkgit:git-rebase[1] carefully.
+
+\--no-rebase::
+       Override earlier \--rebase.
+
 include::fetch-options.txt[]
 
 include::pull-fetch-param.txt[]
@@ -33,16 +52,6 @@ include::urls-remotes.txt[]
 
 include::merge-strategies.txt[]
 
-\--rebase::
-       Instead of a merge, perform a rebase after fetching.
-       *NOTE:* This is a potentially _dangerous_ mode of operation.
-       It rewrites history, which does not bode well when you
-       published that history already.  Do *not* use this option
-       unless you have read linkgit:git-rebase[1] carefully.
-
-\--no-rebase::
-       Override earlier \--rebase.
-
 DEFAULT BEHAVIOUR
 -----------------
 
index 5f24944..3128170 100644 (file)
@@ -47,9 +47,9 @@ even if it does not result in a fast forward update.
 +
 Note: If no explicit refspec is found, (that is neither
 on the command line nor in any Push line of the
-corresponding remotes file---see below), then all the
-heads that exist both on the local side and on the remote
-side are updated.
+corresponding remotes file---see below), then "matching" heads are
+pushed: for every head that exists on the local side, the remote side is
+updated if a head of the same name already exists on the remote side.
 +
 `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
 +
@@ -108,6 +108,55 @@ the remote repository.
 
 include::urls-remotes.txt[]
 
+OUTPUT
+------
+
+The output of "git push" depends on the transport method used; this
+section describes the output when pushing over the git protocol (either
+locally or via ssh).
+
+The status of the push is output in tabular form, with each line
+representing the status of a single ref. Each line is of the form:
+
+-------------------------------
+ <flag> <summary> <from> -> <to> (<reason>)
+-------------------------------
+
+flag::
+       A single character indicating the status of the ref. This is
+       blank for a successfully pushed ref, `!` for a ref that was
+       rejected or failed to push, and '=' for a ref that was up to
+       date and did not need pushing (note that the status of up to
+       date refs is shown only when `git push` is running verbosely).
+
+summary::
+       For a successfully pushed ref, the summary shows the old and new
+       values of the ref in a form suitable for using as an argument to
+       `git log` (this is `<old>..<new>` in most cases, and
+       `<old>...<new>` for forced non-fast forward updates). For a
+       failed update, more details are given for the failure.
+       The string `rejected` indicates that git did not try to send the
+       ref at all (typically because it is not a fast forward). The
+       string `remote rejected` indicates that the remote end refused
+       the update; this rejection is typically caused by a hook on the
+       remote side. The string `remote failure` indicates that the
+       remote end did not report the successful update of the ref
+       (perhaps because of a temporary error on the remote side, a
+       break in the network connection, or other transient error).
+
+from::
+       The name of the local ref being pushed, minus its
+       `refs/<type>/` prefix. In the case of deletion, the
+       name of the local ref is omitted.
+
+to::
+       The name of the remote ref being updated, minus its
+       `refs/<type>/` prefix.
+
+reason::
+       A human-readable explanation. In the case of successfully pushed
+       refs, no explanation is needed. For a failed ref, the reason for
+       failure is described.
 
 Examples
 --------
index 10f6fa5..2cbd1f7 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git-remote'
-'git-remote' add [-t <branch>] [-m <branch>] [-f] [--mirror] <name> <url>
+'git-remote' add [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>
 'git-remote' rm <name>
 'git-remote' show <name>
 'git-remote' prune <name>
index a4e0a77..fac59c9 100644 (file)
@@ -8,8 +8,8 @@ git-reset - Reset current HEAD to the specified state
 SYNOPSIS
 --------
 [verse]
-'git-reset' [--mixed | --soft | --hard] [-q] [<commit>]
-'git-reset' [--mixed] [-q] [<commit>] [--] <paths>...
+'git reset' [--mixed | --soft | --hard] [-q] [<commit>]
+'git reset' [-q] [<commit>] [--] <paths>...
 
 DESCRIPTION
 -----------
@@ -37,7 +37,7 @@ OPTIONS
 --soft::
        Does not touch the index file nor the working tree at all, but
        requires them to be in a good order. This leaves all your changed
-       files "Added but not yet committed", as linkgit:git-status[1] would
+       files "Changes to be committed", as linkgit:git-status[1] would
        put it.
 
 --hard::
@@ -176,6 +176,23 @@ $ git reset                                       <3>
     committed as 'snapshot WIP'.  This updates the index to show your
     WIP files as uncommitted.
 
+Reset a single file in the index::
++
+Suppose you have added a file to your index, but later decide you do not
+want to add it to your commit. You can remove the file from the index
+while keeping your changes with git reset.
++
+------------
+$ git reset -- frotz.c                      <1>
+$ git commit -m "Commit files in index"     <2>
+$ git add frotz.c                           <3>
+------------
++
+<1> This removes the file from the index while keeping it in the working
+    directory.
+<2> This commits all other changes in the index.
+<3> Adds the file to the index again.
+
 Author
 ------
 Written by Junio C Hamano <junkio@cox.net> and Linus Torvalds <torvalds@osdl.org>
index 5b96eab..a8d489f 100644 (file)
@@ -31,6 +31,7 @@ SYNOPSIS
             [ \--(author|committer|grep)=<pattern> ]
             [ \--regexp-ignore-case | \-i ]
             [ \--extended-regexp | \-E ]
+            [ \--fixed-strings | \-F ]
             [ \--date={local|relative|default|iso|rfc|short} ]
             [ [\--objects | \--objects-edge] [ \--unpacked ] ]
             [ \--pretty | \--header ]
index 5d9c369..f02f6bb 100644 (file)
@@ -229,13 +229,13 @@ blobs contained in a commit.
 * A colon, optionally followed by a stage number (0 to 3) and a
   colon, followed by a path; this names a blob object in the
   index at the given path.  Missing stage number (and the colon
-  that follows it) names an stage 0 entry. During a merge, stage
+  that follows it) names a stage 0 entry. During a merge, stage
   1 is the common ancestor, stage 2 is the target branch's version
   (typically the current branch), and stage 3 is the version from
   the branch being merged.
 
-Here is an illustration, by Jon Loeliger.  Both node B and C are
-a commit parents of commit node A.  Parent commits are ordered
+Here is an illustration, by Jon Loeliger.  Both commit nodes B
+and C are parents of commit node A.  Parent commits are ordered
 left-to-right.
 
     G   H   I   J
@@ -291,7 +291,7 @@ and its parent commits exists.  `r1{caret}@` notation means all
 parents of `r1`.  `r1{caret}!` includes commit `r1` but excludes
 its all parents.
 
-Here are a handful examples:
+Here are a handful of examples:
 
    D                G H D
    D F              G H I J D F
index 4b8ec8a..336d797 100644 (file)
@@ -96,11 +96,40 @@ The --cc option must be repeated for each user you want on the cc list.
        servers typically listen to smtp port 25 and ssmtp port
        465).
 
---smtp-user, --smtp-pass::
-       Username and password for SMTP-AUTH. Defaults are the values of
-       the configuration values 'sendemail.smtpuser' and
-       'sendemail.smtppass', but see also 'sendemail.identity'.
-       If not set, authentication is not attempted.
+--smtp-user::
+       Username for SMTP-AUTH. In place of this option, the following
+       configuration variables can be specified:
++
+--
+               * sendemail.smtpuser
+               * sendemail.<identity>.smtpuser (see sendemail.identity).
+--
++
+However, --smtp-user always overrides these variables.
++
+If a username is not specified (with --smtp-user or a
+configuration variable), then authentication is not attempted.
+
+--smtp-pass::
+       Password for SMTP-AUTH. The argument is optional: If no
+       argument is specified, then the empty string is used as
+       the password.
++
+In place of this option, the following configuration variables
+can be specified:
++
+--
+               * sendemail.smtppass
+               * sendemail.<identity>.smtppass (see sendemail.identity).
+--
++
+However, --smtp-pass always overrides these variables.
++
+Furthermore, passwords need not be specified in configuration files
+or on the command line. If a username has been specified (with
+--smtp-user or a configuration variable), but no password has been
+specified (with --smtp-pass or a configuration variable), then the
+user is prompted for a password while the input is masked for privacy.
 
 --smtp-ssl::
        If set, connects to the SMTP server using SSL.
@@ -117,6 +146,17 @@ The --cc option must be repeated for each user you want on the cc list.
         Default is the value of 'sendemail.suppressfrom' configuration value;
         if that is unspecified, default to --no-suppress-from.
 
+--suppress-cc::
+       Specify an additional category of recipients to suppress the
+       auto-cc of.  'self' will avoid including the sender, 'author' will
+       avoid including the patch author, 'cc' will avoid including anyone
+       mentioned in Cc lines in the patch, 'sob' will avoid including
+       anyone mentioned in Signed-off-by lines, and 'cccmd' will avoid
+       running the --cc-cmd.  'all' will suppress all auto cc values.
+       Default is the value of 'sendemail.suppresscc' configuration value;
+       if that is unspecified, default to 'self' if --suppress-from is
+       specified, as well as 'sob' if --no-signed-off-cc is specified.
+
 --thread, --no-thread::
        If this is set, the In-Reply-To header will be set on each email sent.
        If disabled with "--no-thread", no emails will have the In-Reply-To
@@ -137,6 +177,8 @@ The --cc option must be repeated for each user you want on the cc list.
        Specify the primary recipient of the emails generated.
        Generally, this will be the upstream maintainer of the
        project involved.
+       Default is the value of the 'sendemail.to' configuration value;
+       if that is unspecified, this will be prompted for.
 +
 The --to option must be repeated for each user you want on the to list.
 
index 9889806..48e6f5a 100644 (file)
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git-stash' (list | show [<stash>] | apply [<stash>] | clear)
-'git-stash' [save] [message...]
+'git-stash' [save [<message>]]
 
 DESCRIPTION
 -----------
@@ -36,13 +36,14 @@ is also possible).
 OPTIONS
 -------
 
-save::
+save [<message>]::
 
        Save your local modifications to a new 'stash', and run `git-reset
        --hard` to revert them.  This is the default action when no
-       subcommand is given.
+       subcommand is given. The <message> part is optional and gives
+       the description along with the stashed state.
 
-list::
+list [<options>]::
 
        List the stashes that you currently have.  Each 'stash' is listed
        with its name (e.g. `stash@\{0}` is the latest stash, `stash@\{1}` is
@@ -54,6 +55,9 @@ list::
 stash@{0}: WIP on submit: 6ebd0e2... Update git-stash documentation
 stash@{1}: On master: 9cc0589... Add git-stash
 ----------------------------------------------------------------
++
+The command takes options applicable to the linkgit:git-log[1]
+command to control what is shown and how.
 
 show [<stash>]::
 
index cffc6d4..e818e6e 100644 (file)
@@ -9,8 +9,9 @@ git-submodule - Initialize, update or inspect submodules
 SYNOPSIS
 --------
 [verse]
-'git-submodule' [--quiet] [-b branch] add <repository> [<path>]
-'git-submodule' [--quiet] [--cached] [status|init|update] [--] [<path>...]
+'git-submodule' [--quiet] add [-b branch] [--] <repository> [<path>]
+'git-submodule' [--quiet] status [--cached] [--] [<path>...]
+'git-submodule' [--quiet] [init|update] [--] [<path>...]
 
 
 COMMANDS
index e1a1d46..340f1be 100644 (file)
@@ -161,6 +161,13 @@ New features:
 +
 Any other arguments are passed directly to `git log'
 
+'blame'::
+       Show what revision and author last modified each line of a file. This is
+       identical to `git blame', but SVN revision numbers are shown instead of git
+       commit hashes.
++
+All arguments are passed directly to `git blame'.
+
 --
 'find-rev'::
        When given an SVN revision number of the form 'rN', returns the
@@ -456,10 +463,13 @@ have each person clone that repository with 'git clone':
 ------------------------------------------------------------------------
 # Do the initial import on a server
        ssh server "cd /pub && git-svn clone http://svn.foo.org/project
-# Clone locally
-       git clone server:/pub/project
-# Tell git-svn which branch contains the Subversion commits
-       git update-ref refs/remotes/git-svn origin/master
+# Clone locally - make sure the refs/remotes/ space matches the server
+       mkdir project
+       cd project
+       git-init
+       git remote add origin server:/pub/project
+       git config --add remote.origin.fetch=+refs/remotes/*:refs/remotes/*
+       git fetch
 # Initialize git-svn locally (be sure to use the same URL and -T/-b/-t options as were used on server)
        git-svn init http://svn.foo.org/project
 # Pull the latest changes from Subversion
diff --git a/Documentation/git-web--browse.txt b/Documentation/git-web--browse.txt
new file mode 100644 (file)
index 0000000..df57d01
--- /dev/null
@@ -0,0 +1,78 @@
+git-web--browse(1)
+==================
+
+NAME
+----
+git-web--browse - git helper script to launch a web browser
+
+SYNOPSIS
+--------
+'git-web--browse' [OPTIONS] URL/FILE ...
+
+DESCRIPTION
+-----------
+
+This script tries, as much as possible, to display the URLs and FILEs
+that are passed as arguments, as HTML pages in new tabs on an already
+opened web browser.
+
+The following browsers (or commands) are currently supported:
+
+* firefox (this is the default under X Window when not using KDE)
+* iceweasel
+* konqueror (this is the default under KDE)
+* w3m (this is the default outside graphical environments)
+* links
+* lynx
+* dillo
+* open (this is the default under Mac OS X GUI)
+
+OPTIONS
+-------
+-b BROWSER|--browser=BROWSER::
+       Use the specified BROWSER. It must be in the list of supported
+       browsers.
+
+-t BROWSER|--tool=BROWSER::
+       Same as above.
+
+-c CONF.VAR|--config=CONF.VAR::
+       CONF.VAR is looked up in the git config files. If it's set,
+       then its value specify the browser that should be used.
+
+CONFIGURATION VARIABLES
+-----------------------
+
+The web browser can be specified using a configuration variable passed
+with the -c (or --config) command line option, or the 'web.browser'
+configuration variable if the former is not used.
+
+You can explicitly provide a full path to your preferred browser by
+setting the configuration variable 'browser.<tool>.path'. For example,
+you can configure the absolute path to firefox by setting
+'browser.firefox.path'. Otherwise, 'git-web--browse' assumes the tool
+is available in PATH.
+
+Note that these configuration variables should probably be set using
+the '--global' flag, for example like this:
+
+------------------------------------------------
+$ git config --global web.browser firefox
+------------------------------------------------
+
+as they are probably more user specific than repository specific.
+See linkgit:git-config[1] for more information about this.
+
+Author
+------
+Written by Christian Couder <chriscool@tuxfamily.org> and the git-list
+<git@vger.kernel.org>, based on git-mergetool by Theodore Y. Ts'o.
+
+Documentation
+-------------
+Documentation by Christian Couder <chriscool@tuxfamily.org> and the
+git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the linkgit:git[7] suite
index 4ece40d..741ae0e 100644 (file)
@@ -43,6 +43,14 @@ unreleased) version of git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
+* link:v1.5.4.3/git.html[documentation for release 1.5.4.3]
+
+* release notes for
+  link:RelNotes-1.5.4.3.txt[1.5.4.3],
+  link:RelNotes-1.5.4.2.txt[1.5.4.2],
+  link:RelNotes-1.5.4.1.txt[1.5.4.1],
+  link:RelNotes-1.5.4.txt[1.5.4].
+
 * link:v1.5.3.8/git.html[documentation for release 1.5.3.8]
 
 * release notes for
index 35a29fd..84ec962 100644 (file)
@@ -133,6 +133,26 @@ When `core.autocrlf` is set to "input", line endings are
 converted to LF upon checkin, but there is no conversion done
 upon checkout.
 
+If `core.safecrlf` is set to "true" or "warn", git verifies if
+the conversion is reversible for the current setting of
+`core.autocrlf`.  For "true", git rejects irreversible
+conversions; for "warn", git only prints a warning but accepts
+an irreversible conversion.  The safety triggers to prevent such
+a conversion done to the files in the work tree, but there are a
+few exceptions.  Even though...
+
+- "git add" itself does not touch the files in the work tree, the
+  next checkout would, so the safety triggers;
+
+- "git apply" to update a text file with a patch does touch the files
+  in the work tree, but the operation is about text files and CRLF
+  conversion is about fixing the line ending inconsistencies, so the
+  safety does not trigger;
+
+- "git diff" itself does not touch the files in the work tree, it is
+  often run to inspect the changes you intend to next "git add".  To
+  catch potential problems early, safety triggers.
+
 
 `ident`
 ^^^^^^^
index 08373f5..e847b3b 100644 (file)
@@ -57,6 +57,13 @@ Patterns have the following format:
    included again.  If a negated pattern matches, this will
    override lower precedence patterns sources.
 
+ - If the pattern ends with a slash, it is removed for the
+   purpose of the following description, but it would only find
+   a match with a directory.  In other words, `foo/` will match a
+   directory `foo` and paths underneath it, but will not match a
+   regular file or a symbolic link `foo` (this is consistent
+   with the way how pathspec works in general in git).
+
  - If the pattern does not contain a slash '/', git treats it as
    a shell glob pattern and checks for a match against the
    pathname without leading directories.
index f110162..76b8d77 100644 (file)
@@ -61,6 +61,35 @@ The default 'pre-commit' hook, when enabled, catches introduction
 of lines with trailing whitespaces and aborts the commit when
 such a line is found.
 
+All the `git-commit` hooks are invoked with the environment
+variable `GIT_EDITOR=:` if the command will not bring up an editor
+to modify the commit message.
+
+prepare-commit-msg
+------------------
+
+This hook is invoked by `git-commit` right after preparing the
+default log message, and before the editor is started.
+
+It takes one to three parameters.  The first is the name of the file
+that the commit log message.  The second is the source of the commit
+message, and can be: `message` (if a `\-m` or `\-F` option was
+given); `template` (if a `\-t` option was given or the
+configuration option `commit.template` is set); `merge` (if the
+commit is a merge or a `.git/MERGE_MSG` file exists); `squash`
+(if a `.git/SQUASH_MSG` file exists); or `commit`, followed by
+a commit SHA1 (if a `\-c`, `\-C` or `\--amend` option was given).
+
+If the exit status is non-zero, `git-commit` will abort.
+
+The purpose of the hook is to edit the message file in place, and
+it is not suppressed by the `\--no-verify` option.  A non-zero exit
+means a failure of the hook and aborts the commit.  It should not
+be used as replacement for pre-commit hook.
+
+The sample `prepare-commit-msg` hook that comes with git comments
+out the `Conflicts:` part of a merge's commit message.
+
 commit-msg
 ----------
 
diff --git a/Documentation/manpage-1.72.xsl b/Documentation/manpage-1.72.xsl
new file mode 100644 (file)
index 0000000..fe3cd72
--- /dev/null
@@ -0,0 +1,17 @@
+<!-- callout.xsl: converts asciidoc callouts to man page format -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+<xsl:template match="co">
+       <xsl:value-of select="concat('&#x2593;fB(',substring-after(@id,'-'),')&#x2593;fR')"/>
+</xsl:template>
+<xsl:template match="calloutlist">
+       <xsl:text>&#x2302;sp&#10;</xsl:text>
+       <xsl:apply-templates/>
+       <xsl:text>&#10;</xsl:text>
+</xsl:template>
+<xsl:template match="callout">
+       <xsl:value-of select="concat('&#x2593;fB',substring-after(@arearefs,'-'),'. &#x2593;fR')"/>
+       <xsl:apply-templates/>
+       <xsl:text>&#x2302;br&#10;</xsl:text>
+</xsl:template>
+
+</xsl:stylesheet>
index a8138e2..259072c 100644 (file)
@@ -153,6 +153,11 @@ limiting may be applied.
        Consider the limiting patterns to be extended regular expressions
        instead of the default basic regular expressions.
 
+-F, --fixed-strings::
+
+       Consider the limiting patterns to be fixed strings (don't interpret
+       pattern as a regular expression).
+
 --remove-empty::
 
        Stop when a given path disappears from the tree.
diff --git a/Documentation/technical/api-remote.txt b/Documentation/technical/api-remote.txt
new file mode 100644 (file)
index 0000000..073b22b
--- /dev/null
@@ -0,0 +1,123 @@
+Remotes configuration API
+=========================
+
+The API in remote.h gives access to the configuration related to
+remotes. It handles all three configuration mechanisms historically
+and currently used by git, and presents the information in a uniform
+fashion. Note that the code also handles plain URLs without any
+configuration, giving them just the default information.
+
+struct remote
+-------------
+
+`name`::
+
+       The user's nickname for the remote
+
+`url`::
+
+       An array of all of the url_nr URLs configured for the remote
+
+`push`::
+
+        An array of refspecs configured for pushing, with
+        push_refspec being the literal strings, and push_refspec_nr
+        being the quantity.
+
+`fetch`::
+
+       An array of refspecs configured for fetching, with
+       fetch_refspec being the literal strings, and fetch_refspec_nr
+       being the quantity.
+
+`fetch_tags`::
+
+       The setting for whether to fetch tags (as a separate rule from
+       the configured refspecs); -1 means never to fetch tags, 0
+       means to auto-follow tags based on the default heuristic, 1
+       means to always auto-follow tags, and 2 means to fetch all
+       tags.
+
+`receivepack`, `uploadpack`::
+
+       The configured helper programs to run on the remote side, for
+       git-native protocols.
+
+`http_proxy`::
+
+       The proxy to use for curl (http, https, ftp, etc.) URLs.
+
+struct remotes can be found by name with remote_get(), and iterated
+through with for_each_remote(). remote_get(NULL) will return the
+default remote, given the current branch and configuration.
+
+struct refspec
+--------------
+
+A struct refspec holds the parsed interpretation of a refspec. If it
+will force updates (starts with a '+'), force is true. If it is a
+pattern (sides end with '*') pattern is true. src and dest are the two
+sides (if a pattern, only the part outside of the wildcards); if there
+is only one side, it is src, and dst is NULL; if sides exist but are
+empty (i.e., the refspec either starts or ends with ':'), the
+corresponding side is "".
+
+This parsing can be done to an array of strings to give an array of
+struct refpsecs with parse_ref_spec().
+
+remote_find_tracking(), given a remote and a struct refspec with
+either src or dst filled out, will fill out the other such that the
+result is in the "fetch" specification for the remote (note that this
+evaluates patterns and returns a single result).
+
+struct branch
+-------------
+
+Note that this may end up moving to branch.h
+
+struct branch holds the configuration for a branch. It can be looked
+up with branch_get(name) for "refs/heads/{name}", or with
+branch_get(NULL) for HEAD.
+
+It contains:
+
+`name`::
+
+       The short name of the branch.
+
+`refname`::
+
+       The full path for the branch ref.
+
+`remote_name`::
+
+       The name of the remote listed in the configuration.
+
+`remote`::
+
+       The struct remote for that remote.
+
+`merge_name`::
+
+       An array of the "merge" lines in the configuration.
+
+`merge`::
+
+       An array of the struct refspecs used for the merge lines. That
+       is, merge[i]->dst is a local tracking ref which should be
+       merged into this branch by default.
+
+`merge_nr`::
+
+       The number of merge configurations
+
+branch_has_merge_config() returns true if the given branch has merge
+configuration given.
+
+Other stuff
+-----------
+
+There is other stuff in remote.h that is related, in general, to the
+process of interacting with remotes.
+
+(Daniel Barkalow)
index 19d2f64..dfbf9ac 100644 (file)
 run-command API
 ===============
 
-Talk about <run-command.h>, and things like:
+The run-command API offers a versatile tool to run sub-processes with
+redirected input and output as well as with a modified environment
+and an alternate current directory.
 
-* Environment the command runs with (e.g. GIT_DIR);
-* File descriptors and pipes;
-* Exit status;
+A similar API offers the capability to run a function asynchronously,
+which is primarily used to capture the output that the function
+produces in the caller in order to process it.
 
-(Hannes, Dscho, Shawn)
+
+Functions
+---------
+
+`start_command`::
+
+       Start a sub-process. Takes a pointer to a `struct child_process`
+       that specifies the details and returns pipe FDs (if requested).
+       See below for details.
+
+`finish_command`::
+
+       Wait for the completion of a sub-process that was started with
+       start_command().
+
+`run_command`::
+
+       A convenience function that encapsulates a sequence of
+       start_command() followed by finish_command(). Takes a pointer
+       to a `struct child_process` that specifies the details.
+
+`run_command_v_opt`, `run_command_v_opt_dir`, `run_command_v_opt_cd_env`::
+
+       Convenience functions that encapsulate a sequence of
+       start_command() followed by finish_command(). The argument argv
+       specifies the program and its arguments. The argument opt is zero
+       or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`, or
+       `RUN_COMMAND_STDOUT_TO_STDERR` that correspond to the members
+       .no_stdin, .git_cmd, .stdout_to_stderr of `struct child_process`.
+       The argument dir corresponds the member .dir. The argument env
+       corresponds to the member .env.
+
+`start_async`::
+
+       Run a function asynchronously. Takes a pointer to a `struct
+       async` that specifies the details and returns a pipe FD
+       from which the caller reads. See below for details.
+
+`finish_async`::
+
+       Wait for the completeion of an asynchronous function that was
+       started with start_async().
+
+
+Data structures
+---------------
+
+* `struct child_process`
+
+This describes the arguments, redirections, and environment of a
+command to run in a sub-process.
+
+The caller:
+
+1. allocates and clears (memset(&chld, '0', sizeof(chld));) a
+   struct child_process variable;
+2. initializes the members;
+3. calls start_command();
+4. processes the data;
+5. closes file descriptors (if necessary; see below);
+6. calls finish_command().
+
+The .argv member is set up as an array of string pointers (NULL
+terminated), of which .argv[0] is the program name to run (usually
+without a path). If the command to run is a git command, set argv[0] to
+the command name without the 'git-' prefix and set .git_cmd = 1.
+
+The members .in, .out, .err are used to redirect stdin, stdout,
+stderr as follows:
+
+. Specify 0 to request no special redirection. No new file descriptor
+  is allocated. The child process simply inherits the channel from the
+  parent.
+
+. Specify -1 to have a pipe allocated; start_command() replaces -1
+  by the pipe FD in the following way:
+
+       .in: Returns the writable pipe end into which the caller writes;
+               the readable end of the pipe becomes the child's stdin.
+
+       .out, .err: Returns the readable pipe end from which the caller
+               reads; the writable end of the pipe end becomes child's
+               stdout/stderr.
+
+  The caller of start_command() must close the so returned FDs
+  after it has completed reading from/writing to it!
+
+. Specify a file descriptor > 0 to be used by the child:
+
+       .in: The FD must be readable; it becomes child's stdin.
+       .out: The FD must be writable; it becomes child's stdout.
+       .err > 0 is not supported.
+
+  The specified FD is closed by start_command(), even if it fails to
+  run the sub-process!
+
+. Special forms of redirection are available by setting these members
+  to 1:
+
+       .no_stdin, .no_stdout, .no_stderr: The respective channel is
+               redirected to /dev/null.
+
+       .stdout_to_stderr: stdout of the child is redirected to the
+               parent's stderr (i.e. *not* to what .err or
+               .no_stderr specify).
+
+To modify the environment of the sub-process, specify an array of
+string pointers (NULL terminated) in .env:
+
+. If the string is of the form "VAR=value", i.e. it contains '='
+  the variable is added to the child process's environment.
+
+. If the string does not contain '=', it names an environement
+  variable that will be removed from the child process's envionment.
+
+To specify a new initial working directory for the sub-process,
+specify it in the .dir member.
+
+
+* `struct async`
+
+This describes a function to run asynchronously, whose purpose is
+to produce output that the caller reads.
+
+The caller:
+
+1. allocates and clears (memset(&asy, '0', sizeof(asy));) a
+   struct async variable;
+2. initializes .proc and .data;
+3. calls start_async();
+4. processes the data by reading from the fd in .out;
+5. closes .out;
+6. calls finish_async().
+
+The function pointer in .proc has the following signature:
+
+       int proc(int fd, void *data);
+
+. fd specifies a writable file descriptor to which the function must
+  write the data that it produces. The function *must* close this
+  descriptor before it returns.
+
+. data is the value that the caller has specified in the .data member
+  of struct async.
+
+. The return value of the function is 0 on success and non-zero
+  on failure. If the function indicates failure, finish_async() will
+  report failure as well.
+
+
+There are serious restrictions on what the asynchronous function can do
+because this facility is implemented by a pipe to a forked process on
+UNIX, but by a thread in the same address space on Windows:
+
+. It cannot change the program's state (global variables, environment,
+  etc.) in a way that the caller notices; in other words, .out is the
+  only communication channel to the caller.
+
+. It must not change the program's state that the caller of the
+  facility also uses.
index 5030d9f..6bdf034 100644 (file)
@@ -184,7 +184,7 @@ 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
+index file will be more than one seconds later than 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.
index 81ac17f..fa34c67 100644 (file)
@@ -44,3 +44,26 @@ endif::git-clone[]
 ifdef::git-clone[]
 They are equivalent, except the former implies --local option.
 endif::git-clone[]
+
+
+If there are a large number of similarly-named remote repositories and
+you want to use a different format for them (such that the URLs you
+use will be rewritten into URLs that work), you can create a
+configuration section of the form:
+
+------------
+       [url "<actual url base>"]
+               insteadOf = <other url base>
+------------
+
+For example, with this:
+
+------------
+       [url "git://git.host.xz/"]
+               insteadOf = host.xz:/path/to/
+               insteadOf = work:
+------------
+
+a URL like "work:repo.git" or like "host.xz:/path/to/repo.git" will be
+rewritten in any context that takes a URL to be "git://git.host.xz/repo.git".
+
index 2ba142a..6ddf04d 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.5.4-rc3.GIT
+DEF_VER=v1.5.4.GIT
 
 LF='
 '
@@ -15,7 +15,9 @@ elif test -d .git &&
        VN=$(git describe --abbrev=4 HEAD 2>/dev/null) &&
        case "$VN" in
        *$LF*) (exit 1) ;;
-       v[0-9]*) : happy ;;
+       v[0-9]*)
+               test -z "$(git diff-index --name-only HEAD)" ||
+               VN="$VN-dirty" ;;
        esac
 then
        VN=$(echo "$VN" | sed -e 's/-/./g');
@@ -25,14 +27,6 @@ fi
 
 VN=$(expr "$VN" : v*'\(.*\)')
 
-dirty=$(sh -c 'git diff-index --name-only HEAD' 2>/dev/null) || dirty=
-case "$dirty" in
-'')
-       ;;
-*)
-       VN="$VN-dirty" ;;
-esac
-
 if test -r $GVF
 then
        VC=$(sed -e 's/^GIT_VERSION = //' <$GVF)
diff --git a/INSTALL b/INSTALL
index f1eb404..6f3bcb4 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -79,8 +79,8 @@ Issues of note:
        - "perl" and POSIX-compliant shells are needed to use most of
          the barebone Porcelainish scripts.
 
-       - "cpio" is used by git-merge for saving and restoring the index,
-         and by git-clone when doing a local (possibly hardlinked) clone.
+       - "cpio" is used by git-clone when doing a local (possibly
+         hardlinked) clone.
 
  - Some platform specific issues are dealt with Makefile rules,
    but depending on your specific installation, you may not
index 90c0dd8..a5b6eeb 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,9 @@ all::
 
 # Define V=1 to have a more verbose compile.
 #
+# Define FREAD_READS_DIRECTORIES if your are on a system which succeeds
+# when attempting to read from an fopen'ed directory.
+#
 # Define NO_OPENSSL environment variable if you do not have OpenSSL.
 # This also implies MOZILLA_SHA1.
 #
@@ -42,6 +45,8 @@ all::
 #
 # Define NO_MKDTEMP if you don't have mkdtemp in the C library.
 #
+# Define NO_SYS_SELECT_H if you don't have sys/select.h.
+#
 # Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link.
 # Enable it on Windows.  By default, symrefs are still used.
 #
@@ -135,6 +140,10 @@ all::
 # Define THREADED_DELTA_SEARCH if you have pthreads and wish to exploit
 # parallel delta searching when packing objects.
 #
+# Define INTERNAL_QSORT to use Git's implementation of qsort(), which
+# is a simplified version of the merge sort used in glibc. This is
+# recommended if Git triggers O(n^2) behavior in your platform's qsort().
+#
 
 GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -229,7 +238,7 @@ SCRIPT_SH = \
        git-lost-found.sh git-quiltimport.sh git-submodule.sh \
        git-filter-branch.sh \
        git-stash.sh \
-       git-help--browse.sh
+       git-web--browse.sh
 
 SCRIPT_PERL = \
        git-add--interactive.perl \
@@ -261,17 +270,18 @@ PROGRAMS = \
 # Empty...
 EXTRA_PROGRAMS =
 
+# List built-in command $C whose implementation cmd_$C() is not in
+# builtin-$C.o but is linked in as part of some other command.
 BUILT_INS = \
        git-format-patch$X git-show$X git-whatchanged$X git-cherry$X \
        git-get-tar-commit-id$X git-init$X git-repo-config$X \
        git-fsck-objects$X git-cherry-pick$X git-peek-remote$X git-status$X \
+       git-merge-subtree$X \
        $(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
 
 # what 'all' will build and 'install' will install, in gitexecdir
 ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
 
-ALL_PROGRAMS += git-merge-subtree$X
-
 # what 'all' will build but not install in gitexecdir
 OTHER_PROGRAMS = git$X gitweb/gitweb.cgi
 
@@ -317,7 +327,8 @@ LIB_OBJS = \
        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 shallow.o utf8.o \
        convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \
-       transport.o bundle.o walker.o parse-options.o ws.o archive.o branch.o
+       transport.o bundle.o walker.o parse-options.o ws.o archive.o branch.o \
+       alias.o
 
 BUILTIN_OBJS = \
        builtin-add.o \
@@ -504,6 +515,17 @@ ifeq ($(uname_S),IRIX64)
        # for now, build 32-bit version
        BASIC_LDFLAGS += -L/usr/lib32
 endif
+ifeq ($(uname_S),HP-UX)
+       NO_IPV6=YesPlease
+       NO_SETENV=YesPlease
+       NO_STRCASESTR=YesPlease
+       NO_MEMMEM = YesPlease
+       NO_STRLCPY = YesPlease
+       NO_MKDTEMP = YesPlease
+       NO_UNSETENV = YesPlease
+       NO_HSTRERROR = YesPlease
+       NO_SYS_SELECT_H = YesPlease
+endif
 ifneq (,$(findstring arm,$(uname_M)))
        ARM_SHA1 = YesPlease
 endif
@@ -606,6 +628,10 @@ endif
 ifdef NO_C99_FORMAT
        BASIC_CFLAGS += -DNO_C99_FORMAT
 endif
+ifdef FREAD_READS_DIRECTORIES
+       COMPAT_CFLAGS += -DFREAD_READS_DIRECTORIES
+       COMPAT_OBJS += compat/fopen.o
+endif
 ifdef NO_SYMLINK_HEAD
        BASIC_CFLAGS += -DNO_SYMLINK_HEAD
 endif
@@ -636,6 +662,9 @@ ifdef NO_UNSETENV
        COMPAT_CFLAGS += -DNO_UNSETENV
        COMPAT_OBJS += compat/unsetenv.o
 endif
+ifdef NO_SYS_SELECT_H
+       BASIC_CFLAGS += -DNO_SYS_SELECT_H
+endif
 ifdef NO_MMAP
        COMPAT_CFLAGS += -DNO_MMAP
        COMPAT_OBJS += compat/mmap.o
@@ -707,10 +736,15 @@ ifdef NO_MEMMEM
        COMPAT_CFLAGS += -DNO_MEMMEM
        COMPAT_OBJS += compat/memmem.o
 endif
+ifdef INTERNAL_QSORT
+       COMPAT_CFLAGS += -DINTERNAL_QSORT
+       COMPAT_OBJS += compat/qsort.o
+endif
 
 ifdef THREADED_DELTA_SEARCH
        BASIC_CFLAGS += -DTHREADED_DELTA_SEARCH
        EXTLIBS += -lpthread
+       LIB_OBJS += thread-utils.o
 endif
 
 ifeq ($(TCLTK_PATH),)
@@ -804,12 +838,10 @@ git$X: git.o $(BUILTIN_OBJS) $(GITLIBS)
 
 help.o: help.c common-cmds.h GIT-CFLAGS
        $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
+               '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
                '-DGIT_MAN_PATH="$(mandir_SQ)"' \
                '-DGIT_INFO_PATH="$(infodir_SQ)"' $<
 
-git-merge-subtree$X: git-merge-recursive$X
-       $(QUIET_BUILT_IN)$(RM) $@ && ln git-merge-recursive$X $@
-
 $(BUILT_INS): git$X
        $(QUIET_BUILT_IN)$(RM) $@ && ln git$X $@
 
@@ -824,7 +856,6 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
            -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
            -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
-           -e 's|@@HTMLDIR@@|$(htmldir_SQ)|g' \
            $@.sh >$@+ && \
        chmod +x $@+ && \
        mv $@+ $@
@@ -929,7 +960,7 @@ git-%$X: %.o $(GITLIBS)
 
 git-imap-send$X: imap-send.o $(LIB_FILE)
 
-http.o http-walker.o http-push.o: http.h
+http.o http-walker.o http-push.o transport.o: http.h
 
 git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
@@ -1072,7 +1103,7 @@ git.spec: git.spec.in
        mv $@+ $@
 
 GIT_TARNAME=git-$(GIT_VERSION)
-dist: git.spec git-archive configure
+dist: git.spec git-archive$(X) configure
        ./git-archive --format=tar \
                --prefix=$(GIT_TARNAME)/ HEAD^{tree} > $(GIT_TARNAME).tar
        @mkdir -p $(GIT_TARNAME)
index 46308ce..3e77358 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.5.4.txt
\ No newline at end of file
+Documentation/RelNotes-1.5.5.txt
\ No newline at end of file
diff --git a/alias.c b/alias.c
new file mode 100644 (file)
index 0000000..116cac8
--- /dev/null
+++ b/alias.c
@@ -0,0 +1,22 @@
+#include "cache.h"
+
+static const char *alias_key;
+static char *alias_val;
+static int alias_lookup_cb(const char *k, const char *v)
+{
+       if (!prefixcmp(k, "alias.") && !strcmp(k+6, alias_key)) {
+               if (!v)
+                       return config_error_nonbool(k);
+               alias_val = xstrdup(v);
+               return 0;
+       }
+       return 0;
+}
+
+char *alias_lookup(const char *alias)
+{
+       alias_key = alias;
+       alias_val = NULL;
+       git_config(alias_lookup_cb);
+       return alias_val;
+}
index e1bced5..30aa2e2 100644 (file)
@@ -222,7 +222,7 @@ static void write_global_extended_header(const unsigned char *sha1)
 static int git_tar_config(const char *var, const char *value)
 {
        if (!strcmp(var, "tar.umask")) {
-               if (!strcmp(value, "user")) {
+               if (value && !strcmp(value, "user")) {
                        tar_umask = umask(0);
                        umask(tar_umask);
                } else {
diff --git a/attr.c b/attr.c
index 741db3b..64b77b1 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -406,7 +406,7 @@ static void debug_info(const char *what, struct attr_stack *elem)
 {
        fprintf(stderr, "%s: %s\n", what, elem->origin ? elem->origin : "()");
 }
-static void debug_set(const char *what, const char *match, struct git_attr *attr, void *v)
+static void debug_set(const char *what, const char *match, struct git_attr *attr, const void *v)
 {
        const char *value = v;
 
@@ -543,10 +543,10 @@ static int path_matches(const char *pathname, int pathlen,
        if (*pattern == '/')
                pattern++;
        if (pathlen < baselen ||
-           (baselen && pathname[baselen - 1] != '/') ||
+           (baselen && pathname[baselen] != '/') ||
            strncmp(pathname, base, baselen))
                return 0;
-       return fnmatch(pattern, pathname + baselen, FNM_PATHNAME) == 0;
+       return fnmatch(pattern, pathname + baselen + 1, FNM_PATHNAME) == 0;
 }
 
 static int fill_one(const char *what, struct match_attr *a, int rem)
index 4a91e3e..820110e 100644 (file)
@@ -228,6 +228,18 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                goto finish;
        }
 
+       if (*argv) {
+               /* Was there an invalid path? */
+               if (pathspec) {
+                       int num;
+                       for (num = 0; pathspec[num]; num++)
+                               ; /* just counting */
+                       if (argc != num)
+                               exit(1); /* error message already given */
+               } else
+                       exit(1); /* error message already given */
+       }
+
        fill_directory(&dir, pathspec, ignored_too);
 
        if (show_only) {
index 30d86f2..a3f075d 100644 (file)
@@ -161,6 +161,84 @@ struct patch {
        struct patch *next;
 };
 
+/*
+ * A line in a file, len-bytes long (includes the terminating LF,
+ * except for an incomplete line at the end if the file ends with
+ * one), and its contents hashes to 'hash'.
+ */
+struct line {
+       size_t len;
+       unsigned hash : 24;
+       unsigned flag : 8;
+#define LINE_COMMON     1
+};
+
+/*
+ * This represents a "file", which is an array of "lines".
+ */
+struct image {
+       char *buf;
+       size_t len;
+       size_t nr;
+       size_t alloc;
+       struct line *line_allocated;
+       struct line *line;
+};
+
+static uint32_t hash_line(const char *cp, size_t len)
+{
+       size_t i;
+       uint32_t h;
+       for (i = 0, h = 0; i < len; i++) {
+               if (!isspace(cp[i])) {
+                       h = h * 3 + (cp[i] & 0xff);
+               }
+       }
+       return h;
+}
+
+static void add_line_info(struct image *img, const char *bol, size_t len, unsigned flag)
+{
+       ALLOC_GROW(img->line_allocated, img->nr + 1, img->alloc);
+       img->line_allocated[img->nr].len = len;
+       img->line_allocated[img->nr].hash = hash_line(bol, len);
+       img->line_allocated[img->nr].flag = flag;
+       img->nr++;
+}
+
+static void prepare_image(struct image *image, char *buf, size_t len,
+                         int prepare_linetable)
+{
+       const char *cp, *ep;
+
+       memset(image, 0, sizeof(*image));
+       image->buf = buf;
+       image->len = len;
+
+       if (!prepare_linetable)
+               return;
+
+       ep = image->buf + image->len;
+       cp = image->buf;
+       while (cp < ep) {
+               const char *next;
+               for (next = cp; next < ep && *next != '\n'; next++)
+                       ;
+               if (next < ep)
+                       next++;
+               add_line_info(image, cp, next - cp, 0);
+               cp = next;
+       }
+       image->line = image->line_allocated;
+}
+
+static void clear_image(struct image *image)
+{
+       free(image->buf);
+       image->buf = NULL;
+       image->len = 0;
+}
+
 static void say_patch_name(FILE *output, const char *pre,
                           struct patch *patch, const char *post)
 {
@@ -1430,234 +1508,345 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
        case S_IFREG:
                if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
                        return error("unable to open or read %s", path);
-               convert_to_git(path, buf->buf, buf->len, buf);
+               convert_to_git(path, buf->buf, buf->len, buf, 0);
                return 0;
        default:
                return -1;
        }
 }
 
-static int find_offset(const char *buf, unsigned long size,
-                      const char *fragment, unsigned long fragsize,
-                      int line, int *lines)
+static void update_pre_post_images(struct image *preimage,
+                                  struct image *postimage,
+                                  char *buf,
+                                  size_t len)
 {
-       int i;
-       unsigned long start, backwards, forwards;
+       int i, ctx;
+       char *new, *old, *fixed;
+       struct image fixed_preimage;
 
-       if (fragsize > size)
-               return -1;
+       /*
+        * Update the preimage with whitespace fixes.  Note that we
+        * are not losing preimage->buf -- apply_one_fragment() will
+        * free "oldlines".
+        */
+       prepare_image(&fixed_preimage, buf, len, 1);
+       assert(fixed_preimage.nr == preimage->nr);
+       for (i = 0; i < preimage->nr; i++)
+               fixed_preimage.line[i].flag = preimage->line[i].flag;
+       free(preimage->line_allocated);
+       *preimage = fixed_preimage;
 
-       start = 0;
-       if (line > 1) {
-               unsigned long offset = 0;
-               i = line-1;
-               while (offset + fragsize <= size) {
-                       if (buf[offset++] == '\n') {
-                               start = offset;
-                               if (!--i)
-                                       break;
-                       }
+       /*
+        * Adjust the common context lines in postimage, in place.
+        * This is possible because whitespace fixing does not make
+        * the string grow.
+        */
+       new = old = postimage->buf;
+       fixed = preimage->buf;
+       for (i = ctx = 0; i < postimage->nr; i++) {
+               size_t len = postimage->line[i].len;
+               if (!(postimage->line[i].flag & LINE_COMMON)) {
+                       /* an added line -- no counterparts in preimage */
+                       memmove(new, old, len);
+                       old += len;
+                       new += len;
+                       continue;
                }
+
+               /* a common context -- skip it in the original postimage */
+               old += len;
+
+               /* and find the corresponding one in the fixed preimage */
+               while (ctx < preimage->nr &&
+                      !(preimage->line[ctx].flag & LINE_COMMON)) {
+                       fixed += preimage->line[ctx].len;
+                       ctx++;
+               }
+               if (preimage->nr <= ctx)
+                       die("oops");
+
+               /* and copy it in, while fixing the line length */
+               len = preimage->line[ctx].len;
+               memcpy(new, fixed, len);
+               new += len;
+               fixed += len;
+               postimage->line[i].len = len;
+               ctx++;
        }
 
-       /* Exact line number? */
-       if ((start + fragsize <= size) &&
-           !memcmp(buf + start, fragment, fragsize))
-               return start;
+       /* Fix the length of the whole thing */
+       postimage->len = new - postimage->buf;
+}
+
+static int match_fragment(struct image *img,
+                         struct image *preimage,
+                         struct image *postimage,
+                         unsigned long try,
+                         int try_lno,
+                         unsigned ws_rule,
+                         int match_beginning, int match_end)
+{
+       int i;
+       char *fixed_buf, *buf, *orig, *target;
+
+       if (preimage->nr + try_lno > img->nr)
+               return 0;
+
+       if (match_beginning && try_lno)
+               return 0;
+
+       if (match_end && preimage->nr + try_lno != img->nr)
+               return 0;
+
+       /* Quick hash check */
+       for (i = 0; i < preimage->nr; i++)
+               if (preimage->line[i].hash != img->line[try_lno + i].hash)
+                       return 0;
+
+       /*
+        * Do we have an exact match?  If we were told to match
+        * at the end, size must be exactly at try+fragsize,
+        * otherwise try+fragsize must be still within the preimage,
+        * and either case, the old piece should match the preimage
+        * exactly.
+        */
+       if ((match_end
+            ? (try + preimage->len == img->len)
+            : (try + preimage->len <= img->len)) &&
+           !memcmp(img->buf + try, preimage->buf, preimage->len))
+               return 1;
+
+       if (ws_error_action != correct_ws_error)
+               return 0;
+
+       /*
+        * The hunk does not apply byte-by-byte, but the hash says
+        * it might with whitespace fuzz.
+        */
+       fixed_buf = xmalloc(preimage->len + 1);
+       buf = fixed_buf;
+       orig = preimage->buf;
+       target = img->buf + try;
+       for (i = 0; i < preimage->nr; i++) {
+               size_t fixlen; /* length after fixing the preimage */
+               size_t oldlen = preimage->line[i].len;
+               size_t tgtlen = img->line[try_lno + i].len;
+               size_t tgtfixlen; /* length after fixing the target line */
+               char tgtfixbuf[1024], *tgtfix;
+               int match;
+
+               /* Try fixing the line in the preimage */
+               fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL);
+
+               /* Try fixing the line in the target */
+               if (sizeof(tgtfixbuf) < tgtlen)
+                       tgtfix = tgtfixbuf;
+               else
+                       tgtfix = xmalloc(tgtlen);
+               tgtfixlen = ws_fix_copy(tgtfix, target, tgtlen, ws_rule, NULL);
+
+               /*
+                * If they match, either the preimage was based on
+                * a version before our tree fixed whitespace breakage,
+                * or we are lacking a whitespace-fix patch the tree
+                * the preimage was based on already had (i.e. target
+                * has whitespace breakage, the preimage doesn't).
+                * In either case, we are fixing the whitespace breakages
+                * so we might as well take the fix together with their
+                * real change.
+                */
+               match = (tgtfixlen == fixlen && !memcmp(tgtfix, buf, fixlen));
+
+               if (tgtfix != tgtfixbuf)
+                       free(tgtfix);
+               if (!match)
+                       goto unmatch_exit;
+
+               orig += oldlen;
+               buf += fixlen;
+               target += tgtlen;
+       }
+
+       /*
+        * Yes, the preimage is based on an older version that still
+        * has whitespace breakages unfixed, and fixing them makes the
+        * hunk match.  Update the context lines in the postimage.
+        */
+       update_pre_post_images(preimage, postimage,
+                              fixed_buf, buf - fixed_buf);
+       return 1;
+
+ unmatch_exit:
+       free(fixed_buf);
+       return 0;
+}
+
+static int find_pos(struct image *img,
+                   struct image *preimage,
+                   struct image *postimage,
+                   int line,
+                   unsigned ws_rule,
+                   int match_beginning, int match_end)
+{
+       int i;
+       unsigned long backwards, forwards, try;
+       int backwards_lno, forwards_lno, try_lno;
+
+       if (preimage->nr > img->nr)
+               return -1;
+
+       /*
+        * If match_begining or match_end is specified, there is no
+        * point starting from a wrong line that will never match and
+        * wander around and wait for a match at the specified end.
+        */
+       if (match_beginning)
+               line = 0;
+       else if (match_end)
+               line = img->nr - preimage->nr;
+
+       if (line > img->nr)
+               line = img->nr;
+
+       try = 0;
+       for (i = 0; i < line; i++)
+               try += img->line[i].len;
 
        /*
         * There's probably some smart way to do this, but I'll leave
         * that to the smart and beautiful people. I'm simple and stupid.
         */
-       backwards = start;
-       forwards = start;
+       backwards = try;
+       backwards_lno = line;
+       forwards = try;
+       forwards_lno = line;
+       try_lno = line;
+
        for (i = 0; ; i++) {
-               unsigned long try;
-               int n;
+               if (match_fragment(img, preimage, postimage,
+                                  try, try_lno, ws_rule,
+                                  match_beginning, match_end))
+                       return try_lno;
+
+       again:
+               if (backwards_lno == 0 && forwards_lno == img->nr)
+                       break;
 
-               /* "backward" */
                if (i & 1) {
-                       if (!backwards) {
-                               if (forwards + fragsize > size)
-                                       break;
-                               continue;
+                       if (backwards_lno == 0) {
+                               i++;
+                               goto again;
                        }
-                       do {
-                               --backwards;
-                       } while (backwards && buf[backwards-1] != '\n');
+                       backwards_lno--;
+                       backwards -= img->line[backwards_lno].len;
                        try = backwards;
+                       try_lno = backwards_lno;
                } else {
-                       while (forwards + fragsize <= size) {
-                               if (buf[forwards++] == '\n')
-                                       break;
+                       if (forwards_lno == img->nr) {
+                               i++;
+                               goto again;
                        }
+                       forwards += img->line[forwards_lno].len;
+                       forwards_lno++;
                        try = forwards;
+                       try_lno = forwards_lno;
                }
 
-               if (try + fragsize > size)
-                       continue;
-               if (memcmp(buf + try, fragment, fragsize))
-                       continue;
-               n = (i >> 1)+1;
-               if (i & 1)
-                       n = -n;
-               *lines = n;
-               return try;
        }
-
-       /*
-        * We should start searching forward and backward.
-        */
        return -1;
 }
 
-static void remove_first_line(const char **rbuf, int *rsize)
+static void remove_first_line(struct image *img)
 {
-       const char *buf = *rbuf;
-       int size = *rsize;
-       unsigned long offset;
-       offset = 0;
-       while (offset <= size) {
-               if (buf[offset++] == '\n')
-                       break;
-       }
-       *rsize = size - offset;
-       *rbuf = buf + offset;
+       img->buf += img->line[0].len;
+       img->len -= img->line[0].len;
+       img->line++;
+       img->nr--;
 }
 
-static void remove_last_line(const char **rbuf, int *rsize)
+static void remove_last_line(struct image *img)
 {
-       const char *buf = *rbuf;
-       int size = *rsize;
-       unsigned long offset;
-       offset = size - 1;
-       while (offset > 0) {
-               if (buf[--offset] == '\n')
-                       break;
-       }
-       *rsize = offset + 1;
+       img->len -= img->line[--img->nr].len;
 }
 
-static int apply_line(char *output, const char *patch, int plen,
-                     unsigned ws_rule)
+static void update_image(struct image *img,
+                        int applied_pos,
+                        struct image *preimage,
+                        struct image *postimage)
 {
        /*
-        * plen is number of bytes to be copied from patch,
-        * starting at patch+1 (patch[0] is '+').  Typically
-        * patch[plen] is '\n', unless this is the incomplete
-        * last line.
+        * remove the copy of preimage at offset in img
+        * and replace it with postimage
         */
-       int i;
-       int add_nl_to_tail = 0;
-       int fixed = 0;
-       int last_tab_in_indent = 0;
-       int last_space_in_indent = 0;
-       int need_fix_leading_space = 0;
-       char *buf;
-
-       if ((ws_error_action != correct_ws_error) || !whitespace_error ||
-           *patch != '+') {
-               memcpy(output, patch + 1, plen);
-               return plen;
-       }
-
-       /*
-        * Strip trailing whitespace
-        */
-       if ((ws_rule & WS_TRAILING_SPACE) &&
-           (1 < plen && isspace(patch[plen-1]))) {
-               if (patch[plen] == '\n')
-                       add_nl_to_tail = 1;
-               plen--;
-               while (0 < plen && isspace(patch[plen]))
-                       plen--;
-               fixed = 1;
-       }
-
-       /*
-        * Check leading whitespaces (indent)
-        */
-       for (i = 1; i < plen; i++) {
-               char ch = patch[i];
-               if (ch == '\t') {
-                       last_tab_in_indent = i;
-                       if ((ws_rule & WS_SPACE_BEFORE_TAB) &&
-                           0 < last_space_in_indent)
-                           need_fix_leading_space = 1;
-               } else if (ch == ' ') {
-                       last_space_in_indent = i;
-                       if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
-                           8 <= i - last_tab_in_indent)
-                               need_fix_leading_space = 1;
-               }
-               else
-                       break;
-       }
-
-       buf = output;
-       if (need_fix_leading_space) {
-               int consecutive_spaces = 0;
-               int last = last_tab_in_indent + 1;
-
-               if (ws_rule & WS_INDENT_WITH_NON_TAB) {
-                       /* have "last" point at one past the indent */
-                       if (last_tab_in_indent < last_space_in_indent)
-                               last = last_space_in_indent + 1;
-                       else
-                               last = last_tab_in_indent + 1;
-               }
+       int i, nr;
+       size_t remove_count, insert_count, applied_at = 0;
+       char *result;
 
+       for (i = 0; i < applied_pos; i++)
+               applied_at += img->line[i].len;
+
+       remove_count = 0;
+       for (i = 0; i < preimage->nr; i++)
+               remove_count += img->line[applied_pos + i].len;
+       insert_count = postimage->len;
+
+       /* Adjust the contents */
+       result = xmalloc(img->len + insert_count - remove_count + 1);
+       memcpy(result, img->buf, applied_at);
+       memcpy(result + applied_at, postimage->buf, postimage->len);
+       memcpy(result + applied_at + postimage->len,
+              img->buf + (applied_at + remove_count),
+              img->len - (applied_at + remove_count));
+       free(img->buf);
+       img->buf = result;
+       img->len += insert_count - remove_count;
+       result[img->len] = '\0';
+
+       /* Adjust the line table */
+       nr = img->nr + postimage->nr - preimage->nr;
+       if (preimage->nr < postimage->nr) {
                /*
-                * between patch[1..last], strip the funny spaces,
-                * updating them to tab as needed.
+                * NOTE: this knows that we never call remove_first_line()
+                * on anything other than pre/post image.
                 */
-               for (i = 1; i < last; i++, plen--) {
-                       char ch = patch[i];
-                       if (ch != ' ') {
-                               consecutive_spaces = 0;
-                               *output++ = ch;
-                       } else {
-                               consecutive_spaces++;
-                               if (consecutive_spaces == 8) {
-                                       *output++ = '\t';
-                                       consecutive_spaces = 0;
-                               }
-                       }
-               }
-               while (0 < consecutive_spaces--)
-                       *output++ = ' ';
-               fixed = 1;
-               i = last;
+               img->line = xrealloc(img->line, nr * sizeof(*img->line));
+               img->line_allocated = img->line;
        }
-       else
-               i = 1;
-
-       memcpy(output, patch + i, plen);
-       if (add_nl_to_tail)
-               output[plen++] = '\n';
-       if (fixed)
-               applied_after_fixing_ws++;
-       return output + plen - buf;
+       if (preimage->nr != postimage->nr)
+               memmove(img->line + applied_pos + postimage->nr,
+                       img->line + applied_pos + preimage->nr,
+                       (img->nr - (applied_pos + preimage->nr)) *
+                       sizeof(*img->line));
+       memcpy(img->line + applied_pos,
+              postimage->line,
+              postimage->nr * sizeof(*img->line));
+       img->nr = nr;
 }
 
-static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
+static int apply_one_fragment(struct image *img, struct fragment *frag,
                              int inaccurate_eof, unsigned ws_rule)
 {
        int match_beginning, match_end;
        const char *patch = frag->patch;
-       int offset, size = frag->size;
-       char *old = xmalloc(size);
-       char *new = xmalloc(size);
-       const char *oldlines, *newlines;
-       int oldsize = 0, newsize = 0;
+       int size = frag->size;
+       char *old, *new, *oldlines, *newlines;
        int new_blank_lines_at_end = 0;
        unsigned long leading, trailing;
-       int pos, lines;
+       int pos, applied_pos;
+       struct image preimage;
+       struct image postimage;
+
+       memset(&preimage, 0, sizeof(preimage));
+       memset(&postimage, 0, sizeof(postimage));
+       oldlines = xmalloc(size);
+       newlines = xmalloc(size);
 
+       old = oldlines;
+       new = newlines;
        while (size > 0) {
                char first;
                int len = linelen(patch, size);
-               int plen;
+               int plen, added;
                int added_blank_line = 0;
 
                if (!len)
@@ -1670,7 +1859,7 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
                 * followed by "\ No newline", then we also remove the
                 * last one (which is the newline, of course).
                 */
-               plen = len-1;
+               plen = len - 1;
                if (len < size && patch[len] == '\\')
                        plen--;
                first = *patch;
@@ -1687,25 +1876,40 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
                        if (plen < 0)
                                /* ... followed by '\No newline'; nothing */
                                break;
-                       old[oldsize++] = '\n';
-                       new[newsize++] = '\n';
+                       *old++ = '\n';
+                       *new++ = '\n';
+                       add_line_info(&preimage, "\n", 1, LINE_COMMON);
+                       add_line_info(&postimage, "\n", 1, LINE_COMMON);
                        break;
                case ' ':
                case '-':
-                       memcpy(old + oldsize, patch + 1, plen);
-                       oldsize += plen;
+                       memcpy(old, patch + 1, plen);
+                       add_line_info(&preimage, old, plen,
+                                     (first == ' ' ? LINE_COMMON : 0));
+                       old += plen;
                        if (first == '-')
                                break;
                /* Fall-through for ' ' */
                case '+':
-                       if (first != '+' || !no_add) {
-                               int added = apply_line(new + newsize, patch,
-                                                      plen, ws_rule);
-                               newsize += added;
-                               if (first == '+' &&
-                                   added == 1 && new[newsize-1] == '\n')
-                                       added_blank_line = 1;
+                       /* --no-add does not add new lines */
+                       if (first == '+' && no_add)
+                               break;
+
+                       if (first != '+' ||
+                           !whitespace_error ||
+                           ws_error_action != correct_ws_error) {
+                               memcpy(new, patch + 1, plen);
+                               added = plen;
                        }
+                       else {
+                               added = ws_fix_copy(new, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
+                       }
+                       add_line_info(&postimage, new, added,
+                                     (first == '+' ? 0 : LINE_COMMON));
+                       new += added;
+                       if (first == '+' &&
+                           added == 1 && new[-1] == '\n')
+                               added_blank_line = 1;
                        break;
                case '@': case '\\':
                        /* Ignore it, we already handled it */
@@ -1722,16 +1926,13 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
                patch += len;
                size -= len;
        }
-
        if (inaccurate_eof &&
-           oldsize > 0 && old[oldsize - 1] == '\n' &&
-           newsize > 0 && new[newsize - 1] == '\n') {
-               oldsize--;
-               newsize--;
+           old > oldlines && old[-1] == '\n' &&
+           new > newlines && new[-1] == '\n') {
+               old--;
+               new--;
        }
 
-       oldlines = old;
-       newlines = new;
        leading = frag->leading;
        trailing = frag->trailing;
 
@@ -1752,33 +1953,21 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
                match_end = !trailing;
        }
 
-       lines = 0;
-       pos = frag->newpos;
+       pos = frag->newpos ? (frag->newpos - 1) : 0;
+       preimage.buf = oldlines;
+       preimage.len = old - oldlines;
+       postimage.buf = newlines;
+       postimage.len = new - newlines;
+       preimage.line = preimage.line_allocated;
+       postimage.line = postimage.line_allocated;
+
        for (;;) {
-               offset = find_offset(buf->buf, buf->len,
-                                    oldlines, oldsize, pos, &lines);
-               if (match_end && offset + oldsize != buf->len)
-                       offset = -1;
-               if (match_beginning && offset)
-                       offset = -1;
-               if (offset >= 0) {
-                       if (ws_error_action == correct_ws_error &&
-                           (buf->len - oldsize - offset == 0)) /* end of file? */
-                               newsize -= new_blank_lines_at_end;
-
-                       /* 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",
-                                       leading, trailing, pos + lines);
-
-                       strbuf_splice(buf, offset, oldsize, newlines, newsize);
-                       offset = 0;
+
+               applied_pos = find_pos(img, &preimage, &postimage, pos,
+                                      ws_rule, match_beginning, match_end);
+
+               if (applied_pos >= 0)
                        break;
-               }
 
                /* Am I at my context limits? */
                if ((leading <= p_context) && (trailing <= p_context))
@@ -1787,33 +1976,64 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
                        match_beginning = match_end = 0;
                        continue;
                }
+
                /*
                 * Reduce the number of context lines; reduce both
                 * leading and trailing if they are equal otherwise
                 * just reduce the larger context.
                 */
                if (leading >= trailing) {
-                       remove_first_line(&oldlines, &oldsize);
-                       remove_first_line(&newlines, &newsize);
+                       remove_first_line(&preimage);
+                       remove_first_line(&postimage);
                        pos--;
                        leading--;
                }
                if (trailing > leading) {
-                       remove_last_line(&oldlines, &oldsize);
-                       remove_last_line(&newlines, &newsize);
+                       remove_last_line(&preimage);
+                       remove_last_line(&postimage);
                        trailing--;
                }
        }
 
-       if (offset && apply_verbosely)
-               error("while searching for:\n%.*s", oldsize, oldlines);
+       if (applied_pos >= 0) {
+               if (ws_error_action == correct_ws_error &&
+                   new_blank_lines_at_end &&
+                   postimage.nr + applied_pos == img->nr) {
+                       /*
+                        * If the patch application adds blank lines
+                        * at the end, and if the patch applies at the
+                        * end of the image, remove those added blank
+                        * lines.
+                        */
+                       while (new_blank_lines_at_end--)
+                               remove_last_line(&postimage);
+               }
 
-       free(old);
-       free(new);
-       return offset;
+               /*
+                * 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",
+                               leading, trailing, applied_pos+1);
+               update_image(img, applied_pos, &preimage, &postimage);
+       } else {
+               if (apply_verbosely)
+                       error("while searching for:\n%.*s",
+                             (int)(old - oldlines), oldlines);
+       }
+
+       free(oldlines);
+       free(newlines);
+       free(preimage.line_allocated);
+       free(postimage.line_allocated);
+
+       return (applied_pos < 0);
 }
 
-static int apply_binary_fragment(struct strbuf *buf, struct patch *patch)
+static int apply_binary_fragment(struct image *img, struct patch *patch)
 {
        struct fragment *fragment = patch->fragments;
        unsigned long len;
@@ -1830,22 +2050,26 @@ static int apply_binary_fragment(struct strbuf *buf, struct patch *patch)
        }
        switch (fragment->binary_patch_method) {
        case BINARY_DELTA_DEFLATED:
-               dst = patch_delta(buf->buf, buf->len, fragment->patch,
+               dst = patch_delta(img->buf, img->len, fragment->patch,
                                  fragment->size, &len);
                if (!dst)
                        return -1;
-               /* XXX patch_delta NUL-terminates */
-               strbuf_attach(buf, dst, len, len + 1);
+               clear_image(img);
+               img->buf = dst;
+               img->len = len;
                return 0;
        case BINARY_LITERAL_DEFLATED:
-               strbuf_reset(buf);
-               strbuf_add(buf, fragment->patch, fragment->size);
+               clear_image(img);
+               img->len = fragment->size;
+               img->buf = xmalloc(img->len+1);
+               memcpy(img->buf, fragment->patch, img->len);
+               img->buf[img->len] = '\0';
                return 0;
        }
        return -1;
 }
 
-static int apply_binary(struct strbuf *buf, struct patch *patch)
+static int apply_binary(struct image *img, struct patch *patch)
 {
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
        unsigned char sha1[20];
@@ -1866,7 +2090,7 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)
                 * See if the old one matches what the patch
                 * applies to.
                 */
-               hash_sha1_file(buf->buf, buf->len, blob_type, sha1);
+               hash_sha1_file(img->buf, img->len, blob_type, sha1);
                if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
                        return error("the patch applies to '%s' (%s), "
                                     "which does not match the "
@@ -1875,14 +2099,14 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)
        }
        else {
                /* Otherwise, the old one must be empty. */
-               if (buf->len)
+               if (img->len)
                        return error("the patch applies to an empty "
                                     "'%s' but it is not empty", name);
        }
 
        get_sha1_hex(patch->new_sha1_prefix, sha1);
        if (is_null_sha1(sha1)) {
-               strbuf_release(buf);
+               clear_image(img);
                return 0; /* deletion patch */
        }
 
@@ -1897,20 +2121,21 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)
                        return error("the necessary postimage %s for "
                                     "'%s' cannot be read",
                                     patch->new_sha1_prefix, name);
-               /* XXX read_sha1_file NUL-terminates */
-               strbuf_attach(buf, result, size, size + 1);
+               clear_image(img);
+               img->buf = result;
+               img->len = size;
        } else {
                /*
                 * We have verified buf matches the preimage;
                 * apply the patch data to it, which is stored
                 * in the patch->fragments->{patch,size}.
                 */
-               if (apply_binary_fragment(buf, patch))
+               if (apply_binary_fragment(img, patch))
                        return error("binary patch does not apply to '%s'",
                                     name);
 
                /* verify that the result matches */
-               hash_sha1_file(buf->buf, buf->len, blob_type, sha1);
+               hash_sha1_file(img->buf, img->len, blob_type, sha1);
                if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
                        return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)",
                                name, patch->new_sha1_prefix, sha1_to_hex(sha1));
@@ -1919,7 +2144,7 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)
        return 0;
 }
 
-static int apply_fragments(struct strbuf *buf, struct patch *patch)
+static int apply_fragments(struct image *img, struct patch *patch)
 {
        struct fragment *frag = patch->fragments;
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
@@ -1927,10 +2152,10 @@ static int apply_fragments(struct strbuf *buf, struct patch *patch)
        unsigned inaccurate_eof = patch->inaccurate_eof;
 
        if (patch->is_binary)
-               return apply_binary(buf, patch);
+               return apply_binary(img, patch);
 
        while (frag) {
-               if (apply_one_fragment(buf, frag, inaccurate_eof, ws_rule)) {
+               if (apply_one_fragment(img, frag, inaccurate_eof, ws_rule)) {
                        error("patch failed: %s:%ld", name, frag->oldpos);
                        if (!apply_with_reject)
                                return -1;
@@ -1966,6 +2191,9 @@ static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)
 static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
 {
        struct strbuf buf;
+       struct image image;
+       size_t len;
+       char *img;
 
        strbuf_init(&buf, 0);
        if (cached) {
@@ -1988,9 +2216,14 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
                }
        }
 
-       if (apply_fragments(&buf, patch) < 0)
+       img = strbuf_detach(&buf, &len);
+       prepare_image(&image, img, len, !patch->is_binary);
+
+       if (apply_fragments(&image, patch) < 0)
                return -1; /* note with --reject this succeeds. */
-       patch->result = strbuf_detach(&buf, &patch->resultsize);
+       patch->result = image.buf;
+       patch->resultsize = image.len;
+       free(image.line_allocated);
 
        if (0 < patch->is_delete && patch->resultsize)
                return error("removal patch leaves file contents");
@@ -2746,6 +2979,8 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof)
 static int git_apply_config(const char *var, const char *value)
 {
        if (!strcmp(var, "apply.whitespace")) {
+               if (!value)
+                       return config_error_nonbool(var);
                apply_default_whitespace = xstrdup(value);
                return 0;
        }
index c7e6887..59d7237 100644 (file)
@@ -1894,9 +1894,7 @@ static unsigned parse_score(const char *arg)
 
 static const char *add_prefix(const char *prefix, const char *path)
 {
-       if (!prefix || !prefix[0])
-               return path;
-       return prefix_path(prefix, strlen(prefix), path);
+       return prefix_path(prefix, prefix ? strlen(prefix) : 0, path);
 }
 
 /*
@@ -2073,7 +2071,7 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
                if (strbuf_read(&buf, 0, 0) < 0)
                        die("read error %s from stdin", strerror(errno));
        }
-       convert_to_git(path, buf.buf, buf.len, &buf);
+       convert_to_git(path, buf.buf, buf.len, &buf, 0);
        origin->file.ptr = buf.buf;
        origin->file.size = buf.len;
        pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_sha1);
@@ -2369,7 +2367,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
         * bottom commits we would reach while traversing as
         * uninteresting.
         */
-       prepare_revision_walk(&revs);
+       if (prepare_revision_walk(&revs))
+               die("revision walk setup failed");
 
        if (is_null_sha1(sb.final->object.sha1)) {
                char *buf;
index 32eaf0d..a784445 100644 (file)
@@ -30,7 +30,7 @@ static const char * const builtin_branch_usage[] = {
 static const char *head;
 static unsigned char head_sha1[20];
 
-static int branch_use_color;
+static int branch_use_color = -1;
 static char branch_colors[][COLOR_MAXLEN] = {
        "\033[m",       /* reset */
        "",             /* PLAIN (normal) */
@@ -69,15 +69,17 @@ static int git_branch_config(const char *var, const char *value)
        }
        if (!prefixcmp(var, "color.branch.")) {
                int slot = parse_branch_color_slot(var, 13);
+               if (!value)
+                       return config_error_nonbool(var);
                color_parse(value, var, branch_colors[slot]);
                return 0;
        }
-       return git_default_config(var, value);
+       return git_color_default_config(var, value);
 }
 
 static const char *branch_get_color(enum color_branch ix)
 {
-       if (branch_use_color)
+       if (branch_use_color > 0)
                return branch_colors[ix];
        return "";
 }
@@ -448,6 +450,10 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        };
 
        git_config(git_branch_config);
+
+       if (branch_use_color == -1)
+               branch_use_color = git_use_color_default;
+
        track = git_branch_track;
        argc = parse_options(argc, argv, options, builtin_branch_usage, 0);
        if (!!delete + !!rename + !!force_create > 1)
index b1820a4..4a4bb8b 100644 (file)
@@ -205,8 +205,7 @@ static void setup_branch_path(struct branch_info *branch)
 }
 
 static int merge_working_tree(struct checkout_opts *opts,
-                             struct branch_info *old, struct branch_info *new,
-                             const char *prefix)
+                             struct branch_info *old, struct branch_info *new)
 {
        int ret;
        struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
@@ -227,24 +226,25 @@ static int merge_working_tree(struct checkout_opts *opts,
                refresh_cache(REFRESH_QUIET);
 
                if (unmerged_cache()) {
-                       ret = opts->merge ? -1 :
-                               error("you need to resolve your current index first");
-               } else {
-                       topts.update = 1;
-                       topts.merge = 1;
-                       topts.gently = opts->merge;
-                       topts.fn = twoway_merge;
-                       topts.dir = xcalloc(1, sizeof(*topts.dir));
-                       topts.dir->show_ignored = 1;
-                       topts.dir->exclude_per_dir = ".gitignore";
-                       topts.prefix = prefix;
-                       tree = parse_tree_indirect(old->commit->object.sha1);
-                       init_tree_desc(&trees[0], tree->buffer, tree->size);
-                       tree = parse_tree_indirect(new->commit->object.sha1);
-                       init_tree_desc(&trees[1], tree->buffer, tree->size);
-                       ret = unpack_trees(2, trees, &topts);
+                       error("you need to resolve your current index first");
+                       return 1;
                }
-               if (ret) {
+
+               /* 2-way merge to the new branch */
+               topts.update = 1;
+               topts.merge = 1;
+               topts.gently = opts->merge;
+               topts.verbose_update = !opts->quiet;
+               topts.fn = twoway_merge;
+               topts.dir = xcalloc(1, sizeof(*topts.dir));
+               topts.dir->show_ignored = 1;
+               topts.dir->exclude_per_dir = ".gitignore";
+               tree = parse_tree_indirect(old->commit->object.sha1);
+               init_tree_desc(&trees[0], tree->buffer, tree->size);
+               tree = parse_tree_indirect(new->commit->object.sha1);
+               init_tree_desc(&trees[1], tree->buffer, tree->size);
+
+               if (unpack_trees(2, trees, &topts)) {
                        /*
                         * Unpack couldn't do a trivial merge; either
                         * give up or do a real merge, depending on
@@ -291,7 +291,7 @@ static int merge_working_tree(struct checkout_opts *opts,
        return 0;
 }
 
-static void adjust_to_tracking(struct branch_info *new, struct checkout_opts *opts)
+static void report_tracking(struct branch_info *new, struct checkout_opts *opts)
 {
        /*
         * We have switched to a new branch; is it building on
@@ -301,64 +301,88 @@ static void adjust_to_tracking(struct branch_info *new, struct checkout_opts *op
        char *base;
        unsigned char sha1[20];
        struct commit *ours, *theirs;
-       const char *msgfmt;
        char symmetric[84];
-       int show_log;
-       struct branch *branch = branch_get(NULL);
+       struct rev_info revs;
+       const char *rev_argv[10];
+       int rev_argc;
+       int num_ours, num_theirs;
+       const char *remote_msg;
+       struct branch *branch = branch_get(new->name);
 
-       if (!branch || !branch->merge)
+       /*
+        * Nothing to report unless we are marked to build on top of
+        * somebody else.
+        */
+       if (!branch || !branch->merge || !branch->merge[0] || !branch->merge[0]->dst)
                return;
 
-       base = branch->merge[0]->dst;
-
-       ours = new->commit;
-
-       sprintf(symmetric, "%s", sha1_to_hex(ours->object.sha1));
-
        /*
-        * Ok, it is tracking base; is it ahead of us?
+        * If what we used to build on no longer exists, there is
+        * nothing to report.
         */
+       base = branch->merge[0]->dst;
        if (!resolve_ref(base, sha1, 1, NULL))
                return;
-       theirs = lookup_commit(sha1);
-
-       sprintf(symmetric + 40, "...%s", sha1_to_hex(sha1));
 
+       theirs = lookup_commit(sha1);
+       ours = new->commit;
        if (!hashcmp(sha1, ours->object.sha1))
                return; /* we are the same */
 
-       show_log = 1;
-       if (in_merge_bases(theirs, &ours, 1)) {
-               msgfmt = "You are ahead of the tracked branch '%s'\n";
-               show_log = 0;
+       /* Run "rev-list --left-right ours...theirs" internally... */
+       rev_argc = 0;
+       rev_argv[rev_argc++] = NULL;
+       rev_argv[rev_argc++] = "--left-right";
+       rev_argv[rev_argc++] = symmetric;
+       rev_argv[rev_argc++] = "--";
+       rev_argv[rev_argc] = NULL;
+
+       strcpy(symmetric, sha1_to_hex(ours->object.sha1));
+       strcpy(symmetric + 40, "...");
+       strcpy(symmetric + 43, sha1_to_hex(theirs->object.sha1));
+
+       init_revisions(&revs, NULL);
+       setup_revisions(rev_argc, rev_argv, &revs, NULL);
+       prepare_revision_walk(&revs);
+
+       /* ... and count the commits on each side. */
+       num_ours = 0;
+       num_theirs = 0;
+       while (1) {
+               struct commit *c = get_revision(&revs);
+               if (!c)
+                       break;
+               if (c->object.flags & SYMMETRIC_LEFT)
+                       num_ours++;
+               else
+                       num_theirs++;
        }
-       else if (in_merge_bases(ours, &theirs, 1))
-               msgfmt = "Your branch can be fast-forwarded to the tracked branch '%s'\n";
-       else
-               msgfmt = "Both your branch and the tracked branch '%s' have own changes, you would eventually need to merge\n";
 
-       if (!prefixcmp(base, "refs/remotes/"))
+       if (!prefixcmp(base, "refs/remotes/")) {
+               remote_msg = " remote";
                base += strlen("refs/remotes/");
-       fprintf(stderr, msgfmt, base);
-
-       if (show_log) {
-               const char *args[32];
-               int ac;
-
-               ac = 0;
-               args[ac++] = "log";
-               args[ac++] = "--pretty=oneline";
-               args[ac++] = "--abbrev-commit";
-               args[ac++] = "--left-right";
-               args[ac++] = "--boundary";
-               args[ac++] = symmetric;
-               args[ac++] = "--";
-               args[ac] = NULL;
-
-               run_command_v_opt(args, RUN_GIT_CMD);
+       } else {
+               remote_msg = "";
        }
-}
 
+       if (!num_theirs)
+               printf("Your branch is ahead of the tracked%s branch '%s' "
+                      "by %d commit%s.\n",
+                      remote_msg, base,
+                      num_ours, (num_ours == 1) ? "" : "s");
+       else if (!num_ours)
+               printf("Your branch is behind the tracked%s branch '%s' "
+                      "by %d commit%s,\n"
+                      "and can be fast-forwarded.\n",
+                      remote_msg, base,
+                      num_theirs, (num_theirs == 1) ? "" : "s");
+       else
+               printf("Your branch and the tracked%s branch '%s' "
+                      "have diverged,\nand respectively "
+                      "have %d and %d different commit(s) each.\n",
+                      remote_msg, base,
+                      num_ours, num_theirs);
+}
 
 static void update_refs_for_switch(struct checkout_opts *opts,
                                   struct branch_info *old,
@@ -402,12 +426,11 @@ static void update_refs_for_switch(struct checkout_opts *opts,
        }
        remove_branch_state();
        strbuf_release(&msg);
-       if (new->path || !strcmp(new->name, "HEAD"))
-               adjust_to_tracking(new, opts);
+       if (!opts->quiet && (new->path || !strcmp(new->name, "HEAD")))
+               report_tracking(new, opts);
 }
 
-static int switch_branches(struct checkout_opts *opts,
-                          struct branch_info *new, const char *prefix)
+static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
 {
        int ret = 0;
        struct branch_info old;
@@ -448,7 +471,7 @@ static int switch_branches(struct checkout_opts *opts,
                opts->force = 1;
        }
 
-       ret = merge_working_tree(opts, &old, new, prefix);
+       ret = merge_working_tree(opts, &old, new);
        if (ret)
                return ret;
 
@@ -542,5 +565,5 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                die("Cannot switch branch to a non-commit.");
        }
 
-       return switch_branches(&opts, &new, prefix);
+       return switch_branches(&opts, &new);
 }
index eb853a3..3b220d5 100644 (file)
@@ -29,7 +29,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 {
        int i;
        int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0;
-       int ignored_only = 0, baselen = 0, config_set = 0;
+       int ignored_only = 0, baselen = 0, config_set = 0, errors = 0;
        struct strbuf directory;
        struct dir_struct dir;
        const char *path, *base;
@@ -137,12 +137,15 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                        if (show_only && (remove_directories || matches)) {
                                printf("Would remove %s\n",
                                       directory.buf + prefix_offset);
-                       } else if (quiet && (remove_directories || matches)) {
-                               remove_dir_recursively(&directory, 0);
                        } else if (remove_directories || matches) {
-                               printf("Removing %s\n",
-                                      directory.buf + prefix_offset);
-                               remove_dir_recursively(&directory, 0);
+                               if (!quiet)
+                                       printf("Removing %s\n",
+                                              directory.buf + prefix_offset);
+                               if (remove_dir_recursively(&directory, 0) != 0) {
+                                       warning("failed to remove '%s'",
+                                               directory.buf + prefix_offset);
+                                       errors++;
+                               }
                        } else if (show_only) {
                                printf("Would not remove %s\n",
                                       directory.buf + prefix_offset);
@@ -162,11 +165,14 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                                printf("Removing %s\n",
                                       ent->name + prefix_offset);
                        }
-                       unlink(ent->name);
+                       if (unlink(ent->name) != 0) {
+                               warning("failed to remove '%s'", ent->name);
+                               errors++;
+                       }
                }
        }
        free(seen);
 
        strbuf_release(&directory);
-       return 0;
+       return (errors != 0);
 }
index 5b5b7c0..f49c22e 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "cache.h"
 #include "cache-tree.h"
+#include "color.h"
 #include "dir.h"
 #include "builtin.h"
 #include "diff.h"
@@ -122,19 +123,23 @@ static void rollback_index_files(void)
        }
 }
 
-static void commit_index_files(void)
+static int commit_index_files(void)
 {
+       int err = 0;
+
        switch (commit_style) {
        case COMMIT_AS_IS:
                break; /* nothing to do */
        case COMMIT_NORMAL:
-               commit_lock_file(&index_lock);
+               err = commit_lock_file(&index_lock);
                break;
        case COMMIT_PARTIAL:
-               commit_lock_file(&index_lock);
+               err = commit_lock_file(&index_lock);
                rollback_lock_file(&false_lock);
                break;
        }
+
+       return err;
 }
 
 /*
@@ -314,6 +319,10 @@ static char *prepare_index(int argc, const char **argv, const char *prefix)
        if (write_cache(fd, active_cache, active_nr) ||
            close_lock_file(&false_lock))
                die("unable to write temporary index file");
+
+       discard_cache();
+       read_cache_from(false_lock.filename);
+
        return false_lock.filename;
 }
 
@@ -340,45 +349,107 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
        return s.commitable;
 }
 
+static int run_hook(const char *index_file, const char *name, ...)
+{
+       struct child_process hook;
+       const char *argv[10], *env[2];
+       char index[PATH_MAX];
+       va_list args;
+       int i;
+
+       va_start(args, name);
+       argv[0] = git_path("hooks/%s", name);
+       i = 0;
+       do {
+               if (++i >= ARRAY_SIZE(argv))
+                       die ("run_hook(): too many arguments");
+               argv[i] = va_arg(args, const char *);
+       } while (argv[i]);
+       va_end(args);
+
+       snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
+       env[0] = index;
+       env[1] = NULL;
+
+       if (access(argv[0], X_OK) < 0)
+               return 0;
+
+       memset(&hook, 0, sizeof(hook));
+       hook.argv = argv;
+       hook.no_stdin = 1;
+       hook.stdout_to_stderr = 1;
+       hook.env = env;
+
+       return run_command(&hook);
+}
+
+static int is_a_merge(const unsigned char *sha1)
+{
+       struct commit *commit = lookup_commit(sha1);
+       if (!commit || parse_commit(commit))
+               die("could not parse HEAD commit");
+       return !!(commit->parents && commit->parents->next);
+}
+
 static const char sign_off_header[] = "Signed-off-by: ";
 
-static int prepare_log_message(const char *index_file, const char *prefix)
+static int prepare_to_commit(const char *index_file, const char *prefix)
 {
        struct stat statbuf;
        int commitable, saved_color_setting;
        struct strbuf sb;
        char *buffer;
        FILE *fp;
+       const char *hook_arg1 = NULL;
+       const char *hook_arg2 = NULL;
+
+       if (!no_verify && run_hook(index_file, "pre-commit", NULL))
+               return 0;
 
        strbuf_init(&sb, 0);
        if (message.len) {
                strbuf_addbuf(&sb, &message);
+               hook_arg1 = "message";
        } else if (logfile && !strcmp(logfile, "-")) {
                if (isatty(0))
                        fprintf(stderr, "(reading log message from standard input)\n");
                if (strbuf_read(&sb, 0, 0) < 0)
                        die("could not read log from standard input");
+               hook_arg1 = "message";
        } else if (logfile) {
                if (strbuf_read_file(&sb, logfile, 0) < 0)
                        die("could not read log file '%s': %s",
                            logfile, strerror(errno));
+               hook_arg1 = "message";
        } else if (use_message) {
                buffer = strstr(use_message_buffer, "\n\n");
                if (!buffer || buffer[2] == '\0')
                        die("commit has empty message");
                strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
+               hook_arg1 = "commit";
+               hook_arg2 = use_message;
        } else if (!stat(git_path("MERGE_MSG"), &statbuf)) {
                if (strbuf_read_file(&sb, git_path("MERGE_MSG"), 0) < 0)
                        die("could not read MERGE_MSG: %s", strerror(errno));
+               hook_arg1 = "merge";
        } else if (!stat(git_path("SQUASH_MSG"), &statbuf)) {
                if (strbuf_read_file(&sb, git_path("SQUASH_MSG"), 0) < 0)
                        die("could not read SQUASH_MSG: %s", strerror(errno));
+               hook_arg1 = "squash";
        } else if (template_file && !stat(template_file, &statbuf)) {
                if (strbuf_read_file(&sb, template_file, 0) < 0)
                        die("could not read %s: %s",
                            template_file, strerror(errno));
+               hook_arg1 = "template";
        }
 
+       /*
+        * This final case does not modify the template message,
+        * it just sets the argument to the prepare-commit-msg hook.
+        */
+       else if (in_merge)
+               hook_arg1 = "merge";
+
        fp = fopen(git_path(commit_editmsg), "w");
        if (fp == NULL)
                die("could not open %s", git_path(commit_editmsg));
@@ -410,13 +481,38 @@ static int prepare_log_message(const char *index_file, const char *prefix)
 
        strbuf_release(&sb);
 
-       if (!use_editor) {
+       if (use_editor) {
+               if (in_merge)
+                       fprintf(fp,
+                               "#\n"
+                               "# It looks like you may be committing a MERGE.\n"
+                               "# If this is not correct, please remove the file\n"
+                               "#      %s\n"
+                               "# and try again.\n"
+                               "#\n",
+                               git_path("MERGE_HEAD"));
+
+               fprintf(fp,
+                       "\n"
+                       "# Please enter the commit message for your changes.\n"
+                       "# (Comment lines starting with '#' will ");
+               if (cleanup_mode == CLEANUP_ALL)
+                       fprintf(fp, "not be included)\n");
+               else /* CLEANUP_SPACE, that is. */
+                       fprintf(fp, "be kept.\n"
+                               "# You can remove them yourself if you want to)\n");
+               if (only_include_assumed)
+                       fprintf(fp, "# %s\n", only_include_assumed);
+
+               saved_color_setting = wt_status_use_color;
+               wt_status_use_color = 0;
+               commitable = run_status(fp, index_file, prefix, 1);
+               wt_status_use_color = saved_color_setting;
+       } else {
                struct rev_info rev;
                unsigned char sha1[20];
                const char *parent = "HEAD";
 
-               fclose(fp);
-
                if (!active_nr && read_cache() < 0)
                        die("Cannot read index");
 
@@ -424,48 +520,60 @@ static int prepare_log_message(const char *index_file, const char *prefix)
                        parent = "HEAD^1";
 
                if (get_sha1(parent, sha1))
-                       return !!active_nr;
+                       commitable = !!active_nr;
+               else {
+                       init_revisions(&rev, "");
+                       rev.abbrev = 0;
+                       setup_revisions(0, NULL, &rev, parent);
+                       DIFF_OPT_SET(&rev.diffopt, QUIET);
+                       DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS);
+                       run_diff_index(&rev, 1 /* cached */);
+
+                       commitable = !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES);
+               }
+       }
 
-               init_revisions(&rev, "");
-               rev.abbrev = 0;
-               setup_revisions(0, NULL, &rev, parent);
-               DIFF_OPT_SET(&rev.diffopt, QUIET);
-               DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS);
-               run_diff_index(&rev, 1 /* cached */);
+       fclose(fp);
 
-               return !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES);
+       if (!commitable && !in_merge && !allow_empty &&
+           !(amend && is_a_merge(head_sha1))) {
+               run_status(stdout, index_file, prefix, 0);
+               unlink(commit_editmsg);
+               return 0;
        }
 
-       if (in_merge)
-               fprintf(fp,
-                       "#\n"
-                       "# It looks like you may be committing a MERGE.\n"
-                       "# If this is not correct, please remove the file\n"
-                       "#      %s\n"
-                       "# and try again.\n"
-                       "#\n",
-                       git_path("MERGE_HEAD"));
-
-       fprintf(fp,
-               "\n"
-               "# Please enter the commit message for your changes.\n"
-               "# (Comment lines starting with '#' will ");
-       if (cleanup_mode == CLEANUP_ALL)
-               fprintf(fp, "not be included)\n");
-       else /* CLEANUP_SPACE, that is. */
-               fprintf(fp, "be kept.\n"
-                       "# You can remove them yourself if you want to)\n");
-       if (only_include_assumed)
-               fprintf(fp, "# %s\n", only_include_assumed);
-
-       saved_color_setting = wt_status_use_color;
-       wt_status_use_color = 0;
-       commitable = run_status(fp, index_file, prefix, 1);
-       wt_status_use_color = saved_color_setting;
+       /*
+        * Re-read the index as pre-commit hook could have updated it,
+        * and write it out as a tree.  We must do this before we invoke
+        * the editor and after we invoke run_status above.
+        */
+       discard_cache();
+       read_cache_from(index_file);
+       if (!active_cache_tree)
+               active_cache_tree = cache_tree();
+       if (cache_tree_update(active_cache_tree,
+                             active_cache, active_nr, 0, 0) < 0) {
+               error("Error building trees");
+               return 0;
+       }
 
-       fclose(fp);
+       if (run_hook(index_file, "prepare-commit-msg",
+                    git_path(commit_editmsg), hook_arg1, hook_arg2, NULL))
+               return 0;
 
-       return commitable;
+       if (use_editor) {
+               char index[PATH_MAX];
+               const char *env[2] = { index, NULL };
+               snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
+               launch_editor(git_path(commit_editmsg), NULL, env);
+       }
+
+       if (!no_verify &&
+           run_hook(index_file, "commit-msg", git_path(commit_editmsg), NULL)) {
+               return 0;
+       }
+
+       return 1;
 }
 
 /*
@@ -562,6 +670,8 @@ static int parse_and_validate_options(int argc, const char *argv[],
                use_editor = 0;
        if (edit_flag)
                use_editor = 1;
+       if (!use_editor)
+               setenv("GIT_EDITOR", ":", 1);
 
        if (get_sha1("HEAD", head_sha1))
                initial_commit = 1;
@@ -598,7 +708,7 @@ static int parse_and_validate_options(int argc, const char *argv[],
 
                if (get_sha1(use_message, sha1))
                        die("could not lookup commit %s", use_message);
-               commit = lookup_commit(sha1);
+               commit = lookup_commit_reference(sha1);
                if (!commit || parse_commit(commit))
                        die("could not parse commit %s", use_message);
 
@@ -663,6 +773,9 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 
        git_config(git_status_config);
 
+       if (wt_status_use_color == -1)
+               wt_status_use_color = git_use_color_default;
+
        argc = parse_and_validate_options(argc, argv, builtin_status_usage);
 
        index_file = prepare_index(argc, argv, prefix);
@@ -674,31 +787,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)
        return commitable ? 0 : 1;
 }
 
-static int run_hook(const char *index_file, const char *name, const char *arg)
-{
-       struct child_process hook;
-       const char *argv[3], *env[2];
-       char index[PATH_MAX];
-
-       argv[0] = git_path("hooks/%s", name);
-       argv[1] = arg;
-       argv[2] = NULL;
-       snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
-       env[0] = index;
-       env[1] = NULL;
-
-       if (access(argv[0], X_OK) < 0)
-               return 0;
-
-       memset(&hook, 0, sizeof(hook));
-       hook.argv = argv;
-       hook.no_stdin = 1;
-       hook.stdout_to_stderr = 1;
-       hook.env = env;
-
-       return run_command(&hook);
-}
-
 static void print_summary(const char *prefix, const unsigned char *sha1)
 {
        struct rev_info rev;
@@ -740,6 +828,8 @@ static void print_summary(const char *prefix, const unsigned char *sha1)
 int git_commit_config(const char *k, const char *v)
 {
        if (!strcmp(k, "commit.template")) {
+               if (!v)
+                       return config_error_nonbool(v);
                template_file = xstrdup(v);
                return 0;
        }
@@ -747,14 +837,6 @@ int git_commit_config(const char *k, const char *v)
        return git_status_config(k, v);
 }
 
-static int is_a_merge(const unsigned char *sha1)
-{
-       struct commit *commit = lookup_commit(sha1);
-       if (!commit || parse_commit(commit))
-               die("could not parse HEAD commit");
-       return !!(commit->parents && commit->parents->next);
-}
-
 static const char commit_utf8_warn[] =
 "Warning: commit message does not conform to UTF-8.\n"
 "You may want to amend it after fixing the message, or set the config\n"
@@ -786,33 +868,13 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 
        index_file = prepare_index(argc, argv, prefix);
 
-       if (!no_verify && run_hook(index_file, "pre-commit", NULL)) {
+       /* Set up everything for writing the commit object.  This includes
+          running hooks, writing the trees, and interacting with the user.  */
+       if (!prepare_to_commit(index_file, prefix)) {
                rollback_index_files();
                return 1;
        }
 
-       if (!prepare_log_message(index_file, prefix) && !in_merge &&
-           !allow_empty && !(amend && is_a_merge(head_sha1))) {
-               run_status(stdout, index_file, prefix, 0);
-               rollback_index_files();
-               unlink(commit_editmsg);
-               return 1;
-       }
-
-       /*
-        * Re-read the index as pre-commit hook could have updated it,
-        * and write it out as a tree.
-        */
-       discard_cache();
-       read_cache_from(index_file);
-       if (!active_cache_tree)
-               active_cache_tree = cache_tree();
-       if (cache_tree_update(active_cache_tree,
-                             active_cache, active_nr, 0, 0) < 0) {
-               rollback_index_files();
-               die("Error building trees");
-       }
-
        /*
         * The commit object
         */
@@ -864,19 +926,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                strbuf_addf(&sb, "encoding %s\n", git_commit_encoding);
        strbuf_addch(&sb, '\n');
 
-       /* Get the commit message and validate it */
+       /* Finally, get the commit message */
        header_len = sb.len;
-       if (use_editor) {
-               char index[PATH_MAX];
-               const char *env[2] = { index, NULL };
-               snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
-               launch_editor(git_path(commit_editmsg), NULL, env);
-       }
-       if (!no_verify &&
-           run_hook(index_file, "commit-msg", git_path(commit_editmsg))) {
-               rollback_index_files();
-               exit(1);
-       }
        if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
                rollback_index_files();
                die("could not read commit message");
@@ -926,8 +977,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 
        unlink(git_path("MERGE_HEAD"));
        unlink(git_path("MERGE_MSG"));
+       unlink(git_path("SQUASH_MSG"));
 
-       commit_index_files();
+       if (commit_index_files())
+               die ("Repository has been updated, but unable to write\n"
+                    "new_index file. Check that disk is not full or quota is\n"
+                    "not exceeded, and then \"git reset HEAD\" to recover.");
 
        rerere();
        run_hook(get_index_file(), "post-commit", NULL);
index e4a12e3..2b9a426 100644 (file)
@@ -79,9 +79,10 @@ static int get_value(const char* key_, const char* regex_)
                local = getenv(CONFIG_LOCAL_ENVIRONMENT);
                if (!local)
                        local = repo_config = xstrdup(git_path("config"));
-               if (home)
+               if (git_config_global() && home)
                        global = xstrdup(mkpath("%s/.gitconfig", home));
-               system_wide = git_etc_gitconfig();
+               if (git_config_system())
+                       system_wide = git_etc_gitconfig();
        }
 
        key = xstrdup(key_);
@@ -168,6 +169,8 @@ static char parsed_color[COLOR_MAXLEN];
 static int git_get_color_config(const char *var, const char *value)
 {
        if (!strcmp(var, get_color_slot)) {
+               if (!value)
+                       config_error_nonbool(var);
                color_parse(value, var, parsed_color);
                get_color_found = 1;
        }
index 7a148a2..05e309f 100644 (file)
@@ -19,6 +19,7 @@ static int all;       /* Default to annotated tags only */
 static int tags;       /* But allow any tags if --tags is specified */
 static int abbrev = DEFAULT_ABBREV;
 static int max_candidates = 10;
+const char *pattern = NULL;
 
 struct commit_name {
        int prio; /* annotated tag = 2, tag = 1, head = 0 */
@@ -45,21 +46,38 @@ static void add_to_known_names(const char *path,
 
 static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
-       struct commit *commit = lookup_commit_reference_gently(sha1, 1);
+       int might_be_tag = !prefixcmp(path, "refs/tags/");
+       struct commit *commit;
        struct object *object;
-       int prio;
+       unsigned char peeled[20];
+       int is_tag, prio;
 
-       if (!commit)
+       if (!all && !might_be_tag)
                return 0;
-       object = parse_object(sha1);
+
+       if (!peel_ref(path, peeled) && !is_null_sha1(peeled)) {
+               commit = lookup_commit_reference_gently(peeled, 1);
+               if (!commit)
+                       return 0;
+               is_tag = !!hashcmp(sha1, commit->object.sha1);
+       } else {
+               commit = lookup_commit_reference_gently(sha1, 1);
+               object = parse_object(sha1);
+               if (!commit || !object)
+                       return 0;
+               is_tag = object->type == OBJ_TAG;
+       }
+
        /* If --all, then any refs are used.
         * If --tags, then any tags are used.
         * Otherwise only annotated tags are used.
         */
-       if (!prefixcmp(path, "refs/tags/")) {
-               if (object->type == OBJ_TAG)
+       if (might_be_tag) {
+               if (is_tag) {
                        prio = 2;
-               else
+                       if (pattern && fnmatch(pattern, path + 10, 0))
+                               prio = 0;
+               } else
                        prio = 1;
        }
        else
@@ -156,6 +174,8 @@ static void describe(const char *arg, int last_one)
                return;
        }
 
+       if (!max_candidates)
+               die("no tag exactly matches '%s'", sha1_to_hex(cmit->object.sha1));
        if (debug)
                fprintf(stderr, "searching to describe %s\n", arg);
 
@@ -252,26 +272,37 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                OPT_BOOLEAN(0, "all",        &all, "use any ref in .git/refs"),
                OPT_BOOLEAN(0, "tags",       &tags, "use any tag in .git/refs/tags"),
                OPT__ABBREV(&abbrev),
+               OPT_SET_INT(0, "exact-match", &max_candidates,
+                           "only output exact matches", 0),
                OPT_INTEGER(0, "candidates", &max_candidates,
-                                       "consider <n> most recent tags (default: 10)"),
+                           "consider <n> most recent tags (default: 10)"),
+               OPT_STRING(0, "match",       &pattern, "pattern",
+                          "only consider tags matching <pattern>"),
                OPT_END(),
        };
 
        argc = parse_options(argc, argv, options, describe_usage, 0);
-       if (max_candidates < 1)
-               max_candidates = 1;
+       if (max_candidates < 0)
+               max_candidates = 0;
        else if (max_candidates > MAX_TAGS)
                max_candidates = MAX_TAGS;
 
        save_commit_buffer = 0;
 
        if (contains) {
-               const char **args = xmalloc((4 + argc) * sizeof(char*));
+               const char **args = xmalloc((6 + argc) * sizeof(char*));
                int i = 0;
                args[i++] = "name-rev";
                args[i++] = "--name-only";
-               if (!all)
+               args[i++] = "--no-undefined";
+               if (!all) {
                        args[i++] = "--tags";
+                       if (pattern) {
+                               char *s = xmalloc(strlen("--refs=refs/tags/") + strlen(pattern) + 1);
+                               sprintf(s, "--refs=refs/tags/%s", pattern);
+                               args[i++] = s;
+                       }
+               }
                memcpy(args + i, argv, argc * sizeof(char*));
                args[i + argc] = NULL;
                return cmd_name_rev(i + argc, args, prefix);
index 8d7a569..444ff2f 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright (c) 2006 Junio C Hamano
  */
 #include "cache.h"
+#include "color.h"
 #include "commit.h"
 #include "blob.h"
 #include "tag.h"
@@ -43,12 +44,17 @@ static void stuff_change(struct diff_options *opt,
                tmp_u = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_u;
                tmp_c = old_name; old_name = new_name; new_name = tmp_c;
        }
+
+       if (opt->prefix &&
+           (strncmp(old_name, opt->prefix, opt->prefix_length) ||
+            strncmp(new_name, opt->prefix, opt->prefix_length)))
+               return;
+
        one = alloc_filespec(old_name);
        two = alloc_filespec(new_name);
        fill_filespec(one, old_sha1, old_mode);
        fill_filespec(two, new_sha1, new_mode);
 
-       /* NEEDSWORK: shouldn't this part of diffopt??? */
        diff_queue(&diff_queued_diff, one, two);
 }
 
@@ -229,6 +235,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
 
        prefix = setup_git_directory_gently(&nongit);
        git_config(git_diff_ui_config);
+
+       if (diff_use_color_default == -1)
+               diff_use_color_default = git_use_color_default;
+
        init_revisions(&rev, prefix);
        rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index;
 
@@ -241,6 +251,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
                if (diff_setup_done(&rev.diffopt) < 0)
                        die("diff_setup_done failed");
        }
+       if (rev.diffopt.prefix && nongit) {
+               rev.diffopt.prefix = NULL;
+               rev.diffopt.prefix_length = 0;
+       }
        DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
        DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
 
index ef27eee..4bd1356 100755 (executable)
@@ -123,7 +123,7 @@ static void show_filemodify(struct diff_queue_struct *q,
                        printf("D %s\n", spec->path);
                else {
                        struct object *object = lookup_object(spec->sha1);
-                       printf("M 0%06o :%d %s\n", spec->mode,
+                       printf("M %06o :%d %s\n", spec->mode,
                               get_object_mark(object), spec->path);
                }
        }
@@ -383,7 +383,8 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
 
        get_tags_and_duplicates(&revs.pending, &extra_refs);
 
-       prepare_revision_walk(&revs);
+       if (prepare_revision_walk(&revs))
+               die("revision walk setup failed");
        revs.diffopt.format_callback = show_filemodify;
        DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
        while ((commit = get_revision(&revs))) {
index e68e015..5ea48ca 100644 (file)
@@ -7,6 +7,7 @@
 #include "pack.h"
 #include "sideband.h"
 #include "fetch-pack.h"
+#include "remote.h"
 #include "run-command.h"
 
 static int transfer_unpack_limit = -1;
@@ -537,8 +538,10 @@ static int get_pack(int xd[2], char **pack_lockfile)
        cmd.git_cmd = 1;
        if (start_command(&cmd))
                die("fetch-pack: unable to fork off %s", argv[0]);
-       if (do_keep && pack_lockfile)
+       if (do_keep && pack_lockfile) {
                *pack_lockfile = index_pack_lockfile(cmd.out);
+               close(cmd.out);
+       }
 
        if (finish_command(&cmd))
                die("%s failed", argv[0]);
@@ -548,14 +551,14 @@ static int get_pack(int xd[2], char **pack_lockfile)
 }
 
 static struct ref *do_fetch_pack(int fd[2],
+               const struct ref *orig_ref,
                int nr_match,
                char **match,
                char **pack_lockfile)
 {
-       struct ref *ref;
+       struct ref *ref = copy_ref_list(orig_ref);
        unsigned char sha1[20];
 
-       get_remote_heads(fd[0], &ref, 0, NULL, 0);
        if (is_repository_shallow() && !server_supports("shallow"))
                die("Server does not support shallow clients");
        if (server_supports("multi_ack")) {
@@ -573,10 +576,6 @@ static struct ref *do_fetch_pack(int fd[2],
                        fprintf(stderr, "Server supports side-band\n");
                use_sideband = 1;
        }
-       if (!ref) {
-               packet_flush(fd[1]);
-               die("no matching remote head");
-       }
        if (everything_local(&ref, nr_match, match)) {
                packet_flush(fd[1]);
                goto all_done;
@@ -650,8 +649,10 @@ static void fetch_pack_setup(void)
 int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 {
        int i, ret, nr_heads;
-       struct ref *ref;
+       struct ref *ref = NULL;
        char *dest = NULL, **heads;
+       int fd[2];
+       struct child_process *conn;
 
        nr_heads = 0;
        heads = NULL;
@@ -706,9 +707,33 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        if (!dest)
                usage(fetch_pack_usage);
 
-       ref = fetch_pack(&args, dest, nr_heads, heads, NULL);
+       conn = git_connect(fd, (char *)dest, args.uploadpack,
+                          args.verbose ? CONNECT_VERBOSE : 0);
+       if (conn) {
+               get_remote_heads(fd[0], &ref, 0, NULL, 0);
+
+               ref = fetch_pack(&args, fd, conn, ref, dest, nr_heads, heads, NULL);
+               close(fd[0]);
+               close(fd[1]);
+               if (finish_connect(conn))
+                       ref = NULL;
+       } else {
+               ref = NULL;
+       }
        ret = !ref;
 
+       if (!ret && nr_heads) {
+               /* If the heads to pull were given, we should have
+                * consumed all of them by matching the remote.
+                * Otherwise, 'git-fetch remote no-such-ref' would
+                * silently succeed without issuing an error.
+                */
+               for (i = 0; i < nr_heads; i++)
+                       if (heads[i] && heads[i][0]) {
+                               error("no such remote ref %s", heads[i]);
+                               ret = 1;
+                       }
+       }
        while (ref) {
                printf("%s %s\n",
                       sha1_to_hex(ref->old_sha1), ref->name);
@@ -719,16 +744,15 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 }
 
 struct ref *fetch_pack(struct fetch_pack_args *my_args,
+                      int fd[], struct child_process *conn,
+                      const struct ref *ref,
                const char *dest,
                int nr_heads,
                char **heads,
                char **pack_lockfile)
 {
-       int i, ret;
-       int fd[2];
-       struct child_process *conn;
-       struct ref *ref;
        struct stat st;
+       struct ref *ref_cpy;
 
        fetch_pack_setup();
        memcpy(&args, my_args, sizeof(args));
@@ -737,29 +761,15 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
                        st.st_mtime = 0;
        }
 
-       conn = git_connect(fd, (char *)dest, args.uploadpack,
-                          args.verbose ? CONNECT_VERBOSE : 0);
        if (heads && nr_heads)
                nr_heads = remove_duplicates(nr_heads, heads);
-       ref = do_fetch_pack(fd, nr_heads, heads, pack_lockfile);
-       close(fd[0]);
-       close(fd[1]);
-       ret = finish_connect(conn);
-
-       if (!ret && nr_heads) {
-               /* If the heads to pull were given, we should have
-                * consumed all of them by matching the remote.
-                * Otherwise, 'git-fetch remote no-such-ref' would
-                * silently succeed without issuing an error.
-                */
-               for (i = 0; i < nr_heads; i++)
-                       if (heads[i] && heads[i][0]) {
-                               error("no such remote ref %s", heads[i]);
-                               ret = 1;
-                       }
+       if (!ref) {
+               packet_flush(fd[1]);
+               die("no matching remote head");
        }
+       ref_cpy = do_fetch_pack(fd, ref, nr_heads, heads, pack_lockfile);
 
-       if (!ret && args.depth > 0) {
+       if (args.depth > 0) {
                struct cache_time mtime;
                char *shallow = git_path("shallow");
                int fd;
@@ -787,8 +797,5 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
                }
        }
 
-       if (ret)
-               ref = NULL;
-
-       return ref;
+       return ref_cpy;
 }
index 320e235..ac335f2 100644 (file)
@@ -557,6 +557,8 @@ static int do_fetch(struct transport *transport,
 
        free_refs(fetch_map);
 
+       transport_disconnect(transport);
+
        return 0;
 }
 
index 6163bd4..ebb3f37 100644 (file)
@@ -187,7 +187,8 @@ static void shortlog(const char *name, unsigned char *sha1,
        add_pending_object(rev, branch, name);
        add_pending_object(rev, &head->object, "^HEAD");
        head->object.flags |= UNINTERESTING;
-       prepare_revision_walk(rev);
+       if (prepare_revision_walk(rev))
+               die("revision walk setup failed");
        while ((commit = get_revision(rev)) != NULL) {
                char *oneline, *bol, *eol;
 
index f36a43c..07d9c57 100644 (file)
@@ -165,7 +165,7 @@ static int verify_format(const char *format)
        for (cp = format; *cp && (sp = find_next(cp)); ) {
                const char *ep = strchr(sp, ')');
                if (!ep)
-                       return error("malformatted format string %s", sp);
+                       return error("malformed format string %s", sp);
                /* sp points at "%(" and ep points at the closing ")" */
                parse_atom(sp + 2, ep);
                cp = ep + 1;
index 6fc9525..cc7524b 100644 (file)
@@ -360,6 +360,9 @@ static int fsck_commit(struct commit *commit)
                fprintf(stderr, "Checking commit %s\n",
                        sha1_to_hex(commit->object.sha1));
 
+       if (!commit->date)
+               return objerror(&commit->object, "invalid author/committer line");
+
        if (memcmp(buffer, "tree ", 5))
                return objerror(&commit->object, "invalid format - expected 'tree' line");
        if (get_sha1_hex(buffer+5, tree_sha1) || buffer[45] != '\n')
@@ -378,9 +381,6 @@ static int fsck_commit(struct commit *commit)
                return objerror(&commit->object, "could not load commit's tree %s", tree_sha1);
        if (!commit->parents && show_root)
                printf("root %s\n", sha1_to_hex(commit->object.sha1));
-       if (!commit->date)
-               printf("bad commit date in %s\n",
-                      sha1_to_hex(commit->object.sha1));
        return 0;
 }
 
index ac34788..ad4a75e 100644 (file)
@@ -37,7 +37,7 @@ static const char *argv_rerere[] = {"rerere", "gc", NULL};
 static int gc_config(const char *var, const char *value)
 {
        if (!strcmp(var, "gc.packrefs")) {
-               if (!strcmp(value, "notbare"))
+               if (value && !strcmp(value, "notbare"))
                        pack_refs = -1;
                else
                        pack_refs = git_config_bool(var, value);
index 9180b39..f4f4ecb 100644 (file)
@@ -578,6 +578,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        continue;
                }
                if (!strcmp("-l", arg) ||
+                   !strcmp("--name-only", arg) ||
                    !strcmp("--files-with-matches", arg)) {
                        opt.name_only = 1;
                        continue;
index e1393b8..79eaf8d 100644 (file)
@@ -29,27 +29,6 @@ static void safe_create_dir(const char *dir, int share)
                die("Could not make %s writable by group\n", dir);
 }
 
-static int copy_file(const char *dst, const char *src, int mode)
-{
-       int fdi, fdo, status;
-
-       mode = (mode & 0111) ? 0777 : 0666;
-       if ((fdi = open(src, O_RDONLY)) < 0)
-               return fdi;
-       if ((fdo = open(dst, O_WRONLY | O_CREAT | O_EXCL, mode)) < 0) {
-               close(fdi);
-               return fdo;
-       }
-       status = copy_fd(fdi, fdo);
-       if (close(fdo) != 0)
-               return error("%s: write error: %s", dst, strerror(errno));
-
-       if (!status && adjust_shared_perm(dst))
-               return -1;
-
-       return status;
-}
-
 static void copy_templates_1(char *path, int baselen,
                             char *template, int template_baselen,
                             DIR *dir)
@@ -141,9 +120,9 @@ static void copy_templates(const char *git_dir, int len, const char *template_di
                 */
                template_dir = DEFAULT_GIT_TEMPLATE_DIR;
                if (!is_absolute_path(template_dir)) {
-                       const char *exec_path = git_exec_path();
-                       template_dir = prefix_path(exec_path, strlen(exec_path),
-                                                  template_dir);
+                       struct strbuf d = STRBUF_INIT;
+                       strbuf_addf(&d, "%s/%s", git_exec_path(), template_dir);
+                       template_dir = strbuf_detach(&d, NULL);
                }
        }
        strcpy(template_path, template_dir);
index dcc9f81..3209ea5 100644 (file)
@@ -5,6 +5,7 @@
  *              2006 Junio Hamano
  */
 #include "cache.h"
+#include "color.h"
 #include "commit.h"
 #include "diff.h"
 #include "revision.h"
@@ -14,6 +15,8 @@
 #include "reflog-walk.h"
 #include "patch-ids.h"
 #include "refs.h"
+#include "run-command.h"
+#include "shortlog.h"
 
 static int default_show_root = 1;
 static const char *fmt_patch_subject_prefix = "PATCH";
@@ -197,7 +200,8 @@ static int cmd_log_walk(struct rev_info *rev)
        if (rev->early_output)
                setup_early_output(rev);
 
-       prepare_revision_walk(rev);
+       if (prepare_revision_walk(rev))
+               die("revision walk setup failed");
 
        if (rev->early_output)
                finish_early_output(rev);
@@ -219,7 +223,7 @@ static int git_log_config(const char *var, const char *value)
 {
        if (!strcmp(var, "format.subjectprefix")) {
                if (!value)
-                       die("format.subjectprefix without value");
+                       config_error_nonbool(var);
                fmt_patch_subject_prefix = xstrdup(value);
                return 0;
        }
@@ -235,6 +239,10 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
        struct rev_info rev;
 
        git_config(git_log_config);
+
+       if (diff_use_color_default == -1)
+               diff_use_color_default = git_use_color_default;
+
        init_revisions(&rev, prefix);
        rev.diff = 1;
        rev.simplify_history = 0;
@@ -307,6 +315,10 @@ int cmd_show(int argc, const char **argv, const char *prefix)
        int i, count, ret = 0;
 
        git_config(git_log_config);
+
+       if (diff_use_color_default == -1)
+               diff_use_color_default = git_use_color_default;
+
        init_revisions(&rev, prefix);
        rev.diff = 1;
        rev.combine_merges = 1;
@@ -367,6 +379,10 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
        struct rev_info rev;
 
        git_config(git_log_config);
+
+       if (diff_use_color_default == -1)
+               diff_use_color_default = git_use_color_default;
+
        init_revisions(&rev, prefix);
        init_reflog_walk(&rev.reflog_info);
        rev.abbrev_commit = 1;
@@ -395,6 +411,10 @@ int cmd_log(int argc, const char **argv, const char *prefix)
        struct rev_info rev;
 
        git_config(git_log_config);
+
+       if (diff_use_color_default == -1)
+               diff_use_color_default = git_use_color_default;
+
        init_revisions(&rev, prefix);
        rev.always_show_header = 1;
        cmd_log_init(argc, argv, prefix, &rev);
@@ -410,29 +430,52 @@ static int istitlechar(char c)
                (c >= '0' && c <= '9') || c == '.' || c == '_';
 }
 
-static char *extra_headers = NULL;
-static int extra_headers_size = 0;
 static const char *fmt_patch_suffix = ".patch";
 static int numbered = 0;
 static int auto_number = 0;
 
+static char **extra_hdr;
+static int extra_hdr_nr;
+static int extra_hdr_alloc;
+
+static char **extra_to;
+static int extra_to_nr;
+static int extra_to_alloc;
+
+static char **extra_cc;
+static int extra_cc_nr;
+static int extra_cc_alloc;
+
+static void add_header(const char *value)
+{
+       int len = strlen(value);
+       while (value[len - 1] == '\n')
+               len--;
+       if (!strncasecmp(value, "to: ", 4)) {
+               ALLOC_GROW(extra_to, extra_to_nr + 1, extra_to_alloc);
+               extra_to[extra_to_nr++] = xstrndup(value + 4, len - 4);
+               return;
+       }
+       if (!strncasecmp(value, "cc: ", 4)) {
+               ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc);
+               extra_cc[extra_cc_nr++] = xstrndup(value + 4, len - 4);
+               return;
+       }
+       ALLOC_GROW(extra_hdr, extra_hdr_nr + 1, extra_hdr_alloc);
+       extra_hdr[extra_hdr_nr++] = xstrndup(value, len);
+}
+
 static int git_format_config(const char *var, const char *value)
 {
        if (!strcmp(var, "format.headers")) {
-               int len;
-
                if (!value)
                        die("format.headers without value");
-               len = strlen(value);
-               extra_headers_size += len + 1;
-               extra_headers = xrealloc(extra_headers, extra_headers_size);
-               extra_headers[extra_headers_size - len - 1] = 0;
-               strcat(extra_headers, value);
+               add_header(value);
                return 0;
        }
        if (!strcmp(var, "format.suffix")) {
                if (!value)
-                       die("format.suffix without value");
+                       return config_error_nonbool(var);
                fmt_patch_suffix = xstrdup(value);
                return 0;
        }
@@ -440,11 +483,10 @@ static int git_format_config(const char *var, const char *value)
                return 0;
        }
        if (!strcmp(var, "format.numbered")) {
-               if (!strcasecmp(value, "auto")) {
+               if (value && !strcasecmp(value, "auto")) {
                        auto_number = 1;
                        return 0;
                }
-
                numbered = git_config_bool(var, value);
                return 0;
        }
@@ -453,74 +495,81 @@ static int git_format_config(const char *var, const char *value)
 }
 
 
+static const char *get_oneline_for_filename(struct commit *commit,
+                                           int keep_subject)
+{
+       static char filename[PATH_MAX];
+       char *sol;
+       int len = 0;
+       int suffix_len = strlen(fmt_patch_suffix) + 1;
+
+       sol = strstr(commit->buffer, "\n\n");
+       if (!sol)
+               filename[0] = '\0';
+       else {
+               int j, space = 0;
+
+               sol += 2;
+               /* strip [PATCH] or [PATCH blabla] */
+               if (!keep_subject && !prefixcmp(sol, "[PATCH")) {
+                       char *eos = strchr(sol + 6, ']');
+                       if (eos) {
+                               while (isspace(*eos))
+                                       eos++;
+                               sol = eos;
+                       }
+               }
+
+               for (j = 0;
+                    j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 &&
+                            len < sizeof(filename) - suffix_len &&
+                            sol[j] && sol[j] != '\n';
+                    j++) {
+                       if (istitlechar(sol[j])) {
+                               if (space) {
+                                       filename[len++] = '-';
+                                       space = 0;
+                               }
+                               filename[len++] = sol[j];
+                               if (sol[j] == '.')
+                                       while (sol[j + 1] == '.')
+                                               j++;
+                       } else
+                               space = 1;
+               }
+               while (filename[len - 1] == '.'
+                      || filename[len - 1] == '-')
+                       len--;
+               filename[len] = '\0';
+       }
+       return filename;
+}
+
 static FILE *realstdout = NULL;
 static const char *output_directory = NULL;
 
-static int reopen_stdout(struct commit *commit, int nr, int keep_subject,
-                        int numbered_files)
+static int reopen_stdout(const char *oneline, int nr, int total)
 {
        char filename[PATH_MAX];
-       char *sol;
        int len = 0;
        int suffix_len = strlen(fmt_patch_suffix) + 1;
 
        if (output_directory) {
-               if (strlen(output_directory) >=
+               len = snprintf(filename, sizeof(filename), "%s",
+                               output_directory);
+               if (len >=
                    sizeof(filename) - FORMAT_PATCH_NAME_MAX - suffix_len)
                        return error("name of output directory is too long");
-               strlcpy(filename, output_directory, sizeof(filename) - suffix_len);
-               len = strlen(filename);
                if (filename[len - 1] != '/')
                        filename[len++] = '/';
        }
 
-       if (numbered_files) {
-               sprintf(filename + len, "%d", nr);
-               len = strlen(filename);
-
-       } else {
-               sprintf(filename + len, "%04d", nr);
-               len = strlen(filename);
-
-               sol = strstr(commit->buffer, "\n\n");
-               if (sol) {
-                       int j, space = 1;
-
-                       sol += 2;
-                       /* strip [PATCH] or [PATCH blabla] */
-                       if (!keep_subject && !prefixcmp(sol, "[PATCH")) {
-                               char *eos = strchr(sol + 6, ']');
-                               if (eos) {
-                                       while (isspace(*eos))
-                                               eos++;
-                                       sol = eos;
-                               }
-                       }
-
-                       for (j = 0;
-                            j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 &&
-                                    len < sizeof(filename) - suffix_len &&
-                                    sol[j] && sol[j] != '\n';
-                            j++) {
-                               if (istitlechar(sol[j])) {
-                                       if (space) {
-                                               filename[len++] = '-';
-                                               space = 0;
-                                       }
-                                       filename[len++] = sol[j];
-                                       if (sol[j] == '.')
-                                               while (sol[j + 1] == '.')
-                                                       j++;
-                               } else
-                                       space = 1;
-                       }
-                       while (filename[len - 1] == '.'
-                              || filename[len - 1] == '-')
-                               len--;
-                       filename[len] = 0;
-               }
-               if (len + suffix_len >= sizeof(filename))
-                       return error("Patch pathname too long");
+       if (!oneline)
+               len += sprintf(filename + len, "%d", nr);
+       else {
+               len += sprintf(filename + len, "%04d-", nr);
+               len += snprintf(filename + len, sizeof(filename) - len - 1
+                               - suffix_len, "%s", oneline);
                strcpy(filename + len, fmt_patch_suffix);
        }
 
@@ -557,7 +606,8 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha
        o2->flags ^= UNINTERESTING;
        add_pending_object(&check_rev, o1, "o1");
        add_pending_object(&check_rev, o2, "o2");
-       prepare_revision_walk(&check_rev);
+       if (prepare_revision_walk(&check_rev))
+               die("revision walk setup failed");
 
        while ((commit = get_revision(&check_rev)) != NULL) {
                /* ignore merges */
@@ -576,16 +626,89 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha
        o2->flags = flags2;
 }
 
-static void gen_message_id(char *dest, unsigned int length, char *base)
+static void gen_message_id(struct rev_info *info, char *base)
 {
        const char *committer = git_committer_info(IDENT_WARN_ON_NO_NAME);
        const char *email_start = strrchr(committer, '<');
        const char *email_end = strrchr(committer, '>');
-       if(!email_start || !email_end || email_start > email_end - 1)
+       struct strbuf buf;
+       if (!email_start || !email_end || email_start > email_end - 1)
                die("Could not extract email from committer identity.");
-       snprintf(dest, length, "%s.%lu.git.%.*s", base,
-                (unsigned long) time(NULL),
-                (int)(email_end - email_start - 1), email_start + 1);
+       strbuf_init(&buf, 0);
+       strbuf_addf(&buf, "%s.%lu.git.%.*s", base,
+                   (unsigned long) time(NULL),
+                   (int)(email_end - email_start - 1), email_start + 1);
+       info->message_id = strbuf_detach(&buf, NULL);
+}
+
+static void make_cover_letter(struct rev_info *rev, int use_stdout,
+                             int numbered, int numbered_files,
+                             struct commit *origin,
+                             int nr, struct commit **list, struct commit *head)
+{
+       const char *committer;
+       const char *origin_sha1, *head_sha1;
+       const char *argv[7];
+       const char *subject_start = NULL;
+       const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n";
+       const char *msg;
+       const char *extra_headers = rev->extra_headers;
+       struct shortlog log;
+       struct strbuf sb;
+       int i;
+       const char *encoding = "utf-8";
+
+       if (rev->commit_format != CMIT_FMT_EMAIL)
+               die("Cover letter needs email format");
+
+       if (!use_stdout && reopen_stdout(numbered_files ?
+                               NULL : "cover-letter", 0, rev->total))
+               return;
+
+       head_sha1 = sha1_to_hex(head->object.sha1);
+
+       log_write_email_headers(rev, head_sha1, &subject_start, &extra_headers);
+
+       committer = git_committer_info(0);
+
+       msg = body;
+       strbuf_init(&sb, 0);
+       pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822,
+                    encoding);
+       pp_title_line(CMIT_FMT_EMAIL, &msg, &sb, subject_start, extra_headers,
+                     encoding, 0);
+       pp_remainder(CMIT_FMT_EMAIL, &msg, &sb, 0);
+       printf("%s\n", sb.buf);
+
+       strbuf_release(&sb);
+
+       shortlog_init(&log);
+       for (i = 0; i < nr; i++)
+               shortlog_add_commit(&log, list[i]);
+
+       shortlog_output(&log);
+
+       /*
+        * We can only do diffstat with a unique reference point
+        */
+       if (!origin)
+               return;
+
+       origin_sha1 = sha1_to_hex(origin->object.sha1);
+
+       argv[0] = "diff";
+       argv[1] = "--stat";
+       argv[2] = "--summary";
+       argv[3] = head_sha1;
+       argv[4] = "--not";
+       argv[5] = origin_sha1;
+       argv[6] = "--";
+       argv[7] = NULL;
+       fflush(stdout);
+       run_command_v_opt(argv, RUN_GIT_CMD);
+
+       fflush(stdout);
+       printf("\n");
 }
 
 static const char *clean_message_id(const char *msg_id)
@@ -623,11 +746,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        int subject_prefix = 0;
        int ignore_if_in_upstream = 0;
        int thread = 0;
+       int cover_letter = 0;
+       int boundary_count = 0;
+       struct commit *origin = NULL, *head = NULL;
        const char *in_reply_to = NULL;
        struct patch_ids ids;
        char *add_signoff = NULL;
-       char message_id[1024];
-       char ref_message_id[1024];
+       struct strbuf buf;
 
        git_config(git_format_config);
        init_revisions(&rev, prefix);
@@ -640,7 +765,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
 
        rev.subject_prefix = fmt_patch_subject_prefix;
-       rev.extra_headers = extra_headers;
 
        /*
         * Parse the arguments before setup_revisions(), or something
@@ -668,6 +792,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                                die("Need a number for --start-number");
                        start_number = strtol(argv[i], NULL, 10);
                }
+               else if (!prefixcmp(argv[i], "--cc=")) {
+                       ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc);
+                       extra_cc[extra_cc_nr++] = xstrdup(argv[i] + 5);
+               }
                else if (!strcmp(argv[i], "-k") ||
                                !strcmp(argv[i], "--keep-subject")) {
                        keep_subject = 1;
@@ -724,11 +852,44 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        rev.subject_prefix = argv[i] + 17;
                } else if (!prefixcmp(argv[i], "--suffix="))
                        fmt_patch_suffix = argv[i] + 9;
+               else if (!strcmp(argv[i], "--cover-letter"))
+                       cover_letter = 1;
                else
                        argv[j++] = argv[i];
        }
        argc = j;
 
+       strbuf_init(&buf, 0);
+
+       for (i = 0; i < extra_hdr_nr; i++) {
+               strbuf_addstr(&buf, extra_hdr[i]);
+               strbuf_addch(&buf, '\n');
+       }
+
+       if (extra_to_nr)
+               strbuf_addstr(&buf, "To: ");
+       for (i = 0; i < extra_to_nr; i++) {
+               if (i)
+                       strbuf_addstr(&buf, "    ");
+               strbuf_addstr(&buf, extra_to[i]);
+               if (i + 1 < extra_to_nr)
+                       strbuf_addch(&buf, ',');
+               strbuf_addch(&buf, '\n');
+       }
+
+       if (extra_cc_nr)
+               strbuf_addstr(&buf, "Cc: ");
+       for (i = 0; i < extra_cc_nr; i++) {
+               if (i)
+                       strbuf_addstr(&buf, "    ");
+               strbuf_addstr(&buf, extra_cc[i]);
+               if (i + 1 < extra_cc_nr)
+                       strbuf_addch(&buf, ',');
+               strbuf_addch(&buf, '\n');
+       }
+
+       rev.extra_headers = strbuf_detach(&buf, 0);
+
        if (start_number < 0)
                start_number = 1;
        if (numbered && keep_subject)
@@ -775,6 +936,18 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                 * get_revision() to do the usual traversal.
                 */
        }
+       if (cover_letter) {
+               /* remember the range */
+               int i;
+               for (i = 0; i < rev.pending.nr; i++) {
+                       struct object *o = rev.pending.objects[i].item;
+                       if (!(o->flags & UNINTERESTING))
+                               head = (struct commit *)o;
+               }
+               /* We can't generate a cover letter without any patches */
+               if (!head)
+                       return 0;
+       }
 
        if (ignore_if_in_upstream)
                get_patch_ids(&rev, &ids, prefix);
@@ -782,8 +955,17 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        if (!use_stdout)
                realstdout = xfdopen(xdup(1), "w");
 
-       prepare_revision_walk(&rev);
+       if (prepare_revision_walk(&rev))
+               die("revision walk setup failed");
+       rev.boundary = 1;
        while ((commit = get_revision(&rev)) != NULL) {
+               if (commit->object.flags & BOUNDARY) {
+                       fprintf(stderr, "Boundary %s\n", sha1_to_hex(commit->object.sha1));
+                       boundary_count++;
+                       origin = (boundary_count == 1) ? commit : NULL;
+                       continue;
+               }
+
                /* ignore merges */
                if (commit->parents && commit->parents->next)
                        continue;
@@ -801,29 +983,42 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                numbered = 1;
        if (numbered)
                rev.total = total + start_number - 1;
-       rev.add_signoff = add_signoff;
        if (in_reply_to)
                rev.ref_message_id = clean_message_id(in_reply_to);
+       if (cover_letter) {
+               if (thread)
+                       gen_message_id(&rev, "cover");
+               make_cover_letter(&rev, use_stdout, numbered, numbered_files,
+                                 origin, nr, list, head);
+               total++;
+               start_number--;
+       }
+       rev.add_signoff = add_signoff;
        while (0 <= --nr) {
                int shown;
                commit = list[nr];
                rev.nr = total - nr + (start_number - 1);
                /* Make the second and subsequent mails replies to the first */
                if (thread) {
-                       if (nr == (total - 2)) {
-                               strncpy(ref_message_id, message_id,
-                                       sizeof(ref_message_id));
-                               ref_message_id[sizeof(ref_message_id)-1]='\0';
-                               rev.ref_message_id = ref_message_id;
+                       /* Have we already had a message ID? */
+                       if (rev.message_id) {
+                               /*
+                                * If we've got the ID to be a reply
+                                * to, discard the current ID;
+                                * otherwise, make everything a reply
+                                * to that.
+                                */
+                               if (rev.ref_message_id)
+                                       free(rev.message_id);
+                               else
+                                       rev.ref_message_id = rev.message_id;
                        }
-                       gen_message_id(message_id, sizeof(message_id),
-                                      sha1_to_hex(commit->object.sha1));
-                       rev.message_id = message_id;
+                       gen_message_id(&rev, sha1_to_hex(commit->object.sha1));
                }
-               if (!use_stdout)
-                       if (reopen_stdout(commit, rev.nr, keep_subject,
-                                         numbered_files))
-                               die("Failed to create output files");
+               if (!use_stdout && reopen_stdout(numbered_files ? NULL :
+                               get_oneline_for_filename(commit, keep_subject),
+                               rev.nr, rev.total))
+                       die("Failed to create output files");
                shown = log_tree_commit(&rev, commit);
                free(commit->buffer);
                commit->buffer = NULL;
@@ -924,7 +1119,8 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
                die("Unknown commit %s", limit);
 
        /* reverse the list of commits */
-       prepare_revision_walk(&revs);
+       if (prepare_revision_walk(&revs))
+               die("revision walk setup failed");
        while ((commit = get_revision(&revs)) != NULL) {
                /* ignore merges */
                if (commit->parents && commit->parents->next)
index d56e33e..25dbfb4 100644 (file)
@@ -238,7 +238,8 @@ static void show_files(struct dir_struct *dir, const char *prefix)
        if (show_cached | show_stage) {
                for (i = 0; i < active_nr; i++) {
                        struct cache_entry *ce = active_cache[i];
-                       if (excluded(dir, ce->name) != dir->show_ignored)
+                       int dtype = ce_to_dtype(ce);
+                       if (excluded(dir, ce->name, &dtype) != dir->show_ignored)
                                continue;
                        if (show_unmerged && !ce_stage(ce))
                                continue;
@@ -252,7 +253,8 @@ static void show_files(struct dir_struct *dir, const char *prefix)
                        struct cache_entry *ce = active_cache[i];
                        struct stat st;
                        int err;
-                       if (excluded(dir, ce->name) != dir->show_ignored)
+                       int dtype = ce_to_dtype(ce);
+                       if (excluded(dir, ce->name, &dtype) != dir->show_ignored)
                                continue;
                        err = lstat(ce->name, &st);
                        if (show_deleted && err)
@@ -572,8 +574,17 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
        pathspec = get_pathspec(prefix, argv + i);
 
        /* Verify that the pathspec matches the prefix */
-       if (pathspec)
+       if (pathspec) {
+               if (argc != i) {
+                       int cnt;
+                       for (cnt = 0; pathspec[cnt]; cnt++)
+                               ;
+                       if (cnt != (argc - i))
+                               exit(1); /* error message already given */
+               }
                prefix = verify_pathspec(prefix);
+       } else if (argc != i)
+               exit(1); /* error message already given */
 
        /* Treat unmatching pathspec elements as errors */
        if (pathspec && error_unmatch) {
index 6dd31d1..0237549 100644 (file)
@@ -94,6 +94,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
                transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack);
 
        ref = transport_get_remote_refs(transport);
+       transport_disconnect(transport);
 
        if (!ref)
                return 1;
index 2600847..11f154b 100644 (file)
@@ -818,6 +818,7 @@ static void handle_body(void)
 
                switch (transfer_encoding) {
                case TE_BASE64:
+               case TE_QP:
                {
                        char *op = line;
 
index 58deb62..adce6d4 100644 (file)
@@ -46,7 +46,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
        }
 
        ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2],
-                       &xpp, XDL_MERGE_ZEALOUS, &result);
+                       &xpp, XDL_MERGE_ZEALOUS_ALNUM, &result);
 
        for (i = 0; i < 3; i++)
                free(mmfs[i].ptr);
index 558a58e..6fe4102 100644 (file)
@@ -835,8 +835,9 @@ static int read_merge_config(const char *var, const char *value)
        int namelen;
 
        if (!strcmp(var, "merge.default")) {
-               if (value)
-                       default_ll_merge = strdup(value);
+               if (!value)
+                       return config_error_nonbool(var);
+               default_ll_merge = strdup(value);
                return 0;
        }
 
@@ -869,14 +870,14 @@ static int read_merge_config(const char *var, const char *value)
 
        if (!strcmp("name", ep)) {
                if (!value)
-                       return error("%s: lacks value", var);
+                       return config_error_nonbool(var);
                fn->description = strdup(value);
                return 0;
        }
 
        if (!strcmp("driver", ep)) {
                if (!value)
-                       return error("%s: lacks value", var);
+                       return config_error_nonbool(var);
                /*
                 * merge.<name>.driver specifies the command line:
                 *
@@ -899,7 +900,7 @@ static int read_merge_config(const char *var, const char *value)
 
        if (!strcmp("recursive", ep)) {
                if (!value)
-                       return error("%s: lacks value", var);
+                       return config_error_nonbool(var);
                fn->recursive = strdup(value);
                return 0;
        }
@@ -1663,6 +1664,8 @@ static struct commit *get_ref(const char *ref)
        if (get_sha1(ref, sha1))
                die("Could not resolve ref '%s'", ref);
        object = deref_tag(parse_object(sha1), ref, strlen(ref));
+       if (!object)
+               return NULL;
        if (object->type == OBJ_TREE)
                return make_virtual_commit((struct tree*)object,
                        better_branch_name(ref));
index 990e213..68aa2a6 100644 (file)
@@ -19,6 +19,7 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec,
                                  int count, int base_name)
 {
        int i;
+       int len = prefix ? strlen(prefix) : 0;
        const char **result = xmalloc((count + 1) * sizeof(const char *));
        memcpy(result, pathspec, count * sizeof(const char *));
        result[count] = NULL;
@@ -32,8 +33,11 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec,
                        if (last_slash)
                                result[i] = last_slash + 1;
                }
+               result[i] = prefix_path(prefix, len, result[i]);
+               if (!result[i])
+                       exit(1); /* error already given */
        }
-       return get_pathspec(prefix, result);
+       return result;
 }
 
 static void show_list(const char *label, struct path_list *list)
@@ -164,7 +168,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
                                }
 
                                dst = add_slash(dst);
-                               dst_len = strlen(dst) - 1;
+                               dst_len = strlen(dst);
 
                                for (j = 0; j < last - first; j++) {
                                        const char *path =
@@ -172,7 +176,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
                                        source[argc + j] = path;
                                        destination[argc + j] =
                                                prefix_path(dst, dst_len,
-                                                       path + length);
+                                                       path + length + 1);
                                        modes[argc + j] = INDEX;
                                }
                                argc += last - first;
index a0c89a8..f22c8b5 100644 (file)
@@ -125,18 +125,18 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void
 }
 
 /* returns a static buffer */
-static const charget_rev_name(struct object *o)
+static const char *get_rev_name(struct object *o)
 {
        static char buffer[1024];
        struct rev_name *n;
        struct commit *c;
 
        if (o->type != OBJ_COMMIT)
-               return "undefined";
+               return NULL;
        c = (struct commit *) o;
        n = c->util;
        if (!n)
-               return "undefined";
+               return NULL;
 
        if (!n->generation)
                return n->tip_name;
@@ -159,7 +159,7 @@ static char const * const name_rev_usage[] = {
 int cmd_name_rev(int argc, const char **argv, const char *prefix)
 {
        struct object_array revs = { 0, 0, NULL };
-       int all = 0, transform_stdin = 0;
+       int all = 0, transform_stdin = 0, allow_undefined = 1;
        struct name_ref_data data = { 0, 0, NULL };
        struct option opts[] = {
                OPT_BOOLEAN(0, "name-only", &data.name_only, "print only names (no SHA-1)"),
@@ -169,6 +169,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                OPT_GROUP(""),
                OPT_BOOLEAN(0, "all", &all, "list all commits reachable from all refs"),
                OPT_BOOLEAN(0, "stdin", &transform_stdin, "read from stdin"),
+               OPT_BOOLEAN(0, "undefined", &allow_undefined, "allow to print `undefined` names"),
                OPT_END(),
        };
 
@@ -226,7 +227,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                                else if (++forty == 40 &&
                                                !ishex(*(p+1))) {
                                        unsigned char sha1[40];
-                                       const char *name = "undefined";
+                                       const char *name = NULL;
                                        char c = *(p+1);
 
                                        forty = 0;
@@ -240,11 +241,10 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                                        }
                                        *(p+1) = c;
 
-                                       if (!strcmp(name, "undefined"))
+                                       if (!name)
                                                continue;
 
-                                       fwrite(p_start, p - p_start + 1, 1,
-                                              stdout);
+                                       fwrite(p_start, p - p_start + 1, 1, stdout);
                                        printf(" (%s)", name);
                                        p_start = p + 1;
                                }
@@ -260,18 +260,32 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                max = get_max_object_index();
                for (i = 0; i < max; i++) {
                        struct object * obj = get_indexed_object(i);
+                       const char *name;
                        if (!obj)
                                continue;
                        if (!data.name_only)
                                printf("%s ", sha1_to_hex(obj->sha1));
-                       printf("%s\n", get_rev_name(obj));
+                       name = get_rev_name(obj);
+                       if (name)
+                               printf("%s\n", name);
+                       else if (allow_undefined)
+                               printf("undefined\n");
+                       else
+                               die("cannot describe '%s'", sha1_to_hex(obj->sha1));
                }
        } else {
                int i;
                for (i = 0; i < revs.nr; i++) {
+                       const char *name;
                        if (!data.name_only)
                                printf("%s ", revs.objects[i].name);
-                       printf("%s\n", get_rev_name(revs.objects[i].item));
+                       name = get_rev_name(revs.objects[i].item);
+                       if (name)
+                               printf("%s\n", name);
+                       else if (allow_undefined)
+                               printf("undefined\n");
+                       else
+                               die("cannot describe '%s'", sha1_to_hex(revs.objects[i].item->sha1));
                }
        }
 
index ec10238..586ae11 100644 (file)
@@ -16,6 +16,7 @@
 #include "progress.h"
 
 #ifdef THREADED_DELTA_SEARCH
+#include "thread-utils.h"
 #include <pthread.h>
 #endif
 
@@ -68,7 +69,7 @@ static int allow_ofs_delta;
 static const char *base_name;
 static int progress = 1;
 static int window = 10;
-static uint32_t pack_size_limit;
+static uint32_t pack_size_limit, pack_size_limit_cfg;
 static int depth = 50;
 static int delta_search_threads = 1;
 static int pack_to_stdout;
@@ -1672,7 +1673,8 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
                p[i].data_ready = 0;
 
                /* try to split chunks on "path" boundaries */
-               while (sub_size < list_size && list[sub_size]->hash &&
+               while (sub_size && sub_size < list_size &&
+                      list[sub_size]->hash &&
                       list[sub_size]->hash == list[sub_size-1]->hash)
                        sub_size++;
 
@@ -1851,11 +1853,11 @@ static int git_pack_config(const char *k, const char *v)
        }
        if (!strcmp(k, "pack.threads")) {
                delta_search_threads = git_config_int(k, v);
-               if (delta_search_threads < 1)
+               if (delta_search_threads < 0)
                        die("invalid number of threads specified (%d)",
                            delta_search_threads);
 #ifndef THREADED_DELTA_SEARCH
-