Merge branch 'tr/rev-list-count'
authorJunio C Hamano <gitster@pobox.com>
Wed, 30 Jun 2010 18:55:38 +0000 (11:55 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 30 Jun 2010 18:55:38 +0000 (11:55 -0700)
* tr/rev-list-count:
  bash completion: Support "divergence from upstream" messages in __git_ps1
  rev-list: introduce --count option

Conflicts:
contrib/completion/git-completion.bash

222 files changed:
Documentation/RelNotes-1.7.1.1.txt [new file with mode: 0644]
Documentation/RelNotes-1.7.2.txt
Documentation/config.txt
Documentation/diff-options.txt
Documentation/git-add.txt
Documentation/git-cat-file.txt
Documentation/git-checkout.txt
Documentation/git-cherry-pick.txt
Documentation/git-cvsserver.txt
Documentation/git-format-patch.txt
Documentation/git-grep.txt
Documentation/git-instaweb.txt
Documentation/git-log.txt
Documentation/git-mailinfo.txt
Documentation/git-merge.txt
Documentation/git-notes.txt
Documentation/git-rebase.txt
Documentation/git-remote.txt
Documentation/git-revert.txt
Documentation/git-send-email.txt
Documentation/git-status.txt
Documentation/git-submodule.txt
Documentation/git-update-index.txt
Documentation/gitattributes.txt
Documentation/gitignore.txt
Documentation/install-webdoc.sh
Documentation/pretty-formats.txt
Documentation/rev-list-options.txt
Documentation/technical/api-run-command.txt
Makefile
aclocal.m4 [new file with mode: 0644]
attr.c
attr.h
block-sha1/sha1.c
builtin.h
builtin/add.c
builtin/apply.c
builtin/blame.c
builtin/branch.c
builtin/cat-file.c
builtin/checkout.c
builtin/clone.c
builtin/commit.c
builtin/fetch.c
builtin/fmt-merge-msg.c
builtin/grep.c
builtin/help.c
builtin/log.c
builtin/ls-files.c
builtin/ls-remote.c
builtin/mailinfo.c
builtin/merge-file.c
builtin/merge-tree.c
builtin/merge.c
builtin/notes.c
builtin/pack-objects.c
builtin/receive-pack.c
builtin/remote.c
builtin/rerere.c
builtin/rev-parse.c
builtin/revert.c
cache.h
combine-diff.c
commit.h
compat/mingw.h
compat/win32/pthread.c
compat/win32/pthread.h
config.c
config.mak.in
configure.ac
connect.c
contrib/completion/git-completion.bash
convert.c
ctype.c
daemon.c
date.c
diff-lib.c
diff.c
diff.h
dir.c
environment.c
fast-import.c
fsck.c
git-add--interactive.perl
git-am.sh
git-compat-util.h
git-cvsserver.perl
git-instaweb.sh
git-merge-one-file.sh
git-pull.sh
git-rebase--interactive.sh
git-rebase.sh
git-remote-testgit.py
git-request-pull.sh
git-send-email.perl
git-submodule.sh
git-svn.perl
git-web--browse.sh
git_remote_helpers/git/exporter.py
git_remote_helpers/git/importer.py
git_remote_helpers/git/non_local.py
git_remote_helpers/git/repo.py
gitweb/INSTALL
gitweb/Makefile
gitweb/README
gitweb/gitweb.perl
gitweb/static/git-favicon.png [moved from gitweb/git-favicon.png with 100% similarity]
gitweb/static/git-logo.png [moved from gitweb/git-logo.png with 100% similarity]
gitweb/static/gitweb.css [moved from gitweb/gitweb.css with 93% similarity]
gitweb/static/gitweb.js [moved from gitweb/gitweb.js with 100% similarity]
graph.c
grep.c
grep.h
http-backend.c
http-push.c
http-walker.c
imap-send.c
ll-merge.c
log-tree.c
merge-file.c
merge-recursive.h
notes.c
notes.h
parse-options.c
parse-options.h
perl/Git.pm
pretty.c
quote.c
quote.h
refs.c
refs.h
remote.c
remote.h
rerere.c
revision.c
revision.h
run-command.c
run-command.h
sha1_file.c
sha1_name.c
t/Makefile
t/aggregate-results.sh
t/gitweb-lib.sh
t/lib-t6000.sh [moved from t/t6000lib.sh with 98% similarity]
t/t0000-basic.sh
t/t0020-crlf.sh
t/t0021-conversion.sh
t/t0025-crlf-auto.sh [new file with mode: 0755]
t/t0026-eol-config.sh [new file with mode: 0755]
t/t0040-parse-options.sh
t/t1450-fsck.sh
t/t1502-rev-parse-parseopt.sh
t/t2007-checkout-symlink.sh
t/t2017-checkout-orphan.sh
t/t2106-update-index-assume-unchanged.sh [new file with mode: 0755]
t/t3200-branch.sh
t/t3210-pack-refs.sh
t/t3301-notes.sh
t/t3306-notes-prune.sh
t/t3307-notes-man.sh [new file with mode: 0755]
t/t3400-rebase.sh
t/t3404-rebase-interactive.sh
t/t3501-revert-cherry-pick.sh
t/t3508-cherry-pick-many-commits.sh [new file with mode: 0755]
t/t3903-stash.sh
t/t4002-diff-basic.sh
t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^
t/t4014-format-patch.sh
t/t4015-diff-whitespace.sh
t/t4027-diff-submodule.sh
t/t4041-diff-submodule-option.sh [moved from t/t4041-diff-submodule.sh with 74% similarity]
t/t4043-diff-rename-binary.sh [new file with mode: 0755]
t/t4044-diff-index-unique-abbrev.sh [new file with mode: 0755]
t/t4124-apply-ws-rule.sh
t/t4127-apply-same-fn.sh
t/t4205-log-pretty-formats.sh [new file with mode: 0755]
t/t5150-request-pull.sh
t/t5300-pack-object.sh
t/t5505-remote.sh
t/t5510-fetch.sh
t/t5512-ls-remote.sh
t/t5520-pull.sh
t/t5530-upload-pack-error.sh
t/t5601-clone.sh
t/t5700-clone-reference.sh
t/t5800-remote-helpers.sh
t/t6001-rev-list-graft.sh
t/t6002-rev-list-bisect.sh
t/t6003-rev-list-topo-order.sh
t/t6006-rev-list-format.sh
t/t6018-rev-list-glob.sh
t/t6019-rev-list-ancestry-path.sh [new file with mode: 0755]
t/t6022-merge-rename.sh
t/t6101-rev-parse-parents.sh
t/t7002-grep.sh
t/t7005-editor.sh
t/t7006-pager.sh
t/t7008-grep-binary.sh [new file with mode: 0755]
t/t7010-setup.sh
t/t7407-submodule-foreach.sh
t/t7502-commit.sh
t/t7508-status.sh
t/t7509-commit.sh
t/t7604-merge-custom-message.sh
t/t8006-blame-textconv.sh [new file with mode: 0755]
t/t8007-cat-file-textconv.sh [new file with mode: 0755]
t/t9001-send-email.sh
t/t9129-git-svn-i18n-commitencoding.sh
t/t9200-git-cvsexportcommit.sh
t/t9400-git-cvsserver-server.sh
t/t9500-gitweb-standalone-no-errors.sh
t/test-lib.sh
trace.c
transport.c
unpack-trees.c
url.c [new file with mode: 0644]
url.h [new file with mode: 0644]
usage.c
userdiff.c
wrapper.c
wt-status.c
wt-status.h

diff --git a/Documentation/RelNotes-1.7.1.1.txt b/Documentation/RelNotes-1.7.1.1.txt
new file mode 100644 (file)
index 0000000..3f6b314
--- /dev/null
@@ -0,0 +1,96 @@
+Git v1.7.1.1 Release Notes
+==========================
+
+Fixes since v1.7.1
+------------------
+
+ * Authentication over http transport can now be made lazily, in that the
+   request can first go to a URL without username, get a 401 response and
+   then the client will ask for the username to use.
+
+ * We used to mistakenly think "../work" is a subdirectory of the current
+   directory when we are in "../work-xyz".
+
+ * The attribute mechanism now allows an entry that uses an attribute
+   macro that set/unset one attribute, immediately followed by an
+   overriding setting; this makes attribute macros much easier to use.
+
+ * We didn't recognize timezone "Z" as a synonym for "UTC" (75b37e70).
+
+ * In 1.7.0, read-tree and user commands that use the mechanism such as
+   checkout and merge were fixed to handle switching between branches one
+   of which has a file while the other has a directory at the same path
+   correctly even when there are some "confusing" pathnames in them.  But
+   the algorithm used for this fix was suboptimal and had a terrible
+   performance degradation especially in larger trees.
+
+ * "git am -3" did not show diagnosis when the patch in the message was corrupt.
+
+ * After "git apply --whitespace=fix" removed trailing blank lines in an
+   patch in a patch series, it failed to apply later patches that depend
+   on the presence of such blank lines.
+
+ * "git bundle --stdin" segfaulted.
+
+ * "git checkout" and "git rebase" overwrote paths that are marked "assume
+   unchanged".
+
+ * "git commit --amend" on a commit with an invalid author-name line that
+   lacks the display name didn't work.
+
+ * "git describe" did not tie-break tags that point at the same commit
+   correctly; newer ones are preferred by paying attention to the
+   tagger date now.
+
+ * "git diff" used to tell underlying xdiff machinery to work very hard to
+   minimize the output, but this often was spending too many extra cycles
+   for very little gain.
+
+ * "git diff --color" did not paint extended diff headers per line
+   (i.e. the coloring escape sequence didn't end at the end of line),
+   which confused "less -R".
+
+ * "git fetch" over HTTP verifies the downloaded packfiles more robustly.
+
+ * The memory usage by "git index-pack" (run during "git fetch" and "git
+   push") got leaner.
+
+ * "GIT_DIR=foo.git git init --bare bar.git" created foo.git instead of bar.git.
+
+ * "git log --abbrev=$num --format='%h' ignored --abbrev=$num.
+
+ * "git ls-files ../out/side/cwd" refused to work.
+
+ * "git merge --log" used to replace the custom message given by "-m" with
+   the shortlog, instead of appending to it.
+
+ * "git notes copy" without any other argument segfaulted.
+
+ * "git pull" accepted "--dry-run", gave it to underlying "git fetch" but
+   ignored the option itself, resulting in a bogus attempt to merge
+   unrelated commit.
+
+ * "git rebase" did not faithfully reproduce a malformed author ident, that
+   is often seen in a repository converted from foreign SCMs.
+
+ * "git reset --hard" started from a wrong directory and a working tree in
+   a nonstandard location is in use got confused.
+
+ * "git send-email" lacked a way to specify the domainname used in the
+   EHLO/HELO exchange, causing rejected connection from picky servers.
+   It learned --smtp-domain option to solve this issue.
+
+ * "git send-email" did not declare a content-transfer-encoding and
+   content-type even when its payload needs to be sent in 8-bit.
+
+ * "git show -C -C" and other corner cases lost diff metainfo output
+   in 1.7.0.
+
+ * "git stash" incorrectly lost paths in the working tree that were
+   previously removed from the index.
+
+ * "git status" stopped refreshing the index by mistake in 1.7.1.
+
+ * "git status" showed excess "hints" even when advice.statusHints is set to false.
+
+And other minor fixes and documentation updates.
index 37781b4..8ed7406 100644 (file)
@@ -4,37 +4,151 @@ Git v1.7.2 Release Notes (draft)
 Updates since v1.7.1
 --------------------
 
- * After "git apply --whitespace=fix" removed trailing blank lines in an
-   patch in a patch series, it failed to apply later patches that depend
-   on the presense of such blank lines.
+ * core.eol configuration and eol attribute are the new way to control
+   the end of line conventions for files in the working tree;
+   core.autocrlf overrides it, keeping the traditional behaviour by
+   default.
+
+ * The whitespace rules used in "git apply --whitespace" and "git diff"
+   gained a new member in the family (tab-in-indent) to help projects with
+   policy to indent only with spaces.
+
+ * When working from a subdirectory, by default, git does not look for its
+   metadirectory ".git" across filesystems, primarily to help people who
+   have invocations of git in their custom PS1 prompts, as being outside
+   of a git repository would look for ".git" all the way up to the root
+   directory, and NFS mounts are often slow.  DISCOVERY_ACROSS_FILESYSTEM
+   environment variable can be used to tell git not to stop at a
+   filesystem boundary.
+
+ * Usage help messages generated by parse-options library (i.e. most
+   of the Porcelain commands) are sent to the standard output now.
+
+ * ':/<string>' notation to look for a commit now takes regular expression
+   and it is not anchored at the beginning of the commit log message
+   anymore (this is a backward incompatible change).
+
+ * "git" wrapper learned "-c name=value" option to override configuration
+   variable from the command line.
+
+ * Improved portability for various platforms including older SunOS,
+   HP-UX 10/11, AIX, Tru64, etc. and platforms with Python 2.4.
+
+ * The message from "git am -3" has been improved when conflict
+   resolution ended up making the patch a no-op.
+
+ * "git blame" applies the textconv filter to the contents it works
+   on, when available.
+
+ * "git checkout --orphan newbranch" is similar to "-b newbranch" but
+   prepares to create a root commit that is not connected to any existing
+   commit.
+
+ * "git cherry-pick" learned to pick a range of commits (e.g. "cherry-pick
+   A..B"); this does not have nicer sequencing control "rebase [-i]" has,
+   though.
+
+ * "git cvsserver" can be told to use pserver; its password file can be
+   stored outside the repository.
 
  * The output from the textconv filter used by "git diff" can be cached to
    speed up their reuse.
 
- * "git send-email" learned --smtp-domain option to specify the domainname
-   used in the EHLO/HELO exchange.
+ * "git diff --word-diff=<mode>" extends the existing "--color-words"
+   option, making it more useful in color-challenged environments.
+
+ * The regexp to detect function headers used by "git diff" for PHP has
+   been enhanced for visibility modifiers (public, protected, etc.) to
+   better support PHP5.
+
+ * "diff.noprefix" configuration variable can be used to implicitly
+   ask for "diff --no-prefix" behaviour.
+
+ * "git for-each-ref" learned "%(objectname:short)" that gives the object
+   name abbreviated.
+
+ * "git format-patch" learned --signature option and format.signature
+   configuration variable to customize the e-mail signature used in the
+   output.
+
+ * Various options to "git grep" (e.g. --count, --name-only) work better
+   with binary files.
+
+ * "git help -w" learned "chrome" and "chromium" browsers.
+
+ * "git log --follow <path>" follows across copies (it used to only follow
+   renames).  This may make the processing more expensive.
+
+ * "git log --pretty=format:<template>" specifier learned "% <something>"
+   magic that inserts a space only when %<something> expands to a
+   non-empty string; this is similar to "%+<something>" magic, but is
+   useful in a context to generate a single line output.
+
+ * "git notes prune" learned "-n" (dry-run) and "-v" options, similar to
+   what "git prune" has.
+
+ * "git patch-id" can be fed a mbox without getting confused by the
+   signature line in the format-patch output.
+
+ * "git remote" learned "set-branches" subcommand.
 
  * "git revert" learned --strategy option to specify the merge strategy.
 
- * The whitespace rules used in "git apply --whitespace" and "git diff"
-   gained a new member in the family (tab-in-indent) to help projects with
-   policy to indent only with spaces.
+ * "git rev-list A..B" learned --ancestry-path option to further limit
+   the result to the commits that are on the ancestry chain between A and
+   B (i.e. commits that are not descendants of A are excluded).
+
+ * "git show -5" is equivalent to "git show --do-walk 5"; this is similar
+   to the update to make "git show master..next" walk the history,
+   introduced in 1.6.4.
+
+ * "git status [-s] --ignored" can be used to list ignored paths.
+
+ * "git status -s -b" shows the current branch in the output.
 
- * Authentication over http transport can now be made lazily, in that the
-   request can first go to a URL without username, get a 401 response and
-   then the client will ask for the username to use.
+ * Various "gitweb" enhancements and clean-ups, including syntax
+   highlighting, "plackup" support for instaweb, .fcgi suffix to run
+   it as FastCGI script, etc.
 
 
 Fixes since v1.7.1
 ------------------
 
- * In 1.7.1, "git status" stopped refreshing the index by mistake.
-
 All of the fixes in v1.7.1.X maintenance series are included in this
 release, unless otherwise noted.
 
+ * We didn't URL decode "file:///path/to/repo" correctly when path/to/repo
+   had percent-encoded characters (638794c, 9d2e942).
+
+ * "git commit" did not honor GIT_REFLOG_ACTION environment variable, resulting
+   reflog messages for cherry-pick and revert actions to be recorded as "commit".
+
+ * "git clone/fetch/pull" issued an incorrect error message when a ref and
+   a symref that points to the ref were updated at the same time.  This
+   obviously would update them to the same value, and should not result in
+   an error condition (0e71bc3).
+
+ * "git clone" did not configure remote.origin.url correctly for bare
+   clones (df61c889).
+
+ * "git diff" inside a tree with many pathnames that have certain
+   characters has become very slow in 1.7.0 by mistake (will merge
+   e53e6b443 to 'maint').
+
+ * "git diff --graph" works better with "--color-words" and other options
+   (81fa024..4297c0a).
+
+ * "git diff" could show ambiguous abbreviation of blob object names on
+   its "index" line (3e5a188).
+
+ * "git rebase" did not faithfully reproduce a malformed author ident, that
+   is often seen in a repository converted from foreign SCMs (43c23251).
+
+ * "git reset --hard" started from a wrong directory and a working tree in
+   a nonstandard location is in use got confused (560fb6a1).
+
 --
 exec >/var/tmp/1
-O=v1.7.1-77-gb751157
-echo O=$(git describe master)
-git shortlog --no-merges master ^maint ^$O
+O=v1.7.1-592-gcf4403a
+echo O=$(git describe HEAD)
+git shortlog --no-merges HEAD ^maint ^$O
index 85f763c..4c49104 100644 (file)
@@ -196,20 +196,17 @@ core.quotepath::
        quoted without `-z` regardless of the setting of this
        variable.
 
-core.autocrlf::
-       If true, makes git convert `CRLF` at the end of lines in text files to
-       `LF` when reading from the work tree, and convert in reverse when
-       writing to the work tree.  The variable can be set to
-       'input', in which case the conversion happens only while
-       reading from the work tree but files are written out to the work
-       tree with `LF` at the end of lines.  A file is considered
-       "text" (i.e. be subjected to the autocrlf mechanism) based on
-       the file's `crlf` attribute, or if `crlf` is unspecified,
-       based on the file's contents.  See linkgit:gitattributes[5].
+core.eol::
+       Sets the line ending type to use in the working directory for
+       files that have the `text` property set.  Alternatives are
+       'lf', 'crlf' and 'native', which uses the platform's native
+       line ending.  The default value is `native`.  See
+       linkgit:gitattributes[5] for more information on end-of-line
+       conversion.
 
 core.safecrlf::
-       If true, makes git check if converting `CRLF` as controlled by
-       `core.autocrlf` is reversible.  Git will verify if a command
+       If true, makes git check if converting `CRLF` is reversible when
+       end-of-line conversion is active.  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
@@ -219,7 +216,7 @@ core.safecrlf::
        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
+When it is enabled, git 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
@@ -243,15 +240,25 @@ 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
+`core.eol` and `core.autocrlf`, but only for the current one.  For
+example, a text file with `LF` would be accepted with `core.eol=lf`
+and could later be checked out with `core.eol=crlf`, 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.autocrlf::
+       Setting this variable to "true" is almost the same as setting
+       the `text` attribute to "auto" on all files except that text
+       files are not guaranteed to be normalized: files that contain
+       `CRLF` in the repository will not be touched.  Use this
+       setting if you want to have `CRLF` line endings in your
+       working directory even though the repository does not have
+       normalized line endings.  This variable can be set to 'input',
+       in which case no output conversion is performed.
+
 core.symlinks::
        If false, symbolic links are checked out as small plain files that
        contain the link text. linkgit:git-update-index[1] and
@@ -520,18 +527,12 @@ check that makes sure that existing object files will not get overwritten.
 
 core.notesRef::
        When showing commit messages, also show notes which are stored in
-       the given ref.  This ref is expected to contain files named
-       after the full SHA-1 of the commit they annotate.  The ref
-       must be fully qualified.
+       the given ref.  The ref must be fully qualified.  If the given
+       ref does not exist, it is not an error but means that no
+       notes should be printed.
 +
-If such a file exists in the given ref, the referenced blob is read, and
-appended to the commit message, separated by a "Notes (<refname>):"
-line (shortened to "Notes:" in the case of "refs/notes/commits").  If the
-given ref itself does not exist, it is not an error, but means that no
-notes should be printed.
-+
-This setting defaults to "refs/notes/commits", and can be overridden by
-the `GIT_NOTES_REF` environment variable.
+This setting defaults to "refs/notes/commits", and it can be overridden by
+the 'GIT_NOTES_REF' environment variable.  See linkgit:git-notes[1].
 
 core.sparseCheckout::
        Enable "sparse checkout" feature. See section "Sparse checkout" in
@@ -798,6 +799,8 @@ diff.mnemonicprefix::
        standard "a/" and "b/" depending on what is being compared.  When
        this configuration is in effect, reverse diff output also swaps
        the order of the prefixes:
+diff.noprefix::
+       If set, 'git diff' does not show any source or destination prefix.
 `git diff`;;
        compares the (i)ndex and the (w)ork tree;
 `git diff HEAD`;;
@@ -886,6 +889,12 @@ format.subjectprefix::
        The default for format-patch is to output files with the '[PATCH]'
        subject prefix. Use this variable to change that prefix.
 
+format.signature::
+       The default for format-patch is to output a signature containing
+       the git version number. Use this variable to change that default.
+       Set this variable to the empty string ("") to suppress
+       signature generation.
+
 format.suffix::
        The default for format-patch is to output files with the suffix
        `.patch`. Use this variable to change that suffix (make sure to
@@ -983,13 +992,15 @@ gitcvs.logfile::
        various stuff. See linkgit:git-cvsserver[1].
 
 gitcvs.usecrlfattr::
-       If true, the server will look up the `crlf` attribute for
-       files to determine the '-k' modes to use. If `crlf` is set,
-       the '-k' mode will be left blank, so cvs clients will
-       treat it as text. If `crlf` is explicitly unset, the file
+       If true, the server will look up the end-of-line conversion
+       attributes for files to determine the '-k' modes to use. If
+       the attributes force git to treat a file as text,
+       the '-k' mode will be left blank so cvs clients will
+       treat it as text. If they suppress text conversion, the file
        will be set with '-kb' mode, which suppresses any newline munging
-       the client might otherwise do. If `crlf` is not specified,
-       then 'gitcvs.allbinary' is used. See linkgit:gitattributes[5].
+       the client might otherwise do. If the attributes do not allow
+       the file type to be determined, then 'gitcvs.allbinary' is
+       used. See linkgit:gitattributes[5].
 
 gitcvs.allbinary::
        This is used if 'gitcvs.usecrlfattr' does not resolve
@@ -1481,6 +1492,16 @@ pager.<cmd>::
        it takes precedence over this option.  To disable pagination for
        all commands, set `core.pager` or `GIT_PAGER` to `cat`.
 
+pretty.<name>::
+       Alias for a --pretty= format string, as specified in
+       linkgit:git-log[1]. Any aliases defined here can be used just
+       as the built-in pretty formats could. For example,
+       running `git config pretty.changelog "format:{asterisk} %H %s"`
+       would cause the invocation `git log --pretty=changelog`
+       to be equivalent to running `git log "--pretty=format:{asterisk} %H %s"`.
+       Note that an alias with the same name as a built-in format
+       will be silently ignored.
+
 pull.octopus::
        The default merge strategy to use when pulling multiple branches
        at once.
index 0d89aaa..2371262 100644 (file)
@@ -210,7 +210,14 @@ endif::git-format-patch[]
        Break complete rewrite changes into pairs of delete and create.
 
 -M::
+ifndef::git-log[]
        Detect renames.
+endif::git-log[]
+ifdef::git-log[]
+       If generating diffs, detect and report renames for each commit.
+       For following files across renames while traversing history, see
+       `--follow`.
+endif::git-log[]
 
 -C::
        Detect copies as well as renames.  See also `--find-copies-harder`.
@@ -321,8 +328,14 @@ endif::git-format-patch[]
 --no-ext-diff::
        Disallow external diff drivers.
 
---ignore-submodules::
-       Ignore changes to submodules in the diff generation.
+--ignore-submodules[=<when>]::
+       Ignore changes to submodules in the diff generation. <when> can be
+       either "untracked", "dirty" or "all", which is the default. When
+       "untracked" is used submodules are not considered dirty when they only
+       contain untracked content (but they are still scanned for modified
+       content). Using "dirty" ignores all changes to the work tree of submodules,
+       only changes to the commits stored in the superproject are shown (this was
+       the behavior until 1.7.0). Using "all" hides all changes to submodules.
 
 --src-prefix=<prefix>::
        Show the given source prefix instead of "a/".
index 51cbeb7..74741a4 100644 (file)
@@ -266,9 +266,9 @@ patch::
 
        y - stage this hunk
        n - do not stage this hunk
-       q - quit, do not stage this hunk nor any of the remaining ones
-       a - stage this and all the remaining hunks in the file
-       d - do not stage this hunk nor any of the remaining hunks in the file
+       q - quit; do not stage this hunk nor any of the remaining ones
+       a - stage this hunk and all later hunks in the file
+       d - do not stage this hunk nor any of the later hunks in the file
        g - select a hunk to go to
        / - search for a hunk matching the given regex
        j - leave this hunk undecided, see next undecided hunk
index 58c8d65..9ebbe94 100644 (file)
@@ -9,14 +9,15 @@ git-cat-file - Provide content or type and size information for repository objec
 SYNOPSIS
 --------
 [verse]
-'git cat-file' (-t | -s | -e | -p | <type>) <object>
+'git cat-file' (-t | -s | -e | -p | <type> | --textconv ) <object>
 'git cat-file' (--batch | --batch-check) < <list-of-objects>
 
 DESCRIPTION
 -----------
 In its first form, the command provides the content or the type of an object in
 the repository. The type is required unless '-t' or '-p' is used to find the
-object type, or '-s' is used to find the object size.
+object type, or '-s' is used to find the object size, or '--textconv' is used
+(which implies type "blob").
 
 In the second form, a list of objects (separated by linefeeds) is provided on
 stdin, and the SHA1, type, and size of each object is printed on stdout.
@@ -51,6 +52,11 @@ OPTIONS
        or to ask for a "blob" with <object> being a tag object that
        points at it.
 
+--textconv::
+       Show the content as transformed by a textconv filter. In this case,
+       <object> has be of the form <treeish>:<path>, or :<path> in order
+       to apply the filter to the content recorded in the index at <path>.
+
 --batch::
        Print the SHA1, type, size, and contents of each object provided on
        stdin. May not be combined with any other options or arguments.
index afda5c3..261dd90 100644 (file)
@@ -15,33 +15,41 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-
-When <paths> are not given, this command switches branches by
-updating the index, working tree, and HEAD to reflect the specified
+Updates files in the working tree to match the version in the index
+or the specified tree.  If no paths are given, 'git checkout' will
+also update `HEAD` to set the specified branch as the current
 branch.
 
-If `-b` is given, a new branch is created and checked out, as if
-linkgit:git-branch[1] were called; in this case you can
-use the --track or --no-track options, which will be passed to `git
-branch`.  As a convenience, --track without `-b` implies branch
-creation; see the description of --track below.
-
-When <paths> or --patch are given, this command does *not* switch
-branches.  It updates the named paths in the working tree from
-the index file, or from a named <tree-ish> (most often a commit).  In
-this case, the `-b` and `--track` options are meaningless and giving
-either of them results in an error. The <tree-ish> argument can be
-used to specify a specific tree-ish (i.e. commit, tag or tree)
-to update the index for the given paths before updating the
-working tree.
-
-The index may contain unmerged entries after a failed merge.  By
-default, if you try to check out such an entry from the index, the
+'git checkout' [<branch>]::
+'git checkout' -b <new branch> [<start point>]::
+
+       This form switches branches by updating the index, working
+       tree, and HEAD to reflect the specified branch.
++
+If `-b` is given, a new branch is created as if linkgit:git-branch[1]
+were called and then checked out; in this case you can
+use the `--track` or `--no-track` options, which will be passed to
+'git branch'.  As a convenience, `--track` without `-b` implies branch
+creation; see the description of `--track` below.
+
+'git checkout' [--patch] [<tree-ish>] [--] <pathspec>...::
+
+       When <paths> or `--patch` are given, 'git checkout' *not* switch
+       branches.  It updates the named paths in the working tree from
+       the index file or from a named <tree-ish> (most often a commit).  In
+       this case, the `-b` and `--track` options are meaningless and giving
+       either of them results in an error. The <tree-ish> argument can be
+       used to specify a specific tree-ish (i.e. commit, tag or tree)
+       to update the index for the given paths before updating the
+       working tree.
++
+The index may contain unmerged entries because of a previous failed merge.
+By default, if you try to check out such an entry from the index, the
 checkout operation will fail and nothing will be checked out.
-Using -f will ignore these unmerged entries.  The contents from a
+Using `-f` will ignore these unmerged entries.  The contents from a
 specific side of the merge can be checked out of the index by
-using --ours or --theirs.  With -m, changes made to the working tree
-file can be discarded to recreate the original conflicted merge result.
+using `--ours` or `--theirs`.  With `-m`, changes made to the working tree
+file can be discarded to re-create the original conflicted merge result.
 
 OPTIONS
 -------
@@ -91,22 +99,29 @@ explicitly give a name with '-b' in such a case.
        details.
 
 --orphan::
-       Create a new branch named <new_branch>, unparented to any other
-       branch.  The new branch you switch to does not have any commit
-       and after the first one it will become the root of a new history
-       completely unconnected from all the other branches.
+       Create a new 'orphan' branch, named <new_branch>, started from
+       <start_point> and switch to it.  The first commit made on this
+       new branch will have no parents and it will be the root of a new
+       history totally disconnected from all the other branches and
+       commits.
++
+The index and the working tree are adjusted as if you had previously run
+"git checkout <start_point>".  This allows you to start a new history
+that records a set of paths similar to <start_point> by easily running
+"git commit -a" to make the root commit.
 +
-When you use "--orphan", the index and the working tree are kept intact.
-This allows you to start a new history that records set of paths similar
-to that of the start-point commit, which is useful when you want to keep
-different branches for different audiences you are working to like when
-you have an open source and commercial versions of a software, for example.
+This can be useful when you want to publish the tree from a commit
+without exposing its full history. You might want to do this to publish
+an open source branch of a project whose current tree is "clean", but
+whose full history contains proprietary or otherwise encumbered bits of
+code.
 +
-If you want to start a disconnected history that records set of paths
-totally different from the original branch, you may want to first clear
-the index and the working tree, by running "git rm -rf ." from the
-top-level of the working tree, before preparing your files (by copying
-from elsewhere, extracting a tarball, etc.) in the working tree.
+If you want to start a disconnected history that records a set of paths
+that is totally different from the one of <start_point>, then you should
+clear the index and the working tree right after creating the orphan
+branch by running "git rm -rf ." from the top level of the working tree.
+Afterwards you will be ready to prepare your new files, repopulating the
+working tree, by copying them from elsewhere, extracting a tarball, etc.
 
 -m::
 --merge::
index d71607a..bcb4c75 100644 (file)
@@ -3,24 +3,28 @@ git-cherry-pick(1)
 
 NAME
 ----
-git-cherry-pick - Apply the change introduced by an existing commit
+git-cherry-pick - Apply the changes introduced by some existing commits
 
 SYNOPSIS
 --------
-'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] [--ff] <commit>
+'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] [--ff] <commit>...
 
 DESCRIPTION
 -----------
-Given one existing commit, apply the change the patch introduces, and record a
-new commit that records it.  This requires your working tree to be clean (no
-modifications from the HEAD commit).
+
+Given one or more existing commits, apply the change each one
+introduces, recording a new commit for each.  This requires your
+working tree to be clean (no modifications from the HEAD commit).
 
 OPTIONS
 -------
-<commit>::
-       Commit to cherry-pick.
+<commit>...::
+       Commits to cherry-pick.
        For a more complete list of ways to spell commits, see the
        "SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1].
+       Sets of commits can be passed but no traversal is done by
+       default, as if the '--no-walk' option was specified, see
+       linkgit:git-rev-list[1].
 
 -e::
 --edit::
@@ -55,10 +59,10 @@ OPTIONS
 
 -n::
 --no-commit::
-       Usually the command automatically creates a commit.
-       This flag applies the change necessary to cherry-pick
-       the named commit to your working tree and the index,
-       but does not make the commit.  In addition, when this
+       Usually the command automatically creates a sequence of commits.
+       This flag applies the changes necessary to cherry-pick
+       each named commit to your working tree and the index,
+       without making any commit.  In addition, when this
        option is used, your index does not have to match the
        HEAD commit.  The cherry-pick is done against the
        beginning state of your index.
@@ -75,6 +79,40 @@ effect to your index in a row.
        cherry-pick'ed commit, then a fast forward to this commit will
        be performed.
 
+EXAMPLES
+--------
+git cherry-pick master::
+
+       Apply the change introduced by the commit at the tip of the
+       master branch and create a new commit with this change.
+
+git cherry-pick ..master::
+git cherry-pick ^HEAD master::
+
+       Apply the changes introduced by all commits that are ancestors
+       of master but not of HEAD to produce new commits.
+
+git cherry-pick master\~4 master~2::
+
+       Apply the changes introduced by the fifth and third last
+       commits pointed to by master and create 2 new commits with
+       these changes.
+
+git cherry-pick -n master~1 next::
+
+       Apply to the working tree and the index the changes introduced
+       by the second last commit pointed to by master and by the last
+       commit pointed to by next, but do not create any commit with
+       these changes.
+
+git cherry-pick --ff ..next::
+
+       If history is linear and HEAD is an ancestor of next, update
+       the working tree and advance the HEAD pointer to match next.
+       Otherwise, apply the changes introduced by those commits that
+       are in next but not HEAD to the current branch, creating a new
+       commit for each new change.
+
 Author
 ------
 Written by Junio C Hamano <gitster@pobox.com>
@@ -83,6 +121,10 @@ Documentation
 --------------
 Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
 
+SEE ALSO
+--------
+linkgit:git-revert[1]
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index dbb053e..7004dd2 100644 (file)
@@ -72,9 +72,6 @@ plugin. Most functionality works fine with both of these clients.
 LIMITATIONS
 -----------
 
-Currently cvsserver works over SSH connections for read/write clients, and
-over pserver for anonymous CVS access.
-
 CVS clients cannot tag, branch or perform GIT merges.
 
 'git-cvsserver' maps GIT branches to CVS modules. This is very different
@@ -84,7 +81,7 @@ one or more directories.
 INSTALLATION
 ------------
 
-1. If you are going to offer anonymous CVS access via pserver, add a line in
+1. If you are going to offer CVS access via pserver, add a line in
    /etc/inetd.conf like
 +
 --
@@ -101,6 +98,38 @@ looks like
    cvspserver stream tcp nowait nobody /usr/bin/git-cvsserver git-cvsserver pserver
 
 ------
+
+Only anonymous access is provided by pserve by default. To commit you
+will have to create pserver accounts, simply add a gitcvs.authdb
+setting in the config file of the repositories you want the cvsserver
+to allow writes to, for example:
+
+------
+
+   [gitcvs]
+       authdb = /etc/cvsserver/passwd
+
+------
+The format of these files is username followed by the crypted password,
+for example:
+
+------
+   myuser:$1Oyx5r9mdGZ2
+   myuser:$1$BA)@$vbnMJMDym7tA32AamXrm./
+------
+You can use the 'htpasswd' facility that comes with Apache to make these
+files, but Apache's MD5 crypt method differs from the one used by most C
+library's crypt() function, so don't use the -m option.
+
+Alternatively you can produce the password with perl's crypt() operator:
+-----
+   perl -e 'my ($user, $pass) = @ARGV; printf "%s:%s\n", $user, crypt($user, $pass)' $USER password
+-----
+
+Then provide your password via the pserver method, for example:
+------
+   cvs -d:pserver:someuser:somepassword <at> server/path/repo.git co <HEAD_name>
+------
 No special setup is needed for SSH access, other than having GIT tools
 in the PATH. If you have clients that do not accept the CVS_SERVER
 environment variable, you can rename 'git-cvsserver' to `cvs`.
@@ -340,16 +369,13 @@ By default the server leaves the '-k' mode blank for all files,
 which causes the cvs client to treat them as a text files, subject
 to crlf conversion on some platforms.
 
-You can make the server use `crlf` attributes to set the '-k' modes
-for files by setting the `gitcvs.usecrlfattr` config variable.
-In this case, if `crlf` is explicitly unset ('-crlf'), then the
-server will set '-kb' mode for binary files. If `crlf` is set,
-then the '-k' mode will explicitly be left blank.  See
-also linkgit:gitattributes[5] for more information about the `crlf`
-attribute.
+You can make the server use the end-of-line conversion attributes to
+set the '-k' modes for files by setting the `gitcvs.usecrlfattr`
+config variable.  See linkgit:gitattributes[5] for more information
+about end-of-line conversion.
 
 Alternatively, if `gitcvs.usecrlfattr` config is not enabled
-or if the `crlf` attribute is unspecified for a filename, then
+or the attributes do not allow automatic detection for a filename, then
 the server uses the `gitcvs.allbinary` config for the default setting.
 If `gitcvs.allbinary` is set, then file not otherwise
 specified will default to '-kb' mode. Otherwise the '-k' mode
index 835fb71..c8c81e8 100644 (file)
@@ -13,6 +13,7 @@ SYNOPSIS
                   [--no-thread | --thread[=<style>]]
                   [(--attach|--inline)[=<boundary>] | --no-attach]
                   [-s | --signoff]
+                  [--signature=<signature> | --no-signature]
                   [-n | --numbered | -N | --no-numbered]
                   [--start-number <n>] [--numbered-files]
                   [--in-reply-to=Message-Id] [--suffix=.<sfx>]
@@ -180,6 +181,12 @@ will want to ensure that threading is disabled for `git send-email`.
        containing the shortlog and the overall diffstat.  You can
        fill in a description in the file before sending it out.
 
+--[no]-signature=<signature>::
+       Add a signature to each message produced. Per RFC 3676 the signature
+       is separated from the body by a line with '-- ' on it. If the
+       signature option is omitted the signature defaults to the git version
+       number.
+
 --suffix=.<sfx>::
        Instead of using `.patch` as the suffix for generated
        filenames, use specified suffix.  A common alternative is
index 4b32322..912bddd 100644 (file)
@@ -183,7 +183,7 @@ OPTIONS
 Examples
 --------
 
-git grep 'time_t' -- '*.[ch]'::
+git grep 'time_t' \-- '*.[ch]'::
        Looks for `time_t` in all tracked .c and .h files in the working
        directory and its subdirectories.
 
index a1f17df..2c3c4d2 100644 (file)
@@ -29,7 +29,7 @@ OPTIONS
        The HTTP daemon command-line that will be executed.
        Command-line options may be specified here, and the
        configuration file will be added at the end of the command-line.
-       Currently apache2, lighttpd, mongoose and webrick are supported.
+       Currently apache2, lighttpd, mongoose, plackup and webrick are supported.
        (Default: lighttpd)
 
 -m::
index a2d55f9..0e6ff31 100644 (file)
@@ -133,6 +133,48 @@ Discussion
 
 include::i18n.txt[]
 
+Configuration
+-------------
+
+See linkgit:git-config[1] for core variables and linkgit:git-diff[1]
+for settings related to diff generation.
+
+format.pretty::
+       Default for the `--format` option.  (See "PRETTY FORMATS" above.)
+       Defaults to "medium".
+
+i18n.logOutputEncoding::
+       Encoding to use when displaying logs.  (See "Discussion", above.)
+       Defaults to the value of `i18n.commitEncoding` if set, UTF-8
+       otherwise.
+
+log.date::
+       Default format for human-readable dates.  (Compare the
+       `--date` option.)  Defaults to "default", which means to write
+       dates like `Sat May 8 19:35:34 2010 -0500`.
+
+log.showroot::
+       If `false`, 'git log' and related commands will not treat the
+       initial commit as a big creation event.  Any root commits in
+       `git log -p` output would be shown without a diff attached.
+       The default is `true`.
+
+mailmap.file::
+       See linkgit:git-shortlog[1].
+
+notes.displayRef::
+       Which refs, in addition to the default set by `core.notesRef`
+       or 'GIT_NOTES_REF', to read notes from when showing commit
+       messages with the 'log' family of commands.  See
+       linkgit:git-notes[1].
++
+May be an unabbreviated ref name or a glob and may be specified
+multiple times.  A warning will be issued for refs that do not exist,
+but a glob that does not match any refs is silently ignored.
++
+This setting can be disabled by the `--no-standard-notes` option,
+overridden by the 'GIT_NOTES_DISPLAY_REF' environment variable,
+and supplemented by the `--show-notes` option.
 
 Author
 ------
index e3d58cb..3ea5aad 100644 (file)
@@ -40,16 +40,16 @@ OPTIONS
 -u::
        The commit log message, author name and author email are
        taken from the e-mail, and after minimally decoding MIME
-       transfer encoding, re-coded in UTF-8 by transliterating
+       transfer encoding, re-coded in the charset specified by
+       i18n.commitencoding (defaulting to UTF-8) by transliterating
        them.  This used to be optional but now it is the default.
 +
 Note that the patch is always used as-is without charset
 conversion, even with this flag.
 
 --encoding=<encoding>::
-       Similar to -u but if the local convention is different
-       from what is specified by i18n.commitencoding, this flag
-       can be used to override it.
+       Similar to -u.  But when re-coding, the charset specified here is
+       used instead of the one specified by i18n.commitencoding or UTF-8.
 
 -n::
        Disable all charset re-coding of the metadata.
index c2325ef..84043cc 100644 (file)
@@ -58,7 +58,12 @@ include::merge-options.txt[]
 
 -m <msg>::
        Set the commit message to be used for the merge commit (in
-       case one is created). The 'git fmt-merge-msg' command can be
+       case one is created).
+
+       If `--log` is specified, a shortlog of the commits being merged
+       will be appended to the specified message.
+
+       The 'git fmt-merge-msg' command can be
        used to give a good default for automated 'git merge'
        invocations.
 
index 4e5113b..5540af5 100644 (file)
@@ -3,7 +3,7 @@ git-notes(1)
 
 NAME
 ----
-git-notes - Add/inspect object notes
+git-notes - Add or inspect object notes
 
 SYNOPSIS
 --------
@@ -15,29 +15,31 @@ SYNOPSIS
 'git notes' edit [<object>]
 'git notes' show [<object>]
 'git notes' remove [<object>]
-'git notes' prune
+'git notes' prune [-n | -v]
 
 
 DESCRIPTION
 -----------
-This command allows you to add/remove notes to/from objects, without
-changing the objects themselves.
+Adds, removes, or reads notes attached to objects, without touching
+the objects themselves.
 
-A typical use of notes is to extend a commit message without having
-to change the commit itself. Such commit notes can be shown by `git log`
-along with the original commit message. To discern these notes from the
+By default, notes are saved to and read from `refs/notes/commits`, but
+this default can be overridden.  See the OPTIONS, CONFIGURATION, and
+ENVIRONMENT sections below.  If this ref does not exist, it will be
+quietly created when it is first needed to store a note.
+
+A typical use of notes is to supplement a commit message without
+changing the commit itself. Notes can be shown by 'git log' along with
+the original commit message. To distinguish these notes from the
 message stored in the commit object, the notes are indented like the
 message, after an unindented line saying "Notes (<refname>):" (or
-"Notes:" for the default setting).
+"Notes:" for `refs/notes/commits`).
 
-This command always manipulates the notes specified in "core.notesRef"
-(see linkgit:git-config[1]), which can be overridden by GIT_NOTES_REF.
-To change which notes are shown by 'git-log', see the
-"notes.displayRef" configuration.
+To change which notes are shown by 'git log', see the
+"notes.displayRef" configuration in linkgit:git-log[1].
 
-See the description of "notes.rewrite.<command>" in
-linkgit:git-config[1] for a way of carrying your notes across commands
-that rewrite commits.
+See the "notes.rewrite.<command>" configuration for a way to carry
+notes across commands that rewrite commits.
 
 
 SUBCOMMANDS
@@ -101,15 +103,20 @@ OPTIONS
        Use the given note message (instead of prompting).
        If multiple `-m` options are given, their values
        are concatenated as separate paragraphs.
+       Lines starting with `#` and empty lines other than a
+       single line between paragraphs will be stripped out.
 
 -F <file>::
 --file=<file>::
        Take the note message from the given file.  Use '-' to
        read the note message from the standard input.
+       Lines starting with `#` and empty lines other than a
+       single line between paragraphs will be stripped out.
 
 -C <object>::
 --reuse-message=<object>::
-       Reuse the note message from the given note object.
+       Take the note message from the given blob object (for
+       example, another note).
 
 -c <object>::
 --reedit-message=<object>::
@@ -117,22 +124,151 @@ OPTIONS
        the user can further edit the note message.
 
 --ref <ref>::
-       Manipulate the notes tree in <ref>.  This overrides both
-       GIT_NOTES_REF and the "core.notesRef" configuration.  The ref
+       Manipulate the notes tree in <ref>.  This overrides
+       'GIT_NOTES_REF' and the "core.notesRef" configuration.  The ref
        is taken to be in `refs/notes/` if it is not qualified.
 
+-n::
+       Do not remove anything; just report the object names whose notes
+       would be removed.
+
+-v::
+       Report all object names whose notes are removed.
+
+
+DISCUSSION
+----------
 
-NOTES
------
+Commit notes are blobs containing extra information about an object
+(usually information to supplement a commit's message).  These blobs
+are taken from notes refs.  A notes ref is usually a branch which
+contains "files" whose paths are the object names for the objects
+they describe, with some directory separators included for performance
+reasons footnote:[Permitted pathnames have the form
+'ab'`/`'cd'`/`'ef'`/`'...'`/`'abcdef...': a sequence of directory
+names of two hexadecimal digits each followed by a filename with the
+rest of the object ID.].
 
 Every notes change creates a new commit at the specified notes ref.
 You can therefore inspect the history of the notes by invoking, e.g.,
-`git log -p notes/commits`.
+`git log -p notes/commits`.  Currently the commit message only records
+which operation triggered the update, and the commit authorship is
+determined according to the usual rules (see linkgit:git-commit[1]).
+These details may change in the future.
+
+It is also permitted for a notes ref to point directly to a tree
+object, in which case the history of the notes can be read with
+`git log -p -g <refname>`.
+
+
+EXAMPLES
+--------
+
+You can use notes to add annotations with information that was not
+available at the time a commit was written.
+
+------------
+$ git notes add -m 'Tested-by: Johannes Sixt <j6t@kdbg.org>' 72a144e2
+$ git show -s 72a144e
+[...]
+    Signed-off-by: Junio C Hamano <gitster@pobox.com>
+
+Notes:
+    Tested-by: Johannes Sixt <j6t@kdbg.org>
+------------
+
+In principle, a note is a regular Git blob, and any kind of
+(non-)format is accepted.  You can binary-safely create notes from
+arbitrary files using 'git hash-object':
+
+------------
+$ cc *.c
+$ blob=$(git hash-object -w a.out)
+$ git notes --ref=built add -C "$blob" HEAD
+------------
+
+Of course, it doesn't make much sense to display non-text-format notes
+with 'git log', so if you use such notes, you'll probably need to write
+some special-purpose tools to do something useful with them.
+
+
+CONFIGURATION
+-------------
 
-Currently the commit message only records which operation triggered
-the update, and the commit authorship is determined according to the
-usual rules (see linkgit:git-commit[1]).  These details may change in
-the future.
+core.notesRef::
+       Notes ref to read and manipulate instead of
+       `refs/notes/commits`.  Must be an unabbreviated ref name.
+       This setting can be overridden through the environment and
+       command line.
+
+notes.displayRef::
+       Which ref (or refs, if a glob or specified more than once), in
+       addition to the default set by `core.notesRef` or
+       'GIT_NOTES_REF', to read notes from when showing commit
+       messages with the 'git log' family of commands.
+       This setting can be overridden on the command line or by the
+       'GIT_NOTES_DISPLAY_REF' environment variable.
+       See linkgit:git-log[1].
+
+notes.rewrite.<command>::
+       When rewriting commits with <command> (currently `amend` or
+       `rebase`), if this variable is `false`, git will not copy
+       notes from the original to the rewritten commit.  Defaults to
+       `true`.  See also "`notes.rewriteRef`" below.
++
+This setting can be overridden by the 'GIT_NOTES_REWRITE_REF'
+environment variable.
+
+notes.rewriteMode::
+       When copying notes during a rewrite, what to do if the target
+       commit already has a note.  Must be one of `overwrite`,
+       `concatenate`, and `ignore`.  Defaults to `concatenate`.
++
+This setting can be overridden with the `GIT_NOTES_REWRITE_MODE`
+environment variable.
+
+notes.rewriteRef::
+       When copying notes during a rewrite, specifies the (fully
+       qualified) ref whose notes should be copied.  May be a glob,
+       in which case notes in all matching refs will be copied.  You
+       may also specify this configuration several times.
++
+Does not have a default value; you must configure this variable to
+enable note rewriting.
++
+Can be overridden with the 'GIT_NOTES_REWRITE_REF' environment variable.
+
+
+ENVIRONMENT
+-----------
+
+'GIT_NOTES_REF'::
+       Which ref to manipulate notes from, instead of `refs/notes/commits`.
+       This overrides the `core.notesRef` setting.
+
+'GIT_NOTES_DISPLAY_REF'::
+       Colon-delimited list of refs or globs indicating which refs,
+       in addition to the default from `core.notesRef` or
+       'GIT_NOTES_REF', to read notes from when showing commit
+       messages.
+       This overrides the `notes.displayRef` setting.
++
+A warning will be issued for refs that do not exist, but a glob that
+does not match any refs is silently ignored.
+
+'GIT_NOTES_REWRITE_MODE'::
+       When copying notes during a rewrite, what to do if the target
+       commit already has a note.
+       Must be one of `overwrite`, `concatenate`, and `ignore`.
+       This overrides the `core.rewriteMode` setting.
+
+'GIT_NOTES_REWRITE_REF'::
+       When rewriting commits, which notes to copy from the original
+       to the rewritten commit.  Must be a colon-delimited list of
+       refs or globs.
++
+If not set in the environment, the list of notes to copy depends
+on the `notes.rewrite.<command>` and `notes.rewriteRef` settings.
 
 
 Author
index 50ba2e4..be23ad2 100644 (file)
@@ -310,6 +310,11 @@ link:howto/revert-a-faulty-merge.txt[revert-a-faulty-merge How-To] for details).
 -p::
 --preserve-merges::
        Instead of ignoring merges, try to recreate them.
++
+This uses the `--interactive` machinery internally, but combining it
+with the `--interactive` option explicitly is generally not a good
+idea unless you know what you are doing (see BUGS below).
+
 
 --root::
        Rebase all commits reachable from <branch>, instead of
@@ -611,6 +616,28 @@ The ripple effect of a "hard case" recovery is especially bad:
 case" recovery too!
 
 
+BUGS
+----
+The todo list presented by `--preserve-merges --interactive` does not
+represent the topology of the revision graph.  Editing commits and
+rewording their commit messages should work fine, but attempts to
+reorder commits tend to produce counterintuitive results.
+
+For example, an attempt to rearrange
+------------
+1 --- 2 --- 3 --- 4 --- 5
+------------
+to
+------------
+1 --- 2 --- 4 --- 3 --- 5
+------------
+by moving the "pick 4" line will result in the following history:
+------------
+       3
+       /
+1 --- 2 --- 4 --- 5
+------------
+
 Authors
 ------
 Written by Junio C Hamano <gitster@pobox.com> and
index ebaaadc..aa021b0 100644 (file)
@@ -14,6 +14,7 @@ SYNOPSIS
 'git remote rename' <old> <new>
 'git remote rm' <name>
 'git remote set-head' <name> (-a | -d | <branch>)
+'git remote set-branches' <name> [--add] <branch>...
 'git remote set-url' [--push] <name> <newurl> [<oldurl>]
 'git remote set-url --add' [--push] <name> <newurl>
 'git remote set-url --delete' [--push] <name> <url>
@@ -110,6 +111,18 @@ remote set-head origin master" will set `$GIT_DIR/refs/remotes/origin/HEAD` to
 `refs/remotes/origin/master` already exists; if not it must be fetched first.
 +
 
+'set-branches'::
+
+Changes the list of branches tracked by the named remote.
+This can be used to track a subset of the available remote branches
+after the initial setup for a remote.
++
+The named branches will be interpreted as if specified with the
+`-t` option on the 'git remote add' command line.
++
+With `--add`, instead of replacing the list of currently tracked
+branches, adds to that list.
+
 'set-url'::
 
 Changes URL remote points to. Sets first URL remote points to matching
index c66bf80..dea4f53 100644 (file)
@@ -3,20 +3,22 @@ git-revert(1)
 
 NAME
 ----
-git-revert - Revert an existing commit
+git-revert - Revert some existing commits
 
 SYNOPSIS
 --------
-'git revert' [--edit | --no-edit] [-n] [-m parent-number] [-s] <commit>
+'git revert' [--edit | --no-edit] [-n] [-m parent-number] [-s] <commit>...
 
 DESCRIPTION
 -----------
-Given one existing commit, revert the change the patch introduces, and record a
-new commit that records it.  This requires your working tree to be clean (no
-modifications from the HEAD commit).
 
-Note: 'git revert' is used to record a new commit to reverse the
-effect of an earlier commit (often a faulty one).  If you want to
+Given one or more existing commits, revert the changes that the
+related patches introduce, and record some new commits that record
+them.  This requires your working tree to be clean (no modifications
+from the HEAD commit).
+
+Note: 'git revert' is used to record some new commits to reverse the
+effect of some earlier commits (often only a faulty one).  If you want to
 throw away all uncommitted changes in your working directory, you
 should see linkgit:git-reset[1], particularly the '--hard' option.  If
 you want to extract specific files as they were in another commit, you
@@ -26,10 +28,13 @@ both will discard uncommitted changes in your working directory.
 
 OPTIONS
 -------
-<commit>::
-       Commit to revert.
+<commit>...::
+       Commits to revert.
        For a more complete list of ways to spell commit names, see
        "SPECIFYING REVISIONS" section in linkgit:git-rev-parse[1].
+       Sets of commits can also be given but no traversal is done by
+       default, see linkgit:git-rev-list[1] and its '--no-walk'
+       option.
 
 -e::
 --edit::
@@ -59,11 +64,11 @@ more details.
 
 -n::
 --no-commit::
-       Usually the command automatically creates a commit with
-       a commit log message stating which commit was
-       reverted.  This flag applies the change necessary
-       to revert the named commit to your working tree
-       and the index, but does not make the commit.  In addition,
+       Usually the command automatically creates some commits with
+       commit log messages stating which commits were
+       reverted.  This flag applies the changes necessary
+       to revert the named commits to your working tree
+       and the index, but does not make the commits.  In addition,
        when this option is used, your index does not have to match
        the HEAD commit.  The revert is done against the
        beginning state of your index.
@@ -75,6 +80,20 @@ effect to your index in a row.
 --signoff::
        Add Signed-off-by line at the end of the commit message.
 
+EXAMPLES
+--------
+git revert HEAD~3::
+
+       Revert the changes specified by the fourth last commit in HEAD
+       and create a new commit with the reverted changes.
+
+git revert -n master\~5..master~2::
+
+       Revert the changes done by commits from the fifth last commit
+       in master (included) to the third last commit in master
+       (included), but do not create any commit with the reverted
+       changes. The revert only modifies the working tree and the
+       index.
 
 Author
 ------
@@ -84,6 +103,10 @@ Documentation
 --------------
 Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
 
+SEE ALSO
+--------
+linkgit:git-cherry-pick[1]
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index 12622fc..c283084 100644 (file)
@@ -101,6 +101,15 @@ See the CONFIGURATION section for 'sendemail.multiedit'.
 +
 The --to option must be repeated for each user you want on the to list.
 
+--8bit-encoding=<encoding>::
+       When encountering a non-ASCII message or subject that does not
+       declare its encoding, add headers/quoting to indicate it is
+       encoded in <encoding>.  Default is the value of the
+       'sendemail.assume8bitEncoding'; if that is unspecified, this
+       will be prompted for if any non-ASCII files are encountered.
++
+Note that no attempts whatsoever are made to validate the encoding.
+
 
 Sending
 ~~~~~~~
index 2d4bbfc..fd0fe7c 100644 (file)
@@ -27,6 +27,10 @@ OPTIONS
 --short::
        Give the output in the short-format.
 
+-b::
+--branch::
+       Show the branch and tracking info even in short-format.
+
 --porcelain::
        Give the output in a stable, easy-to-parse format for scripts.
        Currently this is identical to --short output, but is guaranteed
@@ -120,6 +124,10 @@ Ignored files are not listed.
     ?           ?    untracked
     -------------------------------------------------
 
+If -b is used the short-format status is preceded by a line
+
+## branchname tracking info
+
 There is an alternate -z format recommended for machine parsing.  In
 that format, the status field is the same, but some other things
 change.  First, the '->' is omitted from rename entries and the field
@@ -128,7 +136,7 @@ order is reversed (e.g 'from -> to' becomes 'to from'). Second, a NUL
 and the terminating newline (but a space still separates the status
 field from the first filename).  Third, filenames containing special
 characters are not specially formatted; no quoting or
-backslash-escaping is performed.
+backslash-escaping is performed. Fourth, there is no branch line.
 
 CONFIGURATION
 -------------
index 2502531..cdabfd2 100644 (file)
@@ -145,10 +145,12 @@ summary::
 
 foreach::
        Evaluates an arbitrary shell command in each checked out submodule.
-       The command has access to the variables $name, $path and $sha1:
+       The command has access to the variables $name, $path, $sha1 and
+       $toplevel:
        $name is the name of the relevant submodule section in .gitmodules,
        $path is the name of the submodule directory relative to the
-       superproject, and $sha1 is the commit as recorded in the superproject.
+       superproject, $sha1 is the commit as recorded in the superproject,
+       and $toplevel is the absolute path to the top-level of the superproject.
        Any submodules defined in the superproject but not checked out are
        ignored by this command. Unless given --quiet, foreach prints the name
        of each submodule before evaluating the command.
index 68dc187..765d4b3 100644 (file)
@@ -93,8 +93,6 @@ OPTIONS
 This option can be also used as a coarse file-level mechanism
 to ignore uncommitted changes in tracked files (akin to what
 `.gitignore` does for untracked files).
-You should remember that an explicit 'git add' operation will
-still cause the file to be refreshed from the working tree.
 Git will fail (gracefully) in case it needs to modify this file
 in the index e.g. when merging in a commit;
 thus, in case the assumed-untracked file is changed upstream,
index 0523a57..564586b 100644 (file)
@@ -92,53 +92,154 @@ such as 'git checkout' and 'git merge' run.  They also affect how
 git stores the contents you prepare in the working tree in the
 repository upon 'git add' and 'git commit'.
 
-`crlf`
+`text`
 ^^^^^^
 
-This attribute controls the line-ending convention.
+This attribute enables and controls end-of-line normalization.  When a
+text file is normalized, its line endings are converted to LF in the
+repository.  To control what line ending style is used in the working
+directory, use the `eol` attribute for a single file and the
+`core.eol` configuration variable for all text files.
 
 Set::
 
-       Setting the `crlf` attribute on a path is meant to mark
-       the path as a "text" file.  'core.autocrlf' conversion
-       takes place without guessing the content type by
-       inspection.
+       Setting the `text` attribute on a path enables end-of-line
+       normalization and marks the path as a text file.  End-of-line
+       conversion takes place without guessing the content type.
 
 Unset::
 
-       Unsetting the `crlf` attribute on a path tells git not to
+       Unsetting the `text` attribute on a path tells git not to
        attempt any end-of-line conversion upon checkin or checkout.
 
+Set to string value "auto"::
+
+       When `text` is set to "auto", the path is marked for automatic
+       end-of-line normalization.  If git decides that the content is
+       text, its line endings are normalized to LF on checkin.
+
 Unspecified::
 
-       Unspecified `crlf` attribute tells git to apply the
-       `core.autocrlf` conversion when the file content looks
-       like text.
+       If the `text` attribute is unspecified, git uses the
+       `core.autocrlf` configuration variable to determine if the
+       file should be converted.
 
-Set to string value "input"::
+Any other value causes git to act as if `text` has been left
+unspecified.
 
-       This is similar to setting the attribute to `true`, but
-       also forces git to act as if `core.autocrlf` is set to
-       `input` for the path.
+`eol`
+^^^^^
 
-Any other value set to `crlf` attribute is ignored and git acts
-as if the attribute is left unspecified.
+This attribute sets a specific line-ending style to be used in the
+working directory.  It enables end-of-line normalization without any
+content checks, effectively setting the `text` attribute.
 
+Set to string value "crlf"::
 
-The `core.autocrlf` conversion
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+       This setting forces git to normalize line endings for this
+       file on checkin and convert them to CRLF when the file is
+       checked out.
+
+Set to string value "lf"::
+
+       This setting forces git to normalize line endings to LF on
+       checkin and prevents conversion to CRLF when the file is
+       checked out.
+
+Backwards compatibility with `crlf` attribute
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+For backwards compatibility, the `crlf` attribute is interpreted as
+follows:
+
+------------------------
+crlf           text
+-crlf          -text
+crlf=input     eol=lf
+------------------------
+
+End-of-line conversion
+^^^^^^^^^^^^^^^^^^^^^^
+
+While git normally leaves file contents alone, it can be configured to
+normalize line endings to LF in the repository and, optionally, to
+convert them to CRLF when files are checked out.
+
+Here is an example that will make git normalize .txt, .vcproj and .sh
+files, ensure that .vcproj files have CRLF and .sh files have LF in
+the working directory, and prevent .jpg files from being normalized
+regardless of their content.
+
+------------------------
+*.txt          text
+*.vcproj       eol=crlf
+*.sh           eol=lf
+*.jpg          -text
+------------------------
+
+Other source code management systems normalize all text files in their
+repositories, and there are two ways to enable similar automatic
+normalization in git.
+
+If you simply want to have CRLF line endings in your working directory
+regardless of the repository you are working with, you can set the
+config variable "core.autocrlf" without changing any attributes.
+
+------------------------
+[core]
+       autocrlf = true
+------------------------
+
+This does not force normalization of all text files, but does ensure
+that text files that you introduce to the repository have their line
+endings normalized to LF when they are added, and that files that are
+already normalized in the repository stay normalized.
+
+If you want to interoperate with a source code management system that
+enforces end-of-line normalization, or you simply want all text files
+in your repository to be normalized, you should instead set the `text`
+attribute to "auto" for _all_ files.
 
-If the configuration variable `core.autocrlf` is false, no
-conversion is done.
+------------------------
+*      text=auto
+------------------------
+
+This ensures that all files that git considers to be text will have
+normalized (LF) line endings in the repository.  The `core.eol`
+configuration variable controls which line endings git will use for
+normalized files in your working directory; the default is to use the
+native line ending for your platform, or CRLF if `core.autocrlf` is
+set.
+
+NOTE: When `text=auto` normalization is enabled in an existing
+repository, any text files containing CRLFs should be normalized.  If
+they are not they will be normalized the next time someone tries to
+change them, causing unfortunate misattribution.  From a clean working
+directory:
+
+-------------------------------------------------
+$ echo "* text=auto" >>.gitattributes
+$ rm .git/index     # Remove the index to force git to
+$ git reset         # re-scan the working directory
+$ git status        # Show files that will be normalized
+$ git add -u
+$ git add .gitattributes
+$ git commit -m "Introduce end-of-line normalization"
+-------------------------------------------------
+
+If any files that should not be normalized show up in 'git status',
+unset their `text` attribute before running 'git add -u'.
+
+------------------------
+manual.pdf     -text
+------------------------
 
-When `core.autocrlf` is true, it means that the platform wants
-CRLF line endings for files in the working tree, and you want to
-convert them back to the normal LF line endings when checking
-in to the repository.
+Conversely, text files that git does not detect can have normalization
+enabled manually.
 
-When `core.autocrlf` is set to "input", line endings are
-converted to LF upon checkin, but there is no conversion done
-upon checkout.
+------------------------
+weirdchars.txt text
+------------------------
 
 If `core.safecrlf` is set to "true" or "warn", git verifies if
 the conversion is reversible for the current setting of
@@ -223,11 +324,11 @@ Interaction between checkin/checkout attributes
 In the check-in codepath, the worktree file is first converted
 with `filter` driver (if specified and corresponding driver
 defined), then the result is processed with `ident` (if
-specified), and then finally with `crlf` (again, if specified
+specified), and then finally with `text` (again, if specified
 and applicable).
 
 In the check-out codepath, the blob content is first converted
-with `crlf`, and then `ident` and fed to `filter`.
+with `text`, and then `ident` and fed to `filter`.
 
 
 Generating diff text
@@ -651,7 +752,7 @@ You do not want any end-of-line conversions applied to, nor textual diffs
 produced for, any binary file you track.  You would need to specify e.g.
 
 ------------
-*.jpg -crlf -diff
+*.jpg -text -diff
 ------------
 
 but that may become cumbersome, when you have many attributes.  Using
@@ -664,7 +765,7 @@ the same time.  The system knows a built-in attribute macro, `binary`:
 
 which is equivalent to the above.  Note that the attribute macros can only
 be "Set" (see the above example that sets "binary" macro as if it were an
-ordinary attribute --- setting it in turn unsets "crlf" and "diff").
+ordinary attribute --- setting it in turn unsets "text" and "diff").
 
 
 DEFINING ATTRIBUTE MACROS
@@ -675,7 +776,7 @@ at the toplevel (i.e. not in any subdirectory).  The built-in attribute
 macro "binary" is equivalent to:
 
 ------------
-[attr]binary -diff -crlf
+[attr]binary -diff -text
 ------------
 
 
index 98c459d..e10fa88 100644 (file)
@@ -83,16 +83,20 @@ Patterns have the following format:
 
  - 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.
+   pathname relative to the location of the `.gitignore` file
+   (relative to the toplevel of the work tree if not from a
+   `.gitignore` file).
 
  - Otherwise, git treats the pattern as a shell glob suitable
    for consumption by fnmatch(3) with the FNM_PATHNAME flag:
    wildcards in the pattern will not match a / in the pathname.
    For example, "Documentation/\*.html" matches
-   "Documentation/git.html" but not
-   "Documentation/ppc/ppc.html".  A leading slash matches the
-   beginning of the pathname; for example, "/*.c" matches
-   "cat-file.c" but not "mozilla-sha1/sha1.c".
+   "Documentation/git.html" but not "Documentation/ppc/ppc.html"
+   or "tools/perf/Documentation/perf.html".
+
+ - A leading slash matches the beginning of the pathname.
+   For example, "/*.c" matches "cat-file.c" but not
+   "mozilla-sha1/sha1.c".
 
 An example:
 
index 2135a8e..34d02a2 100755 (executable)
@@ -12,7 +12,7 @@ do
        then
                : did not match
        elif test -f "$T/$h" &&
-          diff -u -I'Last updated [0-9][0-9]-[A-Z][a-z][a-z]-' "$T/$h" "$h"
+          $DIFF -u -I'Last updated [0-9][0-9]-[A-Z][a-z][a-z]-' "$T/$h" "$h"
        then
                :; # up to date
        else
index b055a67..561cc9f 100644 (file)
@@ -11,7 +11,12 @@ have limited your view of history: for example, if you are
 only interested in changes related to a certain directory or
 file.
 
-Here are some additional details for each format:
+There are several built-in formats, and you can define
+additional formats by setting a pretty.<name>
+config option to either another format name, or a
+'format:' string, as described below (see
+linkgit:git-config[1]). Here are the details of the
+built-in formats:
 
 * 'oneline'
 
@@ -154,6 +159,10 @@ If you add a `-` (minus sign) after '%' of a placeholder, line-feeds that
 immediately precede the expansion are deleted if and only if the
 placeholder expands to an empty string.
 
+If you add a ` ` (space) after '%' of a placeholder, a space
+is inserted immediately before the expansion if and only if the
+placeholder expands to a non-empty string.
+
 * 'tformat:'
 +
 The 'tformat:' format works exactly like 'format:', except that it
index 066ade9..cc562a0 100644 (file)
@@ -393,6 +393,14 @@ Default mode::
        merges from the resulting history, as there are no selected
        commits contributing to this merge.
 
+--ancestry-path::
+
+       When given a range of commits to display (e.g. 'commit1..commit2'
+       or 'commit2 {caret}commit1'), only display commits that exist
+       directly on the ancestry chain between the 'commit1' and
+       'commit2', i.e. commits that are both descendants of 'commit1',
+       and ancestors of 'commit2'.
+
 A more detailed explanation follows.
 
 Suppose you specified `foo` as the <paths>.  We shall call commits
@@ -449,7 +457,7 @@ This results in:
 +
 -----------------------------------------------------------------------
          .-A---N---O
-        /         /
+        /     /   /
        I---------D
 -----------------------------------------------------------------------
 +
@@ -520,8 +528,6 @@ Note that without '\--full-history', this still simplifies merges: if
 one of the parents is TREESAME, we follow only that one, so the other
 sides of the merge are never walked.
 
-Finally, there is a fourth simplification mode available:
-
 --simplify-merges::
 
        First, build a history graph in the same way that
@@ -563,6 +569,46 @@ Note the major differences in `N` and `P` over '\--full-history':
   removed completely, because it had one parent and is TREESAME.
 --
 
+Finally, there is a fifth simplification mode available:
+
+--ancestry-path::
+
+       Limit the displayed commits to those directly on the ancestry
+       chain between the "from" and "to" commits in the given commit
+       range. I.e. only display commits that are ancestor of the "to"
+       commit, and descendants of the "from" commit.
++
+As an example use case, consider the following commit history:
++
+-----------------------------------------------------------------------
+           D---E-------F
+          /     \       \
+         B---C---G---H---I---J
+        /                     \
+       A-------K---------------L--M
+-----------------------------------------------------------------------
++
+A regular 'D..M' computes the set of commits that are ancestors of `M`,
+but excludes the ones that are ancestors of `D`. This is useful to see
+what happened to the history leading to `M` since `D`, in the sense
+that "what does `M` have that did not exist in `D`". The result in this
+example would be all the commits, except `A` and `B` (and `D` itself,
+of course).
++
+When we want to find out what commits in `M` are contaminated with the
+bug introduced by `D` and need fixing, however, we might want to view
+only the subset of 'D..M' that are actually descendants of `D`, i.e.
+excluding `C` and `K`. This is exactly what the '\--ancestry-path'
+option does. Applied to the 'D..M' range, it results in:
++
+-----------------------------------------------------------------------
+               E-------F
+                \       \
+                 G---H---I---J
+                              \
+                               L--M
+-----------------------------------------------------------------------
+
 The '\--simplify-by-decoration' option allows you to view only the
 big picture of the topology of the history, by omitting commits
 that are not referenced by tags.  Commits are marked as !TREESAME
index 44876fa..f18b4f4 100644 (file)
@@ -231,8 +231,9 @@ The function pointer in .proc has the following signature:
 
 
 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:
+because this facility is implemented by a thread in the same address
+space on most platforms (when pthreads is available), but by a pipe to
+a forked process otherwise:
 
 . It cannot change the program's state (global variables, environment,
   etc.) in a way that the caller notices; in other words, .in and .out
index b0cecaa..9aca8a1 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -8,6 +8,12 @@ all::
 # Define SANE_TOOL_PATH to a colon-separated list of paths to prepend
 # to PATH if your tools in /usr/bin are broken.
 #
+# Define SOCKLEN_T to a suitable type (such as 'size_t') if your
+# system headers do not define a socklen_t type.
+#
+# Define INLINE to a suitable substitute (such as '__inline' or '') if git
+# fails to compile with errors about undefined inline functions or similar.
+#
 # Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf()
 # or vsnprintf() return -1 instead of number of characters which would
 # have been written to the final string if enough space had been available.
@@ -227,6 +233,8 @@ all::
 #
 # Define CHECK_HEADER_DEPENDENCIES to check for problems in the hard-coded
 # dependency rules.
+#
+# Define NATIVE_CRLF if your platform uses CRLF for line endings.
 
 GIT-VERSION-FILE: FORCE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -249,7 +257,7 @@ endif
 
 CFLAGS = -g -O2 -Wall
 LDFLAGS =
-ALL_CFLAGS = $(CFLAGS)
+ALL_CFLAGS = $(CPPFLAGS) $(CFLAGS)
 ALL_LDFLAGS = $(LDFLAGS)
 STRIP ?= strip
 
@@ -272,6 +280,7 @@ mandir = share/man
 infodir = share/info
 gitexecdir = libexec/git-core
 sharedir = $(prefix)/share
+gitwebdir = $(sharedir)/gitweb
 template_dir = share/git-core/templates
 htmldir = share/doc/git-doc
 ifeq ($(prefix),/usr)
@@ -285,11 +294,12 @@ lib = lib
 # DESTDIR=
 pathsep = :
 
-export prefix bindir sharedir sysconfdir
+export prefix bindir sharedir sysconfdir gitwebdir
 
 CC = gcc
 AR = ar
 RM = rm -f
+DIFF = diff
 TAR = tar
 FIND = find
 INSTALL = install
@@ -297,6 +307,7 @@ RPMBUILD = rpmbuild
 TCL_PATH = tclsh
 TCLTK_PATH = wish
 PTHREAD_LIBS = -lpthread
+PTHREAD_CFLAGS =
 
 export TCL_PATH TCLTK_PATH
 
@@ -627,6 +638,7 @@ LIB_OBJS += tree-diff.o
 LIB_OBJS += tree.o
 LIB_OBJS += tree-walk.o
 LIB_OBJS += unpack-trees.o
+LIB_OBJS += url.o
 LIB_OBJS += usage.o
 LIB_OBJS += userdiff.o
 LIB_OBJS += utf8.o
@@ -739,6 +751,13 @@ EXTLIBS =
 # because maintaining the nesting to match is a pain.  If
 # we had "elif" things would have been much nicer...
 
+ifeq ($(uname_S),OSF1)
+       # Need this for u_short definitions et al
+       BASIC_CFLAGS += -D_OSF_SOURCE
+       SOCKLEN_T = int
+       NO_STRTOULL = YesPlease
+       NO_NSEC = YesPlease
+endif
 ifeq ($(uname_S),Linux)
        NO_STRLCPY = YesPlease
        NO_MKSTEMPS = YesPlease
@@ -813,6 +832,18 @@ ifeq ($(uname_S),SunOS)
        NO_MKDTEMP = YesPlease
        NO_MKSTEMPS = YesPlease
        NO_REGEX = YesPlease
+       ifeq ($(uname_R),5.6)
+               SOCKLEN_T = int
+               NO_HSTRERROR = YesPlease
+               NO_IPV6 = YesPlease
+               NO_SOCKADDR_STORAGE = YesPlease
+               NO_UNSETENV = YesPlease
+               NO_SETENV = YesPlease
+               NO_STRLCPY = YesPlease
+               NO_C99_FORMAT = YesPlease
+               NO_STRTOUMAX = YesPlease
+               GIT_TEST_CMP = cmp
+       endif
        ifeq ($(uname_R),5.7)
                NEEDS_RESOLV = YesPlease
                NO_IPV6 = YesPlease
@@ -822,18 +853,21 @@ ifeq ($(uname_S),SunOS)
                NO_STRLCPY = YesPlease
                NO_C99_FORMAT = YesPlease
                NO_STRTOUMAX = YesPlease
+               GIT_TEST_CMP = cmp
        endif
        ifeq ($(uname_R),5.8)
                NO_UNSETENV = YesPlease
                NO_SETENV = YesPlease
                NO_C99_FORMAT = YesPlease
                NO_STRTOUMAX = YesPlease
+               GIT_TEST_CMP = cmp
        endif
        ifeq ($(uname_R),5.9)
                NO_UNSETENV = YesPlease
                NO_SETENV = YesPlease
                NO_C99_FORMAT = YesPlease
                NO_STRTOUMAX = YesPlease
+               GIT_TEST_CMP = cmp
        endif
        INSTALL = /usr/ucb/install
        TAR = gtar
@@ -911,7 +945,13 @@ ifeq ($(uname_S),AIX)
        BASIC_CFLAGS += -D_LARGE_FILES
        ifeq ($(shell expr "$(uname_V)" : '[1234]'),1)
                NO_PTHREADS = YesPlease
+       else
+               PTHREAD_LIBS = -lpthread
+       endif
+       ifeq ($(shell expr "$(uname_V).$(uname_R)" : '5\.1'),3)
+               INLINE=''
        endif
+       GIT_TEST_CMP = cmp
 endif
 ifeq ($(uname_S),GNU)
        # GNU/Hurd
@@ -956,6 +996,7 @@ ifeq ($(uname_S),IRIX64)
        NEEDS_LIBGEN = YesPlease
 endif
 ifeq ($(uname_S),HP-UX)
+       INLINE = __inline
        NO_IPV6=YesPlease
        NO_SETENV=YesPlease
        NO_STRCASESTR=YesPlease
@@ -967,6 +1008,20 @@ ifeq ($(uname_S),HP-UX)
        NO_HSTRERROR = YesPlease
        NO_SYS_SELECT_H = YesPlease
        SNPRINTF_RETURNS_BOGUS = YesPlease
+       NO_NSEC = YesPlease
+       ifeq ($(uname_R),B.11.00)
+               NO_INET_NTOP = YesPlease
+               NO_INET_PTON = YesPlease
+       endif
+       ifeq ($(uname_R),B.10.20)
+               # Override HP-UX 11.x setting:
+               INLINE =
+               SOCKLEN_T = size_t
+               NO_PREAD = YesPlease
+               NO_INET_NTOP = YesPlease
+               NO_INET_PTON = YesPlease
+       endif
+       GIT_TEST_CMP = cmp
 endif
 ifeq ($(uname_S),Windows)
        GIT_VERSION := $(GIT_VERSION).MSVC
@@ -1003,6 +1058,7 @@ ifeq ($(uname_S),Windows)
        NO_CURL = YesPlease
        NO_PYTHON = YesPlease
        BLK_SHA1 = YesPlease
+       NATIVE_CRLF = YesPlease
 
        CC = compat/vcbuild/scripts/clink.pl
        AR = compat/vcbuild/scripts/lib.pl
@@ -1040,7 +1096,6 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        NO_STRTOUMAX = YesPlease
        NO_MKDTEMP = YesPlease
        NO_MKSTEMPS = YesPlease
-       SNPRINTF_RETURNS_BOGUS = YesPlease
        NO_SVN_TESTS = YesPlease
        NO_PERL_MAKEMAKER = YesPlease
        RUNTIME_PREFIX = YesPlease
@@ -1077,6 +1132,7 @@ endif
 -include config.mak
 
 ifdef CHECK_HEADER_DEPENDENCIES
+COMPUTE_HEADER_DEPENDENCIES =
 USE_COMPUTED_HEADER_DEPENDENCIES =
 endif
 
@@ -1092,6 +1148,14 @@ else
 BROKEN_PATH_FIX = '/^\# @@BROKEN_PATH_FIX@@$$/d'
 endif
 
+ifneq (,$(INLINE))
+       BASIC_CFLAGS += -Dinline=$(INLINE)
+endif
+
+ifneq (,$(SOCKLEN_T))
+       BASIC_CFLAGS += -Dsocklen_t=$(SOCKLEN_T)
+endif
+
 ifeq ($(uname_S),Darwin)
        ifndef NO_FINK
                ifeq ($(shell test -d /sw/lib && echo y),y)
@@ -1363,6 +1427,7 @@ endif
 ifdef NO_PTHREADS
        BASIC_CFLAGS += -DNO_PTHREADS
 else
+       BASIC_CFLAGS += $(PTHREAD_CFLAGS)
        EXTLIBS += $(PTHREAD_LIBS)
        LIB_OBJS += thread-utils.o
 endif
@@ -1387,6 +1452,10 @@ ifdef USE_NED_ALLOCATOR
        COMPAT_OBJS += compat/nedmalloc/nedmalloc.o
 endif
 
+ifdef GIT_TEST_CMP_USE_COPIED_CONTEXT
+       export GIT_TEST_CMP_USE_COPIED_CONTEXT
+endif
+
 ifeq ($(TCLTK_PATH),)
 NO_TCLTK=NoThanks
 endif
@@ -1443,11 +1512,13 @@ gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
 template_dir_SQ = $(subst ','\'',$(template_dir))
 htmldir_SQ = $(subst ','\'',$(htmldir))
 prefix_SQ = $(subst ','\'',$(prefix))
+gitwebdir_SQ = $(subst ','\'',$(gitwebdir))
 
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
 PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
 TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
+DIFF_SQ = $(subst ','\'',$(DIFF))
 
 LIBS = $(GITLIBS) $(EXTLIBS)
 
@@ -1474,7 +1545,7 @@ endif
 ALL_CFLAGS += $(BASIC_CFLAGS)
 ALL_LDFLAGS += $(BASIC_LDFLAGS)
 
-export TAR INSTALL DESTDIR SHELL_PATH
+export DIFF TAR INSTALL DESTDIR SHELL_PATH
 
 
 ### Build rules
@@ -1536,6 +1607,7 @@ define cmd_munge_script
 $(RM) $@ $@+ && \
 sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
     -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
+    -e 's|@@DIFF@@|$(DIFF_SQ)|' \
     -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
     -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
     -e $(BROKEN_PATH_FIX) \
@@ -1563,11 +1635,10 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
        sed -e '1{' \
            -e '        s|#!.*perl|#!$(PERL_PATH_SQ)|' \
            -e '        h' \
-           -e '        s=.*=use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB} || "@@INSTLIBDIR@@"));=' \
+           -e '        s=.*=use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB} || "'"$$INSTLIBDIR"'"));=' \
            -e '        H' \
            -e '        x' \
            -e '}' \
-           -e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR"'|g' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
            $@.perl >$@+ && \
        chmod +x $@+ && \
@@ -1579,45 +1650,38 @@ gitweb:
        $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) all
 
 ifdef JSMIN
-GITWEB_PROGRAMS += gitweb/gitweb.min.js
-GITWEB_JS = gitweb/gitweb.min.js
+GITWEB_PROGRAMS += gitweb/static/gitweb.min.js
+GITWEB_JS = gitweb/static/gitweb.min.js
 else
-GITWEB_JS = gitweb/gitweb.js
+GITWEB_JS = gitweb/static/gitweb.js
 endif
 ifdef CSSMIN
-GITWEB_PROGRAMS += gitweb/gitweb.min.css
-GITWEB_CSS = gitweb/gitweb.min.css
+GITWEB_PROGRAMS += gitweb/static/gitweb.min.css
+GITWEB_CSS = gitweb/static/gitweb.min.css
 else
-GITWEB_CSS = gitweb/gitweb.css
+GITWEB_CSS = gitweb/static/gitweb.css
 endif
 OTHER_PROGRAMS +=  gitweb/gitweb.cgi  $(GITWEB_PROGRAMS)
 gitweb/gitweb.cgi: gitweb/gitweb.perl $(GITWEB_PROGRAMS)
        $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
 
 ifdef JSMIN
-gitweb/gitweb.min.js: gitweb/gitweb.js
+gitweb/static/gitweb.min.js: gitweb/static/gitweb.js
        $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
 endif # JSMIN
 ifdef CSSMIN
-gitweb/gitweb.min.css: gitweb/gitweb.css
+gitweb/static/gitweb.min.css: gitweb/static/gitweb.css
        $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
 endif # CSSMIN
 
 
-git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css gitweb/gitweb.js
+git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/static/gitweb.css gitweb/static/gitweb.js
        $(QUIET_GEN)$(RM) $@ $@+ && \
        sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
            -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
-           -e '/@@GITWEB_CGI@@/r gitweb/gitweb.cgi' \
-           -e '/@@GITWEB_CGI@@/d' \
-           -e '/@@GITWEB_CSS@@/r $(GITWEB_CSS)' \
-           -e '/@@GITWEB_CSS@@/d' \
-           -e '/@@GITWEB_JS@@/r $(GITWEB_JS)' \
-           -e '/@@GITWEB_JS@@/d' \
+           -e 's|@@GITWEBDIR@@|$(gitwebdir_SQ)|g' \
            -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
-            -e 's|@@GITWEB_CSS_NAME@@|$(GITWEB_CSS)|' \
-            -e 's|@@GITWEB_JS_NAME@@|$(GITWEB_JS)|' \
            $@.sh > $@+ && \
        chmod +x $@+ && \
        mv $@+ $@
@@ -1669,7 +1733,10 @@ git.o git.spec \
 
 TEST_OBJS := $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS))
 GIT_OBJS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
-       git.o http.o http-walker.o remote-curl.o
+       git.o
+ifndef NO_CURL
+       GIT_OBJS += http.o http-walker.o remote-curl.o
+endif
 XDIFF_OBJS = xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \
        xdiff/xmerge.o xdiff/xpatience.o
 OBJECTS := $(GIT_OBJS) $(XDIFF_OBJS)
@@ -1886,10 +1953,18 @@ GIT-CFLAGS: FORCE
 GIT-BUILD-OPTIONS: FORCE
        @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
        @echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@
+       @echo DIFF=\''$(subst ','\'',$(subst ','\'',$(DIFF)))'\' >>$@
+       @echo PYTHON_PATH=\''$(subst ','\'',$(PYTHON_PATH_SQ))'\' >>$@
        @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
        @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
        @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
        @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@
+ifdef GIT_TEST_CMP
+       @echo GIT_TEST_CMP=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_CMP)))'\' >>$@
+endif
+ifdef GIT_TEST_CMP_USE_COPIED_CONTEXT
+       @echo GIT_TEST_CMP_USE_COPIED_CONTEXT=YesPlease >>$@
+endif
 
 ### Detect Tck/Tk interpreter path changes
 ifndef NO_TCLTK
@@ -1984,6 +2059,7 @@ install: all
        $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
 ifndef NO_PERL
        $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
+       $(MAKE) -C gitweb install
 endif
 ifndef NO_PYTHON
        $(MAKE) -C git_remote_helpers prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644 (file)
index 0000000..d399de2
--- /dev/null
@@ -0,0 +1,40 @@
+dnl Check for socklen_t: historically on BSD it is an int, and in
+dnl POSIX 1g it is a type of its own, but some platforms use different
+dnl types for the argument to getsockopt, getpeername, etc.  So we
+dnl have to test to find something that will work.
+AC_DEFUN([TYPE_SOCKLEN_T],
+[
+   AC_CHECK_TYPE([socklen_t], ,[
+      AC_MSG_CHECKING([for socklen_t equivalent])
+      AC_CACHE_VAL([git_cv_socklen_t_equiv],
+      [
+         # Systems have either "struct sockaddr *" or
+         # "void *" as the second argument to getpeername
+         git_cv_socklen_t_equiv=
+         for arg2 in "struct sockaddr" void; do
+            for t in int size_t unsigned long "unsigned long"; do
+               AC_TRY_COMPILE([
+                  #include <sys/types.h>
+                  #include <sys/socket.h>
+
+                  int getpeername (int, $arg2 *, $t *);
+               ],[
+                  $t len;
+                  getpeername(0,0,&len);
+               ],[
+                  git_cv_socklen_t_equiv="$t"
+                  break 2
+               ])
+            done
+         done
+
+         if test "x$git_cv_socklen_t_equiv" = x; then
+            AC_MSG_ERROR([Cannot find a type to use in place of socklen_t])
+         fi
+      ])
+      AC_MSG_RESULT($git_cv_socklen_t_equiv)
+      AC_DEFINE_UNQUOTED(socklen_t, $git_cv_socklen_t_equiv,
+                       [type to use in place of socklen_t if not defined])],
+      [#include <sys/types.h>
+#include <sys/socket.h>])
+])
diff --git a/attr.c b/attr.c
index 7467baf..8ba606c 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -287,7 +287,7 @@ static void free_attr_elem(struct attr_stack *e)
 }
 
 static const char *builtin_attr[] = {
-       "[attr]binary -diff -crlf",
+       "[attr]binary -diff -text",
        NULL,
 };
 
diff --git a/attr.h b/attr.h
index 450f49d..8b3f19b 100644 (file)
--- a/attr.h
+++ b/attr.h
@@ -34,7 +34,7 @@ int git_checkattr(const char *path, int, struct git_attr_check *);
 enum git_attr_direction {
        GIT_ATTR_CHECKIN,
        GIT_ATTR_CHECKOUT,
-       GIT_ATTR_INDEX,
+       GIT_ATTR_INDEX
 };
 void git_attr_set_direction(enum git_attr_direction, struct index_state *);
 
index d893475..c0054a0 100644 (file)
@@ -70,6 +70,7 @@
  */
 
 #if defined(__i386__) || defined(__x86_64__) || \
+    defined(_M_IX86) || defined(_M_X64) || \
     defined(__ppc__) || defined(__ppc64__) || \
     defined(__powerpc__) || defined(__powerpc64__) || \
     defined(__s390__) || defined(__s390x__)
@@ -236,13 +237,13 @@ void blk_SHA1_Init(blk_SHA_CTX *ctx)
 
 void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len)
 {
-       int lenW = ctx->size & 63;
+       unsigned int lenW = ctx->size & 63;
 
        ctx->size += len;
 
        /* Read the data into W and process blocks as they get full */
        if (lenW) {
-               int left = 64 - lenW;
+               unsigned int left = 64 - lenW;
                if (len < left)
                        left = len;
                memcpy(lenW + (char *)ctx->W, data, left);
@@ -269,8 +270,8 @@ void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx)
        int i;
 
        /* Pad with a binary 1 (ie 0x80), then zeroes, then length */
-       padlen[0] = htonl(ctx->size >> 29);
-       padlen[1] = htonl(ctx->size << 3);
+       padlen[0] = htonl((uint32_t)(ctx->size >> 29));
+       padlen[1] = htonl((uint32_t)(ctx->size << 3));
 
        i = ctx->size & 63;
        blk_SHA1_Update(ctx, pad, 1+ (63 & (55 - i)));
index 5c887ef..ed6ee26 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -16,19 +16,20 @@ extern const char *help_unknown_cmd(const char *cmd);
 extern void prune_packed_objects(int);
 extern int fmt_merge_msg(int merge_summary, struct strbuf *in,
        struct strbuf *out);
+extern int fmt_merge_msg_shortlog(struct strbuf *in, struct strbuf *out);
 extern int commit_notes(struct notes_tree *t, const char *msg);
 
 struct notes_rewrite_cfg {
        struct notes_tree **trees;
        const char *cmd;
        int enabled;
-       combine_notes_fn *combine;
+       combine_notes_fn combine;
        struct string_list *refs;
        int refs_from_env;
        int mode_from_env;
 };
 
-combine_notes_fn *parse_combine_notes_fn(const char *v);
+combine_notes_fn parse_combine_notes_fn(const char *v);
 struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd);
 int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
                          const unsigned char *from_obj, const unsigned char *to_obj);
@@ -36,6 +37,8 @@ void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c);
 
 extern int check_pager_config(const char *cmd);
 
+extern int textconv_object(const char *path, const unsigned char *sha1, char **buf, unsigned long *buf_size);
+
 extern int cmd_add(int argc, const char **argv, const char *prefix);
 extern int cmd_annotate(int argc, const char **argv, const char *prefix);
 extern int cmd_apply(int argc, const char **argv, const char *prefix);
index 87d2980..17149cf 100644 (file)
@@ -261,12 +261,14 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
 {
        char *file = xstrdup(git_path("ADD_EDIT.patch"));
        const char *apply_argv[] = { "apply", "--recount", "--cached",
-               file, NULL };
+               NULL, NULL };
        struct child_process child;
        struct rev_info rev;
        int out;
        struct stat st;
 
+       apply_argv[3] = file;
+
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
 
        if (read_cache() < 0)
index 8fc5ec3..562e534 100644 (file)
@@ -56,7 +56,7 @@ static enum ws_error_action {
        nowarn_ws_error,
        warn_on_ws_error,
        die_on_ws_error,
-       correct_ws_error,
+       correct_ws_error
 } ws_error_action = warn_on_ws_error;
 static int whitespace_error;
 static int squelch_whitespace_errors = 5;
@@ -64,7 +64,7 @@ static int applied_after_fixing_ws;
 
 static enum ws_ignore {
        ignore_ws_none,
-       ignore_ws_change,
+       ignore_ws_change
 } ws_ignore_action = ignore_ws_none;
 
 
index 4dd4c3f..01e62fd 100644 (file)
@@ -20,6 +20,7 @@
 #include "mailmap.h"
 #include "parse-options.h"
 #include "utf8.h"
+#include "userdiff.h"
 
 static char blame_usage[] = "git blame [options] [rev-opts] [rev] [--] file";
 
@@ -39,7 +40,7 @@ static int show_root;
 static int reverse;
 static int blank_boundary;
 static int incremental;
-static int xdl_opts = XDF_NEED_MINIMAL;
+static int xdl_opts;
 
 static enum date_mode blame_date_mode = DATE_ISO8601;
 static size_t blame_date_width;
@@ -85,17 +86,51 @@ struct origin {
        char path[FLEX_ARRAY];
 };
 
+/*
+ * Prepare diff_filespec and convert it using diff textconv API
+ * if the textconv driver exists.
+ * Return 1 if the conversion succeeds, 0 otherwise.
+ */
+int textconv_object(const char *path,
+                   const unsigned char *sha1,
+                   char **buf,
+                   unsigned long *buf_size)
+{
+       struct diff_filespec *df;
+       struct userdiff_driver *textconv;
+
+       df = alloc_filespec(path);
+       fill_filespec(df, sha1, S_IFREG | 0664);
+       textconv = get_textconv(df);
+       if (!textconv) {
+               free_filespec(df);
+               return 0;
+       }
+
+       *buf_size = fill_textconv(textconv, df, buf);
+       free_filespec(df);
+       return 1;
+}
+
 /*
  * Given an origin, prepare mmfile_t structure to be used by the
  * diff machinery
  */
-static void fill_origin_blob(struct origin *o, mmfile_t *file)
+static void fill_origin_blob(struct diff_options *opt,
+                            struct origin *o, mmfile_t *file)
 {
        if (!o->file.ptr) {
                enum object_type type;
+               unsigned long file_size;
+
                num_read_blob++;
-               file->ptr = read_sha1_file(o->blob_sha1, &type,
-                                          (unsigned long *)(&(file->size)));
+               if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
+                   textconv_object(o->path, o->blob_sha1, &file->ptr, &file_size))
+                       ;
+               else
+                       file->ptr = read_sha1_file(o->blob_sha1, &type, &file_size);
+               file->size = file_size;
+
                if (!file->ptr)
                        die("Cannot read blob %s for path %s",
                            sha1_to_hex(o->blob_sha1),
@@ -282,7 +317,6 @@ static struct origin *get_origin(struct scoreboard *sb,
 static int fill_blob_sha1(struct origin *origin)
 {
        unsigned mode;
-
        if (!is_null_sha1(origin->blob_sha1))
                return 0;
        if (get_tree_entry(origin->commit->object.sha1,
@@ -733,16 +767,17 @@ static int pass_blame_to_parent(struct scoreboard *sb,
 {
        int last_in_target;
        mmfile_t file_p, file_o;
-       struct blame_chunk_cb_data d = { sb, target, parent, 0, 0 };
+       struct blame_chunk_cb_data d;
        xpparam_t xpp;
        xdemitconf_t xecfg;
-
+       memset(&d, 0, sizeof(d));
+       d.sb = sb; d.target = target; d.parent = parent;
        last_in_target = find_last_in_target(sb, target);
        if (last_in_target < 0)
                return 1; /* nothing remains for this target */
 
-       fill_origin_blob(parent, &file_p);
-       fill_origin_blob(target, &file_o);
+       fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
+       fill_origin_blob(&sb->revs->diffopt, target, &file_o);
        num_get_patch++;
 
        memset(&xpp, 0, sizeof(xpp));
@@ -875,10 +910,11 @@ static void find_copy_in_blob(struct scoreboard *sb,
        const char *cp;
        int cnt;
        mmfile_t file_o;
-       struct handle_split_cb_data d = { sb, ent, parent, split, 0, 0 };
+       struct handle_split_cb_data d;
        xpparam_t xpp;
        xdemitconf_t xecfg;
-
+       memset(&d, 0, sizeof(d));
+       d.sb = sb; d.ent = ent; d.parent = parent; d.split = split;
        /*
         * Prepare mmfile that contains only the lines in ent.
         */
@@ -922,7 +958,7 @@ static int find_move_in_parent(struct scoreboard *sb,
        if (last_in_target < 0)
                return 1; /* nothing remains for this target */
 
-       fill_origin_blob(parent, &file_p);
+       fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
        if (!file_p.ptr)
                return 0;
 
@@ -1063,7 +1099,7 @@ static int find_copy_in_parent(struct scoreboard *sb,
 
                        norigin = get_origin(sb, parent, p->one->path);
                        hashcpy(norigin->blob_sha1, p->one->sha1);
-                       fill_origin_blob(norigin, &file_p);
+                       fill_origin_blob(&sb->revs->diffopt, norigin, &file_p);
                        if (!file_p.ptr)
                                continue;
 
@@ -1983,6 +2019,16 @@ static int git_blame_config(const char *var, const char *value, void *cb)
                blame_date_mode = parse_date_format(value);
                return 0;
        }
+
+       switch (userdiff_config(var, value)) {
+       case 0:
+               break;
+       case -1:
+               return -1;
+       default:
+               return 0;
+       }
+
        return git_default_config(var, value, cb);
 }
 
@@ -1990,7 +2036,9 @@ static int git_blame_config(const char *var, const char *value, void *cb)
  * Prepare a dummy commit that represents the work tree (or staged) item.
  * Note that annotating work tree item never works in the reverse.
  */
-static struct commit *fake_working_tree_commit(const char *path, const char *contents_from)
+static struct commit *fake_working_tree_commit(struct diff_options *opt,
+                                              const char *path,
+                                              const char *contents_from)
 {
        struct commit *commit;
        struct origin *origin;
@@ -2018,6 +2066,7 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
        if (!contents_from || strcmp("-", contents_from)) {
                struct stat st;
                const char *read_from;
+               unsigned long buf_len;
 
                if (contents_from) {
                        if (stat(contents_from, &st) < 0)
@@ -2030,9 +2079,13 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
                        read_from = path;
                }
                mode = canon_mode(st.st_mode);
+
                switch (st.st_mode & S_IFMT) {
                case S_IFREG:
-                       if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
+                       if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
+                           textconv_object(read_from, null_sha1, &buf.buf, &buf_len))
+                               buf.len = buf_len;
+                       else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
                                die_errno("cannot open or read '%s'", read_from);
                        break;
                case S_IFLNK:
@@ -2248,6 +2301,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        git_config(git_blame_config, NULL);
        init_revisions(&revs, NULL);
        revs.date_mode = blame_date_mode;
+       DIFF_OPT_SET(&revs.diffopt, ALLOW_TEXTCONV);
 
        save_commit_buffer = 0;
        dashdash_pos = 0;
@@ -2384,7 +2438,8 @@ parse_done:
                 * or "--contents".
                 */
                setup_work_tree();
-               sb.final = fake_working_tree_commit(path, contents_from);
+               sb.final = fake_working_tree_commit(&sb.revs->diffopt,
+                                                   path, contents_from);
                add_pending_object(&revs, &(sb.final->object), ":");
        }
        else if (contents_from)
@@ -2411,8 +2466,14 @@ parse_done:
                if (fill_blob_sha1(o))
                        die("no such path %s in %s", path, final_commit_name);
 
-               sb.final_buf = read_sha1_file(o->blob_sha1, &type,
-                                             &sb.final_buf_size);
+               if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&
+                   textconv_object(path, o->blob_sha1, (char **) &sb.final_buf,
+                                   &sb.final_buf_size))
+                       ;
+               else
+                       sb.final_buf = read_sha1_file(o->blob_sha1, &type,
+                                                     &sb.final_buf_size);
+
                if (!sb.final_buf)
                        die("Cannot read blob %s for path %s",
                            sha1_to_hex(o->blob_sha1),
index 6cf7e72..87976f0 100644 (file)
@@ -43,13 +43,13 @@ enum color_branch {
        BRANCH_COLOR_PLAIN = 1,
        BRANCH_COLOR_REMOTE = 2,
        BRANCH_COLOR_LOCAL = 3,
-       BRANCH_COLOR_CURRENT = 4,
+       BRANCH_COLOR_CURRENT = 4
 };
 
 static enum merge_filter {
        NO_FILTER = 0,
        SHOW_NOT_MERGED,
-       SHOW_MERGED,
+       SHOW_MERGED
 } merge_filter;
 static unsigned char merge_filter_ref[20];
 
@@ -257,9 +257,15 @@ static char *resolve_symref(const char *src, const char *prefix)
        return xstrdup(dst);
 }
 
+struct append_ref_cb {
+       struct ref_list *ref_list;
+       int ret;
+};
+
 static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
 {
-       struct ref_list *ref_list = (struct ref_list*)(cb_data);
+       struct append_ref_cb *cb = (struct append_ref_cb *)(cb_data);
+       struct ref_list *ref_list = cb->ref_list;
        struct ref_item *newitem;
        struct commit *commit;
        int kind, i;
@@ -293,8 +299,10 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
        commit = NULL;
        if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) {
                commit = lookup_commit_reference_gently(sha1, 1);
-               if (!commit)
-                       return error("branch '%s' does not point at a commit", refname);
+               if (!commit) {
+                       cb->ret = error("branch '%s' does not point at a commit", refname);
+                       return 0;
+               }
 
                /* Filter with with_commit if specified */
                if (!is_descendant_of(commit, ref_list->with_commit))
@@ -484,9 +492,10 @@ static void show_detached(struct ref_list *ref_list)
        }
 }
 
-static void print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit)
+static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit)
 {
        int i;
+       struct append_ref_cb cb;
        struct ref_list ref_list;
 
        memset(&ref_list, 0, sizeof(ref_list));
@@ -496,7 +505,9 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
        ref_list.with_commit = with_commit;
        if (merge_filter != NO_FILTER)
                init_revisions(&ref_list.revs, NULL);
-       for_each_rawref(append_ref, &ref_list);
+       cb.ref_list = &ref_list;
+       cb.ret = 0;
+       for_each_rawref(append_ref, &cb);
        if (merge_filter != NO_FILTER) {
                struct commit *filter;
                filter = lookup_commit_reference_gently(merge_filter_ref, 0);
@@ -527,6 +538,11 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
        }
 
        free_ref_list(&ref_list);
+
+       if (cb.ret)
+               error("some refs could not be read");
+
+       return cb.ret;
 }
 
 static void rename_branch(const char *oldname, const char *newname, int force)
@@ -679,7 +695,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        if (delete)
                return delete_branches(argc, argv, delete > 1, kinds);
        else if (argc == 0)
-               print_ref_list(kinds, detached, verbose, abbrev, with_commit);
+               return print_ref_list(kinds, detached, verbose, abbrev, with_commit);
        else if (rename && (argc == 1))
                rename_branch(head, argv[0], rename > 1);
        else if (rename && (argc == 2))
index a933eaa..76ec3fe 100644 (file)
@@ -9,6 +9,8 @@
 #include "tree.h"
 #include "builtin.h"
 #include "parse-options.h"
+#include "diff.h"
+#include "userdiff.h"
 
 #define BATCH 1
 #define BATCH_CHECK 2
@@ -84,10 +86,11 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
 {
        unsigned char sha1[20];
        enum object_type type;
-       void *buf;
+       char *buf;
        unsigned long size;
+       struct object_context obj_context;
 
-       if (get_sha1(obj_name, sha1))
+       if (get_sha1_with_context(obj_name, sha1, &obj_context))
                die("Not a valid object name %s", obj_name);
 
        buf = NULL;
@@ -118,7 +121,9 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
 
                /* custom pretty-print here */
                if (type == OBJ_TREE) {
-                       const char *ls_args[3] = {"ls-tree", obj_name, NULL};
+                       const char *ls_args[3] = { NULL };
+                       ls_args[0] =  "ls-tree";
+                       ls_args[1] =  obj_name;
                        return cmd_ls_tree(2, ls_args, NULL);
                }
 
@@ -132,6 +137,17 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
 
                /* otherwise just spit out the data */
                break;
+
+       case 'c':
+               if (!obj_context.path[0])
+                       die("git cat-file --textconv %s: <object> must be <sha1:path>",
+                           obj_name);
+
+               if (!textconv_object(obj_context.path, sha1, &buf, &size))
+                       die("git cat-file --textconv: unable to run textconv on %s",
+                           obj_name);
+               break;
+
        case 0:
                buf = read_object_with_reference(sha1, exp_type, &size, NULL);
                break;
@@ -201,11 +217,25 @@ static int batch_objects(int print_contents)
 }
 
 static const char * const cat_file_usage[] = {
-       "git cat-file (-t|-s|-e|-p|<type>) <object>",
+       "git cat-file (-t|-s|-e|-p|<type>|--textconv) <object>",
        "git cat-file (--batch|--batch-check) < <list_of_objects>",
        NULL
 };
 
+static int git_cat_file_config(const char *var, const char *value, void *cb)
+{
+       switch (userdiff_config(var, value)) {
+       case 0:
+               break;
+       case -1:
+               return -1;
+       default:
+               return 0;
+       }
+
+       return git_default_config(var, value, cb);
+}
+
 int cmd_cat_file(int argc, const char **argv, const char *prefix)
 {
        int opt = 0, batch = 0;
@@ -218,6 +248,8 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
                OPT_SET_INT('e', NULL, &opt,
                            "exit with zero when there's no error", 'e'),
                OPT_SET_INT('p', NULL, &opt, "pretty-print object's content", 'p'),
+               OPT_SET_INT(0, "textconv", &opt,
+                           "for blob objects, run textconv on object's content", 'c'),
                OPT_SET_INT(0, "batch", &batch,
                            "show info and content of objects fed from the standard input",
                            BATCH),
@@ -227,7 +259,7 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
 
-       git_config(git_default_config, NULL);
+       git_config(git_cat_file_config, NULL);
 
        if (argc != 3 && argc != 2)
                usage_with_options(cat_file_usage, options);
index c382521..1994be9 100644 (file)
@@ -493,7 +493,24 @@ static void update_refs_for_switch(struct checkout_opts *opts,
        struct strbuf msg = STRBUF_INIT;
        const char *old_desc;
        if (opts->new_branch) {
-               if (!opts->new_orphan_branch)
+               if (opts->new_orphan_branch) {
+                       if (opts->new_branch_log && !log_all_ref_updates) {
+                               int temp;
+                               char log_file[PATH_MAX];
+                               char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
+
+                               temp = log_all_ref_updates;
+                               log_all_ref_updates = 1;
+                               if (log_ref_setup(ref_name, log_file, sizeof(log_file))) {
+                                       fprintf(stderr, "Can not do reflog for '%s'\n",
+                                           opts->new_orphan_branch);
+                                       log_all_ref_updates = temp;
+                                       return;
+                               }
+                               log_all_ref_updates = temp;
+                       }
+               }
+               else
                        create_branch(old->name, opts->new_branch, new->name, 0,
                                      opts->new_branch_log, opts->track);
                new->name = opts->new_branch;
@@ -517,6 +534,14 @@ static void update_refs_for_switch(struct checkout_opts *opts,
                                        opts->new_branch ? " a new" : "",
                                        new->name);
                }
+               if (old->path && old->name) {
+                       char log_file[PATH_MAX], ref_file[PATH_MAX];
+
+                       git_snpath(log_file, sizeof(log_file), "logs/%s", old->path);
+                       git_snpath(ref_file, sizeof(ref_file), "%s", old->path);
+                       if (!file_exists(ref_file) && file_exists(log_file))
+                               remove_path(log_file);
+               }
        } else if (strcmp(new->name, "HEAD")) {
                update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
                           REF_NODEREF, DIE_ON_ERR);
@@ -611,7 +636,8 @@ static int check_tracking_name(const char *refname, const unsigned char *sha1,
 
 static const char *unique_tracking_name(const char *name)
 {
-       struct tracking_name_data cb_data = { name, NULL, 1 };
+       struct tracking_name_data cb_data = { NULL, NULL, 1 };
+       cb_data.name = name;
        for_each_ref(check_tracking_name, &cb_data);
        if (cb_data.unique)
                return cb_data.remote;
@@ -684,8 +710,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        if (opts.new_orphan_branch) {
                if (opts.new_branch)
                        die("--orphan and -b are mutually exclusive");
-               if (opts.track > 0 || opts.new_branch_log)
-                       die("--orphan cannot be used with -t or -l");
+               if (opts.track > 0)
+                       die("--orphan cannot be used with -t");
                opts.new_branch = opts.new_orphan_branch;
        }
 
index 4457922..efb1e6f 100644 (file)
@@ -464,7 +464,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        set_git_dir(make_absolute_path(git_dir));
 
        if (0 <= option_verbosity)
-               printf("Cloning into %s...\n", get_git_dir());
+               printf("Cloning into %s%s...\n",
+                      option_bare ? "bare repository " : "", dir);
        init_db(option_template, INIT_DB_QUIET);
 
        /*
index ddf77e4..3d99cf9 100644 (file)
@@ -48,6 +48,11 @@ static const char implicit_ident_advice[] =
 "\n"
 "    git commit --amend --author='Your Name <you@example.com>'\n";
 
+static const char empty_amend_advice[] =
+"You asked to amend the most recent commit, but doing so would make\n"
+"it empty. You can repeat your command with --allow-empty, or you can\n"
+"remove the commit entirely with \"git reset HEAD^\".\n";
+
 static unsigned char head_sha1[20];
 
 static char *use_message_buffer;
@@ -57,7 +62,7 @@ static struct lock_file false_lock; /* used only for partial commits */
 static enum {
        COMMIT_AS_IS = 1,
        COMMIT_NORMAL,
-       COMMIT_PARTIAL,
+       COMMIT_PARTIAL
 } commit_style;
 
 static const char *logfile, *force_author;
@@ -78,7 +83,7 @@ static char *untracked_files_arg, *force_date;
 static enum {
        CLEANUP_SPACE,
        CLEANUP_NONE,
-       CLEANUP_ALL,
+       CLEANUP_ALL
 } cleanup_mode;
 static char *cleanup_arg;
 
@@ -91,8 +96,9 @@ static int null_termination;
 static enum {
        STATUS_FORMAT_LONG,
        STATUS_FORMAT_SHORT,
-       STATUS_FORMAT_PORCELAIN,
+       STATUS_FORMAT_PORCELAIN
 } status_format = STATUS_FORMAT_LONG;
+static int status_show_branch;
 
 static int opt_parse_m(const struct option *opt, const char *arg, int unset)
 {
@@ -134,6 +140,7 @@ static struct option builtin_commit_options[] = {
        OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"),
        OPT_SET_INT(0, "short", &status_format, "show status concisely",
                    STATUS_FORMAT_SHORT),
+       OPT_BOOLEAN(0, "branch", &status_show_branch, "show branch information"),
        OPT_SET_INT(0, "porcelain", &status_format,
                    "show porcelain output format", STATUS_FORMAT_PORCELAIN),
        OPT_BOOLEAN('z', "null", &null_termination,
@@ -424,7 +431,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
 
        switch (status_format) {
        case STATUS_FORMAT_SHORT:
-               wt_shortstatus_print(s, null_termination);
+               wt_shortstatus_print(s, null_termination, status_show_branch);
                break;
        case STATUS_FORMAT_PORCELAIN:
                wt_porcelain_print(s, null_termination);
@@ -462,15 +469,21 @@ static void determine_author_info(void)
                if (!a)
                        die("invalid commit: %s", use_message);
 
-               lb = strstr(a + 8, " <");
-               rb = strstr(a + 8, "> ");
-               eol = strchr(a + 8, '\n');
-               if (!lb || !rb || !eol)
+               lb = strchrnul(a + strlen("\nauthor "), '<');
+               rb = strchrnul(lb, '>');
+               eol = strchrnul(rb, '\n');
+               if (!*lb || !*rb || !*eol)
                        die("invalid commit: %s", use_message);
 
-               name = xstrndup(a + 8, lb - (a + 8));
-               email = xstrndup(lb + 2, rb - (lb + 2));
-               date = xstrndup(rb + 2, eol - (rb + 2));
+               if (lb == a + strlen("\nauthor "))
+                       /* \nauthor <foo@example.com> */
+                       name = xcalloc(1, 1);
+               else
+                       name = xmemdupz(a + strlen("\nauthor "),
+                                       (lb - strlen(" ") -
+                                        (a + strlen("\nauthor "))));
+               email = xmemdupz(lb + strlen("<"), rb - (lb + strlen("<")));
+               date = xmemdupz(rb + strlen("> "), eol - (rb + strlen("> ")));
        }
 
        if (force_author) {
@@ -700,6 +713,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        if (!commitable && !in_merge && !allow_empty &&
            !(amend && is_a_merge(head_sha1))) {
                run_status(stdout, index_file, prefix, 0, s);
+               if (amend)
+                       fputs(empty_amend_advice, stderr);
                return 0;
        }
 
@@ -724,7 +739,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
        if (use_editor) {
                char index[PATH_MAX];
-               const char *env[2] = { index, NULL };
+               const char *env[2] = { NULL };
+               env[0] =  index;
                snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
                if (launch_editor(git_path(commit_editmsg), NULL, env)) {
                        fprintf(stderr,
@@ -1030,6 +1046,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)
                OPT__VERBOSE(&verbose),
                OPT_SET_INT('s', "short", &status_format,
                            "show status concisely", STATUS_FORMAT_SHORT),
+               OPT_BOOLEAN('b', "branch", &status_show_branch,
+                           "show branch information"),
                OPT_SET_INT(0, "porcelain", &status_format,
                            "show porcelain output format",
                            STATUS_FORMAT_PORCELAIN),
@@ -1082,7 +1100,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 
        switch (status_format) {
        case STATUS_FORMAT_SHORT:
-               wt_shortstatus_print(&s, null_termination);
+               wt_shortstatus_print(&s, null_termination, status_show_branch);
                break;
        case STATUS_FORMAT_PORCELAIN:
                wt_porcelain_print(&s, null_termination);
@@ -1157,13 +1175,11 @@ static void print_summary(const char *prefix, const unsigned char *sha1)
                initial_commit ? " (root-commit)" : "");
 
        if (!log_tree_commit(&rev, commit)) {
-               struct pretty_print_context ctx = {0};
-               struct strbuf buf = STRBUF_INIT;
-               ctx.date_mode = DATE_NORMAL;
-               format_commit_message(commit, format.buf + 7, &buf, &ctx);
-               printf("%s\n", buf.buf);
-               strbuf_release(&buf);
+               rev.always_show_header = 1;
+               rev.use_terminator = 1;
+               log_tree_commit(&rev, commit);
        }
+
        strbuf_release(&format);
 }
 
@@ -1251,13 +1267,16 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        }
 
        /* Determine parents */
+       reflog_msg = getenv("GIT_REFLOG_ACTION");
        if (initial_commit) {
-               reflog_msg = "commit (initial)";
+               if (!reflog_msg)
+                       reflog_msg = "commit (initial)";
        } else if (amend) {
                struct commit_list *c;
                struct commit *commit;
 
-               reflog_msg = "commit (amend)";
+               if (!reflog_msg)
+                       reflog_msg = "commit (amend)";
                commit = lookup_commit(head_sha1);
                if (!commit || parse_commit(commit))
                        die("could not parse HEAD commit");
@@ -1268,7 +1287,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                struct strbuf m = STRBUF_INIT;
                FILE *fp;
 
-               reflog_msg = "commit (merge)";
+               if (!reflog_msg)
+                       reflog_msg = "commit (merge)";
                pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;
                fp = fopen(git_path("MERGE_HEAD"), "r");
                if (fp == NULL)
@@ -1291,7 +1311,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                if (allow_fast_forward)
                        parents = reduce_heads(parents);
        } else {
-               reflog_msg = "commit";
+               if (!reflog_msg)
+                       reflog_msg = "commit";
                pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;
        }
 
index 8470850..5cb369c 100644 (file)
@@ -574,9 +574,10 @@ static void find_non_local_tags(struct transport *transport,
 {
        struct string_list existing_refs = { NULL, 0, 0, 0 };
        struct string_list remote_refs = { NULL, 0, 0, 0 };
-       struct tag_data data = {head, tail};
+       struct tag_data data;
        const struct ref *ref;
        struct string_list_item *item = NULL;
+       data.head = head; data.tail = tail;
 
        for_each_ref(add_existing, &existing_refs);
        for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
@@ -778,7 +779,8 @@ static int get_remote_group(const char *key, const char *value, void *priv)
 static int add_remote_or_group(const char *name, struct string_list *list)
 {
        int prev_nr = list->nr;
-       struct remote_group_data g = { name, list };
+       struct remote_group_data g;
+       g.name = name; g.list = list;
 
        git_config(get_remote_group, &g);
        if (list->nr == prev_nr) {
index 379a031..4420425 100644 (file)
@@ -202,35 +202,10 @@ static void shortlog(const char *name, unsigned char *sha1,
        string_list_clear(&subjects, 0);
 }
 
-int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
-       int limit = 20, i = 0, pos = 0;
+static void do_fmt_merge_msg_title(struct strbuf *out,
+       const char *current_branch) {
+       int i = 0;
        char *sep = "";
-       unsigned char head_sha1[20];
-       const char *current_branch;
-
-       /* get current branch */
-       current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
-       if (!current_branch)
-               die("No current branch");
-       if (!prefixcmp(current_branch, "refs/heads/"))
-               current_branch += 11;
-
-       /* get a line */
-       while (pos < in->len) {
-               int len;
-               char *newline, *p = in->buf + pos;
-
-               newline = strchr(p, '\n');
-               len = newline ? newline - p : strlen(p);
-               pos += len + !!newline;
-               i++;
-               p[len] = 0;
-               if (handle_line(p))
-                       die ("Error in line %d: %.*s", i, len, p);
-       }
-
-       if (!srcs.nr)
-               return 0;
 
        strbuf_addstr(out, "Merge ");
        for (i = 0; i < srcs.nr; i++) {
@@ -278,6 +253,40 @@ int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
                strbuf_addch(out, '\n');
        else
                strbuf_addf(out, " into %s\n", current_branch);
+}
+
+static int do_fmt_merge_msg(int merge_title, int merge_summary,
+       struct strbuf *in, struct strbuf *out) {
+       int limit = 20, i = 0, pos = 0;
+       unsigned char head_sha1[20];
+       const char *current_branch;
+
+       /* get current branch */
+       current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
+       if (!current_branch)
+               die("No current branch");
+       if (!prefixcmp(current_branch, "refs/heads/"))
+               current_branch += 11;
+
+       /* get a line */
+       while (pos < in->len) {
+               int len;
+               char *newline, *p = in->buf + pos;
+
+               newline = strchr(p, '\n');
+               len = newline ? newline - p : strlen(p);
+               pos += len + !!newline;
+               i++;
+               p[len] = 0;
+               if (handle_line(p))
+                       die ("Error in line %d: %.*s", i, len, p);
+       }
+
+       if (!srcs.nr)
+               return 0;
+
+       if (merge_title)
+               do_fmt_merge_msg_title(out, current_branch);
 
        if (merge_summary) {
                struct commit *head;
@@ -289,6 +298,9 @@ int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
                rev.ignore_merges = 1;
                rev.limited = 1;
 
+               if (suffixcmp(out->buf, "\n"))
+                       strbuf_addch(out, '\n');
+
                for (i = 0; i < origins.nr; i++)
                        shortlog(origins.items[i].string, origins.items[i].util,
                                        head, &rev, limit, out);
@@ -296,6 +308,14 @@ int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
        return 0;
 }
 
+int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
+       return do_fmt_merge_msg(1, merge_summary, in, out);
+}
+
+int fmt_merge_msg_shortlog(struct strbuf *in, struct strbuf *out) {
+       return do_fmt_merge_msg(0, 1, in, out);
+}
+
 int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
 {
        const char *inpath = NULL;
index b194ea3..d0a73da 100644 (file)
@@ -724,11 +724,15 @@ static int file_callback(const struct option *opt, const char *arg, int unset)
        if (!patterns)
                die_errno("cannot open '%s'", arg);
        while (strbuf_getline(&sb, patterns, '\n') == 0) {
+               char *s;
+               size_t len;
+
                /* ignore empty line like grep does */
                if (sb.len == 0)
                        continue;
-               append_grep_pattern(grep_opt, strbuf_detach(&sb, NULL), arg,
-                                   ++lno, GREP_PATTERN);
+
+               s = strbuf_detach(&sb, &len);
+               append_grep_pat(grep_opt, s, len, arg, ++lno, GREP_PATTERN);
        }
        fclose(patterns);
        strbuf_release(&sb);
index 3182a2b..a9836b0 100644 (file)
@@ -26,7 +26,7 @@ enum help_format {
        HELP_FORMAT_NONE,
        HELP_FORMAT_MAN,
        HELP_FORMAT_INFO,
-       HELP_FORMAT_WEB,
+       HELP_FORMAT_WEB
 };
 
 static int show_all = 0;
index 976e16f..f068583 100644 (file)
@@ -549,8 +549,9 @@ static void add_header(const char *value)
 
 #define THREAD_SHALLOW 1
 #define THREAD_DEEP 2
-static int thread = 0;
-static int do_signoff = 0;
+static int thread;
+static int do_signoff;
+static const char *signature = git_version_string;
 
 static int git_format_config(const char *var, const char *value, void *cb)
 {
@@ -609,6 +610,8 @@ static int git_format_config(const char *var, const char *value, void *cb)
                do_signoff = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "format.signature"))
+               return git_config_string(&signature, var, value);
 
        return git_log_config(var, value, cb);
 }
@@ -703,6 +706,12 @@ static void gen_message_id(struct rev_info *info, char *base)
        info->message_id = strbuf_detach(&buf, NULL);
 }
 
+static void print_signature(void)
+{
+       if (signature && *signature)
+               printf("-- \n%s\n\n", signature);
+}
+
 static void make_cover_letter(struct rev_info *rev, int use_stdout,
                              int numbered, int numbered_files,
                              struct commit *origin,
@@ -796,6 +805,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
        diff_flush(&opts);
 
        printf("\n");
+       print_signature();
 }
 
 static const char *clean_message_id(const char *msg_id)
@@ -1035,6 +1045,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                { OPTION_CALLBACK, 0, "thread", &thread, "style",
                            "enable message threading, styles: shallow, deep",
                            PARSE_OPT_OPTARG, thread_callback },
+               OPT_STRING(0, "signature", &signature, "signature",
+                           "add a signature"),
                OPT_END()
        };
 
@@ -1313,7 +1325,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                                       mime_boundary_leader,
                                       rev.mime_boundary);
                        else
-                               printf("-- \n%s\n\n", git_version_string);
+                               print_signature();
                }
                if (!use_stdout)
                        fclose(stdout);
index c0fbcdc..0804047 100644 (file)
@@ -26,8 +26,9 @@ static int show_killed;
 static int show_valid_bit;
 static int line_terminator = '\n';
 
+static const char *prefix;
+static int max_prefix_len;
 static int prefix_len;
-static int prefix_offset;
 static const char **pathspec;
 static int error_unmatch;
 static char *ps_matched;
@@ -43,10 +44,15 @@ static const char *tag_modified = "";
 static const char *tag_skip_worktree = "";
 static const char *tag_resolve_undo = "";
 
+static void write_name(const char* name, size_t len)
+{
+       write_name_quoted_relative(name, len, prefix, prefix_len, stdout,
+                       line_terminator);
+}
+
 static void show_dir_entry(const char *tag, struct dir_entry *ent)
 {
-       int len = prefix_len;
-       int offset = prefix_offset;
+       int len = max_prefix_len;
 
        if (len >= ent->len)
                die("git ls-files: internal error - directory entry not superset of prefix");
@@ -55,7 +61,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
                return;
 
        fputs(tag, stdout);
-       write_name_quoted(ent->name + offset, stdout, line_terminator);
+       write_name(ent->name, ent->len);
 }
 
 static void show_other_files(struct dir_struct *dir)
@@ -121,8 +127,7 @@ static void show_killed_files(struct dir_struct *dir)
 
 static void show_ce_entry(const char *tag, struct cache_entry *ce)
 {
-       int len = prefix_len;
-       int offset = prefix_offset;
+       int len = max_prefix_len;
 
        if (len >= ce_namelen(ce))
                die("git ls-files: internal error - cache entry not superset of prefix");
@@ -156,20 +161,19 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
                       find_unique_abbrev(ce->sha1,abbrev),
                       ce_stage(ce));
        }
-       write_name_quoted(ce->name + offset, stdout, line_terminator);
+       write_name(ce->name, ce_namelen(ce));
 }
 
 static int show_one_ru(struct string_list_item *item, void *cbdata)
 {
-       int offset = prefix_offset;
        const char *path = item->string;
        struct resolve_undo_info *ui = item->util;
        int i, len;
 
        len = strlen(path);
-       if (len < prefix_len)
+       if (len < max_prefix_len)
                return 0; /* outside of the prefix */
-       if (!match_pathspec(pathspec, path, len, prefix_len, ps_matched))
+       if (!match_pathspec(pathspec, path, len, max_prefix_len, ps_matched))
                return 0; /* uninterested */
        for (i = 0; i < 3; i++) {
                if (!ui->mode[i])
@@ -177,19 +181,19 @@ static int show_one_ru(struct string_list_item *item, void *cbdata)
                printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
                       find_unique_abbrev(ui->sha1[i], abbrev),
                       i + 1);
-               write_name_quoted(path + offset, stdout, line_terminator);
+               write_name(path, len);
        }
        return 0;
 }
 
-static void show_ru_info(const char *prefix)
+static void show_ru_info(void)
 {
        if (!the_index.resolve_undo)
                return;
        for_each_string_list(show_one_ru, the_index.resolve_undo, NULL);
 }
 
-static void show_files(struct dir_struct *dir, const char *prefix)
+static void show_files(struct dir_struct *dir)
 {
        int i;
 
@@ -243,7 +247,7 @@ static void show_files(struct dir_struct *dir, const char *prefix)
  */
 static void prune_cache(const char *prefix)
 {
-       int pos = cache_name_pos(prefix, prefix_len);
+       int pos = cache_name_pos(prefix, max_prefix_len);
        unsigned int first, last;
 
        if (pos < 0)
@@ -256,7 +260,7 @@ static void prune_cache(const char *prefix)
        while (last > first) {
                int next = (last + first) >> 1;
                struct cache_entry *ce = active_cache[next];
-               if (!strncmp(ce->name, prefix, prefix_len)) {
+               if (!strncmp(ce->name, prefix, max_prefix_len)) {
                        first = next+1;
                        continue;
                }
@@ -265,11 +269,16 @@ static void prune_cache(const char *prefix)
        active_nr = last;
 }
 
-static const char *verify_pathspec(const char *prefix)
+static const char *pathspec_prefix(const char *prefix)
 {
        const char **p, *n, *prev;
        unsigned long max;
 
+       if (!pathspec) {
+               max_prefix_len = prefix ? strlen(prefix) : 0;
+               return prefix;
+       }
+
        prev = NULL;
        max = PATH_MAX;
        for (p = pathspec; (n = *p) != NULL; p++) {
@@ -291,10 +300,7 @@ static const char *verify_pathspec(const char *prefix)
                }
        }
 
-       if (prefix_offset > max || memcmp(prev, prefix, prefix_offset))
-               die("git ls-files: cannot generate relative filenames containing '..'");
-
-       prefix_len = max;
+       max_prefix_len = max;
        return max ? xmemdupz(prev, max) : NULL;
 }
 
@@ -374,7 +380,7 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix)
        }
 }
 
-int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset)
+int report_path_error(const char *ps_matched, const char **pathspec, int prefix_len)
 {
        /*
         * Make sure all pathspec matched; otherwise it is an error.
@@ -404,7 +410,7 @@ int report_path_error(const char *ps_matched, const char **pathspec, int prefix_
                        continue;
 
                error("pathspec '%s' did not match any file(s) known to git.",
-                     pathspec[num] + prefix_offset);
+                     pathspec[num] + prefix_len);
                errors++;
        }
        return errors;
@@ -456,9 +462,10 @@ static int option_parse_exclude_standard(const struct option *opt,
        return 0;
 }
 
-int cmd_ls_files(int argc, const char **argv, const char *prefix)
+int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
 {
        int require_work_tree = 0, show_tag = 0;
+       const char *max_prefix;
        struct dir_struct dir;
        struct option builtin_ls_files_options[] = {
                { OPTION_CALLBACK, 'z', NULL, NULL, NULL,
@@ -504,7 +511,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
                { OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL,
                        "add the standard git exclusions",
                        PARSE_OPT_NOARG, option_parse_exclude_standard },
-               { OPTION_SET_INT, 0, "full-name", &prefix_offset, NULL,
+               { OPTION_SET_INT, 0, "full-name", &prefix_len, NULL,
                        "make the output relative to the project top directory",
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL },
                OPT_BOOLEAN(0, "error-unmatch", &error_unmatch,
@@ -516,8 +523,9 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
        };
 
        memset(&dir, 0, sizeof(dir));
+       prefix = cmd_prefix;
        if (prefix)
-               prefix_offset = strlen(prefix);
+               prefix_len = strlen(prefix);
        git_config(git_default_config, NULL);
 
        if (read_cache() < 0)
@@ -555,9 +563,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
        if (pathspec)
                strip_trailing_slash_from_submodules();
 
-       /* Verify that the pathspec matches the prefix */
-       if (pathspec)
-               prefix = verify_pathspec(prefix);
+       /* Find common prefix for all pathspec's */
+       max_prefix = pathspec_prefix(prefix);
 
        /* Treat unmatching pathspec elements as errors */
        if (pathspec && error_unmatch) {
@@ -575,8 +582,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
              show_killed | show_modified | show_resolve_undo))
                show_cached = 1;
 
-       if (prefix)
-               prune_cache(prefix);
+       if (max_prefix)
+               prune_cache(max_prefix);
        if (with_tree) {
                /*
                 * Basic sanity check; show-stages and show-unmerged
@@ -584,15 +591,15 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
                 */
                if (show_stage || show_unmerged)
                        die("ls-files --with-tree is incompatible with -s or -u");
-               overlay_tree_on_cache(with_tree, prefix);
+               overlay_tree_on_cache(with_tree, max_prefix);
        }
-       show_files(&dir, prefix);
+       show_files(&dir);
        if (show_resolve_undo)
-               show_ru_info(prefix);
+               show_ru_info();
 
        if (ps_matched) {
                int bad;
-               bad = report_path_error(ps_matched, pathspec, prefix_offset);
+               bad = report_path_error(ps_matched, pathspec, prefix_len);
                if (bad)
                        fprintf(stderr, "Did you forget to 'git add'?\n");
 
index 8ee91eb..34480cf 100644 (file)
@@ -5,7 +5,7 @@
 
 static const char ls_remote_usage[] =
 "git ls-remote [--heads] [--tags]  [-u <exec> | --upload-pack <exec>]\n"
-"                     [<repository> [<refs>...]]";
+"                     [-q|--quiet] [<repository> [<refs>...]]";
 
 /*
  * Is there one among the list of patterns that match the tail part
@@ -34,6 +34,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
        const char *dest = NULL;
        int nongit;
        unsigned flags = 0;
+       int quiet = 0;
        const char *uploadpack = NULL;
        const char **pattern = NULL;
 
@@ -67,6 +68,10 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
                                flags |= REF_NORMAL;
                                continue;
                        }
+                       if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) {
+                               quiet = 1;
+                               continue;
+                       }
                        usage(ls_remote_usage);
                }
                dest = arg;
@@ -99,6 +104,9 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
        ref = transport_get_remote_refs(transport);
        if (transport_disconnect(transport))
                return 1;
+
+       if (!dest && !quiet)
+               fprintf(stderr, "From %s\n", *remote->url);
        for ( ; ref; ref = ref->next) {
                if (!check_ref_type(ref, flags))
                        continue;
index 4a9729b..2320d98 100644 (file)
@@ -17,10 +17,10 @@ static struct strbuf name = STRBUF_INIT;
 static struct strbuf email = STRBUF_INIT;
 
 static enum  {
-       TE_DONTCARE, TE_QP, TE_BASE64,
+       TE_DONTCARE, TE_QP, TE_BASE64
 } transfer_encoding;
 static enum  {
-       TYPE_TEXT, TYPE_OTHER,
+       TYPE_TEXT, TYPE_OTHER
 } message_type;
 
 static struct strbuf charset = STRBUF_INIT;
index 610849a..b8e9e5b 100644 (file)
@@ -25,7 +25,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
        const char *names[3] = { NULL, NULL, NULL };
        mmfile_t mmfs[3];
        mmbuffer_t result = {NULL, 0};
-       xmparam_t xmp = {{XDF_NEED_MINIMAL}};
+       xmparam_t xmp = {{0}};
        int ret = 0, i = 0, to_stdout = 0;
        int quiet = 0;
        int nongit;
index a4a4f2c..fc00d79 100644 (file)
@@ -106,7 +106,7 @@ static void show_diff(struct merge_list *entry)
        xdemitconf_t xecfg;
        xdemitcb_t ecb;
 
-       xpp.flags = XDF_NEED_MINIMAL;
+       xpp.flags = 0;
        memset(&xecfg, 0, sizeof(xecfg));
        xecfg.ctxlen = 3;
        ecb.outf = show_outf;
index 37d414b..37ce4f5 100644 (file)
@@ -982,7 +982,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                reset_hard(remote_head->sha1, 0);
                return 0;
        } else {
-               struct strbuf msg = STRBUF_INIT;
+               struct strbuf merge_names = STRBUF_INIT;
 
                /* We are invoked directly as the first-class UI. */
                head_arg = "HEAD";
@@ -995,13 +995,17 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                 * codepath so we discard the error in this
                 * loop.
                 */
-               if (!have_message) {
-                       for (i = 0; i < argc; i++)
-                               merge_name(argv[i], &msg);
-                       fmt_merge_msg(option_log, &msg, &merge_msg);
-                       if (merge_msg.len)
-                               strbuf_setlen(&merge_msg, merge_msg.len-1);
-               }
+               for (i = 0; i < argc; i++)
+                       merge_name(argv[i], &merge_names);
+
+               if (have_message && option_log)
+                       fmt_merge_msg_shortlog(&merge_names, &merge_msg);
+               else if (!have_message)
+                       fmt_merge_msg(option_log, &merge_names, &merge_msg);
+
+
+               if (!(have_message && !option_log) && merge_msg.len)
+                       strbuf_setlen(&merge_msg, merge_msg.len-1);
        }
 
        if (head_invalid || !argc)
index 52b72fc..190005f 100644 (file)
@@ -26,7 +26,7 @@ static const char * const git_notes_usage[] = {
        "git notes [--ref <notes_ref>] edit [<object>]",
        "git notes [--ref <notes_ref>] show [<object>]",
        "git notes [--ref <notes_ref>] remove [<object>]",
-       "git notes [--ref <notes_ref>] prune",
+       "git notes [--ref <notes_ref>] prune [-n | -v]",
        NULL
 };
 
@@ -67,7 +67,7 @@ static const char * const git_notes_remove_usage[] = {
 };
 
 static const char * const git_notes_prune_usage[] = {
-       "git notes prune",
+       "git notes prune [<options>]",
        NULL
 };
 
@@ -313,7 +313,7 @@ int commit_notes(struct notes_tree *t, const char *msg)
        return 0;
 }
 
-combine_notes_fn *parse_combine_notes_fn(const char *v)
+combine_notes_fn parse_combine_notes_fn(const char *v)
 {
        if (!strcasecmp(v, "overwrite"))
                return combine_notes_overwrite;
@@ -416,7 +416,7 @@ int notes_copy_from_stdin(int force, const char *rewrite_cmd)
 {
        struct strbuf buf = STRBUF_INIT;
        struct notes_rewrite_cfg *c = NULL;
-       struct notes_tree *t;
+       struct notes_tree *t = NULL;
        int ret = 0;
 
        if (rewrite_cmd) {
@@ -614,6 +614,10 @@ static int copy(int argc, const char **argv, const char *prefix)
                }
        }
 
+       if (argc < 2) {
+               error("too few parameters");
+               usage_with_options(git_notes_copy_usage, options);
+       }
        if (2 < argc) {
                error("too many parameters");
                usage_with_options(git_notes_copy_usage, options);
@@ -792,7 +796,10 @@ static int remove_cmd(int argc, const char **argv, const char *prefix)
 static int prune(int argc, const char **argv, const char *prefix)
 {
        struct notes_tree *t;
+       int show_only = 0, verbose = 0;
        struct option options[] = {
+               OPT_BOOLEAN('n', NULL, &show_only, "do not remove, show only"),
+               OPT_BOOLEAN('v', NULL, &verbose, "report pruned notes"),
                OPT_END()
        };
 
@@ -806,8 +813,10 @@ static int prune(int argc, const char **argv, const char *prefix)
 
        t = init_notes_check("prune");
 
-       prune_notes(t);
-       commit_notes(t, "Notes removed by 'git notes prune'");
+       prune_notes(t, (verbose ? NOTES_PRUNE_VERBOSE : 0) |
+               (show_only ? NOTES_PRUNE_VERBOSE|NOTES_PRUNE_DRYRUN : 0) );
+       if (!show_only)
+               commit_notes(t, "Notes removed by 'git notes prune'");
        free_notes(t);
        return 0;
 }
index 214d7ef..0e81673 100644 (file)
@@ -1529,6 +1529,8 @@ static void try_to_free_from_threads(size_t size)
        read_unlock();
 }
 
+try_to_free_t old_try_to_free_routine;
+
 /*
  * The main thread waits on the condition that (at least) one of the workers
  * has stopped working (which is indicated in the .working member of
@@ -1563,12 +1565,12 @@ static void init_threaded_search(void)
        pthread_mutex_init(&cache_mutex, NULL);
        pthread_mutex_init(&progress_mutex, NULL);
        pthread_cond_init(&progress_cond, NULL);
-       set_try_to_free_routine(try_to_free_from_threads);
+       old_try_to_free_routine = set_try_to_free_routine(try_to_free_from_threads);
 }
 
 static void cleanup_threaded_search(void)
 {
-       set_try_to_free_routine(NULL);
+       set_try_to_free_routine(old_try_to_free_routine);
        pthread_cond_destroy(&progress_cond);
        pthread_mutex_destroy(&read_mutex);
        pthread_mutex_destroy(&cache_mutex);
index bb34757..29bc8d5 100644 (file)
@@ -17,7 +17,7 @@ enum deny_action {
        DENY_UNCONFIGURED,
        DENY_IGNORE,
        DENY_WARN,
-       DENY_REFUSE,
+       DENY_REFUSE
 };
 
 static int deny_deletes;
@@ -515,9 +515,9 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
        dst_cmd->skip_update = 1;
 
        strcpy(cmd_oldh, find_unique_abbrev(cmd->old_sha1, DEFAULT_ABBREV));
-       strcat(cmd_newh, find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV));
+       strcpy(cmd_newh, find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV));
        strcpy(dst_oldh, find_unique_abbrev(dst_cmd->old_sha1, DEFAULT_ABBREV));
-       strcat(dst_newh, find_unique_abbrev(dst_cmd->new_sha1, DEFAULT_ABBREV));
+       strcpy(dst_newh, find_unique_abbrev(dst_cmd->new_sha1, DEFAULT_ABBREV));
        rp_error("refusing inconsistent update between symref '%s' (%s..%s) and"
                 " its target '%s' (%s..%s)",
                 cmd->ref_name, cmd_oldh, cmd_newh,
index 0e99a99..0a52667 100644 (file)
@@ -16,6 +16,7 @@ static const char * const builtin_remote_usage[] = {
        "git remote [-v | --verbose] show [-n] <name>",
        "git remote prune [-n | --dry-run] <name>",
        "git remote [-v | --verbose] update [-p | --prune] [group | remote]",
+       "git remote set-branches <name> [--add] <branch>...",
        "git remote set-url <name> <newurl> [<oldurl>]",
        "git remote set-url --add <name> <newurl>",
        "git remote set-url --delete <name> <url>",
@@ -42,6 +43,12 @@ static const char * const builtin_remote_sethead_usage[] = {
        NULL
 };
 
+static const char * const builtin_remote_setbranches_usage[] = {
+       "git remote set-branches <name> <branch>...",
+       "git remote set-branches --add <name> <branch>...",
+       NULL
+};
+
 static const char * const builtin_remote_show_usage[] = {
        "git remote show [<options>] <name>",
        NULL
@@ -110,6 +117,20 @@ enum {
        TAGS_SET = 2
 };
 
+static int add_branch(const char *key, const char *branchname,
+               const char *remotename, int mirror, struct strbuf *tmp)
+{
+       strbuf_reset(tmp);
+       strbuf_addch(tmp, '+');
+       if (mirror)
+               strbuf_addf(tmp, "refs/%s:refs/%s",
+                               branchname, branchname);
+       else
+               strbuf_addf(tmp, "refs/heads/%s:refs/remotes/%s/%s",
+                               branchname, remotename, branchname);
+       return git_config_set_multivar(key, tmp->buf, "^$", 0);
+}
+
 static int add(int argc, const char **argv)
 {
        int fetch = 0, mirror = 0, fetch_tags = TAGS_DEFAULT;
@@ -162,17 +183,8 @@ static int add(int argc, const char **argv)
        if (track.nr == 0)
                string_list_append("*", &track);
        for (i = 0; i < track.nr; i++) {
-               struct string_list_item *item = track.items + i;
-
-               strbuf_reset(&buf2);
-               strbuf_addch(&buf2, '+');
-               if (mirror)
-                       strbuf_addf(&buf2, "refs/%s:refs/%s",
-                                       item->string, item->string);
-               else
-                       strbuf_addf(&buf2, "refs/heads/%s:refs/remotes/%s/%s",
-                                       item->string, name, item->string);
-               if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
+               if (add_branch(buf.buf, track.items[i].string,
+                               name, mirror, &buf2))
                        return 1;
        }
 
@@ -336,7 +348,7 @@ struct push_info {
                PUSH_STATUS_UPTODATE,
                PUSH_STATUS_FASTFORWARD,
                PUSH_STATUS_OUTOFDATE,
-               PUSH_STATUS_NOTQUERIED,
+               PUSH_STATUS_NOTQUERIED
        } status;
 };
 
@@ -724,11 +736,14 @@ static int rm(int argc, const char **argv)
        struct known_remotes known_remotes = { NULL, NULL };
        struct string_list branches = { NULL, 0, 0, 1 };
        struct string_list skipped = { NULL, 0, 0, 1 };
-       struct branches_for_remote cb_data = {
-               NULL, &branches, &skipped, &known_remotes
-       };
+       struct branches_for_remote cb_data;
        int i, result;
 
+       memset(&cb_data, 0, sizeof(cb_data));
+       cb_data.branches = &branches;
+       cb_data.skipped = &skipped;
+       cb_data.keep = &known_remotes;
+
        if (argc != 2)
                usage_with_options(builtin_remote_rm_usage, options);
 
@@ -1284,6 +1299,72 @@ static int update(int argc, const char **argv)
        return run_command_v_opt(fetch_argv, RUN_GIT_CMD);
 }
 
+static int remove_all_fetch_refspecs(const char *remote, const char *key)
+{
+       return git_config_set_multivar(key, NULL, NULL, 1);
+}
+
+static int add_branches(struct remote *remote, const char **branches,
+                       const char *key)
+{
+       const char *remotename = remote->name;
+       int mirror = remote->mirror;
+       struct strbuf refspec = STRBUF_INIT;
+
+       for (; *branches; branches++)
+               if (add_branch(key, *branches, remotename, mirror, &refspec)) {
+                       strbuf_release(&refspec);
+                       return 1;
+               }
+
+       strbuf_release(&refspec);
+       return 0;
+}
+
+static int set_remote_branches(const char *remotename, const char **branches,
+                               int add_mode)
+{
+       struct strbuf key = STRBUF_INIT;
+       struct remote *remote;
+
+       strbuf_addf(&key, "remote.%s.fetch", remotename);
+
+       if (!remote_is_configured(remotename))
+               die("No such remote '%s'", remotename);
+       remote = remote_get(remotename);
+
+       if (!add_mode && remove_all_fetch_refspecs(remotename, key.buf)) {
+               strbuf_release(&key);
+               return 1;
+       }
+       if (add_branches(remote, branches, key.buf)) {
+               strbuf_release(&key);
+               return 1;
+       }
+
+       strbuf_release(&key);
+       return 0;
+}
+
+static int set_branches(int argc, const char **argv)
+{
+       int add_mode = 0;
+       struct option options[] = {
+               OPT_BOOLEAN('\0', "add", &add_mode, "add branch"),
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, NULL, options,
+                            builtin_remote_setbranches_usage, 0);
+       if (argc == 0) {
+               error("no remote specified");
+               usage_with_options(builtin_remote_seturl_usage, options);
+       }
+       argv[argc] = NULL;
+
+       return set_remote_branches(argv[0], argv + 1, add_mode);
+}
+
 static int set_url(int argc, const char **argv)
 {
        int i, push_mode = 0, add_mode = 0, delete_mode = 0;
@@ -1449,6 +1530,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix)
                result = rm(argc, argv);
        else if (!strcmp(argv[0], "set-head"))
                result = set_head(argc, argv);
+       else if (!strcmp(argv[0], "set-branches"))
+               result = set_branches(argc, argv);
        else if (!strcmp(argv[0], "set-url"))
                result = set_url(argc, argv);
        else if (!strcmp(argv[0], "show"))
index 34f9ace..0048f9e 100644 (file)
@@ -89,7 +89,7 @@ static int diff_two(const char *file1, const char *label1,
        printf("--- a/%s\n+++ b/%s\n", label1, label2);
        fflush(stdout);
        memset(&xpp, 0, sizeof(xpp));
-       xpp.flags = XDF_NEED_MINIMAL;
+       xpp.flags = 0;
        memset(&xecfg, 0, sizeof(xecfg));
        xecfg.ctxlen = 3;
        ecb.outf = outf;
index 8fbf9d0..b676e29 100644 (file)
@@ -408,7 +408,8 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
        memset(opts + onb, 0, sizeof(opts[onb]));
        argc = parse_options(argc, argv, prefix, opts, usage,
                        keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0 |
-                       stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0);
+                       stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0 |
+                       PARSE_OPT_SHELL_EVAL);
 
        strbuf_addf(&parsed, " --");
        sq_quote_argv(&parsed, argv, 0);
index 7976b5a..853e9e4 100644 (file)
@@ -39,7 +39,8 @@ static const char * const cherry_pick_usage[] = {
 static int edit, no_replay, no_commit, mainline, signoff, allow_ff;
 static enum { REVERT, CHERRY_PICK } action;
 static struct commit *commit;
-static const char *commit_name;
+static int commit_argc;
+static const char **commit_argv;
 static int allow_rerere_auto;
 
 static const char *me;
@@ -53,12 +54,10 @@ static void parse_args(int argc, const char **argv)
 {
        const char * const * usage_str =
                action == REVERT ?  revert_usage : cherry_pick_usage;
-       unsigned char sha1[20];
        int noop;
        struct option options[] = {
                OPT_BOOLEAN('n', "no-commit", &no_commit, "don't automatically commit"),
                OPT_BOOLEAN('e', "edit", &edit, "edit the commit message"),
-               OPT_BOOLEAN('x', NULL, &no_replay, "append commit name when cherry-picking"),
                OPT_BOOLEAN('r', NULL, &noop, "no-op (backward compatibility)"),
                OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
                OPT_INTEGER('m', "mainline", &mainline, "parent number"),
@@ -71,6 +70,7 @@ static void parse_args(int argc, const char **argv)
 
        if (action == CHERRY_PICK) {
                struct option cp_extra[] = {
+                       OPT_BOOLEAN('x', NULL, &no_replay, "append commit name"),
                        OPT_BOOLEAN(0, "ff", &allow_ff, "allow fast-forward"),
                        OPT_END(),
                };
@@ -78,15 +78,11 @@ static void parse_args(int argc, const char **argv)
                        die("program error");
        }
 
-       if (parse_options(argc, argv, NULL, options, usage_str, 0) != 1)
+       commit_argc = parse_options(argc, argv, NULL, options, usage_str, 0);
+       if (commit_argc < 1)
                usage_with_options(usage_str, options);
 
-       commit_name = argv[0];
-       if (get_sha1(commit_name, sha1))
-               die ("Cannot find '%s'", commit_name);
-       commit = lookup_commit_reference(sha1);
-       if (!commit)
-               exit(1);
+       commit_argv = argv;
 }
 
 struct commit_message {
@@ -239,7 +235,7 @@ static void set_author_ident_env(const char *message)
                        sha1_to_hex(commit->object.sha1));
 }
 
-static char *help_msg(const char *name)
+static char *help_msg(void)
 {
        struct strbuf helpbuf = STRBUF_INIT;
        char *msg = getenv("GIT_CHERRY_PICK_HELP");
@@ -255,7 +251,7 @@ static char *help_msg(const char *name)
                strbuf_addf(&helpbuf, " with: \n"
                        "\n"
                        "        git commit -c %s\n",
-                       name);
+                           sha1_to_hex(commit->object.sha1));
        }
        else
                strbuf_addch(&helpbuf, '.');
@@ -357,7 +353,7 @@ static void do_recursive_merge(struct commit *base, struct commit *next,
                }
                write_message(msgbuf, defmsg);
                fprintf(stderr, "Automatic %s failed.%s\n",
-                       me, help_msg(commit_name));
+                       me, help_msg());
                rerere(allow_rerere_auto);
                exit(1);
        }
@@ -365,7 +361,7 @@ static void do_recursive_merge(struct commit *base, struct commit *next,
        fprintf(stderr, "Finished one %s.\n", me);
 }
 
-static int revert_or_cherry_pick(int argc, const char **argv)
+static int do_pick_commit(void)
 {
        unsigned char head[20];
        struct commit *base, *next, *parent;
@@ -374,28 +370,6 @@ static int revert_or_cherry_pick(int argc, const char **argv)
        char *defmsg = NULL;
        struct strbuf msgbuf = STRBUF_INIT;
 
-       git_config(git_default_config, NULL);
-       me = action == REVERT ? "revert" : "cherry-pick";
-       setenv(GIT_REFLOG_ACTION, me, 0);
-       parse_args(argc, argv);
-
-       /* this is copied from the shell script, but it's never triggered... */
-       if (action == REVERT && !no_replay)
-               die("revert is incompatible with replay");
-
-       if (allow_ff) {
-               if (signoff)
-                       die("cherry-pick --ff cannot be used with --signoff");
-               if (no_commit)
-                       die("cherry-pick --ff cannot be used with --no-commit");
-               if (no_replay)
-                       die("cherry-pick --ff cannot be used with -x");
-               if (edit)
-                       die("cherry-pick --ff cannot be used with --edit");
-       }
-
-       if (read_cache() < 0)
-               die("git %s: failed to read the index", me);
        if (no_commit) {
                /*
                 * We do not intend to commit immediately.  We just want to
@@ -506,12 +480,14 @@ static int revert_or_cherry_pick(int argc, const char **argv)
                free_commit_list(remotes);
                if (res) {
                        fprintf(stderr, "Automatic %s with strategy %s failed.%s\n",
-                               me, strategy, help_msg(commit_name));
+                               me, strategy, help_msg());
                        rerere(allow_rerere_auto);
                        exit(1);
                }
        }
 
+       free_message(&msg);
+
        /*
         *
         * If we are cherry-pick, and if the merge did not result in
@@ -524,7 +500,9 @@ static int revert_or_cherry_pick(int argc, const char **argv)
        if (!no_commit) {
                /* 6 is max possible length of our args array including NULL */
                const char *args[6];
+               int res;
                int i = 0;
+
                args[i++] = "commit";
                args[i++] = "-n";
                if (signoff)
@@ -534,26 +512,86 @@ static int revert_or_cherry_pick(int argc, const char **argv)
                        args[i++] = defmsg;
                }
                args[i] = NULL;
-               return execv_git_cmd(args);
+               res = run_command_v_opt(args, RUN_GIT_CMD);
+               free(defmsg);
+
+               return res;
        }
-       free_message(&msg);
+
        free(defmsg);
 
        return 0;
 }
 
+static void prepare_revs(struct rev_info *revs)
+{
+       int argc = 0;
+       int i;
+       const char **argv = xmalloc((commit_argc + 4) * sizeof(*argv));
+
+       argv[argc++] = NULL;
+       argv[argc++] = "--no-walk";
+       if (action != REVERT)
+               argv[argc++] = "--reverse";
+       for (i = 0; i < commit_argc; i++)
+               argv[argc++] = commit_argv[i];
+       argv[argc++] = NULL;
+
+       init_revisions(revs, NULL);
+       setup_revisions(argc - 1, argv, revs, NULL);
+       if (prepare_revision_walk(revs))
+               die("revision walk setup failed");
+
+       if (!revs->commits)
+               die("empty commit set passed");
+
+       free(argv);
+}
+
+static int revert_or_cherry_pick(int argc, const char **argv)
+{
+       struct rev_info revs;
+
+       git_config(git_default_config, NULL);
+       me = action == REVERT ? "revert" : "cherry-pick";
+       setenv(GIT_REFLOG_ACTION, me, 0);
+       parse_args(argc, argv);
+
+       if (allow_ff) {
+               if (signoff)
+                       die("cherry-pick --ff cannot be used with --signoff");
+               if (no_commit)
+                       die("cherry-pick --ff cannot be used with --no-commit");
+               if (no_replay)
+                       die("cherry-pick --ff cannot be used with -x");
+               if (edit)
+                       die("cherry-pick --ff cannot be used with --edit");
+       }
+
+       if (read_cache() < 0)
+               die("git %s: failed to read the index", me);
+
+       prepare_revs(&revs);
+
+       while ((commit = get_revision(&revs))) {
+               int res = do_pick_commit();
+               if (res)
+                       return res;
+       }
+
+       return 0;
+}
+
 int cmd_revert(int argc, const char **argv, const char *prefix)
 {
        if (isatty(0))
                edit = 1;
-       no_replay = 1;
        action = REVERT;
        return revert_or_cherry_pick(argc, argv);
 }
 
 int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
 {
-       no_replay = 0;
        action = CHERRY_PICK;
        return revert_or_cherry_pick(argc, argv);
 }
diff --git a/cache.h b/cache.h
index c966023..c9fa3df 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -361,7 +361,7 @@ enum object_type {
        OBJ_OFS_DELTA = 6,
        OBJ_REF_DELTA = 7,
        OBJ_ANY,
-       OBJ_MAX,
+       OBJ_MAX
 };
 
 static inline enum object_type object_type(unsigned int mode)
@@ -547,7 +547,6 @@ extern int core_compression_seen;
 extern size_t packed_git_window_size;
 extern size_t packed_git_limit;
 extern size_t delta_base_cache_limit;
-extern int auto_crlf;
 extern int read_replace_refs;
 extern int fsync_object_files;
 extern int core_preload_index;
@@ -556,32 +555,53 @@ extern int core_apply_sparse_checkout;
 enum safe_crlf {
        SAFE_CRLF_FALSE = 0,
        SAFE_CRLF_FAIL = 1,
-       SAFE_CRLF_WARN = 2,
+       SAFE_CRLF_WARN = 2
 };
 
 extern enum safe_crlf safe_crlf;
 
+enum auto_crlf {
+       AUTO_CRLF_FALSE = 0,
+       AUTO_CRLF_TRUE = 1,
+       AUTO_CRLF_INPUT = -1,
+};
+
+extern enum auto_crlf auto_crlf;
+
+enum eol {
+       EOL_UNSET,
+       EOL_CRLF,
+       EOL_LF,
+#ifdef NATIVE_CRLF
+       EOL_NATIVE = EOL_CRLF
+#else
+       EOL_NATIVE = EOL_LF
+#endif
+};
+
+extern enum eol eol;
+
 enum branch_track {
        BRANCH_TRACK_UNSPECIFIED = -1,
        BRANCH_TRACK_NEVER = 0,
        BRANCH_TRACK_REMOTE,
        BRANCH_TRACK_ALWAYS,
        BRANCH_TRACK_EXPLICIT,
-       BRANCH_TRACK_OVERRIDE,
+       BRANCH_TRACK_OVERRIDE
 };
 
 enum rebase_setup_type {
        AUTOREBASE_NEVER = 0,
        AUTOREBASE_LOCAL,
        AUTOREBASE_REMOTE,
-       AUTOREBASE_ALWAYS,
+       AUTOREBASE_ALWAYS
 };
 
 enum push_default_type {
        PUSH_DEFAULT_NOTHING = 0,
        PUSH_DEFAULT_MATCHING,
        PUSH_DEFAULT_TRACKING,
-       PUSH_DEFAULT_CURRENT,
+       PUSH_DEFAULT_CURRENT
 };
 
 extern enum branch_track git_branch_track;
@@ -590,7 +610,7 @@ extern enum push_default_type push_default;
 
 enum object_creation_mode {
        OBJECT_CREATION_USES_HARDLINKS = 0,
-       OBJECT_CREATION_USES_RENAMES = 1,
+       OBJECT_CREATION_USES_RENAMES = 1
 };
 
 extern enum object_creation_mode object_creation_mode;
@@ -670,7 +690,7 @@ enum sharedrepo {
        OLD_PERM_GROUP      = 1,
        OLD_PERM_EVERYBODY  = 2,
        PERM_GROUP          = 0660,
-       PERM_EVERYBODY      = 0664,
+       PERM_EVERYBODY      = 0664
 };
 int git_config_perm(const char *var, const char *value);
 int set_shared_perm(const char *path, int mode);
@@ -730,12 +750,23 @@ static inline unsigned int hexval(unsigned char c)
 #define MINIMUM_ABBREV 4
 #define DEFAULT_ABBREV 7
 
+struct object_context {
+       unsigned char tree[20];
+       char path[PATH_MAX];
+       unsigned mode;
+};
+
 extern int get_sha1(const char *str, unsigned char *sha1);
 extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int gently, const char *prefix);
 static inline int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode)
 {
        return get_sha1_with_mode_1(str, sha1, mode, 1, NULL);
 }
+extern int get_sha1_with_context_1(const char *name, unsigned char *sha1, struct object_context *orc, int gently, const char *prefix);
+static inline int get_sha1_with_context(const char *str, unsigned char *sha1, struct object_context *orc)
+{
+       return get_sha1_with_context_1(str, sha1, orc, 1, NULL);
+}
 extern int get_sha1_hex(const char *hex, unsigned char *sha1);
 extern char *sha1_to_hex(const unsigned char *sha1);   /* static buffer result! */
 extern int read_ref(const char *filename, unsigned char *sha1);
@@ -882,7 +913,7 @@ struct ref {
                REF_STATUS_REJECT_NODELETE,
                REF_STATUS_UPTODATE,
                REF_STATUS_REMOTE_REJECT,
-               REF_STATUS_EXPECTING_REPORT,
+               REF_STATUS_EXPECTING_REPORT
        } status;
        char *remote_status;
        struct ref *peer_ref; /* when renaming */
index 7557136..655fa89 100644 (file)
@@ -220,7 +220,7 @@ static void combine_diff(const unsigned char *parent, unsigned int mode,
        parent_file.ptr = grab_blob(parent, mode, &sz);
        parent_file.size = sz;
        memset(&xpp, 0, sizeof(xpp));
-       xpp.flags = XDF_NEED_MINIMAL;
+       xpp.flags = 0;
        memset(&xecfg, 0, sizeof(xecfg));
        memset(&state, 0, sizeof(state));
        state.nmask = nmask;
index 6ef88dc..e958a7c 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -60,7 +60,7 @@ enum cmit_fmt {
        CMIT_FMT_EMAIL,
        CMIT_FMT_USERFORMAT,
 
-       CMIT_FMT_UNSPECIFIED,
+       CMIT_FMT_UNSPECIFIED
 };
 
 struct pretty_print_context
index 0e3e743..3b2477b 100644 (file)
@@ -80,7 +80,7 @@ static inline int fork(void)
 static inline unsigned int alarm(unsigned int seconds)
 { return 0; }
 static inline int fsync(int fd)
-{ return 0; }
+{ return _commit(fd); }
 static inline int getppid(void)
 { return 1; }
 static inline void sync(void)
@@ -89,7 +89,7 @@ static inline int getuid()
 { return 1; }
 static inline struct passwd *getpwnam(const char *name)
 { return NULL; }
-static inline int fcntl(int fd, int cmd, long arg)
+static inline int fcntl(int fd, int cmd, ...)
 {
        if (cmd == F_GETFD || cmd == F_SETFD)
                return 0;
index 0f949fc..010e875 100644 (file)
@@ -16,6 +16,7 @@
 static unsigned __stdcall win32_start_routine(void *arg)
 {
        pthread_t *thread = arg;
+       thread->tid = GetCurrentThreadId();
        thread->arg = thread->start_routine(thread->arg);
        return 0;
 }
@@ -49,6 +50,13 @@ int win32_pthread_join(pthread_t *thread, void **value_ptr)
        }
 }
 
+pthread_t pthread_self(void)
+{
+       pthread_t t = { 0 };
+       t.tid = GetCurrentThreadId();
+       return t;
+}
+
 int pthread_cond_init(pthread_cond_t *cond, const void *unused)
 {
        cond->waiters = 0;
index a45f8d6..2e20548 100644 (file)
@@ -58,6 +58,7 @@ typedef struct {
        HANDLE handle;
        void *(*start_routine)(void*);
        void *arg;
+       DWORD tid;
 } pthread_t;
 
 extern int pthread_create(pthread_t *thread, const void *unused,
@@ -71,4 +72,28 @@ extern int pthread_create(pthread_t *thread, const void *unused,
 
 extern int win32_pthread_join(pthread_t *thread, void **value_ptr);
 
+#define pthread_equal(t1, t2) ((t1).tid == (t2).tid)
+extern pthread_t pthread_self(void);
+
+static inline int pthread_exit(void *ret)
+{
+       ExitThread((DWORD)ret);
+}
+
+typedef DWORD pthread_key_t;
+static inline int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *value))
+{
+       return (*keyp = TlsAlloc()) == TLS_OUT_OF_INDEXES ? EAGAIN : 0;
+}
+
+static inline int pthread_setspecific(pthread_key_t key, const void *value)
+{
+       return TlsSetValue(key, (void *)value) ? 0 : EINVAL;
+}
+
+static inline void *pthread_getspecific(pthread_key_t key)
+{
+       return TlsGetValue(key);
+}
+
 #endif /* PTHREAD_H */
index 9b6b1df..cdcf583 100644 (file)
--- a/config.c
+++ b/config.c
@@ -517,7 +517,9 @@ static int git_default_core_config(const char *var, const char *value)
 
        if (!strcmp(var, "core.autocrlf")) {
                if (value && !strcasecmp(value, "input")) {
-                       auto_crlf = -1;
+                       if (eol == EOL_CRLF)
+                               return error("core.autocrlf=input conflicts with core.eol=crlf");
+                       auto_crlf = AUTO_CRLF_INPUT;
                        return 0;
                }
                auto_crlf = git_config_bool(var, value);
@@ -533,6 +535,20 @@ static int git_default_core_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.eol")) {
+               if (value && !strcasecmp(value, "lf"))
+                       eol = EOL_LF;
+               else if (value && !strcasecmp(value, "crlf"))
+                       eol = EOL_CRLF;
+               else if (value && !strcasecmp(value, "native"))
+                       eol = EOL_NATIVE;
+               else
+                       eol = EOL_UNSET;
+               if (eol == EOL_CRLF && auto_crlf == AUTO_CRLF_INPUT)
+                       return error("core.autocrlf=input conflicts with core.eol=crlf");
+               return 0;
+       }
+
        if (!strcmp(var, "core.notesref")) {
                notes_ref_name = xstrdup(value);
                return 0;
index 0d4b64d..b4e65c3 100644 (file)
@@ -3,10 +3,12 @@
 
 CC = @CC@
 CFLAGS = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@
 LDFLAGS = @LDFLAGS@
 CC_LD_DYNPATH = @CC_LD_DYNPATH@
 AR = @AR@
 TAR = @TAR@
+DIFF = @DIFF@
 #INSTALL = @INSTALL@           # needs install-sh or install.sh in sources
 TCLTK_PATH = @TCLTK_PATH@
 
@@ -42,6 +44,7 @@ NO_D_TYPE_IN_DIRENT=@NO_D_TYPE_IN_DIRENT@
 NO_SOCKADDR_STORAGE=@NO_SOCKADDR_STORAGE@
 NO_IPV6=@NO_IPV6@
 NO_C99_FORMAT=@NO_C99_FORMAT@
+NO_HSTRERROR=@NO_HSTRERROR@
 NO_STRCASESTR=@NO_STRCASESTR@
 NO_MEMMEM=@NO_MEMMEM@
 NO_STRLCPY=@NO_STRLCPY@
@@ -51,10 +54,15 @@ NO_SETENV=@NO_SETENV@
 NO_UNSETENV=@NO_UNSETENV@
 NO_MKDTEMP=@NO_MKDTEMP@
 NO_MKSTEMPS=@NO_MKSTEMPS@
+NO_INET_NTOP=@NO_INET_NTOP@
+NO_INET_PTON=@NO_INET_PTON@
 NO_ICONV=@NO_ICONV@
 OLD_ICONV=@OLD_ICONV@
 NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@
+INLINE=@INLINE@
+SOCKLEN_T=@SOCKLEN_T@
 FREAD_READS_DIRECTORIES=@FREAD_READS_DIRECTORIES@
 SNPRINTF_RETURNS_BOGUS=@SNPRINTF_RETURNS_BOGUS@
 NO_PTHREADS=@NO_PTHREADS@
+PTHREAD_CFLAGS=@PTHREAD_CFLAGS@
 PTHREAD_LIBS=@PTHREAD_LIBS@
index 71038fc..5601e8b 100644 (file)
@@ -327,6 +327,12 @@ GIT_PARSE_WITH(tcltk))
 AC_MSG_NOTICE([CHECKS for programs])
 #
 AC_PROG_CC([cc gcc])
+AC_C_INLINE
+case $ac_cv_c_inline in
+  inline | yes | no)   ;;
+  *)                   AC_SUBST([INLINE], [$ac_cv_c_inline]) ;;
+esac
+
 # which switch to pass runtime path to dynamic libraries to the linker
 AC_CACHE_CHECK([if linker supports -R], git_cv_ld_dashr, [
    SAVE_LDFLAGS="${LDFLAGS}"
@@ -362,6 +368,7 @@ fi
 #AC_PROG_INSTALL               # needs install-sh or install.sh in sources
 AC_CHECK_TOOLS(AR, [gar ar], :)
 AC_CHECK_PROGS(TAR, [gtar tar])
+AC_CHECK_PROGS(DIFF, [gnudiff gdiff diff])
 # TCLTK_PATH will be set to some value if we want Tcl/Tk
 # or will be empty otherwise.
 if test -z "$NO_TCLTK"; then
@@ -544,13 +551,47 @@ AC_SUBST(NEEDS_SOCKET)
 test -n "$NEEDS_SOCKET" && LIBS="$LIBS -lsocket"
 
 #
-# Define NEEDS_RESOLV if linking with -lnsl and/or -lsocket is not enough.
-# Notably on Solaris hstrerror resides in libresolv and on Solaris 7
-# inet_ntop and inet_pton additionally reside there.
-AC_CHECK_LIB([c], [hstrerror],
-[NEEDS_RESOLV=],
-[NEEDS_RESOLV=YesPlease])
+# The next few tests will define NEEDS_RESOLV if linking with
+# libresolv provides some of the functions we would normally get
+# from libc.
+NEEDS_RESOLV=
 AC_SUBST(NEEDS_RESOLV)
+#
+# Define NO_INET_NTOP if linking with -lresolv is not enough.
+# Solaris 2.7 in particular hos inet_ntop in -lresolv.
+NO_INET_NTOP=
+AC_SUBST(NO_INET_NTOP)
+AC_CHECK_FUNC([inet_ntop],
+       [],
+    [AC_CHECK_LIB([resolv], [inet_ntop],
+           [NEEDS_RESOLV=YesPlease],
+       [NO_INET_NTOP=YesPlease])
+])
+#
+# Define NO_INET_PTON if linking with -lresolv is not enough.
+# Solaris 2.7 in particular hos inet_pton in -lresolv.
+NO_INET_PTON=
+AC_SUBST(NO_INET_PTON)
+AC_CHECK_FUNC([inet_pton],
+       [],
+    [AC_CHECK_LIB([resolv], [inet_pton],
+           [NEEDS_RESOLV=YesPlease],
+       [NO_INET_PTON=YesPlease])
+])
+#
+# Define NO_HSTRERROR if linking with -lresolv is not enough.
+# Solaris 2.6 in particular has no hstrerror, even in -lresolv.
+NO_HSTRERROR=
+AC_CHECK_FUNC([hstrerror],
+       [],
+    [AC_CHECK_LIB([resolv], [hstrerror],
+           [NEEDS_RESOLV=YesPlease],
+       [NO_HSTRERROR=YesPlease])
+])
+AC_SUBST(NO_HSTRERROR)
+#
+# If any of the above tests determined that -lresolv is needed at
+# build-time, also set it here for remaining configure-time checks.
 test -n "$NEEDS_RESOLV" && LIBS="$LIBS -lresolv"
 
 AC_CHECK_LIB([c], [basename],
@@ -598,6 +639,12 @@ AC_SUBST(OLD_ICONV)
 ## Checks for typedefs, structures, and compiler characteristics.
 AC_MSG_NOTICE([CHECKS for typedefs, structures, and compiler characteristics])
 #
+TYPE_SOCKLEN_T
+case $ac_cv_type_socklen_t in
+  yes) ;;
+  *)   AC_SUBST([SOCKLEN_T], [$git_cv_socklen_t_equiv]) ;;
+esac
+
 # Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent.
 AC_CHECK_MEMBER(struct dirent.d_ino,
 [NO_D_INO_IN_DIRENT=],
@@ -808,7 +855,11 @@ AC_DEFUN([PTHREADTEST_SRC], [
 int main(void)
 {
        pthread_mutex_t test_mutex;
-       return (0);
+       int retcode = 0;
+       retcode |= pthread_mutex_init(&test_mutex,(void *)0);
+       retcode |= pthread_mutex_lock(&test_mutex);
+       retcode |= pthread_mutex_unlock(&test_mutex);
+       return retcode;
 }
 ])
 
@@ -825,7 +876,8 @@ if test -n "$USER_NOPTHREAD"; then
 # handle these separately since PTHREAD_CFLAGS could be '-lpthreads
 # -D_REENTRANT' or some such.
 elif test -z "$PTHREAD_CFLAGS"; then
-  for opt in -pthread -lpthread; do
+  threads_found=no
+  for opt in -mt -pthread -lpthread; do
      old_CFLAGS="$CFLAGS"
      CFLAGS="$opt $CFLAGS"
      AC_MSG_CHECKING([Checking for POSIX Threads with '$opt'])
@@ -833,11 +885,18 @@ elif test -z "$PTHREAD_CFLAGS"; then
        [AC_MSG_RESULT([yes])
                NO_PTHREADS=
                PTHREAD_LIBS="$opt"
+               PTHREAD_CFLAGS="$opt"
+               threads_found=yes
                break
        ],
        [AC_MSG_RESULT([no])])
       CFLAGS="$old_CFLAGS"
   done
+  if test $threads_found != yes; then
+    AC_CHECK_LIB([pthread], [pthread_create],
+       [PTHREAD_LIBS="-lpthread"],
+       [NO_PTHREADS=UnfortunatelyYes])
+  fi
 else
   old_CFLAGS="$CFLAGS"
   CFLAGS="$PTHREAD_CFLAGS $CFLAGS"
@@ -854,6 +913,7 @@ fi
 
 CFLAGS="$old_CFLAGS"
 
+AC_SUBST(PTHREAD_CFLAGS)
 AC_SUBST(PTHREAD_LIBS)
 AC_SUBST(NO_PTHREADS)
 
index 9ae991a..02e738a 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -5,6 +5,7 @@
 #include "refs.h"
 #include "run-command.h"
 #include "remote.h"
+#include "url.h"
 
 static char *server_capabilities;
 
@@ -131,7 +132,7 @@ int path_match(const char *path, int nr, char **match)
 enum protocol {
        PROTO_LOCAL = 1,
        PROTO_SSH,
-       PROTO_GIT,
+       PROTO_GIT
 };
 
 static enum protocol get_protocol(const char *name)
@@ -450,7 +451,7 @@ static struct child_process no_fork;
 struct child_process *git_connect(int fd[2], const char *url_orig,
                                  const char *prog, int flags)
 {
-       char *url = xstrdup(url_orig);
+       char *url;
        char *host, *path;
        char *end;
        int c;
@@ -466,6 +467,11 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
         */
        signal(SIGCHLD, SIG_DFL);
 
+       if (is_url(url_orig))
+               url = url_decode(url_orig);
+       else
+               url = xstrdup(url_orig);
+
        host = strstr(url, "://");
        if (host) {
                *host = '\0';
index 550985d..6756990 100755 (executable)
@@ -221,8 +221,8 @@ __git_ps1 ()
 {
        local g="$(__gitdir)"
        if [ -n "$g" ]; then
-               local r
-               local b
+               local r=""
+               local b=""
                if [ -f "$g/rebase-merge/interactive" ]; then
                        r="|REBASE-i"
                        b="$(cat "$g/rebase-merge/head-name")"
@@ -264,11 +264,11 @@ __git_ps1 ()
                        }
                fi
 
-               local w
-               local i
-               local s
-               local u
-               local c
+               local w=""
+               local i=""
+               local s=""
+               local u=""
+               local c=""
                local p=""
 
                if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then
@@ -984,7 +984,7 @@ _git_checkout ()
        --*)
                __gitcomp "
                        --quiet --ours --theirs --track --no-track --merge
-                       --conflict= --patch
+                       --conflict= --orphan --patch
                        "
                ;;
        *)
@@ -1194,7 +1194,7 @@ _git_format_patch ()
                        --numbered --start-number
                        --numbered-files
                        --keep-subject
-                       --signoff
+                       --signoff --signature --no-signature
                        --in-reply-to= --cc=
                        --full-index --binary
                        --not --all
@@ -1868,6 +1868,7 @@ _git_config ()
                format.headers
                format.numbered
                format.pretty
+               format.signature
                format.signoff
                format.subjectprefix
                format.suffix
index 4f8fcb7..e41a31e 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -8,13 +8,17 @@
  * This should use the pathname to decide on whether it wants to do some
  * more interesting conversions (automatic gzip/unzip, general format
  * conversions etc etc), but by default it just does automatic CRLF<->LF
- * translation when the "auto_crlf" option is set.
+ * translation when the "text" attribute or "auto_crlf" option is set.
  */
 
-#define CRLF_GUESS     (-1)
-#define CRLF_BINARY    0
-#define CRLF_TEXT      1
-#define CRLF_INPUT     2
+enum action {
+       CRLF_GUESS = -1,
+       CRLF_BINARY = 0,
+       CRLF_TEXT,
+       CRLF_INPUT,
+       CRLF_CRLF,
+       CRLF_AUTO,
+};
 
 struct text_stat {
        /* NUL, CR, LF and CRLF counts */
@@ -89,49 +93,111 @@ static int is_binary(unsigned long size, struct text_stat *stats)
        return 0;
 }
 
-static void check_safe_crlf(const char *path, int action,
+static enum eol determine_output_conversion(enum action action) {
+       switch (action) {
+       case CRLF_BINARY:
+               return EOL_UNSET;
+       case CRLF_CRLF:
+               return EOL_CRLF;
+       case CRLF_INPUT:
+               return EOL_LF;
+       case CRLF_GUESS:
+               if (!auto_crlf)
+                       return EOL_UNSET;
+               /* fall through */
+       case CRLF_TEXT:
+       case CRLF_AUTO:
+               if (auto_crlf == AUTO_CRLF_TRUE)
+                       return EOL_CRLF;
+               else if (auto_crlf == AUTO_CRLF_INPUT)
+                       return EOL_LF;
+               else if (eol == EOL_UNSET)
+                       return EOL_NATIVE;
+       }
+       return eol;
+}
+
+static void check_safe_crlf(const char *path, enum action action,
                             struct text_stat *stats, enum safe_crlf checksafe)
 {
        if (!checksafe)
                return;
 
-       if (action == CRLF_INPUT || auto_crlf <= 0) {
+       if (determine_output_conversion(action) == EOL_LF) {
                /*
                 * CRLFs would not be restored by checkout:
                 * check if we'd remove CRLFs
                 */
                if (stats->crlf) {
                        if (checksafe == SAFE_CRLF_WARN)
-                               warning("CRLF will be replaced by LF in %s.", path);
+                               warning("CRLF will be replaced by LF in %s.\nThe file will have its original line endings in your working directory.", path);
                        else /* i.e. SAFE_CRLF_FAIL */
                                die("CRLF would be replaced by LF in %s.", path);
                }
-       } else if (auto_crlf > 0) {
+       } else if (determine_output_conversion(action) == EOL_CRLF) {
                /*
                 * CRLFs would be added by checkout:
                 * check if we have "naked" LFs
                 */
                if (stats->lf != stats->crlf) {
                        if (checksafe == SAFE_CRLF_WARN)
-                               warning("LF will be replaced by CRLF in %s", path);
+                               warning("LF will be replaced by CRLF in %s.\nThe file will have its original line endings in your working directory.", path);
                        else /* i.e. SAFE_CRLF_FAIL */
                                die("LF would be replaced by CRLF in %s", path);
                }
        }
 }
 
+static int has_cr_in_index(const char *path)
+{
+       int pos, len;
+       unsigned long sz;
+       enum object_type type;
+       void *data;
+       int has_cr;
+       struct index_state *istate = &the_index;
+
+       len = strlen(path);
+       pos = index_name_pos(istate, path, len);
+       if (pos < 0) {
+               /*
+                * We might be in the middle of a merge, in which
+                * case we would read stage #2 (ours).
+                */
+               int i;
+               for (i = -pos - 1;
+                    (pos < 0 && i < istate->cache_nr &&
+                     !strcmp(istate->cache[i]->name, path));
+                    i++)
+                       if (ce_stage(istate->cache[i]) == 2)
+                               pos = i;
+       }
+       if (pos < 0)
+               return 0;
+       data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz);
+       if (!data || type != OBJ_BLOB) {
+               free(data);
+               return 0;
+       }
+
+       has_cr = memchr(data, '\r', sz) != NULL;
+       free(data);
+       return has_cr;
+}
+
 static int crlf_to_git(const char *path, const char *src, size_t len,
-                       struct strbuf *buf, int action, enum safe_crlf checksafe)
+                      struct strbuf *buf, enum action action, enum safe_crlf checksafe)
 {
        struct text_stat stats;
        char *dst;
 
-       if ((action == CRLF_BINARY) || !auto_crlf || !len)
+       if (action == CRLF_BINARY ||
+           (action == CRLF_GUESS && auto_crlf == AUTO_CRLF_FALSE) || !len)
                return 0;
 
        gather_stats(src, len, &stats);
 
-       if (action == CRLF_GUESS) {
+       if (action == CRLF_AUTO || action == CRLF_GUESS) {
                /*
                 * We're currently not going to even try to convert stuff
                 * that has bare CR characters. Does anybody do that crazy
@@ -145,6 +211,15 @@ static int crlf_to_git(const char *path, const char *src, size_t len,
                 */
                if (is_binary(len, &stats))
                        return 0;
+
+               if (action == CRLF_GUESS) {
+                       /*
+                        * If the file in the index has any CR in it, do not convert.
+                        * This is the new safer autocrlf handling.
+                        */
+                       if (has_cr_in_index(path))
+                               return 0;
+               }
        }
 
        check_safe_crlf(path, action, &stats, checksafe);
@@ -157,7 +232,7 @@ static int crlf_to_git(const char *path, const char *src, size_t len,
        if (strbuf_avail(buf) + buf->len < len)
                strbuf_grow(buf, len - buf->len);
        dst = buf->buf;
-       if (action == CRLF_GUESS) {
+       if (action == CRLF_AUTO || action == CRLF_GUESS) {
                /*
                 * If we guessed, we already know we rejected a file with
                 * lone CR, and we can strip a CR without looking at what
@@ -180,16 +255,12 @@ static int crlf_to_git(const char *path, const char *src, size_t len,
 }
 
 static int crlf_to_worktree(const char *path, const char *src, size_t len,
-                            struct strbuf *buf, int action)
+                           struct strbuf *buf, enum action action)
 {
        char *to_free = NULL;
        struct text_stat stats;
 
-       if ((action == CRLF_BINARY) || (action == CRLF_INPUT) ||
-           auto_crlf <= 0)
-               return 0;
-
-       if (!len)
+       if (!len || determine_output_conversion(action) != EOL_CRLF)
                return 0;
 
        gather_stats(src, len, &stats);
@@ -202,7 +273,14 @@ static int crlf_to_worktree(const char *path, const char *src, size_t len,
        if (stats.lf == stats.crlf)
                return 0;
 
-       if (action == CRLF_GUESS) {
+       if (action == CRLF_AUTO || action == CRLF_GUESS) {
+               if (action == CRLF_GUESS) {
+                       /* If we have any CR or CRLF line endings, we do not touch it */
+                       /* This is the new safer autocrlf-handling */
+                       if (stats.cr > 0 || stats.crlf > 0)
+                               return 0;
+               }
+
                /* If we have any bare CR characters, we're not going to touch it */
                if (stats.cr != stats.crlf)
                        return 0;
@@ -249,7 +327,9 @@ static int filter_buffer(int in, int out, void *data)
        struct child_process child_process;
        struct filter_params *params = (struct filter_params *)data;
        int write_err, status;
-       const char *argv[] = { params->cmd, NULL };
+       const char *argv[] = { NULL, NULL };
+
+       argv[0] = params->cmd;
 
        memset(&child_process, 0, sizeof(child_process));
        child_process.argv = argv;
@@ -374,12 +454,16 @@ static int read_convert_config(const char *var, const char *value, void *cb)
 
 static void setup_convert_check(struct git_attr_check *check)
 {
+       static struct git_attr *attr_text;
        static struct git_attr *attr_crlf;
+       static struct git_attr *attr_eol;
        static struct git_attr *attr_ident;
        static struct git_attr *attr_filter;
 
-       if (!attr_crlf) {
+       if (!attr_text) {
+               attr_text = git_attr("text");
                attr_crlf = git_attr("crlf");
+               attr_eol = git_attr("eol");
                attr_ident = git_attr("ident");
                attr_filter = git_attr("filter");
                user_convert_tail = &user_convert;
@@ -388,6 +472,8 @@ static void setup_convert_check(struct git_attr_check *check)
        check[0].attr = attr_crlf;
        check[1].attr = attr_ident;
        check[2].attr = attr_filter;
+       check[3].attr = attr_eol;
+       check[4].attr = attr_text;
 }
 
 static int count_ident(const char *cp, unsigned long size)
@@ -425,6 +511,8 @@ static int count_ident(const char *cp, unsigned long size)
                                cnt++;
                                break;
                        }
+                       if (ch == '\n')
+                               break;
                }
        }
        return cnt;
@@ -455,6 +543,11 @@ static int ident_to_git(const char *path, const char *src, size_t len,
                        dollar = memchr(src + 3, '$', len - 3);
                        if (!dollar)
                                break;
+                       if (memchr(src + 3, '\n', dollar - src - 3)) {
+                               /* Line break before the next dollar. */
+                               continue;
+                       }
+
                        memcpy(dst, "Id$", 3);
                        dst += 3;
                        len -= dollar + 1 - src;
@@ -470,7 +563,7 @@ static int ident_to_worktree(const char *path, const char *src, size_t len,
                              struct strbuf *buf, int ident)
 {
        unsigned char sha1[20];
-       char *to_free = NULL, *dollar;
+       char *to_free = NULL, *dollar, *spc;
        int cnt;
 
        if (!ident)
@@ -506,7 +599,10 @@ static int ident_to_worktree(const char *path, const char *src, size_t len,
                } else if (src[2] == ':') {
                        /*
                         * It's possible that an expanded Id has crept its way into the
-                        * repository, we cope with that by stripping the expansion out
+                        * repository, we cope with that by stripping the expansion out.
+                        * This is probably not a good idea, since it will cause changes
+                        * on checkout, which won't go away by stash, but let's keep it
+                        * for git-style ids.
                         */
                        dollar = memchr(src + 3, '$', len - 3);
                        if (!dollar) {
@@ -514,6 +610,20 @@ static int ident_to_worktree(const char *path, const char *src, size_t len,
                                break;
                        }
 
+                       if (memchr(src + 3, '\n', dollar - src - 3)) {
+                               /* Line break before the next dollar. */
+                               continue;
+                       }
+
+                       spc = memchr(src + 4, ' ', dollar - src - 4);
+                       if (spc && spc < dollar-1) {
+                               /* There are spaces in unexpected places.
+                                * This is probably an id from some other
+                                * versioning system. Keep it for now.
+                                */
+                               continue;
+                       }
+
                        len -= dollar + 1 - src;
                        src  = dollar + 1;
                } else {
@@ -544,9 +654,24 @@ static int git_path_check_crlf(const char *path, struct git_attr_check *check)
                ;
        else if (!strcmp(value, "input"))
                return CRLF_INPUT;
+       else if (!strcmp(value, "auto"))
+               return CRLF_AUTO;
        return CRLF_GUESS;
 }
 
+static int git_path_check_eol(const char *path, struct git_attr_check *check)
+{
+       const char *value = check->value;
+
+       if (ATTR_UNSET(value))
+               ;
+       else if (!strcmp(value, "lf"))
+               return EOL_LF;
+       else if (!strcmp(value, "crlf"))
+               return EOL_CRLF;
+       return EOL_UNSET;
+}
+
 static struct convert_driver *git_path_check_convert(const char *path,
                                             struct git_attr_check *check)
 {
@@ -568,20 +693,34 @@ static int git_path_check_ident(const char *path, struct git_attr_check *check)
        return !!ATTR_TRUE(value);
 }
 
+enum action determine_action(enum action text_attr, enum eol eol_attr) {
+       if (text_attr == CRLF_BINARY)
+               return CRLF_BINARY;
+       if (eol_attr == EOL_LF)
+               return CRLF_INPUT;
+       if (eol_attr == EOL_CRLF)
+               return CRLF_CRLF;
+       return text_attr;
+}
+
 int convert_to_git(const char *path, const char *src, size_t len,
                    struct strbuf *dst, enum safe_crlf checksafe)
 {
-       struct git_attr_check check[3];
-       int crlf = CRLF_GUESS;
+       struct git_attr_check check[5];
+       enum action action = CRLF_GUESS;
+       enum eol eol_attr = EOL_UNSET;
        int ident = 0, ret = 0;
        const char *filter = NULL;
 
        setup_convert_check(check);
        if (!git_checkattr(path, ARRAY_SIZE(check), check)) {
                struct convert_driver *drv;
-               crlf = git_path_check_crlf(path, check + 0);
+               action = git_path_check_crlf(path, check + 4);
+               if (action == CRLF_GUESS)
+                       action = git_path_check_crlf(path, check + 0);
                ident = git_path_check_ident(path, check + 1);
                drv = git_path_check_convert(path, check + 2);
+               eol_attr = git_path_check_eol(path, check + 3);
                if (drv && drv->clean)
                        filter = drv->clean;
        }
@@ -591,7 +730,8 @@ int convert_to_git(const char *path, const char *src, size_t len,
                src = dst->buf;
                len = dst->len;
        }
-       ret |= crlf_to_git(path, src, len, dst, crlf, checksafe);
+       action = determine_action(action, eol_attr);
+       ret |= crlf_to_git(path, src, len, dst, action, checksafe);
        if (ret) {
                src = dst->buf;
                len = dst->len;
@@ -601,17 +741,21 @@ int convert_to_git(const char *path, const char *src, size_t len,
 
 int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst)
 {
-       struct git_attr_check check[3];
-       int crlf = CRLF_GUESS;
+       struct git_attr_check check[5];
+       enum action action = CRLF_GUESS;
+       enum eol eol_attr = EOL_UNSET;
        int ident = 0, ret = 0;
        const char *filter = NULL;
 
        setup_convert_check(check);
        if (!git_checkattr(path, ARRAY_SIZE(check), check)) {
                struct convert_driver *drv;
-               crlf = git_path_check_crlf(path, check + 0);
+               action = git_path_check_crlf(path, check + 4);
+               if (action == CRLF_GUESS)
+                       action = git_path_check_crlf(path, check + 0);
                ident = git_path_check_ident(path, check + 1);
                drv = git_path_check_convert(path, check + 2);
+               eol_attr = git_path_check_eol(path, check + 3);
                if (drv && drv->smudge)
                        filter = drv->smudge;
        }
@@ -621,7 +765,8 @@ int convert_to_working_tree(const char *path, const char *src, size_t len, struc
                src = dst->buf;
                len = dst->len;
        }
-       ret |= crlf_to_worktree(path, src, len, dst, crlf);
+       action = determine_action(action, eol_attr);
+       ret |= crlf_to_worktree(path, src, len, dst, action);
        if (ret) {
                src = dst->buf;
                len = dst->len;
diff --git a/ctype.c b/ctype.c
index 7ee64c7..de60027 100644 (file)
--- a/ctype.c
+++ b/ctype.c
@@ -10,7 +10,7 @@ enum {
        A = GIT_ALPHA,
        D = GIT_DIGIT,
        G = GIT_GLOB_SPECIAL,   /* *, ?, [, \\ */
-       R = GIT_REGEX_SPECIAL,  /* $, (, ), +, ., ^, {, | */
+       R = GIT_REGEX_SPECIAL   /* $, (, ), +, ., ^, {, | */
 };
 
 unsigned char sane_ctype[256] = {
index a90ab10..e22a2b7 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -141,15 +141,14 @@ static char *path_ok(char *directory)
        }
        else if (interpolated_path && saw_extended_args) {
                struct strbuf expanded_path = STRBUF_INIT;
-               struct strbuf_expand_dict_entry dict[] = {
-                       { "H", hostname },
-                       { "CH", canon_hostname },
-                       { "IP", ip_address },
-                       { "P", tcp_port },
-                       { "D", directory },
-                       { NULL }
-               };
-
+               struct strbuf_expand_dict_entry dict[6];
+
+               dict[0].placeholder = "H"; dict[0].value = hostname;
+               dict[1].placeholder = "CH"; dict[1].value = canon_hostname;
+               dict[2].placeholder = "IP"; dict[2].value = ip_address;
+               dict[3].placeholder = "P"; dict[3].value = tcp_port;
+               dict[4].placeholder = "D"; dict[4].value = directory;
+               dict[5].placeholder = NULL; dict[5].value = NULL;
                if (*dir != '/') {
                        /* Allow only absolute */
                        logerror("'%s': Non-absolute path denied (interpolated-path active)", dir);
@@ -343,7 +342,9 @@ static int upload_pack(void)
 {
        /* Timeout as string */
        char timeout_buf[64];
-       const char *argv[] = { "upload-pack", "--strict", timeout_buf, ".", NULL };
+       const char *argv[] = { "upload-pack", "--strict", NULL, ".", NULL };
+
+       argv[2] = timeout_buf;
 
        snprintf(timeout_buf, sizeof timeout_buf, "--timeout=%u", timeout);
        return run_service_command(argv);
diff --git a/date.c b/date.c
index 002aa3c..68cdcaa 100644 (file)
--- a/date.c
+++ b/date.c
@@ -229,6 +229,7 @@ static const struct {
 
        { "GMT",    0, 0, },    /* Greenwich Mean */
        { "UTC",    0, 0, },    /* Universal (Coordinated) */
+       { "Z",      0, 0, },    /* Zulu, alias for UTC */
 
        { "WET",    0, 0, },    /* Western European */
        { "BST",    0, 1, },    /* British Summer */
@@ -305,7 +306,7 @@ static int match_alpha(const char *date, struct tm *tm, int *offset)
 
        for (i = 0; i < ARRAY_SIZE(timezone_names); i++) {
                int match = match_string(date, timezone_names[i].name);
-               if (match >= 3) {
+               if (match >= 3 || match == strlen(timezone_names[i].name)) {
                        int off = timezone_names[i].offset;
 
                        /* This is bogus, but we like summer */
@@ -585,11 +586,17 @@ static int date_string(unsigned long date, int offset, char *buf, int len)
 
 /* Gr. strptime is crap for this; it doesn't have a way to require RFC2822
    (i.e. English) day/month names, and it doesn't work correctly with %z. */
-int parse_date(const char *date, char *result, int maxlen)
+int parse_date_toffset(const char *date, unsigned long *timestamp, int *offset)
 {
        struct tm tm;
-       int offset, tm_gmt;
-       time_t then;
+       int tm_gmt;
+       unsigned long dummy_timestamp;
+       int dummy_offset;
+
+       if (!timestamp)
+               timestamp = &dummy_timestamp;
+       if (!offset)
+               offset = &dummy_offset;
 
        memset(&tm, 0, sizeof(tm));
        tm.tm_year = -1;
@@ -599,7 +606,7 @@ int parse_date(const char *date, char *result, int maxlen)
        tm.tm_hour = -1;
        tm.tm_min = -1;
        tm.tm_sec = -1;
-       offset = -1;
+       *offset = -1;
        tm_gmt = 0;
 
        for (;;) {
@@ -611,11 +618,11 @@ int parse_date(const char *date, char *result, int maxlen)
                        break;
 
                if (isalpha(c))
-                       match = match_alpha(date, &tm, &offset);
+                       match = match_alpha(date, &tm, offset);
                else if (isdigit(c))
-                       match = match_digit(date, &tm, &offset, &tm_gmt);
+                       match = match_digit(date, &tm, offset, &tm_gmt);
                else if ((c == '-' || c == '+') && isdigit(date[1]))
-                       match = match_tz(date, &offset);
+                       match = match_tz(date, offset);
 
                if (!match) {
                        /* BAD CRAP */
@@ -626,16 +633,26 @@ int parse_date(const char *date, char *result, int maxlen)
        }
 
        /* mktime uses local timezone */
-       then = tm_to_time_t(&tm);
-       if (offset == -1)
-               offset = (then - mktime(&tm)) / 60;
+       *timestamp = tm_to_time_t(&tm);
+       if (*offset == -1)
+               *offset = (*timestamp - mktime(&tm)) / 60;
 
-       if (then == -1)
+       if (*timestamp == -1)
                return -1;
 
        if (!tm_gmt)
-               then -= offset * 60;
-       return date_string(then, offset, result, maxlen);
+               *timestamp -= *offset * 60;
+       return 1; /* success */
+}
+
+int parse_date(const char *date, char *result, int maxlen)
+{
+       unsigned long timestamp;
+       int offset;
+       if (parse_date_toffset(date, &timestamp, &offset) > 0)
+               return date_string(timestamp, offset, result, maxlen);
+       else
+               return -1;
 }
 
 enum date_mode parse_date_format(const char *format)
@@ -983,11 +1000,12 @@ static unsigned long approxidate_str(const char *date,
 
 unsigned long approxidate_relative(const char *date, const struct timeval *tv)
 {
-       char buffer[50];
+       unsigned long timestamp;
+       int offset;
        int errors = 0;
 
-       if (parse_date(date, buffer, sizeof(buffer)) > 0)
-               return strtoul(buffer, NULL, 0);
+       if (parse_date_toffset(date, &timestamp, &offset) > 0)
+               return timestamp;
 
        return approxidate_str(date, tv, &errors);
 }
@@ -995,14 +1013,15 @@ unsigned long approxidate_relative(const char *date, const struct timeval *tv)
 unsigned long approxidate_careful(const char *date, int *error_ret)
 {
        struct timeval tv;
-       char buffer[50];
+       unsigned long timestamp;
+       int offset;
        int dummy = 0;
        if (!error_ret)
                error_ret = &dummy;
 
-       if (parse_date(date, buffer, sizeof(buffer)) > 0) {
+       if (parse_date_toffset(date, &timestamp, &offset) > 0) {
                *error_ret = 0;
-               return strtoul(buffer, NULL, 0);
+               return timestamp;
        }
 
        gettimeofday(&tv, NULL);
index c9f6e05..8b8978a 100644 (file)
@@ -70,6 +70,7 @@ static int match_stat_with_submodule(struct diff_options *diffopt,
        int changed = ce_match_stat(ce, st, ce_option);
        if (S_ISGITLINK(ce->ce_mode)
            && !DIFF_OPT_TST(diffopt, IGNORE_SUBMODULES)
+           && !DIFF_OPT_TST(diffopt, IGNORE_DIRTY_SUBMODULES)
            && (!changed || DIFF_OPT_TST(diffopt, DIRTY_SUBMODULES))) {
                *dirty_submodule = is_submodule_modified(ce->name, DIFF_OPT_TST(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES));
        }
diff --git a/diff.c b/diff.c
index 494f560..9d70f9d 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -30,6 +30,7 @@ static const char *diff_word_regex_cfg;
 static const char *external_diff_cmd_cfg;
 int diff_auto_refresh_index = 1;
 static int diff_mnemonic_prefix;
+static int diff_no_prefix;
 
 static char diff_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_RESET,
@@ -43,10 +44,6 @@ static char diff_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_NORMAL,       /* FUNCINFO */
 };
 
-static void diff_filespec_load_driver(struct diff_filespec *one);
-static size_t fill_textconv(struct userdiff_driver *driver,
-                           struct diff_filespec *df, char **outbuf);
-
 static int parse_diff_color_slot(const char *var, int ofs)
 {
        if (!strcasecmp(var+ofs, "plain"))
@@ -101,6 +98,10 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
                diff_mnemonic_prefix = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "diff.noprefix")) {
+               diff_no_prefix = git_config_bool(var, value);
+               return 0;
+       }
        if (!strcmp(var, "diff.external"))
                return git_config_string(&external_diff_cmd_cfg, var, value);
        if (!strcmp(var, "diff.wordregex"))
@@ -194,8 +195,8 @@ struct emit_callback {
        sane_truncate_fn truncate;
        const char **label_path;
        struct diff_words_data *diff_words;
+       struct diff_options *opt;
        int *found_changesp;
-       FILE *file;
        struct strbuf *header;
 };
 
@@ -282,11 +283,19 @@ static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2,
        ecbdata->blank_at_eof_in_postimage = (at - l2) + 1;
 }
 
-static void emit_line_0(FILE *file, const char *set, const char *reset,
+static void emit_line_0(struct diff_options *o, const char *set, const char *reset,
                        int first, const char *line, int len)
 {
        int has_trailing_newline, has_trailing_carriage_return;
        int nofirst;
+       FILE *file = o->file;
+
+       if (o->output_prefix) {
+               struct strbuf *msg = NULL;
+               msg = o->output_prefix(o, o->output_prefix_data);
+               assert(msg);
+               fwrite(msg->buf, msg->len, 1, file);
+       }
 
        if (len == 0) {
                has_trailing_newline = (first == '\n');
@@ -316,10 +325,10 @@ static void emit_line_0(FILE *file, const char *set, const char *reset,
                fputc('\n', file);
 }
 
-static void emit_line(FILE *file, const char *set, const char *reset,
+static void emit_line(struct diff_options *o, const char *set, const char *reset,
                      const char *line, int len)
 {
-       emit_line_0(file, set, reset, line[0], line+1, len-1);
+       emit_line_0(o, set, reset, line[0], line+1, len-1);
 }
 
 static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line, int len)
@@ -341,15 +350,15 @@ static void emit_add_line(const char *reset,
        const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
 
        if (!*ws)
-               emit_line_0(ecbdata->file, set, reset, '+', line, len);
+               emit_line_0(ecbdata->opt, set, reset, '+', line, len);
        else if (new_blank_line_at_eof(ecbdata, line, len))
                /* Blank line at EOF - paint '+' as well */
-               emit_line_0(ecbdata->file, ws, reset, '+', line, len);
+               emit_line_0(ecbdata->opt, ws, reset, '+', line, len);
        else {
                /* Emit just the prefix, then the rest. */
-               emit_line_0(ecbdata->file, set, reset, '+', "", 0);
+               emit_line_0(ecbdata->opt, set, reset, '+', "", 0);
                ws_check_emit(line, len, ecbdata->ws_rule,
-                             ecbdata->file, set, reset, ws);
+                             ecbdata->opt->file, set, reset, ws);
        }
 }
 
@@ -362,6 +371,9 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
        const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
        static const char atat[2] = { '@', '@' };
        const char *cp, *ep;
+       struct strbuf msgbuf = STRBUF_INIT;
+       int org_len = len;
+       int i = 1;
 
        /*
         * As a hunk header must begin with "@@ -<old>, +<new> @@",
@@ -370,23 +382,42 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
        if (len < 10 ||
            memcmp(line, atat, 2) ||
            !(ep = memmem(line + 2, len - 2, atat, 2))) {
-               emit_line(ecbdata->file, plain, reset, line, len);
+               emit_line(ecbdata->opt, plain, reset, line, len);
                return;
        }
        ep += 2; /* skip over @@ */
 
        /* The hunk header in fraginfo color */
-       emit_line(ecbdata->file, frag, reset, line, ep - line);
+       strbuf_add(&msgbuf, frag, strlen(frag));
+       strbuf_add(&msgbuf, line, ep - line);
+       strbuf_add(&msgbuf, reset, strlen(reset));
+
+       /*
+        * trailing "\r\n"
+        */
+       for ( ; i < 3; i++)
+               if (line[len - i] == '\r' || line[len - i] == '\n')
+                       len--;
 
        /* blank before the func header */
        for (cp = ep; ep - line < len; ep++)
                if (*ep != ' ' && *ep != '\t')
                        break;
-       if (ep != cp)
-               emit_line(ecbdata->file, plain, reset, cp, ep - cp);
+       if (ep != cp) {
+               strbuf_add(&msgbuf, plain, strlen(plain));
+               strbuf_add(&msgbuf, cp, ep - cp);
+               strbuf_add(&msgbuf, reset, strlen(reset));
+       }
 
-       if (ep < line + len)
-               emit_line(ecbdata->file, func, reset, ep, line + len - ep);
+       if (ep < line + len) {
+               strbuf_add(&msgbuf, func, strlen(func));
+               strbuf_add(&msgbuf, ep, line + len - ep);
+               strbuf_add(&msgbuf, reset, strlen(reset));
+       }
+
+       strbuf_add(&msgbuf, line + len, org_len - len);
+       emit_line(ecbdata->opt, "", "", msgbuf.buf, msgbuf.len);
+       strbuf_release(&msgbuf);
 }
 
 static struct diff_tempfile *claim_diff_tempfile(void) {
@@ -446,7 +477,7 @@ static void emit_rewrite_lines(struct emit_callback *ecb,
                len = endp ? (endp - data + 1) : size;
                if (prefix != '+') {
                        ecb->lno_in_preimage++;
-                       emit_line_0(ecb->file, old, reset, '-',
+                       emit_line_0(ecb->opt, old, reset, '-',
                                    data, len);
                } else {
                        ecb->lno_in_postimage++;
@@ -458,7 +489,7 @@ static void emit_rewrite_lines(struct emit_callback *ecb,
        if (!endp) {
                const char *plain = diff_get_color(ecb->color_diff,
                                                   DIFF_PLAIN);
-               emit_line_0(ecb->file, plain, reset, '\\',
+               emit_line_0(ecb->opt, plain, reset, '\\',
                            nneof, strlen(nneof));
        }
 }
@@ -482,6 +513,13 @@ static void emit_rewrite_diff(const char *name_a,
        char *data_one, *data_two;
        size_t size_one, size_two;
        struct emit_callback ecbdata;
+       char *line_prefix = "";
+       struct strbuf *msgbuf;
+
+       if (o && o->output_prefix) {
+               msgbuf = o->output_prefix(o, o->output_prefix_data);
+               line_prefix = msgbuf->buf;
+       }
 
        if (diff_mnemonic_prefix && DIFF_OPT_TST(o, REVERSE_DIFF)) {
                a_prefix = o->b_prefix;
@@ -508,7 +546,7 @@ static void emit_rewrite_diff(const char *name_a,
        ecbdata.color_diff = color_diff;
        ecbdata.found_changesp = &o->found_changes;
        ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
-       ecbdata.file = o->file;
+       ecbdata.opt = o;
        if (ecbdata.ws_rule & WS_BLANK_AT_EOF) {
                mmfile_t mf1, mf2;
                mf1.ptr = (char *)data_one;
@@ -523,9 +561,10 @@ static void emit_rewrite_diff(const char *name_a,
        lc_a = count_lines(data_one, size_one);
        lc_b = count_lines(data_two, size_two);
        fprintf(o->file,
-               "%s--- %s%s%s\n%s+++ %s%s%s\n%s@@ -",
-               metainfo, a_name.buf, name_a_tab, reset,
-               metainfo, b_name.buf, name_b_tab, reset, fraginfo);
+               "%s%s--- %s%s%s\n%s%s+++ %s%s%s\n%s%s@@ -",
+               line_prefix, metainfo, a_name.buf, name_a_tab, reset,
+               line_prefix, metainfo, b_name.buf, name_b_tab, reset,
+               line_prefix, fraginfo);
        print_line_count(o->file, lc_a);
        fprintf(o->file, " +");
        print_line_count(o->file, lc_b);
@@ -584,7 +623,8 @@ struct diff_words_style diff_words_styles[] = {
 struct diff_words_data {
        struct diff_words_buffer minus, plus;
        const char *current_plus;
-       FILE *file;
+       int last_minus;
+       struct diff_options *opt;
        regex_t *word_regex;
        enum diff_words_type type;
        struct diff_words_style *style;
@@ -593,10 +633,15 @@ struct diff_words_data {
 static int fn_out_diff_words_write_helper(FILE *fp,
                                          struct diff_words_style_elem *st_el,
                                          const char *newline,
-                                         size_t count, const char *buf)
+                                         size_t count, const char *buf,
+                                         const char *line_prefix)
 {
+       int print = 0;
+
        while (count) {
                char *p = memchr(buf, '\n', count);
+               if (print)
+                       fputs(line_prefix, fp);
                if (p != buf) {
                        if (st_el->color && fputs(st_el->color, fp) < 0)
                                return -1;
@@ -614,21 +659,74 @@ static int fn_out_diff_words_write_helper(FILE *fp,
                        return -1;
                count -= p + 1 - buf;
                buf = p + 1;
+               print = 1;
        }
        return 0;
 }
 
+/*
+ * '--color-words' algorithm can be described as:
+ *
+ *   1. collect a the minus/plus lines of a diff hunk, divided into
+ *      minus-lines and plus-lines;
+ *
+ *   2. break both minus-lines and plus-lines into words and
+ *      place them into two mmfile_t with one word for each line;
+ *
+ *   3. use xdiff to run diff on the two mmfile_t to get the words level diff;
+ *
+ * And for the common parts of the both file, we output the plus side text.
+ * diff_words->current_plus is used to trace the current position of the plus file
+ * which printed. diff_words->last_minus is used to trace the last minus word
+ * printed.
+ *
+ * For '--graph' to work with '--color-words', we need to output the graph prefix
+ * on each line of color words output. Generally, there are two conditions on
+ * which we should output the prefix.
+ *
+ *   1. diff_words->last_minus == 0 &&
+ *      diff_words->current_plus == diff_words->plus.text.ptr
+ *
+ *      that is: the plus text must start as a new line, and if there is no minus
+ *      word printed, a graph prefix must be printed.
+ *
+ *   2. diff_words->current_plus > diff_words->plus.text.ptr &&
+ *      *(diff_words->current_plus - 1) == '\n'
+ *
+ *      that is: a graph prefix must be printed following a '\n'
+ */
+static int color_words_output_graph_prefix(struct diff_words_data *diff_words)
+{
+       if ((diff_words->last_minus == 0 &&
+               diff_words->current_plus == diff_words->plus.text.ptr) ||
+               (diff_words->current_plus > diff_words->plus.text.ptr &&
+               *(diff_words->current_plus - 1) == '\n')) {
+               return 1;
+       } else {
+               return 0;
+       }
+}
+
 static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
 {
        struct diff_words_data *diff_words = priv;
        struct diff_words_style *style = diff_words->style;
        int minus_first, minus_len, plus_first, plus_len;
        const char *minus_begin, *minus_end, *plus_begin, *plus_end;
+       struct diff_options *opt = diff_words->opt;
+       struct strbuf *msgbuf;
+       char *line_prefix = "";
 
        if (line[0] != '@' || parse_hunk_header(line, len,
                        &minus_first, &minus_len, &plus_first, &plus_len))
                return;
 
+       assert(opt);
+       if (opt->output_prefix) {
+               msgbuf = opt->output_prefix(opt, opt->output_prefix_data);
+               line_prefix = msgbuf->buf;
+       }
+
        /* POSIX requires that first be decremented by one if len == 0... */
        if (minus_len) {
                minus_begin = diff_words->minus.orig[minus_first].begin;
@@ -644,21 +742,32 @@ static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
        } else
                plus_begin = plus_end = diff_words->plus.orig[plus_first].end;
 
-       if (diff_words->current_plus != plus_begin)
-               fn_out_diff_words_write_helper(diff_words->file,
+       if (color_words_output_graph_prefix(diff_words)) {
+               fputs(line_prefix, diff_words->opt->file);
+       }
+       if (diff_words->current_plus != plus_begin) {
+               fn_out_diff_words_write_helper(diff_words->opt->file,
                                &style->ctx, style->newline,
                                plus_begin - diff_words->current_plus,
-                               diff_words->current_plus);
-       if (minus_begin != minus_end)
-               fn_out_diff_words_write_helper(diff_words->file,
+                               diff_words->current_plus, line_prefix);
+               if (*(plus_begin - 1) == '\n')
+                       fputs(line_prefix, diff_words->opt->file);
+       }
+       if (minus_begin != minus_end) {
+               fn_out_diff_words_write_helper(diff_words->opt->file,
                                &style->old, style->newline,
-                               minus_end - minus_begin, minus_begin);
-       if (plus_begin != plus_end)
-               fn_out_diff_words_write_helper(diff_words->file,
+                               minus_end - minus_begin, minus_begin,
+                               line_prefix);
+       }
+       if (plus_begin != plus_end) {
+               fn_out_diff_words_write_helper(diff_words->opt->file,
                                &style->new, style->newline,
-                               plus_end - plus_begin, plus_begin);
+                               plus_end - plus_begin, plus_begin,
+                               line_prefix);
+       }
 
        diff_words->current_plus = plus_end;
+       diff_words->last_minus = minus_first;
 }
 
 /* This function starts looking at *begin, and returns 0 iff a word was found. */
@@ -739,22 +848,35 @@ static void diff_words_show(struct diff_words_data *diff_words)
        mmfile_t minus, plus;
        struct diff_words_style *style = diff_words->style;
 
+       struct diff_options *opt = diff_words->opt;
+       struct strbuf *msgbuf;
+       char *line_prefix = "";
+
+       assert(opt);
+       if (opt->output_prefix) {
+               msgbuf = opt->output_prefix(opt, opt->output_prefix_data);
+               line_prefix = msgbuf->buf;
+       }
+
        /* special case: only removal */
        if (!diff_words->plus.text.size) {
-               fn_out_diff_words_write_helper(diff_words->file,
+               fputs(line_prefix, diff_words->opt->file);
+               fn_out_diff_words_write_helper(diff_words->opt->file,
                        &style->old, style->newline,
-                       diff_words->minus.text.size, diff_words->minus.text.ptr);
+                       diff_words->minus.text.size,
+                       diff_words->minus.text.ptr, line_prefix);
                diff_words->minus.text.size = 0;
                return;
        }
 
        diff_words->current_plus = diff_words->plus.text.ptr;
+       diff_words->last_minus = 0;
 
        memset(&xpp, 0, sizeof(xpp));
        memset(&xecfg, 0, sizeof(xecfg));
        diff_words_fill(&diff_words->minus, &minus, diff_words->word_regex);
        diff_words_fill(&diff_words->plus, &plus, diff_words->word_regex);
-       xpp.flags = XDF_NEED_MINIMAL;
+       xpp.flags = 0;
        /* as only the hunk header will be parsed, we need a 0-context */
        xecfg.ctxlen = 0;
        xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words,
@@ -762,11 +884,15 @@ static void diff_words_show(struct diff_words_data *diff_words)
        free(minus.ptr);
        free(plus.ptr);
        if (diff_words->current_plus != diff_words->plus.text.ptr +
-                       diff_words->plus.text.size)
-               fn_out_diff_words_write_helper(diff_words->file,
+                       diff_words->plus.text.size) {
+               if (color_words_output_graph_prefix(diff_words))
+                       fputs(line_prefix, diff_words->opt->file);
+               fn_out_diff_words_write_helper(diff_words->opt->file,
                        &style->ctx, style->newline,
                        diff_words->plus.text.ptr + diff_words->plus.text.size
-                       - diff_words->current_plus, diff_words->current_plus);
+                       - diff_words->current_plus, diff_words->current_plus,
+                       line_prefix);
+       }
        diff_words->minus.text.size = diff_words->plus.text.size = 0;
 }
 
@@ -838,9 +964,17 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
        const char *meta = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
        const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);
        const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
+       struct diff_options *o = ecbdata->opt;
+       char *line_prefix = "";
+       struct strbuf *msgbuf;
+
+       if (o && o->output_prefix) {
+               msgbuf = o->output_prefix(o, o->output_prefix_data);
+               line_prefix = msgbuf->buf;
+       }
 
        if (ecbdata->header) {
-               fprintf(ecbdata->file, "%s", ecbdata->header->buf);
+               fprintf(ecbdata->opt->file, "%s", ecbdata->header->buf);
                strbuf_reset(ecbdata->header);
                ecbdata->header = NULL;
        }
@@ -852,10 +986,10 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                name_a_tab = strchr(ecbdata->label_path[0], ' ') ? "\t" : "";
                name_b_tab = strchr(ecbdata->label_path[1], ' ') ? "\t" : "";
 
-               fprintf(ecbdata->file, "%s--- %s%s%s\n",
-                       meta, ecbdata->label_path[0], reset, name_a_tab);
-               fprintf(ecbdata->file, "%s+++ %s%s%s\n",
-                       meta, ecbdata->label_path[1], reset, name_b_tab);
+               fprintf(ecbdata->opt->file, "%s%s--- %s%s%s\n",
+                       line_prefix, meta, ecbdata->label_path[0], reset, name_a_tab);
+               fprintf(ecbdata->opt->file, "%s%s+++ %s%s%s\n",
+                       line_prefix, meta, ecbdata->label_path[1], reset, name_b_tab);
                ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
        }
 
@@ -872,15 +1006,15 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                find_lno(line, ecbdata);
                emit_hunk_header(ecbdata, line, len);
                if (line[len-1] != '\n')
-                       putc('\n', ecbdata->file);
+                       putc('\n', ecbdata->opt->file);
                return;
        }
 
        if (len < 1) {
-               emit_line(ecbdata->file, reset, reset, line, len);
+               emit_line(ecbdata->opt, reset, reset, line, len);
                if (ecbdata->diff_words
                    && ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN)
-                       fputs("~\n", ecbdata->file);
+                       fputs("~\n", ecbdata->opt->file);
                return;
        }
 
@@ -896,11 +1030,11 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                }
                diff_words_flush(ecbdata);
                if (ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN) {
-                       emit_line(ecbdata->file, plain, reset, line, len);
-                       fputs("~\n", ecbdata->file);
+                       emit_line(ecbdata->opt, plain, reset, line, len);
+                       fputs("~\n", ecbdata->opt->file);
                } else {
                        /* don't print the prefix character */
-                       emit_line(ecbdata->file, plain, reset, line+1, len-1);
+                       emit_line(ecbdata->opt, plain, reset, line+1, len-1);
                }
                return;
        }
@@ -912,7 +1046,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                ecbdata->lno_in_preimage++;
                if (line[0] == ' ')
                        ecbdata->lno_in_postimage++;
-               emit_line(ecbdata->file, color, reset, line, len);
+               emit_line(ecbdata->opt, color, reset, line, len);
        } else {
                ecbdata->lno_in_postimage++;
                emit_add_line(reset, ecbdata, line + 1, len - 1);
@@ -1092,10 +1226,17 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
        int total_files = data->nr;
        int width, name_width;
        const char *reset, *set, *add_c, *del_c;
+       const char *line_prefix = "";
+       struct strbuf *msg = NULL;
 
        if (data->nr == 0)
                return;
 
+       if (options->output_prefix) {
+               msg = options->output_prefix(options, options->output_prefix_data);
+               line_prefix = msg->buf;
+       }
+
        width = options->stat_width ? options->stat_width : 80;
        name_width = options->stat_name_width ? options->stat_name_width : 50;
 
@@ -1165,6 +1306,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                }
 
                if (data->files[i]->is_binary) {
+                       fprintf(options->file, "%s", line_prefix);
                        show_name(options->file, prefix, name, len);
                        fprintf(options->file, "  Bin ");
                        fprintf(options->file, "%s%"PRIuMAX"%s",
@@ -1177,6 +1319,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                        continue;
                }
                else if (data->files[i]->is_unmerged) {
+                       fprintf(options->file, "%s", line_prefix);
                        show_name(options->file, prefix, name, len);
                        fprintf(options->file, "  Unmerged\n");
                        continue;
@@ -1199,6 +1342,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                        add = scale_linear(add, width, max_change);
                        del = scale_linear(del, width, max_change);
                }
+               fprintf(options->file, "%s", line_prefix);
                show_name(options->file, prefix, name, len);
                fprintf(options->file, "%5"PRIuMAX"%s", added + deleted,
                                added + deleted ? " " : "");
@@ -1206,6 +1350,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                show_graph(options->file, '-', del, del_c, reset);
                fprintf(options->file, "\n");
        }
+       fprintf(options->file, "%s", line_prefix);
        fprintf(options->file,
               " %d files changed, %d insertions(+), %d deletions(-)\n",
               total_files, adds, dels);
@@ -1232,6 +1377,12 @@ static void show_shortstats(struct diffstat_t *data, struct diff_options *option
                        }
                }
        }
+       if (options->output_prefix) {
+               struct strbuf *msg = NULL;
+               msg = options->output_prefix(options,
+                               options->output_prefix_data);
+               fprintf(options->file, "%s", msg->buf);
+       }
        fprintf(options->file, " %d files changed, %d insertions(+), %d deletions(-)\n",
               total_files, adds, dels);
 }
@@ -1246,6 +1397,13 @@ static void show_numstat(struct diffstat_t *data, struct diff_options *options)
        for (i = 0; i < data->nr; i++) {
                struct diffstat_file *file = data->files[i];
 
+               if (options->output_prefix) {
+                       struct strbuf *msg = NULL;
+                       msg = options->output_prefix(options,
+                                       options->output_prefix_data);
+                       fprintf(options->file, "%s", msg->buf);
+               }
+
                if (file->is_binary)
                        fprintf(options->file, "-\t-\t");
                else
@@ -1281,10 +1439,18 @@ struct dirstat_dir {
        int alloc, nr, percent, cumulative;
 };
 
-static long gather_dirstat(FILE *file, struct dirstat_dir *dir, unsigned long changed, const char *base, int baselen)
+static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,
+               unsigned long changed, const char *base, int baselen)
 {
        unsigned long this_dir = 0;
        unsigned int sources = 0;
+       const char *line_prefix = "";
+       struct strbuf *msg = NULL;
+
+       if (opt->output_prefix) {
+               msg = opt->output_prefix(opt, opt->output_prefix_data);
+               line_prefix = msg->buf;
+       }
 
        while (dir->nr) {
                struct dirstat_file *f = dir->files;
@@ -1299,7 +1465,7 @@ static long gather_dirstat(FILE *file, struct dirstat_dir *dir, unsigned long ch
                slash = strchr(f->name + baselen, '/');
                if (slash) {
                        int newbaselen = slash + 1 - f->name;
-                       this = gather_dirstat(file, dir, changed, f->name, newbaselen);
+                       this = gather_dirstat(opt, dir, changed, f->name, newbaselen);
                        sources++;
                } else {
                        this = f->changed;
@@ -1321,7 +1487,8 @@ static long gather_dirstat(FILE *file, struct dirstat_dir *dir, unsigned long ch
                if (permille) {
                        int percent = permille / 10;
                        if (percent >= dir->percent) {
-                               fprintf(file, "%4d.%01d%% %.*s\n", percent, permille % 10, baselen, base);
+                               fprintf(opt->file, "%s%4d.%01d%% %.*s\n", line_prefix,
+                                       percent, permille % 10, baselen, base);
                                if (!dir->cumulative)
                                        return 0;
                        }
@@ -1401,7 +1568,7 @@ static void show_dirstat(struct diff_options *options)
 
        /* Show all directories with more than x% of the changes */
        qsort(dir.files, dir.nr, sizeof(dir.files[0]), dirstat_compare);
-       gather_dirstat(options->file, &dir, changed, "", 0);
+       gather_dirstat(options, &dir, changed, "", 0);
 }
 
 static void free_diffstat_info(struct diffstat_t *diffstat)
@@ -1459,6 +1626,15 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)
        const char *reset = diff_get_color(color_diff, DIFF_RESET);
        const char *set = diff_get_color(color_diff, DIFF_FILE_NEW);
        char *err;
+       char *line_prefix = "";
+       struct strbuf *msgbuf;
+
+       assert(data->o);
+       if (data->o->output_prefix) {
+               msgbuf = data->o->output_prefix(data->o,
+                       data->o->output_prefix_data);
+               line_prefix = msgbuf->buf;
+       }
 
        if (line[0] == '+') {
                unsigned bad;
@@ -1466,18 +1642,18 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)
                if (is_conflict_marker(line + 1, marker_size, len - 1)) {
                        data->status |= 1;
                        fprintf(data->o->file,
-                               "%s:%d: leftover conflict marker\n",
-                               data->filename, data->lineno);
+                               "%s%s:%d: leftover conflict marker\n",
+                               line_prefix, data->filename, data->lineno);
                }
                bad = ws_check(line + 1, len - 1, data->ws_rule);
                if (!bad)
                        return;
                data->status |= bad;
                err = whitespace_error_string(bad);
-               fprintf(data->o->file, "%s:%d: %s.\n",
-                       data->filename, data->lineno, err);
+               fprintf(data->o->file, "%s%s:%d: %s.\n",
+                       line_prefix, data->filename, data->lineno, err);
                free(err);
-               emit_line(data->o->file, set, reset, line, 1);
+               emit_line(data->o, set, reset, line, 1);
                ws_check_emit(line + 1, len - 1, data->ws_rule,
                              data->o->file, set, reset, ws);
        } else if (line[0] == ' ') {
@@ -1515,7 +1691,7 @@ static unsigned char *deflate_it(char *data,
        return deflated;
 }
 
-static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two)
+static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two, char *prefix)
 {
        void *cp;
        void *delta;
@@ -1544,13 +1720,13 @@ static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two)
        }
 
        if (delta && delta_size < deflate_size) {
-               fprintf(file, "delta %lu\n", orig_size);
+               fprintf(file, "%sdelta %lu\n", prefix, orig_size);
                free(deflated);
                data = delta;
                data_size = delta_size;
        }
        else {
-               fprintf(file, "literal %lu\n", two->size);
+               fprintf(file, "%sliteral %lu\n", prefix, two->size);
                free(delta);
                data = deflated;
                data_size = deflate_size;
@@ -1568,18 +1744,19 @@ static void emit_binary_diff_body(FILE *file, mmfile_t *one, mmfile_t *two)
                        line[0] = bytes - 26 + 'a' - 1;
                encode_85(line + 1, cp, bytes);
                cp = (char *) cp + bytes;
+               fprintf(file, "%s", prefix);
                fputs(line, file);
                fputc('\n', file);
        }
-       fprintf(file, "\n");
+       fprintf(file, "%s\n", prefix);
        free(data);
 }
 
-static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two)
+static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two, char *prefix)
 {
-       fprintf(file, "GIT binary patch\n");
-       emit_binary_diff_body(file, one, two);
-       emit_binary_diff_body(file, two, one);
+       fprintf(file, "%sGIT binary patch\n", prefix);
+       emit_binary_diff_body(file, one, two, prefix);
+       emit_binary_diff_body(file, two, one, prefix);
 }
 
 static void diff_filespec_load_driver(struct diff_filespec *one)
@@ -1629,7 +1806,7 @@ void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const
                options->b_prefix = b;
 }
 
-static struct userdiff_driver *get_textconv(struct diff_filespec *one)
+struct userdiff_driver *get_textconv(struct diff_filespec *one)
 {
        if (!DIFF_FILE_VALID(one))
                return NULL;
@@ -1656,6 +1833,7 @@ static void builtin_diff(const char *name_a,
                         struct diff_filespec *one,
                         struct diff_filespec *two,
                         const char *xfrm_msg,
+                        int must_show_header,
                         struct diff_options *o,
                         int complete_rewrite)
 {
@@ -1668,6 +1846,13 @@ static void builtin_diff(const char *name_a,
        struct userdiff_driver *textconv_one = NULL;
        struct userdiff_driver *textconv_two = NULL;
        struct strbuf header = STRBUF_INIT;
+       struct strbuf *msgbuf;
+       char *line_prefix = "";
+
+       if (o->output_prefix) {
+               msgbuf = o->output_prefix(o, o->output_prefix_data);
+               line_prefix = msgbuf->buf;
+       }
 
        if (DIFF_OPT_TST(o, SUBMODULE_LOG) &&
                        (!one->mode || S_ISGITLINK(one->mode)) &&
@@ -1702,25 +1887,28 @@ static void builtin_diff(const char *name_a,
        b_two = quote_two(b_prefix, name_b + (*name_b == '/'));
        lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
        lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
-       strbuf_addf(&header, "%sdiff --git %s %s%s\n", set, a_one, b_two, reset);
+       strbuf_addf(&header, "%s%sdiff --git %s %s%s\n", line_prefix, set, a_one, b_two, reset);
        if (lbl[0][0] == '/') {
                /* /dev/null */
-               strbuf_addf(&header, "%snew file mode %06o%s\n", set, two->mode, reset);
-               if