aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/check-whitespace.yml63
-rw-r--r--.github/workflows/l10n.yml6
-rw-r--r--.github/workflows/main.yml48
-rw-r--r--Documentation/RelNotes/2.30.7.txt86
-rw-r--r--Documentation/RelNotes/2.31.6.txt5
-rw-r--r--Documentation/RelNotes/2.32.5.txt8
-rw-r--r--Documentation/RelNotes/2.33.6.txt5
-rw-r--r--Documentation/RelNotes/2.34.6.txt5
-rw-r--r--Documentation/RelNotes/2.35.6.txt5
-rw-r--r--Documentation/RelNotes/2.36.4.txt5
-rw-r--r--Documentation/RelNotes/2.37.5.txt5
-rw-r--r--Documentation/RelNotes/2.38.3.txt5
-rw-r--r--Documentation/RelNotes/2.39.1.txt5
-rw-r--r--Documentation/RelNotes/2.40.0.txt67
-rw-r--r--Documentation/config/feature.txt5
-rw-r--r--Documentation/config/format.txt4
-rw-r--r--Documentation/config/index.txt11
-rw-r--r--Documentation/config/transfer.txt6
-rw-r--r--Documentation/fsck-msgids.txt12
-rw-r--r--Documentation/git-am.txt8
-rw-r--r--Documentation/git-branch.txt2
-rw-r--r--Documentation/git-cat-file.txt53
-rw-r--r--Documentation/git.txt8
-rw-r--r--Documentation/gitattributes.txt2
-rw-r--r--Documentation/gitprotocol-v2.txt201
-rw-r--r--INSTALL2
-rw-r--r--attr.c91
-rw-r--r--attr.h12
-rw-r--r--builtin.h4
-rw-r--r--builtin/am.c11
-rw-r--r--builtin/bundle.c9
-rw-r--r--builtin/cat-file.c28
-rw-r--r--builtin/checkout.c4
-rw-r--r--builtin/clone.c21
-rw-r--r--builtin/log.c8
-rw-r--r--builtin/read-tree.c1
-rw-r--r--builtin/remote-ext.c6
-rw-r--r--builtin/remote-fd.c2
-rw-r--r--builtin/rm.c2
-rw-r--r--builtin/submodule--helper.c92
-rw-r--r--bundle-uri.c87
-rw-r--r--bundle-uri.h35
-rw-r--r--cache-tree.c2
-rw-r--r--cache.h2
-rw-r--r--column.c2
-rw-r--r--commit.c15
-rw-r--r--compat/mingw.c2
-rw-r--r--compat/win32/pthread.h2
-rw-r--r--config.c2
-rw-r--r--connect.c44
-rw-r--r--csum-file.c14
-rw-r--r--csum-file.h7
-rw-r--r--diff.c30
-rw-r--r--dir.c7
-rw-r--r--entry.c12
-rw-r--r--entry.h6
-rw-r--r--environment.c13
-rw-r--r--fsck.c137
-rw-r--r--fsck.h12
-rw-r--r--git-compat-util.h8
-rw-r--r--git-curl-compat.h8
-rw-r--r--git.c41
-rw-r--r--http-push.c6
-rw-r--r--http.c79
-rw-r--r--http.h2
-rw-r--r--line-range.c7
-rw-r--r--object-file.c148
-rw-r--r--object-store.h25
-rw-r--r--packfile.c26
-rw-r--r--parse-options.h4
-rw-r--r--pretty.c63
-rw-r--r--read-cache.c14
-rw-r--r--ref-filter.c2
-rw-r--r--refs/packed-backend.c3
-rw-r--r--remote-curl.c28
-rw-r--r--remote.h5
-rw-r--r--repo-settings.c2
-rw-r--r--repository.h1
-rw-r--r--run-command.c2
-rw-r--r--scalar.c1
-rw-r--r--serve.c6
-rw-r--r--strbuf.c6
-rw-r--r--strbuf.h11
-rw-r--r--streaming.c11
-rw-r--r--submodule.c50
-rw-r--r--submodule.h8
-rw-r--r--t/helper/test-bundle-uri.c41
-rw-r--r--t/lib-bundle-uri-protocol.sh216
-rwxr-xr-xt/t0000-basic.sh7
-rwxr-xr-xt/t0003-attributes.sh59
-rwxr-xr-xt/t1001-read-tree-m-2way.sh2
-rwxr-xr-xt/t1091-sparse-checkout-builtin.sh11
-rwxr-xr-xt/t1301-shared-repo.sh14
-rwxr-xr-xt/t1302-repo-version.sh2
-rwxr-xr-xt/t1450-fsck.sh24
-rwxr-xr-xt/t1500-rev-parse.sh2
-rwxr-xr-xt/t1509-root-work-tree.sh10
-rwxr-xr-xt/t1600-index.sh30
-rwxr-xr-xt/t4014-format-patch.sh6
-rwxr-xr-xt/t4044-diff-index-unique-abbrev.sh4
-rwxr-xr-xt/t4045-diff-relative.sh29
-rwxr-xr-xt/t4150-am.sh34
-rwxr-xr-xt/t4203-mailmap.sh65
-rwxr-xr-xt/t4205-log-pretty-formats.sh76
-rwxr-xr-xt/t4211-line-log.sh22
-rwxr-xr-xt/t4301-merge-tree-write-tree.sh4
-rwxr-xr-xt/t5313-pack-bounds-checks.sh2
-rwxr-xr-xt/t5601-clone.sh59
-rwxr-xr-xt/t5616-partial-clone.sh43
-rwxr-xr-xt/t5701-git-serve.sh40
-rwxr-xr-xt/t5730-protocol-v2-bundle-uri-file.sh17
-rwxr-xr-xt/t5731-protocol-v2-bundle-uri-git.sh17
-rwxr-xr-xt/t5732-protocol-v2-bundle-uri-http.sh17
-rwxr-xr-xt/t5750-bundle-uri-parse.sh82
-rwxr-xr-xt/t6020-bundle-misc.sh7
-rwxr-xr-xt/t6421-merge-partial-clone.sh4
-rwxr-xr-xt/t7412-submodule-absorbgitdirs.sh64
-rwxr-xr-xt/t7527-builtin-fsmonitor.sh42
-rwxr-xr-xt/t9119-git-svn-info.sh2
-rw-r--r--t/test-lib-functions.sh83
-rw-r--r--t/test-lib.sh2
-rw-r--r--transport-helper.c13
-rw-r--r--transport-internal.h7
-rw-r--r--transport.c87
-rw-r--r--transport.h19
-rw-r--r--unpack-trees.c24
-rw-r--r--unpack-trees.h1
-rw-r--r--urlmatch.c6
-rw-r--r--utf8.c77
-rw-r--r--utf8.h2
-rw-r--r--ws.c7
131 files changed, 2641 insertions, 662 deletions
diff --git a/.github/workflows/check-whitespace.yml b/.github/workflows/check-whitespace.yml
index ad3466ad16..a58e2dc8ad 100644
--- a/.github/workflows/check-whitespace.yml
+++ b/.github/workflows/check-whitespace.yml
@@ -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
diff --git a/.github/workflows/l10n.yml b/.github/workflows/l10n.yml
index f7ea0f00a4..6c3849658a 100644
--- a/.github/workflows/l10n.yml
+++ b/.github/workflows/l10n.yml
@@ -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: >-
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index e67847a682..30492eacdd 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -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
diff --git a/Documentation/RelNotes/2.30.7.txt b/Documentation/RelNotes/2.30.7.txt
new file mode 100644
index 0000000000..285beed232
--- /dev/null
+++ b/Documentation/RelNotes/2.30.7.txt
@@ -0,0 +1,86 @@
+Git v2.30.7 Release Notes
+=========================
+
+This release addresses the security issues CVE-2022-41903 and
+CVE-2022-23521.
+
+
+Fixes since v2.30.6
+-------------------
+
+ * CVE-2022-41903:
+
+ git log has the ability to display commits using an arbitrary
+ format with its --format specifiers. This functionality is also
+ exposed to git archive via the export-subst gitattribute.
+
+ When processing the padding operators (e.g., %<(, %<|(, %>(,
+ %>>(, or %><( ), an integer overflow can occur in
+ pretty.c::format_and_pad_commit() where a size_t is improperly
+ stored as an int, and then added as an offset to a subsequent
+ memcpy() call.
+
+ This overflow can be triggered directly by a user running a
+ command which invokes the commit formatting machinery (e.g., git
+ log --format=...). It may also be triggered indirectly through
+ git archive via the export-subst mechanism, which expands format
+ specifiers inside of files within the repository during a git
+ archive.
+
+ This integer overflow can result in arbitrary heap writes, which
+ may result in remote code execution.
+
+* CVE-2022-23521:
+
+ gitattributes are a mechanism to allow defining attributes for
+ paths. These attributes can be defined by adding a `.gitattributes`
+ file to the repository, which contains a set of file patterns and
+ the attributes that should be set for paths matching this pattern.
+
+ When parsing gitattributes, multiple integer overflows can occur
+ when there is a huge number of path patterns, a huge number of
+ attributes for a single pattern, or when the declared attribute
+ names are huge.
+
+ These overflows can be triggered via a crafted `.gitattributes` file
+ that may be part of the commit history. Git silently splits lines
+ longer than 2KB when parsing gitattributes from a file, but not when
+ parsing them from the index. Consequentially, the failure mode
+ depends on whether the file exists in the working tree, the index or
+ both.
+
+ This integer overflow can result in arbitrary heap reads and writes,
+ which may result in remote code execution.
+
+Credit for finding CVE-2022-41903 goes to Joern Schneeweisz of GitLab.
+An initial fix was authored by Markus Vervier of X41 D-Sec. Credit for
+finding CVE-2022-23521 goes to Markus Vervier and Eric Sesterhenn of X41
+D-Sec. This work was sponsored by OSTIF.
+
+The proposed fixes have been polished and extended to cover additional
+findings by Patrick Steinhardt of GitLab, with help from others on the
+Git security mailing list.
+
+Patrick Steinhardt (21):
+ attr: fix overflow when upserting attribute with overly long name
+ attr: fix out-of-bounds read with huge attribute names
+ attr: fix integer overflow when parsing huge attribute names
+ attr: fix out-of-bounds write when parsing huge number of attributes
+ attr: fix out-of-bounds read with unreasonable amount of patterns
+ attr: fix integer overflow with more than INT_MAX macros
+ attr: harden allocation against integer overflows
+ attr: fix silently splitting up lines longer than 2048 bytes
+ attr: ignore attribute lines exceeding 2048 bytes
+ attr: ignore overly large gitattributes files
+ pretty: fix out-of-bounds write caused by integer overflow
+ pretty: fix out-of-bounds read when left-flushing with stealing
+ pretty: fix out-of-bounds read when parsing invalid padding format
+ pretty: fix adding linefeed when placeholder is not expanded
+ pretty: fix integer overflow in wrapping format
+ utf8: fix truncated string lengths in `utf8_strnwidth()`
+ utf8: fix returning negative string width
+ utf8: fix overflow when returning string width
+ utf8: fix checking for glyph width in `strbuf_utf8_replace()`
+ utf8: refactor `strbuf_utf8_replace` to not rely on preallocated buffer
+ pretty: restrict input lengths for padding and wrapping formats
+
diff --git a/Documentation/RelNotes/2.31.6.txt b/Documentation/RelNotes/2.31.6.txt
new file mode 100644
index 0000000000..425a51875a
--- /dev/null
+++ b/Documentation/RelNotes/2.31.6.txt
@@ -0,0 +1,5 @@
+Git v2.31.6 Release Notes
+=========================
+
+This release merges the security fix that appears in v2.30.7; see
+the release notes for that version for details.
diff --git a/Documentation/RelNotes/2.32.5.txt b/Documentation/RelNotes/2.32.5.txt
new file mode 100644
index 0000000000..a8cad1a05b
--- /dev/null
+++ b/Documentation/RelNotes/2.32.5.txt
@@ -0,0 +1,8 @@
+Git v2.32.5 Release Notes
+=========================
+
+This release merges the security fix that appears in v2.30.7; see
+the release notes for that version for details.
+
+In addition, included are additional code for "git fsck" to check
+for questionable .gitattributes files.
diff --git a/Documentation/RelNotes/2.33.6.txt b/Documentation/RelNotes/2.33.6.txt
new file mode 100644
index 0000000000..b63e4e6256
--- /dev/null
+++ b/Documentation/RelNotes/2.33.6.txt
@@ -0,0 +1,5 @@
+Git v2.33.6 Release Notes
+=========================
+
+This release merges the security fix that appears in v2.30.7; see
+the release notes for that version for details.
diff --git a/Documentation/RelNotes/2.34.6.txt b/Documentation/RelNotes/2.34.6.txt
new file mode 100644
index 0000000000..b32080dba8
--- /dev/null
+++ b/Documentation/RelNotes/2.34.6.txt
@@ -0,0 +1,5 @@
+Git v2.34.6 Release Notes
+=========================
+
+This release merges the security fix that appears in v2.30.7; see
+the release notes for that version for details.
diff --git a/Documentation/RelNotes/2.35.6.txt b/Documentation/RelNotes/2.35.6.txt
new file mode 100644
index 0000000000..e7ca57bb41
--- /dev/null
+++ b/Documentation/RelNotes/2.35.6.txt
@@ -0,0 +1,5 @@
+Git v2.35.6 Release Notes
+=========================
+
+This release merges the security fix that appears in v2.30.7; see
+the release notes for that version for details.
diff --git a/Documentation/RelNotes/2.36.4.txt b/Documentation/RelNotes/2.36.4.txt
new file mode 100644
index 0000000000..58fb93a35f
--- /dev/null
+++ b/Documentation/RelNotes/2.36.4.txt
@@ -0,0 +1,5 @@
+Git v2.36.4 Release Notes
+=========================
+
+This release merges the security fix that appears in v2.30.7; see
+the release notes for that version for details.
diff --git a/Documentation/RelNotes/2.37.5.txt b/Documentation/RelNotes/2.37.5.txt
new file mode 100644
index 0000000000..faa1447292
--- /dev/null
+++ b/Documentation/RelNotes/2.37.5.txt
@@ -0,0 +1,5 @@
+Git v2.37.5 Release Notes
+=========================
+
+This release merges the security fix that appears in v2.30.7; see
+the release notes for that version for details.
diff --git a/Documentation/RelNotes/2.38.3.txt b/Documentation/RelNotes/2.38.3.txt
new file mode 100644
index 0000000000..4a46bb4300
--- /dev/null
+++ b/Documentation/RelNotes/2.38.3.txt
@@ -0,0 +1,5 @@
+Git v2.38.3 Release Notes
+=========================
+
+This release merges the security fix that appears in v2.30.7; see
+the release notes for that version for details.
diff --git a/Documentation/RelNotes/2.39.1.txt b/Documentation/RelNotes/2.39.1.txt
new file mode 100644
index 0000000000..60c86f4122
--- /dev/null
+++ b/Documentation/RelNotes/2.39.1.txt
@@ -0,0 +1,5 @@
+Git v2.39.1 Release Notes
+=========================
+
+This release merges the security fix that appears in v2.30.7; see
+the release notes for that version for details.
diff --git a/Documentation/RelNotes/2.40.0.txt b/Documentation/RelNotes/2.40.0.txt
index 634f0ae33a..07ae9f20c3 100644
--- a/Documentation/RelNotes/2.40.0.txt
+++ b/Documentation/RelNotes/2.40.0.txt
@@ -23,6 +23,18 @@ UI, Workflows & Features
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.
+
Performance, Internal Implementation, Development Support etc.
@@ -34,6 +46,32 @@ Performance, Internal Implementation, Development Support etc.
* 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.
+ (merge a0da6deeec js/ci-disable-cmake-by-default later to maint).
+
+ * 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.
+ (merge 4542582e59 cw/ci-whitespace later to maint).
+
+ * 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.
+ (merge eb5b03a9c0 tb/ci-concurrency later to maint).
+
Fixes since v2.39
-----------------
@@ -82,6 +120,30 @@ Fixes since v2.39
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.
+ (merge 4e57c88e02 lk/line-range-parsing-fix later to maint).
+
+ * Fix the sequence to fsync $GIT_DIR/packed-refs file that forgot to
+ flush its output to the disk..
+ (merge ce54672f9b ps/fsync-refs-fix later to maint).
+
+ * Fix to a small regression in 2.38 days.
+ (merge 6d5e9e53aa ab/bundle-wo-args later to maint).
+
+ * "git diff --relative" did not mix well with "git diff --ext-diff",
+ which has been corrected.
+ (merge f034bb1cad jk/ext-diff-with-relative later to maint).
+
+ * 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.
+ (merge 5842710dc2 ws/single-file-cone later to maint).
+
+ * Deal with a few deprecation warning from cURL library.
+ (merge 6c065f72b8 jk/curl-avoid-deprecated-api later to maint).
+
* Other code cleanup, docfix, build fix, etc.
(merge 77e04b2ed4 rs/t4205-do-not-exit-in-test-script later to maint).
(merge faebba436e rs/plug-pattern-list-leak-in-lof later to maint).
@@ -98,3 +160,8 @@ Fixes since v2.39
(merge b07a819c05 rs/reflog-expiry-cleanup later to maint).
(merge d422d06167 rs/clarify-error-in-write-loose-object later to maint).
(merge 92cb135855 sk/remove-duplicate-includes later to maint).
+ (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).
diff --git a/Documentation/config/feature.txt b/Documentation/config/feature.txt
index 95975e5091..e52bc6b858 100644
--- a/Documentation/config/feature.txt
+++ b/Documentation/config/feature.txt
@@ -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
diff --git a/Documentation/config/format.txt b/Documentation/config/format.txt
index c7303d8d9f..3bd78269e2 100644
--- a/Documentation/config/format.txt
+++ b/Documentation/config/format.txt
@@ -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.
diff --git a/Documentation/config/index.txt b/Documentation/config/index.txt
index 75f3a2d105..23c7985eb4 100644
--- a/Documentation/config/index.txt
+++ b/Documentation/config/index.txt
@@ -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`.
diff --git a/Documentation/config/transfer.txt b/Documentation/config/transfer.txt
index 264812cca4..c3ac767d1e 100644
--- a/Documentation/config/transfer.txt
+++ b/Documentation/config/transfer.txt
@@ -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`.
diff --git a/Documentation/fsck-msgids.txt b/Documentation/fsck-msgids.txt
index 7af76ff99a..12eae8a222 100644
--- a/Documentation/fsck-msgids.txt
+++ b/Documentation/fsck-msgids.txt
@@ -46,6 +46,18 @@
`fullPathname`::
(WARN) A path contains the full path starting with "/".
+`gitattributesBlob`::
+ (ERROR) A non-blob found at `.gitattributes`.
+
+`gitattributesLarge`::
+ (ERROR) The `.gitattributes` blob is too large.
+
+`gitattributesLineLength`::
+ (ERROR) The `.gitattributes` blob contains too long lines.
+
+`gitattributesMissing`::
+ (ERROR) Unable to read `.gitattributes` blob.
+
`gitattributesSymlink`::
(INFO) `.gitattributes` is a symlink.
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 326276e51c..0c1dfb3c98 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -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
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 12c5f84e3b..aa2f78c4c2 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -116,7 +116,7 @@ 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
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index ec30b5c574..830f0a2eff 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -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,49 @@ 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:
+
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 1d33e083ab..f9a7a4554c 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -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
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 4b36d51beb..c19e64ea0e 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -1155,7 +1155,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.
diff --git a/Documentation/gitprotocol-v2.txt b/Documentation/gitprotocol-v2.txt
index 59bf41cefb..acb97ad0c2 100644
--- a/Documentation/gitprotocol-v2.txt
+++ b/Documentation/gitprotocol-v2.txt
@@ -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
diff --git a/INSTALL b/INSTALL
index 3344788397..d5694f8c47 100644
--- a/INSTALL
+++ b/INSTALL
@@ -139,7 +139,7 @@ Issues of note:
not need that functionality, use NO_CURL to build without
it.
- Git requires version "7.19.4" or later of "libcurl" to build
+ Git requires version "7.19.5" or later of "libcurl" to build
without NO_CURL. This version requirement may be bumped in
the future.
diff --git a/attr.c b/attr.c
index e4e304a826..7783b87d8a 100644
--- a/attr.c
+++ b/attr.c
@@ -24,7 +24,7 @@ static const char git_attr__unknown[] = "(builtin)unknown";
#define ATTR__UNKNOWN git_attr__unknown
struct git_attr {
- int attr_nr; /* unique attribute number */
+ unsigned int attr_nr; /* unique attribute number */
char name[FLEX_ARRAY]; /* attribute name */
};
@@ -206,7 +206,7 @@ static void report_invalid_attr(const char *name, size_t len,
* dictionary. If no entry is found, create a new attribute and store it in
* the dictionary.
*/
-static const struct git_attr *git_attr_internal(const char *name, int namelen)
+static const struct git_attr *git_attr_internal(const char *name, size_t namelen)
{
struct git_attr *a;
@@ -222,8 +222,8 @@ static const struct git_attr *git_attr_internal(const char *name, int namelen)
a->attr_nr = hashmap_get_size(&g_attr_hashmap.map);
attr_hashmap_add(&g_attr_hashmap, a->name, namelen, a);
- assert(a->attr_nr ==
- (hashmap_get_size(&g_attr_hashmap.map) - 1));
+ if (a->attr_nr != hashmap_get_size(&g_attr_hashmap.map) - 1)
+ die(_("unable to add additional attribute"));
}
hashmap_unlock(&g_attr_hashmap);
@@ -268,7 +268,7 @@ struct match_attr {
const struct git_attr *attr;
} u;
char is_macro;
- unsigned num_attr;
+ size_t num_attr;
struct attr_state state[FLEX_ARRAY];
};
@@ -289,7 +289,7 @@ static const char *parse_attr(const char *src, int lineno, const char *cp,
struct attr_state *e)
{
const char *ep, *equals;
- int len;
+ size_t len;
ep = cp + strcspn(cp, blank);
equals = strchr(cp, '=');
@@ -333,8 +333,7 @@ static const char *parse_attr(const char *src, int lineno, const char *cp,
static struct match_attr *parse_attr_line(const char *line, const char *src,
int lineno, unsigned flags)
{
- int namelen;
- int num_attr, i;
+ size_t namelen, num_attr, i;
const char *cp, *name, *states;
struct match_attr *res = NULL;
int is_macro;
@@ -345,6 +344,11 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
return NULL;
name = cp;
+ if (strlen(line) >= ATTR_MAX_LINE_LENGTH) {
+ warning(_("ignoring overly long attributes line %d"), lineno);
+ return NULL;
+ }
+
if (*cp == '"' && !unquote_c_style(&pattern, name, &states)) {
name = pattern.buf;
namelen = pattern.len;
@@ -381,10 +385,9 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
goto fail_return;
}
- res = xcalloc(1,
- sizeof(*res) +
- sizeof(struct attr_state) * num_attr +
- (is_macro ? 0 : namelen + 1));
+ res = xcalloc(1, st_add3(sizeof(*res),
+ st_mult(sizeof(struct attr_state), num_attr),
+ is_macro ? 0 : namelen + 1));
if (is_macro) {
res->u.attr = git_attr_internal(name, namelen);
} else {
@@ -447,11 +450,12 @@ struct attr_stack {
static void attr_stack_free(struct attr_stack *e)
{
- int i;
+ unsigned i;
free(e->origin);
for (i = 0; i < e->num_matches; i++) {
struct match_attr *a = e->attrs[i];
- int j;
+ size_t j;
+
for (j = 0; j < a->num_attr; j++) {
const char *setto = a->state[j].setto;
if (setto == ATTR__TRUE ||
@@ -659,8 +663,8 @@ static void handle_attr_line(struct attr_stack *res,
a = parse_attr_line(line, src, lineno, flags);
if (!a)
return;
- ALLOC_GROW(res->attrs, res->num_matches + 1, res->alloc);
- res->attrs[res->num_matches++] = a;
+ ALLOC_GROW_BY(res->attrs, res->num_matches, 1, res->alloc);
+ res->attrs[res->num_matches - 1] = a;
}
static struct attr_stack *read_attr_from_array(const char **list)
@@ -700,11 +704,12 @@ void git_attr_set_direction(enum git_attr_direction new_direction)
static struct attr_stack *read_attr_from_file(const char *path, unsigned flags)
{
+ struct strbuf buf = STRBUF_INIT;
int fd;
FILE *fp;
struct attr_stack *res;
- char buf[2048];
int lineno = 0;
+ struct stat st;
if (flags & READ_ATTR_NOFOLLOW)
fd = open_nofollow(path, O_RDONLY);
@@ -716,15 +721,26 @@ static struct attr_stack *read_attr_from_file(const char *path, unsigned flags)
return NULL;
}
fp = xfdopen(fd, "r");
+ if (fstat(fd, &st)) {
+ warning_errno(_("cannot fstat gitattributes file '%s'"), path);
+ fclose(fp);
+ return NULL;
+ }
+ if (st.st_size >= ATTR_MAX_FILE_SIZE) {
+ warning(_("ignoring overly large gitattributes file '%s'"), path);
+ fclose(fp);
+ return NULL;
+ }
CALLOC_ARRAY(res, 1);
- while (fgets(buf, sizeof(buf), fp)) {
- char *bufp = buf;
- if (!lineno)
- skip_utf8_bom(&bufp, strlen(bufp));
- handle_attr_line(res, bufp, path, ++lineno, flags);
+ while (strbuf_getline(&buf, fp) != EOF) {
+ if (!lineno && starts_with(buf.buf, utf8_bom))
+ strbuf_remove(&buf, 0, strlen(utf8_bom));
+ handle_attr_line(res, buf.buf, path, ++lineno, flags);
}
+
fclose(fp);
+ strbuf_release(&buf);
return res;
}
@@ -735,6 +751,7 @@ static struct attr_stack *read_attr_from_index(struct index_state *istate,
struct attr_stack *res;
char *buf, *sp;
int lineno = 0;
+ unsigned long size;
if (!istate)
return NULL;
@@ -753,9 +770,13 @@ static struct attr_stack *read_attr_from_index(struct index_state *istate,
if (!path_in_cone_mode_sparse_checkout(path, istate))
return NULL;
- buf = read_blob_data_from_index(istate, path, NULL);
+ buf = read_blob_data_from_index(istate, path, &size);
if (!buf)
return NULL;
+ if (size >= ATTR_MAX_FILE_SIZE) {
+ warning(_("ignoring overly large gitattributes blob '%s'"), path);
+ return NULL;
+ }
CALLOC_ARRAY(res, 1);
for (sp = buf; *sp; ) {
@@ -998,12 +1019,12 @@ static int macroexpand_one(struct all_attrs_item *all_attrs, int nr, int rem);
static int fill_one(struct all_attrs_item *all_attrs,
const struct match_attr *a, int rem)
{
- int i;
+ size_t i;
- for (i = a->num_attr - 1; rem > 0 && i >= 0; i--) {
- const struct git_attr *attr = a->state[i].attr;
+ for (i = a->num_attr; rem > 0 && i > 0; i--) {
+ const struct git_attr *attr = a->state[i - 1].attr;
const char **n = &(all_attrs[attr->attr_nr].value);
- const char *v = a->state[i].setto;
+ const char *v = a->state[i - 1].setto;
if (*n == ATTR__UNKNOWN) {
*n = v;
@@ -1019,11 +1040,11 @@ static int fill(const char *path, int pathlen, int basename_offset,
struct all_attrs_item *all_attrs, int rem)
{
for (; rem > 0 && stack; stack = stack->prev) {
- int i;
+ unsigned i;
const char *base = stack->origin ? stack->origin : "";
- for (i = stack->num_matches - 1; 0 < rem && 0 <= i; i--) {
- const struct match_attr *a = stack->attrs[i];
+ for (i = stack->num_matches; 0 < rem && 0 < i; i--) {
+ const struct match_attr *a = stack->attrs[i - 1];
if (a->is_macro)
continue;
if (path_matches(path, pathlen, basename_offset,
@@ -1054,11 +1075,11 @@ static void determine_macros(struct all_attrs_item *all_attrs,
const struct attr_stack *stack)
{
for (; stack; stack = stack->prev) {
- int i;
- for (i = stack->num_matches - 1; i >= 0; i--) {
- const struct match_attr *ma = stack->attrs[i];
+ unsigned i;
+ for (i = stack->num_matches; i > 0; i--) {
+ const struct match_attr *ma = stack->attrs[i - 1];
if (ma->is_macro) {
- int n = ma->u.attr->attr_nr;
+ unsigned int n = ma->u.attr->attr_nr;
if (!all_attrs[n].macro) {
all_attrs[n].macro = ma;
}
@@ -1110,7 +1131,7 @@ void git_check_attr(struct index_state *istate,
collect_some_attrs(istate, path, check);
for (i = 0; i < check->nr; i++) {
- size_t n = check->items[i].attr->attr_nr;
+ unsigned int n = check->items[i].attr->attr_nr;
const char *value = check->all_attrs[n].value;
if (value == ATTR__UNKNOWN)
value = ATTR__UNSET;
diff --git a/attr.h b/attr.h
index 3fb40cced0..2f22dffadb 100644
--- a/attr.h
+++ b/attr.h
@@ -107,6 +107,18 @@
* - Free the `attr_check` struct by calling `attr_check_free()`.
*/
+/**
+ * The maximum line length for a gitattributes file. If the line exceeds this
+ * length we will ignore it.
+ */
+#define ATTR_MAX_LINE_LENGTH 2048
+
+ /**
+ * The maximum size of the giattributes file. If the file exceeds this size we
+ * will ignore it.
+ */
+#define ATTR_MAX_FILE_SIZE (100 * 1024 * 1024)
+
struct index_state;
/**
diff --git a/builtin.h b/builtin.h
index aa955466b4..46cc789789 100644
--- a/builtin.h
+++ b/builtin.h
@@ -51,10 +51,6 @@
* 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
diff --git a/builtin/am.c b/builtin/am.c
index eee06bbb6c..82a41cbfc4 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -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);
@@ -1649,7 +1652,7 @@ 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))
@@ -2339,6 +2342,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,
diff --git a/builtin/bundle.c b/builtin/bundle.c
index c12c09f854..acceef6200 100644
--- a/builtin/bundle.c
+++ b/builtin/bundle.c
@@ -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) {
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index b3be58b1fb..cc17635e76 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -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);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index a0142c815f..5963e1b74b 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -232,7 +232,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)
@@ -1270,7 +1270,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
diff --git a/builtin/clone.c b/builtin/clone.c
index f518bb2dc1..5453ba5277 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -1271,6 +1271,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));
diff --git a/builtin/log.c b/builtin/log.c
index 89447a5083..057e299c24 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -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);
}
@@ -2105,6 +2110,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) {
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index f702f9d47b..3ce7541783 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -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),
diff --git a/builtin/remote-ext.c b/builtin/remote-ext.c
index fd3538d4f0..ee338bf440 100644
--- a/builtin/remote-ext.c
+++ b/builtin/remote-ext.c
@@ -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;
diff --git a/builtin/remote-fd.c b/builtin/remote-fd.c
index 91dfe07e06..b2a3980b1d 100644
--- a/builtin/remote-fd.c
+++ b/builtin/remote-fd.c
@@ -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,
diff --git a/builtin/rm.c b/builtin/rm.c
index d4989d4d86..4a4aec0d00 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -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);
}
}
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 6743fb27bd..4c173d8b37 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -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;
@@ -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);
}
diff --git a/bundle-uri.c b/bundle-uri.c
index 79a914f961..6462ab6deb 100644
--- a/bundle-uri.c
+++ b/bundle-uri.c
@@ -7,6 +7,7 @@
#include "hashmap.h"
#include "pkt-line.h"
#include "config.h"
+#include "remote.h"
static int compare_bundles(const void *hashmap_cmp_fn_data,
const struct hashmap_entry *he1,
@@ -49,6 +50,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,
@@ -163,7 +165,7 @@ 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;
}
@@ -190,6 +192,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);
@@ -563,6 +577,77 @@ cleanup:
return result;
}
+int fetch_bundle_list(struct repository *r, struct bundle_list *list)
+{
+ int result;
+ struct bundle_list global_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;
+
+ 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.
*/
diff --git a/bundle-uri.h b/bundle-uri.h
index 4dbc269823..d5e89f1671 100644
--- a/bundle-uri.h
+++ b/bundle-uri.h
@@ -4,6 +4,7 @@
#include "hashmap.h"
#include "strbuf.h"
+struct packet_reader;
struct repository;
struct string_list;
@@ -60,6 +61,20 @@ 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;
};
void init_bundle_list(struct bundle_list *list);
@@ -93,6 +108,26 @@ int bundle_uri_parse_config_format(const char *uri,
int fetch_bundle_uri(struct repository *r, const char *uri);
/**
+ * 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 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.
*/
diff --git a/cache-tree.c b/cache-tree.c
index c97111cccf..9af457f47c 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -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.
*/
diff --git a/cache.h b/cache.h
index fcf49706ad..21ed9633b2 100644
--- a/cache.h
+++ b/cache.h
@@ -480,7 +480,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 +565,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);
/*
diff --git a/column.c b/column.c
index 1261e18a72..fbf88639aa 100644
--- a/column.c
+++ b/column.c
@@ -23,7 +23,7 @@ struct column_data {
/* return length of 's' in letters, ANSI escapes stripped */
static int item_length(const char *s)
{
- return utf8_strnwidth(s, -1, 1);
+ return utf8_strnwidth(s, strlen(s), 1);
}
/*
diff --git a/commit.c b/commit.c
index d00780bee5..14538a811a 100644
--- 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));
diff --git a/compat/mingw.c b/compat/mingw.c
index cd8195ab11..e433740381 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2748,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/win32/pthread.h b/compat/win32/pthread.h
index 737983d00b..cc3221cb2c 100644
--- a/compat/win32/pthread.h
+++ b/compat/win32/pthread.h
@@ -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;
diff --git a/config.c b/config.c
index 27f38283ad..00090a32fc 100644
--- 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(
diff --git a/connect.c b/connect.c
index eef752f14b..63e59641c0 100644
--- 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;
@@ -493,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,
diff --git a/csum-file.c b/csum-file.c
index 59ef3398ca..cce13c0f04 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -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;
diff --git a/csum-file.h b/csum-file.h
index 0d29f528fb..793a59da12 100644
--- a/csum-file.h
+++ b/csum-file.h
@@ -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/diff.c b/diff.c
index 9b14543e6e..329eebf16a 100644
--- a/diff.c
+++ b/diff.c
@@ -4213,7 +4213,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 +4230,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 +4250,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 +4268,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 +4277,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 +4306,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);
}
@@ -7034,7 +7030,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/dir.c b/dir.c
index fbdb24fc81..4e99f0c868 100644
--- 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;
diff --git a/entry.c b/entry.c
index 616e4f073c..971ab26871 100644
--- 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 9be4659881..2d4fbb88c8 100644
--- 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);
diff --git a/environment.c b/environment.c
index 18d042b467..1ee3686fd8 100644
--- a/environment.c
+++ b/environment.c
@@ -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;
/*
diff --git a/fsck.c b/fsck.c
index b3da1d68c0..47eaeedd70 100644
--- a/fsck.c
+++ b/fsck.c
@@ -2,6 +2,7 @@
#include "object-store.h"
#include "repository.h"
#include "object.h"
+#include "attr.h"
#include "blob.h"
#include "tree.h"
#include "tree-walk.h"
@@ -614,17 +615,22 @@ static int fsck_tree(const struct object_id *tree_oid,
".gitmodules is a symbolic link");
}
+ if (is_hfs_dotgitattributes(name) || is_ntfs_dotgitattributes(name)) {
+ if (!S_ISLNK(mode))
+ oidset_insert(&options->gitattributes_found,
+ entry_oid);
+ else
+ retval += report(options, tree_oid, OBJ_TREE,
+ FSCK_MSG_GITATTRIBUTES_SYMLINK,
+ ".gitattributes is a symlink");
+ }
+
if (S_ISLNK(mode)) {
if (is_hfs_dotgitignore(name) ||
is_ntfs_dotgitignore(name))
retval += report(options, tree_oid, OBJ_TREE,
FSCK_MSG_GITIGNORE_SYMLINK,
".gitignore is a symlink");
- if (is_hfs_dotgitattributes(name) ||
- is_ntfs_dotgitattributes(name))
- retval += report(options, tree_oid, OBJ_TREE,
- FSCK_MSG_GITATTRIBUTES_SYMLINK,
- ".gitattributes is a symlink");
if (is_hfs_dotmailmap(name) ||
is_ntfs_dotmailmap(name))
retval += report(options, tree_oid, OBJ_TREE,
@@ -1159,38 +1165,70 @@ static int fsck_gitmodules_fn(const char *var, const char *value, void *vdata)
static int fsck_blob(const struct object_id *oid, const char *buf,
unsigned long size, struct fsck_options *options)
{
- struct fsck_gitmodules_data data;
- struct config_options config_opts = { 0 };
-
- if (!oidset_contains(&options->gitmodules_found, oid))
- return 0;
- oidset_insert(&options->gitmodules_done, oid);
+ int ret = 0;
if (object_on_skiplist(options, oid))
return 0;
- if (!buf) {
- /*
- * A missing buffer here is a sign that the caller found the
- * blob too gigantic to load into memory. Let's just consider
- * that an error.
- */
- return report(options, oid, OBJ_BLOB,
- FSCK_MSG_GITMODULES_LARGE,
- ".gitmodules too large to parse");
+ if (oidset_contains(&options->gitmodules_found, oid)) {
+ struct config_options config_opts = { 0 };
+ struct fsck_gitmodules_data data;
+
+ oidset_insert(&options->gitmodules_done, oid);
+
+ if (!buf) {
+ /*
+ * A missing buffer here is a sign that the caller found the
+ * blob too gigantic to load into memory. Let's just consider
+ * that an error.
+ */
+ return report(options, oid, OBJ_BLOB,
+ FSCK_MSG_GITMODULES_LARGE,
+ ".gitmodules too large to parse");
+ }
+
+ data.oid = oid;
+ data.options = options;
+ data.ret = 0;
+ config_opts.error_action = CONFIG_ERROR_SILENT;
+ if (git_config_from_mem(fsck_gitmodules_fn, CONFIG_ORIGIN_BLOB,
+ ".gitmodules", buf, size, &data, &config_opts))
+ data.ret |= report(options, oid, OBJ_BLOB,
+ FSCK_MSG_GITMODULES_PARSE,
+ "could not parse gitmodules blob");
+ ret |= data.ret;
}
- data.oid = oid;
- data.options = options;
- data.ret = 0;
- config_opts.error_action = CONFIG_ERROR_SILENT;
- if (git_config_from_mem(fsck_gitmodules_fn, CONFIG_ORIGIN_BLOB,
- ".gitmodules", buf, size, &data, &config_opts))
- data.ret |= report(options, oid, OBJ_BLOB,
- FSCK_MSG_GITMODULES_PARSE,
- "could not parse gitmodules blob");
-
- return data.ret;
+ if (oidset_contains(&options->gitattributes_found, oid)) {
+ const char *ptr;
+
+ oidset_insert(&options->gitattributes_done, oid);
+
+ if (!buf || size > ATTR_MAX_FILE_SIZE) {
+ /*
+ * A missing buffer here is a sign that the caller found the
+ * blob too gigantic to load into memory. Let's just consider
+ * that an error.
+ */
+ return report(options, oid, OBJ_BLOB,
+ FSCK_MSG_GITATTRIBUTES_LARGE,
+ ".gitattributes too large to parse");
+ }
+
+ for (ptr = buf; *ptr; ) {
+ const char *eol = strchrnul(ptr, '\n');
+ if (eol - ptr >= ATTR_MAX_LINE_LENGTH) {
+ ret |= report(options, oid, OBJ_BLOB,
+ FSCK_MSG_GITATTRIBUTES_LINE_LENGTH,
+ ".gitattributes has too long lines to parse");
+ break;
+ }
+
+ ptr = *eol ? eol + 1 : eol;
+ }
+ }
+
+ return ret;
}
int fsck_object(struct object *obj, void *data, unsigned long size,
@@ -1229,19 +1267,21 @@ int fsck_error_function(struct fsck_options *o,
return 1;
}
-int fsck_finish(struct fsck_options *options)
+static int fsck_blobs(struct oidset *blobs_found, struct oidset *blobs_done,
+ enum fsck_msg_id msg_missing, enum fsck_msg_id msg_type,
+ struct fsck_options *options, const char *blob_type)
{
int ret = 0;
struct oidset_iter iter;
const struct object_id *oid;
- oidset_iter_init(&options->gitmodules_found, &iter);
+ oidset_iter_init(blobs_found, &iter);
while ((oid = oidset_iter_next(&iter))) {
enum object_type type;
unsigned long size;
char *buf;
- if (oidset_contains(&options->gitmodules_done, oid))
+ if (oidset_contains(blobs_done, oid))
continue;
buf = read_object_file(oid, &type, &size);
@@ -1249,25 +1289,36 @@ int fsck_finish(struct fsck_options *options)
if (is_promisor_object(oid))
continue;
ret |= report(options,
- oid, OBJ_BLOB,
- FSCK_MSG_GITMODULES_MISSING,
- "unable to read .gitmodules blob");
+ oid, OBJ_BLOB, msg_missing,
+ "unable to read %s blob", blob_type);
continue;
}
if (type == OBJ_BLOB)
ret |= fsck_blob(oid, buf, size, options);
else
- ret |= report(options,
- oid, type,
- FSCK_MSG_GITMODULES_BLOB,
- "non-blob found at .gitmodules");
+ ret |= report(options, oid, type, msg_type,
+ "non-blob found at %s", blob_type);
free(buf);
}
+ oidset_clear(blobs_found);
+ oidset_clear(blobs_done);
+
+ return ret;
+}
+
+int fsck_finish(struct fsck_options *options)
+{
+ int ret = 0;
+
+ ret |= fsck_blobs(&options->gitmodules_found, &options->gitmodules_done,
+ FSCK_MSG_GITMODULES_MISSING, FSCK_MSG_GITMODULES_BLOB,
+ options, ".gitmodules");
+ ret |= fsck_blobs(&options->gitattributes_found, &options->gitattributes_done,
+ FSCK_MSG_GITATTRIBUTES_MISSING, FSCK_MSG_GITATTRIBUTES_BLOB,
+ options, ".gitattributes");
- oidset_clear(&options->gitmodules_found);
- oidset_clear(&options->gitmodules_done);
return ret;
}
diff --git a/fsck.h b/fsck.h
index 6fbce68ad6..fcecf4101c 100644
--- a/fsck.h
+++ b/fsck.h
@@ -59,6 +59,10 @@ enum fsck_msg_type {
FUNC(GITMODULES_URL, ERROR) \
FUNC(GITMODULES_PATH, ERROR) \
FUNC(GITMODULES_UPDATE, ERROR) \
+ FUNC(GITATTRIBUTES_MISSING, ERROR) \
+ FUNC(GITATTRIBUTES_LARGE, ERROR) \
+ FUNC(GITATTRIBUTES_LINE_LENGTH, ERROR) \
+ FUNC(GITATTRIBUTES_BLOB, ERROR) \
/* warnings */ \
FUNC(EMPTY_NAME, WARN) \
FUNC(FULL_PATHNAME, WARN) \
@@ -133,6 +137,8 @@ struct fsck_options {
struct oidset skiplist;
struct oidset gitmodules_found;
struct oidset gitmodules_done;
+ struct oidset gitattributes_found;
+ struct oidset gitattributes_done;
kh_oid_map_t *object_names;
};
@@ -140,18 +146,24 @@ struct fsck_options {
.skiplist = OIDSET_INIT, \
.gitmodules_found = OIDSET_INIT, \
.gitmodules_done = OIDSET_INIT, \
+ .gitattributes_found = OIDSET_INIT, \
+ .gitattributes_done = OIDSET_INIT, \
.error_func = fsck_error_function \
}
#define FSCK_OPTIONS_STRICT { \
.strict = 1, \
.gitmodules_found = OIDSET_INIT, \
.gitmodules_done = OIDSET_INIT, \
+ .gitattributes_found = OIDSET_INIT, \
+ .gitattributes_done = OIDSET_INIT, \
.error_func = fsck_error_function, \
}
#define FSCK_OPTIONS_MISSING_GITMODULES { \
.strict = 1, \
.gitmodules_found = OIDSET_INIT, \
.gitmodules_done = OIDSET_INIT, \
+ .gitattributes_found = OIDSET_INIT, \
+ .gitattributes_done = OIDSET_INIT, \
.error_func = fsck_error_cb_print_missing_gitmodules, \
}
diff --git a/git-compat-util.h b/git-compat-util.h
index 44abb240ae..6240fc0795 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -1028,6 +1028,14 @@ static inline unsigned long cast_size_t_to_ulong(size_t a)
return (unsigned long)a;
}
+static inline int cast_size_t_to_int(size_t a)
+{
+ if (a > INT_MAX)
+ die("number too large to represent as int on this platform: %"PRIuMAX,
+ (uintmax_t)a);
+ return (int)a;
+}
+
/*
* Limit size of IO chunks, because huge chunks only cause pain. OS X
* 64-bit is buggy, returning EINVAL if len >= INT_MAX; and even in
diff --git a/git-curl-compat.h b/git-curl-compat.h
index 56a83b6bbd..fd96b3cdff 100644
--- a/git-curl-compat.h
+++ b/git-curl-compat.h
@@ -126,4 +126,12 @@
#define GIT_CURL_HAVE_CURLSSLSET_NO_BACKENDS
#endif
+/**
+ * CURLOPT_PROTOCOLS_STR and CURLOPT_REDIR_PROTOCOLS_STR were added in 7.85.0,
+ * released in August 2022.
+ */
+#if LIBCURL_VERSION_NUM >= 0x075500
+#define GIT_CURL_HAVE_CURLOPT_PROTOCOLS_STR 1
+#endif
+
#endif
diff --git a/git.c b/git.c
index 277a8cce84..32a5be68a1 100644
--- 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();
@@ -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 },
@@ -539,7 +518,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 +562,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 +589,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 +706,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 +775,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");
diff --git a/http-push.c b/http-push.c
index 5f4340a36e..7f71316456 100644
--- a/http-push.c
+++ b/http-push.c
@@ -198,13 +198,13 @@ static void curl_setup_http(CURL *curl, const char *url,
const char *custom_req, struct buffer *buffer,
curl_write_callback write_fn)
{
- curl_easy_setopt(curl, CURLOPT_PUT, 1);
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_INFILE, buffer);
curl_easy_setopt(curl, CURLOPT_INFILESIZE, buffer->buf.len);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
- curl_easy_setopt(curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
- curl_easy_setopt(curl, CURLOPT_IOCTLDATA, buffer);
+ curl_easy_setopt(curl, CURLOPT_SEEKFUNCTION, seek_buffer);
+ curl_easy_setopt(curl, CURLOPT_SEEKDATA, buffer);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_fn);
curl_easy_setopt(curl, CURLOPT_NOBODY, 0);
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, custom_req);
diff --git a/http.c b/http.c
index 8a5ba3f477..c4b6ddef28 100644
--- a/http.c
+++ b/http.c
@@ -157,21 +157,19 @@ size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
return size / eltsize;
}
-curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp)
+int seek_buffer(void *clientp, curl_off_t offset, int origin)
{
struct buffer *buffer = clientp;
- switch (cmd) {
- case CURLIOCMD_NOP:
- return CURLIOE_OK;
-
- case CURLIOCMD_RESTARTREAD:
- buffer->posn = 0;
- return CURLIOE_OK;
-
- default:
- return CURLIOE_UNKNOWNCMD;
+ if (origin != SEEK_SET)
+ BUG("seek_buffer only handles SEEK_SET");
+ if (offset < 0 || offset >= buffer->buf.len) {
+ error("curl seek would be outside of buffer");
+ return CURL_SEEKFUNC_FAIL;
}
+
+ buffer->posn = offset;
+ return CURL_SEEKFUNC_OK;
}
size_t fwrite_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
@@ -766,20 +764,37 @@ void setup_curl_trace(CURL *handle)
curl_easy_setopt(handle, CURLOPT_DEBUGDATA, NULL);
}
-static long get_curl_allowed_protocols(int from_user)
+static void proto_list_append(struct strbuf *list, const char *proto)
{
- long allowed_protocols = 0;
+ if (!list)
+ return;
+ if (list->len)
+ strbuf_addch(list, ',');
+ strbuf_addstr(list, proto);
+}
- if (is_transport_allowed("http", from_user))
- allowed_protocols |= CURLPROTO_HTTP;
- if (is_transport_allowed("https", from_user))
- allowed_protocols |= CURLPROTO_HTTPS;
- if (is_transport_allowed("ftp", from_user))
- allowed_protocols |= CURLPROTO_FTP;
- if (is_transport_allowed("ftps", from_user))
- allowed_protocols |= CURLPROTO_FTPS;
+static long get_curl_allowed_protocols(int from_user, struct strbuf *list)
+{
+ long bits = 0;
- return allowed_protocols;
+ if (is_transport_allowed("http", from_user)) {
+ bits |= CURLPROTO_HTTP;
+ proto_list_append(list, "http");
+ }
+ if (is_transport_allowed("https", from_user)) {
+ bits |= CURLPROTO_HTTPS;
+ proto_list_append(list, "https");
+ }
+ if (is_transport_allowed("ftp", from_user)) {
+ bits |= CURLPROTO_FTP;
+ proto_list_append(list, "ftp");
+ }
+ if (is_transport_allowed("ftps", from_user)) {
+ bits |= CURLPROTO_FTPS;
+ proto_list_append(list, "ftps");
+ }
+
+ return bits;
}
#ifdef GIT_CURL_HAVE_CURL_HTTP_VERSION_2
@@ -923,10 +938,26 @@ static CURL *get_curl_handle(void)
curl_easy_setopt(result, CURLOPT_MAXREDIRS, 20);
curl_easy_setopt(result, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
+
+#ifdef GIT_CURL_HAVE_CURLOPT_PROTOCOLS_STR
+ {
+ struct strbuf buf = STRBUF_INIT;
+
+ get_curl_allowed_protocols(0, &buf);
+ curl_easy_setopt(result, CURLOPT_REDIR_PROTOCOLS_STR, buf.buf);
+ strbuf_reset(&buf);
+
+ get_curl_allowed_protocols(-1, &buf);
+ curl_easy_setopt(result, CURLOPT_PROTOCOLS_STR, buf.buf);
+ strbuf_release(&buf);
+ }
+#else
curl_easy_setopt(result, CURLOPT_REDIR_PROTOCOLS,
- get_curl_allowed_protocols(0));
+ get_curl_allowed_protocols(0, NULL));
curl_easy_setopt(result, CURLOPT_PROTOCOLS,
- get_curl_allowed_protocols(-1));
+ get_curl_allowed_protocols(-1, NULL));
+#endif
+
if (getenv("GIT_CURL_VERBOSE"))
http_trace_curl_no_data();
setup_curl_trace(result);
diff --git a/http.h b/http.h
index 3c94c47910..77c042706c 100644
--- a/http.h
+++ b/http.h
@@ -40,7 +40,7 @@ struct buffer {
size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *strbuf);
size_t fwrite_buffer(char *ptr, size_t eltsize, size_t nmemb, void *strbuf);
size_t fwrite_null(char *ptr, size_t eltsize, size_t nmemb, void *strbuf);
-curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp);
+int seek_buffer(void *clientp, curl_off_t offset, int origin);
/* Slot lifecycle functions */
struct active_request_slot *get_active_slot(void);
diff --git a/line-range.c b/line-range.c
index 955a8a9535..47bf0d6f1a 100644
--- a/line-range.c
+++ b/line-range.c
@@ -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(
diff --git a/object-file.c b/object-file.c
index c1b71c2834..ce9efae994 100644
--- a/object-file.c
+++ b/object-file.c
@@ -1211,35 +1211,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 +1237,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 +1422,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 +1445,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 +1455,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 +1499,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 +1581,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 +1612,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 +1671,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 +1692,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,
@@ -2262,6 +2238,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;
@@ -2269,8 +2246,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);
@@ -2785,13 +2764,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;
diff --git a/object-store.h b/object-store.h
index 1be57abaf1..1a713d89d7 100644
--- a/object-store.h
+++ b/object-store.h
@@ -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);
diff --git a/packfile.c b/packfile.c
index c0d7dd93f4..79e21ab18e 100644
--- a/packfile.c
+++ b/packfile.c
@@ -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;
}
}
diff --git a/parse-options.h b/parse-options.h
index b6ef86e0d1..50d852f299 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -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) \
diff --git a/pretty.c b/pretty.c
index 6cb363ae1c..1e1e21878c 100644
--- a/pretty.c
+++ b/pretty.c
@@ -14,6 +14,13 @@
#include "trailer.h"
#include "run-command.h"
+/*
+ * The limit for formatting directives, which enable the caller to append
+ * arbitrarily many bytes to the formatted buffer. This includes padding
+ * and wrapping formatters.
+ */
+#define FORMATTING_LIMIT (16 * 1024)
+
static char *user_format;
static struct cmt_fmt_map {
const char *name;
@@ -994,7 +1001,9 @@ static void strbuf_wrap(struct strbuf *sb, size_t pos,
if (pos)
strbuf_add(&tmp, sb->buf, pos);
strbuf_add_wrapped_text(&tmp, sb->buf + pos,
- (int) indent1, (int) indent2, (int) width);
+ cast_size_t_to_int(indent1),
+ cast_size_t_to_int(indent2),
+ cast_size_t_to_int(width));
strbuf_swap(&tmp, sb);
strbuf_release(&tmp);
}
@@ -1120,9 +1129,18 @@ static size_t parse_padding_placeholder(const char *placeholder,
const char *end = start + strcspn(start, ",)");
char *next;
int width;
- if (!end || end == start)
+ if (!*end || end == start)
return 0;
width = strtol(start, &next, 10);
+
+ /*
+ * We need to limit the amount of padding, or otherwise this
+ * would allow the user to pad the buffer by arbitrarily many
+ * bytes and thus cause resource exhaustion.
+ */
+ if (width < -FORMATTING_LIMIT || width > FORMATTING_LIMIT)
+ return 0;
+
if (next == start || width == 0)
return 0;
if (width < 0) {
@@ -1405,6 +1423,16 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
if (*next != ')')
return 0;
}
+
+ /*
+ * We need to limit the format here as it allows the
+ * user to prepend arbitrarily many bytes to the buffer
+ * when rewrapping.
+ */
+ if (width > FORMATTING_LIMIT ||
+ indent1 > FORMATTING_LIMIT ||
+ indent2 > FORMATTING_LIMIT)
+ return 0;
rewrap_message_tail(sb, c, width, indent1, indent2);
return end - placeholder + 1;
} else
@@ -1670,19 +1698,21 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */
struct format_commit_context *c)
{
struct strbuf local_sb = STRBUF_INIT;
- int total_consumed = 0, len, padding = c->padding;
+ size_t total_consumed = 0;
+ int len, padding = c->padding;
+
if (padding < 0) {
const char *start = strrchr(sb->buf, '\n');
int occupied;
if (!start)
start = sb->buf;
- occupied = utf8_strnwidth(start, -1, 1);
+ occupied = utf8_strnwidth(start, strlen(start), 1);
occupied += c->pretty_ctx->graph_width;
padding = (-padding) - occupied;
}
while (1) {
int modifier = *placeholder == 'C';
- int consumed = format_commit_one(&local_sb, placeholder, c);
+ size_t consumed = format_commit_one(&local_sb, placeholder, c);
total_consumed += consumed;
if (!modifier)
@@ -1694,7 +1724,7 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */
placeholder++;
total_consumed++;
}
- len = utf8_strnwidth(local_sb.buf, -1, 1);
+ len = utf8_strnwidth(local_sb.buf, local_sb.len, 1);
if (c->flush_type == flush_left_and_steal) {
const char *ch = sb->buf + sb->len - 1;
@@ -1709,7 +1739,7 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */
if (*ch != 'm')
break;
p = ch - 1;
- while (ch - p < 10 && *p != '\033')
+ while (p > sb->buf && ch - p < 10 && *p != '\033')
p--;
if (*p != '\033' ||
ch + 1 - p != display_mode_esc_sequence_len(p))
@@ -1748,7 +1778,7 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */
}
strbuf_addbuf(sb, &local_sb);
} else {
- int sb_len = sb->len, offset = 0;
+ size_t sb_len = sb->len, offset = 0;
if (c->flush_type == flush_left)
offset = padding - len;
else if (c->flush_type == flush_both)
@@ -1771,8 +1801,7 @@ static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */
const char *placeholder,
void *context)
{
- int consumed;
- size_t orig_len;
+ size_t consumed, orig_len;
enum {
NO_MAGIC,
ADD_LF_BEFORE_NON_EMPTY,
@@ -1793,9 +1822,21 @@ static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */
default:
break;
}
- if (magic != NO_MAGIC)
+ if (magic != NO_MAGIC) {
placeholder++;
+ switch (placeholder[0]) {
+ case 'w':
+ /*
+ * `%+w()` cannot ever expand to a non-empty string,
+ * and it potentially changes the layout of preceding
+ * contents. We're thus not able to handle the magic in
+ * this combination and refuse the pattern.
+ */
+ return 0;
+ };
+ }
+
orig_len = sb->len;
if (((struct format_commit_context *)context)->flush_type != no_flush)
consumed = format_and_pad_commit(sb, placeholder, context);
diff --git a/read-cache.c b/read-cache.c
index 1ff518b2a7..cf87ad7097 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -1817,6 +1817,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 +1829,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;
}
@@ -2920,9 +2928,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 ? istate->repo : the_repository;
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++;
diff --git a/ref-filter.c b/ref-filter.c
index a24324123e..f8203c6b05 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -1209,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("");
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index c1c71d183e..6f5a0709fb 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -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),
diff --git a/remote-curl.c b/remote-curl.c
index 72dfb8fb86..a76b6405eb 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -717,25 +717,23 @@ static size_t rpc_out(void *ptr, size_t eltsize,
return avail;
}
-static curlioerr rpc_ioctl(CURL *handle, int cmd, void *clientp)
+static int rpc_seek(void *clientp, curl_off_t offset, int origin)
{
struct rpc_state *rpc = clientp;
- switch (cmd) {
- case CURLIOCMD_NOP:
- return CURLIOE_OK;
+ if (origin != SEEK_SET)
+ BUG("rpc_seek only handles SEEK_SET, not %d", origin);
- case CURLIOCMD_RESTARTREAD:
- if (rpc->initial_buffer) {
- rpc->pos = 0;
- return CURLIOE_OK;
+ if (rpc->initial_buffer) {
+ if (offset < 0 || offset > rpc->len) {
+ error("curl seek would be outside of rpc buffer");
+ return CURL_SEEKFUNC_FAIL;
}
- error(_("unable to rewind rpc post data - try increasing http.postBuffer"));
- return CURLIOE_FAILRESTART;
-
- default:
- return CURLIOE_UNKNOWNCMD;
+ rpc->pos = offset;
+ return CURL_SEEKFUNC_OK;
}
+ error(_("unable to rewind rpc post data - try increasing http.postBuffer"));
+ return CURL_SEEKFUNC_FAIL;
}
struct check_pktline_state {
@@ -959,8 +957,8 @@ retry:
rpc->initial_buffer = 1;
curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, rpc_out);
curl_easy_setopt(slot->curl, CURLOPT_INFILE, rpc);
- curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, rpc_ioctl);
- curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, rpc);
+ curl_easy_setopt(slot->curl, CURLOPT_SEEKFUNCTION, rpc_seek);
+ curl_easy_setopt(slot->curl, CURLOPT_SEEKDATA, rpc);
if (options.verbosity > 1) {
fprintf(stderr, "POST %s (chunked)\n", rpc->service_name);
fflush(stderr);
diff --git a/remote.h b/remote.h
index 1c4621b414..1ebbe42792 100644
--- 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);
/*
diff --git a/repo-settings.c b/repo-settings.c
index 3021921c53..3dbd3f0e2e 100644
--- a/repo-settings.c
+++ b/repo-settings.c
@@ -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
diff --git a/repository.h b/repository.h
index 6c461c5b9d..e8c67ffe16 100644
--- a/repository.h
+++ b/repository.h
@@ -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;
diff --git a/run-command.c b/run-command.c
index 756f1839aa..50cc011654 100644
--- a/run-command.c
+++ b/run-command.c
@@ -1853,7 +1853,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;
diff --git a/scalar.c b/scalar.c
index 6c52243cdf..b49bb8c24e 100644
--- 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 },
diff --git a/serve.c b/serve.c
index 733347f602..cbf4a143cf 100644
--- 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)
diff --git a/strbuf.c b/strbuf.c
index 0890b1405c..c383f41a3c 100644
--- 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);
+}
diff --git a/strbuf.h b/strbuf.h
index 76965a17d4..f6dbb9681e 100644
--- 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,
diff --git a/streaming.c b/streaming.c
index 7b2f8b2b93..27841dc1d9 100644
--- a/streaming.c
+++ b/streaming.c
@@ -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);
}
/*****************************************************************************
diff --git a/submodule.c b/submodule.c
index fae24ef34a..3a0dfc417c 100644
--- a/submodule.c
+++ b/submodule.c
@@ -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)
diff --git a/submodule.h b/submodule.h
index b52a4ff1e7..c55a25ca37 100644
--- a/submodule.h
+++ b/submodule.h
@@ -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
diff --git a/t/helper/test-bundle-uri.c b/t/helper/test-bundle-uri.c
index 25afd39342..b18e760310 100644
--- a/t/helper/test-bundle-uri.c
+++ b/t/helper/test-bundle-uri.c
@@ -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:
diff --git a/t/lib-bundle-uri-protocol.sh b/t/lib-bundle-uri-protocol.sh
new file mode 100644
index 0000000000..a4a1af8d02
--- /dev/null
+++ b/t/lib-bundle-uri-protocol.sh
@@ -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
+'
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 502b4bcf9e..8ea31d187a 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -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
'
diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
index f7ee2f2ff0..d0284fe2d7 100755
--- a/t/t0003-attributes.sh
+++ b/t/t0003-attributes.sh
@@ -376,4 +376,63 @@ test_expect_success SYMLINKS 'symlinks not respected in-tree' '
test_i18ngrep "unable to access.*gitattributes" err
'
+test_expect_success 'large attributes line ignored in tree' '
+ test_when_finished "rm .gitattributes" &&
+ printf "path %02043d" 1 >.gitattributes &&
+ git check-attr --all path >actual 2>err &&
+ echo "warning: ignoring overly long attributes line 1" >expect &&
+ test_cmp expect err &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'large attributes line ignores trailing content in tree' '
+ test_when_finished "rm .gitattributes" &&
+ # older versions of Git broke lines at 2048 bytes; the 2045 bytes
+ # of 0-padding here is accounting for the three bytes of "a 1", which
+ # would knock "trailing" to the "next" line, where it would be
+ # erroneously parsed.
+ printf "a %02045dtrailing attribute\n" 1 >.gitattributes &&
+ git check-attr --all trailing >actual 2>err &&
+ echo "warning: ignoring overly long attributes line 1" >expect &&
+ test_cmp expect err &&
+ test_must_be_empty actual
+'
+
+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 &&
+ git check-attr --all path >/dev/null 2>err &&
+ echo "warning: ignoring overly large gitattributes file ${SQ}.gitattributes${SQ}" >expect &&
+ test_cmp expect err
+'
+
+test_expect_success 'large attributes line ignored in index' '
+ test_when_finished "git update-index --remove .gitattributes" &&
+ blob=$(printf "path %02043d" 1 | git hash-object -w --stdin) &&
+ git update-index --add --cacheinfo 100644,$blob,.gitattributes &&
+ git check-attr --cached --all path >actual 2>err &&
+ echo "warning: ignoring overly long attributes line 1" >expect &&
+ test_cmp expect err &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'large attributes line ignores trailing content in index' '
+ test_when_finished "git update-index --remove .gitattributes" &&
+ blob=$(printf "a %02045dtrailing attribute\n" 1 | git hash-object -w --stdin) &&
+ git update-index --add --cacheinfo 100644,$blob,.gitattributes &&
+ git check-attr --cached --all trailing >actual 2>err &&
+ echo "warning: ignoring overly long attributes line 1" >expect &&
+ test_cmp expect err &&
+ test_must_be_empty actual
+'
+
+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) &&
+ 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 &&
+ test_cmp expect err
+'
+
test_done
diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh
index 516a6112fd..3fb1b0c162 100755
--- a/t/t1001-read-tree-m-2way.sh
+++ b/t/t1001-read-tree-m-2way.sh
@@ -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
'
diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh
index b563d6c263..627267be15 100755
--- a/t/t1091-sparse-checkout-builtin.sh
+++ b/t/t1091-sparse-checkout-builtin.sh
@@ -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
diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh
index 93a2f91f8a..58d6da7feb 100755
--- a/t/t1301-shared-repo.sh
+++ b/t/t1301-shared-repo.sh
@@ -8,6 +8,7 @@ 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-lib.sh
# Remove a default ACL from the test dir if possible.
@@ -25,6 +26,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 +44,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 +131,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 +140,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 +174,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 +194,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 +205,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 &&
diff --git a/t/t1302-repo-version.sh b/t/t1302-repo-version.sh
index 0acabb6d11..7cf80bf66a 100755
--- a/t/t1302-repo-version.sh
+++ b/t/t1302-repo-version.sh
@@ -27,7 +27,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 &&
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index ace4556788..de0f6d5e7f 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -999,4 +999,28 @@ test_expect_success 'fsck error and recovery on invalid object type' '
)
'
+test_expect_success 'fsck error on gitattributes with excessive line lengths' '
+ blob=$(printf "pattern %02048d" 1 | git hash-object -w --stdin) &&
+ test_when_finished "remove_object $blob" &&
+ tree=$(printf "100644 blob %s\t%s\n" $blob .gitattributes | git mktree) &&
+ test_when_finished "remove_object $tree" &&
+ cat >expected <<-EOF &&
+ error in blob $blob: gitattributesLineLength: .gitattributes has too long lines to parse
+ EOF
+ test_must_fail git fsck --no-dangling >actual 2>&1 &&
+ test_cmp expected actual
+'
+
+test_expect_success 'fsck error on gitattributes with excessive size' '
+ blob=$(test-tool genzeros $((100 * 1024 * 1024 + 1)) | git hash-object -w --stdin) &&
+ test_when_finished "remove_object $blob" &&
+ tree=$(printf "100644 blob %s\t%s\n" $blob .gitattributes | git mktree) &&
+ test_when_finished "remove_object $tree" &&
+ cat >expected <<-EOF &&
+ error in blob $blob: gitattributesLarge: .gitattributes too large to parse
+ EOF
+ test_must_fail git fsck --no-dangling >actual 2>&1 &&
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 81de584ea2..37ee5091b5 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -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 &&
diff --git a/t/t1509-root-work-tree.sh b/t/t1509-root-work-tree.sh
index 553a3f601b..c799f5b6ac 100755
--- a/t/t1509-root-work-tree.sh
+++ b/t/t1509-root-work-tree.sh
@@ -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
diff --git a/t/t1600-index.sh b/t/t1600-index.sh
index 010989f90e..0ebbae1305 100755
--- a/t/t1600-index.sh
+++ b/t/t1600-index.sh
@@ -65,6 +65,36 @@ 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 &&
+ >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 &&
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index de1da4673d..012f155e10 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -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
diff --git a/t/t4044-diff-index-unique-abbrev.sh b/t/t4044-diff-index-unique-abbrev.sh
index 29e49d2290..9f6043daba 100755
--- a/t/t4044-diff-index-unique-abbrev.sh
+++ b/t/t4044-diff-index-unique-abbrev.sh
@@ -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
diff --git a/t/t4045-diff-relative.sh b/t/t4045-diff-relative.sh
index 198dfc9190..9b46c4c1be 100755
--- a/t/t4045-diff-relative.sh
+++ b/t/t4045-diff-relative.sh
@@ -164,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 &&
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index cdad4b6880..78cf1c880e 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -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 &&
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
index cd1cab3e54..fa7f987284 100755
--- a/t/t4203-mailmap.sh
+++ b/t/t4203-mailmap.sh
@@ -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
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
index 0404491d6e..3e7ad9d5de 100755
--- a/t/t4205-log-pretty-formats.sh
+++ b/t/t4205-log-pretty-formats.sh
@@ -1018,4 +1018,80 @@ test_expect_success '%(describe:abbrev=...) vs git describe --abbrev=...' '
test_cmp expect actual
'
+test_expect_success 'log --pretty with space stealing' '
+ printf mm0 >expect &&
+ git log -1 --pretty="format:mm%>>|(1)%x30" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --pretty with invalid padding format' '
+ printf "%s%%<(20" "$(git rev-parse HEAD)" >expect &&
+ git log -1 --pretty="format:%H%<(20" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --pretty with magical wrapping directives' '
+ commit_id=$(git commit-tree HEAD^{tree} -m "describe me") &&
+ git tag describe-me $commit_id &&
+ printf "\n(tag:\ndescribe-me)%%+w(2)" >expect &&
+ git log -1 --pretty="format:%w(1)%+d%+w(2)" $commit_id >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success SIZE_T_IS_64BIT 'log --pretty with overflowing wrapping directive' '
+ printf "%%w(2147483649,1,1)0" >expect &&
+ git log -1 --pretty="format:%w(2147483649,1,1)%x30" >actual &&
+ test_cmp expect actual &&
+ printf "%%w(1,2147483649,1)0" >expect &&
+ git log -1 --pretty="format:%w(1,2147483649,1)%x30" >actual &&
+ test_cmp expect actual &&
+ printf "%%w(1,1,2147483649)0" >expect &&
+ git log -1 --pretty="format:%w(1,1,2147483649)%x30" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success SIZE_T_IS_64BIT 'log --pretty with overflowing padding directive' '
+ printf "%%<(2147483649)0" >expect &&
+ git log -1 --pretty="format:%<(2147483649)%x30" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --pretty with padding and preceding control chars' '
+ printf "\20\20 0" >expect &&
+ git log -1 --pretty="format:%x10%x10%>|(4)%x30" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --pretty truncation with control chars' '
+ test_commit "$(printf "\20\20\20\20xxxx")" file contents commit-with-control-chars &&
+ printf "\20\20\20\20x.." >expect &&
+ git log -1 --pretty="format:%<(3,trunc)%s" commit-with-control-chars >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success EXPENSIVE,SIZE_T_IS_64BIT 'log --pretty with huge commit message' '
+ # We only assert that this command does not crash. This needs to be
+ # executed with the address sanitizer to demonstrate failure.
+ git log -1 --pretty="format:%>(2147483646)%x41%41%>(2147483646)%x41" >/dev/null
+'
+
+test_expect_success EXPENSIVE,SIZE_T_IS_64BIT 'set up huge commit' '
+ test-tool genzeros 2147483649 | tr "\000" "1" >expect &&
+ huge_commit=$(git commit-tree -F expect HEAD^{tree})
+'
+
+test_expect_success EXPENSIVE,SIZE_T_IS_64BIT 'log --pretty with huge commit message' '
+ git log -1 --format="%B%<(1)%x30" $huge_commit >actual &&
+ echo 0 >>expect &&
+ test_cmp expect actual
+'
+
+test_expect_success EXPENSIVE,SIZE_T_IS_64BIT 'log --pretty with huge commit message does not cause allocation failure' '
+ test_must_fail git log -1 --format="%<(1)%B" $huge_commit 2>error &&
+ cat >expect <<-EOF &&
+ fatal: number too large to represent as int on this platform: 2147483649
+ EOF
+ test_cmp expect error
+'
+
test_done
diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh
index ac9e4d0928..c6540e822f 100755
--- a/t/t4211-line-log.sh
+++ b/t/t4211-line-log.sh
@@ -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
diff --git a/t/t4301-merge-tree-write-tree.sh b/t/t4301-merge-tree-write-tree.sh
index a8983a0edc..250f721795 100755
--- a/t/t4301-merge-tree-write-tree.sh
+++ b/t/t4301-merge-tree-write-tree.sh
@@ -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
diff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh
index cc4cfaa9d3..ceaa6700a2 100755
--- a/t/t5313-pack-bounds-checks.sh
+++ b/t/t5313-pack-bounds-checks.sh
@@ -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 &&
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index b2524a24c2..1928ea1dd7 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -772,6 +772,65 @@ 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
+'
+
# 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.
diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh
index 037941b95d..f519d2a87a 100755
--- a/t/t5616-partial-clone.sh
+++ b/t/t5616-partial-clone.sh
@@ -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
diff --git a/t/t5701-git-serve.sh b/t/t5701-git-serve.sh
index 1896f671cb..f21e5e9d33 100755
--- a/t/t5701-git-serve.sh
+++ b/t/t5701-git-serve.sh
@@ -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
diff --git a/t/t5730-protocol-v2-bundle-uri-file.sh b/t/t5730-protocol-v2-bundle-uri-file.sh
new file mode 100755
index 0000000000..37bdb725bc
--- /dev/null
+++ b/t/t5730-protocol-v2-bundle-uri-file.sh
@@ -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
index 0000000000..8add1b37ab
--- /dev/null
+++ b/t/t5731-protocol-v2-bundle-uri-git.sh
@@ -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
index 0000000000..129daa0226
--- /dev/null
+++ b/t/t5732-protocol-v2-bundle-uri-http.sh
@@ -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
diff --git a/t/t5750-bundle-uri-parse.sh b/t/t5750-bundle-uri-parse.sh
index c2fe3f9c5a..7b4f930e53 100755
--- a/t/t5750-bundle-uri-parse.sh
+++ b/t/t5750-bundle-uri-parse.sh
@@ -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
diff --git a/t/t6020-bundle-misc.sh b/t/t6020-bundle-misc.sh
index 833205125a..3a1cf30b1d 100755
--- a/t/t6020-bundle-misc.sh
+++ b/t/t6020-bundle-misc.sh
@@ -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=
diff --git a/t/t6421-merge-partial-clone.sh b/t/t6421-merge-partial-clone.sh
index 5413e5dd9d..711b709e75 100755
--- a/t/t6421-merge-partial-clone.sh
+++ b/t/t6421-merge-partial-clone.sh
@@ -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}
diff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh
index 2859695c6d..f778321857 100755
--- a/t/t7412-submodule-absorbgitdirs.sh
+++ b/t/t7412-submodule-absorbgitdirs.sh
@@ -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
diff --git a/t/t7527-builtin-fsmonitor.sh b/t/t7527-builtin-fsmonitor.sh
index 4abc74db2b..c2582d44a6 100755
--- a/t/t7527-builtin-fsmonitor.sh
+++ b/t/t7527-builtin-fsmonitor.sh
@@ -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,15 +886,25 @@ 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.
@@ -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.
diff --git a/t/t9119-git-svn-info.sh b/t/t9119-git-svn-info.sh
index 8201c3e808..088d1c57a8 100755
--- a/t/t9119-git-svn-info.sh
+++ b/t/t9119-git-svn-info.sh
@@ -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 &&
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 796093a7b3..1b8d4344a5 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -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.
#
@@ -1875,3 +1816,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"
+}
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 6db377f68b..4fab1c1984 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -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
diff --git a/transport-helper.c b/transport-helper.c
index e95267a4ab..3ea7c2bb5a 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -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,
diff --git a/transport-internal.h b/transport-internal.h
index c4ca0b733a..90ea749e5c 100644
--- a/transport-internal.h
+++ b/transport-internal.h
@@ -27,6 +27,13 @@ struct transport_vtable {
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.
*
diff --git a/transport.c b/transport.c
index e7b97194c1..77a61a9d7b 100644
--- a/transport.c
+++ b/transport.c
@@ -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;
}
diff --git a/transport.h b/transport.h
index b5bf7b3e70..85150f504f 100644
--- a/transport.h
+++ b/transport.h
@@ -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.
*
diff --git a/unpack-trees.c b/unpack-trees.c
index d11bef96bf..ea09023b01 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -70,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
@@ -82,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 {
@@ -235,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
@@ -262,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);
@@ -289,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);
@@ -311,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;
}
@@ -414,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;
@@ -444,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);
}
}
@@ -2958,8 +2962,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
@@ -3020,7 +3024,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);
}
diff --git a/unpack-trees.h b/unpack-trees.h
index 6ab0d74c84..3a7b3e5f00 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -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;
diff --git a/urlmatch.c b/urlmatch.c
index b615adc923..620a648efc 100644
--- a/urlmatch.c
+++ b/urlmatch.c
@@ -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 {
diff --git a/utf8.c b/utf8.c
index de4ce5c0e6..6a0dd25b0f 100644
--- a/utf8.c
+++ b/utf8.c
@@ -206,26 +206,34 @@ int utf8_width(const char **start, size_t *remainder_p)
* string, assuming that the string is utf8. Returns strlen() instead
* if the string does not look like a valid utf8 string.
*/
-int utf8_strnwidth(const char *string, int len, int skip_ansi)
+int utf8_strnwidth(const char *string, size_t len, int skip_ansi)
{
- int width = 0;
const char *orig = string;
+ size_t width = 0;
- if (len == -1)
- len = strlen(string);
while (string && string < orig + len) {
- int skip;
+ int glyph_width;
+ size_t skip;
+
while (skip_ansi &&
(skip = display_mode_esc_sequence_len(string)) != 0)
string += skip;
- width += utf8_width(&string, NULL);
+
+ glyph_width = utf8_width(&string, NULL);
+ if (glyph_width > 0)
+ width += glyph_width;
}
- return string ? width : len;
+
+ /*
+ * TODO: fix the interface of this function and `utf8_strwidth()` to
+ * return `size_t` instead of `int`.
+ */
+ return cast_size_t_to_int(string ? width : len);
}
int utf8_strwidth(const char *string)
{
- return utf8_strnwidth(string, -1, 0);
+ return utf8_strnwidth(string, strlen(string), 0);
}
int is_utf8(const char *text)
@@ -357,51 +365,52 @@ void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,
void strbuf_utf8_replace(struct strbuf *sb_src, int pos, int width,
const char *subst)
{
- struct strbuf sb_dst = STRBUF_INIT;
- char *src = sb_src->buf;
- char *end = src + sb_src->len;
- char *dst;
- int w = 0, subst_len = 0;
+ const char *src = sb_src->buf, *end = sb_src->buf + sb_src->len;
+ struct strbuf dst;
+ int w = 0;
- if (subst)
- subst_len = strlen(subst);
- strbuf_grow(&sb_dst, sb_src->len + subst_len);
- dst = sb_dst.buf;
+ strbuf_init(&dst, sb_src->len);
while (src < end) {
- char *old;
+ const char *old;
+ int glyph_width;
size_t n;
while ((n = display_mode_esc_sequence_len(src))) {
- memcpy(dst, src, n);
+ strbuf_add(&dst, src, n);
src += n;
- dst += n;
}
if (src >= end)
break;
old = src;
- n = utf8_width((const char**)&src, NULL);
- if (!src) /* broken utf-8, do nothing */
+ glyph_width = utf8_width((const char**)&src, NULL);
+ if (!src) /* broken utf-8, do nothing */
goto out;
- if (n && w >= pos && w < pos + width) {
+
+ /*
+ * In case we see a control character we copy it into the
+ * buffer, but don't add it to the width.
+ */
+ if (glyph_width < 0)
+ glyph_width = 0;
+
+ if (glyph_width && w >= pos && w < pos + width) {
if (subst) {
- memcpy(dst, subst, subst_len);
- dst += subst_len;
+ strbuf_addstr(&dst, subst);
subst = NULL;
}
- w += n;
- continue;
+ } else {
+ strbuf_add(&dst, old, src - old);
}
- memcpy(dst, old, src - old);
- dst += src - old;
- w += n;
+
+ w += glyph_width;
}
- strbuf_setlen(&sb_dst, dst - sb_dst.buf);
- strbuf_swap(sb_src, &sb_dst);
+
+ strbuf_swap(sb_src, &dst);
out:
- strbuf_release(&sb_dst);
+ strbuf_release(&dst);
}
/*
@@ -796,7 +805,7 @@ int skip_utf8_bom(char **text, size_t len)
void strbuf_utf8_align(struct strbuf *buf, align_type position, unsigned int width,
const char *s)
{
- int slen = strlen(s);
+ size_t slen = strlen(s);
int display_len = utf8_strnwidth(s, slen, 0);
int utf8_compensation = slen - display_len;
diff --git a/utf8.h b/utf8.h
index 9a16c8679d..b68efef6f4 100644
--- a/utf8.h
+++ b/utf8.h
@@ -7,7 +7,7 @@ typedef unsigned int ucs_char_t; /* assuming 32bit int */
size_t display_mode_esc_sequence_len(const char *s);
int utf8_width(const char **start, size_t *remainder_p);
-int utf8_strnwidth(const char *string, int len, int skip_ansi);
+int utf8_strnwidth(const char *string, size_t len, int skip_ansi);
int utf8_strwidth(const char *string);
int is_utf8(const char *text);
int is_encoding_utf8(const char *name);
diff --git a/ws.c b/ws.c
index 46a77bcad6..903bfcd53e 100644
--- 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;
}