]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'jk/shorten-unambiguous-ref-wo-sscanf'
authorJunio C Hamano <gitster@pobox.com>
Mon, 27 Feb 2023 18:08:57 +0000 (10:08 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 27 Feb 2023 18:08:57 +0000 (10:08 -0800)
sscanf(3) used in "git symbolic-ref --short" implementation found
to be not working reliably on macOS in UTF-8 locales.  Rewrite the
code to avoid sscanf() altogether to work it around.

* jk/shorten-unambiguous-ref-wo-sscanf:
  shorten_unambiguous_ref(): avoid sscanf()
  shorten_unambiguous_ref(): use NUM_REV_PARSE_RULES constant
  shorten_unambiguous_ref(): avoid integer truncation

494 files changed:
.gitattributes
.github/workflows/check-whitespace.yml
.github/workflows/l10n.yml
.github/workflows/main.yml
.gitignore
Documentation/RelNotes/2.30.8.txt
Documentation/RelNotes/2.39.3.txt [new file with mode: 0644]
Documentation/RelNotes/2.40.0.txt [new file with mode: 0644]
Documentation/config/add.txt
Documentation/config/bundle.txt
Documentation/config/feature.txt
Documentation/config/fetch.txt
Documentation/config/format.txt
Documentation/config/index.txt
Documentation/config/transfer.txt
Documentation/diff-generate-patch.txt
Documentation/diff-options.txt
Documentation/git-add.txt
Documentation/git-am.txt
Documentation/git-apply.txt
Documentation/git-archive.txt
Documentation/git-bisect-lk2009.txt
Documentation/git-branch.txt
Documentation/git-cat-file.txt
Documentation/git-check-attr.txt
Documentation/git-checkout.txt
Documentation/git-cherry-pick.txt
Documentation/git-credential-cache.txt
Documentation/git-credential.txt
Documentation/git-fetch.txt
Documentation/git-hook.txt
Documentation/git-ls-files.txt
Documentation/git-ls-remote.txt
Documentation/git-merge-tree.txt
Documentation/git-merge.txt
Documentation/git-read-tree.txt
Documentation/git-rebase.txt
Documentation/git-reset.txt
Documentation/git-status.txt
Documentation/git-var.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitcredentials.txt
Documentation/gitformat-index.txt
Documentation/gitformat-signature.txt
Documentation/githooks.txt
Documentation/gitprotocol-v2.txt
Documentation/howto/new-command.txt
Documentation/pretty-formats.txt
Documentation/rev-list-options.txt
Documentation/revisions.txt
Documentation/technical/bundle-uri.txt
Documentation/technical/hash-function-transition.txt
Documentation/technical/rerere.txt
Documentation/urls-remotes.txt
GIT-VERSION-GEN
INSTALL
Makefile
RelNotes
add-interactive.c
apply.c
archive.c
archive.h
attr.c
attr.h
bisect.c
blob.c
blob.h
branch.c
builtin.h
builtin/add.c
builtin/am.c
builtin/bisect.c [moved from builtin/bisect--helper.c with 90% similarity]
builtin/branch.c
builtin/bugreport.c
builtin/bundle.c
builtin/cat-file.c
builtin/check-attr.c
builtin/checkout.c
builtin/clean.c
builtin/clone.c
builtin/commit-graph.c
builtin/commit.c
builtin/config.c
builtin/credential-cache--daemon.c
builtin/diff.c
builtin/difftool.c
builtin/fast-export.c
builtin/fetch.c
builtin/fsmonitor--daemon.c
builtin/hash-object.c
builtin/hook.c
builtin/log.c
builtin/ls-files.c
builtin/ls-remote.c
builtin/ls-tree.c
builtin/merge-tree.c
builtin/merge.c
builtin/mv.c
builtin/name-rev.c
builtin/pack-objects.c
builtin/push.c
builtin/range-diff.c
builtin/read-tree.c
builtin/rebase.c
builtin/receive-pack.c
builtin/remote-ext.c
builtin/remote-fd.c
builtin/repack.c
builtin/rerere.c
builtin/reset.c
builtin/rev-parse.c
builtin/revert.c
builtin/rm.c
builtin/show-branch.c
builtin/sparse-checkout.c
builtin/stash.c
builtin/submodule--helper.c
builtin/unpack-file.c
builtin/update-index.c
builtin/var.c
builtin/worktree.c
builtin/write-tree.c
bundle-uri.c
bundle-uri.h
bundle.c
cache-tree.c
cache-tree.h
cache.h
ci/lib.sh
ci/run-build-and-tests.sh
combine-diff.c
commit-graph.c
commit-reach.c
commit.c
commit.h
compat/fsmonitor/fsm-darwin-gcc.h
compat/fsmonitor/fsm-listen-darwin.c
compat/mingw.c
compat/regcomp_enhanced.c [new file with mode: 0644]
compat/win32/pthread.c
compat/win32/pthread.h
compat/winansi.c
config.c
config.h
config.mak.uname
connect.c
connect.h
connected.c
contrib/coccinelle/array.cocci
contrib/coccinelle/index-compatibility.cocci
contrib/coccinelle/index-compatibility.pending.cocci [deleted file]
contrib/completion/git-completion.bash
contrib/git-jump/README
contrib/git-jump/git-jump
convert.c
credential.c
credential.h
csum-file.c
csum-file.h
date.c
delta-islands.c
delta-islands.h
diff-lib.c
diff-no-index.c
diff.c
diff.h
dir-iterator.c
dir-iterator.h
dir.c
dir.h
entry.c
entry.h
environment.c
fetch-pack.c
fsck.c
fsck.h
fsmonitor-settings.c
fsmonitor.c
git-add--interactive.perl [deleted file]
git-bisect.sh [deleted file]
git-compat-util.h
git-request-pull.sh
git-send-email.perl
git-submodule.sh
git.c
gpg-interface.c
grep.c
help.c
hook.c
hook.h
http-backend.c
http-fetch.c
line-range.c
list-objects-filter-options.c
list-objects-filter-options.h
list-objects-filter.c
list-objects.c
ll-merge.c
ls-refs.c
merge-recursive.c
object-file.c
object-store.h
object.c
pack-bitmap.c
pack-write.c
packfile.c
parse-options.c
parse-options.h
pathspec.c
range-diff.c
read-cache.c
ref-filter.c
reflog.c
refs.c
refs.h
refs/packed-backend.c
remote.h
repo-settings.c
repository.c
repository.h
revision.c
run-command.c
scalar.c
sequencer.c
sequencer.h
serve.c
sparse-index.c
split-index.c
strbuf.c
strbuf.h
streaming.c
submodule.c
submodule.h
t/README
t/helper/test-bundle-uri.c
t/helper/test-ctype.c
t/helper/test-dir-iterator.c
t/helper/test-env-helper.c [moved from builtin/env--helper.c with 71% similarity]
t/helper/test-fake-ssh.c
t/helper/test-genzeros.c
t/helper/test-tool.c
t/helper/test-tool.h
t/interop/interop-lib.sh
t/lib-bundle-uri-protocol.sh [new file with mode: 0644]
t/lib-diff-alternative.sh
t/lib-httpd.sh
t/lib-httpd/apache.conf
t/lib-httpd/proxy-passwd [new file with mode: 0644]
t/lib-httpd/ssl.cnf
t/perf/p7822-grep-perl-character.sh [new file with mode: 0755]
t/t0000-basic.sh
t/t0003-attributes.sh
t/t0006-date.sh
t/t0007-git-var.sh
t/t0017-env-helper.sh
t/t0021-conversion.sh
t/t0023-crlf-am.sh
t/t0066-dir-iterator.sh
t/t0068-for-each-repo.sh
t/t0070-fundamental.sh
t/t0300-credentials.sh
t/t1001-read-tree-m-2way.sh
t/t1006-cat-file.sh
t/t1007-hash-object.sh
t/t1011-read-tree-sparse-checkout.sh
t/t1022-read-tree-partial-clone.sh
t/t1091-sparse-checkout-builtin.sh
t/t1301-shared-repo.sh
t/t1302-repo-version.sh
t/t1304-default-acl.sh
t/t1404-update-ref-errors.sh
t/t1408-packed-refs.sh
t/t1409-avoid-packing-refs.sh
t/t1410-reflog.sh
t/t1413-reflog-detach.sh
t/t1416-ref-transaction-hooks.sh
t/t1450-fsck.sh
t/t1451-fsck-buffer.sh [new file with mode: 0755]
t/t1500-rev-parse.sh
t/t1501-work-tree.sh
t/t1509-root-work-tree.sh
t/t1600-index.sh
t/t1800-hook.sh
t/t2012-checkout-last.sh
t/t2015-checkout-unborn.sh
t/t2016-checkout-patch.sh
t/t2018-checkout-branch.sh
t/t2025-checkout-no-overlay.sh
t/t2401-worktree-prune.sh
t/t2402-worktree-list.sh
t/t2406-worktree-repair.sh
t/t3009-ls-files-others-nonsubmodule.sh
t/t3010-ls-files-killed-modified.sh
t/t3050-subprojects-fetch.sh
t/t3060-ls-files-with-tree.sh
t/t3104-ls-tree-format.sh
t/t3204-branch-name-interpretation.sh
t/t3206-range-diff.sh
t/t3210-pack-refs.sh
t/t3404-rebase-interactive.sh
t/t3405-rebase-malformed.sh
t/t3409-rebase-environ.sh
t/t3412-rebase-root.sh
t/t3413-rebase-hook.sh
t/t3416-rebase-onto-threedots.sh
t/t3419-rebase-patch-id.sh
t/t3422-rebase-incompatible-options.sh
t/t3423-rebase-reword.sh
t/t3425-rebase-topology-merges.sh
t/t3428-rebase-signoff.sh
t/t3429-rebase-edit-todo.sh
t/t3431-rebase-fork-point.sh
t/t3432-rebase-fast-forward.sh
t/t3433-rebase-across-mode-change.sh
t/t3437-rebase-fixup-options.sh
t/t3438-rebase-broken-files.sh
t/t3501-revert-cherry-pick.sh
t/t3502-cherry-pick-merge.sh
t/t3503-cherry-pick-root.sh
t/t3506-cherry-pick-ff.sh
t/t3511-cherry-pick-x.sh
t/t3701-add-interactive.sh
t/t3800-mktag.sh
t/t3920-crlf-messages.sh
t/t4014-format-patch.sh
t/t4015-diff-whitespace.sh
t/t4018/java-class-brace-on-separate-line [new file with mode: 0644]
t/t4018/java-class-space-before-type-parameters [new file with mode: 0644]
t/t4018/java-class-type-parameters [new file with mode: 0644]
t/t4018/java-class-type-parameters-implements [new file with mode: 0644]
t/t4018/java-interface-type-parameters [new file with mode: 0644]
t/t4018/java-interface-type-parameters-extends [new file with mode: 0644]
t/t4018/java-non-sealed [new file with mode: 0644]
t/t4018/java-record [new file with mode: 0644]
t/t4018/java-record-space-before-components [new file with mode: 0644]
t/t4018/java-record-type-parameters [new file with mode: 0644]
t/t4018/java-sealed [new file with mode: 0644]
t/t4018/java-sealed-permits [new file with mode: 0644]
t/t4018/java-sealed-type-parameters [new file with mode: 0644]
t/t4018/java-sealed-type-parameters-implements-permits [new file with mode: 0644]
t/t4018/java-sealed-type-parameters-permits [new file with mode: 0644]
t/t4023-diff-rename-typechange.sh
t/t4044-diff-index-unique-abbrev.sh
t/t4045-diff-relative.sh
t/t4046-diff-unmerged.sh
t/t4052-stat-output.sh
t/t4053-diff-no-index.sh
t/t4054-diff-bogus-tree.sh
t/t4058-diff-duplicates.sh
t/t4067-diff-partial-clone.sh
t/t4111-apply-subdir.sh
t/t4135-apply-weird-filenames.sh
t/t4150-am.sh
t/t4152-am-subjects.sh
t/t4203-mailmap.sh
t/t4205-log-pretty-formats.sh
t/t4211-line-log.sh
t/t4212-log-corrupt.sh
t/t4213-log-tabexpand.sh
t/t4254-am-corrupt.sh
t/t4256-am-format-flowed.sh
t/t4257-am-interactive.sh
t/t4301-merge-tree-write-tree.sh
t/t5000-tar-tree.sh
t/t5001-archive-attr.sh
t/t5004-archive-corner-cases.sh
t/t5302-pack-index.sh
t/t5306-pack-nobase.sh
t/t5310-pack-bitmaps.sh
t/t5312-prune-corruption.sh
t/t5313-pack-bounds-checks.sh
t/t5314-pack-cycle-detection.sh
t/t5317-pack-objects-filter-objects.sh
t/t5319-multi-pack-index.sh
t/t5330-no-lazy-fetch-with-commit-graph.sh
t/t5403-post-checkout-hook.sh
t/t5405-send-pack-rewind.sh
t/t5406-remote-rejects.sh
t/t5502-quickfetch.sh
t/t5504-fetch-receive-strict.sh
t/t5506-remote-groups.sh
t/t5507-remote-environment.sh
t/t5510-fetch.sh
t/t5514-fetch-multiple.sh
t/t5522-pull-symlink.sh
t/t5523-push-upstream.sh
t/t5527-fetch-odd-refs.sh
t/t5529-push-errors.sh
t/t5544-pack-objects-hook.sh
t/t5546-receive-limits.sh
t/t5547-push-quarantine.sh
t/t5554-noop-fetch-negotiator.sh
t/t5558-clone-bundle-uri.sh
t/t5560-http-backend-noserver.sh
t/t5561-http-backend.sh
t/t5562-http-backend-content-length.sh
t/t5564-http-proxy.sh [new file with mode: 0755]
t/t5573-pull-verify-signatures.sh
t/t5601-clone.sh
t/t5604-clone-reference.sh
t/t5606-clone-options.sh
t/t5610-clone-detached.sh
t/t5611-clone-config.sh
t/t5613-info-alternate.sh
t/t5614-clone-submodules-shallow.sh
t/t5616-partial-clone.sh
t/t5617-clone-submodules-remote.sh
t/t5618-alternate-refs.sh
t/t5701-git-serve.sh
t/t5702-protocol-v2.sh
t/t5705-session-id-in-capabilities.sh
t/t5730-protocol-v2-bundle-uri-file.sh [new file with mode: 0755]
t/t5731-protocol-v2-bundle-uri-git.sh [new file with mode: 0755]
t/t5732-protocol-v2-bundle-uri-http.sh [new file with mode: 0755]
t/t5750-bundle-uri-parse.sh
t/t5810-proto-disable-local.sh
t/t5813-proto-disable-ssh.sh
t/t6003-rev-list-topo-order.sh
t/t6011-rev-list-with-bad-commit.sh
t/t6014-rev-list-all.sh
t/t6020-bundle-misc.sh
t/t6021-rev-list-exclude-hidden.sh
t/t6030-bisect-porcelain.sh
t/t6060-merge-index.sh
t/t6120-describe.sh
t/t6132-pathspec-exclude.sh
t/t6300-for-each-ref.sh
t/t6301-for-each-ref-errors.sh
t/t6401-merge-criss-cross.sh
t/t6406-merge-attr.sh
t/t6407-merge-binary.sh
t/t6415-merge-dir-to-symlink.sh
t/t6421-merge-partial-clone.sh
t/t6422-merge-rename-corner-cases.sh
t/t6426-merge-skip-unneeded-updates.sh
t/t6435-merge-sparse.sh
t/t6439-merge-co-error-msgs.sh
t/t6501-freshen-objects.sh
t/t7030-verify-tag.sh
t/t7031-verify-tag-signed-ssh.sh
t/t7103-reset-bare.sh
t/t7105-reset-patch.sh
t/t7106-reset-unborn-branch.sh
t/t7107-reset-pathspec-file.sh
t/t7301-clean-interactive.sh
t/t7402-submodule-rebase.sh
t/t7403-submodule-sync.sh
t/t7409-submodule-detached-work-tree.sh
t/t7412-submodule-absorbgitdirs.sh
t/t7416-submodule-dash-url.sh
t/t7450-bad-git-dotfiles.sh
t/t7504-commit-msg-hook.sh
t/t7508-status.sh
t/t7509-commit-authorship.sh
t/t7510-signed-commit.sh
t/t7517-per-repo-email.sh
t/t7520-ignored-hook-warning.sh
t/t7527-builtin-fsmonitor.sh
t/t7528-signed-commit-ssh.sh
t/t7600-merge.sh
t/t7605-merge-resolve.sh
t/t7612-merge-verify-signatures.sh
t/t7614-merge-signoff.sh
t/t7701-repack-unpack-unreachable.sh
t/t8003-blame-corner-cases.sh
t/t9001-send-email.sh
t/t9003-help-autocorrect.sh
t/t9106-git-svn-commit-diff-clobber.sh
t/t9115-git-svn-dcommit-funky-renames.sh
t/t9119-git-svn-info.sh
t/t9146-git-svn-empty-dirs.sh
t/t9148-git-svn-propset.sh
t/t9160-git-svn-preserve-empty-dirs.sh
t/t9164-git-svn-dcommit-concurrent.sh
t/t9210-scalar.sh
t/t9211-scalar-clone.sh
t/t9350-fast-export.sh
t/t9902-completion.sh
t/test-lib-functions.sh
t/test-lib.sh
transport-helper.c
transport-internal.h
transport.c
transport.h
unpack-trees.c
unpack-trees.h
urlmatch.c
userdiff.c
userdiff.h
ws.c
wt-status.c
xdiff/xdiffi.c
xdiff/xemit.c

index b0044cf272fec9b987e99c600d6a95bc357261c3..158c3d45c4c10cb19c7d3fb02d6bfdaf7a1794f1 100644 (file)
@@ -1,17 +1,17 @@
 * whitespace=!indent,trail,space
 *.[ch] whitespace=indent,trail,space diff=cpp
-*.sh whitespace=indent,trail,space eol=lf
-*.perl eol=lf diff=perl
-*.pl eof=lf diff=perl
-*.pm eol=lf diff=perl
-*.py eol=lf diff=python
-*.bat eol=crlf
+*.sh whitespace=indent,trail,space text eol=lf
+*.perl text eol=lf diff=perl
+*.pl text eof=lf diff=perl
+*.pm text eol=lf diff=perl
+*.py text eol=lf diff=python
+*.bat text eol=crlf
 CODE_OF_CONDUCT.md -whitespace
-/Documentation/**/*.txt eol=lf
-/command-list.txt eol=lf
-/GIT-VERSION-GEN eol=lf
-/mergetools/* eol=lf
-/t/oid-info/* eol=lf
+/Documentation/**/*.txt text eol=lf
+/command-list.txt text eol=lf
+/GIT-VERSION-GEN text eol=lf
+/mergetools/* text eol=lf
+/t/oid-info/* text eol=lf
 /Documentation/git-merge.txt conflict-marker-size=32
 /Documentation/gitk.txt conflict-marker-size=32
 /Documentation/user-manual.txt conflict-marker-size=32
index ad3466ad16e36cda7e6218811163adfbde74d266..a58e2dc8ad6279d792635c4034ed0d113d489695 100644 (file)
@@ -9,42 +9,83 @@ on:
   pull_request:
     types: [opened, synchronize]
 
+# Avoid unnecessary builds. Unlike the main CI jobs, these are not
+# ci-configurable (but could be).
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
 jobs:
   check-whitespace:
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
       with:
         fetch-depth: 0
 
     - name: git log --check
       id: check_out
       run: |
-        log=
+        baseSha=${{github.event.pull_request.base.sha}}
+        problems=()
         commit=
-        while read dash etc
+        commitText=
+        commitTextmd=
+        goodparent=
+        while read dash sha etc
         do
           case "${dash}" in
           "---")
-            commit="${etc}"
+            if test -z "${commit}"
+            then
+              goodparent=${sha}
+            fi
+            commit="${sha}"
+            commitText="${sha} ${etc}"
+            commitTextmd="[${sha}](https://github.com/${{ github.repository }}/commit/${sha}) ${etc}"
             ;;
           "")
             ;;
           *)
             if test -n "${commit}"
             then
-              log="${log}\n${commit}"
+              problems+=("1) --- ${commitTextmd}")
               echo ""
-              echo "--- ${commit}"
+              echo "--- ${commitText}"
+              commit=
             fi
-            commit=
-            log="${log}\n${dash} ${etc}"
-            echo "${dash} ${etc}"
+            case "${dash}" in
+            *:[1-9]*:) # contains file and line number information
+              dashend=${dash#*:}
+              problems+=("[${dash}](https://github.com/${{ github.repository }}/blob/${{github.event.pull_request.head.ref}}/${dash%%:*}#L${dashend%:}) ${sha} ${etc}")
+              ;;
+            *)
+              problems+=("\`${dash} ${sha} ${etc}\`")
+              ;;
+            esac
+            echo "${dash} ${sha} ${etc}"
             ;;
           esac
-        done <<< $(git log --check --pretty=format:"---% h% s" ${{github.event.pull_request.base.sha}}..)
+        done <<< $(git log --check --pretty=format:"---% h% s" ${baseSha}..)
 
-        if test -n "${log}"
+        if test ${#problems[*]} -gt 0
         then
+          if test -z "${commit}"
+          then
+            goodparent=${baseSha: 0:7}
+          fi
+          echo "🛑 Please review the Summary output for further information."
+          echo "### :x: A whitespace issue was found in one or more of the commits." >$GITHUB_STEP_SUMMARY
+          echo "" >>$GITHUB_STEP_SUMMARY
+          echo "Run these commands to correct the problem:" >>$GITHUB_STEP_SUMMARY
+          echo "1. \`git rebase --whitespace=fix ${goodparent}\`" >>$GITHUB_STEP_SUMMARY
+          echo "1. \`git push --force\`" >>$GITHUB_STEP_SUMMARY
+          echo " " >>$GITHUB_STEP_SUMMARY
+          echo "Errors:" >>$GITHUB_STEP_SUMMARY
+          for i in "${problems[@]}"
+          do
+            echo "${i}" >>$GITHUB_STEP_SUMMARY
+          done
+
           exit 2
         fi
index f7ea0f00a449cfafd5dc006e59267e464f753387..6c3849658aa061b89ced8e0464b3b580519c3a34 100644 (file)
@@ -2,6 +2,12 @@ name: git-l10n
 
 on: [push, pull_request_target]
 
+# Avoid unnecessary builds. Unlike the main CI jobs, these are not
+# ci-configurable (but could be).
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
 jobs:
   git-po-helper:
     if: >-
index e67847a682cea6da2d85e94cf96ea7f34db28fe2..30492eacddfc7db484e9c73af3ba49960d77f5cb 100644 (file)
@@ -11,6 +11,7 @@ jobs:
     runs-on: ubuntu-latest
     outputs:
       enabled: ${{ steps.check-ref.outputs.enabled }}${{ steps.skip-if-redundant.outputs.enabled }}
+      skip_concurrent: ${{ steps.check-ref.outputs.skip_concurrent }}
     steps:
       - name: try to clone ci-config branch
         run: |
@@ -34,7 +35,15 @@ jobs:
           then
             enabled=no
           fi
+
+          skip_concurrent=yes
+          if test -x config-repo/ci/config/skip-concurrent &&
+             ! config-repo/ci/config/skip-concurrent '${{ github.ref }}'
+          then
+            skip_concurrent=no
+          fi
           echo "enabled=$enabled" >>$GITHUB_OUTPUT
+          echo "skip_concurrent=$skip_concurrent" >>$GITHUB_OUTPUT
       - name: skip if the commit or tree was already tested
         id: skip-if-redundant
         uses: actions/github-script@v6
@@ -82,6 +91,9 @@ jobs:
     needs: ci-config
     if: needs.ci-config.outputs.enabled == 'yes'
     runs-on: windows-latest
+    concurrency:
+      group: windows-build-${{ github.ref }}
+      cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
     - uses: actions/checkout@v3
     - uses: git-for-windows/setup-git-for-windows-sdk@v1
@@ -101,11 +113,14 @@ jobs:
   windows-test:
     name: win test
     runs-on: windows-latest
-    needs: [windows-build]
+    needs: [ci-config, windows-build]
     strategy:
       fail-fast: false
       matrix:
         nr: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+    concurrency:
+      group: windows-test-${{ matrix.nr }}-${{ github.ref }}
+      cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
     - name: download tracked files and build artifacts
       uses: actions/download-artifact@v3
@@ -132,11 +147,14 @@ jobs:
   vs-build:
     name: win+VS build
     needs: ci-config
-    if: needs.ci-config.outputs.enabled == 'yes'
+    if: github.event.repository.owner.login == 'git-for-windows' && needs.ci-config.outputs.enabled == 'yes'
     env:
       NO_PERL: 1
       GIT_CONFIG_PARAMETERS: "'user.name=CI' 'user.email=ci@git'"
     runs-on: windows-latest
+    concurrency:
+      group: vs-build-${{ github.ref }}
+      cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
     - uses: actions/checkout@v3
     - uses: git-for-windows/setup-git-for-windows-sdk@v1
@@ -184,11 +202,14 @@ jobs:
   vs-test:
     name: win+VS test
     runs-on: windows-latest
-    needs: vs-build
+    needs: [ci-config, vs-build]
     strategy:
       fail-fast: false
       matrix:
         nr: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+    concurrency:
+      group: vs-test-${{ matrix.nr }}-${{ github.ref }}
+      cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
     - uses: git-for-windows/setup-git-for-windows-sdk@v1
     - name: download tracked files and build artifacts
@@ -218,6 +239,9 @@ jobs:
     name: ${{matrix.vector.jobname}} (${{matrix.vector.pool}})
     needs: ci-config
     if: needs.ci-config.outputs.enabled == 'yes'
+    concurrency:
+      group: ${{ matrix.vector.jobname }}-${{ matrix.vector.pool }}-${{ github.ref }}
+      cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     strategy:
       fail-fast: false
       matrix:
@@ -265,8 +289,9 @@ jobs:
     - uses: actions/checkout@v3
     - run: ci/install-dependencies.sh
     - run: ci/run-build-and-tests.sh
-    - run: ci/print-test-failures.sh
+    - name: print test failures
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
+      run: ci/print-test-failures.sh
     - name: Upload failed tests' directories
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
       uses: actions/upload-artifact@v3
@@ -277,6 +302,9 @@ jobs:
     name: ${{matrix.vector.jobname}} (${{matrix.vector.image}})
     needs: ci-config
     if: needs.ci-config.outputs.enabled == 'yes'
+    concurrency:
+      group: dockerized-${{ matrix.vector.jobname }}-${{ matrix.vector.image }}-${{ github.ref }}
+      cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     strategy:
       fail-fast: false
       matrix:
@@ -298,8 +326,9 @@ jobs:
       if: matrix.vector.jobname == 'linux32'
     - run: ci/install-docker-dependencies.sh
     - run: ci/run-build-and-tests.sh
-    - run: ci/print-test-failures.sh
+    - name: print test failures
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
+      run: ci/print-test-failures.sh
     - name: Upload failed tests' directories
       if: failure() && env.FAILED_TEST_ARTIFACTS != '' && matrix.vector.jobname != 'linux32'
       uses: actions/upload-artifact@v3
@@ -318,6 +347,9 @@ jobs:
     env:
       jobname: StaticAnalysis
     runs-on: ubuntu-22.04
+    concurrency:
+      group: static-analysis-${{ github.ref }}
+      cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
     - uses: actions/checkout@v3
     - run: ci/install-dependencies.sh
@@ -329,6 +361,9 @@ jobs:
     env:
       jobname: sparse
     runs-on: ubuntu-20.04
+    concurrency:
+      group: sparse-${{ github.ref }}
+      cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
     - name: Download a current `sparse` package
       # Ubuntu's `sparse` version is too old for us
@@ -347,6 +382,9 @@ jobs:
     name: documentation
     needs: ci-config
     if: needs.ci-config.outputs.enabled == 'yes'
+    concurrency:
+      group: documentation-${{ github.ref }}
+      cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     env:
       jobname: Documentation
     runs-on: ubuntu-latest
index 0832f1da77b0a1903d2db65689864c627899c5e5..e875c5905455885b174a4591aa5bb3cc2fd0bae2 100644 (file)
 /bin-wrappers/
 /git
 /git-add
-/git-add--interactive
 /git-am
 /git-annotate
 /git-apply
 /git-archimport
 /git-archive
 /git-bisect
-/git-bisect--helper
 /git-blame
 /git-branch
 /git-bugreport
@@ -60,7 +58,6 @@
 /git-difftool
 /git-difftool--helper
 /git-describe
-/git-env--helper
 /git-fast-export
 /git-fast-import
 /git-fetch
index 38c23e0345551c8c0c6145441ac912ef3a1118f7..5ed3efbd6af4bf799c7cae233d25da7cacd11c66 100644 (file)
@@ -49,4 +49,3 @@ Taylor Blau (3):
       t5619: demonstrate clone_local() with ambiguous transport
       clone: delay picking a transport until after get_repo_path()
       dir-iterator: prevent top-level symlinks without FOLLOW_SYMLINKS
-
diff --git a/Documentation/RelNotes/2.39.3.txt b/Documentation/RelNotes/2.39.3.txt
new file mode 100644 (file)
index 0000000..dddff53
--- /dev/null
@@ -0,0 +1,58 @@
+Git v2.39.3 Release Notes
+=========================
+
+This release is primarily to merge fixes accumulated on the 'master'
+front to prepare for 2.40 release that are still relevant to 2.39.x
+maintenance track.
+
+Fixes since v2.39.2
+-------------------
+
+ * Stop running win+VS build by default.
+
+ * CI updates.  We probably want a clean-up to move the long shell
+   script embedded in yaml file into a separate file, but that can
+   come later.
+
+ * Avoid unnecessary builds in CI, with settings configured in
+   ci-config.
+
+ * Redefining system functions for a few functions did not follow our
+   usual "implement git_foo() and #define foo(args) git_foo(args)"
+   pattern, which has broken build for some folks.
+
+ * Deal with a few deprecation warning from cURL library.
+
+ * Newer regex library macOS stopped enabling GNU-like enhanced BRE,
+   where '\(A\|B\)' works as alternation, unless explicitly asked with
+   the REG_ENHANCED flag.  "git grep" now can be compiled to do so, to
+   retain the old behaviour.
+
+ * When given a pattern that matches an empty string at the end of a
+   line, the code to parse the "git diff" line-ranges fell into an
+   infinite loop, which has been corrected.
+
+ * Fix the sequence to fsync $GIT_DIR/packed-refs file that forgot to
+   flush its output to the disk..
+
+ * "git diff --relative" did not mix well with "git diff --ext-diff",
+   which has been corrected.
+
+ * The logic to see if we are using the "cone" mode by checking the
+   sparsity patterns has been tightened to avoid mistaking a pattern
+   that names a single file as specifying a cone.
+
+ * Doc update for environment variables set when hooks are invoked.
+
+ * Document ORIG_HEAD a bit more.
+
+ * "git ls-tree --format='%(path) %(path)' $tree $path" showed the
+   path three times, which has been corrected.
+
+ * Document that "branch -f <branch>" disables only the safety to
+   avoid recreating an existing branch.
+
+ * Clarify how "checkout -b/-B" and "git branch [-f]" are similar but
+   different in the documentation.
+
+Also contains minor documentation updates and code clean-ups.
diff --git a/Documentation/RelNotes/2.40.0.txt b/Documentation/RelNotes/2.40.0.txt
new file mode 100644 (file)
index 0000000..922cf22
--- /dev/null
@@ -0,0 +1,280 @@
+Git v2.40 Release Notes
+=======================
+
+UI, Workflows & Features
+
+ * "merge-tree" learns a new `--merge-base` option.
+
+ * "git jump" (in contrib/) learned to present the "quickfix list" to
+   its standard output (instead of letting it consumed by the editor
+   it invokes), and learned to also drive emacs/emacsclient.
+
+ * "git var UNKNOWN_VARIABLE" and "git var VARIABLE" with the variable
+   given an empty value used to behave identically.  Now the latter
+   just gives an empty output, while the former still gives an error
+   message.
+
+ * Introduce a case insensitive mode to the Bash completion helpers.
+
+ * The advice message given by "git status" when it takes long time to
+   enumerate untracked paths has been updated.
+
+ * Just like "git var GIT_EDITOR" abstracts the complex logic to
+   choose which editor gets used behind it, "git var" now give support
+   to GIT_SEQUENCE_EDITOR.
+
+ * "git format-patch" learned to honor format.mboxrd even when sending
+   patches to the standard output stream,
+
+ * 'cat-file' gains mailmap support for its '--batch-check' and '-s'
+   options.
+
+ * Conditionally skip the pre-applypatch and applypatch-msg hooks when
+   applying patches with 'git am'.
+
+ * Introduce an optional configuration to allow the trailing hash that
+   protects the index file from bit flipping.
+
+ * "git check-attr" learned to take an optional tree-ish to read the
+   .gitattributes file from.
+
+ * "scalar" learned to give progress bar.
+
+ * "grep -P" learned to use Unicode Character Property to grok
+   character classes when processing \b and \w etc.
+
+ * "git rebase" often ignored incompatible options instead of
+   complaining, which has been corrected.
+
+ * "scalar" warns but continues when its periodic maintenance
+   feature cannot be enabled.
+
+ * The bundle-URI subsystem adds support for creation-token heuristics
+   to help incremental fetches.
+
+ * Userdiff regexp update for Java language.
+
+ * "git fetch --jobs=0" used to hit a BUG(), which has been corrected
+   to use the available CPUs.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * `git bisect` becomes a builtin.
+
+ * The pack-bitmap machinery is taught to log the paths of redundant
+   bitmap(s) to trace2 instead of stderr.
+
+ * Use the SHA1DC implementation on macOS, just like other platforms,
+   by default.
+
+ * Even in a repository with promisor remote, it is useless to
+   attempt to lazily attempt fetching an object that is expected to be
+   commit, because no "filter" mode omits commit objects.  Take
+   advantage of this assumption to fail fast on errors.
+
+ * Stop using "git --super-prefix" and narrow the scope of its use to
+   the submodule--helper.
+
+ * Stop running win+VS build by default.
+
+ * CI updates.  We probably want a clean-up to move the long shell
+   script embedded in yaml file into a separate file, but that can
+   come later.
+
+ * Use `git diff --no-index` as a test_cmp on Windows.
+
+   We'd probably need to revisit "do we really want to, and have to,
+   lose CRLF vs LF?" later, at which time we may be able to further
+   clean this up by replacing "git diff --no-index" with "diff -u".
+
+ * Avoid unnecessary builds in CI, with settings configured in
+   ci-config.
+
+ * Plug leaks in sequencer subsystem and its users.
+
+ * In-tree .gitattributes update to match the way we recommend our
+   users to mark a file as text.
+   (merge 1f34e0cd3d po/attributes-text later to maint).
+
+ * Finally retire the scripted "git add -p/-i" implementation and have
+   everybody use the one reimplemented in C.
+
+
+Fixes since v2.39
+-----------------
+
+ * Various leak fixes.
+
+ * Fix a bug where `pack-objects` would not respect multiple `--filter`
+   arguments when invoked directly.
+   (merge d4f7036887 rs/multi-filter-args later to maint).
+
+ * Make fsmonitor more robust to avoid the flakiness seen in t7527.
+   (merge 6692d45477 jh/t7527-unflake-by-forcing-cookie later to maint).
+
+ * Stop using deprecated macOS API in fsmonitor.
+   (merge b0226007f0 jh/fsmonitor-darwin-modernize later to maint).
+
+ * Redefining system functions for a few functions did not follow our
+   usual "implement git_foo() and #define foo(args) git_foo(args)"
+   pattern, which has broken build for some folks.
+
+ * The way the diff machinery prepares the options array for the
+   parse_options API has been refactored to avoid resource leaks.
+   (merge 189e97bc4b rs/diff-parseopts later to maint).
+
+ * Correct pthread API usage.
+   (merge 786e67611d sx/pthread-error-check-fix later to maint).
+
+ * The code to auto-correct a misspelt subcommand unnecessarily called
+   into git_default_config() from the early config codepath, which was
+   a no-no.  This has bee corrected.
+   (merge 0918d08887 sg/help-autocorrect-config-fix later to maint).
+
+ * "git http-fetch" (which is rarely used) forgot to identify itself
+   in the trace2 output.
+   (merge 7abb43cbc8 jt/http-fetch-trace2-report-name later to maint).
+
+ * The output from "git diff --stat" on an unmerged path lost the
+   terminating LF in Git 2.39, which has been corrected.
+   (merge 209d9cb011 pg/diff-stat-unmerged-regression-fix later to maint).
+
+ * "git pull -v --recurse-submodules" attempted to pass "-v" down to
+   underlying "git submodule update", which did not understand the
+   request and barfed, which has been corrected.
+   (merge 6f65f84766 ss/pull-v-recurse-fix later to maint).
+
+ * When given a pattern that matches an empty string at the end of a
+   line, the code to parse the "git diff" line-ranges fell into an
+   infinite loop, which has been corrected.
+
+ * Fix the sequence to fsync $GIT_DIR/packed-refs file that forgot to
+   flush its output to the disk..
+
+ * Fix to a small regression in 2.38 days.
+
+ * "git diff --relative" did not mix well with "git diff --ext-diff",
+   which has been corrected.
+
+ * The logic to see if we are using the "cone" mode by checking the
+   sparsity patterns has been tightened to avoid mistaking a pattern
+   that names a single file as specifying a cone.
+
+ * Deal with a few deprecation warning from cURL library.
+
+ * Doc update for environment variables set when hooks are invoked.
+
+ * Document ORIG_HEAD a bit more.
+
+ * "git ls-tree --format='%(path) %(path)' $tree $path" showed the
+   path three times, which has been corrected.
+
+ * Remove "git env--helper" and demote it to a test-tool subcommand.
+   (merge 4a1baacd46 ab/test-env-helper later to maint).
+
+ * Newer regex library macOS stopped enabling GNU-like enhanced BRE,
+   where '\(A\|B\)' works as alternation, unless explicitly asked with
+   the REG_ENHANCED flag.  "git grep" now can be compiled to do so, to
+   retain the old behaviour.
+
+ * Pthread emulation on Win32 leaked thread handle when a thread is
+   joined.
+   (merge 238a9dfe86 sk/win32-close-handle-upon-pthread-join later to maint).
+
+ * "git send-email -v 3" used to be expanded to "git send-email
+   --validate 3" when the user meant to pass them down to
+   "format-patch", which has been corrected.
+   (merge 8774aa56ad km/send-email-with-v-reroll-count later to maint).
+
+ * Document that "branch -f <branch>" disables only the safety to
+   avoid recreating an existing branch.
+
+ * "git fetch <group>", when "<group>" of remotes lists the same
+   remote twice, unnecessarily failed when parallel fetching was
+   enabled, which has been corrected.
+   (merge 06a668cb90 cw/fetch-remote-group-with-duplication later to maint).
+
+ * Clarify how "checkout -b/-B" and "git branch [-f]" are similar but
+   different in the documentation.
+
+ * "git hash-object" now checks that the resulting object is well
+   formed with the same code as "git fsck".
+   (merge 8e4309038f jk/hash-object-fsck later to maint).
+
+ * Improve the error message given when private key is not loaded in
+   the ssh agent in the codepath to sign with an ssh key.
+   (merge dce7b31126 as/ssh-signing-improve-key-missing-error later to maint).
+
+ * Adjust "git request-pull" to strip embedded signature from signed
+   tags to notice non-PGP signatures.
+   (merge a9cad02538 gm/request-pull-with-non-pgp-signed-tags later to maint).
+
+ * Remove support for MSys, which now lags way behind MSys2.
+   (merge 2987407f3c hj/remove-msys-support later to maint).
+
+ * Fix use of CreateThread() API call made early in the windows
+   start-up code.
+   (merge 592bcab61b sk/winansi-createthread-fix later to maint).
+
+ * "git pack-objects" learned to release delta-island bitmap data when
+   it is done using it, saving peak heap memory usage.
+   (merge 647982bb71 ew/free-island-marks later to maint).
+
+ * In an environment where dynamically generated code is prohibited to
+   run (e.g. SELinux), failure to JIT pcre patterns is expected.  Fall
+   back to interpreted execution in such a case.
+   (merge 50b6ad55b0 cb/grep-fallback-failing-jit later to maint).
+
+ * "git name-rev" heuristics update.
+   (merge b2182a8730 en/name-rev-make-taggerdate-much-less-important later to maint).
+
+ * Remove more remaining uses of macros that relies on the_index
+   singleton instance without explicitly spelling it out.
+
+ * Remove unnecessary explicit sizing of strbuf.
+   (merge 93ea118bed rs/cache-tree-strbuf-growth-fix later to maint).
+
+ * Doc update.
+   (merge d9ec3b0dc0 jk/doc-ls-remote-matching later to maint).
+
+ * Error messages given upon a signature verification failure used to
+   discard the errors from underlying gpg program, which has been
+   corrected.
+   (merge ad6b320756 js/gpg-errors later to maint).
+
+ * Update --date=default documentation.
+   (merge 9deef088ae rd/doc-default-date-format later to maint).
+
+ * A test helper had a single write(2) of 256kB, which was too big for
+   some platforms (e.g. NonStop), which has been corrected by using
+   xwrite() wrapper appropriately.
+   (merge 58eab6ff13 jc/genzeros-avoid-raw-write later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+   (merge 4eb1ccecd4 dh/mingw-ownership-check-typofix later to maint).
+   (merge f95526419b ar/typofix-gitattributes-doc later to maint).
+   (merge 27875aeec9 km/doc-branch-start-point later to maint).
+   (merge 35c194dc57 es/t1509-root-fixes later to maint).
+   (merge 7b341645e3 pw/ci-print-failure-name-fix later to maint).
+   (merge bcb71d45bf jx/t1301-updates later to maint).
+   (merge ebdc46c242 jc/doc-diff-patch.txt later to maint).
+   (merge a87a20cbb4 ar/test-cleanup later to maint).
+   (merge f5156f1885 ar/bisect-doc-update later to maint).
+   (merge fca2d86c97 jk/interop-error later to maint).
+   (merge cf4936ed74 tl/ls-tree-code-clean-up later to maint).
+   (merge dcb47e52b0 en/t6426-todo-cleanup later to maint).
+   (merge 5b8db44bdd jc/format-patch-v-unleak later to maint).
+   (merge 590b636737 jk/hash-object-literally-fd-leak later to maint).
+   (merge 5458ba0a4d tb/t0003-invoke-dd-more-portably later to maint).
+   (merge 70661d288b ar/markup-em-dash later to maint).
+   (merge e750951e74 en/ls-files-doc-update later to maint).
+   (merge 4f542975d1 mh/doc-credential-cache-only-in-core later to maint).
+   (merge 3a2ebaebc7 gc/index-format-doc later to maint).
+   (merge b08edf709d jk/httpd-test-updates later to maint).
+   (merge d85e9448dd wl/new-command-doc later to maint).
+   (merge d912a603ed kf/t5000-modernise later to maint).
+   (merge e65b868d07 rs/size-t-fixes later to maint).
+   (merge 3eb1e1ca9a ab/config-h-remove-unused later to maint).
+   (merge d390e08076 cw/doc-pushurl-vs-url later to maint).
+   (merge 567342fc77 rs/ctype-test later to maint).
index 3e859f34197bc43c8d266ec04d569e5353dae27e..e0354ceaed9e870abce7b59468ed08439267b17a 100644 (file)
@@ -7,6 +7,7 @@ add.ignore-errors (deprecated)::
        variables.
 
 add.interactive.useBuiltin::
-       Set to `false` to fall back to the original Perl implementation of
-       the interactive version of linkgit:git-add[1] instead of the built-in
-       version. Is `true` by default.
+       Unused configuration variable. Used in Git versions v2.25.0 to
+       v2.36.0 to enable the built-in version of linkgit:git-add[1]'s
+       interactive mode, which then became the default in Git
+       versions v2.37.0 to v2.39.0.
index daa21eb674ae32826ec5074a19092bd009978bcf..3faae3868534e24b396d76b594484d484c1eb78b 100644 (file)
@@ -15,6 +15,13 @@ bundle.mode::
        complete understanding of the bundled information (`all`) or if any one
        of the listed bundle URIs is sufficient (`any`).
 
+bundle.heuristic::
+       If this string-valued key exists, then the bundle list is designed to
+       work well with incremental `git fetch` commands. The heuristic signals
+       that there are additional keys available for each bundle that help
+       determine which subset of bundles the client should download. The
+       only value currently understood is `creationToken`.
+
 bundle.<id>.*::
        The `bundle.<id>.*` keys are used to describe a single item in the
        bundle list, grouped under `<id>` for identification purposes.
index 95975e50912c6f423820a5440d6696ac5326dc51..e52bc6b858470c60f09f52d4f9d2b9f17135483c 100644 (file)
@@ -23,6 +23,11 @@ feature.manyFiles::
        working directory. With many files, commands such as `git status` and
        `git checkout` may be slow and these new defaults improve performance:
 +
+* `index.skipHash=true` speeds up index writes by not computing a trailing
+  checksum. Note that this will cause Git versions earlier than 2.13.0 to
+  refuse to parse the index and Git versions earlier than 2.40.0 will report
+  a corrupted index during `git fsck`.
++
 * `index.version=4` enables path-prefix compression in the index.
 +
 * `core.untrackedCache=true` enables the untracked cache. This setting assumes
index cd65d236b43ffc3a7c08fa9df480fcbe6c82e68c..568f0f75b3027d374a53857c06cf481ff376c6b7 100644 (file)
@@ -96,3 +96,27 @@ fetch.writeCommitGraph::
        merge and the write may take longer. Having an updated commit-graph
        file helps performance of many Git commands, including `git merge-base`,
        `git push -f`, and `git log --graph`. Defaults to false.
+
+fetch.bundleURI::
+       This value stores a URI for downloading Git object data from a bundle
+       URI before performing an incremental fetch from the origin Git server.
+       This is similar to how the `--bundle-uri` option behaves in
+       linkgit:git-clone[1]. `git clone --bundle-uri` will set the
+       `fetch.bundleURI` value if the supplied bundle URI contains a bundle
+       list that is organized for incremental fetches.
++
+If you modify this value and your repository has a `fetch.bundleCreationToken`
+value, then remove that `fetch.bundleCreationToken` value before fetching from
+the new bundle URI.
+
+fetch.bundleCreationToken::
+       When using `fetch.bundleURI` to fetch incrementally from a bundle
+       list that uses the "creationToken" heuristic, this config value
+       stores the maximum `creationToken` value of the downloaded bundles.
+       This value is used to prevent downloading bundles in the future
+       if the advertised `creationToken` is not strictly larger than this
+       value.
++
+The creation token values are chosen by the provider serving the specific
+bundle URI. If you modify the URI at `fetch.bundleURI`, then be sure to
+remove the value for the `fetch.bundleCreationToken` value before fetching.
index c7303d8d9f004b5e413c4e5fe0dbb2f0df0c9171..3bd78269e29a544fa13369f9e5169b403be294ac 100644 (file)
@@ -139,3 +139,7 @@ For example,
 ------------
 +
 will only show notes from `refs/notes/bar`.
+
+format.mboxrd::
+       A boolean value which enables the robust "mboxrd" format when
+       `--stdout` is in use to escape "^>+From " lines.
index 75f3a2d10541460a912946bbbf9987aa18275a02..23c7985eb40974e2557c8bd3d271073c0b49bc58 100644 (file)
@@ -30,3 +30,14 @@ index.version::
        Specify the version with which new index files should be
        initialized.  This does not affect existing repositories.
        If `feature.manyFiles` is enabled, then the default is 4.
+
+index.skipHash::
+       When enabled, do not compute the trailing hash for the index file.
+       This accelerates Git commands that manipulate the index, such as
+       `git add`, `git commit`, or `git status`. Instead of storing the
+       checksum, write a trailing set of bytes with value zero, indicating
+       that the computation was skipped.
++
+If you enable `index.skipHash`, then Git clients older than 2.13.0 will
+refuse to parse the index and Git clients older than 2.40.0 will report an
+error during `git fsck`.
index 264812cca4db9a0a7b70be3eed7703d3439c9f46..c3ac767d1e4de152865ec82de6a995a9b4934834 100644 (file)
@@ -115,3 +115,9 @@ transfer.unpackLimit::
 transfer.advertiseSID::
        Boolean. When true, client and server processes will advertise their
        unique session IDs to their remote counterpart. Defaults to false.
+
+transfer.bundleURI::
+       When `true`, local `git clone` commands will request bundle
+       information from the remote server (if advertised) and download
+       bundles before continuing the clone through the Git protocol.
+       Defaults to `false`.
index c78063d4f74a30dc58c98a7af5db7fcdeeab69f3..546adf79e5a52cf100c71154bb9710cc5c68aaf6 100644 (file)
@@ -1,3 +1,4 @@
+[[generate_patch_text_with_p]]
 Generating patch text with -p
 -----------------------------
 
index 3674ac48e92c29e2a9fe5db0ccf8ce72e7727290..7d73e976d9936bb35981ad7f64b0af66865491b8 100644 (file)
@@ -22,7 +22,13 @@ ifndef::git-format-patch[]
 -p::
 -u::
 --patch::
-       Generate patch (see section on generating patches).
+       Generate patch (see section titled
+ifdef::git-log[]
+<<generate_patch_text_with_p, "Generating patch text with -p">>).
+endif::git-log[]
+ifndef::git-log[]
+"Generating patch text with -p").
+endif::git-log[]
 ifdef::git-diff[]
        This is the default.
 endif::git-diff[]
index a030d33c6e704adaf0b12168cb7ef0d062a1ef19..ed44c1cb31ca6eaf7e63d7920cbc15c3a2e0619f 100644 (file)
@@ -274,7 +274,7 @@ status::
 ------------
               staged     unstaged path
      1:       binary      nothing foo.png
-     2:     +403/-35        +1/-1 git-add--interactive.perl
+     2:     +403/-35        +1/-1 add-interactive.c
 ------------
 +
 It shows that foo.png has differences from HEAD (but that is
@@ -282,7 +282,7 @@ binary so line count cannot be shown) and there is no
 difference between indexed copy and the working tree
 version (if the working tree version were also different,
 'binary' would have been shown in place of 'nothing').  The
-other file, git-add{litdd}interactive.perl, has 403 lines added
+other file, add-interactive.c, has 403 lines added
 and 35 lines deleted if you commit what is in the index, but
 working tree file has further modifications (one addition and
 one deletion).
@@ -303,7 +303,7 @@ like this:
 ------------
            staged     unstaged path
   1:       binary      nothing foo.png
-* 2:     +403/-35        +1/-1 git-add--interactive.perl
+* 2:     +403/-35        +1/-1 add-interactive.c
 ------------
 +
 To remove selection, prefix the input with `-`
index 326276e51ce5734675f842771d41290057c53f90..0c1dfb3c98b440f87e561a0d44c240a41d80dc14 100644 (file)
@@ -9,7 +9,7 @@ git-am - Apply a series of patches from a mailbox
 SYNOPSIS
 --------
 [verse]
-'git am' [--signoff] [--keep] [--[no-]keep-cr] [--[no-]utf8]
+'git am' [--signoff] [--keep] [--[no-]keep-cr] [--[no-]utf8] [--no-verify]
         [--[no-]3way] [--interactive] [--committer-date-is-author-date]
         [--ignore-date] [--ignore-space-change | --ignore-whitespace]
         [--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>]
@@ -138,6 +138,12 @@ include::rerere-options.txt[]
 --interactive::
        Run interactively.
 
+-n::
+--no-verify::
+       By default, the pre-applypatch and applypatch-msg hooks are run.
+       When any of `--no-verify` or `-n` is given, these are bypassed.
+       See also linkgit:githooks[5].
+
 --committer-date-is-author-date::
        By default the command records the date from the e-mail
        message as the commit author date, and uses the time of
index 1d478cbe9b5b873566fd9fc34e5d837bd0a4595c..5e16e6db7e2e0273dacbb76b5d7f4cd6ee0462ec 100644 (file)
@@ -208,7 +208,7 @@ behavior:
 * `warn` outputs warnings for a few such errors, but applies the
   patch as-is (default).
 * `fix` outputs warnings for a few such errors, and applies the
-  patch after fixing them (`strip` is a synonym --- the tool
+  patch after fixing them (`strip` is a synonym -- the tool
   used to consider only trailing whitespace characters as errors, and the
   fix involved 'stripping' them, but modern Gits do more).
 * `error` outputs warnings for a few such errors, and refuses
index 60c040988bb803cbca608b9e58647b75a75ac173..6bab201d37548dd5c4f911baf981f3fc1995a889 100644 (file)
@@ -86,6 +86,11 @@ cases, write an untracked file and use `--add-file` instead.
        Look for attributes in .gitattributes files in the working tree
        as well (see <<ATTRIBUTES>>).
 
+--mtime=<time>::
+       Set modification time of archive entries.  Without this option
+       the committer time is used if `<tree-ish>` is a commit or tag,
+       and the current time if it is a tree.
+
 <extra>::
        This can be any options that the archiver backend understands.
        See next section.
index f3d9566c898818bac6219ecf324e4dd3f188498d..0bc165788e77bd7dc21e1f9f5aec8d8dbc65a77e 100644 (file)
@@ -1347,8 +1347,8 @@ author to given a talk and for publishing this paper.
 References
 ----------
 
-- [[[1]]] https://www.nist.gov/sites/default/files/documents/director/planning/report02-3.pdf['The Economic Impacts of Inadequate Infratructure for Software Testing'.  Nist Planning Report 02-3], see Executive Summary and Chapter 8.
-- [[[2]]] http://www.oracle.com/technetwork/java/codeconvtoc-136057.html['Code Conventions for the Java Programming Language'. Sun Microsystems.]
+- [[[1]]] https://web.archive.org/web/20091206032101/http://www.nist.gov/public_affairs/releases/n02-10.htm['Software Errors Cost U.S. Economy $59.5 Billion Annually'. Nist News Release.] See also https://www.nist.gov/system/files/documents/director/planning/report02-3.pdf['The Economic Impacts of Inadequate Infratructure for Software Testing'.  Nist Planning Report 02-3], Executive Summary and Chapter 8.
+- [[[2]]] https://www.oracle.com/java/technologies/javase/codeconventions-introduction.html['Code Conventions for the Java Programming Language: 1. Introduction'. Sun Microsystems.]
 - [[[3]]] https://en.wikipedia.org/wiki/Software_maintenance['Software maintenance'. Wikipedia.]
 - [[[4]]] https://lore.kernel.org/git/7vps5xsbwp.fsf_-_@assigned-by-dhcp.cox.net/[Junio C Hamano. 'Automated bisect success story'.]
 - [[[5]]] https://lwn.net/Articles/317154/[Christian Couder. 'Fully automated bisecting with "git bisect run"'. LWN.net.]
index 12c5f84e3bef5c83c80edec9850315dd334beffa..d382ac69f77b1ce607d881f149a04f94aa930d82 100644 (file)
@@ -116,13 +116,17 @@ OPTIONS
 
 -f::
 --force::
-       Reset <branchname> to <startpoint>, even if <branchname> exists
+       Reset <branchname> to <start-point>, even if <branchname> exists
        already. Without `-f`, 'git branch' refuses to change an existing branch.
        In combination with `-d` (or `--delete`), allow deleting the
        branch irrespective of its merged status, or whether it even
        points to a valid commit. In combination with
        `-m` (or `--move`), allow renaming the branch even if the new
        branch name already exists, the same applies for `-c` (or `--copy`).
++
+Note that 'git branch -f <branchname> [<start-point>]', even with '-f',
+refuses to change an existing branch `<branchname>` that is checked out
+in another worktree linked to the same repository.
 
 -m::
 --move::
index ec30b5c5743fd64ea9d42f4f451dc6ce0627d80a..411de2e27ddc0db762ec9be6ceec18a7fc1bf069 100644 (file)
@@ -45,7 +45,9 @@ OPTIONS
 
 -s::
        Instead of the content, show the object size identified by
-       `<object>`.
+       `<object>`. If used with `--use-mailmap` option, will show
+       the size of updated object after replacing idents using the
+       mailmap mechanism.
 
 -e::
        Exit with zero status if `<object>` exists and is a valid
@@ -89,26 +91,54 @@ OPTIONS
 --batch::
 --batch=<format>::
        Print object information and contents for each object provided
-       on stdin.  May not be combined with any other options or arguments
-       except `--textconv` or `--filters`, in which case the input lines
-       also need to specify the path, separated by whitespace.  See the
-       section `BATCH OUTPUT` below for details.
+       on stdin. May not be combined with any other options or arguments
+       except `--textconv`, `--filters`, or `--use-mailmap`.
++
+--
+       * When used with `--textconv` or `--filters`, the input lines
+         must specify the path, separated by whitespace. See the section
+         `BATCH OUTPUT` below for details.
+
+       * When used with `--use-mailmap`, for commit and tag objects, the
+         contents part of the output shows the identities replaced using the
+         mailmap mechanism, while the information part of the output shows
+         the size of the object as if it actually recorded the replacement
+         identities.
+--
 
 --batch-check::
 --batch-check=<format>::
-       Print object information for each object provided on stdin.  May
-       not be combined with any other options or arguments except
-       `--textconv` or `--filters`, in which case the input lines also
-       need to specify the path, separated by whitespace.  See the
-       section `BATCH OUTPUT` below for details.
+       Print object information for each object provided on stdin. May not be
+       combined with any other options or arguments except `--textconv`, `--filters`
+       or `--use-mailmap`.
++
+--
+       * When used with `--textconv` or `--filters`, the input lines must
+        specify the path, separated by whitespace. See the section
+        `BATCH OUTPUT` below for details.
+
+       * When used with `--use-mailmap`, for commit and tag objects, the
+         printed object information shows the size of the object as if the
+         identities recorded in it were replaced by the mailmap mechanism.
+--
 
 --batch-command::
 --batch-command=<format>::
        Enter a command mode that reads commands and arguments from stdin. May
-       only be combined with `--buffer`, `--textconv` or `--filters`. In the
-       case of `--textconv` or `--filters`, the input lines also need to specify
-       the path, separated by whitespace. See the section `BATCH OUTPUT` below
-       for details.
+       only be combined with `--buffer`, `--textconv`, `--use-mailmap` or
+       `--filters`.
++
+--
+       * When used with `--textconv` or `--filters`, the input lines must
+         specify the path, separated by whitespace. See the section
+         `BATCH OUTPUT` below for details.
+
+       * When used with `--use-mailmap`, for commit and tag objects, the
+         `contents` command shows the identities replaced using the
+         mailmap mechanism, while the `info` command shows the size
+         of the object as if it actually recorded the replacement
+         identities.
+--
 +
 `--batch-command` recognizes the following commands:
 +
index 84f41a8e82590f54bca103a067a95348093844b5..6e4f3aaf34c9579004be4c3f8d644287913f1505 100644 (file)
@@ -9,8 +9,8 @@ git-check-attr - Display gitattributes information
 SYNOPSIS
 --------
 [verse]
-'git check-attr' [-a | --all | <attr>...] [--] <pathname>...
-'git check-attr' --stdin [-z] [-a | --all | <attr>...]
+'git check-attr' [--source <tree-ish>] [-a | --all | <attr>...] [--] <pathname>...
+'git check-attr' --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]
 
 DESCRIPTION
 -----------
@@ -36,6 +36,11 @@ OPTIONS
        If `--stdin` is also given, input paths are separated
        with a NUL character instead of a linefeed character.
 
+--source=<tree-ish>::
+       Check attributes against the specified tree-ish. It is common to
+       specify the source tree by naming a commit, branch or tag associated
+       with it.
+
 \--::
        Interpret all preceding arguments as attributes and all following
        arguments as path names.
index 4cb9d555b4b436ae6aef8e14c1729599da0d74e8..6bb32ab4602aa9b55c935a0d3bb64d56682c7bed 100644 (file)
@@ -146,14 +146,16 @@ on your side branch as `theirs` (i.e. "one contributor's work on top
 of it").
 
 -b <new-branch>::
-       Create a new branch named `<new-branch>` and start it at
-       `<start-point>`; see linkgit:git-branch[1] for details.
+       Create a new branch named `<new-branch>`, start it at
+       `<start-point>`, and check the resulting branch out;
+       see linkgit:git-branch[1] for details.
 
 -B <new-branch>::
-       Creates the branch `<new-branch>` and start it at `<start-point>`;
-       if it already exists, then reset it to `<start-point>`. This is
-       equivalent to running "git branch" with "-f"; see
-       linkgit:git-branch[1] for details.
+       Creates the branch `<new-branch>`, start it at `<start-point>`;
+       if it already exists, then reset it to `<start-point>`. And then
+       check the resulting branch out.  This is equivalent to running
+       "git branch" with "-f" followed by "git checkout" of that branch;
+       see linkgit:git-branch[1] for details.
 
 -t::
 --track[=(direct|inherit)]::
@@ -477,9 +479,9 @@ before that happens. If we have not yet moved away from commit `f`,
 any of these will create a reference to it:
 
 ------------
-$ git checkout -b foo   <1>
-$ git branch foo        <2>
-$ git tag foo           <3>
+$ git checkout -b foo  # or "git switch -c foo"  <1>
+$ git branch foo                                 <2>
+$ git tag foo                                    <3>
 ------------
 
 <1> creates a new branch `foo`, which refers to commit `f`, and then
index 1e8ac9df60274067dcf57d3f9a82ba6f270ea7d9..fdcad3d2006c8ad59333f38a3161e2060e75a8ee 100644 (file)
@@ -219,7 +219,7 @@ again, this time exercising more care about matching up context lines.
 ------------
 $ git cherry-pick topic^             <1>
 $ git diff                           <2>
-$ git reset --merge ORIG_HEAD        <3>
+$ git cherry-pick --abort            <3>
 $ git cherry-pick -Xpatience topic^  <4>
 ------------
 <1> apply the change that would be shown by `git show topic^`.
index 432e159d95246ccb9e716ad1554c8b6047db1870..f473994a864e9575d57844e1640e8dfb4eea00dd 100644 (file)
@@ -14,10 +14,13 @@ git config credential.helper 'cache [<options>]'
 DESCRIPTION
 -----------
 
-This command caches credentials in memory for use by future Git
-programs. The stored credentials never touch the disk, and are forgotten
-after a configurable timeout.  The cache is accessible over a Unix
-domain socket, restricted to the current user by filesystem permissions.
+This command caches credentials for use by future Git programs.
+The stored credentials are kept in memory of the cache-daemon
+process (instead of written to a file) and are forgotten after a
+configurable timeout. Credentials are forgotten sooner if the
+cache-daemon dies, for example if the system restarts. The cache
+is accessible over a Unix domain socket, restricted to the current
+user by filesystem permissions.
 
 You probably don't want to invoke this command directly; it is meant to
 be used as a credential helper by other parts of Git. See
index ac2818b9f66778d02bce6f344bdc4ef88a2bd263..29d184ab82420c3177b11df875e38efa016a01d9 100644 (file)
@@ -144,6 +144,12 @@ Git understands the following attributes:
 
        The credential's password, if we are asking it to be stored.
 
+`password_expiry_utc`::
+
+       Generated passwords such as an OAuth access token may have an expiry date.
+       When reading credentials from helpers, `git credential fill` ignores expired
+       passwords. Represented as Unix time UTC, seconds since 1970.
+
 `url`::
 
        When this special attribute is read by `git credential`, the
index 63d9569e16444237a7583b8c9dafe232eb1d2e42..fba66f14607a5f3f70e87e3fa1d04af90015a277 100644 (file)
@@ -251,10 +251,10 @@ EXAMPLES
 $ git fetch origin
 ------------------------------------------------
 +
-The above command copies all branches from the remote refs/heads/
-namespace and stores them to the local refs/remotes/origin/ namespace,
-unless the branch.<name>.fetch option is used to specify a non-default
-refspec.
+The above command copies all branches from the remote `refs/heads/`
+namespace and stores them to the local `refs/remotes/origin/` namespace,
+unless the `remote.<repository>.fetch` option is used to specify a
+non-default refspec.
 
 * Using refspecs explicitly:
 +
index 77c3a8ad909def201ea48034fe88eb88cf2a3a97..3407f3c2c078264505d28455c93c6ec5424234b1 100644 (file)
@@ -8,7 +8,7 @@ git-hook - Run git hooks
 SYNOPSIS
 --------
 [verse]
-'git hook' run [--ignore-missing] <hook-name> [-- <hook-args>]
+'git hook' run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>]
 
 DESCRIPTION
 -----------
@@ -31,6 +31,11 @@ linkgit:githooks[5] for arguments hooks might expect (if any).
 OPTIONS
 -------
 
+--to-stdin::
+       For "run"; Specify a file which will be streamed into the
+       hook's stdin. The hook will receive the entire file from
+       beginning to EOF.
+
 --ignore-missing::
        Ignore any missing hook by quietly returning zero. Used for
        tools that want to do a blind one-shot run of a hook that may
index 440043cdb8e4da5908c4378b2d90e8da3b14c956..1abdd3c21c513c3c9f547e25c83d212ba1866b2b 100644 (file)
@@ -12,6 +12,7 @@ SYNOPSIS
 'git ls-files' [-z] [-t] [-v] [-f]
                [-c|--cached] [-d|--deleted] [-o|--others] [-i|--ignored]
                [-s|--stage] [-u|--unmerged] [-k|--killed] [-m|--modified]
+               [--resolve-undo]
                [--directory [--no-empty-directory]] [--eol]
                [--deduplicate]
                [-x <pattern>|--exclude=<pattern>]
@@ -28,21 +29,26 @@ This merges the file listing in the index with the actual working
 directory list, and shows different combinations of the two.
 
 One or more of the options below may be used to determine the files
-shown:
+shown, and each file may be printed multiple times if there are
+multiple entries in the index or multiple statuses are applicable for
+the relevant file selection options.
 
 OPTIONS
 -------
 -c::
 --cached::
-       Show cached files in the output (default)
+       Show all files cached in Git's index, i.e. all tracked files.
+       (This is the default if no -c/-s/-d/-o/-u/-k/-m/--resolve-undo
+       options are specified.)
 
 -d::
 --deleted::
-       Show deleted files in the output
+       Show files with an unstaged deletion
 
 -m::
 --modified::
-       Show modified files in the output
+       Show files with an unstaged modification (note that an unstaged
+       deletion also counts as an unstaged modification)
 
 -o::
 --others::
@@ -50,11 +56,14 @@ OPTIONS
 
 -i::
 --ignored::
-       Show only ignored files in the output. When showing files in the
-       index, print only those matched by an exclude pattern. When
-       showing "other" files, show only those matched by an exclude
-       pattern. Standard ignore rules are not automatically activated,
-       therefore at least one of the `--exclude*` options is required.
+       Show only ignored files in the output.  Must be used with
+       either an explicit '-c' or '-o'.  When showing files in the
+       index (i.e. when used with '-c'), print only those files
+       matching an exclude pattern.  When showing "other" files
+       (i.e. when used with '-o'), show only those matched by an
+       exclude pattern.  Standard ignore rules are not automatically
+       activated, therefore at least one of the `--exclude*` options
+       is required.
 
 -s::
 --stage::
@@ -63,19 +72,29 @@ OPTIONS
 --directory::
        If a whole directory is classified as "other", show just its
        name (with a trailing slash) and not its whole contents.
+       Has no effect without -o/--others.
 
 --no-empty-directory::
        Do not list empty directories. Has no effect without --directory.
 
 -u::
 --unmerged::
-       Show unmerged files in the output (forces --stage)
+       Show information about unmerged files in the output, but do
+       not show any other tracked files (forces --stage, overrides
+       --cached).
 
 -k::
 --killed::
-       Show files on the filesystem that need to be removed due
-       to file/directory conflicts for checkout-index to
-       succeed.
+       Show untracked files on the filesystem that need to be removed
+       due to file/directory conflicts for tracked files to be able to
+       be written to the filesystem.
+
+--resolve-undo::
+       Show files having resolve-undo information in the index
+       together with their resolve-undo information.  (resolve-undo
+       information is what is used to implement "git checkout -m
+       $PATH", i.e. to recreate merge conflicts that were
+       accidentally resolved)
 
 -z::
        \0 line termination on output and do not quote filenames.
@@ -100,7 +119,8 @@ OPTIONS
 
 --exclude-per-directory=<file>::
        Read additional exclude patterns that apply only to the
-       directory and its subdirectories in <file>.
+       directory and its subdirectories in <file>.  Deprecated; use
+       --exclude-standard instead.
 
 --exclude-standard::
        Add the standard Git exclusions: .git/info/exclude, .gitignore
@@ -118,24 +138,27 @@ OPTIONS
        with `-s` or `-u` options does not make any sense.
 
 -t::
-       This feature is semi-deprecated. For scripting purpose,
-       linkgit:git-status[1] `--porcelain` and
+       Show status tags together with filenames.  Note that for
+       scripting purposes, linkgit:git-status[1] `--porcelain` and
        linkgit:git-diff-files[1] `--name-status` are almost always
        superior alternatives, and users should look at
        linkgit:git-status[1] `--short` or linkgit:git-diff[1]
        `--name-status` for more user-friendly alternatives.
 +
 --
-This option identifies the file status with the following tags (followed by
-a space) at the start of each line:
-
-       H::     cached
-       S::     skip-worktree
-       M::     unmerged
-       R::     removed/deleted
-       C::     modified/changed
-       K::     to be killed
-       ?::     other
+This option provides a reason for showing each filename, in the form
+of a status tag (which is followed by a space and then the filename).
+The status tags are all single characters from the following list:
+
+       H::     tracked file that is not either unmerged or skip-worktree
+       S::     tracked file that is skip-worktree
+       M::     tracked file that is unmerged
+       R::     tracked file with unstaged removal/deletion
+       C::     tracked file with unstaged modification/change
+       K::     untracked paths which are part of file/directory conflicts
+               which prevent checking out tracked files
+       ?::     untracked file
+       U::     file with resolve-undo information
 --
 
 -v::
@@ -269,7 +292,9 @@ traversing the directory tree and finding files to show when the
 flags --others or --ignored are specified.  linkgit:gitignore[5]
 specifies the format of exclude patterns.
 
-These exclude patterns come from these places, in order:
+Generally, you should just use --exclude-standard, but for historical
+reasons the exclude patterns can be specified from the following
+places, in order:
 
   1. The command-line flag --exclude=<pattern> specifies a
      single pattern.  Patterns are ordered in the same order
index 492e573856f24f6444fefb842e8ee733fa95be25..ff3da547ddb98a5493b5752a4b2b84bed7f799c5 100644 (file)
@@ -11,7 +11,7 @@ SYNOPSIS
 [verse]
 'git ls-remote' [--heads] [--tags] [--refs] [--upload-pack=<exec>]
              [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]
-             [--symref] [<repository> [<refs>...]]
+             [--symref] [<repository> [<patterns>...]]
 
 DESCRIPTION
 -----------
@@ -85,25 +85,32 @@ OPTIONS
        either a URL or the name of a remote (see the GIT URLS and
        REMOTES sections of linkgit:git-fetch[1]).
 
-<refs>...::
+<patterns>...::
        When unspecified, all references, after filtering done
-       with --heads and --tags, are shown.  When <refs>... are
-       specified, only references matching the given patterns
-       are displayed.
+       with --heads and --tags, are shown.  When <patterns>... are
+       specified, only references matching one or more of the given
+       patterns are displayed. Each pattern is interpreted as a glob
+       (see `glob` in linkgit:gitglossary[7]) which is matched against
+       the "tail" of a ref, starting either from the start of the ref
+       (so a full name like `refs/heads/foo` matches) or from a slash
+       separator (so `bar` matches `refs/heads/bar` but not
+       `refs/heads/foobar`).
 
 EXAMPLES
 --------
 
 ----
-$ git ls-remote --tags ./.
+$ git ls-remote --tags .
 d6602ec5194c87b0fc87103ca4d67251c76f233a       refs/tags/v0.99
 f25a265a342aed6041ab0cc484224d9ca54b6f41       refs/tags/v0.99.1
 7ceca275d047c90c0c7d5afb13ab97efdf51bd6e       refs/tags/v0.99.3
 c5db5456ae3b0873fc659c19fafdde22313cc441       refs/tags/v0.99.2
 0918385dbd9656cab0d1d81ba7453d49bbc16250       refs/tags/junio-gpg-pub
+
 $ git ls-remote http://www.kernel.org/pub/scm/git/git.git master seen rc
 5fe978a5381f1fbad26a80e682ddd2a401966740       refs/heads/master
 c781a84b5204fb294c9ccc79f8b3baceeb32c061       refs/heads/seen
+
 $ git remote add korg http://www.kernel.org/pub/scm/git/git.git
 $ git ls-remote --tags korg v\*
 d6602ec5194c87b0fc87103ca4d67251c76f233a       refs/tags/v0.99
index 04bcc416e6e2966b8b83d911cac61307e0f4feaf..88ee942101a7748ac2a7b5030b364ed7e27d5fdb 100644 (file)
@@ -64,6 +64,11 @@ OPTIONS
        share no common history.  This flag can be given to override that
        check and make the merge proceed anyway.
 
+--merge-base=<commit>::
+       Instead of finding the merge-bases for <branch1> and <branch2>,
+       specify a merge-base for the merge, and specifying multiple bases is
+       currently not supported. This option is incompatible with `--stdin`.
+
 [[OUTPUT]]
 OUTPUT
 ------
@@ -216,6 +221,17 @@ with linkgit:git-merge[1]:
   * any messages that would have been printed to stdout (the
     <<IM,Informational messages>>)
 
+INPUT FORMAT
+------------
+'git merge-tree --stdin' input format is fully text based. Each line
+has this format:
+
+       [<base-commit> -- ]<branch1> <branch2>
+
+If one line is separated by `--`, the string before the separator is
+used for specifying a merge-base for the merge and the string after
+the separator describes the branches to be merged.
+
 MISTAKES TO AVOID
 -----------------
 
index 2d6a1391c89412e66aa1227201bdd89ba2f0f27a..0aeff572a59d8fea319f334232deae4a9df4e464 100644 (file)
@@ -37,7 +37,8 @@ Then "`git merge topic`" will replay the changes made on the
 `topic` branch since it diverged from `master` (i.e., `E`) until
 its current commit (`C`) on top of `master`, and record the result
 in a new commit along with the names of the two parent commits and
-a log message from the user describing the changes.
+a log message from the user describing the changes. Before the operation,
+`ORIG_HEAD` is set to the tip of the current branch (`C`).
 
 ------------
          A---B---C topic
index 7567955bad8552800b92ade66007c442dc000715..b09707474df0ec523ac7e53192103a6edf0dca51 100644 (file)
@@ -219,7 +219,7 @@ see which of the "local changes" that you made were carried forward by running
 `git diff-index --cached $M`.  Note that this does not
 necessarily match what `git diff-index --cached $H` would have
 produced before such a two tree merge.  This is because of cases
-18 and 19 --- if you already had the changes in $M (e.g. maybe
+18 and 19 -- if you already had the changes in $M (e.g. maybe
 you picked it up via e-mail in a patch form), `git diff-index
 --cached $H` would have told you about the change before this
 merge, but it would not show in `git diff-index --cached $M`
index f9675bd24e6e82a6eba44696dad08e9304169b15..9a295bcee45f4f817c5b319789a32beb22e5f07c 100644 (file)
@@ -38,6 +38,13 @@ The current branch is reset to `<upstream>` or `<newbase>` if the
 `git reset --hard <upstream>` (or `<newbase>`). `ORIG_HEAD` is set
 to point at the tip of the branch before the reset.
 
+[NOTE]
+`ORIG_HEAD` is not guaranteed to still point to the previous branch tip
+at the end of the rebase if other commands that write that pseudo-ref
+(e.g. `git reset`) are used during the rebase. The previous branch tip,
+however, is accessible using the reflog of the current branch
+(i.e. `@{1}`, see linkgit:gitrevisions[7]).
+
 The commits that were previously saved into the temporary area are
 then reapplied to the current branch, one by one, in order. Note that
 any commits in `HEAD` which introduce the same textual changes as a commit
@@ -201,6 +208,39 @@ Alternatively, you can undo the 'git rebase' with
 
     git rebase --abort
 
+MODE OPTIONS
+------------
+
+The options in this section cannot be used with any other option,
+including not with each other:
+
+--continue::
+       Restart the rebasing process after having resolved a merge conflict.
+
+--skip::
+       Restart the rebasing process by skipping the current patch.
+
+--abort::
+       Abort the rebase operation and reset HEAD to the original
+       branch. If `<branch>` was provided when the rebase operation was
+       started, then `HEAD` will be reset to `<branch>`. Otherwise `HEAD`
+       will be reset to where it was when the rebase operation was
+       started.
+
+--quit::
+       Abort the rebase operation but `HEAD` is not reset back to the
+       original branch. The index and working tree are also left
+       unchanged as a result. If a temporary stash entry was created
+       using `--autostash`, it will be saved to the stash list.
+
+--edit-todo::
+       Edit the todo list during an interactive rebase.
+
+--show-current-patch::
+       Show the current patch in an interactive rebase or when rebase
+       is stopped because of conflicts. This is the equivalent of
+       `git show REBASE_HEAD`.
+
 OPTIONS
 -------
 --onto <newbase>::
@@ -242,22 +282,6 @@ See also INCOMPATIBLE OPTIONS below.
 <branch>::
        Working branch; defaults to `HEAD`.
 
---continue::
-       Restart the rebasing process after having resolved a merge conflict.
-
---abort::
-       Abort the rebase operation and reset HEAD to the original
-       branch. If `<branch>` was provided when the rebase operation was
-       started, then `HEAD` will be reset to `<branch>`. Otherwise `HEAD`
-       will be reset to where it was when the rebase operation was
-       started.
-
---quit::
-       Abort the rebase operation but `HEAD` is not reset back to the
-       original branch. The index and working tree are also left
-       unchanged as a result. If a temporary stash entry was created
-       using `--autostash`, it will be saved to the stash list.
-
 --apply::
        Use applying strategies to rebase (calling `git-am`
        internally).  This option may become a no-op in the future
@@ -314,7 +338,6 @@ See also INCOMPATIBLE OPTIONS below.
        upstream changes, the behavior towards them is controlled by
        the `--empty` flag.)
 +
-
 In the absence of `--keep-base` (or if `--no-reapply-cherry-picks` is
 given), these commits will be automatically dropped.  Because this
 necessitates reading all upstream commits, this can be expensive in
@@ -323,7 +346,6 @@ read. When using the 'merge' backend, warnings will be issued for each
 dropped commit (unless `--quiet` is given). Advice will also be issued
 unless `advice.skippedCherryPicks` is set to false (see
 linkgit:git-config[1]).
-
 +
 `--reapply-cherry-picks` allows rebase to forgo reading all upstream
 commits, potentially improving performance.
@@ -338,17 +360,6 @@ See also INCOMPATIBLE OPTIONS below.
 +
 See also INCOMPATIBLE OPTIONS below.
 
---skip::
-       Restart the rebasing process by skipping the current patch.
-
---edit-todo::
-       Edit the todo list during an interactive rebase.
-
---show-current-patch::
-       Show the current patch in an interactive rebase or when rebase
-       is stopped because of conflicts. This is the equivalent of
-       `git show REBASE_HEAD`.
-
 -m::
 --merge::
        Using merging strategies to rebase (default).
@@ -567,10 +578,7 @@ See also INCOMPATIBLE OPTIONS below.
 --root::
        Rebase all commits reachable from `<branch>`, instead of
        limiting them with an `<upstream>`.  This allows you to rebase
-       the root commit(s) on a branch.  When used with `--onto`, it
-       will skip changes already contained in `<newbase>` (instead of
-       `<upstream>`) whereas without `--onto` it will operate on every
-       change.
+       the root commit(s) on a branch.
 +
 See also INCOMPATIBLE OPTIONS below.
 
@@ -623,6 +631,8 @@ start would be overridden by the presence of
 +
 If the configuration variable `rebase.updateRefs` is set, then this option
 can be used to override and disable this setting.
++
+See also INCOMPATIBLE OPTIONS below.
 
 INCOMPATIBLE OPTIONS
 --------------------
@@ -638,17 +648,15 @@ are incompatible with the following options:
  * --merge
  * --strategy
  * --strategy-option
- * --allow-empty-message
- * --[no-]autosquash
+ * --autosquash
  * --rebase-merges
  * --interactive
  * --exec
  * --no-keep-empty
  * --empty=
- * --reapply-cherry-picks
- * --edit-todo
+ * --[no-]reapply-cherry-picks when used without --keep-base
  * --update-refs
- * --root when used in combination with --onto
+ * --root when used without --onto
 
 In addition, the following pairs of options are incompatible:
 
index 01cb4c9b9c56a7685f3a8d73fd91cadf5ce7a9e6..79ad5643eedb82ce0d884c0695e1bba8440f8985 100644 (file)
@@ -49,7 +49,8 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
 'git reset' [<mode>] [<commit>]::
        This form resets the current branch head to `<commit>` and
        possibly updates the index (resetting it to the tree of `<commit>`) and
-       the working tree depending on `<mode>`. If `<mode>` is omitted,
+       the working tree depending on `<mode>`. Before the operation, `ORIG_HEAD`
+       is set to the tip of the current branch. If `<mode>` is omitted,
        defaults to `--mixed`. The `<mode>` must be one of the following:
 +
 --
index 5e438a7fdc1ca4e8684c52c3773a6f722b26c287..a051b1e8f383abc618397ea7e2ac2f173d30a111 100644 (file)
@@ -457,6 +457,66 @@ during the write may conflict with other simultaneous processes, causing
 them to fail. Scripts running `status` in the background should consider
 using `git --no-optional-locks status` (see linkgit:git[1] for details).
 
+UNTRACKED FILES AND PERFORMANCE
+-------------------------------
+
+`git status` can be very slow in large worktrees if/when it
+needs to search for untracked files and directories. There are
+many configuration options available to speed this up by either
+avoiding the work or making use of cached results from previous
+Git commands. There is no single optimum set of settings right
+for everyone. We'll list a summary of the relevant options to help
+you, but before going into the list, you may want to run `git status`
+again, because your configuration may already be caching `git status`
+results, so it could be faster on subsequent runs.
+
+* The `--untracked-files=no` flag or the
+       `status.showUntrackedfiles=false` config (see above for both):
+       indicate that `git status` should not report untracked
+       files. This is the fastest option. `git status` will not list
+       the untracked files, so you need to be careful to remember if
+       you create any new files and manually `git add` them.
+
+* `advice.statusUoption=false` (see linkgit:git-config[1]):
+       setting this variable to `false` disables the warning message
+       given when enumerating untracked files takes more than 2
+       seconds.  In a large project, it may take longer and the user
+       may have already accepted the trade off (e.g. using "-uno" may
+       not be an acceptable option for the user), in which case, there
+       is no point issuing the warning message, and in such a case,
+       disabling the warning may be the best.
+
+* `core.untrackedCache=true` (see linkgit:git-update-index[1]):
+       enable the untracked cache feature and only search directories
+       that have been modified since the previous `git status` command.
+       Git remembers the set of untracked files within each directory
+       and assumes that if a directory has not been modified, then
+       the set of untracked files within has not changed.  This is much
+       faster than enumerating the contents of every directory, but still
+       not without cost, because Git still has to search for the set of
+       modified directories. The untracked cache is stored in the
+       `.git/index` file. The reduced cost of searching for untracked
+       files is offset slightly by the increased size of the index and
+       the cost of keeping it up-to-date. That reduced search time is
+       usually worth the additional size.
+
+* `core.untrackedCache=true` and `core.fsmonitor=true` or
+       `core.fsmonitor=<hook_command_pathname>` (see
+       linkgit:git-update-index[1]): enable both the untracked cache
+       and FSMonitor features and only search directories that have
+       been modified since the previous `git status` command.  This
+       is faster than using just the untracked cache alone because
+       Git can also avoid searching for modified directories.  Git
+       only has to enumerate the exact set of directories that have
+       changed recently. While the FSMonitor feature can be enabled
+       without the untracked cache, the benefits are greatly reduced
+       in that case.
+
+Note that after you turn on the untracked cache and/or FSMonitor
+features it may take a few `git status` commands for the various
+caches to warm up before you see improved command times.  This is
+normal.
+
 SEE ALSO
 --------
 linkgit:gitignore[5]
index 6aa521fab2383e5e0d4aaf39d03f695ea53940d3..f40202b8e3ab521ea22c78986d3bf5bb44a67f09 100644 (file)
@@ -13,7 +13,8 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Prints a Git logical variable.
+Prints a Git logical variable. Exits with code 1 if the variable has
+no value.
 
 OPTIONS
 -------
@@ -49,6 +50,14 @@ ifdef::git-default-editor[]
     The build you are using chose '{git-default-editor}' as the default.
 endif::git-default-editor[]
 
+GIT_SEQUENCE_EDITOR::
+    Text editor used to edit the 'todo' file while running `git rebase
+    -i`. Like `GIT_EDITOR`, the value is meant to be interpreted by
+    the shell when it is used. The order of preference is the
+    `$GIT_SEQUENCE_EDITOR` environment variable, then
+    `sequence.editor` configuration, and then the value of `git var
+    GIT_EDITOR`.
+
 GIT_PAGER::
     Text viewer for use by Git commands (e.g., 'less').  The value
     is meant to be interpreted by the shell.  The order of preference
index 1d33e083ab8ba104641001cd6e85b49505c3c374..74973d3cc4044d807ee2f98b0013f6811ae4f65b 100644 (file)
@@ -13,8 +13,7 @@ SYNOPSIS
     [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
     [-p|--paginate|-P|--no-pager] [--no-replace-objects] [--bare]
     [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
-    [--super-prefix=<path>] [--config-env=<name>=<envvar>]
-    <command> [<args>]
+    [--config-env=<name>=<envvar>] <command> [<args>]
 
 DESCRIPTION
 -----------
@@ -169,11 +168,6 @@ If you just want to run git as if it was started in `<path>` then use
        details.  Equivalent to setting the `GIT_NAMESPACE` environment
        variable.
 
---super-prefix=<path>::
-       Currently for internal use only.  Set a prefix which gives a path from
-       above a repository down to its root.  One use is to give submodules
-       context about the superproject that invoked it.
-
 --bare::
        Treat the repository as a bare repository.  If GIT_DIR
        environment is not set, it is set to the current working
@@ -619,7 +613,7 @@ The file parameters can point at the user's working file
 (e.g. `new-file` in "git-diff-files"), `/dev/null` (e.g. `old-file`
 when a new file is added), or a temporary file (e.g. `old-file` in the
 index).  `GIT_EXTERNAL_DIFF` should not worry about unlinking the
-temporary file --- it is removed when `GIT_EXTERNAL_DIFF` exits.
+temporary file -- it is removed when `GIT_EXTERNAL_DIFF` exits.
 +
 For a path that is unmerged, `GIT_EXTERNAL_DIFF` is called with 1
 parameter, <path>.
index 4b36d51beb66f08bff4576ed3afcbc9a6d78d63a..39bfbca1ffe565f52b9536beaf040539ab7c0bbc 100644 (file)
@@ -758,6 +758,37 @@ with the above configuration, i.e. `j-c-diff`, with 7
 parameters, just like `GIT_EXTERNAL_DIFF` program is called.
 See linkgit:git[1] for details.
 
+Setting the internal diff algorithm
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The diff algorithm can be set through the `diff.algorithm` config key, but
+sometimes it may be helpful to set the diff algorithm per path. For example,
+one may want to use the `minimal` diff algorithm for .json files, and the
+`histogram` for .c files, and so on without having to pass in the algorithm
+through the command line each time.
+
+First, in `.gitattributes`, assign the `diff` attribute for paths.
+
+------------------------
+*.json diff=<name>
+------------------------
+
+Then, define a "diff.<name>.algorithm" configuration to specify the diff
+algorithm, choosing from `myers`, `patience`, `minimal`, or `histogram`.
+
+----------------------------------------------------------------
+[diff "<name>"]
+  algorithm = histogram
+----------------------------------------------------------------
+
+This diff algorithm applies to user facing diff output like git-diff(1),
+git-show(1) and is used for the `--stat` output as well. The merge machinery
+will not use the diff algorithm set through this method.
+
+NOTE: If `diff.<name>.command` is defined for path with the
+`diff=<name>` attribute, it is executed as an external diff driver
+(see above), and adding `diff.<name>.algorithm` has no effect, as the
+algorithm is not passed to the external diff driver.
 
 Defining a custom hunk-header
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -1155,7 +1186,7 @@ Unspecified::
 
 String::
 
-       Specify a comma separate list of common whitespace problems to
+       Specify a comma separated list of common whitespace problems to
        notice in the same format as the `core.whitespace` configuration
        variable.
 
index 4522471c33758c534328c010c65f4a69dee39b4b..100f045bb1a0918e51cc3834a015913f9286dbbb 100644 (file)
@@ -167,7 +167,7 @@ helper::
 If there are multiple instances of the `credential.helper` configuration
 variable, each helper will be tried in turn, and may provide a username,
 password, or nothing. Once Git has acquired both a username and a
-password, no more helpers will be tried.
+non-expired password, no more helpers will be tried.
 +
 If `credential.helper` is configured to the empty string, this resets
 the helper list to empty (so you may override a helper set by a
index 015cb21bdc089a30e5403877ca1c61bea2b04e24..0773e5c3800392b8d4a548519b70379501207054 100644 (file)
@@ -83,11 +83,13 @@ Git index format
 
   32-bit mode, split into (high to low bits)
 
+    16-bit unused, must be zero
+
     4-bit object type
       valid values in binary are 1000 (regular file), 1010 (symbolic link)
       and 1110 (gitlink)
 
-    3-bit unused
+    3-bit unused, must be zero
 
     9-bit unix permission. Only 0755 and 0644 are valid for regular files.
     Symbolic links and gitlinks have value 0 in this field.
index a249869fafaa6b10e52c55c7187efb8d2e212e8e..d8e3eb1bac316db170047285e0e094f29ec9cfd1 100644 (file)
@@ -37,7 +37,7 @@ line.
 This is even true for an originally empty line.  In the following
 examples, the end of line that ends with a whitespace letter is
 highlighted with a `$` sign; if you are trying to recreate these
-example by hand, do not cut and paste them---they are there
+example by hand, do not cut and paste them--they are there
 primarily to highlight extra whitespace at the end of some lines.
 
 The signed payload and the way the signature is embedded depends
index a16e62bc8c8ea7fd8bcb419515258b76ef682f61..62908602e7bee3ff4bd6c0aaeca39f5ac3bc7741 100644 (file)
@@ -27,6 +27,18 @@ repository. An exception are hooks triggered during a push ('pre-receive',
 'update', 'post-receive', 'post-update', 'push-to-checkout') which are always
 executed in $GIT_DIR.
 
+Environment variables, such as `GIT_DIR`, `GIT_WORK_TREE`, etc., are exported
+so that Git commands run by the hook can correctly locate the repository.  If
+your hook needs to invoke Git commands in a foreign repository or in a
+different working tree of the same repository, then it should clear these
+environment variables so they do not interfere with Git operations at the
+foreign location.  For example:
+
+------------
+local_desc=$(git describe)
+foreign_desc=$(unset $(git rev-parse --local-env-vars); git -C ../foreign-repo describe)
+------------
+
 Hooks can get their arguments via the environment, command-line
 arguments, and stdin. See the documentation for each hook below for
 details.
index 59bf41cefb9b958f57e6e95201784ec8cec56fb0..acb97ad0c22440a5039d8cf3da6f00dd60295a24 100644 (file)
@@ -578,6 +578,207 @@ and associated requested information, each separated by a single space.
 
        obj-info = obj-id SP obj-size
 
+bundle-uri
+~~~~~~~~~~
+
+If the 'bundle-uri' capability is advertised, the server supports the
+`bundle-uri' command.
+
+The capability is currently advertised with no value (i.e. not
+"bundle-uri=somevalue"), a value may be added in the future for
+supporting command-wide extensions. Clients MUST ignore any unknown
+capability values and proceed with the 'bundle-uri` dialog they
+support.
+
+The 'bundle-uri' command is intended to be issued before `fetch` to
+get URIs to bundle files (see linkgit:git-bundle[1]) to "seed" and
+inform the subsequent `fetch` command.
+
+The client CAN issue `bundle-uri` before or after any other valid
+command. To be useful to clients it's expected that it'll be issued
+after an `ls-refs` and before `fetch`, but CAN be issued at any time
+in the dialog.
+
+DISCUSSION of bundle-uri
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+The intent of the feature is optimize for server resource consumption
+in the common case by changing the common case of fetching a very
+large PACK during linkgit:git-clone[1] into a smaller incremental
+fetch.
+
+It also allows servers to achieve better caching in combination with
+an `uploadpack.packObjectsHook` (see linkgit:git-config[1]).
+
+By having new clones or fetches be a more predictable and common
+negotiation against the tips of recently produces *.bundle file(s).
+Servers might even pre-generate the results of such negotiations for
+the `uploadpack.packObjectsHook` as new pushes come in.
+
+One way that servers could take advantage of these bundles is that the
+server would anticipate that fresh clones will download a known bundle,
+followed by catching up to the current state of the repository using ref
+tips found in that bundle (or bundles).
+
+PROTOCOL for bundle-uri
+^^^^^^^^^^^^^^^^^^^^^^^
+
+A `bundle-uri` request takes no arguments, and as noted above does not
+currently advertise a capability value. Both may be added in the
+future.
+
+When the client issues a `command=bundle-uri` request, the response is a
+list of key-value pairs provided as packet lines with value
+`<key>=<value>`. Each `<key>` should be interpreted as a config key from
+the `bundle.*` namespace to construct a list of bundles. These keys are
+grouped by a `bundle.<id>.` subsection, where each key corresponding to a
+given `<id>` contributes attributes to the bundle defined by that `<id>`.
+See linkgit:git-config[1] for the specific details of these keys and how
+the Git client will interpret their values.
+
+Clients MUST parse the line according to the above format, lines that do
+not conform to the format SHOULD be discarded. The user MAY be warned in
+such a case.
+
+bundle-uri CLIENT AND SERVER EXPECTATIONS
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+URI CONTENTS::
+The content at the advertised URIs MUST be one of two types.
++
+The advertised URI may contain a bundle file that `git bundle verify`
+would accept. I.e. they MUST contain one or more reference tips for
+use by the client, MUST indicate prerequisites (in any) with standard
+"-" prefixes, and MUST indicate their "object-format", if
+applicable.
++
+The advertised URI may alternatively contain a plaintext file that `git
+config --list` would accept (with the `--file` option). The key-value
+pairs in this list are in the `bundle.*` namespace (see
+linkgit:git-config[1]).
+
+bundle-uri CLIENT ERROR RECOVERY::
+A client MUST above all gracefully degrade on errors, whether that
+error is because of bad missing/data in the bundle URI(s), because
+that client is too dumb to e.g. understand and fully parse out bundle
+headers and their prerequisite relationships, or something else.
++
+Server operators should feel confident in turning on "bundle-uri" and
+not worry if e.g. their CDN goes down that clones or fetches will run
+into hard failures. Even if the server bundle(s) are
+incomplete, or bad in some way the client should still end up with a
+functioning repository, just as if it had chosen not to use this
+protocol extension.
++
+All subsequent discussion on client and server interaction MUST keep
+this in mind.
+
+bundle-uri SERVER TO CLIENT::
+The ordering of the returned bundle uris is not significant. Clients
+MUST parse their headers to discover their contained OIDS and
+prerequisites. A client MUST consider the content of the bundle(s)
+themselves and their header as the ultimate source of truth.
++
+A server MAY even return bundle(s) that don't have any direct
+relationship to the repository being cloned (either through accident,
+or intentional "clever" configuration), and expect a client to sort
+out what data they'd like from the bundle(s), if any.
+
+bundle-uri CLIENT TO SERVER::
+The client SHOULD provide reference tips found in the bundle header(s)
+as 'have' lines in any subsequent `fetch` request. A client MAY also
+ignore the bundle(s) entirely if doing so is deemed worse for some
+reason, e.g. if the bundles can't be downloaded, it doesn't like the
+tips it finds etc.
+
+WHEN ADVERTISED BUNDLE(S) REQUIRE NO FURTHER NEGOTIATION::
+If after issuing `bundle-uri` and `ls-refs`, and getting the header(s)
+of the bundle(s) the client finds that the ref tips it wants can be
+retrieved entirely from advertised bundle(s), the client MAY disconnect
+from the Git server. The results of such a 'clone' or 'fetch' should be
+indistinguishable from the state attained without using bundle-uri.
+
+EARLY CLIENT DISCONNECTIONS AND ERROR RECOVERY::
+A client MAY perform an early disconnect while still downloading the
+bundle(s) (having streamed and parsed their headers). In such a case
+the client MUST gracefully recover from any errors related to
+finishing the download and validation of the bundle(s).
++
+I.e. a client might need to re-connect and issue a 'fetch' command,
+and possibly fall back to not making use of 'bundle-uri' at all.
++
+This "MAY" behavior is specified as such (and not a "SHOULD") on the
+assumption that a server advertising bundle uris is more likely than
+not to be serving up a relatively large repository, and to be pointing
+to URIs that have a good chance of being in working order. A client
+MAY e.g. look at the payload size of the bundles as a heuristic to see
+if an early disconnect is worth it, should falling back on a full
+"fetch" dialog be necessary.
+
+WHEN ADVERTISED BUNDLE(S) REQUIRE FURTHER NEGOTIATION::
+A client SHOULD commence a negotiation of a PACK from the server via
+the "fetch" command using the OID tips found in advertised bundles,
+even if's still in the process of downloading those bundle(s).
++
+This allows for aggressive early disconnects from any interactive
+server dialog. The client blindly trusts that the advertised OID tips
+are relevant, and issues them as 'have' lines, it then requests any
+tips it would like (usually from the "ls-refs" advertisement) via
+'want' lines. The server will then compute a (hopefully small) PACK
+with the expected difference between the tips from the bundle(s) and
+the data requested.
++
+The only connection the client then needs to keep active is to the
+concurrently downloading static bundle(s), when those and the
+incremental PACK are retrieved they should be inflated and
+validated. Any errors at this point should be gracefully recovered
+from, see above.
+
+bundle-uri PROTOCOL FEATURES
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The client constructs a bundle list from the `<key>=<value>` pairs
+provided by the server. These pairs are part of the `bundle.*` namespace
+as documented in linkgit:git-config[1]. In this section, we discuss some
+of these keys and describe the actions the client will do in response to
+this information.
+
+In particular, the `bundle.version` key specifies an integer value. The
+only accepted value at the moment is `1`, but if the client sees an
+unexpected value here then the client MUST ignore the bundle list.
+
+As long as `bundle.version` is understood, all other unknown keys MAY be
+ignored by the client. The server will guarantee compatibility with older
+clients, though newer clients may be better able to use the extra keys to
+minimize downloads.
+
+Any backwards-incompatible addition of pre-URI key-value will be
+guarded by a new `bundle.version` value or values in 'bundle-uri'
+capability advertisement itself, and/or by new future `bundle-uri`
+request arguments.
+
+Some example key-value pairs that are not currently implemented but could
+be implemented in the future include:
+
+ * Add a "hash=<val>" or "size=<bytes>" advertise the expected hash or
+   size of the bundle file.
+
+ * Advertise that one or more bundle files are the same (to e.g. have
+   clients round-robin or otherwise choose one of N possible files).
+
+ * A "oid=<OID>" shortcut and "prerequisite=<OID>" shortcut. For
+   expressing the common case of a bundle with one tip and no
+   prerequisites, or one tip and one prerequisite.
++
+This would allow for optimizing the common case of servers who'd like
+to provide one "big bundle" containing only their "main" branch,
+and/or incremental updates thereof.
++
+A client receiving such a a response MAY assume that they can skip
+retrieving the header from a bundle at the indicated URI, and thus
+save themselves and the server(s) the request(s) needed to inspect the
+headers of that bundle or bundles.
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index 15a4c8031f1f3bee61ca77d4d37b7ae443d98d9c..880c51112ba4789bd8b903976c25e7c2cb689599 100644 (file)
@@ -1,13 +1,13 @@
 From: Eric S. Raymond <esr@thyrsus.com>
 Abstract: This is how-to documentation for people who want to add extension
- commands to Git.  It should be read alongside api-builtin.txt.
+ commands to Git.  It should be read alongside builtin.h.
 Content-type: text/asciidoc
 
 How to integrate new subcommands
 ================================
 
 This is how-to documentation for people who want to add extension
-commands to Git.  It should be read alongside api-builtin.txt.
+commands to Git.  It should be read alongside builtin.h.
 
 Runtime environment
 -------------------
index 0b4c1c8d98a4acf01c19722941095da2c2c6525d..3b713344597090037bd6cd7bc034aaeed8316656 100644 (file)
@@ -146,24 +146,34 @@ The placeholders are:
 '%m':: left (`<`), right (`>`) or boundary (`-`) mark
 '%w([<w>[,<i1>[,<i2>]]])':: switch line wrapping, like the -w option of
                            linkgit:git-shortlog[1].
-'%<(<N>[,trunc|ltrunc|mtrunc])':: make the next placeholder take at
-                                 least N columns, padding spaces on
+'%<( <N> [,trunc|ltrunc|mtrunc])':: make the next placeholder take at
+                                 least N column widths, padding spaces on
                                  the right if necessary.  Optionally
-                                 truncate at the beginning (ltrunc),
-                                 the middle (mtrunc) or the end
-                                 (trunc) if the output is longer than
-                                 N columns.  Note that truncating
+                                 truncate (with ellipsis '..') at the left (ltrunc) `..ft`,
+                                 the middle (mtrunc) `mi..le`, or the end
+                                 (trunc) `rig..`, if the output is longer than
+                                 N columns.
+                                 Note 1: that truncating
                                  only works correctly with N >= 2.
-'%<|(<N>)':: make the next placeholder take at least until Nth
-            columns, padding spaces on the right if necessary
-'%>(<N>)', '%>|(<N>)':: similar to '%<(<N>)', '%<|(<N>)' respectively,
+                                 Note 2: spaces around the N and M (see below)
+                                 values are optional.
+                                 Note 3: Emojis and other wide characters
+                                 will take two display columns, which may
+                                 over-run column boundaries.
+                                 Note 4: decomposed character combining marks
+                                 may be misplaced at padding boundaries.
+'%<|( <M> )':: make the next placeholder take at least until Mth
+            display column, padding spaces on the right if necessary.
+            Use negative M values for column positions measured
+            from the right hand edge of the terminal window.
+'%>( <N> )', '%>|( <M> )':: similar to '%<( <N> )', '%<|( <M> )' respectively,
                        but padding spaces on the left
-'%>>(<N>)', '%>>|(<N>)':: similar to '%>(<N>)', '%>|(<N>)'
+'%>>( <N> )', '%>>|( <M> )':: similar to '%>( <N> )', '%>|( <M> )'
                          respectively, except that if the next
                          placeholder takes more spaces than given and
                          there are spaces on its left, use those
                          spaces
-'%><(<N>)', '%><|(<N>)':: similar to '%<(<N>)', '%<|(<N>)'
+'%><( <N> )', '%><|( <M> )':: similar to '%<( <N> )', '%<|( <M> )'
                          respectively, but padding both sides
                          (i.e. the text is centered)
 
index ff68e484069a84854c97d141dbef376badbaec2c..0d90d5b1549aa2e50647e1a0a6195a2ed301d699 100644 (file)
@@ -1100,12 +1100,12 @@ preferred format.  See the `strftime` manual for a complete list of
 format placeholders. When using `-local`, the correct syntax is
 `--date=format-local:...`.
 
-`--date=default` is the default format, and is similar to
-`--date=rfc2822`, with a few exceptions:
+`--date=default` is the default format, and is based on ctime(3)
+output.  It shows a single line with three-letter day of the week,
+three-letter month, day-of-month, hour-minute-seconds in "HH:MM:SS"
+format, followed by 4-digit year, plus timezone information, unless
+the local time zone is used, e.g. `Thu Jan 1 00:00:00 1970 +0000`.
 --
-       - there is no comma after the day-of-week
-
-       - the time zone is omitted when the local time zone is used
 
 ifdef::git-rev-list[]
 --header::
index 0d2e55d781912c36d0131cb9aed790d623f70708..9aa58052bc73dc773844d77819889c66222dc591 100644 (file)
@@ -49,7 +49,8 @@ characters and to avoid word splitting.
 `FETCH_HEAD` records the branch which you fetched from a remote repository
 with your last `git fetch` invocation.
 `ORIG_HEAD` is created by commands that move your `HEAD` in a drastic
-way, to record the position of the `HEAD` before their operation, so that
+way (`git am`, `git merge`, `git rebase`, `git reset`),
+to record the position of the `HEAD` before their operation, so that
 you can easily change the tip of the branch back to the state before you ran
 them.
 `MERGE_HEAD` records the commit(s) which you are merging into your branch
index b78d01d9adfc72ff7598cda0e836d8c9f3c99e0b..91d3a13e3276fcc609abe7268c462e2da61b97af 100644 (file)
@@ -479,14 +479,14 @@ outline for submitting these features:
    (This choice is an opt-in via a config option and a command-line
    option.)
 
-4. Allow the client to understand the `bundle.flag=forFetch` configuration
+4. Allow the client to understand the `bundle.heuristic` configuration key
    and the `bundle.<id>.creationToken` heuristic. When `git clone`
-   discovers a bundle URI with `bundle.flag=forFetch`, it configures the
-   client repository to check that bundle URI during later `git fetch <remote>`
+   discovers a bundle URI with `bundle.heuristic`, it configures the client
+   repository to check that bundle URI during later `git fetch <remote>`
    commands.
 
 5. Allow clients to discover bundle URIs during `git fetch` and configure
-   a bundle URI for later fetches if `bundle.flag=forFetch`.
+   a bundle URI for later fetches if `bundle.heuristic` is set.
 
 6. Implement the "inspect headers" heuristic to reduce data downloads when
    the `bundle.<id>.creationToken` heuristic is not available.
index e2ac36dd210bef993e1cca06f37d788c3d4fe262..ed574810891cad1024658920e0fa8ac550231534 100644 (file)
@@ -562,7 +562,7 @@ hash re-encode during clone and to encourage peers to modernize.
 The design described here allows fetches by SHA-1 clients of a
 personal SHA-256 repository because it's not much more difficult than
 allowing pushes from that repository. This support needs to be guarded
-by a configuration option --- servers like git.kernel.org that serve a
+by a configuration option -- servers like git.kernel.org that serve a
 large number of clients would not be expected to bear that cost.
 
 Meaning of signatures
index 35d454143399e0593af41029d75e9ff4fd3d432e..be58f1bee368941cd7f8050fdfc23c55e48e48fa 100644 (file)
@@ -99,7 +99,7 @@ conflict to leave line D means that the user declares:
     compatible with what AB and AC wanted to do.
 
 So the conflict we would see when merging AB into ACAB should be
-resolved the same way---it is the resolution that is in line with that
+resolved the same way--it is the resolution that is in line with that
 declaration.
 
 Imagine that similarly previously a branch XYXZ was forked from XY,
index 86d0008f94d878278c1318fa6b96dcc36356344a..e410912fe52b9defd6de1601174cd214998ab336 100644 (file)
@@ -33,7 +33,9 @@ config file would appear like this:
 ------------
 
 The `<pushurl>` is used for pushes only. It is optional and defaults
-to `<URL>`.
+to `<URL>`. Pushing to a remote affects all defined pushurls or to all
+defined urls if no pushurls are defined. Fetch, however, will only
+fetch from the first defined url if muliple urls are defined.
 
 Named file in `$GIT_DIR/remotes`
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
index 674110ca3bdc0cb783ec8f1d6e82aa46e36dc3cd..dd62a3ea596c15d8b3a39d93ac5667d29d1bb215 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.39.2
+DEF_VER=v2.40.0-rc0
 
 LF='
 '
diff --git a/INSTALL b/INSTALL
index d5694f8c47089c6e3fef43079f28ec17f969b18e..4b422888828d0e99b7398f9e55055525330cf597 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -120,7 +120,7 @@ Issues of note:
          for everyday use (e.g. "bisect", "request-pull").
 
        - "Perl" version 5.8 or later is needed to use some of the
-         features (e.g. preparing a partial commit using "git add -i/-p",
+         features (e.g. sending patches using "git send-email",
          interacting with svn repositories with "git svn").  If you can
          live without these, use NO_PERL.  Note that recent releases of
          Redhat/Fedora are reported to ship Perl binary package with some
index b258fdbed8623d44b014d3ec0dde0350ecb73d43..50ee51fde3208db679e118338b364efc72658fe1 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -289,6 +289,10 @@ include shared.mak
 # Define NO_REGEX if your C library lacks regex support with REG_STARTEND
 # feature.
 #
+# Define USE_ENHANCED_BASIC_REGULAR_EXPRESSIONS if your C library provides
+# the flag REG_ENHANCED and you'd like to use it to enable enhanced basic
+# regular expressions.
+#
 # Define HAVE_DEV_TTY if your system can open /dev/tty to interact with the
 # user.
 #
@@ -511,10 +515,8 @@ include shared.mak
 # Define BLK_SHA1 to make use of optimized C SHA-1 routines bundled
 # with git (in the block-sha1/ directory).
 #
-# Define NO_APPLE_COMMON_CRYPTO on OSX to opt-out of using the
-# "APPLE_COMMON_CRYPTO" backend for SHA-1, which is currently the
-# default on that OS. On macOS 01.4 (Tiger) or older,
-# NO_APPLE_COMMON_CRYPTO is defined by default.
+# Define APPLE_COMMON_CRYPTO_SHA1 to use Apple's CommonCrypto for
+# SHA-1.
 #
 # If don't enable any of the *_SHA1 settings in this section, Git will
 # default to its built-in sha1collisiondetection library, which is a
@@ -691,7 +693,6 @@ THIRD_PARTY_SOURCES =
 # interactive shell sessions without exporting it.
 unexport CDPATH
 
-SCRIPT_SH += git-bisect.sh
 SCRIPT_SH += git-difftool--helper.sh
 SCRIPT_SH += git-filter-branch.sh
 SCRIPT_SH += git-merge-octopus.sh
@@ -707,7 +708,6 @@ SCRIPT_LIB += git-mergetool--lib
 SCRIPT_LIB += git-sh-i18n
 SCRIPT_LIB += git-sh-setup
 
-SCRIPT_PERL += git-add--interactive.perl
 SCRIPT_PERL += git-archimport.perl
 SCRIPT_PERL += git-cvsexportcommit.perl
 SCRIPT_PERL += git-cvsimport.perl
@@ -801,6 +801,7 @@ TEST_BUILTINS_OBJS += test-dump-cache-tree.o
 TEST_BUILTINS_OBJS += test-dump-fsmonitor.o
 TEST_BUILTINS_OBJS += test-dump-split-index.o
 TEST_BUILTINS_OBJS += test-dump-untracked-cache.o
+TEST_BUILTINS_OBJS += test-env-helper.o
 TEST_BUILTINS_OBJS += test-example-decorate.o
 TEST_BUILTINS_OBJS += test-fast-rebase.o
 TEST_BUILTINS_OBJS += test-fsmonitor-client.o
@@ -1202,7 +1203,7 @@ BUILTIN_OBJS += builtin/am.o
 BUILTIN_OBJS += builtin/annotate.o
 BUILTIN_OBJS += builtin/apply.o
 BUILTIN_OBJS += builtin/archive.o
-BUILTIN_OBJS += builtin/bisect--helper.o
+BUILTIN_OBJS += builtin/bisect.o
 BUILTIN_OBJS += builtin/blame.o
 BUILTIN_OBJS += builtin/branch.o
 BUILTIN_OBJS += builtin/bugreport.o
@@ -1234,7 +1235,6 @@ BUILTIN_OBJS += builtin/diff-index.o
 BUILTIN_OBJS += builtin/diff-tree.o
 BUILTIN_OBJS += builtin/diff.o
 BUILTIN_OBJS += builtin/difftool.o
-BUILTIN_OBJS += builtin/env--helper.o
 BUILTIN_OBJS += builtin/fast-export.o
 BUILTIN_OBJS += builtin/fast-import.o
 BUILTIN_OBJS += builtin/fetch-pack.o
@@ -1912,7 +1912,7 @@ ifdef NO_POSIX_GOODIES
        BASIC_CFLAGS += -DNO_POSIX_GOODIES
 endif
 
-ifdef APPLE_COMMON_CRYPTO
+ifdef APPLE_COMMON_CRYPTO_SHA1
        # Apple CommonCrypto requires chunking
        SHA1_MAX_BLOCK_SIZE = 1024L*1024L*1024L
 endif
@@ -1929,7 +1929,7 @@ ifdef BLK_SHA1
        LIB_OBJS += block-sha1/sha1.o
        BASIC_CFLAGS += -DSHA1_BLK
 else
-ifdef APPLE_COMMON_CRYPTO
+ifdef APPLE_COMMON_CRYPTO_SHA1
        COMPAT_CFLAGS += -DCOMMON_DIGEST_FOR_OPENSSL
        BASIC_CFLAGS += -DSHA1_APPLE
 else
@@ -2040,6 +2040,11 @@ endif
 ifdef NO_REGEX
        COMPAT_CFLAGS += -Icompat/regex
        COMPAT_OBJS += compat/regex/regex.o
+else
+ifdef USE_ENHANCED_BASIC_REGULAR_EXPRESSIONS
+       COMPAT_CFLAGS += -DUSE_ENHANCED_BASIC_REGULAR_EXPRESSIONS
+       COMPAT_OBJS += compat/regcomp_enhanced.o
+endif
 endif
 ifdef NATIVE_CRLF
        BASIC_CFLAGS += -DNATIVE_CRLF
index 25a76d390ec2a2f3daff0dbea99742cd1946cd02..47807bf257fd5e52f95f1fb628867a349034571b 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.39.2.txt
\ No newline at end of file
+Documentation/RelNotes/2.40.0.txt
\ No newline at end of file
index ae1839c04a7fa27125e4a2308c971e63da18492a..00a0f6f96f3f5ebc54bbfb841921c5ceff88c1c3 100644 (file)
@@ -724,7 +724,7 @@ static int run_update(struct add_i_state *s, const struct pathspec *ps,
 }
 
 static void revert_from_diff(struct diff_queue_struct *q,
-                            struct diff_options *opt, void *data)
+                            struct diff_options *opt, void *data UNUSED)
 {
        int i, add_flags = ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE;
 
diff --git a/apply.c b/apply.c
index eec2da2b036ba80a36bcfb897e79520cba8b3833..5cc5479c9c356fb955839fc808d0742d1d54b8e5 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -2913,7 +2913,7 @@ static int apply_one_fragment(struct apply_state *state,
                        break;
                case ' ':
                        if (plen && (ws_rule & WS_BLANK_AT_EOF) &&
-                           ws_blank_line(patch + 1, plen, ws_rule))
+                           ws_blank_line(patch + 1, plen))
                                is_blank_context = 1;
                        /* fallthrough */
                case '-':
@@ -2942,7 +2942,7 @@ static int apply_one_fragment(struct apply_state *state,
                                      (first == '+' ? 0 : LINE_COMMON));
                        if (first == '+' &&
                            (ws_rule & WS_BLANK_AT_EOF) &&
-                           ws_blank_line(patch + 1, plen, ws_rule))
+                           ws_blank_line(patch + 1, plen))
                                added_blank_line = 1;
                        break;
                case '@': case '\\':
@@ -4105,7 +4105,7 @@ static int preimage_oid_in_gitlink_patch(struct patch *p, struct object_id *oid)
 static int build_fake_ancestor(struct apply_state *state, struct patch *list)
 {
        struct patch *patch;
-       struct index_state result = { NULL };
+       struct index_state result = INDEX_STATE_INIT(state->repo);
        struct lock_file lock = LOCK_INIT;
        int res;
 
index 941495f5d7840715dd781ac675f32e3c24ce4cbe..9aeaf2bd87dfb884479f6e6a1b609d082ac39896 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -120,7 +120,7 @@ static const struct attr_check *get_archive_attrs(struct index_state *istate,
        static struct attr_check *check;
        if (!check)
                check = attr_check_initl("export-ignore", "export-subst", NULL);
-       git_check_attr(istate, path, check);
+       git_check_attr(istate, NULL, path, check);
        return check;
 }
 
@@ -472,6 +472,8 @@ static void parse_treeish_arg(const char **argv,
                commit_oid = NULL;
                archive_time = time(NULL);
        }
+       if (ar_args->mtime_option)
+               archive_time = approxidate(ar_args->mtime_option);
 
        tree = parse_tree_indirect(&oid);
        if (!tree)
@@ -586,6 +588,7 @@ static int parse_archive_args(int argc, const char **argv,
        const char *remote = NULL;
        const char *exec = NULL;
        const char *output = NULL;
+       const char *mtime_option = NULL;
        int compression_level = -1;
        int verbose = 0;
        int i;
@@ -607,6 +610,9 @@ static int parse_archive_args(int argc, const char **argv,
                OPT_BOOL(0, "worktree-attributes", &worktree_attributes,
                        N_("read .gitattributes in working directory")),
                OPT__VERBOSE(&verbose, N_("report archived files on stderr")),
+               { OPTION_STRING, 0, "mtime", &mtime_option, N_("time"),
+                 N_("set modification time of archive entries"),
+                 PARSE_OPT_NONEG },
                OPT_NUMBER_CALLBACK(&compression_level,
                        N_("set compression level"), number_callback),
                OPT_GROUP(""),
@@ -668,6 +674,7 @@ static int parse_archive_args(int argc, const char **argv,
        args->base = base;
        args->baselen = strlen(base);
        args->worktree_attributes = worktree_attributes;
+       args->mtime_option = mtime_option;
 
        return argc;
 }
@@ -710,6 +717,7 @@ int write_archive(int argc, const char **argv, const char *prefix,
 
        string_list_clear_func(&args.extra_files, extra_file_info_clear);
        free(args.refname);
+       clear_pathspec(&args.pathspec);
 
        return rc;
 }
index 08bed3ed3af6b062f3ff2791613c64b86d42ec00..7178e2a9a2d06c5c3eaaa8884464e6f4cc26fbde 100644 (file)
--- a/archive.h
+++ b/archive.h
@@ -16,6 +16,7 @@ struct archiver_args {
        struct tree *tree;
        const struct object_id *commit_oid;
        const struct commit *commit;
+       const char *mtime_option;
        timestamp_t time;
        struct pathspec pathspec;
        unsigned int verbose : 1;
diff --git a/attr.c b/attr.c
index 9922529b58c381dbc2cdf778f7e59b17fdf64657..1053dfcd4b61b56e99df60e469ec4e757c80e860 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -13,6 +13,8 @@
 #include "dir.h"
 #include "utf8.h"
 #include "quote.h"
+#include "revision.h"
+#include "object-store.h"
 #include "thread-utils.h"
 
 const char git_attr__true[] = "(builtin)true";
@@ -603,8 +605,7 @@ struct attr_check *attr_check_dup(const struct attr_check *check)
 
        ret->nr = check->nr;
        ret->alloc = check->alloc;
-       ALLOC_ARRAY(ret->items, ret->nr);
-       COPY_ARRAY(ret->items, check->items, ret->nr);
+       DUP_ARRAY(ret->items, check->items, ret->nr);
 
        return ret;
 }
@@ -745,13 +746,61 @@ static struct attr_stack *read_attr_from_file(const char *path, unsigned flags)
        return res;
 }
 
-static struct attr_stack *read_attr_from_index(struct index_state *istate,
-                                              const char *path,
-                                              unsigned flags)
+static struct attr_stack *read_attr_from_buf(char *buf, const char *path,
+                                            unsigned flags)
 {
        struct attr_stack *res;
-       char *buf, *sp;
+       char *sp;
        int lineno = 0;
+
+       if (!buf)
+               return NULL;
+
+       CALLOC_ARRAY(res, 1);
+       for (sp = buf; *sp;) {
+               char *ep;
+               int more;
+
+               ep = strchrnul(sp, '\n');
+               more = (*ep == '\n');
+               *ep = '\0';
+               handle_attr_line(res, sp, path, ++lineno, flags);
+               sp = ep + more;
+       }
+       free(buf);
+
+       return res;
+}
+
+static struct attr_stack *read_attr_from_blob(struct index_state *istate,
+                                             const struct object_id *tree_oid,
+                                             const char *path, unsigned flags)
+{
+       struct object_id oid;
+       unsigned long sz;
+       enum object_type type;
+       void *buf;
+       unsigned short mode;
+
+       if (!tree_oid)
+               return NULL;
+
+       if (get_tree_entry(istate->repo, tree_oid, path, &oid, &mode))
+               return NULL;
+
+       buf = repo_read_object_file(istate->repo, &oid, &type, &sz);
+       if (!buf || type != OBJ_BLOB) {
+               free(buf);
+               return NULL;
+       }
+
+       return read_attr_from_buf(buf, path, flags);
+}
+
+static struct attr_stack *read_attr_from_index(struct index_state *istate,
+                                              const char *path, unsigned flags)
+{
+       char *buf;
        unsigned long size;
 
        if (!istate)
@@ -779,28 +828,19 @@ static struct attr_stack *read_attr_from_index(struct index_state *istate,
                return NULL;
        }
 
-       CALLOC_ARRAY(res, 1);
-       for (sp = buf; *sp; ) {
-               char *ep;
-               int more;
-
-               ep = strchrnul(sp, '\n');
-               more = (*ep == '\n');
-               *ep = '\0';
-               handle_attr_line(res, sp, path, ++lineno, flags);
-               sp = ep + more;
-       }
-       free(buf);
-       return res;
+       return read_attr_from_buf(buf, path, flags);
 }
 
 static struct attr_stack *read_attr(struct index_state *istate,
+                                   const struct object_id *tree_oid,
                                    const char *path, unsigned flags)
 {
        struct attr_stack *res = NULL;
 
        if (direction == GIT_ATTR_INDEX) {
                res = read_attr_from_index(istate, path, flags);
+       } else if (tree_oid) {
+               res = read_attr_from_blob(istate, tree_oid, path, flags);
        } else if (!is_bare_repository()) {
                if (direction == GIT_ATTR_CHECKOUT) {
                        res = read_attr_from_index(istate, path, flags);
@@ -860,6 +900,7 @@ static void push_stack(struct attr_stack **attr_stack_p,
 }
 
 static void bootstrap_attr_stack(struct index_state *istate,
+                                const struct object_id *tree_oid,
                                 struct attr_stack **stack)
 {
        struct attr_stack *e;
@@ -885,7 +926,7 @@ static void bootstrap_attr_stack(struct index_state *istate,
        }
 
        /* root directory */
-       e = read_attr(istate, GITATTRIBUTES_FILE, flags | READ_ATTR_NOFOLLOW);
+       e = read_attr(istate, tree_oid, GITATTRIBUTES_FILE, flags | READ_ATTR_NOFOLLOW);
        push_stack(stack, e, xstrdup(""), 0);
 
        /* info frame */
@@ -899,6 +940,7 @@ static void bootstrap_attr_stack(struct index_state *istate,
 }
 
 static void prepare_attr_stack(struct index_state *istate,
+                              const struct object_id *tree_oid,
                               const char *path, int dirlen,
                               struct attr_stack **stack)
 {
@@ -920,7 +962,7 @@ static void prepare_attr_stack(struct index_state *istate,
         * .gitattributes in deeper directories to shallower ones,
         * and finally use the built-in set as the default.
         */
-       bootstrap_attr_stack(istate, stack);
+       bootstrap_attr_stack(istate, tree_oid, stack);
 
        /*
         * Pop the "info" one that is always at the top of the stack.
@@ -975,7 +1017,7 @@ static void prepare_attr_stack(struct index_state *istate,
                strbuf_add(&pathbuf, path + pathbuf.len, (len - pathbuf.len));
                strbuf_addf(&pathbuf, "/%s", GITATTRIBUTES_FILE);
 
-               next = read_attr(istate, pathbuf.buf, READ_ATTR_NOFOLLOW);
+               next = read_attr(istate, tree_oid, pathbuf.buf, READ_ATTR_NOFOLLOW);
 
                /* reset the pathbuf to not include "/.gitattributes" */
                strbuf_setlen(&pathbuf, len);
@@ -1095,8 +1137,8 @@ static void determine_macros(struct all_attrs_item *all_attrs,
  * Otherwise all attributes are collected.
  */
 static void collect_some_attrs(struct index_state *istate,
-                              const char *path,
-                              struct attr_check *check)
+                              const struct object_id *tree_oid,
+                              const char *path, struct attr_check *check)
 {
        int pathlen, rem, dirlen;
        const char *cp, *last_slash = NULL;
@@ -1115,7 +1157,7 @@ static void collect_some_attrs(struct index_state *istate,
                dirlen = 0;
        }
 
-       prepare_attr_stack(istate, path, dirlen, &check->stack);
+       prepare_attr_stack(istate, tree_oid, path, dirlen, &check->stack);
        all_attrs_init(&g_attr_hashmap, check);
        determine_macros(check->all_attrs, check->stack);
 
@@ -1124,12 +1166,12 @@ static void collect_some_attrs(struct index_state *istate,
 }
 
 void git_check_attr(struct index_state *istate,
-                   const char *path,
+                   const struct object_id *tree_oid, const char *path,
                    struct attr_check *check)
 {
        int i;
 
-       collect_some_attrs(istate, path, check);
+       collect_some_attrs(istate, tree_oid, path, check);
 
        for (i = 0; i < check->nr; i++) {
                unsigned int n = check->items[i].attr->attr_nr;
@@ -1140,13 +1182,13 @@ void git_check_attr(struct index_state *istate,
        }
 }
 
-void git_all_attrs(struct index_state *istate,
+void git_all_attrs(struct index_state *istate, const struct object_id *tree_oid,
                   const char *path, struct attr_check *check)
 {
        int i;
 
        attr_check_reset(check);
-       collect_some_attrs(istate, path, check);
+       collect_some_attrs(istate, tree_oid, path, check);
 
        for (i = 0; i < check->all_attrs_nr; i++) {
                const char *name = check->all_attrs[i].attr->name;
diff --git a/attr.h b/attr.h
index 2f22dffadb32ad6f0b2b9a325e098b9bb0f199f7..9884ea2bc60fb4816ed11222b4c820a855f22a24 100644 (file)
--- a/attr.h
+++ b/attr.h
@@ -45,7 +45,7 @@
  * const char *path;
  *
  * setup_check();
- * git_check_attr(path, check);
+ * git_check_attr(&the_index, tree_oid, path, check);
  * ------------
  *
  * - Act on `.value` member of the result, left in `check->items[]`:
 #define ATTR_MAX_FILE_SIZE (100 * 1024 * 1024)
 
 struct index_state;
+struct object_id;
 
 /**
  * An attribute is an opaque object that is identified by its name. Pass the
@@ -202,13 +203,14 @@ void attr_check_free(struct attr_check *check);
 const char *git_attr_name(const struct git_attr *);
 
 void git_check_attr(struct index_state *istate,
-                   const char *path, struct attr_check *check);
+                   const struct object_id *tree_oid, const char *path,
+                   struct attr_check *check);
 
 /*
  * Retrieve all attributes that apply to the specified path.
  * check holds the attributes and their values.
  */
-void git_all_attrs(struct index_state *istate,
+void git_all_attrs(struct index_state *istate, const struct object_id *tree_oid,
                   const char *path, struct attr_check *check);
 
 enum git_attr_direction {
index ec7487e6836c7ec5819f0b22cc7b08bce0e59fa0..ef5ee5a6436f066e33510219e9a0f73b09bf7aaf 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -472,7 +472,6 @@ static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
 static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
 static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
 static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
-static GIT_PATH_FUNC(git_path_head_name, "head-name")
 
 static void read_bisect_paths(struct strvec *array)
 {
@@ -1188,8 +1187,6 @@ int bisect_clean_state(void)
        unlink_or_warn(git_path_bisect_run());
        unlink_or_warn(git_path_bisect_terms());
        unlink_or_warn(git_path_bisect_first_parent());
-       /* Cleanup head-name if it got left by an old version of git-bisect */
-       unlink_or_warn(git_path_head_name());
        /*
         * Cleanup BISECT_START last to support the --no-checkout option
         * introduced in the commit 4796e823a.
diff --git a/blob.c b/blob.c
index 182718aba9fe320f1064a12fb5baf1969f563b29..8f83523b0cde6dbf1b808b3dcf8ab1c1c23085f3 100644 (file)
--- a/blob.c
+++ b/blob.c
@@ -13,8 +13,7 @@ struct blob *lookup_blob(struct repository *r, const struct object_id *oid)
        return object_as_type(obj, OBJ_BLOB, 0);
 }
 
-int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size)
+void parse_blob_buffer(struct blob *item)
 {
        item->object.parsed = 1;
-       return 0;
 }
diff --git a/blob.h b/blob.h
index 1664872055783557e1836dcc64bc2f51c20c5b4f..74555c90c449ffa89226d264f46c433580707f61 100644 (file)
--- a/blob.h
+++ b/blob.h
@@ -11,8 +11,6 @@ struct blob {
 
 struct blob *lookup_blob(struct repository *r, const struct object_id *oid);
 
-int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size);
-
 /**
  * Blobs do not contain references to other objects and do not have
  * structured data that needs parsing. However, code may use the
@@ -21,5 +19,6 @@ int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size);
  * parse_blob_buffer() is used (by object.c) to flag that the object
  * has been read successfully from the database.
  **/
+void parse_blob_buffer(struct blob *item);
 
 #endif /* BLOB_H */
index d182756827fe5128292798b707a52aed25e7aa48..e5614b53b3699d9fc08d41135b16a4a875b0fc68 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -756,7 +756,7 @@ void create_branches_recursively(struct repository *r, const char *name,
                                _("submodule '%s': unable to find submodule"),
                                submodule_entry_list.entries[i].submodule->name);
                        if (advice_enabled(ADVICE_SUBMODULES_NOT_UPDATED))
-                               advise(_("You may try updating the submodules using 'git checkout %s && git submodule update --init'"),
+                               advise(_("You may try updating the submodules using 'git checkout --no-recurse-submodules %s && git submodule update --init'"),
                                       start_commitish);
                        exit(code);
                }
index 8901a34d6bf424680b9d13a1bdf332bedb4d8e20..46cc7897898a51a7039e5bc9d0c350f3fab2f75e 100644 (file)
--- a/builtin.h
+++ b/builtin.h
  *     on bare repositories.
  *     This only makes sense when `RUN_SETUP` is also set.
  *
- * `SUPPORT_SUPER_PREFIX`:
- *
- *     The built-in supports `--super-prefix`.
- *
  * `DELAY_PAGER_CONFIG`:
  *
  *     If RUN_SETUP or RUN_SETUP_GENTLY is set, git.c normally handles
@@ -116,7 +112,7 @@ int cmd_am(int argc, const char **argv, const char *prefix);
 int cmd_annotate(int argc, const char **argv, const char *prefix);
 int cmd_apply(int argc, const char **argv, const char *prefix);
 int cmd_archive(int argc, const char **argv, const char *prefix);
-int cmd_bisect__helper(int argc, const char **argv, const char *prefix);
+int cmd_bisect(int argc, const char **argv, const char *prefix);
 int cmd_blame(int argc, const char **argv, const char *prefix);
 int cmd_branch(int argc, const char **argv, const char *prefix);
 int cmd_bugreport(int argc, const char **argv, const char *prefix);
index 76277df326b4f47f594e4580f6f645ffa76455f3..61dd386d109b3756e90756b91d9405c71d1b4145 100644 (file)
@@ -88,7 +88,7 @@ static int fix_unmerged_status(struct diff_filepair *p,
 }
 
 static void update_callback(struct diff_queue_struct *q,
-                           struct diff_options *opt, void *cbdata)
+                           struct diff_options *opt UNUSED, void *cbdata)
 {
        int i;
        struct update_callback_data *data = cbdata;
@@ -238,58 +238,14 @@ static int refresh(int verbose, const struct pathspec *pathspec)
        return ret;
 }
 
-int run_add_interactive(const char *revision, const char *patch_mode,
-                       const struct pathspec *pathspec)
-{
-       int i;
-       struct child_process cmd = CHILD_PROCESS_INIT;
-       int use_builtin_add_i =
-               git_env_bool("GIT_TEST_ADD_I_USE_BUILTIN", -1);
-
-       if (use_builtin_add_i < 0 &&
-           git_config_get_bool("add.interactive.usebuiltin",
-                               &use_builtin_add_i))
-               use_builtin_add_i = 1;
-
-       if (use_builtin_add_i != 0) {
-               enum add_p_mode mode;
-
-               if (!patch_mode)
-                       return !!run_add_i(the_repository, pathspec);
-
-               if (!strcmp(patch_mode, "--patch"))
-                       mode = ADD_P_ADD;
-               else if (!strcmp(patch_mode, "--patch=stash"))
-                       mode = ADD_P_STASH;
-               else if (!strcmp(patch_mode, "--patch=reset"))
-                       mode = ADD_P_RESET;
-               else if (!strcmp(patch_mode, "--patch=checkout"))
-                       mode = ADD_P_CHECKOUT;
-               else if (!strcmp(patch_mode, "--patch=worktree"))
-                       mode = ADD_P_WORKTREE;
-               else
-                       die("'%s' not supported", patch_mode);
-
-               return !!run_add_p(the_repository, mode, revision, pathspec);
-       }
-
-       strvec_push(&cmd.args, "add--interactive");
-       if (patch_mode)
-               strvec_push(&cmd.args, patch_mode);
-       if (revision)
-               strvec_push(&cmd.args, revision);
-       strvec_push(&cmd.args, "--");
-       for (i = 0; i < pathspec->nr; i++)
-               /* pass original pathspec, to be re-parsed */
-               strvec_push(&cmd.args, pathspec->items[i].original);
-
-       cmd.git_cmd = 1;
-       return run_command(&cmd);
-}
-
 int interactive_add(const char **argv, const char *prefix, int patch)
 {
        struct pathspec pathspec;
+       int unused;
+
+       if (!git_config_get_bool("add.interactive.usebuiltin", &unused))
+               warning(_("the add.interactive.useBuiltin setting has been removed!\n"
+                         "See its entry in 'git help config' for details."));
 
        parse_pathspec(&pathspec, 0,
                       PATHSPEC_PREFER_FULL |
@@ -297,9 +253,10 @@ int interactive_add(const char **argv, const char *prefix, int patch)
                       PATHSPEC_PREFIX_ORIGIN,
                       prefix, argv);
 
-       return run_add_interactive(NULL,
-                                  patch ? "--patch" : NULL,
-                                  &pathspec);
+       if (patch)
+               return !!run_add_p(the_repository, ADD_P_ADD, NULL, &pathspec);
+       else
+               return !!run_add_i(the_repository, &pathspec);
 }
 
 static int edit_patch(int argc, const char **argv, const char *prefix)
@@ -695,6 +652,6 @@ finish:
                die(_("Unable to write new index file"));
 
        dir_clear(&dir);
-       UNLEAK(pathspec);
+       clear_pathspec(&pathspec);
        return exit_status;
 }
index 30c9b3a9cd72588fc2fb4495faedcc7cf3eda258..e0848ddadfeb26617bcf2188b034a3968d1e0b67 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Based on git-am.sh by Junio C Hamano.
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "cache.h"
 #include "config.h"
 #include "builtin.h"
@@ -117,6 +117,7 @@ struct am_state {
 
        /* various operating modes and command line options */
        int interactive;
+       int no_verify;
        int threeway;
        int quiet;
        int signoff; /* enum signoff_type */
@@ -472,10 +473,12 @@ static void am_destroy(const struct am_state *state)
  */
 static int run_applypatch_msg_hook(struct am_state *state)
 {
-       int ret;
+       int ret = 0;
 
        assert(state->msg);
-       ret = run_hooks_l("applypatch-msg", am_path(state, "final-commit"), NULL);
+
+       if (!state->no_verify)
+               ret = run_hooks_l("applypatch-msg", am_path(state, "final-commit"), NULL);
 
        if (!ret) {
                FREE_AND_NULL(state->msg);
@@ -492,24 +495,12 @@ static int run_applypatch_msg_hook(struct am_state *state)
  */
 static int run_post_rewrite_hook(const struct am_state *state)
 {
-       struct child_process cp = CHILD_PROCESS_INIT;
-       const char *hook = find_hook("post-rewrite");
-       int ret;
-
-       if (!hook)
-               return 0;
+       struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
 
-       strvec_push(&cp.args, hook);
-       strvec_push(&cp.args, "rebase");
+       strvec_push(&opt.args, "rebase");
+       opt.path_to_stdin = am_path(state, "rewritten");
 
-       cp.in = xopen(am_path(state, "rewritten"), O_RDONLY);
-       cp.stdout_to_stderr = 1;
-       cp.trace2_hook_name = "post-rewrite";
-
-       ret = run_command(&cp);
-
-       close(cp.in);
-       return ret;
+       return run_hooks_opt("post-rewrite", &opt);
 }
 
 /**
@@ -1476,6 +1467,7 @@ static int run_apply(const struct am_state *state, const char *index_file)
        int res, opts_left;
        int force_apply = 0;
        int options = 0;
+       const char **apply_argv;
 
        if (init_apply_state(&apply_state, the_repository, NULL))
                BUG("init_apply_state() failed");
@@ -1483,7 +1475,14 @@ static int run_apply(const struct am_state *state, const char *index_file)
        strvec_push(&apply_opts, "apply");
        strvec_pushv(&apply_opts, state->git_apply_opts.v);
 
-       opts_left = apply_parse_options(apply_opts.nr, apply_opts.v,
+       /*
+        * Build a copy that apply_parse_options() can rearrange.
+        * apply_opts.v keeps referencing the allocated strings for
+        * strvec_clear() to release.
+        */
+       DUP_ARRAY(apply_argv, apply_opts.v, apply_opts.nr);
+
+       opts_left = apply_parse_options(apply_opts.nr, apply_argv,
                                        &apply_state, &force_apply, &options,
                                        NULL);
 
@@ -1513,6 +1512,7 @@ static int run_apply(const struct am_state *state, const char *index_file)
        strvec_clear(&apply_paths);
        strvec_clear(&apply_opts);
        clear_apply_state(&apply_state);
+       free(apply_argv);
 
        if (res)
                return res;
@@ -1640,10 +1640,10 @@ static void do_commit(const struct am_state *state)
        const char *reflog_msg, *author, *committer = NULL;
        struct strbuf sb = STRBUF_INIT;
 
-       if (run_hooks("pre-applypatch"))
+       if (!state->no_verify && run_hooks("pre-applypatch"))
                exit(1);
 
-       if (write_cache_as_tree(&tree, 0, NULL))
+       if (write_index_as_tree(&tree, &the_index, get_index_file(), 0, NULL))
                die(_("git write-tree failed to write a tree"));
 
        if (!get_oid_commit("HEAD", &parent)) {
@@ -2051,7 +2051,7 @@ static int clean_index(const struct object_id *head, const struct object_id *rem
        if (fast_forward_to(head_tree, head_tree, 1))
                return -1;
 
-       if (write_cache_as_tree(&index, 0, NULL))
+       if (write_index_as_tree(&index, &the_index, get_index_file(), 0, NULL))
                return -1;
 
        index_tree = parse_tree_indirect(&index);
@@ -2330,6 +2330,8 @@ int cmd_am(int argc, const char **argv, const char *prefix)
        struct option options[] = {
                OPT_BOOL('i', "interactive", &state.interactive,
                        N_("run interactively")),
+               OPT_BOOL('n', "no-verify", &state.no_verify,
+                       N_("bypass pre-applypatch and applypatch-msg hooks")),
                OPT_HIDDEN_BOOL('b', "binary", &binary,
                        N_("historical option -- no-op")),
                OPT_BOOL('3', "3way", &state.threeway,
similarity index 90%
rename from builtin/bisect--helper.c
rename to builtin/bisect.c
index 6e41cbdb2d3785cadb5ab901586585daee8718e0..73017402671d30ce7f868a9691f9bb6f520695e1 100644 (file)
@@ -15,23 +15,44 @@ static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
 static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
 static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
 static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
-static GIT_PATH_FUNC(git_path_head_name, "head-name")
 static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
 static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
 static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
 
-static const char * const git_bisect_helper_usage[] = {
-       N_("git bisect--helper --bisect-reset [<commit>]"),
-       "git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]",
-       N_("git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}=<term>]"
-                                           " [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]"),
-       "git bisect--helper --bisect-next",
-       N_("git bisect--helper --bisect-state (bad|new) [<rev>]"),
-       N_("git bisect--helper --bisect-state (good|old) [<rev>...]"),
-       N_("git bisect--helper --bisect-replay <filename>"),
-       N_("git bisect--helper --bisect-skip [(<rev>|<range>)...]"),
-       "git bisect--helper --bisect-visualize",
-       N_("git bisect--helper --bisect-run <cmd>..."),
+#define BUILTIN_GIT_BISECT_START_USAGE \
+       N_("git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]" \
+          "    [--no-checkout] [--first-parent] [<bad> [<good>...]] [--]" \
+          "    [<pathspec>...]")
+#define BUILTIN_GIT_BISECT_STATE_USAGE \
+       N_("git bisect (good|bad) [<rev>...]")
+#define BUILTIN_GIT_BISECT_TERMS_USAGE \
+       "git bisect terms [--term-good | --term-bad]"
+#define BUILTIN_GIT_BISECT_SKIP_USAGE \
+       N_("git bisect skip [(<rev>|<range>)...]")
+#define BUILTIN_GIT_BISECT_NEXT_USAGE \
+       "git bisect next"
+#define BUILTIN_GIT_BISECT_RESET_USAGE \
+       N_("git bisect reset [<commit>]")
+#define BUILTIN_GIT_BISECT_VISUALIZE_USAGE \
+       "git bisect visualize"
+#define BUILTIN_GIT_BISECT_REPLAY_USAGE \
+       N_("git bisect replay <logfile>")
+#define BUILTIN_GIT_BISECT_LOG_USAGE \
+       "git bisect log"
+#define BUILTIN_GIT_BISECT_RUN_USAGE \
+       N_("git bisect run <cmd>...")
+
+static const char * const git_bisect_usage[] = {
+       BUILTIN_GIT_BISECT_START_USAGE,
+       BUILTIN_GIT_BISECT_STATE_USAGE,
+       BUILTIN_GIT_BISECT_TERMS_USAGE,
+       BUILTIN_GIT_BISECT_SKIP_USAGE,
+       BUILTIN_GIT_BISECT_NEXT_USAGE,
+       BUILTIN_GIT_BISECT_RESET_USAGE,
+       BUILTIN_GIT_BISECT_VISUALIZE_USAGE,
+       BUILTIN_GIT_BISECT_REPLAY_USAGE,
+       BUILTIN_GIT_BISECT_LOG_USAGE,
+       BUILTIN_GIT_BISECT_RUN_USAGE,
        NULL
 };
 
@@ -656,7 +677,8 @@ static enum bisect_error bisect_auto_next(struct bisect_terms *terms, const char
        return bisect_next(terms, prefix);
 }
 
-static enum bisect_error bisect_start(struct bisect_terms *terms, const char **argv, int argc)
+static enum bisect_error bisect_start(struct bisect_terms *terms, int argc,
+                                     const char **argv)
 {
        int no_checkout = 0;
        int first_parent_only = 0;
@@ -785,13 +807,6 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a
                        strbuf_addstr(&start_head, oid_to_hex(&head_oid));
                } else if (!get_oid(head, &head_oid) &&
                           skip_prefix(head, "refs/heads/", &head)) {
-                       /*
-                        * This error message should only be triggered by
-                        * cogito usage, and cogito users should understand
-                        * it relates to cg-seek.
-                        */
-                       if (!is_empty_or_missing_file(git_path_head_name()))
-                               return error(_("won't bisect on cg-seek'ed tree"));
                        strbuf_addstr(&start_head, head);
                } else {
                        return error(_("bad HEAD - strange symbolic ref"));
@@ -886,13 +901,13 @@ static int bisect_autostart(struct bisect_terms *terms)
        yesno = git_prompt(_("Do you want me to do it for you "
                             "[Y/n]? "), PROMPT_ECHO);
        res = tolower(*yesno) == 'n' ?
-               -1 : bisect_start(terms, empty_strvec, 0);
+               -1 : bisect_start(terms, 0, empty_strvec);
 
        return res;
 }
 
-static enum bisect_error bisect_state(struct bisect_terms *terms, const char **argv,
-                                     int argc)
+static enum bisect_error bisect_state(struct bisect_terms *terms, int argc,
+                                     const char **argv)
 {
        const char *state;
        int i, verify_expected = 1;
@@ -1011,7 +1026,7 @@ static int process_replay_line(struct bisect_terms *terms, struct strbuf *line)
                struct strvec argv = STRVEC_INIT;
                int res;
                sq_dequote_to_strvec(rev, &argv);
-               res = bisect_start(terms, argv.v, argv.nr);
+               res = bisect_start(terms, argv.nr, argv.v);
                strvec_clear(&argv);
                return res;
        }
@@ -1061,7 +1076,8 @@ static enum bisect_error bisect_replay(struct bisect_terms *terms, const char *f
        return bisect_auto_next(terms, NULL);
 }
 
-static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **argv, int argc)
+static enum bisect_error bisect_skip(struct bisect_terms *terms, int argc,
+                                    const char **argv)
 {
        int i;
        enum bisect_error res;
@@ -1091,13 +1107,14 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar
                        strvec_push(&argv_state, argv[i]);
                }
        }
-       res = bisect_state(terms, argv_state.v, argv_state.nr);
+       res = bisect_state(terms, argv_state.nr, argv_state.v);
 
        strvec_clear(&argv_state);
        return res;
 }
 
-static int bisect_visualize(struct bisect_terms *terms, const char **argv, int argc)
+static int bisect_visualize(struct bisect_terms *terms, int argc,
+                           const char **argv)
 {
        struct child_process cmd = CHILD_PROCESS_INIT;
        struct strbuf sb = STRBUF_INIT;
@@ -1180,7 +1197,7 @@ static int verify_good(const struct bisect_terms *terms, const char *command)
        return rc;
 }
 
-static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
+static int bisect_run(struct bisect_terms *terms, int argc, const char **argv)
 {
        int res = BISECT_OK;
        struct strbuf command = STRBUF_INIT;
@@ -1191,13 +1208,13 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
        if (bisect_next_check(terms, NULL))
                return BISECT_FAILED;
 
-       if (argc)
-               sq_quote_argv(&command, argv);
-       else {
+       if (!argc) {
                error(_("bisect run failed: no command provided."));
                return BISECT_FAILED;
        }
 
+       sq_quote_argv(&command, argv);
+       strbuf_ltrim(&command);
        while (1) {
                res = do_bisect_run(command.buf);
 
@@ -1211,8 +1228,8 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
                if (is_first_run && (res == 126 || res == 127)) {
                        int rc = verify_good(terms, command.buf);
                        is_first_run = 0;
-                       if (rc < 0) {
-                               error(_("unable to verify '%s' on good"
+                       if (rc < 0 || 128 <= rc) {
+                               error(_("unable to verify %s on good"
                                        " revision"), command.buf);
                                res = BISECT_FAILED;
                                break;
@@ -1227,7 +1244,7 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
 
                if (res < 0 || 128 <= res) {
                        error(_("bisect run failed: exit code %d from"
-                               " '%s' is < 0 or >= 128"), res, command.buf);
+                               " %s is < 0 or >= 128"), res, command.buf);
                        break;
                }
 
@@ -1249,7 +1266,7 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
                saved_stdout = dup(1);
                dup2(temporary_stdout_fd, 1);
 
-               res = bisect_state(terms, &new_state, 1);
+               res = bisect_state(terms, 1, &new_state);
 
                fflush(stdout);
                dup2(saved_stdout, 1);
@@ -1261,14 +1278,14 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
                if (res == BISECT_ONLY_SKIPPED_LEFT)
                        error(_("bisect run cannot continue any more"));
                else if (res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) {
-                       printf(_("bisect run success"));
+                       puts(_("bisect run success"));
                        res = BISECT_OK;
                } else if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) {
-                       printf(_("bisect found first bad commit"));
+                       puts(_("bisect found first bad commit"));
                        res = BISECT_OK;
                } else if (res) {
-                       error(_("bisect run failed: 'git bisect--helper --bisect-state"
-                       " %s' exited with error code %d"), new_state, res);
+                       error(_("bisect run failed: 'git bisect %s'"
+                               " exited with error code %d"), new_state, res);
                } else {
                        continue;
                }
@@ -1282,7 +1299,8 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
 static int cmd_bisect__reset(int argc, const char **argv, const char *prefix UNUSED)
 {
        if (argc > 1)
-               return error(_("--bisect-reset requires either no argument or a commit"));
+               return error(_("'%s' requires either no argument or a commit"),
+                            "git bisect reset");
        return bisect_reset(argc ? argv[0] : NULL);
 }
 
@@ -1292,7 +1310,8 @@ static int cmd_bisect__terms(int argc, const char **argv, const char *prefix UNU
        struct bisect_terms terms = { 0 };
 
        if (argc > 1)
-               return error(_("--bisect-terms requires 0 or 1 argument"));
+               return error(_("'%s' requires 0 or 1 argument"),
+                            "git bisect terms");
        res = bisect_terms(&terms, argc == 1 ? argv[0] : NULL);
        free_terms(&terms);
        return res;
@@ -1304,7 +1323,7 @@ static int cmd_bisect__start(int argc, const char **argv, const char *prefix UNU
        struct bisect_terms terms = { 0 };
 
        set_terms(&terms, "bad", "good");
-       res = bisect_start(&terms, argv, argc);
+       res = bisect_start(&terms, argc, argv);
        free_terms(&terms);
        return res;
 }
@@ -1315,29 +1334,16 @@ static int cmd_bisect__next(int argc, const char **argv UNUSED, const char *pref
        struct bisect_terms terms = { 0 };
 
        if (argc)
-               return error(_("--bisect-next requires 0 arguments"));
+               return error(_("'%s' requires 0 arguments"),
+                            "git bisect next");
        get_terms(&terms);
        res = bisect_next(&terms, prefix);
        free_terms(&terms);
        return res;
 }
 
-static int cmd_bisect__state(int argc, const char **argv, const char *prefix UNUSED)
-{
-       int res;
-       struct bisect_terms terms = { 0 };
-
-       set_terms(&terms, "bad", "good");
-       get_terms(&terms);
-       res = bisect_state(&terms, argv, argc);
-       free_terms(&terms);
-       return res;
-}
-
-static int cmd_bisect__log(int argc, const char **argv UNUSED, const char *prefix UNUSED)
+static int cmd_bisect__log(int argc UNUSED, const char **argv UNUSED, const char *prefix UNUSED)
 {
-       if (argc)
-               return error(_("--bisect-log requires 0 arguments"));
        return bisect_log();
 }
 
@@ -1361,7 +1367,7 @@ static int cmd_bisect__skip(int argc, const char **argv, const char *prefix UNUS
 
        set_terms(&terms, "bad", "good");
        get_terms(&terms);
-       res = bisect_skip(&terms, argv, argc);
+       res = bisect_skip(&terms, argc, argv);
        free_terms(&terms);
        return res;
 }
@@ -1372,7 +1378,7 @@ static int cmd_bisect__visualize(int argc, const char **argv, const char *prefix
        struct bisect_terms terms = { 0 };
 
        get_terms(&terms);
-       res = bisect_visualize(&terms, argv, argc);
+       res = bisect_visualize(&terms, argc, argv);
        free_terms(&terms);
        return res;
 }
@@ -1383,14 +1389,14 @@ static int cmd_bisect__run(int argc, const char **argv, const char *prefix UNUSE
        struct bisect_terms terms = { 0 };
 
        if (!argc)
-               return error(_("bisect run failed: no command provided."));
+               return error(_("'%s' failed: no command provided."), "git bisect run");
        get_terms(&terms);
-       res = bisect_run(&terms, argv, argc);
+       res = bisect_run(&terms, argc, argv);
        free_terms(&terms);
        return res;
 }
 
-int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
+int cmd_bisect(int argc, const char **argv, const char *prefix)
 {
        int res = 0;
        parse_opt_subcommand_fn *fn = NULL;
@@ -1399,7 +1405,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
                OPT_SUBCOMMAND("terms", &fn, cmd_bisect__terms),
                OPT_SUBCOMMAND("start", &fn, cmd_bisect__start),
                OPT_SUBCOMMAND("next", &fn, cmd_bisect__next),
-               OPT_SUBCOMMAND("state", &fn, cmd_bisect__state),
                OPT_SUBCOMMAND("log", &fn, cmd_bisect__log),
                OPT_SUBCOMMAND("replay", &fn, cmd_bisect__replay),
                OPT_SUBCOMMAND("skip", &fn, cmd_bisect__skip),
@@ -1408,22 +1413,27 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
                OPT_SUBCOMMAND("run", &fn, cmd_bisect__run),
                OPT_END()
        };
-       argc = parse_options(argc, argv, prefix, options,
-                            git_bisect_helper_usage, 0);
-
-       if (!fn)
-               usage_with_options(git_bisect_helper_usage, options);
-       argc--;
-       argv++;
-
-       res = fn(argc, argv, prefix);
-
-       /*
-        * Handle early success
-        * From check_merge_bases > check_good_are_ancestors_of_bad > bisect_next_all
-        */
-       if ((res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) || (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND))
-               res = BISECT_OK;
+       argc = parse_options(argc, argv, prefix, options, git_bisect_usage,
+                            PARSE_OPT_SUBCOMMAND_OPTIONAL);
+
+       if (!fn) {
+               struct bisect_terms terms = { 0 };
+
+               if (!argc)
+                       usage_msg_opt(_("need a command"), git_bisect_usage, options);
+
+               set_terms(&terms, "bad", "good");
+               get_terms(&terms);
+               if (check_and_set_terms(&terms, argv[0]))
+                       usage_msg_optf(_("unknown command: '%s'"), git_bisect_usage,
+                                      options, argv[0]);
+               res = bisect_state(&terms, argc, argv);
+               free_terms(&terms);
+       } else {
+               argc--;
+               argv++;
+               res = fn(argc, argv, prefix);
+       }
 
-       return -res;
+       return is_bisect_success(res) ? 0 : -res;
 }
index 9470c980c15dca8a96c264772991b51813f1b355..f63fd45edb96b513c24e1a9cd7c8a55e5a5f7b13 100644 (file)
@@ -581,13 +581,13 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
        strbuf_release(&logmsg);
 
        strbuf_addf(&oldsection, "branch.%s", interpreted_oldname);
-       strbuf_release(&oldref);
        strbuf_addf(&newsection, "branch.%s", interpreted_newname);
-       strbuf_release(&newref);
        if (!copy && git_config_rename_section(oldsection.buf, newsection.buf) < 0)
                die(_("Branch is renamed, but update of config-file failed"));
-       if (copy && strcmp(oldname, newname) && git_config_copy_section(oldsection.buf, newsection.buf) < 0)
+       if (copy && strcmp(interpreted_oldname, interpreted_newname) && git_config_copy_section(oldsection.buf, newsection.buf) < 0)
                die(_("Branch is copied, but update of config-file failed"));
+       strbuf_release(&oldref);
+       strbuf_release(&newref);
        strbuf_release(&oldsection);
        strbuf_release(&newsection);
 }
index 96052541cbfe74cda5aff77396c94f4fb6397ae7..5bc254be80f97e7bb33c732039b7bab620fb71ad 100644 (file)
@@ -106,6 +106,7 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix)
        const char *user_relative_path = NULL;
        char *prefixed_filename;
        size_t output_path_len;
+       int ret;
 
        const struct option bugreport_options[] = {
                OPT_CALLBACK_F(0, "diagnose", &diagnose, N_("mode"),
@@ -182,7 +183,9 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix)
                user_relative_path);
 
        free(prefixed_filename);
-       UNLEAK(buffer);
-       UNLEAK(report_path);
-       return !!launch_editor(report_path.buf, NULL, NULL);
+       strbuf_release(&buffer);
+
+       ret = !!launch_editor(report_path.buf, NULL, NULL);
+       strbuf_release(&report_path);
+       return ret;
 }
index c12c09f8549940774c2882f18259cfc13318bdb2..acceef6200172cf7a44de45bfa6f3e940b6444c9 100644 (file)
@@ -55,13 +55,12 @@ static int parse_options_cmd_bundle(int argc,
                const char * const usagestr[],
                const struct option options[],
                char **bundle_file) {
-       int newargc;
-       newargc = parse_options(argc, argv, NULL, options, usagestr,
+       argc = parse_options(argc, argv, NULL, options, usagestr,
                             PARSE_OPT_STOP_AT_NON_OPTION);
-       if (argc < 1)
-               usage_with_options(usagestr, options);
+       if (!argc)
+               usage_msg_opt(_("need a <file> argument"), usagestr, options);
        *bundle_file = prefix_filename(prefix, argv[0]);
-       return newargc;
+       return argc;
 }
 
 static int cmd_bundle_create(int argc, const char **argv, const char *prefix) {
index b3be58b1fb06678376568753f9fc221d8cfb3750..cc17635e76536471a0a756903a8187eaa01d5733 100644 (file)
@@ -132,8 +132,21 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
 
        case 's':
                oi.sizep = &size;
+
+               if (use_mailmap) {
+                       oi.typep = &type;
+                       oi.contentp = (void**)&buf;
+               }
+
                if (oid_object_info_extended(the_repository, &oid, &oi, flags) < 0)
                        die("git cat-file: could not get object info");
+
+               if (use_mailmap && (type == OBJ_COMMIT || type == OBJ_TAG)) {
+                       size_t s = size;
+                       buf = replace_idents_using_mailmap(buf, &s);
+                       size = cast_size_t_to_ulong(s);
+               }
+
                printf("%"PRIuMAX"\n", (uintmax_t)size);
                ret = 0;
                goto cleanup;
@@ -431,6 +444,9 @@ static void batch_object_write(const char *obj_name,
        if (!data->skip_object_info) {
                int ret;
 
+               if (use_mailmap)
+                       data->info.typep = &data->type;
+
                if (pack)
                        ret = packed_object_info(the_repository, pack, offset,
                                                 &data->info);
@@ -444,6 +460,18 @@ static void batch_object_write(const char *obj_name,
                        fflush(stdout);
                        return;
                }
+
+               if (use_mailmap && (data->type == OBJ_COMMIT || data->type == OBJ_TAG)) {
+                       size_t s = data->size;
+                       char *buf = NULL;
+
+                       buf = repo_read_object_file(the_repository, &data->oid, &data->type,
+                                                   &data->size);
+                       buf = replace_idents_using_mailmap(buf, &s);
+                       data->size = cast_size_t_to_ulong(s);
+
+                       free(buf);
+               }
        }
 
        strbuf_reset(scratch);
index 0fef10eb6bc7dafb6e5e30ef8b988d1921f99f98..d7a40e674ca049e414d1443b2e5a78d85c392ab2 100644 (file)
@@ -9,9 +9,10 @@
 static int all_attrs;
 static int cached_attrs;
 static int stdin_paths;
+static char *source;
 static const char * const check_attr_usage[] = {
-N_("git check-attr [-a | --all | <attr>...] [--] <pathname>..."),
-N_("git check-attr --stdin [-z] [-a | --all | <attr>...]"),
+N_("git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] <pathname>..."),
+N_("git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"),
 NULL
 };
 
@@ -23,6 +24,7 @@ static const struct option check_attr_options[] = {
        OPT_BOOL(0 , "stdin", &stdin_paths, N_("read file names from stdin")),
        OPT_BOOL('z', NULL, &nul_term_line,
                 N_("terminate input and output records by a NUL character")),
+       OPT_STRING(0, "source", &source, N_("<tree-ish>"), N_("which tree-ish to check attributes at")),
        OPT_END()
 };
 
@@ -55,27 +57,26 @@ static void output_attr(struct attr_check *check, const char *file)
        }
 }
 
-static void check_attr(const char *prefix,
-                      struct attr_check *check,
-                      int collect_all,
+static void check_attr(const char *prefix, struct attr_check *check,
+                      const struct object_id *tree_oid, int collect_all,
                       const char *file)
+
 {
        char *full_path =
                prefix_path(prefix, prefix ? strlen(prefix) : 0, file);
 
        if (collect_all) {
-               git_all_attrs(&the_index, full_path, check);
+               git_all_attrs(&the_index, tree_oid, full_path, check);
        } else {
-               git_check_attr(&the_index, full_path, check);
+               git_check_attr(&the_index, tree_oid, full_path, check);
        }
        output_attr(check, file);
 
        free(full_path);
 }
 
-static void check_attr_stdin_paths(const char *prefix,
-                                  struct attr_check *check,
-                                  int collect_all)
+static void check_attr_stdin_paths(const char *prefix, struct attr_check *check,
+                                  const struct object_id *tree_oid, int collect_all)
 {
        struct strbuf buf = STRBUF_INIT;
        struct strbuf unquoted = STRBUF_INIT;
@@ -89,7 +90,7 @@ static void check_attr_stdin_paths(const char *prefix,
                                die("line is badly quoted");
                        strbuf_swap(&buf, &unquoted);
                }
-               check_attr(prefix, check, collect_all, buf.buf);
+               check_attr(prefix, check, tree_oid, collect_all, buf.buf);
                maybe_flush_or_die(stdout, "attribute to stdout");
        }
        strbuf_release(&buf);
@@ -105,6 +106,8 @@ static NORETURN void error_with_usage(const char *msg)
 int cmd_check_attr(int argc, const char **argv, const char *prefix)
 {
        struct attr_check *check;
+       struct object_id *tree_oid = NULL;
+       struct object_id initialized_oid;
        int cnt, i, doubledash, filei;
 
        if (!is_bare_repository())
@@ -176,11 +179,17 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)
                }
        }
 
+       if (source) {
+               if (repo_get_oid_tree(the_repository, source, &initialized_oid))
+                       die("%s: not a valid tree-ish source", source);
+               tree_oid = &initialized_oid;
+       }
+
        if (stdin_paths)
-               check_attr_stdin_paths(prefix, check, all_attrs);
+               check_attr_stdin_paths(prefix, check, tree_oid, all_attrs);
        else {
                for (i = filei; i < argc; i++)
-                       check_attr(prefix, check, all_attrs, argv[i]);
+                       check_attr(prefix, check, tree_oid, all_attrs, argv[i]);
                maybe_flush_or_die(stdout, "attribute to stdout");
        }
 
index 3fa29a08eea1f13ddfe96c1634f920284b87ad11..a5155cf55c1e5150fd954a5b59363ee899974361 100644 (file)
@@ -29,6 +29,7 @@
 #include "xdiff-interface.h"
 #include "entry.h"
 #include "parallel-checkout.h"
+#include "add-interactive.h"
 
 static const char * const checkout_usage[] = {
        N_("git checkout [<options>] <branch>"),
@@ -232,7 +233,7 @@ static int checkout_stage(int stage, const struct cache_entry *ce, int pos,
                pos++;
        }
        if (!overlay_mode) {
-               unlink_entry(ce);
+               unlink_entry(ce, NULL);
                return 0;
        }
        if (stage == 2)
@@ -499,7 +500,7 @@ static int checkout_paths(const struct checkout_opts *opts,
                    "--merge", "--conflict", "--staged");
 
        if (opts->patch_mode) {
-               const char *patch_mode;
+               enum add_p_mode patch_mode;
                const char *rev = new_branch_info->name;
                char rev_oid[GIT_MAX_HEXSZ + 1];
 
@@ -517,15 +518,16 @@ static int checkout_paths(const struct checkout_opts *opts,
                        rev = oid_to_hex_r(rev_oid, &new_branch_info->commit->object.oid);
 
                if (opts->checkout_index && opts->checkout_worktree)
-                       patch_mode = "--patch=checkout";
+                       patch_mode = ADD_P_CHECKOUT;
                else if (opts->checkout_index && !opts->checkout_worktree)
-                       patch_mode = "--patch=reset";
+                       patch_mode = ADD_P_RESET;
                else if (!opts->checkout_index && opts->checkout_worktree)
-                       patch_mode = "--patch=worktree";
+                       patch_mode = ADD_P_WORKTREE;
                else
                        BUG("either flag must have been set, worktree=%d, index=%d",
                            opts->checkout_worktree, opts->checkout_index);
-               return run_add_interactive(rev, patch_mode, &opts->pathspec);
+               return !!run_add_p(the_repository, patch_mode, rev,
+                                  &opts->pathspec);
        }
 
        repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
@@ -1270,7 +1272,7 @@ static int parse_branchname_arg(int argc, const char **argv,
         *       between A and B, A...B names that merge base.
         *
         *   (b) If <something> is _not_ a commit, either "--" is present
-        *       or <something> is not a path, no -t or -b was given, and
+        *       or <something> is not a path, no -t or -b was given,
         *       and there is a tracking branch whose name is <something>
         *       in one and only one remote (or if the branch exists on the
         *       remote named in checkout.defaultRemote), then this is a
@@ -1471,6 +1473,8 @@ static void die_if_some_operation_in_progress(void)
                      "or \"git worktree add\"."));
        if (state.bisect_in_progress)
                warning(_("you are switching branch while bisecting"));
+
+       wt_status_state_free_buffers(&state);
 }
 
 static int checkout_branch(struct checkout_opts *opts,
index b2701a2815803e8c1231eb7516bc5936311e9f0a..10aaa8c603fe587682724531bbdbfc92de80a79c 100644 (file)
@@ -560,7 +560,7 @@ static int parse_choice(struct menu_stuff *menu_stuff,
 
 /*
  * Implement a git-add-interactive compatible UI, which is borrowed
- * from git-add--interactive.perl.
+ * from add-interactive.c.
  *
  * Return value:
  *
@@ -1092,5 +1092,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        strbuf_release(&buf);
        string_list_clear(&del_list, 0);
        string_list_clear(&exclude_list, 0);
+       clear_pathspec(&pathspec);
        return (errors != 0);
 }
index 3c2ae31a559f482ae0615e62cb4993053bc2aeeb..65b5b7db6de5bdac835ad95b6438c1cc548d1599 100644 (file)
@@ -892,6 +892,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        int is_bundle = 0, is_local;
        int reject_shallow = 0;
        const char *repo_name, *repo, *work_tree, *git_dir;
+       char *repo_to_free = NULL;
        char *path = NULL, *dir, *display_repo = NULL;
        int dest_exists, real_dest_exists = 0;
        const struct ref *refs, *remote_head;
@@ -949,7 +950,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        path = get_repo_path(repo_name, &is_bundle);
        if (path) {
                FREE_AND_NULL(path);
-               repo = absolute_pathdup(repo_name);
+               repo = repo_to_free = absolute_pathdup(repo_name);
        } else if (strchr(repo_name, ':')) {
                repo = repo_name;
                display_repo = transport_anonymize_url(repo);
@@ -1248,12 +1249,16 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
         * data from the --bundle-uri option.
         */
        if (bundle_uri) {
+               int has_heuristic = 0;
+
                /* At this point, we need the_repository to match the cloned repo. */
                if (repo_init(the_repository, git_dir, work_tree))
                        warning(_("failed to initialize the repo, skipping bundle URI"));
-               else if (fetch_bundle_uri(the_repository, bundle_uri))
+               else if (fetch_bundle_uri(the_repository, bundle_uri, &has_heuristic))
                        warning(_("failed to fetch objects from bundle URI '%s'"),
                                bundle_uri);
+               else if (has_heuristic)
+                       git_config_set_gently("fetch.bundleuri", bundle_uri);
        }
 
        strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD");
@@ -1271,6 +1276,27 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        if (refs)
                mapped_refs = wanted_peer_refs(refs, &remote->fetch);
 
+       if (!bundle_uri) {
+               /*
+               * Populate transport->got_remote_bundle_uri and
+               * transport->bundle_uri. We might get nothing.
+               */
+               transport_get_remote_bundle_uri(transport);
+
+               if (transport->bundles &&
+                   hashmap_get_size(&transport->bundles->bundles)) {
+                       /* At this point, we need the_repository to match the cloned repo. */
+                       if (repo_init(the_repository, git_dir, work_tree))
+                               warning(_("failed to initialize the repo, skipping bundle URI"));
+                       else if (fetch_bundle_list(the_repository,
+                                                  transport->bundles))
+                               warning(_("failed to fetch advertised bundles"));
+               } else {
+                       clear_bundle_list(transport->bundles);
+                       FREE_AND_NULL(transport->bundles);
+               }
+       }
+
        if (mapped_refs) {
                int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
 
@@ -1392,7 +1418,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        free(unborn_head);
        free(dir);
        free(path);
-       UNLEAK(repo);
+       free(repo_to_free);
        junk_mode = JUNK_LEAVE_ALL;
 
        transport_ls_refs_options_release(&transport_ls_refs_options);
index e8f77f535f3075405853f6ff6d4fdf40a4b019af..93704f95a9da8ab8313e7a17589153951e3bb9fe 100644 (file)
@@ -67,6 +67,7 @@ static int graph_verify(int argc, const char **argv, const char *prefix)
        int fd;
        struct stat st;
        int flags = 0;
+       int ret;
 
        static struct option builtin_commit_graph_verify_options[] = {
                OPT_BOOL(0, "shallow", &opts.shallow,
@@ -111,8 +112,9 @@ static int graph_verify(int argc, const char **argv, const char *prefix)
        if (!graph)
                return !!open_ok;
 
-       UNLEAK(graph);
-       return verify_commit_graph(the_repository, graph, flags);
+       ret = verify_commit_graph(the_repository, graph, flags);
+       free_commit_graph(graph);
+       return ret;
 }
 
 extern int read_replace_refs;
@@ -267,8 +269,8 @@ static int graph_write(int argc, const char **argv, const char *prefix)
 
        if (opts.reachable) {
                if (write_commit_graph_reachable(odb, flags, &write_opts))
-                       return 1;
-               return 0;
+                       result = 1;
+               goto cleanup;
        }
 
        if (opts.stdin_packs) {
index 06b1330346f895af5103fdb4b52243b57e6f4f18..985a0445b7897c936cc69e828d1e9f4ad98abaf7 100644 (file)
@@ -5,7 +5,7 @@
  * Based on git-commit.sh by Junio C Hamano and Linus Torvalds
  */
 
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "cache.h"
 #include "config.h"
 #include "lockfile.h"
@@ -414,7 +414,7 @@ static const char *prepare_index(const char **argv, const char *prefix,
                discard_index(&the_index);
                read_index_from(&the_index, get_lock_file_path(&index_lock),
                                get_git_dir());
-               if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) {
+               if (cache_tree_update(&the_index, WRITE_TREE_SILENT) == 0) {
                        if (reopen_lock_file(&index_lock) < 0)
                                die(_("unable to write index file"));
                        if (write_locked_index(&the_index, &index_lock, 0))
@@ -444,7 +444,7 @@ static const char *prepare_index(const char **argv, const char *prefix,
                                       LOCK_DIE_ON_ERROR);
                add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
                refresh_cache_or_die(refresh_flags);
-               update_main_cache_tree(WRITE_TREE_SILENT);
+               cache_tree_update(&the_index, WRITE_TREE_SILENT);
                if (write_locked_index(&the_index, &index_lock, 0))
                        die(_("unable to write new_index file"));
                commit_style = COMMIT_NORMAL;
@@ -467,7 +467,7 @@ static const char *prepare_index(const char **argv, const char *prefix,
                refresh_cache_or_die(refresh_flags);
                if (the_index.cache_changed
                    || !cache_tree_fully_valid(the_index.cache_tree))
-                       update_main_cache_tree(WRITE_TREE_SILENT);
+                       cache_tree_update(&the_index, WRITE_TREE_SILENT);
                if (write_locked_index(&the_index, &index_lock,
                                       COMMIT_LOCK | SKIP_IF_UNCHANGED))
                        die(_("unable to write new_index file"));
@@ -516,7 +516,7 @@ static const char *prepare_index(const char **argv, const char *prefix,
        repo_hold_locked_index(the_repository, &index_lock, LOCK_DIE_ON_ERROR);
        add_remove_files(&partial);
        refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
-       update_main_cache_tree(WRITE_TREE_SILENT);
+       cache_tree_update(&the_index, WRITE_TREE_SILENT);
        if (write_locked_index(&the_index, &index_lock, 0))
                die(_("unable to write new_index file"));
 
@@ -991,8 +991,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                struct object_id oid;
                const char *parent = "HEAD";
 
-               if (!active_nr && read_cache() < 0)
-                       die(_("Cannot read index"));
+               if (!the_index.cache_nr) {
+                       discard_index(&the_index);
+                       if (repo_read_index(the_repository) < 0)
+                               die(_("Cannot read index"));
+               }
 
                if (amend)
                        parent = "HEAD^1";
@@ -1076,7 +1079,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        }
        read_index_from(&the_index, index_file, get_git_dir());
 
-       if (update_main_cache_tree(0)) {
+       if (cache_tree_update(&the_index, 0)) {
                error(_("Error building trees"));
                return 0;
        }
@@ -1875,8 +1878,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        apply_autostash(git_path_merge_autostash(the_repository));
 
 cleanup:
-       UNLEAK(author_ident);
-       UNLEAK(err);
-       UNLEAK(sb);
+       strbuf_release(&author_ident);
+       strbuf_release(&err);
+       strbuf_release(&sb);
        return ret;
 }
index 753e5fac297e08763317adcab2fefe386ea68421..060cf9f3e05e6718ae02923e132e4804dbdcb291 100644 (file)
@@ -639,8 +639,9 @@ static char *default_user_config(void)
 int cmd_config(int argc, const char **argv, const char *prefix)
 {
        int nongit = !startup_info->have_repository;
-       char *value;
+       char *value = NULL;
        int flags = 0;
+       int ret = 0;
 
        given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
 
@@ -856,44 +857,38 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                free(config_file);
        }
        else if (actions == ACTION_SET) {
-               int ret;
                check_write();
                check_argc(argc, 2, 2);
                value = normalize_value(argv[0], argv[1]);
-               UNLEAK(value);
                ret = git_config_set_in_file_gently(given_config_source.file, argv[0], value);
                if (ret == CONFIG_NOTHING_SET)
                        error(_("cannot overwrite multiple values with a single value\n"
                        "       Use a regexp, --add or --replace-all to change %s."), argv[0]);
-               return ret;
        }
        else if (actions == ACTION_SET_ALL) {
                check_write();
                check_argc(argc, 2, 3);
                value = normalize_value(argv[0], argv[1]);
-               UNLEAK(value);
-               return git_config_set_multivar_in_file_gently(given_config_source.file,
-                                                             argv[0], value, argv[2],
-                                                             flags);
+               ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+                                                            argv[0], value, argv[2],
+                                                            flags);
        }
        else if (actions == ACTION_ADD) {
                check_write();
                check_argc(argc, 2, 2);
                value = normalize_value(argv[0], argv[1]);
-               UNLEAK(value);
-               return git_config_set_multivar_in_file_gently(given_config_source.file,
-                                                             argv[0], value,
-                                                             CONFIG_REGEX_NONE,
-                                                             flags);
+               ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+                                                            argv[0], value,
+                                                            CONFIG_REGEX_NONE,
+                                                            flags);
        }
        else if (actions == ACTION_REPLACE_ALL) {
                check_write();
                check_argc(argc, 2, 3);
                value = normalize_value(argv[0], argv[1]);
-               UNLEAK(value);
-               return git_config_set_multivar_in_file_gently(given_config_source.file,
-                                                             argv[0], value, argv[2],
-                                                             flags | CONFIG_FLAGS_MULTI_REPLACE);
+               ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+                                                            argv[0], value, argv[2],
+                                                            flags | CONFIG_FLAGS_MULTI_REPLACE);
        }
        else if (actions == ACTION_GET) {
                check_argc(argc, 1, 2);
@@ -934,26 +929,28 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                                                              flags | CONFIG_FLAGS_MULTI_REPLACE);
        }
        else if (actions == ACTION_RENAME_SECTION) {
-               int ret;
                check_write();
                check_argc(argc, 2, 2);
                ret = git_config_rename_section_in_file(given_config_source.file,
                                                        argv[0], argv[1]);
                if (ret < 0)
                        return ret;
-               if (ret == 0)
+               else if (!ret)
                        die(_("no such section: %s"), argv[0]);
+               else
+                       ret = 0;
        }
        else if (actions == ACTION_REMOVE_SECTION) {
-               int ret;
                check_write();
                check_argc(argc, 1, 1);
                ret = git_config_rename_section_in_file(given_config_source.file,
                                                        argv[0], NULL);
                if (ret < 0)
                        return ret;
-               if (ret == 0)
+               else if (!ret)
                        die(_("no such section: %s"), argv[0]);
+               else
+                       ret = 0;
        }
        else if (actions == ACTION_GET_COLOR) {
                check_argc(argc, 1, 2);
@@ -966,5 +963,6 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                return get_colorbool(argv[0], argc == 2);
        }
 
-       return 0;
+       free(value);
+       return ret;
 }
index f3c89831d4a27fb5a2637433ffa724ff0b5e9656..338058be7f943759619965911a7ad18f19263e48 100644 (file)
@@ -127,6 +127,9 @@ static void serve_one_client(FILE *in, FILE *out)
                if (e) {
                        fprintf(out, "username=%s\n", e->item.username);
                        fprintf(out, "password=%s\n", e->item.password);
+                       if (e->item.password_expiry_utc != TIME_MAX)
+                               fprintf(out, "password_expiry_utc=%"PRItime"\n",
+                                       e->item.password_expiry_utc);
                }
        }
        else if (!strcmp(action.buf, "exit")) {
index 163f2c6a874266f183b276fa30961e76feb11316..26f1e532c66b173fb4b29d220564fcd90c03d0d2 100644 (file)
@@ -612,7 +612,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        if (1 < rev.diffopt.skip_stat_unmatch)
                refresh_index_quietly();
        release_revisions(&rev);
-       UNLEAK(ent);
+       object_array_clear(&ent);
        UNLEAK(blob);
        return result;
 }
index d9b76226f6a8dd4f256a902c02cde05bd7399293..dbbfb19f1921028f78b499af0ad35a67c9595d59 100644 (file)
@@ -361,7 +361,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
        struct hashmap symlinks2 = HASHMAP_INIT(pair_cmp, NULL);
        struct hashmap_iter iter;
        struct pair_entry *entry;
-       struct index_state wtindex;
+       struct index_state wtindex = INDEX_STATE_INIT(the_repository);
        struct checkout lstate, rstate;
        int err = 0;
        struct child_process cmd = CHILD_PROCESS_INIT;
@@ -387,8 +387,6 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
        mkdir(ldir.buf, 0700);
        mkdir(rdir.buf, 0700);
 
-       memset(&wtindex, 0, sizeof(wtindex));
-
        memset(&lstate, 0, sizeof(lstate));
        lstate.base_dir = lbase_dir = xstrdup(ldir.buf);
        lstate.base_dir_len = ldir.len;
index 3b3314e7b2a3343b78d14ee3bc9d4a92fb578fd6..39a890fc005192a5082a1c170550f4be102efbc1 100644 (file)
@@ -409,7 +409,7 @@ static const char *anonymize_oid(const char *oid_hex)
 }
 
 static void show_filemodify(struct diff_queue_struct *q,
-                           struct diff_options *options, void *data)
+                           struct diff_options *options UNUSED, void *data)
 {
        int i;
        struct string_list *changed = data;
index 7378cafeec9fd6ea3edc521646ab7d8f66bcc02a..a09606b472622c133a683513ffdbe184952f2b01 100644 (file)
@@ -29,6 +29,7 @@
 #include "commit-graph.h"
 #include "shallow.h"
 #include "worktree.h"
+#include "bundle-uri.h"
 
 #define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
 
@@ -2109,6 +2110,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv,
 int cmd_fetch(int argc, const char **argv, const char *prefix)
 {
        int i;
+       const char *bundle_uri;
        struct string_list list = STRING_LIST_INIT_DUP;
        struct remote *remote = NULL;
        int result = 0;
@@ -2194,6 +2196,13 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        if (dry_run)
                write_fetch_head = 0;
 
+       if (!max_jobs)
+               max_jobs = online_cpus();
+
+       if (!git_config_get_string_tmp("fetch.bundleuri", &bundle_uri) &&
+           fetch_bundle_uri(the_repository, bundle_uri, NULL))
+               warning(_("failed to fetch bundles from '%s'"), bundle_uri);
+
        if (all) {
                if (argc == 1)
                        die(_("fetch --all does not take a repository argument"));
@@ -2228,6 +2237,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                        argv++;
                }
        }
+       string_list_remove_duplicates(&list, 0);
 
        if (negotiate_only) {
                struct oidset acked_commits = OIDSET_INIT;
index 6f30a4f93a7fa93873e40312a0fcb569056daf88..0feef8caf6d5e27d508b3d4ea3e2679cd50a9966 100644 (file)
@@ -710,6 +710,7 @@ static int do_handle_client(struct fsmonitor_daemon_state *state,
                                  "fsmonitor: unsupported V1 protocol '%s'"),
                                 command);
                do_trivial = 1;
+               do_cookie = 1;
 
        } else {
                /* We have "builtin:*" */
@@ -719,6 +720,7 @@ static int do_handle_client(struct fsmonitor_daemon_state *state,
                                         "fsmonitor: invalid V2 protocol token '%s'",
                                         command);
                        do_trivial = 1;
+                       do_cookie = 1;
 
                } else {
                        /*
@@ -1209,7 +1211,7 @@ static int fsmonitor_run_daemon_1(struct fsmonitor_daemon_state *state)
         * events.
         */
        if (pthread_create(&state->listener_thread, NULL,
-                          fsm_listen__thread_proc, state) < 0) {
+                          fsm_listen__thread_proc, state)) {
                ipc_server_stop_async(state->ipc_server_data);
                err = error(_("could not start fsmonitor listener thread"));
                goto cleanup;
@@ -1220,7 +1222,7 @@ static int fsmonitor_run_daemon_1(struct fsmonitor_daemon_state *state)
         * Start the health thread to watch over our process.
         */
        if (pthread_create(&state->health_thread, NULL,
-                          fsm_health__thread_proc, state) < 0) {
+                          fsm_health__thread_proc, state)) {
                ipc_server_stop_async(state->ipc_server_data);
                err = error(_("could not start fsmonitor health thread"));
                goto cleanup;
index b5063815020d4a54517e8c5a8b2cb6918fe77e7e..44db83f07fd12c535d758f596b6b12f20b7eeb2e 100644 (file)
@@ -27,6 +27,7 @@ static int hash_literally(struct object_id *oid, int fd, const char *type, unsig
        else
                ret = write_object_file_literally(buf.buf, buf.len, type, oid,
                                                 flags);
+       close(fd);
        strbuf_release(&buf);
        return ret;
 }
index b6530d189ad08dc76367fca61cc8eae95be13ffd..f95b7965c58efe779d91a60d2b91b36742ac31f7 100644 (file)
@@ -7,7 +7,7 @@
 #include "strvec.h"
 
 #define BUILTIN_HOOK_RUN_USAGE \
-       N_("git hook run [--ignore-missing] <hook-name> [-- <hook-args>]")
+       N_("git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>]")
 
 static const char * const builtin_hook_usage[] = {
        BUILTIN_HOOK_RUN_USAGE,
@@ -28,6 +28,8 @@ static int run(int argc, const char **argv, const char *prefix)
        struct option run_options[] = {
                OPT_BOOL(0, "ignore-missing", &ignore_missing,
                         N_("silently ignore missing requested <hook-name>")),
+               OPT_STRING(0, "to-stdin", &opt.path_to_stdin, N_("path"),
+                          N_("file to read into hooks' stdin")),
                OPT_END(),
        };
        int ret;
index 89447a50838562795bb80172db8102ff7a42e91b..04412dd9c936569506c1ca932a7804059a57904f 100644 (file)
@@ -52,6 +52,7 @@ static int decoration_style;
 static int decoration_given;
 static int use_mailmap_config = 1;
 static unsigned int force_in_body_from;
+static int stdout_mboxrd;
 static const char *fmt_patch_subject_prefix = "PATCH";
 static int fmt_patch_name_max = FORMAT_PATCH_NAME_MAX_DEFAULT;
 static const char *fmt_pretty;
@@ -1077,6 +1078,10 @@ static int git_format_config(const char *var, const char *value, void *cb)
                cover_from_description_mode = parse_cover_from_description(value);
                return 0;
        }
+       if (!strcmp(var, "format.mboxrd")) {
+               stdout_mboxrd = git_config_bool(var, value);
+               return 0;
+       }
 
        return git_log_config(var, value, cb);
 }
@@ -1870,6 +1875,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        struct strbuf rdiff1 = STRBUF_INIT;
        struct strbuf rdiff2 = STRBUF_INIT;
        struct strbuf rdiff_title = STRBUF_INIT;
+       struct strbuf sprefix = STRBUF_INIT;
        int creation_factor = -1;
 
        const struct option builtin_format_patch_options[] = {
@@ -2010,12 +2016,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
 
        if (reroll_count) {
-               struct strbuf sprefix = STRBUF_INIT;
-
                strbuf_addf(&sprefix, "%s v%s",
                            rev.subject_prefix, reroll_count);
                rev.reroll_count = reroll_count;
-               rev.subject_prefix = strbuf_detach(&sprefix, NULL);
+               rev.subject_prefix = sprefix.buf;
        }
 
        for (i = 0; i < extra_hdr.nr; i++) {
@@ -2105,6 +2109,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                                  rev.diffopt.close_file, "--output",
                                  !!output_directory, "--output-directory");
 
+       if (use_stdout && stdout_mboxrd)
+               rev.commit_format = CMIT_FMT_MBOXRD;
+
        if (use_stdout) {
                setup_pager();
        } else if (!rev.diffopt.close_file) {
@@ -2376,6 +2383,7 @@ done:
        strbuf_release(&rdiff1);
        strbuf_release(&rdiff2);
        strbuf_release(&rdiff_title);
+       strbuf_release(&sprefix);
        free(to_free);
        if (rev.ref_message_ids)
                string_list_clear(rev.ref_message_ids, 0);
index 4cf8a2364835c9fc1ae39b8d52fd09a8aeeb368e..a03b559ecaab4caeba9a9a18bb268f22274e7b2a 100644 (file)
@@ -613,6 +613,7 @@ void overlay_tree_on_index(struct index_state *istate,
        if (!fn)
                fn = read_one_entry_quick;
        err = read_tree(the_repository, tree, &pathspec, fn, istate);
+       clear_pathspec(&pathspec);
        if (err)
                die("unable to read tree entries %s", tree_name);
 
index 5d5ac0387165fa8f4fb5a4c22b0f0f002f0e1545..6516177348996cd6e73d69e5c3884e53e6dfda73 100644 (file)
@@ -8,7 +8,7 @@
 static const char * const ls_remote_usage[] = {
        N_("git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
           "              [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
-          "              [--symref] [<repository> [<refs>...]]"),
+          "              [--symref] [<repository> [<patterns>...]]"),
        NULL
 };
 
index c3ea09281afebe0c9aefb690f6f10044142a4d1a..8cc8c995df9399cd84465060965d065d33cd5d3b 100644 (file)
 #include "parse-options.h"
 #include "pathspec.h"
 
-static int line_termination = '\n';
-#define LS_RECURSIVE 1
-#define LS_TREE_ONLY (1 << 1)
-#define LS_SHOW_TREES (1 << 2)
-static int abbrev;
-static int ls_options;
-static struct pathspec pathspec;
-static int chomp_prefix;
-static const char *ls_tree_prefix;
-static const char *format;
-struct show_tree_data {
-       unsigned mode;
-       enum object_type type;
-       const struct object_id *oid;
-       const char *pathname;
-       struct strbuf *base;
-};
-
-static const  char * const ls_tree_usage[] = {
+static const char * const ls_tree_usage[] = {
        N_("git ls-tree [<options>] <tree-ish> [<path>...]"),
        NULL
 };
 
-static enum ls_tree_cmdmode {
-       MODE_DEFAULT = 0,
-       MODE_LONG,
-       MODE_NAME_ONLY,
-       MODE_NAME_STATUS,
-       MODE_OBJECT_ONLY,
-} cmdmode;
-
 static void expand_objectsize(struct strbuf *line, const struct object_id *oid,
                              const enum object_type type, unsigned int padded)
 {
@@ -64,10 +38,34 @@ static void expand_objectsize(struct strbuf *line, const struct object_id *oid,
        }
 }
 
+struct ls_tree_options {
+       unsigned null_termination:1;
+       int abbrev;
+       enum ls_tree_path_options {
+               LS_RECURSIVE = 1 << 0,
+               LS_TREE_ONLY = 1 << 1,
+               LS_SHOW_TREES = 1 << 2,
+       } ls_options;
+       struct pathspec pathspec;
+       int chomp_prefix;
+       const char *ls_tree_prefix;
+       const char *format;
+};
+
+struct show_tree_data {
+       struct ls_tree_options *options;
+       unsigned mode;
+       enum object_type type;
+       const struct object_id *oid;
+       const char *pathname;
+       struct strbuf *base;
+};
+
 static size_t expand_show_tree(struct strbuf *sb, const char *start,
                               void *context)
 {
        struct show_tree_data *data = context;
+       struct ls_tree_options *options = data->options;
        const char *end;
        const char *p;
        unsigned int errlen;
@@ -92,18 +90,18 @@ static size_t expand_show_tree(struct strbuf *sb, const char *start,
        } else if (skip_prefix(start, "(objectsize)", &p)) {
                expand_objectsize(sb, data->oid, data->type, 0);
        } else if (skip_prefix(start, "(objectname)", &p)) {
-               strbuf_add_unique_abbrev(sb, data->oid, abbrev);
+               strbuf_add_unique_abbrev(sb, data->oid, options->abbrev);
        } else if (skip_prefix(start, "(path)", &p)) {
                const char *name = data->base->buf;
-               const char *prefix = chomp_prefix ? ls_tree_prefix : NULL;
-               struct strbuf quoted = STRBUF_INIT;
+               const char *prefix = options->chomp_prefix ? options->ls_tree_prefix : NULL;
                struct strbuf sbuf = STRBUF_INIT;
+               size_t baselen = data->base->len;
+
                strbuf_addstr(data->base, data->pathname);
                name = relative_path(data->base->buf, prefix, &sbuf);
-               quote_c_style(name, &quoted, NULL, 0);
-               strbuf_addbuf(sb, &quoted);
+               quote_c_style(name, sb, NULL, 0);
+               strbuf_setlen(data->base, baselen);
                strbuf_release(&sbuf);
-               strbuf_release(&quoted);
        } else {
                errlen = (unsigned long)len;
                die(_("bad ls-tree format: %%%.*s"), errlen, start);
@@ -111,18 +109,19 @@ static size_t expand_show_tree(struct strbuf *sb, const char *start,
        return len;
 }
 
-static int show_recursive(const char *base, size_t baselen, const char *pathname)
+static int show_recursive(struct ls_tree_options *options, const char *base,
+                         size_t baselen, const char *pathname)
 {
        int i;
 
-       if (ls_options & LS_RECURSIVE)
+       if (options->ls_options & LS_RECURSIVE)
                return 1;
 
-       if (!pathspec.nr)
+       if (!options->pathspec.nr)
                return 0;
 
-       for (i = 0; i < pathspec.nr; i++) {
-               const char *spec = pathspec.items[i].match;
+       for (i = 0; i < options->pathspec.nr; i++) {
+               const char *spec = options->pathspec.items[i].match;
                size_t len, speclen;
 
                if (strncmp(base, spec, baselen))
@@ -142,14 +141,14 @@ static int show_recursive(const char *base, size_t baselen, const char *pathname
 }
 
 static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,
-                        const char *pathname, unsigned mode, void *context UNUSED)
+                        const char *pathname, unsigned mode, void *context)
 {
-       size_t baselen;
+       struct ls_tree_options *options = context;
        int recurse = 0;
        struct strbuf sb = STRBUF_INIT;
        enum object_type type = object_type(mode);
-
-       struct show_tree_data data = {
+       struct show_tree_data cb_data = {
+               .options = options,
                .mode = mode,
                .type = type,
                .oid = oid,
@@ -157,94 +156,100 @@ static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,
                .base = base,
        };
 
-       if (type == OBJ_TREE && show_recursive(base->buf, base->len, pathname))
+       if (type == OBJ_TREE && show_recursive(options, base->buf, base->len, pathname))
                recurse = READ_TREE_RECURSIVE;
-       if (type == OBJ_TREE && recurse && !(ls_options & LS_SHOW_TREES))
+       if (type == OBJ_TREE && recurse && !(options->ls_options & LS_SHOW_TREES))
                return recurse;
-       if (type == OBJ_BLOB && (ls_options & LS_TREE_ONLY))
+       if (type == OBJ_BLOB && (options->ls_options & LS_TREE_ONLY))
                return 0;
 
-       baselen = base->len;
-       strbuf_expand(&sb, format, expand_show_tree, &data);
-       strbuf_addch(&sb, line_termination);
+       strbuf_expand(&sb, options->format, expand_show_tree, &cb_data);
+       strbuf_addch(&sb, options->null_termination ? '\0' : '\n');
        fwrite(sb.buf, sb.len, 1, stdout);
        strbuf_release(&sb);
-       strbuf_setlen(base, baselen);
        return recurse;
 }
 
-static int show_tree_common(struct show_tree_data *data, int *recurse,
-                           const struct object_id *oid, struct strbuf *base,
-                           const char *pathname, unsigned mode)
+static int show_tree_common(struct ls_tree_options *options, int *recurse,
+                           struct strbuf *base, const char *pathname,
+                           enum object_type type)
 {
-       enum object_type type = object_type(mode);
        int ret = -1;
-
        *recurse = 0;
-       data->mode = mode;
-       data->type = type;
-       data->oid = oid;
-       data->pathname = pathname;
-       data->base = base;
 
        if (type == OBJ_BLOB) {
-               if (ls_options & LS_TREE_ONLY)
+               if (options->ls_options & LS_TREE_ONLY)
                        ret = 0;
        } else if (type == OBJ_TREE &&
-                  show_recursive(base->buf, base->len, pathname)) {
+                  show_recursive(options, base->buf, base->len, pathname)) {
                *recurse = READ_TREE_RECURSIVE;
-               if (!(ls_options & LS_SHOW_TREES))
+               if (!(options->ls_options & LS_SHOW_TREES))
                        ret = *recurse;
        }
 
        return ret;
 }
 
-static void show_tree_common_default_long(struct strbuf *base,
+static void show_tree_common_default_long(struct ls_tree_options *options,
+                                         struct strbuf *base,
                                          const char *pathname,
                                          const size_t baselen)
 {
+       const char *prefix = options->chomp_prefix ? options->ls_tree_prefix : NULL;
+
        strbuf_addstr(base, pathname);
-       write_name_quoted_relative(base->buf,
-                                  chomp_prefix ? ls_tree_prefix : NULL, stdout,
-                                  line_termination);
+
+       if (options->null_termination) {
+               struct strbuf sb = STRBUF_INIT;
+               const char *name = relative_path(base->buf, prefix, &sb);
+
+               fputs(name, stdout);
+               fputc('\0', stdout);
+
+               strbuf_release(&sb);
+       } else {
+               write_name_quoted_relative(base->buf, prefix, stdout, '\n');
+       }
+
        strbuf_setlen(base, baselen);
 }
 
 static int show_tree_default(const struct object_id *oid, struct strbuf *base,
                             const char *pathname, unsigned mode,
-                            void *context UNUSED)
+                            void *context)
 {
+       struct ls_tree_options *options = context;
        int early;
        int recurse;
-       struct show_tree_data data = { 0 };
+       enum object_type type = object_type(mode);
 
-       early = show_tree_common(&data, &recurse, oid, base, pathname, mode);
+       early = show_tree_common(options, &recurse, base, pathname, type);
        if (early >= 0)
                return early;
 
-       printf("%06o %s %s\t", data.mode, type_name(data.type),
-              find_unique_abbrev(data.oid, abbrev));
-       show_tree_common_default_long(base, pathname, data.base->len);
+       printf("%06o %s %s\t", mode, type_name(object_type(mode)),
+              find_unique_abbrev(oid, options->abbrev));
+       show_tree_common_default_long(options, base, pathname, base->len);
        return recurse;
 }
 
 static int show_tree_long(const struct object_id *oid, struct strbuf *base,
                          const char *pathname, unsigned mode,
-                         void *context UNUSED)
+                         void *context)
 {
+       struct ls_tree_options *options = context;
        int early;
        int recurse;
-       struct show_tree_data data = { 0 };
        char size_text[24];
+       enum object_type type = object_type(mode);
 
-       early = show_tree_common(&data, &recurse, oid, base, pathname, mode);
+       early = show_tree_common(options, &recurse, base, pathname, type);
        if (early >= 0)
                return early;
 
-       if (data.type == OBJ_BLOB) {
+       if (type == OBJ_BLOB) {
                unsigned long size;
-               if (oid_object_info(the_repository, data.oid, &size) == OBJ_BAD)
+               if (oid_object_info(the_repository, oid, &size) == OBJ_BAD)
                        xsnprintf(size_text, sizeof(size_text), "BAD");
                else
                        xsnprintf(size_text, sizeof(size_text),
@@ -253,49 +258,76 @@ static int show_tree_long(const struct object_id *oid, struct strbuf *base,
                xsnprintf(size_text, sizeof(size_text), "-");
        }
 
-       printf("%06o %s %s %7s\t", data.mode, type_name(data.type),
-              find_unique_abbrev(data.oid, abbrev), size_text);
-       show_tree_common_default_long(base, pathname, data.base->len);
+       printf("%06o %s %s %7s\t", mode, type_name(type),
+              find_unique_abbrev(oid, options->abbrev), size_text);
+       show_tree_common_default_long(options, base, pathname, base->len);
        return recurse;
 }
 
 static int show_tree_name_only(const struct object_id *oid, struct strbuf *base,
                               const char *pathname, unsigned mode,
-                              void *context UNUSED)
+                              void *context)
 {
+       struct ls_tree_options *options = context;
        int early;
        int recurse;
        const size_t baselen = base->len;
-       struct show_tree_data data = { 0 };
+       enum object_type type = object_type(mode);
+       const char *prefix;
 
-       early = show_tree_common(&data, &recurse, oid, base, pathname, mode);
+       early = show_tree_common(options, &recurse, base, pathname, type);
        if (early >= 0)
                return early;
 
+       prefix = options->chomp_prefix ? options->ls_tree_prefix : NULL;
        strbuf_addstr(base, pathname);
-       write_name_quoted_relative(base->buf,
-                                  chomp_prefix ? ls_tree_prefix : NULL,
-                                  stdout, line_termination);
+       if (options->null_termination) {
+               struct strbuf sb = STRBUF_INIT;
+               const char *name = relative_path(base->buf, prefix, &sb);
+
+               fputs(name, stdout);
+               fputc('\0', stdout);
+
+               strbuf_release(&sb);
+       } else {
+               write_name_quoted_relative(base->buf, prefix, stdout, '\n');
+       }
        strbuf_setlen(base, baselen);
        return recurse;
 }
 
 static int show_tree_object(const struct object_id *oid, struct strbuf *base,
                            const char *pathname, unsigned mode,
-                           void *context UNUSED)
+                           void *context)
 {
+       struct ls_tree_options *options = context;
        int early;
        int recurse;
-       struct show_tree_data data = { 0 };
+       enum object_type type = object_type(mode);
+       const char *str;
 
-       early = show_tree_common(&data, &recurse, oid, base, pathname, mode);
+       early = show_tree_common(options, &recurse, base, pathname, type);
        if (early >= 0)
                return early;
 
-       printf("%s%c", find_unique_abbrev(oid, abbrev), line_termination);
+       str = find_unique_abbrev(oid, options->abbrev);
+       if (options->null_termination) {
+               fputs(str, stdout);
+               fputc('\0', stdout);
+       } else  {
+               puts(str);
+       }
        return recurse;
 }
 
+enum ls_tree_cmdmode {
+       MODE_DEFAULT = 0,
+       MODE_LONG,
+       MODE_NAME_ONLY,
+       MODE_NAME_STATUS,
+       MODE_OBJECT_ONLY,
+};
+
 struct ls_tree_cmdmode_to_fmt {
        enum ls_tree_cmdmode mode;
        const char *const fmt;
@@ -335,15 +367,18 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
        struct tree *tree;
        int i, full_tree = 0;
        read_tree_fn_t fn = NULL;
+       enum ls_tree_cmdmode cmdmode = MODE_DEFAULT;
+       int null_termination = 0;
+       struct ls_tree_options options = { 0 };
        const struct option ls_tree_options[] = {
-               OPT_BIT('d', NULL, &ls_options, N_("only show trees"),
+               OPT_BIT('d', NULL, &options.ls_options, N_("only show trees"),
                        LS_TREE_ONLY),
-               OPT_BIT('r', NULL, &ls_options, N_("recurse into subtrees"),
+               OPT_BIT('r', NULL, &options.ls_options, N_("recurse into subtrees"),
                        LS_RECURSIVE),
-               OPT_BIT('t', NULL, &ls_options, N_("show trees when recursing"),
+               OPT_BIT('t', NULL, &options.ls_options, N_("show trees when recursing"),
                        LS_SHOW_TREES),
-               OPT_SET_INT('z', NULL, &line_termination,
-                           N_("terminate entries with NUL byte"), 0),
+               OPT_BOOL('z', NULL, &null_termination,
+                        N_("terminate entries with NUL byte")),
                OPT_CMDMODE('l', "long", &cmdmode, N_("include object size"),
                            MODE_LONG),
                OPT_CMDMODE(0, "name-only", &cmdmode, N_("list only filenames"),
@@ -352,29 +387,32 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
                            MODE_NAME_STATUS),
                OPT_CMDMODE(0, "object-only", &cmdmode, N_("list only objects"),
                            MODE_OBJECT_ONLY),
-               OPT_SET_INT(0, "full-name", &chomp_prefix,
+               OPT_SET_INT(0, "full-name", &options.chomp_prefix,
                            N_("use full path names"), 0),
                OPT_BOOL(0, "full-tree", &full_tree,
                         N_("list entire tree; not just current directory "
                            "(implies --full-name)")),
-               OPT_STRING_F(0, "format", &format, N_("format"),
+               OPT_STRING_F(0, "format", &options.format, N_("format"),
                                         N_("format to use for the output"),
                                         PARSE_OPT_NONEG),
-               OPT__ABBREV(&abbrev),
+               OPT__ABBREV(&options.abbrev),
                OPT_END()
        };
        struct ls_tree_cmdmode_to_fmt *m2f = ls_tree_cmdmode_format;
+       int ret;
 
        git_config(git_default_config, NULL);
-       ls_tree_prefix = prefix;
+       options.ls_tree_prefix = prefix;
        if (prefix)
-               chomp_prefix = strlen(prefix);
+               options.chomp_prefix = strlen(prefix);
 
        argc = parse_options(argc, argv, prefix, ls_tree_options,
                             ls_tree_usage, 0);
+       options.null_termination = null_termination;
+
        if (full_tree) {
-               ls_tree_prefix = prefix = NULL;
-               chomp_prefix = 0;
+               options.ls_tree_prefix = prefix = NULL;
+               options.chomp_prefix = 0;
        }
        /*
         * We wanted to detect conflicts between --name-only and
@@ -386,10 +424,10 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
 
        /* -d -r should imply -t, but -d by itself should not have to. */
        if ( (LS_TREE_ONLY|LS_RECURSIVE) ==
-           ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))
-               ls_options |= LS_SHOW_TREES;
+           ((LS_TREE_ONLY|LS_RECURSIVE) & options.ls_options))
+               options.ls_options |= LS_SHOW_TREES;
 
-       if (format && cmdmode)
+       if (options.format && cmdmode)
                usage_msg_opt(
                        _("--format can't be combined with other format-altering options"),
                        ls_tree_usage, ls_tree_options);
@@ -404,13 +442,13 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
         * cannot be lifted until it is converted to use
         * match_pathspec() or tree_entry_interesting()
         */
-       parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC &
-                                 ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL),
+       parse_pathspec(&options.pathspec, PATHSPEC_ALL_MAGIC &
+                      ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL),
                       PATHSPEC_PREFER_CWD,
                       prefix, argv + 1);
-       for (i = 0; i < pathspec.nr; i++)
-               pathspec.items[i].nowildcard_len = pathspec.items[i].len;
-       pathspec.has_wildcard = 0;
+       for (i = 0; i < options.pathspec.nr; i++)
+               options.pathspec.items[i].nowildcard_len = options.pathspec.items[i].len;
+       options.pathspec.has_wildcard = 0;
        tree = parse_tree_indirect(&oid);
        if (!tree)
                die("not a tree object");
@@ -420,11 +458,11 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
         */
        while (m2f) {
                if (!m2f->fmt) {
-                       fn = format ? show_tree_fmt : show_tree_default;
-               } else if (format && !strcmp(format, m2f->fmt)) {
+                       fn = options.format ? show_tree_fmt : show_tree_default;
+               } else if (options.format && !strcmp(options.format, m2f->fmt)) {
                        cmdmode = m2f->mode;
                        fn = m2f->fn;
-               } else if (!format && cmdmode == m2f->mode) {
+               } else if (!options.format && cmdmode == m2f->mode) {
                        fn = m2f->fn;
                } else {
                        m2f++;
@@ -433,5 +471,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
                break;
        }
 
-       return !!read_tree(the_repository, tree, &pathspec, fn, NULL);
+       ret = !!read_tree(the_repository, tree, &options.pathspec, fn, &options);
+       clear_pathspec(&options.pathspec);
+       return ret;
 }
index e3767087bbfdeb065e9a2806df4ba99b922eb2ec..828dc81c426514d822e6bbce172495373f9a92c5 100644 (file)
@@ -3,6 +3,7 @@
 #include "tree-walk.h"
 #include "xdiff-interface.h"
 #include "help.h"
+#include "commit.h"
 #include "commit-reach.h"
 #include "merge-ort.h"
 #include "object-store.h"
@@ -98,7 +99,7 @@ static void *origin(struct merge_list *entry, unsigned long *size)
        return NULL;
 }
 
-static int show_outf(void *priv_, mmbuffer_t *mb, int nbuf)
+static int show_outf(void *priv UNUSED, mmbuffer_t *mb, int nbuf)
 {
        int i;
        for (i = 0; i < nbuf; i++)
@@ -406,6 +407,7 @@ struct merge_tree_options {
 };
 
 static int real_merge(struct merge_tree_options *o,
+                     const char *merge_base,
                      const char *branch1, const char *branch2,
                      const char *prefix)
 {
@@ -432,16 +434,31 @@ static int real_merge(struct merge_tree_options *o,
        opt.branch1 = branch1;
        opt.branch2 = branch2;
 
-       /*
-        * Get the merge bases, in reverse order; see comment above
-        * merge_incore_recursive in merge-ort.h
-        */
-       merge_bases = get_merge_bases(parent1, parent2);
-       if (!merge_bases && !o->allow_unrelated_histories)
-               die(_("refusing to merge unrelated histories"));
-       merge_bases = reverse_commit_list(merge_bases);
+       if (merge_base) {
+               struct commit *base_commit;
+               struct tree *base_tree, *parent1_tree, *parent2_tree;
+
+               base_commit = lookup_commit_reference_by_name(merge_base);
+               if (!base_commit)
+                       die(_("could not lookup commit %s"), merge_base);
+
+               opt.ancestor = merge_base;
+               base_tree = get_commit_tree(base_commit);
+               parent1_tree = get_commit_tree(parent1);
+               parent2_tree = get_commit_tree(parent2);
+               merge_incore_nonrecursive(&opt, base_tree, parent1_tree, parent2_tree, &result);
+       } else {
+               /*
+                * Get the merge bases, in reverse order; see comment above
+                * merge_incore_recursive in merge-ort.h
+                */
+               merge_bases = get_merge_bases(parent1, parent2);
+               if (!merge_bases && !o->allow_unrelated_histories)
+                       die(_("refusing to merge unrelated histories"));
+               merge_bases = reverse_commit_list(merge_bases);
+               merge_incore_recursive(&opt, merge_bases, parent1, parent2, &result);
+       }
 
-       merge_incore_recursive(&opt, merge_bases, parent1, parent2, &result);
        if (result.clean < 0)
                die(_("failure to merge"));
 
@@ -487,6 +504,7 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
        struct merge_tree_options o = { .show_messages = -1 };
        int expected_remaining_argc;
        int original_argc;
+       const char *merge_base = NULL;
 
        const char * const merge_tree_usage[] = {
                N_("git merge-tree [--write-tree] [<options>] <branch1> <branch2>"),
@@ -515,6 +533,10 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
                           &o.use_stdin,
                           N_("perform multiple merges, one per line of input"),
                           PARSE_OPT_NONEG),
+               OPT_STRING(0, "merge-base",
+                          &merge_base,
+                          N_("commit"),
+                          N_("specify a merge-base for the merge")),
                OPT_END()
        };
 
@@ -529,16 +551,35 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
 
                if (o.mode == MODE_TRIVIAL)
                        die(_("--trivial-merge is incompatible with all other options"));
+               if (merge_base)
+                       die(_("--merge-base is incompatible with --stdin"));
                line_termination = '\0';
                while (strbuf_getline_lf(&buf, stdin) != EOF) {
                        struct strbuf **split;
                        int result;
+                       const char *input_merge_base = NULL;
 
                        split = strbuf_split(&buf, ' ');
-                       if (!split[0] || !split[1] || split[2])
+                       if (!split[0] || !split[1])
                                die(_("malformed input line: '%s'."), buf.buf);
                        strbuf_rtrim(split[0]);
-                       result = real_merge(&o, split[0]->buf, split[1]->buf, prefix);
+                       strbuf_rtrim(split[1]);
+
+                       /* parse the merge-base */
+                       if (!strcmp(split[1]->buf, "--")) {
+                               input_merge_base = split[0]->buf;
+                       }
+
+                       if (input_merge_base && split[2] && split[3] && !split[4]) {
+                               strbuf_rtrim(split[2]);
+                               strbuf_rtrim(split[3]);
+                               result = real_merge(&o, input_merge_base, split[2]->buf, split[3]->buf, prefix);
+                       } else if (!input_merge_base && !split[2]) {
+                               result = real_merge(&o, NULL, split[0]->buf, split[1]->buf, prefix);
+                       } else {
+                               die(_("malformed input line: '%s'."), buf.buf);
+                       }
+
                        if (result < 0)
                                die(_("merging cannot continue; got unclean result of %d"), result);
                        strbuf_list_free(split);
@@ -581,7 +622,7 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
 
        /* Do the relevant type of merge */
        if (o.mode == MODE_REAL)
-               return real_merge(&o, argv[0], argv[1], prefix);
+               return real_merge(&o, merge_base, argv[0], argv[1], prefix);
        else
                return trivial_merge(argv[0], argv[1], argv[2]);
 }
index dd474371a256a6170e4f3f1073db2dae2a0f8791..0a3c10a0966bdfc8f354cffb19d8f27987298600 100644 (file)
@@ -6,7 +6,7 @@
  * Based on git-merge.sh by Junio C Hamano.
  */
 
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "cache.h"
 #include "config.h"
 #include "parse-options.h"
@@ -188,7 +188,7 @@ static struct strategy *get_strategy(const char *name)
                for (i = 0; i < main_cmds.cnt; i++) {
                        int j, found = 0;
                        struct cmdname *ent = main_cmds.names[i];
-                       for (j = 0; j < ARRAY_SIZE(all_strategy); j++)
+                       for (j = 0; !found && j < ARRAY_SIZE(all_strategy); j++)
                                if (!strncmp(ent->name, all_strategy[j].name, ent->len)
                                                && !all_strategy[j].name[ent->len])
                                        found = 1;
@@ -390,8 +390,8 @@ static void restore_state(const struct object_id *head,
        run_command(&cmd);
 
 refresh_cache:
-       discard_cache();
-       if (read_cache() < 0)
+       discard_index(&the_index);
+       if (repo_read_index(the_repository) < 0)
                die(_("could not read index"));
 }
 
@@ -706,7 +706,7 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
 
 static void write_tree_trivial(struct object_id *oid)
 {
-       if (write_cache_as_tree(oid, 0, NULL))
+       if (write_index_as_tree(oid, &the_index, get_index_file(), 0, NULL))
                die(_("git write-tree failed to write a tree"));
 }
 
@@ -776,7 +776,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
 }
 
 static void count_diff_files(struct diff_queue_struct *q,
-                            struct diff_options *opt, void *data)
+                            struct diff_options *opt UNUSED, void *data)
 {
        int *count = data;
 
@@ -1560,7 +1560,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        !common->next &&
                        oideq(&common->item->object.oid, &head_commit->object.oid)) {
                /* Again the most common case of merging one remote. */
-               struct strbuf msg = STRBUF_INIT;
+               const char *msg = have_message ?
+                       "Fast-forward (no commit created; -m option ignored)" :
+                       "Fast-forward";
                struct commit *commit;
 
                if (verbosity >= 0) {
@@ -1570,10 +1572,6 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                               find_unique_abbrev(&remoteheads->item->object.oid,
                                                  DEFAULT_ABBREV));
                }
-               strbuf_addstr(&msg, "Fast-forward");
-               if (have_message)
-                       strbuf_addstr(&msg,
-                               " (no commit created; -m option ignored)");
                commit = remoteheads->item;
                if (!commit) {
                        ret = 1;
@@ -1592,9 +1590,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        goto done;
                }
 
-               finish(head_commit, remoteheads, &commit->object.oid, msg.buf);
+               finish(head_commit, remoteheads, &commit->object.oid, msg);
                remove_merge_branch_state(the_repository);
-               strbuf_release(&msg);
                goto done;
        } else if (!remoteheads->next && common->next)
                ;
@@ -1621,7 +1618,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                                error(_("Your local changes to the following files would be overwritten by merge:\n  %s"),
                                      sb.buf);
                                strbuf_release(&sb);
-                               return 2;
+                               ret = 2;
+                               goto done;
                        }
 
                        /* See if it is really trivial. */
@@ -1789,5 +1787,6 @@ done:
        }
        strbuf_release(&buf);
        free(branch_to_free);
+       discard_index(&the_index);
        return ret;
 }
index 19790ce38fa265d1b2b02f738ad6351492752e10..edd7b931fdb95f84eec899acbf0b4ae2a120f2d7 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2006 Johannes Schindelin
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "config.h"
 #include "pathspec.h"
@@ -489,7 +489,8 @@ remove_entry:
                        if ((mode & SPARSE) &&
                            path_in_sparse_checkout(dst, &the_index)) {
                                /* from out-of-cone to in-cone */
-                               int dst_pos = cache_name_pos(dst, strlen(dst));
+                               int dst_pos = index_name_pos(&the_index, dst,
+                                                            strlen(dst));
                                struct cache_entry *dst_ce = the_index.cache[dst_pos];
 
                                dst_ce->ce_flags &= ~CE_SKIP_WORKTREE;
@@ -500,7 +501,8 @@ remove_entry:
                                   !(mode & SPARSE) &&
                                   !path_in_sparse_checkout(dst, &the_index)) {
                                /* from in-cone to out-of-cone */
-                               int dst_pos = cache_name_pos(dst, strlen(dst));
+                               int dst_pos = index_name_pos(&the_index, dst,
+                                                            strlen(dst));
                                struct cache_entry *dst_ce = the_index.cache[dst_pos];
 
                                /*
index 15535e914a6939d661296798e0b325da9f1ac7b6..97959bfaf9eb018dbe83780f60093e3f04f7be42 100644 (file)
@@ -108,19 +108,11 @@ static int is_better_name(struct rev_name *name,
        int name_distance = effective_distance(name->distance, name->generation);
        int new_distance = effective_distance(distance, generation);
 
-       /*
-        * When comparing names based on tags, prefer names
-        * based on the older tag, even if it is farther away.
-        */
+       /* If both are tags, we prefer the nearer one. */
        if (from_tag && name->from_tag)
-               return (name->taggerdate > taggerdate ||
-                       (name->taggerdate == taggerdate &&
-                        name_distance > new_distance));
+               return name_distance > new_distance;
 
-       /*
-        * We know that at least one of them is a non-tag at this point.
-        * favor a tag over a non-tag.
-        */
+       /* Favor a tag over a non-tag. */
        if (name->from_tag != from_tag)
                return from_tag;
 
@@ -273,17 +265,6 @@ static int subpath_matches(const char *path, const char *filter)
        return -1;
 }
 
-static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous)
-{
-       if (shorten_unambiguous)
-               refname = shorten_unambiguous_ref(refname, 0);
-       else if (skip_prefix(refname, "refs/heads/", &refname))
-               ; /* refname already advanced */
-       else
-               skip_prefix(refname, "refs/", &refname);
-       return refname;
-}
-
 struct name_ref_data {
        int tags_only;
        int name_only;
@@ -309,11 +290,19 @@ static void add_to_tip_table(const struct object_id *oid, const char *refname,
                             int shorten_unambiguous, struct commit *commit,
                             timestamp_t taggerdate, int from_tag, int deref)
 {
-       refname = name_ref_abbrev(refname, shorten_unambiguous);
+       char *short_refname = NULL;
+
+       if (shorten_unambiguous)
+               short_refname = shorten_unambiguous_ref(refname, 0);
+       else if (skip_prefix(refname, "refs/heads/", &refname))
+               ; /* refname already advanced */
+       else
+               skip_prefix(refname, "refs/", &refname);
 
        ALLOC_GROW(tip_table.table, tip_table.nr + 1, tip_table.alloc);
        oidcpy(&tip_table.table[tip_table.nr].oid, oid);
-       tip_table.table[tip_table.nr].refname = xstrdup(refname);
+       tip_table.table[tip_table.nr].refname = short_refname ?
+               short_refname : xstrdup(refname);
        tip_table.table[tip_table.nr].commit = commit;
        tip_table.table[tip_table.nr].taggerdate = taggerdate;
        tip_table.table[tip_table.nr].from_tag = from_tag;
index 573d0b20b76b5115c99781b28f65ae1b5d7d8dc3..74a167a180cd4080919d8c3a614c098ad96a2b42 100644 (file)
@@ -929,8 +929,10 @@ static struct object_entry **compute_write_order(void)
         */
        for_each_tag_ref(mark_tagged, NULL);
 
-       if (use_delta_islands)
+       if (use_delta_islands) {
                max_layers = compute_pack_layers(&to_pack);
+               free_island_marks();
+       }
 
        ALLOC_ARRAY(wo, to_pack.nr_objects);
        wo_end = 0;
@@ -1318,7 +1320,7 @@ static int no_try_delta(const char *path)
 
        if (!check)
                check = attr_check_initl("delta", NULL);
-       git_check_attr(the_repository->index, path, check);
+       git_check_attr(the_repository->index, NULL, path, check);
        if (ATTR_FALSE(check->items[0].value))
                return 1;
        return 0;
@@ -1708,17 +1710,14 @@ static void pbase_tree_put(struct pbase_tree_cache *cache)
        free(cache);
 }
 
-static int name_cmp_len(const char *name)
+static size_t name_cmp_len(const char *name)
 {
-       int i;
-       for (i = 0; name[i] && name[i] != '\n' && name[i] != '/'; i++)
-               ;
-       return i;
+       return strcspn(name, "\n/");
 }
 
 static void add_pbase_object(struct tree_desc *tree,
                             const char *name,
-                            int cmplen,
+                            size_t cmplen,
                             const char *fullname)
 {
        struct name_entry entry;
@@ -1743,7 +1742,7 @@ static void add_pbase_object(struct tree_desc *tree,
                        struct tree_desc sub;
                        struct pbase_tree_cache *tree;
                        const char *down = name+cmplen+1;
-                       int downlen = name_cmp_len(down);
+                       size_t downlen = name_cmp_len(down);
 
                        tree = pbase_tree_get(&entry.oid);
                        if (!tree)
@@ -1795,7 +1794,7 @@ static int check_pbase_path(unsigned hash)
 static void add_preferred_base_object(const char *name)
 {
        struct pbase_tree *it;
-       int cmplen;
+       size_t cmplen;
        unsigned hash = pack_name_hash(name);
 
        if (!num_preferred_base || check_pbase_path(hash))
@@ -4149,21 +4148,6 @@ static int option_parse_cruft_expiration(const struct option *opt,
        return 0;
 }
 
-struct po_filter_data {
-       unsigned have_revs:1;
-       struct rev_info revs;
-};
-
-static struct list_objects_filter_options *po_filter_revs_init(void *value)
-{
-       struct po_filter_data *data = value;
-
-       repo_init_revisions(the_repository, &data->revs, NULL);
-       data->have_revs = 1;
-
-       return &data->revs.filter;
-}
-
 int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 {
        int use_internal_rev_list = 0;
@@ -4174,7 +4158,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
        int rev_list_index = 0;
        int stdin_packs = 0;
        struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
-       struct po_filter_data pfd = { .have_revs = 0 };
+       struct list_objects_filter_options filter_options =
+               LIST_OBJECTS_FILTER_INIT;
 
        struct option pack_objects_options[] = {
                OPT_SET_INT('q', "quiet", &progress,
@@ -4265,7 +4250,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                              &write_bitmap_index,
                              N_("write a bitmap index if possible"),
                              WRITE_BITMAP_QUIET, PARSE_OPT_HIDDEN),
-               OPT_PARSE_LIST_OBJECTS_FILTER_INIT(&pfd, po_filter_revs_init),
+               OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
                OPT_CALLBACK_F(0, "missing", NULL, N_("action"),
                  N_("handling for missing objects"), PARSE_OPT_NONEG,
                  option_parse_missing_action),
@@ -4385,7 +4370,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
        if (!rev_list_all || !rev_list_reflog || !rev_list_index)
                unpack_unreachable_expiration = 0;
 
-       if (pfd.have_revs && pfd.revs.filter.choice) {
+       if (filter_options.choice) {
                if (!pack_to_stdout)
                        die(_("cannot use --filter without --stdout"));
                if (stdin_packs)
@@ -4472,13 +4457,11 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                read_cruft_objects();
        } else if (!use_internal_rev_list) {
                read_object_list_from_stdin();
-       } else if (pfd.have_revs) {
-               get_object_list(&pfd.revs, rp.nr, rp.v);
-               release_revisions(&pfd.revs);
        } else {
                struct rev_info revs;
 
                repo_init_revisions(the_repository, &revs, NULL);
+               list_objects_filter_copy(&revs.filter, &filter_options);
                get_object_list(&revs, rp.nr, rp.v);
                release_revisions(&revs);
        }
@@ -4513,6 +4496,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                           reuse_packfile_objects);
 
 cleanup:
+       list_objects_filter_release(&filter_options);
        strvec_clear(&rp);
 
        return 0;
index 60ac8017e521f0c7c9192df89c797e1898ec60fd..8f7d326ab3f4b06d3d3d1262d5276fd8c6b2b968 100644 (file)
@@ -63,16 +63,9 @@ static struct refspec rs = REFSPEC_INIT_PUSH;
 static struct string_list push_options_config = STRING_LIST_INIT_DUP;
 
 static void refspec_append_mapped(struct refspec *refspec, const char *ref,
-                                 struct remote *remote, struct ref *local_refs)
+                                 struct remote *remote, struct ref *matched)
 {
        const char *branch_name;
-       struct ref *matched = NULL;
-
-       /* Does "ref" uniquely name our ref? */
-       if (count_refspec_match(ref, local_refs, &matched) != 1) {
-               refspec_append(refspec, ref);
-               return;
-       }
 
        if (remote->push.nr) {
                struct refspec_item query;
@@ -120,15 +113,28 @@ static void set_refspecs(const char **refs, int nr, const char *repo)
                                die(_("--delete only accepts plain target ref names"));
                        refspec_appendf(&rs, ":%s", ref);
                } else if (!strchr(ref, ':')) {
-                       if (!remote) {
-                               /* lazily grab remote and local_refs */
-                               remote = remote_get(repo);
+                       struct ref *matched = NULL;
+
+                       /* lazily grab local_refs */
+                       if (!local_refs)
                                local_refs = get_local_heads();
+
+                       /* Does "ref" uniquely name our ref? */
+                       if (count_refspec_match(ref, local_refs, &matched) != 1) {
+                               refspec_append(&rs, ref);
+                       } else {
+                               /* lazily grab remote */
+                               if (!remote)
+                                       remote = remote_get(repo);
+                               if (!remote)
+                                       BUG("must get a remote for repo '%s'", repo);
+
+                               refspec_append_mapped(&rs, ref, remote, matched);
                        }
-                       refspec_append_mapped(&rs, ref, remote, local_refs);
                } else
                        refspec_append(&rs, ref);
        }
+       free_refs(local_refs);
 }
 
 static int push_url_of_remote(struct remote *remote, const char ***url_p)
index e2a74efb42a795a5139932bd052999d050cab420..aecfae12d3ae0777a57bec2c02dffa7cffde3d8f 100644 (file)
@@ -47,7 +47,7 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)
 
        repo_diff_setup(the_repository, &diffopt);
 
-       options = parse_options_concat(range_diff_options, diffopt.parseopts);
+       options = add_diff_options(range_diff_options, &diffopt);
        argc = parse_options(argc, argv, prefix, options,
                             builtin_range_diff_usage, PARSE_OPT_KEEP_DASHDASH);
 
index f702f9d47bbfae249497aeeb0e2411ea97fc9f48..3ce75417833bf9f57ca1ddfa17a53c67ef39a9d8 100644 (file)
@@ -114,6 +114,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
        int prefix_set = 0;
        struct lock_file lock_file = LOCK_INIT;
        const struct option read_tree_options[] = {
+               OPT__SUPER_PREFIX(&opts.super_prefix),
                OPT_CALLBACK_F(0, "index-output", NULL, N_("file"),
                  N_("write resulting index to <file>"),
                  PARSE_OPT_NONEG, index_output_cb),
index b22768ca5b9fb0ff1b66f0a031bd2494ee1932d7..6635f10d5294d16d017eae225de799efb7b1b9aa 100644 (file)
@@ -113,7 +113,7 @@ struct rebase_options {
        int autostash;
        int committer_date_is_author_date;
        int ignore_date;
-       char *cmd;
+       struct string_list exec;
        int allow_empty_message;
        int rebase_merges, rebase_cousins;
        char *strategy, *strategy_opts;
@@ -122,6 +122,8 @@ struct rebase_options {
        int reapply_cherry_picks;
        int fork_point;
        int update_refs;
+       int config_autosquash;
+       int config_update_refs;
 };
 
 #define REBASE_OPTIONS_INIT {                          \
@@ -131,8 +133,15 @@ struct rebase_options {
                .default_backend = "merge",             \
                .flags = REBASE_NO_QUIET,               \
                .git_am_opts = STRVEC_INIT,             \
+               .exec = STRING_LIST_INIT_NODUP,         \
                .git_format_patch_opt = STRBUF_INIT,    \
                .fork_point = -1,                       \
+               .reapply_cherry_picks = -1,             \
+               .allow_empty_message = 1,               \
+               .autosquash = -1,                       \
+               .config_autosquash = -1,                \
+               .update_refs = -1,                      \
+               .config_update_refs = -1,               \
        }
 
 static struct replay_opts get_replay_opts(const struct rebase_options *opts)
@@ -243,38 +252,22 @@ static int init_basic_state(struct replay_opts *opts, const char *head_name,
        return write_basic_state(opts, head_name, onto, orig_head);
 }
 
-static void split_exec_commands(const char *cmd, struct string_list *commands)
-{
-       if (cmd && *cmd) {
-               string_list_split(commands, cmd, '\n', -1);
-
-               /* rebase.c adds a new line to cmd after every command,
-                * so here the last command is always empty */
-               string_list_remove_empty_items(commands, 0);
-       }
-}
-
 static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
 {
-       int ret;
+       int ret = -1;
        char *revisions = NULL, *shortrevisions = NULL;
        struct strvec make_script_args = STRVEC_INIT;
        struct todo_list todo_list = TODO_LIST_INIT;
        struct replay_opts replay = get_replay_opts(opts);
-       struct string_list commands = STRING_LIST_INIT_DUP;
 
        if (get_revision_ranges(opts->upstream, opts->onto, &opts->orig_head->object.oid,
                                &revisions, &shortrevisions))
-               return -1;
+               goto cleanup;
 
        if (init_basic_state(&replay,
                             opts->head_name ? opts->head_name : "detached HEAD",
-                            opts->onto, &opts->orig_head->object.oid)) {
-               free(revisions);
-               free(shortrevisions);
-
-               return -1;
-       }
+                            opts->onto, &opts->orig_head->object.oid))
+               goto cleanup;
 
        if (!opts->upstream && opts->squash_onto)
                write_file(path_squash_onto(), "%s\n",
@@ -297,14 +290,14 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
                                                &todo_list))
                        BUG("unusable todo list");
 
-               split_exec_commands(opts->cmd, &commands);
                ret = complete_action(the_repository, &replay, flags,
                        shortrevisions, opts->onto_name, opts->onto,
-                       &opts->orig_head->object.oid, &commands,
+                       &opts->orig_head->object.oid, &opts->exec,
                        opts->autosquash, opts->update_refs, &todo_list);
        }
 
-       string_list_clear(&commands, 0);
+cleanup:
+       replay_opts_release(&replay);
        free(revisions);
        free(shortrevisions);
        todo_list_release(&todo_list);
@@ -346,6 +339,7 @@ static int run_sequencer_rebase(struct rebase_options *opts)
                struct replay_opts replay_opts = get_replay_opts(opts);
 
                ret = sequencer_continue(the_repository, &replay_opts);
+               replay_opts_release(&replay_opts);
                break;
        }
        case ACTION_EDIT_TODO:
@@ -561,6 +555,7 @@ static int finish_rebase(struct rebase_options *opts)
 
                replay.action = REPLAY_INTERACTIVE_REBASE;
                ret = sequencer_remove_state(&replay);
+               replay_opts_release(&replay);
        } else {
                strbuf_addstr(&dir, opts->state_dir);
                if (remove_dir_recursively(&dir, 0))
@@ -789,7 +784,7 @@ static int rebase_config(const char *var, const char *value, void *data)
        }
 
        if (!strcmp(var, "rebase.autosquash")) {
-               opts->autosquash = git_config_bool(var, value);
+               opts->config_autosquash = git_config_bool(var, value);
                return 0;
        }
 
@@ -806,7 +801,7 @@ static int rebase_config(const char *var, const char *value, void *data)
        }
 
        if (!strcmp(var, "rebase.updaterefs")) {
-               opts->update_refs = git_config_bool(var, value);
+               opts->config_update_refs = git_config_bool(var, value);
                return 0;
        }
 
@@ -920,6 +915,9 @@ static int parse_opt_am(const struct option *opt, const char *arg, int unset)
        BUG_ON_OPT_NEG(unset);
        BUG_ON_OPT_ARG(arg);
 
+       if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_APPLY)
+           die(_("apply options and merge options cannot be used together"));
+
        opts->type = REBASE_APPLY;
 
        return 0;
@@ -933,8 +931,10 @@ static int parse_opt_merge(const struct option *opt, const char *arg, int unset)
        BUG_ON_OPT_NEG(unset);
        BUG_ON_OPT_ARG(arg);
 
-       if (!is_merge(opts))
-               opts->type = REBASE_MERGE;
+       if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_MERGE)
+           die(_("apply options and merge options cannot be used together"));
+
+       opts->type = REBASE_MERGE;
 
        return 0;
 }
@@ -948,6 +948,9 @@ static int parse_opt_interactive(const struct option *opt, const char *arg,
        BUG_ON_OPT_NEG(unset);
        BUG_ON_OPT_ARG(arg);
 
+       if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_MERGE)
+           die(_("apply options and merge options cannot be used together"));
+
        opts->type = REBASE_MERGE;
        opts->flags |= REBASE_INTERACTIVE_EXPLICIT;
 
@@ -1032,11 +1035,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        struct object_id branch_base;
        int ignore_whitespace = 0;
        const char *gpg_sign = NULL;
-       struct string_list exec = STRING_LIST_INIT_NODUP;
        const char *rebase_merges = NULL;
        struct string_list strategy_options = STRING_LIST_INIT_NODUP;
        struct object_id squash_onto;
        char *squash_onto_name = NULL;
+       char *keep_base_onto_name = NULL;
        int reschedule_failed_exec = -1;
        int allow_preemptive_ff = 1;
        int preserve_merges_selected = 0;
@@ -1127,7 +1130,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                        N_("GPG-sign commits"),
                        PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
                OPT_AUTOSTASH(&options.autostash),
-               OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
+               OPT_STRING_LIST('x', "exec", &options.exec, N_("exec"),
                                N_("add exec lines after each commit of the "
                                   "editable list")),
                OPT_BOOL_F(0, "allow-empty-message",
@@ -1164,8 +1167,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        prepare_repo_settings(the_repository);
        the_repository->settings.command_requires_full_index = 0;
 
-       options.reapply_cherry_picks = -1;
-       options.allow_empty_message = 1;
        git_config(rebase_config, &options);
        /* options.gpg_sign_opt will be either "-S" or NULL */
        gpg_sign = options.gpg_sign_opt ? "" : NULL;
@@ -1230,13 +1231,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                if (options.fork_point < 0)
                        options.fork_point = 0;
        }
-       /*
-        * --keep-base defaults to --reapply-cherry-picks to avoid losing
-        * commits when using this option.
-        */
-       if (options.reapply_cherry_picks < 0)
-               options.reapply_cherry_picks = keep_base;
-
        if (options.root && options.fork_point > 0)
                die(_("options '%s' and '%s' cannot be used together"), "--root", "--fork-point");
 
@@ -1250,7 +1244,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        if (trace2_is_enabled()) {
                if (is_merge(&options))
                        trace2_cmd_mode("interactive");
-               else if (exec.nr)
+               else if (options.exec.nr)
                        trace2_cmd_mode("interactive-exec");
                else
                        trace2_cmd_mode(action_names[options.action]);
@@ -1322,6 +1316,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                if (reset_head(the_repository, &ropts) < 0)
                        die(_("could not move back to %s"),
                            oid_to_hex(&options.orig_head->object.oid));
+               strbuf_release(&head_msg);
                remove_branch_state(the_repository, 0);
                ret = finish_rebase(&options);
                goto cleanup;
@@ -1333,6 +1328,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
                        replay.action = REPLAY_INTERACTIVE_REBASE;
                        ret = sequencer_remove_state(&replay);
+                       replay_opts_release(&replay);
                } else {
                        strbuf_reset(&buf);
                        strbuf_addstr(&buf, options.state_dir);
@@ -1377,8 +1373,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
        if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) ||
            (options.action != ACTION_NONE) ||
-           (exec.nr > 0) ||
-           options.autosquash) {
+           (options.exec.nr > 0) ||
+           (options.autosquash == -1 && options.config_autosquash == 1) ||
+           options.autosquash == 1) {
                allow_preemptive_ff = 0;
        }
        if (options.committer_date_is_author_date || options.ignore_date)
@@ -1401,8 +1398,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                }
        }
 
-       for (i = 0; i < exec.nr; i++)
-               if (check_exec_cmd(exec.items[i].string))
+       for (i = 0; i < options.exec.nr; i++)
+               if (check_exec_cmd(options.exec.items[i].string))
                        exit(1);
 
        if (!(options.flags & REBASE_NO_QUIET))
@@ -1411,27 +1408,34 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        if (options.empty != EMPTY_UNSPECIFIED)
                imply_merge(&options, "--empty");
 
-       /*
-        * --keep-base implements --reapply-cherry-picks by altering upstream so
-        * it works with both backends.
-        */
-       if (options.reapply_cherry_picks && !keep_base)
-               imply_merge(&options, "--reapply-cherry-picks");
+       if (options.reapply_cherry_picks < 0)
+               /*
+                * We default to --no-reapply-cherry-picks unless
+                * --keep-base is given; when --keep-base is given, we want
+                * to default to --reapply-cherry-picks.
+                */
+               options.reapply_cherry_picks = keep_base;
+       else if (!keep_base)
+               /*
+                * The apply backend always searches for and drops cherry
+                * picks.  This is often not wanted with --keep-base, so
+                * --keep-base allows --reapply-cherry-picks to be
+                * simulated by altering the upstream such that
+                * cherry-picks cannot be detected and thus all commits are
+                * reapplied.  Thus, --[no-]reapply-cherry-picks is
+                * supported when --keep-base is specified, but not when
+                * --keep-base is left out.
+                */
+               imply_merge(&options, options.reapply_cherry_picks ?
+                                         "--reapply-cherry-picks" :
+                                         "--no-reapply-cherry-picks");
 
        if (gpg_sign)
                options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
 
-       if (exec.nr) {
-               int i;
-
+       if (options.exec.nr)
                imply_merge(&options, "--exec");
 
-               strbuf_reset(&buf);
-               for (i = 0; i < exec.nr; i++)
-                       strbuf_addf(&buf, "exec %s\n", exec.items[i].string);
-               options.cmd = xstrdup(buf.buf);
-       }
-
        if (rebase_merges) {
                if (!*rebase_merges)
                        ; /* default mode; do nothing */
@@ -1504,15 +1508,29 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                        if (strcmp(options.git_am_opts.v[i], "-q"))
                                break;
 
-               if (i >= 0) {
+               if (i >= 0 || options.type == REBASE_APPLY) {
                        if (is_merge(&options))
                                die(_("apply options and merge options "
                                          "cannot be used together"));
+                       else if (options.autosquash == -1 && options.config_autosquash == 1)
+                               die(_("apply options are incompatible with rebase.autosquash.  Consider adding --no-autosquash"));
+                       else if (options.update_refs == -1 && options.config_update_refs == 1)
+                               die(_("apply options are incompatible with rebase.updateRefs.  Consider adding --no-update-refs"));
                        else
                                options.type = REBASE_APPLY;
                }
        }
 
+       if (options.update_refs == 1)
+               imply_merge(&options, "--update-refs");
+       options.update_refs = (options.update_refs >= 0) ? options.update_refs :
+                            ((options.config_update_refs >= 0) ? options.config_update_refs : 0);
+
+       if (options.autosquash == 1)
+               imply_merge(&options, "--autosquash");
+       options.autosquash = (options.autosquash >= 0) ? options.autosquash :
+                            ((options.config_autosquash >= 0) ? options.config_autosquash : 0);
+
        if (options.type == REBASE_UNSPECIFIED) {
                if (!strcmp(options.default_backend, "merge"))
                        imply_merge(&options, "--merge");
@@ -1542,7 +1560,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        if (options.empty == EMPTY_UNSPECIFIED) {
                if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
                        options.empty = EMPTY_ASK;
-               else if (exec.nr > 0)
+               else if (options.exec.nr > 0)
                        options.empty = EMPTY_KEEP;
                else
                        options.empty = EMPTY_DROP;
@@ -1658,7 +1676,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                strbuf_addstr(&buf, options.upstream_name);
                strbuf_addstr(&buf, "...");
                strbuf_addstr(&buf, branch_name);
-               options.onto_name = xstrdup(buf.buf);
+               options.onto_name = keep_base_onto_name = xstrdup(buf.buf);
        } else if (!options.onto_name)
                options.onto_name = options.upstream_name;
        if (strstr(options.onto_name, "...")) {
@@ -1828,10 +1846,14 @@ cleanup:
        strbuf_release(&revisions);
        free(options.reflog_action);
        free(options.head_name);
+       strvec_clear(&options.git_am_opts);
        free(options.gpg_sign_opt);
-       free(options.cmd);
+       string_list_clear(&options.exec, 0);
        free(options.strategy);
+       free(options.strategy_opts);
        strbuf_release(&options.git_format_patch_opt);
        free(squash_onto_name);
+       free(keep_base_onto_name);
+       string_list_clear(&strategy_options, 0);
        return !!ret;
 }
index a90af3036303f1c6d8261559c7309fe9934874c7..cd5c7a28eff08f048b79a9d739800709c443c160 100644 (file)
@@ -2032,6 +2032,16 @@ static struct command **queue_command(struct command **tail,
        return &cmd->next;
 }
 
+static void free_commands(struct command *commands)
+{
+       while (commands) {
+               struct command *next = commands->next;
+
+               free(commands);
+               commands = next;
+       }
+}
+
 static void queue_commands_from_cert(struct command **tail,
                                     struct strbuf *push_cert)
 {
@@ -2569,6 +2579,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                run_receive_hook(commands, "post-receive", 1,
                                 &push_options);
                run_update_post_hook(commands);
+               free_commands(commands);
                string_list_clear(&push_options, 0);
                if (auto_gc) {
                        struct child_process proc = CHILD_PROCESS_INIT;
index fd3538d4f0e6924214bedc36c132f631d17f4c8b..ee338bf440c6b0647f4c16174166ebd02929a7f4 100644 (file)
@@ -169,6 +169,8 @@ static int command_loop(const char *child)
 
        while (1) {
                size_t i;
+               const char *arg;
+
                if (!fgets(buffer, MAXCOMMAND - 1, stdin)) {
                        if (ferror(stdin))
                                die("Command input error");
@@ -182,10 +184,10 @@ static int command_loop(const char *child)
                if (!strcmp(buffer, "capabilities")) {
                        printf("*connect\n\n");
                        fflush(stdout);
-               } else if (!strncmp(buffer, "connect ", 8)) {
+               } else if (skip_prefix(buffer, "connect ", &arg)) {
                        printf("\n");
                        fflush(stdout);
-                       return run_child(child, buffer + 8);
+                       return run_child(child, arg);
                } else {
                        fprintf(stderr, "Bad command");
                        return 1;
index 91dfe07e06a1b9603dfd7a0d04f4b7866841d63f..b2a3980b1d51b2ec369c7bf758710357b34816c3 100644 (file)
@@ -40,7 +40,7 @@ static void command_loop(int input_fd, int output_fd)
                if (!strcmp(buffer, "capabilities")) {
                        printf("*connect\n\n");
                        fflush(stdout);
-               } else if (!strncmp(buffer, "connect ", 8)) {
+               } else if (starts_with(buffer, "connect ")) {
                        printf("\n");
                        fflush(stdout);
                        if (bidirectional_transfer_loop(input_fd,
index 65eb1b8bd22b4119908abe700cfd3a6d143289f1..f64937953184fd346923bafed6f7868bd99cded5 100644 (file)
@@ -948,7 +948,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
 
        ret = start_command(&cmd);
        if (ret)
-               return ret;
+               goto cleanup;
 
        if (geometry) {
                FILE *in = xfdopen(cmd.in, "w");
@@ -973,10 +973,11 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                item = string_list_append(&names, line.buf);
                item->util = populate_pack_exts(item->string);
        }
+       strbuf_release(&line);
        fclose(out);
        ret = finish_command(&cmd);
        if (ret)
-               return ret;
+               goto cleanup;
 
        if (!names.nr && !po_args.quiet)
                printf_ln(_("Nothing new to pack."));
@@ -1006,7 +1007,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                                       &existing_nonkept_packs,
                                       &existing_kept_packs);
                if (ret)
-                       return ret;
+                       goto cleanup;
 
                if (delete_redundant && expire_to) {
                        /*
@@ -1038,7 +1039,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                                               &existing_nonkept_packs,
                                               &existing_kept_packs);
                        if (ret)
-                               return ret;
+                               goto cleanup;
                }
        }
 
@@ -1114,7 +1115,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                string_list_clear(&include, 0);
 
                if (ret)
-                       return ret;
+                       goto cleanup;
        }
 
        reprepare_packed_git(the_repository);
@@ -1171,11 +1172,11 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                write_midx_file(get_object_directory(), NULL, NULL, flags);
        }
 
+cleanup:
        string_list_clear(&names, 1);
        string_list_clear(&existing_nonkept_packs, 0);
        string_list_clear(&existing_kept_packs, 0);
        clear_pack_geometry(geometry);
-       strbuf_release(&line);
 
-       return 0;
+       return ret;
 }
index 8b7392d5b4483ff961e14fc7ae5194d97d188d8b..94ffb8c21ab1e08f1775ad15315dd689723a2db8 100644 (file)
@@ -14,7 +14,7 @@ static const char * const rerere_usage[] = {
        NULL,
 };
 
-static int outf(void *dummy, mmbuffer_t *ptr, int nbuf)
+static int outf(void *dummy UNUSED, mmbuffer_t *ptr, int nbuf)
 {
        int i;
        for (i = 0; i < nbuf; i++)
index 1fa86edb4c92f31a21d838bcbff76e687cd15c23..0697fa89de22414a58cce94b5f129c9e97d03fa1 100644 (file)
@@ -26,6 +26,7 @@
 #include "submodule.h"
 #include "submodule-config.h"
 #include "dir.h"
+#include "add-interactive.h"
 
 #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
 
@@ -133,7 +134,8 @@ static void print_new_head_line(struct commit *commit)
 }
 
 static void update_index_from_diff(struct diff_queue_struct *q,
-               struct diff_options *opt, void *data)
+                                  struct diff_options *opt UNUSED,
+                                  void *data)
 {
        int i;
        int intent_to_add = *(int *)data;
@@ -389,7 +391,9 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                if (reset_type != NONE)
                        die(_("options '%s' and '%s' cannot be used together"), "--patch", "--{hard,mixed,soft}");
                trace2_cmd_mode("patch-interactive");
-               return run_add_interactive(rev, "--patch=reset", &pathspec);
+               update_ref_status = !!run_add_p(the_repository, ADD_P_RESET, rev,
+                                  &pathspec);
+               goto cleanup;
        }
 
        /* git reset tree [--] paths... can be used to
@@ -438,8 +442,10 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                                       LOCK_DIE_ON_ERROR);
                if (reset_type == MIXED) {
                        int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
-                       if (read_from_tree(&pathspec, &oid, intent_to_add))
-                               return 1;
+                       if (read_from_tree(&pathspec, &oid, intent_to_add)) {
+                               update_ref_status = 1;
+                               goto cleanup;
+                       }
                        the_index.updated_skipworktree = 1;
                        if (!no_refresh && get_git_work_tree()) {
                                uint64_t t_begin, t_delta_in_ms;
@@ -485,5 +491,9 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
        if (!pathspec.nr)
                remove_branch_state(the_repository, 0);
 
+       discard_index(&the_index);
+
+cleanup:
+       clear_pathspec(&pathspec);
        return update_ref_status;
 }
index 1c344d74a7950fa57abd4d6db4c7e692f930e5b7..e67999e5ebc5ef640fe5dcd45dfa1b4126a9c7a8 100644 (file)
@@ -530,6 +530,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
        strbuf_addstr(&parsed, " --");
        sq_quote_argv(&parsed, argv);
        puts(parsed.buf);
+       strbuf_release(&parsed);
        return 0;
 }
 
index 6a9b550a61facc27777678b93f61999b5be53dd1..77d2035616efa1aa60b412f024da1a7836df4478 100644 (file)
@@ -221,6 +221,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
        opts->strategy = xstrdup_or_null(opts->strategy);
        if (!opts->strategy && getenv("GIT_TEST_MERGE_ALGORITHM"))
                opts->strategy = xstrdup(getenv("GIT_TEST_MERGE_ALGORITHM"));
+       free(options);
 
        if (cmd == 'q') {
                int ret = sequencer_remove_state(opts);
@@ -247,9 +248,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)
        res = run_sequencer(argc, argv, &opts);
        if (res < 0)
                die(_("revert failed"));
-       if (opts.revs)
-               release_revisions(opts.revs);
-       free(opts.revs);
+       replay_opts_release(&opts);
        return res;
 }
 
@@ -263,5 +262,6 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
        res = run_sequencer(argc, argv, &opts);
        if (res < 0)
                die(_("cherry-pick failed"));
+       replay_opts_release(&opts);
        return res;
 }
index d4989d4d8637f6054f763ba516366a7eca7f316e..8844f90655799b16f59080c8a69ca86fcbb9fd6f 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (C) Linus Torvalds 2006
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "advice.h"
 #include "config.h"
@@ -86,7 +86,7 @@ static void submodules_absorb_gitdir_if_needed(void)
                        continue;
 
                if (!submodule_uses_gitfile(name))
-                       absorb_git_dir_into_superproject(name);
+                       absorb_git_dir_into_superproject(name, NULL);
        }
 }
 
index c013abaf94251bd3b37027e8ab48917a4feb099b..358ac3e519a717f1a354b5d6fa00a8fcd8d77ca5 100644 (file)
@@ -956,5 +956,6 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                if (shown_merge_point && --extra < 0)
                        break;
        }
+       free(head);
        return 0;
 }
index 58a22503f049baef60fafc363e0f5d82d1ddf368..c37381549181837ce4afe3659e5790e88485a816 100644 (file)
@@ -217,6 +217,7 @@ static int update_working_directory(struct pattern_list *pl)
        o.head_idx = -1;
        o.src_index = r->index;
        o.dst_index = r->index;
+       index_state_init(&o.result, r);
        o.skip_sparse_checkout = 0;
        o.pl = pl;
 
index 62e36718d381fef809e381e886aa78e6108439d9..3a4f9fd566d27cfea7039f9c92f7a9730f37f912 100644 (file)
@@ -1,4 +1,4 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "config.h"
 #include "parse-options.h"
@@ -18,6 +18,7 @@
 #include "diffcore.h"
 #include "exec-cmd.h"
 #include "reflog.h"
+#include "add-interactive.h"
 
 #define INCLUDE_ALL_FILES 2
 
@@ -528,7 +529,8 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
                                         NULL, NULL, NULL))
                return -1;
 
-       if (write_cache_as_tree(&c_tree, 0, NULL))
+       if (write_index_as_tree(&c_tree, &the_index, get_index_file(), 0,
+                               NULL))
                return error(_("cannot apply a stash in the middle of a merge"));
 
        if (index) {
@@ -552,7 +554,8 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
 
                        discard_index(&the_index);
                        repo_read_index(the_repository);
-                       if (write_cache_as_tree(&index_tree, 0, NULL))
+                       if (write_index_as_tree(&index_tree, &the_index,
+                                               get_index_file(), 0, NULL))
                                return error(_("could not save index tree"));
 
                        reset_head();
@@ -1137,7 +1140,7 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg,
        int ret = 0;
        struct strbuf untracked_msg = STRBUF_INIT;
        struct child_process cp_upd_index = CHILD_PROCESS_INIT;
-       struct index_state istate = { NULL };
+       struct index_state istate = INDEX_STATE_INIT(the_repository);
 
        cp_upd_index.git_cmd = 1;
        strvec_pushl(&cp_upd_index.args, "update-index", "-z", "--add",
@@ -1165,7 +1168,7 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg,
        }
 
 done:
-       discard_index(&istate);
+       release_index(&istate);
        strbuf_release(&untracked_msg);
        remove_path(stash_index_path.buf);
        return ret;
@@ -1176,7 +1179,7 @@ static int stash_staged(struct stash_info *info, struct strbuf *out_patch,
 {
        int ret = 0;
        struct child_process cp_diff_tree = CHILD_PROCESS_INIT;
-       struct index_state istate = { NULL };
+       struct index_state istate = INDEX_STATE_INIT(the_repository);
 
        if (write_index_as_tree(&info->w_tree, &istate, the_repository->index_file,
                                0, NULL)) {
@@ -1199,7 +1202,7 @@ static int stash_staged(struct stash_info *info, struct strbuf *out_patch,
        }
 
 done:
-       discard_index(&istate);
+       release_index(&istate);
        return ret;
 }
 
@@ -1209,7 +1212,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
        int ret = 0;
        struct child_process cp_read_tree = CHILD_PROCESS_INIT;
        struct child_process cp_diff_tree = CHILD_PROCESS_INIT;
-       struct index_state istate = { NULL };
+       struct index_state istate = INDEX_STATE_INIT(the_repository);
        char *old_index_env = NULL, *old_repo_index_file;
 
        remove_path(stash_index_path.buf);
@@ -1229,7 +1232,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
        old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
        setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);
 
-       ret = run_add_interactive(NULL, "--patch=stash", ps);
+       ret = !!run_add_p(the_repository, ADD_P_STASH, NULL, ps);
 
        the_repository->index_file = old_repo_index_file;
        if (old_index_env && *old_index_env)
@@ -1260,7 +1263,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
        }
 
 done:
-       discard_index(&istate);
+       release_index(&istate);
        remove_path(stash_index_path.buf);
        return ret;
 }
@@ -1271,7 +1274,7 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
        struct rev_info rev;
        struct child_process cp_upd_index = CHILD_PROCESS_INIT;
        struct strbuf diff_output = STRBUF_INIT;
-       struct index_state istate = { NULL };
+       struct index_state istate = INDEX_STATE_INIT(the_repository);
 
        init_revisions(&rev, NULL);
        copy_pathspec(&rev.prune_data, ps);
@@ -1319,7 +1322,7 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
        }
 
 done:
-       discard_index(&istate);
+       release_index(&istate);
        release_revisions(&rev);
        strbuf_release(&diff_output);
        remove_path(stash_index_path.buf);
@@ -1377,7 +1380,8 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
 
        strbuf_addf(&commit_tree_label, "index on %s\n", msg.buf);
        commit_list_insert(head_commit, &parents);
-       if (write_cache_as_tree(&info->i_tree, 0, NULL) ||
+       if (write_index_as_tree(&info->i_tree, &the_index, get_index_file(), 0,
+                               NULL) ||
            commit_tree(commit_tree_label.buf, commit_tree_label.len,
                        &info->i_tree, parents, &info->i_commit, NULL, NULL)) {
                if (!quiet)
@@ -1689,8 +1693,10 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
        }
 
 done:
+       strbuf_release(&patch);
        free_stash_info(&info);
        strbuf_release(&stash_msg_buf);
+       strbuf_release(&untracked_files);
        return ret;
 }
 
@@ -1725,6 +1731,7 @@ static int push_stash(int argc, const char **argv, const char *prefix,
                OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
                OPT_END()
        };
+       int ret;
 
        if (argc) {
                force_assume = !strcmp(argv[0], "-p");
@@ -1764,8 +1771,10 @@ static int push_stash(int argc, const char **argv, const char *prefix,
                die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
        }
 
-       return do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
-                            include_untracked, only_staged);
+       ret = do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
+                           include_untracked, only_staged);
+       clear_pathspec(&ps);
+       return ret;
 }
 
 static int push_stash_unassumed(int argc, const char **argv, const char *prefix)
index 05f2c9bc9858d98ae855b566d6128030e002fa18..4c173d8b37adfc72ec840fd88ca27e36f432aef3 100644 (file)
@@ -113,10 +113,9 @@ static char *resolve_relative_url(const char *rel_url, const char *up_path, int
 }
 
 /* the result should be freed by the caller. */
-static char *get_submodule_displaypath(const char *path, const char *prefix)
+static char *get_submodule_displaypath(const char *path, const char *prefix,
+                                      const char *super_prefix)
 {
-       const char *super_prefix = get_super_prefix();
-
        if (prefix && super_prefix) {
                BUG("cannot have prefix '%s' and superprefix '%s'",
                    prefix, super_prefix);
@@ -279,6 +278,7 @@ struct foreach_cb {
        int argc;
        const char **argv;
        const char *prefix;
+       const char *super_prefix;
        int quiet;
        int recursive;
 };
@@ -294,7 +294,8 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
        struct child_process cp = CHILD_PROCESS_INIT;
        char *displaypath;
 
-       displaypath = get_submodule_displaypath(path, info->prefix);
+       displaypath = get_submodule_displaypath(path, info->prefix,
+                                               info->super_prefix);
 
        sub = submodule_from_path(the_repository, null_oid(), path);
 
@@ -364,10 +365,10 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
                cpr.dir = path;
                prepare_submodule_repo_env(&cpr.env);
 
-               strvec_pushl(&cpr.args, "--super-prefix", NULL);
-               strvec_pushf(&cpr.args, "%s/", displaypath);
                strvec_pushl(&cpr.args, "submodule--helper", "foreach", "--recursive",
                             NULL);
+               strvec_pushl(&cpr.args, "--super-prefix", NULL);
+               strvec_pushf(&cpr.args, "%s/", displaypath);
 
                if (info->quiet)
                        strvec_push(&cpr.args, "--quiet");
@@ -391,6 +392,7 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
        struct pathspec pathspec = { 0 };
        struct module_list list = MODULE_LIST_INIT;
        struct option module_foreach_options[] = {
+               OPT__SUPER_PREFIX(&info.super_prefix),
                OPT__QUIET(&info.quiet, N_("suppress output of entering each submodule command")),
                OPT_BOOL(0, "recursive", &info.recursive,
                         N_("recurse into nested submodules")),
@@ -435,11 +437,13 @@ static int starts_with_dot_dot_slash(const char *const path)
 
 struct init_cb {
        const char *prefix;
+       const char *super_prefix;
        unsigned int flags;
 };
 #define INIT_CB_INIT { 0 }
 
 static void init_submodule(const char *path, const char *prefix,
+                          const char *super_prefix,
                           unsigned int flags)
 {
        const struct submodule *sub;
@@ -447,7 +451,7 @@ static void init_submodule(const char *path, const char *prefix,
        const char *upd;
        char *url = NULL, *displaypath;
 
-       displaypath = get_submodule_displaypath(path, prefix);
+       displaypath = get_submodule_displaypath(path, prefix, super_prefix);
 
        sub = submodule_from_path(the_repository, null_oid(), path);
 
@@ -523,7 +527,8 @@ static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data
 {
        struct init_cb *info = cb_data;
 
-       init_submodule(list_item->name, info->prefix, info->flags);
+       init_submodule(list_item->name, info->prefix, info->super_prefix,
+                      info->flags);
 }
 
 static int module_init(int argc, const char **argv, const char *prefix)
@@ -570,6 +575,7 @@ cleanup:
 
 struct status_cb {
        const char *prefix;
+       const char *super_prefix;
        unsigned int flags;
 };
 #define STATUS_CB_INIT { 0 }
@@ -608,7 +614,7 @@ static int handle_submodule_head_ref(const char *refname UNUSED,
 
 static void status_submodule(const char *path, const struct object_id *ce_oid,
                             unsigned int ce_flags, const char *prefix,
-                            unsigned int flags)
+                            const char *super_prefix, unsigned int flags)
 {
        char *displaypath;
        struct strvec diff_files_args = STRVEC_INIT;
@@ -624,7 +630,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
                die(_("no submodule mapping found in .gitmodules for path '%s'"),
                      path);
 
-       displaypath = get_submodule_displaypath(path, prefix);
+       displaypath = get_submodule_displaypath(path, prefix, super_prefix);
 
        if ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) {
                print_status(flags, 'U', path, null_oid(), displaypath);
@@ -682,10 +688,10 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
                cpr.dir = path;
                prepare_submodule_repo_env(&cpr.env);
 
-               strvec_push(&cpr.args, "--super-prefix");
-               strvec_pushf(&cpr.args, "%s/", displaypath);
                strvec_pushl(&cpr.args, "submodule--helper", "status",
                             "--recursive", NULL);
+               strvec_push(&cpr.args, "--super-prefix");
+               strvec_pushf(&cpr.args, "%s/", displaypath);
 
                if (flags & OPT_CACHED)
                        strvec_push(&cpr.args, "--cached");
@@ -709,7 +715,7 @@ static void status_submodule_cb(const struct cache_entry *list_item,
        struct status_cb *info = cb_data;
 
        status_submodule(list_item->name, &list_item->oid, list_item->ce_flags,
-                        info->prefix, info->flags);
+                        info->prefix, info->super_prefix, info->flags);
 }
 
 static int module_status(int argc, const char **argv, const char *prefix)
@@ -719,6 +725,7 @@ static int module_status(int argc, const char **argv, const char *prefix)
        struct module_list list = MODULE_LIST_INIT;
        int quiet = 0;
        struct option module_status_options[] = {
+               OPT__SUPER_PREFIX(&info.super_prefix),
                OPT__QUIET(&quiet, N_("suppress submodule status output")),
                OPT_BIT(0, "cached", &info.flags, N_("use commit stored in the index instead of the one stored in the submodule HEAD"), OPT_CACHED),
                OPT_BIT(0, "recursive", &info.flags, N_("recurse into nested submodules"), OPT_RECURSIVE),
@@ -787,6 +794,7 @@ struct summary_cb {
        int argc;
        const char **argv;
        const char *prefix;
+       const char *super_prefix;
        unsigned int cached: 1;
        unsigned int for_status: 1;
        unsigned int files: 1;
@@ -948,7 +956,8 @@ static void generate_submodule_summary(struct summary_cb *info,
                dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7);
        }
 
-       displaypath = get_submodule_displaypath(p->sm_path, info->prefix);
+       displaypath = get_submodule_displaypath(p->sm_path, info->prefix,
+                                               info->super_prefix);
 
        if (!missing_src && !missing_dst) {
                struct child_process cp_rev_list = CHILD_PROCESS_INIT;
@@ -1043,7 +1052,7 @@ static void prepare_submodule_summary(struct summary_cb *info,
 }
 
 static void submodule_summary_callback(struct diff_queue_struct *q,
-                                      struct diff_options *options,
+                                      struct diff_options *options UNUSED,
                                       void *data)
 {
        int i;
@@ -1203,12 +1212,13 @@ static int module_summary(int argc, const char **argv, const char *prefix)
 
 struct sync_cb {
        const char *prefix;
+       const char *super_prefix;
        unsigned int flags;
 };
 #define SYNC_CB_INIT { 0 }
 
 static void sync_submodule(const char *path, const char *prefix,
-                          unsigned int flags)
+                          const char *super_prefix, unsigned int flags)
 {
        const struct submodule *sub;
        char *remote_key = NULL;
@@ -1239,7 +1249,7 @@ static void sync_submodule(const char *path, const char *prefix,
                super_config_url = xstrdup("");
        }
 
-       displaypath = get_submodule_displaypath(path, prefix);
+       displaypath = get_submodule_displaypath(path, prefix, super_prefix);
 
        if (!(flags & OPT_QUIET))
                printf(_("Synchronizing submodule url for '%s'\n"),
@@ -1276,10 +1286,11 @@ static void sync_submodule(const char *path, const char *prefix,
                cpr.dir = path;
                prepare_submodule_repo_env(&cpr.env);
 
-               strvec_push(&cpr.args, "--super-prefix");
-               strvec_pushf(&cpr.args, "%s/", displaypath);
                strvec_pushl(&cpr.args, "submodule--helper", "sync",
                             "--recursive", NULL);
+               strvec_push(&cpr.args, "--super-prefix");
+               strvec_pushf(&cpr.args, "%s/", displaypath);
+
 
                if (flags & OPT_QUIET)
                        strvec_push(&cpr.args, "--quiet");
@@ -1302,7 +1313,8 @@ static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data
 {
        struct sync_cb *info = cb_data;
 
-       sync_submodule(list_item->name, info->prefix, info->flags);
+       sync_submodule(list_item->name, info->prefix, info->super_prefix,
+                      info->flags);
 }
 
 static int module_sync(int argc, const char **argv, const char *prefix)
@@ -1313,6 +1325,7 @@ static int module_sync(int argc, const char **argv, const char *prefix)
        int quiet = 0;
        int recursive = 0;
        struct option module_sync_options[] = {
+               OPT__SUPER_PREFIX(&info.super_prefix),
                OPT__QUIET(&quiet, N_("suppress output of synchronizing submodule url")),
                OPT_BOOL(0, "recursive", &recursive,
                        N_("recurse into nested submodules")),
@@ -1365,7 +1378,7 @@ static void deinit_submodule(const char *path, const char *prefix,
        if (!sub || !sub->name)
                goto cleanup;
 
-       displaypath = get_submodule_displaypath(path, prefix);
+       displaypath = get_submodule_displaypath(path, prefix, NULL);
 
        /* remove the submodule work tree (unless the user already did it) */
        if (is_directory(path)) {
@@ -1379,7 +1392,7 @@ static void deinit_submodule(const char *path, const char *prefix,
                                          ".git file by using absorbgitdirs."),
                                        displaypath);
 
-                       absorb_git_dir_into_superproject(path);
+                       absorb_git_dir_into_superproject(path, NULL);
 
                }
 
@@ -1883,6 +1896,7 @@ static void submodule_update_clone_release(struct submodule_update_clone *suc)
 
 struct update_data {
        const char *prefix;
+       const char *super_prefix;
        char *displaypath;
        enum submodule_update_type update_default;
        struct object_id suboid;
@@ -1958,7 +1972,8 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
        enum submodule_update_type update_type;
        char *key;
        const struct update_data *ud = suc->update_data;
-       char *displaypath = get_submodule_displaypath(ce->name, ud->prefix);
+       char *displaypath = get_submodule_displaypath(ce->name, ud->prefix,
+                                                     ud->super_prefix);
        struct strbuf sb = STRBUF_INIT;
        int needs_cloning = 0;
        int need_free_url = 0;
@@ -2438,11 +2453,11 @@ static void update_data_to_args(const struct update_data *update_data,
 {
        enum submodule_update_type update_type = update_data->update_default;
 
+       strvec_pushl(args, "submodule--helper", "update", "--recursive", NULL);
        if (update_data->displaypath) {
                strvec_push(args, "--super-prefix");
                strvec_pushf(args, "%s/", update_data->displaypath);
        }
-       strvec_pushl(args, "submodule--helper", "update", "--recursive", NULL);
        strvec_pushf(args, "--jobs=%d", update_data->max_jobs);
        if (update_data->quiet)
                strvec_push(args, "--quiet");
@@ -2608,7 +2623,8 @@ static int update_submodules(struct update_data *update_data)
                        goto fail;
 
                update_data->displaypath = get_submodule_displaypath(
-                       update_data->sm_path, update_data->prefix);
+                       update_data->sm_path, update_data->prefix,
+                       update_data->super_prefix);
                code = update_submodule(update_data);
                FREE_AND_NULL(update_data->displaypath);
 fail:
@@ -2634,6 +2650,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
                LIST_OBJECTS_FILTER_INIT;
        int ret;
        struct option module_update_options[] = {
+               OPT__SUPER_PREFIX(&opt.super_prefix),
                OPT__FORCE(&opt.force, N_("force checkout updates"), 0),
                OPT_BOOL(0, "init", &opt.init,
                         N_("initialize uninitialized submodules before update")),
@@ -2730,6 +2747,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
                        module_list_active(&list);
 
                info.prefix = opt.prefix;
+               info.super_prefix = opt.super_prefix;
                if (opt.quiet)
                        info.flags |= OPT_QUIET;
 
@@ -2828,7 +2846,9 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
        int i;
        struct pathspec pathspec = { 0 };
        struct module_list list = MODULE_LIST_INIT;
+       const char *super_prefix = NULL;
        struct option embed_gitdir_options[] = {
+               OPT__SUPER_PREFIX(&super_prefix),
                OPT_END()
        };
        const char *const git_submodule_helper_usage[] = {
@@ -2844,7 +2864,8 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
                goto cleanup;
 
        for (i = 0; i < list.nr; i++)
-               absorb_git_dir_into_superproject(list.entries[i]->name);
+               absorb_git_dir_into_superproject(list.entries[i]->name,
+                                                super_prefix);
 
        ret = 0;
 cleanup:
@@ -2876,7 +2897,7 @@ static int module_set_url(int argc, const char **argv, const char *prefix)
        config_name = xstrfmt("submodule.%s.url", path);
 
        config_set_in_gitmodules_file_gently(config_name, newurl);
-       sync_submodule(path, prefix, quiet ? OPT_QUIET : 0);
+       sync_submodule(path, prefix, NULL, quiet ? OPT_QUIET : 0);
 
        free(config_name);
 
@@ -3198,9 +3219,8 @@ static void die_on_index_match(const char *path, int force)
                ensure_full_index(&the_index);
 
                /*
-                * Since there is only one pathspec, we just need
-                * need to check ps_matched[0] to know if a cache
-                * entry matched.
+                * Since there is only one pathspec, we just need to
+                * check ps_matched[0] to know if a cache entry matched.
                 */
                for (i = 0; i < the_index.cache_nr; i++) {
                        ce_path_match(&the_index, the_index.cache[i], &ps,
@@ -3353,8 +3373,6 @@ cleanup:
 
 int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
 {
-       const char *cmd = argv[0];
-       const char *subcmd;
        parse_opt_subcommand_fn *fn = NULL;
        const char *const usage[] = {
                N_("git submodule--helper <command>"),
@@ -3378,18 +3396,6 @@ int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
        argc = parse_options(argc, argv, prefix, options, usage, 0);
-       subcmd = argv[0];
-
-       if (strcmp(subcmd, "clone") && strcmp(subcmd, "update") &&
-           strcmp(subcmd, "foreach") && strcmp(subcmd, "status") &&
-           strcmp(subcmd, "sync") && strcmp(subcmd, "absorbgitdirs") &&
-           get_super_prefix())
-               /*
-                * xstrfmt() rather than "%s %s" to keep the translated
-                * string identical to git.c's.
-                */
-               die(_("%s doesn't support --super-prefix"),
-                   xstrfmt("'%s %s'", cmd, subcmd));
 
        return fn(argc, argv, prefix);
 }
index 9e8119dd35422a93c63dbe23f840bda5ec69c5e5..88de32b7d7e66e3d7e1c57775b5f0a1b0b045175 100644 (file)
@@ -19,6 +19,7 @@ static char *create_temp_file(struct object_id *oid)
        if (write_in_full(fd, buf, size) < 0)
                die_errno("unable to write temp-file");
        close(fd);
+       free(buf);
        return path;
 }
 
index 82d5902cc8b15bafaeb09813ae99a45c54800b20..bf38885d5469b24e1ac64e4d3df74a4e70a3dd7b 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "cache.h"
 #include "bulk-checkin.h"
 #include "config.h"
@@ -381,7 +381,7 @@ static int process_path(const char *path, struct stat *st, int stat_errno)
        if (has_symlink_leading_path(path, len))
                return error("'%s' is beyond a symbolic link", path);
 
-       pos = cache_name_pos(path, len);
+       pos = index_name_pos(&the_index, path, len);
        ce = pos < 0 ? NULL : the_index.cache[pos];
        if (ce && ce_skip_worktree(ce)) {
                /*
index 491db2742926dbd6fd08f681817821addcfafab7..a80c1df86fd532bb6353b990381bfb050ac5e688 100644 (file)
@@ -11,12 +11,12 @@ static const char var_usage[] = "git var (-l | <variable>)";
 
 static const char *editor(int flag)
 {
-       const char *pgm = git_editor();
-
-       if (!pgm && flag & IDENT_STRICT)
-               die("Terminal is dumb, but EDITOR unset");
+       return git_editor();
+}
 
-       return pgm;
+static const char *sequence_editor(int flag)
+{
+       return git_sequence_editor();
 }
 
 static const char *pager(int flag)
@@ -41,6 +41,7 @@ static struct git_var git_vars[] = {
        { "GIT_COMMITTER_IDENT", git_committer_info },
        { "GIT_AUTHOR_IDENT",   git_author_info },
        { "GIT_EDITOR", editor },
+       { "GIT_SEQUENCE_EDITOR", sequence_editor },
        { "GIT_PAGER", pager },
        { "GIT_DEFAULT_BRANCH", default_branch },
        { "", NULL },
@@ -56,18 +57,15 @@ static void list_vars(void)
                        printf("%s=%s\n", ptr->name, val);
 }
 
-static const char *read_var(const char *var)
+static const struct git_var *get_git_var(const char *var)
 {
        struct git_var *ptr;
-       const char *val;
-       val = NULL;
        for (ptr = git_vars; ptr->read; ptr++) {
                if (strcmp(var, ptr->name) == 0) {
-                       val = ptr->read(IDENT_STRICT);
-                       break;
+                       return ptr;
                }
        }
-       return val;
+       return NULL;
 }
 
 static int show_config(const char *var, const char *value, void *cb)
@@ -81,7 +79,9 @@ static int show_config(const char *var, const char *value, void *cb)
 
 int cmd_var(int argc, const char **argv, const char *prefix)
 {
-       const char *val = NULL;
+       const struct git_var *git_var;
+       const char *val;
+
        if (argc != 2)
                usage(var_usage);
 
@@ -91,10 +91,15 @@ int cmd_var(int argc, const char **argv, const char *prefix)
                return 0;
        }
        git_config(git_default_config, NULL);
-       val = read_var(argv[1]);
-       if (!val)
+
+       git_var = get_git_var(argv[1]);
+       if (!git_var)
                usage(var_usage);
 
+       val = git_var->read(IDENT_STRICT);
+       if (!val)
+               return 1;
+
        printf("%s\n", val);
 
        return 0;
index 4a24d53be15c3a74d6632c68ccacb813c8aa989b..254283aa6f5716ab2ab83a477972b4630c056b1b 100644 (file)
@@ -173,7 +173,7 @@ static void prune_worktrees(void)
 {
        struct strbuf reason = STRBUF_INIT;
        struct strbuf main_path = STRBUF_INIT;
-       struct string_list kept = STRING_LIST_INIT_NODUP;
+       struct string_list kept = STRING_LIST_INIT_DUP;
        DIR *dir = opendir(git_path("worktrees"));
        struct dirent *d;
        if (!dir)
@@ -184,14 +184,14 @@ static void prune_worktrees(void)
                if (should_prune_worktree(d->d_name, &reason, &path, expire))
                        prune_worktree(d->d_name, reason.buf);
                else if (path)
-                       string_list_append(&kept, path)->util = xstrdup(d->d_name);
+                       string_list_append_nodup(&kept, path)->util = xstrdup(d->d_name);
        }
        closedir(dir);
 
        strbuf_add_absolute_path(&main_path, get_git_common_dir());
        /* massage main worktree absolute path to match 'gitdir' content */
        strbuf_strip_suffix(&main_path, "/.");
-       string_list_append(&kept, strbuf_detach(&main_path, NULL));
+       string_list_append_nodup(&kept, strbuf_detach(&main_path, NULL));
        prune_dups(&kept);
        string_list_clear(&kept, 1);
 
@@ -629,6 +629,7 @@ static int add(int ac, const char **av, const char *prefix)
                         N_("try to match the new branch name with a remote-tracking branch")),
                OPT_END()
        };
+       int ret;
 
        memset(&opts, 0, sizeof(opts));
        opts.checkout = 1;
@@ -705,9 +706,9 @@ static int add(int ac, const char **av, const char *prefix)
                die(_("--[no-]track can only be used if a new branch is created"));
        }
 
-       UNLEAK(path);
-       UNLEAK(opts);
-       return add_worktree(path, branch, &opts);
+       ret = add_worktree(path, branch, &opts);
+       free(path);
+       return ret;
 }
 
 static void show_worktree_porcelain(struct worktree *wt, int line_terminator)
@@ -922,7 +923,7 @@ static int unlock_worktree(int ac, const char **av, const char *prefix)
 
 static void validate_no_submodules(const struct worktree *wt)
 {
-       struct index_state istate = { NULL };
+       struct index_state istate = INDEX_STATE_INIT(the_repository);
        struct strbuf path = STRBUF_INIT;
        int i, found_submodules = 0;
 
index 45d61707e7d11e60e5ba2e6da90017fb38b30dc3..078010315f004647f22865db68f5cab63a799f8f 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "cache.h"
 #include "config.h"
@@ -38,7 +38,8 @@ int cmd_write_tree(int argc, const char **argv, const char *cmd_prefix)
        argc = parse_options(argc, argv, cmd_prefix, write_tree_options,
                             write_tree_usage, 0);
 
-       ret = write_cache_as_tree(&oid, flags, tree_prefix);
+       ret = write_index_as_tree(&oid, &the_index, get_index_file(), flags,
+                                 tree_prefix);
        switch (ret) {
        case 0:
                printf("%s\n", oid_to_hex(&oid));
index 79a914f961b134cccc6029aa403d7a626841c81b..8a3c39ce572b6c0fcfcfd07a0fb2f3d6da2bdecc 100644 (file)
@@ -7,6 +7,15 @@
 #include "hashmap.h"
 #include "pkt-line.h"
 #include "config.h"
+#include "remote.h"
+
+static struct {
+       enum bundle_list_heuristic heuristic;
+       const char *name;
+} heuristics[BUNDLE_HEURISTIC__COUNT] = {
+       { BUNDLE_HEURISTIC_NONE, ""},
+       { BUNDLE_HEURISTIC_CREATIONTOKEN, "creationToken" },
+};
 
 static int compare_bundles(const void *hashmap_cmp_fn_data,
                           const struct hashmap_entry *he1,
@@ -49,6 +58,7 @@ void clear_bundle_list(struct bundle_list *list)
 
        for_all_bundles_in_list(list, clear_remote_bundle_info, NULL);
        hashmap_clear_and_free(&list->bundles, struct remote_bundle_info, ent);
+       free(list->baseURI);
 }
 
 int for_all_bundles_in_list(struct bundle_list *list,
@@ -73,6 +83,9 @@ static int summarize_bundle(struct remote_bundle_info *info, void *data)
        FILE *fp = data;
        fprintf(fp, "[bundle \"%s\"]\n", info->id);
        fprintf(fp, "\turi = %s\n", info->uri);
+
+       if (info->creationToken)
+               fprintf(fp, "\tcreationToken = %"PRIu64"\n", info->creationToken);
        return 0;
 }
 
@@ -98,6 +111,17 @@ void print_bundle_list(FILE *fp, struct bundle_list *list)
        fprintf(fp, "\tversion = %d\n", list->version);
        fprintf(fp, "\tmode = %s\n", mode);
 
+       if (list->heuristic) {
+               int i;
+               for (i = 0; i < BUNDLE_HEURISTIC__COUNT; i++) {
+                       if (heuristics[i].heuristic == list->heuristic) {
+                               printf("\theuristic = %s\n",
+                                      heuristics[list->heuristic].name);
+                               break;
+                       }
+               }
+       }
+
        for_all_bundles_in_list(list, summarize_bundle, fp);
 }
 
@@ -140,6 +164,21 @@ static int bundle_list_update(const char *key, const char *value,
                        return 0;
                }
 
+               if (!strcmp(subkey, "heuristic")) {
+                       int i;
+                       for (i = 0; i < BUNDLE_HEURISTIC__COUNT; i++) {
+                               if (heuristics[i].heuristic &&
+                                   heuristics[i].name &&
+                                   !strcmp(value, heuristics[i].name)) {
+                                       list->heuristic = heuristics[i].heuristic;
+                                       return 0;
+                               }
+                       }
+
+                       /* Ignore unknown heuristics. */
+                       return 0;
+               }
+
                /* Ignore other unknown global keys. */
                return 0;
        }
@@ -163,7 +202,14 @@ static int bundle_list_update(const char *key, const char *value,
        if (!strcmp(subkey, "uri")) {
                if (bundle->uri)
                        return -1;
-               bundle->uri = xstrdup(value);
+               bundle->uri = relative_url(list->baseURI, value, NULL);
+               return 0;
+       }
+
+       if (!strcmp(subkey, "creationtoken")) {
+               if (sscanf(value, "%"PRIu64, &bundle->creationToken) != 1)
+                       warning(_("could not parse bundle list key %s with value '%s'"),
+                               "creationToken", value);
                return 0;
        }
 
@@ -190,6 +236,18 @@ int bundle_uri_parse_config_format(const char *uri,
                .error_action = CONFIG_ERROR_ERROR,
        };
 
+       if (!list->baseURI) {
+               struct strbuf baseURI = STRBUF_INIT;
+               strbuf_addstr(&baseURI, uri);
+
+               /*
+                * If the URI does not end with a trailing slash, then
+                * remove the filename portion of the path. This is
+                * important for relative URIs.
+                */
+               strbuf_strip_file_from_path(&baseURI);
+               list->baseURI = strbuf_detach(&baseURI, NULL);
+       }
        result = git_config_from_file_with_options(config_to_bundle_list,
                                                   filename, list,
                                                   &opts);
@@ -389,6 +447,183 @@ static int download_bundle_to_file(struct remote_bundle_info *bundle, void *data
        return 0;
 }
 
+struct bundles_for_sorting {
+       struct remote_bundle_info **items;
+       size_t alloc;
+       size_t nr;
+};
+
+static int append_bundle(struct remote_bundle_info *bundle, void *data)
+{
+       struct bundles_for_sorting *list = data;
+       list->items[list->nr++] = bundle;
+       return 0;
+}
+
+/**
+ * For use in QSORT() to get a list sorted by creationToken
+ * in decreasing order.
+ */
+static int compare_creation_token_decreasing(const void *va, const void *vb)
+{
+       const struct remote_bundle_info * const *a = va;
+       const struct remote_bundle_info * const *b = vb;
+
+       if ((*a)->creationToken > (*b)->creationToken)
+               return -1;
+       if ((*a)->creationToken < (*b)->creationToken)
+               return 1;
+       return 0;
+}
+
+static int fetch_bundles_by_token(struct repository *r,
+                                 struct bundle_list *list)
+{
+       int cur;
+       int move_direction = 0;
+       const char *creationTokenStr;
+       uint64_t maxCreationToken = 0, newMaxCreationToken = 0;
+       struct bundle_list_context ctx = {
+               .r = r,
+               .list = list,
+               .mode = list->mode,
+       };
+       struct bundles_for_sorting bundles = {
+               .alloc = hashmap_get_size(&list->bundles),
+       };
+
+       ALLOC_ARRAY(bundles.items, bundles.alloc);
+
+       for_all_bundles_in_list(list, append_bundle, &bundles);
+
+       if (!bundles.nr) {
+               free(bundles.items);
+               return 0;
+       }
+
+       QSORT(bundles.items, bundles.nr, compare_creation_token_decreasing);
+
+       /*
+        * If fetch.bundleCreationToken exists, parses to a uint64t, and
+        * is not strictly smaller than the maximum creation token in the
+        * bundle list, then do not download any bundles.
+        */
+       if (!repo_config_get_value(r,
+                                  "fetch.bundlecreationtoken",
+                                  &creationTokenStr) &&
+           sscanf(creationTokenStr, "%"PRIu64, &maxCreationToken) == 1 &&
+           bundles.items[0]->creationToken <= maxCreationToken) {
+               free(bundles.items);
+               return 0;
+       }
+
+       /*
+        * Attempt to download and unbundle the minimum number of bundles by
+        * creationToken in decreasing order. If we fail to unbundle (after
+        * a successful download) then move to the next non-downloaded bundle
+        * and attempt downloading. Once we succeed in applying a bundle,
+        * move to the previous unapplied bundle and attempt to unbundle it
+        * again.
+        *
+        * In the case of a fresh clone, we will likely download all of the
+        * bundles before successfully unbundling the oldest one, then the
+        * rest of the bundles unbundle successfully in increasing order
+        * of creationToken.
+        *
+        * If there are existing objects, then this process may terminate
+        * early when all required commits from "new" bundles exist in the
+        * repo's object store.
+        */
+       cur = 0;
+       while (cur >= 0 && cur < bundles.nr) {
+               struct remote_bundle_info *bundle = bundles.items[cur];
+
+               /*
+                * If we need to dig into bundles below the previous
+                * creation token value, then likely we are in an erroneous
+                * state due to missing or invalid bundles. Halt the process
+                * instead of continuing to download extra data.
+                */
+               if (bundle->creationToken <= maxCreationToken)
+                       break;
+
+               if (!bundle->file) {
+                       /*
+                        * Not downloaded yet. Try downloading.
+                        *
+                        * Note that bundle->file is non-NULL if a download
+                        * was attempted, even if it failed to download.
+                        */
+                       if (fetch_bundle_uri_internal(ctx.r, bundle, ctx.depth + 1, ctx.list)) {
+                               /* Mark as unbundled so we do not retry. */
+                               bundle->unbundled = 1;
+
+                               /* Try looking deeper in the list. */
+                               move_direction = 1;
+                               goto move;
+                       }
+
+                       /* We expect bundles when using creationTokens. */
+                       if (!is_bundle(bundle->file, 1)) {
+                               warning(_("file downloaded from '%s' is not a bundle"),
+                                       bundle->uri);
+                               break;
+                       }
+               }
+
+               if (bundle->file && !bundle->unbundled) {
+                       /*
+                        * This was downloaded, but not successfully
+                        * unbundled. Try unbundling again.
+                        */
+                       if (unbundle_from_file(ctx.r, bundle->file)) {
+                               /* Try looking deeper in the list. */
+                               move_direction = 1;
+                       } else {
+                               /*
+                                * Succeeded in unbundle. Retry bundles
+                                * that previously failed to unbundle.
+                                */
+                               move_direction = -1;
+                               bundle->unbundled = 1;
+
+                               if (bundle->creationToken > newMaxCreationToken)
+                                       newMaxCreationToken = bundle->creationToken;
+                       }
+               }
+
+               /*
+                * Else case: downloaded and unbundled successfully.
+                * Skip this by moving in the same direction as the
+                * previous step.
+                */
+
+move:
+               /* Move in the specified direction and repeat. */
+               cur += move_direction;
+       }
+
+       /*
+        * We succeed if the loop terminates because 'cur' drops below
+        * zero. The other case is that we terminate because 'cur'
+        * reaches the end of the list, so we have a failure no matter
+        * which bundles we apply from the list.
+        */
+       if (cur < 0) {
+               struct strbuf value = STRBUF_INIT;
+               strbuf_addf(&value, "%"PRIu64"", newMaxCreationToken);
+               if (repo_config_set_multivar_gently(ctx.r,
+                                                   "fetch.bundleCreationToken",
+                                                   value.buf, NULL, 0))
+                       warning(_("failed to store maximum creation token"));
+
+               strbuf_release(&value);
+       }
+
+       free(bundles.items);
+       return cur >= 0;
+}
+
 static int download_bundle_list(struct repository *r,
                                struct bundle_list *local_list,
                                struct bundle_list *global_list,
@@ -426,7 +661,15 @@ static int fetch_bundle_list_in_config_format(struct repository *r,
                goto cleanup;
        }
 
-       if ((result = download_bundle_list(r, &list_from_bundle,
+       /*
+        * If this list uses the creationToken heuristic, then the URIs
+        * it advertises are expected to be bundles, not nested lists.
+        * We can drop 'global_list' and 'depth'.
+        */
+       if (list_from_bundle.heuristic == BUNDLE_HEURISTIC_CREATIONTOKEN) {
+               result = fetch_bundles_by_token(r, &list_from_bundle);
+               global_list->heuristic = BUNDLE_HEURISTIC_CREATIONTOKEN;
+       } else if ((result = download_bundle_list(r, &list_from_bundle,
                                           global_list, depth)))
                goto cleanup;
 
@@ -537,7 +780,8 @@ static int unlink_bundle(struct remote_bundle_info *info, void *data)
        return 0;
 }
 
-int fetch_bundle_uri(struct repository *r, const char *uri)
+int fetch_bundle_uri(struct repository *r, const char *uri,
+                    int *has_heuristic)
 {
        int result;
        struct bundle_list list;
@@ -557,12 +801,96 @@ int fetch_bundle_uri(struct repository *r, const char *uri)
        result = unbundle_all_bundles(r, &list);
 
 cleanup:
+       if (has_heuristic)
+               *has_heuristic = (list.heuristic != BUNDLE_HEURISTIC_NONE);
        for_all_bundles_in_list(&list, unlink_bundle, NULL);
        clear_bundle_list(&list);
        clear_remote_bundle_info(&bundle, NULL);
        return result;
 }
 
+int fetch_bundle_list(struct repository *r, struct bundle_list *list)
+{
+       int result;
+       struct bundle_list global_list;
+
+       /*
+        * If the creationToken heuristic is used, then the URIs
+        * advertised by 'list' are not nested lists and instead
+        * direct bundles. We do not need to use global_list.
+        */
+       if (list->heuristic == BUNDLE_HEURISTIC_CREATIONTOKEN)
+               return fetch_bundles_by_token(r, list);
+
+       init_bundle_list(&global_list);
+
+       /* If a bundle is added to this global list, then it is required. */
+       global_list.mode = BUNDLE_MODE_ALL;
+
+       if ((result = download_bundle_list(r, list, &global_list, 0)))
+               goto cleanup;
+
+       if (list->heuristic == BUNDLE_HEURISTIC_CREATIONTOKEN)
+               result = fetch_bundles_by_token(r, list);
+       else
+               result = unbundle_all_bundles(r, &global_list);
+
+cleanup:
+       for_all_bundles_in_list(&global_list, unlink_bundle, NULL);
+       clear_bundle_list(&global_list);
+       return result;
+}
+
+/**
+ * API for serve.c.
+ */
+
+int bundle_uri_advertise(struct repository *r, struct strbuf *value UNUSED)
+{
+       static int advertise_bundle_uri = -1;
+
+       if (advertise_bundle_uri != -1)
+               goto cached;
+
+       advertise_bundle_uri = 0;
+       repo_config_get_maybe_bool(r, "uploadpack.advertisebundleuris", &advertise_bundle_uri);
+
+cached:
+       return advertise_bundle_uri;
+}
+
+static int config_to_packet_line(const char *key, const char *value, void *data)
+{
+       struct packet_reader *writer = data;
+
+       if (starts_with(key, "bundle."))
+               packet_write_fmt(writer->fd, "%s=%s", key, value);
+
+       return 0;
+}
+
+int bundle_uri_command(struct repository *r,
+                      struct packet_reader *request)
+{
+       struct packet_writer writer;
+       packet_writer_init(&writer, 1);
+
+       while (packet_reader_read(request) == PACKET_READ_NORMAL)
+               die(_("bundle-uri: unexpected argument: '%s'"), request->line);
+       if (request->status != PACKET_READ_FLUSH)
+               die(_("bundle-uri: expected flush after arguments"));
+
+       /*
+        * Read all "bundle.*" config lines to the client as key=value
+        * packet lines.
+        */
+       git_config(config_to_packet_line, &writer);
+
+       packet_writer_flush(&writer);
+
+       return 0;
+}
+
 /**
  * General API for {transport,connect}.c etc.
  */
index 4dbc269823cc3a6b4a523b199a8a6d8a83219578..6dbc780f661bc06bbc9f0badddec91101814349c 100644 (file)
@@ -4,6 +4,7 @@
 #include "hashmap.h"
 #include "strbuf.h"
 
+struct packet_reader;
 struct repository;
 struct string_list;
 
@@ -41,6 +42,12 @@ struct remote_bundle_info {
         * this boolean is true.
         */
        unsigned unbundled:1;
+
+       /**
+        * If the bundle is part of a list with the creationToken
+        * heuristic, then we use this member for sorting the bundles.
+        */
+       uint64_t creationToken;
 };
 
 #define REMOTE_BUNDLE_INFO_INIT { 0 }
@@ -51,6 +58,14 @@ enum bundle_list_mode {
        BUNDLE_MODE_ANY
 };
 
+enum bundle_list_heuristic {
+       BUNDLE_HEURISTIC_NONE = 0,
+       BUNDLE_HEURISTIC_CREATIONTOKEN,
+
+       /* Must be last. */
+       BUNDLE_HEURISTIC__COUNT
+};
+
 /**
  * A bundle_list contains an unordered set of remote_bundle_info structs,
  * as well as information about the bundle listing, such as version and
@@ -60,6 +75,26 @@ struct bundle_list {
        int version;
        enum bundle_list_mode mode;
        struct hashmap bundles;
+
+       /**
+        * The baseURI of a bundle_list is the URI that provided the list.
+        *
+        * In the case of the 'bundle-uri' protocol v2 command, the base
+        * URI is the URI of the Git remote.
+        *
+        * Otherwise, the bundle list was downloaded over HTTP from some
+        * known URI. 'baseURI' is set to that value.
+        *
+        * The baseURI is used as the base for any relative URIs
+        * advertised by the bundle list at that location.
+        */
+       char *baseURI;
+
+       /**
+        * A list can have a heuristic, which helps reduce the number of
+        * downloaded bundles.
+        */
+       enum bundle_list_heuristic heuristic;
 };
 
 void init_bundle_list(struct bundle_list *list);
@@ -89,8 +124,34 @@ int bundle_uri_parse_config_format(const char *uri,
  * based on that information.
  *
  * Returns non-zero if no bundle information is found at the given 'uri'.
+ *
+ * If the pointer 'has_heuristic' is non-NULL, then the value it points to
+ * will be set to be non-zero if and only if the fetched list has a
+ * heuristic value. Such a value indicates that the list was designed for
+ * incremental fetches.
+ */
+int fetch_bundle_uri(struct repository *r, const char *uri,
+                    int *has_heuristic);
+
+/**
+ * Given a bundle list that was already advertised (likely by the
+ * bundle-uri protocol v2 verb) at the given uri, fetch and unbundle the
+ * bundles according to the bundle strategy of that list.
+ *
+ * It is expected that the given 'list' is initialized, including its
+ * 'baseURI' value.
+ *
+ * Returns non-zero if there was an error trying to download the list
+ * or any of its advertised bundles.
+ */
+int fetch_bundle_list(struct repository *r,
+                     struct bundle_list *list);
+
+/**
+ * API for serve.c.
  */
-int fetch_bundle_uri(struct repository *r, const char *uri);
+int bundle_uri_advertise(struct repository *r, struct strbuf *value);
+int bundle_uri_command(struct repository *r, struct packet_reader *request);
 
 /**
  * General API for {transport,connect}.c etc.
index 4ef7256aa11e8b43691f238970da6c6bf21cd4ee..6ab6cd7378d2d633d5d6dc6100e98793799c7f4e 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -12,6 +12,7 @@
 #include "refs.h"
 #include "strvec.h"
 #include "list-objects-filter-options.h"
+#include "connected.h"
 
 static const char v2_bundle_signature[] = "# v2 git bundle\n";
 static const char v3_bundle_signature[] = "# v3 git bundle\n";
@@ -187,6 +188,21 @@ static int list_refs(struct string_list *r, int argc, const char **argv)
 /* Remember to update object flag allocation in object.h */
 #define PREREQ_MARK (1u<<16)
 
+struct string_list_iterator {
+       struct string_list *list;
+       size_t cur;
+};
+
+static const struct object_id *iterate_ref_map(void *cb_data)
+{
+       struct string_list_iterator *iter = cb_data;
+
+       if (iter->cur >= iter->list->nr)
+               return NULL;
+
+       return iter->list->items[iter->cur++].util;
+}
+
 int verify_bundle(struct repository *r,
                  struct bundle_header *header,
                  enum verify_bundle_flags flags)
@@ -196,26 +212,25 @@ int verify_bundle(struct repository *r,
         * to be verbose about the errors
         */
        struct string_list *p = &header->prerequisites;
-       struct rev_info revs = REV_INFO_INIT;
-       const char *argv[] = {NULL, "--all", NULL};
-       struct commit *commit;
-       int i, ret = 0, req_nr;
+       int i, ret = 0;
        const char *message = _("Repository lacks these prerequisite commits:");
+       struct string_list_iterator iter = {
+               .list = p,
+       };
+       struct check_connected_options opts = {
+               .quiet = 1,
+       };
 
        if (!r || !r->objects || !r->objects->odb)
                return error(_("need a repository to verify a bundle"));
 
-       repo_init_revisions(r, &revs, NULL);
        for (i = 0; i < p->nr; i++) {
                struct string_list_item *e = p->items + i;
                const char *name = e->string;
                struct object_id *oid = e->util;
                struct object *o = parse_object(r, oid);
-               if (o) {
-                       o->flags |= PREREQ_MARK;
-                       add_pending_object(&revs, o, name);
+               if (o)
                        continue;
-               }
                ret++;
                if (flags & VERIFY_BUNDLE_QUIET)
                        continue;
@@ -223,37 +238,14 @@ int verify_bundle(struct repository *r,
                        error("%s", message);
                error("%s %s", oid_to_hex(oid), name);
        }
-       if (revs.pending.nr != p->nr)
+       if (ret)
                goto cleanup;
-       req_nr = revs.pending.nr;
-       setup_revisions(2, argv, &revs, NULL);
 
-       list_objects_filter_copy(&revs.filter, &header->filter);
-
-       if (prepare_revision_walk(&revs))
-               die(_("revision walk setup failed"));
-
-       i = req_nr;
-       while (i && (commit = get_revision(&revs)))
-               if (commit->object.flags & PREREQ_MARK)
-                       i--;
-
-       for (i = 0; i < p->nr; i++) {
-               struct string_list_item *e = p->items + i;
-               const char *name = e->string;
-               const struct object_id *oid = e->util;
-               struct object *o = parse_object(r, oid);
-               assert(o); /* otherwise we'd have returned early */
-               if (o->flags & SHOWN)
-                       continue;
-               ret++;
-               if (flags & VERIFY_BUNDLE_QUIET)
-                       continue;
-               if (ret == 1)
-                       error("%s", message);
-               error("%s %s", oid_to_hex(oid), name);
-       }
+       if ((ret = check_connected(iterate_ref_map, &iter, &opts)))
+               error(_("some prerequisite commits exist in the object store, "
+                       "but are not connected to the repository's history"));
 
+       /* TODO: preserve this verbose language. */
        if (flags & VERIFY_BUNDLE_VERBOSE) {
                struct string_list *r;
 
@@ -282,15 +274,6 @@ int verify_bundle(struct repository *r,
                                  list_objects_filter_spec(&header->filter));
        }
 cleanup:
-       /* Clean up objects used, as they will be reused. */
-       for (i = 0; i < p->nr; i++) {
-               struct string_list_item *e = p->items + i;
-               struct object_id *oid = e->util;
-               commit = lookup_commit_reference_gently(r, oid, 1);
-               if (commit)
-                       clear_commit_marks(commit, ALL_REV_FLAGS | PREREQ_MARK);
-       }
-       release_revisions(&revs);
        return ret;
 }
 
@@ -627,6 +610,10 @@ int unbundle(struct repository *r, struct bundle_header *header,
             enum verify_bundle_flags flags)
 {
        struct child_process ip = CHILD_PROCESS_INIT;
+
+       if (verify_bundle(r, header, flags))
+               return -1;
+
        strvec_pushl(&ip.args, "index-pack", "--fix-thin", "--stdin", NULL);
 
        /* If there is a filter, then we need to create the promisor pack. */
@@ -638,8 +625,6 @@ int unbundle(struct repository *r, struct bundle_header *header,
                strvec_clear(extra_index_pack_args);
        }
 
-       if (verify_bundle(r, header, flags))
-               return -1;
        ip.in = bundle_fd;
        ip.no_stdout = 1;
        ip.git_cmd = 1;
index c97111cccf2eda3a53be9dfce637fa2c35464f4d..88c2c04f87fe5fcfce380b47f9d9377f9b59d8ef 100644 (file)
@@ -405,7 +405,7 @@ static int update_one(struct cache_tree *it,
                }
 
                /*
-                * CE_INTENT_TO_ADD entries exist on on-disk index but
+                * CE_INTENT_TO_ADD entries exist in on-disk index but
                 * they are not part of generated trees. Invalidate up
                 * to root to force cache-tree users to read elsewhere.
                 */
@@ -760,7 +760,7 @@ static void prime_cache_tree_rec(struct repository *r,
        struct tree_desc desc;
        struct name_entry entry;
        int cnt;
-       int base_path_len = tree_path->len;
+       size_t base_path_len = tree_path->len;
 
        oidcpy(&it->oid, &tree->object.oid);
 
@@ -785,7 +785,6 @@ static void prime_cache_tree_rec(struct repository *r,
                         */
                        if (r->index->sparse_index) {
                                strbuf_setlen(tree_path, base_path_len);
-                               strbuf_grow(tree_path, base_path_len + entry.pathlen + 1);
                                strbuf_add(tree_path, entry.path, entry.pathlen);
                                strbuf_addch(tree_path, '/');
                        }
index 8efeccebfc9f0bf8c9cfda1aa4ac531108b52650..bd97caa07b08f6b5dc62f73485d93d232e179327 100644 (file)
@@ -53,19 +53,4 @@ int write_index_as_tree(struct object_id *oid, struct index_state *index_state,
 void prime_cache_tree(struct repository *, struct index_state *, struct tree *);
 
 int cache_tree_matches_traversal(struct cache_tree *, struct name_entry *ent, struct traverse_info *info);
-
-#ifdef USE_THE_INDEX_COMPATIBILITY_MACROS
-static inline int write_cache_as_tree(struct object_id *oid, int flags, const char *prefix)
-{
-       return write_index_as_tree(oid, &the_index, get_index_file(), flags, prefix);
-}
-
-static inline int update_main_cache_tree(int flags)
-{
-       if (!the_index.cache_tree)
-               the_index.cache_tree = cache_tree();
-       return cache_tree_update(&the_index, flags);
-}
-#endif
-
 #endif
diff --git a/cache.h b/cache.h
index 07d40b0964b12b9f4875c20f26540585ffdc792e..12789903e883df94783b75defa89fbbd5cbcca1f 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -360,6 +360,22 @@ struct index_state {
        struct pattern_list *sparse_checkout_patterns;
 };
 
+/**
+ * A "struct index_state istate" must be initialized with
+ * INDEX_STATE_INIT or the corresponding index_state_init().
+ *
+ * If the variable won't be used again, use release_index() to free()
+ * its resources. If it needs to be used again use discard_index(),
+ * which does the same thing, but will use use index_state_init() at
+ * the end. The discard_index() will use its own "istate->repo" as the
+ * "r" argument to index_state_init() in that case.
+ */
+#define INDEX_STATE_INIT(r) { \
+       .repo = (r), \
+}
+void index_state_init(struct index_state *istate, struct repository *r);
+void release_index(struct index_state *istate);
+
 /* Name hashing */
 int test_lazy_init_name_hash(struct index_state *istate, int try_threaded);
 void add_name_hash(struct index_state *istate, struct cache_entry *ce);
@@ -433,18 +449,8 @@ typedef int (*must_prefetch_predicate)(const struct cache_entry *);
 void prefetch_cache_entries(const struct index_state *istate,
                            must_prefetch_predicate must_prefetch);
 
-#if defined(USE_THE_INDEX_COMPATIBILITY_MACROS) || defined(USE_THE_INDEX_VARIABLE)
+#ifdef USE_THE_INDEX_VARIABLE
 extern struct index_state the_index;
-
-#ifndef USE_THE_INDEX_VARIABLE
-#ifdef USE_THE_INDEX_COMPATIBILITY_MACROS
-#define active_nr (the_index.cache_nr)
-
-#define read_cache() repo_read_index(the_repository)
-#define discard_cache() discard_index(&the_index)
-#define cache_name_pos(name, namelen) index_name_pos(&the_index,(name),(namelen))
-#endif
-#endif
 #endif
 
 #define TYPE_BITS 3
@@ -480,7 +486,6 @@ static inline enum object_type object_type(unsigned int mode)
 #define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
 #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
 #define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
-#define GIT_SUPER_PREFIX_ENVIRONMENT "GIT_INTERNAL_SUPER_PREFIX"
 #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
 #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
 #define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
@@ -566,7 +571,6 @@ int get_common_dir_noenv(struct strbuf *sb, const char *gitdir);
 int get_common_dir(struct strbuf *sb, const char *gitdir);
 const char *get_git_namespace(void);
 const char *strip_namespace(const char *namespaced_ref);
-const char *get_super_prefix(void);
 const char *get_git_work_tree(void);
 
 /*
@@ -1609,8 +1613,10 @@ int repo_interpret_branch_name(struct repository *r,
 
 int validate_headref(const char *ref);
 
-int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
-int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
+int base_name_compare(const char *name1, size_t len1, int mode1,
+                     const char *name2, size_t len2, int mode2);
+int df_name_compare(const char *name1, size_t len1, int mode1,
+                   const char *name2, size_t len2, int mode2);
 int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);
 int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);
 
@@ -1865,7 +1871,7 @@ unsigned ws_check(const char *line, int len, unsigned ws_rule);
 void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws);
 char *whitespace_error_string(unsigned ws);
 void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
-int ws_blank_line(const char *line, int len, unsigned ws_rule);
+int ws_blank_line(const char *line, int len);
 #define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)
 
 /* ls-files */
index 706e3ba7e938d83091d1557ed20f7356e557f61d..db7105e8a8dcdff1432d5ec972e3262e4f787c16 100755 (executable)
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -258,8 +258,7 @@ macos-*)
                MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python3)"
        else
                MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python2)"
-               MAKEFLAGS="$MAKEFLAGS NO_APPLE_COMMON_CRYPTO=NoThanks"
-               MAKEFLAGS="$MAKEFLAGS NO_OPENSSL=NoThanks"
+               MAKEFLAGS="$MAKEFLAGS APPLE_COMMON_CRYPTO_SHA1=Yes"
        fi
        ;;
 esac
index 8ebff4259676e303b953385ba14cf17b78c1a4de..b098e10f52aed2f9d8928958fd393cbd574ddf8b 100755 (executable)
@@ -26,7 +26,6 @@ linux-TEST-vars)
        export GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=1
        export GIT_TEST_MULTI_PACK_INDEX=1
        export GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=1
-       export GIT_TEST_ADD_I_USE_BUILTIN=0
        export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
        export GIT_TEST_WRITE_REV_INDEX=1
        export GIT_TEST_CHECKOUT_WORKERS=2
index b0ece9548082c17510b6abd17427434bbf66cae9..1a39b5dde0946df0c1e36c7b1f4c48552885985a 100644 (file)
@@ -372,7 +372,7 @@ struct combine_diff_state {
 static void consume_hunk(void *state_,
                         long ob, long on,
                         long nb, long nn,
-                        const char *funcline, long funclen)
+                        const char *func UNUSED, long funclen UNUSED)
 {
        struct combine_diff_state *state = state_;
 
index a7d8755932884cfcb05fad4c539c3b8be8322897..c11b59f28b3164cee21ab972088c32082d8f5bb6 100644 (file)
@@ -1594,8 +1594,7 @@ static void compute_bloom_filters(struct write_commit_graph_context *ctx)
                        _("Computing commit changed paths Bloom filters"),
                        ctx->commits.nr);
 
-       ALLOC_ARRAY(sorted_commits, ctx->commits.nr);
-       COPY_ARRAY(sorted_commits, ctx->commits.list, ctx->commits.nr);
+       DUP_ARRAY(sorted_commits, ctx->commits.list, ctx->commits.nr);
 
        if (ctx->order_by_pack)
                QSORT(sorted_commits, ctx->commits.nr, commit_pos_cmp);
index c226ee3da469c50c82dea58804ad3819230ed112..2e33c599a82c81127b9a24547d0834e6c7a55bcf 100644 (file)
@@ -245,8 +245,7 @@ static int remove_redundant_with_gen(struct repository *r,
         * min_gen_pos points to the current position within 'array'
         * that is not yet known to be STALE.
         */
-       ALLOC_ARRAY(sorted, cnt);
-       COPY_ARRAY(sorted, array, cnt);
+       DUP_ARRAY(sorted, array, cnt);
        QSORT(sorted, cnt, compare_commits_by_gen);
        min_generation = commit_graph_generation(sorted[0]);
 
index 572301b80a2a3723daea5e16bc6d77977c95543c..e433c33bb01fa6e3688a0b163c44d62f204d62d0 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -508,6 +508,17 @@ int repo_parse_commit_internal(struct repository *r,
        enum object_type type;
        void *buffer;
        unsigned long size;
+       struct object_info oi = {
+               .typep = &type,
+               .sizep = &size,
+               .contentp = &buffer,
+       };
+       /*
+        * Git does not support partial clones that exclude commits, so set
+        * OBJECT_INFO_SKIP_FETCH_OBJECT to fail fast when an object is missing.
+        */
+       int flags = OBJECT_INFO_LOOKUP_REPLACE | OBJECT_INFO_SKIP_FETCH_OBJECT |
+               OBJECT_INFO_DIE_IF_CORRUPT;
        int ret;
 
        if (!item)
@@ -516,8 +527,8 @@ int repo_parse_commit_internal(struct repository *r,
                return 0;
        if (use_commit_graph && parse_commit_in_graph(r, item))
                return 0;
-       buffer = repo_read_object_file(r, &item->object.oid, &type, &size);
-       if (!buffer)
+
+       if (oid_object_info_extended(r, &item->object.oid, &oi, flags) < 0)
                return quiet_on_missing ? -1 :
                        error("Could not read %s",
                             oid_to_hex(&item->object.oid));
@@ -701,8 +712,10 @@ static void clear_commit_marks_1(struct commit_list **plist,
                if (!parents)
                        return;
 
-               while ((parents = parents->next))
-                       commit_list_insert(parents->item, plist);
+               while ((parents = parents->next)) {
+                       if (parents->item->object.flags & mark)
+                               commit_list_insert(parents->item, plist);
+               }
 
                commit = commit->parents->item;
        }
@@ -1020,6 +1033,7 @@ struct commit *get_fork_point(const char *refname, struct commit *commit)
        ret = bases->item;
 
 cleanup_return:
+       free(revs.commit);
        free_commit_list(bases);
        free(full_refname);
        return ret;
index fa39202fa6b09837f2a08202bc1f758fa0eac4de..cc2c5da7bdb8344e9dbe69ae16092fcbf004d544 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -274,8 +274,6 @@ struct ref;
 int for_each_commit_graft(each_commit_graft_fn, void *);
 
 int interactive_add(const char **argv, const char *prefix, int patch);
-int run_add_interactive(const char *revision, const char *patch_mode,
-                       const struct pathspec *pathspec);
 
 struct commit_extra_header {
        struct commit_extra_header *next;
index 1c75c3d48e7d7c3d960e4cf7ff3bf672f4fe1432..3496e29b3a1f1b8ea04224b63b65539edda602df 100644 (file)
@@ -80,9 +80,7 @@ void CFRunLoopRun(void);
 void CFRunLoopStop(CFRunLoopRef run_loop);
 CFRunLoopRef CFRunLoopGetCurrent(void);
 extern CFStringRef kCFRunLoopDefaultMode;
-void FSEventStreamScheduleWithRunLoop(FSEventStreamRef stream,
-                                     CFRunLoopRef run_loop,
-                                     CFStringRef run_loop_mode);
+void FSEventStreamSetDispatchQueue(FSEventStreamRef stream, dispatch_queue_t q);
 unsigned char FSEventStreamStart(FSEventStreamRef stream);
 void FSEventStreamStop(FSEventStreamRef stream);
 void FSEventStreamInvalidate(FSEventStreamRef stream);
index cc9af1e3cb3b3afca7d519147c4ae252b9f5832a..97a55a6f0a42d8c0ba8b1d14e077e60e7fdd55df 100644 (file)
@@ -1,4 +1,5 @@
 #ifndef __clang__
+#include <dispatch/dispatch.h>
 #include "fsm-darwin-gcc.h"
 #else
 #include <CoreFoundation/CoreFoundation.h>
@@ -38,7 +39,9 @@ struct fsm_listen_data
 
        FSEventStreamRef stream;
 
-       CFRunLoopRef rl;
+       dispatch_queue_t dq;
+       pthread_cond_t dq_finished;
+       pthread_mutex_t dq_lock;
 
        enum shutdown_style {
                SHUTDOWN_EVENT = 0,
@@ -379,8 +382,11 @@ force_shutdown:
        fsmonitor_batch__free_list(batch);
        string_list_clear(&cookie_list, 0);
 
+       pthread_mutex_lock(&data->dq_lock);
        data->shutdown_style = FORCE_SHUTDOWN;
-       CFRunLoopStop(data->rl);
+       pthread_cond_broadcast(&data->dq_finished);
+       pthread_mutex_unlock(&data->dq_lock);
+
        strbuf_release(&tmp);
        return;
 }
@@ -441,10 +447,6 @@ int fsm_listen__ctor(struct fsmonitor_daemon_state *state)
        if (!data->stream)
                goto failed;
 
-       /*
-        * `data->rl` needs to be set inside the listener thread.
-        */
-
        return 0;
 
 failed:
@@ -471,6 +473,11 @@ void fsm_listen__dtor(struct fsmonitor_daemon_state *state)
                FSEventStreamRelease(data->stream);
        }
 
+       if (data->dq)
+               dispatch_release(data->dq);
+       pthread_cond_destroy(&data->dq_finished);
+       pthread_mutex_destroy(&data->dq_lock);
+
        FREE_AND_NULL(state->listen_data);
 }
 
@@ -479,9 +486,11 @@ void fsm_listen__stop_async(struct fsmonitor_daemon_state *state)
        struct fsm_listen_data *data;
 
        data = state->listen_data;
-       data->shutdown_style = SHUTDOWN_EVENT;
 
-       CFRunLoopStop(data->rl);
+       pthread_mutex_lock(&data->dq_lock);
+       data->shutdown_style = SHUTDOWN_EVENT;
+       pthread_cond_broadcast(&data->dq_finished);
+       pthread_mutex_unlock(&data->dq_lock);
 }
 
 void fsm_listen__loop(struct fsmonitor_daemon_state *state)
@@ -490,9 +499,11 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state)
 
        data = state->listen_data;
 
-       data->rl = CFRunLoopGetCurrent();
+       pthread_mutex_init(&data->dq_lock, NULL);
+       pthread_cond_init(&data->dq_finished, NULL);
+       data->dq = dispatch_queue_create("FSMonitor", NULL);
 
-       FSEventStreamScheduleWithRunLoop(data->stream, data->rl, kCFRunLoopDefaultMode);
+       FSEventStreamSetDispatchQueue(data->stream, data->dq);
        data->stream_scheduled = 1;
 
        if (!FSEventStreamStart(data->stream)) {
@@ -501,7 +512,9 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state)
        }
        data->stream_started = 1;
 
-       CFRunLoopRun();
+       pthread_mutex_lock(&data->dq_lock);
+       pthread_cond_wait(&data->dq_finished, &data->dq_lock);
+       pthread_mutex_unlock(&data->dq_lock);
 
        switch (data->shutdown_style) {
        case FORCE_ERROR_STOP:
index d614f156df1db31248e3a1de51273ffefdd671ff..e433740381be9cad8cd6c7453508931684832209 100644 (file)
@@ -1396,8 +1396,7 @@ static wchar_t *make_environment_block(char **deltaenv)
                        p += s;
                }
 
-               ALLOC_ARRAY(result, size);
-               COPY_ARRAY(result, wenv, size);
+               DUP_ARRAY(result, wenv, size);
                FreeEnvironmentStringsW(wenv);
                return result;
        }
@@ -1839,16 +1838,13 @@ static int try_shell_exec(const char *cmd, char *const *argv)
        if (prog) {
                int exec_id;
                int argc = 0;
-#ifndef _MSC_VER
-               const
-#endif
                char **argv2;
                while (argv[argc]) argc++;
                ALLOC_ARRAY(argv2, argc + 1);
                argv2[0] = (char *)cmd; /* full path to the script file */
                COPY_ARRAY(&argv2[1], &argv[1], argc);
-               exec_id = trace2_exec(prog, argv2);
-               pid = mingw_spawnv(prog, argv2, 1);
+               exec_id = trace2_exec(prog, (const char **)argv2);
+               pid = mingw_spawnv(prog, (const char **)argv2, 1);
                if (pid >= 0) {
                        int status;
                        if (waitpid(pid, &status, 0) < 0)
@@ -2752,7 +2748,7 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
                        /*
                         * On FAT32 volumes, ownership is not actually recorded.
                         */
-                       strbuf_addf(report, "'%s' is on a file system that does"
+                       strbuf_addf(report, "'%s' is on a file system that does "
                                    "not record ownership\n", path);
                } else if (report) {
                        LPSTR str1, str2, to_free1 = NULL, to_free2 = NULL;
diff --git a/compat/regcomp_enhanced.c b/compat/regcomp_enhanced.c
new file mode 100644 (file)
index 0000000..84193ce
--- /dev/null
@@ -0,0 +1,9 @@
+#include "../git-compat-util.h"
+#undef regcomp
+
+int git_regcomp(regex_t *preg, const char *pattern, int cflags)
+{
+       if (!(cflags & REG_EXTENDED))
+               cflags |= REG_ENHANCED;
+       return regcomp(preg, pattern, cflags);
+}
index 2e7eead42cb008980c7a3b4de63050897c2996da..85f8f7920ce48d418f4c77896004d17715582840 100644 (file)
@@ -22,12 +22,12 @@ static unsigned __stdcall win32_start_routine(void *arg)
 }
 
 int pthread_create(pthread_t *thread, const void *unused,
-                  void *(*start_routine)(void*), void *arg)
+                  void *(*start_routine)(void *), void *arg)
 {
        thread->arg = arg;
        thread->start_routine = start_routine;
-       thread->handle = (HANDLE)
-               _beginthreadex(NULL, 0, win32_start_routine, thread, 0, NULL);
+       thread->handle = (HANDLE)_beginthreadex(NULL, 0, win32_start_routine,
+                                               thread, 0, NULL);
 
        if (!thread->handle)
                return errno;
@@ -39,14 +39,17 @@ int win32_pthread_join(pthread_t *thread, void **value_ptr)
 {
        DWORD result = WaitForSingleObject(thread->handle, INFINITE);
        switch (result) {
-               case WAIT_OBJECT_0:
-                       if (value_ptr)
-                               *value_ptr = thread->arg;
-                       return 0;
-               case WAIT_ABANDONED:
-                       return EINVAL;
-               default:
-                       return err_win_to_posix(GetLastError());
+       case WAIT_OBJECT_0:
+               if (value_ptr)
+                       *value_ptr = thread->arg;
+               CloseHandle(thread->handle);
+               return 0;
+       case WAIT_ABANDONED:
+               CloseHandle(thread->handle);
+               return EINVAL;
+       default:
+               /* the wait failed, so do not detach */
+               return err_win_to_posix(GetLastError());
        }
 }
 
index 737983d00bae9190e6a328ccf9a2b1a4914fa109..cc3221cb2c8a84a454784c7fa0dc287dbad83983 100644 (file)
@@ -66,7 +66,7 @@ pthread_t pthread_self(void);
 
 static inline void NORETURN pthread_exit(void *ret)
 {
-       ExitThread((DWORD)(intptr_t)ret);
+       _endthreadex((unsigned)(uintptr_t)ret);
 }
 
 typedef DWORD pthread_key_t;
index 3abe8dd5a2711b5268cb5efe623d8c027f0d3d83..f83610f684d031f8660542b108a940207cad89df 100644 (file)
@@ -644,7 +644,7 @@ void winansi_init(void)
 
        /* start console spool thread on the pipe's read end */
        hthread = CreateThread(NULL, 0, console_thread, NULL, 0, NULL);
-       if (hthread == INVALID_HANDLE_VALUE)
+       if (!hthread)
                die_lasterr("CreateThread(console_thread) failed");
 
        /* schedule cleanup routine */
index 27f38283add34658df1d6956b0803717ec191f19..00090a32fc3a2bd82fe01f9060eb7b0b4f7b8ca1 100644 (file)
--- a/config.c
+++ b/config.c
@@ -3154,7 +3154,7 @@ int git_config_set_gently(const char *key, const char *value)
 int repo_config_set_worktree_gently(struct repository *r,
                                    const char *key, const char *value)
 {
-       /* Only use worktree-specific config if it is is already enabled. */
+       /* Only use worktree-specific config if it is already enabled. */
        if (repository_format_worktree_config) {
                char *file = repo_git_path(r, "config.worktree");
                int ret = git_config_set_multivar_in_file_gently(
index ef9eade6414e2661a6ab7e1970dafea3b3219114..7606246531a798a0f400a38742323626ae0d9524 100644 (file)
--- a/config.h
+++ b/config.h
@@ -447,15 +447,6 @@ void git_configset_init(struct config_set *cs);
  */
 int git_configset_add_file(struct config_set *cs, const char *filename);
 
-/**
- * Parses command line options and environment variables, and adds the
- * variable-value pairs to the `config_set`. Returns 0 on success, or -1
- * if there is an error in parsing. The caller decides whether to free
- * the incomplete configset or continue using it when the function
- * returns -1.
- */
-int git_configset_add_parameters(struct config_set *cs);
-
 /**
  * Finds and returns the value list, sorted in order of increasing priority
  * for the configuration variable `key` and config set `cs`. When the
index d63629fe807f59deda80ed780c1915df011bf862..64c44db8058e0ca530519814bd5f4a3714d49695 100644 (file)
@@ -147,6 +147,7 @@ ifeq ($(uname_S),Darwin)
        FREAD_READS_DIRECTORIES = UnfortunatelyYes
        HAVE_NS_GET_EXECUTABLE_PATH = YesPlease
        CSPRNG_METHOD = arc4random
+       USE_ENHANCED_BASIC_REGULAR_EXPRESSIONS = YesPlease
 
        # Workaround for `gettext` being keg-only and not even being linked via
        # `brew link --force gettext`, should be obsolete as of
@@ -623,6 +624,9 @@ ifeq ($(uname_S),NONSTOP_KERNEL)
        SHELL_PATH = /usr/coreutils/bin/bash
 endif
 ifeq ($(uname_S),MINGW)
+       ifeq ($(shell expr "$(uname_R)" : '1\.'),2)
+               $(error "Building with MSys is no longer supported")
+       endif
        pathsep = ;
        HAVE_ALLOCA_H = YesPlease
        NO_PREAD = YesPlease
@@ -652,7 +656,6 @@ ifeq ($(uname_S),MINGW)
        USE_WIN32_IPC = YesPlease
        USE_WIN32_MMAP = YesPlease
        MMAP_PREVENTS_DELETE = UnfortunatelyYes
-       USE_NED_ALLOCATOR = YesPlease
        UNRELIABLE_FSTAT = UnfortunatelyYes
        OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
        NO_REGEX = YesPlease
@@ -677,61 +680,43 @@ ifeq ($(uname_S),MINGW)
        RC = windres -O coff
        NATIVE_CRLF = YesPlease
        X = .exe
-ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
-       htmldir = doc/git/html/
-       prefix =
+       # MSys2
+       prefix = /usr/
+       # Enable DEP
+       BASIC_LDFLAGS += -Wl,--nxcompat
+       # Enable ASLR (unless debugging)
+       ifneq (,$(findstring -O,$(filter-out -O0 -Og,$(CFLAGS))))
+               BASIC_LDFLAGS += -Wl,--dynamicbase
+       endif
+       ifeq (MINGW32,$(MSYSTEM))
+               prefix = /mingw32
+               HOST_CPU = i686
+               BASIC_LDFLAGS += -Wl,--pic-executable,-e,_mainCRTStartup
+       endif
+       ifeq (MINGW64,$(MSYSTEM))
+               prefix = /mingw64
+               HOST_CPU = x86_64
+               BASIC_LDFLAGS += -Wl,--pic-executable,-e,mainCRTStartup
+       else
+               COMPAT_CFLAGS += -D_USE_32BIT_TIME_T
+               BASIC_LDFLAGS += -Wl,--large-address-aware
+       endif
+       CC = gcc
+       COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0 -DDETECT_MSYS_TTY \
+               -fstack-protector-strong
+       EXTLIBS += -lntdll
        INSTALL = /bin/install
-       EXTLIBS += /mingw/lib/libz.a
        INTERNAL_QSORT = YesPlease
        HAVE_LIBCHARSET_H = YesPlease
-       NO_GETTEXT = YesPlease
-       NO_PYTHON = YesPlease
-       COMPAT_CFLAGS += -D__USE_MINGW_ACCESS
-else
-       ifneq ($(shell expr "$(uname_R)" : '1\.'),2)
-               # MSys2
-               prefix = /usr/
-               # Enable DEP
-               BASIC_LDFLAGS += -Wl,--nxcompat
-               # Enable ASLR (unless debugging)
-               ifneq (,$(findstring -O,$(filter-out -O0 -Og,$(CFLAGS))))
-                       BASIC_LDFLAGS += -Wl,--dynamicbase
-               endif
-               ifeq (MINGW32,$(MSYSTEM))
-                       prefix = /mingw32
-                       HOST_CPU = i686
-                       BASIC_LDFLAGS += -Wl,--pic-executable,-e,_mainCRTStartup
-               endif
-               ifeq (MINGW64,$(MSYSTEM))
-                       prefix = /mingw64
-                       HOST_CPU = x86_64
-                       BASIC_LDFLAGS += -Wl,--pic-executable,-e,mainCRTStartup
-               else
-                       COMPAT_CFLAGS += -D_USE_32BIT_TIME_T
-                       BASIC_LDFLAGS += -Wl,--large-address-aware
-               endif
-               CC = gcc
-               COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0 -DDETECT_MSYS_TTY \
-                       -fstack-protector-strong
-               EXTLIBS += -lntdll
-               INSTALL = /bin/install
-               INTERNAL_QSORT = YesPlease
-               HAVE_LIBCHARSET_H = YesPlease
-               USE_GETTEXT_SCHEME = fallthrough
-               USE_LIBPCRE = YesPlease
-               USE_NED_ALLOCATOR = YesPlease
-               ifeq (/mingw64,$(subst 32,64,$(prefix)))
-                       # Move system config into top-level /etc/
-                       ETC_GITCONFIG = ../etc/gitconfig
-                       ETC_GITATTRIBUTES = ../etc/gitattributes
-               endif
-       else
-               COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO
-               NO_CURL = YesPlease
-               NO_PYTHON = YesPlease
+       USE_GETTEXT_SCHEME = fallthrough
+       USE_LIBPCRE = YesPlease
+       USE_NED_ALLOCATOR = YesPlease
+       ifeq (/mingw64,$(subst 32,64,$(prefix)))
+               # Move system config into top-level /etc/
+               ETC_GITCONFIG = ../etc/gitconfig
+               ETC_GITATTRIBUTES = ../etc/gitattributes
        endif
 endif
-endif
 ifeq ($(uname_S),QNX)
        COMPAT_CFLAGS += -DSA_RESTART=0
        EXPAT_NEEDS_XMLPARSE_H = YesPlease
index 5ea53deda23527ded642449fa9d803cc12e34477..63e59641c0d4d5c60fc4bee2761e6acd471535a2 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -15,6 +15,7 @@
 #include "version.h"
 #include "protocol.h"
 #include "alias.h"
+#include "bundle-uri.h"
 
 static char *server_capabilities_v1;
 static struct strvec server_capabilities_v2 = STRVEC_INIT;
@@ -66,7 +67,7 @@ static NORETURN void die_initial_contact(int unexpected)
 }
 
 /* Checks if the server supports the capability 'c' */
-int server_supports_v2(const char *c, int die_on_error)
+int server_supports_v2(const char *c)
 {
        int i;
 
@@ -76,11 +77,13 @@ int server_supports_v2(const char *c, int die_on_error)
                    (!*out || *out == '='))
                        return 1;
        }
+       return 0;
+}
 
-       if (die_on_error)
+void ensure_server_supports_v2(const char *c)
+{
+       if (!server_supports_v2(c))
                die(_("server doesn't support '%s'"), c);
-
-       return 0;
 }
 
 int server_feature_v2(const char *c, const char **v)
@@ -477,7 +480,7 @@ static void send_capabilities(int fd_out, struct packet_reader *reader)
 {
        const char *hash_name;
 
-       if (server_supports_v2("agent", 0))
+       if (server_supports_v2("agent"))
                packet_write_fmt(fd_out, "agent=%s", git_user_agent_sanitized());
 
        if (server_feature_v2("object-format", &hash_name)) {
@@ -491,6 +494,49 @@ static void send_capabilities(int fd_out, struct packet_reader *reader)
        }
 }
 
+int get_remote_bundle_uri(int fd_out, struct packet_reader *reader,
+                         struct bundle_list *bundles, int stateless_rpc)
+{
+       int line_nr = 1;
+
+       /* Assert bundle-uri support */
+       ensure_server_supports_v2("bundle-uri");
+
+       /* (Re-)send capabilities */
+       send_capabilities(fd_out, reader);
+
+       /* Send command */
+       packet_write_fmt(fd_out, "command=bundle-uri\n");
+       packet_delim(fd_out);
+
+       packet_flush(fd_out);
+
+       /* Process response from server */
+       while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
+               const char *line = reader->line;
+               line_nr++;
+
+               if (!bundle_uri_parse_line(bundles, line))
+                       continue;
+
+               return error(_("error on bundle-uri response line %d: %s"),
+                            line_nr, line);
+       }
+
+       if (reader->status != PACKET_READ_FLUSH)
+               return error(_("expected flush after bundle-uri listing"));
+
+       /*
+        * Might die(), but obscure enough that that's OK, e.g. in
+        * serve.c we'll call BUG() on its equivalent (the
+        * PACKET_READ_RESPONSE_END check).
+        */
+       check_stateless_delimiter(stateless_rpc, reader,
+                                 _("expected response end packet after ref listing"));
+
+       return 0;
+}
+
 struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
                             struct ref **list, int for_push,
                             struct transport_ls_refs_options *transport_options,
@@ -504,17 +550,18 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
                &transport_options->unborn_head_target : NULL;
        *list = NULL;
 
-       if (server_supports_v2("ls-refs", 1))
-               packet_write_fmt(fd_out, "command=ls-refs\n");
+       ensure_server_supports_v2("ls-refs");
+       packet_write_fmt(fd_out, "command=ls-refs\n");
 
        /* Send capabilities */
        send_capabilities(fd_out, reader);
 
-       if (server_options && server_options->nr &&
-           server_supports_v2("server-option", 1))
+       if (server_options && server_options->nr) {
+               ensure_server_supports_v2("server-option");
                for (i = 0; i < server_options->nr; i++)
                        packet_write_fmt(fd_out, "server-option=%s",
                                         server_options->items[i].string);
+       }
 
        packet_delim(fd_out);
        /* When pushing we don't want to request the peeled tags */
index c53586e929e8b6f11194d9aeb9934e0fbecd5983..b26f7de7841838e0d6e88bb81169191f579fb56d 100644 (file)
--- a/connect.h
+++ b/connect.h
@@ -20,7 +20,8 @@ enum protocol_version discover_version(struct packet_reader *reader);
 
 int server_supports_hash(const char *desired, int *feature_supported);
 const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp, int *offset);
-int server_supports_v2(const char *c, int die_on_error);
+int server_supports_v2(const char *c);
+void ensure_server_supports_v2(const char *c);
 int server_feature_v2(const char *c, const char **v);
 int server_supports_feature(const char *c, const char *feature,
                            int die_on_error);
index 4f6388eed76f77ebacb0a4b1f68075fc69c50565..b90fd61790cef00d4141bb2eb30f33522e6e8c47 100644 (file)
@@ -85,6 +85,7 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
 promisor_pack_found:
                        ;
                } while ((oid = fn(cb_data)) != NULL);
+               free(new_pack);
                return 0;
        }
 
@@ -121,8 +122,10 @@ no_promisor_pack_found:
        else
                rev_list.no_stderr = opt->quiet;
 
-       if (start_command(&rev_list))
+       if (start_command(&rev_list)) {
+               free(new_pack);
                return error(_("Could not run 'git rev-list'"));
+       }
 
        sigchain_push(SIGPIPE, SIG_IGN);
 
@@ -154,5 +157,6 @@ no_promisor_pack_found:
                err = error_errno(_("failed to close rev-list's stdin"));
 
        sigchain_pop(SIGPIPE);
+       free(new_pack);
        return finish_command(&rev_list) || err;
 }
index aa759379508895ba002c531a13bde3f8995f9251..27a3b479c94e5cb42817c3d2bd1fff6f270ef583 100644 (file)
@@ -94,3 +94,10 @@ expression n != 1;
 @@
 - ptr = xcalloc(n, \( sizeof(*ptr) \| sizeof(T) \) )
 + CALLOC_ARRAY(ptr, n)
+
+@@
+expression dst, src, n;
+@@
+-ALLOC_ARRAY(dst, n);
+-COPY_ARRAY(dst, src, n);
++DUP_ARRAY(dst, src, n);
index 8520f03128a109cc83c559b7477539b8c4c21895..31e36cf3c4167e77a9b1d21154b39d6f31209411 100644 (file)
@@ -1,6 +1,7 @@
 // the_index.* variables
 @@
 identifier AC = active_cache;
+identifier AN = active_nr;
 identifier ACC = active_cache_changed;
 identifier ACT = active_cache_tree;
 @@
@@ -8,6 +9,9 @@ identifier ACT = active_cache_tree;
 - AC
 + the_index.cache
 |
+- AN
++ the_index.cache_nr
+|
 - ACC
 + the_index.cache_changed
 |
@@ -15,19 +19,13 @@ identifier ACT = active_cache_tree;
 + the_index.cache_tree
 )
 
-@@
-identifier AN = active_nr;
-identifier f != prepare_to_commit;
-@@
-  f(...) {<...
-- AN
-+ the_index.cache_nr
-  ...>}
-
 // "the_repository" simple cases
 @@
 @@
 (
+- read_cache
++ repo_read_index
+|
 - read_cache_unmerged
 + repo_read_index_unmerged
 |
@@ -96,6 +94,15 @@ identifier f != prepare_to_commit;
 |
 - resolve_undo_clear
 + resolve_undo_clear_index
+|
+- cache_name_pos
++ index_name_pos
+|
+- update_main_cache_tree
++ cache_tree_update
+|
+- discard_cache
++ discard_index
 )
   (
 + &the_index,
@@ -137,3 +144,14 @@ identifier f != prepare_to_commit;
   ...
 + , NULL, NULL, NULL
   )
+
+@@
+expression O;
+@@
+- write_cache_as_tree
++ write_index_as_tree
+  (
+- O,
++ O, &the_index, get_index_file(),
+  ...
+  )
diff --git a/contrib/coccinelle/index-compatibility.pending.cocci b/contrib/coccinelle/index-compatibility.pending.cocci
deleted file mode 100644 (file)
index 01f875d..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-// "the_repository" simple cases
-@@
-@@
-(
-- read_cache
-+ repo_read_index
-)
-  (
-+ the_repository,
-  ...)
-
-// "the_index" simple cases
-@@
-@@
-(
-- discard_cache
-+ discard_index
-|
-- cache_name_pos
-+ index_name_pos
-)
-  (
-+ &the_index,
-  ...)
index ba5c395d2d804f522d5730f7bca2fa6e17d63a96..dc95c34cc853557efd2a59a33825f834e8d934cf 100644 (file)
 #
 #     When set to "1" suggest all options, including options which are
 #     typically hidden (e.g. '--allow-empty' for 'git commit').
+#
+#   GIT_COMPLETION_IGNORE_CASE
+#
+#     When set, uses for-each-ref '--ignore-case' to find refs that match
+#     case insensitively, even on systems with case sensitive file systems
+#     (e.g., completing tag name "FOO" on "git checkout f<TAB>").
 
 case "$COMP_WORDBREAKS" in
 *:*) : great ;;
@@ -646,6 +652,7 @@ __git_heads ()
        local pfx="${1-}" cur_="${2-}" sfx="${3-}"
 
        __git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \
+                       ${GIT_COMPLETION_IGNORE_CASE+--ignore-case} \
                        "refs/heads/$cur_*" "refs/heads/$cur_*/**"
 }
 
@@ -659,6 +666,7 @@ __git_remote_heads ()
        local pfx="${1-}" cur_="${2-}" sfx="${3-}"
 
        __git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \
+                       ${GIT_COMPLETION_IGNORE_CASE+--ignore-case} \
                        "refs/remotes/$cur_*" "refs/remotes/$cur_*/**"
 }
 
@@ -669,6 +677,7 @@ __git_tags ()
        local pfx="${1-}" cur_="${2-}" sfx="${3-}"
 
        __git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \
+                       ${GIT_COMPLETION_IGNORE_CASE+--ignore-case} \
                        "refs/tags/$cur_*" "refs/tags/$cur_*/**"
 }
 
@@ -688,6 +697,7 @@ __git_dwim_remote_heads ()
        # but only output if the branch name is unique
        __git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \
                --sort="refname:strip=3" \
+               ${GIT_COMPLETION_IGNORE_CASE+--ignore-case} \
                "refs/remotes/*/$cur_*" "refs/remotes/*/$cur_*/**" | \
        uniq -u
 }
@@ -712,6 +722,7 @@ __git_refs ()
        local format refs
        local pfx="${3-}" cur_="${4-$cur}" sfx="${5-}"
        local match="${4-}"
+       local umatch="${4-}"
        local fer_pfx="${pfx//\%/%%}" # "escape" for-each-ref format specifiers
 
        __git_find_repo_path
@@ -735,12 +746,19 @@ __git_refs ()
                fi
        fi
 
+       if test "${GIT_COMPLETION_IGNORE_CASE:+1}" = "1"
+       then
+               # uppercase with tr instead of ${match,^^} for bash 3.2 compatibility
+               umatch=$(echo "$match" | tr a-z A-Z 2>/dev/null || echo "$match")
+       fi
+
        if [ "$list_refs_from" = path ]; then
                if [[ "$cur_" == ^* ]]; then
                        pfx="$pfx^"
                        fer_pfx="$fer_pfx^"
                        cur_=${cur_#^}
                        match=${match#^}
+                       umatch=${umatch#^}
                fi
                case "$cur_" in
                refs|refs/*)
@@ -751,7 +769,7 @@ __git_refs ()
                *)
                        for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD REBASE_HEAD CHERRY_PICK_HEAD; do
                                case "$i" in
-                               $match*)
+                               $match*|$umatch*)
                                        if [ -e "$dir/$i" ]; then
                                                echo "$pfx$i$sfx"
                                        fi
@@ -765,6 +783,7 @@ __git_refs ()
                        ;;
                esac
                __git_dir="$dir" __git for-each-ref --format="$fer_pfx%($format)$sfx" \
+                       ${GIT_COMPLETION_IGNORE_CASE+--ignore-case} \
                        "${refs[@]}"
                if [ -n "$track" ]; then
                        __git_dwim_remote_heads "$pfx" "$match" "$sfx"
@@ -784,15 +803,16 @@ __git_refs ()
        *)
                if [ "$list_refs_from" = remote ]; then
                        case "HEAD" in
-                       $match*       echo "${pfx}HEAD$sfx" ;;
+                       $match*|$umatch*)       echo "${pfx}HEAD$sfx" ;;
                        esac
                        __git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \
+                               ${GIT_COMPLETION_IGNORE_CASE+--ignore-case} \
                                "refs/remotes/$remote/$match*" \
                                "refs/remotes/$remote/$match*/**"
                else
                        local query_symref
                        case "HEAD" in
-                       $match*       query_symref="HEAD" ;;
+                       $match*|$umatch*)       query_symref="HEAD" ;;
                        esac
                        __git ls-remote "$remote" $query_symref \
                                "refs/tags/$match*" "refs/heads/$match*" \
index 8bcace29d21eea15e224d9ef2a841b98fdf36fac..3211841305fcb3cc9cce22bcd6c445cf327e274f 100644 (file)
@@ -79,6 +79,14 @@ git jump grep -i foo_bar
 git config jump.grepCmd "ag --column"
 --------------------------------------------------
 
+You can use the optional argument '--stdout' to print the listing to
+standard output instead of feeding it to the editor. You can use the
+argument with M-x grep on Emacs:
+
+--------------------------------------------------
+# In Emacs, M-x grep and invoke "git jump --stdout <mode>"
+M-x grep<RET>git jump --stdout diff<RET>
+--------------------------------------------------
 
 Related Programs
 ----------------
@@ -100,7 +108,7 @@ Limitations
 -----------
 
 This script was written and tested with vim. Given that the quickfix
-format is the same as what gcc produces, I expect emacs users have a
+format is the same as what gcc produces, I expect other tools have a
 similar feature for iterating through the list, but I know nothing about
 how to activate it.
 
index 92dbd4cde18ea18f6933ed79648a61d0c7493796..40c4b0d111071676c278651f49669036e90f079a 100755 (executable)
@@ -2,7 +2,7 @@
 
 usage() {
        cat <<\EOF
-usage: git jump <mode> [<args>]
+usage: git jump [--stdout] <mode> [<args>]
 
 Jump to interesting elements in an editor.
 The <mode> parameter is one of:
@@ -15,12 +15,30 @@ grep: elements are grep hits. Arguments are given to git grep or, if
       configured, to the command in `jump.grepCmd`.
 
 ws: elements are whitespace errors. Arguments are given to diff --check.
+
+If the optional argument `--stdout` is given, print the quickfix
+lines to standard output instead of feeding it to the editor.
 EOF
 }
 
 open_editor() {
        editor=`git var GIT_EDITOR`
-       eval "$editor -q \$1"
+       case "$editor" in
+       *emacs*)
+               # Supported editor values are:
+               # - emacs
+               # - emacsclient
+               # - emacsclient -t
+               #
+               # Wait for completion of the asynchronously executed process
+               # to avoid race conditions in case of "emacsclient".
+               eval "$editor --eval \"(let ((buf (grep \\\"cat \$1\\\"))) (pop-to-buffer buf) (select-frame-set-input-focus (selected-frame)) (while (get-buffer-process buf) (sleep-for 0.1)))\""
+               ;;
+       *)
+               # assume anything else is vi-compatible
+               eval "$editor -q \$1"
+               ;;
+       esac
 }
 
 mode_diff() {
@@ -64,15 +82,36 @@ mode_ws() {
        git diff --check "$@"
 }
 
+use_stdout=
+while test $# -gt 0; do
+       case "$1" in
+       --stdout)
+               use_stdout=t
+               ;;
+       --*)
+               usage >&2
+               exit 1
+               ;;
+       *)
+               break
+               ;;
+       esac
+       shift
+done
 if test $# -lt 1; then
        usage >&2
        exit 1
 fi
 mode=$1; shift
+type "mode_$mode" >/dev/null 2>&1 || { usage >&2; exit 1; }
+
+if test "$use_stdout" = "t"; then
+       "mode_$mode" "$@"
+       exit 0
+fi
 
 trap 'rm -f "$tmp"' 0 1 2 3 15
 tmp=`mktemp -t git-jump.XXXXXX` || exit 1
-type "mode_$mode" >/dev/null 2>&1 || { usage >&2; exit 1; }
 "mode_$mode" "$@" >"$tmp"
 test -s "$tmp" || exit 0
 open_editor "$tmp"
index 9b67649032092f63fff857df3c78f17a3a566ca8..a54d1690c083178fd0c1356c8c8c8bee5cfdbda1 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -1308,7 +1308,7 @@ void convert_attrs(struct index_state *istate,
                git_config(read_convert_config, NULL);
        }
 
-       git_check_attr(istate, path, check);
+       git_check_attr(istate, NULL, path, check);
        ccheck = check->items;
        ca->crlf_action = git_path_check_crlf(ccheck + 4);
        if (ca->crlf_action == CRLF_UNDEFINED)
index f6389a50684a6e99bad1fe39b04a96fb99abc726..f32011343f9400a80cb9999fe75e0eeefde48146 100644 (file)
@@ -7,6 +7,7 @@
 #include "prompt.h"
 #include "sigchain.h"
 #include "urlmatch.h"
+#include "git-compat-util.h"
 
 void credential_init(struct credential *c)
 {
@@ -234,6 +235,11 @@ int credential_read(struct credential *c, FILE *fp)
                } else if (!strcmp(key, "path")) {
                        free(c->path);
                        c->path = xstrdup(value);
+               } else if (!strcmp(key, "password_expiry_utc")) {
+                       errno = 0;
+                       c->password_expiry_utc = parse_timestamp(value, NULL, 10);
+                       if (c->password_expiry_utc == 0 || errno == ERANGE)
+                               c->password_expiry_utc = TIME_MAX;
                } else if (!strcmp(key, "url")) {
                        credential_from_url(c, value);
                } else if (!strcmp(key, "quit")) {
@@ -269,6 +275,11 @@ void credential_write(const struct credential *c, FILE *fp)
        credential_write_item(fp, "path", c->path, 0);
        credential_write_item(fp, "username", c->username, 0);
        credential_write_item(fp, "password", c->password, 0);
+       if (c->password_expiry_utc != TIME_MAX) {
+               char *s = xstrfmt("%"PRItime, c->password_expiry_utc);
+               credential_write_item(fp, "password_expiry_utc", s, 0);
+               free(s);
+       }
 }
 
 static int run_credential_helper(struct credential *c,
@@ -342,6 +353,12 @@ void credential_fill(struct credential *c)
 
        for (i = 0; i < c->helpers.nr; i++) {
                credential_do(c, c->helpers.items[i].string, "get");
+               if (c->password_expiry_utc < time(NULL)) {
+                       /* Discard expired password */
+                       FREE_AND_NULL(c->password);
+                       /* Reset expiry to maintain consistency */
+                       c->password_expiry_utc = TIME_MAX;
+               }
                if (c->username && c->password)
                        return;
                if (c->quit)
@@ -360,7 +377,7 @@ void credential_approve(struct credential *c)
 
        if (c->approved)
                return;
-       if (!c->username || !c->password)
+       if (!c->username || !c->password || c->password_expiry_utc < time(NULL))
                return;
 
        credential_apply_config(c);
@@ -381,6 +398,7 @@ void credential_reject(struct credential *c)
 
        FREE_AND_NULL(c->username);
        FREE_AND_NULL(c->password);
+       c->password_expiry_utc = TIME_MAX;
        c->approved = 0;
 }
 
index f430e77fea4869ea98156add3956e15a9fc94755..935b28a70f16ec788e9df0126ccfb00fa86de7cd 100644 (file)
@@ -126,10 +126,12 @@ struct credential {
        char *protocol;
        char *host;
        char *path;
+       timestamp_t password_expiry_utc;
 };
 
 #define CREDENTIAL_INIT { \
        .helpers = STRING_LIST_INIT_DUP, \
+       .password_expiry_utc = TIME_MAX, \
 }
 
 /* Initialize a credential structure, setting all fields to empty. */
index 59ef3398ca2b01676055fff40b80ac7896f87ffc..cce13c0f0473183a201a15e86de256cfd7bfc7d9 100644 (file)
@@ -45,7 +45,8 @@ void hashflush(struct hashfile *f)
        unsigned offset = f->offset;
 
        if (offset) {
-               the_hash_algo->update_fn(&f->ctx, f->buffer, offset);
+               if (!f->skip_hash)
+                       the_hash_algo->update_fn(&f->ctx, f->buffer, offset);
                flush(f, f->buffer, offset);
                f->offset = 0;
        }
@@ -64,7 +65,12 @@ int finalize_hashfile(struct hashfile *f, unsigned char *result,
        int fd;
 
        hashflush(f);
-       the_hash_algo->final_fn(f->buffer, &f->ctx);
+
+       if (f->skip_hash)
+               hashclr(f->buffer);
+       else
+               the_hash_algo->final_fn(f->buffer, &f->ctx);
+
        if (result)
                hashcpy(result, f->buffer);
        if (flags & CSUM_HASH_IN_STREAM)
@@ -108,7 +114,8 @@ void hashwrite(struct hashfile *f, const void *buf, unsigned int count)
                         * the hashfile's buffer. In this block,
                         * f->offset is necessarily zero.
                         */
-                       the_hash_algo->update_fn(&f->ctx, buf, nr);
+                       if (!f->skip_hash)
+                               the_hash_algo->update_fn(&f->ctx, buf, nr);
                        flush(f, buf, nr);
                } else {
                        /*
@@ -153,6 +160,7 @@ static struct hashfile *hashfd_internal(int fd, const char *name,
        f->tp = tp;
        f->name = name;
        f->do_crc = 0;
+       f->skip_hash = 0;
        the_hash_algo->init_fn(&f->ctx);
 
        f->buffer_len = buffer_len;
index 0d29f528fbcb51df13eac225c0e2cc120198bd3f..793a59da12b6e6bc23c0542e83f33dcfdc8509fe 100644 (file)
@@ -20,6 +20,13 @@ struct hashfile {
        size_t buffer_len;
        unsigned char *buffer;
        unsigned char *check_buffer;
+
+       /**
+        * If non-zero, skip_hash indicates that we should
+        * not actually compute the hash for this hashfile and
+        * instead only use it as a buffered write.
+        */
+       int skip_hash;
 };
 
 /* Checkpoint */
diff --git a/date.c b/date.c
index 53bd6a7932e3279fe0338c464efeef4416181fee..6f45eeb3568671e5b4bb37c2830315a490a12cfe 100644 (file)
--- a/date.c
+++ b/date.c
@@ -493,6 +493,12 @@ static int match_alpha(const char *date, struct tm *tm, int *offset)
                return 2;
        }
 
+       /* ISO-8601 allows yyyymmDD'T'HHMMSS, with less precision */
+       if (*date == 'T' && isdigit(date[1]) && tm->tm_hour == -1) {
+               tm->tm_min = tm->tm_sec = 0;
+               return 1;
+       }
+
        /* BAD CRAP */
        return skip_alpha(date);
 }
@@ -638,6 +644,18 @@ static inline int nodate(struct tm *tm)
                tm->tm_sec) < 0;
 }
 
+/*
+ * Have we seen an ISO-8601-alike date, i.e. 20220101T0,
+ * In which, hour is still unset,
+ * and minutes and second has been set to 0.
+ */
+static inline int maybeiso8601(struct tm *tm)
+{
+       return tm->tm_hour == -1 &&
+               tm->tm_min == 0 &&
+               tm->tm_sec == 0;
+}
+
 /*
  * We've seen a digit. Time? Year? Date?
  */
@@ -701,6 +719,25 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt
                return end - date;
        }
 
+       /* reduced precision of ISO-8601's time: HHMM or HH */
+       if (maybeiso8601(tm)) {
+               unsigned int num1 = num;
+               unsigned int num2 = 0;
+               if (n == 4) {
+                       num1 = num / 100;
+                       num2 = num % 100;
+               }
+               if ((n == 4 || n == 2) && !nodate(tm) &&
+                   set_time(num1, num2, 0, tm) == 0)
+                       return n;
+               /*
+                * We thought this is an ISO-8601 time string,
+                * we set minutes and seconds to 0,
+                * turn out it isn't, rollback the change.
+                */
+               tm->tm_min = tm->tm_sec = -1;
+       }
+
        /* Four-digit year or a timezone? */
        if (n == 4) {
                if (num <= 1400 && *offset == -1) {
index 90c0d6958f4003b6b883366788ff15943151204d..afdec0a878d9368b134342084442317e15007a9c 100644 (file)
@@ -513,6 +513,22 @@ void propagate_island_marks(struct commit *commit)
        }
 }
 
+void free_island_marks(void)
+{
+       struct island_bitmap *bitmap;
+
+       if (island_marks) {
+               kh_foreach_value(island_marks, bitmap, {
+                       if (!--bitmap->refcount)
+                               free(bitmap);
+               });
+               kh_destroy_oid_map(island_marks);
+       }
+
+       /* detect use-after-free with a an address which is never valid: */
+       island_marks = (void *)-1;
+}
+
 int compute_pack_layers(struct packing_data *to_pack)
 {
        uint32_t i;
index eb0f952629fc0a6cdcc113e19a04ddaa54bb32dd..8d1591ae28be302d2bb56fe05ad6c3465bf7668a 100644 (file)
@@ -14,5 +14,6 @@ void resolve_tree_islands(struct repository *r,
 void load_delta_islands(struct repository *r, int progress);
 void propagate_island_marks(struct commit *commit);
 int compute_pack_layers(struct packing_data *to_pack);
+void free_island_marks(void);
 
 #endif /* DELTA_ISLANDS_H */
index 2edea41a2345af623017d43e14a74d71a32bb873..dec040c366c82a71bc57bad9cf9db865182efe20 100644 (file)
@@ -673,7 +673,7 @@ int index_differs_from(struct repository *r,
        return (has_changes != 0);
 }
 
-static struct strbuf *idiff_prefix_cb(struct diff_options *opt, void *data)
+static struct strbuf *idiff_prefix_cb(struct diff_options *opt UNUSED, void *data)
 {
        return data;
 }
index 18edbdf4b59ec020cc58e3d16d01d92edfdc302e..05fafd0019b6d5acc7295b999a8a46c92e1a649e 100644 (file)
@@ -255,8 +255,7 @@ int diff_no_index(struct rev_info *revs,
        };
        struct option *options;
 
-       options = parse_options_concat(no_index_options,
-                                      revs->diffopt.parseopts);
+       options = add_diff_options(no_index_options, &revs->diffopt);
        argc = parse_options(argc, argv, revs->prefix, options,
                             diff_no_index_usage, 0);
        if (argc != 2) {
diff --git a/diff.c b/diff.c
index 1054a4b7329d0b70ed62311a728b5257df3a8143..469e18aed20ed0128bfa85774cb617d455287eaa 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -604,7 +604,7 @@ static unsigned long diff_filespec_size(struct repository *r,
        return one->size;
 }
 
-static int count_trailing_blank(mmfile_t *mf, unsigned ws_rule)
+static int count_trailing_blank(mmfile_t *mf)
 {
        char *ptr = mf->ptr;
        long size = mf->size;
@@ -622,7 +622,7 @@ static int count_trailing_blank(mmfile_t *mf, unsigned ws_rule)
                for (prev_eol = ptr; mf->ptr <= prev_eol; prev_eol--)
                        if (*prev_eol == '\n')
                                break;
-               if (!ws_blank_line(prev_eol + 1, ptr - prev_eol, ws_rule))
+               if (!ws_blank_line(prev_eol + 1, ptr - prev_eol))
                        break;
                cnt++;
                ptr = prev_eol - 1;
@@ -634,9 +634,8 @@ static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2,
                               struct emit_callback *ecbdata)
 {
        int l1, l2, at;
-       unsigned ws_rule = ecbdata->ws_rule;
-       l1 = count_trailing_blank(mf1, ws_rule);
-       l2 = count_trailing_blank(mf2, ws_rule);
+       l1 = count_trailing_blank(mf1);
+       l2 = count_trailing_blank(mf2);
        if (l2 <= l1) {
                ecbdata->blank_at_eof_in_preimage = 0;
                ecbdata->blank_at_eof_in_postimage = 0;
@@ -1583,7 +1582,7 @@ static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line
              ecbdata->blank_at_eof_in_preimage <= ecbdata->lno_in_preimage &&
              ecbdata->blank_at_eof_in_postimage <= ecbdata->lno_in_postimage))
                return 0;
-       return ws_blank_line(line, len, ecbdata->ws_rule);
+       return ws_blank_line(line, len);
 }
 
 static void emit_add_line(struct emit_callback *ecbdata,
@@ -1955,7 +1954,7 @@ static int color_words_output_graph_prefix(struct diff_words_data *diff_words)
 static void fn_out_diff_words_aux(void *priv,
                                  long minus_first, long minus_len,
                                  long plus_first, long plus_len,
-                                 const char *func, long funclen)
+                                 const char *func UNUSED, long funclen UNUSED)
 {
        struct diff_words_data *diff_words = priv;
        struct diff_words_style *style = diff_words->style;
@@ -2801,7 +2800,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                else if (file->is_unmerged) {
                        strbuf_addf(&out, " %s%s%*s | %*s",
                                    prefix, name, padding, "",
-                                   number_width, "Unmerged");
+                                   number_width, "Unmerged\n");
                        emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE,
                                         out.buf, out.len, 0);
                        strbuf_reset(&out);
@@ -3185,8 +3184,9 @@ static int is_conflict_marker(const char *line, int marker_size, unsigned long l
 }
 
 static void checkdiff_consume_hunk(void *priv,
-                                  long ob, long on, long nb, long nn,
-                                  const char *func, long funclen)
+                                  long ob UNUSED, long on UNUSED,
+                                  long nb, long nn UNUSED,
+                                  const char *func UNUSED, long funclen UNUSED)
 
 {
        struct checkdiff_t *data = priv;
@@ -3437,6 +3437,22 @@ static int diff_filepair_is_phoney(struct diff_filespec *one,
        return !DIFF_FILE_VALID(one) && !DIFF_FILE_VALID(two);
 }
 
+static int set_diff_algorithm(struct diff_options *opts,
+                             const char *alg)
+{
+       long value = parse_algorithm_value(alg);
+
+       if (value < 0)
+               return -1;
+
+       /* clear out previous settings */
+       DIFF_XDL_CLR(opts, NEED_MINIMAL);
+       opts->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;
+       opts->xdl_opts |= value;
+
+       return 0;
+}
+
 static void builtin_diff(const char *name_a,
                         const char *name_b,
                         struct diff_filespec *one,
@@ -4213,7 +4229,6 @@ static void prep_temp_blob(struct index_state *istate,
 }
 
 static struct diff_tempfile *prepare_temp_file(struct repository *r,
-                                              const char *name,
                                               struct diff_filespec *one)
 {
        struct diff_tempfile *temp = claim_diff_tempfile();
@@ -4231,18 +4246,18 @@ static struct diff_tempfile *prepare_temp_file(struct repository *r,
 
        if (!S_ISGITLINK(one->mode) &&
            (!one->oid_valid ||
-            reuse_worktree_file(r->index, name, &one->oid, 1))) {
+            reuse_worktree_file(r->index, one->path, &one->oid, 1))) {
                struct stat st;
-               if (lstat(name, &st) < 0) {
+               if (lstat(one->path, &st) < 0) {
                        if (errno == ENOENT)
                                goto not_a_valid_file;
-                       die_errno("stat(%s)", name);
+                       die_errno("stat(%s)", one->path);
                }
                if (S_ISLNK(st.st_mode)) {
                        struct strbuf sb = STRBUF_INIT;
-                       if (strbuf_readlink(&sb, name, st.st_size) < 0)
-                               die_errno("readlink(%s)", name);
-                       prep_temp_blob(r->index, name, temp, sb.buf, sb.len,
+                       if (strbuf_readlink(&sb, one->path, st.st_size) < 0)
+                               die_errno("readlink(%s)", one->path);
+                       prep_temp_blob(r->index, one->path, temp, sb.buf, sb.len,
                                       (one->oid_valid ?
                                        &one->oid : null_oid()),
                                       (one->oid_valid ?
@@ -4251,7 +4266,7 @@ static struct diff_tempfile *prepare_temp_file(struct repository *r,
                }
                else {
                        /* we can borrow from the file in the work tree */
-                       temp->name = name;
+                       temp->name = one->path;
                        if (!one->oid_valid)
                                oid_to_hex_r(temp->hex, null_oid());
                        else
@@ -4269,7 +4284,7 @@ static struct diff_tempfile *prepare_temp_file(struct repository *r,
        else {
                if (diff_populate_filespec(r, one, NULL))
                        die("cannot read data blob for %s", one->path);
-               prep_temp_blob(r->index, name, temp,
+               prep_temp_blob(r->index, one->path, temp,
                               one->data, one->size,
                               &one->oid, one->mode);
        }
@@ -4278,10 +4293,9 @@ static struct diff_tempfile *prepare_temp_file(struct repository *r,
 
 static void add_external_diff_name(struct repository *r,
                                   struct strvec *argv,
-                                  const char *name,
                                   struct diff_filespec *df)
 {
-       struct diff_tempfile *temp = prepare_temp_file(r, name, df);
+       struct diff_tempfile *temp = prepare_temp_file(r, df);
        strvec_push(argv, temp->name);
        strvec_push(argv, temp->hex);
        strvec_push(argv, temp->mode);
@@ -4308,11 +4322,9 @@ static void run_external_diff(const char *pgm,
        strvec_push(&cmd.args, name);
 
        if (one && two) {
-               add_external_diff_name(o->repo, &cmd.args, name, one);
-               if (!other)
-                       add_external_diff_name(o->repo, &cmd.args, name, two);
-               else {
-                       add_external_diff_name(o->repo, &cmd.args, other, two);
+               add_external_diff_name(o->repo, &cmd.args, one);
+               add_external_diff_name(o->repo, &cmd.args, two);
+               if (other) {
                        strvec_push(&cmd.args, other);
                        strvec_push(&cmd.args, xfrm_msg);
                }
@@ -4444,15 +4456,13 @@ static void run_diff_cmd(const char *pgm,
        const char *xfrm_msg = NULL;
        int complete_rewrite = (p->status == DIFF_STATUS_MODIFIED) && p->score;
        int must_show_header = 0;
+       struct userdiff_driver *drv = NULL;
 
-
-       if (o->flags.allow_external) {
-               struct userdiff_driver *drv;
-
+       if (o->flags.allow_external || !o->ignore_driver_algorithm)
                drv = userdiff_find_by_path(o->repo->index, attr_path);
-               if (drv && drv->external)
-                       pgm = drv->external;
-       }
+
+       if (o->flags.allow_external && drv && drv->external)
+               pgm = drv->external;
 
        if (msg) {
                /*
@@ -4469,12 +4479,16 @@ static void run_diff_cmd(const char *pgm,
                run_external_diff(pgm, name, other, one, two, xfrm_msg, o);
                return;
        }
-       if (one && two)
+       if (one && two) {
+               if (!o->ignore_driver_algorithm && drv && drv->algorithm)
+                       set_diff_algorithm(o, drv->algorithm);
+
                builtin_diff(name, other ? other : name,
                             one, two, xfrm_msg, must_show_header,
                             o, complete_rewrite);
-       else
+       } else {
                fprintf(o->file, "* Unmerged path %s\n", name);
+       }
 }
 
 static void diff_fill_oid_info(struct diff_filespec *one, struct index_state *istate)
@@ -4571,6 +4585,14 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
        const char *name;
        const char *other;
 
+       if (!o->ignore_driver_algorithm) {
+               struct userdiff_driver *drv = userdiff_find_by_path(o->repo->index,
+                                                                   p->one->path);
+
+               if (drv && drv->algorithm)
+                       set_diff_algorithm(o, drv->algorithm);
+       }
+
        if (DIFF_PAIR_UNMERGED(p)) {
                /* unmerged */
                builtin_diffstat(p->one->path, NULL, NULL, NULL,
@@ -4615,8 +4637,6 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
        builtin_checkdiff(name, other, attr_path, p->one, p->two, o);
 }
 
-static void prep_parse_options(struct diff_options *options);
-
 void repo_diff_setup(struct repository *r, struct diff_options *options)
 {
        memcpy(options, &default_diff_options, sizeof(*options));
@@ -4662,8 +4682,6 @@ void repo_diff_setup(struct repository *r, struct diff_options *options)
 
        options->color_moved = diff_color_moved_default;
        options->color_moved_ws_handling = diff_color_moved_ws_default;
-
-       prep_parse_options(options);
 }
 
 static const char diff_status_letters[] = {
@@ -4821,8 +4839,6 @@ void diff_setup_done(struct diff_options *options)
                        options->filter = ~filter_bit[DIFF_STATUS_FILTER_AON];
                options->filter &= ~options->filter_not;
        }
-
-       FREE_AND_NULL(options->parseopts);
 }
 
 int parse_long_opt(const char *opt, const char **argv,
@@ -5117,17 +5133,32 @@ static int diff_opt_diff_algorithm(const struct option *opt,
                                   const char *arg, int unset)
 {
        struct diff_options *options = opt->value;
-       long value = parse_algorithm_value(arg);
 
        BUG_ON_OPT_NEG(unset);
-       if (value < 0)
+
+       if (set_diff_algorithm(options, arg))
                return error(_("option diff-algorithm accepts \"myers\", "
                               "\"minimal\", \"patience\" and \"histogram\""));
 
-       /* clear out previous settings */
-       DIFF_XDL_CLR(options, NEED_MINIMAL);
-       options->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;
-       options->xdl_opts |= value;
+       options->ignore_driver_algorithm = 1;
+
+       return 0;
+}
+
+static int diff_opt_diff_algorithm_no_arg(const struct option *opt,
+                                  const char *arg, int unset)
+{
+       struct diff_options *options = opt->value;
+
+       BUG_ON_OPT_NEG(unset);
+       BUG_ON_OPT_ARG(arg);
+
+       if (set_diff_algorithm(options, opt->long_name))
+               BUG("available diff algorithms include \"myers\", "
+                              "\"minimal\", \"patience\" and \"histogram\"");
+
+       options->ignore_driver_algorithm = 1;
+
        return 0;
 }
 
@@ -5260,7 +5291,6 @@ static int diff_opt_patience(const struct option *opt,
 
        BUG_ON_OPT_NEG(unset);
        BUG_ON_OPT_ARG(arg);
-       options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
        /*
         * Both --patience and --anchored use PATIENCE_DIFF
         * internally, so remove any anchors previously
@@ -5269,7 +5299,9 @@ static int diff_opt_patience(const struct option *opt,
        for (i = 0; i < options->anchors_nr; i++)
                free(options->anchors[i]);
        options->anchors_nr = 0;
-       return 0;
+       options->ignore_driver_algorithm = 1;
+
+       return set_diff_algorithm(options, "patience");
 }
 
 static int diff_opt_ignore_regex(const struct option *opt,
@@ -5419,7 +5451,8 @@ static int diff_opt_rotate_to(const struct option *opt, const char *arg, int uns
        return 0;
 }
 
-static void prep_parse_options(struct diff_options *options)
+struct option *add_diff_options(const struct option *opts,
+                               struct diff_options *options)
 {
        struct option parseopts[] = {
                OPT_GROUP(N_("Diff output format options")),
@@ -5571,9 +5604,10 @@ static void prep_parse_options(struct diff_options *options)
                            N_("prevent rename/copy detection if the number of rename/copy targets exceeds given limit")),
 
                OPT_GROUP(N_("Diff algorithm options")),
-               OPT_BIT(0, "minimal", &options->xdl_opts,
-                       N_("produce the smallest possible diff"),
-                       XDF_NEED_MINIMAL),
+               OPT_CALLBACK_F(0, "minimal", options, NULL,
+                              N_("produce the smallest possible diff"),
+                              PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+                              diff_opt_diff_algorithm_no_arg),
                OPT_BIT_F('w', "ignore-all-space", &options->xdl_opts,
                          N_("ignore whitespace when comparing lines"),
                          XDF_IGNORE_WHITESPACE, PARSE_OPT_NONEG),
@@ -5599,9 +5633,10 @@ static void prep_parse_options(struct diff_options *options)
                               N_("generate diff using the \"patience diff\" algorithm"),
                               PARSE_OPT_NONEG | PARSE_OPT_NOARG,
                               diff_opt_patience),
-               OPT_BITOP(0, "histogram", &options->xdl_opts,
-                         N_("generate diff using the \"histogram diff\" algorithm"),
-                         XDF_HISTOGRAM_DIFF, XDF_DIFF_ALGORITHM_MASK),
+               OPT_CALLBACK_F(0, "histogram", options, NULL,
+                              N_("generate diff using the \"histogram diff\" algorithm"),
+                              PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+                              diff_opt_diff_algorithm_no_arg),
                OPT_CALLBACK_F(0, "diff-algorithm", options, N_("<algorithm>"),
                               N_("choose a diff algorithm"),
                               PARSE_OPT_NONEG, diff_opt_diff_algorithm),
@@ -5689,22 +5724,25 @@ static void prep_parse_options(struct diff_options *options)
                OPT_END()
        };
 
-       ALLOC_ARRAY(options->parseopts, ARRAY_SIZE(parseopts));
-       memcpy(options->parseopts, parseopts, sizeof(parseopts));
+       return parse_options_concat(opts, parseopts);
 }
 
 int diff_opt_parse(struct diff_options *options,
                   const char **av, int ac, const char *prefix)
 {
+       struct option no_options[] = { OPT_END() };
+       struct option *parseopts = add_diff_options(no_options, options);
+
        if (!prefix)
                prefix = "";
 
-       ac = parse_options(ac, av, prefix, options->parseopts, NULL,
+       ac = parse_options(ac, av, prefix, parseopts, NULL,
                           PARSE_OPT_KEEP_DASHDASH |
                           PARSE_OPT_KEEP_UNKNOWN_OPT |
                           PARSE_OPT_NO_INTERNAL_HELP |
                           PARSE_OPT_ONE_SHOT |
                           PARSE_OPT_STOP_AT_NON_OPTION);
+       free(parseopts);
 
        return ac;
 }
@@ -6513,7 +6551,6 @@ void diff_free(struct diff_options *options)
        diff_free_file(options);
        diff_free_ignore_regex(options);
        clear_pathspec(&options->pathspec);
-       FREE_AND_NULL(options->parseopts);
 }
 
 void diff_flush(struct diff_options *options)
@@ -7037,7 +7074,7 @@ static char *run_textconv(struct repository *r,
        struct strbuf buf = STRBUF_INIT;
        int err = 0;
 
-       temp = prepare_temp_file(r, spec->path, spec);
+       temp = prepare_temp_file(r, spec);
        strvec_push(&child.args, pgm);
        strvec_push(&child.args, temp->name);
 
diff --git a/diff.h b/diff.h
index fd33caeb25dc84b53dbac18d59898bcd3cc36a7d..8d770b1d579c8ec61ce52c4ac4c9df799c509901 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -333,6 +333,7 @@ struct diff_options {
        int prefix_length;
        const char *stat_sep;
        int xdl_opts;
+       int ignore_driver_algorithm;
 
        /* see Documentation/diff-options.txt */
        char **anchors;
@@ -394,7 +395,6 @@ struct diff_options {
        unsigned color_moved_ws_handling;
 
        struct repository *repo;
-       struct option *parseopts;
        struct strmap *additional_path_headers;
 
        int no_free;
@@ -539,6 +539,7 @@ int git_diff_ui_config(const char *var, const char *value, void *cb);
 #define diff_setup(diffopts) repo_diff_setup(the_repository, diffopts)
 #endif
 void repo_diff_setup(struct repository *, struct diff_options *);
+struct option *add_diff_options(const struct option *, struct diff_options *);
 int diff_opt_parse(struct diff_options *, const char **, int, const char *);
 void diff_setup_done(struct diff_options *);
 int git_config_rename(const char *var, const char *value);
index 3764dd81a185b296a5d4c0a9fc8c0ae2bd4085ec..cedd30475992d41fd8371d51572117c71e9e9b6c 100644 (file)
@@ -112,10 +112,7 @@ static int prepare_next_entry_data(struct dir_iterator_int *iter,
        iter->base.basename = iter->base.path.buf +
                              iter->levels[iter->levels_nr - 1].prefix_len;
 
-       if (iter->flags & DIR_ITERATOR_FOLLOW_SYMLINKS)
-               err = stat(iter->base.path.buf, &iter->base.st);
-       else
-               err = lstat(iter->base.path.buf, &iter->base.st);
+       err = lstat(iter->base.path.buf, &iter->base.st);
 
        saved_errno = errno;
        if (err && errno != ENOENT)
@@ -213,13 +210,10 @@ struct dir_iterator *dir_iterator_begin(const char *path, unsigned int flags)
        iter->flags = flags;
 
        /*
-        * Note: stat/lstat already checks for NULL or empty strings and
+        * Note: lstat already checks for NULL or empty strings and
         * nonexistent paths.
         */
-       if (iter->flags & DIR_ITERATOR_FOLLOW_SYMLINKS)
-               err = stat(iter->base.path.buf, &iter->base.st);
-       else
-               err = lstat(iter->base.path.buf, &iter->base.st);
+       err = lstat(iter->base.path.buf, &iter->base.st);
 
        if (err < 0) {
                saved_errno = errno;
index e3b6ff28007366568d981631d5b312bd849486c0..479e1ec784dfa4081430ea5a104302e79d10eae3 100644 (file)
  *   and ITER_ERROR is returned immediately. In both cases, a meaningful
  *   warning is emitted. Note: ENOENT errors are always ignored so that
  *   the API users may remove files during iteration.
- *
- * - DIR_ITERATOR_FOLLOW_SYMLINKS: make dir-iterator follow symlinks.
- *   i.e., linked directories' contents will be iterated over and
- *   iter->base.st will contain information on the referred files,
- *   not the symlinks themselves, which is the default behavior. Broken
- *   symlinks are ignored.
- *
- *   Note: setting DIR_ITERATOR_FOLLOW_SYMLINKS affects resolving the
- *   starting path as well (e.g., attempting to iterate starting at a
- *   symbolic link pointing to a directory without FOLLOW_SYMLINKS will
- *   result in an error).
- *
- * Warning: circular symlinks are also followed when
- * DIR_ITERATOR_FOLLOW_SYMLINKS is set. The iteration may end up with
- * an ELOOP if they happen and DIR_ITERATOR_PEDANTIC is set.
  */
 #define DIR_ITERATOR_PEDANTIC (1 << 0)
-#define DIR_ITERATOR_FOLLOW_SYMLINKS (1 << 1)
 
 struct dir_iterator {
        /* The current path: */
@@ -88,9 +72,7 @@ struct dir_iterator {
        const char *basename;
 
        /*
-        * The result of calling lstat() on path; or stat(), if the
-        * DIR_ITERATOR_FOLLOW_SYMLINKS flag was set at
-        * dir_iterator's initialization.
+        * The result of calling lstat() on path.
         */
        struct stat st;
 };
diff --git a/dir.c b/dir.c
index d604d1bab982980fd5506a0fae072bac3510a5ae..4e99f0c868f3d7955c4a6f84bf22bbce0d917ae3 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -732,6 +732,13 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
                goto clear_hashmaps;
        }
 
+       if (!(given->flags & PATTERN_FLAG_MUSTBEDIR) &&
+           strcmp(given->pattern, "/*")) {
+               /* Not a cone pattern. */
+               warning(_("unrecognized pattern: '%s'"), given->pattern);
+               goto clear_hashmaps;
+       }
+
        prev = given->pattern;
        cur = given->pattern + 1;
        next = given->pattern + 2;
@@ -3581,8 +3588,12 @@ static void free_untracked(struct untracked_cache_dir *ucd)
 
 void free_untracked_cache(struct untracked_cache *uc)
 {
-       if (uc)
-               free_untracked(uc->root);
+       if (!uc)
+               return;
+
+       free(uc->exclude_per_dir_to_free);
+       strbuf_release(&uc->ident);
+       free_untracked(uc->root);
        free(uc);
 }
 
@@ -3739,7 +3750,7 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long
                      next + offset + hashsz);
        uc->dir_flags = get_be32(next + ouc_offset(dir_flags));
        exclude_per_dir = (const char *)next + exclude_per_dir_offset;
-       uc->exclude_per_dir = xstrdup(exclude_per_dir);
+       uc->exclude_per_dir = uc->exclude_per_dir_to_free = xstrdup(exclude_per_dir);
        /* NUL after exclude_per_dir is covered by sizeof(*ouc) */
        next += exclude_per_dir_offset + strlen(exclude_per_dir) + 1;
        if (next >= end)
diff --git a/dir.h b/dir.h
index 674747d93af7ad903b1e78a1418ff07c914a69ec..8acfc044181ca2f47aa39bef25713b1a811e987c 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -188,6 +188,7 @@ struct untracked_cache {
        struct oid_stat ss_info_exclude;
        struct oid_stat ss_excludes_file;
        const char *exclude_per_dir;
+       char *exclude_per_dir_to_free;
        struct strbuf ident;
        /*
         * dir_struct#flags must match dir_flags or the untracked
diff --git a/entry.c b/entry.c
index 616e4f073c1d6bd4016252423d4ced0958612432..971ab268714b96cdaa179ae7589c8377ff728e74 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -383,7 +383,7 @@ static int write_entry(struct cache_entry *ce, char *path, struct conv_attrs *ca
                        return error("cannot create submodule directory %s", path);
                sub = submodule_from_ce(ce);
                if (sub)
-                       return submodule_move_head(ce->name,
+                       return submodule_move_head(ce->name, state->super_prefix,
                                NULL, oid_to_hex(&ce->oid),
                                state->force ? SUBMODULE_MOVE_HEAD_FORCE : 0);
                break;
@@ -476,7 +476,7 @@ int checkout_entry_ca(struct cache_entry *ce, struct conv_attrs *ca,
                         * no pathname to return.
                         */
                        BUG("Can't remove entry to a path");
-               unlink_entry(ce);
+               unlink_entry(ce, state->super_prefix);
                return 0;
        }
 
@@ -510,10 +510,10 @@ int checkout_entry_ca(struct cache_entry *ce, struct conv_attrs *ca,
                                if (!(st.st_mode & S_IFDIR))
                                        unlink_or_warn(ce->name);
 
-                               return submodule_move_head(ce->name,
+                               return submodule_move_head(ce->name, state->super_prefix,
                                        NULL, oid_to_hex(&ce->oid), 0);
                        } else
-                               return submodule_move_head(ce->name,
+                               return submodule_move_head(ce->name, state->super_prefix,
                                        "HEAD", oid_to_hex(&ce->oid),
                                        state->force ? SUBMODULE_MOVE_HEAD_FORCE : 0);
                }
@@ -560,12 +560,12 @@ int checkout_entry_ca(struct cache_entry *ce, struct conv_attrs *ca,
        return write_entry(ce, path.buf, ca, state, 0, nr_checkouts);
 }
 
-void unlink_entry(const struct cache_entry *ce)
+void unlink_entry(const struct cache_entry *ce, const char *super_prefix)
 {
        const struct submodule *sub = submodule_from_ce(ce);
        if (sub) {
                /* state.force is set at the caller. */
-               submodule_move_head(ce->name, "HEAD", NULL,
+               submodule_move_head(ce->name, super_prefix, "HEAD", NULL,
                                    SUBMODULE_MOVE_HEAD_FORCE);
        }
        if (check_leading_path(ce->name, ce_namelen(ce), 1) >= 0)
diff --git a/entry.h b/entry.h
index 9be4659881e6ec979bb4ac5233c71af44b337586..2d4fbb88c8ffabbd36faec0247d5a39a30f99c4f 100644 (file)
--- a/entry.h
+++ b/entry.h
@@ -8,6 +8,7 @@ struct checkout {
        struct index_state *istate;
        const char *base_dir;
        int base_dir_len;
+       const char *super_prefix;
        struct delayed_checkout *delayed_checkout;
        struct checkout_metadata meta;
        unsigned force:1,
@@ -48,8 +49,11 @@ int finish_delayed_checkout(struct checkout *state, int show_progress);
 /*
  * Unlink the last component and schedule the leading directories for
  * removal, such that empty directories get removed.
+ *
+ * The "super_prefix" is either NULL, or the "--super-prefix" passed
+ * down from "read-tree" et al.
  */
-void unlink_entry(const struct cache_entry *ce);
+void unlink_entry(const struct cache_entry *ce, const char *super_prefix);
 
 void *read_blob_entry(const struct cache_entry *ce, size_t *size);
 int fstat_checkout_output(int fd, const struct checkout *state, struct stat *st);
index 18d042b467d26a9cb3b228db1ef5afa2d903e72c..1ee3686fd8a34270db8d47738f2ea12fcdbc1976 100644 (file)
@@ -102,8 +102,6 @@ char *git_work_tree_cfg;
 
 static char *git_namespace;
 
-static char *super_prefix;
-
 /*
  * Repository-local GIT_* environment variables; see cache.h for details.
  */
@@ -121,7 +119,6 @@ const char * const local_repo_env[] = {
        NO_REPLACE_OBJECTS_ENVIRONMENT,
        GIT_REPLACE_REF_BASE_ENVIRONMENT,
        GIT_PREFIX_ENVIRONMENT,
-       GIT_SUPER_PREFIX_ENVIRONMENT,
        GIT_SHALLOW_FILE_ENVIRONMENT,
        GIT_COMMON_DIR_ENVIRONMENT,
        NULL
@@ -234,16 +231,6 @@ const char *strip_namespace(const char *namespaced_ref)
        return NULL;
 }
 
-const char *get_super_prefix(void)
-{
-       static int initialized;
-       if (!initialized) {
-               super_prefix = xstrdup_or_null(getenv(GIT_SUPER_PREFIX_ENVIRONMENT));
-               initialized = 1;
-       }
-       return super_prefix;
-}
-
 static int git_work_tree_initialized;
 
 /*
index 998fc2fa1ed4abd32ad0e723115f568768134d3e..04016d1e3250f28ae6cd53a784913f02879769d8 100644 (file)
@@ -1317,15 +1317,15 @@ static void write_fetch_command_and_capabilities(struct strbuf *req_buf,
 {
        const char *hash_name;
 
-       if (server_supports_v2("fetch", 1))
-               packet_buf_write(req_buf, "command=fetch");
-       if (server_supports_v2("agent", 0))
+       ensure_server_supports_v2("fetch");
+       packet_buf_write(req_buf, "command=fetch");
+       if (server_supports_v2("agent"))
                packet_buf_write(req_buf, "agent=%s", git_user_agent_sanitized());
-       if (advertise_sid && server_supports_v2("session-id", 0))
+       if (advertise_sid && server_supports_v2("session-id"))
                packet_buf_write(req_buf, "session-id=%s", trace2_session_id());
-       if (server_options && server_options->nr &&
-           server_supports_v2("server-option", 1)) {
+       if (server_options && server_options->nr) {
                int i;
+               ensure_server_supports_v2("server-option");
                for (i = 0; i < server_options->nr; i++)
                        packet_buf_write(req_buf, "server-option=%s",
                                         server_options->items[i].string);
diff --git a/fsck.c b/fsck.c
index 47eaeedd7076ba60a621e072abb405127b3c33fe..2b18717ee805bc73aa50943731ac9a7212303d18 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -748,6 +748,23 @@ static int fsck_tree(const struct object_id *tree_oid,
        return retval;
 }
 
+/*
+ * Confirm that the headers of a commit or tag object end in a reasonable way,
+ * either with the usual "\n\n" separator, or at least with a trailing newline
+ * on the final header line.
+ *
+ * This property is important for the memory safety of our callers. It allows
+ * them to scan the buffer linewise without constantly checking the remaining
+ * size as long as:
+ *
+ *   - they check that there are bytes left in the buffer at the start of any
+ *     line (i.e., that the last newline they saw was not the final one we
+ *     found here)
+ *
+ *   - any intra-line scanning they do will stop at a newline, which will worst
+ *     case hit the newline we found here as the end-of-header. This makes it
+ *     OK for them to use helpers like parse_oid_hex(), or even skip_prefix().
+ */
 static int verify_headers(const void *data, unsigned long size,
                          const struct object_id *oid, enum object_type type,
                          struct fsck_options *options)
@@ -808,6 +825,20 @@ static int fsck_ident(const char **ident,
        if (*p != ' ')
                return report(options, oid, type, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, "invalid author/committer line - missing space before date");
        p++;
+       /*
+        * Our timestamp parser is based on the C strto*() functions, which
+        * will happily eat whitespace, including the newline that is supposed
+        * to prevent us walking past the end of the buffer. So do our own
+        * scan, skipping linear whitespace but not newlines, and then
+        * confirming we found a digit. We _could_ be even more strict here,
+        * as we really expect only a single space, but since we have
+        * traditionally allowed extra whitespace, we'll continue to do so.
+        */
+       while (*p == ' ' || *p == '\t')
+               p++;
+       if (!isdigit(*p))
+               return report(options, oid, type, FSCK_MSG_BAD_DATE,
+                             "invalid author/committer line - bad date");
        if (*p == '0' && p[1] != ' ')
                return report(options, oid, type, FSCK_MSG_ZERO_PADDED_DATE, "invalid author/committer line - zero-padded date");
        if (date_overflows(parse_timestamp(p, &end, 10)))
@@ -834,12 +865,18 @@ static int fsck_commit(const struct object_id *oid,
        unsigned author_count;
        int err;
        const char *buffer_begin = buffer;
+       const char *buffer_end = buffer + size;
        const char *p;
 
+       /*
+        * We _must_ stop parsing immediately if this reports failure, as the
+        * memory safety of the rest of the function depends on it. See the
+        * comment above the definition of verify_headers() for more details.
+        */
        if (verify_headers(buffer, size, oid, OBJ_COMMIT, options))
                return -1;
 
-       if (!skip_prefix(buffer, "tree ", &buffer))
+       if (buffer >= buffer_end || !skip_prefix(buffer, "tree ", &buffer))
                return report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_TREE, "invalid format - expected 'tree' line");
        if (parse_oid_hex(buffer, &tree_oid, &p) || *p != '\n') {
                err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1");
@@ -847,7 +884,7 @@ static int fsck_commit(const struct object_id *oid,
                        return err;
        }
        buffer = p + 1;
-       while (skip_prefix(buffer, "parent ", &buffer)) {
+       while (buffer < buffer_end && skip_prefix(buffer, "parent ", &buffer)) {
                if (parse_oid_hex(buffer, &parent_oid, &p) || *p != '\n') {
                        err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1");
                        if (err)
@@ -856,7 +893,7 @@ static int fsck_commit(const struct object_id *oid,
                buffer = p + 1;
        }
        author_count = 0;
-       while (skip_prefix(buffer, "author ", &buffer)) {
+       while (buffer < buffer_end && skip_prefix(buffer, "author ", &buffer)) {
                author_count++;
                err = fsck_ident(&buffer, oid, OBJ_COMMIT, options);
                if (err)
@@ -868,7 +905,7 @@ static int fsck_commit(const struct object_id *oid,
                err = report(options, oid, OBJ_COMMIT, FSCK_MSG_MULTIPLE_AUTHORS, "invalid format - multiple 'author' lines");
        if (err)
                return err;
-       if (!skip_prefix(buffer, "committer ", &buffer))
+       if (buffer >= buffer_end || !skip_prefix(buffer, "committer ", &buffer))
                return report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_COMMITTER, "invalid format - expected 'committer' line");
        err = fsck_ident(&buffer, oid, OBJ_COMMIT, options);
        if (err)
@@ -899,13 +936,19 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer,
        int ret = 0;
        char *eol;
        struct strbuf sb = STRBUF_INIT;
+       const char *buffer_end = buffer + size;
        const char *p;
 
+       /*
+        * We _must_ stop parsing immediately if this reports failure, as the
+        * memory safety of the rest of the function depends on it. See the
+        * comment above the definition of verify_headers() for more details.
+        */
        ret = verify_headers(buffer, size, oid, OBJ_TAG, options);
        if (ret)
                goto done;
 
-       if (!skip_prefix(buffer, "object ", &buffer)) {
+       if (buffer >= buffer_end || !skip_prefix(buffer, "object ", &buffer)) {
                ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_OBJECT, "invalid format - expected 'object' line");
                goto done;
        }
@@ -916,11 +959,11 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer,
        }
        buffer = p + 1;
 
-       if (!skip_prefix(buffer, "type ", &buffer)) {
+       if (buffer >= buffer_end || !skip_prefix(buffer, "type ", &buffer)) {
                ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TYPE_ENTRY, "invalid format - expected 'type' line");
                goto done;
        }
-       eol = strchr(buffer, '\n');
+       eol = memchr(buffer, '\n', buffer_end - buffer);
        if (!eol) {
                ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TYPE, "invalid format - unexpected end after 'type' line");
                goto done;
@@ -932,11 +975,11 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer,
                goto done;
        buffer = eol + 1;
 
-       if (!skip_prefix(buffer, "tag ", &buffer)) {
+       if (buffer >= buffer_end || !skip_prefix(buffer, "tag ", &buffer)) {
                ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TAG_ENTRY, "invalid format - expected 'tag' line");
                goto done;
        }
-       eol = strchr(buffer, '\n');
+       eol = memchr(buffer, '\n', buffer_end - buffer);
        if (!eol) {
                ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TAG, "invalid format - unexpected end after 'type' line");
                goto done;
@@ -952,7 +995,7 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer,
        }
        buffer = eol + 1;
 
-       if (!skip_prefix(buffer, "tagger ", &buffer)) {
+       if (buffer >= buffer_end || !skip_prefix(buffer, "tagger ", &buffer)) {
                /* early tags do not contain 'tagger' lines; warn only */
                ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TAGGER_ENTRY, "invalid format - expected 'tagger' line");
                if (ret)
@@ -960,10 +1003,8 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer,
        }
        else
                ret = fsck_ident(&buffer, oid, OBJ_TAG, options);
-       if (!*buffer)
-               goto done;
 
-       if (!starts_with(buffer, "\n")) {
+       if (buffer < buffer_end && !starts_with(buffer, "\n")) {
                /*
                 * The verify_headers() check will allow
                 * e.g. "[...]tagger <tagger>\nsome
@@ -1237,19 +1278,26 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
        if (!obj)
                return report(options, NULL, OBJ_NONE, FSCK_MSG_BAD_OBJECT_SHA1, "no valid object to fsck");
 
-       if (obj->type == OBJ_BLOB)
-               return fsck_blob(&obj->oid, data, size, options);
-       if (obj->type == OBJ_TREE)
-               return fsck_tree(&obj->oid, data, size, options);
-       if (obj->type == OBJ_COMMIT)
-               return fsck_commit(&obj->oid, data, size, options);
-       if (obj->type == OBJ_TAG)
-               return fsck_tag(&obj->oid, data, size, options);
+       return fsck_buffer(&obj->oid, obj->type, data, size, options);
+}
+
+int fsck_buffer(const struct object_id *oid, enum object_type type,
+               void *data, unsigned long size,
+               struct fsck_options *options)
+{
+       if (type == OBJ_BLOB)
+               return fsck_blob(oid, data, size, options);
+       if (type == OBJ_TREE)
+               return fsck_tree(oid, data, size, options);
+       if (type == OBJ_COMMIT)
+               return fsck_commit(oid, data, size, options);
+       if (type == OBJ_TAG)
+               return fsck_tag(oid, data, size, options);
 
-       return report(options, &obj->oid, obj->type,
+       return report(options, oid, type,
                      FSCK_MSG_UNKNOWN_TYPE,
                      "unknown type '%d' (internal fsck error)",
-                     obj->type);
+                     type);
 }
 
 int fsck_error_function(struct fsck_options *o,
diff --git a/fsck.h b/fsck.h
index fcecf4101cfc9cbb60f9467d629403fe4221f782..668330880ef65f72ba4e9d38487152cc28c205a1 100644 (file)
--- a/fsck.h
+++ b/fsck.h
@@ -183,6 +183,14 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options);
 int fsck_object(struct object *obj, void *data, unsigned long size,
        struct fsck_options *options);
 
+/*
+ * Same as fsck_object(), but for when the caller doesn't have an object
+ * struct.
+ */
+int fsck_buffer(const struct object_id *oid, enum object_type,
+               void *data, unsigned long size,
+               struct fsck_options *options);
+
 /*
  * fsck a tag, and pass info about it back to the caller. This is
  * exposed fsck_object() internals for git-mktag(1).
index ee63a97dc51081c5b600cf14c2c2279334c1faa9..899bfe9c8138e1ae46f1fd0f0dfc7aebba05b81a 100644 (file)
@@ -143,8 +143,6 @@ static void lookup_fsmonitor_settings(struct repository *r)
 
 enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)
 {
-       if (!r)
-               r = the_repository;
        if (!r->settings.fsmonitor)
                lookup_fsmonitor_settings(r);
 
@@ -153,8 +151,6 @@ enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)
 
 const char *fsm_settings__get_hook_path(struct repository *r)
 {
-       if (!r)
-               r = the_repository;
        if (!r->settings.fsmonitor)
                lookup_fsmonitor_settings(r);
 
@@ -174,8 +170,6 @@ void fsm_settings__set_ipc(struct repository *r)
         * Caller requested IPC explicitly, so avoid (possibly
         * recursive) config lookup.
         */
-       if (!r)
-               r = the_repository;
        if (!r->settings.fsmonitor)
                r->settings.fsmonitor = alloc_settings();
 
@@ -197,8 +191,6 @@ void fsm_settings__set_hook(struct repository *r, const char *path)
         * Caller requested hook explicitly, so avoid (possibly
         * recursive) config lookup.
         */
-       if (!r)
-               r = the_repository;
        if (!r->settings.fsmonitor)
                r->settings.fsmonitor = alloc_settings();
 
@@ -210,8 +202,6 @@ void fsm_settings__set_hook(struct repository *r, const char *path)
 
 void fsm_settings__set_disabled(struct repository *r)
 {
-       if (!r)
-               r = the_repository;
        if (!r->settings.fsmonitor)
                r->settings.fsmonitor = alloc_settings();
 
@@ -223,8 +213,6 @@ void fsm_settings__set_disabled(struct repository *r)
 void fsm_settings__set_incompatible(struct repository *r,
                                    enum fsmonitor_reason reason)
 {
-       if (!r)
-               r = the_repository;
        if (!r->settings.fsmonitor)
                r->settings.fsmonitor = alloc_settings();
 
@@ -235,8 +223,6 @@ void fsm_settings__set_incompatible(struct repository *r,
 
 enum fsmonitor_reason fsm_settings__get_reason(struct repository *r)
 {
-       if (!r)
-               r = the_repository;
        if (!r->settings.fsmonitor)
                lookup_fsmonitor_settings(r);
 
index 08af00c73872d2c3516d6bb419c099e058fd6345..a5b9e75437b5ec7d485eaba4795f21e53fb77122 100644 (file)
@@ -304,7 +304,7 @@ void refresh_fsmonitor(struct index_state *istate)
        char *buf;
        unsigned int i;
        int is_trivial = 0;
-       struct repository *r = istate->repo ? istate->repo : the_repository;
+       struct repository *r = istate->repo;
        enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);
        enum fsmonitor_reason reason = fsm_settings__get_reason(r);
 
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
deleted file mode 100755 (executable)
index 95887fd..0000000
+++ /dev/null
@@ -1,1920 +0,0 @@
-#!/usr/bin/perl
-
-use 5.008;
-use strict;
-use warnings;
-use Git qw(unquote_path);
-use Git::I18N;
-
-binmode(STDOUT, ":raw");
-
-my $repo = Git->repository();
-
-my $menu_use_color = $repo->get_colorbool('color.interactive');
-my ($prompt_color, $header_color, $help_color) =
-       $menu_use_color ? (
-               $repo->get_color('color.interactive.prompt', 'bold blue'),
-               $repo->get_color('color.interactive.header', 'bold'),
-               $repo->get_color('color.interactive.help', 'red bold'),
-       ) : ();
-my $error_color = ();
-if ($menu_use_color) {
-       my $help_color_spec = ($repo->config('color.interactive.help') or
-                               'red bold');
-       $error_color = $repo->get_color('color.interactive.error',
-                                       $help_color_spec);
-}
-
-my $diff_use_color = $repo->get_colorbool('color.diff');
-my ($fraginfo_color) =
-       $diff_use_color ? (
-               $repo->get_color('color.diff.frag', 'cyan'),
-       ) : ();
-my ($diff_context_color) =
-       $diff_use_color ? (
-               $repo->get_color($repo->config('color.diff.context') ? 'color.diff.context' : 'color.diff.plain', ''),
-       ) : ();
-my ($diff_old_color) =
-       $diff_use_color ? (
-               $repo->get_color('color.diff.old', 'red'),
-       ) : ();
-my ($diff_new_color) =
-       $diff_use_color ? (
-               $repo->get_color('color.diff.new', 'green'),
-       ) : ();
-
-my $normal_color = $repo->get_color("", "reset");
-
-my $diff_algorithm = $repo->config('diff.algorithm');
-my $diff_filter = $repo->config('interactive.difffilter');
-
-my $use_readkey = 0;
-my $use_termcap = 0;
-my %term_escapes;
-
-sub ReadMode;
-sub ReadKey;
-if ($repo->config_bool("interactive.singlekey")) {
-       eval {
-               require Term::ReadKey;
-               Term::ReadKey->import;
-               $use_readkey = 1;
-       };
-       if (!$use_readkey) {
-               print STDERR "missing Term::ReadKey, disabling interactive.singlekey\n";
-       }
-       eval {
-               require Term::Cap;
-               my $termcap = Term::Cap->Tgetent;
-               foreach (values %$termcap) {
-                       $term_escapes{$_} = 1 if /^\e/;
-               }
-               $use_termcap = 1;
-       };
-}
-
-sub colored {
-       my $color = shift;
-       my $string = join("", @_);
-
-       if (defined $color) {
-               # Put a color code at the beginning of each line, a reset at the end
-               # color after newlines that are not at the end of the string
-               $string =~ s/(\n+)(.)/$1$color$2/g;
-               # reset before newlines
-               $string =~ s/(\n+)/$normal_color$1/g;
-               # codes at beginning and end (if necessary):
-               $string =~ s/^/$color/;
-               $string =~ s/$/$normal_color/ unless $string =~ /\n$/;
-       }
-       return $string;
-}
-
-# command line options
-my $patch_mode_only;
-my $patch_mode;
-my $patch_mode_revision;
-
-sub apply_patch;
-sub apply_patch_for_checkout_commit;
-sub apply_patch_for_stash;
-
-my %patch_modes = (
-       'stage' => {
-               DIFF => 'diff-files -p',
-               APPLY => sub { apply_patch 'apply --cached', @_; },
-               APPLY_CHECK => 'apply --cached',
-               FILTER => 'file-only',
-               IS_REVERSE => 0,
-       },
-       'stash' => {
-               DIFF => 'diff-index -p HEAD',
-               APPLY => sub { apply_patch 'apply --cached', @_; },
-               APPLY_CHECK => 'apply --cached',
-               FILTER => undef,
-               IS_REVERSE => 0,
-       },
-       'reset_head' => {
-               DIFF => 'diff-index -p --cached',
-               APPLY => sub { apply_patch 'apply -R --cached', @_; },
-               APPLY_CHECK => 'apply -R --cached',
-               FILTER => 'index-only',
-               IS_REVERSE => 1,
-       },
-       'reset_nothead' => {
-               DIFF => 'diff-index -R -p --cached',
-               APPLY => sub { apply_patch 'apply --cached', @_; },
-               APPLY_CHECK => 'apply --cached',
-               FILTER => 'index-only',
-               IS_REVERSE => 0,
-       },
-       'checkout_index' => {
-               DIFF => 'diff-files -p',
-               APPLY => sub { apply_patch 'apply -R', @_; },
-               APPLY_CHECK => 'apply -R',
-               FILTER => 'file-only',
-               IS_REVERSE => 1,
-       },
-       'checkout_head' => {
-               DIFF => 'diff-index -p',
-               APPLY => sub { apply_patch_for_checkout_commit '-R', @_ },
-               APPLY_CHECK => 'apply -R',
-               FILTER => undef,
-               IS_REVERSE => 1,
-       },
-       'checkout_nothead' => {
-               DIFF => 'diff-index -R -p',
-               APPLY => sub { apply_patch_for_checkout_commit '', @_ },
-               APPLY_CHECK => 'apply',
-               FILTER => undef,
-               IS_REVERSE => 0,
-       },
-       'worktree_head' => {
-               DIFF => 'diff-index -p',
-               APPLY => sub { apply_patch 'apply -R', @_ },
-               APPLY_CHECK => 'apply -R',
-               FILTER => undef,
-               IS_REVERSE => 1,
-       },
-       'worktree_nothead' => {
-               DIFF => 'diff-index -R -p',
-               APPLY => sub { apply_patch 'apply', @_ },
-               APPLY_CHECK => 'apply',
-               FILTER => undef,
-               IS_REVERSE => 0,
-       },
-);
-
-$patch_mode = 'stage';
-my %patch_mode_flavour = %{$patch_modes{$patch_mode}};
-
-sub run_cmd_pipe {
-       if ($^O eq 'MSWin32') {
-               my @invalid = grep {m/[":*]/} @_;
-               die "$^O does not support: @invalid\n" if @invalid;
-               my @args = map { m/ /o ? "\"$_\"": $_ } @_;
-               return qx{@args};
-       } else {
-               my $fh = undef;
-               open($fh, '-|', @_) or die;
-               my @out = <$fh>;
-               close $fh || die "Cannot close @_ ($!)";
-               return @out;
-       }
-}
-
-my ($GIT_DIR) = run_cmd_pipe(qw(git rev-parse --git-dir));
-
-if (!defined $GIT_DIR) {
-       exit(1); # rev-parse would have already said "not a git repo"
-}
-chomp($GIT_DIR);
-
-sub refresh {
-       my $fh;
-       open $fh, 'git update-index --refresh |'
-           or die;
-       while (<$fh>) {
-               ;# ignore 'needs update'
-       }
-       close $fh;
-}
-
-sub list_untracked {
-       map {
-               chomp $_;
-               unquote_path($_);
-       }
-       run_cmd_pipe(qw(git ls-files --others --exclude-standard --), @ARGV);
-}
-
-# TRANSLATORS: you can adjust this to align "git add -i" status menu
-my $status_fmt = __('%12s %12s %s');
-my $status_head = sprintf($status_fmt, __('staged'), __('unstaged'), __('path'));
-
-{
-       my $initial;
-       sub is_initial_commit {
-               $initial = system('git rev-parse HEAD -- >/dev/null 2>&1') != 0
-                       unless defined $initial;
-               return $initial;
-       }
-}
-
-{
-       my $empty_tree;
-       sub get_empty_tree {
-               return $empty_tree if defined $empty_tree;
-
-               ($empty_tree) = run_cmd_pipe(qw(git hash-object -t tree /dev/null));
-               chomp $empty_tree;
-               return $empty_tree;
-       }
-}
-
-sub get_diff_reference {
-       my $ref = shift;
-       if (defined $ref and $ref ne 'HEAD') {
-               return $ref;
-       } elsif (is_initial_commit()) {
-               return get_empty_tree();
-       } else {
-               return 'HEAD';
-       }
-}
-
-# Returns list of hashes, contents of each of which are:
-# VALUE:       pathname
-# BINARY:      is a binary path
-# INDEX:       is index different from HEAD?
-# FILE:                is file different from index?
-# INDEX_ADDDEL:        is it add/delete between HEAD and index?
-# FILE_ADDDEL: is it add/delete between index and file?
-# UNMERGED:    is the path unmerged
-
-sub list_modified {
-       my ($only) = @_;
-       my (%data, @return);
-       my ($add, $del, $adddel, $file);
-
-       my $reference = get_diff_reference($patch_mode_revision);
-       for (run_cmd_pipe(qw(git diff-index --cached
-                            --numstat --summary), $reference,
-                            '--', @ARGV)) {
-               if (($add, $del, $file) =
-                   /^([-\d]+)  ([-\d]+)        (.*)/) {
-                       my ($change, $bin);
-                       $file = unquote_path($file);
-                       if ($add eq '-' && $del eq '-') {
-                               $change = __('binary');
-                               $bin = 1;
-                       }
-                       else {
-                               $change = "+$add/-$del";
-                       }
-                       $data{$file} = {
-                               INDEX => $change,
-                               BINARY => $bin,
-                               FILE => __('nothing'),
-                       }
-               }
-               elsif (($adddel, $file) =
-                      /^ (create|delete) mode [0-7]+ (.*)$/) {
-                       $file = unquote_path($file);
-                       $data{$file}{INDEX_ADDDEL} = $adddel;
-               }
-       }
-
-       for (run_cmd_pipe(qw(git diff-files --ignore-submodules=dirty --numstat --summary --raw --), @ARGV)) {
-               if (($add, $del, $file) =
-                   /^([-\d]+)  ([-\d]+)        (.*)/) {
-                       $file = unquote_path($file);
-                       my ($change, $bin);
-                       if ($add eq '-' && $del eq '-') {
-                               $change = __('binary');
-                               $bin = 1;
-                       }
-                       else {
-                               $change = "+$add/-$del";
-                       }
-                       $data{$file}{FILE} = $change;
-                       if ($bin) {
-                               $data{$file}{BINARY} = 1;
-                       }
-               }
-               elsif (($adddel, $file) =
-                      /^ (create|delete) mode [0-7]+ (.*)$/) {
-                       $file = unquote_path($file);
-                       $data{$file}{FILE_ADDDEL} = $adddel;
-               }
-               elsif (/^:[0-7]+ [0-7]+ [0-9a-f]+ [0-9a-f]+ (.) (.*)$/) {
-                       $file = unquote_path($2);
-                       if (!exists $data{$file}) {
-                               $data{$file} = +{
-                                       INDEX => __('unchanged'),
-                                       BINARY => 0,
-                               };
-                       }
-                       if ($1 eq 'U') {
-                               $data{$file}{UNMERGED} = 1;
-                       }
-               }
-       }
-
-       for (sort keys %data) {
-               my $it = $data{$_};
-
-               if ($only) {
-                       if ($only eq 'index-only') {
-                               next if ($it->{INDEX} eq __('unchanged'));
-                       }
-                       if ($only eq 'file-only') {
-                               next if ($it->{FILE} eq __('nothing'));
-                       }
-               }
-               push @return, +{
-                       VALUE => $_,
-                       %$it,
-               };
-       }
-       return @return;
-}
-
-sub find_unique {
-       my ($string, @stuff) = @_;
-       my $found = undef;
-       for (my $i = 0; $i < @stuff; $i++) {
-               my $it = $stuff[$i];
-               my $hit = undef;
-               if (ref $it) {
-                       if ((ref $it) eq 'ARRAY') {
-                               $it = $it->[0];
-                       }
-                       else {
-                               $it = $it->{VALUE};
-                       }
-               }
-               eval {
-                       if ($it =~ /^$string/) {
-                               $hit = 1;
-                       };
-               };
-               if (defined $hit && defined $found) {
-                       return undef;
-               }
-               if ($hit) {
-                       $found = $i + 1;
-               }
-       }
-       return $found;
-}
-
-# inserts string into trie and updates count for each character
-sub update_trie {
-       my ($trie, $string) = @_;
-       foreach (split //, $string) {
-               $trie = $trie->{$_} ||= {COUNT => 0};
-               $trie->{COUNT}++;
-       }
-}
-
-# returns an array of tuples (prefix, remainder)
-sub find_unique_prefixes {
-       my @stuff = @_;
-       my @return = ();
-
-       # any single prefix exceeding the soft limit is omitted
-       # if any prefix exceeds the hard limit all are omitted
-       # 0 indicates no limit
-       my $soft_limit = 0;
-       my $hard_limit = 3;
-
-       # build a trie modelling all possible options
-       my %trie;
-       foreach my $print (@stuff) {
-               if ((ref $print) eq 'ARRAY') {
-                       $print = $print->[0];
-               }
-               elsif ((ref $print) eq 'HASH') {
-                       $print = $print->{VALUE};
-               }
-               update_trie(\%trie, $print);
-               push @return, $print;
-       }
-
-       # use the trie to find the unique prefixes
-       for (my $i = 0; $i < @return; $i++) {
-               my $ret = $return[$i];
-               my @letters = split //, $ret;
-               my %search = %trie;
-               my ($prefix, $remainder);
-               my $j;
-               for ($j = 0; $j < @letters; $j++) {
-                       my $letter = $letters[$j];
-                       if ($search{$letter}{COUNT} == 1) {
-                               $prefix = substr $ret, 0, $j + 1;
-                               $remainder = substr $ret, $j + 1;
-                               last;
-                       }
-                       else {
-                               my $prefix = substr $ret, 0, $j;
-                               return ()
-                                   if ($hard_limit && $j + 1 > $hard_limit);
-                       }
-                       %search = %{$search{$letter}};
-               }
-               if (ord($letters[0]) > 127 ||
-                   ($soft_limit && $j + 1 > $soft_limit)) {
-                       $prefix = undef;
-                       $remainder = $ret;
-               }
-               $return[$i] = [$prefix, $remainder];
-       }
-       return @return;
-}
-
-# filters out prefixes which have special meaning to list_and_choose()
-sub is_valid_prefix {
-       my $prefix = shift;
-       return (defined $prefix) &&
-           !($prefix =~ /[\s,]/) && # separators
-           !($prefix =~ /^-/) &&    # deselection
-           !($prefix =~ /^\d+/) &&  # selection
-           ($prefix ne '*') &&      # "all" wildcard
-           ($prefix ne '?');        # prompt help
-}
-
-# given a prefix/remainder tuple return a string with the prefix highlighted
-# for now use square brackets; later might use ANSI colors (underline, bold)
-sub highlight_prefix {
-       my $prefix = shift;
-       my $remainder = shift;
-
-       if (!defined $prefix) {
-               return $remainder;
-       }
-
-       if (!is_valid_prefix($prefix)) {
-               return "$prefix$remainder";
-       }
-
-       if (!$menu_use_color) {
-               return "[$prefix]$remainder";
-       }
-
-       return "$prompt_color$prefix$normal_color$remainder";
-}
-
-sub error_msg {
-       print STDERR colored $error_color, @_;
-}
-
-sub list_and_choose {
-       my ($opts, @stuff) = @_;
-       my (@chosen, @return);
-       if (!@stuff) {
-           return @return;
-       }
-       my $i;
-       my @prefixes = find_unique_prefixes(@stuff) unless $opts->{LIST_ONLY};
-
-      TOPLOOP:
-       while (1) {
-               my $last_lf = 0;
-
-               if ($opts->{HEADER}) {
-                       my $indent = $opts->{LIST_FLAT} ? "" : "     ";
-                       print colored $header_color, "$indent$opts->{HEADER}\n";
-               }
-               for ($i = 0; $i < @stuff; $i++) {
-                       my $chosen = $chosen[$i] ? '*' : ' ';
-                       my $print = $stuff[$i];
-                       my $ref = ref $print;
-                       my $highlighted = highlight_prefix(@{$prefixes[$i]})
-                           if @prefixes;
-                       if ($ref eq 'ARRAY') {
-                               $print = $highlighted || $print->[0];
-                       }
-                       elsif ($ref eq 'HASH') {
-                               my $value = $highlighted || $print->{VALUE};
-                               $print = sprintf($status_fmt,
-                                   $print->{INDEX},
-                                   $print->{FILE},
-                                   $value);
-                       }
-                       else {
-                               $print = $highlighted || $print;
-                       }
-                       printf("%s%2d: %s", $chosen, $i+1, $print);
-                       if (($opts->{LIST_FLAT}) &&
-                           (($i + 1) % ($opts->{LIST_FLAT}))) {
-                               print "\t";
-                               $last_lf = 0;
-                       }
-                       else {
-                               print "\n";
-                               $last_lf = 1;
-                       }
-               }
-               if (!$last_lf) {
-                       print "\n";
-               }
-
-               return if ($opts->{LIST_ONLY});
-
-               print colored $prompt_color, $opts->{PROMPT};
-               if ($opts->{SINGLETON}) {
-                       print "> ";
-               }
-               else {
-                       print ">> ";
-               }
-               my $line = <STDIN>;
-               if (!$line) {
-                       print "\n";
-                       $opts->{ON_EOF}->() if $opts->{ON_EOF};
-                       last;
-               }
-               chomp $line;
-               last if $line eq '';
-               if ($line eq '?') {
-                       $opts->{SINGLETON} ?
-                           singleton_prompt_help_cmd() :
-                           prompt_help_cmd();
-                       next TOPLOOP;
-               }
-               for my $choice (split(/[\s,]+/, $line)) {
-                       my $choose = 1;
-                       my ($bottom, $top);
-
-                       # Input that begins with '-'; unchoose
-                       if ($choice =~ s/^-//) {
-                               $choose = 0;
-                       }
-                       # A range can be specified like 5-7 or 5-.
-                       if ($choice =~ /^(\d+)-(\d*)$/) {
-                               ($bottom, $top) = ($1, length($2) ? $2 : 1 + @stuff);
-                       }
-                       elsif ($choice =~ /^\d+$/) {
-                               $bottom = $top = $choice;
-                       }
-                       elsif ($choice eq '*') {
-                               $bottom = 1;
-                               $top = 1 + @stuff;
-                       }
-                       else {
-                               $bottom = $top = find_unique($choice, @stuff);
-                               if (!defined $bottom) {
-                                       error_msg sprintf(__("Huh (%s)?\n"), $choice);
-                                       next TOPLOOP;
-                               }
-                       }
-                       if ($opts->{SINGLETON} && $bottom != $top) {
-                               error_msg sprintf(__("Huh (%s)?\n"), $choice);
-                               next TOPLOOP;
-                       }
-                       for ($i = $bottom-1; $i <= $top-1; $i++) {
-                               next if (@stuff <= $i || $i < 0);
-                               $chosen[$i] = $choose;
-                       }
-               }
-               last if ($opts->{IMMEDIATE} || $line eq '*');
-       }
-       for ($i = 0; $i < @stuff; $i++) {
-               if ($chosen[$i]) {
-                       push @return, $stuff[$i];
-               }
-       }
-       return @return;
-}
-
-sub singleton_prompt_help_cmd {
-       print colored $help_color, __ <<'EOF' ;
-Prompt help:
-1          - select a numbered item
-foo        - select item based on unique prefix
-           - (empty) select nothing
-EOF
-}
-
-sub prompt_help_cmd {
-       print colored $help_color, __ <<'EOF' ;
-Prompt help:
-1          - select a single item
-3-5        - select a range of items
-2-3,6-9    - select multiple ranges
-foo        - select item based on unique prefix
--...       - unselect specified items
-*          - choose all items
-           - (empty) finish selecting
-EOF
-}
-
-sub status_cmd {
-       list_and_choose({ LIST_ONLY => 1, HEADER => $status_head },
-                       list_modified());
-       print "\n";
-}
-
-sub say_n_paths {
-       my $did = shift @_;
-       my $cnt = scalar @_;
-       if ($did eq 'added') {
-               printf(__n("added %d path\n", "added %d paths\n",
-                          $cnt), $cnt);
-       } elsif ($did eq 'updated') {
-               printf(__n("updated %d path\n", "updated %d paths\n",
-                          $cnt), $cnt);
-       } elsif ($did eq 'reverted') {
-               printf(__n("reverted %d path\n", "reverted %d paths\n",
-                          $cnt), $cnt);
-       } else {
-               printf(__n("touched %d path\n", "touched %d paths\n",
-                          $cnt), $cnt);
-       }
-}
-
-sub update_cmd {
-       my @mods = list_modified('file-only');
-       return if (!@mods);
-
-       my @update = list_and_choose({ PROMPT => __('Update'),
-                                      HEADER => $status_head, },
-                                    @mods);
-       if (@update) {
-               system(qw(git update-index --add --remove --),
-                      map { $_->{VALUE} } @update);
-               say_n_paths('updated', @update);
-       }
-       print "\n";
-}
-
-sub revert_cmd {
-       my @update = list_and_choose({ PROMPT => __('Revert'),
-                                      HEADER => $status_head, },
-                                    list_modified());
-       if (@update) {
-               if (is_initial_commit()) {
-                       system(qw(git rm --cached),
-                               map { $_->{VALUE} } @update);
-               }
-               else {
-                       my @lines = run_cmd_pipe(qw(git ls-tree HEAD --),
-                                                map { $_->{VALUE} } @update);
-                       my $fh;
-                       open $fh, '| git update-index --index-info'
-                           or die;
-                       for (@lines) {
-                               print $fh $_;
-                       }
-                       close($fh);
-                       for (@update) {
-                               if ($_->{INDEX_ADDDEL} &&
-                                   $_->{INDEX_ADDDEL} eq 'create') {
-                                       system(qw(git update-index --force-remove --),
-                                              $_->{VALUE});
-                                       printf(__("note: %s is untracked now.\n"), $_->{VALUE});
-                               }
-                       }
-               }
-               refresh();
-               say_n_paths('reverted', @update);
-       }
-       print "\n";
-}
-
-sub add_untracked_cmd {
-       my @add = list_and_choose({ PROMPT => __('Add untracked') },
-                                 list_untracked());
-       if (@add) {
-               system(qw(git update-index --add --), @add);
-               say_n_paths('added', @add);
-       } else {
-               print __("No untracked files.\n");
-       }
-       print "\n";
-}
-
-sub run_git_apply {
-       my $cmd = shift;
-       my $fh;
-       open $fh, '| git ' . $cmd . " --allow-overlap";
-       print $fh @_;
-       return close $fh;
-}
-
-sub parse_diff {
-       my ($path) = @_;
-       my @diff_cmd = split(" ", $patch_mode_flavour{DIFF});
-       if (defined $diff_algorithm) {
-               splice @diff_cmd, 1, 0, "--diff-algorithm=${diff_algorithm}";
-       }
-       if (defined $patch_mode_revision) {
-               push @diff_cmd, get_diff_reference($patch_mode_revision);
-       }
-       my @diff = run_cmd_pipe("git", @diff_cmd, qw(--no-color --), $path);
-       my @colored = ();
-       if ($diff_use_color) {
-               my @display_cmd = ("git", @diff_cmd, qw(--color --), $path);
-               if (defined $diff_filter) {
-                       # quotemeta is overkill, but sufficient for shell-quoting
-                       my $diff = join(' ', map { quotemeta } @display_cmd);
-                       @display_cmd = ("$diff | $diff_filter");
-               }
-
-               @colored = run_cmd_pipe(@display_cmd);
-       }
-       my (@hunk) = { TEXT => [], DISPLAY => [], TYPE => 'header' };
-
-       if (@colored && @colored != @diff) {
-               print STDERR
-                 "fatal: mismatched output from interactive.diffFilter\n",
-                 "hint: Your filter must maintain a one-to-one correspondence\n",
-                 "hint: between its input and output lines.\n";
-               exit 1;
-       }
-
-       for (my $i = 0; $i < @diff; $i++) {
-               if ($diff[$i] =~ /^@@ /) {
-                       push @hunk, { TEXT => [], DISPLAY => [],
-                               TYPE => 'hunk' };
-               }
-               push @{$hunk[-1]{TEXT}}, $diff[$i];
-               push @{$hunk[-1]{DISPLAY}},
-                       (@colored ? $colored[$i] : $diff[$i]);
-       }
-       return @hunk;
-}
-
-sub parse_diff_header {
-       my $src = shift;
-
-       my $head = { TEXT => [], DISPLAY => [], TYPE => 'header' };
-       my $mode = { TEXT => [], DISPLAY => [], TYPE => 'mode' };
-       my $deletion = { TEXT => [], DISPLAY => [], TYPE => 'deletion' };
-       my $addition;
-
-       for (my $i = 0; $i < @{$src->{TEXT}}; $i++) {
-               if ($src->{TEXT}->[$i] =~ /^new file/) {
-                       $addition = 1;
-                       $head->{TYPE} = 'addition';
-               }
-               my $dest =
-                  $src->{TEXT}->[$i] =~ /^(old|new) mode (\d+)$/ ? $mode :
-                  $src->{TEXT}->[$i] =~ /^deleted file/ ? $deletion :
-                  $head;
-               push @{$dest->{TEXT}}, $src->{TEXT}->[$i];
-               push @{$dest->{DISPLAY}}, $src->{DISPLAY}->[$i];
-       }
-       return ($head, $mode, $deletion, $addition);
-}
-
-sub hunk_splittable {
-       my ($text) = @_;
-
-       my @s = split_hunk($text);
-       return (1 < @s);
-}
-
-sub parse_hunk_header {
-       my ($line) = @_;
-       my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) =
-           $line =~ /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
-       $o_cnt = 1 unless defined $o_cnt;
-       $n_cnt = 1 unless defined $n_cnt;
-       return ($o_ofs, $o_cnt, $n_ofs, $n_cnt);
-}
-
-sub format_hunk_header {
-       my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) = @_;
-       return ("@@ -$o_ofs" .
-               (($o_cnt != 1) ? ",$o_cnt" : '') .
-               " +$n_ofs" .
-               (($n_cnt != 1) ? ",$n_cnt" : '') .
-               " @@\n");
-}
-
-sub split_hunk {
-       my ($text, $display) = @_;
-       my @split = ();
-       if (!defined $display) {
-               $display = $text;
-       }
-       # If there are context lines in the middle of a hunk,
-       # it can be split, but we would need to take care of
-       # overlaps later.
-
-       my ($o_ofs, undef, $n_ofs) = parse_hunk_header($text->[0]);
-       my $hunk_start = 1;
-
-      OUTER:
-       while (1) {
-               my $next_hunk_start = undef;
-               my $i = $hunk_start - 1;
-               my $this = +{
-                       TEXT => [],
-                       DISPLAY => [],
-                       TYPE => 'hunk',
-                       OLD => $o_ofs,
-                       NEW => $n_ofs,
-                       OCNT => 0,
-                       NCNT => 0,
-                       ADDDEL => 0,
-                       POSTCTX => 0,
-                       USE => undef,
-               };
-
-               while (++$i < @$text) {
-                       my $line = $text->[$i];
-                       my $display = $display->[$i];
-                       if ($line =~ /^\\/) {
-                               push @{$this->{TEXT}}, $line;
-                               push @{$this->{DISPLAY}}, $display;
-                               next;
-                       }
-                       if ($line =~ /^ /) {
-                               if ($this->{ADDDEL} &&
-                                   !defined $next_hunk_start) {
-                                       # We have seen leading context and
-                                       # adds/dels and then here is another
-                                       # context, which is trailing for this
-                                       # split hunk and leading for the next
-                                       # one.
-                                       $next_hunk_start = $i;
-                               }
-                               push @{$this->{TEXT}}, $line;
-                               push @{$this->{DISPLAY}}, $display;
-                               $this->{OCNT}++;
-                               $this->{NCNT}++;
-                               if (defined $next_hunk_start) {
-                                       $this->{POSTCTX}++;
-                               }
-                               next;
-                       }
-
-                       # add/del
-                       if (defined $next_hunk_start) {
-                               # We are done with the current hunk and
-                               # this is the first real change for the
-                               # next split one.
-                               $hunk_start = $next_hunk_start;
-                               $o_ofs = $this->{OLD} + $this->{OCNT};
-                               $n_ofs = $this->{NEW} + $this->{NCNT};
-                               $o_ofs -= $this->{POSTCTX};
-                               $n_ofs -= $this->{POSTCTX};
-                               push @split, $this;
-                               redo OUTER;
-                       }
-                       push @{$this->{TEXT}}, $line;
-                       push @{$this->{DISPLAY}}, $display;
-                       $this->{ADDDEL}++;
-                       if ($line =~ /^-/) {
-                               $this->{OCNT}++;
-                       }
-                       else {
-                               $this->{NCNT}++;
-                       }
-               }
-
-               push @split, $this;
-               last;
-       }
-
-       for my $hunk (@split) {
-               $o_ofs = $hunk->{OLD};
-               $n_ofs = $hunk->{NEW};
-               my $o_cnt = $hunk->{OCNT};
-               my $n_cnt = $hunk->{NCNT};
-
-               my $head = format_hunk_header($o_ofs, $o_cnt, $n_ofs, $n_cnt);
-               my $display_head = $head;
-               unshift @{$hunk->{TEXT}}, $head;
-               if ($diff_use_color) {
-                       $display_head = colored($fraginfo_color, $head);
-               }
-               unshift @{$hunk->{DISPLAY}}, $display_head;
-       }
-       return @split;
-}
-
-sub find_last_o_ctx {
-       my ($it) = @_;
-       my $text = $it->{TEXT};
-       my ($o_ofs, $o_cnt) = parse_hunk_header($text->[0]);
-       my $i = @{$text};
-       my $last_o_ctx = $o_ofs + $o_cnt;
-       while (0 < --$i) {
-               my $line = $text->[$i];
-               if ($line =~ /^ /) {
-                       $last_o_ctx--;
-                       next;
-               }
-               last;
-       }
-       return $last_o_ctx;
-}
-
-sub merge_hunk {
-       my ($prev, $this) = @_;
-       my ($o0_ofs, $o0_cnt, $n0_ofs, $n0_cnt) =
-           parse_hunk_header($prev->{TEXT}[0]);
-       my ($o1_ofs, $o1_cnt, $n1_ofs, $n1_cnt) =
-           parse_hunk_header($this->{TEXT}[0]);
-
-       my (@line, $i, $ofs, $o_cnt, $n_cnt);
-       $ofs = $o0_ofs;
-       $o_cnt = $n_cnt = 0;
-       for ($i = 1; $i < @{$prev->{TEXT}}; $i++) {
-               my $line = $prev->{TEXT}[$i];
-               if ($line =~ /^\+/) {
-                       $n_cnt++;
-                       push @line, $line;
-                       next;
-               } elsif ($line =~ /^\\/) {
-                       push @line, $line;
-                       next;
-               }
-
-               last if ($o1_ofs <= $ofs);
-
-               $o_cnt++;
-               $ofs++;
-               if ($line =~ /^ /) {
-                       $n_cnt++;
-               }
-               push @line, $line;
-       }
-
-       for ($i = 1; $i < @{$this->{TEXT}}; $i++) {
-               my $line = $this->{TEXT}[$i];
-               if ($line =~ /^\+/) {
-                       $n_cnt++;
-                       push @line, $line;
-                       next;
-               } elsif ($line =~ /^\\/) {
-                       push @line, $line;
-                       next;
-               }
-               $ofs++;
-               $o_cnt++;
-               if ($line =~ /^ /) {
-                       $n_cnt++;
-               }
-               push @line, $line;
-       }
-       my $head = format_hunk_header($o0_ofs, $o_cnt, $n0_ofs, $n_cnt);
-       @{$prev->{TEXT}} = ($head, @line);
-}
-
-sub coalesce_overlapping_hunks {
-       my (@in) = @_;
-       my @out = ();
-
-       my ($last_o_ctx, $last_was_dirty);
-       my $ofs_delta = 0;
-
-       for (@in) {
-               if ($_->{TYPE} ne 'hunk') {
-                       push @out, $_;
-                       next;
-               }
-               my $text = $_->{TEXT};
-               my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) =
-                                               parse_hunk_header($text->[0]);
-               unless ($_->{USE}) {
-                       $ofs_delta += $o_cnt - $n_cnt;
-                       # If this hunk has been edited then subtract
-                       # the delta that is due to the edit.
-                       if ($_->{OFS_DELTA}) {
-                               $ofs_delta -= $_->{OFS_DELTA};
-                       }
-                       next;
-               }
-               if ($ofs_delta) {
-                       if ($patch_mode_flavour{IS_REVERSE}) {
-                               $o_ofs -= $ofs_delta;
-                       } else {
-                               $n_ofs += $ofs_delta;
-                       }
-                       $_->{TEXT}->[0] = format_hunk_header($o_ofs, $o_cnt,
-                                                            $n_ofs, $n_cnt);
-               }
-               # If this hunk was edited then adjust the offset delta
-               # to reflect the edit.
-               if ($_->{OFS_DELTA}) {
-                       $ofs_delta += $_->{OFS_DELTA};
-               }
-               if (defined $last_o_ctx &&
-                   $o_ofs <= $last_o_ctx &&
-                   !$_->{DIRTY} &&
-                   !$last_was_dirty) {
-                       merge_hunk($out[-1], $_);
-               }
-               else {
-                       push @out, $_;
-               }
-               $last_o_ctx = find_last_o_ctx($out[-1]);
-               $last_was_dirty = $_->{DIRTY};
-       }
-       return @out;
-}
-
-sub reassemble_patch {
-       my $head = shift;
-       my @patch;
-
-       # Include everything in the header except the beginning of the diff.
-       push @patch, (grep { !/^[-+]{3}/ } @$head);
-
-       # Then include any headers from the hunk lines, which must
-       # come before any actual hunk.
-       while (@_ && $_[0] !~ /^@/) {
-               push @patch, shift;
-       }
-
-       # Then begin the diff.
-       push @patch, grep { /^[-+]{3}/ } @$head;
-
-       # And then the actual hunks.
-       push @patch, @_;
-
-       return @patch;
-}
-
-sub color_diff {
-       return map {
-               colored((/^@/  ? $fraginfo_color :
-                        /^\+/ ? $diff_new_color :
-                        /^-/  ? $diff_old_color :
-                        $diff_context_color),
-                       $_);
-       } @_;
-}
-
-my %edit_hunk_manually_modes = (
-       stage => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for staging."),
-       stash => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for stashing."),
-       reset_head => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for unstaging."),
-       reset_nothead => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for applying."),
-       checkout_index => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for discarding."),
-       checkout_head => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for discarding."),
-       checkout_nothead => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for applying."),
-       worktree_head => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for discarding."),
-       worktree_nothead => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for applying."),
-);
-
-sub recount_edited_hunk {
-       local $_;
-       my ($oldtext, $newtext) = @_;
-       my ($o_cnt, $n_cnt) = (0, 0);
-       for (@{$newtext}[1..$#{$newtext}]) {
-               my $mode = substr($_, 0, 1);
-               if ($mode eq '-') {
-                       $o_cnt++;
-               } elsif ($mode eq '+') {
-                       $n_cnt++;
-               } elsif ($mode eq ' ' or $mode eq "\n") {
-                       $o_cnt++;
-                       $n_cnt++;
-               }
-       }
-       my ($o_ofs, undef, $n_ofs, undef) =
-                                       parse_hunk_header($newtext->[0]);
-       $newtext->[0] = format_hunk_header($o_ofs, $o_cnt, $n_ofs, $n_cnt);
-       my (undef, $orig_o_cnt, undef, $orig_n_cnt) =
-                                       parse_hunk_header($oldtext->[0]);
-       # Return the change in the number of lines inserted by this hunk
-       return $orig_o_cnt - $orig_n_cnt - $o_cnt + $n_cnt;
-}
-
-sub edit_hunk_manually {
-       my ($oldtext) = @_;
-
-       my $hunkfile = $repo->repo_path . "/addp-hunk-edit.diff";
-       my $fh;
-       open $fh, '>', $hunkfile
-               or die sprintf(__("failed to open hunk edit file for writing: %s"), $!);
-       print $fh Git::comment_lines __("Manual hunk edit mode -- see bottom for a quick guide.\n");
-       print $fh @$oldtext;
-       my $is_reverse = $patch_mode_flavour{IS_REVERSE};
-       my ($remove_plus, $remove_minus) = $is_reverse ? ('-', '+') : ('+', '-');
-       my $comment_line_char = Git::get_comment_line_char;
-       print $fh Git::comment_lines sprintf(__ <<EOF, $remove_minus, $remove_plus, $comment_line_char),
----
-To remove '%s' lines, make them ' ' lines (context).
-To remove '%s' lines, delete them.
-Lines starting with %s will be removed.
-EOF
-__($edit_hunk_manually_modes{$patch_mode}),
-# TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
-__ <<EOF2 ;
-If it does not apply cleanly, you will be given an opportunity to
-edit again.  If all lines of the hunk are removed, then the edit is
-aborted and the hunk is left unchanged.
-EOF2
-       close $fh;
-
-       chomp(my ($editor) = run_cmd_pipe(qw(git var GIT_EDITOR)));
-       system('sh', '-c', $editor.' "$@"', $editor, $hunkfile);
-
-       if ($? != 0) {
-               return undef;
-       }
-
-       open $fh, '<', $hunkfile
-               or die sprintf(__("failed to open hunk edit file for reading: %s"), $!);
-       my @newtext = grep { !/^\Q$comment_line_char\E/ } <$fh>;
-       close $fh;
-       unlink $hunkfile;
-
-       # Abort if nothing remains
-       if (!grep { /\S/ } @newtext) {
-               return undef;
-       }
-
-       # Reinsert the first hunk header if the user accidentally deleted it
-       if ($newtext[0] !~ /^@/) {
-               unshift @newtext, $oldtext->[0];
-       }
-       return \@newtext;
-}
-
-sub diff_applies {
-       return run_git_apply($patch_mode_flavour{APPLY_CHECK} . ' --check',
-                            map { @{$_->{TEXT}} } @_);
-}
-
-sub _restore_terminal_and_die {
-       ReadMode 'restore';
-       print "\n";
-       exit 1;
-}
-
-sub prompt_single_character {
-       if ($use_readkey) {
-               local $SIG{TERM} = \&_restore_terminal_and_die;
-               local $SIG{INT} = \&_restore_terminal_and_die;
-               ReadMode 'cbreak';
-               my $key = ReadKey 0;
-               ReadMode 'restore';
-               if (defined $key) {
-                       if ($use_termcap and $key eq "\e") {
-                               while (!defined $term_escapes{$key}) {
-                                       my $next = ReadKey 0.5;
-                                       last if (!defined $next);
-                                       $key .= $next;
-                               }
-                               $key =~ s/\e/^[/;
-                       }
-                       print "$key";
-               }
-               print "\n";
-               return $key;
-       } else {
-               return <STDIN>;
-       }
-}
-
-sub prompt_yesno {
-       my ($prompt) = @_;
-       while (1) {
-               print colored $prompt_color, $prompt;
-               my $line = prompt_single_character;
-               return undef unless defined $line;
-               return 0 if $line =~ /^n/i;
-               return 1 if $line =~ /^y/i;
-       }
-}
-
-sub edit_hunk_loop {
-       my ($head, $hunks, $ix) = @_;
-       my $hunk = $hunks->[$ix];
-       my $text = $hunk->{TEXT};
-
-       while (1) {
-               my $newtext = edit_hunk_manually($text);
-               if (!defined $newtext) {
-                       return undef;
-               }
-               my $newhunk = {
-                       TEXT => $newtext,
-                       TYPE => $hunk->{TYPE},
-                       USE => 1,
-                       DIRTY => 1,
-               };
-               $newhunk->{OFS_DELTA} = recount_edited_hunk($text, $newtext);
-               # If this hunk has already been edited then add the
-               # offset delta of the previous edit to get the real
-               # delta from the original unedited hunk.
-               $hunk->{OFS_DELTA} and
-                               $newhunk->{OFS_DELTA} += $hunk->{OFS_DELTA};
-               if (diff_applies($head,
-                                @{$hunks}[0..$ix-1],
-                                $newhunk,
-                                @{$hunks}[$ix+1..$#{$hunks}])) {
-                       $newhunk->{DISPLAY} = [color_diff(@{$newtext})];
-                       return $newhunk;
-               }
-               else {
-                       prompt_yesno(
-                               # TRANSLATORS: do not translate [y/n]
-                               # The program will only accept that input
-                               # at this point.
-                               # Consider translating (saying "no" discards!) as
-                               # (saying "n" for "no" discards!) if the translation
-                               # of the word "no" does not start with n.
-                               __('Your edited hunk does not apply. Edit again '
-                                  . '(saying "no" discards!) [y/n]? ')
-                               ) or return undef;
-               }
-       }
-}
-
-my %help_patch_modes = (
-       stage => N__(
-"y - stage this hunk
-n - do not stage this hunk
-q - quit; do not stage this hunk or any of the remaining ones
-a - stage this hunk and all later hunks in the file
-d - do not stage this hunk or any of the later hunks in the file"),
-       stash => N__(
-"y - stash this hunk
-n - do not stash this hunk
-q - quit; do not stash this hunk or any of the remaining ones
-a - stash this hunk and all later hunks in the file
-d - do not stash this hunk or any of the later hunks in the file"),
-       reset_head => N__(
-"y - unstage this hunk
-n - do not unstage this hunk
-q - quit; do not unstage this hunk or any of the remaining ones
-a - unstage this hunk and all later hunks in the file
-d - do not unstage this hunk or any of the later hunks in the file"),
-       reset_nothead => N__(
-"y - apply this hunk to index
-n - do not apply this hunk to index
-q - quit; do not apply this hunk or any of the remaining ones
-a - apply this hunk and all later hunks in the file
-d - do not apply this hunk or any of the later hunks in the file"),
-       checkout_index => N__(
-"y - discard this hunk from worktree
-n - do not discard this hunk from worktree
-q - quit; do not discard this hunk or any of the remaining ones
-a - discard this hunk and all later hunks in the file
-d - do not discard this hunk or any of the later hunks in the file"),
-       checkout_head => N__(
-"y - discard this hunk from index and worktree
-n - do not discard this hunk from index and worktree
-q - quit; do not discard this hunk or any of the remaining ones
-a - discard this hunk and all later hunks in the file
-d - do not discard this hunk or any of the later hunks in the file"),
-       checkout_nothead => N__(
-"y - apply this hunk to index and worktree
-n - do not apply this hunk to index and worktree
-q - quit; do not apply this hunk or any of the remaining ones
-a - apply this hunk and all later hunks in the file
-d - do not apply this hunk or any of the later hunks in the file"),
-       worktree_head => N__(
-"y - discard this hunk from worktree
-n - do not discard this hunk from worktree
-q - quit; do not discard this hunk or any of the remaining ones
-a - discard this hunk and all later hunks in the file
-d - do not discard this hunk or any of the later hunks in the file"),
-       worktree_nothead => N__(
-"y - apply this hunk to worktree
-n - do not apply this hunk to worktree
-q - quit; do not apply this hunk or any of the remaining ones
-a - apply this hunk and all later hunks in the file
-d - do not apply this hunk or any of the later hunks in the file"),
-);
-
-sub help_patch_cmd {
-       local $_;
-       my $other = $_[0] . ",?";
-       print colored $help_color, __($help_patch_modes{$patch_mode}), "\n",
-               map { "$_\n" } grep {
-                       my $c = quotemeta(substr($_, 0, 1));
-                       $other =~ /,$c/
-               } split "\n", __ <<EOF ;
-g - select a hunk to go to
-/ - search for a hunk matching the given regex
-j - leave this hunk undecided, see next undecided hunk
-J - leave this hunk undecided, see next hunk
-k - leave this hunk undecided, see previous undecided hunk
-K - leave this hunk undecided, see previous hunk
-s - split the current hunk into smaller hunks
-e - manually edit the current hunk
-? - print help
-EOF
-}
-
-sub apply_patch {
-       my $cmd = shift;
-       my $ret = run_git_apply $cmd, @_;
-       if (!$ret) {
-               print STDERR @_;
-       }
-       return $ret;
-}
-
-sub apply_patch_for_checkout_commit {
-       my $reverse = shift;
-       my $applies_index = run_git_apply 'apply '.$reverse.' --cached --check', @_;
-       my $applies_worktree = run_git_apply 'apply '.$reverse.' --check', @_;
-
-       if ($applies_worktree && $applies_index) {
-               run_git_apply 'apply '.$reverse.' --cached', @_;
-               run_git_apply 'apply '.$reverse, @_;
-               return 1;
-       } elsif (!$applies_index) {
-               print colored $error_color, __("The selected hunks do not apply to the index!\n");
-               if (prompt_yesno __("Apply them to the worktree anyway? ")) {
-                       return run_git_apply 'apply '.$reverse, @_;
-               } else {
-                       print colored $error_color, __("Nothing was applied.\n");
-                       return 0;
-               }
-       } else {
-               print STDERR @_;
-               return 0;
-       }
-}
-
-sub patch_update_cmd {
-       my @all_mods = list_modified($patch_mode_flavour{FILTER});
-       error_msg sprintf(__("ignoring unmerged: %s\n"), $_->{VALUE})
-               for grep { $_->{UNMERGED} } @all_mods;
-       @all_mods = grep { !$_->{UNMERGED} } @all_mods;
-
-       my @mods = grep { !($_->{BINARY}) } @all_mods;
-       my @them;
-
-       if (!@mods) {
-               if (@all_mods) {
-                       print STDERR __("Only binary files changed.\n");
-               } else {
-                       print STDERR __("No changes.\n");
-               }
-               return 0;
-       }
-       if ($patch_mode_only) {
-               @them = @mods;
-       }
-       else {
-               @them = list_and_choose({ PROMPT => __('Patch update'),
-                                         HEADER => $status_head, },
-                                       @mods);
-       }
-       for (@them) {
-               return 0 if patch_update_file($_->{VALUE});
-       }
-}
-
-# Generate a one line summary of a hunk.
-sub summarize_hunk {
-       my $rhunk = shift;
-       my $summary = $rhunk->{TEXT}[0];
-
-       # Keep the line numbers, discard extra context.
-       $summary =~ s/@@(.*?)@@.*/$1 /s;
-       $summary .= " " x (20 - length $summary);
-
-       # Add some user context.
-       for my $line (@{$rhunk->{TEXT}}) {
-               if ($line =~ m/^[+-].*\w/) {
-                       $summary .= $line;
-                       last;
-               }
-       }
-
-       chomp $summary;
-       return substr($summary, 0, 80) . "\n";
-}
-
-
-# Print a one-line summary of each hunk in the array ref in
-# the first argument, starting with the index in the 2nd.
-sub display_hunks {
-       my ($hunks, $i) = @_;
-       my $ctr = 0;
-       $i ||= 0;
-       for (; $i < @$hunks && $ctr < 20; $i++, $ctr++) {
-               my $status = " ";
-               if (defined $hunks->[$i]{USE}) {
-                       $status = $hunks->[$i]{USE} ? "+" : "-";
-               }
-               printf "%s%2d: %s",
-                       $status,
-                       $i + 1,
-                       summarize_hunk($hunks->[$i]);
-       }
-       return $i;
-}
-
-my %patch_update_prompt_modes = (
-       stage => {
-               mode => N__("Stage mode change [y,n,q,a,d%s,?]? "),
-               deletion => N__("Stage deletion [y,n,q,a,d%s,?]? "),
-               addition => N__("Stage addition [y,n,q,a,d%s,?]? "),
-               hunk => N__("Stage this hunk [y,n,q,a,d%s,?]? "),
-       },
-       stash => {
-               mode => N__("Stash mode change [y,n,q,a,d%s,?]? "),
-               deletion => N__("Stash deletion [y,n,q,a,d%s,?]? "),
-               addition => N__("Stash addition [y,n,q,a,d%s,?]? "),
-               hunk => N__("Stash this hunk [y,n,q,a,d%s,?]? "),
-       },
-       reset_head => {
-               mode => N__("Unstage mode change [y,n,q,a,d%s,?]? "),
-               deletion => N__("Unstage deletion [y,n,q,a,d%s,?]? "),
-               addition => N__("Unstage addition [y,n,q,a,d%s,?]? "),
-               hunk => N__("Unstage this hunk [y,n,q,a,d%s,?]? "),
-       },
-       reset_nothead => {
-               mode => N__("Apply mode change to index [y,n,q,a,d%s,?]? "),
-               deletion => N__("Apply deletion to index [y,n,q,a,d%s,?]? "),
-               addition => N__("Apply addition to index [y,n,q,a,d%s,?]? "),
-               hunk => N__("Apply this hunk to index [y,n,q,a,d%s,?]? "),
-       },
-       checkout_index => {
-               mode => N__("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
-               deletion => N__("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
-               addition => N__("Discard addition from worktree [y,n,q,a,d%s,?]? "),
-               hunk => N__("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
-       },
-       checkout_head => {
-               mode => N__("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
-               deletion => N__("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
-               addition => N__("Discard addition from index and worktree [y,n,q,a,d%s,?]? "),
-               hunk => N__("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
-       },
-       checkout_nothead => {
-               mode => N__("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
-               deletion => N__("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
-               addition => N__("Apply addition to index and worktree [y,n,q,a,d%s,?]? "),
-               hunk => N__("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
-       },
-       worktree_head => {
-               mode => N__("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
-               deletion => N__("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
-               addition => N__("Discard addition from worktree [y,n,q,a,d%s,?]? "),
-               hunk => N__("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
-       },
-       worktree_nothead => {
-               mode => N__("Apply mode change to worktree [y,n,q,a,d%s,?]? "),
-               deletion => N__("Apply deletion to worktree [y,n,q,a,d%s,?]? "),
-               addition => N__("Apply addition to worktree [y,n,q,a,d%s,?]? "),
-               hunk => N__("Apply this hunk to worktree [y,n,q,a,d%s,?]? "),
-       },
-);
-
-sub patch_update_file {
-       my $quit = 0;
-       my ($ix, $num);
-       my $path = shift;
-       my ($head, @hunk) = parse_diff($path);
-       ($head, my $mode, my $deletion, my $addition) = parse_diff_header($head);
-       for (@{$head->{DISPLAY}}) {
-               print;
-       }
-
-       if (@{$mode->{TEXT}}) {
-               unshift @hunk, $mode;
-       }
-       if (@{$deletion->{TEXT}}) {
-               foreach my $hunk (@hunk) {
-                       push @{$deletion->{TEXT}}, @{$hunk->{TEXT}};
-                       push @{$deletion->{DISPLAY}}, @{$hunk->{DISPLAY}};
-               }
-               @hunk = ($deletion);
-       }
-
-       $num = scalar @hunk;
-       $ix = 0;
-
-       while (1) {
-               my ($prev, $next, $other, $undecided, $i);
-               $other = '';
-
-               last if ($ix and !$num);
-               if ($num <= $ix) {
-                       $ix = 0;
-               }
-               for ($i = 0; $i < $ix; $i++) {
-                       if (!defined $hunk[$i]{USE}) {
-                               $prev = 1;
-                               $other .= ',k';
-                               last;
-                       }
-               }
-               if ($ix) {
-                       $other .= ',K';
-               }
-               for ($i = $ix + 1; $i < $num; $i++) {
-                       if (!defined $hunk[$i]{USE}) {
-                               $next = 1;
-                               $other .= ',j';
-                               last;
-                       }
-               }
-               if ($ix < $num - 1) {
-                       $other .= ',J';
-               }
-               if ($num > 1) {
-                       $other .= ',g,/';
-               }
-               for ($i = 0; $i < $num; $i++) {
-                       if (!defined $hunk[$i]{USE}) {
-                               $undecided = 1;
-                               last;
-                       }
-               }
-               last if (!$undecided && ($num || !$addition));
-
-               if ($num) {
-                       if ($hunk[$ix]{TYPE} eq 'hunk' &&
-                           hunk_splittable($hunk[$ix]{TEXT})) {
-                               $other .= ',s';
-                       }
-                       if ($hunk[$ix]{TYPE} eq 'hunk') {
-                               $other .= ',e';
-                       }
-                       for (@{$hunk[$ix]{DISPLAY}}) {
-                               print;
-                       }
-               }
-               my $type = $num ? $hunk[$ix]{TYPE} : $head->{TYPE};
-               print colored $prompt_color, "(", ($ix+1), "/", ($num ? $num : 1), ") ",
-                       sprintf(__($patch_update_prompt_modes{$patch_mode}{$type}), $other);
-
-               my $line = prompt_single_character;
-               last unless defined $line;
-               if ($line) {
-                       if ($line =~ /^y/i) {
-                               if ($num) {
-                                       $hunk[$ix]{USE} = 1;
-                               } else {
-                                       $head->{USE} = 1;
-                               }
-                       }
-                       elsif ($line =~ /^n/i) {
-                               if ($num) {
-                                       $hunk[$ix]{USE} = 0;
-                               } else {
-                                       $head->{USE} = 0;
-                               }
-                       }
-                       elsif ($line =~ /^a/i) {
-                               if ($num) {
-                                       while ($ix < $num) {
-                                               if (!defined $hunk[$ix]{USE}) {
-                                                       $hunk[$ix]{USE} = 1;
-                                               }
-                                               $ix++;
-                                       }
-                               } else {
-                                       $head->{USE} = 1;
-                                       $ix++;
-                               }
-                               next;
-                       }
-                       elsif ($line =~ /^g(.*)/) {
-                               my $response = $1;
-                               unless ($other =~ /g/) {
-                                       error_msg __("No other hunks to goto\n");
-                                       next;
-                               }
-                               my $no = $ix > 10 ? $ix - 10 : 0;
-                               while ($response eq '') {
-                                       $no = display_hunks(\@hunk, $no);
-                                       if ($no < $num) {
-                                               print __("go to which hunk (<ret> to see more)? ");
-                                       } else {
-                                               print __("go to which hunk? ");
-                                       }
-                                       $response = <STDIN>;
-                                       if (!defined $response) {
-                                               $response = '';
-                                       }
-                                       chomp $response;
-                               }
-                               if ($response !~ /^\s*\d+\s*$/) {
-                                       error_msg sprintf(__("Invalid number: '%s'\n"),
-                                                            $response);
-                               } elsif (0 < $response && $response <= $num) {
-                                       $ix = $response - 1;
-                               } else {
-                                       error_msg sprintf(__n("Sorry, only %d hunk available.\n",
-                                                             "Sorry, only %d hunks available.\n", $num), $num);
-                               }
-                               next;
-                       }
-                       elsif ($line =~ /^d/i) {
-                               if ($num) {
-                                       while ($ix < $num) {
-                                               if (!defined $hunk[$ix]{USE}) {
-                                                       $hunk[$ix]{USE} = 0;
-                                               }
-                                               $ix++;
-                                       }
-                               } else {
-                                       $head->{USE} = 0;
-                                       $ix++;
-                               }
-                               next;
-                       }
-                       elsif ($line =~ /^q/i) {
-                               if ($num) {
-                                       for ($i = 0; $i < $num; $i++) {
-                                               if (!defined $hunk[$i]{USE}) {
-                                                       $hunk[$i]{USE} = 0;
-                                               }
-                                       }
-                               } elsif (!defined $head->{USE}) {
-                                       $head->{USE} = 0;
-                               }
-                               $quit = 1;
-                               last;
-                       }
-                       elsif ($line =~ m|^/(.*)|) {
-                               my $regex = $1;
-                               unless ($other =~ m|/|) {
-                                       error_msg __("No other hunks to search\n");
-                                       next;
-                               }
-                               if ($regex eq "") {
-                                       print colored $prompt_color, __("search for regex? ");
-                                       $regex = <STDIN>;
-                                       if (defined $regex) {
-                                               chomp $regex;
-                                       }
-                               }
-                               my $search_string;
-                               eval {
-                                       $search_string = qr{$regex}m;
-                               };
-                               if ($@) {
-                                       my ($err,$exp) = ($@, $1);
-                                       $err =~ s/ at .*git-add--interactive line \d+, <STDIN> line \d+.*$//;
-                                       error_msg sprintf(__("Malformed search regexp %s: %s\n"), $exp, $err);
-                                       next;
-                               }
-                               my $iy = $ix;
-                               while (1) {
-                                       my $text = join ("", @{$hunk[$iy]{TEXT}});
-                                       last if ($text =~ $search_string);
-                                       $iy++;
-                                       $iy = 0 if ($iy >= $num);
-                                       if ($ix == $iy) {
-                                               error_msg __("No hunk matches the given pattern\n");
-                                               last;
-                                       }
-                               }
-                               $ix = $iy;
-                               next;
-                       }
-                       elsif ($line =~ /^K/) {
-                               if ($other =~ /K/) {
-                                       $ix--;
-                               }
-                               else {
-                                       error_msg __("No previous hunk\n");
-                               }
-                               next;
-                       }
-                       elsif ($line =~ /^J/) {
-                               if ($other =~ /J/) {
-                                       $ix++;
-                               }
-                               else {
-                                       error_msg __("No next hunk\n");
-                               }
-                               next;
-                       }
-                       elsif ($line =~ /^k/) {
-                               if ($other =~ /k/) {
-                                       while (1) {
-                                               $ix--;
-                                               last if (!$ix ||
-                                                        !defined $hunk[$ix]{USE});
-                                       }
-                               }
-                               else {
-                                       error_msg __("No previous hunk\n");
-                               }
-                               next;
-                       }
-                       elsif ($line =~ /^j/) {
-                               if ($other !~ /j/) {
-                                       error_msg __("No next hunk\n");
-                                       next;
-                               }
-                       }
-                       elsif ($line =~ /^s/) {
-                               unless ($other =~ /s/) {
-                                       error_msg __("Sorry, cannot split this hunk\n");
-                                       next;
-                               }
-                               my @split = split_hunk($hunk[$ix]{TEXT}, $hunk[$ix]{DISPLAY});
-                               if (1 < @split) {
-                                       print colored $header_color, sprintf(
-                                               __n("Split into %d hunk.\n",
-                                                   "Split into %d hunks.\n",
-                                                   scalar(@split)), scalar(@split));
-                               }
-                               splice (@hunk, $ix, 1, @split);
-                               $num = scalar @hunk;
-                               next;
-                       }
-                       elsif ($line =~ /^e/) {
-                               unless ($other =~ /e/) {
-                                       error_msg __("Sorry, cannot edit this hunk\n");
-                                       next;
-                               }
-                               my $newhunk = edit_hunk_loop($head, \@hunk, $ix);
-                               if (defined $newhunk) {
-                                       splice @hunk, $ix, 1, $newhunk;
-                               }
-                       }
-                       else {
-                               help_patch_cmd($other);
-                               next;
-                       }
-                       # soft increment
-                       while (1) {
-                               $ix++;
-                               last if ($ix >= $num ||
-                                        !defined $hunk[$ix]{USE});
-                       }
-               }
-       }
-
-       @hunk = coalesce_overlapping_hunks(@hunk) if ($num);
-
-       my $n_lofs = 0;
-       my @result = ();
-       for (@hunk) {
-               if ($_->{USE}) {
-                       push @result, @{$_->{TEXT}};
-               }
-       }
-
-       if (@result or $head->{USE}) {
-               my @patch = reassemble_patch($head->{TEXT}, @result);
-               my $apply_routine = $patch_mode_flavour{APPLY};
-               &$apply_routine(@patch);
-               refresh();
-       }
-
-       print "\n";
-       return $quit;
-}
-
-sub diff_cmd {
-       my @mods = list_modified('index-only');
-       @mods = grep { !($_->{BINARY}) } @mods;
-       return if (!@mods);
-       my (@them) = list_and_choose({ PROMPT => __('Review diff'),
-                                    IMMEDIATE => 1,
-                                    HEADER => $status_head, },
-                                  @mods);
-       return if (!@them);
-       my $reference = (is_initial_commit()) ? get_empty_tree() : 'HEAD';
-       system(qw(git diff -p --cached), $reference, '--',
-               map { $_->{VALUE} } @them);
-}
-
-sub quit_cmd {
-       print __("Bye.\n");
-       exit(0);
-}
-
-sub help_cmd {
-# TRANSLATORS: please do not translate the command names
-# 'status', 'update', 'revert', etc.
-       print colored $help_color, __ <<'EOF' ;
-status        - show paths with changes
-update        - add working tree state to the staged set of changes
-revert        - revert staged set of changes back to the HEAD version
-patch         - pick hunks and update selectively
-diff          - view diff between HEAD and index
-add untracked - add contents of untracked files to the staged set of changes
-EOF
-}
-
-sub process_args {
-       return unless @ARGV;
-       my $arg = shift @ARGV;
-       if ($arg =~ /--patch(?:=(.*))?/) {
-               if (defined $1) {
-                       if ($1 eq 'reset') {
-                               $patch_mode = 'reset_head';
-                               $patch_mode_revision = 'HEAD';
-                               $arg = shift @ARGV or die __("missing --");
-                               if ($arg ne '--') {
-                                       $patch_mode_revision = $arg;
-
-                                       # NEEDSWORK: Instead of comparing to the literal "HEAD",
-                                       # compare the commit objects instead so that other ways of
-                                       # saying the same thing (such as "@") are also handled
-                                       # appropriately.
-                                       #
-                                       # This applies to the cases below too.
-                                       $patch_mode = ($arg eq 'HEAD' ?
-                                                      'reset_head' : 'reset_nothead');
-                                       $arg = shift @ARGV or die __("missing --");
-                               }
-                       } elsif ($1 eq 'checkout') {
-                               $arg = shift @ARGV or die __("missing --");
-                               if ($arg eq '--') {
-                                       $patch_mode = 'checkout_index';
-                               } else {
-                                       $patch_mode_revision = $arg;
-                                       $patch_mode = ($arg eq 'HEAD' ?
-                                                      'checkout_head' : 'checkout_nothead');
-                                       $arg = shift @ARGV or die __("missing --");
-                               }
-                       } elsif ($1 eq 'worktree') {
-                               $arg = shift @ARGV or die __("missing --");
-                               if ($arg eq '--') {
-                                       $patch_mode = 'checkout_index';
-                               } else {
-                                       $patch_mode_revision = $arg;
-                                       $patch_mode = ($arg eq 'HEAD' ?
-                                                      'worktree_head' : 'worktree_nothead');
-                                       $arg = shift @ARGV or die __("missing --");
-                               }
-                       } elsif ($1 eq 'stage' or $1 eq 'stash') {
-                               $patch_mode = $1;
-                               $arg = shift @ARGV or die __("missing --");
-                       } else {
-                               die sprintf(__("unknown --patch mode: %s"), $1);
-                       }
-               } else {
-                       $patch_mode = 'stage';
-                       $arg = shift @ARGV or die __("missing --");
-               }
-               die sprintf(__("invalid argument %s, expecting --"),
-                              $arg) unless $arg eq "--";
-               %patch_mode_flavour = %{$patch_modes{$patch_mode}};
-               $patch_mode_only = 1;
-       }
-       elsif ($arg ne "--") {
-               die sprintf(__("invalid argument %s, expecting --"), $arg);
-       }
-}
-
-sub main_loop {
-       my @cmd = ([ 'status', \&status_cmd, ],
-                  [ 'update', \&update_cmd, ],
-                  [ 'revert', \&revert_cmd, ],
-                  [ 'add untracked', \&add_untracked_cmd, ],
-                  [ 'patch', \&patch_update_cmd, ],
-                  [ 'diff', \&diff_cmd, ],
-                  [ 'quit', \&quit_cmd, ],
-                  [ 'help', \&help_cmd, ],
-       );
-       while (1) {
-               my ($it) = list_and_choose({ PROMPT => __('What now'),
-                                            SINGLETON => 1,
-                                            LIST_FLAT => 4,
-                                            HEADER => __('*** Commands ***'),
-                                            ON_EOF => \&quit_cmd,
-                                            IMMEDIATE => 1 }, @cmd);
-               if ($it) {
-                       eval {
-                               $it->[1]->();
-                       };
-                       if ($@) {
-                               print "$@";
-                       }
-               }
-       }
-}
-
-process_args();
-refresh();
-if ($patch_mode_only) {
-       patch_update_cmd();
-}
-else {
-       status_cmd();
-       main_loop();
-}
diff --git a/git-bisect.sh b/git-bisect.sh
deleted file mode 100755 (executable)
index dfce4b4..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-#!/bin/sh
-
-USAGE='[help|start|bad|good|new|old|terms|skip|next|reset|visualize|view|replay|log|run]'
-LONG_USAGE='git bisect help
-       print this long help message.
-git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]
-                [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]
-       reset bisect state and start bisection.
-git bisect (bad|new) [<rev>]
-       mark <rev> a known-bad revision/
-               a revision after change in a given property.
-git bisect (good|old) [<rev>...]
-       mark <rev>... known-good revisions/
-               revisions before change in a given property.
-git bisect terms [--term-good | --term-bad]
-       show the terms used for old and new commits (default: bad, good)
-git bisect skip [(<rev>|<range>)...]
-       mark <rev>... untestable revisions.
-git bisect next
-       find next bisection to test and check it out.
-git bisect reset [<commit>]
-       finish bisection search and go back to commit.
-git bisect (visualize|view)
-       show bisect status in gitk.
-git bisect replay <logfile>
-       replay bisection log.
-git bisect log
-       show bisect log.
-git bisect run <cmd>...
-       use <cmd>... to automatically bisect.
-
-Please use "git help bisect" to get the full man page.'
-
-OPTIONS_SPEC=
-. git-sh-setup
-
-TERM_BAD=bad
-TERM_GOOD=good
-
-get_terms () {
-       if test -s "$GIT_DIR/BISECT_TERMS"
-       then
-               {
-               read TERM_BAD
-               read TERM_GOOD
-               } <"$GIT_DIR/BISECT_TERMS"
-       fi
-}
-
-case "$#" in
-0)
-       usage ;;
-*)
-       cmd="$1"
-       get_terms
-       shift
-       case "$cmd" in
-       help)
-               git bisect -h ;;
-       bad|good|new|old|"$TERM_BAD"|"$TERM_GOOD")
-               git bisect--helper state "$cmd" "$@" ;;
-       log)
-               git bisect--helper log || exit ;;
-       *)
-               git bisect--helper "$cmd" "$@" ;;
-       esac
-esac
index af05077560a6671a143239d57a1912934c7a0f81..4f0028ce60ce44ca7febc1fe78dda77b5a793a01 100644 (file)
@@ -97,8 +97,14 @@ struct strbuf;
 # define BARF_UNLESS_AN_ARRAY(arr)                                             \
        BUILD_ASSERT_OR_ZERO(!__builtin_types_compatible_p(__typeof__(arr), \
                                                           __typeof__(&(arr)[0])))
+# define BARF_UNLESS_COPYABLE(dst, src) \
+       BUILD_ASSERT_OR_ZERO(__builtin_types_compatible_p(__typeof__(*(dst)), \
+                                                         __typeof__(*(src))))
 #else
 # define BARF_UNLESS_AN_ARRAY(arr) 0
+# define BARF_UNLESS_COPYABLE(dst, src) \
+       BUILD_ASSERT_OR_ZERO(0 ? ((*(dst) = *(src)), 0) : \
+                                sizeof(*(dst)) == sizeof(*(src)))
 #endif
 /*
  * ARRAY_SIZE - get the number of elements in a visible array
@@ -341,11 +347,13 @@ struct itimerval {
 #endif
 
 #ifdef NO_SETITIMER
-static inline int setitimer(int which UNUSED,
-                           const struct itimerval *value UNUSED,
-                           struct itimerval *newvalue UNUSED) {
+static inline int git_setitimer(int which UNUSED,
+                               const struct itimerval *value UNUSED,
+                               struct itimerval *newvalue UNUSED) {
        return 0; /* pretend success */
 }
+#undef setitimer
+#define setitimer(which,value,ovalue) git_setitimer(which,value,ovalue)
 #endif
 
 #ifndef NO_LIBGEN_H
@@ -1100,7 +1108,7 @@ int xstrncmpz(const char *s, const char *t, size_t len);
 #define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), st_mult(sizeof(*(x)), (alloc)))
 
 #define COPY_ARRAY(dst, src, n) copy_array((dst), (src), (n), sizeof(*(dst)) + \
-       BUILD_ASSERT_OR_ZERO(sizeof(*(dst)) == sizeof(*(src))))
+       BARF_UNLESS_COPYABLE((dst), (src)))
 static inline void copy_array(void *dst, const void *src, size_t n, size_t size)
 {
        if (n)
@@ -1108,13 +1116,18 @@ static inline void copy_array(void *dst, const void *src, size_t n, size_t size)
 }
 
 #define MOVE_ARRAY(dst, src, n) move_array((dst), (src), (n), sizeof(*(dst)) + \
-       BUILD_ASSERT_OR_ZERO(sizeof(*(dst)) == sizeof(*(src))))
+       BARF_UNLESS_COPYABLE((dst), (src)))
 static inline void move_array(void *dst, const void *src, size_t n, size_t size)
 {
        if (n)
                memmove(dst, src, st_mult(size, n));
 }
 
+#define DUP_ARRAY(dst, src, n) do { \
+       size_t dup_array_n_ = (n); \
+       COPY_ARRAY(ALLOC_ARRAY((dst), dup_array_n_), (src), dup_array_n_); \
+} while (0)
+
 /*
  * These functions help you allocate structs with flex arrays, and copy
  * the data directly into the array. For example, if you had:
@@ -1344,6 +1357,11 @@ static inline int regexec_buf(const regex_t *preg, const char *buf, size_t size,
        return regexec(preg, buf, nmatch, pmatch, eflags | REG_STARTEND);
 }
 
+#ifdef USE_ENHANCED_BASIC_REGULAR_EXPRESSIONS
+int git_regcomp(regex_t *preg, const char *pattern, int cflags);
+#define regcomp git_regcomp
+#endif
+
 #ifndef DIR_HAS_BSD_GROUP_SEMANTICS
 # define FORCE_DIR_SET_GID S_ISGID
 #else
@@ -1479,14 +1497,19 @@ int open_nofollow(const char *path, int flags);
 #endif
 
 #ifndef _POSIX_THREAD_SAFE_FUNCTIONS
-static inline void flockfile(FILE *fh UNUSED)
+static inline void git_flockfile(FILE *fh UNUSED)
 {
        ; /* nothing */
 }
-static inline void funlockfile(FILE *fh UNUSED)
+static inline void git_funlockfile(FILE *fh UNUSED)
 {
        ; /* nothing */
 }
+#undef flockfile
+#undef funlockfile
+#undef getc_unlocked
+#define flockfile(fh) git_flockfile(fh)
+#define funlockfile(fh) git_funlockfile(fh)
 #define getc_unlocked(fh) getc(fh)
 #endif
 
index 2d0e44656cc6ca4cc0cd8b579a1ebd8798e90b5f..01640a044bb10f4167338fca957c43a240e1b6c0 100755 (executable)
@@ -153,7 +153,7 @@ for you to fetch changes up to %H:
 if test $(git cat-file -t "$head") = tag
 then
        git cat-file tag "$head" |
-       sed -n -e '1,/^$/d' -e '/^-----BEGIN PGP /q' -e p
+       sed -n -e '1,/^$/d' -e '/^-----BEGIN \(PGP\|SSH\|SIGNED\) /q' -e p
        echo
        echo "----------------------------------------------------------------"
 fi &&
index 5861e99a6eb2a16bb45e759645e124a66a50e107..07f2a0cbeaadd37f6ea13ee394a05257cdd3ae5d 100755 (executable)
@@ -220,6 +220,10 @@ my $compose_filename;
 my $force = 0;
 my $dump_aliases = 0;
 
+# Variables to prevent short format-patch options from being captured
+# as abbreviated send-email options
+my $reroll_count;
+
 # Handle interactive edition of files.
 my $multiedit;
 my $editor;
@@ -542,6 +546,7 @@ my %options = (
                    "batch-size=i" => \$batch_size,
                    "relogin-delay=i" => \$relogin_delay,
                    "git-completion-helper" => \$git_completion_helper,
+                   "v=s" => \$reroll_count,
 );
 $rc = GetOptions(%options);
 
@@ -782,7 +787,9 @@ if (@rev_list_opts) {
        die __("Cannot run git format-patch from outside a repository\n")
                unless $repo;
        require File::Temp;
-       push @files, $repo->command('format-patch', '-o', File::Temp::tempdir(CLEANUP => 1), @rev_list_opts);
+       push @files, $repo->command('format-patch', '-o', File::Temp::tempdir(CLEANUP => 1),
+                                   defined $reroll_count ? ('-v', $reroll_count) : (),
+                                   @rev_list_opts);
 }
 
 @files = handle_backup_files(@files);
index 9a50f2e9124492589aa3a298a8bec74dbded8e32..7f9582d92343450e0c71ba72e5faee845cc1165b 100755 (executable)
@@ -244,6 +244,9 @@ cmd_update()
                -q|--quiet)
                        quiet=1
                        ;;
+               -v|--verbose)
+                       quiet=0
+                       ;;
                --progress)
                        progress=1
                        ;;
diff --git a/git.c b/git.c
index 10202a7f126aba88c41c51e140f8c0aa0b148c02..96b0a2837dcd69d875181fe6b9f395f1703c4583 100644 (file)
--- a/git.c
+++ b/git.c
@@ -14,9 +14,8 @@
  * RUN_SETUP for reading from the configuration file.
  */
 #define NEED_WORK_TREE         (1<<3)
-#define SUPPORT_SUPER_PREFIX   (1<<4)
-#define DELAY_PAGER_CONFIG     (1<<5)
-#define NO_PARSEOPT            (1<<6) /* parse-options is not used */
+#define DELAY_PAGER_CONFIG     (1<<4)
+#define NO_PARSEOPT            (1<<5) /* parse-options is not used */
 
 struct cmd_struct {
        const char *cmd;
@@ -29,8 +28,7 @@ const char git_usage_string[] =
           "           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
           "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]\n"
           "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
-          "           [--super-prefix=<path>] [--config-env=<name>=<envvar>]\n"
-          "           <command> [<args>]");
+          "           [--config-env=<name>=<envvar>] <command> [<args>]");
 
 const char git_more_info_string[] =
        N_("'git help -a' and 'git help -g' list available subcommands and some\n"
@@ -226,20 +224,6 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                        setenv(GIT_WORK_TREE_ENVIRONMENT, cmd, 1);
                        if (envchanged)
                                *envchanged = 1;
-               } else if (!strcmp(cmd, "--super-prefix")) {
-                       if (*argc < 2) {
-                               fprintf(stderr, _("no prefix given for --super-prefix\n" ));
-                               usage(git_usage_string);
-                       }
-                       setenv(GIT_SUPER_PREFIX_ENVIRONMENT, (*argv)[1], 1);
-                       if (envchanged)
-                               *envchanged = 1;
-                       (*argv)++;
-                       (*argc)--;
-               } else if (skip_prefix(cmd, "--super-prefix=", &cmd)) {
-                       setenv(GIT_SUPER_PREFIX_ENVIRONMENT, cmd, 1);
-                       if (envchanged)
-                               *envchanged = 1;
                } else if (!strcmp(cmd, "--bare")) {
                        char *cwd = xgetcwd();
                        is_bare_repository_cfg = 1;
@@ -449,11 +433,6 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
                trace_repo_setup(prefix);
        commit_pager_choice();
 
-       if (!help && get_super_prefix()) {
-               if (!(p->option & SUPPORT_SUPER_PREFIX))
-                       die(_("%s doesn't support --super-prefix"), p->cmd);
-       }
-
        if (!help && p->option & NEED_WORK_TREE)
                setup_work_tree();
 
@@ -492,7 +471,7 @@ static struct cmd_struct commands[] = {
        { "annotate", cmd_annotate, RUN_SETUP },
        { "apply", cmd_apply, RUN_SETUP_GENTLY },
        { "archive", cmd_archive, RUN_SETUP_GENTLY },
-       { "bisect--helper", cmd_bisect__helper, RUN_SETUP },
+       { "bisect", cmd_bisect, RUN_SETUP },
        { "blame", cmd_blame, RUN_SETUP },
        { "branch", cmd_branch, RUN_SETUP | DELAY_PAGER_CONFIG },
        { "bugreport", cmd_bugreport, RUN_SETUP_GENTLY },
@@ -504,7 +483,7 @@ static struct cmd_struct commands[] = {
        { "check-ref-format", cmd_check_ref_format, NO_PARSEOPT  },
        { "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
        { "checkout--worker", cmd_checkout__worker,
-               RUN_SETUP | NEED_WORK_TREE | SUPPORT_SUPER_PREFIX },
+               RUN_SETUP | NEED_WORK_TREE },
        { "checkout-index", cmd_checkout_index,
                RUN_SETUP | NEED_WORK_TREE},
        { "cherry", cmd_cherry, RUN_SETUP },
@@ -528,7 +507,6 @@ static struct cmd_struct commands[] = {
        { "diff-index", cmd_diff_index, RUN_SETUP | NO_PARSEOPT },
        { "diff-tree", cmd_diff_tree, RUN_SETUP | NO_PARSEOPT },
        { "difftool", cmd_difftool, RUN_SETUP_GENTLY },
-       { "env--helper", cmd_env__helper },
        { "fast-export", cmd_fast_export, RUN_SETUP },
        { "fast-import", cmd_fast_import, RUN_SETUP | NO_PARSEOPT },
        { "fetch", cmd_fetch, RUN_SETUP },
@@ -539,7 +517,7 @@ static struct cmd_struct commands[] = {
        { "format-patch", cmd_format_patch, RUN_SETUP },
        { "fsck", cmd_fsck, RUN_SETUP },
        { "fsck-objects", cmd_fsck, RUN_SETUP },
-       { "fsmonitor--daemon", cmd_fsmonitor__daemon, SUPPORT_SUPER_PREFIX | RUN_SETUP },
+       { "fsmonitor--daemon", cmd_fsmonitor__daemon, RUN_SETUP },
        { "gc", cmd_gc, RUN_SETUP },
        { "get-tar-commit-id", cmd_get_tar_commit_id, NO_PARSEOPT },
        { "grep", cmd_grep, RUN_SETUP_GENTLY },
@@ -583,7 +561,7 @@ static struct cmd_struct commands[] = {
        { "pull", cmd_pull, RUN_SETUP | NEED_WORK_TREE },
        { "push", cmd_push, RUN_SETUP },
        { "range-diff", cmd_range_diff, RUN_SETUP | USE_PAGER },
-       { "read-tree", cmd_read_tree, RUN_SETUP | SUPPORT_SUPER_PREFIX},
+       { "read-tree", cmd_read_tree, RUN_SETUP },
        { "rebase", cmd_rebase, RUN_SETUP | NEED_WORK_TREE },
        { "receive-pack", cmd_receive_pack },
        { "reflog", cmd_reflog, RUN_SETUP },
@@ -610,7 +588,7 @@ static struct cmd_struct commands[] = {
        { "stash", cmd_stash, RUN_SETUP | NEED_WORK_TREE },
        { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
        { "stripspace", cmd_stripspace },
-       { "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX },
+       { "submodule--helper", cmd_submodule__helper, RUN_SETUP },
        { "switch", cmd_switch, RUN_SETUP | NEED_WORK_TREE },
        { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
        { "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
@@ -727,9 +705,6 @@ static void execv_dashed_external(const char **argv)
        struct child_process cmd = CHILD_PROCESS_INIT;
        int status;
 
-       if (get_super_prefix())
-               die(_("%s doesn't support --super-prefix"), argv[0]);
-
        if (use_pager == -1 && !is_builtin(argv[0]))
                use_pager = check_pager_config(argv[0]);
        commit_pager_choice();
@@ -799,9 +774,6 @@ static int run_argv(int *argcp, const char ***argv)
                         */
                        trace2_cmd_name("_run_git_alias_");
 
-                       if (get_super_prefix())
-                               die("%s doesn't support --super-prefix", **argv);
-
                        commit_pager_choice();
 
                        strvec_push(&cmd.args, "git");
index f877a1ea56460de8e7c831b58ab4cfbda837a304..5cd66d3a7864a443ed74ce0ae9e08b31673047c2 100644 (file)
@@ -977,9 +977,13 @@ static int sign_buffer_gpg(struct strbuf *buffer, struct strbuf *signature,
                        break; /* found */
        }
        ret |= !cp;
+       if (ret) {
+               error(_("gpg failed to sign the data:\n%s"),
+                     gpg_status.len ? gpg_status.buf : "(no gpg output)");
+               strbuf_release(&gpg_status);
+               return -1;
+       }
        strbuf_release(&gpg_status);
-       if (ret)
-               return error(_("gpg failed to sign the data"));
 
        /* Strip CR from the line endings, in case we are on Windows. */
        remove_cr_after(signature, bottom);
@@ -998,6 +1002,7 @@ static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,
        char *ssh_signing_key_file = NULL;
        struct strbuf ssh_signature_filename = STRBUF_INIT;
        const char *literal_key = NULL;
+       int literal_ssh_key = 0;
 
        if (!signing_key || signing_key[0] == '\0')
                return error(
@@ -1005,6 +1010,7 @@ static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,
 
        if (is_literal_ssh_key(signing_key, &literal_key)) {
                /* A literal ssh key */
+               literal_ssh_key = 1;
                key_file = mks_tempfile_t(".git_signing_key_tmpXXXXXX");
                if (!key_file)
                        return error_errno(
@@ -1039,8 +1045,10 @@ static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,
                     "-Y", "sign",
                     "-n", "git",
                     "-f", ssh_signing_key_file,
-                    buffer_file->filename.buf,
                     NULL);
+       if (literal_ssh_key)
+               strvec_push(&signer.args, "-U");
+       strvec_push(&signer.args, buffer_file->filename.buf);
 
        sigchain_push(SIGPIPE, SIG_IGN);
        ret = pipe_command(&signer, NULL, 0, NULL, 0, &signer_stderr, 0);
diff --git a/grep.c b/grep.c
index 06eed694936c93a0b0d00c993f0a7c068a71916e..cee44a78d044dad174f5560b14432b396d6fdfb4 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -262,6 +262,31 @@ static void pcre2_free(void *pointer, MAYBE_UNUSED void *memory_data)
        free(pointer);
 }
 
+static int pcre2_jit_functional(void)
+{
+       static int jit_working = -1;
+       pcre2_code *code;
+       size_t off;
+       int err;
+
+       if (jit_working != -1)
+               return jit_working;
+
+       /*
+        * Try to JIT compile a simple pattern to probe if the JIT is
+        * working in general. It might fail for systems where creating
+        * memory mappings for runtime code generation is restricted.
+        */
+       code = pcre2_compile((PCRE2_SPTR)".", 1, 0, &err, &off, NULL);
+       if (!code)
+               return 0;
+
+       jit_working = pcre2_jit_compile(code, PCRE2_JIT_COMPLETE) == 0;
+       pcre2_code_free(code);
+
+       return jit_working;
+}
+
 static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt)
 {
        int error;
@@ -293,7 +318,7 @@ static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt
                options |= PCRE2_CASELESS;
        }
        if (!opt->ignore_locale && is_utf8_locale() && !literal)
-               options |= (PCRE2_UTF | PCRE2_MATCH_INVALID_UTF);
+               options |= (PCRE2_UTF | PCRE2_UCP | PCRE2_MATCH_INVALID_UTF);
 
 #ifndef GIT_PCRE2_VERSION_10_36_OR_HIGHER
        /* Work around https://bugs.exim.org/show_bug.cgi?id=2642 fixed in 10.36 */
@@ -317,8 +342,29 @@ static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt
        pcre2_config(PCRE2_CONFIG_JIT, &p->pcre2_jit_on);
        if (p->pcre2_jit_on) {
                jitret = pcre2_jit_compile(p->pcre2_pattern, PCRE2_JIT_COMPLETE);
-               if (jitret)
-                       die("Couldn't JIT the PCRE2 pattern '%s', got '%d'\n", p->pattern, jitret);
+               if (jitret == PCRE2_ERROR_NOMEMORY && !pcre2_jit_functional()) {
+                       /*
+                        * Even though pcre2_config(PCRE2_CONFIG_JIT, ...)
+                        * indicated JIT support, the library might still
+                        * fail to generate JIT code for various reasons,
+                        * e.g. when SELinux's 'deny_execmem' or PaX's
+                        * MPROTECT prevent creating W|X memory mappings.
+                        *
+                        * Instead of faling hard, fall back to interpreter
+                        * mode, just as if the pattern was prefixed with
+                        * '(*NO_JIT)'.
+                        */
+                       p->pcre2_jit_on = 0;
+                       return;
+               } else if (jitret) {
+                       int need_clip = p->patternlen > 64;
+                       int clip_len = need_clip ? 64 : p->patternlen;
+                       die("Couldn't JIT the PCRE2 pattern '%.*s'%s, got '%d'%s",
+                           clip_len, p->pattern, need_clip ? "..." : "", jitret,
+                           pcre2_jit_functional()
+                           ? "\nPerhaps prefix (*NO_JIT) to your pattern?"
+                           : "");
+               }
 
                /*
                 * The pcre2_config(PCRE2_CONFIG_JIT, ...) call just
@@ -769,11 +815,11 @@ static void free_pattern_expr(struct grep_expr *x)
        free(x);
 }
 
-void free_grep_patterns(struct grep_opt *opt)
+static void free_grep_pat(struct grep_pat *pattern)
 {
        struct grep_pat *p, *n;
 
-       for (p = opt->pattern_list; p; p = n) {
+       for (p = pattern; p; p = n) {
                n = p->next;
                switch (p->token) {
                case GREP_PATTERN: /* atom */
@@ -790,10 +836,15 @@ void free_grep_patterns(struct grep_opt *opt)
                }
                free(p);
        }
+}
 
-       if (!opt->pattern_expression)
-               return;
-       free_pattern_expr(opt->pattern_expression);
+void free_grep_patterns(struct grep_opt *opt)
+{
+       free_grep_pat(opt->pattern_list);
+       free_grep_pat(opt->header_list);
+
+       if (opt->pattern_expression)
+               free_pattern_expr(opt->pattern_expression);
 }
 
 static const char *end_of_line(const char *cp, unsigned long *left)
diff --git a/help.c b/help.c
index f1e090a4428f0e9e55b11a528e983d0908ffc70e..812af4cdea62129c12ec33813d934d677c61d714 100644 (file)
--- a/help.c
+++ b/help.c
@@ -563,7 +563,7 @@ static int git_unknown_cmd_config(const char *var, const char *value, void *cb)
        if (skip_prefix(var, "alias.", &p))
                add_cmdname(&aliases, p, strlen(p));
 
-       return git_default_config(var, value, cb);
+       return 0;
 }
 
 static int levenshtein_compare(const void *p1, const void *p2)
diff --git a/hook.c b/hook.c
index a4fa1031f287652eb734b276d9d6c98df40d9a08..1a8483186349b00608d40bc3d8702aa39ffb79c8 100644 (file)
--- a/hook.c
+++ b/hook.c
@@ -55,6 +55,11 @@ static int pick_next_hook(struct child_process *cp,
 
        cp->no_stdin = 1;
        strvec_pushv(&cp->env, hook_cb->options->env.v);
+       /* reopen the file for stdin; run_command closes it. */
+       if (hook_cb->options->path_to_stdin) {
+               cp->no_stdin = 0;
+               cp->in = xopen(hook_cb->options->path_to_stdin, O_RDONLY);
+       }
        cp->stdout_to_stderr = 1;
        cp->trace2_hook_name = hook_cb->hook_name;
        cp->dir = hook_cb->options->dir;
diff --git a/hook.h b/hook.h
index 4258b13da0d7c3c88a81b79eb25e8cc6a29dc6b6..19ab9a5806e1c1de11f7b300471c957fa5e6b4d4 100644 (file)
--- a/hook.h
+++ b/hook.h
@@ -30,6 +30,11 @@ struct run_hooks_opt
         * was invoked.
         */
        int *invoked_hook;
+
+       /**
+        * Path to file which should be piped to stdin for each hook.
+        */
+       const char *path_to_stdin;
 };
 
 #define RUN_HOOKS_OPT_INIT { \
index 6eb3b2fe51c6fe839f2dec5dd584cf174d5b5f73..8ab58e55f8524851fa77e047e9b9a5dfd31eb6bd 100644 (file)
@@ -759,10 +759,14 @@ int cmd_main(int argc, const char **argv)
                struct service_cmd *c = &services[i];
                regex_t re;
                regmatch_t out[1];
+               int ret;
 
                if (regcomp(&re, c->pattern, REG_EXTENDED))
                        die("Bogus regex in service table: %s", c->pattern);
-               if (!regexec(&re, dir, 1, out, 0)) {
+               ret = regexec(&re, dir, 1, out, 0);
+               regfree(&re);
+
+               if (!ret) {
                        size_t n;
 
                        if (strcmp(method, c->method))
@@ -774,7 +778,6 @@ int cmd_main(int argc, const char **argv)
                        dir[out[0].rm_so] = 0;
                        break;
                }
-               regfree(&re);
        }
 
        if (!cmd)
@@ -786,6 +789,7 @@ int cmd_main(int argc, const char **argv)
        if (!getenv("GIT_HTTP_EXPORT_ALL") &&
            access("git-daemon-export-ok", F_OK) )
                not_found(&hdr, "Repository not exported: '%s'", dir);
+       free(dir);
 
        http_config();
        max_request_buffer = git_env_ulong("GIT_HTTP_MAX_REQUEST_BUFFER",
@@ -795,5 +799,6 @@ int cmd_main(int argc, const char **argv)
                setenv(GIT_PROTOCOL_ENVIRONMENT, proto_header, 0);
 
        cmd->imp(&hdr, cmd_arg);
+       free(cmd_arg);
        return 0;
 }
index 31bc5c7767ce865bb3fb1a89f311f7d2b03c7e6d..258fec2068262c815ae68b08c8729b0bc60ee498 100644 (file)
@@ -5,6 +5,7 @@
 #include "walker.h"
 #include "strvec.h"
 #include "urlmatch.h"
+#include "trace2.h"
 
 static const char http_fetch_usage[] = "git http-fetch "
 "[-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin | --packfile=hash | commit-id] url";
@@ -137,6 +138,8 @@ int cmd_main(int argc, const char **argv)
        if (nongit)
                die(_("not a git repository"));
 
+       trace2_cmd_name("http-fetch");
+
        git_config(git_default_config, NULL);
 
        if (packfile) {
index 955a8a9535575fe0a397497d765f3e0e97334259..47bf0d6f1a253a01baf6e3d0f68dfa5d5df6a0f0 100644 (file)
@@ -135,7 +135,7 @@ static const char *find_funcname_matching_regexp(xdemitconf_t *xecfg, const char
 {
        int reg_error;
        regmatch_t match[1];
-       while (1) {
+       while (*start) {
                const char *bol, *eol;
                reg_error = regexec(regexp, start, 1, match, 0);
                if (reg_error == REG_NOMATCH)
@@ -148,8 +148,8 @@ static const char *find_funcname_matching_regexp(xdemitconf_t *xecfg, const char
                /* determine extent of line matched */
                bol = start+match[0].rm_so;
                eol = start+match[0].rm_eo;
-               while (bol > start && *bol != '\n')
-                       bol--;
+               while (bol > start && *--bol != '\n')
+                       ; /* nothing */
                if (*bol == '\n')
                        bol++;
                while (*eol && *eol != '\n')
@@ -161,6 +161,7 @@ static const char *find_funcname_matching_regexp(xdemitconf_t *xecfg, const char
                        return bol;
                start = eol;
        }
+       return NULL;
 }
 
 static const char *parse_range_funcname(
index 533966023804776ec19ca06c7e84b4b264c07314..ee01bcd2cc365c12c72bcd3f5597ecceecb2a7d9 100644 (file)
@@ -290,10 +290,6 @@ int opt_parse_list_objects_filter(const struct option *opt,
                                  const char *arg, int unset)
 {
        struct list_objects_filter_options *filter_options = opt->value;
-       opt_lof_init init = (opt_lof_init)opt->defval;
-
-       if (init)
-               filter_options = init(opt->value);
 
        if (unset || !arg)
                list_objects_filter_set_no_filter(filter_options);
index 7eeadab2dd0551c5eea8f7f14cd741841317230e..1fe393f447303a6d94abbd571a1ea83a1bfc7620 100644 (file)
@@ -111,27 +111,13 @@ void parse_list_objects_filter(
  * The opt->value to opt_parse_list_objects_filter() is either a
  * "struct list_objects_filter_option *" when using
  * OPT_PARSE_LIST_OBJECTS_FILTER().
- *
- * Or, if using no "struct option" field is used by the callback,
- * except the "defval" which is expected to be an "opt_lof_init"
- * function, which is called with the "opt->value" and must return a
- * pointer to the ""struct list_objects_filter_option *" to be used.
- *
- * The OPT_PARSE_LIST_OBJECTS_FILTER_INIT() can be used e.g. the
- * "struct list_objects_filter_option" is embedded in a "struct
- * rev_info", which the "defval" could be tasked with lazily
- * initializing. See cmd_pack_objects() for an example.
  */
 int opt_parse_list_objects_filter(const struct option *opt,
                                  const char *arg, int unset);
-typedef struct list_objects_filter_options *(*opt_lof_init)(void *);
-#define OPT_PARSE_LIST_OBJECTS_FILTER_INIT(fo, init) \
-       { OPTION_CALLBACK, 0, "filter", (fo), N_("args"), \
-         N_("object filtering"), 0, opt_parse_list_objects_filter, \
-         (intptr_t)(init) }
 
 #define OPT_PARSE_LIST_OBJECTS_FILTER(fo) \
-       OPT_PARSE_LIST_OBJECTS_FILTER_INIT((fo), NULL)
+       OPT_CALLBACK(0, "filter", (fo), N_("args"), \
+                    N_("object filtering"), opt_parse_list_objects_filter)
 
 /*
  * Translates abbreviated numbers in the filter's filter_spec into their
index b9543545ca4af96e062ec69546c354e318e4ad0d..7ed21cb299c126fb2d2107eb3e418a18e64fbc6c 100644 (file)
@@ -70,13 +70,13 @@ struct filter {
 };
 
 static enum list_objects_filter_result filter_blobs_none(
-       struct repository *r,
+       struct repository *r UNUSED,
        enum list_objects_filter_situation filter_situation,
        struct object *obj,
-       const char *pathname,
-       const char *filename,
+       const char *pathname UNUSED,
+       const char *filename UNUSED,
        struct oidset *omits,
-       void *filter_data_)
+       void *filter_data_ UNUSED)
 {
        switch (filter_situation) {
        default:
@@ -112,7 +112,7 @@ static enum list_objects_filter_result filter_blobs_none(
 }
 
 static void filter_blobs_none__init(
-       struct list_objects_filter_options *filter_options,
+       struct list_objects_filter_options *filter_options UNUSED,
        struct filter *filter)
 {
        filter->filter_object_fn = filter_blobs_none;
@@ -159,11 +159,11 @@ static int filter_trees_update_omits(
 }
 
 static enum list_objects_filter_result filter_trees_depth(
-       struct repository *r,
+       struct repository *r UNUSED,
        enum list_objects_filter_situation filter_situation,
        struct object *obj,
-       const char *pathname,
-       const char *filename,
+       const char *pathname UNUSED,
+       const char *filename UNUSED,
        struct oidset *omits,
        void *filter_data_)
 {
@@ -274,8 +274,8 @@ static enum list_objects_filter_result filter_blobs_limit(
        struct repository *r,
        enum list_objects_filter_situation filter_situation,
        struct object *obj,
-       const char *pathname,
-       const char *filename,
+       const char *pathname UNUSED,
+       const char *filename UNUSED,
        struct oidset *omits,
        void *filter_data_)
 {
@@ -514,6 +514,7 @@ static enum list_objects_filter_result filter_sparse(
 static void filter_sparse_free(void *filter_data)
 {
        struct filter_sparse_data *d = filter_data;
+       clear_pattern_list(&d->pl);
        free(d->array_frame);
        free(d);
 }
@@ -554,12 +555,12 @@ struct filter_object_type_data {
 };
 
 static enum list_objects_filter_result filter_object_type(
-       struct repository *r,
+       struct repository *r UNUSED,
        enum list_objects_filter_situation filter_situation,
        struct object *obj,
-       const char *pathname,
-       const char *filename,
-       struct oidset *omits,
+       const char *pathname UNUSED,
+       const char *filename UNUSED,
+       struct oidset *omits UNUSED,
        void *filter_data_)
 {
        struct filter_object_type_data *filter_data = filter_data_;
@@ -675,7 +676,7 @@ static enum list_objects_filter_result filter_combine(
        struct object *obj,
        const char *pathname,
        const char *filename,
-       struct oidset *omits,
+       struct oidset *omits UNUSED,
        void *filter_data)
 {
        struct combine_filter_data *d = filter_data;
index 250d9de41cb56072e95420530b203ce417191a2b..7528fe1db2c85c0b4d923854ef3ec53c3a8f1fad 100644 (file)
@@ -81,36 +81,6 @@ static void process_blob(struct traversal_context *ctx,
        strbuf_setlen(path, pathlen);
 }
 
-/*
- * Processing a gitlink entry currently does nothing, since
- * we do not recurse into the subproject.
- *
- * We *could* eventually add a flag that actually does that,
- * which would involve:
- *  - is the subproject actually checked out?
- *  - if so, see if the subproject has already been added
- *    to the alternates list, and add it if not.
- *  - process the commit (or tag) the gitlink points to
- *    recursively.
- *
- * However, it's unclear whether there is really ever any
- * reason to see superprojects and subprojects as such a
- * "unified" object pool (potentially resulting in a totally
- * humongous pack - avoiding which was the whole point of
- * having gitlinks in the first place!).
- *
- * So for now, there is just a note that we *could* follow
- * the link, and how to do it. Whether it necessarily makes
- * any sense what-so-ever to ever do that is another issue.
- */
-static void process_gitlink(struct traversal_context *ctx,
-                           const unsigned char *sha1,
-                           struct strbuf *path,
-                           const char *name)
-{
-       /* Nothing to do */
-}
-
 static void process_tree(struct traversal_context *ctx,
                         struct tree *tree,
                         struct strbuf *base,
@@ -149,8 +119,7 @@ static void process_tree_contents(struct traversal_context *ctx,
                        process_tree(ctx, t, base, entry.path);
                }
                else if (S_ISGITLINK(entry.mode))
-                       process_gitlink(ctx, entry.oid.hash,
-                                       base, entry.path);
+                       ; /* ignore gitlink */
                else {
                        struct blob *b = lookup_blob(ctx->revs->repo, &entry.oid);
                        if (!b) {
index 22a603e8af402d11fff97172d3f3ded3a9849cd4..130d26501c69cd47b762f13b46ff4835cfa6261a 100644 (file)
@@ -391,7 +391,7 @@ enum ll_merge_result ll_merge(mmbuffer_t *result_buf,
                normalize_file(theirs, path, istate);
        }
 
-       git_check_attr(istate, path, check);
+       git_check_attr(istate, NULL, path, check);
        ll_driver_name = check->items[0].value;
        if (check->items[1].value) {
                marker_size = atoi(check->items[1].value);
@@ -419,7 +419,7 @@ int ll_merge_marker_size(struct index_state *istate, const char *path)
 
        if (!check)
                check = attr_check_initl("conflict-marker-size", NULL);
-       git_check_attr(istate, path, check);
+       git_check_attr(istate, NULL, path, check);
        if (check->items[0].value) {
                marker_size = atoi(check->items[0].value);
                if (marker_size <= 0)
index fb6769742cd85ebe3782b071634d45922305f5d0..697d4beb8de43a4a7ae48be8284e10c6be2a9bfd 100644 (file)
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -194,8 +194,9 @@ int ls_refs(struct repository *r, struct packet_reader *request)
        send_possibly_unborn_head(&data);
        if (!data.prefixes.nr)
                strvec_push(&data.prefixes, "");
-       for_each_fullref_in_prefixes(get_git_namespace(), data.prefixes.v,
-                                    send_ref, &data);
+       refs_for_each_fullref_in_prefixes(get_main_ref_store(r),
+                                         get_git_namespace(), data.prefixes.v,
+                                         send_ref, &data);
        packet_fflush(stdout);
        strvec_clear(&data.prefixes);
        strbuf_release(&data.buf);
index 2fd0aa968751f7defa06a51148f75bb5838b44d4..ae469f8cc81d58a2be40be4e0365c715866012ec 100644 (file)
@@ -412,7 +412,7 @@ static int unpack_trees_start(struct merge_options *opt,
 {
        int rc;
        struct tree_desc t[3];
-       struct index_state tmp_index = { NULL };
+       struct index_state tmp_index = INDEX_STATE_INIT(opt->repo);
 
        memset(&opt->priv->unpack_opts, 0, sizeof(opt->priv->unpack_opts));
        if (opt->priv->call_depth)
index 26290554bb4c5f1d679194fe7d3f2b5c78a273e2..939865c1ae0566ba577861505f2ecf2e9fd19eeb 100644 (file)
@@ -33,6 +33,7 @@
 #include "object-store.h"
 #include "promisor-remote.h"
 #include "submodule.h"
+#include "fsck.h"
 
 /* The maximum size for an object header. */
 #define MAX_HEADER_LEN 32
@@ -1211,35 +1212,25 @@ static int quick_has_loose(struct repository *r,
 }
 
 /*
- * Map the loose object at "path" if it is not NULL, or the path found by
- * searching for a loose object named "oid".
+ * Map and close the given loose object fd. The path argument is used for
+ * error reporting.
  */
-static void *map_loose_object_1(struct repository *r, const char *path,
-                            const struct object_id *oid, unsigned long *size)
+static void *map_fd(int fd, const char *path, unsigned long *size)
 {
-       void *map;
-       int fd;
-
-       if (path)
-               fd = git_open(path);
-       else
-               fd = open_loose_object(r, oid, &path);
-       map = NULL;
-       if (fd >= 0) {
-               struct stat st;
+       void *map = NULL;
+       struct stat st;
 
-               if (!fstat(fd, &st)) {
-                       *size = xsize_t(st.st_size);
-                       if (!*size) {
-                               /* mmap() is forbidden on empty files */
-                               error(_("object file %s is empty"), path);
-                               close(fd);
-                               return NULL;
-                       }
-                       map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0);
+       if (!fstat(fd, &st)) {
+               *size = xsize_t(st.st_size);
+               if (!*size) {
+                       /* mmap() is forbidden on empty files */
+                       error(_("object file %s is empty"), path);
+                       close(fd);
+                       return NULL;
                }
-               close(fd);
+               map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0);
        }
+       close(fd);
        return map;
 }
 
@@ -1247,7 +1238,12 @@ void *map_loose_object(struct repository *r,
                       const struct object_id *oid,
                       unsigned long *size)
 {
-       return map_loose_object_1(r, NULL, oid, size);
+       const char *p;
+       int fd = open_loose_object(r, oid, &p);
+
+       if (fd < 0)
+               return NULL;
+       return map_fd(fd, p, size);
 }
 
 enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,
@@ -1427,7 +1423,9 @@ static int loose_object_info(struct repository *r,
                             struct object_info *oi, int flags)
 {
        int status = 0;
+       int fd;
        unsigned long mapsize;
+       const char *path;
        void *map;
        git_zstream stream;
        char hdr[MAX_HEADER_LEN];
@@ -1448,7 +1446,6 @@ static int loose_object_info(struct repository *r,
         * object even exists.
         */
        if (!oi->typep && !oi->type_name && !oi->sizep && !oi->contentp) {
-               const char *path;
                struct stat st;
                if (!oi->disk_sizep && (flags & OBJECT_INFO_QUICK))
                        return quick_has_loose(r, oid) ? 0 : -1;
@@ -1459,7 +1456,13 @@ static int loose_object_info(struct repository *r,
                return 0;
        }
 
-       map = map_loose_object(r, oid, &mapsize);
+       fd = open_loose_object(r, oid, &path);
+       if (fd < 0) {
+               if (errno != ENOENT)
+                       error_errno(_("unable to open loose object %s"), oid_to_hex(oid));
+               return -1;
+       }
+       map = map_fd(fd, path, &mapsize);
        if (!map)
                return -1;
 
@@ -1497,6 +1500,10 @@ static int loose_object_info(struct repository *r,
                break;
        }
 
+       if (status && (flags & OBJECT_INFO_DIE_IF_CORRUPT))
+               die(_("loose object %s (stored in %s) is corrupt"),
+                   oid_to_hex(oid), path);
+
        git_inflate_end(&stream);
 cleanup:
        munmap(map, mapsize);
@@ -1575,9 +1582,6 @@ static int do_oid_object_info_extended(struct repository *r,
                if (find_pack_entry(r, real, &e))
                        break;
 
-               if (flags & OBJECT_INFO_IGNORE_LOOSE)
-                       return -1;
-
                /* Most likely it's a loose object. */
                if (!loose_object_info(r, real, oi, flags))
                        return 0;
@@ -1609,6 +1613,15 @@ static int do_oid_object_info_extended(struct repository *r,
                        continue;
                }
 
+               if (flags & OBJECT_INFO_DIE_IF_CORRUPT) {
+                       const struct packed_git *p;
+                       if ((flags & OBJECT_INFO_LOOKUP_REPLACE) && !oideq(real, oid))
+                               die(_("replacement %s not found for %s"),
+                                   oid_to_hex(real), oid_to_hex(oid));
+                       if ((p = has_packed_and_bad(r, real)))
+                               die(_("packed object %s (stored in %s) is corrupt"),
+                                   oid_to_hex(real), p->pack_name);
+               }
                return -1;
        }
 
@@ -1659,21 +1672,6 @@ int oid_object_info(struct repository *r,
        return type;
 }
 
-static void *read_object(struct repository *r,
-                        const struct object_id *oid, enum object_type *type,
-                        unsigned long *size)
-{
-       struct object_info oi = OBJECT_INFO_INIT;
-       void *content;
-       oi.typep = type;
-       oi.sizep = size;
-       oi.contentp = &content;
-
-       if (oid_object_info_extended(r, oid, &oi, 0) < 0)
-               return NULL;
-       return content;
-}
-
 int pretend_object_file(void *buf, unsigned long len, enum object_type type,
                        struct object_id *oid)
 {
@@ -1695,46 +1693,25 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
 
 /*
  * This function dies on corrupt objects; the callers who want to
- * deal with them should arrange to call read_object() and give error
- * messages themselves.
+ * deal with them should arrange to call oid_object_info_extended() and give
+ * error messages themselves.
  */
-void *read_object_file_extended(struct repository *r,
-                               const struct object_id *oid,
-                               enum object_type *type,
-                               unsigned long *size,
-                               int lookup_replace)
+void *repo_read_object_file(struct repository *r,
+                           const struct object_id *oid,
+                           enum object_type *type,
+                           unsigned long *size)
 {
+       struct object_info oi = OBJECT_INFO_INIT;
+       unsigned flags = OBJECT_INFO_DIE_IF_CORRUPT | OBJECT_INFO_LOOKUP_REPLACE;
        void *data;
-       const struct packed_git *p;
-       const char *path;
-       struct stat st;
-       const struct object_id *repl = lookup_replace ?
-               lookup_replace_object(r, oid) : oid;
-
-       errno = 0;
-       data = read_object(r, repl, type, size);
-       if (data)
-               return data;
-
-       obj_read_lock();
-       if (errno && errno != ENOENT)
-               die_errno(_("failed to read object %s"), oid_to_hex(oid));
-
-       /* die if we replaced an object with one that does not exist */
-       if (repl != oid)
-               die(_("replacement %s not found for %s"),
-                   oid_to_hex(repl), oid_to_hex(oid));
-
-       if (!stat_loose_object(r, repl, &st, &path))
-               die(_("loose object %s (stored in %s) is corrupt"),
-                   oid_to_hex(repl), path);
 
-       if ((p = has_packed_and_bad(r, repl)))
-               die(_("packed object %s (stored in %s) is corrupt"),
-                   oid_to_hex(repl), p->pack_name);
-       obj_read_unlock();
+       oi.typep = type;
+       oi.sizep = size;
+       oi.contentp = &data;
+       if (oid_object_info_extended(r, oid, &oi, flags))
+               return NULL;
 
-       return NULL;
+       return data;
 }
 
 void *read_object_with_reference(struct repository *r,
@@ -1864,13 +1841,6 @@ out:
        return 0;
 }
 
-static int write_buffer(int fd, const void *buf, size_t len)
-{
-       if (write_in_full(fd, buf, len) < 0)
-               return error_errno(_("file write error"));
-       return 0;
-}
-
 static void hash_object_file_literally(const struct git_hash_algo *algo,
                                       const void *buf, unsigned long len,
                                       const char *type, struct object_id *oid)
@@ -2015,8 +1985,8 @@ static int write_loose_object_common(git_hash_ctx *c,
 
        ret = git_deflate(stream, flush ? Z_FINISH : 0);
        the_hash_algo->update_fn(c, in0, stream->next_in - in0);
-       if (write_buffer(fd, compressed, stream->next_out - compressed) < 0)
-               die(_("unable to write loose object file"));
+       if (write_in_full(fd, compressed, stream->next_out - compressed) < 0)
+               die_errno(_("unable to write loose object file"));
        stream->next_out = compressed;
        stream->avail_out = compressed_len;
 
@@ -2269,6 +2239,7 @@ int force_object_loose(const struct object_id *oid, time_t mtime)
 {
        void *buf;
        unsigned long len;
+       struct object_info oi = OBJECT_INFO_INIT;
        enum object_type type;
        char hdr[MAX_HEADER_LEN];
        int hdrlen;
@@ -2276,8 +2247,10 @@ int force_object_loose(const struct object_id *oid, time_t mtime)
 
        if (has_loose_object(oid))
                return 0;
-       buf = read_object(the_repository, oid, &type, &len);
-       if (!buf)
+       oi.typep = &type;
+       oi.sizep = &len;
+       oi.contentp = &buf;
+       if (oid_object_info_extended(the_repository, oid, &oi, 0))
                return error(_("cannot read object for %s"), oid_to_hex(oid));
        hdrlen = format_object_header(hdr, sizeof(hdr), type, len);
        ret = write_loose_object(oid, hdr, hdrlen, buf, len, mtime, 0);
@@ -2312,32 +2285,21 @@ int repo_has_object_file(struct repository *r,
        return repo_has_object_file_with_flags(r, oid, 0);
 }
 
-static void check_tree(const void *buf, size_t size)
-{
-       struct tree_desc desc;
-       struct name_entry entry;
-
-       init_tree_desc(&desc, buf, size);
-       while (tree_entry(&desc, &entry))
-               /* do nothing
-                * tree_entry() will die() on malformed entries */
-               ;
-}
-
-static void check_commit(const void *buf, size_t size)
-{
-       struct commit c;
-       memset(&c, 0, sizeof(c));
-       if (parse_commit_buffer(the_repository, &c, buf, size, 0))
-               die(_("corrupt commit"));
-}
-
-static void check_tag(const void *buf, size_t size)
-{
-       struct tag t;
-       memset(&t, 0, sizeof(t));
-       if (parse_tag_buffer(the_repository, &t, buf, size))
-               die(_("corrupt tag"));
+/*
+ * We can't use the normal fsck_error_function() for index_mem(),
+ * because we don't yet have a valid oid for it to report. Instead,
+ * report the minimal fsck error here, and rely on the caller to
+ * give more context.
+ */
+static int hash_format_check_report(struct fsck_options *opts,
+                                    const struct object_id *oid,
+                                    enum object_type object_type,
+                                    enum fsck_msg_type msg_type,
+                                    enum fsck_msg_id msg_id,
+                                    const char *message)
+{
+       error(_("object fails fsck: %s"), message);
+       return 1;
 }
 
 static int index_mem(struct index_state *istate,
@@ -2364,12 +2326,13 @@ static int index_mem(struct index_state *istate,
                }
        }
        if (flags & HASH_FORMAT_CHECK) {
-               if (type == OBJ_TREE)
-                       check_tree(buf, size);
-               if (type == OBJ_COMMIT)
-                       check_commit(buf, size);
-               if (type == OBJ_TAG)
-                       check_tag(buf, size);
+               struct fsck_options opts = FSCK_OPTIONS_DEFAULT;
+
+               opts.strict = 1;
+               opts.error_func = hash_format_check_report;
+               if (fsck_buffer(null_oid(), type, buf, size, &opts))
+                       die(_("refusing to create malformed object"));
+               fsck_finish(&opts);
        }
 
        if (write_object)
@@ -2792,13 +2755,16 @@ int read_loose_object(const char *path,
                      struct object_info *oi)
 {
        int ret = -1;
+       int fd;
        void *map = NULL;
        unsigned long mapsize;
        git_zstream stream;
        char hdr[MAX_HEADER_LEN];
        unsigned long *size = oi->sizep;
 
-       map = map_loose_object_1(the_repository, path, NULL, &mapsize);
+       fd = git_open(path);
+       if (fd >= 0)
+               map = map_fd(fd, path, &mapsize);
        if (!map) {
                error_errno(_("unable to mmap %s"), path);
                goto out;
index 1be57abaf10d7aa527df7f22ebaf660f6430d4d8..1a713d89d7c872bfd253c4a88bec6efa8ed597f8 100644 (file)
@@ -241,17 +241,10 @@ const char *loose_object_path(struct repository *r, struct strbuf *buf,
 void *map_loose_object(struct repository *r, const struct object_id *oid,
                       unsigned long *size);
 
-void *read_object_file_extended(struct repository *r,
-                               const struct object_id *oid,
-                               enum object_type *type,
-                               unsigned long *size, int lookup_replace);
-static inline void *repo_read_object_file(struct repository *r,
-                                         const struct object_id *oid,
-                                         enum object_type *type,
-                                         unsigned long *size)
-{
-       return read_object_file_extended(r, oid, type, size, 1);
-}
+void *repo_read_object_file(struct repository *r,
+                           const struct object_id *oid,
+                           enum object_type *type,
+                           unsigned long *size);
 #ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
 #define read_object_file(oid, type, size) repo_read_object_file(the_repository, oid, type, size)
 #endif
@@ -358,8 +351,7 @@ void assert_oid_type(const struct object_id *oid, enum object_type expect);
 /*
  * Enabling the object read lock allows multiple threads to safely call the
  * following functions in parallel: repo_read_object_file(), read_object_file(),
- * read_object_file_extended(), read_object_with_reference(), read_object(),
- * oid_object_info() and oid_object_info_extended().
+ * read_object_with_reference(), oid_object_info() and oid_object_info_extended().
  *
  * obj_read_lock() and obj_read_unlock() may also be used to protect other
  * section which cannot execute in parallel with object reading. Since the used
@@ -434,19 +426,20 @@ struct object_info {
 #define OBJECT_INFO_ALLOW_UNKNOWN_TYPE 2
 /* Do not retry packed storage after checking packed and loose storage */
 #define OBJECT_INFO_QUICK 8
-/* Do not check loose object */
-#define OBJECT_INFO_IGNORE_LOOSE 16
 /*
  * Do not attempt to fetch the object if missing (even if fetch_is_missing is
  * nonzero).
  */
-#define OBJECT_INFO_SKIP_FETCH_OBJECT 32
+#define OBJECT_INFO_SKIP_FETCH_OBJECT 16
 /*
  * This is meant for bulk prefetching of missing blobs in a partial
  * clone. Implies OBJECT_INFO_SKIP_FETCH_OBJECT and OBJECT_INFO_QUICK
  */
 #define OBJECT_INFO_FOR_PREFETCH (OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK)
 
+/* Die if object corruption (not just an object being missing) was detected. */
+#define OBJECT_INFO_DIE_IF_CORRUPT 32
+
 int oid_object_info_extended(struct repository *r,
                             const struct object_id *,
                             struct object_info *, unsigned flags);
index 682b852a46c1aa89c85c24c66660a1948b5c2184..344087de4de5184fcbfb599277180cec8f39c474 100644 (file)
--- a/object.c
+++ b/object.c
@@ -212,8 +212,7 @@ struct object *parse_object_buffer(struct repository *r, const struct object_id
        if (type == OBJ_BLOB) {
                struct blob *blob = lookup_blob(r, oid);
                if (blob) {
-                       if (parse_blob_buffer(blob, buffer, size))
-                               return NULL;
+                       parse_blob_buffer(blob);
                        obj = &blob->object;
                }
        } else if (type == OBJ_TREE) {
@@ -292,7 +291,7 @@ struct object *parse_object_with_flags(struct repository *r,
                        error(_("hash mismatch %s"), oid_to_hex(oid));
                        return NULL;
                }
-               parse_blob_buffer(lookup_blob(r, oid), NULL, 0);
+               parse_blob_buffer(lookup_blob(r, oid));
                return lookup_object(r, oid);
        }
 
index 440407f1be742c7e2a19a62f5adb968d409bcc29..d2a42abf28cc7246b3d076b1c855a350edcded70 100644 (file)
@@ -354,8 +354,8 @@ static int open_midx_bitmap_1(struct bitmap_index *bitmap_git,
        if (bitmap_git->pack || bitmap_git->midx) {
                struct strbuf buf = STRBUF_INIT;
                get_midx_filename(&buf, midx->object_dir);
-               /* ignore extra bitmap file; we can only handle one */
-               warning(_("ignoring extra bitmap file: '%s'"), buf.buf);
+               trace2_data_string("bitmap", the_repository,
+                                  "ignoring extra midx bitmap file", buf.buf);
                close(fd);
                strbuf_release(&buf);
                return -1;
@@ -411,9 +411,6 @@ static int open_pack_bitmap_1(struct bitmap_index *bitmap_git, struct packed_git
        struct stat st;
        char *bitmap_name;
 
-       if (open_pack_index(packfile))
-               return -1;
-
        bitmap_name = pack_bitmap_filename(packfile);
        fd = git_open(bitmap_name);
 
@@ -432,8 +429,8 @@ static int open_pack_bitmap_1(struct bitmap_index *bitmap_git, struct packed_git
        }
 
        if (bitmap_git->pack || bitmap_git->midx) {
-               /* ignore extra bitmap file; we can only handle one */
-               warning(_("ignoring extra bitmap file: '%s'"), packfile->pack_name);
+               trace2_data_string("bitmap", the_repository,
+                                  "ignoring extra bitmap file", packfile->pack_name);
                close(fd);
                return -1;
        }
@@ -458,6 +455,8 @@ static int open_pack_bitmap_1(struct bitmap_index *bitmap_git, struct packed_git
                return -1;
        }
 
+       trace2_data_string("bitmap", the_repository, "opened bitmap file",
+                          packfile->pack_name);
        return 0;
 }
 
@@ -525,11 +524,16 @@ static int open_pack_bitmap(struct repository *r,
        struct packed_git *p;
        int ret = -1;
 
-       assert(!bitmap_git->map);
-
        for (p = get_all_packs(r); p; p = p->next) {
-               if (open_pack_bitmap_1(bitmap_git, p) == 0)
+               if (open_pack_bitmap_1(bitmap_git, p) == 0) {
                        ret = 0;
+                       /*
+                        * The only reason to keep looking is to report
+                        * duplicates.
+                        */
+                       if (!trace2_is_enabled())
+                               break;
+               }
        }
 
        return ret;
@@ -553,11 +557,20 @@ static int open_midx_bitmap(struct repository *r,
 static int open_bitmap(struct repository *r,
                       struct bitmap_index *bitmap_git)
 {
+       int found;
+
        assert(!bitmap_git->map);
 
-       if (!open_midx_bitmap(r, bitmap_git))
-               return 0;
-       return open_pack_bitmap(r, bitmap_git);
+       found = !open_midx_bitmap(r, bitmap_git);
+
+       /*
+        * these will all be skipped if we opened a midx bitmap; but run it
+        * anyway if tracing is enabled to report the duplicates
+        */
+       if (!found || trace2_is_enabled())
+               found |= !open_pack_bitmap(r, bitmap_git);
+
+       return found ? 0 : -1;
 }
 
 struct bitmap_index *prepare_bitmap_git(struct repository *r)
index 00787e306db4c010ae68845f27ed69966fb9e250..336372974872882a235598d254c876e31c49b24a 100644 (file)
@@ -5,7 +5,6 @@
 #include "chunk-format.h"
 #include "pack-mtimes.h"
 #include "oidmap.h"
-#include "chunk-format.h"
 #include "pack-objects.h"
 
 void reset_pack_idx_option(struct pack_idx_option *opts)
index c0d7dd93f46dfbc07ea0f17839d4a2d1a9d43622..79e21ab18e7844461c3743e45ebb7ccc1022caea 100644 (file)
@@ -1650,22 +1650,6 @@ struct unpack_entry_stack_ent {
        unsigned long size;
 };
 
-static void *read_object(struct repository *r,
-                        const struct object_id *oid,
-                        enum object_type *type,
-                        unsigned long *size)
-{
-       struct object_info oi = OBJECT_INFO_INIT;
-       void *content;
-       oi.typep = type;
-       oi.sizep = size;
-       oi.contentp = &content;
-
-       if (oid_object_info_extended(r, oid, &oi, 0) < 0)
-               return NULL;
-       return content;
-}
-
 void *unpack_entry(struct repository *r, struct packed_git *p, off_t obj_offset,
                   enum object_type *final_type, unsigned long *final_size)
 {
@@ -1798,6 +1782,8 @@ void *unpack_entry(struct repository *r, struct packed_git *p, off_t obj_offset,
                        uint32_t pos;
                        struct object_id base_oid;
                        if (!(offset_to_pack_pos(p, obj_offset, &pos))) {
+                               struct object_info oi = OBJECT_INFO_INIT;
+
                                nth_packed_object_id(&base_oid, p,
                                                     pack_pos_to_index(p, pos));
                                error("failed to read delta base object %s"
@@ -1805,7 +1791,13 @@ void *unpack_entry(struct repository *r, struct packed_git *p, off_t obj_offset,
                                      oid_to_hex(&base_oid), (uintmax_t)obj_offset,
                                      p->pack_name);
                                mark_bad_packed_object(p, &base_oid);
-                               base = read_object(r, &base_oid, &type, &base_size);
+
+                               oi.typep = &type;
+                               oi.sizep = &base_size;
+                               oi.contentp = &base;
+                               if (oid_object_info_extended(r, &base_oid, &oi, 0) < 0)
+                                       base = NULL;
+
                                external_base = base;
                        }
                }
index a1ec932f0f9ff32e82223735dfd927473051fd3b..fd4743293fc48f9453499ad4f9695462b9c08217 100644 (file)
@@ -702,8 +702,7 @@ static struct option *preprocess_options(struct parse_opt_ctx_t *ctx,
        if (!nr_aliases)
                return NULL;
 
-       ALLOC_ARRAY(newopt, nr + 1);
-       COPY_ARRAY(newopt, options, nr + 1);
+       DUP_ARRAY(newopt, options, nr + 1);
 
        /* each alias has two string pointers and NULL */
        CALLOC_ARRAY(ctx->alias_groups, 3 * (nr_aliases + 1));
index b6ef86e0d15e3dc36364977dd3515fa118dee165..50d852f2991738c951bb447ce8206640aabb8a80 100644 (file)
@@ -369,6 +369,10 @@ int parse_opt_tracking_mode(const struct option *, const char *, int);
        { OPTION_CALLBACK, 0, "abbrev", (var), N_("n"), \
          N_("use <n> digits to display object names"), \
          PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 }
+#define OPT__SUPER_PREFIX(var) \
+       OPT_STRING_F(0, "super-prefix", (var), N_("prefix"), \
+               N_("prefixed path to initial superproject"), PARSE_OPT_HIDDEN)
+
 #define OPT__COLOR(var, h) \
        OPT_COLOR_FLAG(0, "color", (var), (h))
 #define OPT_COLUMN(s, l, v, h) \
index 46e77a85fee9d86e6e16e9f466ecfc3db893fd16..ab70fcbe613526dd09e59cb87d2393c95409d28d 100644 (file)
@@ -545,7 +545,7 @@ static void NORETURN unsupported_magic(const char *pattern,
        }
        /*
         * We may want to substitute "this command" with a command
-        * name. E.g. when add--interactive dies when running
+        * name. E.g. when "git add -p" or "git add -i" dies when running
         * "checkout -p"
         */
        die(_("%s: pathspec magic not supported by this command: %s"),
@@ -681,8 +681,7 @@ void copy_pathspec(struct pathspec *dst, const struct pathspec *src)
        int i, j;
 
        *dst = *src;
-       ALLOC_ARRAY(dst->items, dst->nr);
-       COPY_ARRAY(dst->items, src->items, dst->nr);
+       DUP_ARRAY(dst->items, src->items, dst->nr);
 
        for (i = 0; i < dst->nr; i++) {
                struct pathspec_item *d = &dst->items[i];
@@ -691,8 +690,7 @@ void copy_pathspec(struct pathspec *dst, const struct pathspec *src)
                d->match = xstrdup(s->match);
                d->original = xstrdup(s->original);
 
-               ALLOC_ARRAY(d->attr_match, d->attr_match_nr);
-               COPY_ARRAY(d->attr_match, s->attr_match, d->attr_match_nr);
+               DUP_ARRAY(d->attr_match, s->attr_match, d->attr_match_nr);
                for (j = 0; j < d->attr_match_nr; j++) {
                        const char *value = s->attr_match[j].value;
                        d->attr_match[j].value = xstrdup_or_null(value);
@@ -732,7 +730,7 @@ int match_pathspec_attrs(struct index_state *istate,
        if (name[namelen])
                name = to_free = xmemdupz(name, namelen);
 
-       git_check_attr(istate, name, item->attr_check);
+       git_check_attr(istate, NULL, name, item->attr_check);
 
        free(to_free);
 
index 8b7d81adc1be09d91a1551c370c08058766f76f6..086365dffb7e42de0158cfb9cd64c218571c2ba2 100644 (file)
@@ -269,14 +269,18 @@ static void find_exact_matches(struct string_list *a, struct string_list *b)
        hashmap_clear(&map);
 }
 
-static int diffsize_consume(void *data, char *line, unsigned long len)
+static int diffsize_consume(void *data,
+                            char *line UNUSED,
+                            unsigned long len UNUSED)
 {
        (*(int *)data)++;
        return 0;
 }
 
-static void diffsize_hunk(void *data, long ob, long on, long nb, long nn,
-                         const char *funcline, long funclen)
+static void diffsize_hunk(void *data,
+                         long ob UNUSED, long on UNUSED,
+                         long nb UNUSED, long nn UNUSED,
+                         const char *func UNUSED, long funclen UNUSED)
 {
        diffsize_consume(data, NULL, 0);
 }
@@ -379,11 +383,14 @@ static void output_pair_header(struct diff_options *diffopt,
        const char *color_new = diff_get_color_opt(diffopt, DIFF_FILE_NEW);
        const char *color_commit = diff_get_color_opt(diffopt, DIFF_COMMIT);
        const char *color;
+       char abbrev = diffopt->abbrev;
+
+       if (abbrev < 0)
+               abbrev = DEFAULT_ABBREV;
 
        if (!dashes->len)
                strbuf_addchars(dashes, '-',
-                               strlen(find_unique_abbrev(oid,
-                                                         DEFAULT_ABBREV)));
+                               strlen(find_unique_abbrev(oid, abbrev)));
 
        if (!b_util) {
                color = color_old;
@@ -405,7 +412,7 @@ static void output_pair_header(struct diff_options *diffopt,
                strbuf_addf(buf, "%*s:  %s ", patch_no_width, "-", dashes->buf);
        else
                strbuf_addf(buf, "%*d:  %s ", patch_no_width, a_util->i + 1,
-                           find_unique_abbrev(&a_util->oid, DEFAULT_ABBREV));
+                           find_unique_abbrev(&a_util->oid, abbrev));
 
        if (status == '!')
                strbuf_addf(buf, "%s%s", color_reset, color);
@@ -417,7 +424,7 @@ static void output_pair_header(struct diff_options *diffopt,
                strbuf_addf(buf, " %*s:  %s", patch_no_width, "-", dashes->buf);
        else
                strbuf_addf(buf, " %*d:  %s", patch_no_width, b_util->i + 1,
-                           find_unique_abbrev(&b_util->oid, DEFAULT_ABBREV));
+                           find_unique_abbrev(&b_util->oid, abbrev));
 
        commit = lookup_commit_reference(the_repository, oid);
        if (commit) {
@@ -461,7 +468,7 @@ static void patch_diff(const char *a, const char *b,
        diff_flush(diffopt);
 }
 
-static struct strbuf *output_prefix_cb(struct diff_options *opt, void *data)
+static struct strbuf *output_prefix_cb(struct diff_options *opt UNUSED, void *data)
 {
        return data;
 }
index 46f5e497b142a912ca27a19ad2defb6f50ba4f89..35e5657877c7a4ebd070654f7b252a99f05d702f 100644 (file)
@@ -488,11 +488,11 @@ int ie_modified(struct index_state *istate,
        return 0;
 }
 
-int base_name_compare(const char *name1, int len1, int mode1,
-                     const char *name2, int len2, int mode2)
+int base_name_compare(const char *name1, size_t len1, int mode1,
+                     const char *name2, size_t len2, int mode2)
 {
        unsigned char c1, c2;
-       int len = len1 < len2 ? len1 : len2;
+       size_t len = len1 < len2 ? len1 : len2;
        int cmp;
 
        cmp = memcmp(name1, name2, len);
@@ -517,11 +517,12 @@ int base_name_compare(const char *name1, int len1, int mode1,
  * This is used by routines that want to traverse the git namespace
  * but then handle conflicting entries together when possible.
  */
-int df_name_compare(const char *name1, int len1, int mode1,
-                   const char *name2, int len2, int mode2)
+int df_name_compare(const char *name1, size_t len1, int mode1,
+                   const char *name2, size_t len2, int mode2)
 {
-       int len = len1 < len2 ? len1 : len2, cmp;
        unsigned char c1, c2;
+       size_t len = len1 < len2 ? len1 : len2;
+       int cmp;
 
        cmp = memcmp(name1, name2, len);
        if (cmp)
@@ -1817,6 +1818,8 @@ static int verify_hdr(const struct cache_header *hdr, unsigned long size)
        git_hash_ctx c;
        unsigned char hash[GIT_MAX_RAWSZ];
        int hdr_version;
+       unsigned char *start, *end;
+       struct object_id oid;
 
        if (hdr->hdr_signature != htonl(CACHE_SIGNATURE))
                return error(_("bad signature 0x%08x"), hdr->hdr_signature);
@@ -1827,10 +1830,16 @@ static int verify_hdr(const struct cache_header *hdr, unsigned long size)
        if (!verify_index_checksum)
                return 0;
 
+       end = (unsigned char *)hdr + size;
+       start = end - the_hash_algo->rawsz;
+       oidread(&oid, start);
+       if (oideq(&oid, null_oid()))
+               return 0;
+
        the_hash_algo->init_fn(&c);
        the_hash_algo->update_fn(&c, hdr, size - the_hash_algo->rawsz);
        the_hash_algo->final_fn(hash, &c);
-       if (!hasheq(hash, (unsigned char *)hdr + size - the_hash_algo->rawsz))
+       if (!hasheq(hash, start))
                return error(_("bad index file sha1 signature"));
        return 0;
 }
@@ -2292,12 +2301,10 @@ static void set_new_index_sparsity(struct index_state *istate)
         * If the index's repo exists, mark it sparse according to
         * repo settings.
         */
-       if (istate->repo) {
-               prepare_repo_settings(istate->repo);
-               if (!istate->repo->settings.command_requires_full_index &&
-                   is_sparse_index_allowed(istate, 0))
-                       istate->sparse_index = 1;
-       }
+       prepare_repo_settings(istate->repo);
+       if (!istate->repo->settings.command_requires_full_index &&
+           is_sparse_index_allowed(istate, 0))
+               istate->sparse_index = 1;
 }
 
 /* remember to discard_cache() before reading a different cache! */
@@ -2322,8 +2329,6 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
        fd = open(path, O_RDONLY);
        if (fd < 0) {
                if (!must_exist && errno == ENOENT) {
-                       if (!istate->repo)
-                               istate->repo = the_repository;
                        set_new_index_sparsity(istate);
                        return 0;
                }
@@ -2425,9 +2430,6 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
        trace2_data_intmax("index", the_repository, "read/cache_nr",
                           istate->cache_nr);
 
-       if (!istate->repo)
-               istate->repo = the_repository;
-
        /*
         * If the command explicitly requires a full index, force it
         * to be full. Otherwise, correct the sparsity based on repository
@@ -2490,9 +2492,10 @@ int read_index_from(struct index_state *istate, const char *path,
 
        trace_performance_enter();
        if (split_index->base)
-               discard_index(split_index->base);
+               release_index(split_index->base);
        else
-               CALLOC_ARRAY(split_index->base, 1);
+               ALLOC_ARRAY(split_index->base, 1);
+       index_state_init(split_index->base, istate->repo);
 
        base_oid_hex = oid_to_hex(&split_index->base_oid);
        base_path = xstrfmt("%s/sharedindex.%s", gitdir, base_oid_hex);
@@ -2531,7 +2534,13 @@ int is_index_unborn(struct index_state *istate)
        return (!istate->cache_nr && !istate->timestamp.sec);
 }
 
-void discard_index(struct index_state *istate)
+void index_state_init(struct index_state *istate, struct repository *r)
+{
+       struct index_state blank = INDEX_STATE_INIT(r);
+       memcpy(istate, &blank, sizeof(*istate));
+}
+
+void release_index(struct index_state *istate)
 {
        /*
         * Cache entries in istate->cache[] should have been allocated
@@ -2543,20 +2552,17 @@ void discard_index(struct index_state *istate)
        validate_cache_entries(istate);
 
        resolve_undo_clear_index(istate);
-       istate->cache_nr = 0;
-       istate->cache_changed = 0;
-       istate->timestamp.sec = 0;
-       istate->timestamp.nsec = 0;
        free_name_hash(istate);
        cache_tree_free(&(istate->cache_tree));
-       istate->initialized = 0;
-       istate->fsmonitor_has_run_once = 0;
-       FREE_AND_NULL(istate->fsmonitor_last_update);
-       FREE_AND_NULL(istate->cache);
-       istate->cache_alloc = 0;
+       free(istate->fsmonitor_last_update);
+       free(istate->cache);
        discard_split_index(istate);
        free_untracked_cache(istate->untracked);
-       istate->untracked = NULL;
+
+       if (istate->sparse_checkout_patterns) {
+               clear_pattern_list(istate->sparse_checkout_patterns);
+               FREE_AND_NULL(istate->sparse_checkout_patterns);
+       }
 
        if (istate->ce_mem_pool) {
                mem_pool_discard(istate->ce_mem_pool, should_validate_cache_entries());
@@ -2564,6 +2570,12 @@ void discard_index(struct index_state *istate)
        }
 }
 
+void discard_index(struct index_state *istate)
+{
+       release_index(istate);
+       index_state_init(istate, istate->repo);
+}
+
 /*
  * Validate the cache entries of this index.
  * All cache entries associated with this index
@@ -2915,9 +2927,13 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
        int ieot_entries = 1;
        struct index_entry_offset_table *ieot = NULL;
        int nr, nr_threads;
+       struct repository *r = istate->repo;
 
        f = hashfd(tempfile->fd, tempfile->filename.buf);
 
+       prepare_repo_settings(r);
+       f->skip_hash = r->settings.index_skip_hash;
+
        for (i = removed = extended = 0; i < entries; i++) {
                if (cache[i]->ce_flags & CE_REMOVE)
                        removed++;
index 9dc2cd14519d07fb976a3007c1d8d47fa9a9a549..f8203c6b05254b624cf98e6d4590d07064cbb101 100644 (file)
@@ -228,6 +228,22 @@ static int strbuf_addf_ret(struct strbuf *sb, int ret, const char *fmt, ...)
        return ret;
 }
 
+static int err_no_arg(struct strbuf *sb, const char *name)
+{
+       size_t namelen = strchrnul(name, ':') - name;
+       strbuf_addf(sb, _("%%(%.*s) does not take arguments"),
+                   (int)namelen, name);
+       return -1;
+}
+
+static int err_bad_arg(struct strbuf *sb, const char *name, const char *arg)
+{
+       size_t namelen = strchrnul(name, ':') - name;
+       strbuf_addf(sb, _("unrecognized %%(%.*s) argument: %s"),
+                   (int)namelen, name, arg);
+       return -1;
+}
+
 static int color_atom_parser(struct ref_format *format, struct used_atom *atom,
                             const char *color_value, struct strbuf *err)
 {
@@ -262,7 +278,7 @@ static int refname_atom_parser_internal(struct refname_atom *atom, const char *a
                if (strtol_i(arg, 10, &atom->rstrip))
                        return strbuf_addf_ret(err, -1, _("Integer value expected refname:rstrip=%s"), arg);
        } else
-               return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), name, arg);
+               return err_bad_arg(err, name, arg);
        return 0;
 }
 
@@ -317,7 +333,7 @@ static int objecttype_atom_parser(struct ref_format *format, struct used_atom *a
                                  const char *arg, struct strbuf *err)
 {
        if (arg)
-               return strbuf_addf_ret(err, -1, _("%%(objecttype) does not take arguments"));
+               return err_no_arg(err, "objecttype");
        if (*atom->name == '*')
                oi_deref.info.typep = &oi_deref.type;
        else
@@ -341,7 +357,7 @@ static int objectsize_atom_parser(struct ref_format *format, struct used_atom *a
                else
                        oi.info.disk_sizep = &oi.disk_size;
        } else
-               return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), "objectsize", arg);
+               return err_bad_arg(err, "objectsize", arg);
        return 0;
 }
 
@@ -349,7 +365,7 @@ static int deltabase_atom_parser(struct ref_format *format, struct used_atom *at
                                 const char *arg, struct strbuf *err)
 {
        if (arg)
-               return strbuf_addf_ret(err, -1, _("%%(deltabase) does not take arguments"));
+               return err_no_arg(err, "deltabase");
        if (*atom->name == '*')
                oi_deref.info.delta_base_oid = &oi_deref.delta_base_oid;
        else
@@ -361,7 +377,7 @@ static int body_atom_parser(struct ref_format *format, struct used_atom *atom,
                            const char *arg, struct strbuf *err)
 {
        if (arg)
-               return strbuf_addf_ret(err, -1, _("%%(body) does not take arguments"));
+               return err_no_arg(err, "body");
        atom->u.contents.option = C_BODY_DEP;
        return 0;
 }
@@ -374,7 +390,7 @@ static int subject_atom_parser(struct ref_format *format, struct used_atom *atom
        else if (!strcmp(arg, "sanitize"))
                atom->u.contents.option = C_SUB_SANITIZE;
        else
-               return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), "subject", arg);
+               return err_bad_arg(err, "subject", arg);
        return 0;
 }
 
@@ -428,7 +444,7 @@ static int contents_atom_parser(struct ref_format *format, struct used_atom *ato
                if (strtoul_ui(arg, 10, &atom->u.contents.nlines))
                        return strbuf_addf_ret(err, -1, _("positive value expected contents:lines=%s"), arg);
        } else
-               return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), "contents", arg);
+               return err_bad_arg(err, "contents", arg);
        return 0;
 }
 
@@ -440,7 +456,7 @@ static int raw_atom_parser(struct ref_format *format, struct used_atom *atom,
        else if (!strcmp(arg, "size"))
                atom->u.raw_data.option = RAW_LENGTH;
        else
-               return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), "raw", arg);
+               return err_bad_arg(err, "raw", arg);
        return 0;
 }
 
@@ -459,7 +475,7 @@ static int oid_atom_parser(struct ref_format *format, struct used_atom *atom,
                if (atom->u.oid.length < MINIMUM_ABBREV)
                        atom->u.oid.length = MINIMUM_ABBREV;
        } else
-               return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), atom->name, arg);
+               return err_bad_arg(err, atom->name, arg);
        return 0;
 }
 
@@ -473,7 +489,7 @@ static int person_email_atom_parser(struct ref_format *format, struct used_atom
        else if (!strcmp(arg, "localpart"))
                atom->u.email_option.option = EO_LOCALPART;
        else
-               return strbuf_addf_ret(err, -1, _("unrecognized email option: %s"), arg);
+               return err_bad_arg(err, atom->name, arg);
        return 0;
 }
 
@@ -557,7 +573,7 @@ static int if_atom_parser(struct ref_format *format, struct used_atom *atom,
        } else if (skip_prefix(arg, "notequals=", &atom->u.if_then_else.str)) {
                atom->u.if_then_else.cmp_status = COMPARE_UNEQUAL;
        } else
-               return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), "if", arg);
+               return err_bad_arg(err, "if", arg);
        return 0;
 }
 
@@ -565,14 +581,16 @@ static int rest_atom_parser(struct ref_format *format, struct used_atom *atom,
                            const char *arg, struct strbuf *err)
 {
        if (arg)
-               return strbuf_addf_ret(err, -1, _("%%(rest) does not take arguments"));
+               return err_no_arg(err, "rest");
        format->use_rest = 1;
        return 0;
 }
 
 static int head_atom_parser(struct ref_format *format, struct used_atom *atom,
-                           const char *arg, struct strbuf *unused_err)
+                           const char *arg, struct strbuf *err)
 {
+       if (arg)
+               return err_no_arg(err, "HEAD");
        atom->u.head = resolve_refdup("HEAD", RESOLVE_REF_READING, NULL, NULL);
        return 0;
 }
@@ -1191,7 +1209,7 @@ static const char *copy_name(const char *buf)
 {
        const char *cp;
        for (cp = buf; *cp && *cp != '\n'; cp++) {
-               if (!strncmp(cp, " <", 2))
+               if (starts_with(cp, " <"))
                        return xmemdupz(buf, cp - buf);
        }
        return xstrdup("");
@@ -1358,6 +1376,7 @@ static void find_subpos(const char *buf,
 
        /* parse signature first; we might not even have a subject line */
        parse_signature(buf, end - buf, &payload, &signature);
+       strbuf_release(&payload);
 
        /* skip past header until we hit empty line */
        while (*buf && *buf != '\n') {
@@ -2128,8 +2147,9 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
                return for_each_fullref_in("", cb, cb_data);
        }
 
-       return for_each_fullref_in_prefixes(NULL, filter->name_patterns,
-                                           cb, cb_data);
+       return refs_for_each_fullref_in_prefixes(get_main_ref_store(the_repository),
+                                                NULL, filter->name_patterns,
+                                                cb, cb_data);
 }
 
 /*
index 78e9350e20526a279fd5f5cedfb3d5a726edd481..04630f56ec6b3661ecb027fa0a8a5f49155bb3da 100644 (file)
--- a/reflog.c
+++ b/reflog.c
@@ -193,7 +193,6 @@ static void mark_reachable(struct expire_reflog_policy_cb *cb)
                        commit_list_insert(commit, &leftover);
                        continue;
                }
-               commit->object.flags |= REACHABLE;
                parent = commit->parents;
                while (parent) {
                        commit = parent->item;
@@ -371,6 +370,9 @@ void reflog_expiry_cleanup(void *cb_data)
                clear_commit_marks(cb->tip_commit, REACHABLE);
                break;
        }
+       for (elem = cb->mark_list; elem; elem = elem->next)
+               clear_commit_marks(elem->item, REACHABLE);
+       free_commit_list(cb->mark_list);
 }
 
 int count_reflog_ent(struct object_id *ooid UNUSED,
diff --git a/refs.c b/refs.c
index a5e546244de257c82f2708e718a417163b432483..aeae31c972f8c88084cc3d052329a3e23e60f1d4 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -1726,9 +1726,10 @@ static void find_longest_prefixes(struct string_list *out,
        strbuf_release(&prefix);
 }
 
-int for_each_fullref_in_prefixes(const char *namespace,
-                                const char **patterns,
-                                each_ref_fn fn, void *cb_data)
+int refs_for_each_fullref_in_prefixes(struct ref_store *ref_store,
+                                     const char *namespace,
+                                     const char **patterns,
+                                     each_ref_fn fn, void *cb_data)
 {
        struct string_list prefixes = STRING_LIST_INIT_DUP;
        struct string_list_item *prefix;
@@ -1743,7 +1744,7 @@ int for_each_fullref_in_prefixes(const char *namespace,
 
        for_each_string_list_item(prefix, &prefixes) {
                strbuf_addstr(&buf, prefix->string);
-               ret = for_each_fullref_in(buf.buf, fn, cb_data);
+               ret = refs_for_each_fullref_in(ref_store, buf.buf, fn, cb_data);
                if (ret)
                        break;
                strbuf_setlen(&buf, namespace_len);
diff --git a/refs.h b/refs.h
index 3266fd8f57d39938c2c496c8f869b14697c34c69..935cdd1ece3db3af483cb2225f204966c0e6cfa5 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -354,8 +354,10 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data);
  *
  * callers should be prepared to ignore references that they did not ask for.
  */
-int for_each_fullref_in_prefixes(const char *namespace, const char **patterns,
-                                each_ref_fn fn, void *cb_data);
+int refs_for_each_fullref_in_prefixes(struct ref_store *refs,
+                                     const char *namespace, const char **patterns,
+                                     each_ref_fn fn, void *cb_data);
+
 /**
  * iterate refs from the respective area.
  */
index c1c71d183ea3c0baf082b16cf18bc373a7615fe3..6f5a0709fba65e80081fdb04563019e1f1e15400 100644 (file)
@@ -1263,7 +1263,8 @@ static int write_with_updates(struct packed_ref_store *refs,
                goto error;
        }
 
-       if (fsync_component(FSYNC_COMPONENT_REFERENCE, get_tempfile_fd(refs->tempfile)) ||
+       if (fflush(out) ||
+           fsync_component(FSYNC_COMPONENT_REFERENCE, get_tempfile_fd(refs->tempfile)) ||
            close_tempfile_gently(refs->tempfile)) {
                strbuf_addf(err, "error closing file %s: %s",
                            get_tempfile_path(refs->tempfile),
index 1c4621b414bdc0372613e58c3b2133b84004d1e7..1ebbe42792ec67230b62dbbe4cb8ed9646576858 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -234,6 +234,11 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
                             const struct string_list *server_options,
                             int stateless_rpc);
 
+/* Used for protocol v2 in order to retrieve refs from a remote */
+struct bundle_list;
+int get_remote_bundle_uri(int fd_out, struct packet_reader *reader,
+                         struct bundle_list *bundles, int stateless_rpc);
+
 int resolve_remote_symref(struct ref *ref, struct ref *list);
 
 /*
index 3021921c53d22a29ff4a677fc0f569f4818e8244..3dbd3f0e2ec33df0753780f4d84605b16ebdc2b3 100644 (file)
@@ -47,6 +47,7 @@ void prepare_repo_settings(struct repository *r)
        }
        if (manyfiles) {
                r->settings.index_version = 4;
+               r->settings.index_skip_hash = 1;
                r->settings.core_untracked_cache = UNTRACKED_CACHE_WRITE;
        }
 
@@ -61,6 +62,7 @@ void prepare_repo_settings(struct repository *r)
        repo_cfg_bool(r, "pack.usesparse", &r->settings.pack_use_sparse, 1);
        repo_cfg_bool(r, "core.multipackindex", &r->settings.core_multi_pack_index, 1);
        repo_cfg_bool(r, "index.sparse", &r->settings.sparse_index, 0);
+       repo_cfg_bool(r, "index.skiphash", &r->settings.index_skip_hash, r->settings.index_skip_hash);
 
        /*
         * The GIT_TEST_MULTI_PACK_INDEX variable is special in that
index 3427085fd6d2e44fa32c3e0ebc4ffba7a4c8286f..937fa974b38e86a1873a0e6904cd4c35ac5fd8f1 100644 (file)
@@ -28,6 +28,8 @@ void initialize_the_repository(void)
        the_repo.remote_state = remote_state_new();
        the_repo.parsed_objects = parsed_object_pool_new();
 
+       index_state_init(&the_index, the_repository);
+
        repo_set_hash_algo(&the_repo, GIT_HASH_SHA1);
 }
 
@@ -302,14 +304,13 @@ int repo_read_index(struct repository *repo)
 {
        int res;
 
-       if (!repo->index)
-               CALLOC_ARRAY(repo->index, 1);
-
        /* Complete the double-reference */
-       if (!repo->index->repo)
-               repo->index->repo = repo;
-       else if (repo->index->repo != repo)
+       if (!repo->index) {
+               ALLOC_ARRAY(repo->index, 1);
+               index_state_init(repo->index, repo);
+       } else if (repo->index->repo != repo) {
                BUG("repo's index should point back at itself");
+       }
 
        res = read_index_from(repo->index, repo->index_file, repo->gitdir);
 
index 6c461c5b9def017947c90bd84140ae48556e252a..e8c67ffe16542425b3660b88f2dcf339a473369e 100644 (file)
@@ -42,6 +42,7 @@ struct repo_settings {
        struct fsmonitor_settings *fsmonitor; /* lazily loaded */
 
        int index_version;
+       int index_skip_hash;
        enum untracked_cache_setting core_untracked_cache;
 
        int pack_use_sparse;
index 439e34a7c51803335c4f5677f67058b633c46339..21f5f572c22ec7054da6bc20fef26ee2a59a19d5 100644 (file)
@@ -600,10 +600,12 @@ static struct commit *one_relevant_parent(const struct rev_info *revs,
 static int tree_difference = REV_TREE_SAME;
 
 static void file_add_remove(struct diff_options *options,
-                   int addremove, unsigned mode,
-                   const struct object_id *oid,
-                   int oid_valid,
-                   const char *fullpath, unsigned dirty_submodule)
+                   int addremove,
+                   unsigned mode UNUSED,
+                   const struct object_id *oid UNUSED,
+                   int oid_valid UNUSED,
+                   const char *fullpath UNUSED,
+                   unsigned dirty_submodule UNUSED)
 {
        int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;
        struct rev_info *revs = options->change_fn_data;
@@ -614,12 +616,15 @@ static void file_add_remove(struct diff_options *options,
 }
 
 static void file_change(struct diff_options *options,
-                unsigned old_mode, unsigned new_mode,
-                const struct object_id *old_oid,
-                const struct object_id *new_oid,
-                int old_oid_valid, int new_oid_valid,
-                const char *fullpath,
-                unsigned old_dirty_submodule, unsigned new_dirty_submodule)
+                unsigned old_mode UNUSED,
+                unsigned new_mode UNUSED,
+                const struct object_id *old_oid UNUSED,
+                const struct object_id *new_oid UNUSED,
+                int old_oid_valid UNUSED,
+                int new_oid_valid UNUSED,
+                const char *fullpath UNUSED,
+                unsigned old_dirty_submodule UNUSED,
+                unsigned new_dirty_submodule UNUSED)
 {
        tree_difference = REV_TREE_DIFFERENT;
        options->flags.has_changes = 1;
@@ -1808,7 +1813,7 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags)
        worktrees = get_worktrees();
        for (p = worktrees; *p; p++) {
                struct worktree *wt = *p;
-               struct index_state istate = { NULL };
+               struct index_state istate = INDEX_STATE_INIT(revs->repo);
 
                if (wt->is_current)
                        continue; /* current index already taken care of */
@@ -3054,6 +3059,7 @@ void release_revisions(struct rev_info *revs)
        date_mode_release(&revs->date_mode);
        release_revisions_mailmap(revs->mailmap);
        free_grep_patterns(&revs->grep_filter);
+       graph_clear(revs->graph);
        /* TODO (need to handle "no_free"): diff_free(&revs->diffopt) */
        diff_free(&revs->pruning);
        reflog_walk_info_release(revs->reflog_info);
index 48b9ba6d6f077e5729f3efc4c1a88ab2be508c36..6bd16acb0603b16a71c729978bba812326ca9400 100644 (file)
@@ -1019,7 +1019,7 @@ static void *run_thread(void *data)
                sigset_t mask;
                sigemptyset(&mask);
                sigaddset(&mask, SIGPIPE);
-               if (pthread_sigmask(SIG_BLOCK, &mask, NULL) < 0) {
+               if (pthread_sigmask(SIG_BLOCK, &mask, NULL)) {
                        ret = error("unable to block SIGPIPE in async thread");
                        return (void *)ret;
                }
@@ -1586,6 +1586,14 @@ static int pp_start_one(struct parallel_processes *pp,
        if (i == opts->processes)
                BUG("bookkeeping is hard");
 
+       /*
+        * By default, do not inherit stdin from the parent process - otherwise,
+        * all children would share stdin! Users may overwrite this to provide
+        * something to the child's stdin by having their 'get_next_task'
+        * callback assign 0 to .no_stdin and an appropriate integer to .in.
+        */
+       pp->children[i].process.no_stdin = 1;
+
        code = opts->get_next_task(&pp->children[i].process,
                                   opts->ungroup ? NULL : &pp->children[i].err,
                                   opts->data,
@@ -1601,7 +1609,6 @@ static int pp_start_one(struct parallel_processes *pp,
                pp->children[i].process.err = -1;
                pp->children[i].process.stdout_to_stderr = 1;
        }
-       pp->children[i].process.no_stdin = 1;
 
        if (start_command(&pp->children[i].process)) {
                if (opts->start_failure)
@@ -1632,9 +1639,7 @@ static void pp_buffer_stderr(struct parallel_processes *pp,
                             const struct run_process_parallel_opts *opts,
                             int output_timeout)
 {
-       int i;
-
-       while ((i = poll(pp->pfd, opts->processes, output_timeout) < 0)) {
+       while (poll(pp->pfd, opts->processes, output_timeout) < 0) {
                if (errno == EINTR)
                        continue;
                pp_cleanup(pp, opts);
@@ -1853,7 +1858,7 @@ enum start_bg_result start_bg_command(struct child_process *cmd,
                 *
                 * We also assume that `start_command()` does not add
                 * us to the cleanup list.  And that it calls
-                * calls `child_process_clear()`.
+                * `child_process_clear()`.
                 */
                sbgr = SBGR_ERROR;
                goto done;
index 6c52243cdf1f68bb3716891bfabbdf85fe26fcd4..ca19b95ce46cfdaa6f501eb487d4ba869fd0c5bd 100644 (file)
--- a/scalar.c
+++ b/scalar.c
@@ -143,6 +143,7 @@ static int set_recommended_config(int reconfigure)
                { "credential.validate", "false", 1 }, /* GCM4W-only */
                { "gc.auto", "0", 1 },
                { "gui.GCWarning", "false", 1 },
+               { "index.skipHash", "false", 1 },
                { "index.threads", "true", 1 },
                { "index.version", "4", 1 },
                { "merge.stat", "false", 1 },
@@ -261,7 +262,7 @@ static int register_dir(void)
                return error(_("could not set recommended config"));
 
        if (toggle_maintenance(1))
-               return error(_("could not turn on maintenance"));
+               warning(_("could not turn on maintenance"));
 
        if (have_fsmonitor_support() && start_fsmonitor_daemon()) {
                return error(_("could not start the FSMonitor daemon"));
@@ -404,7 +405,7 @@ void load_builtin_commands(const char *prefix, struct cmdnames *cmds)
 static int cmd_clone(int argc, const char **argv)
 {
        const char *branch = NULL;
-       int full_clone = 0, single_branch = 0;
+       int full_clone = 0, single_branch = 0, show_progress = isatty(2);
        struct option clone_options[] = {
                OPT_STRING('b', "branch", &branch, N_("<branch>"),
                           N_("branch to checkout after clone")),
@@ -499,7 +500,9 @@ static int cmd_clone(int argc, const char **argv)
        if (set_recommended_config(0))
                return error(_("could not configure '%s'"), dir);
 
-       if ((res = run_git("fetch", "--quiet", "origin", NULL))) {
+       if ((res = run_git("fetch", "--quiet",
+                               show_progress ? "--progress" : "--no-progress",
+                               "origin", NULL))) {
                warning(_("partial clone failed; attempting full clone"));
 
                if (set_config("remote.origin.promisor") ||
@@ -508,7 +511,9 @@ static int cmd_clone(int argc, const char **argv)
                        goto cleanup;
                }
 
-               if ((res = run_git("fetch", "--quiet", "origin", NULL)))
+               if ((res = run_git("fetch", "--quiet",
+                                       show_progress ? "--progress" : "--no-progress",
+                                       "origin", NULL)))
                        goto cleanup;
        }
 
index 9827e1adcbf0de181b7e3eea9a411d60effc2124..a7e6db4f78f60fc0d62fd8a333c0941085375be4 100644 (file)
@@ -36,7 +36,6 @@
 #include "rebase-interactive.h"
 #include "reset.h"
 #include "branch.h"
-#include "log-tree.h"
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
@@ -352,10 +351,25 @@ static const char *gpg_sign_opt_quoted(struct replay_opts *opts)
        return buf.buf;
 }
 
+void replay_opts_release(struct replay_opts *opts)
+{
+       free(opts->gpg_sign);
+       free(opts->reflog_action);
+       free(opts->default_strategy);
+       free(opts->strategy);
+       for (size_t i = 0; i < opts->xopts_nr; i++)
+               free(opts->xopts[i]);
+       free(opts->xopts);
+       strbuf_release(&opts->current_fixups);
+       if (opts->revs)
+               release_revisions(opts->revs);
+       free(opts->revs);
+}
+
 int sequencer_remove_state(struct replay_opts *opts)
 {
        struct strbuf buf = STRBUF_INIT;
-       int i, ret = 0;
+       int ret = 0;
 
        if (is_rebase_i(opts) &&
            strbuf_read_file(&buf, rebase_path_refs_to_delete(), 0) > 0) {
@@ -374,15 +388,6 @@ int sequencer_remove_state(struct replay_opts *opts)
                }
        }
 
-       free(opts->gpg_sign);
-       free(opts->reflog_action);
-       free(opts->default_strategy);
-       free(opts->strategy);
-       for (i = 0; i < opts->xopts_nr; i++)
-               free(opts->xopts[i]);
-       free(opts->xopts);
-       strbuf_release(&opts->current_fixups);
-
        strbuf_reset(&buf);
        strbuf_addstr(&buf, get_dir(opts));
        if (remove_dir_recursively(&buf, 0))
@@ -2272,8 +2277,10 @@ static int do_pick_commit(struct repository *r,
                reword = 1;
        else if (is_fixup(command)) {
                if (update_squash_messages(r, command, commit,
-                                          opts, item->flags))
-                       return -1;
+                                          opts, item->flags)) {
+                       res = -1;
+                       goto leave;
+               }
                flags |= AMEND_MSG;
                if (!final_fixup)
                        msg_file = rebase_path_squash_msg();
@@ -2283,9 +2290,11 @@ static int do_pick_commit(struct repository *r,
                } else {
                        const char *dest = git_path_squash_msg(r);
                        unlink(dest);
-                       if (copy_file(dest, rebase_path_squash_msg(), 0666))
-                               return error(_("could not rename '%s' to '%s'"),
-                                            rebase_path_squash_msg(), dest);
+                       if (copy_file(dest, rebase_path_squash_msg(), 0666)) {
+                               res = error(_("could not rename '%s' to '%s'"),
+                                           rebase_path_squash_msg(), dest);
+                               goto leave;
+                       }
                        unlink(git_path_merge_msg(r));
                        msg_file = dest;
                        flags |= EDIT_MSG;
@@ -2323,7 +2332,6 @@ static int do_pick_commit(struct repository *r,
                free_commit_list(common);
                free_commit_list(remotes);
        }
-       strbuf_release(&msgbuf);
 
        /*
         * If the merge was clean or if it failed due to conflict, we write
@@ -2397,6 +2405,7 @@ fast_forward_edit:
 leave:
        free_message(commit, &msg);
        free(author);
+       strbuf_release(&msgbuf);
        update_abort_safety_file();
 
        return res;
@@ -2478,6 +2487,34 @@ static int is_command(enum todo_command command, const char **bol)
                 (*bol = p));
 }
 
+static int check_label_or_ref_arg(enum todo_command command, const char *arg)
+{
+       switch (command) {
+       case TODO_LABEL:
+               /*
+                * '#' is not a valid label as the merge command uses it to
+                * separate merge parents from the commit subject.
+                */
+               if (!strcmp(arg, "#") ||
+                   check_refname_format(arg, REFNAME_ALLOW_ONELEVEL))
+                       return error(_("'%s' is not a valid label"), arg);
+               break;
+
+       case TODO_UPDATE_REF:
+               if (check_refname_format(arg, REFNAME_ALLOW_ONELEVEL))
+                       return error(_("'%s' is not a valid refname"), arg);
+               if (check_refname_format(arg, 0))
+                       return error(_("update-ref requires a fully qualified "
+                                      "refname e.g. refs/heads/%s"), arg);
+               break;
+
+       default:
+               BUG("unexpected todo_command");
+       }
+
+       return 0;
+}
+
 static int parse_insn_line(struct repository *r, struct todo_item *item,
                           const char *buf, const char *bol, char *eol)
 {
@@ -2526,10 +2563,19 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
 
        if (item->command == TODO_EXEC || item->command == TODO_LABEL ||
            item->command == TODO_RESET || item->command == TODO_UPDATE_REF) {
+               int ret = 0;
+
                item->commit = NULL;
                item->arg_offset = bol - buf;
                item->arg_len = (int)(eol - bol);
-               return 0;
+               if (item->command == TODO_LABEL ||
+                   item->command == TODO_UPDATE_REF) {
+                       saved = *eol;
+                       *eol = '\0';
+                       ret = check_label_or_ref_arg(item->command, bol);
+                       *eol = saved;
+               }
+               return ret;
        }
 
        if (item->command == TODO_FIXUP) {
@@ -2897,6 +2943,7 @@ static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
        strbuf_reset(buf);
        if (!read_oneliner(buf, rebase_path_strategy(), 0))
                return;
+       free(opts->strategy);
        opts->strategy = strbuf_detach(buf, NULL);
        if (!read_oneliner(buf, rebase_path_strategy_opts(), 0))
                return;
@@ -4834,8 +4881,7 @@ cleanup_head_ref:
                if (!stat(rebase_path_rewritten_list(), &st) &&
                                st.st_size > 0) {
                        struct child_process child = CHILD_PROCESS_INIT;
-                       const char *post_rewrite_hook =
-                               find_hook("post-rewrite");
+                       struct run_hooks_opt hook_opt = RUN_HOOKS_OPT_INIT;
 
                        child.in = open(rebase_path_rewritten_list(), O_RDONLY);
                        child.git_cmd = 1;
@@ -4845,18 +4891,9 @@ cleanup_head_ref:
                        /* we don't care if this copying failed */
                        run_command(&child);
 
-                       if (post_rewrite_hook) {
-                               struct child_process hook = CHILD_PROCESS_INIT;
-
-                               hook.in = open(rebase_path_rewritten_list(),
-                                       O_RDONLY);
-                               hook.stdout_to_stderr = 1;
-                               hook.trace2_hook_name = "post-rewrite";
-                               strvec_push(&hook.args, post_rewrite_hook);
-                               strvec_push(&hook.args, "rebase");
-                               /* we don't care if this hook failed */
-                               run_command(&hook);
-                       }
+                       hook_opt.path_to_stdin = rebase_path_rewritten_list();
+                       strvec_push(&hook_opt.args, "rebase");
+                       run_hooks_opt("post-rewrite", &hook_opt);
                }
                apply_autostash(rebase_path_autostash());
 
@@ -5745,8 +5782,8 @@ static void todo_list_add_exec_commands(struct todo_list *todo_list,
 
                base_items[i].command = TODO_EXEC;
                base_items[i].offset_in_buf = base_offset;
-               base_items[i].arg_offset = base_offset + strlen("exec ");
-               base_items[i].arg_len = command_len - strlen("exec ");
+               base_items[i].arg_offset = base_offset;
+               base_items[i].arg_len = command_len;
 
                base_offset += command_len + 1;
        }
index 888c18aad7188e48985eb5c994b13853dc30c137..3bcdfa1b5865fc5209141870076b7325edfa36d4 100644 (file)
@@ -158,6 +158,7 @@ int sequencer_pick_revisions(struct repository *repo,
 int sequencer_continue(struct repository *repo, struct replay_opts *opts);
 int sequencer_rollback(struct repository *repo, struct replay_opts *opts);
 int sequencer_skip(struct repository *repo, struct replay_opts *opts);
+void replay_opts_release(struct replay_opts *opts);
 int sequencer_remove_state(struct replay_opts *opts);
 
 #define TODO_LIST_KEEP_EMPTY (1U << 0)
diff --git a/serve.c b/serve.c
index 733347f602aa1ede3e45fee94050163790d65bf9..cbf4a143cfea9bd450d5d033b1ec051025af8a1f 100644 (file)
--- a/serve.c
+++ b/serve.c
@@ -7,6 +7,7 @@
 #include "protocol-caps.h"
 #include "serve.h"
 #include "upload-pack.h"
+#include "bundle-uri.h"
 
 static int advertise_sid = -1;
 static int client_hash_algo = GIT_HASH_SHA1;
@@ -135,6 +136,11 @@ static struct protocol_capability capabilities[] = {
                .advertise = always_advertise,
                .command = cap_object_info,
        },
+       {
+               .name = "bundle-uri",
+               .advertise = bundle_uri_advertise,
+               .command = bundle_uri_command,
+       },
 };
 
 void protocol_v2_advertise_capabilities(void)
index 8c269dab803fa9a06a0e31ff42b6a20ec1983dbc..147a13386a445b7278b87c5072d7d5c308a35b8f 100644 (file)
@@ -128,9 +128,6 @@ int is_sparse_index_allowed(struct index_state *istate, int flags)
        if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)
                return 0;
 
-       if (!istate->repo)
-               istate->repo = the_repository;
-
        if (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {
                int test_env;
 
@@ -299,7 +296,7 @@ void expand_index(struct index_state *istate, struct pattern_list *pl)
         * If the index is already full, then keep it full. We will convert
         * it to a sparse index on write, if possible.
         */
-       if (!istate || istate->sparse_index == INDEX_EXPANDED)
+       if (istate->sparse_index == INDEX_EXPANDED)
                return;
 
        /*
@@ -327,9 +324,6 @@ void expand_index(struct index_state *istate, struct pattern_list *pl)
                        pl = NULL;
        }
 
-       if (!istate->repo)
-               istate->repo = the_repository;
-
        /*
         * A NULL pattern set indicates we are expanding a full index, so
         * we use a special region name that indicates the full expansion.
@@ -424,6 +418,8 @@ void expand_index(struct index_state *istate, struct pattern_list *pl)
 
 void ensure_full_index(struct index_state *istate)
 {
+       if (!istate)
+               BUG("ensure_full_index() must get an index!");
        expand_index(istate, NULL);
 }
 
@@ -547,12 +543,9 @@ void expand_to_path(struct index_state *istate,
        if (in_expand_to_path)
                return;
 
-       if (!istate || !istate->sparse_index)
+       if (!istate->sparse_index)
                return;
 
-       if (!istate->repo)
-               istate->repo = the_repository;
-
        in_expand_to_path = 1;
 
        /*
index 9d0ccc30d00e35965f47d55253166f3d8ce142e2..5d0f04763ea2d201a3bbe417bd1951aea23a8436 100644 (file)
@@ -90,7 +90,8 @@ void move_cache_to_base_index(struct index_state *istate)
                mem_pool_combine(istate->ce_mem_pool, istate->split_index->base->ce_mem_pool);
        }
 
-       CALLOC_ARRAY(si->base, 1);
+       ALLOC_ARRAY(si->base, 1);
+       index_state_init(si->base, istate->repo);
        si->base->version = istate->version;
        /* zero timestamp disables racy test in ce_write_index() */
        si->base->timestamp = istate->timestamp;
index 0890b1405c5cc6888396bac8558549ebdb2e2977..c383f41a3c5ccc6c6228c4ed4f9631529633f878 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -1200,3 +1200,9 @@ int strbuf_edit_interactively(struct strbuf *buffer, const char *path,
        free(path2);
        return res;
 }
+
+void strbuf_strip_file_from_path(struct strbuf *sb)
+{
+       char *path_sep = find_last_dir_sep(sb->buf);
+       strbuf_setlen(sb, path_sep ? path_sep - sb->buf + 1 : 0);
+}
index 76965a17d444829b4a5b5edc099d12bf1fcc7ea3..f6dbb9681ee768221e707a39c37dc0a00f63b95d 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -664,6 +664,17 @@ int launch_sequence_editor(const char *path, struct strbuf *buffer,
 int strbuf_edit_interactively(struct strbuf *buffer, const char *path,
                              const char *const *env);
 
+/*
+ * Remove the filename from the provided path string. If the path
+ * contains a trailing separator, then the path is considered a directory
+ * and nothing is modified.
+ *
+ * Examples:
+ * - "/path/to/file" -> "/path/to/"
+ * - "/path/to/dir/" -> "/path/to/dir/"
+ */
+void strbuf_strip_file_from_path(struct strbuf *sb);
+
 void strbuf_add_lines(struct strbuf *sb,
                      const char *prefix,
                      const char *buf,
index 7b2f8b2b9384b8c9c516be005c9b9f811348b0c7..27841dc1d9e535df291a1005a7a005d65323dd1d 100644 (file)
@@ -38,7 +38,7 @@ struct git_istream {
 
        union {
                struct {
-                       char *buf; /* from read_object() */
+                       char *buf; /* from oid_object_info_extended() */
                        unsigned long read_ptr;
                } incore;
 
@@ -388,12 +388,17 @@ static ssize_t read_istream_incore(struct git_istream *st, char *buf, size_t sz)
 static int open_istream_incore(struct git_istream *st, struct repository *r,
                               const struct object_id *oid, enum object_type *type)
 {
-       st->u.incore.buf = read_object_file_extended(r, oid, type, &st->size, 0);
+       struct object_info oi = OBJECT_INFO_INIT;
+
        st->u.incore.read_ptr = 0;
        st->close = close_istream_incore;
        st->read = read_istream_incore;
 
-       return st->u.incore.buf ? 0 : -1;
+       oi.typep = type;
+       oi.sizep = &st->size;
+       oi.contentp = (void **)&st->u.incore.buf;
+       return oid_object_info_extended(r, oid, &oi,
+                                       OBJECT_INFO_DIE_IF_CORRUPT);
 }
 
 /*****************************************************************************
index 8ac2fca855d6c3615be2e5b8a7f16a4746441e1a..3a0dfc417c05b627a5100f518768a1b32ee22339 100644 (file)
@@ -832,7 +832,7 @@ static void changed_submodule_data_clear(struct changed_submodule_data *cs_data)
 }
 
 static void collect_changed_submodules_cb(struct diff_queue_struct *q,
-                                         struct diff_options *options,
+                                         struct diff_options *options UNUSED,
                                          void *data)
 {
        struct collect_changed_submodules_cb_data *me = data;
@@ -2054,14 +2054,6 @@ void submodule_unset_core_worktree(const struct submodule *sub)
        strbuf_release(&config_path);
 }
 
-static const char *get_super_prefix_or_empty(void)
-{
-       const char *s = get_super_prefix();
-       if (!s)
-               s = "";
-       return s;
-}
-
 static int submodule_has_dirty_index(const struct submodule *sub)
 {
        struct child_process cp = CHILD_PROCESS_INIT;
@@ -2080,7 +2072,7 @@ static int submodule_has_dirty_index(const struct submodule *sub)
        return finish_command(&cp);
 }
 
-static void submodule_reset_index(const char *path)
+static void submodule_reset_index(const char *path, const char *super_prefix)
 {
        struct child_process cp = CHILD_PROCESS_INIT;
        prepare_submodule_repo_env(&cp.env);
@@ -2089,10 +2081,10 @@ static void submodule_reset_index(const char *path)
        cp.no_stdin = 1;
        cp.dir = path;
 
-       strvec_pushf(&cp.args, "--super-prefix=%s%s/",
-                    get_super_prefix_or_empty(), path);
        /* TODO: determine if this might overwright untracked files */
        strvec_pushl(&cp.args, "read-tree", "-u", "--reset", NULL);
+       strvec_pushf(&cp.args, "--super-prefix=%s%s/",
+                    (super_prefix ? super_prefix : ""), path);
 
        strvec_push(&cp.args, empty_tree_oid_hex());
 
@@ -2105,10 +2097,9 @@ static void submodule_reset_index(const char *path)
  * For edge cases (a submodule coming into existence or removing a submodule)
  * pass NULL for old or new respectively.
  */
-int submodule_move_head(const char *path,
-                        const char *old_head,
-                        const char *new_head,
-                        unsigned flags)
+int submodule_move_head(const char *path, const char *super_prefix,
+                       const char *old_head, const char *new_head,
+                       unsigned flags)
 {
        int ret = 0;
        struct child_process cp = CHILD_PROCESS_INIT;
@@ -2145,7 +2136,8 @@ int submodule_move_head(const char *path,
        if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) {
                if (old_head) {
                        if (!submodule_uses_gitfile(path))
-                               absorb_git_dir_into_superproject(path);
+                               absorb_git_dir_into_superproject(path,
+                                                                super_prefix);
                } else {
                        struct strbuf gitdir = STRBUF_INIT;
                        submodule_name_to_gitdir(&gitdir, the_repository,
@@ -2154,7 +2146,7 @@ int submodule_move_head(const char *path,
                        strbuf_release(&gitdir);
 
                        /* make sure the index is clean as well */
-                       submodule_reset_index(path);
+                       submodule_reset_index(path, super_prefix);
                }
 
                if (old_head && (flags & SUBMODULE_MOVE_HEAD_FORCE)) {
@@ -2172,9 +2164,9 @@ int submodule_move_head(const char *path,
        cp.no_stdin = 1;
        cp.dir = path;
 
-       strvec_pushf(&cp.args, "--super-prefix=%s%s/",
-                    get_super_prefix_or_empty(), path);
        strvec_pushl(&cp.args, "read-tree", "--recurse-submodules", NULL);
+       strvec_pushf(&cp.args, "--super-prefix=%s%s/",
+                    (super_prefix ? super_prefix : ""), path);
 
        if (flags & SUBMODULE_MOVE_HEAD_DRY_RUN)
                strvec_push(&cp.args, "-n");
@@ -2274,7 +2266,8 @@ int validate_submodule_git_dir(char *git_dir, const char *submodule_name)
  * Embeds a single submodules git directory into the superprojects git dir,
  * non recursively.
  */
-static void relocate_single_git_dir_into_superproject(const char *path)
+static void relocate_single_git_dir_into_superproject(const char *path,
+                                                     const char *super_prefix)
 {
        char *old_git_dir = NULL, *real_old_git_dir = NULL, *real_new_git_dir = NULL;
        struct strbuf new_gitdir = STRBUF_INIT;
@@ -2304,7 +2297,7 @@ static void relocate_single_git_dir_into_superproject(const char *path)
        real_new_git_dir = real_pathdup(new_gitdir.buf, 1);
 
        fprintf(stderr, _("Migrating git directory of '%s%s' from\n'%s' to\n'%s'\n"),
-               get_super_prefix_or_empty(), path,
+               super_prefix ? super_prefix : "", path,
                real_old_git_dir, real_new_git_dir);
 
        relocate_gitdir(path, real_old_git_dir, real_new_git_dir);
@@ -2315,7 +2308,8 @@ static void relocate_single_git_dir_into_superproject(const char *path)
        strbuf_release(&new_gitdir);
 }
 
-static void absorb_git_dir_into_superproject_recurse(const char *path)
+static void absorb_git_dir_into_superproject_recurse(const char *path,
+                                                    const char *super_prefix)
 {
 
        struct child_process cp = CHILD_PROCESS_INIT;
@@ -2323,10 +2317,11 @@ static void absorb_git_dir_into_superproject_recurse(const char *path)
        cp.dir = path;
        cp.git_cmd = 1;
        cp.no_stdin = 1;
-       strvec_pushf(&cp.args, "--super-prefix=%s%s/",
-                    get_super_prefix_or_empty(), path);
        strvec_pushl(&cp.args, "submodule--helper",
                     "absorbgitdirs", NULL);
+       strvec_pushf(&cp.args, "--super-prefix=%s%s/", super_prefix ?
+                    super_prefix : "", path);
+
        prepare_submodule_repo_env(&cp.env);
        if (run_command(&cp))
                die(_("could not recurse into submodule '%s'"), path);
@@ -2337,7 +2332,8 @@ static void absorb_git_dir_into_superproject_recurse(const char *path)
  * having its git directory within the working tree to the git dir nested
  * in its superprojects git dir under modules/.
  */
-void absorb_git_dir_into_superproject(const char *path)
+void absorb_git_dir_into_superproject(const char *path,
+                                     const char *super_prefix)
 {
        int err_code;
        const char *sub_git_dir;
@@ -2379,14 +2375,14 @@ void absorb_git_dir_into_superproject(const char *path)
                char *real_common_git_dir = real_pathdup(get_git_common_dir(), 1);
 
                if (!starts_with(real_sub_git_dir, real_common_git_dir))
-                       relocate_single_git_dir_into_superproject(path);
+                       relocate_single_git_dir_into_superproject(path, super_prefix);
 
                free(real_sub_git_dir);
                free(real_common_git_dir);
        }
        strbuf_release(&gitdir);
 
-       absorb_git_dir_into_superproject_recurse(path);
+       absorb_git_dir_into_superproject_recurse(path, super_prefix);
 }
 
 int get_superproject_working_tree(struct strbuf *buf)
index b52a4ff1e73e3b137b7cd1a01e420c7f302497e6..c55a25ca37d2425d398f59c98ff177c8dfa6fdaf 100644 (file)
@@ -150,9 +150,8 @@ int validate_submodule_git_dir(char *git_dir, const char *submodule_name);
 
 #define SUBMODULE_MOVE_HEAD_DRY_RUN (1<<0)
 #define SUBMODULE_MOVE_HEAD_FORCE   (1<<1)
-int submodule_move_head(const char *path,
-                       const char *old,
-                       const char *new_head,
+int submodule_move_head(const char *path, const char *super_prefix,
+                       const char *old_head, const char *new_head,
                        unsigned flags);
 
 void submodule_unset_core_worktree(const struct submodule *sub);
@@ -164,7 +163,8 @@ void submodule_unset_core_worktree(const struct submodule *sub);
  */
 void prepare_submodule_repo_env(struct strvec *env);
 
-void absorb_git_dir_into_superproject(const char *path);
+void absorb_git_dir_into_superproject(const char *path,
+                                     const char *super_prefix);
 
 /*
  * Return the absolute path of the working tree of the superproject, which this
index 979b2d4833d983ef50a71c3a9394bd9c8b6e4051..29576c37488593d79978a50d405e107b25dbcc7c 100644 (file)
--- a/t/README
+++ b/t/README
@@ -449,10 +449,6 @@ the --sparse command-line argument.
 GIT_TEST_PRELOAD_INDEX=<boolean> exercises the preload-index code path
 by overriding the minimum number of cache entries required per thread.
 
-GIT_TEST_ADD_I_USE_BUILTIN=<boolean>, when false, disables the
-built-in version of git add -i. See 'add.interactive.useBuiltin' in
-git-config(1).
-
 GIT_TEST_INDEX_THREADS=<n> enables exercising the multi-threaded loading
 of the index for the whole test suite by bypassing the default number of
 cache entries and thread minimums. Setting this to 1 will make the
index 25afd3934284ddc8bc0fdca7cca514a742e44e7e..b18e7603103817b931acf6ab2a8cbbae8538bb8e 100644 (file)
@@ -3,6 +3,10 @@
 #include "bundle-uri.h"
 #include "strbuf.h"
 #include "string-list.h"
+#include "transport.h"
+#include "ref-filter.h"
+#include "remote.h"
+#include "refs.h"
 
 enum input_mode {
        KEY_VALUE_PAIRS,
@@ -36,6 +40,8 @@ static int cmd__bundle_uri_parse(int argc, const char **argv, enum input_mode mo
 
        init_bundle_list(&list);
 
+       list.baseURI = xstrdup("<uri>");
+
        switch (mode) {
        case KEY_VALUE_PAIRS:
                if (argc != 1)
@@ -68,6 +74,39 @@ usage:
        usage_with_options(usage, options);
 }
 
+static int cmd_ls_remote(int argc, const char **argv)
+{
+       const char *dest;
+       struct remote *remote;
+       struct transport *transport;
+       int status = 0;
+
+       dest = argc > 1 ? argv[1] : NULL;
+
+       remote = remote_get(dest);
+       if (!remote) {
+               if (dest)
+                       die(_("bad repository '%s'"), dest);
+               die(_("no remote configured to get bundle URIs from"));
+       }
+       if (!remote->url_nr)
+               die(_("remote '%s' has no configured URL"), dest);
+
+       transport = transport_get(remote, NULL);
+       if (transport_get_remote_bundle_uri(transport) < 0) {
+               error(_("could not get the bundle-uri list"));
+               status = 1;
+               goto cleanup;
+       }
+
+       print_bundle_list(stdout, transport->bundles);
+
+cleanup:
+       if (transport_disconnect(transport))
+               return 1;
+       return status;
+}
+
 int cmd__bundle_uri(int argc, const char **argv)
 {
        const char *usage[] = {
@@ -88,6 +127,8 @@ int cmd__bundle_uri(int argc, const char **argv)
                return cmd__bundle_uri_parse(argc - 1, argv + 1, KEY_VALUE_PAIRS);
        if (!strcmp(argv[1], "parse-config"))
                return cmd__bundle_uri_parse(argc - 1, argv + 1, CONFIG_FILE);
+       if (!strcmp(argv[1], "ls-remote"))
+               return cmd_ls_remote(argc - 1, argv + 1);
        error("there is no test-tool bundle-uri tool '%s'", argv[1]);
 
 usage:
index 92c4c2313e78a305dd0da1e7a853e58e325d2f84..b21bd672d9ff63d1cf29a9c7a2ad300f0e58c0c0 100644 (file)
@@ -11,9 +11,14 @@ static void report_error(const char *class, int ch)
 
 static int is_in(const char *s, int ch)
 {
-       /* We can't find NUL using strchr.  It's classless anyway. */
+       /*
+        * We can't find NUL using strchr. Accept it as the first
+        * character in the spec -- there are no empty classes.
+        */
        if (ch == '\0')
-               return 0;
+               return ch == *s;
+       if (*s == '\0')
+               s++;
        return !!strchr(s, ch);
 }
 
@@ -28,6 +33,20 @@ static int is_in(const char *s, int ch)
 #define DIGIT "0123456789"
 #define LOWER "abcdefghijklmnopqrstuvwxyz"
 #define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define PUNCT "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
+#define ASCII \
+       "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
+       "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
+       "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" \
+       "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" \
+       "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" \
+       "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" \
+       "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" \
+       "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
+#define CNTRL \
+       "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
+       "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
+       "\x7f"
 
 int cmd__ctype(int argc, const char **argv)
 {
@@ -38,6 +57,13 @@ int cmd__ctype(int argc, const char **argv)
        TEST_CLASS(is_glob_special, "*?[\\");
        TEST_CLASS(is_regex_special, "$()*+.?[\\^{|");
        TEST_CLASS(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~");
+       TEST_CLASS(isascii, ASCII);
+       TEST_CLASS(islower, LOWER);
+       TEST_CLASS(isupper, UPPER);
+       TEST_CLASS(iscntrl, CNTRL);
+       TEST_CLASS(ispunct, PUNCT);
+       TEST_CLASS(isxdigit, DIGIT "abcdefABCDEF");
+       TEST_CLASS(isprint, LOWER UPPER DIGIT PUNCT " ");
 
        return rc;
 }
index 659b6bfa81df6d0bcffb8d0a975492d54d79d6bc..6b297bd75361407fbba7cb6dcab0190d5ef5a1b2 100644 (file)
@@ -15,7 +15,7 @@ static const char *error_name(int error_number)
 
 /*
  * usage:
- * tool-test dir-iterator [--follow-symlinks] [--pedantic] directory_path
+ * tool-test dir-iterator [--pedantic] directory_path
  */
 int cmd__dir_iterator(int argc, const char **argv)
 {
@@ -24,9 +24,7 @@ int cmd__dir_iterator(int argc, const char **argv)
        int iter_status;
 
        for (++argv, --argc; *argv && starts_with(*argv, "--"); ++argv, --argc) {
-               if (strcmp(*argv, "--follow-symlinks") == 0)
-                       flags |= DIR_ITERATOR_FOLLOW_SYMLINKS;
-               else if (strcmp(*argv, "--pedantic") == 0)
+               if (strcmp(*argv, "--pedantic") == 0)
                        flags |= DIR_ITERATOR_PEDANTIC;
                else
                        die("invalid option '%s'", *argv);
similarity index 71%
rename from builtin/env--helper.c
rename to t/helper/test-env-helper.c
index ea04c166364fe5776cd196950c3ee44a866076d7..66c88b8ff3d311573db482035aa143a0c1ad136f 100644 (file)
@@ -1,9 +1,9 @@
-#include "builtin.h"
+#include "test-tool.h"
 #include "config.h"
 #include "parse-options.h"
 
 static char const * const env__helper_usage[] = {
-       N_("git env--helper --type=[bool|ulong] <options> <env-var>"),
+       "test-tool env-helper --type=[bool|ulong] <options> <env-var>",
        NULL
 };
 
@@ -24,12 +24,12 @@ static int option_parse_type(const struct option *opt, const char *arg,
        else if (!strcmp(arg, "ulong"))
                *cmdmode = ENV_HELPER_TYPE_ULONG;
        else
-               die(_("unrecognized --type argument, %s"), arg);
+               die("unrecognized --type argument, %s", arg);
 
        return 0;
 }
 
-int cmd_env__helper(int argc, const char **argv, const char *prefix)
+int cmd__env_helper(int argc, const char **argv)
 {
        int exit_code = 0;
        const char *env_variable = NULL;
@@ -39,17 +39,17 @@ int cmd_env__helper(int argc, const char **argv, const char *prefix)
        unsigned long ret_ulong, default_ulong;
        enum cmdmode cmdmode = 0;
        struct option opts[] = {
-               OPT_CALLBACK_F(0, "type", &cmdmode, N_("type"),
-                              N_("value is given this type"), PARSE_OPT_NONEG,
+               OPT_CALLBACK_F(0, "type", &cmdmode, "type",
+                              "value is given this type", PARSE_OPT_NONEG,
                               option_parse_type),
-               OPT_STRING(0, "default", &env_default, N_("value"),
-                          N_("default for git_env_*(...) to fall back on")),
+               OPT_STRING(0, "default", &env_default, "value",
+                          "default for git_env_*(...) to fall back on"),
                OPT_BOOL(0, "exit-code", &exit_code,
-                        N_("be quiet only use git_env_*() value as exit code")),
+                        "be quiet only use git_env_*() value as exit code"),
                OPT_END(),
        };
 
-       argc = parse_options(argc, argv, prefix, opts, env__helper_usage,
+       argc = parse_options(argc, argv, NULL, opts, env__helper_usage,
                             PARSE_OPT_KEEP_UNKNOWN_OPT);
        if (env_default && !*env_default)
                usage_with_options(env__helper_usage, opts);
@@ -64,7 +64,7 @@ int cmd_env__helper(int argc, const char **argv, const char *prefix)
                if (env_default) {
                        default_int = git_parse_maybe_bool(env_default);
                        if (default_int == -1) {
-                               error(_("option `--default' expects a boolean value with `--type=bool`, not `%s`"),
+                               error("option `--default' expects a boolean value with `--type=bool`, not `%s`",
                                      env_default);
                                usage_with_options(env__helper_usage, opts);
                        }
@@ -79,7 +79,7 @@ int cmd_env__helper(int argc, const char **argv, const char *prefix)
        case ENV_HELPER_TYPE_ULONG:
                if (env_default) {
                        if (!git_parse_ulong(env_default, &default_ulong)) {
-                               error(_("option `--default' expects an unsigned long value with `--type=ulong`, not `%s`"),
+                               error("option `--default' expects an unsigned long value with `--type=ulong`, not `%s`",
                                      env_default);
                                usage_with_options(env__helper_usage, opts);
                        }
index 2e576bcc11e9a62dc01206d46e01574ad47193d4..27323cb3672c35152e83e8741585a6c9520159f0 100644 (file)
@@ -17,6 +17,7 @@ int cmd_main(int argc, const char **argv)
        f = fopen(buf.buf, "w");
        if (!f)
                die("Could not write to %s", buf.buf);
+       strbuf_release(&buf);
        for (i = 0; i < argc; i++)
                fprintf(f, "%s%s", i > 0 ? " " : "", i > 0 ? argv[i] : "ssh:");
        fprintf(f, "\n");
index 8ca988d6216e7895d8afea772e110b7eeaa875d1..47af843b6816005f2370fb130bc9f2bde873c029 100644 (file)
@@ -17,15 +17,16 @@ int cmd__genzeros(int argc, const char **argv)
 
        /* Writing out individual NUL bytes is slow... */
        while (count < 0)
-               if (write(1, zeros, ARRAY_SIZE(zeros)) < 0)
-                       return -1;
+               if (xwrite(1, zeros, ARRAY_SIZE(zeros)) < 0)
+                       die_errno("write error");
 
        while (count > 0) {
-               n = write(1, zeros, count < ARRAY_SIZE(zeros) ?
-                         count : ARRAY_SIZE(zeros));
+               n = xwrite(1, zeros,
+                          count < ARRAY_SIZE(zeros)
+                          ? count : ARRAY_SIZE(zeros));
 
                if (n < 0)
-                       return -1;
+                       die_errno("write error");
 
                count -= n;
        }
index 7eb1a26a305b89c3d45954a2d41c2f3ad5f82196..abe8a785eb651017ce2336eed7fa3bda06d4658c 100644 (file)
@@ -28,6 +28,7 @@ static struct test_cmd cmds[] = {
        { "dump-fsmonitor", cmd__dump_fsmonitor },
        { "dump-split-index", cmd__dump_split_index },
        { "dump-untracked-cache", cmd__dump_untracked_cache },
+       { "env-helper", cmd__env_helper },
        { "example-decorate", cmd__example_decorate },
        { "fast-rebase", cmd__fast_rebase },
        { "fsmonitor-client", cmd__fsmonitor_client },
index 2e20a16eb82a115c09071b6a08887a4d11ab0761..ea2672436c9ab56ed544d0a3ff414c366de5fdc0 100644 (file)
@@ -22,6 +22,7 @@ int cmd__dump_fsmonitor(int argc, const char **argv);
 int cmd__dump_split_index(int argc, const char **argv);
 int cmd__dump_untracked_cache(int argc, const char **argv);
 int cmd__dump_reftable(int argc, const char **argv);
+int cmd__env_helper(int argc, const char **argv);
 int cmd__example_decorate(int argc, const char **argv);
 int cmd__fast_rebase(int argc, const char **argv);
 int cmd__fsmonitor_client(int argc, const char **argv);
index 3e0a2911d4f9baa889eef85877a6332b27015dfc..62f4481b6e4097db907806aa6330ae0b64793d5d 100644 (file)
@@ -68,7 +68,7 @@ generate_wrappers () {
        wrap_git .bin/git.a "$DIR_A" &&
        wrap_git .bin/git.b "$DIR_B" &&
        write_script .bin/git <<-\EOF &&
-       echo >&2 fatal: test tried to run generic git
+       echo >&2 fatal: test tried to run generic git: $*
        exit 1
        EOF
        PATH=$(pwd)/.bin:$PATH
diff --git a/t/lib-bundle-uri-protocol.sh b/t/lib-bundle-uri-protocol.sh
new file mode 100644 (file)
index 0000000..a4a1af8
--- /dev/null
@@ -0,0 +1,216 @@
+# Set up and run tests of the 'bundle-uri' command in protocol v2
+#
+# The test that includes this script should set BUNDLE_URI_PROTOCOL
+# to one of "file", "git", or "http".
+
+BUNDLE_URI_TEST_PARENT=
+BUNDLE_URI_TEST_URI=
+BUNDLE_URI_TEST_BUNDLE_URI=
+case "$BUNDLE_URI_PROTOCOL" in
+file)
+       BUNDLE_URI_PARENT=file_parent
+       BUNDLE_URI_REPO_URI="file://$PWD/file_parent"
+       BUNDLE_URI_BUNDLE_URI="$BUNDLE_URI_REPO_URI/fake.bdl"
+       test_set_prereq BUNDLE_URI_FILE
+       ;;
+git)
+       . "$TEST_DIRECTORY"/lib-git-daemon.sh
+       start_git_daemon --export-all --enable=receive-pack
+       BUNDLE_URI_PARENT="$GIT_DAEMON_DOCUMENT_ROOT_PATH/parent"
+       BUNDLE_URI_REPO_URI="$GIT_DAEMON_URL/parent"
+       BUNDLE_URI_BUNDLE_URI="https://example.com/fake.bdl"
+       test_set_prereq BUNDLE_URI_GIT
+       ;;
+http)
+       . "$TEST_DIRECTORY"/lib-httpd.sh
+       start_httpd
+       BUNDLE_URI_PARENT="$HTTPD_DOCUMENT_ROOT_PATH/http_parent"
+       BUNDLE_URI_REPO_URI="$HTTPD_URL/smart/http_parent"
+       BUNDLE_URI_BUNDLE_URI="https://example.com/fake.bdl"
+       test_set_prereq BUNDLE_URI_HTTP
+       ;;
+*)
+       BUG "Need to pass valid BUNDLE_URI_PROTOCOL (was \"$BUNDLE_URI_PROTOCOL\")"
+       ;;
+esac
+
+test_expect_success "setup protocol v2 $BUNDLE_URI_PROTOCOL:// tests" '
+       git init "$BUNDLE_URI_PARENT" &&
+       test_commit -C "$BUNDLE_URI_PARENT" one &&
+       git -C "$BUNDLE_URI_PARENT" config uploadpack.advertiseBundleURIs true
+'
+
+case "$BUNDLE_URI_PROTOCOL" in
+http)
+       test_expect_success "setup config for $BUNDLE_URI_PROTOCOL:// tests" '
+               git -C "$BUNDLE_URI_PARENT" config http.receivepack true
+       '
+       ;;
+*)
+       ;;
+esac
+BUNDLE_URI_BUNDLE_URI_ESCAPED=$(echo "$BUNDLE_URI_BUNDLE_URI" | test_uri_escape)
+
+test_expect_success "connect with $BUNDLE_URI_PROTOCOL:// using protocol v2: no bundle-uri" '
+       test_when_finished "rm -f log" &&
+       test_when_finished "git -C \"$BUNDLE_URI_PARENT\" config uploadpack.advertiseBundleURIs true" &&
+       git -C "$BUNDLE_URI_PARENT" config uploadpack.advertiseBundleURIs false &&
+
+       GIT_TRACE_PACKET="$PWD/log" \
+       git \
+               -c protocol.version=2 \
+               ls-remote --symref "$BUNDLE_URI_REPO_URI" \
+               >actual 2>err &&
+
+       # Server responded using protocol v2
+       grep "< version 2" log &&
+
+       ! grep bundle-uri log
+'
+
+test_expect_success "connect with $BUNDLE_URI_PROTOCOL:// using protocol v2: have bundle-uri" '
+       test_when_finished "rm -f log" &&
+
+       GIT_TRACE_PACKET="$PWD/log" \
+       git \
+               -c protocol.version=2 \
+               ls-remote --symref "$BUNDLE_URI_REPO_URI" \
+               >actual 2>err &&
+
+       # Server responded using protocol v2
+       grep "< version 2" log &&
+
+       # Server advertised bundle-uri capability
+       grep "< bundle-uri" log
+'
+
+test_expect_success "clone with $BUNDLE_URI_PROTOCOL:// using protocol v2: request bundle-uris" '
+       test_when_finished "rm -rf log* cloned*" &&
+
+       GIT_TRACE_PACKET="$PWD/log" \
+       git \
+               -c transfer.bundleURI=false \
+               -c protocol.version=2 \
+               clone "$BUNDLE_URI_REPO_URI" cloned \
+               >actual 2>err &&
+
+       # Server responded using protocol v2
+       grep "< version 2" log &&
+
+       # Server advertised bundle-uri capability
+       grep "< bundle-uri" log &&
+
+       # Client did not issue bundle-uri command
+       ! grep "> command=bundle-uri" log &&
+
+       GIT_TRACE_PACKET="$PWD/log" \
+       git \
+               -c transfer.bundleURI=true \
+               -c protocol.version=2 \
+               clone "$BUNDLE_URI_REPO_URI" cloned2 \
+               >actual 2>err &&
+
+       # Server responded using protocol v2
+       grep "< version 2" log &&
+
+       # Server advertised bundle-uri capability
+       grep "< bundle-uri" log &&
+
+       # Client issued bundle-uri command
+       grep "> command=bundle-uri" log &&
+
+       GIT_TRACE_PACKET="$PWD/log3" \
+       git \
+               -c transfer.bundleURI=true \
+               -c protocol.version=2 \
+               clone --bundle-uri="$BUNDLE_URI_BUNDLE_URI" \
+               "$BUNDLE_URI_REPO_URI" cloned3 \
+               >actual 2>err &&
+
+       # Server responded using protocol v2
+       grep "< version 2" log3 &&
+
+       # Server advertised bundle-uri capability
+       grep "< bundle-uri" log3 &&
+
+       # Client did not issue bundle-uri command (--bundle-uri override)
+       ! grep "> command=bundle-uri" log3
+'
+
+# The remaining tests will all assume transfer.bundleURI=true
+#
+# This test can be removed when transfer.bundleURI is enabled by default.
+test_expect_success 'enable transfer.bundleURI for remaining tests' '
+       git config --global transfer.bundleURI true
+'
+
+test_expect_success "test bundle-uri with $BUNDLE_URI_PROTOCOL:// using protocol v2" '
+       test_config -C "$BUNDLE_URI_PARENT" \
+               bundle.only.uri "$BUNDLE_URI_BUNDLE_URI_ESCAPED" &&
+
+       # All data about bundle URIs
+       cat >expect <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+       [bundle "only"]
+               uri = $BUNDLE_URI_BUNDLE_URI_ESCAPED
+       EOF
+
+       test-tool bundle-uri \
+               ls-remote \
+               "$BUNDLE_URI_REPO_URI" \
+               >actual &&
+       test_cmp_config_output expect actual
+'
+
+test_expect_success "test bundle-uri with $BUNDLE_URI_PROTOCOL:// using protocol v2 and extra data" '
+       test_config -C "$BUNDLE_URI_PARENT" \
+               bundle.only.uri "$BUNDLE_URI_BUNDLE_URI_ESCAPED" &&
+
+       # Extra data should be ignored
+       test_config -C "$BUNDLE_URI_PARENT" bundle.only.extra bogus &&
+
+       # All data about bundle URIs
+       cat >expect <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+       [bundle "only"]
+               uri = $BUNDLE_URI_BUNDLE_URI_ESCAPED
+       EOF
+
+       test-tool bundle-uri \
+               ls-remote \
+               "$BUNDLE_URI_REPO_URI" \
+               >actual &&
+       test_cmp_config_output expect actual
+'
+
+test_expect_success "test bundle-uri with $BUNDLE_URI_PROTOCOL:// using protocol v2 with list" '
+       test_config -C "$BUNDLE_URI_PARENT" \
+               bundle.bundle1.uri "$BUNDLE_URI_BUNDLE_URI_ESCAPED-1.bdl" &&
+       test_config -C "$BUNDLE_URI_PARENT" \
+               bundle.bundle2.uri "$BUNDLE_URI_BUNDLE_URI_ESCAPED-2.bdl" &&
+       test_config -C "$BUNDLE_URI_PARENT" \
+               bundle.bundle3.uri "$BUNDLE_URI_BUNDLE_URI_ESCAPED-3.bdl" &&
+
+       # All data about bundle URIs
+       cat >expect <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+       [bundle "bundle1"]
+               uri = $BUNDLE_URI_BUNDLE_URI_ESCAPED-1.bdl
+       [bundle "bundle2"]
+               uri = $BUNDLE_URI_BUNDLE_URI_ESCAPED-2.bdl
+       [bundle "bundle3"]
+               uri = $BUNDLE_URI_BUNDLE_URI_ESCAPED-3.bdl
+       EOF
+
+       test-tool bundle-uri \
+               ls-remote \
+               "$BUNDLE_URI_REPO_URI" \
+               >actual &&
+       test_cmp_config_output expect actual
+'
index 8d1e408bb58f5e097f242d5c1fd2c7bb036deb6e..a8f5d3274a5a6007ca9acb3317752c6c1c7982a0 100644 (file)
@@ -105,10 +105,46 @@ index $file1..$file2 100644
  }
 EOF
 
+       cat >expect_diffstat <<EOF
+ file1 => file2 | 21 ++++++++++-----------
+ 1 file changed, 10 insertions(+), 11 deletions(-)
+EOF
+
        STRATEGY=$1
 
+       test_expect_success "$STRATEGY diff from attributes" '
+               echo "file* diff=driver" >.gitattributes &&
+               git config diff.driver.algorithm "$STRATEGY" &&
+               test_must_fail git diff --no-index file1 file2 > output &&
+               cat expect &&
+               cat output &&
+               test_cmp expect output
+       '
+
+       test_expect_success "$STRATEGY diff from attributes has valid diffstat" '
+               echo "file* diff=driver" >.gitattributes &&
+               git config diff.driver.algorithm "$STRATEGY" &&
+               test_must_fail git diff --stat --no-index file1 file2 > output &&
+               test_cmp expect_diffstat output
+       '
+
        test_expect_success "$STRATEGY diff" '
-               test_must_fail git diff --no-index "--$STRATEGY" file1 file2 > output &&
+               test_must_fail git diff --no-index "--diff-algorithm=$STRATEGY" file1 file2 > output &&
+               test_cmp expect output
+       '
+
+       test_expect_success "$STRATEGY diff command line precedence before attributes" '
+               echo "file* diff=driver" >.gitattributes &&
+               git config diff.driver.algorithm myers &&
+               test_must_fail git diff --no-index "--diff-algorithm=$STRATEGY" file1 file2 > output &&
+               test_cmp expect output
+       '
+
+       test_expect_success "$STRATEGY diff attributes precedence before config" '
+               git config diff.algorithm default &&
+               echo "file* diff=driver" >.gitattributes &&
+               git config diff.driver.algorithm "$STRATEGY" &&
+               test_must_fail git diff --no-index file1 file2 > output &&
                test_cmp expect output
        '
 
index 608949ea80b9a90cd00fcde239e7a39e21184f29..059fd74adb7b1b123a65bd5918115d141efb8201 100644 (file)
@@ -25,6 +25,7 @@
 #    LIB_HTTPD_DAV               enable DAV
 #    LIB_HTTPD_SVN               enable SVN at given location (e.g. "svn")
 #    LIB_HTTPD_SSL               enable SSL
+#    LIB_HTTPD_PROXY             enable proxy
 #
 # Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
 #
@@ -99,16 +100,19 @@ then
 fi
 
 HTTPD_VERSION=$($LIB_HTTPD_PATH -v | \
-       sed -n 's/^Server version: Apache\/\([0-9]*\)\..*$/\1/p; q')
+       sed -n 's/^Server version: Apache\/\([0-9.]*\).*$/\1/p; q')
+HTTPD_VERSION_MAJOR=$(echo $HTTPD_VERSION | cut -d. -f1)
+HTTPD_VERSION_MINOR=$(echo $HTTPD_VERSION | cut -d. -f2)
 
-if test -n "$HTTPD_VERSION"
+if test -n "$HTTPD_VERSION_MAJOR"
 then
        if test -z "$LIB_HTTPD_MODULE_PATH"
        then
-               if ! test $HTTPD_VERSION -ge 2
+               if ! test "$HTTPD_VERSION_MAJOR" -eq 2 ||
+                  ! test "$HTTPD_VERSION_MINOR" -ge 4
                then
                        test_skip_or_die GIT_TEST_HTTPD \
-                               "at least Apache version 2 is required"
+                               "at least Apache version 2.4 is required"
                fi
                if ! test -d "$DEFAULT_HTTPD_MODULE_PATH"
                then
@@ -130,6 +134,7 @@ install_script () {
 prepare_httpd() {
        mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH"
        cp "$TEST_PATH"/passwd "$HTTPD_ROOT_PATH"
+       cp "$TEST_PATH"/proxy-passwd "$HTTPD_ROOT_PATH"
        install_script incomplete-length-upload-pack-v2-http.sh
        install_script incomplete-body-upload-pack-v2-http.sh
        install_script error-no-report.sh
@@ -173,6 +178,11 @@ prepare_httpd() {
                        export LIB_HTTPD_SVN LIB_HTTPD_SVNPATH
                fi
        fi
+
+       if test -n "$LIB_HTTPD_PROXY"
+       then
+               HTTPD_PARA="$HTTPD_PARA -DPROXY"
+       fi
 }
 
 enable_http2 () {
index 0294739a77a24f9c005250cffb0cab86f0b113ff..e31293a45f144b6d3c02a5dc9dff13f05ba52e2b 100644 (file)
@@ -34,17 +34,6 @@ LoadModule http2_module modules/mod_http2.so
 Protocols h2c
 </IfDefine>
 
-<IfVersion < 2.4>
-LockFile accept.lock
-</IfVersion>
-
-<IfVersion < 2.1>
-<IfModule !mod_auth.c>
-       LoadModule auth_module modules/mod_auth.so
-</IfModule>
-</IfVersion>
-
-<IfVersion >= 2.1>
 <IfModule !mod_auth_basic.c>
        LoadModule auth_basic_module modules/mod_auth_basic.so
 </IfModule>
@@ -57,9 +46,23 @@ LockFile accept.lock
 <IfModule !mod_authz_host.c>
        LoadModule authz_host_module modules/mod_authz_host.so
 </IfModule>
-</IfVersion>
 
-<IfVersion >= 2.4>
+<IfDefine PROXY>
+<IfModule !mod_proxy.c>
+       LoadModule proxy_module modules/mod_proxy.so
+</IfModule>
+<IfModule !mod_proxy_http.c>
+       LoadModule proxy_http_module modules/mod_proxy_http.so
+</IfModule>
+ProxyRequests On
+<Proxy "*">
+       AuthType Basic
+       AuthName "proxy-auth"
+       AuthUserFile proxy-passwd
+       Require valid-user
+</Proxy>
+</IfDefine>
+
 <IfModule !mod_authn_core.c>
        LoadModule authn_core_module modules/mod_authn_core.so
 </IfModule>
@@ -83,7 +86,6 @@ LockFile accept.lock
        LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
 </IfModule>
 </IfDefine>
-</IfVersion>
 
 PassEnv GIT_VALGRIND
 PassEnv GIT_VALGRIND_OPTIONS
@@ -123,6 +125,10 @@ Alias /auth/dumb/ www/auth/dumb/
        Header set Set-Cookie name=value
 </LocationMatch>
 <LocationMatch /smart_headers/>
+       <RequireAll>
+               Require expr %{HTTP:x-magic-one} == 'abra'
+               Require expr %{HTTP:x-magic-two} == 'cadabra'
+       </RequireAll>
        SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
        SetEnv GIT_HTTP_EXPORT_ALL
 </LocationMatch>
@@ -205,18 +211,6 @@ RewriteRule ^/intern-redir/(.*)/foo$ /smart/$1 [PT]
 RewriteRule ^/redir-objects/(.*/info/refs)$ /dumb/$1 [PT]
 RewriteRule ^/redir-objects/(.*/objects/.*)$ /dumb/$1 [R=301]
 
-# Apache 2.2 does not understand <RequireAll>, so we use RewriteCond.
-# And as RewriteCond does not allow testing for non-matches, we match
-# the desired case first (one has abra, two has cadabra), and let it
-# pass by marking the RewriteRule as [L], "last rule, do not process
-# any other matching RewriteRules after this"), and then have another
-# RewriteRule that matches all other cases and lets them fail via '[F]',
-# "fail the request".
-RewriteCond %{HTTP:x-magic-one} =abra
-RewriteCond %{HTTP:x-magic-two} =cadabra
-RewriteRule ^/smart_headers/.* - [L]
-RewriteRule ^/smart_headers/.* - [F]
-
 <IfDefine SSL>
 LoadModule ssl_module modules/mod_ssl.so
 
@@ -225,7 +219,6 @@ SSLCertificateKeyFile httpd.pem
 SSLRandomSeed startup file:/dev/urandom 512
 SSLRandomSeed connect file:/dev/urandom 512
 SSLSessionCache none
-SSLMutex file:ssl_mutex
 SSLEngine On
 </IfDefine>
 
diff --git a/t/lib-httpd/proxy-passwd b/t/lib-httpd/proxy-passwd
new file mode 100644 (file)
index 0000000..77c2513
--- /dev/null
@@ -0,0 +1 @@
+proxuser:2x7tAukjAED5M
index 6dab2579cbf9658c3ac2bd55c8a66333d67eda47..812e8253f0eea30c2411fd06ba518351a8253f9a 100644 (file)
@@ -1,7 +1,7 @@
 RANDFILE                = $ENV::RANDFILE_PATH
 
 [ req ]
-default_bits            = 1024
+default_bits            = 2048
 distinguished_name      = req_distinguished_name
 prompt                  = no
 [ req_distinguished_name ]
diff --git a/t/perf/p7822-grep-perl-character.sh b/t/perf/p7822-grep-perl-character.sh
new file mode 100755 (executable)
index 0000000..87009c6
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description="git-grep's perl regex
+
+If GIT_PERF_GREP_THREADS is set to a list of threads (e.g. '1 4 8'
+etc.) we will test the patterns under those numbers of threads.
+"
+
+. ./perf-lib.sh
+
+test_perf_large_repo
+test_checkout_worktree
+
+if test -n "$GIT_PERF_GREP_THREADS"
+then
+       test_set_prereq PERF_GREP_ENGINES_THREADS
+fi
+
+for pattern in \
+       '\\bhow' \
+       '\\bÆvar' \
+       '\\d+ \\bÆvar' \
+       '\\bBelón\\b' \
+       '\\w{12}\\b'
+do
+       echo '$pattern' >pat
+       if ! test_have_prereq PERF_GREP_ENGINES_THREADS
+       then
+               test_perf "grep -P '$pattern'" --prereq PCRE "
+                       git -P grep -f pat || :
+               "
+       else
+               for threads in $GIT_PERF_GREP_THREADS
+               do
+                       test_perf "grep -P '$pattern' with $threads threads" --prereq PTHREADS,PCRE "
+                               git -c grep.threads=$threads -P grep -f pat || :
+                       "
+               done
+       fi
+done
+
+test_done
index 502b4bcf9ea0ad06cddaad50b999dd8024af28eb..8ea31d187a91ee4df52203cbd72a1fac7dd4140e 100755 (executable)
@@ -815,7 +815,8 @@ test_expect_success 'test_oid provides sane info by default' '
        grep "^00*\$" actual &&
        rawsz="$(test_oid rawsz)" &&
        hexsz="$(test_oid hexsz)" &&
-       test "$hexsz" -eq $(wc -c <actual) &&
+       # +1 accounts for the trailing newline
+       test $(( $hexsz + 1)) -eq $(wc -c <actual) &&
        test $(( $rawsz * 2)) -eq "$hexsz"
 '
 
@@ -826,7 +827,7 @@ test_expect_success 'test_oid can look up data for SHA-1' '
        grep "^00*\$" actual &&
        rawsz="$(test_oid rawsz)" &&
        hexsz="$(test_oid hexsz)" &&
-       test $(wc -c <actual) -eq 40 &&
+       test $(wc -c <actual) -eq 41 &&
        test "$rawsz" -eq 20 &&
        test "$hexsz" -eq 40
 '
@@ -838,7 +839,7 @@ test_expect_success 'test_oid can look up data for SHA-256' '
        grep "^00*\$" actual &&
        rawsz="$(test_oid rawsz)" &&
        hexsz="$(test_oid hexsz)" &&
-       test $(wc -c <actual) -eq 64 &&
+       test $(wc -c <actual) -eq 65 &&
        test "$rawsz" -eq 32 &&
        test "$hexsz" -eq 64
 '
index d0284fe2d7592d52a2aa9d6bfe38e342c94ec417..89b306cb114debb61e787e6d80417cd362cc367f 100755 (executable)
@@ -25,7 +25,15 @@ attr_check_quote () {
        git check-attr test -- "$path" >actual &&
        echo "\"$quoted_path\": test: $expect" >expect &&
        test_cmp expect actual
+}
+
+attr_check_source () {
+       path="$1" expect="$2" source="$3" git_opts="$4" &&
 
+       git $git_opts check-attr --source $source test -- "$path" >actual 2>err &&
+       echo "$path: test: $expect" >expect &&
+       test_cmp expect actual &&
+       test_must_be_empty err
 }
 
 test_expect_success 'open-quoted pathname' '
@@ -33,7 +41,6 @@ test_expect_success 'open-quoted pathname' '
        attr_check a unspecified
 '
 
-
 test_expect_success 'setup' '
        mkdir -p a/b/d a/c b &&
        (
@@ -80,12 +87,23 @@ test_expect_success 'setup' '
        EOF
 '
 
+test_expect_success 'setup branches' '
+       mkdir -p foo/bar &&
+       test_commit --printf "add .gitattributes" foo/bar/.gitattributes \
+               "f test=f\na/i test=n\n" tag-1 &&
+       test_commit --printf "add .gitattributes" foo/bar/.gitattributes \
+               "g test=g\na/i test=m\n" tag-2 &&
+       rm foo/bar/.gitattributes
+'
+
 test_expect_success 'command line checks' '
        test_must_fail git check-attr &&
        test_must_fail git check-attr -- &&
        test_must_fail git check-attr test &&
        test_must_fail git check-attr test -- &&
        test_must_fail git check-attr -- f &&
+       test_must_fail git check-attr --source &&
+       test_must_fail git check-attr --source not-a-valid-ref &&
        echo "f" | test_must_fail git check-attr --stdin &&
        echo "f" | test_must_fail git check-attr --stdin -- f &&
        echo "f" | test_must_fail git check-attr --stdin test -- f &&
@@ -203,9 +221,12 @@ test_expect_success 'attribute test: read paths from stdin' '
        test_cmp expect actual
 '
 
-test_expect_success 'attribute test: --all option' '
+test_expect_success 'setup --all option' '
        grep -v unspecified <expect-all | sort >specified-all &&
-       sed -e "s/:.*//" <expect-all | uniq >stdin-all &&
+       sed -e "s/:.*//" <expect-all | uniq >stdin-all
+'
+
+test_expect_success 'attribute test: --all option' '
        git check-attr --stdin --all <stdin-all >tmp &&
        sort tmp >actual &&
        test_cmp specified-all actual
@@ -284,6 +305,15 @@ test_expect_success 'using --git-dir and --work-tree' '
        )
 '
 
+test_expect_success 'using --source' '
+       attr_check_source foo/bar/f f tag-1 &&
+       attr_check_source foo/bar/a/i n tag-1 &&
+       attr_check_source foo/bar/f unspecified tag-2 &&
+       attr_check_source foo/bar/a/i m tag-2 &&
+       attr_check_source foo/bar/g g tag-2 &&
+       attr_check_source foo/bar/g unspecified tag-1
+'
+
 test_expect_success 'setup bare' '
        git clone --template= --bare . bare.git
 '
@@ -303,6 +333,18 @@ test_expect_success 'bare repository: check that .gitattribute is ignored' '
        )
 '
 
+test_expect_success 'bare repository: with --source' '
+       (
+               cd bare.git &&
+               attr_check_source foo/bar/f f tag-1 &&
+               attr_check_source foo/bar/a/i n tag-1 &&
+               attr_check_source foo/bar/f unspecified tag-2 &&
+               attr_check_source foo/bar/a/i m tag-2 &&
+               attr_check_source foo/bar/g g tag-2 &&
+               attr_check_source foo/bar/g unspecified tag-1
+       )
+'
+
 test_expect_success 'bare repository: check that --cached honors index' '
        (
                cd bare.git &&
@@ -400,7 +442,7 @@ test_expect_success 'large attributes line ignores trailing content in tree' '
 
 test_expect_success EXPENSIVE 'large attributes file ignored in tree' '
        test_when_finished "rm .gitattributes" &&
-       dd if=/dev/zero of=.gitattributes bs=101M count=1 2>/dev/null &&
+       dd if=/dev/zero of=.gitattributes bs=1048576 count=101 2>/dev/null &&
        git check-attr --all path >/dev/null 2>err &&
        echo "warning: ignoring overly large gitattributes file ${SQ}.gitattributes${SQ}" >expect &&
        test_cmp expect err
@@ -428,7 +470,7 @@ test_expect_success 'large attributes line ignores trailing content in index' '
 
 test_expect_success EXPENSIVE 'large attributes file ignored in index' '
        test_when_finished "git update-index --remove .gitattributes" &&
-       blob=$(dd if=/dev/zero bs=101M count=1 2>/dev/null | git hash-object -w --stdin) &&
+       blob=$(dd if=/dev/zero bs=1048576 count=101 2>/dev/null | git hash-object -w --stdin) &&
        git update-index --add --cacheinfo 100644,$blob,.gitattributes &&
        git check-attr --cached --all path >/dev/null 2>err &&
        echo "warning: ignoring overly large gitattributes blob ${SQ}.gitattributes${SQ}" >expect &&
index 2490162071e700e8a69d0e6311b6f22eda4e7046..e18b1602864ec432553266e89d3796e94741e8e0 100755 (executable)
@@ -88,6 +88,13 @@ check_parse 2008-02-14 bad
 check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 +0000'
 check_parse '2008-02-14 20:30:45 -0500' '2008-02-14 20:30:45 -0500'
 check_parse '2008.02.14 20:30:45 -0500' '2008-02-14 20:30:45 -0500'
+check_parse '20080214T20:30:45' '2008-02-14 20:30:45 +0000'
+check_parse '20080214T20:30' '2008-02-14 20:30:00 +0000'
+check_parse '20080214T20' '2008-02-14 20:00:00 +0000'
+check_parse '20080214T203045' '2008-02-14 20:30:45 +0000'
+check_parse '20080214T2030' '2008-02-14 20:30:00 +0000'
+check_parse '20080214T000000.20' '2008-02-14 00:00:00 +0000'
+check_parse '20080214T00:00:00.20' '2008-02-14 00:00:00 +0000'
 check_parse '20080214T203045-04:00' '2008-02-14 20:30:45 -0400'
 check_parse '20080214T203045 -04:00' '2008-02-14 20:30:45 -0400'
 check_parse '20080214T203045.019-04:00' '2008-02-14 20:30:45 -0400'
@@ -99,6 +106,7 @@ check_parse '2008-02-14 20:30:45 -05' '2008-02-14 20:30:45 -0500'
 check_parse '2008-02-14 20:30:45 -:30' '2008-02-14 20:30:45 +0000'
 check_parse '2008-02-14 20:30:45 -05:00' '2008-02-14 20:30:45 -0500'
 check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 -0500' EST5
+check_parse 'Thu, 7 Apr 2005 15:14:13 -0700' '2005-04-07 15:14:13 -0700'
 
 check_approxidate() {
        echo "$1 -> $2 +0000" >expect
index e56f4b9ac59572288f69cbd3554ad27df736c6b4..eeb8539c1bcb2df86a5aff1fa2c78fc555598d14 100755 (executable)
@@ -5,6 +5,12 @@ test_description='basic sanity checks for git var'
 TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
+sane_unset_all_editors () {
+       sane_unset GIT_EDITOR &&
+       sane_unset VISUAL &&
+       sane_unset EDITOR
+}
+
 test_expect_success 'get GIT_AUTHOR_IDENT' '
        test_tick &&
        echo "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE" >expect &&
@@ -47,6 +53,100 @@ test_expect_success 'get GIT_DEFAULT_BRANCH with configuration' '
        )
 '
 
+test_expect_success 'get GIT_EDITOR without configuration' '
+       (
+               sane_unset_all_editors &&
+               test_expect_code 1 git var GIT_EDITOR >out &&
+               test_must_be_empty out
+       )
+'
+
+test_expect_success 'get GIT_EDITOR with configuration' '
+       test_config core.editor foo &&
+       (
+               sane_unset_all_editors &&
+               echo foo >expect &&
+               git var GIT_EDITOR >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'get GIT_EDITOR with environment variable GIT_EDITOR' '
+       (
+               sane_unset_all_editors &&
+               echo bar >expect &&
+               GIT_EDITOR=bar git var GIT_EDITOR >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'get GIT_EDITOR with environment variable EDITOR' '
+       (
+               sane_unset_all_editors &&
+               echo bar >expect &&
+               EDITOR=bar git var GIT_EDITOR >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'get GIT_EDITOR with configuration and environment variable GIT_EDITOR' '
+       test_config core.editor foo &&
+       (
+               sane_unset_all_editors &&
+               echo bar >expect &&
+               GIT_EDITOR=bar git var GIT_EDITOR >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'get GIT_EDITOR with configuration and environment variable EDITOR' '
+       test_config core.editor foo &&
+       (
+               sane_unset_all_editors &&
+               echo foo >expect &&
+               EDITOR=bar git var GIT_EDITOR >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'get GIT_SEQUENCE_EDITOR without configuration' '
+       (
+               sane_unset GIT_SEQUENCE_EDITOR &&
+               git var GIT_EDITOR >expect &&
+               git var GIT_SEQUENCE_EDITOR >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'get GIT_SEQUENCE_EDITOR with configuration' '
+       test_config sequence.editor foo &&
+       (
+               sane_unset GIT_SEQUENCE_EDITOR &&
+               echo foo >expect &&
+               git var GIT_SEQUENCE_EDITOR >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'get GIT_SEQUENCE_EDITOR with environment variable' '
+       (
+               sane_unset GIT_SEQUENCE_EDITOR &&
+               echo bar >expect &&
+               GIT_SEQUENCE_EDITOR=bar git var GIT_SEQUENCE_EDITOR >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'get GIT_SEQUENCE_EDITOR with configuration and environment variable' '
+       test_config sequence.editor foo &&
+       (
+               sane_unset GIT_SEQUENCE_EDITOR &&
+               echo bar >expect &&
+               GIT_SEQUENCE_EDITOR=bar git var GIT_SEQUENCE_EDITOR >actual &&
+               test_cmp expect actual
+       )
+'
+
 # For git var -l, we check only a representative variable;
 # testing the whole output would make our test too brittle with
 # respect to unrelated changes in the test suite's environment.
index 2e42fba9567d5b35bf72eda214d336a7c3564566..fc14ba091cb247e5fc7395b0b89f615e43193c85 100755 (executable)
@@ -1,87 +1,87 @@
 #!/bin/sh
 
-test_description='test env--helper'
+test_description='test test-tool env-helper'
 
 TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 
-test_expect_success 'env--helper usage' '
-       test_must_fail git env--helper &&
-       test_must_fail git env--helper --type=bool &&
-       test_must_fail git env--helper --type=ulong &&
-       test_must_fail git env--helper --type=bool &&
-       test_must_fail git env--helper --type=bool --default &&
-       test_must_fail git env--helper --type=bool --default= &&
-       test_must_fail git env--helper --defaultxyz
+test_expect_success 'test-tool env-helper usage' '
+       test_must_fail test-tool env-helper &&
+       test_must_fail test-tool env-helper --type=bool &&
+       test_must_fail test-tool env-helper --type=ulong &&
+       test_must_fail test-tool env-helper --type=bool &&
+       test_must_fail test-tool env-helper --type=bool --default &&
+       test_must_fail test-tool env-helper --type=bool --default= &&
+       test_must_fail test-tool env-helper --defaultxyz
 '
 
-test_expect_success 'env--helper bad default values' '
-       test_must_fail git env--helper --type=bool --default=1xyz MISSING &&
-       test_must_fail git env--helper --type=ulong --default=1xyz MISSING
+test_expect_success 'test-tool env-helper bad default values' '
+       test_must_fail test-tool env-helper --type=bool --default=1xyz MISSING &&
+       test_must_fail test-tool env-helper --type=ulong --default=1xyz MISSING
 '
 
-test_expect_success 'env--helper --type=bool' '
+test_expect_success 'test-tool env-helper --type=bool' '
        # Test various --default bool values
        echo true >expected &&
-       git env--helper --type=bool --default=1 MISSING >actual &&
+       test-tool env-helper --type=bool --default=1 MISSING >actual &&
        test_cmp expected actual &&
-       git env--helper --type=bool --default=yes MISSING >actual &&
+       test-tool env-helper --type=bool --default=yes MISSING >actual &&
        test_cmp expected actual &&
-       git env--helper --type=bool --default=true MISSING >actual &&
+       test-tool env-helper --type=bool --default=true MISSING >actual &&
        test_cmp expected actual &&
        echo false >expected &&
-       test_must_fail git env--helper --type=bool --default=0 MISSING >actual &&
+       test_must_fail test-tool env-helper --type=bool --default=0 MISSING >actual &&
        test_cmp expected actual &&
-       test_must_fail git env--helper --type=bool --default=no MISSING >actual &&
+       test_must_fail test-tool env-helper --type=bool --default=no MISSING >actual &&
        test_cmp expected actual &&
-       test_must_fail git env--helper --type=bool --default=false MISSING >actual &&
+       test_must_fail test-tool env-helper --type=bool --default=false MISSING >actual &&
        test_cmp expected actual &&
 
        # No output with --exit-code
-       git env--helper --type=bool --default=true --exit-code MISSING >actual.out 2>actual.err &&
+       test-tool env-helper --type=bool --default=true --exit-code MISSING >actual.out 2>actual.err &&
        test_must_be_empty actual.out &&
        test_must_be_empty actual.err &&
-       test_must_fail git env--helper --type=bool --default=false --exit-code MISSING >actual.out 2>actual.err &&
+       test_must_fail test-tool env-helper --type=bool --default=false --exit-code MISSING >actual.out 2>actual.err &&
        test_must_be_empty actual.out &&
        test_must_be_empty actual.err &&
 
        # Existing variable
-       EXISTS=true git env--helper --type=bool --default=false --exit-code EXISTS >actual.out 2>actual.err &&
+       EXISTS=true test-tool env-helper --type=bool --default=false --exit-code EXISTS >actual.out 2>actual.err &&
        test_must_be_empty actual.out &&
        test_must_be_empty actual.err &&
        test_must_fail \
                env EXISTS=false \
-               git env--helper --type=bool --default=true --exit-code EXISTS >actual.out 2>actual.err &&
+               test-tool env-helper --type=bool --default=true --exit-code EXISTS >actual.out 2>actual.err &&
        test_must_be_empty actual.out &&
        test_must_be_empty actual.err
 '
 
-test_expect_success 'env--helper --type=ulong' '
+test_expect_success 'test-tool env-helper --type=ulong' '
        echo 1234567890 >expected &&
-       git env--helper --type=ulong --default=1234567890 MISSING >actual.out 2>actual.err &&
+       test-tool env-helper --type=ulong --default=1234567890 MISSING >actual.out 2>actual.err &&
        test_cmp expected actual.out &&
        test_must_be_empty actual.err &&
 
        echo 0 >expected &&
-       test_must_fail git env--helper --type=ulong --default=0 MISSING >actual &&
+       test_must_fail test-tool env-helper --type=ulong --default=0 MISSING >actual &&
        test_cmp expected actual &&
 
-       git env--helper --type=ulong --default=1234567890 --exit-code MISSING >actual.out 2>actual.err &&
+       test-tool env-helper --type=ulong --default=1234567890 --exit-code MISSING >actual.out 2>actual.err &&
        test_must_be_empty actual.out &&
        test_must_be_empty actual.err &&
 
-       EXISTS=1234567890 git env--helper --type=ulong --default=0 EXISTS --exit-code >actual.out 2>actual.err &&
+       EXISTS=1234567890 test-tool env-helper --type=ulong --default=0 EXISTS --exit-code >actual.out 2>actual.err &&
        test_must_be_empty actual.out &&
        test_must_be_empty actual.err &&
 
        echo 1234567890 >expected &&
-       EXISTS=1234567890 git env--helper --type=ulong --default=0 EXISTS >actual.out 2>actual.err &&
+       EXISTS=1234567890 test-tool env-helper --type=ulong --default=0 EXISTS >actual.out 2>actual.err &&
        test_cmp expected actual.out &&
        test_must_be_empty actual.err
 '
 
-test_expect_success 'env--helper reads config thanks to trace2' '
+test_expect_success 'test-tool env-helper reads config thanks to trace2' '
        mkdir home &&
        git config -f home/.gitconfig include.path cycle &&
        git config -f home/cycle include.path .gitconfig &&
@@ -93,7 +93,7 @@ test_expect_success 'env--helper reads config thanks to trace2' '
 
        test_must_fail \
                env HOME="$(pwd)/home" GIT_TEST_ENV_HELPER=true \
-               git -C cycle env--helper --type=bool --default=0 --exit-code GIT_TEST_ENV_HELPER 2>err &&
+               test-tool -C cycle env-helper --type=bool --default=0 --exit-code GIT_TEST_ENV_HELPER 2>err &&
        grep "exceeded maximum include depth" err
 '
 
index abecd75e4e430b6a1182690ae54a0e5d4e76bc32..46abbeed683992672f9dcaa6f9e2b256f1ea02eb 100755 (executable)
@@ -8,8 +8,8 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
-TEST_ROOT="$PWD"
-PATH=$TEST_ROOT:$PATH
+PATH=$PWD:$PATH
+TEST_ROOT="$(pwd)"
 
 write_script <<\EOF "$TEST_ROOT/rot13.sh"
 tr \
index f9bbb91f64e35d284a4ac79a6b2ecaa1d2913455..575805513a3d7f6d27fee7ceefcb75e71753f1f9 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='Test am with auto.crlf'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 cat >patchfile <<\EOF
index 04b811622bc4b1299095d7663928400679fc56be..7d0a0da8c01b8b69a9d9ca607c55145d96e59ad1 100755 (executable)
@@ -106,13 +106,7 @@ test_expect_success SYMLINKS 'setup dirs with symlinks' '
        ln -s d dir4/a/e &&
        ln -s ../b dir4/a/f &&
 
-       mkdir -p dir5/a/b &&
-       mkdir -p dir5/a/c &&
-       ln -s ../c dir5/a/b/d &&
-       ln -s ../ dir5/a/b/e &&
-       ln -s ../../ dir5/a/b/f &&
-
-       ln -s dir4 dir6
+       ln -s dir4 dir5
 '
 
 test_expect_success SYMLINKS 'dir-iterator should not follow symlinks by default' '
@@ -131,44 +125,10 @@ test_expect_success SYMLINKS 'dir-iterator should not follow symlinks by default
        test_cmp expected-no-follow-sorted-output actual-no-follow-sorted-output
 '
 
-test_expect_success SYMLINKS 'dir-iterator should follow symlinks w/ follow flag' '
-       cat >expected-follow-sorted-output <<-EOF &&
-       [d] (a) [a] ./dir4/a
-       [d] (a/f) [f] ./dir4/a/f
-       [d] (a/f/c) [c] ./dir4/a/f/c
-       [d] (b) [b] ./dir4/b
-       [d] (b/c) [c] ./dir4/b/c
-       [f] (a/d) [d] ./dir4/a/d
-       [f] (a/e) [e] ./dir4/a/e
-       EOF
-
-       test-tool dir-iterator --follow-symlinks ./dir4 >out &&
-       sort out >actual-follow-sorted-output &&
-
-       test_cmp expected-follow-sorted-output actual-follow-sorted-output
-'
-
 test_expect_success SYMLINKS 'dir-iterator does not resolve top-level symlinks' '
-       test_must_fail test-tool dir-iterator ./dir6 >out &&
+       test_must_fail test-tool dir-iterator ./dir5 >out &&
 
        grep "ENOTDIR" out
 '
 
-test_expect_success SYMLINKS 'dir-iterator resolves top-level symlinks w/ follow flag' '
-       cat >expected-follow-sorted-output <<-EOF &&
-       [d] (a) [a] ./dir6/a
-       [d] (a/f) [f] ./dir6/a/f
-       [d] (a/f/c) [c] ./dir6/a/f/c
-       [d] (b) [b] ./dir6/b
-       [d] (b/c) [c] ./dir6/b/c
-       [f] (a/d) [d] ./dir6/a/d
-       [f] (a/e) [e] ./dir6/a/e
-       EOF
-
-       test-tool dir-iterator --follow-symlinks ./dir6 >out &&
-       sort out >actual-follow-sorted-output &&
-
-       test_cmp expected-follow-sorted-output actual-follow-sorted-output
-'
-
 test_done
index c6e0d655630638853b6831350618bc10b15de6e2..3648d439a87eabb37b581cf5e731f9048085703b 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git for-each-repo builtin'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'run based on configured value' '
index 8d59905ef099bf0ab46d0681568dd17d7ed85d98..574de3419800e5f392038de8f35374d355fc9bcf 100755 (executable)
@@ -6,6 +6,7 @@ test_description='check that the most basic functions work
 Verify wrappers and compatibility functions.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'character classes (isspace, isalpha etc.)' '
index 3485c0534e6d39be9ef6f285fa2ec80400dc47cd..c66d91e82d8bc737bc6b909c9427f163aef5dfea 100755 (executable)
@@ -35,6 +35,16 @@ test_expect_success 'setup helper scripts' '
        test -z "$pass" || echo password=$pass
        EOF
 
+       write_script git-credential-verbatim-with-expiry <<-\EOF &&
+       user=$1; shift
+       pass=$1; shift
+       pexpiry=$1; shift
+       . ./dump
+       test -z "$user" || echo username=$user
+       test -z "$pass" || echo password=$pass
+       test -z "$pexpiry" || echo password_expiry_utc=$pexpiry
+       EOF
+
        PATH="$PWD:$PATH"
 '
 
@@ -109,6 +119,43 @@ test_expect_success 'credential_fill continues through partial response' '
        EOF
 '
 
+test_expect_success 'credential_fill populates password_expiry_utc' '
+       check fill "verbatim-with-expiry one two 9999999999" <<-\EOF
+       protocol=http
+       host=example.com
+       --
+       protocol=http
+       host=example.com
+       username=one
+       password=two
+       password_expiry_utc=9999999999
+       --
+       verbatim-with-expiry: get
+       verbatim-with-expiry: protocol=http
+       verbatim-with-expiry: host=example.com
+       EOF
+'
+
+test_expect_success 'credential_fill ignores expired password' '
+       check fill "verbatim-with-expiry one two 5" "verbatim three four" <<-\EOF
+       protocol=http
+       host=example.com
+       --
+       protocol=http
+       host=example.com
+       username=three
+       password=four
+       --
+       verbatim-with-expiry: get
+       verbatim-with-expiry: protocol=http
+       verbatim-with-expiry: host=example.com
+       verbatim: get
+       verbatim: protocol=http
+       verbatim: host=example.com
+       verbatim: username=one
+       EOF
+'
+
 test_expect_success 'credential_fill passes along metadata' '
        check fill "verbatim one two" <<-\EOF
        protocol=ftp
@@ -149,6 +196,24 @@ test_expect_success 'credential_approve calls all helpers' '
        EOF
 '
 
+test_expect_success 'credential_approve stores password expiry' '
+       check approve useless <<-\EOF
+       protocol=http
+       host=example.com
+       username=foo
+       password=bar
+       password_expiry_utc=9999999999
+       --
+       --
+       useless: store
+       useless: protocol=http
+       useless: host=example.com
+       useless: username=foo
+       useless: password=bar
+       useless: password_expiry_utc=9999999999
+       EOF
+'
+
 test_expect_success 'do not bother storing password-less credential' '
        check approve useless <<-\EOF
        protocol=http
@@ -159,6 +224,17 @@ test_expect_success 'do not bother storing password-less credential' '
        EOF
 '
 
+test_expect_success 'credential_approve does not store expired password' '
+       check approve useless <<-\EOF
+       protocol=http
+       host=example.com
+       username=foo
+       password=bar
+       password_expiry_utc=5
+       --
+       --
+       EOF
+'
 
 test_expect_success 'credential_reject calls all helpers' '
        check reject useless "verbatim one two" <<-\EOF
@@ -181,6 +257,24 @@ test_expect_success 'credential_reject calls all helpers' '
        EOF
 '
 
+test_expect_success 'credential_reject erases credential regardless of expiry' '
+       check reject useless <<-\EOF
+       protocol=http
+       host=example.com
+       username=foo
+       password=bar
+       password_expiry_utc=5
+       --
+       --
+       useless: erase
+       useless: protocol=http
+       useless: host=example.com
+       useless: username=foo
+       useless: password=bar
+       useless: password_expiry_utc=5
+       EOF
+'
+
 test_expect_success 'usernames can be preserved' '
        check fill "verbatim \"\" three" <<-\EOF
        protocol=http
index 516a6112fdcf1907fb3827cc23e7ad0b24b39099..3fb1b0c162ded2a5d78767dbd62de97fc59e5d29 100755 (executable)
@@ -370,7 +370,7 @@ test_expect_success 'read-tree supports the super-prefix' '
        cat <<-EOF >expect &&
                error: Updating '\''fictional/a'\'' would lose untracked files in it
        EOF
-       test_must_fail git --super-prefix fictional/ read-tree -u -m "$treeH" "$treeM" 2>actual &&
+       test_must_fail git read-tree --super-prefix fictional/ -u -m "$treeH" "$treeM" 2>actual &&
        test_cmp expect actual
 '
 
index 23b8942edba45c32822886ee93acef9b5474ea02..2d875b17d8a94a45b9789e3466139adbd6b4c680 100755 (executable)
@@ -292,8 +292,8 @@ commit_message="Initial commit"
 commit_sha1=$(echo_without_newline "$commit_message" | git commit-tree $tree_sha1)
 commit_size=$(($(test_oid hexsz) + 137))
 commit_content="tree $tree_sha1
-author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> 0000000000 +0000
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 0000000000 +0000
+author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> 0 +0000
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 0 +0000
 
 $commit_message"
 
@@ -304,7 +304,7 @@ type blob
 tag hellotag
 tagger $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
 tag_description="This is a tag"
-tag_content="$tag_header_without_timestamp 0000000000 +0000
+tag_content="$tag_header_without_timestamp 0 +0000
 
 $tag_description"
 
index ac5ad8c7402d2bd3f43d38e5a676d864ea415d37..ac3d173767ae72be7bc43e4df269d88fb040f7ba 100755 (executable)
@@ -203,23 +203,34 @@ done
 test_expect_success 'too-short tree' '
        echo abc >malformed-tree &&
        test_must_fail git hash-object -t tree malformed-tree 2>err &&
-       test_i18ngrep "too-short tree object" err
+       grep "too-short tree object" err
 '
 
 test_expect_success 'malformed mode in tree' '
-       hex_sha1=$(echo foo | git hash-object --stdin -w) &&
-       bin_sha1=$(echo $hex_sha1 | hex2oct) &&
-       printf "9100644 \0$bin_sha1" >tree-with-malformed-mode &&
+       hex_oid=$(echo foo | git hash-object --stdin -w) &&
+       bin_oid=$(echo $hex_oid | hex2oct) &&
+       printf "9100644 \0$bin_oid" >tree-with-malformed-mode &&
        test_must_fail git hash-object -t tree tree-with-malformed-mode 2>err &&
-       test_i18ngrep "malformed mode in tree entry" err
+       grep "malformed mode in tree entry" err
 '
 
 test_expect_success 'empty filename in tree' '
-       hex_sha1=$(echo foo | git hash-object --stdin -w) &&
-       bin_sha1=$(echo $hex_sha1 | hex2oct) &&
-       printf "100644 \0$bin_sha1" >tree-with-empty-filename &&
+       hex_oid=$(echo foo | git hash-object --stdin -w) &&
+       bin_oid=$(echo $hex_oid | hex2oct) &&
+       printf "100644 \0$bin_oid" >tree-with-empty-filename &&
        test_must_fail git hash-object -t tree tree-with-empty-filename 2>err &&
-       test_i18ngrep "empty filename in tree entry" err
+       grep "empty filename in tree entry" err
+'
+
+test_expect_success 'duplicate filename in tree' '
+       hex_oid=$(echo foo | git hash-object --stdin -w) &&
+       bin_oid=$(echo $hex_oid | hex2oct) &&
+       {
+               printf "100644 file\0$bin_oid" &&
+               printf "100644 file\0$bin_oid"
+       } >tree-with-duplicate-filename &&
+       test_must_fail git hash-object -t tree tree-with-duplicate-filename 2>err &&
+       grep "duplicateEntries" err
 '
 
 test_expect_success 'corrupt commit' '
index 742f0fa909fd6e8a7fe2b376b3e5083a30d71c73..595b24c0adb3081230fe65884fd436e1f12896d4 100755 (executable)
@@ -12,6 +12,7 @@ test_description='sparse checkout tests
 '
 
 TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-read-tree.sh
 
index da539716359614f6d91cd1db896b3981e1805d6d..cca4380e4319a024416c4bbe2e097f63e409af48 100755 (executable)
@@ -3,7 +3,7 @@
 test_description='git read-tree in partial clones'
 
 TEST_NO_CREATE_REPO=1
-
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'read-tree in partial clone prefetches in one batch' '
index b563d6c263ec1cee0d4bfc93cba1e85e4833f4e6..627267be1531056c4eff9e5a0dc2bc1d8d1eab38 100755 (executable)
@@ -238,7 +238,7 @@ test_expect_success 'cone mode: match patterns' '
 test_expect_success 'cone mode: warn on bad pattern' '
        test_when_finished mv sparse-checkout repo/.git/info/ &&
        cp repo/.git/info/sparse-checkout . &&
-       echo "!/deep/deeper/*" >>repo/.git/info/sparse-checkout &&
+       echo "!/deep/deeper/*/" >>repo/.git/info/sparse-checkout &&
        git -C repo read-tree -mu HEAD 2>err &&
        test_i18ngrep "unrecognized negative pattern" err
 '
@@ -667,6 +667,15 @@ test_expect_success 'pattern-checks: starting "*"' '
        check_read_tree_errors repo "a deep" "disabling cone pattern matching"
 '
 
+test_expect_success 'pattern-checks: non directory pattern' '
+       cat >repo/.git/info/sparse-checkout <<-\EOF &&
+       /deep/deeper1/a
+       EOF
+       check_read_tree_errors repo deep "disabling cone pattern matching" &&
+       check_files repo/deep deeper1 &&
+       check_files repo/deep/deeper1 a
+'
+
 test_expect_success 'pattern-checks: contained glob characters' '
        for c in "[a]" "\\" "?" "*"
        do
index 93a2f91f8a5181c5307c6de14ab6f2823d2b959a..1b6437ec0794e887e1d14452c516c487d0bb007f 100755 (executable)
@@ -8,6 +8,8 @@ test_description='Test shared repository initialization'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Remove a default ACL from the test dir if possible.
@@ -25,6 +27,7 @@ test_expect_success 'shared = 0400 (faulty permission u-w)' '
 for u in 002 022
 do
        test_expect_success POSIXPERM "shared=1 does not clear bits preset by umask $u" '
+               test_when_finished "rm -rf sub" &&
                mkdir sub && (
                        cd sub &&
                        umask $u &&
@@ -42,12 +45,9 @@ do
                        ;;
                esac
        '
-       rm -rf sub
 done
 
 test_expect_success 'shared=all' '
-       mkdir sub &&
-       cd sub &&
        git init --template= --shared=all &&
        test 2 = $(git config core.sharedrepository)
 '
@@ -132,6 +132,7 @@ test_expect_success POSIXPERM 'git reflog expire honors core.sharedRepository' '
 '
 
 test_expect_success POSIXPERM 'forced modes' '
+       test_when_finished "rm -rf new" &&
        mkdir -p templates/hooks &&
        echo update-server-info >templates/hooks/post-update &&
        chmod +x templates/hooks/post-update &&
@@ -140,7 +141,8 @@ test_expect_success POSIXPERM 'forced modes' '
        (
                cd new &&
                umask 002 &&
-               git init --shared=0660 --template=templates &&
+               git init --shared=0660 --template=../templates &&
+               test_path_is_file .git/hooks/post-update &&
                >frotz &&
                git add frotz &&
                git commit -a -m initial &&
@@ -173,6 +175,7 @@ test_expect_success POSIXPERM 'forced modes' '
 '
 
 test_expect_success POSIXPERM 'remote init does not use config from cwd' '
+       test_when_finished "rm -rf child.git" &&
        git config core.sharedrepository 0666 &&
        umask 0022 &&
        git init --bare child.git &&
@@ -192,7 +195,7 @@ test_expect_success POSIXPERM 're-init respects core.sharedrepository (local)' '
 '
 
 test_expect_success POSIXPERM 're-init respects core.sharedrepository (remote)' '
-       rm -rf child.git &&
+       test_when_finished "rm -rf child.git" &&
        umask 0022 &&
        git init --bare --shared=0666 child.git &&
        test_path_is_missing child.git/foo &&
@@ -203,7 +206,7 @@ test_expect_success POSIXPERM 're-init respects core.sharedrepository (remote)'
 '
 
 test_expect_success POSIXPERM 'template can set core.sharedrepository' '
-       rm -rf child.git &&
+       test_when_finished "rm -rf child.git" &&
        umask 0022 &&
        git config core.sharedrepository 0666 &&
        cp .git/config templates/config &&
index 0acabb6d11b48aca54e73025965f4c4e6b561d34..70389fa2ebbfa47ad410ca5c30bb294b6cf0753f 100755 (executable)
@@ -5,6 +5,7 @@
 
 test_description='Test repository version check'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -27,7 +28,7 @@ test_expect_success 'setup' '
 '
 
 test_expect_success 'gitdir selection on normal repos' '
-       echo $(test_oid version) >expect &&
+       test_oid version >expect &&
        git config core.repositoryformatversion >actual &&
        git -C test config core.repositoryformatversion >actual2 &&
        test_cmp expect actual &&
index c69ae41306c90d33b2ac253a25961ae76fec81e7..31b89dd9693afc95f30e0bb9e11dc704fda24b41 100755 (executable)
@@ -9,6 +9,7 @@ test_description='Test repository with default ACL'
 # => this must come before . ./test-lib.sh
 umask 077
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # We need an arbitrary other user give permission to using ACLs. root
index 13c2b43bbae8343c2402952bba8ac9635001b453..b5606d93b52bf44366a51f3d4b6dd19e7a8816ab 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='Test git update-ref error handling'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Create some references, perhaps run pack-refs --all, then try to
index 41ba1f1d7fca94e9504e0c198b2e1f7885c41185..9469c79a585f051177a4c9223c0e0f9fd37124dd 100755 (executable)
@@ -5,6 +5,7 @@ test_description='packed-refs entries are covered by loose refs'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index be12fb63506e2a8c242e876526dbe1d60e105f49..f23c0152a858265e4a8c6b0c068e00823aadb444 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='avoid rewriting packed-refs unnecessarily'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Add an identifying mark to the packed-refs file header line. This
index aa59954f6c52faa2a4b5ba3fa32fdd2a2b4b0b85..6c45965b1e4bef9652a1bc876f28605ad213b261 100755 (executable)
@@ -7,6 +7,7 @@ test_description='Test prune and reflog expiration'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 check_have () {
index 934688a1ee82e29f2552bb2444eb06626fe254f8..d2a4822d46fd05c45273f48b0caf48cadb2a07f1 100755 (executable)
@@ -4,6 +4,7 @@ test_description='Test reflog interaction with detached HEAD'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 reset_state () {
index 27731722a5b6b26bad0b1929c274414239cc449e..b32ca798f9ff9fb60796dfefd41ad391e229dbf5 100755 (executable)
@@ -5,6 +5,7 @@ test_description='reference transaction hooks'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index de0f6d5e7fd49ca5276696833d8f9dd034a15629..fdb886dfe431f36dfc4ef6bcf24abf6827c2243d 100755 (executable)
@@ -212,7 +212,7 @@ test_expect_success 'email without @ is okay' '
 test_expect_success 'email with embedded > is not okay' '
        git cat-file commit HEAD >basis &&
        sed "s/@[a-z]/&>/" basis >bad-email &&
-       new=$(git hash-object -t commit -w --stdin <bad-email) &&
+       new=$(git hash-object --literally -t commit -w --stdin <bad-email) &&
        test_when_finished "remove_object $new" &&
        git update-ref refs/heads/bogus "$new" &&
        test_when_finished "git update-ref -d refs/heads/bogus" &&
@@ -223,7 +223,7 @@ test_expect_success 'email with embedded > is not okay' '
 test_expect_success 'missing < email delimiter is reported nicely' '
        git cat-file commit HEAD >basis &&
        sed "s/<//" basis >bad-email-2 &&
-       new=$(git hash-object -t commit -w --stdin <bad-email-2) &&
+       new=$(git hash-object --literally -t commit -w --stdin <bad-email-2) &&
        test_when_finished "remove_object $new" &&
        git update-ref refs/heads/bogus "$new" &&
        test_when_finished "git update-ref -d refs/heads/bogus" &&
@@ -234,7 +234,7 @@ test_expect_success 'missing < email delimiter is reported nicely' '
 test_expect_success 'missing email is reported nicely' '
        git cat-file commit HEAD >basis &&
        sed "s/[a-z]* <[^>]*>//" basis >bad-email-3 &&
-       new=$(git hash-object -t commit -w --stdin <bad-email-3) &&
+       new=$(git hash-object --literally -t commit -w --stdin <bad-email-3) &&
        test_when_finished "remove_object $new" &&
        git update-ref refs/heads/bogus "$new" &&
        test_when_finished "git update-ref -d refs/heads/bogus" &&
@@ -245,7 +245,7 @@ test_expect_success 'missing email is reported nicely' '
 test_expect_success '> in name is reported' '
        git cat-file commit HEAD >basis &&
        sed "s/ </> </" basis >bad-email-4 &&
-       new=$(git hash-object -t commit -w --stdin <bad-email-4) &&
+       new=$(git hash-object --literally -t commit -w --stdin <bad-email-4) &&
        test_when_finished "remove_object $new" &&
        git update-ref refs/heads/bogus "$new" &&
        test_when_finished "git update-ref -d refs/heads/bogus" &&
@@ -258,7 +258,7 @@ test_expect_success 'integer overflow in timestamps is reported' '
        git cat-file commit HEAD >basis &&
        sed "s/^\\(author .*>\\) [0-9]*/\\1 18446744073709551617/" \
                <basis >bad-timestamp &&
-       new=$(git hash-object -t commit -w --stdin <bad-timestamp) &&
+       new=$(git hash-object --literally -t commit -w --stdin <bad-timestamp) &&
        test_when_finished "remove_object $new" &&
        git update-ref refs/heads/bogus "$new" &&
        test_when_finished "git update-ref -d refs/heads/bogus" &&
@@ -269,7 +269,7 @@ test_expect_success 'integer overflow in timestamps is reported' '
 test_expect_success 'commit with NUL in header' '
        git cat-file commit HEAD >basis &&
        sed "s/author ./author Q/" <basis | q_to_nul >commit-NUL-header &&
-       new=$(git hash-object -t commit -w --stdin <commit-NUL-header) &&
+       new=$(git hash-object --literally -t commit -w --stdin <commit-NUL-header) &&
        test_when_finished "remove_object $new" &&
        git update-ref refs/heads/bogus "$new" &&
        test_when_finished "git update-ref -d refs/heads/bogus" &&
@@ -292,7 +292,7 @@ test_expect_success 'tree object with duplicate entries' '
                        git cat-file tree $T &&
                        git cat-file tree $T
                ) |
-               git hash-object -w -t tree --stdin
+               git hash-object --literally -w -t tree --stdin
        ) &&
        test_must_fail git fsck 2>out &&
        test_i18ngrep "error in tree .*contains duplicate file entries" out
@@ -426,7 +426,7 @@ test_expect_success 'tag with incorrect tag name & missing tagger' '
        This is an invalid tag.
        EOF
 
-       tag=$(git hash-object -t tag -w --stdin <wrong-tag) &&
+       tag=$(git hash-object --literally -t tag -w --stdin <wrong-tag) &&
        test_when_finished "remove_object $tag" &&
        echo $tag >.git/refs/tags/wrong &&
        test_when_finished "git update-ref -d refs/tags/wrong" &&
@@ -558,7 +558,7 @@ test_expect_success 'rev-list --verify-objects with commit graph (parent)' '
 test_expect_success 'force fsck to ignore double author' '
        git cat-file commit HEAD >basis &&
        sed "s/^author .*/&,&/" <basis | tr , \\n >multiple-authors &&
-       new=$(git hash-object -t commit -w --stdin <multiple-authors) &&
+       new=$(git hash-object --literally -t commit -w --stdin <multiple-authors) &&
        test_when_finished "remove_object $new" &&
        git update-ref refs/heads/bogus "$new" &&
        test_when_finished "git update-ref -d refs/heads/bogus" &&
@@ -573,7 +573,7 @@ test_expect_success 'fsck notices blob entry pointing to null sha1' '
        (git init null-blob &&
         cd null-blob &&
         sha=$(printf "100644 file$_bz$_bzoid" |
-              git hash-object -w --stdin -t tree) &&
+              git hash-object --literally -w --stdin -t tree) &&
          git fsck 2>out &&
          test_i18ngrep "warning.*null sha1" out
        )
@@ -583,7 +583,7 @@ test_expect_success 'fsck notices submodule entry pointing to null sha1' '
        (git init null-commit &&
         cd null-commit &&
         sha=$(printf "160000 submodule$_bz$_bzoid" |
-              git hash-object -w --stdin -t tree) &&
+              git hash-object --literally -w --stdin -t tree) &&
          git fsck 2>out &&
          test_i18ngrep "warning.*null sha1" out
        )
@@ -648,7 +648,7 @@ test_expect_success 'NUL in commit' '
                git commit --allow-empty -m "initial commitQNUL after message" &&
                git cat-file commit HEAD >original &&
                q_to_nul <original >munged &&
-               git hash-object -w -t commit --stdin <munged >name &&
+               git hash-object --literally -w -t commit --stdin <munged >name &&
                git branch bad $(cat name) &&
 
                test_must_fail git -c fsck.nulInCommit=error fsck 2>warn.1 &&
@@ -794,8 +794,8 @@ test_expect_success 'fsck errors in packed objects' '
        git cat-file commit HEAD >basis &&
        sed "s/</one/" basis >one &&
        sed "s/</foo/" basis >two &&
-       one=$(git hash-object -t commit -w one) &&
-       two=$(git hash-object -t commit -w two) &&
+       one=$(git hash-object --literally -t commit -w one) &&
+       two=$(git hash-object --literally -t commit -w two) &&
        pack=$(
                {
                        echo $one &&
diff --git a/t/t1451-fsck-buffer.sh b/t/t1451-fsck-buffer.sh
new file mode 100755 (executable)
index 0000000..3413da4
--- /dev/null
@@ -0,0 +1,142 @@
+#!/bin/sh
+
+test_description='fsck on buffers without NUL termination
+
+The goal here is to make sure that the various fsck parsers never look
+past the end of the buffer they are given, even when encountering broken
+or truncated objects.
+
+We have to use "hash-object" for this because most code paths that read objects
+append an extra NUL for safety after the buffer. But hash-object, since it is
+reading straight from a file (and possibly even mmap-ing it) cannot always do
+so.
+
+These tests _might_ catch such overruns in normal use, but should be run with
+ASan or valgrind for more confidence.
+'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+# the general idea for tags and commits is to build up the "base" file
+# progressively, and then test new truncations on top of it.
+reset () {
+       test_expect_success 'reset input to empty' '
+               >base
+       '
+}
+
+add () {
+       content="$1"
+       type=${content%% *}
+       test_expect_success "add $type line" '
+               echo "$content" >>base
+       '
+}
+
+check () {
+       type=$1
+       fsck=$2
+       content=$3
+       test_expect_success "truncated $type ($fsck, \"$content\")" '
+               # do not pipe into hash-object here; we want to increase
+               # the chance that it uses a fixed-size buffer or mmap,
+               # and a pipe would be read into a strbuf.
+               {
+                       cat base &&
+                       echo "$content"
+               } >input &&
+               test_must_fail git hash-object -t "$type" input 2>err &&
+               grep "$fsck" err
+       '
+}
+
+test_expect_success 'create valid objects' '
+       git commit --allow-empty -m foo &&
+       commit=$(git rev-parse --verify HEAD) &&
+       tree=$(git rev-parse --verify HEAD^{tree})
+'
+
+reset
+check commit missingTree ""
+check commit missingTree "tr"
+check commit missingTree "tree"
+check commit badTreeSha1 "tree "
+check commit badTreeSha1 "tree 1234"
+add "tree $tree"
+
+# these expect missingAuthor because "parent" is optional
+check commit missingAuthor ""
+check commit missingAuthor "par"
+check commit missingAuthor "parent"
+check commit badParentSha1 "parent "
+check commit badParentSha1 "parent 1234"
+add "parent $commit"
+
+check commit missingAuthor ""
+check commit missingAuthor "au"
+check commit missingAuthor "author"
+ident_checks () {
+       check $1 missingEmail "$2 "
+       check $1 missingEmail "$2 name"
+       check $1 badEmail "$2 name <"
+       check $1 badEmail "$2 name <email"
+       check $1 missingSpaceBeforeDate "$2 name <email>"
+       check $1 badDate "$2 name <email> "
+       check $1 badDate "$2 name <email> 1234"
+       check $1 badTimezone "$2 name <email> 1234 "
+       check $1 badTimezone "$2 name <email> 1234 +"
+}
+ident_checks commit author
+add "author name <email> 1234 +0000"
+
+check commit missingCommitter ""
+check commit missingCommitter "co"
+check commit missingCommitter "committer"
+ident_checks commit committer
+add "committer name <email> 1234 +0000"
+
+reset
+check tag missingObject ""
+check tag missingObject "obj"
+check tag missingObject "object"
+check tag badObjectSha1 "object "
+check tag badObjectSha1 "object 1234"
+add "object $commit"
+
+check tag missingType ""
+check tag missingType "ty"
+check tag missingType "type"
+check tag badType "type "
+check tag badType "type com"
+add "type commit"
+
+check tag missingTagEntry ""
+check tag missingTagEntry "ta"
+check tag missingTagEntry "tag"
+check tag badTagName "tag "
+add "tag foo"
+
+check tag missingTagger ""
+check tag missingTagger "ta"
+check tag missingTagger "tagger"
+ident_checks tag tagger
+
+# trees are a binary format and can't use our earlier helpers
+test_expect_success 'truncated tree (short hash)' '
+       printf "100644 foo\0\1\1\1\1" >input &&
+       test_must_fail git hash-object -t tree input 2>err &&
+       grep badTree err
+'
+
+test_expect_success 'truncated tree (missing nul)' '
+       # these two things are indistinguishable to the parser. The important
+       # thing about this is example is that there are enough bytes to
+       # make up a hash, and that there is no NUL (and we confirm that the
+       # parser does not walk past the end of the buffer).
+       printf "100644 a long filename, or a hash with missing nul?" >input &&
+       test_must_fail git hash-object -t tree input 2>err &&
+       grep badTree err
+'
+
+test_done
index 81de584ea29bca8cfcbe20769c69072202a53efb..37ee5091b5caa7c087663cbb51f701084227c414 100755 (executable)
@@ -195,7 +195,7 @@ test_expect_success 'rev-parse --is-shallow-repository in non-shallow repo' '
 '
 
 test_expect_success 'rev-parse --show-object-format in repo' '
-       echo "$(test_oid algo)" >expect &&
+       test_oid algo >expect &&
        git rev-parse --show-object-format >actual &&
        test_cmp expect actual &&
        git rev-parse --show-object-format=storage >actual &&
index b75558040ffd6ee9174ed37bd2ca746212b57013..ae6528aecea7d3b36cfe51c5f19c7098f05429e3 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test separate work tree'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 553a3f601ba7c76efa193d4a33823197c05281e7..c799f5b6acad35633c6efa87a1a5e10aa67b8fd2 100755 (executable)
@@ -221,7 +221,8 @@ test_expect_success 'setup' '
        rm -rf /.git &&
        echo "Initialized empty Git repository in /.git/" > expected &&
        git init > result &&
-       test_cmp expected result
+       test_cmp expected result &&
+       git config --global --add safe.directory /
 '
 
 test_vars 'auto gitdir, root' ".git" "/" ""
@@ -242,7 +243,7 @@ say "auto bare gitdir"
 # DESTROYYYYY!!!!!
 test_expect_success 'setup' '
        rm -rf /refs /objects /info /hooks &&
-       rm -f /expected /ls.expected /me /result &&
+       rm -f /HEAD /expected /ls.expected /me /result &&
        cd / &&
        echo "Initialized empty Git repository in /" > expected &&
        git init --bare > result &&
@@ -255,4 +256,9 @@ test_expect_success 'go to /foo' 'cd /foo'
 
 test_vars 'auto gitdir, root' "/" "" ""
 
+test_expect_success 'cleanup root' '
+       rm -rf /.git /refs /objects /info /hooks /branches /foo &&
+       rm -f /HEAD /config /description /expected /ls.expected /me /result
+'
+
 test_done
index 010989f90e63f9ddf5a88d25198998c55782bbd2..9368d82f7d70ca8133616ed40f167ae4344266c4 100755 (executable)
@@ -65,6 +65,37 @@ test_expect_success 'out of bounds index.version issues warning' '
        )
 '
 
+test_expect_success 'index.skipHash config option' '
+       rm -f .git/index &&
+       git -c index.skipHash=true add a &&
+       test_trailing_hash .git/index >hash &&
+       echo $(test_oid zero) >expect &&
+       test_cmp expect hash &&
+       git fsck &&
+
+       rm -f .git/index &&
+       git -c feature.manyFiles=true add a &&
+       test_trailing_hash .git/index >hash &&
+       cmp expect hash &&
+
+       rm -f .git/index &&
+       git -c feature.manyFiles=true \
+           -c index.skipHash=false add a &&
+       test_trailing_hash .git/index >hash &&
+       ! cmp expect hash &&
+
+       test_commit start &&
+       git -c protocol.file.allow=always submodule add ./ sub &&
+       git config index.skipHash false &&
+       git -C sub config index.skipHash true &&
+       rm -f .git/modules/sub/index &&
+       >sub/file &&
+       git -C sub add a &&
+       test_trailing_hash .git/modules/sub/index >hash &&
+       test_cmp expect hash &&
+       git -C sub fsck
+'
+
 test_index_version () {
        INDEX_VERSION_CONFIG=$1 &&
        FEATURE_MANY_FILES=$2 &&
index 2ef3579fa7c23db5055c41e68438d39871bcf6e9..3506f627b6cf20f11e4cfd78b309e9e9acc07148 100755 (executable)
@@ -177,4 +177,22 @@ test_expect_success 'git hook run a hook with a bad shebang' '
        test_cmp expect actual
 '
 
+test_expect_success 'stdin to hooks' '
+       write_script .git/hooks/test-hook <<-\EOF &&
+       echo BEGIN stdin
+       cat
+       echo END stdin
+       EOF
+
+       cat >expect <<-EOF &&
+       BEGIN stdin
+       hello
+       END stdin
+       EOF
+
+       echo hello >input &&
+       git hook run --to-stdin=input test-hook 2>actual &&
+       test_cmp expect actual
+'
+
 test_done
index 1f6c4ed0428a5c966c76359c1f4fa2e536f37fcf..4b6372f4c3e946c26a490698335a821cff5701f2 100755 (executable)
@@ -5,6 +5,7 @@ test_description='checkout can switch to last branch and merge base'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 9425aae6395838b3d0fd57b8cf8fe228d1db335f..fb0e13881cd8a290dd272c6dc16c3f097f49cb39 100755 (executable)
@@ -9,11 +9,12 @@ TEST_PASSES_SANITIZE_LEAK=true
 
 test_expect_success 'setup' '
        mkdir parent &&
-       (cd parent &&
-        git init &&
-        echo content >file &&
-        git add file &&
-        git commit -m base
+       (
+               cd parent &&
+               git init &&
+               echo content >file &&
+               git add file &&
+               git commit -m base
        ) &&
        git fetch parent main:origin
 '
index a5822e41af28112d44f8a2076804a5dd7d197904..747eb5563eca8f1df4374f179a08019221ba1fac 100755 (executable)
@@ -4,12 +4,6 @@ test_description='git checkout --patch'
 
 . ./lib-patch-mode.sh
 
-if ! test_have_prereq ADD_I_USE_BUILTIN && ! test_have_prereq PERL
-then
-       skip_all='skipping interactive add tests, PERL not set'
-       test_done
-fi
-
 test_expect_success 'setup' '
        mkdir dir &&
        echo parent > dir/foo &&
index 771c3c3c50e60583e3ba8dffbb62f47a7619f35b..8581ad3437907862e022e1bea8339182d68ef873 100755 (executable)
@@ -3,6 +3,7 @@
 test_description='checkout'
 
 TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Arguments: [!] <branch> <oid> [<checkout options>]
index 8f13341cf8ee0d82e0adeeb28449e31d104a5243..3832c3de8135524b4294b7ece693194b3bf506c1 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='checkout --no-overlay <tree-ish> -- <pathspec>'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 3d28c7f06b2c8717e31fae23d37fda0acc008a48..568a47ec4268778052534d93a168361744215efa 100755 (executable)
@@ -5,6 +5,7 @@ test_description='prune $GIT_DIR/worktrees'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success initialize '
index 79e0fce2d90fb9e947a76052c32e41d20519e6bd..9ad9be0c2089335b67fcb08f00ade4b46ab5637c 100755 (executable)
@@ -5,6 +5,7 @@ test_description='test git worktree list'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 5c44453e1c13322e1fcf30d2facac3c2942a274e..8970780efccd85f54c1e1bd830f841d34ce90ce8 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='test git worktree repair'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 963f3462b750b2a4fc7d009df6cf1fa1949090d6..14218b342437788fef40c539c45fb41575ef0071 100755 (executable)
@@ -18,6 +18,7 @@ This test runs git ls-files --others with the following working tree:
       git repository with a commit and an untracked file
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup: directories' '
index 580e158f9918bcaf86224ce8743894da67266096..054178703d732403ce32eda9871d8d85c47dddaa 100755 (executable)
@@ -41,6 +41,8 @@ Also for modification test, the cache and working tree have:
 We should report path0, path1, path2/file2, path3/file3, path7 and path8
 modified without reporting path9 and path10.  submod1 is also modified.
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'git update-index --add to add various paths.' '
index f1f09abdd9b254eed1efa6b0d06369f3c8606cf8..3884694165525b9e4a3d8527785f40758817be53 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='fetching and pushing project with subproject'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 52f76f7b57f3ba9861bc9220602a5ca9fc45aeea..c4a72ae44623a0bab3ce06c1a5e850e9e8405054 100755 (executable)
@@ -8,6 +8,8 @@ test_description='git ls-files test (--with-tree).
 This test runs git ls-files --with-tree and in particular in
 a scenario known to trigger a crash with some versions of git.
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 383896667b6dc9e446c157370dedd3a3aa8051d1..3adb206a93bc58aac509a1f093f9dd86cb98e5be 100755 (executable)
@@ -20,7 +20,6 @@ test_ls_tree_format () {
        format=$1 &&
        opts=$2 &&
        fmtopts=$3 &&
-       shift 2 &&
 
        test_expect_success "ls-tree '--format=<$format>' is like options '$opts $fmtopts'" '
                git ls-tree $opts -r HEAD >expect &&
@@ -36,6 +35,12 @@ test_ls_tree_format () {
        '
 }
 
+test_expect_success "ls-tree --format='%(path) %(path) %(path)' HEAD top-file" '
+       git ls-tree --format="%(path) %(path) %(path)" HEAD top-file.t >actual &&
+       echo top-file.t top-file.t top-file.t >expect &&
+       test_cmp expect actual
+'
+
 test_ls_tree_format \
        "%(objectmode) %(objecttype) %(objectname)%x09%(path)" \
        ""
index 793bf4d269e6a1f101481ae70280dd5db19e75a9..3399344f25dc859ad743b3914a3f5c37e92bffbb 100755 (executable)
@@ -57,6 +57,16 @@ test_expect_success 'create branch with pseudo-qualified name' '
        expect_branch refs/heads/refs/heads/qualified two
 '
 
+test_expect_success 'force-copy a branch to itself via @{-1} is no-op' '
+       git branch -t copiable main &&
+       git checkout copiable &&
+       git checkout - &&
+       git branch -C @{-1} copiable &&
+       git config --get-all branch.copiable.merge >actual &&
+       echo refs/heads/main >expect &&
+       test_cmp expect actual
+'
+
 test_expect_success 'delete branch via @{-1}' '
        git branch previous-del &&
 
index 84dd0cd26d02cb3a3151f1529544ef1f70e052ae..b5f4d6a653045dafa7c2317036196866f78b9ca5 100755 (executable)
@@ -33,6 +33,26 @@ test_expect_success 'setup' '
        u3 sha256:736c4bc
        u4 sha256:673e77d
 
+       # topic (abbrev=10)
+       t1_abbrev sha1:4de457d2c0
+       t2_abbrev sha1:fccce22f8c
+       t3_abbrev sha1:147e64ef53
+       t4_abbrev sha1:a63e992599
+       t1_abbrev sha256:b89f8b9092
+       t2_abbrev sha256:5f12aadf34
+       t3_abbrev sha256:ea8b273a6c
+       t4_abbrev sha256:14b73361fc
+
+       # unmodified (abbrev=10)
+       u1_abbrev sha1:35b9b25f76
+       u2_abbrev sha1:de345ab3de
+       u3_abbrev sha1:9af6654000
+       u4_abbrev sha1:2901f773f3
+       u1_abbrev sha256:e3731be242
+       u2_abbrev sha256:14fadf8cee
+       u3_abbrev sha256:736c4bcb44
+       u4_abbrev sha256:673e77d589
+
        # reordered
        r1 sha1:aca177a
        r2 sha1:14ad629
@@ -153,6 +173,18 @@ test_expect_success 'simple A B C (unmodified)' '
        test_cmp expect actual
 '
 
+test_expect_success 'simple A..B A..C (unmodified) with --abbrev' '
+       git range-diff --no-color --abbrev=10 main..topic main..unmodified \
+               >actual &&
+       cat >expect <<-EOF &&
+       1:  $(test_oid t1_abbrev) = 1:  $(test_oid u1_abbrev) s/5/A/
+       2:  $(test_oid t2_abbrev) = 2:  $(test_oid u2_abbrev) s/4/A/
+       3:  $(test_oid t3_abbrev) = 3:  $(test_oid u3_abbrev) s/11/B/
+       4:  $(test_oid t4_abbrev) = 4:  $(test_oid u4_abbrev) s/12/B/
+       EOF
+       test_cmp expect actual
+'
+
 test_expect_success 'A^! and A^-<n> (unmodified)' '
        git range-diff --no-color topic^! unmodified^-1 >actual &&
        cat >expect <<-EOF &&
index 577f32dc71ff2ccf0f34c01b060fd82b87c566f9..07a0ff93defebe395fe49344834b2fc627c7f81d 100755 (executable)
@@ -12,6 +12,7 @@ semantic is still the same.
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'enable reflogs' '
index 462cefd25df3efd51b06023523530b3d97d3d3a9..efeb74ad50a9efb74ec689311efbb9e72dc3141e 100755 (executable)
@@ -2072,6 +2072,7 @@ test_expect_success '--update-refs: --edit-todo with no update-ref lines' '
 '
 
 test_expect_success '--update-refs: check failed ref update' '
+       test_when_finished "test_might_fail git rebase --abort" &&
        git checkout -B update-refs-error no-conflict-branch &&
        git branch -f base HEAD~4 &&
        git branch -f first HEAD~3 &&
@@ -2123,6 +2124,28 @@ test_expect_success '--update-refs: check failed ref update' '
        test_cmp expect err.trimmed
 '
 
+test_expect_success 'bad labels and refs rejected when parsing todo list' '
+       test_when_finished "test_might_fail git rebase --abort" &&
+       cat >todo <<-\EOF &&
+       exec >execed
+       label #
+       label :invalid
+       update-ref :bad
+       update-ref topic
+       EOF
+       rm -f execed &&
+       (
+               set_replace_editor todo &&
+               test_must_fail git rebase -i HEAD 2>err
+       ) &&
+       grep "'\''#'\'' is not a valid label" err &&
+       grep "'\'':invalid'\'' is not a valid label" err &&
+       grep "'\'':bad'\'' is not a valid refname" err &&
+       grep "update-ref requires a fully qualified refname e.g. refs/heads/topic" \
+               err &&
+       test_path_is_missing execed
+'
+
 # This must be the last test in this file
 test_expect_success '$EDITOR and friends are unchanged' '
        test_editor_unchanged
index 2524331861869d6b9ee9f80082c09d2ed089e8fe..8979bc340734f860f56c49a4380366dca1d46ee8 100755 (executable)
@@ -5,6 +5,7 @@ test_description='rebase should handle arbitrary git message'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-rebase.sh
 
index 83ffb39d9ffdfd869ecb24e5adbbe11944a73c53..acaf5558dbe2e445efe2989e137b2f9ef5140aaf 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git rebase interactive environment'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 58371d8a5477f47d53384a84ad0dae14457128d9..e75b3d0e07cb13b5d8b4fd3b83b07c35eeddce49 100755 (executable)
@@ -7,6 +7,7 @@ Tests if git rebase --root --onto <newparent> can rebase the root commit.
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 log_with_names () {
index 9fab0d779bb6cf32dbc0ece0a69ad2c2821a598e..e8456831e8ba1a3ff2009ff6339459f6c00055da 100755 (executable)
@@ -5,6 +5,7 @@ test_description='git rebase with its hook(s)'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index ea501f2b42b8cc43394192d11051992605bfb619..f8c4ed78c9ef7ffd0fa970525bf3cab47842b55a 100755 (executable)
@@ -5,6 +5,7 @@ test_description='git rebase --onto A...B'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY/lib-rebase.sh"
 
index 7181f176b8136504b4a9bc5e962cf358b00ffbba..6c61f240cf9874bbdb0190739b66a1c0ed99289f 100755 (executable)
@@ -5,6 +5,7 @@ test_description='git rebase - test patch id computation'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 scramble () {
index 6dabb05a2ad993d0d4d1f41c619f517038f7134f..4711b37a28863e50a06e098bcbcd9417951c9b4d 100755 (executable)
@@ -25,11 +25,11 @@ test_expect_success 'setup' '
 '
 
 #
-# Rebase has lots of useful options like --whitepsace=fix, which are
-# actually all built in terms of flags to git-am.  Since neither
-# --merge nor --interactive (nor any options that imply those two) use
-# git-am, using them together will result in flags like --whitespace=fix
-# being ignored.  Make sure rebase warns the user and aborts instead.
+# Rebase has a couple options which are specific to the apply backend,
+# and several options which are specific to the merge backend.  Flags
+# from the different sets cannot work together, and we do not want to
+# just ignore one of the sets of flags.  Make sure rebase warns the
+# user and aborts instead.
 #
 
 test_rebase_am_only () {
@@ -50,6 +50,11 @@ test_rebase_am_only () {
                test_must_fail git rebase $opt --strategy-option=ours A
        "
 
+       test_expect_success "$opt incompatible with --autosquash" "
+               git checkout B^0 &&
+               test_must_fail git rebase $opt --autosquash A
+       "
+
        test_expect_success "$opt incompatible with --interactive" "
                git checkout B^0 &&
                test_must_fail git rebase $opt --interactive A
@@ -60,9 +65,65 @@ test_rebase_am_only () {
                test_must_fail git rebase $opt --exec 'true' A
        "
 
+       test_expect_success "$opt incompatible with --keep-empty" "
+               git checkout B^0 &&
+               test_must_fail git rebase $opt --keep-empty A
+       "
+
+       test_expect_success "$opt incompatible with --empty=..." "
+               git checkout B^0 &&
+               test_must_fail git rebase $opt --empty=ask A
+       "
+
+       test_expect_success "$opt incompatible with --no-reapply-cherry-picks" "
+               git checkout B^0 &&
+               test_must_fail git rebase $opt --no-reapply-cherry-picks A
+       "
+
+       test_expect_success "$opt incompatible with --reapply-cherry-picks" "
+               git checkout B^0 &&
+               test_must_fail git rebase $opt --reapply-cherry-picks A
+       "
+
+       test_expect_success "$opt incompatible with --update-refs" "
+               git checkout B^0 &&
+               test_must_fail git rebase $opt --update-refs A
+       "
+
+       test_expect_success "$opt incompatible with --root without --onto" "
+               git checkout B^0 &&
+               test_must_fail git rebase $opt --root A
+       "
+
+       test_expect_success "$opt incompatible with rebase.autosquash" "
+               git checkout B^0 &&
+               test_must_fail git -c rebase.autosquash=true rebase $opt A 2>err &&
+               grep -e --no-autosquash err
+       "
+
+       test_expect_success "$opt incompatible with rebase.updateRefs" "
+               git checkout B^0 &&
+               test_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&
+               grep -e --no-update-refs err
+       "
+
+       test_expect_success "$opt okay with overridden rebase.autosquash" "
+               test_when_finished \"git reset --hard B^0\" &&
+               git checkout B^0 &&
+               git -c rebase.autosquash=true rebase --no-autosquash $opt A
+       "
+
+       test_expect_success "$opt okay with overridden rebase.updateRefs" "
+               test_when_finished \"git reset --hard B^0\" &&
+               git checkout B^0 &&
+               git -c rebase.updateRefs=true rebase --no-update-refs $opt A
+       "
 }
 
+# Check options which imply --apply
 test_rebase_am_only --whitespace=fix
 test_rebase_am_only -C4
+# Also check an explicit --apply
+test_rebase_am_only --apply
 
 test_done
index 4859bb8f722314df2fdb67aa51a4419009ce22ec..2fab703d615485020f0692bdc2d39748ba0f72d4 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git rebase interactive with rewording'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 . "$TEST_DIRECTORY"/lib-rebase.sh
index 63acc1ea4dab43e5fc1f9eb26b031f49e3d9a432..a16428bdf54ab9157dfefb7cc9797e4c5dd86e1e 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='rebase topology tests with merges'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-rebase.sh
 
index f6993b7e14d91617b337bfaefa717ec8591fb28f..e1b1e947647462f7cbc9fb2492b2cfe127056f1e 100755 (executable)
@@ -5,6 +5,7 @@ test_description='git rebase --signoff
 This test runs git rebase --signoff and make sure that it works.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # A simple file to commit
index abd66f360213e18ad80b1c7bb2396b06d3e77b28..8e0d03969a2fec13fba015158b3380ec2ee37c42 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='rebase should reread the todo file if an exec modifies it'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-rebase.sh
 
index 70e81363569c32659b79a97b064b0d5711109bc6..4bfc779bb875fe9989bf67833eb5c5477095d72c 100755 (executable)
@@ -8,6 +8,7 @@ test_description='git rebase --fork-point test'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # A---B---D---E    (main)
index 5086e14c02207184bd7c048ee2bc2419ad4d1b34..7f1a5dd3deb10bef921654ef767a7cd53347bfd8 100755 (executable)
@@ -8,6 +8,7 @@ test_description='ensure rebase fast-forwards commits when possible'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 05df964670f49d011bf35b414177a35f812a4c4e..c8172b0852233b6f370eab47c6cc4877db54d8f7 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git rebase across mode change'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index c023fefd681ba62b8296925b2051cffe365e63ac..274699dadb891873cda74fe824bb011d59fc2d87 100755 (executable)
@@ -14,6 +14,7 @@ to the "fixup" command that works with "fixup!", "fixup -C" works with
 "amend!" upon --autosquash.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 . "$TEST_DIRECTORY"/lib-rebase.sh
index b92a3ce46b8cc044e44adfa9cc807c2d2ddb4af2..c614c4f2e4ba285ba7ffc6b7b4384b69f0b9a851 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='rebase behavior when on-disk files are broken'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'set up conflicting branches' '
index 1f4cfc37449186c52a36cbe19f2631df759643e3..2f3e3e2416921c28b27b75dc9f76c20b40401d4d 100755 (executable)
@@ -13,6 +13,7 @@ test_description='test cherry-pick and revert with renames
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 5495eacfec11a904ba24d69439cb1b98b5a353ec..1b2c0d6aca64e05a85ffd6568be17bb5897a297e 100755 (executable)
@@ -11,6 +11,7 @@ test_description='cherry picking and reverting a merge
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 95fe4feaeee98f6432b39ee3a4817f05da5d649b..76d393dc8a307ebef13dfab5e4eb3d88a7a7cd69 100755 (executable)
@@ -5,6 +5,7 @@ test_description='test cherry-picking (and reverting) a root commit'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 7e11bd4a4c5c41c9e326244f598bdc9bb4e4e3ba..b71bad17b853c566caac5828c19453da9c862647 100755 (executable)
@@ -5,6 +5,7 @@ test_description='test cherry-picking with --ff option'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 84a587daf3af0356244c58c6472e202875679200..dd5d92ef302124aeb9d6fe02642489875a9e1b22 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='Test cherry-pick -x and -s'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 pristine_detach () {
index 5841f280fb2d4c3185e0a42b9d9a894d72cc4e79..3a99837d9b15f78f2f8e034d4fdea9a0b88441ed 100755 (executable)
@@ -7,7 +7,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
-if test_have_prereq !ADD_I_USE_BUILTIN,!PERL
+if test_have_prereq !PERL
 then
        skip_all='skipping add -i (scripted) tests, perl not available'
        test_done
@@ -46,6 +46,21 @@ force_color () {
        )
 }
 
+test_expect_success 'warn about add.interactive.useBuiltin' '
+       cat >expect <<-\EOF &&
+       warning: the add.interactive.useBuiltin setting has been removed!
+       See its entry in '\''git help config'\'' for details.
+       No changes.
+       EOF
+
+       for v in = =true =false
+       do
+               git -c "add.interactive.useBuiltin$v" add -p >out 2>actual &&
+               test_must_be_empty out &&
+               test_cmp expect actual || return 1
+       done
+'
+
 test_expect_success 'setup (initial)' '
        echo content >file &&
        git add file &&
@@ -547,15 +562,7 @@ test_expect_success 'split hunk "add -p (edit)"' '
        ! grep "^+15" actual
 '
 
-test_expect_success 'setup ADD_I_USE_BUILTIN check' '
-       result=success &&
-       if ! test_have_prereq ADD_I_USE_BUILTIN
-       then
-               result=failure
-       fi
-'
-
-test_expect_$result 'split hunk "add -p (no, yes, edit)"' '
+test_expect_success 'split hunk "add -p (no, yes, edit)"' '
        test_write_lines 5 10 20 21 30 31 40 50 60 >test &&
        git reset &&
        # test sequence is s(plit), n(o), y(es), e(dit)
@@ -579,7 +586,7 @@ test_expect_success 'split hunk with incomplete line at end' '
        test_must_fail git grep --cached before
 '
 
-test_expect_$result 'edit, adding lines to the first hunk' '
+test_expect_success 'edit, adding lines to the first hunk' '
        test_write_lines 10 11 20 30 40 50 51 60 >test &&
        git reset &&
        tr _ " " >patch <<-EOF &&
index e3cf0ffbe59c449b218cafedd74bd3cf07e82ff1..d3e428ff46eb5c99d98a8fe27b54cf98d6d2a936 100755 (executable)
@@ -4,6 +4,7 @@
 
 test_description='git mktag: tag object verify test'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 ###########################################################
index 4c661d4d54a779f8bc056c62e5d9e864401420c7..67fd2345affd78507a6cb714b48565ed190cb5af 100755 (executable)
@@ -12,7 +12,7 @@ create_crlf_ref () {
        cat >.crlf-orig-$branch.txt &&
        cat .crlf-orig-$branch.txt | append_cr >.crlf-message-$branch.txt &&
        grep 'Subject' .crlf-orig-$branch.txt | tr '\n' ' ' | sed 's/[ ]*$//' | tr -d '\n' >.crlf-subject-$branch.txt &&
-       grep 'Body' .crlf-message-$branch.txt >.crlf-body-$branch.txt || true &&
+       grep 'Body' .crlf-orig-$branch.txt | append_cr >.crlf-body-$branch.txt &&
        LIB_CRLF_BRANCHES="${LIB_CRLF_BRANCHES} ${branch}" &&
        test_tick &&
        hash=$(git commit-tree HEAD^{tree} -p HEAD -F .crlf-message-${branch}.txt) &&
index de1da4673da9626f0c155fd4c67597a403870ece..012f155e10ae9f9e1fcbd3472cd9a789bae919ec 100755 (executable)
@@ -2281,7 +2281,7 @@ test_expect_success 'format-patch --attach cover-letter only is non-multipart' '
        test_line_count = 1 output
 '
 
-test_expect_success 'format-patch --pretty=mboxrd' '
+test_expect_success '-c format.mboxrd format-patch' '
        sp=" " &&
        cat >msg <<-INPUT_END &&
        mboxrd should escape the body
@@ -2316,7 +2316,9 @@ test_expect_success 'format-patch --pretty=mboxrd' '
        INPUT_END
 
        C=$(git commit-tree HEAD^^{tree} -p HEAD <msg) &&
-       git format-patch --pretty=mboxrd --stdout -1 $C~1..$C >patch &&
+       git -c format.mboxrd format-patch --stdout -1 $C~1..$C >patch &&
+       git format-patch --pretty=mboxrd --stdout -1 $C~1..$C >compat &&
+       test_cmp patch compat &&
        git grep -h --no-index -A11 \
                "^>From could trip up a loose mbox parser" patch >actual &&
        test_cmp expect actual
index f3e20dd5bba202ba5d5589e897d753672096b866..b298f220e01fe6de17a5dfe608550a5a57dcf9c6 100755 (executable)
@@ -1638,7 +1638,7 @@ test_expect_success 'no effect on diff from --color-moved with --word-diff' '
        test_cmp expect actual
 '
 
-test_expect_success !SANITIZE_LEAK 'no effect on show from --color-moved with --word-diff' '
+test_expect_success 'no effect on show from --color-moved with --word-diff' '
        git show --color-moved --word-diff >actual &&
        git show --word-diff >expect &&
        test_cmp expect actual
@@ -2024,7 +2024,7 @@ test_expect_success '--color-moved rewinds for MIN_ALNUM_COUNT' '
        test_cmp expected actual
 '
 
-test_expect_success !SANITIZE_LEAK 'move detection with submodules' '
+test_expect_success 'move detection with submodules' '
        test_create_repo bananas &&
        echo ripe >bananas/recipe &&
        git -C bananas add recipe &&
diff --git a/t/t4018/java-class-brace-on-separate-line b/t/t4018/java-class-brace-on-separate-line
new file mode 100644 (file)
index 0000000..8795acd
--- /dev/null
@@ -0,0 +1,6 @@
+class RIGHT
+{
+    static int ONE;
+    static int TWO;
+    static int ChangeMe;
+}
diff --git a/t/t4018/java-class-space-before-type-parameters b/t/t4018/java-class-space-before-type-parameters
new file mode 100644 (file)
index 0000000..0bdef1d
--- /dev/null
@@ -0,0 +1,6 @@
+class RIGHT <TYPE, PARAMS, AFTER, SPACE> {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    private A ChangeMe;
+}
diff --git a/t/t4018/java-class-type-parameters b/t/t4018/java-class-type-parameters
new file mode 100644 (file)
index 0000000..579aa7a
--- /dev/null
@@ -0,0 +1,6 @@
+class RIGHT<A, B> {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    private A ChangeMe;
+}
diff --git a/t/t4018/java-class-type-parameters-implements b/t/t4018/java-class-type-parameters-implements
new file mode 100644 (file)
index 0000000..b8038b1
--- /dev/null
@@ -0,0 +1,6 @@
+class RIGHT<A, B> implements List<A> {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    private A ChangeMe;
+}
diff --git a/t/t4018/java-interface-type-parameters b/t/t4018/java-interface-type-parameters
new file mode 100644 (file)
index 0000000..a4baa1a
--- /dev/null
@@ -0,0 +1,6 @@
+interface RIGHT<A, B> {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    public B foo(A ChangeMe);
+}
diff --git a/t/t4018/java-interface-type-parameters-extends b/t/t4018/java-interface-type-parameters-extends
new file mode 100644 (file)
index 0000000..31d7fb3
--- /dev/null
@@ -0,0 +1,6 @@
+interface RIGHT<A, B> extends Function<A, B> {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    public B foo(A ChangeMe);
+}
diff --git a/t/t4018/java-non-sealed b/t/t4018/java-non-sealed
new file mode 100644 (file)
index 0000000..069087c
--- /dev/null
@@ -0,0 +1,8 @@
+public abstract sealed class SealedClass {
+    public static non-sealed class RIGHT extends SealedClass {
+        static int ONE;
+        static int TWO;
+        static int THREE;
+        private int ChangeMe;
+    }
+}
diff --git a/t/t4018/java-record b/t/t4018/java-record
new file mode 100644 (file)
index 0000000..97aa819
--- /dev/null
@@ -0,0 +1,6 @@
+public record RIGHT(int comp1, double comp2, String comp3) {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    static int ChangeMe;
+}
diff --git a/t/t4018/java-record-space-before-components b/t/t4018/java-record-space-before-components
new file mode 100644 (file)
index 0000000..9827f22
--- /dev/null
@@ -0,0 +1,6 @@
+public record RIGHT (String components, String after, String space) {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    static int ChangeMe;
+}
diff --git a/t/t4018/java-record-type-parameters b/t/t4018/java-record-type-parameters
new file mode 100644 (file)
index 0000000..f62a035
--- /dev/null
@@ -0,0 +1,6 @@
+public record RIGHT<A, N extends Number>(A comp1, N comp2, int comp3) {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    static int ChangeMe;
+}
diff --git a/t/t4018/java-sealed b/t/t4018/java-sealed
new file mode 100644 (file)
index 0000000..785fbc6
--- /dev/null
@@ -0,0 +1,7 @@
+public abstract sealed class Sealed { // RIGHT
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    public final class ChangeMe extends Sealed {
+    }
+}
diff --git a/t/t4018/java-sealed-permits b/t/t4018/java-sealed-permits
new file mode 100644 (file)
index 0000000..18dd489
--- /dev/null
@@ -0,0 +1,6 @@
+public abstract sealed class RIGHT permits PermittedA, PermittedB {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    private int ChangeMe;
+}
diff --git a/t/t4018/java-sealed-type-parameters b/t/t4018/java-sealed-type-parameters
new file mode 100644 (file)
index 0000000..e6530c4
--- /dev/null
@@ -0,0 +1,6 @@
+public abstract sealed class RIGHT<A, B> {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    private int ChangeMe;
+}
diff --git a/t/t4018/java-sealed-type-parameters-implements-permits b/t/t4018/java-sealed-type-parameters-implements-permits
new file mode 100644 (file)
index 0000000..bd6e6d3
--- /dev/null
@@ -0,0 +1,6 @@
+public abstract sealed class RIGHT<A, B> implements List<A> permits PermittedA, PermittedB {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    private int ChangeMe;
+}
diff --git a/t/t4018/java-sealed-type-parameters-permits b/t/t4018/java-sealed-type-parameters-permits
new file mode 100644 (file)
index 0000000..25a0da6
--- /dev/null
@@ -0,0 +1,6 @@
+public abstract sealed class RIGHT<A, B> permits PermittedA, PermittedB {
+    static int ONE;
+    static int TWO;
+    static int THREE;
+    private int ChangeMe;
+}
index 7cb99092938d7dd64e8ab6f4e147fc5f70c0a310..787605ce3fd076b101d1c825fca174020ae7ff46 100755 (executable)
@@ -52,8 +52,8 @@ test_expect_success setup '
 '
 
 test_expect_success 'cross renames to be detected for regular files' '
-
-       git diff-tree five six -r --name-status -B -M | sort >actual &&
+       git diff-tree five six -r --name-status -B -M >out &&
+       sort out >actual &&
        {
                echo "R100      foo     bar" &&
                echo "R100      bar     foo"
@@ -63,8 +63,8 @@ test_expect_success 'cross renames to be detected for regular files' '
 '
 
 test_expect_success 'cross renames to be detected for typechange' '
-
-       git diff-tree one two -r --name-status -B -M | sort >actual &&
+       git diff-tree one two -r --name-status -B -M >out &&
+       sort out >actual &&
        {
                echo "R100      foo     bar" &&
                echo "R100      bar     foo"
@@ -74,8 +74,8 @@ test_expect_success 'cross renames to be detected for typechange' '
 '
 
 test_expect_success 'moves and renames' '
-
-       git diff-tree three four -r --name-status -B -M | sort >actual &&
+       git diff-tree three four -r --name-status -B -M >out &&
+       sort out >actual &&
        {
                # see -B -M (#6) in t4008
                echo "C100      foo     bar" &&
index 29e49d22902dd7e7566f2662174475bf35c6ebed..9f6043dabaccdcbbaecccf2b2f0248ca132c946e 100755 (executable)
@@ -34,12 +34,12 @@ test_expect_success 'setup' '
        100644 blob $(test_oid hash2)   foo
        EOF
 
-       echo "$(test_oid val1)" > foo &&
+       test_oid val1 > foo &&
        git add foo &&
        git commit -m "initial" &&
        git cat-file -p HEAD: > actual &&
        test_cmp expect_initial actual &&
-       echo "$(test_oid val2)" > foo &&
+       test_oid val2 > foo &&
        git commit -a -m "update" &&
        git cat-file -p HEAD: > actual &&
        test_cmp expect_update actual
index fab351b48a1f52b450f3911aa6edafae70015613..9b46c4c1befece1f3e231b9672291b7a538c3f6f 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='diff --relative tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -162,6 +164,35 @@ check_diff_relative_option subdir file2 true --no-relative --relative
 check_diff_relative_option . file2 false --no-relative --relative=subdir
 check_diff_relative_option . file2 true --no-relative --relative=subdir
 
+test_expect_success 'external diff with --relative' '
+       test_when_finished "git reset --hard" &&
+       echo changed >file1 &&
+       echo changed >subdir/file2 &&
+
+       write_script mydiff <<-\EOF &&
+       # hacky pretend diff; the goal here is just to make sure we got
+       # passed sensible input that we _could_ diff, without relying on
+       # the specific output of a system diff tool.
+       echo "diff a/$1 b/$1" &&
+       echo "--- a/$1" &&
+       echo "+++ b/$1" &&
+       echo "@@ -1 +0,0 @@" &&
+       sed "s/^/-/" "$2" &&
+       sed "s/^/+/" "$5"
+       EOF
+
+       cat >expect <<-\EOF &&
+       diff a/file2 b/file2
+       --- a/file2
+       +++ b/file2
+       @@ -1 +0,0 @@
+       -other content
+       +changed
+       EOF
+       GIT_EXTERNAL_DIFF=./mydiff git diff --relative=subdir >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'setup diff --relative unmerged' '
        test_commit zero file0 &&
        test_commit base subdir/file0 &&
index 0ae0cd3a524da312d7f1cc7b1d4b0f6ed7b2b529..ffaf69335f7b224f76ece7228435a4ce057e5c83 100755 (executable)
@@ -86,4 +86,14 @@ test_expect_success 'diff-files -3' '
        test_cmp diff-files-3.expect diff-files-3.actual
 '
 
+test_expect_success 'diff --stat' '
+       for path in $paths
+       do
+               echo " $path | Unmerged" || return 1
+       done >diff-stat.expect &&
+       echo " 0 files changed" >>diff-stat.expect &&
+       git diff --cached --stat >diff-stat.actual &&
+       test_cmp diff-stat.expect diff-stat.actual
+'
+
 test_done
index b5c281edaa7037097f3c8fad28c1bb1994607a20..3ee27e277dca1cd95484ec9ef3b1251e38b22dde 100755 (executable)
@@ -8,6 +8,7 @@ test_description='test --stat output of various commands'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
index 3feadf0e3595b2950dd6b5698ba0f25969de8b93..4e9fa0403d3631fdeafd0e90e3ae80894930c2e9 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='diff --no-index'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 294fb5531372d597a5a551b40e66437d7599e766..05c88f8cdf01d98b61eacff08b32cb57320e7d3b 100755 (executable)
@@ -10,7 +10,7 @@ test_expect_success 'create bogus tree' '
        bogus_tree=$(
                printf "100644 fooQ$name" |
                q_to_nul |
-               git hash-object -w --stdin -t tree
+               git hash-object --literally -w --stdin -t tree
        )
 '
 
index 54614b814dbc858e15fd82b8bc563ab9aae5fae8..2501c89c1c91ec7ddc02f4e285aa4c10c32b98f6 100755 (executable)
@@ -29,7 +29,7 @@ make_tree () {
                make_tree_entry "$1" "$2" "$3"
                shift; shift; shift
        done |
-       git hash-object -w -t tree --stdin
+       git hash-object --literally -w -t tree --stdin
 }
 
 # this is kind of a convoluted setup, but matches
index 28f42a4046e08ed61fcf2f300b826c33136e9e98..f60f5cbd65f049e1d3148bae352f3c6032454f2e 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='behavior of diff when reading objects in a partial clone'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'git show batches blobs' '
index 1618a6dbc7c718677cf669b5f6fc96f2682992fb..e9a87d761d168323ada1e58ecd9df719cc7511b3 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='patching from inconvenient places'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 6bc3fb97a754bf242f8baf1ba867aa9f7fa93d0d..d3502c6fddf5a6a69bde769b1f808c9431d1e38e 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git apply with weird postimage filenames'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index cdad4b688078adc24da922bce14e130982f4484e..78cf1c880e69832ecd796ec11af8eff6ce19bd80 100755 (executable)
@@ -345,6 +345,21 @@ test_expect_success 'am with failing applypatch-msg hook' '
        test_cmp_rev first HEAD
 '
 
+test_expect_success 'am with failing applypatch-msg hook (no verify)' '
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
+       git checkout first &&
+       test_hook applypatch-msg <<-\EOF &&
+       echo hook-message >"$1"
+       exit 1
+       EOF
+       git am --no-verify patch1 &&
+       test_path_is_missing .git/rebase-apply &&
+       git diff --exit-code second &&
+       git log -1 --format=format:%B >actual &&
+       test_cmp msg actual
+'
+
 test_expect_success 'am with pre-applypatch hook' '
        rm -fr .git/rebase-apply &&
        git reset --hard &&
@@ -374,6 +389,23 @@ test_expect_success 'am with failing pre-applypatch hook' '
        test_cmp_rev first HEAD
 '
 
+test_expect_success 'am with failing pre-applypatch hook (no verify)' '
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
+       git checkout first &&
+       touch empty-file &&
+       test_hook pre-applypatch <<-\EOF &&
+       rm empty-file
+       exit 1
+       EOF
+       git am --no-verify patch1 &&
+       test_path_is_missing .git/rebase-apply &&
+       test_path_is_file empty-file &&
+       git diff --exit-code second &&
+       git log -1 --format=format:%B >actual &&
+       test_cmp msg actual
+'
+
 test_expect_success 'am with post-applypatch hook' '
        rm -fr .git/rebase-apply &&
        git reset --hard &&
@@ -1033,7 +1065,7 @@ test_expect_success 'am --patch-format=mboxrd handles mboxrd' '
        >From extra escape for reversibility
        INPUT_END
        git commit -F msg &&
-       git format-patch --pretty=mboxrd --stdout -1 >mboxrd1 &&
+       git -c format.mboxrd format-patch --stdout -1 >mboxrd1 &&
        grep "^>From could trip up a loose mbox parser" mboxrd1 &&
        git checkout -f first &&
        git am --patch-format=mboxrd mboxrd1 &&
index 4c68245acad30534d7e3e1f6e980e683d8b5aafd..9f2edba1f833a7127589f9c4883b91bf99bc00b1 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test subject preservation with format-patch | am'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 make_patches() {
index cd1cab3e54b9170d5751279bd68e02dbc3cbcad2..fa7f987284b117607fa051c91dd439749da546d4 100755 (executable)
@@ -1022,4 +1022,69 @@ test_expect_success '--mailmap enables mailmap in cat-file for annotated tag obj
        test_cmp expect actual
 '
 
+test_expect_success 'git cat-file -s returns correct size with --use-mailmap' '
+       test_when_finished "rm .mailmap" &&
+       cat >.mailmap <<-\EOF &&
+       C O Mitter <committer@example.com> Orig <orig@example.com>
+       EOF
+       git cat-file commit HEAD >commit.out &&
+       echo $(wc -c <commit.out) >expect &&
+       git cat-file --use-mailmap commit HEAD >commit.out &&
+       echo $(wc -c <commit.out) >>expect &&
+       git cat-file -s HEAD >actual &&
+       git cat-file --use-mailmap -s HEAD >>actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git cat-file -s returns correct size with --use-mailmap for tag objects' '
+       test_when_finished "rm .mailmap" &&
+       cat >.mailmap <<-\EOF &&
+       Orig <orig@example.com> C O Mitter <committer@example.com>
+       EOF
+       git tag -a -m "annotated tag" v3 &&
+       git cat-file tag v3 >tag.out &&
+       echo $(wc -c <tag.out) >expect &&
+       git cat-file --use-mailmap tag v3 >tag.out &&
+       echo $(wc -c <tag.out) >>expect &&
+       git cat-file -s v3 >actual &&
+       git cat-file --use-mailmap -s v3 >>actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check returns correct size with --use-mailmap' '
+       test_when_finished "rm .mailmap" &&
+       cat >.mailmap <<-\EOF &&
+       C O Mitter <committer@example.com> Orig <orig@example.com>
+       EOF
+       git cat-file commit HEAD >commit.out &&
+       commit_size=$(wc -c <commit.out) &&
+       commit_sha=$(git rev-parse HEAD) &&
+       echo $commit_sha commit $commit_size >expect &&
+       git cat-file --use-mailmap commit HEAD >commit.out &&
+       commit_size=$(wc -c <commit.out) &&
+       echo $commit_sha commit $commit_size >>expect &&
+       echo "HEAD" >in &&
+       git cat-file --batch-check <in >actual &&
+       git cat-file --use-mailmap --batch-check <in >>actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-command returns correct size with --use-mailmap' '
+       test_when_finished "rm .mailmap" &&
+       cat >.mailmap <<-\EOF &&
+       C O Mitter <committer@example.com> Orig <orig@example.com>
+       EOF
+       git cat-file commit HEAD >commit.out &&
+       commit_size=$(wc -c <commit.out) &&
+       commit_sha=$(git rev-parse HEAD) &&
+       echo $commit_sha commit $commit_size >expect &&
+       git cat-file --use-mailmap commit HEAD >commit.out &&
+       commit_size=$(wc -c <commit.out) &&
+       echo $commit_sha commit $commit_size >>expect &&
+       echo "info HEAD" >in &&
+       git cat-file --batch-command <in >actual &&
+       git cat-file --use-mailmap --batch-command <in >>actual &&
+       test_cmp expect actual
+'
+
 test_done
index fedb7ca7493f650ce9def7179cf63fa11ef90d70..4cf8a7766768bfed03c7016ffdf70c10802c0708 100755 (executable)
@@ -156,7 +156,7 @@ test_expect_success 'NUL termination with --reflog --pretty=oneline' '
        for r in $revs
        do
                git show -s --pretty=oneline "$r" >raw &&
-               cat raw | lf_to_nul || exit 1
+               cat raw | lf_to_nul || return 1
        done >expect &&
        # the trailing NUL is already produced so we do not need to
        # output another one
@@ -1094,4 +1094,31 @@ test_expect_success EXPENSIVE,SIZE_T_IS_64BIT 'log --pretty with huge commit mes
        test_cmp expect error
 '
 
+# pretty-formats note wide char limitations, and add tests
+test_expect_failure 'wide and decomposed characters column counting' '
+
+# from t/lib-unicode-nfc-nfd.sh hex values converted to octal
+       utf8_nfc=$(printf "\303\251") && # e acute combined.
+       utf8_nfd=$(printf "\145\314\201") && # e with a combining acute (i.e. decomposed)
+       utf8_emoji=$(printf "\360\237\221\250") &&
+
+# replacement character when requesting a wide char fits in a single display colum.
+# "half wide" alternative could be a plain ASCII dot `.`
+       utf8_vert_ell=$(printf "\342\213\256") &&
+
+# use ${xxx} here!
+       nfc10="${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}" &&
+       nfd10="${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}" &&
+       emoji5="${utf8_emoji}${utf8_emoji}${utf8_emoji}${utf8_emoji}${utf8_emoji}" &&
+# emoji5 uses 10 display columns
+
+       test_commit "abcdefghij" &&
+       test_commit --no-tag "${nfc10}" &&
+       test_commit --no-tag "${nfd10}" &&
+       test_commit --no-tag "${emoji5}" &&
+       printf "${utf8_emoji}..${utf8_emoji}${utf8_vert_ell}\n${utf8_nfd}..${utf8_nfd}${utf8_nfd}\n${utf8_nfc}..${utf8_nfc}${utf8_nfc}\na..ij\n" >expected &&
+       git log --format="%<(5,mtrunc)%s" -4 >actual &&
+       test_cmp expected actual
+'
+
 test_done
index ac9e4d0928593cd37b4a79e27f5fe99b6e4a3faf..c6540e822fbdc56f068ae1b8d991175303bff038 100755 (executable)
@@ -315,4 +315,26 @@ test_expect_success 'line-log with --before' '
        test_cmp expect actual
 '
 
+test_expect_success 'setup tests for zero-width regular expressions' '
+       cat >expect <<-EOF
+       Modify func1() in file.c
+       Add func1() and func2() in file.c
+       EOF
+'
+
+test_expect_success 'zero-width regex $ matches any function name' '
+       git log --format="%s" --no-patch "-L:$:file.c" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'zero-width regex ^ matches any function name' '
+       git log --format="%s" --no-patch "-L:^:file.c" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'zero-width regex .* matches any function name' '
+       git log --format="%s" --no-patch "-L:.*:file.c" >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 30a219894bb45f2655596fe02e8a7d001a35eabd..e89e1f54b6caed2e97d04aaf4672a0491a398acf 100755 (executable)
@@ -10,7 +10,7 @@ test_expect_success 'setup' '
 
        git cat-file commit HEAD |
        sed "/^author /s/>/>-<>/" >broken_email.commit &&
-       git hash-object -w -t commit broken_email.commit >broken_email.hash &&
+       git hash-object --literally -w -t commit broken_email.commit >broken_email.hash &&
        git update-ref refs/heads/broken_email $(cat broken_email.hash)
 '
 
@@ -46,7 +46,7 @@ test_expect_success 'git log --format with broken author email' '
 munge_author_date () {
        git cat-file commit "$1" >commit.orig &&
        sed "s/^\(author .*>\) [0-9]*/\1 $2/" <commit.orig >commit.munge &&
-       git hash-object -w -t commit commit.munge
+       git hash-object --literally -w -t commit commit.munge
 }
 
 test_expect_success 'unparsable dates produce sentinel value' '
index 53a4af324495dda3738388af31dcecf31f8ee1ee..590fce95e90a8bd751daf13c0696d0a39b8f7d1b 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='log/show --expand-tabs'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 HT="   "
index 54be7da1611212143d843d175bf48f73bb83b417..45f1d4f95e5d5af7cff901639b5ecaf541846968 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='git am with corrupt input'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 make_mbox_with_nul () {
index 2369c4e17ad8d4ee6645ddc6f87b87896603dec8..1015273bc827aeeef1e75edbf6c819e04719bde6 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='test format=flowed support of git am'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index aed8f4de3d670143753bfbce1dd40c5664abd8c6..f26d7fd2dbd35ce40534a5d76f9903361f4b7904 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='am --interactive tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'set up patches to apply' '
index cac85591b52b3b3d7d0a7aa7f5dbc0c32d8ebdda..250f721795b5bd2ecba3090b7e181c50673540aa 100755 (executable)
@@ -141,7 +141,7 @@ test_expect_success 'test conflict notices and such' '
 #   Commit O: foo, olddir/{a,b,c}
 #   Commit A: modify foo, newdir/{a,b,c}
 #   Commit B: modify foo differently & rename foo -> olddir/bar
-#   Expected: CONFLICT(content) for for newdir/bar (not olddir/bar or foo)
+#   Expected: CONFLICT(content) for newdir/bar (not olddir/bar or foo)
 
 test_expect_success 'directory rename + content conflict' '
        # Setup
@@ -653,7 +653,7 @@ test_expect_success 'mod6: chains of rename/rename(1to2) and add/add via collidi
 #   Commit O: foo, olddir/{a,b,c}
 #   Commit A: delete foo, rename olddir/ -> newdir/, add newdir/bar/file
 #   Commit B: modify foo & rename foo -> olddir/bar
-#   Expected: CONFLICT(content) for for newdir/bar (not olddir/bar or foo)
+#   Expected: CONFLICT(content) for newdir/bar (not olddir/bar or foo)
 
 test_expect_success 'directory rename + rename/delete + modify/delete + directory/file conflict' '
        # Setup
@@ -860,4 +860,66 @@ test_expect_success '--stdin with both a successful and a conflicted merge' '
        test_cmp expect actual
 '
 
+
+test_expect_success '--merge-base is incompatible with --stdin' '
+       test_must_fail git merge-tree --merge-base=side1 --stdin 2>expect &&
+
+       grep "^fatal: --merge-base is incompatible with --stdin" expect
+'
+
+# specify merge-base as parent of branch2
+# git merge-tree --write-tree --merge-base=c2 c1 c3
+#   Commit c1: add file1
+#   Commit c2: add file2 after c1
+#   Commit c3: add file3 after c2
+#   Expected: add file3, and file2 does NOT appear
+
+test_expect_success 'specify merge-base as parent of branch2' '
+       # Setup
+       test_when_finished "rm -rf base-b2-p" &&
+       git init base-b2-p &&
+       test_commit -C base-b2-p c1 file1 &&
+       test_commit -C base-b2-p c2 file2 &&
+       test_commit -C base-b2-p c3 file3 &&
+
+       # Testing
+       TREE_OID=$(git -C base-b2-p merge-tree --write-tree --merge-base=c2 c1 c3) &&
+
+       q_to_tab <<-EOF >expect &&
+       100644 blob $(git -C base-b2-p rev-parse c1:file1)Qfile1
+       100644 blob $(git -C base-b2-p rev-parse c3:file3)Qfile3
+       EOF
+
+       git -C base-b2-p ls-tree $TREE_OID >actual &&
+       test_cmp expect actual
+'
+
+# Since the earlier tests have verified that individual merge-tree calls
+# are doing the right thing, this test case is only used to verify that
+# we can also trigger merges via --stdin, and that when we do we get
+# the same answer as running a bunch of separate merges.
+
+test_expect_success 'check the input format when --stdin is passed' '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       test_commit -C repo c1 &&
+       test_commit -C repo c2 &&
+       test_commit -C repo c3 &&
+       printf "c1 c3\nc2 -- c1 c3\nc2 c3" | git -C repo merge-tree --stdin >actual &&
+
+       printf "1\0" >expect &&
+       git -C repo merge-tree --write-tree -z c1 c3 >>expect &&
+       printf "\0" >>expect &&
+
+       printf "1\0" >>expect &&
+       git -C repo merge-tree --write-tree -z --merge-base=c2 c1 c3 >>expect &&
+       printf "\0" >>expect &&
+
+       printf "1\0" >>expect &&
+       git -C repo merge-tree --write-tree -z c2 c3 >>expect &&
+       printf "\0" >>expect &&
+
+       test_cmp expect actual
+'
+
 test_done
index d473048138466322e505f4b3b249e89f19101b0b..918a2fc7c69432c26e3135460d62f9b27f0e3dbd 100755 (executable)
@@ -105,6 +105,18 @@ check_added() {
        '
 }
 
+check_mtime() {
+       dir=$1
+       path_in_archive=$2
+       mtime=$3
+
+       test_expect_success " validate mtime of $path_in_archive" '
+               test-tool chmtime --get $dir/$path_in_archive >actual.mtime &&
+               echo $mtime >expect.mtime &&
+               test_cmp expect.mtime actual.mtime
+       '
+}
+
 test_expect_success 'setup' '
        test_oid_cache <<-EOF
        obj sha1:19f9c8273ec45a8938e6999cb59b3ff66739902a
@@ -174,6 +186,13 @@ test_expect_success 'git archive' '
 
 check_tar b
 
+test_expect_success 'git archive --mtime' '
+       git archive --mtime=2002-02-02T02:02:02-0200 HEAD >with_mtime.tar
+'
+
+check_tar with_mtime
+check_mtime with_mtime a/a 1012622522
+
 test_expect_success 'git archive --prefix=prefix/' '
        git archive --prefix=prefix/ HEAD >with_prefix.tar
 '
@@ -402,11 +421,11 @@ test_expect_success GZIP 'extract tgz file (external gzip)' '
 
 test_expect_success 'archive and :(glob)' '
        git archive -v HEAD -- ":(glob)**/sh" >/dev/null 2>actual &&
-       cat >expect <<EOF &&
-a/
-a/bin/
-a/bin/sh
-EOF
+       cat >expect <<-\EOF &&
+       a/
+       a/bin/
+       a/bin/sh
+       EOF
        test_cmp expect actual
 '
 
index 2f6eef5e3720452df49c4810bd9f4f63dc1167fb..04d300eeda760033c0e72beb2aee1235c664c457 100755 (executable)
@@ -3,6 +3,7 @@
 test_description='git archive attribute tests'
 
 TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 SUBSTFORMAT='%H (%h)%n'
index ae508e21623fb4af7b55c1c8afe27556b24bb232..9f2c6da80e8cd97c12eb5452e940a9022b2e3a84 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test corner cases of git-archive'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # the 10knuls.tar file is used to test for an empty git generated tar
index b0095ab41d30aadda45c7dd6e812fd0a5f40ab26..f89809be53cf335295e91b91af42de0c1ccf2b5b 100755 (executable)
@@ -4,6 +4,8 @@
 #
 
 test_description='pack index with 64-bit offsets and object CRC'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -263,7 +265,7 @@ tag guten tag
 This is an invalid tag.
 EOF
 
-       tag=$(git hash-object -t tag -w --stdin <wrong-tag) &&
+       tag=$(git hash-object -t tag -w --stdin --literally <wrong-tag) &&
        pack1=$(echo $tag $sha | git pack-objects tag-test) &&
        echo remove tag object &&
        thirtyeight=${tag#??} &&
index 51973f4a512bf914ee0fb50d5472d050f241a2ed..846c5ca7d341ccb1705e4bded2c910c430337f3f 100755 (executable)
@@ -6,6 +6,8 @@
 test_description='git-pack-object with missing base
 
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Create A-B chain
index 6d693eef82f74a72ad4f29c399e297427c892d12..7d8dee41b0ded364d9b364ab46296c20b2c8eee1 100755 (executable)
@@ -428,8 +428,9 @@ test_bitmap_cases () {
                        test_line_count = 2 packs &&
                        test_line_count = 2 bitmaps &&
 
-                       git rev-list --use-bitmap-index HEAD 2>err &&
-                       grep "ignoring extra bitmap file" err
+                       GIT_TRACE2_EVENT=$(pwd)/trace2.txt git rev-list --use-bitmap-index HEAD &&
+                       grep "opened bitmap" trace2.txt &&
+                       grep "ignoring extra bitmap" trace2.txt
                )
        '
 }
index 9d8e249ae8b7f38c4c941bd76ef904cdfe1b3d27..230cb3871223e1f28482c841093cb7a0e6873754 100755 (executable)
@@ -14,6 +14,7 @@ what currently happens. If that changes, these tests should be revisited.
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'disable reflogs' '
index cc4cfaa9d3712684f57f34372a4f40d0abf11f7e..ceaa6700a27230cdea34bd65aeff2db34c53ad64 100755 (executable)
@@ -59,7 +59,7 @@ test_expect_success 'setup' '
 test_expect_success 'set up base packfile and variables' '
        # the hash of this content starts with ff, which
        # makes some later computations much simpler
-       echo $(test_oid oidfff) >file &&
+       test_oid oidfff >file &&
        git add file &&
        git commit -m base &&
        git repack -ad &&
index 73a241743aa50101c032b53566de7ba2e7a3b410..82734b9a3c4420a63b91901d165ae06f4b823ea7 100755 (executable)
@@ -63,13 +63,16 @@ TEST_PASSES_SANITIZE_LEAK=true
 # Note that the two variants of "file" must be similar enough to convince git
 # to create the delta.
 make_pack () {
-       {
-               printf '%s\n' "-$(git rev-parse $2)"
-               printf '%s dummy\n' "$(git rev-parse $1:dummy)"
-               printf '%s file\n' "$(git rev-parse $1:file)"
-       } |
-       git pack-objects --stdout |
-       git index-pack --stdin --fix-thin
+       ln1=$(git rev-parse "$2") &&
+       ln2=$(git rev-parse "$1:dummy") &&
+       ln3=$(git rev-parse "$1:file") &&
+       cat >list <<-EOF
+       -$ln1
+       $ln2 dummy
+       $ln3 file
+       EOF
+       git pack-objects --stdout <list >pack &&
+       git index-pack --stdin --fix-thin <pack
 }
 
 test_expect_success 'setup' '
index bb633c9b099ed034c275389cb8e9e9da16efe704..b26d476c646fdb63a9569d98bc9d2792dd6f3426 100755 (executable)
@@ -5,6 +5,7 @@ test_description='git pack-objects using object filtering'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Test blob:none filter.
@@ -24,8 +25,9 @@ parse_verify_pack_blob_oid () {
 }
 
 test_expect_success 'verify blob count in normal packfile' '
-       git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 |
-       test_parse_ls_files_stage_oids |
+       git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 \
+               >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        git -C r1 pack-objects --revs --stdout >all.pack <<-EOF &&
@@ -123,8 +125,8 @@ test_expect_success 'setup r2' '
 '
 
 test_expect_success 'verify blob count in normal packfile' '
-       git -C r2 ls-files -s large.1000 large.10000 |
-       test_parse_ls_files_stage_oids |
+       git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        git -C r2 pack-objects --revs --stdout >all.pack <<-EOF &&
@@ -161,8 +163,8 @@ test_expect_success 'verify blob:limit=1000' '
 '
 
 test_expect_success 'verify blob:limit=1001' '
-       git -C r2 ls-files -s large.1000 |
-       test_parse_ls_files_stage_oids |
+       git -C r2 ls-files -s large.1000 >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        git -C r2 pack-objects --revs --stdout --filter=blob:limit=1001 >filter.pack <<-EOF &&
@@ -179,8 +181,8 @@ test_expect_success 'verify blob:limit=1001' '
 '
 
 test_expect_success 'verify blob:limit=10001' '
-       git -C r2 ls-files -s large.1000 large.10000 |
-       test_parse_ls_files_stage_oids |
+       git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        git -C r2 pack-objects --revs --stdout --filter=blob:limit=10001 >filter.pack <<-EOF &&
@@ -197,8 +199,8 @@ test_expect_success 'verify blob:limit=10001' '
 '
 
 test_expect_success 'verify blob:limit=1k' '
-       git -C r2 ls-files -s large.1000 |
-       test_parse_ls_files_stage_oids |
+       git -C r2 ls-files -s large.1000 >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        git -C r2 pack-objects --revs --stdout --filter=blob:limit=1k >filter.pack <<-EOF &&
@@ -215,8 +217,8 @@ test_expect_success 'verify blob:limit=1k' '
 '
 
 test_expect_success 'verify explicitly specifying oversized blob in input' '
-       git -C r2 ls-files -s large.1000 large.10000 |
-       test_parse_ls_files_stage_oids |
+       git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        echo HEAD >objects &&
@@ -233,8 +235,8 @@ test_expect_success 'verify explicitly specifying oversized blob in input' '
 '
 
 test_expect_success 'verify blob:limit=1m' '
-       git -C r2 ls-files -s large.1000 large.10000 |
-       test_parse_ls_files_stage_oids |
+       git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        git -C r2 pack-objects --revs --stdout --filter=blob:limit=1m >filter.pack <<-EOF &&
@@ -264,6 +266,44 @@ test_expect_success 'verify normal and blob:limit packfiles have same commits/tr
        test_cmp expected observed
 '
 
+test_expect_success 'verify small limit and big limit results in small limit' '
+       git -C r2 ls-files -s large.1000 >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
+       sort >expected &&
+
+       git -C r2 pack-objects --revs --stdout --filter=blob:limit=1001 \
+               --filter=blob:limit=10001 >filter.pack <<-EOF &&
+       HEAD
+       EOF
+       git -C r2 index-pack ../filter.pack &&
+
+       git -C r2 verify-pack -v ../filter.pack >verify_result &&
+       grep blob verify_result |
+       parse_verify_pack_blob_oid |
+       sort >observed &&
+
+       test_cmp expected observed
+'
+
+test_expect_success 'verify big limit and small limit results in small limit' '
+       git -C r2 ls-files -s large.1000 >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
+       sort >expected &&
+
+       git -C r2 pack-objects --revs --stdout --filter=blob:limit=10001 \
+               --filter=blob:limit=1001 >filter.pack <<-EOF &&
+       HEAD
+       EOF
+       git -C r2 index-pack ../filter.pack &&
+
+       git -C r2 verify-pack -v ../filter.pack >verify_result &&
+       grep blob verify_result |
+       parse_verify_pack_blob_oid |
+       sort >observed &&
+
+       test_cmp expected observed
+'
+
 # Test sparse:path=<path> filter.
 # !!!!
 # NOTE: sparse:path filter support has been dropped for security reasons,
@@ -289,8 +329,9 @@ test_expect_success 'setup r3' '
 '
 
 test_expect_success 'verify blob count in normal packfile' '
-       git -C r3 ls-files -s sparse1 sparse2 dir1/sparse1 dir1/sparse2 |
-       test_parse_ls_files_stage_oids |
+       git -C r3 ls-files -s sparse1 sparse2 dir1/sparse1 dir1/sparse2 \
+               >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        git -C r3 pack-objects --revs --stdout >all.pack <<-EOF &&
@@ -341,8 +382,9 @@ test_expect_success 'setup r4' '
 '
 
 test_expect_success 'verify blob count in normal packfile' '
-       git -C r4 ls-files -s pattern sparse1 sparse2 dir1/sparse1 dir1/sparse2 |
-       test_parse_ls_files_stage_oids |
+       git -C r4 ls-files -s pattern sparse1 sparse2 dir1/sparse1 dir1/sparse2 \
+               >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        git -C r4 pack-objects --revs --stdout >all.pack <<-EOF &&
@@ -359,8 +401,8 @@ test_expect_success 'verify blob count in normal packfile' '
 '
 
 test_expect_success 'verify sparse:oid=OID' '
-       git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 |
-       test_parse_ls_files_stage_oids |
+       git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        git -C r4 ls-files -s pattern >staged &&
@@ -379,8 +421,8 @@ test_expect_success 'verify sparse:oid=OID' '
 '
 
 test_expect_success 'verify sparse:oid=oid-ish' '
-       git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 |
-       test_parse_ls_files_stage_oids |
+       git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        git -C r4 pack-objects --revs --stdout --filter=sparse:oid=main:pattern >filter.pack <<-EOF &&
@@ -400,8 +442,9 @@ test_expect_success 'verify sparse:oid=oid-ish' '
 # This models previously omitted objects that we did not receive.
 
 test_expect_success 'setup r1 - delete loose blobs' '
-       git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 |
-       test_parse_ls_files_stage_oids |
+       git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 \
+               >ls_files_result &&
+       test_parse_ls_files_stage_oids <ls_files_result |
        sort >expected &&
 
        for id in `cat expected | sed "s|..|&/|"`
index b5f9b1092229d3d8c1879d41c5c3f02f4ad04923..499d5d4c78590ae16879d8dbd386259d847efb76 100755 (executable)
@@ -1015,4 +1015,20 @@ test_expect_success 'complains when run outside of a repository' '
        grep "not a git repository" err
 '
 
+test_expect_success 'repack with delta islands' '
+       git init repo &&
+       test_when_finished "rm -fr repo" &&
+       (
+               cd repo &&
+
+               test_commit first &&
+               git repack &&
+               test_commit second &&
+               git repack &&
+
+               git multi-pack-index write &&
+               git -c repack.useDeltaIslands=true multi-pack-index repack
+       )
+'
+
 test_done
index 2cc7fd7a4765b2ba8057591df7588150d453a44a..5eb28f0512d447dcea6a16fe27b0a3272514de42 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='test for no lazy fetch with the commit-graph'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup: prepare a repository with a commit' '
index 978f240cdaceb4e7593adcf89136e9de9b558aa1..cfaae547398e0e54cfc5b8f6403d2502ac826782 100755 (executable)
@@ -7,6 +7,7 @@ test_description='Test the post-checkout hook.'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 11f03239a0628c4a50ef13758647f6c85e35e552..1686ac13aa628584297fcfd961356ae5291c57e7 100755 (executable)
@@ -5,6 +5,7 @@ test_description='forced push to replace commit we do not have'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index dcbeb42082791ba0bb2683e9f78b545250d380fc..d6a994663383c32da26d1654527f938873589e2b 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='remote push rejects are reported by client'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index b160f8b7fb7e1f9973361cf3ed86d769d5610206..7b3ff21b984ff4fcfbd4a64a3eecceabf694e27b 100755 (executable)
@@ -5,6 +5,7 @@ test_description='test quickfetch from local'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index ac4099ca8931930989eece26fa735e2f6c00bbc3..0b8ab4afdbe9a0bd0aa304770b6db8f6d303ec9b 100755 (executable)
@@ -4,6 +4,7 @@ test_description='fetch/receive strict mode'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup and inject "corrupt or missing" object' '
@@ -138,7 +139,7 @@ This commit object intentionally broken
 EOF
 
 test_expect_success 'setup bogus commit' '
-       commit="$(git hash-object -t commit -w --stdin <bogus-commit)"
+       commit="$(git hash-object --literally -t commit -w --stdin <bogus-commit)"
 '
 
 test_expect_success 'fsck with no skipList input' '
index 5bac03ede81b4f964a91df275e3620f9da476c46..0e176175a353571428bcf2ca505fa116564f80a3 100755 (executable)
@@ -99,4 +99,13 @@ test_expect_success 'updating remote name updates that remote' '
        ! repo_fetched two
 '
 
+test_expect_success 'updating group in parallel with a duplicate remote does not fail (fetch)' '
+       mark fetch-group-duplicate &&
+       update_repo one &&
+       git config --add remotes.duplicate one &&
+       git config --add remotes.duplicate one &&
+       git -c fetch.parallel=2 remote update duplicate &&
+       repo_fetched one
+'
+
 test_done
index e6149295b187429575aa6fe56e2e206900d30341..c6a6957c5001e9480cbd26ccba5fd26c54126ece 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='check environment showed to remote side of transports'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'set up "remote" push situation' '
index c0b745e33b8a52b6c2defda1fa04ef1b8c202841..34a1261520c757053bfb64c8aa2104c407514091 100755 (executable)
@@ -806,6 +806,14 @@ test_expect_success 'fetch.writeCommitGraph with submodules' '
        )
 '
 
+# fetches from first configured url
+test_expect_success 'fetch from multiple configured URLs in single remote' '
+       git init url1 &&
+       git remote add multipleurls url1 &&
+       git remote set-url --add multipleurls url2 &&
+       git fetch multipleurls
+'
+
 # configured prune tests
 
 set_config_tristate () {
index 511ba3bd454808943c73298c7c8f44a0b5bb9c7a..54f422ced3241870a40da6714c34564abab7c119 100755 (executable)
@@ -197,4 +197,9 @@ test_expect_success 'parallel' '
        test_i18ngrep "could not fetch .two.*128" err
 '
 
+test_expect_success 'git fetch --multiple --jobs=0 picks a default' '
+       (cd test &&
+        git fetch --multiple --jobs=0)
+'
+
 test_done
index bcff460d0a2ba2b6b9965e7bff58bc26033231ab..394bc60cb8eea153e534b9fc887ae37b0e2abf36 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='pulling from symlinked subdir'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # The scenario we are building:
index fdb429205643b8ccb03529795f5b6f6aef63d93a..c9acc076353a6ae784ced36a4927cda352950521 100755 (executable)
@@ -4,6 +4,7 @@ test_description='push with --set-upstream'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
index e2770e4541f94d60dba1ae8698abf3496a6a50e9..98ece27c6a0819093cd54ce19916456ce2136dae 100755 (executable)
@@ -4,6 +4,7 @@ test_description='test fetching of oddly-named refs'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # afterwards we will have:
index ce85fd30ad195eecd061ad0ec734542daf2d7c59..0247137cb36474c22f6ce5ed045379fa43f1a3ea 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='detect some push errors early (before contacting remote)'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup commits' '
index 54f54f8d2ebde39c846e1c193e93e13e8b509618..1a9e14bbccd3c572d86f5cb15afef37abd8d3d67 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test custom script in place of pack-objects'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create some history to fetch' '
index 0b0e987fdb73fcd7d8f54c393ee7deb15a4b5b8c..eed3c9d81abaffa8886b44f26224111b369735ce 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='check receive input limits'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Let's run tests with different unpack limits: 1 and 10000
index 1876fb34e51a09f8b3d7f6d82d174044c9a16c84..9f899b8c7d7bcc63ba5fe97c513748eeba436edb 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='check quarantine of objects during push'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create picky dest repo' '
index 2ac7b5859e7eefdea13613ba9ae1d238313833dd..06991e8e8aabc28c1a4e522324e5c3eb505cc37e 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test noop fetch negotiator'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'noop negotiator does not emit any "have"' '
index 9155f31fa2cb58db4e09814f451ee87b63c5dfdc..afd56926c5371b90eaf42d5823541f6f93788ba1 100755 (executable)
@@ -285,6 +285,8 @@ test_expect_success 'clone HTTP bundle' '
 '
 
 test_expect_success 'clone bundle list (HTTP, no heuristic)' '
+       test_when_finished rm -f trace*.txt &&
+
        cp clone-from/bundle-*.bundle "$HTTPD_DOCUMENT_ROOT_PATH/" &&
        cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
        [bundle]
@@ -304,12 +306,26 @@ test_expect_success 'clone bundle list (HTTP, no heuristic)' '
                uri = $HTTPD_URL/bundle-4.bundle
        EOF
 
-       git clone --bundle-uri="$HTTPD_URL/bundle-list" \
+       GIT_TRACE2_EVENT="$(pwd)/trace-clone.txt" \
+               git clone --bundle-uri="$HTTPD_URL/bundle-list" \
                clone-from clone-list-http  2>err &&
        ! grep "Repository lacks these prerequisite commits" err &&
 
        git -C clone-from for-each-ref --format="%(objectname)" >oids &&
-       git -C clone-list-http cat-file --batch-check <oids
+       git -C clone-list-http cat-file --batch-check <oids &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-1.bundle
+       $HTTPD_URL/bundle-2.bundle
+       $HTTPD_URL/bundle-3.bundle
+       $HTTPD_URL/bundle-4.bundle
+       $HTTPD_URL/bundle-list
+       EOF
+
+       # Sort the list, since the order is not well-defined
+       # without a heuristic.
+       test_remote_https_urls <trace-clone.txt | sort >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'clone bundle list (HTTP, any mode)' '
@@ -350,6 +366,658 @@ test_expect_success 'clone bundle list (HTTP, any mode)' '
        test_cmp expect actual
 '
 
+test_expect_success 'clone bundle list (http, creationToken)' '
+       test_when_finished rm -f trace*.txt &&
+
+       cp clone-from/bundle-*.bundle "$HTTPD_DOCUMENT_ROOT_PATH/" &&
+       cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+
+       [bundle "bundle-1"]
+               uri = bundle-1.bundle
+               creationToken = 1
+
+       [bundle "bundle-2"]
+               uri = bundle-2.bundle
+               creationToken = 2
+
+       [bundle "bundle-3"]
+               uri = bundle-3.bundle
+               creationToken = 3
+
+       [bundle "bundle-4"]
+               uri = bundle-4.bundle
+               creationToken = 4
+       EOF
+
+       GIT_TRACE2_EVENT="$(pwd)/trace-clone.txt" git \
+               clone --bundle-uri="$HTTPD_URL/bundle-list" \
+               "$HTTPD_URL/smart/fetch.git" clone-list-http-2 &&
+
+       git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+       git -C clone-list-http-2 cat-file --batch-check <oids &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/bundle-4.bundle
+       $HTTPD_URL/bundle-3.bundle
+       $HTTPD_URL/bundle-2.bundle
+       $HTTPD_URL/bundle-1.bundle
+       EOF
+
+       test_remote_https_urls <trace-clone.txt >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'clone incomplete bundle list (http, creationToken)' '
+       test_when_finished rm -f trace*.txt &&
+
+       cp clone-from/bundle-*.bundle "$HTTPD_DOCUMENT_ROOT_PATH/" &&
+       cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+
+       [bundle "bundle-1"]
+               uri = bundle-1.bundle
+               creationToken = 1
+       EOF
+
+       GIT_TRACE2_EVENT=$(pwd)/trace-clone.txt \
+       git clone --bundle-uri="$HTTPD_URL/bundle-list" \
+               --single-branch --branch=base --no-tags \
+               "$HTTPD_URL/smart/fetch.git" clone-token-http &&
+
+       test_cmp_config -C clone-token-http "$HTTPD_URL/bundle-list" fetch.bundleuri &&
+       test_cmp_config -C clone-token-http 1 fetch.bundlecreationtoken &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/bundle-1.bundle
+       EOF
+
+       test_remote_https_urls <trace-clone.txt >actual &&
+       test_cmp expect actual &&
+
+       # We now have only one bundle ref.
+       git -C clone-token-http for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+       cat >expect <<-\EOF &&
+       refs/bundles/base
+       EOF
+       test_cmp expect refs &&
+
+       # Add remaining bundles, exercising the "deepening" strategy
+       # for downloading via the creationToken heurisitc.
+       cat >>"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle "bundle-2"]
+               uri = bundle-2.bundle
+               creationToken = 2
+
+       [bundle "bundle-3"]
+               uri = bundle-3.bundle
+               creationToken = 3
+
+       [bundle "bundle-4"]
+               uri = bundle-4.bundle
+               creationToken = 4
+       EOF
+
+       GIT_TRACE2_EVENT="$(pwd)/trace1.txt" \
+               git -C clone-token-http fetch origin --no-tags \
+               refs/heads/merge:refs/heads/merge &&
+       test_cmp_config -C clone-token-http 4 fetch.bundlecreationtoken &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/bundle-4.bundle
+       $HTTPD_URL/bundle-3.bundle
+       $HTTPD_URL/bundle-2.bundle
+       EOF
+
+       test_remote_https_urls <trace1.txt >actual &&
+       test_cmp expect actual &&
+
+       # We now have all bundle refs.
+       git -C clone-token-http for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+
+       cat >expect <<-\EOF &&
+       refs/bundles/base
+       refs/bundles/left
+       refs/bundles/merge
+       refs/bundles/right
+       EOF
+       test_cmp expect refs
+'
+
+test_expect_success 'http clone with bundle.heuristic creates fetch.bundleURI' '
+       test_when_finished rm -rf fetch-http-4 trace*.txt &&
+
+       cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+
+       [bundle "bundle-1"]
+               uri = bundle-1.bundle
+               creationToken = 1
+       EOF
+
+       GIT_TRACE2_EVENT="$(pwd)/trace-clone.txt" \
+       git clone --single-branch --branch=base \
+               --bundle-uri="$HTTPD_URL/bundle-list" \
+               "$HTTPD_URL/smart/fetch.git" fetch-http-4 &&
+
+       test_cmp_config -C fetch-http-4 "$HTTPD_URL/bundle-list" fetch.bundleuri &&
+       test_cmp_config -C fetch-http-4 1 fetch.bundlecreationtoken &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/bundle-1.bundle
+       EOF
+
+       test_remote_https_urls <trace-clone.txt >actual &&
+       test_cmp expect actual &&
+
+       # only received base ref from bundle-1
+       git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+       cat >expect <<-\EOF &&
+       refs/bundles/base
+       EOF
+       test_cmp expect refs &&
+
+       cat >>"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle "bundle-2"]
+               uri = bundle-2.bundle
+               creationToken = 2
+       EOF
+
+       # Fetch the objects for bundle-2 _and_ bundle-3.
+       GIT_TRACE2_EVENT="$(pwd)/trace1.txt" \
+               git -C fetch-http-4 fetch origin --no-tags \
+               refs/heads/left:refs/heads/left \
+               refs/heads/right:refs/heads/right &&
+       test_cmp_config -C fetch-http-4 2 fetch.bundlecreationtoken &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/bundle-2.bundle
+       EOF
+
+       test_remote_https_urls <trace1.txt >actual &&
+       test_cmp expect actual &&
+
+       # received left from bundle-2
+       git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+       cat >expect <<-\EOF &&
+       refs/bundles/base
+       refs/bundles/left
+       EOF
+       test_cmp expect refs &&
+
+       # No-op fetch
+       GIT_TRACE2_EVENT="$(pwd)/trace1b.txt" \
+               git -C fetch-http-4 fetch origin --no-tags \
+               refs/heads/left:refs/heads/left \
+               refs/heads/right:refs/heads/right &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       EOF
+       test_remote_https_urls <trace1b.txt >actual &&
+       test_cmp expect actual &&
+
+       cat >>"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle "bundle-3"]
+               uri = bundle-3.bundle
+               creationToken = 3
+
+       [bundle "bundle-4"]
+               uri = bundle-4.bundle
+               creationToken = 4
+       EOF
+
+       # This fetch should skip bundle-3.bundle, since its objects are
+       # already local (we have the requisite commits for bundle-4.bundle).
+       GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+               git -C fetch-http-4 fetch origin --no-tags \
+               refs/heads/merge:refs/heads/merge &&
+       test_cmp_config -C fetch-http-4 4 fetch.bundlecreationtoken &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/bundle-4.bundle
+       EOF
+
+       test_remote_https_urls <trace2.txt >actual &&
+       test_cmp expect actual &&
+
+       # received merge ref from bundle-4, but right is missing
+       # because we did not download bundle-3.
+       git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+
+       cat >expect <<-\EOF &&
+       refs/bundles/base
+       refs/bundles/left
+       refs/bundles/merge
+       EOF
+       test_cmp expect refs &&
+
+       # No-op fetch
+       GIT_TRACE2_EVENT="$(pwd)/trace2b.txt" \
+               git -C fetch-http-4 fetch origin &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       EOF
+       test_remote_https_urls <trace2b.txt >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'creationToken heuristic with failed downloads (clone)' '
+       test_when_finished rm -rf download-* trace*.txt &&
+
+       # Case 1: base bundle does not exist, nothing can unbundle
+       cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+
+       [bundle "bundle-1"]
+               uri = fake.bundle
+               creationToken = 1
+
+       [bundle "bundle-2"]
+               uri = bundle-2.bundle
+               creationToken = 2
+
+       [bundle "bundle-3"]
+               uri = bundle-3.bundle
+               creationToken = 3
+
+       [bundle "bundle-4"]
+               uri = bundle-4.bundle
+               creationToken = 4
+       EOF
+
+       GIT_TRACE2_EVENT="$(pwd)/trace-clone-1.txt" \
+       git clone --single-branch --branch=base \
+               --bundle-uri="$HTTPD_URL/bundle-list" \
+               "$HTTPD_URL/smart/fetch.git" download-1 &&
+
+       # Bundle failure does not set these configs.
+       test_must_fail git -C download-1 config fetch.bundleuri &&
+       test_must_fail git -C download-1 config fetch.bundlecreationtoken &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/bundle-4.bundle
+       $HTTPD_URL/bundle-3.bundle
+       $HTTPD_URL/bundle-2.bundle
+       $HTTPD_URL/fake.bundle
+       EOF
+       test_remote_https_urls <trace-clone-1.txt >actual &&
+       test_cmp expect actual &&
+
+       # All bundles failed to unbundle
+       git -C download-1 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+       test_must_be_empty refs &&
+
+       # Case 2: middle bundle does not exist, only two bundles can unbundle
+       cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+
+       [bundle "bundle-1"]
+               uri = bundle-1.bundle
+               creationToken = 1
+
+       [bundle "bundle-2"]
+               uri = fake.bundle
+               creationToken = 2
+
+       [bundle "bundle-3"]
+               uri = bundle-3.bundle
+               creationToken = 3
+
+       [bundle "bundle-4"]
+               uri = bundle-4.bundle
+               creationToken = 4
+       EOF
+
+       GIT_TRACE2_EVENT="$(pwd)/trace-clone-2.txt" \
+       git clone --single-branch --branch=base \
+               --bundle-uri="$HTTPD_URL/bundle-list" \
+               "$HTTPD_URL/smart/fetch.git" download-2 &&
+
+       # Bundle failure does not set these configs.
+       test_must_fail git -C download-2 config fetch.bundleuri &&
+       test_must_fail git -C download-2 config fetch.bundlecreationtoken &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/bundle-4.bundle
+       $HTTPD_URL/bundle-3.bundle
+       $HTTPD_URL/fake.bundle
+       $HTTPD_URL/bundle-1.bundle
+       EOF
+       test_remote_https_urls <trace-clone-2.txt >actual &&
+       test_cmp expect actual &&
+
+       # bundle-1 and bundle-3 could unbundle, but bundle-4 could not
+       git -C download-2 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+       cat >expect <<-EOF &&
+       refs/bundles/base
+       refs/bundles/right
+       EOF
+       test_cmp expect refs &&
+
+       # Case 3: top bundle does not exist, rest unbundle fine.
+       cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+
+       [bundle "bundle-1"]
+               uri = bundle-1.bundle
+               creationToken = 1
+
+       [bundle "bundle-2"]
+               uri = bundle-2.bundle
+               creationToken = 2
+
+       [bundle "bundle-3"]
+               uri = bundle-3.bundle
+               creationToken = 3
+
+       [bundle "bundle-4"]
+               uri = fake.bundle
+               creationToken = 4
+       EOF
+
+       GIT_TRACE2_EVENT="$(pwd)/trace-clone-3.txt" \
+       git clone --single-branch --branch=base \
+               --bundle-uri="$HTTPD_URL/bundle-list" \
+               "$HTTPD_URL/smart/fetch.git" download-3 &&
+
+       # As long as we have continguous successful downloads,
+       # we _do_ set these configs.
+       test_cmp_config -C download-3 "$HTTPD_URL/bundle-list" fetch.bundleuri &&
+       test_cmp_config -C download-3 3 fetch.bundlecreationtoken &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/fake.bundle
+       $HTTPD_URL/bundle-3.bundle
+       $HTTPD_URL/bundle-2.bundle
+       $HTTPD_URL/bundle-1.bundle
+       EOF
+       test_remote_https_urls <trace-clone-3.txt >actual &&
+       test_cmp expect actual &&
+
+       # fake.bundle did not unbundle, but the others did.
+       git -C download-3 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+       cat >expect <<-EOF &&
+       refs/bundles/base
+       refs/bundles/left
+       refs/bundles/right
+       EOF
+       test_cmp expect refs
+'
+
+# Expand the bundle list to include other interesting shapes, specifically
+# interesting for use when fetching from a previous state.
+#
+# ---------------- bundle-7
+#       7
+#     _/|\_
+# ---/--|--\------ bundle-6
+#   5   |   6
+# --|---|---|----- bundle-4
+#   |   4   |
+#   |  / \  /
+# --|-|---|/------ bundle-3 (the client will be caught up to this point.)
+#   \ |   3
+# ---\|---|------- bundle-2
+#     2   |
+# ----|---|------- bundle-1
+#      \ /
+#       1
+#       |
+# (previous commits)
+test_expect_success 'expand incremental bundle list' '
+       (
+               cd clone-from &&
+               git checkout -b lefter left &&
+               test_commit 5 &&
+               git checkout -b righter right &&
+               test_commit 6 &&
+               git checkout -b top lefter &&
+               git merge -m "7" merge righter &&
+
+               git bundle create bundle-6.bundle lefter righter --not left right &&
+               git bundle create bundle-7.bundle top --not lefter merge righter &&
+
+               cp bundle-*.bundle "$HTTPD_DOCUMENT_ROOT_PATH/"
+       ) &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/fetch.git" fetch origin +refs/heads/*:refs/heads/*
+'
+
+test_expect_success 'creationToken heuristic with failed downloads (fetch)' '
+       test_when_finished rm -rf download-* trace*.txt &&
+
+       cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+
+       [bundle "bundle-1"]
+               uri = bundle-1.bundle
+               creationToken = 1
+
+       [bundle "bundle-2"]
+               uri = bundle-2.bundle
+               creationToken = 2
+
+       [bundle "bundle-3"]
+               uri = bundle-3.bundle
+               creationToken = 3
+       EOF
+
+       git clone --single-branch --branch=left \
+               --bundle-uri="$HTTPD_URL/bundle-list" \
+               "$HTTPD_URL/smart/fetch.git" fetch-base &&
+       test_cmp_config -C fetch-base "$HTTPD_URL/bundle-list" fetch.bundleURI &&
+       test_cmp_config -C fetch-base 3 fetch.bundleCreationToken &&
+
+       # Case 1: all bundles exist: successful unbundling of all bundles
+       cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+
+       [bundle "bundle-1"]
+               uri = bundle-1.bundle
+               creationToken = 1
+
+       [bundle "bundle-2"]
+               uri = bundle-2.bundle
+               creationToken = 2
+
+       [bundle "bundle-3"]
+               uri = bundle-3.bundle
+               creationToken = 3
+
+       [bundle "bundle-4"]
+               uri = bundle-4.bundle
+               creationToken = 4
+
+       [bundle "bundle-6"]
+               uri = bundle-6.bundle
+               creationToken = 6
+
+       [bundle "bundle-7"]
+               uri = bundle-7.bundle
+               creationToken = 7
+       EOF
+
+       cp -r fetch-base fetch-1 &&
+       GIT_TRACE2_EVENT="$(pwd)/trace-fetch-1.txt" \
+               git -C fetch-1 fetch origin &&
+       test_cmp_config -C fetch-1 7 fetch.bundlecreationtoken &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/bundle-7.bundle
+       $HTTPD_URL/bundle-6.bundle
+       $HTTPD_URL/bundle-4.bundle
+       EOF
+       test_remote_https_urls <trace-fetch-1.txt >actual &&
+       test_cmp expect actual &&
+
+       # Check which bundles have unbundled by refs
+       git -C fetch-1 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+       cat >expect <<-EOF &&
+       refs/bundles/base
+       refs/bundles/left
+       refs/bundles/lefter
+       refs/bundles/merge
+       refs/bundles/right
+       refs/bundles/righter
+       refs/bundles/top
+       EOF
+       test_cmp expect refs &&
+
+       # Case 2: middle bundle does not exist, only bundle-4 can unbundle
+       cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+
+       [bundle "bundle-1"]
+               uri = bundle-1.bundle
+               creationToken = 1
+
+       [bundle "bundle-2"]
+               uri = bundle-2.bundle
+               creationToken = 2
+
+       [bundle "bundle-3"]
+               uri = bundle-3.bundle
+               creationToken = 3
+
+       [bundle "bundle-4"]
+               uri = bundle-4.bundle
+               creationToken = 4
+
+       [bundle "bundle-6"]
+               uri = fake.bundle
+               creationToken = 6
+
+       [bundle "bundle-7"]
+               uri = bundle-7.bundle
+               creationToken = 7
+       EOF
+
+       cp -r fetch-base fetch-2 &&
+       GIT_TRACE2_EVENT="$(pwd)/trace-fetch-2.txt" \
+               git -C fetch-2 fetch origin &&
+
+       # Since bundle-7 fails to unbundle, do not update creation token.
+       test_cmp_config -C fetch-2 3 fetch.bundlecreationtoken &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/bundle-7.bundle
+       $HTTPD_URL/fake.bundle
+       $HTTPD_URL/bundle-4.bundle
+       EOF
+       test_remote_https_urls <trace-fetch-2.txt >actual &&
+       test_cmp expect actual &&
+
+       # Check which bundles have unbundled by refs
+       git -C fetch-2 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+       cat >expect <<-EOF &&
+       refs/bundles/base
+       refs/bundles/left
+       refs/bundles/merge
+       refs/bundles/right
+       EOF
+       test_cmp expect refs &&
+
+       # Case 3: top bundle does not exist, rest unbundle fine.
+       cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+
+       [bundle "bundle-1"]
+               uri = bundle-1.bundle
+               creationToken = 1
+
+       [bundle "bundle-2"]
+               uri = bundle-2.bundle
+               creationToken = 2
+
+       [bundle "bundle-3"]
+               uri = bundle-3.bundle
+               creationToken = 3
+
+       [bundle "bundle-4"]
+               uri = bundle-4.bundle
+               creationToken = 4
+
+       [bundle "bundle-6"]
+               uri = bundle-6.bundle
+               creationToken = 6
+
+       [bundle "bundle-7"]
+               uri = fake.bundle
+               creationToken = 7
+       EOF
+
+       cp -r fetch-base fetch-3 &&
+       GIT_TRACE2_EVENT="$(pwd)/trace-fetch-3.txt" \
+               git -C fetch-3 fetch origin &&
+
+       # As long as we have continguous successful downloads,
+       # we _do_ set the maximum creation token.
+       test_cmp_config -C fetch-3 6 fetch.bundlecreationtoken &&
+
+       # NOTE: the fetch skips bundle-4 since bundle-6 successfully
+       # unbundles itself and bundle-7 failed to download.
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/fake.bundle
+       $HTTPD_URL/bundle-6.bundle
+       EOF
+       test_remote_https_urls <trace-fetch-3.txt >actual &&
+       test_cmp expect actual &&
+
+       # Check which bundles have unbundled by refs
+       git -C fetch-3 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+       cat >expect <<-EOF &&
+       refs/bundles/base
+       refs/bundles/left
+       refs/bundles/lefter
+       refs/bundles/right
+       refs/bundles/righter
+       EOF
+       test_cmp expect refs
+'
+
 # Do not add tests here unless they use the HTTP server, as they will
 # not run unless the HTTP dependencies exist.
 
index d30cf4f5b8398e1cf98c86221dd42208d64fd014..f75068de64851faf86d7ae270c1b88bb9d08df16 100755 (executable)
@@ -4,6 +4,7 @@ test_description='test git-http-backend-noserver'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 HTTPD_DOCUMENT_ROOT_PATH="$TRASH_DIRECTORY"
index 9c57d843152dd5861603316650a0f7f440711c52..e1d3b8caed07394fcdf02db4d31d4f038d3474b2 100755 (executable)
@@ -4,6 +4,7 @@ test_description='test git-http-backend'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-httpd.sh
 
index b68ec22d3fdb0bab650b39642a61a11a6579fcb5..7ee9858a78b6fb9a473d777f280a9d450fd49830 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test git-http-backend respects CONTENT_LENGTH'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_lazy_prereq GZIP 'gzip --version'
diff --git a/t/t5564-http-proxy.sh b/t/t5564-http-proxy.sh
new file mode 100755 (executable)
index 0000000..9da5134
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+test_description="test fetching through http proxy"
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-httpd.sh
+
+LIB_HTTPD_PROXY=1
+start_httpd
+
+test_expect_success 'setup repository' '
+       test_commit foo &&
+       git init --bare "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       git push --mirror "$HTTPD_DOCUMENT_ROOT_PATH/repo.git"
+'
+
+setup_askpass_helper
+
+# sanity check that our test setup is correctly using proxy
+test_expect_success 'proxy requires password' '
+       test_config_global http.proxy $HTTPD_DEST &&
+       test_must_fail git clone $HTTPD_URL/smart/repo.git 2>err &&
+       grep "error.*407" err
+'
+
+test_expect_success 'clone through proxy with auth' '
+       test_when_finished "rm -rf clone" &&
+       test_config_global http.proxy http://proxuser:proxpass@$HTTPD_DEST &&
+       GIT_TRACE_CURL=$PWD/trace git clone $HTTPD_URL/smart/repo.git clone &&
+       grep -i "Proxy-Authorization: Basic <redacted>" trace
+'
+
+test_expect_success 'clone can prompt for proxy password' '
+       test_when_finished "rm -rf clone" &&
+       test_config_global http.proxy http://proxuser@$HTTPD_DEST &&
+       set_askpass nobody proxpass &&
+       GIT_TRACE_CURL=$PWD/trace git clone $HTTPD_URL/smart/repo.git clone &&
+       expect_askpass pass proxuser
+'
+
+test_done
index a53dd8550d0b8cbe40f49ece0381fb554ba87062..1221ac05978e2c5cff0675f7e46e488361908098 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='pull signature verification tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY/lib-gpg.sh"
 
index b2524a24c29796960bfd4dc59ec9a9650e53175e..b7d5551262c7b715fbeea48f0260906f35a64a10 100755 (executable)
@@ -772,6 +772,111 @@ test_expect_success 'reject cloning shallow repository using HTTP' '
        git clone --no-reject-shallow $HTTPD_URL/smart/repo.git repo
 '
 
+test_expect_success 'auto-discover bundle URI from HTTP clone' '
+       test_when_finished rm -rf trace.txt repo2 "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" &&
+       git -C src bundle create "$HTTPD_DOCUMENT_ROOT_PATH/everything.bundle" --all &&
+       git clone --bare --no-local src "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" &&
+
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" config \
+               uploadpack.advertiseBundleURIs true &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" config \
+               bundle.version 1 &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" config \
+               bundle.mode all &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" config \
+               bundle.everything.uri "$HTTPD_URL/everything.bundle" &&
+
+       GIT_TRACE2_EVENT="$(pwd)/trace.txt" \
+               git -c protocol.version=2 \
+                   -c transfer.bundleURI=true clone \
+               $HTTPD_URL/smart/repo2.git repo2 &&
+       cat >pattern <<-EOF &&
+       "event":"child_start".*"argv":\["git-remote-https","$HTTPD_URL/everything.bundle"\]
+       EOF
+       grep -f pattern trace.txt
+'
+
+test_expect_success 'auto-discover multiple bundles from HTTP clone' '
+       test_when_finished rm -rf trace.txt repo3 "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" &&
+
+       test_commit -C src new &&
+       git -C src bundle create "$HTTPD_DOCUMENT_ROOT_PATH/new.bundle" HEAD~1..HEAD &&
+       git clone --bare --no-local src "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" &&
+
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" config \
+               uploadpack.advertiseBundleURIs true &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" config \
+               bundle.version 1 &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" config \
+               bundle.mode all &&
+
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" config \
+               bundle.everything.uri "$HTTPD_URL/everything.bundle" &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" config \
+               bundle.new.uri "$HTTPD_URL/new.bundle" &&
+
+       GIT_TRACE2_EVENT="$(pwd)/trace.txt" \
+               git -c protocol.version=2 \
+                   -c transfer.bundleURI=true clone \
+               $HTTPD_URL/smart/repo3.git repo3 &&
+
+       # We should fetch _both_ bundles
+       cat >pattern <<-EOF &&
+       "event":"child_start".*"argv":\["git-remote-https","$HTTPD_URL/everything.bundle"\]
+       EOF
+       grep -f pattern trace.txt &&
+       cat >pattern <<-EOF &&
+       "event":"child_start".*"argv":\["git-remote-https","$HTTPD_URL/new.bundle"\]
+       EOF
+       grep -f pattern trace.txt
+'
+
+test_expect_success 'auto-discover multiple bundles from HTTP clone: creationToken heuristic' '
+       test_when_finished rm -rf "$HTTPD_DOCUMENT_ROOT_PATH/repo4.git" &&
+       test_when_finished rm -rf clone-heuristic trace*.txt &&
+
+       test_commit -C src newest &&
+       git -C src bundle create "$HTTPD_DOCUMENT_ROOT_PATH/newest.bundle" HEAD~1..HEAD &&
+       git clone --bare --no-local src "$HTTPD_DOCUMENT_ROOT_PATH/repo4.git" &&
+
+       cat >>"$HTTPD_DOCUMENT_ROOT_PATH/repo4.git/config" <<-EOF &&
+       [uploadPack]
+               advertiseBundleURIs = true
+
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+
+       [bundle "everything"]
+               uri = $HTTPD_URL/everything.bundle
+               creationtoken = 1
+
+       [bundle "new"]
+               uri = $HTTPD_URL/new.bundle
+               creationtoken = 2
+
+       [bundle "newest"]
+               uri = $HTTPD_URL/newest.bundle
+               creationtoken = 3
+       EOF
+
+       GIT_TRACE2_EVENT="$(pwd)/trace-clone.txt" \
+               git -c protocol.version=2 \
+                   -c transfer.bundleURI=true clone \
+               "$HTTPD_URL/smart/repo4.git" clone-heuristic &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/newest.bundle
+       $HTTPD_URL/new.bundle
+       $HTTPD_URL/everything.bundle
+       EOF
+
+       # We should fetch all bundles in the expected order.
+       test_remote_https_urls <trace-clone.txt >actual &&
+       test_cmp expect actual
+'
+
 # DO NOT add non-httpd-specific tests here, because the last part of this
 # test script is only executed when httpd is available and enabled.
 
index 7ccebb40c33825561618fce95afc2e96b0349f6f..83e3c97861ddc50eb9e6b1a78e68d693224c3fc9 100755 (executable)
@@ -7,6 +7,7 @@ test_description='test clone --reference'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 base_dir=$(pwd)
index cf221e92c4d97e852f1bdefbf61f6caded87aff2..27f9f7763895ec98761800d2a3e4ad2adf33aeb9 100755 (executable)
@@ -4,6 +4,7 @@ test_description='basic clone options'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index a7ec21eda5aabf16144f79a7f348e6f6be9b7507..022ed3d87c37159f758b349f6604b781a29b9327 100755 (executable)
@@ -4,6 +4,7 @@ test_description='test cloning a repository with detached HEAD'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 head_is_detached() {
index 4b3877216ee4649a0a31986962740e0f37b5ad85..727caff443368365ca281373ad730de8f3987ab1 100755 (executable)
@@ -4,6 +4,7 @@ test_description='tests for git clone -c key=value'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'clone -c sets config in cloned repo' '
index 895f46bb9118bbf15d32f594ce79f0e638d25b6a..7708cbafa982ef3a5579f8bde075fcad40e29e40 100755 (executable)
@@ -4,6 +4,8 @@
 #
 
 test_description='test transitive info/alternate entries'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'preparing first repository' '
index 0c85ef834ab90e6fddec2ec510929df1d62acb1d..c2a2bb453eeabcc8725bffae2c537ec99b8a7d87 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='Test shallow cloning of repos with submodules'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 pwd=$(pwd)
index 037941b95d2019f3c65b1b7fa39c03bfc343cd33..f519d2a87a7d84e482284998cbca338a0dfba419 100755 (executable)
@@ -644,6 +644,49 @@ test_expect_success 'repack does not loosen promisor objects' '
        grep "loosen_unused_packed_objects/loosened:0" trace
 '
 
+test_expect_success 'lazy-fetch in submodule succeeds' '
+       # setup
+       test_config_global protocol.file.allow always &&
+
+       test_when_finished "rm -rf src-sub" &&
+       git init src-sub &&
+       git -C src-sub config uploadpack.allowfilter 1 &&
+       git -C src-sub config uploadpack.allowanysha1inwant 1 &&
+
+       # This blob must be missing in the subsequent commit.
+       echo foo >src-sub/file &&
+       git -C src-sub add file &&
+       git -C src-sub commit -m "submodule one" &&
+       SUB_ONE=$(git -C src-sub rev-parse HEAD) &&
+
+       echo bar >src-sub/file &&
+       git -C src-sub add file &&
+       git -C src-sub commit -m "submodule two" &&
+       SUB_TWO=$(git -C src-sub rev-parse HEAD) &&
+
+       test_when_finished "rm -rf src-super" &&
+       git init src-super &&
+       git -C src-super config uploadpack.allowfilter 1 &&
+       git -C src-super config uploadpack.allowanysha1inwant 1 &&
+       git -C src-super submodule add ../src-sub src-sub &&
+
+       git -C src-super/src-sub checkout $SUB_ONE &&
+       git -C src-super add src-sub &&
+       git -C src-super commit -m "superproject one" &&
+
+       git -C src-super/src-sub checkout $SUB_TWO &&
+       git -C src-super add src-sub &&
+       git -C src-super commit -m "superproject two" &&
+
+       # the fetch
+       test_when_finished "rm -rf client" &&
+       git clone --filter=blob:none --also-filter-submodules \
+               --recurse-submodules "file://$(pwd)/src-super" client &&
+
+       # Trigger lazy-fetch from the superproject
+       git -C client restore --recurse-submodules --source=HEAD^ :/
+'
+
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
 
index 688433824934d89354669d920acd1fb06a5da725..5a4d7936a72aac48fde876076393956bb83d42e9 100755 (executable)
@@ -5,6 +5,7 @@ test_description='Test cloning repos with submodules using remote-tracking branc
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 pwd=$(pwd)
index 3353216f09e52450a28fd10d68df9478dcfc129b..f905db0a3fdf406dd564114713cb0236fda42ce6 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test handling of --alternate-refs traversal'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Avoid test_commit because we want a specific and known set of refs:
index 1896f671cb37f916bc09e50f0f5a45df81da1327..f21e5e9d33d199368df3aeb5ebae64e6b20cc5e3 100755 (executable)
@@ -13,7 +13,7 @@ test_expect_success 'test capability advertisement' '
        wrong_algo sha1:sha256
        wrong_algo sha256:sha1
        EOF
-       cat >expect <<-EOF &&
+       cat >expect.base <<-EOF &&
        version 2
        agent=git/$(git version | cut -d" " -f3)
        ls-refs=unborn
@@ -21,8 +21,11 @@ test_expect_success 'test capability advertisement' '
        server-option
        object-format=$(test_oid algo)
        object-info
+       EOF
+       cat >expect.trailer <<-EOF &&
        0000
        EOF
+       cat expect.base expect.trailer >expect &&
 
        GIT_TEST_SIDEBAND_ALL=0 test-tool serve-v2 \
                --advertise-capabilities >out &&
@@ -342,4 +345,39 @@ test_expect_success 'basics of object-info' '
        test_cmp expect actual
 '
 
+test_expect_success 'test capability advertisement with uploadpack.advertiseBundleURIs' '
+       test_config uploadpack.advertiseBundleURIs true &&
+
+       cat >expect.extra <<-EOF &&
+       bundle-uri
+       EOF
+       cat expect.base \
+           expect.extra \
+           expect.trailer >expect &&
+
+       GIT_TEST_SIDEBAND_ALL=0 test-tool serve-v2 \
+               --advertise-capabilities >out &&
+       test-tool pkt-line unpack <out >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'basics of bundle-uri: dies if not enabled' '
+       test-tool pkt-line pack >in <<-EOF &&
+       command=bundle-uri
+       0000
+       EOF
+
+       cat >err.expect <<-\EOF &&
+       fatal: invalid command '"'"'bundle-uri'"'"'
+       EOF
+
+       cat >expect <<-\EOF &&
+       ERR serve: invalid command '"'"'bundle-uri'"'"'
+       EOF
+
+       test_must_fail test-tool serve-v2 --stateless-rpc <in >out 2>err.actual &&
+       test_cmp err.expect err.actual &&
+       test_must_be_empty out
+'
+
 test_done
index b33cd4afca3d021f1f8f38393521589243ae95fb..e4db7513f42192895e33e7eec431c73bec0007d8 100755 (executable)
@@ -1114,7 +1114,7 @@ test_expect_success 'packfile-uri with transfer.fsckobjects fails on bad object'
 
        This commit object intentionally broken
        EOF
-       BOGUS=$(git -C "$P" hash-object -t commit -w --stdin <bogus-commit) &&
+       BOGUS=$(git -C "$P" hash-object -t commit -w --stdin --literally <bogus-commit) &&
        git -C "$P" branch bogus-branch "$BOGUS" &&
 
        echo my-blob >"$P/my-blob" &&
index ed38c76c29059d3f5363116db1b8231c31b0dc5a..b8a722ec27e73aeeb259e938417017607f64b5ee 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='session ID in capabilities'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 REPO="$(pwd)/repo"
diff --git a/t/t5730-protocol-v2-bundle-uri-file.sh b/t/t5730-protocol-v2-bundle-uri-file.sh
new file mode 100755 (executable)
index 0000000..37bdb72
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+test_description="Test bundle-uri with protocol v2 and 'file://' transport"
+
+TEST_NO_CREATE_REPO=1
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+# Test protocol v2 with 'file://' transport
+#
+BUNDLE_URI_PROTOCOL=file
+. "$TEST_DIRECTORY"/lib-bundle-uri-protocol.sh
+
+test_done
diff --git a/t/t5731-protocol-v2-bundle-uri-git.sh b/t/t5731-protocol-v2-bundle-uri-git.sh
new file mode 100755 (executable)
index 0000000..8add1b3
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+test_description="Test bundle-uri with protocol v2 and 'git://' transport"
+
+TEST_NO_CREATE_REPO=1
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+# Test protocol v2 with 'git://' transport
+#
+BUNDLE_URI_PROTOCOL=git
+. "$TEST_DIRECTORY"/lib-bundle-uri-protocol.sh
+
+test_done
diff --git a/t/t5732-protocol-v2-bundle-uri-http.sh b/t/t5732-protocol-v2-bundle-uri-http.sh
new file mode 100755 (executable)
index 0000000..129daa0
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+test_description="Test bundle-uri with protocol v2 and 'http://' transport"
+
+TEST_NO_CREATE_REPO=1
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+# Test protocol v2 with 'http://' transport
+#
+BUNDLE_URI_PROTOCOL=http
+. "$TEST_DIRECTORY"/lib-bundle-uri-protocol.sh
+
+test_done
index c2fe3f9c5a5c40543e8c2c0b65949c0dbfc1b594..81bdf58b94443415083cd1dca427e08564f9af53 100755 (executable)
@@ -30,6 +30,58 @@ test_expect_success 'bundle_uri_parse_line() just URIs' '
        test_cmp_config_output expect actual
 '
 
+test_expect_success 'bundle_uri_parse_line(): relative URIs' '
+       cat >in <<-\EOF &&
+       bundle.one.uri=bundle.bdl
+       bundle.two.uri=../bundle.bdl
+       bundle.three.uri=sub/dir/bundle.bdl
+       EOF
+
+       cat >expect <<-\EOF &&
+       [bundle]
+               version = 1
+               mode = all
+       [bundle "one"]
+               uri = <uri>/bundle.bdl
+       [bundle "two"]
+               uri = bundle.bdl
+       [bundle "three"]
+               uri = <uri>/sub/dir/bundle.bdl
+       EOF
+
+       test-tool bundle-uri parse-key-values in >actual 2>err &&
+       test_must_be_empty err &&
+       test_cmp_config_output expect actual
+'
+
+test_expect_success 'bundle_uri_parse_line(): relative URIs and parent paths' '
+       cat >in <<-\EOF &&
+       bundle.one.uri=bundle.bdl
+       bundle.two.uri=../bundle.bdl
+       bundle.three.uri=../../bundle.bdl
+       EOF
+
+       cat >expect <<-\EOF &&
+       [bundle]
+               version = 1
+               mode = all
+       [bundle "one"]
+               uri = <uri>/bundle.bdl
+       [bundle "two"]
+               uri = bundle.bdl
+       [bundle "three"]
+               uri = <uri>/../bundle.bdl
+       EOF
+
+       # TODO: We would prefer if parsing a bundle list would not cause
+       # a die() and instead would give a warning and allow the rest of
+       # a Git command to continue. This test_must_fail is necessary for
+       # now until the interface for relative_url() allows for reporting
+       # an error instead of die()ing.
+       test_must_fail test-tool bundle-uri parse-key-values in >actual 2>err &&
+       grep "fatal: cannot strip one component off url" err
+'
+
 test_expect_success 'bundle_uri_parse_line() parsing edge cases: empty key or value' '
        cat >in <<-\EOF &&
        =bogus-value
@@ -136,6 +188,36 @@ test_expect_success 'parse config format: just URIs' '
        test_cmp_config_output expect actual
 '
 
+test_expect_success 'parse config format: relative URIs' '
+       cat >in <<-\EOF &&
+       [bundle]
+               version = 1
+               mode = all
+       [bundle "one"]
+               uri = bundle.bdl
+       [bundle "two"]
+               uri = ../bundle.bdl
+       [bundle "three"]
+               uri = sub/dir/bundle.bdl
+       EOF
+
+       cat >expect <<-\EOF &&
+       [bundle]
+               version = 1
+               mode = all
+       [bundle "one"]
+               uri = <uri>/bundle.bdl
+       [bundle "two"]
+               uri = bundle.bdl
+       [bundle "three"]
+               uri = <uri>/sub/dir/bundle.bdl
+       EOF
+
+       test-tool bundle-uri parse-config in >actual 2>err &&
+       test_must_be_empty err &&
+       test_cmp_config_output expect actual
+'
+
 test_expect_success 'parse config format edge cases: empty key or value' '
        cat >in1 <<-\EOF &&
        = bogus-value
@@ -168,4 +250,41 @@ test_expect_success 'parse config format edge cases: empty key or value' '
        test_cmp_config_output expect actual
 '
 
+test_expect_success 'parse config format: creationToken heuristic' '
+       cat >expect <<-\EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+       [bundle "one"]
+               uri = http://example.com/bundle.bdl
+               creationToken = 123456
+       [bundle "two"]
+               uri = https://example.com/bundle.bdl
+               creationToken = 12345678901234567890
+       [bundle "three"]
+               uri = file:///usr/share/git/bundle.bdl
+               creationToken = 1
+       EOF
+
+       test-tool bundle-uri parse-config expect >actual 2>err &&
+       test_must_be_empty err &&
+       test_cmp_config_output expect actual
+'
+
+test_expect_success 'parse config format edge cases: creationToken heuristic' '
+       cat >expect <<-\EOF &&
+       [bundle]
+               version = 1
+               mode = all
+               heuristic = creationToken
+       [bundle "one"]
+               uri = http://example.com/bundle.bdl
+               creationToken = bogus
+       EOF
+
+       test-tool bundle-uri parse-config expect >actual 2>err &&
+       grep "could not parse bundle list key creationToken with value '\''bogus'\''" err
+'
+
 test_done
index c1ef99b85c293e8a8bfc0872e3511fe8b7029213..862610256fb082a9ed62547cc51dc0a3cb754c48 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test disabling of local paths in clone/fetch'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY/lib-proto-disable.sh"
 
index 3f084ee306517b2bc991365f6f15b76627e1d125..2e975dc70ec2c8cdb8b92cada469439a99c1d544 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test disabling of git-over-ssh in clone/fetch'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY/lib-proto-disable.sh"
 
index 1f7d7dd20c1cf129c8a98ba59694f75e8e0de911..5cf2cee74dbcf3250491642983942905618eca92 100755 (executable)
@@ -326,19 +326,16 @@ a2
 c3
 EOF
 
-#
-# this test fails on --topo-order - a fix is required
-#
-#test_output_expect_success '--max-age=c3, --topo-order' "git rev-list --topo-order --max-age=$(commit_date c3) l5" <<EOF
-#l5
-#l4
-#l3
-#a4
-#c3
-#b4
-#a3
-#a2
-#EOF
+test_output_expect_success '--max-age=c3, --topo-order' "git rev-list --topo-order --max-age=$(commit_date c3) l5" <<EOF
+l5
+l4
+l3
+a4
+c3
+b4
+a3
+a2
+EOF
 
 test_output_expect_success 'one specified head reachable from another a4, c3, --topo-order' "list_duplicates git rev-list --topo-order a4 c3" <<EOF
 EOF
index bad02cf5b83dbc014c23cc8ac66135e0cf095138..b2e422cf0f7eaa43411fdb6f2bb769ee7294a322 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git rev-list should notice bad commits'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Note:
index c9bedd29cba4aac6b7db1aa8f1698fc5eac11b15..16b8bd1d090eae0449e409cd8313ffd45f769208 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='--all includes detached HEADs'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 
index 833205125abf5a2673ae0cbede62653fbfd151c8..7d40994991e423a21ae384010c00b5eb71c64502 100755 (executable)
@@ -11,6 +11,13 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-bundle.sh
 
+for cmd in create verify list-heads unbundle
+do
+       test_expect_success "usage: git bundle $cmd needs an argument" '
+               test_expect_code 129 git bundle $cmd
+       '
+done
+
 # Create a commit or tag and set the variable with the object ID.
 test_commit_setvar () {
        notick=
@@ -559,4 +566,44 @@ test_expect_success 'cloning from filtered bundle has useful error' '
        grep "cannot clone from filtered bundle" err
 '
 
+test_expect_success 'verify catches unreachable, broken prerequisites' '
+       test_when_finished rm -rf clone-from clone-to &&
+       git init clone-from &&
+       (
+               cd clone-from &&
+               git checkout -b base &&
+               test_commit A &&
+               git checkout -b tip &&
+               git commit --allow-empty -m "will drop by shallow" &&
+               git commit --allow-empty -m "will keep by shallow" &&
+               git commit --allow-empty -m "for bundle, not clone" &&
+               git bundle create tip.bundle tip~1..tip &&
+               git reset --hard HEAD~1 &&
+               git checkout base
+       ) &&
+       BAD_OID=$(git -C clone-from rev-parse tip~1) &&
+       TIP_OID=$(git -C clone-from rev-parse tip) &&
+       git clone --depth=1 --no-single-branch \
+               "file://$(pwd)/clone-from" clone-to &&
+       (
+               cd clone-to &&
+
+               # Set up broken history by removing shallow markers
+               git update-ref -d refs/remotes/origin/tip &&
+               rm .git/shallow &&
+
+               # Verify should fail
+               test_must_fail git bundle verify \
+                       ../clone-from/tip.bundle 2>err &&
+               grep "some prerequisite commits .* are not connected" err &&
+               test_line_count = 1 err &&
+
+               # Unbundling should fail
+               test_must_fail git bundle unbundle \
+                       ../clone-from/tip.bundle 2>err &&
+               grep "some prerequisite commits .* are not connected" err &&
+               test_line_count = 1 err
+       )
+'
+
 test_done
index 32b2b09413898f400e19de2e536532027556a310..11c50b7c0dd85c629ef9687eb81d36543939bddd 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git rev-list --exclude-hidden test'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 6dbbe62eb2203c4c12619463e01e438d0bfa93bc..3ba4fdf6153b69bce18bef335970742701b2dd34 100755 (executable)
@@ -34,6 +34,36 @@ HASH2=
 HASH3=
 HASH4=
 
+test_bisect_usage () {
+       local code="$1" &&
+       shift &&
+       cat >expect &&
+       test_expect_code $code "$@" >out 2>actual &&
+       test_must_be_empty out &&
+       test_cmp expect actual
+}
+
+test_expect_success 'bisect usage' "
+       test_bisect_usage 1 git bisect reset extra1 extra2 <<-\EOF &&
+       error: 'git bisect reset' requires either no argument or a commit
+       EOF
+       test_bisect_usage 1 git bisect terms extra1 extra2 <<-\EOF &&
+       error: 'git bisect terms' requires 0 or 1 argument
+       EOF
+       test_bisect_usage 1 git bisect next extra1 <<-\EOF &&
+       error: 'git bisect next' requires 0 arguments
+       EOF
+       test_bisect_usage 1 git bisect log extra1 <<-\EOF &&
+       error: We are not bisecting.
+       EOF
+       test_bisect_usage 1 git bisect replay <<-\EOF &&
+       error: no logfile given
+       EOF
+       test_bisect_usage 1 git bisect run <<-\EOF
+       error: 'git bisect run' failed: no command provided.
+       EOF
+"
+
 test_expect_success 'set up basic repo with 1 file (hello) and 4 commits' '
      add_line_into_file "1: Hello World" hello &&
      HASH1=$(git rev-parse --verify HEAD) &&
@@ -252,6 +282,124 @@ test_expect_success 'bisect skip: with commit both bad and skipped' '
        grep $HASH4 my_bisect_log.txt
 '
 
+test_bisect_run_args () {
+       test_when_finished "rm -f run.sh actual" &&
+       >actual &&
+       cat >expect.args &&
+       cat <&6 >expect.out &&
+       cat <&7 >expect.err &&
+       write_script run.sh <<-\EOF &&
+       while test $# != 0
+       do
+               echo "<$1>" &&
+               shift
+       done >actual.args
+       EOF
+
+       test_when_finished "git bisect reset" &&
+       git bisect start &&
+       git bisect good $HASH1 &&
+       git bisect bad $HASH4 &&
+       git bisect run ./run.sh $@ >actual.out.raw 2>actual.err &&
+       # Prune just the log output
+       sed -n \
+               -e '/^Author:/d' \
+               -e '/^Date:/d' \
+               -e '/^$/d' \
+               -e '/^commit /d' \
+               -e '/^ /d' \
+               -e 'p' \
+               <actual.out.raw >actual.out &&
+       test_cmp expect.out actual.out &&
+       test_cmp expect.err actual.err &&
+       test_cmp expect.args actual.args
+}
+
+test_expect_success 'git bisect run: args, stdout and stderr with no arguments' "
+       test_bisect_run_args <<-'EOF_ARGS' 6<<-EOF_OUT 7<<-'EOF_ERR'
+       EOF_ARGS
+       running './run.sh'
+       $HASH4 is the first bad commit
+       bisect found first bad commit
+       EOF_OUT
+       EOF_ERR
+"
+
+test_expect_success 'git bisect run: args, stdout and stderr: "--" argument' "
+       test_bisect_run_args -- <<-'EOF_ARGS' 6<<-EOF_OUT 7<<-'EOF_ERR'
+       <-->
+       EOF_ARGS
+       running './run.sh' '--'
+       $HASH4 is the first bad commit
+       bisect found first bad commit
+       EOF_OUT
+       EOF_ERR
+"
+
+test_expect_success 'git bisect run: args, stdout and stderr: "--log foo --no-log bar" arguments' "
+       test_bisect_run_args --log foo --no-log bar <<-'EOF_ARGS' 6<<-EOF_OUT 7<<-'EOF_ERR'
+       <--log>
+       <foo>
+       <--no-log>
+       <bar>
+       EOF_ARGS
+       running './run.sh' '--log' 'foo' '--no-log' 'bar'
+       $HASH4 is the first bad commit
+       bisect found first bad commit
+       EOF_OUT
+       EOF_ERR
+"
+
+test_expect_success 'git bisect run: args, stdout and stderr: "--bisect-start" argument' "
+       test_bisect_run_args --bisect-start <<-'EOF_ARGS' 6<<-EOF_OUT 7<<-'EOF_ERR'
+       <--bisect-start>
+       EOF_ARGS
+       running './run.sh' '--bisect-start'
+       $HASH4 is the first bad commit
+       bisect found first bad commit
+       EOF_OUT
+       EOF_ERR
+"
+
+test_expect_success 'git bisect run: negative exit code' "
+       write_script fail.sh <<-'EOF' &&
+       exit 255
+       EOF
+       cat <<-'EOF' >expect &&
+       bisect run failed: exit code -1 from './fail.sh' is < 0 or >= 128
+       EOF
+       test_when_finished 'git bisect reset' &&
+       git bisect start &&
+       git bisect good $HASH1 &&
+       git bisect bad $HASH4 &&
+       ! git bisect run ./fail.sh 2>err &&
+       sed -En 's/.*(bisect.*code) (-?[0-9]+) (from.*)/\1 -1 \3/p' err >actual &&
+       test_cmp expect actual
+"
+
+test_expect_success 'git bisect run: unable to verify on good' "
+       write_script fail.sh <<-'EOF' &&
+       head=\$(git rev-parse --verify HEAD)
+       good=\$(git rev-parse --verify $HASH1)
+       if test "\$head" = "\$good"
+       then
+               exit 255
+       else
+               exit 127
+       fi
+       EOF
+       cat <<-'EOF' >expect &&
+       unable to verify './fail.sh' on good revision
+       EOF
+       test_when_finished 'git bisect reset' &&
+       git bisect start &&
+       git bisect good $HASH1 &&
+       git bisect bad $HASH4 &&
+       ! git bisect run ./fail.sh 2>err &&
+       sed -n 's/.*\(unable to verify.*\)/\1/p' err >actual &&
+       test_cmp expect actual
+"
+
 # We want to automatically find the commit that
 # added "Another" into hello.
 test_expect_success '"git bisect run" simple case' '
@@ -910,6 +1058,16 @@ test_expect_success 'bisect start with one term1 and term2' '
        git bisect reset
 '
 
+test_expect_success 'bogus command does not start bisect' '
+       git bisect reset &&
+       test_must_fail git bisect --bisect-terms 1 2 2>out &&
+       ! grep "You need to start" out &&
+       test_must_fail git bisect --bisect-terms 2>out &&
+       ! grep "You need to start" out &&
+       grep "git bisect.*visualize" out &&
+       git bisect reset
+'
+
 test_expect_success 'bisect replay with term1 and term2' '
        git bisect replay log_to_replay.txt >bisect_result &&
        grep "$HASH2 is the first term1 commit" bisect_result &&
@@ -1000,7 +1158,6 @@ test_expect_success 'git bisect reset cleans bisection state properly' '
        test_path_is_missing ".git/BISECT_LOG" &&
        test_path_is_missing ".git/BISECT_RUN" &&
        test_path_is_missing ".git/BISECT_TERMS" &&
-       test_path_is_missing ".git/head-name" &&
        test_path_is_missing ".git/BISECT_HEAD" &&
        test_path_is_missing ".git/BISECT_START"
 '
@@ -1063,4 +1220,14 @@ test_expect_success 'bisect state output with bad commit' '
        grep -F "waiting for good commit(s), bad commit known" output
 '
 
+test_expect_success 'verify correct error message' '
+       git bisect reset &&
+       git bisect start $HASH4 $HASH1 &&
+       write_script test_script.sh <<-\EOF &&
+       rm .git/BISECT*
+       EOF
+       test_must_fail git bisect run ./test_script.sh 2>error &&
+       grep "git bisect good.*exited with error code" error
+'
+
 test_done
index ed449abe5520e59ddb0ad27def5711007f620650..1a8b64cce18239284a28260734a0348f95a2d7b6 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='basic git merge-index / git-merge-one-file tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup diverging branches' '
index 9a35e783a757b6fe9800ce475e65e73b880708c7..c9afcef2018b052a81600487c33846e2917c982f 100755 (executable)
@@ -657,4 +657,10 @@ test_expect_success 'setup: describe commits with disjoint bases 2' '
 
 check_describe -C disjoint2 "B-3-gHASH" HEAD
 
+test_expect_success 'setup misleading taggerdates' '
+       GIT_COMMITTER_DATE="2006-12-12 12:31" git tag -a -m "another tag" newer-tag-older-commit unique-file~1
+'
+
+check_describe newer-tag-older-commit~1 --contains unique-file~2
+
 test_done
index cada952f9aee35fe795af5cd99af222f1a8b599f..9fdafeb1e907f4381f2b1e74c6c00771d188d136 100755 (executable)
@@ -293,11 +293,7 @@ test_expect_success 'add with all negative' '
        test_cmp expect actual
 '
 
-test_lazy_prereq ADD_I_USE_BUILTIN_OR_PERL '
-       test_have_prereq ADD_I_USE_BUILTIN || test_have_prereq PERL
-'
-
-test_expect_success ADD_I_USE_BUILTIN_OR_PERL 'add -p with all negative' '
+test_expect_success 'add -p with all negative' '
        H=$(git rev-parse HEAD) &&
        git reset --hard $H &&
        git clean -f &&
index fa38b874416220d3d3c39ee48a99ad53b94dc2df..c466fd989f168b0fc696e83516474f11040a58be 100755 (executable)
@@ -606,7 +606,7 @@ test_expect_success 'create tag without tagger' '
        git tag -a -m "Broken tag" taggerless &&
        git tag -f taggerless $(git cat-file tag taggerless |
                sed -e "/^tagger /d" |
-               git hash-object --stdin -w -t tag)
+               git hash-object --literally --stdin -w -t tag)
 '
 
 test_atom refs/tags/taggerless type 'commit'
@@ -1242,6 +1242,24 @@ test_expect_success 'basic atom: rest must fail' '
        test_must_fail git for-each-ref --format="%(rest)" refs/heads/main
 '
 
+test_expect_success 'HEAD atom does not take arguments' '
+       test_must_fail git for-each-ref --format="%(HEAD:foo)" 2>err &&
+       echo "fatal: %(HEAD) does not take arguments" >expect &&
+       test_cmp expect err
+'
+
+test_expect_success 'subject atom rejects unknown arguments' '
+       test_must_fail git for-each-ref --format="%(subject:foo)" 2>err &&
+       echo "fatal: unrecognized %(subject) argument: foo" >expect &&
+       test_cmp expect err
+'
+
+test_expect_success 'refname atom rejects unknown arguments' '
+       test_must_fail git for-each-ref --format="%(refname:foo)" 2>err &&
+       echo "fatal: unrecognized %(refname) argument: foo" >expect &&
+       test_cmp expect err
+'
+
 test_expect_success 'trailer parsing not fooled by --- line' '
        git commit --allow-empty -F - <<-\EOF &&
        this is the subject
index 40edf9dab53402db9b5fad6dd060768537f9ba9a..bfda1f46ad25f49c7d51ce2e5853b1818314010f 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='for-each-ref errors for broken refs'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 ZEROS=$ZERO_OID
index 9d5e992878fad10f20c404a0dd676261345b2973..1962310408b12209aef235b9d6e09b8049de5f60 100755 (executable)
@@ -8,6 +8,8 @@
 
 
 test_description='Test criss-cross merge'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'prepare repository' '
index 8650a88c40a2a58182ee39f36cdc59315ae1a35c..5e4e4dd6d9e6b967f8c1d94fe7ce1a79c787a177 100755 (executable)
@@ -8,6 +8,7 @@ test_description='per path merge controlled by merge attribute'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index e8a28717cece3248c2d9996e797dcfdb9af128eb..0753fc95f45efb642543f9f23191d3430d4d6cde 100755 (executable)
@@ -5,6 +5,7 @@ test_description='ask merge-recursive to merge binary files'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 2655e295f5ae4536eaf86dcf95e8062f61cc2948..ae00492c768217a46d73fde7cfbc330dcc502616 100755 (executable)
@@ -4,6 +4,7 @@ test_description='merging when a directory was replaced with a symlink'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create a commit where dir a/b changed to symlink' '
index 5413e5dd9d68c36b95ecf9ccf5ca9e438ff0413b..711b709e755268ab1d02986c8aaa8257c27b41ee 100755 (executable)
@@ -155,7 +155,7 @@ test_setup_repo () {
 #   Commit A:
 #     (Rename leap->jump, rename basename/ -> basename/subdir/, rename dir/
 #      -> folder/, move e into newsubdir, add newfile.rs, remove f, modify
-#      both both Makefiles and jumps)
+#      both Makefiles and jumps)
 #              general/{jump1_A, jump2_A}
 #              basename/subdir/{numbers_A, sequence_A, values_A}
 #              folder/subdir/{a,b,c,d,Makefile_TOP_A}
@@ -343,7 +343,7 @@ test_expect_merge_algorithm failure success 'Objects downloaded when a directory
 #   Commit A:
 #     (Rename leap->jump, rename basename/ -> basename/subdir/, rename dir/
 #      -> folder/, move e into newsubdir, add newfile.rs, remove f, modify
-#      both both Makefiles and jumps)
+#      both Makefiles and jumps)
 #              general/{jump1_A, jump2_A}
 #              basename/subdir/{numbers_A, sequence_A, values_A}
 #              folder/subdir/{a,b,c,d,Makefile_TOP_A}
index 346253c7c88ee58ebf6735b6c06ae69f18ce5861..076b6a74d5b6de28c8499f687f7517daad849cfb 100755 (executable)
@@ -1159,7 +1159,6 @@ test_conflicts_with_adds_and_renames() {
        #   4) There should not be any three~* files in the working
        #      tree
        test_setup_collision_conflict () {
-       #test_expect_success "setup simple $sideL/$sideR conflict" '
                git init simple_${sideL}_${sideR} &&
                (
                        cd simple_${sideL}_${sideR} &&
@@ -1236,7 +1235,6 @@ test_conflicts_with_adds_and_renames() {
                        fi &&
                        test_tick && git commit -m R
                )
-       #'
        }
 
        test_expect_success "check simple $sideL/$sideR conflict" '
index 2bb8e7f09bb5897fa670aaf808e84ac7bc3516d3..fd21c1a48639bfc176986bfcd5a65c0aa82fd392 100755 (executable)
@@ -378,42 +378,30 @@ test_expect_success '2c: Modify b & add c VS rename b->c' '
                test_i18ngrep "CONFLICT (.*/add):" out &&
                test_must_be_empty err &&
 
-               # Make sure c WAS updated
+               git ls-files -s >index_files &&
+               test_line_count = 2 index_files &&
+
+               # Ensure b was removed
+               test_path_is_missing b &&
+
+               # Make sure c WAS updated...
                test-tool chmtime --get c >new-mtime &&
-               test $(cat old-mtime) -lt $(cat new-mtime)
-
-               # FIXME: rename/add conflicts are horribly broken right now;
-               # when I get back to my patch series fixing it and
-               # rename/rename(2to1) conflicts to bring them in line with
-               # how add/add conflicts behave, then checks like the below
-               # could be added.  But that patch series is waiting until
-               # the rename-directory-detection series lands, which this
-               # is part of.  And in the mean time, I do not want to further
-               # enforce broken behavior.  So for now, the main test is the
-               # one above that err is an empty file.
-
-               #git ls-files -s >index_files &&
-               #test_line_count = 2 index_files &&
-
-               #git rev-parse >actual :2:c :3:c &&
-               #git rev-parse >expect A:b  A:c  &&
-               #test_cmp expect actual &&
-
-               #git cat-file -p A:b >>merged &&
-               #git cat-file -p A:c >>merge-me &&
-               #>empty &&
-               #test_must_fail git merge-file \
-               #       -L "Temporary merge branch 1" \
-               #       -L "" \
-               #       -L "Temporary merge branch 2" \
-               #       merged empty merge-me &&
-               #sed -e "s/^\([<=>]\)/\1\1\1/" merged >merged-internal &&
-
-               #git hash-object c               >actual &&
-               #git hash-object merged-internal >expect &&
-               #test_cmp expect actual &&
-
-               #test_path_is_missing b
+               test $(cat old-mtime) -lt $(cat new-mtime) &&
+
+               # ...and has correct index entries and working tree contents
+               git rev-parse >actual :2:c :3:c &&
+               git rev-parse >expect A:c  A:b  &&
+               test_cmp expect actual &&
+
+               git cat-file -p A:b >>merge-me &&
+               git cat-file -p A:c >>merged &&
+               >empty &&
+               test_must_fail git merge-file \
+                       -L "HEAD" \
+                       -L "" \
+                       -L "B^0" \
+                       merged empty merge-me &&
+               test_cmp merged c
        )
 '
 
index fde4aa3cd1ab66a13c61234155da0c2bacb851a9..78628fb248ab6143ef83d11e819f15c30b52f278 100755 (executable)
@@ -3,6 +3,7 @@
 test_description='merge with sparse files'
 
 TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # test_file $filename $content
index 52cf0c87690be83e758b97c12b0ab00fa77613a2..0cbec57cdabc48b7cd952a6fbf5458391b44a645 100755 (executable)
@@ -5,6 +5,7 @@ test_description='unpack-trees error messages'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 
index 10662456aeef411141e60245391c50dad2545bfe..3968b47ed53b7e55eab26fdf8e34aa1472a1bf78 100755 (executable)
@@ -28,6 +28,7 @@ test_description='check pruning of dependent objects'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # We care about reachability, so we do not want to use
index 10faa645157ea4521e370abdfd470c84a24f6051..6f526c37c2776e6288a1abe5860b6a9efed25183 100755 (executable)
@@ -115,7 +115,7 @@ test_expect_success GPGSM 'verify and show signatures x509 with high minTrustLev
 
 test_expect_success GPG 'detect fudged signature' '
        git cat-file tag seventh-signed >raw &&
-       sed -e "/^tag / s/seventh/7th forged/" raw >forged1 &&
+       sed -e "/^tag / s/seventh/7th-forged/" raw >forged1 &&
        git hash-object -w -t tag forged1 >forged1.tag &&
        test_must_fail git verify-tag $(cat forged1.tag) 2>actual1 &&
        grep "BAD signature from" actual1 &&
index 1cb36b9ab83cdba01a554a94c982c968cd572f8b..36eb86a4b19f3c758b2830fe5157a1667069321a 100755 (executable)
@@ -125,7 +125,7 @@ test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-tag failes with tag date ou
 test_expect_success GPGSSH 'detect fudged ssh signature' '
        test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
        git cat-file tag seventh-signed >raw &&
-       sed -e "/^tag / s/seventh/7th forged/" raw >forged1 &&
+       sed -e "/^tag / s/seventh/7th-forged/" raw >forged1 &&
        git hash-object -w -t tag forged1 >forged1.tag &&
        test_must_fail git verify-tag $(cat forged1.tag) 2>actual1 &&
        grep "${GPGSSH_BAD_SIGNATURE}" actual1 &&
index a60153f9f32318c5ee31d2b16ec506a7a59ddd6d..18bbd9975ebfda330d0727c600bd0cec4b8b5181 100755 (executable)
@@ -63,7 +63,7 @@ test_expect_success '"mixed" reset is not allowed in bare' '
        test_must_fail git reset --mixed HEAD^
 '
 
-test_expect_success !SANITIZE_LEAK '"soft" reset is allowed in bare' '
+test_expect_success '"soft" reset is allowed in bare' '
        git reset --soft HEAD^ &&
        git show --pretty=format:%s >out &&
        echo one >expect &&
index fc2a6cf5c7a49a7971e23213d4d324799c2f92c9..9b46da7aaa7e59c08bf799262a21056fe7417d86 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='git reset --patch'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./lib-patch-mode.sh
 
 test_expect_success PERL 'setup' '
index ecb85c3b823275efc68fb64643a4304a3cbf28b2..a0b67a0b843b3411de060ca9f4e507e59d516dd2 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='git reset should work on unborn branch'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 523efbecde1dff517ffebf3c04cfcf74ea280f15..af5ea406db3bf5748d3cf1a1ae16abd3b6fd9363 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='reset --pathspec-from-file'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_tick
index a07e8b86de2014fd24bcb4692a744f147ef07787..d82a3210a1db48288c54ce3c06d430546da70f48 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git clean -i basic tests'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
index ebeca12a71115f60d10fa3ffa4725b1c7080f6f0..b19792b326901bbb8c00c20f07dbb38b322a9e74 100755 (executable)
@@ -5,6 +5,7 @@
 
 test_description='Test rebasing, stashing, etc. with submodules'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index ea92ef52a5eb9c593039614a6e42fa308a68b852..ff09443a0a4b0a5a4fdf1e14adc7e5d964a14dd6 100755 (executable)
@@ -11,6 +11,7 @@ These tests exercise the "git submodule sync" subcommand.
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 374ed481e9c64b9813a11b57c80f7b6f22c791e1..574a6fc526ea0c22ed2ca9df925854235632bb42 100755 (executable)
@@ -13,6 +13,7 @@ TEST_NO_CREATE_REPO=1
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 2859695c6d208341f5ba1de01e1cfe4131388a2b..f77832185765585e2bda1677f8cbbe13841127f7 100755 (executable)
@@ -10,6 +10,7 @@ TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a real submodule' '
+       cwd="$(pwd)" &&
        git init sub1 &&
        test_commit -C sub1 first &&
        git submodule add ./sub1 &&
@@ -18,13 +19,21 @@ test_expect_success 'setup a real submodule' '
 '
 
 test_expect_success 'absorb the git dir' '
+       >expect &&
+       >actual &&
        >expect.1 &&
        >expect.2 &&
        >actual.1 &&
        >actual.2 &&
        git status >expect.1 &&
        git -C sub1 rev-parse HEAD >expect.2 &&
-       git submodule absorbgitdirs &&
+       cat >expect <<-EOF &&
+       Migrating git directory of '\''sub1'\'' from
+       '\''$cwd/sub1/.git'\'' to
+       '\''$cwd/.git/modules/sub1'\''
+       EOF
+       git submodule absorbgitdirs 2>actual &&
+       test_cmp expect actual &&
        git fsck &&
        test -f sub1/.git &&
        test -d .git/modules/sub1 &&
@@ -37,7 +46,8 @@ test_expect_success 'absorb the git dir' '
 test_expect_success 'absorbing does not fail for deinitialized submodules' '
        test_when_finished "git submodule update --init" &&
        git submodule deinit --all &&
-       git submodule absorbgitdirs &&
+       git submodule absorbgitdirs 2>err &&
+       test_must_be_empty err &&
        test -d .git/modules/sub1 &&
        test -d sub1 &&
        ! test -e sub1/.git
@@ -56,7 +66,13 @@ test_expect_success 'setup nested submodule' '
 test_expect_success 'absorb the git dir in a nested submodule' '
        git status >expect.1 &&
        git -C sub1/nested rev-parse HEAD >expect.2 &&
-       git submodule absorbgitdirs &&
+       cat >expect <<-EOF &&
+       Migrating git directory of '\''sub1/nested'\'' from
+       '\''$cwd/sub1/nested/.git'\'' to
+       '\''$cwd/.git/modules/sub1/modules/nested'\''
+       EOF
+       git submodule absorbgitdirs 2>actual &&
+       test_cmp expect actual &&
        test -f sub1/nested/.git &&
        test -d .git/modules/sub1/modules/nested &&
        git status >actual.1 &&
@@ -87,7 +103,13 @@ test_expect_success 're-setup nested submodule' '
 test_expect_success 'absorb the git dir in a nested submodule' '
        git status >expect.1 &&
        git -C sub1/nested rev-parse HEAD >expect.2 &&
-       git submodule absorbgitdirs &&
+       cat >expect <<-EOF &&
+       Migrating git directory of '\''sub1'\'' from
+       '\''$cwd/sub1/.git'\'' to
+       '\''$cwd/.git/modules/sub1'\''
+       EOF
+       git submodule absorbgitdirs 2>actual &&
+       test_cmp expect actual &&
        test -f sub1/.git &&
        test -f sub1/nested/.git &&
        test -d .git/modules/sub1/modules/nested &&
@@ -97,6 +119,27 @@ test_expect_success 'absorb the git dir in a nested submodule' '
        test_cmp expect.2 actual.2
 '
 
+test_expect_success 'absorb the git dir outside of primary worktree' '
+       test_when_finished "rm -rf repo-bare.git" &&
+       git clone --bare . repo-bare.git &&
+       test_when_finished "rm -rf repo-wt" &&
+       git -C repo-bare.git worktree add ../repo-wt &&
+
+       test_when_finished "rm -f .gitconfig" &&
+       test_config_global protocol.file.allow always &&
+       git -C repo-wt submodule update --init &&
+       git init repo-wt/sub2 &&
+       test_commit -C repo-wt/sub2 A &&
+       git -C repo-wt submodule add ./sub2 sub2 &&
+       cat >expect <<-EOF &&
+       Migrating git directory of '\''sub2'\'' from
+       '\''$cwd/repo-wt/sub2/.git'\'' to
+       '\''$cwd/repo-bare.git/worktrees/repo-wt/modules/sub2'\''
+       EOF
+       git -C repo-wt submodule absorbgitdirs 2>actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'setup a gitlink with missing .gitmodules entry' '
        git init sub2 &&
        test_commit -C sub2 first &&
@@ -107,7 +150,11 @@ test_expect_success 'setup a gitlink with missing .gitmodules entry' '
 test_expect_success 'absorbing the git dir fails for incomplete submodules' '
        git status >expect.1 &&
        git -C sub2 rev-parse HEAD >expect.2 &&
-       test_must_fail git submodule absorbgitdirs &&
+       cat >expect <<-\EOF &&
+       fatal: could not lookup name for submodule '\''sub2'\''
+       EOF
+       test_must_fail git submodule absorbgitdirs 2>actual &&
+       test_cmp expect actual &&
        git -C sub2 fsck &&
        test -d sub2/.git &&
        git status >actual &&
@@ -127,8 +174,11 @@ test_expect_success 'setup a submodule with multiple worktrees' '
 '
 
 test_expect_success 'absorbing fails for a submodule with multiple worktrees' '
-       test_must_fail git submodule absorbgitdirs sub3 2>error &&
-       test_i18ngrep "not supported" error
+       cat >expect <<-\EOF &&
+       fatal: could not lookup name for submodule '\''sub2'\''
+       EOF
+       test_must_fail git submodule absorbgitdirs 2>actual &&
+       test_cmp expect actual
 '
 
 test_done
index 3ebd98598144c95165cc75870e6d586407add1c8..7cf72b9a07671c671c5ed7bbe275125ee7c142e6 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='check handling of disallowed .gitmodule urls'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index ba1f569bcbbdc613c768bd34e7de6e560b809c9d..0d0c3f2c683122dbf611cd0ac57bce86166acab3 100755 (executable)
@@ -12,6 +12,8 @@ Such as:
 
   - symlinked .gitmodules, etc
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-pack.sh
 
index a39de8c112674f070f8dc19745e27980b7b52c9c..07ca46fb0d539c62c7dcbe066b8c95e550d0b3a3 100755 (executable)
@@ -5,6 +5,7 @@ test_description='commit-msg hook'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'with no hook' '
index 2b7ef6c41a455423c0c62087a440e51bc4c72486..aed07c5b622e9a244313e781ceba0e6898024d21 100755 (executable)
@@ -1676,4 +1676,74 @@ test_expect_success 'racy timestamps will be fixed for dirty worktree' '
        ! test_is_magic_mtime .git/index
 '
 
+test_expect_success 'setup slow status advice' '
+       GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main git init slowstatus &&
+       (
+               cd slowstatus &&
+               cat >.gitignore <<-\EOF &&
+               /actual
+               /expected
+               /out
+               EOF
+               git add .gitignore &&
+               git commit -m "Add .gitignore" &&
+               git config advice.statusuoption true
+       )
+'
+
+test_expect_success 'slow status advice when core.untrackedCache and fsmonitor are unset' '
+       (
+               cd slowstatus &&
+               git config core.untrackedCache false &&
+               git config core.fsmonitor false &&
+               GIT_TEST_UF_DELAY_WARNING=1 git status >actual &&
+               cat >expected <<-\EOF &&
+               On branch main
+
+               It took 3.25 seconds to enumerate untracked files.
+               See '\''git help status'\'' for information on how to improve this.
+
+               nothing to commit, working tree clean
+               EOF
+               test_cmp expected actual
+       )
+'
+
+test_expect_success 'slow status advice when core.untrackedCache true, but not fsmonitor' '
+       (
+               cd slowstatus &&
+               git config core.untrackedCache true &&
+               git config core.fsmonitor false &&
+               GIT_TEST_UF_DELAY_WARNING=1 git status >actual &&
+               cat >expected <<-\EOF &&
+               On branch main
+
+               It took 3.25 seconds to enumerate untracked files.
+               See '\''git help status'\'' for information on how to improve this.
+
+               nothing to commit, working tree clean
+               EOF
+               test_cmp expected actual
+       )
+'
+
+test_expect_success 'slow status advice when core.untrackedCache true, and fsmonitor' '
+       (
+               cd slowstatus &&
+               git config core.untrackedCache true &&
+               git config core.fsmonitor true &&
+               GIT_TEST_UF_DELAY_WARNING=1 git status >actual &&
+               cat >expected <<-\EOF &&
+               On branch main
+
+               It took 3.25 seconds to enumerate untracked files,
+               but the results were cached, and subsequent runs may be faster.
+               See '\''git help status'\'' for information on how to improve this.
+
+               nothing to commit, working tree clean
+               EOF
+               test_cmp expected actual
+       )
+'
+
 test_done
index 21c668f75ed7345430f88f65b7e9072a873b9c42..5d890949f75b5acb4ef9334a41c080ece60c1653 100755 (executable)
@@ -105,7 +105,7 @@ test_expect_success '--amend option with empty author' '
 test_expect_success '--amend option with missing author' '
        git cat-file commit Initial >tmp &&
        sed "s/author [^<]* </author </" tmp >malformed &&
-       sha=$(git hash-object -t commit -w malformed) &&
+       sha=$(git hash-object --literally -t commit -w malformed) &&
        test_when_finished "remove_object $sha" &&
        git checkout $sha &&
        test_when_finished "git checkout Initial" &&
index 8593b7e3cb8d9aa0c4badeef4273ed406cecc9ca..48f86cb3678140ba1b0f914da8ebb442abd754b8 100755 (executable)
@@ -202,7 +202,7 @@ test_expect_success GPG 'detect fudged signature with NUL' '
        git cat-file commit seventh-signed >raw &&
        cat raw >forged2 &&
        echo Qwik | tr "Q" "\000" >>forged2 &&
-       git hash-object -w -t commit forged2 >forged2.commit &&
+       git hash-object --literally -w -t commit forged2 >forged2.commit &&
        test_must_fail git verify-commit $(cat forged2.commit) &&
        git show --pretty=short --show-signature $(cat forged2.commit) >actual2 &&
        grep "BAD signature from" actual2 &&
@@ -387,4 +387,48 @@ test_expect_success GPG 'verify-commit verifies multiply signed commits' '
        ! grep "BAD signature from" actual
 '
 
+test_expect_success 'custom `gpg.program`' '
+       write_script fake-gpg <<-\EOF &&
+       args="$*"
+
+       # skip uninteresting options
+       while case "$1" in
+       --status-fd=*|--keyid-format=*) ;; # skip
+       *) break;;
+       esac; do shift; done
+
+       case "$1" in
+       -bsau)
+               test -z "$LET_GPG_PROGRAM_FAIL" || {
+                       echo "zOMG signing failed!" >&2
+                       exit 1
+               }
+               cat >sign.file
+               echo "[GNUPG:] SIG_CREATED $args" >&2
+               echo "-----BEGIN PGP MESSAGE-----"
+               echo "$args"
+               echo "-----END PGP MESSAGE-----"
+               ;;
+       --verify)
+               cat "$2" >verify.file
+               exit 0
+               ;;
+       *)
+               echo "Unhandled args: $*" >&2
+               exit 1
+               ;;
+       esac
+       EOF
+
+       test_config gpg.program "$(pwd)/fake-gpg" &&
+       git commit -S --allow-empty -m signed-commit &&
+       test_path_exists sign.file &&
+       git show --show-signature &&
+       test_path_exists verify.file &&
+
+       test_must_fail env LET_GPG_PROGRAM_FAIL=1 \
+       git commit -S --allow-empty -m must-fail 2>err &&
+       grep zOMG err
+'
+
 test_done
index 163ae8046850e729ef5e329f6589faf5381e9cdc..efc6496e2b27f3ec74431d625e64e52c384e0383 100755 (executable)
@@ -9,6 +9,7 @@ test_description='per-repo forced setting of email address'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a likely user.useConfigOnly use case' '
index dc57526e6f1f7af741a4cd6c1ce4800563be5c37..184b2589893b78ef1e77521a6a9e7f0e5b514de9 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='ignored hook warning'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 4abc74db2bb8ede3879ab32ea771d688b8211eac..4c0327b2bb27af53e24b4158a63f6d2aec9ae19e 100755 (executable)
@@ -866,27 +866,9 @@ test_expect_success 'submodule always visited' '
 # the submodule, and someone does a `git submodule absorbgitdirs`
 # in the super, Git will recursively invoke `git submodule--helper`
 # to do the work and this may try to read the index.  This will
-# try to start the daemon in the submodule *and* pass (either
-# directly or via inheritance) the `--super-prefix` arg to the
-# `git fsmonitor--daemon start` command inside the submodule.
-# This causes a warning because fsmonitor--daemon does take that
-# global arg (see the table in git.c)
-#
-# This causes a warning when trying to start the daemon that is
-# somewhat confusing.  It does not seem to hurt anything because
-# the fsmonitor code maps the query failure into a trivial response
-# and does the work anyway.
-#
-# It would be nice to silence the warning, however.
-
-have_t2_error_event () {
-       log=$1
-       msg="fsmonitor--daemon doesnQt support --super-prefix" &&
-
-       tr '\047' Q <$1 | grep -e "$msg"
-}
+# try to start the daemon in the submodule.
 
-test_expect_success "stray submodule super-prefix warning" '
+test_expect_success "submodule absorbgitdirs implicitly starts daemon" '
        test_when_finished "rm -rf super; \
                            rm -rf sub;   \
                            rm super-sub.trace" &&
@@ -904,21 +886,31 @@ test_expect_success "stray submodule super-prefix warning" '
 
        test_path_is_dir super/dir_1/dir_2/sub/.git &&
 
+       cwd="$(cd super && pwd)" &&
+       cat >expect <<-EOF &&
+       Migrating git directory of '\''dir_1/dir_2/sub'\'' from
+       '\''$cwd/dir_1/dir_2/sub/.git'\'' to
+       '\''$cwd/.git/modules/dir_1/dir_2/sub'\''
+       EOF
        GIT_TRACE2_EVENT="$PWD/super-sub.trace" \
-               git -C super submodule absorbgitdirs &&
+               git -C super submodule absorbgitdirs >out 2>actual &&
+       test_cmp expect actual &&
+       test_must_be_empty out &&
 
-       ! have_t2_error_event super-sub.trace
+       # Confirm that the trace2 log contains a record of the
+       # daemon starting.
+       test_subcommand git fsmonitor--daemon start <super-sub.trace
 '
 
 # On a case-insensitive file system, confirm that the daemon
 # notices when the .git directory is moved/renamed/deleted
-# regardless of how it is spelled in the the FS event.
+# regardless of how it is spelled in the FS event.
 # That is, does the FS event receive the spelling of the
 # operation or does it receive the spelling preserved with
 # the file/directory.
 #
 test_expect_success CASE_INSENSITIVE_FS 'case insensitive+preserving' '
-#      test_when_finished "stop_daemon_delete_repo test_insensitive" &&
+       test_when_finished "stop_daemon_delete_repo test_insensitive" &&
 
        git init test_insensitive &&
 
@@ -930,8 +922,8 @@ test_expect_success CASE_INSENSITIVE_FS 'case insensitive+preserving' '
        test_path_is_dir test_insensitive/.git &&
        test_path_is_dir test_insensitive/.GIT &&
 
-       # Rename .git using an alternate spelling to verify that that
-       # daemon detects it and automatically shuts down.
+       # Rename .git using an alternate spelling to verify that
+       # the daemon detects it and automatically shuts down.
        mv test_insensitive/.GIT test_insensitive/.FOO &&
 
        # See [1] above.
index f47e99517983163e0bbb3fce9c35800270fe4e28..065f78063629cbfad15e9360836544da34a16484 100755 (executable)
@@ -270,7 +270,7 @@ test_expect_success GPGSSH 'detect fudged signature with NUL' '
        git cat-file commit seventh-signed >raw &&
        cat raw >forged2 &&
        echo Qwik | tr "Q" "\000" >>forged2 &&
-       git hash-object -w -t commit forged2 >forged2.commit &&
+       git hash-object --literally -w -t commit forged2 >forged2.commit &&
        test_must_fail git verify-commit $(cat forged2.commit) &&
        git show --pretty=short --show-signature $(cat forged2.commit) >actual2 &&
        grep "${GPGSSH_BAD_SIGNATURE}" actual2 &&
index 7c3f6ed99431839abc42533d520cd342f25dafd1..060e145957f3ad94933b37ebe6148ffbdf168d37 100755 (executable)
@@ -105,7 +105,7 @@ verify_mergeheads () {
        test_write_lines "$@" >mergehead.expected &&
        while read sha1 rest
        do
-               git rev-parse $sha1
+               git rev-parse $sha1 || return 1
        done <.git/MERGE_HEAD >mergehead.actual &&
        test_cmp mergehead.expected mergehead.actual
 }
index 5d56c3854647b2f2bc2fdf4221b1f76f5d0b0629..62d935d31c2e1dc9cbca3779407b6b86bc5ce4c5 100755 (executable)
@@ -4,6 +4,7 @@ test_description='git merge
 
 Testing the resolve strategy.'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 61330f71b1749c92a79153a3fce4f1834bfed248..f5c90cc22a1bba33368634c3679d2ea26cbc36bf 100755 (executable)
@@ -4,6 +4,7 @@ test_description='merge signature verification tests'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY/lib-gpg.sh"
 
index fee258d4f0c2c0d4b230891eeb93d522d27a4979..cf96a35e8e7b20975628cce6a5882c18c32fbeaa 100755 (executable)
@@ -8,6 +8,7 @@ This test runs git merge --signoff and makes sure that it works.
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Setup test files
index b7ac4f598a85c47b360b50726ca3d7b04840ff96..ebb267855fe06a0cfb3fa9a4d1cc21157f832d1d 100755 (executable)
@@ -5,6 +5,7 @@ test_description='git repack works correctly'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 fsha1=
index d751d48b7dae6a3e007d5eb19f92cd41eb445065..8bcd39e81bfb7f28768c988caf16f27773460b4e 100755 (executable)
@@ -201,7 +201,7 @@ committer David Reiss <dreiss@facebook.com> 1234567890 +0000
 
 some message
 EOF
-  COMMIT=$(git hash-object -t commit -w badcommit) &&
+  COMMIT=$(git hash-object --literally -t commit -w badcommit) &&
   git --no-pager blame $COMMIT -- uno >/dev/null
 '
 
index 1130ef21b342a83f02f7d5865fa726b80fd4ce01..323952a572d61b0cff9c05086f9735231c3b0db5 100755 (executable)
@@ -2334,6 +2334,12 @@ test_expect_success $PREREQ 'test that send-email works outside a repo' '
                "$(pwd)/0001-add-main.patch"
 '
 
+test_expect_success $PREREQ 'send-email relays -v 3 to format-patch' '
+       test_when_finished "rm -f out" &&
+       git send-email --dry-run -v 3 -1 >out &&
+       grep "PATCH v3" out
+'
+
 test_expect_success $PREREQ 'test that sendmail config is rejected' '
        test_config sendmail.program sendmail &&
        test_must_fail git send-email \
index f00deaf3815f3b49c42da4021335e011bbcb3c32..14a704d0a8c311ff776f65313178073e70b0a38f 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='help.autocorrect finding a match'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -60,4 +62,10 @@ test_expect_success 'autocorrect can be declined altogether' '
        test_line_count = 1 actual
 '
 
+test_expect_success 'autocorrect works in work tree created from bare repo' '
+       git clone --bare . bare.git &&
+       git -C bare.git worktree add ../worktree &&
+       git -C worktree -c help.autocorrect=immediate stauts
+'
+
 test_done
index 3cab0b9720a6544ade5caf91a3f678ad577cc3b0..bca496c40e0a22011465083d6b36aa32e48e86b5 100755 (executable)
@@ -3,7 +3,6 @@
 # Copyright (c) 2006 Eric Wong
 test_description='git svn commit-diff clobber'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
index 419f055721dbc32122e9d595d691c77bb8a24212..743fbe1fe46929583aacacc60ba959c38d8a2f09 100755 (executable)
@@ -5,7 +5,6 @@
 
 test_description='git svn dcommit can commit renames of files with ugly names'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'load repository with strange names' '
index 8201c3e808a931d07bc7e135f87bcc0499f65e20..088d1c57a88a209d57bdc95e45368f40af87c7a6 100755 (executable)
@@ -28,7 +28,7 @@ test_cmp_info () {
        rm -f tmp.expect tmp.actual
 }
 
-quoted_svnrepo="$(echo $svnrepo | sed 's/ /%20/')"
+quoted_svnrepo="$(echo $svnrepo | test_uri_escape)"
 
 test_expect_success 'setup repository and import' '
        mkdir info &&
index 79c26ed69c16cad42a32a555bb33635a154441e7..09606f1b3cfeb4244de4fbc12e4e96ddb7fd7de5 100755 (executable)
@@ -4,7 +4,6 @@
 
 test_description='git svn creates empty directories'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
index 6cc76a07b39a90d590759bbb7033da3953969092..aebb28995e50a0a589e8fff33997d9816de1fdb2 100755 (executable)
@@ -5,7 +5,6 @@
 
 test_description='git svn propset tests'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup propset via import' '
index 9cf7a1427ab081f2084b147b7dc4b1e3aa0f1005..36c6b1a12ffd95a1d6fe4ee4efd18a47f1581078 100755 (executable)
@@ -9,7 +9,6 @@ This test uses git to clone a Subversion repository that contains empty
 directories, and checks that corresponding directories are created in the
 local Git repository with placeholder files.'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 GIT_REPO=git-svn-repo
index 1465156072e32a75fb759784e65f9f7e2ca4c84a..c8e6c0733f41cf90a41941a3ad3550efb7ea56f1 100755 (executable)
@@ -5,7 +5,6 @@
 
 test_description='concurrent git svn dcommit'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 
index 25f500cf682a33787e0809bf5e7b513fd9282e4c..4432a30d10b69a168ca477f222bfa52d36447006 100755 (executable)
@@ -104,6 +104,13 @@ test_expect_success FSMONITOR_DAEMON 'scalar register starts fsmon daemon' '
        test_cmp_config -C test/src true core.fsmonitor
 '
 
+test_expect_success 'scalar register warns when background maintenance fails' '
+       git init register-repo &&
+       GIT_TEST_MAINT_SCHEDULER="crontab:false,launchctl:false,schtasks:false" \
+               scalar register register-repo 2>err &&
+       grep "could not turn on maintenance" err
+'
+
 test_expect_success 'scalar unregister' '
        git init vanish/src &&
        scalar register vanish/src &&
index dd33d87e9be16515276b4b75758ba5eb3a7f8d05..872ad1c9c2b156bf25797b2651c69e9749cf391e 100755 (executable)
@@ -3,6 +3,7 @@
 test_description='test the `scalar clone` subcommand'
 
 . ./test-lib.sh
+. "${TEST_DIRECTORY}/lib-terminal.sh"
 
 GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt,launchctl:true,schtasks:true"
 export GIT_TEST_MAINT_SCHEDULER
@@ -148,4 +149,35 @@ test_expect_success '--no-single-branch clones all branches' '
        cleanup_clone $enlistment
 '
 
+test_expect_success TTY 'progress with tty' '
+       enlistment=progress1 &&
+
+       test_config -C to-clone uploadpack.allowfilter true &&
+       test_config -C to-clone uploadpack.allowanysha1inwant true &&
+
+       test_terminal env GIT_PROGRESS_DELAY=0 \
+               scalar clone "file://$(pwd)/to-clone" "$enlistment" 2>stderr &&
+       grep "Enumerating objects" stderr >actual &&
+       test_line_count = 2 actual &&
+       cleanup_clone $enlistment
+'
+
+test_expect_success 'progress without tty' '
+       enlistment=progress2 &&
+
+       test_config -C to-clone uploadpack.allowfilter true &&
+       test_config -C to-clone uploadpack.allowanysha1inwant true &&
+
+       GIT_PROGRESS_DELAY=0 scalar clone "file://$(pwd)/to-clone" "$enlistment" 2>stderr &&
+       ! grep "Enumerating objects" stderr &&
+       ! grep "Updating files" stderr &&
+       cleanup_clone $enlistment
+'
+
+test_expect_success 'scalar clone warns when background maintenance fails' '
+       GIT_TEST_MAINT_SCHEDULER="crontab:false,launchctl:false,schtasks:false" \
+               scalar clone "file://$(pwd)/to-clone" maint-fail 2>err &&
+       grep "could not turn on maintenance" err
+'
+
 test_done
index ff21a12ee6e4eb1557eca22bb60ab2e01aa6b909..26c25c0eb2ba57fa90c682950fda4899329474f8 100755 (executable)
@@ -373,7 +373,7 @@ EOF
 
 test_expect_success 'cope with tagger-less tags' '
 
-       TAG=$(git hash-object -t tag -w tag-content) &&
+       TAG=$(git hash-object --literally -t tag -w tag-content) &&
        git update-ref refs/tags/sonnenschein $TAG &&
        git fast-export -C -C --signed-tags=strip --all > output &&
        test $(grep -c "^tag " output) = 4 &&
index 43de868b8005d330c10ec5d81fcacdc325aff858..d6c0478d98b85412307a422b142386c180ad030a 100755 (executable)
@@ -2255,6 +2255,36 @@ test_expect_success 'checkout completes ref names' '
        EOF
 '
 
+test_expect_success 'checkout does not match ref names of a different case' '
+       test_completion "git checkout M" ""
+'
+
+test_expect_success 'checkout matches case insensitively with GIT_COMPLETION_IGNORE_CASE' '
+       (
+               GIT_COMPLETION_IGNORE_CASE=1 &&
+               test_completion "git checkout M" <<-\EOF
+               main Z
+               mybranch Z
+               mytag Z
+               EOF
+       )
+'
+
+test_expect_success 'checkout completes pseudo refs' '
+       test_completion "git checkout H" <<-\EOF
+       HEAD Z
+       EOF
+'
+
+test_expect_success 'checkout completes pseudo refs case insensitively with GIT_COMPLETION_IGNORE_CASE' '
+       (
+               GIT_COMPLETION_IGNORE_CASE=1 &&
+               test_completion "git checkout h" <<-\EOF
+               HEAD Z
+               EOF
+       )
+'
+
 test_expect_success 'git -C <path> checkout uses the right repo' '
        test_completion "git -C subdir -C subsubdir -C .. -C ../otherrepo checkout b" <<-\EOF
        branch-in-other Z
index 796093a7b32f983a558d8ef6ea2f406c8edd70da..58cfd2f1fdaea1bfea1733a9b0cec1fc06dcf39a 100644 (file)
@@ -1016,7 +1016,7 @@ test_must_fail_acceptable () {
        fi
 
        case "$1" in
-       git|__git*|test-tool|test_terminal)
+       git|__git*|scalar|test-tool|test_terminal)
                return 0
                ;;
        *)
@@ -1422,7 +1422,7 @@ test_bool_env () {
                BUG "test_bool_env requires two parameters (variable name and default value)"
        fi
 
-       git env--helper --type=bool --default="$2" --exit-code "$1"
+       test-tool env-helper --type=bool --default="$2" --exit-code "$1"
        ret=$?
        case $ret in
        0|1)    # unset or valid bool value
@@ -1450,72 +1450,6 @@ test_skip_or_die () {
        error "$2"
 }
 
-# The following mingw_* functions obey POSIX shell syntax, but are actually
-# bash scripts, and are meant to be used only with bash on Windows.
-
-# A test_cmp function that treats LF and CRLF equal and avoids to fork
-# diff when possible.
-mingw_test_cmp () {
-       # Read text into shell variables and compare them. If the results
-       # are different, use regular diff to report the difference.
-       local test_cmp_a= test_cmp_b=
-
-       # When text came from stdin (one argument is '-') we must feed it
-       # to diff.
-       local stdin_for_diff=
-
-       # Since it is difficult to detect the difference between an
-       # empty input file and a failure to read the files, we go straight
-       # to diff if one of the inputs is empty.
-       if test -s "$1" && test -s "$2"
-       then
-               # regular case: both files non-empty
-               mingw_read_file_strip_cr_ test_cmp_a <"$1"
-               mingw_read_file_strip_cr_ test_cmp_b <"$2"
-       elif test -s "$1" && test "$2" = -
-       then
-               # read 2nd file from stdin
-               mingw_read_file_strip_cr_ test_cmp_a <"$1"
-               mingw_read_file_strip_cr_ test_cmp_b
-               stdin_for_diff='<<<"$test_cmp_b"'
-       elif test "$1" = - && test -s "$2"
-       then
-               # read 1st file from stdin
-               mingw_read_file_strip_cr_ test_cmp_a
-               mingw_read_file_strip_cr_ test_cmp_b <"$2"
-               stdin_for_diff='<<<"$test_cmp_a"'
-       fi
-       test -n "$test_cmp_a" &&
-       test -n "$test_cmp_b" &&
-       test "$test_cmp_a" = "$test_cmp_b" ||
-       eval "diff -u \"\$@\" $stdin_for_diff"
-}
-
-# $1 is the name of the shell variable to fill in
-mingw_read_file_strip_cr_ () {
-       # Read line-wise using LF as the line separator
-       # and use IFS to strip CR.
-       local line
-       while :
-       do
-               if IFS=$'\r' read -r -d $'\n' line
-               then
-                       # good
-                       line=$line$'\n'
-               else
-                       # we get here at EOF, but also if the last line
-                       # was not terminated by LF; in the latter case,
-                       # some text was read
-                       if test -z "$line"
-                       then
-                               # EOF, really
-                               break
-                       fi
-               fi
-               eval "$1=\$$1\$line"
-       done
-}
-
 # Like "env FOO=BAR some-program", but run inside a subshell, which means
 # it also works for shell functions (though those functions cannot impact
 # the environment outside of the test_env invocation).
@@ -1682,7 +1616,7 @@ test_oid () {
        then
                BUG "undefined key '$1'"
        fi &&
-       eval "printf '%s' \"\${$var}\""
+       eval "printf '%s\n' \"\${$var}\""
 }
 
 # Insert a slash into an object ID so it can be used to reference a location
@@ -1751,6 +1685,13 @@ test_path_is_hidden () {
        return 1
 }
 
+# Poor man's URI escaping. Good enough for the test suite whose trash
+# directory has a space in it. See 93c3fcbe4d4 (git-svn: attempt to
+# mimic SVN 1.7 URL canonicalization, 2012-07-28) for prior art.
+test_uri_escape() {
+       sed 's/ /%20/g'
+}
+
 # Check that the given command was invoked as part of the
 # trace2-format trace on stdin.
 #
@@ -1826,6 +1767,14 @@ test_region () {
        return 0
 }
 
+# Given a GIT_TRACE2_EVENT log over stdin, writes to stdout a list of URLs
+# sent to git-remote-https child processes.
+test_remote_https_urls() {
+       grep -e '"event":"child_start".*"argv":\["git-remote-https",".*"\]' |
+               sed -e 's/{"event":"child_start".*"argv":\["git-remote-https","//g' \
+                   -e 's/"\]}//g'
+}
+
 # Print the destination of symlink(s) provided as arguments. Basically
 # the same as the readlink command, but it's not available everywhere.
 test_readlink () {
@@ -1875,3 +1824,11 @@ test_cmp_config_output () {
        sort config-actual >sorted-actual &&
        test_cmp sorted-expect sorted-actual
 }
+
+# Given a filename, extract its trailing hash as a hex string
+test_trailing_hash () {
+       local file="$1" &&
+       tail -c $(test_oid rawsz) "$file" |
+               test-tool hexdump |
+               sed "s/ //g"
+}
index 6db377f68b82c41b297538855391d335275ff6da..d272cca008bb61ce44483515e2fec9c9ebd6cc4c 100644 (file)
@@ -1542,8 +1542,8 @@ then
        # Normalize with test_bool_env
        passes_sanitize_leak=
 
-       # We need to see TEST_PASSES_SANITIZE_LEAK in "git
-       # env--helper" (via test_bool_env)
+       # We need to see TEST_PASSES_SANITIZE_LEAK in "test-tool
+       # env-helper" (via test_bool_env)
        export TEST_PASSES_SANITIZE_LEAK
        if test_bool_env TEST_PASSES_SANITIZE_LEAK false
        then
@@ -1682,7 +1682,7 @@ yes () {
 # The GIT_TEST_FAIL_PREREQS code hooks into test_set_prereq(), and
 # thus needs to be set up really early, and set an internal variable
 # for convenience so the hot test_set_prereq() codepath doesn't need
-# to call "git env--helper" (via test_bool_env). Only do that work
+# to call "test-tool env-helper" (via test_bool_env). Only do that work
 # if needed by seeing if GIT_TEST_FAIL_PREREQS is set at all.
 GIT_TEST_FAIL_PREREQS_INTERNAL=
 if test -n "$GIT_TEST_FAIL_PREREQS"
@@ -1721,7 +1721,7 @@ case $uname_s in
        test_set_prereq SED_STRIPS_CR
        test_set_prereq GREP_STRIPS_CR
        test_set_prereq WINDOWS
-       GIT_TEST_CMP=mingw_test_cmp
+       GIT_TEST_CMP="GIT_DIR=/dev/null git diff --no-index --ignore-cr-at-eol --"
        ;;
 *CYGWIN*)
        test_set_prereq POSIXPERM
@@ -1937,10 +1937,6 @@ test_lazy_prereq SHA1 '
        esac
 '
 
-test_lazy_prereq ADD_I_USE_BUILTIN '
-       test_bool_env GIT_TEST_ADD_I_USE_BUILTIN true
-'
-
 # Ensure that no test accidentally triggers a Git command
 # that runs the actual maintenance scheduler, affecting a user's
 # system permanently.
index e95267a4ab54dc12318435d157d1fdd37821f567..3ea7c2bb5ad8c462ae0f54c9466af210bc27d0c2 100644 (file)
@@ -1267,9 +1267,22 @@ static struct ref *get_refs_list_using_list(struct transport *transport,
        return ret;
 }
 
+static int get_bundle_uri(struct transport *transport)
+{
+       get_helper(transport);
+
+       if (process_connect(transport, 0)) {
+               do_take_over(transport);
+               return transport->vtable->get_bundle_uri(transport);
+       }
+
+       return -1;
+}
+
 static struct transport_vtable vtable = {
        .set_option     = set_helper_option,
        .get_refs_list  = get_refs_list,
+       .get_bundle_uri = get_bundle_uri,
        .fetch_refs     = fetch_refs,
        .push_refs      = push_refs,
        .connect        = connect_helper,
index c4ca0b733acbdbe7fc7a5906490e396240ec7b06..90ea749e5cfbf7de994a8747186ad5fb3ae7c653 100644 (file)
@@ -26,6 +26,13 @@ struct transport_vtable {
        struct ref *(*get_refs_list)(struct transport *transport, int for_push,
                                     struct transport_ls_refs_options *transport_options);
 
+       /**
+        * Populates the remote side's bundle-uri under protocol v2,
+        * if the "bundle-uri" capability was advertised. Returns 0 if
+        * OK, negative values on error.
+        */
+       int (*get_bundle_uri)(struct transport *transport);
+
        /**
         * Fetch the objects for the given refs. Note that this gets
         * an array, and should ignore the list structure.
index e7b97194c103a3a7fad6d4174e40c96db9ff2a4c..77a61a9d7bb06a5cfc7992c6fc4f9f3aa8a8ada5 100644 (file)
@@ -22,6 +22,7 @@
 #include "protocol.h"
 #include "object-store.h"
 #include "color.h"
+#include "bundle-uri.h"
 
 static int transport_use_color = -1;
 static char transport_colors[][COLOR_MAXLEN] = {
@@ -197,7 +198,7 @@ struct git_transport_data {
        struct git_transport_options options;
        struct child_process *conn;
        int fd[2];
-       unsigned got_remote_heads : 1;
+       unsigned finished_handshake : 1;
        enum protocol_version version;
        struct oid_array extra_have;
        struct oid_array shallow;
@@ -344,7 +345,7 @@ static struct ref *handshake(struct transport *transport, int for_push,
        case protocol_unknown_version:
                BUG("unknown protocol version");
        }
-       data->got_remote_heads = 1;
+       data->finished_handshake = 1;
        transport->hash_algo = reader.hash_algo;
 
        if (reader.line_peeked)
@@ -359,6 +360,39 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
        return handshake(transport, for_push, options, 1);
 }
 
+static int get_bundle_uri(struct transport *transport)
+{
+       struct git_transport_data *data = transport->data;
+       struct packet_reader reader;
+       int stateless_rpc = transport->stateless_rpc;
+
+       if (!transport->bundles) {
+               CALLOC_ARRAY(transport->bundles, 1);
+               init_bundle_list(transport->bundles);
+       }
+
+       if (!data->finished_handshake) {
+               struct ref *refs = handshake(transport, 0, NULL, 0);
+
+               if (refs)
+                       free_refs(refs);
+       }
+
+       /*
+        * "Support" protocol v0 and v2 without bundle-uri support by
+        * silently degrading to a NOOP.
+        */
+       if (!server_supports_v2("bundle-uri"))
+               return 0;
+
+       packet_reader_init(&reader, data->fd[0], NULL, 0,
+                          PACKET_READ_CHOMP_NEWLINE |
+                          PACKET_READ_GENTLE_ON_EOF);
+
+       return get_remote_bundle_uri(data->fd[1], &reader,
+                                    transport->bundles, stateless_rpc);
+}
+
 static int fetch_refs_via_pack(struct transport *transport,
                               int nr_heads, struct ref **to_fetch)
 {
@@ -394,7 +428,7 @@ static int fetch_refs_via_pack(struct transport *transport,
        args.negotiation_tips = data->options.negotiation_tips;
        args.reject_shallow_remote = transport->smart_options->reject_shallow;
 
-       if (!data->got_remote_heads) {
+       if (!data->finished_handshake) {
                int i;
                int must_list_refs = 0;
                for (i = 0; i < nr_heads; i++) {
@@ -434,7 +468,7 @@ static int fetch_refs_via_pack(struct transport *transport,
                          to_fetch, nr_heads, &data->shallow,
                          &transport->pack_lockfiles, data->version);
 
-       data->got_remote_heads = 0;
+       data->finished_handshake = 0;
        data->options.self_contained_and_connected =
                args.self_contained_and_connected;
        data->options.connectivity_checked = args.connectivity_checked;
@@ -819,7 +853,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
        if (transport_color_config() < 0)
                return -1;
 
-       if (!data->got_remote_heads)
+       if (!data->finished_handshake)
                get_refs_via_connect(transport, 1, NULL);
 
        memset(&args, 0, sizeof(args));
@@ -867,7 +901,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
        else
                ret = finish_connect(data->conn);
        data->conn = NULL;
-       data->got_remote_heads = 0;
+       data->finished_handshake = 0;
 
        return ret;
 }
@@ -887,7 +921,7 @@ static int disconnect_git(struct transport *transport)
 {
        struct git_transport_data *data = transport->data;
        if (data->conn) {
-               if (data->got_remote_heads && !transport->stateless_rpc)
+               if (data->finished_handshake && !transport->stateless_rpc)
                        packet_flush(data->fd[1]);
                close(data->fd[0]);
                if (data->fd[1] >= 0)
@@ -902,6 +936,7 @@ static int disconnect_git(struct transport *transport)
 
 static struct transport_vtable taken_over_vtable = {
        .get_refs_list  = get_refs_via_connect,
+       .get_bundle_uri = get_bundle_uri,
        .fetch_refs     = fetch_refs_via_pack,
        .push_refs      = git_transport_push,
        .disconnect     = disconnect_git
@@ -921,7 +956,7 @@ void transport_take_over(struct transport *transport,
        data->conn = child;
        data->fd[0] = data->conn->out;
        data->fd[1] = data->conn->in;
-       data->got_remote_heads = 0;
+       data->finished_handshake = 0;
        transport->data = data;
 
        transport->vtable = &taken_over_vtable;
@@ -1054,6 +1089,7 @@ static struct transport_vtable bundle_vtable = {
 
 static struct transport_vtable builtin_smart_vtable = {
        .get_refs_list  = get_refs_via_connect,
+       .get_bundle_uri = get_bundle_uri,
        .fetch_refs     = fetch_refs_via_pack,
        .push_refs      = git_transport_push,
        .connect        = connect_git,
@@ -1068,6 +1104,9 @@ struct transport *transport_get(struct remote *remote, const char *url)
        ret->progress = isatty(2);
        string_list_init_dup(&ret->pack_lockfiles);
 
+       CALLOC_ARRAY(ret->bundles, 1);
+       init_bundle_list(ret->bundles);
+
        if (!remote)
                BUG("No remote provided to transport_get()");
 
@@ -1118,7 +1157,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
                ret->smart_options = &(data->options);
 
                data->conn = NULL;
-               data->got_remote_heads = 0;
+               data->finished_handshake = 0;
        } else {
                /* Unknown protocol in URL. Pass to external handler. */
                int len = external_specification_len(url);
@@ -1482,6 +1521,34 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs)
        return rc;
 }
 
+int transport_get_remote_bundle_uri(struct transport *transport)
+{
+       int value = 0;
+       const struct transport_vtable *vtable = transport->vtable;
+
+       /* Check config only once. */
+       if (transport->got_remote_bundle_uri)
+               return 0;
+       transport->got_remote_bundle_uri = 1;
+
+       /*
+        * Don't request bundle-uri from the server unless configured to
+        * do so by the transfer.bundleURI=true config option.
+        */
+       if (git_config_get_bool("transfer.bundleuri", &value) || !value)
+               return 0;
+
+       if (!transport->bundles->baseURI)
+               transport->bundles->baseURI = xstrdup(transport->url);
+
+       if (!vtable->get_bundle_uri)
+               return error(_("bundle-uri operation not supported by protocol"));
+
+       if (vtable->get_bundle_uri(transport) < 0)
+               return error(_("could not retrieve server-advertised bundle-uri list"));
+       return 0;
+}
+
 void transport_unlock_pack(struct transport *transport, unsigned int flags)
 {
        int in_signal_handler = !!(flags & TRANSPORT_UNLOCK_PACK_IN_SIGNAL_HANDLER);
@@ -1512,6 +1579,8 @@ int transport_disconnect(struct transport *transport)
                ret = transport->vtable->disconnect(transport);
        if (transport->got_remote_refs)
                free_refs((void *)transport->remote_refs);
+       clear_bundle_list(transport->bundles);
+       free(transport->bundles);
        free(transport);
        return ret;
 }
index b5bf7b3e70418a9ded196bf124b1e445bc8076cb..85150f504fbfb9501109d297223f566cc36fe0e5 100644 (file)
@@ -62,6 +62,7 @@ enum transport_family {
        TRANSPORT_FAMILY_IPV6
 };
 
+struct bundle_list;
 struct transport {
        const struct transport_vtable *vtable;
 
@@ -76,6 +77,18 @@ struct transport {
         */
        unsigned got_remote_refs : 1;
 
+       /**
+        * Indicates whether we already called get_bundle_uri_list(); set by
+        * transport.c::transport_get_remote_bundle_uri().
+        */
+       unsigned got_remote_bundle_uri : 1;
+
+       /*
+        * The results of "command=bundle-uri", if both sides support
+        * the "bundle-uri" capability.
+        */
+       struct bundle_list *bundles;
+
        /*
         * Transports that call take-over destroys the data specific to
         * the transport type while doing so, and cannot be reused.
@@ -281,6 +294,12 @@ void transport_ls_refs_options_release(struct transport_ls_refs_options *opts);
 const struct ref *transport_get_remote_refs(struct transport *transport,
                                            struct transport_ls_refs_options *transport_options);
 
+/**
+ * Retrieve bundle URI(s) from a remote. Populates "struct
+ * transport"'s "bundle_uri" and "got_remote_bundle_uri".
+ */
+int transport_get_remote_bundle_uri(struct transport *transport);
+
 /*
  * Fetch the hash algorithm used by a remote.
  *
index 8a762aa0772eb4f7e107da6807092fe0a2be1a61..3d05e45a279b0b0231da5d9a5829c76818af18bd 100644 (file)
@@ -19,7 +19,6 @@
 #include "promisor-remote.h"
 #include "entry.h"
 #include "parallel-checkout.h"
-#include "sparse-index.h"
 
 /*
  * Error messages expected by scripts out of plumbing commands such as
@@ -71,7 +70,7 @@ static const char *unpack_plumbing_errors[NB_UNPACK_TREES_WARNING_TYPES] = {
          ? ((o)->msgs[(type)])      \
          : (unpack_plumbing_errors[(type)]) )
 
-static const char *super_prefixed(const char *path)
+static const char *super_prefixed(const char *path, const char *super_prefix)
 {
        /*
         * It is necessary and sufficient to have two static buffers
@@ -83,7 +82,6 @@ static const char *super_prefixed(const char *path)
        static unsigned idx = ARRAY_SIZE(buf) - 1;
 
        if (super_prefix_len < 0) {
-               const char *super_prefix = get_super_prefix();
                if (!super_prefix) {
                        super_prefix_len = 0;
                } else {
@@ -236,7 +234,8 @@ static int add_rejected_path(struct unpack_trees_options *o,
                return -1;
 
        if (!o->show_all_errors)
-               return error(ERRORMSG(o, e), super_prefixed(path));
+               return error(ERRORMSG(o, e), super_prefixed(path,
+                                                           o->super_prefix));
 
        /*
         * Otherwise, insert in a list for future display by
@@ -263,7 +262,8 @@ static void display_error_msgs(struct unpack_trees_options *o)
                        error_displayed = 1;
                        for (i = 0; i < rejects->nr; i++)
                                strbuf_addf(&path, "\t%s\n", rejects->items[i].string);
-                       error(ERRORMSG(o, e), super_prefixed(path.buf));
+                       error(ERRORMSG(o, e), super_prefixed(path.buf,
+                                                            o->super_prefix));
                        strbuf_release(&path);
                }
                string_list_clear(rejects, 0);
@@ -290,7 +290,8 @@ static void display_warning_msgs(struct unpack_trees_options *o)
                        warning_displayed = 1;
                        for (i = 0; i < rejects->nr; i++)
                                strbuf_addf(&path, "\t%s\n", rejects->items[i].string);
-                       warning(ERRORMSG(o, e), super_prefixed(path.buf));
+                       warning(ERRORMSG(o, e), super_prefixed(path.buf,
+                                                              o->super_prefix));
                        strbuf_release(&path);
                }
                string_list_clear(rejects, 0);
@@ -312,7 +313,8 @@ static int check_submodule_move_head(const struct cache_entry *ce,
        if (o->reset)
                flags |= SUBMODULE_MOVE_HEAD_FORCE;
 
-       if (submodule_move_head(ce->name, old_id, new_id, flags))
+       if (submodule_move_head(ce->name, o->super_prefix, old_id, new_id,
+                               flags))
                return add_rejected_path(o, ERROR_WOULD_LOSE_SUBMODULE, ce->name);
        return 0;
 }
@@ -415,6 +417,7 @@ static int check_updates(struct unpack_trees_options *o,
        int i, pc_workers, pc_threshold;
 
        trace_performance_enter();
+       state.super_prefix = o->super_prefix;
        state.force = 1;
        state.quiet = 1;
        state.refresh_cache = 1;
@@ -445,7 +448,7 @@ static int check_updates(struct unpack_trees_options *o,
 
                if (ce->ce_flags & CE_WT_REMOVE) {
                        display_progress(progress, ++cnt);
-                       unlink_entry(ce);
+                       unlink_entry(ce, o->super_prefix);
                }
        }
 
@@ -1877,7 +1880,8 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        prepare_repo_settings(repo);
        if (repo->settings.command_requires_full_index) {
                ensure_full_index(o->src_index);
-               ensure_full_index(o->dst_index);
+               if (o->dst_index)
+                       ensure_full_index(o->dst_index);
        }
 
        if (o->reset == UNPACK_RESET_OVERWRITE_UNTRACKED &&
@@ -1901,7 +1905,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                populate_from_existing_patterns(o, &pl);
        }
 
-       memset(&o->result, 0, sizeof(o->result));
+       index_state_init(&o->result, o->src_index->repo);
        o->result.initialized = 1;
        o->result.timestamp.sec = o->src_index->timestamp.sec;
        o->result.timestamp.nsec = o->src_index->timestamp.nsec;
@@ -2959,8 +2963,8 @@ int bind_merge(const struct cache_entry * const *src,
        if (a && old)
                return o->quiet ? -1 :
                        error(ERRORMSG(o, ERROR_BIND_OVERLAP),
-                             super_prefixed(a->name),
-                             super_prefixed(old->name));
+                             super_prefixed(a->name, o->super_prefix),
+                             super_prefixed(old->name, o->super_prefix));
        if (!a)
                return keep_entry(old, o);
        else
@@ -3021,7 +3025,7 @@ int stash_worktree_untracked_merge(const struct cache_entry * const *src,
 
        if (worktree && untracked)
                return error(_("worktree and untracked commit have duplicate entries: %s"),
-                            super_prefixed(worktree->name));
+                            super_prefixed(worktree->name, o->super_prefix));
 
        return merged_entry(worktree ? worktree : untracked, NULL, o);
 }
index 6ab0d74c84dc5ca3004e8b3b160dde98d1805e5d..3a7b3e5f0077938f1e28de5f41a691191c098744 100644 (file)
@@ -75,6 +75,7 @@ struct unpack_trees_options {
                     skip_cache_tree_update;
        enum unpack_trees_reset_type reset;
        const char *prefix;
+       const char *super_prefix;
        int cache_bottom;
        struct pathspec *pathspec;
        merge_fn_t fn;
index b615adc923ae019b756e25a1cdcfb251333e69ff..620a648efc5e81ac8b233153ae504ba145d6ee8f 100644 (file)
@@ -209,7 +209,7 @@ static char *url_normalize_1(const char *url, struct url_info *out_info, char al
         */
        if (!url_len || strchr(":/?#", *url)) {
                /* Missing host invalid for all URL schemes except file */
-               if (strncmp(norm.buf, "file:", 5)) {
+               if (!starts_with(norm.buf, "file:")) {
                        if (out_info) {
                                out_info->url = NULL;
                                out_info->err = _("missing host and scheme is not 'file:'");
@@ -268,11 +268,11 @@ static char *url_normalize_1(const char *url, struct url_info *out_info, char al
                if (url == slash_ptr) {
                        /* Skip ":" port with no number, it's same as default */
                } else if (slash_ptr - url == 2 &&
-                          !strncmp(norm.buf, "http:", 5) &&
+                          starts_with(norm.buf, "http:") &&
                           !strncmp(url, "80", 2)) {
                        /* Skip http :80 as it's the default */
                } else if (slash_ptr - url == 3 &&
-                          !strncmp(norm.buf, "https:", 6) &&
+                          starts_with(norm.buf, "https:") &&
                           !strncmp(url, "443", 3)) {
                        /* Skip https :443 as it's the default */
                } else {
index 151d9a52784b1f4a149913d38739c91e389d95d6..58a3d59ef8f471b9c6ffc34cad10e56689c3354c 100644 (file)
@@ -170,8 +170,8 @@ PATTERNS("html",
         "[^<>= \t]+"),
 PATTERNS("java",
         "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
-        /* Class, enum, and interface declarations */
-        "^[ \t]*(([a-z]+[ \t]+)*(class|enum|interface)[ \t]+[A-Za-z][A-Za-z0-9_$]*[ \t]+.*)$\n"
+        /* Class, enum, interface, and record declarations */
+        "^[ \t]*(([a-z-]+[ \t]+)*(class|enum|interface|record)[ \t]+.*)$\n"
         /* Method definitions; note that constructor signatures are not */
         /* matched because they are indistinguishable from method calls. */
         "^[ \t]*(([A-Za-z_<>&][][?&<>.,A-Za-z_0-9]*[ \t]+)+[A-Za-z_][A-Za-z_0-9]*[ \t]*\\([^;]*)$",
@@ -293,7 +293,7 @@ PATTERNS("scheme",
         "|([^][)(}{[ \t])+"),
 PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
         "\\\\[a-zA-Z@]+|\\\\.|[a-zA-Z0-9\x80-\xff]+"),
-{ "default", NULL, -1, { NULL, 0 } },
+{ "default", NULL, NULL, -1, { NULL, 0 } },
 };
 #undef PATTERNS
 #undef IPATTERN
@@ -315,7 +315,8 @@ struct find_by_namelen_data {
 };
 
 static int userdiff_find_by_namelen_cb(struct userdiff_driver *driver,
-                                      enum userdiff_driver_type type, void *priv)
+                                      enum userdiff_driver_type type UNUSED,
+                                      void *priv)
 {
        struct find_by_namelen_data *cb_data = priv;
 
@@ -393,6 +394,8 @@ int userdiff_config(const char *k, const char *v)
                return parse_bool(&drv->textconv_want_cache, k, v);
        if (!strcmp(type, "wordregex"))
                return git_config_string(&drv->word_regex, k, v);
+       if (!strcmp(type, "algorithm"))
+               return git_config_string(&drv->algorithm, k, v);
 
        return 0;
 }
@@ -412,7 +415,7 @@ struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,
                check = attr_check_initl("diff", NULL);
        if (!path)
                return NULL;
-       git_check_attr(istate, path, check);
+       git_check_attr(istate, NULL, path, check);
 
        if (ATTR_TRUE(check->items[0].value))
                return &driver_true;
index aee91bc77e6d592021d8597da7fce0123c779a32..24419db6973f5cd9bdfc6b9f300d5ce472c15cfd 100644 (file)
@@ -14,6 +14,7 @@ struct userdiff_funcname {
 struct userdiff_driver {
        const char *name;
        const char *external;
+       const char *algorithm;
        int binary;
        struct userdiff_funcname funcname;
        const char *word_regex;
diff --git a/ws.c b/ws.c
index 6e69877f25791632d98bf7b109a2eaebd04c96af..da3d0e28cbba9cb5e5d459f8e807a9a0fcf42102 100644 (file)
--- a/ws.c
+++ b/ws.c
@@ -29,6 +29,7 @@ unsigned parse_whitespace_rule(const char *string)
                int i;
                size_t len;
                const char *ep;
+               const char *arg;
                int negated = 0;
 
                string = string + strspn(string, ", \t\n\r");
@@ -52,15 +53,15 @@ unsigned parse_whitespace_rule(const char *string)
                                rule |= whitespace_rule_names[i].rule_bits;
                        break;
                }
-               if (strncmp(string, "tabwidth=", 9) == 0) {
-                       unsigned tabwidth = atoi(string + 9);
+               if (skip_prefix(string, "tabwidth=", &arg)) {
+                       unsigned tabwidth = atoi(arg);
                        if (0 < tabwidth && tabwidth < 0100) {
                                rule &= ~WS_TAB_WIDTH_MASK;
                                rule |= tabwidth;
                        }
                        else
                                warning("tabwidth %.*s out of range",
-                                       (int)(len - 9), string + 9);
+                                       (int)(ep - arg), arg);
                }
                string = ep;
        }
@@ -78,7 +79,7 @@ unsigned whitespace_rule(struct index_state *istate, const char *pathname)
        if (!attr_whitespace_rule)
                attr_whitespace_rule = attr_check_initl("whitespace", NULL);
 
-       git_check_attr(istate, pathname, attr_whitespace_rule);
+       git_check_attr(istate, NULL, pathname, attr_whitespace_rule);
        value = attr_whitespace_rule->items[0].value;
        if (ATTR_TRUE(value)) {
                /* true (whitespace) */
@@ -252,7 +253,7 @@ unsigned ws_check(const char *line, int len, unsigned ws_rule)
        return ws_check_emit_1(line, len, ws_rule, NULL, NULL, NULL, NULL);
 }
 
-int ws_blank_line(const char *line, int len, unsigned ws_rule)
+int ws_blank_line(const char *line, int len)
 {
        /*
         * We _might_ want to treat CR differently from other
index 5813174896cc9ae5fa9287c8bcc432dc8cf2ef47..3162241a570cb60393607f06a8a12963b71afb78 100644 (file)
 #include "worktree.h"
 #include "lockfile.h"
 #include "sequencer.h"
+#include "fsmonitor-settings.h"
 
 #define AB_DELAY_WARNING_IN_MS (2 * 1000)
+#define UF_DELAY_WARNING_IN_MS (2 * 1000)
 
 static const char cut_line[] =
 "------------------------ >8 ------------------------\n";
@@ -438,7 +440,7 @@ static char short_submodule_status(struct wt_status_change_data *d)
 }
 
 static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
-                                        struct diff_options *options,
+                                        struct diff_options *options UNUSED,
                                         void *data)
 {
        struct wt_status *s = data;
@@ -525,7 +527,7 @@ static int unmerged_mask(struct index_state *istate, const char *path)
 }
 
 static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
-                                        struct diff_options *options,
+                                        struct diff_options *options UNUSED,
                                         void *data)
 {
        struct wt_status *s = data;
@@ -1205,6 +1207,13 @@ static void wt_longstatus_print_tracking(struct wt_status *s)
        strbuf_release(&sb);
 }
 
+static int uf_was_slow(struct wt_status *s)
+{
+       if (getenv("GIT_TEST_UF_DELAY_WARNING"))
+               s->untracked_in_ms = 3250;
+       return UF_DELAY_WARNING_IN_MS < s->untracked_in_ms;
+}
+
 static void show_merge_in_progress(struct wt_status *s,
                                   const char *color)
 {
@@ -1814,6 +1823,7 @@ static void wt_longstatus_print(struct wt_status *s)
 {
        const char *branch_color = color(WT_STATUS_ONBRANCH, s);
        const char *branch_status_color = color(WT_STATUS_HEADER, s);
+       enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(s->repo);
 
        if (s->branch) {
                const char *on_what = _("On branch ");
@@ -1870,13 +1880,21 @@ static void wt_longstatus_print(struct wt_status *s)
                wt_longstatus_print_other(s, &s->untracked, _("Untracked files"), "add");
                if (s->show_ignored_mode)
                        wt_longstatus_print_other(s, &s->ignored, _("Ignored files"), "add -f");
-               if (advice_enabled(ADVICE_STATUS_U_OPTION) && 2000 < s->untracked_in_ms) {
+               if (advice_enabled(ADVICE_STATUS_U_OPTION) && uf_was_slow(s)) {
                        status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
+                       if (fsm_mode > FSMONITOR_MODE_DISABLED) {
+                               status_printf_ln(s, GIT_COLOR_NORMAL,
+                                               _("It took %.2f seconds to enumerate untracked files,\n"
+                                               "but the results were cached, and subsequent runs may be faster."),
+                                               s->untracked_in_ms / 1000.0);
+                       } else {
+                               status_printf_ln(s, GIT_COLOR_NORMAL,
+                                               _("It took %.2f seconds to enumerate untracked files."),
+                                               s->untracked_in_ms / 1000.0);
+                       }
                        status_printf_ln(s, GIT_COLOR_NORMAL,
-                                        _("It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
-                                          "may speed it up, but you have to be careful not to forget to add\n"
-                                          "new files yourself (see 'git help status')."),
-                                        s->untracked_in_ms / 1000.0);
+                                       _("See 'git help status' for information on how to improve this."));
+                       status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
                }
        } else if (s->committable)
                status_printf_ln(s, GIT_COLOR_NORMAL, _("Untracked files not listed%s"),
index 32652ded2d7c5cad87f120a13c3c6c84d6def61f..344c2dfc3eb443a721cc45b6ba3be56e9f186c97 100644 (file)
@@ -973,7 +973,7 @@ void xdl_free_script(xdchange_t *xscr) {
        }
 }
 
-static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+static int xdl_call_hunk_func(xdfenv_t *xe UNUSED, xdchange_t *xscr, xdemitcb_t *ecb,
                              xdemitconf_t const *xecfg)
 {
        xdchange_t *xch, *xche;
index c4ccd68d4760bc08735d6ca3b7dcdd3440abaf16..75f0fe498661c43078fe19f8b5d10926700763de 100644 (file)
@@ -95,7 +95,7 @@ xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)
 }
 
 
-static long def_ff(const char *rec, long len, char *buf, long sz, void *priv)
+static long def_ff(const char *rec, long len, char *buf, long sz)
 {
        if (len > 0 &&
                        (isalpha((unsigned char)*rec) || /* identifier? */
@@ -117,7 +117,7 @@ static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,
        const char *rec;
        long len = xdl_get_rec(xdf, ri, &rec);
        if (!xecfg->find_func)
-               return def_ff(rec, len, buf, sz, xecfg->find_func_priv);
+               return def_ff(rec, len, buf, sz);
        return xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv);
 }