]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'jc/spht'
authorJunio C Hamano <gitster@pobox.com>
Sun, 9 Dec 2007 09:23:48 +0000 (01:23 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sun, 9 Dec 2007 09:23:48 +0000 (01:23 -0800)
* jc/spht:
  Use gitattributes to define per-path whitespace rule
  core.whitespace: documentation updates.
  builtin-apply: teach whitespace_rules
  builtin-apply: rename "whitespace" variables and fix styles
  core.whitespace: add test for diff whitespace error highlighting
  git-diff: complain about >=8 consecutive spaces in initial indent
  War on whitespace: first, a bit of retreat.

Conflicts:

cache.h
config.c
diff.c

1  2 
Documentation/config.txt
Documentation/gitattributes.txt
Makefile
builtin-apply.c
cache.h
config.c
diff.c
environment.c

diff --combined Documentation/config.txt
index 79d51f26ccfa69a3785e6bebcbdce9b320d96e6d,0e711374dd99e5be07e17bae1b37c81e9062fd66..fabe7f859f32aa53ae86244f014e58bd3aa5ee3d
@@@ -226,15 -226,13 +226,15 @@@ core.compression:
        An integer -1..9, indicating a default compression level.
        -1 is the zlib default. 0 means no compression,
        and 1..9 are various speed/size tradeoffs, 9 being slowest.
 +      If set, this provides a default to other compression variables,
 +      such as 'core.loosecompression' and 'pack.compression'.
  
  core.loosecompression::
        An integer -1..9, indicating the compression level for objects that
        are not in a pack file. -1 is the zlib default. 0 means no
        compression, and 1..9 are various speed/size tradeoffs, 9 being
        slowest.  If not set,  defaults to core.compression.  If that is
 -      not set,  defaults to 0 (best speed).
 +      not set,  defaults to 1 (best speed).
  
  core.packedGitWindowSize::
        Number of bytes of a pack file to map into memory in a
@@@ -295,6 -293,20 +295,20 @@@ core.pager:
        The command that git will use to paginate output.  Can be overridden
        with the `GIT_PAGER` environment variable.
  
+ core.whitespace::
+       A comma separated list of common whitespace problems to
+       notice.  `git diff` will use `color.diff.whitespace` to
+       highlight them, and `git apply --whitespace=error` will
+       consider them as errors:
+ +
+ * `trailing-space` treats trailing whitespaces at the end of the line
+   as an error (enabled by default).
+ * `space-before-tab` treats a space character that appears immediately
+   before a tab character in the initial indent part of the line as an
+   error (enabled by default).
+ * `indent-with-non-tab` treats a line that is indented with 8 or more
+   space characters that can be replaced with tab characters.
  alias.*::
        Command aliases for the gitlink:git[1] command wrapper - e.g.
        after defining "alias.last = cat-file commit HEAD", the invocation
@@@ -346,21 -358,14 +360,21 @@@ branch.<name>.mergeoptions:
        option values containing whitespace characters are currently not
        supported.
  
 +branch.<name>.rebase::
 +      When true, rebase the branch <name> on top of the fetched branch,
 +      instead of merging the default branch from the default remote.
 +      *NOTE*: this is a possibly dangerous operation; do *not* use
 +      it unless you understand the implications (see gitlink:git-rebase[1]
 +      for details).
 +
  clean.requireForce::
 -      A boolean to make git-clean do nothing unless given -f or -n.  Defaults
 -      to false.
 +      A boolean to make git-clean do nothing unless given -f
 +      or -n.   Defaults to true.
  
  color.branch::
        A boolean to enable/disable color in the output of
 -      gitlink:git-branch[1]. May be set to `true` (or `always`),
 -      `false` (or `never`) or `auto`, in which case colors are used
 +      gitlink:git-branch[1]. May be set to `always`,
 +      `false` (or `never`) or `auto` (or `true`), in which case colors are used
        only when the output is to a terminal. Defaults to false.
  
  color.branch.<slot>::
@@@ -378,39 -383,27 +392,39 @@@ second is the background.  The positio
  doesn't matter.
  
  color.diff::
 -      When true (or `always`), always use colors in patch.
 -      When false (or `never`), never.  When set to `auto`, use
 -      colors only when the output is to the terminal.
 +      When set to `always`, always use colors in patch.
 +      When false (or `never`), never.  When set to `true` or `auto`, use
 +      colors only when the output is to the terminal. Defaults to false.
  
  color.diff.<slot>::
        Use customized color for diff colorization.  `<slot>` specifies
        which part of the patch to use the specified color, and is one
        of `plain` (context text), `meta` (metainformation), `frag`
        (hunk header), `old` (removed lines), `new` (added lines),
-       `commit` (commit headers), or `whitespace` (highlighting dubious
-       whitespace).  The values of these variables may be specified as
+       `commit` (commit headers), or `whitespace` (highlighting
+       whitespace errors). The values of these variables may be specified as
        in color.branch.<slot>.
  
 +color.interactive::
 +      When set to `always`, always use colors in `git add --interactive`.
 +      When false (or `never`), never.  When set to `true` or `auto`, use
 +      colors only when the output is to the terminal. Defaults to false.
 +
 +color.interactive.<slot>::
 +      Use customized color for `git add --interactive`
 +      output. `<slot>` may be `prompt`, `header`, or `help`, for
 +      three distinct types of normal output from interactive
 +      programs.  The values of these variables may be specified as
 +      in color.branch.<slot>.
 +
  color.pager::
        A boolean to enable/disable colored output when the pager is in
        use (default is true).
  
  color.status::
        A boolean to enable/disable color in the output of
 -      gitlink:git-status[1]. May be set to `true` (or `always`),
 -      `false` (or `never`) or `auto`, in which case colors are used
 +      gitlink:git-status[1]. May be set to `always`,
 +      `false` (or `never`) or `auto` (or `true`), in which case colors are used
        only when the output is to a terminal. Defaults to false.
  
  color.status.<slot>::
@@@ -453,12 -446,6 +467,12 @@@ fetch.unpackLimit:
        pack from a push can make the push operation complete faster,
        especially on slow filesystems.
  
 +format.numbered::
 +      A boolean which can enable sequence numbers in patch subjects.
 +      Seting this option to "auto" will enable it only if there is
 +      more than one patch.  See --numbered option in
 +      gitlink:git-format-patch[1].
 +
  format.headers::
        Additional email headers to include in a patch to be submitted
        by mail.  See gitlink:git-format-patch[1].
@@@ -519,9 -506,7 +533,9 @@@ gc.rerereunresolved:
  rerere.enabled::
        Activate recording of resolved conflicts, so that identical
        conflict hunks can be resolved automatically, should they
 -      be encountered again.  See gitlink:git-rerere[1].
 +      be encountered again.  gitlink:git-rerere[1] command is by
 +      default enabled, but can be disabled by setting this option to
 +      false.
  
  gitcvs.enabled::
        Whether the CVS server interface is enabled for this repository.
@@@ -564,11 -549,6 +578,11 @@@ specified as 'gitcvs.<access_method>.<v
  is one of "ext" and "pserver") to make them apply only for the given
  access method.
  
 +http.proxy::
 +      Override the HTTP proxy, normally configured using the 'http_proxy'
 +      environment variable (see gitlink:curl[1]).  This can be overridden
 +      on a per-remote basis; see remote.<name>.proxy
 +
  http.sslVerify::
        Whether to verify the SSL certificate when fetching or pushing
        over HTTPS. Can be overridden by the 'GIT_SSL_NO_VERIFY' environment
@@@ -676,9 -656,7 +690,9 @@@ pack.compression:
        in a pack file. -1 is the zlib default. 0 means no
        compression, and 1..9 are various speed/size tradeoffs, 9 being
        slowest.  If not set,  defaults to core.compression.  If that is
 -      not set,  defaults to -1.
 +      not set,  defaults to -1, the zlib default, which is "a default
 +      compromise between speed and compression (currently equivalent
 +      to level 6)."
  
  pack.deltaCacheSize::
        The maximum memory in bytes used for caching deltas in
@@@ -697,15 -675,6 +711,15 @@@ pack.threads:
        machines. The required amount of memory for the delta search window
        is however multiplied by the number of threads.
  
 +pack.indexVersion::
 +      Specify the default pack index version.  Valid values are 1 for
 +      legacy pack index used by Git versions prior to 1.5.2, and 2 for
 +      the new pack index with capabilities for packs larger than 4 GB
 +      as well as proper protection against the repacking of corrupted
 +      packs.  Version 2 is selected and this config option ignored
 +      whenever the corresponding pack is larger than 2 GB.  Otherwise
 +      the default is 1.
 +
  pull.octopus::
        The default merge strategy to use when pulling multiple branches
        at once.
@@@ -717,11 -686,6 +731,11 @@@ remote.<name>.url:
        The URL of a remote repository.  See gitlink:git-fetch[1] or
        gitlink:git-push[1].
  
 +remote.<name>.proxy::
 +      For remotes that require curl (http, https and ftp), the URL to
 +      the proxy to use for that remote.  Set to the empty string to
 +      disable proxying for that remote.
 +
  remote.<name>.fetch::
        The default set of "refspec" for gitlink:git-fetch[1]. See
        gitlink:git-fetch[1].
@@@ -762,12 -726,6 +776,12 @@@ showbranch.default:
        The default set of branches for gitlink:git-show-branch[1].
        See gitlink:git-show-branch[1].
  
 +status.relativePaths::
 +      By default, gitlink:git-status[1] shows paths relative to the
 +      current directory. Setting this variable to `false` shows paths
 +      relative to the repository root (this was the default for git
 +      prior to v1.5.4).
 +
  tar.umask::
        This variable can be used to restrict the permission bits of
        tar archive entries.  The default is 0002, which turns off the
index 19bd25f29900c99d78592fe290772856092e2dc5,c4bcbb9358a2902f32400366b498e18cc14e073e..71c7ad76d57f5ce89206ccbc8f4c66cadd8f7743
@@@ -148,23 -148,22 +148,23 @@@ with `$Id$` upon check-in
  `filter`
  ^^^^^^^^
  
 -A `filter` attribute can be set to a string value.  This names
 +A `filter` attribute can be set to a string value that names a
  filter driver specified in the configuration.
  
 -A filter driver consists of `clean` command and `smudge`
 +A filter driver consists of a `clean` command and a `smudge`
  command, either of which can be left unspecified.  Upon
 -checkout, when `smudge` command is specified, the command is fed
 -the blob object from its standard input, and its standard output
 -is used to update the worktree file.  Similarly, `clean` command
 -is used to convert the contents of worktree file upon checkin.
 +checkout, when the `smudge` command is specified, the command is
 +fed the blob object from its standard input, and its standard
 +output is used to update the worktree file.  Similarly, the
 +`clean` command is used to convert the contents of worktree file
 +upon checkin.
  
 -Missing filter driver definition in the config is not an error
 +A missing filter driver definition in the config is not an error
  but makes the filter a no-op passthru.
  
  The content filtering is done to massage the content into a
  shape that is more convenient for the platform, filesystem, and
 -the user to use.  The keyword here is "more convenient" and not
 +the user to use.  The key phrase here is "more convenient" and not
  "turning something unusable into usable".  In other words, the
  intent is that if someone unsets the filter driver definition,
  or does not have the appropriate filter program, the project
@@@ -361,6 -360,37 +361,37 @@@ When left unspecified, the driver itsel
  internal merge and the final merge.
  
  
+ Checking whitespace errors
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ `whitespace`
+ ^^^^^^^^^^^^
+ The `core.whitespace` configuration variable allows you to define what
+ `diff` and `apply` should consider whitespace errors for all paths in
+ the project (See gitlink:git-config[1]).  This attribute gives you finer
+ control per path.
+ Set::
+       Notice all types of potential whitespace errors known to git.
+ Unset::
+       Do not notice anything as error.
+ Unspecified::
+       Use the value of `core.whitespace` configuration variable to
+       decide what to notice as error.
+ String::
+       Specify a comma separate list of common whitespace problems to
+       notice in the same format as `core.whitespace` configuration
+       variable.
  EXAMPLE
  -------
  
diff --combined Makefile
index 3a119ae7b0b0f39fb72f3b70fa393a133bdb88fc,ac6b079030f894cd9412faa00eb73bd5cc3cc80b..4cdb84ba6242595a5e0e4765a16497aad53a56f3
+++ b/Makefile
@@@ -98,8 -98,6 +98,8 @@@ all:
  # Define OLD_ICONV if your library has an old iconv(), where the second
  # (input buffer pointer) parameter is declared with type (const char **).
  #
 +# Define NO_DEFLATE_BOUND if your zlib does not have deflateBound.
 +#
  # Define NO_R_TO_GCC_LINKER if your gcc does not like "-R/path/lib"
  # that tells runtime paths to dynamic libraries;
  # "-Wl,-rpath=/path/lib" is used instead.
  # times (my ext3 doesn't).
  #
  # Define USE_STDEV below if you want git to care about the underlying device
 -# change being considered an inode change from the update-cache perspective.
 +# change being considered an inode change from the update-index perspective.
  #
  # Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8
  #
 +# Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72.
 +#
  # Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
  # MakeMaker (e.g. using ActiveState under Cygwin).
  #
@@@ -154,7 -150,6 +154,7 @@@ STRIP ?= stri
  
  prefix = $(HOME)
  bindir = $(prefix)/bin
 +mandir = $(prefix)/share/man
  gitexecdir = $(bindir)
  sharedir = $(prefix)/share
  template_dir = $(sharedir)/git-core/templates
@@@ -214,14 -209,15 +214,14 @@@ BASIC_LDFLAGS 
  
  SCRIPT_SH = \
        git-bisect.sh git-checkout.sh \
 -      git-clean.sh git-clone.sh git-commit.sh \
 -      git-ls-remote.sh \
 +      git-clone.sh \
        git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
        git-pull.sh git-rebase.sh git-rebase--interactive.sh \
        git-repack.sh git-request-pull.sh \
        git-sh-setup.sh \
        git-am.sh \
        git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
 -      git-merge-resolve.sh git-merge-ours.sh \
 +      git-merge-resolve.sh \
        git-lost-found.sh git-quiltimport.sh git-submodule.sh \
        git-filter-branch.sh \
        git-stash.sh
@@@ -234,7 -230,7 +234,7 @@@ SCRIPT_PERL = 
  
  SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
          $(patsubst %.perl,%,$(SCRIPT_PERL)) \
 -        git-status git-instaweb
 +        git-instaweb
  
  # ... and all the rest that could be moved out of bindir to gitexecdir
  PROGRAMS = \
        git-fast-import$X \
        git-daemon$X \
        git-merge-index$X git-mktag$X git-mktree$X git-patch-id$X \
 -      git-peek-remote$X git-receive-pack$X \
 +      git-receive-pack$X \
        git-send-pack$X git-shell$X \
        git-show-index$X \
        git-unpack-file$X \
@@@ -260,7 -256,7 +260,7 @@@ EXTRA_PROGRAMS 
  BUILT_INS = \
        git-format-patch$X git-show$X git-whatchanged$X git-cherry$X \
        git-get-tar-commit-id$X git-init$X git-repo-config$X \
 -      git-fsck-objects$X git-cherry-pick$X \
 +      git-fsck-objects$X git-cherry-pick$X git-peek-remote$X git-status$X \
        $(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
  
  # what 'all' will build and 'install' will install, in gitexecdir
@@@ -270,6 -266,9 +270,6 @@@ ALL_PROGRAMS += git-merge-subtree$
  
  # what 'all' will build but not install in gitexecdir
  OTHER_PROGRAMS = git$X gitweb/gitweb.cgi
 -ifndef NO_TCLTK
 -OTHER_PROGRAMS += gitk-wish
 -endif
  
  # Set paths to tools early so that they can be used for version tests.
  ifndef SHELL_PATH
@@@ -300,7 -299,7 +300,7 @@@ DIFF_OBJS = 
  LIB_OBJS = \
        blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \
        date.o diff-delta.o entry.o exec_cmd.o ident.o \
 -      interpolate.o hash.o \
 +      pretty.o interpolate.o hash.o \
        lockfile.o \
        patch-ids.o \
        object.o pack-check.o pack-write.o patch-delta.o path.o pkt-line.o \
        alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
        color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
        convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \
-       transport.o bundle.o walker.o parse-options.o
+       transport.o bundle.o walker.o parse-options.o ws.o
  
  BUILTIN_OBJS = \
        builtin-add.o \
        builtin-check-attr.o \
        builtin-checkout-index.o \
        builtin-check-ref-format.o \
 +      builtin-clean.o \
 +      builtin-commit.o \
        builtin-commit-tree.o \
        builtin-count-objects.o \
        builtin-describe.o \
        builtin-diff-files.o \
        builtin-diff-index.o \
        builtin-diff-tree.o \
 +      builtin-fast-export.o \
        builtin-fetch.o \
        builtin-fetch-pack.o \
        builtin-fetch--tool.o \
        builtin-log.o \
        builtin-ls-files.o \
        builtin-ls-tree.o \
 +      builtin-ls-remote.o \
        builtin-mailinfo.o \
        builtin-mailsplit.o \
        builtin-merge-base.o \
        builtin-merge-file.o \
 +      builtin-merge-ours.o \
        builtin-mv.o \
        builtin-name-rev.o \
        builtin-pack-objects.o \
        builtin-push.o \
        builtin-read-tree.o \
        builtin-reflog.o \
 +      builtin-send-pack.o \
        builtin-config.o \
        builtin-rerere.o \
        builtin-reset.o \
        builtin-rev-parse.o \
        builtin-revert.o \
        builtin-rm.o \
 -      builtin-runstatus.o \
        builtin-shortlog.o \
        builtin-show-branch.o \
        builtin-stripspace.o \
@@@ -407,9 -401,7 +407,9 @@@ endi
  ifeq ($(uname_S),Darwin)
        NEEDS_SSL_WITH_CRYPTO = YesPlease
        NEEDS_LIBICONV = YesPlease
 -      OLD_ICONV = UnfortunatelyYes
 +      ifneq ($(shell expr "$(uname_R)" : '9\.'),2)
 +              OLD_ICONV = UnfortunatelyYes
 +      endif
        NO_STRLCPY = YesPlease
        NO_MEMMEM = YesPlease
  endif
@@@ -420,17 -412,18 +420,17 @@@ ifeq ($(uname_S),SunOS
        NO_STRCASESTR = YesPlease
        NO_MEMMEM = YesPlease
        NO_HSTRERROR = YesPlease
 +      NO_MKDTEMP = YesPlease
        ifeq ($(uname_R),5.8)
                NEEDS_LIBICONV = YesPlease
                NO_UNSETENV = YesPlease
                NO_SETENV = YesPlease
 -              NO_MKDTEMP = YesPlease
                NO_C99_FORMAT = YesPlease
                NO_STRTOUMAX = YesPlease
        endif
        ifeq ($(uname_R),5.9)
                NO_UNSETENV = YesPlease
                NO_SETENV = YesPlease
 -              NO_MKDTEMP = YesPlease
                NO_C99_FORMAT = YesPlease
                NO_STRTOUMAX = YesPlease
        endif
@@@ -447,7 -440,6 +447,7 @@@ ifeq ($(uname_O),Cygwin
        NEEDS_LIBICONV = YesPlease
        NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes
        NO_TRUSTABLE_FILEMODE = UnfortunatelyYes
 +      OLD_ICONV = UnfortunatelyYes
        # There are conflicting reports about this.
        # On some boxes NO_MMAP is needed, and not so elsewhere.
        # Try commenting this out if you suspect MMAP is more efficient
@@@ -669,10 -661,6 +669,10 @@@ ifdef OLD_ICON
        BASIC_CFLAGS += -DOLD_ICONV
  endif
  
 +ifdef NO_DEFLATE_BOUND
 +      BASIC_CFLAGS += -DNO_DEFLATE_BOUND
 +endif
 +
  ifdef PPC_SHA1
        SHA1_HEADER = "ppc/sha1.h"
        LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o
@@@ -747,7 -735,6 +747,7 @@@ ETC_GITCONFIG_SQ = $(subst ','\'',$(ETC
  
  DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
  bindir_SQ = $(subst ','\'',$(bindir))
 +mandir_SQ = $(subst ','\'',$(mandir))
  gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
  template_dir_SQ = $(subst ','\'',$(template_dir))
  prefix_SQ = $(subst ','\'',$(prefix))
@@@ -759,7 -746,7 +759,7 @@@ TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_
  LIBS = $(GITLIBS) $(EXTLIBS)
  
  BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' \
 -      -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"' $(COMPAT_CFLAGS)
 +      $(COMPAT_CFLAGS)
  LIB_OBJS += $(COMPAT_OBJS)
  
  ALL_CFLAGS += $(BASIC_CFLAGS)
@@@ -778,7 -765,6 +778,7 @@@ endi
  all::
  ifndef NO_TCLTK
        $(QUIET_SUBDIR0)git-gui $(QUIET_SUBDIR1) all
 +      $(QUIET_SUBDIR0)gitk-git $(QUIET_SUBDIR1) all
  endif
        $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all
        $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1)
  strip: $(PROGRAMS) git$X
        $(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X
  
 -gitk-wish: gitk GIT-GUI-VARS
 -      $(QUIET_GEN)$(RM) $@ $@+ && \
 -      sed -e '1,3s|^exec .* "$$0"|exec $(subst |,'\|',$(TCLTK_PATH_SQ)) "$$0"|' <gitk >$@+ && \
 -      chmod +x $@+ && \
 -      mv -f $@+ $@
 -
  git.o: git.c common-cmds.h GIT-CFLAGS
        $(QUIET_CC)$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \
                $(ALL_CFLAGS) -c $(filter %.c,$^)
@@@ -794,8 -786,7 +794,8 @@@ git$X: git.o $(BUILTIN_OBJS) $(GITLIBS
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
                $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
  
 -help.o: common-cmds.h
 +help.o: help.c common-cmds.h GIT-CFLAGS
 +      $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) '-DGIT_MAN_PATH="$(mandir_SQ)"' $<
  
  git-merge-subtree$X: git-merge-recursive$X
        $(QUIET_BUILT_IN)$(RM) $@ && ln git-merge-recursive$X $@
  $(BUILT_INS): git$X
        $(QUIET_BUILT_IN)$(RM) $@ && ln git$X $@
  
 -common-cmds.h: ./generate-cmdlist.sh
 +common-cmds.h: ./generate-cmdlist.sh command-list.txt
  
  common-cmds.h: $(wildcard Documentation/git-*.txt)
        $(QUIET_GEN)./generate-cmdlist.sh > $@+ && mv $@+ $@
@@@ -839,6 -830,9 +839,6 @@@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % 
        chmod +x $@+ && \
        mv $@+ $@
  
 -git-status: git-commit
 -      $(QUIET_GEN)cp $< $@+ && mv $@+ $@
 -
  gitweb/gitweb.cgi: gitweb/gitweb.perl
        $(QUIET_GEN)$(RM) $@ $@+ && \
        sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
@@@ -901,9 -895,6 +901,9 @@@ exec_cmd.o: exec_cmd.c GIT-CFLAG
  builtin-init-db.o: builtin-init-db.c GIT-CFLAGS
        $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $<
  
 +config.o: config.c GIT-CFLAGS
 +      $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"' $<
 +
  http.o: http.c GIT-CFLAGS
        $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DGIT_USER_AGENT='"git/$(GIT_VERSION)"' $<
  
@@@ -925,7 -916,6 +925,7 @@@ git-http-push$X: revision.o http.o http
  
  $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
  $(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
 +builtin-revert.o builtin-runstatus.o wt-status.o: wt-status.h
  
  $(LIB_FILE): $(LIB_OBJS)
        $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
@@@ -1001,8 -991,6 +1001,8 @@@ test-date$X: date.o ctype.
  
  test-delta$X: diff-delta.o patch-delta.o
  
 +test-parse-options$X: parse-options.o
 +
  .PRECIOUS: $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS))
  
  test-%$X: test-%.o $(GITLIBS)
@@@ -1020,14 -1008,14 +1020,14 @@@ remove-dashes
  ### Installation rules
  
  install: all
 -      $(INSTALL) -d -m755 '$(DESTDIR_SQ)$(bindir_SQ)'
 -      $(INSTALL) -d -m755 '$(DESTDIR_SQ)$(gitexecdir_SQ)'
 +      $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
 +      $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexecdir_SQ)'
        $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
        $(INSTALL) git$X '$(DESTDIR_SQ)$(bindir_SQ)'
        $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
        $(MAKE) -C perl prefix='$(prefix_SQ)' install
  ifndef NO_TCLTK
 -      $(INSTALL) gitk-wish '$(DESTDIR_SQ)$(bindir_SQ)'/gitk
 +      $(MAKE) -C gitk-git install
        $(MAKE) -C git-gui install
  endif
        if test 'z$(bindir_SQ)' != 'z$(gitexecdir_SQ)'; \
@@@ -1120,7 -1108,7 +1120,7 @@@ clean
        $(MAKE) -C templates/ clean
        $(MAKE) -C t/ clean
  ifndef NO_TCLTK
 -      $(RM) gitk-wish
 +      $(MAKE) -C gitk-git clean
        $(MAKE) -C git-gui clean
  endif
        $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS
  ### Check documentation
  #
  check-docs::
 -      @for v in $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk; \
 +      @(for v in $(ALL_PROGRAMS) $(BUILT_INS) git gitk; \
        do \
                case "$$v" in \
                git-merge-octopus | git-merge-ours | git-merge-recursive | \
 -              git-merge-resolve | git-merge-stupid | \
 +              git-merge-resolve | git-merge-stupid | git-merge-subtree | \
                git-add--interactive | git-fsck-objects | git-init-db | \
 +              git-rebase--interactive | \
                git-repo-config | git-fetch--tool ) continue ;; \
                esac ; \
                test -f "Documentation/$$v.txt" || \
                echo "no doc: $$v"; \
 -              sed -e '1,/^__DATA__/d' Documentation/cmd-list.perl | \
 +              sed -e '/^#/d' command-list.txt | \
                grep -q "^$$v[  ]" || \
                case "$$v" in \
                git) ;; \
                *) echo "no link: $$v";; \
                esac ; \
 -      done | sort
 +      done; \
 +      ( \
 +              sed -e '/^#/d' \
 +                  -e 's/[     ].*//' \
 +                  -e 's/^/listed /' command-list.txt; \
 +              ls -1 Documentation/git*txt | \
 +              sed -e 's|Documentation/|documented |' \
 +                  -e 's/\.txt//'; \
 +      ) | while read how cmd; \
 +      do \
 +              case "$$how,$$cmd" in \
 +              *,git-citool | \
 +              *,git-gui | \
 +              *,git-help | \
 +              documented,gitattributes | \
 +              documented,gitignore | \
 +              documented,gitmodules | \
 +              documented,git-tools | \
 +              sentinel,not,matching,is,ok ) continue ;; \
 +              esac; \
 +              case " $(ALL_PROGRAMS) $(BUILT_INS) git gitk " in \
 +              *" $$cmd "*)    ;; \
 +              *) echo "removed but $$how: $$cmd" ;; \
 +              esac; \
 +      done ) | sort
  
  ### Make sure built-ins do not have dups and listed in git.c
  #
diff --combined builtin-apply.c
index 91f8752ff7fdb588e64f594519a2e9503c516630,ee3ef60268540ef73402b8a86c7b96bd594828d7..f2e9a332ca0e148366f49b2825ed8826576f1bad
@@@ -45,14 -45,14 +45,14 @@@ static const char *fake_ancestor
  static int line_termination = '\n';
  static unsigned long p_context = ULONG_MAX;
  static const char apply_usage[] =
- "git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [--reverse] [--reject] [--verbose] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...";
- static enum whitespace_eol {
-       nowarn_whitespace,
-       warn_on_whitespace,
-       error_on_whitespace,
-       strip_whitespace,
- } new_whitespace = warn_on_whitespace;
+ "git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [--reverse] [--reject] [--verbose] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|fix|error|error-all>] <patch>...";
+ static enum ws_error_action {
+       nowarn_ws_error,
+       warn_on_ws_error,
+       die_on_ws_error,
+       correct_ws_error,
+ } ws_error_action = warn_on_ws_error;
  static int whitespace_error;
  static int squelch_whitespace_errors = 5;
  static int applied_after_fixing_ws;
@@@ -61,28 -61,28 +61,28 @@@ static const char *patch_input_file
  static void parse_whitespace_option(const char *option)
  {
        if (!option) {
-               new_whitespace = warn_on_whitespace;
+               ws_error_action = warn_on_ws_error;
                return;
        }
        if (!strcmp(option, "warn")) {
-               new_whitespace = warn_on_whitespace;
+               ws_error_action = warn_on_ws_error;
                return;
        }
        if (!strcmp(option, "nowarn")) {
-               new_whitespace = nowarn_whitespace;
+               ws_error_action = nowarn_ws_error;
                return;
        }
        if (!strcmp(option, "error")) {
-               new_whitespace = error_on_whitespace;
+               ws_error_action = die_on_ws_error;
                return;
        }
        if (!strcmp(option, "error-all")) {
-               new_whitespace = error_on_whitespace;
+               ws_error_action = die_on_ws_error;
                squelch_whitespace_errors = 0;
                return;
        }
-       if (!strcmp(option, "strip")) {
-               new_whitespace = strip_whitespace;
+       if (!strcmp(option, "strip") || !strcmp(option, "fix")) {
+               ws_error_action = correct_ws_error;
                return;
        }
        die("unrecognized whitespace option '%s'", option);
  
  static void set_default_whitespace_mode(const char *whitespace_option)
  {
-       if (!whitespace_option && !apply_default_whitespace) {
-               new_whitespace = (apply
-                                 ? warn_on_whitespace
-                                 : nowarn_whitespace);
-       }
+       if (!whitespace_option && !apply_default_whitespace)
+               ws_error_action = (apply ? warn_on_ws_error : nowarn_ws_error);
  }
  
  /*
@@@ -137,11 -134,17 +134,17 @@@ struct fragment 
  #define BINARY_DELTA_DEFLATED 1
  #define BINARY_LITERAL_DEFLATED 2
  
+ /*
+  * This represents a "patch" to a file, both metainfo changes
+  * such as creation/deletion, filemode and content changes represented
+  * as a series of fragments.
+  */
  struct patch {
        char *new_name, *old_name, *def_name;
        unsigned int old_mode, new_mode;
        int is_new, is_delete;  /* -1 = unknown, 0 = false, 1 = true */
        int rejected;
+       unsigned ws_rule;
        unsigned long deflate_origlen;
        int lines_added, lines_deleted;
        int score;
        struct patch *next;
  };
  
- static void say_patch_name(FILE *output, const char *pre, struct patch *patch, const char *post)
+ static void say_patch_name(FILE *output, const char *pre,
+                          struct patch *patch, const char *post)
  {
        fputs(pre, output);
        if (patch->old_name && patch->new_name &&
@@@ -229,7 -233,8 +233,8 @@@ static char *find_name(const char *line
        if (*line == '"') {
                struct strbuf name;
  
-               /* Proposed "new-style" GNU patch/diff format; see
+               /*
+                * Proposed "new-style" GNU patch/diff format; see
                 * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
                 */
                strbuf_init(&name, 0);
@@@ -499,7 -504,8 +504,8 @@@ static int gitdiff_dissimilarity(const 
  
  static int gitdiff_index(const char *line, struct patch *patch)
  {
-       /* index line is N hexadecimal, "..", N hexadecimal,
+       /*
+        * index line is N hexadecimal, "..", N hexadecimal,
         * and optional space with octal mode.
         */
        const char *ptr, *eol;
@@@ -550,7 -556,8 +556,8 @@@ static const char *stop_at_slash(const 
        return NULL;
  }
  
- /* This is to extract the same name that appears on "diff --git"
+ /*
+  * This is to extract the same name that appears on "diff --git"
   * line.  We do not find and return anything if it is a rename
   * patch, and it is OK because we will find the name elsewhere.
   * We need to reliably find name only when it is mode-change only,
@@@ -584,7 -591,8 +591,8 @@@ static char *git_header_name(char *line
                        goto free_and_fail1;
                strbuf_remove(&first, 0, cp + 1 - first.buf);
  
-               /* second points at one past closing dq of name.
+               /*
+                * second points at one past closing dq of name.
                 * find the second name.
                 */
                while ((second < line + llen) && isspace(*second))
                return NULL;
        name++;
  
-       /* since the first name is unquoted, a dq if exists must be
+       /*
+        * since the first name is unquoted, a dq if exists must be
         * the beginning of the second name.
         */
        for (second = name; second < line + llen; second++) {
                        }
                }
        }
 -      return NULL;
  }
  
  /* Verify that we recognize the lines following a git header */
@@@ -758,7 -768,7 +767,7 @@@ static int parse_num(const char *line, 
  }
  
  static int parse_range(const char *line, int len, int offset, const char *expect,
-                       unsigned long *p1, unsigned long *p2)
+                      unsigned long *p1, unsigned long *p2)
  {
        int digits, ex;
  
@@@ -867,14 -877,14 +876,14 @@@ static int find_header(char *line, unsi
                        return offset;
                }
  
-               /** --- followed by +++ ? */
+               /* --- followed by +++ ? */
                if (memcmp("--- ", line,  4) || memcmp("+++ ", line + len, 4))
                        continue;
  
                /*
                 * We only accept unified patches, so we want it to
                 * at least have "@@ -a,b +c,d @@\n", which is 14 chars
-                * minimum
+                * minimum ("@@ -0,0 +1 @@\n" is the shortest).
                 */
                nextlen = linelen(line + len, size - len);
                if (size < nextlen + 14 || memcmp("@@ -", line + len + nextlen, 4))
        return -1;
  }
  
- static void check_whitespace(const char *line, int len)
+ static void check_whitespace(const char *line, int len, unsigned ws_rule)
  {
        const char *err = "Adds trailing whitespace";
        int seen_space = 0;
         * this function.  That is, an addition of an empty line would
         * check the '+' here.  Sneaky...
         */
-       if (isspace(line[len-2]))
+       if ((ws_rule & WS_TRAILING_SPACE) && isspace(line[len-2]))
                goto error;
  
        /*
         * Make sure that there is no space followed by a tab in
         * indentation.
         */
-       err = "Space in indent is followed by a tab";
-       for (i = 1; i < len; i++) {
-               if (line[i] == '\t') {
-                       if (seen_space)
-                               goto error;
-               }
-               else if (line[i] == ' ')
-                       seen_space = 1;
-               else
-                       break;
+       if (ws_rule & WS_SPACE_BEFORE_TAB) {
+               err = "Space in indent is followed by a tab";
+               for (i = 1; i < len; i++) {
+                       if (line[i] == '\t') {
+                               if (seen_space)
+                                       goto error;
+                       }
+                       else if (line[i] == ' ')
+                               seen_space = 1;
+                       else
+                               break;
+               }
+       }
+       /*
+        * Make sure that the indentation does not contain more than
+        * 8 spaces.
+        */
+       if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
+           (8 < len) && !strncmp("+        ", line, 9)) {
+               err = "Indent more than 8 places with spaces";
+               goto error;
        }
        return;
  
                        err, patch_input_file, linenr, len-2, line+1);
  }
  
  /*
   * Parse a unified diff. Note that this really needs to parse each
   * fragment separately, since the only way to know the difference
   * between a "---" that is part of a patch, and a "---" that starts
   * the next patch is to look at the line counts..
   */
- static int parse_fragment(char *line, unsigned long size, struct patch *patch, struct fragment *fragment)
+ static int parse_fragment(char *line, unsigned long size,
+                         struct patch *patch, struct fragment *fragment)
  {
        int added, deleted;
        int len = linelen(line, size), offset;
                        break;
                case '-':
                        if (apply_in_reverse &&
-                                       new_whitespace != nowarn_whitespace)
-                               check_whitespace(line, len);
+                           ws_error_action != nowarn_ws_error)
+                               check_whitespace(line, len, patch->ws_rule);
                        deleted++;
                        oldlines--;
                        trailing = 0;
                        break;
                case '+':
                        if (!apply_in_reverse &&
-                                       new_whitespace != nowarn_whitespace)
-                               check_whitespace(line, len);
+                           ws_error_action != nowarn_ws_error)
+                               check_whitespace(line, len, patch->ws_rule);
                        added++;
                        newlines--;
                        trailing = 0;
                        break;
  
-                 /* We allow "\ No newline at end of file". Depending
+               /*
+                * We allow "\ No newline at end of file". Depending
                   * on locale settings when the patch was produced we
                   * don't know what this line looks like. The only
                   * thing we do know is that it begins with "\ ".
        fragment->leading = leading;
        fragment->trailing = trailing;
  
-       /* If a fragment ends with an incomplete line, we failed to include
+       /*
+        * If a fragment ends with an incomplete line, we failed to include
         * it in the above loop because we hit oldlines == newlines == 0
         * before seeing it.
         */
@@@ -1140,7 -1164,8 +1163,8 @@@ static struct fragment *parse_binary_hu
                                          int *status_p,
                                          int *used_p)
  {
-       /* Expect a line that begins with binary patch method ("literal"
+       /*
+        * Expect a line that begins with binary patch method ("literal"
         * or "delta"), followed by the length of data before deflating.
         * a sequence of 'length-byte' followed by base-85 encoded data
         * should follow, terminated by a newline.
                        size--;
                        break;
                }
-               /* Minimum line is "A00000\n" which is 7-byte long,
+               /*
+                * Minimum line is "A00000\n" which is 7-byte long,
                 * and the line length must be multiple of 5 plus 2.
                 */
                if ((llen < 7) || (llen-2) % 5)
  
  static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
  {
-       /* We have read "GIT binary patch\n"; what follows is a line
+       /*
+        * We have read "GIT binary patch\n"; what follows is a line
         * that says the patch method (currently, either "literal" or
         * "delta") and the length of data before deflating; a
         * sequence of 'length-byte' followed by base-85 encoded data
        if (reverse)
                used += used_1;
        else if (status) {
-               /* not having reverse hunk is not an error, but having
+               /*
+                * Not having reverse hunk is not an error, but having
                 * a corrupt reverse hunk is.
                 */
                free((void*) forward->patch);
@@@ -1291,7 -1319,12 +1318,12 @@@ static int parse_chunk(char *buffer, un
        if (offset < 0)
                return offset;
  
-       patchsize = parse_single_patch(buffer + offset + hdrsize, size - offset - hdrsize, patch);
+       patch->ws_rule = whitespace_rule(patch->new_name
+                                        ? patch->new_name
+                                        : patch->old_name);
+       patchsize = parse_single_patch(buffer + offset + hdrsize,
+                                      size - offset - hdrsize, patch);
  
        if (!patchsize) {
                static const char *binhdr[] = {
@@@ -1367,8 -1400,10 +1399,10 @@@ static void reverse_patches(struct patc
        }
  }
  
- static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
- static const char minuses[]= "----------------------------------------------------------------------";
+ static const char pluses[] =
+ "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
+ static const char minuses[]=
+ "----------------------------------------------------------------------";
  
  static void show_stats(struct patch *patch)
  {
@@@ -1437,7 -1472,9 +1471,9 @@@ static int read_old_data(struct stat *s
        }
  }
  
- static int find_offset(const char *buf, unsigned long size, const char *fragment, unsigned long fragsize, int line, int *lines)
+ static int find_offset(const char *buf, unsigned long size,
+                      const char *fragment, unsigned long fragsize,
+                      int line, int *lines)
  {
        int i;
        unsigned long start, backwards, forwards;
@@@ -1536,9 -1573,11 +1572,11 @@@ static void remove_last_line(const cha
        *rsize = offset + 1;
  }
  
- static int apply_line(char *output, const char *patch, int plen)
+ static int apply_line(char *output, const char *patch, int plen,
+                     unsigned ws_rule)
  {
-       /* plen is number of bytes to be copied from patch,
+       /*
+        * plen is number of bytes to be copied from patch,
         * starting at patch+1 (patch[0] is '+').  Typically
         * patch[plen] is '\n', unless this is the incomplete
         * last line.
        int need_fix_leading_space = 0;
        char *buf;
  
-       if ((new_whitespace != strip_whitespace) || !whitespace_error ||
+       if ((ws_error_action != correct_ws_error) || !whitespace_error ||
            *patch != '+') {
                memcpy(output, patch + 1, plen);
                return plen;
        }
  
-       if (1 < plen && isspace(patch[plen-1])) {
+       /*
+        * Strip trailing whitespace
+        */
+       if ((ws_rule & WS_TRAILING_SPACE) &&
+           (1 < plen && isspace(patch[plen-1]))) {
                if (patch[plen] == '\n')
                        add_nl_to_tail = 1;
                plen--;
                fixed = 1;
        }
  
+       /*
+        * Check leading whitespaces (indent)
+        */
        for (i = 1; i < plen; i++) {
                char ch = patch[i];
                if (ch == '\t') {
                        last_tab_in_indent = i;
-                       if (0 <= last_space_in_indent)
+                       if ((ws_rule & WS_SPACE_BEFORE_TAB) &&
+                           0 <= last_space_in_indent)
+                           need_fix_leading_space = 1;
+               } else if (ch == ' ') {
+                       last_space_in_indent = i;
+                       if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
+                           last_tab_in_indent < 0 &&
+                           8 <= i)
                                need_fix_leading_space = 1;
                }
-               else if (ch == ' ')
-                       last_space_in_indent = i;
                else
                        break;
        }
        buf = output;
        if (need_fix_leading_space) {
                int consecutive_spaces = 0;
-               /* between patch[1..last_tab_in_indent] strip the
-                * funny spaces, updating them to tab as needed.
+               int last = last_tab_in_indent + 1;
+               if (ws_rule & WS_INDENT_WITH_NON_TAB) {
+                       /* have "last" point at one past the indent */
+                       if (last_tab_in_indent < last_space_in_indent)
+                               last = last_space_in_indent + 1;
+                       else
+                               last = last_tab_in_indent + 1;
+               }
+               /*
+                * between patch[1..last], strip the funny spaces,
+                * updating them to tab as needed.
                 */
-               for (i = 1; i < last_tab_in_indent; i++, plen--) {
+               for (i = 1; i < last; i++, plen--) {
                        char ch = patch[i];
                        if (ch != ' ') {
                                consecutive_spaces = 0;
                                }
                        }
                }
+               while (0 < consecutive_spaces--)
+                       *output++ = ' ';
                fixed = 1;
-               i = last_tab_in_indent;
+               i = last;
        }
        else
                i = 1;
        return output + plen - buf;
  }
  
- static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int inaccurate_eof)
+ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
+                             int inaccurate_eof, unsigned ws_rule)
  {
        int match_beginning, match_end;
        const char *patch = frag->patch;
                case '+':
                        if (first != '+' || !no_add) {
                                int added = apply_line(new + newsize, patch,
-                                                      plen);
+                                                      plen, ws_rule);
                                newsize += added;
                                if (first == '+' &&
                                    added == 1 && new[newsize-1] == '\n')
                size -= len;
        }
  
-       if (inaccurate_eof && oldsize > 0 && old[oldsize - 1] == '\n' &&
-                       newsize > 0 && new[newsize - 1] == '\n') {
+       if (inaccurate_eof &&
+           oldsize > 0 && old[oldsize - 1] == '\n' &&
+           newsize > 0 && new[newsize - 1] == '\n') {
                oldsize--;
                newsize--;
        }
                if (match_beginning && offset)
                        offset = -1;
                if (offset >= 0) {
-                       if (new_whitespace == strip_whitespace &&
+                       if (ws_error_action == correct_ws_error &&
                            (buf->len - oldsize - offset == 0)) /* end of file? */
                                newsize -= new_blank_lines_at_end;
  
                        match_beginning = match_end = 0;
                        continue;
                }
-               /* Reduce the number of context lines
-                * Reduce both leading and trailing if they are equal
-                * otherwise just reduce the larger context.
+               /*
+                * Reduce the number of context lines; reduce both
+                * leading and trailing if they are equal otherwise
+                * just reduce the larger context.
                 */
                if (leading >= trailing) {
                        remove_first_line(&oldlines, &oldsize);
@@@ -1819,7 -1886,8 +1885,8 @@@ static int apply_binary(struct strbuf *
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
        unsigned char sha1[20];
  
-       /* For safety, we require patch index line to contain
+       /*
+        * For safety, we require patch index line to contain
         * full 40-byte textual SHA1 for old and new, at least for now.
         */
        if (strlen(patch->old_sha1_prefix) != 40 ||
                             "without full index line", name);
  
        if (patch->old_name) {
-               /* See if the old one matches what the patch
+               /*
+                * See if the old one matches what the patch
                 * applies to.
                 */
                hash_sha1_file(buf->buf, buf->len, blob_type, sha1);
                /* XXX read_sha1_file NUL-terminates */
                strbuf_attach(buf, result, size, size + 1);
        } else {
-               /* We have verified buf matches the preimage;
+               /*
+                * We have verified buf matches the preimage;
                 * apply the patch data to it, which is stored
                 * in the patch->fragments->{patch,size}.
                 */
@@@ -1889,12 -1959,14 +1958,14 @@@ static int apply_fragments(struct strbu
  {
        struct fragment *frag = patch->fragments;
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
+       unsigned ws_rule = patch->ws_rule;
+       unsigned inaccurate_eof = patch->inaccurate_eof;
  
        if (patch->is_binary)
                return apply_binary(buf, patch);
  
        while (frag) {
-               if (apply_one_fragment(buf, frag, patch->inaccurate_eof)) {
+               if (apply_one_fragment(buf, frag, inaccurate_eof, ws_rule)) {
                        error("patch failed: %s:%ld", name, frag->oldpos);
                        if (!apply_with_reject)
                                return -1;
@@@ -1992,7 -2064,7 +2063,7 @@@ static int verify_index_match(struct ca
                        return -1;
                return 0;
        }
 -      return ce_match_stat(ce, st, 1);
 +      return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID);
  }
  
  static int check_patch(struct patch *patch, struct patch *prev_patch)
  
        if (new_name && prev_patch && 0 < prev_patch->is_delete &&
            !strcmp(prev_patch->old_name, new_name))
-               /* A type-change diff is always split into a patch to
+               /*
+                * A type-change diff is always split into a patch to
                 * delete old, immediately followed by a patch to
                 * create new (see diff.c::run_diff()); in such a case
                 * it is Ok that the entry to be deleted by the
@@@ -2670,7 -2743,7 +2742,7 @@@ static int apply_patch(int fd, const ch
                offset += nr;
        }
  
-       if (whitespace_error && (new_whitespace == error_on_whitespace))
+       if (whitespace_error && (ws_error_action == die_on_ws_error))
                apply = 0;
  
        update_index = check_index && apply;
@@@ -2865,7 -2938,7 +2937,7 @@@ int cmd_apply(int argc, const char **ar
                                squelched,
                                squelched == 1 ? "" : "s");
                }
-               if (new_whitespace == error_on_whitespace)
+               if (ws_error_action == die_on_ws_error)
                        die("%d line%s add%s whitespace errors.",
                            whitespace_error,
                            whitespace_error == 1 ? "" : "s",
diff --combined cache.h
index c6142e9b7a8effbe31b5dcca3035d32138de84cf,9cc6268d4531f17703268f49057e4c9f284f0331..1bcb3df7a20b7eb90c15807b92ae0e016266d5fd
+++ b/cache.h
@@@ -7,7 -7,7 +7,7 @@@
  #include SHA1_HEADER
  #include <zlib.h>
  
 -#if ZLIB_VERNUM < 0x1200
 +#if defined(NO_DEFLATE_BOUND) || ZLIB_VERNUM < 0x1200
  #define deflateBound(c,s)  ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11)
  #endif
  
@@@ -175,8 -175,8 +175,8 @@@ extern struct index_state the_index
  #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
  #define add_file_to_cache(path, verbose) add_file_to_index(&the_index, (path), (verbose))
  #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL)
 -#define ce_match_stat(ce, st, really) ie_match_stat(&the_index, (ce), (st), (really))
 -#define ce_modified(ce, st, really) ie_modified(&the_index, (ce), (st), (really))
 +#define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
 +#define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
  #endif
  
  enum object_type {
        OBJ_MAX,
  };
  
 +static inline enum object_type object_type(unsigned int mode)
 +{
 +      return S_ISDIR(mode) ? OBJ_TREE :
 +              S_ISGITLINK(mode) ? OBJ_COMMIT :
 +              OBJ_BLOB;
 +}
 +
  #define GIT_DIR_ENVIRONMENT "GIT_DIR"
  #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
  #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
@@@ -229,7 -222,6 +229,7 @@@ extern const char *get_git_work_tree(vo
  #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
  
  extern const char **get_pathspec(const char *prefix, const char **pathspec);
 +extern void setup_work_tree(void);
  extern const char *setup_git_directory_gently(int *);
  extern const char *setup_git_directory(void);
  extern const char *prefix_path(const char *prefix, int len, const char *path);
@@@ -275,14 -267,8 +275,14 @@@ extern int remove_file_from_index(struc
  extern int add_file_to_index(struct index_state *, const char *path, int verbose);
  extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
  extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
 -extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, int);
 -extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, int);
 +
 +/* do stat comparison even if CE_VALID is true */
 +#define CE_MATCH_IGNORE_VALID         01
 +/* do not check the contents but report dirty on racily-clean entries */
 +#define CE_MATCH_RACY_IS_DIRTY        02
 +extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, unsigned int);
 +extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, unsigned int);
 +
  extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
  extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
  extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
@@@ -297,7 -283,6 +297,7 @@@ extern int refresh_index(struct index_s
  
  struct lock_file {
        struct lock_file *next;
 +      int fd;
        pid_t owner;
        char on_list;
        char filename[PATH_MAX];
@@@ -423,10 -408,6 +423,10 @@@ extern const char *resolve_ref(const ch
  extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
  extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
  
 +extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
 +extern const char *ref_rev_parse_rules[];
 +extern const char *ref_fetch_rules[];
 +
  extern int create_symref(const char *ref, const char *refs_heads_master, const char *logmsg);
  extern int validate_headref(const char *ref);
  
@@@ -453,13 -434,9 +453,13 @@@ void datestamp(char *buf, int bufsize)
  unsigned long approxidate(const char *);
  enum date_mode parse_date_format(const char *format);
  
 +#define IDENT_WARN_ON_NO_NAME  1
 +#define IDENT_ERROR_ON_NO_NAME 2
 +#define IDENT_NO_DATE        4
  extern const char *git_author_info(int);
  extern const char *git_committer_info(int);
  extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
 +extern const char *fmt_name(const char *name, const char *email);
  
  struct checkout {
        const char *base_dir;
@@@ -515,20 -492,8 +515,20 @@@ struct ref 
        struct ref *next;
        unsigned char old_sha1[20];
        unsigned char new_sha1[20];
 -      unsigned char force;
 -      unsigned char merge;
 +      unsigned int force:1,
 +              merge:1,
 +              nonfastforward:1,
 +              deletion:1;
 +      enum {
 +              REF_STATUS_NONE = 0,
 +              REF_STATUS_OK,
 +              REF_STATUS_REJECT_NONFASTFORWARD,
 +              REF_STATUS_REJECT_NODELETE,
 +              REF_STATUS_UPTODATE,
 +              REF_STATUS_REMOTE_REJECT,
 +              REF_STATUS_EXPECTING_REPORT,
 +      } status;
 +      char *remote_status;
        struct ref *peer_ref; /* when renaming */
        char name[FLEX_ARRAY]; /* more */
  };
  #define REF_HEADS     (1u << 1)
  #define REF_TAGS      (1u << 2)
  
 +extern struct ref *find_ref_by_name(struct ref *list, const char *name);
 +
  #define CONNECT_VERBOSE       (1u << 0)
 -extern struct child_process *git_connect(int fd[2], char *url, const char *prog, int flags);
 +extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
  extern int finish_connect(struct child_process *conn);
  extern int path_match(const char *path, int nr, char **match);
  extern int get_ack(int fd, unsigned char *result_sha1);
@@@ -586,7 -549,6 +586,7 @@@ extern int git_config_bool(const char *
  extern int git_config_set(const char *, const char *);
  extern int git_config_set_multivar(const char *, const char *, const char *, int);
  extern int git_config_rename_section(const char *, const char *);
 +extern const char *git_etc_gitconfig(void);
  extern int check_repository_format_version(const char *var, const char *value);
  
  #define MAX_GITNAME (1000)
@@@ -612,7 -574,6 +612,7 @@@ extern int pager_in_use
  extern int pager_use_color;
  
  extern char *editor_program;
 +extern char *excludes_file;
  
  /* base85 */
  int decode_85(char *dst, const char *line, int linelen);
@@@ -628,25 -589,29 +628,37 @@@ extern void alloc_report(void)
  
  /* trace.c */
  extern void trace_printf(const char *format, ...);
 -extern void trace_argv_printf(const char **argv, int count, const char *format, ...);
 +extern void trace_argv_printf(const char **argv, const char *format, ...);
  
  /* convert.c */
  /* returns 1 if *dst was used */
  extern int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst);
  extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
  
 +/* add */
 +void add_files_to_cache(int verbose, const char *prefix, const char **pathspec);
 +
  /* diff.c */
  extern int diff_auto_refresh_index;
  
  /* match-trees.c */
  void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int);
  
+ /*
+  * whitespace rules.
+  * used by both diff and apply
+  */
+ #define WS_TRAILING_SPACE     01
+ #define WS_SPACE_BEFORE_TAB   02
+ #define WS_INDENT_WITH_NON_TAB        04
+ #define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
+ extern unsigned whitespace_rule_cfg;
+ extern unsigned whitespace_rule(const char *);
+ extern unsigned parse_whitespace_rule(const char *);
 +/* ls-files */
 +int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen);
 +int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
 +void overlay_tree_on_cache(const char *tree_name, const char *prefix);
 +
  #endif /* CACHE_H */
diff --combined config.c
index 15b3d07349b246d3a46721c5d84c65a2093d9ad3,2500e0d59a6b772966e07c8b9ad15f36cc8000d4..49d2b427e52008e612b534f0c567e9fadaeab6e3
+++ b/config.c
@@@ -6,7 -6,6 +6,7 @@@
   *
   */
  #include "cache.h"
 +#include "exec_cmd.h"
  
  #define MAXNAME (256)
  
@@@ -432,13 -431,11 +432,18 @@@ int git_default_config(const char *var
                return 0;
        }
  
 +      if (!strcmp(var, "core.excludesfile")) {
 +              if (!value)
 +                      die("core.excludesfile without value");
 +              excludes_file = xstrdup(value);
 +              return 0;
 +      }
 +
+       if (!strcmp(var, "core.whitespace")) {
+               whitespace_rule_cfg = parse_whitespace_rule(value);
+               return 0;
+       }
        /* Add other config variables here and to Documentation/config.txt. */
        return 0;
  }
@@@ -460,21 -457,6 +465,21 @@@ int git_config_from_file(config_fn_t fn
        return ret;
  }
  
 +const char *git_etc_gitconfig(void)
 +{
 +      static const char *system_wide;
 +      if (!system_wide) {
 +              system_wide = ETC_GITCONFIG;
 +              if (!is_absolute_path(system_wide)) {
 +                      /* interpret path relative to exec-dir */
 +                      const char *exec_path = git_exec_path();
 +                      system_wide = prefix_path(exec_path, strlen(exec_path),
 +                                              system_wide);
 +              }
 +      }
 +      return system_wide;
 +}
 +
  int git_config(config_fn_t fn)
  {
        int ret = 0;
         * config file otherwise. */
        filename = getenv(CONFIG_ENVIRONMENT);
        if (!filename) {
 -              if (!access(ETC_GITCONFIG, R_OK))
 -                      ret += git_config_from_file(fn, ETC_GITCONFIG);
 +              if (!access(git_etc_gitconfig(), R_OK))
 +                      ret += git_config_from_file(fn, git_etc_gitconfig());
                home = getenv("HOME");
                filename = getenv(CONFIG_LOCAL_ENVIRONMENT);
                if (!filename)
@@@ -646,19 -628,13 +651,19 @@@ static int store_write_pair(int fd, con
        int length = strlen(key+store.baselen+1);
        int quote = 0;
  
 -      /* Check to see if the value needs to be quoted. */
 +      /*
 +       * Check to see if the value needs to be surrounded with a dq pair.
 +       * Note that problematic characters are always backslash-quoted; this
 +       * check is about not losing leading or trailing SP and strings that
 +       * follow beginning-of-comment characters (i.e. ';' and '#') by the
 +       * configuration parser.
 +       */
        if (value[0] == ' ')
                quote = 1;
        for (i = 0; value[i]; i++)
                if (value[i] == ';' || value[i] == '#')
                        quote = 1;
 -      if (value[i-1] == ' ')
 +      if (i && value[i-1] == ' ')
                quote = 1;
  
        if (write_in_full(fd, "\t", 1) != 1 ||
diff --combined diff.c
index be6cf687a4421acdc127df73aafef4aa9cf4daac,c3a1942bbb7da298cfbe2cb23c97fe361c9400ae..f780e3e8e67f4b1e07ef1d70ab3b9f101ff6b868
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -146,7 -146,7 +146,7 @@@ int git_diff_ui_config(const char *var
                return 0;
        }
        if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
 -              diff_use_color_default = git_config_colorbool(var, value);
 +              diff_use_color_default = git_config_colorbool(var, value, -1);
                return 0;
        }
        if (!strcmp(var, "diff.renames")) {
@@@ -454,6 -454,7 +454,7 @@@ static void diff_words_show(struct diff
  struct emit_callback {
        struct xdiff_emit_state xm;
        int nparents, color_diff;
+       unsigned ws_rule;
        const char **label_path;
        struct diff_words_data *diff_words;
        int *found_changesp;
@@@ -493,8 -494,8 +494,8 @@@ static void emit_line(const char *set, 
  }
  
  static void emit_line_with_ws(int nparents,
-               const char *set, const char *reset, const char *ws,
-               const char *line, int len)
+                             const char *set, const char *reset, const char *ws,
+                             const char *line, int len, unsigned ws_rule)
  {
        int col0 = nparents;
        int last_tab_in_indent = -1;
        int i;
        int tail = len;
        int need_highlight_leading_space = 0;
-       /* The line is a newly added line.  Does it have funny leading
-        * whitespaces?  In indent, SP should never precede a TAB.
+       /*
+        * The line is a newly added line.  Does it have funny leading
+        * whitespaces?  In indent, SP should never precede a TAB.  In
+        * addition, under "indent with non tab" rule, there should not
+        * be more than 8 consecutive spaces.
         */
        for (i = col0; i < len; i++) {
                if (line[i] == '\t') {
                        last_tab_in_indent = i;
-                       if (0 <= last_space_in_indent)
+                       if ((ws_rule & WS_SPACE_BEFORE_TAB) &&
+                           0 <= last_space_in_indent)
                                need_highlight_leading_space = 1;
                }
                else if (line[i] == ' ')
                else
                        break;
        }
+       if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
+           0 <= last_space_in_indent &&
+           last_tab_in_indent < 0 &&
+           8 <= (i - col0)) {
+               last_tab_in_indent = i;
+               need_highlight_leading_space = 1;
+       }
        fputs(set, stdout);
        fwrite(line, col0, 1, stdout);
        fputs(reset, stdout);
        tail = len - 1;
        if (line[tail] == '\n' && i < tail)
                tail--;
-       while (i < tail) {
-               if (!isspace(line[tail]))
-                       break;
-               tail--;
+       if (ws_rule & WS_TRAILING_SPACE) {
+               while (i < tail) {
+                       if (!isspace(line[tail]))
+                               break;
+                       tail--;
+               }
        }
        if ((i < tail && line[tail + 1] != '\n')) {
                /* This has whitespace between tail+1..len */
@@@ -565,7 -579,7 +579,7 @@@ static void emit_add_line(const char *r
                emit_line(set, reset, line, len);
        else
                emit_line_with_ws(ecbdata->nparents, set, reset, ws,
-                               line, len);
+                                 line, len, ecbdata->ws_rule);
  }
  
  static void fn_out_consume(void *priv, char *line, unsigned long len)
@@@ -814,10 -828,10 +828,10 @@@ static void show_stats(struct diffstat_
        }
  
        /* Find the longest filename and max number of changes */
 -      reset = diff_get_color(options->color_diff, DIFF_RESET);
 -      set = diff_get_color(options->color_diff, DIFF_PLAIN);
 -      add_c = diff_get_color(options->color_diff, DIFF_FILE_NEW);
 -      del_c = diff_get_color(options->color_diff, DIFF_FILE_OLD);
 +      reset = diff_get_color_opt(options, DIFF_RESET);
 +      set   = diff_get_color_opt(options, DIFF_PLAIN);
 +      add_c = diff_get_color_opt(options, DIFF_FILE_NEW);
 +      del_c = diff_get_color_opt(options, DIFF_FILE_OLD);
  
        for (i = 0; i < data->nr; i++) {
                struct diffstat_file *file = data->files[i];
@@@ -981,6 -995,7 +995,7 @@@ struct checkdiff_t 
        struct xdiff_emit_state xm;
        const char *filename;
        int lineno, color_diff;
+       unsigned ws_rule;
  };
  
  static void checkdiff_consume(void *priv, char *line, unsigned long len)
                        if (white_space_at_end)
                                printf("white space at end");
                        printf(":%s ", reset);
-                       emit_line_with_ws(1, set, reset, ws, line, len);
+                       emit_line_with_ws(1, set, reset, ws, line, len,
+                                         data->ws_rule);
                }
  
                data->lineno++;
@@@ -1243,8 -1259,8 +1259,8 @@@ static void builtin_diff(const char *na
        mmfile_t mf1, mf2;
        const char *lbl[2];
        char *a_one, *b_two;
 -      const char *set = diff_get_color(o->color_diff, DIFF_METAINFO);
 -      const char *reset = diff_get_color(o->color_diff, DIFF_RESET);
 +      const char *set = diff_get_color_opt(o, DIFF_METAINFO);
 +      const char *reset = diff_get_color_opt(o, DIFF_RESET);
  
        a_one = quote_two("a/", name_a + (*name_a == '/'));
        b_two = quote_two("b/", name_b + (*name_b == '/'));
                        goto free_ab_and_return;
                if (complete_rewrite) {
                        emit_rewrite_diff(name_a, name_b, one, two,
 -                                      o->color_diff);
 +                                      DIFF_OPT_TST(o, COLOR_DIFF));
                        o->found_changes = 1;
                        goto free_ab_and_return;
                }
        if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
                die("unable to read files to diff");
  
 -      if (!o->text &&
 +      if (!DIFF_OPT_TST(o, TEXT) &&
            (diff_filespec_is_binary(one) || diff_filespec_is_binary(two))) {
                /* Quite common confusing case */
                if (mf1.size == mf2.size &&
                    !memcmp(mf1.ptr, mf2.ptr, mf1.size))
                        goto free_ab_and_return;
 -              if (o->binary)
 +              if (DIFF_OPT_TST(o, BINARY))
                        emit_binary_diff(&mf1, &mf2);
                else
                        printf("Binary files %s and %s differ\n",
                memset(&xecfg, 0, sizeof(xecfg));
                memset(&ecbdata, 0, sizeof(ecbdata));
                ecbdata.label_path = lbl;
 -              ecbdata.color_diff = o->color_diff;
 +              ecbdata.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
                ecbdata.found_changesp = &o->found_changes;
+               ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
                xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
                xecfg.ctxlen = o->context;
                xecfg.flags = XDL_EMIT_FUNCNAMES;
                ecb.outf = xdiff_outf;
                ecb.priv = &ecbdata;
                ecbdata.xm.consume = fn_out_consume;
 -              if (o->color_diff_words)
 +              if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))
                        ecbdata.diff_words =
                                xcalloc(1, sizeof(struct diff_words_data));
                xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
 -              if (o->color_diff_words)
 +              if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))
                        free_diff_words_data(&ecbdata);
        }
  
@@@ -1409,7 -1426,8 +1426,8 @@@ static void builtin_checkdiff(const cha
        data.xm.consume = checkdiff_consume;
        data.filename = name_b ? name_b : name_a;
        data.lineno = 0;
 -      data.color_diff = o->color_diff;
 +      data.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
+       data.ws_rule = whitespace_rule(data.filename);
  
        if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
                die("unable to read files to diff");
@@@ -1853,7 -1871,7 +1871,7 @@@ static void run_diff_cmd(const char *pg
                         struct diff_options *o,
                         int complete_rewrite)
  {
 -      if (!o->allow_external)
 +      if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
                pgm = NULL;
        else {
                const char *cmd = external_diff_attr(name);
@@@ -1951,9 -1969,9 +1969,9 @@@ static void run_diff(struct diff_filepa
        }
  
        if (hashcmp(one->sha1, two->sha1)) {
 -              int abbrev = o->full_index ? 40 : DEFAULT_ABBREV;
 +              int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
  
 -              if (o->binary) {
 +              if (DIFF_OPT_TST(o, BINARY)) {
                        mmfile_t mf;
                        if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) ||
                            (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
@@@ -2045,10 -2063,7 +2063,10 @@@ void diff_setup(struct diff_options *op
  
        options->change = diff_change;
        options->add_remove = diff_addremove;
 -      options->color_diff = diff_use_color_default;
 +      if (diff_use_color_default)
 +              DIFF_OPT_SET(options, COLOR_DIFF);
 +      else
 +              DIFF_OPT_CLR(options, COLOR_DIFF);
        options->detect_rename = diff_detect_rename_default;
  }
  
@@@ -2067,7 -2082,7 +2085,7 @@@ int diff_setup_done(struct diff_option
        if (count > 1)
                die("--name-only, --name-status, --check and -s are mutually exclusive");
  
 -      if (options->find_copies_harder)
 +      if (DIFF_OPT_TST(options, FIND_COPIES_HARDER))
                options->detect_rename = DIFF_DETECT_COPY;
  
        if (options->output_format & (DIFF_FORMAT_NAME |
                                      DIFF_FORMAT_SHORTSTAT |
                                      DIFF_FORMAT_SUMMARY |
                                      DIFF_FORMAT_CHECKDIFF))
 -              options->recursive = 1;
 +              DIFF_OPT_SET(options, RECURSIVE);
        /*
         * Also pickaxe would not work very well if you do not say recursive
         */
        if (options->pickaxe)
 -              options->recursive = 1;
 +              DIFF_OPT_SET(options, RECURSIVE);
  
        if (options->detect_rename && options->rename_limit < 0)
                options->rename_limit = diff_rename_limit_default;
         * to have found.  It does not make sense not to return with
         * exit code in such a case either.
         */
 -      if (options->quiet) {
 +      if (DIFF_OPT_TST(options, QUIET)) {
                options->output_format = DIFF_FORMAT_NO_OUTPUT;
 -              options->exit_with_status = 1;
 +              DIFF_OPT_SET(options, EXIT_WITH_STATUS);
        }
  
        /*
         * upon the first hit.  We need to run diff as usual.
         */
        if (options->pickaxe || options->filter)
 -              options->quiet = 0;
 +              DIFF_OPT_CLR(options, QUIET);
  
        return 0;
  }
@@@ -2185,32 -2200,21 +2203,32 @@@ static int diff_scoreopt_parse(const ch
  int diff_opt_parse(struct diff_options *options, const char **av, int ac)
  {
        const char *arg = av[0];
 +
 +      /* Output format options */
        if (!strcmp(arg, "-p") || !strcmp(arg, "-u"))
                options->output_format |= DIFF_FORMAT_PATCH;
        else if (opt_arg(arg, 'U', "unified", &options->context))
                options->output_format |= DIFF_FORMAT_PATCH;
        else if (!strcmp(arg, "--raw"))
                options->output_format |= DIFF_FORMAT_RAW;
 -      else if (!strcmp(arg, "--patch-with-raw")) {
 +      else if (!strcmp(arg, "--patch-with-raw"))
                options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_RAW;
 -      }
 -      else if (!strcmp(arg, "--numstat")) {
 +      else if (!strcmp(arg, "--numstat"))
                options->output_format |= DIFF_FORMAT_NUMSTAT;
 -      }
 -      else if (!strcmp(arg, "--shortstat")) {
 +      else if (!strcmp(arg, "--shortstat"))
                options->output_format |= DIFF_FORMAT_SHORTSTAT;
 -      }
 +      else if (!strcmp(arg, "--check"))
 +              options->output_format |= DIFF_FORMAT_CHECKDIFF;
 +      else if (!strcmp(arg, "--summary"))
 +              options->output_format |= DIFF_FORMAT_SUMMARY;
 +      else if (!strcmp(arg, "--patch-with-stat"))
 +              options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_DIFFSTAT;
 +      else if (!strcmp(arg, "--name-only"))
 +              options->output_format |= DIFF_FORMAT_NAME;
 +      else if (!strcmp(arg, "--name-status"))
 +              options->output_format |= DIFF_FORMAT_NAME_STATUS;
 +      else if (!strcmp(arg, "-s"))
 +              options->output_format |= DIFF_FORMAT_NO_OUTPUT;
        else if (!prefixcmp(arg, "--stat")) {
                char *end;
                int width = options->stat_width;
                options->stat_name_width = name_width;
                options->stat_width = width;
        }
 -      else if (!strcmp(arg, "--check"))
 -              options->output_format |= DIFF_FORMAT_CHECKDIFF;
 -      else if (!strcmp(arg, "--summary"))
 -              options->output_format |= DIFF_FORMAT_SUMMARY;
 -      else if (!strcmp(arg, "--patch-with-stat")) {
 -              options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_DIFFSTAT;
 -      }
 -      else if (!strcmp(arg, "-z"))
 -              options->line_termination = 0;
 -      else if (!prefixcmp(arg, "-l"))
 -              options->rename_limit = strtoul(arg+2, NULL, 10);
 -      else if (!strcmp(arg, "--full-index"))
 -              options->full_index = 1;
 -      else if (!strcmp(arg, "--binary")) {
 -              options->output_format |= DIFF_FORMAT_PATCH;
 -              options->binary = 1;
 -      }
 -      else if (!strcmp(arg, "-a") || !strcmp(arg, "--text")) {
 -              options->text = 1;
 -      }
 -      else if (!strcmp(arg, "--name-only"))
 -              options->output_format |= DIFF_FORMAT_NAME;
 -      else if (!strcmp(arg, "--name-status"))
 -              options->output_format |= DIFF_FORMAT_NAME_STATUS;
 -      else if (!strcmp(arg, "-R"))
 -              options->reverse_diff = 1;
 -      else if (!prefixcmp(arg, "-S"))
 -              options->pickaxe = arg + 2;
 -      else if (!strcmp(arg, "-s")) {
 -              options->output_format |= DIFF_FORMAT_NO_OUTPUT;
 -      }
 -      else if (!prefixcmp(arg, "-O"))
 -              options->orderfile = arg + 2;
 -      else if (!prefixcmp(arg, "--diff-filter="))
 -              options->filter = arg + 14;
 -      else if (!strcmp(arg, "--pickaxe-all"))
 -              options->pickaxe_opts = DIFF_PICKAXE_ALL;
 -      else if (!strcmp(arg, "--pickaxe-regex"))
 -              options->pickaxe_opts = DIFF_PICKAXE_REGEX;
 +
 +      /* renames options */
        else if (!prefixcmp(arg, "-B")) {
 -              if ((options->break_opt =
 -                   diff_scoreopt_parse(arg)) == -1)
 +              if ((options->break_opt = diff_scoreopt_parse(arg)) == -1)
                        return -1;
        }
        else if (!prefixcmp(arg, "-M")) {
 -              if ((options->rename_score =
 -                   diff_scoreopt_parse(arg)) == -1)
 +              if ((options->rename_score = diff_scoreopt_parse(arg)) == -1)
                        return -1;
                options->detect_rename = DIFF_DETECT_RENAME;
        }
        else if (!prefixcmp(arg, "-C")) {
                if (options->detect_rename == DIFF_DETECT_COPY)
 -                      options->find_copies_harder = 1;
 -              if ((options->rename_score =
 -                   diff_scoreopt_parse(arg)) == -1)
 +                      DIFF_OPT_SET(options, FIND_COPIES_HARDER);
 +              if ((options->rename_score = diff_scoreopt_parse(arg)) == -1)
                        return -1;
                options->detect_rename = DIFF_DETECT_COPY;
        }
 -      else if (!strcmp(arg, "--find-copies-harder"))
 -              options->find_copies_harder = 1;
 -      else if (!strcmp(arg, "--follow"))
 -              options->follow_renames = 1;
 -      else if (!strcmp(arg, "--abbrev"))
 -              options->abbrev = DEFAULT_ABBREV;
 -      else if (!prefixcmp(arg, "--abbrev=")) {
 -              options->abbrev = strtoul(arg + 9, NULL, 10);
 -              if (options->abbrev < MINIMUM_ABBREV)
 -                      options->abbrev = MINIMUM_ABBREV;
 -              else if (40 < options->abbrev)
 -                      options->abbrev = 40;
 -      }
 -      else if (!strcmp(arg, "--color"))
 -              options->color_diff = 1;
 -      else if (!strcmp(arg, "--no-color"))
 -              options->color_diff = 0;
 +      else if (!strcmp(arg, "--no-renames"))
 +              options->detect_rename = 0;
 +
 +      /* xdiff options */
        else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space"))
                options->xdl_opts |= XDF_IGNORE_WHITESPACE;
        else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change"))
                options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE;
        else if (!strcmp(arg, "--ignore-space-at-eol"))
                options->xdl_opts |= XDF_IGNORE_WHITESPACE_AT_EOL;
 +
 +      /* flags options */
 +      else if (!strcmp(arg, "--binary")) {
 +              options->output_format |= DIFF_FORMAT_PATCH;
 +              DIFF_OPT_SET(options, BINARY);
 +      }
 +      else if (!strcmp(arg, "--full-index"))
 +              DIFF_OPT_SET(options, FULL_INDEX);
 +      else if (!strcmp(arg, "-a") || !strcmp(arg, "--text"))
 +              DIFF_OPT_SET(options, TEXT);
 +      else if (!strcmp(arg, "-R"))
 +              DIFF_OPT_SET(options, REVERSE_DIFF);
 +      else if (!strcmp(arg, "--find-copies-harder"))
 +              DIFF_OPT_SET(options, FIND_COPIES_HARDER);
 +      else if (!strcmp(arg, "--follow"))
 +              DIFF_OPT_SET(options, FOLLOW_RENAMES);
 +      else if (!strcmp(arg, "--color"))
 +              DIFF_OPT_SET(options, COLOR_DIFF);
 +      else if (!strcmp(arg, "--no-color"))
 +              DIFF_OPT_CLR(options, COLOR_DIFF);
        else if (!strcmp(arg, "--color-words"))
 -              options->color_diff = options->color_diff_words = 1;
 -      else if (!strcmp(arg, "--no-renames"))
 -              options->detect_rename = 0;
 +              options->flags |= DIFF_OPT_COLOR_DIFF | DIFF_OPT_COLOR_DIFF_WORDS;
        else if (!strcmp(arg, "--exit-code"))
 -              options->exit_with_status = 1;
 +              DIFF_OPT_SET(options, EXIT_WITH_STATUS);
        else if (!strcmp(arg, "--quiet"))
 -              options->quiet = 1;
 +              DIFF_OPT_SET(options, QUIET);
        else if (!strcmp(arg, "--ext-diff"))
 -              options->allow_external = 1;
 +              DIFF_OPT_SET(options, ALLOW_EXTERNAL);
        else if (!strcmp(arg, "--no-ext-diff"))
 -              options->allow_external = 0;
 +              DIFF_OPT_CLR(options, ALLOW_EXTERNAL);
 +
 +      /* misc options */
 +      else if (!strcmp(arg, "-z"))
 +              options->line_termination = 0;
 +      else if (!prefixcmp(arg, "-l"))
 +              options->rename_limit = strtoul(arg+2, NULL, 10);
 +      else if (!prefixcmp(arg, "-S"))
 +              options->pickaxe = arg + 2;
 +      else if (!strcmp(arg, "--pickaxe-all"))
 +              options->pickaxe_opts = DIFF_PICKAXE_ALL;
 +      else if (!strcmp(arg, "--pickaxe-regex"))
 +              options->pickaxe_opts = DIFF_PICKAXE_REGEX;
 +      else if (!prefixcmp(arg, "-O"))
 +              options->orderfile = arg + 2;
 +      else if (!prefixcmp(arg, "--diff-filter="))
 +              options->filter = arg + 14;
 +      else if (!strcmp(arg, "--abbrev"))
 +              options->abbrev = DEFAULT_ABBREV;
 +      else if (!prefixcmp(arg, "--abbrev=")) {
 +              options->abbrev = strtoul(arg + 9, NULL, 10);
 +              if (options->abbrev < MINIMUM_ABBREV)
 +                      options->abbrev = MINIMUM_ABBREV;
 +              else if (40 < options->abbrev)
 +                      options->abbrev = 40;
 +      }
        else
                return 0;
        return 1;
@@@ -2716,7 -2730,7 +2734,7 @@@ static void diff_summary(struct diff_fi
                break;
        default:
                if (p->score) {
 -                      puts(" rewrite ");
 +                      fputs(" rewrite ", stdout);
                        write_name_quoted(p->two->path, stdout, ' ');
                        printf("(%d%%)\n", similarity_index(p));
                }
@@@ -3075,7 -3089,7 +3093,7 @@@ static void diffcore_skip_stat_unmatch(
                         * to determine how many paths were dirty only
                         * due to stat info mismatch.
                         */
 -                      if (!diffopt->no_index)
 +                      if (!DIFF_OPT_TST(diffopt, NO_INDEX))
                                diffopt->skip_stat_unmatch++;
                        diff_free_filepair(p);
                }
  
  void diffcore_std(struct diff_options *options)
  {
 -      if (options->quiet)
 +      if (DIFF_OPT_TST(options, QUIET))
                return;
  
 -      if (options->skip_stat_unmatch && !options->find_copies_harder)
 +      if (options->skip_stat_unmatch && !DIFF_OPT_TST(options, FIND_COPIES_HARDER))
                diffcore_skip_stat_unmatch(options);
        if (options->break_opt != -1)
                diffcore_break(options->break_opt);
        diff_resolve_rename_copy();
        diffcore_apply_filter(options->filter);
  
 -      options->has_changes = !!diff_queued_diff.nr;
 +      if (diff_queued_diff.nr)
 +              DIFF_OPT_SET(options, HAS_CHANGES);
 +      else
 +              DIFF_OPT_CLR(options, HAS_CHANGES);
  }
  
  
@@@ -3131,7 -3142,7 +3149,7 @@@ void diff_addremove(struct diff_option
         * Before the final output happens, they are pruned after
         * merged into rename/copy pairs as appropriate.
         */
 -      if (options->reverse_diff)
 +      if (DIFF_OPT_TST(options, REVERSE_DIFF))
                addremove = (addremove == '+' ? '-' :
                             addremove == '-' ? '+' : addremove);
  
                fill_filespec(two, sha1, mode);
  
        diff_queue(&diff_queued_diff, one, two);
 -      options->has_changes = 1;
 +      DIFF_OPT_SET(options, HAS_CHANGES);
  }
  
  void diff_change(struct diff_options *options,
        char concatpath[PATH_MAX];
        struct diff_filespec *one, *two;
  
 -      if (options->reverse_diff) {
 +      if (DIFF_OPT_TST(options, REVERSE_DIFF)) {
                unsigned tmp;
                const unsigned char *tmp_c;
                tmp = old_mode; old_mode = new_mode; new_mode = tmp;
        fill_filespec(two, new_sha1, new_mode);
  
        diff_queue(&diff_queued_diff, one, two);
 -      options->has_changes = 1;
 +      DIFF_OPT_SET(options, HAS_CHANGES);
  }
  
  void diff_unmerge(struct diff_options *options,
diff --combined environment.c
index 1dab72ec1525ca6dd15ca20666cd91506b85890c,2fbbc8e430cb6cd331f6835e07ede7c1e7d2a45a..f3e3d4138d463520dbe6d709dfbe0803b619456d
@@@ -34,8 -34,8 +34,9 @@@ char *pager_program
  int pager_in_use;
  int pager_use_color = 1;
  char *editor_program;
 +char *excludes_file;
  int auto_crlf = 0;    /* 1: both ways, -1: only when adding git objects */
+ unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
  
  /* This is set by setup_git_dir_gently() and/or git_default_config() */
  char *git_work_tree_cfg;