Merge branch 'jk/test-z-n-unquoted'
authorJunio C Hamano <gitster@pobox.com>
Mon, 23 May 2016 21:54:35 +0000 (14:54 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 23 May 2016 21:54:35 +0000 (14:54 -0700)
t9xxx series has been updated primarily for readability, while
fixing small bugs in it. A few scripted Porcelains have also been
updated to fix possible bugs around their use of "test -z" and
"test -n".

* jk/test-z-n-unquoted:
always quote shell arguments to test -z/-n
t9103: modernize test style
t9107: switch inverted single/double quotes in test
t9107: use "return 1" instead of "exit 1"
t9100,t3419: enclose all test code in single-quotes
t/lib-git-svn: drop $remote_git_svn and $git_svn_id

149 files changed:
.travis.yml
Documentation/Makefile
Documentation/RelNotes/2.8.3.txt
Documentation/RelNotes/2.9.0.txt
Documentation/config.txt
Documentation/diff-options.txt
Documentation/everyday.txto
Documentation/git-check-ignore.txt
Documentation/git-commit.txt
Documentation/git-filter-branch.txt
Documentation/git-for-each-ref.txt
Documentation/git-format-patch.txt
Documentation/git-init.txt
Documentation/git-mailinfo.txt
Documentation/git-notes.txt
Documentation/git-submodule.txt
Documentation/git.txt
Documentation/githooks.txt
Documentation/lint-gitlink.perl [new file with mode: 0755]
Documentation/merge-options.txt
Documentation/pretty-formats.txt
Documentation/technical/api-credentials.txt
Documentation/technical/api-parse-options.txt
Documentation/technical/pack-protocol.txt
Makefile
bisect.c
branch.c
branch.h
builtin/am.c
builtin/branch.c
builtin/checkout.c
builtin/commit.c
builtin/diff-files.c
builtin/diff-index.c
builtin/diff-tree.c
builtin/diff.c
builtin/fetch.c
builtin/help.c
builtin/index-pack.c
builtin/log.c
builtin/mailsplit.c
builtin/merge-file.c
builtin/notes.c
builtin/pack-objects.c
builtin/pull.c
builtin/remote-ext.c
builtin/remote.c
builtin/rm.c
builtin/submodule--helper.c
builtin/update-index.c
builtin/upload-archive.c
builtin/worktree.c
cache.h
check-racy.c
ci/test-documentation.sh [new file with mode: 0755]
combine-diff.c
compat/mingw.c
compat/mingw.h
compat/precompose_utf8.c
compat/win32/pthread.h
compat/win32/syslog.c
config.c
connected.c
convert.c
copy.c
credential-cache--daemon.c
diff-no-index.c
dir.c
dir.h
editor.c
entry.c
environment.c
fast-import.c
fsck.c
git-compat-util.h
git-cvsserver.perl
git-parse-remote.sh
git-rebase--interactive.sh
git-submodule.sh
gpg-interface.c
grep.c
http.c
ident.c
ll-merge.c
mailmap.c
parse-options.c
patch-ids.c
patch-ids.h
path.c
perl/Git.pm
reachable.c
remote.c
rerere.c
run-command.c
sequencer.c
server-info.c
sha1_file.c
split-index.c
submodule.c
submodule.h
t/README
t/helper/test-parse-options.c
t/lib-httpd/apache.conf
t/perf/p3404-rebase-interactive.sh [new file with mode: 0755]
t/perf/perf-lib.sh
t/t0000-basic.sh
t/t0001-init.sh
t/t0027-auto-crlf.sh
t/t0040-parse-options.sh
t/t0060-path-utils.sh
t/t1350-config-hooks-path.sh [new file with mode: 0755]
t/t1450-fsck.sh
t/t2025-worktree-add.sh
t/t3404-rebase-interactive.sh
t/t3513-revert-submodule.sh
t/t3910-mac-os-precompose.sh
t/t4014-format-patch.sh
t/t5550-http-fetch-dumb.sh
t/t5551-http-fetch-smart.sh
t/t5601-clone.sh
t/t5611-clone-config.sh
t/t6024-recursive-merge.sh
t/t6036-recursive-corner-cases.sh
t/t6041-bisect-submodule.sh
t/t6302-for-each-ref-filter.sh
t/t7400-submodule-basic.sh
t/t7406-submodule-update.sh
t/t7412-submodule--helper.sh [deleted file]
t/t7501-commit.sh
t/t7507-commit-verbose.sh
t/t7609-merge-co-error-msgs.sh
t/test-lib-functions.sh
t/test-lib.sh
transport-helper.c
unpack-trees.c
upload-pack.c
usage.c
utf8.h
vcs-svn/line_buffer.c
vcs-svn/sliding_window.c
vcs-svn/svndiff.c
vcs-svn/svndump.c
wildmatch.c
worktree.c
worktree.h
wrap-for-bin.sh
wrapper.c
wt-status.c
wt-status.h
index 1fdcec8437a8c78b3697b0e0ca55eb2a0778541e..adab5b89bba7b41a07a2a000dddbd4496ba7c7d3 100644 (file)
@@ -35,6 +35,21 @@ env:
     # t9816 occasionally fails with "TAP out of sequence errors" on Travis CI OS X
     - GIT_SKIP_TESTS="t9810 t9816"
 
+matrix:
+  include:
+    - env: Documentation
+      os: linux
+      compiler: clang
+      addons:
+        apt:
+          packages:
+          - asciidoc
+          - xmlto
+      before_install:
+      before_script:
+      script: ci/test-documentation.sh
+      after_failure:
+
 before_install:
   - >
     case "${TRAVIS_OS_NAME:-linux}" in
index 3e39e2815baca82fe5f1fff8791de37c3c7247ac..f6e288bc634cc9a7b231cac0a237a7770efd098f 100644 (file)
@@ -204,6 +204,7 @@ ifndef V
        QUIET_DBLATEX   = @echo '   ' DBLATEX $@;
        QUIET_XSLTPROC  = @echo '   ' XSLTPROC $@;
        QUIET_GEN       = @echo '   ' GEN $@;
+       QUIET_LINT      = @echo '   ' LINT $@;
        QUIET_STDERR    = 2> /dev/null
        QUIET_SUBDIR0   = +@subdir=
        QUIET_SUBDIR1   = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
@@ -427,4 +428,7 @@ quick-install-html: require-htmlrepo
 print-man1:
        @for i in $(MAN1_TXT); do echo $$i; done
 
+lint-docs::
+       $(QUIET_LINT)$(PERL_PATH) lint-gitlink.perl
+
 .PHONY: FORCE
index af184783bc07534b1519b0b5081d2350d029029b..fedd9968e5e3f20984cfedb54ec6ebcb0c4514e5 100644 (file)
@@ -67,4 +67,35 @@ Fixes since v2.8.2
    recurses into, but this was incorrect when the command was not run
    from the root level of the superproject.
 
+ * The test scripts for "git p4" (but not "git p4" implementation
+   itself) has been updated so that they would work even on a system
+   where the installed version of Python is python 3.
+
+ * The "user.useConfigOnly" configuration variable makes it an error
+   if users do not explicitly set user.name and user.email.  However,
+   its check was not done early enough and allowed another error to
+   trigger, reporting that the default value we guessed from the
+   system setting was unusable.  This was a suboptimal end-user
+   experience as we want the users to set user.name/user.email without
+   relying on the auto-detection at all.
+
+ * "git mv old new" did not adjust the path for a submodule that lives
+   as a subdirectory inside old/ directory correctly.
+
+ * "git push" from a corrupt repository that attempts to push a large
+   number of refs deadlocked; the thread to relay rejection notices
+   for these ref updates blocked on writing them to the main thread,
+   after the main thread at the receiving end notices that the push
+   failed and decides not to read these notices and return a failure.
+
+ * A question by "git send-email" to ask the identity of the sender
+   has been updated.
+
+ * Recent update to Git LFS broke "git p4" by changing the output from
+   its "lfs pointer" subcommand.
+
+ * Some multi-byte encoding can have a backslash byte as a later part
+   of one letter, which would confuse "highlight" filter used in
+   gitweb.
+
 Also contains minor documentation updates and code clean-ups.
index 2448a7eb7f509b3a4931cba9a652cc3255c722d8..211b9722dc0f0004c40b6c50587ff0021b92482e 100644 (file)
@@ -124,6 +124,14 @@ UI, Workflows & Features
    more readable by using a blank line as a strong hint that the
    contents before and after it belong to a logically separate unit.
 
+ * A new configuration variable core.hooksPath allows customizing
+   where the hook directory is.
+
+ * An earlier addition of "sanitize_submodule_env" with 14111fc4 (git:
+   submodule honor -c credential.* from command line, 2016-02-29)
+   turned out to be a convoluted no-op; implement what it wanted to do
+   correctly, and stop filtering settings given via "git -c var=val".
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -165,12 +173,6 @@ Performance, Internal Implementation, Development Support etc.
    have been moved to t/helper/ subdirectory to reduce clutter at the
    top level of the tree.
 
-   Note that this can break your tests if you check out revisions
-   across the merge boundary of this topic, e0b58519 (Merge branch
-   'nd/test-helpers', 2016-04-29), as bin-wrappers/test-* are not
-   rebuilt to point the underlying executables.  For now, "make
-   distclean" is your friend.
-
  * Unify internal logic between "git tag -v" and "git verify-tag"
    commands by making one directly call into the other.
    (merge bef234b st/verify-tag later to maint).
@@ -188,6 +190,21 @@ Performance, Internal Implementation, Development Support etc.
 
  * Move from unsigned char[20] to struct object_id continues.
 
+ * Update of "git submodule" to move pieces of logic to C continues.
+
+ * The code for warning_errno/die_errno has been refactored and a new
+   error_errno() reporting helper is introduced.
+   (merge 1da045f nd/error-errno later to maint).
+
+ * Running tests with '-x' option to trace the individual command
+   executions is a useful way to debug test scripts, but some tests
+   that capture the standard error stream and check what the command
+   said can be broken with the trace output mixed in.  When running
+   our tests under "bash", however, we can redirect the trace output
+   to another file descriptor to keep the standard error of programs
+   being tested intact.
+   (merge d88785e jk/test-send-sh-x-trace-elsewhere later to maint).
+
 
 Also contains various documentation updates and code clean-ups.
 
@@ -379,6 +396,51 @@ notes for details).
    "git stash".
    (merge 6694856 jc/commit-tree-ignore-commit-gpgsign later to maint).
 
+ * "http.cookieFile" configuration variable clearly wants a pathname,
+   but we forgot to treat it as such by e.g. applying tilde expansion.
+   (merge e5a39ad bn/http-cookiefile-config later to maint).
+
+ * Consolidate description of tilde-expansion that is done to
+   configuration variables that take pathname to a single place.
+   (merge dca83ab jc/config-pathname-type later to maint).
+
+ * Correct faulty recommendation to use "git submodule deinit ." when
+   de-initialising all submodules, which would result in a strange
+   error message in a pathological corner case.
+   (merge f6a5279 sb/submodule-deinit-all later to maint).
+
+ * Many 'linkgit:<git documentation page>' references were broken,
+   which are all fixed with this.
+   (merge 1cca17d jc/linkgit-fix later to maint).
+
+ * "git rerere" can get confused by conflict markers deliberately left
+   by the inner merge step, because they are indistinguishable from
+   the real conflict markers left by the outermost merge which are
+   what the end user and "rerere" need to look at.  This was fixed by
+   making the conflict markers left by the inner merges a bit longer.
+   (merge 0f9fd5c jc/ll-merge-internal later to maint).
+
+ * CI test was taught to build documentation pages.
+   (merge b98712b ls/travis-build-doc later to maint).
+
+ * "git fsck" learned to catch NUL byte in a commit object as
+   potential error and warn.
+   (merge 6d2d780 jc/fsck-nul-in-commit later to maint).
+
+ * Portability enhancement for "rebase -i" to help platforms whose
+   shell does not like "for i in <empty>" (which is not POSIX-kosher).
+   (merge 8e98b35 jk/rebase-interative-eval-fix later to maint).
+
+ * On Windows, .git and optionally any files whose name starts with a
+   dot are now marked as hidden, with a core.hideDotFiles knob to
+   customize this behaviour.
+   (merge ebf31e7 js/windows-dotgit later to maint).
+
+ * Documentation for "git merge --verify-signatures" has been updated
+   to clarify that the signature of only the commit at the tip is
+   verified.  Also the phrasing used for signature and key validity is
+   adjusted to align with that used by OpenPGP.
+   (merge 05a5869 kf/gpg-sig-verification-doc later to maint).
 
  * Other minor clean-ups and documentation updates
    (merge 8b5a3e9 kn/for-each-tag-branch later to maint).
@@ -393,3 +455,13 @@ notes for details).
    (merge fa72245 ew/normal-to-e later to maint).
    (merge 2e39a24 rn/glossary-typofix later to maint).
    (merge cadfbef sb/clean-test-fix later to maint).
+   (merge 832c0e5 lp/typofixes later to maint).
+   (merge f5ee54a sb/z-is-gnutar-ism later to maint).
+   (merge 2e3926b va/i18n-misc-updates later to maint).
+   (merge f212dcc bn/config-doc-tt-varnames later to maint).
+   (merge f54bea4 nd/remote-plural-ours-plus-theirs later to maint).
+   (merge 2bb0518 ak/t4151-ls-files-could-be-empty later to maint).
+   (merge 4df4313 jc/test-seq later to maint).
+   (merge a75a308 tb/t5601-sed-fix later to maint).
+   (merge 6c1fbe1 va/i18n-remote-comment-to-align later to maint).
+   (merge dee2303 va/mailinfo-doc-typofix later to maint).
index ece0acdbab468d2ba968abcf2940211dda15493f..53f00dbc267db194c7c5558a008fd9d8ec20068c 100644 (file)
@@ -81,13 +81,16 @@ Includes
 
 You can include one config file from another by setting the special
 `include.path` variable to the name of the file to be included. The
+variable takes a pathname as its value, and is subject to tilde
+expansion.
+
+The
 included file is expanded immediately, as if its contents had been
 found at the location of the include directive. If the value of the
 `include.path` variable is a relative path, the path is considered to be
 relative to the configuration file in which the include directive was
-found. The value of `include.path` is subject to tilde expansion: `~/`
-is expanded to the value of `$HOME`, and `~user/` to the specified
-user's home directory. See below for examples.
+found.  See below for examples.
+
 
 Example
 ~~~~~~~
@@ -114,7 +117,7 @@ Example
        [include]
                path = /path/to/foo.inc ; include by absolute path
                path = foo ; expand "foo" relative to the current file
-               path = ~/foo ; expand "foo" in your $HOME directory
+               path = ~/foo ; expand "foo" in your `$HOME` directory
 
 
 Values
@@ -169,6 +172,13 @@ thing on the same output line (e.g. opening parenthesis before the
 list of branch names in `log --decorate` output) is set to be
 painted with `bold` or some other attribute.
 
+pathname::
+       A variable that takes a pathname value can be given a
+       string that begins with "`~/`" or "`~user/`", and the usual
+       tilde expansion happens to such a string: `~/`
+       is expanded to the value of `$HOME`, and `~user/` to the
+       specified user's home directory.
+
 
 Variables
 ~~~~~~~~~
@@ -269,6 +279,12 @@ See linkgit:git-update-index[1].
 +
 The default is true (when core.filemode is not specified in the config file).
 
+core.hideDotFiles::
+       (Windows-only) If true, mark newly-created directories and files whose
+       name starts with a dot as hidden.  If 'dotGitOnly', only the `.git/`
+       directory is hidden, but no other files starting with a dot.  The
+       default mode is 'dotGitOnly'.
+
 core.ignoreCase::
        If true, this option enables various workarounds to enable
        Git to work better on filesystems that are not case sensitive,
@@ -337,9 +353,9 @@ core.quotePath::
 
 core.eol::
        Sets the line ending type to use in the working directory for
-       files that have the `text` property set.  Alternatives are
-       'lf', 'crlf' and 'native', which uses the platform's native
-       line ending.  The default value is `native`.  See
+       files that have the `text` property set when core.autocrlf is false.
+       Alternatives are 'lf', 'crlf' and 'native', which uses the platform's
+       native line ending.  The default value is `native`.  See
        linkgit:gitattributes[5] for more information on end-of-line
        conversion.
 
@@ -486,10 +502,10 @@ repository's usual working tree).
 
 core.logAllRefUpdates::
        Enable the reflog. Updates to a ref <ref> is logged to the file
-       "$GIT_DIR/logs/<ref>", by appending the new and old
+       "`$GIT_DIR/logs/<ref>`", by appending the new and old
        SHA-1, the date/time and the reason of the update, but
        only when the file exists.  If this configuration
-       variable is set to true, missing "$GIT_DIR/logs/<ref>"
+       variable is set to true, missing "`$GIT_DIR/logs/<ref>`"
        file is automatically created for branch heads (i.e. under
        refs/heads/), remote refs (i.e. under refs/remotes/),
        note refs (i.e. under refs/notes/), and the symbolic ref HEAD.
@@ -593,12 +609,11 @@ be delta compressed, but larger binary media files won't be.
 Common unit suffixes of 'k', 'm', or 'g' are supported.
 
 core.excludesFile::
-       In addition to '.gitignore' (per-directory) and
-       '.git/info/exclude', Git looks into this file for patterns
-       of files which are not meant to be tracked.  "`~/`" is expanded
-       to the value of `$HOME` and "`~user/`" to the specified user's
-       home directory. Its default value is $XDG_CONFIG_HOME/git/ignore.
-       If $XDG_CONFIG_HOME is either not set or empty, $HOME/.config/git/ignore
+       Specifies the pathname to the file that contains patterns to
+       describe paths that are not meant to be tracked, in addition
+       to '.gitignore' (per-directory) and '.git/info/exclude'.
+       Defaults to `$XDG_CONFIG_HOME/git/ignore`.
+       If `$XDG_CONFIG_HOME` is either not set or empty, `$HOME/.config/git/ignore`
        is used instead. See linkgit:gitignore[5].
 
 core.askPass::
@@ -615,8 +630,25 @@ core.attributesFile::
        '.git/info/attributes', Git looks into this file for attributes
        (see linkgit:gitattributes[5]). Path expansions are made the same
        way as for `core.excludesFile`. Its default value is
-       $XDG_CONFIG_HOME/git/attributes. If $XDG_CONFIG_HOME is either not
-       set or empty, $HOME/.config/git/attributes is used instead.
+       `$XDG_CONFIG_HOME/git/attributes`. If `$XDG_CONFIG_HOME` is either not
+       set or empty, `$HOME/.config/git/attributes` is used instead.
+
+core.hooksPath::
+       By default Git will look for your hooks in the
+       '$GIT_DIR/hooks' directory. Set this to different path,
+       e.g. '/etc/git/hooks', and Git will try to find your hooks in
+       that directory, e.g. '/etc/git/hooks/pre-receive' instead of
+       in '$GIT_DIR/hooks/pre-receive'.
++
+The path can be either absolute or relative. A relative path is
+taken as relative to the directory where the hooks are run (see
+the "DESCRIPTION" section of linkgit:githooks[5]).
++
+This configuration variable is useful in cases where you'd like to
+centrally configure your Git hooks instead of configuring them on a
+per-repository basis, or as a more flexible and centralized
+alternative to having an `init.templateDir` where you've changed
+default hooks.
 
 core.editor::
        Commands such as `commit` and `tag` that lets you edit
@@ -1106,9 +1138,12 @@ commit.status::
        message.  Defaults to true.
 
 commit.template::
-       Specify a file to use as the template for new commit messages.
-       "`~/`" is expanded to the value of `$HOME` and "`~user/`" to the
-       specified user's home directory.
+       Specify the pathname of a file to use as the template for
+       new commit messages.
+
+commit.verbose::
+       A boolean or int to specify the level of verbose with `git commit`.
+       See linkgit:git-commit[1].
 
 credential.helper::
        Specify an external helper to be called when a username or
@@ -1259,6 +1294,10 @@ format.outputDirectory::
        Set a custom directory to store the resulting files instead of the
        current working directory.
 
+format.useAutoBase::
+       A boolean value which lets you enable the `--base=auto` option of
+       format-patch by default.
+
 filter.<driver>.clean::
        The command which is used to convert the content of a worktree
        file to a blob upon checkin.  See linkgit:gitattributes[5] for
@@ -1335,7 +1374,7 @@ gc.worktreePruneExpire::
        'git worktree prune --expire 3.months.ago'.
        This config variable can be used to set a different grace
        period. The value "now" may be used to disable the grace
-       period and prune $GIT_DIR/worktrees immediately, or "never"
+       period and prune `$GIT_DIR/worktrees` immediately, or "never"
        may be used to suppress pruning.
 
 gc.reflogExpire::
@@ -1475,13 +1514,13 @@ grep.fallbackToNoIndex::
        is executed outside of a git repository.  Defaults to false.
 
 gpg.program::
-       Use this custom program instead of "gpg" found on $PATH when
+       Use this custom program instead of "`gpg`" found on `$PATH` when
        making or verifying a PGP signature. The program must support the
        same command-line interface as GPG, namely, to verify a detached
-       signature, "gpg --verify $file - <$signature" is run, and the
+       signature, "`gpg --verify $file - <$signature`" is run, and the
        program is expected to signal a good signature by exiting with
        code 0, and to generate an ASCII-armored detached signature, the
-       standard input of "gpg -bsau $key" is fed with the contents to be
+       standard input of "`gpg -bsau $key`" is fed with the contents to be
        signed, and the program is expected to send the result to its
        standard output.
 
@@ -1494,7 +1533,7 @@ gui.diffContext::
        made by the linkgit:git-gui[1]. The default is "5".
 
 gui.displayUntracked::
-       Determines if linkgit::git-gui[1] shows untracked files
+       Determines if linkgit:git-gui[1] shows untracked files
        in the file list. The default is "true".
 
 gui.encoding::
@@ -1662,11 +1701,12 @@ http.extraHeader::
        config, an empty value will reset the extra headers to the empty list.
 
 http.cookieFile::
-       File containing previously stored cookie lines which should be used
+       The pathname of a file containing previously stored cookie lines,
+       which should be used
        in the Git http session, if they match the server. The file format
        of the file to read cookies from should be plain HTTP headers or
-       the Netscape/Mozilla cookie file format (see linkgit:curl[1]).
-       NOTE that the file specified with http.cookieFile is only used as
+       the Netscape/Mozilla cookie file format (see `curl(1)`).
+       NOTE that the file specified with http.cookieFile is used only as
        input unless http.saveCookies is set.
 
 http.saveCookies::
index 4b0318e2ac159eb56ff4fbafe2f3c2ebfa374392..3cb301556eb2c6571298020cdea0374eb771638f 100644 (file)
@@ -271,7 +271,7 @@ For example, `--word-diff-regex=.` will treat each character as a word
 and, correspondingly, show differences character by character.
 +
 The regex can also be set via a diff driver or configuration option, see
-linkgit:gitattributes[1] or linkgit:git-config[1].  Giving it explicitly
+linkgit:gitattributes[5] or linkgit:git-config[1].  Giving it explicitly
 overrides any diff driver or configuration setting.  Diff drivers
 override configuration settings.
 
index c5047d8f9be9db6c776cca34361732e6c286d2fc..ae555bd47e123544c03a7480ed8000a73610029f 100644 (file)
@@ -1,7 +1,7 @@
 Everyday Git With 20 Commands Or So
 ===================================
 
-This document has been moved to linkgit:giteveryday[1].
+This document has been moved to linkgit:giteveryday[7].
 
 Please let the owners of the referring site know so that they can update the
 link you clicked to get here.
index e94367a5ed8e8b94bca7036ff7616bc3cfa9ca97..611754f10bb86d62edcb3265cc29ccdc97c259ae 100644 (file)
@@ -112,7 +112,7 @@ EXIT STATUS
 SEE ALSO
 --------
 linkgit:gitignore[5]
-linkgit:gitconfig[5]
+linkgit:git-config[1]
 linkgit:git-ls-files[1]
 
 GIT
index 9ec6b3cc17fd776b16f6410cf88c708831d4bca3..d474226eb79b45604884587f400b072c08f4ea2d 100644 (file)
@@ -290,7 +290,8 @@ configuration variable documented in linkgit:git-config[1].
        what changes the commit has.
        Note that this diff output doesn't have its
        lines prefixed with '#'. This diff will not be a part
-       of the commit message.
+       of the commit message. See the `commit.verbose` configuration
+       variable in linkgit:git-config[1].
 +
 If specified twice, show in addition the unified diff between
 what would be committed and the worktree files, i.e. the unstaged
index 73fd9e8230a89a988bdf16497eb3686ab18dfb91..003731f6a990559fc4f0c785687a639bae218468 100644 (file)
@@ -205,7 +205,7 @@ to other tags will be rewritten to point to the underlying commit.
 Remap to ancestor
 ~~~~~~~~~~~~~~~~~
 
-By using linkgit:rev-list[1] arguments, e.g., path limiters, you can limit the
+By using linkgit:git-rev-list[1] arguments, e.g., path limiters, you can limit the
 set of revisions which get rewritten. However, positive refs on the command
 line are distinguished: we don't let them be excluded by such limiters. For
 this purpose, they are instead rewritten to point at the nearest ancestor that
index c52578bb87ccfec1cf37defd4c3be4afbb16259b..d9d406dcfb7d6bc9f26799635b7333bae7f7fd43 100644 (file)
@@ -179,7 +179,7 @@ returns an empty string instead.
 
 As a special case for the date-type fields, you may specify a format for
 the date by adding `:` followed by date format name (see the
-values the `--date` option to linkgit::git-rev-list[1] takes).
+values the `--date` option to linkgit:git-rev-list[1] takes).
 
 
 EXAMPLES
index 6821441d7d7beedac1f5b322248c3012d23bb103..bdeecd59e002b074f32960db2d19d745e21539c4 100644 (file)
@@ -265,6 +265,11 @@ you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.
   Output an all-zero hash in each patch's From header instead
   of the hash of the commit.
 
+--base=<commit>::
+       Record the base tree information to identify the state the
+       patch series applies to.  See the BASE TREE INFORMATION section
+       below for details.
+
 --root::
        Treat the revision argument as a <revision range>, even if it
        is just a single commit (that would normally be treated as a
@@ -520,6 +525,61 @@ This should help you to submit patches inline using KMail.
 5. Back in the compose window: add whatever other text you wish to the
    message, complete the addressing and subject fields, and press send.
 
+BASE TREE INFORMATION
+---------------------
+
+The base tree information block is used for maintainers or third party
+testers to know the exact state the patch series applies to. It consists
+of the 'base commit', which is a well-known commit that is part of the
+stable part of the project history everybody else works off of, and zero
+or more 'prerequisite patches', which are well-known patches in flight
+that is not yet part of the 'base commit' that need to be applied on top
+of 'base commit' in topological order before the patches can be applied.
+
+The 'base commit' is shown as "base-commit: " followed by the 40-hex of
+the commit object name.  A 'prerequisite patch' is shown as
+"prerequisite-patch-id: " followed by the 40-hex 'patch id', which can
+be obtained by passing the patch through the `git patch-id --stable`
+command.
+
+Imagine that on top of the public commit P, you applied well-known
+patches X, Y and Z from somebody else, and then built your three-patch
+series A, B, C, the history would be like:
+
+................................................
+---P---X---Y---Z---A---B---C
+................................................
+
+With `git format-patch --base=P -3 C` (or variants thereof, e.g. with
+`--cover-letter` of using `Z..C` instead of `-3 C` to specify the
+range), the base tree information block is shown at the end of the
+first message the command outputs (either the first patch, or the
+cover letter), like this:
+
+------------
+base-commit: P
+prerequisite-patch-id: X
+prerequisite-patch-id: Y
+prerequisite-patch-id: Z
+------------
+
+For non-linear topology, such as
+
+................................................
+---P---X---A---M---C
+    \         /
+     Y---Z---B
+................................................
+
+You can also use `git format-patch --base=P -3 C` to generate patches
+for A, B and C, and the identifiers for P, X, Y, Z are appended at the
+end of the first message.
+
+If set `--base=auto` in cmdline, it will track base commit automatically,
+the base commit will be the merge base of tip commit of the remote-tracking
+branch and revision-range specified in cmdline.
+For a local branch, you need to track a remote branch by `git branch
+--set-upstream-to` before using this option.
 
 EXAMPLES
 --------
index 8174d27efdc13b3f611b4aedda70086f39867d73..6364e5dc45b16dd00737321848b792388af7233a 100644 (file)
@@ -130,7 +130,12 @@ The template directory will be one of the following (in order):
  - the default template directory: `/usr/share/git-core/templates`.
 
 The default template directory includes some directory structure, suggested
-"exclude patterns" (see linkgit:gitignore[5]), and sample hook files (see linkgit:githooks[5]).
+"exclude patterns" (see linkgit:gitignore[5]), and sample hook files.
+
+The sample hooks are all disabled by default, To enable one of the
+sample hooks rename it by removing its `.sample` suffix.
+
+See linkgit:githooks[5] for more general info on hook execution.
 
 EXAMPLES
 --------
index 0947084140f217a4e7fe1d6fa203e4d7e9a36ffe..3bbc731f67e50bdd65dd361cd9ca249ff706728f 100644 (file)
@@ -85,7 +85,7 @@ with comments and suggestions on the message you are responding to, and to
 conclude it with a patch submission, separating the discussion and the
 beginning of the proposed commit log message with a scissors line.
 +
-This can enabled by default with the configuration option mailinfo.scissors.
+This can be enabled by default with the configuration option mailinfo.scissors.
 
 --no-scissors::
        Ignore scissors lines. Useful for overriding mailinfo.scissors settings.
index 8de349968a3be4ea7e65a4a254041b0f0fb63347..9c4fd6812cc07b8537fb28a12f4119354a2dc5c8 100644 (file)
@@ -402,4 +402,4 @@ on the `notes.rewrite.<command>` and `notes.rewriteRef` settings.
 
 GIT
 ---
-Part of the linkgit:git[7] suite
+Part of the linkgit:git[1] suite
index 13adebf7b75f2ab122c4d23708493ef098d3548d..9226c4380c6c147108af9d38d7f7101c732a2095 100644 (file)
@@ -13,7 +13,7 @@ SYNOPSIS
              [--reference <repository>] [--depth <depth>] [--] <repository> [<path>]
 'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...]
 'git submodule' [--quiet] init [--] [<path>...]
-'git submodule' [--quiet] deinit [-f|--force] [--] <path>...
+'git submodule' [--quiet] deinit [-f|--force] (--all|[--] <path>...)
 'git submodule' [--quiet] update [--init] [--remote] [-N|--no-fetch]
              [-f|--force] [--rebase|--merge] [--reference <repository>]
              [--depth <depth>] [--recursive] [--jobs <n>] [--] [<path>...]
@@ -140,12 +140,15 @@ deinit::
        tree. Further calls to `git submodule update`, `git submodule foreach`
        and `git submodule sync` will skip any unregistered submodules until
        they are initialized again, so use this command if you don't want to
-       have a local checkout of the submodule in your work tree anymore. If
+       have a local checkout of the submodule in your working tree anymore. If
        you really want to remove a submodule from the repository and commit
        that use linkgit:git-rm[1] instead.
 +
-If `--force` is specified, the submodule's work tree will be removed even if
-it contains local modifications.
+When the command is run without pathspec, it errors out,
+instead of deinit-ing everything, to prevent mistakes.
++
+If `--force` is specified, the submodule's working tree will
+be removed even if it contains local modifications.
 
 update::
 +
@@ -247,6 +250,10 @@ OPTIONS
 --quiet::
        Only print error messages.
 
+--all::
+       This option is only valid for the deinit command. Unregister all
+       submodules in the working tree.
+
 -b::
 --branch::
        Branch of repository to add as submodule.
@@ -257,8 +264,8 @@ OPTIONS
 --force::
        This option is only valid for add, deinit and update commands.
        When running add, allow adding an otherwise ignored submodule path.
-       When running deinit the submodule work trees will be removed even if
-       they contain local changes.
+       When running deinit the submodule working trees will be removed even
+       if they contain local changes.
        When running update (only effective with the checkout procedure),
        throw away local changes in submodules when switching to a
        different commit; and always run a checkout operation in the
index 34ff007a98156162431d5dd03abaaffe5d0c29a0..dd6dbf7dd909bb14300706bfdc7ea83a164998d2 100644 (file)
@@ -43,11 +43,12 @@ unreleased) version of Git, that is available from the 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v2.8.2/git.html[documentation for release 2.8.2]
+* link:v2.8.3/git.html[documentation for release 2.8.3]
 
 * release notes for
-  link:RelNotes/2.8.2.txt[2.8.2].
-  link:RelNotes/2.8.1.txt[2.8.1].
+  link:RelNotes/2.8.3.txt[2.8.3],
+  link:RelNotes/2.8.2.txt[2.8.2],
+  link:RelNotes/2.8.1.txt[2.8.1],
   link:RelNotes/2.8.0.txt[2.8].
 
 * link:v2.7.3/git.html[documentation for release 2.7.3]
index a2f59b194c85f5dee4eaa530a7ab6d7533f126c6..d82e912e550ee0032c20a46c4238f7ce10003657 100644 (file)
@@ -7,24 +7,35 @@ githooks - Hooks used by Git
 
 SYNOPSIS
 --------
-$GIT_DIR/hooks/*
+$GIT_DIR/hooks/* (or \`git config core.hooksPath`/*)
 
 
 DESCRIPTION
 -----------
 
-Hooks are little scripts you can place in `$GIT_DIR/hooks`
-directory to trigger action at certain points.  When
-'git init' is run, a handful of example hooks are copied into the
-`hooks` directory of the new repository, but by default they are
-all disabled.  To enable a hook, rename it by removing its `.sample`
-suffix.
+Hooks are programs you can place in a hooks directory to trigger
+actions at certain points in git's execution. Hooks that don't have
+the executable bit set are ignored.
 
-NOTE: It is also a requirement for a given hook to be executable.
-However - in a freshly initialized repository - the `.sample` files are
-executable by default.
+By default the hooks directory is `$GIT_DIR/hooks`, but that can be
+changed via the `core.hooksPath` configuration variable (see
+linkgit:git-config[1]).
 
-This document describes the currently defined hooks.
+Before Git invokes a hook, it changes its working directory to either
+the root of the working tree in a non-bare repository, or to the
+$GIT_DIR in a bare repository.
+
+Hooks can get their arguments via the environment, command-line
+arguments, and stdin. See the documentation for each hook below for
+details.
+
+'git init' may copy hooks to the new repository, depending on its
+configuration. See the "TEMPLATE DIRECTORY" section in
+linkgit:git-init[1] for details. When the rest of this document refers
+to "default hooks" it's talking about the default template shipped
+with Git.
+
+The currently supported hooks are described below.
 
 HOOKS
 -----
@@ -32,15 +43,15 @@ HOOKS
 applypatch-msg
 ~~~~~~~~~~~~~~
 
-This hook is invoked by 'git am' script.  It takes a single
+This hook is invoked by 'git am'.  It takes a single
 parameter, the name of the file that holds the proposed commit
-log message.  Exiting with non-zero status causes
-'git am' to abort before applying the patch.
+log message.  Exiting with a non-zero status causes 'git am' to abort
+before applying the patch.
 
 The hook is allowed to edit the message file in place, and can
 be used to normalize the message into some project standard
-format (if the project has one). It can also be used to refuse
-the commit after inspecting the message file.
+format. It can also be used to refuse the commit after inspecting
+the message file.
 
 The default 'applypatch-msg' hook, when enabled, runs the
 'commit-msg' hook, if the latter is enabled.
@@ -73,10 +84,10 @@ pre-commit
 ~~~~~~~~~~
 
 This hook is invoked by 'git commit', and can be bypassed
-with `--no-verify` option.  It takes no parameter, and is
+with the `--no-verify` option.  It takes no parameters, and is
 invoked before obtaining the proposed commit log message and
-making a commit.  Exiting with non-zero status from this script
-causes the 'git commit' to abort.
+making a commit.  Exiting with non-zero status from this script
+causes the 'git commit' command to abort before creating a commit.
 
 The default 'pre-commit' hook, when enabled, catches introduction
 of lines with trailing whitespaces and aborts the commit when
@@ -115,15 +126,15 @@ commit-msg
 ~~~~~~~~~~
 
 This hook is invoked by 'git commit', and can be bypassed
-with `--no-verify` option.  It takes a single parameter, the
+with the `--no-verify` option.  It takes a single parameter, the
 name of the file that holds the proposed commit log message.
-Exiting with non-zero status causes the 'git commit' to
+Exiting with non-zero status causes the 'git commit' to
 abort.
 
-The hook is allowed to edit the message file in place, and can
-be used to normalize the message into some project standard
-format (if the project has one). It can also be used to refuse
-the commit after inspecting the message file.
+The hook is allowed to edit the message file in place, and can be used
+to normalize the message into some project standard format. It
+can also be used to refuse the commit after inspecting the message
+file.
 
 The default 'commit-msg' hook, when enabled, detects duplicate
 "Signed-off-by" lines, and aborts the commit if one is found.
@@ -131,8 +142,8 @@ The default 'commit-msg' hook, when enabled, detects duplicate
 post-commit
 ~~~~~~~~~~~
 
-This hook is invoked by 'git commit'.  It takes no
-parameter, and is invoked after a commit is made.
+This hook is invoked by 'git commit'. It takes no parameters, and is
+invoked after a commit is made.
 
 This hook is meant primarily for notification, and cannot affect
 the outcome of 'git commit'.
@@ -267,9 +278,11 @@ does not know the entire set of branches, so it would end up
 firing one e-mail per ref when used naively, though.  The
 <<post-receive,'post-receive'>> hook is more suited to that.
 
-Another use suggested on the mailing list is to use this hook to
-implement access control which is finer grained than the one
-based on filesystem group.
+In an environment that restricts the users' access only to git
+commands over the wire, this hook can be used to implement access
+control without relying on filesystem ownership and group
+membership. See linkgit:git-shell[1] for how you might use the login
+shell to restrict the user's access to only git commands.
 
 Both standard output and standard error output are forwarded to
 'git send-pack' on the other end, so you can simply `echo` messages
diff --git a/Documentation/lint-gitlink.perl b/Documentation/lint-gitlink.perl
new file mode 100755 (executable)
index 0000000..476cc30
--- /dev/null
@@ -0,0 +1,71 @@
+#!/usr/bin/perl
+
+use File::Find;
+use Getopt::Long;
+
+my $basedir = ".";
+GetOptions("basedir=s" => \$basedir)
+       or die("Cannot parse command line arguments\n");
+
+my $found_errors = 0;
+
+sub report {
+       my ($where, $what, $error) = @_;
+       print "$where: $error: $what\n";
+       $found_errors = 1;
+}
+
+sub grab_section {
+       my ($page) = @_;
+       open my $fh, "<", "$basedir/$page.txt";
+       my $firstline = <$fh>;
+       chomp $firstline;
+       close $fh;
+       my ($section) = ($firstline =~ /.*\((\d)\)$/);
+       return $section;
+}
+
+sub lint {
+       my ($file) = @_;
+       open my $fh, "<", $file
+               or return;
+       while (<$fh>) {
+               my $where = "$file:$.";
+               while (s/linkgit:((.*?)\[(\d)\])//) {
+                       my ($target, $page, $section) = ($1, $2, $3);
+
+                       # De-AsciiDoc
+                       $page =~ s/{litdd}/--/g;
+
+                       if ($page !~ /^git/) {
+                               report($where, $target, "nongit link");
+                               next;
+                       }
+                       if (! -f "$basedir/$page.txt") {
+                               report($where, $target, "no such source");
+                               next;
+                       }
+                       $real_section = grab_section($page);
+                       if ($real_section != $section) {
+                               report($where, $target,
+                                       "wrong section (should be $real_section)");
+                               next;
+                       }
+               }
+       }
+       close $fh;
+}
+
+sub lint_it {
+       lint($File::Find::name) if -f && /\.txt$/;
+}
+
+if (!@ARGV) {
+       find({ wanted => \&lint_it, no_chdir => 1 }, $basedir);
+} else {
+       for (@ARGV) {
+               lint($_);
+       }
+}
+
+exit $found_errors;
index dfb43d000fa8f539a99bae771fca87abfd46e507..5b4a62e93624bc8289c835631aab5d5f13dff708 100644 (file)
@@ -89,8 +89,11 @@ option can be used to override --squash.
 
 --verify-signatures::
 --no-verify-signatures::
-       Verify that the commits being merged have good and trusted GPG signatures
-       and abort the merge in case they do not.
+       Verify that the tip commit of the side branch being merged is
+       signed with a valid key, i.e. a key that has a valid uid: in the
+       default trust model, this means the signing key has been signed by
+       a trusted key.  If the tip commit of the side branch is not signed
+       with a valid key, the merge is aborted.
 
 --summary::
 --no-summary::
index 671cebd95c36f2dc6e17fb599219e305a567bc55..29b19b992f2e652d6fc9258cbd64cad51f86c2ef 100644 (file)
@@ -143,8 +143,8 @@ ifndef::git-rev-list[]
 - '%N': commit notes
 endif::git-rev-list[]
 - '%GG': raw verification message from GPG for a signed commit
-- '%G?': show "G" for a Good signature, "B" for a Bad signature, "U" for a good,
-  untrusted signature and "N" for no signature
+- '%G?': show "G" for a good (valid) signature, "B" for a bad signature,
+  "U" for a good signature with unknown validity and "N" for no signature
 - '%GS': show the name of the signer for a signed commit
 - '%GK': show the key used to sign a signed commit
 - '%gD': reflog selector, e.g., `refs/stash@{1}`
index e44426dd041516b8d9220ce9f1b2413be0319703..75368f26ca28a4afd381c5ab180a5b90a8368c84 100644 (file)
@@ -243,7 +243,7 @@ appended to its command line, which is one of:
 The details of the credential will be provided on the helper's stdin
 stream. The exact format is the same as the input/output format of the
 `git credential` plumbing command (see the section `INPUT/OUTPUT
-FORMAT` in linkgit:git-credential[7] for a detailed specification).
+FORMAT` in linkgit:git-credential[1] for a detailed specification).
 
 For a `get` operation, the helper should produce a list of attributes
 on stdout in the same format. A helper is free to produce a subset, or
@@ -268,4 +268,4 @@ See also
 
 linkgit:gitcredentials[7]
 
-linkgit:git-config[5] (See configuration variables `credential.*`)
+linkgit:git-config[1] (See configuration variables `credential.*`)
index 695bd4bf43255e42ef26d29a5c6dff35217f220d..27bd701c0d6862f38c700fbfc02c24b135bd6c0e 100644 (file)
@@ -144,8 +144,12 @@ There are some macros to easily define options:
 
 `OPT_COUNTUP(short, long, &int_var, description)`::
        Introduce a count-up option.
-       `int_var` is incremented on each use of `--option`, and
-       reset to zero with `--no-option`.
+       Each use of `--option` increments `int_var`, starting from zero
+       (even if initially negative), and `--no-option` resets it to
+       zero. To determine if `--option` or `--no-option` was encountered at
+       all, initialize `int_var` to a negative value, and if it is still
+       negative after parse_options(), then neither `--option` nor
+       `--no-option` was seen.
 
 `OPT_BIT(short, long, &int_var, description, mask)`::
        Introduce a boolean option.
index c6977bbc5af9c9bcdd21dc26ddb93588686a38d8..8b363438021bf1ac635042b002976ad75816caed 100644 (file)
@@ -526,7 +526,7 @@ Push Certificate
 
 A push certificate begins with a set of header lines.  After the
 header and an empty line, the protocol commands follow, one per
-line. Note that the the trailing LF in push-cert PKT-LINEs is _not_
+line. Note that the trailing LF in push-cert PKT-LINEs is _not_
 optional; it must be present.
 
 Currently, the following header fields are defined:
index 3f03366bcdbec1979709a05fa6aef2b2565514e7..0d59718bf72a8f033f2d2a5be7d260b52039408e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2062,7 +2062,7 @@ XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) --language=Shell \
        --keyword=gettextln --keyword=eval_gettextln
 XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --keyword=__ --language=Perl
 LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
-LOCALIZED_SH = $(SCRIPT_SH)
+LOCALIZED_SH = $(SCRIPT_SH) git-parse-remote.sh
 LOCALIZED_PERL = $(SCRIPT_PERL)
 
 ifdef XGETTEXT_INCLUDE_TESTS
@@ -2483,6 +2483,7 @@ ALL_COMMANDS += git-gui git-citool
 
 .PHONY: check-docs
 check-docs::
+       $(MAKE) -C Documentation lint-docs
        @(for v in $(ALL_COMMANDS); \
        do \
                case "$$v" in \
index 7996c2907b0e571578f2ce18095c83c134a503b3..6d93edbcb97382269bd310d38cacd95ce3619091 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -860,8 +860,8 @@ static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
        /* Create file BISECT_ANCESTORS_OK. */
        fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
        if (fd < 0)
-               warning("could not create file '%s': %s",
-                       filename, strerror(errno));
+               warning_errno("could not create file '%s'",
+                             filename);
        else
                close(fd);
  done:
@@ -910,8 +910,7 @@ void read_bisect_terms(const char **read_bad, const char **read_good)
                        *read_good = "good";
                        return;
                } else {
-                       die("could not read file '%s': %s", filename,
-                               strerror(errno));
+                       die_errno("could not read file '%s'", filename);
                }
        } else {
                strbuf_getline_lf(&str, fp);
index 416244370783adcd7648ae7c03c7d0d8cf778b12..a5a8dcbd0ed929d09a73674361ee1dd81dd5b88c 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -334,15 +334,16 @@ void remove_branch_state(void)
        unlink(git_path_squash_msg());
 }
 
-void die_if_checked_out(const char *branch)
+void die_if_checked_out(const char *branch, int ignore_current_worktree)
 {
-       char *existing;
+       const struct worktree *wt;
 
-       existing = find_shared_symref("HEAD", branch);
-       if (existing) {
-               skip_prefix(branch, "refs/heads/", &branch);
-               die(_("'%s' is already checked out at '%s'"), branch, existing);
-       }
+       wt = find_shared_symref("HEAD", branch);
+       if (!wt || (ignore_current_worktree && wt->is_current))
+               return;
+       skip_prefix(branch, "refs/heads/", &branch);
+       die(_("'%s' is already checked out at '%s'"),
+           branch, wt->path);
 }
 
 int replace_each_worktree_head_symref(const char *oldref, const char *newref)
@@ -357,7 +358,8 @@ int replace_each_worktree_head_symref(const char *oldref, const char *newref)
                if (strcmp(oldref, worktrees[i]->head_ref))
                        continue;
 
-               if (set_worktree_head_symref(worktrees[i]->git_dir, newref)) {
+               if (set_worktree_head_symref(get_worktree_git_dir(worktrees[i]),
+                                            newref)) {
                        ret = -1;
                        error(_("HEAD of working tree %s is not updated"),
                              worktrees[i]->path);
index d69163daf793f92f3dab92e2fb9bc5217b2dfd6b..b2f964933270e1306eff1f0589c58efd22d6c76b 100644 (file)
--- a/branch.h
+++ b/branch.h
@@ -58,7 +58,7 @@ extern int read_branch_desc(struct strbuf *, const char *branch_name);
  * worktree and die (with a message describing its checkout location) if
  * it is.
  */
-extern void die_if_checked_out(const char *branch);
+extern void die_if_checked_out(const char *branch, int ignore_current_worktree);
 
 /*
  * Update all per-worktree HEADs pointing at the old ref to point the new ref.
index d003939bc5c65947451de4a6e1817eb0f184211f..3dfe70b7a039afadf2de4dfe8f809e3321287004 100644 (file)
@@ -769,15 +769,15 @@ static int split_mail_conv(mail_conv_fn fn, struct am_state *state,
                        in = fopen(*paths, "r");
 
                if (!in)
-                       return error(_("could not open '%s' for reading: %s"),
-                                       *paths, strerror(errno));
+                       return error_errno(_("could not open '%s' for reading"),
+                                          *paths);
 
                mail = mkpath("%s/%0*d", state->dir, state->prec, i + 1);
 
                out = fopen(mail, "w");
                if (!out)
-                       return error(_("could not open '%s' for writing: %s"),
-                                       mail, strerror(errno));
+                       return error_errno(_("could not open '%s' for writing"),
+                                          mail);
 
                ret = fn(out, in, keep_cr);
 
@@ -857,8 +857,7 @@ static int split_mail_stgit_series(struct am_state *state, const char **paths,
 
        fp = fopen(*paths, "r");
        if (!fp)
-               return error(_("could not open '%s' for reading: %s"), *paths,
-                               strerror(errno));
+               return error_errno(_("could not open '%s' for reading"), *paths);
 
        while (!strbuf_getline_lf(&sb, fp)) {
                if (*sb.buf == '#')
index 0adba629d2ae35a4db74fb9d1450b54bb11e51e3..2ecde53bf838777e191926557c0c8190cdc7d09a 100644 (file)
@@ -220,12 +220,12 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                name = mkpathdup(fmt, bname.buf);
 
                if (kinds == FILTER_REFS_BRANCHES) {
-                       char *worktree = find_shared_symref("HEAD", name);
-                       if (worktree) {
+                       const struct worktree *wt =
+                               find_shared_symref("HEAD", name);
+                       if (wt) {
                                error(_("Cannot delete branch '%s' "
                                        "checked out at '%s'"),
-                                     bname.buf, worktree);
-                               free(worktree);
+                                     bname.buf, wt->path);
                                ret = 1;
                                continue;
                        }
@@ -375,12 +375,14 @@ static char *get_head_description(void)
                strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
                            state.branch);
        else if (state.detached_from) {
-               /* TRANSLATORS: make sure these match _("HEAD detached at ")
-                  and _("HEAD detached from ") in wt-status.c */
                if (state.detached_at)
+                       /* TRANSLATORS: make sure this matches
+                          "HEAD detached at " in wt-status.c */
                        strbuf_addf(&desc, _("(HEAD detached at %s)"),
                                state.detached_from);
                else
+                       /* TRANSLATORS: make sure this matches
+                          "HEAD detached from " in wt-status.c */
                        strbuf_addf(&desc, _("(HEAD detached from %s)"),
                                state.detached_from);
        }
@@ -524,6 +526,29 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
        ref_array_clear(&array);
 }
 
+static void reject_rebase_or_bisect_branch(const char *target)
+{
+       struct worktree **worktrees = get_worktrees();
+       int i;
+
+       for (i = 0; worktrees[i]; i++) {
+               struct worktree *wt = worktrees[i];
+
+               if (!wt->is_detached)
+                       continue;
+
+               if (is_worktree_being_rebased(wt, target))
+                       die(_("Branch %s is being rebased at %s"),
+                           target, wt->path);
+
+               if (is_worktree_being_bisected(wt, target))
+                       die(_("Branch %s is being bisected at %s"),
+                           target, wt->path);
+       }
+
+       free_worktrees(worktrees);
+}
+
 static void rename_branch(const char *oldname, const char *newname, int force)
 {
        struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
@@ -553,6 +578,8 @@ static void rename_branch(const char *oldname, const char *newname, int force)
 
        validate_new_branchname(newname, &newref, force, clobber_head_ok);
 
+       reject_rebase_or_bisect_branch(oldref.buf);
+
        strbuf_addf(&logmsg, "Branch: renamed %s to %s",
                 oldref.buf, newref.buf);
 
@@ -593,8 +620,7 @@ static int edit_branch_description(const char *branch_name)
                    branch_name, comment_line_char);
        if (write_file_gently(git_path(edit_description), "%s", buf.buf)) {
                strbuf_release(&buf);
-               return error(_("could not write branch description template: %s"),
-                            strerror(errno));
+               return error_errno(_("could not write branch description template"));
        }
        strbuf_reset(&buf);
        if (launch_editor(git_path(edit_description), &buf, NULL)) {
@@ -630,7 +656,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                        BRANCH_TRACK_EXPLICIT),
                OPT_SET_INT( 0, "set-upstream",  &track, N_("change upstream info"),
                        BRANCH_TRACK_OVERRIDE),
-               OPT_STRING('u', "set-upstream-to", &new_upstream, "upstream", "change the upstream info"),
+               OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")),
                OPT_BOOL(0, "unset-upstream", &unset_upstream, "Unset the upstream info"),
                OPT__COLOR(&branch_use_color, N_("use colored output")),
                OPT_SET_INT('r', "remotes",     &filter.kind, N_("act on remote-tracking branches"),
@@ -838,8 +864,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                if (argc == 1 && track == BRANCH_TRACK_OVERRIDE &&
                    !branch_existed && remote_tracking) {
                        fprintf(stderr, _("\nIf you wanted to make '%s' track '%s', do this:\n\n"), head, branch->name);
-                       fprintf(stderr, _("    git branch -d %s\n"), branch->name);
-                       fprintf(stderr, _("    git branch --set-upstream-to %s\n"), branch->name);
+                       fprintf(stderr, "    git branch -d %s\n", branch->name);
+                       fprintf(stderr, "    git branch --set-upstream-to %s\n", branch->name);
                }
 
        } else
index ea2fe1cf3fc251dcab2f2d70e737a0f8f5000a21..3398c61e9a64ab686bf59fb027277f3de578b5fb 100644 (file)
@@ -1110,7 +1110,7 @@ static int checkout_branch(struct checkout_opts *opts,
                char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
                if (head_ref &&
                    (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)))
-                       die_if_checked_out(new->path);
+                       die_if_checked_out(new->path, 1);
                free(head_ref);
        }
 
index 391126e58d12f4683e131a310d94d6de4571c7e6..443ff9196d16a9b6fc2d1d8b22393b1533faeb8f 100644 (file)
@@ -114,6 +114,7 @@ static char *fixup_message, *squash_message;
 static int all, also, interactive, patch_interactive, only, amend, signoff;
 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 *sign_commit;
@@ -1515,6 +1516,11 @@ static int git_commit_config(const char *k, const char *v, void *cb)
                sign_commit = git_config_bool(k, v) ? "" : NULL;
                return 0;
        }
+       if (!strcmp(k, "commit.verbose")) {
+               int is_bool;
+               config_commit_verbose = git_config_bool_or_int(k, v, &is_bool);
+               return 0;
+       }
 
        status = git_gpg_config(k, v, NULL);
        if (status)
@@ -1661,9 +1667,13 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                if (parse_commit(current_head))
                        die(_("could not parse HEAD commit"));
        }
+       verbose = -1; /* unspecified */
        argc = parse_and_validate_options(argc, argv, builtin_commit_options,
                                          builtin_commit_usage,
                                          prefix, current_head, &s);
+       if (verbose == -1)
+               verbose = (config_commit_verbose < 0) ? 0 : config_commit_verbose;
+
        if (dry_run)
                return dry_run_commit(argc, argv, prefix, current_head, &s);
        index_file = prepare_index(argc, argv, prefix, current_head, 0);
index 8ed2eb8813a442b64c8f43ed23acbdbd1c0dd452..15c61fd8d1ef891b013404ea5e7cbe42befac7bd 100644 (file)
@@ -24,6 +24,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
        gitmodules_config();
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
        rev.abbrev = 0;
+       precompose_argv(argc, argv);
 
        argc = setup_revisions(argc, argv, &rev, NULL);
        while (1 < argc && argv[1][0] == '-') {
index d979824f9395a0cbc8ffffa9cede524589710d43..1af373d0021f56f539a1d261e908a60d92ff5b4f 100644 (file)
@@ -21,6 +21,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
        gitmodules_config();
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
        rev.abbrev = 0;
+       precompose_argv(argc, argv);
 
        argc = setup_revisions(argc, argv, &rev, NULL);
        for (i = 1; i < argc; i++) {
index 2a12b81e065bfb75f05baf6694e1a9d53129bc40..806dd7a885e9e7b87e19ca8acd2b6d0383b41d85 100644 (file)
@@ -114,6 +114,8 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
        opt->disable_stdin = 1;
        memset(&s_r_opt, 0, sizeof(s_r_opt));
        s_r_opt.tweak = diff_tree_tweak_rev;
+
+       precompose_argv(argc, argv);
        argc = setup_revisions(argc, argv, opt, &s_r_opt);
 
        while (--argc > 0) {
index 343c6b8f2522a295a6148434572e77a3a3c5a16b..b7a9405d9fbec46edf99637557d7499fb58c4d77 100644 (file)
@@ -320,6 +320,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
                gitmodules_config();
        init_diff_ui_defaults();
        git_config(git_diff_ui_config, NULL);
+       precompose_argv(argc, argv);
 
        init_revisions(&rev, prefix);
 
index f8455bde7a84e110da182d56f62ac3f89026f55c..1582ca71847fe4d79366e85cbea86f9f64f55b90 100644 (file)
@@ -607,7 +607,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
 
        fp = fopen(filename, "a");
        if (!fp)
-               return error(_("cannot open %s: %s\n"), filename, strerror(errno));
+               return error_errno(_("cannot open %s"), filename);
 
        if (raw_url)
                url = transport_anonymize_url(raw_url);
@@ -848,7 +848,7 @@ static int truncate_fetch_head(void)
        FILE *fp = fopen_for_writing(filename);
 
        if (!fp)
-               return error(_("cannot open %s: %s\n"), filename, strerror(errno));
+               return error_errno(_("cannot open %s"), filename);
        fclose(fp);
        return 0;
 }
index 3c55ce456309ee7da95eac0517f11161ab3c1f7c..88480131cf9fa85f303ede2662eabe09d848ba4e 100644 (file)
@@ -127,7 +127,7 @@ static void exec_woman_emacs(const char *path, const char *page)
                        path = "emacsclient";
                strbuf_addf(&man_page, "(woman \"%s\")", page);
                execlp(path, "emacsclient", "-e", man_page.buf, (char *)NULL);
-               warning(_("failed to exec '%s': %s"), path, strerror(errno));
+               warning_errno(_("failed to exec '%s'"), path);
        }
 }
 
@@ -148,7 +148,7 @@ static void exec_man_konqueror(const char *path, const char *page)
                        path = "kfmclient";
                strbuf_addf(&man_page, "man:%s(1)", page);
                execlp(path, filename, "newTab", man_page.buf, (char *)NULL);
-               warning(_("failed to exec '%s': %s"), path, strerror(errno));
+               warning_errno(_("failed to exec '%s'"), path);
        }
 }
 
@@ -157,7 +157,7 @@ static void exec_man_man(const char *path, const char *page)
        if (!path)
                path = "man";
        execlp(path, "man", page, (char *)NULL);
-       warning(_("failed to exec '%s': %s"), path, strerror(errno));
+       warning_errno(_("failed to exec '%s'"), path);
 }
 
 static void exec_man_cmd(const char *cmd, const char *page)
@@ -165,7 +165,7 @@ static void exec_man_cmd(const char *cmd, const char *page)
        struct strbuf shell_cmd = STRBUF_INIT;
        strbuf_addf(&shell_cmd, "%s %s", cmd, page);
        execl(SHELL_PATH, SHELL_PATH, "-c", shell_cmd.buf, (char *)NULL);
-       warning(_("failed to exec '%s': %s"), cmd, strerror(errno));
+       warning(_("failed to exec '%s'"), cmd);
 }
 
 static void add_man_viewer(const char *name)
index 2d1eb8bb8a433e6bda50e93e3001ae6d43db093f..e8c71fc1d2e44ef9bd93c37ff714eeefc53664cf 100644 (file)
@@ -1250,7 +1250,9 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha
                       nr_unresolved * sizeof(*objects));
                f = sha1fd(output_fd, curr_pack);
                fix_unresolved_deltas(f);
-               strbuf_addf(&msg, _("completed with %d local objects"),
+               strbuf_addf(&msg, Q_("completed with %d local object",
+                                    "completed with %d local objects",
+                                    nr_objects - nr_objects_initial),
                            nr_objects - nr_objects_initial);
                stop_progress_msg(&progress, msg.buf);
                strbuf_release(&msg);
index dff3fbbb437c462e51544dbf1d4781c550a14d1a..099f4f7be92c6d4371dcc8cba1322351c6840439 100644 (file)
@@ -702,6 +702,7 @@ static void add_header(const char *value)
 #define THREAD_DEEP 2
 static int thread;
 static int do_signoff;
+static int base_auto;
 static const char *signature = git_version_string;
 static const char *signature_file;
 static int config_cover_letter;
@@ -786,6 +787,10 @@ static int git_format_config(const char *var, const char *value, void *cb)
        }
        if (!strcmp(var, "format.outputdirectory"))
                return git_config_string(&config_output_directory, var, value);
+       if (!strcmp(var, "format.useautobase")) {
+               base_auto = git_config_bool(var, value);
+               return 0;
+       }
 
        return git_log_config(var, value, cb);
 }
@@ -1191,6 +1196,155 @@ static int from_callback(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
+struct base_tree_info {
+       struct object_id base_commit;
+       int nr_patch_id, alloc_patch_id;
+       struct object_id *patch_id;
+};
+
+static struct commit *get_base_commit(const char *base_commit,
+                                     struct commit **list,
+                                     int total)
+{
+       struct commit *base = NULL;
+       struct commit **rev;
+       int i = 0, rev_nr = 0;
+
+       if (base_commit && strcmp(base_commit, "auto")) {
+               base = lookup_commit_reference_by_name(base_commit);
+               if (!base)
+                       die(_("Unknown commit %s"), base_commit);
+       } else if ((base_commit && !strcmp(base_commit, "auto")) || base_auto) {
+               struct branch *curr_branch = branch_get(NULL);
+               const char *upstream = branch_get_upstream(curr_branch, NULL);
+               if (upstream) {
+                       struct commit_list *base_list;
+                       struct commit *commit;
+                       unsigned char sha1[20];
+
+                       if (get_sha1(upstream, sha1))
+                               die(_("Failed to resolve '%s' as a valid ref."), upstream);
+                       commit = lookup_commit_or_die(sha1, "upstream base");
+                       base_list = get_merge_bases_many(commit, total, list);
+                       /* There should be one and only one merge base. */
+                       if (!base_list || base_list->next)
+                               die(_("Could not find exact merge base."));
+                       base = base_list->item;
+                       free_commit_list(base_list);
+               } else {
+                       die(_("Failed to get upstream, if you want to record base commit automatically,\n"
+                             "please use git branch --set-upstream-to to track a remote branch.\n"
+                             "Or you could specify base commit by --base=<base-commit-id> manually."));
+               }
+       }
+
+       ALLOC_ARRAY(rev, total);
+       for (i = 0; i < total; i++)
+               rev[i] = list[i];
+
+       rev_nr = total;
+       /*
+        * Get merge base through pair-wise computations
+        * and store it in rev[0].
+        */
+       while (rev_nr > 1) {
+               for (i = 0; i < rev_nr / 2; i++) {
+                       struct commit_list *merge_base;
+                       merge_base = get_merge_bases(rev[2 * i], rev[2 * i + 1]);
+                       if (!merge_base || merge_base->next)
+                               die(_("Failed to find exact merge base"));
+
+                       rev[i] = merge_base->item;
+               }
+
+               if (rev_nr % 2)
+                       rev[i] = rev[2 * i];
+               rev_nr = (rev_nr + 1) / 2;
+       }
+
+       if (!in_merge_bases(base, rev[0]))
+               die(_("base commit should be the ancestor of revision list"));
+
+       for (i = 0; i < total; i++) {
+               if (base == list[i])
+                       die(_("base commit shouldn't be in revision list"));
+       }
+
+       free(rev);
+       return base;
+}
+
+static void prepare_bases(struct base_tree_info *bases,
+                         struct commit *base,
+                         struct commit **list,
+                         int total)
+{
+       struct commit *commit;
+       struct rev_info revs;
+       struct diff_options diffopt;
+       int i;
+
+       if (!base)
+               return;
+
+       diff_setup(&diffopt);
+       DIFF_OPT_SET(&diffopt, RECURSIVE);
+       diff_setup_done(&diffopt);
+
+       oidcpy(&bases->base_commit, &base->object.oid);
+
+       init_revisions(&revs, NULL);
+       revs.max_parents = 1;
+       revs.topo_order = 1;
+       for (i = 0; i < total; i++) {
+               list[i]->object.flags &= ~UNINTERESTING;
+               add_pending_object(&revs, &list[i]->object, "rev_list");
+               list[i]->util = (void *)1;
+       }
+       base->object.flags |= UNINTERESTING;
+       add_pending_object(&revs, &base->object, "base");
+
+       if (prepare_revision_walk(&revs))
+               die(_("revision walk setup failed"));
+       /*
+        * Traverse the commits list, get prerequisite patch ids
+        * and stuff them in bases structure.
+        */
+       while ((commit = get_revision(&revs)) != NULL) {
+               unsigned char sha1[20];
+               struct object_id *patch_id;
+               if (commit->util)
+                       continue;
+               if (commit_patch_id(commit, &diffopt, sha1))
+                       die(_("cannot get patch id"));
+               ALLOC_GROW(bases->patch_id, bases->nr_patch_id + 1, bases->alloc_patch_id);
+               patch_id = bases->patch_id + bases->nr_patch_id;
+               hashcpy(patch_id->hash, sha1);
+               bases->nr_patch_id++;
+       }
+}
+
+static void print_bases(struct base_tree_info *bases)
+{
+       int i;
+
+       /* Only do this once, either for the cover or for the first one */
+       if (is_null_oid(&bases->base_commit))
+               return;
+
+       /* Show the base commit */
+       printf("base-commit: %s\n", oid_to_hex(&bases->base_commit));
+
+       /* Show the prerequisite patches */
+       for (i = bases->nr_patch_id - 1; i >= 0; i--)
+               printf("prerequisite-patch-id: %s\n", oid_to_hex(&bases->patch_id[i]));
+
+       free(bases->patch_id);
+       bases->nr_patch_id = 0;
+       bases->alloc_patch_id = 0;
+       oidclr(&bases->base_commit);
+}
+
 int cmd_format_patch(int argc, const char **argv, const char *prefix)
 {
        struct commit *commit;
@@ -1215,6 +1369,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        int reroll_count = -1;
        char *branch_name = NULL;
        char *from = NULL;
+       char *base_commit = NULL;
+       struct base_tree_info bases;
+
        const struct option builtin_format_patch_options[] = {
                { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
                            N_("use [PATCH n/m] even with a single patch"),
@@ -1277,6 +1434,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                            PARSE_OPT_OPTARG, thread_callback },
                OPT_STRING(0, "signature", &signature, N_("signature"),
                            N_("add a signature")),
+               OPT_STRING(0, "base", &base_commit, N_("base-commit"),
+                          N_("add prerequisite tree info to the patch series")),
                OPT_FILENAME(0, "signature-file", &signature_file,
                                N_("add a signature from a file")),
                OPT__QUIET(&quiet, N_("don't print the patch filenames")),
@@ -1514,6 +1673,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                signature = strbuf_detach(&buf, NULL);
        }
 
+       memset(&bases, 0, sizeof(bases));
+       if (base_commit || base_auto) {
+               struct commit *base = get_base_commit(base_commit, list, nr);
+               reset_revision_walk();
+               prepare_bases(&bases, base, list, nr);
+       }
+
        if (in_reply_to || thread || cover_letter)
                rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
        if (in_reply_to) {
@@ -1527,6 +1693,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        gen_message_id(&rev, "cover");
                make_cover_letter(&rev, use_stdout,
                                  origin, nr, list, branch_name, quiet);
+               print_bases(&bases);
                total++;
                start_number--;
        }
@@ -1592,6 +1759,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                                       rev.mime_boundary);
                        else
                                print_signature();
+                       print_bases(&bases);
                }
                if (!use_stdout)
                        fclose(stdout);
index 104277acc49a08d9472523edc8337c3eeeb345cd..4859ede38adc76521baf97326b82a007a37b3893 100644 (file)
@@ -109,7 +109,7 @@ static int populate_maildir_list(struct string_list *list, const char *path)
                if ((dir = opendir(name)) == NULL) {
                        if (errno == ENOENT)
                                continue;
-                       error("cannot opendir %s (%s)", name, strerror(errno));
+                       error_errno("cannot opendir %s", name);
                        goto out;
                }
 
@@ -174,12 +174,12 @@ static int split_maildir(const char *maildir, const char *dir,
 
                f = fopen(file, "r");
                if (!f) {
-                       error("cannot open mail %s (%s)", file, strerror(errno));
+                       error_errno("cannot open mail %s", file);
                        goto out;
                }
 
                if (strbuf_getwholeline(&buf, f, '\n')) {
-                       error("cannot read mail %s (%s)", file, strerror(errno));
+                       error_errno("cannot read mail %s", file);
                        goto out;
                }
 
@@ -210,7 +210,7 @@ static int split_mbox(const char *file, const char *dir, int allow_bare,
        int file_done = 0;
 
        if (!f) {
-               error("cannot open mbox %s", file);
+               error_errno("cannot open mbox %s", file);
                goto out;
        }
 
@@ -318,7 +318,7 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix)
                }
 
                if (stat(arg, &argstat) == -1) {
-                       error("cannot stat %s (%s)", arg, strerror(errno));
+                       error_errno("cannot stat %s", arg);
                        return 1;
                }
 
index 55447053f2dde004ceffff9967b015b4a01c35d1..13e22a2f0be73ec7e521a02d1f36c414e77a8700 100644 (file)
@@ -62,8 +62,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
                usage_with_options(merge_file_usage, options);
        if (quiet) {
                if (!freopen("/dev/null", "w", stderr))
-                       return error("failed to redirect stderr to /dev/null: "
-                                    "%s", strerror(errno));
+                       return error_errno("failed to redirect stderr to /dev/null");
        }
 
        if (prefix)
@@ -95,12 +94,13 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
                FILE *f = to_stdout ? stdout : fopen(fpath, "wb");
 
                if (!f)
-                       ret = error("Could not open %s for writing", filename);
+                       ret = error_errno("Could not open %s for writing",
+                                         filename);
                else if (result.size &&
                         fwrite(result.ptr, result.size, 1, f) != 1)
-                       ret = error("Could not write to %s", filename);
+                       ret = error_errno("Could not write to %s", filename);
                else if (fclose(f))
-                       ret = error("Could not close %s", filename);
+                       ret = error_errno("Could not close %s", filename);
                free(result.ptr);
        }
 
index 6fd058de9272631a3d5135ccbbd79bf1b4ff135c..c65b59ad9a340e9b68e4d09ea8912499227fce66 100644 (file)
@@ -847,15 +847,15 @@ static int merge(int argc, const char **argv, const char *prefix)
                update_ref(msg.buf, default_notes_ref(), result_sha1, NULL,
                           0, UPDATE_REFS_DIE_ON_ERR);
        else { /* Merge has unresolved conflicts */
-               char *existing;
+               const struct worktree *wt;
                /* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
                update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_sha1, NULL,
                           0, UPDATE_REFS_DIE_ON_ERR);
                /* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
-               existing = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
-               if (existing)
+               wt = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
+               if (wt)
                        die(_("A notes merge into %s is already in-progress at %s"),
-                           default_notes_ref(), existing);
+                           default_notes_ref(), wt->path);
                if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
                        die("Failed to store link to current notes ref (%s)",
                            default_notes_ref());
index 14dccb5283c47accc6b2be6a02c4ceabc9153a3c..8f5e358e22b40ff2e162cc7037339ad357622650 100644 (file)
@@ -835,8 +835,7 @@ static void write_pack_file(void)
                         * to preserve this property.
                         */
                        if (stat(pack_tmp_name, &st) < 0) {
-                               warning("failed to stat %s: %s",
-                                       pack_tmp_name, strerror(errno));
+                               warning_errno("failed to stat %s", pack_tmp_name);
                        } else if (!last_mtime) {
                                last_mtime = st.st_mtime;
                        } else {
@@ -844,8 +843,7 @@ static void write_pack_file(void)
                                utb.actime = st.st_atime;
                                utb.modtime = --last_mtime;
                                if (utime(pack_tmp_name, &utb) < 0)
-                                       warning("failed utime() on %s: %s",
-                                               pack_tmp_name, strerror(errno));
+                                       warning_errno("failed utime() on %s", pack_tmp_name);
                        }
 
                        strbuf_addf(&tmpname, "%s-", base_name);
index 596b92fc5699b92bf2e2b9e3f53a6e9925874a66..1d7333c8a11f7aedf244c99f5ac67bea7fccaab4 100644 (file)
@@ -478,13 +478,13 @@ static void NORETURN die_no_merge_candidates(const char *repo, const char **refs
                        fprintf_ln(stderr, _("Please specify which branch you want to merge with."));
                fprintf_ln(stderr, _("See git-pull(1) for details."));
                fprintf(stderr, "\n");
-               fprintf_ln(stderr, "    git pull <remote> <branch>");
+               fprintf_ln(stderr, "    git pull %s %s", _("<remote>"), _("<branch>"));
                fprintf(stderr, "\n");
        } else if (!curr_branch->merge_nr) {
                const char *remote_name = NULL;
 
                if (for_each_remote(get_only_remote, &remote_name) || !remote_name)
-                       remote_name = "<remote>";
+                       remote_name = _("<remote>");
 
                fprintf_ln(stderr, _("There is no tracking information for the current branch."));
                if (opt_rebase)
@@ -493,12 +493,12 @@ static void NORETURN die_no_merge_candidates(const char *repo, const char **refs
                        fprintf_ln(stderr, _("Please specify which branch you want to merge with."));
                fprintf_ln(stderr, _("See git-pull(1) for details."));
                fprintf(stderr, "\n");
-               fprintf_ln(stderr, "    git pull <remote> <branch>");
+               fprintf_ln(stderr, "    git pull %s %s", _("<remote>"), _("<branch>"));
                fprintf(stderr, "\n");
-               fprintf_ln(stderr, _("If you wish to set tracking information for this branch you can do so with:\n"
-                               "\n"
-                               "    git branch --set-upstream-to=%s/<branch> %s\n"),
-                               remote_name, curr_branch->name);
+               fprintf_ln(stderr, _("If you wish to set tracking information for this branch you can do so with:"));
+               fprintf(stderr, "\n");
+               fprintf_ln(stderr, "    git branch --set-upstream-to=%s/%s %s\n",
+                               remote_name, _("<branch>"), curr_branch->name);
        } else
                fprintf_ln(stderr, _("Your configuration specifies to merge with the ref '%s'\n"
                        "from the remote, but no such ref was fetched."),
index 7457c743e8d8539c4f08df81f86c8b27c0bce392..88eb8f901367aceffa008cf33b35ca8bee848626 100644 (file)
@@ -168,7 +168,7 @@ static int command_loop(const char *child)
                size_t i;
                if (!fgets(buffer, MAXCOMMAND - 1, stdin)) {
                        if (ferror(stdin))
-                               die("Comammand input error");
+                               die("Command input error");
                        exit(0);
                }
                /* Strip end of line characters. */
index fda5c2e53d28ae84f4876ca77ac9fefebee9ada2..d33766be395ad637c4505f5ca9024a9b8331d99d 100644 (file)
@@ -1154,6 +1154,8 @@ static int show(int argc, const char **argv)
                        url_nr = states.remote->url_nr;
                }
                for (i = 0; i < url_nr; i++)
+                       /* TRANSLATORS: the colon ':' should align with
+                          the one in "  Fetch URL: %s" translation */
                        printf_ln(_("  Push  URL: %s"), url[i]);
                if (!i)
                        printf_ln(_("  Push  URL: %s"), "(no URL)");
index 8829b09d0ba5d49edbad5f37f8246123dad3fd5f..8abb0207fa8e4da05ea447ae4e58c8e54c97c35b 100644 (file)
@@ -152,7 +152,7 @@ static int check_local_mod(unsigned char *head, int index_only)
 
                if (lstat(ce->name, &st) < 0) {
                        if (errno != ENOENT && errno != ENOTDIR)
-                               warning("'%s': %s", ce->name, strerror(errno));
+                               warning_errno(_("failed to stat '%s'"), ce->name);
                        /* It already vanished from the working tree */
                        continue;
                }
@@ -314,7 +314,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
                list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode);
                if (list.entry[list.nr++].is_submodule &&
                    !is_staging_gitmodules_ok())
-                       die (_("Please, stage your changes to .gitmodules or stash them to proceed"));
+                       die (_("Please stage your changes to .gitmodules or stash them to proceed"));
        }
 
        if (pathspec.nr) {
index 3bd6883eff842ee139a3d24475401d75f65fe53e..8da263f0b0b086a74e23c2a5854bfa66623f70df 100644 (file)
@@ -9,6 +9,211 @@
 #include "submodule-config.h"
 #include "string-list.h"
 #include "run-command.h"
+#include "remote.h"
+#include "refs.h"
+#include "connect.h"
+
+static char *get_default_remote(void)
+{
+       char *dest = NULL, *ret;
+       unsigned char sha1[20];
+       struct strbuf sb = STRBUF_INIT;
+       const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, NULL);
+
+       if (!refname)
+               die(_("No such ref: %s"), "HEAD");
+
+       /* detached HEAD */
+       if (!strcmp(refname, "HEAD"))
+               return xstrdup("origin");
+
+       if (!skip_prefix(refname, "refs/heads/", &refname))
+               die(_("Expecting a full ref name, got %s"), refname);
+
+       strbuf_addf(&sb, "branch.%s.remote", refname);
+       if (git_config_get_string(sb.buf, &dest))
+               ret = xstrdup("origin");
+       else
+               ret = dest;
+
+       strbuf_release(&sb);
+       return ret;
+}
+
+static int starts_with_dot_slash(const char *str)
+{
+       return str[0] == '.' && is_dir_sep(str[1]);
+}
+
+static int starts_with_dot_dot_slash(const char *str)
+{
+       return str[0] == '.' && str[1] == '.' && is_dir_sep(str[2]);
+}
+
+/*
+ * Returns 1 if it was the last chop before ':'.
+ */
+static int chop_last_dir(char **remoteurl, int is_relative)
+{
+       char *rfind = find_last_dir_sep(*remoteurl);
+       if (rfind) {
+               *rfind = '\0';
+               return 0;
+       }
+
+       rfind = strrchr(*remoteurl, ':');
+       if (rfind) {
+               *rfind = '\0';
+               return 1;
+       }
+
+       if (is_relative || !strcmp(".", *remoteurl))
+               die(_("cannot strip one component off url '%s'"),
+                       *remoteurl);
+
+       free(*remoteurl);
+       *remoteurl = xstrdup(".");
+       return 0;
+}
+
+/*
+ * The `url` argument is the URL that navigates to the submodule origin
+ * repo. When relative, this URL is relative to the superproject origin
+ * URL repo. The `up_path` argument, if specified, is the relative
+ * path that navigates from the submodule working tree to the superproject
+ * working tree. Returns the origin URL of the submodule.
+ *
+ * Return either an absolute URL or filesystem path (if the superproject
+ * origin URL is an absolute URL or filesystem path, respectively) or a
+ * relative file system path (if the superproject origin URL is a relative
+ * file system path).
+ *
+ * When the output is a relative file system path, the path is either
+ * relative to the submodule working tree, if up_path is specified, or to
+ * the superproject working tree otherwise.
+ *
+ * NEEDSWORK: This works incorrectly on the domain and protocol part.
+ * remote_url      url              outcome          expectation
+ * http://a.com/b  ../c             http://a.com/c   as is
+ * http://a.com/b  ../../c          http://c         error out
+ * http://a.com/b  ../../../c       http:/c          error out
+ * http://a.com/b  ../../../../c    http:c           error out
+ * http://a.com/b  ../../../../../c    .:c           error out
+ * NEEDSWORK: Given how chop_last_dir() works, this function is broken
+ * when a local part has a colon in its path component, too.
+ */
+static char *relative_url(const char *remote_url,
+                               const char *url,
+                               const char *up_path)
+{
+       int is_relative = 0;
+       int colonsep = 0;
+       char *out;
+       char *remoteurl = xstrdup(remote_url);
+       struct strbuf sb = STRBUF_INIT;
+       size_t len = strlen(remoteurl);
+
+       if (is_dir_sep(remoteurl[len]))
+               remoteurl[len] = '\0';
+
+       if (!url_is_local_not_ssh(remoteurl) || is_absolute_path(remoteurl))
+               is_relative = 0;
+       else {
+               is_relative = 1;
+               /*
+                * Prepend a './' to ensure all relative
+                * remoteurls start with './' or '../'
+                */
+               if (!starts_with_dot_slash(remoteurl) &&
+                   !starts_with_dot_dot_slash(remoteurl)) {
+                       strbuf_reset(&sb);
+                       strbuf_addf(&sb, "./%s", remoteurl);
+                       free(remoteurl);
+                       remoteurl = strbuf_detach(&sb, NULL);
+               }
+       }
+       /*
+        * When the url starts with '../', remove that and the
+        * last directory in remoteurl.
+        */
+       while (url) {
+               if (starts_with_dot_dot_slash(url)) {
+                       url += 3;
+                       colonsep |= chop_last_dir(&remoteurl, is_relative);
+               } else if (starts_with_dot_slash(url))
+                       url += 2;
+               else
+                       break;
+       }
+       strbuf_reset(&sb);
+       strbuf_addf(&sb, "%s%s%s", remoteurl, colonsep ? ":" : "/", url);
+       free(remoteurl);
+
+       if (starts_with_dot_slash(sb.buf))
+               out = xstrdup(sb.buf + 2);
+       else
+               out = xstrdup(sb.buf);
+       strbuf_reset(&sb);
+
+       if (!up_path || !is_relative)
+               return out;
+
+       strbuf_addf(&sb, "%s%s", up_path, out);
+       free(out);
+       return strbuf_detach(&sb, NULL);
+}
+
+static int resolve_relative_url(int argc, const char **argv, const char *prefix)
+{
+       char *remoteurl = NULL;
+       char *remote = get_default_remote();
+       const char *up_path = NULL;
+       char *res;
+       const char *url;
+       struct strbuf sb = STRBUF_INIT;
+
+       if (argc != 2 && argc != 3)
+               die("resolve-relative-url only accepts one or two arguments");
+
+       url = argv[1];
+       strbuf_addf(&sb, "remote.%s.url", remote);
+       free(remote);
+
+       if (git_config_get_string(sb.buf, &remoteurl))
+               /* the repository is its own authoritative upstream */
+               remoteurl = xgetcwd();
+
+       if (argc == 3)
+               up_path = argv[2];
+
+       res = relative_url(remoteurl, url, up_path);
+       puts(res);
+       free(res);
+       free(remoteurl);
+       return 0;
+}
+
+static int resolve_relative_url_test(int argc, const char **argv, const char *prefix)
+{
+       char *remoteurl, *res;
+       const char *up_path, *url;
+
+       if (argc != 4)
+               die("resolve-relative-url-test only accepts three arguments: <up_path> <remoteurl> <url>");
+
+       up_path = argv[1];
+       remoteurl = xstrdup(argv[2]);
+       url = argv[3];
+
+       if (!strcmp(up_path, "(null)"))
+               up_path = NULL;
+
+       res = relative_url(remoteurl, url, up_path);
+       puts(res);
+       free(res);
+       free(remoteurl);
+       return 0;
+}
 
 struct module_list {
        const struct cache_entry **entries;
@@ -100,71 +305,142 @@ static int module_list(int argc, const char **argv, const char *prefix)
        return 0;
 }
 
-static int module_name(int argc, const char **argv, const char *prefix)
+static void init_submodule(const char *path, const char *prefix, int quiet)
 {
        const struct submodule *sub;
+       struct strbuf sb = STRBUF_INIT;
+       char *upd = NULL, *url = NULL, *displaypath;
 
-       if (argc != 2)
-               usage(_("git submodule--helper name <path>"));
-
+       /* Only loads from .gitmodules, no overlay with .git/config */
        gitmodules_config();
-       sub = submodule_from_path(null_sha1, argv[1]);
+
+       if (prefix) {
+               strbuf_addf(&sb, "%s%s", prefix, path);
+               displaypath = strbuf_detach(&sb, NULL);
+       } else
+               displaypath = xstrdup(path);
+
+       sub = submodule_from_path(null_sha1, path);
 
        if (!sub)
-               die(_("no submodule mapping found in .gitmodules for path '%s'"),
-                   argv[1]);
+               die(_("No url found for submodule path '%s' in .gitmodules"),
+                       displaypath);
 
-       printf("%s\n", sub->name);
+       /*
+        * Copy url setting when it is not set yet.
+        * 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)) {
+               url = xstrdup(sub->url);
 
-       return 0;
+               if (!url)
+                       die(_("No url found for submodule path '%s' in .gitmodules"),
+                               displaypath);
+
+               /* Possibly a url relative to parent */
+               if (starts_with_dot_dot_slash(url) ||
+                   starts_with_dot_slash(url)) {
+                       char *remoteurl, *relurl;
+                       char *remote = get_default_remote();
+                       struct strbuf remotesb = STRBUF_INIT;
+                       strbuf_addf(&remotesb, "remote.%s.url", remote);
+                       free(remote);
+
+                       if (git_config_get_string(remotesb.buf, &remoteurl))
+                               /*
+                                * The repository is its own
+                                * authoritative upstream
+                                */
+                               remoteurl = xgetcwd();
+                       relurl = relative_url(remoteurl, url, NULL);
+                       strbuf_release(&remotesb);
+                       free(remoteurl);
+                       free(url);
+                       url = relurl;
+               }
+
+               if (git_config_set_gently(sb.buf, url))
+                       die(_("Failed to register url for submodule path '%s'"),
+                           displaypath);
+               if (!quiet)
+                       fprintf(stderr,
+                               _("Submodule '%s' (%s) registered for path '%s'\n"),
+                               sub->name, url, displaypath);
+       }
+
+       /* 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) {
+               if (sub->update_strategy.type == SM_UPDATE_COMMAND) {
+                       fprintf(stderr, _("warning: command update mode suggested for submodule '%s'\n"),
+                               sub->name);
+                       upd = xstrdup("none");
+               } else
+                       upd = xstrdup(submodule_strategy_to_string(&sub->update_strategy));
+
+               if (git_config_set_gently(sb.buf, upd))
+                       die(_("Failed to register update mode for submodule path '%s'"), displaypath);
+       }
+       strbuf_release(&sb);
+       free(displaypath);
+       free(url);
+       free(upd);
 }
 
-/*
- * Rules to sanitize configuration variables that are Ok to be passed into
- * submodule operations from the parent project using "-c". Should only
- * include keys which are both (a) safe and (b) necessary for proper
- * operation.
- */
-static int submodule_config_ok(const char *var)
+static int module_init(int argc, const char **argv, const char *prefix)
 {
-       if (starts_with(var, "credential."))
+       struct pathspec pathspec;
+       struct module_list list = MODULE_LIST_INIT;
+       int quiet = 0;
+       int i;
+
+       struct option module_init_options[] = {
+               OPT_STRING(0, "prefix", &prefix,
+                          N_("path"),
+                          N_("alternative anchor for relative paths")),
+               OPT__QUIET(&quiet, N_("Suppress output for initializing a submodule")),
+               OPT_END()
+       };
+
+       const char *const git_submodule_helper_usage[] = {
+               N_("git submodule--helper init [<path>]"),
+               NULL
+       };
+
+       argc = parse_options(argc, argv, prefix, module_init_options,
+                            git_submodule_helper_usage, 0);
+
+       if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
                return 1;
+
+       for (i = 0; i < list.nr; i++)
+               init_submodule(list.entries[i]->name, prefix, quiet);
+
        return 0;
 }
 
-static int sanitize_submodule_config(const char *var, const char *value, void *data)
+static int module_name(int argc, const char **argv, const char *prefix)
 {
-       struct strbuf *out = data;
+       const struct submodule *sub;
 
-       if (submodule_config_ok(var)) {
-               if (out->len)
-                       strbuf_addch(out, ' ');
+       if (argc != 2)
+               usage(_("git submodule--helper name <path>"));
 
-               if (value)
-                       sq_quotef(out, "%s=%s", var, value);
-               else
-                       sq_quote_buf(out, var);
-       }
+       gitmodules_config();
+       sub = submodule_from_path(null_sha1, argv[1]);
 
-       return 0;
-}
+       if (!sub)
+               die(_("no submodule mapping found in .gitmodules for path '%s'"),
+                   argv[1]);
 
-static void prepare_submodule_repo_env(struct argv_array *out)
-{
-       const char * const *var;
-
-       for (var = local_repo_env; *var; var++) {
-               if (!strcmp(*var, CONFIG_DATA_ENVIRONMENT)) {
-                       struct strbuf sanitized_config = STRBUF_INIT;
-                       git_config_from_parameters(sanitize_submodule_config,
-                                                  &sanitized_config);
-                       argv_array_pushf(out, "%s=%s", *var, sanitized_config.buf);
-                       strbuf_release(&sanitized_config);
-               } else {
-                       argv_array_push(out, *var);
-               }
-       }
+       printf("%s\n", sub->name);
 
+       return 0;
 }
 
 static int clone_submodule(const char *path, const char *gitdir, const char *url,
@@ -294,22 +570,6 @@ static int module_clone(int argc, const char **argv, const char *prefix)
        return 0;
 }
 
-static int module_sanitize_config(int argc, const char **argv, const char *prefix)
-{
-       struct strbuf sanitized_config = STRBUF_INIT;
-
-       if (argc > 1)
-               usage(_("git submodule--helper sanitize-config"));
-
-       git_config_from_parameters(sanitize_submodule_config, &sanitized_config);
-       if (sanitized_config.len)
-               printf("%s\n", sanitized_config.buf);
-
-       strbuf_release(&sanitized_config);
-
-       return 0;
-}
-
 struct submodule_update_clone {
        /* index into 'list', the list of submodules to look into for cloning */
        int current;
@@ -336,6 +596,25 @@ struct submodule_update_clone {
        SUBMODULE_UPDATE_STRATEGY_INIT, 0, NULL, NULL, NULL, NULL, \
        STRING_LIST_INIT_DUP, 0}
 
+
+static void next_submodule_warn_missing(struct submodule_update_clone *suc,
+               struct strbuf *out, const char *displaypath)
+{
+       /*
+        * Only mention uninitialized submodules when their
+        * paths have been specified.
+        */
+       if (suc->warn_if_uninitialized) {
+               strbuf_addf(out,
+                       _("Submodule path '%s' not initialized"),
+                       displaypath);
+               strbuf_addch(out, '\n');
+               strbuf_addstr(out,
+                       _("Maybe you want to use 'update --init'?"));
+               strbuf_addch(out, '\n');
+       }
+}
+
 /**
  * Determine whether 'ce' needs to be cloned. If so, prepare the 'child' to
  * run the clone. Returns 1 if 'ce' needs to be cloned, 0 otherwise.
@@ -370,6 +649,11 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
        else
                displaypath = ce->name;
 
+       if (!sub) {
+               next_submodule_warn_missing(suc, out, displaypath);
+               goto cleanup;
+       }
+
        if (suc->update.type == SM_UPDATE_NONE
            || (suc->update.type == SM_UPDATE_UNSPECIFIED
                && sub->update_strategy.type == SM_UPDATE_NONE)) {
@@ -387,19 +671,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
        strbuf_addf(&sb, "submodule.%s.url", sub->name);
        git_config_get_string(sb.buf, &url);
        if (!url) {
-               /*
-                * Only mention uninitialized submodules when their
-                * path have been specified
-                */
-               if (suc->warn_if_uninitialized) {
-                       strbuf_addf(out,
-                               _("Submodule path '%s' not initialized"),
-                               displaypath);
-                       strbuf_addch(out, '\n');
-                       strbuf_addstr(out,
-                               _("Maybe you want to use 'update --init'?"));
-                       strbuf_addch(out, '\n');
-               }
+               next_submodule_warn_missing(suc, out, displaypath);
                goto cleanup;
        }
 
@@ -570,8 +842,10 @@ static struct cmd_struct commands[] = {
        {"list", module_list},
        {"name", module_name},
        {"clone", module_clone},
-       {"sanitize-config", module_sanitize_config},
-       {"update-clone", update_clone}
+       {"update-clone", update_clone},
+       {"resolve-relative-url", resolve_relative_url},
+       {"resolve-relative-url-test", resolve_relative_url_test},
+       {"init", module_init}
 };
 
 int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
index 1c94ca59bfcd756c94d86ef46592528e8fbfdcb1..b8b8522249337703eb66b245ad263e7dc1b41582 100644 (file)
@@ -255,7 +255,7 @@ static int process_lstat_error(const char *path, int err)
 {
        if (err == ENOENT || err == ENOTDIR)
                return remove_one_path(path);
-       return error("lstat(\"%s\"): %s", path, strerror(errno));
+       return error("lstat(\"%s\"): %s", path, strerror(err));
 }
 
 static int add_one_path(const struct cache_entry *old, const char *path, int len, struct stat *st)
index dbfe14f3fec3f8122e4855727bd7d1c3e4b3073e..2caedf184959e2120d8e30a02f885925ceb412b4 100644 (file)
@@ -104,8 +104,7 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix)
                pfd[1].events = POLLIN;
                if (poll(pfd, 2, -1) < 0) {
                        if (errno != EINTR) {
-                               error("poll failed resuming: %s",
-                                     strerror(errno));
+                               error_errno("poll failed resuming");
                                sleep(1);
                        }
                        continue;
index d8e3795dc44a0dc047cac77af92282361227111a..96a2834a18be8ee03427cfdd408914ea71d59243 100644 (file)
@@ -110,7 +110,7 @@ static void prune_worktrees(void)
                if (ret < 0 && errno == ENOTDIR)
                        ret = unlink(path.buf);
                if (ret)
-                       error(_("failed to remove: %s"), strerror(errno));
+                       error_errno(_("failed to remove '%s'"), path.buf);
        }
        closedir(dir);
        if (!show_only)
@@ -205,7 +205,7 @@ static int add_worktree(const char *path, const char *refname,
        if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
                 ref_exists(symref.buf)) { /* it's a branch */
                if (!opts->force)
-                       die_if_checked_out(symref.buf);
+                       die_if_checked_out(symref.buf, 0);
        } else { /* must be a commit */
                commit = lookup_commit_reference_by_name(refname);
                if (!commit)
@@ -349,7 +349,7 @@ static int add(int ac, const char **av, const char *prefix)
                if (!opts.force &&
                    !strbuf_check_branch_ref(&symref, opts.new_branch) &&
                    ref_exists(symref.buf))
-                       die_if_checked_out(symref.buf);
+                       die_if_checked_out(symref.buf, 0);
                strbuf_release(&symref);
        }
 
diff --git a/cache.h b/cache.h
index 160f8e32e31a4ebd832f96fcdc55a6063aa30dab..6049f867113896def34306f22ac6927b8f0e8e1e 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -654,6 +654,7 @@ extern int warn_on_object_refname_ambiguity;
 extern const char *apply_default_whitespace;
 extern const char *apply_default_ignorewhitespace;
 extern const char *git_attributes_file;
+extern const char *git_hooks_path;
 extern int zlib_compression_level;
 extern int core_compression_level;
 extern int core_compression_seen;
@@ -700,6 +701,14 @@ extern int ref_paranoia;
 extern char comment_line_char;
 extern int auto_comment_line_char;
 
+/* Windows only */
+enum hide_dotfiles_type {
+       HIDE_DOTFILES_FALSE = 0,
+       HIDE_DOTFILES_TRUE,
+       HIDE_DOTFILES_DOTGITONLY
+};
+extern enum hide_dotfiles_type hide_dotfiles;
+
 enum branch_track {
        BRANCH_TRACK_UNSPECIFIED = -1,
        BRANCH_TRACK_NEVER = 0,
@@ -799,11 +808,14 @@ extern void check_repository_format(void);
  */
 extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern const char *git_common_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 
 extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
        __attribute__((format (printf, 3, 4)));
 extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
+extern void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
+       __attribute__((format (printf, 2, 3)));
 extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
 extern void strbuf_git_path_submodule(struct strbuf *sb, const char *path,
index 00d92a16631a80ff8ec4e995dafcd3e55434fad5..24b6542352a60006ac23c09a7cc17fb3aef009fa 100644 (file)
@@ -12,7 +12,7 @@ int main(int ac, char **av)
                struct stat st;
 
                if (lstat(ce->name, &st)) {
-                       error("lstat(%s): %s", ce->name, strerror(errno));
+                       error_errno("lstat(%s)", ce->name);
                        continue;
                }
 
diff --git a/ci/test-documentation.sh b/ci/test-documentation.sh
new file mode 100755 (executable)
index 0000000..579d540
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/sh
+#
+# Perform sanity checks on documentation and build it.
+#
+
+set -e
+
+make check-builtins
+make check-docs
+make doc
+
+test -s Documentation/git.html
+test -s Documentation/git.xml
+test -s Documentation/git.1
index 0e1d4b0893ce0266a6d15f880bb6ad66eb2619fb..8f2313d5022326dc244fa8eba4a8640299ebeac8 100644 (file)
@@ -1005,8 +1005,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                        struct strbuf buf = STRBUF_INIT;
 
                        if (strbuf_readlink(&buf, elem->path, st.st_size) < 0) {
-                               error("readlink(%s): %s", elem->path,
-                                     strerror(errno));
+                               error_errno("readlink(%s)", elem->path);
                                return;
                        }
                        result_size = buf.len;
index 0413d5c3cdd128ca721c75b588dc557bdbb1f949..a8218e6f0f2177e769cf3841b1c7d7d9b9ed270d 100644 (file)
@@ -286,6 +286,49 @@ int mingw_rmdir(const char *pathname)
        return ret;
 }
 
+static inline int needs_hiding(const char *path)
+{
+       const char *basename;
+
+       if (hide_dotfiles == HIDE_DOTFILES_FALSE)
+               return 0;
+
+       /* We cannot use basename(), as it would remove trailing slashes */
+       mingw_skip_dos_drive_prefix((char **)&path);
+       if (!*path)
+               return 0;
+
+       for (basename = path; *path; path++)
+               if (is_dir_sep(*path)) {
+                       do {
+                               path++;
+                       } while (is_dir_sep(*path));
+                       /* ignore trailing slashes */
+                       if (*path)
+                               basename = path;
+               }
+
+       if (hide_dotfiles == HIDE_DOTFILES_TRUE)
+               return *basename == '.';
+
+       assert(hide_dotfiles == HIDE_DOTFILES_DOTGITONLY);
+       return !strncasecmp(".git", basename, 4) &&
+               (!basename[4] || is_dir_sep(basename[4]));
+}
+
+static int set_hidden_flag(const wchar_t *path, int set)
+{
+       DWORD original = GetFileAttributesW(path), modified;
+       if (set)
+               modified = original | FILE_ATTRIBUTE_HIDDEN;
+       else
+               modified = original & ~FILE_ATTRIBUTE_HIDDEN;
+       if (original == modified || SetFileAttributesW(path, modified))
+               return 0;
+       errno = err_win_to_posix(GetLastError());
+       return -1;
+}
+
 int mingw_mkdir(const char *path, int mode)
 {
        int ret;
@@ -293,6 +336,8 @@ int mingw_mkdir(const char *path, int mode)
        if (xutftowcs_path(wpath, path) < 0)
                return -1;
        ret = _wmkdir(wpath);
+       if (!ret && needs_hiding(path))
+               return set_hidden_flag(wpath, 1);
        return ret;
 }
 
@@ -319,6 +364,21 @@ int mingw_open (const char *filename, int oflags, ...)
                if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
                        errno = EISDIR;
        }
+       if ((oflags & O_CREAT) && needs_hiding(filename)) {
+               /*
+                * Internally, _wopen() uses the CreateFile() API which errors
+                * out with an ERROR_ACCESS_DENIED if CREATE_ALWAYS was
+                * specified and an already existing file's attributes do not
+                * match *exactly*. As there is no mode or flag we can set that
+                * would correspond to FILE_ATTRIBUTE_HIDDEN, let's just try
+                * again *without* the O_CREAT flag (that corresponds to the
+                * CREATE_ALWAYS flag of CreateFile()).
+                */
+               if (fd < 0 && errno == EACCES)
+                       fd = _wopen(wfilename, oflags & ~O_CREAT, mode);
+               if (fd >= 0 && set_hidden_flag(wfilename, 1))
+                       warning("could not mark '%s' as hidden.", filename);
+       }
        return fd;
 }
 
@@ -350,6 +410,7 @@ int mingw_fgetc(FILE *stream)
 #undef fopen
 FILE *mingw_fopen (const char *filename, const char *otype)
 {
+       int hide = needs_hiding(filename);
        FILE *file;
        wchar_t wfilename[MAX_PATH], wotype[4];
        if (filename && !strcmp(filename, "/dev/null"))
@@ -357,12 +418,19 @@ FILE *mingw_fopen (const char *filename, const char *otype)
        if (xutftowcs_path(wfilename, filename) < 0 ||
                xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
                return NULL;
+       if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
+               error("could not unhide %s", filename);
+               return NULL;
+       }
        file = _wfopen(wfilename, wotype);
+       if (file && hide && set_hidden_flag(wfilename, 1))
+               warning("could not mark '%s' as hidden.", filename);
        return file;
 }
 
 FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
 {
+       int hide = needs_hiding(filename);
        FILE *file;
        wchar_t wfilename[MAX_PATH], wotype[4];
        if (filename && !strcmp(filename, "/dev/null"))
@@ -370,7 +438,13 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
        if (xutftowcs_path(wfilename, filename) < 0 ||
                xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
                return NULL;
+       if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
+               error("could not unhide %s", filename);
+               return NULL;
+       }
        file = _wfreopen(wfilename, wotype, stream);
+       if (file && hide && set_hidden_flag(wfilename, 1))
+               warning("could not mark '%s' as hidden.", filename);
        return file;
 }
 
index 1de70ffd62a63070ca79d27ad0b323dfbcacb5aa..69bb43dc35d4c28724f007d9684715ec40421542 100644 (file)
@@ -142,6 +142,7 @@ static inline int fcntl(int fd, int cmd, ...)
 #define sigemptyset(x) (void)0
 static inline int sigaddset(sigset_t *set, int signum)
 { return 0; }
+#define SIG_BLOCK 0
 #define SIG_UNBLOCK 0
 static inline int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
 { return 0; }
@@ -416,9 +417,6 @@ int mingw_offset_1st_component(const char *path);
 void mingw_open_html(const char *path);
 #define open_html mingw_open_html
 
-void mingw_mark_as_git_dir(const char *dir);
-#define mark_as_git_dir mingw_mark_as_git_dir
-
 /**
  * Converts UTF-8 encoded string to UTF-16LE.
  *
index dfbe6d84081c8b8eea50450a4c23c9a80b3b57a7..4293b53b171f5002127d7a354309d343706c0272 100644 (file)
@@ -147,7 +147,7 @@ struct dirent_prec_psx *precompose_utf8_readdir(PREC_DIR *prec_dir)
                                if (errno || inleft) {
                                        /*
                                         * iconv() failed and errno could be E2BIG, EILSEQ, EINVAL, EBADF
-                                        * MacOS X avoids illegal byte sequemces.
+                                        * MacOS X avoids illegal byte sequences.
                                         * If they occur on a mounted drive (e.g. NFS) it is not worth to
                                         * die() for that, but rather let the user see the original name
                                        */
index b6ed9e74621fe0bc506beb893e477bff26e88756..1c164088fbb64d2f0143f536f3186f481d876d28 100644 (file)
@@ -104,4 +104,11 @@ static inline void *pthread_getspecific(pthread_key_t key)
        return TlsGetValue(key);
 }
 
+#ifndef __MINGW64_VERSION_MAJOR
+static inline int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
+{
+       return 0;
+}
+#endif
+
 #endif /* PTHREAD_H */
index b905aea31bf53dc0f614d5e95c0dbd382818cbc4..6c7c9b60538d932d6dbc2d32774a6f64f8687c69 100644 (file)
@@ -28,13 +28,13 @@ void syslog(int priority, const char *fmt, ...)
        va_end(ap);
 
        if (str_len < 0) {
-               warning("vsnprintf failed: '%s'", strerror(errno));
+               warning_errno("vsnprintf failed");
                return;
        }
 
        str = malloc(st_add(str_len, 1));
        if (!str) {
-               warning("malloc failed: '%s'", strerror(errno));
+               warning_errno("malloc failed");
                return;
        }
 
@@ -45,7 +45,7 @@ void syslog(int priority, const char *fmt, ...)
        while ((pos = strstr(str, "%1")) != NULL) {
                str = realloc(str, st_add(++str_len, 1));
                if (!str) {
-                       warning("realloc failed: '%s'", strerror(errno));
+                       warning_errno("realloc failed");
                        return;
                }
                memmove(pos + 2, pos + 1, strlen(pos));
index 262d8d74896819ec90fc017e5e12a68c1b48b2f4..f51c56bf92d384ace98e580c510842bd7eab9871 100644 (file)
--- a/config.c
+++ b/config.c
@@ -717,6 +717,9 @@ static int git_default_core_config(const char *var, const char *value)
        if (!strcmp(var, "core.attributesfile"))
                return git_config_pathname(&git_attributes_file, var, value);
 
+       if (!strcmp(var, "core.hookspath"))
+               return git_config_pathname(&git_hooks_path, var, value);
+
        if (!strcmp(var, "core.bare")) {
                is_bare_repository_cfg = git_config_bool(var, value);
                return 0;
@@ -803,8 +806,6 @@ static int git_default_core_config(const char *var, const char *value)
 
        if (!strcmp(var, "core.autocrlf")) {
                if (value && !strcasecmp(value, "input")) {
-                       if (core_eol == EOL_CRLF)
-                               return error("core.autocrlf=input conflicts with core.eol=crlf");
                        auto_crlf = AUTO_CRLF_INPUT;
                        return 0;
                }
@@ -830,8 +831,6 @@ static int git_default_core_config(const char *var, const char *value)
                        core_eol = EOL_NATIVE;
                else
                        core_eol = EOL_UNSET;
-               if (core_eol == EOL_CRLF && auto_crlf == AUTO_CRLF_INPUT)
-                       return error("core.autocrlf=input conflicts with core.eol=crlf");
                return 0;
        }
 
@@ -912,6 +911,14 @@ static int git_default_core_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.hidedotfiles")) {
+               if (value && !strcasecmp(value, "dotgitonly"))
+                       hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
+               else
+                       hide_dotfiles = git_config_bool(var, value);
+               return 0;
+       }
+
        /* Add other config variables here and to Documentation/config.txt. */
        return 0;
 }
@@ -2009,7 +2016,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
        lock = xcalloc(1, sizeof(struct lock_file));
        fd = hold_lock_file_for_update(lock, config_filename, 0);
        if (fd < 0) {
-               error("could not lock config file %s: %s", config_filename, strerror(errno));
+               error_errno("could not lock config file %s", config_filename);
                free(store.key);
                ret = CONFIG_NO_LOCK;
                goto out_free;
@@ -2023,8 +2030,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                free(store.key);
 
                if ( ENOENT != errno ) {
-                       error("opening %s: %s", config_filename,
-                             strerror(errno));
+                       error_errno("opening %s", config_filename);
                        ret = CONFIG_INVALID_FILE; /* same as "invalid config file" */
                        goto out_free;
                }
@@ -2108,8 +2114,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                if (contents == MAP_FAILED) {
                        if (errno == ENODEV && S_ISDIR(st.st_mode))
                                errno = EISDIR;
-                       error("unable to mmap '%s': %s",
-                             config_filename, strerror(errno));
+                       error_errno("unable to mmap '%s'", config_filename);
                        ret = CONFIG_INVALID_FILE;
                        contents = NULL;
                        goto out_free;
@@ -2118,8 +2123,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                in_fd = -1;
 
                if (chmod(get_lock_file_path(lock), st.st_mode & 07777) < 0) {
-                       error("chmod on %s failed: %s",
-                             get_lock_file_path(lock), strerror(errno));
+                       error_errno("chmod on %s failed", get_lock_file_path(lock));
                        ret = CONFIG_NO_WRITE;
                        goto out_free;
                }
@@ -2175,8 +2179,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
        }
 
        if (commit_lock_file(lock) < 0) {
-               error("could not write config file %s: %s", config_filename,
-                     strerror(errno));
+               error_errno("could not write config file %s", config_filename);
                ret = CONFIG_NO_WRITE;
                lock = NULL;
                goto out_free;
@@ -2327,8 +2330,8 @@ int git_config_rename_section_in_file(const char *config_filename,
        fstat(fileno(config_file), &st);
 
        if (chmod(get_lock_file_path(lock), st.st_mode & 07777) < 0) {
-               ret = error("chmod on %s failed: %s",
-                           get_lock_file_path(lock), strerror(errno));
+               ret = error_errno("chmod on %s failed",
+                                 get_lock_file_path(lock));
                goto out;
        }
 
@@ -2382,8 +2385,8 @@ int git_config_rename_section_in_file(const char *config_filename,
        fclose(config_file);
 unlock_and_out:
        if (commit_lock_file(lock) < 0)
-               ret = error("could not write config file %s: %s",
-                           config_filename, strerror(errno));
+               ret = error_errno("could not write config file %s",
+                                 config_filename);
 out:
        free(filename_buf);
        return ret;
index 299c56090bc38b1572b0ac03ec48532f40de605f..bf1b12e7ecaf476304811cabc870d58fc7f3f1dd 100644 (file)
@@ -86,17 +86,14 @@ static int check_everything_connected_real(sha1_iterate_fn fn,
                memcpy(commit, sha1_to_hex(sha1), 40);
                if (write_in_full(rev_list.in, commit, 41) < 0) {
                        if (errno != EPIPE && errno != EINVAL)
-                               error(_("failed write to rev-list: %s"),
-                                     strerror(errno));
+                               error_errno(_("failed write to rev-list"));
                        err = -1;
                        break;
                }
        } while (!fn(cb_data, sha1));
 
-       if (close(rev_list.in)) {
-               error(_("failed to close rev-list's stdin: %s"), strerror(errno));
-               err = -1;
-       }
+       if (close(rev_list.in))
+               err = error_errno(_("failed to close rev-list's stdin"));
 
        sigchain_pop(SIGPIPE);
        return finish_command(&rev_list) || err;
index f524b8d7f4daf70a610a1e00af2ae6f1c23f8f62..b1614bf7ff0d38325baa427da09859fb4e36e1c9 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -1380,27 +1380,22 @@ static struct stream_filter *ident_filter(const unsigned char *sha1)
 struct stream_filter *get_stream_filter(const char *path, const unsigned char *sha1)
 {
        struct conv_attrs ca;
-       enum crlf_action crlf_action;
        struct stream_filter *filter = NULL;
 
        convert_attrs(&ca, path);
-
        if (ca.drv && (ca.drv->smudge || ca.drv->clean))
-               return filter;
+               return NULL;
+
+       if (ca.crlf_action == CRLF_AUTO || ca.crlf_action == CRLF_AUTO_CRLF)
+               return NULL;
 
        if (ca.ident)
                filter = ident_filter(sha1);
 
-       crlf_action = ca.crlf_action;
-
-       if ((crlf_action == CRLF_BINARY) ||
-                       crlf_action == CRLF_AUTO_INPUT ||
-                       (crlf_action == CRLF_TEXT_INPUT))
-               filter = cascade_filter(filter, &null_filter_singleton);
-
-       else if (output_eol(crlf_action) == EOL_CRLF &&
-                !(crlf_action == CRLF_AUTO || crlf_action == CRLF_AUTO_CRLF))
+       if (output_eol(ca.crlf_action) == EOL_CRLF)
                filter = cascade_filter(filter, lf_to_crlf_filter());
+       else
+               filter = cascade_filter(filter, &null_filter_singleton);
 
        return filter;
 }
diff --git a/copy.c b/copy.c
index 574fa1f09dadc2e9cc9a39702bafb352f71a56c4..4de6a110f0912d81def3f2dd4ffd6ddc9bc30d2f 100644 (file)
--- a/copy.c
+++ b/copy.c
@@ -42,15 +42,15 @@ int copy_file(const char *dst, const char *src, int mode)
        status = copy_fd(fdi, fdo);
        switch (status) {
        case COPY_READ_ERROR:
-               error("copy-fd: read returned %s", strerror(errno));
+               error_errno("copy-fd: read returned");
                break;
        case COPY_WRITE_ERROR:
-               error("copy-fd: write returned %s", strerror(errno));
+               error_errno("copy-fd: write returned");
                break;
        }
        close(fdi);
        if (close(fdo) != 0)
-               return error("%s: close error: %s", dst, strerror(errno));
+               return error_errno("%s: close error", dst);
 
        if (!status && adjust_shared_perm(dst))
                return -1;
index 291c0fd5e935b5abedc629697d44e5a9f57727bd..1f14d56e98834e73dce91f3a20c3e3795ba6fb37 100644 (file)
@@ -179,12 +179,12 @@ static int serve_cache_loop(int fd)
 
                client = accept(fd, NULL, NULL);
                if (client < 0) {
-                       warning("accept failed: %s", strerror(errno));
+                       warning_errno("accept failed");
                        return 1;
                }
                client2 = dup(client);
                if (client2 < 0) {
-                       warning("dup failed: %s", strerror(errno));
+                       warning_errno("dup failed");
                        close(client);
                        return 1;
                }
index 03daadb25af835a695c7d0baf466b0e1f82b58c4..1f8999b9cabf52c85b63f27178023ff53b7567c3 100644 (file)
@@ -65,8 +65,7 @@ static int populate_from_stdin(struct diff_filespec *s)
        size_t size = 0;
 
        if (strbuf_read(&buf, 0, 0) < 0)
-               return error("error while reading from stdin %s",
-                                    strerror(errno));
+               return error_errno("error while reading from stdin");
 
        s->should_munmap = 0;
        s->data = strbuf_detach(&buf, &size);
diff --git a/dir.c b/dir.c
index 656f272adc69b633ddd06277bf9cc77205a7e8ee..6172b3438d356d0359c2158708c718c6694e183a 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -53,13 +53,12 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
        int check_only, const struct path_simplify *simplify);
 static int get_dtype(struct dirent *de, const char *path, int len);
 
-/* helper string functions with support for the ignore_case flag */
-int strcmp_icase(const char *a, const char *b)
+int fspathcmp(const char *a, const char *b)
 {
        return ignore_case ? strcasecmp(a, b) : strcmp(a, b);
 }
 
-int strncmp_icase(const char *a, const char *b, size_t count)
+int fspathncmp(const char *a, const char *b, size_t count)
 {
        return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);
 }
@@ -795,12 +794,12 @@ int match_basename(const char *basename, int basenamelen,
 {
        if (prefix == patternlen) {
                if (patternlen == basenamelen &&
-                   !strncmp_icase(pattern, basename, basenamelen))
+                   !fspathncmp(pattern, basename, basenamelen))
                        return 1;
        } else if (flags & EXC_FLAG_ENDSWITH) {
                /* "*literal" matching against "fooliteral" */
                if (patternlen - 1 <= basenamelen &&
-                   !strncmp_icase(pattern + 1,
+                   !fspathncmp(pattern + 1,
                                   basename + basenamelen - (patternlen - 1),
                                   patternlen - 1))
                        return 1;
@@ -837,7 +836,7 @@ int match_pathname(const char *pathname, int pathlen,
         */
        if (pathlen < baselen + 1 ||
            (baselen && pathname[baselen] != '/') ||
-           strncmp_icase(pathname, base, baselen))
+           fspathncmp(pathname, base, baselen))
                return 0;
 
        namelen = baselen ? pathlen - baselen - 1 : pathlen;
@@ -851,7 +850,7 @@ int match_pathname(const char *pathname, int pathlen,
                if (prefix > namelen)
                        return 0;
 
-               if (strncmp_icase(pattern, name, prefix))
+               if (fspathncmp(pattern, name, prefix))
                        return 0;
                pattern += prefix;
                patternlen -= prefix;
diff --git a/dir.h b/dir.h
index d56d2fb48f68191d54fdfb8c5c104cb87cba1c41..bfde698c488adcc75b7a294476c622afb6f92b36 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -270,8 +270,8 @@ extern int remove_dir_recursively(struct strbuf *path, int flag);
 /* tries to remove the path with empty directories along it, ignores ENOENT */
 extern int remove_path(const char *path);
 
-extern int strcmp_icase(const char *a, const char *b);
-extern int strncmp_icase(const char *a, const char *b, size_t count);
+extern int fspathcmp(const char *a, const char *b);
+extern int fspathncmp(const char *a, const char *b, size_t count);
 
 /*
  * The prefix part of pattern must not contains wildcards.
index 01c644cddbe8e57e31cc711aa0ca3838cf23c64d..7519edecdc297a0c0fdbbb2f3c68cb55abf2e8b2 100644 (file)
--- a/editor.c
+++ b/editor.c
@@ -63,7 +63,6 @@ int launch_editor(const char *path, struct strbuf *buffer, const char *const *en
        if (!buffer)
                return 0;
        if (strbuf_read_file(buffer, path, 0) < 0)
-               return error("could not read file '%s': %s",
-                               path, strerror(errno));
+               return error_errno("could not read file '%s'", path);
        return 0;
 }
diff --git a/entry.c b/entry.c
index a4109574fa72e0f3f32ad7b9e1ed0e402c8aecff..519e04227b72f92a338836ce457d2a5be2b80218 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -168,8 +168,8 @@ static int write_entry(struct cache_entry *ce,
                        ret = symlink(new, path);
                        free(new);
                        if (ret)
-                               return error("unable to create symlink %s (%s)",
-                                            path, strerror(errno));
+                               return error_errno("unable to create symlink %s",
+                                                  path);
                        break;
                }
 
@@ -186,8 +186,7 @@ static int write_entry(struct cache_entry *ce,
                fd = open_output_fd(path, ce, to_tempfile);
                if (fd < 0) {
                        free(new);
-                       return error("unable to create file %s (%s)",
-                               path, strerror(errno));
+                       return error_errno("unable to create file %s", path);
                }
 
                wrote = write_in_full(fd, new, size);
@@ -284,8 +283,7 @@ int checkout_entry(struct cache_entry *ce,
                                return error("%s is a directory", path.buf);
                        remove_subtree(&path);
                } else if (unlink(path.buf))
-                       return error("unable to unlink old '%s' (%s)",
-                                    path.buf, strerror(errno));
+                       return error_errno("unable to unlink old '%s'", path.buf);
        } else if (state->not_new)
                return 0;
 
index 57acb2fe2aee79a30c4c956dcececd6a7122aeb6..ca72464a985021e58b898bb0b1f9af6c4a5282ac 100644 (file)
@@ -31,6 +31,7 @@ const char *git_log_output_encoding;
 const char *apply_default_whitespace;
 const char *apply_default_ignorewhitespace;
 const char *git_attributes_file;
+const char *git_hooks_path;
 int zlib_compression_level = Z_BEST_SPEED;
 int core_compression_level;
 int core_compression_seen;
@@ -63,6 +64,7 @@ int core_apply_sparse_checkout;
 int merge_log_config = -1;
 int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
 unsigned long pack_size_limit_cfg;
+enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
 
 #ifndef PROTECT_HFS_DEFAULT
 #define PROTECT_HFS_DEFAULT 0
index 9fc7093406b1e2085a3b2180a8506bcd6d486e06..83558dcfe3ab442a415accd82ec7503f95f4746e 100644 (file)
@@ -414,7 +414,7 @@ static void write_crash_report(const char *err)
        struct recent_command *rc;
 
        if (!rpt) {
-               error("can't write crash report %s: %s", loc, strerror(errno));
+               error_errno("can't write crash report %s", loc);
                free(loc);
                return;
        }
@@ -1512,7 +1512,7 @@ static int tree_content_set(
        t = root->tree;
        for (i = 0; i < t->entry_count; i++) {
                e = t->entries[i];
-               if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
+               if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) {
                        if (!*slash1) {
                                if (!S_ISDIR(mode)
                                                && e->versions[1].mode == mode
@@ -1602,7 +1602,7 @@ static int tree_content_remove(
        t = root->tree;
        for (i = 0; i < t->entry_count; i++) {
                e = t->entries[i];
-               if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
+               if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) {
                        if (*slash1 && !S_ISDIR(e->versions[1].mode))
                                /*
                                 * If p names a file in some subdirectory, and a
@@ -1669,7 +1669,7 @@ static int tree_content_get(
        t = root->tree;
        for (i = 0; i < t->entry_count; i++) {
                e = t->entries[i];
-               if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
+               if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) {
                        if (!*slash1)
                                goto found_entry;
                        if (!S_ISDIR(e->versions[1].mode))
@@ -1806,8 +1806,8 @@ static void dump_marks(void)
                return;
 
        if (hold_lock_file_for_update(&mark_lock, export_marks_file, 0) < 0) {
-               failure |= error("Unable to write marks file %s: %s",
-                       export_marks_file, strerror(errno));
+               failure |= error_errno("Unable to write marks file %s",
+                                      export_marks_file);
                return;
        }
 
@@ -1822,8 +1822,8 @@ static void dump_marks(void)
 
        dump_marks_helper(f, 0, marks);
        if (commit_lock_file(&mark_lock)) {
-               failure |= error("Unable to write file %s: %s",
-                       export_marks_file, strerror(errno));
+               failure |= error_errno("Unable to write file %s",
+                                      export_marks_file);
                return;
        }
 }
diff --git a/fsck.c b/fsck.c
index 92b17f5d6adb98499eb57f95d098688d2f96a2f7..05315451c56fe19b2a2e8663d1f4847070bc946f 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -59,6 +59,7 @@
        FUNC(HAS_DOTGIT, WARN) \
        FUNC(NULL_SHA1, WARN) \
        FUNC(ZERO_PADDED_FILEMODE, WARN) \
+       FUNC(NUL_IN_COMMIT, WARN) \
        /* infos (reported as warnings, but ignored by default) */ \
        FUNC(BAD_TAG_NAME, INFO) \
        FUNC(MISSING_TAGGER_ENTRY, INFO)
@@ -610,6 +611,7 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,
        struct commit_graft *graft;
        unsigned parent_count, parent_line_count = 0, author_count;
        int err;
+       const char *buffer_begin = buffer;
 
        if (verify_headers(buffer, size, &commit->object, options))
                return -1;
@@ -666,9 +668,17 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,
        err = fsck_ident(&buffer, &commit->object, options);
        if (err)
                return err;
-       if (!commit->tree)
-               return report(options, &commit->object, FSCK_MSG_BAD_TREE, "could not load commit's tree %s", sha1_to_hex(tree_sha1));
-
+       if (!commit->tree) {
+               err = report(options, &commit->object, FSCK_MSG_BAD_TREE, "could not load commit's tree %s", sha1_to_hex(tree_sha1));
+               if (err)
+                       return err;
+       }
+       if (memchr(buffer_begin, '\0', size)) {
+               err = report(options, &commit->object, FSCK_MSG_NUL_IN_COMMIT,
+                            "NUL byte in the commit object body");
+               if (err)
+                       return err;
+       }
        return 0;
 }
 
index 1f8b5f3b1f1ac17716681fee2d72b9c124a9b99c..49d4029b8dddcb06dc6bea3d5f47c020785e3ddf 100644 (file)
@@ -409,7 +409,9 @@ extern NORETURN void usagef(const char *err, ...) __attribute__((format (printf,
 extern NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2)));
 extern NORETURN void die_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
 extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
+extern int error_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
 extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
+extern void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
 
 #ifndef NO_OPENSSL
 #ifdef APPLE_COMMON_CRYPTO
index 02c0445be1057619e01e56bd6792f78fc011dc42..d50c85ed7ba719a0dc04f7ca96cf05216104599d 100755 (executable)
@@ -1156,7 +1156,7 @@ sub prepDirForOutput
     # FUTURE: This would more accurately emulate CVS by sending
     #   another copy of sticky after processing the files in that
     #   directory.  Or intermediate: perhaps send all sticky's for
-    #   $seendirs after after processing all files.
+    #   $seendirs after processing all files.
 }
 
 # update \n
@@ -2824,7 +2824,7 @@ sub statecleanup
 }
 
 # Return working directory CVS revision "1.X" out
-# of the the working directory "entries" state, for the given filename.
+# of the working directory "entries" state, for the given filename.
 # This is prefixed with a dash if the file is scheduled for removal
 # when it is committed.
 sub revparse
@@ -2935,7 +2935,7 @@ sub filecleanup
     return $filename;
 }
 
-# Remove prependdir from the path, so that is is relative to the directory
+# Remove prependdir from the path, so that it is relative to the directory
 # the CVS client was started from, rather than the top of the project.
 # Essentially the inverse of filecleanup().
 sub remove_prependdir
index 55fe8d56c9d5107df0843378ef137d64a48b2980..d3c39980f3fe2c346e4264d4b99a49acf7140301 100644 (file)
@@ -56,11 +56,13 @@ get_remote_merge_branch () {
 error_on_missing_default_upstream () {
        cmd="$1"
        op_type="$2"
-       op_prep="$3"
+       op_prep="$3" # FIXME: op_prep is no longer used
        example="$4"
        branch_name=$(git symbolic-ref -q HEAD)
+       display_branch_name="${branch_name#refs/heads/}"
        # If there's only one remote, use that in the suggestion
-       remote="<remote>"
+       remote="$(gettext "<remote>")"
+       branch="$(gettext "<branch>")"
        if test $(git remote | wc -l) = 1
        then
                remote=$(git remote)
@@ -68,22 +70,32 @@ error_on_missing_default_upstream () {
 
        if test -z "$branch_name"
        then
-               echo "You are not currently on a branch. Please specify which
-branch you want to $op_type $op_prep. See git-${cmd}(1) for details.
-
-    $example
-"
+               gettextln "You are not currently on a branch."
        else
-               echo "There is no tracking information for the current branch.
-Please specify which branch you want to $op_type $op_prep.
-See git-${cmd}(1) for details
-
-    $example
-
-If you wish to set tracking information for this branch you can do so with:
-
-    git branch --set-upstream-to=$remote/<branch> ${branch_name#refs/heads/}
-"
+               gettextln "There is no tracking information for the current branch."
+       fi
+       case "$op_type" in
+       rebase)
+               gettextln "Please specify which branch you want to rebase against."
+               ;;
+       merge)
+               gettextln "Please specify which branch you want to merge with."
+               ;;
+       *)
+               echo >&2 "BUG: unknown operation type: $op_type"
+               exit 1
+               ;;
+       esac
+       eval_gettextln "See git-\${cmd}(1) for details."
+       echo
+       echo "    $example"
+       echo
+       if test -n "$branch_name"
+       then
+               gettextln "If you wish to set tracking information for this branch you can do so with:"
+               echo
+               echo "    git branch --set-upstream-to=$remote/$branch $display_branch_name"
+               echo
        fi
        exit 1
 }
index 470413b9f9b603b8f9fe8718607bf66f6c794cdf..9d2bfb7a16afc10c1a52fe80e718d6199df0e331 100644 (file)
@@ -82,6 +82,7 @@ rewritten_pending="$state_dir"/rewritten-pending
 cr=$(printf "\015")
 
 strategy_args=${strategy:+--strategy=$strategy}
+test -n "$strategy_opts" &&
 eval '
        for strategy_opt in '"$strategy_opts"'
        do
index 2a84d7e66a0e6c2bbd0bee1c3e796e4580feaea9..5a4dec050b2ebdbe8f9674666ddaabb4c0e193f2 100755 (executable)
@@ -8,7 +8,7 @@ dashless=$(basename "$0" | sed -e 's/-/ /')
 USAGE="[--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <repository>] [--] <repository> [<path>]
    or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
    or: $dashless [--quiet] init [--] [<path>...]
-   or: $dashless [--quiet] deinit [-f|--force] [--] <path>...
+   or: $dashless [--quiet] deinit [-f|--force] (--all| [--] <path>...)
    or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--reference <repository>] [--recursive] [--] [<path>...]
    or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
    or: $dashless [--quiet] foreach [--recursive] <command>
@@ -46,79 +46,6 @@ prefix=
 custom_name=
 depth=
 
-# The function takes at most 2 arguments. The first argument is the
-# URL that navigates to the submodule origin repo. When relative, this URL
-# is relative to the superproject origin URL repo. The second up_path
-# argument, if specified, is the relative path that navigates
-# from the submodule working tree to the superproject working tree.
-#
-# The output of the function is the origin URL of the submodule.
-#
-# The output will either be an absolute URL or filesystem path (if the
-# superproject origin URL is an absolute URL or filesystem path,
-# respectively) or a relative file system path (if the superproject
-# origin URL is a relative file system path).
-#
-# When the output is a relative file system path, the path is either
-# relative to the submodule working tree, if up_path is specified, or to
-# the superproject working tree otherwise.
-resolve_relative_url ()
-{
-       remote=$(get_default_remote)
-       remoteurl=$(git config "remote.$remote.url") ||
-               remoteurl=$(pwd) # the repository is its own authoritative upstream
-       url="$1"
-       remoteurl=${remoteurl%/}
-       sep=/
-       up_path="$2"
-
-       case "$remoteurl" in
-       *:*|/*)
-               is_relative=
-               ;;
-       ./*|../*)
-               is_relative=t
-               ;;
-       *)
-               is_relative=t
-               remoteurl="./$remoteurl"
-               ;;
-       esac
-
-       while test -n "$url"
-       do
-               case "$url" in
-               ../*)
-                       url="${url#../}"
-                       case "$remoteurl" in
-                       */*)
-                               remoteurl="${remoteurl%/*}"
-                               ;;
-                       *:*)
-                               remoteurl="${remoteurl%:*}"
-                               sep=:
-                               ;;
-                       *)
-                               if test -z "$is_relative" || test "." = "$remoteurl"
-                               then
-                                       die "$(eval_gettext "cannot strip one component off url '\$remoteurl'")"
-                               else
-                                       remoteurl=.
-                               fi
-                               ;;
-                       esac
-                       ;;
-               ./*)
-                       url="${url#./}"
-                       ;;
-               *)
-                       break;;
-               esac
-       done
-       remoteurl="$remoteurl$sep${url%/}"
-       echo "${is_relative:+${up_path}}${remoteurl#./}"
-}
-
 # Resolve a path to be relative to another path.  This is intended for
 # converting submodule paths when git-submodule is run in a subdirectory
 # and only handles paths where the directory separator is '/'.
@@ -197,9 +124,10 @@ isnumber()
 # of the settings from GIT_CONFIG_PARAMETERS.
 sanitize_submodule_env()
 {
-       sanitized_config=$(git submodule--helper sanitize-config)
+       save_config=$GIT_CONFIG_PARAMETERS
        clear_local_git_env
-       GIT_CONFIG_PARAMETERS=$sanitized_config
+       GIT_CONFIG_PARAMETERS=$save_config
+       export GIT_CONFIG_PARAMETERS
 }
 
 #
@@ -291,7 +219,7 @@ cmd_add()
                die "$(gettext "Relative path can only be used from the toplevel of the working tree")"
 
                # dereference source url relative to parent's url
-               realrepo=$(resolve_relative_url "$repo") || exit
+               realrepo=$(git submodule--helper resolve-relative-url "$repo") || exit
                ;;
        *:*|/*)
                # absolute url
@@ -477,50 +405,7 @@ cmd_init()
                shift
        done
 
-       git submodule--helper list --prefix "$wt_prefix" "$@" |
-       while read mode sha1 stage sm_path
-       do
-               die_if_unmatched "$mode"
-               name=$(git submodule--helper name "$sm_path") || exit
-
-               displaypath=$(relative_path "$prefix$sm_path")
-
-               # Copy url setting when it is not set yet
-               if test -z "$(git config "submodule.$name.url")"
-               then
-                       url=$(git config -f .gitmodules submodule."$name".url)
-                       test -z "$url" &&
-                       die "$(eval_gettext "No url found for submodule path '\$displaypath' in .gitmodules")"
-
-                       # Possibly a url relative to parent
-                       case "$url" in
-                       ./*|../*)
-                               url=$(resolve_relative_url "$url") || exit
-                               ;;
-                       esac
-                       git config submodule."$name".url "$url" ||
-                       die "$(eval_gettext "Failed to register url for submodule path '\$displaypath'")"
-
-                       say "$(eval_gettext "Submodule '\$name' (\$url) registered for path '\$displaypath'")"
-               fi
-
-               # Copy "update" setting when it is not set yet
-               if upd="$(git config -f .gitmodules submodule."$name".update)" &&
-                  test -n "$upd" &&
-                  test -z "$(git config submodule."$name".update)"
-               then
-                       case "$upd" in
-                       checkout | rebase | merge | none)
-                               ;; # known modes of updating
-                       *)
-                               echo >&2 "warning: unknown update mode '$upd' suggested for submodule '$name'"
-                               upd=none
-                               ;;
-                       esac
-                       git config submodule."$name".update "$upd" ||
-                       die "$(eval_gettext "Failed to register update mode for submodule path '\$displaypath'")"
-               fi
-       done
+       git ${wt_prefix:+-C "$wt_prefix"} submodule--helper init ${GIT_QUIET:+--quiet} ${prefix:+--prefix "$prefix"} "$@"
 }
 
 #
@@ -531,6 +416,7 @@ cmd_init()
 cmd_deinit()
 {
        # parse $args after "submodule ... deinit".
+       deinit_all=
        while test $# -ne 0
        do
                case "$1" in
@@ -540,6 +426,9 @@ cmd_deinit()
                -q|--quiet)
                        GIT_QUIET=1
                        ;;
+               --all)
+                       deinit_all=t
+                       ;;
                --)
                        shift
                        break
@@ -554,9 +443,14 @@ cmd_deinit()
                shift
        done
 
-       if test $# = 0
+       if test -n "$deinit_all" && test "$#" -ne 0
+       then
+               echo >&2 "$(eval_gettext "pathspec and --all are incompatible")"
+               usage
+       fi
+       if test $# = 0 && test -z "$deinit_all"
        then
-               die "$(eval_gettext "Use '.' if you really want to deinitialize all submodules")"
+               die "$(eval_gettext "Use '--all' if you really want to deinitialize all submodules")"
        fi
 
        git submodule--helper list --prefix "$wt_prefix" "$@" |
@@ -823,7 +717,8 @@ cmd_update()
                if test -n "$recursive"
                then
                        (
-                               prefix="$prefix$sm_path/"
+                               prefix=$(relative_path "$prefix$sm_path/")
+                               wt_prefix=
                                sanitize_submodule_env
                                cd "$sm_path" &&
                                eval cmd_update
@@ -1212,9 +1107,9 @@ cmd_sync()
                        # guarantee a trailing /
                        up_path=${up_path%/}/ &&
                        # path from submodule work tree to submodule origin repo
-                       sub_origin_url=$(resolve_relative_url "$url" "$up_path") &&
+                       sub_origin_url=$(git submodule--helper resolve-relative-url "$url" "$up_path") &&
                        # path from superproject work tree to submodule origin repo
-                       super_config_url=$(resolve_relative_url "$url") || exit
+                       super_config_url=$(git submodule--helper resolve-relative-url "$url") || exit
                        ;;
                *)
                        sub_origin_url="$url"
index 22599382365eb564cdfc22b434cb60796e06b82f..c4b1e8c78d396194753d8a7287678bc476f858d6 100644 (file)
@@ -219,11 +219,9 @@ int verify_signed_buffer(const char *payload, size_t payload_size,
        args_gpg[0] = gpg_program;
        fd = git_mkstemp(path, PATH_MAX, ".git_vtag_tmpXXXXXX");
        if (fd < 0)
-               return error(_("could not create temporary file '%s': %s"),
-                            path, strerror(errno));
+               return error_errno(_("could not create temporary file '%s'"), path);
        if (write_in_full(fd, signature, signature_size) < 0)
-               return error(_("failed writing detached signature to '%s': %s"),
-                            path, strerror(errno));
+               return error_errno(_("failed writing detached signature to '%s'"), path);
        close(fd);
 
        gpg.argv = args_gpg;
diff --git a/grep.c b/grep.c
index 528b652f713d2b6db5f48e3829448212cc3837bf..ec6f7ffa19622f1a63a4cdd51936d1061e90fc49 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -1732,7 +1732,7 @@ static int grep_source_load_file(struct grep_source *gs)
        if (lstat(filename, &st) < 0) {
        err_ret:
                if (errno != ENOENT)
-                       error(_("'%s': %s"), filename, strerror(errno));
+                       error_errno(_("failed to stat '%s'"), filename);
                return -1;
        }
        if (!S_ISREG(st.st_mode))
@@ -1743,7 +1743,7 @@ static int grep_source_load_file(struct grep_source *gs)
                goto err_ret;
        data = xmallocz(size);
        if (st.st_size != read_in_full(i, data, size)) {
-               error(_("'%s': short read %s"), filename, strerror(errno));
+               error_errno(_("'%s': short read"), filename);
                close(i);
                free(data);
                return -1;
diff --git a/http.c b/http.c
index 985b995c1d05b9a1ae461d6056e14e66b39d563c..df6dd01594a0c7bacbb9beacec683072de38a5b1 100644 (file)
--- a/http.c
+++ b/http.c
@@ -294,7 +294,7 @@ static int http_options(const char *var, const char *value, void *cb)
                return git_config_string(&http_proxy_authmethod, var, value);
 
        if (!strcmp("http.cookiefile", var))
-               return git_config_string(&curl_cookie_file, var, value);
+               return git_config_pathname(&curl_cookie_file, var, value);
        if (!strcmp("http.savecookies", var)) {
                curl_save_cookies = git_config_bool(var, value);
                return 0;
@@ -460,8 +460,7 @@ static int sockopt_callback(void *client, curl_socket_t fd, curlsocktype type)
 
        rc = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&ka, len);
        if (rc < 0)
-               warning("unable to set SO_KEEPALIVE on socket %s",
-                       strerror(errno));
+               warning_errno("unable to set SO_KEEPALIVE on socket");
 
        return 0; /* CURL_SOCKOPT_OK only exists since curl 7.21.5 */
 }
@@ -1923,8 +1922,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
        }
 
        if (freq->localfile < 0) {
-               error("Couldn't create temporary file %s: %s",
-                     freq->tmpfile, strerror(errno));
+               error_errno("Couldn't create temporary file %s", freq->tmpfile);
                goto abort;
        }
 
@@ -1969,8 +1967,8 @@ struct http_object_request *new_http_object_request(const char *base_url,
                        prev_posn = 0;
                        lseek(freq->localfile, 0, SEEK_SET);
                        if (ftruncate(freq->localfile, 0) < 0) {
-                               error("Couldn't truncate temporary file %s: %s",
-                                         freq->tmpfile, strerror(errno));
+                               error_errno("Couldn't truncate temporary file %s",
+                                           freq->tmpfile);
                                goto abort;
                        }
                }
diff --git a/ident.c b/ident.c
index 4fd82d104365c2e2c1ab7e1597e7e246c8eded24..139c5289d03b7594af2a07e6e0364bb285ae0348 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -75,14 +75,12 @@ static int add_mailname_host(struct strbuf *buf)
        mailname = fopen("/etc/mailname", "r");
        if (!mailname) {
                if (errno != ENOENT)
-                       warning("cannot open /etc/mailname: %s",
-                               strerror(errno));
+                       warning_errno("cannot open /etc/mailname");
                return -1;
        }
        if (strbuf_getline(&mailnamebuf, mailname) == EOF) {
                if (ferror(mailname))
-                       warning("cannot read /etc/mailname: %s",
-                               strerror(errno));
+                       warning_errno("cannot read /etc/mailname");
                strbuf_release(&mailnamebuf);
                fclose(mailname);
                return -1;
@@ -125,7 +123,7 @@ static void add_domainname(struct strbuf *out, int *is_bogus)
        char buf[1024];
 
        if (gethostname(buf, sizeof(buf))) {
-               warning("cannot get host name: %s", strerror(errno));
+               warning_errno("cannot get host name");
                strbuf_addstr(out, "(none)");
                *is_bogus = 1;
                return;
index ff4a43a982a6a67ec826fdb01153a9e35e720751..ad8be42f912b456492ca1704ce067fea4713401a 100644 (file)
@@ -47,7 +47,9 @@ static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
        assert(opts);
 
        /*
-        * The tentative merge result is the or common ancestor for an internal merge.
+        * The tentative merge result is the common ancestor for an
+        * internal merge.  For the final merge, it is "ours" by
+        * default but -Xours/-Xtheirs can tweak the choice.
         */
        if (opts->virtual_ancestor) {
                stolen = orig;
@@ -383,8 +385,12 @@ int ll_merge(mmbuffer_t *result_buf,
                }
        }
        driver = find_ll_merge_driver(ll_driver_name);
-       if (opts->virtual_ancestor && driver->recursive)
-               driver = find_ll_merge_driver(driver->recursive);
+
+       if (opts->virtual_ancestor) {
+               if (driver->recursive)
+                       driver = find_ll_merge_driver(driver->recursive);
+               marker_size += 2;
+       }
        return driver->fn(driver, result_buf, path, ancestor, ancestor_label,
                          ours, our_label, theirs, their_label,
                          opts, marker_size);
index 972623709fdac5503328f7e252d5334dfcb461c6..b5c521fdea8b0bfc577795b172010dfc2b8bf8ff 100644 (file)
--- a/mailmap.c
+++ b/mailmap.c
@@ -189,8 +189,7 @@ static int read_mailmap_file(struct string_list *map, const char *filename,
        if (!f) {
                if (errno == ENOENT)
                        return 0;
-               return error("unable to open mailmap at %s: %s",
-                            filename, strerror(errno));
+               return error_errno("unable to open mailmap at %s", filename);
        }
 
        while (fgets(buffer, sizeof(buffer), f) != NULL)
index 47a91920601d74842a0947ce1431c00b3d723972..312a85dbdef5723ef805917f5a556b75a55a7041 100644 (file)
@@ -110,6 +110,8 @@ static int get_value(struct parse_opt_ctx_t *p,
                return 0;
 
        case OPTION_COUNTUP:
+               if (*(int *)opt->value < 0)
+                       *(int *)opt->value = 0;
                *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1;
                return 0;
 
index b7b3e5a1a700d4d2e1656c605697c2cf6345b8a6..a4d00166646d3dc354f3c06e9e75ce7dc3724f6a 100644 (file)
@@ -4,7 +4,7 @@
 #include "sha1-lookup.h"
 #include "patch-ids.h"
 
-static int commit_patch_id(struct commit *commit, struct diff_options *options,
+int commit_patch_id(struct commit *commit, struct diff_options *options,
                    unsigned char *sha1)
 {
        if (commit->parents)
index c8c7ca110ad34def12a3594a1560b3c3052eb701..eeb56b307f602773a41ea3b8cb22d89959c4f9d9 100644 (file)
@@ -13,6 +13,8 @@ struct patch_ids {
        struct patch_id_bucket *patches;
 };
 
+int commit_patch_id(struct commit *commit, struct diff_options *options,
+                   unsigned char *sha1);
 int init_patch_ids(struct patch_ids *);
 int free_patch_ids(struct patch_ids *);
 struct patch_id *add_commit_patch_id(struct commit *, struct patch_ids *);
diff --git a/path.c b/path.c
index bbaea5ab0bf73056dfa74063474f62685fda1d5e..259aeed846bd3dac8e10cde30a22ec1ad8697a63 100644 (file)
--- a/path.c
+++ b/path.c
@@ -5,6 +5,7 @@
 #include "strbuf.h"
 #include "string-list.h"
 #include "dir.h"
+#include "worktree.h"
 
 static int get_st_mode_bits(const char *path, int *mode)
 {
@@ -134,7 +135,7 @@ static struct common_dir common_list[] = {
  * definite
  * definition
  *
- * The trie would look look like:
+ * The trie would look like:
  * root: len = 0, children a and d non-NULL, value = NULL.
  *    a: len = 2, contents = bc, value = (data for "abc")
  *    d: len = 2, contents = ef, children i non-NULL, value = (data for "def")
@@ -383,10 +384,11 @@ static void adjust_git_path(struct strbuf *buf, int git_dir_len)
                update_common_dir(buf, git_dir_len, NULL);
 }
 
-static void do_git_path(struct strbuf *buf, const char *fmt, va_list args)
+static void do_git_path(const struct worktree *wt, struct strbuf *buf,
+                       const char *fmt, va_list args)
 {
        int gitdir_len;
-       strbuf_addstr(buf, get_git_dir());
+       strbuf_addstr(buf, get_worktree_git_dir(wt));
        if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
                strbuf_addch(buf, '/');
        gitdir_len = buf->len;
@@ -400,7 +402,7 @@ char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
        va_list args;
        strbuf_reset(buf);
        va_start(args, fmt);
-       do_git_path(buf, fmt, args);
+       do_git_path(NULL, buf, fmt, args);
        va_end(args);
        return buf->buf;
 }
@@ -409,7 +411,7 @@ void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
 {
        va_list args;
        va_start(args, fmt);
-       do_git_path(sb, fmt, args);
+       do_git_path(NULL, sb, fmt, args);
        va_end(args);
 }
 
@@ -418,7 +420,7 @@ const char *git_path(const char *fmt, ...)
        struct strbuf *pathname = get_pathname();
        va_list args;
        va_start(args, fmt);
-       do_git_path(pathname, fmt, args);
+       do_git_path(NULL, pathname, fmt, args);
        va_end(args);
        return pathname->buf;
 }
@@ -428,7 +430,7 @@ char *git_pathdup(const char *fmt, ...)
        struct strbuf path = STRBUF_INIT;
        va_list args;
        va_start(args, fmt);
-       do_git_path(&path, fmt, args);
+       do_git_path(NULL, &path, fmt, args);
        va_end(args);
        return strbuf_detach(&path, NULL);
 }
@@ -454,6 +456,16 @@ const char *mkpath(const char *fmt, ...)
        return cleanup_path(pathname->buf);
 }
 
+const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...)
+{
+       struct strbuf *pathname = get_pathname();
+       va_list args;
+       va_start(args, fmt);
+       do_git_path(wt, pathname, fmt, args);
+       va_end(args);
+       return pathname->buf;
+}
+
 static void do_submodule_path(struct strbuf *buf, const char *path,
                              const char *fmt, va_list args)
 {
@@ -503,6 +515,35 @@ void strbuf_git_path_submodule(struct strbuf *buf, const char *path,
        va_end(args);
 }
 
+static void do_git_common_path(struct strbuf *buf,
+                              const char *fmt,
+                              va_list args)
+{
+       strbuf_addstr(buf, get_git_common_dir());
+       if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
+               strbuf_addch(buf, '/');
+       strbuf_vaddf(buf, fmt, args);
+       strbuf_cleanup_path(buf);
+}
+
+const char *git_common_path(const char *fmt, ...)
+{
+       struct strbuf *pathname = get_pathname();
+       va_list args;
+       va_start(args, fmt);
+       do_git_common_path(pathname, fmt, args);
+       va_end(args);
+       return pathname->buf;
+}
+
+void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
+{
+       va_list args;
+       va_start(args, fmt);
+       do_git_common_path(sb, fmt, args);
+       va_end(args);
+}
+
 int validate_headref(const char *path)
 {
        struct stat st;
index 49eb88af8d052e2bd71b83576034f8d338290013..ce7e4e8da3947bb2c527c49d6d09e1e49b0392c3 100644 (file)
@@ -393,7 +393,7 @@ sub command_close_pipe {
 Execute the given C<COMMAND> in the same way as command_output_pipe()
 does but return both an input pipe filehandle and an output pipe filehandle.
 
-The function will return return C<($pid, $pipe_in, $pipe_out, $ctx)>.
+The function will return C<($pid, $pipe_in, $pipe_out, $ctx)>.
 See C<command_close_bidi_pipe()> for details.
 
 =cut
index ed352018964ef2260f5da51239800bbcc816ad1b..d0199cace4aa0ab22a46fdb5ac2777c4e7e6153b 100644 (file)
@@ -119,8 +119,7 @@ static int add_recent_loose(const unsigned char *sha1,
                 */
                if (errno == ENOENT)
                        return 0;
-               return error("unable to stat %s: %s",
-                            sha1_to_hex(sha1), strerror(errno));
+               return error_errno("unable to stat %s", sha1_to_hex(sha1));
        }
 
        add_recent_object(sha1, st.st_mtime, data);
index ddc4f8fd4801f2ab211340d25d2528affa50a352..a326e4e2516e2129e7a08bfc149786402ec160fb 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -2108,7 +2108,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)
                           "Your branch and '%s' have diverged,\n"
                               "and have %d and %d different commits each, "
                               "respectively.\n",
-                          theirs),
+                          ours + theirs),
                        base, ours, theirs);
                if (advice_status_hints)
                        strbuf_addf(sb,
index c8b9f407872f431399a4c5c321742c32cded5711..1810c040daba6398580bd9a40179b365887a91a6 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -501,8 +501,7 @@ static int handle_file(const char *path, unsigned char *sha1, const char *output
                error("There were errors while writing %s (%s)",
                      path, strerror(io.io.wrerror));
        if (io.io.output && fclose(io.io.output))
-               io.io.wrerror = error("Failed to flush %s: %s",
-                                     path, strerror(errno));
+               io.io.wrerror = error_errno("Failed to flush %s", path);
 
        if (hunk_no < 0) {
                if (output)
@@ -684,20 +683,17 @@ static int merge(const struct rerere_id *id, const char *path)
         * Mark that "postimage" was used to help gc.
         */
        if (utime(rerere_path(id, "postimage"), NULL) < 0)
-               warning("failed utime() on %s: %s",
-                       rerere_path(id, "postimage"),
-                       strerror(errno));
+               warning_errno("failed utime() on %s",
+                             rerere_path(id, "postimage"));
 
        /* Update "path" with the resolution */
        f = fopen(path, "w");
        if (!f)
-               return error("Could not open %s: %s", path,
-                            strerror(errno));
+               return error_errno("Could not open %s", path);
        if (fwrite(result.ptr, result.size, 1, f) != 1)
-               error("Could not write %s: %s", path, strerror(errno));
+               error_errno("Could not write %s", path);
        if (fclose(f))
-               return error("Writing %s failed: %s", path,
-                            strerror(errno));
+               return error_errno("Writing %s failed", path);
 
 out:
        free(cur.ptr);
@@ -1071,7 +1067,7 @@ static int rerere_forget_one_path(const char *path, struct string_list *rr)
        if (unlink(filename))
                return (errno == ENOENT
                        ? error("no remembered resolution for %s", path)
-                       : error("cannot unlink %s: %s", filename, strerror(errno)));
+                       : error_errno("cannot unlink %s", filename));
 
        /*
         * Update the preimage so that the user can resolve the
index e4593cd99b13fdbfec02939526ac151270aa20f5..af0c8a10df0ca06e900d26486fa6c1a86bb2ff7f 100644 (file)
@@ -233,7 +233,7 @@ static int wait_or_whine(pid_t pid, const char *argv0, int in_signal)
 
        if (waiting < 0) {
                failed_errno = errno;
-               error("waitpid for %s failed: %s", argv0, strerror(errno));
+               error_errno("waitpid for %s failed", argv0);
        } else if (waiting != pid) {
                error("waitpid is confused (%s)", argv0);
        } else if (WIFSIGNALED(status)) {
@@ -420,8 +420,7 @@ int start_command(struct child_process *cmd)
                }
        }
        if (cmd->pid < 0)
-               error("cannot fork() for %s: %s", cmd->argv[0],
-                       strerror(errno));
+               error_errno("cannot fork() for %s", cmd->argv[0]);
        else if (cmd->clean_on_exit)
                mark_child_for_cleanup(cmd->pid);
 
@@ -482,7 +481,7 @@ int start_command(struct child_process *cmd)
                        cmd->dir, fhin, fhout, fherr);
        failed_errno = errno;
        if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT))
-               error("cannot spawn %s: %s", cmd->argv[0], strerror(errno));
+               error_errno("cannot spawn %s", cmd->argv[0]);
        if (cmd->clean_on_exit && cmd->pid >= 0)
                mark_child_for_cleanup(cmd->pid);
 
@@ -703,7 +702,7 @@ int start_async(struct async *async)
                if (pipe(fdin) < 0) {
                        if (async->out > 0)
                                close(async->out);
-                       return error("cannot create pipe: %s", strerror(errno));
+                       return error_errno("cannot create pipe");
                }
                async->in = fdin[1];
        }
@@ -715,7 +714,7 @@ int start_async(struct async *async)
                                close_pair(fdin);
                        else if (async->in)
                                close(async->in);
-                       return error("cannot create pipe: %s", strerror(errno));
+                       return error_errno("cannot create pipe");
                }
                async->out = fdout[0];
        }
@@ -740,7 +739,7 @@ int start_async(struct async *async)
 
        async->pid = fork();
        if (async->pid < 0) {
-               error("fork (async) failed: %s", strerror(errno));
+               error_errno("fork (async) failed");
                goto error;
        }
        if (!async->pid) {
@@ -787,7 +786,7 @@ int start_async(struct async *async)
        {
                int err = pthread_create(&async->tid, NULL, run_thread, async);
                if (err) {
-                       error("cannot create thread: %s", strerror(err));
+                       error_errno("cannot create thread");
                        goto error;
                }
        }
@@ -825,7 +824,10 @@ const char *find_hook(const char *name)
        static struct strbuf path = STRBUF_INIT;
 
        strbuf_reset(&path);
-       strbuf_git_path(&path, "hooks/%s", name);
+       if (git_hooks_path)
+               strbuf_addf(&path, "%s/%s", git_hooks_path, name);
+       else
+               strbuf_git_path(&path, "hooks/%s", name);
        if (access(path.buf, X_OK) < 0)
                return NULL;
        return path.buf;
index e66f2fe0f0409cccf1647f7851044f91d68252a6..4687ad4b80bf651fd9dab66916681b71f7a6e31d 100644 (file)
@@ -875,8 +875,7 @@ static int sequencer_rollback(struct replay_opts *opts)
                return rollback_single_pick();
        }
        if (!f)
-               return error(_("cannot open %s: %s"), git_path_head_file(),
-                                               strerror(errno));
+               return error_errno(_("cannot open %s"), git_path_head_file());
        if (strbuf_getline_lf(&buf, f)) {
                error(_("cannot read %s: %s"), git_path_head_file(),
                      ferror(f) ?  strerror(errno) : _("unexpected end of file"));
index 5a86e297b51261b5f1ae9fa6dad6fa7dc86cd4ba..75dd6774136258c677fe4f4bbe2124c83b4cea68 100644 (file)
@@ -36,7 +36,7 @@ static int update_info_file(char *path, int (*generate)(FILE *))
 
 out:
        if (ret) {
-               error("unable to update %s: %s", path, strerror(errno));
+               error_errno("unable to update %s", path);
                if (fp)
                        fclose(fp);
                else if (fd >= 0)
index d0f2aa029b14451d4ffe669d48602c55060b9f42..d5e11217f523018008b3c4861d5d70068de14c72 100644 (file)
@@ -301,7 +301,7 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base,
                        return -1;
                }
        }
-       if (!strcmp_icase(ent->base, normalized_objdir)) {
+       if (!fspathcmp(ent->base, normalized_objdir)) {
                free(ent);
                return -1;
        }
@@ -1107,9 +1107,8 @@ unsigned char *use_pack(struct packed_git *p,
                                PROT_READ, MAP_PRIVATE,
                                p->pack_fd, win->offset);
                        if (win->base == MAP_FAILED)
-                               die("packfile %s cannot be mapped: %s",
-                                       p->pack_name,
-                                       strerror(errno));
+                               die_errno("packfile %s cannot be mapped",
+                                         p->pack_name);
                        if (!win->offset && win->len == p->pack_size
                                && !p->do_not_close)
                                close_pack_fd(p);
@@ -1279,8 +1278,8 @@ static void prepare_packed_git_one(char *objdir, int local)
        dir = opendir(path.buf);
        if (!dir) {
                if (errno != ENOENT)
-                       error("unable to open object pack directory: %s: %s",
-                             path.buf, strerror(errno));
+                       error_errno("unable to open object pack directory: %s",
+                                   path.buf);
                strbuf_release(&path);
                return;
        }
@@ -2984,7 +2983,7 @@ int finalize_object_file(const char *tmpfile, const char *filename)
        unlink_or_warn(tmpfile);
        if (ret) {
                if (ret != EEXIST) {
-                       return error("unable to write sha1 filename %s: %s", filename, strerror(ret));
+                       return error_errno("unable to write sha1 filename %s", filename);
                }
                /* FIXME!!! Collision check here ? */
        }
@@ -2998,7 +2997,7 @@ int finalize_object_file(const char *tmpfile, const char *filename)
 static int write_buffer(int fd, const void *buf, size_t len)
 {
        if (write_in_full(fd, buf, len) < 0)
-               return error("file write error (%s)", strerror(errno));
+               return error_errno("file write error");
        return 0;
 }
 
@@ -3081,7 +3080,7 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
                if (errno == EACCES)
                        return error("insufficient permission for adding an object to repository database %s", get_object_directory());
                else
-                       return error("unable to create temporary file: %s", strerror(errno));
+                       return error_errno("unable to create temporary file");
        }
 
        /* Set it up */
@@ -3126,8 +3125,7 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
                utb.actime = mtime;
                utb.modtime = mtime;
                if (utime(tmp_file.buf, &utb) < 0)
-                       warning("failed utime() on %s: %s",
-                               tmp_file.buf, strerror(errno));
+                       warning_errno("failed utime() on %s", tmp_file.buf);
        }
 
        return finalize_object_file(tmp_file.buf, filename);
@@ -3360,7 +3358,7 @@ static int index_core(unsigned char *sha1, int fd, size_t size,
                if (size == read_in_full(fd, buf, size))
                        ret = index_mem(sha1, buf, size, type, path, flags);
                else
-                       ret = error("short read %s", strerror(errno));
+                       ret = error_errno("short read");
                free(buf);
        } else {
                void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
@@ -3425,18 +3423,14 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned
        case S_IFREG:
                fd = open(path, O_RDONLY);
                if (fd < 0)
-                       return error("open(\"%s\"): %s", path,
-                                    strerror(errno));
+                       return error_errno("open(\"%s\")", path);
                if (index_fd(sha1, fd, st, OBJ_BLOB, path, flags) < 0)
                        return error("%s: failed to insert into database",
                                     path);
                break;
        case S_IFLNK:
-               if (strbuf_readlink(&sb, path, st->st_size)) {
-                       char *errstr = strerror(errno);
-                       return error("readlink(\"%s\"): %s", path,
-                                    errstr);
-               }
+               if (strbuf_readlink(&sb, path, st->st_size))
+                       return error_errno("readlink(\"%s\")", path);
                if (!(flags & HASH_WRITE_OBJECT))
                        hash_sha1_file(sb.buf, sb.len, blob_type, sha1);
                else if (write_sha1_file(sb.buf, sb.len, blob_type, sha1))
@@ -3492,7 +3486,7 @@ static int for_each_file_in_obj_subdir(int subdir_nr,
        if (!dir) {
                if (errno == ENOENT)
                        return 0;
-               return error("unable to open %s: %s", path->buf, strerror(errno));
+               return error_errno("unable to open %s", path->buf);
        }
 
        while ((de = readdir(dir))) {
index 968b780a06d1f17b190395f06f78fc3124fcf445..3c75d4b9ce717fa92520881ef754485d068a3a60 100644 (file)
@@ -60,7 +60,7 @@ static void mark_base_index_entries(struct index_state *base)
         * To keep track of the shared entries between
         * istate->base->cache[] and istate->cache[], base entry
         * position is stored in each base entry. All positions start
-        * from 1 instead of 0, which is resrved to say "this is a new
+        * from 1 instead of 0, which is reserved to say "this is a new
         * entry".
         */
        for (i = 0; i < base->cache_nr; i++)
index 90825e17fab5872d2e4c94a3cab84c696eb69248..4532b11d66f8cdc0abdf003b3a16a632bd923890 100644 (file)
@@ -13,6 +13,7 @@
 #include "argv-array.h"
 #include "blob.h"
 #include "thread-utils.h"
+#include "quote.h"
 
 static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
 static int parallel_jobs = 1;
@@ -237,6 +238,27 @@ int parse_submodule_update_strategy(const char *value,
        return 0;
 }
 
+const char *submodule_strategy_to_string(const struct submodule_update_strategy *s)
+{
+       struct strbuf sb = STRBUF_INIT;
+       switch (s->type) {
+       case SM_UPDATE_CHECKOUT:
+               return "checkout";
+       case SM_UPDATE_MERGE:
+               return "merge";
+       case SM_UPDATE_REBASE:
+               return "rebase";
+       case SM_UPDATE_NONE:
+               return "none";
+       case SM_UPDATE_UNSPECIFIED:
+               return NULL;
+       case SM_UPDATE_COMMAND:
+               strbuf_addf(&sb, "!%s", s->command);
+               return strbuf_detach(&sb, NULL);
+       }
+       return NULL;
+}
+
 void handle_ignore_submodules_arg(struct diff_options *diffopt,
                                  const char *arg)
 {
@@ -393,7 +415,7 @@ static int submodule_needs_pushing(const char *path, const unsigned char sha1[20
 
                argv[1] = sha1_to_hex(sha1);
                cp.argv = argv;
-               cp.env = local_repo_env;
+               prepare_submodule_repo_env(&cp.env_array);
                cp.git_cmd = 1;
                cp.no_stdin = 1;
                cp.out = -1;
@@ -480,7 +502,7 @@ static int push_submodule(const char *path)
                const char *argv[] = {"push", NULL};
 
                cp.argv = argv;
-               cp.env = local_repo_env;
+               prepare_submodule_repo_env(&cp.env_array);
                cp.git_cmd = 1;
                cp.no_stdin = 1;
                cp.dir = path;
@@ -526,7 +548,7 @@ static int is_submodule_commit_present(const char *path, unsigned char sha1[20])
 
                argv[3] = sha1_to_hex(sha1);
                cp.argv = argv;
-               cp.env = local_repo_env;
+               prepare_submodule_repo_env(&cp.env_array);
                cp.git_cmd = 1;
                cp.no_stdin = 1;
                cp.dir = path;
@@ -709,7 +731,7 @@ static int get_next_submodule(struct child_process *cp,
                if (is_directory(git_dir)) {
                        child_process_init(cp);
                        cp->dir = strbuf_detach(&submodule_path, NULL);
-                       cp->env = local_repo_env;
+                       prepare_submodule_repo_env(&cp->env_array);
                        cp->git_cmd = 1;
                        if (!spf->quiet)
                                strbuf_addf(err, "Fetching submodule %s%s\n",
@@ -824,7 +846,7 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
                argv[2] = "-uno";
 
        cp.argv = argv;
-       cp.env = local_repo_env;
+       prepare_submodule_repo_env(&cp.env_array);
        cp.git_cmd = 1;
        cp.no_stdin = 1;
        cp.out = -1;
@@ -885,7 +907,7 @@ int submodule_uses_gitfile(const char *path)
 
        /* Now test that all nested submodules use a gitfile too */
        cp.argv = argv;
-       cp.env = local_repo_env;
+       prepare_submodule_repo_env(&cp.env_array);
        cp.git_cmd = 1;
        cp.no_stdin = 1;
        cp.no_stderr = 1;
@@ -918,7 +940,7 @@ int ok_to_remove_submodule(const char *path)
                return 0;
 
        cp.argv = argv;
-       cp.env = local_repo_env;
+       prepare_submodule_repo_env(&cp.env_array);
        cp.git_cmd = 1;
        cp.no_stdin = 1;
        cp.out = -1;
@@ -1129,3 +1151,13 @@ int parallel_submodules(void)
 {
        return parallel_jobs;
 }
+
+void prepare_submodule_repo_env(struct argv_array *out)
+{
+       const char * const *var;
+
+       for (var = local_repo_env; *var; var++) {
+               if (strcmp(*var, CONFIG_DATA_ENVIRONMENT))
+                       argv_array_push(out, *var);
+       }
+}
index 7ef3775184e1a54bbbebc5b940977c2b61cb914f..2af9390998194c7d51a3e23b14723574d6696926 100644 (file)
@@ -39,6 +39,7 @@ int submodule_config(const char *var, const char *value, void *cb);
 void gitmodules_config(void);
 int parse_submodule_update_strategy(const char *value,
                struct submodule_update_strategy *dst);
+const char *submodule_strategy_to_string(const struct submodule_update_strategy *s);
 void handle_ignore_submodules_arg(struct diff_options *diffopt, const char *);
 void show_submodule_summary(FILE *f, const char *path,
                const char *line_prefix,
@@ -61,4 +62,11 @@ int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_nam
 void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
 int parallel_submodules(void);
 
+/*
+ * Prepare the "env_array" parameter of a "struct child_process" for executing
+ * a submodule by clearing any repo-specific envirionment variables, but
+ * retaining any config in the environment.
+ */
+void prepare_submodule_repo_env(struct argv_array *out);
+
 #endif
index 1dc908e43a993502bed611023448aa76fc9da6ad..76a0daa3ac00d298bc515c655f4548403095f5f3 100644 (file)
--- a/t/README
+++ b/t/README
@@ -84,9 +84,9 @@ appropriately before running "make".
 
 -x::
        Turn on shell tracing (i.e., `set -x`) during the tests
-       themselves. Implies `--verbose`. Note that this can cause
-       failures in some tests which redirect and test the
-       output of shell functions. Use with caution.
+       themselves. Implies `--verbose`. Note that in non-bash shells,
+       this can cause failures in some tests which redirect and test
+       the output of shell functions. Use with caution.
 
 -d::
 --debug::
index 2c8c8f18edb46378b39c170b4ae6f7250ec631f5..8a1235d03e2daab4b9e90d31c5da6708e5c712f2 100644 (file)
@@ -7,16 +7,25 @@ static int integer = 0;
 static unsigned long magnitude = 0;
 static unsigned long timestamp;
 static int abbrev = 7;
-static int verbose = 0, dry_run = 0, quiet = 0;
+static int verbose = -1; /* unspecified */
+static int dry_run = 0, quiet = 0;
 static char *string = NULL;
 static char *file = NULL;
 static int ambiguous;
 static struct string_list list;
 
+static struct {
+       int called;
+       const char *arg;
+       int unset;
+} length_cb;
+
 static int length_callback(const struct option *opt, const char *arg, int unset)
 {
-       printf("Callback: \"%s\", %d\n",
-               (arg ? arg : "not set"), unset);
+       length_cb.called = 1;
+       length_cb.arg = arg;
+       length_cb.unset = unset;
+
        if (unset)
                return 1; /* do not support unset */
 
@@ -30,6 +39,61 @@ static int number_callback(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
+static int collect_expect(const struct option *opt, const char *arg, int unset)
+{
+       struct string_list *expect;
+       struct string_list_item *item;
+       struct strbuf label = STRBUF_INIT;
+       const char *colon;
+
+       if (!arg || unset)
+               die("malformed --expect option");
+
+       expect = (struct string_list *)opt->value;
+       colon = strchr(arg, ':');
+       if (!colon)
+               die("malformed --expect option, lacking a colon");
+       strbuf_add(&label, arg, colon - arg);
+       item = string_list_insert(expect, strbuf_detach(&label, NULL));
+       if (item->util)
+               die("malformed --expect option, duplicate %s", label.buf);
+       item->util = (void *)arg;
+       return 0;
+}
+
+__attribute__((format (printf,3,4)))
+static void show(struct string_list *expect, int *status, const char *fmt, ...)
+{
+       struct string_list_item *item;
+       struct strbuf buf = STRBUF_INIT;
+       va_list args;
+
+       va_start(args, fmt);
+       strbuf_vaddf(&buf, fmt, args);
+       va_end(args);
+
+       if (!expect->nr)
+               printf("%s\n", buf.buf);
+       else {
+               char *colon = strchr(buf.buf, ':');
+               if (!colon)
+                       die("malformed output format, output lacking colon: %s", fmt);
+               *colon = '\0';
+               item = string_list_lookup(expect, buf.buf);
+               *colon = ':';
+               if (!item)
+                       ; /* not among entries being checked */
+               else {
+                       if (strcmp((const char *)item->util, buf.buf)) {
+                               printf("-%s\n", (char *)item->util);
+                               printf("+%s\n", buf.buf);
+                               *status = 1;
+                       }
+               }
+       }
+       strbuf_release(&buf);
+}
+
 int main(int argc, char **argv)
 {
        const char *prefix = "prefix/";
@@ -37,6 +101,7 @@ int main(int argc, char **argv)
                "test-parse-options <options>",
                NULL
        };
+       struct string_list expect = STRING_LIST_INIT_NODUP;
        struct option options[] = {
                OPT_BOOL(0, "yes", &boolean, "get a boolean"),
                OPT_BOOL('D', "no-doubt", &boolean, "begins with 'no-'"),
@@ -77,28 +142,38 @@ int main(int argc, char **argv)
                OPT__VERBOSE(&verbose, "be verbose"),
                OPT__DRY_RUN(&dry_run, "dry run"),
                OPT__QUIET(&quiet, "be quiet"),
+               OPT_CALLBACK(0, "expect", &expect, "string",
+                            "expected output in the variable dump",
+                            collect_expect),
                OPT_END(),
        };
        int i;
+       int ret = 0;
 
        argc = parse_options(argc, (const char **)argv, prefix, options, usage, 0);
 
-       printf("boolean: %d\n", boolean);
-       printf("integer: %d\n", integer);
-       printf("magnitude: %lu\n", magnitude);
-       printf("timestamp: %lu\n", timestamp);
-       printf("string: %s\n", string ? string : "(not set)");
-       printf("abbrev: %d\n", abbrev);
-       printf("verbose: %d\n", verbose);
-       printf("quiet: %s\n", quiet ? "yes" : "no");
-       printf("dry run: %s\n", dry_run ? "yes" : "no");
-       printf("file: %s\n", file ? file : "(not set)");
+       if (length_cb.called) {
+               const char *arg = length_cb.arg;
+               int unset = length_cb.unset;
+               show(&expect, &ret, "Callback: \"%s\", %d",
+                    (arg ? arg : "not set"), unset);
+       }
+       show(&expect, &ret, "boolean: %d", boolean);
+       show(&expect, &ret, "integer: %d", integer);
+       show(&expect, &ret, "magnitude: %lu", magnitude);
+       show(&expect, &ret, "timestamp: %lu", timestamp);
+       show(&expect, &ret, "string: %s", string ? string : "(not set)");
+       show(&expect, &ret, "abbrev: %d", abbrev);
+       show(&expect, &ret, "verbose: %d", verbose);
+       show(&expect, &ret, "quiet: %d", quiet);
+       show(&expect, &ret, "dry run: %s", dry_run ? "yes" : "no");
+       show(&expect, &ret, "file: %s", file ? file : "(not set)");
 
        for (i = 0; i < list.nr; i++)
-               printf("list: %s\n", list.items[i].string);
+               show(&expect, &ret, "list: %s", list.items[i].string);
 
        for (i = 0; i < argc; i++)
-               printf("arg %02d: %s\n", i, argv[i]);
+               show(&expect, &ret, "arg %02d: %s", i, argv[i]);
 
-       return 0;
+       return ret;
 }
index b8ed96fac6e2e8a371b7caf8716b671585022382..018a83a5a18431f120672b7cd8874bfb72e8d8fb 100644 (file)
@@ -103,10 +103,6 @@ Alias /auth/dumb/ www/auth/dumb/
        Header set Set-Cookie name=value
 </LocationMatch>
 <LocationMatch /smart_headers/>
-       <RequireAll>
-               Require expr %{HTTP:x-magic-one} == 'abra'
-               Require expr %{HTTP:x-magic-two} == 'cadabra'
-       </RequireAll>
        SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
        SetEnv GIT_HTTP_EXPORT_ALL
 </LocationMatch>
@@ -136,6 +132,18 @@ RewriteRule ^/ftp-redir/(.*)$ ftp://localhost:1000/$1 [R=302]
 RewriteRule ^/loop-redir/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-(.*) /$1 [R=302]
 RewriteRule ^/loop-redir/(.*)$ /loop-redir/x-$1 [R=302]
 
+# Apache 2.2 does not understand <RequireAll>, so we use RewriteCond.
+# And as RewriteCond does not allow testing for non-matches, we match
+# the desired case first (one has abra, two has cadabra), and let it
+# pass by marking the RewriteRule as [L], "last rule, do not process
+# any other matching RewriteRules after this"), and then have another
+# RewriteRule that matches all other cases and lets them fail via '[F]',
+# "fail the request".
+RewriteCond %{HTTP:x-magic-one} =abra
+RewriteCond %{HTTP:x-magic-two} =cadabra
+RewriteRule ^/smart_headers/.* - [L]
+RewriteRule ^/smart_headers/.* - [F]
+
 <IfDefine SSL>
 LoadModule ssl_module modules/mod_ssl.so
 
diff --git a/t/perf/p3404-rebase-interactive.sh b/t/perf/p3404-rebase-interactive.sh
new file mode 100755 (executable)
index 0000000..88f47de
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+test_description='Tests rebase -i performance'
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+# This commit merges a sufficiently long topic branch for reasonable
+# performance testing
+branch_merge=ba5312da19c6fdb6c6747d479f58932aae6e900c^{commit}
+export branch_merge
+
+git rev-parse --verify $branch_merge >/dev/null 2>&1 || {
+       skip_all='skipping because $branch_merge was not found'
+       test_done
+}
+
+write_script swap-first-two.sh <<\EOF
+case "$1" in
+*/COMMIT_EDITMSG)
+       mv "$1" "$1".bak &&
+       sed -e '1{h;d}' -e 2G <"$1".bak >"$1"
+       ;;
+esac
+EOF
+
+test_expect_success 'setup' '
+       git config core.editor "\"$PWD"/swap-first-two.sh\" &&
+       git checkout -f $branch_merge^2
+'
+
+test_perf 'rebase -i' '
+       git rebase -i $branch_merge^
+'
+
+test_done
index 5cf74eddec7552140c5bef14f23fc637a2ad9818..5ef17440c0e1e63102adddba958686c28b4e80ec 100644 (file)
@@ -80,23 +80,26 @@ test_perf_create_repo_from () {
        error "bug in the test script: not 2 parameters to test-create-repo"
        repo="$1"
        source="$2"
-       source_git=$source/$(cd "$source" && git rev-parse --git-dir)
+       source_git="$(git -C "$source" rev-parse --git-dir)"
+       objects_dir="$(git -C "$source" rev-parse --git-path objects)"
        mkdir -p "$repo/.git"
        (
-               cd "$repo/.git" &&
-               { cp -Rl "$source_git/objects" . 2>/dev/null ||
-                       cp -R "$source_git/objects" .; } &&
+               { cp -Rl "$objects_dir" "$repo/.git/" 2>/dev/null ||
+                       cp -R "$objects_dir" "$repo/.git/"; } &&
                for stuff in "$source_git"/*; do
                        case "$stuff" in
-                               */objects|*/hooks|*/config)
+                               */objects|*/hooks|*/config|*/commondir)
                                        ;;
                                *)
-                                       cp -R "$stuff" . || exit 1
+                                       cp -R "$stuff" "$repo/.git/" || exit 1
                                        ;;
                        esac
                done &&
-               cd .. &&
-               git init -q &&
+               cd "$repo" &&
+               git init -q && {
+                       test_have_prereq SYMLINKS ||
+                       git config core.symlinks false
+               } &&
                mv .git/hooks .git/hooks-disabled 2>/dev/null
        ) || error "failed to copy repository '$source' to '$repo'"
 }
index 79b9074172ce00771c92a8f3cda35ebf4ac24d89..60811a3a7ca56f995ecda5944121127e2a330bc0 100755 (executable)
@@ -98,7 +98,7 @@ check_sub_test_lib_test () {
 }
 
 check_sub_test_lib_test_err () {
-       name="$1" # stdin is the expected output output from the test
+       name="$1" # stdin is the expected output from the test
        # expected error output is in descriptior 3
        (
                cd "$name" &&
index a5b9e7a4c7e85a2f09718ee7618ae6fb48c600fe..a6fdd5ef3a66f06f5b4a787ca8ad8db2ab4da96f 100755 (executable)
@@ -354,4 +354,34 @@ test_expect_success SYMLINKS 're-init to move gitdir symlink' '
        test_path_is_dir realgitdir/refs
 '
 
+# Tests for the hidden file attribute on windows
+is_hidden () {
+       # Use the output of `attrib`, ignore the absolute path
+       case "$(attrib "$1")" in *H*?:*) return 0;; esac
+       return 1
+}
+
+test_expect_success MINGW '.git hidden' '
+       rm -rf newdir &&
+       (
+               unset GIT_DIR GIT_WORK_TREE
+               mkdir newdir &&
+               cd newdir &&
+               git init &&
+               is_hidden .git
+       ) &&
+       check_config newdir/.git false unset
+'
+
+test_expect_success MINGW 'bare git dir not hidden' '
+       rm -rf newdir &&
+       (
+               unset GIT_DIR GIT_WORK_TREE GIT_CONFIG
+               mkdir newdir &&
+               cd newdir &&
+               git --bare init
+       ) &&
+       ! is_hidden newdir
+'
+
 test_done
index f33962b178238126d4cbe88cfbda7dd7bb747490..93725895a4050dca835fd61ef5eee75aa1174ed5 100755 (executable)
@@ -12,7 +12,7 @@ fi
 
 compare_files () {
        tr '\015\000' QN <"$1" >"$1".expect &&
-       tr '\015\000' QN <"$2" >"$2".actual &&
+       tr '\015\000' QN <"$2" | tr -d 'Z' >"$2".actual &&
        test_cmp "$1".expect "$2".actual &&
        rm "$1".expect "$2".actual
 }
@@ -52,14 +52,17 @@ create_gitattributes () {
 create_NNO_files () {
        for crlf in false true input
        do
-               for attr in "" auto text -text lf crlf
+               for attr in "" auto text -text
                do
-                       pfx=NNO_${crlf}_attr_${attr} &&
-                       cp CRLF_mix_LF ${pfx}_LF.txt &&
-                       cp CRLF_mix_LF ${pfx}_CRLF.txt &&
-                       cp CRLF_mix_LF ${pfx}_CRLF_mix_LF.txt &&
-                       cp CRLF_mix_LF ${pfx}_LF_mix_CR.txt &&
-                       cp CRLF_mix_LF ${pfx}_CRLF_nul.txt
+                       for aeol in "" lf crlf
+                       do
+                               pfx=NNO_attr_${attr}_aeol_${aeol}_${crlf}
+                               cp CRLF_mix_LF ${pfx}_LF.txt &&
+                               cp CRLF_mix_LF ${pfx}_CRLF.txt &&
+                               cp CRLF_mix_LF ${pfx}_CRLF_mix_LF.txt &&
+                               cp CRLF_mix_LF ${pfx}_LF_mix_CR.txt &&
+                               cp CRLF_mix_LF ${pfx}_CRLF_nul.txt
+                       done
                done
        done
 }
@@ -100,20 +103,22 @@ commit_check_warn () {
 }
 
 commit_chk_wrnNNO () {
-       crlf=$1
-       attr=$2
-       lfwarn=$3
-       crlfwarn=$4
-       lfmixcrlf=$5
-       lfmixcr=$6
-       crlfnul=$7
-       pfx=NNO_${crlf}_attr_${attr}
+       attr=$1 ; shift
+       aeol=$1 ; shift
+       crlf=$1 ; shift
+       lfwarn=$1 ; shift
+       crlfwarn=$1 ; shift
+       lfmixcrlf=$1 ; shift
+       lfmixcr=$1 ; shift
+       crlfnul=$1 ; shift
+       pfx=NNO_attr_${attr}_aeol_${aeol}_${crlf}
        #Commit files on top of existing file
-       create_gitattributes "$attr" &&
+       create_gitattributes "$attr" $aeol &&
        for f in LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
        do
                fname=${pfx}_$f.txt &&
                cp $f $fname &&
+               printf Z >>"$fname" &&
                git -c core.autocrlf=$crlf add $fname 2>/dev/null &&
                git -c core.autocrlf=$crlf commit -m "commit_$fname" $fname >"${pfx}_$f.err" 2>&1
        done
@@ -121,19 +126,19 @@ commit_chk_wrnNNO () {
        test_expect_success "commit NNO files crlf=$crlf attr=$attr LF" '
                check_warning "$lfwarn" ${pfx}_LF.err
        '
-       test_expect_success "commit NNO files crlf=$crlf attr=$attr CRLF" '
+       test_expect_success "commit NNO files attr=$attr aeol=$aeol crlf=$crlf CRLF" '
                check_warning "$crlfwarn" ${pfx}_CRLF.err
        '
 
-       test_expect_success "commit NNO files crlf=$crlf attr=$attr CRLF_mix_LF" '
+       test_expect_success "commit NNO files attr=$attr aeol=$aeol crlf=$crlf CRLF_mix_LF" '
                check_warning "$lfmixcrlf" ${pfx}_CRLF_mix_LF.err
        '
 
-       test_expect_success "commit NNO files crlf=$crlf attr=$attr LF_mix_cr" '
+       test_expect_success "commit NNO files attr=$attr aeol=$aeol crlf=$crlf LF_mix_cr" '
                check_warning "$lfmixcr" ${pfx}_LF_mix_CR.err
        '
 
-       test_expect_success "commit NNO files crlf=$crlf attr=$attr CRLF_nul" '
+       test_expect_success "commit NNO files attr=$attr aeol=$aeol crlf=$crlf CRLF_nul" '
                check_warning "$crlfnul" ${pfx}_CRLF_nul.err
        '
 }
@@ -162,6 +167,7 @@ stats_ascii () {
 
 # contruct the attr/ returned by git ls-files --eol
 # Take none (=empty), one or two args
+# convert.c: eol=XX overrides text=auto
 attr_ascii () {
        case $1,$2 in
        -text,*)   echo "-text" ;;
@@ -169,8 +175,8 @@ attr_ascii () {
        text,lf)   echo "text eol=lf" ;;
        text,crlf) echo "text eol=crlf" ;;
        auto,)     echo "text=auto" ;;
-       auto,lf)   echo "text=auto eol=lf" ;;
-       auto,crlf) echo "text=auto eol=crlf" ;;
+       auto,lf)   echo "text eol=lf" ;;
+       auto,crlf) echo "text eol=crlf" ;;
        lf,)       echo "text eol=lf" ;;
        crlf,)     echo "text eol=crlf" ;;
        ,) echo "" ;;
@@ -195,28 +201,29 @@ check_files_in_repo () {
 }
 
 check_in_repo_NNO () {
-       crlf=$1
-       attr=$2
-       lfname=$3
-       crlfname=$4
-       lfmixcrlf=$5
-       lfmixcr=$6
-       crlfnul=$7
-       pfx=NNO_${crlf}_attr_${attr}_
-       test_expect_success "compare_files $lfname ${pfx}LF.txt" '
-               compare_files $lfname ${pfx}LF.txt
+       attr=$1 ; shift
+       aeol=$1 ; shift
+       crlf=$1 ; shift
+       lfname=$1 ; shift
+       crlfname=$1 ; shift
+       lfmixcrlf=$1 ; shift
+       lfmixcr=$1 ; shift
+       crlfnul=$1 ; shift
+       pfx=NNO_attr_${attr}_aeol_${aeol}_${crlf}
+       test_expect_success "compare_files $lfname ${pfx}_LF.txt" '
+               compare_files $lfname ${pfx}_LF.txt
        '
-       test_expect_success "compare_files $crlfname ${pfx}CRLF.txt" '
-               compare_files $crlfname ${pfx}CRLF.txt
+       test_expect_success "compare_files $crlfname ${pfx}_CRLF.txt" '
+               compare_files $crlfname ${pfx}_CRLF.txt
        '
-       test_expect_success "compare_files $lfmixcrlf ${pfx}CRLF_mix_LF.txt" '
-               compare_files $lfmixcrlf ${pfx}CRLF_mix_LF.txt
+       test_expect_success "compare_files $lfmixcrlf ${pfx}_CRLF_mix_LF.txt" '
+               compare_files $lfmixcrlf ${pfx}_CRLF_mix_LF.txt
        '
-       test_expect_success "compare_files $lfmixcr ${pfx}LF_mix_CR.txt" '
-               compare_files $lfmixcr ${pfx}LF_mix_CR.txt
+       test_expect_success "compare_files $lfmixcr ${pfx}_LF_mix_CR.txt" '
+               compare_files $lfmixcr ${pfx}_LF_mix_CR.txt
        '
-       test_expect_success "compare_files $crlfnul ${pfx}CRLF_nul.txt" '
-               compare_files $crlfnul ${pfx}CRLF_nul.txt
+       test_expect_success "compare_files $crlfnul ${pfx}_CRLF_nul.txt" '
+               compare_files $crlfnul ${pfx}_CRLF_nul.txt
        '
 }
 
@@ -231,7 +238,7 @@ checkout_files () {
        lfmixcrlf=$1 ; shift
        lfmixcr=$1 ; shift
        crlfnul=$1 ; shift
-       create_gitattributes "$attr" "$ident" &&
+       create_gitattributes "$attr" $ident $aeol &&
        git config core.autocrlf $crlf &&
        pfx=eol_${ceol}_crlf_${crlf}_attr_${attr}_ &&
        for f in LF CRLF LF_mix_CR CRLF_mix_LF LF_nul
@@ -244,7 +251,7 @@ checkout_files () {
                fi
        done
 
-       test_expect_success "ls-files --eol attr=$attr $ident $aeol core.autocrlf=$crlf core.eol=$ceol" '
+       test_expect_success "ls-files --eol attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol" '
                test_when_finished "rm expect actual" &&
                sort <<-EOF >expect &&
                i/crlf w/$(stats_ascii $crlfname) attr/$(attr_ascii $attr $aeol) crlf_false_attr__CRLF.txt
@@ -259,19 +266,19 @@ checkout_files () {
                sort >actual &&
                test_cmp expect actual
        '
-       test_expect_success "checkout $ident $attr $aeol core.autocrlf=$crlf core.eol=$ceol file=LF" "
+       test_expect_success "checkout attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol file=LF" "
                compare_ws_file $pfx $lfname    crlf_false_attr__LF.txt
        "
-       test_expect_success "checkout $ident $attr $aeol core.autocrlf=$crlf core.eol=$ceol file=CRLF" "
+       test_expect_success "checkout attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol file=CRLF" "
                compare_ws_file $pfx $crlfname  crlf_false_attr__CRLF.txt
        "
-       test_expect_success "checkout $ident $attr $aeol core.autocrlf=$crlf core.eol=$ceol file=CRLF_mix_LF" "
+       test_expect_success "checkout attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol file=CRLF_mix_LF" "
                compare_ws_file $pfx $lfmixcrlf crlf_false_attr__CRLF_mix_LF.txt
        "
-       test_expect_success "checkout $ident $attr $aeol core.autocrlf=$crlf core.eol=$ceol file=LF_mix_CR" "
+       test_expect_success "checkout attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol file=LF_mix_CR" "
                compare_ws_file $pfx $lfmixcr   crlf_false_attr__LF_mix_CR.txt
        "
-       test_expect_success "checkout $ident $attr $aeol core.autocrlf=$crlf core.eol=$ceol file=LF_nul" "
+       test_expect_success "checkout attr=$attr $ident aeol=$aeol core.autocrlf=$crlf core.eol=$ceol file=LF_nul" "
                compare_ws_file $pfx $crlfnul   crlf_false_attr__LF_nul.txt
        "
 }
@@ -385,31 +392,31 @@ test_expect_success 'commit files attr=crlf' '
        commit_check_warn input "crlf" "LF_CRLF" ""        "LF_CRLF" "LF_CRLF" ""
 '
 
-#                       attr   LF        CRLF      CRLFmixLF    LF_mix_CR   CRLFNUL
-commit_chk_wrnNNO false ""     ""        ""        ""           ""              ""
-commit_chk_wrnNNO true  ""     "LF_CRLF" ""        ""           ""              ""
-commit_chk_wrnNNO input ""     ""        ""        ""           ""              ""
-
+#                 attr                    LF        CRLF      CRLFmixLF   LF_mix_CR   CRLFNUL
+commit_chk_wrnNNO ""      ""      false   ""        ""        ""          ""          ""
+commit_chk_wrnNNO ""      ""      true    LF_CRLF   ""        ""          ""          ""
+commit_chk_wrnNNO ""      ""      input   ""        ""        ""          ""          ""
 
-commit_chk_wrnNNO false "auto" "$WILC"   "$WICL"   "$WAMIX"     ""              ""
-commit_chk_wrnNNO true  "auto" "LF_CRLF" ""        "LF_CRLF"    ""              ""
-commit_chk_wrnNNO input "auto" ""        "CRLF_LF" "CRLF_LF"    ""              ""
+commit_chk_wrnNNO "auto"  ""      false   "$WILC"   "$WICL"   "$WAMIX"    ""          ""
+commit_chk_wrnNNO "auto"  ""      true    LF_CRLF   ""        LF_CRLF     ""          ""
+commit_chk_wrnNNO "auto"  ""      input   ""        CRLF_LF   CRLF_LF     ""          ""
 
-commit_chk_wrnNNO false "text" "$WILC"   "$WICL"   "$WAMIX"     "$WILC"         "$WICL"
-commit_chk_wrnNNO true  "text" "LF_CRLF" ""        "LF_CRLF"    "LF_CRLF"       ""
-commit_chk_wrnNNO input "text" ""        "CRLF_LF" "CRLF_LF"    ""              "CRLF_LF"
-
-commit_chk_wrnNNO false "-text" ""       ""        ""           ""              ""
-commit_chk_wrnNNO true  "-text" ""       ""        ""           ""              ""
-commit_chk_wrnNNO input "-text" ""       ""        ""           ""              ""
-
-commit_chk_wrnNNO false "lf"    ""       "CRLF_LF" "CRLF_LF"     ""             "CRLF_LF"
-commit_chk_wrnNNO true  "lf"    ""       "CRLF_LF" "CRLF_LF"     ""             "CRLF_LF"
-commit_chk_wrnNNO input "lf"    ""       "CRLF_LF" "CRLF_LF"     ""             "CRLF_LF"
+for crlf in true false input
+do
+       commit_chk_wrnNNO -text ""      $crlf   ""        ""        ""          ""          ""
+       commit_chk_wrnNNO -text lf      $crlf   ""        ""        ""          ""          ""
+       commit_chk_wrnNNO -text crlf    $crlf   ""        ""        ""          ""          ""
+       commit_chk_wrnNNO ""    lf      $crlf   ""       CRLF_LF    CRLF_LF      ""         CRLF_LF
+       commit_chk_wrnNNO ""    crlf    $crlf   LF_CRLF   ""        LF_CRLF     LF_CRLF     ""
+       commit_chk_wrnNNO auto  lf      $crlf   ""       CRLF_LF    CRLF_LF     ""          CRLF_LF
+       commit_chk_wrnNNO auto  crlf    $crlf   LF_CRLF   ""        LF_CRLF     LF_CRLF     ""
+       commit_chk_wrnNNO text  lf      $crlf   ""       CRLF_LF    CRLF_LF     ""          CRLF_LF
+       commit_chk_wrnNNO text  crlf    $crlf   LF_CRLF   ""        LF_CRLF     LF_CRLF     ""
+done
 
-commit_chk_wrnNNO false "crlf" "LF_CRLF" ""        "LF_CRLF"    "LF_CRLF"       ""
-commit_chk_wrnNNO true  "crlf" "LF_CRLF" ""        "LF_CRLF"    "LF_CRLF"       ""
-commit_chk_wrnNNO input "crlf" "LF_CRLF" ""        "LF_CRLF"    "LF_CRLF"       ""
+commit_chk_wrnNNO "text"  ""      false   "$WILC"   "$WICL"   "$WAMIX"    "$WILC"     "$WICL"
+commit_chk_wrnNNO "text"  ""      true    LF_CRLF   ""        LF_CRLF     LF_CRLF     ""
+commit_chk_wrnNNO "text"  ""      input   ""        CRLF_LF   CRLF_LF     ""          CRLF_LF
 
 test_expect_success 'create files cleanup' '
        rm -f *.txt &&
@@ -440,24 +447,20 @@ test_expect_success 'commit -text' '
        check_files_in_repo input "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
 '
 
-#                       attr    LF        CRLF      CRLF_mix_LF  LF_mix_CR     CRLFNUL
-check_in_repo_NNO false ""      LF        CRLF      CRLF_mix_LF  LF_mix_CR     CRLF_nul
-check_in_repo_NNO true  ""      LF        CRLF      CRLF_mix_LF  LF_mix_CR     CRLF_nul
-check_in_repo_NNO input ""      LF        CRLF      CRLF_mix_LF  LF_mix_CR     CRLF_nul
-
-check_in_repo_NNO false "auto"  LF        LF        LF           LF_mix_CR     CRLF_nul
-check_in_repo_NNO true  "auto"  LF        LF        LF           LF_mix_CR     CRLF_nul
-check_in_repo_NNO input "auto"  LF        LF        LF           LF_mix_CR     CRLF_nul
-
-check_in_repo_NNO false "text"  LF        LF        LF           LF_mix_CR     LF_nul
-check_in_repo_NNO true  "text"  LF        LF        LF           LF_mix_CR     LF_nul
-check_in_repo_NNO input "text"  LF        LF        LF           LF_mix_CR     LF_nul
-
-check_in_repo_NNO false "-text" LF        CRLF      CRLF_mix_LF  LF_mix_CR     CRLF_nul
-check_in_repo_NNO true  "-text" LF        CRLF      CRLF_mix_LF  LF_mix_CR     CRLF_nul
-check_in_repo_NNO input "-text" LF        CRLF      CRLF_mix_LF  LF_mix_CR     CRLF_nul
-
-
+for crlf in true false input
+do
+       #                 attr  aeol           LF  CRLF  CRLF_mix_LF  LF_mix_CR  CRLFNUL
+       check_in_repo_NNO ""    ""     $crlf   LF  CRLF  CRLF_mix_LF  LF_mix_CR  CRLF_nul
+       check_in_repo_NNO -text ""     $crlf   LF  CRLF  CRLF_mix_LF  LF_mix_CR  CRLF_nul
+       check_in_repo_NNO -text lf     $crlf   LF  CRLF  CRLF_mix_LF  LF_mix_CR  CRLF_nul
+       check_in_repo_NNO -text crlf   $crlf   LF  CRLF  CRLF_mix_LF  LF_mix_CR  CRLF_nul
+       check_in_repo_NNO auto  ""     $crlf   LF  LF    LF           LF_mix_CR  CRLF_nul
+       check_in_repo_NNO auto  lf     $crlf   LF  LF    LF           LF_mix_CR  LF_nul
+       check_in_repo_NNO auto  crlf   $crlf   LF  LF    LF           LF_mix_CR  LF_nul
+       check_in_repo_NNO text  ""     $crlf   LF  LF    LF           LF_mix_CR  LF_nul
+       check_in_repo_NNO text  lf     $crlf   LF  LF    LF           LF_mix_CR  LF_nul
+       check_in_repo_NNO text  crlf   $crlf   LF  LF    LF           LF_mix_CR  LF_nul
+done
 ################################################################################
 # Check how files in the repo are changed when they are checked out
 # How to read the table below:
@@ -489,89 +492,47 @@ LFNUL=LF_nul
 fi
 export CRLF_MIX_LF_CR MIX NL
 
-checkout_files ""      ""       ""    false  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ""       ""    false  crlf     LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ""       ""    false  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ""       ""    false  native   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ""       ""    input  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ""       ""    input  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ""       ""    true   ""       CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ""       ""    true   crlf     CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ""       ""    true   lf       CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ""       ""    true   native   CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ident ""    false  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ident ""    false  crlf     LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ident ""    false  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ident ""    false  native   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ident ""    input  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ident ""    input  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ident ""    true   ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ident ""    true   crlf     LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ident ""    true   lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files ""      ident ""    true   native   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ""       ""    false  ""       $NL   CRLF  $MIX_CRLF_LF LF_mix_CR    LF_nul
-checkout_files "auto"  ""       ""    false  crlf     CRLF  CRLF  CRLF         LF_mix_CR    LF_nul
-checkout_files "auto"  ""       ""    false  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ""       ""    false  native   $NL   CRLF  $MIX_CRLF_LF LF_mix_CR    LF_nul
-checkout_files "auto"  ""       ""    input  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ""       ""    input  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ""       ""    true   ""       CRLF  CRLF  CRLF         LF_mix_CR    LF_nul
-checkout_files "auto"  ""       ""    true   crlf     CRLF  CRLF  CRLF         LF_mix_CR    LF_nul
-checkout_files "auto"  ""       ""    true   lf       CRLF  CRLF  CRLF         LF_mix_CR    LF_nul
-checkout_files "auto"  ""       ""    true   native   CRLF  CRLF  CRLF         LF_mix_CR    LF_nul
-checkout_files "auto"  ident ""    false  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ident ""    false  crlf     LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ident ""    false  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ident ""    false  native   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ident ""    input  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ident ""    input  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ident ""    true   ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ident ""    true   crlf     LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ident ""    true   lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-checkout_files "auto"  ident ""    true   native   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-
-for id in "" ident;
+# Same handling with and without ident
+for id in "" ident
 do
-       checkout_files "crlf"  "$id" ""    false  ""       CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "crlf"  "$id" ""    false  crlf     CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "crlf"  "$id" ""    false  lf       CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "crlf"  "$id" ""    false  native   CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "crlf"  "$id" ""    input  ""       CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "crlf"  "$id" ""    input  lf       CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "crlf"  "$id" ""    true   ""       CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "crlf"  "$id" ""    true   crlf     CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "crlf"  "$id" ""    true   lf       CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "crlf"  "$id" ""    true   native   CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "lf"    "$id" ""    false  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "lf"    "$id" ""    false  crlf     LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "lf"    "$id" ""    false  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "lf"    "$id" ""    false  native   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "lf"    "$id" ""    input  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "lf"    "$id" ""    input  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "lf"    "$id" ""    true   ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "lf"    "$id" ""    true   crlf     LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "lf"    "$id" ""    true   lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "lf"    "$id" ""    true   native   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "text"  "$id" ""    false  ""       $NL   CRLF  $MIX_CRLF_LF $MIX_LF_CR   $LFNUL
-       checkout_files "text"  "$id" ""    false  crlf     CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "text"  "$id" ""    false  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "text"  "$id" ""    false  native   $NL   CRLF  $MIX_CRLF_LF $MIX_LF_CR   $LFNUL
-       checkout_files "text"  "$id" ""    input  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "text"  "$id" ""    input  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "text"  "$id" ""    true   ""       CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "text"  "$id" ""    true   crlf     CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "text"  "$id" ""    true   lf       CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "text"  "$id" ""    true   native   CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-       checkout_files "-text" "$id" ""    false  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "-text" "$id" ""    false  crlf     LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "-text" "$id" ""    false  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "-text" "$id" ""    false  native   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "-text" "$id" ""    input  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "-text" "$id" ""    input  lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "-text" "$id" ""    true   ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "-text" "$id" ""    true   crlf     LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "-text" "$id" ""    true   lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
-       checkout_files "-text" "$id" ""    true   native   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+       for ceol in lf crlf native
+       do
+               for crlf in true false input
+               do
+                       # -text overrides core.autocrlf and core.eol
+                       # text and eol=crlf or eol=lf override core.autocrlf and core.eol
+                       checkout_files -text "$id" ""     "$crlf" "$ceol"  LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+                       checkout_files -text "$id" "lf"   "$crlf" "$ceol"  LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+                       checkout_files -text "$id" "crlf" "$crlf" "$ceol"  LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+                       # text
+                       checkout_files text  "$id" "lf"   "$crlf" "$ceol"  LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+                       checkout_files text  "$id" "crlf" "$crlf" "$ceol"  CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+                       # currently the same as text, eol=XXX
+                       checkout_files auto  "$id" "lf"   "$crlf" "$ceol"  LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+                       checkout_files auto  "$id" "crlf" "$crlf" "$ceol"  CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+               done
+
+               # core.autocrlf false, different core.eol
+               checkout_files   ""    "$id" ""     false   "$ceol"  LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+               # core.autocrlf true
+               checkout_files   ""    "$id" ""     true    "$ceol"  CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+               # text: core.autocrlf = true overrides core.eol
+               checkout_files   auto  "$id" ""     true    "$ceol"  CRLF  CRLF  CRLF         LF_mix_CR    LF_nul
+               checkout_files   text  "$id" ""     true    "$ceol"  CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+               # text: core.autocrlf = input overrides core.eol
+               checkout_files   text  "$id" ""     input   "$ceol"  LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+               checkout_files   auto  "$id" ""     input   "$ceol"  LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+               # text=auto + eol=XXX
+       done
+       # text: core.autocrlf=false uses core.eol
+       checkout_files     text  "$id" ""     false   crlf     CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+       checkout_files     text  "$id" ""     false   lf       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+       # text: core.autocrlf=false and core.eol unset(or native) uses native eol
+       checkout_files     text  "$id" ""     false   ""       $NL   CRLF  $MIX_CRLF_LF $MIX_LF_CR   $LFNUL
+       checkout_files     text  "$id" ""     false   native   $NL   CRLF  $MIX_CRLF_LF $MIX_LF_CR   $LFNUL
+       # auto: core.autocrlf=false and core.eol unset(or native) uses native eol
+       checkout_files     auto  "$id" ""     false   ""       $NL   CRLF  $MIX_CRLF_LF LF_mix_CR    LF_nul
+       checkout_files     auto  "$id" ""     false   native   $NL   CRLF  $MIX_CRLF_LF LF_mix_CR    LF_nul
 done
 
 # Should be the last test case: remove some files from the worktree
index 9be6411104999959ff93276ec2bbfb065bfa71e7..db5f60d0c5882fc491ca571eb6385554148bbf3a 100755 (executable)
@@ -7,7 +7,7 @@ test_description='our own option parser'
 
 . ./test-lib.sh
 
-cat > expect << EOF
+cat >expect <<\EOF
 usage: test-parse-options <options>
 
     --yes                 get a boolean
@@ -45,63 +45,24 @@ Standard options
     -v, --verbose         be verbose
     -n, --dry-run         dry run
     -q, --quiet           be quiet
+    --expect <string>     expected output in the variable dump
 
 EOF
 
 test_expect_success 'test help' '
-       test_must_fail test-parse-options -h > output 2> output.err &&
+       test_must_fail test-parse-options -h >output 2>output.err &&
        test_must_be_empty output.err &&
        test_i18ncmp expect output
 '
 
 mv expect expect.err
 
-cat >expect.template <<EOF
-boolean: 0
-integer: 0
-magnitude: 0
-timestamp: 0
-string: (not set)
-abbrev: 7
-verbose: 0
-quiet: no
-dry run: no
-file: (not set)
-EOF
-
-check() {
+check () {
        what="$1" &&
        shift &&
        expect="$1" &&
        shift &&
-       sed "s/^$what .*/$what $expect/" <expect.template >expect &&
-       test-parse-options $* >output 2>output.err &&
-       test_must_be_empty output.err &&
-       test_cmp expect output
-}
-
-check_i18n() {
-       what="$1" &&
-       shift &&
-       expect="$1" &&
-       shift &&
-       sed "s/^$what .*/$what $expect/" <expect.template >expect &&
-       test-parse-options $* >output 2>output.err &&
-       test_must_be_empty output.err &&
-       test_i18ncmp expect output
-}
-
-check_unknown() {
-       case "$1" in
-       --*)
-               echo error: unknown option \`${1#--}\' >expect ;;
-       -*)
-               echo error: unknown switch \`${1#-}\' >expect ;;
-       esac &&
-       cat expect.err >>expect &&
-       test_must_fail test-parse-options $* >output 2>output.err &&
-       test_must_be_empty output &&
-       test_cmp expect output.err
+       test-parse-options --expect="$what $expect" "$@"
 }
 
 check_unknown_i18n() {
@@ -156,7 +117,7 @@ test_expect_success 'OPT_MAGNITUDE() 3giga' '
        check magnitude: 3221225472 -m 3g
 '
 
-cat > expect << EOF
+cat >expect <<\EOF
 boolean: 2
 integer: 1729
 magnitude: 16384
@@ -164,7 +125,7 @@ timestamp: 0
 string: 123
 abbrev: 7
 verbose: 2
-quiet: no
+quiet: 0
 dry run: yes
 file: prefix/my.file
 EOF
@@ -176,7 +137,7 @@ test_expect_success 'short options' '
        test_must_be_empty output.err
 '
 
-cat > expect << EOF
+cat >expect <<\EOF
 boolean: 2
 integer: 1729
 magnitude: 16384
@@ -184,7 +145,7 @@ timestamp: 0
 string: 321
 abbrev: 10
 verbose: 2
-quiet: no
+quiet: 0
 dry run: no
 file: prefix/fi.le
 EOF
@@ -204,15 +165,15 @@ test_expect_success 'missing required value' '
        test_expect_code 129 test-parse-options --file
 '
 
-cat > expect << EOF
+cat >expect <<\EOF
 boolean: 1
 integer: 13
 magnitude: 0
 timestamp: 0
 string: 123
 abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
 dry run: no
 file: (not set)
 arg 00: a1
@@ -222,32 +183,32 @@ EOF
 
 test_expect_success 'intermingled arguments' '
        test-parse-options a1 --string 123 b1 --boolean -j 13 -- --boolean \
-               > output 2> output.err &&
+               >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
-cat > expect << EOF
+cat >expect <<\EOF
 boolean: 0
 integer: 2
 magnitude: 0
 timestamp: 0
 string: (not set)
 abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
 dry run: no
 file: (not set)
 EOF
 
 test_expect_success 'unambiguously abbreviated option' '
-       test-parse-options --int 2 --boolean --no-bo > output 2> output.err &&
+       test-parse-options --int 2 --boolean --no-bo >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
 test_expect_success 'unambiguously abbreviated option with "="' '
-       test-parse-options --int=2 > output 2> output.err &&
+       test-parse-options --int=2 >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
@@ -256,74 +217,74 @@ test_expect_success 'ambiguously abbreviated option' '
        test_expect_code 129 test-parse-options --strin 123
 '
 
-cat > expect << EOF
+cat >expect <<\EOF
 boolean: 0
 integer: 0
 magnitude: 0
 timestamp: 0
 string: 123
 abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
 dry run: no
 file: (not set)
 EOF
 
 test_expect_success 'non ambiguous option (after two options it abbreviates)' '
-       test-parse-options --st 123 > output 2> output.err &&
+       test-parse-options --st 123 >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
-cat > typo.err << EOF
-error: did you mean \`--boolean\` (with two dashes ?)
+cat >typo.err <<\EOF
+error: did you mean `--boolean` (with two dashes ?)
 EOF
 
 test_expect_success 'detect possible typos' '
-       test_must_fail test-parse-options -boolean > output 2> output.err &&
+       test_must_fail test-parse-options -boolean >output 2>output.err &&
        test_must_be_empty output &&
        test_cmp typo.err output.err
 '
 
-cat > typo.err << EOF
-error: did you mean \`--ambiguous\` (with two dashes ?)
+cat >typo.err <<\EOF
+error: did you mean `--ambiguous` (with two dashes ?)
 EOF
 
 test_expect_success 'detect possible typos' '
-       test_must_fail test-parse-options -ambiguous > output 2> output.err &&
+       test_must_fail test-parse-options -ambiguous >output 2>output.err &&
        test_must_be_empty output &&
        test_cmp typo.err output.err
 '
 
-cat > expect <<EOF
+cat >expect <<\EOF
 boolean: 0
 integer: 0
 magnitude: 0
 timestamp: 0
 string: (not set)
 abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
 dry run: no
 file: (not set)
 arg 00: --quux
 EOF
 
 test_expect_success 'keep some options as arguments' '
-       test-parse-options --quux > output 2> output.err &&
+       test-parse-options --quux >output 2>output.err &&
        test_must_be_empty output.err &&
-        test_cmp expect output
+       test_cmp expect output
 '
 
-cat > expect <<EOF
+cat >expect <<\EOF
 boolean: 0
 integer: 0
 magnitude: 0
 timestamp: 1
 string: (not set)
 abbrev: 7
-verbose: 0
-quiet: yes
+verbose: -1
+quiet: 1
 dry run: no
 file: (not set)
 arg 00: foo
@@ -331,12 +292,12 @@ EOF
 
 test_expect_success 'OPT_DATE() works' '
        test-parse-options -t "1970-01-01 00:00:01 +0000" \
-               foo -q > output 2> output.err &&
+               foo -q >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
-cat > expect <<EOF
+cat >expect <<\EOF
 Callback: "four", 0
 boolean: 5
 integer: 4
@@ -344,112 +305,110 @@ magnitude: 0
 timestamp: 0
 string: (not set)
 abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
 dry run: no
 file: (not set)
 EOF
 
 test_expect_success 'OPT_CALLBACK() and OPT_BIT() work' '
-       test-parse-options --length=four -b -4 > output 2> output.err &&
+       test-parse-options --length=four -b -4 >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
-cat > expect <<EOF
-Callback: "not set", 1
-EOF
+>expect
 
 test_expect_success 'OPT_CALLBACK() and callback errors work' '
-       test_must_fail test-parse-options --no-length > output 2> output.err &&
+       test_must_fail test-parse-options --no-length >output 2>output.err &&
        test_i18ncmp expect output &&
        test_i18ncmp expect.err output.err
 '
 
-cat > expect <<EOF
+cat >expect <<\EOF
 boolean: 1
 integer: 23
 magnitude: 0
 timestamp: 0
 string: (not set)
 abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
 dry run: no
 file: (not set)
 EOF
 
 test_expect_success 'OPT_BIT() and OPT_SET_INT() work' '
-       test-parse-options --set23 -bbbbb --no-or4 > output 2> output.err &&
+       test-parse-options --set23 -bbbbb --no-or4 >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
 test_expect_success 'OPT_NEGBIT() and OPT_SET_INT() work' '
-       test-parse-options --set23 -bbbbb --neg-or4 > output 2> output.err &&
+       test-parse-options --set23 -bbbbb --neg-or4 >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
-cat > expect <<EOF
+cat >expect <<\EOF
 boolean: 6
 integer: 0
 magnitude: 0
 timestamp: 0
 string: (not set)
 abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
 dry run: no
 file: (not set)
 EOF
 
 test_expect_success 'OPT_BIT() works' '
-       test-parse-options -bb --or4 > output 2> output.err &&
+       test-parse-options -bb --or4 >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
 test_expect_success 'OPT_NEGBIT() works' '
-       test-parse-options -bb --no-neg-or4 > output 2> output.err &&
+       test-parse-options -bb --no-neg-or4 >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
 test_expect_success 'OPT_COUNTUP() with PARSE_OPT_NODASH works' '
-       test-parse-options + + + + + + > output 2> output.err &&
+       test-parse-options + + + + + + >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
-cat > expect <<EOF
+cat >expect <<\EOF
 boolean: 0
 integer: 12345
 magnitude: 0
 timestamp: 0
 string: (not set)
 abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
 dry run: no
 file: (not set)
 EOF
 
 test_expect_success 'OPT_NUMBER_CALLBACK() works' '
-       test-parse-options -12345 > output 2> output.err &&
+       test-parse-options -12345 >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
-cat >expect <<EOF
+cat >expect <<\EOF
 boolean: 0
 integer: 0
 magnitude: 0
 timestamp: 0
 string: (not set)
 abbrev: 7
-verbose: 0
-quiet: no
+verbose: -1
+quiet: 0
 dry run: no
 file: (not set)
 EOF
@@ -460,7 +419,7 @@ test_expect_success 'negation of OPT_NONEG flags is not ambiguous' '
        test_cmp expect output
 '
 
-cat >>expect <<'EOF'
+cat >>expect <<\EOF
 list: foo
 list: bar
 list: baz
@@ -476,4 +435,118 @@ test_expect_success '--no-list resets list' '
        test_cmp expect output
 '
 
+cat >expect <<\EOF
+boolean: 0
+integer: 0
+magnitude: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: -1
+quiet: 3
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success 'multiple quiet levels' '
+       test-parse-options -q -q -q >output 2>output.err &&
+       test_must_be_empty output.err &&
+       test_cmp expect output
+'
+
+cat >expect <<\EOF
+boolean: 0
+integer: 0
+magnitude: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: 3
+quiet: 0
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success 'multiple verbose levels' '
+       test-parse-options -v -v -v >output 2>output.err &&
+       test_must_be_empty output.err &&
+       test_cmp expect output
+'
+
+cat >expect <<\EOF
+boolean: 0
+integer: 0
+magnitude: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: -1
+quiet: 0
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success '--no-quiet sets --quiet to 0' '
+       test-parse-options --no-quiet >output 2>output.err &&
+       test_must_be_empty output.err &&
+       test_cmp expect output
+'
+
+cat >expect <<\EOF
+boolean: 0
+integer: 0
+magnitude: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: -1
+quiet: 0
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success '--no-quiet resets multiple -q to 0' '
+       test-parse-options -q -q -q --no-quiet >output 2>output.err &&
+       test_must_be_empty output.err &&
+       test_cmp expect output
+'
+
+cat >expect <<\EOF
+boolean: 0
+integer: 0
+magnitude: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: 0
+quiet: 0
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success '--no-verbose sets verbose to 0' '
+       test-parse-options --no-verbose >output 2>output.err &&
+       test_must_be_empty output.err &&
+       test_cmp expect output
+'
+
+cat >expect <<\EOF
+boolean: 0
+integer: 0
+magnitude: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: 0
+quiet: 0
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success '--no-verbose resets multiple verbose to 0' '
+       test-parse-options -v -v -v --no-verbose >output 2>output.err &&
+       test_must_be_empty output.err &&
+       test_cmp expect output
+'
+
 test_done
index 8532a028e7c814057fe0bb7fd87addbd5dc90392..bf2deee10926f0d8ab7ba7d2645eb17a35b9a218 100755 (executable)
@@ -19,6 +19,13 @@ relative_path() {
        "test \"\$(test-path-utils relative_path '$1' '$2')\" = '$expected'"
 }
 
+test_submodule_relative_url() {
+       test_expect_success "test_submodule_relative_url: $1 $2 $3 => $4" "
+               actual=\$(git submodule--helper resolve-relative-url-test '$1' '$2' '$3') &&
+               test \"\$actual\" = '$4'
+       "
+}
+
 test_git_path() {
        test_expect_success "git-path $1 $2 => $3" "
                $1 git rev-parse --git-path $2 >actual &&
@@ -298,4 +305,43 @@ test_git_path GIT_COMMON_DIR=bar config                   bar/config
 test_git_path GIT_COMMON_DIR=bar packed-refs              bar/packed-refs
 test_git_path GIT_COMMON_DIR=bar shallow                  bar/shallow
 
+# In the tests below, the distinction between $PWD and $(pwd) is important:
+# on Windows, $PWD is POSIX style (/c/foo), $(pwd) has drive letter (c:/foo).
+
+test_submodule_relative_url "../" "../foo" "../submodule" "../../submodule"
+test_submodule_relative_url "../" "../foo/bar" "../submodule" "../../foo/submodule"
+test_submodule_relative_url "../" "../foo/submodule" "../submodule" "../../foo/submodule"
+test_submodule_relative_url "../" "./foo" "../submodule" "../submodule"
+test_submodule_relative_url "../" "./foo/bar" "../submodule" "../foo/submodule"
+test_submodule_relative_url "../../../" "../foo/bar" "../sub/a/b/c" "../../../../foo/sub/a/b/c"
+test_submodule_relative_url "../" "$PWD/addtest" "../repo" "$(pwd)/repo"
+test_submodule_relative_url "../" "foo/bar" "../submodule" "../foo/submodule"
+test_submodule_relative_url "../" "foo" "../submodule" "../submodule"
+
+test_submodule_relative_url "(null)" "../foo/bar" "../sub/a/b/c" "../foo/sub/a/b/c"
+test_submodule_relative_url "(null)" "../foo/bar" "../submodule" "../foo/submodule"
+test_submodule_relative_url "(null)" "../foo/submodule" "../submodule" "../foo/submodule"
+test_submodule_relative_url "(null)" "../foo" "../submodule" "../submodule"
+test_submodule_relative_url "(null)" "./foo/bar" "../submodule" "foo/submodule"
+test_submodule_relative_url "(null)" "./foo" "../submodule" "submodule"
+test_submodule_relative_url "(null)" "//somewhere else/repo" "../subrepo" "//somewhere else/subrepo"
+test_submodule_relative_url "(null)" "$PWD/subsuper_update_r" "../subsubsuper_update_r" "$(pwd)/subsubsuper_update_r"
+test_submodule_relative_url "(null)" "$PWD/super_update_r2" "../subsuper_update_r" "$(pwd)/subsuper_update_r"
+test_submodule_relative_url "(null)" "$PWD/." "../." "$(pwd)/."
+test_submodule_relative_url "(null)" "$PWD" "./." "$(pwd)/."
+test_submodule_relative_url "(null)" "$PWD/addtest" "../repo" "$(pwd)/repo"
+test_submodule_relative_url "(null)" "$PWD" "./Ã¥ Ã¤Ã¶" "$(pwd)/Ã¥ Ã¤Ã¶"
+test_submodule_relative_url "(null)" "$PWD/." "../submodule" "$(pwd)/submodule"
+test_submodule_relative_url "(null)" "$PWD/submodule" "../submodule" "$(pwd)/submodule"
+test_submodule_relative_url "(null)" "$PWD/home2/../remote" "../bundle1" "$(pwd)/home2/../bundle1"
+test_submodule_relative_url "(null)" "$PWD/submodule_update_repo" "./." "$(pwd)/submodule_update_repo/."
+test_submodule_relative_url "(null)" "file:///tmp/repo" "../subrepo" "file:///tmp/subrepo"
+test_submodule_relative_url "(null)" "foo/bar" "../submodule" "foo/submodule"
+test_submodule_relative_url "(null)" "foo" "../submodule" "submodule"
+test_submodule_relative_url "(null)" "helper:://hostname/repo" "../subrepo" "helper:://hostname/subrepo"
+test_submodule_relative_url "(null)" "ssh://hostname/repo" "../subrepo" "ssh://hostname/subrepo"
+test_submodule_relative_url "(null)" "ssh://hostname:22/repo" "../subrepo" "ssh://hostname:22/subrepo"
+test_submodule_relative_url "(null)" "user@host:path/to/repo" "../subrepo" "user@host:path/to/subrepo"
+test_submodule_relative_url "(null)" "user@host:repo" "../subrepo" "user@host:subrepo"
+
 test_done
diff --git a/t/t1350-config-hooks-path.sh b/t/t1350-config-hooks-path.sh
new file mode 100755 (executable)
index 0000000..5e3fb3a
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+test_description='Test the core.hooksPath configuration variable'
+
+. ./test-lib.sh
+
+test_expect_success 'set up a pre-commit hook in core.hooksPath' '
+       mkdir -p .git/custom-hooks .git/hooks &&
+       write_script .git/custom-hooks/pre-commit <<-\EOF &&
+       echo CUSTOM >>actual
+       EOF
+       write_script .git/hooks/pre-commit <<-\EOF
+       echo NORMAL >>actual
+       EOF
+'
+
+test_expect_success 'Check that various forms of specifying core.hooksPath work' '
+       test_commit no_custom_hook &&
+       git config core.hooksPath .git/custom-hooks &&
+       test_commit have_custom_hook &&
+       git config core.hooksPath .git/custom-hooks/ &&
+       test_commit have_custom_hook_trailing_slash &&
+       git config core.hooksPath "$PWD/.git/custom-hooks" &&
+       test_commit have_custom_hook_abs_path &&
+       git config core.hooksPath "$PWD/.git/custom-hooks/" &&
+       test_commit have_custom_hook_abs_path_trailing_slash &&
+       cat >expect <<-\EOF &&
+       NORMAL
+       CUSTOM
+       CUSTOM
+       CUSTOM
+       CUSTOM
+       EOF
+       test_cmp expect actual
+'
+
+test_done
index e66b7cb697bc8a4ef7268e5d8d29f0b69a8ac213..7ee8ea004f1731b1efbaedf0a8022cdb4946c7cf 100755 (executable)
@@ -427,6 +427,24 @@ test_expect_success 'fsck allows .Ňit' '
        )
 '
 
+test_expect_success 'NUL in commit' '
+       rm -fr nul-in-commit &&
+       git init nul-in-commit &&
+       (
+               cd nul-in-commit &&
+               git commit --allow-empty -m "initial commitQNUL after message" &&
+               git cat-file commit HEAD >original &&
+               q_to_nul <original >munged &&
+               git hash-object -w -t commit --stdin <munged >name &&
+               git branch bad $(cat name) &&
+
+               test_must_fail git -c fsck.nulInCommit=error fsck 2>warn.1 &&
+               grep nulInCommit warn.1 &&
+               git fsck 2>warn.2 &&
+               grep nulInCommit warn.2
+       )
+'
+
 # create a static test repo which is broken by omitting
 # one particular object ($1, which is looked up via rev-parse
 # in the new repository).
index 3acb9926f2ff7fbf2b367a089b294a4a88e2ea31..3a22fc55fc324fbfbfdcdf39fb71e829c89729b5 100755 (executable)
@@ -4,6 +4,8 @@ test_description='test git worktree add'
 
 . ./test-lib.sh
 
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
 test_expect_success 'setup' '
        test_commit init
 '
@@ -225,4 +227,61 @@ test_expect_success '"add" worktree with --checkout' '
        test_cmp init.t swamp2/init.t
 '
 
+test_expect_success 'put a worktree under rebase' '
+       git worktree add under-rebase &&
+       (
+               cd under-rebase &&
+               set_fake_editor &&
+               FAKE_LINES="edit 1" git rebase -i HEAD^ &&
+               git worktree list | grep "under-rebase.*detached HEAD"
+       )
+'
+
+test_expect_success 'add a worktree, checking out a rebased branch' '
+       test_must_fail git worktree add new-rebase under-rebase &&
+       ! test -d new-rebase
+'
+
+test_expect_success 'checking out a rebased branch from another worktree' '
+       git worktree add new-place &&
+       test_must_fail git -C new-place checkout under-rebase
+'
+
+test_expect_success 'not allow to delete a branch under rebase' '
+       (
+               cd under-rebase &&
+               test_must_fail git branch -D under-rebase
+       )
+'
+
+test_expect_success 'rename a branch under rebase not allowed' '
+       test_must_fail git branch -M under-rebase rebase-with-new-name
+'
+
+test_expect_success 'check out from current worktree branch ok' '
+       (
+               cd under-rebase &&
+               git checkout under-rebase &&
+               git checkout - &&
+               git rebase --abort
+       )
+'
+
+test_expect_success 'checkout a branch under bisect' '
+       git worktree add under-bisect &&
+       (
+               cd under-bisect &&
+               git bisect start &&
+               git bisect bad &&
+               git bisect good HEAD~2 &&
+               git worktree list | grep "under-bisect.*detached HEAD" &&
+               test_must_fail git worktree add new-bisect under-bisect &&
+               ! test -d new-bisect
+       )
+'
+
+test_expect_success 'rename a branch under bisect not allowed' '
+       test_must_fail git branch -M under-bisect bisect-with-new-name
+'
+
 test_done
index d96d0e4c9b517ecb712d643cdd13e08bff066036..66348f11d11598e2d7e550d5216c5b227e85f1c7 100755 (executable)
@@ -62,7 +62,7 @@ test_expect_success 'setup' '
 
 # "exec" commands are ran with the user shell by default, but this may
 # be non-POSIX. For example, if SHELL=zsh then ">file" doesn't work
-# to create a file. Unseting SHELL avoids such non-portable behavior
+# to create a file. Unsetting SHELL avoids such non-portable behavior
 # in tests. It must be exported for it to take effect where needed.
 SHELL=
 export SHELL
index a1c4e0216ff7421082a957e5b7789f3cc9a13181..db9378142a93338d2988f40e2748bc476490bcd5 100755 (executable)
@@ -14,11 +14,11 @@ test_description='revert can handle submodules'
 git_revert () {
        git status -su >expect &&
        ls -1pR * >>expect &&
-       tar czf "$TRASH_DIRECTORY/tmp.tgz" * &&
+       tar cf "$TRASH_DIRECTORY/tmp.tar" * &&
        git checkout "$1" &&
        git revert HEAD &&
        rm -rf * &&
-       tar xzf "$TRASH_DIRECTORY/tmp.tgz" &&
+       tar xf "$TRASH_DIRECTORY/tmp.tar" &&
        git status -su >actual &&
        ls -1pR * >>actual &&
        test_cmp expect actual &&
index 831935665e012c04bbe4c57feee92c0310df5c04..26dd5b7f78a7f7e65ddda24c4686d2ae26fc30de 100755 (executable)
@@ -49,12 +49,54 @@ test_expect_success "setup" '
 test_expect_success "setup case mac" '
        git checkout -b mac_os
 '
+# This will test nfd2nfc in git diff
+test_expect_success "git diff f.Adiar" '
+       touch f.$Adiarnfc &&
+       git add f.$Adiarnfc &&
+       echo f.Adiarnfc >f.$Adiarnfc &&
+       git diff f.$Adiarnfd >expect &&
+       git diff f.$Adiarnfc >actual &&
+       test_cmp expect actual &&
+       git reset HEAD f.Adiarnfc &&
+       rm f.$Adiarnfc expect actual
+'
+# This will test nfd2nfc in git diff-files
+test_expect_success "git diff-files f.Adiar" '
+       touch f.$Adiarnfc &&
+       git add f.$Adiarnfc &&
+       echo f.Adiarnfc >f.$Adiarnfc &&
+       git diff-files f.$Adiarnfd >expect &&
+       git diff-files f.$Adiarnfc >actual &&
+       test_cmp expect actual &&
+       git reset HEAD f.Adiarnfc &&
+       rm f.$Adiarnfc expect actual
+'
+# This will test nfd2nfc in git diff-index
+test_expect_success "git diff-index f.Adiar" '
+       touch f.$Adiarnfc &&
+       git add f.$Adiarnfc &&
+       echo f.Adiarnfc >f.$Adiarnfc &&
+       git diff-index HEAD f.$Adiarnfd >expect &&
+       git diff-index HEAD f.$Adiarnfc >actual &&
+       test_cmp expect actual &&
+       git reset HEAD f.Adiarnfc &&
+       rm f.$Adiarnfc expect actual
+'
 # This will test nfd2nfc in readdir()
 test_expect_success "add file Adiarnfc" '
        echo f.Adiarnfc >f.$Adiarnfc &&
        git add f.$Adiarnfc &&
        git commit -m "add f.$Adiarnfc"
 '
+# This will test nfd2nfc in git diff-tree
+test_expect_success "git diff-tree f.Adiar" '
+       echo f.Adiarnfc >>f.$Adiarnfc &&
+       git diff-tree HEAD f.$Adiarnfd >expect &&
+       git diff-tree HEAD f.$Adiarnfc >actual &&
+       test_cmp expect actual &&
+       git checkout f.$Adiarnfc &&
+       rm expect actual
+'
 # This will test nfd2nfc in git stage()
 test_expect_success "stage file d.Adiarnfd/f.Adiarnfd" '
        mkdir d.$Adiarnfd &&
index eed2981b96df71c6acdc0f90f8b477561241a9a3..8049cad374827d15ec098218b01acb59b65383ee 100755 (executable)
@@ -1460,4 +1460,109 @@ test_expect_success 'format-patch -o overrides format.outputDirectory' '
        test_path_is_dir patchset
 '
 
+test_expect_success 'format-patch --base' '
+       git checkout side &&
+       git format-patch --stdout --base=HEAD~3 -1 >patch &&
+       grep "^base-commit:" patch >actual &&
+       grep "^prerequisite-patch-id:" patch >>actual &&
+       echo "base-commit: $(git rev-parse HEAD~3)" >expected &&
+       echo "prerequisite-patch-id: $(git show --patch HEAD~2 | git patch-id --stable | awk "{print \$1}")" >>expected &&
+       echo "prerequisite-patch-id: $(git show --patch HEAD~1 | git patch-id --stable | awk "{print \$1}")" >>expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'format-patch --base errors out when base commit is in revision list' '
+       test_must_fail git format-patch --base=HEAD -2 &&
+       test_must_fail git format-patch --base=HEAD~1 -2 &&
+       git format-patch --stdout --base=HEAD~2 -2 >patch &&
+       grep "^base-commit:" patch >actual &&
+       echo "base-commit: $(git rev-parse HEAD~2)" >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'format-patch --base errors out when base commit is not ancestor of revision list' '
+       # For history as below:
+       #
+       #    ---Q---P---Z---Y---*---X
+       #        \             /
+       #         ------------W
+       #
+       # If "format-patch Z..X" is given, P and Z can not be specified as the base commit
+       git checkout -b topic1 master &&
+       git rev-parse HEAD >commit-id-base &&
+       test_commit P &&
+       git rev-parse HEAD >commit-id-P &&
+       test_commit Z &&
+       git rev-parse HEAD >commit-id-Z &&
+       test_commit Y &&
+       git checkout -b topic2 master &&
+       test_commit W &&
+       git merge topic1 &&
+       test_commit X &&
+       test_must_fail git format-patch --base=$(cat commit-id-P) -3 &&
+       test_must_fail git format-patch --base=$(cat commit-id-Z) -3 &&
+       git format-patch --stdout --base=$(cat commit-id-base) -3 >patch &&
+       grep "^base-commit:" patch >actual &&
+       echo "base-commit: $(cat commit-id-base)" >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'format-patch --base=auto' '
+       git checkout -b upstream master &&
+       git checkout -b local upstream &&
+       git branch --set-upstream-to=upstream &&
+       test_commit N1 &&
+       test_commit N2 &&
+       git format-patch --stdout --base=auto -2 >patch &&
+       grep "^base-commit:" patch >actual &&
+       echo "base-commit: $(git rev-parse upstream)" >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'format-patch errors out when history involves criss-cross' '
+       # setup criss-cross history
+       #
+       #   B---M1---D
+       #  / \ /
+       # A   X
+       #  \ / \
+       #   C---M2---E
+       #
+       git checkout master &&
+       test_commit A &&
+       git checkout -b xb master &&
+       test_commit B &&
+       git checkout -b xc master &&
+       test_commit C &&
+       git checkout -b xbc xb -- &&
+       git merge xc &&
+       git checkout -b xcb xc -- &&
+       git branch --set-upstream-to=xbc &&
+       git merge xb &&
+       git checkout xbc &&
+       test_commit D &&
+       git checkout xcb &&
+       test_commit E &&
+       test_must_fail  git format-patch --base=auto -1
+'
+
+test_expect_success 'format-patch format.useAutoBaseoption' '
+       test_when_finished "git config --unset format.useAutoBase" &&
+       git checkout local &&
+       git config format.useAutoBase true &&
+       git format-patch --stdout -1 >patch &&
+       grep "^base-commit:" patch >actual &&
+       echo "base-commit: $(git rev-parse upstream)" >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'format-patch --base overrides format.useAutoBase' '
+       test_when_finished "git config --unset format.useAutoBase" &&
+       git config format.useAutoBase true &&
+       git format-patch --stdout --base=HEAD~1 -1 >patch &&
+       grep "^base-commit:" patch >actual &&
+       echo "base-commit: $(git rev-parse HEAD~1)" >expected &&
+       test_cmp expected actual
+'
+
 test_done
index 48e2ab62da7a4293dd647524f75f3590783bcb02..3484b6f0f3cf04a9cf831a5d91c31486efebcc38 100755 (executable)
@@ -91,23 +91,55 @@ test_expect_success 'configured username does not override URL' '
        expect_askpass pass user@host
 '
 
-test_expect_success 'cmdline credential config passes into submodules' '
+test_expect_success 'set up repo with http submodules' '
        git init super &&
        set_askpass user@host pass@host &&
        (
                cd super &&
                git submodule add "$HTTPD_URL/auth/dumb/repo.git" sub &&
                git commit -m "add submodule"
-       ) &&
+       )
+'
+
+test_expect_success 'cmdline credential config passes to submodule via clone' '
        set_askpass wrong pass@host &&
        test_must_fail git clone --recursive super super-clone &&
        rm -rf super-clone &&
+
        set_askpass wrong pass@host &&
-       git -c "credential.$HTTP_URL.username=user@host" \
+       git -c "credential.$HTTPD_URL.username=user@host" \
                clone --recursive super super-clone &&
        expect_askpass pass user@host
 '
 
+test_expect_success 'cmdline credential config passes submodule via fetch' '
+       set_askpass wrong pass@host &&
+       test_must_fail git -C super-clone fetch --recurse-submodules &&
+
+       set_askpass wrong pass@host &&
+       git -C super-clone \
+           -c "credential.$HTTPD_URL.username=user@host" \
+           fetch --recurse-submodules &&
+       expect_askpass pass user@host
+'
+
+test_expect_success 'cmdline credential config passes submodule update' '
+       # advance the submodule HEAD so that a fetch is required
+       git commit --allow-empty -m foo &&
+       git push "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/repo.git" HEAD &&
+       sha1=$(git rev-parse HEAD) &&
+       git -C super-clone update-index --cacheinfo 160000,$sha1,sub &&
+
+       set_askpass wrong pass@host &&
+       test_must_fail git -C super-clone submodule update &&
+
+       set_askpass wrong pass@host &&
+       git -C super-clone \
+           -c "credential.$HTTPD_URL.username=user@host" \
+           submodule update &&
+       expect_askpass pass user@host
+'
+
 test_expect_success 'fetch changes via http' '
        echo content >>file &&
        git commit -a -m two &&
index e44fe72c7aa4d670a6796a5c342e8e131fac6a8d..2f375eb94d528baefad038ff5b07baf0fe08d1fc 100755 (executable)
@@ -283,10 +283,20 @@ test_expect_success EXPENSIVE 'http can handle enormous ref negotiation' '
 '
 
 test_expect_success 'custom http headers' '
-       test_must_fail git fetch "$HTTPD_URL/smart_headers/repo.git" &&
+       test_must_fail git -c http.extraheader="x-magic-two: cadabra" \
+               fetch "$HTTPD_URL/smart_headers/repo.git" &&
        git -c http.extraheader="x-magic-one: abra" \
            -c http.extraheader="x-magic-two: cadabra" \
-           fetch "$HTTPD_URL/smart_headers/repo.git"
+           fetch "$HTTPD_URL/smart_headers/repo.git" &&
+       git update-index --add --cacheinfo 160000,$(git rev-parse HEAD),sub &&
+       git config -f .gitmodules submodule.sub.path sub &&
+       git config -f .gitmodules submodule.sub.url \
+               "$HTTPD_URL/smart_headers/repo.git" &&
+       git submodule init sub &&
+       test_must_fail git submodule update sub &&
+       git -c http.extraheader="x-magic-one: abra" \
+           -c http.extraheader="x-magic-two: cadabra" \
+               submodule update sub
 '
 
 stop_httpd
index 150aeaf7137b6521874322fd9e966d4061d71044..a43339420019718b76963d5ced92ebb981b0f0ed 100755 (executable)
@@ -466,7 +466,7 @@ test_expect_success 'clone ssh://host.xz:22/~repo' '
 #IPv6
 for tuah in ::1 [::1] [::1]: user@::1 user@[::1] user@[::1]: [user@::1] [user@::1]:
 do
-       ehost=$(echo $tuah | sed -e "s/1]:/1]/ "| tr -d "[]")
+       ehost=$(echo $tuah | sed -e "s/1]:/1]/| tr -d "[]")
        test_expect_success "clone ssh://$tuah/home/user/repo" "
          test_clone_url ssh://$tuah/home/user/repo $ehost /home/user/repo
        "
index 27d730c0a7209480c5349e4ace65f92c9e75c699..e4850b778c2f20df02ce187cc8cd057df2759d7d 100755 (executable)
@@ -37,4 +37,24 @@ test_expect_success 'clone -c config is available during clone' '
        test_cmp expect child/file
 '
 
+# Tests for the hidden file attribute on windows
+is_hidden () {
+       # Use the output of `attrib`, ignore the absolute path
+       case "$(attrib "$1")" in *H*?:*) return 0;; esac
+       return 1
+}
+
+test_expect_success MINGW 'clone -c core.hideDotFiles' '
+       test_commit attributes .gitattributes "" &&
+       rm -rf child &&
+       git clone -c core.hideDotFiles=false . child &&
+       ! is_hidden child/.gitattributes &&
+       rm -rf child &&
+       git clone -c core.hideDotFiles=dotGitOnly . child &&
+       ! is_hidden child/.gitattributes &&
+       rm -rf child &&
+       git clone -c core.hideDotFiles=true . child &&
+       is_hidden child/.gitattributes
+'
+
 test_done
index 755d30ce2a5d1c5e34751d8906ad41b02d553b03..3f59e58dfb5171d2e5ef53399c2f8d24fff1e94e 100755 (executable)
@@ -76,7 +76,7 @@ test_expect_success "result contains a conflict" "test_cmp expect a1"
 
 git ls-files --stage > out
 cat > expect << EOF
-100644 439cc46de773d8a83c77799b7cc9191c128bfcff 1      a1
+100644 ec3fe2a791706733f2d8fa7ad45d9a9672031f5e 1      a1
 100644 cf84443e49e1b366fac938711ddf4be2d4d1d9e9 2      a1
 100644 fd7923529855d0b274795ae3349c5e0438333979 3      a1
 EOF
index 9d6621c05604e22c258ea21ef0cd549ef4d0d278..18aa88b5c096c14f2f8a3a8d8c6488127fddf055 100755 (executable)
@@ -212,7 +212,8 @@ test_expect_success 'git detects differently handled merges conflict' '
                -L "" \
                -L "Temporary merge branch 1" \
                merged empty merge-me &&
-       test $(git rev-parse :1:new_a) = $(git hash-object merged)
+       sed -e "s/^\([<=>]\)/\1\1\1/" merged >merged-internal &&
+       test $(git rev-parse :1:new_a) = $(git hash-object merged-internal)
 '
 
 #
@@ -298,89 +299,6 @@ test_expect_success 'git detects conflict merging criss-cross+modify/delete, rev
        test $(git rev-parse :3:file) = $(git rev-parse B:file)
 '
 
-#
-# criss-cross + modify/modify with very contrived file contents:
-#
-#      B   D
-#      o---o
-#     / \ / \
-#  A o   X   ? F
-#     \ / \ /
-#      o---o
-#      C   E
-#
-#   Commit A: file with contents 'A\n'
-#   Commit B: file with contents 'B\n'
-#   Commit C: file with contents 'C\n'
-#   Commit D: file with contents 'D\n'
-#   Commit E: file with contents:
-#      <<<<<<< Temporary merge branch 1
-#      C
-#      =======
-#      B
-#      >>>>>>> Temporary merge branch 2
-#
-# Now, when we merge commits D & E, does git detect the conflict?
-
-test_expect_success 'setup differently handled merges of content conflict' '
-       git clean -fdqx &&
-       rm -rf .git &&
-       git init &&
-
-       echo A >file &&
-       git add file &&
-       test_tick &&
-       git commit -m A &&
-
-       git branch B &&
-       git checkout -b C &&
-       echo C >file &&
-       git add file &&
-       test_tick &&
-       git commit -m C &&
-
-       git checkout B &&
-       echo B >file &&
-       git add file &&
-       test_tick &&
-       git commit -m B &&
-
-       git checkout B^0 &&
-       test_must_fail git merge C &&
-       echo D >file &&
-       git add file &&
-       test_tick &&
-       git commit -m D &&
-       git tag D &&
-
-       git checkout C^0 &&
-       test_must_fail git merge B &&
-       cat <<EOF >file &&
-<<<<<<< Temporary merge branch 1
-C
-=======
-B
->>>>>>> Temporary merge branch 2
-EOF
-       git add file &&
-       test_tick &&
-       git commit -m E &&
-       git tag E
-'
-
-test_expect_failure 'git detects conflict w/ criss-cross+contrived resolution' '
-       git checkout D^0 &&
-
-       test_must_fail git merge -s recursive E^0 &&
-
-       test 3 -eq $(git ls-files -s | wc -l) &&
-       test 3 -eq $(git ls-files -u | wc -l) &&
-       test 0 -eq $(git ls-files -o | wc -l) &&
-
-       test $(git rev-parse :2:file) = $(git rev-parse D:file) &&
-       test $(git rev-parse :3:file) = $(git rev-parse E:file)
-'
-
 #
 # criss-cross + d/f conflict via add/add:
 #   Commit A: Neither file 'a' nor directory 'a/' exists.
index c6b7aa6977e0a5a1017c759d93f8883cad3713b0..62b8a2e7bbd1cf26710be0a0eb90041d5e3f7d2c 100755 (executable)
@@ -8,7 +8,7 @@ test_description='bisect can handle submodules'
 git_bisect () {
        git status -su >expect &&
        ls -1pR * >>expect &&
-       tar czf "$TRASH_DIRECTORY/tmp.tgz" * &&
+       tar cf "$TRASH_DIRECTORY/tmp.tar" * &&
        GOOD=$(git rev-parse --verify HEAD) &&
        git checkout "$1" &&
        echo "foo" >bar &&
@@ -20,7 +20,7 @@ git_bisect () {
        git bisect start &&
        git bisect good $GOOD &&
        rm -rf * &&
-       tar xzf "$TRASH_DIRECTORY/tmp.tgz" &&
+       tar xf "$TRASH_DIRECTORY/tmp.tar" &&
        git status -su >actual &&
        ls -1pR * >>actual &&
        test_cmp expect actual &&
index 70afb44271a38024d89d0e266bafa009961508de..d0ab09f4bd804730eb049ea982e5724653583f06 100755 (executable)
@@ -5,15 +5,6 @@ test_description='test for-each-refs usage of ref-filter APIs'
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-gpg.sh
 
-test_prepare_expect () {
-       if test_have_prereq GPG
-       then
-               cat
-       else
-               sed '/signed/d'
-       fi
-}
-
 test_expect_success 'setup some history and refs' '
        test_commit one &&
        test_commit two &&
@@ -22,11 +13,19 @@ test_expect_success 'setup some history and refs' '
        test_commit four &&
        git tag -m "An annotated tag" annotated-tag &&
        git tag -m "Annonated doubly" doubly-annotated-tag annotated-tag &&
+
+       # Note that these "signed" tags might not actually be signed.
+       # Tests which care about the distinction should be marked
+       # with the GPG prereq.
        if test_have_prereq GPG
        then
-               git tag -s -m "A signed tag" signed-tag &&
-               git tag -s -m "Signed doubly" doubly-signed-tag signed-tag
+               sign=-s
+       else
+               sign=
        fi &&
+       git tag $sign -m "A signed tag" signed-tag &&
+       git tag $sign -m "Signed doubly" doubly-signed-tag signed-tag &&
+
        git checkout master &&
        git update-ref refs/odd/spot master
 '
@@ -42,7 +41,7 @@ test_expect_success 'filtering with --points-at' '
 '
 
 test_expect_success 'check signed tags with --points-at' '
-       test_prepare_expect <<-\EOF | sed -e "s/Z$//" >expect &&
+       sed -e "s/Z$//" >expect <<-\EOF &&
        refs/heads/side Z
        refs/tags/annotated-tag four
        refs/tags/four Z
@@ -65,7 +64,7 @@ test_expect_success 'filtering with --merged' '
 '
 
 test_expect_success 'filtering with --no-merged' '
-       test_prepare_expect >expect <<-\EOF &&
+       cat >expect <<-\EOF &&
        refs/heads/side
        refs/tags/annotated-tag
        refs/tags/doubly-annotated-tag
@@ -78,7 +77,7 @@ test_expect_success 'filtering with --no-merged' '
 '
 
 test_expect_success 'filtering with --contains' '
-       test_prepare_expect >expect <<-\EOF &&
+       cat >expect <<-\EOF &&
        refs/heads/master
        refs/heads/side
        refs/odd/spot
@@ -99,7 +98,7 @@ test_expect_success '%(color) must fail' '
 '
 
 test_expect_success 'left alignment is default' '
-       test_prepare_expect >expect <<-\EOF &&
+       cat >expect <<-\EOF &&
        refname is refs/heads/master  |refs/heads/master
        refname is refs/heads/side    |refs/heads/side
        refname is refs/odd/spot      |refs/odd/spot
@@ -117,7 +116,7 @@ test_expect_success 'left alignment is default' '
 '
 
 test_expect_success 'middle alignment' '
-       test_prepare_expect >expect <<-\EOF &&
+       cat >expect <<-\EOF &&
        | refname is refs/heads/master |refs/heads/master
        |  refname is refs/heads/side  |refs/heads/side
        |   refname is refs/odd/spot   |refs/odd/spot
@@ -135,7 +134,7 @@ test_expect_success 'middle alignment' '
 '
 
 test_expect_success 'right alignment' '
-       test_prepare_expect >expect <<-\EOF &&
+       cat >expect <<-\EOF &&
        |  refname is refs/heads/master|refs/heads/master
        |    refname is refs/heads/side|refs/heads/side
        |      refname is refs/odd/spot|refs/odd/spot
@@ -152,7 +151,7 @@ test_expect_success 'right alignment' '
        test_cmp expect actual
 '
 
-test_prepare_expect >expect <<-\EOF
+cat >expect <<-\EOF
 |       refname is refs/heads/master       |refs/heads/master
 |        refname is refs/heads/side        |refs/heads/side
 |         refname is refs/odd/spot         |refs/odd/spot
@@ -199,7 +198,7 @@ EOF
 # Individual atoms inside %(align:...) and %(end) must not be quoted.
 
 test_expect_success 'alignment with format quote' "
-       test_prepare_expect >expect <<-\EOF &&
+       cat >expect <<-\EOF &&
        |'      '\''master| A U Thor'\''      '|
        |'       '\''side| A U Thor'\''       '|
        |'     '\''odd/spot| A U Thor'\''     '|
@@ -217,7 +216,7 @@ test_expect_success 'alignment with format quote' "
 "
 
 test_expect_success 'nested alignment with quote formatting' "
-       test_prepare_expect >expect <<-\EOF &&
+       cat >expect <<-\EOF &&
        |'         master               '|
        |'           side               '|
        |'       odd/spot               '|
@@ -235,7 +234,7 @@ test_expect_success 'nested alignment with quote formatting' "
 "
 
 test_expect_success 'check `%(contents:lines=1)`' '
-       test_prepare_expect >expect <<-\EOF &&
+       cat >expect <<-\EOF &&
        master |three
        side |four
        odd/spot |three
@@ -253,7 +252,7 @@ test_expect_success 'check `%(contents:lines=1)`' '
 '
 
 test_expect_success 'check `%(contents:lines=0)`' '
-       test_prepare_expect >expect <<-\EOF &&
+       cat >expect <<-\EOF &&
        master |
        side |
        odd/spot |
@@ -271,7 +270,7 @@ test_expect_success 'check `%(contents:lines=0)`' '
 '
 
 test_expect_success 'check `%(contents:lines=99999)`' '
-       test_prepare_expect >expect <<-\EOF &&
+       cat >expect <<-\EOF &&
        master |three
        side |four
        odd/spot |three
index d48d63a6fd0d3f4480d3b42ae146326e7b11a56a..3570f7bb8c8955a1bf519eda3278392d249e6fd1 100755 (executable)
@@ -11,6 +11,10 @@ subcommands of git submodule.
 
 . ./test-lib.sh
 
+test_expect_success 'submodule deinit works on empty repository' '
+       git submodule deinit --all
+'
+
 test_expect_success 'setup - initial commit' '
        >t &&
        git add t &&
@@ -18,6 +22,22 @@ test_expect_success 'setup - initial commit' '
        git branch initial
 '
 
+test_expect_success 'submodule init aborts on missing .gitmodules file' '
+       test_when_finished "git update-index --remove sub" &&
+       git update-index --add --cacheinfo 160000,$(git rev-parse HEAD),sub &&
+       # missing the .gitmodules file here
+       test_must_fail git submodule init 2>actual &&
+       test_i18ngrep "No url found for submodule path" actual
+'
+
+test_expect_success 'submodule update aborts on missing .gitmodules file' '
+       test_when_finished "git update-index --remove sub" &&
+       git update-index --add --cacheinfo 160000,$(git rev-parse HEAD),sub &&
+       # missing the .gitmodules file here
+       git submodule update sub 2>actual &&
+       test_i18ngrep "Submodule path .sub. not initialized" actual
+'
+
 test_expect_success 'configuration parsing' '
        test_when_finished "rm -f .gitmodules" &&
        cat >.gitmodules <<-\EOF &&
@@ -899,7 +919,8 @@ test_expect_success 'submodule deinit works on repository without submodules' '
                >file &&
                git add file &&
                git commit -m "repo should not be empty" &&
-               git submodule deinit .
+               git submodule deinit . &&
+               git submodule deinit --all
        )
 '
 
@@ -941,6 +962,19 @@ test_expect_success 'submodule deinit . deinits all initialized submodules' '
        rmdir init example2
 '
 
+test_expect_success 'submodule deinit --all deinits all initialized submodules' '
+       git submodule update --init &&
+       git config submodule.example.foo bar &&
+       git config submodule.example2.frotz nitfol &&
+       test_must_fail git submodule deinit &&
+       git submodule deinit --all >actual &&
+       test -z "$(git config --get-regexp "submodule\.example\.")" &&
+       test -z "$(git config --get-regexp "submodule\.example2\.")" &&
+       test_i18ngrep "Cleared directory .init" actual &&
+       test_i18ngrep "Cleared directory .example2" actual &&
+       rmdir init example2
+'
+
 test_expect_success 'submodule deinit deinits a submodule when its work tree is missing or empty' '
        git submodule update --init &&
        rm -rf init example2/* example2/.git &&
@@ -1007,6 +1041,10 @@ test_expect_success 'submodule deinit is silent when used on an uninitialized su
        test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
        test_i18ngrep ! "Submodule .example2. (.*) unregistered for path .example2" actual &&
        test_i18ngrep "Cleared directory .init" actual &&
+       git submodule deinit --all >actual &&
+       test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
+       test_i18ngrep ! "Submodule .example2. (.*) unregistered for path .example2" actual &&
+       test_i18ngrep "Cleared directory .init" actual &&
        rmdir init example2
 '
 
index fd741f506f4192e56348fcced49b2deabe59b9e7..5f278799d5a0ceed90b9297ace552b653026e596 100755 (executable)
@@ -108,24 +108,36 @@ pwd=$(pwd)
 
 cat <<EOF >expect
 Submodule path '../super': checked out '$supersha1'
-Submodule 'merging' ($pwd/merging) registered for path '../super/merging'
-Submodule 'none' ($pwd/none) registered for path '../super/none'
-Submodule 'rebasing' ($pwd/rebasing) registered for path '../super/rebasing'
-Submodule 'submodule' ($pwd/submodule) registered for path '../super/submodule'
 Submodule path '../super/merging': checked out '$mergingsha1'
 Submodule path '../super/none': checked out '$nonesha1'
 Submodule path '../super/rebasing': checked out '$rebasingsha1'
 Submodule path '../super/submodule': checked out '$submodulesha1'
 EOF
 
+cat <<EOF >expect2
+Submodule 'merging' ($pwd/merging) registered for path '../super/merging'
+Submodule 'none' ($pwd/none) registered for path '../super/none'
+Submodule 'rebasing' ($pwd/rebasing) registered for path '../super/rebasing'
+Submodule 'submodule' ($pwd/submodule) registered for path '../super/submodule'
+Cloning into '$pwd/recursivesuper/super/merging'...
+done.
+Cloning into '$pwd/recursivesuper/super/none'...
+done.
+Cloning into '$pwd/recursivesuper/super/rebasing'...
+done.
+Cloning into '$pwd/recursivesuper/super/submodule'...
+done.
+EOF
+
 test_expect_success 'submodule update --init --recursive from subdirectory' '
        git -C recursivesuper/super reset --hard HEAD^ &&
        (cd recursivesuper &&
         mkdir tmp &&
         cd tmp &&
-        git submodule update --init --recursive ../super >../../actual
+        git submodule update --init --recursive ../super >../../actual 2>../../actual2
        ) &&
-       test_cmp expect actual
+       test_cmp expect actual &&
+       test_cmp expect2 actual2
 '
 
 apos="'";
diff --git a/t/t7412-submodule--helper.sh b/t/t7412-submodule--helper.sh
deleted file mode 100755 (executable)
index 149d428..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2016 Jacob Keller
-#
-
-test_description='Basic plumbing support of submodule--helper
-
-This test verifies the submodule--helper plumbing command used to implement
-git-submodule.
-'
-
-. ./test-lib.sh
-
-test_expect_success 'sanitize-config clears configuration' '
-       git -c user.name="Some User" submodule--helper sanitize-config >actual &&
-       test_must_be_empty actual
-'
-
-sq="'"
-test_expect_success 'sanitize-config keeps credential.helper' '
-       git -c credential.helper=helper submodule--helper sanitize-config >actual &&
-       echo "${sq}credential.helper=helper${sq}" >expect &&
-       test_cmp expect actual
-'
-
-test_done
index 900f7de05a67424c867e0f149e7e7a448111791c..d84897a67a3c365e280f88b36f43fc49e1ac9d7b 100755 (executable)
@@ -607,4 +607,24 @@ test_expect_success '--only works on to-be-born branch' '
        test_cmp expected actual
 '
 
+test_expect_success '--dry-run with conflicts fixed from a merge' '
+       # setup two branches with conflicting information
+       # in the same file, resolve the conflict,
+       # call commit with --dry-run
+       echo "Initial contents, unimportant" >test-file &&
+       git add test-file &&
+       git commit -m "Initial commit" &&
+       echo "commit-1-state" >test-file &&
+       git commit -m "commit 1" -i test-file &&
+       git tag commit-1 &&
+       git checkout -b branch-2 HEAD^1 &&
+       echo "commit-2-state" >test-file &&
+       git commit -m "commit 2" -i test-file &&
+       ! $(git merge --no-commit commit-1) &&
+       echo "commit-2-state" >test-file &&
+       git add test-file &&
+       git commit --dry-run &&
+       git commit -m "conflicts fixed from merge."
+'
+
 test_done
index 2ddf28c984de99cb17884f76d68a708076f57e12..ed2653d46fe6cd0ed1dd5dc2dfd73f7340b8fe31 100755 (executable)
@@ -3,11 +3,10 @@
 test_description='verbose commit template'
 . ./test-lib.sh
 
-cat >check-for-diff <<EOF
-#!$SHELL_PATH
-exec grep '^diff --git' "\$1"
+write_script "check-for-diff" <<\EOF &&
+grep '^diff --git' "$1" >out
+exit 0
 EOF
-chmod +x check-for-diff
 test_set_editor "$PWD/check-for-diff"
 
 cat >message <<'EOF'
@@ -23,7 +22,8 @@ test_expect_success 'setup' '
 '
 
 test_expect_success 'initial commit shows verbose diff' '
-       git commit --amend -v
+       git commit --amend -v &&
+       test_line_count = 1 out
 '
 
 test_expect_success 'second commit' '
@@ -39,13 +39,15 @@ check_message() {
 
 test_expect_success 'verbose diff is stripped out' '
        git commit --amend -v &&
-       check_message message
+       check_message message &&
+       test_line_count = 1 out
 '
 
 test_expect_success 'verbose diff is stripped out (mnemonicprefix)' '
        git config diff.mnemonicprefix true &&
        git commit --amend -v &&
-       check_message message
+       check_message message &&
+       test_line_count = 1 out
 '
 
 cat >diff <<'EOF'
@@ -96,4 +98,60 @@ test_expect_success 'verbose diff is stripped out with set core.commentChar' '
        test_i18ngrep "Aborting commit due to empty commit message." err
 '
 
+test_expect_success 'status does not verbose without --verbose' '
+       git status >actual &&
+       ! grep "^diff --git" actual
+'
+
+test_expect_success 'setup -v -v' '
+       echo dirty >file
+'
+
+for i in true 1
+do
+       test_expect_success "commit.verbose=$i and --verbose omitted" "
+               git -c commit.verbose=$i commit --amend &&
+               test_line_count = 1 out
+       "
+done
+
+for i in false -2 -1 0
+do
+       test_expect_success "commit.verbose=$i and --verbose omitted" "
+               git -c commit.verbose=$i commit --amend &&
+               test_line_count = 0 out
+       "
+done
+
+for i in 2 3
+do
+       test_expect_success "commit.verbose=$i and --verbose omitted" "
+               git -c commit.verbose=$i commit --amend &&
+               test_line_count = 2 out
+       "
+done
+
+for i in true false -2 -1 0 1 2 3
+do
+       test_expect_success "commit.verbose=$i and --verbose" "
+               git -c commit.verbose=$i commit --amend --verbose &&
+               test_line_count = 1 out
+       "
+
+       test_expect_success "commit.verbose=$i and --no-verbose" "
+               git -c commit.verbose=$i commit --amend --no-verbose &&
+               test_line_count = 0 out
+       "
+
+       test_expect_success "commit.verbose=$i and -v -v" "
+               git -c commit.verbose=$i commit --amend -v -v &&
+               test_line_count = 2 out
+       "
+done
+
+test_expect_success "status ignores commit.verbose=true" '
+       git -c commit.verbose=true status >actual &&
+       ! grep "^diff --git actual"
+'
+
 test_done
index 0e4a682c6428a96af4d98bb7e24b0de7f4be865c..6729cb379fdb16160c79393b96a2f5b3025540f4 100755 (executable)
@@ -37,14 +37,14 @@ EOF
 
 test_expect_success 'untracked files overwritten by merge (fast and non-fast forward)' '
        test_must_fail git merge branch 2>out &&
-       test_cmp out expect &&
+       test_i18ncmp out expect &&
        git commit --allow-empty -m empty &&
        (
                GIT_MERGE_VERBOSITY=0 &&
                export GIT_MERGE_VERBOSITY &&
                test_must_fail git merge branch 2>out2
        ) &&
-       test_cmp out2 expect &&
+       test_i18ncmp out2 expect &&
        git reset --hard HEAD^
 '
 
@@ -53,7 +53,7 @@ error: Your local changes to the following files would be overwritten by merge:
        four
        three
        two
-Please, commit your changes or stash them before you can merge.
+Please commit your changes or stash them before you can merge.
 error: The following untracked working tree files would be overwritten by merge:
        five
 Please move or remove them before you can merge.
@@ -65,14 +65,14 @@ test_expect_success 'untracked files or local changes ovewritten by merge' '
        git add three &&
        git add four &&
        test_must_fail git merge branch 2>out &&
-       test_cmp out expect
+       test_i18ncmp out expect
 '
 
 cat >expect <<\EOF
 error: Your local changes to the following files would be overwritten by checkout:
        rep/one
        rep/two
-Please, commit your changes or stash them before you can switch branches.
+Please commit your changes or stash them before you can switch branches.
 Aborting
 EOF
 
@@ -87,21 +87,21 @@ test_expect_success 'cannot switch branches because of local changes' '
        echo uno >rep/one &&
        echo dos >rep/two &&
        test_must_fail git checkout branch 2>out &&
-       test_cmp out expect
+       test_i18ncmp out expect
 '
 
 cat >expect <<\EOF
 error: Your local changes to the following files would be overwritten by checkout:
        rep/one
        rep/two
-Please, commit your changes or stash them before you can switch branches.
+Please commit your changes or stash them before you can switch branches.
 Aborting
 EOF
 
 test_expect_success 'not uptodate file porcelain checkout error' '
        git add rep/one rep/two &&
        test_must_fail git checkout branch 2>out &&
-       test_cmp out expect
+       test_i18ncmp out expect
 '
 
 cat >expect <<\EOF
@@ -132,7 +132,7 @@ test_expect_success 'not_uptodate_dir porcelain checkout error' '
        >rep/untracked-file &&
        >rep2/untracked-file &&
        test_must_fail git checkout branch 2>out &&
-       test_cmp out ../expect
+       test_i18ncmp out ../expect
 '
 
 test_done
index 8d99eb303fd62a1c179ab31f471a3376898586b0..3978fc0b45de2645560207cc6e884a505506215d 100644 (file)
@@ -718,20 +718,13 @@ test_cmp_rev () {
        test_cmp expect.rev actual.rev
 }
 
-# Print a sequence of numbers or letters in increasing order.  This is
-# similar to GNU seq(1), but the latter might not be available
-# everywhere (and does not do letters).  It may be used like:
-#
-#      for i in $(test_seq 100)
-#      do
-#              for j in $(test_seq 10 20)
-#              do
-#                      for k in $(test_seq a z)
-#                      do
-#                              echo $i-$j-$k
-#                      done
-#              done
-#      done
+# Print a sequence of integers in increasing order, either with
+# two arguments (start and end):
+#
+#     test_seq 1 5 -- outputs 1 2 3 4 5 one line at a time
+#
+# or with one argument (end), in which case it starts counting
+# from 1.
 
 test_seq () {
        case $# in
@@ -739,7 +732,12 @@ test_seq () {
        2)      ;;
        *)      error "bug in the test script: not 1 or 2 parameters to test_seq" ;;
        esac
-       perl -le 'print for $ARGV[0]..$ARGV[1]' -- "$@"
+       test_seq_counter__=$1
+       while test "$test_seq_counter__" -le "$2"
+       do
+               echo "$test_seq_counter__"
+               test_seq_counter__=$(( $test_seq_counter__ + 1 ))
+       done
 }
 
 # This function can be used to schedule some commands to be run
index 286c5f33d1e7bac81045293f78d2adf69ce8378c..0055ebba46d539f30b9484335501571c3b4879bd 100644 (file)
@@ -322,6 +322,19 @@ else
        exec 4>/dev/null 3>/dev/null
 fi
 
+# Send any "-x" output directly to stderr to avoid polluting tests
+# which capture stderr. We can do this unconditionally since it
+# has no effect if tracing isn't turned on.
+#
+# Note that this sets up the trace fd as soon as we assign the variable, so it
+# must come after the creation of descriptor 4 above. Likewise, we must never
+# unset this, as it has the side effect of closing descriptor 4, which we
+# use to show verbose tests to the user.
+#
+# Note also that we don't need or want to export it. The tracing is local to
+# this shell, and we would not want to influence any shells we exec.
+BASH_XTRACEFD=4
+
 test_failure=0
 test_count=0
 test_fixed=0
index b934183236ca571093f55818f5018c278150e274..bd666b29ec12d4657d1939a6b7de61f55d52bd1f 100644 (file)
@@ -1152,7 +1152,7 @@ static void udt_close_if_finished(struct unidirectional_transfer *t)
 }
 
 /*
- * Tries to read read data from source into buffer. If buffer is full,
+ * Tries to read data from source into buffer. If buffer is full,
  * no data is read. Returns 0 on success, -1 on error.
  */
 static int udt_do_read(struct unidirectional_transfer *t)
@@ -1166,7 +1166,7 @@ static int udt_do_read(struct unidirectional_transfer *t)
        bytes = read(t->src, t->buf + t->bufuse, BUFFERSIZE - t->bufuse);
        if (bytes < 0 && errno != EWOULDBLOCK && errno != EAGAIN &&
                errno != EINTR) {
-               error("read(%s) failed: %s", t->src_name, strerror(errno));
+               error_errno("read(%s) failed", t->src_name);
                return -1;
        } else if (bytes == 0) {
                transfer_debug("%s EOF (with %i bytes in buffer)",
@@ -1193,7 +1193,7 @@ static int udt_do_write(struct unidirectional_transfer *t)
        transfer_debug("%s is writable", t->dest_name);
        bytes = xwrite(t->dest, t->buf, t->bufuse);
        if (bytes < 0 && errno != EWOULDBLOCK) {
-               error("write(%s) failed: %s", t->dest_name, strerror(errno));
+               error_errno("write(%s) failed", t->dest_name);
                return -1;
        } else if (bytes > 0) {
                t->bufuse -= bytes;
@@ -1306,7 +1306,7 @@ static int tloop_join(pid_t pid, const char *name)
 {
        int tret;
        if (waitpid(pid, &tret, 0) < 0) {
-               error("%s process failed to wait: %s", name, strerror(errno));
+               error_errno("%s process failed to wait", name);
                return 1;
        }
        if (!WIFEXITED(tret) || WEXITSTATUS(tret)) {
index 11308e9e5b72c05b2713fb5abec9fc5dae02815e..6bc9512a4516396c7ac0cc0fd5b5cebb01c60cbd 100644 (file)
@@ -58,40 +58,74 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
        int i;
        const char **msgs = opts->msgs;
        const char *msg;
-       const char *cmd2 = strcmp(cmd, "checkout") ? cmd : "switch branches";
 
-       if (advice_commit_before_merge)
-               msg = "Your local changes to the following files would be overwritten by %s:\n%%s"
-                       "Please, commit your changes or stash them before you can %s.";
+       if (!strcmp(cmd, "checkout"))
+               msg = advice_commit_before_merge
+                     ? _("Your local changes to the following files would be overwritten by checkout:\n%%s"
+                         "Please commit your changes or stash them before you can switch branches.")
+                     : _("Your local changes to the following files would be overwritten by checkout:\n%%s");
+       else if (!strcmp(cmd, "merge"))
+               msg = advice_commit_before_merge
+                     ? _("Your local changes to the following files would be overwritten by merge:\n%%s"
+                         "Please commit your changes or stash them before you can merge.")
+                     : _("Your local changes to the following files would be overwritten by merge:\n%%s");
        else
-               msg = "Your local changes to the following files would be overwritten by %s:\n%%s";
+               msg = advice_commit_before_merge
+                     ? _("Your local changes to the following files would be overwritten by %s:\n%%s"
+                         "Please commit your changes or stash them before you can %s.")
+                     : _("Your local changes to the following files would be overwritten by %s:\n%%s");
        msgs[ERROR_WOULD_OVERWRITE] = msgs[ERROR_NOT_UPTODATE_FILE] =
-               xstrfmt(msg, cmd, cmd2);
+               xstrfmt(msg, cmd, cmd);
 
        msgs[ERROR_NOT_UPTODATE_DIR] =
-               "Updating the following directories would lose untracked files in it:\n%s";
-
-       if (advice_commit_before_merge)
-               msg = "The following untracked working tree files would be %s by %s:\n%%s"
-                       "Please move or remove them before you can %s.";
+               _("Updating the following directories would lose untracked files in it:\n%s");
+
+       if (!strcmp(cmd, "checkout"))
+               msg = advice_commit_before_merge
+                     ? _("The following untracked working tree files would be removed by checkout:\n%%s"
+                         "Please move or remove them before you can switch branches.")
+                     : _("The following untracked working tree files would be removed by checkout:\n%%s");
+       else if (!strcmp(cmd, "merge"))
+               msg = advice_commit_before_merge
+                     ? _("The following untracked working tree files would be removed by merge:\n%%s"
+                         "Please move or remove them before you can merge.")
+                     : _("The following untracked working tree files would be removed by merge:\n%%s");
        else
-               msg = "The following untracked working tree files would be %s by %s:\n%%s";
-
-       msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = xstrfmt(msg, "removed", cmd, cmd2);
-       msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = xstrfmt(msg, "overwritten", cmd, cmd2);
+               msg = advice_commit_before_merge
+                     ? _("The following untracked working tree files would be removed by %s:\n%%s"
+                         "Please move or remove them before you can %s.")
+                     : _("The following untracked working tree files would be removed by %s:\n%%s");
+       msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = xstrfmt(msg, cmd, cmd);
+
+       if (!strcmp(cmd, "checkout"))
+               msg = advice_commit_before_merge
+                     ? _("The following untracked working tree files would be overwritten by checkout:\n%%s"
+                         "Please move or remove them before you can switch branches.")
+                     : _("The following untracked working tree files would be overwritten by checkout:\n%%s");
+       else if (!strcmp(cmd, "merge"))
+               msg = advice_commit_before_merge
+                     ? _("The following untracked working tree files would be overwritten by merge:\n%%s"
+                         "Please move or remove them before you can merge.")
+                     : _("The following untracked working tree files would be overwritten by merge:\n%%s");
+       else
+               msg = advice_commit_before_merge
+                     ? _("The following untracked working tree files would be overwritten by %s:\n%%s"
+                         "Please move or remove them before you can %s.")
+                     : _("The following untracked working tree files would be overwritten by %s:\n%%s");
+       msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = xstrfmt(msg, cmd, cmd);
 
        /*
         * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we
         * cannot easily display it as a list.
         */
-       msgs[ERROR_BIND_OVERLAP] = "Entry '%s' overlaps with '%s'.  Cannot bind.";
+       msgs[ERROR_BIND_OVERLAP] = _("Entry '%s' overlaps with '%s'.  Cannot bind.");
 
        msgs[ERROR_SPARSE_NOT_UPTODATE_FILE] =
-               "Cannot update sparse checkout: the following entries are not up-to-date:\n%s";
+               _("Cannot update sparse checkout: the following entries are not up-to-date:\n%s");
        msgs[ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN] =
-               "The following Working tree files would be overwritten by sparse checkout update:\n%s";
+               _("The following Working tree files would be overwritten by sparse checkout update:\n%s");
        msgs[ERROR_WOULD_LOSE_ORPHANED_REMOVED] =
-               "The following Working tree files would be removed by sparse checkout update:\n%s";
+               _("The following Working tree files would be removed by sparse checkout update:\n%s");
 
        opts->show_all_errors = 1;
        /* rejected paths may not have a static buffer */
@@ -168,7 +202,7 @@ static void display_error_msgs(struct unpack_trees_options *o)
                string_list_clear(rejects, 0);
        }
        if (something_displayed)
-               fprintf(stderr, "Aborting\n");
+               fprintf(stderr, _("Aborting\n"));
 }
 
 /*
@@ -1499,8 +1533,7 @@ static int verify_absent_1(const struct cache_entry *ce,
 
                path = xmemdupz(ce->name, len);
                if (lstat(path, &st))
-                       ret = error("cannot stat '%s': %s", path,
-                                       strerror(errno));
+                       ret = error_errno("cannot stat '%s'", path);
                else
                        ret = check_ok_to_remove(path, len, DT_UNKNOWN, NULL,
                                                 &st, error_type, o);
@@ -1508,8 +1541,7 @@ static int verify_absent_1(const struct cache_entry *ce,
                return ret;
        } else if (lstat(ce->name, &st)) {
                if (errno != ENOENT)
-                       return error("cannot stat '%s': %s", ce->name,
-                                    strerror(errno));
+                       return error_errno("cannot stat '%s'", ce->name);
                return 0;
        } else {
                return check_ok_to_remove(ce->name, ce_namelen(ce),
index dc802a07c2225463c2e4ee923b9cf144d57a00cc..f19444df7bc9c19da4d2f3bf831525f7a5d6034d 100644 (file)
@@ -174,8 +174,7 @@ static void create_pack_file(void)
 
                if (ret < 0) {
                        if (errno != EINTR) {
-                               error("poll failed, resuming: %s",
-                                     strerror(errno));
+                               error_errno("poll failed, resuming");
                                sleep(1);
                        }
                        continue;
diff --git a/usage.c b/usage.c
index 82ff13163b542aab07e68bfa1056dc46316634a1..1dad03fb5c9b7f8f7785915d004aff43e39b0d2a 100644 (file)
--- a/usage.c
+++ b/usage.c
@@ -109,19 +109,11 @@ void NORETURN die(const char *err, ...)
        va_end(params);
 }
 
-void NORETURN die_errno(const char *fmt, ...)
+static const char *fmt_with_err(char *buf, int n, const char *fmt)
 {
-       va_list params;
-       char fmt_with_err[1024];
        char str_error[256], *err;
        int i, j;
 
-       if (die_is_recursing()) {
-               fputs("fatal: recursion detected in die_errno handler\n",
-                       stderr);
-               exit(128);
-       }
-
        err = strerror(errno);
        for (i = j = 0; err[i] && j < sizeof(str_error) - 1; ) {
                if ((str_error[j++] = err[i++]) != '%')
@@ -136,13 +128,37 @@ void NORETURN die_errno(const char *fmt, ...)
                }
        }
        str_error[j] = 0;
-       snprintf(fmt_with_err, sizeof(fmt_with_err), "%s: %s", fmt, str_error);
+       snprintf(buf, n, "%s: %s", fmt, str_error);
+       return buf;
+}
+
+void NORETURN die_errno(const char *fmt, ...)
+{
+       char buf[1024];
+       va_list params;
+
+       if (die_is_recursing()) {
+               fputs("fatal: recursion detected in die_errno handler\n",
+                       stderr);
+               exit(128);
+       }
 
        va_start(params, fmt);
-       die_routine(fmt_with_err, params);
+       die_routine(fmt_with_err(buf, sizeof(buf), fmt), params);
        va_end(params);
 }
 
+int error_errno(const char *fmt, ...)
+{
+       char buf[1024];
+       va_list params;
+
+       va_start(params, fmt);
+       error_routine(fmt_with_err(buf, sizeof(buf), fmt), params);
+       va_end(params);
+       return -1;
+}
+
 #undef error
 int error(const char *err, ...)
 {
@@ -154,6 +170,16 @@ int error(const char *err, ...)
        return -1;
 }
 
+void warning_errno(const char *warn, ...)
+{
+       char buf[1024];
+       va_list params;
+
+       va_start(params, warn);
+       warn_routine(fmt_with_err(buf, sizeof(buf), warn), params);
+       va_end(params);
+}
+
 void warning(const char *warn, ...)
 {
        va_list params;
diff --git a/utf8.h b/utf8.h
index 7930b44f19c701cc671d9639289782abb1812034..6bbcf31a831d60faf119fdc3f82f1eb10233e255 100644 (file)
--- a/utf8.h
+++ b/utf8.h
@@ -48,7 +48,7 @@ static inline char *reencode_string(const char *in,
 int mbs_chrlen(const char **text, size_t *remainder_p, const char *encoding);
 
 /*
- * Returns true if the the path would match ".git" after HFS case-folding.
+ * Returns true if the path would match ".git" after HFS case-folding.
  * The path should be NUL-terminated, but we will match variants of both ".git\0"
  * and ".git/..." (but _not_ ".../.git"). This makes it suitable for both fsck
  * and verify_path().
index 57cc1cec033f638e6d408370039b46b653289e84..e416caf8a4fc744232ca95450bce2b6109388aad 100644 (file)
@@ -53,9 +53,9 @@ long buffer_tmpfile_prepare_to_read(struct line_buffer *buf)
 {
        long pos = ftell(buf->infile);
        if (pos < 0)
-               return error("ftell error: %s", strerror(errno));
+               return error_errno("ftell error");
        if (fseek(buf->infile, 0, SEEK_SET))
-               return error("seek error: %s", strerror(errno));
+               return error_errno("seek error");
        return pos;
 }
 
index f11d490995ee7e3367c1d18e1058f2e322cfa177..06d273c9e8ce2b6b8ddbf967693e5305355d5be6 100644 (file)
@@ -12,7 +12,7 @@ static int input_error(struct line_buffer *file)
 {
        if (!buffer_ferror(file))
                return error("delta preimage ends early");
-       return error("cannot read delta preimage: %s", strerror(errno));
+       return error_errno("cannot read delta preimage");
 }
 
 static int skip_or_whine(struct line_buffer *file, off_t gap)
index 74c97c4543d1fa707961aab1453edfb69b2679d2..75c753162ab3b39293b258ec51f06dcc8d07ec3a 100644 (file)
@@ -64,13 +64,13 @@ static int write_strbuf(struct strbuf *sb, FILE *out)
 {
        if (fwrite(sb->buf, 1, sb->len, out) == sb->len)        /* Success. */
                return 0;
-       return error("cannot write delta postimage: %s", strerror(errno));
+       return error_errno("cannot write delta postimage");
 }
 
 static int error_short_read(struct line_buffer *input)
 {
        if (buffer_ferror(input))
-               return error("error reading delta: %s", strerror(errno));
+               return error_errno("error reading delta");
        return error("invalid delta: unexpected end of file");
 }
 
index 31d1d83d451e0e78f19067799d14e555a62e2cc8..e4b395963b9457a680a369c3e1997c799f8793ab 100644 (file)
@@ -501,7 +501,7 @@ static void init(int report_fd)
 int svndump_init(const char *filename)
 {
        if (buffer_init(&input, filename))
-               return error("cannot open %s: %s", filename ? filename : "NULL", strerror(errno));
+               return error_errno("cannot open %s", filename ? filename : "NULL");
        init(REPORT_FILENO);
        return 0;
 }
@@ -509,7 +509,7 @@ int svndump_init(const char *filename)
 int svndump_init_fd(int in_fd, int back_fd)
 {
        if(buffer_fdinit(&input, xdup(in_fd)))
-               return error("cannot open fd %d: %s", in_fd, strerror(errno));
+               return error_errno("cannot open fd %d", in_fd);
        init(xdup(back_fd));
        return 0;
 }
index f91ba99f32c047e5f3238668ae83de647ab92df2..57c876580592ab246d0c5c20cf20079d2097243c 100644 (file)
@@ -136,7 +136,7 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
                                /*
                                 * Try to advance faster when an asterisk is
                                 * followed by a literal. We know in this case
-                                * that the the string before the literal
+                                * that the string before the literal
                                 * must belong to "*".
                                 * If match_slash is false, do not look past
                                 * the first slash as it cannot belong to '*'.
index 6181a66f1ee2e1e45d7d8b2c88d312746473661f..199b1ef94ba84aad094a5a0fe95360c2df631003 100644 (file)
@@ -2,6 +2,8 @@
 #include "refs.h"
 #include "strbuf.h"
 #include "worktree.h"
+#include "dir.h"
+#include "wt-status.h"
 
 void free_worktrees(struct worktree **worktrees)
 {
@@ -9,7 +11,7 @@ void free_worktrees(struct worktree **worktrees)
 
        for (i = 0; worktrees[i]; i++) {
                free(worktrees[i]->path);
-               free(worktrees[i]->git_dir);
+               free(worktrees[i]->id);
                free(worktrees[i]->head_ref);
                free(worktrees[i]);
        }
@@ -18,7 +20,7 @@ void free_worktrees(struct worktree **worktrees)
 
 /*
  * read 'path_to_ref' into 'ref'.  Also if is_detached is not NULL,
- * set is_detached to 1 (0) if the ref is detatched (is not detached).
+ * set is_detached to 1 (0) if the ref is detached (is not detached).
  *
  * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside $GIT_DIR so
  * for linked worktrees, `resolve_ref_unsafe()` won't work (it uses
@@ -74,13 +76,11 @@ static struct worktree *get_main_worktree(void)
        struct worktree *worktree = NULL;
        struct strbuf path = STRBUF_INIT;
        struct strbuf worktree_path = STRBUF_INIT;
-       struct strbuf gitdir = STRBUF_INIT;
        struct strbuf head_ref = STRBUF_INIT;
        int is_bare = 0;
        int is_detached = 0;
 
-       strbuf_addf(&gitdir, "%s", absolute_path(get_git_common_dir()));
-       strbuf_addbuf(&worktree_path, &gitdir);
+       strbuf_addstr(&worktree_path, absolute_path(get_git_common_dir()));
        is_bare = !strbuf_strip_suffix(&worktree_path, "/.git");
        if (is_bare)
                strbuf_strip_suffix(&worktree_path, "/.");
@@ -92,15 +92,15 @@ static struct worktree *get_main_worktree(void)
 
        worktree = xmalloc(sizeof(struct worktree));
        worktree->path = strbuf_detach(&worktree_path, NULL);
-       worktree->git_dir = strbuf_detach(&gitdir, NULL);
+       worktree->id = NULL;
        worktree->is_bare = is_bare;
        worktree->head_ref = NULL;
        worktree->is_detached = is_detached;
+       worktree->is_current = 0;
        add_head_info(&head_ref, worktree);
 
 done:
        strbuf_release(&path);
-       strbuf_release(&gitdir);
        strbuf_release(&worktree_path);
        strbuf_release(&head_ref);
        return worktree;
@@ -111,16 +111,13 @@ static struct worktree *get_linked_worktree(const char *id)
        struct worktree *worktree = NULL;
        struct strbuf path = STRBUF_INIT;
        struct strbuf worktree_path = STRBUF_INIT;
-       struct strbuf gitdir = STRBUF_INIT;
        struct strbuf head_ref = STRBUF_INIT;
        int is_detached = 0;
 
        if (!id)
                die("Missing linked worktree name");
 
-       strbuf_addf(&gitdir, "%s/worktrees/%s",
-                       absolute_path(get_git_common_dir()), id);
-       strbuf_addf(&path, "%s/gitdir", gitdir.buf);
+       strbuf_git_common_path(&path, "worktrees/%s/gitdir", id);
        if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
                /* invalid gitdir file */
                goto done;
@@ -140,20 +137,39 @@ static struct worktree *get_linked_worktree(const char *id)
 
        worktree = xmalloc(sizeof(struct worktree));
        worktree->path = strbuf_detach(&worktree_path, NULL);
-       worktree->git_dir = strbuf_detach(&gitdir, NULL);
+       worktree->id = xstrdup(id);
        worktree->is_bare = 0;
        worktree->head_ref = NULL;
        worktree->is_detached = is_detached;
+       worktree->is_current = 0;
        add_head_info(&head_ref, worktree);
 
 done:
        strbuf_release(&path);
-       strbuf_release(&gitdir);
        strbuf_release(&worktree_path);
        strbuf_release(&head_ref);
        return worktree;
 }
 
+static void mark_current_worktree(struct worktree **worktrees)
+{
+       struct strbuf git_dir = STRBUF_INIT;
+       struct strbuf path = STRBUF_INIT;
+       int i;
+
+       strbuf_addstr(&git_dir, absolute_path(get_git_dir()));
+       for (i = 0; worktrees[i]; i++) {
+               struct worktree *wt = worktrees[i];
+               strbuf_addstr(&path, absolute_path(get_worktree_git_dir(wt)));
+               wt->is_current = !fspathcmp(git_dir.buf, path.buf);
+               strbuf_reset(&path);
+               if (wt->is_current)
+                       break;
+       }
+       strbuf_release(&git_dir);
+       strbuf_release(&path);
+}
+
 struct worktree **get_worktrees(void)
 {
        struct worktree **list = NULL;
@@ -185,35 +201,105 @@ struct worktree **get_worktrees(void)
        }
        ALLOC_GROW(list, counter + 1, alloc);
        list[counter] = NULL;
+
+       mark_current_worktree(list);
        return list;
 }
 
-char *find_shared_symref(const char *symref, const char *target)
+const char *get_worktree_git_dir(const struct worktree *wt)
+{
+       if (!wt)
+               return get_git_dir();
+       else if (!wt->id)
+               return get_git_common_dir();
+       else
+               return git_common_path("worktrees/%s", wt->id);
+}
+
+int is_worktree_being_rebased(const struct worktree *wt,
+                             const char *target)
+{
+       struct wt_status_state state;
+       int found_rebase;
+
+       memset(&state, 0, sizeof(state));
+       found_rebase = wt_status_check_rebase(wt, &state) &&
+               ((state.rebase_in_progress ||
+                 state.rebase_interactive_in_progress) &&
+                state.branch &&
+                starts_with(target, "refs/heads/") &&
+                !strcmp(state.branch, target + strlen("refs/heads/")));
+       free(state.branch);
+       free(state.onto);
+       return found_rebase;
+}
+
+int is_worktree_being_bisected(const struct worktree *wt,
+                              const char *target)
 {
-       char *existing = NULL;
+       struct wt_status_state state;
+       int found_rebase;
+
+       memset(&state, 0, sizeof(state));
+       found_rebase = wt_status_check_bisect(wt, &state) &&
+               state.branch &&
+               starts_with(target, "refs/heads/") &&
+               !strcmp(state.branch, target + strlen("refs/heads/"));
+       free(state.branch);
+       return found_rebase;
+}
+
+/*
+ * note: this function should be able to detect shared symref even if
+ * HEAD is temporarily detached (e.g. in the middle of rebase or
+ * bisect). New commands that do similar things should update this
+ * function as well.
+ */
+const struct worktree *find_shared_symref(const char *symref,
+                                         const char *target)
+{
+       const struct worktree *existing = NULL;
        struct strbuf path = STRBUF_INIT;
        struct strbuf sb = STRBUF_INIT;
-       struct worktree **worktrees = get_worktrees();
+       static struct worktree **worktrees;
        int i = 0;
 
+       if (worktrees)
+               free_worktrees(worktrees);
+       worktrees = get_worktrees();
+
        for (i = 0; worktrees[i]; i++) {
+               struct worktree *wt = worktrees[i];
+
+               if (wt->is_detached && !strcmp(symref, "HEAD")) {
+                       if (is_worktree_being_rebased(wt, target)) {
+                               existing = wt;
+                               break;
+                       }
+                       if (is_worktree_being_bisected(wt, target)) {
+                               existing = wt;
+                               break;
+                       }
+               }
+
                strbuf_reset(&path);
                strbuf_reset(&sb);
-               strbuf_addf(&path, "%s/%s", worktrees[i]->git_dir, symref);
+               strbuf_addf(&path, "%s/%s",
+                           get_worktree_git_dir(wt),
+                           symref);
 
                if (parse_ref(path.buf, &sb, NULL)) {
                        continue;
                }
 
                if (!strcmp(sb.buf, target)) {
-                       existing = xstrdup(worktrees[i]->path);
+                       existing = wt;
                        break;
                }
        }
 
        strbuf_release(&path);
        strbuf_release(&sb);
-       free_worktrees(worktrees);
 
        return existing;
 }
index b4b3dda79280707d2d65bf790be15c7a1e662b5e..13949093cc1610fcafbefb22d1f8b9441fbd4ade 100644 (file)
@@ -3,11 +3,12 @@
 
 struct worktree {
        char *path;
-       char *git_dir;
+       char *id;
        char *head_ref;
        unsigned char head_sha1[20];
        int is_detached;
        int is_bare;
+       int is_current;
 };
 
 /* Functions for acting on the information about worktrees. */
@@ -22,6 +23,12 @@ struct worktree {
  */
 extern struct worktree **get_worktrees(void);
 
+/*
+ * Return git dir of the worktree. Note that the path may be relative.
+ * If wt is NULL, git dir of current worktree is returned.
+ */
+extern const char *get_worktree_git_dir(const struct worktree *wt);
+
 /*
  * Free up the memory for worktree(s)
  */
@@ -29,10 +36,21 @@ extern void free_worktrees(struct worktree **);
 
 /*
  * Check if a per-worktree symref points to a ref in the main worktree
- * or any linked worktree, and return the path to the exising worktree
- * if it is.  Returns NULL if there is no existing ref.  The caller is
- * responsible for freeing the returned path.
+ * or any linked worktree, and return the worktree that holds the ref,
+ * or NULL otherwise. The result may be destroyed by the next call.
+ */
+extern const struct worktree *find_shared_symref(const char *symref,
+                                                const char *target);
+
+int is_worktree_being_rebased(const struct worktree *wt, const char *target);
+int is_worktree_being_bisected(const struct worktree *wt, const char *target);
+
+/*
+ * Similar to git_path() but can produce paths for a specified
+ * worktree instead of current one
  */
-extern char *find_shared_symref(const char *symref, const char *target);
+extern const char *worktree_git_path(const struct worktree *wt,
+                                    const char *fmt, ...)
+       __attribute__((format (printf, 2, 3)));
 
 #endif
index db0ec6a7370fd56e4e11c49a4b9e5e15f461e1f9..22b6e4948fbfa97da33dd0299da2130944940a42 100644 (file)
@@ -17,6 +17,7 @@ fi
 GITPERLLIB='@@BUILD_DIR@@/perl/blib/lib'"${GITPERLLIB:+:$GITPERLLIB}"
 GIT_TEXTDOMAINDIR='@@BUILD_DIR@@/po/build/locale'
 PATH='@@BUILD_DIR@@/bin-wrappers:'"$PATH"
+
 export GIT_EXEC_PATH GITPERLLIB PATH GIT_TEXTDOMAINDIR
 
 if test -n "$GIT_TEST_GDB"
index 9009f8bd3d32a768454ad5fbe6cb8f7b88781a24..5dc4e15aa9bf73bb2ce0a7af1837e092e0cab585 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -555,7 +555,7 @@ static int warn_if_unremovable(const char *op, const char *file, int rc)
        if (!rc || errno == ENOENT)
                return 0;
        err = errno;
-       warning("unable to %s %s: %s", op, file, strerror(errno));
+       warning_errno("unable to %s %s", op, file);
        errno = err;
        return rc;
 }
@@ -591,7 +591,7 @@ int remove_or_warn(unsigned int mode, const char *file)
 
 void warn_on_inaccessible(const char *path)
 {
-       warning(_("unable to access '%s': %s"), path, strerror(errno));
+       warning_errno(_("unable to access '%s'"), path);
 }
 
 static int access_error_is_ok(int err, unsigned flag)
index 1ea2ebe4c00d23885515ac48f43844fdb03f70b9..4f27bd62af992122d83df911c42b585342ac1066 100644 (file)
@@ -15,6 +15,7 @@
 #include "column.h"
 #include "strbuf.h"
 #include "utf8.h"
+#include "worktree.h"
 
 static const char cut_line[] =
 "------------------------ >8 ------------------------\n";
@@ -950,6 +951,7 @@ static void show_merge_in_progress(struct wt_status *s,
                        status_printf_ln(s, color,
                                _("  (fix conflicts and run \"git commit\")"));
        } else {
+               s-> commitable = 1;
                status_printf_ln(s, color,
                        _("All conflicts fixed but you are still merging."));
                if (s->hints)
@@ -1262,13 +1264,13 @@ static void show_bisect_in_progress(struct wt_status *s,
 /*
  * Extract branch information from rebase/bisect
  */
-static char *read_and_strip_branch(const char *path)
+static char *get_branch(const struct worktree *wt, const char *path)
 {
        struct strbuf sb = STRBUF_INIT;
        unsigned char sha1[20];
        const char *branch_name;
 
-       if (strbuf_read_file(&sb, git_path("%s", path), 0) <= 0)
+       if (strbuf_read_file(&sb, worktree_git_path(wt, "%s", path), 0) <= 0)
                goto got_nothing;
 
        while (sb.len && sb.buf[sb.len - 1] == '\n')
@@ -1360,40 +1362,62 @@ static void wt_status_get_detached_from(struct wt_status_state *state)
        strbuf_release(&cb.buf);
 }
 
-void wt_status_get_state(struct wt_status_state *state,
-                        int get_detached_from)
+int wt_status_check_rebase(const struct worktree *wt,
+                          struct wt_status_state *state)
 {
        struct stat st;
-       unsigned char sha1[20];
 
-       if (!stat(git_path_merge_head(), &st)) {
-               state->merge_in_progress = 1;
-       } else if (!stat(git_path("rebase-apply"), &st)) {
-               if (!stat(git_path("rebase-apply/applying"), &st)) {
+       if (!stat(worktree_git_path(wt, "rebase-apply"), &st)) {
+               if (!stat(worktree_git_path(wt, "rebase-apply/applying"), &st)) {
                        state->am_in_progress = 1;
-                       if (!stat(git_path("rebase-apply/patch"), &st) && !st.st_size)
+                       if (!stat(worktree_git_path(wt, "rebase-apply/patch"), &st) && !st.st_size)
                                state->am_empty_patch = 1;
                } else {
                        state->rebase_in_progress = 1;
-                       state->branch = read_and_strip_branch("rebase-apply/head-name");
-                       state->onto = read_and_strip_branch("rebase-apply/onto");
+                       state->branch = get_branch(wt, "rebase-apply/head-name");
+                       state->onto = get_branch(wt, "rebase-apply/onto");
                }
-       } else if (!stat(git_path("rebase-merge"), &st)) {
-               if (!stat(git_path("rebase-merge/interactive"), &st))
+       } else if (!stat(worktree_git_path(wt, "rebase-merge"), &st)) {
+               if (!stat(worktree_git_path(wt, "rebase-merge/interactive"), &st))
                        state->rebase_interactive_in_progress = 1;
                else
                        state->rebase_in_progress = 1;
-               state->branch = read_and_strip_branch("rebase-merge/head-name");
-               state->onto = read_and_strip_branch("rebase-merge/onto");
+               state->branch = get_branch(wt, "rebase-merge/head-name");
+               state->onto = get_branch(wt, "rebase-merge/onto");
+       } else
+               return 0;
+       return 1;
+}
+
+int wt_status_check_bisect(const struct worktree *wt,
+                          struct wt_status_state *state)
+{
+       struct stat st;
+
+       if (!stat(worktree_git_path(wt, "BISECT_LOG"), &st)) {
+               state->bisect_in_progress = 1;
+               state->branch = get_branch(wt, "BISECT_START");
+               return 1;
+       }
+       return 0;
+}
+
+void wt_status_get_state(struct wt_status_state *state,
+                        int get_detached_from)
+{
+       struct stat st;
+       unsigned char sha1[20];
+
+       if (!stat(git_path_merge_head(), &st)) {
+               state->merge_in_progress = 1;
+       } else if (wt_status_check_rebase(NULL, state)) {
+               ;               /* all set */
        } else if (!stat(git_path_cherry_pick_head(), &st) &&
                        !get_sha1("CHERRY_PICK_HEAD", sha1)) {
                state->cherry_pick_in_progress = 1;
                hashcpy(state->cherry_pick_head_sha1, sha1);
        }
-       if (!stat(git_path("BISECT_LOG"), &st)) {
-               state->bisect_in_progress = 1;
-               state->branch = read_and_strip_branch("BISECT_START");
-       }
+       wt_status_check_bisect(NULL, state);
        if (!stat(git_path_revert_head(), &st) &&
            !get_sha1("REVERT_HEAD", sha1)) {
                state->revert_in_progress = 1;
index c9b3b744e923f2f559f64f1bf4a1f2159b5060d6..2ca93f6957f69cd5652ddc764f5afe81b598a2a9 100644 (file)
@@ -6,6 +6,8 @@
 #include "color.h"
 #include "pathspec.h"
 
+struct worktree;
+
 enum color_wt_status {
        WT_STATUS_HEADER = 0,
        WT_STATUS_UPDATED,
@@ -100,6 +102,10 @@ void wt_status_prepare(struct wt_status *s);
 void wt_status_print(struct wt_status *s);
 void wt_status_collect(struct wt_status *s);
 void wt_status_get_state(struct wt_status_state *state, int get_detached_from);
+int wt_status_check_rebase(const struct worktree *wt,
+                          struct wt_status_state *state);
+int wt_status_check_bisect(const struct worktree *wt,
+                          struct wt_status_state *state);
 
 void wt_shortstatus_print(struct wt_status *s);
 void wt_porcelain_print(struct wt_status *s);