]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'jk/t0000-subtests-fix'
authorJunio C Hamano <gitster@pobox.com>
Wed, 28 Jul 2021 20:18:00 +0000 (13:18 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 28 Jul 2021 20:18:00 +0000 (13:18 -0700)
Test fix.

* jk/t0000-subtests-fix:
  t0000: clear GIT_SKIP_TESTS before running sub-tests

67 files changed:
.github/workflows/main.yml
Documentation/RelNotes/2.33.0.txt
Documentation/config/sendemail.txt
Documentation/git-diff.txt
Documentation/git-worktree.txt
Documentation/gitignore.txt
Documentation/rev-list-options.txt
Makefile
add-patch.c
advice.h
builtin/am.c
builtin/bisect--helper.c
builtin/commit.c
builtin/diff.c
builtin/index-pack.c
builtin/log.c
builtin/receive-pack.c
builtin/rev-list.c
builtin/worktree.c
cache.h
cbtree.c [new file with mode: 0644]
cbtree.h [new file with mode: 0644]
commit-graph.c
contrib/credential/osxkeychain/git-credential-osxkeychain.c
contrib/credential/wincred/git-credential-wincred.c
dir.c
dir.h
gettext.c
git-send-email.perl
hash.h
imap-send.c
log-tree.c
mailmap.c
merge-ort.c
merge-recursive.c
midx.c
object-file.c
object-name.c
object-store.h
object.c
object.h
oidtree.c [new file with mode: 0644]
oidtree.h [new file with mode: 0644]
perl/Git.pm
pkt-line.c
pretty.c
pretty.h
quote.h
reachable.c
ref-filter.c
remote-curl.c
revision.h
sequencer.c
serve.c
server-info.c
strbuf.h
t/helper/test-advise.c
t/helper/test-oidtree.c [new file with mode: 0644]
t/helper/test-tool.c
t/helper/test-tool.h
t/t0069-oidtree.sh [new file with mode: 0755]
t/t2400-worktree-add.sh
t/t4202-log.sh
t/t6006-rev-list-format.sh
t/t7500-commit-template-squash-signoff.sh
t/t9001-send-email.sh
worktree.c

index 73856bafc9debd7cfed82419706a4b023c69651b..e6f99e29a3db08f05c63edb3ddaa0757f2b8b748 100644 (file)
@@ -81,44 +81,21 @@ jobs:
     if: needs.ci-config.outputs.enabled == 'yes'
     runs-on: windows-latest
     steps:
-    - uses: actions/checkout@v1
-    - name: download git-sdk-64-minimal
-      shell: bash
-      run: |
-        ## Get artifact
-        urlbase=https://dev.azure.com/git-for-windows/git/_apis/build/builds
-        id=$(curl "$urlbase?definitions=22&statusFilter=completed&resultFilter=succeeded&\$top=1" |
-          jq -r ".value[] | .id")
-        download_url="$(curl "$urlbase/$id/artifacts" |
-          jq -r '.value[] | select(.name == "git-sdk-64-minimal").resource.downloadUrl')"
-        curl --connect-timeout 10 --retry 5 --retry-delay 0 --retry-max-time 240 \
-          -o artifacts.zip "$download_url"
-
-        ## Unzip and remove the artifact
-        unzip artifacts.zip
-        rm artifacts.zip
+    - uses: actions/checkout@v2
+    - uses: git-for-windows/setup-git-for-windows-sdk@v1
     - name: build
-      shell: powershell
+      shell: bash
       env:
         HOME: ${{runner.workspace}}
-        MSYSTEM: MINGW64
         NO_PERL: 1
-      run: |
-        & .\git-sdk-64-minimal\usr\bin\bash.exe -lc @"
-        printf '%s\n' /git-sdk-64-minimal/ >>.git/info/exclude
-
-          ci/make-test-artifacts.sh artifacts
-        "@
-    - name: upload build artifacts
-      uses: actions/upload-artifact@v1
+      run: ci/make-test-artifacts.sh artifacts
+    - name: zip up tracked files
+      run: git archive -o artifacts/tracked.tar.gz HEAD
+    - name: upload tracked files and build artifacts
+      uses: actions/upload-artifact@v2
       with:
         name: windows-artifacts
         path: artifacts
-    - name: upload git-sdk-64-minimal
-      uses: actions/upload-artifact@v1
-      with:
-        name: git-sdk-64-minimal
-        path: git-sdk-64-minimal
   windows-test:
     runs-on: windows-latest
     needs: [windows-build]
@@ -127,37 +104,25 @@ jobs:
       matrix:
         nr: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
     steps:
-    - uses: actions/checkout@v1
-    - name: download build artifacts
-      uses: actions/download-artifact@v1
+    - name: download tracked files and build artifacts
+      uses: actions/download-artifact@v2
       with:
         name: windows-artifacts
         path: ${{github.workspace}}
-    - name: extract build artifacts
+    - name: extract tracked files and build artifacts
       shell: bash
-      run: tar xf artifacts.tar.gz
-    - name: download git-sdk-64-minimal
-      uses: actions/download-artifact@v1
-      with:
-        name: git-sdk-64-minimal
-        path: ${{github.workspace}}/git-sdk-64-minimal/
+      run: tar xf artifacts.tar.gz && tar xf tracked.tar.gz
+    - uses: git-for-windows/setup-git-for-windows-sdk@v1
     - name: test
-      shell: powershell
-      run: |
-        & .\git-sdk-64-minimal\usr\bin\bash.exe -lc @"
-          # Let Git ignore the SDK
-          printf '%s\n' /git-sdk-64-minimal/ >>.git/info/exclude
-
-          ci/run-test-slice.sh ${{matrix.nr}} 10
-        "@
+      shell: bash
+      run: ci/run-test-slice.sh ${{matrix.nr}} 10
     - name: ci/print-test-failures.sh
       if: failure()
-      shell: powershell
-      run: |
-        & .\git-sdk-64-minimal\usr\bin\bash.exe -lc ci/print-test-failures.sh
+      shell: bash
+      run: ci/print-test-failures.sh
     - name: Upload failed tests' directories
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
-      uses: actions/upload-artifact@v1
+      uses: actions/upload-artifact@v2
       with:
         name: failed-tests-windows
         path: ${{env.FAILED_TEST_ARTIFACTS}}
@@ -165,27 +130,12 @@ jobs:
     needs: ci-config
     if: needs.ci-config.outputs.enabled == 'yes'
     env:
-      MSYSTEM: MINGW64
       NO_PERL: 1
       GIT_CONFIG_PARAMETERS: "'user.name=CI' 'user.email=ci@git'"
     runs-on: windows-latest
     steps:
-    - uses: actions/checkout@v1
-    - name: download git-sdk-64-minimal
-      shell: bash
-      run: |
-        ## Get artifact
-        urlbase=https://dev.azure.com/git-for-windows/git/_apis/build/builds
-        id=$(curl "$urlbase?definitions=22&statusFilter=completed&resultFilter=succeeded&\$top=1" |
-          jq -r ".value[] | .id")
-        download_url="$(curl "$urlbase/$id/artifacts" |
-          jq -r '.value[] | select(.name == "git-sdk-64-minimal").resource.downloadUrl')"
-        curl --connect-timeout 10 --retry 5 --retry-delay 0 --retry-max-time 240 \
-          -o artifacts.zip "$download_url"
-
-        ## Unzip and remove the artifact
-        unzip artifacts.zip
-        rm artifacts.zip
+    - uses: actions/checkout@v2
+    - uses: git-for-windows/setup-git-for-windows-sdk@v1
     - name: initialize vcpkg
       uses: actions/checkout@v2
       with:
@@ -203,75 +153,60 @@ jobs:
     - name: add msbuild to PATH
       uses: microsoft/setup-msbuild@v1
     - name: copy dlls to root
-      shell: powershell
-      run: |
-        & compat\vcbuild\vcpkg_copy_dlls.bat release
-        if (!$?) { exit(1) }
+      shell: cmd
+      run: compat\vcbuild\vcpkg_copy_dlls.bat release
     - name: generate Visual Studio solution
       shell: bash
       run: |
         cmake `pwd`/contrib/buildsystems/ -DCMAKE_PREFIX_PATH=`pwd`/compat/vcbuild/vcpkg/installed/x64-windows \
-        -DMSGFMT_EXE=`pwd`/git-sdk-64-minimal/mingw64/bin/msgfmt.exe -DPERL_TESTS=OFF -DPYTHON_TESTS=OFF -DCURL_NO_CURL_CMAKE=ON
+        -DNO_GETTEXT=YesPlease -DPERL_TESTS=OFF -DPYTHON_TESTS=OFF -DCURL_NO_CURL_CMAKE=ON
     - name: MSBuild
       run: msbuild git.sln -property:Configuration=Release -property:Platform=x64 -maxCpuCount:4 -property:PlatformToolset=v142
     - name: bundle artifact tar
-      shell: powershell
+      shell: bash
       env:
         MSVC: 1
         VCPKG_ROOT: ${{github.workspace}}\compat\vcbuild\vcpkg
       run: |
-        & git-sdk-64-minimal\usr\bin\bash.exe -lc @"
-          mkdir -p artifacts &&
-          eval \"`$(make -n artifacts-tar INCLUDE_DLLS_IN_ARTIFACTS=YesPlease ARTIFACTS_DIRECTORY=artifacts 2>&1 | grep ^tar)\"
-        "@
-    - name: upload build artifacts
-      uses: actions/upload-artifact@v1
+        mkdir -p artifacts &&
+        eval "$(make -n artifacts-tar INCLUDE_DLLS_IN_ARTIFACTS=YesPlease ARTIFACTS_DIRECTORY=artifacts NO_GETTEXT=YesPlease 2>&1 | grep ^tar)"
+    - name: zip up tracked files
+      run: git archive -o artifacts/tracked.tar.gz HEAD
+    - name: upload tracked files and build artifacts
+      uses: actions/upload-artifact@v2
       with:
         name: vs-artifacts
         path: artifacts
   vs-test:
     runs-on: windows-latest
-    needs: [vs-build, windows-build]
+    needs: vs-build
     strategy:
       fail-fast: false
       matrix:
         nr: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
     steps:
-    - uses: actions/checkout@v1
-    - name: download git-sdk-64-minimal
-      uses: actions/download-artifact@v1
-      with:
-        name: git-sdk-64-minimal
-        path: ${{github.workspace}}/git-sdk-64-minimal/
-    - name: download build artifacts
-      uses: actions/download-artifact@v1
+    - uses: git-for-windows/setup-git-for-windows-sdk@v1
+    - name: download tracked files and build artifacts
+      uses: actions/download-artifact@v2
       with:
         name: vs-artifacts
         path: ${{github.workspace}}
-    - name: extract build artifacts
+    - name: extract tracked files and build artifacts
       shell: bash
-      run: tar xf artifacts.tar.gz
+      run: tar xf artifacts.tar.gz && tar xf tracked.tar.gz
     - name: test
-      shell: powershell
+      shell: bash
       env:
-        MSYSTEM: MINGW64
         NO_SVN_TESTS: 1
         GIT_TEST_SKIP_REBASE_P: 1
-      run: |
-        & .\git-sdk-64-minimal\usr\bin\bash.exe -lc @"
-          # Let Git ignore the SDK and the test-cache
-          printf '%s\n' /git-sdk-64-minimal/ /test-cache/ >>.git/info/exclude
-
-          ci/run-test-slice.sh ${{matrix.nr}} 10
-        "@
+      run: ci/run-test-slice.sh ${{matrix.nr}} 10
     - name: ci/print-test-failures.sh
       if: failure()
-      shell: powershell
-      run: |
-        & .\git-sdk-64-minimal\usr\bin\bash.exe -lc ci/print-test-failures.sh
+      shell: bash
+      run: ci/print-test-failures.sh
     - name: Upload failed tests' directories
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
-      uses: actions/upload-artifact@v1
+      uses: actions/upload-artifact@v2
       with:
         name: failed-tests-windows
         path: ${{env.FAILED_TEST_ARTIFACTS}}
@@ -302,14 +237,14 @@ jobs:
       jobname: ${{matrix.vector.jobname}}
     runs-on: ${{matrix.vector.pool}}
     steps:
-    - uses: actions/checkout@v1
+    - uses: actions/checkout@v2
     - run: ci/install-dependencies.sh
     - run: ci/run-build-and-tests.sh
     - run: ci/print-test-failures.sh
       if: failure()
     - name: Upload failed tests' directories
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
-      uses: actions/upload-artifact@v1
+      uses: actions/upload-artifact@v2
       with:
         name: failed-tests-${{matrix.vector.jobname}}
         path: ${{env.FAILED_TEST_ARTIFACTS}}
@@ -336,7 +271,7 @@ jobs:
       if: failure()
     - name: Upload failed tests' directories
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
-      uses: actions/upload-artifact@v1
+      uses: actions/upload-artifact@v2
       with:
         name: failed-tests-${{matrix.vector.jobname}}
         path: ${{env.FAILED_TEST_ARTIFACTS}}
@@ -347,7 +282,7 @@ jobs:
       jobname: StaticAnalysis
     runs-on: ubuntu-18.04
     steps:
-    - uses: actions/checkout@v1
+    - uses: actions/checkout@v2
     - run: ci/install-dependencies.sh
     - run: ci/run-static-analysis.sh
   documentation:
@@ -357,6 +292,6 @@ jobs:
       jobname: Documentation
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v1
+    - uses: actions/checkout@v2
     - run: ci/install-dependencies.sh
     - run: ci/test-documentation.sh
index d4c56de5cb7e66a89769442d882c57ab6cab4f72..a69531c1efe48290c86645de86c17dc883069bdc 100644 (file)
@@ -30,6 +30,9 @@ UI, Workflows & Features
 
  * The userdiff pattern for C# learned the token "record".
 
+ * "git rev-list" learns to omit the "commit <object-name>" header
+   lines from the output with the `--no-commit-header` option.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -54,6 +57,11 @@ Performance, Internal Implementation, Development Support etc.
 
  * Code cleanup around struct_type_init() functions.
 
+ * "git send-email" optimization.
+
+ * GitHub Actions / CI update.
+   (merge 0dc787a9f2 js/ci-windows-update later to maint).
+
 
 Fixes since v2.32
 -----------------
@@ -208,3 +216,5 @@ Fixes since v2.32
    (merge 617480d75b hn/refs-iterator-peel-returns-boolean later to maint).
    (merge 6a24cc71ed ar/submodule-helper-include-cleanup later to maint).
    (merge 5632e838f8 rs/khash-alloc-cleanup later to maint).
+   (merge b1d87fbaf1 jk/typofix later to maint).
+   (merge e04170697a ab/gitignore-discovery-doc later to maint).
index cbc5af42fdf05c1d9450c55a4342c58714e9a766..50baa5d6bfbe8ad8f60e9f538b0643a13e1c8576 100644 (file)
@@ -8,9 +8,6 @@ sendemail.smtpEncryption::
        See linkgit:git-send-email[1] for description.  Note that this
        setting is not subject to the 'identity' mechanism.
 
-sendemail.smtpssl (deprecated)::
-       Deprecated alias for 'sendemail.smtpEncryption = ssl'.
-
 sendemail.smtpsslcertpath::
        Path to ca-certificates (either a directory or a single file).
        Set it to an empty string to disable certificate verification.
index 7f4c8a8ce7fd54f87e2c3936a6a1cf1cc623c60d..6236c75c9b826a315e553ead169b7fc3fb183c72 100644 (file)
@@ -51,16 +51,20 @@ files on disk.
        --staged is a synonym of --cached.
 +
 If --merge-base is given, instead of using <commit>, use the merge base
-of <commit> and HEAD.  `git diff --merge-base A` is equivalent to
-`git diff $(git merge-base A HEAD)`.
+of <commit> and HEAD.  `git diff --cached --merge-base A` is equivalent to
+`git diff --cached $(git merge-base A HEAD)`.
 
-'git diff' [<options>] <commit> [--] [<path>...]::
+'git diff' [<options>] [--merge-base] <commit> [--] [<path>...]::
 
        This form is to view the changes you have in your
        working tree relative to the named <commit>.  You can
        use HEAD to compare it with the latest commit, or a
        branch name to compare with the tip of a different
        branch.
++
+If --merge-base is given, instead of using <commit>, use the merge base
+of <commit> and HEAD.  `git diff --merge-base A` is equivalent to
+`git diff $(git merge-base A HEAD)`.
 
 'git diff' [<options>] [--merge-base] <commit> <commit> [--] [<path>...]::
 
index 66e67e6cbfaa7d3e37a8425cf7548c2477d18550..8a7cbdd19c151e71a5de0fce27c7140f81f95724 100644 (file)
@@ -9,7 +9,7 @@ git-worktree - Manage multiple working trees
 SYNOPSIS
 --------
 [verse]
-'git worktree add' [-f] [--detach] [--checkout] [--lock] [-b <new-branch>] <path> [<commit-ish>]
+'git worktree add' [-f] [--detach] [--checkout] [--lock [--reason <string>]] [-b <new-branch>] <path> [<commit-ish>]
 'git worktree list' [--porcelain]
 'git worktree lock' [--reason <string>] <worktree>
 'git worktree move' <worktree> <new-path>
@@ -242,7 +242,7 @@ With `list`, annotate missing working trees as prunable if they are
 older than `<time>`.
 
 --reason <string>::
-       With `lock`, an explanation why the working tree is locked.
+       With `lock` or with `add --lock`, an explanation why the working tree is locked.
 
 <worktree>::
        Working trees can be identified by path, either relative or
index 53e7d5c914d2d2e220a39a36a82cbd7f10fee9bc..f8a1fc20147ed324177b5b351bafeff3bf5d64e0 100644 (file)
@@ -27,12 +27,11 @@ precedence, the last matching pattern decides the outcome):
    them.
 
  * Patterns read from a `.gitignore` file in the same directory
-   as the path, or in any parent directory, with patterns in the
-   higher level files (up to the toplevel of the work tree) being overridden
-   by those in lower level files down to the directory containing the file.
-   These patterns match relative to the location of the
-   `.gitignore` file.  A project normally includes such
-   `.gitignore` files in its repository, containing patterns for
+   as the path, or in any parent directory (up to the top-level of the working
+   tree), with patterns in the higher level files being overridden by those in
+   lower level files down to the directory containing the file. These patterns
+   match relative to the location of the `.gitignore` file.  A project normally
+   includes such `.gitignore` files in its repository, containing patterns for
    files generated as part of the project build.
 
  * Patterns read from `$GIT_DIR/info/exclude`.
index 5bf2a85f69da26c9aef033b98b73be4215e2e6e3..24569b06d19d1df690d180000a133008e381d4a3 100644 (file)
@@ -897,7 +897,7 @@ which are not of the requested type.
 +
 The form '--filter=sparse:oid=<blob-ish>' uses a sparse-checkout
 specification contained in the blob (or blob-expression) '<blob-ish>'
-to omit blobs that would not be not required for a sparse checkout on
+to omit blobs that would not be required for a sparse checkout on
 the requested refs.
 +
 The form '--filter=tree:<depth>' omits all blobs and trees whose depth
@@ -1064,6 +1064,14 @@ ifdef::git-rev-list[]
 --header::
        Print the contents of the commit in raw-format; each record is
        separated with a NUL character.
+
+--no-commit-header::
+       Suppress the header line containing "commit" and the object ID printed before
+       the specified format.  This has no effect on the built-in formats; only custom
+       formats are affected.
+
+--commit-header::
+       Overrides a previous `--no-commit-header`.
 endif::git-rev-list[]
 
 --parents::
index c7c46c017d3ba8f8cc773e746df3a499c30c5a4f..c6f6246bf63ef244ae94d503dbc0d8f757e29af6 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -726,6 +726,7 @@ TEST_BUILTINS_OBJS += test-mergesort.o
 TEST_BUILTINS_OBJS += test-mktemp.o
 TEST_BUILTINS_OBJS += test-oid-array.o
 TEST_BUILTINS_OBJS += test-oidmap.o
+TEST_BUILTINS_OBJS += test-oidtree.o
 TEST_BUILTINS_OBJS += test-online-cpus.o
 TEST_BUILTINS_OBJS += test-parse-options.o
 TEST_BUILTINS_OBJS += test-parse-pathspec-file.o
@@ -850,6 +851,7 @@ LIB_OBJS += branch.o
 LIB_OBJS += bulk-checkin.o
 LIB_OBJS += bundle.o
 LIB_OBJS += cache-tree.o
+LIB_OBJS += cbtree.o
 LIB_OBJS += chdir-notify.o
 LIB_OBJS += checkout.o
 LIB_OBJS += chunk-format.o
@@ -945,6 +947,7 @@ LIB_OBJS += object.o
 LIB_OBJS += oid-array.o
 LIB_OBJS += oidmap.o
 LIB_OBJS += oidset.o
+LIB_OBJS += oidtree.o
 LIB_OBJS += pack-bitmap-write.o
 LIB_OBJS += pack-bitmap.o
 LIB_OBJS += pack-check.o
@@ -2687,10 +2690,13 @@ po/git.pot: $(GENERATED_H) FORCE
 .PHONY: pot
 pot: po/git.pot
 
+ifdef NO_GETTEXT
+POFILES :=
+MOFILES :=
+else
 POFILES := $(wildcard po/*.po)
 MOFILES := $(patsubst po/%.po,po/build/locale/%/LC_MESSAGES/git.mo,$(POFILES))
 
-ifndef NO_GETTEXT
 all:: $(MOFILES)
 endif
 
index 2fad92ca373292f328d5e5de4584638faa7bbd45..8c41cdfe39be041e7d119737a83d754a06733649 100644 (file)
@@ -280,6 +280,7 @@ static void add_p_state_clear(struct add_p_state *s)
        clear_add_i_state(&s->s);
 }
 
+__attribute__((format (printf, 2, 3)))
 static void err(struct add_p_state *s, const char *fmt, ...)
 {
        va_list args;
index bd26c385d00dd3af9e9bbd60812697607107b8dd..9f8ffc73546b3939ad5ca9496b187c7aeb60242e 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -90,6 +90,7 @@ int advice_enabled(enum advice_type type);
 /**
  * Checks the visibility of the advice before printing.
  */
+__attribute__((format (printf, 2, 3)))
 void advise_if_enabled(enum advice_type type, const char *advice, ...);
 
 int error_resolve_conflict(const char *me);
index 0b2d886c81b775d4b36cd575669808b8fc98868a..0c2ad96b70eef408492987a8b358887cc56dcb9c 100644 (file)
@@ -210,6 +210,7 @@ static void write_state_bool(const struct am_state *state,
  * If state->quiet is false, calls fprintf(fp, fmt, ...), and appends a newline
  * at the end.
  */
+__attribute__((format (printf, 3, 4)))
 static void say(const struct am_state *state, FILE *fp, const char *fmt, ...)
 {
        va_list ap;
index 9d9540a0abfe20e4a725797ba7aad65eecfd5d92..f184eaeac6d0fd60ae8d255c458f6b873f2bd3cf 100644 (file)
@@ -117,6 +117,7 @@ static int write_in_file(const char *path, const char *mode, const char *format,
        return fclose(fp);
 }
 
+__attribute__((format (printf, 2, 3)))
 static int write_to_file(const char *path, const char *format, ...)
 {
        int res;
@@ -129,6 +130,7 @@ static int write_to_file(const char *path, const char *format, ...)
        return res;
 }
 
+__attribute__((format (printf, 2, 3)))
 static int append_to_file(const char *path, const char *format, ...)
 {
        int res;
index 190d215d43b37b27f276347eb3bd1010264917f6..cfbc83751a5f3fadaecd9bd512f85854b888398e 100644 (file)
@@ -889,7 +889,22 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                int ident_shown = 0;
                int saved_color_setting;
                struct ident_split ci, ai;
-
+               const char *hint_cleanup_all = allow_empty_message ?
+                       _("Please enter the commit message for your changes."
+                         " Lines starting\nwith '%c' will be ignored.\n") :
+                       _("Please enter the commit message for your changes."
+                         " Lines starting\nwith '%c' will be ignored, and an empty"
+                         " message aborts the commit.\n");
+               const char *hint_cleanup_space = allow_empty_message ?
+                       _("Please enter the commit message for your changes."
+                         " Lines starting\n"
+                         "with '%c' will be kept; you may remove them"
+                         " yourself if you want to.\n") :
+                       _("Please enter the commit message for your changes."
+                         " Lines starting\n"
+                         "with '%c' will be kept; you may remove them"
+                         " yourself if you want to.\n"
+                         "An empty message aborts the commit.\n");
                if (whence != FROM_COMMIT) {
                        if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
                                !merge_contains_scissors)
@@ -911,20 +926,12 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
                fprintf(s->fp, "\n");
                if (cleanup_mode == COMMIT_MSG_CLEANUP_ALL)
-                       status_printf(s, GIT_COLOR_NORMAL,
-                               _("Please enter the commit message for your changes."
-                                 " Lines starting\nwith '%c' will be ignored, and an empty"
-                                 " message aborts the commit.\n"), comment_line_char);
+                       status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_all, comment_line_char);
                else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
                        if (whence == FROM_COMMIT && !merge_contains_scissors)
                                wt_status_add_cut_line(s->fp);
                } else /* COMMIT_MSG_CLEANUP_SPACE, that is. */
-                       status_printf(s, GIT_COLOR_NORMAL,
-                               _("Please enter the commit message for your changes."
-                                 " Lines starting\n"
-                                 "with '%c' will be kept; you may remove them"
-                                 " yourself if you want to.\n"
-                                 "An empty message aborts the commit.\n"), comment_line_char);
+                       status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_space, comment_line_char);
 
                /*
                 * These should never fail because they come from our own
index 2d87c37a17674fda2fa7e90425164f69bcd78f41..dd8ce688ba7054e7ee76ca4764b1a17b84a3044a 100644 (file)
@@ -26,8 +26,8 @@
 
 static const char builtin_diff_usage[] =
 "git diff [<options>] [<commit>] [--] [<path>...]\n"
-"   or: git diff [<options>] --cached [<commit>] [--] [<path>...]\n"
-"   or: git diff [<options>] <commit> [--merge-base] [<commit>...] <commit> [--] [<path>...]\n"
+"   or: git diff [<options>] --cached [--merge-base] [<commit>] [--] [<path>...]\n"
+"   or: git diff [<options>] [--merge-base] <commit> [<commit>...] <commit> [--] [<path>...]\n"
 "   or: git diff [<options>] <commit>...<commit>] [--] [<path>...]\n"
 "   or: git diff [<options>] <blob> <blob>]\n"
 "   or: git diff [<options>] --no-index [--] <path> <path>]\n"
index 3fbc5d70777e5045a78a3d29e1ec240b0cc6ecab..8336466865cbef122d552da29611196a1164bcbc 100644 (file)
@@ -369,9 +369,7 @@ static void parse_pack_header(void)
        use(sizeof(struct pack_header));
 }
 
-static NORETURN void bad_object(off_t offset, const char *format,
-                      ...) __attribute__((format (printf, 2, 3)));
-
+__attribute__((format (printf, 2, 3)))
 static NORETURN void bad_object(off_t offset, const char *format, ...)
 {
        va_list params;
index 516a1142ddf0bc9bfb532612acf390ec2e4149ca..3d7717ba5ca234c06595e74aaeeab7c5f91114dd 100644 (file)
@@ -245,6 +245,9 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
                        rev->abbrev_commit = 0;
        }
 
+       if (rev->commit_format == CMIT_FMT_USERFORMAT && !w.decorate)
+               decoration_style = 0;
+
        if (decoration_style) {
                const struct string_list *config_exclude =
                        repo_config_get_value_multi(the_repository,
index a34742513aca7e771c60263ded17d24d0cdc9df5..2d1f97e1ca7b5346d9cae3239165d6824df309af 100644 (file)
@@ -425,9 +425,6 @@ static int proc_receive_ref_matches(struct command *cmd)
        return 0;
 }
 
-static void rp_error(const char *err, ...) __attribute__((format (printf, 1, 2)));
-static void rp_warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
-
 static void report_message(const char *prefix, const char *err, va_list params)
 {
        int sz;
@@ -445,6 +442,7 @@ static void report_message(const char *prefix, const char *err, va_list params)
                xwrite(2, msg, sz);
 }
 
+__attribute__((format (printf, 1, 2)))
 static void rp_warning(const char *err, ...)
 {
        va_list params;
@@ -453,6 +451,7 @@ static void rp_warning(const char *err, ...)
        va_end(params);
 }
 
+__attribute__((format (printf, 1, 2)))
 static void rp_error(const char *err, ...)
 {
        va_list params;
index 7677b1af5a45fcaf6df9afd00660e98081c68bd8..36cb909ebaa51940024dce982dd53e5109c653eb 100644 (file)
@@ -127,13 +127,15 @@ static void show_commit(struct commit *commit, void *data)
        if (info->header_prefix)
                fputs(info->header_prefix, stdout);
 
-       if (!revs->graph)
-               fputs(get_revision_mark(revs, commit), stdout);
-       if (revs->abbrev_commit && revs->abbrev)
-               fputs(find_unique_abbrev(&commit->object.oid, revs->abbrev),
-                     stdout);
-       else
-               fputs(oid_to_hex(&commit->object.oid), stdout);
+       if (revs->include_header) {
+               if (!revs->graph)
+                       fputs(get_revision_mark(revs, commit), stdout);
+               if (revs->abbrev_commit && revs->abbrev)
+                       fputs(find_unique_abbrev(&commit->object.oid, revs->abbrev),
+                             stdout);
+               else
+                       fputs(oid_to_hex(&commit->object.oid), stdout);
+       }
        if (revs->print_parents) {
                struct commit_list *parents = commit->parents;
                while (parents) {
@@ -153,7 +155,7 @@ static void show_commit(struct commit *commit, void *data)
        show_decorations(revs, commit);
        if (revs->commit_format == CMIT_FMT_ONELINE)
                putchar(' ');
-       else
+       else if (revs->include_header)
                putchar('\n');
 
        if (revs->verbose_header) {
@@ -512,6 +514,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
        repo_init_revisions(the_repository, &revs, prefix);
        revs.abbrev = DEFAULT_ABBREV;
        revs.commit_format = CMIT_FMT_UNSPECIFIED;
+       revs.include_header = 1;
 
        /*
         * Scan the argument list before invoking setup_revisions(), so that we
@@ -627,6 +630,16 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                        continue;
                }
 
+               if (!strcmp(arg, ("--commit-header"))) {
+                       revs.include_header = 1;
+                       continue;
+               }
+
+               if (!strcmp(arg, ("--no-commit-header"))) {
+                       revs.include_header = 0;
+                       continue;
+               }
+
                if (!strcmp(arg, "--disk-usage")) {
                        show_disk_usage = 1;
                        info.flags |= REV_LIST_QUIET;
@@ -636,10 +649,12 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                usage(rev_list_usage);
 
        }
+       if (revs.commit_format != CMIT_FMT_USERFORMAT)
+               revs.include_header = 1;
        if (revs.commit_format != CMIT_FMT_UNSPECIFIED) {
                /* The command line has a --pretty  */
                info.hdr_termination = '\n';
-               if (revs.commit_format == CMIT_FMT_ONELINE)
+               if (revs.commit_format == CMIT_FMT_ONELINE || !revs.include_header)
                        info.header_prefix = "";
                else
                        info.header_prefix = "commit ";
index 976bf8ed063f1db6d2ab6582b0ed7faea4c31a42..0d0a80da61f1ee4944a8728091ac472389dc6255 100644 (file)
@@ -30,7 +30,7 @@ struct add_opts {
        int detach;
        int quiet;
        int checkout;
-       int keep_locked;
+       const char *keep_locked;
 };
 
 static int show_only;
@@ -302,10 +302,10 @@ static int add_worktree(const char *path, const char *refname,
         * after the preparation is over.
         */
        strbuf_addf(&sb, "%s/locked", sb_repo.buf);
-       if (!opts->keep_locked)
-               write_file(sb.buf, "initializing");
+       if (opts->keep_locked)
+               write_file(sb.buf, "%s", opts->keep_locked);
        else
-               write_file(sb.buf, "added with --lock");
+               write_file(sb.buf, _("initializing"));
 
        strbuf_addf(&sb_git, "%s/.git", path);
        if (safe_create_leading_directories_const(sb_git.buf))
@@ -475,6 +475,8 @@ static int add(int ac, const char **av, const char *prefix)
        const char *branch;
        const char *new_branch = NULL;
        const char *opt_track = NULL;
+       const char *lock_reason = NULL;
+       int keep_locked = 0;
        struct option options[] = {
                OPT__FORCE(&opts.force,
                           N_("checkout <branch> even if already checked out in other worktree"),
@@ -485,7 +487,9 @@ static int add(int ac, const char **av, const char *prefix)
                           N_("create or reset a branch")),
                OPT_BOOL('d', "detach", &opts.detach, N_("detach HEAD at named commit")),
                OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
-               OPT_BOOL(0, "lock", &opts.keep_locked, N_("keep the new working tree locked")),
+               OPT_BOOL(0, "lock", &keep_locked, N_("keep the new working tree locked")),
+               OPT_STRING(0, "reason", &lock_reason, N_("string"),
+                          N_("reason for locking")),
                OPT__QUIET(&opts.quiet, N_("suppress progress reporting")),
                OPT_PASSTHRU(0, "track", &opt_track, NULL,
                             N_("set up tracking mode (see git-branch(1))"),
@@ -500,6 +504,13 @@ static int add(int ac, const char **av, const char *prefix)
        ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
        if (!!opts.detach + !!new_branch + !!new_branch_force > 1)
                die(_("-b, -B, and --detach are mutually exclusive"));
+       if (lock_reason && !keep_locked)
+               die(_("--reason requires --lock"));
+       if (lock_reason)
+               opts.keep_locked = lock_reason;
+       else if (keep_locked)
+               opts.keep_locked = _("added with --lock");
+
        if (ac < 1 || ac > 2)
                usage_with_options(worktree_usage, options);
 
diff --git a/cache.h b/cache.h
index ba04ff8bd36b3627c95fc7b9b3fc2aec2d55c205..f9aed2d45c75084a00a79df82bcd6f2bebefaf97 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -1385,6 +1385,7 @@ enum get_oid_result {
 };
 
 int repo_get_oid(struct repository *r, const char *str, struct object_id *oid);
+__attribute__((format (printf, 2, 3)))
 int get_oidf(struct object_id *oid, const char *fmt, ...);
 int repo_get_oid_commit(struct repository *r, const char *str, struct object_id *oid);
 int repo_get_oid_committish(struct repository *r, const char *str, struct object_id *oid);
diff --git a/cbtree.c b/cbtree.c
new file mode 100644 (file)
index 0000000..b0c65d8
--- /dev/null
+++ b/cbtree.c
@@ -0,0 +1,167 @@
+/*
+ * crit-bit tree implementation, does no allocations internally
+ * For more information on crit-bit trees: https://cr.yp.to/critbit.html
+ * Based on Adam Langley's adaptation of Dan Bernstein's public domain code
+ * git clone https://github.com/agl/critbit.git
+ */
+#include "cbtree.h"
+
+static struct cb_node *cb_node_of(const void *p)
+{
+       return (struct cb_node *)((uintptr_t)p - 1);
+}
+
+/* locate the best match, does not do a final comparision */
+static struct cb_node *cb_internal_best_match(struct cb_node *p,
+                                       const uint8_t *k, size_t klen)
+{
+       while (1 & (uintptr_t)p) {
+               struct cb_node *q = cb_node_of(p);
+               uint8_t c = q->byte < klen ? k[q->byte] : 0;
+               size_t direction = (1 + (q->otherbits | c)) >> 8;
+
+               p = q->child[direction];
+       }
+       return p;
+}
+
+/* returns NULL if successful, existing cb_node if duplicate */
+struct cb_node *cb_insert(struct cb_tree *t, struct cb_node *node, size_t klen)
+{
+       size_t newbyte, newotherbits;
+       uint8_t c;
+       int newdirection;
+       struct cb_node **wherep, *p;
+
+       assert(!((uintptr_t)node & 1)); /* allocations must be aligned */
+
+       if (!t->root) {         /* insert into empty tree */
+               t->root = node;
+               return NULL;    /* success */
+       }
+
+       /* see if a node already exists */
+       p = cb_internal_best_match(t->root, node->k, klen);
+
+       /* find first differing byte */
+       for (newbyte = 0; newbyte < klen; newbyte++) {
+               if (p->k[newbyte] != node->k[newbyte])
+                       goto different_byte_found;
+       }
+       return p;       /* element exists, let user deal with it */
+
+different_byte_found:
+       newotherbits = p->k[newbyte] ^ node->k[newbyte];
+       newotherbits |= newotherbits >> 1;
+       newotherbits |= newotherbits >> 2;
+       newotherbits |= newotherbits >> 4;
+       newotherbits = (newotherbits & ~(newotherbits >> 1)) ^ 255;
+       c = p->k[newbyte];
+       newdirection = (1 + (newotherbits | c)) >> 8;
+
+       node->byte = newbyte;
+       node->otherbits = newotherbits;
+       node->child[1 - newdirection] = node;
+
+       /* find a place to insert it */
+       wherep = &t->root;
+       for (;;) {
+               struct cb_node *q;
+               size_t direction;
+
+               p = *wherep;
+               if (!(1 & (uintptr_t)p))
+                       break;
+               q = cb_node_of(p);
+               if (q->byte > newbyte)
+                       break;
+               if (q->byte == newbyte && q->otherbits > newotherbits)
+                       break;
+               c = q->byte < klen ? node->k[q->byte] : 0;
+               direction = (1 + (q->otherbits | c)) >> 8;
+               wherep = q->child + direction;
+       }
+
+       node->child[newdirection] = *wherep;
+       *wherep = (struct cb_node *)(1 + (uintptr_t)node);
+
+       return NULL; /* success */
+}
+
+struct cb_node *cb_lookup(struct cb_tree *t, const uint8_t *k, size_t klen)
+{
+       struct cb_node *p = cb_internal_best_match(t->root, k, klen);
+
+       return p && !memcmp(p->k, k, klen) ? p : NULL;
+}
+
+struct cb_node *cb_unlink(struct cb_tree *t, const uint8_t *k, size_t klen)
+{
+       struct cb_node **wherep = &t->root;
+       struct cb_node **whereq = NULL;
+       struct cb_node *q = NULL;
+       size_t direction = 0;
+       uint8_t c;
+       struct cb_node *p = t->root;
+
+       if (!p) return NULL;    /* empty tree, nothing to delete */
+
+       /* traverse to find best match, keeping link to parent */
+       while (1 & (uintptr_t)p) {
+               whereq = wherep;
+               q = cb_node_of(p);
+               c = q->byte < klen ? k[q->byte] : 0;
+               direction = (1 + (q->otherbits | c)) >> 8;
+               wherep = q->child + direction;
+               p = *wherep;
+       }
+
+       if (memcmp(p->k, k, klen))
+               return NULL;            /* no match, nothing unlinked */
+
+       /* found an exact match */
+       if (whereq)     /* update parent */
+               *whereq = q->child[1 - direction];
+       else
+               t->root = NULL;
+       return p;
+}
+
+static enum cb_next cb_descend(struct cb_node *p, cb_iter fn, void *arg)
+{
+       if (1 & (uintptr_t)p) {
+               struct cb_node *q = cb_node_of(p);
+               enum cb_next n = cb_descend(q->child[0], fn, arg);
+
+               return n == CB_BREAK ? n : cb_descend(q->child[1], fn, arg);
+       } else {
+               return fn(p, arg);
+       }
+}
+
+void cb_each(struct cb_tree *t, const uint8_t *kpfx, size_t klen,
+                       cb_iter fn, void *arg)
+{
+       struct cb_node *p = t->root;
+       struct cb_node *top = p;
+       size_t i = 0;
+
+       if (!p) return; /* empty tree */
+
+       /* Walk tree, maintaining top pointer */
+       while (1 & (uintptr_t)p) {
+               struct cb_node *q = cb_node_of(p);
+               uint8_t c = q->byte < klen ? kpfx[q->byte] : 0;
+               size_t direction = (1 + (q->otherbits | c)) >> 8;
+
+               p = q->child[direction];
+               if (q->byte < klen)
+                       top = p;
+       }
+
+       for (i = 0; i < klen; i++) {
+               if (p->k[i] != kpfx[i])
+                       return; /* "best" match failed */
+       }
+       cb_descend(top, fn, arg);
+}
diff --git a/cbtree.h b/cbtree.h
new file mode 100644 (file)
index 0000000..fe45870
--- /dev/null
+++ b/cbtree.h
@@ -0,0 +1,56 @@
+/*
+ * crit-bit tree implementation, does no allocations internally
+ * For more information on crit-bit trees: https://cr.yp.to/critbit.html
+ * Based on Adam Langley's adaptation of Dan Bernstein's public domain code
+ * git clone https://github.com/agl/critbit.git
+ *
+ * This is adapted to store arbitrary data (not just NUL-terminated C strings
+ * and allocates no memory internally.  The user needs to allocate
+ * "struct cb_node" and fill cb_node.k[] with arbitrary match data
+ * for memcmp.
+ * If "klen" is variable, then it should be embedded into "c_node.k[]"
+ * Recursion is bound by the maximum value of "klen" used.
+ */
+#ifndef CBTREE_H
+#define CBTREE_H
+
+#include "git-compat-util.h"
+
+struct cb_node;
+struct cb_node {
+       struct cb_node *child[2];
+       /*
+        * n.b. uint32_t for `byte' is excessive for OIDs,
+        * we may consider shorter variants if nothing else gets stored.
+        */
+       uint32_t byte;
+       uint8_t otherbits;
+       uint8_t k[FLEX_ARRAY]; /* arbitrary data */
+};
+
+struct cb_tree {
+       struct cb_node *root;
+};
+
+enum cb_next {
+       CB_CONTINUE = 0,
+       CB_BREAK = 1
+};
+
+#define CBTREE_INIT { .root = NULL }
+
+static inline void cb_init(struct cb_tree *t)
+{
+       t->root = NULL;
+}
+
+struct cb_node *cb_lookup(struct cb_tree *, const uint8_t *k, size_t klen);
+struct cb_node *cb_insert(struct cb_tree *, struct cb_node *, size_t klen);
+struct cb_node *cb_unlink(struct cb_tree *t, const uint8_t *k, size_t klen);
+
+typedef enum cb_next (*cb_iter)(struct cb_node *, void *arg);
+
+void cb_each(struct cb_tree *, const uint8_t *kpfx, size_t klen,
+               cb_iter, void *arg);
+
+#endif /* CBTREE_H */
index 1a2602da618c9bf6e5868c3993e747541ae7e75d..3860a0d847737930ed39286646d89c4e252e8367 100644 (file)
@@ -2408,6 +2408,7 @@ cleanup:
 #define VERIFY_COMMIT_GRAPH_ERROR_HASH 2
 static int verify_commit_graph_error;
 
+__attribute__((format (printf, 1, 2)))
 static void graph_report(const char *fmt, ...)
 {
        va_list ap;
index bcd3f575a3e38611a88a8a0fcd51d39b9edb1599..0b44a9b7cc6f2b859f4b5c42edc52f528bdde84e 100644 (file)
@@ -10,6 +10,7 @@ static char *username;
 static char *password;
 static UInt16 port;
 
+__attribute__((format (printf, 1, 2)))
 static void die(const char *err, ...)
 {
        char msg[4096];
index 5bdad41de1f834b0685390258161a9e749d0dca0..5091048f9c649f31f0c11c49f33a7e757043023a 100644 (file)
@@ -11,6 +11,7 @@
 
 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
 
+__attribute__((format (printf, 1, 2)))
 static void die(const char *err, ...)
 {
        char msg[4096];
diff --git a/dir.c b/dir.c
index 313e9324597a1ae1b03b3979be412b86ab63dcd0..0efa67fbecc0e32e5d9ed67ae6e549610789a30f 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -78,11 +78,21 @@ int fspathcmp(const char *a, const char *b)
        return ignore_case ? strcasecmp(a, b) : strcmp(a, b);
 }
 
+int fspatheq(const char *a, const char *b)
+{
+       return !fspathcmp(a, b);
+}
+
 int fspathncmp(const char *a, const char *b, size_t count)
 {
        return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);
 }
 
+unsigned int fspathhash(const char *str)
+{
+       return ignore_case ? strihash(str) : strhash(str);
+}
+
 int git_fnmatch(const struct pathspec_item *item,
                const char *pattern, const char *string,
                int prefix)
diff --git a/dir.h b/dir.h
index 8d0ddd8f18d9c3f2ac0385d6acb1688b28e66432..b3e1a54a97145d6be7385d044968a080e2f17ae8 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -489,7 +489,9 @@ int remove_dir_recursively(struct strbuf *path, int flag);
 int remove_path(const char *path);
 
 int fspathcmp(const char *a, const char *b);
+int fspatheq(const char *a, const char *b);
 int fspathncmp(const char *a, const char *b, size_t count);
+unsigned int fspathhash(const char *str);
 
 /*
  * The prefix part of pattern must not contains wildcards.
index af2413b47e85223536b63f90180dd4c0ac2404d3..bb5ba1fe7cc5979255fe79fd8dfc0085fc8df8f1 100644 (file)
--- a/gettext.c
+++ b/gettext.c
@@ -66,6 +66,7 @@ const char *get_preferred_languages(void)
 }
 
 #ifndef NO_GETTEXT
+__attribute__((format (printf, 1, 2)))
 static int test_vsnprintf(const char *fmt, ...)
 {
        char buf[26];
index 7ba0b3433d7c4b856cb76a772ca343aa4d9025fd..e65d969d0bb257f5c95ba7fe955a3788d4b98a32 100755 (executable)
 
 use 5.008;
 use strict;
-use warnings;
-use POSIX qw/strftime/;
-use Term::ReadLine;
+use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use Getopt::Long;
-use Text::ParseWords;
-use Term::ANSIColor;
-use File::Temp qw/ tempdir tempfile /;
-use File::Spec::Functions qw(catdir catfile);
 use Git::LoadCPAN::Error qw(:try);
-use Cwd qw(abs_path cwd);
 use Git;
 use Git::I18N;
-use Net::Domain ();
-use Net::SMTP ();
-use Git::LoadCPAN::Mail::Address;
 
 Getopt::Long::Configure qw/ pass_through /;
 
@@ -167,7 +157,6 @@ sub format_2822_time {
                       );
 }
 
-my $have_email_valid = eval { require Email::Valid; 1 };
 my $smtp;
 my $auth;
 my $num_sent = 0;
@@ -193,14 +182,6 @@ my (@config_bcc, @getopt_bcc);
 
 my $repo = eval { Git->repository() };
 my @repo = $repo ? ($repo) : ();
-my $term = eval {
-       $ENV{"GIT_SEND_EMAIL_NOTTY"}
-               ? new Term::ReadLine 'git-send-email', \*STDIN, \*STDOUT
-               : new Term::ReadLine 'git-send-email';
-};
-if ($@) {
-       $term = new FakeTerm "$@: going non-interactive";
-}
 
 # Behavior modification variables
 my ($quiet, $dry_run) = (0, 0);
@@ -289,6 +270,7 @@ my %config_bool_settings = (
 );
 
 my %config_settings = (
+    "smtpencryption" => \$smtp_encryption,
     "smtpserver" => \$smtp_server,
     "smtpserverport" => \$smtp_server_port,
     "smtpserveroption" => \@smtp_server_options,
@@ -321,9 +303,9 @@ my %config_path_settings = (
 
 # Handle Uncouth Termination
 sub signal_handler {
-
        # Make text normal
-       print color("reset"), "\n";
+       require Term::ANSIColor;
+       print Term::ANSIColor::color("reset"), "\n";
 
        # SMTP password masked
        system "stty echo";
@@ -349,11 +331,17 @@ $SIG{INT}  = \&signal_handler;
 
 # Read our sendemail.* config
 sub read_config {
-       my ($configured, $prefix) = @_;
+       my ($known_keys, $configured, $prefix) = @_;
 
        foreach my $setting (keys %config_bool_settings) {
                my $target = $config_bool_settings{$setting};
-               my $v = Git::config_bool(@repo, "$prefix.$setting");
+               my $key = "$prefix.$setting";
+               next unless exists $known_keys->{$key};
+               my $v = (@{$known_keys->{$key}} == 1 &&
+                        (defined $known_keys->{$key}->[0] &&
+                         $known_keys->{$key}->[0] =~ /^(?:true|false)$/s))
+                       ? $known_keys->{$key}->[0] eq 'true'
+                       : Git::config_bool(@repo, $key);
                next unless defined $v;
                next if $configured->{$setting}++;
                $$target = $v;
@@ -361,8 +349,10 @@ sub read_config {
 
        foreach my $setting (keys %config_path_settings) {
                my $target = $config_path_settings{$setting};
+               my $key = "$prefix.$setting";
+               next unless exists $known_keys->{$key};
                if (ref($target) eq "ARRAY") {
-                       my @values = Git::config_path(@repo, "$prefix.$setting");
+                       my @values = Git::config_path(@repo, $key);
                        next unless @values;
                        next if $configured->{$setting}++;
                        @$target = @values;
@@ -377,36 +367,64 @@ sub read_config {
 
        foreach my $setting (keys %config_settings) {
                my $target = $config_settings{$setting};
+               my $key = "$prefix.$setting";
+               next unless exists $known_keys->{$key};
                if (ref($target) eq "ARRAY") {
-                       my @values = Git::config(@repo, "$prefix.$setting");
-                       next unless @values;
+                       my @values = @{$known_keys->{$key}};
+                       @values = grep { defined } @values;
                        next if $configured->{$setting}++;
                        @$target = @values;
                }
                else {
-                       my $v = Git::config(@repo, "$prefix.$setting");
+                       my $v = $known_keys->{$key}->[0];
                        next unless defined $v;
                        next if $configured->{$setting}++;
                        $$target = $v;
                }
        }
+}
 
-       if (!defined $smtp_encryption) {
-               my $setting = "$prefix.smtpencryption";
-               my $enc = Git::config(@repo, $setting);
-               return unless defined $enc;
-               return if $configured->{$setting}++;
-               if (defined $enc) {
-                       $smtp_encryption = $enc;
-               } elsif (Git::config_bool(@repo, "$prefix.smtpssl")) {
-                       $smtp_encryption = 'ssl';
-               }
+sub config_regexp {
+       my ($regex) = @_;
+       my @ret;
+       eval {
+               my $ret = Git::command(
+                       'config',
+                       '--null',
+                       '--get-regexp',
+                       $regex,
+               );
+               @ret = map {
+                       # We must always return ($k, $v) here, since
+                       # empty config values will be just "key\0",
+                       # not "key\nvalue\0".
+                       my ($k, $v) = split /\n/, $_, 2;
+                       ($k, $v);
+               } split /\0/, $ret;
+               1;
+       } or do {
+               # If we have no keys we're OK, otherwise re-throw
+               die $@ if $@->value != 1;
+       };
+       return @ret;
+}
+
+# Save ourselves a lot of work of shelling out to 'git config' (it
+# parses 'bool' etc.) by only doing so for config keys that exist.
+my %known_config_keys;
+{
+       my @kv = config_regexp("^sende?mail[.]");
+       while (my ($k, $v) = splice @kv, 0, 2) {
+               push @{$known_config_keys{$k}} => $v;
        }
 }
 
 # sendemail.identity yields to --identity. We must parse this
 # special-case first before the rest of the config is read.
-$identity = Git::config(@repo, "sendemail.identity");
+{
+       my $key = "sendemail.identity";
+       $identity = Git::config(@repo, $key) if exists $known_config_keys{$key};
+}
 my $rc = GetOptions(
        "identity=s" => \$identity,
        "no-identity" => \$no_identity,
@@ -417,8 +435,8 @@ undef $identity if $no_identity;
 # Now we know enough to read the config
 {
     my %configured;
-    read_config(\%configured, "sendemail.$identity") if defined $identity;
-    read_config(\%configured, "sendemail");
+    read_config(\%known_config_keys, \%configured, "sendemail.$identity") if defined $identity;
+    read_config(\%known_config_keys, \%configured, "sendemail");
 }
 
 # Begin by accumulating all the variables (defined above), that we will end up
@@ -503,7 +521,7 @@ unless ($rc) {
     usage();
 }
 
-if ($forbid_sendmail_variables && (scalar Git::config_regexp("^sendmail[.]")) != 0) {
+if ($forbid_sendmail_variables && grep { /^sendmail/s } keys %known_config_keys) {
        die __("fatal: found configuration options for 'sendmail'\n" .
                "git-send-email is configured with the sendemail.* options - note the 'e'.\n" .
                "Set sendemail.forbidSendmailVariables to false to disable this check.\n");
@@ -568,15 +586,27 @@ if (0) {
 }
 
 my ($repoauthor, $repocommitter);
-($repoauthor) = Git::ident_person(@repo, 'author');
-($repocommitter) = Git::ident_person(@repo, 'committer');
+{
+       my %cache;
+       my ($author, $committer);
+       my $common = sub {
+               my ($what) = @_;
+               return $cache{$what} if exists $cache{$what};
+               ($cache{$what}) = Git::ident_person(@repo, $what);
+               return $cache{$what};
+       };
+       $repoauthor = sub { $common->('author') };
+       $repocommitter = sub { $common->('committer') };
+}
 
 sub parse_address_line {
+       require Git::LoadCPAN::Mail::Address;
        return map { $_->format } Mail::Address->parse($_[0]);
 }
 
 sub split_addrs {
-       return quotewords('\s*,\s*', 1, @_);
+       require Text::ParseWords;
+       return Text::ParseWords::quotewords('\s*,\s*', 1, @_);
 }
 
 my %aliases;
@@ -625,10 +655,11 @@ my %parse_alias = (
                        s/\\"/"/g foreach @addr;
                        $aliases{$alias} = \@addr
                }}},
-       mailrc => sub { my $fh = shift; while (<$fh>) {
+       mailrc => sub { my $fh = shift; while (<$fh>) {
                if (/^alias\s+(\S+)\s+(.*?)\s*$/) {
+                       require Text::ParseWords;
                        # spaces delimit multiple addresses
-                       $aliases{$1} = [ quotewords('\s+', 0, $2) ];
+                       $aliases{$1} = [ Text::ParseWords::quotewords('\s+', 0, $2) ];
                }}},
        pine => sub { my $fh = shift; my $f='\t[^\t]*';
                for (my $x = ''; defined($x); $x = $_) {
@@ -676,7 +707,7 @@ sub is_format_patch_arg {
                if (defined($format_patch)) {
                        return $format_patch;
                }
-               die sprintf(__ <<EOF, $f, $f);
+               die sprintf(__(<<EOF), $f, $f);
 File '%s' exists but it could also be the range of commits
 to produce patches for.  Please disambiguate by...
 
@@ -700,7 +731,8 @@ while (defined(my $f = shift @ARGV)) {
                opendir my $dh, $f
                        or die sprintf(__("Failed to opendir %s: %s"), $f, $!);
 
-               push @files, grep { -f $_ } map { catfile($f, $_) }
+               require File::Spec;
+               push @files, grep { -f $_ } map { File::Spec->catfile($f, $_) }
                                sort readdir $dh;
                closedir $dh;
        } elsif ((-f $f or -p $f) and !is_format_patch_arg($f)) {
@@ -713,7 +745,8 @@ while (defined(my $f = shift @ARGV)) {
 if (@rev_list_opts) {
        die __("Cannot run git format-patch from outside a repository\n")
                unless $repo;
-       push @files, $repo->command('format-patch', '-o', tempdir(CLEANUP => 1), @rev_list_opts);
+       require File::Temp;
+       push @files, $repo->command('format-patch', '-o', File::Temp::tempdir(CLEANUP => 1), @rev_list_opts);
 }
 
 @files = handle_backup_files(@files);
@@ -750,19 +783,20 @@ sub get_patch_subject {
 if ($compose) {
        # Note that this does not need to be secure, but we will make a small
        # effort to have it be unique
+       require File::Temp;
        $compose_filename = ($repo ?
-               tempfile(".gitsendemail.msg.XXXXXX", DIR => $repo->repo_path()) :
-               tempfile(".gitsendemail.msg.XXXXXX", DIR => "."))[1];
+               File::Temp::tempfile(".gitsendemail.msg.XXXXXX", DIR => $repo->repo_path()) :
+               File::Temp::tempfile(".gitsendemail.msg.XXXXXX", DIR => "."))[1];
        open my $c, ">", $compose_filename
                or die sprintf(__("Failed to open for writing %s: %s"), $compose_filename, $!);
 
 
-       my $tpl_sender = $sender || $repoauthor || $repocommitter || '';
+       my $tpl_sender = $sender || $repoauthor->() || $repocommitter->() || '';
        my $tpl_subject = $initial_subject || '';
        my $tpl_in_reply_to = $initial_in_reply_to || '';
        my $tpl_reply_to = $reply_to || '';
 
-       print $c <<EOT1, Git::prefix_lines("GIT: ", __ <<EOT2), <<EOT3;
+       print $c <<EOT1, Git::prefix_lines("GIT: ", __(<<EOT2)), <<EOT3;
 From $tpl_sender # This line is ignored.
 EOT1
 Lines beginning in "GIT:" will be removed.
@@ -859,6 +893,19 @@ EOT3
        do_edit(@files);
 }
 
+sub term {
+       my $term = eval {
+               require Term::ReadLine;
+               $ENV{"GIT_SEND_EMAIL_NOTTY"}
+                       ? Term::ReadLine->new('git-send-email', \*STDIN, \*STDOUT)
+                       : Term::ReadLine->new('git-send-email');
+       };
+       if ($@) {
+               $term = FakeTerm->new("$@: going non-interactive");
+       }
+       return $term;
+}
+
 sub ask {
        my ($prompt, %arg) = @_;
        my $valid_re = $arg{valid_re};
@@ -866,6 +913,7 @@ sub ask {
        my $confirm_only = $arg{confirm_only};
        my $resp;
        my $i = 0;
+       my $term = term();
        return defined $default ? $default : undef
                unless defined $term->IN and defined fileno($term->IN) and
                       defined $term->OUT and defined fileno($term->OUT);
@@ -963,7 +1011,7 @@ if (defined $sender) {
        $sender =~ s/^\s+|\s+$//g;
        ($sender) = expand_aliases($sender);
 } else {
-       $sender = $repoauthor || $repocommitter || '';
+       $sender = $repoauthor->() || $repocommitter->() || '';
 }
 
 # $sender could be an already sanitized address
@@ -1049,6 +1097,7 @@ sub extract_valid_address {
        return $address if ($address =~ /^($local_part_regexp)$/);
 
        $address =~ s/^\s*<(.*)>\s*$/$1/;
+       my $have_email_valid = eval { require Email::Valid; 1 };
        if ($have_email_valid) {
                return scalar Email::Valid->address($address);
        }
@@ -1108,14 +1157,15 @@ my ($message_id_stamp, $message_id_serial);
 sub make_message_id {
        my $uniq;
        if (!defined $message_id_stamp) {
-               $message_id_stamp = strftime("%Y%m%d%H%M%S.$$", gmtime(time));
+               require POSIX;
+               $message_id_stamp = POSIX::strftime("%Y%m%d%H%M%S.$$", gmtime(time));
                $message_id_serial = 0;
        }
        $message_id_serial++;
        $uniq = "$message_id_stamp-$message_id_serial";
 
        my $du_part;
-       for ($sender, $repocommitter, $repoauthor) {
+       for ($sender, $repocommitter->(), $repoauthor->()) {
                $du_part = extract_valid_address(sanitize_address($_));
                last if (defined $du_part and $du_part ne '');
        }
@@ -1278,6 +1328,7 @@ sub valid_fqdn {
 sub maildomain_net {
        my $maildomain;
 
+       require Net::Domain;
        my $domain = Net::Domain::domainname();
        $maildomain = $domain if valid_fqdn($domain);
 
@@ -1288,6 +1339,7 @@ sub maildomain_mta {
        my $maildomain;
 
        for my $host (qw(mailhost localhost)) {
+               require Net::SMTP;
                my $smtp = Net::SMTP->new($host);
                if (defined $smtp) {
                        my $domain = $smtp->domain;
@@ -1980,13 +2032,15 @@ sub validate_patch {
 
        if ($repo) {
                my $hooks_path = $repo->command_oneline('rev-parse', '--git-path', 'hooks');
-               my $validate_hook = catfile($hooks_path,
+               require File::Spec;
+               my $validate_hook = File::Spec->catfile($hooks_path,
                                            'sendemail-validate');
                my $hook_error;
                if (-x $validate_hook) {
-                       my $target = abs_path($fn);
+                       require Cwd;
+                       my $target = Cwd::abs_path($fn);
                        # The hook needs a correct cwd and GIT_DIR.
-                       my $cwd_save = cwd();
+                       my $cwd_save = Cwd::getcwd();
                        chdir($repo->wc_path() or $repo->repo_path())
                                or die("chdir: $!");
                        local $ENV{"GIT_DIR"} = $repo->repo_path();
diff --git a/hash.h b/hash.h
index 9c6df4d9527e0a5bc3581d60cdae6e03945ddaaf..27a180248f96fd4cf394b059ed92b02bcaeebc7b 100644 (file)
--- a/hash.h
+++ b/hash.h
@@ -265,7 +265,7 @@ static inline void oidcpy(struct object_id *dst, const struct object_id *src)
 
 /* Like oidcpy() but zero-pads the unused bytes in dst's hash array. */
 static inline void oidcpy_with_padding(struct object_id *dst,
-                                      struct object_id *src)
+                                      const struct object_id *src)
 {
        size_t hashsz;
 
index bb085d66d10509214eae1994e430d6058955f7cf..a0540ba5cf43e5af6c749f8c1bc7b06a7ff222ad 100644 (file)
@@ -451,6 +451,7 @@ static int buffer_gets(struct imap_buffer *b, char **s)
        /* not reached */
 }
 
+__attribute__((format (printf, 1, 2)))
 static void imap_info(const char *msg, ...)
 {
        va_list va;
@@ -463,6 +464,7 @@ static void imap_info(const char *msg, ...)
        }
 }
 
+__attribute__((format (printf, 1, 2)))
 static void imap_warn(const char *msg, ...)
 {
        va_list va;
@@ -504,6 +506,7 @@ static char *next_arg(char **s)
        return ret;
 }
 
+__attribute__((format (printf, 3, 4)))
 static int nfsnprintf(char *buf, int blen, const char *fmt, ...)
 {
        int ret;
@@ -1266,18 +1269,6 @@ static void wrap_in_html(struct strbuf *msg)
        *msg = buf;
 }
 
-#define CHUNKSIZE 0x1000
-
-static int read_message(FILE *f, struct strbuf *all_msgs)
-{
-       do {
-               if (strbuf_fread(all_msgs, CHUNKSIZE, f) <= 0)
-                       break;
-       } while (!feof(f));
-
-       return ferror(f) ? -1 : 0;
-}
-
 static int count_messages(struct strbuf *all_msgs)
 {
        int count = 0;
@@ -1582,8 +1573,8 @@ int cmd_main(int argc, const char **argv)
        }
 
        /* read the messages */
-       if (read_message(stdin, &all_msgs)) {
-               fprintf(stderr, "error reading input\n");
+       if (strbuf_read(&all_msgs, 0, 0) < 0) {
+               error_errno(_("could not read from stdin"));
                return 1;
        }
 
index 7b823786c2cba7733df8c2977b4a3d6c0478b459..6dc4412268b8ae34f215e066bd94a1f2ddad1ce8 100644 (file)
@@ -134,7 +134,8 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid,
                              int flags, void *cb_data)
 {
        struct object *obj;
-       enum decoration_type type = DECORATION_NONE;
+       enum object_type objtype;
+       enum decoration_type deco_type = DECORATION_NONE;
        struct decoration_filter *filter = (struct decoration_filter *)cb_data;
 
        if (filter && !ref_filter_match(refname, filter))
@@ -155,28 +156,29 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid,
                return 0;
        }
 
-       obj = parse_object(the_repository, oid);
-       if (!obj)
+       objtype = oid_object_info(the_repository, oid, NULL);
+       if (objtype < 0)
                return 0;
+       obj = lookup_object_by_type(the_repository, oid, objtype);
 
        if (starts_with(refname, "refs/heads/"))
-               type = DECORATION_REF_LOCAL;
+               deco_type = DECORATION_REF_LOCAL;
        else if (starts_with(refname, "refs/remotes/"))
-               type = DECORATION_REF_REMOTE;
+               deco_type = DECORATION_REF_REMOTE;
        else if (starts_with(refname, "refs/tags/"))
-               type = DECORATION_REF_TAG;
+               deco_type = DECORATION_REF_TAG;
        else if (!strcmp(refname, "refs/stash"))
-               type = DECORATION_REF_STASH;
+               deco_type = DECORATION_REF_STASH;
        else if (!strcmp(refname, "HEAD"))
-               type = DECORATION_REF_HEAD;
+               deco_type = DECORATION_REF_HEAD;
 
-       add_name_decoration(type, refname, obj);
+       add_name_decoration(deco_type, refname, obj);
        while (obj->type == OBJ_TAG) {
+               if (!obj->parsed)
+                       parse_object(the_repository, &obj->oid);
                obj = ((struct tag *)obj)->tagged;
                if (!obj)
                        break;
-               if (!obj->parsed)
-                       parse_object(the_repository, &obj->oid);
                add_name_decoration(DECORATION_REF_TAG, refname, obj);
        }
        return 0;
index d1f7c0d272d81ca00bfef33c80a02b9d7da997cf..462b3956340be0bb917f7bda2cf82bdc9d489b11 100644 (file)
--- a/mailmap.c
+++ b/mailmap.c
@@ -8,6 +8,7 @@
 #define debug_mm(...) fprintf(stderr, __VA_ARGS__)
 #define debug_str(X) ((X) ? (X) : "(none)")
 #else
+__attribute__((format (printf, 1, 2)))
 static inline void debug_mm(const char *format, ...) {}
 static inline const char *debug_str(const char *s) { return s; }
 #endif
index 8cfa0ccd3c87695592eb52af7d2125082b4b4cb4..c3b3ab28c1fa3a34719032952817f5afde4e66ad 100644 (file)
@@ -530,6 +530,7 @@ static void clear_or_reinit_internal_opts(struct merge_options_internal *opti,
        renames->callback_data_nr = renames->callback_data_alloc = 0;
 }
 
+__attribute__((format (printf, 2, 3)))
 static int err(struct merge_options *opt, const char *err, ...)
 {
        va_list params;
index a67bf2acfa539d6e4802da9cd51fcf944c715c5a..03f73cfc47c62fcac003c4d0aa97a571f46d769d 100644 (file)
@@ -167,6 +167,7 @@ static void flush_output(struct merge_options *opt)
        }
 }
 
+__attribute__((format (printf, 2, 3)))
 static int err(struct merge_options *opt, const char *err, ...)
 {
        va_list params;
diff --git a/midx.c b/midx.c
index 9a35b0255d5c04258aeacd0a63c21cdb627e58ce..321c6fdd2f184ad8842be703a072413699a74a9d 100644 (file)
--- a/midx.c
+++ b/midx.c
@@ -1172,6 +1172,7 @@ void clear_midx_file(struct repository *r)
 
 static int verify_midx_error;
 
+__attribute__((format (printf, 1, 2)))
 static void midx_report(const char *fmt, ...)
 {
        va_list ap;
index ecca5a8da00f14d2bdbd8c56ced17aacde728ad7..3d27dc8dea333778a9592335ad98f9c4472cb874 100644 (file)
@@ -517,9 +517,9 @@ const char *loose_object_path(struct repository *r, struct strbuf *buf,
  */
 static int alt_odb_usable(struct raw_object_store *o,
                          struct strbuf *path,
-                         const char *normalized_objdir)
+                         const char *normalized_objdir, khiter_t *pos)
 {
-       struct object_directory *odb;
+       int r;
 
        /* Detect cases where alternate disappeared */
        if (!is_directory(path->buf)) {
@@ -533,14 +533,20 @@ static int alt_odb_usable(struct raw_object_store *o,
         * Prevent the common mistake of listing the same
         * thing twice, or object directory itself.
         */
-       for (odb = o->odb; odb; odb = odb->next) {
-               if (!fspathcmp(path->buf, odb->path))
-                       return 0;
+       if (!o->odb_by_path) {
+               khiter_t p;
+
+               o->odb_by_path = kh_init_odb_path_map();
+               assert(!o->odb->next);
+               p = kh_put_odb_path_map(o->odb_by_path, o->odb->path, &r);
+               assert(r == 1); /* never used */
+               kh_value(o->odb_by_path, p) = o->odb;
        }
-       if (!fspathcmp(path->buf, normalized_objdir))
+       if (fspatheq(path->buf, normalized_objdir))
                return 0;
-
-       return 1;
+       *pos = kh_put_odb_path_map(o->odb_by_path, path->buf, &r);
+       /* r: 0 = exists, 1 = never used, 2 = deleted */
+       return r == 0 ? 0 : 1;
 }
 
 /*
@@ -561,17 +567,18 @@ static int alt_odb_usable(struct raw_object_store *o,
 static void read_info_alternates(struct repository *r,
                                 const char *relative_base,
                                 int depth);
-static int link_alt_odb_entry(struct repository *r, const char *entry,
+static int link_alt_odb_entry(struct repository *r, const struct strbuf *entry,
        const char *relative_base, int depth, const char *normalized_objdir)
 {
        struct object_directory *ent;
        struct strbuf pathbuf = STRBUF_INIT;
+       khiter_t pos;
 
-       if (!is_absolute_path(entry) && relative_base) {
+       if (!is_absolute_path(entry->buf) && relative_base) {
                strbuf_realpath(&pathbuf, relative_base, 1);
                strbuf_addch(&pathbuf, '/');
        }
-       strbuf_addstr(&pathbuf, entry);
+       strbuf_addbuf(&pathbuf, entry);
 
        if (strbuf_normalize_path(&pathbuf) < 0 && relative_base) {
                error(_("unable to normalize alternate object path: %s"),
@@ -587,23 +594,25 @@ static int link_alt_odb_entry(struct repository *r, const char *entry,
        while (pathbuf.len && pathbuf.buf[pathbuf.len - 1] == '/')
                strbuf_setlen(&pathbuf, pathbuf.len - 1);
 
-       if (!alt_odb_usable(r->objects, &pathbuf, normalized_objdir)) {
+       if (!alt_odb_usable(r->objects, &pathbuf, normalized_objdir, &pos)) {
                strbuf_release(&pathbuf);
                return -1;
        }
 
        CALLOC_ARRAY(ent, 1);
-       ent->path = xstrdup(pathbuf.buf);
+       /* pathbuf.buf is already in r->objects->odb_by_path */
+       ent->path = strbuf_detach(&pathbuf, NULL);
 
        /* add the alternate entry */
        *r->objects->odb_tail = ent;
        r->objects->odb_tail = &(ent->next);
        ent->next = NULL;
+       assert(r->objects->odb_by_path);
+       kh_value(r->objects->odb_by_path, pos) = ent;
 
        /* recursively add alternates */
-       read_info_alternates(r, pathbuf.buf, depth + 1);
+       read_info_alternates(r, ent->path, depth + 1);
 
-       strbuf_release(&pathbuf);
        return 0;
 }
 
@@ -660,7 +669,7 @@ static void link_alt_odb_entries(struct repository *r, const char *alt,
                alt = parse_alt_odb_entry(alt, sep, &entry);
                if (!entry.len)
                        continue;
-               link_alt_odb_entry(r, entry.buf,
+               link_alt_odb_entry(r, &entry,
                                   relative_base, depth, objdirbuf.buf);
        }
        strbuf_release(&entry);
@@ -1178,7 +1187,7 @@ static int quick_has_loose(struct repository *r,
 
        prepare_alt_odb(r);
        for (odb = r->objects->odb; odb; odb = odb->next) {
-               if (oid_array_lookup(odb_loose_cache(odb, oid), oid) >= 0)
+               if (oidtree_contains(odb_loose_cache(odb, oid), oid))
                        return 1;
        }
        return 0;
@@ -2454,39 +2463,45 @@ int for_each_loose_object(each_loose_object_fn cb, void *data,
 static int append_loose_object(const struct object_id *oid, const char *path,
                               void *data)
 {
-       oid_array_append(data, oid);
+       oidtree_insert(data, oid);
        return 0;
 }
 
-struct oid_array *odb_loose_cache(struct object_directory *odb,
+struct oidtree *odb_loose_cache(struct object_directory *odb,
                                  const struct object_id *oid)
 {
        int subdir_nr = oid->hash[0];
        struct strbuf buf = STRBUF_INIT;
+       size_t word_bits = bitsizeof(odb->loose_objects_subdir_seen[0]);
+       size_t word_index = subdir_nr / word_bits;
+       size_t mask = 1 << (subdir_nr % word_bits);
+       uint32_t *bitmap;
 
        if (subdir_nr < 0 ||
-           subdir_nr >= ARRAY_SIZE(odb->loose_objects_subdir_seen))
+           subdir_nr >= bitsizeof(odb->loose_objects_subdir_seen))
                BUG("subdir_nr out of range");
 
-       if (odb->loose_objects_subdir_seen[subdir_nr])
-               return &odb->loose_objects_cache[subdir_nr];
-
+       bitmap = &odb->loose_objects_subdir_seen[word_index];
+       if (*bitmap & mask)
+               return odb->loose_objects_cache;
+       if (!odb->loose_objects_cache) {
+               ALLOC_ARRAY(odb->loose_objects_cache, 1);
+               oidtree_init(odb->loose_objects_cache);
+       }
        strbuf_addstr(&buf, odb->path);
        for_each_file_in_obj_subdir(subdir_nr, &buf,
                                    append_loose_object,
                                    NULL, NULL,
-                                   &odb->loose_objects_cache[subdir_nr]);
-       odb->loose_objects_subdir_seen[subdir_nr] = 1;
+                                   odb->loose_objects_cache);
+       *bitmap |= mask;
        strbuf_release(&buf);
-       return &odb->loose_objects_cache[subdir_nr];
+       return odb->loose_objects_cache;
 }
 
 void odb_clear_loose_cache(struct object_directory *odb)
 {
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(odb->loose_objects_cache); i++)
-               oid_array_clear(&odb->loose_objects_cache[i]);
+       oidtree_clear(odb->loose_objects_cache);
+       FREE_AND_NULL(odb->loose_objects_cache);
        memset(&odb->loose_objects_subdir_seen, 0,
               sizeof(odb->loose_objects_subdir_seen));
 }
index 64202de60b1f5af3f09b15ba9c6240d7bb136930..3263c19457fa3ff89d95536a18a7b03f2d9beb2a 100644 (file)
@@ -87,27 +87,21 @@ static void update_candidates(struct disambiguate_state *ds, const struct object
 
 static int match_hash(unsigned, const unsigned char *, const unsigned char *);
 
+static enum cb_next match_prefix(const struct object_id *oid, void *arg)
+{
+       struct disambiguate_state *ds = arg;
+       /* no need to call match_hash, oidtree_each did prefix match */
+       update_candidates(ds, oid);
+       return ds->ambiguous ? CB_BREAK : CB_CONTINUE;
+}
+
 static void find_short_object_filename(struct disambiguate_state *ds)
 {
        struct object_directory *odb;
 
-       for (odb = ds->repo->objects->odb; odb && !ds->ambiguous; odb = odb->next) {
-               int pos;
-               struct oid_array *loose_objects;
-
-               loose_objects = odb_loose_cache(odb, &ds->bin_pfx);
-               pos = oid_array_lookup(loose_objects, &ds->bin_pfx);
-               if (pos < 0)
-                       pos = -1 - pos;
-               while (!ds->ambiguous && pos < loose_objects->nr) {
-                       const struct object_id *oid;
-                       oid = loose_objects->oid + pos;
-                       if (!match_hash(ds->len, ds->bin_pfx.hash, oid->hash))
-                               break;
-                       update_candidates(ds, oid);
-                       pos++;
-               }
-       }
+       for (odb = ds->repo->objects->odb; odb && !ds->ambiguous; odb = odb->next)
+               oidtree_each(odb_loose_cache(odb, &ds->bin_pfx),
+                               &ds->bin_pfx, ds->len, match_prefix, ds);
 }
 
 static int match_hash(unsigned len, const unsigned char *a, const unsigned char *b)
index ec32c23dcb5615f3bfc0d5c82d7c193f251b125d..e679acc4c31316cee2863579721e6c0f97210831 100644 (file)
@@ -7,6 +7,9 @@
 #include "oid-array.h"
 #include "strbuf.h"
 #include "thread-utils.h"
+#include "khash.h"
+#include "dir.h"
+#include "oidtree.h"
 
 struct object_directory {
        struct object_directory *next;
@@ -20,8 +23,8 @@ struct object_directory {
         *
         * Be sure to call odb_load_loose_cache() before using.
         */
-       char loose_objects_subdir_seen[256];
-       struct oid_array loose_objects_cache[256];
+       uint32_t loose_objects_subdir_seen[8]; /* 256 bits */
+       struct oidtree *loose_objects_cache;
 
        /*
         * Path to the alternative object store. If this is a relative path,
@@ -30,6 +33,9 @@ struct object_directory {
        char *path;
 };
 
+KHASH_INIT(odb_path_map, const char * /* key: odb_path */,
+       struct object_directory *, 1, fspathhash, fspatheq);
+
 void prepare_alt_odb(struct repository *r);
 char *compute_alternate_path(const char *path, struct strbuf *err);
 typedef int alt_odb_fn(struct object_directory *, void *);
@@ -54,7 +60,7 @@ void add_to_alternates_memory(const char *dir);
  * Populate and return the loose object cache array corresponding to the
  * given object ID.
  */
-struct oid_array *odb_loose_cache(struct object_directory *odb,
+struct oidtree *odb_loose_cache(struct object_directory *odb,
                                  const struct object_id *oid);
 
 /* Empty the loose object cache for the specified object directory. */
@@ -116,6 +122,8 @@ struct raw_object_store {
         */
        struct object_directory *odb;
        struct object_directory **odb_tail;
+       kh_odb_path_map_t *odb_by_path;
+
        int loaded_alternates;
 
        /*
index 14188453c56706e0c364c995946bdc16d3d97fea..4e85955a941168bc4bcd0f97ee98353402561dd1 100644 (file)
--- a/object.c
+++ b/object.c
@@ -185,6 +185,24 @@ struct object *lookup_unknown_object(struct repository *r, const struct object_i
        return obj;
 }
 
+struct object *lookup_object_by_type(struct repository *r,
+                           const struct object_id *oid,
+                           enum object_type type)
+{
+       switch (type) {
+       case OBJ_COMMIT:
+               return (struct object *)lookup_commit(r, oid);
+       case OBJ_TREE:
+               return (struct object *)lookup_tree(r, oid);
+       case OBJ_TAG:
+               return (struct object *)lookup_tag(r, oid);
+       case OBJ_BLOB:
+               return (struct object *)lookup_blob(r, oid);
+       default:
+               die("BUG: unknown object type %d", type);
+       }
+}
+
 struct object *parse_object_buffer(struct repository *r, const struct object_id *oid, enum object_type type, unsigned long size, void *buffer, int *eaten_p)
 {
        struct object *obj;
@@ -511,6 +529,8 @@ static void free_object_directories(struct raw_object_store *o)
                free_object_directory(o->odb);
                o->odb = next;
        }
+       kh_destroy_odb_path_map(o->odb_by_path);
+       o->odb_by_path = NULL;
 }
 
 void raw_object_store_clear(struct raw_object_store *o)
index 8bca31071326c648968959a46343e829cc65cdc8..3b38c9cc988b8fcc9bd2600ce53ed74614a24b19 100644 (file)
--- a/object.h
+++ b/object.h
@@ -144,9 +144,27 @@ struct object *parse_object_or_die(const struct object_id *oid, const char *name
  */
 struct object *parse_object_buffer(struct repository *r, const struct object_id *oid, enum object_type type, unsigned long size, void *buffer, int *eaten_p);
 
-/** Returns the object, with potentially excess memory allocated. **/
+/*
+ * Allocate and return an object struct, even if you do not know the type of
+ * the object. The returned object may have its "type" field set to a real type
+ * (if somebody previously called lookup_blob(), etc), or it may be set to
+ * OBJ_NONE. In the latter case, subsequent calls to lookup_blob(), etc, will
+ * set the type field as appropriate.
+ *
+ * Use this when you do not know the expected type of an object and want to
+ * avoid parsing it for efficiency reasons. Try to avoid it otherwise; it
+ * may allocate excess memory, since the returned object must be as large as
+ * the maximum struct of any type.
+ */
 struct object *lookup_unknown_object(struct repository *r, const struct object_id *oid);
 
+/*
+ * Dispatch to the appropriate lookup_blob(), lookup_commit(), etc, based on
+ * "type".
+ */
+struct object *lookup_object_by_type(struct repository *r, const struct object_id *oid,
+                                    enum object_type type);
+
 struct object_list *object_list_insert(struct object *item,
                                       struct object_list **list_p);
 
diff --git a/oidtree.c b/oidtree.c
new file mode 100644 (file)
index 0000000..7eb0e9b
--- /dev/null
+++ b/oidtree.c
@@ -0,0 +1,104 @@
+/*
+ * A wrapper around cbtree which stores oids
+ * May be used to replace oid-array for prefix (abbreviation) matches
+ */
+#include "oidtree.h"
+#include "alloc.h"
+#include "hash.h"
+
+struct oidtree_node {
+       /* n.k[] is used to store "struct object_id" */
+       struct cb_node n;
+};
+
+struct oidtree_iter_data {
+       oidtree_iter fn;
+       void *arg;
+       size_t *last_nibble_at;
+       int algo;
+       uint8_t last_byte;
+};
+
+void oidtree_init(struct oidtree *ot)
+{
+       cb_init(&ot->tree);
+       mem_pool_init(&ot->mem_pool, 0);
+}
+
+void oidtree_clear(struct oidtree *ot)
+{
+       if (ot) {
+               mem_pool_discard(&ot->mem_pool, 0);
+               oidtree_init(ot);
+       }
+}
+
+void oidtree_insert(struct oidtree *ot, const struct object_id *oid)
+{
+       struct oidtree_node *on;
+
+       if (!oid->algo)
+               BUG("oidtree_insert requires oid->algo");
+
+       on = mem_pool_alloc(&ot->mem_pool, sizeof(*on) + sizeof(*oid));
+       oidcpy_with_padding((struct object_id *)on->n.k, oid);
+
+       /*
+        * n.b. Current callers won't get us duplicates, here.  If a
+        * future caller causes duplicates, there'll be a a small leak
+        * that won't be freed until oidtree_clear.  Currently it's not
+        * worth maintaining a free list
+        */
+       cb_insert(&ot->tree, &on->n, sizeof(*oid));
+}
+
+
+int oidtree_contains(struct oidtree *ot, const struct object_id *oid)
+{
+       struct object_id k;
+       size_t klen = sizeof(k);
+
+       oidcpy_with_padding(&k, oid);
+
+       if (oid->algo == GIT_HASH_UNKNOWN)
+               klen -= sizeof(oid->algo);
+
+       /* cb_lookup relies on memcmp on the struct, so order matters: */
+       klen += BUILD_ASSERT_OR_ZERO(offsetof(struct object_id, hash) <
+                               offsetof(struct object_id, algo));
+
+       return cb_lookup(&ot->tree, (const uint8_t *)&k, klen) ? 1 : 0;
+}
+
+static enum cb_next iter(struct cb_node *n, void *arg)
+{
+       struct oidtree_iter_data *x = arg;
+       const struct object_id *oid = (const struct object_id *)n->k;
+
+       if (x->algo != GIT_HASH_UNKNOWN && x->algo != oid->algo)
+               return CB_CONTINUE;
+
+       if (x->last_nibble_at) {
+               if ((oid->hash[*x->last_nibble_at] ^ x->last_byte) & 0xf0)
+                       return CB_CONTINUE;
+       }
+
+       return x->fn(oid, x->arg);
+}
+
+void oidtree_each(struct oidtree *ot, const struct object_id *oid,
+                       size_t oidhexsz, oidtree_iter fn, void *arg)
+{
+       size_t klen = oidhexsz / 2;
+       struct oidtree_iter_data x = { 0 };
+       assert(oidhexsz <= GIT_MAX_HEXSZ);
+
+       x.fn = fn;
+       x.arg = arg;
+       x.algo = oid->algo;
+       if (oidhexsz & 1) {
+               x.last_byte = oid->hash[klen];
+               x.last_nibble_at = &klen;
+       }
+       cb_each(&ot->tree, (const uint8_t *)oid, klen, iter, &x);
+}
diff --git a/oidtree.h b/oidtree.h
new file mode 100644 (file)
index 0000000..77898f5
--- /dev/null
+++ b/oidtree.h
@@ -0,0 +1,22 @@
+#ifndef OIDTREE_H
+#define OIDTREE_H
+
+#include "cbtree.h"
+#include "hash.h"
+#include "mem-pool.h"
+
+struct oidtree {
+       struct cb_tree tree;
+       struct mem_pool mem_pool;
+};
+
+void oidtree_init(struct oidtree *);
+void oidtree_clear(struct oidtree *);
+void oidtree_insert(struct oidtree *, const struct object_id *);
+int oidtree_contains(struct oidtree *, const struct object_id *);
+
+typedef enum cb_next (*oidtree_iter)(const struct object_id *, void *data);
+void oidtree_each(struct oidtree *, const struct object_id *,
+                       size_t oidhexsz, oidtree_iter, void *data);
+
+#endif /* OIDTREE_H */
index 02eacef0c2a4a43058a74dda4cebe1ae16bdd2eb..090a7df63fc004ecff6fc3425c1fb13006fb499d 100644 (file)
@@ -11,9 +11,6 @@ use 5.008;
 use strict;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 
-use File::Temp ();
-use File::Spec ();
-
 BEGIN {
 
 our ($VERSION, @ISA, @EXPORT, @EXPORT_OK);
@@ -103,12 +100,9 @@ increase notwithstanding).
 =cut
 
 
-use Carp qw(carp croak); # but croak is bad - throw instead
+sub carp { require Carp; goto &Carp::carp }
+sub croak { require Carp; goto &Carp::croak }
 use Git::LoadCPAN::Error qw(:try);
-use Cwd qw(abs_path cwd);
-use IPC::Open2 qw(open2);
-use Fcntl qw(SEEK_SET SEEK_CUR);
-use Time::Local qw(timegm);
 }
 
 
@@ -191,13 +185,15 @@ sub repository {
                        $dir = undef;
                };
 
+               require Cwd;
                if ($dir) {
+                       require File::Spec;
                        File::Spec->file_name_is_absolute($dir) or $dir = $opts{Directory} . '/' . $dir;
-                       $opts{Repository} = abs_path($dir);
+                       $opts{Repository} = Cwd::abs_path($dir);
 
                        # If --git-dir went ok, this shouldn't die either.
                        my $prefix = $search->command_oneline('rev-parse', '--show-prefix');
-                       $dir = abs_path($opts{Directory}) . '/';
+                       $dir = Cwd::abs_path($opts{Directory}) . '/';
                        if ($prefix) {
                                if (substr($dir, -length($prefix)) ne $prefix) {
                                        throw Error::Simple("rev-parse confused me - $dir does not have trailing $prefix");
@@ -223,7 +219,7 @@ sub repository {
                                throw Error::Simple("fatal: Not a git repository: $dir");
                        }
 
-                       $opts{Repository} = abs_path($dir);
+                       $opts{Repository} = Cwd::abs_path($dir);
                }
 
                delete $opts{Directory};
@@ -408,10 +404,12 @@ sub command_bidi_pipe {
        my $cwd_save = undef;
        if ($self) {
                shift;
-               $cwd_save = cwd();
+               require Cwd;
+               $cwd_save = Cwd::getcwd();
                _setup_git_cmd_env($self);
        }
-       $pid = open2($in, $out, 'git', @_);
+       require IPC::Open2;
+       $pid = IPC::Open2::open2($in, $out, 'git', @_);
        chdir($cwd_save) if $cwd_save;
        return ($pid, $in, $out, join(' ', @_));
 }
@@ -538,7 +536,8 @@ sub get_tz_offset {
        my $t = shift || time;
        my @t = localtime($t);
        $t[5] += 1900;
-       my $gm = timegm(@t);
+       require Time::Local;
+       my $gm = Time::Local::timegm(@t);
        my $sign = qw( + + - )[ $gm <=> $t ];
        return sprintf("%s%02d%02d", $sign, (gmtime(abs($t - $gm)))[2,1]);
 }
@@ -1340,6 +1339,7 @@ sub _temp_cache {
                my $n = $name;
                $n =~ s/\W/_/g; # no strange chars
 
+               require File::Temp;
                ($$temp_fd, $fname) = File::Temp::tempfile(
                        "Git_${n}_XXXXXX", UNLINK => 1, DIR => $tmpdir,
                        ) or throw Error::Simple("couldn't open new temp file");
@@ -1362,9 +1362,9 @@ sub temp_reset {
 
        truncate $temp_fd, 0
                or throw Error::Simple("couldn't truncate file");
-       sysseek($temp_fd, 0, SEEK_SET) and seek($temp_fd, 0, SEEK_SET)
+       sysseek($temp_fd, 0, Fcntl::SEEK_SET()) and seek($temp_fd, 0, Fcntl::SEEK_SET())
                or throw Error::Simple("couldn't seek to beginning of file");
-       sysseek($temp_fd, 0, SEEK_CUR) == 0 and tell($temp_fd) == 0
+       sysseek($temp_fd, 0, Fcntl::SEEK_CUR()) == 0 and tell($temp_fd) == 0
                or throw Error::Simple("expected file position to be reset");
 }
 
index 98304ce374de8b0c5a4979700230e5f89da30a48..9f63eae2e643f7a70d23edccdb9944a12ff7cdad 100644 (file)
@@ -103,7 +103,7 @@ void packet_response_end(int fd)
 {
        packet_trace("0002", 4, 1);
        if (write_in_full(fd, "0002", 4) < 0)
-               die_errno(_("unable to write stateless separator packet"));
+               die_errno(_("unable to write response end packet"));
 }
 
 int packet_flush_gently(int fd)
index b1ecd039cef29ecc77edd894c500dfadf49058c8..9631529c10a072a21acb76d65545d61a80b337d4 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -1735,6 +1735,10 @@ static size_t userformat_want_item(struct strbuf *sb, const char *placeholder,
        case 'S':
                w->source = 1;
                break;
+       case 'd':
+       case 'D':
+               w->decorate = 1;
+               break;
        }
        return 0;
 }
index f034609e4db6e68efb24a149c580fc7bda3c4cfa..2f16acd213d6d70c6055472f4f4d6b0e3e4b6f9e 100644 (file)
--- a/pretty.h
+++ b/pretty.h
@@ -65,12 +65,16 @@ static inline int cmit_fmt_is_mail(enum cmit_fmt fmt)
        return (fmt == CMIT_FMT_EMAIL || fmt == CMIT_FMT_MBOXRD);
 }
 
+/*
+ * Examine the user-specified format given by "fmt" (or if NULL, the global one
+ * previously saved by get_commit_format()), and set flags based on which items
+ * the format will need when it is expanded.
+ */
 struct userformat_want {
        unsigned notes:1;
        unsigned source:1;
+       unsigned decorate:1;
 };
-
-/* Set the flag "w->notes" if there is placeholder %N in "fmt". */
 void userformat_find_requirements(const char *fmt, struct userformat_want *w);
 
 /*
diff --git a/quote.h b/quote.h
index 768cc6338e27d73c8a10cf5eae76ad5626c73e3e..049d8dd0b3d7e42547ef42b460dcaaf95a412a68 100644 (file)
--- a/quote.h
+++ b/quote.h
@@ -31,6 +31,7 @@ struct strbuf;
 
 void sq_quote_buf(struct strbuf *, const char *src);
 void sq_quote_argv(struct strbuf *, const char **argv);
+__attribute__((format (printf, 2, 3)))
 void sq_quotef(struct strbuf *, const char *fmt, ...);
 
 /*
index c59847257a5209d0d011e80b45898aac1a08f66a..84e3d0d75ed05fbae051bb031a5e54d600def1e0 100644 (file)
@@ -159,24 +159,6 @@ int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
                                      FOR_EACH_OBJECT_LOCAL_ONLY);
 }
 
-static void *lookup_object_by_type(struct repository *r,
-                                  const struct object_id *oid,
-                                  enum object_type type)
-{
-       switch (type) {
-       case OBJ_COMMIT:
-               return lookup_commit(r, oid);
-       case OBJ_TREE:
-               return lookup_tree(r, oid);
-       case OBJ_TAG:
-               return lookup_tag(r, oid);
-       case OBJ_BLOB:
-               return lookup_blob(r, oid);
-       default:
-               die("BUG: unknown object type %d", type);
-       }
-}
-
 static int mark_object_seen(const struct object_id *oid,
                             enum object_type type,
                             int exclude,
index 4db0e40ff4c6251aa808738e1630db1e397956de..f45d3a1b26dbd04f1f288e1afa49596b09c38f54 100644 (file)
@@ -213,6 +213,7 @@ static int used_atom_cnt, need_tagged, need_symref;
  * Expand string, append it to strbuf *sb, then return error code ret.
  * Allow to save few lines of code.
  */
+__attribute__((format (printf, 3, 4)))
 static int strbuf_addf_ret(struct strbuf *sb, int ret, const char *fmt, ...)
 {
        va_list ap;
index 9d432c299a242e4f0b337756bc6aedfcc5be45b8..6c320d5704598e4f0025af9c35dcc407c4cba980 100644 (file)
@@ -653,7 +653,7 @@ static int rpc_read_from_out(struct rpc_state *rpc, int options,
                        memcpy(buf - 4, "0000", 4);
                        break;
                case PACKET_READ_RESPONSE_END:
-                       die(_("remote server sent stateless separator"));
+                       die(_("remote server sent unexpected response end packet"));
                }
        }
 
index 5c5510d4220679a32b2b89cc9f2867868ac392f8..fbb068da9fb4d5ba8a89dde1d8e99c7b19f6aebe 100644 (file)
@@ -215,7 +215,8 @@ struct rev_info {
                        missing_newline:1,
                        date_mode_explicit:1,
                        preserve_subject:1,
-                       encode_email_headers:1;
+                       encode_email_headers:1,
+                       include_header:1;
        unsigned int    disable_stdin:1;
        /* --show-linear-break */
        unsigned int    track_linear:1,
index 0bec01cf38e8173c930b84250a61e84eb0c6963b..7f07cd00f3f20ab3dc0c508653ac28bdd8e8b8c1 100644 (file)
@@ -3521,6 +3521,7 @@ static int do_exec(struct repository *r, const char *command_line)
        return status;
 }
 
+__attribute__((format (printf, 2, 3)))
 static int safe_append(const char *filename, const char *fmt, ...)
 {
        va_list ap;
@@ -3598,8 +3599,27 @@ static int do_label(struct repository *r, const char *name, int len)
        return ret;
 }
 
+__attribute__((format (printf, 3, 4)))
 static const char *reflog_message(struct replay_opts *opts,
-       const char *sub_action, const char *fmt, ...);
+       const char *sub_action, const char *fmt, ...)
+{
+       va_list ap;
+       static struct strbuf buf = STRBUF_INIT;
+       char *reflog_action = getenv(GIT_REFLOG_ACTION);
+
+       va_start(ap, fmt);
+       strbuf_reset(&buf);
+       strbuf_addstr(&buf, reflog_action ? reflog_action : action_name(opts));
+       if (sub_action)
+               strbuf_addf(&buf, " (%s)", sub_action);
+       if (fmt) {
+               strbuf_addstr(&buf, ": ");
+               strbuf_vaddf(&buf, fmt, ap);
+       }
+       va_end(ap);
+
+       return buf.buf;
+}
 
 static int do_reset(struct repository *r,
                    const char *name, int len,
@@ -4178,27 +4198,6 @@ int apply_autostash_oid(const char *stash_oid)
        return apply_save_autostash_oid(stash_oid, 1);
 }
 
-static const char *reflog_message(struct replay_opts *opts,
-       const char *sub_action, const char *fmt, ...)
-{
-       va_list ap;
-       static struct strbuf buf = STRBUF_INIT;
-       char *reflog_action = getenv(GIT_REFLOG_ACTION);
-
-       va_start(ap, fmt);
-       strbuf_reset(&buf);
-       strbuf_addstr(&buf, reflog_action ? reflog_action : action_name(opts));
-       if (sub_action)
-               strbuf_addf(&buf, " (%s)", sub_action);
-       if (fmt) {
-               strbuf_addstr(&buf, ": ");
-               strbuf_vaddf(&buf, fmt, ap);
-       }
-       va_end(ap);
-
-       return buf.buf;
-}
-
 static int run_git_checkout(struct repository *r, struct replay_opts *opts,
                            const char *commit, const char *action)
 {
diff --git a/serve.c b/serve.c
index aa8209f147ef25c674502e663906779b09a237f8..f11c0e07c45fc2653c4204bafbf04473358444fb 100644 (file)
--- a/serve.c
+++ b/serve.c
@@ -258,7 +258,7 @@ static int process_request(void)
                        state = PROCESS_REQUEST_DONE;
                        break;
                case PACKET_READ_RESPONSE_END:
-                       BUG("unexpected stateless separator packet");
+                       BUG("unexpected response end packet");
                }
        }
 
index de0aa4498c6131da80fb9ef8afb061fc1b9aff7e..7701d7c20a12af53d2f91737f12f3dfb97ac59e9 100644 (file)
@@ -27,6 +27,7 @@ static int uic_is_stale(const struct update_info_ctx *uic)
        return uic->old_fp == NULL;
 }
 
+__attribute__((format (printf, 2, 3)))
 static int uic_printf(struct update_info_ctx *uic, const char *fmt, ...)
 {
        va_list ap;
index a86dcaaf44d15805c3f87aa18a7848a1aba31ec3..5b1113abf8fccd49c936ed3fb5fe5d9e5dc774aa 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -263,6 +263,7 @@ static inline void strbuf_insertstr(struct strbuf *sb, size_t pos,
 void strbuf_vinsertf(struct strbuf *sb, size_t pos, const char *fmt,
                     va_list ap);
 
+__attribute__((format (printf, 3, 4)))
 void strbuf_insertf(struct strbuf *sb, size_t pos, const char *fmt, ...);
 
 /**
index a7043df1d38ad3e94c76390dc5c33157c72fc76d..cb881139f7316f475ea072c1b549aafe7cca2ad1 100644 (file)
@@ -16,7 +16,7 @@ int cmd__advise_if_enabled(int argc, const char **argv)
         * selected here and in t0018 where this command is being
         * executed.
         */
-       advise_if_enabled(ADVICE_NESTED_TAG, argv[1]);
+       advise_if_enabled(ADVICE_NESTED_TAG, "%s", argv[1]);
 
        return 0;
 }
diff --git a/t/helper/test-oidtree.c b/t/helper/test-oidtree.c
new file mode 100644 (file)
index 0000000..180ee28
--- /dev/null
@@ -0,0 +1,49 @@
+#include "test-tool.h"
+#include "cache.h"
+#include "oidtree.h"
+
+static enum cb_next print_oid(const struct object_id *oid, void *data)
+{
+       puts(oid_to_hex(oid));
+       return CB_CONTINUE;
+}
+
+int cmd__oidtree(int argc, const char **argv)
+{
+       struct oidtree ot;
+       struct strbuf line = STRBUF_INIT;
+       int nongit_ok;
+       int algo = GIT_HASH_UNKNOWN;
+
+       oidtree_init(&ot);
+       setup_git_directory_gently(&nongit_ok);
+
+       while (strbuf_getline(&line, stdin) != EOF) {
+               const char *arg;
+               struct object_id oid;
+
+               if (skip_prefix(line.buf, "insert ", &arg)) {
+                       if (get_oid_hex_any(arg, &oid) == GIT_HASH_UNKNOWN)
+                               die("insert not a hexadecimal oid: %s", arg);
+                       algo = oid.algo;
+                       oidtree_insert(&ot, &oid);
+               } else if (skip_prefix(line.buf, "contains ", &arg)) {
+                       if (get_oid_hex(arg, &oid))
+                               die("contains not a hexadecimal oid: %s", arg);
+                       printf("%d\n", oidtree_contains(&ot, &oid));
+               } else if (skip_prefix(line.buf, "each ", &arg)) {
+                       char buf[GIT_MAX_HEXSZ + 1] = { '0' };
+                       memset(&oid, 0, sizeof(oid));
+                       memcpy(buf, arg, strlen(arg));
+                       buf[hash_algos[algo].hexsz] = '\0';
+                       get_oid_hex_any(buf, &oid);
+                       oid.algo = algo;
+                       oidtree_each(&ot, &oid, strlen(arg), print_oid, NULL);
+               } else if (!strcmp(line.buf, "clear")) {
+                       oidtree_clear(&ot);
+               } else {
+                       die("unknown command: %s", line.buf);
+               }
+       }
+       return 0;
+}
index b21e8f15190228f8664ad21405c6c7bd37f9a6e9..490ac026c5108c70ed90dfd67f522174f2032afa 100644 (file)
@@ -43,6 +43,7 @@ static struct test_cmd cmds[] = {
        { "mktemp", cmd__mktemp },
        { "oid-array", cmd__oid_array },
        { "oidmap", cmd__oidmap },
+       { "oidtree", cmd__oidtree },
        { "online-cpus", cmd__online_cpus },
        { "parse-options", cmd__parse_options },
        { "parse-pathspec-file", cmd__parse_pathspec_file },
index f845ced4b3a58881980e7f7c2e3b4d72b941a069..f8dc266721fafce04c3715bf969f5fffa10d9a1f 100644 (file)
@@ -32,6 +32,7 @@ int cmd__match_trees(int argc, const char **argv);
 int cmd__mergesort(int argc, const char **argv);
 int cmd__mktemp(int argc, const char **argv);
 int cmd__oidmap(int argc, const char **argv);
+int cmd__oidtree(int argc, const char **argv);
 int cmd__online_cpus(int argc, const char **argv);
 int cmd__parse_options(int argc, const char **argv);
 int cmd__parse_pathspec_file(int argc, const char** argv);
diff --git a/t/t0069-oidtree.sh b/t/t0069-oidtree.sh
new file mode 100755 (executable)
index 0000000..bfb1397
--- /dev/null
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='basic tests for the oidtree implementation'
+. ./test-lib.sh
+
+maxhexsz=$(test_oid hexsz)
+echoid () {
+       prefix="${1:+$1 }"
+       shift
+       while test $# -gt 0
+       do
+               shortoid="$1"
+               shift
+               difference=$(($maxhexsz - ${#shortoid}))
+               printf "%s%s%0${difference}d\\n" "$prefix" "$shortoid" "0"
+       done
+}
+
+test_expect_success 'oidtree insert and contains' '
+       cat >expect <<-\EOF &&
+               0
+               0
+               0
+               1
+               1
+               0
+       EOF
+       {
+               echoid insert 444 1 2 3 4 5 a b c d e &&
+               echoid contains 44 441 440 444 4440 4444
+               echo clear
+       } | test-tool oidtree >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'oidtree each' '
+       echoid "" 123 321 321 >expect &&
+       {
+               echoid insert f 9 8 123 321 a b c d e
+               echo each 12300
+               echo each 3211
+               echo each 3210
+               echo each 32100
+               echo clear
+       } | test-tool oidtree >actual &&
+       test_cmp expect actual
+'
+
+test_done
index 96dfca1554217df3a9ed734f7dff21e04659dbef..37ad79470fb9e009e99447b04cc05673314e49e3 100755 (executable)
@@ -67,11 +67,25 @@ test_expect_success '"add" worktree' '
 '
 
 test_expect_success '"add" worktree with lock' '
-       git rev-parse HEAD >expect &&
        git worktree add --detach --lock here-with-lock main &&
+       test_when_finished "git worktree unlock here-with-lock || :" &&
        test -f .git/worktrees/here-with-lock/locked
 '
 
+test_expect_success '"add" worktree with lock and reason' '
+       lock_reason="why not" &&
+       git worktree add --detach --lock --reason "$lock_reason" here-with-lock-reason main &&
+       test_when_finished "git worktree unlock here-with-lock-reason || :" &&
+       test -f .git/worktrees/here-with-lock-reason/locked &&
+       echo "$lock_reason" >expect &&
+       test_cmp expect .git/worktrees/here-with-lock-reason/locked
+'
+
+test_expect_success '"add" worktree with reason but no lock' '
+       test_must_fail git worktree add --detach --reason "why not" here-with-reason-only main &&
+       test_path_is_missing .git/worktrees/here-with-reason-only/locked
+'
+
 test_expect_success '"add" worktree from a subdir' '
        (
                mkdir sub &&
index 39e746fbcbe228b7d81678859cc8f549e06f2927..9dfead936b7b5be7e0c5a16c2deb90837d7c96d4 100755 (executable)
@@ -1915,6 +1915,20 @@ test_expect_success '--exclude-promisor-objects does not BUG-crash' '
        test_must_fail git log --exclude-promisor-objects source-a
 '
 
+test_expect_success 'log --decorate includes all levels of tag annotated tags' '
+       git checkout -b branch &&
+       git commit --allow-empty -m "new commit" &&
+       git tag lightweight HEAD &&
+       git tag -m annotated annotated HEAD &&
+       git tag -m double-0 double-0 HEAD &&
+       git tag -m double-1 double-1 double-0 &&
+       cat >expect <<-\EOF &&
+       HEAD -> branch, tag: lightweight, tag: double-1, tag: double-0, tag: annotated
+       EOF
+       git log -1 --format="%D" >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'log --end-of-options' '
        git update-ref refs/heads/--source HEAD &&
        git log --end-of-options --source >actual &&
index 35a2f623925535fe889170ee10723df05bd62e9a..41d0ca00b1c025beaadedf195586fb81db835bbb 100755 (executable)
@@ -41,22 +41,59 @@ test_expect_success 'setup' '
        echo "$added_iso88591" | git commit -F - &&
        head1=$(git rev-parse --verify HEAD) &&
        head1_short=$(git rev-parse --verify --short $head1) &&
+       head1_short4=$(git rev-parse --verify --short=4 $head1) &&
        tree1=$(git rev-parse --verify HEAD:) &&
        tree1_short=$(git rev-parse --verify --short $tree1) &&
        echo "$changed" > foo &&
        echo "$changed_iso88591" | git commit -a -F - &&
        head2=$(git rev-parse --verify HEAD) &&
        head2_short=$(git rev-parse --verify --short $head2) &&
+       head2_short4=$(git rev-parse --verify --short=4 $head2) &&
        tree2=$(git rev-parse --verify HEAD:) &&
        tree2_short=$(git rev-parse --verify --short $tree2) &&
        git config --unset i18n.commitEncoding
 '
 
-# usage: test_format name format_string [failure] <expected_output
+# usage: test_format [argument...] name format_string [failure] <expected_output
 test_format () {
+       local args=
+       while true
+       do
+               case "$1" in
+               --*)
+                       args="$args $1"
+                       shift;;
+               *)
+                       break;;
+               esac
+       done
        cat >expect.$1
        test_expect_${3:-success} "format $1" "
-               git rev-list --pretty=format:'$2' main >output.$1 &&
+               git rev-list $args --pretty=format:'$2' main >output.$1 &&
+               test_cmp expect.$1 output.$1
+       "
+}
+
+# usage: test_pretty [argument...] name format_name [failure] <expected_output
+test_pretty () {
+       local args=
+       while true
+       do
+               case "$1" in
+               --*)
+                       args="$args $1"
+                       shift;;
+               *)
+                       break;;
+               esac
+       done
+       cat >expect.$1
+       test_expect_${3:-success} "pretty $1 (without --no-commit-header)" "
+               git rev-list $args --pretty='$2' main >output.$1 &&
+               test_cmp expect.$1 output.$1
+       "
+       test_expect_${3:-success} "pretty $1 (with --no-commit-header)" "
+               git rev-list $args --no-commit-header --pretty='$2' main >output.$1 &&
                test_cmp expect.$1 output.$1
        "
 }
@@ -93,6 +130,20 @@ $head1
 $head1_short
 EOF
 
+test_format --no-commit-header hash-no-header %H%n%h <<EOF
+$head2
+$head2_short
+$head1
+$head1_short
+EOF
+
+test_format --abbrev-commit --abbrev=0 --no-commit-header hash-no-header-abbrev %H%n%h <<EOF
+$head2
+$head2_short4
+$head1
+$head1_short4
+EOF
+
 test_format tree %T%n%t <<EOF
 commit $head2
 $tree2
@@ -181,6 +232,31 @@ $added
 
 EOF
 
+test_format --no-commit-header raw-body-no-header %B <<EOF
+$changed
+
+$added
+
+EOF
+
+test_pretty oneline oneline <<EOF
+$head2 $changed
+$head1 $added
+EOF
+
+test_pretty short short <<EOF
+commit $head2
+Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
+
+    $changed
+
+commit $head1
+Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
+
+    $added
+
+EOF
+
 test_expect_success 'basic colors' '
        cat >expect <<-EOF &&
        commit $head2
index 7d02f79c0deef8d271468f0aeb6ee6ddbdc44969..54c2082acb8d19338aed25277467b6a9760c7bda 100755 (executable)
@@ -498,7 +498,7 @@ test_expect_success 'invalid message options when using --fixup' '
 cat >expected-template <<EOF
 
 # Please enter the commit message for your changes. Lines starting
-# with '#' will be ignored, and an empty message aborts the commit.
+# with '#' will be ignored.
 #
 # Author:    A U Thor <author@example.com>
 #
index e9dc58f508d512e0fb8803803a5511a68595c708..57fc10e7f82186e6a9af93f50c503e4c6bf6448b 100755 (executable)
@@ -1368,6 +1368,16 @@ test_expect_success $PREREQ 'sendemail.identity: bool variable fallback' '
        ! grep "X-Mailer" stdout
 '
 
+test_expect_success $PREREQ 'sendemail.identity: bool variable without a value' '
+       git -c sendemail.xmailer \
+               send-email \
+               --dry-run \
+               --from="nobody@example.com" \
+               $patches >stdout &&
+       grep "To: default@example.com" stdout &&
+       grep "X-Mailer" stdout
+'
+
 test_expect_success $PREREQ '--no-to overrides sendemail.to' '
        git send-email \
                --dry-run \
@@ -2092,6 +2102,18 @@ test_expect_success $PREREQ '--[no-]xmailer with sendemail.xmailer=true' '
        do_xmailer_test 1 "--xmailer"
 '
 
+test_expect_success $PREREQ '--[no-]xmailer with sendemail.xmailer' '
+       test_when_finished "test_unconfig sendemail.xmailer" &&
+       cat >>.git/config <<-\EOF &&
+       [sendemail]
+               xmailer
+       EOF
+       test_config sendemail.xmailer true &&
+       do_xmailer_test 1 "" &&
+       do_xmailer_test 0 "--no-xmailer" &&
+       do_xmailer_test 1 "--xmailer"
+'
+
 test_expect_success $PREREQ '--[no-]xmailer with sendemail.xmailer=false' '
        test_config sendemail.xmailer false &&
        do_xmailer_test 0 "" &&
@@ -2099,6 +2121,13 @@ test_expect_success $PREREQ '--[no-]xmailer with sendemail.xmailer=false' '
        do_xmailer_test 1 "--xmailer"
 '
 
+test_expect_success $PREREQ '--[no-]xmailer with sendemail.xmailer=' '
+       test_config sendemail.xmailer "" &&
+       do_xmailer_test 0 "" &&
+       do_xmailer_test 0 "--no-xmailer" &&
+       do_xmailer_test 1 "--xmailer"
+'
+
 test_expect_success $PREREQ 'setup expected-list' '
        git send-email \
        --dry-run \
index 237517baee675631d50020ba0d75853881010f6d..092a4f92ad250e6ab474b6528957cfcf18bb8fda 100644 (file)
@@ -265,6 +265,7 @@ const char *worktree_prune_reason(struct worktree *wt, timestamp_t expire)
 }
 
 /* convenient wrapper to deal with NULL strbuf */
+__attribute__((format (printf, 2, 3)))
 static void strbuf_addf_gently(struct strbuf *buf, const char *fmt, ...)
 {
        va_list params;