Merge branch 'jt/submodule-tests-cleanup'
authorJunio C Hamano <gitster@pobox.com>
Wed, 6 Dec 2017 17:23:38 +0000 (09:23 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 6 Dec 2017 17:23:38 +0000 (09:23 -0800)
Further test clean-up.

* jt/submodule-tests-cleanup:
  Tests: clean up submodule recursive helpers

214 files changed:
.mailmap
.travis.yml
Documentation/.gitignore
Documentation/Makefile
Documentation/RelNotes/2.15.0.txt
Documentation/RelNotes/2.15.1.txt [new file with mode: 0644]
Documentation/RelNotes/2.16.0.txt
Documentation/SubmittingPatches
Documentation/config.txt
Documentation/diff-options.txt
Documentation/git-add.txt
Documentation/git-bisect.txt
Documentation/git-branch.txt
Documentation/git-checkout.txt
Documentation/git-config.txt
Documentation/git-for-each-ref.txt
Documentation/git-ls-files.txt
Documentation/git-merge-base.txt
Documentation/git-status.txt
Documentation/git-update-index.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/githooks.txt
Documentation/merge-strategies.txt
Documentation/technical/api-directory-listing.txt
Documentation/technical/index-format.txt
Makefile
apply.c
bisect.c
bisect.h
blame.c
branch.c
branch.h
builtin/add.c
builtin/am.c
builtin/bisect--helper.c
builtin/blame.c
builtin/branch.c
builtin/checkout.c
builtin/clone.c
builtin/commit.c
builtin/config.c
builtin/diff.c
builtin/fast-export.c
builtin/fmt-merge-msg.c
builtin/fsck.c
builtin/log.c
builtin/ls-files.c
builtin/merge-base.c
builtin/merge-ours.c
builtin/merge.c
builtin/notes.c
builtin/pull.c
builtin/reflog.c
builtin/remote.c
builtin/reset.c
builtin/rev-list.c
builtin/symbolic-ref.c
builtin/update-index.c
builtin/update-ref.c
cache.h
ci/install-dependencies.sh
ci/lib-travisci.sh
combine-diff.c
commit.c
commit.h
compat/bswap.h
compat/mingw.c
compat/obstack.c
compat/obstack.h
compat/poll/poll.c
compat/poll/poll.h
compat/regex/regcomp.c
compat/regex/regex.c
compat/regex/regex.h
compat/regex/regex_internal.c
compat/regex/regex_internal.h
compat/regex/regexec.c
config.c
config.h
contrib/completion/git-completion.bash
contrib/credential/gnome-keyring/git-credential-gnome-keyring.c
contrib/credential/libsecret/git-credential-libsecret.c
contrib/credential/wincred/git-credential-wincred.c
contrib/emacs/git-blame.el
contrib/emacs/git.el
contrib/fast-import/import-directories.perl
contrib/git-jump/README
contrib/git-jump/git-jump
contrib/hg-to-git/hg-to-git.py
contrib/mw-to-git/Git/Mediawiki.pm
contrib/mw-to-git/git-remote-mediawiki.perl
diff-lib.c
diff-no-index.c
diff.c
diff.h
diffcore-pickaxe.c
diffcore-rename.c
dir.c
dir.h
entry.c
environment.c
ewah/bitmap.c
ewah/ewah_bitmap.c
ewah/ewah_io.c
ewah/ewah_rlw.c
ewah/ewok.h
ewah/ewok_rlw.h
fsmonitor.c [new file with mode: 0644]
fsmonitor.h [new file with mode: 0644]
generate-cmdlist.sh
git-bisect.sh
git-gui/Makefile
git-gui/git-gui.sh
git-rebase--am.sh
git-rebase--interactive.sh
git-rebase.sh
git.c
git.rc
grep.c
grep.h
hex.c
http-push.c
imap-send.c
kwset.c
kwset.h
log-tree.c
merge-recursive.c
notes-merge.c
notes.c
patch-ids.c
perl/Git/Packet.pm [new file with mode: 0644]
perl/Makefile
preload-index.c
read-cache.c
ref-filter.c
refs.c
refs.h
refs/files-backend.c
refs/packed-backend.c
refs/packed-backend.h
refs/ref-cache.c
refs/refs-internal.h
remote.c
remote.h
revision.c
sequencer.c
setup.c
sh-i18n--envsubst.c
sha1_file.c
sha1_name.c
submodule.c
t/helper/.gitignore
t/helper/test-date.c
t/helper/test-drop-caches.c [new file with mode: 0644]
t/helper/test-dump-fsmonitor.c [new file with mode: 0644]
t/lib-credential.sh
t/lib-gpg.sh
t/lib-submodule-update.sh
t/perf/aggregate.perl
t/perf/p7519-fsmonitor.sh [new file with mode: 0755]
t/perf/perf-lib.sh
t/perf/run
t/t0001-init.sh
t/t0021/rot13-filter.pl
t/t0025-crlf-renormalize.sh [new file with mode: 0755]
t/t1300-repo-config.sh
t/t1409-avoid-packing-refs.sh [new file with mode: 0755]
t/t1430-bad-ref-name.sh
t/t1700-split-index.sh
t/t3310-notes-merge-manual-resolve.sh
t/t3320-notes-merge-worktrees.sh
t/t3400-rebase.sh
t/t3426-rebase-submodule.sh
t/t3512-cherry-pick-submodule.sh
t/t3600-rm.sh
t/t4015-diff-whitespace.sh
t/t4051-diff-function-context.sh
t/t4051/hello.c
t/t4107-apply-ignore-whitespace.sh
t/t4201-shortlog.sh
t/t5580-clone-push-unc.sh
t/t6300-for-each-ref.sh
t/t7001-mv.sh
t/t7006-pager.sh
t/t7519-status-fsmonitor.sh [new file with mode: 0755]
t/t7519/fsmonitor-all [new file with mode: 0755]
t/t7519/fsmonitor-none [new file with mode: 0755]
t/t7519/fsmonitor-watchman [new file with mode: 0755]
t/t7521-ignored-mode.sh [new file with mode: 0755]
t/t7810-grep.sh
t/t9114-git-svn-dcommit-merge.sh
t/test-lib.sh
templates/hooks--fsmonitor-watchman.sample [new file with mode: 0755]
trace.c
tree-diff.c
unpack-trees.c
wrapper.c
wt-status.c
wt-status.h
xdiff/xdiff.h
xdiff/xdiffi.c
xdiff/xdiffi.h
xdiff/xemit.c
xdiff/xemit.h
xdiff/xinclude.h
xdiff/xmacros.h
xdiff/xmerge.c
xdiff/xpatience.c
xdiff/xprepare.c
xdiff/xprepare.h
xdiff/xtypes.h
xdiff/xutils.c
xdiff/xutils.h

index 224db83..7c71e88 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -113,6 +113,7 @@ Junio C Hamano <gitster@pobox.com> <junio@pobox.com>
 Junio C Hamano <gitster@pobox.com> <junio@twinsun.com>
 Junio C Hamano <gitster@pobox.com> <junkio@cox.net>
 Junio C Hamano <gitster@pobox.com> <junkio@twinsun.com>
+Kaartic Sivaraam <kaartic.sivaraam@gmail.com> <kaarticsivaraam91196@gmail.com>
 Karl Wiberg <kha@treskal.com> Karl  Hasselström
 Karl Wiberg <kha@treskal.com> <kha@yoghurt.hemma.treskal.com>
 Karsten Blees <blees@dcon.de> <karsten.blees@dcon.de>
index fead995..281f101 100644 (file)
@@ -71,7 +71,7 @@ matrix:
           packages:
           - coccinelle
       before_install:
-      # "before_script" that builds Git is inherited from base job
+      before_script:
       script: ci/run-static-analysis.sh
       after_failure:
     - env: Documentation
index 2c8b2d6..c7096f1 100644 (file)
@@ -11,3 +11,4 @@ doc.dep
 cmds-*.txt
 mergetools-*.txt
 manpage-base-url.xsl
+SubmittingPatches.txt
index 471bb29..2ab6556 100644 (file)
@@ -67,6 +67,7 @@ SP_ARTICLES += howto/maintain-git
 API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt technical/api-index.txt, $(wildcard technical/api-*.txt)))
 SP_ARTICLES += $(API_DOCS)
 
+TECH_DOCS += SubmittingPatches
 TECH_DOCS += technical/hash-function-transition
 TECH_DOCS += technical/http-protocol
 TECH_DOCS += technical/index-format
@@ -181,6 +182,7 @@ ASCIIDOC = asciidoctor
 ASCIIDOC_CONF =
 ASCIIDOC_HTML = xhtml5
 ASCIIDOC_DOCBOOK = docbook45
+ASCIIDOC_EXTRA += -acompat-mode
 ASCIIDOC_EXTRA += -I. -rasciidoctor-extensions
 ASCIIDOC_EXTRA += -alitdd='&\#x2d;&\#x2d;'
 DBLATEX_COMMON =
@@ -323,6 +325,7 @@ clean:
        $(RM) *.pdf
        $(RM) howto-index.txt howto/*.html doc.dep
        $(RM) technical/*.html technical/api-index.txt
+       $(RM) SubmittingPatches.txt
        $(RM) $(cmds_txt) $(mergetools_txt) *.made
        $(RM) manpage-base-url.xsl
 
@@ -361,6 +364,9 @@ technical/%.html: ASCIIDOC_EXTRA += -a git-relative-html-prefix=../
 $(patsubst %,%.html,$(API_DOCS) technical/api-index $(TECH_DOCS)): %.html : %.txt asciidoc.conf
        $(QUIET_ASCIIDOC)$(TXT_TO_HTML) $*.txt
 
+SubmittingPatches.txt: SubmittingPatches
+       $(QUIET_GEN) cp $< $@
+
 XSLT = docbook.xsl
 XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css
 
index 248ba70..cdd761b 100644 (file)
@@ -65,7 +65,7 @@ UI, Workflows & Features
    learned to take the 'unfold' and 'only' modifiers to normalize its
    output, e.g. "git log --format=%(trailers:only,unfold)".
 
- * "gitweb" shows a link to visit the 'raw' contents of blbos in the
+ * "gitweb" shows a link to visit the 'raw' contents of blobs in the
    history overview page.
 
  * "[gc] rerereResolved = 5.days" used to be invalid, as the variable
@@ -109,13 +109,13 @@ Performance, Internal Implementation, Development Support etc.
  * Conversion from uchar[20] to struct object_id continues.
 
  * Start using selected c99 constructs in small, stable and
-   essentialpart of the system to catch people who care about
+   essential part of the system to catch people who care about
    older compilers that do not grok them.
 
  * The filter-process interface learned to allow a process with long
    latency give a "delayed" response.
 
- * Many uses of comparision callback function the hashmap API uses
+ * Many uses of comparison callback function the hashmap API uses
    cast the callback function type when registering it to
    hashmap_init(), which defeats the compile time type checking when
    the callback interface changes (e.g. gaining more parameters).
diff --git a/Documentation/RelNotes/2.15.1.txt b/Documentation/RelNotes/2.15.1.txt
new file mode 100644 (file)
index 0000000..ec06704
--- /dev/null
@@ -0,0 +1,88 @@
+Git v2.15.1 Release Notes
+=========================
+
+Fixes since v2.15
+-----------------
+
+ * TravisCI build updates.
+
+ * "auto" as a value for the columnar output configuration ought to
+   judge "is the output consumed by humans?" with the same criteria as
+   "auto" for coloured output configuration, i.e. either the standard
+   output stream is going to tty, or a pager is in use.  We forgot the
+   latter, which has been fixed.
+
+ * The experimental "color moved lines differently in diff output"
+   feature was buggy around "ignore whitespace changes" edges, which
+   has been corrected.
+
+ * Instead of using custom line comparison and hashing functions to
+   implement "moved lines" coloring in the diff output, use the pair
+   of these functions from lower-layer xdiff/ code.
+
+ * Some codepaths did not check for errors when asking what branch the
+   HEAD points at, which have been fixed.
+
+ * "git commit", after making a commit, did not check for errors when
+   asking on what branch it made the commit, which has been corrected.
+
+ * "git status --ignored -u" did not stop at a working tree of a
+   separate project that is embedded in an ignored directory and
+   listed files in that other project, instead of just showing the
+   directory itself as ignored.
+
+ * A broken access to object databases in recent update to "git grep
+   --recurse-submodules" has been fixed.
+
+ * A recent regression in "git rebase -i" that broke execution of git
+   commands from subdirectories via "exec" instruction has been fixed.
+
+ * "git check-ref-format --branch @{-1}" bit a "BUG()" when run
+   outside a repository for obvious reasons; clarify the documentation
+   and make sure we do not even try to expand the at-mark magic in
+   such a case, but still call the validation logic for branch names.
+
+ * Command line completion (in contrib/) update.
+
+ * Description of blame.{showroot,blankboundary,showemail,date}
+   configuration variables have been added to "git config --help".
+
+ * After an error from lstat(), diff_populate_filespec() function
+   sometimes still went ahead and used invalid data in struct stat,
+   which has been fixed.
+
+ * UNC paths are also relevant in Cygwin builds and they are now
+   tested just like Mingw builds.
+
+ * Correct start-up sequence so that a repository could be placed
+   immediately under the root directory again (which was broken at
+   around Git 2.13).
+
+ * The credential helper for libsecret (in contrib/) has been improved
+   to allow possibly prompting the end user to unlock secrets that are
+   currently locked (otherwise the secrets may not be loaded).
+
+ * Updates from GfW project.
+
+ * "git rebase -i" recently started misbehaving when a submodule that
+   is configured with 'submodule.<name>.ignore' is dirty; this has
+   been corrected.
+
+ * Some error messages did not quote filenames shown in it, which have
+   been fixed.
+
+ * Building with NO_LIBPCRE1_JIT did not disable it, which has been fixed.
+
+ * We used to add an empty alternate object database to the system
+   that does not help anything; it has been corrected.
+
+ * Error checking in "git imap-send" for empty response has been
+   improved.
+
+ * An ancient bug in "git apply --ignore-space-change" codepath has
+   been fixed.
+
+ * There was a recent semantic mismerge in the codepath to write out a
+   section of a configuration section, which has been corrected.
+
+Also contains various documentation updates and code clean-ups.
index c7bd0f7..c617e37 100644 (file)
@@ -33,6 +33,63 @@ UI, Workflows & Features
 
  * "git stash save" has been deprecated in favour of "git stash push".
 
+ * The set of paths output from "git status --ignored" was tied
+   closely with its "--untracked=<mode>" option, but now it can be
+   controlled more flexibly.  Most notably, a directory that is
+   ignored because it is listed to be ignored in the ignore/exclude
+   mechanism can be handled differently from a directory that ends up
+   to be ignored only because all files in it are ignored.
+
+ * The remote-helper for talking to MediaWiki has been updated to
+   truncate an overlong pagename so that ".mw" suffix can still be
+   added.
+
+ * The remote-helper for talking to MediaWiki has been updated to
+   work with mediawiki namespaces.
+
+ * The "--format=..." option "git for-each-ref" takes learned to show
+   the name of the 'remote' repository and the ref at the remote side
+   that is affected for 'upstream' and 'push' via "%(push:remotename)"
+   and friends.
+
+ * Doc and message updates to teach users "bisect view" is a synonym
+   for "bisect visualize".
+
+ * "git bisect run" that did not specify any command to run used to go
+   ahead and treated all commits to be tested as 'good'.  This has
+   been corrected by making the command error out.
+
+ * The SubmittingPatches document has been converted to produce an
+   HTML version via AsciiDoc/Asciidoctor.
+   (merge 049e64aa50 bc/submitting-patches-in-asciidoc later to maint).
+
+ * We learned to talk to watchman to speed up "git status" and other
+   operations that need to see which paths have been modified.
+
+ * The "diff" family of commands learned to ignore differences in
+   carriage return at the end of line.
+
+ * Places that know about "sendemail.to", like documentation and shell
+   completion (in contrib/) have been taught about "sendemail.tocmd",
+   too.
+
+ * "git add --renormalize ." is a new and safer way to record the fact
+   that you are correcting the end-of-line convention and other
+   "convert_to_git()" glitches in the in-repository data.
+
+ * "git branch" and "git checkout -b" are now forbidden from creating
+   a branch whose name is "HEAD".
+
+ * "git branch --list" learned to show its output through the pager by
+   default when the output is going to a terminal, which is controlled
+   by the pager.branch configuration variable.  This is similar to a
+   recent change to "git tag --list".
+
+ * "git grep -W", "git diff -W" and their friends learned a heuristic
+   to extend a pre-context beyond the line that matches the "function
+   pattern" (aka "diff.*.xfuncname") to include a comment block, if
+   exists, that immediately precedes it.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -59,6 +116,19 @@ Performance, Internal Implementation, Development Support etc.
 
  * Conversion from uchar[20] to struct object_id continues.
 
+ * Code cleanup.
+
+ * A single-word "unsigned flags" in the diff options is being split
+   into a structure with many bitfields.
+
+ * TravisCI build updates.
+
+ * Parts of a test to drive the long-running content filter interface
+   has been split into its own module, hopefully to eventually become
+   reusable.
+
+ * Drop (perhaps overly cautious) sanity check before using the index
+   read from the filesystem at runtime.
 
 Also contains various documentation updates and code clean-ups.
 
@@ -71,56 +141,137 @@ Fixes since v2.15
    "auto" for coloured output configuration, i.e. either the standard
    output stream is going to tty, or a pager is in use.  We forgot the
    latter, which has been fixed.
-   (merge 965ff23a43 kd/auto-col-with-pager-fix later to maint).
 
  * The experimental "color moved lines differently in diff output"
-   feature was buggy around "ignore whitespace changes" edges, whihch
+   feature was buggy around "ignore whitespace changes" edges, which
    has been corrected.
-   (merge b66b507292 jk/diff-color-moved-fix later to maint).
 
  * Instead of using custom line comparison and hashing functions to
    implement "moved lines" coloring in the diff output, use the pair
    of these functions from lower-layer xdiff/ code.
-   (merge 01be97c2b2 sb/diff-color-moved-use-xdl-recmatch later to maint).
 
  * Some codepaths did not check for errors when asking what branch the
    HEAD points at, which have been fixed.
-   (merge dbd2b55cb7 jk/misc-resolve-ref-unsafe-fixes later to maint).
 
  * "git commit", after making a commit, did not check for errors when
-   asking on what branch it made the commit, which has been correted.
-   (merge c26de08370 ao/check-resolve-ref-unsafe-result later to maint).
+   asking on what branch it made the commit, which has been corrected.
 
  * "git status --ignored -u" did not stop at a working tree of a
    separate project that is embedded in an ignored directory and
    listed files in that other project, instead of just showing the
    directory itself as ignored.
-   (merge fadb4820c4 js/submodule-in-excluded later to maint).
 
  * A broken access to object databases in recent update to "git grep
    --recurse-submodules" has been fixed.
-   (merge 9560e6245a bw/grep-recurse-submodules later to maint).
 
  * A recent regression in "git rebase -i" that broke execution of git
-   commands from subdirectories via "exec" insn has been fixed.
-   (merge 09d7b6c6fa jk/rebase-i-exec-gitdir-fix later to maint).
+   commands from subdirectories via "exec" instruction has been fixed.
 
  * A (possibly flakey) test fix.
-   (merge cff48ccf2a jc/t5601-copy-workaround later to maint).
 
  * "git check-ref-format --branch @{-1}" bit a "BUG()" when run
    outside a repository for obvious reasons; clarify the documentation
    and make sure we do not even try to expand the at-mark magic in
    such a case, but still call the validation logic for branch names.
-   (merge 89dd32aedc jc/check-ref-format-oor later to maint).
 
  * "git fetch --recurse-submodules" now knows that submodules can be
    moved around in the superproject in addition to getting updated,
    and finds the ones that need to be fetched accordingly.
-   (merge 4b4acedd61 hv/fetch-moved-submodules-on-demand later to maint).
 
  * Command line completion (in contrib/) update.
-   (merge 6357d9d004 tb/complete-checkout later to maint).
+
+ * Description of blame.{showroot,blankboundary,showemail,date}
+   configuration variables have been added to "git config --help".
+
+ * After an error from lstat(), diff_populate_filespec() function
+   sometimes still went ahead and used invalid data in struct stat,
+   which has been fixed.
+
+ * UNC paths are also relevant in Cygwin builds and they are now
+   tested just like Mingw builds.
+
+ * Correct start-up sequence so that a repository could be placed
+   immediately under the root directory again (which was broken at
+   around Git 2.13).
+
+ * The credential helper for libsecret (in contrib/) has been improved
+   to allow possibly prompting the end user to unlock secrets that are
+   currently locked (otherwise the secrets may not be loaded).
+
+ * MinGW updates.
+
+ * Error checking in "git imap-send" for empty response has been
+   improved.
+
+ * Recent update to the refs infrastructure implementation started
+   rewriting packed-refs file more often than before; this has been
+   optimized again for most trivial cases.
+   (merge 7c6bd25c7d mh/avoid-rewriting-packed-refs later to maint).
+
+ * Some error messages did not quote filenames shown in it, which have
+   been fixed.
+
+ * "git rebase -i" recently started misbehaving when a submodule that
+   is configured with 'submodule.<name>.ignore' is dirty; this has
+   been corrected.
+
+ * Building with NO_LIBPCRE1_JIT did not disable it, which has been fixed.
+
+ * We used to add an empty alternate object database to the system
+   that does not help anything; it has been corrected.
+
+ * Doc update around use of "format-patch --subject-prefix" etc.
+
+ * A fix for an ancient bug in "git apply --ignore-space-change" codepath.
+
+ * Clarify and enhance documentation for "merge-base --fork-point", as
+   it was clear what it computed but not why/what for.
+   (merge 6d1700b8af jc/merge-base-fork-point-doc later to maint).
+
+ * A few scripts (both in production and tests) incorrectly redirected
+   their error output.  These have been corrected.
+   (merge eadf1c8f45 tz/redirect-fix later to maint).
+
+ * "git notes" sent its error message to its standard output stream,
+   which was corrected.
+   (merge 89b9e31dd5 tz/notes-error-to-stderr later to maint).
+
+ * The three-way merge performed by "git cherry-pick" was confused
+   when a new submodule was added in the meantime, which has been
+   fixed (or "papered over").
+   (merge c641ca6707 sb/test-cherry-pick-submodule-getting-in-a-way later to maint).
+
+ * The sequencer machinery (used by "git cherry-pick A..B", and "git
+   rebase -i", among other things) would have lost a commit if stopped
+   due to an unlockable index file, which has been fixed.
+   (merge bd58886775 pw/sequencer-recover-from-unlockable-index later to maint).
+
+ * "git apply --inaccurate-eof" when used with "--ignore-space-change"
+   triggered an internal sanity check, which has been fixed.
+   (merge 4855de1233 rs/apply-inaccurate-eof-with-incomplete-line later to maint).
+
+ * Command line completion (in contrib/) has been taught about the
+   "--copy" option of "git branch".
+   (merge 41ca0f773e tz/complete-branch-copy later to maint).
+
+ * When "git rebase" prepared an mailbox of changes and fed it to "git
+   am" to replay them, it was confused when a stray "From " happened
+   to be in the log message of one of the replayed changes.  This has
+   been corrected.
+   (merge ae3b2b04bb ew/rebase-mboxrd later to maint).
+
+ * There was a recent semantic mismerge in the codepath to write out a
+   section of a configuration section, which has been corrected.
+
+ * Mentions of "git-rebase" and "git-am" (dashed form) still remained
+   in end-user visible strings emitted by the "git rebase" command;
+   they have been corrected.
+   (merge 82cb775c06 ks/rebase-no-git-foo later to maint).
+
+ * Contrary to the documentation, "git pull -4/-6 other-args" did not
+   ask the underlying "git fetch" to go over IPv4/IPv6, which has been
+   corrected.
+   (merge ffb4568afe sw/pull-ipv46-passthru later to maint).
 
  * Other minor doc, test and build updates and code cleanups.
-   (merge bab76141da cn/diff-indent-no-longer-is-experimental later to maint).
+   (merge c5e3bc6ec4 sd/branch-copy later to maint).
index 558d465..3ef3092 100644 (file)
@@ -1,40 +1,47 @@
+Submitting Patches
+==================
+
+== Guidelines
+
 Here are some guidelines for people who want to contribute their code
 to this software.
 
-(0) Decide what to base your work on.
+[[base-branch]]
+=== Decide what to base your work on.
 
 In general, always base your work on the oldest branch that your
 change is relevant to.
 
- - A bugfix should be based on 'maint' in general. If the bug is not
-   present in 'maint', base it on 'master'. For a bug that's not yet
-   in 'master', find the topic that introduces the regression, and
-   base your work on the tip of the topic.
+* A bugfix should be based on `maint` in general. If the bug is not
+  present in `maint`, base it on `master`. For a bug that's not yet
+  in `master`, find the topic that introduces the regression, and
+  base your work on the tip of the topic.
 
- - A new feature should be based on 'master' in general. If the new
-   feature depends on a topic that is in 'pu', but not in 'master',
-   base your work on the tip of that topic.
+* A new feature should be based on `master` in general. If the new
+  feature depends on a topic that is in `pu`, but not in `master`,
+  base your work on the tip of that topic.
 
- - Corrections and enhancements to a topic not yet in 'master' should
-   be based on the tip of that topic. If the topic has not been merged
-   to 'next', it's alright to add a note to squash minor corrections
-   into the series.
+* Corrections and enhancements to a topic not yet in `master` should
+  be based on the tip of that topic. If the topic has not been merged
+  to `next`, it's alright to add a note to squash minor corrections
+  into the series.
 
- - In the exceptional case that a new feature depends on several topics
-   not in 'master', start working on 'next' or 'pu' privately and send
-   out patches for discussion. Before the final merge, you may have to
-   wait until some of the dependent topics graduate to 'master', and
-   rebase your work.
+* In the exceptional case that a new feature depends on several topics
+  not in `master`, start working on `next` or `pu` privately and send
+  out patches for discussion. Before the final merge, you may have to
+  wait until some of the dependent topics graduate to `master`, and
+  rebase your work.
 
- - Some parts of the system have dedicated maintainers with their own
-   repositories (see the section "Subsystems" below).  Changes to
-   these parts should be based on their trees.
+* Some parts of the system have dedicated maintainers with their own
+  repositories (see the section "Subsystems" below).  Changes to
+  these parts should be based on their trees.
 
-To find the tip of a topic branch, run "git log --first-parent
-master..pu" and look for the merge commit. The second parent of this
+To find the tip of a topic branch, run `git log --first-parent
+master..pu` and look for the merge commit. The second parent of this
 commit is the tip of the topic branch.
 
-(1) Make separate commits for logically separate changes.
+[[separate-commits]]
+=== Make separate commits for logically separate changes.
 
 Unless your patch is really trivial, you should not be sending
 out a patch that was generated between your working tree and
@@ -58,8 +65,9 @@ differs substantially from the prior version, are all good things
 to have.
 
 Make sure that you have tests for the bug you are fixing.  See
-t/README for guidance.
+`t/README` for guidance.
 
+[[tests]]
 When adding a new feature, make sure that you have new tests to show
 the feature triggers the new behavior when it should, and to show the
 feature does not trigger when it shouldn't.  After any code change, make
@@ -84,41 +92,45 @@ turning en_UK spelling to en_US).  Obvious typographical fixes are much
 more welcomed ("teh -> "the"), preferably submitted as independent
 patches separate from other documentation changes.
 
+[[whitespace-check]]
 Oh, another thing.  We are picky about whitespaces.  Make sure your
 changes do not trigger errors with the sample pre-commit hook shipped
-in templates/hooks--pre-commit.  To help ensure this does not happen,
-run "git diff --check" on your changes before you commit.
+in `templates/hooks--pre-commit`.  To help ensure this does not happen,
+run `git diff --check` on your changes before you commit.
 
-
-(2) Describe your changes well.
+[[describe-changes]]
+=== Describe your changes well.
 
 The first line of the commit message should be a short description (50
-characters is the soft limit, see DISCUSSION in git-commit(1)), and
-should skip the full stop.  It is also conventional in most cases to
+characters is the soft limit, see DISCUSSION in linkgit:git-commit[1]),
+and should skip the full stop.  It is also conventional in most cases to
 prefix the first line with "area: " where the area is a filename or
 identifier for the general area of the code being modified, e.g.
 
-  . doc: clarify distinction between sign-off and pgp-signing
-  . githooks.txt: improve the intro section
+* doc: clarify distinction between sign-off and pgp-signing
+* githooks.txt: improve the intro section
 
-If in doubt which identifier to use, run "git log --no-merges" on the
+If in doubt which identifier to use, run `git log --no-merges` on the
 files you are modifying to see the current conventions.
 
+[[summary-section]]
 It's customary to start the remainder of the first line after "area: "
 with a lower-case letter. E.g. "doc: clarify...", not "doc:
 Clarify...", or "githooks.txt: improve...", not "githooks.txt:
 Improve...".
 
+[[meaningful-message]]
 The body should provide a meaningful commit message, which:
 
-  . explains the problem the change tries to solve, i.e. what is wrong
-    with the current code without the change.
+. explains the problem the change tries to solve, i.e. what is wrong
+  with the current code without the change.
 
-  . justifies the way the change solves the problem, i.e. why the
-    result with the change is better.
+. justifies the way the change solves the problem, i.e. why the
+  result with the change is better.
 
-  . alternate solutions considered but discarded, if any.
+. alternate solutions considered but discarded, if any.
 
+[[imperative-mood]]
 Describe your changes in imperative mood, e.g. "make xyzzy do frotz"
 instead of "[This patch] makes xyzzy do frotz" or "[I] changed xyzzy
 to do frotz", as if you are giving orders to the codebase to change
@@ -126,36 +138,43 @@ its behavior.  Try to make sure your explanation can be understood
 without external resources. Instead of giving a URL to a mailing list
 archive, summarize the relevant points of the discussion.
 
+[[commit-reference]]
 If you want to reference a previous commit in the history of a stable
 branch, use the format "abbreviated sha1 (subject, date)",
 with the subject enclosed in a pair of double-quotes, like this:
 
-    Commit f86a374 ("pack-bitmap.c: fix a memleak", 2015-03-30)
-    noticed that ...
+....
+       Commit f86a374 ("pack-bitmap.c: fix a memleak", 2015-03-30)
+       noticed that ...
+....
 
 The "Copy commit summary" command of gitk can be used to obtain this
-format, or this invocation of "git show":
+format, or this invocation of `git show`:
 
-    git show -s --date=short --pretty='format:%h ("%s", %ad)' <commit>
+....
+       git show -s --date=short --pretty='format:%h ("%s", %ad)' <commit>
+....
 
-(3) Generate your patch using Git tools out of your commits.
+[[git-tools]]
+=== Generate your patch using Git tools out of your commits.
 
 Git based diff tools generate unidiff which is the preferred format.
 
-You do not have to be afraid to use -M option to "git diff" or
-"git format-patch", if your patch involves file renames.  The
+You do not have to be afraid to use `-M` option to `git diff` or
+`git format-patch`, if your patch involves file renames.  The
 receiving end can handle them just fine.
 
+[[review-patch]]
 Please make sure your patch does not add commented out debugging code,
 or include any extra files which do not relate to what your patch
 is trying to achieve. Make sure to review
 your patch after generating it, to ensure accuracy.  Before
-sending out, please make sure it cleanly applies to the "master"
+sending out, please make sure it cleanly applies to the `master`
 branch head.  If you are preparing a work based on "next" branch,
 that is fine, but please mark it as such.
 
-
-(4) Sending your patches.
+[[send-patches]]
+=== Sending your patches.
 
 Learn to use format-patch and send-email if possible.  These commands
 are optimized for the workflow of sending patches, avoiding many ways
@@ -184,14 +203,15 @@ lose tabs that way if you are not careful.
 
 It is a common convention to prefix your subject line with
 [PATCH].  This lets people easily distinguish patches from other
-e-mail discussions.  Use of additional markers after PATCH and
-the closing bracket to mark the nature of the patch is also
-encouraged.  E.g. [PATCH/RFC] is often used when the patch is
-not ready to be applied but it is for discussion, [PATCH v2],
-[PATCH v3] etc. are often seen when you are sending an update to
-what you have previously sent.
-
-"git format-patch" command follows the best current practice to
+e-mail discussions.  Use of markers in addition to PATCH within
+the brackets to describe the nature of the patch is also
+encouraged.  E.g. [RFC PATCH] (where RFC stands for "request for
+comments") is often used to indicate a patch needs further
+discussion before being accepted, [PATCH v2], [PATCH v3] etc.
+are often seen when you are sending an update to what you have
+previously sent.
+
+The `git format-patch` command follows the best current practice to
 format the body of an e-mail message.  At the beginning of the
 patch should come your commit message, ending with the
 Signed-off-by: lines, and a line that consists of three dashes,
@@ -199,6 +219,10 @@ followed by the diffstat information and the patch itself.  If
 you are forwarding a patch from somebody else, optionally, at
 the beginning of the e-mail message just before the commit
 message starts, you can put a "From: " line to name that person.
+To change the default "[PATCH]" in the subject to "[<text>]", use
+`git format-patch --subject-prefix=<text>`.  As a shortcut, you
+can use `--rfc` instead of `--subject-prefix="RFC PATCH"`, or
+`-v <n>` instead of `--subject-prefix="PATCH v<n>"`.
 
 You often want to add additional explanation about the patch,
 other than the commit message itself.  Place such "cover letter"
@@ -208,6 +232,7 @@ an explanation of changes between each iteration can be kept in
 Git-notes and inserted automatically following the three-dash
 line via `git format-patch --notes`.
 
+[[attachment]]
 Do not attach the patch as a MIME attachment, compressed or not.
 Do not let your e-mail client send quoted-printable.  Do not let
 your e-mail client send format=flowed which would destroy
@@ -222,6 +247,7 @@ that it will be postponed.
 Exception:  If your mailer is mangling patches then someone may ask
 you to re-send them using MIME, that is OK.
 
+[[pgp-signature]]
 Do not PGP sign your patch. Most likely, your maintainer or other people on the
 list would not have your PGP key and would not bother obtaining it anyway.
 Your patch is not judged by who you are; a good patch from an unknown origin
@@ -230,28 +256,27 @@ origin that is done poorly or does incorrect things.
 
 If you really really really really want to do a PGP signed
 patch, format it as "multipart/signed", not a text/plain message
-that starts with '-----BEGIN PGP SIGNED MESSAGE-----'.  That is
+that starts with `-----BEGIN PGP SIGNED MESSAGE-----`.  That is
 not a text/plain, it's something else.
 
 Send your patch with "To:" set to the mailing list, with "cc:" listing
 people who are involved in the area you are touching (the output from
-"git blame $path" and "git shortlog --no-merges $path" would help to
++git blame _$path_+ and +git shortlog {litdd}no-merges _$path_+ would help to
 identify them), to solicit comments and reviews.
 
+:1: footnote:[The current maintainer: gitster@pobox.com]
+:2: footnote:[The mailing list: git@vger.kernel.org]
+
 After the list reached a consensus that it is a good idea to apply the
-patch, re-send it with "To:" set to the maintainer [*1*] and "cc:" the
-list [*2*] for inclusion.
+patch, re-send it with "To:" set to the maintainer{1} and "cc:" the
+list{2} for inclusion.
 
-Do not forget to add trailers such as "Acked-by:", "Reviewed-by:" and
-"Tested-by:" lines as necessary to credit people who helped your
+Do not forget to add trailers such as `Acked-by:`, `Reviewed-by:` and
+`Tested-by:` lines as necessary to credit people who helped your
 patch.
 
-    [Addresses]
-     *1* The current maintainer: gitster@pobox.com
-     *2* The mailing list: git@vger.kernel.org
-
-
-(5) Certify your work by adding your "Signed-off-by: " line
+[[sign-off]]
+=== Certify your work by adding your "Signed-off-by: " line
 
 To improve tracking of who did what, we've borrowed the
 "sign-off" procedure from the Linux kernel project on patches
@@ -263,35 +288,39 @@ the patch, which certifies that you wrote it or otherwise have
 the right to pass it on as a open-source patch.  The rules are
 pretty simple: if you can certify the below D-C-O:
 
-        Developer's Certificate of Origin 1.1
-
-        By making a contribution to this project, I certify that:
-
-        (a) The contribution was created in whole or in part by me and I
-            have the right to submit it under the open source license
-            indicated in the file; or
-
-        (b) The contribution is based upon previous work that, to the best
-            of my knowledge, is covered under an appropriate open source
-            license and I have the right under that license to submit that
-            work with modifications, whether created in whole or in part
-            by me, under the same open source license (unless I am
-            permitted to submit under a different license), as indicated
-            in the file; or
-
-        (c) The contribution was provided directly to me by some other
-            person who certified (a), (b) or (c) and I have not modified
-            it.
-
-        (d) I understand and agree that this project and the contribution
-            are public and that a record of the contribution (including all
-            personal information I submit with it, including my sign-off) is
-            maintained indefinitely and may be redistributed consistent with
-            this project or the open source license(s) involved.
+[[dco]]
+.Developer's Certificate of Origin 1.1
+____
+By making a contribution to this project, I certify that:
+
+a. The contribution was created in whole or in part by me and I
+   have the right to submit it under the open source license
+   indicated in the file; or
+
+b. The contribution is based upon previous work that, to the best
+   of my knowledge, is covered under an appropriate open source
+   license and I have the right under that license to submit that
+   work with modifications, whether created in whole or in part
+   by me, under the same open source license (unless I am
+   permitted to submit under a different license), as indicated
+   in the file; or
+
+c. The contribution was provided directly to me by some other
+   person who certified (a), (b) or (c) and I have not modified
+   it.
+
+d. I understand and agree that this project and the contribution
+   are public and that a record of the contribution (including all
+   personal information I submit with it, including my sign-off) is
+   maintained indefinitely and may be redistributed consistent with
+   this project or the open source license(s) involved.
+____
 
 then you just add a line saying
 
-        Signed-off-by: Random J Developer <random@developer.example.org>
+....
+       Signed-off-by: Random J Developer <random@developer.example.org>
+....
 
 This line can be automatically added by Git if you run the git-commit
 command with the -s option.
@@ -302,85 +331,86 @@ D-C-O.  Indeed you are encouraged to do so.  Do not forget to
 place an in-body "From: " line at the beginning to properly attribute
 the change to its true author (see (2) above).
 
+[[real-name]]
 Also notice that a real name is used in the Signed-off-by: line. Please
 don't hide your real name.
 
+[[commit-trailers]]
 If you like, you can put extra tags at the end:
 
-1. "Reported-by:" is used to credit someone who found the bug that
-   the patch attempts to fix.
-2. "Acked-by:" says that the person who is more familiar with the area
-   the patch attempts to modify liked the patch.
-3. "Reviewed-by:", unlike the other tags, can only be offered by the
-   reviewer and means that she is completely satisfied that the patch
-   is ready for application.  It is usually offered only after a
-   detailed review.
-4. "Tested-by:" is used to indicate that the person applied the patch
-   and found it to have the desired effect.
+. `Reported-by:` is used to credit someone who found the bug that
+  the patch attempts to fix.
+. `Acked-by:` says that the person who is more familiar with the area
+  the patch attempts to modify liked the patch.
+. `Reviewed-by:`, unlike the other tags, can only be offered by the
+  reviewer and means that she is completely satisfied that the patch
+  is ready for application.  It is usually offered only after a
+  detailed review.
+. `Tested-by:` is used to indicate that the person applied the patch
+  and found it to have the desired effect.
 
 You can also create your own tag or use one that's in common usage
 such as "Thanks-to:", "Based-on-patch-by:", or "Mentored-by:".
 
-------------------------------------------------
-Subsystems with dedicated maintainers
+== Subsystems with dedicated maintainers
 
 Some parts of the system have dedicated maintainers with their own
 repositories.
 
- - git-gui/ comes from git-gui project, maintained by Pat Thoyts:
+- 'git-gui/' comes from git-gui project, maintained by Pat Thoyts:
 
-        git://repo.or.cz/git-gui.git
+       git://repo.or.cz/git-gui.git
 
- - gitk-git/ comes from Paul Mackerras's gitk project:
+- 'gitk-git/' comes from Paul Mackerras's gitk project:
 
-        git://ozlabs.org/~paulus/gitk
+       git://ozlabs.org/~paulus/gitk
 
- - po/ comes from the localization coordinator, Jiang Xin:
+- 'po/' comes from the localization coordinator, Jiang Xin:
 
        https://github.com/git-l10n/git-po/
 
 Patches to these parts should be based on their trees.
 
-------------------------------------------------
-An ideal patch flow
+[[patch-flow]]
+== An ideal patch flow
 
 Here is an ideal patch flow for this project the current maintainer
 suggests to the contributors:
 
- (0) You come up with an itch.  You code it up.
+. You come up with an itch.  You code it up.
 
- (1) Send it to the list and cc people who may need to know about
-     the change.
+. Send it to the list and cc people who may need to know about
+  the change.
++
+The people who may need to know are the ones whose code you
+are butchering.  These people happen to be the ones who are
+most likely to be knowledgeable enough to help you, but
+they have no obligation to help you (i.e. you ask for help,
+don't demand).  +git log -p {litdd} _$area_you_are_modifying_+ would
+help you find out who they are.
 
-     The people who may need to know are the ones whose code you
-     are butchering.  These people happen to be the ones who are
-     most likely to be knowledgeable enough to help you, but
-     they have no obligation to help you (i.e. you ask for help,
-     don't demand).  "git log -p -- $area_you_are_modifying" would
-     help you find out who they are.
+. You get comments and suggestions for improvements.  You may
+  even get them in a "on top of your change" patch form.
 
- (2) You get comments and suggestions for improvements.  You may
-     even get them in a "on top of your change" patch form.
+. Polish, refine, and re-send to the list and the people who
+  spend their time to improve your patch.  Go back to step (2).
 
- (3) Polish, refine, and re-send to the list and the people who
-     spend their time to improve your patch.  Go back to step (2).
+. The list forms consensus that the last round of your patch is
+  good.  Send it to the maintainer and cc the list.
 
- (4) The list forms consensus that the last round of your patch is
-     good.  Send it to the maintainer and cc the list.
-
- (5) A topic branch is created with the patch and is merged to 'next',
-     and cooked further and eventually graduates to 'master'.
+. A topic branch is created with the patch and is merged to `next`,
+  and cooked further and eventually graduates to `master`.
 
 In any time between the (2)-(3) cycle, the maintainer may pick it up
-from the list and queue it to 'pu', in order to make it easier for
+from the list and queue it to `pu`, in order to make it easier for
 people play with it without having to pick up and apply the patch to
 their trees themselves.
 
-------------------------------------------------
-Know the status of your patch after submission
+[[patch-status]]
+== Know the status of your patch after submission
 
 * You can use Git itself to find out when your patch is merged in
-  master. 'git pull --rebase' will automatically skip already-applied
+  master. `git pull --rebase` will automatically skip already-applied
   patches, and will let you know. This works only if you rebase on top
   of the branch in which your patch has been merged (i.e. it will not
   tell you if your patch is merged in pu if you rebase on top of
@@ -390,8 +420,8 @@ Know the status of your patch after submission
   entitled "What's cooking in git.git" and "What's in git.git" giving
   the status of various proposed changes.
 
---------------------------------------------------
-GitHub-Travis CI hints
+[[travis]]
+== GitHub-Travis CI hints
 
 With an account at GitHub (you can get one for free to work on open
 source projects), you can use Travis CI to test your changes on Linux,
@@ -400,25 +430,25 @@ test build here: https://travis-ci.org/git/git/builds/120473209
 
 Follow these steps for the initial setup:
 
- (1) Fork https://github.com/git/git to your GitHub account.
-     You can find detailed instructions how to fork here:
-     https://help.github.com/articles/fork-a-repo/
+. Fork https://github.com/git/git to your GitHub account.
+  You can find detailed instructions how to fork here:
+  https://help.github.com/articles/fork-a-repo/
 
- (2) Open the Travis CI website: https://travis-ci.org
+. Open the Travis CI website: https://travis-ci.org
 
- (3) Press the "Sign in with GitHub" button.
+. Press the "Sign in with GitHub" button.
 
- (4) Grant Travis CI permissions to access your GitHub account.
-     You can find more information about the required permissions here:
-     https://docs.travis-ci.com/user/github-oauth-scopes
+. Grant Travis CI permissions to access your GitHub account.
+  You can find more information about the required permissions here:
+  https://docs.travis-ci.com/user/github-oauth-scopes
 
- (5) Open your Travis CI profile page: https://travis-ci.org/profile
+. Open your Travis CI profile page: https://travis-ci.org/profile
 
- (6) Enable Travis CI builds for your Git fork.
+. Enable Travis CI builds for your Git fork.
 
 After the initial setup, Travis CI will run whenever you push new changes
 to your fork of Git on GitHub.  You can monitor the test state of all your
-branches here: https://travis-ci.org/<Your GitHub handle>/git/branches
+branches here: https://travis-ci.org/__<Your GitHub handle>__/git/branches
 
 If a branch did not pass all test cases then it is marked with a red
 cross.  In that case you can click on the failing Travis CI job and
@@ -430,17 +460,16 @@ example: https://travis-ci.org/git/git/jobs/122676187
 Fix the problem and push your fix to your Git fork.  This will trigger
 a new Travis CI build to ensure all tests pass.
 
-
-------------------------------------------------
-MUA specific hints
+[[mua]]
+== MUA specific hints
 
 Some of patches I receive or pick up from the list share common
 patterns of breakage.  Please make sure your MUA is set up
 properly not to corrupt whitespaces.
 
-See the DISCUSSION section of git-format-patch(1) for hints on
+See the DISCUSSION section of linkgit:git-format-patch[1] for hints on
 checking your patch by mailing it to yourself and applying with
-git-am(1).
+linkgit:git-am[1].
 
 While you are at it, check the resulting commit log message from
 a trial run of applying the patch.  If what is in the resulting
@@ -452,23 +481,24 @@ should come after the three-dash line that signals the end of the
 commit message.
 
 
-Pine
-----
+=== Pine
 
 (Johannes Schindelin)
 
+....
 I don't know how many people still use pine, but for those poor
 souls it may be good to mention that the quell-flowed-text is
 needed for recent versions.
 
 ... the "no-strip-whitespace-before-send" option, too. AFAIK it
 was introduced in 4.60.
+....
 
 (Linus Torvalds)
 
+....
 And 4.58 needs at least this.
 
----
 diff-tree 8326dd8350be64ac7fc805f6563a1d61ad10d32c (from e886a61f76edf5410573e92e38ce22974f9c40f1)
 Author: Linus Torvalds <torvalds@g5.osdl.org>
 Date:   Mon Aug 15 17:23:51 2005 -0700
@@ -490,10 +520,11 @@ diff --git a/pico/pico.c b/pico/pico.c
 +#endif
                c |= COMP_EXIT;
                break;
-
+....
 
 (Daniel Barkalow)
 
+....
 > A patch to SubmittingPatches, MUA specific help section for
 > users of Pine 4.63 would be very much appreciated.
 
@@ -503,23 +534,21 @@ that or Gentoo did it.) So you need to set the
 "no-strip-whitespace-before-send" option, unless the option you have is
 "strip-whitespace-before-send", in which case you should avoid checking
 it.
+....
 
+=== Thunderbird, KMail, GMail
 
-Thunderbird, KMail, GMail
--------------------------
-
-See the MUA-SPECIFIC HINTS section of git-format-patch(1).
+See the MUA-SPECIFIC HINTS section of linkgit:git-format-patch[1].
 
-Gnus
-----
+=== Gnus
 
-'|' in the *Summary* buffer can be used to pipe the current
+"|" in the `*Summary*` buffer can be used to pipe the current
 message to an external program, and this is a handy way to drive
-"git am".  However, if the message is MIME encoded, what is
+`git am`.  However, if the message is MIME encoded, what is
 piped into the program is the representation you see in your
-*Article* buffer after unwrapping MIME.  This is often not what
+`*Article*` buffer after unwrapping MIME.  This is often not what
 you would want for two reasons.  It tends to screw up non ASCII
 characters (most notably in people's names), and also
-whitespaces (fatal in patches).  Running 'C-u g' to display the
-message in raw form before using '|' to run the pipe can work
+whitespaces (fatal in patches).  Running "C-u g" to display the
+message in raw form before using "|" to run the pipe can work
 this problem around.
index 5f0d627..531649c 100644 (file)
@@ -416,6 +416,13 @@ core.protectNTFS::
        8.3 "short" names.
        Defaults to `true` on Windows, and `false` elsewhere.
 
+core.fsmonitor::
+       If set, the value of this variable is used as a command which
+       will identify all files that may have changed since the
+       requested date/time. This information is used to speed up git by
+       avoiding unnecessary processing of files that have not changed.
+       See the "fsmonitor-watchman" section of linkgit:githooks[5].
+
 core.trustctime::
        If false, the ctime differences between the index and the
        working tree are ignored; useful when the inode change time
@@ -952,6 +959,23 @@ apply.whitespace::
        Tells 'git apply' how to handle whitespaces, in the same way
        as the `--whitespace` option. See linkgit:git-apply[1].
 
+blame.showRoot::
+       Do not treat root commits as boundaries in linkgit:git-blame[1].
+       This option defaults to false.
+
+blame.blankBoundary::
+       Show blank commit object name for boundary commits in
+       linkgit:git-blame[1]. This option defaults to false.
+
+blame.showEmail::
+       Show the author email instead of author name in linkgit:git-blame[1].
+       This option defaults to false.
+
+blame.date::
+       Specifies the format used to output dates in linkgit:git-blame[1].
+       If unset the iso format is used. For supported values,
+       see the discussion of the `--date` option at linkgit:git-log[1].
+
 branch.autoSetupMerge::
        Tells 'git branch' and 'git checkout' to set up new branches
        so that linkgit:git-pull[1] will appropriately merge from the
@@ -2983,6 +3007,7 @@ sendemail.smtpPass::
 sendemail.suppresscc::
 sendemail.suppressFrom::
 sendemail.to::
+sendemail.tocmd::
 sendemail.smtpDomain::
 sendemail.smtpServer::
 sendemail.smtpServerPort::
index dd0dba5..3c93c21 100644 (file)
@@ -557,6 +557,9 @@ endif::git-format-patch[]
 --text::
        Treat all files as text.
 
+--ignore-cr-at-eol::
+       Ignore carrige-return at the end of line when doing a comparison.
+
 --ignore-space-at-eol::
        Ignore changes in whitespace at EOL.
 
index b700bea..d50fa33 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 [verse]
 'git add' [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p]
          [--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]]
-         [--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing]
+         [--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [--renormalize]
          [--chmod=(+|-)x] [--] [<pathspec>...]
 
 DESCRIPTION
@@ -175,6 +175,13 @@ for "git add --no-all <pathspec>...", i.e. ignored removed files.
        warning (e.g., if you are manually performing operations on
        submodules).
 
+--renormalize::
+       Apply the "clean" process freshly to all tracked files to
+       forcibly add them again to the index.  This is useful after
+       changing `core.autocrlf` configuration or the `text` attribute
+       in order to correct files added with wrong CRLF/LF line endings.
+       This option implies `-u`.
+
 --chmod=(+|-)x::
        Override the executable bit of the added files.  The executable
        bit is only changed in the index, the files on disk are left
index 6c42abf..4a1417b 100644 (file)
@@ -23,7 +23,7 @@ on the subcommand:
  git bisect terms [--term-good | --term-bad]
  git bisect skip [(<rev>|<range>)...]
  git bisect reset [<commit>]
- git bisect visualize
+ git bisect (visualize|view)
  git bisect replay <logfile>
  git bisect log
  git bisect run <cmd>...
@@ -193,24 +193,23 @@ git bisect start --term-new fixed --term-old broken
 Then, use `git bisect <term-old>` and `git bisect <term-new>` instead
 of `git bisect good` and `git bisect bad` to mark commits.
 
-Bisect visualize
-~~~~~~~~~~~~~~~~
+Bisect visualize/view
+~~~~~~~~~~~~~~~~~~~~~
 
 To see the currently remaining suspects in 'gitk', issue the following
-command during the bisection process:
+command during the bisection process (the subcommand `view` can be used
+as an alternative to `visualize`):
 
 ------------
 $ git bisect visualize
 ------------
 
-`view` may also be used as a synonym for `visualize`.
-
 If the `DISPLAY` environment variable is not set, 'git log' is used
 instead.  You can also give command-line options such as `-p` and
 `--stat`.
 
 ------------
-$ git bisect view --stat
+$ git bisect visualize --stat
 ------------
 
 Bisect log and bisect replay
index d6587c5..b3084c9 100644 (file)
@@ -14,7 +14,7 @@ SYNOPSIS
        [(--merged | --no-merged) [<commit>]]
        [--contains [<commit]] [--no-contains [<commit>]]
        [--points-at <object>] [--format=<format>] [<pattern>...]
-'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
+'git branch' [--track | --no-track] [-l] [-f] <branchname> [<start-point>]
 'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
 'git branch' --unset-upstream [<branchname>]
 'git branch' (-m | -M) [<oldbranch>] <newbranch>
@@ -86,7 +86,7 @@ OPTIONS
 --delete::
        Delete a branch. The branch must be fully merged in its
        upstream branch, or in `HEAD` if no upstream was set with
-       `--track` or `--set-upstream`.
+       `--track` or `--set-upstream-to`.
 
 -D::
        Shortcut for `--delete --force`.
@@ -281,6 +281,12 @@ start-point is either a local or remote-tracking branch.
        and the object it points at.  The format is the same as
        that of linkgit:git-for-each-ref[1].
 
+CONFIGURATION
+-------------
+`pager.branch` is only respected when listing branches, i.e., when
+`--list` is used or implied. The default is to use a pager.
+See linkgit:git-config[1].
+
 Examples
 --------
 
index e108b0f..bfa64ca 100644 (file)
@@ -264,6 +264,8 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
        local modifications in a submodule would be overwritten the checkout
        will fail unless `-f` is used. If nothing (or --no-recurse-submodules)
        is used, the work trees of submodules will not be updated.
+       Just like linkgit:git-submodule[1], this will detach the
+       submodules HEAD.
 
 <branch>::
        Branch to checkout; if it refers to a branch (i.e., a name that,
index 4edd09f..14da5fc 100644 (file)
@@ -180,6 +180,11 @@ See also <<FILES>>.
        value (but you can use `git config section.variable ~/`
        from the command line to let your shell do the expansion).
 
+--expiry-date::
+       `git config` will ensure that the output is converted from
+       a fixed or relative date-string to a timestamp. This option
+       has no effect when setting the value.
+
 -z::
 --null::
        For all options that output values and/or keys, always
index 1d420e4..dffa14a 100644 (file)
@@ -145,18 +145,25 @@ upstream::
        (behind), "<>" (ahead and behind), or "=" (in sync). `:track`
        also prints "[gone]" whenever unknown upstream ref is
        encountered. Append `:track,nobracket` to show tracking
-       information without brackets (i.e "ahead N, behind M").  Has
-       no effect if the ref does not have tracking information
-       associated with it.  All the options apart from `nobracket`
-       are mutually exclusive, but if used together the last option
-       is selected.
+       information without brackets (i.e "ahead N, behind M").
++
+For any remote-tracking branch `%(upstream)`, `%(upstream:remotename)`
+and `%(upstream:remoteref)` refer to the name of the remote and the
+name of the tracked remote ref, respectively. In other words, the
+remote-tracking branch can be updated explicitly and individually by
+using the refspec `%(upstream:remoteref):%(upstream)` to fetch from
+`%(upstream:remotename)`.
++
+Has no effect if the ref does not have tracking information associated
+with it.  All the options apart from `nobracket` are mutually exclusive,
+but if used together the last option is selected.
 
 push::
        The name of a local ref which represents the `@{push}`
        location for the displayed ref. Respects `:short`, `:lstrip`,
-       `:rstrip`, `:track`, and `:trackshort` options as `upstream`
-       does. Produces an empty string if no `@{push}` ref is
-       configured.
+       `:rstrip`, `:track`, `:trackshort`, `:remotename`, and `:remoteref`
+       options as `upstream` does. Produces an empty string if no `@{push}`
+       ref is configured.
 
 HEAD::
        '*' if HEAD matches current ref (the checked out branch), ' '
index d153c17..3ac3e3a 100644 (file)
@@ -9,7 +9,7 @@ git-ls-files - Show information about files in the index and the working tree
 SYNOPSIS
 --------
 [verse]
-'git ls-files' [-z] [-t] [-v]
+'git ls-files' [-z] [-t] [-v] [-f]
                (--[cached|deleted|others|ignored|stage|unmerged|killed|modified])*
                (-[c|d|o|i|s|u|k|m])*
                [--eol]
@@ -133,6 +133,11 @@ a space) at the start of each line:
        that are marked as 'assume unchanged' (see
        linkgit:git-update-index[1]).
 
+-f::
+       Similar to `-t`, but use lowercase letters for files
+       that are marked as 'fsmonitor valid' (see
+       linkgit:git-update-index[1]).
+
 --full-name::
        When run from a subdirectory, the command usually
        outputs paths relative to the current directory.  This
index b968b64..502e00e 100644 (file)
@@ -154,23 +154,71 @@ topic origin/master`, the history of remote-tracking branch
 `origin/master` may have been rewound and rebuilt, leading to a
 history of this shape:
 
-                        o---B1
+                        o---B2
                        /
-       ---o---o---B2--o---o---o---B (origin/master)
+       ---o---o---B1--o---o---o---B (origin/master)
                \
-                B3
+                B0
                  \
-                  Derived (topic)
+                  D0---D1---D (topic)
 
-where `origin/master` used to point at commits B3, B2, B1 and now it
+where `origin/master` used to point at commits B0, B1, B2 and now it
 points at B, and your `topic` branch was started on top of it back
-when `origin/master` was at B3. This mode uses the reflog of
-`origin/master` to find B3 as the fork point, so that the `topic`
-can be rebased on top of the updated `origin/master` by:
+when `origin/master` was at B0, and you built three commits, D0, D1,
+and D, on top of it.  Imagine that you now want to rebase the work
+you did on the topic on top of the updated origin/master.
+
+In such a case, `git merge-base origin/master topic` would return the
+parent of B0 in the above picture, but B0^..D is *not* the range of
+commits you would want to replay on top of B (it includes B0, which
+is not what you wrote; it is a commit the other side discarded when
+it moved its tip from B0 to B1).
+
+`git merge-base --fork-point origin/master topic` is designed to
+help in such a case.  It takes not only B but also B0, B1, and B2
+(i.e. old tips of the remote-tracking branches your repository's
+reflog knows about) into account to see on which commit your topic
+branch was built and finds B0, allowing you to replay only the
+commits on your topic, excluding the commits the other side later
+discarded.
+
+Hence
 
     $ fork_point=$(git merge-base --fork-point origin/master topic)
+
+will find B0, and
+
     $ git rebase --onto origin/master $fork_point topic
 
+will replay D0, D1 and D on top of B to create a new history of this
+shape:
+
+                        o---B2
+                       /
+       ---o---o---B1--o---o---o---B (origin/master)
+               \                   \
+                B0                  D0'--D1'--D' (topic - updated)
+                 \
+                  D0---D1---D (topic - old)
+
+A caveat is that older reflog entries in your repository may be
+expired by `git gc`.  If B0 no longer appears in the reflog of the
+remote-tracking branch `origin/master`, the `--fork-point` mode
+obviously cannot find it and fails, avoiding to give a random and
+useless result (such as the parent of B0, like the same command
+without the `--fork-point` option gives).
+
+Also, the remote-tracking branch you use the `--fork-point` mode
+with must be the one your topic forked from its tip.  If you forked
+from an older commit than the tip, this mode would not find the fork
+point (imagine in the above sample history B0 did not exist,
+origin/master started at B1, moved to B2 and then B, and you forked
+your topic at origin/master^ when origin/master was B1; the shape of
+the history would be the same as above, without B0, and the parent
+of B1 is what `git merge-base origin/master topic` correctly finds,
+but the `--fork-point` mode will not, because it is not one of the
+commits that used to be at the tip of origin/master).
+
 
 See also
 --------
index 9f3a78a..fc282e0 100644 (file)
@@ -97,8 +97,27 @@ configuration variable documented in linkgit:git-config[1].
        (and suppresses the output of submodule summaries when the config option
        `status.submoduleSummary` is set).
 
---ignored::
+--ignored[=<mode>]::
        Show ignored files as well.
++
+The mode parameter is used to specify the handling of ignored files.
+It is optional: it defaults to 'traditional'.
++
+The possible options are:
++
+       - 'traditional' - Shows ignored files and directories, unless
+                         --untracked-files=all is specifed, in which case
+                         individual files in ignored directories are
+                         displayed.
+       - 'no'          - Show no ignored files.
+       - 'matching'    - Shows ignored files and directories matching an
+                         ignore pattern.
++
+When 'matching' mode is specified, paths that explicity match an
+ignored pattern are shown. If a directory matches an ignore pattern,
+then it is shown, but not paths contained in the ignored directory. If
+a directory does not match an ignore pattern, but all contents are
+ignored, then the directory is not shown, but all contents are shown.
 
 -z::
        Terminate entries with NUL, instead of LF.  This implies
index 75c7dd9..bdb0342 100644 (file)
@@ -16,9 +16,11 @@ SYNOPSIS
             [--chmod=(+|-)x]
             [--[no-]assume-unchanged]
             [--[no-]skip-worktree]
+            [--[no-]fsmonitor-valid]
             [--ignore-submodules]
             [--[no-]split-index]
             [--[no-|test-|force-]untracked-cache]
+            [--[no-]fsmonitor]
             [--really-refresh] [--unresolve] [--again | -g]
             [--info-only] [--index-info]
             [-z] [--stdin] [--index-version <n>]
@@ -111,6 +113,12 @@ you will need to handle the situation manually.
        set and unset the "skip-worktree" bit for the paths. See
        section "Skip-worktree bit" below for more information.
 
+--[no-]fsmonitor-valid::
+       When one of these flags is specified, the object name recorded
+       for the paths are not updated. Instead, these options
+       set and unset the "fsmonitor valid" bit for the paths. See
+       section "File System Monitor" below for more information.
+
 -g::
 --again::
        Runs 'git update-index' itself on the paths whose index
@@ -201,6 +209,15 @@ will remove the intended effect of the option.
        `--untracked-cache` used to imply `--test-untracked-cache` but
        this option would enable the extension unconditionally.
 
+--fsmonitor::
+--no-fsmonitor::
+       Enable or disable files system monitor feature. These options
+       take effect whatever the value of the `core.fsmonitor`
+       configuration variable (see linkgit:git-config[1]). But a warning
+       is emitted when the change goes against the configured value, as
+       the configured value will take effect next time the index is
+       read and this will remove the intended effect of the option.
+
 \--::
        Do not interpret any more arguments as options.
 
@@ -447,6 +464,34 @@ command reads the index; while when `--[no-|force-]untracked-cache`
 are used, the untracked cache is immediately added to or removed from
 the index.
 
+File System Monitor
+-------------------
+
+This feature is intended to speed up git operations for repos that have
+large working directories.
+
+It enables git to work together with a file system monitor (see the
+"fsmonitor-watchman" section of linkgit:githooks[5]) that can
+inform it as to what files have been modified. This enables git to avoid
+having to lstat() every file to find modified files.
+
+When used in conjunction with the untracked cache, it can further improve
+performance by avoiding the cost of scanning the entire working directory
+looking for new files.
+
+If you want to enable (or disable) this feature, it is easier to use
+the `core.fsmonitor` configuration variable (see
+linkgit:git-config[1]) than using the `--fsmonitor` option to
+`git update-index` in each repository, especially if you want to do so
+across all repositories you use, because you can set the configuration
+variable to `true` (or `false`) in your `$HOME/.gitconfig` just once
+and have it affect all repositories you touch.
+
+When the `core.fsmonitor` configuration variable is changed, the
+file system monitor is added to or removed from the index the next time
+a command reads the index. When `--[no-]fsmonitor` are used, the file
+system monitor is immediately added to or removed from the index.
+
 Configuration
 -------------
 
index 7a1d629..483a1f3 100644 (file)
@@ -595,6 +595,10 @@ into it.
 Unsetting the variable, or setting it to empty, "0" or
 "false" (case insensitive) disables trace messages.
 
+`GIT_TRACE_FSMONITOR`::
+       Enables trace messages for the filesystem monitor extension.
+       See `GIT_TRACE` for available trace output options.
+
 `GIT_TRACE_PACK_ACCESS`::
        Enables trace messages for all accesses to any packs. For each
        access, the pack file name and an offset in the pack is
@@ -709,6 +713,24 @@ of clones and fetches.
        the background which do not want to cause lock contention with
        other operations on the repository.  Defaults to `1`.
 
+`GIT_REDIRECT_STDIN`::
+`GIT_REDIRECT_STDOUT`::
+`GIT_REDIRECT_STDERR`::
+       Windows-only: allow redirecting the standard input/output/error
+       handles to paths specified by the environment variables. This is
+       particularly useful in multi-threaded applications where the
+       canonical way to pass standard handles via `CreateProcess()` is
+       not an option because it would require the handles to be marked
+       inheritable (and consequently *every* spawned process would
+       inherit them, possibly blocking regular Git operations). The
+       primary intended use case is to use named pipes for communication
+       (e.g. `\\.\pipe\my-git-stdin-123`).
++
+Two special values are supported: `off` will simply close the
+corresponding standard handle, and if `GIT_REDIRECT_STDERR` is
+`2>&1`, standard error will be redirected to the same handle as
+standard output.
+
 Discussion[[Discussion]]
 ------------------------
 
index 4c68bc1..30687de 100644 (file)
@@ -232,8 +232,7 @@ From a clean working directory:
 
 -------------------------------------------------
 $ echo "* text=auto" >.gitattributes
-$ git read-tree --empty   # Clean index, force re-scan of working directory
-$ git add .
+$ git add --renormalize .
 $ git status        # Show files that will be normalized
 $ git commit -m "Introduce end-of-line normalization"
 -------------------------------------------------
@@ -328,6 +327,9 @@ You can declare that a filter turns a content that by itself is unusable
 into a usable content by setting the filter.<driver>.required configuration
 variable to `true`.
 
+Note: Whenever the clean filter is changed, the repo should be renormalized:
+$ git add --renormalize .
+
 For example, in .gitattributes, you would assign the `filter`
 attribute for paths.
 
index 5d3f455..0bb0042 100644 (file)
@@ -454,6 +454,34 @@ the name of the file that holds the e-mail to be sent.  Exiting with a
 non-zero status causes 'git send-email' to abort before sending any
 e-mails.
 
+fsmonitor-watchman
+~~~~~~~~~~~~~~~~~~
+
+This hook is invoked when the configuration option core.fsmonitor is
+set to .git/hooks/fsmonitor-watchman.  It takes two arguments, a version
+(currently 1) and the time in elapsed nanoseconds since midnight,
+January 1, 1970.
+
+The hook should output to stdout the list of all files in the working
+directory that may have changed since the requested time.  The logic
+should be inclusive so that it does not miss any potential changes.
+The paths should be relative to the root of the working directory
+and be separated by a single NUL.
+
+It is OK to include files which have not actually changed.  All changes
+including newly-created and deleted files should be included. When
+files are renamed, both the old and the new name should be included.
+
+Git will limit what files it checks for changes as well as which
+directories are checked for untracked files based on the path names
+given.
+
+An optimized way to tell git "all files have changed" is to return
+the filename '/'.
+
+The exit status determines whether git will use the data from the
+hook to limit its search.  On error, it will fall back to verifying
+all files and folders.
 
 GIT
 ---
index a09d597..fd5d748 100644 (file)
@@ -58,11 +58,12 @@ diff-algorithm=[patience|minimal|histogram|myers];;
 ignore-space-change;;
 ignore-all-space;;
 ignore-space-at-eol;;
+ignore-cr-at-eol;;
        Treats lines with the indicated type of whitespace change as
        unchanged for the sake of a three-way merge.  Whitespace
        changes mixed with other changes to a line are not ignored.
-       See also linkgit:git-diff[1] `-b`, `-w`, and
-       `--ignore-space-at-eol`.
+       See also linkgit:git-diff[1] `-b`, `-w`,
+       `--ignore-space-at-eol`, and `--ignore-cr-at-eol`.
 +
 * If 'their' version only introduces whitespace changes to a line,
   'our' version is used;
index 6c77b49..7fae00f 100644 (file)
@@ -22,16 +22,20 @@ The notable options are:
 
 `flags`::
 
-       A bit-field of options (the `*IGNORED*` flags are mutually exclusive):
+       A bit-field of options:
 
 `DIR_SHOW_IGNORED`:::
 
-       Return just ignored files in `entries[]`, not untracked files.
+       Return just ignored files in `entries[]`, not untracked
+       files. This flag is mutually exclusive with
+       `DIR_SHOW_IGNORED_TOO`.
 
 `DIR_SHOW_IGNORED_TOO`:::
 
-       Similar to `DIR_SHOW_IGNORED`, but return ignored files in `ignored[]`
-       in addition to untracked files in `entries[]`.
+       Similar to `DIR_SHOW_IGNORED`, but return ignored files in
+       `ignored[]` in addition to untracked files in
+       `entries[]`. This flag is mutually exclusive with
+       `DIR_SHOW_IGNORED`.
 
 `DIR_KEEP_UNTRACKED_CONTENTS`:::
 
@@ -39,6 +43,21 @@ The notable options are:
        untracked contents of untracked directories are also returned in
        `entries[]`.
 
+`DIR_SHOW_IGNORED_TOO_MODE_MATCHING`:::
+
+       Only has meaning if `DIR_SHOW_IGNORED_TOO` is also set; if
+       this is set, returns ignored files and directories that match
+       an exclude pattern. If a directory matches an exclude pattern,
+       then the directory is returned and the contained paths are
+       not. A directory that does not match an exclude pattern will
+       not be returned even if all of its contents are ignored. In
+       this case, the contents are returned as individual entries.
++
+If this is set, files and directories that explicity match an ignore
+pattern are reported. Implicity ignored directories (directories that
+do not match an ignore pattern, but whose contents are all ignored)
+are not reported, instead all of the contents are reported.
+
 `DIR_COLLECT_IGNORED`:::
 
        Special mode for git-add. Return ignored files in `ignored[]` and
index ade0b0c..db35726 100644 (file)
@@ -295,3 +295,22 @@ The remaining data of each directory block is grouped by type:
     in the previous ewah bitmap.
 
   - One NUL.
+
+== File System Monitor cache
+
+  The file system monitor cache tracks files for which the core.fsmonitor
+  hook has told us about changes.  The signature for this extension is
+  { 'F', 'S', 'M', 'N' }.
+
+  The extension starts with
+
+  - 32-bit version number: the current supported version is 1.
+
+  - 64-bit time: the extension data reflects all changes through the given
+       time which is stored as the nanoseconds elapsed since midnight,
+       January 1, 1970.
+
+  - 32-bit bitmap size: the size of the CE_FSMONITOR_VALID bitmap.
+
+  - An ewah bitmap, the n-th bit indicates whether the n-th index entry
+    is not CE_FSMONITOR_VALID.
index cd75985..e53750c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -646,7 +646,9 @@ TEST_PROGRAMS_NEED_X += test-ctype
 TEST_PROGRAMS_NEED_X += test-config
 TEST_PROGRAMS_NEED_X += test-date
 TEST_PROGRAMS_NEED_X += test-delta
+TEST_PROGRAMS_NEED_X += test-drop-caches
 TEST_PROGRAMS_NEED_X += test-dump-cache-tree
+TEST_PROGRAMS_NEED_X += test-dump-fsmonitor
 TEST_PROGRAMS_NEED_X += test-dump-split-index
 TEST_PROGRAMS_NEED_X += test-dump-untracked-cache
 TEST_PROGRAMS_NEED_X += test-fake-ssh
@@ -794,6 +796,7 @@ LIB_OBJS += ewah/ewah_rlw.o
 LIB_OBJS += exec_cmd.o
 LIB_OBJS += fetch-pack.o
 LIB_OBJS += fsck.o
+LIB_OBJS += fsmonitor.o
 LIB_OBJS += gettext.o
 LIB_OBJS += gpg-interface.o
 LIB_OBJS += graph.o
@@ -1940,7 +1943,8 @@ $(SCRIPT_LIB) : % : %.sh GIT-SCRIPT-DEFINES
 
 git.res: git.rc GIT-VERSION-FILE
        $(QUIET_RC)$(RC) \
-         $(join -DMAJOR= -DMINOR=, $(wordlist 1,2,$(subst -, ,$(subst ., ,$(GIT_VERSION))))) \
+         $(join -DMAJOR= -DMINOR= -DMICRO= -DPATCHLEVEL=, $(wordlist 1, 4, \
+           $(shell echo $(GIT_VERSION) 0 0 0 0 | tr '.a-zA-Z-' ' '))) \
          -DGIT_VERSION="\\\"$(GIT_VERSION)\\\"" -i $< -o $@
 
 # This makes sure we depend on the NO_PERL setting itself.
diff --git a/apply.c b/apply.c
index d676deb..321a9fa 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -300,52 +300,33 @@ static uint32_t hash_line(const char *cp, size_t len)
 static int fuzzy_matchlines(const char *s1, size_t n1,
                            const char *s2, size_t n2)
 {
-       const char *last1 = s1 + n1 - 1;
-       const char *last2 = s2 + n2 - 1;
-       int result = 0;
+       const char *end1 = s1 + n1;
+       const char *end2 = s2 + n2;
 
        /* ignore line endings */
-       while ((*last1 == '\r') || (*last1 == '\n'))
-               last1--;
-       while ((*last2 == '\r') || (*last2 == '\n'))
-               last2--;
-
-       /* skip leading whitespaces, if both begin with whitespace */
-       if (s1 <= last1 && s2 <= last2 && isspace(*s1) && isspace(*s2)) {
-               while (isspace(*s1) && (s1 <= last1))
-                       s1++;
-               while (isspace(*s2) && (s2 <= last2))
-                       s2++;
-       }
-       /* early return if both lines are empty */
-       if ((s1 > last1) && (s2 > last2))
-               return 1;
-       while (!result) {
-               result = *s1++ - *s2++;
-               /*
-                * Skip whitespace inside. We check for whitespace on
-                * both buffers because we don't want "a b" to match
-                * "ab"
-                */
-               if (isspace(*s1) && isspace(*s2)) {
-                       while (isspace(*s1) && s1 <= last1)
+       while (s1 < end1 && (end1[-1] == '\r' || end1[-1] == '\n'))
+               end1--;
+       while (s2 < end2 && (end2[-1] == '\r' || end2[-1] == '\n'))
+               end2--;
+
+       while (s1 < end1 && s2 < end2) {
+               if (isspace(*s1)) {
+                       /*
+                        * Skip whitespace. We check on both buffers
+                        * because we don't want "a b" to match "ab".
+                        */
+                       if (!isspace(*s2))
+                               return 0;
+                       while (s1 < end1 && isspace(*s1))
                                s1++;
-                       while (isspace(*s2) && s2 <= last2)
+                       while (s2 < end2 && isspace(*s2))
                                s2++;
-               }
-               /*
-                * If we reached the end on one side only,
-                * lines don't match
-                */
-               if (
-                   ((s2 > last2) && (s1 <= last1)) ||
-                   ((s1 > last1) && (s2 <= last2)))
+               } else if (*s1++ != *s2++)
                        return 0;
-               if ((s1 > last1) && (s2 > last2))
-                       break;
        }
 
-       return !result;
+       /* If we reached the end on one side only, lines don't match. */
+       return s1 == end1 && s2 == end2;
 }
 
 static void add_line_info(struct image *img, const char *bol, size_t len, unsigned flag)
@@ -2972,6 +2953,8 @@ static int apply_one_fragment(struct apply_state *state,
            newlines.len > 0 && newlines.buf[newlines.len - 1] == '\n') {
                old--;
                strbuf_setlen(&newlines, newlines.len - 1);
+               preimage.line_allocated[preimage.nr - 1].len--;
+               postimage.line_allocated[postimage.nr - 1].len--;
        }
 
        leading = frag->leading;
index fda2c4a..0fca17c 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -226,10 +226,11 @@ static struct commit_list *best_bisection_sorted(struct commit_list *list, int n
                add_name_decoration(DECORATION_NONE, buf.buf, obj);
 
                p->item = array[i].commit;
-               p = p->next;
+               if (i < cnt - 1)
+                       p = p->next;
        }
-       if (p)
-               p->next = NULL;
+       free_commit_list(p->next);
+       p->next = NULL;
        strbuf_release(&buf);
        free(array);
        return list;
@@ -360,28 +361,29 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
                return best_bisection_sorted(list, nr);
 }
 
-struct commit_list *find_bisection(struct commit_list *list,
-                                         int *reaches, int *all,
-                                         int find_all)
+void find_bisection(struct commit_list **commit_list, int *reaches,
+                   int *all, int find_all)
 {
        int nr, on_list;
-       struct commit_list *p, *best, *next, *last;
+       struct commit_list *list, *p, *best, *next, *last;
        int *weights;
 
-       show_list("bisection 2 entry", 0, 0, list);
+       show_list("bisection 2 entry", 0, 0, *commit_list);
 
        /*
         * Count the number of total and tree-changing items on the
         * list, while reversing the list.
         */
-       for (nr = on_list = 0, last = NULL, p = list;
+       for (nr = on_list = 0, last = NULL, p = *commit_list;
             p;
             p = next) {
                unsigned flags = p->item->object.flags;
 
                next = p->next;
-               if (flags & UNINTERESTING)
+               if (flags & UNINTERESTING) {
+                       free(p);
                        continue;
+               }
                p->next = last;
                last = p;
                if (!(flags & TREESAME))
@@ -397,12 +399,16 @@ struct commit_list *find_bisection(struct commit_list *list,
        /* Do the real work of finding bisection commit. */
        best = do_find_bisection(list, nr, weights, find_all);
        if (best) {
-               if (!find_all)
+               if (!find_all) {
+                       list->item = best->item;
+                       free_commit_list(list->next);
+                       best = list;
                        best->next = NULL;
+               }
                *reaches = weight(best);
        }
        free(weights);
-       return best;
+       *commit_list = best;
 }
 
 static int register_ref(const char *refname, const struct object_id *oid,
@@ -960,8 +966,7 @@ int bisect_next_all(const char *prefix, int no_checkout)
 
        bisect_common(&revs);
 
-       revs.commits = find_bisection(revs.commits, &reaches, &all,
-                                      !!skipped_revs.nr);
+       find_bisection(&revs.commits, &reaches, &all, !!skipped_revs.nr);
        revs.commits = managed_skipped(revs.commits, &tried);
 
        if (!revs.commits) {
@@ -1068,7 +1073,7 @@ int bisect_clean_state(void)
        struct string_list refs_for_removal = STRING_LIST_INIT_NODUP;
        for_each_ref_in("refs/bisect", mark_for_removal, (void *) &refs_for_removal);
        string_list_append(&refs_for_removal, xstrdup("BISECT_HEAD"));
-       result = delete_refs("bisect: remove", &refs_for_removal, REF_NODEREF);
+       result = delete_refs("bisect: remove", &refs_for_removal, REF_NO_DEREF);
        refs_for_removal.strdup_strings = 1;
        string_list_clear(&refs_for_removal, 0);
        unlink_or_warn(git_path_bisect_expected_rev());
index 0ae63d4..a5d9248 100644 (file)
--- a/bisect.h
+++ b/bisect.h
@@ -1,9 +1,15 @@
 #ifndef BISECT_H
 #define BISECT_H
 
-extern struct commit_list *find_bisection(struct commit_list *list,
-                                         int *reaches, int *all,
-                                         int find_all);
+/*
+ * Find bisection. If something is found, `reaches` will be the number of
+ * commits that the best commit reaches. `all` will be the count of
+ * non-SAMETREE commits. If nothing is found, `list` will be NULL.
+ * Otherwise, it will be either all non-SAMETREE commits or the single
+ * best commit, as chosen by `find_all`.
+ */
+extern void find_bisection(struct commit_list **list, int *reaches, int *all,
+                          int find_all);
 
 extern struct commit_list *filter_skipped(struct commit_list *list,
                                          struct commit_list **tried,
diff --git a/blame.c b/blame.c
index c3060de..2893f3c 100644 (file)
--- a/blame.c
+++ b/blame.c
@@ -209,7 +209,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
 
                switch (st.st_mode & S_IFMT) {
                case S_IFREG:
-                       if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
+                       if (opt->flags.allow_textconv &&
                            textconv_object(read_from, mode, &null_oid, 0, &buf_ptr, &buf_len))
                                strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
                        else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
@@ -293,7 +293,7 @@ static void fill_origin_blob(struct diff_options *opt,
                unsigned long file_size;
 
                (*num_read_blob)++;
-               if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
+               if (opt->flags.allow_textconv &&
                    textconv_object(o->path, o->mode, &o->blob_oid, 1, &file->ptr, &file_size))
                        ;
                else
@@ -541,7 +541,7 @@ static struct blame_origin *find_origin(struct commit *parent,
         * same and diff-tree is fairly efficient about this.
         */
        diff_setup(&diff_opts);
-       DIFF_OPT_SET(&diff_opts, RECURSIVE);
+       diff_opts.flags.recursive = 1;
        diff_opts.detect_rename = 0;
        diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
        paths[0] = origin->path;
@@ -615,7 +615,7 @@ static struct blame_origin *find_rename(struct commit *parent,
        int i;
 
        diff_setup(&diff_opts);
-       DIFF_OPT_SET(&diff_opts, RECURSIVE);
+       diff_opts.flags.recursive = 1;
        diff_opts.detect_rename = DIFF_DETECT_RENAME;
        diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
        diff_opts.single_follow = origin->path;
@@ -1238,7 +1238,7 @@ static void find_copy_in_parent(struct blame_scoreboard *sb,
                return; /* nothing remains for this target */
 
        diff_setup(&diff_opts);
-       DIFF_OPT_SET(&diff_opts, RECURSIVE);
+       diff_opts.flags.recursive = 1;
        diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
 
        diff_setup_done(&diff_opts);
@@ -1253,7 +1253,7 @@ static void find_copy_in_parent(struct blame_scoreboard *sb,
        if ((opt & PICKAXE_BLAME_COPY_HARDEST)
            || ((opt & PICKAXE_BLAME_COPY_HARDER)
                && (!porigin || strcmp(target->path, porigin->path))))
-               DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);
+               diff_opts.flags.find_copies_harder = 1;
 
        if (is_null_oid(&target->commit->object.oid))
                do_diff_cache(&parent->tree->object.oid, &diff_opts);
@@ -1262,7 +1262,7 @@ static void find_copy_in_parent(struct blame_scoreboard *sb,
                              &target->commit->tree->object.oid,
                              "", &diff_opts);
 
-       if (!DIFF_OPT_TST(&diff_opts, FIND_COPIES_HARDER))
+       if (!diff_opts.flags.find_copies_harder)
                diffcore_std(&diff_opts);
 
        do {
@@ -1825,7 +1825,7 @@ void setup_scoreboard(struct blame_scoreboard *sb, const char *path, struct blam
                if (fill_blob_sha1_and_mode(o))
                        die(_("no such path %s in %s"), path, final_commit_name);
 
-               if (DIFF_OPT_TST(&sb->revs->diffopt, ALLOW_TEXTCONV) &&
+               if (sb->revs->diffopt.flags.allow_textconv &&
                    textconv_object(path, o->mode, &o->blob_oid, 1, (char **) &sb->final_buf,
                                    &sb->final_buf_size))
                        ;
index 62f7b0d..fe1e1c3 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -178,24 +178,40 @@ int read_branch_desc(struct strbuf *buf, const char *branch_name)
        return 0;
 }
 
-int validate_new_branchname(const char *name, struct strbuf *ref,
-                           int force, int attr_only)
+/*
+ * Check if 'name' can be a valid name for a branch; die otherwise.
+ * Return 1 if the named branch already exists; return 0 otherwise.
+ * Fill ref with the full refname for the branch.
+ */
+int validate_branchname(const char *name, struct strbuf *ref)
 {
        if (strbuf_check_branch_ref(ref, name))
                die(_("'%s' is not a valid branch name."), name);
 
-       if (!ref_exists(ref->buf))
+       return ref_exists(ref->buf);
+}
+
+/*
+ * Check if a branch 'name' can be created as a new branch; die otherwise.
+ * 'force' can be used when it is OK for the named branch already exists.
+ * Return 1 if the named branch already exists; return 0 otherwise.
+ * Fill ref with the full refname for the branch.
+ */
+int validate_new_branchname(const char *name, struct strbuf *ref, int force)
+{
+       const char *head;
+
+       if (!validate_branchname(name, ref))
                return 0;
-       else if (!force && !attr_only)
-               die(_("A branch named '%s' already exists."), ref->buf + strlen("refs/heads/"));
 
-       if (!attr_only) {
-               const char *head;
+       if (!force)
+               die(_("A branch named '%s' already exists."),
+                   ref->buf + strlen("refs/heads/"));
+
+       head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
+       if (!is_bare_repository() && head && !strcmp(head, ref->buf))
+               die(_("Cannot force update the current branch."));
 
-               head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
-               if (!is_bare_repository() && head && !strcmp(head, ref->buf))
-                       die(_("Cannot force update the current branch."));
-       }
        return 1;
 }
 
@@ -242,9 +258,9 @@ void create_branch(const char *name, const char *start_name,
        if (track == BRANCH_TRACK_EXPLICIT || track == BRANCH_TRACK_OVERRIDE)
                explicit_tracking = 1;
 
-       if (validate_new_branchname(name, &ref, force,
-                                   track == BRANCH_TRACK_OVERRIDE ||
-                                   clobber_head)) {
+       if ((track == BRANCH_TRACK_OVERRIDE || clobber_head)
+           ? validate_branchname(name, &ref)
+           : validate_new_branchname(name, &ref, force)) {
                if (!force)
                        dont_change_ref = 1;
                else
index b077885..be5e5d1 100644 (file)
--- a/branch.h
+++ b/branch.h
@@ -23,22 +23,19 @@ void create_branch(const char *name, const char *start_name,
                   int clobber_head, int quiet, enum branch_track track);
 
 /*
- * Validates that the requested branch may be created, returning the
- * interpreted ref in ref, force indicates whether (non-head) branches
- * may be overwritten. A non-zero return value indicates that the force
- * parameter was non-zero and the branch already exists.
- *
- * Contrary to all of the above, when attr_only is 1, the caller is
- * not interested in verifying if it is Ok to update the named
- * branch to point at a potentially different commit. It is merely
- * asking if it is OK to change some attribute for the named branch
- * (e.g. tracking upstream).
- *
- * NEEDSWORK: This needs to be split into two separate functions in the
- * longer run for sanity.
- *
+ * Check if 'name' can be a valid name for a branch; die otherwise.
+ * Return 1 if the named branch already exists; return 0 otherwise.
+ * Fill ref with the full refname for the branch.
+ */
+extern int validate_branchname(const char *name, struct strbuf *ref);
+
+/*
+ * Check if a branch 'name' can be created as a new branch; die otherwise.
+ * 'force' can be used when it is OK for the named branch already exists.
+ * Return 1 if the named branch already exists; return 0 otherwise.
+ * Fill ref with the full refname for the branch.
  */
-int validate_new_branchname(const char *name, struct strbuf *ref, int force, int attr_only);
+extern int validate_new_branchname(const char *name, struct strbuf *ref, int force);
 
 /*
  * Remove information about the state of working on the current
index a648cf4..bf01d89 100644 (file)
@@ -26,6 +26,7 @@ static const char * const builtin_add_usage[] = {
 };
 static int patch_interactive, add_interactive, edit_interactive;
 static int take_worktree_changes;
+static int add_renormalize;
 
 struct update_callback_data {
        int flags;
@@ -116,13 +117,32 @@ int add_files_to_cache(const char *prefix,
        rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
        rev.diffopt.format_callback = update_callback;
        rev.diffopt.format_callback_data = &data;
-       rev.diffopt.flags |= DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG;
+       rev.diffopt.flags.override_submodule_config = 1;
        rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
        run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
        clear_pathspec(&rev.prune_data);
        return !!data.add_errors;
 }
 
+static int renormalize_tracked_files(const struct pathspec *pathspec, int flags)
+{
+       int i, retval = 0;
+
+       for (i = 0; i < active_nr; i++) {
+               struct cache_entry *ce = active_cache[i];
+
+               if (ce_stage(ce))
+                       continue; /* do not touch unmerged paths */
+               if (!S_ISREG(ce->ce_mode) && !S_ISLNK(ce->ce_mode))
+                       continue; /* do not touch non blobs */
+               if (pathspec && !ce_path_match(ce, pathspec, NULL))
+                       continue;
+               retval |= add_file_to_cache(ce->name, flags | HASH_RENORMALIZE);
+       }
+
+       return retval;
+}
+
 static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec, int prefix)
 {
        char *seen;
@@ -218,7 +238,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
        argc = setup_revisions(argc, argv, &rev, NULL);
        rev.diffopt.output_format = DIFF_FORMAT_PATCH;
        rev.diffopt.use_color = 0;
-       DIFF_OPT_SET(&rev.diffopt, IGNORE_DIRTY_SUBMODULES);
+       rev.diffopt.flags.ignore_dirty_submodules = 1;
        out = open(file, O_CREAT | O_WRONLY, 0666);
        if (out < 0)
                die(_("Could not open '%s' for writing."), file);
@@ -276,6 +296,7 @@ static struct option builtin_add_options[] = {
        OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")),
        OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files")),
        OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")),
+       OPT_BOOL(0, "renormalize", &add_renormalize, N_("renormalize EOL of tracked files (implies -u)")),
        OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that the path will be added later")),
        OPT_BOOL('A', "all", &addremove_explicit, N_("add changes from all tracked and untracked files")),
        { OPTION_CALLBACK, 0, "ignore-removal", &addremove_explicit,
@@ -406,7 +427,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                          chmod_arg[1] != 'x' || chmod_arg[2]))
                die(_("--chmod param '%s' must be either -x or +x"), chmod_arg);
 
-       add_new_files = !take_worktree_changes && !refresh_only;
+       add_new_files = !take_worktree_changes && !refresh_only && !add_renormalize;
        require_pathspec = !(take_worktree_changes || (0 < addremove_explicit));
 
        hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
@@ -500,7 +521,10 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 
        plug_bulk_checkin();
 
-       exit_status |= add_files_to_cache(prefix, &pathspec, flags);
+       if (add_renormalize)
+               exit_status |= renormalize_tracked_files(&pathspec, flags);
+       else
+               exit_status |= add_files_to_cache(prefix, &pathspec, flags);
 
        if (add_new_files)
                exit_status |= add_files(&dir, flags);
index 4b6f153..02853b3 100644 (file)
@@ -1157,9 +1157,9 @@ static int index_has_changes(struct strbuf *sb)
                struct diff_options opt;
 
                diff_setup(&opt);
-               DIFF_OPT_SET(&opt, EXIT_WITH_STATUS);
+               opt.flags.exit_with_status = 1;
                if (!sb)
-                       DIFF_OPT_SET(&opt, QUICK);
+                       opt.flags.quick = 1;
                do_diff_cache(&head, &opt);
                diffcore_std(&opt);
                for (i = 0; sb && i < diff_queued_diff.nr; i++) {
@@ -1168,7 +1168,7 @@ static int index_has_changes(struct strbuf *sb)
                        strbuf_addstr(sb, diff_queued_diff.queue[i]->two->path);
                }
                diff_flush(&opt);
-               return DIFF_OPT_TST(&opt, HAS_CHANGES) != 0;
+               return opt.flags.has_changes != 0;
        } else {
                for (i = 0; sb && i < active_nr; i++) {
                        if (i)
@@ -1409,8 +1409,8 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm
        rev_info.show_root_diff = 1;
        rev_info.diffopt.output_format = DIFF_FORMAT_PATCH;
        rev_info.no_commit_id = 1;
-       DIFF_OPT_SET(&rev_info.diffopt, BINARY);
-       DIFF_OPT_SET(&rev_info.diffopt, FULL_INDEX);
+       rev_info.diffopt.flags.binary = 1;
+       rev_info.diffopt.flags.full_index = 1;
        rev_info.diffopt.use_color = 0;
        rev_info.diffopt.file = fp;
        rev_info.diffopt.close_file = 1;
@@ -2148,7 +2148,7 @@ static void am_abort(struct am_state *state)
                           has_curr_head ? &curr_head : NULL, 0,
                           UPDATE_REFS_DIE_ON_ERR);
        else if (curr_branch)
-               delete_ref(NULL, curr_branch, NULL, REF_NODEREF);
+               delete_ref(NULL, curr_branch, NULL, REF_NO_DEREF);
 
        free(curr_branch);
        am_destroy(state);
index 35d2105..4b5fadc 100644 (file)
@@ -46,7 +46,7 @@ static int check_term_format(const char *term, const char *orig_term)
                return error(_("'%s' is not a valid term"), term);
 
        if (one_of(term, "help", "start", "skip", "next", "reset",
-                       "visualize", "replay", "log", "run", "terms", NULL))
+                       "visualize", "view", "replay", "log", "run", "terms", NULL))
                return error(_("can't use the builtin command '%s' as a term"), term);
 
        /*
index 67adaef..005f55a 100644 (file)
@@ -708,8 +708,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        git_config(git_blame_config, &output_option);
        init_revisions(&revs, NULL);
        revs.date_mode = blame_date_mode;
-       DIFF_OPT_SET(&revs.diffopt, ALLOW_TEXTCONV);
-       DIFF_OPT_SET(&revs.diffopt, FOLLOW_RENAMES);
+       revs.diffopt.flags.allow_textconv = 1;
+       revs.diffopt.flags.follow_renames = 1;
 
        save_commit_buffer = 0;
        dashdash_pos = 0;
@@ -734,9 +734,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                parse_revision_opt(&revs, &ctx, options, blame_opt_usage);
        }
 parse_done:
-       no_whole_file_rename = !DIFF_OPT_TST(&revs.diffopt, FOLLOW_RENAMES);
+       no_whole_file_rename = !revs.diffopt.flags.follow_renames;
        xdl_opts |= revs.diffopt.xdl_opts & XDF_INDENT_HEURISTIC;
-       DIFF_OPT_CLR(&revs.diffopt, FOLLOW_RENAMES);
+       revs.diffopt.flags.follow_renames = 0;
        argc = parse_options_end(&ctx);
 
        if (incremental || (output_option & OUTPUT_PORCELAIN)) {
@@ -803,7 +803,7 @@ parse_done:
        }
        blame_date_width -= 1; /* strip the null */
 
-       if (DIFF_OPT_TST(&revs.diffopt, FIND_COPIES_HARDER))
+       if (revs.diffopt.flags.find_copies_harder)
                opt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE |
                        PICKAXE_BLAME_COPY_HARDER);
 
index b1ed649..af95ad2 100644 (file)
@@ -258,7 +258,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                }
 
                if (delete_ref(NULL, name, is_null_oid(&oid) ? NULL : &oid,
-                              REF_NODEREF)) {
+                              REF_NO_DEREF)) {
                        error(remote_branch
                              ? _("Error deleting remote-tracking branch '%s'")
                              : _("Error deleting branch '%s'"),
@@ -463,7 +463,6 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
        struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
        struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
        int recovery = 0;
-       int clobber_head_ok;
 
        if (!oldname) {
                if (copy)
@@ -487,9 +486,10 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
         * A command like "git branch -M currentbranch currentbranch" cannot
         * cause the worktree to become inconsistent with HEAD, so allow it.
         */
-       clobber_head_ok = !strcmp(oldname, newname);
-
-       validate_new_branchname(newname, &newref, force, clobber_head_ok);
+       if (!strcmp(oldname, newname))
+               validate_branchname(newname, &newref);
+       else
+               validate_new_branchname(newname, &newref, force);
 
        reject_rebase_or_bisect_branch(oldref.buf);
 
@@ -675,6 +675,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                copy *= 2;
        }
 
+       if (list)
+               setup_auto_pager("branch", 1);
+
        if (delete) {
                if (!argc)
                        die(_("branch name required"));
@@ -793,9 +796,6 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        } else if (argc > 0 && argc <= 2) {
                struct branch *branch = branch_get(argv[0]);
 
-               if (!strcmp(argv[0], "HEAD"))
-                       die(_("it does not make sense to create 'HEAD' manually"));
-
                if (!branch)
                        die(_("no such branch '%s'"), argv[0]);
 
index 6c2b4cd..3faae38 100644 (file)
@@ -663,7 +663,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
                /* Nothing to do. */
        } else if (opts->force_detach || !new->path) {  /* No longer on any branch. */
                update_ref(msg.buf, "HEAD", &new->commit->object.oid, NULL,
-                          REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
+                          REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
                if (!opts->quiet) {
                        if (old->path &&
                            advice_detached_head && !opts->force_detach)
@@ -1287,11 +1287,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        if (opts.new_branch) {
                struct strbuf buf = STRBUF_INIT;
 
-               opts.branch_exists =
-                       validate_new_branchname(opts.new_branch, &buf,
-                                               !!opts.new_branch_force,
-                                               !!opts.new_branch_force);
-
+               if (opts.new_branch_force)
+                       opts.branch_exists = validate_branchname(opts.new_branch, &buf);
+               else
+                       opts.branch_exists =
+                               validate_new_branchname(opts.new_branch, &buf, 0);
                strbuf_release(&buf);
        }
 
index cf6eddc..b228457 100644 (file)
@@ -689,7 +689,7 @@ static void update_head(const struct ref *our, const struct ref *remote,
        } else if (our) {
                struct commit *c = lookup_commit_reference(&our->old_oid);
                /* --branch specifies a non-branch (i.e. tags), detach HEAD */
-               update_ref(msg, "HEAD", &c->object.oid, NULL, REF_NODEREF,
+               update_ref(msg, "HEAD", &c->object.oid, NULL, REF_NO_DEREF,
                           UPDATE_REFS_DIE_ON_ERR);
        } else if (remote) {
                /*
@@ -697,7 +697,7 @@ static void update_head(const struct ref *our, const struct ref *remote,
                 * HEAD points to a branch but we don't know which one.
                 * Detach HEAD in all these cases.
                 */
-               update_ref(msg, "HEAD", &remote->old_oid, NULL, REF_NODEREF,
+               update_ref(msg, "HEAD", &remote->old_oid, NULL, REF_NO_DEREF,
                           UPDATE_REFS_DIE_ON_ERR);
        }
 }
index c38542e..8a87701 100644 (file)
@@ -118,7 +118,7 @@ static int edit_flag = -1; /* unspecified */
 static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
 static int config_commit_verbose = -1; /* unspecified */
 static int no_post_rewrite, allow_empty_message;
-static char *untracked_files_arg, *force_date, *ignore_submodule_arg;
+static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
 static char *sign_commit;
 
 /*
@@ -139,7 +139,7 @@ static const char *cleanup_arg;
 static enum commit_whence whence;
 static int sequencer_in_use;
 static int use_editor = 1, include_status = 1;
-static int show_ignored_in_status, have_option_m;
+static int have_option_m;
 static struct strbuf message = STRBUF_INIT;
 
 static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
@@ -912,11 +912,12 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                         * submodules which were manually staged, which would
                         * be really confusing.
                         */
-                       int diff_flags = DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG;
+                       struct diff_flags flags = DIFF_FLAGS_INIT;
+                       flags.override_submodule_config = 1;
                        if (ignore_submodule_arg &&
                            !strcmp(ignore_submodule_arg, "all"))
-                               diff_flags |= DIFF_OPT_IGNORE_SUBMODULES;
-                       commitable = index_differs_from(parent, diff_flags, 1);
+                               flags.ignore_submodules = 1;
+                       commitable = index_differs_from(parent, &flags, 1);
                }
        }
        strbuf_release(&committer_ident);
@@ -1075,6 +1076,19 @@ static const char *find_author_by_nickname(const char *name)
        die(_("--author '%s' is not 'Name <email>' and matches no existing author"), name);
 }
 
+static void handle_ignored_arg(struct wt_status *s)
+{
+       if (!ignored_arg)
+               ; /* default already initialized */
+       else if (!strcmp(ignored_arg, "traditional"))
+               s->show_ignored_mode = SHOW_TRADITIONAL_IGNORED;
+       else if (!strcmp(ignored_arg, "no"))
+               s->show_ignored_mode = SHOW_NO_IGNORED;
+       else if (!strcmp(ignored_arg, "matching"))
+               s->show_ignored_mode = SHOW_MATCHING_IGNORED;
+       else
+               die(_("Invalid ignored mode '%s'"), ignored_arg);
+}
 
 static void handle_untracked_files_arg(struct wt_status *s)
 {
@@ -1363,8 +1377,10 @@ int cmd_status(int argc, const char **argv, const char *prefix)
                  N_("mode"),
                  N_("show untracked files, optional modes: all, normal, no. (Default: all)"),
                  PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
-               OPT_BOOL(0, "ignored", &show_ignored_in_status,
-                        N_("show ignored files")),
+               { OPTION_STRING, 0, "ignored", &ignored_arg,
+                 N_("mode"),
+                 N_("show ignored files, optional modes: traditional, matching, no. (Default: traditional)"),
+                 PARSE_OPT_OPTARG, NULL, (intptr_t)"traditional" },
                { OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, N_("when"),
                  N_("ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)"),
                  PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
@@ -1383,8 +1399,12 @@ int cmd_status(int argc, const char **argv, const char *prefix)
        finalize_deferred_config(&s);
 
        handle_untracked_files_arg(&s);
-       if (show_ignored_in_status)
-               s.show_ignored_files = 1;
+       handle_ignored_arg(&s);
+
+       if (s.show_ignored_mode == SHOW_MATCHING_IGNORED &&
+           s.show_untracked_files == SHOW_NO_UNTRACKED_FILES)
+               die(_("Unsupported combination of ignored and untracked-files arguments"));
+
        parse_pathspec(&s.pathspec, 0,
                       PATHSPEC_PREFER_FULL,
                       prefix, argv);
@@ -1730,7 +1750,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                                allow_fast_forward = 0;
                }
                if (allow_fast_forward)
-                       parents = reduce_heads(parents);
+                       reduce_heads_replace(&parents);
        } else {
                if (!reflog_msg)
                        reflog_msg = (whence == FROM_CHERRY_PICK)
index d13daee..ab5f954 100644 (file)
@@ -52,6 +52,7 @@ static int show_origin;
 #define TYPE_INT (1<<1)
 #define TYPE_BOOL_OR_INT (1<<2)
 #define TYPE_PATH (1<<3)
+#define TYPE_EXPIRY_DATE (1<<4)
 
 static struct option builtin_config_options[] = {
        OPT_GROUP(N_("Config file location")),
@@ -80,6 +81,7 @@ static struct option builtin_config_options[] = {
        OPT_BIT(0, "int", &types, N_("value is decimal number"), TYPE_INT),
        OPT_BIT(0, "bool-or-int", &types, N_("value is --bool or --int"), TYPE_BOOL_OR_INT),
        OPT_BIT(0, "path", &types, N_("value is a path (file or directory name)"), TYPE_PATH),
+       OPT_BIT(0, "expiry-date", &types, N_("value is an expiry date"), TYPE_EXPIRY_DATE),
        OPT_GROUP(N_("Other")),
        OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")),
        OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")),
@@ -159,6 +161,11 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value
                                return -1;
                        strbuf_addstr(buf, v);
                        free((char *)v);
+               } else if (types == TYPE_EXPIRY_DATE) {
+                       timestamp_t t;
+                       if (git_config_expiry_date(&t, key_, value_) < 0)
+                               return -1;
+                       strbuf_addf(buf, "%"PRItime, t);
                } else if (value_) {
                        strbuf_addstr(buf, value_);
                } else {
@@ -273,12 +280,13 @@ static char *normalize_value(const char *key, const char *value)
        if (!value)
                return NULL;
 
-       if (types == 0 || types == TYPE_PATH)
+       if (types == 0 || types == TYPE_PATH || types == TYPE_EXPIRY_DATE)
                /*
                 * We don't do normalization for TYPE_PATH here: If
                 * the path is like ~/foobar/, we prefer to store
                 * "~/foobar/" in the config file, and to expand the ~
                 * when retrieving the value.
+                * Also don't do normalization for expiry dates.
                 */
                return xstrdup(value);
        if (types == TYPE_INT)
index aa6f746..9808d06 100644 (file)
@@ -44,7 +44,7 @@ static void stuff_change(struct diff_options *opt,
            !oidcmp(old_oid, new_oid) && (old_mode == new_mode))
                return;
 
-       if (DIFF_OPT_TST(opt, REVERSE_DIFF)) {
+       if (opt->flags.reverse_diff) {
                SWAP(old_mode, new_mode);
                SWAP(old_oid, new_oid);
                SWAP(old_path, new_path);
@@ -349,8 +349,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        rev.diffopt.stat_graph_width = -1;
 
        /* Default to let external and textconv be used */
-       DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
-       DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
+       rev.diffopt.flags.allow_external = 1;
+       rev.diffopt.flags.allow_textconv = 1;
 
        if (nongit)
                die(_("Not a git repository"));
@@ -360,7 +360,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
                diff_setup_done(&rev.diffopt);
        }
 
-       DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
+       rev.diffopt.flags.recursive = 1;
 
        setup_diff_pager(&rev.diffopt);
 
index d74c73f..f8fe04c 100644 (file)
@@ -1066,7 +1066,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
                die("revision walk setup failed");
        revs.diffopt.format_callback = show_filemodify;
        revs.diffopt.format_callback_data = &paths_of_changed_objects;
-       DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
+       revs.diffopt.flags.recursive = 1;
        while ((commit = get_revision(&revs))) {
                if (has_unshown_parent(commit)) {
                        add_object_array(&commit->object, NULL, &commits);
index b69f7d3..22034f8 100644 (file)
@@ -571,7 +571,7 @@ static void find_merge_parents(struct merge_parents *result,
        head_commit = lookup_commit(head);
        if (head_commit)
                commit_list_insert(head_commit, &parents);
-       parents = reduce_heads(parents);
+       reduce_heads_replace(&parents);
 
        while (parents) {
                struct commit *cmit = pop_commit(&parents);
index 5f91116..04846d4 100644 (file)
@@ -759,6 +759,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
 
        if (keep_cache_objects) {
                verify_index_checksum = 1;
+               verify_ce_order = 1;
                read_cache();
                for (i = 0; i < active_nr; i++) {
                        unsigned int mode;
index ba9d4cd..6c1fa89 100644 (file)
@@ -121,20 +121,19 @@ static void cmd_log_init_defaults(struct rev_info *rev)
        if (fmt_pretty)
                get_commit_format(fmt_pretty, rev);
        if (default_follow)
-               DIFF_OPT_SET(&rev->diffopt, DEFAULT_FOLLOW_RENAMES);
+               rev->diffopt.flags.default_follow_renames = 1;
        rev->verbose_header = 1;
-       DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
+       rev->diffopt.flags.recursive = 1;
        rev->diffopt.stat_width = -1; /* use full terminal width */
        rev->diffopt.stat_graph_width = -1; /* respect statGraphWidth config */
        rev->abbrev_commit = default_abbrev_commit;
        rev->show_root_diff = default_show_root;
        rev->subject_prefix = fmt_patch_subject_prefix;
        rev->show_signature = default_show_signature;
-       DIFF_OPT_SET(&rev->diffopt, ALLOW_TEXTCONV);
+       rev->diffopt.flags.allow_textconv = 1;
 
        if (default_date_mode)
                parse_date_format(default_date_mode, &rev->date_mode);
-       rev->diffopt.touched_flags = 0;
 }
 
 static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
@@ -182,7 +181,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
                init_display_notes(&rev->notes_opt);
 
        if (rev->diffopt.pickaxe || rev->diffopt.filter ||
-           DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES))
+           rev->diffopt.flags.follow_renames)
                rev->always_show_header = 0;
 
        if (source)
@@ -392,7 +391,7 @@ static int cmd_log_walk(struct rev_info *rev)
                fclose(rev->diffopt.file);
 
        if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF &&
-           DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) {
+           rev->diffopt.flags.check_failed) {
                return 02;
        }
        return diff_result_code(&rev->diffopt, 0);
@@ -484,8 +483,8 @@ static int show_blob_object(const struct object_id *oid, struct rev_info *rev, c
        unsigned long size;
 
        fflush(rev->diffopt.file);
-       if (!DIFF_OPT_TOUCHED(&rev->diffopt, ALLOW_TEXTCONV) ||
-           !DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV))
+       if (!rev->diffopt.flags.textconv_set_via_cmdline ||
+           !rev->diffopt.flags.allow_textconv)
                return stream_blob_to_fd(1, oid, NULL, 0);
 
        if (get_oid_with_context(obj_name, GET_OID_RECORD_PATH,
@@ -667,9 +666,9 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
 static void log_setup_revisions_tweak(struct rev_info *rev,
                                      struct setup_revision_opt *opt)
 {
-       if (DIFF_OPT_TST(&rev->diffopt, DEFAULT_FOLLOW_RENAMES) &&
+       if (rev->diffopt.flags.default_follow_renames &&
            rev->prune_data.nr == 1)
-               DIFF_OPT_SET(&rev->diffopt, FOLLOW_RENAMES);
+               rev->diffopt.flags.follow_renames = 1;
 
        /* Turn --cc/-c into -p --cc/-c when -p was not given */
        if (!rev->diffopt.output_format && rev->combine_merges)
@@ -1341,7 +1340,7 @@ static void prepare_bases(struct base_tree_info *bases,
                return;
 
        diff_setup(&diffopt);
-       DIFF_OPT_SET(&diffopt, RECURSIVE);
+       diffopt.flags.recursive = 1;
        diff_setup_done(&diffopt);
 
        oidcpy(&bases->base_commit, &base->object.oid);
@@ -1512,7 +1511,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        rev.verbose_header = 1;
        rev.diff = 1;
        rev.max_parents = 1;
-       DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
+       rev.diffopt.flags.recursive = 1;
        rev.subject_prefix = fmt_patch_subject_prefix;
        memset(&s_r_opt, 0, sizeof(s_r_opt));
        s_r_opt.def = "HEAD";
@@ -1613,8 +1612,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 
        rev.zero_commit = zero_commit;
 
-       if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff)
-               DIFF_OPT_SET(&rev.diffopt, BINARY);
+       if (!rev.diffopt.flags.text && !no_binary_diff)
+               rev.diffopt.flags.binary = 1;
 
        if (rev.show_notes)
                init_display_notes(&rev.notes_opt);
index 8c713c4..2fc836e 100644 (file)
@@ -31,6 +31,7 @@ static int show_resolve_undo;
 static int show_modified;
 static int show_killed;
 static int show_valid_bit;
+static int show_fsmonitor_bit;
 static int line_terminator = '\n';
 static int debug_mode;
 static int show_eol;
@@ -86,7 +87,8 @@ static const char *get_tag(const struct cache_entry *ce, const char *tag)
 {
        static char alttag[4];
 
-       if (tag && *tag && show_valid_bit && (ce->ce_flags & CE_VALID)) {
+       if (tag && *tag && ((show_valid_bit && (ce->ce_flags & CE_VALID)) ||
+               (show_fsmonitor_bit && (ce->ce_flags & CE_FSMONITOR_VALID)))) {
                memcpy(alttag, tag, 3);
 
                if (isalpha(tag[0])) {
@@ -515,6 +517,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
                        N_("identify the file status with tags")),
                OPT_BOOL('v', NULL, &show_valid_bit,
                        N_("use lowercase letters for 'assume unchanged' files")),
+               OPT_BOOL('f', NULL, &show_fsmonitor_bit,
+                       N_("use lowercase letters for 'fsmonitor clean' files")),
                OPT_BOOL('c', "cached", &show_cached,
                        N_("show cached files in the output (default)")),
                OPT_BOOL('d', "deleted", &show_deleted,
@@ -584,7 +588,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
        for (i = 0; i < exclude_list.nr; i++) {
                add_exclude(exclude_list.items[i].string, "", 0, el, --exclude_args);
        }
-       if (show_tag || show_valid_bit) {
+       if (show_tag || show_valid_bit || show_fsmonitor_bit) {
                tag_cached = "H ";
                tag_unmerged = "M ";
                tag_removed = "R ";
index e99f540..3b76001 100644 (file)
@@ -9,20 +9,20 @@
 
 static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
 {
-       struct commit_list *result;
+       struct commit_list *result, *r;
 
        result = get_merge_bases_many_dirty(rev[0], rev_nr - 1, rev + 1);
 
        if (!result)
                return 1;
 
-       while (result) {
-               printf("%s\n", oid_to_hex(&result->item->object.oid));
+       for (r = result; r; r = r->next) {
+               printf("%s\n", oid_to_hex(&r->item->object.oid));
                if (!show_all)
-                       return 0;
-               result = result->next;
+                       break;
        }
 
+       free_commit_list(result);
        return 0;
 }
 
@@ -51,45 +51,47 @@ static struct commit *get_commit_reference(const char *arg)
 
 static int handle_independent(int count, const char **args)
 {
-       struct commit_list *revs = NULL;
-       struct commit_list *result;
+       struct commit_list *revs = NULL, *rev;
        int i;
 
        for (i = count - 1; i >= 0; i--)
                commit_list_insert(get_commit_reference(args[i]), &revs);
 
-       result = reduce_heads(revs);
-       if (!result)
+       reduce_heads_replace(&revs);
+
+       if (!revs)
                return 1;
 
-       while (result) {
-               printf("%s\n", oid_to_hex(&result->item->object.oid));
-               result = result->next;
-       }
+       for (rev = revs; rev; rev = rev->next)
+               printf("%s\n", oid_to_hex(&rev->item->object.oid));
+
+       free_commit_list(revs);
        return 0;
 }
 
 static int handle_octopus(int count, const char **args, int show_all)
 {
        struct commit_list *revs = NULL;
-       struct commit_list *result;
+       struct commit_list *result, *rev;
        int i;
 
        for (i = count - 1; i >= 0; i--)
                commit_list_insert(get_commit_reference(args[i]), &revs);
 
-       result = reduce_heads(get_octopus_merge_bases(revs));
+       result = get_octopus_merge_bases(revs);
+       free_commit_list(revs);
+       reduce_heads_replace(&result);
 
        if (!result)
                return 1;
 
-       while (result) {
-               printf("%s\n", oid_to_hex(&result->item->object.oid));
+       for (rev = result; rev; rev = rev->next) {
+               printf("%s\n", oid_to_hex(&rev->item->object.oid));
                if (!show_all)
-                       return 0;
-               result = result->next;
+                       break;
        }
 
+       free_commit_list(result);
        return 0;
 }
 
index beb0623..c84c6e0 100644 (file)
@@ -26,7 +26,7 @@ int cmd_merge_ours(int argc, const char **argv, const char *prefix)
         */
        if (read_cache() < 0)
                die_errno("read_cache failed");
-       if (index_differs_from("HEAD", 0, 0))
+       if (index_differs_from("HEAD", NULL, 0))
                exit(2);
        exit(0);
 }
index 6071dbf..612dd7b 100644 (file)
@@ -998,6 +998,7 @@ static struct commit_list *reduce_parents(struct commit *head_commit,
 
        /* Find what parents to record by checking independent ones. */
        parents = reduce_heads(remoteheads);
+       free_commit_list(remoteheads);
 
        remoteheads = NULL;
        remotes = &remoteheads;
index 12afdf1..a2a542f 100644 (file)
@@ -686,7 +686,7 @@ static int merge_abort(struct notes_merge_options *o)
 
        if (delete_ref(NULL, "NOTES_MERGE_PARTIAL", NULL, 0))
                ret += error(_("failed to delete ref NOTES_MERGE_PARTIAL"));
-       if (delete_ref(NULL, "NOTES_MERGE_REF", NULL, REF_NODEREF))
+       if (delete_ref(NULL, "NOTES_MERGE_REF", NULL, REF_NO_DEREF))
                ret += error(_("failed to delete ref NOTES_MERGE_REF"));
        if (notes_merge_abort(o))
                ret += error(_("failed to remove 'git notes merge' worktree"));
@@ -865,10 +865,10 @@ static int merge(int argc, const char **argv, const char *prefix)
                if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
                        die(_("failed to store link to current notes ref (%s)"),
                            default_notes_ref());
-               printf(_("Automatic notes merge failed. Fix conflicts in %s and "
-                        "commit the result with 'git notes merge --commit', or "
-                        "abort the merge with 'git notes merge --abort'.\n"),
-                      git_path(NOTES_MERGE_WORKTREE));
+               fprintf(stderr, _("Automatic notes merge failed. Fix conflicts in %s "
+                                 "and commit the result with 'git notes merge --commit', "
+                                 "or abort the merge with 'git notes merge --abort'.\n"),
+                       git_path(NOTES_MERGE_WORKTREE));
        }
 
        free_notes(t);
index a28f0ff..166b777 100644 (file)
@@ -113,6 +113,8 @@ static char *opt_depth;
 static char *opt_unshallow;
 static char *opt_update_shallow;
 static char *opt_refmap;
+static char *opt_ipv4;
+static char *opt_ipv6;
 
 static struct option pull_options[] = {
        /* Shared options */
@@ -218,6 +220,12 @@ static struct option pull_options[] = {
        OPT_PASSTHRU(0, "refmap", &opt_refmap, N_("refmap"),
                N_("specify fetch refmap"),
                PARSE_OPT_NONEG),
+       OPT_PASSTHRU('4',  "ipv4", &opt_ipv4, NULL,
+               N_("use IPv4 addresses only"),
+               PARSE_OPT_NOARG),
+       OPT_PASSTHRU('6',  "ipv6", &opt_ipv6, NULL,
+               N_("use IPv6 addresses only"),
+               PARSE_OPT_NOARG),
 
        OPT_END()
 };
@@ -522,6 +530,10 @@ static int run_fetch(const char *repo, const char **refspecs)
                argv_array_push(&args, opt_update_shallow);
        if (opt_refmap)
                argv_array_push(&args, opt_refmap);
+       if (opt_ipv4)
+               argv_array_push(&args, opt_ipv4);
+       if (opt_ipv6)
+               argv_array_push(&args, opt_ipv6);
 
        if (repo) {
                argv_array_push(&args, repo);
@@ -751,12 +763,15 @@ static int get_octopus_merge_base(struct object_id *merge_base,
        if (!is_null_oid(fork_point))
                commit_list_insert(lookup_commit_reference(fork_point), &revs);
 
-       result = reduce_heads(get_octopus_merge_bases(revs));
+       result = get_octopus_merge_bases(revs);
        free_commit_list(revs);
+       reduce_heads_replace(&result);
+
        if (!result)
                return 1;
 
        oidcpy(merge_base, &result->item->object.oid);
+       free_commit_list(result);
        return 0;
 }
 
index ab31a3b..2233725 100644 (file)
@@ -416,16 +416,6 @@ static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
        return ent;
 }
 
-static int parse_expire_cfg_value(const char *var, const char *value, timestamp_t *expire)
-{
-       if (!value)
-               return config_error_nonbool(var);
-       if (parse_expiry_date(value, expire))
-               return error(_("'%s' for '%s' is not a valid timestamp"),
-                            value, var);
-       return 0;
-}
-
 /* expiry timer slot */
 #define EXPIRE_TOTAL   01
 #define EXPIRE_UNREACH 02
@@ -443,11 +433,11 @@ static int reflog_expire_config(const char *var, const char *value, void *cb)
 
        if (!strcmp(key, "reflogexpire")) {
                slot = EXPIRE_TOTAL;
-               if (parse_expire_cfg_value(var, value, &expire))
+               if (git_config_expiry_date(&expire, var, value))
                        return -1;
        } else if (!strcmp(key, "reflogexpireunreachable")) {
                slot = EXPIRE_UNREACH;
-               if (parse_expire_cfg_value(var, value, &expire))
+               if (git_config_expiry_date(&expire, var, value))
                        return -1;
        } else
                return git_default_config(var, value, cb);
index a04ea50..d95bf90 100644 (file)
@@ -693,7 +693,7 @@ static int mv(int argc, const char **argv)
                read_ref_full(item->string, RESOLVE_REF_READING, &oid, &flag);
                if (!(flag & REF_ISSYMREF))
                        continue;
-               if (delete_ref(NULL, item->string, NULL, REF_NODEREF))
+               if (delete_ref(NULL, item->string, NULL, REF_NO_DEREF))
                        die(_("deleting '%s' failed"), item->string);
        }
        for (i = 0; i < remote_branches.nr; i++) {
@@ -788,7 +788,7 @@ static int rm(int argc, const char **argv)
        strbuf_release(&buf);
 
        if (!result)
-               result = delete_refs("remote: remove", &branches, REF_NODEREF);
+               result = delete_refs("remote: remove", &branches, REF_NO_DEREF);
        string_list_clear(&branches, 0);
 
        if (skipped.nr) {
@@ -1255,7 +1255,7 @@ static int set_head(int argc, const char **argv)
                        head_name = xstrdup(states.heads.items[0].string);
                free_remote_ref_states(&states);
        } else if (opt_d && !opt_a && argc == 1) {
-               if (delete_ref(NULL, buf.buf, NULL, REF_NODEREF))
+               if (delete_ref(NULL, buf.buf, NULL, REF_NO_DEREF))
                        result |= error(_("Could not delete %s"), buf.buf);
        } else
                usage_with_options(builtin_remote_sethead_usage, options);
index d4003f7..906e541 100644 (file)
@@ -166,7 +166,7 @@ static int read_from_tree(const struct pathspec *pathspec,
        opt.output_format = DIFF_FORMAT_CALLBACK;
        opt.format_callback = update_index_from_diff;
        opt.format_callback_data = &intent_to_add;
-       opt.flags |= DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG;
+       opt.flags.override_submodule_config = 1;
 
        if (do_diff_cache(tree_oid, &opt))
                return 1;
index 9bf8d59..4032eb3 100644 (file)
@@ -294,7 +294,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
        if (revs.bisect)
                bisect_list = 1;
 
-       if (DIFF_OPT_TST(&revs.diffopt, QUICK))
+       if (revs.diffopt.flags.quick)
                info.flags |= REV_LIST_QUIET;
        for (i = 1 ; i < argc; i++) {
                const char *arg = argv[i];
@@ -397,8 +397,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
        if (bisect_list) {
                int reaches = reaches, all = all;
 
-               revs.commits = find_bisection(revs.commits, &reaches, &all,
-                                             bisect_find_all);
+               find_bisection(&revs.commits, &reaches, &all, bisect_find_all);
 
                if (bisect_show_vars)
                        return show_bisect_vars(&info, reaches, all);
index 17aabaa..80237f0 100644 (file)
@@ -58,7 +58,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
                        die("Cannot delete %s, not a symbolic ref", argv[0]);
                if (!strcmp(argv[0], "HEAD"))
                        die("deleting '%s' is not allowed", argv[0]);
-               return delete_ref(NULL, argv[0], NULL, REF_NODEREF);
+               return delete_ref(NULL, argv[0], NULL, REF_NO_DEREF);
        }
 
        switch (argc) {
index fefbe60..58d1c2d 100644 (file)
@@ -16,6 +16,7 @@
 #include "pathspec.h"
 #include "dir.h"
 #include "split-index.h"
+#include "fsmonitor.h"
 
 /*
  * Default to not allowing changes to the list of files. The
@@ -32,6 +33,7 @@ static int force_remove;
 static int verbose;
 static int mark_valid_only;
 static int mark_skip_worktree_only;
+static int mark_fsmonitor_only;
 #define MARK_FLAG 1
 #define UNMARK_FLAG 2
 static struct strbuf mtime_dir = STRBUF_INIT;
@@ -228,6 +230,7 @@ static int mark_ce_flags(const char *path, int flag, int mark)
        int namelen = strlen(path);
        int pos = cache_name_pos(path, namelen);
        if (0 <= pos) {
+               mark_fsmonitor_invalid(&the_index, active_cache[pos]);
                if (mark)
                        active_cache[pos]->ce_flags |= flag;
                else
@@ -460,6 +463,11 @@ static void update_one(const char *path)
                        die("Unable to mark file %s", path);
                return;
        }
+       if (mark_fsmonitor_only) {
+               if (mark_ce_flags(path, CE_FSMONITOR_VALID, mark_fsmonitor_only == MARK_FLAG))
+                       die("Unable to mark file %s", path);
+               return;
+       }
 
        if (force_remove) {
                if (remove_file_from_cache(path))
@@ -917,6 +925,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
        struct refresh_params refresh_args = {0, &has_errors};
        int lock_error = 0;
        int split_index = -1;
+       int force_write = 0;
+       int fsmonitor = -1;
        struct lock_file lock_file = LOCK_INIT;
        struct parse_opt_ctx_t ctx;
        strbuf_getline_fn getline_fn;
@@ -1008,6 +1018,16 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                            N_("test if the filesystem supports untracked cache"), UC_TEST),
                OPT_SET_INT(0, "force-untracked-cache", &untracked_cache,
                            N_("enable untracked cache without testing the filesystem"), UC_FORCE),
+               OPT_SET_INT(0, "force-write-index", &force_write,
+                       N_("write out the index even if is not flagged as changed"), 1),
+               OPT_BOOL(0, "fsmonitor", &fsmonitor,
+                       N_("enable or disable file system monitor")),
+               {OPTION_SET_INT, 0, "fsmonitor-valid", &mark_fsmonitor_only, NULL,
+                       N_("mark files as fsmonitor valid"),
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG},
+               {OPTION_SET_INT, 0, "no-fsmonitor-valid", &mark_fsmonitor_only, NULL,
+                       N_("clear fsmonitor valid bit"),
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
                OPT_END()
        };
 
@@ -1146,7 +1166,23 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                die("BUG: bad untracked_cache value: %d", untracked_cache);
        }
 
-       if (active_cache_changed) {
+       if (fsmonitor > 0) {
+               if (git_config_get_fsmonitor() == 0)
+                       warning(_("core.fsmonitor is unset; "
+                               "set it if you really want to "
+                               "enable fsmonitor"));
+               add_fsmonitor(&the_index);
+               report(_("fsmonitor enabled"));
+       } else if (!fsmonitor) {
+               if (git_config_get_fsmonitor() == 1)
+                       warning(_("core.fsmonitor is set; "
+                               "remove it if you really want to "
+                               "disable fsmonitor"));
+               remove_fsmonitor(&the_index);
+               report(_("fsmonitor disabled"));
+       }
+
+       if (active_cache_changed || force_write) {
                if (newfd < 0) {
                        if (refresh_args.flags & REFRESH_QUIET)
                                exit(128);
index cf1552b..4b4714b 100644 (file)
@@ -312,7 +312,7 @@ static const char *parse_cmd_verify(struct ref_transaction *transaction,
 static const char *parse_cmd_option(struct strbuf *input, const char *next)
 {
        if (!strncmp(next, "no-deref", 8) && next[8] == line_termination)
-               update_flags |= REF_NODEREF;
+               update_flags |= REF_NO_DEREF;
        else
                die("option unknown: %s", next);
        return next + 8;
@@ -427,7 +427,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
        }
 
        if (no_deref)
-               flags = REF_NODEREF;
+               flags = REF_NO_DEREF;
        if (delete)
                /*
                 * For purposes of backwards compatibility, we treat
diff --git a/cache.h b/cache.h
index d74f00d..2e14345 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -204,6 +204,7 @@ struct cache_entry {
 #define CE_ADDED             (1 << 19)
 
 #define CE_HASHED            (1 << 20)
+#define CE_FSMONITOR_VALID   (1 << 21)
 #define CE_WT_REMOVE         (1 << 22) /* remove in work directory */
 #define CE_CONFLICTED        (1 << 23)
 
@@ -327,6 +328,7 @@ static inline unsigned int canon_mode(unsigned int mode)
 #define CACHE_TREE_CHANGED     (1 << 5)
 #define SPLIT_INDEX_ORDERED    (1 << 6)
 #define UNTRACKED_CHANGED      (1 << 7)
+#define FSMONITOR_CHANGED      (1 << 8)
 
 struct split_index;
 struct untracked_cache;
@@ -345,6 +347,8 @@ struct index_state {
        struct hashmap dir_hash;
        unsigned char sha1[20];
        struct untracked_cache *untracked;
+       uint64_t fsmonitor_last_update;
+       struct ewah_bitmap *fsmonitor_dirty;
 };
 
 extern struct index_state the_index;
@@ -700,11 +704,14 @@ extern void *read_blob_data_from_index(const struct index_state *, const char *,
 #define CE_MATCH_IGNORE_MISSING                0x08
 /* enable stat refresh */
 #define CE_MATCH_REFRESH               0x10
-extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
-extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
+/* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */
+#define CE_MATCH_IGNORE_FSMONITOR 0X20
+extern int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
+extern int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 
 #define HASH_WRITE_OBJECT 1
 #define HASH_FORMAT_CHECK 2
+#define HASH_RENORMALIZE  4
 extern int index_fd(struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
 extern int index_path(struct object_id *oid, const char *path, struct stat *st, unsigned flags);
 
@@ -745,6 +752,7 @@ extern int hold_locked_index(struct lock_file *, int);
 extern void set_alternate_index_output(const char *);
 
 extern int verify_index_checksum;
+extern int verify_ce_order;
 
 /* Environment bits from configuration mechanism */
 extern int trust_executable_bit;
@@ -798,6 +806,7 @@ extern int core_apply_sparse_checkout;
 extern int precomposed_unicode;
 extern int protect_hfs;
 extern int protect_ntfs;
+extern const char *core_fsmonitor;
 
 /*
  * Include broken refs in all ref iterations, which will
@@ -1340,6 +1349,13 @@ extern int set_disambiguate_hint_config(const char *var, const char *value);
 extern int get_sha1_hex(const char *hex, unsigned char *sha1);
 extern int get_oid_hex(const char *hex, struct object_id *sha1);
 
+/*
+ * Read `len` pairs of hexadecimal digits from `hex` and write the
+ * values to `binary` as `len` bytes. Return 0 on success, or -1 if
+ * the input does not consist of hex digits).
+ */
+extern int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
+
 /*
  * Convert a binary sha1 to its hex equivalent. The `_r` variant is reentrant,
  * and writes the NUL-terminated output to the buffer `out`, which must be at
index a29246a..5bd06fe 100755 (executable)
@@ -12,20 +12,18 @@ case "${TRAVIS_OS_NAME:-linux}" in
 linux)
        export GIT_TEST_HTTPD=YesPlease
 
-       mkdir --parents custom/p4
-       pushd custom/p4
+       mkdir --parents "$P4_PATH"
+       pushd "$P4_PATH"
                wget --quiet "$P4WHENCE/bin.linux26x86_64/p4d"
                wget --quiet "$P4WHENCE/bin.linux26x86_64/p4"
                chmod u+x p4d
                chmod u+x p4
-               export PATH="$(pwd):$PATH"
        popd
-       mkdir --parents custom/git-lfs
-       pushd custom/git-lfs
+       mkdir --parents "$GIT_LFS_PATH"
+       pushd "$GIT_LFS_PATH"
                wget --quiet "$LFSWHENCE/git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
                tar --extract --gunzip --file "git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
                cp git-lfs-$LINUX_GIT_LFS_VERSION/git-lfs .
-               export PATH="$(pwd):$PATH"
        popd
        ;;
 osx)
index b3ed0a0..ac05f1f 100755 (executable)
@@ -26,3 +26,11 @@ skip_branch_tip_with_tag () {
 set -e
 
 skip_branch_tip_with_tag
+
+case "${TRAVIS_OS_NAME:-linux}" in
+linux)
+       P4_PATH="$(pwd)/custom/p4"
+       GIT_LFS_PATH="$(pwd)/custom/git-lfs"
+       export PATH="$GIT_LFS_PATH:$P4_PATH:$PATH"
+       ;;
+esac
index 82f6070..2505de1 100644 (file)
@@ -898,7 +898,7 @@ static void show_combined_header(struct combine_diff_path *elem,
                                 int show_file_header)
 {
        struct diff_options *opt = &rev->diffopt;
-       int abbrev = DIFF_OPT_TST(opt, FULL_INDEX) ? GIT_SHA1_HEXSZ : DEFAULT_ABBREV;
+       int abbrev = opt->flags.full_index ? GIT_SHA1_HEXSZ : DEFAULT_ABBREV;
        const char *a_prefix = opt->a_prefix ? opt->a_prefix : "a/";
        const char *b_prefix = opt->b_prefix ? opt->b_prefix : "b/";
        const char *c_meta = diff_get_color_opt(opt, DIFF_METAINFO);
@@ -987,7 +987,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
        userdiff = userdiff_find_by_path(elem->path);
        if (!userdiff)
                userdiff = userdiff_find_by_name("default");
-       if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV))
+       if (opt->flags.allow_textconv)
                textconv = userdiff_get_textconv(userdiff);
 
        /* Read the result of merge first */
@@ -1413,8 +1413,8 @@ void diff_tree_combined(const struct object_id *oid,
 
        diffopts = *opt;
        copy_pathspec(&diffopts.pathspec, &opt->pathspec);
-       DIFF_OPT_SET(&diffopts, RECURSIVE);
-       DIFF_OPT_CLR(&diffopts, ALLOW_EXTERNAL);
+       diffopts.flags.recursive = 1;
+       diffopts.flags.allow_external = 0;
 
        /* find set of paths that everybody touches
         *
@@ -1435,7 +1435,7 @@ void diff_tree_combined(const struct object_id *oid,
         * NOTE please keep this semantically in sync with diffcore_std()
         */
        need_generic_pathscan = opt->skip_stat_unmatch  ||
-                       DIFF_OPT_TST(opt, FOLLOW_RENAMES)       ||
+                       opt->flags.follow_renames       ||
                        opt->break_opt != -1    ||
                        opt->detect_rename      ||
                        opt->pickaxe            ||
index 1e0e633..cab8d44 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -1090,6 +1090,13 @@ struct commit_list *reduce_heads(struct commit_list *heads)
        return result;
 }
 
+void reduce_heads_replace(struct commit_list **heads)
+{
+       struct commit_list *result = reduce_heads(*heads);
+       free_commit_list(*heads);
+       *heads = result;
+}
+
 static const char gpg_sig_header[] = "gpgsig";
 static const int gpg_sig_header_len = sizeof(gpg_sig_header) - 1;
 
index 6d76959..99a3fea 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -313,7 +313,23 @@ extern int interactive_add(int argc, const char **argv, const char *prefix, int
 extern int run_add_interactive(const char *revision, const char *patch_mode,
                               const struct pathspec *pathspec);
 
-struct commit_list *reduce_heads(struct commit_list *heads);
+/*
+ * Takes a list of commits and returns a new list where those
+ * have been removed that can be reached from other commits in
+ * the list. It is useful for, e.g., reducing the commits
+ * randomly thrown at the git-merge command and removing
+ * redundant commits that the user shouldn't have given to it.
+ *
+ * This function destroys the STALE bit of the commit objects'
+ * flags.
+ */
+extern struct commit_list *reduce_heads(struct commit_list *heads);
+
+/*
+ * Like `reduce_heads()`, except it replaces the list. Use this
+ * instead of `foo = reduce_heads(foo);` to avoid memory leaks.
+ */
+extern void reduce_heads_replace(struct commit_list **heads);
 
 struct commit_extra_header {
        struct commit_extra_header *next;
index 7d063e9..5078ce5 100644 (file)
@@ -158,7 +158,9 @@ static inline uint64_t git_bswap64(uint64_t x)
 
 #define get_be16(p)    ntohs(*(unsigned short *)(p))
 #define get_be32(p)    ntohl(*(unsigned int *)(p))
+#define get_be64(p)    ntohll(*(uint64_t *)(p))
 #define put_be32(p, v) do { *(unsigned int *)(p) = htonl(v); } while (0)
+#define put_be64(p, v) do { *(uint64_t *)(p) = htonll(v); } while (0)
 
 #else
 
@@ -178,6 +180,13 @@ static inline uint32_t get_be32(const void *ptr)
                (uint32_t)p[3] <<  0;
 }
 
+static inline uint64_t get_be64(const void *ptr)
+{
+       const unsigned char *p = ptr;
+       return  (uint64_t)get_be32(&p[0]) << 32 |
+               (uint64_t)get_be32(&p[4]) <<  0;
+}
+
 static inline void put_be32(void *ptr, uint32_t value)
 {
        unsigned char *p = ptr;
@@ -187,4 +196,17 @@ static inline void put_be32(void *ptr, uint32_t value)
        p[3] = value >>  0;
 }
 
+static inline void put_be64(void *ptr, uint64_t value)
+{
+       unsigned char *p = ptr;
+       p[0] = value >> 56;
+       p[1] = value >> 48;
+       p[2] = value >> 40;
+       p[3] = value >> 32;
+       p[4] = value >> 24;
+       p[5] = value >> 16;
+       p[6] = value >>  8;
+       p[7] = value >>  0;
+}
+
 #endif
index 8b6fa0d..2d44d21 100644 (file)
@@ -2139,6 +2139,62 @@ static char *wcstoutfdup_startup(char *buffer, const wchar_t *wcs, size_t len)
        return memcpy(malloc_startup(len), buffer, len);
 }
 
+static void maybe_redirect_std_handle(const wchar_t *key, DWORD std_id, int fd,
+                                     DWORD desired_access, DWORD flags)
+{
+       DWORD create_flag = fd ? OPEN_ALWAYS : OPEN_EXISTING;
+       wchar_t buf[MAX_PATH];
+       DWORD max = ARRAY_SIZE(buf);
+       HANDLE handle;
+       DWORD ret = GetEnvironmentVariableW(key, buf, max);
+
+       if (!ret || ret >= max)
+               return;
+
+       /* make sure this does not leak into child processes */
+       SetEnvironmentVariableW(key, NULL);
+       if (!wcscmp(buf, L"off")) {
+               close(fd);
+               handle = GetStdHandle(std_id);
+               if (handle != INVALID_HANDLE_VALUE)
+                       CloseHandle(handle);
+               return;
+       }
+       if (std_id == STD_ERROR_HANDLE && !wcscmp(buf, L"2>&1")) {
+               handle = GetStdHandle(STD_OUTPUT_HANDLE);
+               if (handle == INVALID_HANDLE_VALUE) {
+                       close(fd);
+                       handle = GetStdHandle(std_id);
+                       if (handle != INVALID_HANDLE_VALUE)
+                               CloseHandle(handle);
+               } else {
+                       int new_fd = _open_osfhandle((intptr_t)handle, O_BINARY);
+                       SetStdHandle(std_id, handle);
+                       dup2(new_fd, fd);
+                       /* do *not* close the new_fd: that would close stdout */
+               }
+               return;
+       }
+       handle = CreateFileW(buf, desired_access, 0, NULL, create_flag,
+                            flags, NULL);
+       if (handle != INVALID_HANDLE_VALUE) {
+               int new_fd = _open_osfhandle((intptr_t)handle, O_BINARY);
+               SetStdHandle(std_id, handle);
+               dup2(new_fd, fd);
+               close(new_fd);
+       }
+}
+
+static void maybe_redirect_std_handles(void)
+{
+       maybe_redirect_std_handle(L"GIT_REDIRECT_STDIN", STD_INPUT_HANDLE, 0,
+                                 GENERIC_READ, FILE_ATTRIBUTE_NORMAL);
+       maybe_redirect_std_handle(L"GIT_REDIRECT_STDOUT", STD_OUTPUT_HANDLE, 1,
+                                 GENERIC_WRITE, FILE_ATTRIBUTE_NORMAL);
+       maybe_redirect_std_handle(L"GIT_REDIRECT_STDERR", STD_ERROR_HANDLE, 2,
+                                 GENERIC_WRITE, FILE_FLAG_NO_BUFFERING);
+}
+
 void mingw_startup(void)
 {
        int i, maxlen, argc;
@@ -2146,6 +2202,8 @@ void mingw_startup(void)
        wchar_t **wenv, **wargv;
        _startupinfo si;
 
+       maybe_redirect_std_handles();
+
        /* get wide char arguments and environment */
        si.newmode = 0;
        if (__wgetmainargs(&argc, &wargv, &wenv, _CRT_glob, &si) < 0)
index e276ccd..4d1d95b 100644 (file)
@@ -14,9 +14,8 @@
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-   Boston, MA 02110-1301, USA.  */
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
 
 #include "git-compat-util.h"
 #include <gettext.h>
index ceb4bdb..6bc24b7 100644 (file)
@@ -14,9 +14,8 @@
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-   Boston, MA 02110-1301, USA.  */
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
 
 /* Summary:
 
index ae03b74..7ed3fbb 100644 (file)
@@ -16,8 +16,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License along
-   with this program; if not, write to the Free Software Foundation,
-   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+   with this program; if not, see <http://www.gnu.org/licenses/>.  */
 
 /* Tell gcc not to warn about the (nfd < 0) tests, below.  */
 #if (__GNUC__ == 4 && 3 <= __GNUC_MINOR__) || 4 < __GNUC__
index b7aa59d..cd19952 100644 (file)
@@ -16,8 +16,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License along
-   with this program; if not, write to the Free Software Foundation,
-   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+   with this program; if not, see <http://www.gnu.org/licenses/>.  */
 
 #ifndef _GL_POLL_H
 #define _GL_POLL_H
index d8bde06..51cd60b 100644 (file)
@@ -14,9 +14,8 @@
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301 USA.  */
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
 
 static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern,
                                          size_t length, reg_syntax_t syntax);
index 5cb23e5..f3e03a9 100644 (file)
@@ -14,9 +14,8 @@
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301 USA.  */
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
index 61c9683..4d81358 100644 (file)
@@ -18,9 +18,8 @@
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301 USA.  */
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
 
 #ifndef _REGEX_H
 #define _REGEX_H 1
index 98342b8..59bf151 100644 (file)
@@ -14,9 +14,8 @@
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301 USA.  */
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
 
 static void re_string_construct_common (const char *str, int len,
                                        re_string_t *pstr,
index 4184d7f..3ee8aae 100644 (file)
@@ -14,9 +14,8 @@
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
 
 #ifndef _REGEX_INTERNAL_H
 #define _REGEX_INTERNAL_H 1
index 6f2b48a..1b5d89f 100644 (file)
@@ -14,9 +14,8 @@
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301 USA.  */
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
 
 static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags,
                                     int n) internal_function;
index 903abf9..e617c20 100644 (file)
--- a/config.c
+++ b/config.c
@@ -990,6 +990,16 @@ int git_config_pathname(const char **dest, const char *var, const char *value)
        return 0;
 }
 
+int git_config_expiry_date(timestamp_t *timestamp, const char *var, const char *value)
+{
+       if (!value)
+               return config_error_nonbool(var);
+       if (parse_expiry_date(value, timestamp))
+               return error(_("'%s' for '%s' is not a valid timestamp"),
+                            value, var);
+       return 0;
+}
+
 static int git_default_core_config(const char *var, const char *value)
 {
        /* This needs a better name */
@@ -2156,6 +2166,20 @@ int git_config_get_max_percent_split_change(void)
        return -1; /* default value */
 }
 
+int git_config_get_fsmonitor(void)
+{
+       if (git_config_get_pathname("core.fsmonitor", &core_fsmonitor))
+               core_fsmonitor = getenv("GIT_FSMONITOR_TEST");
+
+       if (core_fsmonitor && !*core_fsmonitor)
+               core_fsmonitor = NULL;
+
+       if (core_fsmonitor)
+               return 1;
+
+       return 0;
+}
+
 NORETURN
 void git_die_config_linenr(const char *key, const char *filename, int linenr)
 {
@@ -2315,7 +2339,7 @@ static ssize_t write_section(int fd, const char *key)
        struct strbuf sb = store_create_section(key);
        ssize_t ret;
 
-       ret = write_in_full(fd, sb.buf, sb.len) == sb.len;
+       ret = write_in_full(fd, sb.buf, sb.len);
        strbuf_release(&sb);
 
        return ret;
@@ -2810,7 +2834,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
                         * multiple [branch "$name"] sections.
                         */
                        if (copystr.len > 0) {
-                               if (write_in_full(out_fd, copystr.buf, copystr.len) != copystr.len) {
+                               if (write_in_full(out_fd, copystr.buf, copystr.len) < 0) {
                                        ret = write_error(get_lock_file_path(&lock));
                                        goto out;
                                }
@@ -2872,7 +2896,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
         * logic in the loop above.
         */
        if (copystr.len > 0) {
-               if (write_in_full(out_fd, copystr.buf, copystr.len) != copystr.len) {
+               if (write_in_full(out_fd, copystr.buf, copystr.len) < 0) {
                        ret = write_error(get_lock_file_path(&lock));
                        goto out;
                }
index a49d264..ef70a9c 100644 (file)
--- a/config.h
+++ b/config.h
@@ -58,6 +58,7 @@ extern int git_config_bool_or_int(const char *, const char *, int *);
 extern int git_config_bool(const char *, const char *);
 extern int git_config_string(const char **, const char *, const char *);
 extern int git_config_pathname(const char **, const char *, const char *);
+extern int git_config_expiry_date(timestamp_t *, const char *, const char *);
 extern int git_config_set_in_file_gently(const char *, const char *, const char *);
 extern void git_config_set_in_file(const char *, const char *, const char *);
 extern int git_config_set_gently(const char *, const char *);
@@ -212,6 +213,7 @@ extern int git_config_get_pathname(const char *key, const char **dest);
 extern int git_config_get_untracked_cache(void);
 extern int git_config_get_split_index(void);
 extern int git_config_get_max_percent_split_change(void);
+extern int git_config_get_fsmonitor(void);
 
 /* This dies if the configured or default date is in the future */
 extern int git_config_get_expiry(const char *key, const char **output);
index fdd984d..3683c77 100644 (file)
@@ -111,8 +111,7 @@ __git ()
 #   GNU General Public License for more details.
 #
 #   You should have received a copy of the GNU General Public License
-#   along with this program; if not, write to the Free Software Foundation,
-#   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#   along with this program; if not, see <http://www.gnu.org/licenses/>.
 #
 #   The latest version of this software can be obtained here:
 #
@@ -1205,7 +1204,7 @@ _git_branch ()
                        --color --no-color --verbose --abbrev= --no-abbrev
                        --track --no-track --contains --no-contains --merged --no-merged
                        --set-upstream-to= --edit-description --list
-                       --unset-upstream --delete --move --remotes
+                       --unset-upstream --delete --move --copy --remotes
                        --column --no-column --sort= --points-at
                        "
                ;;
@@ -1401,7 +1400,7 @@ __git_diff_common_options="--stat --numstat --shortstat --summary
                        --patch-with-stat --name-only --name-status --color
                        --no-color --color-words --no-renames --check
                        --full-index --binary --abbrev --diff-filter=
-                       --find-copies-harder
+                       --find-copies-harder --ignore-cr-at-eol
                        --text --ignore-space-at-eol --ignore-space-change
                        --ignore-all-space --ignore-blank-lines --exit-code
                        --quiet --ext-diff --no-ext-diff
@@ -1923,6 +1922,7 @@ _git_pull ()
        --*)
                __gitcomp "
                        --rebase --no-rebase
+                       --autostash --no-autostash
                        $__git_merge_options
                        $__git_fetch_options
                "
@@ -2642,6 +2642,7 @@ _git_config ()
                sendemail.suppressfrom
                sendemail.thread
                sendemail.to
+               sendemail.tocmd
                sendemail.validate
                sendemail.smtpbatchsize
                sendemail.smtprelogindelay
index 2a317fc..d389bfa 100644 (file)
@@ -13,8 +13,7 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 /*
index 4c56979..e6598b6 100644 (file)
@@ -14,8 +14,7 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 /*
@@ -104,7 +103,7 @@ static int keyring_get(struct credential *c)
        items = secret_service_search_sync(service,
                                           SECRET_SCHEMA_COMPAT_NETWORK,
                                           attributes,
-                                          SECRET_SEARCH_LOAD_SECRETS,
+                                          SECRET_SEARCH_LOAD_SECRETS | SECRET_SEARCH_UNLOCK,
                                           NULL,
                                           &error);
        g_hash_table_unref(attributes);
index 0061340..86518cd 100644 (file)
@@ -94,6 +94,12 @@ static WCHAR *wusername, *password, *protocol, *host, *path, target[1024];
 static void write_item(const char *what, LPCWSTR wbuf, int wlen)
 {
        char *buf;
+
+       if (!wbuf || !wlen) {
+               printf("%s=\n", what);
+               return;
+       }
+
        int len = WideCharToMultiByte(CP_UTF8, 0, wbuf, wlen, NULL, 0, NULL,
            FALSE);
        buf = xmalloc(len);
@@ -160,7 +166,7 @@ static int match_part_last(LPCWSTR *ptarget, LPCWSTR want, LPCWSTR delim)
 static int match_cred(const CREDENTIALW *cred)
 {
        LPCWSTR target = cred->TargetName;
-       if (wusername && wcscmp(wusername, cred->UserName))
+       if (wusername && wcscmp(wusername, cred->UserName ? cred->UserName : L""))
                return 0;
 
        return match_part(&target, L"git", L":") &&
@@ -183,7 +189,7 @@ static void get_credential(void)
        for (i = 0; i < num_creds; ++i)
                if (match_cred(creds[i])) {
                        write_item("username", creds[i]->UserName,
-                               wcslen(creds[i]->UserName));
+                               creds[i]->UserName ? wcslen(creds[i]->UserName) : 0);
                        write_item("password",
                                (LPCWSTR)creds[i]->CredentialBlob,
                                creds[i]->CredentialBlobSize / sizeof(WCHAR));
index e671f6c..510e0f7 100644 (file)
@@ -25,9 +25,8 @@
 ;; PURPOSE.  See the GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public
-;; License along with this program; if not, write to the Free
-;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
-;; MA 02111-1307 USA
+;; License along with this program; if not, see
+;; <http://www.gnu.org/licenses/>.
 
 ;; http://www.fsf.org/copyleft/gpl.html
 
index 5ffc506..97919f2 100644 (file)
@@ -15,9 +15,8 @@
 ;; PURPOSE.  See the GNU General Public License for more details.
 ;;
 ;; You should have received a copy of the GNU General Public
-;; License along with this program; if not, write to the Free
-;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
-;; MA 02111-1307 USA
+;; License along with this program; if not, see
+;; <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
index 4dec1f1..a16f79c 100755 (executable)
@@ -14,8 +14,7 @@
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
 #
 # ------------------------------------------------------------------------
 
index 225e3f0..4484bda 100644 (file)
@@ -63,6 +63,9 @@ git jump grep foo_bar
 # same as above, but case-insensitive; you can give
 # arbitrary grep options
 git jump grep -i foo_bar
+
+# use the silver searcher for git jump grep
+git config jump.grepCmd "ag --column"
 --------------------------------------------------
 
 
@@ -92,3 +95,10 @@ how to activate it.
 
 The shell snippets to generate the quickfix lines will almost certainly
 choke on filenames with exotic characters (like newlines).
+
+Contributing
+------------
+
+Bug fixes, bug reports, and feature requests should be discussed on the
+Git mailing list <git@vger.kernel.org>, and cc'd to the git-jump
+maintainer, Jeff King <peff@peff.net>.
index 427f206..80ab059 100755 (executable)
@@ -11,7 +11,8 @@ diff: elements are diff hunks. Arguments are given to diff.
 
 merge: elements are merge conflicts. Arguments are ignored.
 
-grep: elements are grep hits. Arguments are given to grep.
+grep: elements are grep hits. Arguments are given to git grep or, if
+      configured, to the command in `jump.grepCmd`.
 
 ws: elements are whitespace errors. Arguments are given to diff --check.
 EOF
@@ -50,7 +51,9 @@ mode_merge() {
 # but let's clean up extra whitespace, so they look better if the
 # editor shows them to us in the status bar.
 mode_grep() {
-       git grep -n "$@" |
+       cmd=$(git config jump.grepCmd)
+       test -n "$cmd" || cmd="git grep -n"
+       $cmd "$@" |
        perl -pe '
        s/[ \t]+/ /g;
        s/^ *//;
index 60dec86..de3f816 100755 (executable)
@@ -15,8 +15,7 @@
     GNU General Public License for more details.
 
     You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+    along with this program; if not, see <http://www.gnu.org/licenses/>.
 """
 
 import os, os.path, sys
index d13c4df..917d9e2 100644 (file)
@@ -2,6 +2,7 @@ package Git::Mediawiki;
 
 use 5.008;
 use strict;
+use POSIX;
 use Git;
 
 BEGIN {
@@ -52,7 +53,7 @@ sub smudge_filename {
        $filename =~ s/ /_/g;
        # Decode forbidden characters encoded in clean_filename
        $filename =~ s/_%_([0-9a-fA-F][0-9a-fA-F])/sprintf('%c', hex($1))/ge;
-       return $filename;
+       return substr($filename, 0, NAME_MAX-length('.mw'));
 }
 
 sub connect_maybe {
index e7f857c..af9cbc9 100755 (executable)
@@ -63,6 +63,11 @@ chomp(@tracked_pages);
 my @tracked_categories = split(/[ \n]/, run_git("config --get-all remote.${remotename}.categories"));
 chomp(@tracked_categories);
 
+# Just like @tracked_categories, but for MediaWiki namespaces.
+my @tracked_namespaces = split(/[ \n]/, run_git("config --get-all remote.${remotename}.namespaces"));
+for (@tracked_namespaces) { s/_/ /g; }
+chomp(@tracked_namespaces);
+
 # Import media files on pull
 my $import_media = run_git("config --get --bool remote.${remotename}.mediaimport");
 chomp($import_media);
@@ -256,6 +261,32 @@ sub get_mw_tracked_categories {
        return;
 }
 
+sub get_mw_tracked_namespaces {
+    my $pages = shift;
+    foreach my $local_namespace (sort @tracked_namespaces) {
+        my $namespace_id;
+        if ($local_namespace eq "(Main)") {
+            $namespace_id = 0;
+        } else {
+            $namespace_id = get_mw_namespace_id($local_namespace);
+        }
+        # virtual namespaces don't support allpages
+        next if !defined($namespace_id) || $namespace_id < 0;
+        my $mw_pages = $mediawiki->list( {
+            action => 'query',
+            list => 'allpages',
+            apnamespace => $namespace_id,
+            aplimit => 'max' } )
+            || die $mediawiki->{error}->{code} . ': '
+                . $mediawiki->{error}->{details} . "\n";
+        print {*STDERR} "$#{$mw_pages} found in namespace $local_namespace ($namespace_id)\n";
+        foreach my $page (@{$mw_pages}) {
+            $pages->{$page->{title}} = $page;
+        }
+    }
+    return;
+}
+
 sub get_mw_all_pages {
        my $pages = shift;
        # No user-provided list, get the list of pages from the API.
@@ -319,6 +350,10 @@ sub get_mw_pages {
                $user_defined = 1;
                get_mw_tracked_categories(\%pages);
        }
+       if (@tracked_namespaces) {
+               $user_defined = 1;
+               get_mw_tracked_namespaces(\%pages);
+       }
        if (!$user_defined) {
                get_mw_all_pages(\%pages);
        }
@@ -1308,7 +1343,8 @@ sub get_mw_namespace_id {
        my $id;
 
        if (!defined $ns) {
-               print {*STDERR} "No such namespace ${name} on MediaWiki.\n";
+               my @namespaces = map { s/ /_/g; $_; } sort keys %namespace_id;
+               print {*STDERR} "No such namespace ${name} on MediaWiki, known namespaces: @namespaces\n";
                $ns = {is_namespace => 0};
                $namespace_id{$name} = $ns;
        }
index d2ea02f..5173023 100644 (file)
@@ -12,6 +12,7 @@
 #include "refs.h"
 #include "submodule.h"
 #include "dir.h"
+#include "fsmonitor.h"
 
 /*
  * diff-files
@@ -71,14 +72,15 @@ 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)) {
-               unsigned orig_flags = diffopt->flags;
-               if (!DIFF_OPT_TST(diffopt, OVERRIDE_SUBMODULE_CONFIG))
+               struct diff_flags orig_flags = diffopt->flags;
+               if (!diffopt->flags.override_submodule_config)
                        set_diffopt_flags_from_submodule_config(diffopt, ce->name);
-               if (DIFF_OPT_TST(diffopt, IGNORE_SUBMODULES))
+               if (diffopt->flags.ignore_submodules)
                        changed = 0;
-               else if (!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));
+               else if (!diffopt->flags.ignore_dirty_submodules &&
+                        (!changed || diffopt->flags.dirty_submodules))
+                       *dirty_submodule = is_submodule_modified(ce->name,
+                                                                diffopt->flags.ignore_untracked_in_submodules);
                diffopt->flags = orig_flags;
        }
        return changed;
@@ -228,7 +230,8 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
 
                if (!changed && !dirty_submodule) {
                        ce_mark_uptodate(ce);
-                       if (!DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
+                       mark_fsmonitor_valid(ce);
+                       if (!revs->diffopt.flags.find_copies_harder)
                                continue;
                }
                oldmode = ce->ce_mode;
@@ -362,7 +365,7 @@ static int show_modified(struct rev_info *revs,
 
        oldmode = old->ce_mode;
        if (mode == oldmode && !oidcmp(oid, &old->oid) && !dirty_submodule &&
-           !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
+           !revs->diffopt.flags.find_copies_harder)
                return 0;
 
        diff_change(&revs->diffopt, oldmode, mode,
@@ -493,7 +496,7 @@ static int diff_cache(struct rev_info *revs,
        opts.head_idx = 1;
        opts.index_only = cached;
        opts.diff_index_cached = (cached &&
-                                 !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER));
+                                 !revs->diffopt.flags.find_copies_harder);
        opts.merge = 1;
        opts.fn = oneway_diff;
        opts.unpack_data = revs;
@@ -534,7 +537,7 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
        return 0;
 }
 
-int index_differs_from(const char *def, int diff_flags,
+int index_differs_from(const char *def, const struct diff_flags *flags,
                       int ita_invisible_in_index)
 {
        struct rev_info rev;
@@ -544,11 +547,12 @@ int index_differs_from(const char *def, int diff_flags,
        memset(&opt, 0, sizeof(opt));
        opt.def = def;
        setup_revisions(0, NULL, &rev, &opt);
-       DIFF_OPT_SET(&rev.diffopt, QUICK);
-       DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS);
-       rev.diffopt.flags |= diff_flags;
+       rev.diffopt.flags.quick = 1;
+       rev.diffopt.flags.exit_with_status = 1;
+       if (flags)
+               diff_flags_or(&rev.diffopt.flags, flags);
        rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
        run_diff_index(&rev, 1);
        object_array_clear(&rev.pending);
-       return (DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES) != 0);
+       return (rev.diffopt.flags.has_changes != 0);
 }
index 80ff17d..0ed5f0f 100644 (file)
@@ -184,7 +184,7 @@ static int queue_diff(struct diff_options *o,
        } else {
                struct diff_filespec *d1, *d2;
 
-               if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
+               if (o->flags.reverse_diff) {
                        SWAP(mode1, mode2);
                        SWAP(name1, name2);
                }
@@ -276,16 +276,16 @@ void diff_no_index(struct rev_info *revs,
        if (!revs->diffopt.output_format)
                revs->diffopt.output_format = DIFF_FORMAT_PATCH;
 
-       DIFF_OPT_SET(&revs->diffopt, NO_INDEX);
+       revs->diffopt.flags.no_index = 1;
 
-       DIFF_OPT_SET(&revs->diffopt, RELATIVE_NAME);
+       revs->diffopt.flags.relative_name = 1;
        revs->diffopt.prefix = prefix;
 
        revs->max_count = -2;
        diff_setup_done(&revs->diffopt);
 
        setup_diff_pager(&revs->diffopt);
-       DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS);
+       revs->diffopt.flags.exit_with_status = 1;
 
        if (queue_diff(&revs->diffopt, paths[0], paths[1]))
                exit(1);
diff --git a/diff.c b/diff.c
index e6814b9..2ebe222 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -124,18 +124,18 @@ static int parse_dirstat_params(struct diff_options *options, const char *params
        for (i = 0; i < params.nr; i++) {
                const char *p = params.items[i].string;
                if (!strcmp(p, "changes")) {
-                       DIFF_OPT_CLR(options, DIRSTAT_BY_LINE);
-                       DIFF_OPT_CLR(options, DIRSTAT_BY_FILE);
+                       options->flags.dirstat_by_line = 0;
+                       options->flags.dirstat_by_file = 0;
                } else if (!strcmp(p, "lines")) {
-                       DIFF_OPT_SET(options, DIRSTAT_BY_LINE);
-                       DIFF_OPT_CLR(options, DIRSTAT_BY_FILE);
+                       options->flags.dirstat_by_line = 1;
+                       options->flags.dirstat_by_file = 0;
                } else if (!strcmp(p, "files")) {
-                       DIFF_OPT_CLR(options, DIRSTAT_BY_LINE);
-                       DIFF_OPT_SET(options, DIRSTAT_BY_FILE);
+                       options->flags.dirstat_by_line = 0;
+                       options->flags.dirstat_by_file = 1;
                } else if (!strcmp(p, "noncumulative")) {
-                       DIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);
+                       options->flags.dirstat_cumulative = 0;
                } else if (!strcmp(p, "cumulative")) {
-                       DIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);
+                       options->flags.dirstat_cumulative = 1;
                } else if (isdigit(*p)) {
                        char *end;
                        int permille = strtoul(p, &end, 10) * 10;
@@ -1412,7 +1412,7 @@ static void emit_rewrite_diff(const char *name_a,
        struct emit_callback ecbdata;
        struct strbuf out = STRBUF_INIT;
 
-       if (diff_mnemonic_prefix && DIFF_OPT_TST(o, REVERSE_DIFF)) {
+       if (diff_mnemonic_prefix && o->flags.reverse_diff) {
                a_prefix = o->b_prefix;
                b_prefix = o->a_prefix;
        } else {
@@ -2660,7 +2660,7 @@ static void show_dirstat(struct diff_options *options)
        dir.alloc = 0;
        dir.nr = 0;
        dir.permille = options->dirstat_permille;
-       dir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE);
+       dir.cumulative = options->flags.dirstat_cumulative;
 
        changed = 0;
        for (i = 0; i < q->nr; i++) {
@@ -2686,7 +2686,7 @@ static void show_dirstat(struct diff_options *options)
                        goto found_damage;
                }
 
-               if (DIFF_OPT_TST(options, DIRSTAT_BY_FILE)) {
+               if (options->flags.dirstat_by_file) {
                        /*
                         * In --dirstat-by-file mode, we don't really need to
                         * look at the actual file contents at all.
@@ -2761,7 +2761,7 @@ static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *o
        dir.alloc = 0;
        dir.nr = 0;
        dir.permille = options->dirstat_permille;
-       dir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE);
+       dir.cumulative = options->flags.dirstat_cumulative;
 
        changed = 0;
        for (i = 0; i < data->nr; i++) {
@@ -3048,7 +3048,7 @@ static void builtin_diff(const char *name_a,
        const char *line_prefix = diff_line_prefix(o);
 
        diff_set_mnemonic_prefix(o, "a/", "b/");
-       if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
+       if (o->flags.reverse_diff) {
                a_prefix = o->b_prefix;
                b_prefix = o->a_prefix;
        } else {
@@ -3072,7 +3072,7 @@ static void builtin_diff(const char *name_a,
                return;
        }
 
-       if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) {
+       if (o->flags.allow_textconv) {
                textconv_one = get_textconv(one);
                textconv_two = get_textconv(two);
        }
@@ -3132,13 +3132,13 @@ static void builtin_diff(const char *name_a,
                                 header.len, 0);
                strbuf_reset(&header);
                goto free_ab_and_return;
-       } else if (!DIFF_OPT_TST(o, TEXT) &&
+       } else if (!o->flags.text &&
            ( (!textconv_one && diff_filespec_is_binary(one)) ||
              (!textconv_two && diff_filespec_is_binary(two)) )) {
                struct strbuf sb = STRBUF_INIT;
                if (!one->data && !two->data &&
                    S_ISREG(one->mode) && S_ISREG(two->mode) &&
-                   !DIFF_OPT_TST(o, BINARY)) {
+                   !o->flags.binary) {
                        if (!oidcmp(&one->oid, &two->oid)) {
                                if (must_show_header)
                                        emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
@@ -3167,7 +3167,7 @@ static void builtin_diff(const char *name_a,
                }
                emit_diff_symbol(o, DIFF_SYMBOL_HEADER, header.buf, header.len, 0);
                strbuf_reset(&header);
-               if (DIFF_OPT_TST(o, BINARY))
+               if (o->flags.binary)
                        emit_binary_diff(o, &mf1, &mf2);
                else {
                        strbuf_addf(&sb, "%sBinary files %s and %s differ\n",
@@ -3213,7 +3213,7 @@ static void builtin_diff(const char *name_a,
                xecfg.ctxlen = o->context;
                xecfg.interhunkctxlen = o->interhunkcontext;
                xecfg.flags = XDL_EMIT_FUNCNAMES;
-               if (DIFF_OPT_TST(o, FUNCCONTEXT))
+               if (o->flags.funccontext)
                        xecfg.flags |= XDL_EMIT_FUNCCONTEXT;
                if (pe)
                        xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
@@ -3378,7 +3378,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
        diff_free_filespec_data(one);
        diff_free_filespec_data(two);
        if (data.status)
-               DIFF_OPT_SET(o, CHECK_FAILED);
+               o->flags.check_failed = 1;
 }
 
 struct diff_filespec *alloc_filespec(const char *path)
@@ -3545,14 +3545,12 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
                int fd;
 
                if (lstat(s->path, &st) < 0) {
-                       if (errno == ENOENT) {
-                       err_empty:
-                               err = -1;
-                       empty:
-                               s->data = (char *)"";
-                               s->size = 0;
-                               return err;
-                       }
+               err_empty:
+                       err = -1;
+               empty:
+                       s->data = (char *)"";
+                       s->size = 0;
+                       return err;
                }
                s->size = xsize_t(st.st_size);
                if (!s->size)
@@ -3872,9 +3870,9 @@ static void fill_metainfo(struct strbuf *msg,
                *must_show_header = 0;
        }
        if (one && two && oidcmp(&one->oid, &two->oid)) {
-               int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
+               int abbrev = o->flags.full_index ? 40 : DEFAULT_ABBREV;
 
-               if (DIFF_OPT_TST(o, BINARY)) {
+               if (o->flags.binary) {
                        mmfile_t mf;
                        if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) ||
                            (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
@@ -3904,7 +3902,7 @@ static void run_diff_cmd(const char *pgm,
        int must_show_header = 0;
 
 
-       if (DIFF_OPT_TST(o, ALLOW_EXTERNAL)) {
+       if (o->flags.allow_external) {
                struct userdiff_driver *drv = userdiff_find_by_path(attr_path);
                if (drv && drv->external)
                        pgm = drv->external;
@@ -3984,7 +3982,7 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
        if (o->prefix_length)
                strip_prefix(o->prefix_length, &name, &other);
 
-       if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
+       if (!o->flags.allow_external)
                pgm = NULL;
 
        if (DIFF_PAIR_UNMERGED(p)) {
@@ -4083,7 +4081,7 @@ void diff_setup(struct diff_options *options)
        options->context = diff_context_default;
        options->interhunkcontext = diff_interhunk_context_default;
        options->ws_error_highlight = ws_error_highlight_default;
-       DIFF_OPT_SET(options, RENAME_EMPTY);
+       options->flags.rename_empty = 1;
 
        /* pathchange left =NULL by default */
        options->change = diff_change;
@@ -4131,17 +4129,15 @@ void diff_setup_done(struct diff_options *options)
         * inside contents.
         */
 
-       if (DIFF_XDL_TST(options, IGNORE_WHITESPACE) ||
-           DIFF_XDL_TST(options, IGNORE_WHITESPACE_CHANGE) ||
-           DIFF_XDL_TST(options, IGNORE_WHITESPACE_AT_EOL))
-               DIFF_OPT_SET(options, DIFF_FROM_CONTENTS);
+       if ((options->xdl_opts & XDF_WHITESPACE_FLAGS))
+               options->flags.diff_from_contents = 1;
        else
-               DIFF_OPT_CLR(options, DIFF_FROM_CONTENTS);
+               options->flags.diff_from_contents = 0;
 
-       if (DIFF_OPT_TST(options, FIND_COPIES_HARDER))
+       if (options->flags.find_copies_harder)
                options->detect_rename = DIFF_DETECT_COPY;
 
-       if (!DIFF_OPT_TST(options, RELATIVE_NAME))
+       if (!options->flags.relative_name)
                options->prefix = NULL;
        if (options->prefix)
                options->prefix_length = strlen(options->prefix);
@@ -4171,18 +4167,18 @@ void diff_setup_done(struct diff_options *options)
                                      DIFF_FORMAT_DIRSTAT |
                                      DIFF_FORMAT_SUMMARY |
                                      DIFF_FORMAT_CHECKDIFF))
-               DIFF_OPT_SET(options, RECURSIVE);
+               options->flags.recursive = 1;
        /*
         * Also pickaxe would not work very well if you do not say recursive
         */
        if (options->pickaxe)
-               DIFF_OPT_SET(options, RECURSIVE);
+               options->flags.recursive = 1;
        /*
         * When patches are generated, submodules diffed against the work tree
         * must be checked for dirtiness too so it can be shown in the output
         */
        if (options->output_format & DIFF_FORMAT_PATCH)
-               DIFF_OPT_SET(options, DIRTY_SUBMODULES);
+               options->flags.dirty_submodules = 1;
 
        if (options->detect_rename && options->rename_limit < 0)
                options->rename_limit = diff_rename_limit_default;
@@ -4204,14 +4200,14 @@ void diff_setup_done(struct diff_options *options)
         * to have found.  It does not make sense not to return with
         * exit code in such a case either.
         */
-       if (DIFF_OPT_TST(options, QUICK)) {
+       if (options->flags.quick) {
                options->output_format = DIFF_FORMAT_NO_OUTPUT;
-               DIFF_OPT_SET(options, EXIT_WITH_STATUS);
+               options->flags.exit_with_status = 1;
        }
 
        options->diff_path_counter = 0;
 
-       if (DIFF_OPT_TST(options, FOLLOW_RENAMES) && options->pathspec.nr != 1)
+       if (options->flags.follow_renames && options->pathspec.nr != 1)
                die(_("--follow requires exactly one pathspec"));
 
        if (!options->use_color || external_diff())
@@ -4561,7 +4557,7 @@ int diff_opt_parse(struct diff_options *options,
        else if (starts_with(arg, "-C") || starts_with(arg, "--find-copies=") ||
                 !strcmp(arg, "--find-copies")) {
                if (options->detect_rename == DIFF_DETECT_COPY)
-                       DIFF_OPT_SET(options, FIND_COPIES_HARDER);
+                       options->flags.find_copies_harder = 1;
                if ((options->rename_score = diff_scoreopt_parse(arg)) == -1)
                        return error("invalid argument to -C: %s", arg+2);
                options->detect_rename = DIFF_DETECT_COPY;
@@ -4569,13 +4565,13 @@ int diff_opt_parse(struct diff_options *options,
        else if (!strcmp(arg, "--no-renames"))
                options->detect_rename = 0;
        else if (!strcmp(arg, "--rename-empty"))
-               DIFF_OPT_SET(options, RENAME_EMPTY);
+               options->flags.rename_empty = 1;
        else if (!strcmp(arg, "--no-rename-empty"))
-               DIFF_OPT_CLR(options, RENAME_EMPTY);
+               options->flags.rename_empty = 0;
        else if (!strcmp(arg, "--relative"))
-               DIFF_OPT_SET(options, RELATIVE_NAME);
+               options->flags.relative_name = 1;
        else if (skip_prefix(arg, "--relative=", &arg)) {
-               DIFF_OPT_SET(options, RELATIVE_NAME);
+               options->flags.relative_name = 1;
                options->prefix = arg;
        }
 
@@ -4590,6 +4586,8 @@ int diff_opt_parse(struct diff_options *options,
                DIFF_XDL_SET(options, IGNORE_WHITESPACE_CHANGE);
        else if (!strcmp(arg, "--ignore-space-at-eol"))
                DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
+       else if (!strcmp(arg, "--ignore-cr-at-eol"))
+               DIFF_XDL_SET(options, IGNORE_CR_AT_EOL);
        else if (!strcmp(arg, "--ignore-blank-lines"))
                DIFF_XDL_SET(options, IGNORE_BLANK_LINES);
        else if (!strcmp(arg, "--indent-heuristic"))
@@ -4615,21 +4613,21 @@ int diff_opt_parse(struct diff_options *options,
        /* flags options */
        else if (!strcmp(arg, "--binary")) {
                enable_patch_output(&options->output_format);
-               DIFF_OPT_SET(options, BINARY);
+               options->flags.binary = 1;
        }
        else if (!strcmp(arg, "--full-index"))
-               DIFF_OPT_SET(options, FULL_INDEX);
+               options->flags.full_index = 1;
        else if (!strcmp(arg, "-a") || !strcmp(arg, "--text"))
-               DIFF_OPT_SET(options, TEXT);
+               options->flags.text = 1;
        else if (!strcmp(arg, "-R"))
-               DIFF_OPT_SET(options, REVERSE_DIFF);
+               options->flags.reverse_diff = 1;
        else if (!strcmp(arg, "--find-copies-harder"))
-               DIFF_OPT_SET(options, FIND_COPIES_HARDER);
+               options->flags.find_copies_harder = 1;
        else if (!strcmp(arg, "--follow"))
-               DIFF_OPT_SET(options, FOLLOW_RENAMES);
+               options->flags.follow_renames = 1;
        else if (!strcmp(arg, "--no-follow")) {
-               DIFF_OPT_CLR(options, FOLLOW_RENAMES);
-               DIFF_OPT_CLR(options, DEFAULT_FOLLOW_RENAMES);
+               options->flags.follow_renames = 0;
+               options->flags.default_follow_renames = 0;
        } else if (!strcmp(arg, "--color"))
                options->use_color = 1;
        else if (skip_prefix(arg, "--color=", &arg)) {
@@ -4686,22 +4684,23 @@ int diff_opt_parse(struct diff_options *options,
                return argcount;
        }
        else if (!strcmp(arg, "--exit-code"))
-               DIFF_OPT_SET(options, EXIT_WITH_STATUS);
+               options->flags.exit_with_status = 1;
        else if (!strcmp(arg, "--quiet"))
-               DIFF_OPT_SET(options, QUICK);
+               options->flags.quick = 1;
        else if (!strcmp(arg, "--ext-diff"))
-               DIFF_OPT_SET(options, ALLOW_EXTERNAL);
+               options->flags.allow_external = 1;
        else if (!strcmp(arg, "--no-ext-diff"))
-               DIFF_OPT_CLR(options, ALLOW_EXTERNAL);
-       else if (!strcmp(arg, "--textconv"))
-               DIFF_OPT_SET(options, ALLOW_TEXTCONV);
-       else if (!strcmp(arg, "--no-textconv"))
-               DIFF_OPT_CLR(options, ALLOW_TEXTCONV);
+               options->flags.allow_external = 0;
+       else if (!strcmp(arg, "--textconv")) {
+               options->flags.allow_textconv = 1;
+               options->flags.textconv_set_via_cmdline = 1;
+       } else if (!strcmp(arg, "--no-textconv"))
+               options->flags.allow_textconv = 0;
        else if (!strcmp(arg, "--ignore-submodules")) {
-               DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
+               options->flags.override_submodule_config = 1;
                handle_ignore_submodules_arg(options, "all");
        } else if (skip_prefix(arg, "--ignore-submodules=", &arg)) {
-               DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
+               options->flags.override_submodule_config = 1;
                handle_ignore_submodules_arg(options, arg);
        } else if (!strcmp(arg, "--submodule"))
                options->submodule_format = DIFF_SUBMODULE_LOG;
@@ -4776,11 +4775,11 @@ int diff_opt_parse(struct diff_options *options,
                         &options->interhunkcontext))
                ;
        else if (!strcmp(arg, "-W"))
-               DIFF_OPT_SET(options, FUNCCONTEXT);
+               options->flags.funccontext = 1;
        else if (!strcmp(arg, "--function-context"))
-               DIFF_OPT_SET(options, FUNCCONTEXT);
+               options->flags.funccontext = 1;
        else if (!strcmp(arg, "--no-function-context"))
-               DIFF_OPT_CLR(options, FUNCCONTEXT);
+               options->flags.funccontext = 0;
        else if ((argcount = parse_long_opt("output", av, &optarg))) {
                char *path = prefix_filename(prefix, optarg);
                options->file = xfopen(path, "w");
@@ -5530,7 +5529,7 @@ void diff_flush(struct diff_options *options)
                separator++;
        }
 
-       if (output_format & DIFF_FORMAT_DIRSTAT && DIFF_OPT_TST(options, DIRSTAT_BY_LINE))
+       if (output_format & DIFF_FORMAT_DIRSTAT && options->flags.dirstat_by_line)
                dirstat_by_line = 1;
 
        if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT) ||
@@ -5565,8 +5564,8 @@ void diff_flush(struct diff_options *options)
        }
 
        if (output_format & DIFF_FORMAT_NO_OUTPUT &&
-           DIFF_OPT_TST(options, EXIT_WITH_STATUS) &&
-           DIFF_OPT_TST(options, DIFF_FROM_CONTENTS)) {
+           options->flags.exit_with_status &&
+           options->flags.diff_from_contents) {
                /*
                 * run diff_flush_patch for the exit status. setting
                 * options->file to /dev/null should be safe, because we
@@ -5614,11 +5613,11 @@ free_queue:
         * diff_addremove/diff_change does not set the bit when
         * DIFF_FROM_CONTENTS is in effect (e.g. with -w).
         */
-       if (DIFF_OPT_TST(options, DIFF_FROM_CONTENTS)) {
+       if (options->flags.diff_from_contents) {
                if (options->found_changes)
-                       DIFF_OPT_SET(options, HAS_CHANGES);
+                       options->flags.has_changes = 1;
                else
-                       DIFF_OPT_CLR(options, HAS_CHANGES);
+                       options->flags.has_changes = 0;
        }
 }
 
@@ -5738,7 +5737,7 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
                         * to determine how many paths were dirty only
                         * due to stat info mismatch.
                         */
-                       if (!DIFF_OPT_TST(diffopt, NO_INDEX))
+                       if (!diffopt->flags.no_index)
                                diffopt->skip_stat_unmatch++;
                        diff_free_filepair(p);
                }
@@ -5787,10 +5786,10 @@ void diffcore_std(struct diff_options *options)
                diff_resolve_rename_copy();
        diffcore_apply_filter(options);
 
-       if (diff_queued_diff.nr && !DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
-               DIFF_OPT_SET(options, HAS_CHANGES);
+       if (diff_queued_diff.nr && !options->flags.diff_from_contents)
+               options->flags.has_changes = 1;
        else
-               DIFF_OPT_CLR(options, HAS_CHANGES);
+               options->flags.has_changes = 0;
 
        options->found_follow = 0;
 }
@@ -5802,23 +5801,23 @@ int diff_result_code(struct diff_options *opt, int status)
        diff_warn_rename_limit("diff.renameLimit",
                               opt->needed_rename_limit,
                               opt->degraded_cc_to_c);
-       if (!DIFF_OPT_TST(opt, EXIT_WITH_STATUS) &&
+       if (!opt->flags.exit_with_status &&
            !(opt->output_format & DIFF_FORMAT_CHECKDIFF))
                return status;
-       if (DIFF_OPT_TST(opt, EXIT_WITH_STATUS) &&
-           DIFF_OPT_TST(opt, HAS_CHANGES))
+       if (opt->flags.exit_with_status &&
+           opt->flags.has_changes)
                result |= 01;
        if ((opt->output_format & DIFF_FORMAT_CHECKDIFF) &&
-           DIFF_OPT_TST(opt, CHECK_FAILED))
+           opt->flags.check_failed)
                result |= 02;
        return result;
 }
 
 int diff_can_quit_early(struct diff_options *opt)
 {
-       return (DIFF_OPT_TST(opt, QUICK) &&
+       return (opt->flags.quick &&
                !opt->filter &&
-               DIFF_OPT_TST(opt, HAS_CHANGES));
+               opt->flags.has_changes);
 }
 
 /*
@@ -5830,10 +5829,10 @@ int diff_can_quit_early(struct diff_options *opt)
 static int is_submodule_ignored(const char *path, struct diff_options *options)
 {
        int ignored = 0;
-       unsigned orig_flags = options->flags;
-       if (!DIFF_OPT_TST(options, OVERRIDE_SUBMODULE_CONFIG))
+       struct diff_flags orig_flags = options->flags;
+       if (!options->flags.override_submodule_config)
                set_diffopt_flags_from_submodule_config(options, path);
-       if (DIFF_OPT_TST(options, IGNORE_SUBMODULES))
+       if (options->flags.ignore_submodules)
                ignored = 1;
        options->flags = orig_flags;
        return ignored;
@@ -5862,7 +5861,7 @@ void diff_addremove(struct diff_options *options,
         * Before the final output happens, they are pruned after
         * merged into rename/copy pairs as appropriate.
         */
-       if (DIFF_OPT_TST(options, REVERSE_DIFF))
+       if (options->flags.reverse_diff)
                addremove = (addremove == '+' ? '-' :
                             addremove == '-' ? '+' : addremove);
 
@@ -5881,8 +5880,8 @@ void diff_addremove(struct diff_options *options,
        }
 
        diff_queue(&diff_queued_diff, one, two);
-       if (!DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
-               DIFF_OPT_SET(options, HAS_CHANGES);
+       if (!options->flags.diff_from_contents)
+               options->flags.has_changes = 1;
 }
 
 void diff_change(struct diff_options *options,
@@ -5900,7 +5899,7 @@ void diff_change(struct diff_options *options,
            is_submodule_ignored(concatpath, options))
                return;
 
-       if (DIFF_OPT_TST(options, REVERSE_DIFF)) {
+       if (options->flags.reverse_diff) {
                SWAP(old_mode, new_mode);
                SWAP(old_oid, new_oid);
                SWAP(old_oid_valid, new_oid_valid);
@@ -5919,14 +5918,14 @@ void diff_change(struct diff_options *options,
        two->dirty_submodule = new_dirty_submodule;
        p = diff_queue(&diff_queued_diff, one, two);
 
-       if (DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
+       if (options->flags.diff_from_contents)
                return;
 
-       if (DIFF_OPT_TST(options, QUICK) && options->skip_stat_unmatch &&
+       if (options->flags.quick && options->skip_stat_unmatch &&
            !diff_filespec_check_stat_unmatch(p))
                return;
 
-       DIFF_OPT_SET(options, HAS_CHANGES);
+       options->flags.has_changes = 1;
 }
 
 struct diff_filepair *diff_unmerge(struct diff_options *options, const char *path)
@@ -6064,7 +6063,7 @@ void setup_diff_pager(struct diff_options *opt)
         * and because it is easy to find people oneline advising "git diff
         * --exit-code" in hooks and other scripts, we do not do so.
         */
-       if (!DIFF_OPT_TST(opt, EXIT_WITH_STATUS) &&
+       if (!opt->flags.exit_with_status &&
            check_pager_config("diff") != 0)
                setup_pager();
 }
diff --git a/diff.h b/diff.h
index 398b87b..0fb18dd 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -60,42 +60,52 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data)
 
 #define DIFF_FORMAT_CALLBACK   0x1000
 
-#define DIFF_OPT_RECURSIVE           (1 <<  0)
-#define DIFF_OPT_TREE_IN_RECURSIVE   (1 <<  1)
-#define DIFF_OPT_BINARY              (1 <<  2)
-#define DIFF_OPT_TEXT                (1 <<  3)
-#define DIFF_OPT_FULL_INDEX          (1 <<  4)
-#define DIFF_OPT_SILENT_ON_REMOVE    (1 <<  5)
-#define DIFF_OPT_FIND_COPIES_HARDER  (1 <<  6)
-#define DIFF_OPT_FOLLOW_RENAMES      (1 <<  7)
-#define DIFF_OPT_RENAME_EMPTY        (1 <<  8)
-/* (1 <<  9) unused */
-#define DIFF_OPT_HAS_CHANGES         (1 << 10)
-#define DIFF_OPT_QUICK               (1 << 11)
-#define DIFF_OPT_NO_INDEX            (1 << 12)
-#define DIFF_OPT_ALLOW_EXTERNAL      (1 << 13)
-#define DIFF_OPT_EXIT_WITH_STATUS    (1 << 14)
-#define DIFF_OPT_REVERSE_DIFF        (1 << 15)
-#define DIFF_OPT_CHECK_FAILED        (1 << 16)
-#define DIFF_OPT_RELATIVE_NAME       (1 << 17)
-#define DIFF_OPT_IGNORE_SUBMODULES   (1 << 18)
-#define DIFF_OPT_DIRSTAT_CUMULATIVE  (1 << 19)
-#define DIFF_OPT_DIRSTAT_BY_FILE     (1 << 20)
-#define DIFF_OPT_ALLOW_TEXTCONV      (1 << 21)
-#define DIFF_OPT_DIFF_FROM_CONTENTS  (1 << 22)
-#define DIFF_OPT_DIRTY_SUBMODULES    (1 << 24)
-#define DIFF_OPT_IGNORE_UNTRACKED_IN_SUBMODULES (1 << 25)
-#define DIFF_OPT_IGNORE_DIRTY_SUBMODULES (1 << 26)
-#define DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG (1 << 27)
-#define DIFF_OPT_DIRSTAT_BY_LINE     (1 << 28)
-#define DIFF_OPT_FUNCCONTEXT         (1 << 29)
-#define DIFF_OPT_PICKAXE_IGNORE_CASE (1 << 30)
-#define DIFF_OPT_DEFAULT_FOLLOW_RENAMES (1U << 31)
-
-#define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)
-#define DIFF_OPT_TOUCHED(opts, flag)    ((opts)->touched_flags & DIFF_OPT_##flag)
-#define DIFF_OPT_SET(opts, flag)    (((opts)->flags |= DIFF_OPT_##flag),((opts)->touched_flags |= DIFF_OPT_##flag))
-#define DIFF_OPT_CLR(opts, flag)    (((opts)->flags &= ~DIFF_OPT_##flag),((opts)->touched_flags |= DIFF_OPT_##flag))
+#define DIFF_FLAGS_INIT { 0 }
+struct diff_flags {
+       unsigned recursive:1;
+       unsigned tree_in_recursive:1;
+       unsigned binary:1;
+       unsigned text:1;
+       unsigned full_index:1;
+       unsigned silent_on_remove:1;
+       unsigned find_copies_harder:1;
+       unsigned follow_renames:1;
+       unsigned rename_empty:1;
+       unsigned has_changes:1;
+       unsigned quick:1;
+       unsigned no_index:1;
+       unsigned allow_external:1;
+       unsigned exit_with_status:1;
+       unsigned reverse_diff:1;
+       unsigned check_failed:1;
+       unsigned relative_name:1;
+       unsigned ignore_submodules:1;
+       unsigned dirstat_cumulative:1;
+       unsigned dirstat_by_file:1;
+       unsigned allow_textconv:1;
+       unsigned textconv_set_via_cmdline:1;
+       unsigned diff_from_contents:1;
+       unsigned dirty_submodules:1;
+       unsigned ignore_untracked_in_submodules:1;
+       unsigned ignore_dirty_submodules:1;
+       unsigned override_submodule_config:1;
+       unsigned dirstat_by_line:1;
+       unsigned funccontext:1;
+       unsigned pickaxe_ignore_case:1;
+       unsigned default_follow_renames:1;
+};
+
+static inline void diff_flags_or(struct diff_flags *a,
+                                const struct diff_flags *b)
+{
+       char *tmp_a = (char *)a;
+       const char *tmp_b = (const char *)b;
+       int i;
+
+       for (i = 0; i < sizeof(struct diff_flags); i++)
+               tmp_a[i] |= tmp_b[i];
+}
+
 #define DIFF_XDL_TST(opts, flag)    ((opts)->xdl_opts & XDF_##flag)
 #define DIFF_XDL_SET(opts, flag)    ((opts)->xdl_opts |= XDF_##flag)
 #define DIFF_XDL_CLR(opts, flag)    ((opts)->xdl_opts &= ~XDF_##flag)
@@ -122,8 +132,7 @@ struct diff_options {
        const char *a_prefix, *b_prefix;
        const char *line_prefix;
        size_t line_prefix_length;
-       unsigned flags;
-       unsigned touched_flags;
+       struct diff_flags flags;
 
        /* diff-filter bits */
        unsigned int filter;
@@ -389,7 +398,8 @@ extern int diff_result_code(struct diff_options *, int);
 
 extern void diff_no_index(struct rev_info *, int, const char **);
 
-extern int index_differs_from(const char *def, int diff_flags, int ita_invisible_in_index);
+extern int index_differs_from(const char *def, const struct diff_flags *flags,
+                             int ita_invisible_in_index);
 
 /*
  * Fill the contents of the filespec "df", respecting any textconv defined by
index 341529b..9476bd2 100644 (file)
@@ -131,7 +131,7 @@ static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,
        if (!DIFF_FILE_VALID(p->one) && !DIFF_FILE_VALID(p->two))
                return 0;
 
-       if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) {
+       if (o->flags.allow_textconv) {
                textconv_one = get_textconv(p->one);
                textconv_two = get_textconv(p->two);
        }
@@ -222,11 +222,11 @@ void diffcore_pickaxe(struct diff_options *o)
 
        if (opts & (DIFF_PICKAXE_REGEX | DIFF_PICKAXE_KIND_G)) {
                int cflags = REG_EXTENDED | REG_NEWLINE;
-               if (DIFF_OPT_TST(o, PICKAXE_IGNORE_CASE))
+               if (o->flags.pickaxe_ignore_case)
                        cflags |= REG_ICASE;
                regcomp_or_die(&regex, needle, cflags);
                regexp = &regex;
-       } else if (DIFF_OPT_TST(o, PICKAXE_IGNORE_CASE) &&
+       } else if (o->flags.pickaxe_ignore_case &&
                   has_non_ascii(needle)) {
                struct strbuf sb = STRBUF_INIT;
                int cflags = REG_NEWLINE | REG_ICASE;
@@ -236,7 +236,7 @@ void diffcore_pickaxe(struct diff_options *o)
                strbuf_release(&sb);
                regexp = &regex;
        } else {
-               kws = kwsalloc(DIFF_OPT_TST(o, PICKAXE_IGNORE_CASE)
+               kws = kwsalloc(o->flags.pickaxe_ignore_case
                               ? tolower_trans_tbl : NULL);
                kwsincr(kws, needle, strlen(needle));
                kwsprep(kws);
index 0d8c3d2..12dc2a0 100644 (file)
@@ -405,7 +405,7 @@ static int too_many_rename_candidates(int num_create,
                num_src > num_create ? num_src : num_create;
 
        /* Are we running under -C -C? */
-       if (!DIFF_OPT_TST(options, FIND_COPIES_HARDER))
+       if (!options->flags.find_copies_harder)
                return 1;
 
        /* Would we bust the limit if we were running under -C? */
@@ -463,7 +463,7 @@ void diffcore_rename(struct diff_options *options)
                        else if (options->single_follow &&
                                 strcmp(options->single_follow, p->two->path))
                                continue; /* not interested */
-                       else if (!DIFF_OPT_TST(options, RENAME_EMPTY) &&
+                       else if (!options->flags.rename_empty &&
                                 is_empty_blob_oid(&p->two->oid))
                                continue;
                        else if (add_rename_dst(p->two) < 0) {
@@ -473,7 +473,7 @@ void diffcore_rename(struct diff_options *options)
                                goto cleanup;
                        }
                }
-               else if (!DIFF_OPT_TST(options, RENAME_EMPTY) &&
+               else if (!options->flags.rename_empty &&
                         is_empty_blob_oid(&p->one->oid))
                        continue;
                else if (!DIFF_PAIR_UNMERGED(p) && !DIFF_FILE_VALID(p->two)) {
diff --git a/dir.c b/dir.c
index fc2bdc7..3c54366 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -18,6 +18,7 @@
 #include "utf8.h"
 #include "varint.h"
 #include "ewah/ewok.h"
+#include "fsmonitor.h"
 
 /*
  * Tells read_directory_recursive how a file or directory should be treated.
@@ -1389,6 +1390,30 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
        case index_nonexistent:
                if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)
                        break;
+               if (exclude &&
+                       (dir->flags & DIR_SHOW_IGNORED_TOO) &&
+                       (dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING)) {
+
+                       /*
+                        * This is an excluded directory and we are
+                        * showing ignored paths that match an exclude
+                        * pattern.  (e.g. show directory as ignored
+                        * only if it matches an exclude pattern).
+                        * This path will either be 'path_excluded`
+                        * (if we are showing empty directories or if
+                        * the directory is not empty), or will be
+                        * 'path_none' (empty directory, and we are
+                        * not showing empty directories).
+                        */
+                       if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))
+                               return path_excluded;
+
+                       if (read_directory_recursive(dir, istate, dirname, len,
+                                                    untracked, 1, 1, pathspec) == path_excluded)
+                               return path_excluded;
+
+                       return path_none;
+               }
                if (!(dir->flags & DIR_NO_GITLINKS)) {
                        struct object_id oid;
                        if (resolve_gitlink_ref(dirname, "HEAD", &oid) == 0)
@@ -1561,6 +1586,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
 {
        int exclude;
        int has_path_in_index = !!index_file_exists(istate, path->buf, path->len, ignore_case);
+       enum path_treatment path_treatment;
 
        if (dtype == DT_UNKNOWN)
                dtype = get_dtype(de, istate, path->buf, path->len);
@@ -1607,8 +1633,23 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
                return path_none;
        case DT_DIR:
                strbuf_addch(path, '/');
-               return treat_directory(dir, istate, untracked, path->buf, path->len,
-                                      baselen, exclude, pathspec);
+               path_treatment = treat_directory(dir, istate, untracked,
+                                                path->buf, path->len,
+                                                baselen, exclude, pathspec);
+               /*
+                * If 1) we only want to return directories that
+                * match an exclude pattern and 2) this directory does
+                * not match an exclude pattern but all of its
+                * contents are excluded, then indicate that we should
+                * recurse into this directory (instead of marking the
+                * directory itself as an ignored path).
+                */
+               if (!exclude &&
+                   path_treatment == path_excluded &&
+                   (dir->flags & DIR_SHOW_IGNORED_TOO) &&
+                   (dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING))
+                       return path_recurse;
+               return path_treatment;
        case DT_REG:
        case DT_LNK:
                return exclude ? path_excluded : path_untracked;
@@ -1693,17 +1734,23 @@ static int valid_cached_dir(struct dir_struct *dir,
        if (!untracked)
                return 0;
 
-       if (stat(path->len ? path->buf : ".", &st)) {
-               invalidate_directory(dir->untracked, untracked);
-               memset(&untracked->stat_data, 0, sizeof(untracked->stat_data));
-               return 0;
-       }
-       if (!untracked->valid ||
-           match_stat_data_racy(istate, &untracked->stat_data, &st)) {
-               if (untracked->valid)
+       /*
+        * With fsmonitor, we can trust the untracked cache's valid field.
+        */
+       refresh_fsmonitor(istate);
+       if (!(dir->untracked->use_fsmonitor && untracked->valid)) {
+               if (stat(path->len ? path->buf : ".", &st)) {
                        invalidate_directory(dir->untracked, untracked);
-               fill_stat_data(&untracked->stat_data, &st);
-               return 0;
+                       memset(&untracked->stat_data, 0, sizeof(untracked->stat_data));
+                       return 0;
+               }
+               if (!untracked->valid ||
+                       match_stat_data_racy(istate, &untracked->stat_data, &st)) {
+                       if (untracked->valid)
+                               invalidate_directory(dir->untracked, untracked);
+                       fill_stat_data(&untracked->stat_data, &st);
+                       return 0;
+               }
        }
 
        if (untracked->check_only != !!check_only) {
diff --git a/dir.h b/dir.h
index e371705..233a2eb 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -139,6 +139,8 @@ struct untracked_cache {
        int gitignore_invalidated;
        int dir_invalidated;
        int dir_opened;
+       /* fsmonitor invalidation data */
+       unsigned int use_fsmonitor : 1;
 };
 
 struct dir_struct {
@@ -152,7 +154,8 @@ struct dir_struct {
                DIR_COLLECT_IGNORED = 1<<4,
                DIR_SHOW_IGNORED_TOO = 1<<5,
                DIR_COLLECT_KILLED_ONLY = 1<<6,
-               DIR_KEEP_UNTRACKED_CONTENTS = 1<<7
+               DIR_KEEP_UNTRACKED_CONTENTS = 1<<7,
+               DIR_SHOW_IGNORED_TOO_MODE_MATCHING = 1<<8
        } flags;
        struct dir_entry **entries;
        struct dir_entry **ignored;
diff --git a/entry.c b/entry.c
index 944c183..3021144 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -4,6 +4,7 @@
 #include "streaming.h"
 #include "submodule.h"
 #include "progress.h"
+#include "fsmonitor.h"
 
 static void create_directories(const char *path, int path_len,
                               const struct checkout *state)
@@ -373,6 +374,7 @@ finish:
                                                   ce->name);
                fill_stat_cache_info(ce, &st);
                ce->ce_flags |= CE_UPDATE_IN_BASE;
+               mark_fsmonitor_invalid(state->istate, ce);
                state->istate->cache_changed |= CE_ENTRY_CHANGED;
        }
 delayed:
index 8289c25..8fa032f 100644 (file)
@@ -76,6 +76,7 @@ int protect_hfs = PROTECT_HFS_DEFAULT;
 #define PROTECT_NTFS_DEFAULT 0
 #endif
 int protect_ntfs = PROTECT_NTFS_DEFAULT;
+const char *core_fsmonitor;
 
 /*
  * The character that begins a commented line in user-editable file
index 7103cee..756bdd0 100644 (file)
@@ -14,8 +14,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 #include "cache.h"
 #include "ewok.h"
index 06c479f..b9fdda1 100644 (file)
@@ -14,8 +14,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 #include "git-compat-util.h"
 #include "ewok.h"
index f732109..bed1994 100644 (file)
@@ -14,8 +14,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 #include "git-compat-util.h"
 #include "ewok.h"
index c723f1a..b9643b7 100644 (file)
@@ -14,8 +14,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 #include "git-compat-util.h"
 #include "ewok.h"
index 269a1a8..dc43d05 100644 (file)
@@ -14,8 +14,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 #ifndef __EWOK_BITMAP_H__
 #define __EWOK_BITMAP_H__
index 63efdf9..bb3c6ff 100644 (file)
@@ -14,8 +14,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 #ifndef __EWOK_RLW_H__
 #define __EWOK_RLW_H__
diff --git a/fsmonitor.c b/fsmonitor.c
new file mode 100644 (file)
index 0000000..0af7c4e
--- /dev/null
@@ -0,0 +1,266 @@
+#include "cache.h"
+#include "config.h"
+#include "dir.h"
+#include "ewah/ewok.h"
+#include "fsmonitor.h"
+#include "run-command.h"
+#include "strbuf.h"
+
+#define INDEX_EXTENSION_VERSION        (1)
+#define HOOK_INTERFACE_VERSION (1)
+
+struct trace_key trace_fsmonitor = TRACE_KEY_INIT(FSMONITOR);
+
+static void fsmonitor_ewah_callback(size_t pos, void *is)
+{
+       struct index_state *istate = (struct index_state *)is;
+       struct cache_entry *ce = istate->cache[pos];
+
+       ce->ce_flags &= ~CE_FSMONITOR_VALID;
+}
+
+int read_fsmonitor_extension(struct index_state *istate, const void *data,
+       unsigned long sz)
+{
+       const char *index = data;
+       uint32_t hdr_version;
+       uint32_t ewah_size;
+       struct ewah_bitmap *fsmonitor_dirty;
+       int ret;
+
+       if (sz < sizeof(uint32_t) + sizeof(uint64_t) + sizeof(uint32_t))
+               return error("corrupt fsmonitor extension (too short)");
+
+       hdr_version = get_be32(index);
+       index += sizeof(uint32_t);
+       if (hdr_version != INDEX_EXTENSION_VERSION)
+               return error("bad fsmonitor version %d", hdr_version);
+
+       istate->fsmonitor_last_update = get_be64(index);
+       index += sizeof(uint64_t);
+
+       ewah_size = get_be32(index);
+       index += sizeof(uint32_t);
+
+       fsmonitor_dirty = ewah_new();
+       ret = ewah_read_mmap(fsmonitor_dirty, index, ewah_size);
+       if (ret != ewah_size) {
+               ewah_free(fsmonitor_dirty);
+               return error("failed to parse ewah bitmap reading fsmonitor index extension");
+       }
+       istate->fsmonitor_dirty = fsmonitor_dirty;
+
+       trace_printf_key(&trace_fsmonitor, "read fsmonitor extension successful");
+       return 0;
+}
+
+void fill_fsmonitor_bitmap(struct index_state *istate)
+{
+       int i;
+       istate->fsmonitor_dirty = ewah_new();
+       for (i = 0; i < istate->cache_nr; i++)
+               if (!(istate->cache[i]->ce_flags & CE_FSMONITOR_VALID))
+                       ewah_set(istate->fsmonitor_dirty, i);
+}
+
+void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate)
+{
+       uint32_t hdr_version;
+       uint64_t tm;
+       uint32_t ewah_start;
+       uint32_t ewah_size = 0;
+       int fixup = 0;
+
+       put_be32(&hdr_version, INDEX_EXTENSION_VERSION);
+       strbuf_add(sb, &hdr_version, sizeof(uint32_t));
+
+       put_be64(&tm, istate->fsmonitor_last_update);
+       strbuf_add(sb, &tm, sizeof(uint64_t));
+       fixup = sb->len;
+       strbuf_add(sb, &ewah_size, sizeof(uint32_t)); /* we'll fix this up later */
+
+       ewah_start = sb->len;
+       ewah_serialize_strbuf(istate->fsmonitor_dirty, sb);
+       ewah_free(istate->fsmonitor_dirty);
+       istate->fsmonitor_dirty = NULL;
+
+       /* fix up size field */
+       put_be32(&ewah_size, sb->len - ewah_start);
+       memcpy(sb->buf + fixup, &ewah_size, sizeof(uint32_t));
+
+       trace_printf_key(&trace_fsmonitor, "write fsmonitor extension successful");
+}
+
+/*
+ * Call the query-fsmonitor hook passing the time of the last saved results.
+ */
+static int query_fsmonitor(int version, uint64_t last_update, struct strbuf *query_result)
+{
+       struct child_process cp = CHILD_PROCESS_INIT;
+       char ver[64];
+       char date[64];
+       const char *argv[4];
+
+       if (!(argv[0] = core_fsmonitor))
+               return -1;
+
+       snprintf(ver, sizeof(version), "%d", version);
+       snprintf(date, sizeof(date), "%" PRIuMAX, (uintmax_t)last_update);
+       argv[1] = ver;
+       argv[2] = date;
+       argv[3] = NULL;
+       cp.argv = argv;
+       cp.use_shell = 1;
+       cp.dir = get_git_work_tree();
+
+       return capture_command(&cp, query_result, 1024);
+}
+
+static void fsmonitor_refresh_callback(struct index_state *istate, const char *name)
+{
+       int pos = index_name_pos(istate, name, strlen(name));
+
+       if (pos >= 0) {
+               struct cache_entry *ce = istate->cache[pos];
+               ce->ce_flags &= ~CE_FSMONITOR_VALID;
+       }
+
+       /*
+        * Mark the untracked cache dirty even if it wasn't found in the index
+        * as it could be a new untracked file.
+        */
+       trace_printf_key(&trace_fsmonitor, "fsmonitor_refresh_callback '%s'", name);
+       untracked_cache_invalidate_path(istate, name);
+}
+
+void refresh_fsmonitor(struct index_state *istate)
+{
+       static int has_run_once = 0;
+       struct strbuf query_result = STRBUF_INIT;
+       int query_success = 0;
+       size_t bol; /* beginning of line */
+       uint64_t last_update;
+       char *buf;
+       int i;
+
+       if (!core_fsmonitor || has_run_once)
+               return;
+       has_run_once = 1;
+
+       trace_printf_key(&trace_fsmonitor, "refresh fsmonitor");
+       /*
+        * This could be racy so save the date/time now and query_fsmonitor
+        * should be inclusive to ensure we don't miss potential changes.
+        */
+       last_update = getnanotime();
+
+       /*
+        * If we have a last update time, call query_fsmonitor for the set of
+        * changes since that time, else assume everything is possibly dirty
+        * and check it all.
+        */
+       if (istate->fsmonitor_last_update) {
+               query_success = !query_fsmonitor(HOOK_INTERFACE_VERSION,
+                       istate->fsmonitor_last_update, &query_result);
+               trace_performance_since(last_update, "fsmonitor process '%s'", core_fsmonitor);
+               trace_printf_key(&trace_fsmonitor, "fsmonitor process '%s' returned %s",
+                       core_fsmonitor, query_success ? "success" : "failure");
+       }
+
+       /* a fsmonitor process can return '/' to indicate all entries are invalid */
+       if (query_success && query_result.buf[0] != '/') {
+               /* Mark all entries returned by the monitor as dirty */
+               buf = query_result.buf;
+               bol = 0;
+               for (i = 0; i < query_result.len; i++) {
+                       if (buf[i] != '\0')
+                               continue;
+                       fsmonitor_refresh_callback(istate, buf + bol);
+                       bol = i + 1;
+               }
+               if (bol < query_result.len)
+                       fsmonitor_refresh_callback(istate, buf + bol);
+       } else {
+               /* Mark all entries invalid */
+               for (i = 0; i < istate->cache_nr; i++)
+                       istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
+
+               if (istate->untracked)
+                       istate->untracked->use_fsmonitor = 0;
+       }
+       strbuf_release(&query_result);
+
+       /* Now that we've updated istate, save the last_update time */
+       istate->fsmonitor_last_update = last_update;
+}
+
+void add_fsmonitor(struct index_state *istate)
+{
+       int i;
+
+       if (!istate->fsmonitor_last_update) {
+               trace_printf_key(&trace_fsmonitor, "add fsmonitor");
+               istate->cache_changed |= FSMONITOR_CHANGED;
+               istate->fsmonitor_last_update = getnanotime();
+
+               /* reset the fsmonitor state */
+               for (i = 0; i < istate->cache_nr; i++)
+                       istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
+
+               /* reset the untracked cache */
+               if (istate->untracked) {
+                       add_untracked_cache(istate);
+                       istate->untracked->use_fsmonitor = 1;
+               }
+
+               /* Update the fsmonitor state */
+               refresh_fsmonitor(istate);
+       }
+}
+
+void remove_fsmonitor(struct index_state *istate)
+{
+       if (istate->fsmonitor_last_update) {
+               trace_printf_key(&trace_fsmonitor, "remove fsmonitor");
+               istate->cache_changed |= FSMONITOR_CHANGED;
+               istate->fsmonitor_last_update = 0;
+       }
+}
+
+void tweak_fsmonitor(struct index_state *istate)
+{
+       int i;
+       int fsmonitor_enabled = git_config_get_fsmonitor();
+
+       if (istate->fsmonitor_dirty) {
+               if (fsmonitor_enabled) {
+                       /* Mark all entries valid */
+                       for (i = 0; i < istate->cache_nr; i++) {
+                               istate->cache[i]->ce_flags |= CE_FSMONITOR_VALID;
+                       }
+
+                       /* Mark all previously saved entries as dirty */
+                       ewah_each_bit(istate->fsmonitor_dirty, fsmonitor_ewah_callback, istate);
+
+                       /* Now mark the untracked cache for fsmonitor usage */
+                       if (istate->untracked)
+                               istate->untracked->use_fsmonitor = 1;
+               }
+
+               ewah_free(istate->fsmonitor_dirty);
+               istate->fsmonitor_dirty = NULL;
+       }
+
+       switch (fsmonitor_enabled) {
+       case -1: /* keep: do nothing */
+               break;
+       case 0: /* false */
+               remove_fsmonitor(istate);
+               break;
+       case 1: /* true */
+               add_fsmonitor(istate);
+               break;
+       default: /* unknown value: do nothing */
+               break;
+       }
+}
diff --git a/fsmonitor.h b/fsmonitor.h
new file mode 100644 (file)
index 0000000..cd3cc0c
--- /dev/null
@@ -0,0 +1,73 @@
+#ifndef FSMONITOR_H
+#define FSMONITOR_H
+
+extern struct trace_key trace_fsmonitor;
+
+/*
+ * Read the fsmonitor index extension and (if configured) restore the
+ * CE_FSMONITOR_VALID state.
+ */
+extern int read_fsmonitor_extension(struct index_state *istate, const void *data, unsigned long sz);
+
+/*
+ * Fill the fsmonitor_dirty ewah bits with their state from the index,
+ * before it is split during writing.
+ */
+extern void fill_fsmonitor_bitmap(struct index_state *istate);
+
+/*
+ * Write the CE_FSMONITOR_VALID state into the fsmonitor index
+ * extension.  Reads from the fsmonitor_dirty ewah in the index.
+ */
+extern void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate);
+
+/*
+ * Add/remove the fsmonitor index extension
+ */
+extern void add_fsmonitor(struct index_state *istate);
+extern void remove_fsmonitor(struct index_state *istate);
+
+/*
+ * Add/remove the fsmonitor index extension as necessary based on the current
+ * core.fsmonitor setting.
+ */
+extern void tweak_fsmonitor(struct index_state *istate);
+
+/*
+ * Run the configured fsmonitor integration script and clear the
+ * CE_FSMONITOR_VALID bit for any files returned as dirty.  Also invalidate
+ * any corresponding untracked cache directory structures. Optimized to only
+ * run the first time it is called.
+ */
+extern void refresh_fsmonitor(struct index_state *istate);
+
+/*
+ * Set the given cache entries CE_FSMONITOR_VALID bit. This should be
+ * called any time the cache entry has been updated to reflect the
+ * current state of the file on disk.
+ */
+static inline void mark_fsmonitor_valid(struct cache_entry *ce)
+{
+       if (core_fsmonitor) {
+               ce->ce_flags |= CE_FSMONITOR_VALID;
+               trace_printf_key(&trace_fsmonitor, "mark_fsmonitor_clean '%s'", ce->name);
+       }
+}
+
+/*
+ * Clear the given cache entry's CE_FSMONITOR_VALID bit and invalidate
+ * any corresponding untracked cache directory structures. This should
+ * be called any time git creates or modifies a file that should
+ * trigger an lstat() or invalidate the untracked cache for the
+ * corresponding directory
+ */
+static inline void mark_fsmonitor_invalid(struct index_state *istate, struct cache_entry *ce)
+{
+       if (core_fsmonitor) {
+               ce->ce_flags &= ~CE_FSMONITOR_VALID;
+               untracked_cache_invalidate_path(istate, ce->name);
+               trace_printf_key(&trace_fsmonitor, "mark_fsmonitor_invalid '%s'", ce->name);
+       }
+}
+
+#endif
index ab0d1b0..eeea4b6 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-echo "/* Automatically generated by $0 */
+echo "/* Automatically generated by generate-cmdlist.sh */
 struct cmdname_help {
        char name[16];
        char help[80];
index 0138a88..54cbfec 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-USAGE='[help|start|bad|good|new|old|terms|skip|next|reset|visualize|replay|log|run]'
+USAGE='[help|start|bad|good|new|old|terms|skip|next|reset|visualize|view|replay|log|run]'
 LONG_USAGE='git bisect help
        print this long help message.
 git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>]
@@ -20,7 +20,7 @@ git bisect next
        find next bisection to test and check it out.
 git bisect reset [<commit>]
        finish bisection search and go back to commit.
-git bisect visualize
+git bisect (visualize|view)
        show bisect status in gitk.
 git bisect replay <logfile>
        replay bisection log.
@@ -450,6 +450,8 @@ bisect_replay () {
 bisect_run () {
        bisect_next_check fail
 
+       test -n "$*" || die "$(gettext "bisect run failed: no command provided.")"
+
        while true
        do
                command="$@"
index 918a8de..f10caed 100644 (file)
@@ -254,7 +254,7 @@ $(ALL_MSGFILES): %.msg : %.po
 lib/tclIndex: $(ALL_LIBFILES) GIT-GUI-VARS
        $(QUIET_INDEX)if echo \
          $(foreach p,$(PRELOAD_FILES),source $p\;) \
-         auto_mkindex lib '*.tcl' \
+         auto_mkindex lib $(patsubst lib/%,%,$(sort $(ALL_LIBFILES))) \
        | $(TCL_PATH) $(QUIET_2DEVNULL); then : ok; \
        else \
         echo >&2 "    * $(TCL_PATH) failed; using unoptimized loading"; \
index 5bc21b8..ed24aa9 100755 (executable)
@@ -24,8 +24,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA}]
+along with this program; if not, see <http://www.gnu.org/licenses/>.}]
 
 ######################################################################
 ##
index 6e64d40..14c5078 100644 (file)
@@ -53,6 +53,7 @@ else
 
        git format-patch -k --stdout --full-index --cherry-pick --right-only \
                --src-prefix=a/ --dst-prefix=b/ --no-renames --no-cover-letter \
+               --pretty=mboxrd \
                $git_format_patch_opt \
                "$revisions" ${restrict_revision+^$restrict_revision} \
                >"$GIT_DIR/rebased-patches"
@@ -83,6 +84,7 @@ else
        fi
 
        git am $git_am_opt --rebasing --resolvemsg="$resolvemsg" \
+               --patch-format=mboxrd \
                $allow_rerere_autoupdate \
                ${gpg_sign_opt:+"$gpg_sign_opt"} <"$GIT_DIR/rebased-patches"
        ret=$?
index 2563dc5..4378156 100644 (file)
@@ -722,7 +722,7 @@ collapse_todo_ids() {
        git rebase--helper --shorten-ids
 }
 
-# Add commands after a pick or after a squash/fixup serie
+# Add commands after a pick or after a squash/fixup series
 # in the todo list.
 add_exec_commands () {
        {
index 6344e8d..60b70f3 100755 (executable)
@@ -9,7 +9,7 @@ OPTIONS_STUCKLONG=t
 OPTIONS_SPEC="\
 git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]
 git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
-git-rebase --continue | --abort | --skip | --edit-todo
+git rebase --continue | --abort | --skip | --edit-todo
 --
  Available options are
 v,verbose!         display a diffstat of what changed upstream
@@ -166,7 +166,7 @@ apply_autostash () {
        if test -f "$state_dir/autostash"
        then
                stash_sha1=$(cat "$state_dir/autostash")
-               if git stash apply $stash_sha1 2>&1 >/dev/null
+               if git stash apply $stash_sha1 >/dev/null 2>&1
                then
                        echo "$(gettext 'Applied autostash.')" >&2
                else
@@ -216,7 +216,7 @@ run_pre_rebase_hook () {
 }
 
 test -f "$apply_dir"/applying &&
-       die "$(gettext "It looks like git-am is in progress. Cannot rebase.")"
+       die "$(gettext "It looks like 'git am' is in progress. Cannot rebase.")"
 
 if test -d "$apply_dir"
 then
diff --git a/git.c b/git.c
index 9e96dd4..c870b97 100644 (file)
--- a/git.c
+++ b/git.c
@@ -372,7 +372,7 @@ static struct cmd_struct commands[] = {
        { "archive", cmd_archive, RUN_SETUP_GENTLY },
        { "bisect--helper", cmd_bisect__helper, RUN_SETUP },
        { "blame", cmd_blame, RUN_SETUP },
-       { "branch", cmd_branch, RUN_SETUP },
+       { "branch", cmd_branch, RUN_SETUP | DELAY_PAGER_CONFIG },
        { "bundle", cmd_bundle, RUN_SETUP_GENTLY },
        { "cat-file", cmd_cat_file, RUN_SETUP },
        { "check-attr", cmd_check_attr, RUN_SETUP },
diff --git a/git.rc b/git.rc
index 33aafb7..49002e0 100644 (file)
--- a/git.rc
+++ b/git.rc
@@ -1,6 +1,6 @@
 1 VERSIONINFO
-FILEVERSION     MAJOR,MINOR,0,0
-PRODUCTVERSION  MAJOR,MINOR,0,0
+FILEVERSION     MAJOR,MINOR,MICRO,PATCHLEVEL
+PRODUCTVERSION  MAJOR,MINOR,MICRO,PATCHLEVEL
 BEGIN
   BLOCK "StringFileInfo"
   BEGIN
diff --git a/grep.c b/grep.c
index ce6a48e..a69c05e 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -387,7 +387,7 @@ static void compile_pcre1_regexp(struct grep_pat *p, const struct grep_opt *opt)
        if (!p->pcre1_regexp)
                compile_regexp_failed(p, error);
 
-       p->pcre1_extra_info = pcre_study(p->pcre1_regexp, PCRE_STUDY_JIT_COMPILE, &error);
+       p->pcre1_extra_info = pcre_study(p->pcre1_regexp, GIT_PCRE_STUDY_JIT_COMPILE, &error);
        if (!p->pcre1_extra_info && error)
                die("%s", error);
 
@@ -1476,31 +1476,52 @@ static void show_funcname_line(struct grep_opt *opt, struct grep_source *gs,
        }
 }
 
+static int is_empty_line(const char *bol, const char *eol);
+
 static void show_pre_context(struct grep_opt *opt, struct grep_source *gs,
                             char *bol, char *end, unsigned lno)
 {
-       unsigned cur = lno, from = 1, funcname_lno = 0;
-       int funcname_needed = !!opt->funcname;
-
-       if (opt->funcbody && !match_funcname(opt, gs, bol, end))
-               funcname_needed = 2;
+       unsigned cur = lno, from = 1, funcname_lno = 0, orig_from;
+       int funcname_needed = !!opt->funcname, comment_needed = 0;
 
        if (opt->pre_context < lno)
                from = lno - opt->pre_context;
        if (from <= opt->last_shown)
                from = opt->last_shown + 1;
+       orig_from = from;
+       if (opt->funcbody) {
+               if (match_funcname(opt, gs, bol, end))
+                       comment_needed = 1;
+               else
+                       funcname_needed = 1;
+               from = opt->last_shown + 1;
+       }
 
        /* Rewind. */
-       while (bol > gs->buf &&
-              cur > (funcname_needed == 2 ? opt->last_shown + 1 : from)) {
+       while (bol > gs->buf && cur > from) {
+               char *next_bol = bol;
                char *eol = --bol;
 
                while (bol > gs->buf && bol[-1] != '\n')
                        bol--;
                cur--;
+               if (comment_needed && (is_empty_line(bol, eol) ||
+                                      match_funcname(opt, gs, bol, eol))) {
+                       comment_needed = 0;
+                       from = orig_from;
+                       if (cur < from) {
+                               cur++;
+                               bol = next_bol;
+                               break;
+                       }
+               }
                if (funcname_needed && match_funcname(opt, gs, bol, eol)) {
                        funcname_lno = cur;
                        funcname_needed = 0;
+                       if (opt->funcbody)
+                               comment_needed = 1;
+                       else
+                               from = orig_from;
                }
        }
 
diff --git a/grep.h b/grep.h
index 52aecfa..399381c 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -7,11 +7,12 @@
 #if PCRE_MAJOR >= 8 && PCRE_MINOR >= 32
 #ifndef NO_LIBPCRE1_JIT
 #define GIT_PCRE1_USE_JIT
+#define GIT_PCRE_STUDY_JIT_COMPILE PCRE_STUDY_JIT_COMPILE
 #endif
 #endif
 #endif
-#ifndef PCRE_STUDY_JIT_COMPILE
-#define PCRE_STUDY_JIT_COMPILE 0
+#ifndef GIT_PCRE_STUDY_JIT_COMPILE
+#define GIT_PCRE_STUDY_JIT_COMPILE 0
 #endif
 #if PCRE_MAJOR <= 8 && PCRE_MINOR < 20
 typedef int pcre_jit_stack;
diff --git a/hex.c b/hex.c
index 28b4411..8df2d63 100644 (file)
--- a/hex.c
+++ b/hex.c
@@ -35,6 +35,18 @@ const signed char hexval_table[256] = {
         -1, -1, -1, -1, -1, -1, -1, -1,                /* f8-ff */
 };
 
+int hex_to_bytes(unsigned char *binary, const char *hex, size_t len)
+{
+       for (; len; len--, hex += 2) {
+               unsigned int val = (hexval(hex[0]) << 4) | hexval(hex[1]);
+
+               if (val & ~0xff)
+                       return -1;
+               *binary++ = val;
+       }
+       return 0;
+}
+
 int get_sha1_hex(const char *hex, unsigned char *sha1)
 {
        int i;
index 493ee7d..14435ab 100644 (file)
@@ -1007,20 +1007,18 @@ static void remote_ls(const char *path, int flags,
                      void (*userFunc)(struct remote_ls_ctx *ls),
                      void *userData);
 
-/* extract hex from sharded "xx/x{40}" filename */
+/* extract hex from sharded "xx/x{38}" filename */
 static int get_oid_hex_from_objpath(const char *path, struct object_id *oid)
 {
-       char hex[GIT_MAX_HEXSZ];
-
        if (strlen(path) != GIT_SHA1_HEXSZ + 1)
                return -1;
 
-       memcpy(hex, path, 2);
+       if (hex_to_bytes(oid->hash, path, 1))
+               return -1;
        path += 2;
        path++; /* skip '/' */
-       memcpy(hex + 2, path, GIT_SHA1_HEXSZ - 2);
 
-       return get_oid_hex(hex, oid);
+       return hex_to_bytes(oid->hash + 1, path, GIT_SHA1_RAWSZ - 1);
 }
 
 static void process_ls_object(struct remote_ls_ctx *ls)
index 8c785f3..54e6a80 100644 (file)
@@ -18,8 +18,7 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "cache.h"
@@ -684,7 +683,7 @@ static int parse_response_code(struct imap_store *ctx, struct imap_cmd_cb *cb,
        struct imap *imap = ctx->imap;
        char *arg, *p;
 
-       if (*s != '[')
+       if (!s || *s != '[')
                return RESP_OK;         /* no response code */
        s++;
        if (!(p = st