Merge branch 'jk/progress-delay-fix'
authorJunio C Hamano <gitster@pobox.com>
Wed, 13 Dec 2017 21:28:57 +0000 (13:28 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 13 Dec 2017 21:28:57 +0000 (13:28 -0800)
A regression in the progress eye-candy was fixed.

* jk/progress-delay-fix:
progress: drop delay-threshold code
progress: set default delay threshold to 100%, not 0%

325 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.15.2.txt [new file with mode: 0644]
Documentation/RelNotes/2.16.0.txt [new file with mode: 0644]
Documentation/SubmittingPatches
Documentation/config.txt
Documentation/diff-heuristic-options.txt [deleted file]
Documentation/diff-options.txt
Documentation/git-add.txt
Documentation/git-annotate.txt
Documentation/git-bisect.txt
Documentation/git-blame.txt
Documentation/git-branch.txt
Documentation/git-check-ref-format.txt
Documentation/git-checkout.txt
Documentation/git-config.txt
Documentation/git-for-each-ref.txt
Documentation/git-log.txt
Documentation/git-ls-files.txt
Documentation/git-merge-base.txt
Documentation/git-merge.txt
Documentation/git-notes.txt
Documentation/git-prune.txt
Documentation/git-push.txt
Documentation/git-reflog.txt
Documentation/git-send-email.txt
Documentation/git-stash.txt
Documentation/git-status.txt
Documentation/git-update-index.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/githooks.txt
Documentation/gitrepository-layout.txt
Documentation/gitworkflows.txt
Documentation/merge-options.txt
Documentation/merge-strategies.txt
Documentation/technical/api-directory-listing.txt
Documentation/technical/http-protocol.txt
Documentation/technical/index-format.txt
Documentation/technical/pack-protocol.txt
Documentation/user-manual.txt
GIT-VERSION-GEN
Makefile
RelNotes
advice.c
advice.h
apply.c
apply.h
archive.c
bisect.c
bisect.h
blame.c
branch.c
branch.h
builtin/add.c
builtin/am.c
builtin/apply.c
builtin/bisect--helper.c
builtin/blame.c
builtin/branch.c
builtin/check-ref-format.c
builtin/checkout-index.c
builtin/checkout.c
builtin/clone.c
builtin/commit.c
builtin/config.c
builtin/describe.c
builtin/diff.c
builtin/difftool.c
builtin/fast-export.c
builtin/fetch.c
builtin/fmt-merge-msg.c
builtin/fsck.c
builtin/grep.c
builtin/log.c
builtin/ls-files.c
builtin/merge-base.c
builtin/merge-ours.c
builtin/merge.c
builtin/notes.c
builtin/pack-objects.c
builtin/prune.c
builtin/pull.c
builtin/push.c
builtin/receive-pack.c
builtin/reflog.c
builtin/remote.c
builtin/replace.c
builtin/reset.c
builtin/rev-list.c
builtin/rev-parse.c
builtin/show-branch.c
builtin/show-ref.c
builtin/submodule--helper.c
builtin/symbolic-ref.c
builtin/tag.c
builtin/update-index.c
builtin/update-ref.c
builtin/worktree.c
bundle.c
cache-tree.c
cache.h
ci/install-dependencies.sh
ci/lib-travisci.sh
column.c
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
connect.c
connected.c
connected.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
daemon.c
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
fast-import.c
fetch-pack.c
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-send-email.perl
git-stash.sh
git-submodule.sh
git.c
git.rc
gitweb/gitweb.perl
grep.c
grep.h
hash.h
hex.c
http-push.c
http.c
imap-send.c
kwset.c
kwset.h
lockfile.h
log-tree.c
log-tree.h
merge-recursive.c
merge.c
notes-cache.c
notes-merge.c
notes-utils.c
notes.c
pack-bitmap.c
pack-bitmap.h
patch-ids.c
pathspec.c
perl/Git/Packet.pm [new file with mode: 0644]
perl/Makefile
pkt-line.c
pkt-line.h
preload-index.c
pretty.c
protocol.c [new file with mode: 0644]
protocol.h [new file with mode: 0644]
read-cache.c
ref-filter.c
reflog-walk.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-testsvn.c
remote.c
remote.h
repository.c
repository.h
revision.c
run-command.c
sequencer.c
setup.c
sh-i18n--envsubst.c
sha1_file.c
sha1_name.c
submodule-config.h
submodule.c
t/README
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/helper/test-ref-store.c
t/interop/i5700-protocol-transition.sh [new file with mode: 0755]
t/lib-credential.sh
t/lib-gpg.sh
t/lib-httpd/apache.conf
t/lib-submodule-update.sh
t/perf/aggregate.perl
t/perf/lib-pack.sh [new file with mode: 0644]
t/perf/p4211-line-log.sh
t/perf/p5550-fetch-tags.sh
t/perf/p5551-fetch-rescan.sh [new file with mode: 0755]
t/perf/p7519-fsmonitor.sh [new file with mode: 0755]
t/perf/perf-lib.sh
t/perf/run
t/t0000-basic.sh
t/t0001-init.sh
t/t0021/rot13-filter.pl
t/t0025-crlf-renormalize.sh [new file with mode: 0755]
t/t0027-auto-crlf.sh
t/t1300-repo-config.sh
t/t1402-check-ref-format.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/t3404-rebase-interactive.sh
t/t3426-rebase-submodule.sh
t/t3512-cherry-pick-submodule.sh
t/t3600-rm.sh
t/t3700-add.sh
t/t3903-stash.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/t4202-log.sh
t/t5521-pull-options.sh
t/t5526-fetch-submodules.sh
t/t5545-push-options.sh
t/t5580-clone-push-unc.sh
t/t5601-clone.sh
t/t5603-clone-dirname.sh
t/t5700-protocol-v1.sh [new file with mode: 0755]
t/t6030-bisect-porcelain.sh
t/t6300-for-each-ref.sh
t/t7001-mv.sh
t/t7006-pager.sh
t/t7061-wtstatus-ignore.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/t7520-ignored-hook-warning.sh [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/t9902-completion.sh
t/test-lib.sh
tempfile.h
templates/hooks--fsmonitor-watchman.sample [new file with mode: 0755]
trace.c
transport-helper.c
transport.c
tree-diff.c
unpack-trees.c
upload-pack.c
walker.c
worktree.c
worktree.h
wrapper.c
wt-status.c
wt-status.h
xdiff-interface.c
xdiff-interface.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 224db83887e7e875422742048e3f68274917291d..7c71e88ea51c52d453b0d6c08a3415f4c03de22b 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 fead995eddd15460b6be81e6a5f7c8f0648368ca..281f101f3154e3884e71cde6184b59fd2b21c154 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 2c8b2d612ee0d6c1f687bfa062bb7fe6471d9280..c7096f11f1e03f5eae463fa23dc7802136097f99 100644 (file)
@@ -11,3 +11,4 @@ doc.dep
 cmds-*.txt
 mergetools-*.txt
 manpage-base-url.xsl
+SubmittingPatches.txt
index 471bb29725cdc47b4e56bc85e2af635f7b836776..2ab65561af628820f862a48ac05246cc9e773ee8 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 248ba70c3dc1c6959f24ded8db7ce0119f811ad4..cdd761bcc2072cc88523194311604f53d90dd41b 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.
diff --git a/Documentation/RelNotes/2.15.2.txt b/Documentation/RelNotes/2.15.2.txt
new file mode 100644 (file)
index 0000000..9f7e28f
--- /dev/null
@@ -0,0 +1,47 @@
+Git v2.15.2 Release Notes
+=========================
+
+Fixes since v2.15.1
+-------------------
+
+ * 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.
+
+ * The SubmittingPatches document has been converted to produce an
+   HTML version via AsciiDoc/Asciidoctor.
+
+ * 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.
+
+ * 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.
+
+ * Command line completion (in contrib/) has been taught about the
+   "--copy" option of "git branch".
+
+ * "git apply --inaccurate-eof" when used with "--ignore-space-change"
+   triggered an internal sanity check, which has been fixed.
+
+ * 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.
+
+ * 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").
+
+ * "git notes" sent its error message to its standard output stream,
+   which was corrected.
+
+ * A few scripts (both in production and tests) incorrectly redirected
+   their error output.  These have been corrected.
+
+ * Clarify and enhance documentation for "merge-base --fork-point", as
+   it was clear what it computed but not why/what for.
+
+
+Also contains various documentation updates and code clean-ups.
diff --git a/Documentation/RelNotes/2.16.0.txt b/Documentation/RelNotes/2.16.0.txt
new file mode 100644 (file)
index 0000000..431bd5e
--- /dev/null
@@ -0,0 +1,309 @@
+Git 2.16 Release Notes
+======================
+
+Backward compatibility notes and other notable changes.
+
+ * Use of an empty string as a pathspec element that is used for
+   'everything matches' is now an error.
+
+
+Updates since v2.15
+-------------------
+
+UI, Workflows & Features
+
+ * An empty string as a pathspec element that means "everything"
+   i.e. 'git add ""', is now illegal.  We started this by first
+   deprecating and warning a pathspec that has such an element in
+   2.11 (Nov 2016).
+
+ * A hook script that is set unexecutable is simply ignored.  Git
+   notifies when such a file is ignored, unless the message is
+   squelched via advice.ignoredHook configuration.
+
+ * "git pull" has been taught to accept "--[no-]signoff" option and
+   pass it down to "git merge".
+
+ * The "--push-option=<string>" option to "git push" now defaults to a
+   list of strings configured via push.pushOption variable.
+
+ * "gitweb" checks if a directory is searchable with Perl's "-x"
+   operator, which can be enhanced by using "filetest 'access'"
+   pragma, which now we do.
+
+ * "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.
+
+ * 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.
+
+ * "git config --expiry-date gc.reflogexpire" can read "2.weeks" from
+   the configuration and report it as a timestamp, just like "--int"
+   would read "1k" and report 1024, to help consumption by scripts.
+
+ * The shell completion (in contrib/) learned that "git pull" can take
+   the "--autostash" option.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * An earlier update made it possible to use an on-stack in-core
+   lockfile structure (as opposed to having to deliberately leak an
+   on-heap one).  Many codepaths have been updated to take advantage
+   of this new facility.
+
+ * Calling cmd_foo() as if it is a general purpose helper function is
+   a no-no.  Correct two instances of such to set an example.
+
+ * We try to see if somebody runs our test suite with a shell that
+   does not support "local" like bash/dash does.
+
+ * An early part of piece-by-piece rewrite of "git bisect" in C.
+
+ * GSoC to piece-by-piece rewrite "git submodule" in C.
+
+ * Optimize the code to find shortest unique prefix of object names.
+
+ * Pathspec-limited revision traversal was taught not to keep finding
+   unneeded differences once it knows two trees are different inside
+   given pathspec.
+
+ * 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.
+
+ * The build procedure has been taught to avoid some unnecessary
+   instability in the build products.
+
+ * A new mechanism to upgrade the wire protocol in place is proposed
+   and demonstrated that it works with the older versions of Git
+   without harming them.
+
+Also contains various documentation updates and code clean-ups.
+
+
+Fixes since v2.15
+-----------------
+
+ * "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.
+
+ * A (possibly flakey) test fix.
+
+ * "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.
+
+ * "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.
+
+ * 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).
+
+ * 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.
+
+ * 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.
+
+ * A few scripts (both in production and tests) incorrectly redirected
+   their error output.  These have been corrected.
+
+ * "git notes" sent its error message to its standard output stream,
+   which was corrected.
+
+ * 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").
+
+ * 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.
+
+ * "git apply --inaccurate-eof" when used with "--ignore-space-change"
+   triggered an internal sanity check, which has been fixed.
+
+ * Command line completion (in contrib/) has been taught about the
+   "--copy" option of "git branch".
+
+ * 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.
+
+ * 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.
+
+ * 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.
+
+ * "git checkout --recursive" may overwrite and rewind the history of
+   the branch that happens to be checked out in submodule
+   repositories, which might not be desirable.  Detach the HEAD but
+   still allow the recursive checkout to succeed in such a case.
+   (merge 57f22bf997 sb/submodule-recursive-checkout-detach-head later to maint).
+
+ * "git branch --set-upstream" has been deprecated and (sort of)
+   removed, as "--set-upstream-to" is the preferred one these days.
+   The documentation still had "--set-upstream" listed on its
+   synopsys section, which has been corrected.
+   (merge a060f3d3d8 tz/branch-doc-remove-set-upstream later to maint).
+
+ * Internaly we use 0{40} as a placeholder object name to signal the
+   codepath that there is no such object (e.g. the fast-forward check
+   while "git fetch" stores a new remote-tracking ref says "we know
+   there is no 'old' thing pointed at by the ref, as we are creating
+   it anew" by passing 0{40} for the 'old' side), and expect that a
+   codepath to locate an in-core object to return NULL as a sign that
+   the object does not exist.  A look-up for an object that does not
+   exist however is quite costly with a repository with large number
+   of packfiles.  This access pattern has been optimized.
+   (merge 87b5e236a1 jk/fewer-pack-rescan later to maint).
+
+ * In addition to "git stash -m message", the command learned to
+   accept "git stash -mmessage" form.
+   (merge 5675473fcb ph/stash-save-m-option-fix later to maint).
+
+ * Other minor doc, test and build updates and code cleanups.
+   (merge 1a1fc2d5b5 rd/man-prune-progress later to maint).
+   (merge 0ba014035a rd/man-reflog-add-n later to maint).
+   (merge e54b63359f rd/doc-notes-prune-fix later to maint).
+   (merge ff4c9b413a sp/doc-info-attributes later to maint).
index 558d465b656a0b606a480e0205443fdc8b0d23fe..3ef30922ecdce94c84c5e9df33f6696622ab4844 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 1ac0ae6adb0460a3460084aef2302434e24ee6de..c1598ee7039fca0439a18b31f186052bbb40bbce 100644 (file)
@@ -351,6 +351,9 @@ advice.*::
        addEmbeddedRepo::
                Advice on what to do when you've accidentally added one
                git repo inside of another.
+       ignoredHook::
+               Advice shown if an hook is ignored because the hook is not
+               set as executable.
 --
 
 core.fileMode::
@@ -413,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
@@ -949,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
@@ -2081,15 +2108,40 @@ matched against are those given directly to Git commands.  This means any URLs
 visited as a result of a redirection do not participate in matching.
 
 ssh.variant::
-       Depending on the value of the environment variables `GIT_SSH` or
-       `GIT_SSH_COMMAND`, or the config setting `core.sshCommand`, Git
-       auto-detects whether to adjust its command-line parameters for use
-       with plink or tortoiseplink, as opposed to the default (OpenSSH).
+       By default, Git determines the command line arguments to use
+       based on the basename of the configured SSH command (configured
+       using the environment variable `GIT_SSH` or `GIT_SSH_COMMAND` or
+       the config setting `core.sshCommand`). If the basename is
+       unrecognized, Git will attempt to detect support of OpenSSH
+       options by first invoking the configured SSH command with the
+       `-G` (print configuration) option and will subsequently use
+       OpenSSH options (if that is successful) or no options besides
+       the host and remote command (if it fails).
++
+The config variable `ssh.variant` can be set to override this detection.
+Valid values are `ssh` (to use OpenSSH options), `plink`, `putty`,
+`tortoiseplink`, `simple` (no options except the host and remote command).
+The default auto-detection can be explicitly requested using the value
+`auto`.  Any other value is treated as `ssh`.  This setting can also be
+overridden via the environment variable `GIT_SSH_VARIANT`.
++
+The current command-line parameters used for each variant are as
+follows:
++
+--
+
+* `ssh` - [-p port] [-4] [-6] [-o option] [username@]host command
+
+* `simple` - [username@]host command
+
+* `plink` or `putty` - [-P port] [-4] [-6] [username@]host command
+
+* `tortoiseplink` - [-P port] [-4] [-6] -batch [username@]host command
+
+--
 +
-The config variable `ssh.variant` can be set to override this auto-detection;
-valid values are `ssh`, `plink`, `putty` or `tortoiseplink`. Any other value
-will be treated as normal ssh. This setting can be overridden via the
-environment variable `GIT_SSH_VARIANT`.
+Except for the `simple` variant, command-line parameters are likely to
+change as git gains new features.
 
 i18n.commitEncoding::
        Character encoding the commit messages are stored in; Git itself
@@ -2517,6 +2569,23 @@ The protocol names currently used by git are:
     `hg` to allow the `git-remote-hg` helper)
 --
 
+protocol.version::
+       Experimental. If set, clients will attempt to communicate with a
+       server using the specified protocol version.  If unset, no
+       attempt will be made by the client to communicate using a
+       particular protocol version, this results in protocol version 0
+       being used.
+       Supported versions:
++
+--
+
+* `0` - the original wire protocol.
+
+* `1` - the original wire protocol with the addition of a version string
+  in the initial response from the server.
+
+--
+
 pull.ff::
        By default, Git does not create an extra merge commit when merging
        a commit that is a descendant of the current commit. Instead, the
@@ -2621,6 +2690,35 @@ push.gpgSign::
        override a value from a lower-priority config file. An explicit
        command-line flag always overrides this config option.
 
+push.pushOption::
+       When no `--push-option=<option>` argument is given from the
+       command line, `git push` behaves as if each <value> of
+       this variable is given as `--push-option=<value>`.
++
+This is a multi-valued variable, and an empty value can be used in a
+higher priority configuration file (e.g. `.git/config` in a
+repository) to clear the values inherited from a lower priority
+configuration files (e.g. `$HOME/.gitconfig`).
++
+--
+
+Example:
+
+/etc/gitconfig
+  push.pushoption = a
+  push.pushoption = b
+
+~/.gitconfig
+  push.pushoption = c
+
+repo/.git/config
+  push.pushoption =
+  push.pushoption = b
+
+This will result in only b (a and c are cleared).
+
+--
+
 push.recurseSubmodules::
        Make sure all submodule commits used by the revisions to be pushed
        are available on a remote-tracking branch. If the value is 'check'
@@ -2951,6 +3049,7 @@ sendemail.smtpPass::
 sendemail.suppresscc::
 sendemail.suppressFrom::
 sendemail.to::
+sendemail.tocmd::
 sendemail.smtpDomain::
 sendemail.smtpServer::
 sendemail.smtpServerPort::
diff --git a/Documentation/diff-heuristic-options.txt b/Documentation/diff-heuristic-options.txt
deleted file mode 100644 (file)
index d4f3d95..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
---indent-heuristic::
---no-indent-heuristic::
-       These are to help debugging and tuning experimental heuristics
-       (which are off by default) that shift diff hunk boundaries to
-       make patches easier to read.
index a88c76741e781f5888fd467e17d02c633c87f15d..3c93c216831e722ec9b8b8a231f455df328e7fe2 100644 (file)
@@ -63,7 +63,12 @@ ifndef::git-format-patch[]
        Synonym for `-p --raw`.
 endif::git-format-patch[]
 
-include::diff-heuristic-options.txt[]
+--indent-heuristic::
+       Enable the heuristic that shift diff hunk boundaries to make patches
+       easier to read. This is the default.
+
+--no-indent-heuristic::
+       Disable the indent heuristic.
 
 --minimal::
        Spend extra time to make sure the smallest possible
@@ -552,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 b700beaff5ad2eaa407bfb3818e016a5bb27d87c..d50fa339dcc523158896fda8cdccd1a2784dfdd7 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 94be4b85e099f8b67901a737b8347ee02e6c6a6c..05fd482b74ed00445613c7cc9b8a0b019ae31550 100644 (file)
@@ -23,7 +23,6 @@ familiar command name for people coming from other SCM systems.
 OPTIONS
 -------
 include::blame-options.txt[]
-include::diff-heuristic-options.txt[]
 
 SEE ALSO
 --------
index 6c42abf070df93187e11dd31d263b199346cace2..4a1417bdcd7826d444dbfd4cbc438ec9ec2edf1b 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 fdc3aea30a4cdb480d469455a66e51a76366be93..16323eb80e3108794067c4dbfcbfe25e46938498 100644 (file)
@@ -89,8 +89,6 @@ include::blame-options.txt[]
        abbreviated object name, use <n>+1 digits. Note that 1 column
        is used for a caret to mark the boundary commit.
 
-include::diff-heuristic-options.txt[]
-
 
 THE PORCELAIN FORMAT
 --------------------
index d6587c5e96f4dbcdb4176d9dd5f6a992c0e83f3b..b3084c99c1cabdccc690e4cec5071b22774bf6c8 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 92777cef25e1145fc2a060d572255be564dec491..cf0a0b7df28e560a243805618bf9e5b093354eeb 100644 (file)
@@ -77,7 +77,14 @@ reference name expressions (see linkgit:gitrevisions[7]):
 
 . at-open-brace `@{` is used as a notation to access a reflog entry.
 
-With the `--branch` option, it expands the ``previous branch syntax''
+With the `--branch` option, the command takes a name and checks if
+it can be used as a valid branch name (e.g. when creating a new
+branch).  The rule `git check-ref-format --branch $name` implements
+may be stricter than what `git check-ref-format refs/heads/$name`
+says (e.g. a dash may appear at the beginning of a ref component,
+but it is explicitly forbidden at the beginning of a branch name).
+When run with `--branch` option in a repository, the input is first
+expanded for the ``previous branch syntax''
 `@{-n}`.  For example, `@{-1}` is a way to refer the last branch you
 were on.  This option should be used by porcelains to accept this
 syntax anywhere a branch name is expected, so they can act as if you
index e108b0f74bb6dc4d9f3fc27e7c3fe571b19a9d2f..ca5fc9c79887652e38e3d11dc86dba657fe585c0 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,
@@ -272,11 +274,11 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
        commit, your HEAD becomes "detached" and you are no longer on
        any branch (see below for details).
 +
-As a special case, the `"@{-N}"` syntax for the N-th last branch/commit
-checks out branches (instead of detaching).  You may also specify
-`-` which is synonymous with `"@{-1}"`.
+You can use the `"@{-N}"` syntax to refer to the N-th last
+branch/commit checked out using "git checkout" operation. You may
+also specify `-` which is synonymous to `"@{-1}`.
 +
-As a further special case, you may use `"A...B"` as a shortcut for the
+As a special case, you may use `"A...B"` as a shortcut for the
 merge base of `A` and `B` if there is exactly one merge base. You can
 leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
index 4edd09fc6b0742042f3236598d8ecb793773d8ab..14da5fc157ee0ea7467e7b9bd9d33d465c1f7a3f 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 1d420e4cde8230de00aae583a296128fdd59140f..dffa14a7950e074bbff73ec79defdbbdcc9702be 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 32246fdb007e9b25f414cff092b9bf0426c55d15..5437f8b0f0e6699eca662879290d47df85387f0f 100644 (file)
@@ -38,6 +38,13 @@ OPTIONS
        are shown as if 'short' were given, otherwise no ref names are
        shown. The default option is 'short'.
 
+--decorate-refs=<pattern>::
+--decorate-refs-exclude=<pattern>::
+       If no `--decorate-refs` is given, pretend as if all refs were
+       included.  For each candidate, do not use it for decoration if it
+       matches any patterns given to `--decorate-refs-exclude` or if it
+       doesn't match any of the patterns given to `--decorate-refs`.
+
 --source::
        Print out the ref name given on the command line by which each
        commit was reached.
index d153c17e0660d8ab9edb0b10a53172007d355510..3ac3e3a77d171d55a2992b263e03350cf4c08b3d 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 b968b64c380b4fbe2663da42355fc9e8fdd24679..502e00ec35ff1fd543dbd8361a317cb583ca1eba 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 3c6927b1fb87940c53ff01339a297d6b85dc1676..d5dfd8430f3370dc168e42e3991d1d4a9b854617 100644 (file)
@@ -64,14 +64,6 @@ OPTIONS
 -------
 include::merge-options.txt[]
 
---signoff::
-       Add Signed-off-by line by the committer at the end of the commit
-       log message.  The meaning of a signoff depends on the project,
-       but it typically certifies that committer has
-       the rights to submit this work under the same license and
-       agrees to a Developer Certificate of Origin
-       (see http://developercertificate.org/ for more information).
-
 -m <msg>::
        Set the commit message to be used for the merge commit (in
        case one is created).
index 43677297f3b85814c18c972a159b81c8076bc445..e8dec1b3c89a66dc2f9e78737fa7d243bee85d37 100644 (file)
@@ -18,7 +18,7 @@ SYNOPSIS
 'git notes' merge --commit [-v | -q]
 'git notes' merge --abort [-v | -q]
 'git notes' remove [--ignore-missing] [--stdin] [<object>...]
-'git notes' prune [-n | -v]
+'git notes' prune [-n] [-v]
 'git notes' get-ref
 
 
index 7a493c80f776092265abd4fb2575639e683bcf5c..a37c0af9313e8e47806c89a584c70c09517244d1 100644 (file)
@@ -9,7 +9,7 @@ git-prune - Prune all unreachable objects from the object database
 SYNOPSIS
 --------
 [verse]
-'git prune' [-n] [-v] [--expire <expire>] [--] [<head>...]
+'git prune' [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]
 
 DESCRIPTION
 -----------
@@ -42,12 +42,15 @@ OPTIONS
 --verbose::
        Report all removed objects.
 
-\--::
-       Do not interpret any more arguments as options.
+--progress::
+       Show progress.
 
 --expire <time>::
        Only expire loose objects older than <time>.
 
+\--::
+       Do not interpret any more arguments as options.
+
 <head>...::
        In addition to objects
        reachable from any of our references, keep objects
index 3e76e99f38f67a530d43e2da3b3129c2a99e3366..5b08302fc2299fe1b42fc0137b50a3bab774ca10 100644 (file)
@@ -156,11 +156,17 @@ already exists on the remote side.
        Either all refs are updated, or on error, no refs are updated.
        If the server does not support atomic pushes the push will fail.
 
--o::
---push-option::
+-o <option>::
+--push-option=<option>::
        Transmit the given string to the server, which passes them to
        the pre-receive as well as the post-receive hook. The given string
        must not contain a NUL or LF character.
+       When multiple `--push-option=<option>` are given, they are
+       all sent to the other side in the order listed on the
+       command line.
+       When no `--push-option=<option>` is given from the command
+       line, the values of configuration variable `push.pushOption`
+       are used instead.
 
 --receive-pack=<git-receive-pack>::
 --exec=<git-receive-pack>::
index 44c736f1a8e581bc072d5de3b719284f7376ec3b..472a6808cd60899f4554e998b9fb8e4f3103aac2 100644 (file)
@@ -20,9 +20,9 @@ depending on the subcommand:
 'git reflog' ['show'] [log-options] [<ref>]
 'git reflog expire' [--expire=<time>] [--expire-unreachable=<time>]
        [--rewrite] [--updateref] [--stale-fix]
-       [--dry-run] [--verbose] [--all | <refs>...]
+       [--dry-run | -n] [--verbose] [--all | <refs>...]
 'git reflog delete' [--rewrite] [--updateref]
-       [--dry-run] [--verbose] ref@\{specifier\}...
+       [--dry-run | -n] [--verbose] ref@\{specifier\}...
 'git reflog exists' <ref>
 
 Reference logs, or "reflogs", record when the tips of branches and
index bac9014ac71775e5116ad7686af697eb22286ee2..8060ea35c5f932c1e4328f000a501ff399418f27 100644 (file)
@@ -203,9 +203,9 @@ a password is obtained using 'git-credential'.
        specify a full pathname of a sendmail-like program instead;
        the program must support the `-i` option.  Default value can
        be specified by the `sendemail.smtpServer` configuration
-       option; the built-in default is `/usr/sbin/sendmail` or
-       `/usr/lib/sendmail` if such program is available, or
-       `localhost` otherwise.
+       option; the built-in default is to search for `sendmail` in
+       `/usr/sbin`, `/usr/lib` and $PATH if such program is
+       available, falling back to `localhost` otherwise.
 
 --smtp-server-port=<port>::
        Specifies a port different from the default port (SMTP
index 00f95fee1faf52138ce014104148c444be89dabe..056dfb86612d0c5be0d01f16ac6b126fac80b92c 100644 (file)
@@ -13,8 +13,6 @@ SYNOPSIS
 'git stash' drop [-q|--quiet] [<stash>]
 'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
 'git stash' branch <branchname> [<stash>]
-'git stash' save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
-            [-u|--include-untracked] [-a|--all] [<message>]
 'git stash' [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
             [-u|--include-untracked] [-a|--all] [-m|--message <message>]]
             [--] [<pathspec>...]]
@@ -33,7 +31,7 @@ and reverts the working directory to match the `HEAD` commit.
 The modifications stashed away by this command can be listed with
 `git stash list`, inspected with `git stash show`, and restored
 (potentially on top of a different commit) with `git stash apply`.
-Calling `git stash` without any arguments is equivalent to `git stash save`.
+Calling `git stash` without any arguments is equivalent to `git stash push`.
 A stash is by default listed as "WIP on 'branchname' ...", but
 you can give a more descriptive message on the command line when
 you create one.
@@ -48,7 +46,6 @@ stash index (e.g. the integer `n` is equivalent to `stash@{n}`).
 OPTIONS
 -------
 
-save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::
 push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]::
 
        Save your local modifications to a new 'stash entry' and roll them
@@ -87,6 +84,12 @@ linkgit:git-add[1] to learn how to operate the `--patch` mode.
 The `--patch` option implies `--keep-index`.  You can use
 `--no-keep-index` to override this.
 
+save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::
+
+       This option is deprecated in favour of 'git stash push'.  It
+       differs from "stash push" in that it cannot take pathspecs,
+       and any non-option arguments form the message.
+
 list [<options>]::
 
        List the stash entries that you currently have.  Each 'stash entry' is
@@ -118,7 +121,7 @@ pop [--index] [-q|--quiet] [<stash>]::
 
        Remove a single stashed state from the stash list and apply it
        on top of the current working tree state, i.e., do the inverse
-       operation of `git stash save`. The working directory must
+       operation of `git stash push`. The working directory must
        match the index.
 +
 Applying the state can fail with conflicts; in this case, it is not
@@ -137,7 +140,7 @@ apply [--index] [-q|--quiet] [<stash>]::
 
        Like `pop`, but do not remove the state from the stash list. Unlike `pop`,
        `<stash>` may be any commit that looks like a commit created by
-       `stash save` or `stash create`.
+       `stash push` or `stash create`.
 
 branch <branchname> [<stash>]::
 
@@ -148,7 +151,7 @@ branch <branchname> [<stash>]::
        `stash@{<revision>}`, it then drops the `<stash>`. When no `<stash>`
        is given, applies the latest one.
 +
-This is useful if the branch on which you ran `git stash save` has
+This is useful if the branch on which you ran `git stash push` has
 changed enough that `git stash apply` fails due to conflicts. Since
 the stash entry is applied on top of the commit that was HEAD at the
 time `git stash` was run, it restores the originally stashed state
@@ -172,14 +175,14 @@ create::
        return its object name, without storing it anywhere in the ref
        namespace.
        This is intended to be useful for scripts.  It is probably not
-       the command you want to use; see "save" above.
+       the command you want to use; see "push" above.
 
 store::
 
        Store a given stash created via 'git stash create' (which is a
        dangling merge commit) in the stash ref, updating the stash
        reflog.  This is intended to be useful for scripts.  It is
-       probably not the command you want to use; see "save" above.
+       probably not the command you want to use; see "push" above.
 
 DISCUSSION
 ----------
@@ -255,14 +258,14 @@ $ git stash pop
 
 Testing partial commits::
 
-You can use `git stash save --keep-index` when you want to make two or
+You can use `git stash push --keep-index` when you want to make two or
 more commits out of the changes in the work tree, and you want to test
 each change before committing:
 +
 ----------------------------------------------------------------
 # ... hack hack hack ...
 $ git add --patch foo            # add just first part to the index
-$ git stash save --keep-index    # save all other changes to the stash
+$ git stash push --keep-index    # save all other changes to the stash
 $ edit/build/test first part
 $ git commit -m 'First part'     # commit fully tested change
 $ git stash pop                  # prepare to work on all other changes
index 9f3a78a36c48c55318ee0eea8e96a64ccce5bfa2..fc282e0a920c84f491d24e3cf765ca634b54c425 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 75c7dd9dea4d2b6c26b84b99fb21ba75cf1cdcf3..bdb0342593229e32425bf73d17ad6fcdd0dd3732 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 7a1d629ca068059d274da66728eb7f886b43081d..e75db104e3a33dde93a98cc09de97f4d4f0bc5eb 100644 (file)
@@ -522,11 +522,10 @@ other
        If either of these environment variables is set then 'git fetch'
        and 'git push' will use the specified command instead of 'ssh'
        when they need to connect to a remote system.
-       The command will be given exactly two or four arguments: the
-       'username@host' (or just 'host') from the URL and the shell
-       command to execute on that remote system, optionally preceded by
-       `-p` (literally) and the 'port' from the URL when it specifies
-       something other than the default SSH port.
+       The command-line parameters passed to the configured command are
+       determined by the ssh variant.  See `ssh.variant` option in
+       linkgit:git-config[1] for details.
+
 +
 `$GIT_SSH_COMMAND` takes precedence over `$GIT_SSH`, and is interpreted
 by the shell, which allows additional arguments to be included.
@@ -595,6 +594,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
@@ -701,6 +704,12 @@ of clones and fetches.
        which feed potentially-untrusted URLS to git commands.  See
        linkgit:git-config[1] for more details.
 
+`GIT_PROTOCOL`::
+       For internal use only.  Used in handshaking the wire protocol.
+       Contains a colon ':' separated list of keys with optional values
+       'key[=value]'.  Presence of unknown keys and values must be
+       ignored.
+
 `GIT_OPTIONAL_LOCKS`::
        If set to `0`, Git will complete any requested operation without
        performing any optional sub-operations that require taking a lock.
@@ -709,6 +718,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 4c68bc19d5108d1ba0c91b05d35acf8060eb2c9d..30687de81a6e40b3d9cc120f659817b1a8d03603 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 5d3f45560ea9c29ec382c8772522f06ab5f81f73..b63f2ea860be34fb49de77a2de5b68336e4a49bd 100644 (file)
@@ -223,8 +223,8 @@ to the user by writing to standard error.
 pre-receive
 ~~~~~~~~~~~
 
-This hook is invoked by 'git-receive-pack' on the remote repository,
-which happens when a 'git push' is done on a local repository.
+This hook is invoked by 'git-receive-pack' when it reacts to
+'git push' and updates reference(s) in its repository.
 Just before starting to update refs on the remote repository, the
 pre-receive hook is invoked.  Its exit status determines the success
 or failure of the update.
@@ -264,8 +264,8 @@ linkgit:git-receive-pack[1] for some caveats.
 update
 ~~~~~~
 
-This hook is invoked by 'git-receive-pack' on the remote repository,
-which happens when a 'git push' is done on a local repository.
+This hook is invoked by 'git-receive-pack' when it reacts to
+'git push' and updates reference(s) in its repository.
 Just before updating the ref on the remote repository, the update hook
 is invoked.  Its exit status determines the success or failure of
 the ref update.
@@ -309,8 +309,8 @@ unannotated tags to be pushed.
 post-receive
 ~~~~~~~~~~~~
 
-This hook is invoked by 'git-receive-pack' on the remote repository,
-which happens when a 'git push' is done on a local repository.
+This hook is invoked by 'git-receive-pack' when it reacts to
+'git push' and updates reference(s) in its repository.
 It executes on the remote repository once after all the refs have
 been updated.
 
@@ -348,8 +348,8 @@ will be set to zero, `GIT_PUSH_OPTION_COUNT=0`.
 post-update
 ~~~~~~~~~~~
 
-This hook is invoked by 'git-receive-pack' on the remote repository,
-which happens when a 'git push' is done on a local repository.
+This hook is invoked by 'git-receive-pack' when it reacts to
+'git push' and updates reference(s) in its repository.
 It executes on the remote repository once after all the refs have
 been updated.
 
@@ -379,8 +379,8 @@ for the user.
 push-to-checkout
 ~~~~~~~~~~~~~~~~
 
-This hook is invoked by 'git-receive-pack' on the remote repository,
-which happens when a 'git push' is done on a local repository, when
+This hook is invoked by 'git-receive-pack' when it reacts to
+'git push' and updates reference(s) in its repository, and when
 the push tries to update the branch that is currently checked out
 and the `receive.denyCurrentBranch` configuration variable is set to
 `updateInstead`.  Such a push by default is refused if the working
@@ -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 adf9554ad2f84cb69a6b0a940e4d3e0e336df9ff..c60bcad44aa581b2449a7a638b6487c1c73e8c23 100644 (file)
@@ -208,6 +208,10 @@ info/exclude::
        'git clean' look at it but the core Git commands do not look
        at it.  See also: linkgit:gitignore[5].
 
+info/attributes::
+       Defines which attributes to assign to a path, similar to per-directory
+       `.gitattributes` files.   See also: linkgit:gitattributes[5].
+
 info/sparse-checkout::
        This file stores sparse checkout patterns.
        See also: linkgit:git-read-tree[1].
index 177610e44ea31eede07035292a66b0dca167c9ae..02569d0614773276841a1d91a7b90cb353b8ba5e 100644 (file)
@@ -40,7 +40,7 @@ beginning. It is always easier to squash a few commits together than
 to split one big commit into several.  Don't be afraid of making too
 small or imperfect steps along the way. You can always go back later
 and edit the commits with `git rebase --interactive` before you
-publish them.  You can use `git stash save --keep-index` to run the
+publish them.  You can use `git stash push --keep-index` to run the
 test suite independent of other uncommitted changes; see the EXAMPLES
 section of linkgit:git-stash[1].
 
index 2552ab8e8d5104d09c1c95010ab028068c7d18ae..3888c3ff85e2dc5b137e4e3ed50e39327760a02a 100644 (file)
@@ -57,6 +57,16 @@ set to `no` at the beginning of them.
 With --no-log do not list one-line descriptions from the
 actual commits being merged.
 
+--signoff::
+--no-signoff::
+       Add Signed-off-by line by the committer at the end of the commit
+       log message.  The meaning of a signoff depends on the project,
+       but it typically certifies that committer has
+       the rights to submit this work under the same license and
+       agrees to a Developer Certificate of Origin
+       (see http://developercertificate.org/ for more information).
++
+With --no-signoff do not add a Signed-off-by line.
 
 --stat::
 -n::
index a09d5974639fa772ba86c5547fe70716fd044f73..fd5d748d1b508c9cef1063227b5c478952d3bc7e 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 6c77b4920c92a0b327fb88d2b461ec0d918ec99a..7fae00f44fe1798da82bfdbb902479a03d583843 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 1c561bdd9204fe221512064f765bea9eb7ec5945..a0e45f2889e6e52db71f88d7368bbb2efc02616a 100644 (file)
@@ -219,6 +219,10 @@ smart server reply:
    S: 003c2cb58b79488a98d2721cea644875a8dd0026b115 refs/tags/v1.0\n
    S: 003fa3c2e2402b99163d1d59756e5f207ae21cccba4c refs/tags/v1.0^{}\n
 
+The client may send Extra Parameters (see
+Documentation/technical/pack-protocol.txt) as a colon-separated string
+in the Git-Protocol HTTP header.
+
 Dumb Server Response
 ^^^^^^^^^^^^^^^^^^^^
 Dumb servers MUST respond with the dumb server reply format.
@@ -269,7 +273,11 @@ the C locale ordering.  The stream SHOULD include the default ref
 named `HEAD` as the first ref.  The stream MUST include capability
 declarations behind a NUL on the first ref.
 
+The returned response contains "version 1" if "version=1" was sent as an
+Extra Parameter.
+
   smart_reply     =  PKT-LINE("# service=$servicename" LF)
+                    *1("version 1")
                     ref_list
                     "0000"
   ref_list        =  empty_list / non_empty_list
index ade0b0c4454651812e88b292a846f23a58925f5f..db3572626beba6fc04f169b4ccccca0084d7326b 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 ed1eae8b83a651d34f9ce2488f58d02a763fbfd4..cd31edc91ea1989adc7f2321327c0a97685fe62f 100644 (file)
@@ -39,6 +39,19 @@ communicates with that invoked process over the SSH connection.
 The file:// transport runs the 'upload-pack' or 'receive-pack'
 process locally and communicates with it over a pipe.
 
+Extra Parameters
+----------------
+
+The protocol provides a mechanism in which clients can send additional
+information in its first message to the server. These are called "Extra
+Parameters", and are supported by the Git, SSH, and HTTP protocols.
+
+Each Extra Parameter takes the form of `<key>=<value>` or `<key>`.
+
+Servers that receive any such Extra Parameters MUST ignore all
+unrecognized keys. Currently, the only Extra Parameter recognized is
+"version=1".
+
 Git Transport
 -------------
 
@@ -46,18 +59,25 @@ The Git transport starts off by sending the command and repository
 on the wire using the pkt-line format, followed by a NUL byte and a
 hostname parameter, terminated by a NUL byte.
 
-   0032git-upload-pack /project.git\0host=myserver.com\0
+   0033git-upload-pack /project.git\0host=myserver.com\0
+
+The transport may send Extra Parameters by adding an additional NUL
+byte, and then adding one or more NUL-terminated strings:
+
+   003egit-upload-pack /project.git\0host=myserver.com\0\0version=1\0
 
 --
-   git-proto-request = request-command SP pathname NUL [ host-parameter NUL ]
+   git-proto-request = request-command SP pathname NUL
+                      [ host-parameter NUL ] [ NUL extra-parameters ]
    request-command   = "git-upload-pack" / "git-receive-pack" /
                       "git-upload-archive"   ; case sensitive
    pathname          = *( %x01-ff ) ; exclude NUL
    host-parameter    = "host=" hostname [ ":" port ]
+   extra-parameters  = 1*extra-parameter
+   extra-parameter   = 1*( %x01-ff ) NUL
 --
 
-Only host-parameter is allowed in the git-proto-request. Clients
-MUST NOT attempt to send additional parameters. It is used for the
+host-parameter is used for the
 git-daemon name based virtual hosting.  See --interpolated-path
 option to git daemon, with the %H/%CH format characters.
 
@@ -117,6 +137,12 @@ we execute it without the leading '/'.
                     v
    ssh user@example.com "git-upload-pack '~alice/project.git'"
 
+Depending on the value of the `protocol.version` configuration variable,
+Git may attempt to send Extra Parameters as a colon-separated string in
+the GIT_PROTOCOL environment variable. This is done only if
+the `ssh.variant` configuration variable indicates that the ssh command
+supports passing environment variables as an argument.
+
 A few things to remember here:
 
 - The "command name" is spelled with dash (e.g. git-upload-pack), but
@@ -137,11 +163,13 @@ Reference Discovery
 -------------------
 
 When the client initially connects the server will immediately respond
-with a listing of each reference it has (all branches and tags) along
+with a version number (if "version=1" is sent as an Extra Parameter),
+and a listing of each reference it has (all branches and tags) along
 with the object name that each reference currently points to.
 
-   $ echo -e -n "0039git-upload-pack /schacon/gitbook.git\0host=example.com\0" |
+   $ echo -e -n "0044git-upload-pack /schacon/gitbook.git\0host=example.com\0\0version=1\0" |
       nc -v example.com 9418
+   000aversion 1
    00887217a7c7e582c46cec22a130adf4b9d7d950fba0 HEAD\0multi_ack thin-pack
                side-band side-band-64k ofs-delta shallow no-progress include-tag
    00441d3fcd5ced445d1abc402225c0b8a1299641f497 refs/heads/integration
@@ -165,7 +193,8 @@ immediately after the ref itself, if presented. A conforming server
 MUST peel the ref if it's an annotated tag.
 
 ----
-  advertised-refs  =  (no-refs / list-of-refs)
+  advertised-refs  =  *1("version 1")
+                     (no-refs / list-of-refs)
                      *shallow
                      flush-pkt
 
index b4d88af133e8eafd92f86443bd74de063bad4835..3a03e63eb0d853f5f423977147f67769e1e33924 100644 (file)
@@ -1556,7 +1556,7 @@ so on a different branch and then coming back), unstash the
 work-in-progress changes.
 
 ------------------------------------------------
-$ git stash save "work in progress for foo feature"
+$ git stash push -m "work in progress for foo feature"
 ------------------------------------------------
 
 This command will save your changes away to the `stash`, and
index a9dbc3f12c428ff4e86932ad1eb29448b9290812..c1906f0fd1588ebb296f80b478f138dab4dc7c5a 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.15.0
+DEF_VER=v2.15.GIT
 
 LF='
 '
index cd75985991f4535c45e2589222a9e6a38fb1d613..fef9c8d2725d6806d9ef4516de64c6d68bd7a7fe 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
@@ -846,6 +849,7 @@ LIB_OBJS += pretty.o
 LIB_OBJS += prio-queue.o
 LIB_OBJS += progress.o
 LIB_OBJS += prompt.o
+LIB_OBJS += protocol.o
 LIB_OBJS += quote.o
 LIB_OBJS += reachable.o
 LIB_OBJS += read-cache.o
@@ -1940,7 +1944,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.
index 80ae7a3110de6bd298cfa9d8481808d0af5e7d2d..bec73d6767c965a8cfa684c96eaa8eefb66901d3 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.15.0.txt
\ No newline at end of file
+Documentation/RelNotes/2.16.0.txt
\ No newline at end of file
index d81e1cb7425b0f2d75461865b5c965a21ada5587..c6169bcb52fa935969ee938967adb337b06961c7 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -17,6 +17,7 @@ int advice_set_upstream_failure = 1;
 int advice_object_name_warning = 1;
 int advice_rm_hints = 1;
 int advice_add_embedded_repo = 1;
+int advice_ignored_hook = 1;
 
 static struct {
        const char *name;
@@ -38,6 +39,7 @@ static struct {
        { "objectnamewarning", &advice_object_name_warning },
        { "rmhints", &advice_rm_hints },
        { "addembeddedrepo", &advice_add_embedded_repo },
+       { "ignoredhook", &advice_ignored_hook },
 
        /* make this an alias for backward compatibility */
        { "pushnonfastforward", &advice_push_update_rejected }
index c84a44531c7d8fcc9867b2df4ba9dc11d0766508..f525d6f89cb44dc39188dd06472cf8a59b56f411 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -19,6 +19,7 @@ extern int advice_set_upstream_failure;
 extern int advice_object_name_warning;
 extern int advice_rm_hints;
 extern int advice_add_embedded_repo;
+extern int advice_ignored_hook;
 
 int git_default_advice_config(const char *var, const char *value);
 __attribute__((format (printf, 1, 2)))
diff --git a/apply.c b/apply.c
index c022af53a24bf56556d136a3456862cf6a2ca7cc..321a9fa68d491f7e5e89dc9e398b067f67a10c77 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -75,13 +75,10 @@ static int parse_ignorewhitespace_option(struct apply_state *state,
 }
 
 int init_apply_state(struct apply_state *state,
-                    const char *prefix,
-                    struct lock_file *lock_file)
+                    const char *prefix)
 {
        memset(state, 0, sizeof(*state));
        state->prefix = prefix;
-       state->lock_file = lock_file;
-       state->newfd = -1;
        state->apply = 1;
        state->line_termination = '\n';
        state->p_value = 1;
@@ -146,8 +143,6 @@ int check_apply_state(struct apply_state *state, int force_apply)
        }
        if (state->check_index)
                state->unsafe_paths = 0;
-       if (!state->lock_file)
-               return error("BUG: state->lock_file should not be NULL");
 
        if (state->apply_verbosity <= verbosity_silent) {
                state->saved_error_routine = get_error_routine();
@@ -305,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)
@@ -2977,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;
@@ -4709,13 +4687,13 @@ static int apply_patch(struct apply_state *state,
                state->apply = 0;
 
        state->update_index = state->check_index && state->apply;
-       if (state->update_index && state->newfd < 0) {
+       if (state->update_index && !is_lock_file_locked(&state->lock_file)) {
                if (state->index_file)
-                       state->newfd = hold_lock_file_for_update(state->lock_file,
-                                                                state->index_file,
-                                                                LOCK_DIE_ON_ERROR);
+                       hold_lock_file_for_update(&state->lock_file,
+                                                 state->index_file,
+                                                 LOCK_DIE_ON_ERROR);
                else
-                       state->newfd = hold_locked_index(state->lock_file, LOCK_DIE_ON_ERROR);
+                       hold_locked_index(&state->lock_file, LOCK_DIE_ON_ERROR);
        }
 
        if (state->check_index && read_apply_cache(state) < 0) {
@@ -4911,22 +4889,18 @@ int apply_all_patches(struct apply_state *state,
        }
 
        if (state->update_index) {
-               res = write_locked_index(&the_index, state->lock_file, COMMIT_LOCK);
+               res = write_locked_index(&the_index, &state->lock_file, COMMIT_LOCK);
                if (res) {
                        error(_("Unable to write new index file"));
                        res = -128;
                        goto end;
                }
-               state->newfd = -1;
        }
 
        res = !!errs;
 
 end:
-       if (state->newfd >= 0) {
-               rollback_lock_file(state->lock_file);
-               state->newfd = -1;
-       }
+       rollback_lock_file(&state->lock_file);
 
        if (state->apply_verbosity <= verbosity_silent) {
                set_error_routine(state->saved_error_routine);
diff --git a/apply.h b/apply.h
index d9b395770364b5495fc1136248efd64bd3a5e66d..dc4a0190570d060d23e8464f465232eddcf76deb 100644 (file)
--- a/apply.h
+++ b/apply.h
@@ -36,9 +36,8 @@ enum apply_verbosity {
 struct apply_state {
        const char *prefix;
 
-       /* These are lock_file related */
-       struct lock_file *lock_file;
-       int newfd;
+       /* Lock file */
+       struct lock_file lock_file;
 
        /* These control what gets looked at and modified */
        int apply; /* this is not a dry-run */
@@ -116,8 +115,7 @@ extern int apply_parse_options(int argc, const char **argv,
                               int *force_apply, int *options,
                               const char * const *apply_usage);
 extern int init_apply_state(struct apply_state *state,
-                           const char *prefix,
-                           struct lock_file *lock_file);
+                           const char *prefix);
 extern void clear_apply_state(struct apply_state *state);
 extern int check_apply_state(struct apply_state *state, int force_apply);
 
index 1e41f4bbeb1d8730da83aad12bc8987abedcad8a..0b7b62af0c3ecee10a26e9bd2d274690604ffcad 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -371,7 +371,7 @@ static void parse_treeish_arg(const char **argv,
                const char *colon = strchrnul(name, ':');
                int refnamelen = colon - name;
 
-               if (!dwim_ref(name, refnamelen, oid.hash, &ref))
+               if (!dwim_ref(name, refnamelen, &oid, &ref))
                        die("no such ref: %.*s", refnamelen, name);
                free(ref);
        }
index 96beeb5d13630672fba3021468670a1bcfc72158..0fca17c02bba89d6c65df95e03e88ef6dd02971e 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,
@@ -433,7 +439,12 @@ static int read_bisect_refs(void)
 
 static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
 static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
+static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
+static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
+static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
+static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
 static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
+static GIT_PATH_FUNC(git_path_head_name, "head-name")
 
 static void read_bisect_paths(struct argv_array *array)
 {
@@ -685,11 +696,12 @@ static int bisect_checkout(const struct object_id *bisect_rev, int no_checkout)
        char bisect_rev_hex[GIT_MAX_HEXSZ + 1];
 
        memcpy(bisect_rev_hex, oid_to_hex(bisect_rev), GIT_SHA1_HEXSZ + 1);
-       update_ref(NULL, "BISECT_EXPECTED_REV", bisect_rev->hash, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
+       update_ref(NULL, "BISECT_EXPECTED_REV", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
 
        argv_checkout[2] = bisect_rev_hex;
        if (no_checkout) {
-               update_ref(NULL, "BISECT_HEAD", bisect_rev->hash, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
+               update_ref(NULL, "BISECT_HEAD", bisect_rev, NULL, 0,
+                          UPDATE_REFS_DIE_ON_ERR);
        } else {
                int res;
                res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
@@ -954,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) {
@@ -1044,3 +1055,40 @@ int estimate_bisect_steps(int all)
 
        return (e < 3 * x) ? n : n - 1;
 }
+
+static int mark_for_removal(const char *refname, const struct object_id *oid,
+                           int flag, void *cb_data)
+{
+       struct string_list *refs = cb_data;
+       char *ref = xstrfmt("refs/bisect%s", refname);
+       string_list_append(refs, ref);
+       return 0;
+}
+
+int bisect_clean_state(void)
+{
+       int result = 0;
+
+       /* There may be some refs packed during bisection */
+       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_NO_DEREF);
+       refs_for_removal.strdup_strings = 1;
+       string_list_clear(&refs_for_removal, 0);
+       unlink_or_warn(git_path_bisect_expected_rev());
+       unlink_or_warn(git_path_bisect_ancestors_ok());
+       unlink_or_warn(git_path_bisect_log());
+       unlink_or_warn(git_path_bisect_names());
+       unlink_or_warn(git_path_bisect_run());
+       unlink_or_warn(git_path_bisect_terms());
+       /* Cleanup head-name if it got left by an old version of git-bisect */
+       unlink_or_warn(git_path_head_name());
+       /*
+        * Cleanup BISECT_START last to support the --no-checkout option
+        * introduced in the commit 4796e823a.
+        */
+       unlink_or_warn(git_path_bisect_start());
+
+       return result;
+}
index acd12ef802c611505139d641fc44776cf6431a5c..a5d9248a47675194e7e0d16aed37018cbb67eb33 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,
@@ -28,4 +34,6 @@ extern int estimate_bisect_steps(int all);
 
 extern void read_bisect_terms(const char **bad, const char **good);
 
+extern int bisect_clean_state(void);
+
 #endif
diff --git a/blame.c b/blame.c
index f575e9cbf4e3224049fe43b7f794d2dd8770eda7..2893f3c1030aab91a42ff9e0daf8a54ba8c3ef3c 100644 (file)
--- a/blame.c
+++ b/blame.c
@@ -166,7 +166,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
        commit->date = now;
        parent_tail = &commit->parents;
 
-       if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL))
+       if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
                die("no such ref: HEAD");
 
        parent_tail = append_parent(parent_tail, &head_oid);
@@ -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 {
@@ -1689,7 +1689,7 @@ static struct commit *dwim_reverse_initial(struct rev_info *revs,
                return NULL;
 
        /* Do we have HEAD? */
-       if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL))
+       if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
                return NULL;
        head_commit = lookup_commit_reference_gently(&head_oid, 1);
        if (!head_commit)
@@ -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 4377ce2fb17d7467ee92d4db02cb4859f6397505..fe1e1c3676b26a4f35feaf8137700b25916ad5ee 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
@@ -264,7 +280,7 @@ void create_branch(const char *name, const char *start_name,
                die(_("Not a valid object name: '%s'."), start_name);
        }
 
-       switch (dwim_ref(start_name, strlen(start_name), oid.hash, &real_ref)) {
+       switch (dwim_ref(start_name, strlen(start_name), &oid, &real_ref)) {
        case 0:
                /* Not branching from any existing branch */
                if (explicit_tracking)
@@ -305,7 +321,7 @@ void create_branch(const char *name, const char *start_name,
                transaction = ref_transaction_begin(&err);
                if (!transaction ||
                    ref_transaction_update(transaction, ref.buf,
-                                          oid.hash, forcing ? NULL : null_sha1,
+                                          &oid, forcing ? NULL : &null_oid,
                                           0, msg, &err) ||
                    ref_transaction_commit(transaction, &err))
                        die("%s", err.buf);
index b07788558c0e76a701b571f73c0462dcf2abcc63..be5e5d13083a98e57e834d81afe5450f11202130 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 a648cf4c56c9f56eb60a97ca50ccf0b191dc50ff..bf01d89e28eaef0f8113d42c6b9d4142b8e912e4 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 d7513f53759bc5834aa02bd904432f64e036ccb6..3d98e52085711abc6fa5ff9fd16d2828156df375 100644 (file)
@@ -1068,8 +1068,8 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
        if (!get_oid("HEAD", &curr_head)) {
                write_state_text(state, "abort-safety", oid_to_hex(&curr_head));
                if (!state->rebasing)
-                       update_ref_oid("am", "ORIG_HEAD", &curr_head, NULL, 0,
-                                       UPDATE_REFS_DIE_ON_ERR);
+                       update_ref("am", "ORIG_HEAD", &curr_head, NULL, 0,
+                                  UPDATE_REFS_DIE_ON_ERR);
        } else {
                write_state_text(state, "abort-safety", "");
                if (!state->rebasing)
@@ -1134,11 +1134,11 @@ static const char *msgnum(const struct am_state *state)
  */
 static void refresh_and_write_cache(void)
 {
-       struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+       struct lock_file lock_file = LOCK_INIT;
 
-       hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
+       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
        refresh_cache(REFRESH_QUIET);
-       if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+       if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write index 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;
@@ -1433,7 +1433,7 @@ static void write_index_patch(const struct am_state *state)
        if (!get_oid_tree("HEAD", &head))
                tree = lookup_tree(&head);
        else
-               tree = lookup_tree(&empty_tree_oid);
+               tree = lookup_tree(the_hash_algo->empty_tree);
 
        fp = xfopen(am_path(state, "patch"), "w");
        init_revisions(&rev_info, NULL);
@@ -1488,11 +1488,10 @@ static int run_apply(const struct am_state *state, const char *index_file)
        struct argv_array apply_opts = ARGV_ARRAY_INIT;
        struct apply_state apply_state;
        int res, opts_left;
-       static struct lock_file lock_file;
        int force_apply = 0;
        int options = 0;
 
-       if (init_apply_state(&apply_state, NULL, &lock_file))
+       if (init_apply_state(&apply_state, NULL))
                die("BUG: init_apply_state() failed");
 
        argv_array_push(&apply_opts, "apply");
@@ -1686,8 +1685,8 @@ static void do_commit(const struct am_state *state)
        strbuf_addf(&sb, "%s: %.*s", reflog_msg, linelen(state->msg),
                        state->msg);
 
-       update_ref_oid(sb.buf, "HEAD", &commit, old_oid, 0,
-                       UPDATE_REFS_DIE_ON_ERR);
+       update_ref(sb.buf, "HEAD", &commit, old_oid, 0,
+                  UPDATE_REFS_DIE_ON_ERR);
 
        if (state->rebasing) {
                FILE *fp = xfopen(am_path(state, "rewritten"), "a");
@@ -1946,15 +1945,14 @@ static void am_resolve(struct am_state *state)
  */
 static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
 {
-       struct lock_file *lock_file;
+       struct lock_file lock_file = LOCK_INIT;
        struct unpack_trees_options opts;
        struct tree_desc t[2];
 
        if (parse_tree(head) || parse_tree(remote))
                return -1;
 
-       lock_file = xcalloc(1, sizeof(struct lock_file));
-       hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
+       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
 
        refresh_cache(REFRESH_QUIET);
 
@@ -1970,11 +1968,11 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
        init_tree_desc(&t[1], remote->buffer, remote->size);
 
        if (unpack_trees(2, t, &opts)) {
-               rollback_lock_file(lock_file);
+               rollback_lock_file(&lock_file);
                return -1;
        }
 
-       if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+       if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
 
        return 0;
@@ -1986,15 +1984,14 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
  */
 static int merge_tree(struct tree *tree)
 {
-       struct lock_file *lock_file;
+       struct lock_file lock_file = LOCK_INIT;
        struct unpack_trees_options opts;
        struct tree_desc t[1];
 
        if (parse_tree(tree))
                return -1;
 
-       lock_file = xcalloc(1, sizeof(struct lock_file));
-       hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
+       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
 
        memset(&opts, 0, sizeof(opts));
        opts.head_idx = 1;
@@ -2005,11 +2002,11 @@ static int merge_tree(struct tree *tree)
        init_tree_desc(&t[0], tree->buffer, tree->size);
 
        if (unpack_trees(1, t, &opts)) {
-               rollback_lock_file(lock_file);
+               rollback_lock_file(&lock_file);
                return -1;
        }
 
-       if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+       if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
 
        return 0;
@@ -2135,7 +2132,7 @@ static void am_abort(struct am_state *state)
 
        am_rerere_clear();
 
-       curr_branch = resolve_refdup("HEAD", 0, curr_head.hash, NULL);
+       curr_branch = resolve_refdup("HEAD", 0, &curr_head, NULL);
        has_curr_head = curr_branch && !is_null_oid(&curr_head);
        if (!has_curr_head)
                hashcpy(curr_head.hash, EMPTY_TREE_SHA1_BIN);
@@ -2147,11 +2144,11 @@ static void am_abort(struct am_state *state)
        clean_index(&curr_head, &orig_head);
 
        if (has_orig_head)
-               update_ref_oid("am --abort", "HEAD", &orig_head,
-                               has_curr_head ? &curr_head : NULL, 0,
-                               UPDATE_REFS_DIE_ON_ERR);
+               update_ref("am --abort", "HEAD", &orig_head,
+                          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 81b9a61c3713a718a912d49916cf2632478cb47c..48d3989331d9b7deeb034642a027729bd1267e38 100644 (file)
@@ -9,8 +9,6 @@ static const char * const apply_usage[] = {
        NULL
 };
 
-static struct lock_file lock_file;
-
 int cmd_apply(int argc, const char **argv, const char *prefix)
 {
        int force_apply = 0;
@@ -18,7 +16,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
        int ret;
        struct apply_state state;
 
-       if (init_apply_state(&state, prefix, &lock_file))
+       if (init_apply_state(&state, prefix))
                exit(128);
 
        argc = apply_parse_options(argc, argv,
index 3324229025300f7d7de600753c03af3ac22e2152..4b5fadcbe1ae6a1b10fe88e80a5fcffd3296cfa7 100644 (file)
 #include "cache.h"
 #include "parse-options.h"
 #include "bisect.h"
+#include "refs.h"
+
+static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
+static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
+static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
 
 static const char * const git_bisect_helper_usage[] = {
        N_("git bisect--helper --next-all [--no-checkout]"),
+       N_("git bisect--helper --write-terms <bad_term> <good_term>"),
+       N_("git bisect--helper --bisect-clean-state"),
        NULL
 };
 
+/*
+ * Check whether the string `term` belongs to the set of strings
+ * included in the variable arguments.
+ */
+LAST_ARG_MUST_BE_NULL
+static int one_of(const char *term, ...)
+{
+       int res = 0;
+       va_list matches;
+       const char *match;
+
+       va_start(matches, term);
+       while (!res && (match = va_arg(matches, const char *)))
+               res = !strcmp(term, match);
+       va_end(matches);
+
+       return res;
+}
+
+static int check_term_format(const char *term, const char *orig_term)
+{
+       int res;
+       char *new_term = xstrfmt("refs/bisect/%s", term);
+
+       res = check_refname_format(new_term, 0);
+       free(new_term);
+
+       if (res)
+               return error(_("'%s' is not a valid term"), term);
+
+       if (one_of(term, "help", "start", "skip", "next", "reset",
+                       "visualize", "view", "replay", "log", "run", "terms", NULL))
+               return error(_("can't use the builtin command '%s' as a term"), term);
+
+       /*
+        * In theory, nothing prevents swapping completely good and bad,
+        * but this situation could be confusing and hasn't been tested
+        * enough. Forbid it for now.
+        */
+
+       if ((strcmp(orig_term, "bad") && one_of(term, "bad", "new", NULL)) ||
+                (strcmp(orig_term, "good") && one_of(term, "good", "old", NULL)))
+               return error(_("can't change the meaning of the term '%s'"), term);
+
+       return 0;
+}
+
+static int write_terms(const char *bad, const char *good)
+{
+       FILE *fp = NULL;
+       int res;
+
+       if (!strcmp(bad, good))
+               return error(_("please use two different terms"));
+
+       if (check_term_format(bad, "bad") || check_term_format(good, "good"))
+               return -1;
+
+       fp = fopen(git_path_bisect_terms(), "w");
+       if (!fp)
+               return error_errno(_("could not open the file BISECT_TERMS"));
+
+       res = fprintf(fp, "%s\n%s\n", bad, good);
+       res |= fclose(fp);
+       return (res < 0) ? -1 : 0;
+}
+
+static int is_expected_rev(const char *expected_hex)
+{
+       struct strbuf actual_hex = STRBUF_INIT;
+       int res = 0;
+       if (strbuf_read_file(&actual_hex, git_path_bisect_expected_rev(), 0) >= 40) {
+               strbuf_trim(&actual_hex);
+               res = !strcmp(actual_hex.buf, expected_hex);
+       }
+       strbuf_release(&actual_hex);
+       return res;
+}
+
+static void check_expected_revs(const char **revs, int rev_nr)
+{
+       int i;
+
+       for (i = 0; i < rev_nr; i++) {
+               if (!is_expected_rev(revs[i])) {
+                       unlink_or_warn(git_path_bisect_ancestors_ok());
+                       unlink_or_warn(git_path_bisect_expected_rev());
+               }
+       }
+}
+
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
-       int next_all = 0;
+       enum {
+               NEXT_ALL = 1,
+               WRITE_TERMS,
+               BISECT_CLEAN_STATE,
+               CHECK_EXPECTED_REVS
+       } cmdmode = 0;
        int no_checkout = 0;
        struct option options[] = {
-               OPT_BOOL(0, "next-all", &next_all,
-                        N_("perform 'git bisect next'")),
+               OPT_CMDMODE(0, "next-all", &cmdmode,
+                        N_("perform 'git bisect next'"), NEXT_ALL),
+               OPT_CMDMODE(0, "write-terms", &cmdmode,
+                        N_("write the terms to .git/BISECT_TERMS"), WRITE_TERMS),
+               OPT_CMDMODE(0, "bisect-clean-state", &cmdmode,
+                        N_("cleanup the bisection state"), BISECT_CLEAN_STATE),
+               OPT_CMDMODE(0, "check-expected-revs", &cmdmode,
+                        N_("check for expected revs"), CHECK_EXPECTED_REVS),
                OPT_BOOL(0, "no-checkout", &no_checkout,
                         N_("update BISECT_HEAD instead of checking out the current commit")),
                OPT_END()
@@ -23,9 +132,25 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, options,
                             git_bisect_helper_usage, 0);
 
-       if (!next_all)
+       if (!cmdmode)
                usage_with_options(git_bisect_helper_usage, options);
 
-       /* next-all */
-       return bisect_next_all(prefix, no_checkout);
+       switch (cmdmode) {
+       case NEXT_ALL:
+               return bisect_next_all(prefix, no_checkout);
+       case WRITE_TERMS:
+               if (argc != 2)
+                       return error(_("--write-terms requires two arguments"));
+               return write_terms(argv[0], argv[1]);
+       case BISECT_CLEAN_STATE:
+               if (argc != 0)
+                       return error(_("--bisect-clean-state requires no arguments"));
+               return bisect_clean_state();
+       case CHECK_EXPECTED_REVS:
+               check_expected_revs(argv, argc);
+               return 0;
+       default:
+               return error("BUG: unknown subcommand '%d'", cmdmode);
+       }
+       return 0;
 }
index 67adaef4d80ee4e2fcd28755255935f0fcae6c03..005f55aaa257fc34f81517650223a3c195c03178 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 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        }
        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 79dc9181fd6c0008d4b8c3b841f35cc1223d6d74..af95ad2192004ad3a712c15f1e50356daadb7c86 100644 (file)
@@ -125,7 +125,7 @@ static int branch_merged(int kind, const char *name,
                if (upstream &&
                    (reference_name = reference_name_to_free =
                     resolve_refdup(upstream, RESOLVE_REF_READING,
-                                   oid.hash, NULL)) != NULL)
+                                   &oid, NULL)) != NULL)
                        reference_rev = lookup_commit_reference(&oid);
        }
        if (!reference_rev)
@@ -241,7 +241,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                                        RESOLVE_REF_READING
                                        | RESOLVE_REF_NO_RECURSE
                                        | RESOLVE_REF_ALLOW_BAD_NAME,
-                                       oid.hash, &flags);
+                                       &oid, &flags);
                if (!target) {
                        error(remote_branch
                              ? _("remote-tracking branch '%s' not found.")
@@ -257,8 +257,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                        goto next;
                }
 
-               if (delete_ref(NULL, name, is_null_oid(&oid) ? NULL : oid.hash,
-                              REF_NODEREF)) {
+               if (delete_ref(NULL, name, is_null_oid(&oid) ? NULL : &oid,
+                              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);
 
@@ -636,7 +636,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
        track = git_branch_track;
 
-       head = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
+       head = resolve_refdup("HEAD", 0, &head_oid, NULL);
        if (!head)
                die(_("Failed to resolve HEAD as a valid ref."));
        if (!strcmp(head, "HEAD"))
@@ -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 6c40ff110bee7387d4eb4e9c7c04a21e8b12ca44..bc67d3f0a83d35b2fc6d81f28c6908075564644a 100644 (file)
@@ -39,12 +39,14 @@ static char *collapse_slashes(const char *refname)
 static int check_ref_format_branch(const char *arg)
 {
        struct strbuf sb = STRBUF_INIT;
+       const char *name;
        int nongit;
 
        setup_git_directory_gently(&nongit);
-       if (strbuf_check_branch_ref(&sb, arg))
+       if (strbuf_check_branch_ref(&sb, arg) ||
+           !skip_prefix(sb.buf, "refs/heads/", &name))
                die("'%s' is not a valid branch name", arg);
-       printf("%s\n", sb.buf + 11);
+       printf("%s\n", name);
        strbuf_release(&sb);
        return 0;
 }
index 39c8be05dc4beda2f0cd179e5c5f495ce079f8d9..b0e78b819db3138f1c5ac4fe4b5d850ab306b470 100644 (file)
@@ -129,8 +129,6 @@ static const char * const builtin_checkout_index_usage[] = {
        NULL
 };
 
-static struct lock_file lock_file;
-
 static int option_parse_stage(const struct option *opt,
                              const char *arg, int unset)
 {
@@ -150,7 +148,7 @@ static int option_parse_stage(const struct option *opt,
 int cmd_checkout_index(int argc, const char **argv, const char *prefix)
 {
        int i;
-       int newfd = -1;
+       struct lock_file lock_file = LOCK_INIT;
        int all = 0;
        int read_from_stdin = 0;
        int prefix_length;
@@ -206,7 +204,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
        if (index_opt && !state.base_dir_len && !to_tempfile) {
                state.refresh_cache = 1;
                state.istate = &the_index;
-               newfd = hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+               hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
        }
 
        /* Check out named files first */
@@ -251,7 +249,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
        if (all)
                checkout_all(prefix, prefix_length);
 
-       if (0 <= newfd &&
+       if (is_lock_file_locked(&lock_file) &&
            write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die("Unable to write new index file");
        return 0;
index fc4f8fd2ea29c7c23d3b1c7ca0ba78824c255a91..e1e157d205a06ffad39a9685cab55b16c69d339c 100644 (file)
@@ -247,7 +247,7 @@ static int checkout_paths(const struct checkout_opts *opts,
        struct object_id rev;
        struct commit *head;
        int errs = 0;
-       struct lock_file *lock_file;
+       struct lock_file lock_file = LOCK_INIT;
 
        if (opts->track != BRANCH_TRACK_UNSPECIFIED)
                die(_("'%s' cannot be used with updating paths"), "--track");
@@ -275,9 +275,7 @@ static int checkout_paths(const struct checkout_opts *opts,
                return run_add_interactive(revision, "--patch=checkout",
                                           &opts->pathspec);
 
-       lock_file = xcalloc(1, sizeof(struct lock_file));
-
-       hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
+       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
        if (read_cache_preload(&opts->pathspec) < 0)
                return error(_("index file corrupt"));
 
@@ -376,10 +374,10 @@ static int checkout_paths(const struct checkout_opts *opts,
        }
        errs |= finish_delayed_checkout(&state);
 
-       if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+       if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
 
-       read_ref_full("HEAD", 0, rev.hash, NULL);
+       read_ref_full("HEAD", 0, &rev, NULL);
        head = lookup_commit_reference_gently(&rev, 1);
 
        errs |= post_checkout_hook(head, head, 0);
@@ -472,9 +470,9 @@ static int merge_working_tree(const struct checkout_opts *opts,
                              int *writeout_error)
 {
        int ret;
-       struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+       struct lock_file lock_file = LOCK_INIT;
 
-       hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
+       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
        if (read_cache_preload(NULL) < 0)
                return error(_("index file corrupt"));
 
@@ -516,7 +514,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
                }
                tree = parse_tree_indirect(old->commit ?
                                           &old->commit->object.oid :
-                                          &empty_tree_oid);
+                                          the_hash_algo->empty_tree);
                init_tree_desc(&trees[0], tree->buffer, tree->size);
                tree = parse_tree_indirect(&new->commit->object.oid);
                init_tree_desc(&trees[1], tree->buffer, tree->size);
@@ -591,7 +589,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
        if (!cache_tree_fully_valid(active_cache_tree))
                cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
 
-       if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+       if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
 
        if (!opts->force && !opts->quiet)
@@ -664,8 +662,8 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
        if (!strcmp(new->name, "HEAD") && !new->path && !opts->force_detach) {
                /* Nothing to do. */
        } else if (opts->force_detach || !new->path) {  /* No longer on any branch. */
-               update_ref(msg.buf, "HEAD", new->commit->object.oid.hash, NULL,
-                          REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
+               update_ref(msg.buf, "HEAD", &new->commit->object.oid, NULL,
+                          REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
                if (!opts->quiet) {
                        if (old->path &&
                            advice_detached_head && !opts->force_detach)
@@ -827,7 +825,7 @@ static int switch_branches(const struct checkout_opts *opts,
        struct object_id rev;
        int flag, writeout_error = 0;
        memset(&old, 0, sizeof(old));
-       old.path = path_to_free = resolve_refdup("HEAD", 0, rev.hash, &flag);
+       old.path = path_to_free = resolve_refdup("HEAD", 0, &rev, &flag);
        if (old.path)
                old.commit = lookup_commit_reference_gently(&rev, 1);
        if (!(flag & REF_ISSYMREF))
@@ -1038,7 +1036,7 @@ static int parse_branchname_arg(int argc, const char **argv,
        setup_branch_path(new);
 
        if (!check_refname_format(new->path, 0) &&
-           !read_ref(new->path, branch_rev.hash))
+           !read_ref(new->path, &branch_rev))
                oidcpy(rev, &branch_rev);
        else
                new->path = NULL; /* not an existing branch */
@@ -1136,7 +1134,7 @@ static int checkout_branch(struct checkout_opts *opts,
                struct object_id rev;
                int flag;
 
-               if (!read_ref_full("HEAD", 0, rev.hash, &flag) &&
+               if (!read_ref_full("HEAD", 0, &rev, &flag) &&
                    (flag & REF_ISSYMREF) && is_null_oid(&rev))
                        return switch_unborn_to_new_branch(opts);
        }
@@ -1289,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 dbddd98f80d6660f2cde190a0e121022f079071d..b22845738afe68e61d45883e89a470213de921ef 100644 (file)
@@ -588,7 +588,7 @@ static void write_remote_refs(const struct ref *local_refs)
        for (r = local_refs; r; r = r->next) {
                if (!r->peer_ref)
                        continue;
-               if (ref_transaction_create(t, r->peer_ref->name, r->old_oid.hash,
+               if (ref_transaction_create(t, r->peer_ref->name, &r->old_oid,
                                           0, NULL, &err))
                        die("%s", err.buf);
        }
@@ -610,12 +610,12 @@ static void write_followtags(const struct ref *refs, const char *msg)
                        continue;
                if (!has_object_file(&ref->old_oid))
                        continue;
-               update_ref(msg, ref->name, ref->old_oid.hash,
-                          NULL, 0, UPDATE_REFS_DIE_ON_ERR);
+               update_ref(msg, ref->name, &ref->old_oid, NULL, 0,
+                          UPDATE_REFS_DIE_ON_ERR);
        }
 }
 
-static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
+static int iterate_ref_map(void *cb_data, struct object_id *oid)
 {
        struct ref **rm = cb_data;
        struct ref *ref = *rm;
@@ -630,7 +630,7 @@ static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
        if (!ref)
                return -1;
 
-       hashcpy(sha1, ref->old_oid.hash);
+       oidcpy(oid, &ref->old_oid);
        *rm = ref->next;
        return 0;
 }
@@ -682,23 +682,23 @@ static void update_head(const struct ref *our, const struct ref *remote,
                if (create_symref("HEAD", our->name, NULL) < 0)
                        die(_("unable to update HEAD"));
                if (!option_bare) {
-                       update_ref(msg, "HEAD", our->old_oid.hash, NULL, 0,
+                       update_ref(msg, "HEAD", &our->old_oid, NULL, 0,
                                   UPDATE_REFS_DIE_ON_ERR);
                        install_branch_config(0, head, option_origin, our->name);
                }
        } 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.hash,
-                          NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
+               update_ref(msg, "HEAD", &c->object.oid, NULL, REF_NO_DEREF,
+                          UPDATE_REFS_DIE_ON_ERR);
        } else if (remote) {
                /*
                 * We know remote HEAD points to a non-branch, or
                 * 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.hash,
-                          NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
+               update_ref(msg, "HEAD", &remote->old_oid, NULL, REF_NO_DEREF,
+                          UPDATE_REFS_DIE_ON_ERR);
        }
 }
 
@@ -706,7 +706,7 @@ static int checkout(int submodule_progress)
 {
        struct object_id oid;
        char *head;
-       struct lock_file *lock_file;
+       struct lock_file lock_file = LOCK_INIT;
        struct unpack_trees_options opts;
        struct tree *tree;
        struct tree_desc t;
@@ -715,7 +715,7 @@ static int checkout(int submodule_progress)
        if (option_no_checkout)
                return 0;
 
-       head = resolve_refdup("HEAD", RESOLVE_REF_READING, oid.hash, NULL);
+       head = resolve_refdup("HEAD", RESOLVE_REF_READING, &oid, NULL);
        if (!head) {
                warning(_("remote HEAD refers to nonexistent ref, "
                          "unable to checkout.\n"));
@@ -733,8 +733,7 @@ static int checkout(int submodule_progress)
        /* We need to be in the new work tree for the checkout */
        setup_work_tree();
 
-       lock_file = xcalloc(1, sizeof(struct lock_file));
-       hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
+       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
 
        memset(&opts, 0, sizeof opts);
        opts.update = 1;
@@ -750,7 +749,7 @@ static int checkout(int submodule_progress)
        if (unpack_trees(1, &t, &opts) < 0)
                die(_("unable to checkout working tree"));
 
-       if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+       if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
 
        err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
index d75b3805ea7fe3475564f337bf660d8155909445..8a877014145435516930c787dec37b8c4ac3da90 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;
@@ -355,7 +355,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
 
                refresh_cache_or_die(refresh_flags);
 
-               if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
+               if (write_locked_index(&the_index, &index_lock, 0))
                        die(_("unable to create temporary index"));
 
                old_index_env = getenv(INDEX_ENVIRONMENT);
@@ -374,7 +374,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
                if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) {
                        if (reopen_lock_file(&index_lock) < 0)
                                die(_("unable to write index file"));
-                       if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
+                       if (write_locked_index(&the_index, &index_lock, 0))
                                die(_("unable to update temporary index"));
                } else
                        warning(_("Failed to update main cache tree"));
@@ -401,7 +401,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
                add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
                refresh_cache_or_die(refresh_flags);
                update_main_cache_tree(WRITE_TREE_SILENT);
-               if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
+               if (write_locked_index(&the_index, &index_lock, 0))
                        die(_("unable to write new_index file"));
                commit_style = COMMIT_NORMAL;
                ret = get_lock_file_path(&index_lock);
@@ -474,7 +474,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
        add_remove_files(&partial);
        refresh_cache(REFRESH_QUIET);
        update_main_cache_tree(WRITE_TREE_SILENT);
-       if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
+       if (write_locked_index(&the_index, &index_lock, 0))
                die(_("unable to write new_index file"));
 
        hold_lock_file_for_update(&false_lock,
@@ -486,7 +486,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
        add_remove_files(&partial);
        refresh_cache(REFRESH_QUIET);
 
-       if (write_locked_index(&the_index, &false_lock, CLOSE_LOCK))
+       if (write_locked_index(&the_index, &false_lock, 0))
                die(_("unable to write temporary index file"));
 
        discard_cache();
@@ -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);
@@ -1492,6 +1512,8 @@ static void print_summary(const char *prefix, const struct object_id *oid,
        diff_setup_done(&rev.diffopt);
 
        head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
+       if (!head)
+               die_errno(_("unable to resolve HEAD after creating commit"));
        if (!strcmp(head, "HEAD"))
                head = _("detached HEAD");
        else
@@ -1728,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)
@@ -1788,9 +1810,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
-           ref_transaction_update(transaction, "HEAD", oid.hash,
+           ref_transaction_update(transaction, "HEAD", &oid,
                                   current_head
-                                  ? current_head->object.oid.hash : null_sha1,
+                                  ? &current_head->object.oid : &null_oid,
                                   0, sb.buf, &err) ||
            ref_transaction_commit(transaction, &err)) {
                rollback_index_files();
index d13daeeb55927758ceec816f39412123d2ce5846..ab5f95476e6c726798fd84b4a6300577bf0fafba 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 29075dbd0f884513420b646cdb06d7c0b3d33643..e14e162ef6ef829072f01f89ab2c797256a6861d 100644 (file)
@@ -7,12 +7,12 @@
 #include "builtin.h"
 #include "exec_cmd.h"
 #include "parse-options.h"
+#include "revision.h"
 #include "diff.h"
 #include "hashmap.h"
 #include "argv-array.h"
 #include "run-command.h"
 
-#define SEEN           (1u << 0)
 #define MAX_TAGS       (FLAG_BITS - 1)
 
 static const char * const describe_usage[] = {
@@ -181,7 +181,7 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi
        }
 
        /* Is it annotated? */
-       if (!peel_ref(path, peeled.hash)) {
+       if (!peel_ref(path, &peeled)) {
                is_annotated = !!oidcmp(oid, &peeled);
        } else {
                oidcpy(&peeled, oid);
@@ -543,7 +543,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                        }
                } else if (dirty) {
                        static struct lock_file index_lock;
-                       int fd;
+                       struct rev_info revs;
+                       struct argv_array args = ARGV_ARRAY_INIT;
+                       int fd, result;
 
                        read_cache_preload(NULL);
                        refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED,
@@ -552,8 +554,13 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                        if (0 <= fd)
                                update_index_if_able(&the_index, &index_lock);
 
-                       if (!cmd_diff_index(ARRAY_SIZE(diff_index_args) - 1,
-                                           diff_index_args, prefix))
+                       init_revisions(&revs, prefix);
+                       argv_array_pushv(&args, diff_index_args);
+                       if (setup_revisions(args.argc, args.argv, &revs, NULL) != 1)
+                               BUG("malformed internal diff-index command line");
+                       result = run_diff_index(&revs, 0);
+
+                       if (!diff_result_code(&revs.diffopt, result))
                                suffix = NULL;
                        else
                                suffix = dirty;
index f5bbd4d757e12e6e34a86fac006298acb771909f..16bfb22f7381ee8e6967ab836686c5def7cff892 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);
@@ -203,17 +203,16 @@ static int builtin_diff_combined(struct rev_info *revs,
 
 static void refresh_index_quietly(void)
 {
-       struct lock_file *lock_file;
+       struct lock_file lock_file = LOCK_INIT;
        int fd;
 
-       lock_file = xcalloc(1, sizeof(struct lock_file));
-       fd = hold_locked_index(lock_file, 0);
+       fd = hold_locked_index(&lock_file, 0);
        if (fd < 0)
                return;
        discard_cache();
        read_cache();
        refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED);
-       update_index_if_able(&the_index, lock_file);
+       update_index_if_able(&the_index, &lock_file);
 }
 
 static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv)
@@ -350,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"));
@@ -361,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);
 
@@ -380,7 +379,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
                                add_head_to_pending(&rev);
                                if (!rev.pending.nr) {
                                        struct tree *tree;
-                                       tree = lookup_tree(&empty_tree_oid);
+                                       tree = lookup_tree(the_hash_algo->empty_tree);
                                        add_pending_object(&rev, &tree->object, "HEAD");
                                }
                                break;
index b2d3ba7539d5b9cdc7ec6f910ee24510e709f76c..bcc79d1888f2217bcb380ffb1e7178c100a41e8e 100644 (file)
@@ -616,7 +616,6 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
                        if (hold_lock_file_for_update(&lock, buf.buf, 0) < 0 ||
                            write_locked_index(&wtindex, &lock, COMMIT_LOCK)) {
                                ret = error("could not write %s", buf.buf);
-                               rollback_lock_file(&lock);
                                goto finish;
                        }
                        changed_files(&wt_modified, buf.buf, workdir);
index 2fb60d6d48eae68affdcf64f6dbab0282408f393..f8fe04ca5332541213dc059dd02fdf09e0c6cf85 100644 (file)
@@ -823,7 +823,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
                if (e->flags & UNINTERESTING)
                        continue;
 
-               if (dwim_ref(e->name, strlen(e->name), oid.hash, &full_name) != 1)
+               if (dwim_ref(e->name, strlen(e->name), &oid, &full_name) != 1)
                        continue;
 
                if (refspecs) {
@@ -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 225c734924f14854784cbc1e310f2fb817f1f38b..e705237fa900dd7e4e73a58e7b6dad425bc7e431 100644 (file)
@@ -457,8 +457,8 @@ static int s_update_ref(const char *action,
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
            ref_transaction_update(transaction, ref->name,
-                                  ref->new_oid.hash,
-                                  check_old ? ref->old_oid.hash : NULL,
+                                  &ref->new_oid,
+                                  check_old ? &ref->old_oid : NULL,
                                   0, msg, &err))
                goto fail;
 
@@ -727,7 +727,7 @@ static int update_local_ref(struct ref *ref,
        }
 }
 
-static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
+static int iterate_ref_map(void *cb_data, struct object_id *oid)
 {
        struct ref **rm = cb_data;
        struct ref *ref = *rm;
@@ -737,7 +737,7 @@ static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
        if (!ref)
                return -1; /* end of the list */
        *rm = ref->next;
-       hashcpy(sha1, ref->old_oid.hash);
+       oidcpy(oid, &ref->old_oid);
        return 0;
 }
 
index e99b5ddbf9a51be17bc0d976c942c29afe6563cd..22034f87e7f8c7fa1166d3731319aca610fef2a2 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);
@@ -603,7 +603,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 
        /* get current branch */
        current_branch = current_branch_to_free =
-               resolve_refdup("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL);
+               resolve_refdup("HEAD", RESOLVE_REF_READING, &head_oid, NULL);
        if (!current_branch)
                die("No current branch");
        if (starts_with(current_branch, "refs/heads/"))
index 56afe405b8072b3099a23661ac586f673e54ff0e..04846d46f91d6cde14d379da8e0e3dd458c08dc5 100644 (file)
@@ -555,7 +555,7 @@ static int fsck_head_link(void)
        if (verbose)
                fprintf(stderr, "Checking HEAD link\n");
 
-       head_points_at = resolve_ref_unsafe("HEAD", 0, head_oid.hash, NULL);
+       head_points_at = resolve_ref_unsafe("HEAD", 0, &head_oid, NULL);
        if (!head_points_at) {
                errors_found |= ERROR_REFS;
                return error("Invalid HEAD");
@@ -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 2d65f27d01f3da772f7bad21f101fdc0af0102ed..5a6cfe6b45b06b6b75ea399a83cb8dcf7e197141 100644 (file)
@@ -431,7 +431,9 @@ static int grep_submodule(struct grep_opt *opt, struct repository *superproject,
         * store is no longer global and instead is a member of the repository
         * object.
         */
+       grep_read_lock();
        add_to_alternates_memory(submodule.objectdir);
+       grep_read_unlock();
 
        if (oid) {
                struct object *object;
index d81a09051ead0bb14161c25ef30bf8b0348b3198..14fdf39165d20602ad8b682b55660ec2466c1d20 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,
@@ -143,11 +142,19 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
        struct userformat_want w;
        int quiet = 0, source = 0, mailmap = 0;
        static struct line_opt_callback_data line_cb = {NULL, NULL, STRING_LIST_INIT_DUP};
+       static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
+       static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP;
+       struct decoration_filter decoration_filter = {&decorate_refs_include,
+                                                     &decorate_refs_exclude};
 
        const struct option builtin_log_options[] = {
                OPT__QUIET(&quiet, N_("suppress diff output")),
                OPT_BOOL(0, "source", &source, N_("show source")),
                OPT_BOOL(0, "use-mailmap", &mailmap, N_("Use mail map file")),
+               OPT_STRING_LIST(0, "decorate-refs", &decorate_refs_include,
+                               N_("pattern"), N_("only decorate refs that match <pattern>")),
+               OPT_STRING_LIST(0, "decorate-refs-exclude", &decorate_refs_exclude,
+                               N_("pattern"), N_("do not decorate refs that match <pattern>")),
                { OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"),
                  PARSE_OPT_OPTARG, decorate_callback},
                OPT_CALLBACK('L', NULL, &line_cb, "n,m:file",
@@ -182,7 +189,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)
@@ -206,7 +213,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
 
        if (decoration_style) {
                rev->show_decorations = 1;
-               load_ref_decorations(decoration_style);
+               load_ref_decorations(&decoration_filter, decoration_style);
        }
 
        if (rev->line_level_traverse)
@@ -392,7 +399,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 +491,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 +674,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)
@@ -975,7 +982,7 @@ static char *find_branch_name(struct rev_info *rev)
                return NULL;
        ref = rev->cmdline.rev[positive].name;
        tip_oid = &rev->cmdline.rev[positive].item->oid;
-       if (dwim_ref(ref, strlen(ref), branch_oid.hash, &full_ref) &&
+       if (dwim_ref(ref, strlen(ref), &branch_oid, &full_ref) &&
            skip_prefix(full_ref, "refs/heads/", &v) &&
            !oidcmp(tip_oid, &branch_oid))
                branch = xstrdup(v);
@@ -1341,7 +1348,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 +1519,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 +1620,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 8c713c47acccf0a3a50ec8a0b822aafae8d6bada..2fc836e33086d5b70b86a3fc746f9e75706ef7ea 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 6dbd167d3b0874cd966b4590951a12d01f8f6aeb..3b7600150b66c4bf814e21b74b2d931f44c83805 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;
 }
 
@@ -156,7 +158,7 @@ static int handle_fork_point(int argc, const char **argv)
        struct commit_list *bases;
        int i, ret = 0;
 
-       switch (dwim_ref(argv[0], strlen(argv[0]), oid.hash, &refname)) {
+       switch (dwim_ref(argv[0], strlen(argv[0]), &oid, &refname)) {
        case 0:
                die("No such ref: '%s'", argv[0]);
        case 1:
index 684411694f64ca216fb03430df2d2a0ec9ea93ff..c84c6e05e922f09692b9b133cd391b850c7951df 100644 (file)
@@ -9,26 +9,24 @@
  */
 #include "git-compat-util.h"
 #include "builtin.h"
+#include "diff.h"
 
 static const char builtin_merge_ours_usage[] =
        "git merge-ours <base>... -- HEAD <remote>...";
 
-static const char *diff_index_args[] = {
-       "diff-index", "--quiet", "--cached", "HEAD", "--", NULL
-};
-#define NARGS (ARRAY_SIZE(diff_index_args) - 1)
-
 int cmd_merge_ours(int argc, const char **argv, const char *prefix)
 {
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage(builtin_merge_ours_usage);
 
        /*
-        * We need to exit with 2 if the index does not match our HEAD tree,
-        * because the current index is what we will be committing as the
-        * merge result.
+        * The contents of the current index becomes the tree we
+        * commit.  The index must match HEAD, or this merge cannot go
+        * through.
         */
-       if (cmd_diff_index(NARGS, diff_index_args, prefix))
+       if (read_cache() < 0)
+               die_errno("read_cache failed");
+       if (index_differs_from("HEAD", NULL, 0))
                exit(2);
        exit(0);
 }
index ab5ffe85e8f5d7a359004f2bbb52a5778d0b91b4..612dd7bfb6c7e6ae6f636e0b2e55bfed1aff92f2 100644 (file)
@@ -405,9 +405,8 @@ static void finish(struct commit *head_commit,
                        printf(_("No merge message -- not updating HEAD\n"));
                else {
                        const char *argv_gc_auto[] = { "gc", "--auto", NULL };
-                       update_ref(reflog_message.buf, "HEAD",
-                               new_head->hash, head->hash, 0,
-                               UPDATE_REFS_DIE_ON_ERR);
+                       update_ref(reflog_message.buf, "HEAD", new_head, head,
+                                  0, UPDATE_REFS_DIE_ON_ERR);
                        /*
                         * We ignore errors in 'gc --auto', since the
                         * user should see them.
@@ -455,7 +454,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
        if (!remote_head)
                die(_("'%s' does not point to a commit"), remote);
 
-       if (dwim_ref(remote, strlen(remote), branch_head.hash, &found_ref) > 0) {
+       if (dwim_ref(remote, strlen(remote), &branch_head, &found_ref) > 0) {
                if (starts_with(found_ref, "refs/heads/")) {
                        strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
                                    oid_to_hex(&branch_head), remote);
@@ -999,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;
@@ -1143,7 +1143,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
         * Check if we are _not_ on a detached HEAD, i.e. if there is a
         * current branch.
         */
-       branch = branch_to_free = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
+       branch = branch_to_free = resolve_refdup("HEAD", 0, &head_oid, NULL);
        if (branch)
                skip_prefix(branch, "refs/heads/", &branch);
        if (!branch || is_null_oid(&head_oid))
@@ -1261,8 +1261,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        die(_("Can merge only exactly one commit into empty head"));
                remote_head_oid = &remoteheads->item->object.oid;
                read_empty(remote_head_oid->hash, 0);
-               update_ref("initial pull", "HEAD", remote_head_oid->hash,
-                          NULL, 0, UPDATE_REFS_DIE_ON_ERR);
+               update_ref("initial pull", "HEAD", remote_head_oid, NULL, 0,
+                          UPDATE_REFS_DIE_ON_ERR);
                goto done;
        }
 
@@ -1357,8 +1357,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                free(list);
        }
 
-       update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.oid.hash,
-                  NULL, 0, UPDATE_REFS_DIE_ON_ERR);
+       update_ref("updating ORIG_HEAD", "ORIG_HEAD",
+                  &head_commit->object.oid, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
 
        if (remoteheads && !common) {
                /* No common ancestors found. */
index 8e54f2d14648bcf41369e71c2f7f586322c702ef..1a2c7d92ad7e73246be8dc64946feb00c16f7261 100644 (file)
@@ -33,7 +33,7 @@ static const char * const git_notes_usage[] = {
        N_("git notes merge --commit [-v | -q]"),
        N_("git notes merge --abort [-v | -q]"),
        N_("git notes [--ref <notes-ref>] remove [<object>...]"),
-       N_("git notes [--ref <notes-ref>] prune [-n | -v]"),
+       N_("git notes [--ref <notes-ref>] prune [-n] [-v]"),
        N_("git notes [--ref <notes-ref>] get-ref"),
        NULL
 };
@@ -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"));
@@ -724,7 +724,7 @@ static int merge_commit(struct notes_merge_options *o)
        init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
 
        o->local_ref = local_ref_to_free =
-               resolve_refdup("NOTES_MERGE_REF", 0, oid.hash, NULL);
+               resolve_refdup("NOTES_MERGE_REF", 0, &oid, NULL);
        if (!o->local_ref)
                die(_("failed to resolve NOTES_MERGE_REF"));
 
@@ -736,8 +736,8 @@ static int merge_commit(struct notes_merge_options *o)
        format_commit_message(partial, "%s", &msg, &pretty_ctx);
        strbuf_trim(&msg);
        strbuf_insert(&msg, 0, "notes: ", 7);
-       update_ref(msg.buf, o->local_ref, oid.hash,
-                  is_null_oid(&parent_oid) ? NULL : parent_oid.hash,
+       update_ref(msg.buf, o->local_ref, &oid,
+                  is_null_oid(&parent_oid) ? NULL : &parent_oid,
                   0, UPDATE_REFS_DIE_ON_ERR);
 
        free_notes(t);
@@ -850,12 +850,12 @@ static int merge(int argc, const char **argv, const char *prefix)
 
        if (result >= 0) /* Merge resulted (trivially) in result_oid */
                /* Update default notes ref with new commit */
-               update_ref(msg.buf, default_notes_ref(), result_oid.hash, NULL,
-                          0, UPDATE_REFS_DIE_ON_ERR);
+               update_ref(msg.buf, default_notes_ref(), &result_oid, NULL, 0,
+                          UPDATE_REFS_DIE_ON_ERR);
        else { /* Merge has unresolved conflicts */
                const struct worktree *wt;
                /* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
-               update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_oid.hash, NULL,
+               update_ref(msg.buf, "NOTES_MERGE_PARTIAL", &result_oid, NULL,
                           0, UPDATE_REFS_DIE_ON_ERR);
                /* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
                wt = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
@@ -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 6e77dfd44439f4c5f928a73dce3233ff6e63c3b4..631de28761e820124299fa2190917e1515a65362 100644 (file)
@@ -151,7 +151,7 @@ static unsigned long do_compress(void **pptr, unsigned long size)
 }
 
 static unsigned long write_large_blob_data(struct git_istream *st, struct sha1file *f,
-                                          const unsigned char *sha1)
+                                          const struct object_id *oid)
 {
        git_zstream stream;
        unsigned char ibuf[1024 * 16];
@@ -165,7 +165,7 @@ static unsigned long write_large_blob_data(struct git_istream *st, struct sha1fi
                int zret = Z_OK;
                readlen = read_istream(st, ibuf, sizeof(ibuf));
                if (readlen == -1)
-                       die(_("unable to read %s"), sha1_to_hex(sha1));
+                       die(_("unable to read %s"), oid_to_hex(oid));
 
                stream.next_in = ibuf;
                stream.avail_in = readlen;
@@ -339,7 +339,7 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent
                sha1write(f, header, hdrlen);
        }
        if (st) {
-               datalen = write_large_blob_data(st, f, entry->idx.oid.hash);
+               datalen = write_large_blob_data(st, f, &entry->idx.oid);
                close_istream(st);
        } else {
                sha1write(f, buf, datalen);
@@ -557,13 +557,13 @@ static enum write_one_status write_one(struct sha1file *f,
 static int mark_tagged(const char *path, const struct object_id *oid, int flag,
                       void *cb_data)
 {
-       unsigned char peeled[20];
+       struct object_id peeled;
        struct object_entry *entry = packlist_find(&to_pack, oid->hash, NULL);
 
        if (entry)
                entry->tagged = 1;
-       if (!peel_ref(path, peeled)) {
-               entry = packlist_find(&to_pack, peeled, NULL);
+       if (!peel_ref(path, &peeled)) {
+               entry = packlist_find(&to_pack, peeled.hash, NULL);
                if (entry)
                        entry->tagged = 1;
        }
@@ -792,7 +792,7 @@ static void write_pack_file(void)
        write_order = compute_write_order();
 
        do {
-               unsigned char sha1[20];
+               struct object_id oid;
                char *pack_tmp_name = NULL;
 
                if (pack_to_stdout)
@@ -823,13 +823,13 @@ static void write_pack_file(void)
                 * If so, rewrite it like in fast-import
                 */
                if (pack_to_stdout) {
-                       sha1close(f, sha1, CSUM_CLOSE);
+                       sha1close(f, oid.hash, CSUM_CLOSE);
                } else if (nr_written == nr_remaining) {
-                       sha1close(f, sha1, CSUM_FSYNC);
+                       sha1close(f, oid.hash, CSUM_FSYNC);
                } else {
-                       int fd = sha1close(f, sha1, 0);
-                       fixup_pack_header_footer(fd, sha1, pack_tmp_name,
-                                                nr_written, sha1, offset);
+                       int fd = sha1close(f, oid.hash, 0);
+                       fixup_pack_header_footer(fd, oid.hash, pack_tmp_name,
+                                                nr_written, oid.hash, offset);
                        close(fd);
                        if (write_bitmap_index) {
                                warning(_(no_split_warning));
@@ -863,16 +863,16 @@ static void write_pack_file(void)
                        strbuf_addf(&tmpname, "%s-", base_name);
 
                        if (write_bitmap_index) {
-                               bitmap_writer_set_checksum(sha1);
+                               bitmap_writer_set_checksum(oid.hash);
                                bitmap_writer_build_type_index(written_list, nr_written);
                        }
 
                        finish_tmp_packfile(&tmpname, pack_tmp_name,
                                            written_list, nr_written,
-                                           &pack_idx_opts, sha1);
+                                           &pack_idx_opts, oid.hash);
 
                        if (write_bitmap_index) {
-                               strbuf_addf(&tmpname, "%s.bitmap", sha1_to_hex(sha1));
+                               strbuf_addf(&tmpname, "%s.bitmap", oid_to_hex(&oid));
 
                                stop_progress(&progress_state);
 
@@ -887,7 +887,7 @@ static void write_pack_file(void)
 
                        strbuf_release(&tmpname);
                        free(pack_tmp_name);
-                       puts(sha1_to_hex(sha1));
+                       puts(oid_to_hex(&oid));
                }
 
                /* mark written objects as written to previous pack */
@@ -928,13 +928,13 @@ static int no_try_delta(const char *path)
  * found the item, since that saves us from having to look it up again a
  * few lines later when we want to add the new entry.
  */
-static int have_duplicate_entry(const unsigned char *sha1,
+static int have_duplicate_entry(const struct object_id *oid,
                                int exclude,
                                uint32_t *index_pos)
 {
        struct object_entry *entry;
 
-       entry = packlist_find(&to_pack, sha1, index_pos);
+       entry = packlist_find(&to_pack, oid->hash, index_pos);
        if (!entry)
                return 0;
 
@@ -990,7 +990,7 @@ static int want_found_object(int exclude, struct packed_git *p)
  * function finds if there is any pack that has the object and returns the pack
  * and its offset in these variables.
  */
-static int want_object_in_pack(const unsigned char *sha1,
+static int want_object_in_pack(const struct object_id *oid,
                               int exclude,
                               struct packed_git **found_pack,
                               off_t *found_offset)
@@ -998,7 +998,7 @@ static int want_object_in_pack(const unsigned char *sha1,
        struct mru_entry *entry;
        int want;
 
-       if (!exclude && local && has_loose_object_nonlocal(sha1))
+       if (!exclude && local && has_loose_object_nonlocal(oid->hash))
                return 0;
 
        /*
@@ -1019,7 +1019,7 @@ static int want_object_in_pack(const unsigned char *sha1,
                if (p == *found_pack)
                        offset = *found_offset;
                else
-                       offset = find_pack_entry_one(sha1, p);
+                       offset = find_pack_entry_one(oid->hash, p);
 
                if (offset) {
                        if (!*found_pack) {
@@ -1039,7 +1039,7 @@ static int want_object_in_pack(const unsigned char *sha1,
        return 1;
 }
 
-static void create_object_entry(const unsigned char *sha1,
+static void create_object_entry(const struct object_id *oid,
                                enum object_type type,
                                uint32_t hash,
                                int exclude,
@@ -1050,7 +1050,7 @@ static void create_object_entry(const unsigned char *sha1,
 {
        struct object_entry *entry;
 
-       entry = packlist_alloc(&to_pack, sha1, index_pos);
+       entry = packlist_alloc(&to_pack, oid->hash, index_pos);
        entry->hash = hash;
        if (type)
                entry->type = type;
@@ -1070,17 +1070,17 @@ static const char no_closure_warning[] = N_(
 "disabling bitmap writing, as some objects are not being packed"
 );
 
-static int add_object_entry(const unsigned char *sha1, enum object_type type,
+static int add_object_entry(const struct object_id *oid, enum object_type type,
                            const char *name, int exclude)
 {
        struct packed_git *found_pack = NULL;
        off_t found_offset = 0;
        uint32_t index_pos;
 
-       if (have_duplicate_entry(sha1, exclude, &index_pos))
+       if (have_duplicate_entry(oid, exclude, &index_pos))
                return 0;
 
-       if (!want_object_in_pack(sha1, exclude, &found_pack, &found_offset)) {
+       if (!want_object_in_pack(oid, exclude, &found_pack, &found_offset)) {
                /* The pack is missing an object, so it will not have closure */
                if (write_bitmap_index) {
                        warning(_(no_closure_warning));
@@ -1089,7 +1089,7 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,
                return 0;
        }
 
-       create_object_entry(sha1, type, pack_name_hash(name),
+       create_object_entry(oid, type, pack_name_hash(name),
                            exclude, name && no_try_delta(name),
                            index_pos, found_pack, found_offset);
 
@@ -1097,27 +1097,27 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,
        return 1;
 }
 
-static int add_object_entry_from_bitmap(const unsigned char *sha1,
+static int add_object_entry_from_bitmap(const struct object_id *oid,
                                        enum object_type type,
                                        int flags, uint32_t name_hash,
                                        struct packed_git *pack, off_t offset)
 {
        uint32_t index_pos;
 
-       if (have_duplicate_entry(sha1, 0, &index_pos))
+       if (have_duplicate_entry(oid, 0, &index_pos))
                return 0;
 
-       if (!want_object_in_pack(sha1, 0, &pack, &offset))
+       if (!want_object_in_pack(oid, 0, &pack, &offset))
                return 0;
 
-       create_object_entry(sha1, type, name_hash, 0, 0, index_pos, pack, offset);
+       create_object_entry(oid, type, name_hash, 0, 0, index_pos, pack, offset);
 
        display_progress(progress_state, nr_result);
        return 1;
 }
 
 struct pbase_tree_cache {
-       unsigned char sha1[20];
+       struct object_id oid;
        int ref;
        int temporary;
        void *tree_data;
@@ -1125,9 +1125,9 @@ struct pbase_tree_cache {
 };
 
 static struct pbase_tree_cache *(pbase_tree_cache[256]);
-static int pbase_tree_cache_ix(const unsigned char *sha1)
+static int pbase_tree_cache_ix(const struct object_id *oid)
 {
-       return sha1[0] % ARRAY_SIZE(pbase_tree_cache);
+       return oid->hash[0] % ARRAY_SIZE(pbase_tree_cache);
 }
 static int pbase_tree_cache_ix_incr(int ix)
 {
@@ -1144,14 +1144,14 @@ static struct pbase_tree {
        struct pbase_tree_cache pcache;
 } *pbase_tree;
 
-static struct pbase_tree_cache *pbase_tree_get(const unsigned char *sha1)
+static struct pbase_tree_cache *pbase_tree_get(const struct object_id *oid)
 {
        struct pbase_tree_cache *ent, *nent;
        void *data;
        unsigned long size;
        enum object_type type;
        int neigh;
-       int my_ix = pbase_tree_cache_ix(sha1);
+       int my_ix = pbase_tree_cache_ix(oid);
        int available_ix = -1;
 
        /* pbase-tree-cache acts as a limited hashtable.
@@ -1160,7 +1160,7 @@ static struct pbase_tree_cache *pbase_tree_get(const unsigned char *sha1)
         */
        for (neigh = 0; neigh < 8; neigh++) {
                ent = pbase_tree_cache[my_ix];
-               if (ent && !hashcmp(ent->sha1, sha1)) {
+               if (ent && !oidcmp(&ent->oid, oid)) {
                        ent->ref++;
                        return ent;
                }
@@ -1176,7 +1176,7 @@ static struct pbase_tree_cache *pbase_tree_get(const unsigned char *sha1)
        /* Did not find one.  Either we got a bogus request or
         * we need to read and perhaps cache.
         */
-       data = read_sha1_file(sha1, &type, &size);
+       data = read_sha1_file(oid->hash, &type, &size);
        if (!data)
                return NULL;
        if (type != OBJ_TREE) {
@@ -1202,7 +1202,7 @@ static struct pbase_tree_cache *pbase_tree_get(const unsigned char *sha1)
                free(ent->tree_data);
                nent = ent;
        }
-       hashcpy(nent->sha1, sha1);
+       oidcpy(&nent->oid, oid);
        nent->tree_data = data;
        nent->tree_size = size;
        nent->ref = 1;
@@ -1247,7 +1247,7 @@ static void add_pbase_object(struct tree_desc *tree,
                if (cmp < 0)
                        return;
                if (name[cmplen] != '/') {
-                       add_object_entry(entry.oid->hash,
+                       add_object_entry(entry.oid,
                                         object_type(entry.mode),
                                         fullname, 1);
                        return;
@@ -1258,7 +1258,7 @@ static void add_pbase_object(struct tree_desc *tree,
                        const char *down = name+cmplen+1;
                        int downlen = name_cmp_len(down);
 
-                       tree = pbase_tree_get(entry.oid->hash);
+                       tree = pbase_tree_get(entry.oid);
                        if (!tree)
                                return;
                        init_tree_desc(&sub, tree->tree_data, tree->tree_size);
@@ -1317,7 +1317,7 @@ static void add_preferred_base_object(const char *name)
        cmplen = name_cmp_len(name);
        for (it = pbase_tree; it; it = it->next) {
                if (cmplen == 0) {
-                       add_object_entry(it->pcache.sha1, OBJ_TREE, NULL, 1);
+                       add_object_entry(&it->pcache.oid, OBJ_TREE, NULL, 1);
                }
                else {
                        struct tree_desc tree;
@@ -1327,22 +1327,22 @@ static void add_preferred_base_object(const char *name)
        }
 }
 
-static void add_preferred_base(unsigned char *sha1)
+static void add_preferred_base(struct object_id *oid)
 {
        struct pbase_tree *it;
        void *data;
        unsigned long size;
-       unsigned char tree_sha1[20];
+       struct object_id tree_oid;
 
        if (window <= num_preferred_base++)
                return;
 
-       data = read_object_with_reference(sha1, tree_type, &size, tree_sha1);
+       data = read_object_with_reference(oid->hash, tree_type, &size, tree_oid.hash);
        if (!data)
                return;
 
        for (it = pbase_tree; it; it = it->next) {
-               if (!hashcmp(it->pcache.sha1, tree_sha1)) {
+               if (!oidcmp(&it->pcache.oid, &tree_oid)) {
                        free(data);
                        return;
                }
@@ -1352,7 +1352,7 @@ static void add_preferred_base(unsigned char *sha1)
        it->next = pbase_tree;
        pbase_tree = it;
 
-       hashcpy(it->pcache.sha1, tree_sha1);
+       oidcpy(&it->pcache.oid, &tree_oid);
        it->pcache.tree_data = data;
        it->pcache.tree_size = size;
 }
@@ -2357,7 +2357,7 @@ static void add_tag_chain(const struct object_id *oid)
                        die("unable to pack objects reachable from tag %s",
                            oid_to_hex(oid));
 
-               add_object_entry(tag->object.oid.hash, OBJ_TAG, NULL, 0);
+               add_object_entry(&tag->object.oid, OBJ_TAG, NULL, 0);
 
                if (tag->tagged->type != OBJ_TAG)
                        return;
@@ -2371,7 +2371,7 @@ static int add_ref_tag(const char *path, const struct object_id *oid, int flag,
        struct object_id peeled;
 
        if (starts_with(path, "refs/tags/") && /* is a tag? */
-           !peel_ref(path, peeled.hash)    && /* peelable? */
+           !peel_ref(path, &peeled)    && /* peelable? */
            packlist_find(&to_pack, peeled.hash, NULL))      /* object packed? */
                add_tag_chain(oid);
        return 0;
@@ -2505,8 +2505,9 @@ static int git_pack_config(const char *k, const char *v, void *cb)
 
 static void read_object_list_from_stdin(void)
 {
-       char line[40 + 1 + PATH_MAX + 2];
-       unsigned char sha1[20];
+       char line[GIT_MAX_HEXSZ + 1 + PATH_MAX + 2];
+       struct object_id oid;
+       const char *p;
 
        for (;;) {
                if (!fgets(line, sizeof(line), stdin)) {
@@ -2520,17 +2521,17 @@ static void read_object_list_from_stdin(void)
                        continue;
                }
                if (line[0] == '-') {
-                       if (get_sha1_hex(line+1, sha1))
-                               die("expected edge sha1, got garbage:\n %s",
+                       if (get_oid_hex(line+1, &oid))
+                               die("expected edge object ID, got garbage:\n %s",
                                    line);
-                       add_preferred_base(sha1);
+                       add_preferred_base(&oid);
                        continue;
                }
-               if (get_sha1_hex(line, sha1))
-                       die("expected sha1, got garbage:\n %s", line);
+               if (parse_oid_hex(line, &oid, &p))
+                       die("expected object ID, got garbage:\n %s", line);
 
-               add_preferred_base_object(line+41);
-               add_object_entry(sha1, 0, line+41, 0);
+               add_preferred_base_object(p + 1);
+               add_object_entry(&oid, 0, p + 1, 0);
        }
 }
 
@@ -2538,7 +2539,7 @@ static void read_object_list_from_stdin(void)
 
 static void show_commit(struct commit *commit, void *data)
 {
-       add_object_entry(commit->object.oid.hash, OBJ_COMMIT, NULL, 0);
+       add_object_entry(&commit->object.oid, OBJ_COMMIT, NULL, 0);
        commit->object.flags |= OBJECT_ADDED;
 
        if (write_bitmap_index)
@@ -2548,13 +2549,13 @@ static void show_commit(struct commit *commit, void *data)
 static void show_object(struct object *obj, const char *name, void *data)
 {
        add_preferred_base_object(name);
-       add_object_entry(obj->oid.hash, obj->type, name, 0);
+       add_object_entry(&obj->oid, obj->type, name, 0);
        obj->flags |= OBJECT_ADDED;
 }
 
 static void show_edge(struct commit *commit)
 {
-       add_preferred_base(commit->object.oid.hash);
+       add_preferred_base(&commit->object.oid);
 }
 
 struct in_pack_object {
@@ -2601,7 +2602,7 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs)
        memset(&in_pack, 0, sizeof(in_pack));
 
        for (p = packed_git; p; p = p->next) {
-               const unsigned char *sha1;
+               struct object_id oid;
                struct object *o;
 
                if (!p->pack_local || p->pack_keep)
@@ -2614,8 +2615,8 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs)
                           in_pack.alloc);
 
                for (i = 0; i < p->num_objects; i++) {
-                       sha1 = nth_packed_object_sha1(p, i);
-                       o = lookup_unknown_object(sha1);
+                       nth_packed_object_oid(&oid, p, i);
+                       o = lookup_unknown_object(oid.hash);
                        if (!(o->flags & OBJECT_ADDED))
                                mark_in_pack_object(o, p, &in_pack);
                        o->flags |= OBJECT_ADDED;
@@ -2626,7 +2627,7 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs)
                QSORT(in_pack.array, in_pack.nr, ofscmp);
                for (i = 0; i < in_pack.nr; i++) {
                        struct object *o = in_pack.array[i].object;
-                       add_object_entry(o->oid.hash, o->type, "", 0);
+                       add_object_entry(&o->oid, o->type, "", 0);
                }
        }
        free(in_pack.array);
@@ -2642,7 +2643,7 @@ static int add_loose_object(const struct object_id *oid, const char *path,
                return 0;
        }
 
-       add_object_entry(oid->hash, type, "", 0);
+       add_object_entry(oid, type, "", 0);
        return 0;
 }
 
@@ -2658,7 +2659,7 @@ static void add_unreachable_loose_objects(void)
                                      NULL, NULL, NULL);
 }
 
-static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
+static int has_sha1_pack_kept_or_nonlocal(const struct object_id *oid)
 {
        static struct packed_git *last_found = (void *)1;
        struct packed_git *p;
@@ -2667,7 +2668,7 @@ static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
 
        while (p) {
                if ((!p->pack_local || p->pack_keep) &&
-                       find_pack_entry_one(sha1, p)) {
+                       find_pack_entry_one(oid->hash, p)) {
                        last_found = p;
                        return 1;
                }
@@ -2718,7 +2719,7 @@ static void loosen_unused_packed_objects(struct rev_info *revs)
                for (i = 0; i < p->num_objects; i++) {
                        nth_packed_object_oid(&oid, p, i);
                        if (!packlist_find(&to_pack, oid.hash, NULL) &&
-                           !has_sha1_pack_kept_or_nonlocal(oid.hash) &&
+                           !has_sha1_pack_kept_or_nonlocal(&oid) &&
                            !loosened_object_can_be_discarded(&oid, p->mtime))
                                if (force_object_loose(oid.hash, p->mtime))
                                        die("unable to force loose object");
index cddabf26a95cc22dfcad9843554dc8af26c858bc..d2fdae680a1ebe75d55d4fb66a3bdd0b54829666 100644 (file)
@@ -8,7 +8,7 @@
 #include "progress.h"
 
 static const char * const prune_usage[] = {
-       N_("git prune [-n] [-v] [--expire <time>] [--] [<head>...]"),
+       N_("git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"),
        NULL
 };
 static int show_only;
index 6f772e8a224c18238cf9a75436e8ea192765e7fe..511dbbe0f6e25d8f0e8c6ce511cc0ff734adc9bc 100644 (file)
@@ -86,6 +86,7 @@ static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
 static enum rebase_type opt_rebase = -1;
 static char *opt_diffstat;
 static char *opt_log;
+static char *opt_signoff;
 static char *opt_squash;
 static char *opt_commit;
 static char *opt_edit;
@@ -112,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 */
@@ -142,6 +145,9 @@ static struct option pull_options[] = {
        OPT_PASSTHRU(0, "log", &opt_log, N_("n"),
                N_("add (at most <n>) entries from shortlog to merge commit message"),
                PARSE_OPT_OPTARG),
+       OPT_PASSTHRU(0, "signoff", &opt_signoff, NULL,
+               N_("add Signed-off-by:"),
+               PARSE_OPT_OPTARG),
        OPT_PASSTHRU(0, "squash", &opt_squash, NULL,
                N_("create a single commit instead of doing a merge"),
                PARSE_OPT_NOARG),
@@ -214,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()
 };
@@ -518,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);
@@ -541,10 +557,10 @@ static int pull_into_void(const struct object_id *merge_head,
         * index/worktree changes that the user already made on the unborn
         * branch.
         */
-       if (checkout_fast_forward(&empty_tree_oid, merge_head, 0))
+       if (checkout_fast_forward(the_hash_algo->empty_tree, merge_head, 0))
                return 1;
 
-       if (update_ref("initial pull", "HEAD", merge_head->hash, curr_head->hash, 0, UPDATE_REFS_DIE_ON_ERR))
+       if (update_ref("initial pull", "HEAD", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR))
                return 1;
 
        return 0;
@@ -594,6 +610,8 @@ static int run_merge(void)
                argv_array_push(&args, opt_diffstat);
        if (opt_log)
                argv_array_push(&args, opt_log);
+       if (opt_signoff)
+               argv_array_push(&args, opt_signoff);
        if (opt_squash)
                argv_array_push(&args, opt_squash);
        if (opt_commit)
@@ -745,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 2ac81042292ef1852ec9a31dea34ec91a1b796e3..1c28427d82ee73631fb02b5f126c40ebd2cb1774 100644 (file)
@@ -32,6 +32,8 @@ static const char **refspec;
 static int refspec_nr;
 static int refspec_alloc;
 
+static struct string_list push_options_config = STRING_LIST_INIT_DUP;
+
 static void add_refspec(const char *ref)
 {
        refspec_nr++;
@@ -503,6 +505,15 @@ static int git_push_config(const char *k, const char *v, void *cb)
                int val = git_config_bool(k, v) ?
                        RECURSE_SUBMODULES_ON_DEMAND : RECURSE_SUBMODULES_OFF;
                recurse_submodules = val;
+       } else if (!strcmp(k, "push.pushoption")) {
+               if (!v)
+                       return config_error_nonbool(k);
+               else
+                       if (!*v)
+                               string_list_clear(&push_options_config, 0);
+                       else
+                               string_list_append(&push_options_config, v);
+               return 0;
        }
 
        return git_default_config(k, v, NULL);
@@ -515,7 +526,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)
        int push_cert = -1;
        int rc;
        const char *repo = NULL;        /* default repository */
-       struct string_list push_options = STRING_LIST_INIT_DUP;
+       struct string_list push_options_cmdline = STRING_LIST_INIT_DUP;
+       struct string_list *push_options;
        const struct string_list_item *item;
 
        struct option options[] = {
@@ -551,7 +563,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                  0, "signed", &push_cert, "yes|no|if-asked", N_("GPG sign the push"),
                  PARSE_OPT_OPTARG, option_parse_push_signed },
                OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC),
-               OPT_STRING_LIST('o', "push-option", &push_options, N_("server-specific"), N_("option to transmit")),
+               OPT_STRING_LIST('o', "push-option", &push_options_cmdline, N_("server-specific"), N_("option to transmit")),
                OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
                                TRANSPORT_FAMILY_IPV4),
                OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
@@ -562,6 +574,9 @@ int cmd_push(int argc, const char **argv, const char *prefix)
        packet_trace_identity("push");
        git_config(git_push_config, &flags);
        argc = parse_options(argc, argv, prefix, options, push_usage, 0);
+       push_options = (push_options_cmdline.nr
+               ? &push_options_cmdline
+               : &push_options_config);
        set_push_cert_flags(&flags, push_cert);
 
        if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR))))
@@ -584,12 +599,13 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                set_refspecs(argv + 1, argc - 1, repo);
        }
 
-       for_each_string_list_item(item, &push_options)
+       for_each_string_list_item(item, push_options)
                if (strchr(item->string, '\n'))
                        die(_("push options must not have new line characters"));
 
-       rc = do_push(repo, flags, &push_options);
-       string_list_clear(&push_options, 0);
+       rc = do_push(repo, flags, push_options);
+       string_list_clear(&push_options_cmdline, 0);
+       string_list_clear(&push_options_config, 0);
        if (rc == -1)
                usage_with_options(push_usage, options);
        else
index cc4876740569c6e495b7cb32161d019619171d79..b7ce7c7f5275febbf6db6b71e3b610b09a4fe191 100644 (file)
@@ -24,6 +24,7 @@
 #include "tmp-objdir.h"
 #include "oidset.h"
 #include "packfile.h"
+#include "protocol.h"
 
 static const char * const receive_pack_usage[] = {
        N_("git receive-pack <git-dir>"),
@@ -870,7 +871,7 @@ static void refuse_unconfigured_deny_delete_current(void)
        rp_error("%s", _(refuse_unconfigured_deny_delete_current_msg));
 }
 
-static int command_singleton_iterator(void *cb_data, unsigned char sha1[20]);
+static int command_singleton_iterator(void *cb_data, struct object_id *oid);
 static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
 {
        static struct lock_file shallow_lock;
@@ -1139,7 +1140,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                }
                if (ref_transaction_delete(transaction,
                                           namespaced_name,
-                                          old_oid ? old_oid->hash : NULL,
+                                          old_oid,
                                           0, "push", &err)) {
                        rp_error("%s", err.buf);
                        strbuf_release(&err);
@@ -1156,7 +1157,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
 
                if (ref_transaction_update(transaction,
                                           namespaced_name,
-                                          new_oid->hash, old_oid->hash,
+                                          new_oid, old_oid,
                                           0, "push",
                                           &err)) {
                        rp_error("%s", err.buf);
@@ -1270,7 +1271,7 @@ static void check_aliased_updates(struct command *commands)
        string_list_clear(&ref_list, 0);
 }
 
-static int command_singleton_iterator(void *cb_data, unsigned char sha1[20])
+static int command_singleton_iterator(void *cb_data, struct object_id *oid)
 {
        struct command **cmd_list = cb_data;
        struct command *cmd = *cmd_list;
@@ -1278,7 +1279,7 @@ static int command_singleton_iterator(void *cb_data, unsigned char sha1[20])
        if (!cmd || is_null_oid(&cmd->new_oid))
                return -1; /* end of list */
        *cmd_list = NULL; /* this returns only one */
-       hashcpy(sha1, cmd->new_oid.hash);
+       oidcpy(oid, &cmd->new_oid);
        return 0;
 }
 
@@ -1309,7 +1310,7 @@ struct iterate_data {
        struct shallow_info *si;
 };
 
-static int iterate_receive_command_list(void *cb_data, unsigned char sha1[20])
+static int iterate_receive_command_list(void *cb_data, struct object_id *oid)
 {
        struct iterate_data *data = cb_data;
        struct command **cmd_list = &data->cmds;
@@ -1320,7 +1321,7 @@ static int iterate_receive_command_list(void *cb_data, unsigned char sha1[20])
                        /* to be checked in update_shallow_ref() */
                        continue;
                if (!is_null_oid(&cmd->new_oid) && !cmd->skip_update) {
-                       hashcpy(sha1, cmd->new_oid.hash);
+                       oidcpy(oid, &cmd->new_oid);
                        *cmd_list = cmd->next;
                        return 0;
                }
@@ -1961,6 +1962,22 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
        else if (0 <= receive_unpack_limit)
                unpack_limit = receive_unpack_limit;
 
+       switch (determine_protocol_version_server()) {
+       case protocol_v1:
+               /*
+                * v1 is just the original protocol with a version string,
+                * so just fall through after writing the version string.
+                */
+               if (advertise_refs || !stateless_rpc)
+                       packet_write_fmt(1, "version 1\n");
+
+               /* fallthrough */
+       case protocol_v0:
+               break;
+       case protocol_unknown_version:
+               BUG("unknown protocol version");
+       }
+
        if (advertise_refs || !stateless_rpc) {
                write_head_info();
        }
index 2067cca5b1ec4ed141a3e404cb7738c9b0ebc5d9..2233725315ba32832ec5c866b7dbdf72103ac74d 100644 (file)
@@ -42,7 +42,7 @@ struct expire_reflog_policy_cb {
 };
 
 struct collected_reflog {
-       unsigned char sha1[20];
+       struct object_id oid;
        char reflog[FLEX_ARRAY];
 };
 
@@ -385,7 +385,7 @@ static int collect_reflog(const char *ref, const struct object_id *oid, int unus
        struct collect_reflog_cb *cb = cb_data;
 
        FLEX_ALLOC_STR(e, reflog, ref);
-       hashcpy(e->sha1, oid->hash);
+       oidcpy(&e->oid, oid);
        ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc);
        cb->e[cb->nr++] = e;
        return 0;
@@ -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);
@@ -589,7 +579,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
                for (i = 0; i < collected.nr; i++) {
                        struct collected_reflog *e = collected.e[i];
                        set_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog);
-                       status |= reflog_expire(e->reflog, e->sha1, flags,
+                       status |= reflog_expire(e->reflog, &e->oid, flags,
                                                reflog_expiry_prepare,
                                                should_expire_reflog_ent,
                                                reflog_expiry_cleanup,
@@ -601,13 +591,13 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 
        for (; i < argc; i++) {
                char *ref;
-               unsigned char sha1[20];
-               if (!dwim_log(argv[i], strlen(argv[i]), sha1, &ref)) {
+               struct object_id oid;
+               if (!dwim_log(argv[i], strlen(argv[i]), &oid, &ref)) {
                        status |= error("%s points nowhere!", argv[i]);
                        continue;
                }
                set_reflog_expiry_param(&cb.cmd, explicit_expiry, ref);
-               status |= reflog_expire(ref, sha1, flags,
+               status |= reflog_expire(ref, &oid, flags,
                                        reflog_expiry_prepare,
                                        should_expire_reflog_ent,
                                        reflog_expiry_cleanup,
@@ -659,7 +649,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
 
        for ( ; i < argc; i++) {
                const char *spec = strstr(argv[i], "@{");
-               unsigned char sha1[20];
+               struct object_id oid;
                char *ep, *ref;
                int recno;
 
@@ -668,7 +658,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
                        continue;
                }
 
-               if (!dwim_log(argv[i], spec - argv[i], sha1, &ref)) {
+               if (!dwim_log(argv[i], spec - argv[i], &oid, &ref)) {
                        status |= error("no reflog for '%s'", argv[i]);
                        continue;
                }
@@ -683,7 +673,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
                        cb.cmd.expire_total = 0;
                }
 
-               status |= reflog_expire(ref, sha1, flags,
+               status |= reflog_expire(ref, &oid, flags,
                                        reflog_expiry_prepare,
                                        should_expire_reflog_ent,
                                        reflog_expiry_cleanup,
index 4f5cac96b0f5e0044ddf0fd1e2ea686f694d62ca..d95bf904c3b3fb40438335198a2729e45a9897cb 100644 (file)
@@ -565,7 +565,7 @@ static int read_remote_branches(const char *refname,
                item = string_list_append(rename->remote_branches, xstrdup(refname));
                symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING,
                                            NULL, &flag);
-               if (flag & REF_ISSYMREF)
+               if (symref && (flag & REF_ISSYMREF))
                        item->util = xstrdup(symref);
                else
                        item->util = NULL;
@@ -690,10 +690,10 @@ static int mv(int argc, const char **argv)
                int flag = 0;
                struct object_id oid;
 
-               read_ref_full(item->string, RESOLVE_REF_READING, oid.hash, &flag);
+               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 3e71a771523d8566590bfbd5de71b08acf79e3a4..10078ae37136f154486d8163b6b905ad163c7047 100644 (file)
@@ -113,7 +113,7 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
                strbuf_addstr(&ref, oid_to_hex(&oid));
                full_hex = ref.buf + base_len;
 
-               if (read_ref(ref.buf, oid.hash)) {
+               if (read_ref(ref.buf, &oid)) {
                        error("replace ref '%s' not found.", full_hex);
                        had_error = 1;
                        continue;
@@ -128,7 +128,7 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
 static int delete_replace_ref(const char *name, const char *ref,
                              const struct object_id *oid)
 {
-       if (delete_ref(NULL, ref, oid->hash, 0))
+       if (delete_ref(NULL, ref, oid, 0))
                return 1;
        printf("Deleted replace ref '%s'\n", name);
        return 0;
@@ -144,7 +144,7 @@ static void check_ref_valid(struct object_id *object,
        if (check_refname_format(ref->buf, 0))
                die("'%s' is not a valid ref name.", ref->buf);
 
-       if (read_ref(ref->buf, prev->hash))
+       if (read_ref(ref->buf, prev))
                oidclr(prev);
        else if (!force)
                die("replace ref '%s' already exists", ref->buf);
@@ -175,7 +175,7 @@ static int replace_object_oid(const char *object_ref,
 
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
-           ref_transaction_update(transaction, ref.buf, repl->hash, prev.hash,
+           ref_transaction_update(transaction, ref.buf, repl, &prev,
                                   0, NULL, &err) ||
            ref_transaction_commit(transaction, &err))
                die("%s", err.buf);
index 9cd89b23059f768c4651725f2c26c5b08c35f650..906e5416582301233cb275fb102a63cb6d1dd139 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;
@@ -266,12 +266,12 @@ static int reset_refs(const char *rev, const struct object_id *oid)
        if (!get_oid("HEAD", &oid_orig)) {
                orig = &oid_orig;
                set_reflog_message(&msg, "updating ORIG_HEAD", NULL);
-               update_ref_oid(msg.buf, "ORIG_HEAD", orig, old_orig, 0,
+               update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0,
                           UPDATE_REFS_MSG_ON_ERR);
        } else if (old_orig)
-               delete_ref(NULL, "ORIG_HEAD", old_orig->hash, 0);
+               delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
        set_reflog_message(&msg, "updating HEAD", rev);
-       update_ref_status = update_ref_oid(msg.buf, "HEAD", oid, orig, 0,
+       update_ref_status = update_ref(msg.buf, "HEAD", oid, orig, 0,
                                       UPDATE_REFS_MSG_ON_ERR);
        strbuf_release(&msg);
        return update_ref_status;
index c1c74d4a7956430fca46fd743946280daf2f0f3f..4032eb381158942b83e9ce12a5c984fceb239b20 100644 (file)
@@ -258,14 +258,14 @@ static int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
 }
 
 static int show_object_fast(
-       const unsigned char *sha1,
+       const struct object_id *oid,
        enum object_type type,
        int exclude,
        uint32_t name_hash,
        struct packed_git *found_pack,
        off_t found_offset)
 {
-       fprintf(stdout, "%s\n", sha1_to_hex(sha1));
+       fprintf(stdout, "%s\n", oid_to_hex(oid));
        return 1;
 }
 
@@ -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 a8d7e6f7aecc2e4179547bde3fd6d07231764b9f..74aa644cbb37b5741c521eba53d0a040aaf1582e 100644 (file)
@@ -133,7 +133,7 @@ static void show_rev(int type, const struct object_id *oid, const char *name)
                        struct object_id discard;
                        char *full;
 
-                       switch (dwim_ref(name, strlen(name), discard.hash, &full)) {
+                       switch (dwim_ref(name, strlen(name), &discard, &full)) {
                        case 0:
                                /*
                                 * Not found -- not a ref.  We could
index 6fa1f62a88ac2704abc6f274ed7a6170bd3a2f4c..2e24b5c330e8eb17a8755e9b47fa3c66810981d3 100644 (file)
@@ -705,8 +705,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                        static const char *fake_av[2];
 
                        fake_av[0] = resolve_refdup("HEAD",
-                                                   RESOLVE_REF_READING,
-                                                   oid.hash, NULL);
+                                                   RESOLVE_REF_READING, &oid,
+                                                   NULL);
                        fake_av[1] = NULL;
                        av = fake_av;
                        ac = 1;
@@ -720,7 +720,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                        die(Q_("only %d entry can be shown at one time.",
                               "only %d entries can be shown at one time.",
                               MAX_REVS), MAX_REVS);
-               if (!dwim_ref(*av, strlen(*av), oid.hash, &ref))
+               if (!dwim_ref(*av, strlen(*av), &oid, &ref))
                        die(_("no such ref %s"), *av);
 
                /* Has the base been specified? */
@@ -731,7 +731,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                                /* Ah, that is a date spec... */
                                timestamp_t at;
                                at = approxidate(reflog_base);
-                               read_ref_at(ref, flags, at, -1, oid.hash, NULL,
+                               read_ref_at(ref, flags, at, -1, &oid, NULL,
                                            NULL, NULL, &base);
                        }
                }
@@ -743,7 +743,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                        timestamp_t timestamp;
                        int tz;
 
-                       if (read_ref_at(ref, flags, 0, base+i, oid.hash, &logmsg,
+                       if (read_ref_at(ref, flags, 0, base + i, &oid, &logmsg,
                                        &timestamp, &tz, NULL)) {
                                reflog = i;
                                break;
@@ -775,7 +775,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
        }
 
        head = resolve_refdup("HEAD", RESOLVE_REF_READING,
-                             head_oid.hash, NULL);
+                             &head_oid, NULL);
 
        if (with_current_branch && head) {
                int has_head = 0;
index 013d241abc09be76c318dbc25d5fb3b40ade7b3a..41e5e71cad660d26ddc90ffeaa383fd7bf10f79f 100644 (file)
@@ -38,7 +38,7 @@ static void show_one(const char *refname, const struct object_id *oid)
        if (!deref_tags)
                return;
 
-       if (!peel_ref(refname, peeled.hash)) {
+       if (!peel_ref(refname, &peeled)) {
                hex = find_unique_abbrev(peeled.hash, abbrev);
                printf("%s %s^{}\n", hex, refname);
        }
@@ -197,7 +197,7 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)
                        struct object_id oid;
 
                        if ((starts_with(*pattern, "refs/") || !strcmp(*pattern, "HEAD")) &&
-                           !read_ref(*pattern, oid.hash)) {
+                           !read_ref(*pattern, &oid)) {
                                show_one(*pattern, &oid);
                        }
                        else if (!quiet)
index 06ed02f99402be6a7edb36ff0eb08b903c513c6c..a5c4a8a6941d46c78e4dc58d0df47532f0bb2b45 100644 (file)
 #include "remote.h"
 #include "refs.h"
 #include "connect.h"
+#include "revision.h"
+#include "diffcore.h"
+#include "diff.h"
+
+#define OPT_QUIET (1 << 0)
+#define OPT_CACHED (1 << 1)
+#define OPT_RECURSIVE (1 << 2)
+
+typedef void (*each_submodule_fn)(const struct cache_entry *list_item,
+                                 void *cb_data);
 
 static char *get_default_remote(void)
 {
@@ -219,6 +229,64 @@ static int resolve_relative_url_test(int argc, const char **argv, const char *pr
        return 0;
 }
 
+/* the result should be freed by the caller. */
+static char *get_submodule_displaypath(const char *path, const char *prefix)
+{
+       const char *super_prefix = get_super_prefix();
+
+       if (prefix && super_prefix) {
+               BUG("cannot have prefix '%s' and superprefix '%s'",
+                   prefix, super_prefix);
+       } else if (prefix) {
+               struct strbuf sb = STRBUF_INIT;
+               char *displaypath = xstrdup(relative_path(path, prefix, &sb));
+               strbuf_release(&sb);
+               return displaypath;
+       } else if (super_prefix) {
+               return xstrfmt("%s%s", super_prefix, path);
+       } else {
+               return xstrdup(path);
+       }
+}
+
+static char *compute_rev_name(const char *sub_path, const char* object_id)
+{
+       struct strbuf sb = STRBUF_INIT;
+       const char ***d;
+
+       static const char *describe_bare[] = { NULL };
+
+       static const char *describe_tags[] = { "--tags", NULL };
+
+       static const char *describe_contains[] = { "--contains", NULL };
+
+       static const char *describe_all_always[] = { "--all", "--always", NULL };
+
+       static const char **describe_argv[] = { describe_bare, describe_tags,
+                                               describe_contains,
+                                               describe_all_always, NULL };
+
+       for (d = describe_argv; *d; d++) {
+               struct child_process cp = CHILD_PROCESS_INIT;
+               prepare_submodule_repo_env(&cp.env_array);
+               cp.dir = sub_path;
+               cp.git_cmd = 1;
+               cp.no_stderr = 1;
+
+               argv_array_push(&cp.args, "describe");
+               argv_array_pushv(&cp.args, *d);
+               argv_array_push(&cp.args, object_id);
+
+               if (!capture_command(&cp, &sb, 0)) {
+                       strbuf_strip_suffix(&sb, "\n");
+                       return strbuf_detach(&sb, NULL);
+               }
+       }
+
+       strbuf_release(&sb);
+       return NULL;
+}
+
 struct module_list {
        const struct cache_entry **entries;
        int alloc, nr;
@@ -328,21 +396,29 @@ static int module_list(int argc, const char **argv, const char *prefix)
        return 0;
 }
 
-static void init_submodule(const char *path, const char *prefix, int quiet)
+static void for_each_listed_submodule(const struct module_list *list,
+                                     each_submodule_fn fn, void *cb_data)
+{
+       int i;
+       for (i = 0; i < list->nr; i++)
+               fn(list->entries[i], cb_data);
+}
+
+struct init_cb {
+       const char *prefix;
+       unsigned int flags;
+};
+
+#define INIT_CB_INIT { NULL, 0 }
+
+static void init_submodule(const char *path, const char *prefix,
+                          unsigned int flags)
 {
        const struct submodule *sub;
        struct strbuf sb = STRBUF_INIT;
        char *upd = NULL, *url = NULL, *displaypath;
 
-       if (prefix && get_super_prefix())
-               die("BUG: cannot have prefix and superprefix");
-       else if (prefix)
-               displaypath = xstrdup(relative_path(path, prefix, &sb));
-       else if (get_super_prefix()) {
-               strbuf_addf(&sb, "%s%s", get_super_prefix(), path);
-               displaypath = strbuf_detach(&sb, NULL);
-       } else
-               displaypath = xstrdup(path);
+       displaypath = get_submodule_displaypath(path, prefix);
 
        sub = submodule_from_path(&null_oid, path);
 
@@ -357,9 +433,9 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
         * Set active flag for the submodule being initialized
         */
        if (!is_submodule_active(the_repository, path)) {
-               strbuf_reset(&sb);
                strbuf_addf(&sb, "submodule.%s.active", sub->name);
                git_config_set_gently(sb.buf, "true");
+               strbuf_reset(&sb);
        }
 
        /*
@@ -367,7 +443,6 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
         * To look up the url in .git/config, we must not fall back to
         * .gitmodules, so look it up directly.
         */
-       strbuf_reset(&sb);
        strbuf_addf(&sb, "submodule.%s.url", sub->name);
        if (git_config_get_string(sb.buf, &url)) {
                if (!sub->url)
@@ -399,14 +474,14 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
                if (git_config_set_gently(sb.buf, url))
                        die(_("Failed to register url for submodule path '%s'"),
                            displaypath);
-               if (!quiet)
+               if (!(flags & OPT_QUIET))
                        fprintf(stderr,
                                _("Submodule '%s' (%s) registered for path '%s'\n"),
                                sub->name, url, displaypath);
        }
+       strbuf_reset(&sb);
 
        /* Copy "update" setting when it is not set yet */
-       strbuf_reset(&sb);
        strbuf_addf(&sb, "submodule.%s.update", sub->name);
        if (git_config_get_string(sb.buf, &upd) &&
            sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {
@@ -426,12 +501,18 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
        free(upd);
 }
 
+static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data)
+{
+       struct init_cb *info = cb_data;
+       init_submodule(list_item->name, info->prefix, info->flags);
+}
+
 static int module_init(int argc, const char **argv, const char *prefix)
 {
+       struct init_cb info = INIT_CB_INIT;
        struct pathspec pathspec;
        struct module_list list = MODULE_LIST_INIT;
        int quiet = 0;
-       int i;
 
        struct option module_init_options[] = {
                OPT__QUIET(&quiet, N_("Suppress output for initializing a submodule")),
@@ -456,8 +537,165 @@ static int module_init(int argc, const char **argv, const char *prefix)
        if (!argc && git_config_get_value_multi("submodule.active"))
                module_list_active(&list);
 
-       for (i = 0; i < list.nr; i++)
-               init_submodule(list.entries[i]->name, prefix, quiet);
+       info.prefix = prefix;
+       if (quiet)
+               info.flags |= OPT_QUIET;
+
+       for_each_listed_submodule(&list, init_submodule_cb, &info);
+
+       return 0;
+}
+
+struct status_cb {
+       const char *prefix;
+       unsigned int flags;
+};
+
+#define STATUS_CB_INIT { NULL, 0 }
+
+static void print_status(unsigned int flags, char state, const char *path,
+                        const struct object_id *oid, const char *displaypath)
+{
+       if (flags & OPT_QUIET)
+               return;
+
+       printf("%c%s %s", state, oid_to_hex(oid), displaypath);
+
+       if (state == ' ' || state == '+')
+               printf(" (%s)", compute_rev_name(path, oid_to_hex(oid)));
+
+       printf("\n");
+}
+
+static int handle_submodule_head_ref(const char *refname,
+                                    const struct object_id *oid, int flags,
+                                    void *cb_data)
+{
+       struct object_id *output = cb_data;
+       if (oid)
+               oidcpy(output, oid);
+
+       return 0;
+}
+
+static void status_submodule(const char *path, const struct object_id *ce_oid,
+                            unsigned int ce_flags, const char *prefix,
+                            unsigned int flags)
+{
+       char *displaypath;
+       struct argv_array diff_files_args = ARGV_ARRAY_INIT;
+       struct rev_info rev;
+       int diff_files_result;
+
+       if (!submodule_from_path(&null_oid, path))
+               die(_("no submodule mapping found in .gitmodules for path '%s'"),
+                     path);
+
+       displaypath = get_submodule_displaypath(path, prefix);
+
+       if ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) {
+               print_status(flags, 'U', path, &null_oid, displaypath);
+               goto cleanup;
+       }
+
+       if (!is_submodule_active(the_repository, path)) {
+               print_status(flags, '-', path, ce_oid, displaypath);
+               goto cleanup;
+       }
+
+       argv_array_pushl(&diff_files_args, "diff-files",
+                        "--ignore-submodules=dirty", "--quiet", "--",
+                        path, NULL);
+
+       git_config(git_diff_basic_config, NULL);
+       init_revisions(&rev, prefix);
+       rev.abbrev = 0;
+       diff_files_args.argc = setup_revisions(diff_files_args.argc,
+                                              diff_files_args.argv,
+                                              &rev, NULL);
+       diff_files_result = run_diff_files(&rev, 0);
+
+       if (!diff_result_code(&rev.diffopt, diff_files_result)) {
+               print_status(flags, ' ', path, ce_oid,
+                            displaypath);
+       } else if (!(flags & OPT_CACHED)) {
+               struct object_id oid;
+
+               if (refs_head_ref(get_submodule_ref_store(path),
+                                 handle_submodule_head_ref, &oid))
+                       die(_("could not resolve HEAD ref inside the "
+                             "submodule '%s'"), path);
+
+               print_status(flags, '+', path, &oid, displaypath);
+       } else {
+               print_status(flags, '+', path, ce_oid, displaypath);
+       }
+
+       if (flags & OPT_RECURSIVE) {
+               struct child_process cpr = CHILD_PROCESS_INIT;
+
+               cpr.git_cmd = 1;
+               cpr.dir = path;
+               prepare_submodule_repo_env(&cpr.env_array);
+
+               argv_array_push(&cpr.args, "--super-prefix");
+               argv_array_pushf(&cpr.args, "%s/", displaypath);
+               argv_array_pushl(&cpr.args, "submodule--helper", "status",
+                                "--recursive", NULL);
+
+               if (flags & OPT_CACHED)
+                       argv_array_push(&cpr.args, "--cached");
+
+               if (flags & OPT_QUIET)
+                       argv_array_push(&cpr.args, "--quiet");
+
+               if (run_command(&cpr))
+                       die(_("failed to recurse into submodule '%s'"), path);
+       }
+
+cleanup:
+       argv_array_clear(&diff_files_args);
+       free(displaypath);
+}
+
+static void status_submodule_cb(const struct cache_entry *list_item,
+                               void *cb_data)
+{
+       struct status_cb *info = cb_data;
+       status_submodule(list_item->name, &list_item->oid, list_item->ce_flags,
+                        info->prefix, info->flags);
+}
+
+static int module_status(int argc, const char **argv, const char *prefix)
+{
+       struct status_cb info = STATUS_CB_INIT;
+       struct pathspec pathspec;
+       struct module_list list = MODULE_LIST_INIT;
+       int quiet = 0;
+
+       struct option module_status_options[] = {
+               OPT__QUIET(&quiet, N_("Suppress submodule status output")),
+               OPT_BIT(0, "cached", &info.flags, N_("Use commit stored in the index instead of the one stored in the submodule HEAD"), OPT_CACHED),
+               OPT_BIT(0, "recursive", &info.flags, N_("recurse into nested submodules"), OPT_RECURSIVE),
+               OPT_END()
+       };
+
+       const char *const git_submodule_helper_usage[] = {
+               N_("git submodule status [--quiet] [--cached] [--recursive] [<path>...]"),
+               NULL
+       };
+
+       argc = parse_options(argc, argv, prefix, module_status_options,
+                            git_submodule_helper_usage, 0);
+
+       if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+               return 1;
+
+       info.prefix = prefix;
+       if (quiet)
+               info.flags |= OPT_QUIET;
+
+       for_each_listed_submodule(&list, status_submodule_cb, &info);
 
        return 0;
 }
@@ -1144,7 +1382,7 @@ static int push_check(int argc, const char **argv, const char *prefix)
        argv++;
        argc--;
        /* Get the submodule's head ref and determine if it is detached */
-       head = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
+       head = resolve_refdup("HEAD", 0, &head_oid, NULL);
        if (!head)
                die(_("Failed to resolve HEAD as a valid ref."));
        if (!strcmp(head, "HEAD"))
@@ -1259,6 +1497,7 @@ static struct cmd_struct commands[] = {
        {"resolve-relative-url", resolve_relative_url, 0},
        {"resolve-relative-url-test", resolve_relative_url_test, 0},
        {"init", module_init, SUPPORT_SUPER_PREFIX},
+       {"status", module_status, SUPPORT_SUPER_PREFIX},
        {"remote-branch", resolve_remote_submodule_branch, 0},
        {"push-check", push_check, 0},
        {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
index 17aabaa679d599090ef5eb01d0c3650108c4f79c..80237f0df10f442181814900a93d797f965ec3de 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 b38329b593b595edad3efde82aaf1ecb90a73141..a7e6a5b0f234a95fb45a71d7e9aa7f0baa2b47f8 100644 (file)
@@ -82,7 +82,7 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
        for (p = argv; *p; p++) {
                strbuf_reset(&ref);
                strbuf_addf(&ref, "refs/tags/%s", *p);
-               if (read_ref(ref.buf, oid.hash)) {
+               if (read_ref(ref.buf, &oid)) {
                        error(_("tag '%s' not found."), *p);
                        had_error = 1;
                        continue;
@@ -97,7 +97,7 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
 static int delete_tag(const char *name, const char *ref,
                      const struct object_id *oid, const void *cb_data)
 {
-       if (delete_ref(NULL, ref, oid->hash, 0))
+       if (delete_ref(NULL, ref, oid, 0))
                return 1;
        printf(_("Deleted tag '%s' (was %s)\n"), name, find_unique_abbrev(oid->hash, DEFAULT_ABBREV));
        return 0;
@@ -518,7 +518,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        if (strbuf_check_tag_ref(&ref, tag))
                die(_("'%s' is not a valid tag name."), tag);
 
-       if (read_ref(ref.buf, prev.hash))
+       if (read_ref(ref.buf, &prev))
                oidclr(&prev);
        else if (!force)
                die(_("tag '%s' already exists"), tag);
@@ -544,7 +544,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
-           ref_transaction_update(transaction, ref.buf, object.hash, prev.hash,
+           ref_transaction_update(transaction, ref.buf, &object, &prev,
                                   create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
                                   reflog_msg.buf, &err) ||
            ref_transaction_commit(transaction, &err))
index bf7420b808fd1e0314d981bf65073df62f7578a4..58d1c2d2827d61899d73f1ea7632c5ee219f3ace 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
@@ -328,7 +331,7 @@ static int process_directory(const char *path, int len, struct stat *st)
                if (S_ISGITLINK(ce->ce_mode)) {
 
                        /* Do nothing to the index if there is no HEAD! */
-                       if (resolve_gitlink_ref(path, "HEAD", oid.hash) < 0)
+                       if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
                                return 0;
 
                        return add_one_path(ce, path, len, st);
@@ -354,7 +357,7 @@ static int process_directory(const char *path, int len, struct stat *st)
        }
 
        /* No match - should we add it as a gitlink? */
-       if (!resolve_gitlink_ref(path, "HEAD", oid.hash))
+       if (!resolve_gitlink_ref(path, "HEAD", &oid))
                return add_one_path(NULL, path, len, st);
 
        /* Error out. */
@@ -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))
@@ -679,9 +687,9 @@ static int unresolve_one(const char *path)
 
 static void read_head_pointers(void)
 {
-       if (read_ref("HEAD", head_oid.hash))
+       if (read_ref("HEAD", &head_oid))
                die("No HEAD -- no initial commit yet?");
-       if (read_ref("MERGE_HEAD", merge_head_oid.hash)) {
+       if (read_ref("MERGE_HEAD", &merge_head_oid)) {
                fprintf(stderr, "Not in the middle of a merge.\n");
                exit(0);
        }
@@ -721,7 +729,7 @@ static int do_reupdate(int ac, const char **av,
                       PATHSPEC_PREFER_CWD,
                       prefix, av + 1);
 
-       if (read_ref("HEAD", head_oid.hash))
+       if (read_ref("HEAD", &head_oid))
                /* If there is no HEAD, that means it is an initial
                 * commit.  Update everything in the index.
                 */
@@ -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 6b90c5deadb62600aa08166f500bde24cb800fbf..4b4714b3fd82ca9510b9b241867e497cdae92e76 100644 (file)
@@ -200,7 +200,7 @@ static const char *parse_cmd_update(struct ref_transaction *transaction,
                die("update %s: extra input: %s", refname, next);
 
        if (ref_transaction_update(transaction, refname,
-                                  new_oid.hash, have_old ? old_oid.hash : NULL,
+                                  &new_oid, have_old ? &old_oid : NULL,
                                   update_flags | create_reflog_flag,
                                   msg, &err))
                die("%s", err.buf);
@@ -232,7 +232,7 @@ static const char *parse_cmd_create(struct ref_transaction *transaction,
        if (*next != line_termination)
                die("create %s: extra input: %s", refname, next);
 
-       if (ref_transaction_create(transaction, refname, new_oid.hash,
+       if (ref_transaction_create(transaction, refname, &new_oid,
                                   update_flags | create_reflog_flag,
                                   msg, &err))
                die("%s", err.buf);
@@ -269,7 +269,7 @@ static const char *parse_cmd_delete(struct ref_transaction *transaction,
                die("delete %s: extra input: %s", refname, next);
 
        if (ref_transaction_delete(transaction, refname,
-                                  have_old ? old_oid.hash : NULL,
+                                  have_old ? &old_oid : NULL,
                                   update_flags, msg, &err))
                die("%s", err.buf);
 
@@ -298,7 +298,7 @@ static const char *parse_cmd_verify(struct ref_transaction *transaction,
        if (*next != line_termination)
                die("verify %s: extra input: %s", refname, next);
 
-       if (ref_transaction_verify(transaction, refname, old_oid.hash,
+       if (ref_transaction_verify(transaction, refname, &old_oid,
                                   update_flags, &err))
                die("%s", err.buf);
 
@@ -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,17 +427,17 @@ 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
                 * NULL_SHA1 as "don't care" here:
                 */
                return delete_ref(msg, refname,
-                                 (oldval && !is_null_oid(&oldoid)) ? oldoid.hash : NULL,
+                                 (oldval && !is_null_oid(&oldoid)) ? &oldoid : NULL,
                                  flags);
        else
-               return update_ref(msg, refname, oid.hash, oldval ? oldoid.hash : NULL,
+               return update_ref(msg, refname, &oid, oldval ? &oldoid : NULL,
                                  flags | create_reflog_flag,
                                  UPDATE_REFS_DIE_ON_ERR);
 }
index 7b9307aa588a70a47de4c53a380e165f90924ab9..ed043d5f1c0a107fb0650de826c0e5aea0fe165e 100644 (file)
@@ -410,7 +410,7 @@ static void show_worktree_porcelain(struct worktree *wt)
        if (wt->is_bare)
                printf("bare\n");
        else {
-               printf("HEAD %s\n", sha1_to_hex(wt->head_sha1));
+               printf("HEAD %s\n", oid_to_hex(&wt->head_oid));
                if (wt->is_detached)
                        printf("detached\n");
                else if (wt->head_ref)
@@ -430,7 +430,7 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
                strbuf_addstr(&sb, "(bare)");
        else {
                strbuf_addf(&sb, "%-*s ", abbrev_len,
-                               find_unique_abbrev(wt->head_sha1, DEFAULT_ABBREV));
+                               find_unique_abbrev(wt->head_oid.hash, DEFAULT_ABBREV));
                if (wt->is_detached)
                        strbuf_addstr(&sb, "(detached HEAD)");
                else if (wt->head_ref) {
@@ -455,7 +455,7 @@ static void measure_widths(struct worktree **wt, int *abbrev, int *maxlen)
 
                if (path_len > *maxlen)
                        *maxlen = path_len;
-               sha1_len = strlen(find_unique_abbrev(wt[i]->head_sha1, *abbrev));
+               sha1_len = strlen(find_unique_abbrev(wt[i]->head_oid.hash, *abbrev));
                if (sha1_len > *abbrev)
                        *abbrev = sha1_len;
        }
index c092d5d68f32652c229fe232af72d02111f51de7..93290962c95e6c27732f121f980d03289ac1ea6d 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -338,9 +338,9 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
 
                if (e->item->flags & UNINTERESTING)
                        continue;
-               if (dwim_ref(e->name, strlen(e->name), oid.hash, &ref) != 1)
+               if (dwim_ref(e->name, strlen(e->name), &oid, &ref) != 1)
                        goto skip_write_ref;
-               if (read_ref_full(e->name, RESOLVE_REF_READING, oid.hash, &flag))
+               if (read_ref_full(e->name, RESOLVE_REF_READING, &oid, &flag))
                        flag = 0;
                display_ref = (flag & REF_ISSYMREF) ? e->name : ref;
 
index d3f7401278bd5130558dde18da1235c188bb7303..e03e72c34a5c1fc618994ee63f38875d28d91886 100644 (file)
@@ -602,11 +602,11 @@ static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *pat
 
 int write_index_as_tree(unsigned char *sha1, struct index_state *index_state, const char *index_path, int flags, const char *prefix)
 {
-       int entries, was_valid, newfd;
+       int entries, was_valid;
        struct lock_file lock_file = LOCK_INIT;
        int ret = 0;
 
-       newfd = hold_lock_file_for_update(&lock_file, index_path, LOCK_DIE_ON_ERROR);
+       hold_lock_file_for_update(&lock_file, index_path, LOCK_DIE_ON_ERROR);
 
        entries = read_index_from(index_state, index_path);
        if (entries < 0) {
@@ -625,10 +625,7 @@ int write_index_as_tree(unsigned char *sha1, struct index_state *index_state, co
                        ret = WRITE_TREE_UNMERGED_INDEX;
                        goto out;
                }
-               if (0 <= newfd) {
-                       if (!write_locked_index(index_state, &lock_file, COMMIT_LOCK))
-                               newfd = -1;
-               }
+               write_locked_index(index_state, &lock_file, COMMIT_LOCK);
                /* Not being able to write is fine -- we are only interested
                 * in updating the cache-tree part, and if the next caller
                 * ends up using the old index with unupdated cache-tree part
@@ -650,8 +647,7 @@ int write_index_as_tree(unsigned char *sha1, struct index_state *index_state, co
                hashcpy(sha1, index_state->cache_tree->oid.hash);
 
 out:
-       if (0 <= newfd)
-               rollback_lock_file(&lock_file);
+       rollback_lock_file(&lock_file);
        return ret;
 }
 
diff --git a/cache.h b/cache.h
index 6440e2bf21f5800db98f82fcedff46478188674e..d3e240228c9c488f78ee9be06c9d151f1d2325b2 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -14,6 +14,7 @@
 #include "hash.h"
 #include "path.h"
 #include "sha1-array.h"
+#include "repository.h"
 
 #ifndef platform_SHA_CTX
 /*
@@ -77,6 +78,8 @@ struct object_id {
        unsigned char hash[GIT_MAX_RAWSZ];
 };
 
+#define the_hash_algo the_repository->hash_algo
+
 #if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
 #define DTYPE(de)      ((de)->d_type)
 #else
@@ -204,6 +207,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 +331,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 +350,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;
@@ -446,6 +453,16 @@ static inline enum object_type object_type(unsigned int mode)
 #define GIT_QUARANTINE_ENVIRONMENT "GIT_QUARANTINE_PATH"
 #define GIT_OPTIONAL_LOCKS_ENVIRONMENT "GIT_OPTIONAL_LOCKS"
 
+/*
+ * Environment variable used in handshaking the wire protocol.
+ * Contains a colon ':' separated list of keys with optional values
+ * 'key[=value]'.  Presence of unknown keys and values must be
+ * ignored.
+ */
+#define GIT_PROTOCOL_ENVIRONMENT "GIT_PROTOCOL"
+/* HTTP header used to handshake the wire protocol */
+#define GIT_PROTOCOL_HEADER "Git-Protocol"
+
 /*
  * This environment variable is expected to contain a boolean indicating
  * whether we should or should not treat:
@@ -602,9 +619,28 @@ extern int do_read_index(struct index_state *istate, const char *path,
 extern int read_index_from(struct index_state *, const char *path);
 extern int is_index_unborn(struct index_state *);
 extern int read_index_unmerged(struct index_state *);
+
+/* For use with `write_locked_index()`. */
 #define COMMIT_LOCK            (1 << 0)
-#define CLOSE_LOCK             (1 << 1)
+
+/*
+ * Write the index while holding an already-taken lock. Close the lock,
+ * and if `COMMIT_LOCK` is given, commit it.
+ *
+ * Unless a split index is in use, write the index into the lockfile.
+ *
+ * With a split index, write the shared index to a temporary file,
+ * adjust its permissions and rename it into place, then write the
+ * split index to the lockfile. If the temporary file for the shared
+ * index cannot be created, fall back to the behavior described in
+ * the previous paragraph.
+ *
+ * With `COMMIT_LOCK`, the lock is always committed or rolled back.
+ * Without it, the lock is closed, but neither committed nor rolled
+ * back.
+ */
 extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
+
 extern int discard_index(struct index_state *);
 extern void move_index_extensions(struct index_state *dst, struct index_state *src);
 extern int unmerged_index(const struct index_state *);
@@ -681,11 +717,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);
 
@@ -716,12 +755,17 @@ extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
 extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
 extern struct cache_entry *refresh_cache_entry(struct cache_entry *, unsigned int);
 
+/*
+ * Opportunistically update the index but do not complain if we can't.
+ * The lockfile is always committed or rolled back.
+ */
 extern void update_index_if_able(struct index_state *, struct lock_file *);
 
 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;
@@ -775,6 +819,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
@@ -865,6 +910,7 @@ struct repository_format {
        int version;
        int precious_objects;
        int is_bare;
+       int hash_algo;
        char *work_tree;
        struct string_list unknown_extensions;
 };
@@ -997,22 +1043,22 @@ extern const struct object_id empty_blob_oid;
 
 static inline int is_empty_blob_sha1(const unsigned char *sha1)
 {
-       return !hashcmp(sha1, EMPTY_BLOB_SHA1_BIN);
+       return !hashcmp(sha1, the_hash_algo->empty_blob->hash);
 }
 
 static inline int is_empty_blob_oid(const struct object_id *oid)
 {
-       return !hashcmp(oid->hash, EMPTY_BLOB_SHA1_BIN);
+       return !oidcmp(oid, the_hash_algo->empty_blob);
 }
 
 static inline int is_empty_tree_sha1(const unsigned char *sha1)
 {
-       return !hashcmp(sha1, EMPTY_TREE_SHA1_BIN);
+       return !hashcmp(sha1, the_hash_algo->empty_tree->hash);
 }
 
 static inline int is_empty_tree_oid(const struct object_id *oid)
 {
-       return !hashcmp(oid->hash, EMPTY_TREE_SHA1_BIN);
+       return !oidcmp(oid, the_hash_algo->empty_tree);
 }
 
 /* set default permissions by passing mode arguments to open(2) */
@@ -1317,6 +1363,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 a29246af3580dc9ef7af584cb53a2fc6e084d09a..5bd06fe900d44916fd11875fa5641e9b97e90540 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 b3ed0a0ddac4261307a31a99f3cd18fb7aabc4fc..ac05f1f4694347bbd96fe1911aa33450d43ae24f 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 ff7bdab1a32478c8719ee3b973742456df7dc629..49ab85b76916784d5d5e6d6ef15a9638ef97b59f 100644 (file)
--- a/column.c
+++ b/column.c
@@ -224,7 +224,7 @@ int finalize_colopts(unsigned int *colopts, int stdout_is_tty)
                if (stdout_is_tty < 0)
                        stdout_is_tty = isatty(1);
                *colopts &= ~COL_ENABLE_MASK;
-               if (stdout_is_tty)
+               if (stdout_is_tty || pager_in_use())
                        *colopts |= COL_ENABLED;
        }
        return 0;
index 9e163d5adabd93fd48a6b563c5f31c32740e01c7..2505de119a2be37e9dfb313a2e44db68595ad13f 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 */
@@ -1014,7 +1014,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                        elem->mode = canon_mode(st.st_mode);
                } else if (S_ISDIR(st.st_mode)) {
                        struct object_id oid;
-                       if (resolve_gitlink_ref(elem->path, "HEAD", oid.hash) < 0)
+                       if (resolve_gitlink_ref(elem->path, "HEAD", &oid) < 0)
                                result = grab_blob(&elem->oid, elem->mode,
                                                   &result_size, NULL, NULL);
                        else
@@ -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 1e0e633790bb834ad05ed9619e4afa0bac33ce19..cab8d4455bdbd6e4b87031613300be1112a19df5 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 6d769590f285e534d20bfd8108e8e980253f8815..99a3fea68d3f63064351ccabeaf7f11cdf8e0d04 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 7d063e9e40a213ce378d90377a9ac2b1f04ddbe3..5078ce5eccfaf17620c21e0a7f7133b3badef3d3 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 8b6fa0db446aee9888ff6484560131143c7e22e5..2d44d21aca8d31f67b16cb9a90245b4db526ff76 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 e276ccd7b38faf3d96778c300ebca0d0a22fe410..4d1d95beeb509b3bb159b4eaadf20deeb498e73a 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 ceb4bdbcdd6ed583ab3b7c9cc5d9d2783128dee6..6bc24b76445686b2ef55fe5461273e4c5b2cd4f7 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 ae03b74a6f4e9c8ff6d519d9a784ab6b84179872..7ed3fbbea13c994467280ed3e981ae9f37f15c09 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 b7aa59d973db33e049dc8492df45f2a68d43e037..cd1995292a536f33eb1bf698eaddd761b34d05bd 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 d8bde06f1a3dbdc0af02ef6d63f8e18d39989dc3..51cd60baa37adbeef788098482d804c58cb9979f 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 5cb23e5d5912ac40473643fff0393ac01284df41..f3e03a9eabeeef48abb401ec14b824261b590fa0 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 61c96838721501031935dd560eb86d8dbcedd2b0..4d81358a83d0a4a4086c6ecfc7d84988eba860e9 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 98342b83163f2b4f1221891995c6484246a45977..59bf151336c22d65ae3470ba3ded73d99f7130fe 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 4184d7f5a62b5d9c8a9e744136e05a53efc99233..3ee8aae59d7978449b35b6147dce8c3013953c3b 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 6f2b48a78bc3d8578a3a1a1e8fc28757ef2ea78b..1b5d89fd5ed1a2c143bc6dafdf027b1f5f2cf790 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 adb7d7a3e5ee164d24f1d2420677a8ad5b61e49f..e617c2018d22b6d389548a3573d2deb2ec608b99 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;
@@ -2751,7 +2775,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
 {
        int ret = 0, remove = 0;
        char *filename_buf = NULL;
-       struct lock_file *lock;
+       struct lock_file lock = LOCK_INIT;
        int out_fd;
        char buf[1024];
        FILE *config_file = NULL;
@@ -2766,8 +2790,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
        if (!config_filename)
                config_filename = filename_buf = git_pathdup("config");
 
-       lock = xcalloc(1, sizeof(struct lock_file));
-       out_fd = hold_lock_file_for_update(lock, config_filename, 0);
+       out_fd = hold_lock_file_for_update(&lock, config_filename, 0);
        if (out_fd < 0) {
                ret = error("could not lock config file %s", config_filename);
                goto out;
@@ -2786,9 +2809,9 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
                goto out;
        }
 
-       if (chmod(get_lock_file_path(lock), st.st_mode & 07777) < 0) {
+       if (chmod(get_lock_file_path(&lock), st.st_mode & 07777) < 0) {
                ret = error_errno("chmod on %s failed",
-                                 get_lock_file_path(lock));
+                                 get_lock_file_path(&lock));
                goto out;
        }
 
@@ -2811,8 +2834,8 @@ 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) {
-                                       ret = write_error(get_lock_file_path(lock));
+                               if (write_in_full(out_fd, copystr.buf, copystr.len) < 0) {
+                                       ret = write_error(get_lock_file_path(&lock));
                                        goto out;
                                }
                                strbuf_reset(&copystr);
@@ -2828,7 +2851,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
                                store.baselen = strlen(new_name);
                                if (!copy) {
                                        if (write_section(out_fd, new_name) < 0) {
-                                               ret = write_error(get_lock_file_path(lock));
+                                               ret = write_error(get_lock_file_path(&lock));
                                                goto out;
                                        }
                                        /*
@@ -2862,7 +2885,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
                }
 
                if (write_in_full(out_fd, output, length) < 0) {
-                       ret = write_error(get_lock_file_path(lock));
+                       ret = write_error(get_lock_file_path(&lock));
                        goto out;
                }
        }
@@ -2873,8 +2896,8 @@ 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) {
-                       ret = write_error(get_lock_file_path(lock));
+               if (write_in_full(out_fd, copystr.buf, copystr.len) < 0) {
+                       ret = write_error(get_lock_file_path(&lock));
                        goto out;
                }
                strbuf_reset(&copystr);
@@ -2883,13 +2906,13 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
        fclose(config_file);
        config_file = NULL;
 commit_and_out:
-       if (commit_lock_file(lock) < 0)
+       if (commit_lock_file(&lock) < 0)
                ret = error_errno("could not write config file %s",
                                  config_filename);
 out:
        if (config_file)
                fclose(config_file);
-       rollback_lock_file(lock);
+       rollback_lock_file(&lock);
 out_no_rollback:
        free(filename_buf);
        return ret;
index a49d26441622508fd3c6560ac9784e84541c10dc..ef70a9cac1e6dc67df24d157a4e5de38edd8c984 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 df56c0cbff4dfb1263917f6147bd7e1912418dd6..c3a014c5babf72ee4c0d135fec264afb37b040de 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -11,6 +11,8 @@
 #include "string-list.h"
 #include "sha1-array.h"
 #include "transport.h"
+#include "strbuf.h"
+#include "protocol.h"
 
 static char *server_capabilities;
 static const char *parse_feature_value(const char *, const char *, int *);
@@ -107,6 +109,118 @@ static void annotate_refs_with_symref_info(struct ref *ref)
        string_list_clear(&symref, 0);
 }
 
+/*
+ * Read one line of a server's ref advertisement into packet_buffer.
+ */
+static int read_remote_ref(int in, char **src_buf, size_t *src_len,
+                          int *responded)
+{
+       int len = packet_read(in, src_buf, src_len,
+                             packet_buffer, sizeof(packet_buffer),
+                             PACKET_READ_GENTLE_ON_EOF |
+                             PACKET_READ_CHOMP_NEWLINE);
+       const char *arg;
+       if (len < 0)
+               die_initial_contact(*responded);
+       if (len > 4 && skip_prefix(packet_buffer, "ERR ", &arg))
+               die("remote error: %s", arg);
+
+       *responded = 1;
+
+       return len;
+}
+
+#define EXPECTING_PROTOCOL_VERSION 0
+#define EXPECTING_FIRST_REF 1
+#define EXPECTING_REF 2
+#define EXPECTING_SHALLOW 3
+
+/* Returns 1 if packet_buffer is a protocol version pkt-line, 0 otherwise. */
+static int process_protocol_version(void)
+{
+       switch (determine_protocol_version_client(packet_buffer)) {
+       case protocol_v1:
+               return 1;
+       case protocol_v0:
+               return 0;
+       default:
+               die("server is speaking an unknown protocol");
+       }
+}
+
+static void process_capabilities(int *len)
+{
+       int nul_location = strlen(packet_buffer);
+       if (nul_location == *len)
+               return;
+       server_capabilities = xstrdup(packet_buffer + nul_location + 1);
+       *len = nul_location;
+}
+
+static int process_dummy_ref(void)
+{
+       struct object_id oid;
+       const char *name;
+
+       if (parse_oid_hex(packet_buffer, &oid, &name))
+               return 0;
+       if (*name != ' ')
+               return 0;
+       name++;
+
+       return !oidcmp(&null_oid, &oid) && !strcmp(name, "capabilities^{}");
+}
+
+static void check_no_capabilities(int len)
+{
+       if (strlen(packet_buffer) != len)
+               warning("Ignoring capabilities after first line '%s'",
+                       packet_buffer + strlen(packet_buffer));
+}
+
+static int process_ref(int len, struct ref ***list, unsigned int flags,
+                      struct oid_array *extra_have)
+{
+       struct object_id old_oid;
+       const char *name;
+
+       if (parse_oid_hex(packet_buffer, &old_oid, &name))
+               return 0;
+       if (*name != ' ')
+               return 0;
+       name++;
+
+       if (extra_have && !strcmp(name, ".have")) {
+               oid_array_append(extra_have, &old_oid);
+       } else if (!strcmp(name, "capabilities^{}")) {
+               die("protocol error: unexpected capabilities^{}");
+       } else if (check_ref(name, flags)) {
+               struct ref *ref = alloc_ref(name);
+               oidcpy(&ref->old_oid, &old_oid);
+               **list = ref;
+               *list = &ref->next;
+       }
+       check_no_capabilities(len);
+       return 1;
+}
+
+static int process_shallow(int len, struct oid_array *shallow_points)
+{
+       const char *arg;
+       struct object_id old_oid;
+
+       if (!skip_prefix(packet_buffer, "shallow ", &arg))
+               return 0;
+
+       if (get_oid_hex(arg, &old_oid))
+               die("protocol error: expected shallow sha-1, got '%s'", arg);
+       if (!shallow_points)
+               die("repository on the other end cannot be shallow");
+       oid_array_append(shallow_points, &old_oid);
+       check_no_capabilities(len);
+       return 1;
+}
+
 /*
  * Read all the refs from the other end
  */
@@ -123,76 +237,41 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
         * willing to talk to us.  A hang-up before seeing any
         * response does not necessarily mean an ACL problem, though.
         */
-       int saw_response;
-       int got_dummy_ref_with_capabilities_declaration = 0;
+       int responded = 0;
+       int len;
+       int state = EXPECTING_PROTOCOL_VERSION;
 
        *list = NULL;
-       for (saw_response = 0; ; saw_response = 1) {
-               struct ref *ref;
-               struct object_id old_oid;
-               char *name;
-               int len, name_len;
-               char *buffer = packet_buffer;
-               const char *arg;
-
-               len = packet_read(in, &src_buf, &src_len,
-                                 packet_buffer, sizeof(packet_buffer),
-                                 PACKET_READ_GENTLE_ON_EOF |
-                                 PACKET_READ_CHOMP_NEWLINE);
-               if (len < 0)
-                       die_initial_contact(saw_response);
-
-               if (!len)
-                       break;
-
-               if (len > 4 && skip_prefix(buffer, "ERR ", &arg))
-                       die("remote error: %s", arg);
-
-               if (len == GIT_SHA1_HEXSZ + strlen("shallow ") &&
-                       skip_prefix(buffer, "shallow ", &arg)) {
-                       if (get_oid_hex(arg, &old_oid))
-                               die("protocol error: expected shallow sha-1, got '%s'", arg);
-                       if (!shallow_points)
-                               die("repository on the other end cannot be shallow");
-                       oid_array_append(shallow_points, &old_oid);
-                       continue;
-               }
-
-               if (len < GIT_SHA1_HEXSZ + 2 || get_oid_hex(buffer, &old_oid) ||
-                       buffer[GIT_SHA1_HEXSZ] != ' ')
-                       die("protocol error: expected sha/ref, got '%s'", buffer);
-               name = buffer + GIT_SHA1_HEXSZ + 1;
-
-               name_len = strlen(name);
-               if (len != name_len + GIT_SHA1_HEXSZ + 1) {
-                       free(server_capabilities);
-                       server_capabilities = xstrdup(name + name_len + 1);
-               }
 
-               if (extra_have && !strcmp(name, ".have")) {
-                       oid_array_append(extra_have, &old_oid);
-                       continue;
-               }
-
-               if (!strcmp(name, "capabilities^{}")) {
-                       if (saw_response)
-                               die("protocol error: unexpected capabilities^{}");
-                       if (got_dummy_ref_with_capabilities_declaration)
-                               die("protocol error: multiple capabilities^{}");
-                       got_dummy_ref_with_capabilities_declaration = 1;
-                       continue;
+       while ((len = read_remote_ref(in, &src_buf, &src_len, &responded))) {
+               switch (state) {
+               case EXPECTING_PROTOCOL_VERSION:
+                       if (process_protocol_version()) {
+                               state = EXPECTING_FIRST_REF;
+                               break;
+                       }
+                       state = EXPECTING_FIRST_REF;
+                       /* fallthrough */
+               case EXPECTING_FIRST_REF:
+                       process_capabilities(&len);
+                       if (process_dummy_ref()) {
+                               state = EXPECTING_SHALLOW;
+                               break;
+                       }
+                       state = EXPECTING_REF;
+                       /* fallthrough */
+               case EXPECTING_REF:
+                       if (process_ref(len, &list, flags, extra_have))
+                               break;
+                       state = EXPECTING_SHALLOW;
+                       /* fallthrough */
+               case EXPECTING_SHALLOW:
+                       if (process_shallow(len, shallow_points))
+                               break;
+                       die("protocol error: unexpected '%s'", packet_buffer);
+               default:
+                       die("unexpected state %d", state);
                }
-
-               if (!check_ref(name, flags))
-                       continue;
-
-               if (got_dummy_ref_with_capabilities_declaration)
-                       die("protocol error: unexpected ref after capabilities^{}");
-
-               ref = alloc_ref(buffer + GIT_SHA1_HEXSZ + 1);
-               oidcpy(&ref->old_oid, &old_oid);
-               *list = ref;
-               list = &ref->next;
        }
 
        annotate_refs_with_symref_info(*orig_list);
@@ -503,12 +582,25 @@ static int git_tcp_connect_sock(char *host, int flags)
 #endif /* NO_IPV6 */
 
 
-static void git_tcp_connect(int fd[2], char *host, int flags)
+/*
+ * Dummy child_process returned by git_connect() if the transport protocol
+ * does not need fork(2).
+ */
+static struct child_process no_fork = CHILD_PROCESS_INIT;
+
+int git_connection_is_socket(struct child_process *conn)
+{
+       return conn == &no_fork;
+}
+
+static struct child_process *git_tcp_connect(int fd[2], char *host, int flags)
 {
        int sockfd = git_tcp_connect_sock(host, flags);
 
        fd[0] = sockfd;
        fd[1] = dup(sockfd);
+
+       return &no_fork;
 }
 
 
@@ -682,8 +774,6 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
        return protocol;
 }
 
-static struct child_process no_fork = CHILD_PROCESS_INIT;
-
 static const char *get_ssh_command(void)
 {
        const char *ssh;
@@ -697,37 +787,47 @@ static const char *get_ssh_command(void)
        return NULL;
 }
 
-static int override_ssh_variant(int *port_option, int *needs_batch)
+enum ssh_variant {
+       VARIANT_AUTO,
+       VARIANT_SIMPLE,
+       VARIANT_SSH,
+       VARIANT_PLINK,
+       VARIANT_PUTTY,
+       VARIANT_TORTOISEPLINK,
+};
+
+static void override_ssh_variant(enum ssh_variant *ssh_variant)
 {
-       char *variant;
+       const char *variant = getenv("GIT_SSH_VARIANT");
 
-       variant = xstrdup_or_null(getenv("GIT_SSH_VARIANT"));
-       if (!variant &&
-           git_config_get_string("ssh.variant", &variant))
-               return 0;
+       if (!variant && git_config_get_string_const("ssh.variant", &variant))
+               return;
 
-       if (!strcmp(variant, "plink") || !strcmp(variant, "putty")) {
-               *port_option = 'P';
-               *needs_batch = 0;
-       } else if (!strcmp(variant, "tortoiseplink")) {
-               *port_option = 'P';
-               *needs_batch = 1;
-       } else {
-               *port_option = 'p';
-               *needs_batch = 0;
-       }
-       free(variant);
-       return 1;
+       if (!strcmp(variant, "auto"))
+               *ssh_variant = VARIANT_AUTO;
+       else if (!strcmp(variant, "plink"))
+               *ssh_variant = VARIANT_PLINK;
+       else if (!strcmp(variant, "putty"))
+               *ssh_variant = VARIANT_PUTTY;
+       else if (!strcmp(variant, "tortoiseplink"))
+               *ssh_variant = VARIANT_TORTOISEPLINK;
+       else if (!strcmp(variant, "simple"))
+               *ssh_variant = VARIANT_SIMPLE;
+       else
+               *ssh_variant = VARIANT_SSH;
 }
 
-static void handle_ssh_variant(const char *ssh_command, int is_cmdline,
-                              int *port_option, int *needs_batch)
+static enum ssh_variant determine_ssh_variant(const char *ssh_command,
+                                             int is_cmdline)
 {
+       enum ssh_variant ssh_variant = VARIANT_AUTO;
        const char *variant;
        char *p = NULL;
 
-       if (override_ssh_variant(port_option, needs_batch))
-               return;
+       override_ssh_variant(&ssh_variant);
+
+       if (ssh_variant != VARIANT_AUTO)
+               return ssh_variant;
 
        if (!is_cmdline) {
                p = xstrdup(ssh_command);
@@ -746,27 +846,200 @@ static void handle_ssh_variant(const char *ssh_command, int is_cmdline,
                        free(ssh_argv);
                } else {
                        free(p);
-                       return;
+                       return ssh_variant;
                }
        }
 
-       if (!strcasecmp(variant, "plink") ||
-           !strcasecmp(variant, "plink.exe"))
-               *port_option = 'P';
+       if (!strcasecmp(variant, "ssh") ||
+           !strcasecmp(variant, "ssh.exe"))
+               ssh_variant = VARIANT_SSH;
+       else if (!strcasecmp(variant, "plink") ||
+                !strcasecmp(variant, "plink.exe"))
+               ssh_variant = VARIANT_PLINK;
        else if (!strcasecmp(variant, "tortoiseplink") ||
-                !strcasecmp(variant, "tortoiseplink.exe")) {
-               *port_option = 'P';
-               *needs_batch = 1;
-       }
+                !strcasecmp(variant, "tortoiseplink.exe"))
+               ssh_variant = VARIANT_TORTOISEPLINK;
+
        free(p);
+       return ssh_variant;
+}
+
+/*
+ * Open a connection using Git's native protocol.
+ *
+ * The caller is responsible for freeing hostandport, but this function may
+ * modify it (for example, to truncate it to remove the port part).
+ */
+static struct child_process *git_connect_git(int fd[2], char *hostandport,
+                                            const char *path, const char *prog,
+                                            int flags)
+{
+       struct child_process *conn;
+       struct strbuf request = STRBUF_INIT;
+       /*
+        * Set up virtual host information based on where we will
+        * connect, unless the user has overridden us in
+        * the environment.
+        */
+       char *target_host = getenv("GIT_OVERRIDE_VIRTUAL_HOST");
+       if (target_host)
+               target_host = xstrdup(target_host);
+       else
+               target_host = xstrdup(hostandport);
+
+       transport_check_allowed("git");
+
+       /*
+        * These underlying connection commands die() if they
+        * cannot connect.
+        */
+       if (git_use_proxy(hostandport))
+               conn = git_proxy_connect(fd, hostandport);
+       else
+               conn = git_tcp_connect(fd, hostandport, flags);
+       /*
+        * Separate original protocol components prog and path
+        * from extended host header with a NUL byte.
+        *
+        * Note: Do not add any other headers here!  Doing so
+        * will cause older git-daemon servers to crash.
+        */
+       strbuf_addf(&request,
+                   "%s %s%chost=%s%c",
+                   prog, path, 0,
+                   target_host, 0);
+
+       /* If using a new version put that stuff here after a second null byte */
+       if (get_protocol_version_config() > 0) {
+               strbuf_addch(&request, '\0');
+               strbuf_addf(&request, "version=%d%c",
+                           get_protocol_version_config(), '\0');
+       }
+
+       packet_write(fd[1], request.buf, request.len);
+
+       free(target_host);
+       strbuf_release(&request);
+       return conn;
+}
+
+/*
+ * Append the appropriate environment variables to `env` and options to
+ * `args` for running ssh in Git's SSH-tunneled transport.
+ */
+static void push_ssh_options(struct argv_array *args, struct argv_array *env,
+                            enum ssh_variant variant, const char *port,
+                            int flags)
+{
+       if (variant == VARIANT_SSH &&
+           get_protocol_version_config() > 0) {
+               argv_array_push(args, "-o");
+               argv_array_push(args, "SendEnv=" GIT_PROTOCOL_ENVIRONMENT);
+               argv_array_pushf(env, GIT_PROTOCOL_ENVIRONMENT "=version=%d",
+                                get_protocol_version_config());
+       }
+
+       if (flags & CONNECT_IPV4) {
+               switch (variant) {
+               case VARIANT_AUTO:
+                       BUG("VARIANT_AUTO passed to push_ssh_options");
+               case VARIANT_SIMPLE:
+                       die("ssh variant 'simple' does not support -4");
+               case VARIANT_SSH:
+               case VARIANT_PLINK:
+               case VARIANT_PUTTY:
+               case VARIANT_TORTOISEPLINK:
+                       argv_array_push(args, "-4");
+               }
+       } else if (flags & CONNECT_IPV6) {
+               switch (variant) {
+               case VARIANT_AUTO:
+                       BUG("VARIANT_AUTO passed to push_ssh_options");
+               case VARIANT_SIMPLE:
+                       die("ssh variant 'simple' does not support -6");
+               case VARIANT_SSH:
+               case VARIANT_PLINK:
+               case VARIANT_PUTTY:
+               case VARIANT_TORTOISEPLINK:
+                       argv_array_push(args, "-6");
+               }
+       }
+
+       if (variant == VARIANT_TORTOISEPLINK)
+               argv_array_push(args, "-batch");
+
+       if (port) {
+               switch (variant) {
+               case VARIANT_AUTO:
+                       BUG("VARIANT_AUTO passed to push_ssh_options");
+               case VARIANT_SIMPLE:
+                       die("ssh variant 'simple' does not support setting port");
+               case VARIANT_SSH:
+                       argv_array_push(args, "-p");
+                       break;
+               case VARIANT_PLINK:
+               case VARIANT_PUTTY:
+               case VARIANT_TORTOISEPLINK:
+                       argv_array_push(args, "-P");
+               }
+
+               argv_array_push(args, port);
+       }
+}
+
+/* Prepare a child_process for use by Git's SSH-tunneled transport. */
+static void fill_ssh_args(struct child_process *conn, const char *ssh_host,
+                         const char *port, int flags)
+{
+       const char *ssh;
+       enum ssh_variant variant;
+
+       if (looks_like_command_line_option(ssh_host))
+               die("strange hostname '%s' blocked", ssh_host);
+
+       ssh = get_ssh_command();
+       if (ssh) {
+               variant = determine_ssh_variant(ssh, 1);
+       } else {
+               /*
+                * GIT_SSH is the no-shell version of
+                * GIT_SSH_COMMAND (and must remain so for
+                * historical compatibility).
+                */
+               conn->use_shell = 0;
+
+               ssh = getenv("GIT_SSH");
+               if (!ssh)
+                       ssh = "ssh";
+               variant = determine_ssh_variant(ssh, 0);
+       }
+
+       if (variant == VARIANT_AUTO) {
+               struct child_process detect = CHILD_PROCESS_INIT;
+
+               detect.use_shell = conn->use_shell;
+               detect.no_stdin = detect.no_stdout = detect.no_stderr = 1;
+
+               argv_array_push(&detect.args, ssh);
+               argv_array_push(&detect.args, "-G");
+               push_ssh_options(&detect.args, &detect.env_array,
+                                VARIANT_SSH, port, flags);
+               argv_array_push(&detect.args, ssh_host);
+
+               variant = run_command(&detect) ? VARIANT_SIMPLE : VARIANT_SSH;
+       }
+
+       argv_array_push(&conn->args, ssh);
+       push_ssh_options(&conn->args, &conn->env_array, variant, port, flags);
+       argv_array_push(&conn->args, ssh_host);
 }
 
 /*
- * This returns a dummy child_process if the transport protocol does not
- * need fork(2), or a struct child_process object if it does.  Once done,
- * finish the connection with finish_connect() with the value returned from
- * this function (it is safe to call finish_connect() with NULL to support
- * the former case).
+ * This returns the dummy child_process `no_fork` if the transport protocol
+ * does not need fork(2), or a struct child_process object if it does.  Once
+ * done, finish the connection with finish_connect() with the value returned
+ * from this function (it is safe to call finish_connect() with NULL to
+ * support the former case).
  *
  * If it returns, the connect is successful; it just dies on errors (this
  * will hopefully be changed in a libification effort, to return NULL when
@@ -776,7 +1049,7 @@ struct child_process *git_connect(int fd[2], const char *url,
                                  const char *prog, int flags)
 {
        char *hostandport, *path;
-       struct child_process *conn = &no_fork;
+       struct child_process *conn;
        enum protocol protocol;
 
        /* Without this we cannot rely on waitpid() to tell
@@ -792,40 +1065,10 @@ struct child_process *git_connect(int fd[2], const char *url,
                printf("Diag: path=%s\n", path ? path : "NULL");
                conn = NULL;
        } else if (protocol == PROTO_GIT) {
-               /*
-                * Set up virtual host information based on where we will
-                * connect, unless the user has overridden us in
-                * the environment.
-                */
-               char *target_host = getenv("GIT_OVERRIDE_VIRTUAL_HOST");
-               if (target_host)
-                       target_host = xstrdup(target_host);
-               else
-                       target_host = xstrdup(hostandport);
-
-               transport_check_allowed("git");
-
-               /* These underlying connection commands die() if they
-                * cannot connect.
-                */
-               if (git_use_proxy(hostandport))
-                       conn = git_proxy_connect(fd, hostandport);
-               else
-                       git_tcp_connect(fd, hostandport, flags);
-               /*
-                * Separate original protocol components prog and path
-                * from extended host header with a NUL byte.
-                *
-                * Note: Do not add any other headers here!  Doing so
-                * will cause older git-daemon servers to crash.
-                */
-               packet_write_fmt(fd[1],
-                            "%s %s%chost=%s%c",
-                            prog, path, 0,
-                            target_host, 0);
-               free(target_host);
+               conn = git_connect_git(fd, hostandport, path, prog, flags);
        } else {
                struct strbuf cmd = STRBUF_INIT;
+               const char *const *var;
 
                conn = xmalloc(sizeof(*conn));
                child_process_init(conn);
@@ -838,13 +1081,12 @@ struct child_process *git_connect(int fd[2], const char *url,
                sq_quote_buf(&cmd, path);
 
                /* remove repo-local variables from the environment */
-               conn->env = local_repo_env;
+               for (var = local_repo_env; *var; var++)
+                       argv_array_push(&conn->env_array, *var);
+
                conn->use_shell = 1;
                conn->in = conn->out = -1;
                if (protocol == PROTO_SSH) {
-                       const char *ssh;
-                       int needs_batch = 0;
-                       int port_option = 'p';
                        char *ssh_host = hostandport;
                        const char *port = NULL;
                        transport_check_allowed("ssh");
@@ -866,46 +1108,13 @@ struct child_process *git_connect(int fd[2], const char *url,
                                strbuf_release(&cmd);
                                return NULL;
                        }
-
-                       if (looks_like_command_line_option(ssh_host))
-                               die("strange hostname '%s' blocked", ssh_host);
-
-                       ssh = get_ssh_command();
-                       if (ssh)
-                               handle_ssh_variant(ssh, 1, &port_option,
-                                                  &needs_batch);
-                       else {
-                               /*
-                                * GIT_SSH is the no-shell version of
-                                * GIT_SSH_COMMAND (and must remain so for
-                                * historical compatibility).
-                                */
-                               conn->use_shell = 0;
-
-                               ssh = getenv("GIT_SSH");
-                               if (!ssh)
-                                       ssh = "ssh";
-                               else
-                                       handle_ssh_variant(ssh, 0,
-                                                          &port_option,
-                                                          &needs_batch);
-                       }
-
-                       argv_array_push(&conn->args, ssh);
-                       if (flags & CONNECT_IPV4)
-                               argv_array_push(&conn->args, "-4");
-                       else if (flags & CONNECT_IPV6)
-                               argv_array_push(&conn->args, "-6");
-                       if (needs_batch)
-                               argv_array_push(&conn->args, "-batch");
-                       if (port) {
-                               argv_array_pushf(&conn->args,
-                                                "-%c", port_option);
-                               argv_array_push(&conn->args, port);
-                       }
-                       argv_array_push(&conn->args, ssh_host);
+                       fill_ssh_args(conn, ssh_host, port, flags);
                } else {
                        transport_check_allowed("file");
+                       if (get_protocol_version_config() > 0) {
+                               argv_array_pushf(&conn->env_array, GIT_PROTOCOL_ENVIRONMENT "=version=%d",
+                                                get_protocol_version_config());
+                       }
                }
                argv_array_push(&conn->args, cmd.buf);
 
@@ -921,11 +1130,6 @@ struct child_process *git_connect(int fd[2], const char *url,
        return conn;
 }
 
-int git_connection_is_socket(struct child_process *conn)
-{
-       return conn == &no_fork;
-}
-
 int finish_connect(struct child_process *conn)
 {
        int code;
index f416b05051f3b794c9be74e9baaf3b84e18ff6a2..4a47f332706a332174610ae275b3bcb5afd30f2f 100644 (file)
  *
  * Returns 0 if everything is connected, non-zero otherwise.
  */
-int check_connected(sha1_iterate_fn fn, void *cb_data,
+int check_connected(oid_iterate_fn fn, void *cb_data,
                    struct check_connected_options *opt)
 {
        struct child_process rev_list = CHILD_PROCESS_INIT;
        struct check_connected_options defaults = CHECK_CONNECTED_INIT;
-       char commit[41];
-       unsigned char sha1[20];
+       char commit[GIT_MAX_HEXSZ + 1];
+       struct object_id oid;
        int err = 0;
        struct packed_git *new_pack = NULL;
        struct transport *transport;
@@ -32,7 +32,7 @@ int check_connected(sha1_iterate_fn fn, void *cb_data,
                opt = &defaults;
        transport = opt->transport;
 
-       if (fn(cb_data, sha1)) {
+       if (fn(cb_data, &oid)) {
                if (opt->err_fd)
                        close(opt->err_fd);
                return err;
@@ -77,7 +77,7 @@ int check_connected(sha1_iterate_fn fn, void *cb_data,
 
        sigchain_push(SIGPIPE, SIG_IGN);
 
-       commit[40] = '\n';
+       commit[GIT_SHA1_HEXSZ] = '\n';
        do {
                /*
                 * If index-pack already checked that:
@@ -87,17 +87,17 @@ int check_connected(sha1_iterate_fn fn, void *cb_data,
                 * are sure the ref is good and not sending it to
                 * rev-list for verification.
                 */
-               if (new_pack && find_pack_entry_one(sha1, new_pack))
+               if (new_pack && find_pack_entry_one(oid.hash, new_pack))
                        continue;
 
-               memcpy(commit, sha1_to_hex(sha1), 40);
-               if (write_in_full(rev_list.in, commit, 41) < 0) {
+               memcpy(commit, oid_to_hex(&oid), GIT_SHA1_HEXSZ);
+               if (write_in_full(rev_list.in, commit, GIT_SHA1_HEXSZ + 1) < 0) {
                        if (errno != EPIPE && errno != EINVAL)
                                error_errno(_("failed write to rev-list"));
                        err = -1;
                        break;
                }
-       } while (!fn(cb_data, sha1));
+       } while (!fn(cb_data, &oid));
 
        if (close(rev_list.in))
                err = error_errno(_("failed to close rev-list's stdin"));
index 4ca325f79dc5ee45823834f71f66a6d3b278c3e5..a53f03a61aca4871be5ab75db9bf63ee895668e1 100644 (file)
@@ -8,7 +8,7 @@ struct transport;
  * When called after returning the name for the last object, return -1
  * to signal EOF, otherwise return 0.
  */
-typedef int (*sha1_iterate_fn)(void *, unsigned char [20]);
+typedef int (*oid_iterate_fn)(void *, struct object_id *oid);
 
 /*
  * Named-arguments struct for check_connected. All arguments are
@@ -51,7 +51,7 @@ struct check_connected_options {
  *
  * If "opt" is NULL, behaves as if CHECK_CONNECTED_INIT was passed.
  */
-int check_connected(sha1_iterate_fn fn, void *cb_data,
+int check_connected(oid_iterate_fn fn, void *cb_data,
                    struct check_connected_options *opt);
 
 #endif /* CONNECTED_H */
index 0e16f017a4144220043d27e0c1556a6d1edbdf01..3683c772c5586ccc1f6dfc8c047919d8d96aa891 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
                        "
                ;;
@@ -1250,7 +1249,8 @@ _git_checkout ()
        --*)
                __gitcomp "
                        --quiet --ours --theirs --track --no-track --merge
-                       --conflict= --orphan --patch
+                       --conflict= --orphan --patch --detach --ignore-skip-worktree-bits
+                       --recurse-submodules --no-recurse-submodules
                        "
                ;;
        *)
@@ -1400,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
@@ -1922,6 +1922,7 @@ _git_pull ()
        --*)
                __gitcomp "
                        --rebase --no-rebase
+                       --autostash --no-autostash
                        $__git_merge_options
                        $__git_fetch_options
                "
@@ -2350,6 +2351,7 @@ _git_config ()
                advice.rmHints
                advice.statusHints
                advice.statusUoption
+               advice.ignoredHook
                alias.
                am.keepcr
                am.threeWay
@@ -2640,6 +2642,7 @@ _git_config ()
                sendemail.suppressfrom
                sendemail.thread
                sendemail.to
+               sendemail.tocmd
                sendemail.validate
                sendemail.smtpbatchsize
                sendemail.smtprelogindelay
index 2a317fca442a51dd6f47bfeeaa19bf32ed6e670f..d389bfadceeb26c6d82c05a9c0eac27b67ad8549 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 4c56979d8a08baf5ecfe5922a3239b8e183ba9c0..e6598b63833963115d43c62d9dd794bc03e120f1 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 006134043a472edd54720d6d4237e9484476d3a3..86518cd93d9c58d8b9f02d7cb8a67adf7b3fb3b6 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 e671f6c1c62956e34c935b24da6dfc617230ce61..510e0f710374cfb06d5875108acdad9e877eaf2d 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 5ffc506f6d7e3e1b4c0a0919b1d2e9beb141444f..97919f2d73a73d06bf4e23831fddd473ed75022d 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 4dec1f18e425bd755052ca5645651eec47821cc0..a16f79cfdc46271723d8aa5cd3e685b62845c047 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 225e3f09547d07563a8da34b21677dce514659fa..4484bda41065af6f6e23c667a7bab59a8a1a61cc 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 427f206a45b326f2ee80af0fae7080d5f794c14a..80ab0590bc92221e2b2f8f35b76f5a0cbb970f8b 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 60dec86d37efbffb557e611cde4bf2b2dbfbf478..de3f81667ed9725cb13f2fbc2d76be099f97155e 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 d13c4dfa7d746a827879bb0445aff33de4541e6a..917d9e2d3222c12f2d0dc043a1434acae2b398ae 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 e7f857c1a2882c7c701af3e2ce6c3223bfd88c7c..af9cbc9d0f7bc296ec728c3b965c26a057c8cf72 100755 (executable)
 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 30747075f0c45942220f63202f8c984e3e0956a7..e37e343d0aec37fd74e7bdf694b99f59d022690a 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -282,7 +282,7 @@ static const char *path_ok(const char *directory, struct hostinfo *hi)
        return NULL;            /* Fallthrough. Deny by default */
 }
 
-typedef int (*daemon_service_fn)(void);
+typedef int (*daemon_service_fn)(const struct argv_array *env);
 struct daemon_service {
        const char *name;
        const char *config_name;
@@ -363,7 +363,7 @@ static int run_access_hook(struct daemon_service *service, const char *dir,
 }
 
 static int run_service(const char *dir, struct daemon_service *service,
-                      struct hostinfo *hi)
+                      struct hostinfo *hi, const struct argv_array *env)
 {
        const char *path;
        int enabled = service->enabled;
@@ -422,7 +422,7 @@ static int run_service(const char *dir, struct daemon_service *service,
         */
        signal(SIGTERM, SIG_IGN);
 
-       return service->fn();
+       return service->fn(env);
 }
 
 static void copy_to_log(int fd)
@@ -462,25 +462,34 @@ static int run_service_command(struct child_process *cld)
        return finish_command(cld);
 }
 
-static int upload_pack(void)
+static int upload_pack(const struct argv_array *env)
 {
        struct child_process cld = CHILD_PROCESS_INIT;
        argv_array_pushl(&cld.args, "upload-pack", "--strict", NULL);
        argv_array_pushf(&cld.args, "--timeout=%u", timeout);
+
+       argv_array_pushv(&cld.env_array, env->argv);
+
        return run_service_command(&cld);
 }
 
-static int upload_archive(void)
+static int upload_archive(const struct argv_array *env)
 {
        struct child_process cld = CHILD_PROCESS_INIT;
        argv_array_push(&cld.args, "upload-archive");
+
+       argv_array_pushv(&cld.env_array, env->argv);
+
        return run_service_command(&cld);
 }
 
-static int receive_pack(void)
+static int receive_pack(const struct argv_array *env)
 {
        struct child_process cld = CHILD_PROCESS_INIT;
        argv_array_push(&cld.args, "receive-pack");
+
+       argv_array_pushv(&cld.env_array, env->argv);
+
        return run_service_command(&cld);
 }
 
@@ -573,8 +582,11 @@ static void canonicalize_client(struct strbuf *out, const char *in)
 
 /*
  * Read the host as supplied by the client connection.
+ *
+ * Returns a pointer to the character after the NUL byte terminating the host
+ * arguemnt, or 'extra_args' if there is no host arguemnt.
  */
-static void parse_host_arg(struct hostinfo *hi, char *extra_args, int buflen)
+static char *parse_host_arg(struct hostinfo *hi, char *extra_args, int buflen)
 {
        char *val;
        int vallen;
@@ -602,6 +614,43 @@ static void parse_host_arg(struct hostinfo *hi, char *extra_args, int buflen)
                if (extra_args < end && *extra_args)
                        die("Invalid request");
        }
+
+       return extra_args;
+}
+
+static void parse_extra_args(struct hostinfo *hi, struct argv_array *env,
+                            char *extra_args, int buflen)
+{
+       const char *end = extra_args + buflen;
+       struct strbuf git_protocol = STRBUF_INIT;
+
+       /* First look for the host argument */
+       extra_args = parse_host_arg(hi, extra_args, buflen);
+
+       /* Look for additional arguments places after a second NUL byte */
+       for (; extra_args < end; extra_args += strlen(extra_args) + 1) {
+               const char *arg = extra_args;
+
+               /*
+                * Parse the extra arguments, adding most to 'git_protocol'
+                * which will be used to set the 'GIT_PROTOCOL' envvar in the
+                * service that will be run.
+                *
+                * If there ends up being a particular arg in the future that
+                * git-daemon needs to parse specificly (like the 'host' arg)
+                * then it can be parsed here and not added to 'git_protocol'.
+                */
+               if (*arg) {
+                       if (git_protocol.len > 0)
+                               strbuf_addch(&git_protocol, ':');
+                       strbuf_addstr(&git_protocol, arg);
+               }
+       }
+
+       if (git_protocol.len > 0)
+               argv_array_pushf(env, GIT_PROTOCOL_ENVIRONMENT "=%s",
+                                git_protocol.buf);
+       strbuf_release(&git_protocol);
 }
 
 /*
@@ -695,6 +744,7 @@ static int execute(void)
        int pktlen, len, i;
        char *addr = getenv("REMOTE_ADDR"), *port = getenv("REMOTE_PORT");
        struct hostinfo hi;
+       struct argv_array env = ARGV_ARRAY_INIT;
 
        hostinfo_init(&hi);
 
@@ -716,8 +766,9 @@ static int execute(void)
                pktlen--;
        }
 
+       /* parse additional args hidden behind a NUL byte */
        if (len != pktlen)
-               parse_host_arg(&hi, line + len + 1, pktlen - len - 1);
+               parse_extra_args(&hi, &env, line + len + 1, pktlen - len - 1);
 
        for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
                struct daemon_service *s = &(daemon_service[i]);
@@ -730,13 +781,15 @@ static int execute(void)
                         * Note: The directory here is probably context sensitive,
                         * and might depend on the actual service being performed.
                         */
-                       int rc = run_service(arg, s, &hi);
+                       int rc = run_service(arg, s, &hi, &env);
                        hostinfo_clear(&hi);
+                       argv_array_clear(&env);
                        return rc;
                }
        }
 
        hostinfo_clear(&hi);
+       argv_array_clear(&env);
        logerror("Protocol error: '%s'", line);
        return -1;
 }
index 4e0980caa80fb980938f68c8d350ec00cc24398b..8104603a3b36f2fd73761db34efde44d5938f68f 100644 (file)
@@ -12,6 +12,7 @@
 #include "refs.h"
 #include "submodule.h"
 #include "dir.h"
+#include "fsmonitor.h"
 
 /*
  * diff-files
@@ -36,7 +37,7 @@ static int check_removed(const struct cache_entry *ce, struct stat *st)
        if (has_symlink_leading_path(ce->name, ce_namelen(ce)))
                return 1;
        if (S_ISDIR(st->st_mode)) {
-               unsigned char sub[20];
+               struct object_id sub;
 
                /*
                 * If ce is already a gitlink, we can have a plain
@@ -50,7 +51,7 @@ static int check_removed(const struct cache_entry *ce, struct stat *st)
                 * a directory --- the blob was removed!
                 */
                if (!S_ISGITLINK(ce->ce_mode) &&
-                   resolve_gitlink_ref(ce->name, "HEAD", sub))
+                   resolve_gitlink_ref(ce->name, "HEAD", &sub))
                        return 1;
        }
        return 0;
@@ -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;
@@ -216,7 +218,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                        } else if (revs->diffopt.ita_invisible_in_index &&
                                   ce_intent_to_add(ce)) {
                                diff_addremove(&revs->diffopt, '+', ce->ce_mode,
-                                              &empty_tree_oid, 0,
+                                              the_hash_algo->empty_tree, 0,
                                               ce->name, 0);
                                continue;
                        }
@@ -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 80ff17d460ed67fa4b38f8a8dfce7504b27bbb64..0ed5f0f496882cef9e04f9a93fe718a9f55a268a 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 6fd288420bc55b5213ad6dfea4a4f411543d6dbf..2ebe2227b467e53bbd759c21e463646701071999 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;
@@ -707,83 +707,14 @@ struct moved_entry {
        struct moved_entry *next_line;
 };
 
-static int next_byte(const char **cp, const char **endp,
-                    const struct diff_options *diffopt)
-{
-       int retval;
-
-       if (*cp > *endp)
-               return -1;
-
-       if (isspace(**cp)) {
-               if (DIFF_XDL_TST(diffopt, IGNORE_WHITESPACE_CHANGE)) {
-                       while (*cp < *endp && isspace(**cp))
-                               (*cp)++;
-                       /*
-                        * After skipping a couple of whitespaces,
-                        * we still have to account for one space.
-                        */
-                       return (int)' ';
-               }
-
-               if (DIFF_XDL_TST(diffopt, IGNORE_WHITESPACE)) {
-                       while (*cp < *endp && isspace(**cp))
-                               (*cp)++;
-                       /* return the first non-ws character via the usual below */
-               }
-       }
-
-       retval = (unsigned char)(**cp);
-       (*cp)++;
-       return retval;
-}
-
 static int moved_entry_cmp(const struct diff_options *diffopt,
                           const struct moved_entry *a,
                           const struct moved_entry *b,
                           const void *keydata)
 {
-       const char *ap = a->es->line, *ae = a->es->line + a->es->len;
-       const char *bp = b->es->line, *be = b->es->line + b->es->len;
-
-       if (!(diffopt->xdl_opts & XDF_WHITESPACE_FLAGS))
-               return a->es->len != b->es->len  || memcmp(ap, bp, a->es->len);
-
-       if (DIFF_XDL_TST(diffopt, IGNORE_WHITESPACE_AT_EOL)) {
-               while (ae > ap && isspace(*ae))
-                       ae--;
-               while (be > bp && isspace(*be))
-                       be--;
-       }
-
-       while (1) {
-               int ca, cb;
-               ca = next_byte(&ap, &ae, diffopt);
-               cb = next_byte(&bp, &be, diffopt);
-               if (ca != cb)
-                       return 1;
-               if (ca < 0)
-                       return 0;
-       }
-}
-
-static unsigned get_string_hash(struct emitted_diff_symbol *es, struct diff_options *o)
-{
-       if (o->xdl_opts & XDF_WHITESPACE_FLAGS) {
-               static struct strbuf sb = STRBUF_INIT;
-               const char *ap = es->line, *ae = es->line + es->len;
-               int c;
-
-               strbuf_reset(&sb);
-               while (ae > ap && isspace(*ae))
-                       ae--;
-               while ((c = next_byte(&ap, &ae, o)) > 0)
-                       strbuf_addch(&sb, c);
-
-               return memhash(sb.buf, sb.len);
-       } else {
-               return memhash(es->line, es->len);
-       }
+       return !xdiff_compare_lines(a->es->line, a->es->len,
+                                   b->es->line, b->es->len,
+                                   diffopt->xdl_opts);
 }
 
 static struct moved_entry *prepare_entry(struct diff_options *o,
@@ -792,7 +723,7 @@ static struct moved_entry *prepare_entry(struct diff_options *o,
        struct moved_entry *ret = xmalloc(sizeof(*ret));
        struct emitted_diff_symbol *l = &o->emitted_symbols->buf[line_no];
 
-       ret->ent.hash = get_string_hash(l, o);
+       ret->ent.hash = xdiff_hash_string(l->line, l->len, o->xdl_opts);
        ret->es = l;
        ret->next_line = NULL;
 
@@ -1481,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 {
@@ -2729,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++) {
@@ -2755,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.
@@ -2830,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++) {
@@ -3117,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 {
@@ -3141,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);
        }
@@ -3201,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,
@@ -3236,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",
@@ -3282,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);
@@ -3447,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)
@@ -3614,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)
@@ -3941,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)))
@@ -3973,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;
@@ -4053,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)) {
@@ -4152,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;
@@ -4200,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);
@@ -4240,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;
@@ -4273,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())
@@ -4630,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;
@@ -4638,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;
        }
 
@@ -4659,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"))
@@ -4684,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)) {
@@ -4755,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;
@@ -4845,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");
@@ -5599,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) ||
@@ -5634,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
@@ -5683,11 +5613,11 @@ void diff_flush(struct diff_options *options)
         * 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;
        }
 }
 
@@ -5807,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);
                }
@@ -5856,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;
 }
@@ -5871,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);
 }
 
 /*
@@ -5899,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;
@@ -5931,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);
 
@@ -5950,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,
@@ -5969,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);
@@ -5988,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)
@@ -6133,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 aca150ba2e0f0b0a984e7f15c292af5f5b057d61..0fb18dd735b5462e7b186c609efd048561381be4 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;
@@ -180,6 +189,7 @@ struct diff_options {
        pathchange_fn_t pathchange;
        change_fn_t change;
        add_remove_fn_t add_remove;
+       void *change_fn_data;
        diff_format_fn_t format_callback;
        void *format_callback_data;
        diff_prefix_fn_t output_prefix;
@@ -388,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 341529b5a865ae6460f50d8593d52497059f2408..9476bd21081f456be6ae6412ea591e11dde50686 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 0d8c3d2ee480d70718f1e6f0b43a1175a14e4584..12dc2a056f119124a0518bcb898b5526ea9c5ba5 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 1d17b800cf374d179d7cef3c6bbb180a5a1b4aba..3c54366a1730ec37fcc56e185e2362e12d89b566 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,10 +1390,34 @@ 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)) {
-                       unsigned char sha1[20];
-                       if (resolve_gitlink_ref(dirname, "HEAD", sha1) == 0)
-                               return path_untracked;
+                       struct object_id oid;
+                       if (resolve_gitlink_ref(dirname, "HEAD", &oid) == 0)
+                               return exclude ? path_excluded : path_untracked;
                }
                return path_recurse;
        }
@@ -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) {
@@ -2279,10 +2326,10 @@ static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
        int ret = 0, original_len = path->len, len, kept_down = 0;
        int only_empty = (flag & REMOVE_DIR_EMPTY_ONLY);
        int keep_toplevel = (flag & REMOVE_DIR_KEEP_TOPLEVEL);
-       unsigned char submodule_head[20];
+       struct object_id submodule_head;
 
        if ((flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
-           !resolve_gitlink_ref(path->buf, "HEAD", submodule_head)) {
+           !resolve_gitlink_ref(path->buf, "HEAD", &submodule_head)) {
                /* Do not descend and nuke a nested git work tree. */
                if (kept_up)
                        *kept_up = 1;
diff --git a/dir.h b/dir.h
index e3717055d193366e6780b50d68f6a3c2360d32c4..233a2eb36bc00aa5fba48631408fa528d02a2d56 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 944c183b0f9cce90d62a2d998098ca48fc53c450..30211447ac839899d2c232af232c95312856816c 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 @@ static int write_entry(struct cache_entry *ce,
                                                   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 8289c25b44d74a0f054f9fe3eaf44c5fdcf0b3d2..8fa032f3074255541dc3f3d926fedf6b1991dd91 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 7103ceefbff6768042161345565bb773ab6b8d30..756bdd050e6df9e90bb08edc73617351d1c1f9b1 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 06c479f70a8eef4d2c9537be2723d7ccc10ae845..b9fdda1d3d2e033ddcfa7c1b31730d4188c75306 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 f73210973f12256f120351ef6826c64b62e51880..bed1994551cd03532fe56913b0e27343834c805f 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 c723f1aefd42113bf28162d5cd955e4bf5c36cd8..b9643b7d0f4420b0ebbd7315a3308407bdfafa53 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 269a1a87063af0d89ab4b7569f0193e08bd98bf3..dc43d05b644af07a86bb64dbc6790ea0897d2f43 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 63efdf969886889b5c09e055f37088a998122155..bb3c6ff7e05c0e3cb51a2ae5a065baa372e1d7f4 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__
index d5e4cf0bad411dad859dbe421ab7583cfb0f1561..b70ac025e0428b239c9cb8b8a70dda70c0cc1007 100644 (file)
@@ -1758,7 +1758,7 @@ static int update_branch(struct branch *b)
                        delete_ref(NULL, b->name, NULL, 0);
                return 0;
        }
-       if (read_ref(b->name, old_oid.hash))
+       if (read_ref(b->name, &old_oid))
                oidclr(&old_oid);
        if (!force_update && !is_null_oid(&old_oid)) {
                struct commit *old_cmit, *new_cmit;
@@ -1778,7 +1778,7 @@ static int update_branch(struct branch *b)
        }
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
-           ref_transaction_update(transaction, b->name, b->oid.hash, old_oid.hash,
+           ref_transaction_update(transaction, b->name, &b->oid, &old_oid,
                                   0, msg, &err) ||
            ref_transaction_commit(transaction, &err)) {
                ref_transaction_free(transaction);
@@ -1820,7 +1820,7 @@ static void dump_tags(void)
                strbuf_addf(&ref_name, "refs/tags/%s", t->name);
 
                if (ref_transaction_update(transaction, ref_name.buf,
-                                          t->oid.hash, NULL, 0, msg, &err)) {
+                                          &t->oid, NULL, 0, msg, &err)) {
                        failure |= error("%s", err.buf);
                        goto cleanup;
                }
index 008b25d3db08726b0fc1569f88fba76694bbcc12..9f6b07ad91f8c2c17e85004a0e71a8b69752d120 100644 (file)
@@ -716,7 +716,8 @@ static int everything_local(struct fetch_pack_args *args,
        for (ref = *refs; ref; ref = ref->next) {
                struct object *o;
 
-               if (!has_object_file(&ref->old_oid))
+               if (!has_object_file_with_flags(&ref->old_oid,
+                                               OBJECT_INFO_QUICK))
                        continue;
 
                o = parse_object(&ref->old_oid);
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 ab0d1b0c064a72480d7eac454b297ce95a48c8f3..eeea4b67ea7f1ccee1770c18a36d018b5f3b32b0 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 ae3cb013e7d5679bc4c63fe4cea35d40167178c5..54cbfecc5ab0531513ff9e069be55d74339ad427 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.
@@ -186,7 +186,7 @@ bisect_start() {
        #
        # Get rid of any old bisect state.
        #
-       bisect_clean_state || exit
+       git bisect--helper --bisect-clean-state || exit
 
        #
        # Change state.
@@ -195,7 +195,7 @@ bisect_start() {
        # We have to trap this to be able to clean up using
        # "bisect_clean_state".
        #
-       trap 'bisect_clean_state' 0
+       trap 'git bisect--helper --bisect-clean-state' 0
        trap 'exit 255' 1 2 3 15
 
        #
@@ -209,7 +209,7 @@ bisect_start() {
        eval "$eval true" &&
        if test $must_write_terms -eq 1
        then
-               write_terms "$TERM_BAD" "$TERM_GOOD"
+               git bisect--helper --write-terms "$TERM_BAD" "$TERM_GOOD" || exit
        fi &&
        echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
        #
@@ -237,22 +237,6 @@ bisect_write() {
        test -n "$nolog" || echo "git bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
 }
 
-is_expected_rev() {
-       test -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
-       test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV")
-}
-
-check_expected_revs() {
-       for _rev in "$@"; do
-               if ! is_expected_rev "$_rev"
-               then
-                       rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
-                       rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
-                       return
-               fi
-       done
-}
-
 bisect_skip() {
        all=''
        for arg in "$@"
@@ -280,7 +264,7 @@ bisect_state() {
                rev=$(git rev-parse --verify "$bisected_head") ||
                        die "$(eval_gettext "Bad rev input: \$bisected_head")"
                bisect_write "$state" "$rev"
-               check_expected_revs "$rev" ;;
+               git bisect--helper --check-expected-revs "$rev" ;;
        2,"$TERM_BAD"|*,"$TERM_GOOD"|*,skip)
                shift
                hash_list=''
@@ -294,7 +278,7 @@ bisect_state() {
                do
                        bisect_write "$state" "$rev"
                done
-               check_expected_revs $hash_list ;;
+               git bisect--helper --check-expected-revs $hash_list ;;
        *,"$TERM_BAD")
                die "$(eval_gettext "'git bisect \$TERM_BAD' can take only one argument.")" ;;
        *)
@@ -430,27 +414,7 @@ bisect_reset() {
                die "$(eval_gettext "Could not check out original HEAD '\$branch'.
 Try 'git bisect reset <commit>'.")"
        fi
-       bisect_clean_state
-}
-
-bisect_clean_state() {
-       # There may be some refs packed during bisection.
-       git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
-       while read ref hash
-       do
-               git update-ref -d $ref $hash || exit
-       done
-       rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
-       rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
-       rm -f "$GIT_DIR/BISECT_LOG" &&
-       rm -f "$GIT_DIR/BISECT_NAMES" &&
-       rm -f "$GIT_DIR/BISECT_RUN" &&
-       rm -f "$GIT_DIR/BISECT_TERMS" &&
-       # Cleanup head-name if it got left by an old version of git-bisect
-       rm -f "$GIT_DIR/head-name" &&
-       git update-ref -d --no-deref BISECT_HEAD &&
-       # clean up BISECT_START last
-       rm -f "$GIT_DIR/BISECT_START"
+       git bisect--helper --bisect-clean-state || exit
 }
 
 bisect_replay () {
@@ -486,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="$@"
@@ -557,45 +523,6 @@ get_terms () {
        fi
 }
 
-write_terms () {
-       TERM_BAD=$1
-       TERM_GOOD=$2
-       if test "$TERM_BAD" = "$TERM_GOOD"
-       then
-               die "$(gettext "please use two different terms")"
-       fi
-       check_term_format "$TERM_BAD" bad
-       check_term_format "$TERM_GOOD" good
-       printf '%s\n%s\n' "$TERM_BAD" "$TERM_GOOD" >"$GIT_DIR/BISECT_TERMS"
-}
-
-check_term_format () {
-       term=$1
-       git check-ref-format refs/bisect/"$term" ||
-       die "$(eval_gettext "'\$term' is not a valid term")"
-       case "$term" in
-       help|start|terms|skip|next|reset|visualize|replay|log|run)
-               die "$(eval_gettext "can't use the builtin command '\$term' as a term")"
-               ;;
-       bad|new)
-               if test "$2" != bad
-               then
-                       # In theory, nothing prevents swapping
-                       # completely good and bad, but this situation
-                       # could be confusing and hasn't been tested
-                       # enough. Forbid it for now.
-                       die "$(eval_gettext "can't change the meaning of term '\$term'")"
-               fi
-               ;;
-       good|old)
-               if test "$2" != good
-               then
-                       die "$(eval_gettext "can't change the meaning of term '\$term'")"
-               fi
-               ;;
-       esac
-}
-
 check_and_set_terms () {
        cmd="$1"
        case "$cmd" in
@@ -609,13 +536,17 @@ check_and_set_terms () {
                bad|good)
                        if ! test -s "$GIT_DIR/BISECT_TERMS"
                        then
-                               write_terms bad good
+                               TERM_BAD=bad
+                               TERM_GOOD=good
+                               git bisect--helper --write-terms "$TERM_BAD" "$TERM_GOOD" || exit
                        fi
                        ;;
                new|old)
                        if ! test -s "$GIT_DIR/BISECT_TERMS"
                        then
-                               write_terms new old
+                               TERM_BAD=new
+                               TERM_GOOD=old
+                               git bisect--helper --write-terms "$TERM_BAD" "$TERM_GOOD" || exit
                        fi
                        ;;
                esac ;;
index 918a8de369113bb022f5080ac9c516771f544577..f10caedaa7a3970628148258ed8dc9f6a5e8c827 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 5bc21b878d413e27de8e3d54e66a138ff41c7daa..ed24aa9d2f16a4eb600fc3298ebeb931f9fb6c9c 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 6e64d40d6fb8fe1566dd26c3df534afe2278390c..14c50782e096966b860d49cbaed7658e9f56958f 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 2563dc52daaf7acbbdba803360f94bfa5040c5b6..437815669f00b5b6639d48364be1e3c21a63696b 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 6344e8d5e38ca237dc5b5b67844f87f48aaa74ca..60b70f3defb5678af099e4c6bd7fe0610035a432 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
index 2208dcc2139be7130749fd3bae462320f17d3b21..edcc6d34692b28575d9728c25a407782310a39fb 100755 (executable)
@@ -885,7 +885,9 @@ sub expand_one_alias {
 }
 
 if (!defined $smtp_server) {
-       foreach (qw( /usr/sbin/sendmail /usr/lib/sendmail )) {
+       my @sendmail_paths = qw( /usr/sbin/sendmail /usr/lib/sendmail );
+       push @sendmail_paths, map {"$_/sendmail"} split /:/, $ENV{PATH};
+       foreach (@sendmail_paths) {
                if (-x $_) {
                        $smtp_server = $_;
                        last;
index 8b2ce9afdab6d67e8b9af5fc97a30dc6941323f2..1114005ce2829011cde2e6de608a2b90df355fc9 100755 (executable)
@@ -76,6 +76,12 @@ create_stash () {
                        shift
                        stash_msg=${1?"BUG: create_stash () -m requires an argument"}
                        ;;
+               -m*)
+                       stash_msg=${1#-m}
+                       ;;
+               --message=*)
+                       stash_msg=${1#--message=}
+                       ;;
                -u|--include-untracked)
                        shift
                        untracked=${1?"BUG: create_stash () -u requires an argument"}
@@ -193,6 +199,12 @@ store_stash () {
                        shift
                        stash_msg="$1"
                        ;;
+               -m*)
+                       stash_msg=${1#-m}
+                       ;;
+               --message=*)
+                       stash_msg=${1#--message=}
+                       ;;
                -q|--quiet)
                        quiet=t
                        ;;
@@ -251,6 +263,12 @@ push_stash () {
                        test -z ${1+x} && usage
                        stash_msg=$1
                        ;;
+               -m*)
+                       stash_msg=${1#-m}
+                       ;;
+               --message=*)
+                       stash_msg=${1#--message=}
+                       ;;
                --help)
                        show_help
                        ;;
@@ -260,18 +278,7 @@ push_stash () {
                        ;;
                -*)
                        option="$1"
-                       # TRANSLATORS: $option is an invalid option, like
-                       # `--blah-blah'. The 7 spaces at the beginning of the
-                       # second line correspond to "error: ". So you should line
-                       # up the second line with however many characters the
-                       # translation of "error: " takes in your language. E.g. in
-                       # English this is:
-                       #
-                       #    $ git stash save --blah-blah 2>&1 | head -n 2
-                       #    error: unknown option for 'stash save': --blah-blah
-                       #           To provide a message, use git stash save -- '--blah-blah'
-                       eval_gettextln "error: unknown option for 'stash save': \$option
-       To provide a message, use git stash save -- '\$option'"
+                       eval_gettextln "error: unknown option for 'stash push': \$option"
                        usage
                        ;;
                *)
index 66d1ae8ef6c5f12c856a2f88eabce515dcafd489..156255a9e56e7a5930f3f652ee38994064db03ea 100755 (executable)
@@ -758,18 +758,6 @@ cmd_update()
        }
 }
 
-set_name_rev () {
-       revname=$( (
-               sanitize_submodule_env
-               cd "$1" && {
-                       git describe "$2" 2>/dev/null ||
-                       git describe --tags "$2" 2>/dev/null ||
-                       git describe --contains "$2" 2>/dev/null ||
-                       git describe --all --always "$2"
-               }
-       ) )
-       test -z "$revname" || revname=" ($revname)"
-}
 #
 # Show commit summary for submodules in index or working tree
 #
@@ -1016,54 +1004,7 @@ cmd_status()
                shift
        done
 
-       {
-               git submodule--helper list --prefix "$wt_prefix" "$@" ||
-               echo "#unmatched" $?
-       } |
-       while read -r mode sha1 stage sm_path
-       do
-               die_if_unmatched "$mode" "$sha1"
-               name=$(git submodule--helper name "$sm_path") || exit
-               displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
-               if test "$stage" = U
-               then
-                       say "U$sha1 $displaypath"
-                       continue
-               fi
-               if ! git submodule--helper is-active "$sm_path" ||
-               {
-                       ! test -d "$sm_path"/.git &&
-                       ! test -f "$sm_path"/.git
-               }
-               then
-                       say "-$sha1 $displaypath"
-                       continue;
-               fi
-               if git diff-files --ignore-submodules=dirty --quiet -- "$sm_path"
-               then
-                       set_name_rev "$sm_path" "$sha1"
-                       say " $sha1 $displaypath$revname"
-               else
-                       if test -z "$cached"
-                       then
-                               sha1=$(sanitize_submodule_env; cd "$sm_path" && git rev-parse --verify HEAD)
-                       fi
-                       set_name_rev "$sm_path" "$sha1"
-                       say "+$sha1 $displaypath$revname"
-               fi
-
-               if test -n "$recursive"
-               then
-                       (
-                               prefix="$displaypath/"
-                               sanitize_submodule_env
-                               wt_prefix=
-                               cd "$sm_path" &&
-                               eval cmd_status
-                       ) ||
-                       die "$(eval_gettext "Failed to recurse into submodule path '\$sm_path'")"
-               fi
-       done
+       git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper status ${GIT_QUIET:+--quiet} ${cached:+--cached} ${recursive:+--recursive} "$@"
 }
 #
 # Sync remote urls for submodules
diff --git a/git.c b/git.c
index 9e96dd409017c643e8a9bebbc41281728978b0b8..c870b9719c21b2db23ea7ab5d56fdeda483cb02e 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 33aafb786cf7f73eb7d4a769e585cb958999e68a..49002e0d541f1ab080efd8b9b4732f52db89e984 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
index 959f04b494e610258488867aa7de013a3477e5fc..2417057f2bc61a98e68dc6c817e456a21bf6044e 100755 (executable)
@@ -10,6 +10,8 @@
 use 5.008;
 use strict;
 use warnings;
+# handle ACL in file access tests
+use filetest 'access';
 use CGI qw(:standard :escapeHTML -nosticky);
 use CGI::Util qw(unescape);
 use CGI::Carp qw(fatalsToBrowser set_message);
diff --git a/grep.c b/grep.c
index ce6a48e634105154ca4bb267b4eb744c74d4d803..3d7cd0e96f1ee160a66dd500c58d4026bf24e34c 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);
 
@@ -477,6 +477,8 @@ static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt
        int options = PCRE2_MULTILINE;
        const uint8_t *character_tables = NULL;
        int jitret;
+       int patinforet;
+       size_t jitsizearg;
 
        assert(opt->pcre2);
 
@@ -511,6 +513,30 @@ static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt
                jitret = pcre2_jit_compile(p->pcre2_pattern, PCRE2_JIT_COMPLETE);
                if (jitret)
                        die("Couldn't JIT the PCRE2 pattern '%s', got '%d'\n", p->pattern, jitret);
+
+               /*
+                * The pcre2_config(PCRE2_CONFIG_JIT, ...) call just
+                * tells us whether the library itself supports JIT,
+                * but to see whether we're going to be actually using
+                * JIT we need to extract PCRE2_INFO_JITSIZE from the
+                * pattern *after* we do pcre2_jit_compile() above.
+                *
+                * This is because if the pattern contains the
+                * (*NO_JIT) verb (see pcre2syntax(3))
+                * pcre2_jit_compile() will exit early with 0. If we
+                * then proceed to call pcre2_jit_match() further down
+                * the line instead of pcre2_match() we'll either
+                * segfault (pre PCRE 10.31) or run into a fatal error
+                * (post PCRE2 10.31)
+                */
+               patinforet = pcre2_pattern_info(p->pcre2_pattern, PCRE2_INFO_JITSIZE, &jitsizearg);
+               if (patinforet)
+                       BUG("pcre2_pattern_info() failed: %d", patinforet);
+               if (jitsizearg == 0) {
+                       p->pcre2_jit_on = 0;
+                       return;
+               }
+
                p->pcre2_jit_stack = pcre2_jit_stack_create(1, 1024 * 1024, NULL);
                if (!p->pcre2_jit_stack)
                        die("Couldn't allocate PCRE2 JIT stack");
@@ -1476,31 +1502,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 52aecfab6ebdd6c5565e79db6062b7d7a99cf3df..399381c908c2c93cbcf447498fb435e517dddde0 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/hash.h b/hash.h
index 024d0d3d50b174aac1d948e9ecfa7ae92020355a..7d7a864f5dd43bb53ee6b31d391df3e15cbdbb08 100644 (file)
--- a/hash.h
+++ b/hash.h
@@ -1,6 +1,8 @@
 #ifndef HASH_H
 #define HASH_H
 
+#include "git-compat-util.h"
+
 #if defined(SHA1_PPC)
 #include "ppc/sha1.h"
 #elif defined(SHA1_APPLE)
 #include "block-sha1/sha1.h"
 #endif
 
+/*
+ * Note that these constants are suitable for indexing the hash_algos array and
+ * comparing against each other, but are otherwise arbitrary, so they should not
+ * be exposed to the user or serialized to disk.  To know whether a
+ * git_hash_algo struct points to some usable hash function, test the format_id
+ * field for being non-zero.  Use the name field for user-visible situations and
+ * the format_id field for fixed-length fields on disk.
+ */
+/* An unknown hash function. */
+#define GIT_HASH_UNKNOWN 0
+/* SHA-1 */
+#define GIT_HASH_SHA1 1
+/* Number of algorithms supported (including unknown). */
+#define GIT_HASH_NALGOS (GIT_HASH_SHA1 + 1)
+
+typedef void (*git_hash_init_fn)(void *ctx);
+typedef void (*git_hash_update_fn)(void *ctx, const void *in, size_t len);
+typedef void (*git_hash_final_fn)(unsigned char *hash, void *ctx);
+
+struct git_hash_algo {
+       /*
+        * The name of the algorithm, as appears in the config file and in
+        * messages.
+        */
+       const char *name;
+
+       /* A four-byte version identifier, used in pack indices. */
+       uint32_t format_id;
+
+       /* The size of a hash context (e.g. git_SHA_CTX). */
+       size_t ctxsz;
+
+       /* The length of the hash in binary. */
+       size_t rawsz;
+
+       /* The length of the hash in hex characters. */
+       size_t hexsz;
+
+       /* The hash initialization function. */
+       git_hash_init_fn init_fn;
+
+       /* The hash update function. */
+       git_hash_update_fn update_fn;
+
+       /* The hash finalization function. */
+       git_hash_final_fn final_fn;
+
+       /* The OID of the empty tree. */
+       const struct object_id *empty_tree;
+
+       /* The OID of the empty blob. */
+       const struct object_id *empty_blob;
+};
+extern const struct git_hash_algo hash_algos[GIT_HASH_NALGOS];
+
 #endif
diff --git a/hex.c b/hex.c
index 28b44118cbf2c75b26d017a34ecfdf65dbf4c7eb..8df2d63728f0405a9b1dc70aa404954dd921b6bc 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 493ee7d719d488f25cd6c45bb4c7ce1d5a75977b..14435ab65d1bbe3fc73be6a6d774b6ab6ffc8360 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)
diff --git a/http.c b/http.c
index 713525f38ed3f254aed2b0dd65fece7972444072..215bebef1bfb935355a765027fc8605fccbc3d1b 100644 (file)
--- a/http.c
+++ b/http.c
@@ -12,6 +12,7 @@
 #include "gettext.h"
 #include "transport.h"
 #include "packfile.h"
+#include "protocol.h"
 
 static struct trace_key trace_curl = TRACE_KEY_INIT(CURL);
 #if LIBCURL_VERSION_NUM >= 0x070a08
@@ -898,6 +899,21 @@ static void set_from_env(const char **var, const char *envname)
                *var = val;
 }
 
+static void protocol_http_header(void)
+{
+       if (get_protocol_version_config() > 0) {
+               struct strbuf protocol_header = STRBUF_INIT;
+
+               strbuf_addf(&protocol_header, GIT_PROTOCOL_HEADER ": version=%d",
+                           get_protocol_version_config());
+
+
+               extra_http_headers = curl_slist_append(extra_http_headers,
+                                                      protocol_header.buf);
+               strbuf_release(&protocol_header);
+       }
+}
+
 void http_init(struct remote *remote, const char *url, int proactive_auth)
 {
        char *low_speed_limit;
@@ -928,6 +944,8 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
        if (remote)
                var_override(&http_proxy_authmethod, remote->http_proxy_authmethod);
 
+       protocol_http_header();
+
        pragma_header = curl_slist_append(http_copy_default_headers(),
                "Pragma: no-cache");
        no_pragma_header = curl_slist_append(http_copy_default_headers(),
index 8c785f3ca20c52266d7f99ca0d306a324756ede8..54e6a80fd64e16420a526461e90aed559e0bd1d6 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 = strchr(s, ']'))) {
@@ -693,6 +692,10 @@ static int parse_response_code(struct imap_store *ctx, struct imap_cmd_cb *cb,
        }
        *p++ = 0;
        arg = next_arg(&s);
+       if (!arg) {
+               fprintf(stderr, "IMAP error: empty response code\n");
+               return RESP_BAD;
+       }
        if (!strcmp("UIDVALIDITY", arg)) {
                if (!(arg = next_arg(&s)) || !(ctx->uidvalidity = atoi(arg))) {
                        fprintf(stderr, "IMAP error: malformed UIDVALIDITY status\n");
@@ -725,7 +728,8 @@ static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd)
 {
        struct imap *imap = ctx->imap;
        struct imap_cmd *cmdp, **pcmdp;
-       char *cmd, *arg, *arg1;
+       char *cmd;
+       const char *arg, *arg1;
        int n, resp, resp2, tag;
 
        for (;;) {
@@ -733,6 +737,10 @@ static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd)
                        return RESP_BAD;
 
                arg = next_arg(&cmd);
+               if (!arg) {
+                       fprintf(stderr, "IMAP error: empty response\n");
+                       return RESP_BAD;
+               }
                if (*arg == '*') {
                        arg = next_arg(&cmd);
                        if (!arg) {
@@ -807,6 +815,8 @@ static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd)
                        if (cmdp->cb.cont || cmdp->cb.data)
                                imap->literal_pending = 0;
                        arg = next_arg(&cmd);
+                       if (!arg)
+                               arg = "";
                        if (!strcmp("OK", arg))
                                resp = DRV_OK;
                        else {
diff --git a/kwset.c b/kwset.c
index e6236a0359cb3661acb8b1e609be236f6fb9d4bf..4fb6455acaf1293c4f47f27eb6e47be4d39633e2 100644 (file)
--- a/kwset.c
+++ b/kwset.c
@@ -18,9 +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., 51 Franklin Street - Fifth Floor, Boston, MA
-   02110-1301, USA.  */
+   along with this program; if not, see <http://www.gnu.org/licenses/>. */
 
 /* Written August 1989 by Mike Haertel.
    The author may be reached (Email) at the address mike@ai.mit.edu,
diff --git a/kwset.h b/kwset.h
index 61a134f256cee951f726c9619861b614fb66f9d6..583f6268ef0beb121e8b776f38b4169784caf7a8 100644 (file)
--- a/kwset.h
+++ b/kwset.h
@@ -17,9 +17,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/>. */
 
 /* Written August 1989 by Mike Haertel.
    The author may be reached (Email) at the address mike@ai.mit.edu,
index 7c1c484d7cfdfab126fd0ce947ac40f650e5d107..f401c979f0ed907b40bfb509ab8f06ad2632c19e 100644 (file)
@@ -240,8 +240,8 @@ extern char *get_locked_file_path(struct lock_file *lk);
  * If the lockfile is still open, close it (and the file pointer if it
  * has been opened using `fdopen_lock_file()`) without renaming the
  * lockfile over the file being locked. Return 0 upon success. On
- * failure to `close(2)`, return a negative value and roll back the
- * lock file. Usually `commit_lock_file()`, `commit_lock_file_to()`,
+ * failure to `close(2)`, return a negative value (the lockfile is not
+ * rolled back). Usually `commit_lock_file()`, `commit_lock_file_to()`,
  * or `rollback_lock_file()` should eventually be called.
  */
 static inline int close_lock_file_gently(struct lock_file *lk)
index cea056234d0a0b605c988a4e157852cd659f22ef..fca29d4799da27164394a8c59da067a0522241fe 100644 (file)
@@ -94,8 +94,12 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid,
 {
        struct object *obj;
        enum decoration_type type = DECORATION_NONE;
+       struct decoration_filter *filter = (struct decoration_filter *)cb_data;
 
-       assert(cb_data == NULL);
+       if (filter && !ref_filter_match(refname,
+                             filter->include_ref_pattern,
+                             filter->exclude_ref_pattern))
+               return 0;
 
        if (starts_with(refname, git_replace_ref_base)) {
                struct object_id original_oid;
@@ -148,15 +152,23 @@ static int add_graft_decoration(const struct commit_graft *graft, void *cb_data)
        return 0;
 }
 
-void load_ref_decorations(int flags)
+void load_ref_decorations(struct decoration_filter *filter, int flags)
 {
        if (!decoration_loaded) {
-
+               if (filter) {
+                       struct string_list_item *item;
+                       for_each_string_list_item(item, filter->exclude_ref_pattern) {
+                               normalize_glob_ref(item, NULL, item->string);
+                       }
+                       for_each_string_list_item(item, filter->include_ref_pattern) {
+                               normalize_glob_ref(item, NULL, item->string);
+                       }
+               }
                decoration_loaded = 1;
                decoration_flags = flags;
-               for_each_ref(add_ref_decoration, NULL);
-               head_ref(add_ref_decoration, NULL);
-               for_each_commit_graft(add_graft_decoration, NULL);
+               for_each_ref(add_ref_decoration, filter);
+               head_ref(add_ref_decoration, filter);
+               for_each_commit_graft(add_graft_decoration, filter);
        }
 }
 
@@ -198,7 +210,7 @@ static const struct name_decoration *current_pointed_by_HEAD(const struct name_d
 
        /* Now resolve and find the matching current branch */
        branch_name = resolve_ref_unsafe("HEAD", 0, NULL, &rru_flags);
-       if (!(rru_flags & REF_ISSYMREF))
+       if (!branch_name || !(rru_flags & REF_ISSYMREF))
                return NULL;
 
        if (!starts_with(branch_name, "refs/"))
@@ -793,7 +805,7 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
        struct commit_list *parents;
        struct object_id *oid;
 
-       if (!opt->diff && !DIFF_OPT_TST(&opt->diffopt, EXIT_WITH_STATUS))
+       if (!opt->diff && !opt->diffopt.flags.exit_with_status)
                return 0;
 
        parse_commit_or_die(commit);
index 48f11fb740ddf670cac819cb733cbc736cc30d0e..deba035187185bc28a83520b47480c3141d8398e 100644 (file)
@@ -7,6 +7,10 @@ struct log_info {
        struct commit *commit, *parent;
 };
 
+struct decoration_filter {
+       struct string_list *include_ref_pattern, *exclude_ref_pattern;
+};
+
 int parse_decorate_color_config(const char *var, const char *slot_name, const char *value);
 void init_log_tree_opt(struct rev_info *);
 int log_tree_diff_flush(struct rev_info *);
@@ -24,7 +28,7 @@ void show_decorations(struct rev_info *opt, struct commit *commit);
 void log_write_email_headers(struct rev_info *opt, struct commit *commit,
                             const char **extra_headers_p,
                             int *need_8bit_cte_p);
-void load_ref_decorations(int flags);
+void load_ref_decorations(struct decoration_filter *filter, int flags);
 
 #define FORMAT_PATCH_NAME_MAX 64
 void fmt_output_commit(struct strbuf *, struct commit *, struct rev_info *);
index 1d3f8f0d22157e11c1e0db6f230e39240d2f3e63..a4c280dfc78e943ec541f83ccb0b39921ccdf2b2 100644 (file)
@@ -540,8 +540,8 @@ static struct string_list *get_renames(struct merge_options *o,
                return renames;
 
        diff_setup(&opts);
-       DIFF_OPT_SET(&opts, RECURSIVE);
-       DIFF_OPT_CLR(&opts, RENAME_EMPTY);
+       opts.flags.recursive = 1;
+       opts.flags.rename_empty = 0;
        opts.detect_rename = DIFF_DETECT_RENAME;
        opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
                            o->diff_rename_limit >= 0 ? o->diff_rename_limit :
@@ -1901,8 +1901,9 @@ static int process_entry(struct merge_options *o,
                        oid = b_oid;
                        conf = _("directory/file");
                }
-               if (dir_in_way(path, !o->call_depth,
-                              S_ISGITLINK(a_mode))) {
+               if (dir_in_way(path,
+                              !o->call_depth && !S_ISGITLINK(a_mode),
+                              0)) {
                        char *new_path = unique_path(o, path, add_branch);
                        clean_merge = 0;
                        output(o, 1, _("CONFLICT (%s): There is a directory with name %s in %s. "
@@ -2081,7 +2082,7 @@ int merge_recursive(struct merge_options *o,
                /* if there is no common ancestor, use an empty tree */
                struct tree *tree;
 
-               tree = lookup_tree(&empty_tree_oid);
+               tree = lookup_tree(the_hash_algo->empty_tree);
                merged_common_ancestors = make_virtual_commit(tree, "ancestor");
        }
 
@@ -2162,7 +2163,7 @@ int merge_recursive_generic(struct merge_options *o,
                            struct commit **result)
 {
        int clean;
-       struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+       struct lock_file lock = LOCK_INIT;
        struct commit *head_commit = get_ref(head, o->branch1);
        struct commit *next_commit = get_ref(merge, o->branch2);
        struct commit_list *ca = NULL;
@@ -2178,14 +2179,14 @@ int merge_recursive_generic(struct merge_options *o,
                }
        }
 
-       hold_locked_index(lock, LOCK_DIE_ON_ERROR);
+       hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
        clean = merge_recursive(o, head_commit, next_commit, ca,
                        result);
        if (clean < 0)
                return clean;
 
        if (active_cache_changed &&
-           write_locked_index(&the_index, lock, COMMIT_LOCK))
+           write_locked_index(&the_index, &lock, COMMIT_LOCK))
                return err(o, _("Unable to write index."));
 
        return clean ? 0 : 1;
@@ -2201,6 +2202,7 @@ static void merge_recursive_config(struct merge_options *o)
 
 void init_merge_options(struct merge_options *o)
 {
+       const char *merge_verbosity;
        memset(o, 0, sizeof(struct merge_options));
        o->verbosity = 2;
        o->buffer_output = 1;
@@ -2209,9 +2211,9 @@ void init_merge_options(struct merge_options *o)
        o->renormalize = 0;
        o->detect_rename = 1;
        merge_recursive_config(o);
-       if (getenv("GIT_MERGE_VERBOSITY"))
-               o->verbosity =
-                       strtol(getenv("GIT_MERGE_VERBOSITY"), NULL, 10);
+       merge_verbosity = getenv("GIT_MERGE_VERBOSITY");
+       if (merge_verbosity)
+               o->verbosity = strtol(merge_verbosity, NULL, 10);
        if (o->verbosity >= 5)
                o->buffer_output = 0;
        strbuf_init(&o->obuf, 0);
@@ -2251,6 +2253,8 @@ int parse_merge_opt(struct merge_options *o, const char *s)
                DIFF_XDL_SET(o, IGNORE_WHITESPACE);
        else if (!strcmp(s, "ignore-space-at-eol"))
                DIFF_XDL_SET(o, IGNORE_WHITESPACE_AT_EOL);
+       else if (!strcmp(s, "ignore-cr-at-eol"))
+               DIFF_XDL_SET(o, IGNORE_CR_AT_EOL);
        else if (!strcmp(s, "renormalize"))
                o->renormalize = 1;
        else if (!strcmp(s, "no-renormalize"))
diff --git a/merge.c b/merge.c
index 1d441ad94254257ad2ac28b8a17dc0b8e5ce14f9..e5d796c9f2d110bdcd378b361fe2052ac1dcc266 100644 (file)
--- a/merge.c
+++ b/merge.c
@@ -53,11 +53,11 @@ int checkout_fast_forward(const struct object_id *head,
        struct tree_desc t[MAX_UNPACK_TREES];
        int i, nr_trees = 0;
        struct dir_struct dir;
-       struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+       struct lock_file lock_file = LOCK_INIT;
 
        refresh_cache(REFRESH_QUIET);
 
-       if (hold_locked_index(lock_file, LOCK_REPORT_ON_ERROR) < 0)
+       if (hold_locked_index(&lock_file, LOCK_REPORT_ON_ERROR) < 0)
                return -1;
 
        memset(&trees, 0, sizeof(trees));
@@ -91,9 +91,7 @@ int checkout_fast_forward(const struct object_id *head,
        }
        if (unpack_trees(nr_trees, t, &opts))
                return -1;
-       if (write_locked_index(&the_index, lock_file, COMMIT_LOCK)) {
-               rollback_lock_file(lock_file);
+       if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                return error(_("unable to write new index file"));
-       }
        return 0;
 }
index 29b4cede5f8d539416d0af22f31b0aabc0e28c46..17ee8602b3d2fbc2469a98e6cdeab0f2ff2b7ad3 100644 (file)
@@ -11,7 +11,7 @@ static int notes_cache_match_validity(const char *ref, const char *validity)
        struct strbuf msg = STRBUF_INIT;
        int ret;
 
-       if (read_ref(ref, oid.hash) < 0)
+       if (read_ref(ref, &oid) < 0)
                return 0;
 
        commit = lookup_commit_reference_gently(&oid, 1);
@@ -59,7 +59,7 @@ int notes_cache_write(struct notes_cache *c)
        if (commit_tree(c->validity, strlen(c->validity), tree_oid.hash, NULL,
                        commit_oid.hash, NULL, NULL) < 0)
                return -1;
-       if (update_ref("update notes cache", c->tree.update_ref, commit_oid.hash,
+       if (update_ref("update notes cache", c->tree.update_ref, &commit_oid,
                       NULL, 0, UPDATE_REFS_QUIET_ON_ERR) < 0)
                return -1;
 
index 4352c34a6ee50f907f2d03e7d0ed1a596345a5d3..0f6573cb17cbbbc2219453bdca424fd7ebc156dc 100644 (file)
@@ -125,7 +125,7 @@ static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o,
               oid_to_hex(base), oid_to_hex(remote));
 
        diff_setup(&opt);
-       DIFF_OPT_SET(&opt, RECURSIVE);
+       opt.flags.recursive = 1;
        opt.output_format = DIFF_FORMAT_NO_OUTPUT;
        diff_setup_done(&opt);
        diff_tree_oid(base, remote, "", &opt);
@@ -188,7 +188,7 @@ static void diff_tree_local(struct notes_merge_options *o,
               len, oid_to_hex(base), oid_to_hex(local));
 
        diff_setup(&opt);
-       DIFF_OPT_SET(&opt, RECURSIVE);
+       opt.flags.recursive = 1;
        opt.output_format = DIFF_FORMAT_NO_OUTPUT;
        diff_setup_done(&opt);
        diff_tree_oid(base, local, "", &opt);
@@ -547,7 +547,7 @@ int notes_merge(struct notes_merge_options *o,
               o->local_ref, o->remote_ref);
 
        /* Dereference o->local_ref into local_sha1 */
-       if (read_ref_full(o->local_ref, 0, local_oid.hash, NULL))
+       if (read_ref_full(o->local_ref, 0, &local_oid, NULL))
                die("Failed to resolve local notes ref '%s'", o->local_ref);
        else if (!check_refname_format(o->local_ref, 0) &&
                is_null_oid(&local_oid))
@@ -595,7 +595,7 @@ int notes_merge(struct notes_merge_options *o,
        bases = get_merge_bases(local, remote);
        if (!bases) {
                base_oid = &null_oid;
-               base_tree_oid = &empty_tree_oid;
+               base_tree_oid = the_hash_algo->empty_tree;
                if (o->verbosity >= 4)
                        printf("No merge base found; doing history-less merge\n");
        } else if (!bases->next) {
index 9765deb41ab02dba909ec9027701bf2fdc53f310..5c8e70c98fd26cca8bc690bc92653dd578a78c92 100644 (file)
@@ -18,7 +18,7 @@ void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
        if (!parents) {
                /* Deduce parent commit from t->ref */
                struct object_id parent_oid;
-               if (!read_ref(t->ref, parent_oid.hash)) {
+               if (!read_ref(t->ref, &parent_oid)) {
                        struct commit *parent = lookup_commit(&parent_oid);
                        if (parse_commit(parent))
                                die("Failed to find/parse commit %s", t->ref);
@@ -49,7 +49,7 @@ void commit_notes(struct notes_tree *t, const char *msg)
 
        create_notes_commit(t, NULL, buf.buf, buf.len, commit_oid.hash);
        strbuf_insert(&buf, 0, "notes: ", 7); /* commit message starts at index 7 */
-       update_ref(buf.buf, t->update_ref, commit_oid.hash, NULL, 0,
+       update_ref(buf.buf, t->update_ref, &commit_oid, NULL, 0,
                   UPDATE_REFS_DIE_ON_ERR);
 
        strbuf_release(&buf);
diff --git a/notes.c b/notes.c
index 5c62862574399acb486530d3fab5a7f659eb7c2f..c7f21fae441067f7250caf0b4186fcbcbc7630bb 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -334,23 +334,6 @@ static void note_tree_free(struct int_node *tree)
        }
 }
 
-/*
- * 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).
- */
-static 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;
-}
-
 static int non_note_cmp(const struct non_note *a, const struct non_note *b)
 {
        return strcmp(a->path, b->path);
@@ -1027,7 +1010,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,
        if (flags & NOTES_INIT_EMPTY || !notes_ref ||
            get_oid_treeish(notes_ref, &object_oid))
                return;
-       if (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, object_oid.hash))
+       if (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, &object_oid))
                die("Cannot use notes ref %s", notes_ref);
        if (get_tree_entry(object_oid.hash, "", oid.hash, &mode))
                die("Failed to read notes tree referenced by %s (%s)",
index 42e3d5f4f26ee0f31d13c2a3d15bc31da5f7c1d4..9270983e5f581e40f894a8885396e43d13e71015 100644 (file)
@@ -587,7 +587,7 @@ static void show_extended_objects(struct bitmap *objects,
                        continue;
 
                obj = eindex->objects[i];
-               show_reach(obj->oid.hash, obj->type, 0, eindex->hashes[i], NULL, 0);
+               show_reach(&obj->oid, obj->type, 0, eindex->hashes[i], NULL, 0);
        }
 }
 
@@ -612,7 +612,7 @@ static void show_objects_for_type(
                eword_t word = objects->words[i] & filter;
 
                for (offset = 0; offset < BITS_IN_EWORD; ++offset) {
-                       const unsigned char *sha1;
+                       struct object_id oid;
                        struct revindex_entry *entry;
                        uint32_t hash = 0;
 
@@ -625,12 +625,12 @@ static void show_objects_for_type(
                                continue;
 
                        entry = &bitmap_git.pack->revindex[pos + offset];
-                       sha1 = nth_packed_object_sha1(bitmap_git.pack, entry->nr);
+                       nth_packed_object_oid(&oid, bitmap_git.pack, entry->nr);
 
                        if (bitmap_git.hashes)
                                hash = get_be32(bitmap_git.hashes + entry->nr);
 
-                       show_reach(sha1, object_type, 0, hash, bitmap_git.pack, entry->offset);
+                       show_reach(&oid, object_type, 0, hash, bitmap_git.pack, entry->offset);
                }
 
                pos += BITS_IN_EWORD;
index 0adcef77b58cc13822f1b400d5418ec6f8b34421..3742a00e14a0d4da335253b2a76f978edb499d35 100644 (file)
@@ -27,7 +27,7 @@ enum pack_bitmap_flags {
 };
 
 typedef int (*show_reachable_fn)(
-       const unsigned char *sha1,
+       const struct object_id *oid,
        enum object_type type,
        int flags,
        uint32_t hash,
index 7a583b3011390c1a3cb1c0885e420e76b7751d26..8f7c25d5dbf5ccdcbf27db51fbe1970addebf9a7 100644 (file)
@@ -61,7 +61,7 @@ int init_patch_ids(struct patch_ids *ids)
        memset(ids, 0, sizeof(*ids));
        diff_setup(&ids->diffopts);
        ids->diffopts.detect_rename = 0;
-       DIFF_OPT_SET(&ids->diffopts, RECURSIVE);
+       ids->diffopts.flags.recursive = 1;
        diff_setup_done(&ids->diffopts);
        hashmap_init(&ids->patches, patch_id_cmp, &ids->diffopts, 256);
        return 0;
index cdefdc7cc0e0be248297d1191eefe72418923f7a..82eb39cd679ffbce82abac691c6f4343335b9924 100644 (file)
@@ -532,7 +532,7 @@ void parse_pathspec(struct pathspec *pathspec,
 {
        struct pathspec_item *item;
        const char *entry = argv ? *argv : NULL;
-       int i, n, prefixlen, warn_empty_string, nr_exclude = 0;
+       int i, n, prefixlen, nr_exclude = 0;
 
        memset(pathspec, 0, sizeof(*pathspec));
 
@@ -565,13 +565,10 @@ void parse_pathspec(struct pathspec *pathspec,
        }
 
        n = 0;
-       warn_empty_string = 1;
        while (argv[n]) {
-               if (*argv[n] == '\0' && warn_empty_string) {
-                       warning(_("empty strings as pathspecs will be made invalid in upcoming releases. "
-                                 "please use . instead if you meant to match all paths"));
-                       warn_empty_string = 0;
-               }
+               if (*argv[n] == '\0')
+                       die("empty string is not a valid pathspec. "
+                                 "please use . instead if you meant to match all paths");
                n++;
        }
 
diff --git a/perl/Git/Packet.pm b/perl/Git/Packet.pm
new file mode 100644 (file)
index 0000000..b75738b
--- /dev/null
@@ -0,0 +1,173 @@
+package Git::Packet;
+use 5.008;
+use strict;
+use warnings;
+BEGIN {
+       require Exporter;
+       if ($] < 5.008003) {
+               *import = \&Exporter::import;
+       } else {
+               # Exporter 5.57 which supports this invocation was
+               # released with perl 5.8.3
+               Exporter->import('import');
+       }
+}
+
+our @EXPORT = qw(
+                       packet_compare_lists
+                       packet_bin_read
+                       packet_txt_read
+                       packet_key_val_read
+                       packet_bin_write
+                       packet_txt_write
+                       packet_flush
+                       packet_initialize
+                       packet_read_capabilities
+                       packet_read_and_check_capabilities
+                       packet_check_and_write_capabilities
+               );
+our @EXPORT_OK = @EXPORT;
+
+sub packet_compare_lists {
+       my ($expect, @result) = @_;
+       my $ix;
+       if (scalar @$expect != scalar @result) {
+               return undef;
+       }
+       for ($ix = 0; $ix < $#result; $ix++) {
+               if ($expect->[$ix] ne $result[$ix]) {
+                       return undef;
+               }
+       }
+       return 1;
+}
+
+sub packet_bin_read {
+       my $buffer;
+       my $bytes_read = read STDIN, $buffer, 4;
+       if ( $bytes_read == 0 ) {
+               # EOF - Git stopped talking to us!
+               return ( -1, "" );
+       } elsif ( $bytes_read != 4 ) {
+               die "invalid packet: '$buffer'";
+       }
+       my $pkt_size = hex($buffer);
+       if ( $pkt_size == 0 ) {
+               return ( 1, "" );
+       } elsif ( $pkt_size > 4 ) {
+               my $content_size = $pkt_size - 4;
+               $bytes_read = read STDIN, $buffer, $content_size;
+               if ( $bytes_read != $content_size ) {
+                       die "invalid packet ($content_size bytes expected; $bytes_read bytes read)";
+               }
+               return ( 0, $buffer );
+       } else {
+               die "invalid packet size: $pkt_size";
+       }
+}
+
+sub remove_final_lf_or_die {
+       my $buf = shift;
+       if ( $buf =~ s/\n$// ) {
+               return $buf;
+       }
+       die "A non-binary line MUST be terminated by an LF.\n"
+           . "Received: '$buf'";
+}
+
+sub packet_txt_read {
+       my ( $res, $buf ) = packet_bin_read();
+       if ( $res != -1 and $buf ne '' ) {
+               $buf = remove_final_lf_or_die($buf);
+       }
+       return ( $res, $buf );
+}
+
+# Read a text packet, expecting that it is in the form "key=value" for
+# the given $key.  An EOF does not trigger any error and is reported
+# back to the caller (like packet_txt_read() does).  Die if the "key"
+# part of "key=value" does not match the given $key, or the value part
+# is empty.
+sub packet_key_val_read {
+       my ( $key ) = @_;
+       my ( $res, $buf ) = packet_txt_read();
+       if ( $res == -1 or ( $buf =~ s/^$key=// and $buf ne '' ) ) {
+               return ( $res, $buf );
+       }
+       die "bad $key: '$buf'";
+}
+
+sub packet_bin_write {
+       my $buf = shift;
+       print STDOUT sprintf( "%04x", length($buf) + 4 );
+       print STDOUT $buf;
+       STDOUT->flush();
+}
+
+sub packet_txt_write {
+       packet_bin_write( $_[0] . "\n" );
+}
+
+sub packet_flush {
+       print STDOUT sprintf( "%04x", 0 );
+       STDOUT->flush();
+}
+
+sub packet_initialize {
+       my ($name, $version) = @_;
+
+       packet_compare_lists([0, $name . "-client"], packet_txt_read()) ||
+               die "bad initialize";
+       packet_compare_lists([0, "version=" . $version], packet_txt_read()) ||
+               die "bad version";
+       packet_compare_lists([1, ""], packet_bin_read()) ||
+               die "bad version end";
+
+       packet_txt_write( $name . "-server" );
+       packet_txt_write( "version=" . $version );
+       packet_flush();
+}
+
+sub packet_read_capabilities {
+       my @cap;
+       while (1) {
+               my ( $res, $buf ) = packet_bin_read();
+               if ( $res == -1 ) {
+                       die "unexpected EOF when reading capabilities";
+               }
+               return ( $res, @cap ) if ( $res != 0 );
+               $buf = remove_final_lf_or_die($buf);
+               unless ( $buf =~ s/capability=// ) {
+                       die "bad capability buf: '$buf'";
+               }
+               push @cap, $buf;
+       }
+}
+
+# Read remote capabilities and check them against capabilities we require
+sub packet_read_and_check_capabilities {
+       my @required_caps = @_;
+       my ($res, @remote_caps) = packet_read_capabilities();
+       my %remote_caps = map { $_ => 1 } @remote_caps;
+       foreach (@required_caps) {
+               unless (exists($remote_caps{$_})) {
+                       die "required '$_' capability not available from remote" ;
+               }
+       }
+       return %remote_caps;
+}
+
+# Check our capabilities we want to advertise against the remote ones
+# and then advertise our capabilities
+sub packet_check_and_write_capabilities {
+       my ($remote_caps, @our_caps) = @_;
+       foreach (@our_caps) {
+               unless (exists($remote_caps->{$_})) {
+                       die "our capability '$_' is not available from remote"
+               }
+               packet_txt_write( "capability=" . $_ );
+       }
+       packet_flush();
+}
+
+1;
index 15d96fcc7a5a81ae0304bc51a603fe54bcbb95c6..f657de20e3983af8f990114c24bddd2775845d88 100644 (file)
@@ -30,6 +30,7 @@ instdir_SQ = $(subst ','\'',$(prefix)/lib)
 modules += Git
 modules += Git/I18N
 modules += Git/IndexInfo
+modules += Git/Packet
 modules += Git/SVN
 modules += Git/SVN/Memoize/YAML
 modules += Git/SVN/Fetcher
index 93ea311443a37b81b85a72dc569c715417d7ef29..2827ca772a3703f71bc588d0f6cacd5caa318fe7 100644 (file)
@@ -188,6 +188,12 @@ static int packet_write_gently(const int fd_out, const char *buf, size_t size)
        return 0;
 }
 
+void packet_write(int fd_out, const char *buf, size_t size)
+{
+       if (packet_write_gently(fd_out, buf, size))
+               die_errno("packet write failed");
+}
+
 void packet_buf_write(struct strbuf *buf, const char *fmt, ...)
 {
        va_list args;
index 66ef610fc4f7de2f6b7572f1e5fb36438c5dec19..3dad583e2d02264c4a831c939ae0e13a54de2ff6 100644 (file)
@@ -22,6 +22,7 @@
 void packet_flush(int fd);
 void packet_write_fmt(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
 void packet_buf_flush(struct strbuf *buf);
+void packet_write(int fd_out, const char *buf, size_t size);
 void packet_buf_write(struct strbuf *buf, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
 int packet_flush_gently(int fd);
 int packet_write_fmt_gently(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
index 70a4c808783bab92b4b6319e3dbd8e16641b1918..2a83255e4eeaf8e8ce74516880b0333b23d6f583 100644 (file)
@@ -4,6 +4,7 @@
 #include "cache.h"
 #include "pathspec.h"
 #include "dir.h"
+#include "fsmonitor.h"
 
 #ifdef NO_PTHREADS
 static void preload_index(struct index_state *index,
@@ -55,15 +56,18 @@ static void *preload_thread(void *_data)
                        continue;
                if (ce_skip_worktree(ce))
                        continue;
+               if (ce->ce_flags & CE_FSMONITOR_VALID)
+                       continue;
                if (!ce_path_match(ce, &p->pathspec, NULL))
                        continue;
                if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce)))
                        continue;
                if (lstat(ce->name, &st))
                        continue;
-               if (ie_match_stat(index, ce, &st, CE_MATCH_RACY_IS_DIRTY))
+               if (ie_match_stat(index, ce, &st, CE_MATCH_RACY_IS_DIRTY|CE_MATCH_IGNORE_FSMONITOR))
                        continue;
                ce_mark_uptodate(ce);
+               mark_fsmonitor_valid(ce);
        } while (--nr > 0);
        cache_def_clear(&cache);
        return NULL;
@@ -79,6 +83,8 @@ static void preload_index(struct index_state *index,
                return;
 
        threads = index->cache_nr / THREAD_COST;
+       if ((index->cache_nr > 1) && (threads < 2) && getenv("GIT_FORCE_PRELOAD_TEST"))
+               threads = 2;
        if (threads < 2)
                return;
        if (threads > MAX_PARALLEL)
index 2f6b0ae6c1486660bb37442b991cb1140a9f2bb6..f7ce4902301490d73bdd79bd396cf7bbe5f893ea 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -1186,11 +1186,11 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                strbuf_addstr(sb, get_revision_mark(NULL, commit));
                return 1;
        case 'd':
-               load_ref_decorations(DECORATE_SHORT_REFS);
+               load_ref_decorations(NULL, DECORATE_SHORT_REFS);
                format_decorations(sb, commit, c->auto_color);
                return 1;
        case 'D':
-               load_ref_decorations(DECORATE_SHORT_REFS);
+               load_ref_decorations(NULL, DECORATE_SHORT_REFS);
                format_decorations_extended(sb, commit, c->auto_color, "", ", ", "");
                return 1;
        case 'g':               /* reflog info */
diff --git a/protocol.c b/protocol.c
new file mode 100644 (file)
index 0000000..43012b7
--- /dev/null
@@ -0,0 +1,79 @@
+#include "cache.h"
+#include "config.h"
+#include "protocol.h"
+
+static enum protocol_version parse_protocol_version(const char *value)
+{
+       if (!strcmp(value, "0"))
+               return protocol_v0;
+       else if (!strcmp(value, "1"))
+               return protocol_v1;
+       else
+               return protocol_unknown_version;
+}
+
+enum protocol_version get_protocol_version_config(void)
+{
+       const char *value;
+       if (!git_config_get_string_const("protocol.version", &value)) {
+               enum protocol_version version = parse_protocol_version(value);
+
+               if (version == protocol_unknown_version)
+                       die("unknown value for config 'protocol.version': %s",
+                           value);
+
+               return version;
+       }
+
+       return protocol_v0;
+}
+
+enum protocol_version determine_protocol_version_server(void)
+{
+       const char *git_protocol = getenv(GIT_PROTOCOL_ENVIRONMENT);
+       enum protocol_version version = protocol_v0;
+
+       /*
+        * Determine which protocol version the client has requested.  Since
+        * multiple 'version' keys can be sent by the client, indicating that
+        * the client is okay to speak any of them, select the greatest version
+        * that the client has requested.  This is due to the assumption that
+        * the most recent protocol version will be the most state-of-the-art.
+        */
+       if (git_protocol) {
+               struct string_list list = STRING_LIST_INIT_DUP;
+               const struct string_list_item *item;
+               string_list_split(&list, git_protocol, ':', -1);
+
+               for_each_string_list_item(item, &list) {
+                       const char *value;
+                       enum protocol_version v;
+
+                       if (skip_prefix(item->string, "version=", &value)) {
+                               v = parse_protocol_version(value);
+                               if (v > version)
+                                       version = v;
+                       }
+               }
+
+               string_list_clear(&list, 0);
+       }
+
+       return version;
+}
+
+enum protocol_version determine_protocol_version_client(const char *server_response)
+{
+       enum protocol_version version = protocol_v0;
+
+       if (skip_prefix(server_response, "version ", &server_response)) {
+               version = parse_protocol_version(server_response);
+
+               if (version == protocol_unknown_version)
+                       die("server is speaking an unknown protocol");
+               if (version == protocol_v0)
+                       die("protocol error: server explicitly said version 0");
+       }
+
+       return version;
+}
diff --git a/protocol.h b/protocol.h
new file mode 100644 (file)
index 0000000..1b2bc94
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef PROTOCOL_H
+#define PROTOCOL_H
+
+enum protocol_version {
+       protocol_unknown_version = -1,
+       protocol_v0 = 0,
+       protocol_v1 = 1,
+};
+
+/*
+ * Used by a client to determine which protocol version to request be used when
+ * communicating with a server, reflecting the configured value of the
+ * 'protocol.version' config.  If unconfigured, a value of 'protocol_v0' is
+ * returned.
+ */
+extern enum protocol_version get_protocol_version_config(void);
+
+/*
+ * Used by a server to determine which protocol version should be used based on
+ * a client's request, communicated via the 'GIT_PROTOCOL' environment variable
+ * by setting appropriate values for the key 'version'.  If a client doesn't
+ * request a particular protocol version, a default of 'protocol_v0' will be
+ * used.
+ */
+extern enum protocol_version determine_protocol_version_server(void);
+
+/*
+ * Used by a client to determine which protocol version the server is speaking
+ * based on the server's initial response.
+ */
+extern enum protocol_version determine_protocol_version_client(const char *server_response);
+
+#endif /* PROTOCOL_H */
index 65f4fe8375d59234d5e58f87a8593fc50e6336a2..2eb81a66b941325dc339d61a0ab804a60cbb8be2 100644 (file)
@@ -19,6 +19,7 @@
 #include "varint.h"
 #include "split-index.h"
 #include "utf8.h"
+#include "fsmonitor.h"
 
 /* Mask for the name length in ce_flags in the on-disk index */
 
 #define CACHE_EXT_RESOLVE_UNDO 0x52455543 /* "REUC" */
 #define CACHE_EXT_LINK 0x6c696e6b        /* "link" */
 #define CACHE_EXT_UNTRACKED 0x554E5452   /* "UNTR" */
+#define CACHE_EXT_FSMONITOR 0x46534D4E   /* "FSMN" */
 
 /* changes that can be kept in $GIT_DIR/index (basically all extensions) */
 #define EXTMASK (RESOLVE_UNDO_CHANGED | CACHE_TREE_CHANGED | \
                 CE_ENTRY_ADDED | CE_ENTRY_REMOVED | CE_ENTRY_CHANGED | \
-                SPLIT_INDEX_ORDERED | UNTRACKED_CHANGED)
+                SPLIT_INDEX_ORDERED | UNTRACKED_CHANGED | FSMONITOR_CHANGED)
 
 struct index_state the_index;
 static const char *alternate_index_output;
@@ -62,6 +64,7 @@ static void replace_index_entry(struct index_state *istate, int nr, struct cache
        free(old);
        set_index_entry(istate, nr, ce);
        ce->ce_flags |= CE_UPDATE_IN_BASE;
+       mark_fsmonitor_invalid(istate, ce);
        istate->cache_changed |= CE_ENTRY_CHANGED;
 }
 
@@ -150,8 +153,10 @@ void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
        if (assume_unchanged)
                ce->ce_flags |= CE_VALID;
 
-       if (S_ISREG(st->st_mode))
+       if (S_ISREG(st->st_mode)) {
                ce_mark_uptodate(ce);
+               mark_fsmonitor_valid(ce);
+       }
 }
 
 static int ce_compare_data(const struct cache_entry *ce, struct stat *st)
@@ -191,7 +196,7 @@ static int ce_compare_link(const struct cache_entry *ce, size_t expected_size)
 
 static int ce_compare_gitlink(const struct cache_entry *ce)
 {
-       unsigned char sha1[20];
+       struct object_id oid;
 
        /*
         * We don't actually require that the .git directory
@@ -201,9 +206,9 @@ static int ce_compare_gitlink(const struct cache_entry *ce)
         *
         * If so, we consider it always to match.
         */
-       if (resolve_gitlink_ref(ce->name, "HEAD", sha1) < 0)
+       if (resolve_gitlink_ref(ce->name, "HEAD", &oid) < 0)
                return 0;
-       return hashcmp(sha1, ce->oid.hash);
+       return oidcmp(&oid, &ce->oid);
 }
 
 static int ce_modified_check_fs(const struct cache_entry *ce, struct stat *st)
@@ -301,7 +306,7 @@ int match_stat_data_racy(const struct index_state *istate,
        return match_stat_data(sd, st);
 }
 
-int ie_match_stat(const struct index_state *istate,
+int ie_match_stat(struct index_state *istate,
                  const struct cache_entry *ce, struct stat *st,
                  unsigned int options)
 {
@@ -309,7 +314,10 @@ int ie_match_stat(const struct index_state *istate,
        int ignore_valid = options & CE_MATCH_IGNORE_VALID;
        int ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE;
        int assume_racy_is_modified = options & CE_MATCH_RACY_IS_DIRTY;
+       int ignore_fsmonitor = options & CE_MATCH_IGNORE_FSMONITOR;
 
+       if (!ignore_fsmonitor)
+               refresh_fsmonitor(istate);
        /*
         * If it's marked as always valid in the index, it's
         * valid whatever the checked-out copy says.
@@ -320,6 +328,8 @@ int ie_match_stat(const struct index_state *istate,
                return 0;
        if (!ignore_valid && (ce->ce_flags & CE_VALID))
                return 0;
+       if (!ignore_fsmonitor && (ce->ce_flags & CE_FSMONITOR_VALID))
+               return 0;
 
        /*
         * Intent-to-add entries have not been added, so the index entry
@@ -357,7 +367,7 @@ int ie_match_stat(const struct index_state *istate,
        return changed;
 }
 
-int ie_modified(const struct index_state *istate,
+int ie_modified(struct index_state *istate,
                const struct cache_entry *ce,
                struct stat *st, unsigned int options)
 {
@@ -631,13 +641,17 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
 {
        int size, namelen, was_same;
        mode_t st_mode = st->st_mode;
-       struct cache_entry *ce, *alias;
+       struct cache_entry *ce, *alias = NULL;
        unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE|CE_MATCH_RACY_IS_DIRTY;
        int verbose = flags & (ADD_CACHE_VERBOSE | ADD_CACHE_PRETEND);
        int pretend = flags & ADD_CACHE_PRETEND;
        int intent_only = flags & ADD_CACHE_INTENT;
        int add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|
                          (intent_only ? ADD_CACHE_NEW_ONLY : 0));
+       int newflags = HASH_WRITE_OBJECT;
+
+       if (flags & HASH_RENORMALIZE)
+               newflags |= HASH_RENORMALIZE;
 
        if (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode))
                return error("%s: can only add regular files, symbolic links or git-directories", path);
@@ -678,19 +692,23 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
        if (ignore_case) {
                adjust_dirname_case(istate, ce->name);
        }
+       if (!(flags & HASH_RENORMALIZE)) {
+               alias = index_file_exists(istate, ce->name,
+                                         ce_namelen(ce), ignore_case);
+               if (alias &&
+                   !ce_stage(alias) &&
+                   !ie_match_stat(istate, alias, st, ce_option)) {
+                       /* Nothing changed, really */
+                       if (!S_ISGITLINK(alias->ce_mode))
+                               ce_mark_uptodate(alias);
+                       alias->ce_flags |= CE_ADDED;
 
-       alias = index_file_exists(istate, ce->name, ce_namelen(ce), ignore_case);
-       if (alias && !ce_stage(alias) && !ie_match_stat(istate, alias, st, ce_option)) {
-               /* Nothing changed, really */
-               if (!S_ISGITLINK(alias->ce_mode))
-                       ce_mark_uptodate(alias);
-               alias->ce_flags |= CE_ADDED;
-
-               free(ce);
-               return 0;
+                       free(ce);
+                       return 0;
+               }
        }
        if (!intent_only) {
-               if (index_path(&ce->oid, path, st, HASH_WRITE_OBJECT)) {
+               if (index_path(&ce->oid, path, st, newflags)) {
                        free(ce);
                        return error("unable to index file %s", path);
                }
@@ -778,6 +796,7 @@ int chmod_index_entry(struct index_state *istate, struct cache_entry *ce,
        }
        cache_tree_invalidate_path(istate, ce->name);
        ce->ce_flags |= CE_UPDATE_IN_BASE;
+       mark_fsmonitor_invalid(istate, ce);
        istate->cache_changed |= CE_ENTRY_CHANGED;
 
        return 0;
@@ -1229,10 +1248,13 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
        int ignore_valid = options & CE_MATCH_IGNORE_VALID;
        int ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE;
        int ignore_missing = options & CE_MATCH_IGNORE_MISSING;
+       int ignore_fsmonitor = options & CE_MATCH_IGNORE_FSMONITOR;
 
        if (!refresh || ce_uptodate(ce))
                return ce;
 
+       if (!ignore_fsmonitor)
+               refresh_fsmonitor(istate);
        /*
         * CE_VALID or CE_SKIP_WORKTREE means the user promised us
         * that the change to the work tree does not matter and told
@@ -1246,6 +1268,10 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
                ce_mark_uptodate(ce);
                return ce;
        }
+       if (!ignore_fsmonitor && (ce->ce_flags & CE_FSMONITOR_VALID)) {
+               ce_mark_uptodate(ce);
+               return ce;
+       }
 
        if (has_symlink_leading_path(ce->name, ce_namelen(ce))) {
                if (ignore_missing)
@@ -1283,8 +1309,10 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
                         * because CE_UPTODATE flag is in-core only;
                         * we are not going to write this change out.
                         */
-                       if (!S_ISGITLINK(ce->ce_mode))
+                       if (!S_ISGITLINK(ce->ce_mode)) {
                                ce_mark_uptodate(ce);
+                               mark_fsmonitor_valid(ce);
+                       }
                        return ce;
                }
        }
@@ -1392,6 +1420,7 @@ int refresh_index(struct index_state *istate, unsigned int flags,
                                 */
                                ce->ce_flags &= ~CE_VALID;
                                ce->ce_flags |= CE_UPDATE_IN_BASE;
+                               mark_fsmonitor_invalid(istate, ce);
                                istate->cache_changed |= CE_ENTRY_CHANGED;
                        }
                        if (quiet)
@@ -1511,6 +1540,9 @@ struct ondisk_cache_entry_extended {
 /* Allow fsck to force verification of the index checksum. */
 int verify_index_checksum;
 
+/* Allow fsck to force verification of the cache entry order. */
+int verify_ce_order;
+
 static int verify_hdr(struct cache_header *hdr, unsigned long size)
 {
        git_SHA_CTX c;
@@ -1551,6 +1583,9 @@ static int read_index_extension(struct index_state *istate,
        case CACHE_EXT_UNTRACKED:
                istate->untracked = read_untracked_extension(data, sz);
                break;
+       case CACHE_EXT_FSMONITOR:
+               read_fsmonitor_extension(istate, data, sz);
+               break;
        default:
                if (*ext < 'A' || 'Z' < *ext)
                        return error("index uses %.4s extension, which we do not understand",
@@ -1668,6 +1703,9 @@ static void check_ce_order(struct index_state *istate)
 {
        unsigned int i;
 
+       if (!verify_ce_order)
+               return;
+
        for (i = 1; i < istate->cache_nr; i++) {
                struct cache_entry *ce = istate->cache[i - 1];
                struct cache_entry *next_ce = istate->cache[i];
@@ -1723,6 +1761,7 @@ static void post_read_index_from(struct index_state *istate)
        check_ce_order(istate);
        tweak_untracked_cache(istate);
        tweak_split_index(istate);
+       tweak_fsmonitor(istate);
 }
 
 /* remember to discard_cache() before reading a different cache! */
@@ -2176,17 +2215,22 @@ static int has_racy_timestamp(struct index_state *istate)
        return 0;
 }
 
-/*
- * Opportunistically update the index but do not complain if we can't
- */
 void update_index_if_able(struct index_state *istate, struct lock_file *lockfile)
 {
        if ((istate->cache_changed || has_racy_timestamp(istate)) &&
-           verify_index(istate) &&
-           write_locked_index(istate, lockfile, COMMIT_LOCK))
+           verify_index(istate))
+               write_locked_index(istate, lockfile, COMMIT_LOCK);
+       else
                rollback_lock_file(lockfile);
 }
 
+/*
+ * On success, `tempfile` is closed. If it is the temporary file
+ * of a `struct lock_file`, we will therefore effectively perform
+ * a 'close_lock_file_gently()`. Since that is an implementation
+ * detail of lockfiles, callers of `do_write_index()` should not
+ * rely on it.
+ */
 static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                          int strip_extensions)
 {
@@ -2309,12 +2353,21 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                if (err)
                        return -1;
        }
+       if (!strip_extensions && istate->fsmonitor_last_update) {
+               struct strbuf sb = STRBUF_INIT;
+
+               write_fsmonitor_extension(&sb, istate);
+               err = write_index_ext_header(&c, newfd, CACHE_EXT_FSMONITOR, sb.len) < 0
+                       || ce_write(&c, newfd, sb.buf, sb.len) < 0;
+               strbuf_release(&sb);
+               if (err)
+                       return -1;
+       }
 
        if (ce_flush(&c, newfd, istate->sha1))
                return -1;
        if (close_tempfile_gently(tempfile)) {
                error(_("could not close '%s'"), tempfile->filename.buf);
-               delete_tempfile(&tempfile);
                return -1;
        }
        if (stat(tempfile->filename.buf, &st))
@@ -2343,14 +2396,9 @@ static int do_write_locked_index(struct index_state *istate, struct lock_file *l
        int ret = do_write_index(istate, lock->tempfile, 0);
        if (ret)
                return ret;
-       assert((flags & (COMMIT_LOCK | CLOSE_LOCK)) !=
-              (COMMIT_LOCK | CLOSE_LOCK));
        if (flags & COMMIT_LOCK)
                return commit_locked_index(lock);
-       else if (flags & CLOSE_LOCK)
-               return close_lock_file_gently(lock);
-       else
-               return ret;
+       return close_lock_file_gently(lock);
 }
 
 static int write_split_index(struct index_state *istate,
@@ -2495,11 +2543,15 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
        int new_shared_index, ret;
        struct split_index *si = istate->split_index;
 
+       if (istate->fsmonitor_last_update)
+               fill_fsmonitor_bitmap(istate);
+
        if (!si || alternate_index_output ||
            (istate->cache_changed & ~EXTMASK)) {
                if (si)
                        hashclr(si->base_sha1);
-               return do_write_locked_index(istate, lock, flags);
+               ret = do_write_locked_index(istate, lock, flags);
+               goto out;
        }
 
        if (getenv("GIT_TEST_SPLIT_INDEX")) {
@@ -2515,7 +2567,7 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
        if (new_shared_index) {
                ret = write_shared_index(istate, lock, flags);
                if (ret)
-                       return ret;
+                       goto out;
        }
 
        ret = write_split_index(istate, lock, flags);
@@ -2524,6 +2576,9 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
        if (!ret && !new_shared_index)
                freshen_shared_index(sha1_to_hex(si->base_sha1), 1);
 
+out:
+       if (flags & COMMIT_LOCK)
+               rollback_lock_file(lock);
        return ret;
 }
 
index e728b15b3aeec8e8a9154d4ee4a4284cf06b5931..3f9161707e66be86eabaf7347da23a2450ffdd9f 100644 (file)
@@ -76,9 +76,11 @@ static struct used_atom {
                char color[COLOR_MAXLEN];
                struct align align;
                struct {
-                       enum { RR_REF, RR_TRACK, RR_TRACKSHORT } option;
+                       enum {
+                               RR_REF, RR_TRACK, RR_TRACKSHORT, RR_REMOTE_NAME, RR_REMOTE_REF
+                       } option;
                        struct refname_atom refname;
-                       unsigned int nobracket : 1;
+                       unsigned int nobracket : 1, push : 1, push_remote : 1;
                } remote_ref;
                struct {
                        enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_TRAILERS } option;
@@ -138,6 +140,9 @@ static void remote_ref_atom_parser(const struct ref_format *format, struct used_
        struct string_list params = STRING_LIST_INIT_DUP;
        int i;
 
+       if (!strcmp(atom->name, "push") || starts_with(atom->name, "push:"))
+               atom->u.remote_ref.push = 1;
+
        if (!arg) {
                atom->u.remote_ref.option = RR_REF;
                refname_atom_parser_internal(&atom->u.remote_ref.refname,
@@ -157,7 +162,13 @@ static void remote_ref_atom_parser(const struct ref_format *format, struct used_
                        atom->u.remote_ref.option = RR_TRACKSHORT;
                else if (!strcmp(s, "nobracket"))
                        atom->u.remote_ref.nobracket = 1;
-               else {
+               else if (!strcmp(s, "remotename")) {
+                       atom->u.remote_ref.option = RR_REMOTE_NAME;
+                       atom->u.remote_ref.push_remote = 1;
+               } else if (!strcmp(s, "remoteref")) {
+                       atom->u.remote_ref.option = RR_REMOTE_REF;
+                       atom->u.remote_ref.push_remote = 1;
+               } else {
                        atom->u.remote_ref.option = RR_REF;
                        refname_atom_parser_internal(&atom->u.remote_ref.refname,
                                                     arg, atom->name);
@@ -1268,6 +1279,25 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
                        *s = ">";
                else
                        *s = "<>";
+       } else if (atom->u.remote_ref.option == RR_REMOTE_NAME) {
+               int explicit;
+               const char *remote = atom->u.remote_ref.push ?
+                       pushremote_for_branch(branch, &explicit) :
+                       remote_for_branch(branch, &explicit);
+               if (explicit)
+                       *s = xstrdup(remote);
+               else
+                       *s = "";
+       } else if (atom->u.remote_ref.option == RR_REMOTE_REF) {
+               int explicit;
+               const char *merge;
+
+               merge = remote_ref_for_branch(branch, atom->u.remote_ref.push,
+                                             &explicit);
+               if (explicit)
+                       *s = xstrdup(merge);
+               else
+                       *s = "";
        } else
                die("BUG: unhandled RR_* enum");
 }
@@ -1377,16 +1407,20 @@ static void populate_value(struct ref_array_item *ref)
                        if (refname)
                                fill_remote_ref_details(atom, refname, branch, &v->s);
                        continue;
-               } else if (starts_with(name, "push")) {
+               } else if (atom->u.remote_ref.push) {
                        const char *branch_name;
                        if (!skip_prefix(ref->refname, "refs/heads/",
                                         &branch_name))
                                continue;
                        branch = branch_get(branch_name);
 
-                       refname = branch_get_push(branch, NULL);
-                       if (!refname)
-                               continue;
+                       if (atom->u.remote_ref.push_remote)
+                               refname = NULL;
+                       else {
+                               refname = branch_get_push(branch, NULL);
+                               if (!refname)
+                                       continue;
+                       }
                        fill_remote_ref_details(atom, refname, branch, &v->s);
                        continue;
                } else if (starts_with(name, "color:")) {
index 842b2f77dc3e40a2eb296bfade00d9d2104ff9e1..5008bbf6ada3707009722e3a3ca5b37437fbfb24 100644 (file)
@@ -161,7 +161,7 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
                        struct object_id oid;
                        char *b;
                        int ret = dwim_log(branch, strlen(branch),
-                                          oid.hash, &b);
+                                          &oid, &b);
                        if (ret > 1)
                                free(b);
                        else if (ret == 1) {
diff --git a/refs.c b/refs.c
index c590a992fb19ce92dcf0932b58f83b65028f8316..20ba82b4343ff2ef72cea32deec8a8d7fbd6def7 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -194,21 +194,21 @@ int ref_resolves_to_object(const char *refname,
 
 char *refs_resolve_refdup(struct ref_store *refs,
                          const char *refname, int resolve_flags,
-                         unsigned char *sha1, int *flags)
+                         struct object_id *oid, int *flags)
 {
        const char *result;
 
        result = refs_resolve_ref_unsafe(refs, refname, resolve_flags,
-                                        sha1, flags);
+                                        oid, flags);
        return xstrdup_or_null(result);
 }
 
 char *resolve_refdup(const char *refname, int resolve_flags,
-                    unsigned char *sha1, int *flags)
+                    struct object_id *oid, int *flags)
 {
        return refs_resolve_refdup(get_main_ref_store(),
                                   refname, resolve_flags,
-                                  sha1, flags);
+                                  oid, flags);
 }
 
 /* The argument to filter_refs */
@@ -219,22 +219,22 @@ struct ref_filter {
 };
 
 int refs_read_ref_full(struct ref_store *refs, const char *refname,
-                      int resolve_flags, unsigned char *sha1, int *flags)
+                      int resolve_flags, struct object_id *oid, int *flags)
 {
-       if (refs_resolve_ref_unsafe(refs, refname, resolve_flags, sha1, flags))
+       if (refs_resolve_ref_unsafe(refs, refname, resolve_flags, oid, flags))
                return 0;
        return -1;
 }
 
-int read_ref_full(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)
+int read_ref_full(const char *refname, int resolve_flags, struct object_id *oid, int *flags)
 {
        return refs_read_ref_full(get_main_ref_store(), refname,
-                                 resolve_flags, sha1, flags);
+                                 resolve_flags, oid, flags);
 }
 
-int read_ref(const char *refname, unsigned char *sha1)
+int read_ref(const char *refname, struct object_id *oid)
 {
-       return read_ref_full(refname, RESOLVE_REF_READING, sha1, NULL);
+       return read_ref_full(refname, RESOLVE_REF_READING, oid, NULL);
 }
 
 int ref_exists(const char *refname)
@@ -242,6 +242,50 @@ int ref_exists(const char *refname)
        return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, NULL, NULL);
 }
 
+static int match_ref_pattern(const char *refname,
+                            const struct string_list_item *item)
+{
+       int matched = 0;
+       if (item->util == NULL) {
+               if (!wildmatch(item->string, refname, 0))
+                       matched = 1;
+       } else {
+               const char *rest;
+               if (skip_prefix(refname, item->string, &rest) &&
+                   (!*rest || *rest == '/'))
+                       matched = 1;
+       }
+       return matched;
+}
+
+int ref_filter_match(const char *refname,
+                    const struct string_list *include_patterns,
+                    const struct string_list *exclude_patterns)
+{
+       struct string_list_item *item;
+
+       if (exclude_patterns && exclude_patterns->nr) {
+               for_each_string_list_item(item, exclude_patterns) {
+                       if (match_ref_pattern(refname, item))
+                               return 0;
+               }
+       }
+
+       if (include_patterns && include_patterns->nr) {
+               int found = 0;
+               for_each_string_list_item(item, include_patterns) {
+                       if (match_ref_pattern(refname, item)) {
+                               found = 1;
+                               break;
+                       }
+               }
+
+               if (!found)
+                       return 0;
+       }
+       return 1;
+}
+
 static int filter_refs(const char *refname, const struct object_id *oid,
                           int flags, void *data)
 {
@@ -252,12 +296,12 @@ static int filter_refs(const char *refname, const struct object_id *oid,
        return filter->fn(refname, oid, flags, filter->cb_data);
 }
 
-enum peel_status peel_object(const unsigned char *name, unsigned char *sha1)
+enum peel_status peel_object(const struct object_id *name, struct object_id *oid)
 {
-       struct object *o = lookup_unknown_object(name);
+       struct object *o = lookup_unknown_object(name->hash);
 
        if (o->type == OBJ_NONE) {
-               int type = sha1_object_info(name, NULL);
+               int type = sha1_object_info(name->hash, NULL);
                if (type < 0 || !object_as_type(o, type, 0))
                        return PEEL_INVALID;
        }
@@ -269,7 +313,7 @@ enum peel_status peel_object(const unsigned char *name, unsigned char *sha1)
        if (!o)
                return PEEL_INVALID;
 
-       hashcpy(sha1, o->oid.hash);
+       oidcpy(oid, &o->oid);
        return PEEL_PEELED;
 }
 
@@ -362,13 +406,34 @@ int head_ref_namespaced(each_ref_fn fn, void *cb_data)
        int flag;
 
        strbuf_addf(&buf, "%sHEAD", get_git_namespace());
-       if (!read_ref_full(buf.buf, RESOLVE_REF_READING, oid.hash, &flag))
+       if (!read_ref_full(buf.buf, RESOLVE_REF_READING, &oid, &flag))
                ret = fn(buf.buf, &oid, flag, cb_data);
        strbuf_release(&buf);
 
        return ret;
 }
 
+void normalize_glob_ref(struct string_list_item *item, const char *prefix,
+                       const char *pattern)
+{
+       struct strbuf normalized_pattern = STRBUF_INIT;
+
+       if (*pattern == '/')
+               BUG("pattern must not start with '/'");
+
+       if (prefix) {
+               strbuf_addstr(&normalized_pattern, prefix);
+       }
+       else if (!starts_with(pattern, "refs/"))
+               strbuf_addstr(&normalized_pattern, "refs/");
+       strbuf_addstr(&normalized_pattern, pattern);
+       strbuf_strip_suffix(&normalized_pattern, "/");
+
+       item->string = strbuf_detach(&normalized_pattern, NULL);
+       item->util = has_glob_specials(pattern) ? NULL : item->string;
+       strbuf_release(&normalized_pattern);
+}
+
 int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
        const char *prefix, void *cb_data)
 {
@@ -456,15 +521,15 @@ static char *substitute_branch_name(const char **string, int *len)
        return NULL;
 }
 
-int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
+int dwim_ref(const char *str, int len, struct object_id *oid, char **ref)
 {
        char *last_branch = substitute_branch_name(&str, &len);
-       int   refs_found  = expand_ref(str, len, sha1, ref);
+       int   refs_found  = expand_ref(str, len, oid, ref);
        free(last_branch);
        return refs_found;
 }
 
-int expand_ref(const char *str, int len, unsigned char *sha1, char **ref)
+int expand_ref(const char *str, int len, struct object_id *oid, char **ref)
 {
        const char **p, *r;
        int refs_found = 0;
@@ -472,11 +537,11 @@ int expand_ref(const char *str, int len, unsigned char *sha1, char **ref)
 
        *ref = NULL;
        for (p = ref_rev_parse_rules; *p; p++) {
-               unsigned char sha1_from_ref[20];
-               unsigned char *this_result;
+               struct object_id oid_from_ref;
+               struct object_id *this_result;
                int flag;
 
-               this_result = refs_found ? sha1_from_ref : sha1;
+               this_result = refs_found ? &oid_from_ref : oid;
                strbuf_reset(&fullref);
                strbuf_addf(&fullref, *p, len, str);
                r = resolve_ref_unsafe(fullref.buf, RESOLVE_REF_READING,
@@ -496,7 +561,7 @@ int expand_ref(const char *str, int len, unsigned char *sha1, char **ref)
        return refs_found;
 }
 
-int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
+int dwim_log(const char *str, int len, struct object_id *oid, char **log)
 {
        char *last_branch = substitute_branch_name(&str, &len);
        const char **p;
@@ -505,13 +570,13 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
 
        *log = NULL;
        for (p = ref_rev_parse_rules; *p; p++) {
-               unsigned char hash[20];
+               struct object_id hash;
                const char *ref, *it;
 
                strbuf_reset(&path);
                strbuf_addf(&path, *p, len, str);
                ref = resolve_ref_unsafe(path.buf, RESOLVE_REF_READING,
-                                        hash, NULL);
+                                        &hash, NULL);
                if (!ref)
                        continue;
                if (reflog_exists(path.buf))
@@ -522,7 +587,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
                        continue;
                if (!logs_found++) {
                        *log = xstrdup(it);
-                       hashcpy(sha1, hash);
+                       oidcpy(oid, &hash);
                }
                if (!warn_ambiguous_refs)
                        break;
@@ -574,8 +639,8 @@ long get_files_ref_lock_timeout_ms(void)
        return timeout_ms;
 }
 
-static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,
-                          const unsigned char *old_sha1, struct strbuf *err)
+static int write_pseudoref(const char *pseudoref, const struct object_id *oid,
+                          const struct object_id *old_oid, struct strbuf *err)
 {
        const char *filename;
        int fd;
@@ -583,7 +648,10 @@ static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,
        struct strbuf buf = STRBUF_INIT;
        int ret = -1;
 
-       strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1));
+       if (!oid)
+               return 0;
+
+       strbuf_addf(&buf, "%s\n", oid_to_hex(oid));
 
        filename = git_path("%s", pseudoref);
        fd = hold_lock_file_for_update_timeout(&lock, filename,
@@ -595,12 +663,12 @@ static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,
                goto done;
        }
 
-       if (old_sha1) {
-               unsigned char actual_old_sha1[20];
+       if (old_oid) {
+               struct object_id actual_old_oid;
 
-               if (read_ref(pseudoref, actual_old_sha1))
+               if (read_ref(pseudoref, &actual_old_oid))
                        die("could not read ref '%s'", pseudoref);
-               if (hashcmp(actual_old_sha1, old_sha1)) {
+               if (oidcmp(&actual_old_oid, old_oid)) {
                        strbuf_addf(err, "unexpected sha1 when writing '%s'", pseudoref);
                        rollback_lock_file(&lock);
                        goto done;
@@ -620,25 +688,25 @@ static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,
        return ret;
 }
 
-static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1)
+static int delete_pseudoref(const char *pseudoref, const struct object_id *old_oid)
 {
        static struct lock_file lock;
        const char *filename;
 
        filename = git_path("%s", pseudoref);
 
-       if (old_sha1 && !is_null_sha1(old_sha1)) {
+       if (old_oid && !is_null_oid(old_oid)) {
                int fd;
-               unsigned char actual_old_sha1[20];
+               struct object_id actual_old_oid;
 
                fd = hold_lock_file_for_update_timeout(
                                &lock, filename, LOCK_DIE_ON_ERROR,
                                get_files_ref_lock_timeout_ms());
                if (fd < 0)
                        die_errno(_("Could not open '%s' for writing"), filename);
-               if (read_ref(pseudoref, actual_old_sha1))
+               if (read_ref(pseudoref, &actual_old_oid))
                        die("could not read ref '%s'", pseudoref);
-               if (hashcmp(actual_old_sha1, old_sha1)) {
+               if (oidcmp(&actual_old_oid, old_oid)) {
                        warning("Unexpected sha1 when deleting %s", pseudoref);
                        rollback_lock_file(&lock);
                        return -1;
@@ -655,7 +723,7 @@ static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1
 
 int refs_delete_ref(struct ref_store *refs, const char *msg,
                    const char *refname,
-                   const unsigned char *old_sha1,
+                   const struct object_id *old_oid,
                    unsigned int flags)
 {
        struct ref_transaction *transaction;
@@ -663,12 +731,12 @@ int refs_delete_ref(struct ref_store *refs, const char *msg,
 
        if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
                assert(refs == get_main_ref_store());
-               return delete_pseudoref(refname, old_sha1);
+               return delete_pseudoref(refname, old_oid);
        }
 
        transaction = ref_store_transaction_begin(refs, &err);
        if (!transaction ||
-           ref_transaction_delete(transaction, refname, old_sha1,
+           ref_transaction_delete(transaction, refname, old_oid,
                                   flags, msg, &err) ||
            ref_transaction_commit(transaction, &err)) {
                error("%s", err.buf);
@@ -682,10 +750,10 @@ int refs_delete_ref(struct ref_store *refs, const char *msg,
 }
 
 int delete_ref(const char *msg, const char *refname,
-              const unsigned char *old_sha1, unsigned int flags)
+              const struct object_id *old_oid, unsigned int flags)
 {
        return refs_delete_ref(get_main_ref_store(), msg, refname,
-                              old_sha1, flags);
+                              old_oid, flags);
 }
 
 int copy_reflog_msg(char *buf, const char *msg)
@@ -734,11 +802,11 @@ struct read_ref_at_cb {
        timestamp_t at_time;
        int cnt;
        int reccnt;
-       unsigned char *sha1;
+       struct object_id *oid;
        int found_it;
 
-       unsigned char osha1[20];
-       unsigned char nsha1[20];
+       struct object_id ooid;
+       struct object_id noid;
        int tz;
        timestamp_t date;
        char **msg;
@@ -767,28 +835,28 @@ static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
                if (cb->cutoff_cnt)
                        *cb->cutoff_cnt = cb->reccnt - 1;
                /*
-                * we have not yet updated cb->[n|o]sha1 so they still
+                * we have not yet updated cb->[n|o]oid so they still
                 * hold the values for the previous record.
                 */
-               if (!is_null_sha1(cb->osha1)) {
-                       hashcpy(cb->sha1, noid->hash);
-                       if (hashcmp(cb->osha1, noid->hash))
+               if (!is_null_oid(&cb->ooid)) {
+                       oidcpy(cb->oid, noid);
+                       if (oidcmp(&cb->ooid, noid))
                                warning("Log for ref %s has gap after %s.",
                                        cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));
                }
                else if (cb->date == cb->at_time)
-                       hashcpy(cb->sha1, noid->hash);
-               else if (hashcmp(noid->hash, cb->sha1))
+                       oidcpy(cb->oid, noid);
+               else if (oidcmp(noid, cb->oid))
                        warning("Log for ref %s unexpectedly ended on %s.",
                                cb->refname, show_date(cb->date, cb->tz,
                                                       DATE_MODE(RFC2822)));
-               hashcpy(cb->osha1, ooid->hash);
-               hashcpy(cb->nsha1, noid->hash);
+               oidcpy(&cb->ooid, ooid);
+               oidcpy(&cb->noid, noid);
                cb->found_it = 1;
                return 1;
        }
-       hashcpy(cb->osha1, ooid->hash);
-       hashcpy(cb->nsha1, noid->hash);
+       oidcpy(&cb->ooid, ooid);
+       oidcpy(&cb->noid, noid);
        if (cb->cnt > 0)
                cb->cnt--;
        return 0;
@@ -808,15 +876,15 @@ static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid
                *cb->cutoff_tz = tz;
        if (cb->cutoff_cnt)
                *cb->cutoff_cnt = cb->reccnt;
-       hashcpy(cb->sha1, ooid->hash);
-       if (is_null_sha1(cb->sha1))
-               hashcpy(cb->sha1, noid->hash);
+       oidcpy(cb->oid, ooid);
+       if (is_null_oid(cb->oid))
+               oidcpy(cb->oid, noid);
        /* We just want the first entry */
        return 1;
 }
 
 int read_ref_at(const char *refname, unsigned int flags, timestamp_t at_time, int cnt,
-               unsigned char *sha1, char **msg,
+               struct object_id *oid, char **msg,
                timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
 {
        struct read_ref_at_cb cb;
@@ -829,7 +897,7 @@ int read_ref_at(const char *refname, unsigned int flags, timestamp_t at_time, in
        cb.cutoff_time = cutoff_time;
        cb.cutoff_tz = cutoff_tz;
        cb.cutoff_cnt = cutoff_cnt;
-       cb.sha1 = sha1;
+       cb.oid = oid;
 
        for_each_reflog_ent_reverse(refname, read_ref_at_ent, &cb);
 
@@ -894,8 +962,8 @@ void ref_transaction_free(struct ref_transaction *transaction)
 struct ref_update *ref_transaction_add_update(
                struct ref_transaction *transaction,
                const char *refname, unsigned int flags,
-               const unsigned char *new_sha1,
-               const unsigned char *old_sha1,
+               const struct object_id *new_oid,
+               const struct object_id *old_oid,
                const char *msg)
 {
        struct ref_update *update;
@@ -903,9 +971,6 @@ struct ref_update *ref_transaction_add_update(
        if (transaction->state != REF_TRANSACTION_OPEN)
                die("BUG: update called for transaction that is not open");
 
-       if ((flags & REF_ISPRUNING) && !(flags & REF_NODEREF))
-               die("BUG: REF_ISPRUNING set without REF_NODEREF");
-
        FLEX_ALLOC_STR(update, refname, refname);
        ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
        transaction->updates[transaction->nr++] = update;
@@ -913,23 +978,23 @@ struct ref_update *ref_transaction_add_update(
        update->flags = flags;
 
        if (flags & REF_HAVE_NEW)
-               hashcpy(update->new_oid.hash, new_sha1);
+               oidcpy(&update->new_oid, new_oid);
        if (flags & REF_HAVE_OLD)
-               hashcpy(update->old_oid.hash, old_sha1);
+               oidcpy(&update->old_oid, old_oid);
        update->msg = xstrdup_or_null(msg);
        return update;
 }
 
 int ref_transaction_update(struct ref_transaction *transaction,
                           const char *refname,
-                          const unsigned char *new_sha1,
-                          const unsigned char *old_sha1,
+                          const struct object_id *new_oid,
+                          const struct object_id *old_oid,
                           unsigned int flags, const char *msg,
                           struct strbuf *err)
 {
        assert(err);
 
-       if ((new_sha1 && !is_null_sha1(new_sha1)) ?
+       if ((new_oid && !is_null_oid(new_oid)) ?
            check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) :
            !refname_is_safe(refname)) {
                strbuf_addf(err, "refusing to update ref with bad name '%s'",
@@ -937,64 +1002,57 @@ int ref_transaction_update(struct ref_transaction *transaction,
                return -1;
        }
 
-       flags &= REF_TRANSACTION_UPDATE_ALLOWED_FLAGS;
+       if (flags & ~REF_TRANSACTION_UPDATE_ALLOWED_FLAGS)
+               BUG("illegal flags 0x%x passed to ref_transaction_update()", flags);
 
-       flags |= (new_sha1 ? REF_HAVE_NEW : 0) | (old_sha1 ? REF_HAVE_OLD : 0);
+       flags |= (new_oid ? REF_HAVE_NEW : 0) | (old_oid ? REF_HAVE_OLD : 0);
 
        ref_transaction_add_update(transaction, refname, flags,
-                                  new_sha1, old_sha1, msg);
+                                  new_oid, old_oid, msg);
        return 0;
 }
 
 int ref_transaction_create(struct ref_transaction *transaction,
                           const char *refname,
-                          const unsigned char *new_sha1,
+                          const struct object_id *new_oid,
                           unsigned int flags, const char *msg,
                           struct strbuf *err)
 {
-       if (!new_sha1 || is_null_sha1(new_sha1))
-               die("BUG: create called without valid new_sha1");
-       return ref_transaction_update(transaction, refname, new_sha1,
-                                     null_sha1, flags, msg, err);
+       if (!new_oid || is_null_oid(new_oid))
+               die("BUG: create called without valid new_oid");
+       return ref_transaction_update(transaction, refname, new_oid,
+                                     &null_oid, flags, msg, err);
 }
 
 int ref_transaction_delete(struct ref_transaction *transaction,
                           const char *refname,
-                          const unsigned char *old_sha1,
+                          const struct object_id *old_oid,
                           unsigned int flags, const char *msg,
                           struct strbuf *err)
 {
-       if (old_sha1 && is_null_sha1(old_sha1))
-               die("BUG: delete called with old_sha1 set to zeros");
+       if (old_oid && is_null_oid(old_oid))
+               die("BUG: delete called with old_oid set to zeros");
        return ref_transaction_update(transaction, refname,
-                                     null_sha1, old_sha1,
+                                     &null_oid, old_oid,
                                      flags, msg, err);
 }
 
 int ref_transaction_verify(struct ref_transaction *transaction,
                           const char *refname,
-                          const unsigned char *old_sha1,
+                          const struct object_id *old_oid,
                           unsigned int flags,
                           struct strbuf *err)
 {
-       if (!old_sha1)
-               die("BUG: verify called with old_sha1 set to NULL");
+       if (!old_oid)
+               die("BUG: verify called with old_oid set to NULL");
        return ref_transaction_update(transaction, refname,
-                                     NULL, old_sha1,
+                                     NULL, old_oid,
                                      flags, NULL, err);
 }
 
-int update_ref_oid(const char *msg, const char *refname,
-              const struct object_id *new_oid, const struct object_id *old_oid,
-              unsigned int flags, enum action_on_err onerr)
-{
-       return update_ref(msg, refname, new_oid ? new_oid->hash : NULL,
-               old_oid ? old_oid->hash : NULL, flags, onerr);
-}
-
 int refs_update_ref(struct ref_store *refs, const char *msg,
-                   const char *refname, const unsigned char *new_sha1,
-                   const unsigned char *old_sha1, unsigned int flags,
+                   const char *refname, const struct object_id *new_oid,
+                   const struct object_id *old_oid, unsigned int flags,
                    enum action_on_err onerr)
 {
        struct ref_transaction *t = NULL;
@@ -1003,11 +1061,11 @@ int refs_update_ref(struct ref_store *refs, const char *msg,
 
        if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
                assert(refs == get_main_ref_store());
-               ret = write_pseudoref(refname, new_sha1, old_sha1, &err);
+               ret = write_pseudoref(refname, new_oid, old_oid, &err);
        } else {
                t = ref_store_transaction_begin(refs, &err);
                if (!t ||
-                   ref_transaction_update(t, refname, new_sha1, old_sha1,
+                   ref_transaction_update(t, refname, new_oid, old_oid,
                                           flags, msg, &err) ||
                    ref_transaction_commit(t, &err)) {
                        ret = 1;
@@ -1037,12 +1095,12 @@ int refs_update_ref(struct ref_store *refs, const char *msg,
 }
 
 int update_ref(const char *msg, const char *refname,
-              const unsigned char *new_sha1,
-              const unsigned char *old_sha1,
+              const struct object_id *new_oid,
+              const struct object_id *old_oid,
               unsigned int flags, enum action_on_err onerr)
 {
-       return refs_update_ref(get_main_ref_store(), msg, refname, new_sha1,
-                              old_sha1, flags, onerr);
+       return refs_update_ref(get_main_ref_store(), msg, refname, new_oid,
+                              old_oid, flags, onerr);
 }
 
 char *shorten_unambiguous_ref(const char *refname, int strict)
@@ -1254,7 +1312,7 @@ int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
        int flag;
 
        if (!refs_read_ref_full(refs, "HEAD", RESOLVE_REF_READING,
-                               oid.hash, &flag))
+                               &oid, &flag))
                return fn("HEAD", &oid, flag, cb_data);
 
        return 0;
@@ -1387,25 +1445,25 @@ int for_each_rawref(each_ref_fn fn, void *cb_data)
 }
 
 int refs_read_raw_ref(struct ref_store *ref_store,
-                     const char *refname, unsigned char *sha1,
+                     const char *refname, struct object_id *oid,
                      struct strbuf *referent, unsigned int *type)
 {
-       return ref_store->be->read_raw_ref(ref_store, refname, sha1, referent, type);
+       return ref_store->be->read_raw_ref(ref_store, refname, oid, referent, type);
 }
 
 /* This function needs to return a meaningful errno on failure */
 const char *refs_resolve_ref_unsafe(struct ref_store *refs,
                                    const char *refname,
                                    int resolve_flags,
-                                   unsigned char *sha1, int *flags)
+                                   struct object_id *oid, int *flags)
 {
        static struct strbuf sb_refname = STRBUF_INIT;
        struct object_id unused_oid;
        int unused_flags;
        int symref_count;
 
-       if (!sha1)
-               sha1 = unused_oid.hash;
+       if (!oid)
+               oid = &unused_oid;
        if (!flags)
                flags = &unused_flags;
 
@@ -1433,7 +1491,7 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
                unsigned int read_flags = 0;
 
                if (refs_read_raw_ref(refs, refname,
-                                     sha1, &sb_refname, &read_flags)) {
+                                     oid, &sb_refname, &read_flags)) {
                        *flags |= read_flags;
 
                        /* In reading mode, refs must eventually resolve */
@@ -1450,7 +1508,7 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
                            errno != ENOTDIR)
                                return NULL;
 
-                       hashclr(sha1);
+                       oidclr(oid);
                        if (*flags & REF_BAD_NAME)
                                *flags |= REF_ISBROKEN;
                        return refname;
@@ -1460,7 +1518,7 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
 
                if (!(read_flags & REF_ISSYMREF)) {
                        if (*flags & REF_BAD_NAME) {
-                               hashclr(sha1);
+                               oidclr(oid);
                                *flags |= REF_ISBROKEN;
                        }
                        return refname;
@@ -1468,7 +1526,7 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
 
                refname = sb_refname.buf;
                if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
-                       hashclr(sha1);
+                       oidclr(oid);
                        return refname;
                }
                if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
@@ -1495,14 +1553,14 @@ int refs_init_db(struct strbuf *err)
 }
 
 const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
-                              unsigned char *sha1, int *flags)
+                              struct object_id *oid, int *flags)
 {
        return refs_resolve_ref_unsafe(get_main_ref_store(), refname,
-                                      resolve_flags, sha1, flags);
+                                      resolve_flags, oid, flags);
 }
 
 int resolve_gitlink_ref(const char *submodule, const char *refname,
-                       unsigned char *sha1)
+                       struct object_id *oid)
 {
        struct ref_store *refs;
        int flags;
@@ -1512,8 +1570,8 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,
        if (!refs)
                return -1;
 
-       if (!refs_resolve_ref_unsafe(refs, refname, 0, sha1, &flags) ||
-           is_null_sha1(sha1))
+       if (!refs_resolve_ref_unsafe(refs, refname, 0, oid, &flags) ||
+           is_null_oid(oid))
                return -1;
        return 0;
 }
@@ -1701,30 +1759,30 @@ int refs_pack_refs(struct ref_store *refs, unsigned int flags)
 }
 
 int refs_peel_ref(struct ref_store *refs, const char *refname,
-                 unsigned char *sha1)
+                 struct object_id *oid)
 {
        int flag;
-       unsigned char base[20];
+       struct object_id base;
 
        if (current_ref_iter && current_ref_iter->refname == refname) {
                struct object_id peeled;
 
                if (ref_iterator_peel(current_ref_iter, &peeled))
                        return -1;
-               hashcpy(sha1, peeled.hash);
+               oidcpy(oid, &peeled);
                return 0;
        }
 
        if (refs_read_ref_full(refs, refname,
-                              RESOLVE_REF_READING, base, &flag))
+                              RESOLVE_REF_READING, &base, &flag))
                return -1;
 
-       return peel_object(base, sha1);
+       return peel_object(&base, oid);
 }
 
-int peel_ref(const char *refname, unsigned char *sha1)
+int peel_ref(const char *refname, struct object_id *oid)
 {
-       return refs_peel_ref(get_main_ref_store(), refname, sha1);
+       return refs_peel_ref(get_main_ref_store(), refname, oid);
 }
 
 int refs_create_symref(struct ref_store *refs,
@@ -1884,7 +1942,7 @@ int refs_verify_refname_available(struct ref_store *refs,
                if (skip && string_list_has_string(skip, dirname.buf))
                        continue;
 
-               if (!refs_read_raw_ref(refs, dirname.buf, oid.hash, &referent, &type)) {
+               if (!refs_read_raw_ref(refs, dirname.buf, &oid, &referent, &type)) {
                        strbuf_addf(err, "'%s' exists; cannot create '%s'",
                                    dirname.buf, refname);
                        goto cleanup;
@@ -2014,19 +2072,19 @@ int delete_reflog(const char *refname)
 }
 
 int refs_reflog_expire(struct ref_store *refs,
-                      const char *refname, const unsigned char *sha1,
+                      const char *refname, const struct object_id *oid,
                       unsigned int flags,
                       reflog_expiry_prepare_fn prepare_fn,
                       reflog_expiry_should_prune_fn should_prune_fn,
                       reflog_expiry_cleanup_fn cleanup_fn,
                       void *policy_cb_data)
 {
-       return refs->be->reflog_expire(refs, refname, sha1, flags,
+       return refs->be->reflog_expire(refs, refname, oid, flags,
                                       prepare_fn, should_prune_fn,
                                       cleanup_fn, policy_cb_data);
 }
 
-int reflog_expire(const char *refname, const unsigned char *sha1,
+int reflog_expire(const char *refname, const struct object_id *oid,
                  unsigned int flags,
                  reflog_expiry_prepare_fn prepare_fn,
                  reflog_expiry_should_prune_fn should_prune_fn,
@@ -2034,7 +2092,7 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
                  void *policy_cb_data)
 {
        return refs_reflog_expire(get_main_ref_store(),
-                                 refname, sha1, flags,
+                                 refname, oid, flags,
                                  prepare_fn, should_prune_fn,
                                  cleanup_fn, policy_cb_data);
 }
diff --git a/refs.h b/refs.h
index a02b628c8fe81b648f13c66b7e618d8660151e77..01be5ae32fb01298ff6c0738ac4adfe42643b682 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -14,22 +14,22 @@ struct worktree;
  * at the resolved object name.  The return value, if not NULL, is a
  * pointer into either a static buffer or the input ref.
  *
- * If sha1 is non-NULL, store the referred-to object's name in it.
+ * If oid is non-NULL, store the referred-to object's name in it.
  *
  * If the reference cannot be resolved to an object, the behavior
  * depends on the RESOLVE_REF_READING flag:
  *
  * - If RESOLVE_REF_READING is set, return NULL.
  *
- * - If RESOLVE_REF_READING is not set, clear sha1 and return the name of
+ * - If RESOLVE_REF_READING is not set, clear oid and return the name of
  *   the last reference name in the chain, which will either be a non-symbolic
  *   reference or an undefined reference.  If this is a prelude to
  *   "writing" to the ref, the return value is the name of the ref
  *   that will actually be created or changed.
  *
  * If the RESOLVE_REF_NO_RECURSE flag is passed, only resolves one
- * level of symbolic reference.  The value stored in sha1 for a symbolic
- * reference will always be null_sha1 in this case, and the return
+ * level of symbolic reference.  The value stored in oid for a symbolic
+ * reference will always be null_oid in this case, and the return
  * value is the reference that the symref refers to directly.
  *
  * If flags is non-NULL, set the value that it points to the
@@ -46,7 +46,7 @@ struct worktree;
  *
  * RESOLVE_REF_ALLOW_BAD_NAME allows resolving refs even when their
  * name is invalid according to git-check-ref-format(1).  If the name
- * is bad then the value stored in sha1 will be null_sha1 and the two
+ * is bad then the value stored in oid will be null_oid and the two
  * flags REF_ISBROKEN and REF_BAD_NAME will be set.
  *
  * Even with RESOLVE_REF_ALLOW_BAD_NAME, names that escape the refs/
@@ -62,22 +62,22 @@ struct worktree;
 const char *refs_resolve_ref_unsafe(struct ref_store *refs,
                                    const char *refname,
                                    int resolve_flags,
-                                   unsigned char *sha1,
+                                   struct object_id *oid,
                                    int *flags);
 const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
-                              unsigned char *sha1, int *flags);
+                              struct object_id *oid, int *flags);
 
 char *refs_resolve_refdup(struct ref_store *refs,
                          const char *refname, int resolve_flags,
-                         unsigned char *sha1, int *flags);
+                         struct object_id *oid, int *flags);
 char *resolve_refdup(const char *refname, int resolve_flags,
-                    unsigned char *sha1, int *flags);
+                    struct object_id *oid, int *flags);
 
 int refs_read_ref_full(struct ref_store *refs, const char *refname,
-                      int resolve_flags, unsigned char *sha1, int *flags);
+                      int resolve_flags, struct object_id *oid, int *flags);
 int read_ref_full(const char *refname, int resolve_flags,
-                 unsigned char *sha1, int *flags);
-int read_ref(const char *refname, unsigned char *sha1);
+                 struct object_id *oid, int *flags);
+int read_ref(const char *refname, struct object_id *oid);
 
 /*
  * Return 0 if a reference named refname could be created without
@@ -114,23 +114,23 @@ extern int refs_init_db(struct strbuf *err);
 /*
  * If refname is a non-symbolic reference that refers to a tag object,
  * and the tag can be (recursively) dereferenced to a non-tag object,
- * store the SHA1 of the referred-to object to sha1 and return 0.  If
- * any of these conditions are not met, return a non-zero value.
+ * store the object ID of the referred-to object to oid and return 0.
+ * If any of these conditions are not met, return a non-zero value.
  * Symbolic references are considered unpeelable, even if they
  * ultimately resolve to a peelable tag.
  */
 int refs_peel_ref(struct ref_store *refs, const char *refname,
-                 unsigned char *sha1);
-int peel_ref(const char *refname, unsigned char *sha1);
+                 struct object_id *oid);
+int peel_ref(const char *refname, struct object_id *oid);
 
 /**
  * Resolve refname in the nested "gitlink" repository in the specified
  * submodule (which must be non-NULL). If the resolution is
- * successful, return 0 and set sha1 to the name of the object;
+ * successful, return 0 and set oid to the name of the object;
  * otherwise, return a non-zero value.
  */
 int resolve_gitlink_ref(const char *submodule, const char *refname,
-                       unsigned char *sha1);
+                       struct object_id *oid);
 
 /*
  * Return true iff abbrev_name is a possible abbreviation for
@@ -139,9 +139,9 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,
  */
 int refname_match(const char *abbrev_name, const char *full_name);
 
-int expand_ref(const char *str, int len, unsigned char *sha1, char **ref);
-int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
-int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
+int expand_ref(const char *str, int len, struct object_id *oid, char **ref);
+int dwim_ref(const char *str, int len, struct object_id *oid, char **ref);
+int dwim_log(const char *str, int len, struct object_id *oid, char **ref);
 
 /*
  * A ref_transaction represents a collection of reference updates that
@@ -260,7 +260,7 @@ struct ref_transaction;
 
 /*
  * The signature for the callback function for the for_each_*()
- * functions below.  The memory pointed to by the refname and sha1
+ * functions below.  The memory pointed to by the refname and oid
  * arguments is only guaranteed to be valid for the duration of a
  * single callback invocation.
  */
@@ -312,6 +312,30 @@ int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);
 int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data);
 int for_each_rawref(each_ref_fn fn, void *cb_data);
 
+/*
+ * Normalizes partial refs to their fully qualified form.
+ * Will prepend <prefix> to the <pattern> if it doesn't start with 'refs/'.
+ * <prefix> will default to 'refs/' if NULL.
+ *
+ * item.string will be set to the result.
+ * item.util will be set to NULL if <pattern> contains glob characters, or
+ * non-NULL if it doesn't.
+ */
+void normalize_glob_ref(struct string_list_item *item, const char *prefix,
+                       const char *pattern);
+
+/*
+ * Returns 0 if refname matches any of the exclude_patterns, or if it doesn't
+ * match any of the include_patterns. Returns 1 otherwise.
+ *
+ * If pattern list is NULL or empty, matching against that list is skipped.
+ * This has the effect of matching everything by default, unless the user
+ * specifies rules otherwise.
+ */
+int ref_filter_match(const char *refname,
+                    const struct string_list *include_patterns,
+                    const struct string_list *exclude_patterns);
+
 static inline const char *has_glob_specials(const char *pattern)
 {
        return strpbrk(pattern, "?*[");
@@ -335,24 +359,6 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt,
  */
 int refs_pack_refs(struct ref_store *refs, unsigned int flags);
 
-/*
- * Flags controlling ref_transaction_update(), ref_transaction_create(), etc.
- * REF_NODEREF: act on the ref directly, instead of dereferencing
- *              symbolic references.
- *
- * Other flags are reserved for internal use.
- */
-#define REF_NODEREF    0x01
-#define REF_FORCE_CREATE_REFLOG 0x40
-
-/*
- * Flags that can be passed in to ref_transaction_update
- */
-#define REF_TRANSACTION_UPDATE_ALLOWED_FLAGS \
-       REF_ISPRUNING |                      \
-       REF_FORCE_CREATE_REFLOG |            \
-       REF_NODEREF
-
 /*
  * Setup reflog before using. Fill in err and return -1 on failure.
  */
@@ -363,7 +369,7 @@ int safe_create_reflog(const char *refname, int force_create, struct strbuf *err
 /** Reads log for the value of ref during at_time. **/
 int read_ref_at(const char *refname, unsigned int flags,
                timestamp_t at_time, int cnt,
-               unsigned char *sha1, char **msg,
+               struct object_id *oid, char **msg,
                timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
 
 /** Check if a particular reflog exists */
@@ -371,19 +377,19 @@ int refs_reflog_exists(struct ref_store *refs, const char *refname);
 int reflog_exists(const char *refname);
 
 /*
- * Delete the specified reference. If old_sha1 is non-NULL, then
- * verify that the current value of the reference is old_sha1 before
- * deleting it. If old_sha1 is NULL, delete the reference if it
- * exists, regardless of its old value. It is an error for old_sha1 to
- * be NULL_SHA1. msg and flags are passed through to
+ * Delete the specified reference. If old_oid is non-NULL, then
+ * verify that the current value of the reference is old_oid before
+ * deleting it. If old_oid is NULL, delete the reference if it
+ * exists, regardless of its old value. It is an error for old_oid to
+ * be null_oid. msg and flags are passed through to
  * ref_transaction_delete().
  */
 int refs_delete_ref(struct ref_store *refs, const char *msg,
                    const char *refname,
-                   const unsigned char *old_sha1,
+                   const struct object_id *old_oid,
                    unsigned int flags);
 int delete_ref(const char *msg, const char *refname,
-              const unsigned char *old_sha1, unsigned int flags);
+              const struct object_id *old_oid, unsigned int flags);
 
 /*
  * Delete the specified references. If there are any problems, emit
@@ -480,22 +486,23 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
  *
  *     refname -- the name of the reference to be affected.
  *
- *     new_sha1 -- the SHA-1 that should be set to be the new value of
- *         the reference. Some functions allow this parameter to be
+ *     new_oid -- the object ID that should be set to be the new value
+ *         of the reference. Some functions allow this parameter to be
  *         NULL, meaning that the reference is not changed, or
- *         null_sha1, meaning that the reference should be deleted. A
+ *         null_oid, meaning that the reference should be deleted. A
  *         copy of this value is made in the transaction.
  *
- *     old_sha1 -- the SHA-1 value that the reference must have before
+ *     old_oid -- the object ID that the reference must have before
  *         the update. Some functions allow this parameter to be NULL,
  *         meaning that the old value of the reference is not checked,
- *         or null_sha1, meaning that the reference must not exist
+ *         or null_oid, meaning that the reference must not exist
  *         before the update. A copy of this value is made in the
  *         transaction.
  *
  *     flags -- flags affecting the update, passed to
- *         update_ref_lock(). Can be REF_NODEREF, which means that
- *         symbolic references should not be followed.
+ *         update_ref_lock(). Possible flags: REF_NO_DEREF,
+ *         REF_FORCE_CREATE_REFLOG. See those constants for more
+ *         information.
  *
  *     msg -- a message describing the change (for the reflog).
  *
@@ -511,14 +518,40 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
  */
 
 /*
- * Add a reference update to transaction. new_sha1 is the value that
- * the reference should have after the update, or null_sha1 if it
- * should be deleted. If new_sha1 is NULL, then the reference is not
- * changed at all. old_sha1 is the value that the reference must have
- * before the update, or null_sha1 if it must not have existed
+ * The following flags can be passed to ref_transaction_update() etc.
+ * Internally, they are stored in `ref_update::flags`, along with some
+ * internal flags.
+ */
+
+/*
+ * Act on the ref directly; i.e., without dereferencing symbolic refs.
+ * If this flag is not specified, then symbolic references are
+ * dereferenced and the update is applied to the referent.
+ */
+#define REF_NO_DEREF (1 << 0)
+
+/*
+ * Force the creation of a reflog for this reference, even if it
+ * didn't previously have a reflog.
+ */
+#define REF_FORCE_CREATE_REFLOG (1 << 1)
+
+/*
+ * Bitmask of all of the flags that are allowed to be passed in to
+ * ref_transaction_update() and friends:
+ */
+#define REF_TRANSACTION_UPDATE_ALLOWED_FLAGS \
+       (REF_NO_DEREF | REF_FORCE_CREATE_REFLOG)
+
+/*
+ * Add a reference update to transaction. `new_oid` is the value that
+ * the reference should have after the update, or `null_oid` if it
+ * should be deleted. If `new_oid` is NULL, then the reference is not
+ * changed at all. `old_oid` is the value that the reference must have
+ * before the update, or `null_oid` if it must not have existed
  * beforehand. The old value is checked after the lock is taken to
- * prevent races. If the old value doesn't agree with old_sha1, the
- * whole transaction fails. If old_sha1 is NULL, then the previous
+ * prevent races. If the old value doesn't agree with old_oid, the
+ * whole transaction fails. If old_oid is NULL, then the previous
  * value is not checked.
  *
  * See the above comment "Reference transaction updates" for more
@@ -526,15 +559,15 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
  */
 int ref_transaction_update(struct ref_transaction *transaction,
                           const char *refname,
-                          const unsigned char *new_sha1,
-                          const unsigned char *old_sha1,
+                          const struct object_id *new_oid,
+                          const struct object_id *old_oid,
                           unsigned int flags, const char *msg,
                           struct strbuf *err);
 
 /*
- * Add a reference creation to transaction. new_sha1 is the value that
+ * Add a reference creation to transaction. new_oid is the value that
  * the reference should have after the update; it must not be
- * null_sha1. It is verified that the reference does not exist
+ * null_oid. It is verified that the reference does not exist
  * already.
  *
  * See the above comment "Reference transaction updates" for more
@@ -542,35 +575,35 @@ int ref_transaction_update(struct ref_transaction *transaction,
  */
 int ref_transaction_create(struct ref_transaction *transaction,
                           const char *refname,
-                          const unsigned char *new_sha1,
+                          const struct object_id *new_oid,
                           unsigned int flags, const char *msg,
                           struct strbuf *err);
 
 /*
- * Add a reference deletion to transaction. If old_sha1 is non-NULL,
+ * Add a reference deletion to transaction. If old_oid is non-NULL,
  * then it holds the value that the reference should have had before
- * the update (which must not be null_sha1).
+ * the update (which must not be null_oid).
  *
  * See the above comment "Reference transaction updates" for more
  * information.
  */
 int ref_transaction_delete(struct ref_transaction *transaction,
                           const char *refname,
-                          const unsigned char *old_sha1,
+                          const struct object_id *old_oid,
                           unsigned int flags, const char *msg,
                           struct strbuf *err);
 
 /*
- * Verify, within a transaction, that refname has the value old_sha1,
- * or, if old_sha1 is null_sha1, then verify that the reference
- * doesn't exist. old_sha1 must be non-NULL.
+ * Verify, within a transaction, that refname has the value old_oid,
+ * or, if old_oid is null_oid, then verify that the reference
+ * doesn't exist. old_oid must be non-NULL.
  *
  * See the above comment "Reference transaction updates" for more
  * information.
  */
 int ref_transaction_verify(struct ref_transaction *transaction,
                           const char *refname,
-                          const unsigned char *old_sha1,
+                          const struct object_id *old_oid,
                           unsigned int flags,
                           struct strbuf *err);
 
@@ -624,7 +657,7 @@ int ref_transaction_abort(struct ref_transaction *transaction,
  * It is a bug to call this function when there might be other
  * processes accessing the repository or if there are existing
  * references that might conflict with the ones being created. All
- * old_sha1 values must either be absent or NULL_SHA1.
+ * old_oid values must either be absent or null_oid.
  */
 int initial_ref_transaction_commit(struct ref_transaction *transaction,
                                   struct strbuf *err);
@@ -643,12 +676,9 @@ void ref_transaction_free(struct ref_transaction *transaction);
  * argument.
  */
 int refs_update_ref(struct ref_store *refs, const char *msg, const char *refname,
-                   const unsigned char *new_sha1, const unsigned char *old_sha1,
+                   const struct object_id *new_oid, const struct object_id *old_oid,
                    unsigned int flags, enum action_on_err onerr);
 int update_ref(const char *msg, const char *refname,
-              const unsigned char *new_sha1, const unsigned char *old_sha1,
-              unsigned int flags, enum action_on_err onerr);
-int update_ref_oid(const char *msg, const char *refname,
               const struct object_id *new_oid, const struct object_id *old_oid,
               unsigned int flags, enum action_on_err onerr);
 
@@ -706,20 +736,20 @@ typedef int reflog_expiry_should_prune_fn(struct object_id *ooid,
 typedef void reflog_expiry_cleanup_fn(void *cb_data);
 
 /*
- * Expire reflog entries for the specified reference. sha1 is the old
+ * Expire reflog entries for the specified reference. oid is the old
  * value of the reference. flags is a combination of the constants in
  * enum expire_reflog_flags. The three function pointers are described
  * above. On success, return zero.
  */
 int refs_reflog_expire(struct ref_store *refs,
                       const char *refname,
-                      const unsigned char *sha1,
+                      const struct object_id *oid,
                       unsigned int flags,
                       reflog_expiry_prepare_fn prepare_fn,
                       reflog_expiry_should_prune_fn should_prune_fn,
                       reflog_expiry_cleanup_fn cleanup_fn,
                       void *policy_cb_data);
-int reflog_expire(const char *refname, const unsigned char *sha1,
+int reflog_expire(const char *refname, const struct object_id *oid,
                  unsigned int flags,
                  reflog_expiry_prepare_fn prepare_fn,
                  reflog_expiry_should_prune_fn should_prune_fn,
index 8cc1e07fdb7204d13c99826304317c66d4249cf5..f75d960e1982a2db5dc8828c6f1b10ffe64fda48 100644 (file)
 #include "../object.h"
 #include "../dir.h"
 
+/*
+ * This backend uses the following flags in `ref_update::flags` for
+ * internal bookkeeping purposes. Their numerical values must not
+ * conflict with REF_NO_DEREF, REF_FORCE_CREATE_REFLOG, REF_HAVE_NEW,
+ * REF_HAVE_OLD, or REF_IS_PRUNING, which are also stored in
+ * `ref_update::flags`.
+ */
+
+/*
+ * Used as a flag in ref_update::flags when a loose ref is being
+ * pruned. This flag must only be used when REF_NO_DEREF is set.
+ */
+#define REF_IS_PRUNING (1 << 4)
+
+/*
+ * Flag passed to lock_ref_sha1_basic() telling it to tolerate broken
+ * refs (i.e., because the reference is about to be deleted anyway).
+ */
+#define REF_DELETING (1 << 5)
+
+/*
+ * Used as a flag in ref_update::flags when the lockfile needs to be
+ * committed.
+ */
+#define REF_NEEDS_COMMIT (1 << 6)
+
+/*
+ * Used as a flag in ref_update::flags when we want to log a ref
+ * update but not actually perform it.  This is used when a symbolic
+ * ref update is split up.
+ */
+#define REF_LOG_ONLY (1 << 7)
+
+/*
+ * Used as a flag in ref_update::flags when the ref_update was via an
+ * update to HEAD.
+ */
+#define REF_UPDATE_VIA_HEAD (1 << 8)
+
+/*
+ * Used as a flag in ref_update::flags when the loose reference has
+ * been deleted.
+ */
+#define REF_DELETED_LOOSE (1 << 9)
+
 struct ref_lock {
        char *ref_name;
        struct lock_file lk;
@@ -189,13 +234,13 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
                        if (!refs_resolve_ref_unsafe(&refs->base,
                                                     refname.buf,
                                                     RESOLVE_REF_READING,
-                                                    oid.hash, &flag)) {
+                                                    &oid, &flag)) {
                                oidclr(&oid);
                                flag |= REF_ISBROKEN;
                        } else if (is_null_oid(&oid)) {
                                /*
                                 * It is so astronomically unlikely
-                                * that NULL_SHA1 is the SHA-1 of an
+                                * that null_oid is the OID of an
                                 * actual object that we consider its
                                 * appearance in a loose reference
                                 * file to be repo corruption
@@ -261,7 +306,7 @@ static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs)
 }
 
 static int files_read_raw_ref(struct ref_store *ref_store,
-                             const char *refname, unsigned char *sha1,
+                             const char *refname, struct object_id *oid,
                              struct strbuf *referent, unsigned int *type)
 {
        struct files_ref_store *refs =
@@ -270,6 +315,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,
        struct strbuf sb_path = STRBUF_INIT;
        const char *path;
        const char *buf;
+       const char *p;
        struct stat st;
        int fd;
        int ret = -1;
@@ -304,7 +350,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,
                if (errno != ENOENT)
                        goto out;
                if (refs_read_raw_ref(refs->packed_ref_store, refname,
-                                     sha1, referent, type)) {
+                                     oid, referent, type)) {
                        errno = ENOENT;
                        goto out;
                }
@@ -344,7 +390,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,
                 * packed ref:
                 */
                if (refs_read_raw_ref(refs->packed_ref_store, refname,
-                                     sha1, referent, type)) {
+                                     oid, referent, type)) {
                        errno = EISDIR;
                        goto out;
                }
@@ -390,8 +436,8 @@ static int files_read_raw_ref(struct ref_store *ref_store,
         * Please note that FETCH_HEAD has additional
         * data after the sha.
         */
-       if (get_sha1_hex(buf, sha1) ||
-           (buf[40] != '\0' && !isspace(buf[40]))) {
+       if (parse_oid_hex(buf, oid, &p) ||
+           (*p != '\0' && !isspace(*p))) {
                *type |= REF_ISBROKEN;
                errno = EINVAL;
                goto out;
@@ -427,7 +473,7 @@ static void unlock_ref(struct ref_lock *lock)
  * are passed to refs_verify_refname_available() for this check.
  *
  * If mustexist is not set and the reference is not found or is
- * broken, lock the reference anyway but clear sha1.
+ * broken, lock the reference anyway but clear old_oid.
  *
  * Return 0 on success. On failure, write an error message to err and
  * return TRANSACTION_NAME_CONFLICT or TRANSACTION_GENERIC_ERROR.
@@ -545,7 +591,7 @@ static int lock_raw_ref(struct files_ref_store *refs,
         */
 
        if (files_read_raw_ref(&refs->base, refname,
-                              lock->old_oid.hash, referent, type)) {
+                              &lock->old_oid, referent, type)) {
                if (errno == ENOENT) {
                        if (mustexist) {
                                /* Garden variety missing reference. */
@@ -769,21 +815,21 @@ static struct ref_iterator *files_ref_iterator_begin(
 }
 
 /*
- * Verify that the reference locked by lock has the value old_sha1.
- * Fail if the reference doesn't exist and mustexist is set. Return 0
- * on success. On error, write an error message to err, set errno, and
- * return a negative value.
+ * Verify that the reference locked by lock has the value old_oid
+ * (unless it is NULL).  Fail if the reference doesn't exist and
+ * mustexist is set. Return 0 on success. On error, write an error
+ * message to err, set errno, and return a negative value.
  */
 static int verify_lock(struct ref_store *ref_store, struct ref_lock *lock,
-                      const unsigned char *old_sha1, int mustexist,
+                      const struct object_id *old_oid, int mustexist,
                       struct strbuf *err)
 {
        assert(err);
 
        if (refs_read_ref_full(ref_store, lock->ref_name,
                               mustexist ? RESOLVE_REF_READING : 0,
-                              lock->old_oid.hash, NULL)) {
-               if (old_sha1) {
+                              &lock->old_oid, NULL)) {
+               if (old_oid) {
                        int save_errno = errno;
                        strbuf_addf(err, "can't verify ref '%s'", lock->ref_name);
                        errno = save_errno;
@@ -793,11 +839,11 @@ static int verify_lock(struct ref_store *ref_store, struct ref_lock *lock,
                        return 0;
                }
        }
-       if (old_sha1 && hashcmp(lock->old_oid.hash, old_sha1)) {
+       if (old_oid && oidcmp(&lock->old_oid, old_oid)) {
                strbuf_addf(err, "ref '%s' is at %s but expected %s",
                            lock->ref_name,
                            oid_to_hex(&lock->old_oid),
-                           sha1_to_hex(old_sha1));
+                           oid_to_hex(old_oid));
                errno = EBUSY;
                return -1;
        }
@@ -827,22 +873,22 @@ static int create_reflock(const char *path, void *cb)
  * Locks a ref returning the lock on success and NULL on failure.
  * On failure errno is set to something meaningful.
  */
-static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
-                                           const char *refname,
-                                           const unsigned char *old_sha1,
-                                           const struct string_list *extras,
-                                           const struct string_list *skip,
-                                           unsigned int flags, int *type,
-                                           struct strbuf *err)
+static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs,
+                                          const char *refname,
+                                          const struct object_id *old_oid,
+                                          const struct string_list *extras,
+                                          const struct string_list *skip,
+                                          unsigned int flags, int *type,
+                                          struct strbuf *err)
 {
        struct strbuf ref_file = STRBUF_INIT;
        struct ref_lock *lock;
        int last_errno = 0;
-       int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
+       int mustexist = (old_oid && !is_null_oid(old_oid));
        int resolve_flags = RESOLVE_REF_NO_RECURSE;
        int resolved;
 
-       files_assert_main_repository(refs, "lock_ref_sha1_basic");
+       files_assert_main_repository(refs, "lock_ref_oid_basic");
        assert(err);
 
        lock = xcalloc(1, sizeof(struct ref_lock));
@@ -855,7 +901,7 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
        files_ref_path(refs, &ref_file, refname);
        resolved = !!refs_resolve_ref_unsafe(&refs->base,
                                             refname, resolve_flags,
-                                            lock->old_oid.hash, type);
+                                            &lock->old_oid, type);
        if (!resolved && errno == EISDIR) {
                /*
                 * we are trying to lock foo but we used to
@@ -874,7 +920,7 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
                }
                resolved = !!refs_resolve_ref_unsafe(&refs->base,
                                                     refname, resolve_flags,
-                                                    lock->old_oid.hash, type);
+                                                    &lock->old_oid, type);
        }
        if (!resolved) {
                last_errno = errno;
@@ -908,7 +954,7 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
                goto error_return;
        }
 
-       if (verify_lock(&refs->base, lock, old_sha1, mustexist, err)) {
+       if (verify_lock(&refs->base, lock, old_oid, mustexist, err)) {
                last_errno = errno;
                goto error_return;
        }
@@ -926,7 +972,7 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
 
 struct ref_to_prune {
        struct ref_to_prune *next;
-       unsigned char sha1[20];
+       struct object_id oid;
        char name[FLEX_ARRAY];
 };
 
@@ -988,22 +1034,29 @@ static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r)
 {
        struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
+       int ret = -1;
 
        if (check_refname_format(r->name, 0))
                return;
 
        transaction = ref_store_transaction_begin(&refs->base, &err);
-       if (!transaction ||
-           ref_transaction_delete(transaction, r->name, r->sha1,
-                                  REF_ISPRUNING | REF_NODEREF, NULL, &err) ||
-           ref_transaction_commit(transaction, &err)) {
-               ref_transaction_free(transaction);
+       if (!transaction)
+               goto cleanup;
+       ref_transaction_add_update(
+                       transaction, r->name,
+                       REF_NO_DEREF | REF_HAVE_NEW | REF_HAVE_OLD | REF_IS_PRUNING,
+                       &null_oid, &r->oid, NULL);
+       if (ref_transaction_commit(transaction, &err))
+               goto cleanup;
+
+       ret = 0;
+
+cleanup:
+       if (ret)
                error("%s", err.buf);
-               strbuf_release(&err);
-               return;
-       }
-       ref_transaction_free(transaction);
        strbuf_release(&err);
+       ref_transaction_free(transaction);
+       return;
 }
 
 /*
@@ -1079,8 +1132,8 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
                 * packed-refs transaction:
                 */
                if (ref_transaction_update(transaction, iter->refname,
-                                          iter->oid->hash, NULL,
-                                          REF_NODEREF, NULL, &err))
+                                          iter->oid, NULL,
+                                          REF_NO_DEREF, NULL, &err))
                        die("failure preparing to create packed reference %s: %s",
                            iter->refname, err.buf);
 
@@ -1088,7 +1141,7 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
                if ((flags & PACK_REFS_PRUNE)) {
                        struct ref_to_prune *n;
                        FLEX_ALLOC_STR(n, name, iter->refname);
-                       hashcpy(n->sha1, iter->oid->hash);
+                       oidcpy(&n->oid, iter->oid);
                        n->next = refs_to_prune;
                        refs_to_prune = n;
                }
@@ -1251,7 +1304,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
 
        if (!refs_resolve_ref_unsafe(&refs->base, oldrefname,
                                     RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
-                               orig_oid.hash, &flag)) {
+                               &orig_oid, &flag)) {
                ret = error("refname %s not found", oldrefname);
                goto out;
        }
@@ -1283,7 +1336,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
        }
 
        if (!copy && refs_delete_ref(&refs->base, logmsg, oldrefname,
-                           orig_oid.hash, REF_NODEREF)) {
+                           &orig_oid, REF_NO_DEREF)) {
                error("unable to delete old %s", oldrefname);
                goto rollback;
        }
@@ -1297,9 +1350,9 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
         */
        if (!copy && !refs_read_ref_full(&refs->base, newrefname,
                                RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
-                               oid.hash, NULL) &&
+                               &oid, NULL) &&
            refs_delete_ref(&refs->base, NULL, newrefname,
-                           NULL, REF_NODEREF)) {
+                           NULL, REF_NO_DEREF)) {
                if (errno == EISDIR) {
                        struct strbuf path = STRBUF_INIT;
                        int result;
@@ -1323,8 +1376,8 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
 
        logmoved = log;
 
-       lock = lock_ref_sha1_basic(refs, newrefname, NULL, NULL, NULL,
-                                  REF_NODEREF, NULL, &err);
+       lock = lock_ref_oid_basic(refs, newrefname, NULL, NULL, NULL,
+                                 REF_NO_DEREF, NULL, &err);
        if (!lock) {
                if (copy)
                        error("unable to copy '%s' to '%s': %s", oldrefname, newrefname, err.buf);
@@ -1346,8 +1399,8 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
        goto out;
 
  rollback:
-       lock = lock_ref_sha1_basic(refs, oldrefname, NULL, NULL, NULL,
-                                  REF_NODEREF, NULL, &err);
+       lock = lock_ref_oid_basic(refs, oldrefname, NULL, NULL, NULL,
+                                 REF_NO_DEREF, NULL, &err);
        if (!lock) {
                error("unable to lock %s for rollback: %s", oldrefname, err.buf);
                strbuf_release(&err);
@@ -1595,9 +1648,8 @@ static int files_log_ref_write(struct files_ref_store *refs,
 }
 
 /*
- * Write sha1 into the open lockfile, then close the lockfile. On
- * errors, rollback the lockfile, fill in *err and
- * return -1.
+ * Write oid into the open lockfile, then close the lockfile. On
+ * errors, rollback the lockfile, fill in *err and return -1.
  */
 static int write_ref_to_lockfile(struct ref_lock *lock,
                                 const struct object_id *oid, struct strbuf *err)
@@ -1721,7 +1773,7 @@ static void update_symref_reflog(struct files_ref_store *refs,
        struct object_id new_oid;
        if (logmsg &&
            !refs_read_ref_full(&refs->base, target,
-                               RESOLVE_REF_READING, new_oid.hash, NULL) &&
+                               RESOLVE_REF_READING, &new_oid, NULL) &&
            files_log_ref_write(refs, refname, &lock->old_oid,
                                &new_oid, logmsg, 0, &err)) {
                error("%s", err.buf);
@@ -1762,9 +1814,9 @@ static int files_create_symref(struct ref_store *ref_store,
        struct ref_lock *lock;
        int ret;
 
-       lock = lock_ref_sha1_basic(refs, refname, NULL,
-                                  NULL, NULL, REF_NODEREF, NULL,
-                                  &err);
+       lock = lock_ref_oid_basic(refs, refname, NULL,
+                                 NULL, NULL, REF_NO_DEREF, NULL,
+                                 &err);
        if (!lock) {
                error("%s", err.buf);
                strbuf_release(&err);
@@ -2010,7 +2062,7 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
 
                if (refs_read_ref_full(iter->ref_store,
                                       diter->relative_path, 0,
-                                      iter->oid.hash, &flags)) {
+                                      &iter->oid, &flags)) {
                        error("bad ref for %s", diter->path.buf);
                        continue;
                }
@@ -2124,7 +2176,7 @@ static int split_head_update(struct ref_update *update,
        struct ref_update *new_update;
 
        if ((update->flags & REF_LOG_ONLY) ||
-           (update->flags & REF_ISPRUNING) ||
+           (update->flags & REF_IS_PRUNING) ||
            (update->flags & REF_UPDATE_VIA_HEAD))
                return 0;
 
@@ -2147,8 +2199,8 @@ static int split_head_update(struct ref_update *update,
 
        new_update = ref_transaction_add_update(
                        transaction, "HEAD",
-                       update->flags | REF_LOG_ONLY | REF_NODEREF,
-                       update->new_oid.hash, update->old_oid.hash,
+                       update->flags | REF_LOG_ONLY | REF_NO_DEREF,
+                       &update->new_oid, &update->old_oid,
                        update->msg);
 
        /*
@@ -2166,8 +2218,8 @@ static int split_head_update(struct ref_update *update,
 
 /*
  * update is for a symref that points at referent and doesn't have
- * REF_NODEREF set. Split it into two updates:
- * - The original update, but with REF_LOG_ONLY and REF_NODEREF set
+ * REF_NO_DEREF set. Split it into two updates:
+ * - The original update, but with REF_LOG_ONLY and REF_NO_DEREF set
  * - A new, separate update for the referent reference
  * Note that the new update will itself be subject to splitting when
  * the iteration gets to it.
@@ -2212,17 +2264,17 @@ static int split_symref_update(struct files_ref_store *refs,
 
        new_update = ref_transaction_add_update(
                        transaction, referent, new_flags,
-                       update->new_oid.hash, update->old_oid.hash,
+                       &update->new_oid, &update->old_oid,
                        update->msg);
 
        new_update->parent_update = update;
 
        /*
         * Change the symbolic ref update to log only. Also, it
-        * doesn't need to check its old SHA-1 value, as that will be
+        * doesn't need to check its old OID value, as that will be
         * done when new_update is processed.
         */
-       update->flags |= REF_LOG_ONLY | REF_NODEREF;
+       update->flags |= REF_LOG_ONLY | REF_NO_DEREF;
        update->flags &= ~REF_HAVE_OLD;
 
        /*
@@ -2288,10 +2340,10 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid,
  * Prepare for carrying out update:
  * - Lock the reference referred to by update.
  * - Read the reference under lock.
- * - Check that its old SHA-1 value (if specified) is correct, and in
+ * - Check that its old OID value (if specified) is correct, and in
  *   any case record it in update->lock->old_oid for later use when
  *   writing the reflog.
- * - If it is a symref update without REF_NODEREF, split it up into a
+ * - If it is a symref update without REF_NO_DEREF, split it up into a
  *   REF_LOG_ONLY update of the symref and add a separate update for
  *   the referent to transaction.
  * - If it is an update of head_ref, add a corresponding REF_LOG_ONLY
@@ -2339,15 +2391,15 @@ static int lock_ref_for_update(struct files_ref_store *refs,
        update->backend_data = lock;
 
        if (update->type & REF_ISSYMREF) {
-               if (update->flags & REF_NODEREF) {
+               if (update->flags & REF_NO_DEREF) {
                        /*
                         * We won't be reading the referent as part of
                         * the transaction, so we have to read it here
-                        * to record and possibly check old_sha1:
+                        * to record and possibly check old_oid:
                         */
                        if (refs_read_ref_full(&refs->base,
                                               referent.buf, 0,
-                                              lock->old_oid.hash, NULL)) {
+                                              &lock->old_oid, NULL)) {
                                if (update->flags & REF_HAVE_OLD) {
                                        strbuf_addf(err, "cannot lock ref '%s': "
                                                    "error reading reference",
@@ -2363,7 +2415,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                        /*
                         * Create a new update for the reference this
                         * symref is pointing at. Also, we will record
-                        * and verify old_sha1 for this update as part
+                        * and verify old_oid for this update as part
                         * of processing the split-off update, so we
                         * don't have to do it here.
                         */
@@ -2383,7 +2435,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
 
                /*
                 * If this update is happening indirectly because of a
-                * symref update, record the old SHA-1 in the parent
+                * symref update, record the old OID in the parent
                 * update:
                 */
                for (parent_update = update->parent_update;
@@ -2510,13 +2562,18 @@ static int files_transaction_prepare(struct ref_store *ref_store,
         * transaction. (If we end up splitting up any updates using
         * split_symref_update() or split_head_update(), those
         * functions will check that the new updates don't have the
-        * same refname as any existing ones.)
+        * same refname as any existing ones.) Also fail if any of the
+        * updates use REF_IS_PRUNING without REF_NO_DEREF.
         */
        for (i = 0; i < transaction->nr; i++) {
                struct ref_update *update = transaction->updates[i];
                struct string_list_item *item =
                        string_list_append(&affected_refnames, update->refname);
 
+               if ((update->flags & REF_IS_PRUNING) &&
+                   !(update->flags & REF_NO_DEREF))
+                       BUG("REF_IS_PRUNING set without REF_NO_DEREF");
+
                /*
                 * We store a pointer to update in item->util, but at
                 * the moment we never use the value of this field
@@ -2574,7 +2631,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
 
                if (update->flags & REF_DELETING &&
                    !(update->flags & REF_LOG_ONLY) &&
-                   !(update->flags & REF_ISPRUNING)) {
+                   !(update->flags & REF_IS_PRUNING)) {
                        /*
                         * This reference has to be deleted from
                         * packed-refs if it exists there.
@@ -2593,8 +2650,8 @@ static int files_transaction_prepare(struct ref_store *ref_store,
 
                        ref_transaction_add_update(
                                        packed_transaction, update->refname,
-                                       update->flags & ~REF_HAVE_OLD,
-                                       update->new_oid.hash, update->old_oid.hash,
+                                       REF_HAVE_NEW | REF_NO_DEREF,
+                                       &update->new_oid, NULL,
                                        NULL);
                }
        }
@@ -2605,7 +2662,23 @@ static int files_transaction_prepare(struct ref_store *ref_store,
                        goto cleanup;
                }
                backend_data->packed_refs_locked = 1;
-               ret = ref_transaction_prepare(packed_transaction, err);
+
+               if (is_packed_transaction_needed(refs->packed_ref_store,
+                                                packed_transaction)) {
+                       ret = ref_transaction_prepare(packed_transaction, err);
+               } else {
+                       /*
+                        * We can skip rewriting the `packed-refs`
+                        * file. But we do need to leave it locked, so
+                        * that somebody else doesn't pack a reference
+                        * that we are trying to delete.
+                        */
+                       if (ref_transaction_abort(packed_transaction, err)) {
+                               ret = TRANSACTION_GENERIC_ERROR;
+                               goto cleanup;
+                       }
+                       backend_data->packed_transaction = NULL;
+               }
        }
 
 cleanup:
@@ -2691,7 +2764,7 @@ static int files_transaction_finish(struct ref_store *ref_store,
                struct ref_update *update = transaction->updates[i];
                if (update->flags & REF_DELETING &&
                    !(update->flags & REF_LOG_ONLY) &&
-                   !(update->flags & REF_ISPRUNING)) {
+                   !(update->flags & REF_IS_PRUNING)) {
                        strbuf_reset(&sb);
                        files_reflog_path(refs, &sb, update->refname);
                        if (!unlink_or_warn(sb.buf))
@@ -2847,7 +2920,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
                 */
                ref_transaction_add_update(packed_transaction, update->refname,
                                           update->flags & ~REF_HAVE_OLD,
-                                          update->new_oid.hash, update->old_oid.hash,
+                                          &update->new_oid, &update->old_oid,
                                           NULL);
        }
 
@@ -2908,7 +2981,7 @@ static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
 }
 
 static int files_reflog_expire(struct ref_store *ref_store,
-                              const char *refname, const unsigned char *sha1,
+                              const char *refname, const struct object_id *oid,
                               unsigned int flags,
                               reflog_expiry_prepare_fn prepare_fn,
                               reflog_expiry_should_prune_fn should_prune_fn,
@@ -2925,7 +2998,6 @@ static int files_reflog_expire(struct ref_store *ref_store,
        int status = 0;
        int type;
        struct strbuf err = STRBUF_INIT;
-       struct object_id oid;
 
        memset(&cb, 0, sizeof(cb));
        cb.flags = flags;
@@ -2937,9 +3009,9 @@ static int files_reflog_expire(struct ref_store *ref_store,
         * reference itself, plus we might need to update the
         * reference if --updateref was specified:
         */
-       lock = lock_ref_sha1_basic(refs, refname, sha1,
-                                  NULL, NULL, REF_NODEREF,
-                                  &type, &err);
+       lock = lock_ref_oid_basic(refs, refname, oid,
+                                 NULL, NULL, REF_NO_DEREF,
+                                 &type, &err);
        if (!lock) {
                error("cannot lock ref '%s': %s", refname, err.buf);
                strbuf_release(&err);
@@ -2975,9 +3047,7 @@ static int files_reflog_expire(struct ref_store *ref_store,
                }
        }
 
-       hashcpy(oid.hash, sha1);
-
-       (*prepare_fn)(refname, &oid, cb.policy_cb);
+       (*prepare_fn)(refname, oid, cb.policy_cb);
        refs_for_each_reflog_ent(ref_store, refname, expire_reflog_ent, &cb);
        (*cleanup_fn)(cb.policy_cb);
 
index 3279d42c5a696434bbcdb85d6244cded1ddbc758..023243fd5f1833f3c5f0b6fd3cd82b2e0c69644e 100644 (file)
@@ -716,7 +716,7 @@ static struct snapshot *get_snapshot(struct packed_ref_store *refs)
 }
 
 static int packed_read_raw_ref(struct ref_store *ref_store,
-                              const char *refname, unsigned char *sha1,
+                              const char *refname, struct object_id *oid,
                               struct strbuf *referent, unsigned int *type)
 {
        struct packed_ref_store *refs =
@@ -734,7 +734,7 @@ static int packed_read_raw_ref(struct ref_store *ref_store,
                return -1;
        }
 
-       if (get_sha1_hex(rec, sha1))
+       if (get_oid_hex(rec, oid))
                die_invalid_line(refs->path, rec, snapshot->eof - rec);
 
        *type = REF_ISPACKED;
@@ -744,7 +744,7 @@ static int packed_read_raw_ref(struct ref_store *ref_store,
 /*
  * This value is set in `base.flags` if the peeled value of the
  * current reference is known. In that case, `peeled` contains the
- * correct peeled value for the reference, which might be `null_sha1`
+ * correct peeled value for the reference, which might be `null_oid`
  * if the reference is not a tag or if it is broken.
  */
 #define REF_KNOWS_PEELED 0x40
@@ -880,7 +880,7 @@ static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
        } else if ((iter->base.flags & (REF_ISBROKEN | REF_ISSYMREF))) {
                return -1;
        } else {
-               return !!peel_object(iter->oid.hash, peeled->hash);
+               return !!peel_object(&iter->oid, peeled);
        }
 }
 
@@ -961,11 +961,11 @@ static struct ref_iterator *packed_ref_iterator_begin(
  * by the failing call to `fprintf()`.
  */
 static int write_packed_entry(FILE *fh, const char *refname,
-                             const unsigned char *sha1,
-                             const unsigned char *peeled)
+                             const struct object_id *oid,
+                             const struct object_id *peeled)
 {
-       if (fprintf(fh, "%s %s\n", sha1_to_hex(sha1), refname) < 0 ||
-           (peeled && fprintf(fh, "^%s\n", sha1_to_hex(peeled)) < 0))
+       if (fprintf(fh, "%s %s\n", oid_to_hex(oid), refname) < 0 ||
+           (peeled && fprintf(fh, "^%s\n", oid_to_hex(peeled)) < 0))
                return -1;
 
        return 0;
@@ -1203,8 +1203,8 @@ static int write_with_updates(struct packed_ref_store *refs,
                        int peel_error = ref_iterator_peel(iter, &peeled);
 
                        if (write_packed_entry(out, iter->refname,
-                                              iter->oid->hash,
-                                              peel_error ? NULL : peeled.hash))
+                                              iter->oid,
+                                              peel_error ? NULL : &peeled))
                                goto write_error;
 
                        if ((ok = ref_iterator_advance(iter)) != ITER_OK)
@@ -1220,12 +1220,12 @@ static int write_with_updates(struct packed_ref_store *refs,
                        i++;
                } else {
                        struct object_id peeled;
-                       int peel_error = peel_object(update->new_oid.hash,
-                                                    peeled.hash);
+                       int peel_error = peel_object(&update->new_oid,
+                                                    &peeled);
 
                        if (write_packed_entry(out, update->refname,
-                                              update->new_oid.hash,
-                                              peel_error ? NULL : peeled.hash))
+                                              &update->new_oid,
+                                              peel_error ? NULL : &peeled))
                                goto write_error;
 
                        i++;
@@ -1261,6 +1261,100 @@ static int write_with_updates(struct packed_ref_store *refs,
        return -1;
 }
 
+int is_packed_transaction_needed(struct ref_store *ref_store,
+                                struct ref_transaction *transaction)
+{
+       struct packed_ref_store *refs = packed_downcast(
+                       ref_store,
+                       REF_STORE_READ,
+                       "is_packed_transaction_needed");
+       struct strbuf referent = STRBUF_INIT;
+       size_t i;
+       int ret;
+
+       if (!is_lock_file_locked(&refs->lock))
+               BUG("is_packed_transaction_needed() called while unlocked");
+
+       /*
+        * We're only going to bother returning false for the common,
+        * trivial case that references are only being deleted, their
+        * old values are not being checked, and the old `packed-refs`
+        * file doesn't contain any of those reference(s). This gives
+        * false positives for some other cases that could
+        * theoretically be optimized away:
+        *
+        * 1. It could be that the old value is being verified without
+        *    setting a new value. In this case, we could verify the
+        *    old value here and skip the update if it agrees. If it
+        *    disagrees, we could either let the update go through
+        *    (the actual commit would re-detect and report the
+        *    problem), or come up with a way of reporting such an
+        *    error to *our* caller.
+        *
+        * 2. It could be that a new value is being set, but that it
+        *    is identical to the current packed value of the
+        *    reference.
+        *
+        * Neither of these cases will come up in the current code,
+        * because the only caller of this function passes to it a
+        * transaction that only includes `delete` updates with no
+        * `old_id`. Even if that ever changes, false positives only
+        * cause an optimization to be missed; they do not affect
+        * correctness.
+        */
+
+       /*
+        * Start with the cheap checks that don't require old
+        * reference values to be read:
+        */
+       for (i = 0; i < transaction->nr; i++) {
+               struct ref_update *update = transaction->updates[i];
+
+               if (update->flags & REF_HAVE_OLD)
+                       /* Have to check the old value -> needed. */
+                       return 1;
+
+               if ((update->flags & REF_HAVE_NEW) && !is_null_oid(&update->new_oid))
+                       /* Have to set a new value -> needed. */
+                       return 1;
+       }
+
+       /*
+        * The transaction isn't checking any old values nor is it
+        * setting any nonzero new values, so it still might be able
+        * to be skipped. Now do the more expensive check: the update
+        * is needed if any of the updates is a delete, and the old
+        * `packed-refs` file contains a value for that reference.
+        */
+       ret = 0;
+       for (i = 0; i < transaction->nr; i++) {
+               struct ref_update *update = transaction->updates[i];
+               unsigned int type;
+               struct object_id oid;
+
+               if (!(update->flags & REF_HAVE_NEW))
+                       /*
+                        * This reference isn't being deleted -> not
+                        * needed.
+                        */
+                       continue;
+
+               if (!refs_read_raw_ref(ref_store, update->refname,
+                                      &oid, &referent, &type) ||
+                   errno != ENOENT) {
+                       /*
+                        * We have to actually delete that reference
+                        * -> this transaction is needed.
+                        */
+                       ret = 1;
+                       break;
+               }
+       }
+
+       strbuf_release(&referent);
+       return ret;
+}
+
 struct packed_transaction_backend_data {
        /* True iff the transaction owns the packed-refs lock. */
        int own_lock;
@@ -1519,7 +1613,7 @@ static int packed_delete_reflog(struct ref_store *ref_store,
 }
 
 static int packed_reflog_expire(struct ref_store *ref_store,
-                               const char *refname, const unsigned char *sha1,
+                               const char *refname, const struct object_id *oid,
                                unsigned int flags,
                                reflog_expiry_prepare_fn prepare_fn,
                                reflog_expiry_should_prune_fn should_prune_fn,
index 61687e408ad4b83d111531f9d5b10a058abcf673..640245d3b9f23a8ae7866dd477a5ed9bbfc71cde 100644 (file)
@@ -23,4 +23,13 @@ int packed_refs_lock(struct ref_store *ref_store, int flags, struct strbuf *err)
 void packed_refs_unlock(struct ref_store *ref_store);
 int packed_refs_is_locked(struct ref_store *ref_store);
 
+/*
+ * Return true if `transaction` really needs to be carried out against
+ * the specified packed_ref_store, or false if it can be skipped
+ * (i.e., because it is an obvious NOOP). `ref_store` must be locked
+ * before calling this function.
+ */
+int is_packed_transaction_needed(struct ref_store *ref_store,
+                                struct ref_transaction *transaction);
+
 #endif /* REFS_PACKED_BACKEND_H */
index 4f850e1b5c9ed1fae746a804ceceee42e2f54b86..82c1cf90a7ef61cc8174bff5a9800af2ec8d7053 100644 (file)
@@ -260,8 +260,8 @@ int add_ref_entry(struct ref_dir *dir, struct ref_entry *ref)
 
 /*
  * Emit a warning and return true iff ref1 and ref2 have the same name
- * and the same sha1.  Die if they have the same name but different
- * sha1s.
+ * and the same oid. Die if they have the same name but different
+ * oids.
  */
 static int is_dup_ref(const struct ref_entry *ref1, const struct ref_entry *ref2)
 {
@@ -493,7 +493,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
 static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
                                   struct object_id *peeled)
 {
-       return peel_object(ref_iterator->oid->hash, peeled->hash);
+       return peel_object(ref_iterator->oid, peeled);
 }
 
 static int cache_ref_iterator_abort(struct ref_iterator *ref_iterator)
index 448de4bccb8f3d9531b50d5489af001ec7ce4732..dd834314bd8d5af0315dab19af50f3300a73ad90 100644 (file)
@@ -8,58 +8,22 @@
  */
 
 /*
- * Flag passed to lock_ref_sha1_basic() telling it to tolerate broken
- * refs (i.e., because the reference is about to be deleted anyway).
+ * The following flags can appear in `ref_update::flags`. Their
+ * numerical values must not conflict with those of REF_NO_DEREF and
+ * REF_FORCE_CREATE_REFLOG, which are also stored in
+ * `ref_update::flags`.
  */
-#define REF_DELETING   0x02
 
 /*
- * Used as a flag in ref_update::flags when a loose ref is being
- * pruned. This flag must only be used when REF_NODEREF is set.
+ * The reference should be updated to new_oid.
  */
-#define REF_ISPRUNING  0x04
+#define REF_HAVE_NEW (1 << 2)
 
 /*
- * Used as a flag in ref_update::flags when the reference should be
- * updated to new_sha1.
+ * The current reference's value should be checked to make sure that
+ * it agrees with old_oid.
  */
-#define REF_HAVE_NEW   0x08
-
-/*
- * Used as a flag in ref_update::flags when old_sha1 should be
- * checked.
- */
-#define REF_HAVE_OLD   0x10
-
-/*
- * Used as a flag in ref_update::flags when the lockfile needs to be
- * committed.
- */
-#define REF_NEEDS_COMMIT 0x20
-
-/*
- * 0x40 is REF_FORCE_CREATE_REFLOG, so skip it if you're adding a
- * value to ref_update::flags
- */
-
-/*
- * Used as a flag in ref_update::flags when we want to log a ref
- * update but not actually perform it.  This is used when a symbolic
- * ref update is split up.
- */
-#define REF_LOG_ONLY 0x80
-
-/*
- * Internal flag, meaning that the containing ref_update was via an
- * update to HEAD.
- */
-#define REF_UPDATE_VIA_HEAD 0x100
-
-/*
- * Used as a flag in ref_update::flags when the loose reference has
- * been deleted.
- */
-#define REF_DELETED_LOOSE 0x200
+#define REF_HAVE_OLD (1 << 3)
 
 /*
  * Return the length of time to retry acquiring a loose reference lock
@@ -120,11 +84,11 @@ enum peel_status {
 /*
  * Peel the named object; i.e., if the object is a tag, resolve the
  * tag recursively until a non-tag is found.  If successful, store the
- * result to sha1 and return PEEL_PEELED.  If the object is not a tag
+ * result to oid and return PEEL_PEELED.  If the object is not a tag
  * or is not valid, return PEEL_NON_TAG or PEEL_INVALID, respectively,
- * and leave sha1 unchanged.
+ * and leave oid unchanged.
  */
-enum peel_status peel_object(const unsigned char *name, unsigned char *sha1);
+enum peel_status peel_object(const struct object_id *name, struct object_id *oid);
 
 /*
  * Copy the reflog message msg to buf, which has been allocated sufficiently
@@ -134,30 +98,29 @@ enum peel_status peel_object(const unsigned char *name, unsigned char *sha1);
 int copy_reflog_msg(char *buf, const char *msg);
 
 /**
- * Information needed for a single ref update. Set new_sha1 to the new
- * value or to null_sha1 to delete the ref. To check the old value
- * while the ref is locked, set (flags & REF_HAVE_OLD) and set
- * old_sha1 to the old value, or to null_sha1 to ensure the ref does
- * not exist before update.
+ * Information needed for a single ref update. Set new_oid to the new
+ * value or to null_oid to delete the ref. To check the old value
+ * while the ref is locked, set (flags & REF_HAVE_OLD) and set old_oid
+ * to the old value, or to null_oid to ensure the ref does not exist
+ * before update.
  */
 struct ref_update {
-
        /*
-        * If (flags & REF_HAVE_NEW), set the reference to this value:
+        * If (flags & REF_HAVE_NEW), set the reference to this value
+        * (or delete it, if `new_oid` is `null_oid`).
         */
        struct object_id new_oid;
 
        /*
         * If (flags & REF_HAVE_OLD), check that the reference
-        * previously had this value:
+        * previously had this value (or didn't previously exist, if
+        * `old_oid` is `null_oid`).
         */
        struct object_id old_oid;
 
        /*
-        * One or more of REF_HAVE_NEW, REF_HAVE_OLD, REF_NODEREF,
-        * REF_DELETING, REF_ISPRUNING, REF_LOG_ONLY,
-        * REF_UPDATE_VIA_HEAD, REF_NEEDS_COMMIT, and
-        * REF_DELETED_LOOSE:
+        * One or more of REF_NO_DEREF, REF_FORCE_CREATE_REFLOG,
+        * REF_HAVE_NEW, REF_HAVE_OLD, or backend-specific flags.
         */
        unsigned int flags;
 
@@ -181,7 +144,7 @@ struct ref_update {
 };
 
 int refs_read_raw_ref(struct ref_store *ref_store,
-                     const char *refname, unsigned char *sha1,
+                     const char *refname, struct object_id *oid,
                      struct strbuf *referent, unsigned int *type);
 
 /*
@@ -195,15 +158,15 @@ int ref_update_reject_duplicates(struct string_list *refnames,
 /*
  * Add a ref_update with the specified properties to transaction, and
  * return a pointer to the new object. This function does not verify
- * that refname is well-formed. new_sha1 and old_sha1 are only
+ * that refname is well-formed. new_oid and old_oid are only
  * dereferenced if the REF_HAVE_NEW and REF_HAVE_OLD bits,
  * respectively, are set in flags.
  */
 struct ref_update *ref_transaction_add_update(
                struct ref_transaction *transaction,
                const char *refname, unsigned int flags,
-               const unsigned char *new_sha1,
-               const unsigned char *old_sha1,
+               const struct object_id *new_oid,
+               const struct object_id *old_oid,
                const char *msg);
 
 /*
@@ -608,7 +571,7 @@ typedef int create_reflog_fn(struct ref_store *ref_store, const char *refname,
                             int force_create, struct strbuf *err);
 typedef int delete_reflog_fn(struct ref_store *ref_store, const char *refname);
 typedef int reflog_expire_fn(struct ref_store *ref_store,
-                            const char *refname, const unsigned char *sha1,
+                            const char *refname, const struct object_id *oid,
                             unsigned int flags,
                             reflog_expiry_prepare_fn prepare_fn,
                             reflog_expiry_should_prune_fn should_prune_fn,
@@ -619,13 +582,13 @@ typedef int reflog_expire_fn(struct ref_store *ref_store,
  * Read a reference from the specified reference store, non-recursively.
  * Set type to describe the reference, and:
  *
- * - If refname is the name of a normal reference, fill in sha1
+ * - If refname is the name of a normal reference, fill in oid
  *   (leaving referent unchanged).
  *
  * - If refname is the name of a symbolic reference, write the full
  *   name of the reference to which it refers (e.g.
  *   "refs/heads/master") to referent and set the REF_ISSYMREF bit in
- *   type (leaving sha1 unchanged). The caller is responsible for
+ *   type (leaving oid unchanged). The caller is responsible for
  *   validating that referent is a valid reference name.
  *
  * WARNING: refname might be used as part of a filename, so it is
@@ -637,7 +600,7 @@ typedef int reflog_expire_fn(struct ref_store *ref_store,
  *
  * Return 0 on success. If the ref doesn't exist, set errno to ENOENT
  * and return -1. If the ref exists but is neither a symbolic ref nor
- * a sha1, it is broken; set REF_ISBROKEN in type, set errno to
+ * an object ID, it is broken; set REF_ISBROKEN in type, set errno to
  * EINVAL, and return -1. If there is another error reading the ref,
  * set errno appropriately and return -1.
  *
@@ -654,7 +617,7 @@ typedef int reflog_expire_fn(struct ref_store *ref_store,
  *   refname will still be valid and unchanged.
  */
 typedef int read_raw_ref_fn(struct ref_store *ref_store,
-                           const char *refname, unsigned char *sha1,
+                           const char *refname, struct object_id *oid,
                            struct strbuf *referent, unsigned int *type);
 
 struct ref_storage_be {
index 0ff4a312629d4ed4250a076f39a9d78e76917158..bcebb4c789567eb4017a3a0132ba55c59c427991 100644 (file)
@@ -174,7 +174,7 @@ static int cmd_import(const char *line)
        struct child_process svndump_proc = CHILD_PROCESS_INIT;
        const char *command = "svnrdump";
 
-       if (read_ref(private_ref, head_oid.hash))
+       if (read_ref(private_ref, &head_oid))
                startrev = 0;
        else {
                note_msg = read_ref_note(&head_oid);
index b220f0dfc619a6d3150cb2bcee00114f76bd0223..4e93753e1988afd4a01559951f96142c6dc2e73d 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -675,6 +675,36 @@ const char *pushremote_for_branch(struct branch *branch, int *explicit)
        return remote_for_branch(branch, explicit);
 }
 
+const char *remote_ref_for_branch(struct branch *branch, int for_push,
+                                 int *explicit)
+{
+       if (branch) {
+               if (!for_push) {
+                       if (branch->merge_nr) {
+                               if (explicit)
+                                       *explicit = 1;
+                               return branch->merge_name[0];
+                       }
+               } else {
+                       const char *dst, *remote_name =
+                               pushremote_for_branch(branch, NULL);
+                       struct remote *remote = remote_get(remote_name);
+
+                       if (remote && remote->push_refspec_nr &&
+                           (dst = apply_refspecs(remote->push,
+                                                 remote->push_refspec_nr,
+                                                 branch->refname))) {
+                               if (explicit)
+                                       *explicit = 1;
+                               return dst;
+                       }
+               }
+       }
+       if (explicit)
+               *explicit = 0;
+       return "";
+}
+
 static struct remote *remote_get_1(const char *name,
                                   const char *(*get_default)(struct branch *, int *))
 {
@@ -1629,7 +1659,7 @@ static void set_merge(struct branch *ret)
                    strcmp(ret->remote_name, "."))
                        continue;
                if (dwim_ref(ret->merge_name[i], strlen(ret->merge_name[i]),
-                            oid.hash, &ref) == 1)
+                            &oid, &ref) == 1)
                        ret->merge[i]->dst = ref;
                else
                        ret->merge[i]->dst = xstrdup(ret->merge_name[i]);
@@ -2002,13 +2032,13 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
                return -1;
 
        /* Cannot stat if what we used to build on no longer exists */
-       if (read_ref(base, oid.hash))
+       if (read_ref(base, &oid))
                return -1;
        theirs = lookup_commit_reference(&oid);
        if (!theirs)
                return -1;
 
-       if (read_ref(branch->refname, oid.hash))
+       if (read_ref(branch->refname, &oid))
                return -1;
        ours = lookup_commit_reference(&oid);
        if (!ours)
@@ -2327,7 +2357,7 @@ static int remote_tracking(struct remote *remote, const char *refname,
        dst = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname);
        if (!dst)
                return -1; /* no tracking ref for refname at remote */
-       if (read_ref(dst, oid->hash))
+       if (read_ref(dst, oid))
                return -1; /* we know what the tracking ref is but we cannot read it */
        return 0;
 }
index 2ecf4c8c74ce590c9bcc917d997b3c7acf9c07c1..1f6611be214363a4be363fad959135a9d123cee0 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -223,6 +223,8 @@ struct branch {
 struct branch *branch_get(const char *name);
 const char *remote_for_branch(struct branch *branch, int *explicit);
 const char *pushremote_for_branch(struct branch *branch, int *explicit);
+const char *remote_ref_for_branch(struct branch *branch, int for_push,
+                                 int *explicit);
 
 int branch_has_merge_config(struct branch *branch);
 int branch_merge_matches(struct branch *, int n, const char *);
index bb2fae5446b7ea8d2bfac87faa30c165f714d121..998413b8bb8deaddef76d93f67be4a1435c9e3a7 100644 (file)
@@ -5,7 +5,7 @@
 
 /* The main repository */
 static struct repository the_repo = {
-       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &the_index, 0, 0
+       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &the_index, NULL, 0, 0
 };
 struct repository *the_repository = &the_repo;
 
@@ -64,6 +64,11 @@ void repo_set_gitdir(struct repository *repo, const char *path)
        free(old_gitdir);
 }
 
+void repo_set_hash_algo(struct repository *repo, int hash_algo)
+{
+       repo->hash_algo = &hash_algos[hash_algo];
+}
+
 /*
  * Attempt to resolve and set the provided 'gitdir' for repository 'repo'.
  * Return 0 upon success and a non-zero value upon failure.
@@ -136,6 +141,8 @@ int repo_init(struct repository *repo, const char *gitdir, const char *worktree)
        if (read_and_verify_repository_format(&format, repo->commondir))
                goto error;
 
+       repo_set_hash_algo(repo, format.hash_algo);
+
        if (worktree)
                repo_set_worktree(repo, worktree);
 
index 7f5e24a0a24011393e9b1b9986895ebe8b3f10ab..0329e40c7f5e72dad3ba46328a8e3d6c29ed8e58 100644 (file)
@@ -4,6 +4,7 @@
 struct config_set;
 struct index_state;
 struct submodule_cache;
+struct git_hash_algo;
 
 struct repository {
        /* Environment */
@@ -67,6 +68,9 @@ struct repository {
         */
        struct index_state *index;
 
+       /* Repository's current hash algorithm, as serialized on disk. */
+       const struct git_hash_algo *hash_algo;
+
        /* Configurations */
        /*
         * Bit used during initialization to indicate if repository state (like
@@ -86,6 +90,7 @@ extern struct repository *the_repository;
 
 extern void repo_set_gitdir(struct repository *repo, const char *path);
 extern void repo_set_worktree(struct repository *repo, const char *path);
+extern void repo_set_hash_algo(struct repository *repo, int algo);
 extern int repo_init(struct repository *repo, const char *gitdir, const char *worktree);
 extern int repo_submodule_init(struct repository *submodule,
                               struct repository *superproject,
index d167223e694e54626786740954454d4973e28752..f6a3da5cd969765ada5ef536f08fb0cf4846cd39 100644 (file)
@@ -395,8 +395,16 @@ static struct commit *one_relevant_parent(const struct rev_info *revs,
  * if the whole diff is removal of old data, and otherwise
  * REV_TREE_DIFFERENT (of course if the trees are the same we
  * want REV_TREE_SAME).
- * That means that once we get to REV_TREE_DIFFERENT, we do not
- * have to look any further.
+ *
+ * The only time we care about the distinction is when
+ * remove_empty_trees is in effect, in which case we care only about
+ * whether the whole change is REV_TREE_NEW, or if there's another type
+ * of change. Which means we can stop the diff early in either of these
+ * cases:
+ *
+ *   1. We're not using remove_empty_trees at all.
+ *
+ *   2. We saw anything except REV_TREE_NEW.
  */
 static int tree_difference = REV_TREE_SAME;
 
@@ -407,10 +415,11 @@ static void file_add_remove(struct diff_options *options,
                    const char *fullpath, unsigned dirty_submodule)
 {
        int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;
+       struct rev_info *revs = options->change_fn_data;
 
        tree_difference |= diff;
-       if (tree_difference == REV_TREE_DIFFERENT)
-               DIFF_OPT_SET(options, HAS_CHANGES);
+       if (!revs->remove_empty_trees || tree_difference != REV_TREE_NEW)
+               options->flags.has_changes = 1;
 }
 
 static void file_change(struct diff_options *options,
@@ -422,7 +431,7 @@ static void file_change(struct diff_options *options,
                 unsigned old_dirty_submodule, unsigned new_dirty_submodule)
 {
        tree_difference = REV_TREE_DIFFERENT;
-       DIFF_OPT_SET(options, HAS_CHANGES);
+       options->flags.has_changes = 1;
 }
 
 static int rev_compare_tree(struct rev_info *revs,
@@ -455,7 +464,7 @@ static int rev_compare_tree(struct rev_info *revs,
        }
 
        tree_difference = REV_TREE_SAME;
-       DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
+       revs->pruning.flags.has_changes = 0;
        if (diff_tree_oid(&t1->object.oid, &t2->object.oid, "",
                           &revs->pruning) < 0)
                return REV_TREE_DIFFERENT;
@@ -471,7 +480,7 @@ static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit)
                return 0;
 
        tree_difference = REV_TREE_SAME;
-       DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
+       revs->pruning.flags.has_changes = 0;
        retval = diff_tree_oid(NULL, &t1->object.oid, "", &revs->pruning);
 
        return retval >= 0 && (tree_difference == REV_TREE_SAME);
@@ -1403,10 +1412,11 @@ void init_revisions(struct rev_info *revs, const char *prefix)
        revs->abbrev = DEFAULT_ABBREV;
        revs->ignore_merges = 1;
        revs->simplify_history = 1;
-       DIFF_OPT_SET(&revs->pruning, RECURSIVE);
-       DIFF_OPT_SET(&revs->pruning, QUICK);
+       revs->pruning.flags.recursive = 1;
+       revs->pruning.flags.quick = 1;
        revs->pruning.add_remove = file_add_remove;
        revs->pruning.change = file_change;
+       revs->pruning.change_fn_data = revs;
        revs->sort_order = REV_SORT_IN_GRAPH_ORDER;
        revs->dense = 1;
        revs->prefix = prefix;
@@ -1822,7 +1832,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->simplify_by_decoration = 1;
                revs->limited = 1;
                revs->prune = 1;
-               load_ref_decorations(DECORATE_SHORT_REFS);
+               load_ref_decorations(NULL, DECORATE_SHORT_REFS);
        } else if (!strcmp(arg, "--date-order")) {
                revs->sort_order = REV_SORT_BY_COMMIT_DATE;
                revs->topo_order = 1;
@@ -1917,11 +1927,11 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                die("--unpacked=<packfile> no longer supported.");
        } else if (!strcmp(arg, "-r")) {
                revs->diff = 1;
-               DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
+               revs->diffopt.flags.recursive = 1;
        } else if (!strcmp(arg, "-t")) {
                revs->diff = 1;
-               DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
-               DIFF_OPT_SET(&revs->diffopt, TREE_IN_RECURSIVE);
+               revs->diffopt.flags.recursive = 1;
+               revs->diffopt.flags.tree_in_recursive = 1;
        } else if (!strcmp(arg, "-m")) {
                revs->ignore_merges = 0;
        } else if (!strcmp(arg, "-c")) {
@@ -2066,7 +2076,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_ERE;
        } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
                revs->grep_filter.ignore_case = 1;
-               DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE);
+               revs->diffopt.flags.pickaxe_ignore_case = 1;
        } else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) {
                revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_FIXED;
        } else if (!strcmp(arg, "--perl-regexp") || !strcmp(arg, "-P")) {
@@ -2399,7 +2409,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
        /* Pickaxe, diff-filter and rename following need diffs */
        if (revs->diffopt.pickaxe ||
            revs->diffopt.filter ||
-           DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
+           revs->diffopt.flags.follow_renames)
                revs->diff = 1;
 
        if (revs->topo_order)
@@ -2408,7 +2418,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
        if (revs->prune_data.nr) {
                copy_pathspec(&revs->pruning.pathspec, &revs->prune_data);
                /* Can't prune commits with rename following: the paths change.. */
-               if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
+               if (!revs->diffopt.flags.follow_renames)
                        revs->prune = 1;
                if (!revs->full_diff)
                        copy_pathspec(&revs->diffopt.pathspec,
index 014b2165b5a2f92ff18869effafd4c6df01b33eb..31fc5ea86eb6a3fe3650b8ac50f19f8621d90b9d 100644 (file)
@@ -5,6 +5,7 @@
 #include "argv-array.h"
 #include "thread-utils.h"
 #include "strbuf.h"
+#include "string-list.h"
 
 void child_process_init(struct child_process *child)
 {
@@ -1169,11 +1170,28 @@ const char *find_hook(const char *name)
        strbuf_reset(&path);
        strbuf_git_path(&path, "hooks/%s", name);
        if (access(path.buf, X_OK) < 0) {
+               int err = errno;
+
 #ifdef STRIP_EXTENSION
                strbuf_addstr(&path, STRIP_EXTENSION);
                if (access(path.buf, X_OK) >= 0)
                        return path.buf;
+               if (errno == EACCES)
+                       err = errno;
 #endif
+
+               if (err == EACCES && advice_ignored_hook) {
+                       static struct string_list advise_given = STRING_LIST_INIT_DUP;
+
+                       if (!string_list_lookup(&advise_given, name)) {
+                               string_list_insert(&advise_given, name);
+                               advise(_("The '%s' hook was ignored because "
+                                        "it's not set as executable.\n"
+                                        "You can disable this warning with "
+                                        "`git config advice.ignoredHook false`."),
+                                      path.buf);
+                       }
+               }
                return NULL;
        }
        return path.buf;
index f2a10cc4f24c10adf072eb26ba427f00037b3c79..e90bc316bb7a232a40c332c6d1add255d3893869 100644 (file)
@@ -347,7 +347,7 @@ static int read_oneliner(struct strbuf *buf,
 
 static struct tree *empty_tree(void)
 {
-       return lookup_tree(&empty_tree_oid);
+       return lookup_tree(the_hash_algo->empty_tree);
 }
 
 static int error_dirty_index(struct replay_opts *opts)
@@ -393,7 +393,7 @@ static int fast_forward_to(const struct object_id *to, const struct object_id *f
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
            ref_transaction_update(transaction, "HEAD",
-                                  to->hash, unborn ? null_sha1 : from->hash,
+                                  to, unborn ? &null_oid : from,
                                   0, sb.buf, &err) ||
            ref_transaction_commit(transaction, &err)) {
                ref_transaction_free(transaction);
@@ -438,7 +438,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
        char **xopt;
        static struct lock_file index_lock;
 
-       hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
+       if (hold_locked_index(&index_lock, LOCK_REPORT_ON_ERROR) < 0)
+               return -1;
 
        read_cache();
 
@@ -489,7 +490,7 @@ static int is_index_unchanged(void)
        struct object_id head_oid;
        struct commit *head_commit;
 
-       if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL))
+       if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
                return error(_("could not resolve HEAD commit\n"));
 
        head_commit = lookup_commit(&head_oid);
@@ -705,7 +706,7 @@ static int is_original_commit_empty(struct commit *commit)
                                oid_to_hex(&parent->object.oid));
                ptree_oid = &parent->tree->object.oid;
        } else {
-               ptree_oid = &empty_tree_oid; /* commit is root */
+               ptree_oid = the_hash_algo->empty_tree; /* commit is root */
        }
 
        return !oidcmp(ptree_oid, &commit->tree->object.oid);
@@ -958,8 +959,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
        } else {
                unborn = get_oid("HEAD", &head);
                if (unborn)
-                       oidcpy(&head, &empty_tree_oid);
-               if (index_differs_from(unborn ? EMPTY_TREE_SHA1_HEX : "HEAD", 0, 0))
+                       oidcpy(&head, the_hash_algo->empty_tree);
+               if (index_differs_from(unborn ? EMPTY_TREE_SHA1_HEX : "HEAD",
+                                      NULL, 0))
                        return error_dirty_index(opts);
        }
        discard_cache();
@@ -1115,12 +1117,12 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
         * write it at all.
         */
        if (command == TODO_PICK && !opts->no_commit && (res == 0 || res == 1) &&
-           update_ref(NULL, "CHERRY_PICK_HEAD", commit->object.oid.hash, NULL,
-                      REF_NODEREF, UPDATE_REFS_MSG_ON_ERR))
+           update_ref(NULL, "CHERRY_PICK_HEAD", &commit->object.oid, NULL,
+                      REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
                res = -1;
        if (command == TODO_REVERT && ((opts->no_commit && res == 0) || res == 1) &&
-           update_ref(NULL, "REVERT_HEAD", commit->object.oid.hash, NULL,
-                      REF_NODEREF, UPDATE_REFS_MSG_ON_ERR))
+           update_ref(NULL, "REVERT_HEAD", &commit->object.oid, NULL,
+                      REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
                res = -1;
 
        if (res) {
@@ -1184,7 +1186,6 @@ static int read_and_refresh_cache(struct replay_opts *opts)
        refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
        if (the_index.cache_changed && index_fd >= 0) {
                if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK)) {
-                       rollback_lock_file(&index_lock);
                        return error(_("git %s: failed to refresh the index"),
                                _(action_name(opts)));
                }
@@ -1630,7 +1631,7 @@ static int rollback_single_pick(void)
        if (!file_exists(git_path_cherry_pick_head()) &&
            !file_exists(git_path_revert_head()))
                return error(_("no cherry-pick or revert in progress"));
-       if (read_ref_full("HEAD", 0, head_oid.hash, NULL))
+       if (read_ref_full("HEAD", 0, &head_oid, NULL))
                return error(_("cannot resolve HEAD"));
        if (is_null_oid(&head_oid))
                return error(_("cannot abort from a branch yet to be born"));
@@ -1862,12 +1863,15 @@ static int error_failed_squash(struct commit *commit,
 
 static int do_exec(const char *command_line)
 {
+       struct argv_array child_env = ARGV_ARRAY_INIT;
        const char *child_argv[] = { NULL, NULL };
        int dirty, status;
 
        fprintf(stderr, "Executing: %s\n", command_line);
        child_argv[0] = command_line;
-       status = run_command_v_opt(child_argv, RUN_USING_SHELL);
+       argv_array_pushf(&child_env, "GIT_DIR=%s", absolute_path(get_git_dir()));
+       status = run_command_v_opt_cd_env(child_argv, RUN_USING_SHELL, NULL,
+                                         child_env.argv);
 
        /* force re-reading of the cache */
        if (discard_cache() < 0 || read_cache() < 0)
@@ -1897,6 +1901,8 @@ static int do_exec(const char *command_line)
                status = 1;
        }
 
+       argv_array_clear(&child_env);
+
        return status;
 }
 
@@ -2124,8 +2130,8 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                        }
                        msg = reflog_message(opts, "finish", "%s onto %s",
                                head_ref.buf, buf.buf);
-                       if (update_ref(msg, head_ref.buf, head.hash, orig.hash,
-                                       REF_NODEREF, UPDATE_REFS_MSG_ON_ERR)) {
+                       if (update_ref(msg, head_ref.buf, &head, &orig,
+                                      REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
                                res = error(_("could not update %s"),
                                        head_ref.buf);
                                goto cleanup_head_ref;
@@ -2279,7 +2285,7 @@ int sequencer_continue(struct replay_opts *opts)
                        if (res)
                                goto release_todo_list;
                }
-               if (index_differs_from("HEAD", 0, 0)) {
+               if (index_differs_from("HEAD", NULL, 0)) {
                        res = error_dirty_index(opts);
                        goto release_todo_list;
                }
@@ -2665,6 +2671,19 @@ int check_todo_list(void)
        return res;
 }
 
+static int rewrite_file(const char *path, const char *buf, size_t len)
+{
+       int rc = 0;
+       int fd = open(path, O_WRONLY | O_TRUNC);
+       if (fd < 0)
+               return error_errno(_("could not open '%s' for writing"), path);
+       if (write_in_full(fd, buf, len) < 0)
+               rc = error_errno(_("could not write to '%s'"), path);
+       if (close(fd) && !rc)
+               rc = error_errno(_("could not close '%s'"), path);
+       return rc;
+}
+
 /* skip picking commits whose parents are unchanged */
 int skip_unnecessary_picks(void)
 {
@@ -2737,29 +2756,11 @@ int skip_unnecessary_picks(void)
                }
                close(fd);
 
-               fd = open(rebase_path_todo(), O_WRONLY, 0666);
-               if (fd < 0) {
-                       error_errno(_("could not open '%s' for writing"),
-                                   rebase_path_todo());
-                       todo_list_release(&todo_list);
-                       return -1;
-               }
-               if (write_in_full(fd, todo_list.buf.buf + offset,
-                               todo_list.buf.len - offset) < 0) {
-                       error_errno(_("could not write to '%s'"),
-                                   rebase_path_todo());
-                       close(fd);
+               if (rewrite_file(rebase_path_todo(), todo_list.buf.buf + offset,
+                                todo_list.buf.len - offset) < 0) {
                        todo_list_release(&todo_list);
                        return -1;
                }
-               if (ftruncate(fd, todo_list.buf.len - offset) < 0) {
-                       error_errno(_("could not truncate '%s'"),
-                                   rebase_path_todo());
-                       todo_list_release(&todo_list);
-                       close(fd);
-                       return -1;
-               }
-               close(fd);
 
                todo_list.current = i;
                if (is_fixup(peek_command(&todo_list, 0)))
@@ -2944,15 +2945,7 @@ int rearrange_squash(void)
                        }
                }
 
-               fd = open(todo_file, O_WRONLY);
-               if (fd < 0)
-                       res = error_errno(_("could not open '%s'"), todo_file);
-               else if (write(fd, buf.buf, buf.len) < 0)
-                       res = error_errno(_("could not write to '%s'"), todo_file);
-               else if (ftruncate(fd, buf.len) < 0)
-                       res = error_errno(_("could not truncate '%s'"),
-                                          todo_file);
-               close(fd);
+               res = rewrite_file(todo_file, buf.buf, buf.len);
                strbuf_release(&buf);
        }
 
diff --git a/setup.c b/setup.c
index 03f51e056cd6e672ecd80ba94348173b9d496cc0..50c6b2ab11fa3579a17079c16a4fb7241df9b875 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -312,7 +312,9 @@ int is_git_directory(const char *suspect)
        size_t len;
 
        /* Check worktree-related signatures */
-       strbuf_addf(&path, "%s/HEAD", suspect);
+       strbuf_addstr(&path, suspect);
+       strbuf_complete(&path, '/');
+       strbuf_addstr(&path, "HEAD");
        if (validate_headref(path.buf))
                goto done;
 
@@ -432,16 +434,15 @@ static int check_repo_format(const char *var, const char *value, void *vdata)
        return 0;
 }
 
-static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
+static int check_repository_format_gently(const char *gitdir, struct repository_format *candidate, int *nongit_ok)
 {
        struct strbuf sb = STRBUF_INIT;
        struct strbuf err = STRBUF_INIT;
-       struct repository_format candidate;
        int has_common;
 
        has_common = get_common_dir(&sb, gitdir);
        strbuf_addstr(&sb, "/config");
-       read_repository_format(&candidate, sb.buf);
+       read_repository_format(candidate, sb.buf);
        strbuf_release(&sb);
 
        /*
@@ -449,10 +450,10 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
         * we treat a missing config as a silent "ok", even when nongit_ok
         * is unset.
         */
-       if (candidate.version < 0)
+       if (candidate->version < 0)
                return 0;
 
-       if (verify_repository_format(&candidate, &err) < 0) {
+       if (verify_repository_format(candidate, &err) < 0) {
                if (nongit_ok) {
                        warning("%s", err.buf);
                        strbuf_release(&err);
@@ -462,21 +463,21 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
                die("%s", err.buf);
        }
 
-       repository_format_precious_objects = candidate.precious_objects;
-       string_list_clear(&candidate.unknown_extensions, 0);
+       repository_format_precious_objects = candidate->precious_objects;
+       string_list_clear(&candidate->unknown_extensions, 0);
        if (!has_common) {
-               if (candidate.is_bare != -1) {
-                       is_bare_repository_cfg = candidate.is_bare;
+               if (candidate->is_bare != -1) {
+                       is_bare_repository_cfg = candidate->is_bare;
                        if (is_bare_repository_cfg == 1)
                                inside_work_tree = -1;
                }
-               if (candidate.work_tree) {
+               if (candidate->work_tree) {
                        free(git_work_tree_cfg);
-                       git_work_tree_cfg = candidate.work_tree;
+                       git_work_tree_cfg = candidate->work_tree;
                        inside_work_tree = -1;
                }
        } else {
-               free(candidate.work_tree);
+               free(candidate->work_tree);
        }
 
        return 0;
@@ -487,6 +488,7 @@ int read_repository_format(struct repository_format *format, const char *path)
        memset(format, 0, sizeof(*format));
        format->version = -1;
        format->is_bare = -1;
+       format->hash_algo = GIT_HASH_SHA1;
        string_list_init(&format->unknown_extensions, 1);
        git_config_from_file(check_repo_format, path, format);
        return format->version;
@@ -623,6 +625,7 @@ const char *read_gitfile_gently(const char *path, int *return_error_code)
 
 static const char *setup_explicit_git_dir(const char *gitdirenv,
                                          struct strbuf *cwd,
+                                         struct repository_format *repo_fmt,
                                          int *nongit_ok)
 {
        const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
@@ -648,7 +651,7 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
                die("Not a git repository: '%s'", gitdirenv);
        }
 
-       if (check_repository_format_gently(gitdirenv, nongit_ok)) {
+       if (check_repository_format_gently(gitdirenv, repo_fmt, nongit_ok)) {
                free(gitfile);
                return NULL;
        }
@@ -721,9 +724,10 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
 
 static const char *setup_discovered_git_dir(const char *gitdir,
                                            struct strbuf *cwd, int offset,
+                                           struct repository_format *repo_fmt,
                                            int *nongit_ok)
 {
-       if (check_repository_format_gently(gitdir, nongit_ok))
+       if (check_repository_format_gently(gitdir, repo_fmt, nongit_ok))
                return NULL;
 
        /* --work-tree is set without --git-dir; use discovered one */
@@ -735,7 +739,7 @@ static const char *setup_discovered_git_dir(const char *gitdir,
                        gitdir = to_free = real_pathdup(gitdir, 1);
                if (chdir(cwd->buf))
                        die_errno("Could not come back to cwd");
-               ret = setup_explicit_git_dir(gitdir, cwd, nongit_ok);
+               ret = setup_explicit_git_dir(gitdir, cwd, repo_fmt, nongit_ok);
                free(to_free);
                return ret;
        }
@@ -767,11 +771,12 @@ static const char *setup_discovered_git_dir(const char *gitdir,
 
 /* #16.1, #17.1, #20.1, #21.1, #22.1 (see t1510) */
 static const char *setup_bare_git_dir(struct strbuf *cwd, int offset,
+                                     struct repository_format *repo_fmt,
                                      int *nongit_ok)
 {
        int root_len;
 
-       if (check_repository_format_gently(".", nongit_ok))
+       if (check_repository_format_gently(".", repo_fmt, nongit_ok))
                return NULL;
 
        setenv(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, "0", 1);
@@ -783,7 +788,7 @@ static const char *setup_bare_git_dir(struct strbuf *cwd, int offset,
                gitdir = offset == cwd->len ? "." : xmemdupz(cwd->buf, offset);
                if (chdir(cwd->buf))
                        die_errno("Could not come back to cwd");
-               return setup_explicit_git_dir(gitdir, cwd, nongit_ok);
+               return setup_explicit_git_dir(gitdir, cwd, repo_fmt, nongit_ok);
        }
 
        inside_git_dir = 1;
@@ -1024,6 +1029,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
        static struct strbuf cwd = STRBUF_INIT;
        struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT;
        const char *prefix;
+       struct repository_format repo_fmt;
 
        /*
         * We may have read an incomplete configuration before
@@ -1051,18 +1057,18 @@ const char *setup_git_directory_gently(int *nongit_ok)
                prefix = NULL;
                break;
        case GIT_DIR_EXPLICIT:
-               prefix = setup_explicit_git_dir(gitdir.buf, &cwd, nongit_ok);
+               prefix = setup_explicit_git_dir(gitdir.buf, &cwd, &repo_fmt, nongit_ok);
                break;
        case GIT_DIR_DISCOVERED:
                if (dir.len < cwd.len && chdir(dir.buf))
                        die(_("Cannot change to '%s'"), dir.buf);
                prefix = setup_discovered_git_dir(gitdir.buf, &cwd, dir.len,
-                                                 nongit_ok);
+                                                 &repo_fmt, nongit_ok);
                break;
        case GIT_DIR_BARE:
                if (dir.len < cwd.len && chdir(dir.buf))
                        die(_("Cannot change to '%s'"), dir.buf);
-               prefix = setup_bare_git_dir(&cwd, dir.len, nongit_ok);
+               prefix = setup_bare_git_dir(&cwd, dir.len, &repo_fmt, nongit_ok);
                break;
        case GIT_DIR_HIT_CEILING:
                prefix = setup_nongit(cwd.buf, nongit_ok);
@@ -1108,6 +1114,8 @@ const char *setup_git_directory_gently(int *nongit_ok)
                        repo_set_gitdir(the_repository, gitdir);
                        setup_git_env();
                }
+               if (startup_info->have_repository)
+                       repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
        }
 
        strbuf_release(&dir);
@@ -1169,7 +1177,8 @@ int git_config_perm(const char *var, const char *value)
 
 void check_repository_format(void)
 {
-       check_repository_format_gently(get_git_dir(), NULL);
+       struct repository_format repo_fmt;
+       check_repository_format_gently(get_git_dir(), &repo_fmt, NULL);
        startup_info->have_repository = 1;
 }
 
index c3a2b5ad17c74613811a0ce850586fa1b9e099bd..09c6b445b9631202c69d2a1804a757148b43beb0 100644 (file)
@@ -30,8 +30,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/>.  */
 
 /* closeout.c - close standard output and standard error
    Copyright (C) 1998-2007 Free Software Foundation, Inc.
@@ -47,8 +46,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 <errno.h>
 #include <stdio.h>
index 10c3a0083dedee98dc3d48267ad349589adef3f0..b44f5247caa9389f8d1c7643b7d9d99418d21f72 100644 (file)
@@ -39,6 +39,64 @@ const struct object_id empty_blob_oid = {
        EMPTY_BLOB_SHA1_BIN_LITERAL
 };
 
+static void git_hash_sha1_init(void *ctx)
+{
+       git_SHA1_Init((git_SHA_CTX *)ctx);
+}
+
+static void git_hash_sha1_update(void *ctx, const void *data, size_t len)
+{
+       git_SHA1_Update((git_SHA_CTX *)ctx, data, len);
+}
+
+static void git_hash_sha1_final(unsigned char *hash, void *ctx)
+{
+       git_SHA1_Final(hash, (git_SHA_CTX *)ctx);
+}
+
+static void git_hash_unknown_init(void *ctx)
+{
+       die("trying to init unknown hash");
+}
+
+static void git_hash_unknown_update(void *ctx, const void *data, size_t len)
+{
+       die("trying to update unknown hash");
+}
+
+static void git_hash_unknown_final(unsigned char *hash, void *ctx)
+{
+       die("trying to finalize unknown hash");
+}
+
+const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {
+       {
+               NULL,
+               0x00000000,
+               0,
+               0,
+               0,
+               git_hash_unknown_init,
+               git_hash_unknown_update,
+               git_hash_unknown_final,
+               NULL,
+               NULL,
+       },
+       {
+               "sha-1",
+               /* "sha1", big-endian */
+               0x73686131,
+               sizeof(git_SHA_CTX),
+               GIT_SHA1_RAWSZ,
+               GIT_SHA1_HEXSZ,
+               git_hash_sha1_init,
+               git_hash_sha1_update,
+               git_hash_sha1_final,
+               &empty_tree_oid,
+               &empty_blob_oid,
+       },
+};
+
 /*
  * This is meant to hold a *small* number of objects that you would
  * want read_sha1_file() to be able to return, but yet you do not want
@@ -74,6 +132,18 @@ static struct cached_object *find_cached_object(const unsigned char *sha1)
        return NULL;
 }
 
+
+static enum safe_crlf get_safe_crlf(unsigned flags)
+{
+       if (flags & HASH_RENORMALIZE)
+               return SAFE_CRLF_RENORMALIZE;
+       else if (flags & HASH_WRITE_OBJECT)
+               return safe_crlf;
+       else
+               return SAFE_CRLF_FALSE;
+}
+
+
 int mkdir_in_gitdir(const char *path)
 {
        if (mkdir(path, 0777)) {
@@ -404,6 +474,9 @@ static void link_alt_odb_entries(const char *alt, int sep,
        struct strbuf objdirbuf = STRBUF_INIT;
        struct strbuf entry = STRBUF_INIT;
 
+       if (!alt || !*alt)
+               return;
+
        if (depth > 5) {
                error("%s: ignoring alternate object stores, nesting too deep.",
                                relative_base);
@@ -456,19 +529,19 @@ struct alternate_object_database *alloc_alt_odb(const char *dir)
 
 void add_to_alternates_file(const char *reference)
 {
-       struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+       struct lock_file lock = LOCK_INIT;
        char *alts = git_pathdup("objects/info/alternates");
        FILE *in, *out;
+       int found = 0;
 
-       hold_lock_file_for_update(lock, alts, LOCK_DIE_ON_ERROR);
-       out = fdopen_lock_file(lock, "w");
+       hold_lock_file_for_update(&lock, alts, LOCK_DIE_ON_ERROR);
+       out = fdopen_lock_file(&lock, "w");
        if (!out)
                die_errno("unable to fdopen alternates lockfile");
 
        in = fopen(alts, "r");
        if (in) {
                struct strbuf line = STRBUF_INIT;
-               int found = 0;
 
                while (strbuf_getline(&line, in) != EOF) {
                        if (!strcmp(reference, line.buf)) {
@@ -480,18 +553,15 @@ void add_to_alternates_file(const char *reference)
 
                strbuf_release(&line);
                fclose(in);
-
-               if (found) {
-                       rollback_lock_file(lock);
-                       lock = NULL;
-               }
        }
        else if (errno != ENOENT)
                die_errno("unable to read alternates file");
 
-       if (lock) {
+       if (found) {
+               rollback_lock_file(&lock);
+       } else {
                fprintf_or_die(out, "%s\n", reference);
-               if (commit_lock_file(lock))
+               if (commit_lock_file(&lock))
                        die_errno("unable to move new alternates file into place");
                if (alt_odb_tail)
                        link_alt_odb_entries(reference, '\n', NULL, 0);
@@ -607,7 +677,6 @@ void prepare_alt_odb(void)
                return;
 
        alt = getenv(ALTERNATE_DB_ENVIRONMENT);
-       if (!alt) alt = "";
 
        alt_odb_tail = &alt_odb_list;
        link_alt_odb_entries(alt, PATH_SEP, NULL, 0);
@@ -1153,6 +1222,9 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi,
                                    lookup_replace_object(sha1) :
                                    sha1;
 
+       if (is_null_sha1(real))
+               return -1;
+
        if (!oi)
                oi = &blank_oi;
 
@@ -1664,7 +1736,7 @@ static void check_tag(const void *buf, size_t size)
                die("corrupt tag");
 }
 
-static int index_mem(unsigned char *sha1, void *buf, size_t size,
+static int index_mem(struct object_id *oid, void *buf, size_t size,
                     enum object_type type,
                     const char *path, unsigned flags)
 {
@@ -1680,7 +1752,7 @@ static int index_mem(unsigned char *sha1, void *buf, size_t size,
        if ((type == OBJ_BLOB) && path) {
                struct strbuf nbuf = STRBUF_INIT;
                if (convert_to_git(&the_index, path, buf, size, &nbuf,
-                                  write_object ? safe_crlf : SAFE_CRLF_FALSE)) {
+                                  get_safe_crlf(flags))) {
                        buf = strbuf_detach(&nbuf, &size);
                        re_allocated = 1;
                }
@@ -1695,15 +1767,15 @@ static int index_mem(unsigned char *sha1, void *buf, size_t size,
        }
 
        if (write_object)
-               ret = write_sha1_file(buf, size, typename(type), sha1);
+               ret = write_sha1_file(buf, size, typename(type), oid->hash);
        else
-               ret = hash_sha1_file(buf, size, typename(type), sha1);
+               ret = hash_sha1_file(buf, size, typename(type), oid->hash);
        if (re_allocated)
                free(buf);
        return ret;
 }
 
-static int index_stream_convert_blob(unsigned char *sha1, int fd,
+static int index_stream_convert_blob(struct object_id *oid, int fd,
                                     const char *path, unsigned flags)
 {
        int ret;
@@ -1714,26 +1786,26 @@ static int index_stream_convert_blob(unsigned char *sha1, int fd,
        assert(would_convert_to_git_filter_fd(path));
 
        convert_to_git_filter_fd(&the_index, path, fd, &sbuf,
-                                write_object ? safe_crlf : SAFE_CRLF_FALSE);
+                                get_safe_crlf(flags));
 
        if (write_object)
                ret = write_sha1_file(sbuf.buf, sbuf.len, typename(OBJ_BLOB),
-                                     sha1);
+                                     oid->hash);
        else
                ret = hash_sha1_file(sbuf.buf, sbuf.len, typename(OBJ_BLOB),
-                                    sha1);
+                                    oid->hash);
        strbuf_release(&sbuf);
        return ret;
 }
 
-static int index_pipe(unsigned char *sha1, int fd, enum object_type type,
+static int index_pipe(struct object_id *oid, int fd, enum object_type type,
                      const char *path, unsigned flags)
 {
        struct strbuf sbuf = STRBUF_INIT;
        int ret;
 
        if (strbuf_read(&sbuf, fd, 4096) >= 0)
-               ret = index_mem(sha1, sbuf.buf, sbuf.len, type, path, flags);
+               ret = index_mem(oid, sbuf.buf, sbuf.len, type, path, flags);
        else
                ret = -1;
        strbuf_release(&sbuf);
@@ -1742,14 +1814,14 @@ static int index_pipe(unsigned char *sha1, int fd, enum object_type type,
 
 #define SMALL_FILE_SIZE (32*1024)
 
-static int index_core(unsigned char *sha1, int fd, size_t size,
+static int index_core(struct object_id *oid, int fd, size_t size,
                      enum object_type type, const char *path,
                      unsigned flags)
 {
        int ret;
 
        if (!size) {
-               ret = index_mem(sha1, "", size, type, path, flags);
+               ret = index_mem(oid, "", size, type, path, flags);
        } else if (size <= SMALL_FILE_SIZE) {
                char *buf = xmalloc(size);
                ssize_t read_result = read_in_full(fd, buf, size);
@@ -1760,11 +1832,11 @@ static int index_core(unsigned char *sha1, int fd, size_t size,
                        ret = error("short read while indexing %s",
                                    path ? path : "<unknown>");
                else
-                       ret = index_mem(sha1, buf, size, type, path, flags);
+                       ret = index_mem(oid, buf, size, type, path, flags);
                free(buf);
        } else {
                void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
-               ret = index_mem(sha1, buf, size, type, path, flags);
+               ret = index_mem(oid, buf, size, type, path, flags);
                munmap(buf, size);
        }
        return ret;
@@ -1802,12 +1874,12 @@ int index_fd(struct object_id *oid, int fd, struct stat *st,
         * die() for large files.
         */
        if (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(path))
-               ret = index_stream_convert_blob(oid->hash, fd, path, flags);
+               ret = index_stream_convert_blob(oid, fd, path, flags);
        else if (!S_ISREG(st->st_mode))
-               ret = index_pipe(oid->hash, fd, type, path, flags);
+               ret = index_pipe(oid, fd, type, path, flags);
        else if (st->st_size <= big_file_threshold || type != OBJ_BLOB ||
                 (path && would_convert_to_git(&the_index, path)))
-               ret = index_core(oid->hash, fd, xsize_t(st->st_size), type, path,
+               ret = index_core(oid, fd, xsize_t(st->st_size), type, path,
                                 flags);
        else
                ret = index_stream(oid, fd, xsize_t(st->st_size), type, path,
@@ -1841,7 +1913,7 @@ int index_path(struct object_id *oid, const char *path, struct stat *st, unsigne
                strbuf_release(&sb);
                break;
        case S_IFDIR:
-               return resolve_gitlink_ref(path, "HEAD", oid->hash);
+               return resolve_gitlink_ref(path, "HEAD", oid);
        default:
                return error("%s: unsupported file type", path);
        }
@@ -1884,6 +1956,7 @@ int for_each_file_in_obj_subdir(unsigned int subdir_nr,
        DIR *dir;
        struct dirent *de;
        int r = 0;
+       struct object_id oid;
 
        if (subdir_nr > 0xff)
                BUG("invalid loose object subdirectory: %x", subdir_nr);
@@ -1901,6 +1974,8 @@ int for_each_file_in_obj_subdir(unsigned int subdir_nr,
                return r;
        }
 
+       oid.hash[0] = subdir_nr;
+
        while ((de = readdir(dir))) {
                if (is_dot_or_dotdot(de->d_name))
                        continue;
@@ -1908,20 +1983,15 @@ int for_each_file_in_obj_subdir(unsigned int subdir_nr,
                strbuf_setlen(path, baselen);
                strbuf_addf(path, "/%s", de->d_name);
 
-               if (strlen(de->d_name) == GIT_SHA1_HEXSZ - 2)  {
-                       char hex[GIT_MAX_HEXSZ+1];
-                       struct object_id oid;
-
-                       xsnprintf(hex, sizeof(hex), "%02x%s",
-                                 subdir_nr, de->d_name);
-                       if (!get_oid_hex(hex, &oid)) {
-                               if (obj_cb) {
-                                       r = obj_cb(&oid, path->buf, data);
-                                       if (r)
-                                               break;
-                               }
-                               continue;
+               if (strlen(de->d_name) == GIT_SHA1_HEXSZ - 2 &&
+                   !hex_to_bytes(oid.hash + 1, de->d_name,
+                                 GIT_SHA1_RAWSZ - 1)) {
+                       if (obj_cb) {
+                               r = obj_cb(&oid, path->buf, data);
+                               if (r)
+                                       break;
                        }
+                       continue;
                }
 
                if (cruft_cb) {
index c7c5ab376ccb56b3a4f4533f61293bd543ea8097..611c7d24ddee678470ba74cb3c2ca669a778b44a 100644 (file)
@@ -153,7 +153,9 @@ static void unique_in_pack(struct packed_git *p,
        uint32_t num, last, i, first = 0;
        const struct object_id *current = NULL;
 
-       open_pack_index(p);
+       if (open_pack_index(p) || !p->num_objects)
+               return;
+
        num = p->num_objects;
        last = num;
        while (first < last) {
@@ -474,10 +476,104 @@ static unsigned msb(unsigned long val)
        return r;
 }
 
-int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)
+struct min_abbrev_data {
+       unsigned int init_len;
+       unsigned int cur_len;
+       char *hex;
+       const unsigned char *hash;
+};
+
+static inline char get_hex_char_from_oid(const struct object_id *oid,
+                                        unsigned int pos)
+{
+       static const char hex[] = "0123456789abcdef";
+
+       if ((pos & 1) == 0)
+               return hex[oid->hash[pos >> 1] >> 4];
+       else
+               return hex[oid->hash[pos >> 1] & 0xf];
+}
+
+static int extend_abbrev_len(const struct object_id *oid, void *cb_data)
 {
-       int status, exists;
+       struct min_abbrev_data *mad = cb_data;
 
+       unsigned int i = mad->init_len;
+       while (mad->hex[i] && mad->hex[i] == get_hex_char_from_oid(oid, i))
+               i++;
+
+       if (i < GIT_MAX_RAWSZ && i >= mad->cur_len)
+               mad->cur_len = i + 1;
+
+       return 0;
+}
+
+static void find_abbrev_len_for_pack(struct packed_git *p,
+                                    struct min_abbrev_data *mad)
+{
+       int match = 0;
+       uint32_t num, last, first = 0;
+       struct object_id oid;
+
+       if (open_pack_index(p) || !p->num_objects)
+               return;
+
+       num = p->num_objects;
+       last = num;
+       while (first < last) {
+               uint32_t mid = first + (last - first) / 2;
+               const unsigned char *current;
+               int cmp;
+
+               current = nth_packed_object_sha1(p, mid);
+               cmp = hashcmp(mad->hash, current);
+               if (!cmp) {
+                       match = 1;
+                       first = mid;
+                       break;
+               }
+               if (cmp > 0) {
+                       first = mid + 1;
+                       continue;
+               }
+               last = mid;
+       }
+
+       /*
+        * first is now the position in the packfile where we would insert
+        * mad->hash if it does not exist (or the position of mad->hash if
+        * it does exist). Hence, we consider a maximum of three objects
+        * nearby for the abbreviation length.
+        */
+       mad->init_len = 0;
+       if (!match) {
+               nth_packed_object_oid(&oid, p, first);
+               extend_abbrev_len(&oid, mad);
+       } else if (first < num - 1) {
+               nth_packed_object_oid(&oid, p, first + 1);
+               extend_abbrev_len(&oid, mad);
+       }
+       if (first > 0) {
+               nth_packed_object_oid(&oid, p, first - 1);
+               extend_abbrev_len(&oid, mad);
+       }
+       mad->init_len = mad->cur_len;
+}
+
+static void find_abbrev_len_packed(struct min_abbrev_data *mad)
+{
+       struct packed_git *p;
+
+       prepare_packed_git();
+       for (p = packed_git; p; p = p->next)
+               find_abbrev_len_for_pack(p, mad);
+}
+
+int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)
+{
+       struct disambiguate_state ds;
+       struct min_abbrev_data mad;
+       struct object_id oid_ret;
        if (len < 0) {
                unsigned long count = approximate_object_count();
                /*
@@ -503,19 +599,26 @@ int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)
        sha1_to_hex_r(hex, sha1);
        if (len == GIT_SHA1_HEXSZ || !len)
                return GIT_SHA1_HEXSZ;
-       exists = has_sha1_file(sha1);
-       while (len < GIT_SHA1_HEXSZ) {
-               struct object_id oid_ret;
-               status = get_short_oid(hex, len, &oid_ret, GET_OID_QUIETLY);
-               if (exists
-                   ? !status
-                   : status == SHORT_NAME_NOT_FOUND) {
-                       hex[len] = 0;
-                       return len;
-               }
-               len++;
-       }
-       return len;
+
+       mad.init_len = len;
+       mad.cur_len = len;
+       mad.hex = hex;
+       mad.hash = sha1;
+
+       find_abbrev_len_packed(&mad);
+
+       if (init_object_disambiguation(hex, mad.cur_len, &ds) < 0)
+               return -1;
+
+       ds.fn = extend_abbrev_len;
+       ds.always_call_fn = 1;
+       ds.cb_data = (void *)&mad;
+
+       find_short_object_filename(&ds);
+       (void)finish_object_disambiguation(&ds, &oid_ret);
+
+       hex[mad.cur_len] = 0;
+       return mad.cur_len;
 }
 
 const char *find_unique_abbrev(const unsigned char *sha1, int len)
@@ -603,7 +706,7 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,
 
        if (len == GIT_SHA1_HEXSZ && !get_oid_hex(str, oid)) {
                if (warn_ambiguous_refs && warn_on_object_refname_ambiguity) {
-                       refs_found = dwim_ref(str, len, tmp_oid.hash, &real_ref);
+                       refs_found = dwim_ref(str, len, &tmp_oid, &real_ref);
                        if (refs_found > 0) {
                                warning(warn_msg, len, str);
                                if (advice_object_name_warning)
@@ -654,11 +757,11 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,
 
        if (!len && reflog_len)
                /* allow "@{...}" to mean the current branch reflog */
-               refs_found = dwim_ref("HEAD", 4, oid->hash, &real_ref);
+               refs_found = dwim_ref("HEAD", 4, oid, &real_ref);
        else if (reflog_len)
-               refs_found = dwim_log(str, len, oid->hash, &real_ref);
+               refs_found = dwim_log(str, len, oid, &real_ref);
        else
-               refs_found = dwim_ref(str, len, oid->hash, &real_ref);
+               refs_found = dwim_ref(str, len, oid, &real_ref);
 
        if (!refs_found)
                return -1;
@@ -697,7 +800,7 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,
                                return -1;
                        }
                }
-               if (read_ref_at(real_ref, flags, at_time, nth, oid->hash, NULL,
+               if (read_ref_at(real_ref, flags, at_time, nth, oid, NULL,
                                &co_time, &co_tz, &co_cnt)) {
                        if (!len) {
                                if (starts_with(real_ref, "refs/heads/")) {
@@ -1331,10 +1434,23 @@ void strbuf_branchname(struct strbuf *sb, const char *name, unsigned allowed)
 
 int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
 {
-       strbuf_branchname(sb, name, INTERPRET_BRANCH_LOCAL);
-       if (name[0] == '-')
-               return -1;
+       if (startup_info->have_repository)
+               strbuf_branchname(sb, name, INTERPRET_BRANCH_LOCAL);
+       else
+               strbuf_addstr(sb, name);
+
+       /*
+        * This splice must be done even if we end up rejecting the
+        * name; builtin/branch.c::copy_or_rename_branch() still wants
+        * to see what the name expanded to so that "branch -m" can be
+        * used as a tool to correct earlier mistakes.
+        */
        strbuf_splice(sb, 0, 0, "refs/heads/", 11);
+
+       if (*name == '-' ||
+           !strcmp(sb->buf, "refs/heads/HEAD"))
+               return -1;
+
        return check_refname_format(sb->buf, 0);
 }
 
index e3845831f6cbd25097ea54b3e2246faa8a32e192..a5503a5d177e90e009be9240bfddd68c9ead475b 100644 (file)
@@ -22,6 +22,9 @@ struct submodule {
        int recommend_shallow;
 };
 
+#define SUBMODULE_INIT { NULL, NULL, NULL, RECURSE_SUBMODULES_NONE, \
+       NULL, NULL, SUBMODULE_UPDATE_STRATEGY_INIT, {0}, -1 };
+
 struct submodule_cache;
 struct repository;
 
index 63e7094e1620c2c3eb7dda0a8c38a833ae2d1c5a..fa25888783aa9ba7ce5b290ba64ec3c1b7a87cec 100644 (file)
@@ -21,7 +21,7 @@
 #include "parse-options.h"
 
 static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF;
-static struct string_list changed_submodule_paths = STRING_LIST_INIT_DUP;
+static struct string_list changed_submodule_names = STRING_LIST_INIT_DUP;
 static int initialized_fetch_ref_tips;
 static struct oid_array ref_tips_before_fetch;
 static struct oid_array ref_tips_after_fetch;
@@ -62,7 +62,7 @@ int is_staging_gitmodules_ok(const struct index_state *istate)
        if ((pos >= 0) && (pos < istate->cache_nr)) {
                struct stat st;
                if (lstat(GITMODULES_FILE, &st) == 0 &&
-                   ce_match_stat(istate->cache[pos], &st, 0) & DATA_CHANGED)
+                   ce_match_stat(istate->cache[pos], &st, CE_MATCH_IGNORE_FSMONITOR) & DATA_CHANGED)
                        return 0;
        }
 
@@ -183,7 +183,7 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
                if (ignore)
                        handle_ignore_submodules_arg(diffopt, ignore);
                else if (is_gitmodules_unmerged(&the_index))
-                       DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
+                       diffopt->flags.ignore_submodules = 1;
        }
 }
 
@@ -402,16 +402,16 @@ const char *submodule_strategy_to_string(const struct submodule_update_strategy
 void handle_ignore_submodules_arg(struct diff_options *diffopt,
                                  const char *arg)
 {
-       DIFF_OPT_CLR(diffopt, IGNORE_SUBMODULES);
-       DIFF_OPT_CLR(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
-       DIFF_OPT_CLR(diffopt, IGNORE_DIRTY_SUBMODULES);
+       diffopt->flags.ignore_submodules = 0;
+       diffopt->flags.ignore_untracked_in_submodules = 0;
+       diffopt->flags.ignore_dirty_submodules = 0;
 
        if (!strcmp(arg, "all"))
-               DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
+               diffopt->flags.ignore_submodules = 1;
        else if (!strcmp(arg, "untracked"))
-               DIFF_OPT_SET(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
+               diffopt->flags.ignore_untracked_in_submodules = 1;
        else if (!strcmp(arg, "dirty"))
-               DIFF_OPT_SET(diffopt, IGNORE_DIRTY_SUBMODULES);
+               diffopt->flags.ignore_dirty_submodules = 1;
        else if (strcmp(arg, "none"))
                die("bad --ignore-submodules argument: %s", arg);
 }
@@ -587,7 +587,7 @@ void show_submodule_inline_diff(struct diff_options *o, const char *path,
                struct object_id *one, struct object_id *two,
                unsigned dirty_submodule)
 {
-       const struct object_id *old = &empty_tree_oid, *new = &empty_tree_oid;
+       const struct object_id *old = the_hash_algo->empty_tree, *new = the_hash_algo->empty_tree;
        struct commit *left = NULL, *right = NULL;
        struct commit_list *merge_bases = NULL;
        struct child_process cp = CHILD_PROCESS_INIT;
@@ -616,7 +616,7 @@ void show_submodule_inline_diff(struct diff_options *o, const char *path,
        argv_array_pushf(&cp.args, "--color=%s", want_color(o->use_color) ?
                         "always" : "never");
 
-       if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
+       if (o->flags.reverse_diff) {
                argv_array_pushf(&cp.args, "--src-prefix=%s%s/",
                                 o->b_prefix, path);
                argv_array_pushf(&cp.args, "--dst-prefix=%s%s/",
@@ -674,11 +674,11 @@ const struct submodule *submodule_from_ce(const struct cache_entry *ce)
 }
 
 static struct oid_array *submodule_commits(struct string_list *submodules,
-                                          const char *path)
+                                          const char *name)
 {
        struct string_list_item *item;
 
-       item = string_list_insert(submodules, path);
+       item = string_list_insert(submodules, name);
        if (item->util)
                return (struct oid_array *) item->util;
 
@@ -687,39 +687,67 @@ static struct oid_array *submodule_commits(struct string_list *submodules,
        return (struct oid_array *) item->util;
 }
 
+struct collect_changed_submodules_cb_data {
+       struct string_list *changed;
+       const struct object_id *commit_oid;
+};
+
+/*
+ * this would normally be two functions: default_name_from_path() and
+ * path_from_default_name(). Since the default name is the same as
+ * the submodule path we can get away with just one function which only
+ * checks whether there is a submodule in the working directory at that
+ * location.
+ */
+static const char *default_name_or_path(const char *path_or_name)
+{
+       int error_code;
+
+       if (!is_submodule_populated_gently(path_or_name, &error_code))
+               return NULL;
+
+       return path_or_name;
+}
+
 static void collect_changed_submodules_cb(struct diff_queue_struct *q,
                                          struct diff_options *options,
                                          void *data)
 {
+       struct collect_changed_submodules_cb_data *me = data;
+       struct string_list *changed = me->changed;
+       const struct object_id *commit_oid = me->commit_oid;
        int i;
-       struct string_list *changed = data;
 
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
                struct oid_array *commits;
+               const struct submodule *submodule;
+               const char *name;
+
                if (!S_ISGITLINK(p->two->mode))
                        continue;
 
-               if (S_ISGITLINK(p->one->mode)) {
-                       /*
-                        * NEEDSWORK: We should honor the name configured in
-                        * the .gitmodules file of the commit we are examining
-                        * here to be able to correctly follow submodules
-                        * being moved around.
-                        */
-                       commits = submodule_commits(changed, p->two->path);
-                       oid_array_append(commits, &p->two->oid);
-               } else {
-                       /* Submodule is new or was moved here */
-                       /*
-                        * NEEDSWORK: When the .git directories of submodules
-                        * live inside the superprojects .git directory some
-                        * day we should fetch new submodules directly into
-                        * that location too when config or options request
-                        * that so they can be checked out from there.
-                        */
-                       continue;
+               submodule = submodule_from_path(commit_oid, p->two->path);
+               if (submodule)
+                       name = submodule->name;
+               else {
+                       name = default_name_or_path(p->two->path);
+                       /* make sure name does not collide with existing one */
+                       submodule = submodule_from_name(commit_oid, name);
+                       if (submodule) {
+                               warning("Submodule in commit %s at path: "
+                                       "'%s' collides with a submodule named "
+                                       "the same. Skipping it.",
+                                       oid_to_hex(commit_oid), name);
+                               name = NULL;
+                       }
                }
+
+               if (!name)
+                       continue;
+
+               commits = submodule_commits(changed, name);
+               oid_array_append(commits, &p->two->oid);
        }
 }
 
@@ -742,11 +770,14 @@ static void collect_changed_submodules(struct string_list *changed,
 
        while ((commit = get_revision(&rev))) {
                struct rev_info diff_rev;
+               struct collect_changed_submodules_cb_data data;
+               data.changed = changed;
+               data.commit_oid = &commit->object.oid;
 
                init_revisions(&diff_rev, NULL);
                diff_rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
                diff_rev.diffopt.format_callback = collect_changed_submodules_cb;
-               diff_rev.diffopt.format_callback_data = changed;
+               diff_rev.diffopt.format_callback_data = &data;
                diff_tree_combined_merge(commit, 1, &diff_rev);
        }
 
@@ -894,7 +925,7 @@ int find_unpushed_submodules(struct oid_array *commits,
                const char *remotes_name, struct string_list *needs_pushing)
 {
        struct string_list submodules = STRING_LIST_INIT_DUP;
-       struct string_list_item *submodule;
+       struct string_list_item *name;
        struct argv_array argv = ARGV_ARRAY_INIT;
 
        /* argv.argv[0] will be ignored by setup_revisions */
@@ -905,9 +936,19 @@ int find_unpushed_submodules(struct oid_array *commits,
 
        collect_changed_submodules(&submodules, &argv);
 
-       for_each_string_list_item(submodule, &submodules) {
-               struct oid_array *commits = submodule->util;
-               const char *path = submodule->string;
+       for_each_string_list_item(name, &submodules) {
+               struct oid_array *commits = name->util;
+               const struct submodule *submodule;
+               const char *path = NULL;
+
+               submodule = submodule_from_name(&null_oid, name->string);
+               if (submodule)
+                       path = submodule->path;
+               else
+                       path = default_name_or_path(name->string);
+
+               if (!path)
+                       continue;
 
                if (submodule_needs_pushing(path, commits))
                        string_list_insert(needs_pushing, path);
@@ -1016,7 +1057,7 @@ int push_unpushed_submodules(struct oid_array *commits,
                char *head;
                struct object_id head_oid;
 
-               head = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
+               head = resolve_refdup("HEAD", 0, &head_oid, NULL);
                if (!head)
                        die(_("Failed to resolve HEAD as a valid ref."));
 
@@ -1065,7 +1106,7 @@ static void calculate_changed_submodule_paths(void)
 {
        struct argv_array argv = ARGV_ARRAY_INIT;
        struct string_list changed_submodules = STRING_LIST_INIT_DUP;
-       const struct string_list_item *item;
+       const struct string_list_item *name;
 
        /* No need to check if there are no submodules configured */
        if (!submodule_from_path(NULL, NULL))
@@ -1080,16 +1121,26 @@ static void calculate_changed_submodule_paths(void)
 
        /*
         * Collect all submodules (whether checked out or not) for which new
-        * commits have been recorded upstream in "changed_submodule_paths".
+        * commits have been recorded upstream in "changed_submodule_names".
         */
        collect_changed_submodules(&changed_submodules, &argv);
 
-       for_each_string_list_item(item, &changed_submodules) {
-               struct oid_array *commits = item->util;
-               const char *path = item->string;
+       for_each_string_list_item(name, &changed_submodules) {
+               struct oid_array *commits = name->util;
+               const struct submodule *submodule;
+               const char *path = NULL;
+
+               submodule = submodule_from_name(&null_oid, name->string);
+               if (submodule)
+                       path = submodule->path;
+               else
+                       path = default_name_or_path(name->string);
+
+               if (!path)
+                       continue;
 
                if (!submodule_has_commits(path, commits))
-                       string_list_append(&changed_submodule_paths, path);
+                       string_list_append(&changed_submodule_names, name->string);
        }
 
        free_submodules_oids(&changed_submodules);
@@ -1136,6 +1187,31 @@ struct submodule_parallel_fetch {
 };
 #define SPF_INIT {0, ARGV_ARRAY_INIT, NULL, NULL, 0, 0, 0, 0}
 
+static int get_fetch_recurse_config(const struct submodule *submodule,
+                                   struct submodule_parallel_fetch *spf)
+{
+       if (spf->command_line_option != RECURSE_SUBMODULES_DEFAULT)
+               return spf->command_line_option;
+
+       if (submodule) {
+               char *key;
+               const char *value;
+
+               int fetch_recurse = submodule->fetch_recurse;
+               key = xstrfmt("submodule.%s.fetchRecurseSubmodules", submodule->name);
+               if (!repo_config_get_string_const(the_repository, key, &value)) {
+                       fetch_recurse = parse_fetch_recurse_submodules_arg(key, value);
+               }
+               free(key);
+
+               if (fetch_recurse != RECURSE_SUBMODULES_NONE)
+                       /* local config overrules everything except commandline */
+                       return fetch_recurse;
+       }
+
+       return spf->default_option;
+}
+
 static int get_next_submodule(struct child_process *cp,
                              struct strbuf *err, void *data, void **task_cb)
 {
@@ -1149,49 +1225,35 @@ static int get_next_submodule(struct child_process *cp,
                const struct cache_entry *ce = active_cache[spf->count];
                const char *git_dir, *default_argv;
                const struct submodule *submodule;
+               struct submodule default_submodule = SUBMODULE_INIT;
 
                if (!S_ISGITLINK(ce->ce_mode))
                        continue;
 
                submodule = submodule_from_path(&null_oid, ce->name);
-
-               default_argv = "yes";
-               if (spf->command_line_option == RECURSE_SUBMODULES_DEFAULT) {
-                       int fetch_recurse = RECURSE_SUBMODULES_NONE;
-
-                       if (submodule) {
-                               char *key;
-                               const char *value;
-
-                               fetch_recurse = submodule->fetch_recurse;
-                               key = xstrfmt("submodule.%s.fetchRecurseSubmodules", submodule->name);
-                               if (!repo_config_get_string_const(the_repository, key, &value)) {
-                                       fetch_recurse = parse_fetch_recurse_submodules_arg(key, value);
-                               }
-                               free(key);
+               if (!submodule) {
+                       const char *name = default_name_or_path(ce->name);
+                       if (name) {
+                               default_submodule.path = default_submodule.name = name;
+                               submodule = &default_submodule;
                        }
+               }
 
-                       if (fetch_recurse != RECURSE_SUBMODULES_NONE) {
-                               if (fetch_recurse == RECURSE_SUBMODULES_OFF)
-                                       continue;
-                               if (fetch_recurse == RECURSE_SUBMODULES_ON_DEMAND) {
-                                       if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
-                                               continue;
-                                       default_argv = "on-demand";
-                               }
-                       } else {
-                               if (spf->default_option == RECURSE_SUBMODULES_OFF)
-                                       continue;
-                               if (spf->default_option == RECURSE_SUBMODULES_ON_DEMAND) {
-                                       if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
-                                               continue;
-                                       default_argv = "on-demand";
-                               }
-                       }
-               } else if (spf->command_line_option == RECURSE_SUBMODULES_ON_DEMAND) {
-                       if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
+               switch (get_fetch_recurse_config(submodule, spf))
+               {
+               default:
+               case RECURSE_SUBMODULES_DEFAULT:
+               case RECURSE_SUBMODULES_ON_DEMAND:
+                       if (!submodule || !unsorted_string_list_lookup(&changed_submodule_names,
+                                                        submodule->name))
                                continue;
                        default_argv = "on-demand";
+                       break;
+               case RECURSE_SUBMODULES_ON:
+                       default_argv = "yes";
+                       break;
+               case RECURSE_SUBMODULES_OFF:
+                       continue;
                }
 
                strbuf_addf(&submodule_path, "%s/%s", spf->work_tree, ce->name);
@@ -1282,7 +1344,7 @@ int fetch_populated_submodules(const struct argv_array *options,
 
        argv_array_clear(&spf.args);
 out:
-       string_list_clear(&changed_submodule_paths, 1);
+       string_list_clear(&changed_submodule_names, 1);
        return spf.result;
 }
 
@@ -1608,7 +1670,8 @@ int submodule_move_head(const char *path,
                        cp.dir = path;
 
                        prepare_submodule_repo_env(&cp.env_array);
-                       argv_array_pushl(&cp.args, "update-ref", "HEAD", new, NULL);
+                       argv_array_pushl(&cp.args, "update-ref", "HEAD",
+                                        "--no-deref", new, NULL);
 
                        if (run_command(&cp)) {
                                ret = -1;
index 4b079e4494d9324f0b03dfe8f714b3717292c0b6..b3f7b449c366c2d7d7afd12139a54b7a588f9747 100644 (file)
--- a/t/README
+++ b/t/README
@@ -332,13 +332,10 @@ Writing Tests
 -------------
 
 The test script is written as a shell script.  It should start
-with the standard "#!/bin/sh" with copyright notices, and an
+with the standard "#!/bin/sh", and an
 assignment to variable 'test_description', like this:
 
        #!/bin/sh
-       #
-       # Copyright (c) 2005 Junio C Hamano
-       #
 
        test_description='xxx test (option --frotz)
 
@@ -677,6 +674,11 @@ library for your script to use.
    <expected> file.  This behaves like "cmp" but produces more
    helpful output when the test is run with "-v" option.
 
+ - test_cmp_rev <expected> <actual>
+
+   Check whether the <expected> rev points to the same commit as the
+   <actual> rev.
+
  - test_line_count (= | -lt | -ge | ...) <length> <file>
 
    Check whether a file has the length it is expected to.
@@ -808,6 +810,18 @@ use these, and "test_set_prereq" for how to define your own.
    Git was compiled with support for PCRE. Wrap any tests
    that use git-grep --perl-regexp or git-grep -P in these.
 
+ - LIBPCRE1
+
+   Git was compiled with PCRE v1 support via
+   USE_LIBPCRE1=YesPlease. Wrap any PCRE using tests that for some
+   reason need v1 of the PCRE library instead of v2 in these.
+
+ - LIBPCRE2
+
+   Git was compiled with PCRE v2 support via
+   USE_LIBPCRE2=YesPlease. Wrap any PCRE using tests that for some
+   reason need v2 of the PCRE library instead of v1 in these.
+
  - CASE_INSENSITIVE_FS
 
    Test is run on a case insensitive file system.
index 7c9d28a8348341c148f150c3371d09dd98693b53..d02f9b39ac3630ab6ebfc1ee0f7f266c19f93a0c 100644 (file)
@@ -3,7 +3,9 @@
 /test-config
 /test-date
 /test-delta
+/test-drop-caches
 /test-dump-cache-tree
+/test-dump-fsmonitor
 /test-dump-split-index
 /test-dump-untracked-cache
 /test-fake-ssh
index f414a3ac670fb2d77645f5d7301b6aa8f603379e..ac8368797073adfdf203f60d93df55a988443dbd 100644 (file)
@@ -5,6 +5,7 @@ static const char *usage_msg = "\n"
 "  test-date show:<format> [time_t]...\n"
 "  test-date parse [date]...\n"
 "  test-date approxidate [date]...\n"
+"  test-date timestamp [date]...\n"
 "  test-date is64bit\n"
 "  test-date time_t-is64bit\n";
 
@@ -71,6 +72,15 @@ static void parse_approxidate(const char **argv, struct timeval *now)
        }
 }
 
+static void parse_approx_timestamp(const char **argv, struct timeval *now)
+{
+       for (; *argv; argv++) {
+               timestamp_t t;
+               t = approxidate_relative(*argv, now);
+               printf("%s -> %"PRItime"\n", *argv, t);
+       }
+}
+
 int cmd_main(int argc, const char **argv)
 {
        struct timeval now;
@@ -95,6 +105,8 @@ int cmd_main(int argc, const char **argv)
                parse_dates(argv+1, &now);
        else if (!strcmp(*argv, "approxidate"))
                parse_approxidate(argv+1, &now);
+       else if (!strcmp(*argv, "timestamp"))
+               parse_approx_timestamp(argv+1, &now);
        else if (!strcmp(*argv, "is64bit"))
                return sizeof(timestamp_t) == 8 ? 0 : 1;
        else if (!strcmp(*argv, "time_t-is64bit"))
diff --git a/t/helper/test-drop-caches.c b/t/helper/test-drop-caches.c
new file mode 100644 (file)
index 0000000..bd1a857
--- /dev/null
@@ -0,0 +1,164 @@
+#include "git-compat-util.h"
+
+#if defined(GIT_WINDOWS_NATIVE)
+
+static int cmd_sync(void)
+{
+       char Buffer[MAX_PATH];
+       DWORD dwRet;
+       char szVolumeAccessPath[] = "\\\\.\\X:";
+       HANDLE hVolWrite;
+       int success = 0;
+
+       dwRet = GetCurrentDirectory(MAX_PATH, Buffer);
+       if ((0 == dwRet) || (dwRet > MAX_PATH))
+               return error("Error getting current directory");
+
+       if ((Buffer[0] < 'A') || (Buffer[0] > 'Z'))
+               return error("Invalid drive letter '%c'", Buffer[0]);
+
+       szVolumeAccessPath[4] = Buffer[0];
+       hVolWrite = CreateFile(szVolumeAccessPath, GENERIC_READ | GENERIC_WRITE,
+               FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+       if (INVALID_HANDLE_VALUE == hVolWrite)
+               return error("Unable to open volume for writing, need admin access");
+
+       success = FlushFileBuffers(hVolWrite);
+       if (!success)
+               error("Unable to flush volume");
+
+       CloseHandle(hVolWrite);
+
+       return !success;
+}
+
+#define STATUS_SUCCESS                 (0x00000000L)
+#define STATUS_PRIVILEGE_NOT_HELD      (0xC0000061L)
+
+typedef enum _SYSTEM_INFORMATION_CLASS {
+       SystemMemoryListInformation = 80,
+} SYSTEM_INFORMATION_CLASS;
+
+typedef enum _SYSTEM_MEMORY_LIST_COMMAND {
+       MemoryCaptureAccessedBits,
+       MemoryCaptureAndResetAccessedBits,
+       MemoryEmptyWorkingSets,
+       MemoryFlushModifiedList,
+       MemoryPurgeStandbyList,
+       MemoryPurgeLowPriorityStandbyList,
+       MemoryCommandMax
+} SYSTEM_MEMORY_LIST_COMMAND;
+
+static BOOL GetPrivilege(HANDLE TokenHandle, LPCSTR lpName, int flags)
+{
+       BOOL bResult;
+       DWORD dwBufferLength;
+       LUID luid;
+       TOKEN_PRIVILEGES tpPreviousState;
+       TOKEN_PRIVILEGES tpNewState;
+
+       dwBufferLength = 16;
+       bResult = LookupPrivilegeValueA(0, lpName, &luid);
+       if (bResult) {
+               tpNewState.PrivilegeCount = 1;
+               tpNewState.Privileges[0].Luid = luid;
+               tpNewState.Privileges[0].Attributes = 0;
+               bResult = AdjustTokenPrivileges(TokenHandle, 0, &tpNewState,
+                       (DWORD)((LPBYTE)&(tpNewState.Privileges[1]) - (LPBYTE)&tpNewState),
+                       &tpPreviousState, &dwBufferLength);
+               if (bResult) {
+                       tpPreviousState.PrivilegeCount = 1;
+                       tpPreviousState.Privileges[0].Luid = luid;
+                       tpPreviousState.Privileges[0].Attributes = flags != 0 ? 2 : 0;
+                       bResult = AdjustTokenPrivileges(TokenHandle, 0, &tpPreviousState,
+                               dwBufferLength, 0, 0);
+               }
+       }
+       return bResult;
+}
+
+static int cmd_dropcaches(void)
+{
+       HANDLE hProcess = GetCurrentProcess();
+       HANDLE hToken;
+       HMODULE ntdll;
+       DWORD(WINAPI *NtSetSystemInformation)(INT, PVOID, ULONG);
+       SYSTEM_MEMORY_LIST_COMMAND command;
+       int status;
+
+       if (!OpenProcessToken(hProcess, TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken))
+               return error("Can't open current process token");
+
+       if (!GetPrivilege(hToken, "SeProfileSingleProcessPrivilege", 1))
+               return error("Can't get SeProfileSingleProcessPrivilege");
+
+       CloseHandle(hToken);
+
+       ntdll = LoadLibrary("ntdll.dll");
+       if (!ntdll)
+               return error("Can't load ntdll.dll, wrong Windows version?");
+
+       NtSetSystemInformation =
+               (DWORD(WINAPI *)(INT, PVOID, ULONG))GetProcAddress(ntdll, "NtSetSystemInformation");
+       if (!NtSetSystemInformation)
+               return error("Can't get function addresses, wrong Windows version?");
+
+       command = MemoryPurgeStandbyList;
+       status = NtSetSystemInformation(
+               SystemMemoryListInformation,
+               &command,
+               sizeof(SYSTEM_MEMORY_LIST_COMMAND)
+       );
+       if (status == STATUS_PRIVILEGE_NOT_HELD)
+               error("Insufficient privileges to purge the standby list, need admin access");
+       else if (status != STATUS_SUCCESS)
+               error("Unable to execute the memory list command %d", status);
+
+       FreeLibrary(ntdll);
+
+       return status;
+}
+
+#elif defined(__linux__)
+
+static int cmd_sync(void)
+{
+       return system("sync");
+}
+
+static int cmd_dropcaches(void)
+{
+       return system("echo 3 | sudo tee /proc/sys/vm/drop_caches");
+}
+
+#elif defined(__APPLE__)
+
+static int cmd_sync(void)
+{
+       return system("sync");
+}
+
+static int cmd_dropcaches(void)
+{
+       return system("sudo purge");
+}
+
+#else
+
+static int cmd_sync(void)
+{
+       return 0;
+}
+
+static int cmd_dropcaches(void)
+{
+       return error("drop caches not implemented on this platform");
+}
+
+#endif
+
+int cmd_main(int argc, const char **argv)
+{
+       cmd_sync();
+       return cmd_dropcaches();
+}
diff --git a/t/helper/test-dump-fsmonitor.c b/t/helper/test-dump-fsmonitor.c
new file mode 100644 (file)
index 0000000..ad45270
--- /dev/null
@@ -0,0 +1,21 @@
+#include "cache.h"
+
+int cmd_main(int ac, const char **av)
+{
+       struct index_state *istate = &the_index;
+       int i;
+
+       setup_git_directory();
+       if (do_read_index(istate, get_index_file(), 0) < 0)
+               die("unable to read index file");
+       if (!istate->fsmonitor_last_update) {
+               printf("no fsmonitor\n");
+               return 0;
+       }
+       printf("fsmonitor last update %"PRIuMAX"\n", (uintmax_t)istate->fsmonitor_last_update);
+
+       for (i = 0; i < istate->cache_nr; i++)
+               printf((istate->cache[i]->ce_flags & CE_FSMONITOR_VALID) ? "+" : "-");
+
+       return 0;
+}
index 05d8c4d8af093ad42d855b17d243fe07b8893bfb..7120634b04733bb8abe1f0622f0e1e9c8280b643 100644 (file)
@@ -72,12 +72,12 @@ static int cmd_pack_refs(struct ref_store *refs, const char **argv)
 static int cmd_peel_ref(struct ref_store *refs, const char **argv)
 {
        const char *refname = notnull(*argv++, "refname");
-       unsigned char sha1[20];
+       struct object_id oid;
        int ret;
 
-       ret = refs_peel_ref(refs, refname, sha1);
+       ret = refs_peel_ref(refs, refname, &oid);
        if (!ret)
-               puts(sha1_to_hex(sha1));
+               puts(oid_to_hex(&oid));
        return ret;
 }
 
@@ -127,15 +127,15 @@ static int cmd_for_each_ref(struct ref_store *refs, const char **argv)
 
 static int cmd_resolve_ref(struct ref_store *refs, const char **argv)
 {
-       unsigned char sha1[20];
+       struct object_id oid;
        const char *refname = notnull(*argv++, "refname");
        int resolve_flags = arg_flags(*argv++, "resolve-flags");
        int flags;
        const char *ref;
 
        ref = refs_resolve_ref_unsafe(refs, refname, resolve_flags,
-                                     sha1, &flags);
-       printf("%s %s 0x%x\n", sha1_to_hex(sha1), ref, flags);
+                                     &oid, &flags);
+       printf("%s %s 0x%x\n", oid_to_hex(&oid), ref ? ref : "(null)", flags);
        return ref ? 0 : 1;
 }
 
@@ -218,12 +218,12 @@ static int cmd_delete_ref(struct ref_store *refs, const char **argv)
        const char *refname = notnull(*argv++, "refname");
        const char *sha1_buf = notnull(*argv++, "old-sha1");
        unsigned int flags = arg_flags(*argv++, "flags");
-       unsigned char old_sha1[20];
+       struct object_id old_oid;
 
-       if (get_sha1_hex(sha1_buf, old_sha1))
+       if (get_oid_hex(sha1_buf, &old_oid))
                die("not sha-1");
 
-       return refs_delete_ref(refs, msg, refname, old_sha1, flags);
+       return refs_delete_ref(refs, msg, refname, &old_oid, flags);
 }
 
 static int cmd_update_ref(struct ref_store *refs, const char **argv)
@@ -233,15 +233,15 @@ static int cmd_update_ref(struct ref_store *refs, const char **argv)
        const char *new_sha1_buf = notnull(*argv++, "old-sha1");
        const char *old_sha1_buf = notnull(*argv++, "old-sha1");
        unsigned int flags = arg_flags(*argv++, "flags");
-       unsigned char old_sha1[20];
-       unsigned char new_sha1[20];
+       struct object_id old_oid;
+       struct object_id new_oid;
 
-       if (get_sha1_hex(old_sha1_buf, old_sha1) ||
-           get_sha1_hex(new_sha1_buf, new_sha1))
+       if (get_oid_hex(old_sha1_buf, &old_oid) ||
+           get_oid_hex(new_sha1_buf, &new_oid))
                die("not sha-1");
 
        return refs_update_ref(refs, msg, refname,
-                              new_sha1, old_sha1,
+                              &new_oid, &old_oid,
                               flags, UPDATE_REFS_DIE_ON_ERR);
 }
 
diff --git a/t/interop/i5700-protocol-transition.sh b/t/interop/i5700-protocol-transition.sh
new file mode 100755 (executable)
index 0000000..97e8e58
--- /dev/null
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+VERSION_A=.
+VERSION_B=v2.0.0
+
+: ${LIB_GIT_DAEMON_PORT:=5700}
+LIB_GIT_DAEMON_COMMAND='git.b daemon'
+
+test_description='clone and fetch by client who is trying to use a new protocol'
+. ./interop-lib.sh
+. "$TEST_DIRECTORY"/lib-git-daemon.sh
+
+start_git_daemon --export-all
+
+repo=$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo
+
+test_expect_success "create repo served by $VERSION_B" '
+       git.b init "$repo" &&
+       git.b -C "$repo" commit --allow-empty -m one
+'
+
+test_expect_success "git:// clone with $VERSION_A and protocol v1" '
+       GIT_TRACE_PACKET=1 git.a -c protocol.version=1 clone "$GIT_DAEMON_URL/repo" child 2>log &&
+       git.a -C child log -1 --format=%s >actual &&
+       git.b -C "$repo" log -1 --format=%s >expect &&
+       test_cmp expect actual &&
+       grep "version=1" log
+'
+
+test_expect_success "git:// fetch with $VERSION_A and protocol v1" '
+       git.b -C "$repo" commit --allow-empty -m two &&
+       git.b -C "$repo" log -1 --format=%s >expect &&
+
+       GIT_TRACE_PACKET=1 git.a -C child -c protocol.version=1 fetch 2>log &&
+       git.a -C child log -1 --format=%s FETCH_HEAD >actual &&
+
+       test_cmp expect actual &&
+       grep "version=1" log &&
+       ! grep "version 1" log
+'
+
+stop_git_daemon
+
+test_expect_success "create repo served by $VERSION_B" '
+       git.b init parent &&
+       git.b -C parent commit --allow-empty -m one
+'
+
+test_expect_success "file:// clone with $VERSION_A and protocol v1" '
+       GIT_TRACE_PACKET=1 git.a -c protocol.version=1 clone --upload-pack="git.b upload-pack" parent child2 2>log &&
+       git.a -C child2 log -1 --format=%s >actual &&
+       git.b -C parent log -1 --format=%s >expect &&
+       test_cmp expect actual &&
+       ! grep "version 1" log
+'
+
+test_expect_success "file:// fetch with $VERSION_A and protocol v1" '
+       git.b -C parent commit --allow-empty -m two &&
+       git.b -C parent log -1 --format=%s >expect &&
+
+       GIT_TRACE_PACKET=1 git.a -C child2 -c protocol.version=1 fetch --upload-pack="git.b upload-pack" 2>log &&
+       git.a -C child2 log -1 --format=%s FETCH_HEAD >actual &&
+
+       test_cmp expect actual &&
+       ! grep "version 1" log
+'
+
+test_done
index d8e41f7ddd155b9f2861f55ab97943fed28968e7..937b831ea675230c1e0fbe8ba7ebb3004de71b3b 100755 (executable)
@@ -44,6 +44,7 @@ helper_test_clean() {
        reject $1 https example.com user2
        reject $1 http path.tld user
        reject $1 https timeout.tld user
+       reject $1 https sso.tld
 }
 
 reject() {
@@ -250,6 +251,24 @@ helper_test() {
                password=pass2
                EOF
        '
+
+       test_expect_success "helper ($HELPER) can store empty username" '
+               check approve $HELPER <<-\EOF &&
+               protocol=https
+               host=sso.tld
+               username=
+               password=
+               EOF
+               check fill $HELPER <<-\EOF
+               protocol=https
+               host=sso.tld
+               --
+               protocol=https
+               host=sso.tld
+               username=
+               password=
+               EOF
+       '
 }
 
 helper_test_timeout() {
index 43679a4c640e874aed8d3a378a378b1a6080fc1c..a5d3b2cbaad4edd078b8d58160dc4252ce8adec9 100755 (executable)
@@ -31,7 +31,7 @@ then
                chmod 0700 ./gpghome &&
                GNUPGHOME="$(pwd)/gpghome" &&
                export GNUPGHOME &&
-               (gpgconf --kill gpg-agent 2>&1 >/dev/null || : ) &&
+               (gpgconf --kill gpg-agent >/dev/null 2>&1 || : ) &&
                gpg --homedir "${GNUPGHOME}" 2>/dev/null --import \
                        "$TEST_DIRECTORY"/lib-gpg/keyring.gpg &&
                gpg --homedir "${GNUPGHOME}" 2>/dev/null --import-ownertrust \
index 0642ae7e6ef0fbbd9e7eacdebd061d258c8b4222..df19436314b7c6d16833044c5c53ba90eae62225 100644 (file)
@@ -67,6 +67,9 @@ LockFile accept.lock
 <IfModule !mod_unixd.c>
        LoadModule unixd_module modules/mod_unixd.so
 </IfModule>
+<IfModule !mod_setenvif.c>
+       LoadModule setenvif_module modules/mod_setenvif.so
+</IfModule>
 </IfVersion>
 
 PassEnv GIT_VALGRIND
@@ -76,6 +79,10 @@ PassEnv ASAN_OPTIONS
 PassEnv GIT_TRACE
 PassEnv GIT_CONFIG_NOSYSTEM
 
+<IfVersion >= 2.4>
+       SetEnvIf Git-Protocol ".*" GIT_PROTOCOL=$0
+</IfVersion>
+
 Alias /dumb/ www/
 Alias /auth/dumb/ www/auth/dumb/
 
index 2d26f86800906ab1783edf10e7196adadd5f4af7..38dadd2c2902f3621d54168d32a54d7863497968 100755 (executable)
@@ -306,9 +306,9 @@ test_submodule_content () {
 # to protect the history!
 #
 
-# Test that submodule contents are currently not updated when switching
-# between commits that change a submodule.
-test_submodule_switch () {
+# Internal function; use test_submodule_switch() or
+# test_submodule_forced_switch() instead.
+test_submodule_switch_common() {
        command="$1"
        ######################### Appearing submodule #########################
        # Switching to a commit letting a submodule appear creates empty dir ...
@@ -332,7 +332,7 @@ test_submodule_switch () {
                        test_submodule_content sub1 origin/add_sub1
                )
        '
-       # ... and doesn't care if it already exists ...
+       # ... and doesn't care if it already exists.
        test_expect_$RESULT "$command: added submodule leaves existing empty directory alone" '
                prolog &&
                reset_work_tree_to no_submodule &&
@@ -347,19 +347,6 @@ test_submodule_switch () {
                        test_submodule_content sub1 origin/add_sub1
                )
        '
-       # ... unless there is an untracked file in its place.
-       test_expect_success "$command: added submodule doesn't remove untracked unignored file with same name" '
-               prolog &&
-               reset_work_tree_to no_submodule &&
-               (
-                       cd submodule_update &&
-                       git branch -t add_sub1 origin/add_sub1 &&
-                       >sub1 &&
-                       test_must_fail $command add_sub1 &&
-                       test_superproject_content origin/no_submodule &&
-                       test_must_be_empty sub1
-               )
-       '
        # Replacing a tracked file with a submodule produces an empty
        # directory ...
        test_expect_$RESULT "$command: replace tracked file with submodule creates empty directory" '
@@ -441,6 +428,11 @@ test_submodule_switch () {
                # submodule files with the newly checked out ones in the
                # directory of the same name while it shouldn't.
                RESULT="failure"
+       elif test "$KNOWN_FAILURE_FORCED_SWITCH_TESTS" = 1
+       then
+               # All existing tests that use test_submodule_forced_switch()
+               # require this.
+               RESULT="failure"
        else
                RESULT="success"
        fi
@@ -522,7 +514,6 @@ test_submodule_switch () {
                        test_submodule_content sub1 origin/modify_sub1
                )
        '
-
        # Updating a submodule to an invalid sha1 doesn't update the
        # submodule's work tree, subsequent update will fail
        test_expect_$RESULT "$command: modified submodule does not update submodule work tree to invalid commit" '
@@ -555,235 +546,269 @@ test_submodule_switch () {
        '
 }
 
-# Test that submodule contents are currently not updated when switching
-# between commits that change a submodule, but throwing away local changes in
+# Declares and invokes several tests that, in various situations, checks that
+# the provided transition function:
+#  - succeeds in updating the worktree and index of a superproject to a target
+#    commit, or fails atomically (depending on the test situation)
+#  - if succeeds, the contents of submodule directories are unchanged
+#  - if succeeds, once "git submodule update" is invoked, the contents of
+#    submodule directories are updated
+#
+# If the command under test is known to not work with submodules in certain
+# conditions, set the appropriate KNOWN_FAILURE_* variable used in the tests
+# below to 1.
+#
+# Use as follows:
+#
+# my_func () {
+#   target=$1
+#   # Do something here that updates the worktree and index to match target,
+#   # but not any submodule directories.
+# }
+# test_submodule_switch "my_func"
+test_submodule_switch () {
+       command="$1"
+       test_submodule_switch_common "$command"
+
+       # An empty directory does not prevent the creation of a submodule of
+       # the same name, but a file does.
+       test_expect_success "$command: added submodule doesn't remove untracked unignored file with same name" '
+               prolog &&
+               reset_work_tree_to no_submodule &&
+               (
+                       cd submodule_update &&
+                       git branch -t add_sub1 origin/add_sub1 &&
+                       >sub1 &&
+                       test_must_fail $command add_sub1 &&
+                       test_superproject_content origin/no_submodule &&
+                       test_must_be_empty sub1
+               )
+       '
+}
+
+# Same as test_submodule_switch(), except that throwing away local changes in
 # the superproject is allowed.
 test_submodule_forced_switch () {
        command="$1"
-       ######################### Appearing submodule #########################
-       # Switching to a commit letting a submodule appear creates empty dir ...
-       test_expect_success "$command: added submodule creates empty directory" '
+       KNOWN_FAILURE_FORCED_SWITCH_TESTS=1
+       test_submodule_switch_common "$command"
+
+       # When forced, a file in the superproject does not prevent creating a
+       # submodule of the same name.
+       test_expect_success "$command: added submodule does remove untracked unignored file with same name when forced" '
                prolog &&
                reset_work_tree_to no_submodule &&
                (
                        cd submodule_update &&
                        git branch -t add_sub1 origin/add_sub1 &&
+                       >sub1 &&
                        $command add_sub1 &&
                        test_superproject_content origin/add_sub1 &&
-                       test_dir_is_empty sub1 &&
-                       git submodule update --init --recursive &&
-                       test_submodule_content sub1 origin/add_sub1
+                       test_dir_is_empty sub1
                )
        '
-       # ... and doesn't care if it already exists ...
-       test_expect_success "$command: added submodule leaves existing empty directory alone" '
+}
+
+# Test that submodule contents are correctly updated when switching
+# between commits that change a submodule.
+# Test that the following transitions are correctly handled:
+# (These tests are also above in the case where we expect no change
+#  in the submodule)
+# - Updated submodule
+# - New submodule
+# - Removed submodule
+# - Directory containing tracked files replaced by submodule
+# - Submodule replaced by tracked files in directory
+# - Submodule replaced by tracked file with the same name
+# - tracked file replaced by submodule
+#
+# New test cases
+# - Removing a submodule with a git directory absorbs the submodules
+#   git directory first into the superproject.
+
+# Internal function; use test_submodule_switch_recursing_with_args() or
+# test_submodule_forced_switch_recursing_with_args() instead.
+test_submodule_recursing_with_args_common() {
+       command="$1"
+
+       ######################### Appearing submodule #########################
+       # Switching to a commit letting a submodule appear checks it out ...
+       test_expect_success "$command: added submodule is checked out" '
                prolog &&
-               reset_work_tree_to no_submodule &&
+               reset_work_tree_to_interested no_submodule &&
                (
                        cd submodule_update &&
                        git branch -t add_sub1 origin/add_sub1 &&
-                       mkdir sub1 &&
                        $command add_sub1 &&
                        test_superproject_content origin/add_sub1 &&
-                       test_dir_is_empty sub1 &&
-                       git submodule update --init --recursive &&
                        test_submodule_content sub1 origin/add_sub1
                )
        '
-       # ... unless there is an untracked file in its place.
-       test_expect_success "$command: added submodule does remove untracked unignored file with same name when forced" '
+       # ... ignoring an empty existing directory.
+       test_expect_success "$command: added submodule is checked out in empty dir" '
                prolog &&
-               reset_work_tree_to no_submodule &&
+               reset_work_tree_to_interested no_submodule &&
                (
                        cd submodule_update &&
+                       mkdir sub1 &&
                        git branch -t add_sub1 origin/add_sub1 &&
-                       >sub1 &&
                        $command add_sub1 &&
                        test_superproject_content origin/add_sub1 &&
-                       test_dir_is_empty sub1
+                       test_submodule_content sub1 origin/add_sub1
                )
        '
-       # Replacing a tracked file with a submodule produces an empty
-       # directory ...
-       test_expect_success "$command: replace tracked file with submodule creates empty directory" '
+       test_expect_success "$command: submodule branch is not changed, detach HEAD instead" '
                prolog &&
-               reset_work_tree_to replace_sub1_with_file &&
+               reset_work_tree_to_interested add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git -C sub1 checkout -b keep_branch &&
+                       git -C sub1 rev-parse HEAD >expect &&
+                       git branch -t check-keep origin/modify_sub1 &&
+                       $command check-keep &&
+                       test_superproject_content origin/modify_sub1 &&
+                       test_submodule_content sub1 origin/modify_sub1 &&
+                       git -C sub1 rev-parse keep_branch >actual &&
+                       test_cmp expect actual &&
+                       test_must_fail git -C sub1 symbolic-ref HEAD
+               )
+       '
+
+       # Replacing a tracked file with a submodule produces a checked out submodule
+       test_expect_success "$command: replace tracked file with submodule checks out submodule" '
+               prolog &&
+               reset_work_tree_to_interested replace_sub1_with_file &&
                (
                        cd submodule_update &&
                        git branch -t replace_file_with_sub1 origin/replace_file_with_sub1 &&
                        $command replace_file_with_sub1 &&
                        test_superproject_content origin/replace_file_with_sub1 &&
-                       test_dir_is_empty sub1 &&
-                       git submodule update --init --recursive &&
                        test_submodule_content sub1 origin/replace_file_with_sub1
                )
        '
-       # ... as does removing a directory with tracked files with a
-       # submodule.
+       # ... as does removing a directory with tracked files with a submodule.
        test_expect_success "$command: replace directory with submodule" '
                prolog &&
-               reset_work_tree_to replace_sub1_with_directory &&
+               reset_work_tree_to_interested replace_sub1_with_directory &&
                (
                        cd submodule_update &&
                        git branch -t replace_directory_with_sub1 origin/replace_directory_with_sub1 &&
                        $command replace_directory_with_sub1 &&
                        test_superproject_content origin/replace_directory_with_sub1 &&
-                       test_dir_is_empty sub1 &&
-                       git submodule update --init --recursive &&
                        test_submodule_content sub1 origin/replace_directory_with_sub1
                )
        '
 
        ######################## Disappearing submodule #######################
-       # Removing a submodule doesn't remove its work tree ...
-       test_expect_success "$command: removed submodule leaves submodule directory and its contents in place" '
+       # Removing a submodule removes its work tree ...
+       test_expect_success "$command: removed submodule removes submodules working tree" '
                prolog &&
-               reset_work_tree_to add_sub1 &&
+               reset_work_tree_to_interested add_sub1 &&
                (
                        cd submodule_update &&
                        git branch -t remove_sub1 origin/remove_sub1 &&
                        $command remove_sub1 &&
                        test_superproject_content origin/remove_sub1 &&
-                       test_submodule_content sub1 origin/add_sub1
+                       ! test -e sub1
                )
        '
-       # ... especially when it contains a .git directory.
-       test_expect_success "$command: removed submodule leaves submodule containing a .git directory alone" '
+       # ... absorbing a .git directory along the way.
+       test_expect_success "$command: removed submodule absorbs submodules .git directory" '
                prolog &&
-               reset_work_tree_to add_sub1 &&
+               reset_work_tree_to_interested add_sub1 &&
                (
                        cd submodule_update &&
                        git branch -t remove_sub1 origin/remove_sub1 &&
                        replace_gitfile_with_git_dir sub1 &&
+                       rm -rf .git/modules &&
                        $command remove_sub1 &&
                        test_superproject_content origin/remove_sub1 &&
-                       test_git_directory_is_unchanged sub1 &&
-                       test_submodule_content sub1 origin/add_sub1
-               )
-       '
-       # Replacing a submodule with files in a directory must fail as the
-       # submodule work tree isn't removed ...
-       test_expect_failure "$command: replace submodule with a directory must fail" '
-               prolog &&
-               reset_work_tree_to add_sub1 &&
-               (
-                       cd submodule_update &&
-                       git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
-                       test_must_fail $command replace_sub1_with_directory &&
-                       test_superproject_content origin/add_sub1 &&
-                       test_submodule_content sub1 origin/add_sub1
-               )
-       '
-       # ... especially when it contains a .git directory.
-       test_expect_failure "$command: replace submodule containing a .git directory with a directory must fail" '
-               prolog &&
-               reset_work_tree_to add_sub1 &&
-               (
-                       cd submodule_update &&
-                       git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
-                       replace_gitfile_with_git_dir sub1 &&
-                       test_must_fail $command replace_sub1_with_directory &&
-                       test_superproject_content origin/add_sub1 &&
-                       test_git_directory_is_unchanged sub1 &&
-                       test_submodule_content sub1 origin/add_sub1
+                       ! test -e sub1 &&
+                       test_git_directory_exists sub1
                )
        '
-       # Replacing it with a file must fail as it could throw away any local
-       # work tree changes ...
-       test_expect_failure "$command: replace submodule with a file must fail" '
+
+       # Replacing it with a file ...
+       test_expect_success "$command: replace submodule with a file" '
                prolog &&
-               reset_work_tree_to add_sub1 &&
+               reset_work_tree_to_interested add_sub1 &&
                (
                        cd submodule_update &&
                        git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
-                       test_must_fail $command replace_sub1_with_file &&
-                       test_superproject_content origin/add_sub1 &&
-                       test_submodule_content sub1 origin/add_sub1
+                       $command replace_sub1_with_file &&
+                       test_superproject_content origin/replace_sub1_with_file &&
+                       test -f sub1
                )
        '
-       # ... or even destroy unpushed parts of submodule history if that
-       # still uses a .git directory.
-       test_expect_failure "$command: replace submodule containing a .git directory with a file must fail" '
+       RESULTDS=success
+       if test "$KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS" = 1
+       then
+               RESULTDS=failure
+       fi
+       # ... must check its local work tree for untracked files
+       test_expect_$RESULTDS "$command: replace submodule with a file must fail with untracked files" '
                prolog &&
-               reset_work_tree_to add_sub1 &&
+               reset_work_tree_to_interested add_sub1 &&
                (
                        cd submodule_update &&
                        git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
-                       replace_gitfile_with_git_dir sub1 &&
+                       : >sub1/untrackedfile &&
                        test_must_fail $command replace_sub1_with_file &&
                        test_superproject_content origin/add_sub1 &&
-                       test_git_directory_is_unchanged sub1 &&
                        test_submodule_content sub1 origin/add_sub1
+                       test -f sub1/untracked_file
                )
        '
 
        ########################## Modified submodule #########################
-       # Updating a submodule sha1 doesn't update the submodule's work tree
-       test_expect_success "$command: modified submodule does not update submodule work tree" '
+       # Updating a submodule sha1 updates the submodule's work tree
+       test_expect_success "$command: modified submodule updates submodule work tree" '
                prolog &&
-               reset_work_tree_to add_sub1 &&
+               reset_work_tree_to_interested add_sub1 &&
                (
                        cd submodule_update &&
                        git branch -t modify_sub1 origin/modify_sub1 &&
                        $command modify_sub1 &&
                        test_superproject_content origin/modify_sub1 &&
-                       test_submodule_content sub1 origin/add_sub1 &&
-                       git submodule update &&
                        test_submodule_content sub1 origin/modify_sub1
                )
        '
        # Updating a submodule to an invalid sha1 doesn't update the
-       # submodule's work tree, subsequent update will fail
-       test_expect_success "$command: modified submodule does not update submodule work tree to invalid commit" '
+       # superproject nor the submodule's work tree.
+       test_expect_success "$command: updating to a missing submodule commit fails" '
                prolog &&
-               reset_work_tree_to add_sub1 &&
+               reset_work_tree_to_interested add_sub1 &&
                (
                        cd submodule_update &&
                        git branch -t invalid_sub1 origin/invalid_sub1 &&
-                       $command invalid_sub1 &&
-                       test_superproject_content origin/invalid_sub1 &&
-                       test_submodule_content sub1 origin/add_sub1 &&
-                       test_must_fail git submodule update &&
+                       test_must_fail $command invalid_sub1 &&
+                       test_superproject_content origin/add_sub1 &&
                        test_submodule_content sub1 origin/add_sub1
                )
        '
-       # Updating a submodule from an invalid sha1 doesn't update the
-       # submodule's work tree, subsequent update will succeed
-       test_expect_success "$command: modified submodule does not update submodule work tree from invalid commit" '
-               prolog &&
-               reset_work_tree_to invalid_sub1 &&
-               (
-                       cd submodule_update &&
-                       git branch -t valid_sub1 origin/valid_sub1 &&
-                       $command valid_sub1 &&
-                       test_superproject_content origin/valid_sub1 &&
-                       test_dir_is_empty sub1 &&
-                       git submodule update --init --recursive &&
-                       test_submodule_content sub1 origin/valid_sub1
-               )
-       '
 }
 
-# Test that submodule contents are correctly updated when switching
-# between commits that change a submodule.
-# Test that the following transitions are correctly handled:
-# (These tests are also above in the case where we expect no change
-#  in the submodule)
-# - Updated submodule
-# - New submodule
-# - Removed submodule
-# - Directory containing tracked files replaced by submodule
-# - Submodule replaced by tracked files in directory
-# - Submodule replaced by tracked file with the same name
-# - tracked file replaced by submodule
+# Declares and invokes several tests that, in various situations, checks that
+# the provided Git command, when invoked with --recurse-submodules:
+#  - succeeds in updating the worktree and index of a superproject to a target
+#    commit, or fails atomically (depending on the test situation)
+#  - if succeeds, the contents of submodule directories are updated
 #
-# New test cases
-# - Removing a submodule with a git directory absorbs the submodules
-#   git directory first into the superproject.
-
+# Specify the Git command so that "git $GIT_COMMAND --recurse-submodules"
+# works.
+#
+# If the command under test is known to not work with submodules in certain
+# conditions, set the appropriate KNOWN_FAILURE_* variable used in the tests
+# below to 1.
+#
+# Use as follows:
+#
+# test_submodule_switch_recursing_with_args "$GIT_COMMAND"
 test_submodule_switch_recursing_with_args () {
        cmd_args="$1"
        command="git $cmd_args --recurse-submodules"
+       test_submodule_recursing_with_args_common "$command"
+
        RESULTDS=success
        if test "$KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS" = 1
        then
@@ -794,33 +819,8 @@ test_submodule_switch_recursing_with_args () {
        then
                RESULTOI=failure
        fi
-       ######################### Appearing submodule #########################
-       # Switching to a commit letting a submodule appear checks it out ...
-       test_expect_success "$command: added submodule is checked out" '
-               prolog &&
-               reset_work_tree_to_interested no_submodule &&
-               (
-                       cd submodule_update &&
-                       git branch -t add_sub1 origin/add_sub1 &&
-                       $command add_sub1 &&
-                       test_superproject_content origin/add_sub1 &&
-                       test_submodule_content sub1 origin/add_sub1
-               )
-       '
-       # ... ignoring an empty existing directory ...
-       test_expect_success "$command: added submodule is checked out in empty dir" '
-               prolog &&
-               reset_work_tree_to_interested no_submodule &&
-               (
-                       cd submodule_update &&
-                       mkdir sub1 &&
-                       git branch -t add_sub1 origin/add_sub1 &&
-                       $command add_sub1 &&
-                       test_superproject_content origin/add_sub1 &&
-                       test_submodule_content sub1 origin/add_sub1
-               )
-       '
-       # ... unless there is an untracked file in its place.
+       # Switching to a commit letting a submodule appear cannot override an
+       # untracked file.
        test_expect_success "$command: added submodule doesn't remove untracked file with same name" '
                prolog &&
                reset_work_tree_to_interested no_submodule &&
@@ -848,59 +848,7 @@ test_submodule_switch_recursing_with_args () {
                        test_submodule_content sub1 origin/add_sub1
                )
        '
-       # Replacing a tracked file with a submodule produces a checked out submodule
-       test_expect_success "$command: replace tracked file with submodule checks out submodule" '
-               prolog &&
-               reset_work_tree_to_interested replace_sub1_with_file &&
-               (
-                       cd submodule_update &&
-                       git branch -t replace_file_with_sub1 origin/replace_file_with_sub1 &&
-                       $command replace_file_with_sub1 &&
-                       test_superproject_content origin/replace_file_with_sub1 &&
-                       test_submodule_content sub1 origin/replace_file_with_sub1
-               )
-       '
-       # ... as does removing a directory with tracked files with a submodule.
-       test_expect_success "$command: replace directory with submodule" '
-               prolog &&
-               reset_work_tree_to_interested replace_sub1_with_directory &&
-               (
-                       cd submodule_update &&
-                       git branch -t replace_directory_with_sub1 origin/replace_directory_with_sub1 &&
-                       $command replace_directory_with_sub1 &&
-                       test_superproject_content origin/replace_directory_with_sub1 &&
-                       test_submodule_content sub1 origin/replace_directory_with_sub1
-               )
-       '
 
-       ######################## Disappearing submodule #######################
-       # Removing a submodule removes its work tree ...
-       test_expect_success "$command: removed submodule removes submodules working tree" '
-               prolog &&
-               reset_work_tree_to_interested add_sub1 &&
-               (
-                       cd submodule_update &&
-                       git branch -t remove_sub1 origin/remove_sub1 &&
-                       $command remove_sub1 &&
-                       test_superproject_content origin/remove_sub1 &&
-                       ! test -e sub1
-               )
-       '
-       # ... absorbing a .git directory along the way.
-       test_expect_success "$command: removed submodule absorbs submodules .git directory" '
-               prolog &&
-               reset_work_tree_to_interested add_sub1 &&
-               (
-                       cd submodule_update &&
-                       git branch -t remove_sub1 origin/remove_sub1 &&
-                       replace_gitfile_with_git_dir sub1 &&
-                       rm -rf .git/modules &&
-                       $command remove_sub1 &&
-                       test_superproject_content origin/remove_sub1 &&
-                       ! test -e sub1 &&
-                       test_git_directory_exists sub1
-               )
-       '
        # Replacing a submodule with files in a directory must succeeds
        # when the submodule is clean
        test_expect_$RESULTDS "$command: replace submodule with a directory" '
@@ -929,33 +877,6 @@ test_submodule_switch_recursing_with_args () {
                )
        '
 
-       # Replacing it with a file ...
-       test_expect_success "$command: replace submodule with a file" '
-               prolog &&
-               reset_work_tree_to_interested add_sub1 &&
-               (
-                       cd submodule_update &&
-                       git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
-                       $command replace_sub1_with_file &&
-                       test_superproject_content origin/replace_sub1_with_file &&
-                       test -f sub1
-               )
-       '
-
-       # ... must check its local work tree for untracked files
-       test_expect_$RESULTDS "$command: replace submodule with a file must fail with untracked files" '
-               prolog &&
-               reset_work_tree_to_interested add_sub1 &&
-               (
-                       cd submodule_update &&
-                       git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
-                       : >sub1/untrackedfile &&
-                       test_must_fail $command replace_sub1_with_file &&
-                       test_superproject_content origin/add_sub1 &&
-                       test_submodule_content sub1 origin/add_sub1
-               )
-       '
-
        # ... and ignored files are ignored
        test_expect_success "$command: replace submodule with a file works ignores ignored files in submodule" '
                test_when_finished "rm submodule_update/.git/modules/sub1/info/exclude" &&
@@ -971,20 +892,6 @@ test_submodule_switch_recursing_with_args () {
                )
        '
 
-       ########################## Modified submodule #########################
-       # Updating a submodule sha1 updates the submodule's work tree
-       test_expect_success "$command: modified submodule updates submodule work tree" '
-               prolog &&
-               reset_work_tree_to_interested add_sub1 &&
-               (
-                       cd submodule_update &&
-                       git branch -t modify_sub1 origin/modify_sub1 &&
-                       $command modify_sub1 &&
-                       test_superproject_content origin/modify_sub1 &&
-                       test_submodule_content sub1 origin/modify_sub1
-               )
-       '
-
        test_expect_success "git -c submodule.recurse=true $cmd_args: modified submodule updates submodule work tree" '
                prolog &&
                reset_work_tree_to_interested add_sub1 &&
@@ -997,20 +904,6 @@ test_submodule_switch_recursing_with_args () {
                )
        '
 
-       # Updating a submodule to an invalid sha1 doesn't update the
-       # superproject nor the submodule's work tree.
-       test_expect_success "$command: updating to a missing submodule commit fails" '
-               prolog &&
-               reset_work_tree_to_interested add_sub1 &&
-               (
-                       cd submodule_update &&
-                       git branch -t invalid_sub1 origin/invalid_sub1 &&
-                       test_must_fail $command invalid_sub1 &&
-                       test_superproject_content origin/add_sub1 &&
-                       test_submodule_content sub1 origin/add_sub1
-               )
-       '
-
        # recursing deeper than one level doesn't work yet.
        test_expect_success "$command: modified submodule updates submodule recursively" '
                prolog &&
@@ -1026,44 +919,20 @@ test_submodule_switch_recursing_with_args () {
        '
 }
 
-# Test that submodule contents are updated when switching between commits
-# that change a submodule, but throwing away local changes in
-# the superproject as well as the submodule is allowed.
+# Same as test_submodule_switch_recursing_with_args(), except that throwing
+# away local changes in the superproject is allowed.
 test_submodule_forced_switch_recursing_with_args () {
        cmd_args="$1"
        command="git $cmd_args --recurse-submodules"
+       test_submodule_recursing_with_args_common "$command"
+
        RESULT=success
        if test "$KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS" = 1
        then
                RESULT=failure
        fi
-       ######################### Appearing submodule #########################
-       # Switching to a commit letting a submodule appear creates empty dir ...
-       test_expect_success "$command: added submodule is checked out" '
-               prolog &&
-               reset_work_tree_to_interested no_submodule &&
-               (
-                       cd submodule_update &&
-                       git branch -t add_sub1 origin/add_sub1 &&
-                       $command add_sub1 &&
-                       test_superproject_content origin/add_sub1 &&
-                       test_submodule_content sub1 origin/add_sub1
-               )
-       '
-       # ... and doesn't care if it already exists ...
-       test_expect_success "$command: added submodule ignores empty directory" '
-               prolog &&
-               reset_work_tree_to_interested no_submodule &&
-               (
-                       cd submodule_update &&
-                       git branch -t add_sub1 origin/add_sub1 &&
-                       mkdir sub1 &&
-                       $command add_sub1 &&
-                       test_superproject_content origin/add_sub1 &&
-                       test_submodule_content sub1 origin/add_sub1
-               )
-       '
-       # ... not caring about an untracked file either
+       # Switching to a commit letting a submodule appear does not care about
+       # an untracked file.
        test_expect_success "$command: added submodule does remove untracked unignored file with same name when forced" '
                prolog &&
                reset_work_tree_to_interested no_submodule &&
@@ -1076,60 +945,7 @@ test_submodule_forced_switch_recursing_with_args () {
                        test_submodule_content sub1 origin/add_sub1
                )
        '
-       # Replacing a tracked file with a submodule checks out the submodule
-       test_expect_success "$command: replace tracked file with submodule populates the submodule" '
-               prolog &&
-               reset_work_tree_to_interested replace_sub1_with_file &&
-               (
-                       cd submodule_update &&
-                       git branch -t replace_file_with_sub1 origin/replace_file_with_sub1 &&
-                       $command replace_file_with_sub1 &&
-                       test_superproject_content origin/replace_file_with_sub1 &&
-                       test_submodule_content sub1 origin/replace_file_with_sub1
-               )
-       '
-       # ... as does removing a directory with tracked files with a
-       # submodule.
-       test_expect_success "$command: replace directory with submodule" '
-               prolog &&
-               reset_work_tree_to_interested replace_sub1_with_directory &&
-               (
-                       cd submodule_update &&
-                       git branch -t replace_directory_with_sub1 origin/replace_directory_with_sub1 &&
-                       $command replace_directory_with_sub1 &&
-                       test_superproject_content origin/replace_directory_with_sub1 &&
-                       test_submodule_content sub1 origin/replace_directory_with_sub1
-               )
-       '
 
-       ######################## Disappearing submodule #######################
-       # Removing a submodule doesn't remove its work tree ...
-       test_expect_success "$command: removed submodule leaves submodule directory and its contents in place" '
-               prolog &&
-               reset_work_tree_to_interested add_sub1 &&
-               (
-                       cd submodule_update &&
-                       git branch -t remove_sub1 origin/remove_sub1 &&
-                       $command remove_sub1 &&
-                       test_superproject_content origin/remove_sub1 &&
-                       ! test -e sub1
-               )
-       '
-       # ... especially when it contains a .git directory.
-       test_expect_success "$command: removed submodule leaves submodule containing a .git directory alone" '
-               prolog &&
-               reset_work_tree_to_interested add_sub1 &&
-               (
-                       cd submodule_update &&
-                       git branch -t remove_sub1 origin/remove_sub1 &&
-                       replace_gitfile_with_git_dir sub1 &&
-                       rm -rf .git/modules/sub1 &&
-                       $command remove_sub1 &&
-                       test_superproject_content origin/remove_sub1 &&
-                       test_git_directory_exists sub1 &&
-                       ! test -e sub1
-               )
-       '
        # Replacing a submodule with files in a directory ...
        test_expect_success "$command: replace submodule with a directory" '
                prolog &&
@@ -1156,17 +972,6 @@ test_submodule_forced_switch_recursing_with_args () {
                        test_git_directory_exists sub1
                )
        '
-       # Replacing it with a file
-       test_expect_success "$command: replace submodule with a file" '
-               prolog &&
-               reset_work_tree_to_interested add_sub1 &&
-               (
-                       cd submodule_update &&
-                       git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
-                       $command replace_sub1_with_file &&
-                       test_superproject_content origin/replace_sub1_with_file
-               )
-       '
 
        # ... even if the submodule contains ignored files
        test_expect_success "$command: replace submodule with a file ignoring ignored files" '
@@ -1181,46 +986,6 @@ test_submodule_forced_switch_recursing_with_args () {
                )
        '
 
-       # ... but stops for untracked files that would be lost
-       test_expect_$RESULT "$command: replace submodule with a file stops for untracked files" '
-               prolog &&
-               reset_work_tree_to_interested add_sub1 &&
-               (
-                       cd submodule_update &&
-                       git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
-                       : >sub1/untracked_file &&
-                       test_must_fail $command replace_sub1_with_file &&
-                       test_superproject_content origin/add_sub1 &&
-                       test -f sub1/untracked_file
-               )
-       '
-
-       ########################## Modified submodule #########################
-       # Updating a submodule sha1 updates the submodule's work tree
-       test_expect_success "$command: modified submodule updates submodule work tree" '
-               prolog &&
-               reset_work_tree_to_interested add_sub1 &&
-               (
-                       cd submodule_update &&
-                       git branch -t modify_sub1 origin/modify_sub1 &&
-                       $command modify_sub1 &&
-                       test_superproject_content origin/modify_sub1 &&
-                       test_submodule_content sub1 origin/modify_sub1
-               )
-       '
-       # Updating a submodule to an invalid sha1 doesn't update the
-       # submodule's work tree, subsequent update will fail
-       test_expect_success "$command: modified submodule does not update submodule work tree to invalid commit" '
-               prolog &&
-               reset_work_tree_to_interested add_sub1 &&
-               (
-                       cd submodule_update &&
-                       git branch -t invalid_sub1 origin/invalid_sub1 &&
-                       test_must_fail $command invalid_sub1 &&
-                       test_superproject_content origin/add_sub1 &&
-                       test_submodule_content sub1 origin/add_sub1
-               )
-       '
        # Updating a submodule from an invalid sha1 updates
        test_expect_success "$command: modified submodule does update submodule work tree from invalid commit" '
                prolog &&
index 1dbc85b21407dcfc83d9dc6a56e8e0f21c748c0c..e40120848837c1ef78cf75d1724c69226823db15 100755 (executable)
@@ -69,12 +69,17 @@ sub format_times {
        @tests = glob "p????-*.sh";
 }
 
+my $resultsdir = "test-results";
+if ($ENV{GIT_PERF_SUBSECTION} ne "") {
+       $resultsdir .= "/" . $ENV{GIT_PERF_SUBSECTION};
+}
+
 my @subtests;
 my %shorttests;
 for my $t (@tests) {
        $t =~ s{(?:.*/)?(p(\d+)-[^/]+)\.sh$}{$1} or die "bad test name: $t";
        my $n = $2;
-       my $fname = "test-results/$t.subtests";
+       my $fname = "$resultsdir/$t.subtests";
        open my $fp, "<", $fname or die "cannot open $fname: $!";
        for (<$fp>) {
                chomp;
@@ -98,7 +103,7 @@ sub read_descr {
 my %descrs;
 my $descrlen = 4; # "Test"
 for my $t (@subtests) {
-       $descrs{$t} = $shorttests{$t}.": ".read_descr("test-results/$t.descr");
+       $descrs{$t} = $shorttests{$t}.": ".read_descr("$resultsdir/$t.descr");
        $descrlen = length $descrs{$t} if length $descrs{$t}>$descrlen;
 }
 
@@ -138,7 +143,7 @@ sub have_slash {
        my $firstr;
        for my $i (0..$#dirs) {
                my $d = $dirs[$i];
-               $times{$prefixes{$d}.$t} = [get_times("test-results/$prefixes{$d}$t.times")];
+               $times{$prefixes{$d}.$t} = [get_times("$resultsdir/$prefixes{$d}$t.times")];
                my ($r,$u,$s) = @{$times{$prefixes{$d}.$t}};
                my $w = length format_times($r,$u,$s,$firstr);
                $colwidth[$i] = $w if $w > $colwidth[$i];
diff --git a/t/perf/lib-pack.sh b/t/perf/lib-pack.sh
new file mode 100644 (file)
index 0000000..d3865db
--- /dev/null
@@ -0,0 +1,25 @@
+# Helpers for dealing with large numbers of packs.
+
+# create $1 nonsense packs, each with a single blob
+create_packs () {
+       perl -le '
+               my ($n) = @ARGV;
+               for (1..$n) {
+                       print "blob";
+                       print "data <<EOF";
+                       print "$_";
+                       print "EOF";
+                       print "checkpoint"
+               }
+       ' "$@" |
+       git fast-import
+}
+
+# create a large number of packs, disabling any gc which might
+# cause us to repack them
+setup_many_packs () {
+       git config gc.auto 0 &&
+       git config gc.autopacklimit 0 &&
+       git config fastimport.unpacklimit 0 &&
+       create_packs 500
+}
index b7ff68d4fa3de5559af672795191a0b81deaa4f8..e0ed05907c7277def1e9fcda15bd1e1fe98ae6cc 100755 (executable)
@@ -31,4 +31,8 @@ test_perf 'git log -L (renames on)' '
        git log -M -L 1:"$file" >/dev/null
 '
 
+test_perf 'git log --oneline --raw --parents' '
+       git log --oneline --raw --parents >/dev/null
+'
+
 test_done
index a5dc39f86af65d6f6b200b95c4f3c66d5f482ec1..d0e0e019ea364cb1cef3863d256aa5596e01eb56 100755 (executable)
@@ -20,6 +20,7 @@ start to show a noticeable performance problem on my machine, but without
 taking too long to set up and run the tests.
 '
 . ./perf-lib.sh
+. "$TEST_DIRECTORY/perf/lib-pack.sh"
 
 # make a long nonsense history on branch $1, consisting of $2 commits, each
 # with a unique file pointing to the blob at $2.
@@ -44,26 +45,6 @@ create_tags () {
        git update-ref --stdin
 }
 
-# create $1 nonsense packs, each with a single blob
-create_packs () {
-       perl -le '
-               my ($n) = @ARGV;
-               for (1..$n) {
-                       print "blob";
-                       print "data <<EOF";
-                       print "$_";
-                       print "EOF";
-               }
-       ' "$@" |
-       git fast-import &&
-
-       git cat-file --batch-all-objects --batch-check='%(objectname)' |
-       while read sha1
-       do
-               echo $sha1 | git pack-objects .git/objects/pack/pack
-       done
-}
-
 test_expect_success 'create parent and child' '
        git init parent &&
        git -C parent commit --allow-empty -m base &&
@@ -84,9 +65,7 @@ test_expect_success 'populate parent tags' '
 test_expect_success 'create child packs' '
        (
                cd child &&
-               git config gc.auto 0 &&
-               git config gc.autopacklimit 0 &&
-               create_packs 500
+               setup_many_packs
        )
 '
 
diff --git a/t/perf/p5551-fetch-rescan.sh b/t/perf/p5551-fetch-rescan.sh
new file mode 100755 (executable)
index 0000000..b99dc23
--- /dev/null
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+test_description='fetch performance with many packs
+
+It is common for fetch to consider objects that we might not have, and it is an
+easy mistake for the code to use a function like `parse_object` that might
+give the correct _answer_ on such an object, but do so slowly (due to
+re-scanning the pack directory for lookup failures).
+
+The resulting performance drop can be hard to notice in a real repository, but
+becomes quite large in a repository with a large number of packs. So this
+test creates a more pathological case, since any mistakes would produce a more
+noticeable slowdown.
+'
+. ./perf-lib.sh
+. "$TEST_DIRECTORY"/perf/lib-pack.sh
+
+test_expect_success 'create parent and child' '
+       git init parent &&
+       git clone parent child
+'
+
+
+test_expect_success 'create refs in the parent' '
+       (
+               cd parent &&
+               git commit --allow-empty -m foo &&
+               head=$(git rev-parse HEAD) &&
+               test_seq 1000 |
+               sed "s,.*,update refs/heads/& $head," |
+               $MODERN_GIT update-ref --stdin
+       )
+'
+
+test_expect_success 'create many packs in the child' '
+       (
+               cd child &&
+               setup_many_packs
+       )
+'
+
+test_perf 'fetch' '
+       # start at the same state for each iteration
+       obj=$($MODERN_GIT -C parent rev-parse HEAD) &&
+       (
+               cd child &&
+               $MODERN_GIT for-each-ref --format="delete %(refname)" refs/remotes |
+               $MODERN_GIT update-ref --stdin &&
+               rm -vf .git/objects/$(echo $obj | sed "s|^..|&/|") &&
+
+               git fetch
+       )
+'
+
+test_done
diff --git a/t/perf/p7519-fsmonitor.sh b/t/perf/p7519-fsmonitor.sh
new file mode 100755 (executable)
index 0000000..16d1bf7
--- /dev/null
@@ -0,0 +1,184 @@
+#!/bin/sh
+
+test_description="Test core.fsmonitor"
+
+. ./perf-lib.sh
+
+#
+# Performance test for the fsmonitor feature which enables git to talk to a
+# file system change monitor and avoid having to scan the working directory
+# for new or modified files.
+#
+# By default, the performance test will utilize the Watchman file system
+# monitor if it is installed.  If Watchman is not installed, it will use a
+# dummy integration script that does not report any new or modified files.
+# The dummy script has very little overhead which provides optimistic results.
+#
+# The performance test will also use the untracked cache feature if it is
+# available as fsmonitor uses it to speed up scanning for untracked files.
+#
+# There are 3 environment variables that can be used to alter the default
+# behavior of the performance test:
+#
+# GIT_PERF_7519_UNTRACKED_CACHE: used to configure core.untrackedCache
+# GIT_PERF_7519_SPLIT_INDEX: used to configure core.splitIndex
+# GIT_PERF_7519_FSMONITOR: used to configure core.fsMonitor
+#
+# The big win for using fsmonitor is the elimination of the need to scan the
+# working directory looking for changed and untracked files. If the file
+# information is all cached in RAM, the benefits are reduced.
+#
+# GIT_PERF_7519_DROP_CACHE: if set, the OS caches are dropped between tests
+#
+
+test_perf_large_repo
+test_checkout_worktree
+
+test_lazy_prereq UNTRACKED_CACHE '
+       { git update-index --test-untracked-cache; ret=$?; } &&
+       test $ret -ne 1
+'
+
+test_lazy_prereq WATCHMAN '
+       { command -v watchman >/dev/null 2>&1; ret=$?; } &&
+       test $ret -ne 1
+'
+
+if test_have_prereq WATCHMAN
+then
+       # Convert unix style paths to escaped Windows style paths for Watchman
+       case "$(uname -s)" in
+       MSYS_NT*)
+         GIT_WORK_TREE="$(cygpath -aw "$PWD" | sed 's,\\,/,g')"
+         ;;
+       *)
+         GIT_WORK_TREE="$PWD"
+         ;;
+       esac
+fi
+
+if test -n "$GIT_PERF_7519_DROP_CACHE"
+then
+       # When using GIT_PERF_7519_DROP_CACHE, GIT_PERF_REPEAT_COUNT must be 1 to
+       # generate valid results. Otherwise the caching that happens for the nth
+       # run will negate the validity of the comparisons.
+       if test "$GIT_PERF_REPEAT_COUNT" -ne 1
+       then
+               echo "warning: Setting GIT_PERF_REPEAT_COUNT=1" >&2
+               GIT_PERF_REPEAT_COUNT=1
+       fi
+fi
+
+test_expect_success "setup for fsmonitor" '
+       # set untrackedCache depending on the environment
+       if test -n "$GIT_PERF_7519_UNTRACKED_CACHE"
+       then
+               git config core.untrackedCache "$GIT_PERF_7519_UNTRACKED_CACHE"
+       else
+               if test_have_prereq UNTRACKED_CACHE
+               then
+                       git config core.untrackedCache true
+               else
+                       git config core.untrackedCache false
+               fi
+       fi &&
+
+       # set core.splitindex depending on the environment
+       if test -n "$GIT_PERF_7519_SPLIT_INDEX"
+       then
+               git config core.splitIndex "$GIT_PERF_7519_SPLIT_INDEX"
+       fi &&
+
+       # set INTEGRATION_SCRIPT depending on the environment
+       if test -n "$GIT_PERF_7519_FSMONITOR"
+       then
+               INTEGRATION_SCRIPT="$GIT_PERF_7519_FSMONITOR"
+       else
+               #
+               # Choose integration script based on existence of Watchman.
+               # If Watchman exists, watch the work tree and attempt a query.
+               # If everything succeeds, use Watchman integration script,
+               # else fall back to an empty integration script.
+               #
+               mkdir .git/hooks &&
+               if test_have_prereq WATCHMAN
+               then
+                       INTEGRATION_SCRIPT=".git/hooks/fsmonitor-watchman" &&
+                       cp "$TEST_DIRECTORY/../templates/hooks--fsmonitor-watchman.sample" "$INTEGRATION_SCRIPT" &&
+                       watchman watch "$GIT_WORK_TREE" &&
+                       watchman watch-list | grep -q -F "$GIT_WORK_TREE"
+               else
+                       INTEGRATION_SCRIPT=".git/hooks/fsmonitor-empty" &&
+                       write_script "$INTEGRATION_SCRIPT"<<-\EOF
+                       EOF
+               fi
+       fi &&
+
+       git config core.fsmonitor "$INTEGRATION_SCRIPT" &&
+       git update-index --fsmonitor
+'
+
+if test -n "$GIT_PERF_7519_DROP_CACHE"; then
+       test-drop-caches
+fi
+
+test_perf "status (fsmonitor=$INTEGRATION_SCRIPT)" '
+       git status
+'
+
+if test -n "$GIT_PERF_7519_DROP_CACHE"; then
+       test-drop-caches
+fi
+
+test_perf "status -uno (fsmonitor=$INTEGRATION_SCRIPT)" '
+       git status -uno
+'
+
+if test -n "$GIT_PERF_7519_DROP_CACHE"; then
+       test-drop-caches
+fi
+
+test_perf "status -uall (fsmonitor=$INTEGRATION_SCRIPT)" '
+       git status -uall
+'
+
+test_expect_success "setup without fsmonitor" '
+       unset INTEGRATION_SCRIPT &&
+       git config --unset core.fsmonitor &&
+       git update-index --no-fsmonitor
+'
+
+if test -n "$GIT_PERF_7519_DROP_CACHE"; then
+       test-drop-caches
+fi
+
+test_perf "status (fsmonitor=$INTEGRATION_SCRIPT)" '
+       git status
+'
+
+if test -n "$GIT_PERF_7519_DROP_CACHE"; then
+       test-drop-caches
+fi
+
+test_perf "status -uno (fsmonitor=$INTEGRATION_SCRIPT)" '
+       git status -uno
+'
+
+if test -n "$GIT_PERF_7519_DROP_CACHE"; then
+       test-drop-caches
+fi
+
+test_perf "status -uall (fsmonitor=$INTEGRATION_SCRIPT)" '
+       git status -uall
+'
+
+if test_have_prereq WATCHMAN
+then
+       watchman watch-del "$GIT_WORK_TREE" >/dev/null 2>&1 &&
+
+       # Work around Watchman bug on Windows where it holds on to handles
+       # preventing the removal of the trash directory
+       watchman shutdown-server >/dev/null 2>&1
+fi
+
+test_done
index b50211b2591d19904fa3c995b01aea2121db476a..e4c343a6b795b6f990fa278788ad65a0a5cdc4e6 100644 (file)
@@ -56,12 +56,10 @@ MODERN_GIT=$GIT_BUILD_DIR/bin-wrappers/git
 export MODERN_GIT
 
 perf_results_dir=$TEST_OUTPUT_DIRECTORY/test-results
+test -n "$GIT_PERF_SUBSECTION" && perf_results_dir="$perf_results_dir/$GIT_PERF_SUBSECTION"
 mkdir -p "$perf_results_dir"
 rm -f "$perf_results_dir"/$(basename "$0" .sh).subtests
 
-if test -z "$GIT_PERF_REPEAT_COUNT"; then
-       GIT_PERF_REPEAT_COUNT=3
-fi
 die_if_build_dir_not_repo () {
        if ! ( cd "$TEST_DIRECTORY/.." &&
                    git rev-parse --build-dir >/dev/null 2>&1 ); then
index beb4acc0e428d20280a649c428da8b0b3aa5b4c6..43e4de49ef2bea9ae09b502f28fbc0913e294571 100755 (executable)
@@ -2,9 +2,14 @@
 
 case "$1" in
        --help)
-               echo "usage: $0 [other_git_tree...] [--] [test_scripts]"
+               echo "usage: $0 [--config file] [other_git_tree...] [--] [test_scripts]"
                exit 0
                ;;
+       --config)
+               shift
+               GIT_PERF_CONFIG_FILE=$(cd "$(dirname "$1")"; pwd)/$(basename "$1")
+               export GIT_PERF_CONFIG_FILE
+               shift ;;
 esac
 
 die () {
@@ -29,8 +34,10 @@ unpack_git_rev () {
        (cd "$(git rev-parse --show-cdup)" && git archive --format=tar $rev) |
        (cd build/$rev && tar x)
 }
+
 build_git_rev () {
        rev=$1
+       name="$2"
        for config in config.mak config.mak.autogen config.status
        do
                if test -e "../../$config"
@@ -38,7 +45,7 @@ build_git_rev () {
                        cp "../../$config" "build/$rev/"
                fi
        done
-       echo "=== Building $rev ==="
+       echo "=== Building $rev ($name) ==="
        (
                cd build/$rev &&
                if test -n "$GIT_PERF_MAKE_COMMAND"
@@ -65,7 +72,7 @@ run_dirs_helper () {
                if [ ! -d build/$rev ]; then
                        unpack_git_rev $rev
                fi
-               build_git_rev $rev
+               build_git_rev $rev "$mydir"
                mydir=build/$rev
        fi
        if test "$mydir" = .; then
@@ -87,14 +94,78 @@ run_dirs () {
        done
 }
 
-GIT_PERF_AGGREGATING_LATER=t
-export GIT_PERF_AGGREGATING_LATER
+get_subsections () {
+       section="$1"
+       test -z "$GIT_PERF_CONFIG_FILE" && return
+       git config -f "$GIT_PERF_CONFIG_FILE" --name-only --get-regex "$section\..*\.[^.]+" |
+       sed -e "s/$section\.\(.*\)\..*/\1/" | sort | uniq
+}
+
+get_var_from_env_or_config () {
+       env_var="$1"
+       conf_sec="$2"
+       conf_var="$3"
+       # $4 can be set to a default value
+
+       # Do nothing if the env variable is already set
+       eval "test -z \"\${$env_var+x}\"" || return
+
+       test -z "$GIT_PERF_CONFIG_FILE" && return
+
+       # Check if the variable is in the config file
+       if test -n "$GIT_PERF_SUBSECTION"
+       then
+               var="$conf_sec.$GIT_PERF_SUBSECTION.$conf_var"
+               conf_value=$(git config -f "$GIT_PERF_CONFIG_FILE" "$var") &&
+               eval "$env_var=\"$conf_value\"" && return
+       fi
+       var="$conf_sec.$conf_var"
+       conf_value=$(git config -f "$GIT_PERF_CONFIG_FILE" "$var") &&
+       eval "$env_var=\"$conf_value\"" && return
+
+       test -n "${4+x}" && eval "$env_var=\"$4\""
+}
+
+run_subsection () {
+       get_var_from_env_or_config "GIT_PERF_REPEAT_COUNT" "perf" "repeatCount" 3
+       export GIT_PERF_REPEAT_COUNT
+
+       get_var_from_env_or_config "GIT_PERF_DIRS_OR_REVS" "perf" "dirsOrRevs"
+       set -- $GIT_PERF_DIRS_OR_REVS "$@"
+
+       get_var_from_env_or_config "GIT_PERF_MAKE_COMMAND" "perf" "makeCommand"
+       get_var_from_env_or_config "GIT_PERF_MAKE_OPTS" "perf" "makeOpts"
+
+       GIT_PERF_AGGREGATING_LATER=t
+       export GIT_PERF_AGGREGATING_LATER
+
+       if test $# = 0 -o "$1" = -- -o -f "$1"; then
+               set -- . "$@"
+       fi
+
+       run_dirs "$@"
+       ./aggregate.perl "$@"
+}
 
 cd "$(dirname $0)"
 . ../../GIT-BUILD-OPTIONS
 
-if test $# = 0 -o "$1" = -- -o -f "$1"; then
-       set -- . "$@"
+mkdir -p test-results
+get_subsections "perf" >test-results/run_subsections.names
+
+if test $(wc -l <test-results/run_subsections.names) -eq 0
+then
+       (
+               run_subsection "$@"
+       )
+else
+       while read -r subsec
+       do
+               (
+                       GIT_PERF_SUBSECTION="$subsec"
+                       export GIT_PERF_SUBSECTION
+                       echo "======== Run for subsection '$GIT_PERF_SUBSECTION' ========"
+                       run_subsection "$@"
+               )
+       done <test-results/run_subsections.names
 fi
-run_dirs "$@"
-./aggregate.perl "$@"
index 1aa5093f36ea61a75cb11de5a65ed11b2b3b4e65..7fd87dd5444040ad7273be51c29a56dea738a861 100755 (executable)
@@ -20,6 +20,31 @@ modification *should* take notice and update the test vectors here.
 
 . ./test-lib.sh
 
+try_local_x () {
+       local x="local" &&
+       echo "$x"
+}
+
+# This test is an experiment to check whether any Git users are using
+# Shells that don't support the "local" keyword. "local" is not
+# POSIX-standard, but it is very widely supported by POSIX-compliant
+# shells, and if it doesn't cause problems for people, we would like
+# to be able to use it in Git code.
+#
+# For now, this is the only test that requires "local". If your shell
+# fails this test, you can ignore the failure, but please report the
+# problem to the Git mailing list <git@vger.kernel.org>, as it might
+# convince us to continue avoiding the use of "local".
+test_expect_success 'verify that the running shell supports "local"' '
+       x="notlocal" &&
+       echo "local" >expected1 &&
+       try_local_x >actual1 &&
+       test_cmp expected1 actual1 &&
+       echo "notlocal" >expected2 &&
+       echo "$x" >actual2 &&
+       test_cmp expected2 actual2
+'
+
 ################################################################
 # git init has been done in an empty repository.
 # make sure it is empty.
index 86c1a51654fa6c1b049a313fe8d880c6a266ee62..c413bff9cf1f3a79ef494b39844c42d3a8c877f1 100755 (executable)
@@ -453,4 +453,16 @@ test_expect_success 're-init from a linked worktree' '
        )
 '
 
+test_expect_success MINGW 'redirect std handles' '
+       GIT_REDIRECT_STDOUT=output.txt git rev-parse --git-dir &&
+       test .git = "$(cat output.txt)" &&
+       test -z "$(GIT_REDIRECT_STDOUT=off git rev-parse --git-dir)" &&
+       test_must_fail env \
+               GIT_REDIRECT_STDOUT=output.txt \
+               GIT_REDIRECT_STDERR="2>&1" \
+               git rev-parse --git-dir --verify refs/invalid &&
+       printf ".git\nfatal: Needed a single revision\n" >expect &&
+       test_cmp expect output.txt
+'
+
 test_done
index ad685d92f8c7aa138cb29b0ef46e5d09e246f42c..f1678851de954dd93ab0e2fc7912813ad5f09960 100644 (file)
 #     to the "list_available_blobs" response.
 #
 
+use 5.008;
+use lib (split(/:/, $ENV{GITPERLLIB}));
 use strict;
 use warnings;
 use IO::File;
+use Git::Packet;
 
 my $MAX_PACKET_CONTENT_SIZE = 65516;
 my $log_file                = shift @ARGV;
@@ -55,89 +58,30 @@ sub rot13 {
        return $str;
 }
 
-sub packet_bin_read {
-       my $buffer;
-       my $bytes_read = read STDIN, $buffer, 4;
-       if ( $bytes_read == 0 ) {
-               # EOF - Git stopped talking to us!
-               print $debug "STOP\n";
-               exit();
-       }
-       elsif ( $bytes_read != 4 ) {
-               die "invalid packet: '$buffer'";
-       }
-       my $pkt_size = hex($buffer);
-       if ( $pkt_size == 0 ) {
-               return ( 1, "" );
-       }
-       elsif ( $pkt_size > 4 ) {
-               my $content_size = $pkt_size - 4;
-               $bytes_read = read STDIN, $buffer, $content_size;
-               if ( $bytes_read != $content_size ) {
-                       die "invalid packet ($content_size bytes expected; $bytes_read bytes read)";
-               }
-               return ( 0, $buffer );
-       }
-       else {
-               die "invalid packet size: $pkt_size";
-       }
-}
-
-sub packet_txt_read {
-       my ( $res, $buf ) = packet_bin_read();
-       unless ( $buf eq '' or $buf =~ s/\n$// ) {
-               die "A non-binary line MUST be terminated by an LF.";
-       }
-       return ( $res, $buf );
-}
-
-sub packet_bin_write {
-       my $buf = shift;
-       print STDOUT sprintf( "%04x", length($buf) + 4 );
-       print STDOUT $buf;
-       STDOUT->flush();
-}
-
-sub packet_txt_write {
-       packet_bin_write( $_[0] . "\n" );
-}
-
-sub packet_flush {
-       print STDOUT sprintf( "%04x", 0 );
-       STDOUT->flush();
-}
-
 print $debug "START\n";
 $debug->flush();
 
-( packet_txt_read() eq ( 0, "git-filter-client" ) ) || die "bad initialize";
-( packet_txt_read() eq ( 0, "version=2" ) )         || die "bad version";
-( packet_bin_read() eq ( 1, "" ) )                  || die "bad version end";
+packet_initialize("git-filter", 2);
 
-packet_txt_write("git-filter-server");
-packet_txt_write("version=2");
-packet_flush();
+my %remote_caps = packet_read_and_check_capabilities("clean", "smudge", "delay");
+packet_check_and_write_capabilities(\%remote_caps, @capabilities);
 
-( packet_txt_read() eq ( 0, "capability=clean" ) )  || die "bad capability";
-( packet_txt_read() eq ( 0, "capability=smudge" ) ) || die "bad capability";
-( packet_txt_read() eq ( 0, "capability=delay" ) )  || die "bad capability";
-( packet_bin_read() eq ( 1, "" ) )                  || die "bad capability end";
-
-foreach (@capabilities) {
-       packet_txt_write( "capability=" . $_ );
-}
-packet_flush();
 print $debug "init handshake complete\n";
 $debug->flush();
 
 while (1) {
-       my ( $command ) = packet_txt_read() =~ /^command=(.+)$/;
+       my ( $res, $command ) = packet_key_val_read("command");
+       if ( $res == -1 ) {
+               print $debug "STOP\n";
+               exit();
+       }
        print $debug "IN: $command";
        $debug->flush();
 
        if ( $command eq "list_available_blobs" ) {
                # Flush
-               packet_bin_read();
+               packet_compare_lists([1, ""], packet_bin_read()) ||
+                       die "bad list_available_blobs end";
 
                foreach my $pathname ( sort keys %DELAY ) {
                        if ( $DELAY{$pathname}{"requested"} >= 1 ) {
@@ -161,16 +105,14 @@ sub packet_flush {
                $debug->flush();
                packet_txt_write("status=success");
                packet_flush();
-       }
-       else {
-               my ( $pathname ) = packet_txt_read() =~ /^pathname=(.+)$/;
+       } else {
+               my ( $res, $pathname ) = packet_key_val_read("pathname");
+               if ( $res == -1 ) {
+                       die "unexpected EOF while expecting pathname";
+               }
                print $debug " $pathname";
                $debug->flush();
 
-               if ( $pathname eq "" ) {
-                       die "bad pathname '$pathname'";
-               }
-
                # Read until flush
                my ( $done, $buffer ) = packet_txt_read();
                while ( $buffer ne '' ) {
@@ -184,6 +126,9 @@ sub packet_flush {
 
                        ( $done, $buffer ) = packet_txt_read();
                }
+               if ( $done == -1 ) {
+                       die "unexpected EOF after pathname '$pathname'";
+               }
 
                my $input = "";
                {
@@ -194,6 +139,9 @@ sub packet_flush {
                                ( $done, $buffer ) = packet_bin_read();
                                $input .= $buffer;
                        }
+                       if ( $done == -1 ) {
+                               die "unexpected EOF while reading input for '$pathname'";
+                       }                       
                        print $debug " " . length($input) . " [OK] -- ";
                        $debug->flush();
                }
@@ -201,17 +149,13 @@ sub packet_flush {
                my $output;
                if ( exists $DELAY{$pathname} and exists $DELAY{$pathname}{"output"} ) {
                        $output = $DELAY{$pathname}{"output"}
-               }
-               elsif ( $pathname eq "error.r" or $pathname eq "abort.r" ) {
+               } elsif ( $pathname eq "error.r" or $pathname eq "abort.r" ) {
                        $output = "";
-               }
-               elsif ( $command eq "clean" and grep( /^clean$/, @capabilities ) ) {
+               } elsif ( $command eq "clean" and grep( /^clean$/, @capabilities ) ) {
                        $output = rot13($input);
-               }
-               elsif ( $command eq "smudge" and grep( /^smudge$/, @capabilities ) ) {
+               } elsif ( $command eq "smudge" and grep( /^smudge$/, @capabilities ) ) {
                        $output = rot13($input);
-               }
-               else {
+               } else {
                        die "bad command '$command'";
                }
 
@@ -220,25 +164,21 @@ sub packet_flush {
                        $debug->flush();
                        packet_txt_write("status=error");
                        packet_flush();
-               }
-               elsif ( $pathname eq "abort.r" ) {
+               } elsif ( $pathname eq "abort.r" ) {
                        print $debug "[ABORT]\n";
                        $debug->flush();
                        packet_txt_write("status=abort");
                        packet_flush();
-               }
-               elsif ( $command eq "smudge" and
+               } elsif ( $command eq "smudge" and
                        exists $DELAY{$pathname} and
-                       $DELAY{$pathname}{"requested"} == 1
-               ) {
+                       $DELAY{$pathname}{"requested"} == 1 ) {
                        print $debug "[DELAYED]\n";
                        $debug->flush();
                        packet_txt_write("status=delayed");
                        packet_flush();
                        $DELAY{$pathname}{"requested"} = 2;
                        $DELAY{$pathname}{"output"} = $output;
-               }
-               else {
+               } else {
                        packet_txt_write("status=success");
                        packet_flush();
 
@@ -258,8 +198,7 @@ sub packet_flush {
                                print $debug ".";
                                if ( length($output) > $MAX_PACKET_CONTENT_SIZE ) {
                                        $output = substr( $output, $MAX_PACKET_CONTENT_SIZE );
-                               }
-                               else {
+                               } else {
                                        $output = "";
                                }
                        }
diff --git a/t/t0025-crlf-renormalize.sh b/t/t0025-crlf-renormalize.sh
new file mode 100755 (executable)
index 0000000..9d9e02a
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+test_description='CRLF renormalization'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       git config core.autocrlf false &&
+       printf "LINEONE\nLINETWO\nLINETHREE\n" >LF.txt &&
+       printf "LINEONE\r\nLINETWO\r\nLINETHREE\r\n" >CRLF.txt &&
+       printf "LINEONE\r\nLINETWO\nLINETHREE\n" >CRLF_mix_LF.txt &&
+       git add . &&
+       git commit -m initial
+'
+
+test_expect_success 'renormalize CRLF in repo' '
+       echo "*.txt text=auto" >.gitattributes &&
+       git add --renormalize "*.txt" &&
+       cat >expect <<-\EOF &&
+       i/lf w/crlf attr/text=auto CRLF.txt
+       i/lf w/lf attr/text=auto LF.txt
+       i/lf w/mixed attr/text=auto CRLF_mix_LF.txt
+       EOF
+       git ls-files --eol |
+       sed -e "s/      / /g" -e "s/  */ /g" |
+       sort >actual &&
+       test_cmp expect actual
+'
+
+test_done
index deb3ae7813052d01b6dab92586e2c37d313ef8ff..68108d956a3f65c868b08ecb81bae87e9c1f5e67 100755 (executable)
@@ -315,7 +315,7 @@ test_expect_success 'setup master' '
        echo >.gitattributes &&
        git checkout -b master &&
        git add .gitattributes &&
-       git commit -m "add .gitattributes" "" &&
+       git commit -m "add .gitattributes" . &&
        printf "\$Id: 0000000000000000000000000000000000000000 \$\nLINEONE\nLINETWO\nLINETHREE"     >LF &&
        printf "\$Id: 0000000000000000000000000000000000000000 \$\r\nLINEONE\r\nLINETWO\r\nLINETHREE" >CRLF &&
        printf "\$Id: 0000000000000000000000000000000000000000 \$\nLINEONE\r\nLINETWO\nLINETHREE"   >CRLF_mix_LF &&
index 364a537000bbbdd43047bd3f9c52c950a96dcbda..cbeb9bebeea67c3654c279b2c12fed4825840d8f 100755 (executable)
@@ -901,6 +901,36 @@ test_expect_success 'get --path barfs on boolean variable' '
        test_must_fail git config --get --path path.bool
 '
 
+test_expect_success 'get --expiry-date' '
+       rel="3.weeks.5.days.00:00" &&
+       rel_out="$rel ->" &&
+       cat >.git/config <<-\EOF &&
+       [date]
+       valid1 = "3.weeks.5.days 00:00"
+       valid2 = "Fri Jun 4 15:46:55 2010"
+       valid3 = "2017/11/11 11:11:11PM"
+       valid4 = "2017/11/10 09:08:07 PM"
+       valid5 = "never"
+       invalid1 = "abc"
+       EOF
+       cat >expect <<-EOF &&
+       $(test-date timestamp $rel)
+       1275666415
+       1510441871
+       1510348087
+       0
+       EOF
+       {
+               echo "$rel_out $(git config --expiry-date date.valid1)"
+               git config --expiry-date date.valid2 &&
+               git config --expiry-date date.valid3 &&
+               git config --expiry-date date.valid4 &&
+               git config --expiry-date date.valid5
+       } >actual &&
+       test_cmp expect actual &&
+       test_must_fail git config --expiry-date date.invalid1
+'
+
 cat > expect << EOF
 [quote]
        leading = " test"
index 0790edf60de2d0324ea4752087f03db1113d822e..98e4a8613ba8ab4aebe99360e89c21be2ce3473d 100755 (executable)
@@ -144,6 +144,11 @@ test_expect_success "check-ref-format --branch @{-1}" '
        refname2=$(git check-ref-format --branch @{-2}) &&
        test "$refname2" = master'
 
+test_expect_success 'check-ref-format --branch -naster' '
+       test_must_fail git check-ref-format --branch -naster >actual &&
+       test_must_be_empty actual
+'
+
 test_expect_success 'check-ref-format --branch from subdir' '
        mkdir subdir &&
 
@@ -161,6 +166,17 @@ test_expect_success 'check-ref-format --branch from subdir' '
        test "$refname" = "$sha1"
 '
 
+test_expect_success 'check-ref-format --branch @{-1} from non-repo' '
+       nongit test_must_fail git check-ref-format --branch @{-1} >actual &&
+       test_must_be_empty actual
+'
+
+test_expect_success 'check-ref-format --branch master from non-repo' '
+       echo master >expect &&
+       nongit git check-ref-format --branch master >actual &&
+       test_cmp expect actual
+'
+
 valid_ref_normalized() {
        prereq=
        case $1 in
diff --git a/t/t1409-avoid-packing-refs.sh b/t/t1409-avoid-packing-refs.sh
new file mode 100755 (executable)
index 0000000..e5cb8a2
--- /dev/null
@@ -0,0 +1,118 @@
+#!/bin/sh
+
+test_description='avoid rewriting packed-refs unnecessarily'
+
+. ./test-lib.sh
+
+# Add an identifying mark to the packed-refs file header line. This
+# shouldn't upset readers, and it should be omitted if the file is
+# ever rewritten.
+mark_packed_refs () {
+       sed -e "s/^\(#.*\)/\1 t1409 /" <.git/packed-refs >.git/packed-refs.new &&
+       mv .git/packed-refs.new .git/packed-refs
+}
+
+# Verify that the packed-refs file is still marked.
+check_packed_refs_marked () {
+       grep -q '^#.* t1409 ' .git/packed-refs
+}
+
+test_expect_success 'setup' '
+       git commit --allow-empty -m "Commit A" &&
+       A=$(git rev-parse HEAD) &&
+       git commit --allow-empty -m "Commit B" &&
+       B=$(git rev-parse HEAD) &&
+       git commit --allow-empty -m "Commit C" &&
+       C=$(git rev-parse HEAD)
+'
+
+test_expect_success 'do not create packed-refs file gratuitously' '
+       test_must_fail test -f .git/packed-refs &&
+       git update-ref refs/heads/foo $A &&
+       test_must_fail test -f .git/packed-refs &&
+       git update-ref refs/heads/foo $B &&
+       test_must_fail test -f .git/packed-refs &&
+       git update-ref refs/heads/foo $C $B &&
+       test_must_fail test -f .git/packed-refs &&
+       git update-ref -d refs/heads/foo &&
+       test_must_fail test -f .git/packed-refs
+'
+
+test_expect_success 'check that marking the packed-refs file works' '
+       git for-each-ref >expected &&
+       git pack-refs --all &&
+       mark_packed_refs &&
+       check_packed_refs_marked &&
+       git for-each-ref >actual &&
+       test_cmp expected actual &&
+       git pack-refs --all &&
+       test_must_fail check_packed_refs_marked &&
+       git for-each-ref >actual2 &&
+       test_cmp expected actual2
+'
+
+test_expect_success 'leave packed-refs untouched on update of packed' '
+       git update-ref refs/heads/packed-update $A &&
+       git pack-refs --all &&
+       mark_packed_refs &&
+       git update-ref refs/heads/packed-update $B &&
+       check_packed_refs_marked
+'
+
+test_expect_success 'leave packed-refs untouched on checked update of packed' '
+       git update-ref refs/heads/packed-checked-update $A &&
+       git pack-refs --all &&
+       mark_packed_refs &&
+       git update-ref refs/heads/packed-checked-update $B $A &&
+       check_packed_refs_marked
+'
+
+test_expect_success 'leave packed-refs untouched on verify of packed' '
+       git update-ref refs/heads/packed-verify $A &&
+       git pack-refs --all &&
+       mark_packed_refs &&
+       echo "verify refs/heads/packed-verify $A" | git update-ref --stdin &&
+       check_packed_refs_marked
+'
+
+test_expect_success 'touch packed-refs on delete of packed' '
+       git update-ref refs/heads/packed-delete $A &&
+       git pack-refs --all &&
+       mark_packed_refs &&
+       git update-ref -d refs/heads/packed-delete &&
+       test_must_fail check_packed_refs_marked
+'
+
+test_expect_success 'leave packed-refs untouched on update of loose' '
+       git pack-refs --all &&
+       git update-ref refs/heads/loose-update $A &&
+       mark_packed_refs &&
+       git update-ref refs/heads/loose-update $B &&
+       check_packed_refs_marked
+'
+
+test_expect_success 'leave packed-refs untouched on checked update of loose' '
+       git pack-refs --all &&
+       git update-ref refs/heads/loose-checked-update $A &&
+       mark_packed_refs &&
+       git update-ref refs/heads/loose-checked-update $B $A &&
+       check_packed_refs_marked
+'
+
+test_expect_success 'leave packed-refs untouched on verify of loose' '
+       git pack-refs --all &&
+       git update-ref refs/heads/loose-verify $A &&
+       mark_packed_refs &&
+       echo "verify refs/heads/loose-verify $A" | git update-ref --stdin &&
+       check_packed_refs_marked
+'
+
+test_expect_success 'leave packed-refs untouched on delete of loose' '
+       git pack-refs --all &&
+       git update-ref refs/heads/loose-delete $A &&
+       mark_packed_refs &&
+       git update-ref -d refs/heads/loose-delete &&
+       check_packed_refs_marked
+'
+
+test_done
index e88349c8a0483b97ea38013de61d799003fd85b5..c7878a60edfdcf24f3c45367f58e62b0ce6dab71 100755 (executable)
@@ -331,4 +331,47 @@ test_expect_success 'update-ref --stdin -z fails delete with bad ref name' '
        grep "fatal: invalid ref format: ~a" err
 '
 
+test_expect_success 'branch rejects HEAD as a branch name' '
+       test_must_fail git branch HEAD HEAD^ &&
+       test_must_fail git show-ref refs/heads/HEAD
+'
+
+test_expect_success 'checkout -b rejects HEAD as a branch name' '
+       test_must_fail git checkout -B HEAD HEAD^ &&
+       test_must_fail git show-ref refs/heads/HEAD
+'
+
+test_expect_success 'update-ref can operate on refs/heads/HEAD' '
+       git update-ref refs/heads/HEAD HEAD^ &&
+       git show-ref refs/heads/HEAD &&
+       git update-ref -d refs/heads/HEAD &&
+       test_must_fail git show-ref refs/heads/HEAD
+'
+
+test_expect_success 'branch -d can remove refs/heads/HEAD' '
+       git update-ref refs/heads/HEAD HEAD^ &&
+       git branch -d HEAD &&
+       test_must_fail git show-ref refs/heads/HEAD
+'
+
+test_expect_success 'branch -m can rename refs/heads/HEAD' '
+       git update-ref refs/heads/HEAD HEAD^ &&
+       git branch -m HEAD tail &&
+       test_must_fail git show-ref refs/heads/HEAD &&
+       git show-ref refs/heads/tail
+'
+
+test_expect_success 'branch -d can remove refs/heads/-dash' '
+       git update-ref refs/heads/-dash HEAD^ &&
+       git branch -d -- -dash &&
+       test_must_fail git show-ref refs/heads/-dash
+'
+
+test_expect_success 'branch -m can rename refs/heads/-dash' '
+       git update-ref refs/heads/-dash HEAD^ &&
+       git branch -m -- -dash dash &&
+       test_must_fail git show-ref refs/heads/-dash &&
+       git show-ref refs/heads/dash
+'
+
 test_done
index 22f69a410ba8194bbdff70471f5b3e746af116da..af9b847761f3c25ddbda8d1e1e978d1a323fa561 100755 (executable)
@@ -6,6 +6,7 @@ test_description='split index mode tests'
 
 # We need total control of index splitting here
 sane_unset GIT_TEST_SPLIT_INDEX
+sane_unset GIT_FSMONITOR_TEST
 
 test_expect_success 'enable split index' '
        git config splitIndex.maxPercentChange 100 &&
index baef2d692431169d83e0bc3aed11d9030ea4a24a..9c1bf6eb3d46f31c0e1816508dcc8fee4699f823 100755 (executable)
@@ -176,7 +176,7 @@ git rev-parse refs/notes/z > pre_merge_z
 test_expect_success 'merge z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
        git update-ref refs/notes/m refs/notes/y &&
        git config core.notesRef refs/notes/m &&
-       test_must_fail git notes merge z >output &&
+       test_must_fail git notes merge z >output 2>&1 &&
        # Output should point to where to resolve conflicts
        test_i18ngrep "\\.git/NOTES_MERGE_WORKTREE" output &&
        # Inspect merge conflicts
@@ -379,7 +379,7 @@ git rev-parse refs/notes/z > pre_merge_z
 test_expect_success 'redo merge of z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
        git update-ref refs/notes/m refs/notes/y &&
        git config core.notesRef refs/notes/m &&
-       test_must_fail git notes merge z >output &&
+       test_must_fail git notes merge z >output 2>&1 &&
        # Output should point to where to resolve conflicts
        test_i18ngrep "\\.git/NOTES_MERGE_WORKTREE" output &&
        # Inspect merge conflicts
@@ -413,7 +413,7 @@ git rev-parse refs/notes/y > pre_merge_y
 git rev-parse refs/notes/z > pre_merge_z
 
 test_expect_success 'redo merge of z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
-       test_must_fail git notes merge z >output &&
+       test_must_fail git notes merge z >output 2>&1 &&
        # Output should point to where to resolve conflicts
        test_i18ngrep "\\.git/NOTES_MERGE_WORKTREE" output &&
        # Inspect merge conflicts
@@ -494,7 +494,7 @@ cp expect_log_y expect_log_m
 
 test_expect_success 'redo merge of z into m (== y) with default ("manual") resolver => Conflicting 3-way merge' '
        git update-ref refs/notes/m refs/notes/y &&
-       test_must_fail git notes merge z >output &&
+       test_must_fail git notes merge z >output 2>&1 &&
        # Output should point to where to resolve conflicts
        test_i18ngrep "\\.git/NOTES_MERGE_WORKTREE" output &&
        # Inspect merge conflicts
index b9c3bc2487aa7dd4bb009d45f784e047f772f62d..10bfc8b94710caf8d33a3a1fa66ad3a4ba394c0f 100755 (executable)
@@ -61,7 +61,7 @@ test_expect_success 'merge z into x while mid-merge on y succeeds' '
        (
                cd worktree2 &&
                git config core.notesRef refs/notes/x &&
-               test_must_fail git notes merge z 2>&1 >out &&
+               test_must_fail git notes merge z >out 2>&1 &&
                test_i18ngrep "Automatic notes merge failed" out &&
                grep -v "A notes merge into refs/notes/x is already in-progress in" out
        ) &&
index f5fd15e55994bafc0f0a2c91b0d00455df13d18d..8ac58d5ea5e4b8b75deaa74f3d6bca29f37dbcb6 100755 (executable)
@@ -255,4 +255,26 @@ test_expect_success 'rebase commit with an ancient timestamp' '
        grep "author .* 34567 +0600$" actual
 '
 
+test_expect_success 'rebase with "From " line in commit message' '
+       git checkout -b preserve-from master~1 &&
+       cat >From_.msg <<EOF &&
+Somebody embedded an mbox in a commit message
+
+This is from so-and-so:
+
+From a@b Mon Sep 17 00:00:00 2001
+From: John Doe <nobody@example.com>
+Date: Sat, 11 Nov 2017 00:00:00 +0000
+Subject: not this message
+
+something
+EOF
+       >From_ &&
+       git add From_ &&
+       git commit -F From_.msg &&
+       git rebase master &&
+       git log -1 --pretty=format:%B >out &&
+       test_cmp From_.msg out
+'
+
 test_done
index 3704dbb2ecf6046e09adb03b451e4e531509b2f4..6a82d1ed876dd5d1073dc63be8ba5720adbf12e3 100755 (executable)
@@ -108,6 +108,17 @@ test_expect_success 'rebase -i with the exec command runs from tree root' '
        rm -fr subdir
 '
 
+test_expect_success 'rebase -i with exec allows git commands in subdirs' '
+       test_when_finished "rm -rf subdir" &&
+       test_when_finished "git rebase --abort ||:" &&
+       git checkout master &&
+       mkdir subdir && (cd subdir &&
+       set_fake_editor &&
+       FAKE_LINES="1 exec_cd_subdir_&&_git_rev-parse_--is-inside-work-tree" \
+               git rebase -i HEAD^
+       )
+'
+
 test_expect_success 'rebase -i with the exec command checks tree cleanness' '
        git checkout master &&
        set_fake_editor &&
index ebf4f5e4b2c1c1cc20a49888df14cedd14b49a8b..a2bba04ba96cb5e16dfbecf3f0d6180e150a7ec0 100755 (executable)
@@ -40,4 +40,21 @@ git_rebase_interactive () {
 
 test_submodule_switch "git_rebase_interactive"
 
+test_expect_success 'rebase interactive ignores modified submodules' '
+       test_when_finished "rm -rf super sub" &&
+       git init sub &&
+       git -C sub commit --allow-empty -m "Initial commit" &&
+       git init super &&
+       git -C super submodule add ../sub &&
+       git -C super config submodule.sub.ignore dirty &&
+       >super/foo &&
+       git -C super add foo &&
+       git -C super commit -m "Initial commit" &&
+       test_commit -C super a &&
+       test_commit -C super b &&
+       test_commit -C super/sub c &&
+       set_fake_editor &&
+       git -C super rebase -i HEAD^^
+'
+
 test_done
index 6863b7bb6fd94cbbd3fcc8f29ab02e344cac23c9..ce48c4fcca80b183927292cc1e5902cfe286f994 100755 (executable)
@@ -10,4 +10,40 @@ KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
 KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
 test_submodule_switch "git cherry-pick"
 
+test_expect_success 'unrelated submodule/file conflict is ignored' '
+       test_create_repo sub &&
+
+       touch sub/file &&
+       git -C sub add file &&
+       git -C sub commit -m "add a file in a submodule" &&
+
+       test_create_repo a_repo &&
+       (
+               cd a_repo &&
+               >a_file &&
+               git add a_file &&
+               git commit -m "add a file" &&
+
+               git branch test &&
+               git checkout test &&
+
+               mkdir sub &&
+               >sub/content &&
+               git add sub/content &&
+               git commit -m "add a regular folder with name sub" &&
+
+               echo "123" >a_file &&
+               git add a_file &&
+               git commit -m "modify a file" &&
+
+               git checkout master &&
+
+               git submodule add ../sub sub &&
+               git submodule update sub &&
+               git commit -m "add a submodule info folder with name sub" &&
+
+               git cherry-pick test
+       )
+'
+
 test_done
index f8568f8841d34d17f3d8fdae4d8d2404a6693c4d..46f15169f55c03742717f36f8d0e99241391b2ea 100755 (executable)
@@ -688,7 +688,7 @@ test_expect_success 'checking out a commit after submodule removal needs manual
        git submodule update &&
        git checkout -q HEAD^ &&
        git checkout -q master 2>actual &&
-       test_i18ngrep "^warning: unable to rmdir submod:" actual &&
+       test_i18ngrep "^warning: unable to rmdir '\''submod'\'':" actual &&
        git status -s submod >actual &&
        echo "?? submod/" >expected &&
        test_cmp expected actual &&
@@ -858,9 +858,8 @@ test_expect_success 'rm files with two different errors' '
        test_i18ncmp expect actual
 '
 
-test_expect_success 'rm empty string should invoke warning' '
-       git rm -rf "" 2>output &&
-       test_i18ngrep "warning: empty strings" output
+test_expect_success 'rm empty string should fail' '
+       test_must_fail git rm -rf ""
 '
 
 test_done
index 0aae21d6984bf0bd5c7c7de425a021da6122beee..2748805642201d7c514792bab8d8b3940fb4086c 100755 (executable)
@@ -331,9 +331,8 @@ test_expect_success 'git add --dry-run --ignore-missing of non-existing file out
        test_i18ncmp expect.err actual.err
 '
 
-test_expect_success 'git add empty string should invoke warning' '
-       git add "" 2>output &&
-       test_i18ngrep "warning: empty strings" output
+test_expect_success 'git add empty string should fail' '
+       test_must_fail git add ""
 '
 
 test_expect_success 'git add --chmod=[+-]x stages correctly' '
index 3b1ac1971af1d972b77b3dfe8ae4a56e00e1fa55..39c7f2ebd740db097d67b12a68fa35f3fa884fc8 100755 (executable)
@@ -804,6 +804,99 @@ test_expect_success 'push -m shows right message' '
        test_cmp expect actual
 '
 
+test_expect_success 'push -m also works without space' '
+       >foo &&
+       git add foo &&
+       git stash push -m"unspaced test message" &&
+       echo "stash@{0}: On master: unspaced test message" >expect &&
+       git stash list -1 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'store -m foo shows right message' '
+       git stash clear &&
+       git reset --hard &&
+       echo quux >bazzy &&
+       git add bazzy &&
+       STASH_ID=$(git stash create) &&
+       git stash store -m "store m" $STASH_ID &&
+       echo "stash@{0}: store m" >expect &&
+       git stash list -1 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'store -mfoo shows right message' '
+       git stash clear &&
+       git reset --hard &&
+       echo quux >bazzy &&
+       git add bazzy &&
+       STASH_ID=$(git stash create) &&
+       git stash store -m"store mfoo" $STASH_ID &&
+       echo "stash@{0}: store mfoo" >expect &&
+       git stash list -1 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'store --message=foo shows right message' '
+       git stash clear &&
+       git reset --hard &&
+       echo quux >bazzy &&
+       git add bazzy &&
+       STASH_ID=$(git stash create) &&
+       git stash store --message="store message=foo" $STASH_ID &&
+       echo "stash@{0}: store message=foo" >expect &&
+       git stash list -1 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'store --message foo shows right message' '
+       git stash clear &&
+       git reset --hard &&
+       echo quux >bazzy &&
+       git add bazzy &&
+       STASH_ID=$(git stash create) &&
+       git stash store --message "store message foo" $STASH_ID &&
+       echo "stash@{0}: store message foo" >expect &&
+       git stash list -1 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'push -mfoo uses right message' '
+       >foo &&
+       git add foo &&
+       git stash push -m"test mfoo" &&
+       echo "stash@{0}: On master: test mfoo" >expect &&
+       git stash list -1 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'push --message foo is synonym for -mfoo' '
+       >foo &&
+       git add foo &&
+       git stash push --message "test message foo" &&
+       echo "stash@{0}: On master: test message foo" >expect &&
+       git stash list -1 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'push --message=foo is synonym for -mfoo' '
+       >foo &&
+       git add foo &&
+       git stash push --message="test message=foo" &&
+       echo "stash@{0}: On master: test message=foo" >expect &&
+       git stash list -1 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'push -m shows right message' '
+       >foo &&
+       git add foo &&
+       git stash push -m "test m foo" &&
+       echo "stash@{0}: On master: test m foo" >expect &&
+       git stash list -1 >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'create stores correct message' '
        >foo &&
        git add foo &&
index 87083f728fed1be9d93d971419ac533ee5314d4f..559a7541a83eb8bd8b465aac154cf67f8783b13d 100755 (executable)
@@ -106,6 +106,8 @@ test_expect_success 'another test, without options' '
        git diff -w -b --ignore-space-at-eol >out &&
        test_cmp expect out &&
 
+       git diff -w --ignore-cr-at-eol >out &&
+       test_cmp expect out &&
 
        tr "Q_" "\015 " <<-\EOF >expect &&
        diff --git a/x b/x
@@ -128,6 +130,9 @@ test_expect_success 'another test, without options' '
        git diff -b --ignore-space-at-eol >out &&
        test_cmp expect out &&
 
+       git diff -b --ignore-cr-at-eol >out &&
+       test_cmp expect out &&
+
        tr "Q_" "\015 " <<-\EOF >expect &&
        diff --git a/x b/x
        index d99af23..22d9f73 100644
@@ -145,6 +150,29 @@ test_expect_success 'another test, without options' '
         CR at end
        EOF
        git diff --ignore-space-at-eol >out &&
+       test_cmp expect out &&
+
+       git diff --ignore-space-at-eol --ignore-cr-at-eol >out &&
+       test_cmp expect out &&
+
+       tr "Q_" "\015 " <<-\EOF >expect &&
+       diff --git a/x b/x
+       index_d99af23..22d9f73 100644
+       --- a/x
+       +++ b/x
+       @@ -1,6 +1,6 @@
+       -whitespace at beginning
+       -whitespace change
+       -whitespace in the middle
+       -whitespace at end
+       +_      whitespace at beginning
+       +whitespace_    _change
+       +white space in the middle
+       +whitespace at end__
+        unchanged line
+        CR at end
+       EOF
+       git diff --ignore-cr-at-eol >out &&
        test_cmp expect out
 '
 
@@ -1318,30 +1346,38 @@ test_expect_success 'no effect from --color-moved with --word-diff' '
        test_cmp expect actual
 '
 
-test_expect_success 'move detection ignoring whitespace ' '
+test_expect_success 'set up whitespace tests' '
        git reset --hard &&
-       cat <<\EOF >lines.txt &&
-line 1
-line 2
-line 3
-line 4
-long line 5
-long line 6
-long line 7
-EOF
-       git add lines.txt &&
-       git commit -m "add poetry" &&
-       cat <<\EOF >lines.txt &&
-       long line 5
+       # Note that these lines have no leading or trailing whitespace.
+       cat <<-\EOF >lines.txt &&
+       line 1
+       line 2
+       line 3
+       line 4
+       line 5
        long line 6
        long line 7
-line 1
-line 2
-line 3
-line 4
-EOF
-       test_config color.diff.oldMoved "magenta" &&
-       test_config color.diff.newMoved "cyan" &&
+       long line 8
+       long line 9
+       EOF
+       git add lines.txt &&
+       git commit -m "add poetry" &&
+       git config color.diff.oldMoved "magenta" &&
+       git config color.diff.newMoved "cyan"
+'
+
+test_expect_success 'move detection ignoring whitespace ' '
+       q_to_tab <<-\EOF >lines.txt &&
+       Qlong line 6
+       Qlong line 7
+       Qlong line 8
+       Qchanged long line 9
+       line 1
+       line 2
+       line 3
+       line 4
+       line 5
+       EOF
        git diff HEAD --no-renames --color-moved --color |
                grep -v "index" |
                test_decode_color >actual &&
@@ -1349,17 +1385,20 @@ EOF
        <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
        <BOLD>--- a/lines.txt<RESET>
        <BOLD>+++ b/lines.txt<RESET>
-       <CYAN>@@ -1,7 +1,7 @@<RESET>
-       <GREEN>+<RESET> <GREEN>long line 5<RESET>
+       <CYAN>@@ -1,9 +1,9 @@<RESET>
        <GREEN>+<RESET> <GREEN>long line 6<RESET>
        <GREEN>+<RESET> <GREEN>long line 7<RESET>
+       <GREEN>+<RESET> <GREEN>long line 8<RESET>
+       <GREEN>+<RESET> <GREEN>changed long line 9<RESET>
         line 1<RESET>
         line 2<RESET>
         line 3<RESET>
         line 4<RESET>
-       <RED>-long line 5<RESET>
+        line 5<RESET>
        <RED>-long line 6<RESET>
        <RED>-long line 7<RESET>
+       <RED>-long line 8<RESET>
+       <RED>-long line 9<RESET>
        EOF
        test_cmp expected actual &&
 
@@ -1370,21 +1409,160 @@ EOF
        <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
        <BOLD>--- a/lines.txt<RESET>
        <BOLD>+++ b/lines.txt<RESET>
-       <CYAN>@@ -1,7 +1,7 @@<RESET>
-       <CYAN>+<RESET>  <CYAN>long line 5<RESET>
+       <CYAN>@@ -1,9 +1,9 @@<RESET>
        <CYAN>+<RESET>  <CYAN>long line 6<RESET>
        <CYAN>+<RESET>  <CYAN>long line 7<RESET>
+       <CYAN>+<RESET>  <CYAN>long line 8<RESET>
+       <GREEN>+<RESET> <GREEN>changed long line 9<RESET>
+        line 1<RESET>
+        line 2<RESET>
+        line 3<RESET>
+        line 4<RESET>
+        line 5<RESET>
+       <MAGENTA>-long line 6<RESET>
+       <MAGENTA>-long line 7<RESET>
+       <MAGENTA>-long line 8<RESET>
+       <RED>-long line 9<RESET>
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'move detection ignoring whitespace changes' '
+       git reset --hard &&
+       # Lines 6-8 have a space change, but 9 is new whitespace
+       q_to_tab <<-\EOF >lines.txt &&
+       longQline 6
+       longQline 7
+       longQline 8
+       long liQne 9
+       line 1
+       line 2
+       line 3
+       line 4
+       line 5
+       EOF
+
+       git diff HEAD --no-renames --color-moved --color |
+               grep -v "index" |
+               test_decode_color >actual &&
+       cat <<-\EOF >expected &&
+       <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+       <BOLD>--- a/lines.txt<RESET>
+       <BOLD>+++ b/lines.txt<RESET>
+       <CYAN>@@ -1,9 +1,9 @@<RESET>
+       <GREEN>+<RESET><GREEN>long      line 6<RESET>
+       <GREEN>+<RESET><GREEN>long      line 7<RESET>
+       <GREEN>+<RESET><GREEN>long      line 8<RESET>
+       <GREEN>+<RESET><GREEN>long li   ne 9<RESET>
         line 1<RESET>
         line 2<RESET>
         line 3<RESET>
         line 4<RESET>
-       <MAGENTA>-long line 5<RESET>
+        line 5<RESET>
+       <RED>-long line 6<RESET>
+       <RED>-long line 7<RESET>
+       <RED>-long line 8<RESET>
+       <RED>-long line 9<RESET>
+       EOF
+       test_cmp expected actual &&
+
+       git diff HEAD --no-renames -b --color-moved --color |
+               grep -v "index" |
+               test_decode_color >actual &&
+       cat <<-\EOF >expected &&
+       <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+       <BOLD>--- a/lines.txt<RESET>
+       <BOLD>+++ b/lines.txt<RESET>
+       <CYAN>@@ -1,9 +1,9 @@<RESET>
+       <CYAN>+<RESET><CYAN>long        line 6<RESET>
+       <CYAN>+<RESET><CYAN>long        line 7<RESET>
+       <CYAN>+<RESET><CYAN>long        line 8<RESET>
+       <GREEN>+<RESET><GREEN>long li   ne 9<RESET>
+        line 1<RESET>
+        line 2<RESET>
+        line 3<RESET>
+        line 4<RESET>
+        line 5<RESET>
        <MAGENTA>-long line 6<RESET>
        <MAGENTA>-long line 7<RESET>
+       <MAGENTA>-long line 8<RESET>
+       <RED>-long line 9<RESET>
        EOF
        test_cmp expected actual
 '
 
+test_expect_success 'move detection ignoring whitespace at eol' '
+       git reset --hard &&
+       # Lines 6-9 have new eol whitespace, but 9 also has it in the middle
+       q_to_tab <<-\EOF >lines.txt &&
+       long line 6Q
+       long line 7Q
+       long line 8Q
+       longQline 9Q
+       line 1
+       line 2
+       line 3
+       line 4
+       line 5
+       EOF
+
+       # avoid cluttering the output with complaints about our eol whitespace
+       test_config core.whitespace -blank-at-eol &&
+
+       git diff HEAD --no-renames --color-moved --color |
+               grep -v "index" |
+               test_decode_color >actual &&
+       cat <<-\EOF >expected &&
+       <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+       <BOLD>--- a/lines.txt<RESET>
+       <BOLD>+++ b/lines.txt<RESET>
+       <CYAN>@@ -1,9 +1,9 @@<RESET>
+       <GREEN>+<RESET><GREEN>long line 6       <RESET>
+       <GREEN>+<RESET><GREEN>long line 7       <RESET>
+       <GREEN>+<RESET><GREEN>long line 8       <RESET>
+       <GREEN>+<RESET><GREEN>long      line 9  <RESET>
+        line 1<RESET>
+        line 2<RESET>
+        line 3<RESET>
+        line 4<RESET>
+        line 5<RESET>
+       <RED>-long line 6<RESET>
+       <RED>-long line 7<RESET>
+       <RED>-long line 8<RESET>
+       <RED>-long line 9<RESET>
+       EOF
+       test_cmp expected actual &&
+
+       git diff HEAD --no-renames --ignore-space-at-eol --color-moved --color |
+               grep -v "index" |
+               test_decode_color >actual &&
+       cat <<-\EOF >expected &&
+       <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+       <BOLD>--- a/lines.txt<RESET>
+       <BOLD>+++ b/lines.txt<RESET>
+       <CYAN>@@ -1,9 +1,9 @@<RESET>
+       <CYAN>+<RESET><CYAN>long line 6 <RESET>
+       <CYAN>+<RESET><CYAN>long line 7 <RESET>
+       <CYAN>+<RESET><CYAN>long line 8 <RESET>
+       <GREEN>+<RESET><GREEN>long      line 9  <RESET>
+        line 1<RESET>
+        line 2<RESET>
+        line 3<RESET>
+        line 4<RESET>
+        line 5<RESET>
+       <MAGENTA>-long line 6<RESET>
+       <MAGENTA>-long line 7<RESET>
+       <MAGENTA>-long line 8<RESET>
+       <RED>-long line 9<RESET>
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'clean up whitespace-test colors' '
+       git config --unset color.diff.oldMoved &&
+       git config --unset color.diff.newMoved
+'
+
 test_expect_success '--color-moved block at end of diff output respects MIN_ALNUM_COUNT' '
        git reset --hard &&
        >bar &&
@@ -1530,13 +1708,4 @@ test_expect_success 'move detection with submodules' '
        test_cmp expect decoded_actual
 '
 
-test_expect_success 'move detection with whitespace changes' '
-       test_when_finished "git reset --hard" &&
-       test_seq 10 >test &&
-       git add test &&
-       sed s/3/42/ <test >test.tmp &&
-       mv test.tmp test &&
-       git -c diff.colormoved diff --ignore-space-change -- test
-'
-
 test_done
index 3e6b485ecba1a59e3d69edc3ac5a6f0f1d327bd3..2d76a971c43f6850a1f443eab3e91bf3762fe952 100755 (executable)
@@ -85,6 +85,10 @@ test_expect_success 'setup' '
 
 check_diff changed_hello 'changed function'
 
+test_expect_success ' context includes comment' '
+       grep "^ .*Hello comment" changed_hello.diff
+'
+
 test_expect_success ' context includes begin' '
        grep "^ .*Begin of hello" changed_hello.diff
 '
index 63b1a1e4efbb4c5dee315ba56a6ad0658da0a9a5..73e767e1783d8ab97d7a45297a173b1bed9f00a2 100644 (file)
@@ -1,4 +1,7 @@
 
+/*
+ * Hello comment.
+ */
 static void hello(void)        // Begin of hello
 {
        /*
index 9e29b5262d3f2e8514d5742951e28a3a339218dc..ac72eeaf27a9b925d801052fb3d051189c0a832a 100755 (executable)
@@ -178,4 +178,18 @@ test_expect_success 'patch5 fails (--no-ignore-whitespace)' '
        test_must_fail git apply --no-ignore-whitespace patch5.patch
 '
 
+test_expect_success 'apply --ignore-space-change --inaccurate-eof' '
+       echo 1 >file &&
+       git apply --ignore-space-change --inaccurate-eof <<-\EOF &&
+       diff --git a/file b/file
+       --- a/file
+       +++ b/file
+       @@ -1 +1 @@
+       -1
+       +2
+       EOF
+       printf 2 >expect &&
+       test_cmp expect file
+'
+
 test_done
index 9df054bf05b8cd40011c0577aa0ce0f30cb8c23f..da10478f59da1a301edf7def229d37fbc964dce9 100755 (executable)
@@ -9,6 +9,7 @@ test_description='git shortlog
 . ./test-lib.sh
 
 test_expect_success 'setup' '
+       test_tick &&
        echo 1 >a1 &&
        git add a1 &&
        tree=$(git write-tree) &&
@@ -59,7 +60,7 @@ fuzz() {
        file=$1 &&
        sed "
                        s/$_x40/OBJECT_NAME/g
-                       s/$_x05/OBJID/g
+                       s/$_x35/OBJID/g
                        s/^ \{6\}[CTa].*/      SUBJECT/g
                        s/^ \{8\}[^ ].*/        CONTINUATION/g
                " <"$file" >"$file.fuzzy" &&
@@ -81,7 +82,7 @@ test_expect_success 'pretty format' '
 
 test_expect_success '--abbrev' '
        sed s/SUBJECT/OBJID/ expect.template >expect &&
-       git shortlog --format="%h" --abbrev=5 HEAD >log &&
+       git shortlog --format="%h" --abbrev=35 HEAD >log &&
        fuzz log >log.predictable &&
        test_cmp expect log.predictable
 '
index 8f155da7a50a657db9fd092e24639f67cd03cd24..25b1f8cc73bc35bedaaffeacbb75a1329d725c3b 100755 (executable)
@@ -737,6 +737,107 @@ test_expect_success 'log.decorate configuration' '
 
 '
 
+test_expect_success 'decorate-refs with glob' '
+       cat >expect.decorate <<-\EOF &&
+       Merge-tag-reach
+       Merge-tags-octopus-a-and-octopus-b
+       seventh
+       octopus-b (octopus-b)
+       octopus-a (octopus-a)
+       reach
+       EOF
+       git log -n6 --decorate=short --pretty="tformat:%f%d" \
+               --decorate-refs="heads/octopus*" >actual &&
+       test_cmp expect.decorate actual
+'
+
+test_expect_success 'decorate-refs without globs' '
+       cat >expect.decorate <<-\EOF &&
+       Merge-tag-reach
+       Merge-tags-octopus-a-and-octopus-b
+       seventh
+       octopus-b
+       octopus-a
+       reach (tag: reach)
+       EOF
+       git log -n6 --decorate=short --pretty="tformat:%f%d" \
+               --decorate-refs="tags/reach" >actual &&
+       test_cmp expect.decorate actual
+'
+
+test_expect_success 'multiple decorate-refs' '
+       cat >expect.decorate <<-\EOF &&
+       Merge-tag-reach
+       Merge-tags-octopus-a-and-octopus-b
+       seventh
+       octopus-b (octopus-b)
+       octopus-a (octopus-a)
+       reach (tag: reach)
+       EOF
+       git log -n6 --decorate=short --pretty="tformat:%f%d" \
+               --decorate-refs="heads/octopus*" \
+               --decorate-refs="tags/reach" >actual &&
+    test_cmp expect.decorate actual
+'
+
+test_expect_success 'decorate-refs-exclude with glob' '
+       cat >expect.decorate <<-\EOF &&
+       Merge-tag-reach (HEAD -> master)
+       Merge-tags-octopus-a-and-octopus-b
+       seventh (tag: seventh)
+       octopus-b (tag: octopus-b)
+       octopus-a (tag: octopus-a)
+       reach (tag: reach, reach)
+       EOF
+       git log -n6 --decorate=short --pretty="tformat:%f%d" \
+               --decorate-refs-exclude="heads/octopus*" >actual &&
+       test_cmp expect.decorate actual
+'
+
+test_expect_success 'decorate-refs-exclude without globs' '
+       cat >expect.decorate <<-\EOF &&
+       Merge-tag-reach (HEAD -> master)
+       Merge-tags-octopus-a-and-octopus-b
+       seventh (tag: seventh)
+       octopus-b (tag: octopus-b, octopus-b)
+       octopus-a (tag: octopus-a, octopus-a)
+       reach (reach)
+       EOF
+       git log -n6 --decorate=short --pretty="tformat:%f%d" \
+               --decorate-refs-exclude="tags/reach" >actual &&
+       test_cmp expect.decorate actual
+'
+
+test_expect_success 'multiple decorate-refs-exclude' '
+       cat >expect.decorate <<-\EOF &&
+       Merge-tag-reach (HEAD -> master)
+       Merge-tags-octopus-a-and-octopus-b
+       seventh (tag: seventh)
+       octopus-b (tag: octopus-b)
+       octopus-a (tag: octopus-a)
+       reach (reach)
+       EOF
+       git log -n6 --decorate=short --pretty="tformat:%f%d" \
+               --decorate-refs-exclude="heads/octopus*" \
+               --decorate-refs-exclude="tags/reach" >actual &&
+       test_cmp expect.decorate actual
+'
+
+test_expect_success 'decorate-refs and decorate-refs-exclude' '
+       cat >expect.decorate <<-\EOF &&
+       Merge-tag-reach (master)
+       Merge-tags-octopus-a-and-octopus-b
+       seventh
+       octopus-b
+       octopus-a
+       reach (reach)
+       EOF
+       git log -n6 --decorate=short --pretty="tformat:%f%d" \
+               --decorate-refs="heads/*" \
+               --decorate-refs-exclude="heads/oc*" >actual &&
+       test_cmp expect.decorate actual
+'
+
 test_expect_success 'log.decorate config parsing' '
        git log --oneline --decorate=full >expect.full &&
        git log --oneline --decorate=short >expect.short &&
index ded8f98dbed9764744799f7b4bed0c51fc57a158..c19d8dbc9d916463d51dc0903185cb50a6cb5af7 100755 (executable)
@@ -165,4 +165,49 @@ test_expect_success 'git pull --allow-unrelated-histories' '
        )
 '
 
+test_expect_success 'git pull does not add a sign-off line' '
+       test_when_finished "rm -fr src dst actual" &&
+       git init src &&
+       test_commit -C src one &&
+       git clone src dst &&
+       test_commit -C src two &&
+       git -C dst pull --no-ff &&
+       git -C dst show -s --pretty="format:%(trailers)" HEAD >actual &&
+       test_must_be_empty actual
+'
+
+test_expect_success 'git pull --no-signoff does not add sign-off line' '
+       test_when_finished "rm -fr src dst actual" &&
+       git init src &&
+       test_commit -C src one &&
+       git clone src dst &&
+       test_commit -C src two &&
+       git -C dst pull --no-signoff --no-ff &&
+       git -C dst show -s --pretty="format:%(trailers)" HEAD >actual &&
+       test_must_be_empty actual
+'
+
+test_expect_success 'git pull --signoff add a sign-off line' '
+       test_when_finished "rm -fr src dst expected actual" &&
+       echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >expected &&
+       git init src &&
+       test_commit -C src one &&
+       git clone src dst &&
+       test_commit -C src two &&
+       git -C dst pull --signoff --no-ff &&
+       git -C dst show -s --pretty="format:%(trailers)" HEAD >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'git pull --no-signoff flag cancels --signoff flag' '
+       test_when_finished "rm -fr src dst actual" &&
+       git init src &&
+       test_commit -C src one &&
+       git clone src dst &&
+       test_commit -C src two &&
+       git -C dst pull --signoff --no-signoff --no-ff &&
+       git -C dst show -s --pretty="format:%(trailers)" HEAD >actual &&
+       test_must_be_empty actual
+'
+
 test_done
index 42251f7f3af0865a31fd38722fbf7433d81fe3f0..a552ad4ead899fbb431c68bef558811d25cd8c63 100755 (executable)
@@ -478,7 +478,47 @@ test_expect_success "don't fetch submodule when newly recorded commits are alrea
                git fetch >../actual.out 2>../actual.err
        ) &&
        ! test -s actual.out &&
-       test_i18ncmp expect.err actual.err
+       test_i18ncmp expect.err actual.err &&
+       (
+               cd submodule &&
+               git checkout -q master
+       )
+'
+
+test_expect_success "'fetch.recurseSubmodules=on-demand' works also without .gitmodule entry" '
+       (
+               cd downstream &&
+               git fetch --recurse-submodules
+       ) &&
+       add_upstream_commit &&
+       head1=$(git rev-parse --short HEAD) &&
+       git add submodule &&
+       git rm .gitmodules &&
+       git commit -m "new submodule without .gitmodules" &&
+       printf "" >expect.out &&
+       head2=$(git rev-parse --short HEAD) &&
+       echo "From $pwd/." >expect.err.2 &&
+       echo "   $head1..$head2  master     -> origin/master" >>expect.err.2 &&
+       head -3 expect.err >>expect.err.2 &&
+       (
+               cd downstream &&
+               rm .gitmodules &&
+               git config fetch.recurseSubmodules on-demand &&
+               # fake submodule configuration to avoid skipping submodule handling
+               git config -f .gitmodules submodule.fake.path fake &&
+               git config -f .gitmodules submodule.fake.url fakeurl &&
+               git add .gitmodules &&
+               git config --unset submodule.submodule.url &&
+               git fetch >../actual.out 2>../actual.err &&
+               # cleanup
+               git config --unset fetch.recurseSubmodules &&
+               git reset --hard
+       ) &&
+       test_i18ncmp expect.out actual.out &&
+       test_i18ncmp expect.err.2 actual.err &&
+       git checkout HEAD^ -- .gitmodules &&
+       git add .gitmodules &&
+       git commit -m "new submodule restored .gitmodules"
 '
 
 test_expect_success 'fetching submodules respects parallel settings' '
@@ -530,4 +570,39 @@ test_expect_success 'fetching submodule into a broken repository' '
        test_must_fail git -C dst fetch --recurse-submodules
 '
 
+test_expect_success "fetch new commits when submodule got renamed" '
+       git clone . downstream_rename &&
+       (
+               cd downstream_rename &&
+               git submodule update --init &&
+# NEEDSWORK: we omitted --recursive for the submodule update here since
+# that does not work. See test 7001 for mv "moving nested submodules"
+# for details. Once that is fixed we should add the --recursive option
+# here.
+               git checkout -b rename &&
+               git mv submodule submodule_renamed &&
+               (
+                       cd submodule_renamed &&
+                       git checkout -b rename_sub &&
+                       echo a >a &&
+                       git add a &&
+                       git commit -ma &&
+                       git push origin rename_sub &&
+                       git rev-parse HEAD >../../expect
+               ) &&
+               git add submodule_renamed &&
+               git commit -m "update renamed submodule" &&
+               git push origin rename
+       ) &&
+       (
+               cd downstream &&
+               git fetch --recurse-submodules=on-demand &&
+               (
+                       cd submodule &&
+                       git rev-parse origin/rename_sub >../../actual
+               )
+       ) &&
+       test_cmp expect actual
+'
+
 test_done
index 90a4b0d2fe9d8a1d9963bea54d2d1198a24003c2..463783789c8ccda6a483197f6626afbfca7d8ccf 100755 (executable)
@@ -140,6 +140,83 @@ test_expect_success 'push options and submodules' '
        test_cmp expect parent_upstream/.git/hooks/post-receive.push_options
 '
 
+test_expect_success 'default push option' '
+       mk_repo_pair &&
+       git -C upstream config receive.advertisePushOptions true &&
+       (
+               cd workbench &&
+               test_commit one &&
+               git push --mirror up &&
+               test_commit two &&
+               git -c push.pushOption=default push up master
+       ) &&
+       test_refs master master &&
+       echo "default" >expect &&
+       test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
+       test_cmp expect upstream/.git/hooks/post-receive.push_options
+'
+
+test_expect_success 'two default push options' '
+       mk_repo_pair &&
+       git -C upstream config receive.advertisePushOptions true &&
+       (
+               cd workbench &&
+               test_commit one &&
+               git push --mirror up &&
+               test_commit two &&
+               git -c push.pushOption=default1 -c push.pushOption=default2 push up master
+       ) &&
+       test_refs master master &&
+       printf "default1\ndefault2\n" >expect &&
+       test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
+       test_cmp expect upstream/.git/hooks/post-receive.push_options
+'
+
+test_expect_success 'push option from command line overrides from-config push option' '
+       mk_repo_pair &&
+       git -C upstream config receive.advertisePushOptions true &&
+       (
+               cd workbench &&
+               test_commit one &&
+               git push --mirror up &&
+               test_commit two &&
+               git -c push.pushOption=default push --push-option=manual up master
+       ) &&
+       test_refs master master &&
+       echo "manual" >expect &&
+       test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
+       test_cmp expect upstream/.git/hooks/post-receive.push_options
+'
+
+test_expect_success 'empty value of push.pushOption in config clears the list' '
+       mk_repo_pair &&
+       git -C upstream config receive.advertisePushOptions true &&
+       (
+               cd workbench &&
+               test_commit one &&
+               git push --mirror up &&
+               test_commit two &&
+               git -c push.pushOption=default1 -c push.pushOption= -c push.pushOption=default2 push up master
+       ) &&
+       test_refs master master &&
+       echo "default2" >expect &&
+       test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
+       test_cmp expect upstream/.git/hooks/post-receive.push_options
+'
+
+test_expect_success 'invalid push option in config' '
+       mk_repo_pair &&
+       git -C upstream config receive.advertisePushOptions true &&
+       (
+               cd workbench &&
+               test_commit one &&
+               git push --mirror up &&
+               test_commit two &&
+               test_must_fail git -c push.pushOption push up master
+       ) &&
+       test_refs master HEAD@{1}
+'
+
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
 
index b322c2f72202fbfda5c74f8d085744dd29c55fb3..ba548df4a918243ee1b2bcedab525e6ec7eff7d6 100755 (executable)
@@ -3,12 +3,18 @@
 test_description='various Windows-only path tests'
 . ./test-lib.sh
 
-if ! test_have_prereq MINGW; then
+if test_have_prereq CYGWIN
+then
+       alias winpwd='cygpath -aw .'
+elif test_have_prereq MINGW
+then
+       alias winpwd=pwd
+else
        skip_all='skipping Windows-only path tests'
        test_done
 fi
 
-UNCPATH="$(pwd)"
+UNCPATH="$(winpwd)"
 case "$UNCPATH" in
 [A-Z]:*)
        # Use administrative share e.g. \\localhost\C$\git-sdk-64\usr\src\git
@@ -45,8 +51,8 @@ test_expect_success push '
        test "$rev" = "$(git rev-parse --verify refs/heads/to-push)"
 '
 
-test_expect_success 'remote nick cannot contain backslashes' '
-       BACKSLASHED="$(pwd | tr / \\\\)" &&
+test_expect_success MINGW 'remote nick cannot contain backslashes' '
+       BACKSLASHED="$(winpwd | tr / \\\\)" &&
        git ls-remote "$BACKSLASHED" >out 2>err &&
        test_i18ngrep ! "unable to access" err
 '
index 9c56f771b619e4bb931ce3d34a5f9325b8f61e90..0f895478f02bcf4cc4408ce065e79e0314b66793 100755 (executable)
@@ -306,21 +306,21 @@ test_expect_success 'clone checking out a tag' '
        test_cmp fetch.expected fetch.actual
 '
 
-setup_ssh_wrapper () {
-       test_expect_success 'setup ssh wrapper' '
-               cp "$GIT_BUILD_DIR/t/helper/test-fake-ssh$X" \
-                       "$TRASH_DIRECTORY/ssh-wrapper$X" &&
-               GIT_SSH="$TRASH_DIRECTORY/ssh-wrapper$X" &&
-               export GIT_SSH &&
-               export TRASH_DIRECTORY &&
-               >"$TRASH_DIRECTORY"/ssh-output
-       '
-}
+test_expect_success 'set up ssh wrapper' '
+       cp "$GIT_BUILD_DIR/t/helper/test-fake-ssh$X" \
+               "$TRASH_DIRECTORY/ssh$X" &&
+       GIT_SSH="$TRASH_DIRECTORY/ssh$X" &&
+       export GIT_SSH &&
+       export TRASH_DIRECTORY &&
+       >"$TRASH_DIRECTORY"/ssh-output
+'
 
 copy_ssh_wrapper_as () {
-       cp "$TRASH_DIRECTORY/ssh-wrapper$X" "${1%$X}$X" &&
+       rm -f "${1%$X}$X" &&
+       cp "$TRASH_DIRECTORY/ssh$X" "${1%$X}$X" &&
+       test_when_finished "rm $(git rev-parse --sq-quote "${1%$X}$X")" &&
        GIT_SSH="${1%$X}$X" &&
-       export GIT_SSH
+       test_when_finished "GIT_SSH=\"\$TRASH_DIRECTORY/ssh\$X\""
 }
 
 expect_ssh () {
@@ -344,8 +344,6 @@ expect_ssh () {
        (cd "$TRASH_DIRECTORY" && test_cmp ssh-expect ssh-output)
 }
 
-setup_ssh_wrapper
-
 test_expect_success 'clone myhost:src uses ssh' '
        git clone myhost:src ssh-clone &&
        expect_ssh myhost src
@@ -362,9 +360,52 @@ test_expect_success 'bracketed hostnames are still ssh' '
        expect_ssh "-p 123" myhost src
 '
 
-test_expect_success 'uplink is not treated as putty' '
+test_expect_success 'OpenSSH variant passes -4' '
+       git clone -4 "[myhost:123]:src" ssh-ipv4-clone &&
+       expect_ssh "-4 -p 123" myhost src
+'
+
+test_expect_success 'variant can be overridden' '
+       copy_ssh_wrapper_as "$TRASH_DIRECTORY/putty" &&
+       git -c ssh.variant=putty clone -4 "[myhost:123]:src" ssh-putty-clone &&
+       expect_ssh "-4 -P 123" myhost src
+'
+
+test_expect_success 'variant=auto picks based on basename' '
+       copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
+       git -c ssh.variant=auto clone -4 "[myhost:123]:src" ssh-auto-clone &&
+       expect_ssh "-4 -P 123" myhost src
+'
+
+test_expect_success 'simple does not support -4/-6' '
+       copy_ssh_wrapper_as "$TRASH_DIRECTORY/simple" &&
+       test_must_fail git clone -4 "myhost:src" ssh-4-clone-simple
+'
+
+test_expect_success 'simple does not support port' '
+       copy_ssh_wrapper_as "$TRASH_DIRECTORY/simple" &&
+       test_must_fail git clone "[myhost:123]:src" ssh-bracket-clone-simple
+'
+
+test_expect_success 'uplink is treated as simple' '
        copy_ssh_wrapper_as "$TRASH_DIRECTORY/uplink" &&
-       git clone "[myhost:123]:src" ssh-bracket-clone-uplink &&
+       test_must_fail git clone "[myhost:123]:src" ssh-bracket-clone-uplink &&
+       git clone "myhost:src" ssh-clone-uplink &&
+       expect_ssh myhost src
+'
+
+test_expect_success 'OpenSSH-like uplink is treated as ssh' '
+       write_script "$TRASH_DIRECTORY/uplink" <<-EOF &&
+       if test "\$1" = "-G"
+       then
+               exit 0
+       fi &&
+       exec "\$TRASH_DIRECTORY/ssh$X" "\$@"
+       EOF
+       test_when_finished "rm -f \"\$TRASH_DIRECTORY/uplink\"" &&
+       GIT_SSH="$TRASH_DIRECTORY/uplink" &&
+       test_when_finished "GIT_SSH=\"\$TRASH_DIRECTORY/ssh\$X\"" &&
+       git clone "[myhost:123]:src" ssh-bracket-clone-sshlike-uplink &&
        expect_ssh "-p 123" myhost src
 '
 
@@ -416,12 +457,14 @@ test_expect_success 'ssh.variant overrides plink detection' '
 '
 
 test_expect_success 'GIT_SSH_VARIANT overrides plink detection to plink' '
+       copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
        GIT_SSH_VARIANT=plink \
        git clone "[myhost:123]:src" ssh-bracket-clone-variant-3 &&
        expect_ssh "-P 123" myhost src
 '
 
 test_expect_success 'GIT_SSH_VARIANT overrides plink to tortoiseplink' '
+       copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
        GIT_SSH_VARIANT=tortoiseplink \
        git clone "[myhost:123]:src" ssh-bracket-clone-variant-4 &&
        expect_ssh "-batch -P 123" myhost src
@@ -433,9 +476,6 @@ test_expect_success 'clean failure on broken quoting' '
                git clone "[myhost:123]:src" sq-failure
 '
 
-# Reset the GIT_SSH environment variable for clone tests.
-setup_ssh_wrapper
-
 counter=0
 # $1 url
 # $2 none|host
index d5af7581297d6167b82d029a015e367c057ff7af..13b5e5eb9b9fa2aa96e0496eb9fa3e7049fdfb51 100755 (executable)
@@ -11,7 +11,9 @@ test_expect_success 'setup ssh wrapper' '
        git upload-pack "$TRASH_DIRECTORY"
        EOF
        GIT_SSH="$TRASH_DIRECTORY/ssh-wrapper" &&
+       GIT_SSH_VARIANT=ssh &&
        export GIT_SSH &&
+       export GIT_SSH_VARIANT &&
        export TRASH_DIRECTORY
 '
 
diff --git a/t/t5700-protocol-v1.sh b/t/t5700-protocol-v1.sh
new file mode 100755 (executable)
index 0000000..ba86a44
--- /dev/null
@@ -0,0 +1,294 @@
+#!/bin/sh
+
+test_description='test git wire-protocol transition'
+
+TEST_NO_CREATE_REPO=1
+
+. ./test-lib.sh
+
+# Test protocol v1 with 'git://' transport
+#
+. "$TEST_DIRECTORY"/lib-git-daemon.sh
+start_git_daemon --export-all --enable=receive-pack
+daemon_parent=$GIT_DAEMON_DOCUMENT_ROOT_PATH/parent
+
+test_expect_success 'create repo to be served by git-daemon' '
+       git init "$daemon_parent" &&
+       test_commit -C "$daemon_parent" one
+'
+
+test_expect_success 'clone with git:// using protocol v1' '
+       GIT_TRACE_PACKET=1 git -c protocol.version=1 \
+               clone "$GIT_DAEMON_URL/parent" daemon_child 2>log &&
+
+       git -C daemon_child log -1 --format=%s >actual &&
+       git -C "$daemon_parent" log -1 --format=%s >expect &&
+       test_cmp expect actual &&
+
+       # Client requested to use protocol v1
+       grep "clone> .*\\\0\\\0version=1\\\0$" log &&
+       # Server responded using protocol v1
+       grep "clone< version 1" log
+'
+
+test_expect_success 'fetch with git:// using protocol v1' '
+       test_commit -C "$daemon_parent" two &&
+
+       GIT_TRACE_PACKET=1 git -C daemon_child -c protocol.version=1 \
+               fetch 2>log &&
+
+       git -C daemon_child log -1 --format=%s origin/master >actual &&
+       git -C "$daemon_parent" log -1 --format=%s >expect &&
+       test_cmp expect actual &&
+
+       # Client requested to use protocol v1
+       grep "fetch> .*\\\0\\\0version=1\\\0$" log &&
+       # Server responded using protocol v1
+       grep "fetch< version 1" log
+'
+
+test_expect_success 'pull with git:// using protocol v1' '
+       GIT_TRACE_PACKET=1 git -C daemon_child -c protocol.version=1 \
+               pull 2>log &&
+
+       git -C daemon_child log -1 --format=%s >actual &&
+       git -C "$daemon_parent" log -1 --format=%s >expect &&
+       test_cmp expect actual &&
+
+       # Client requested to use protocol v1
+       grep "fetch> .*\\\0\\\0version=1\\\0$" log &&
+       # Server responded using protocol v1
+       grep "fetch< version 1" log
+'
+
+test_expect_success 'push with git:// using protocol v1' '
+       test_commit -C daemon_child three &&
+
+       # Push to another branch, as the target repository has the
+       # master branch checked out and we cannot push into it.
+       GIT_TRACE_PACKET=1 git -C daemon_child -c protocol.version=1 \
+               push origin HEAD:client_branch 2>log &&
+
+       git -C daemon_child log -1 --format=%s >actual &&
+       git -C "$daemon_parent" log -1 --format=%s client_branch >expect &&
+       test_cmp expect actual &&
+
+       # Client requested to use protocol v1
+       grep "push> .*\\\0\\\0version=1\\\0$" log &&
+       # Server responded using protocol v1
+       grep "push< version 1" log
+'
+
+stop_git_daemon
+
+# Test protocol v1 with 'file://' transport
+#
+test_expect_success 'create repo to be served by file:// transport' '
+       git init file_parent &&
+       test_commit -C file_parent one
+'
+
+test_expect_success 'clone with file:// using protocol v1' '
+       GIT_TRACE_PACKET=1 git -c protocol.version=1 \
+               clone "file://$(pwd)/file_parent" file_child 2>log &&
+
+       git -C file_child log -1 --format=%s >actual &&
+       git -C file_parent log -1 --format=%s >expect &&
+       test_cmp expect actual &&
+
+       # Server responded using protocol v1
+       grep "clone< version 1" log
+'
+
+test_expect_success 'fetch with file:// using protocol v1' '
+       test_commit -C file_parent two &&
+
+       GIT_TRACE_PACKET=1 git -C file_child -c protocol.version=1 \
+               fetch 2>log &&
+
+       git -C file_child log -1 --format=%s origin/master >actual &&
+       git -C file_parent log -1 --format=%s >expect &&
+       test_cmp expect actual &&
+
+       # Server responded using protocol v1
+       grep "fetch< version 1" log
+'
+
+test_expect_success 'pull with file:// using protocol v1' '
+       GIT_TRACE_PACKET=1 git -C file_child -c protocol.version=1 \
+               pull 2>log &&
+
+       git -C file_child log -1 --format=%s >actual &&
+       git -C file_parent log -1 --format=%s >expect &&
+       test_cmp expect actual &&
+
+       # Server responded using protocol v1
+       grep "fetch< version 1" log
+'
+
+test_expect_success 'push with file:// using protocol v1' '
+       test_commit -C file_child three &&
+
+       # Push to another branch, as the target repository has the
+       # master branch checked out and we cannot push into it.
+       GIT_TRACE_PACKET=1 git -C file_child -c protocol.version=1 \
+               push origin HEAD:client_branch 2>log &&
+
+       git -C file_child log -1 --format=%s >actual &&
+       git -C file_parent log -1 --format=%s client_branch >expect &&
+       test_cmp expect actual &&
+
+       # Server responded using protocol v1
+       grep "push< version 1" log
+'
+
+# Test protocol v1 with 'ssh://' transport
+#
+test_expect_success 'setup ssh wrapper' '
+       GIT_SSH="$GIT_BUILD_DIR/t/helper/test-fake-ssh" &&
+       export GIT_SSH &&
+       GIT_SSH_VARIANT=ssh &&
+       export GIT_SSH_VARIANT &&
+       export TRASH_DIRECTORY &&
+       >"$TRASH_DIRECTORY"/ssh-output
+'
+
+expect_ssh () {
+       test_when_finished '(cd "$TRASH_DIRECTORY" && rm -f ssh-expect && >ssh-output)' &&
+       echo "ssh: -o SendEnv=GIT_PROTOCOL myhost $1 '$PWD/ssh_parent'" >"$TRASH_DIRECTORY/ssh-expect" &&
+       (cd "$TRASH_DIRECTORY" && test_cmp ssh-expect ssh-output)
+}
+
+test_expect_success 'create repo to be served by ssh:// transport' '
+       git init ssh_parent &&
+       test_commit -C ssh_parent one
+'
+
+test_expect_success 'clone with ssh:// using protocol v1' '
+       GIT_TRACE_PACKET=1 git -c protocol.version=1 \
+               clone "ssh://myhost:$(pwd)/ssh_parent" ssh_child 2>log &&
+       expect_ssh git-upload-pack &&
+
+       git -C ssh_child log -1 --format=%s >actual &&
+       git -C ssh_parent log -1 --format=%s >expect &&
+       test_cmp expect actual &&
+
+       # Server responded using protocol v1
+       grep "clone< version 1" log
+'
+
+test_expect_success 'fetch with ssh:// using protocol v1' '
+       test_commit -C ssh_parent two &&
+
+       GIT_TRACE_PACKET=1 git -C ssh_child -c protocol.version=1 \
+               fetch 2>log &&
+       expect_ssh git-upload-pack &&
+
+       git -C ssh_child log -1 --format=%s origin/master >actual &&
+       git -C ssh_parent log -1 --format=%s >expect &&
+       test_cmp expect actual &&
+
+       # Server responded using protocol v1
+       grep "fetch< version 1" log
+'
+
+test_expect_success 'pull with ssh:// using protocol v1' '
+       GIT_TRACE_PACKET=1 git -C ssh_child -c protocol.version=1 \
+               pull 2>log &&
+       expect_ssh git-upload-pack &&
+
+       git -C ssh_child log -1 --format=%s >actual &&
+       git -C ssh_parent log -1 --format=%s >expect &&
+       test_cmp expect actual &&
+
+       # Server responded using protocol v1
+       grep "fetch< version 1" log
+'
+
+test_expect_success 'push with ssh:// using protocol v1' '
+       test_commit -C ssh_child three &&
+
+       # Push to another branch, as the target repository has the
+       # master branch checked out and we cannot push into it.
+       GIT_TRACE_PACKET=1 git -C ssh_child -c protocol.version=1 \
+               push origin HEAD:client_branch 2>log &&
+       expect_ssh git-receive-pack &&
+
+       git -C ssh_child log -1 --format=%s >actual &&
+       git -C ssh_parent log -1 --format=%s client_branch >expect &&
+       test_cmp expect actual &&
+
+       # Server responded using protocol v1
+       grep "push< version 1" log
+'
+
+# Test protocol v1 with 'http://' transport
+#
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'create repo to be served by http:// transport' '
+       git init "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" config http.receivepack true &&
+       test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" one
+'
+
+test_expect_success 'clone with http:// using protocol v1' '
+       GIT_TRACE_PACKET=1 GIT_TRACE_CURL=1 git -c protocol.version=1 \
+               clone "$HTTPD_URL/smart/http_parent" http_child 2>log &&
+
+       git -C http_child log -1 --format=%s >actual &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" log -1 --format=%s >expect &&
+       test_cmp expect actual &&
+
+       # Client requested to use protocol v1
+       grep "Git-Protocol: version=1" log &&
+       # Server responded using protocol v1
+       grep "git< version 1" log
+'
+
+test_expect_success 'fetch with http:// using protocol v1' '
+       test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" two &&
+
+       GIT_TRACE_PACKET=1 git -C http_child -c protocol.version=1 \
+               fetch 2>log &&
+
+       git -C http_child log -1 --format=%s origin/master >actual &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" log -1 --format=%s >expect &&
+       test_cmp expect actual &&
+
+       # Server responded using protocol v1
+       grep "git< version 1" log
+'
+
+test_expect_success 'pull with http:// using protocol v1' '
+       GIT_TRACE_PACKET=1 git -C http_child -c protocol.version=1 \
+               pull 2>log &&
+
+       git -C http_child log -1 --format=%s >actual &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" log -1 --format=%s >expect &&
+       test_cmp expect actual &&
+
+       # Server responded using protocol v1
+       grep "git< version 1" log
+'
+
+test_expect_success 'push with http:// using protocol v1' '
+       test_commit -C http_child three &&
+
+       # Push to another branch, as the target repository has the
+       # master branch checked out and we cannot push into it.
+       GIT_TRACE_PACKET=1 git -C http_child -c protocol.version=1 \
+               push origin HEAD:client_branch && #2>log &&
+
+       git -C http_child log -1 --format=%s >actual &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" log -1 --format=%s client_branch >expect &&
+       test_cmp expect actual &&
+
+       # Server responded using protocol v1
+       grep "git< version 1" log
+'
+
+stop_httpd
+
+test_done
index 8c2c6eaef83fe90d1faa088fe65ee4a851b1e471..f84ff941c3624be821fd87a67922ad1d2ca00b34 100755 (executable)
@@ -894,4 +894,21 @@ test_expect_success 'bisect start takes options and revs in any order' '
        test_cmp expected actual
 '
 
+test_expect_success 'git bisect reset cleans bisection state properly' '
+       git bisect reset &&
+       git bisect start &&
+       git bisect good $HASH1 &&
+       git bisect bad $HASH4 &&
+       git bisect reset &&
+       test -z "$(git for-each-ref "refs/bisect/*")" &&
+       test_path_is_missing "$GIT_DIR/BISECT_EXPECTED_REV" &&
+       test_path_is_missing "$GIT_DIR/BISECT_ANCESTORS_OK" &&
+       test_path_is_missing "$GIT_DIR/BISECT_LOG" &&
+       test_path_is_missing "$GIT_DIR/BISECT_RUN" &&
+       test_path_is_missing "$GIT_DIR/BISECT_TERMS" &&
+       test_path_is_missing "$GIT_DIR/head-name" &&
+       test_path_is_missing "$GIT_DIR/BISECT_HEAD" &&
+       test_path_is_missing "$GIT_DIR/BISECT_START"
+'
+
 test_done
index 3aa534933e08f85ea1c215e5ef6cc6c7f4a7cd31..c128dfc5790790de9edf1b4d2cfa8b028c1036bc 100755 (executable)
@@ -766,4 +766,36 @@ test_expect_success 'Verify usage of %(symref:rstrip) atom' '
        test_cmp expected actual
 '
 
+test_expect_success ':remotename and :remoteref' '
+       git init remote-tests &&
+       (
+               cd remote-tests &&
+               test_commit initial &&
+               git remote add from fifth.coffee:blub &&
+               git config branch.master.remote from &&
+               git config branch.master.merge refs/heads/stable &&
+               git remote add to southridge.audio:repo &&
+               git config remote.to.push "refs/heads/*:refs/heads/pushed/*" &&
+               git config branch.master.pushRemote to &&
+               for pair in "%(upstream)=refs/remotes/from/stable" \
+                       "%(upstream:remotename)=from" \
+                       "%(upstream:remoteref)=refs/heads/stable" \
+                       "%(push)=refs/remotes/to/pushed/master" \
+                       "%(push:remotename)=to" \
+                       "%(push:remoteref)=refs/heads/pushed/master"
+               do
+                       echo "${pair#*=}" >expect &&
+                       git for-each-ref --format="${pair%=*}" \
+                               refs/heads/master >actual &&
+                       test_cmp expect actual
+               done &&
+               git branch push-simple &&
+               git config branch.push-simple.pushRemote from &&
+               actual="$(git for-each-ref \
+                       --format="%(push:remotename),%(push:remoteref)" \
+                       refs/heads/push-simple)" &&
+               test from, = "$actual"
+       )
+'
+
 test_done
index f5929c46f3ea7ed6317a77086d6263ac25a4f02b..6e5031f56fb4f211e0ba43911d4495b1bb0b5b62 100755 (executable)
@@ -452,7 +452,7 @@ test_expect_success 'checking out a commit before submodule moved needs manual u
        git mv sub sub2 &&
        git commit -m "moved sub to sub2" &&
        git checkout -q HEAD^ 2>actual &&
-       test_i18ngrep "^warning: unable to rmdir sub2:" actual &&
+       test_i18ngrep "^warning: unable to rmdir '\''sub2'\'':" actual &&
        git status -s sub2 >actual &&
        echo "?? sub2/" >expected &&
        test_cmp expected actual &&
index f0f1abd1c23d4303a686310d7325cd523d6c921b..f5f46a95b4be9bca4ca30e70cbe75051a191c96a 100755 (executable)
@@ -214,6 +214,44 @@ test_expect_success TTY 'git tag as alias respects pager.tag with -l' '
        ! test -e paginated.out
 '
 
+test_expect_success TTY 'git branch defaults to paging' '
+       rm -f paginated.out &&
+       test_terminal git branch &&
+       test -e paginated.out
+'
+
+test_expect_success TTY 'git branch respects pager.branch' '
+       rm -f paginated.out &&
+       test_terminal git -c pager.branch=false branch &&
+       ! test -e paginated.out
+'
+
+test_expect_success TTY 'git branch respects --no-pager' '
+       rm -f paginated.out &&
+       test_terminal git --no-pager branch &&
+       ! test -e paginated.out
+'
+
+test_expect_success TTY 'git branch --edit-description ignores pager.branch' '
+       rm -f paginated.out editor.used &&
+       write_script editor <<-\EOF &&
+               echo "New description" >"$1"
+               touch editor.used
+       EOF
+       EDITOR=./editor test_terminal git -c pager.branch branch --edit-description &&
+       ! test -e paginated.out &&
+       test -e editor.used
+'
+
+test_expect_success TTY 'git branch --set-upstream-to ignores pager.branch' '
+       rm -f paginated.out &&
+       git branch other &&
+       test_when_finished "git branch -D other" &&
+       test_terminal git -c pager.branch branch --set-upstream-to=other &&
+       test_when_finished "git branch --unset-upstream" &&
+       ! test -e paginated.out
+'
+
 # A colored commit log will begin with an appropriate ANSI escape
 # for the first color; the text "commit" comes later.
 colorful() {
@@ -570,4 +608,18 @@ test_expect_success 'command with underscores does not complain' '
        test_cmp expect actual
 '
 
+test_expect_success TTY 'git tag with auto-columns ' '
+       test_commit one &&
+       test_commit two &&
+       test_commit three &&
+       test_commit four &&
+       test_commit five &&
+       cat >expect <<-\EOF &&
+       initial  one      two      three    four     five
+       EOF
+       test_terminal env PAGER="cat >actual" COLUMNS=80 \
+               git -c column.ui=auto tag --sort=authordate &&
+       test_cmp expect actual
+'
+
 test_done
index fc6013ba3c83aa7243f15595bc7b6ddd8b8f46dc..0c394cf995cbcf17b1ebd8f38e73a0740e3e00b7 100755 (executable)
@@ -272,4 +272,15 @@ test_expect_success 'status ignored tracked directory with uncommitted file in t
        test_cmp expected actual
 '
 
+cat >expected <<\EOF
+!! tracked/submodule/
+EOF
+
+test_expect_success 'status ignores submodule in excluded directory' '
+       git init tracked/submodule &&
+       test_commit -C tracked/submodule initial &&
+       git status --porcelain --ignored -u tracked/submodule >actual &&
+       test_cmp expected actual
+'
+
 test_done
diff --git a/t/t7519-status-fsmonitor.sh b/t/t7519-status-fsmonitor.sh
new file mode 100755 (executable)
index 0000000..eb2d13b
--- /dev/null
@@ -0,0 +1,317 @@
+#!/bin/sh
+
+test_description='git status with file system watcher'
+
+. ./test-lib.sh
+
+#
+# To run the entire git test suite using fsmonitor:
+#
+# copy t/t7519/fsmonitor-all to a location in your path and then set
+# GIT_FSMONITOR_TEST=fsmonitor-all and run your tests.
+#
+
+# Note, after "git reset --hard HEAD" no extensions exist other than 'TREE'
+# "git update-index --fsmonitor" can be used to get the extension written
+# before testing the results.
+
+clean_repo () {
+       git reset --hard HEAD &&
+       git clean -fd
+}
+
+dirty_repo () {
+       : >untracked &&
+       : >dir1/untracked &&
+       : >dir2/untracked &&
+       echo 1 >modified &&
+       echo 2 >dir1/modified &&
+       echo 3 >dir2/modified &&
+       echo 4 >new &&
+       echo 5 >dir1/new &&
+       echo 6 >dir2/new
+}
+
+write_integration_script () {
+       write_script .git/hooks/fsmonitor-test<<-\EOF
+       if test "$#" -ne 2
+       then
+               echo "$0: exactly 2 arguments expected"
+               exit 2
+       fi
+       if test "$1" != 1
+       then
+               echo "Unsupported core.fsmonitor hook version." >&2
+               exit 1
+       fi
+       printf "untracked\0"
+       printf "dir1/untracked\0"
+       printf "dir2/untracked\0"
+       printf "modified\0"
+       printf "dir1/modified\0"
+       printf "dir2/modified\0"
+       printf "new\0"
+       printf "dir1/new\0"
+       printf "dir2/new\0"
+       EOF
+}
+
+test_lazy_prereq UNTRACKED_CACHE '
+       { git update-index --test-untracked-cache; ret=$?; } &&
+       test $ret -ne 1
+'
+
+test_expect_success 'setup' '
+       mkdir -p .git/hooks &&
+       : >tracked &&
+       : >modified &&
+       mkdir dir1 &&
+       : >dir1/tracked &&
+       : >dir1/modified &&
+       mkdir dir2 &&
+       : >dir2/tracked &&
+       : >dir2/modified &&
+       git -c core.fsmonitor= add . &&
+       git -c core.fsmonitor= commit -m initial &&
+       git config core.fsmonitor .git/hooks/fsmonitor-test &&
+       cat >.gitignore <<-\EOF
+       .gitignore
+       expect*
+       actual*
+       marker*
+       EOF
+'
+
+# test that the fsmonitor extension is off by default
+test_expect_success 'fsmonitor extension is off by default' '
+       test-dump-fsmonitor >actual &&
+       grep "^no fsmonitor" actual
+'
+
+# test that "update-index --fsmonitor" adds the fsmonitor extension
+test_expect_success 'update-index --fsmonitor" adds the fsmonitor extension' '
+       git update-index --fsmonitor &&
+       test-dump-fsmonitor >actual &&
+       grep "^fsmonitor last update" actual
+'
+
+# test that "update-index --no-fsmonitor" removes the fsmonitor extension
+test_expect_success 'update-index --no-fsmonitor" removes the fsmonitor extension' '
+       git update-index --no-fsmonitor &&
+       test-dump-fsmonitor >actual &&
+       grep "^no fsmonitor" actual
+'
+
+cat >expect <<EOF &&
+h dir1/modified
+H dir1/tracked
+h dir2/modified
+H dir2/tracked
+h modified
+H tracked
+EOF
+
+# test that "update-index --fsmonitor-valid" sets the fsmonitor valid bit
+test_expect_success 'update-index --fsmonitor-valid" sets the fsmonitor valid bit' '
+       git update-index --fsmonitor &&
+       git update-index --fsmonitor-valid dir1/modified &&
+       git update-index --fsmonitor-valid dir2/modified &&
+       git update-index --fsmonitor-valid modified &&
+       git ls-files -f >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<EOF &&
+H dir1/modified
+H dir1/tracked
+H dir2/modified
+H dir2/tracked
+H modified
+H tracked
+EOF
+
+# test that "update-index --no-fsmonitor-valid" clears the fsmonitor valid bit
+test_expect_success 'update-index --no-fsmonitor-valid" clears the fsmonitor valid bit' '
+       git update-index --no-fsmonitor-valid dir1/modified &&
+       git update-index --no-fsmonitor-valid dir2/modified &&
+       git update-index --no-fsmonitor-valid modified &&
+       git ls-files -f >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<EOF &&
+H dir1/modified
+H dir1/tracked
+H dir2/modified
+H dir2/tracked
+H modified
+H tracked
+EOF
+
+# test that all files returned by the script get flagged as invalid
+test_expect_success 'all files returned by integration script get flagged as invalid' '
+       write_integration_script &&
+       dirty_repo &&
+       git update-index --fsmonitor &&
+       git ls-files -f >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<EOF &&
+H dir1/modified
+h dir1/new
+H dir1/tracked
+H dir2/modified
+h dir2/new
+H dir2/tracked
+H modified
+h new
+H tracked
+EOF
+
+# test that newly added files are marked valid
+test_expect_success 'newly added files are marked valid' '
+       git add new &&
+       git add dir1/new &&
+       git add dir2/new &&
+       git ls-files -f >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<EOF &&
+H dir1/modified
+h dir1/new
+h dir1/tracked
+H dir2/modified
+h dir2/new
+h dir2/tracked
+H modified
+h new
+h tracked
+EOF
+
+# test that all unmodified files get marked valid
+test_expect_success 'all unmodified files get marked valid' '
+       # modified files result in update-index returning 1
+       test_must_fail git update-index --refresh --force-write-index &&
+       git ls-files -f >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<EOF &&
+H dir1/modified
+h dir1/tracked
+h dir2/modified
+h dir2/tracked
+h modified
+h tracked
+EOF
+
+# test that *only* files returned by the integration script get flagged as invalid
+test_expect_success '*only* files returned by the integration script get flagged as invalid' '
+       write_script .git/hooks/fsmonitor-test<<-\EOF &&
+       printf "dir1/modified\0"
+       EOF
+       clean_repo &&
+       git update-index --refresh --force-write-index &&
+       echo 1 >modified &&
+       echo 2 >dir1/modified &&
+       echo 3 >dir2/modified &&
+       test_must_fail git update-index --refresh --force-write-index &&
+       git ls-files -f >actual &&
+       test_cmp expect actual
+'
+
+# Ensure commands that call refresh_index() to move the index back in time
+# properly invalidate the fsmonitor cache
+test_expect_success 'refresh_index() invalidates fsmonitor cache' '
+       write_script .git/hooks/fsmonitor-test<<-\EOF &&
+       EOF
+       clean_repo &&
+       dirty_repo &&
+       git add . &&
+       git commit -m "to reset" &&
+       git reset HEAD~1 &&
+       git status >actual &&
+       git -c core.fsmonitor= status >expect &&
+       test_i18ncmp expect actual
+'
+
+# test fsmonitor with and without preloadIndex
+preload_values="false true"
+for preload_val in $preload_values
+do
+       test_expect_success "setup preloadIndex to $preload_val" '
+               git config core.preloadIndex $preload_val &&
+               if test $preload_val = true
+               then
+                       GIT_FORCE_PRELOAD_TEST=$preload_val; export GIT_FORCE_PRELOAD_TEST
+               else
+                       unset GIT_FORCE_PRELOAD_TEST
+               fi
+       '
+
+       # test fsmonitor with and without the untracked cache (if available)
+       uc_values="false"
+       test_have_prereq UNTRACKED_CACHE && uc_values="false true"
+       for uc_val in $uc_values
+       do
+               test_expect_success "setup untracked cache to $uc_val" '
+                       git config core.untrackedcache $uc_val
+               '
+
+               # Status is well tested elsewhere so we'll just ensure that the results are
+               # the same when using core.fsmonitor.
+               test_expect_success 'compare status with and without fsmonitor' '
+                       write_integration_script &&
+                       clean_repo &&
+                       dirty_repo &&
+                       git add new &&
+                       git add dir1/new &&
+                       git add dir2/new &&
+                       git status >actual &&
+                       git -c core.fsmonitor= status >expect &&
+                       test_i18ncmp expect actual
+               '
+
+               # Make sure it's actually skipping the check for modified and untracked
+               # (if enabled) files unless it is told about them.
+               test_expect_success "status doesn't detect unreported modifications" '
+                       write_script .git/hooks/fsmonitor-test<<-\EOF &&
+                       :>marker
+                       EOF
+                       clean_repo &&
+                       git status &&
+                       test_path_is_file marker &&
+                       dirty_repo &&
+                       rm -f marker &&
+                       git status >actual &&
+                       test_path_is_file marker &&
+                       test_i18ngrep ! "Changes not staged for commit:" actual &&
+                       if test $uc_val = true
+                       then
+                               test_i18ngrep ! "Untracked files:" actual
+                       fi &&
+                       if test $uc_val = false
+                       then
+                               test_i18ngrep "Untracked files:" actual
+                       fi &&
+                       rm -f marker
+               '
+       done
+done
+
+# test that splitting the index dosn't interfere
+test_expect_success 'splitting the index results in the same state' '
+       write_integration_script &&
+       dirty_repo &&
+       git update-index --fsmonitor  &&
+       git ls-files -f >expect &&
+       test-dump-fsmonitor >&2 && echo &&
+       git update-index --fsmonitor --split-index &&
+       test-dump-fsmonitor >&2 && echo &&
+       git ls-files -f >actual &&
+       test_cmp expect actual
+'
+
+test_done
diff --git a/t/t7519/fsmonitor-all b/t/t7519/fsmonitor-all
new file mode 100755 (executable)
index 0000000..691bc94
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# An test hook script to integrate with git to test fsmonitor.
+#
+# The hook is passed a version (currently 1) and a time in nanoseconds
+# formatted as a string and outputs to stdout all files that have been
+# modified since the given time. Paths must be relative to the root of
+# the working tree and separated by a single NUL.
+#
+#echo "$0 $*" >&2
+
+if test "$#" -ne 2
+then
+       echo "$0: exactly 2 arguments expected" >&2
+       exit 2
+fi
+
+if test "$1" != 1
+then
+       echo "Unsupported core.fsmonitor hook version." >&2
+       exit 1
+fi
+
+echo "/"
diff --git a/t/t7519/fsmonitor-none b/t/t7519/fsmonitor-none
new file mode 100755 (executable)
index 0000000..ed9cf5a
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# An test hook script to integrate with git to test fsmonitor.
+#
+# The hook is passed a version (currently 1) and a time in nanoseconds
+# formatted as a string and outputs to stdout all files that have been
+# modified since the given time. Paths must be relative to the root of
+# the working tree and separated by a single NUL.
+#
+#echo "$0 $*" >&2
+
+if test "$#" -ne 2
+then
+       echo "$0: exactly 2 arguments expected" >&2
+       exit 2
+fi
+
+if test "$1" != 1
+then
+       echo "Unsupported core.fsmonitor hook version." >&2
+       exit 1
+fi
diff --git a/t/t7519/fsmonitor-watchman b/t/t7519/fsmonitor-watchman
new file mode 100755 (executable)
index 0000000..5514edc
--- /dev/null
@@ -0,0 +1,133 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use IPC::Open2;
+
+# An example hook script to integrate Watchman
+# (https://facebook.github.io/watchman/) with git to speed up detecting
+# new and modified files.
+#
+# The hook is passed a version (currently 1) and a time in nanoseconds
+# formatted as a string and outputs to stdout all files that have been
+# modified since the given time. Paths must be relative to the root of
+# the working tree and separated by a single NUL.
+#
+# To enable this hook, rename this file to "query-watchman" and set
+# 'git config core.fsmonitor .git/hooks/query-watchman'
+#
+my ($version, $time) = @ARGV;
+#print STDERR "$0 $version $time\n";
+
+# Check the hook interface version
+
+if ($version == 1) {
+       # convert nanoseconds to seconds
+       $time = int $time / 1000000000;
+} else {
+       die "Unsupported query-fsmonitor hook version '$version'.\n" .
+           "Falling back to scanning...\n";
+}
+
+my $git_work_tree;
+if ($^O =~ 'msys' || $^O =~ 'cygwin') {
+       $git_work_tree = Win32::GetCwd();
+       $git_work_tree =~ tr/\\/\//;
+} else {
+       require Cwd;
+       $git_work_tree = Cwd::cwd();
+}
+
+my $retry = 1;
+
+launch_watchman();
+
+sub launch_watchman {
+
+       my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j')
+           or die "open2() failed: $!\n" .
+           "Falling back to scanning...\n";
+
+       # In the query expression below we're asking for names of files that
+       # changed since $time but were not transient (ie created after
+       # $time but no longer exist).
+       #
+       # To accomplish this, we're using the "since" generator to use the
+       # recency index to select candidate nodes and "fields" to limit the
+       # output to file names only. Then we're using the "expression" term to
+       # further constrain the results.
+       #
+       # The category of transient files that we want to ignore will have a
+       # creation clock (cclock) newer than $time_t value and will also not
+       # currently exist.
+
+       my $query = <<" END";
+               ["query", "$git_work_tree", {
+                       "since": $time,
+                       "fields": ["name"],
+                       "expression": ["not", ["allof", ["since", $time, "cclock"], ["not", "exists"]]]
+               }]
+       END
+       
+       open (my $fh, ">", ".git/watchman-query.json");
+       print $fh $query;
+       close $fh;
+
+       print CHLD_IN $query;
+       close CHLD_IN;
+       my $response = do {local $/; <CHLD_OUT>};
+
+       open ($fh, ">", ".git/watchman-response.json");
+       print $fh $response;
+       close $fh;
+
+       die "Watchman: command returned no output.\n" .
+           "Falling back to scanning...\n" if $response eq "";
+       die "Watchman: command returned invalid output: $response\n" .
+           "Falling back to scanning...\n" unless $response =~ /^\{/;
+
+       my $json_pkg;
+       eval {
+               require JSON::XS;
+               $json_pkg = "JSON::XS";
+               1;
+       } or do {
+               require JSON::PP;
+               $json_pkg = "JSON::PP";
+       };
+
+       my $o = $json_pkg->new->utf8->decode($response);
+
+       if ($retry > 0 and $o->{error} and $o->{error} =~ m/unable to resolve root .* directory (.*) is not watched/) {
+               print STDERR "Adding '$git_work_tree' to watchman's watch list.\n";
+               $retry--;
+               qx/watchman watch "$git_work_tree"/;
+               die "Failed to make watchman watch '$git_work_tree'.\n" .
+                   "Falling back to scanning...\n" if $? != 0;
+
+               # Watchman will always return all files on the first query so
+               # return the fast "everything is dirty" flag to git and do the
+               # Watchman query just to get it over with now so we won't pay
+               # the cost in git to look up each individual file.
+
+               open ($fh, ">", ".git/watchman-output.out");
+               print "/\0";
+               close $fh;
+
+               print "/\0";
+               eval { launch_watchman() };
+               exit 0;
+       }
+
+       die "Watchman: $o->{error}.\n" .
+           "Falling back to scanning...\n" if $o->{error};
+
+       open ($fh, ">", ".git/watchman-output.out");
+       binmode $fh, ":utf8";
+       print $fh @{$o->{files}};
+       close $fh;
+
+       binmode STDOUT, ":utf8";
+       local $, = "\0";
+       print @{$o->{files}};
+}
diff --git a/t/t7520-ignored-hook-warning.sh b/t/t7520-ignored-hook-warning.sh
new file mode 100755 (executable)
index 0000000..634fb7f
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+test_description='ignored hook warning'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       hookdir="$(git rev-parse --git-dir)/hooks" &&
+       hook="$hookdir/pre-commit" &&
+       mkdir -p "$hookdir" &&
+       write_script "$hook" <<-\EOF
+       exit 0
+       EOF
+'
+
+test_expect_success 'no warning if hook is not ignored' '
+       git commit --allow-empty -m "more" 2>message &&
+       test_i18ngrep ! -e "hook was ignored" message
+'
+
+test_expect_success POSIXPERM 'warning if hook is ignored' '
+       chmod -x "$hook" &&
+       git commit --allow-empty -m "even more" 2>message &&
+       test_i18ngrep -e "hook was ignored" message
+'
+
+test_expect_success POSIXPERM 'no warning if advice.ignoredHook set to false' '
+       test_config advice.ignoredHook false &&
+       chmod -x "$hook" &&
+       git commit --allow-empty -m "even more" 2>message &&
+       test_i18ngrep ! -e "hook was ignored" message
+'
+
+test_expect_success 'no warning if unset advice.ignoredHook and hook removed' '
+       rm -f "$hook" &&
+       test_unconfig advice.ignoredHook &&
+       git commit --allow-empty -m "even more" 2>message &&
+       test_i18ngrep ! -e "hook was ignored" message
+'
+
+test_done
diff --git a/t/t7521-ignored-mode.sh b/t/t7521-ignored-mode.sh
new file mode 100755 (executable)
index 0000000..9179094
--- /dev/null
@@ -0,0 +1,233 @@
+#!/bin/sh
+
+test_description='git status ignored modes'
+
+. ./test-lib.sh
+
+test_expect_success 'setup initial commit and ignore file' '
+       cat >.gitignore <<-\EOF &&
+       *.ign
+       ignored_dir/
+       !*.unignore
+       EOF
+       git add . &&
+       git commit -m "Initial commit"
+'
+
+test_expect_success 'Verify behavior of status on directories with ignored files' '
+       test_when_finished "git clean -fdx" &&
+       cat >expect <<-\EOF &&
+       ? expect
+       ? output
+       ! dir/ignored/ignored_1.ign
+       ! dir/ignored/ignored_2.ign
+       ! ignored/ignored_1.ign
+       ! ignored/ignored_2.ign
+       EOF
+
+       mkdir -p ignored dir/ignored &&
+       touch ignored/ignored_1.ign ignored/ignored_2.ign \
+               dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
+
+       git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
+       test_i18ncmp expect output
+'
+
+test_expect_success 'Verify status behavior on directory with tracked & ignored files' '
+       test_when_finished "git clean -fdx && git reset HEAD~1 --hard" &&
+       cat >expect <<-\EOF &&
+       ? expect
+       ? output
+       ! dir/tracked_ignored/ignored_1.ign
+       ! dir/tracked_ignored/ignored_2.ign
+       ! tracked_ignored/ignored_1.ign
+       ! tracked_ignored/ignored_2.ign
+       EOF
+
+       mkdir -p tracked_ignored dir/tracked_ignored &&
+       touch tracked_ignored/tracked_1 tracked_ignored/tracked_2 \
+               tracked_ignored/ignored_1.ign tracked_ignored/ignored_2.ign \
+               dir/tracked_ignored/tracked_1 dir/tracked_ignored/tracked_2 \
+               dir/tracked_ignored/ignored_1.ign dir/tracked_ignored/ignored_2.ign &&
+
+       git add tracked_ignored/tracked_1 tracked_ignored/tracked_2 \
+               dir/tracked_ignored/tracked_1 dir/tracked_ignored/tracked_2 &&
+       git commit -m "commit tracked files" &&
+
+       git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
+       test_i18ncmp expect output
+'
+
+test_expect_success 'Verify status behavior on directory with untracked and ignored files' '
+       test_when_finished "git clean -fdx" &&
+       cat >expect <<-\EOF &&
+       ? dir/untracked_ignored/untracked_1
+       ? dir/untracked_ignored/untracked_2
+       ? expect
+       ? output
+       ? untracked_ignored/untracked_1
+       ? untracked_ignored/untracked_2
+       ! dir/untracked_ignored/ignored_1.ign
+       ! dir/untracked_ignored/ignored_2.ign
+       ! untracked_ignored/ignored_1.ign
+       ! untracked_ignored/ignored_2.ign
+       EOF
+
+       mkdir -p untracked_ignored dir/untracked_ignored &&
+       touch untracked_ignored/untracked_1 untracked_ignored/untracked_2 \
+               untracked_ignored/ignored_1.ign untracked_ignored/ignored_2.ign \
+               dir/untracked_ignored/untracked_1 dir/untracked_ignored/untracked_2 \
+               dir/untracked_ignored/ignored_1.ign dir/untracked_ignored/ignored_2.ign &&
+
+       git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
+       test_i18ncmp expect output
+'
+
+test_expect_success 'Verify status matching ignored files on ignored directory' '
+       test_when_finished "git clean -fdx" &&
+       cat >expect <<-\EOF &&
+       ? expect
+       ? output
+       ! ignored_dir/
+       EOF
+
+       mkdir ignored_dir &&
+       touch ignored_dir/ignored_1 ignored_dir/ignored_2 \
+               ignored_dir/ignored_1.ign ignored_dir/ignored_2.ign &&
+
+       git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
+       test_i18ncmp expect output
+'
+
+test_expect_success 'Verify status behavior on ignored directory containing tracked file' '
+       test_when_finished "git clean -fdx && git reset HEAD~1 --hard" &&
+       cat >expect <<-\EOF &&
+       ? expect
+       ? output
+       ! ignored_dir/ignored_1
+       ! ignored_dir/ignored_1.ign
+       ! ignored_dir/ignored_2
+       ! ignored_dir/ignored_2.ign
+       EOF
+
+       mkdir ignored_dir &&
+       touch ignored_dir/ignored_1 ignored_dir/ignored_2 \
+               ignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \
+               ignored_dir/tracked &&
+       git add -f ignored_dir/tracked &&
+       git commit -m "Force add file in ignored directory" &&
+       git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
+       test_i18ncmp expect output
+'
+
+test_expect_success 'Verify matching ignored files with --untracked-files=normal' '
+       test_when_finished "git clean -fdx" &&
+       cat >expect <<-\EOF &&
+       ? expect
+       ? output
+       ? untracked_dir/
+       ! ignored_dir/
+       ! ignored_files/ignored_1.ign
+       ! ignored_files/ignored_2.ign
+       EOF
+
+       mkdir ignored_dir ignored_files untracked_dir &&
+       touch ignored_dir/ignored_1 ignored_dir/ignored_2 \
+               ignored_files/ignored_1.ign ignored_files/ignored_2.ign \
+               untracked_dir/untracked &&
+       git status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&
+       test_i18ncmp expect output
+'
+
+test_expect_success 'Verify matching ignored files with --untracked-files=normal' '
+       test_when_finished "git clean -fdx" &&
+       cat >expect <<-\EOF &&
+       ? expect
+       ? output
+       ? untracked_dir/
+       ! ignored_dir/
+       ! ignored_files/ignored_1.ign
+       ! ignored_files/ignored_2.ign
+       EOF
+
+       mkdir ignored_dir ignored_files untracked_dir &&
+       touch ignored_dir/ignored_1 ignored_dir/ignored_2 \
+               ignored_files/ignored_1.ign ignored_files/ignored_2.ign \
+               untracked_dir/untracked &&
+       git status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&
+       test_i18ncmp expect output
+'
+
+test_expect_success 'Verify status behavior on ignored directory containing tracked file' '
+       test_when_finished "git clean -fdx && git reset HEAD~1 --hard" &&
+       cat >expect <<-\EOF &&
+       ? expect
+       ? output
+       ! ignored_dir/ignored_1
+       ! ignored_dir/ignored_1.ign
+       ! ignored_dir/ignored_2
+       ! ignored_dir/ignored_2.ign
+       EOF
+
+       mkdir ignored_dir &&
+       touch ignored_dir/ignored_1 ignored_dir/ignored_2 \
+               ignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \
+               ignored_dir/tracked &&
+       git add -f ignored_dir/tracked &&
+       git commit -m "Force add file in ignored directory" &&
+       git status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&
+       test_i18ncmp expect output
+'
+
+test_expect_success 'Verify behavior of status with --ignored=no' '
+       test_when_finished "git clean -fdx" &&
+       cat >expect <<-\EOF &&
+       ? expect
+       ? output
+       EOF
+
+       mkdir -p ignored dir/ignored &&
+       touch ignored/ignored_1.ign ignored/ignored_2.ign \
+               dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
+
+       git status --porcelain=v2 --ignored=no --untracked-files=all >output &&
+       test_i18ncmp expect output
+'
+
+test_expect_success 'Verify behavior of status with --ignored=traditional and --untracked-files=all' '
+       test_when_finished "git clean -fdx" &&
+       cat >expect <<-\EOF &&
+       ? expect
+       ? output
+       ! dir/ignored/ignored_1.ign
+       ! dir/ignored/ignored_2.ign
+       ! ignored/ignored_1.ign
+       ! ignored/ignored_2.ign
+       EOF
+
+       mkdir -p ignored dir/ignored &&
+       touch ignored/ignored_1.ign ignored/ignored_2.ign \
+               dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
+
+       git status --porcelain=v2 --ignored=traditional --untracked-files=all >output &&
+       test_i18ncmp expect output
+'
+
+test_expect_success 'Verify behavior of status with --ignored=traditional and --untracked-files=normal' '
+       test_when_finished "git clean -fdx" &&
+       cat >expect <<-\EOF &&
+       ? expect
+       ? output
+       ! dir/
+       ! ignored/
+       EOF
+
+       mkdir -p ignored dir/ignored &&
+       touch ignored/ignored_1.ign ignored/ignored_2.ign \
+               dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
+
+       git status --porcelain=v2 --ignored=traditional --untracked-files=normal >output &&
+       test_i18ncmp expect output
+'
+
+test_done
index 2a6679c2f596fb13a34786a4aa4b954e61c61113..1797f632a388d16f23a47c7693c7a3f0f0deabf6 100755 (executable)
@@ -60,6 +60,18 @@ test_expect_success setup '
                echo " line with leading space3"
                echo "line without leading space2"
        } >space &&
+       cat >hello.ps1 <<-\EOF &&
+       # No-op.
+       function dummy() {}
+
+       # Say hello.
+       function hello() {
+         echo "Hello world."
+       } # hello
+
+       # Still a no-op.
+       function dummy() {}
+       EOF
        git add . &&
        test_tick &&
        git commit -m initial
@@ -766,18 +778,27 @@ test_expect_success 'grep -W shows no trailing empty lines' '
        test_cmp expected actual
 '
 
-cat >expected <<EOF
-hello.c=       printf("Hello world.\n");
-hello.c:       return 0;
-hello.c-       /* char ?? */
-EOF
-
 test_expect_success 'grep -W with userdiff' '
        test_when_finished "rm -f .gitattributes" &&
-       git config diff.custom.xfuncname "(printf.*|})$" &&
-       echo "hello.c diff=custom" >.gitattributes &&
-       git grep -W return >actual &&
-       test_cmp expected actual
+       git config diff.custom.xfuncname "^function .*$" &&
+       echo "hello.ps1 diff=custom" >.gitattributes &&
+       git grep -W echo >function-context-userdiff-actual
+'
+
+test_expect_success ' includes preceding comment' '
+       grep "# Say hello" function-context-userdiff-actual
+'
+
+test_expect_success ' includes function line' '
+       grep "=function hello" function-context-userdiff-actual
+'
+
+test_expect_success ' includes matching line' '
+       grep ":  echo" function-context-userdiff-actual
+'
+
+test_expect_success ' includes last line of the function' '
+       grep "} # hello" function-context-userdiff-actual
 '
 
 for threads in $(test_seq 0 10)
@@ -1110,6 +1131,12 @@ test_expect_success PCRE 'grep -P pattern' '
        test_cmp expected actual
 '
 
+test_expect_success LIBPCRE2 "grep -P with (*NO_JIT) doesn't error out" '
+       git grep -P "(*NO_JIT)\p{Ps}.*?\p{Pe}" hello.c >actual &&
+       test_cmp expected actual
+
+'
+
 test_expect_success !PCRE 'grep -P pattern errors without PCRE' '
        test_must_fail git grep -P "foo.*bar"
 '
index a3d388228a19cec73c7c9521905b82cd36d07752..50bca62def6b0632cd89d1c0cfb2d98b76e6aa1d 100755 (executable)
@@ -27,9 +27,7 @@ cat << EOF
 # 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/>.
 #
 EOF
 }
index 2cb999ecfab69eb7825ad05c79a874114f921716..fc614dcbfa74c39e120ff2fc80cdec07ea16a338 100755 (executable)
@@ -1245,6 +1245,10 @@ test_expect_success 'double dash "git checkout"' '
        --conflict=
        --orphan Z
        --patch Z
+       --detach Z
+       --ignore-skip-worktree-bits Z
+       --recurse-submodules Z
+       --no-recurse-submodules Z
        EOF
 '
 
index 9b61f16f7a8807ad96014d79f1d1b968397584bf..e7065df2bb028aff76f3169d84873a1a3d789571 100644 (file)
@@ -175,9 +175,10 @@ esac
 
 # Convenience
 #
-# A regexp to match 5 and 40 hexdigits
+# A regexp to match 5, 35 and 40 hexdigits
 _x05='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x05$_x05$_x05$_x05$_x05$_x05$_x05$_x05"
+_x35="$_x05$_x05$_x05$_x05$_x05$_x05$_x05"
+_x40="$_x35$_x05"
 
 # Zero SHA-1
 _z40=0000000000000000000000000000000000000000
@@ -193,7 +194,7 @@ LF='
 # when case-folding filenames
 u200c=$(printf '\342\200\214')
 
-export _x05 _x40 _z40 LF u200c EMPTY_TREE EMPTY_BLOB
+export _x05 _x35 _x40 _z40 LF u200c EMPTY_TREE EMPTY_BLOB
 
 # Each test should start with something like this, after copyright notices:
 #
@@ -1027,6 +1028,8 @@ test -z "$NO_PERL" && test_set_prereq PERL
 test -z "$NO_PTHREADS" && test_set_prereq PTHREADS
 test -z "$NO_PYTHON" && test_set_prereq PYTHON
 test -n "$USE_LIBPCRE1$USE_LIBPCRE2" && test_set_prereq PCRE
+test -n "$USE_LIBPCRE1" && test_set_prereq LIBPCRE1
+test -n "$USE_LIBPCRE2" && test_set_prereq LIBPCRE2
 test -z "$NO_GETTEXT" && test_set_prereq GETTEXT
 
 # Can we rely on git's output in the C locale?
index b8f4b5e145b8b6b1d80cac299dbaedc8253e861b..450908b2e0bc4a94bc23a862abf04a56366aedd7 100644 (file)
  * `create_tempfile()` returns an allocated tempfile on success or NULL
  * on failure. On errors, `errno` describes the reason for failure.
  *
- * `delete_tempfile()`, `rename_tempfile()`, and `close_tempfile_gently()`
- * return 0 on success. On failure they set `errno` appropriately and return
- * -1. `delete` and `rename` (but not `close`) do their best to delete the
- * temporary file before returning.
+ * `rename_tempfile()` and `close_tempfile_gently()` return 0 on success.
+ * On failure they set `errno` appropriately and return -1.
+ * `delete_tempfile()` and `rename` (but not `close`) do their best to
+ * delete the temporary file before returning.
  */
 
 struct tempfile {
diff --git a/templates/hooks--fsmonitor-watchman.sample b/templates/hooks--fsmonitor-watchman.sample
new file mode 100755 (executable)
index 0000000..e673bb3
--- /dev/null
@@ -0,0 +1,114 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use IPC::Open2;
+
+# An example hook script to integrate Watchman
+# (https://facebook.github.io/watchman/) with git to speed up detecting
+# new and modified files.
+#
+# The hook is passed a version (currently 1) and a time in nanoseconds
+# formatted as a string and outputs to stdout all files that have been
+# modified since the given time. Paths must be relative to the root of
+# the working tree and separated by a single NUL.
+#
+# To enable this hook, rename this file to "query-watchman" and set
+# 'git config core.fsmonitor .git/hooks/query-watchman'
+#
+my ($version, $time) = @ARGV;
+
+# Check the hook interface version
+
+if ($version == 1) {
+       # convert nanoseconds to seconds
+       $time = int $time / 1000000000;
+} else {
+       die "Unsupported query-fsmonitor hook version '$version'.\n" .
+           "Falling back to scanning...\n";
+}
+
+my $git_work_tree;
+if ($^O =~ 'msys' || $^O =~ 'cygwin') {
+       $git_work_tree = Win32::GetCwd();
+       $git_work_tree =~ tr/\\/\//;
+} else {
+       require Cwd;
+       $git_work_tree = Cwd::cwd();
+}
+
+my $retry = 1;
+
+launch_watchman();
+
+sub launch_watchman {
+
+       my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty')
+           or die "open2() failed: $!\n" .
+           "Falling back to scanning...\n";
+
+       # In the query expression below we're asking for names of files that
+       # changed since $time but were not transient (ie created after
+       # $time but no longer exist).
+       #
+       # To accomplish this, we're using the "since" generator to use the
+       # recency index to select candidate nodes and "fields" to limit the
+       # output to file names only. Then we're using the "expression" term to
+       # further constrain the results.
+       #
+       # The category of transient files that we want to ignore will have a
+       # creation clock (cclock) newer than $time_t value and will also not
+       # currently exist.
+
+       my $query = <<" END";
+               ["query", "$git_work_tree", {
+                       "since": $time,
+                       "fields": ["name"],
+                       "expression": ["not", ["allof", ["since", $time, "cclock"], ["not", "exists"]]]
+               }]
+       END
+
+       print CHLD_IN $query;
+       close CHLD_IN;
+       my $response = do {local $/; <CHLD_OUT>};
+
+       die "Watchman: command returned no output.\n" .
+           "Falling back to scanning...\n" if $response eq "";
+       die "Watchman: command returned invalid output: $response\n" .
+           "Falling back to scanning...\n" unless $response =~ /^\{/;
+
+       my $json_pkg;
+       eval {
+               require JSON::XS;
+               $json_pkg = "JSON::XS";
+               1;
+       } or do {
+               require JSON::PP;
+               $json_pkg = "JSON::PP";
+       };
+
+       my $o = $json_pkg->new->utf8->decode($response);
+
+       if ($retry > 0 and $o->{error} and $o->{error} =~ m/unable to resolve root .* directory (.*) is not watched/) {
+               print STDERR "Adding '$git_work_tree' to watchman's watch list.\n";
+               $retry--;
+               qx/watchman watch "$git_work_tree"/;
+               die "Failed to make watchman watch '$git_work_tree'.\n" .
+                   "Falling back to scanning...\n" if $? != 0;
+
+               # Watchman will always return all files on the first query so
+               # return the fast "everything is dirty" flag to git and do the
+               # Watchman query just to get it over with now so we won't pay
+               # the cost in git to look up each individual file.
+               print "/\0";
+               eval { launch_watchman() };
+               exit 0;
+       }
+
+       die "Watchman: $o->{error}.\n" .
+           "Falling back to scanning...\n" if $o->{error};
+
+       binmode STDOUT, ":utf8";
+       local $, = "\0";
+       print @{$o->{files}};
+}
diff --git a/trace.c b/trace.c
index 7508aea028bb642140cabcd02c37cdaf3a21234c..cb1293ed33e16f605f8ac99336f040275c7bda7a 100644 (file)
--- a/trace.c
+++ b/trace.c
@@ -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"
index c948d5215c22fbbb3e174e219017935741e9b2d5..bf05a2dcf1073ad3b84b030cd9e866c6e5414792 100644 (file)
@@ -535,7 +535,7 @@ static int fetch_with_import(struct transport *transport,
                else
                        private = xstrdup(name);
                if (private) {
-                       if (read_ref(private, posn->old_oid.hash) < 0)
+                       if (read_ref(private, &posn->old_oid) < 0)
                                die("Could not read ref %s", private);
                        free(private);
                }
@@ -795,7 +795,8 @@ static int push_update_refs_status(struct helper_data *data,
                private = apply_refspecs(data->refspecs, data->refspec_nr, ref->name);
                if (!private)
                        continue;
-               update_ref("update by helper", private, ref->new_oid.hash, NULL, 0, 0);
+               update_ref("update by helper", private, &ref->new_oid, NULL,
+                          0, 0);
                free(private);
        }
        strbuf_release(&buf);
@@ -941,10 +942,9 @@ static int push_refs_with_export(struct transport *transport,
                                        int flag;
 
                                        /* Follow symbolic refs (mainly for HEAD). */
-                                       name = resolve_ref_unsafe(
-                                                ref->peer_ref->name,
-                                                RESOLVE_REF_READING,
-                                                oid.hash, &flag);
+                                       name = resolve_ref_unsafe(ref->peer_ref->name,
+                                                                 RESOLVE_REF_READING,
+                                                                 &oid, &flag);
                                        if (!name || !(flag & REF_ISSYMREF))
                                                name = ref->peer_ref->name;
 
@@ -1066,8 +1066,7 @@ static struct ref *get_refs_list(struct transport *transport, int for_push)
                if (eon) {
                        if (has_attribute(eon + 1, "unchanged")) {
                                (*tail)->status |= REF_STATUS_UPTODATE;
-                               if (read_ref((*tail)->name,
-                                            (*tail)->old_oid.hash) < 0)
+                               if (read_ref((*tail)->name, &(*tail)->old_oid) < 0)
                                        die(_("Could not read ref %s"),
                                            (*tail)->name);
                        }
index f1e2f61991424f3314158676754f6b8d305a31f2..7231d1b1b0632fa9bb71bcc074b143ff34c1a733 100644 (file)
@@ -305,8 +305,8 @@ void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int v
                if (ref->deletion) {
                        delete_ref(NULL, rs.dst, NULL, 0);
                } else
-                       update_ref("update by push", rs.dst,
-                                       ref->new_oid.hash, NULL, 0, 0);
+                       update_ref("update by push", rs.dst, &ref->new_oid,
+                                  NULL, 0, 0);
                free(rs.dst);
        }
 }
index 4bb93155bc6e0349156b21ad1287d573593a79d5..fe2e466ac1dcb164b214ce4856ed9d62884ddbd3 100644 (file)
@@ -212,9 +212,9 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,
                mode = 0;
        }
 
-       if (DIFF_OPT_TST(opt, RECURSIVE) && isdir) {
+       if (opt->flags.recursive && isdir) {
                recurse = 1;
-               emitthis = DIFF_OPT_TST(opt, TREE_IN_RECURSIVE);
+               emitthis = opt->flags.tree_in_recursive;
        }
 
        if (emitthis) {
@@ -425,7 +425,7 @@ static struct combine_diff_path *ll_diff_tree_paths(
        ttree = fill_tree_descriptor(&t, oid);
 
        /* Enable recursion indefinitely */
-       opt->pathspec.recursive = DIFF_OPT_TST(opt, RECURSIVE);
+       opt->pathspec.recursive = opt->flags.recursive;
 
        for (;;) {
                int imin, cmp;
@@ -484,7 +484,7 @@ static struct combine_diff_path *ll_diff_tree_paths(
                /* t = p[imin] */
                if (cmp == 0) {
                        /* are either pi > p[imin] or diff(t,pi) != Ã¸ ? */
-                       if (!DIFF_OPT_TST(opt, FIND_COPIES_HARDER)) {
+                       if (!opt->flags.find_copies_harder) {
                                for (i = 0; i < nparent; ++i) {
                                        /* p[i] > p[imin] */
                                        if (tp[i].entry.mode & S_IFXMIN_NEQ)
@@ -522,7 +522,7 @@ static struct combine_diff_path *ll_diff_tree_paths(
                /* t > p[imin] */
                else {
                        /* âˆ€i pi=p[imin] -> D += "-p[imin]" */
-                       if (!DIFF_OPT_TST(opt, FIND_COPIES_HARDER)) {
+                       if (!opt->flags.find_copies_harder) {
                                for (i = 0; i < nparent; ++i)
                                        if (tp[i].entry.mode & S_IFXMIN_NEQ)
                                                goto skip_emit_tp;
@@ -608,8 +608,8 @@ static void try_to_follow_renames(const struct object_id *old_oid,
        q->nr = 0;
 
        diff_setup(&diff_opts);
-       DIFF_OPT_SET(&diff_opts, RECURSIVE);
-       DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);
+       diff_opts.flags.recursive = 1;
+       diff_opts.flags.find_copies_harder = 1;
        diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
        diff_opts.single_follow = opt->pathspec.items[0].match;
        diff_opts.break_opt = opt->break_opt;
@@ -706,7 +706,7 @@ int diff_tree_oid(const struct object_id *old_oid,
        strbuf_addstr(&base, base_str);
 
        retval = ll_diff_tree_oid(old_oid, new_oid, &base, opt);
-       if (!*base_str && DIFF_OPT_TST(opt, FOLLOW_RENAMES) && diff_might_be_rename())
+       if (!*base_str && opt->flags.follow_renames && diff_might_be_rename())
                try_to_follow_renames(old_oid, new_oid, &base, opt);
 
        strbuf_release(&base);
index 71b70ccb12756cde1f1c5aeda952be6e2b91f5b8..bf8b6029013c31000d83d6292f83ce0d0021d5d4 100644 (file)
@@ -14,6 +14,7 @@
 #include "dir.h"
 #include "submodule.h"
 #include "submodule-config.h"
+#include "fsmonitor.h"
 
 /*
  * Error messages expected by scripts out of plumbing commands such as
@@ -408,6 +409,7 @@ static int apply_sparse_checkout(struct index_state *istate,
                ce->ce_flags &= ~CE_SKIP_WORKTREE;
        if (was_skip_worktree != ce_skip_worktree(ce)) {
                ce->ce_flags |= CE_UPDATE_IN_BASE;
+               mark_fsmonitor_invalid(istate, ce);
                istate->cache_changed |= CE_ENTRY_CHANGED;
        }
 
@@ -1541,15 +1543,15 @@ static int verify_clean_subdirectory(const struct cache_entry *ce,
        int cnt = 0;
 
        if (S_ISGITLINK(ce->ce_mode)) {
-               unsigned char sha1[20];
-               int sub_head = resolve_gitlink_ref(ce->name, "HEAD", sha1);
+               struct object_id oid;
+               int sub_head = resolve_gitlink_ref(ce->name, "HEAD", &oid);
                /*
                 * If we are not going to update the submodule, then
                 * we don't care.
                 */
-               if (!sub_head && !hashcmp(sha1, ce->oid.hash))
+               if (!sub_head && !oidcmp(&oid, &ce->oid))
                        return 0;
-               return verify_clean_submodule(sub_head ? NULL : sha1_to_hex(sha1),
+               return verify_clean_submodule(sub_head ? NULL : oid_to_hex(&oid),
                                              ce, error_type, o);
        }
 
index e25f725c0feaa57c9a09c976628217c99dfb5652..d5de18127c63a9d017578e0969743392d39526d4 100644 (file)
@@ -18,6 +18,7 @@
 #include "parse-options.h"
 #include "argv-array.h"
 #include "prio-queue.h"
+#include "protocol.h"
 
 static const char * const upload_pack_usage[] = {
        N_("git upload-pack [<options>] <dir>"),
@@ -787,7 +788,7 @@ static void receive_needs(void)
                if (skip_prefix(line, "deepen-not ", &arg)) {
                        char *ref = NULL;
                        struct object_id oid;
-                       if (expand_ref(arg, strlen(arg), oid.hash, &ref) != 1)
+                       if (expand_ref(arg, strlen(arg), &oid, &ref) != 1)
                                die("git upload-pack: ambiguous deepen-not: %s", line);
                        string_list_append(&deepen_not, ref);
                        free(ref);
@@ -955,7 +956,7 @@ static int send_ref(const char *refname, const struct object_id *oid,
                packet_write_fmt(1, "%s %s\n", oid_to_hex(oid), refname_nons);
        }
        capabilities = NULL;
-       if (!peel_ref(refname, peeled.hash))
+       if (!peel_ref(refname, &peeled))
                packet_write_fmt(1, "%s %s^{}\n", oid_to_hex(&peeled), refname_nons);
        return 0;
 }
@@ -1066,6 +1067,23 @@ int cmd_main(int argc, const char **argv)
                die("'%s' does not appear to be a git repository", dir);
 
        git_config(upload_pack_config, NULL);
-       upload_pack();
+
+       switch (determine_protocol_version_server()) {
+       case protocol_v1:
+               /*
+                * v1 is just the original protocol with a version string,
+                * so just fall through after writing the version string.
+                */
+               if (advertise_refs || !stateless_rpc)
+                       packet_write_fmt(1, "version 1\n");
+
+               /* fallthrough */
+       case protocol_v0:
+               upload_pack();
+               break;
+       case protocol_unknown_version:
+               BUG("unknown protocol version");
+       }
+
        return 0;
 }
index 274f1a4935798cba3e8cc7250559f14305678419..5d4d3733f75648dfa8d6955a5ec1ed9f3dfeb77b 100644 (file)
--- a/walker.c
+++ b/walker.c
@@ -7,7 +7,7 @@
 #include "blob.h"
 #include "refs.h"
 
-static unsigned char current_commit_sha1[20];
+static struct object_id current_commit_oid;
 
 void walker_say(struct walker *walker, const char *fmt, ...)
 {
@@ -24,9 +24,9 @@ static void report_missing(const struct object *obj)
        fprintf(stderr, "Cannot obtain needed %s %s\n",
                obj->type ? typename(obj->type): "object",
                oid_to_hex(&obj->oid));
-       if (!is_null_sha1(current_commit_sha1))
+       if (!is_null_oid(&current_commit_oid))
                fprintf(stderr, "while processing commit %s.\n",
-                       sha1_to_hex(current_commit_sha1));
+                       oid_to_hex(&current_commit_oid));
 }
 
 static int process(struct walker *walker, struct object *obj);
@@ -82,7 +82,7 @@ static int process_commit(struct walker *walker, struct commit *commit)
        if (commit->object.flags & COMPLETE)
                return 0;
 
-       hashcpy(current_commit_sha1, commit->object.oid.hash);
+       oidcpy(&current_commit_oid, &commit->object.oid);
 
        walker_say(walker, "walk %s\n", oid_to_hex(&commit->object.oid));
 
@@ -187,14 +187,14 @@ static int loop(struct walker *walker)
        return 0;
 }
 
-static int interpret_target(struct walker *walker, char *target, unsigned char *sha1)
+static int interpret_target(struct walker *walker, char *target, struct object_id *oid)
 {
-       if (!get_sha1_hex(target, sha1))
+       if (!get_oid_hex(target, oid))
                return 0;
        if (!check_refname_format(target, 0)) {
                struct ref *ref = alloc_ref(target);
                if (!walker->fetch_ref(walker, ref)) {
-                       hashcpy(sha1, ref->old_oid.hash);
+                       oidcpy(oid, &ref->old_oid);
                        free(ref);
                        return 0;
                }
@@ -259,7 +259,7 @@ int walker_fetch(struct walker *walker, int targets, char **target,
        struct strbuf refname = STRBUF_INIT;
        struct strbuf err = STRBUF_INIT;
        struct ref_transaction *transaction = NULL;
-       unsigned char *sha1 = xmalloc(targets * 20);
+       struct object_id *oids = xmalloc(targets * sizeof(struct object_id));
        char *msg = NULL;
        int i, ret = -1;
 
@@ -279,11 +279,11 @@ int walker_fetch(struct walker *walker, int targets, char **target,
        }
 
        for (i = 0; i < targets; i++) {
-               if (interpret_target(walker, target[i], &sha1[20 * i])) {
+               if (interpret_target(walker, target[i], oids + i)) {
                        error("Could not interpret response from server '%s' as something to pull", target[i]);
                        goto done;
                }
-               if (process(walker, lookup_unknown_object(&sha1[20 * i])))
+               if (process(walker, lookup_unknown_object(oids[i].hash)))
                        goto done;
        }
 
@@ -304,7 +304,7 @@ int walker_fetch(struct walker *walker, int targets, char **target,
                strbuf_reset(&refname);
                strbuf_addf(&refname, "refs/%s", write_ref[i]);
                if (ref_transaction_update(transaction, refname.buf,
-                                          &sha1[20 * i], NULL, 0,
+                                          oids + i, NULL, 0,
                                           msg ? msg : "fetch (unknown)",
                                           &err)) {
                        error("%s", err.buf);
@@ -321,7 +321,7 @@ int walker_fetch(struct walker *walker, int targets, char **target,
 done:
        ref_transaction_free(transaction);
        free(msg);
-       free(sha1);
+       free(oids);
        strbuf_release(&err);
        strbuf_release(&refname);
        return ret;
index 70015629dc69e4bdf7f8ef1973596f3360898da3..f5da7d286d537fa99a1bd2dd5180068b9d85da2f 100644 (file)
@@ -31,7 +31,7 @@ static void add_head_info(struct worktree *wt)
        target = refs_resolve_ref_unsafe(get_worktree_ref_store(wt),
                                         "HEAD",
                                         0,
-                                        wt->head_sha1, &flags);
+                                        &wt->head_oid, &flags);
        if (!target)
                return;
 
@@ -327,7 +327,8 @@ const struct worktree *find_shared_symref(const char *symref,
                refs = get_worktree_ref_store(wt);
                symref_target = refs_resolve_ref_unsafe(refs, symref, 0,
                                                        NULL, &flags);
-               if ((flags & REF_ISSYMREF) && !strcmp(symref_target, target)) {
+               if ((flags & REF_ISSYMREF) &&
+                   symref_target && !strcmp(symref_target, target)) {
                        existing = wt;
                        break;
                }
index 9276c81ae7cb5c42a86fd9ebbdb28720be409e47..c28a880e1839ef26604d80de78eb9c663a6be381 100644 (file)
@@ -8,7 +8,7 @@ struct worktree {
        char *id;
        char *head_ref;         /* NULL if HEAD is broken or detached */
        char *lock_reason;      /* internal use */
-       unsigned char head_sha1[20];
+       struct object_id head_oid;
        int is_detached;
        int is_bare;
        int is_current;
index 61aba0b5c1bece4aad04bee649accae990a8ce18..d20356a776bc63d4a94e4b421582433fb915087a 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -569,7 +569,7 @@ static int warn_if_unremovable(const char *op, const char *file, int rc)
        if (!rc || errno == ENOENT)
                return 0;
        err = errno;
-       warning_errno("unable to %s %s", op, file);
+       warning_errno("unable to %s '%s'", op, file);
        errno = err;
        return rc;
 }
@@ -583,7 +583,7 @@ int unlink_or_msg(const char *file, struct strbuf *err)
        if (!rc || errno == ENOENT)
                return 0;
 
-       strbuf_addf(err, "unable to unlink %s: %s",
+       strbuf_addf(err, "unable to unlink '%s': %s",
                    file, strerror(errno));
        return -1;
 }
@@ -653,9 +653,9 @@ void write_file_buf(const char *path, const char *buf, size_t len)
 {
        int fd = xopen(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
        if (write_in_full(fd, buf, len) < 0)
-               die_errno(_("could not write to %s"), path);
+               die_errno(_("could not write to '%s'"), path);
        if (close(fd))
-               die_errno(_("could not close %s"), path);
+               die_errno(_("could not close '%s'"), path);
 }
 
 void write_file(const char *path, const char *fmt, ...)
index 29bc64cc0280675065142bac257930904216d415..ef26f0744632fdcca919e68ffc58bf414b29e597 100644 (file)
@@ -559,12 +559,12 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
        init_revisions(&rev, NULL);
        setup_revisions(0, NULL, &rev, NULL);
        rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
-       DIFF_OPT_SET(&rev.diffopt, DIRTY_SUBMODULES);
+       rev.diffopt.flags.dirty_submodules = 1;
        rev.diffopt.ita_invisible_in_index = 1;
        if (!s->show_untracked_files)
-               DIFF_OPT_SET(&rev.diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
+               rev.diffopt.flags.ignore_untracked_in_submodules = 1;
        if (s->ignore_submodule_arg) {
-               DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
+               rev.diffopt.flags.override_submodule_config = 1;
                handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
        }
        rev.diffopt.format_callback = wt_status_collect_changed_cb;
@@ -583,7 +583,7 @@ static void wt_status_collect_changes_index(struct wt_status *s)
        opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference;
        setup_revisions(0, NULL, &rev, &opt);
 
-       DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
+       rev.diffopt.flags.override_submodule_config = 1;
        rev.diffopt.ita_invisible_in_index = 1;
        if (s->ignore_submodule_arg) {
                handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
@@ -658,10 +658,15 @@ static void wt_status_collect_untracked(struct wt_status *s)
        if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)
                dir.flags |=
                        DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
-       if (s->show_ignored_files)
+       if (s->show_ignored_mode) {
                dir.flags |= DIR_SHOW_IGNORED_TOO;
-       else
+
+               if (s->show_ignored_mode == SHOW_MATCHING_IGNORED)
+                       dir.flags |= DIR_SHOW_IGNORED_TOO_MODE_MATCHING;
+       } else {
                dir.untracked = the_index.untracked;
+       }
+
        setup_standard_excludes(&dir);
 
        fill_directory(&dir, &the_index, &s->pathspec);
@@ -949,7 +954,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
        const char *c = color(WT_STATUS_HEADER, s);
 
        init_revisions(&rev, NULL);
-       DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
+       rev.diffopt.flags.allow_textconv = 1;
        rev.diffopt.ita_invisible_in_index = 1;
 
        memset(&opt, 0, sizeof(opt));
@@ -1449,7 +1454,7 @@ static void wt_status_get_detached_from(struct wt_status_state *state)
                return;
        }
 
-       if (dwim_ref(cb.buf.buf, cb.buf.len, oid.hash, &ref) == 1 &&
+       if (dwim_ref(cb.buf.buf, cb.buf.len, &oid, &ref) == 1 &&
            /* sha1 is a commit? match without further lookup */
            (!oidcmp(&cb.noid, &oid) ||
             /* perhaps sha1 is a tag, try to dereference to a commit */
@@ -1619,7 +1624,7 @@ static void wt_longstatus_print(struct wt_status *s)
        }
        if (s->show_untracked_files) {
                wt_longstatus_print_other(s, &s->untracked, _("Untracked files"), "add");
-               if (s->show_ignored_files)
+               if (s->show_ignored_mode)
                        wt_longstatus_print_other(s, &s->ignored, _("Ignored files"), "add -f");
                if (advice_status_u_option && 2000 < s->untracked_in_ms) {
                        status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
@@ -2262,9 +2267,11 @@ int has_unstaged_changes(int ignore_submodules)
        int result;
 
        init_revisions(&rev_info, NULL);
-       if (ignore_submodules)
-               DIFF_OPT_SET(&rev_info.diffopt, IGNORE_SUBMODULES);
-       DIFF_OPT_SET(&rev_info.diffopt, QUICK);
+       if (ignore_submodules) {
+               rev_info.diffopt.flags.ignore_submodules = 1;
+               rev_info.diffopt.flags.override_submodule_config = 1;
+       }
+       rev_info.diffopt.flags.quick = 1;
        diff_setup_done(&rev_info.diffopt);
        result = run_diff_files(&rev_info, 0);
        return diff_result_code(&rev_info.diffopt, result);
@@ -2283,8 +2290,8 @@ int has_uncommitted_changes(int ignore_submodules)
 
        init_revisions(&rev_info, NULL);
        if (ignore_submodules)
-               DIFF_OPT_SET(&rev_info.diffopt, IGNORE_SUBMODULES);
-       DIFF_OPT_SET(&rev_info.diffopt, QUICK);
+               rev_info.diffopt.flags.ignore_submodules = 1;
+       rev_info.diffopt.flags.quick = 1;
        add_head_to_pending(&rev_info);
        diff_setup_done(&rev_info.diffopt);
        result = run_diff_index(&rev_info, 1);
@@ -2297,14 +2304,14 @@ int has_uncommitted_changes(int ignore_submodules)
  */
 int require_clean_work_tree(const char *action, const char *hint, int ignore_submodules, int gently)
 {
-       struct lock_file *lock_file = xcalloc(1, sizeof(*lock_file));
+       struct lock_file lock_file = LOCK_INIT;
        int err = 0, fd;
 
-       fd = hold_locked_index(lock_file, 0);
+       fd = hold_locked_index(&lock_file, 0);
        refresh_cache(REFRESH_QUIET);
        if (0 <= fd)
-               update_index_if_able(&the_index, lock_file);
-       rollback_lock_file(lock_file);
+               update_index_if_able(&the_index, &lock_file);
+       rollback_lock_file(&lock_file);
 
        if (has_unstaged_changes(ignore_submodules)) {
                /* TRANSLATORS: the action is e.g. "pull with rebase" */
index 64f4d33ea183c1a6e700f1f6667b83f0e904773e..fe27b465e220bb720dfd0b4d52ddeae74a942707 100644 (file)
@@ -27,6 +27,12 @@ enum untracked_status_type {
        SHOW_ALL_UNTRACKED_FILES
 };
 
+enum show_ignored_type {
+       SHOW_NO_IGNORED,
+       SHOW_TRADITIONAL_IGNORED,
+       SHOW_MATCHING_IGNORED,
+};
+
 /* from where does this commit originate */
 enum commit_whence {
        FROM_COMMIT,     /* normal */
@@ -70,7 +76,7 @@ struct wt_status {
        int display_comment_prefix;
        int relative_paths;
        int submodule_summary;
-       int show_ignored_files;
+       enum show_ignored_type show_ignored_mode;
        enum untracked_status_type show_untracked_files;
        const char *ignore_submodule_arg;
        char color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];
index 018e03308921e3b44bb75b8bc1e005424ca4fc1b..770e1f7f8185e05f2618c261b70a5773041432fb 100644 (file)
@@ -5,6 +5,7 @@
 #include "xdiff/xdiffi.h"
 #include "xdiff/xemit.h"
 #include "xdiff/xmacros.h"
+#include "xdiff/xutils.h"
 
 struct xdiff_emit_state {
        xdiff_emit_consume_fn consume;
@@ -296,6 +297,17 @@ void xdiff_clear_find_func(xdemitconf_t *xecfg)
        }
 }
 
+unsigned long xdiff_hash_string(const char *s, size_t len, long flags)
+{
+       return xdl_hash_record(&s, s + len, flags);
+}
+
+int xdiff_compare_lines(const char *l1, long s1,
+                       const char *l2, long s2, long flags)
+{
+       return xdl_recmatch(l1, s1, l2, s2, flags);
+}
+
 int git_xmerge_style = -1;
 
 int git_xmerge_config(const char *var, const char *value, void *cb)
index 6f6ba9095df1f81c975652ce515c451649974145..135fc05d72e8f066a63902785d12485a656efa97 100644 (file)
@@ -29,4 +29,20 @@ extern void xdiff_clear_find_func(xdemitconf_t *xecfg);
 extern int git_xmerge_config(const char *var, const char *value, void *cb);
 extern int git_xmerge_style;
 
+/*
+ * Compare the strings l1 with l2 which are of size s1 and s2 respectively.
+ * Returns 1 if the strings are deemed equal, 0 otherwise.
+ * The `flags` given as XDF_WHITESPACE_FLAGS determine how white spaces
+ * are treated for the comparision.
+ */
+extern int xdiff_compare_lines(const char *l1, long s1,
+                              const char *l2, long s2, long flags);
+
+/*
+ * Returns a hash of the string s of length len.
+ * The `flags` given as XDF_WHITESPACE_FLAGS determine how white spaces
+ * are treated for the hash.
+ */
+extern unsigned long xdiff_hash_string(const char *s, size_t len, long flags);
+
 #endif
index b090ad8eacfe6ed171aa14b39901a8042ab678d9..915591f7d4797ace61761195933ab170c6b70eff 100644 (file)
@@ -13,8 +13,8 @@
  *  Lesser General Public License for more details.
  *
  *  You should have received a copy of the GNU Lesser General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
 extern "C" {
 #endif /* #ifdef __cplusplus */
 
+/* xpparm_t.flags */
+#define XDF_NEED_MINIMAL (1 << 0)
 
-#define XDF_NEED_MINIMAL (1 << 1)
-#define XDF_IGNORE_WHITESPACE (1 << 2)
-#define XDF_IGNORE_WHITESPACE_CHANGE (1 << 3)
-#define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 4)
-#define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE | XDF_IGNORE_WHITESPACE_AT_EOL)
+#define XDF_IGNORE_WHITESPACE (1 << 1)
+#define XDF_IGNORE_WHITESPACE_CHANGE (1 << 2)
+#define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 3)
+#define XDF_IGNORE_CR_AT_EOL (1 << 4)
+#define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | \
+                             XDF_IGNORE_WHITESPACE_CHANGE | \
+                             XDF_IGNORE_WHITESPACE_AT_EOL | \
+                             XDF_IGNORE_CR_AT_EOL)
 
-#define XDF_PATIENCE_DIFF (1 << 5)
-#define XDF_HISTOGRAM_DIFF (1 << 6)
+#define XDF_IGNORE_BLANK_LINES (1 << 7)
+
+#define XDF_PATIENCE_DIFF (1 << 14)
+#define XDF_HISTOGRAM_DIFF (1 << 15)
 #define XDF_DIFF_ALGORITHM_MASK (XDF_PATIENCE_DIFF | XDF_HISTOGRAM_DIFF)
 #define XDF_DIFF_ALG(x) ((x) & XDF_DIFF_ALGORITHM_MASK)
 
-#define XDF_IGNORE_BLANK_LINES (1 << 7)
-
-#define XDF_INDENT_HEURISTIC (1 << 8)
+#define XDF_INDENT_HEURISTIC (1 << 23)
 
+/* xdemitconf_t.flags */
 #define XDL_EMIT_FUNCNAMES (1 << 0)
 #define XDL_EMIT_FUNCCONTEXT (1 << 2)
 
index 93a65680a18284041ce57f61191d20dcc763e022..0de1ef463bf71b2021ae142a80ac5f87a03a43fc 100644 (file)
@@ -13,8 +13,8 @@
  *  Lesser General Public License for more details.
  *
  *  You should have received a copy of the GNU Lesser General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
index 8b81206c9af0767bd91c4b9e453f7c5c2bde47b1..8f1c7c8b0445f88514d0cb0ce868380d1711ccd2 100644 (file)
@@ -13,8 +13,8 @@
  *  Lesser General Public License for more details.
  *
  *  You should have received a copy of the GNU Lesser General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
index 8c88dbde3827ce9d1b5e4f95287513514b80fac1..7778dc2b190f84fe2ab1e1539756a7e1f4254c3b 100644 (file)
@@ -13,8 +13,8 @@
  *  Lesser General Public License for more details.
  *
  *  You should have received a copy of the GNU Lesser General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
@@ -121,6 +121,12 @@ static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,
        return xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv);
 }
 
+static int is_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri)
+{
+       char dummy[1];
+       return match_func_rec(xdf, xecfg, ri, dummy, sizeof(dummy)) >= 0;
+}
+
 struct func_line {
        long len;
        char buf[80];
@@ -178,7 +184,6 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
 
                        /* Appended chunk? */
                        if (i1 >= xe->xdf1.nrec) {
-                               char dummy[1];
                                long i2 = xch->i2;
 
                                /*
@@ -186,8 +191,7 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                                 * a whole function was added.
                                 */
                                while (i2 < xe->xdf2.nrec) {
-                                       if (match_func_rec(&xe->xdf2, xecfg, i2,
-                                               dummy, sizeof(dummy)) >= 0)
+                                       if (is_func_rec(&xe->xdf2, xecfg, i2))
                                                goto post_context_calculation;
                                        i2++;
                                }
@@ -200,6 +204,9 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                        }
 
                        fs1 = get_func_line(xe, xecfg, NULL, i1, -1);
+                       while (fs1 > 0 && !is_empty_rec(&xe->xdf1, fs1 - 1) &&
+                              !is_func_rec(&xe->xdf1, xecfg, fs1 - 1))
+                               fs1--;
                        if (fs1 < 0)
                                fs1 = 0;
                        if (fs1 < s1) {
index d29710770ce40bafa6e9eb2b2ea7c9c8ba43c727..1b9887e670d48ddab89fd3e8724f442c35fc1adc 100644 (file)
@@ -13,8 +13,8 @@
  *  Lesser General Public License for more details.
  *
  *  You should have received a copy of the GNU Lesser General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
index 526ccb344d231fb978f53b80deb17ec6c8fed368..f35c4485dfee480badc0d383a556ac3a17768e10 100644 (file)
@@ -13,8 +13,8 @@
  *  Lesser General Public License for more details.
  *
  *  You should have received a copy of the GNU Lesser General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
index 165a895a93e04b33ca7c8f3839ee85e0eccb4a07..2809a28ca960147c285bc5a224ed377a0964663a 100644 (file)
@@ -13,8 +13,8 @@
  *  Lesser General Public License for more details.
  *
  *  You should have received a copy of the GNU Lesser General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
index f338ad6c757cda29a052960a504715c062ab5dda..1659edb45393a6b57ca70654e67784e5d0025c65 100644 (file)
@@ -13,8 +13,8 @@
  *  Lesser General Public License for more details.
  *
  *  You should have received a copy of the GNU Lesser General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
index 9f91702de740c3a2ca922059e71a32b0245edf1b..a44e776328ce617c3144c17870ab1616a2239947 100644 (file)
@@ -13,8 +13,8 @@
  *  Lesser General Public License for more details.
  *
  *  You should have received a copy of the GNU Lesser General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
index 13b55aba7441bc84d2c5c075110e9ef798ba18f8..abeb8fb84e6d73086d612b831963a227e35743b8 100644 (file)
@@ -13,8 +13,8 @@
  *  Lesser General Public License for more details.
  *
  *  You should have received a copy of the GNU Lesser General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
index 8fb06a537451cbf3335ab4bdacb0f992e9744338..947d9fc1bb8cf95719284de6563227485907988f 100644 (file)
@@ -13,8 +13,8 @@
  *  Lesser General Public License for more details.
  *
  *  You should have received a copy of the GNU Lesser General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
index 2511aef8d89ab52be5ec6a5e46236b4b6bcd07ea..8442bd436efeab81afc25db9d89da082638fcca4 100644 (file)
@@ -13,8 +13,8 @@
  *  Lesser General Public License for more details.
  *
  *  You should have received a copy of the GNU Lesser General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
index 04d7b32e4e4a75b9d5762f3d3a2a2f9c236075a0..88e5995535467475216de82b1eeb744e744f3b4d 100644 (file)
@@ -13,8 +13,8 @@
  *  Lesser General Public License for more details.
  *
  *  You should have received a copy of the GNU Lesser General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
@@ -156,6 +156,24 @@ int xdl_blankline(const char *line, long size, long flags)
        return (i == size);
 }
 
+/*
+ * Have we eaten everything on the line, except for an optional
+ * CR at the very end?
+ */
+static int ends_with_optional_cr(const char *l, long s, long i)
+{
+       int complete = s && l[s-1] == '\n';
+
+       if (complete)
+               s--;
+       if (s == i)
+               return 1;
+       /* do not ignore CR at the end of an incomplete line */
+       if (complete && s == i + 1 && l[i] == '\r')
+               return 1;
+       return 0;
+}
+
 int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
 {
        int i1, i2;
@@ -170,7 +188,8 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
 
        /*
         * -w matches everything that matches with -b, and -b in turn
-        * matches everything that matches with --ignore-space-at-eol.
+        * matches everything that matches with --ignore-space-at-eol,
+        * which in turn matches everything that matches with --ignore-cr-at-eol.
         *
         * Each flavor of ignoring needs different logic to skip whitespaces
         * while we have both sides to compare.
@@ -204,6 +223,14 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
                        i1++;
                        i2++;
                }
+       } else if (flags & XDF_IGNORE_CR_AT_EOL) {
+               /* Find the first difference and see how the line ends */
+               while (i1 < s1 && i2 < s2 && l1[i1] == l2[i2]) {
+                       i1++;
+                       i2++;
+               }
+               return (ends_with_optional_cr(l1, s1, i1) &&
+                       ends_with_optional_cr(l2, s2, i2));
        }
 
        /*
@@ -230,9 +257,16 @@ static unsigned long xdl_hash_record_with_whitespace(char const **data,
                char const *top, long flags) {
        unsigned long ha = 5381;
        char const *ptr = *data;
+       int cr_at_eol_only = (flags & XDF_WHITESPACE_FLAGS) == XDF_IGNORE_CR_AT_EOL;
 
        for (; ptr < top && *ptr != '\n'; ptr++) {
-               if (XDL_ISSPACE(*ptr)) {
+               if (cr_at_eol_only) {
+                       /* do not ignore CR at the end of an incomplete line */
+                       if (*ptr == '\r' &&
+                           (ptr + 1 < top && ptr[1] == '\n'))
+                               continue;
+               }
+               else if (XDL_ISSPACE(*ptr)) {
                        const char *ptr2 = ptr;
                        int at_eol;
                        while (ptr + 1 < top && XDL_ISSPACE(ptr[1])
index 4646ce575251b07053f20285be99422d6576603e..fba7bae03c7855ca90aff3f238321581a91a6676 100644 (file)
@@ -13,8 +13,8 @@
  *  Lesser General Public License for more details.
  *
  *  You should have received a copy of the GNU Lesser General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *