Merge branch 'jc/genzeros-avoid-raw-write'
A test helper had a single write(2) of 256kB, which was too big for
some platforms (e.g. NonStop), which has been corrected by using
xwrite() wrapper appropriately.
* jc/genzeros-avoid-raw-write:
test-genzeros: avoid raw write(2)
diff --git a/.gitattributes b/.gitattributes
index b0044cf..158c3d4 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,17 +1,17 @@
* whitespace=!indent,trail,space
*.[ch] whitespace=indent,trail,space diff=cpp
-*.sh whitespace=indent,trail,space eol=lf
-*.perl eol=lf diff=perl
-*.pl eof=lf diff=perl
-*.pm eol=lf diff=perl
-*.py eol=lf diff=python
-*.bat eol=crlf
+*.sh whitespace=indent,trail,space text eol=lf
+*.perl text eol=lf diff=perl
+*.pl text eof=lf diff=perl
+*.pm text eol=lf diff=perl
+*.py text eol=lf diff=python
+*.bat text eol=crlf
CODE_OF_CONDUCT.md -whitespace
-/Documentation/**/*.txt eol=lf
-/command-list.txt eol=lf
-/GIT-VERSION-GEN eol=lf
-/mergetools/* eol=lf
-/t/oid-info/* eol=lf
+/Documentation/**/*.txt text eol=lf
+/command-list.txt text eol=lf
+/GIT-VERSION-GEN text eol=lf
+/mergetools/* text eol=lf
+/t/oid-info/* text eol=lf
/Documentation/git-merge.txt conflict-marker-size=32
/Documentation/gitk.txt conflict-marker-size=32
/Documentation/user-manual.txt conflict-marker-size=32
diff --git a/.github/workflows/check-whitespace.yml b/.github/workflows/check-whitespace.yml
index ad3466a..a58e2dc 100644
--- a/.github/workflows/check-whitespace.yml
+++ b/.github/workflows/check-whitespace.yml
@@ -9,42 +9,83 @@
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 f7ea0f0..6c38496 100644
--- a/.github/workflows/l10n.yml
+++ b/.github/workflows/l10n.yml
@@ -2,6 +2,12 @@
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 e67847a..30492ea 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -11,6 +11,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
- 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 @@
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 @@
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 @@
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 @@
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 @@
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/.gitignore b/.gitignore
index 0832f1d..e875c59 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,14 +14,12 @@
/bin-wrappers/
/git
/git-add
-/git-add--interactive
/git-am
/git-annotate
/git-apply
/git-archimport
/git-archive
/git-bisect
-/git-bisect--helper
/git-blame
/git-branch
/git-bugreport
@@ -60,7 +58,6 @@
/git-difftool
/git-difftool--helper
/git-describe
-/git-env--helper
/git-fast-export
/git-fast-import
/git-fetch
diff --git a/Documentation/RelNotes/2.30.7.txt b/Documentation/RelNotes/2.30.7.txt
new file mode 100644
index 0000000..285beed
--- /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.30.8.txt b/Documentation/RelNotes/2.30.8.txt
new file mode 100644
index 0000000..5ed3efb
--- /dev/null
+++ b/Documentation/RelNotes/2.30.8.txt
@@ -0,0 +1,51 @@
+Git v2.30.8 Release Notes
+=========================
+
+This release addresses the security issues CVE-2023-22490 and
+CVE-2023-23946.
+
+
+Fixes since v2.30.7
+-------------------
+
+ * CVE-2023-22490:
+
+ Using a specially-crafted repository, Git can be tricked into using
+ its local clone optimization even when using a non-local transport.
+ Though Git will abort local clones whose source $GIT_DIR/objects
+ directory contains symbolic links (c.f., CVE-2022-39253), the objects
+ directory itself may still be a symbolic link.
+
+ These two may be combined to include arbitrary files based on known
+ paths on the victim's filesystem within the malicious repository's
+ working copy, allowing for data exfiltration in a similar manner as
+ CVE-2022-39253.
+
+ * CVE-2023-23946:
+
+ By feeding a crafted input to "git apply", a path outside the
+ working tree can be overwritten as the user who is running "git
+ apply".
+
+ * A mismatched type in `attr.c::read_attr_from_index()` which could
+ cause Git to errantly reject attributes on Windows and 32-bit Linux
+ has been corrected.
+
+Credit for finding CVE-2023-22490 goes to yvvdwf, and the fix was
+developed by Taylor Blau, with additional help from others on the
+Git security mailing list.
+
+Credit for finding CVE-2023-23946 goes to Joern Schneeweisz, and the
+fix was developed by Patrick Steinhardt.
+
+
+Johannes Schindelin (1):
+ attr: adjust a mismatched data type
+
+Patrick Steinhardt (1):
+ apply: fix writing behind newly created symbolic links
+
+Taylor Blau (3):
+ t5619: demonstrate clone_local() with ambiguous transport
+ clone: delay picking a transport until after get_repo_path()
+ dir-iterator: prevent top-level symlinks without FOLLOW_SYMLINKS
diff --git a/Documentation/RelNotes/2.31.6.txt b/Documentation/RelNotes/2.31.6.txt
new file mode 100644
index 0000000..425a518
--- /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.31.7.txt b/Documentation/RelNotes/2.31.7.txt
new file mode 100644
index 0000000..dd44d5b
--- /dev/null
+++ b/Documentation/RelNotes/2.31.7.txt
@@ -0,0 +1,6 @@
+Git v2.31.7 Release Notes
+=========================
+
+This release merges up the fixes that appear in v2.30.8 to
+address the security issues CVE-2023-22490 and CVE-2023-23946;
+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 0000000..a8cad1a
--- /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.32.6.txt b/Documentation/RelNotes/2.32.6.txt
new file mode 100644
index 0000000..fd65961
--- /dev/null
+++ b/Documentation/RelNotes/2.32.6.txt
@@ -0,0 +1,6 @@
+Git v2.32.6 Release Notes
+=========================
+
+This release merges up the fixes that appear in v2.30.8 and v2.31.7
+to address the security issues CVE-2023-22490 and CVE-2023-23946;
+see the release notes for these versions for details.
diff --git a/Documentation/RelNotes/2.33.6.txt b/Documentation/RelNotes/2.33.6.txt
new file mode 100644
index 0000000..b63e4e6
--- /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.33.7.txt b/Documentation/RelNotes/2.33.7.txt
new file mode 100644
index 0000000..078a837
--- /dev/null
+++ b/Documentation/RelNotes/2.33.7.txt
@@ -0,0 +1,7 @@
+Git v2.33.7 Release Notes
+=========================
+
+This release merges up the fixes that appear in v2.30.8, v2.31.7
+and v2.32.6 to address the security issues CVE-2023-22490 and
+CVE-2023-23946; see the release notes for these versions for
+details.
diff --git a/Documentation/RelNotes/2.34.6.txt b/Documentation/RelNotes/2.34.6.txt
new file mode 100644
index 0000000..b32080d
--- /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.34.7.txt b/Documentation/RelNotes/2.34.7.txt
new file mode 100644
index 0000000..88898ad
--- /dev/null
+++ b/Documentation/RelNotes/2.34.7.txt
@@ -0,0 +1,7 @@
+Git v2.34.7 Release Notes
+=========================
+
+This release merges up the fixes that appear in v2.30.8, v2.31.7,
+v2.32.6 and v2.33.7 to address the security issues CVE-2023-22490
+and CVE-2023-23946; see the release notes for these versions
+for details.
diff --git a/Documentation/RelNotes/2.35.6.txt b/Documentation/RelNotes/2.35.6.txt
new file mode 100644
index 0000000..e7ca57b
--- /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.35.7.txt b/Documentation/RelNotes/2.35.7.txt
new file mode 100644
index 0000000..42baabf
--- /dev/null
+++ b/Documentation/RelNotes/2.35.7.txt
@@ -0,0 +1,7 @@
+Git v2.35.7 Release Notes
+=========================
+
+This release merges up the fixes that appear in v2.30.8, v2.31.7,
+v2.32.6, v2.33.7 and v2.34.7 to address the security issues
+CVE-2023-22490 and CVE-2023-23946; see the release notes for
+these versions for details.
diff --git a/Documentation/RelNotes/2.36.4.txt b/Documentation/RelNotes/2.36.4.txt
new file mode 100644
index 0000000..58fb93a
--- /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.36.5.txt b/Documentation/RelNotes/2.36.5.txt
new file mode 100644
index 0000000..8a098c7
--- /dev/null
+++ b/Documentation/RelNotes/2.36.5.txt
@@ -0,0 +1,7 @@
+Git v2.36.5 Release Notes
+=========================
+
+This release merges up the fixes that appear in v2.30.8, v2.31.7,
+v2.32.6, v2.33.7, v2.34.7 and v2.35.7 to address the security
+issues CVE-2023-22490 and CVE-2023-23946; see the release notes
+for these versions for details.
diff --git a/Documentation/RelNotes/2.37.5.txt b/Documentation/RelNotes/2.37.5.txt
new file mode 100644
index 0000000..faa1447
--- /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.37.6.txt b/Documentation/RelNotes/2.37.6.txt
new file mode 100644
index 0000000..51dc149
--- /dev/null
+++ b/Documentation/RelNotes/2.37.6.txt
@@ -0,0 +1,7 @@
+Git v2.37.6 Release Notes
+=========================
+
+This release merges up the fixes that appear in v2.30.8, v2.31.7,
+v2.32.6, v2.33.7, v2.34.7, v2.35.7 and v2.36.5 to address the
+security issues CVE-2023-22490 and CVE-2023-23946; see the release
+notes for these versions for details.
diff --git a/Documentation/RelNotes/2.38.3.txt b/Documentation/RelNotes/2.38.3.txt
new file mode 100644
index 0000000..4a46bb4
--- /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.38.4.txt b/Documentation/RelNotes/2.38.4.txt
new file mode 100644
index 0000000..fdfde22
--- /dev/null
+++ b/Documentation/RelNotes/2.38.4.txt
@@ -0,0 +1,7 @@
+Git v2.38.4 Release Notes
+=========================
+
+This release merges up the fixes that appear in v2.30.8, v2.31.7,
+v2.32.6, v2.33.7, v2.34.7, v2.35.7, v2.36.5 and v2.37.6 to
+address the security issues CVE-2023-22490 and CVE-2023-23946;
+see the release notes for these versions for details.
diff --git a/Documentation/RelNotes/2.39.1.txt b/Documentation/RelNotes/2.39.1.txt
new file mode 100644
index 0000000..60c86f4
--- /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.39.2.txt b/Documentation/RelNotes/2.39.2.txt
new file mode 100644
index 0000000..ebb9900
--- /dev/null
+++ b/Documentation/RelNotes/2.39.2.txt
@@ -0,0 +1,7 @@
+Git v2.39.2 Release Notes
+=========================
+
+This release merges up the fixes that appear in v2.30.8, v2.31.7,
+v2.32.6, v2.33.7, v2.34.7, v2.35.7, v2.36.5, v2.37.6 and v2.38.4
+to address the security issues CVE-2023-22490 and CVE-2023-23946;
+see the release notes for these versions for details.
diff --git a/Documentation/RelNotes/2.39.3.txt b/Documentation/RelNotes/2.39.3.txt
new file mode 100644
index 0000000..dddff53
--- /dev/null
+++ b/Documentation/RelNotes/2.39.3.txt
@@ -0,0 +1,58 @@
+Git v2.39.3 Release Notes
+=========================
+
+This release is primarily to merge fixes accumulated on the 'master'
+front to prepare for 2.40 release that are still relevant to 2.39.x
+maintenance track.
+
+Fixes since v2.39.2
+-------------------
+
+ * Stop running win+VS build by default.
+
+ * CI updates. We probably want a clean-up to move the long shell
+ script embedded in yaml file into a separate file, but that can
+ come later.
+
+ * Avoid unnecessary builds in CI, with settings configured in
+ ci-config.
+
+ * Redefining system functions for a few functions did not follow our
+ usual "implement git_foo() and #define foo(args) git_foo(args)"
+ pattern, which has broken build for some folks.
+
+ * Deal with a few deprecation warning from cURL library.
+
+ * Newer regex library macOS stopped enabling GNU-like enhanced BRE,
+ where '\(A\|B\)' works as alternation, unless explicitly asked with
+ the REG_ENHANCED flag. "git grep" now can be compiled to do so, to
+ retain the old behaviour.
+
+ * When given a pattern that matches an empty string at the end of a
+ line, the code to parse the "git diff" line-ranges fell into an
+ infinite loop, which has been corrected.
+
+ * Fix the sequence to fsync $GIT_DIR/packed-refs file that forgot to
+ flush its output to the disk..
+
+ * "git diff --relative" did not mix well with "git diff --ext-diff",
+ which has been corrected.
+
+ * The logic to see if we are using the "cone" mode by checking the
+ sparsity patterns has been tightened to avoid mistaking a pattern
+ that names a single file as specifying a cone.
+
+ * Doc update for environment variables set when hooks are invoked.
+
+ * Document ORIG_HEAD a bit more.
+
+ * "git ls-tree --format='%(path) %(path)' $tree $path" showed the
+ path three times, which has been corrected.
+
+ * Document that "branch -f <branch>" disables only the safety to
+ avoid recreating an existing branch.
+
+ * Clarify how "checkout -b/-B" and "git branch [-f]" are similar but
+ different in the documentation.
+
+Also contains minor documentation updates and code clean-ups.
diff --git a/Documentation/RelNotes/2.40.0.txt b/Documentation/RelNotes/2.40.0.txt
new file mode 100644
index 0000000..4232ad8
--- /dev/null
+++ b/Documentation/RelNotes/2.40.0.txt
@@ -0,0 +1,263 @@
+Git v2.40 Release Notes
+=======================
+
+UI, Workflows & Features
+
+ * "merge-tree" learns a new `--merge-base` option.
+
+ * "git jump" (in contrib/) learned to present the "quickfix list" to
+ its standard output (instead of letting it consumed by the editor
+ it invokes), and learned to also drive emacs/emacsclient.
+
+ * "git var UNKNOWN_VARIABLE" and "git var VARIABLE" with the variable
+ given an empty value used to behave identically. Now the latter
+ just gives an empty output, while the former still gives an error
+ message.
+
+ * Introduce a case insensitive mode to the Bash completion helpers.
+
+ * The advice message given by "git status" when it takes long time to
+ enumerate untracked paths has been updated.
+
+ * Just like "git var GIT_EDITOR" abstracts the complex logic to
+ choose which editor gets used behind it, "git var" now give support
+ to GIT_SEQUENCE_EDITOR.
+
+ * "git format-patch" learned to honor format.mboxrd even when sending
+ patches to the standard output stream,
+
+ * 'cat-file' gains mailmap support for its '--batch-check' and '-s'
+ options.
+
+ * Conditionally skip the pre-applypatch and applypatch-msg hooks when
+ applying patches with 'git am'.
+
+ * Introduce an optional configuration to allow the trailing hash that
+ protects the index file from bit flipping.
+
+ * "git check-attr" learned to take an optional tree-ish to read the
+ .gitattributes file from.
+
+ * "scalar" learned to give progress bar.
+
+ * "grep -P" learned to use Unicode Character Property to grok
+ character classes when processing \b and \w etc.
+
+ * "git rebase" often ignored incompatible options instead of
+ complaining, which has been corrected.
+
+ * "scalar" warns but continues when its periodic maintenance
+ feature cannot be enabled.
+
+ * The bundle-URI subsystem adds support for creation-token heuristics
+ to help incremental fetches.
+
+ * Userdiff regexp update for Java language.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * `git bisect` becomes a builtin.
+
+ * The pack-bitmap machinery is taught to log the paths of redundant
+ bitmap(s) to trace2 instead of stderr.
+
+ * Use the SHA1DC implementation on macOS, just like other platforms,
+ by default.
+
+ * Even in a repository with promisor remote, it is useless to
+ attempt to lazily attempt fetching an object that is expected to be
+ commit, because no "filter" mode omits commit objects. Take
+ advantage of this assumption to fail fast on errors.
+
+ * Stop using "git --super-prefix" and narrow the scope of its use to
+ the submodule--helper.
+
+ * Stop running win+VS build by default.
+
+ * CI updates. We probably want a clean-up to move the long shell
+ script embedded in yaml file into a separate file, but that can
+ come later.
+
+ * Use `git diff --no-index` as a test_cmp on Windows.
+
+ We'd probably need to revisit "do we really want to, and have to,
+ lose CRLF vs LF?" later, at which time we may be able to further
+ clean this up by replacing "git diff --no-index" with "diff -u".
+
+ * Avoid unnecessary builds in CI, with settings configured in
+ ci-config.
+
+ * Plug leaks in sequencer subsystem and its users.
+
+ * In-tree .gitattributes update to match the way we recommend our
+ users to mark a file as text.
+ (merge 1f34e0cd3d po/attributes-text later to maint).
+
+ * Finally retire the scripted "git add -p/-i" implementation and have
+ everybody use the one reimplemented in C.
+
+
+Fixes since v2.39
+-----------------
+
+ * Various leak fixes.
+
+ * Fix a bug where `pack-objects` would not respect multiple `--filter`
+ arguments when invoked directly.
+ (merge d4f7036887 rs/multi-filter-args later to maint).
+
+ * Make fsmonitor more robust to avoid the flakiness seen in t7527.
+ (merge 6692d45477 jh/t7527-unflake-by-forcing-cookie later to maint).
+
+ * Stop using deprecated macOS API in fsmonitor.
+ (merge b0226007f0 jh/fsmonitor-darwin-modernize later to maint).
+
+ * Redefining system functions for a few functions did not follow our
+ usual "implement git_foo() and #define foo(args) git_foo(args)"
+ pattern, which has broken build for some folks.
+
+ * The way the diff machinery prepares the options array for the
+ parse_options API has been refactored to avoid resource leaks.
+ (merge 189e97bc4b rs/diff-parseopts later to maint).
+
+ * Correct pthread API usage.
+ (merge 786e67611d sx/pthread-error-check-fix later to maint).
+
+ * The code to auto-correct a misspelt subcommand unnecessarily called
+ into git_default_config() from the early config codepath, which was
+ a no-no. This has bee corrected.
+ (merge 0918d08887 sg/help-autocorrect-config-fix later to maint).
+
+ * "git http-fetch" (which is rarely used) forgot to identify itself
+ in the trace2 output.
+ (merge 7abb43cbc8 jt/http-fetch-trace2-report-name later to maint).
+
+ * The output from "git diff --stat" on an unmerged path lost the
+ terminating LF in Git 2.39, which has been corrected.
+ (merge 209d9cb011 pg/diff-stat-unmerged-regression-fix later to maint).
+
+ * "git pull -v --recurse-submodules" attempted to pass "-v" down to
+ underlying "git submodule update", which did not understand the
+ request and barfed, which has been corrected.
+ (merge 6f65f84766 ss/pull-v-recurse-fix later to maint).
+
+ * When given a pattern that matches an empty string at the end of a
+ line, the code to parse the "git diff" line-ranges fell into an
+ infinite loop, which has been corrected.
+
+ * Fix the sequence to fsync $GIT_DIR/packed-refs file that forgot to
+ flush its output to the disk..
+
+ * Fix to a small regression in 2.38 days.
+
+ * "git diff --relative" did not mix well with "git diff --ext-diff",
+ which has been corrected.
+
+ * The logic to see if we are using the "cone" mode by checking the
+ sparsity patterns has been tightened to avoid mistaking a pattern
+ that names a single file as specifying a cone.
+
+ * Deal with a few deprecation warning from cURL library.
+
+ * Doc update for environment variables set when hooks are invoked.
+
+ * Document ORIG_HEAD a bit more.
+
+ * "git ls-tree --format='%(path) %(path)' $tree $path" showed the
+ path three times, which has been corrected.
+
+ * Remove "git env--helper" and demote it to a test-tool subcommand.
+ (merge 4a1baacd46 ab/test-env-helper later to maint).
+
+ * Newer regex library macOS stopped enabling GNU-like enhanced BRE,
+ where '\(A\|B\)' works as alternation, unless explicitly asked with
+ the REG_ENHANCED flag. "git grep" now can be compiled to do so, to
+ retain the old behaviour.
+
+ * Pthread emulation on Win32 leaked thread handle when a thread is
+ joined.
+ (merge 238a9dfe86 sk/win32-close-handle-upon-pthread-join later to maint).
+
+ * "git send-email -v 3" used to be expanded to "git send-email
+ --validate 3" when the user meant to pass them down to
+ "format-patch", which has been corrected.
+ (merge 8774aa56ad km/send-email-with-v-reroll-count later to maint).
+
+ * Document that "branch -f <branch>" disables only the safety to
+ avoid recreating an existing branch.
+
+ * "git fetch <group>", when "<group>" of remotes lists the same
+ remote twice, unnecessarily failed when parallel fetching was
+ enabled, which has been corrected.
+ (merge 06a668cb90 cw/fetch-remote-group-with-duplication later to maint).
+
+ * Clarify how "checkout -b/-B" and "git branch [-f]" are similar but
+ different in the documentation.
+
+ * "git hash-object" now checks that the resulting object is well
+ formed with the same code as "git fsck".
+ (merge 8e4309038f jk/hash-object-fsck later to maint).
+
+ * Improve the error message given when private key is not loaded in
+ the ssh agent in the codepath to sign with an ssh key.
+ (merge dce7b31126 as/ssh-signing-improve-key-missing-error later to maint).
+
+ * Adjust "git request-pull" to strip embedded signature from signed
+ tags to notice non-PGP signatures.
+ (merge a9cad02538 gm/request-pull-with-non-pgp-signed-tags later to maint).
+
+ * Remove support for MSys, which now lags way behind MSys2.
+ (merge 2987407f3c hj/remove-msys-support later to maint).
+
+ * Fix use of CreateThread() API call made early in the windows
+ start-up code.
+ (merge 592bcab61b sk/winansi-createthread-fix later to maint).
+
+ * "git pack-objects" learned to release delta-island bitmap data when
+ it is done using it, saving peak heap memory usage.
+ (merge 647982bb71 ew/free-island-marks later to maint).
+
+ * In an environment where dynamically generated code is prohibited to
+ run (e.g. SELinux), failure to JIT pcre patterns is expected. Fall
+ back to interpreted execution in such a case.
+ (merge 50b6ad55b0 cb/grep-fallback-failing-jit later to maint).
+
+ * "git name-rev" heuristics update.
+ (merge b2182a8730 en/name-rev-make-taggerdate-much-less-important later to maint).
+
+ * Remove more remaining uses of macros that relies on the_index
+ singleton instance without explicitly spelling it out.
+
+ * Remove unnecessary explicit sizing of strbuf.
+ (merge 93ea118bed rs/cache-tree-strbuf-growth-fix later to maint).
+
+ * Doc update.
+ (merge d9ec3b0dc0 jk/doc-ls-remote-matching later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+ (merge 4eb1ccecd4 dh/mingw-ownership-check-typofix later to maint).
+ (merge f95526419b ar/typofix-gitattributes-doc later to maint).
+ (merge 27875aeec9 km/doc-branch-start-point later to maint).
+ (merge 35c194dc57 es/t1509-root-fixes later to maint).
+ (merge 7b341645e3 pw/ci-print-failure-name-fix later to maint).
+ (merge bcb71d45bf jx/t1301-updates later to maint).
+ (merge ebdc46c242 jc/doc-diff-patch.txt later to maint).
+ (merge a87a20cbb4 ar/test-cleanup later to maint).
+ (merge f5156f1885 ar/bisect-doc-update later to maint).
+ (merge fca2d86c97 jk/interop-error later to maint).
+ (merge cf4936ed74 tl/ls-tree-code-clean-up later to maint).
+ (merge dcb47e52b0 en/t6426-todo-cleanup later to maint).
+ (merge 5b8db44bdd jc/format-patch-v-unleak later to maint).
+ (merge 590b636737 jk/hash-object-literally-fd-leak later to maint).
+ (merge 5458ba0a4d tb/t0003-invoke-dd-more-portably later to maint).
+ (merge 70661d288b ar/markup-em-dash later to maint).
+ (merge e750951e74 en/ls-files-doc-update later to maint).
+ (merge 4f542975d1 mh/doc-credential-cache-only-in-core later to maint).
+ (merge 3a2ebaebc7 gc/index-format-doc later to maint).
+ (merge b08edf709d jk/httpd-test-updates later to maint).
+ (merge d85e9448dd wl/new-command-doc later to maint).
+ (merge d912a603ed kf/t5000-modernise later to maint).
+ (merge e65b868d07 rs/size-t-fixes later to maint).
+ (merge 3eb1e1ca9a ab/config-h-remove-unused later to maint).
+ (merge d390e08076 cw/doc-pushurl-vs-url later to maint).
diff --git a/Documentation/config/add.txt b/Documentation/config/add.txt
index 3e859f3..e0354ce 100644
--- a/Documentation/config/add.txt
+++ b/Documentation/config/add.txt
@@ -7,6 +7,7 @@
variables.
add.interactive.useBuiltin::
- Set to `false` to fall back to the original Perl implementation of
- the interactive version of linkgit:git-add[1] instead of the built-in
- version. Is `true` by default.
+ Unused configuration variable. Used in Git versions v2.25.0 to
+ v2.36.0 to enable the built-in version of linkgit:git-add[1]'s
+ interactive mode, which then became the default in Git
+ versions v2.37.0 to v2.39.0.
diff --git a/Documentation/config/bundle.txt b/Documentation/config/bundle.txt
index daa21eb..3faae386 100644
--- a/Documentation/config/bundle.txt
+++ b/Documentation/config/bundle.txt
@@ -15,6 +15,13 @@
complete understanding of the bundled information (`all`) or if any one
of the listed bundle URIs is sufficient (`any`).
+bundle.heuristic::
+ If this string-valued key exists, then the bundle list is designed to
+ work well with incremental `git fetch` commands. The heuristic signals
+ that there are additional keys available for each bundle that help
+ determine which subset of bundles the client should download. The
+ only value currently understood is `creationToken`.
+
bundle.<id>.*::
The `bundle.<id>.*` keys are used to describe a single item in the
bundle list, grouped under `<id>` for identification purposes.
diff --git a/Documentation/config/feature.txt b/Documentation/config/feature.txt
index 95975e5..e52bc6b 100644
--- a/Documentation/config/feature.txt
+++ b/Documentation/config/feature.txt
@@ -23,6 +23,11 @@
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/fetch.txt b/Documentation/config/fetch.txt
index cd65d23..568f0f7 100644
--- a/Documentation/config/fetch.txt
+++ b/Documentation/config/fetch.txt
@@ -96,3 +96,27 @@
merge and the write may take longer. Having an updated commit-graph
file helps performance of many Git commands, including `git merge-base`,
`git push -f`, and `git log --graph`. Defaults to false.
+
+fetch.bundleURI::
+ This value stores a URI for downloading Git object data from a bundle
+ URI before performing an incremental fetch from the origin Git server.
+ This is similar to how the `--bundle-uri` option behaves in
+ linkgit:git-clone[1]. `git clone --bundle-uri` will set the
+ `fetch.bundleURI` value if the supplied bundle URI contains a bundle
+ list that is organized for incremental fetches.
++
+If you modify this value and your repository has a `fetch.bundleCreationToken`
+value, then remove that `fetch.bundleCreationToken` value before fetching from
+the new bundle URI.
+
+fetch.bundleCreationToken::
+ When using `fetch.bundleURI` to fetch incrementally from a bundle
+ list that uses the "creationToken" heuristic, this config value
+ stores the maximum `creationToken` value of the downloaded bundles.
+ This value is used to prevent downloading bundles in the future
+ if the advertised `creationToken` is not strictly larger than this
+ value.
++
+The creation token values are chosen by the provider serving the specific
+bundle URI. If you modify the URI at `fetch.bundleURI`, then be sure to
+remove the value for the `fetch.bundleCreationToken` value before fetching.
diff --git a/Documentation/config/format.txt b/Documentation/config/format.txt
index c7303d8..3bd7826 100644
--- a/Documentation/config/format.txt
+++ b/Documentation/config/format.txt
@@ -139,3 +139,7 @@
------------
+
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 75f3a2d..23c7985 100644
--- a/Documentation/config/index.txt
+++ b/Documentation/config/index.txt
@@ -30,3 +30,14 @@
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 264812c..c3ac767 100644
--- a/Documentation/config/transfer.txt
+++ b/Documentation/config/transfer.txt
@@ -115,3 +115,9 @@
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/diff-generate-patch.txt b/Documentation/diff-generate-patch.txt
index c78063d..546adf7 100644
--- a/Documentation/diff-generate-patch.txt
+++ b/Documentation/diff-generate-patch.txt
@@ -1,3 +1,4 @@
+[[generate_patch_text_with_p]]
Generating patch text with -p
-----------------------------
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 3674ac4..7d73e97 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -22,7 +22,13 @@
-p::
-u::
--patch::
- Generate patch (see section on generating patches).
+ Generate patch (see section titled
+ifdef::git-log[]
+<<generate_patch_text_with_p, "Generating patch text with -p">>).
+endif::git-log[]
+ifndef::git-log[]
+"Generating patch text with -p").
+endif::git-log[]
ifdef::git-diff[]
This is the default.
endif::git-diff[]
diff --git a/Documentation/fsck-msgids.txt b/Documentation/fsck-msgids.txt
index 7af76ff..12eae8a 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-add.txt b/Documentation/git-add.txt
index a030d33..ed44c1c 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -274,7 +274,7 @@
------------
staged unstaged path
1: binary nothing foo.png
- 2: +403/-35 +1/-1 git-add--interactive.perl
+ 2: +403/-35 +1/-1 add-interactive.c
------------
+
It shows that foo.png has differences from HEAD (but that is
@@ -282,7 +282,7 @@
difference between indexed copy and the working tree
version (if the working tree version were also different,
'binary' would have been shown in place of 'nothing'). The
-other file, git-add{litdd}interactive.perl, has 403 lines added
+other file, add-interactive.c, has 403 lines added
and 35 lines deleted if you commit what is in the index, but
working tree file has further modifications (one addition and
one deletion).
@@ -303,7 +303,7 @@
------------
staged unstaged path
1: binary nothing foo.png
-* 2: +403/-35 +1/-1 git-add--interactive.perl
+* 2: +403/-35 +1/-1 add-interactive.c
------------
+
To remove selection, prefix the input with `-`
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 326276e..0c1dfb3 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -9,7 +9,7 @@
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 @@
--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-apply.txt b/Documentation/git-apply.txt
index 1d478cb..5e16e6d 100644
--- a/Documentation/git-apply.txt
+++ b/Documentation/git-apply.txt
@@ -208,7 +208,7 @@
* `warn` outputs warnings for a few such errors, but applies the
patch as-is (default).
* `fix` outputs warnings for a few such errors, and applies the
- patch after fixing them (`strip` is a synonym --- the tool
+ patch after fixing them (`strip` is a synonym -- the tool
used to consider only trailing whitespace characters as errors, and the
fix involved 'stripping' them, but modern Gits do more).
* `error` outputs warnings for a few such errors, and refuses
diff --git a/Documentation/git-bisect-lk2009.txt b/Documentation/git-bisect-lk2009.txt
index f3d9566..0bc1657 100644
--- a/Documentation/git-bisect-lk2009.txt
+++ b/Documentation/git-bisect-lk2009.txt
@@ -1347,8 +1347,8 @@
References
----------
-- [[[1]]] https://www.nist.gov/sites/default/files/documents/director/planning/report02-3.pdf['The Economic Impacts of Inadequate Infratructure for Software Testing'. Nist Planning Report 02-3], see Executive Summary and Chapter 8.
-- [[[2]]] http://www.oracle.com/technetwork/java/codeconvtoc-136057.html['Code Conventions for the Java Programming Language'. Sun Microsystems.]
+- [[[1]]] https://web.archive.org/web/20091206032101/http://www.nist.gov/public_affairs/releases/n02-10.htm['Software Errors Cost U.S. Economy $59.5 Billion Annually'. Nist News Release.] See also https://www.nist.gov/system/files/documents/director/planning/report02-3.pdf['The Economic Impacts of Inadequate Infratructure for Software Testing'. Nist Planning Report 02-3], Executive Summary and Chapter 8.
+- [[[2]]] https://www.oracle.com/java/technologies/javase/codeconventions-introduction.html['Code Conventions for the Java Programming Language: 1. Introduction'. Sun Microsystems.]
- [[[3]]] https://en.wikipedia.org/wiki/Software_maintenance['Software maintenance'. Wikipedia.]
- [[[4]]] https://lore.kernel.org/git/7vps5xsbwp.fsf_-_@assigned-by-dhcp.cox.net/[Junio C Hamano. 'Automated bisect success story'.]
- [[[5]]] https://lwn.net/Articles/317154/[Christian Couder. 'Fully automated bisecting with "git bisect run"'. LWN.net.]
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 12c5f84..d382ac6 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -116,13 +116,17 @@
-f::
--force::
- Reset <branchname> to <startpoint>, even if <branchname> exists
+ Reset <branchname> to <start-point>, even if <branchname> exists
already. Without `-f`, 'git branch' refuses to change an existing branch.
In combination with `-d` (or `--delete`), allow deleting the
branch irrespective of its merged status, or whether it even
points to a valid commit. In combination with
`-m` (or `--move`), allow renaming the branch even if the new
branch name already exists, the same applies for `-c` (or `--copy`).
++
+Note that 'git branch -f <branchname> [<start-point>]', even with '-f',
+refuses to change an existing branch `<branchname>` that is checked out
+in another worktree linked to the same repository.
-m::
--move::
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index ec30b5c..411de2e 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -45,7 +45,9 @@
-s::
Instead of the content, show the object size identified by
- `<object>`.
+ `<object>`. If used with `--use-mailmap` option, will show
+ the size of updated object after replacing idents using the
+ mailmap mechanism.
-e::
Exit with zero status if `<object>` exists and is a valid
@@ -89,26 +91,54 @@
--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-check-attr.txt b/Documentation/git-check-attr.txt
index 84f41a8..6e4f3aa 100644
--- a/Documentation/git-check-attr.txt
+++ b/Documentation/git-check-attr.txt
@@ -9,8 +9,8 @@
SYNOPSIS
--------
[verse]
-'git check-attr' [-a | --all | <attr>...] [--] <pathname>...
-'git check-attr' --stdin [-z] [-a | --all | <attr>...]
+'git check-attr' [--source <tree-ish>] [-a | --all | <attr>...] [--] <pathname>...
+'git check-attr' --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]
DESCRIPTION
-----------
@@ -36,6 +36,11 @@
If `--stdin` is also given, input paths are separated
with a NUL character instead of a linefeed character.
+--source=<tree-ish>::
+ Check attributes against the specified tree-ish. It is common to
+ specify the source tree by naming a commit, branch or tag associated
+ with it.
+
\--::
Interpret all preceding arguments as attributes and all following
arguments as path names.
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 4cb9d55..6bb32ab 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -146,14 +146,16 @@
of it").
-b <new-branch>::
- Create a new branch named `<new-branch>` and start it at
- `<start-point>`; see linkgit:git-branch[1] for details.
+ Create a new branch named `<new-branch>`, start it at
+ `<start-point>`, and check the resulting branch out;
+ see linkgit:git-branch[1] for details.
-B <new-branch>::
- Creates the branch `<new-branch>` and start it at `<start-point>`;
- if it already exists, then reset it to `<start-point>`. This is
- equivalent to running "git branch" with "-f"; see
- linkgit:git-branch[1] for details.
+ Creates the branch `<new-branch>`, start it at `<start-point>`;
+ if it already exists, then reset it to `<start-point>`. And then
+ check the resulting branch out. This is equivalent to running
+ "git branch" with "-f" followed by "git checkout" of that branch;
+ see linkgit:git-branch[1] for details.
-t::
--track[=(direct|inherit)]::
@@ -477,9 +479,9 @@
any of these will create a reference to it:
------------
-$ git checkout -b foo <1>
-$ git branch foo <2>
-$ git tag foo <3>
+$ git checkout -b foo # or "git switch -c foo" <1>
+$ git branch foo <2>
+$ git tag foo <3>
------------
<1> creates a new branch `foo`, which refers to commit `f`, and then
diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index 1e8ac9d..fdcad3d 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -219,7 +219,7 @@
------------
$ git cherry-pick topic^ <1>
$ git diff <2>
-$ git reset --merge ORIG_HEAD <3>
+$ git cherry-pick --abort <3>
$ git cherry-pick -Xpatience topic^ <4>
------------
<1> apply the change that would be shown by `git show topic^`.
diff --git a/Documentation/git-credential-cache.txt b/Documentation/git-credential-cache.txt
index 432e159..f473994 100644
--- a/Documentation/git-credential-cache.txt
+++ b/Documentation/git-credential-cache.txt
@@ -14,10 +14,13 @@
DESCRIPTION
-----------
-This command caches credentials in memory for use by future Git
-programs. The stored credentials never touch the disk, and are forgotten
-after a configurable timeout. The cache is accessible over a Unix
-domain socket, restricted to the current user by filesystem permissions.
+This command caches credentials for use by future Git programs.
+The stored credentials are kept in memory of the cache-daemon
+process (instead of written to a file) and are forgotten after a
+configurable timeout. Credentials are forgotten sooner if the
+cache-daemon dies, for example if the system restarts. The cache
+is accessible over a Unix domain socket, restricted to the current
+user by filesystem permissions.
You probably don't want to invoke this command directly; it is meant to
be used as a credential helper by other parts of Git. See
diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt
index 63d9569..fba66f1 100644
--- a/Documentation/git-fetch.txt
+++ b/Documentation/git-fetch.txt
@@ -251,10 +251,10 @@
$ git fetch origin
------------------------------------------------
+
-The above command copies all branches from the remote refs/heads/
-namespace and stores them to the local refs/remotes/origin/ namespace,
-unless the branch.<name>.fetch option is used to specify a non-default
-refspec.
+The above command copies all branches from the remote `refs/heads/`
+namespace and stores them to the local `refs/remotes/origin/` namespace,
+unless the `remote.<repository>.fetch` option is used to specify a
+non-default refspec.
* Using refspecs explicitly:
+
diff --git a/Documentation/git-hook.txt b/Documentation/git-hook.txt
index 77c3a8a..3407f3c 100644
--- a/Documentation/git-hook.txt
+++ b/Documentation/git-hook.txt
@@ -8,7 +8,7 @@
SYNOPSIS
--------
[verse]
-'git hook' run [--ignore-missing] <hook-name> [-- <hook-args>]
+'git hook' run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>]
DESCRIPTION
-----------
@@ -31,6 +31,11 @@
OPTIONS
-------
+--to-stdin::
+ For "run"; Specify a file which will be streamed into the
+ hook's stdin. The hook will receive the entire file from
+ beginning to EOF.
+
--ignore-missing::
Ignore any missing hook by quietly returning zero. Used for
tools that want to do a blind one-shot run of a hook that may
diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index 440043c..1abdd3c 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -12,6 +12,7 @@
'git ls-files' [-z] [-t] [-v] [-f]
[-c|--cached] [-d|--deleted] [-o|--others] [-i|--ignored]
[-s|--stage] [-u|--unmerged] [-k|--killed] [-m|--modified]
+ [--resolve-undo]
[--directory [--no-empty-directory]] [--eol]
[--deduplicate]
[-x <pattern>|--exclude=<pattern>]
@@ -28,21 +29,26 @@
directory list, and shows different combinations of the two.
One or more of the options below may be used to determine the files
-shown:
+shown, and each file may be printed multiple times if there are
+multiple entries in the index or multiple statuses are applicable for
+the relevant file selection options.
OPTIONS
-------
-c::
--cached::
- Show cached files in the output (default)
+ Show all files cached in Git's index, i.e. all tracked files.
+ (This is the default if no -c/-s/-d/-o/-u/-k/-m/--resolve-undo
+ options are specified.)
-d::
--deleted::
- Show deleted files in the output
+ Show files with an unstaged deletion
-m::
--modified::
- Show modified files in the output
+ Show files with an unstaged modification (note that an unstaged
+ deletion also counts as an unstaged modification)
-o::
--others::
@@ -50,11 +56,14 @@
-i::
--ignored::
- Show only ignored files in the output. When showing files in the
- index, print only those matched by an exclude pattern. When
- showing "other" files, show only those matched by an exclude
- pattern. Standard ignore rules are not automatically activated,
- therefore at least one of the `--exclude*` options is required.
+ Show only ignored files in the output. Must be used with
+ either an explicit '-c' or '-o'. When showing files in the
+ index (i.e. when used with '-c'), print only those files
+ matching an exclude pattern. When showing "other" files
+ (i.e. when used with '-o'), show only those matched by an
+ exclude pattern. Standard ignore rules are not automatically
+ activated, therefore at least one of the `--exclude*` options
+ is required.
-s::
--stage::
@@ -63,19 +72,29 @@
--directory::
If a whole directory is classified as "other", show just its
name (with a trailing slash) and not its whole contents.
+ Has no effect without -o/--others.
--no-empty-directory::
Do not list empty directories. Has no effect without --directory.
-u::
--unmerged::
- Show unmerged files in the output (forces --stage)
+ Show information about unmerged files in the output, but do
+ not show any other tracked files (forces --stage, overrides
+ --cached).
-k::
--killed::
- Show files on the filesystem that need to be removed due
- to file/directory conflicts for checkout-index to
- succeed.
+ Show untracked files on the filesystem that need to be removed
+ due to file/directory conflicts for tracked files to be able to
+ be written to the filesystem.
+
+--resolve-undo::
+ Show files having resolve-undo information in the index
+ together with their resolve-undo information. (resolve-undo
+ information is what is used to implement "git checkout -m
+ $PATH", i.e. to recreate merge conflicts that were
+ accidentally resolved)
-z::
\0 line termination on output and do not quote filenames.
@@ -100,7 +119,8 @@
--exclude-per-directory=<file>::
Read additional exclude patterns that apply only to the
- directory and its subdirectories in <file>.
+ directory and its subdirectories in <file>. Deprecated; use
+ --exclude-standard instead.
--exclude-standard::
Add the standard Git exclusions: .git/info/exclude, .gitignore
@@ -118,24 +138,27 @@
with `-s` or `-u` options does not make any sense.
-t::
- This feature is semi-deprecated. For scripting purpose,
- linkgit:git-status[1] `--porcelain` and
+ Show status tags together with filenames. Note that for
+ scripting purposes, linkgit:git-status[1] `--porcelain` and
linkgit:git-diff-files[1] `--name-status` are almost always
superior alternatives, and users should look at
linkgit:git-status[1] `--short` or linkgit:git-diff[1]
`--name-status` for more user-friendly alternatives.
+
--
-This option identifies the file status with the following tags (followed by
-a space) at the start of each line:
+This option provides a reason for showing each filename, in the form
+of a status tag (which is followed by a space and then the filename).
+The status tags are all single characters from the following list:
- H:: cached
- S:: skip-worktree
- M:: unmerged
- R:: removed/deleted
- C:: modified/changed
- K:: to be killed
- ?:: other
+ H:: tracked file that is not either unmerged or skip-worktree
+ S:: tracked file that is skip-worktree
+ M:: tracked file that is unmerged
+ R:: tracked file with unstaged removal/deletion
+ C:: tracked file with unstaged modification/change
+ K:: untracked paths which are part of file/directory conflicts
+ which prevent checking out tracked files
+ ?:: untracked file
+ U:: file with resolve-undo information
--
-v::
@@ -269,7 +292,9 @@
flags --others or --ignored are specified. linkgit:gitignore[5]
specifies the format of exclude patterns.
-These exclude patterns come from these places, in order:
+Generally, you should just use --exclude-standard, but for historical
+reasons the exclude patterns can be specified from the following
+places, in order:
1. The command-line flag --exclude=<pattern> specifies a
single pattern. Patterns are ordered in the same order
diff --git a/Documentation/git-ls-remote.txt b/Documentation/git-ls-remote.txt
index 492e573..ff3da54 100644
--- a/Documentation/git-ls-remote.txt
+++ b/Documentation/git-ls-remote.txt
@@ -11,7 +11,7 @@
[verse]
'git ls-remote' [--heads] [--tags] [--refs] [--upload-pack=<exec>]
[-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]
- [--symref] [<repository> [<refs>...]]
+ [--symref] [<repository> [<patterns>...]]
DESCRIPTION
-----------
@@ -85,25 +85,32 @@
either a URL or the name of a remote (see the GIT URLS and
REMOTES sections of linkgit:git-fetch[1]).
-<refs>...::
+<patterns>...::
When unspecified, all references, after filtering done
- with --heads and --tags, are shown. When <refs>... are
- specified, only references matching the given patterns
- are displayed.
+ with --heads and --tags, are shown. When <patterns>... are
+ specified, only references matching one or more of the given
+ patterns are displayed. Each pattern is interpreted as a glob
+ (see `glob` in linkgit:gitglossary[7]) which is matched against
+ the "tail" of a ref, starting either from the start of the ref
+ (so a full name like `refs/heads/foo` matches) or from a slash
+ separator (so `bar` matches `refs/heads/bar` but not
+ `refs/heads/foobar`).
EXAMPLES
--------
----
-$ git ls-remote --tags ./.
+$ git ls-remote --tags .
d6602ec5194c87b0fc87103ca4d67251c76f233a refs/tags/v0.99
f25a265a342aed6041ab0cc484224d9ca54b6f41 refs/tags/v0.99.1
7ceca275d047c90c0c7d5afb13ab97efdf51bd6e refs/tags/v0.99.3
c5db5456ae3b0873fc659c19fafdde22313cc441 refs/tags/v0.99.2
0918385dbd9656cab0d1d81ba7453d49bbc16250 refs/tags/junio-gpg-pub
+
$ git ls-remote http://www.kernel.org/pub/scm/git/git.git master seen rc
5fe978a5381f1fbad26a80e682ddd2a401966740 refs/heads/master
c781a84b5204fb294c9ccc79f8b3baceeb32c061 refs/heads/seen
+
$ git remote add korg http://www.kernel.org/pub/scm/git/git.git
$ git ls-remote --tags korg v\*
d6602ec5194c87b0fc87103ca4d67251c76f233a refs/tags/v0.99
diff --git a/Documentation/git-merge-tree.txt b/Documentation/git-merge-tree.txt
index 04bcc41..88ee942 100644
--- a/Documentation/git-merge-tree.txt
+++ b/Documentation/git-merge-tree.txt
@@ -64,6 +64,11 @@
share no common history. This flag can be given to override that
check and make the merge proceed anyway.
+--merge-base=<commit>::
+ Instead of finding the merge-bases for <branch1> and <branch2>,
+ specify a merge-base for the merge, and specifying multiple bases is
+ currently not supported. This option is incompatible with `--stdin`.
+
[[OUTPUT]]
OUTPUT
------
@@ -216,6 +221,17 @@
* any messages that would have been printed to stdout (the
<<IM,Informational messages>>)
+INPUT FORMAT
+------------
+'git merge-tree --stdin' input format is fully text based. Each line
+has this format:
+
+ [<base-commit> -- ]<branch1> <branch2>
+
+If one line is separated by `--`, the string before the separator is
+used for specifying a merge-base for the merge and the string after
+the separator describes the branches to be merged.
+
MISTAKES TO AVOID
-----------------
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 2d6a139..0aeff57 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -37,7 +37,8 @@
`topic` branch since it diverged from `master` (i.e., `E`) until
its current commit (`C`) on top of `master`, and record the result
in a new commit along with the names of the two parent commits and
-a log message from the user describing the changes.
+a log message from the user describing the changes. Before the operation,
+`ORIG_HEAD` is set to the tip of the current branch (`C`).
------------
A---B---C topic
diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt
index 7567955..b097074 100644
--- a/Documentation/git-read-tree.txt
+++ b/Documentation/git-read-tree.txt
@@ -219,7 +219,7 @@
`git diff-index --cached $M`. Note that this does not
necessarily match what `git diff-index --cached $H` would have
produced before such a two tree merge. This is because of cases
-18 and 19 --- if you already had the changes in $M (e.g. maybe
+18 and 19 -- if you already had the changes in $M (e.g. maybe
you picked it up via e-mail in a patch form), `git diff-index
--cached $H` would have told you about the change before this
merge, but it would not show in `git diff-index --cached $M`
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index f9675bd..9a295bc 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -38,6 +38,13 @@
`git reset --hard <upstream>` (or `<newbase>`). `ORIG_HEAD` is set
to point at the tip of the branch before the reset.
+[NOTE]
+`ORIG_HEAD` is not guaranteed to still point to the previous branch tip
+at the end of the rebase if other commands that write that pseudo-ref
+(e.g. `git reset`) are used during the rebase. The previous branch tip,
+however, is accessible using the reflog of the current branch
+(i.e. `@{1}`, see linkgit:gitrevisions[7]).
+
The commits that were previously saved into the temporary area are
then reapplied to the current branch, one by one, in order. Note that
any commits in `HEAD` which introduce the same textual changes as a commit
@@ -201,6 +208,39 @@
git rebase --abort
+MODE OPTIONS
+------------
+
+The options in this section cannot be used with any other option,
+including not with each other:
+
+--continue::
+ Restart the rebasing process after having resolved a merge conflict.
+
+--skip::
+ Restart the rebasing process by skipping the current patch.
+
+--abort::
+ Abort the rebase operation and reset HEAD to the original
+ branch. If `<branch>` was provided when the rebase operation was
+ started, then `HEAD` will be reset to `<branch>`. Otherwise `HEAD`
+ will be reset to where it was when the rebase operation was
+ started.
+
+--quit::
+ Abort the rebase operation but `HEAD` is not reset back to the
+ original branch. The index and working tree are also left
+ unchanged as a result. If a temporary stash entry was created
+ using `--autostash`, it will be saved to the stash list.
+
+--edit-todo::
+ Edit the todo list during an interactive rebase.
+
+--show-current-patch::
+ Show the current patch in an interactive rebase or when rebase
+ is stopped because of conflicts. This is the equivalent of
+ `git show REBASE_HEAD`.
+
OPTIONS
-------
--onto <newbase>::
@@ -242,22 +282,6 @@
<branch>::
Working branch; defaults to `HEAD`.
---continue::
- Restart the rebasing process after having resolved a merge conflict.
-
---abort::
- Abort the rebase operation and reset HEAD to the original
- branch. If `<branch>` was provided when the rebase operation was
- started, then `HEAD` will be reset to `<branch>`. Otherwise `HEAD`
- will be reset to where it was when the rebase operation was
- started.
-
---quit::
- Abort the rebase operation but `HEAD` is not reset back to the
- original branch. The index and working tree are also left
- unchanged as a result. If a temporary stash entry was created
- using `--autostash`, it will be saved to the stash list.
-
--apply::
Use applying strategies to rebase (calling `git-am`
internally). This option may become a no-op in the future
@@ -314,7 +338,6 @@
upstream changes, the behavior towards them is controlled by
the `--empty` flag.)
+
-
In the absence of `--keep-base` (or if `--no-reapply-cherry-picks` is
given), these commits will be automatically dropped. Because this
necessitates reading all upstream commits, this can be expensive in
@@ -323,7 +346,6 @@
dropped commit (unless `--quiet` is given). Advice will also be issued
unless `advice.skippedCherryPicks` is set to false (see
linkgit:git-config[1]).
-
+
`--reapply-cherry-picks` allows rebase to forgo reading all upstream
commits, potentially improving performance.
@@ -338,17 +360,6 @@
+
See also INCOMPATIBLE OPTIONS below.
---skip::
- Restart the rebasing process by skipping the current patch.
-
---edit-todo::
- Edit the todo list during an interactive rebase.
-
---show-current-patch::
- Show the current patch in an interactive rebase or when rebase
- is stopped because of conflicts. This is the equivalent of
- `git show REBASE_HEAD`.
-
-m::
--merge::
Using merging strategies to rebase (default).
@@ -567,10 +578,7 @@
--root::
Rebase all commits reachable from `<branch>`, instead of
limiting them with an `<upstream>`. This allows you to rebase
- the root commit(s) on a branch. When used with `--onto`, it
- will skip changes already contained in `<newbase>` (instead of
- `<upstream>`) whereas without `--onto` it will operate on every
- change.
+ the root commit(s) on a branch.
+
See also INCOMPATIBLE OPTIONS below.
@@ -623,6 +631,8 @@
+
If the configuration variable `rebase.updateRefs` is set, then this option
can be used to override and disable this setting.
++
+See also INCOMPATIBLE OPTIONS below.
INCOMPATIBLE OPTIONS
--------------------
@@ -638,17 +648,15 @@
* --merge
* --strategy
* --strategy-option
- * --allow-empty-message
- * --[no-]autosquash
+ * --autosquash
* --rebase-merges
* --interactive
* --exec
* --no-keep-empty
* --empty=
- * --reapply-cherry-picks
- * --edit-todo
+ * --[no-]reapply-cherry-picks when used without --keep-base
* --update-refs
- * --root when used in combination with --onto
+ * --root when used without --onto
In addition, the following pairs of options are incompatible:
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 01cb4c9..79ad564 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -49,7 +49,8 @@
'git reset' [<mode>] [<commit>]::
This form resets the current branch head to `<commit>` and
possibly updates the index (resetting it to the tree of `<commit>`) and
- the working tree depending on `<mode>`. If `<mode>` is omitted,
+ the working tree depending on `<mode>`. Before the operation, `ORIG_HEAD`
+ is set to the tip of the current branch. If `<mode>` is omitted,
defaults to `--mixed`. The `<mode>` must be one of the following:
+
--
diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt
index 5e438a7..a051b1e 100644
--- a/Documentation/git-status.txt
+++ b/Documentation/git-status.txt
@@ -457,6 +457,66 @@
them to fail. Scripts running `status` in the background should consider
using `git --no-optional-locks status` (see linkgit:git[1] for details).
+UNTRACKED FILES AND PERFORMANCE
+-------------------------------
+
+`git status` can be very slow in large worktrees if/when it
+needs to search for untracked files and directories. There are
+many configuration options available to speed this up by either
+avoiding the work or making use of cached results from previous
+Git commands. There is no single optimum set of settings right
+for everyone. We'll list a summary of the relevant options to help
+you, but before going into the list, you may want to run `git status`
+again, because your configuration may already be caching `git status`
+results, so it could be faster on subsequent runs.
+
+* The `--untracked-files=no` flag or the
+ `status.showUntrackedfiles=false` config (see above for both):
+ indicate that `git status` should not report untracked
+ files. This is the fastest option. `git status` will not list
+ the untracked files, so you need to be careful to remember if
+ you create any new files and manually `git add` them.
+
+* `advice.statusUoption=false` (see linkgit:git-config[1]):
+ setting this variable to `false` disables the warning message
+ given when enumerating untracked files takes more than 2
+ seconds. In a large project, it may take longer and the user
+ may have already accepted the trade off (e.g. using "-uno" may
+ not be an acceptable option for the user), in which case, there
+ is no point issuing the warning message, and in such a case,
+ disabling the warning may be the best.
+
+* `core.untrackedCache=true` (see linkgit:git-update-index[1]):
+ enable the untracked cache feature and only search directories
+ that have been modified since the previous `git status` command.
+ Git remembers the set of untracked files within each directory
+ and assumes that if a directory has not been modified, then
+ the set of untracked files within has not changed. This is much
+ faster than enumerating the contents of every directory, but still
+ not without cost, because Git still has to search for the set of
+ modified directories. The untracked cache is stored in the
+ `.git/index` file. The reduced cost of searching for untracked
+ files is offset slightly by the increased size of the index and
+ the cost of keeping it up-to-date. That reduced search time is
+ usually worth the additional size.
+
+* `core.untrackedCache=true` and `core.fsmonitor=true` or
+ `core.fsmonitor=<hook_command_pathname>` (see
+ linkgit:git-update-index[1]): enable both the untracked cache
+ and FSMonitor features and only search directories that have
+ been modified since the previous `git status` command. This
+ is faster than using just the untracked cache alone because
+ Git can also avoid searching for modified directories. Git
+ only has to enumerate the exact set of directories that have
+ changed recently. While the FSMonitor feature can be enabled
+ without the untracked cache, the benefits are greatly reduced
+ in that case.
+
+Note that after you turn on the untracked cache and/or FSMonitor
+features it may take a few `git status` commands for the various
+caches to warm up before you see improved command times. This is
+normal.
+
SEE ALSO
--------
linkgit:gitignore[5]
diff --git a/Documentation/git-var.txt b/Documentation/git-var.txt
index 6aa521f..f40202b 100644
--- a/Documentation/git-var.txt
+++ b/Documentation/git-var.txt
@@ -13,7 +13,8 @@
DESCRIPTION
-----------
-Prints a Git logical variable.
+Prints a Git logical variable. Exits with code 1 if the variable has
+no value.
OPTIONS
-------
@@ -49,6 +50,14 @@
The build you are using chose '{git-default-editor}' as the default.
endif::git-default-editor[]
+GIT_SEQUENCE_EDITOR::
+ Text editor used to edit the 'todo' file while running `git rebase
+ -i`. Like `GIT_EDITOR`, the value is meant to be interpreted by
+ the shell when it is used. The order of preference is the
+ `$GIT_SEQUENCE_EDITOR` environment variable, then
+ `sequence.editor` configuration, and then the value of `git var
+ GIT_EDITOR`.
+
GIT_PAGER::
Text viewer for use by Git commands (e.g., 'less'). The value
is meant to be interpreted by the shell. The order of preference
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 1d33e08..74973d3 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -13,8 +13,7 @@
[--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 @@
details. Equivalent to setting the `GIT_NAMESPACE` environment
variable.
---super-prefix=<path>::
- Currently for internal use only. Set a prefix which gives a path from
- above a repository down to its root. One use is to give submodules
- context about the superproject that invoked it.
-
--bare::
Treat the repository as a bare repository. If GIT_DIR
environment is not set, it is set to the current working
@@ -619,7 +613,7 @@
(e.g. `new-file` in "git-diff-files"), `/dev/null` (e.g. `old-file`
when a new file is added), or a temporary file (e.g. `old-file` in the
index). `GIT_EXTERNAL_DIFF` should not worry about unlinking the
-temporary file --- it is removed when `GIT_EXTERNAL_DIFF` exits.
+temporary file -- it is removed when `GIT_EXTERNAL_DIFF` exits.
+
For a path that is unmerged, `GIT_EXTERNAL_DIFF` is called with 1
parameter, <path>.
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 4b36d51..c19e64e 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -1155,7 +1155,7 @@
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/gitformat-index.txt b/Documentation/gitformat-index.txt
index 015cb21..0773e5c 100644
--- a/Documentation/gitformat-index.txt
+++ b/Documentation/gitformat-index.txt
@@ -83,11 +83,13 @@
32-bit mode, split into (high to low bits)
+ 16-bit unused, must be zero
+
4-bit object type
valid values in binary are 1000 (regular file), 1010 (symbolic link)
and 1110 (gitlink)
- 3-bit unused
+ 3-bit unused, must be zero
9-bit unix permission. Only 0755 and 0644 are valid for regular files.
Symbolic links and gitlinks have value 0 in this field.
diff --git a/Documentation/gitformat-signature.txt b/Documentation/gitformat-signature.txt
index a249869..d8e3eb1 100644
--- a/Documentation/gitformat-signature.txt
+++ b/Documentation/gitformat-signature.txt
@@ -37,7 +37,7 @@
This is even true for an originally empty line. In the following
examples, the end of line that ends with a whitespace letter is
highlighted with a `$` sign; if you are trying to recreate these
-example by hand, do not cut and paste them---they are there
+example by hand, do not cut and paste them--they are there
primarily to highlight extra whitespace at the end of some lines.
The signed payload and the way the signature is embedded depends
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index a16e62b..6290860 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -27,6 +27,18 @@
'update', 'post-receive', 'post-update', 'push-to-checkout') which are always
executed in $GIT_DIR.
+Environment variables, such as `GIT_DIR`, `GIT_WORK_TREE`, etc., are exported
+so that Git commands run by the hook can correctly locate the repository. If
+your hook needs to invoke Git commands in a foreign repository or in a
+different working tree of the same repository, then it should clear these
+environment variables so they do not interfere with Git operations at the
+foreign location. For example:
+
+------------
+local_desc=$(git describe)
+foreign_desc=$(unset $(git rev-parse --local-env-vars); git -C ../foreign-repo describe)
+------------
+
Hooks can get their arguments via the environment, command-line
arguments, and stdin. See the documentation for each hook below for
details.
diff --git a/Documentation/gitprotocol-v2.txt b/Documentation/gitprotocol-v2.txt
index 59bf41c..acb97ad 100644
--- a/Documentation/gitprotocol-v2.txt
+++ b/Documentation/gitprotocol-v2.txt
@@ -578,6 +578,207 @@
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/Documentation/howto/new-command.txt b/Documentation/howto/new-command.txt
index 15a4c80..880c511 100644
--- a/Documentation/howto/new-command.txt
+++ b/Documentation/howto/new-command.txt
@@ -1,13 +1,13 @@
From: Eric S. Raymond <esr@thyrsus.com>
Abstract: This is how-to documentation for people who want to add extension
- commands to Git. It should be read alongside api-builtin.txt.
+ commands to Git. It should be read alongside builtin.h.
Content-type: text/asciidoc
How to integrate new subcommands
================================
This is how-to documentation for people who want to add extension
-commands to Git. It should be read alongside api-builtin.txt.
+commands to Git. It should be read alongside builtin.h.
Runtime environment
-------------------
diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index 0b4c1c8..3b71334 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -146,24 +146,34 @@
'%m':: left (`<`), right (`>`) or boundary (`-`) mark
'%w([<w>[,<i1>[,<i2>]]])':: switch line wrapping, like the -w option of
linkgit:git-shortlog[1].
-'%<(<N>[,trunc|ltrunc|mtrunc])':: make the next placeholder take at
- least N columns, padding spaces on
+'%<( <N> [,trunc|ltrunc|mtrunc])':: make the next placeholder take at
+ least N column widths, padding spaces on
the right if necessary. Optionally
- truncate at the beginning (ltrunc),
- the middle (mtrunc) or the end
- (trunc) if the output is longer than
- N columns. Note that truncating
+ truncate (with ellipsis '..') at the left (ltrunc) `..ft`,
+ the middle (mtrunc) `mi..le`, or the end
+ (trunc) `rig..`, if the output is longer than
+ N columns.
+ Note 1: that truncating
only works correctly with N >= 2.
-'%<|(<N>)':: make the next placeholder take at least until Nth
- columns, padding spaces on the right if necessary
-'%>(<N>)', '%>|(<N>)':: similar to '%<(<N>)', '%<|(<N>)' respectively,
+ Note 2: spaces around the N and M (see below)
+ values are optional.
+ Note 3: Emojis and other wide characters
+ will take two display columns, which may
+ over-run column boundaries.
+ Note 4: decomposed character combining marks
+ may be misplaced at padding boundaries.
+'%<|( <M> )':: make the next placeholder take at least until Mth
+ display column, padding spaces on the right if necessary.
+ Use negative M values for column positions measured
+ from the right hand edge of the terminal window.
+'%>( <N> )', '%>|( <M> )':: similar to '%<( <N> )', '%<|( <M> )' respectively,
but padding spaces on the left
-'%>>(<N>)', '%>>|(<N>)':: similar to '%>(<N>)', '%>|(<N>)'
+'%>>( <N> )', '%>>|( <M> )':: similar to '%>( <N> )', '%>|( <M> )'
respectively, except that if the next
placeholder takes more spaces than given and
there are spaces on its left, use those
spaces
-'%><(<N>)', '%><|(<N>)':: similar to '%<(<N>)', '%<|(<N>)'
+'%><( <N> )', '%><|( <M> )':: similar to '%<( <N> )', '%<|( <M> )'
respectively, but padding both sides
(i.e. the text is centered)
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index ff68e48..0d90d5b 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -1100,12 +1100,12 @@
format placeholders. When using `-local`, the correct syntax is
`--date=format-local:...`.
-`--date=default` is the default format, and is similar to
-`--date=rfc2822`, with a few exceptions:
+`--date=default` is the default format, and is based on ctime(3)
+output. It shows a single line with three-letter day of the week,
+three-letter month, day-of-month, hour-minute-seconds in "HH:MM:SS"
+format, followed by 4-digit year, plus timezone information, unless
+the local time zone is used, e.g. `Thu Jan 1 00:00:00 1970 +0000`.
--
- - there is no comma after the day-of-week
-
- - the time zone is omitted when the local time zone is used
ifdef::git-rev-list[]
--header::
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index 0d2e55d..9aa5805 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -49,7 +49,8 @@
`FETCH_HEAD` records the branch which you fetched from a remote repository
with your last `git fetch` invocation.
`ORIG_HEAD` is created by commands that move your `HEAD` in a drastic
-way, to record the position of the `HEAD` before their operation, so that
+way (`git am`, `git merge`, `git rebase`, `git reset`),
+to record the position of the `HEAD` before their operation, so that
you can easily change the tip of the branch back to the state before you ran
them.
`MERGE_HEAD` records the commit(s) which you are merging into your branch
diff --git a/Documentation/technical/bundle-uri.txt b/Documentation/technical/bundle-uri.txt
index b78d01d..91d3a13 100644
--- a/Documentation/technical/bundle-uri.txt
+++ b/Documentation/technical/bundle-uri.txt
@@ -479,14 +479,14 @@
(This choice is an opt-in via a config option and a command-line
option.)
-4. Allow the client to understand the `bundle.flag=forFetch` configuration
+4. Allow the client to understand the `bundle.heuristic` configuration key
and the `bundle.<id>.creationToken` heuristic. When `git clone`
- discovers a bundle URI with `bundle.flag=forFetch`, it configures the
- client repository to check that bundle URI during later `git fetch <remote>`
+ discovers a bundle URI with `bundle.heuristic`, it configures the client
+ repository to check that bundle URI during later `git fetch <remote>`
commands.
5. Allow clients to discover bundle URIs during `git fetch` and configure
- a bundle URI for later fetches if `bundle.flag=forFetch`.
+ a bundle URI for later fetches if `bundle.heuristic` is set.
6. Implement the "inspect headers" heuristic to reduce data downloads when
the `bundle.<id>.creationToken` heuristic is not available.
diff --git a/Documentation/technical/hash-function-transition.txt b/Documentation/technical/hash-function-transition.txt
index e2ac36d..ed57481 100644
--- a/Documentation/technical/hash-function-transition.txt
+++ b/Documentation/technical/hash-function-transition.txt
@@ -562,7 +562,7 @@
The design described here allows fetches by SHA-1 clients of a
personal SHA-256 repository because it's not much more difficult than
allowing pushes from that repository. This support needs to be guarded
-by a configuration option --- servers like git.kernel.org that serve a
+by a configuration option -- servers like git.kernel.org that serve a
large number of clients would not be expected to bear that cost.
Meaning of signatures
diff --git a/Documentation/technical/rerere.txt b/Documentation/technical/rerere.txt
index 35d4541..be58f1b 100644
--- a/Documentation/technical/rerere.txt
+++ b/Documentation/technical/rerere.txt
@@ -99,7 +99,7 @@
compatible with what AB and AC wanted to do.
So the conflict we would see when merging AB into ACAB should be
-resolved the same way---it is the resolution that is in line with that
+resolved the same way--it is the resolution that is in line with that
declaration.
Imagine that similarly previously a branch XYXZ was forked from XY,
diff --git a/Documentation/urls-remotes.txt b/Documentation/urls-remotes.txt
index 86d0008..e410912 100644
--- a/Documentation/urls-remotes.txt
+++ b/Documentation/urls-remotes.txt
@@ -33,7 +33,9 @@
------------
The `<pushurl>` is used for pushes only. It is optional and defaults
-to `<URL>`.
+to `<URL>`. Pushing to a remote affects all defined pushurls or to all
+defined urls if no pushurls are defined. Fetch, however, will only
+fetch from the first defined url if muliple urls are defined.
Named file in `$GIT_DIR/remotes`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index b2580ab..c4c2d3e 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.39.0
+DEF_VER=v2.39.GIT
LF='
'
diff --git a/INSTALL b/INSTALL
index 3344788..4b42288 100644
--- a/INSTALL
+++ b/INSTALL
@@ -120,7 +120,7 @@
for everyday use (e.g. "bisect", "request-pull").
- "Perl" version 5.8 or later is needed to use some of the
- features (e.g. preparing a partial commit using "git add -i/-p",
+ features (e.g. sending patches using "git send-email",
interacting with svn repositories with "git svn"). If you can
live without these, use NO_PERL. Note that recent releases of
Redhat/Fedora are reported to ship Perl binary package with some
@@ -139,7 +139,7 @@
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/Makefile b/Makefile
index b258fdb..50ee51f 100644
--- a/Makefile
+++ b/Makefile
@@ -289,6 +289,10 @@
# Define NO_REGEX if your C library lacks regex support with REG_STARTEND
# feature.
#
+# Define USE_ENHANCED_BASIC_REGULAR_EXPRESSIONS if your C library provides
+# the flag REG_ENHANCED and you'd like to use it to enable enhanced basic
+# regular expressions.
+#
# Define HAVE_DEV_TTY if your system can open /dev/tty to interact with the
# user.
#
@@ -511,10 +515,8 @@
# Define BLK_SHA1 to make use of optimized C SHA-1 routines bundled
# with git (in the block-sha1/ directory).
#
-# Define NO_APPLE_COMMON_CRYPTO on OSX to opt-out of using the
-# "APPLE_COMMON_CRYPTO" backend for SHA-1, which is currently the
-# default on that OS. On macOS 01.4 (Tiger) or older,
-# NO_APPLE_COMMON_CRYPTO is defined by default.
+# Define APPLE_COMMON_CRYPTO_SHA1 to use Apple's CommonCrypto for
+# SHA-1.
#
# If don't enable any of the *_SHA1 settings in this section, Git will
# default to its built-in sha1collisiondetection library, which is a
@@ -691,7 +693,6 @@
# interactive shell sessions without exporting it.
unexport CDPATH
-SCRIPT_SH += git-bisect.sh
SCRIPT_SH += git-difftool--helper.sh
SCRIPT_SH += git-filter-branch.sh
SCRIPT_SH += git-merge-octopus.sh
@@ -707,7 +708,6 @@
SCRIPT_LIB += git-sh-i18n
SCRIPT_LIB += git-sh-setup
-SCRIPT_PERL += git-add--interactive.perl
SCRIPT_PERL += git-archimport.perl
SCRIPT_PERL += git-cvsexportcommit.perl
SCRIPT_PERL += git-cvsimport.perl
@@ -801,6 +801,7 @@
TEST_BUILTINS_OBJS += test-dump-fsmonitor.o
TEST_BUILTINS_OBJS += test-dump-split-index.o
TEST_BUILTINS_OBJS += test-dump-untracked-cache.o
+TEST_BUILTINS_OBJS += test-env-helper.o
TEST_BUILTINS_OBJS += test-example-decorate.o
TEST_BUILTINS_OBJS += test-fast-rebase.o
TEST_BUILTINS_OBJS += test-fsmonitor-client.o
@@ -1202,7 +1203,7 @@
BUILTIN_OBJS += builtin/annotate.o
BUILTIN_OBJS += builtin/apply.o
BUILTIN_OBJS += builtin/archive.o
-BUILTIN_OBJS += builtin/bisect--helper.o
+BUILTIN_OBJS += builtin/bisect.o
BUILTIN_OBJS += builtin/blame.o
BUILTIN_OBJS += builtin/branch.o
BUILTIN_OBJS += builtin/bugreport.o
@@ -1234,7 +1235,6 @@
BUILTIN_OBJS += builtin/diff-tree.o
BUILTIN_OBJS += builtin/diff.o
BUILTIN_OBJS += builtin/difftool.o
-BUILTIN_OBJS += builtin/env--helper.o
BUILTIN_OBJS += builtin/fast-export.o
BUILTIN_OBJS += builtin/fast-import.o
BUILTIN_OBJS += builtin/fetch-pack.o
@@ -1912,7 +1912,7 @@
BASIC_CFLAGS += -DNO_POSIX_GOODIES
endif
-ifdef APPLE_COMMON_CRYPTO
+ifdef APPLE_COMMON_CRYPTO_SHA1
# Apple CommonCrypto requires chunking
SHA1_MAX_BLOCK_SIZE = 1024L*1024L*1024L
endif
@@ -1929,7 +1929,7 @@
LIB_OBJS += block-sha1/sha1.o
BASIC_CFLAGS += -DSHA1_BLK
else
-ifdef APPLE_COMMON_CRYPTO
+ifdef APPLE_COMMON_CRYPTO_SHA1
COMPAT_CFLAGS += -DCOMMON_DIGEST_FOR_OPENSSL
BASIC_CFLAGS += -DSHA1_APPLE
else
@@ -2040,6 +2040,11 @@
ifdef NO_REGEX
COMPAT_CFLAGS += -Icompat/regex
COMPAT_OBJS += compat/regex/regex.o
+else
+ifdef USE_ENHANCED_BASIC_REGULAR_EXPRESSIONS
+ COMPAT_CFLAGS += -DUSE_ENHANCED_BASIC_REGULAR_EXPRESSIONS
+ COMPAT_OBJS += compat/regcomp_enhanced.o
+endif
endif
ifdef NATIVE_CRLF
BASIC_CFLAGS += -DNATIVE_CRLF
diff --git a/RelNotes b/RelNotes
index 7583683..47807bf 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.39.0.txt
\ No newline at end of file
+Documentation/RelNotes/2.40.0.txt
\ No newline at end of file
diff --git a/add-interactive.c b/add-interactive.c
index ae1839c..00a0f6f 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -724,7 +724,7 @@
}
static void revert_from_diff(struct diff_queue_struct *q,
- struct diff_options *opt, void *data)
+ struct diff_options *opt, void *data UNUSED)
{
int i, add_flags = ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE;
diff --git a/apply.c b/apply.c
index bc33814..5cc5479 100644
--- a/apply.c
+++ b/apply.c
@@ -2913,7 +2913,7 @@
break;
case ' ':
if (plen && (ws_rule & WS_BLANK_AT_EOF) &&
- ws_blank_line(patch + 1, plen, ws_rule))
+ ws_blank_line(patch + 1, plen))
is_blank_context = 1;
/* fallthrough */
case '-':
@@ -2942,7 +2942,7 @@
(first == '+' ? 0 : LINE_COMMON));
if (first == '+' &&
(ws_rule & WS_BLANK_AT_EOF) &&
- ws_blank_line(patch + 1, plen, ws_rule))
+ ws_blank_line(patch + 1, plen))
added_blank_line = 1;
break;
case '@': case '\\':
@@ -4105,7 +4105,7 @@
static int build_fake_ancestor(struct apply_state *state, struct patch *list)
{
struct patch *patch;
- struct index_state result = { NULL };
+ struct index_state result = INDEX_STATE_INIT(state->repo);
struct lock_file lock = LOCK_INIT;
int res;
@@ -4418,6 +4418,33 @@
if (state->cached)
return 0;
+ /*
+ * We already try to detect whether files are beyond a symlink in our
+ * up-front checks. But in the case where symlinks are created by any
+ * of the intermediate hunks it can happen that our up-front checks
+ * didn't yet see the symlink, but at the point of arriving here there
+ * in fact is one. We thus repeat the check for symlinks here.
+ *
+ * Note that this does not make the up-front check obsolete as the
+ * failure mode is different:
+ *
+ * - The up-front checks cause us to abort before we have written
+ * anything into the working directory. So when we exit this way the
+ * working directory remains clean.
+ *
+ * - The checks here happen in the middle of the action where we have
+ * already started to apply the patch. The end result will be a dirty
+ * working directory.
+ *
+ * Ideally, we should update the up-front checks to catch what would
+ * happen when we apply the patch before we damage the working tree.
+ * We have all the information necessary to do so. But for now, as a
+ * part of embargoed security work, having this check would serve as a
+ * reasonable first step.
+ */
+ if (path_is_beyond_symlink(state, path))
+ return error(_("affected file '%s' is beyond a symbolic link"), path);
+
res = try_create_file(state, path, mode, buf, size);
if (res < 0)
return -1;
diff --git a/archive.c b/archive.c
index 941495f..f2a8756 100644
--- a/archive.c
+++ b/archive.c
@@ -120,7 +120,7 @@
static struct attr_check *check;
if (!check)
check = attr_check_initl("export-ignore", "export-subst", NULL);
- git_check_attr(istate, path, check);
+ git_check_attr(istate, NULL, path, check);
return check;
}
@@ -710,6 +710,7 @@
string_list_clear_func(&args.extra_files, extra_file_info_clear);
free(args.refname);
+ clear_pathspec(&args.pathspec);
return rc;
}
diff --git a/attr.c b/attr.c
index 42ad6de..1053dfc 100644
--- a/attr.c
+++ b/attr.c
@@ -13,6 +13,8 @@
#include "dir.h"
#include "utf8.h"
#include "quote.h"
+#include "revision.h"
+#include "object-store.h"
#include "thread-utils.h"
const char git_attr__true[] = "(builtin)true";
@@ -24,7 +26,7 @@
#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 +208,7 @@
* 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 +224,8 @@
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 +270,7 @@
const struct git_attr *attr;
} u;
char is_macro;
- unsigned num_attr;
+ size_t num_attr;
struct attr_state state[FLEX_ARRAY];
};
@@ -289,7 +291,7 @@
struct attr_state *e)
{
const char *ep, *equals;
- int len;
+ size_t len;
ep = cp + strcspn(cp, blank);
equals = strchr(cp, '=');
@@ -333,8 +335,7 @@
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 +346,11 @@
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 +387,9 @@
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 +452,12 @@
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 ||
@@ -599,8 +605,7 @@
ret->nr = check->nr;
ret->alloc = check->alloc;
- ALLOC_ARRAY(ret->items, ret->nr);
- COPY_ARRAY(ret->items, check->items, ret->nr);
+ DUP_ARRAY(ret->items, check->items, ret->nr);
return ret;
}
@@ -660,8 +665,8 @@
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)
@@ -701,11 +706,12 @@
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);
@@ -717,26 +723,86 @@
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;
}
-static struct attr_stack *read_attr_from_index(struct index_state *istate,
- const char *path,
- unsigned flags)
+static struct attr_stack *read_attr_from_buf(char *buf, const char *path,
+ unsigned flags)
{
struct attr_stack *res;
- char *buf, *sp;
+ char *sp;
int lineno = 0;
+ if (!buf)
+ return NULL;
+
+ CALLOC_ARRAY(res, 1);
+ for (sp = buf; *sp;) {
+ char *ep;
+ int more;
+
+ ep = strchrnul(sp, '\n');
+ more = (*ep == '\n');
+ *ep = '\0';
+ handle_attr_line(res, sp, path, ++lineno, flags);
+ sp = ep + more;
+ }
+ free(buf);
+
+ return res;
+}
+
+static struct attr_stack *read_attr_from_blob(struct index_state *istate,
+ const struct object_id *tree_oid,
+ const char *path, unsigned flags)
+{
+ struct object_id oid;
+ unsigned long sz;
+ enum object_type type;
+ void *buf;
+ unsigned short mode;
+
+ if (!tree_oid)
+ return NULL;
+
+ if (get_tree_entry(istate->repo, tree_oid, path, &oid, &mode))
+ return NULL;
+
+ buf = repo_read_object_file(istate->repo, &oid, &type, &sz);
+ if (!buf || type != OBJ_BLOB) {
+ free(buf);
+ return NULL;
+ }
+
+ return read_attr_from_buf(buf, path, flags);
+}
+
+static struct attr_stack *read_attr_from_index(struct index_state *istate,
+ const char *path, unsigned flags)
+{
+ char *buf;
+ unsigned long size;
+
if (!istate)
return NULL;
@@ -754,32 +820,27 @@
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;
-
- CALLOC_ARRAY(res, 1);
- for (sp = buf; *sp; ) {
- char *ep;
- int more;
-
- ep = strchrnul(sp, '\n');
- more = (*ep == '\n');
- *ep = '\0';
- handle_attr_line(res, sp, path, ++lineno, flags);
- sp = ep + more;
+ if (size >= ATTR_MAX_FILE_SIZE) {
+ warning(_("ignoring overly large gitattributes blob '%s'"), path);
+ return NULL;
}
- free(buf);
- return res;
+
+ return read_attr_from_buf(buf, path, flags);
}
static struct attr_stack *read_attr(struct index_state *istate,
+ const struct object_id *tree_oid,
const char *path, unsigned flags)
{
struct attr_stack *res = NULL;
if (direction == GIT_ATTR_INDEX) {
res = read_attr_from_index(istate, path, flags);
+ } else if (tree_oid) {
+ res = read_attr_from_blob(istate, tree_oid, path, flags);
} else if (!is_bare_repository()) {
if (direction == GIT_ATTR_CHECKOUT) {
res = read_attr_from_index(istate, path, flags);
@@ -839,6 +900,7 @@
}
static void bootstrap_attr_stack(struct index_state *istate,
+ const struct object_id *tree_oid,
struct attr_stack **stack)
{
struct attr_stack *e;
@@ -864,7 +926,7 @@
}
/* root directory */
- e = read_attr(istate, GITATTRIBUTES_FILE, flags | READ_ATTR_NOFOLLOW);
+ e = read_attr(istate, tree_oid, GITATTRIBUTES_FILE, flags | READ_ATTR_NOFOLLOW);
push_stack(stack, e, xstrdup(""), 0);
/* info frame */
@@ -878,6 +940,7 @@
}
static void prepare_attr_stack(struct index_state *istate,
+ const struct object_id *tree_oid,
const char *path, int dirlen,
struct attr_stack **stack)
{
@@ -899,7 +962,7 @@
* .gitattributes in deeper directories to shallower ones,
* and finally use the built-in set as the default.
*/
- bootstrap_attr_stack(istate, stack);
+ bootstrap_attr_stack(istate, tree_oid, stack);
/*
* Pop the "info" one that is always at the top of the stack.
@@ -954,7 +1017,7 @@
strbuf_add(&pathbuf, path + pathbuf.len, (len - pathbuf.len));
strbuf_addf(&pathbuf, "/%s", GITATTRIBUTES_FILE);
- next = read_attr(istate, pathbuf.buf, READ_ATTR_NOFOLLOW);
+ next = read_attr(istate, tree_oid, pathbuf.buf, READ_ATTR_NOFOLLOW);
/* reset the pathbuf to not include "/.gitattributes" */
strbuf_setlen(&pathbuf, len);
@@ -999,12 +1062,12 @@
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;
@@ -1020,11 +1083,11 @@
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,
@@ -1055,11 +1118,11 @@
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;
}
@@ -1074,8 +1137,8 @@
* Otherwise all attributes are collected.
*/
static void collect_some_attrs(struct index_state *istate,
- const char *path,
- struct attr_check *check)
+ const struct object_id *tree_oid,
+ const char *path, struct attr_check *check)
{
int pathlen, rem, dirlen;
const char *cp, *last_slash = NULL;
@@ -1094,7 +1157,7 @@
dirlen = 0;
}
- prepare_attr_stack(istate, path, dirlen, &check->stack);
+ prepare_attr_stack(istate, tree_oid, path, dirlen, &check->stack);
all_attrs_init(&g_attr_hashmap, check);
determine_macros(check->all_attrs, check->stack);
@@ -1103,15 +1166,15 @@
}
void git_check_attr(struct index_state *istate,
- const char *path,
+ const struct object_id *tree_oid, const char *path,
struct attr_check *check)
{
int i;
- collect_some_attrs(istate, path, check);
+ collect_some_attrs(istate, tree_oid, path, check);
for (i = 0; i < check->nr; i++) {
- 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;
@@ -1119,13 +1182,13 @@
}
}
-void git_all_attrs(struct index_state *istate,
+void git_all_attrs(struct index_state *istate, const struct object_id *tree_oid,
const char *path, struct attr_check *check)
{
int i;
attr_check_reset(check);
- collect_some_attrs(istate, path, check);
+ collect_some_attrs(istate, tree_oid, path, check);
for (i = 0; i < check->all_attrs_nr; i++) {
const char *name = check->all_attrs[i].attr->name;
diff --git a/attr.h b/attr.h
index 3fb40cc..9884ea2 100644
--- a/attr.h
+++ b/attr.h
@@ -45,7 +45,7 @@
* const char *path;
*
* setup_check();
- * git_check_attr(path, check);
+ * git_check_attr(&the_index, tree_oid, path, check);
* ------------
*
* - Act on `.value` member of the result, left in `check->items[]`:
@@ -107,7 +107,20 @@
* - 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;
+struct object_id;
/**
* An attribute is an opaque object that is identified by its name. Pass the
@@ -190,13 +203,14 @@
const char *git_attr_name(const struct git_attr *);
void git_check_attr(struct index_state *istate,
- const char *path, struct attr_check *check);
+ const struct object_id *tree_oid, const char *path,
+ struct attr_check *check);
/*
* Retrieve all attributes that apply to the specified path.
* check holds the attributes and their values.
*/
-void git_all_attrs(struct index_state *istate,
+void git_all_attrs(struct index_state *istate, const struct object_id *tree_oid,
const char *path, struct attr_check *check);
enum git_attr_direction {
diff --git a/bisect.c b/bisect.c
index ec7487e..ef5ee5a 100644
--- a/bisect.c
+++ b/bisect.c
@@ -472,7 +472,6 @@
static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
-static GIT_PATH_FUNC(git_path_head_name, "head-name")
static void read_bisect_paths(struct strvec *array)
{
@@ -1188,8 +1187,6 @@
unlink_or_warn(git_path_bisect_run());
unlink_or_warn(git_path_bisect_terms());
unlink_or_warn(git_path_bisect_first_parent());
- /* Cleanup head-name if it got left by an old version of git-bisect */
- unlink_or_warn(git_path_head_name());
/*
* Cleanup BISECT_START last to support the --no-checkout option
* introduced in the commit 4796e823a.
diff --git a/blob.c b/blob.c
index 182718a..8f83523 100644
--- a/blob.c
+++ b/blob.c
@@ -13,8 +13,7 @@
return object_as_type(obj, OBJ_BLOB, 0);
}
-int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size)
+void parse_blob_buffer(struct blob *item)
{
item->object.parsed = 1;
- return 0;
}
diff --git a/blob.h b/blob.h
index 1664872..74555c9 100644
--- a/blob.h
+++ b/blob.h
@@ -11,8 +11,6 @@
struct blob *lookup_blob(struct repository *r, const struct object_id *oid);
-int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size);
-
/**
* Blobs do not contain references to other objects and do not have
* structured data that needs parsing. However, code may use the
@@ -21,5 +19,6 @@
* parse_blob_buffer() is used (by object.c) to flag that the object
* has been read successfully from the database.
**/
+void parse_blob_buffer(struct blob *item);
#endif /* BLOB_H */
diff --git a/branch.c b/branch.c
index d182756..e5614b5 100644
--- a/branch.c
+++ b/branch.c
@@ -756,7 +756,7 @@
_("submodule '%s': unable to find submodule"),
submodule_entry_list.entries[i].submodule->name);
if (advice_enabled(ADVICE_SUBMODULES_NOT_UPDATED))
- advise(_("You may try updating the submodules using 'git checkout %s && git submodule update --init'"),
+ advise(_("You may try updating the submodules using 'git checkout --no-recurse-submodules %s && git submodule update --init'"),
start_commitish);
exit(code);
}
diff --git a/builtin.h b/builtin.h
index 8901a34..46cc789 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
@@ -116,7 +112,7 @@
int cmd_annotate(int argc, const char **argv, const char *prefix);
int cmd_apply(int argc, const char **argv, const char *prefix);
int cmd_archive(int argc, const char **argv, const char *prefix);
-int cmd_bisect__helper(int argc, const char **argv, const char *prefix);
+int cmd_bisect(int argc, const char **argv, const char *prefix);
int cmd_blame(int argc, const char **argv, const char *prefix);
int cmd_branch(int argc, const char **argv, const char *prefix);
int cmd_bugreport(int argc, const char **argv, const char *prefix);
diff --git a/builtin/add.c b/builtin/add.c
index 76277df..61dd386 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -88,7 +88,7 @@
}
static void update_callback(struct diff_queue_struct *q,
- struct diff_options *opt, void *cbdata)
+ struct diff_options *opt UNUSED, void *cbdata)
{
int i;
struct update_callback_data *data = cbdata;
@@ -238,58 +238,14 @@
return ret;
}
-int run_add_interactive(const char *revision, const char *patch_mode,
- const struct pathspec *pathspec)
-{
- int i;
- struct child_process cmd = CHILD_PROCESS_INIT;
- int use_builtin_add_i =
- git_env_bool("GIT_TEST_ADD_I_USE_BUILTIN", -1);
-
- if (use_builtin_add_i < 0 &&
- git_config_get_bool("add.interactive.usebuiltin",
- &use_builtin_add_i))
- use_builtin_add_i = 1;
-
- if (use_builtin_add_i != 0) {
- enum add_p_mode mode;
-
- if (!patch_mode)
- return !!run_add_i(the_repository, pathspec);
-
- if (!strcmp(patch_mode, "--patch"))
- mode = ADD_P_ADD;
- else if (!strcmp(patch_mode, "--patch=stash"))
- mode = ADD_P_STASH;
- else if (!strcmp(patch_mode, "--patch=reset"))
- mode = ADD_P_RESET;
- else if (!strcmp(patch_mode, "--patch=checkout"))
- mode = ADD_P_CHECKOUT;
- else if (!strcmp(patch_mode, "--patch=worktree"))
- mode = ADD_P_WORKTREE;
- else
- die("'%s' not supported", patch_mode);
-
- return !!run_add_p(the_repository, mode, revision, pathspec);
- }
-
- strvec_push(&cmd.args, "add--interactive");
- if (patch_mode)
- strvec_push(&cmd.args, patch_mode);
- if (revision)
- strvec_push(&cmd.args, revision);
- strvec_push(&cmd.args, "--");
- for (i = 0; i < pathspec->nr; i++)
- /* pass original pathspec, to be re-parsed */
- strvec_push(&cmd.args, pathspec->items[i].original);
-
- cmd.git_cmd = 1;
- return run_command(&cmd);
-}
-
int interactive_add(const char **argv, const char *prefix, int patch)
{
struct pathspec pathspec;
+ int unused;
+
+ if (!git_config_get_bool("add.interactive.usebuiltin", &unused))
+ warning(_("the add.interactive.useBuiltin setting has been removed!\n"
+ "See its entry in 'git help config' for details."));
parse_pathspec(&pathspec, 0,
PATHSPEC_PREFER_FULL |
@@ -297,9 +253,10 @@
PATHSPEC_PREFIX_ORIGIN,
prefix, argv);
- return run_add_interactive(NULL,
- patch ? "--patch" : NULL,
- &pathspec);
+ if (patch)
+ return !!run_add_p(the_repository, ADD_P_ADD, NULL, &pathspec);
+ else
+ return !!run_add_i(the_repository, &pathspec);
}
static int edit_patch(int argc, const char **argv, const char *prefix)
@@ -695,6 +652,6 @@
die(_("Unable to write new index file"));
dir_clear(&dir);
- UNLEAK(pathspec);
+ clear_pathspec(&pathspec);
return exit_status;
}
diff --git a/builtin/am.c b/builtin/am.c
index 30c9b3a..e0848dd 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -3,7 +3,7 @@
*
* Based on git-am.sh by Junio C Hamano.
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "cache.h"
#include "config.h"
#include "builtin.h"
@@ -117,6 +117,7 @@
/* 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 int run_applypatch_msg_hook(struct am_state *state)
{
- int ret;
+ int ret = 0;
assert(state->msg);
- ret = run_hooks_l("applypatch-msg", am_path(state, "final-commit"), NULL);
+
+ if (!state->no_verify)
+ ret = run_hooks_l("applypatch-msg", am_path(state, "final-commit"), NULL);
if (!ret) {
FREE_AND_NULL(state->msg);
@@ -492,24 +495,12 @@
*/
static int run_post_rewrite_hook(const struct am_state *state)
{
- struct child_process cp = CHILD_PROCESS_INIT;
- const char *hook = find_hook("post-rewrite");
- int ret;
+ struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
- if (!hook)
- return 0;
+ strvec_push(&opt.args, "rebase");
+ opt.path_to_stdin = am_path(state, "rewritten");
- strvec_push(&cp.args, hook);
- strvec_push(&cp.args, "rebase");
-
- cp.in = xopen(am_path(state, "rewritten"), O_RDONLY);
- cp.stdout_to_stderr = 1;
- cp.trace2_hook_name = "post-rewrite";
-
- ret = run_command(&cp);
-
- close(cp.in);
- return ret;
+ return run_hooks_opt("post-rewrite", &opt);
}
/**
@@ -1476,6 +1467,7 @@
int res, opts_left;
int force_apply = 0;
int options = 0;
+ const char **apply_argv;
if (init_apply_state(&apply_state, the_repository, NULL))
BUG("init_apply_state() failed");
@@ -1483,7 +1475,14 @@
strvec_push(&apply_opts, "apply");
strvec_pushv(&apply_opts, state->git_apply_opts.v);
- opts_left = apply_parse_options(apply_opts.nr, apply_opts.v,
+ /*
+ * Build a copy that apply_parse_options() can rearrange.
+ * apply_opts.v keeps referencing the allocated strings for
+ * strvec_clear() to release.
+ */
+ DUP_ARRAY(apply_argv, apply_opts.v, apply_opts.nr);
+
+ opts_left = apply_parse_options(apply_opts.nr, apply_argv,
&apply_state, &force_apply, &options,
NULL);
@@ -1513,6 +1512,7 @@
strvec_clear(&apply_paths);
strvec_clear(&apply_opts);
clear_apply_state(&apply_state);
+ free(apply_argv);
if (res)
return res;
@@ -1640,10 +1640,10 @@
const char *reflog_msg, *author, *committer = NULL;
struct strbuf sb = STRBUF_INIT;
- if (run_hooks("pre-applypatch"))
+ if (!state->no_verify && run_hooks("pre-applypatch"))
exit(1);
- if (write_cache_as_tree(&tree, 0, NULL))
+ if (write_index_as_tree(&tree, &the_index, get_index_file(), 0, NULL))
die(_("git write-tree failed to write a tree"));
if (!get_oid_commit("HEAD", &parent)) {
@@ -2051,7 +2051,7 @@
if (fast_forward_to(head_tree, head_tree, 1))
return -1;
- if (write_cache_as_tree(&index, 0, NULL))
+ if (write_index_as_tree(&index, &the_index, get_index_file(), 0, NULL))
return -1;
index_tree = parse_tree_indirect(&index);
@@ -2330,6 +2330,8 @@
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/bisect--helper.c b/builtin/bisect.c
similarity index 90%
rename from builtin/bisect--helper.c
rename to builtin/bisect.c
index 6e41cbd..7301740 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect.c
@@ -15,23 +15,44 @@
static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
-static GIT_PATH_FUNC(git_path_head_name, "head-name")
static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
-static const char * const git_bisect_helper_usage[] = {
- N_("git bisect--helper --bisect-reset [<commit>]"),
- "git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]",
- N_("git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}=<term>]"
- " [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]"),
- "git bisect--helper --bisect-next",
- N_("git bisect--helper --bisect-state (bad|new) [<rev>]"),
- N_("git bisect--helper --bisect-state (good|old) [<rev>...]"),
- N_("git bisect--helper --bisect-replay <filename>"),
- N_("git bisect--helper --bisect-skip [(<rev>|<range>)...]"),
- "git bisect--helper --bisect-visualize",
- N_("git bisect--helper --bisect-run <cmd>..."),
+#define BUILTIN_GIT_BISECT_START_USAGE \
+ N_("git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]" \
+ " [--no-checkout] [--first-parent] [<bad> [<good>...]] [--]" \
+ " [<pathspec>...]")
+#define BUILTIN_GIT_BISECT_STATE_USAGE \
+ N_("git bisect (good|bad) [<rev>...]")
+#define BUILTIN_GIT_BISECT_TERMS_USAGE \
+ "git bisect terms [--term-good | --term-bad]"
+#define BUILTIN_GIT_BISECT_SKIP_USAGE \
+ N_("git bisect skip [(<rev>|<range>)...]")
+#define BUILTIN_GIT_BISECT_NEXT_USAGE \
+ "git bisect next"
+#define BUILTIN_GIT_BISECT_RESET_USAGE \
+ N_("git bisect reset [<commit>]")
+#define BUILTIN_GIT_BISECT_VISUALIZE_USAGE \
+ "git bisect visualize"
+#define BUILTIN_GIT_BISECT_REPLAY_USAGE \
+ N_("git bisect replay <logfile>")
+#define BUILTIN_GIT_BISECT_LOG_USAGE \
+ "git bisect log"
+#define BUILTIN_GIT_BISECT_RUN_USAGE \
+ N_("git bisect run <cmd>...")
+
+static const char * const git_bisect_usage[] = {
+ BUILTIN_GIT_BISECT_START_USAGE,
+ BUILTIN_GIT_BISECT_STATE_USAGE,
+ BUILTIN_GIT_BISECT_TERMS_USAGE,
+ BUILTIN_GIT_BISECT_SKIP_USAGE,
+ BUILTIN_GIT_BISECT_NEXT_USAGE,
+ BUILTIN_GIT_BISECT_RESET_USAGE,
+ BUILTIN_GIT_BISECT_VISUALIZE_USAGE,
+ BUILTIN_GIT_BISECT_REPLAY_USAGE,
+ BUILTIN_GIT_BISECT_LOG_USAGE,
+ BUILTIN_GIT_BISECT_RUN_USAGE,
NULL
};
@@ -656,7 +677,8 @@
return bisect_next(terms, prefix);
}
-static enum bisect_error bisect_start(struct bisect_terms *terms, const char **argv, int argc)
+static enum bisect_error bisect_start(struct bisect_terms *terms, int argc,
+ const char **argv)
{
int no_checkout = 0;
int first_parent_only = 0;
@@ -785,13 +807,6 @@
strbuf_addstr(&start_head, oid_to_hex(&head_oid));
} else if (!get_oid(head, &head_oid) &&
skip_prefix(head, "refs/heads/", &head)) {
- /*
- * This error message should only be triggered by
- * cogito usage, and cogito users should understand
- * it relates to cg-seek.
- */
- if (!is_empty_or_missing_file(git_path_head_name()))
- return error(_("won't bisect on cg-seek'ed tree"));
strbuf_addstr(&start_head, head);
} else {
return error(_("bad HEAD - strange symbolic ref"));
@@ -886,13 +901,13 @@
yesno = git_prompt(_("Do you want me to do it for you "
"[Y/n]? "), PROMPT_ECHO);
res = tolower(*yesno) == 'n' ?
- -1 : bisect_start(terms, empty_strvec, 0);
+ -1 : bisect_start(terms, 0, empty_strvec);
return res;
}
-static enum bisect_error bisect_state(struct bisect_terms *terms, const char **argv,
- int argc)
+static enum bisect_error bisect_state(struct bisect_terms *terms, int argc,
+ const char **argv)
{
const char *state;
int i, verify_expected = 1;
@@ -1011,7 +1026,7 @@
struct strvec argv = STRVEC_INIT;
int res;
sq_dequote_to_strvec(rev, &argv);
- res = bisect_start(terms, argv.v, argv.nr);
+ res = bisect_start(terms, argv.nr, argv.v);
strvec_clear(&argv);
return res;
}
@@ -1061,7 +1076,8 @@
return bisect_auto_next(terms, NULL);
}
-static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **argv, int argc)
+static enum bisect_error bisect_skip(struct bisect_terms *terms, int argc,
+ const char **argv)
{
int i;
enum bisect_error res;
@@ -1091,13 +1107,14 @@
strvec_push(&argv_state, argv[i]);
}
}
- res = bisect_state(terms, argv_state.v, argv_state.nr);
+ res = bisect_state(terms, argv_state.nr, argv_state.v);
strvec_clear(&argv_state);
return res;
}
-static int bisect_visualize(struct bisect_terms *terms, const char **argv, int argc)
+static int bisect_visualize(struct bisect_terms *terms, int argc,
+ const char **argv)
{
struct child_process cmd = CHILD_PROCESS_INIT;
struct strbuf sb = STRBUF_INIT;
@@ -1180,7 +1197,7 @@
return rc;
}
-static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
+static int bisect_run(struct bisect_terms *terms, int argc, const char **argv)
{
int res = BISECT_OK;
struct strbuf command = STRBUF_INIT;
@@ -1191,13 +1208,13 @@
if (bisect_next_check(terms, NULL))
return BISECT_FAILED;
- if (argc)
- sq_quote_argv(&command, argv);
- else {
+ if (!argc) {
error(_("bisect run failed: no command provided."));
return BISECT_FAILED;
}
+ sq_quote_argv(&command, argv);
+ strbuf_ltrim(&command);
while (1) {
res = do_bisect_run(command.buf);
@@ -1211,8 +1228,8 @@
if (is_first_run && (res == 126 || res == 127)) {
int rc = verify_good(terms, command.buf);
is_first_run = 0;
- if (rc < 0) {
- error(_("unable to verify '%s' on good"
+ if (rc < 0 || 128 <= rc) {
+ error(_("unable to verify %s on good"
" revision"), command.buf);
res = BISECT_FAILED;
break;
@@ -1227,7 +1244,7 @@
if (res < 0 || 128 <= res) {
error(_("bisect run failed: exit code %d from"
- " '%s' is < 0 or >= 128"), res, command.buf);
+ " %s is < 0 or >= 128"), res, command.buf);
break;
}
@@ -1249,7 +1266,7 @@
saved_stdout = dup(1);
dup2(temporary_stdout_fd, 1);
- res = bisect_state(terms, &new_state, 1);
+ res = bisect_state(terms, 1, &new_state);
fflush(stdout);
dup2(saved_stdout, 1);
@@ -1261,14 +1278,14 @@
if (res == BISECT_ONLY_SKIPPED_LEFT)
error(_("bisect run cannot continue any more"));
else if (res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) {
- printf(_("bisect run success"));
+ puts(_("bisect run success"));
res = BISECT_OK;
} else if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) {
- printf(_("bisect found first bad commit"));
+ puts(_("bisect found first bad commit"));
res = BISECT_OK;
} else if (res) {
- error(_("bisect run failed: 'git bisect--helper --bisect-state"
- " %s' exited with error code %d"), new_state, res);
+ error(_("bisect run failed: 'git bisect %s'"
+ " exited with error code %d"), new_state, res);
} else {
continue;
}
@@ -1282,7 +1299,8 @@
static int cmd_bisect__reset(int argc, const char **argv, const char *prefix UNUSED)
{
if (argc > 1)
- return error(_("--bisect-reset requires either no argument or a commit"));
+ return error(_("'%s' requires either no argument or a commit"),
+ "git bisect reset");
return bisect_reset(argc ? argv[0] : NULL);
}
@@ -1292,7 +1310,8 @@
struct bisect_terms terms = { 0 };
if (argc > 1)
- return error(_("--bisect-terms requires 0 or 1 argument"));
+ return error(_("'%s' requires 0 or 1 argument"),
+ "git bisect terms");
res = bisect_terms(&terms, argc == 1 ? argv[0] : NULL);
free_terms(&terms);
return res;
@@ -1304,7 +1323,7 @@
struct bisect_terms terms = { 0 };
set_terms(&terms, "bad", "good");
- res = bisect_start(&terms, argv, argc);
+ res = bisect_start(&terms, argc, argv);
free_terms(&terms);
return res;
}
@@ -1315,29 +1334,16 @@
struct bisect_terms terms = { 0 };
if (argc)
- return error(_("--bisect-next requires 0 arguments"));
+ return error(_("'%s' requires 0 arguments"),
+ "git bisect next");
get_terms(&terms);
res = bisect_next(&terms, prefix);
free_terms(&terms);
return res;
}
-static int cmd_bisect__state(int argc, const char **argv, const char *prefix UNUSED)
+static int cmd_bisect__log(int argc UNUSED, const char **argv UNUSED, const char *prefix UNUSED)
{
- int res;
- struct bisect_terms terms = { 0 };
-
- set_terms(&terms, "bad", "good");
- get_terms(&terms);
- res = bisect_state(&terms, argv, argc);
- free_terms(&terms);
- return res;
-}
-
-static int cmd_bisect__log(int argc, const char **argv UNUSED, const char *prefix UNUSED)
-{
- if (argc)
- return error(_("--bisect-log requires 0 arguments"));
return bisect_log();
}
@@ -1361,7 +1367,7 @@
set_terms(&terms, "bad", "good");
get_terms(&terms);
- res = bisect_skip(&terms, argv, argc);
+ res = bisect_skip(&terms, argc, argv);
free_terms(&terms);
return res;
}
@@ -1372,7 +1378,7 @@
struct bisect_terms terms = { 0 };
get_terms(&terms);
- res = bisect_visualize(&terms, argv, argc);
+ res = bisect_visualize(&terms, argc, argv);
free_terms(&terms);
return res;
}
@@ -1383,14 +1389,14 @@
struct bisect_terms terms = { 0 };
if (!argc)
- return error(_("bisect run failed: no command provided."));
+ return error(_("'%s' failed: no command provided."), "git bisect run");
get_terms(&terms);
- res = bisect_run(&terms, argv, argc);
+ res = bisect_run(&terms, argc, argv);
free_terms(&terms);
return res;
}
-int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
+int cmd_bisect(int argc, const char **argv, const char *prefix)
{
int res = 0;
parse_opt_subcommand_fn *fn = NULL;
@@ -1399,7 +1405,6 @@
OPT_SUBCOMMAND("terms", &fn, cmd_bisect__terms),
OPT_SUBCOMMAND("start", &fn, cmd_bisect__start),
OPT_SUBCOMMAND("next", &fn, cmd_bisect__next),
- OPT_SUBCOMMAND("state", &fn, cmd_bisect__state),
OPT_SUBCOMMAND("log", &fn, cmd_bisect__log),
OPT_SUBCOMMAND("replay", &fn, cmd_bisect__replay),
OPT_SUBCOMMAND("skip", &fn, cmd_bisect__skip),
@@ -1408,22 +1413,27 @@
OPT_SUBCOMMAND("run", &fn, cmd_bisect__run),
OPT_END()
};
- argc = parse_options(argc, argv, prefix, options,
- git_bisect_helper_usage, 0);
+ argc = parse_options(argc, argv, prefix, options, git_bisect_usage,
+ PARSE_OPT_SUBCOMMAND_OPTIONAL);
- if (!fn)
- usage_with_options(git_bisect_helper_usage, options);
- argc--;
- argv++;
+ if (!fn) {
+ struct bisect_terms terms = { 0 };
- res = fn(argc, argv, prefix);
+ if (!argc)
+ usage_msg_opt(_("need a command"), git_bisect_usage, options);
- /*
- * Handle early success
- * From check_merge_bases > check_good_are_ancestors_of_bad > bisect_next_all
- */
- if ((res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) || (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND))
- res = BISECT_OK;
+ set_terms(&terms, "bad", "good");
+ get_terms(&terms);
+ if (check_and_set_terms(&terms, argv[0]))
+ usage_msg_optf(_("unknown command: '%s'"), git_bisect_usage,
+ options, argv[0]);
+ res = bisect_state(&terms, argc, argv);
+ free_terms(&terms);
+ } else {
+ argc--;
+ argv++;
+ res = fn(argc, argv, prefix);
+ }
- return -res;
+ return is_bisect_success(res) ? 0 : -res;
}
diff --git a/builtin/branch.c b/builtin/branch.c
index 9470c98..f63fd45 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -581,13 +581,13 @@
strbuf_release(&logmsg);
strbuf_addf(&oldsection, "branch.%s", interpreted_oldname);
- strbuf_release(&oldref);
strbuf_addf(&newsection, "branch.%s", interpreted_newname);
- strbuf_release(&newref);
if (!copy && git_config_rename_section(oldsection.buf, newsection.buf) < 0)
die(_("Branch is renamed, but update of config-file failed"));
- if (copy && strcmp(oldname, newname) && git_config_copy_section(oldsection.buf, newsection.buf) < 0)
+ if (copy && strcmp(interpreted_oldname, interpreted_newname) && git_config_copy_section(oldsection.buf, newsection.buf) < 0)
die(_("Branch is copied, but update of config-file failed"));
+ strbuf_release(&oldref);
+ strbuf_release(&newref);
strbuf_release(&oldsection);
strbuf_release(&newsection);
}
diff --git a/builtin/bugreport.c b/builtin/bugreport.c
index 9605254..5bc254b 100644
--- a/builtin/bugreport.c
+++ b/builtin/bugreport.c
@@ -106,6 +106,7 @@
const char *user_relative_path = NULL;
char *prefixed_filename;
size_t output_path_len;
+ int ret;
const struct option bugreport_options[] = {
OPT_CALLBACK_F(0, "diagnose", &diagnose, N_("mode"),
@@ -182,7 +183,9 @@
user_relative_path);
free(prefixed_filename);
- UNLEAK(buffer);
- UNLEAK(report_path);
- return !!launch_editor(report_path.buf, NULL, NULL);
+ strbuf_release(&buffer);
+
+ ret = !!launch_editor(report_path.buf, NULL, NULL);
+ strbuf_release(&report_path);
+ return ret;
}
diff --git a/builtin/bundle.c b/builtin/bundle.c
index c12c09f..acceef6 100644
--- a/builtin/bundle.c
+++ b/builtin/bundle.c
@@ -55,13 +55,12 @@
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 b3be58b..cc17635 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -132,8 +132,21 @@
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 @@
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 @@
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/check-attr.c b/builtin/check-attr.c
index 0fef10e..d7a40e6 100644
--- a/builtin/check-attr.c
+++ b/builtin/check-attr.c
@@ -9,9 +9,10 @@
static int all_attrs;
static int cached_attrs;
static int stdin_paths;
+static char *source;
static const char * const check_attr_usage[] = {
-N_("git check-attr [-a | --all | <attr>...] [--] <pathname>..."),
-N_("git check-attr --stdin [-z] [-a | --all | <attr>...]"),
+N_("git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] <pathname>..."),
+N_("git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"),
NULL
};
@@ -23,6 +24,7 @@
OPT_BOOL(0 , "stdin", &stdin_paths, N_("read file names from stdin")),
OPT_BOOL('z', NULL, &nul_term_line,
N_("terminate input and output records by a NUL character")),
+ OPT_STRING(0, "source", &source, N_("<tree-ish>"), N_("which tree-ish to check attributes at")),
OPT_END()
};
@@ -55,27 +57,26 @@
}
}
-static void check_attr(const char *prefix,
- struct attr_check *check,
- int collect_all,
+static void check_attr(const char *prefix, struct attr_check *check,
+ const struct object_id *tree_oid, int collect_all,
const char *file)
+
{
char *full_path =
prefix_path(prefix, prefix ? strlen(prefix) : 0, file);
if (collect_all) {
- git_all_attrs(&the_index, full_path, check);
+ git_all_attrs(&the_index, tree_oid, full_path, check);
} else {
- git_check_attr(&the_index, full_path, check);
+ git_check_attr(&the_index, tree_oid, full_path, check);
}
output_attr(check, file);
free(full_path);
}
-static void check_attr_stdin_paths(const char *prefix,
- struct attr_check *check,
- int collect_all)
+static void check_attr_stdin_paths(const char *prefix, struct attr_check *check,
+ const struct object_id *tree_oid, int collect_all)
{
struct strbuf buf = STRBUF_INIT;
struct strbuf unquoted = STRBUF_INIT;
@@ -89,7 +90,7 @@
die("line is badly quoted");
strbuf_swap(&buf, &unquoted);
}
- check_attr(prefix, check, collect_all, buf.buf);
+ check_attr(prefix, check, tree_oid, collect_all, buf.buf);
maybe_flush_or_die(stdout, "attribute to stdout");
}
strbuf_release(&buf);
@@ -105,6 +106,8 @@
int cmd_check_attr(int argc, const char **argv, const char *prefix)
{
struct attr_check *check;
+ struct object_id *tree_oid = NULL;
+ struct object_id initialized_oid;
int cnt, i, doubledash, filei;
if (!is_bare_repository())
@@ -176,11 +179,17 @@
}
}
+ if (source) {
+ if (repo_get_oid_tree(the_repository, source, &initialized_oid))
+ die("%s: not a valid tree-ish source", source);
+ tree_oid = &initialized_oid;
+ }
+
if (stdin_paths)
- check_attr_stdin_paths(prefix, check, all_attrs);
+ check_attr_stdin_paths(prefix, check, tree_oid, all_attrs);
else {
for (i = filei; i < argc; i++)
- check_attr(prefix, check, all_attrs, argv[i]);
+ check_attr(prefix, check, tree_oid, all_attrs, argv[i]);
maybe_flush_or_die(stdout, "attribute to stdout");
}
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 3fa29a0..a5155cf 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -29,6 +29,7 @@
#include "xdiff-interface.h"
#include "entry.h"
#include "parallel-checkout.h"
+#include "add-interactive.h"
static const char * const checkout_usage[] = {
N_("git checkout [<options>] <branch>"),
@@ -232,7 +233,7 @@
pos++;
}
if (!overlay_mode) {
- unlink_entry(ce);
+ unlink_entry(ce, NULL);
return 0;
}
if (stage == 2)
@@ -499,7 +500,7 @@
"--merge", "--conflict", "--staged");
if (opts->patch_mode) {
- const char *patch_mode;
+ enum add_p_mode patch_mode;
const char *rev = new_branch_info->name;
char rev_oid[GIT_MAX_HEXSZ + 1];
@@ -517,15 +518,16 @@
rev = oid_to_hex_r(rev_oid, &new_branch_info->commit->object.oid);
if (opts->checkout_index && opts->checkout_worktree)
- patch_mode = "--patch=checkout";
+ patch_mode = ADD_P_CHECKOUT;
else if (opts->checkout_index && !opts->checkout_worktree)
- patch_mode = "--patch=reset";
+ patch_mode = ADD_P_RESET;
else if (!opts->checkout_index && opts->checkout_worktree)
- patch_mode = "--patch=worktree";
+ patch_mode = ADD_P_WORKTREE;
else
BUG("either flag must have been set, worktree=%d, index=%d",
opts->checkout_worktree, opts->checkout_index);
- return run_add_interactive(rev, patch_mode, &opts->pathspec);
+ return !!run_add_p(the_repository, patch_mode, rev,
+ &opts->pathspec);
}
repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
@@ -1270,7 +1272,7 @@
* between A and B, A...B names that merge base.
*
* (b) If <something> is _not_ a commit, either "--" is present
- * or <something> is not a path, no -t or -b was given, and
+ * or <something> is not a path, no -t or -b was given,
* and there is a tracking branch whose name is <something>
* in one and only one remote (or if the branch exists on the
* remote named in checkout.defaultRemote), then this is a
@@ -1471,6 +1473,8 @@
"or \"git worktree add\"."));
if (state.bisect_in_progress)
warning(_("you are switching branch while bisecting"));
+
+ wt_status_state_free_buffers(&state);
}
static int checkout_branch(struct checkout_opts *opts,
diff --git a/builtin/clean.c b/builtin/clean.c
index b2701a2..10aaa8c 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -560,7 +560,7 @@
/*
* Implement a git-add-interactive compatible UI, which is borrowed
- * from git-add--interactive.perl.
+ * from add-interactive.c.
*
* Return value:
*
@@ -1092,5 +1092,6 @@
strbuf_release(&buf);
string_list_clear(&del_list, 0);
string_list_clear(&exclude_list, 0);
+ clear_pathspec(&pathspec);
return (errors != 0);
}
diff --git a/builtin/clone.c b/builtin/clone.c
index f518bb2..65b5b7d 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -892,6 +892,7 @@
int is_bundle = 0, is_local;
int reject_shallow = 0;
const char *repo_name, *repo, *work_tree, *git_dir;
+ char *repo_to_free = NULL;
char *path = NULL, *dir, *display_repo = NULL;
int dest_exists, real_dest_exists = 0;
const struct ref *refs, *remote_head;
@@ -949,7 +950,7 @@
path = get_repo_path(repo_name, &is_bundle);
if (path) {
FREE_AND_NULL(path);
- repo = absolute_pathdup(repo_name);
+ repo = repo_to_free = absolute_pathdup(repo_name);
} else if (strchr(repo_name, ':')) {
repo = repo_name;
display_repo = transport_anonymize_url(repo);
@@ -1170,10 +1171,6 @@
refspec_appendf(&remote->fetch, "+%s*:%s*", src_ref_prefix,
branch_top.buf);
- transport = transport_get(remote, remote->url[0]);
- transport_set_verbosity(transport, option_verbosity, option_progress);
- transport->family = family;
-
path = get_repo_path(remote->url[0], &is_bundle);
is_local = option_local != 0 && path && !is_bundle;
if (is_local) {
@@ -1195,6 +1192,10 @@
}
if (option_local > 0 && !is_local)
warning(_("--local is ignored"));
+
+ transport = transport_get(remote, path ? path : remote->url[0]);
+ transport_set_verbosity(transport, option_verbosity, option_progress);
+ transport->family = family;
transport->cloning = 1;
if (is_bundle) {
@@ -1248,12 +1249,16 @@
* data from the --bundle-uri option.
*/
if (bundle_uri) {
+ int has_heuristic = 0;
+
/* At this point, we need the_repository to match the cloned repo. */
if (repo_init(the_repository, git_dir, work_tree))
warning(_("failed to initialize the repo, skipping bundle URI"));
- else if (fetch_bundle_uri(the_repository, bundle_uri))
+ else if (fetch_bundle_uri(the_repository, bundle_uri, &has_heuristic))
warning(_("failed to fetch objects from bundle URI '%s'"),
bundle_uri);
+ else if (has_heuristic)
+ git_config_set_gently("fetch.bundleuri", bundle_uri);
}
strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD");
@@ -1271,6 +1276,27 @@
if (refs)
mapped_refs = wanted_peer_refs(refs, &remote->fetch);
+ if (!bundle_uri) {
+ /*
+ * Populate transport->got_remote_bundle_uri and
+ * transport->bundle_uri. We might get nothing.
+ */
+ transport_get_remote_bundle_uri(transport);
+
+ if (transport->bundles &&
+ hashmap_get_size(&transport->bundles->bundles)) {
+ /* At this point, we need the_repository to match the cloned repo. */
+ if (repo_init(the_repository, git_dir, work_tree))
+ warning(_("failed to initialize the repo, skipping bundle URI"));
+ else if (fetch_bundle_list(the_repository,
+ transport->bundles))
+ warning(_("failed to fetch advertised bundles"));
+ } else {
+ clear_bundle_list(transport->bundles);
+ FREE_AND_NULL(transport->bundles);
+ }
+ }
+
if (mapped_refs) {
int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
@@ -1392,7 +1418,7 @@
free(unborn_head);
free(dir);
free(path);
- UNLEAK(repo);
+ free(repo_to_free);
junk_mode = JUNK_LEAVE_ALL;
transport_ls_refs_options_release(&transport_ls_refs_options);
diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
index e8f77f5..93704f9 100644
--- a/builtin/commit-graph.c
+++ b/builtin/commit-graph.c
@@ -67,6 +67,7 @@
int fd;
struct stat st;
int flags = 0;
+ int ret;
static struct option builtin_commit_graph_verify_options[] = {
OPT_BOOL(0, "shallow", &opts.shallow,
@@ -111,8 +112,9 @@
if (!graph)
return !!open_ok;
- UNLEAK(graph);
- return verify_commit_graph(the_repository, graph, flags);
+ ret = verify_commit_graph(the_repository, graph, flags);
+ free_commit_graph(graph);
+ return ret;
}
extern int read_replace_refs;
@@ -267,8 +269,8 @@
if (opts.reachable) {
if (write_commit_graph_reachable(odb, flags, &write_opts))
- return 1;
- return 0;
+ result = 1;
+ goto cleanup;
}
if (opts.stdin_packs) {
diff --git a/builtin/commit.c b/builtin/commit.c
index 06b1330..985a044 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -5,7 +5,7 @@
* Based on git-commit.sh by Junio C Hamano and Linus Torvalds
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "cache.h"
#include "config.h"
#include "lockfile.h"
@@ -414,7 +414,7 @@
discard_index(&the_index);
read_index_from(&the_index, get_lock_file_path(&index_lock),
get_git_dir());
- if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) {
+ if (cache_tree_update(&the_index, WRITE_TREE_SILENT) == 0) {
if (reopen_lock_file(&index_lock) < 0)
die(_("unable to write index file"));
if (write_locked_index(&the_index, &index_lock, 0))
@@ -444,7 +444,7 @@
LOCK_DIE_ON_ERROR);
add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
refresh_cache_or_die(refresh_flags);
- update_main_cache_tree(WRITE_TREE_SILENT);
+ cache_tree_update(&the_index, WRITE_TREE_SILENT);
if (write_locked_index(&the_index, &index_lock, 0))
die(_("unable to write new_index file"));
commit_style = COMMIT_NORMAL;
@@ -467,7 +467,7 @@
refresh_cache_or_die(refresh_flags);
if (the_index.cache_changed
|| !cache_tree_fully_valid(the_index.cache_tree))
- update_main_cache_tree(WRITE_TREE_SILENT);
+ cache_tree_update(&the_index, WRITE_TREE_SILENT);
if (write_locked_index(&the_index, &index_lock,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("unable to write new_index file"));
@@ -516,7 +516,7 @@
repo_hold_locked_index(the_repository, &index_lock, LOCK_DIE_ON_ERROR);
add_remove_files(&partial);
refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
- update_main_cache_tree(WRITE_TREE_SILENT);
+ cache_tree_update(&the_index, WRITE_TREE_SILENT);
if (write_locked_index(&the_index, &index_lock, 0))
die(_("unable to write new_index file"));
@@ -991,8 +991,11 @@
struct object_id oid;
const char *parent = "HEAD";
- if (!active_nr && read_cache() < 0)
- die(_("Cannot read index"));
+ if (!the_index.cache_nr) {
+ discard_index(&the_index);
+ if (repo_read_index(the_repository) < 0)
+ die(_("Cannot read index"));
+ }
if (amend)
parent = "HEAD^1";
@@ -1076,7 +1079,7 @@
}
read_index_from(&the_index, index_file, get_git_dir());
- if (update_main_cache_tree(0)) {
+ if (cache_tree_update(&the_index, 0)) {
error(_("Error building trees"));
return 0;
}
@@ -1875,8 +1878,8 @@
apply_autostash(git_path_merge_autostash(the_repository));
cleanup:
- UNLEAK(author_ident);
- UNLEAK(err);
- UNLEAK(sb);
+ strbuf_release(&author_ident);
+ strbuf_release(&err);
+ strbuf_release(&sb);
return ret;
}
diff --git a/builtin/config.c b/builtin/config.c
index 753e5fa..060cf9f 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -639,8 +639,9 @@
int cmd_config(int argc, const char **argv, const char *prefix)
{
int nongit = !startup_info->have_repository;
- char *value;
+ char *value = NULL;
int flags = 0;
+ int ret = 0;
given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
@@ -856,44 +857,38 @@
free(config_file);
}
else if (actions == ACTION_SET) {
- int ret;
check_write();
check_argc(argc, 2, 2);
value = normalize_value(argv[0], argv[1]);
- UNLEAK(value);
ret = git_config_set_in_file_gently(given_config_source.file, argv[0], value);
if (ret == CONFIG_NOTHING_SET)
error(_("cannot overwrite multiple values with a single value\n"
" Use a regexp, --add or --replace-all to change %s."), argv[0]);
- return ret;
}
else if (actions == ACTION_SET_ALL) {
check_write();
check_argc(argc, 2, 3);
value = normalize_value(argv[0], argv[1]);
- UNLEAK(value);
- return git_config_set_multivar_in_file_gently(given_config_source.file,
- argv[0], value, argv[2],
- flags);
+ ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+ argv[0], value, argv[2],
+ flags);
}
else if (actions == ACTION_ADD) {
check_write();
check_argc(argc, 2, 2);
value = normalize_value(argv[0], argv[1]);
- UNLEAK(value);
- return git_config_set_multivar_in_file_gently(given_config_source.file,
- argv[0], value,
- CONFIG_REGEX_NONE,
- flags);
+ ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+ argv[0], value,
+ CONFIG_REGEX_NONE,
+ flags);
}
else if (actions == ACTION_REPLACE_ALL) {
check_write();
check_argc(argc, 2, 3);
value = normalize_value(argv[0], argv[1]);
- UNLEAK(value);
- return git_config_set_multivar_in_file_gently(given_config_source.file,
- argv[0], value, argv[2],
- flags | CONFIG_FLAGS_MULTI_REPLACE);
+ ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+ argv[0], value, argv[2],
+ flags | CONFIG_FLAGS_MULTI_REPLACE);
}
else if (actions == ACTION_GET) {
check_argc(argc, 1, 2);
@@ -934,26 +929,28 @@
flags | CONFIG_FLAGS_MULTI_REPLACE);
}
else if (actions == ACTION_RENAME_SECTION) {
- int ret;
check_write();
check_argc(argc, 2, 2);
ret = git_config_rename_section_in_file(given_config_source.file,
argv[0], argv[1]);
if (ret < 0)
return ret;
- if (ret == 0)
+ else if (!ret)
die(_("no such section: %s"), argv[0]);
+ else
+ ret = 0;
}
else if (actions == ACTION_REMOVE_SECTION) {
- int ret;
check_write();
check_argc(argc, 1, 1);
ret = git_config_rename_section_in_file(given_config_source.file,
argv[0], NULL);
if (ret < 0)
return ret;
- if (ret == 0)
+ else if (!ret)
die(_("no such section: %s"), argv[0]);
+ else
+ ret = 0;
}
else if (actions == ACTION_GET_COLOR) {
check_argc(argc, 1, 2);
@@ -966,5 +963,6 @@
return get_colorbool(argv[0], argc == 2);
}
- return 0;
+ free(value);
+ return ret;
}
diff --git a/builtin/diff.c b/builtin/diff.c
index 163f2c6..26f1e53 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -612,7 +612,7 @@
if (1 < rev.diffopt.skip_stat_unmatch)
refresh_index_quietly();
release_revisions(&rev);
- UNLEAK(ent);
+ object_array_clear(&ent);
UNLEAK(blob);
return result;
}
diff --git a/builtin/difftool.c b/builtin/difftool.c
index d9b7622..dbbfb19 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -361,7 +361,7 @@
struct hashmap symlinks2 = HASHMAP_INIT(pair_cmp, NULL);
struct hashmap_iter iter;
struct pair_entry *entry;
- struct index_state wtindex;
+ struct index_state wtindex = INDEX_STATE_INIT(the_repository);
struct checkout lstate, rstate;
int err = 0;
struct child_process cmd = CHILD_PROCESS_INIT;
@@ -387,8 +387,6 @@
mkdir(ldir.buf, 0700);
mkdir(rdir.buf, 0700);
- memset(&wtindex, 0, sizeof(wtindex));
-
memset(&lstate, 0, sizeof(lstate));
lstate.base_dir = lbase_dir = xstrdup(ldir.buf);
lstate.base_dir_len = ldir.len;
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 3b3314e..39a890f 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -409,7 +409,7 @@
}
static void show_filemodify(struct diff_queue_struct *q,
- struct diff_options *options, void *data)
+ struct diff_options *options UNUSED, void *data)
{
int i;
struct string_list *changed = data;
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 7378caf..a21ce89 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -29,6 +29,7 @@
#include "commit-graph.h"
#include "shallow.h"
#include "worktree.h"
+#include "bundle-uri.h"
#define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
@@ -2109,6 +2110,7 @@
int cmd_fetch(int argc, const char **argv, const char *prefix)
{
int i;
+ const char *bundle_uri;
struct string_list list = STRING_LIST_INIT_DUP;
struct remote *remote = NULL;
int result = 0;
@@ -2194,6 +2196,10 @@
if (dry_run)
write_fetch_head = 0;
+ if (!git_config_get_string_tmp("fetch.bundleuri", &bundle_uri) &&
+ fetch_bundle_uri(the_repository, bundle_uri, NULL))
+ warning(_("failed to fetch bundles from '%s'"), bundle_uri);
+
if (all) {
if (argc == 1)
die(_("fetch --all does not take a repository argument"));
@@ -2228,6 +2234,7 @@
argv++;
}
}
+ string_list_remove_duplicates(&list, 0);
if (negotiate_only) {
struct oidset acked_commits = OIDSET_INIT;
diff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c
index 6f30a4f..0feef8c 100644
--- a/builtin/fsmonitor--daemon.c
+++ b/builtin/fsmonitor--daemon.c
@@ -710,6 +710,7 @@
"fsmonitor: unsupported V1 protocol '%s'"),
command);
do_trivial = 1;
+ do_cookie = 1;
} else {
/* We have "builtin:*" */
@@ -719,6 +720,7 @@
"fsmonitor: invalid V2 protocol token '%s'",
command);
do_trivial = 1;
+ do_cookie = 1;
} else {
/*
@@ -1209,7 +1211,7 @@
* events.
*/
if (pthread_create(&state->listener_thread, NULL,
- fsm_listen__thread_proc, state) < 0) {
+ fsm_listen__thread_proc, state)) {
ipc_server_stop_async(state->ipc_server_data);
err = error(_("could not start fsmonitor listener thread"));
goto cleanup;
@@ -1220,7 +1222,7 @@
* Start the health thread to watch over our process.
*/
if (pthread_create(&state->health_thread, NULL,
- fsm_health__thread_proc, state) < 0) {
+ fsm_health__thread_proc, state)) {
ipc_server_stop_async(state->ipc_server_data);
err = error(_("could not start fsmonitor health thread"));
goto cleanup;
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
index b506381..44db83f 100644
--- a/builtin/hash-object.c
+++ b/builtin/hash-object.c
@@ -27,6 +27,7 @@
else
ret = write_object_file_literally(buf.buf, buf.len, type, oid,
flags);
+ close(fd);
strbuf_release(&buf);
return ret;
}
diff --git a/builtin/hook.c b/builtin/hook.c
index b6530d1..f95b796 100644
--- a/builtin/hook.c
+++ b/builtin/hook.c
@@ -7,7 +7,7 @@
#include "strvec.h"
#define BUILTIN_HOOK_RUN_USAGE \
- N_("git hook run [--ignore-missing] <hook-name> [-- <hook-args>]")
+ N_("git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>]")
static const char * const builtin_hook_usage[] = {
BUILTIN_HOOK_RUN_USAGE,
@@ -28,6 +28,8 @@
struct option run_options[] = {
OPT_BOOL(0, "ignore-missing", &ignore_missing,
N_("silently ignore missing requested <hook-name>")),
+ OPT_STRING(0, "to-stdin", &opt.path_to_stdin, N_("path"),
+ N_("file to read into hooks' stdin")),
OPT_END(),
};
int ret;
diff --git a/builtin/log.c b/builtin/log.c
index 89447a5..04412dd 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -52,6 +52,7 @@
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 @@
cover_from_description_mode = parse_cover_from_description(value);
return 0;
}
+ if (!strcmp(var, "format.mboxrd")) {
+ stdout_mboxrd = git_config_bool(var, value);
+ return 0;
+ }
return git_log_config(var, value, cb);
}
@@ -1870,6 +1875,7 @@
struct strbuf rdiff1 = STRBUF_INIT;
struct strbuf rdiff2 = STRBUF_INIT;
struct strbuf rdiff_title = STRBUF_INIT;
+ struct strbuf sprefix = STRBUF_INIT;
int creation_factor = -1;
const struct option builtin_format_patch_options[] = {
@@ -2010,12 +2016,10 @@
cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
if (reroll_count) {
- struct strbuf sprefix = STRBUF_INIT;
-
strbuf_addf(&sprefix, "%s v%s",
rev.subject_prefix, reroll_count);
rev.reroll_count = reroll_count;
- rev.subject_prefix = strbuf_detach(&sprefix, NULL);
+ rev.subject_prefix = sprefix.buf;
}
for (i = 0; i < extra_hdr.nr; i++) {
@@ -2105,6 +2109,9 @@
rev.diffopt.close_file, "--output",
!!output_directory, "--output-directory");
+ if (use_stdout && stdout_mboxrd)
+ rev.commit_format = CMIT_FMT_MBOXRD;
+
if (use_stdout) {
setup_pager();
} else if (!rev.diffopt.close_file) {
@@ -2376,6 +2383,7 @@
strbuf_release(&rdiff1);
strbuf_release(&rdiff2);
strbuf_release(&rdiff_title);
+ strbuf_release(&sprefix);
free(to_free);
if (rev.ref_message_ids)
string_list_clear(rev.ref_message_ids, 0);
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 4cf8a23..a03b559 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -613,6 +613,7 @@
if (!fn)
fn = read_one_entry_quick;
err = read_tree(the_repository, tree, &pathspec, fn, istate);
+ clear_pathspec(&pathspec);
if (err)
die("unable to read tree entries %s", tree_name);
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index 5d5ac03..6516177 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -8,7 +8,7 @@
static const char * const ls_remote_usage[] = {
N_("git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
" [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n"
- " [--symref] [<repository> [<refs>...]]"),
+ " [--symref] [<repository> [<patterns>...]]"),
NULL
};
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
index c3ea092..8cc8c99 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -14,37 +14,11 @@
#include "parse-options.h"
#include "pathspec.h"
-static int line_termination = '\n';
-#define LS_RECURSIVE 1
-#define LS_TREE_ONLY (1 << 1)
-#define LS_SHOW_TREES (1 << 2)
-static int abbrev;
-static int ls_options;
-static struct pathspec pathspec;
-static int chomp_prefix;
-static const char *ls_tree_prefix;
-static const char *format;
-struct show_tree_data {
- unsigned mode;
- enum object_type type;
- const struct object_id *oid;
- const char *pathname;
- struct strbuf *base;
-};
-
-static const char * const ls_tree_usage[] = {
+static const char * const ls_tree_usage[] = {
N_("git ls-tree [<options>] <tree-ish> [<path>...]"),
NULL
};
-static enum ls_tree_cmdmode {
- MODE_DEFAULT = 0,
- MODE_LONG,
- MODE_NAME_ONLY,
- MODE_NAME_STATUS,
- MODE_OBJECT_ONLY,
-} cmdmode;
-
static void expand_objectsize(struct strbuf *line, const struct object_id *oid,
const enum object_type type, unsigned int padded)
{
@@ -64,10 +38,34 @@
}
}
+struct ls_tree_options {
+ unsigned null_termination:1;
+ int abbrev;
+ enum ls_tree_path_options {
+ LS_RECURSIVE = 1 << 0,
+ LS_TREE_ONLY = 1 << 1,
+ LS_SHOW_TREES = 1 << 2,
+ } ls_options;
+ struct pathspec pathspec;
+ int chomp_prefix;
+ const char *ls_tree_prefix;
+ const char *format;
+};
+
+struct show_tree_data {
+ struct ls_tree_options *options;
+ unsigned mode;
+ enum object_type type;
+ const struct object_id *oid;
+ const char *pathname;
+ struct strbuf *base;
+};
+
static size_t expand_show_tree(struct strbuf *sb, const char *start,
void *context)
{
struct show_tree_data *data = context;
+ struct ls_tree_options *options = data->options;
const char *end;
const char *p;
unsigned int errlen;
@@ -92,18 +90,18 @@
} else if (skip_prefix(start, "(objectsize)", &p)) {
expand_objectsize(sb, data->oid, data->type, 0);
} else if (skip_prefix(start, "(objectname)", &p)) {
- strbuf_add_unique_abbrev(sb, data->oid, abbrev);
+ strbuf_add_unique_abbrev(sb, data->oid, options->abbrev);
} else if (skip_prefix(start, "(path)", &p)) {
const char *name = data->base->buf;
- const char *prefix = chomp_prefix ? ls_tree_prefix : NULL;
- struct strbuf quoted = STRBUF_INIT;
+ const char *prefix = options->chomp_prefix ? options->ls_tree_prefix : NULL;
struct strbuf sbuf = STRBUF_INIT;
+ size_t baselen = data->base->len;
+
strbuf_addstr(data->base, data->pathname);
name = relative_path(data->base->buf, prefix, &sbuf);
- quote_c_style(name, "ed, NULL, 0);
- strbuf_addbuf(sb, "ed);
+ quote_c_style(name, sb, NULL, 0);
+ strbuf_setlen(data->base, baselen);
strbuf_release(&sbuf);
- strbuf_release("ed);
} else {
errlen = (unsigned long)len;
die(_("bad ls-tree format: %%%.*s"), errlen, start);
@@ -111,18 +109,19 @@
return len;
}
-static int show_recursive(const char *base, size_t baselen, const char *pathname)
+static int show_recursive(struct ls_tree_options *options, const char *base,
+ size_t baselen, const char *pathname)
{
int i;
- if (ls_options & LS_RECURSIVE)
+ if (options->ls_options & LS_RECURSIVE)
return 1;
- if (!pathspec.nr)
+ if (!options->pathspec.nr)
return 0;
- for (i = 0; i < pathspec.nr; i++) {
- const char *spec = pathspec.items[i].match;
+ for (i = 0; i < options->pathspec.nr; i++) {
+ const char *spec = options->pathspec.items[i].match;
size_t len, speclen;
if (strncmp(base, spec, baselen))
@@ -142,14 +141,14 @@
}
static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,
- const char *pathname, unsigned mode, void *context UNUSED)
+ const char *pathname, unsigned mode, void *context)
{
- size_t baselen;
+ struct ls_tree_options *options = context;
int recurse = 0;
struct strbuf sb = STRBUF_INIT;
enum object_type type = object_type(mode);
-
- struct show_tree_data data = {
+ struct show_tree_data cb_data = {
+ .options = options,
.mode = mode,
.type = type,
.oid = oid,
@@ -157,94 +156,100 @@
.base = base,
};
- if (type == OBJ_TREE && show_recursive(base->buf, base->len, pathname))
+ if (type == OBJ_TREE && show_recursive(options, base->buf, base->len, pathname))
recurse = READ_TREE_RECURSIVE;
- if (type == OBJ_TREE && recurse && !(ls_options & LS_SHOW_TREES))
+ if (type == OBJ_TREE && recurse && !(options->ls_options & LS_SHOW_TREES))
return recurse;
- if (type == OBJ_BLOB && (ls_options & LS_TREE_ONLY))
+ if (type == OBJ_BLOB && (options->ls_options & LS_TREE_ONLY))
return 0;
- baselen = base->len;
- strbuf_expand(&sb, format, expand_show_tree, &data);
- strbuf_addch(&sb, line_termination);
+ strbuf_expand(&sb, options->format, expand_show_tree, &cb_data);
+ strbuf_addch(&sb, options->null_termination ? '\0' : '\n');
fwrite(sb.buf, sb.len, 1, stdout);
strbuf_release(&sb);
- strbuf_setlen(base, baselen);
return recurse;
}
-static int show_tree_common(struct show_tree_data *data, int *recurse,
- const struct object_id *oid, struct strbuf *base,
- const char *pathname, unsigned mode)
+static int show_tree_common(struct ls_tree_options *options, int *recurse,
+ struct strbuf *base, const char *pathname,
+ enum object_type type)
{
- enum object_type type = object_type(mode);
int ret = -1;
-
*recurse = 0;
- data->mode = mode;
- data->type = type;
- data->oid = oid;
- data->pathname = pathname;
- data->base = base;
if (type == OBJ_BLOB) {
- if (ls_options & LS_TREE_ONLY)
+ if (options->ls_options & LS_TREE_ONLY)
ret = 0;
} else if (type == OBJ_TREE &&
- show_recursive(base->buf, base->len, pathname)) {
+ show_recursive(options, base->buf, base->len, pathname)) {
*recurse = READ_TREE_RECURSIVE;
- if (!(ls_options & LS_SHOW_TREES))
+ if (!(options->ls_options & LS_SHOW_TREES))
ret = *recurse;
}
return ret;
}
-static void show_tree_common_default_long(struct strbuf *base,
+static void show_tree_common_default_long(struct ls_tree_options *options,
+ struct strbuf *base,
const char *pathname,
const size_t baselen)
{
+ const char *prefix = options->chomp_prefix ? options->ls_tree_prefix : NULL;
+
strbuf_addstr(base, pathname);
- write_name_quoted_relative(base->buf,
- chomp_prefix ? ls_tree_prefix : NULL, stdout,
- line_termination);
+
+ if (options->null_termination) {
+ struct strbuf sb = STRBUF_INIT;
+ const char *name = relative_path(base->buf, prefix, &sb);
+
+ fputs(name, stdout);
+ fputc('\0', stdout);
+
+ strbuf_release(&sb);
+ } else {
+ write_name_quoted_relative(base->buf, prefix, stdout, '\n');
+ }
+
strbuf_setlen(base, baselen);
}
static int show_tree_default(const struct object_id *oid, struct strbuf *base,
const char *pathname, unsigned mode,
- void *context UNUSED)
+ void *context)
{
+ struct ls_tree_options *options = context;
int early;
int recurse;
- struct show_tree_data data = { 0 };
+ enum object_type type = object_type(mode);
- early = show_tree_common(&data, &recurse, oid, base, pathname, mode);
+ early = show_tree_common(options, &recurse, base, pathname, type);
if (early >= 0)
return early;
- printf("%06o %s %s\t", data.mode, type_name(data.type),
- find_unique_abbrev(data.oid, abbrev));
- show_tree_common_default_long(base, pathname, data.base->len);
+ printf("%06o %s %s\t", mode, type_name(object_type(mode)),
+ find_unique_abbrev(oid, options->abbrev));
+ show_tree_common_default_long(options, base, pathname, base->len);
return recurse;
}
static int show_tree_long(const struct object_id *oid, struct strbuf *base,
const char *pathname, unsigned mode,
- void *context UNUSED)
+ void *context)
{
+ struct ls_tree_options *options = context;
int early;
int recurse;
- struct show_tree_data data = { 0 };
char size_text[24];
+ enum object_type type = object_type(mode);
- early = show_tree_common(&data, &recurse, oid, base, pathname, mode);
+ early = show_tree_common(options, &recurse, base, pathname, type);
if (early >= 0)
return early;
- if (data.type == OBJ_BLOB) {
+ if (type == OBJ_BLOB) {
unsigned long size;
- if (oid_object_info(the_repository, data.oid, &size) == OBJ_BAD)
+ if (oid_object_info(the_repository, oid, &size) == OBJ_BAD)
xsnprintf(size_text, sizeof(size_text), "BAD");
else
xsnprintf(size_text, sizeof(size_text),
@@ -253,49 +258,76 @@
xsnprintf(size_text, sizeof(size_text), "-");
}
- printf("%06o %s %s %7s\t", data.mode, type_name(data.type),
- find_unique_abbrev(data.oid, abbrev), size_text);
- show_tree_common_default_long(base, pathname, data.base->len);
+ printf("%06o %s %s %7s\t", mode, type_name(type),
+ find_unique_abbrev(oid, options->abbrev), size_text);
+ show_tree_common_default_long(options, base, pathname, base->len);
return recurse;
}
static int show_tree_name_only(const struct object_id *oid, struct strbuf *base,
const char *pathname, unsigned mode,
- void *context UNUSED)
+ void *context)
{
+ struct ls_tree_options *options = context;
int early;
int recurse;
const size_t baselen = base->len;
- struct show_tree_data data = { 0 };
+ enum object_type type = object_type(mode);
+ const char *prefix;
- early = show_tree_common(&data, &recurse, oid, base, pathname, mode);
+ early = show_tree_common(options, &recurse, base, pathname, type);
if (early >= 0)
return early;
+ prefix = options->chomp_prefix ? options->ls_tree_prefix : NULL;
strbuf_addstr(base, pathname);
- write_name_quoted_relative(base->buf,
- chomp_prefix ? ls_tree_prefix : NULL,
- stdout, line_termination);
+ if (options->null_termination) {
+ struct strbuf sb = STRBUF_INIT;
+ const char *name = relative_path(base->buf, prefix, &sb);
+
+ fputs(name, stdout);
+ fputc('\0', stdout);
+
+ strbuf_release(&sb);
+ } else {
+ write_name_quoted_relative(base->buf, prefix, stdout, '\n');
+ }
strbuf_setlen(base, baselen);
return recurse;
}
static int show_tree_object(const struct object_id *oid, struct strbuf *base,
const char *pathname, unsigned mode,
- void *context UNUSED)
+ void *context)
{
+ struct ls_tree_options *options = context;
int early;
int recurse;
- struct show_tree_data data = { 0 };
+ enum object_type type = object_type(mode);
+ const char *str;
- early = show_tree_common(&data, &recurse, oid, base, pathname, mode);
+ early = show_tree_common(options, &recurse, base, pathname, type);
if (early >= 0)
return early;
- printf("%s%c", find_unique_abbrev(oid, abbrev), line_termination);
+ str = find_unique_abbrev(oid, options->abbrev);
+ if (options->null_termination) {
+ fputs(str, stdout);
+ fputc('\0', stdout);
+ } else {
+ puts(str);
+ }
return recurse;
}
+enum ls_tree_cmdmode {
+ MODE_DEFAULT = 0,
+ MODE_LONG,
+ MODE_NAME_ONLY,
+ MODE_NAME_STATUS,
+ MODE_OBJECT_ONLY,
+};
+
struct ls_tree_cmdmode_to_fmt {
enum ls_tree_cmdmode mode;
const char *const fmt;
@@ -335,15 +367,18 @@
struct tree *tree;
int i, full_tree = 0;
read_tree_fn_t fn = NULL;
+ enum ls_tree_cmdmode cmdmode = MODE_DEFAULT;
+ int null_termination = 0;
+ struct ls_tree_options options = { 0 };
const struct option ls_tree_options[] = {
- OPT_BIT('d', NULL, &ls_options, N_("only show trees"),
+ OPT_BIT('d', NULL, &options.ls_options, N_("only show trees"),
LS_TREE_ONLY),
- OPT_BIT('r', NULL, &ls_options, N_("recurse into subtrees"),
+ OPT_BIT('r', NULL, &options.ls_options, N_("recurse into subtrees"),
LS_RECURSIVE),
- OPT_BIT('t', NULL, &ls_options, N_("show trees when recursing"),
+ OPT_BIT('t', NULL, &options.ls_options, N_("show trees when recursing"),
LS_SHOW_TREES),
- OPT_SET_INT('z', NULL, &line_termination,
- N_("terminate entries with NUL byte"), 0),
+ OPT_BOOL('z', NULL, &null_termination,
+ N_("terminate entries with NUL byte")),
OPT_CMDMODE('l', "long", &cmdmode, N_("include object size"),
MODE_LONG),
OPT_CMDMODE(0, "name-only", &cmdmode, N_("list only filenames"),
@@ -352,29 +387,32 @@
MODE_NAME_STATUS),
OPT_CMDMODE(0, "object-only", &cmdmode, N_("list only objects"),
MODE_OBJECT_ONLY),
- OPT_SET_INT(0, "full-name", &chomp_prefix,
+ OPT_SET_INT(0, "full-name", &options.chomp_prefix,
N_("use full path names"), 0),
OPT_BOOL(0, "full-tree", &full_tree,
N_("list entire tree; not just current directory "
"(implies --full-name)")),
- OPT_STRING_F(0, "format", &format, N_("format"),
+ OPT_STRING_F(0, "format", &options.format, N_("format"),
N_("format to use for the output"),
PARSE_OPT_NONEG),
- OPT__ABBREV(&abbrev),
+ OPT__ABBREV(&options.abbrev),
OPT_END()
};
struct ls_tree_cmdmode_to_fmt *m2f = ls_tree_cmdmode_format;
+ int ret;
git_config(git_default_config, NULL);
- ls_tree_prefix = prefix;
+ options.ls_tree_prefix = prefix;
if (prefix)
- chomp_prefix = strlen(prefix);
+ options.chomp_prefix = strlen(prefix);
argc = parse_options(argc, argv, prefix, ls_tree_options,
ls_tree_usage, 0);
+ options.null_termination = null_termination;
+
if (full_tree) {
- ls_tree_prefix = prefix = NULL;
- chomp_prefix = 0;
+ options.ls_tree_prefix = prefix = NULL;
+ options.chomp_prefix = 0;
}
/*
* We wanted to detect conflicts between --name-only and
@@ -386,10 +424,10 @@
/* -d -r should imply -t, but -d by itself should not have to. */
if ( (LS_TREE_ONLY|LS_RECURSIVE) ==
- ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))
- ls_options |= LS_SHOW_TREES;
+ ((LS_TREE_ONLY|LS_RECURSIVE) & options.ls_options))
+ options.ls_options |= LS_SHOW_TREES;
- if (format && cmdmode)
+ if (options.format && cmdmode)
usage_msg_opt(
_("--format can't be combined with other format-altering options"),
ls_tree_usage, ls_tree_options);
@@ -404,13 +442,13 @@
* cannot be lifted until it is converted to use
* match_pathspec() or tree_entry_interesting()
*/
- parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC &
- ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL),
+ parse_pathspec(&options.pathspec, PATHSPEC_ALL_MAGIC &
+ ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL),
PATHSPEC_PREFER_CWD,
prefix, argv + 1);
- for (i = 0; i < pathspec.nr; i++)
- pathspec.items[i].nowildcard_len = pathspec.items[i].len;
- pathspec.has_wildcard = 0;
+ for (i = 0; i < options.pathspec.nr; i++)
+ options.pathspec.items[i].nowildcard_len = options.pathspec.items[i].len;
+ options.pathspec.has_wildcard = 0;
tree = parse_tree_indirect(&oid);
if (!tree)
die("not a tree object");
@@ -420,11 +458,11 @@
*/
while (m2f) {
if (!m2f->fmt) {
- fn = format ? show_tree_fmt : show_tree_default;
- } else if (format && !strcmp(format, m2f->fmt)) {
+ fn = options.format ? show_tree_fmt : show_tree_default;
+ } else if (options.format && !strcmp(options.format, m2f->fmt)) {
cmdmode = m2f->mode;
fn = m2f->fn;
- } else if (!format && cmdmode == m2f->mode) {
+ } else if (!options.format && cmdmode == m2f->mode) {
fn = m2f->fn;
} else {
m2f++;
@@ -433,5 +471,7 @@
break;
}
- return !!read_tree(the_repository, tree, &pathspec, fn, NULL);
+ ret = !!read_tree(the_repository, tree, &options.pathspec, fn, &options);
+ clear_pathspec(&options.pathspec);
+ return ret;
}
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index e376708..828dc81 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -3,6 +3,7 @@
#include "tree-walk.h"
#include "xdiff-interface.h"
#include "help.h"
+#include "commit.h"
#include "commit-reach.h"
#include "merge-ort.h"
#include "object-store.h"
@@ -98,7 +99,7 @@
return NULL;
}
-static int show_outf(void *priv_, mmbuffer_t *mb, int nbuf)
+static int show_outf(void *priv UNUSED, mmbuffer_t *mb, int nbuf)
{
int i;
for (i = 0; i < nbuf; i++)
@@ -406,6 +407,7 @@
};
static int real_merge(struct merge_tree_options *o,
+ const char *merge_base,
const char *branch1, const char *branch2,
const char *prefix)
{
@@ -432,16 +434,31 @@
opt.branch1 = branch1;
opt.branch2 = branch2;
- /*
- * Get the merge bases, in reverse order; see comment above
- * merge_incore_recursive in merge-ort.h
- */
- merge_bases = get_merge_bases(parent1, parent2);
- if (!merge_bases && !o->allow_unrelated_histories)
- die(_("refusing to merge unrelated histories"));
- merge_bases = reverse_commit_list(merge_bases);
+ if (merge_base) {
+ struct commit *base_commit;
+ struct tree *base_tree, *parent1_tree, *parent2_tree;
- merge_incore_recursive(&opt, merge_bases, parent1, parent2, &result);
+ base_commit = lookup_commit_reference_by_name(merge_base);
+ if (!base_commit)
+ die(_("could not lookup commit %s"), merge_base);
+
+ opt.ancestor = merge_base;
+ base_tree = get_commit_tree(base_commit);
+ parent1_tree = get_commit_tree(parent1);
+ parent2_tree = get_commit_tree(parent2);
+ merge_incore_nonrecursive(&opt, base_tree, parent1_tree, parent2_tree, &result);
+ } else {
+ /*
+ * Get the merge bases, in reverse order; see comment above
+ * merge_incore_recursive in merge-ort.h
+ */
+ merge_bases = get_merge_bases(parent1, parent2);
+ if (!merge_bases && !o->allow_unrelated_histories)
+ die(_("refusing to merge unrelated histories"));
+ merge_bases = reverse_commit_list(merge_bases);
+ merge_incore_recursive(&opt, merge_bases, parent1, parent2, &result);
+ }
+
if (result.clean < 0)
die(_("failure to merge"));
@@ -487,6 +504,7 @@
struct merge_tree_options o = { .show_messages = -1 };
int expected_remaining_argc;
int original_argc;
+ const char *merge_base = NULL;
const char * const merge_tree_usage[] = {
N_("git merge-tree [--write-tree] [<options>] <branch1> <branch2>"),
@@ -515,6 +533,10 @@
&o.use_stdin,
N_("perform multiple merges, one per line of input"),
PARSE_OPT_NONEG),
+ OPT_STRING(0, "merge-base",
+ &merge_base,
+ N_("commit"),
+ N_("specify a merge-base for the merge")),
OPT_END()
};
@@ -529,16 +551,35 @@
if (o.mode == MODE_TRIVIAL)
die(_("--trivial-merge is incompatible with all other options"));
+ if (merge_base)
+ die(_("--merge-base is incompatible with --stdin"));
line_termination = '\0';
while (strbuf_getline_lf(&buf, stdin) != EOF) {
struct strbuf **split;
int result;
+ const char *input_merge_base = NULL;
split = strbuf_split(&buf, ' ');
- if (!split[0] || !split[1] || split[2])
+ if (!split[0] || !split[1])
die(_("malformed input line: '%s'."), buf.buf);
strbuf_rtrim(split[0]);
- result = real_merge(&o, split[0]->buf, split[1]->buf, prefix);
+ strbuf_rtrim(split[1]);
+
+ /* parse the merge-base */
+ if (!strcmp(split[1]->buf, "--")) {
+ input_merge_base = split[0]->buf;
+ }
+
+ if (input_merge_base && split[2] && split[3] && !split[4]) {
+ strbuf_rtrim(split[2]);
+ strbuf_rtrim(split[3]);
+ result = real_merge(&o, input_merge_base, split[2]->buf, split[3]->buf, prefix);
+ } else if (!input_merge_base && !split[2]) {
+ result = real_merge(&o, NULL, split[0]->buf, split[1]->buf, prefix);
+ } else {
+ die(_("malformed input line: '%s'."), buf.buf);
+ }
+
if (result < 0)
die(_("merging cannot continue; got unclean result of %d"), result);
strbuf_list_free(split);
@@ -581,7 +622,7 @@
/* Do the relevant type of merge */
if (o.mode == MODE_REAL)
- return real_merge(&o, argv[0], argv[1], prefix);
+ return real_merge(&o, merge_base, argv[0], argv[1], prefix);
else
return trivial_merge(argv[0], argv[1], argv[2]);
}
diff --git a/builtin/merge.c b/builtin/merge.c
index dd47437..0a3c10a 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -6,7 +6,7 @@
* Based on git-merge.sh by Junio C Hamano.
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "cache.h"
#include "config.h"
#include "parse-options.h"
@@ -188,7 +188,7 @@
for (i = 0; i < main_cmds.cnt; i++) {
int j, found = 0;
struct cmdname *ent = main_cmds.names[i];
- for (j = 0; j < ARRAY_SIZE(all_strategy); j++)
+ for (j = 0; !found && j < ARRAY_SIZE(all_strategy); j++)
if (!strncmp(ent->name, all_strategy[j].name, ent->len)
&& !all_strategy[j].name[ent->len])
found = 1;
@@ -390,8 +390,8 @@
run_command(&cmd);
refresh_cache:
- discard_cache();
- if (read_cache() < 0)
+ discard_index(&the_index);
+ if (repo_read_index(the_repository) < 0)
die(_("could not read index"));
}
@@ -706,7 +706,7 @@
static void write_tree_trivial(struct object_id *oid)
{
- if (write_cache_as_tree(oid, 0, NULL))
+ if (write_index_as_tree(oid, &the_index, get_index_file(), 0, NULL))
die(_("git write-tree failed to write a tree"));
}
@@ -776,7 +776,7 @@
}
static void count_diff_files(struct diff_queue_struct *q,
- struct diff_options *opt, void *data)
+ struct diff_options *opt UNUSED, void *data)
{
int *count = data;
@@ -1560,7 +1560,9 @@
!common->next &&
oideq(&common->item->object.oid, &head_commit->object.oid)) {
/* Again the most common case of merging one remote. */
- struct strbuf msg = STRBUF_INIT;
+ const char *msg = have_message ?
+ "Fast-forward (no commit created; -m option ignored)" :
+ "Fast-forward";
struct commit *commit;
if (verbosity >= 0) {
@@ -1570,10 +1572,6 @@
find_unique_abbrev(&remoteheads->item->object.oid,
DEFAULT_ABBREV));
}
- strbuf_addstr(&msg, "Fast-forward");
- if (have_message)
- strbuf_addstr(&msg,
- " (no commit created; -m option ignored)");
commit = remoteheads->item;
if (!commit) {
ret = 1;
@@ -1592,9 +1590,8 @@
goto done;
}
- finish(head_commit, remoteheads, &commit->object.oid, msg.buf);
+ finish(head_commit, remoteheads, &commit->object.oid, msg);
remove_merge_branch_state(the_repository);
- strbuf_release(&msg);
goto done;
} else if (!remoteheads->next && common->next)
;
@@ -1621,7 +1618,8 @@
error(_("Your local changes to the following files would be overwritten by merge:\n %s"),
sb.buf);
strbuf_release(&sb);
- return 2;
+ ret = 2;
+ goto done;
}
/* See if it is really trivial. */
@@ -1789,5 +1787,6 @@
}
strbuf_release(&buf);
free(branch_to_free);
+ discard_index(&the_index);
return ret;
}
diff --git a/builtin/mv.c b/builtin/mv.c
index 19790ce..edd7b93 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2006 Johannes Schindelin
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "config.h"
#include "pathspec.h"
@@ -489,7 +489,8 @@
if ((mode & SPARSE) &&
path_in_sparse_checkout(dst, &the_index)) {
/* from out-of-cone to in-cone */
- int dst_pos = cache_name_pos(dst, strlen(dst));
+ int dst_pos = index_name_pos(&the_index, dst,
+ strlen(dst));
struct cache_entry *dst_ce = the_index.cache[dst_pos];
dst_ce->ce_flags &= ~CE_SKIP_WORKTREE;
@@ -500,7 +501,8 @@
!(mode & SPARSE) &&
!path_in_sparse_checkout(dst, &the_index)) {
/* from in-cone to out-of-cone */
- int dst_pos = cache_name_pos(dst, strlen(dst));
+ int dst_pos = index_name_pos(&the_index, dst,
+ strlen(dst));
struct cache_entry *dst_ce = the_index.cache[dst_pos];
/*
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 15535e9..97959bf 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -108,19 +108,11 @@
int name_distance = effective_distance(name->distance, name->generation);
int new_distance = effective_distance(distance, generation);
- /*
- * When comparing names based on tags, prefer names
- * based on the older tag, even if it is farther away.
- */
+ /* If both are tags, we prefer the nearer one. */
if (from_tag && name->from_tag)
- return (name->taggerdate > taggerdate ||
- (name->taggerdate == taggerdate &&
- name_distance > new_distance));
+ return name_distance > new_distance;
- /*
- * We know that at least one of them is a non-tag at this point.
- * favor a tag over a non-tag.
- */
+ /* Favor a tag over a non-tag. */
if (name->from_tag != from_tag)
return from_tag;
@@ -273,17 +265,6 @@
return -1;
}
-static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous)
-{
- if (shorten_unambiguous)
- refname = shorten_unambiguous_ref(refname, 0);
- else if (skip_prefix(refname, "refs/heads/", &refname))
- ; /* refname already advanced */
- else
- skip_prefix(refname, "refs/", &refname);
- return refname;
-}
-
struct name_ref_data {
int tags_only;
int name_only;
@@ -309,11 +290,19 @@
int shorten_unambiguous, struct commit *commit,
timestamp_t taggerdate, int from_tag, int deref)
{
- refname = name_ref_abbrev(refname, shorten_unambiguous);
+ char *short_refname = NULL;
+
+ if (shorten_unambiguous)
+ short_refname = shorten_unambiguous_ref(refname, 0);
+ else if (skip_prefix(refname, "refs/heads/", &refname))
+ ; /* refname already advanced */
+ else
+ skip_prefix(refname, "refs/", &refname);
ALLOC_GROW(tip_table.table, tip_table.nr + 1, tip_table.alloc);
oidcpy(&tip_table.table[tip_table.nr].oid, oid);
- tip_table.table[tip_table.nr].refname = xstrdup(refname);
+ tip_table.table[tip_table.nr].refname = short_refname ?
+ short_refname : xstrdup(refname);
tip_table.table[tip_table.nr].commit = commit;
tip_table.table[tip_table.nr].taggerdate = taggerdate;
tip_table.table[tip_table.nr].from_tag = from_tag;
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 573d0b2..74a167a 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -929,8 +929,10 @@
*/
for_each_tag_ref(mark_tagged, NULL);
- if (use_delta_islands)
+ if (use_delta_islands) {
max_layers = compute_pack_layers(&to_pack);
+ free_island_marks();
+ }
ALLOC_ARRAY(wo, to_pack.nr_objects);
wo_end = 0;
@@ -1318,7 +1320,7 @@
if (!check)
check = attr_check_initl("delta", NULL);
- git_check_attr(the_repository->index, path, check);
+ git_check_attr(the_repository->index, NULL, path, check);
if (ATTR_FALSE(check->items[0].value))
return 1;
return 0;
@@ -1708,17 +1710,14 @@
free(cache);
}
-static int name_cmp_len(const char *name)
+static size_t name_cmp_len(const char *name)
{
- int i;
- for (i = 0; name[i] && name[i] != '\n' && name[i] != '/'; i++)
- ;
- return i;
+ return strcspn(name, "\n/");
}
static void add_pbase_object(struct tree_desc *tree,
const char *name,
- int cmplen,
+ size_t cmplen,
const char *fullname)
{
struct name_entry entry;
@@ -1743,7 +1742,7 @@
struct tree_desc sub;
struct pbase_tree_cache *tree;
const char *down = name+cmplen+1;
- int downlen = name_cmp_len(down);
+ size_t downlen = name_cmp_len(down);
tree = pbase_tree_get(&entry.oid);
if (!tree)
@@ -1795,7 +1794,7 @@
static void add_preferred_base_object(const char *name)
{
struct pbase_tree *it;
- int cmplen;
+ size_t cmplen;
unsigned hash = pack_name_hash(name);
if (!num_preferred_base || check_pbase_path(hash))
@@ -4149,21 +4148,6 @@
return 0;
}
-struct po_filter_data {
- unsigned have_revs:1;
- struct rev_info revs;
-};
-
-static struct list_objects_filter_options *po_filter_revs_init(void *value)
-{
- struct po_filter_data *data = value;
-
- repo_init_revisions(the_repository, &data->revs, NULL);
- data->have_revs = 1;
-
- return &data->revs.filter;
-}
-
int cmd_pack_objects(int argc, const char **argv, const char *prefix)
{
int use_internal_rev_list = 0;
@@ -4174,7 +4158,8 @@
int rev_list_index = 0;
int stdin_packs = 0;
struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
- struct po_filter_data pfd = { .have_revs = 0 };
+ struct list_objects_filter_options filter_options =
+ LIST_OBJECTS_FILTER_INIT;
struct option pack_objects_options[] = {
OPT_SET_INT('q', "quiet", &progress,
@@ -4265,7 +4250,7 @@
&write_bitmap_index,
N_("write a bitmap index if possible"),
WRITE_BITMAP_QUIET, PARSE_OPT_HIDDEN),
- OPT_PARSE_LIST_OBJECTS_FILTER_INIT(&pfd, po_filter_revs_init),
+ OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
OPT_CALLBACK_F(0, "missing", NULL, N_("action"),
N_("handling for missing objects"), PARSE_OPT_NONEG,
option_parse_missing_action),
@@ -4385,7 +4370,7 @@
if (!rev_list_all || !rev_list_reflog || !rev_list_index)
unpack_unreachable_expiration = 0;
- if (pfd.have_revs && pfd.revs.filter.choice) {
+ if (filter_options.choice) {
if (!pack_to_stdout)
die(_("cannot use --filter without --stdout"));
if (stdin_packs)
@@ -4472,13 +4457,11 @@
read_cruft_objects();
} else if (!use_internal_rev_list) {
read_object_list_from_stdin();
- } else if (pfd.have_revs) {
- get_object_list(&pfd.revs, rp.nr, rp.v);
- release_revisions(&pfd.revs);
} else {
struct rev_info revs;
repo_init_revisions(the_repository, &revs, NULL);
+ list_objects_filter_copy(&revs.filter, &filter_options);
get_object_list(&revs, rp.nr, rp.v);
release_revisions(&revs);
}
@@ -4513,6 +4496,7 @@
reuse_packfile_objects);
cleanup:
+ list_objects_filter_release(&filter_options);
strvec_clear(&rp);
return 0;
diff --git a/builtin/push.c b/builtin/push.c
index 60ac801..8f7d326 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -63,16 +63,9 @@
static struct string_list push_options_config = STRING_LIST_INIT_DUP;
static void refspec_append_mapped(struct refspec *refspec, const char *ref,
- struct remote *remote, struct ref *local_refs)
+ struct remote *remote, struct ref *matched)
{
const char *branch_name;
- struct ref *matched = NULL;
-
- /* Does "ref" uniquely name our ref? */
- if (count_refspec_match(ref, local_refs, &matched) != 1) {
- refspec_append(refspec, ref);
- return;
- }
if (remote->push.nr) {
struct refspec_item query;
@@ -120,15 +113,28 @@
die(_("--delete only accepts plain target ref names"));
refspec_appendf(&rs, ":%s", ref);
} else if (!strchr(ref, ':')) {
- if (!remote) {
- /* lazily grab remote and local_refs */
- remote = remote_get(repo);
+ struct ref *matched = NULL;
+
+ /* lazily grab local_refs */
+ if (!local_refs)
local_refs = get_local_heads();
+
+ /* Does "ref" uniquely name our ref? */
+ if (count_refspec_match(ref, local_refs, &matched) != 1) {
+ refspec_append(&rs, ref);
+ } else {
+ /* lazily grab remote */
+ if (!remote)
+ remote = remote_get(repo);
+ if (!remote)
+ BUG("must get a remote for repo '%s'", repo);
+
+ refspec_append_mapped(&rs, ref, remote, matched);
}
- refspec_append_mapped(&rs, ref, remote, local_refs);
} else
refspec_append(&rs, ref);
}
+ free_refs(local_refs);
}
static int push_url_of_remote(struct remote *remote, const char ***url_p)
diff --git a/builtin/range-diff.c b/builtin/range-diff.c
index e2a74ef..aecfae1 100644
--- a/builtin/range-diff.c
+++ b/builtin/range-diff.c
@@ -47,7 +47,7 @@
repo_diff_setup(the_repository, &diffopt);
- options = parse_options_concat(range_diff_options, diffopt.parseopts);
+ options = add_diff_options(range_diff_options, &diffopt);
argc = parse_options(argc, argv, prefix, options,
builtin_range_diff_usage, PARSE_OPT_KEEP_DASHDASH);
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index f702f9d..3ce7541 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -114,6 +114,7 @@
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/rebase.c b/builtin/rebase.c
index b22768c..6635f10 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -113,7 +113,7 @@
int autostash;
int committer_date_is_author_date;
int ignore_date;
- char *cmd;
+ struct string_list exec;
int allow_empty_message;
int rebase_merges, rebase_cousins;
char *strategy, *strategy_opts;
@@ -122,6 +122,8 @@
int reapply_cherry_picks;
int fork_point;
int update_refs;
+ int config_autosquash;
+ int config_update_refs;
};
#define REBASE_OPTIONS_INIT { \
@@ -131,8 +133,15 @@
.default_backend = "merge", \
.flags = REBASE_NO_QUIET, \
.git_am_opts = STRVEC_INIT, \
+ .exec = STRING_LIST_INIT_NODUP, \
.git_format_patch_opt = STRBUF_INIT, \
.fork_point = -1, \
+ .reapply_cherry_picks = -1, \
+ .allow_empty_message = 1, \
+ .autosquash = -1, \
+ .config_autosquash = -1, \
+ .update_refs = -1, \
+ .config_update_refs = -1, \
}
static struct replay_opts get_replay_opts(const struct rebase_options *opts)
@@ -243,38 +252,22 @@
return write_basic_state(opts, head_name, onto, orig_head);
}
-static void split_exec_commands(const char *cmd, struct string_list *commands)
-{
- if (cmd && *cmd) {
- string_list_split(commands, cmd, '\n', -1);
-
- /* rebase.c adds a new line to cmd after every command,
- * so here the last command is always empty */
- string_list_remove_empty_items(commands, 0);
- }
-}
-
static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
{
- int ret;
+ int ret = -1;
char *revisions = NULL, *shortrevisions = NULL;
struct strvec make_script_args = STRVEC_INIT;
struct todo_list todo_list = TODO_LIST_INIT;
struct replay_opts replay = get_replay_opts(opts);
- struct string_list commands = STRING_LIST_INIT_DUP;
if (get_revision_ranges(opts->upstream, opts->onto, &opts->orig_head->object.oid,
&revisions, &shortrevisions))
- return -1;
+ goto cleanup;
if (init_basic_state(&replay,
opts->head_name ? opts->head_name : "detached HEAD",
- opts->onto, &opts->orig_head->object.oid)) {
- free(revisions);
- free(shortrevisions);
-
- return -1;
- }
+ opts->onto, &opts->orig_head->object.oid))
+ goto cleanup;
if (!opts->upstream && opts->squash_onto)
write_file(path_squash_onto(), "%s\n",
@@ -297,14 +290,14 @@
&todo_list))
BUG("unusable todo list");
- split_exec_commands(opts->cmd, &commands);
ret = complete_action(the_repository, &replay, flags,
shortrevisions, opts->onto_name, opts->onto,
- &opts->orig_head->object.oid, &commands,
+ &opts->orig_head->object.oid, &opts->exec,
opts->autosquash, opts->update_refs, &todo_list);
}
- string_list_clear(&commands, 0);
+cleanup:
+ replay_opts_release(&replay);
free(revisions);
free(shortrevisions);
todo_list_release(&todo_list);
@@ -346,6 +339,7 @@
struct replay_opts replay_opts = get_replay_opts(opts);
ret = sequencer_continue(the_repository, &replay_opts);
+ replay_opts_release(&replay_opts);
break;
}
case ACTION_EDIT_TODO:
@@ -561,6 +555,7 @@
replay.action = REPLAY_INTERACTIVE_REBASE;
ret = sequencer_remove_state(&replay);
+ replay_opts_release(&replay);
} else {
strbuf_addstr(&dir, opts->state_dir);
if (remove_dir_recursively(&dir, 0))
@@ -789,7 +784,7 @@
}
if (!strcmp(var, "rebase.autosquash")) {
- opts->autosquash = git_config_bool(var, value);
+ opts->config_autosquash = git_config_bool(var, value);
return 0;
}
@@ -806,7 +801,7 @@
}
if (!strcmp(var, "rebase.updaterefs")) {
- opts->update_refs = git_config_bool(var, value);
+ opts->config_update_refs = git_config_bool(var, value);
return 0;
}
@@ -920,6 +915,9 @@
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(arg);
+ if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_APPLY)
+ die(_("apply options and merge options cannot be used together"));
+
opts->type = REBASE_APPLY;
return 0;
@@ -933,8 +931,10 @@
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(arg);
- if (!is_merge(opts))
- opts->type = REBASE_MERGE;
+ if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_MERGE)
+ die(_("apply options and merge options cannot be used together"));
+
+ opts->type = REBASE_MERGE;
return 0;
}
@@ -948,6 +948,9 @@
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(arg);
+ if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_MERGE)
+ die(_("apply options and merge options cannot be used together"));
+
opts->type = REBASE_MERGE;
opts->flags |= REBASE_INTERACTIVE_EXPLICIT;
@@ -1032,11 +1035,11 @@
struct object_id branch_base;
int ignore_whitespace = 0;
const char *gpg_sign = NULL;
- struct string_list exec = STRING_LIST_INIT_NODUP;
const char *rebase_merges = NULL;
struct string_list strategy_options = STRING_LIST_INIT_NODUP;
struct object_id squash_onto;
char *squash_onto_name = NULL;
+ char *keep_base_onto_name = NULL;
int reschedule_failed_exec = -1;
int allow_preemptive_ff = 1;
int preserve_merges_selected = 0;
@@ -1127,7 +1130,7 @@
N_("GPG-sign commits"),
PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
OPT_AUTOSTASH(&options.autostash),
- OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
+ OPT_STRING_LIST('x', "exec", &options.exec, N_("exec"),
N_("add exec lines after each commit of the "
"editable list")),
OPT_BOOL_F(0, "allow-empty-message",
@@ -1164,8 +1167,6 @@
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
- options.reapply_cherry_picks = -1;
- options.allow_empty_message = 1;
git_config(rebase_config, &options);
/* options.gpg_sign_opt will be either "-S" or NULL */
gpg_sign = options.gpg_sign_opt ? "" : NULL;
@@ -1230,13 +1231,6 @@
if (options.fork_point < 0)
options.fork_point = 0;
}
- /*
- * --keep-base defaults to --reapply-cherry-picks to avoid losing
- * commits when using this option.
- */
- if (options.reapply_cherry_picks < 0)
- options.reapply_cherry_picks = keep_base;
-
if (options.root && options.fork_point > 0)
die(_("options '%s' and '%s' cannot be used together"), "--root", "--fork-point");
@@ -1250,7 +1244,7 @@
if (trace2_is_enabled()) {
if (is_merge(&options))
trace2_cmd_mode("interactive");
- else if (exec.nr)
+ else if (options.exec.nr)
trace2_cmd_mode("interactive-exec");
else
trace2_cmd_mode(action_names[options.action]);
@@ -1322,6 +1316,7 @@
if (reset_head(the_repository, &ropts) < 0)
die(_("could not move back to %s"),
oid_to_hex(&options.orig_head->object.oid));
+ strbuf_release(&head_msg);
remove_branch_state(the_repository, 0);
ret = finish_rebase(&options);
goto cleanup;
@@ -1333,6 +1328,7 @@
replay.action = REPLAY_INTERACTIVE_REBASE;
ret = sequencer_remove_state(&replay);
+ replay_opts_release(&replay);
} else {
strbuf_reset(&buf);
strbuf_addstr(&buf, options.state_dir);
@@ -1377,8 +1373,9 @@
if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) ||
(options.action != ACTION_NONE) ||
- (exec.nr > 0) ||
- options.autosquash) {
+ (options.exec.nr > 0) ||
+ (options.autosquash == -1 && options.config_autosquash == 1) ||
+ options.autosquash == 1) {
allow_preemptive_ff = 0;
}
if (options.committer_date_is_author_date || options.ignore_date)
@@ -1401,8 +1398,8 @@
}
}
- for (i = 0; i < exec.nr; i++)
- if (check_exec_cmd(exec.items[i].string))
+ for (i = 0; i < options.exec.nr; i++)
+ if (check_exec_cmd(options.exec.items[i].string))
exit(1);
if (!(options.flags & REBASE_NO_QUIET))
@@ -1411,27 +1408,34 @@
if (options.empty != EMPTY_UNSPECIFIED)
imply_merge(&options, "--empty");
- /*
- * --keep-base implements --reapply-cherry-picks by altering upstream so
- * it works with both backends.
- */
- if (options.reapply_cherry_picks && !keep_base)
- imply_merge(&options, "--reapply-cherry-picks");
+ if (options.reapply_cherry_picks < 0)
+ /*
+ * We default to --no-reapply-cherry-picks unless
+ * --keep-base is given; when --keep-base is given, we want
+ * to default to --reapply-cherry-picks.
+ */
+ options.reapply_cherry_picks = keep_base;
+ else if (!keep_base)
+ /*
+ * The apply backend always searches for and drops cherry
+ * picks. This is often not wanted with --keep-base, so
+ * --keep-base allows --reapply-cherry-picks to be
+ * simulated by altering the upstream such that
+ * cherry-picks cannot be detected and thus all commits are
+ * reapplied. Thus, --[no-]reapply-cherry-picks is
+ * supported when --keep-base is specified, but not when
+ * --keep-base is left out.
+ */
+ imply_merge(&options, options.reapply_cherry_picks ?
+ "--reapply-cherry-picks" :
+ "--no-reapply-cherry-picks");
if (gpg_sign)
options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
- if (exec.nr) {
- int i;
-
+ if (options.exec.nr)
imply_merge(&options, "--exec");
- strbuf_reset(&buf);
- for (i = 0; i < exec.nr; i++)
- strbuf_addf(&buf, "exec %s\n", exec.items[i].string);
- options.cmd = xstrdup(buf.buf);
- }
-
if (rebase_merges) {
if (!*rebase_merges)
; /* default mode; do nothing */
@@ -1504,15 +1508,29 @@
if (strcmp(options.git_am_opts.v[i], "-q"))
break;
- if (i >= 0) {
+ if (i >= 0 || options.type == REBASE_APPLY) {
if (is_merge(&options))
die(_("apply options and merge options "
"cannot be used together"));
+ else if (options.autosquash == -1 && options.config_autosquash == 1)
+ die(_("apply options are incompatible with rebase.autosquash. Consider adding --no-autosquash"));
+ else if (options.update_refs == -1 && options.config_update_refs == 1)
+ die(_("apply options are incompatible with rebase.updateRefs. Consider adding --no-update-refs"));
else
options.type = REBASE_APPLY;
}
}
+ if (options.update_refs == 1)
+ imply_merge(&options, "--update-refs");
+ options.update_refs = (options.update_refs >= 0) ? options.update_refs :
+ ((options.config_update_refs >= 0) ? options.config_update_refs : 0);
+
+ if (options.autosquash == 1)
+ imply_merge(&options, "--autosquash");
+ options.autosquash = (options.autosquash >= 0) ? options.autosquash :
+ ((options.config_autosquash >= 0) ? options.config_autosquash : 0);
+
if (options.type == REBASE_UNSPECIFIED) {
if (!strcmp(options.default_backend, "merge"))
imply_merge(&options, "--merge");
@@ -1542,7 +1560,7 @@
if (options.empty == EMPTY_UNSPECIFIED) {
if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
options.empty = EMPTY_ASK;
- else if (exec.nr > 0)
+ else if (options.exec.nr > 0)
options.empty = EMPTY_KEEP;
else
options.empty = EMPTY_DROP;
@@ -1658,7 +1676,7 @@
strbuf_addstr(&buf, options.upstream_name);
strbuf_addstr(&buf, "...");
strbuf_addstr(&buf, branch_name);
- options.onto_name = xstrdup(buf.buf);
+ options.onto_name = keep_base_onto_name = xstrdup(buf.buf);
} else if (!options.onto_name)
options.onto_name = options.upstream_name;
if (strstr(options.onto_name, "...")) {
@@ -1828,10 +1846,14 @@
strbuf_release(&revisions);
free(options.reflog_action);
free(options.head_name);
+ strvec_clear(&options.git_am_opts);
free(options.gpg_sign_opt);
- free(options.cmd);
+ string_list_clear(&options.exec, 0);
free(options.strategy);
+ free(options.strategy_opts);
strbuf_release(&options.git_format_patch_opt);
free(squash_onto_name);
+ free(keep_base_onto_name);
+ string_list_clear(&strategy_options, 0);
return !!ret;
}
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index a90af30..cd5c7a2 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -2032,6 +2032,16 @@
return &cmd->next;
}
+static void free_commands(struct command *commands)
+{
+ while (commands) {
+ struct command *next = commands->next;
+
+ free(commands);
+ commands = next;
+ }
+}
+
static void queue_commands_from_cert(struct command **tail,
struct strbuf *push_cert)
{
@@ -2569,6 +2579,7 @@
run_receive_hook(commands, "post-receive", 1,
&push_options);
run_update_post_hook(commands);
+ free_commands(commands);
string_list_clear(&push_options, 0);
if (auto_gc) {
struct child_process proc = CHILD_PROCESS_INIT;
diff --git a/builtin/remote-ext.c b/builtin/remote-ext.c
index fd3538d..ee338bf 100644
--- a/builtin/remote-ext.c
+++ b/builtin/remote-ext.c
@@ -169,6 +169,8 @@
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 @@
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 91dfe07..b2a3980 100644
--- a/builtin/remote-fd.c
+++ b/builtin/remote-fd.c
@@ -40,7 +40,7 @@
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/repack.c b/builtin/repack.c
index 65eb1b8..f649379 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -948,7 +948,7 @@
ret = start_command(&cmd);
if (ret)
- return ret;
+ goto cleanup;
if (geometry) {
FILE *in = xfdopen(cmd.in, "w");
@@ -973,10 +973,11 @@
item = string_list_append(&names, line.buf);
item->util = populate_pack_exts(item->string);
}
+ strbuf_release(&line);
fclose(out);
ret = finish_command(&cmd);
if (ret)
- return ret;
+ goto cleanup;
if (!names.nr && !po_args.quiet)
printf_ln(_("Nothing new to pack."));
@@ -1006,7 +1007,7 @@
&existing_nonkept_packs,
&existing_kept_packs);
if (ret)
- return ret;
+ goto cleanup;
if (delete_redundant && expire_to) {
/*
@@ -1038,7 +1039,7 @@
&existing_nonkept_packs,
&existing_kept_packs);
if (ret)
- return ret;
+ goto cleanup;
}
}
@@ -1114,7 +1115,7 @@
string_list_clear(&include, 0);
if (ret)
- return ret;
+ goto cleanup;
}
reprepare_packed_git(the_repository);
@@ -1171,11 +1172,11 @@
write_midx_file(get_object_directory(), NULL, NULL, flags);
}
+cleanup:
string_list_clear(&names, 1);
string_list_clear(&existing_nonkept_packs, 0);
string_list_clear(&existing_kept_packs, 0);
clear_pack_geometry(geometry);
- strbuf_release(&line);
- return 0;
+ return ret;
}
diff --git a/builtin/rerere.c b/builtin/rerere.c
index 8b7392d..94ffb8c 100644
--- a/builtin/rerere.c
+++ b/builtin/rerere.c
@@ -14,7 +14,7 @@
NULL,
};
-static int outf(void *dummy, mmbuffer_t *ptr, int nbuf)
+static int outf(void *dummy UNUSED, mmbuffer_t *ptr, int nbuf)
{
int i;
for (i = 0; i < nbuf; i++)
diff --git a/builtin/reset.c b/builtin/reset.c
index 1fa86ed..0697fa8 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -26,6 +26,7 @@
#include "submodule.h"
#include "submodule-config.h"
#include "dir.h"
+#include "add-interactive.h"
#define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
@@ -133,7 +134,8 @@
}
static void update_index_from_diff(struct diff_queue_struct *q,
- struct diff_options *opt, void *data)
+ struct diff_options *opt UNUSED,
+ void *data)
{
int i;
int intent_to_add = *(int *)data;
@@ -389,7 +391,9 @@
if (reset_type != NONE)
die(_("options '%s' and '%s' cannot be used together"), "--patch", "--{hard,mixed,soft}");
trace2_cmd_mode("patch-interactive");
- return run_add_interactive(rev, "--patch=reset", &pathspec);
+ update_ref_status = !!run_add_p(the_repository, ADD_P_RESET, rev,
+ &pathspec);
+ goto cleanup;
}
/* git reset tree [--] paths... can be used to
@@ -438,8 +442,10 @@
LOCK_DIE_ON_ERROR);
if (reset_type == MIXED) {
int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
- if (read_from_tree(&pathspec, &oid, intent_to_add))
- return 1;
+ if (read_from_tree(&pathspec, &oid, intent_to_add)) {
+ update_ref_status = 1;
+ goto cleanup;
+ }
the_index.updated_skipworktree = 1;
if (!no_refresh && get_git_work_tree()) {
uint64_t t_begin, t_delta_in_ms;
@@ -485,5 +491,9 @@
if (!pathspec.nr)
remove_branch_state(the_repository, 0);
+ discard_index(&the_index);
+
+cleanup:
+ clear_pathspec(&pathspec);
return update_ref_status;
}
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 1c344d7..e67999e 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -530,6 +530,7 @@
strbuf_addstr(&parsed, " --");
sq_quote_argv(&parsed, argv);
puts(parsed.buf);
+ strbuf_release(&parsed);
return 0;
}
diff --git a/builtin/revert.c b/builtin/revert.c
index 6a9b550..77d2035 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -221,6 +221,7 @@
opts->strategy = xstrdup_or_null(opts->strategy);
if (!opts->strategy && getenv("GIT_TEST_MERGE_ALGORITHM"))
opts->strategy = xstrdup(getenv("GIT_TEST_MERGE_ALGORITHM"));
+ free(options);
if (cmd == 'q') {
int ret = sequencer_remove_state(opts);
@@ -247,9 +248,7 @@
res = run_sequencer(argc, argv, &opts);
if (res < 0)
die(_("revert failed"));
- if (opts.revs)
- release_revisions(opts.revs);
- free(opts.revs);
+ replay_opts_release(&opts);
return res;
}
@@ -263,5 +262,6 @@
res = run_sequencer(argc, argv, &opts);
if (res < 0)
die(_("cherry-pick failed"));
+ replay_opts_release(&opts);
return res;
}
diff --git a/builtin/rm.c b/builtin/rm.c
index d4989d4..8844f90 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -3,7 +3,7 @@
*
* Copyright (C) Linus Torvalds 2006
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "advice.h"
#include "config.h"
@@ -86,7 +86,7 @@
continue;
if (!submodule_uses_gitfile(name))
- absorb_git_dir_into_superproject(name);
+ absorb_git_dir_into_superproject(name, NULL);
}
}
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index c013aba..358ac3e 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -956,5 +956,6 @@
if (shown_merge_point && --extra < 0)
break;
}
+ free(head);
return 0;
}
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index 58a2250..c373815 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -217,6 +217,7 @@
o.head_idx = -1;
o.src_index = r->index;
o.dst_index = r->index;
+ index_state_init(&o.result, r);
o.skip_sparse_checkout = 0;
o.pl = pl;
diff --git a/builtin/stash.c b/builtin/stash.c
index 62e3671..3a4f9fd 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1,4 +1,4 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "config.h"
#include "parse-options.h"
@@ -18,6 +18,7 @@
#include "diffcore.h"
#include "exec-cmd.h"
#include "reflog.h"
+#include "add-interactive.h"
#define INCLUDE_ALL_FILES 2
@@ -528,7 +529,8 @@
NULL, NULL, NULL))
return -1;
- if (write_cache_as_tree(&c_tree, 0, NULL))
+ if (write_index_as_tree(&c_tree, &the_index, get_index_file(), 0,
+ NULL))
return error(_("cannot apply a stash in the middle of a merge"));
if (index) {
@@ -552,7 +554,8 @@
discard_index(&the_index);
repo_read_index(the_repository);
- if (write_cache_as_tree(&index_tree, 0, NULL))
+ if (write_index_as_tree(&index_tree, &the_index,
+ get_index_file(), 0, NULL))
return error(_("could not save index tree"));
reset_head();
@@ -1137,7 +1140,7 @@
int ret = 0;
struct strbuf untracked_msg = STRBUF_INIT;
struct child_process cp_upd_index = CHILD_PROCESS_INIT;
- struct index_state istate = { NULL };
+ struct index_state istate = INDEX_STATE_INIT(the_repository);
cp_upd_index.git_cmd = 1;
strvec_pushl(&cp_upd_index.args, "update-index", "-z", "--add",
@@ -1165,7 +1168,7 @@
}
done:
- discard_index(&istate);
+ release_index(&istate);
strbuf_release(&untracked_msg);
remove_path(stash_index_path.buf);
return ret;
@@ -1176,7 +1179,7 @@
{
int ret = 0;
struct child_process cp_diff_tree = CHILD_PROCESS_INIT;
- struct index_state istate = { NULL };
+ struct index_state istate = INDEX_STATE_INIT(the_repository);
if (write_index_as_tree(&info->w_tree, &istate, the_repository->index_file,
0, NULL)) {
@@ -1199,7 +1202,7 @@
}
done:
- discard_index(&istate);
+ release_index(&istate);
return ret;
}
@@ -1209,7 +1212,7 @@
int ret = 0;
struct child_process cp_read_tree = CHILD_PROCESS_INIT;
struct child_process cp_diff_tree = CHILD_PROCESS_INIT;
- struct index_state istate = { NULL };
+ struct index_state istate = INDEX_STATE_INIT(the_repository);
char *old_index_env = NULL, *old_repo_index_file;
remove_path(stash_index_path.buf);
@@ -1229,7 +1232,7 @@
old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);
- ret = run_add_interactive(NULL, "--patch=stash", ps);
+ ret = !!run_add_p(the_repository, ADD_P_STASH, NULL, ps);
the_repository->index_file = old_repo_index_file;
if (old_index_env && *old_index_env)
@@ -1260,7 +1263,7 @@
}
done:
- discard_index(&istate);
+ release_index(&istate);
remove_path(stash_index_path.buf);
return ret;
}
@@ -1271,7 +1274,7 @@
struct rev_info rev;
struct child_process cp_upd_index = CHILD_PROCESS_INIT;
struct strbuf diff_output = STRBUF_INIT;
- struct index_state istate = { NULL };
+ struct index_state istate = INDEX_STATE_INIT(the_repository);
init_revisions(&rev, NULL);
copy_pathspec(&rev.prune_data, ps);
@@ -1319,7 +1322,7 @@
}
done:
- discard_index(&istate);
+ release_index(&istate);
release_revisions(&rev);
strbuf_release(&diff_output);
remove_path(stash_index_path.buf);
@@ -1377,7 +1380,8 @@
strbuf_addf(&commit_tree_label, "index on %s\n", msg.buf);
commit_list_insert(head_commit, &parents);
- if (write_cache_as_tree(&info->i_tree, 0, NULL) ||
+ if (write_index_as_tree(&info->i_tree, &the_index, get_index_file(), 0,
+ NULL) ||
commit_tree(commit_tree_label.buf, commit_tree_label.len,
&info->i_tree, parents, &info->i_commit, NULL, NULL)) {
if (!quiet)
@@ -1689,8 +1693,10 @@
}
done:
+ strbuf_release(&patch);
free_stash_info(&info);
strbuf_release(&stash_msg_buf);
+ strbuf_release(&untracked_files);
return ret;
}
@@ -1725,6 +1731,7 @@
OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
OPT_END()
};
+ int ret;
if (argc) {
force_assume = !strcmp(argv[0], "-p");
@@ -1764,8 +1771,10 @@
die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
}
- return do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
- include_untracked, only_staged);
+ ret = do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
+ include_untracked, only_staged);
+ clear_pathspec(&ps);
+ return ret;
}
static int push_stash_unassumed(int argc, const char **argv, const char *prefix)
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 05f2c9b..4c173d8 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -113,10 +113,9 @@
}
/* 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 @@
int argc;
const char **argv;
const char *prefix;
+ const char *super_prefix;
int quiet;
int recursive;
};
@@ -294,7 +294,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
{
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 @@
struct status_cb {
const char *prefix;
+ const char *super_prefix;
unsigned int flags;
};
#define STATUS_CB_INIT { 0 }
@@ -608,7 +614,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7);
}
- displaypath = get_submodule_displaypath(p->sm_path, info->prefix);
+ displaypath = get_submodule_displaypath(p->sm_path, info->prefix,
+ info->super_prefix);
if (!missing_src && !missing_dst) {
struct child_process cp_rev_list = CHILD_PROCESS_INIT;
@@ -1043,7 +1052,7 @@
}
static void submodule_summary_callback(struct diff_queue_struct *q,
- struct diff_options *options,
+ struct diff_options *options UNUSED,
void *data)
{
int i;
@@ -1203,12 +1212,13 @@
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 @@
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 @@
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 @@
{
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 @@
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 @@
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 @@
".git file by using absorbgitdirs."),
displaypath);
- absorb_git_dir_into_superproject(path);
+ absorb_git_dir_into_superproject(path, NULL);
}
@@ -1883,6 +1896,7 @@
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 @@
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 @@
{
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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/builtin/unpack-file.c b/builtin/unpack-file.c
index 9e8119d..88de32b 100644
--- a/builtin/unpack-file.c
+++ b/builtin/unpack-file.c
@@ -19,6 +19,7 @@
if (write_in_full(fd, buf, size) < 0)
die_errno("unable to write temp-file");
close(fd);
+ free(buf);
return path;
}
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 82d5902..bf38885 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -3,7 +3,7 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "cache.h"
#include "bulk-checkin.h"
#include "config.h"
@@ -381,7 +381,7 @@
if (has_symlink_leading_path(path, len))
return error("'%s' is beyond a symbolic link", path);
- pos = cache_name_pos(path, len);
+ pos = index_name_pos(&the_index, path, len);
ce = pos < 0 ? NULL : the_index.cache[pos];
if (ce && ce_skip_worktree(ce)) {
/*
diff --git a/builtin/var.c b/builtin/var.c
index 491db27..a80c1df 100644
--- a/builtin/var.c
+++ b/builtin/var.c
@@ -11,12 +11,12 @@
static const char *editor(int flag)
{
- const char *pgm = git_editor();
+ return git_editor();
+}
- if (!pgm && flag & IDENT_STRICT)
- die("Terminal is dumb, but EDITOR unset");
-
- return pgm;
+static const char *sequence_editor(int flag)
+{
+ return git_sequence_editor();
}
static const char *pager(int flag)
@@ -41,6 +41,7 @@
{ "GIT_COMMITTER_IDENT", git_committer_info },
{ "GIT_AUTHOR_IDENT", git_author_info },
{ "GIT_EDITOR", editor },
+ { "GIT_SEQUENCE_EDITOR", sequence_editor },
{ "GIT_PAGER", pager },
{ "GIT_DEFAULT_BRANCH", default_branch },
{ "", NULL },
@@ -56,18 +57,15 @@
printf("%s=%s\n", ptr->name, val);
}
-static const char *read_var(const char *var)
+static const struct git_var *get_git_var(const char *var)
{
struct git_var *ptr;
- const char *val;
- val = NULL;
for (ptr = git_vars; ptr->read; ptr++) {
if (strcmp(var, ptr->name) == 0) {
- val = ptr->read(IDENT_STRICT);
- break;
+ return ptr;
}
}
- return val;
+ return NULL;
}
static int show_config(const char *var, const char *value, void *cb)
@@ -81,7 +79,9 @@
int cmd_var(int argc, const char **argv, const char *prefix)
{
- const char *val = NULL;
+ const struct git_var *git_var;
+ const char *val;
+
if (argc != 2)
usage(var_usage);
@@ -91,10 +91,15 @@
return 0;
}
git_config(git_default_config, NULL);
- val = read_var(argv[1]);
- if (!val)
+
+ git_var = get_git_var(argv[1]);
+ if (!git_var)
usage(var_usage);
+ val = git_var->read(IDENT_STRICT);
+ if (!val)
+ return 1;
+
printf("%s\n", val);
return 0;
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 4a24d53..254283a 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -173,7 +173,7 @@
{
struct strbuf reason = STRBUF_INIT;
struct strbuf main_path = STRBUF_INIT;
- struct string_list kept = STRING_LIST_INIT_NODUP;
+ struct string_list kept = STRING_LIST_INIT_DUP;
DIR *dir = opendir(git_path("worktrees"));
struct dirent *d;
if (!dir)
@@ -184,14 +184,14 @@
if (should_prune_worktree(d->d_name, &reason, &path, expire))
prune_worktree(d->d_name, reason.buf);
else if (path)
- string_list_append(&kept, path)->util = xstrdup(d->d_name);
+ string_list_append_nodup(&kept, path)->util = xstrdup(d->d_name);
}
closedir(dir);
strbuf_add_absolute_path(&main_path, get_git_common_dir());
/* massage main worktree absolute path to match 'gitdir' content */
strbuf_strip_suffix(&main_path, "/.");
- string_list_append(&kept, strbuf_detach(&main_path, NULL));
+ string_list_append_nodup(&kept, strbuf_detach(&main_path, NULL));
prune_dups(&kept);
string_list_clear(&kept, 1);
@@ -629,6 +629,7 @@
N_("try to match the new branch name with a remote-tracking branch")),
OPT_END()
};
+ int ret;
memset(&opts, 0, sizeof(opts));
opts.checkout = 1;
@@ -705,9 +706,9 @@
die(_("--[no-]track can only be used if a new branch is created"));
}
- UNLEAK(path);
- UNLEAK(opts);
- return add_worktree(path, branch, &opts);
+ ret = add_worktree(path, branch, &opts);
+ free(path);
+ return ret;
}
static void show_worktree_porcelain(struct worktree *wt, int line_terminator)
@@ -922,7 +923,7 @@
static void validate_no_submodules(const struct worktree *wt)
{
- struct index_state istate = { NULL };
+ struct index_state istate = INDEX_STATE_INIT(the_repository);
struct strbuf path = STRBUF_INIT;
int i, found_submodules = 0;
diff --git a/builtin/write-tree.c b/builtin/write-tree.c
index 45d6170..0780103 100644
--- a/builtin/write-tree.c
+++ b/builtin/write-tree.c
@@ -3,7 +3,7 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "cache.h"
#include "config.h"
@@ -38,7 +38,8 @@
argc = parse_options(argc, argv, cmd_prefix, write_tree_options,
write_tree_usage, 0);
- ret = write_cache_as_tree(&oid, flags, tree_prefix);
+ ret = write_index_as_tree(&oid, &the_index, get_index_file(), flags,
+ tree_prefix);
switch (ret) {
case 0:
printf("%s\n", oid_to_hex(&oid));
diff --git a/bundle-uri.c b/bundle-uri.c
index 79a914f..8a3c39c 100644
--- a/bundle-uri.c
+++ b/bundle-uri.c
@@ -7,6 +7,15 @@
#include "hashmap.h"
#include "pkt-line.h"
#include "config.h"
+#include "remote.h"
+
+static struct {
+ enum bundle_list_heuristic heuristic;
+ const char *name;
+} heuristics[BUNDLE_HEURISTIC__COUNT] = {
+ { BUNDLE_HEURISTIC_NONE, ""},
+ { BUNDLE_HEURISTIC_CREATIONTOKEN, "creationToken" },
+};
static int compare_bundles(const void *hashmap_cmp_fn_data,
const struct hashmap_entry *he1,
@@ -49,6 +58,7 @@
for_all_bundles_in_list(list, clear_remote_bundle_info, NULL);
hashmap_clear_and_free(&list->bundles, struct remote_bundle_info, ent);
+ free(list->baseURI);
}
int for_all_bundles_in_list(struct bundle_list *list,
@@ -73,6 +83,9 @@
FILE *fp = data;
fprintf(fp, "[bundle \"%s\"]\n", info->id);
fprintf(fp, "\turi = %s\n", info->uri);
+
+ if (info->creationToken)
+ fprintf(fp, "\tcreationToken = %"PRIu64"\n", info->creationToken);
return 0;
}
@@ -98,6 +111,17 @@
fprintf(fp, "\tversion = %d\n", list->version);
fprintf(fp, "\tmode = %s\n", mode);
+ if (list->heuristic) {
+ int i;
+ for (i = 0; i < BUNDLE_HEURISTIC__COUNT; i++) {
+ if (heuristics[i].heuristic == list->heuristic) {
+ printf("\theuristic = %s\n",
+ heuristics[list->heuristic].name);
+ break;
+ }
+ }
+ }
+
for_all_bundles_in_list(list, summarize_bundle, fp);
}
@@ -140,6 +164,21 @@
return 0;
}
+ if (!strcmp(subkey, "heuristic")) {
+ int i;
+ for (i = 0; i < BUNDLE_HEURISTIC__COUNT; i++) {
+ if (heuristics[i].heuristic &&
+ heuristics[i].name &&
+ !strcmp(value, heuristics[i].name)) {
+ list->heuristic = heuristics[i].heuristic;
+ return 0;
+ }
+ }
+
+ /* Ignore unknown heuristics. */
+ return 0;
+ }
+
/* Ignore other unknown global keys. */
return 0;
}
@@ -163,7 +202,14 @@
if (!strcmp(subkey, "uri")) {
if (bundle->uri)
return -1;
- bundle->uri = xstrdup(value);
+ bundle->uri = relative_url(list->baseURI, value, NULL);
+ return 0;
+ }
+
+ if (!strcmp(subkey, "creationtoken")) {
+ if (sscanf(value, "%"PRIu64, &bundle->creationToken) != 1)
+ warning(_("could not parse bundle list key %s with value '%s'"),
+ "creationToken", value);
return 0;
}
@@ -190,6 +236,18 @@
.error_action = CONFIG_ERROR_ERROR,
};
+ if (!list->baseURI) {
+ struct strbuf baseURI = STRBUF_INIT;
+ strbuf_addstr(&baseURI, uri);
+
+ /*
+ * If the URI does not end with a trailing slash, then
+ * remove the filename portion of the path. This is
+ * important for relative URIs.
+ */
+ strbuf_strip_file_from_path(&baseURI);
+ list->baseURI = strbuf_detach(&baseURI, NULL);
+ }
result = git_config_from_file_with_options(config_to_bundle_list,
filename, list,
&opts);
@@ -389,6 +447,183 @@
return 0;
}
+struct bundles_for_sorting {
+ struct remote_bundle_info **items;
+ size_t alloc;
+ size_t nr;
+};
+
+static int append_bundle(struct remote_bundle_info *bundle, void *data)
+{
+ struct bundles_for_sorting *list = data;
+ list->items[list->nr++] = bundle;
+ return 0;
+}
+
+/**
+ * For use in QSORT() to get a list sorted by creationToken
+ * in decreasing order.
+ */
+static int compare_creation_token_decreasing(const void *va, const void *vb)
+{
+ const struct remote_bundle_info * const *a = va;
+ const struct remote_bundle_info * const *b = vb;
+
+ if ((*a)->creationToken > (*b)->creationToken)
+ return -1;
+ if ((*a)->creationToken < (*b)->creationToken)
+ return 1;
+ return 0;
+}
+
+static int fetch_bundles_by_token(struct repository *r,
+ struct bundle_list *list)
+{
+ int cur;
+ int move_direction = 0;
+ const char *creationTokenStr;
+ uint64_t maxCreationToken = 0, newMaxCreationToken = 0;
+ struct bundle_list_context ctx = {
+ .r = r,
+ .list = list,
+ .mode = list->mode,
+ };
+ struct bundles_for_sorting bundles = {
+ .alloc = hashmap_get_size(&list->bundles),
+ };
+
+ ALLOC_ARRAY(bundles.items, bundles.alloc);
+
+ for_all_bundles_in_list(list, append_bundle, &bundles);
+
+ if (!bundles.nr) {
+ free(bundles.items);
+ return 0;
+ }
+
+ QSORT(bundles.items, bundles.nr, compare_creation_token_decreasing);
+
+ /*
+ * If fetch.bundleCreationToken exists, parses to a uint64t, and
+ * is not strictly smaller than the maximum creation token in the
+ * bundle list, then do not download any bundles.
+ */
+ if (!repo_config_get_value(r,
+ "fetch.bundlecreationtoken",
+ &creationTokenStr) &&
+ sscanf(creationTokenStr, "%"PRIu64, &maxCreationToken) == 1 &&
+ bundles.items[0]->creationToken <= maxCreationToken) {
+ free(bundles.items);
+ return 0;
+ }
+
+ /*
+ * Attempt to download and unbundle the minimum number of bundles by
+ * creationToken in decreasing order. If we fail to unbundle (after
+ * a successful download) then move to the next non-downloaded bundle
+ * and attempt downloading. Once we succeed in applying a bundle,
+ * move to the previous unapplied bundle and attempt to unbundle it
+ * again.
+ *
+ * In the case of a fresh clone, we will likely download all of the
+ * bundles before successfully unbundling the oldest one, then the
+ * rest of the bundles unbundle successfully in increasing order
+ * of creationToken.
+ *
+ * If there are existing objects, then this process may terminate
+ * early when all required commits from "new" bundles exist in the
+ * repo's object store.
+ */
+ cur = 0;
+ while (cur >= 0 && cur < bundles.nr) {
+ struct remote_bundle_info *bundle = bundles.items[cur];
+
+ /*
+ * If we need to dig into bundles below the previous
+ * creation token value, then likely we are in an erroneous
+ * state due to missing or invalid bundles. Halt the process
+ * instead of continuing to download extra data.
+ */
+ if (bundle->creationToken <= maxCreationToken)
+ break;
+
+ if (!bundle->file) {
+ /*
+ * Not downloaded yet. Try downloading.
+ *
+ * Note that bundle->file is non-NULL if a download
+ * was attempted, even if it failed to download.
+ */
+ if (fetch_bundle_uri_internal(ctx.r, bundle, ctx.depth + 1, ctx.list)) {
+ /* Mark as unbundled so we do not retry. */
+ bundle->unbundled = 1;
+
+ /* Try looking deeper in the list. */
+ move_direction = 1;
+ goto move;
+ }
+
+ /* We expect bundles when using creationTokens. */
+ if (!is_bundle(bundle->file, 1)) {
+ warning(_("file downloaded from '%s' is not a bundle"),
+ bundle->uri);
+ break;
+ }
+ }
+
+ if (bundle->file && !bundle->unbundled) {
+ /*
+ * This was downloaded, but not successfully
+ * unbundled. Try unbundling again.
+ */
+ if (unbundle_from_file(ctx.r, bundle->file)) {
+ /* Try looking deeper in the list. */
+ move_direction = 1;
+ } else {
+ /*
+ * Succeeded in unbundle. Retry bundles
+ * that previously failed to unbundle.
+ */
+ move_direction = -1;
+ bundle->unbundled = 1;
+
+ if (bundle->creationToken > newMaxCreationToken)
+ newMaxCreationToken = bundle->creationToken;
+ }
+ }
+
+ /*
+ * Else case: downloaded and unbundled successfully.
+ * Skip this by moving in the same direction as the
+ * previous step.
+ */
+
+move:
+ /* Move in the specified direction and repeat. */
+ cur += move_direction;
+ }
+
+ /*
+ * We succeed if the loop terminates because 'cur' drops below
+ * zero. The other case is that we terminate because 'cur'
+ * reaches the end of the list, so we have a failure no matter
+ * which bundles we apply from the list.
+ */
+ if (cur < 0) {
+ struct strbuf value = STRBUF_INIT;
+ strbuf_addf(&value, "%"PRIu64"", newMaxCreationToken);
+ if (repo_config_set_multivar_gently(ctx.r,
+ "fetch.bundleCreationToken",
+ value.buf, NULL, 0))
+ warning(_("failed to store maximum creation token"));
+
+ strbuf_release(&value);
+ }
+
+ free(bundles.items);
+ return cur >= 0;
+}
+
static int download_bundle_list(struct repository *r,
struct bundle_list *local_list,
struct bundle_list *global_list,
@@ -426,7 +661,15 @@
goto cleanup;
}
- if ((result = download_bundle_list(r, &list_from_bundle,
+ /*
+ * If this list uses the creationToken heuristic, then the URIs
+ * it advertises are expected to be bundles, not nested lists.
+ * We can drop 'global_list' and 'depth'.
+ */
+ if (list_from_bundle.heuristic == BUNDLE_HEURISTIC_CREATIONTOKEN) {
+ result = fetch_bundles_by_token(r, &list_from_bundle);
+ global_list->heuristic = BUNDLE_HEURISTIC_CREATIONTOKEN;
+ } else if ((result = download_bundle_list(r, &list_from_bundle,
global_list, depth)))
goto cleanup;
@@ -537,7 +780,8 @@
return 0;
}
-int fetch_bundle_uri(struct repository *r, const char *uri)
+int fetch_bundle_uri(struct repository *r, const char *uri,
+ int *has_heuristic)
{
int result;
struct bundle_list list;
@@ -557,12 +801,96 @@
result = unbundle_all_bundles(r, &list);
cleanup:
+ if (has_heuristic)
+ *has_heuristic = (list.heuristic != BUNDLE_HEURISTIC_NONE);
for_all_bundles_in_list(&list, unlink_bundle, NULL);
clear_bundle_list(&list);
clear_remote_bundle_info(&bundle, NULL);
return result;
}
+int fetch_bundle_list(struct repository *r, struct bundle_list *list)
+{
+ int result;
+ struct bundle_list global_list;
+
+ /*
+ * If the creationToken heuristic is used, then the URIs
+ * advertised by 'list' are not nested lists and instead
+ * direct bundles. We do not need to use global_list.
+ */
+ if (list->heuristic == BUNDLE_HEURISTIC_CREATIONTOKEN)
+ return fetch_bundles_by_token(r, list);
+
+ init_bundle_list(&global_list);
+
+ /* If a bundle is added to this global list, then it is required. */
+ global_list.mode = BUNDLE_MODE_ALL;
+
+ if ((result = download_bundle_list(r, list, &global_list, 0)))
+ goto cleanup;
+
+ if (list->heuristic == BUNDLE_HEURISTIC_CREATIONTOKEN)
+ result = fetch_bundles_by_token(r, list);
+ else
+ result = unbundle_all_bundles(r, &global_list);
+
+cleanup:
+ for_all_bundles_in_list(&global_list, unlink_bundle, NULL);
+ clear_bundle_list(&global_list);
+ return result;
+}
+
+/**
+ * API for serve.c.
+ */
+
+int bundle_uri_advertise(struct repository *r, struct strbuf *value UNUSED)
+{
+ static int advertise_bundle_uri = -1;
+
+ if (advertise_bundle_uri != -1)
+ goto cached;
+
+ advertise_bundle_uri = 0;
+ repo_config_get_maybe_bool(r, "uploadpack.advertisebundleuris", &advertise_bundle_uri);
+
+cached:
+ return advertise_bundle_uri;
+}
+
+static int config_to_packet_line(const char *key, const char *value, void *data)
+{
+ struct packet_reader *writer = data;
+
+ if (starts_with(key, "bundle."))
+ packet_write_fmt(writer->fd, "%s=%s", key, value);
+
+ return 0;
+}
+
+int bundle_uri_command(struct repository *r,
+ struct packet_reader *request)
+{
+ struct packet_writer writer;
+ packet_writer_init(&writer, 1);
+
+ while (packet_reader_read(request) == PACKET_READ_NORMAL)
+ die(_("bundle-uri: unexpected argument: '%s'"), request->line);
+ if (request->status != PACKET_READ_FLUSH)
+ die(_("bundle-uri: expected flush after arguments"));
+
+ /*
+ * Read all "bundle.*" config lines to the client as key=value
+ * packet lines.
+ */
+ git_config(config_to_packet_line, &writer);
+
+ packet_writer_flush(&writer);
+
+ return 0;
+}
+
/**
* General API for {transport,connect}.c etc.
*/
diff --git a/bundle-uri.h b/bundle-uri.h
index 4dbc269..6dbc780 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;
@@ -41,6 +42,12 @@
* this boolean is true.
*/
unsigned unbundled:1;
+
+ /**
+ * If the bundle is part of a list with the creationToken
+ * heuristic, then we use this member for sorting the bundles.
+ */
+ uint64_t creationToken;
};
#define REMOTE_BUNDLE_INFO_INIT { 0 }
@@ -51,6 +58,14 @@
BUNDLE_MODE_ANY
};
+enum bundle_list_heuristic {
+ BUNDLE_HEURISTIC_NONE = 0,
+ BUNDLE_HEURISTIC_CREATIONTOKEN,
+
+ /* Must be last. */
+ BUNDLE_HEURISTIC__COUNT
+};
+
/**
* A bundle_list contains an unordered set of remote_bundle_info structs,
* as well as information about the bundle listing, such as version and
@@ -60,6 +75,26 @@
int version;
enum bundle_list_mode mode;
struct hashmap bundles;
+
+ /**
+ * The baseURI of a bundle_list is the URI that provided the list.
+ *
+ * In the case of the 'bundle-uri' protocol v2 command, the base
+ * URI is the URI of the Git remote.
+ *
+ * Otherwise, the bundle list was downloaded over HTTP from some
+ * known URI. 'baseURI' is set to that value.
+ *
+ * The baseURI is used as the base for any relative URIs
+ * advertised by the bundle list at that location.
+ */
+ char *baseURI;
+
+ /**
+ * A list can have a heuristic, which helps reduce the number of
+ * downloaded bundles.
+ */
+ enum bundle_list_heuristic heuristic;
};
void init_bundle_list(struct bundle_list *list);
@@ -89,8 +124,34 @@
* based on that information.
*
* Returns non-zero if no bundle information is found at the given 'uri'.
+ *
+ * If the pointer 'has_heuristic' is non-NULL, then the value it points to
+ * will be set to be non-zero if and only if the fetched list has a
+ * heuristic value. Such a value indicates that the list was designed for
+ * incremental fetches.
*/
-int fetch_bundle_uri(struct repository *r, const char *uri);
+int fetch_bundle_uri(struct repository *r, const char *uri,
+ int *has_heuristic);
+
+/**
+ * Given a bundle list that was already advertised (likely by the
+ * bundle-uri protocol v2 verb) at the given uri, fetch and unbundle the
+ * bundles according to the bundle strategy of that list.
+ *
+ * It is expected that the given 'list' is initialized, including its
+ * 'baseURI' value.
+ *
+ * Returns non-zero if there was an error trying to download the list
+ * or any of its advertised bundles.
+ */
+int fetch_bundle_list(struct repository *r,
+ struct bundle_list *list);
+
+/**
+ * API for serve.c.
+ */
+int 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/bundle.c b/bundle.c
index 4ef7256..6ab6cd7 100644
--- a/bundle.c
+++ b/bundle.c
@@ -12,6 +12,7 @@
#include "refs.h"
#include "strvec.h"
#include "list-objects-filter-options.h"
+#include "connected.h"
static const char v2_bundle_signature[] = "# v2 git bundle\n";
static const char v3_bundle_signature[] = "# v3 git bundle\n";
@@ -187,6 +188,21 @@
/* Remember to update object flag allocation in object.h */
#define PREREQ_MARK (1u<<16)
+struct string_list_iterator {
+ struct string_list *list;
+ size_t cur;
+};
+
+static const struct object_id *iterate_ref_map(void *cb_data)
+{
+ struct string_list_iterator *iter = cb_data;
+
+ if (iter->cur >= iter->list->nr)
+ return NULL;
+
+ return iter->list->items[iter->cur++].util;
+}
+
int verify_bundle(struct repository *r,
struct bundle_header *header,
enum verify_bundle_flags flags)
@@ -196,26 +212,25 @@
* to be verbose about the errors
*/
struct string_list *p = &header->prerequisites;
- struct rev_info revs = REV_INFO_INIT;
- const char *argv[] = {NULL, "--all", NULL};
- struct commit *commit;
- int i, ret = 0, req_nr;
+ int i, ret = 0;
const char *message = _("Repository lacks these prerequisite commits:");
+ struct string_list_iterator iter = {
+ .list = p,
+ };
+ struct check_connected_options opts = {
+ .quiet = 1,
+ };
if (!r || !r->objects || !r->objects->odb)
return error(_("need a repository to verify a bundle"));
- repo_init_revisions(r, &revs, NULL);
for (i = 0; i < p->nr; i++) {
struct string_list_item *e = p->items + i;
const char *name = e->string;
struct object_id *oid = e->util;
struct object *o = parse_object(r, oid);
- if (o) {
- o->flags |= PREREQ_MARK;
- add_pending_object(&revs, o, name);
+ if (o)
continue;
- }
ret++;
if (flags & VERIFY_BUNDLE_QUIET)
continue;
@@ -223,37 +238,14 @@
error("%s", message);
error("%s %s", oid_to_hex(oid), name);
}
- if (revs.pending.nr != p->nr)
+ if (ret)
goto cleanup;
- req_nr = revs.pending.nr;
- setup_revisions(2, argv, &revs, NULL);
- list_objects_filter_copy(&revs.filter, &header->filter);
+ if ((ret = check_connected(iterate_ref_map, &iter, &opts)))
+ error(_("some prerequisite commits exist in the object store, "
+ "but are not connected to the repository's history"));
- if (prepare_revision_walk(&revs))
- die(_("revision walk setup failed"));
-
- i = req_nr;
- while (i && (commit = get_revision(&revs)))
- if (commit->object.flags & PREREQ_MARK)
- i--;
-
- for (i = 0; i < p->nr; i++) {
- struct string_list_item *e = p->items + i;
- const char *name = e->string;
- const struct object_id *oid = e->util;
- struct object *o = parse_object(r, oid);
- assert(o); /* otherwise we'd have returned early */
- if (o->flags & SHOWN)
- continue;
- ret++;
- if (flags & VERIFY_BUNDLE_QUIET)
- continue;
- if (ret == 1)
- error("%s", message);
- error("%s %s", oid_to_hex(oid), name);
- }
-
+ /* TODO: preserve this verbose language. */
if (flags & VERIFY_BUNDLE_VERBOSE) {
struct string_list *r;
@@ -282,15 +274,6 @@
list_objects_filter_spec(&header->filter));
}
cleanup:
- /* Clean up objects used, as they will be reused. */
- for (i = 0; i < p->nr; i++) {
- struct string_list_item *e = p->items + i;
- struct object_id *oid = e->util;
- commit = lookup_commit_reference_gently(r, oid, 1);
- if (commit)
- clear_commit_marks(commit, ALL_REV_FLAGS | PREREQ_MARK);
- }
- release_revisions(&revs);
return ret;
}
@@ -627,6 +610,10 @@
enum verify_bundle_flags flags)
{
struct child_process ip = CHILD_PROCESS_INIT;
+
+ if (verify_bundle(r, header, flags))
+ return -1;
+
strvec_pushl(&ip.args, "index-pack", "--fix-thin", "--stdin", NULL);
/* If there is a filter, then we need to create the promisor pack. */
@@ -638,8 +625,6 @@
strvec_clear(extra_index_pack_args);
}
- if (verify_bundle(r, header, flags))
- return -1;
ip.in = bundle_fd;
ip.no_stdout = 1;
ip.git_cmd = 1;
diff --git a/cache-tree.c b/cache-tree.c
index c97111c..88c2c04 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -405,7 +405,7 @@
}
/*
- * CE_INTENT_TO_ADD entries exist on on-disk index but
+ * CE_INTENT_TO_ADD entries exist in on-disk index but
* they are not part of generated trees. Invalidate up
* to root to force cache-tree users to read elsewhere.
*/
@@ -760,7 +760,7 @@
struct tree_desc desc;
struct name_entry entry;
int cnt;
- int base_path_len = tree_path->len;
+ size_t base_path_len = tree_path->len;
oidcpy(&it->oid, &tree->object.oid);
@@ -785,7 +785,6 @@
*/
if (r->index->sparse_index) {
strbuf_setlen(tree_path, base_path_len);
- strbuf_grow(tree_path, base_path_len + entry.pathlen + 1);
strbuf_add(tree_path, entry.path, entry.pathlen);
strbuf_addch(tree_path, '/');
}
diff --git a/cache-tree.h b/cache-tree.h
index 8efecce..bd97caa 100644
--- a/cache-tree.h
+++ b/cache-tree.h
@@ -53,19 +53,4 @@
void prime_cache_tree(struct repository *, struct index_state *, struct tree *);
int cache_tree_matches_traversal(struct cache_tree *, struct name_entry *ent, struct traverse_info *info);
-
-#ifdef USE_THE_INDEX_COMPATIBILITY_MACROS
-static inline int write_cache_as_tree(struct object_id *oid, int flags, const char *prefix)
-{
- return write_index_as_tree(oid, &the_index, get_index_file(), flags, prefix);
-}
-
-static inline int update_main_cache_tree(int flags)
-{
- if (!the_index.cache_tree)
- the_index.cache_tree = cache_tree();
- return cache_tree_update(&the_index, flags);
-}
-#endif
-
#endif
diff --git a/cache.h b/cache.h
index 07d40b0..1278990 100644
--- a/cache.h
+++ b/cache.h
@@ -360,6 +360,22 @@
struct pattern_list *sparse_checkout_patterns;
};
+/**
+ * A "struct index_state istate" must be initialized with
+ * INDEX_STATE_INIT or the corresponding index_state_init().
+ *
+ * If the variable won't be used again, use release_index() to free()
+ * its resources. If it needs to be used again use discard_index(),
+ * which does the same thing, but will use use index_state_init() at
+ * the end. The discard_index() will use its own "istate->repo" as the
+ * "r" argument to index_state_init() in that case.
+ */
+#define INDEX_STATE_INIT(r) { \
+ .repo = (r), \
+}
+void index_state_init(struct index_state *istate, struct repository *r);
+void release_index(struct index_state *istate);
+
/* Name hashing */
int test_lazy_init_name_hash(struct index_state *istate, int try_threaded);
void add_name_hash(struct index_state *istate, struct cache_entry *ce);
@@ -433,18 +449,8 @@
void prefetch_cache_entries(const struct index_state *istate,
must_prefetch_predicate must_prefetch);
-#if defined(USE_THE_INDEX_COMPATIBILITY_MACROS) || defined(USE_THE_INDEX_VARIABLE)
+#ifdef USE_THE_INDEX_VARIABLE
extern struct index_state the_index;
-
-#ifndef USE_THE_INDEX_VARIABLE
-#ifdef USE_THE_INDEX_COMPATIBILITY_MACROS
-#define active_nr (the_index.cache_nr)
-
-#define read_cache() repo_read_index(the_repository)
-#define discard_cache() discard_index(&the_index)
-#define cache_name_pos(name, namelen) index_name_pos(&the_index,(name),(namelen))
-#endif
-#endif
#endif
#define TYPE_BITS 3
@@ -480,7 +486,6 @@
#define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
#define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
#define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
-#define GIT_SUPER_PREFIX_ENVIRONMENT "GIT_INTERNAL_SUPER_PREFIX"
#define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
#define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
#define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
@@ -566,7 +571,6 @@
int get_common_dir(struct strbuf *sb, const char *gitdir);
const char *get_git_namespace(void);
const char *strip_namespace(const char *namespaced_ref);
-const char *get_super_prefix(void);
const char *get_git_work_tree(void);
/*
@@ -1609,8 +1613,10 @@
int validate_headref(const char *ref);
-int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
-int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
+int base_name_compare(const char *name1, size_t len1, int mode1,
+ const char *name2, size_t len2, int mode2);
+int df_name_compare(const char *name1, size_t len1, int mode1,
+ const char *name2, size_t len2, int mode2);
int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);
int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);
@@ -1865,7 +1871,7 @@
void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws);
char *whitespace_error_string(unsigned ws);
void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
-int ws_blank_line(const char *line, int len, unsigned ws_rule);
+int ws_blank_line(const char *line, int len);
#define ws_tab_width(rule) ((rule) & WS_TAB_WIDTH_MASK)
/* ls-files */
diff --git a/ci/lib.sh b/ci/lib.sh
index 706e3ba..db7105e 100755
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -258,8 +258,7 @@
MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python3)"
else
MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python2)"
- MAKEFLAGS="$MAKEFLAGS NO_APPLE_COMMON_CRYPTO=NoThanks"
- MAKEFLAGS="$MAKEFLAGS NO_OPENSSL=NoThanks"
+ MAKEFLAGS="$MAKEFLAGS APPLE_COMMON_CRYPTO_SHA1=Yes"
fi
;;
esac
diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh
index 8ebff42..b098e10 100755
--- a/ci/run-build-and-tests.sh
+++ b/ci/run-build-and-tests.sh
@@ -26,7 +26,6 @@
export GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=1
export GIT_TEST_MULTI_PACK_INDEX=1
export GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=1
- export GIT_TEST_ADD_I_USE_BUILTIN=0
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
export GIT_TEST_WRITE_REV_INDEX=1
export GIT_TEST_CHECKOUT_WORKERS=2
diff --git a/column.c b/column.c
index 1261e18..fbf8863 100644
--- a/column.c
+++ b/column.c
@@ -23,7 +23,7 @@
/* 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/combine-diff.c b/combine-diff.c
index b0ece95..1a39b5d 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -372,7 +372,7 @@
static void consume_hunk(void *state_,
long ob, long on,
long nb, long nn,
- const char *funcline, long funclen)
+ const char *func UNUSED, long funclen UNUSED)
{
struct combine_diff_state *state = state_;
diff --git a/commit-graph.c b/commit-graph.c
index a7d8755..c11b59f 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -1594,8 +1594,7 @@
_("Computing commit changed paths Bloom filters"),
ctx->commits.nr);
- ALLOC_ARRAY(sorted_commits, ctx->commits.nr);
- COPY_ARRAY(sorted_commits, ctx->commits.list, ctx->commits.nr);
+ DUP_ARRAY(sorted_commits, ctx->commits.list, ctx->commits.nr);
if (ctx->order_by_pack)
QSORT(sorted_commits, ctx->commits.nr, commit_pos_cmp);
diff --git a/commit-reach.c b/commit-reach.c
index c226ee3..2e33c59 100644
--- a/commit-reach.c
+++ b/commit-reach.c
@@ -245,8 +245,7 @@
* min_gen_pos points to the current position within 'array'
* that is not yet known to be STALE.
*/
- ALLOC_ARRAY(sorted, cnt);
- COPY_ARRAY(sorted, array, cnt);
+ DUP_ARRAY(sorted, array, cnt);
QSORT(sorted, cnt, compare_commits_by_gen);
min_generation = commit_graph_generation(sorted[0]);
diff --git a/commit.c b/commit.c
index 572301b..e433c33 100644
--- a/commit.c
+++ b/commit.c
@@ -508,6 +508,17 @@
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 @@
return 0;
if (use_commit_graph && parse_commit_in_graph(r, item))
return 0;
- buffer = repo_read_object_file(r, &item->object.oid, &type, &size);
- if (!buffer)
+
+ if (oid_object_info_extended(r, &item->object.oid, &oi, flags) < 0)
return quiet_on_missing ? -1 :
error("Could not read %s",
oid_to_hex(&item->object.oid));
@@ -701,8 +712,10 @@
if (!parents)
return;
- while ((parents = parents->next))
- commit_list_insert(parents->item, plist);
+ while ((parents = parents->next)) {
+ if (parents->item->object.flags & mark)
+ commit_list_insert(parents->item, plist);
+ }
commit = commit->parents->item;
}
@@ -1020,6 +1033,7 @@
ret = bases->item;
cleanup_return:
+ free(revs.commit);
free_commit_list(bases);
free(full_refname);
return ret;
diff --git a/commit.h b/commit.h
index fa39202..cc2c5da 100644
--- a/commit.h
+++ b/commit.h
@@ -274,8 +274,6 @@
int for_each_commit_graft(each_commit_graft_fn, void *);
int interactive_add(const char **argv, const char *prefix, int patch);
-int run_add_interactive(const char *revision, const char *patch_mode,
- const struct pathspec *pathspec);
struct commit_extra_header {
struct commit_extra_header *next;
diff --git a/compat/fsmonitor/fsm-darwin-gcc.h b/compat/fsmonitor/fsm-darwin-gcc.h
index 1c75c3d..3496e29 100644
--- a/compat/fsmonitor/fsm-darwin-gcc.h
+++ b/compat/fsmonitor/fsm-darwin-gcc.h
@@ -80,9 +80,7 @@
void CFRunLoopStop(CFRunLoopRef run_loop);
CFRunLoopRef CFRunLoopGetCurrent(void);
extern CFStringRef kCFRunLoopDefaultMode;
-void FSEventStreamScheduleWithRunLoop(FSEventStreamRef stream,
- CFRunLoopRef run_loop,
- CFStringRef run_loop_mode);
+void FSEventStreamSetDispatchQueue(FSEventStreamRef stream, dispatch_queue_t q);
unsigned char FSEventStreamStart(FSEventStreamRef stream);
void FSEventStreamStop(FSEventStreamRef stream);
void FSEventStreamInvalidate(FSEventStreamRef stream);
diff --git a/compat/fsmonitor/fsm-listen-darwin.c b/compat/fsmonitor/fsm-listen-darwin.c
index cc9af1e..97a55a6 100644
--- a/compat/fsmonitor/fsm-listen-darwin.c
+++ b/compat/fsmonitor/fsm-listen-darwin.c
@@ -1,4 +1,5 @@
#ifndef __clang__
+#include <dispatch/dispatch.h>
#include "fsm-darwin-gcc.h"
#else
#include <CoreFoundation/CoreFoundation.h>
@@ -38,7 +39,9 @@
FSEventStreamRef stream;
- CFRunLoopRef rl;
+ dispatch_queue_t dq;
+ pthread_cond_t dq_finished;
+ pthread_mutex_t dq_lock;
enum shutdown_style {
SHUTDOWN_EVENT = 0,
@@ -379,8 +382,11 @@
fsmonitor_batch__free_list(batch);
string_list_clear(&cookie_list, 0);
+ pthread_mutex_lock(&data->dq_lock);
data->shutdown_style = FORCE_SHUTDOWN;
- CFRunLoopStop(data->rl);
+ pthread_cond_broadcast(&data->dq_finished);
+ pthread_mutex_unlock(&data->dq_lock);
+
strbuf_release(&tmp);
return;
}
@@ -441,10 +447,6 @@
if (!data->stream)
goto failed;
- /*
- * `data->rl` needs to be set inside the listener thread.
- */
-
return 0;
failed:
@@ -471,6 +473,11 @@
FSEventStreamRelease(data->stream);
}
+ if (data->dq)
+ dispatch_release(data->dq);
+ pthread_cond_destroy(&data->dq_finished);
+ pthread_mutex_destroy(&data->dq_lock);
+
FREE_AND_NULL(state->listen_data);
}
@@ -479,9 +486,11 @@
struct fsm_listen_data *data;
data = state->listen_data;
- data->shutdown_style = SHUTDOWN_EVENT;
- CFRunLoopStop(data->rl);
+ pthread_mutex_lock(&data->dq_lock);
+ data->shutdown_style = SHUTDOWN_EVENT;
+ pthread_cond_broadcast(&data->dq_finished);
+ pthread_mutex_unlock(&data->dq_lock);
}
void fsm_listen__loop(struct fsmonitor_daemon_state *state)
@@ -490,9 +499,11 @@
data = state->listen_data;
- data->rl = CFRunLoopGetCurrent();
+ pthread_mutex_init(&data->dq_lock, NULL);
+ pthread_cond_init(&data->dq_finished, NULL);
+ data->dq = dispatch_queue_create("FSMonitor", NULL);
- FSEventStreamScheduleWithRunLoop(data->stream, data->rl, kCFRunLoopDefaultMode);
+ FSEventStreamSetDispatchQueue(data->stream, data->dq);
data->stream_scheduled = 1;
if (!FSEventStreamStart(data->stream)) {
@@ -501,7 +512,9 @@
}
data->stream_started = 1;
- CFRunLoopRun();
+ pthread_mutex_lock(&data->dq_lock);
+ pthread_cond_wait(&data->dq_finished, &data->dq_lock);
+ pthread_mutex_unlock(&data->dq_lock);
switch (data->shutdown_style) {
case FORCE_ERROR_STOP:
diff --git a/compat/mingw.c b/compat/mingw.c
index d614f15..e433740 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -1396,8 +1396,7 @@
p += s;
}
- ALLOC_ARRAY(result, size);
- COPY_ARRAY(result, wenv, size);
+ DUP_ARRAY(result, wenv, size);
FreeEnvironmentStringsW(wenv);
return result;
}
@@ -1839,16 +1838,13 @@
if (prog) {
int exec_id;
int argc = 0;
-#ifndef _MSC_VER
- const
-#endif
char **argv2;
while (argv[argc]) argc++;
ALLOC_ARRAY(argv2, argc + 1);
argv2[0] = (char *)cmd; /* full path to the script file */
COPY_ARRAY(&argv2[1], &argv[1], argc);
- exec_id = trace2_exec(prog, argv2);
- pid = mingw_spawnv(prog, argv2, 1);
+ exec_id = trace2_exec(prog, (const char **)argv2);
+ pid = mingw_spawnv(prog, (const char **)argv2, 1);
if (pid >= 0) {
int status;
if (waitpid(pid, &status, 0) < 0)
@@ -2752,7 +2748,7 @@
/*
* On FAT32 volumes, ownership is not actually recorded.
*/
- strbuf_addf(report, "'%s' is on a file system that does"
+ strbuf_addf(report, "'%s' is on a file system that does "
"not record ownership\n", path);
} else if (report) {
LPSTR str1, str2, to_free1 = NULL, to_free2 = NULL;
diff --git a/compat/regcomp_enhanced.c b/compat/regcomp_enhanced.c
new file mode 100644
index 0000000..84193ce
--- /dev/null
+++ b/compat/regcomp_enhanced.c
@@ -0,0 +1,9 @@
+#include "../git-compat-util.h"
+#undef regcomp
+
+int git_regcomp(regex_t *preg, const char *pattern, int cflags)
+{
+ if (!(cflags & REG_EXTENDED))
+ cflags |= REG_ENHANCED;
+ return regcomp(preg, pattern, cflags);
+}
diff --git a/compat/win32/pthread.c b/compat/win32/pthread.c
index 2e7eead..85f8f79 100644
--- a/compat/win32/pthread.c
+++ b/compat/win32/pthread.c
@@ -22,12 +22,12 @@
}
int pthread_create(pthread_t *thread, const void *unused,
- void *(*start_routine)(void*), void *arg)
+ void *(*start_routine)(void *), void *arg)
{
thread->arg = arg;
thread->start_routine = start_routine;
- thread->handle = (HANDLE)
- _beginthreadex(NULL, 0, win32_start_routine, thread, 0, NULL);
+ thread->handle = (HANDLE)_beginthreadex(NULL, 0, win32_start_routine,
+ thread, 0, NULL);
if (!thread->handle)
return errno;
@@ -39,14 +39,17 @@
{
DWORD result = WaitForSingleObject(thread->handle, INFINITE);
switch (result) {
- case WAIT_OBJECT_0:
- if (value_ptr)
- *value_ptr = thread->arg;
- return 0;
- case WAIT_ABANDONED:
- return EINVAL;
- default:
- return err_win_to_posix(GetLastError());
+ case WAIT_OBJECT_0:
+ if (value_ptr)
+ *value_ptr = thread->arg;
+ CloseHandle(thread->handle);
+ return 0;
+ case WAIT_ABANDONED:
+ CloseHandle(thread->handle);
+ return EINVAL;
+ default:
+ /* the wait failed, so do not detach */
+ return err_win_to_posix(GetLastError());
}
}
diff --git a/compat/win32/pthread.h b/compat/win32/pthread.h
index 737983d..cc3221c 100644
--- a/compat/win32/pthread.h
+++ b/compat/win32/pthread.h
@@ -66,7 +66,7 @@
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/compat/winansi.c b/compat/winansi.c
index 3abe8dd..f83610f 100644
--- a/compat/winansi.c
+++ b/compat/winansi.c
@@ -644,7 +644,7 @@
/* start console spool thread on the pipe's read end */
hthread = CreateThread(NULL, 0, console_thread, NULL, 0, NULL);
- if (hthread == INVALID_HANDLE_VALUE)
+ if (!hthread)
die_lasterr("CreateThread(console_thread) failed");
/* schedule cleanup routine */
diff --git a/config.c b/config.c
index 27f3828..00090a3 100644
--- a/config.c
+++ b/config.c
@@ -3154,7 +3154,7 @@
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/config.h b/config.h
index ef9eade..7606246 100644
--- a/config.h
+++ b/config.h
@@ -448,15 +448,6 @@
int git_configset_add_file(struct config_set *cs, const char *filename);
/**
- * Parses command line options and environment variables, and adds the
- * variable-value pairs to the `config_set`. Returns 0 on success, or -1
- * if there is an error in parsing. The caller decides whether to free
- * the incomplete configset or continue using it when the function
- * returns -1.
- */
-int git_configset_add_parameters(struct config_set *cs);
-
-/**
* Finds and returns the value list, sorted in order of increasing priority
* for the configuration variable `key` and config set `cs`. When the
* configuration variable `key` is not found, returns NULL. The caller
diff --git a/config.mak.uname b/config.mak.uname
index d63629f..64c44db 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -147,6 +147,7 @@
FREAD_READS_DIRECTORIES = UnfortunatelyYes
HAVE_NS_GET_EXECUTABLE_PATH = YesPlease
CSPRNG_METHOD = arc4random
+ USE_ENHANCED_BASIC_REGULAR_EXPRESSIONS = YesPlease
# Workaround for `gettext` being keg-only and not even being linked via
# `brew link --force gettext`, should be obsolete as of
@@ -623,6 +624,9 @@
SHELL_PATH = /usr/coreutils/bin/bash
endif
ifeq ($(uname_S),MINGW)
+ ifeq ($(shell expr "$(uname_R)" : '1\.'),2)
+ $(error "Building with MSys is no longer supported")
+ endif
pathsep = ;
HAVE_ALLOCA_H = YesPlease
NO_PREAD = YesPlease
@@ -652,7 +656,6 @@
USE_WIN32_IPC = YesPlease
USE_WIN32_MMAP = YesPlease
MMAP_PREVENTS_DELETE = UnfortunatelyYes
- USE_NED_ALLOCATOR = YesPlease
UNRELIABLE_FSTAT = UnfortunatelyYes
OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
NO_REGEX = YesPlease
@@ -677,61 +680,43 @@
RC = windres -O coff
NATIVE_CRLF = YesPlease
X = .exe
-ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
- htmldir = doc/git/html/
- prefix =
+ # MSys2
+ prefix = /usr/
+ # Enable DEP
+ BASIC_LDFLAGS += -Wl,--nxcompat
+ # Enable ASLR (unless debugging)
+ ifneq (,$(findstring -O,$(filter-out -O0 -Og,$(CFLAGS))))
+ BASIC_LDFLAGS += -Wl,--dynamicbase
+ endif
+ ifeq (MINGW32,$(MSYSTEM))
+ prefix = /mingw32
+ HOST_CPU = i686
+ BASIC_LDFLAGS += -Wl,--pic-executable,-e,_mainCRTStartup
+ endif
+ ifeq (MINGW64,$(MSYSTEM))
+ prefix = /mingw64
+ HOST_CPU = x86_64
+ BASIC_LDFLAGS += -Wl,--pic-executable,-e,mainCRTStartup
+ else
+ COMPAT_CFLAGS += -D_USE_32BIT_TIME_T
+ BASIC_LDFLAGS += -Wl,--large-address-aware
+ endif
+ CC = gcc
+ COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0 -DDETECT_MSYS_TTY \
+ -fstack-protector-strong
+ EXTLIBS += -lntdll
INSTALL = /bin/install
- EXTLIBS += /mingw/lib/libz.a
INTERNAL_QSORT = YesPlease
HAVE_LIBCHARSET_H = YesPlease
- NO_GETTEXT = YesPlease
- NO_PYTHON = YesPlease
- COMPAT_CFLAGS += -D__USE_MINGW_ACCESS
-else
- ifneq ($(shell expr "$(uname_R)" : '1\.'),2)
- # MSys2
- prefix = /usr/
- # Enable DEP
- BASIC_LDFLAGS += -Wl,--nxcompat
- # Enable ASLR (unless debugging)
- ifneq (,$(findstring -O,$(filter-out -O0 -Og,$(CFLAGS))))
- BASIC_LDFLAGS += -Wl,--dynamicbase
- endif
- ifeq (MINGW32,$(MSYSTEM))
- prefix = /mingw32
- HOST_CPU = i686
- BASIC_LDFLAGS += -Wl,--pic-executable,-e,_mainCRTStartup
- endif
- ifeq (MINGW64,$(MSYSTEM))
- prefix = /mingw64
- HOST_CPU = x86_64
- BASIC_LDFLAGS += -Wl,--pic-executable,-e,mainCRTStartup
- else
- COMPAT_CFLAGS += -D_USE_32BIT_TIME_T
- BASIC_LDFLAGS += -Wl,--large-address-aware
- endif
- CC = gcc
- COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0 -DDETECT_MSYS_TTY \
- -fstack-protector-strong
- EXTLIBS += -lntdll
- INSTALL = /bin/install
- INTERNAL_QSORT = YesPlease
- HAVE_LIBCHARSET_H = YesPlease
- USE_GETTEXT_SCHEME = fallthrough
- USE_LIBPCRE = YesPlease
- USE_NED_ALLOCATOR = YesPlease
- ifeq (/mingw64,$(subst 32,64,$(prefix)))
- # Move system config into top-level /etc/
- ETC_GITCONFIG = ../etc/gitconfig
- ETC_GITATTRIBUTES = ../etc/gitattributes
- endif
- else
- COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO
- NO_CURL = YesPlease
- NO_PYTHON = YesPlease
+ USE_GETTEXT_SCHEME = fallthrough
+ USE_LIBPCRE = YesPlease
+ USE_NED_ALLOCATOR = YesPlease
+ ifeq (/mingw64,$(subst 32,64,$(prefix)))
+ # Move system config into top-level /etc/
+ ETC_GITCONFIG = ../etc/gitconfig
+ ETC_GITATTRIBUTES = ../etc/gitattributes
endif
endif
-endif
ifeq ($(uname_S),QNX)
COMPAT_CFLAGS += -DSA_RESTART=0
EXPAT_NEEDS_XMLPARSE_H = YesPlease
diff --git a/connect.c b/connect.c
index 5ea53de..63e5964 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;
@@ -66,7 +67,7 @@
}
/* Checks if the server supports the capability 'c' */
-int server_supports_v2(const char *c, int die_on_error)
+int server_supports_v2(const char *c)
{
int i;
@@ -76,13 +77,15 @@
(!*out || *out == '='))
return 1;
}
-
- if (die_on_error)
- die(_("server doesn't support '%s'"), c);
-
return 0;
}
+void ensure_server_supports_v2(const char *c)
+{
+ if (!server_supports_v2(c))
+ die(_("server doesn't support '%s'"), c);
+}
+
int server_feature_v2(const char *c, const char **v)
{
int i;
@@ -477,7 +480,7 @@
{
const char *hash_name;
- if (server_supports_v2("agent", 0))
+ if (server_supports_v2("agent"))
packet_write_fmt(fd_out, "agent=%s", git_user_agent_sanitized());
if (server_feature_v2("object-format", &hash_name)) {
@@ -491,6 +494,49 @@
}
}
+int get_remote_bundle_uri(int fd_out, struct packet_reader *reader,
+ struct bundle_list *bundles, int stateless_rpc)
+{
+ int line_nr = 1;
+
+ /* Assert bundle-uri support */
+ ensure_server_supports_v2("bundle-uri");
+
+ /* (Re-)send capabilities */
+ send_capabilities(fd_out, reader);
+
+ /* Send command */
+ packet_write_fmt(fd_out, "command=bundle-uri\n");
+ packet_delim(fd_out);
+
+ packet_flush(fd_out);
+
+ /* Process response from server */
+ while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
+ const char *line = reader->line;
+ line_nr++;
+
+ if (!bundle_uri_parse_line(bundles, line))
+ continue;
+
+ return error(_("error on bundle-uri response line %d: %s"),
+ line_nr, line);
+ }
+
+ if (reader->status != PACKET_READ_FLUSH)
+ return error(_("expected flush after bundle-uri listing"));
+
+ /*
+ * Might die(), but obscure enough that that's OK, e.g. in
+ * serve.c we'll call BUG() on its equivalent (the
+ * PACKET_READ_RESPONSE_END check).
+ */
+ check_stateless_delimiter(stateless_rpc, reader,
+ _("expected response end packet after ref listing"));
+
+ return 0;
+}
+
struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
struct ref **list, int for_push,
struct transport_ls_refs_options *transport_options,
@@ -504,17 +550,18 @@
&transport_options->unborn_head_target : NULL;
*list = NULL;
- if (server_supports_v2("ls-refs", 1))
- packet_write_fmt(fd_out, "command=ls-refs\n");
+ ensure_server_supports_v2("ls-refs");
+ packet_write_fmt(fd_out, "command=ls-refs\n");
/* Send capabilities */
send_capabilities(fd_out, reader);
- if (server_options && server_options->nr &&
- server_supports_v2("server-option", 1))
+ if (server_options && server_options->nr) {
+ ensure_server_supports_v2("server-option");
for (i = 0; i < server_options->nr; i++)
packet_write_fmt(fd_out, "server-option=%s",
server_options->items[i].string);
+ }
packet_delim(fd_out);
/* When pushing we don't want to request the peeled tags */
diff --git a/connect.h b/connect.h
index c53586e..b26f7de 100644
--- a/connect.h
+++ b/connect.h
@@ -20,7 +20,8 @@
int server_supports_hash(const char *desired, int *feature_supported);
const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp, int *offset);
-int server_supports_v2(const char *c, int die_on_error);
+int server_supports_v2(const char *c);
+void ensure_server_supports_v2(const char *c);
int server_feature_v2(const char *c, const char **v);
int server_supports_feature(const char *c, const char *feature,
int die_on_error);
diff --git a/connected.c b/connected.c
index 4f6388e..b90fd61 100644
--- a/connected.c
+++ b/connected.c
@@ -85,6 +85,7 @@
promisor_pack_found:
;
} while ((oid = fn(cb_data)) != NULL);
+ free(new_pack);
return 0;
}
@@ -121,8 +122,10 @@
else
rev_list.no_stderr = opt->quiet;
- if (start_command(&rev_list))
+ if (start_command(&rev_list)) {
+ free(new_pack);
return error(_("Could not run 'git rev-list'"));
+ }
sigchain_push(SIGPIPE, SIG_IGN);
@@ -154,5 +157,6 @@
err = error_errno(_("failed to close rev-list's stdin"));
sigchain_pop(SIGPIPE);
+ free(new_pack);
return finish_command(&rev_list) || err;
}
diff --git a/contrib/coccinelle/array.cocci b/contrib/coccinelle/array.cocci
index aa75937..27a3b47 100644
--- a/contrib/coccinelle/array.cocci
+++ b/contrib/coccinelle/array.cocci
@@ -94,3 +94,10 @@
@@
- ptr = xcalloc(n, \( sizeof(*ptr) \| sizeof(T) \) )
+ CALLOC_ARRAY(ptr, n)
+
+@@
+expression dst, src, n;
+@@
+-ALLOC_ARRAY(dst, n);
+-COPY_ARRAY(dst, src, n);
++DUP_ARRAY(dst, src, n);
diff --git a/contrib/coccinelle/index-compatibility.cocci b/contrib/coccinelle/index-compatibility.cocci
index 8520f03..31e36cf 100644
--- a/contrib/coccinelle/index-compatibility.cocci
+++ b/contrib/coccinelle/index-compatibility.cocci
@@ -1,6 +1,7 @@
// the_index.* variables
@@
identifier AC = active_cache;
+identifier AN = active_nr;
identifier ACC = active_cache_changed;
identifier ACT = active_cache_tree;
@@
@@ -8,6 +9,9 @@
- AC
+ the_index.cache
|
+- AN
++ the_index.cache_nr
+|
- ACC
+ the_index.cache_changed
|
@@ -15,19 +19,13 @@
+ the_index.cache_tree
)
-@@
-identifier AN = active_nr;
-identifier f != prepare_to_commit;
-@@
- f(...) {<...
-- AN
-+ the_index.cache_nr
- ...>}
-
// "the_repository" simple cases
@@
@@
(
+- read_cache
++ repo_read_index
+|
- read_cache_unmerged
+ repo_read_index_unmerged
|
@@ -96,6 +94,15 @@
|
- resolve_undo_clear
+ resolve_undo_clear_index
+|
+- cache_name_pos
++ index_name_pos
+|
+- update_main_cache_tree
++ cache_tree_update
+|
+- discard_cache
++ discard_index
)
(
+ &the_index,
@@ -137,3 +144,14 @@
...
+ , NULL, NULL, NULL
)
+
+@@
+expression O;
+@@
+- write_cache_as_tree
++ write_index_as_tree
+ (
+- O,
++ O, &the_index, get_index_file(),
+ ...
+ )
diff --git a/contrib/coccinelle/index-compatibility.pending.cocci b/contrib/coccinelle/index-compatibility.pending.cocci
deleted file mode 100644
index 01f875d0..0000000
--- a/contrib/coccinelle/index-compatibility.pending.cocci
+++ /dev/null
@@ -1,24 +0,0 @@
-// "the_repository" simple cases
-@@
-@@
-(
-- read_cache
-+ repo_read_index
-)
- (
-+ the_repository,
- ...)
-
-// "the_index" simple cases
-@@
-@@
-(
-- discard_cache
-+ discard_index
-|
-- cache_name_pos
-+ index_name_pos
-)
- (
-+ &the_index,
- ...)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index ba5c395..dc95c34 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -58,6 +58,12 @@
#
# When set to "1" suggest all options, including options which are
# typically hidden (e.g. '--allow-empty' for 'git commit').
+#
+# GIT_COMPLETION_IGNORE_CASE
+#
+# When set, uses for-each-ref '--ignore-case' to find refs that match
+# case insensitively, even on systems with case sensitive file systems
+# (e.g., completing tag name "FOO" on "git checkout f<TAB>").
case "$COMP_WORDBREAKS" in
*:*) : great ;;
@@ -646,6 +652,7 @@
local pfx="${1-}" cur_="${2-}" sfx="${3-}"
__git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \
+ ${GIT_COMPLETION_IGNORE_CASE+--ignore-case} \
"refs/heads/$cur_*" "refs/heads/$cur_*/**"
}
@@ -659,6 +666,7 @@
local pfx="${1-}" cur_="${2-}" sfx="${3-}"
__git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \
+ ${GIT_COMPLETION_IGNORE_CASE+--ignore-case} \
"refs/remotes/$cur_*" "refs/remotes/$cur_*/**"
}
@@ -669,6 +677,7 @@
local pfx="${1-}" cur_="${2-}" sfx="${3-}"
__git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \
+ ${GIT_COMPLETION_IGNORE_CASE+--ignore-case} \
"refs/tags/$cur_*" "refs/tags/$cur_*/**"
}
@@ -688,6 +697,7 @@
# but only output if the branch name is unique
__git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \
--sort="refname:strip=3" \
+ ${GIT_COMPLETION_IGNORE_CASE+--ignore-case} \
"refs/remotes/*/$cur_*" "refs/remotes/*/$cur_*/**" | \
uniq -u
}
@@ -712,6 +722,7 @@
local format refs
local pfx="${3-}" cur_="${4-$cur}" sfx="${5-}"
local match="${4-}"
+ local umatch="${4-}"
local fer_pfx="${pfx//\%/%%}" # "escape" for-each-ref format specifiers
__git_find_repo_path
@@ -735,12 +746,19 @@
fi
fi
+ if test "${GIT_COMPLETION_IGNORE_CASE:+1}" = "1"
+ then
+ # uppercase with tr instead of ${match,^^} for bash 3.2 compatibility
+ umatch=$(echo "$match" | tr a-z A-Z 2>/dev/null || echo "$match")
+ fi
+
if [ "$list_refs_from" = path ]; then
if [[ "$cur_" == ^* ]]; then
pfx="$pfx^"
fer_pfx="$fer_pfx^"
cur_=${cur_#^}
match=${match#^}
+ umatch=${umatch#^}
fi
case "$cur_" in
refs|refs/*)
@@ -751,7 +769,7 @@
*)
for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD REBASE_HEAD CHERRY_PICK_HEAD; do
case "$i" in
- $match*)
+ $match*|$umatch*)
if [ -e "$dir/$i" ]; then
echo "$pfx$i$sfx"
fi
@@ -765,6 +783,7 @@
;;
esac
__git_dir="$dir" __git for-each-ref --format="$fer_pfx%($format)$sfx" \
+ ${GIT_COMPLETION_IGNORE_CASE+--ignore-case} \
"${refs[@]}"
if [ -n "$track" ]; then
__git_dwim_remote_heads "$pfx" "$match" "$sfx"
@@ -784,15 +803,16 @@
*)
if [ "$list_refs_from" = remote ]; then
case "HEAD" in
- $match*) echo "${pfx}HEAD$sfx" ;;
+ $match*|$umatch*) echo "${pfx}HEAD$sfx" ;;
esac
__git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \
+ ${GIT_COMPLETION_IGNORE_CASE+--ignore-case} \
"refs/remotes/$remote/$match*" \
"refs/remotes/$remote/$match*/**"
else
local query_symref
case "HEAD" in
- $match*) query_symref="HEAD" ;;
+ $match*|$umatch*) query_symref="HEAD" ;;
esac
__git ls-remote "$remote" $query_symref \
"refs/tags/$match*" "refs/heads/$match*" \
diff --git a/contrib/git-jump/README b/contrib/git-jump/README
index 8bcace2..3211841 100644
--- a/contrib/git-jump/README
+++ b/contrib/git-jump/README
@@ -79,6 +79,14 @@
git config jump.grepCmd "ag --column"
--------------------------------------------------
+You can use the optional argument '--stdout' to print the listing to
+standard output instead of feeding it to the editor. You can use the
+argument with M-x grep on Emacs:
+
+--------------------------------------------------
+# In Emacs, M-x grep and invoke "git jump --stdout <mode>"
+M-x grep<RET>git jump --stdout diff<RET>
+--------------------------------------------------
Related Programs
----------------
@@ -100,7 +108,7 @@
-----------
This script was written and tested with vim. Given that the quickfix
-format is the same as what gcc produces, I expect emacs users have a
+format is the same as what gcc produces, I expect other tools have a
similar feature for iterating through the list, but I know nothing about
how to activate it.
diff --git a/contrib/git-jump/git-jump b/contrib/git-jump/git-jump
index 92dbd4c..40c4b0d 100755
--- a/contrib/git-jump/git-jump
+++ b/contrib/git-jump/git-jump
@@ -2,7 +2,7 @@
usage() {
cat <<\EOF
-usage: git jump <mode> [<args>]
+usage: git jump [--stdout] <mode> [<args>]
Jump to interesting elements in an editor.
The <mode> parameter is one of:
@@ -15,12 +15,30 @@
configured, to the command in `jump.grepCmd`.
ws: elements are whitespace errors. Arguments are given to diff --check.
+
+If the optional argument `--stdout` is given, print the quickfix
+lines to standard output instead of feeding it to the editor.
EOF
}
open_editor() {
editor=`git var GIT_EDITOR`
- eval "$editor -q \$1"
+ case "$editor" in
+ *emacs*)
+ # Supported editor values are:
+ # - emacs
+ # - emacsclient
+ # - emacsclient -t
+ #
+ # Wait for completion of the asynchronously executed process
+ # to avoid race conditions in case of "emacsclient".
+ eval "$editor --eval \"(let ((buf (grep \\\"cat \$1\\\"))) (pop-to-buffer buf) (select-frame-set-input-focus (selected-frame)) (while (get-buffer-process buf) (sleep-for 0.1)))\""
+ ;;
+ *)
+ # assume anything else is vi-compatible
+ eval "$editor -q \$1"
+ ;;
+ esac
}
mode_diff() {
@@ -64,15 +82,36 @@
git diff --check "$@"
}
+use_stdout=
+while test $# -gt 0; do
+ case "$1" in
+ --stdout)
+ use_stdout=t
+ ;;
+ --*)
+ usage >&2
+ exit 1
+ ;;
+ *)
+ break
+ ;;
+ esac
+ shift
+done
if test $# -lt 1; then
usage >&2
exit 1
fi
mode=$1; shift
+type "mode_$mode" >/dev/null 2>&1 || { usage >&2; exit 1; }
+
+if test "$use_stdout" = "t"; then
+ "mode_$mode" "$@"
+ exit 0
+fi
trap 'rm -f "$tmp"' 0 1 2 3 15
tmp=`mktemp -t git-jump.XXXXXX` || exit 1
-type "mode_$mode" >/dev/null 2>&1 || { usage >&2; exit 1; }
"mode_$mode" "$@" >"$tmp"
test -s "$tmp" || exit 0
open_editor "$tmp"
diff --git a/convert.c b/convert.c
index 9b67649..a54d169 100644
--- a/convert.c
+++ b/convert.c
@@ -1308,7 +1308,7 @@
git_config(read_convert_config, NULL);
}
- git_check_attr(istate, path, check);
+ git_check_attr(istate, NULL, path, check);
ccheck = check->items;
ca->crlf_action = git_path_check_crlf(ccheck + 4);
if (ca->crlf_action == CRLF_UNDEFINED)
diff --git a/csum-file.c b/csum-file.c
index 59ef339..cce13c0 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -45,7 +45,8 @@
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 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 @@
* 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 @@
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 0d29f52..793a59d 100644
--- a/csum-file.h
+++ b/csum-file.h
@@ -20,6 +20,13 @@
size_t buffer_len;
unsigned char *buffer;
unsigned char *check_buffer;
+
+ /**
+ * If non-zero, skip_hash indicates that we should
+ * not actually compute the hash for this hashfile and
+ * instead only use it as a buffered write.
+ */
+ int skip_hash;
};
/* Checkpoint */
diff --git a/date.c b/date.c
index 53bd6a7..6f45eeb 100644
--- a/date.c
+++ b/date.c
@@ -493,6 +493,12 @@
return 2;
}
+ /* ISO-8601 allows yyyymmDD'T'HHMMSS, with less precision */
+ if (*date == 'T' && isdigit(date[1]) && tm->tm_hour == -1) {
+ tm->tm_min = tm->tm_sec = 0;
+ return 1;
+ }
+
/* BAD CRAP */
return skip_alpha(date);
}
@@ -639,6 +645,18 @@
}
/*
+ * Have we seen an ISO-8601-alike date, i.e. 20220101T0,
+ * In which, hour is still unset,
+ * and minutes and second has been set to 0.
+ */
+static inline int maybeiso8601(struct tm *tm)
+{
+ return tm->tm_hour == -1 &&
+ tm->tm_min == 0 &&
+ tm->tm_sec == 0;
+}
+
+/*
* We've seen a digit. Time? Year? Date?
*/
static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt)
@@ -701,6 +719,25 @@
return end - date;
}
+ /* reduced precision of ISO-8601's time: HHMM or HH */
+ if (maybeiso8601(tm)) {
+ unsigned int num1 = num;
+ unsigned int num2 = 0;
+ if (n == 4) {
+ num1 = num / 100;
+ num2 = num % 100;
+ }
+ if ((n == 4 || n == 2) && !nodate(tm) &&
+ set_time(num1, num2, 0, tm) == 0)
+ return n;
+ /*
+ * We thought this is an ISO-8601 time string,
+ * we set minutes and seconds to 0,
+ * turn out it isn't, rollback the change.
+ */
+ tm->tm_min = tm->tm_sec = -1;
+ }
+
/* Four-digit year or a timezone? */
if (n == 4) {
if (num <= 1400 && *offset == -1) {
diff --git a/delta-islands.c b/delta-islands.c
index 90c0d69..8b234cb 100644
--- a/delta-islands.c
+++ b/delta-islands.c
@@ -513,6 +513,20 @@
}
}
+void free_island_marks(void)
+{
+ struct island_bitmap *bitmap;
+
+ kh_foreach_value(island_marks, bitmap, {
+ if (!--bitmap->refcount)
+ free(bitmap);
+ });
+ kh_destroy_oid_map(island_marks);
+
+ /* detect use-after-free with a an address which is never valid: */
+ island_marks = (void *)-1;
+}
+
int compute_pack_layers(struct packing_data *to_pack)
{
uint32_t i;
diff --git a/delta-islands.h b/delta-islands.h
index eb0f952..8d1591a 100644
--- a/delta-islands.h
+++ b/delta-islands.h
@@ -14,5 +14,6 @@
void load_delta_islands(struct repository *r, int progress);
void propagate_island_marks(struct commit *commit);
int compute_pack_layers(struct packing_data *to_pack);
+void free_island_marks(void);
#endif /* DELTA_ISLANDS_H */
diff --git a/diff-lib.c b/diff-lib.c
index 2edea41..dec040c 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -673,7 +673,7 @@
return (has_changes != 0);
}
-static struct strbuf *idiff_prefix_cb(struct diff_options *opt, void *data)
+static struct strbuf *idiff_prefix_cb(struct diff_options *opt UNUSED, void *data)
{
return data;
}
diff --git a/diff-no-index.c b/diff-no-index.c
index 18edbdf..05fafd0 100644
--- a/diff-no-index.c
+++ b/diff-no-index.c
@@ -255,8 +255,7 @@
};
struct option *options;
- options = parse_options_concat(no_index_options,
- revs->diffopt.parseopts);
+ options = add_diff_options(no_index_options, &revs->diffopt);
argc = parse_options(argc, argv, revs->prefix, options,
diff_no_index_usage, 0);
if (argc != 2) {
diff --git a/diff.c b/diff.c
index 1054a4b..329eebf 100644
--- a/diff.c
+++ b/diff.c
@@ -604,7 +604,7 @@
return one->size;
}
-static int count_trailing_blank(mmfile_t *mf, unsigned ws_rule)
+static int count_trailing_blank(mmfile_t *mf)
{
char *ptr = mf->ptr;
long size = mf->size;
@@ -622,7 +622,7 @@
for (prev_eol = ptr; mf->ptr <= prev_eol; prev_eol--)
if (*prev_eol == '\n')
break;
- if (!ws_blank_line(prev_eol + 1, ptr - prev_eol, ws_rule))
+ if (!ws_blank_line(prev_eol + 1, ptr - prev_eol))
break;
cnt++;
ptr = prev_eol - 1;
@@ -634,9 +634,8 @@
struct emit_callback *ecbdata)
{
int l1, l2, at;
- unsigned ws_rule = ecbdata->ws_rule;
- l1 = count_trailing_blank(mf1, ws_rule);
- l2 = count_trailing_blank(mf2, ws_rule);
+ l1 = count_trailing_blank(mf1);
+ l2 = count_trailing_blank(mf2);
if (l2 <= l1) {
ecbdata->blank_at_eof_in_preimage = 0;
ecbdata->blank_at_eof_in_postimage = 0;
@@ -1583,7 +1582,7 @@
ecbdata->blank_at_eof_in_preimage <= ecbdata->lno_in_preimage &&
ecbdata->blank_at_eof_in_postimage <= ecbdata->lno_in_postimage))
return 0;
- return ws_blank_line(line, len, ecbdata->ws_rule);
+ return ws_blank_line(line, len);
}
static void emit_add_line(struct emit_callback *ecbdata,
@@ -1955,7 +1954,7 @@
static void fn_out_diff_words_aux(void *priv,
long minus_first, long minus_len,
long plus_first, long plus_len,
- const char *func, long funclen)
+ const char *func UNUSED, long funclen UNUSED)
{
struct diff_words_data *diff_words = priv;
struct diff_words_style *style = diff_words->style;
@@ -2801,7 +2800,7 @@
else if (file->is_unmerged) {
strbuf_addf(&out, " %s%s%*s | %*s",
prefix, name, padding, "",
- number_width, "Unmerged");
+ number_width, "Unmerged\n");
emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE,
out.buf, out.len, 0);
strbuf_reset(&out);
@@ -3185,8 +3184,9 @@
}
static void checkdiff_consume_hunk(void *priv,
- long ob, long on, long nb, long nn,
- const char *func, long funclen)
+ long ob UNUSED, long on UNUSED,
+ long nb, long nn UNUSED,
+ const char *func UNUSED, long funclen UNUSED)
{
struct checkdiff_t *data = priv;
@@ -4213,7 +4213,6 @@
}
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 @@
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 @@
}
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 @@
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 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 @@
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);
}
@@ -4615,8 +4611,6 @@
builtin_checkdiff(name, other, attr_path, p->one, p->two, o);
}
-static void prep_parse_options(struct diff_options *options);
-
void repo_diff_setup(struct repository *r, struct diff_options *options)
{
memcpy(options, &default_diff_options, sizeof(*options));
@@ -4662,8 +4656,6 @@
options->color_moved = diff_color_moved_default;
options->color_moved_ws_handling = diff_color_moved_ws_default;
-
- prep_parse_options(options);
}
static const char diff_status_letters[] = {
@@ -4821,8 +4813,6 @@
options->filter = ~filter_bit[DIFF_STATUS_FILTER_AON];
options->filter &= ~options->filter_not;
}
-
- FREE_AND_NULL(options->parseopts);
}
int parse_long_opt(const char *opt, const char **argv,
@@ -5419,7 +5409,8 @@
return 0;
}
-static void prep_parse_options(struct diff_options *options)
+struct option *add_diff_options(const struct option *opts,
+ struct diff_options *options)
{
struct option parseopts[] = {
OPT_GROUP(N_("Diff output format options")),
@@ -5689,22 +5680,25 @@
OPT_END()
};
- ALLOC_ARRAY(options->parseopts, ARRAY_SIZE(parseopts));
- memcpy(options->parseopts, parseopts, sizeof(parseopts));
+ return parse_options_concat(opts, parseopts);
}
int diff_opt_parse(struct diff_options *options,
const char **av, int ac, const char *prefix)
{
+ struct option no_options[] = { OPT_END() };
+ struct option *parseopts = add_diff_options(no_options, options);
+
if (!prefix)
prefix = "";
- ac = parse_options(ac, av, prefix, options->parseopts, NULL,
+ ac = parse_options(ac, av, prefix, parseopts, NULL,
PARSE_OPT_KEEP_DASHDASH |
PARSE_OPT_KEEP_UNKNOWN_OPT |
PARSE_OPT_NO_INTERNAL_HELP |
PARSE_OPT_ONE_SHOT |
PARSE_OPT_STOP_AT_NON_OPTION);
+ free(parseopts);
return ac;
}
@@ -6513,7 +6507,6 @@
diff_free_file(options);
diff_free_ignore_regex(options);
clear_pathspec(&options->pathspec);
- FREE_AND_NULL(options->parseopts);
}
void diff_flush(struct diff_options *options)
@@ -7037,7 +7030,7 @@
struct strbuf buf = STRBUF_INIT;
int err = 0;
- temp = prepare_temp_file(r, spec->path, spec);
+ temp = prepare_temp_file(r, spec);
strvec_push(&child.args, pgm);
strvec_push(&child.args, temp->name);
diff --git a/diff.h b/diff.h
index fd33cae..41eb2c3 100644
--- a/diff.h
+++ b/diff.h
@@ -394,7 +394,6 @@
unsigned color_moved_ws_handling;
struct repository *repo;
- struct option *parseopts;
struct strmap *additional_path_headers;
int no_free;
@@ -539,6 +538,7 @@
#define diff_setup(diffopts) repo_diff_setup(the_repository, diffopts)
#endif
void repo_diff_setup(struct repository *, struct diff_options *);
+struct option *add_diff_options(const struct option *, struct diff_options *);
int diff_opt_parse(struct diff_options *, const char **, int, const char *);
void diff_setup_done(struct diff_options *);
int git_config_rename(const char *var, const char *value);
diff --git a/dir-iterator.c b/dir-iterator.c
index b17e9f9..3764dd8 100644
--- a/dir-iterator.c
+++ b/dir-iterator.c
@@ -203,7 +203,7 @@
{
struct dir_iterator_int *iter = xcalloc(1, sizeof(*iter));
struct dir_iterator *dir_iterator = &iter->base;
- int saved_errno;
+ int saved_errno, err;
strbuf_init(&iter->base.path, PATH_MAX);
strbuf_addstr(&iter->base.path, path);
@@ -213,10 +213,15 @@
iter->flags = flags;
/*
- * Note: stat already checks for NULL or empty strings and
- * inexistent paths.
+ * Note: stat/lstat already checks for NULL or empty strings and
+ * nonexistent paths.
*/
- if (stat(iter->base.path.buf, &iter->base.st) < 0) {
+ if (iter->flags & DIR_ITERATOR_FOLLOW_SYMLINKS)
+ err = stat(iter->base.path.buf, &iter->base.st);
+ else
+ err = lstat(iter->base.path.buf, &iter->base.st);
+
+ if (err < 0) {
saved_errno = errno;
goto error_out;
}
diff --git a/dir-iterator.h b/dir-iterator.h
index 0822915..e3b6ff2 100644
--- a/dir-iterator.h
+++ b/dir-iterator.h
@@ -61,6 +61,11 @@
* not the symlinks themselves, which is the default behavior. Broken
* symlinks are ignored.
*
+ * Note: setting DIR_ITERATOR_FOLLOW_SYMLINKS affects resolving the
+ * starting path as well (e.g., attempting to iterate starting at a
+ * symbolic link pointing to a directory without FOLLOW_SYMLINKS will
+ * result in an error).
+ *
* Warning: circular symlinks are also followed when
* DIR_ITERATOR_FOLLOW_SYMLINKS is set. The iteration may end up with
* an ELOOP if they happen and DIR_ITERATOR_PEDANTIC is set.
diff --git a/dir.c b/dir.c
index d604d1b..4e99f0c 100644
--- a/dir.c
+++ b/dir.c
@@ -732,6 +732,13 @@
goto clear_hashmaps;
}
+ if (!(given->flags & PATTERN_FLAG_MUSTBEDIR) &&
+ strcmp(given->pattern, "/*")) {
+ /* Not a cone pattern. */
+ warning(_("unrecognized pattern: '%s'"), given->pattern);
+ goto clear_hashmaps;
+ }
+
prev = given->pattern;
cur = given->pattern + 1;
next = given->pattern + 2;
@@ -3581,8 +3588,12 @@
void free_untracked_cache(struct untracked_cache *uc)
{
- if (uc)
- free_untracked(uc->root);
+ if (!uc)
+ return;
+
+ free(uc->exclude_per_dir_to_free);
+ strbuf_release(&uc->ident);
+ free_untracked(uc->root);
free(uc);
}
@@ -3739,7 +3750,7 @@
next + offset + hashsz);
uc->dir_flags = get_be32(next + ouc_offset(dir_flags));
exclude_per_dir = (const char *)next + exclude_per_dir_offset;
- uc->exclude_per_dir = xstrdup(exclude_per_dir);
+ uc->exclude_per_dir = uc->exclude_per_dir_to_free = xstrdup(exclude_per_dir);
/* NUL after exclude_per_dir is covered by sizeof(*ouc) */
next += exclude_per_dir_offset + strlen(exclude_per_dir) + 1;
if (next >= end)
diff --git a/dir.h b/dir.h
index 674747d..8acfc04 100644
--- a/dir.h
+++ b/dir.h
@@ -188,6 +188,7 @@
struct oid_stat ss_info_exclude;
struct oid_stat ss_excludes_file;
const char *exclude_per_dir;
+ char *exclude_per_dir_to_free;
struct strbuf ident;
/*
* dir_struct#flags must match dir_flags or the untracked
diff --git a/entry.c b/entry.c
index 616e4f0..971ab26 100644
--- a/entry.c
+++ b/entry.c
@@ -383,7 +383,7 @@
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 @@
* 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 @@
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 @@
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 9be4659..2d4fbb8 100644
--- a/entry.h
+++ b/entry.h
@@ -8,6 +8,7 @@
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 @@
/*
* 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 18d042b..1ee3686 100644
--- a/environment.c
+++ b/environment.c
@@ -102,8 +102,6 @@
static char *git_namespace;
-static char *super_prefix;
-
/*
* Repository-local GIT_* environment variables; see cache.h for details.
*/
@@ -121,7 +119,6 @@
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 @@
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/fetch-pack.c b/fetch-pack.c
index 998fc2f..04016d1 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -1317,15 +1317,15 @@
{
const char *hash_name;
- if (server_supports_v2("fetch", 1))
- packet_buf_write(req_buf, "command=fetch");
- if (server_supports_v2("agent", 0))
+ ensure_server_supports_v2("fetch");
+ packet_buf_write(req_buf, "command=fetch");
+ if (server_supports_v2("agent"))
packet_buf_write(req_buf, "agent=%s", git_user_agent_sanitized());
- if (advertise_sid && server_supports_v2("session-id", 0))
+ if (advertise_sid && server_supports_v2("session-id"))
packet_buf_write(req_buf, "session-id=%s", trace2_session_id());
- if (server_options && server_options->nr &&
- server_supports_v2("server-option", 1)) {
+ if (server_options && server_options->nr) {
int i;
+ ensure_server_supports_v2("server-option");
for (i = 0; i < server_options->nr; i++)
packet_buf_write(req_buf, "server-option=%s",
server_options->items[i].string);
diff --git a/fsck.c b/fsck.c
index b3da1d6..2b18717 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 @@
".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,
@@ -742,6 +748,23 @@
return retval;
}
+/*
+ * Confirm that the headers of a commit or tag object end in a reasonable way,
+ * either with the usual "\n\n" separator, or at least with a trailing newline
+ * on the final header line.
+ *
+ * This property is important for the memory safety of our callers. It allows
+ * them to scan the buffer linewise without constantly checking the remaining
+ * size as long as:
+ *
+ * - they check that there are bytes left in the buffer at the start of any
+ * line (i.e., that the last newline they saw was not the final one we
+ * found here)
+ *
+ * - any intra-line scanning they do will stop at a newline, which will worst
+ * case hit the newline we found here as the end-of-header. This makes it
+ * OK for them to use helpers like parse_oid_hex(), or even skip_prefix().
+ */
static int verify_headers(const void *data, unsigned long size,
const struct object_id *oid, enum object_type type,
struct fsck_options *options)
@@ -802,6 +825,20 @@
if (*p != ' ')
return report(options, oid, type, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, "invalid author/committer line - missing space before date");
p++;
+ /*
+ * Our timestamp parser is based on the C strto*() functions, which
+ * will happily eat whitespace, including the newline that is supposed
+ * to prevent us walking past the end of the buffer. So do our own
+ * scan, skipping linear whitespace but not newlines, and then
+ * confirming we found a digit. We _could_ be even more strict here,
+ * as we really expect only a single space, but since we have
+ * traditionally allowed extra whitespace, we'll continue to do so.
+ */
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (!isdigit(*p))
+ return report(options, oid, type, FSCK_MSG_BAD_DATE,
+ "invalid author/committer line - bad date");
if (*p == '0' && p[1] != ' ')
return report(options, oid, type, FSCK_MSG_ZERO_PADDED_DATE, "invalid author/committer line - zero-padded date");
if (date_overflows(parse_timestamp(p, &end, 10)))
@@ -828,12 +865,18 @@
unsigned author_count;
int err;
const char *buffer_begin = buffer;
+ const char *buffer_end = buffer + size;
const char *p;
+ /*
+ * We _must_ stop parsing immediately if this reports failure, as the
+ * memory safety of the rest of the function depends on it. See the
+ * comment above the definition of verify_headers() for more details.
+ */
if (verify_headers(buffer, size, oid, OBJ_COMMIT, options))
return -1;
- if (!skip_prefix(buffer, "tree ", &buffer))
+ if (buffer >= buffer_end || !skip_prefix(buffer, "tree ", &buffer))
return report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_TREE, "invalid format - expected 'tree' line");
if (parse_oid_hex(buffer, &tree_oid, &p) || *p != '\n') {
err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1");
@@ -841,7 +884,7 @@
return err;
}
buffer = p + 1;
- while (skip_prefix(buffer, "parent ", &buffer)) {
+ while (buffer < buffer_end && skip_prefix(buffer, "parent ", &buffer)) {
if (parse_oid_hex(buffer, &parent_oid, &p) || *p != '\n') {
err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1");
if (err)
@@ -850,7 +893,7 @@
buffer = p + 1;
}
author_count = 0;
- while (skip_prefix(buffer, "author ", &buffer)) {
+ while (buffer < buffer_end && skip_prefix(buffer, "author ", &buffer)) {
author_count++;
err = fsck_ident(&buffer, oid, OBJ_COMMIT, options);
if (err)
@@ -862,7 +905,7 @@
err = report(options, oid, OBJ_COMMIT, FSCK_MSG_MULTIPLE_AUTHORS, "invalid format - multiple 'author' lines");
if (err)
return err;
- if (!skip_prefix(buffer, "committer ", &buffer))
+ if (buffer >= buffer_end || !skip_prefix(buffer, "committer ", &buffer))
return report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_COMMITTER, "invalid format - expected 'committer' line");
err = fsck_ident(&buffer, oid, OBJ_COMMIT, options);
if (err)
@@ -893,13 +936,19 @@
int ret = 0;
char *eol;
struct strbuf sb = STRBUF_INIT;
+ const char *buffer_end = buffer + size;
const char *p;
+ /*
+ * We _must_ stop parsing immediately if this reports failure, as the
+ * memory safety of the rest of the function depends on it. See the
+ * comment above the definition of verify_headers() for more details.
+ */
ret = verify_headers(buffer, size, oid, OBJ_TAG, options);
if (ret)
goto done;
- if (!skip_prefix(buffer, "object ", &buffer)) {
+ if (buffer >= buffer_end || !skip_prefix(buffer, "object ", &buffer)) {
ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_OBJECT, "invalid format - expected 'object' line");
goto done;
}
@@ -910,11 +959,11 @@
}
buffer = p + 1;
- if (!skip_prefix(buffer, "type ", &buffer)) {
+ if (buffer >= buffer_end || !skip_prefix(buffer, "type ", &buffer)) {
ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TYPE_ENTRY, "invalid format - expected 'type' line");
goto done;
}
- eol = strchr(buffer, '\n');
+ eol = memchr(buffer, '\n', buffer_end - buffer);
if (!eol) {
ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TYPE, "invalid format - unexpected end after 'type' line");
goto done;
@@ -926,11 +975,11 @@
goto done;
buffer = eol + 1;
- if (!skip_prefix(buffer, "tag ", &buffer)) {
+ if (buffer >= buffer_end || !skip_prefix(buffer, "tag ", &buffer)) {
ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TAG_ENTRY, "invalid format - expected 'tag' line");
goto done;
}
- eol = strchr(buffer, '\n');
+ eol = memchr(buffer, '\n', buffer_end - buffer);
if (!eol) {
ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TAG, "invalid format - unexpected end after 'type' line");
goto done;
@@ -946,7 +995,7 @@
}
buffer = eol + 1;
- if (!skip_prefix(buffer, "tagger ", &buffer)) {
+ if (buffer >= buffer_end || !skip_prefix(buffer, "tagger ", &buffer)) {
/* early tags do not contain 'tagger' lines; warn only */
ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TAGGER_ENTRY, "invalid format - expected 'tagger' line");
if (ret)
@@ -954,10 +1003,8 @@
}
else
ret = fsck_ident(&buffer, oid, OBJ_TAG, options);
- if (!*buffer)
- goto done;
- if (!starts_with(buffer, "\n")) {
+ if (buffer < buffer_end && !starts_with(buffer, "\n")) {
/*
* The verify_headers() check will allow
* e.g. "[...]tagger <tagger>\nsome
@@ -1159,38 +1206,70 @@
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");
+ if (oidset_contains(&options->gitattributes_found, oid)) {
+ const char *ptr;
- return data.ret;
+ 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,
@@ -1199,19 +1278,26 @@
if (!obj)
return report(options, NULL, OBJ_NONE, FSCK_MSG_BAD_OBJECT_SHA1, "no valid object to fsck");
- if (obj->type == OBJ_BLOB)
- return fsck_blob(&obj->oid, data, size, options);
- if (obj->type == OBJ_TREE)
- return fsck_tree(&obj->oid, data, size, options);
- if (obj->type == OBJ_COMMIT)
- return fsck_commit(&obj->oid, data, size, options);
- if (obj->type == OBJ_TAG)
- return fsck_tag(&obj->oid, data, size, options);
+ return fsck_buffer(&obj->oid, obj->type, data, size, options);
+}
- return report(options, &obj->oid, obj->type,
+int fsck_buffer(const struct object_id *oid, enum object_type type,
+ void *data, unsigned long size,
+ struct fsck_options *options)
+{
+ if (type == OBJ_BLOB)
+ return fsck_blob(oid, data, size, options);
+ if (type == OBJ_TREE)
+ return fsck_tree(oid, data, size, options);
+ if (type == OBJ_COMMIT)
+ return fsck_commit(oid, data, size, options);
+ if (type == OBJ_TAG)
+ return fsck_tag(oid, data, size, options);
+
+ return report(options, oid, type,
FSCK_MSG_UNKNOWN_TYPE,
"unknown type '%d' (internal fsck error)",
- obj->type);
+ type);
}
int fsck_error_function(struct fsck_options *o,
@@ -1229,19 +1315,21 @@
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 +1337,36 @@
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);
- oidset_clear(&options->gitmodules_found);
- oidset_clear(&options->gitmodules_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");
+
return ret;
}
diff --git a/fsck.h b/fsck.h
index 6fbce68..6683308 100644
--- a/fsck.h
+++ b/fsck.h
@@ -59,6 +59,10 @@
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 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 @@
.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, \
}
@@ -172,6 +184,14 @@
struct fsck_options *options);
/*
+ * Same as fsck_object(), but for when the caller doesn't have an object
+ * struct.
+ */
+int fsck_buffer(const struct object_id *oid, enum object_type,
+ void *data, unsigned long size,
+ struct fsck_options *options);
+
+/*
* fsck a tag, and pass info about it back to the caller. This is
* exposed fsck_object() internals for git-mktag(1).
*/
diff --git a/fsmonitor-settings.c b/fsmonitor-settings.c
index ee63a97..899bfe9 100644
--- a/fsmonitor-settings.c
+++ b/fsmonitor-settings.c
@@ -143,8 +143,6 @@
enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)
{
- if (!r)
- r = the_repository;
if (!r->settings.fsmonitor)
lookup_fsmonitor_settings(r);
@@ -153,8 +151,6 @@
const char *fsm_settings__get_hook_path(struct repository *r)
{
- if (!r)
- r = the_repository;
if (!r->settings.fsmonitor)
lookup_fsmonitor_settings(r);
@@ -174,8 +170,6 @@
* Caller requested IPC explicitly, so avoid (possibly
* recursive) config lookup.
*/
- if (!r)
- r = the_repository;
if (!r->settings.fsmonitor)
r->settings.fsmonitor = alloc_settings();
@@ -197,8 +191,6 @@
* Caller requested hook explicitly, so avoid (possibly
* recursive) config lookup.
*/
- if (!r)
- r = the_repository;
if (!r->settings.fsmonitor)
r->settings.fsmonitor = alloc_settings();
@@ -210,8 +202,6 @@
void fsm_settings__set_disabled(struct repository *r)
{
- if (!r)
- r = the_repository;
if (!r->settings.fsmonitor)
r->settings.fsmonitor = alloc_settings();
@@ -223,8 +213,6 @@
void fsm_settings__set_incompatible(struct repository *r,
enum fsmonitor_reason reason)
{
- if (!r)
- r = the_repository;
if (!r->settings.fsmonitor)
r->settings.fsmonitor = alloc_settings();
@@ -235,8 +223,6 @@
enum fsmonitor_reason fsm_settings__get_reason(struct repository *r)
{
- if (!r)
- r = the_repository;
if (!r->settings.fsmonitor)
lookup_fsmonitor_settings(r);
diff --git a/fsmonitor.c b/fsmonitor.c
index 08af00c..a5b9e75 100644
--- a/fsmonitor.c
+++ b/fsmonitor.c
@@ -304,7 +304,7 @@
char *buf;
unsigned int i;
int is_trivial = 0;
- struct repository *r = istate->repo ? istate->repo : the_repository;
+ struct repository *r = istate->repo;
enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);
enum fsmonitor_reason reason = fsm_settings__get_reason(r);
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
deleted file mode 100755
index 95887fd..0000000
--- a/git-add--interactive.perl
+++ /dev/null
@@ -1,1920 +0,0 @@
-#!/usr/bin/perl
-
-use 5.008;
-use strict;
-use warnings;
-use Git qw(unquote_path);
-use Git::I18N;
-
-binmode(STDOUT, ":raw");
-
-my $repo = Git->repository();
-
-my $menu_use_color = $repo->get_colorbool('color.interactive');
-my ($prompt_color, $header_color, $help_color) =
- $menu_use_color ? (
- $repo->get_color('color.interactive.prompt', 'bold blue'),
- $repo->get_color('color.interactive.header', 'bold'),
- $repo->get_color('color.interactive.help', 'red bold'),
- ) : ();
-my $error_color = ();
-if ($menu_use_color) {
- my $help_color_spec = ($repo->config('color.interactive.help') or
- 'red bold');
- $error_color = $repo->get_color('color.interactive.error',
- $help_color_spec);
-}
-
-my $diff_use_color = $repo->get_colorbool('color.diff');
-my ($fraginfo_color) =
- $diff_use_color ? (
- $repo->get_color('color.diff.frag', 'cyan'),
- ) : ();
-my ($diff_context_color) =
- $diff_use_color ? (
- $repo->get_color($repo->config('color.diff.context') ? 'color.diff.context' : 'color.diff.plain', ''),
- ) : ();
-my ($diff_old_color) =
- $diff_use_color ? (
- $repo->get_color('color.diff.old', 'red'),
- ) : ();
-my ($diff_new_color) =
- $diff_use_color ? (
- $repo->get_color('color.diff.new', 'green'),
- ) : ();
-
-my $normal_color = $repo->get_color("", "reset");
-
-my $diff_algorithm = $repo->config('diff.algorithm');
-my $diff_filter = $repo->config('interactive.difffilter');
-
-my $use_readkey = 0;
-my $use_termcap = 0;
-my %term_escapes;
-
-sub ReadMode;
-sub ReadKey;
-if ($repo->config_bool("interactive.singlekey")) {
- eval {
- require Term::ReadKey;
- Term::ReadKey->import;
- $use_readkey = 1;
- };
- if (!$use_readkey) {
- print STDERR "missing Term::ReadKey, disabling interactive.singlekey\n";
- }
- eval {
- require Term::Cap;
- my $termcap = Term::Cap->Tgetent;
- foreach (values %$termcap) {
- $term_escapes{$_} = 1 if /^\e/;
- }
- $use_termcap = 1;
- };
-}
-
-sub colored {
- my $color = shift;
- my $string = join("", @_);
-
- if (defined $color) {
- # Put a color code at the beginning of each line, a reset at the end
- # color after newlines that are not at the end of the string
- $string =~ s/(\n+)(.)/$1$color$2/g;
- # reset before newlines
- $string =~ s/(\n+)/$normal_color$1/g;
- # codes at beginning and end (if necessary):
- $string =~ s/^/$color/;
- $string =~ s/$/$normal_color/ unless $string =~ /\n$/;
- }
- return $string;
-}
-
-# command line options
-my $patch_mode_only;
-my $patch_mode;
-my $patch_mode_revision;
-
-sub apply_patch;
-sub apply_patch_for_checkout_commit;
-sub apply_patch_for_stash;
-
-my %patch_modes = (
- 'stage' => {
- DIFF => 'diff-files -p',
- APPLY => sub { apply_patch 'apply --cached', @_; },
- APPLY_CHECK => 'apply --cached',
- FILTER => 'file-only',
- IS_REVERSE => 0,
- },
- 'stash' => {
- DIFF => 'diff-index -p HEAD',
- APPLY => sub { apply_patch 'apply --cached', @_; },
- APPLY_CHECK => 'apply --cached',
- FILTER => undef,
- IS_REVERSE => 0,
- },
- 'reset_head' => {
- DIFF => 'diff-index -p --cached',
- APPLY => sub { apply_patch 'apply -R --cached', @_; },
- APPLY_CHECK => 'apply -R --cached',
- FILTER => 'index-only',
- IS_REVERSE => 1,
- },
- 'reset_nothead' => {
- DIFF => 'diff-index -R -p --cached',
- APPLY => sub { apply_patch 'apply --cached', @_; },
- APPLY_CHECK => 'apply --cached',
- FILTER => 'index-only',
- IS_REVERSE => 0,
- },
- 'checkout_index' => {
- DIFF => 'diff-files -p',
- APPLY => sub { apply_patch 'apply -R', @_; },
- APPLY_CHECK => 'apply -R',
- FILTER => 'file-only',
- IS_REVERSE => 1,
- },
- 'checkout_head' => {
- DIFF => 'diff-index -p',
- APPLY => sub { apply_patch_for_checkout_commit '-R', @_ },
- APPLY_CHECK => 'apply -R',
- FILTER => undef,
- IS_REVERSE => 1,
- },
- 'checkout_nothead' => {
- DIFF => 'diff-index -R -p',
- APPLY => sub { apply_patch_for_checkout_commit '', @_ },
- APPLY_CHECK => 'apply',
- FILTER => undef,
- IS_REVERSE => 0,
- },
- 'worktree_head' => {
- DIFF => 'diff-index -p',
- APPLY => sub { apply_patch 'apply -R', @_ },
- APPLY_CHECK => 'apply -R',
- FILTER => undef,
- IS_REVERSE => 1,
- },
- 'worktree_nothead' => {
- DIFF => 'diff-index -R -p',
- APPLY => sub { apply_patch 'apply', @_ },
- APPLY_CHECK => 'apply',
- FILTER => undef,
- IS_REVERSE => 0,
- },
-);
-
-$patch_mode = 'stage';
-my %patch_mode_flavour = %{$patch_modes{$patch_mode}};
-
-sub run_cmd_pipe {
- if ($^O eq 'MSWin32') {
- my @invalid = grep {m/[":*]/} @_;
- die "$^O does not support: @invalid\n" if @invalid;
- my @args = map { m/ /o ? "\"$_\"": $_ } @_;
- return qx{@args};
- } else {
- my $fh = undef;
- open($fh, '-|', @_) or die;
- my @out = <$fh>;
- close $fh || die "Cannot close @_ ($!)";
- return @out;
- }
-}
-
-my ($GIT_DIR) = run_cmd_pipe(qw(git rev-parse --git-dir));
-
-if (!defined $GIT_DIR) {
- exit(1); # rev-parse would have already said "not a git repo"
-}
-chomp($GIT_DIR);
-
-sub refresh {
- my $fh;
- open $fh, 'git update-index --refresh |'
- or die;
- while (<$fh>) {
- ;# ignore 'needs update'
- }
- close $fh;
-}
-
-sub list_untracked {
- map {
- chomp $_;
- unquote_path($_);
- }
- run_cmd_pipe(qw(git ls-files --others --exclude-standard --), @ARGV);
-}
-
-# TRANSLATORS: you can adjust this to align "git add -i" status menu
-my $status_fmt = __('%12s %12s %s');
-my $status_head = sprintf($status_fmt, __('staged'), __('unstaged'), __('path'));
-
-{
- my $initial;
- sub is_initial_commit {
- $initial = system('git rev-parse HEAD -- >/dev/null 2>&1') != 0
- unless defined $initial;
- return $initial;
- }
-}
-
-{
- my $empty_tree;
- sub get_empty_tree {
- return $empty_tree if defined $empty_tree;
-
- ($empty_tree) = run_cmd_pipe(qw(git hash-object -t tree /dev/null));
- chomp $empty_tree;
- return $empty_tree;
- }
-}
-
-sub get_diff_reference {
- my $ref = shift;
- if (defined $ref and $ref ne 'HEAD') {
- return $ref;
- } elsif (is_initial_commit()) {
- return get_empty_tree();
- } else {
- return 'HEAD';
- }
-}
-
-# Returns list of hashes, contents of each of which are:
-# VALUE: pathname
-# BINARY: is a binary path
-# INDEX: is index different from HEAD?
-# FILE: is file different from index?
-# INDEX_ADDDEL: is it add/delete between HEAD and index?
-# FILE_ADDDEL: is it add/delete between index and file?
-# UNMERGED: is the path unmerged
-
-sub list_modified {
- my ($only) = @_;
- my (%data, @return);
- my ($add, $del, $adddel, $file);
-
- my $reference = get_diff_reference($patch_mode_revision);
- for (run_cmd_pipe(qw(git diff-index --cached
- --numstat --summary), $reference,
- '--', @ARGV)) {
- if (($add, $del, $file) =
- /^([-\d]+) ([-\d]+) (.*)/) {
- my ($change, $bin);
- $file = unquote_path($file);
- if ($add eq '-' && $del eq '-') {
- $change = __('binary');
- $bin = 1;
- }
- else {
- $change = "+$add/-$del";
- }
- $data{$file} = {
- INDEX => $change,
- BINARY => $bin,
- FILE => __('nothing'),
- }
- }
- elsif (($adddel, $file) =
- /^ (create|delete) mode [0-7]+ (.*)$/) {
- $file = unquote_path($file);
- $data{$file}{INDEX_ADDDEL} = $adddel;
- }
- }
-
- for (run_cmd_pipe(qw(git diff-files --ignore-submodules=dirty --numstat --summary --raw --), @ARGV)) {
- if (($add, $del, $file) =
- /^([-\d]+) ([-\d]+) (.*)/) {
- $file = unquote_path($file);
- my ($change, $bin);
- if ($add eq '-' && $del eq '-') {
- $change = __('binary');
- $bin = 1;
- }
- else {
- $change = "+$add/-$del";
- }
- $data{$file}{FILE} = $change;
- if ($bin) {
- $data{$file}{BINARY} = 1;
- }
- }
- elsif (($adddel, $file) =
- /^ (create|delete) mode [0-7]+ (.*)$/) {
- $file = unquote_path($file);
- $data{$file}{FILE_ADDDEL} = $adddel;
- }
- elsif (/^:[0-7]+ [0-7]+ [0-9a-f]+ [0-9a-f]+ (.) (.*)$/) {
- $file = unquote_path($2);
- if (!exists $data{$file}) {
- $data{$file} = +{
- INDEX => __('unchanged'),
- BINARY => 0,
- };
- }
- if ($1 eq 'U') {
- $data{$file}{UNMERGED} = 1;
- }
- }
- }
-
- for (sort keys %data) {
- my $it = $data{$_};
-
- if ($only) {
- if ($only eq 'index-only') {
- next if ($it->{INDEX} eq __('unchanged'));
- }
- if ($only eq 'file-only') {
- next if ($it->{FILE} eq __('nothing'));
- }
- }
- push @return, +{
- VALUE => $_,
- %$it,
- };
- }
- return @return;
-}
-
-sub find_unique {
- my ($string, @stuff) = @_;
- my $found = undef;
- for (my $i = 0; $i < @stuff; $i++) {
- my $it = $stuff[$i];
- my $hit = undef;
- if (ref $it) {
- if ((ref $it) eq 'ARRAY') {
- $it = $it->[0];
- }
- else {
- $it = $it->{VALUE};
- }
- }
- eval {
- if ($it =~ /^$string/) {
- $hit = 1;
- };
- };
- if (defined $hit && defined $found) {
- return undef;
- }
- if ($hit) {
- $found = $i + 1;
- }
- }
- return $found;
-}
-
-# inserts string into trie and updates count for each character
-sub update_trie {
- my ($trie, $string) = @_;
- foreach (split //, $string) {
- $trie = $trie->{$_} ||= {COUNT => 0};
- $trie->{COUNT}++;
- }
-}
-
-# returns an array of tuples (prefix, remainder)
-sub find_unique_prefixes {
- my @stuff = @_;
- my @return = ();
-
- # any single prefix exceeding the soft limit is omitted
- # if any prefix exceeds the hard limit all are omitted
- # 0 indicates no limit
- my $soft_limit = 0;
- my $hard_limit = 3;
-
- # build a trie modelling all possible options
- my %trie;
- foreach my $print (@stuff) {
- if ((ref $print) eq 'ARRAY') {
- $print = $print->[0];
- }
- elsif ((ref $print) eq 'HASH') {
- $print = $print->{VALUE};
- }
- update_trie(\%trie, $print);
- push @return, $print;
- }
-
- # use the trie to find the unique prefixes
- for (my $i = 0; $i < @return; $i++) {
- my $ret = $return[$i];
- my @letters = split //, $ret;
- my %search = %trie;
- my ($prefix, $remainder);
- my $j;
- for ($j = 0; $j < @letters; $j++) {
- my $letter = $letters[$j];
- if ($search{$letter}{COUNT} == 1) {
- $prefix = substr $ret, 0, $j + 1;
- $remainder = substr $ret, $j + 1;
- last;
- }
- else {
- my $prefix = substr $ret, 0, $j;
- return ()
- if ($hard_limit && $j + 1 > $hard_limit);
- }
- %search = %{$search{$letter}};
- }
- if (ord($letters[0]) > 127 ||
- ($soft_limit && $j + 1 > $soft_limit)) {
- $prefix = undef;
- $remainder = $ret;
- }
- $return[$i] = [$prefix, $remainder];
- }
- return @return;
-}
-
-# filters out prefixes which have special meaning to list_and_choose()
-sub is_valid_prefix {
- my $prefix = shift;
- return (defined $prefix) &&
- !($prefix =~ /[\s,]/) && # separators
- !($prefix =~ /^-/) && # deselection
- !($prefix =~ /^\d+/) && # selection
- ($prefix ne '*') && # "all" wildcard
- ($prefix ne '?'); # prompt help
-}
-
-# given a prefix/remainder tuple return a string with the prefix highlighted
-# for now use square brackets; later might use ANSI colors (underline, bold)
-sub highlight_prefix {
- my $prefix = shift;
- my $remainder = shift;
-
- if (!defined $prefix) {
- return $remainder;
- }
-
- if (!is_valid_prefix($prefix)) {
- return "$prefix$remainder";
- }
-
- if (!$menu_use_color) {
- return "[$prefix]$remainder";
- }
-
- return "$prompt_color$prefix$normal_color$remainder";
-}
-
-sub error_msg {
- print STDERR colored $error_color, @_;
-}
-
-sub list_and_choose {
- my ($opts, @stuff) = @_;
- my (@chosen, @return);
- if (!@stuff) {
- return @return;
- }
- my $i;
- my @prefixes = find_unique_prefixes(@stuff) unless $opts->{LIST_ONLY};
-
- TOPLOOP:
- while (1) {
- my $last_lf = 0;
-
- if ($opts->{HEADER}) {
- my $indent = $opts->{LIST_FLAT} ? "" : " ";
- print colored $header_color, "$indent$opts->{HEADER}\n";
- }
- for ($i = 0; $i < @stuff; $i++) {
- my $chosen = $chosen[$i] ? '*' : ' ';
- my $print = $stuff[$i];
- my $ref = ref $print;
- my $highlighted = highlight_prefix(@{$prefixes[$i]})
- if @prefixes;
- if ($ref eq 'ARRAY') {
- $print = $highlighted || $print->[0];
- }
- elsif ($ref eq 'HASH') {
- my $value = $highlighted || $print->{VALUE};
- $print = sprintf($status_fmt,
- $print->{INDEX},
- $print->{FILE},
- $value);
- }
- else {
- $print = $highlighted || $print;
- }
- printf("%s%2d: %s", $chosen, $i+1, $print);
- if (($opts->{LIST_FLAT}) &&
- (($i + 1) % ($opts->{LIST_FLAT}))) {
- print "\t";
- $last_lf = 0;
- }
- else {
- print "\n";
- $last_lf = 1;
- }
- }
- if (!$last_lf) {
- print "\n";
- }
-
- return if ($opts->{LIST_ONLY});
-
- print colored $prompt_color, $opts->{PROMPT};
- if ($opts->{SINGLETON}) {
- print "> ";
- }
- else {
- print ">> ";
- }
- my $line = <STDIN>;
- if (!$line) {
- print "\n";
- $opts->{ON_EOF}->() if $opts->{ON_EOF};
- last;
- }
- chomp $line;
- last if $line eq '';
- if ($line eq '?') {
- $opts->{SINGLETON} ?
- singleton_prompt_help_cmd() :
- prompt_help_cmd();
- next TOPLOOP;
- }
- for my $choice (split(/[\s,]+/, $line)) {
- my $choose = 1;
- my ($bottom, $top);
-
- # Input that begins with '-'; unchoose
- if ($choice =~ s/^-//) {
- $choose = 0;
- }
- # A range can be specified like 5-7 or 5-.
- if ($choice =~ /^(\d+)-(\d*)$/) {
- ($bottom, $top) = ($1, length($2) ? $2 : 1 + @stuff);
- }
- elsif ($choice =~ /^\d+$/) {
- $bottom = $top = $choice;
- }
- elsif ($choice eq '*') {
- $bottom = 1;
- $top = 1 + @stuff;
- }
- else {
- $bottom = $top = find_unique($choice, @stuff);
- if (!defined $bottom) {
- error_msg sprintf(__("Huh (%s)?\n"), $choice);
- next TOPLOOP;
- }
- }
- if ($opts->{SINGLETON} && $bottom != $top) {
- error_msg sprintf(__("Huh (%s)?\n"), $choice);
- next TOPLOOP;
- }
- for ($i = $bottom-1; $i <= $top-1; $i++) {
- next if (@stuff <= $i || $i < 0);
- $chosen[$i] = $choose;
- }
- }
- last if ($opts->{IMMEDIATE} || $line eq '*');
- }
- for ($i = 0; $i < @stuff; $i++) {
- if ($chosen[$i]) {
- push @return, $stuff[$i];
- }
- }
- return @return;
-}
-
-sub singleton_prompt_help_cmd {
- print colored $help_color, __ <<'EOF' ;
-Prompt help:
-1 - select a numbered item
-foo - select item based on unique prefix
- - (empty) select nothing
-EOF
-}
-
-sub prompt_help_cmd {
- print colored $help_color, __ <<'EOF' ;
-Prompt help:
-1 - select a single item
-3-5 - select a range of items
-2-3,6-9 - select multiple ranges
-foo - select item based on unique prefix
--... - unselect specified items
-* - choose all items
- - (empty) finish selecting
-EOF
-}
-
-sub status_cmd {
- list_and_choose({ LIST_ONLY => 1, HEADER => $status_head },
- list_modified());
- print "\n";
-}
-
-sub say_n_paths {
- my $did = shift @_;
- my $cnt = scalar @_;
- if ($did eq 'added') {
- printf(__n("added %d path\n", "added %d paths\n",
- $cnt), $cnt);
- } elsif ($did eq 'updated') {
- printf(__n("updated %d path\n", "updated %d paths\n",
- $cnt), $cnt);
- } elsif ($did eq 'reverted') {
- printf(__n("reverted %d path\n", "reverted %d paths\n",
- $cnt), $cnt);
- } else {
- printf(__n("touched %d path\n", "touched %d paths\n",
- $cnt), $cnt);
- }
-}
-
-sub update_cmd {
- my @mods = list_modified('file-only');
- return if (!@mods);
-
- my @update = list_and_choose({ PROMPT => __('Update'),
- HEADER => $status_head, },
- @mods);
- if (@update) {
- system(qw(git update-index --add --remove --),
- map { $_->{VALUE} } @update);
- say_n_paths('updated', @update);
- }
- print "\n";
-}
-
-sub revert_cmd {
- my @update = list_and_choose({ PROMPT => __('Revert'),
- HEADER => $status_head, },
- list_modified());
- if (@update) {
- if (is_initial_commit()) {
- system(qw(git rm --cached),
- map { $_->{VALUE} } @update);
- }
- else {
- my @lines = run_cmd_pipe(qw(git ls-tree HEAD --),
- map { $_->{VALUE} } @update);
- my $fh;
- open $fh, '| git update-index --index-info'
- or die;
- for (@lines) {
- print $fh $_;
- }
- close($fh);
- for (@update) {
- if ($_->{INDEX_ADDDEL} &&
- $_->{INDEX_ADDDEL} eq 'create') {
- system(qw(git update-index --force-remove --),
- $_->{VALUE});
- printf(__("note: %s is untracked now.\n"), $_->{VALUE});
- }
- }
- }
- refresh();
- say_n_paths('reverted', @update);
- }
- print "\n";
-}
-
-sub add_untracked_cmd {
- my @add = list_and_choose({ PROMPT => __('Add untracked') },
- list_untracked());
- if (@add) {
- system(qw(git update-index --add --), @add);
- say_n_paths('added', @add);
- } else {
- print __("No untracked files.\n");
- }
- print "\n";
-}
-
-sub run_git_apply {
- my $cmd = shift;
- my $fh;
- open $fh, '| git ' . $cmd . " --allow-overlap";
- print $fh @_;
- return close $fh;
-}
-
-sub parse_diff {
- my ($path) = @_;
- my @diff_cmd = split(" ", $patch_mode_flavour{DIFF});
- if (defined $diff_algorithm) {
- splice @diff_cmd, 1, 0, "--diff-algorithm=${diff_algorithm}";
- }
- if (defined $patch_mode_revision) {
- push @diff_cmd, get_diff_reference($patch_mode_revision);
- }
- my @diff = run_cmd_pipe("git", @diff_cmd, qw(--no-color --), $path);
- my @colored = ();
- if ($diff_use_color) {
- my @display_cmd = ("git", @diff_cmd, qw(--color --), $path);
- if (defined $diff_filter) {
- # quotemeta is overkill, but sufficient for shell-quoting
- my $diff = join(' ', map { quotemeta } @display_cmd);
- @display_cmd = ("$diff | $diff_filter");
- }
-
- @colored = run_cmd_pipe(@display_cmd);
- }
- my (@hunk) = { TEXT => [], DISPLAY => [], TYPE => 'header' };
-
- if (@colored && @colored != @diff) {
- print STDERR
- "fatal: mismatched output from interactive.diffFilter\n",
- "hint: Your filter must maintain a one-to-one correspondence\n",
- "hint: between its input and output lines.\n";
- exit 1;
- }
-
- for (my $i = 0; $i < @diff; $i++) {
- if ($diff[$i] =~ /^@@ /) {
- push @hunk, { TEXT => [], DISPLAY => [],
- TYPE => 'hunk' };
- }
- push @{$hunk[-1]{TEXT}}, $diff[$i];
- push @{$hunk[-1]{DISPLAY}},
- (@colored ? $colored[$i] : $diff[$i]);
- }
- return @hunk;
-}
-
-sub parse_diff_header {
- my $src = shift;
-
- my $head = { TEXT => [], DISPLAY => [], TYPE => 'header' };
- my $mode = { TEXT => [], DISPLAY => [], TYPE => 'mode' };
- my $deletion = { TEXT => [], DISPLAY => [], TYPE => 'deletion' };
- my $addition;
-
- for (my $i = 0; $i < @{$src->{TEXT}}; $i++) {
- if ($src->{TEXT}->[$i] =~ /^new file/) {
- $addition = 1;
- $head->{TYPE} = 'addition';
- }
- my $dest =
- $src->{TEXT}->[$i] =~ /^(old|new) mode (\d+)$/ ? $mode :
- $src->{TEXT}->[$i] =~ /^deleted file/ ? $deletion :
- $head;
- push @{$dest->{TEXT}}, $src->{TEXT}->[$i];
- push @{$dest->{DISPLAY}}, $src->{DISPLAY}->[$i];
- }
- return ($head, $mode, $deletion, $addition);
-}
-
-sub hunk_splittable {
- my ($text) = @_;
-
- my @s = split_hunk($text);
- return (1 < @s);
-}
-
-sub parse_hunk_header {
- my ($line) = @_;
- my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) =
- $line =~ /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
- $o_cnt = 1 unless defined $o_cnt;
- $n_cnt = 1 unless defined $n_cnt;
- return ($o_ofs, $o_cnt, $n_ofs, $n_cnt);
-}
-
-sub format_hunk_header {
- my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) = @_;
- return ("@@ -$o_ofs" .
- (($o_cnt != 1) ? ",$o_cnt" : '') .
- " +$n_ofs" .
- (($n_cnt != 1) ? ",$n_cnt" : '') .
- " @@\n");
-}
-
-sub split_hunk {
- my ($text, $display) = @_;
- my @split = ();
- if (!defined $display) {
- $display = $text;
- }
- # If there are context lines in the middle of a hunk,
- # it can be split, but we would need to take care of
- # overlaps later.
-
- my ($o_ofs, undef, $n_ofs) = parse_hunk_header($text->[0]);
- my $hunk_start = 1;
-
- OUTER:
- while (1) {
- my $next_hunk_start = undef;
- my $i = $hunk_start - 1;
- my $this = +{
- TEXT => [],
- DISPLAY => [],
- TYPE => 'hunk',
- OLD => $o_ofs,
- NEW => $n_ofs,
- OCNT => 0,
- NCNT => 0,
- ADDDEL => 0,
- POSTCTX => 0,
- USE => undef,
- };
-
- while (++$i < @$text) {
- my $line = $text->[$i];
- my $display = $display->[$i];
- if ($line =~ /^\\/) {
- push @{$this->{TEXT}}, $line;
- push @{$this->{DISPLAY}}, $display;
- next;
- }
- if ($line =~ /^ /) {
- if ($this->{ADDDEL} &&
- !defined $next_hunk_start) {
- # We have seen leading context and
- # adds/dels and then here is another
- # context, which is trailing for this
- # split hunk and leading for the next
- # one.
- $next_hunk_start = $i;
- }
- push @{$this->{TEXT}}, $line;
- push @{$this->{DISPLAY}}, $display;
- $this->{OCNT}++;
- $this->{NCNT}++;
- if (defined $next_hunk_start) {
- $this->{POSTCTX}++;
- }
- next;
- }
-
- # add/del
- if (defined $next_hunk_start) {
- # We are done with the current hunk and
- # this is the first real change for the
- # next split one.
- $hunk_start = $next_hunk_start;
- $o_ofs = $this->{OLD} + $this->{OCNT};
- $n_ofs = $this->{NEW} + $this->{NCNT};
- $o_ofs -= $this->{POSTCTX};
- $n_ofs -= $this->{POSTCTX};
- push @split, $this;
- redo OUTER;
- }
- push @{$this->{TEXT}}, $line;
- push @{$this->{DISPLAY}}, $display;
- $this->{ADDDEL}++;
- if ($line =~ /^-/) {
- $this->{OCNT}++;
- }
- else {
- $this->{NCNT}++;
- }
- }
-
- push @split, $this;
- last;
- }
-
- for my $hunk (@split) {
- $o_ofs = $hunk->{OLD};
- $n_ofs = $hunk->{NEW};
- my $o_cnt = $hunk->{OCNT};
- my $n_cnt = $hunk->{NCNT};
-
- my $head = format_hunk_header($o_ofs, $o_cnt, $n_ofs, $n_cnt);
- my $display_head = $head;
- unshift @{$hunk->{TEXT}}, $head;
- if ($diff_use_color) {
- $display_head = colored($fraginfo_color, $head);
- }
- unshift @{$hunk->{DISPLAY}}, $display_head;
- }
- return @split;
-}
-
-sub find_last_o_ctx {
- my ($it) = @_;
- my $text = $it->{TEXT};
- my ($o_ofs, $o_cnt) = parse_hunk_header($text->[0]);
- my $i = @{$text};
- my $last_o_ctx = $o_ofs + $o_cnt;
- while (0 < --$i) {
- my $line = $text->[$i];
- if ($line =~ /^ /) {
- $last_o_ctx--;
- next;
- }
- last;
- }
- return $last_o_ctx;
-}
-
-sub merge_hunk {
- my ($prev, $this) = @_;
- my ($o0_ofs, $o0_cnt, $n0_ofs, $n0_cnt) =
- parse_hunk_header($prev->{TEXT}[0]);
- my ($o1_ofs, $o1_cnt, $n1_ofs, $n1_cnt) =
- parse_hunk_header($this->{TEXT}[0]);
-
- my (@line, $i, $ofs, $o_cnt, $n_cnt);
- $ofs = $o0_ofs;
- $o_cnt = $n_cnt = 0;
- for ($i = 1; $i < @{$prev->{TEXT}}; $i++) {
- my $line = $prev->{TEXT}[$i];
- if ($line =~ /^\+/) {
- $n_cnt++;
- push @line, $line;
- next;
- } elsif ($line =~ /^\\/) {
- push @line, $line;
- next;
- }
-
- last if ($o1_ofs <= $ofs);
-
- $o_cnt++;
- $ofs++;
- if ($line =~ /^ /) {
- $n_cnt++;
- }
- push @line, $line;
- }
-
- for ($i = 1; $i < @{$this->{TEXT}}; $i++) {
- my $line = $this->{TEXT}[$i];
- if ($line =~ /^\+/) {
- $n_cnt++;
- push @line, $line;
- next;
- } elsif ($line =~ /^\\/) {
- push @line, $line;
- next;
- }
- $ofs++;
- $o_cnt++;
- if ($line =~ /^ /) {
- $n_cnt++;
- }
- push @line, $line;
- }
- my $head = format_hunk_header($o0_ofs, $o_cnt, $n0_ofs, $n_cnt);
- @{$prev->{TEXT}} = ($head, @line);
-}
-
-sub coalesce_overlapping_hunks {
- my (@in) = @_;
- my @out = ();
-
- my ($last_o_ctx, $last_was_dirty);
- my $ofs_delta = 0;
-
- for (@in) {
- if ($_->{TYPE} ne 'hunk') {
- push @out, $_;
- next;
- }
- my $text = $_->{TEXT};
- my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) =
- parse_hunk_header($text->[0]);
- unless ($_->{USE}) {
- $ofs_delta += $o_cnt - $n_cnt;
- # If this hunk has been edited then subtract
- # the delta that is due to the edit.
- if ($_->{OFS_DELTA}) {
- $ofs_delta -= $_->{OFS_DELTA};
- }
- next;
- }
- if ($ofs_delta) {
- if ($patch_mode_flavour{IS_REVERSE}) {
- $o_ofs -= $ofs_delta;
- } else {
- $n_ofs += $ofs_delta;
- }
- $_->{TEXT}->[0] = format_hunk_header($o_ofs, $o_cnt,
- $n_ofs, $n_cnt);
- }
- # If this hunk was edited then adjust the offset delta
- # to reflect the edit.
- if ($_->{OFS_DELTA}) {
- $ofs_delta += $_->{OFS_DELTA};
- }
- if (defined $last_o_ctx &&
- $o_ofs <= $last_o_ctx &&
- !$_->{DIRTY} &&
- !$last_was_dirty) {
- merge_hunk($out[-1], $_);
- }
- else {
- push @out, $_;
- }
- $last_o_ctx = find_last_o_ctx($out[-1]);
- $last_was_dirty = $_->{DIRTY};
- }
- return @out;
-}
-
-sub reassemble_patch {
- my $head = shift;
- my @patch;
-
- # Include everything in the header except the beginning of the diff.
- push @patch, (grep { !/^[-+]{3}/ } @$head);
-
- # Then include any headers from the hunk lines, which must
- # come before any actual hunk.
- while (@_ && $_[0] !~ /^@/) {
- push @patch, shift;
- }
-
- # Then begin the diff.
- push @patch, grep { /^[-+]{3}/ } @$head;
-
- # And then the actual hunks.
- push @patch, @_;
-
- return @patch;
-}
-
-sub color_diff {
- return map {
- colored((/^@/ ? $fraginfo_color :
- /^\+/ ? $diff_new_color :
- /^-/ ? $diff_old_color :
- $diff_context_color),
- $_);
- } @_;
-}
-
-my %edit_hunk_manually_modes = (
- stage => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for staging."),
- stash => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for stashing."),
- reset_head => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for unstaging."),
- reset_nothead => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for applying."),
- checkout_index => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for discarding."),
- checkout_head => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for discarding."),
- checkout_nothead => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for applying."),
- worktree_head => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for discarding."),
- worktree_nothead => N__(
-"If the patch applies cleanly, the edited hunk will immediately be
-marked for applying."),
-);
-
-sub recount_edited_hunk {
- local $_;
- my ($oldtext, $newtext) = @_;
- my ($o_cnt, $n_cnt) = (0, 0);
- for (@{$newtext}[1..$#{$newtext}]) {
- my $mode = substr($_, 0, 1);
- if ($mode eq '-') {
- $o_cnt++;
- } elsif ($mode eq '+') {
- $n_cnt++;
- } elsif ($mode eq ' ' or $mode eq "\n") {
- $o_cnt++;
- $n_cnt++;
- }
- }
- my ($o_ofs, undef, $n_ofs, undef) =
- parse_hunk_header($newtext->[0]);
- $newtext->[0] = format_hunk_header($o_ofs, $o_cnt, $n_ofs, $n_cnt);
- my (undef, $orig_o_cnt, undef, $orig_n_cnt) =
- parse_hunk_header($oldtext->[0]);
- # Return the change in the number of lines inserted by this hunk
- return $orig_o_cnt - $orig_n_cnt - $o_cnt + $n_cnt;
-}
-
-sub edit_hunk_manually {
- my ($oldtext) = @_;
-
- my $hunkfile = $repo->repo_path . "/addp-hunk-edit.diff";
- my $fh;
- open $fh, '>', $hunkfile
- or die sprintf(__("failed to open hunk edit file for writing: %s"), $!);
- print $fh Git::comment_lines __("Manual hunk edit mode -- see bottom for a quick guide.\n");
- print $fh @$oldtext;
- my $is_reverse = $patch_mode_flavour{IS_REVERSE};
- my ($remove_plus, $remove_minus) = $is_reverse ? ('-', '+') : ('+', '-');
- my $comment_line_char = Git::get_comment_line_char;
- print $fh Git::comment_lines sprintf(__ <<EOF, $remove_minus, $remove_plus, $comment_line_char),
----
-To remove '%s' lines, make them ' ' lines (context).
-To remove '%s' lines, delete them.
-Lines starting with %s will be removed.
-EOF
-__($edit_hunk_manually_modes{$patch_mode}),
-# TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
-__ <<EOF2 ;
-If it does not apply cleanly, you will be given an opportunity to
-edit again. If all lines of the hunk are removed, then the edit is
-aborted and the hunk is left unchanged.
-EOF2
- close $fh;
-
- chomp(my ($editor) = run_cmd_pipe(qw(git var GIT_EDITOR)));
- system('sh', '-c', $editor.' "$@"', $editor, $hunkfile);
-
- if ($? != 0) {
- return undef;
- }
-
- open $fh, '<', $hunkfile
- or die sprintf(__("failed to open hunk edit file for reading: %s"), $!);
- my @newtext = grep { !/^\Q$comment_line_char\E/ } <$fh>;
- close $fh;
- unlink $hunkfile;
-
- # Abort if nothing remains
- if (!grep { /\S/ } @newtext) {
- return undef;
- }
-
- # Reinsert the first hunk header if the user accidentally deleted it
- if ($newtext[0] !~ /^@/) {
- unshift @newtext, $oldtext->[0];
- }
- return \@newtext;
-}
-
-sub diff_applies {
- return run_git_apply($patch_mode_flavour{APPLY_CHECK} . ' --check',
- map { @{$_->{TEXT}} } @_);
-}
-
-sub _restore_terminal_and_die {
- ReadMode 'restore';
- print "\n";
- exit 1;
-}
-
-sub prompt_single_character {
- if ($use_readkey) {
- local $SIG{TERM} = \&_restore_terminal_and_die;
- local $SIG{INT} = \&_restore_terminal_and_die;
- ReadMode 'cbreak';
- my $key = ReadKey 0;
- ReadMode 'restore';
- if (defined $key) {
- if ($use_termcap and $key eq "\e") {
- while (!defined $term_escapes{$key}) {
- my $next = ReadKey 0.5;
- last if (!defined $next);
- $key .= $next;
- }
- $key =~ s/\e/^[/;
- }
- print "$key";
- }
- print "\n";
- return $key;
- } else {
- return <STDIN>;
- }
-}
-
-sub prompt_yesno {
- my ($prompt) = @_;
- while (1) {
- print colored $prompt_color, $prompt;
- my $line = prompt_single_character;
- return undef unless defined $line;
- return 0 if $line =~ /^n/i;
- return 1 if $line =~ /^y/i;
- }
-}
-
-sub edit_hunk_loop {
- my ($head, $hunks, $ix) = @_;
- my $hunk = $hunks->[$ix];
- my $text = $hunk->{TEXT};
-
- while (1) {
- my $newtext = edit_hunk_manually($text);
- if (!defined $newtext) {
- return undef;
- }
- my $newhunk = {
- TEXT => $newtext,
- TYPE => $hunk->{TYPE},
- USE => 1,
- DIRTY => 1,
- };
- $newhunk->{OFS_DELTA} = recount_edited_hunk($text, $newtext);
- # If this hunk has already been edited then add the
- # offset delta of the previous edit to get the real
- # delta from the original unedited hunk.
- $hunk->{OFS_DELTA} and
- $newhunk->{OFS_DELTA} += $hunk->{OFS_DELTA};
- if (diff_applies($head,
- @{$hunks}[0..$ix-1],
- $newhunk,
- @{$hunks}[$ix+1..$#{$hunks}])) {
- $newhunk->{DISPLAY} = [color_diff(@{$newtext})];
- return $newhunk;
- }
- else {
- prompt_yesno(
- # TRANSLATORS: do not translate [y/n]
- # The program will only accept that input
- # at this point.
- # Consider translating (saying "no" discards!) as
- # (saying "n" for "no" discards!) if the translation
- # of the word "no" does not start with n.
- __('Your edited hunk does not apply. Edit again '
- . '(saying "no" discards!) [y/n]? ')
- ) or return undef;
- }
- }
-}
-
-my %help_patch_modes = (
- stage => N__(
-"y - stage this hunk
-n - do not stage this hunk
-q - quit; do not stage this hunk or any of the remaining ones
-a - stage this hunk and all later hunks in the file
-d - do not stage this hunk or any of the later hunks in the file"),
- stash => N__(
-"y - stash this hunk
-n - do not stash this hunk
-q - quit; do not stash this hunk or any of the remaining ones
-a - stash this hunk and all later hunks in the file
-d - do not stash this hunk or any of the later hunks in the file"),
- reset_head => N__(
-"y - unstage this hunk
-n - do not unstage this hunk
-q - quit; do not unstage this hunk or any of the remaining ones
-a - unstage this hunk and all later hunks in the file
-d - do not unstage this hunk or any of the later hunks in the file"),
- reset_nothead => N__(
-"y - apply this hunk to index
-n - do not apply this hunk to index
-q - quit; do not apply this hunk or any of the remaining ones
-a - apply this hunk and all later hunks in the file
-d - do not apply this hunk or any of the later hunks in the file"),
- checkout_index => N__(
-"y - discard this hunk from worktree
-n - do not discard this hunk from worktree
-q - quit; do not discard this hunk or any of the remaining ones
-a - discard this hunk and all later hunks in the file
-d - do not discard this hunk or any of the later hunks in the file"),
- checkout_head => N__(
-"y - discard this hunk from index and worktree
-n - do not discard this hunk from index and worktree
-q - quit; do not discard this hunk or any of the remaining ones
-a - discard this hunk and all later hunks in the file
-d - do not discard this hunk or any of the later hunks in the file"),
- checkout_nothead => N__(
-"y - apply this hunk to index and worktree
-n - do not apply this hunk to index and worktree
-q - quit; do not apply this hunk or any of the remaining ones
-a - apply this hunk and all later hunks in the file
-d - do not apply this hunk or any of the later hunks in the file"),
- worktree_head => N__(
-"y - discard this hunk from worktree
-n - do not discard this hunk from worktree
-q - quit; do not discard this hunk or any of the remaining ones
-a - discard this hunk and all later hunks in the file
-d - do not discard this hunk or any of the later hunks in the file"),
- worktree_nothead => N__(
-"y - apply this hunk to worktree
-n - do not apply this hunk to worktree
-q - quit; do not apply this hunk or any of the remaining ones
-a - apply this hunk and all later hunks in the file
-d - do not apply this hunk or any of the later hunks in the file"),
-);
-
-sub help_patch_cmd {
- local $_;
- my $other = $_[0] . ",?";
- print colored $help_color, __($help_patch_modes{$patch_mode}), "\n",
- map { "$_\n" } grep {
- my $c = quotemeta(substr($_, 0, 1));
- $other =~ /,$c/
- } split "\n", __ <<EOF ;
-g - select a hunk to go to
-/ - search for a hunk matching the given regex
-j - leave this hunk undecided, see next undecided hunk
-J - leave this hunk undecided, see next hunk
-k - leave this hunk undecided, see previous undecided hunk
-K - leave this hunk undecided, see previous hunk
-s - split the current hunk into smaller hunks
-e - manually edit the current hunk
-? - print help
-EOF
-}
-
-sub apply_patch {
- my $cmd = shift;
- my $ret = run_git_apply $cmd, @_;
- if (!$ret) {
- print STDERR @_;
- }
- return $ret;
-}
-
-sub apply_patch_for_checkout_commit {
- my $reverse = shift;
- my $applies_index = run_git_apply 'apply '.$reverse.' --cached --check', @_;
- my $applies_worktree = run_git_apply 'apply '.$reverse.' --check', @_;
-
- if ($applies_worktree && $applies_index) {
- run_git_apply 'apply '.$reverse.' --cached', @_;
- run_git_apply 'apply '.$reverse, @_;
- return 1;
- } elsif (!$applies_index) {
- print colored $error_color, __("The selected hunks do not apply to the index!\n");
- if (prompt_yesno __("Apply them to the worktree anyway? ")) {
- return run_git_apply 'apply '.$reverse, @_;
- } else {
- print colored $error_color, __("Nothing was applied.\n");
- return 0;
- }
- } else {
- print STDERR @_;
- return 0;
- }
-}
-
-sub patch_update_cmd {
- my @all_mods = list_modified($patch_mode_flavour{FILTER});
- error_msg sprintf(__("ignoring unmerged: %s\n"), $_->{VALUE})
- for grep { $_->{UNMERGED} } @all_mods;
- @all_mods = grep { !$_->{UNMERGED} } @all_mods;
-
- my @mods = grep { !($_->{BINARY}) } @all_mods;
- my @them;
-
- if (!@mods) {
- if (@all_mods) {
- print STDERR __("Only binary files changed.\n");
- } else {
- print STDERR __("No changes.\n");
- }
- return 0;
- }
- if ($patch_mode_only) {
- @them = @mods;
- }
- else {
- @them = list_and_choose({ PROMPT => __('Patch update'),
- HEADER => $status_head, },
- @mods);
- }
- for (@them) {
- return 0 if patch_update_file($_->{VALUE});
- }
-}
-
-# Generate a one line summary of a hunk.
-sub summarize_hunk {
- my $rhunk = shift;
- my $summary = $rhunk->{TEXT}[0];
-
- # Keep the line numbers, discard extra context.
- $summary =~ s/@@(.*?)@@.*/$1 /s;
- $summary .= " " x (20 - length $summary);
-
- # Add some user context.
- for my $line (@{$rhunk->{TEXT}}) {
- if ($line =~ m/^[+-].*\w/) {
- $summary .= $line;
- last;
- }
- }
-
- chomp $summary;
- return substr($summary, 0, 80) . "\n";
-}
-
-
-# Print a one-line summary of each hunk in the array ref in
-# the first argument, starting with the index in the 2nd.
-sub display_hunks {
- my ($hunks, $i) = @_;
- my $ctr = 0;
- $i ||= 0;
- for (; $i < @$hunks && $ctr < 20; $i++, $ctr++) {
- my $status = " ";
- if (defined $hunks->[$i]{USE}) {
- $status = $hunks->[$i]{USE} ? "+" : "-";
- }
- printf "%s%2d: %s",
- $status,
- $i + 1,
- summarize_hunk($hunks->[$i]);
- }
- return $i;
-}
-
-my %patch_update_prompt_modes = (
- stage => {
- mode => N__("Stage mode change [y,n,q,a,d%s,?]? "),
- deletion => N__("Stage deletion [y,n,q,a,d%s,?]? "),
- addition => N__("Stage addition [y,n,q,a,d%s,?]? "),
- hunk => N__("Stage this hunk [y,n,q,a,d%s,?]? "),
- },
- stash => {
- mode => N__("Stash mode change [y,n,q,a,d%s,?]? "),
- deletion => N__("Stash deletion [y,n,q,a,d%s,?]? "),
- addition => N__("Stash addition [y,n,q,a,d%s,?]? "),
- hunk => N__("Stash this hunk [y,n,q,a,d%s,?]? "),
- },
- reset_head => {
- mode => N__("Unstage mode change [y,n,q,a,d%s,?]? "),
- deletion => N__("Unstage deletion [y,n,q,a,d%s,?]? "),
- addition => N__("Unstage addition [y,n,q,a,d%s,?]? "),
- hunk => N__("Unstage this hunk [y,n,q,a,d%s,?]? "),
- },
- reset_nothead => {
- mode => N__("Apply mode change to index [y,n,q,a,d%s,?]? "),
- deletion => N__("Apply deletion to index [y,n,q,a,d%s,?]? "),
- addition => N__("Apply addition to index [y,n,q,a,d%s,?]? "),
- hunk => N__("Apply this hunk to index [y,n,q,a,d%s,?]? "),
- },
- checkout_index => {
- mode => N__("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
- deletion => N__("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
- addition => N__("Discard addition from worktree [y,n,q,a,d%s,?]? "),
- hunk => N__("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
- },
- checkout_head => {
- mode => N__("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
- deletion => N__("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
- addition => N__("Discard addition from index and worktree [y,n,q,a,d%s,?]? "),
- hunk => N__("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
- },
- checkout_nothead => {
- mode => N__("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
- deletion => N__("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
- addition => N__("Apply addition to index and worktree [y,n,q,a,d%s,?]? "),
- hunk => N__("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
- },
- worktree_head => {
- mode => N__("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
- deletion => N__("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
- addition => N__("Discard addition from worktree [y,n,q,a,d%s,?]? "),
- hunk => N__("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
- },
- worktree_nothead => {
- mode => N__("Apply mode change to worktree [y,n,q,a,d%s,?]? "),
- deletion => N__("Apply deletion to worktree [y,n,q,a,d%s,?]? "),
- addition => N__("Apply addition to worktree [y,n,q,a,d%s,?]? "),
- hunk => N__("Apply this hunk to worktree [y,n,q,a,d%s,?]? "),
- },
-);
-
-sub patch_update_file {
- my $quit = 0;
- my ($ix, $num);
- my $path = shift;
- my ($head, @hunk) = parse_diff($path);
- ($head, my $mode, my $deletion, my $addition) = parse_diff_header($head);
- for (@{$head->{DISPLAY}}) {
- print;
- }
-
- if (@{$mode->{TEXT}}) {
- unshift @hunk, $mode;
- }
- if (@{$deletion->{TEXT}}) {
- foreach my $hunk (@hunk) {
- push @{$deletion->{TEXT}}, @{$hunk->{TEXT}};
- push @{$deletion->{DISPLAY}}, @{$hunk->{DISPLAY}};
- }
- @hunk = ($deletion);
- }
-
- $num = scalar @hunk;
- $ix = 0;
-
- while (1) {
- my ($prev, $next, $other, $undecided, $i);
- $other = '';
-
- last if ($ix and !$num);
- if ($num <= $ix) {
- $ix = 0;
- }
- for ($i = 0; $i < $ix; $i++) {
- if (!defined $hunk[$i]{USE}) {
- $prev = 1;
- $other .= ',k';
- last;
- }
- }
- if ($ix) {
- $other .= ',K';
- }
- for ($i = $ix + 1; $i < $num; $i++) {
- if (!defined $hunk[$i]{USE}) {
- $next = 1;
- $other .= ',j';
- last;
- }
- }
- if ($ix < $num - 1) {
- $other .= ',J';
- }
- if ($num > 1) {
- $other .= ',g,/';
- }
- for ($i = 0; $i < $num; $i++) {
- if (!defined $hunk[$i]{USE}) {
- $undecided = 1;
- last;
- }
- }
- last if (!$undecided && ($num || !$addition));
-
- if ($num) {
- if ($hunk[$ix]{TYPE} eq 'hunk' &&
- hunk_splittable($hunk[$ix]{TEXT})) {
- $other .= ',s';
- }
- if ($hunk[$ix]{TYPE} eq 'hunk') {
- $other .= ',e';
- }
- for (@{$hunk[$ix]{DISPLAY}}) {
- print;
- }
- }
- my $type = $num ? $hunk[$ix]{TYPE} : $head->{TYPE};
- print colored $prompt_color, "(", ($ix+1), "/", ($num ? $num : 1), ") ",
- sprintf(__($patch_update_prompt_modes{$patch_mode}{$type}), $other);
-
- my $line = prompt_single_character;
- last unless defined $line;
- if ($line) {
- if ($line =~ /^y/i) {
- if ($num) {
- $hunk[$ix]{USE} = 1;
- } else {
- $head->{USE} = 1;
- }
- }
- elsif ($line =~ /^n/i) {
- if ($num) {
- $hunk[$ix]{USE} = 0;
- } else {
- $head->{USE} = 0;
- }
- }
- elsif ($line =~ /^a/i) {
- if ($num) {
- while ($ix < $num) {
- if (!defined $hunk[$ix]{USE}) {
- $hunk[$ix]{USE} = 1;
- }
- $ix++;
- }
- } else {
- $head->{USE} = 1;
- $ix++;
- }
- next;
- }
- elsif ($line =~ /^g(.*)/) {
- my $response = $1;
- unless ($other =~ /g/) {
- error_msg __("No other hunks to goto\n");
- next;
- }
- my $no = $ix > 10 ? $ix - 10 : 0;
- while ($response eq '') {
- $no = display_hunks(\@hunk, $no);
- if ($no < $num) {
- print __("go to which hunk (<ret> to see more)? ");
- } else {
- print __("go to which hunk? ");
- }
- $response = <STDIN>;
- if (!defined $response) {
- $response = '';
- }
- chomp $response;
- }
- if ($response !~ /^\s*\d+\s*$/) {
- error_msg sprintf(__("Invalid number: '%s'\n"),
- $response);
- } elsif (0 < $response && $response <= $num) {
- $ix = $response - 1;
- } else {
- error_msg sprintf(__n("Sorry, only %d hunk available.\n",
- "Sorry, only %d hunks available.\n", $num), $num);
- }
- next;
- }
- elsif ($line =~ /^d/i) {
- if ($num) {
- while ($ix < $num) {
- if (!defined $hunk[$ix]{USE}) {
- $hunk[$ix]{USE} = 0;
- }
- $ix++;
- }
- } else {
- $head->{USE} = 0;
- $ix++;
- }
- next;
- }
- elsif ($line =~ /^q/i) {
- if ($num) {
- for ($i = 0; $i < $num; $i++) {
- if (!defined $hunk[$i]{USE}) {
- $hunk[$i]{USE} = 0;
- }
- }
- } elsif (!defined $head->{USE}) {
- $head->{USE} = 0;
- }
- $quit = 1;
- last;
- }
- elsif ($line =~ m|^/(.*)|) {
- my $regex = $1;
- unless ($other =~ m|/|) {
- error_msg __("No other hunks to search\n");
- next;
- }
- if ($regex eq "") {
- print colored $prompt_color, __("search for regex? ");
- $regex = <STDIN>;
- if (defined $regex) {
- chomp $regex;
- }
- }
- my $search_string;
- eval {
- $search_string = qr{$regex}m;
- };
- if ($@) {
- my ($err,$exp) = ($@, $1);
- $err =~ s/ at .*git-add--interactive line \d+, <STDIN> line \d+.*$//;
- error_msg sprintf(__("Malformed search regexp %s: %s\n"), $exp, $err);
- next;
- }
- my $iy = $ix;
- while (1) {
- my $text = join ("", @{$hunk[$iy]{TEXT}});
- last if ($text =~ $search_string);
- $iy++;
- $iy = 0 if ($iy >= $num);
- if ($ix == $iy) {
- error_msg __("No hunk matches the given pattern\n");
- last;
- }
- }
- $ix = $iy;
- next;
- }
- elsif ($line =~ /^K/) {
- if ($other =~ /K/) {
- $ix--;
- }
- else {
- error_msg __("No previous hunk\n");
- }
- next;
- }
- elsif ($line =~ /^J/) {
- if ($other =~ /J/) {
- $ix++;
- }
- else {
- error_msg __("No next hunk\n");
- }
- next;
- }
- elsif ($line =~ /^k/) {
- if ($other =~ /k/) {
- while (1) {
- $ix--;
- last if (!$ix ||
- !defined $hunk[$ix]{USE});
- }
- }
- else {
- error_msg __("No previous hunk\n");
- }
- next;
- }
- elsif ($line =~ /^j/) {
- if ($other !~ /j/) {
- error_msg __("No next hunk\n");
- next;
- }
- }
- elsif ($line =~ /^s/) {
- unless ($other =~ /s/) {
- error_msg __("Sorry, cannot split this hunk\n");
- next;
- }
- my @split = split_hunk($hunk[$ix]{TEXT}, $hunk[$ix]{DISPLAY});
- if (1 < @split) {
- print colored $header_color, sprintf(
- __n("Split into %d hunk.\n",
- "Split into %d hunks.\n",
- scalar(@split)), scalar(@split));
- }
- splice (@hunk, $ix, 1, @split);
- $num = scalar @hunk;
- next;
- }
- elsif ($line =~ /^e/) {
- unless ($other =~ /e/) {
- error_msg __("Sorry, cannot edit this hunk\n");
- next;
- }
- my $newhunk = edit_hunk_loop($head, \@hunk, $ix);
- if (defined $newhunk) {
- splice @hunk, $ix, 1, $newhunk;
- }
- }
- else {
- help_patch_cmd($other);
- next;
- }
- # soft increment
- while (1) {
- $ix++;
- last if ($ix >= $num ||
- !defined $hunk[$ix]{USE});
- }
- }
- }
-
- @hunk = coalesce_overlapping_hunks(@hunk) if ($num);
-
- my $n_lofs = 0;
- my @result = ();
- for (@hunk) {
- if ($_->{USE}) {
- push @result, @{$_->{TEXT}};
- }
- }
-
- if (@result or $head->{USE}) {
- my @patch = reassemble_patch($head->{TEXT}, @result);
- my $apply_routine = $patch_mode_flavour{APPLY};
- &$apply_routine(@patch);
- refresh();
- }
-
- print "\n";
- return $quit;
-}
-
-sub diff_cmd {
- my @mods = list_modified('index-only');
- @mods = grep { !($_->{BINARY}) } @mods;
- return if (!@mods);
- my (@them) = list_and_choose({ PROMPT => __('Review diff'),
- IMMEDIATE => 1,
- HEADER => $status_head, },
- @mods);
- return if (!@them);
- my $reference = (is_initial_commit()) ? get_empty_tree() : 'HEAD';
- system(qw(git diff -p --cached), $reference, '--',
- map { $_->{VALUE} } @them);
-}
-
-sub quit_cmd {
- print __("Bye.\n");
- exit(0);
-}
-
-sub help_cmd {
-# TRANSLATORS: please do not translate the command names
-# 'status', 'update', 'revert', etc.
- print colored $help_color, __ <<'EOF' ;
-status - show paths with changes
-update - add working tree state to the staged set of changes
-revert - revert staged set of changes back to the HEAD version
-patch - pick hunks and update selectively
-diff - view diff between HEAD and index
-add untracked - add contents of untracked files to the staged set of changes
-EOF
-}
-
-sub process_args {
- return unless @ARGV;
- my $arg = shift @ARGV;
- if ($arg =~ /--patch(?:=(.*))?/) {
- if (defined $1) {
- if ($1 eq 'reset') {
- $patch_mode = 'reset_head';
- $patch_mode_revision = 'HEAD';
- $arg = shift @ARGV or die __("missing --");
- if ($arg ne '--') {
- $patch_mode_revision = $arg;
-
- # NEEDSWORK: Instead of comparing to the literal "HEAD",
- # compare the commit objects instead so that other ways of
- # saying the same thing (such as "@") are also handled
- # appropriately.
- #
- # This applies to the cases below too.
- $patch_mode = ($arg eq 'HEAD' ?
- 'reset_head' : 'reset_nothead');
- $arg = shift @ARGV or die __("missing --");
- }
- } elsif ($1 eq 'checkout') {
- $arg = shift @ARGV or die __("missing --");
- if ($arg eq '--') {
- $patch_mode = 'checkout_index';
- } else {
- $patch_mode_revision = $arg;
- $patch_mode = ($arg eq 'HEAD' ?
- 'checkout_head' : 'checkout_nothead');
- $arg = shift @ARGV or die __("missing --");
- }
- } elsif ($1 eq 'worktree') {
- $arg = shift @ARGV or die __("missing --");
- if ($arg eq '--') {
- $patch_mode = 'checkout_index';
- } else {
- $patch_mode_revision = $arg;
- $patch_mode = ($arg eq 'HEAD' ?
- 'worktree_head' : 'worktree_nothead');
- $arg = shift @ARGV or die __("missing --");
- }
- } elsif ($1 eq 'stage' or $1 eq 'stash') {
- $patch_mode = $1;
- $arg = shift @ARGV or die __("missing --");
- } else {
- die sprintf(__("unknown --patch mode: %s"), $1);
- }
- } else {
- $patch_mode = 'stage';
- $arg = shift @ARGV or die __("missing --");
- }
- die sprintf(__("invalid argument %s, expecting --"),
- $arg) unless $arg eq "--";
- %patch_mode_flavour = %{$patch_modes{$patch_mode}};
- $patch_mode_only = 1;
- }
- elsif ($arg ne "--") {
- die sprintf(__("invalid argument %s, expecting --"), $arg);
- }
-}
-
-sub main_loop {
- my @cmd = ([ 'status', \&status_cmd, ],
- [ 'update', \&update_cmd, ],
- [ 'revert', \&revert_cmd, ],
- [ 'add untracked', \&add_untracked_cmd, ],
- [ 'patch', \&patch_update_cmd, ],
- [ 'diff', \&diff_cmd, ],
- [ 'quit', \&quit_cmd, ],
- [ 'help', \&help_cmd, ],
- );
- while (1) {
- my ($it) = list_and_choose({ PROMPT => __('What now'),
- SINGLETON => 1,
- LIST_FLAT => 4,
- HEADER => __('*** Commands ***'),
- ON_EOF => \&quit_cmd,
- IMMEDIATE => 1 }, @cmd);
- if ($it) {
- eval {
- $it->[1]->();
- };
- if ($@) {
- print "$@";
- }
- }
- }
-}
-
-process_args();
-refresh();
-if ($patch_mode_only) {
- patch_update_cmd();
-}
-else {
- status_cmd();
- main_loop();
-}
diff --git a/git-bisect.sh b/git-bisect.sh
deleted file mode 100755
index dfce4b4..0000000
--- a/git-bisect.sh
+++ /dev/null
@@ -1,67 +0,0 @@
-#!/bin/sh
-
-USAGE='[help|start|bad|good|new|old|terms|skip|next|reset|visualize|view|replay|log|run]'
-LONG_USAGE='git bisect help
- print this long help message.
-git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]
- [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]
- reset bisect state and start bisection.
-git bisect (bad|new) [<rev>]
- mark <rev> a known-bad revision/
- a revision after change in a given property.
-git bisect (good|old) [<rev>...]
- mark <rev>... known-good revisions/
- revisions before change in a given property.
-git bisect terms [--term-good | --term-bad]
- show the terms used for old and new commits (default: bad, good)
-git bisect skip [(<rev>|<range>)...]
- mark <rev>... untestable revisions.
-git bisect next
- find next bisection to test and check it out.
-git bisect reset [<commit>]
- finish bisection search and go back to commit.
-git bisect (visualize|view)
- show bisect status in gitk.
-git bisect replay <logfile>
- replay bisection log.
-git bisect log
- show bisect log.
-git bisect run <cmd>...
- use <cmd>... to automatically bisect.
-
-Please use "git help bisect" to get the full man page.'
-
-OPTIONS_SPEC=
-. git-sh-setup
-
-TERM_BAD=bad
-TERM_GOOD=good
-
-get_terms () {
- if test -s "$GIT_DIR/BISECT_TERMS"
- then
- {
- read TERM_BAD
- read TERM_GOOD
- } <"$GIT_DIR/BISECT_TERMS"
- fi
-}
-
-case "$#" in
-0)
- usage ;;
-*)
- cmd="$1"
- get_terms
- shift
- case "$cmd" in
- help)
- git bisect -h ;;
- bad|good|new|old|"$TERM_BAD"|"$TERM_GOOD")
- git bisect--helper state "$cmd" "$@" ;;
- log)
- git bisect--helper log || exit ;;
- *)
- git bisect--helper "$cmd" "$@" ;;
- esac
-esac
diff --git a/git-compat-util.h b/git-compat-util.h
index a76d052..4f0028c 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -97,8 +97,14 @@
# define BARF_UNLESS_AN_ARRAY(arr) \
BUILD_ASSERT_OR_ZERO(!__builtin_types_compatible_p(__typeof__(arr), \
__typeof__(&(arr)[0])))
+# define BARF_UNLESS_COPYABLE(dst, src) \
+ BUILD_ASSERT_OR_ZERO(__builtin_types_compatible_p(__typeof__(*(dst)), \
+ __typeof__(*(src))))
#else
# define BARF_UNLESS_AN_ARRAY(arr) 0
+# define BARF_UNLESS_COPYABLE(dst, src) \
+ BUILD_ASSERT_OR_ZERO(0 ? ((*(dst) = *(src)), 0) : \
+ sizeof(*(dst)) == sizeof(*(src)))
#endif
/*
* ARRAY_SIZE - get the number of elements in a visible array
@@ -341,11 +347,13 @@
#endif
#ifdef NO_SETITIMER
-static inline int setitimer(int which UNUSED,
- const struct itimerval *value UNUSED,
- struct itimerval *newvalue UNUSED) {
+static inline int git_setitimer(int which UNUSED,
+ const struct itimerval *value UNUSED,
+ struct itimerval *newvalue UNUSED) {
return 0; /* pretend success */
}
+#undef setitimer
+#define setitimer(which,value,ovalue) git_setitimer(which,value,ovalue)
#endif
#ifndef NO_LIBGEN_H
@@ -1020,6 +1028,14 @@
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
@@ -1092,7 +1108,7 @@
#define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), st_mult(sizeof(*(x)), (alloc)))
#define COPY_ARRAY(dst, src, n) copy_array((dst), (src), (n), sizeof(*(dst)) + \
- BUILD_ASSERT_OR_ZERO(sizeof(*(dst)) == sizeof(*(src))))
+ BARF_UNLESS_COPYABLE((dst), (src)))
static inline void copy_array(void *dst, const void *src, size_t n, size_t size)
{
if (n)
@@ -1100,13 +1116,18 @@
}
#define MOVE_ARRAY(dst, src, n) move_array((dst), (src), (n), sizeof(*(dst)) + \
- BUILD_ASSERT_OR_ZERO(sizeof(*(dst)) == sizeof(*(src))))
+ BARF_UNLESS_COPYABLE((dst), (src)))
static inline void move_array(void *dst, const void *src, size_t n, size_t size)
{
if (n)
memmove(dst, src, st_mult(size, n));
}
+#define DUP_ARRAY(dst, src, n) do { \
+ size_t dup_array_n_ = (n); \
+ COPY_ARRAY(ALLOC_ARRAY((dst), dup_array_n_), (src), dup_array_n_); \
+} while (0)
+
/*
* These functions help you allocate structs with flex arrays, and copy
* the data directly into the array. For example, if you had:
@@ -1336,6 +1357,11 @@
return regexec(preg, buf, nmatch, pmatch, eflags | REG_STARTEND);
}
+#ifdef USE_ENHANCED_BASIC_REGULAR_EXPRESSIONS
+int git_regcomp(regex_t *preg, const char *pattern, int cflags);
+#define regcomp git_regcomp
+#endif
+
#ifndef DIR_HAS_BSD_GROUP_SEMANTICS
# define FORCE_DIR_SET_GID S_ISGID
#else
@@ -1471,14 +1497,19 @@
#endif
#ifndef _POSIX_THREAD_SAFE_FUNCTIONS
-static inline void flockfile(FILE *fh UNUSED)
+static inline void git_flockfile(FILE *fh UNUSED)
{
; /* nothing */
}
-static inline void funlockfile(FILE *fh UNUSED)
+static inline void git_funlockfile(FILE *fh UNUSED)
{
; /* nothing */
}
+#undef flockfile
+#undef funlockfile
+#undef getc_unlocked
+#define flockfile(fh) git_flockfile(fh)
+#define funlockfile(fh) git_funlockfile(fh)
#define getc_unlocked(fh) getc(fh)
#endif
diff --git a/git-curl-compat.h b/git-curl-compat.h
index 56a83b6..fd96b3c 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-request-pull.sh b/git-request-pull.sh
index 2d0e446..01640a0 100755
--- a/git-request-pull.sh
+++ b/git-request-pull.sh
@@ -153,7 +153,7 @@
if test $(git cat-file -t "$head") = tag
then
git cat-file tag "$head" |
- sed -n -e '1,/^$/d' -e '/^-----BEGIN PGP /q' -e p
+ sed -n -e '1,/^$/d' -e '/^-----BEGIN \(PGP\|SSH\|SIGNED\) /q' -e p
echo
echo "----------------------------------------------------------------"
fi &&
diff --git a/git-send-email.perl b/git-send-email.perl
index 5861e99..07f2a0c 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -220,6 +220,10 @@
my $force = 0;
my $dump_aliases = 0;
+# Variables to prevent short format-patch options from being captured
+# as abbreviated send-email options
+my $reroll_count;
+
# Handle interactive edition of files.
my $multiedit;
my $editor;
@@ -542,6 +546,7 @@
"batch-size=i" => \$batch_size,
"relogin-delay=i" => \$relogin_delay,
"git-completion-helper" => \$git_completion_helper,
+ "v=s" => \$reroll_count,
);
$rc = GetOptions(%options);
@@ -782,7 +787,9 @@
die __("Cannot run git format-patch from outside a repository\n")
unless $repo;
require File::Temp;
- push @files, $repo->command('format-patch', '-o', File::Temp::tempdir(CLEANUP => 1), @rev_list_opts);
+ push @files, $repo->command('format-patch', '-o', File::Temp::tempdir(CLEANUP => 1),
+ defined $reroll_count ? ('-v', $reroll_count) : (),
+ @rev_list_opts);
}
@files = handle_backup_files(@files);
diff --git a/git-submodule.sh b/git-submodule.sh
index 9a50f2e..7f9582d 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -244,6 +244,9 @@
-q|--quiet)
quiet=1
;;
+ -v|--verbose)
+ quiet=0
+ ;;
--progress)
progress=1
;;
diff --git a/git.c b/git.c
index 10202a7..96b0a28 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 @@
" [--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 @@
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 @@
trace_repo_setup(prefix);
commit_pager_choice();
- if (!help && get_super_prefix()) {
- if (!(p->option & SUPPORT_SUPER_PREFIX))
- die(_("%s doesn't support --super-prefix"), p->cmd);
- }
-
if (!help && p->option & NEED_WORK_TREE)
setup_work_tree();
@@ -492,7 +471,7 @@
{ "annotate", cmd_annotate, RUN_SETUP },
{ "apply", cmd_apply, RUN_SETUP_GENTLY },
{ "archive", cmd_archive, RUN_SETUP_GENTLY },
- { "bisect--helper", cmd_bisect__helper, RUN_SETUP },
+ { "bisect", cmd_bisect, RUN_SETUP },
{ "blame", cmd_blame, RUN_SETUP },
{ "branch", cmd_branch, RUN_SETUP | DELAY_PAGER_CONFIG },
{ "bugreport", cmd_bugreport, RUN_SETUP_GENTLY },
@@ -504,7 +483,7 @@
{ "check-ref-format", cmd_check_ref_format, NO_PARSEOPT },
{ "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
{ "checkout--worker", cmd_checkout__worker,
- RUN_SETUP | NEED_WORK_TREE | SUPPORT_SUPER_PREFIX },
+ RUN_SETUP | NEED_WORK_TREE },
{ "checkout-index", cmd_checkout_index,
RUN_SETUP | NEED_WORK_TREE},
{ "cherry", cmd_cherry, RUN_SETUP },
@@ -528,7 +507,6 @@
{ "diff-index", cmd_diff_index, RUN_SETUP | NO_PARSEOPT },
{ "diff-tree", cmd_diff_tree, RUN_SETUP | NO_PARSEOPT },
{ "difftool", cmd_difftool, RUN_SETUP_GENTLY },
- { "env--helper", cmd_env__helper },
{ "fast-export", cmd_fast_export, RUN_SETUP },
{ "fast-import", cmd_fast_import, RUN_SETUP | NO_PARSEOPT },
{ "fetch", cmd_fetch, RUN_SETUP },
@@ -539,7 +517,7 @@
{ "format-patch", cmd_format_patch, RUN_SETUP },
{ "fsck", cmd_fsck, RUN_SETUP },
{ "fsck-objects", cmd_fsck, RUN_SETUP },
- { "fsmonitor--daemon", cmd_fsmonitor__daemon, SUPPORT_SUPER_PREFIX | RUN_SETUP },
+ { "fsmonitor--daemon", cmd_fsmonitor__daemon, RUN_SETUP },
{ "gc", cmd_gc, RUN_SETUP },
{ "get-tar-commit-id", cmd_get_tar_commit_id, NO_PARSEOPT },
{ "grep", cmd_grep, RUN_SETUP_GENTLY },
@@ -583,7 +561,7 @@
{ "pull", cmd_pull, RUN_SETUP | NEED_WORK_TREE },
{ "push", cmd_push, RUN_SETUP },
{ "range-diff", cmd_range_diff, RUN_SETUP | USE_PAGER },
- { "read-tree", cmd_read_tree, RUN_SETUP | SUPPORT_SUPER_PREFIX},
+ { "read-tree", cmd_read_tree, RUN_SETUP },
{ "rebase", cmd_rebase, RUN_SETUP | NEED_WORK_TREE },
{ "receive-pack", cmd_receive_pack },
{ "reflog", cmd_reflog, RUN_SETUP },
@@ -610,7 +588,7 @@
{ "stash", cmd_stash, RUN_SETUP | NEED_WORK_TREE },
{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
{ "stripspace", cmd_stripspace },
- { "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX },
+ { "submodule--helper", cmd_submodule__helper, RUN_SETUP },
{ "switch", cmd_switch, RUN_SETUP | NEED_WORK_TREE },
{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
{ "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
@@ -727,9 +705,6 @@
struct child_process cmd = CHILD_PROCESS_INIT;
int status;
- if (get_super_prefix())
- die(_("%s doesn't support --super-prefix"), argv[0]);
-
if (use_pager == -1 && !is_builtin(argv[0]))
use_pager = check_pager_config(argv[0]);
commit_pager_choice();
@@ -799,9 +774,6 @@
*/
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/gpg-interface.c b/gpg-interface.c
index f877a1e..5cd66d3 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -977,9 +977,13 @@
break; /* found */
}
ret |= !cp;
+ if (ret) {
+ error(_("gpg failed to sign the data:\n%s"),
+ gpg_status.len ? gpg_status.buf : "(no gpg output)");
+ strbuf_release(&gpg_status);
+ return -1;
+ }
strbuf_release(&gpg_status);
- if (ret)
- return error(_("gpg failed to sign the data"));
/* Strip CR from the line endings, in case we are on Windows. */
remove_cr_after(signature, bottom);
@@ -998,6 +1002,7 @@
char *ssh_signing_key_file = NULL;
struct strbuf ssh_signature_filename = STRBUF_INIT;
const char *literal_key = NULL;
+ int literal_ssh_key = 0;
if (!signing_key || signing_key[0] == '\0')
return error(
@@ -1005,6 +1010,7 @@
if (is_literal_ssh_key(signing_key, &literal_key)) {
/* A literal ssh key */
+ literal_ssh_key = 1;
key_file = mks_tempfile_t(".git_signing_key_tmpXXXXXX");
if (!key_file)
return error_errno(
@@ -1039,8 +1045,10 @@
"-Y", "sign",
"-n", "git",
"-f", ssh_signing_key_file,
- buffer_file->filename.buf,
NULL);
+ if (literal_ssh_key)
+ strvec_push(&signer.args, "-U");
+ strvec_push(&signer.args, buffer_file->filename.buf);
sigchain_push(SIGPIPE, SIG_IGN);
ret = pipe_command(&signer, NULL, 0, NULL, 0, &signer_stderr, 0);
diff --git a/grep.c b/grep.c
index 06eed69..cee44a7 100644
--- a/grep.c
+++ b/grep.c
@@ -262,6 +262,31 @@
free(pointer);
}
+static int pcre2_jit_functional(void)
+{
+ static int jit_working = -1;
+ pcre2_code *code;
+ size_t off;
+ int err;
+
+ if (jit_working != -1)
+ return jit_working;
+
+ /*
+ * Try to JIT compile a simple pattern to probe if the JIT is
+ * working in general. It might fail for systems where creating
+ * memory mappings for runtime code generation is restricted.
+ */
+ code = pcre2_compile((PCRE2_SPTR)".", 1, 0, &err, &off, NULL);
+ if (!code)
+ return 0;
+
+ jit_working = pcre2_jit_compile(code, PCRE2_JIT_COMPLETE) == 0;
+ pcre2_code_free(code);
+
+ return jit_working;
+}
+
static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt)
{
int error;
@@ -293,7 +318,7 @@
options |= PCRE2_CASELESS;
}
if (!opt->ignore_locale && is_utf8_locale() && !literal)
- options |= (PCRE2_UTF | PCRE2_MATCH_INVALID_UTF);
+ options |= (PCRE2_UTF | PCRE2_UCP | PCRE2_MATCH_INVALID_UTF);
#ifndef GIT_PCRE2_VERSION_10_36_OR_HIGHER
/* Work around https://bugs.exim.org/show_bug.cgi?id=2642 fixed in 10.36 */
@@ -317,8 +342,29 @@
pcre2_config(PCRE2_CONFIG_JIT, &p->pcre2_jit_on);
if (p->pcre2_jit_on) {
jitret = pcre2_jit_compile(p->pcre2_pattern, PCRE2_JIT_COMPLETE);
- if (jitret)
- die("Couldn't JIT the PCRE2 pattern '%s', got '%d'\n", p->pattern, jitret);
+ if (jitret == PCRE2_ERROR_NOMEMORY && !pcre2_jit_functional()) {
+ /*
+ * Even though pcre2_config(PCRE2_CONFIG_JIT, ...)
+ * indicated JIT support, the library might still
+ * fail to generate JIT code for various reasons,
+ * e.g. when SELinux's 'deny_execmem' or PaX's
+ * MPROTECT prevent creating W|X memory mappings.
+ *
+ * Instead of faling hard, fall back to interpreter
+ * mode, just as if the pattern was prefixed with
+ * '(*NO_JIT)'.
+ */
+ p->pcre2_jit_on = 0;
+ return;
+ } else if (jitret) {
+ int need_clip = p->patternlen > 64;
+ int clip_len = need_clip ? 64 : p->patternlen;
+ die("Couldn't JIT the PCRE2 pattern '%.*s'%s, got '%d'%s",
+ clip_len, p->pattern, need_clip ? "..." : "", jitret,
+ pcre2_jit_functional()
+ ? "\nPerhaps prefix (*NO_JIT) to your pattern?"
+ : "");
+ }
/*
* The pcre2_config(PCRE2_CONFIG_JIT, ...) call just
@@ -769,11 +815,11 @@
free(x);
}
-void free_grep_patterns(struct grep_opt *opt)
+static void free_grep_pat(struct grep_pat *pattern)
{
struct grep_pat *p, *n;
- for (p = opt->pattern_list; p; p = n) {
+ for (p = pattern; p; p = n) {
n = p->next;
switch (p->token) {
case GREP_PATTERN: /* atom */
@@ -790,10 +836,15 @@
}
free(p);
}
+}
- if (!opt->pattern_expression)
- return;
- free_pattern_expr(opt->pattern_expression);
+void free_grep_patterns(struct grep_opt *opt)
+{
+ free_grep_pat(opt->pattern_list);
+ free_grep_pat(opt->header_list);
+
+ if (opt->pattern_expression)
+ free_pattern_expr(opt->pattern_expression);
}
static const char *end_of_line(const char *cp, unsigned long *left)
diff --git a/help.c b/help.c
index f1e090a..812af4c 100644
--- a/help.c
+++ b/help.c
@@ -563,7 +563,7 @@
if (skip_prefix(var, "alias.", &p))
add_cmdname(&aliases, p, strlen(p));
- return git_default_config(var, value, cb);
+ return 0;
}
static int levenshtein_compare(const void *p1, const void *p2)
diff --git a/hook.c b/hook.c
index a4fa103..1a84831 100644
--- a/hook.c
+++ b/hook.c
@@ -55,6 +55,11 @@
cp->no_stdin = 1;
strvec_pushv(&cp->env, hook_cb->options->env.v);
+ /* reopen the file for stdin; run_command closes it. */
+ if (hook_cb->options->path_to_stdin) {
+ cp->no_stdin = 0;
+ cp->in = xopen(hook_cb->options->path_to_stdin, O_RDONLY);
+ }
cp->stdout_to_stderr = 1;
cp->trace2_hook_name = hook_cb->hook_name;
cp->dir = hook_cb->options->dir;
diff --git a/hook.h b/hook.h
index 4258b13..19ab9a5 100644
--- a/hook.h
+++ b/hook.h
@@ -30,6 +30,11 @@
* was invoked.
*/
int *invoked_hook;
+
+ /**
+ * Path to file which should be piped to stdin for each hook.
+ */
+ const char *path_to_stdin;
};
#define RUN_HOOKS_OPT_INIT { \
diff --git a/http-backend.c b/http-backend.c
index 6eb3b2f..8ab58e5 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -759,10 +759,14 @@
struct service_cmd *c = &services[i];
regex_t re;
regmatch_t out[1];
+ int ret;
if (regcomp(&re, c->pattern, REG_EXTENDED))
die("Bogus regex in service table: %s", c->pattern);
- if (!regexec(&re, dir, 1, out, 0)) {
+ ret = regexec(&re, dir, 1, out, 0);
+ regfree(&re);
+
+ if (!ret) {
size_t n;
if (strcmp(method, c->method))
@@ -774,7 +778,6 @@
dir[out[0].rm_so] = 0;
break;
}
- regfree(&re);
}
if (!cmd)
@@ -786,6 +789,7 @@
if (!getenv("GIT_HTTP_EXPORT_ALL") &&
access("git-daemon-export-ok", F_OK) )
not_found(&hdr, "Repository not exported: '%s'", dir);
+ free(dir);
http_config();
max_request_buffer = git_env_ulong("GIT_HTTP_MAX_REQUEST_BUFFER",
@@ -795,5 +799,6 @@
setenv(GIT_PROTOCOL_ENVIRONMENT, proto_header, 0);
cmd->imp(&hdr, cmd_arg);
+ free(cmd_arg);
return 0;
}
diff --git a/http-fetch.c b/http-fetch.c
index 31bc5c7..258fec2 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -5,6 +5,7 @@
#include "walker.h"
#include "strvec.h"
#include "urlmatch.h"
+#include "trace2.h"
static const char http_fetch_usage[] = "git http-fetch "
"[-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin | --packfile=hash | commit-id] url";
@@ -137,6 +138,8 @@
if (nongit)
die(_("not a git repository"));
+ trace2_cmd_name("http-fetch");
+
git_config(git_default_config, NULL);
if (packfile) {
diff --git a/http-push.c b/http-push.c
index 5f4340a..7f71316 100644
--- a/http-push.c
+++ b/http-push.c
@@ -198,13 +198,13 @@
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 8a5ba3f..c4b6dde 100644
--- a/http.c
+++ b/http.c
@@ -157,21 +157,19 @@
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 @@
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 @@
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 3c94c47..77c0427 100644
--- a/http.h
+++ b/http.h
@@ -40,7 +40,7 @@
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 955a8a9..47bf0d6 100644
--- a/line-range.c
+++ b/line-range.c
@@ -135,7 +135,7 @@
{
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 @@
/* 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 @@
return bol;
start = eol;
}
+ return NULL;
}
static const char *parse_range_funcname(
diff --git a/list-objects-filter-options.c b/list-objects-filter-options.c
index 5339660..ee01bcd 100644
--- a/list-objects-filter-options.c
+++ b/list-objects-filter-options.c
@@ -290,10 +290,6 @@
const char *arg, int unset)
{
struct list_objects_filter_options *filter_options = opt->value;
- opt_lof_init init = (opt_lof_init)opt->defval;
-
- if (init)
- filter_options = init(opt->value);
if (unset || !arg)
list_objects_filter_set_no_filter(filter_options);
diff --git a/list-objects-filter-options.h b/list-objects-filter-options.h
index 7eeadab..1fe393f 100644
--- a/list-objects-filter-options.h
+++ b/list-objects-filter-options.h
@@ -111,27 +111,13 @@
* The opt->value to opt_parse_list_objects_filter() is either a
* "struct list_objects_filter_option *" when using
* OPT_PARSE_LIST_OBJECTS_FILTER().
- *
- * Or, if using no "struct option" field is used by the callback,
- * except the "defval" which is expected to be an "opt_lof_init"
- * function, which is called with the "opt->value" and must return a
- * pointer to the ""struct list_objects_filter_option *" to be used.
- *
- * The OPT_PARSE_LIST_OBJECTS_FILTER_INIT() can be used e.g. the
- * "struct list_objects_filter_option" is embedded in a "struct
- * rev_info", which the "defval" could be tasked with lazily
- * initializing. See cmd_pack_objects() for an example.
*/
int opt_parse_list_objects_filter(const struct option *opt,
const char *arg, int unset);
-typedef struct list_objects_filter_options *(*opt_lof_init)(void *);
-#define OPT_PARSE_LIST_OBJECTS_FILTER_INIT(fo, init) \
- { OPTION_CALLBACK, 0, "filter", (fo), N_("args"), \
- N_("object filtering"), 0, opt_parse_list_objects_filter, \
- (intptr_t)(init) }
#define OPT_PARSE_LIST_OBJECTS_FILTER(fo) \
- OPT_PARSE_LIST_OBJECTS_FILTER_INIT((fo), NULL)
+ OPT_CALLBACK(0, "filter", (fo), N_("args"), \
+ N_("object filtering"), opt_parse_list_objects_filter)
/*
* Translates abbreviated numbers in the filter's filter_spec into their
diff --git a/list-objects-filter.c b/list-objects-filter.c
index b954354..7ed21cb 100644
--- a/list-objects-filter.c
+++ b/list-objects-filter.c
@@ -70,13 +70,13 @@
};
static enum list_objects_filter_result filter_blobs_none(
- struct repository *r,
+ struct repository *r UNUSED,
enum list_objects_filter_situation filter_situation,
struct object *obj,
- const char *pathname,
- const char *filename,
+ const char *pathname UNUSED,
+ const char *filename UNUSED,
struct oidset *omits,
- void *filter_data_)
+ void *filter_data_ UNUSED)
{
switch (filter_situation) {
default:
@@ -112,7 +112,7 @@
}
static void filter_blobs_none__init(
- struct list_objects_filter_options *filter_options,
+ struct list_objects_filter_options *filter_options UNUSED,
struct filter *filter)
{
filter->filter_object_fn = filter_blobs_none;
@@ -159,11 +159,11 @@
}
static enum list_objects_filter_result filter_trees_depth(
- struct repository *r,
+ struct repository *r UNUSED,
enum list_objects_filter_situation filter_situation,
struct object *obj,
- const char *pathname,
- const char *filename,
+ const char *pathname UNUSED,
+ const char *filename UNUSED,
struct oidset *omits,
void *filter_data_)
{
@@ -274,8 +274,8 @@
struct repository *r,
enum list_objects_filter_situation filter_situation,
struct object *obj,
- const char *pathname,
- const char *filename,
+ const char *pathname UNUSED,
+ const char *filename UNUSED,
struct oidset *omits,
void *filter_data_)
{
@@ -514,6 +514,7 @@
static void filter_sparse_free(void *filter_data)
{
struct filter_sparse_data *d = filter_data;
+ clear_pattern_list(&d->pl);
free(d->array_frame);
free(d);
}
@@ -554,12 +555,12 @@
};
static enum list_objects_filter_result filter_object_type(
- struct repository *r,
+ struct repository *r UNUSED,
enum list_objects_filter_situation filter_situation,
struct object *obj,
- const char *pathname,
- const char *filename,
- struct oidset *omits,
+ const char *pathname UNUSED,
+ const char *filename UNUSED,
+ struct oidset *omits UNUSED,
void *filter_data_)
{
struct filter_object_type_data *filter_data = filter_data_;
@@ -675,7 +676,7 @@
struct object *obj,
const char *pathname,
const char *filename,
- struct oidset *omits,
+ struct oidset *omits UNUSED,
void *filter_data)
{
struct combine_filter_data *d = filter_data;
diff --git a/list-objects.c b/list-objects.c
index 250d9de..7528fe1 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -81,36 +81,6 @@
strbuf_setlen(path, pathlen);
}
-/*
- * Processing a gitlink entry currently does nothing, since
- * we do not recurse into the subproject.
- *
- * We *could* eventually add a flag that actually does that,
- * which would involve:
- * - is the subproject actually checked out?
- * - if so, see if the subproject has already been added
- * to the alternates list, and add it if not.
- * - process the commit (or tag) the gitlink points to
- * recursively.
- *
- * However, it's unclear whether there is really ever any
- * reason to see superprojects and subprojects as such a
- * "unified" object pool (potentially resulting in a totally
- * humongous pack - avoiding which was the whole point of
- * having gitlinks in the first place!).
- *
- * So for now, there is just a note that we *could* follow
- * the link, and how to do it. Whether it necessarily makes
- * any sense what-so-ever to ever do that is another issue.
- */
-static void process_gitlink(struct traversal_context *ctx,
- const unsigned char *sha1,
- struct strbuf *path,
- const char *name)
-{
- /* Nothing to do */
-}
-
static void process_tree(struct traversal_context *ctx,
struct tree *tree,
struct strbuf *base,
@@ -149,8 +119,7 @@
process_tree(ctx, t, base, entry.path);
}
else if (S_ISGITLINK(entry.mode))
- process_gitlink(ctx, entry.oid.hash,
- base, entry.path);
+ ; /* ignore gitlink */
else {
struct blob *b = lookup_blob(ctx->revs->repo, &entry.oid);
if (!b) {
diff --git a/ll-merge.c b/ll-merge.c
index 22a603e..130d265 100644
--- a/ll-merge.c
+++ b/ll-merge.c
@@ -391,7 +391,7 @@
normalize_file(theirs, path, istate);
}
- git_check_attr(istate, path, check);
+ git_check_attr(istate, NULL, path, check);
ll_driver_name = check->items[0].value;
if (check->items[1].value) {
marker_size = atoi(check->items[1].value);
@@ -419,7 +419,7 @@
if (!check)
check = attr_check_initl("conflict-marker-size", NULL);
- git_check_attr(istate, path, check);
+ git_check_attr(istate, NULL, path, check);
if (check->items[0].value) {
marker_size = atoi(check->items[0].value);
if (marker_size <= 0)
diff --git a/ls-refs.c b/ls-refs.c
index fb67697..697d4be 100644
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -194,8 +194,9 @@
send_possibly_unborn_head(&data);
if (!data.prefixes.nr)
strvec_push(&data.prefixes, "");
- for_each_fullref_in_prefixes(get_git_namespace(), data.prefixes.v,
- send_ref, &data);
+ refs_for_each_fullref_in_prefixes(get_main_ref_store(r),
+ get_git_namespace(), data.prefixes.v,
+ send_ref, &data);
packet_fflush(stdout);
strvec_clear(&data.prefixes);
strbuf_release(&data.buf);
diff --git a/merge-recursive.c b/merge-recursive.c
index 2fd0aa9..ae469f8 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -412,7 +412,7 @@
{
int rc;
struct tree_desc t[3];
- struct index_state tmp_index = { NULL };
+ struct index_state tmp_index = INDEX_STATE_INIT(opt->repo);
memset(&opt->priv->unpack_opts, 0, sizeof(opt->priv->unpack_opts));
if (opt->priv->call_depth)
diff --git a/object-file.c b/object-file.c
index 2629055..939865c 100644
--- a/object-file.c
+++ b/object-file.c
@@ -33,6 +33,7 @@
#include "object-store.h"
#include "promisor-remote.h"
#include "submodule.h"
+#include "fsck.h"
/* The maximum size for an object header. */
#define MAX_HEADER_LEN 32
@@ -1211,35 +1212,25 @@
}
/*
- * 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;
+ void *map = NULL;
+ struct stat st;
- if (path)
- fd = git_open(path);
- else
- fd = open_loose_object(r, oid, &path);
- map = NULL;
- if (fd >= 0) {
- struct stat st;
-
- if (!fstat(fd, &st)) {
- *size = xsize_t(st.st_size);
- if (!*size) {
- /* mmap() is forbidden on empty files */
- error(_("object file %s is empty"), path);
- close(fd);
- return NULL;
- }
- map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (!fstat(fd, &st)) {
+ *size = xsize_t(st.st_size);
+ if (!*size) {
+ /* mmap() is forbidden on empty files */
+ error(_("object file %s is empty"), path);
+ close(fd);
+ return NULL;
}
- close(fd);
+ map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0);
}
+ close(fd);
return map;
}
@@ -1247,7 +1238,12 @@
const struct object_id *oid,
unsigned long *size)
{
- return map_loose_object_1(r, NULL, oid, size);
+ const char *p;
+ int fd = open_loose_object(r, oid, &p);
+
+ if (fd < 0)
+ return NULL;
+ return map_fd(fd, p, size);
}
enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,
@@ -1427,7 +1423,9 @@
struct object_info *oi, int flags)
{
int status = 0;
+ int fd;
unsigned long mapsize;
+ const char *path;
void *map;
git_zstream stream;
char hdr[MAX_HEADER_LEN];
@@ -1448,7 +1446,6 @@
* object even exists.
*/
if (!oi->typep && !oi->type_name && !oi->sizep && !oi->contentp) {
- const char *path;
struct stat st;
if (!oi->disk_sizep && (flags & OBJECT_INFO_QUICK))
return quick_has_loose(r, oid) ? 0 : -1;
@@ -1459,7 +1456,13 @@
return 0;
}
- map = map_loose_object(r, oid, &mapsize);
+ fd = open_loose_object(r, oid, &path);
+ if (fd < 0) {
+ if (errno != ENOENT)
+ error_errno(_("unable to open loose object %s"), oid_to_hex(oid));
+ return -1;
+ }
+ map = map_fd(fd, path, &mapsize);
if (!map)
return -1;
@@ -1497,6 +1500,10 @@
break;
}
+ if (status && (flags & OBJECT_INFO_DIE_IF_CORRUPT))
+ die(_("loose object %s (stored in %s) is corrupt"),
+ oid_to_hex(oid), path);
+
git_inflate_end(&stream);
cleanup:
munmap(map, mapsize);
@@ -1575,9 +1582,6 @@
if (find_pack_entry(r, real, &e))
break;
- if (flags & OBJECT_INFO_IGNORE_LOOSE)
- return -1;
-
/* Most likely it's a loose object. */
if (!loose_object_info(r, real, oi, flags))
return 0;
@@ -1609,6 +1613,15 @@
continue;
}
+ if (flags & OBJECT_INFO_DIE_IF_CORRUPT) {
+ const struct packed_git *p;
+ if ((flags & OBJECT_INFO_LOOKUP_REPLACE) && !oideq(real, oid))
+ die(_("replacement %s not found for %s"),
+ oid_to_hex(real), oid_to_hex(oid));
+ if ((p = has_packed_and_bad(r, real)))
+ die(_("packed object %s (stored in %s) is corrupt"),
+ oid_to_hex(real), p->pack_name);
+ }
return -1;
}
@@ -1659,21 +1672,6 @@
return type;
}
-static void *read_object(struct repository *r,
- const struct object_id *oid, enum object_type *type,
- unsigned long *size)
-{
- struct object_info oi = OBJECT_INFO_INIT;
- void *content;
- oi.typep = type;
- oi.sizep = size;
- oi.contentp = &content;
-
- if (oid_object_info_extended(r, oid, &oi, 0) < 0)
- return NULL;
- return content;
-}
-
int pretend_object_file(void *buf, unsigned long len, enum object_type type,
struct object_id *oid)
{
@@ -1695,46 +1693,25 @@
/*
* 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;
+ oi.typep = type;
+ oi.sizep = size;
+ oi.contentp = &data;
+ if (oid_object_info_extended(r, oid, &oi, flags))
+ return NULL;
- 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();
-
- return NULL;
+ return data;
}
void *read_object_with_reference(struct repository *r,
@@ -1864,13 +1841,6 @@
return 0;
}
-static int write_buffer(int fd, const void *buf, size_t len)
-{
- if (write_in_full(fd, buf, len) < 0)
- return error_errno(_("file write error"));
- return 0;
-}
-
static void hash_object_file_literally(const struct git_hash_algo *algo,
const void *buf, unsigned long len,
const char *type, struct object_id *oid)
@@ -2015,8 +1985,8 @@
ret = git_deflate(stream, flush ? Z_FINISH : 0);
the_hash_algo->update_fn(c, in0, stream->next_in - in0);
- if (write_buffer(fd, compressed, stream->next_out - compressed) < 0)
- die(_("unable to write loose object file"));
+ if (write_in_full(fd, compressed, stream->next_out - compressed) < 0)
+ die_errno(_("unable to write loose object file"));
stream->next_out = compressed;
stream->avail_out = compressed_len;
@@ -2269,6 +2239,7 @@
{
void *buf;
unsigned long len;
+ struct object_info oi = OBJECT_INFO_INIT;
enum object_type type;
char hdr[MAX_HEADER_LEN];
int hdrlen;
@@ -2276,8 +2247,10 @@
if (has_loose_object(oid))
return 0;
- buf = read_object(the_repository, oid, &type, &len);
- if (!buf)
+ oi.typep = &type;
+ oi.sizep = &len;
+ oi.contentp = &buf;
+ if (oid_object_info_extended(the_repository, oid, &oi, 0))
return error(_("cannot read object for %s"), oid_to_hex(oid));
hdrlen = format_object_header(hdr, sizeof(hdr), type, len);
ret = write_loose_object(oid, hdr, hdrlen, buf, len, mtime, 0);
@@ -2312,32 +2285,21 @@
return repo_has_object_file_with_flags(r, oid, 0);
}
-static void check_tree(const void *buf, size_t size)
+/*
+ * We can't use the normal fsck_error_function() for index_mem(),
+ * because we don't yet have a valid oid for it to report. Instead,
+ * report the minimal fsck error here, and rely on the caller to
+ * give more context.
+ */
+static int hash_format_check_report(struct fsck_options *opts,
+ const struct object_id *oid,
+ enum object_type object_type,
+ enum fsck_msg_type msg_type,
+ enum fsck_msg_id msg_id,
+ const char *message)
{
- struct tree_desc desc;
- struct name_entry entry;
-
- init_tree_desc(&desc, buf, size);
- while (tree_entry(&desc, &entry))
- /* do nothing
- * tree_entry() will die() on malformed entries */
- ;
-}
-
-static void check_commit(const void *buf, size_t size)
-{
- struct commit c;
- memset(&c, 0, sizeof(c));
- if (parse_commit_buffer(the_repository, &c, buf, size, 0))
- die(_("corrupt commit"));
-}
-
-static void check_tag(const void *buf, size_t size)
-{
- struct tag t;
- memset(&t, 0, sizeof(t));
- if (parse_tag_buffer(the_repository, &t, buf, size))
- die(_("corrupt tag"));
+ error(_("object fails fsck: %s"), message);
+ return 1;
}
static int index_mem(struct index_state *istate,
@@ -2364,12 +2326,13 @@
}
}
if (flags & HASH_FORMAT_CHECK) {
- if (type == OBJ_TREE)
- check_tree(buf, size);
- if (type == OBJ_COMMIT)
- check_commit(buf, size);
- if (type == OBJ_TAG)
- check_tag(buf, size);
+ struct fsck_options opts = FSCK_OPTIONS_DEFAULT;
+
+ opts.strict = 1;
+ opts.error_func = hash_format_check_report;
+ if (fsck_buffer(null_oid(), type, buf, size, &opts))
+ die(_("refusing to create malformed object"));
+ fsck_finish(&opts);
}
if (write_object)
@@ -2792,13 +2755,16 @@
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 1be57ab..1a713d8 100644
--- a/object-store.h
+++ b/object-store.h
@@ -241,17 +241,10 @@
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 @@
/*
* 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 @@
#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/object.c b/object.c
index 682b852..344087d 100644
--- a/object.c
+++ b/object.c
@@ -212,8 +212,7 @@
if (type == OBJ_BLOB) {
struct blob *blob = lookup_blob(r, oid);
if (blob) {
- if (parse_blob_buffer(blob, buffer, size))
- return NULL;
+ parse_blob_buffer(blob);
obj = &blob->object;
}
} else if (type == OBJ_TREE) {
@@ -292,7 +291,7 @@
error(_("hash mismatch %s"), oid_to_hex(oid));
return NULL;
}
- parse_blob_buffer(lookup_blob(r, oid), NULL, 0);
+ parse_blob_buffer(lookup_blob(r, oid));
return lookup_object(r, oid);
}
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 440407f..d2a42ab 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -354,8 +354,8 @@
if (bitmap_git->pack || bitmap_git->midx) {
struct strbuf buf = STRBUF_INIT;
get_midx_filename(&buf, midx->object_dir);
- /* ignore extra bitmap file; we can only handle one */
- warning(_("ignoring extra bitmap file: '%s'"), buf.buf);
+ trace2_data_string("bitmap", the_repository,
+ "ignoring extra midx bitmap file", buf.buf);
close(fd);
strbuf_release(&buf);
return -1;
@@ -411,9 +411,6 @@
struct stat st;
char *bitmap_name;
- if (open_pack_index(packfile))
- return -1;
-
bitmap_name = pack_bitmap_filename(packfile);
fd = git_open(bitmap_name);
@@ -432,8 +429,8 @@
}
if (bitmap_git->pack || bitmap_git->midx) {
- /* ignore extra bitmap file; we can only handle one */
- warning(_("ignoring extra bitmap file: '%s'"), packfile->pack_name);
+ trace2_data_string("bitmap", the_repository,
+ "ignoring extra bitmap file", packfile->pack_name);
close(fd);
return -1;
}
@@ -458,6 +455,8 @@
return -1;
}
+ trace2_data_string("bitmap", the_repository, "opened bitmap file",
+ packfile->pack_name);
return 0;
}
@@ -525,11 +524,16 @@
struct packed_git *p;
int ret = -1;
- assert(!bitmap_git->map);
-
for (p = get_all_packs(r); p; p = p->next) {
- if (open_pack_bitmap_1(bitmap_git, p) == 0)
+ if (open_pack_bitmap_1(bitmap_git, p) == 0) {
ret = 0;
+ /*
+ * The only reason to keep looking is to report
+ * duplicates.
+ */
+ if (!trace2_is_enabled())
+ break;
+ }
}
return ret;
@@ -553,11 +557,20 @@
static int open_bitmap(struct repository *r,
struct bitmap_index *bitmap_git)
{
+ int found;
+
assert(!bitmap_git->map);
- if (!open_midx_bitmap(r, bitmap_git))
- return 0;
- return open_pack_bitmap(r, bitmap_git);
+ found = !open_midx_bitmap(r, bitmap_git);
+
+ /*
+ * these will all be skipped if we opened a midx bitmap; but run it
+ * anyway if tracing is enabled to report the duplicates
+ */
+ if (!found || trace2_is_enabled())
+ found |= !open_pack_bitmap(r, bitmap_git);
+
+ return found ? 0 : -1;
}
struct bitmap_index *prepare_bitmap_git(struct repository *r)
diff --git a/pack-write.c b/pack-write.c
index 00787e3..3363729 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -5,7 +5,6 @@
#include "chunk-format.h"
#include "pack-mtimes.h"
#include "oidmap.h"
-#include "chunk-format.h"
#include "pack-objects.h"
void reset_pack_idx_option(struct pack_idx_option *opts)
diff --git a/packfile.c b/packfile.c
index c0d7dd9..79e21ab 100644
--- a/packfile.c
+++ b/packfile.c
@@ -1650,22 +1650,6 @@
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 @@
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 @@
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.c b/parse-options.c
index a1ec932..fd47432 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -702,8 +702,7 @@
if (!nr_aliases)
return NULL;
- ALLOC_ARRAY(newopt, nr + 1);
- COPY_ARRAY(newopt, options, nr + 1);
+ DUP_ARRAY(newopt, options, nr + 1);
/* each alias has two string pointers and NULL */
CALLOC_ARRAY(ctx->alias_groups, 3 * (nr_aliases + 1));
diff --git a/parse-options.h b/parse-options.h
index b6ef86e..50d852f 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -369,6 +369,10 @@
{ 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/pathspec.c b/pathspec.c
index 46e77a8..ab70fcb 100644
--- a/pathspec.c
+++ b/pathspec.c
@@ -545,7 +545,7 @@
}
/*
* We may want to substitute "this command" with a command
- * name. E.g. when add--interactive dies when running
+ * name. E.g. when "git add -p" or "git add -i" dies when running
* "checkout -p"
*/
die(_("%s: pathspec magic not supported by this command: %s"),
@@ -681,8 +681,7 @@
int i, j;
*dst = *src;
- ALLOC_ARRAY(dst->items, dst->nr);
- COPY_ARRAY(dst->items, src->items, dst->nr);
+ DUP_ARRAY(dst->items, src->items, dst->nr);
for (i = 0; i < dst->nr; i++) {
struct pathspec_item *d = &dst->items[i];
@@ -691,8 +690,7 @@
d->match = xstrdup(s->match);
d->original = xstrdup(s->original);
- ALLOC_ARRAY(d->attr_match, d->attr_match_nr);
- COPY_ARRAY(d->attr_match, s->attr_match, d->attr_match_nr);
+ DUP_ARRAY(d->attr_match, s->attr_match, d->attr_match_nr);
for (j = 0; j < d->attr_match_nr; j++) {
const char *value = s->attr_match[j].value;
d->attr_match[j].value = xstrdup_or_null(value);
@@ -732,7 +730,7 @@
if (name[namelen])
name = to_free = xmemdupz(name, namelen);
- git_check_attr(istate, name, item->attr_check);
+ git_check_attr(istate, NULL, name, item->attr_check);
free(to_free);
diff --git a/pretty.c b/pretty.c
index 6cb363a..1e1e218 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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/range-diff.c b/range-diff.c
index 8b7d81a..8255ab4 100644
--- a/range-diff.c
+++ b/range-diff.c
@@ -269,14 +269,18 @@
hashmap_clear(&map);
}
-static int diffsize_consume(void *data, char *line, unsigned long len)
+static int diffsize_consume(void *data,
+ char *line UNUSED,
+ unsigned long len UNUSED)
{
(*(int *)data)++;
return 0;
}
-static void diffsize_hunk(void *data, long ob, long on, long nb, long nn,
- const char *funcline, long funclen)
+static void diffsize_hunk(void *data,
+ long ob UNUSED, long on UNUSED,
+ long nb UNUSED, long nn UNUSED,
+ const char *func UNUSED, long funclen UNUSED)
{
diffsize_consume(data, NULL, 0);
}
@@ -461,7 +465,7 @@
diff_flush(diffopt);
}
-static struct strbuf *output_prefix_cb(struct diff_options *opt, void *data)
+static struct strbuf *output_prefix_cb(struct diff_options *opt UNUSED, void *data)
{
return data;
}
diff --git a/read-cache.c b/read-cache.c
index 46f5e49..35e5657 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -488,11 +488,11 @@
return 0;
}
-int base_name_compare(const char *name1, int len1, int mode1,
- const char *name2, int len2, int mode2)
+int base_name_compare(const char *name1, size_t len1, int mode1,
+ const char *name2, size_t len2, int mode2)
{
unsigned char c1, c2;
- int len = len1 < len2 ? len1 : len2;
+ size_t len = len1 < len2 ? len1 : len2;
int cmp;
cmp = memcmp(name1, name2, len);
@@ -517,11 +517,12 @@
* This is used by routines that want to traverse the git namespace
* but then handle conflicting entries together when possible.
*/
-int df_name_compare(const char *name1, int len1, int mode1,
- const char *name2, int len2, int mode2)
+int df_name_compare(const char *name1, size_t len1, int mode1,
+ const char *name2, size_t len2, int mode2)
{
- int len = len1 < len2 ? len1 : len2, cmp;
unsigned char c1, c2;
+ size_t len = len1 < len2 ? len1 : len2;
+ int cmp;
cmp = memcmp(name1, name2, len);
if (cmp)
@@ -1817,6 +1818,8 @@
git_hash_ctx c;
unsigned char hash[GIT_MAX_RAWSZ];
int hdr_version;
+ unsigned char *start, *end;
+ struct object_id oid;
if (hdr->hdr_signature != htonl(CACHE_SIGNATURE))
return error(_("bad signature 0x%08x"), hdr->hdr_signature);
@@ -1827,10 +1830,16 @@
if (!verify_index_checksum)
return 0;
+ end = (unsigned char *)hdr + size;
+ start = end - the_hash_algo->rawsz;
+ oidread(&oid, start);
+ if (oideq(&oid, null_oid()))
+ return 0;
+
the_hash_algo->init_fn(&c);
the_hash_algo->update_fn(&c, hdr, size - the_hash_algo->rawsz);
the_hash_algo->final_fn(hash, &c);
- if (!hasheq(hash, (unsigned char *)hdr + size - the_hash_algo->rawsz))
+ if (!hasheq(hash, start))
return error(_("bad index file sha1 signature"));
return 0;
}
@@ -2292,12 +2301,10 @@
* If the index's repo exists, mark it sparse according to
* repo settings.
*/
- if (istate->repo) {
- prepare_repo_settings(istate->repo);
- if (!istate->repo->settings.command_requires_full_index &&
- is_sparse_index_allowed(istate, 0))
- istate->sparse_index = 1;
- }
+ prepare_repo_settings(istate->repo);
+ if (!istate->repo->settings.command_requires_full_index &&
+ is_sparse_index_allowed(istate, 0))
+ istate->sparse_index = 1;
}
/* remember to discard_cache() before reading a different cache! */
@@ -2322,8 +2329,6 @@
fd = open(path, O_RDONLY);
if (fd < 0) {
if (!must_exist && errno == ENOENT) {
- if (!istate->repo)
- istate->repo = the_repository;
set_new_index_sparsity(istate);
return 0;
}
@@ -2425,9 +2430,6 @@
trace2_data_intmax("index", the_repository, "read/cache_nr",
istate->cache_nr);
- if (!istate->repo)
- istate->repo = the_repository;
-
/*
* If the command explicitly requires a full index, force it
* to be full. Otherwise, correct the sparsity based on repository
@@ -2490,9 +2492,10 @@
trace_performance_enter();
if (split_index->base)
- discard_index(split_index->base);
+ release_index(split_index->base);
else
- CALLOC_ARRAY(split_index->base, 1);
+ ALLOC_ARRAY(split_index->base, 1);
+ index_state_init(split_index->base, istate->repo);
base_oid_hex = oid_to_hex(&split_index->base_oid);
base_path = xstrfmt("%s/sharedindex.%s", gitdir, base_oid_hex);
@@ -2531,7 +2534,13 @@
return (!istate->cache_nr && !istate->timestamp.sec);
}
-void discard_index(struct index_state *istate)
+void index_state_init(struct index_state *istate, struct repository *r)
+{
+ struct index_state blank = INDEX_STATE_INIT(r);
+ memcpy(istate, &blank, sizeof(*istate));
+}
+
+void release_index(struct index_state *istate)
{
/*
* Cache entries in istate->cache[] should have been allocated
@@ -2543,20 +2552,17 @@
validate_cache_entries(istate);
resolve_undo_clear_index(istate);
- istate->cache_nr = 0;
- istate->cache_changed = 0;
- istate->timestamp.sec = 0;
- istate->timestamp.nsec = 0;
free_name_hash(istate);
cache_tree_free(&(istate->cache_tree));
- istate->initialized = 0;
- istate->fsmonitor_has_run_once = 0;
- FREE_AND_NULL(istate->fsmonitor_last_update);
- FREE_AND_NULL(istate->cache);
- istate->cache_alloc = 0;
+ free(istate->fsmonitor_last_update);
+ free(istate->cache);
discard_split_index(istate);
free_untracked_cache(istate->untracked);
- istate->untracked = NULL;
+
+ if (istate->sparse_checkout_patterns) {
+ clear_pattern_list(istate->sparse_checkout_patterns);
+ FREE_AND_NULL(istate->sparse_checkout_patterns);
+ }
if (istate->ce_mem_pool) {
mem_pool_discard(istate->ce_mem_pool, should_validate_cache_entries());
@@ -2564,6 +2570,12 @@
}
}
+void discard_index(struct index_state *istate)
+{
+ release_index(istate);
+ index_state_init(istate, istate->repo);
+}
+
/*
* Validate the cache entries of this index.
* All cache entries associated with this index
@@ -2915,9 +2927,13 @@
int ieot_entries = 1;
struct index_entry_offset_table *ieot = NULL;
int nr, nr_threads;
+ struct repository *r = istate->repo;
f = hashfd(tempfile->fd, tempfile->filename.buf);
+ prepare_repo_settings(r);
+ f->skip_hash = r->settings.index_skip_hash;
+
for (i = removed = extended = 0; i < entries; i++) {
if (cache[i]->ce_flags & CE_REMOVE)
removed++;
diff --git a/ref-filter.c b/ref-filter.c
index 9dc2cd1..f8203c6 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -228,6 +228,22 @@
return ret;
}
+static int err_no_arg(struct strbuf *sb, const char *name)
+{
+ size_t namelen = strchrnul(name, ':') - name;
+ strbuf_addf(sb, _("%%(%.*s) does not take arguments"),
+ (int)namelen, name);
+ return -1;
+}
+
+static int err_bad_arg(struct strbuf *sb, const char *name, const char *arg)
+{
+ size_t namelen = strchrnul(name, ':') - name;
+ strbuf_addf(sb, _("unrecognized %%(%.*s) argument: %s"),
+ (int)namelen, name, arg);
+ return -1;
+}
+
static int color_atom_parser(struct ref_format *format, struct used_atom *atom,
const char *color_value, struct strbuf *err)
{
@@ -262,7 +278,7 @@
if (strtol_i(arg, 10, &atom->rstrip))
return strbuf_addf_ret(err, -1, _("Integer value expected refname:rstrip=%s"), arg);
} else
- return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), name, arg);
+ return err_bad_arg(err, name, arg);
return 0;
}
@@ -317,7 +333,7 @@
const char *arg, struct strbuf *err)
{
if (arg)
- return strbuf_addf_ret(err, -1, _("%%(objecttype) does not take arguments"));
+ return err_no_arg(err, "objecttype");
if (*atom->name == '*')
oi_deref.info.typep = &oi_deref.type;
else
@@ -341,7 +357,7 @@
else
oi.info.disk_sizep = &oi.disk_size;
} else
- return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), "objectsize", arg);
+ return err_bad_arg(err, "objectsize", arg);
return 0;
}
@@ -349,7 +365,7 @@
const char *arg, struct strbuf *err)
{
if (arg)
- return strbuf_addf_ret(err, -1, _("%%(deltabase) does not take arguments"));
+ return err_no_arg(err, "deltabase");
if (*atom->name == '*')
oi_deref.info.delta_base_oid = &oi_deref.delta_base_oid;
else
@@ -361,7 +377,7 @@
const char *arg, struct strbuf *err)
{
if (arg)
- return strbuf_addf_ret(err, -1, _("%%(body) does not take arguments"));
+ return err_no_arg(err, "body");
atom->u.contents.option = C_BODY_DEP;
return 0;
}
@@ -374,7 +390,7 @@
else if (!strcmp(arg, "sanitize"))
atom->u.contents.option = C_SUB_SANITIZE;
else
- return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), "subject", arg);
+ return err_bad_arg(err, "subject", arg);
return 0;
}
@@ -428,7 +444,7 @@
if (strtoul_ui(arg, 10, &atom->u.contents.nlines))
return strbuf_addf_ret(err, -1, _("positive value expected contents:lines=%s"), arg);
} else
- return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), "contents", arg);
+ return err_bad_arg(err, "contents", arg);
return 0;
}
@@ -440,7 +456,7 @@
else if (!strcmp(arg, "size"))
atom->u.raw_data.option = RAW_LENGTH;
else
- return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), "raw", arg);
+ return err_bad_arg(err, "raw", arg);
return 0;
}
@@ -459,7 +475,7 @@
if (atom->u.oid.length < MINIMUM_ABBREV)
atom->u.oid.length = MINIMUM_ABBREV;
} else
- return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), atom->name, arg);
+ return err_bad_arg(err, atom->name, arg);
return 0;
}
@@ -473,7 +489,7 @@
else if (!strcmp(arg, "localpart"))
atom->u.email_option.option = EO_LOCALPART;
else
- return strbuf_addf_ret(err, -1, _("unrecognized email option: %s"), arg);
+ return err_bad_arg(err, atom->name, arg);
return 0;
}
@@ -557,7 +573,7 @@
} else if (skip_prefix(arg, "notequals=", &atom->u.if_then_else.str)) {
atom->u.if_then_else.cmp_status = COMPARE_UNEQUAL;
} else
- return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), "if", arg);
+ return err_bad_arg(err, "if", arg);
return 0;
}
@@ -565,14 +581,16 @@
const char *arg, struct strbuf *err)
{
if (arg)
- return strbuf_addf_ret(err, -1, _("%%(rest) does not take arguments"));
+ return err_no_arg(err, "rest");
format->use_rest = 1;
return 0;
}
static int head_atom_parser(struct ref_format *format, struct used_atom *atom,
- const char *arg, struct strbuf *unused_err)
+ const char *arg, struct strbuf *err)
{
+ if (arg)
+ return err_no_arg(err, "HEAD");
atom->u.head = resolve_refdup("HEAD", RESOLVE_REF_READING, NULL, NULL);
return 0;
}
@@ -1191,7 +1209,7 @@
{
const char *cp;
for (cp = buf; *cp && *cp != '\n'; cp++) {
- if (!strncmp(cp, " <", 2))
+ if (starts_with(cp, " <"))
return xmemdupz(buf, cp - buf);
}
return xstrdup("");
@@ -1358,6 +1376,7 @@
/* parse signature first; we might not even have a subject line */
parse_signature(buf, end - buf, &payload, &signature);
+ strbuf_release(&payload);
/* skip past header until we hit empty line */
while (*buf && *buf != '\n') {
@@ -2128,8 +2147,9 @@
return for_each_fullref_in("", cb, cb_data);
}
- return for_each_fullref_in_prefixes(NULL, filter->name_patterns,
- cb, cb_data);
+ return refs_for_each_fullref_in_prefixes(get_main_ref_store(the_repository),
+ NULL, filter->name_patterns,
+ cb, cb_data);
}
/*
diff --git a/reflog.c b/reflog.c
index 78e9350..04630f5 100644
--- a/reflog.c
+++ b/reflog.c
@@ -193,7 +193,6 @@
commit_list_insert(commit, &leftover);
continue;
}
- commit->object.flags |= REACHABLE;
parent = commit->parents;
while (parent) {
commit = parent->item;
@@ -371,6 +370,9 @@
clear_commit_marks(cb->tip_commit, REACHABLE);
break;
}
+ for (elem = cb->mark_list; elem; elem = elem->next)
+ clear_commit_marks(elem->item, REACHABLE);
+ free_commit_list(cb->mark_list);
}
int count_reflog_ent(struct object_id *ooid UNUSED,
diff --git a/refs.c b/refs.c
index 2c7e88b..e31dbcd 100644
--- a/refs.c
+++ b/refs.c
@@ -1723,9 +1723,10 @@
strbuf_release(&prefix);
}
-int for_each_fullref_in_prefixes(const char *namespace,
- const char **patterns,
- each_ref_fn fn, void *cb_data)
+int refs_for_each_fullref_in_prefixes(struct ref_store *ref_store,
+ const char *namespace,
+ const char **patterns,
+ each_ref_fn fn, void *cb_data)
{
struct string_list prefixes = STRING_LIST_INIT_DUP;
struct string_list_item *prefix;
@@ -1740,7 +1741,7 @@
for_each_string_list_item(prefix, &prefixes) {
strbuf_addstr(&buf, prefix->string);
- ret = for_each_fullref_in(buf.buf, fn, cb_data);
+ ret = refs_for_each_fullref_in(ref_store, buf.buf, fn, cb_data);
if (ret)
break;
strbuf_setlen(&buf, namespace_len);
diff --git a/refs.h b/refs.h
index 3266fd8..935cdd1 100644
--- a/refs.h
+++ b/refs.h
@@ -354,8 +354,10 @@
*
* callers should be prepared to ignore references that they did not ask for.
*/
-int for_each_fullref_in_prefixes(const char *namespace, const char **patterns,
- each_ref_fn fn, void *cb_data);
+int refs_for_each_fullref_in_prefixes(struct ref_store *refs,
+ const char *namespace, const char **patterns,
+ each_ref_fn fn, void *cb_data);
+
/**
* iterate refs from the respective area.
*/
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index c1c71d1..6f5a070 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1263,7 +1263,8 @@
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 72dfb8f..a76b640 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -717,25 +717,23 @@
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 @@
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 1c4621b..1ebbe42 100644
--- a/remote.h
+++ b/remote.h
@@ -234,6 +234,11 @@
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 3021921..3dbd3f0 100644
--- a/repo-settings.c
+++ b/repo-settings.c
@@ -47,6 +47,7 @@
}
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 @@
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.c b/repository.c
index 3427085..937fa97 100644
--- a/repository.c
+++ b/repository.c
@@ -28,6 +28,8 @@
the_repo.remote_state = remote_state_new();
the_repo.parsed_objects = parsed_object_pool_new();
+ index_state_init(&the_index, the_repository);
+
repo_set_hash_algo(&the_repo, GIT_HASH_SHA1);
}
@@ -302,14 +304,13 @@
{
int res;
- if (!repo->index)
- CALLOC_ARRAY(repo->index, 1);
-
/* Complete the double-reference */
- if (!repo->index->repo)
- repo->index->repo = repo;
- else if (repo->index->repo != repo)
+ if (!repo->index) {
+ ALLOC_ARRAY(repo->index, 1);
+ index_state_init(repo->index, repo);
+ } else if (repo->index->repo != repo) {
BUG("repo's index should point back at itself");
+ }
res = read_index_from(repo->index, repo->index_file, repo->gitdir);
diff --git a/repository.h b/repository.h
index 6c461c5..e8c67ff 100644
--- a/repository.h
+++ b/repository.h
@@ -42,6 +42,7 @@
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/revision.c b/revision.c
index 439e34a..21f5f57 100644
--- a/revision.c
+++ b/revision.c
@@ -600,10 +600,12 @@
static int tree_difference = REV_TREE_SAME;
static void file_add_remove(struct diff_options *options,
- int addremove, unsigned mode,
- const struct object_id *oid,
- int oid_valid,
- const char *fullpath, unsigned dirty_submodule)
+ int addremove,
+ unsigned mode UNUSED,
+ const struct object_id *oid UNUSED,
+ int oid_valid UNUSED,
+ const char *fullpath UNUSED,
+ unsigned dirty_submodule UNUSED)
{
int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;
struct rev_info *revs = options->change_fn_data;
@@ -614,12 +616,15 @@
}
static void file_change(struct diff_options *options,
- unsigned old_mode, unsigned new_mode,
- const struct object_id *old_oid,
- const struct object_id *new_oid,
- int old_oid_valid, int new_oid_valid,
- const char *fullpath,
- unsigned old_dirty_submodule, unsigned new_dirty_submodule)
+ unsigned old_mode UNUSED,
+ unsigned new_mode UNUSED,
+ const struct object_id *old_oid UNUSED,
+ const struct object_id *new_oid UNUSED,
+ int old_oid_valid UNUSED,
+ int new_oid_valid UNUSED,
+ const char *fullpath UNUSED,
+ unsigned old_dirty_submodule UNUSED,
+ unsigned new_dirty_submodule UNUSED)
{
tree_difference = REV_TREE_DIFFERENT;
options->flags.has_changes = 1;
@@ -1808,7 +1813,7 @@
worktrees = get_worktrees();
for (p = worktrees; *p; p++) {
struct worktree *wt = *p;
- struct index_state istate = { NULL };
+ struct index_state istate = INDEX_STATE_INIT(revs->repo);
if (wt->is_current)
continue; /* current index already taken care of */
@@ -3054,6 +3059,7 @@
date_mode_release(&revs->date_mode);
release_revisions_mailmap(revs->mailmap);
free_grep_patterns(&revs->grep_filter);
+ graph_clear(revs->graph);
/* TODO (need to handle "no_free"): diff_free(&revs->diffopt) */
diff_free(&revs->pruning);
reflog_walk_info_release(revs->reflog_info);
diff --git a/run-command.c b/run-command.c
index 48b9ba6..6bd16ac 100644
--- a/run-command.c
+++ b/run-command.c
@@ -1019,7 +1019,7 @@
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGPIPE);
- if (pthread_sigmask(SIG_BLOCK, &mask, NULL) < 0) {
+ if (pthread_sigmask(SIG_BLOCK, &mask, NULL)) {
ret = error("unable to block SIGPIPE in async thread");
return (void *)ret;
}
@@ -1586,6 +1586,14 @@
if (i == opts->processes)
BUG("bookkeeping is hard");
+ /*
+ * By default, do not inherit stdin from the parent process - otherwise,
+ * all children would share stdin! Users may overwrite this to provide
+ * something to the child's stdin by having their 'get_next_task'
+ * callback assign 0 to .no_stdin and an appropriate integer to .in.
+ */
+ pp->children[i].process.no_stdin = 1;
+
code = opts->get_next_task(&pp->children[i].process,
opts->ungroup ? NULL : &pp->children[i].err,
opts->data,
@@ -1601,7 +1609,6 @@
pp->children[i].process.err = -1;
pp->children[i].process.stdout_to_stderr = 1;
}
- pp->children[i].process.no_stdin = 1;
if (start_command(&pp->children[i].process)) {
if (opts->start_failure)
@@ -1632,9 +1639,7 @@
const struct run_process_parallel_opts *opts,
int output_timeout)
{
- int i;
-
- while ((i = poll(pp->pfd, opts->processes, output_timeout) < 0)) {
+ while (poll(pp->pfd, opts->processes, output_timeout) < 0) {
if (errno == EINTR)
continue;
pp_cleanup(pp, opts);
@@ -1853,7 +1858,7 @@
*
* 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 6c52243..ca19b95 100644
--- a/scalar.c
+++ b/scalar.c
@@ -143,6 +143,7 @@
{ "credential.validate", "false", 1 }, /* GCM4W-only */
{ "gc.auto", "0", 1 },
{ "gui.GCWarning", "false", 1 },
+ { "index.skipHash", "false", 1 },
{ "index.threads", "true", 1 },
{ "index.version", "4", 1 },
{ "merge.stat", "false", 1 },
@@ -261,7 +262,7 @@
return error(_("could not set recommended config"));
if (toggle_maintenance(1))
- return error(_("could not turn on maintenance"));
+ warning(_("could not turn on maintenance"));
if (have_fsmonitor_support() && start_fsmonitor_daemon()) {
return error(_("could not start the FSMonitor daemon"));
@@ -404,7 +405,7 @@
static int cmd_clone(int argc, const char **argv)
{
const char *branch = NULL;
- int full_clone = 0, single_branch = 0;
+ int full_clone = 0, single_branch = 0, show_progress = isatty(2);
struct option clone_options[] = {
OPT_STRING('b', "branch", &branch, N_("<branch>"),
N_("branch to checkout after clone")),
@@ -499,7 +500,9 @@
if (set_recommended_config(0))
return error(_("could not configure '%s'"), dir);
- if ((res = run_git("fetch", "--quiet", "origin", NULL))) {
+ if ((res = run_git("fetch", "--quiet",
+ show_progress ? "--progress" : "--no-progress",
+ "origin", NULL))) {
warning(_("partial clone failed; attempting full clone"));
if (set_config("remote.origin.promisor") ||
@@ -508,7 +511,9 @@
goto cleanup;
}
- if ((res = run_git("fetch", "--quiet", "origin", NULL)))
+ if ((res = run_git("fetch", "--quiet",
+ show_progress ? "--progress" : "--no-progress",
+ "origin", NULL)))
goto cleanup;
}
diff --git a/sequencer.c b/sequencer.c
index 9827e1a..65a34f9 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -36,7 +36,6 @@
#include "rebase-interactive.h"
#include "reset.h"
#include "branch.h"
-#include "log-tree.h"
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
@@ -352,10 +351,25 @@
return buf.buf;
}
+void replay_opts_release(struct replay_opts *opts)
+{
+ free(opts->gpg_sign);
+ free(opts->reflog_action);
+ free(opts->default_strategy);
+ free(opts->strategy);
+ for (size_t i = 0; i < opts->xopts_nr; i++)
+ free(opts->xopts[i]);
+ free(opts->xopts);
+ strbuf_release(&opts->current_fixups);
+ if (opts->revs)
+ release_revisions(opts->revs);
+ free(opts->revs);
+}
+
int sequencer_remove_state(struct replay_opts *opts)
{
struct strbuf buf = STRBUF_INIT;
- int i, ret = 0;
+ int ret = 0;
if (is_rebase_i(opts) &&
strbuf_read_file(&buf, rebase_path_refs_to_delete(), 0) > 0) {
@@ -374,15 +388,6 @@
}
}
- free(opts->gpg_sign);
- free(opts->reflog_action);
- free(opts->default_strategy);
- free(opts->strategy);
- for (i = 0; i < opts->xopts_nr; i++)
- free(opts->xopts[i]);
- free(opts->xopts);
- strbuf_release(&opts->current_fixups);
-
strbuf_reset(&buf);
strbuf_addstr(&buf, get_dir(opts));
if (remove_dir_recursively(&buf, 0))
@@ -2272,8 +2277,10 @@
reword = 1;
else if (is_fixup(command)) {
if (update_squash_messages(r, command, commit,
- opts, item->flags))
- return -1;
+ opts, item->flags)) {
+ res = -1;
+ goto leave;
+ }
flags |= AMEND_MSG;
if (!final_fixup)
msg_file = rebase_path_squash_msg();
@@ -2283,9 +2290,11 @@
} else {
const char *dest = git_path_squash_msg(r);
unlink(dest);
- if (copy_file(dest, rebase_path_squash_msg(), 0666))
- return error(_("could not rename '%s' to '%s'"),
- rebase_path_squash_msg(), dest);
+ if (copy_file(dest, rebase_path_squash_msg(), 0666)) {
+ res = error(_("could not rename '%s' to '%s'"),
+ rebase_path_squash_msg(), dest);
+ goto leave;
+ }
unlink(git_path_merge_msg(r));
msg_file = dest;
flags |= EDIT_MSG;
@@ -2323,7 +2332,6 @@
free_commit_list(common);
free_commit_list(remotes);
}
- strbuf_release(&msgbuf);
/*
* If the merge was clean or if it failed due to conflict, we write
@@ -2397,6 +2405,7 @@
leave:
free_message(commit, &msg);
free(author);
+ strbuf_release(&msgbuf);
update_abort_safety_file();
return res;
@@ -2897,6 +2906,7 @@
strbuf_reset(buf);
if (!read_oneliner(buf, rebase_path_strategy(), 0))
return;
+ free(opts->strategy);
opts->strategy = strbuf_detach(buf, NULL);
if (!read_oneliner(buf, rebase_path_strategy_opts(), 0))
return;
@@ -4834,8 +4844,7 @@
if (!stat(rebase_path_rewritten_list(), &st) &&
st.st_size > 0) {
struct child_process child = CHILD_PROCESS_INIT;
- const char *post_rewrite_hook =
- find_hook("post-rewrite");
+ struct run_hooks_opt hook_opt = RUN_HOOKS_OPT_INIT;
child.in = open(rebase_path_rewritten_list(), O_RDONLY);
child.git_cmd = 1;
@@ -4845,18 +4854,9 @@
/* we don't care if this copying failed */
run_command(&child);
- if (post_rewrite_hook) {
- struct child_process hook = CHILD_PROCESS_INIT;
-
- hook.in = open(rebase_path_rewritten_list(),
- O_RDONLY);
- hook.stdout_to_stderr = 1;
- hook.trace2_hook_name = "post-rewrite";
- strvec_push(&hook.args, post_rewrite_hook);
- strvec_push(&hook.args, "rebase");
- /* we don't care if this hook failed */
- run_command(&hook);
- }
+ hook_opt.path_to_stdin = rebase_path_rewritten_list();
+ strvec_push(&hook_opt.args, "rebase");
+ run_hooks_opt("post-rewrite", &hook_opt);
}
apply_autostash(rebase_path_autostash());
@@ -5745,8 +5745,8 @@
base_items[i].command = TODO_EXEC;
base_items[i].offset_in_buf = base_offset;
- base_items[i].arg_offset = base_offset + strlen("exec ");
- base_items[i].arg_len = command_len - strlen("exec ");
+ base_items[i].arg_offset = base_offset;
+ base_items[i].arg_len = command_len;
base_offset += command_len + 1;
}
diff --git a/sequencer.h b/sequencer.h
index 888c18a..3bcdfa1 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -158,6 +158,7 @@
int sequencer_continue(struct repository *repo, struct replay_opts *opts);
int sequencer_rollback(struct repository *repo, struct replay_opts *opts);
int sequencer_skip(struct repository *repo, struct replay_opts *opts);
+void replay_opts_release(struct replay_opts *opts);
int sequencer_remove_state(struct replay_opts *opts);
#define TODO_LIST_KEEP_EMPTY (1U << 0)
diff --git a/serve.c b/serve.c
index 733347f..cbf4a14 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 @@
.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/sparse-index.c b/sparse-index.c
index 8c269da..147a133 100644
--- a/sparse-index.c
+++ b/sparse-index.c
@@ -128,9 +128,6 @@
if (!core_apply_sparse_checkout || !core_sparse_checkout_cone)
return 0;
- if (!istate->repo)
- istate->repo = the_repository;
-
if (!(flags & SPARSE_INDEX_MEMORY_ONLY)) {
int test_env;
@@ -299,7 +296,7 @@
* If the index is already full, then keep it full. We will convert
* it to a sparse index on write, if possible.
*/
- if (!istate || istate->sparse_index == INDEX_EXPANDED)
+ if (istate->sparse_index == INDEX_EXPANDED)
return;
/*
@@ -327,9 +324,6 @@
pl = NULL;
}
- if (!istate->repo)
- istate->repo = the_repository;
-
/*
* A NULL pattern set indicates we are expanding a full index, so
* we use a special region name that indicates the full expansion.
@@ -424,6 +418,8 @@
void ensure_full_index(struct index_state *istate)
{
+ if (!istate)
+ BUG("ensure_full_index() must get an index!");
expand_index(istate, NULL);
}
@@ -547,12 +543,9 @@
if (in_expand_to_path)
return;
- if (!istate || !istate->sparse_index)
+ if (!istate->sparse_index)
return;
- if (!istate->repo)
- istate->repo = the_repository;
-
in_expand_to_path = 1;
/*
diff --git a/split-index.c b/split-index.c
index 9d0ccc3..5d0f047 100644
--- a/split-index.c
+++ b/split-index.c
@@ -90,7 +90,8 @@
mem_pool_combine(istate->ce_mem_pool, istate->split_index->base->ce_mem_pool);
}
- CALLOC_ARRAY(si->base, 1);
+ ALLOC_ARRAY(si->base, 1);
+ index_state_init(si->base, istate->repo);
si->base->version = istate->version;
/* zero timestamp disables racy test in ce_write_index() */
si->base->timestamp = istate->timestamp;
diff --git a/strbuf.c b/strbuf.c
index 0890b14..c383f41 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -1200,3 +1200,9 @@
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 76965a1..f6dbb96 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -664,6 +664,17 @@
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 7b2f8b2..27841dc 100644
--- a/streaming.c
+++ b/streaming.c
@@ -38,7 +38,7 @@
union {
struct {
- char *buf; /* from read_object() */
+ char *buf; /* from oid_object_info_extended() */
unsigned long read_ptr;
} incore;
@@ -388,12 +388,17 @@
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 8ac2fca..3a0dfc4 100644
--- a/submodule.c
+++ b/submodule.c
@@ -832,7 +832,7 @@
}
static void collect_changed_submodules_cb(struct diff_queue_struct *q,
- struct diff_options *options,
+ struct diff_options *options UNUSED,
void *data)
{
struct collect_changed_submodules_cb_data *me = data;
@@ -2054,14 +2054,6 @@
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 @@
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 @@
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 @@
* 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 @@
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 @@
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 @@
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 @@
* 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 @@
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 @@
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 @@
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 @@
* 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 @@
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 b52a4ff..c55a25c 100644
--- a/submodule.h
+++ b/submodule.h
@@ -150,9 +150,8 @@
#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 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/README b/t/README
index 979b2d4..29576c3 100644
--- a/t/README
+++ b/t/README
@@ -449,10 +449,6 @@
GIT_TEST_PRELOAD_INDEX=<boolean> exercises the preload-index code path
by overriding the minimum number of cache entries required per thread.
-GIT_TEST_ADD_I_USE_BUILTIN=<boolean>, when false, disables the
-built-in version of git add -i. See 'add.interactive.useBuiltin' in
-git-config(1).
-
GIT_TEST_INDEX_THREADS=<n> enables exercising the multi-threaded loading
of the index for the whole test suite by bypassing the default number of
cache entries and thread minimums. Setting this to 1 will make the
diff --git a/t/helper/test-bundle-uri.c b/t/helper/test-bundle-uri.c
index 25afd39..b18e760 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 @@
init_bundle_list(&list);
+ list.baseURI = xstrdup("<uri>");
+
switch (mode) {
case KEY_VALUE_PAIRS:
if (argc != 1)
@@ -68,6 +74,39 @@
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 @@
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/helper/test-ctype.c b/t/helper/test-ctype.c
index 92c4c23..b21bd67 100644
--- a/t/helper/test-ctype.c
+++ b/t/helper/test-ctype.c
@@ -11,9 +11,14 @@
static int is_in(const char *s, int ch)
{
- /* We can't find NUL using strchr. It's classless anyway. */
+ /*
+ * We can't find NUL using strchr. Accept it as the first
+ * character in the spec -- there are no empty classes.
+ */
if (ch == '\0')
- return 0;
+ return ch == *s;
+ if (*s == '\0')
+ s++;
return !!strchr(s, ch);
}
@@ -28,6 +33,20 @@
#define DIGIT "0123456789"
#define LOWER "abcdefghijklmnopqrstuvwxyz"
#define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define PUNCT "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
+#define ASCII \
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
+ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
+ "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" \
+ "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" \
+ "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" \
+ "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" \
+ "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" \
+ "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
+#define CNTRL \
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
+ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
+ "\x7f"
int cmd__ctype(int argc, const char **argv)
{
@@ -38,6 +57,13 @@
TEST_CLASS(is_glob_special, "*?[\\");
TEST_CLASS(is_regex_special, "$()*+.?[\\^{|");
TEST_CLASS(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~");
+ TEST_CLASS(isascii, ASCII);
+ TEST_CLASS(islower, LOWER);
+ TEST_CLASS(isupper, UPPER);
+ TEST_CLASS(iscntrl, CNTRL);
+ TEST_CLASS(ispunct, PUNCT);
+ TEST_CLASS(isxdigit, DIGIT "abcdefABCDEF");
+ TEST_CLASS(isprint, LOWER UPPER DIGIT PUNCT " ");
return rc;
}
diff --git a/builtin/env--helper.c b/t/helper/test-env-helper.c
similarity index 71%
rename from builtin/env--helper.c
rename to t/helper/test-env-helper.c
index ea04c16..66c88b8 100644
--- a/builtin/env--helper.c
+++ b/t/helper/test-env-helper.c
@@ -1,9 +1,9 @@
-#include "builtin.h"
+#include "test-tool.h"
#include "config.h"
#include "parse-options.h"
static char const * const env__helper_usage[] = {
- N_("git env--helper --type=[bool|ulong] <options> <env-var>"),
+ "test-tool env-helper --type=[bool|ulong] <options> <env-var>",
NULL
};
@@ -24,12 +24,12 @@
else if (!strcmp(arg, "ulong"))
*cmdmode = ENV_HELPER_TYPE_ULONG;
else
- die(_("unrecognized --type argument, %s"), arg);
+ die("unrecognized --type argument, %s", arg);
return 0;
}
-int cmd_env__helper(int argc, const char **argv, const char *prefix)
+int cmd__env_helper(int argc, const char **argv)
{
int exit_code = 0;
const char *env_variable = NULL;
@@ -39,17 +39,17 @@
unsigned long ret_ulong, default_ulong;
enum cmdmode cmdmode = 0;
struct option opts[] = {
- OPT_CALLBACK_F(0, "type", &cmdmode, N_("type"),
- N_("value is given this type"), PARSE_OPT_NONEG,
+ OPT_CALLBACK_F(0, "type", &cmdmode, "type",
+ "value is given this type", PARSE_OPT_NONEG,
option_parse_type),
- OPT_STRING(0, "default", &env_default, N_("value"),
- N_("default for git_env_*(...) to fall back on")),
+ OPT_STRING(0, "default", &env_default, "value",
+ "default for git_env_*(...) to fall back on"),
OPT_BOOL(0, "exit-code", &exit_code,
- N_("be quiet only use git_env_*() value as exit code")),
+ "be quiet only use git_env_*() value as exit code"),
OPT_END(),
};
- argc = parse_options(argc, argv, prefix, opts, env__helper_usage,
+ argc = parse_options(argc, argv, NULL, opts, env__helper_usage,
PARSE_OPT_KEEP_UNKNOWN_OPT);
if (env_default && !*env_default)
usage_with_options(env__helper_usage, opts);
@@ -64,7 +64,7 @@
if (env_default) {
default_int = git_parse_maybe_bool(env_default);
if (default_int == -1) {
- error(_("option `--default' expects a boolean value with `--type=bool`, not `%s`"),
+ error("option `--default' expects a boolean value with `--type=bool`, not `%s`",
env_default);
usage_with_options(env__helper_usage, opts);
}
@@ -79,7 +79,7 @@
case ENV_HELPER_TYPE_ULONG:
if (env_default) {
if (!git_parse_ulong(env_default, &default_ulong)) {
- error(_("option `--default' expects an unsigned long value with `--type=ulong`, not `%s`"),
+ error("option `--default' expects an unsigned long value with `--type=ulong`, not `%s`",
env_default);
usage_with_options(env__helper_usage, opts);
}
diff --git a/t/helper/test-fake-ssh.c b/t/helper/test-fake-ssh.c
index 2e576bc..27323cb 100644
--- a/t/helper/test-fake-ssh.c
+++ b/t/helper/test-fake-ssh.c
@@ -17,6 +17,7 @@
f = fopen(buf.buf, "w");
if (!f)
die("Could not write to %s", buf.buf);
+ strbuf_release(&buf);
for (i = 0; i < argc; i++)
fprintf(f, "%s%s", i > 0 ? " " : "", i > 0 ? argv[i] : "ssh:");
fprintf(f, "\n");
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index 7eb1a26..abe8a78 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -28,6 +28,7 @@
{ "dump-fsmonitor", cmd__dump_fsmonitor },
{ "dump-split-index", cmd__dump_split_index },
{ "dump-untracked-cache", cmd__dump_untracked_cache },
+ { "env-helper", cmd__env_helper },
{ "example-decorate", cmd__example_decorate },
{ "fast-rebase", cmd__fast_rebase },
{ "fsmonitor-client", cmd__fsmonitor_client },
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index 2e20a16..ea26724 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -22,6 +22,7 @@
int cmd__dump_split_index(int argc, const char **argv);
int cmd__dump_untracked_cache(int argc, const char **argv);
int cmd__dump_reftable(int argc, const char **argv);
+int cmd__env_helper(int argc, const char **argv);
int cmd__example_decorate(int argc, const char **argv);
int cmd__fast_rebase(int argc, const char **argv);
int cmd__fsmonitor_client(int argc, const char **argv);
diff --git a/t/interop/interop-lib.sh b/t/interop/interop-lib.sh
index 3e0a291..62f4481 100644
--- a/t/interop/interop-lib.sh
+++ b/t/interop/interop-lib.sh
@@ -68,7 +68,7 @@
wrap_git .bin/git.a "$DIR_A" &&
wrap_git .bin/git.b "$DIR_B" &&
write_script .bin/git <<-\EOF &&
- echo >&2 fatal: test tried to run generic git
+ echo >&2 fatal: test tried to run generic git: $*
exit 1
EOF
PATH=$(pwd)/.bin:$PATH
diff --git a/t/lib-bundle-uri-protocol.sh b/t/lib-bundle-uri-protocol.sh
new file mode 100644
index 0000000..a4a1af8
--- /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/lib-httpd.sh b/t/lib-httpd.sh
index 608949e..5d2d56c 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -99,16 +99,19 @@
fi
HTTPD_VERSION=$($LIB_HTTPD_PATH -v | \
- sed -n 's/^Server version: Apache\/\([0-9]*\)\..*$/\1/p; q')
+ sed -n 's/^Server version: Apache\/\([0-9.]*\).*$/\1/p; q')
+HTTPD_VERSION_MAJOR=$(echo $HTTPD_VERSION | cut -d. -f1)
+HTTPD_VERSION_MINOR=$(echo $HTTPD_VERSION | cut -d. -f2)
-if test -n "$HTTPD_VERSION"
+if test -n "$HTTPD_VERSION_MAJOR"
then
if test -z "$LIB_HTTPD_MODULE_PATH"
then
- if ! test $HTTPD_VERSION -ge 2
+ if ! test "$HTTPD_VERSION_MAJOR" -eq 2 ||
+ ! test "$HTTPD_VERSION_MINOR" -ge 4
then
test_skip_or_die GIT_TEST_HTTPD \
- "at least Apache version 2 is required"
+ "at least Apache version 2.4 is required"
fi
if ! test -d "$DEFAULT_HTTPD_MODULE_PATH"
then
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index 0294739..51a4fbc 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -34,17 +34,6 @@
Protocols h2c
</IfDefine>
-<IfVersion < 2.4>
-LockFile accept.lock
-</IfVersion>
-
-<IfVersion < 2.1>
-<IfModule !mod_auth.c>
- LoadModule auth_module modules/mod_auth.so
-</IfModule>
-</IfVersion>
-
-<IfVersion >= 2.1>
<IfModule !mod_auth_basic.c>
LoadModule auth_basic_module modules/mod_auth_basic.so
</IfModule>
@@ -57,9 +46,7 @@
<IfModule !mod_authz_host.c>
LoadModule authz_host_module modules/mod_authz_host.so
</IfModule>
-</IfVersion>
-<IfVersion >= 2.4>
<IfModule !mod_authn_core.c>
LoadModule authn_core_module modules/mod_authn_core.so
</IfModule>
@@ -83,7 +70,6 @@
LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
</IfModule>
</IfDefine>
-</IfVersion>
PassEnv GIT_VALGRIND
PassEnv GIT_VALGRIND_OPTIONS
@@ -123,6 +109,10 @@
Header set Set-Cookie name=value
</LocationMatch>
<LocationMatch /smart_headers/>
+ <RequireAll>
+ Require expr %{HTTP:x-magic-one} == 'abra'
+ Require expr %{HTTP:x-magic-two} == 'cadabra'
+ </RequireAll>
SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
SetEnv GIT_HTTP_EXPORT_ALL
</LocationMatch>
@@ -205,18 +195,6 @@
RewriteRule ^/redir-objects/(.*/info/refs)$ /dumb/$1 [PT]
RewriteRule ^/redir-objects/(.*/objects/.*)$ /dumb/$1 [R=301]
-# Apache 2.2 does not understand <RequireAll>, so we use RewriteCond.
-# And as RewriteCond does not allow testing for non-matches, we match
-# the desired case first (one has abra, two has cadabra), and let it
-# pass by marking the RewriteRule as [L], "last rule, do not process
-# any other matching RewriteRules after this"), and then have another
-# RewriteRule that matches all other cases and lets them fail via '[F]',
-# "fail the request".
-RewriteCond %{HTTP:x-magic-one} =abra
-RewriteCond %{HTTP:x-magic-two} =cadabra
-RewriteRule ^/smart_headers/.* - [L]
-RewriteRule ^/smart_headers/.* - [F]
-
<IfDefine SSL>
LoadModule ssl_module modules/mod_ssl.so
@@ -225,7 +203,6 @@
SSLRandomSeed startup file:/dev/urandom 512
SSLRandomSeed connect file:/dev/urandom 512
SSLSessionCache none
-SSLMutex file:ssl_mutex
SSLEngine On
</IfDefine>
diff --git a/t/lib-httpd/ssl.cnf b/t/lib-httpd/ssl.cnf
index 6dab257..812e825 100644
--- a/t/lib-httpd/ssl.cnf
+++ b/t/lib-httpd/ssl.cnf
@@ -1,7 +1,7 @@
RANDFILE = $ENV::RANDFILE_PATH
[ req ]
-default_bits = 1024
+default_bits = 2048
distinguished_name = req_distinguished_name
prompt = no
[ req_distinguished_name ]
diff --git a/t/perf/p7822-grep-perl-character.sh b/t/perf/p7822-grep-perl-character.sh
new file mode 100755
index 0000000..87009c6
--- /dev/null
+++ b/t/perf/p7822-grep-perl-character.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description="git-grep's perl regex
+
+If GIT_PERF_GREP_THREADS is set to a list of threads (e.g. '1 4 8'
+etc.) we will test the patterns under those numbers of threads.
+"
+
+. ./perf-lib.sh
+
+test_perf_large_repo
+test_checkout_worktree
+
+if test -n "$GIT_PERF_GREP_THREADS"
+then
+ test_set_prereq PERF_GREP_ENGINES_THREADS
+fi
+
+for pattern in \
+ '\\bhow' \
+ '\\bÆvar' \
+ '\\d+ \\bÆvar' \
+ '\\bBelón\\b' \
+ '\\w{12}\\b'
+do
+ echo '$pattern' >pat
+ if ! test_have_prereq PERF_GREP_ENGINES_THREADS
+ then
+ test_perf "grep -P '$pattern'" --prereq PCRE "
+ git -P grep -f pat || :
+ "
+ else
+ for threads in $GIT_PERF_GREP_THREADS
+ do
+ test_perf "grep -P '$pattern' with $threads threads" --prereq PTHREADS,PCRE "
+ git -c grep.threads=$threads -P grep -f pat || :
+ "
+ done
+ fi
+done
+
+test_done
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 502b4bc..8ea31d1 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -815,7 +815,8 @@
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 @@
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 @@
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 f7ee2f2..89b306c 100755
--- a/t/t0003-attributes.sh
+++ b/t/t0003-attributes.sh
@@ -25,7 +25,15 @@
git check-attr test -- "$path" >actual &&
echo "\"$quoted_path\": test: $expect" >expect &&
test_cmp expect actual
+}
+attr_check_source () {
+ path="$1" expect="$2" source="$3" git_opts="$4" &&
+
+ git $git_opts check-attr --source $source test -- "$path" >actual 2>err &&
+ echo "$path: test: $expect" >expect &&
+ test_cmp expect actual &&
+ test_must_be_empty err
}
test_expect_success 'open-quoted pathname' '
@@ -33,7 +41,6 @@
attr_check a unspecified
'
-
test_expect_success 'setup' '
mkdir -p a/b/d a/c b &&
(
@@ -80,12 +87,23 @@
EOF
'
+test_expect_success 'setup branches' '
+ mkdir -p foo/bar &&
+ test_commit --printf "add .gitattributes" foo/bar/.gitattributes \
+ "f test=f\na/i test=n\n" tag-1 &&
+ test_commit --printf "add .gitattributes" foo/bar/.gitattributes \
+ "g test=g\na/i test=m\n" tag-2 &&
+ rm foo/bar/.gitattributes
+'
+
test_expect_success 'command line checks' '
test_must_fail git check-attr &&
test_must_fail git check-attr -- &&
test_must_fail git check-attr test &&
test_must_fail git check-attr test -- &&
test_must_fail git check-attr -- f &&
+ test_must_fail git check-attr --source &&
+ test_must_fail git check-attr --source not-a-valid-ref &&
echo "f" | test_must_fail git check-attr --stdin &&
echo "f" | test_must_fail git check-attr --stdin -- f &&
echo "f" | test_must_fail git check-attr --stdin test -- f &&
@@ -203,9 +221,12 @@
test_cmp expect actual
'
-test_expect_success 'attribute test: --all option' '
+test_expect_success 'setup --all option' '
grep -v unspecified <expect-all | sort >specified-all &&
- sed -e "s/:.*//" <expect-all | uniq >stdin-all &&
+ sed -e "s/:.*//" <expect-all | uniq >stdin-all
+'
+
+test_expect_success 'attribute test: --all option' '
git check-attr --stdin --all <stdin-all >tmp &&
sort tmp >actual &&
test_cmp specified-all actual
@@ -284,6 +305,15 @@
)
'
+test_expect_success 'using --source' '
+ attr_check_source foo/bar/f f tag-1 &&
+ attr_check_source foo/bar/a/i n tag-1 &&
+ attr_check_source foo/bar/f unspecified tag-2 &&
+ attr_check_source foo/bar/a/i m tag-2 &&
+ attr_check_source foo/bar/g g tag-2 &&
+ attr_check_source foo/bar/g unspecified tag-1
+'
+
test_expect_success 'setup bare' '
git clone --template= --bare . bare.git
'
@@ -303,6 +333,18 @@
)
'
+test_expect_success 'bare repository: with --source' '
+ (
+ cd bare.git &&
+ attr_check_source foo/bar/f f tag-1 &&
+ attr_check_source foo/bar/a/i n tag-1 &&
+ attr_check_source foo/bar/f unspecified tag-2 &&
+ attr_check_source foo/bar/a/i m tag-2 &&
+ attr_check_source foo/bar/g g tag-2 &&
+ attr_check_source foo/bar/g unspecified tag-1
+ )
+'
+
test_expect_success 'bare repository: check that --cached honors index' '
(
cd bare.git &&
@@ -376,4 +418,63 @@
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=1048576 count=101 2>/dev/null &&
+ git check-attr --all path >/dev/null 2>err &&
+ echo "warning: ignoring overly large gitattributes file ${SQ}.gitattributes${SQ}" >expect &&
+ test_cmp expect err
+'
+
+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=1048576 count=101 2>/dev/null | git hash-object -w --stdin) &&
+ git update-index --add --cacheinfo 100644,$blob,.gitattributes &&
+ git check-attr --cached --all path >/dev/null 2>err &&
+ echo "warning: ignoring overly large gitattributes blob ${SQ}.gitattributes${SQ}" >expect &&
+ test_cmp expect err
+'
+
test_done
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index 2490162..e18b160 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -88,6 +88,13 @@
check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 +0000'
check_parse '2008-02-14 20:30:45 -0500' '2008-02-14 20:30:45 -0500'
check_parse '2008.02.14 20:30:45 -0500' '2008-02-14 20:30:45 -0500'
+check_parse '20080214T20:30:45' '2008-02-14 20:30:45 +0000'
+check_parse '20080214T20:30' '2008-02-14 20:30:00 +0000'
+check_parse '20080214T20' '2008-02-14 20:00:00 +0000'
+check_parse '20080214T203045' '2008-02-14 20:30:45 +0000'
+check_parse '20080214T2030' '2008-02-14 20:30:00 +0000'
+check_parse '20080214T000000.20' '2008-02-14 00:00:00 +0000'
+check_parse '20080214T00:00:00.20' '2008-02-14 00:00:00 +0000'
check_parse '20080214T203045-04:00' '2008-02-14 20:30:45 -0400'
check_parse '20080214T203045 -04:00' '2008-02-14 20:30:45 -0400'
check_parse '20080214T203045.019-04:00' '2008-02-14 20:30:45 -0400'
@@ -99,6 +106,7 @@
check_parse '2008-02-14 20:30:45 -:30' '2008-02-14 20:30:45 +0000'
check_parse '2008-02-14 20:30:45 -05:00' '2008-02-14 20:30:45 -0500'
check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 -0500' EST5
+check_parse 'Thu, 7 Apr 2005 15:14:13 -0700' '2005-04-07 15:14:13 -0700'
check_approxidate() {
echo "$1 -> $2 +0000" >expect
diff --git a/t/t0007-git-var.sh b/t/t0007-git-var.sh
index e56f4b9..eeb8539 100755
--- a/t/t0007-git-var.sh
+++ b/t/t0007-git-var.sh
@@ -5,6 +5,12 @@
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
+sane_unset_all_editors () {
+ sane_unset GIT_EDITOR &&
+ sane_unset VISUAL &&
+ sane_unset EDITOR
+}
+
test_expect_success 'get GIT_AUTHOR_IDENT' '
test_tick &&
echo "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE" >expect &&
@@ -47,6 +53,100 @@
)
'
+test_expect_success 'get GIT_EDITOR without configuration' '
+ (
+ sane_unset_all_editors &&
+ test_expect_code 1 git var GIT_EDITOR >out &&
+ test_must_be_empty out
+ )
+'
+
+test_expect_success 'get GIT_EDITOR with configuration' '
+ test_config core.editor foo &&
+ (
+ sane_unset_all_editors &&
+ echo foo >expect &&
+ git var GIT_EDITOR >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'get GIT_EDITOR with environment variable GIT_EDITOR' '
+ (
+ sane_unset_all_editors &&
+ echo bar >expect &&
+ GIT_EDITOR=bar git var GIT_EDITOR >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'get GIT_EDITOR with environment variable EDITOR' '
+ (
+ sane_unset_all_editors &&
+ echo bar >expect &&
+ EDITOR=bar git var GIT_EDITOR >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'get GIT_EDITOR with configuration and environment variable GIT_EDITOR' '
+ test_config core.editor foo &&
+ (
+ sane_unset_all_editors &&
+ echo bar >expect &&
+ GIT_EDITOR=bar git var GIT_EDITOR >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'get GIT_EDITOR with configuration and environment variable EDITOR' '
+ test_config core.editor foo &&
+ (
+ sane_unset_all_editors &&
+ echo foo >expect &&
+ EDITOR=bar git var GIT_EDITOR >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'get GIT_SEQUENCE_EDITOR without configuration' '
+ (
+ sane_unset GIT_SEQUENCE_EDITOR &&
+ git var GIT_EDITOR >expect &&
+ git var GIT_SEQUENCE_EDITOR >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'get GIT_SEQUENCE_EDITOR with configuration' '
+ test_config sequence.editor foo &&
+ (
+ sane_unset GIT_SEQUENCE_EDITOR &&
+ echo foo >expect &&
+ git var GIT_SEQUENCE_EDITOR >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'get GIT_SEQUENCE_EDITOR with environment variable' '
+ (
+ sane_unset GIT_SEQUENCE_EDITOR &&
+ echo bar >expect &&
+ GIT_SEQUENCE_EDITOR=bar git var GIT_SEQUENCE_EDITOR >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'get GIT_SEQUENCE_EDITOR with configuration and environment variable' '
+ test_config sequence.editor foo &&
+ (
+ sane_unset GIT_SEQUENCE_EDITOR &&
+ echo bar >expect &&
+ GIT_SEQUENCE_EDITOR=bar git var GIT_SEQUENCE_EDITOR >actual &&
+ test_cmp expect actual
+ )
+'
+
# For git var -l, we check only a representative variable;
# testing the whole output would make our test too brittle with
# respect to unrelated changes in the test suite's environment.
diff --git a/t/t0017-env-helper.sh b/t/t0017-env-helper.sh
index 2e42fba..fc14ba0 100755
--- a/t/t0017-env-helper.sh
+++ b/t/t0017-env-helper.sh
@@ -1,87 +1,87 @@
#!/bin/sh
-test_description='test env--helper'
+test_description='test test-tool env-helper'
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
-test_expect_success 'env--helper usage' '
- test_must_fail git env--helper &&
- test_must_fail git env--helper --type=bool &&
- test_must_fail git env--helper --type=ulong &&
- test_must_fail git env--helper --type=bool &&
- test_must_fail git env--helper --type=bool --default &&
- test_must_fail git env--helper --type=bool --default= &&
- test_must_fail git env--helper --defaultxyz
+test_expect_success 'test-tool env-helper usage' '
+ test_must_fail test-tool env-helper &&
+ test_must_fail test-tool env-helper --type=bool &&
+ test_must_fail test-tool env-helper --type=ulong &&
+ test_must_fail test-tool env-helper --type=bool &&
+ test_must_fail test-tool env-helper --type=bool --default &&
+ test_must_fail test-tool env-helper --type=bool --default= &&
+ test_must_fail test-tool env-helper --defaultxyz
'
-test_expect_success 'env--helper bad default values' '
- test_must_fail git env--helper --type=bool --default=1xyz MISSING &&
- test_must_fail git env--helper --type=ulong --default=1xyz MISSING
+test_expect_success 'test-tool env-helper bad default values' '
+ test_must_fail test-tool env-helper --type=bool --default=1xyz MISSING &&
+ test_must_fail test-tool env-helper --type=ulong --default=1xyz MISSING
'
-test_expect_success 'env--helper --type=bool' '
+test_expect_success 'test-tool env-helper --type=bool' '
# Test various --default bool values
echo true >expected &&
- git env--helper --type=bool --default=1 MISSING >actual &&
+ test-tool env-helper --type=bool --default=1 MISSING >actual &&
test_cmp expected actual &&
- git env--helper --type=bool --default=yes MISSING >actual &&
+ test-tool env-helper --type=bool --default=yes MISSING >actual &&
test_cmp expected actual &&
- git env--helper --type=bool --default=true MISSING >actual &&
+ test-tool env-helper --type=bool --default=true MISSING >actual &&
test_cmp expected actual &&
echo false >expected &&
- test_must_fail git env--helper --type=bool --default=0 MISSING >actual &&
+ test_must_fail test-tool env-helper --type=bool --default=0 MISSING >actual &&
test_cmp expected actual &&
- test_must_fail git env--helper --type=bool --default=no MISSING >actual &&
+ test_must_fail test-tool env-helper --type=bool --default=no MISSING >actual &&
test_cmp expected actual &&
- test_must_fail git env--helper --type=bool --default=false MISSING >actual &&
+ test_must_fail test-tool env-helper --type=bool --default=false MISSING >actual &&
test_cmp expected actual &&
# No output with --exit-code
- git env--helper --type=bool --default=true --exit-code MISSING >actual.out 2>actual.err &&
+ test-tool env-helper --type=bool --default=true --exit-code MISSING >actual.out 2>actual.err &&
test_must_be_empty actual.out &&
test_must_be_empty actual.err &&
- test_must_fail git env--helper --type=bool --default=false --exit-code MISSING >actual.out 2>actual.err &&
+ test_must_fail test-tool env-helper --type=bool --default=false --exit-code MISSING >actual.out 2>actual.err &&
test_must_be_empty actual.out &&
test_must_be_empty actual.err &&
# Existing variable
- EXISTS=true git env--helper --type=bool --default=false --exit-code EXISTS >actual.out 2>actual.err &&
+ EXISTS=true test-tool env-helper --type=bool --default=false --exit-code EXISTS >actual.out 2>actual.err &&
test_must_be_empty actual.out &&
test_must_be_empty actual.err &&
test_must_fail \
env EXISTS=false \
- git env--helper --type=bool --default=true --exit-code EXISTS >actual.out 2>actual.err &&
+ test-tool env-helper --type=bool --default=true --exit-code EXISTS >actual.out 2>actual.err &&
test_must_be_empty actual.out &&
test_must_be_empty actual.err
'
-test_expect_success 'env--helper --type=ulong' '
+test_expect_success 'test-tool env-helper --type=ulong' '
echo 1234567890 >expected &&
- git env--helper --type=ulong --default=1234567890 MISSING >actual.out 2>actual.err &&
+ test-tool env-helper --type=ulong --default=1234567890 MISSING >actual.out 2>actual.err &&
test_cmp expected actual.out &&
test_must_be_empty actual.err &&
echo 0 >expected &&
- test_must_fail git env--helper --type=ulong --default=0 MISSING >actual &&
+ test_must_fail test-tool env-helper --type=ulong --default=0 MISSING >actual &&
test_cmp expected actual &&
- git env--helper --type=ulong --default=1234567890 --exit-code MISSING >actual.out 2>actual.err &&
+ test-tool env-helper --type=ulong --default=1234567890 --exit-code MISSING >actual.out 2>actual.err &&
test_must_be_empty actual.out &&
test_must_be_empty actual.err &&
- EXISTS=1234567890 git env--helper --type=ulong --default=0 EXISTS --exit-code >actual.out 2>actual.err &&
+ EXISTS=1234567890 test-tool env-helper --type=ulong --default=0 EXISTS --exit-code >actual.out 2>actual.err &&
test_must_be_empty actual.out &&
test_must_be_empty actual.err &&
echo 1234567890 >expected &&
- EXISTS=1234567890 git env--helper --type=ulong --default=0 EXISTS >actual.out 2>actual.err &&
+ EXISTS=1234567890 test-tool env-helper --type=ulong --default=0 EXISTS >actual.out 2>actual.err &&
test_cmp expected actual.out &&
test_must_be_empty actual.err
'
-test_expect_success 'env--helper reads config thanks to trace2' '
+test_expect_success 'test-tool env-helper reads config thanks to trace2' '
mkdir home &&
git config -f home/.gitconfig include.path cycle &&
git config -f home/cycle include.path .gitconfig &&
@@ -93,7 +93,7 @@
test_must_fail \
env HOME="$(pwd)/home" GIT_TEST_ENV_HELPER=true \
- git -C cycle env--helper --type=bool --default=0 --exit-code GIT_TEST_ENV_HELPER 2>err &&
+ test-tool -C cycle env-helper --type=bool --default=0 --exit-code GIT_TEST_ENV_HELPER 2>err &&
grep "exceeded maximum include depth" err
'
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index abecd75..46abbee 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -8,8 +8,8 @@
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
-TEST_ROOT="$PWD"
-PATH=$TEST_ROOT:$PATH
+PATH=$PWD:$PATH
+TEST_ROOT="$(pwd)"
write_script <<\EOF "$TEST_ROOT/rot13.sh"
tr \
diff --git a/t/t0023-crlf-am.sh b/t/t0023-crlf-am.sh
index f9bbb91..5758055 100755
--- a/t/t0023-crlf-am.sh
+++ b/t/t0023-crlf-am.sh
@@ -2,6 +2,7 @@
test_description='Test am with auto.crlf'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
cat >patchfile <<\EOF
diff --git a/t/t0066-dir-iterator.sh b/t/t0066-dir-iterator.sh
index 63a1a45..04b8116 100755
--- a/t/t0066-dir-iterator.sh
+++ b/t/t0066-dir-iterator.sh
@@ -110,7 +110,9 @@
mkdir -p dir5/a/c &&
ln -s ../c dir5/a/b/d &&
ln -s ../ dir5/a/b/e &&
- ln -s ../../ dir5/a/b/f
+ ln -s ../../ dir5/a/b/f &&
+
+ ln -s dir4 dir6
'
test_expect_success SYMLINKS 'dir-iterator should not follow symlinks by default' '
@@ -146,4 +148,27 @@
test_cmp expected-follow-sorted-output actual-follow-sorted-output
'
+test_expect_success SYMLINKS 'dir-iterator does not resolve top-level symlinks' '
+ test_must_fail test-tool dir-iterator ./dir6 >out &&
+
+ grep "ENOTDIR" out
+'
+
+test_expect_success SYMLINKS 'dir-iterator resolves top-level symlinks w/ follow flag' '
+ cat >expected-follow-sorted-output <<-EOF &&
+ [d] (a) [a] ./dir6/a
+ [d] (a/f) [f] ./dir6/a/f
+ [d] (a/f/c) [c] ./dir6/a/f/c
+ [d] (b) [b] ./dir6/b
+ [d] (b/c) [c] ./dir6/b/c
+ [f] (a/d) [d] ./dir6/a/d
+ [f] (a/e) [e] ./dir6/a/e
+ EOF
+
+ test-tool dir-iterator --follow-symlinks ./dir6 >out &&
+ sort out >actual-follow-sorted-output &&
+
+ test_cmp expected-follow-sorted-output actual-follow-sorted-output
+'
+
test_done
diff --git a/t/t0068-for-each-repo.sh b/t/t0068-for-each-repo.sh
index c6e0d65..3648d43 100755
--- a/t/t0068-for-each-repo.sh
+++ b/t/t0068-for-each-repo.sh
@@ -2,6 +2,7 @@
test_description='git for-each-repo builtin'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'run based on configured value' '
diff --git a/t/t0070-fundamental.sh b/t/t0070-fundamental.sh
index 8d59905..574de34 100755
--- a/t/t0070-fundamental.sh
+++ b/t/t0070-fundamental.sh
@@ -6,6 +6,7 @@
Verify wrappers and compatibility functions.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'character classes (isspace, isalpha etc.)' '
diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh
index 516a611..3fb1b0c 100755
--- a/t/t1001-read-tree-m-2way.sh
+++ b/t/t1001-read-tree-m-2way.sh
@@ -370,7 +370,7 @@
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/t1006-cat-file.sh b/t/t1006-cat-file.sh
index 23b8942..2d875b1 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -292,8 +292,8 @@
commit_sha1=$(echo_without_newline "$commit_message" | git commit-tree $tree_sha1)
commit_size=$(($(test_oid hexsz) + 137))
commit_content="tree $tree_sha1
-author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> 0000000000 +0000
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 0000000000 +0000
+author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> 0 +0000
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 0 +0000
$commit_message"
@@ -304,7 +304,7 @@
tag hellotag
tagger $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
tag_description="This is a tag"
-tag_content="$tag_header_without_timestamp 0000000000 +0000
+tag_content="$tag_header_without_timestamp 0 +0000
$tag_description"
diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh
index ac5ad8c..ac3d173 100755
--- a/t/t1007-hash-object.sh
+++ b/t/t1007-hash-object.sh
@@ -203,23 +203,34 @@
test_expect_success 'too-short tree' '
echo abc >malformed-tree &&
test_must_fail git hash-object -t tree malformed-tree 2>err &&
- test_i18ngrep "too-short tree object" err
+ grep "too-short tree object" err
'
test_expect_success 'malformed mode in tree' '
- hex_sha1=$(echo foo | git hash-object --stdin -w) &&
- bin_sha1=$(echo $hex_sha1 | hex2oct) &&
- printf "9100644 \0$bin_sha1" >tree-with-malformed-mode &&
+ hex_oid=$(echo foo | git hash-object --stdin -w) &&
+ bin_oid=$(echo $hex_oid | hex2oct) &&
+ printf "9100644 \0$bin_oid" >tree-with-malformed-mode &&
test_must_fail git hash-object -t tree tree-with-malformed-mode 2>err &&
- test_i18ngrep "malformed mode in tree entry" err
+ grep "malformed mode in tree entry" err
'
test_expect_success 'empty filename in tree' '
- hex_sha1=$(echo foo | git hash-object --stdin -w) &&
- bin_sha1=$(echo $hex_sha1 | hex2oct) &&
- printf "100644 \0$bin_sha1" >tree-with-empty-filename &&
+ hex_oid=$(echo foo | git hash-object --stdin -w) &&
+ bin_oid=$(echo $hex_oid | hex2oct) &&
+ printf "100644 \0$bin_oid" >tree-with-empty-filename &&
test_must_fail git hash-object -t tree tree-with-empty-filename 2>err &&
- test_i18ngrep "empty filename in tree entry" err
+ grep "empty filename in tree entry" err
+'
+
+test_expect_success 'duplicate filename in tree' '
+ hex_oid=$(echo foo | git hash-object --stdin -w) &&
+ bin_oid=$(echo $hex_oid | hex2oct) &&
+ {
+ printf "100644 file\0$bin_oid" &&
+ printf "100644 file\0$bin_oid"
+ } >tree-with-duplicate-filename &&
+ test_must_fail git hash-object -t tree tree-with-duplicate-filename 2>err &&
+ grep "duplicateEntries" err
'
test_expect_success 'corrupt commit' '
diff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh
index 742f0fa..595b24c 100755
--- a/t/t1011-read-tree-sparse-checkout.sh
+++ b/t/t1011-read-tree-sparse-checkout.sh
@@ -12,6 +12,7 @@
'
TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-read-tree.sh
diff --git a/t/t1022-read-tree-partial-clone.sh b/t/t1022-read-tree-partial-clone.sh
index da53971..cca4380 100755
--- a/t/t1022-read-tree-partial-clone.sh
+++ b/t/t1022-read-tree-partial-clone.sh
@@ -3,7 +3,7 @@
test_description='git read-tree in partial clones'
TEST_NO_CREATE_REPO=1
-
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'read-tree in partial clone prefetches in one batch' '
diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh
index b563d6c..627267b 100755
--- a/t/t1091-sparse-checkout-builtin.sh
+++ b/t/t1091-sparse-checkout-builtin.sh
@@ -238,7 +238,7 @@
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 @@
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 93a2f91..1b6437e 100755
--- a/t/t1301-shared-repo.sh
+++ b/t/t1301-shared-repo.sh
@@ -8,6 +8,8 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Remove a default ACL from the test dir if possible.
@@ -25,6 +27,7 @@
for u in 002 022
do
test_expect_success POSIXPERM "shared=1 does not clear bits preset by umask $u" '
+ test_when_finished "rm -rf sub" &&
mkdir sub && (
cd sub &&
umask $u &&
@@ -42,12 +45,9 @@
;;
esac
'
- rm -rf sub
done
test_expect_success 'shared=all' '
- mkdir sub &&
- cd sub &&
git init --template= --shared=all &&
test 2 = $(git config core.sharedrepository)
'
@@ -132,6 +132,7 @@
'
test_expect_success POSIXPERM 'forced modes' '
+ test_when_finished "rm -rf new" &&
mkdir -p templates/hooks &&
echo update-server-info >templates/hooks/post-update &&
chmod +x templates/hooks/post-update &&
@@ -140,7 +141,8 @@
(
cd new &&
umask 002 &&
- git init --shared=0660 --template=templates &&
+ git init --shared=0660 --template=../templates &&
+ test_path_is_file .git/hooks/post-update &&
>frotz &&
git add frotz &&
git commit -a -m initial &&
@@ -173,6 +175,7 @@
'
test_expect_success POSIXPERM 'remote init does not use config from cwd' '
+ test_when_finished "rm -rf child.git" &&
git config core.sharedrepository 0666 &&
umask 0022 &&
git init --bare child.git &&
@@ -192,7 +195,7 @@
'
test_expect_success POSIXPERM 're-init respects core.sharedrepository (remote)' '
- rm -rf child.git &&
+ test_when_finished "rm -rf child.git" &&
umask 0022 &&
git init --bare --shared=0666 child.git &&
test_path_is_missing child.git/foo &&
@@ -203,7 +206,7 @@
'
test_expect_success POSIXPERM '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 0acabb6..70389fa 100755
--- a/t/t1302-repo-version.sh
+++ b/t/t1302-repo-version.sh
@@ -5,6 +5,7 @@
test_description='Test repository version check'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -27,7 +28,7 @@
'
test_expect_success '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/t1304-default-acl.sh b/t/t1304-default-acl.sh
index c69ae41..31b89dd 100755
--- a/t/t1304-default-acl.sh
+++ b/t/t1304-default-acl.sh
@@ -9,6 +9,7 @@
# => this must come before . ./test-lib.sh
umask 077
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# We need an arbitrary other user give permission to using ACLs. root
diff --git a/t/t1404-update-ref-errors.sh b/t/t1404-update-ref-errors.sh
index 13c2b43..b5606d9 100755
--- a/t/t1404-update-ref-errors.sh
+++ b/t/t1404-update-ref-errors.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='Test git update-ref error handling'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Create some references, perhaps run pack-refs --all, then try to
diff --git a/t/t1408-packed-refs.sh b/t/t1408-packed-refs.sh
index 41ba1f1..9469c79 100755
--- a/t/t1408-packed-refs.sh
+++ b/t/t1408-packed-refs.sh
@@ -5,6 +5,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t1409-avoid-packing-refs.sh b/t/t1409-avoid-packing-refs.sh
index be12fb6..f23c015 100755
--- a/t/t1409-avoid-packing-refs.sh
+++ b/t/t1409-avoid-packing-refs.sh
@@ -2,6 +2,7 @@
test_description='avoid rewriting packed-refs unnecessarily'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Add an identifying mark to the packed-refs file header line. This
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index aa59954..6c45965 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -7,6 +7,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
check_have () {
diff --git a/t/t1413-reflog-detach.sh b/t/t1413-reflog-detach.sh
index 934688a..d2a4822 100755
--- a/t/t1413-reflog-detach.sh
+++ b/t/t1413-reflog-detach.sh
@@ -4,6 +4,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
reset_state () {
diff --git a/t/t1416-ref-transaction-hooks.sh b/t/t1416-ref-transaction-hooks.sh
index 2773172..b32ca79 100755
--- a/t/t1416-ref-transaction-hooks.sh
+++ b/t/t1416-ref-transaction-hooks.sh
@@ -5,6 +5,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index ace4556..fdb886d 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -212,7 +212,7 @@
test_expect_success 'email with embedded > is not okay' '
git cat-file commit HEAD >basis &&
sed "s/@[a-z]/&>/" basis >bad-email &&
- new=$(git hash-object -t commit -w --stdin <bad-email) &&
+ new=$(git hash-object --literally -t commit -w --stdin <bad-email) &&
test_when_finished "remove_object $new" &&
git update-ref refs/heads/bogus "$new" &&
test_when_finished "git update-ref -d refs/heads/bogus" &&
@@ -223,7 +223,7 @@
test_expect_success 'missing < email delimiter is reported nicely' '
git cat-file commit HEAD >basis &&
sed "s/<//" basis >bad-email-2 &&
- new=$(git hash-object -t commit -w --stdin <bad-email-2) &&
+ new=$(git hash-object --literally -t commit -w --stdin <bad-email-2) &&
test_when_finished "remove_object $new" &&
git update-ref refs/heads/bogus "$new" &&
test_when_finished "git update-ref -d refs/heads/bogus" &&
@@ -234,7 +234,7 @@
test_expect_success 'missing email is reported nicely' '
git cat-file commit HEAD >basis &&
sed "s/[a-z]* <[^>]*>//" basis >bad-email-3 &&
- new=$(git hash-object -t commit -w --stdin <bad-email-3) &&
+ new=$(git hash-object --literally -t commit -w --stdin <bad-email-3) &&
test_when_finished "remove_object $new" &&
git update-ref refs/heads/bogus "$new" &&
test_when_finished "git update-ref -d refs/heads/bogus" &&
@@ -245,7 +245,7 @@
test_expect_success '> in name is reported' '
git cat-file commit HEAD >basis &&
sed "s/ </> </" basis >bad-email-4 &&
- new=$(git hash-object -t commit -w --stdin <bad-email-4) &&
+ new=$(git hash-object --literally -t commit -w --stdin <bad-email-4) &&
test_when_finished "remove_object $new" &&
git update-ref refs/heads/bogus "$new" &&
test_when_finished "git update-ref -d refs/heads/bogus" &&
@@ -258,7 +258,7 @@
git cat-file commit HEAD >basis &&
sed "s/^\\(author .*>\\) [0-9]*/\\1 18446744073709551617/" \
<basis >bad-timestamp &&
- new=$(git hash-object -t commit -w --stdin <bad-timestamp) &&
+ new=$(git hash-object --literally -t commit -w --stdin <bad-timestamp) &&
test_when_finished "remove_object $new" &&
git update-ref refs/heads/bogus "$new" &&
test_when_finished "git update-ref -d refs/heads/bogus" &&
@@ -269,7 +269,7 @@
test_expect_success 'commit with NUL in header' '
git cat-file commit HEAD >basis &&
sed "s/author ./author Q/" <basis | q_to_nul >commit-NUL-header &&
- new=$(git hash-object -t commit -w --stdin <commit-NUL-header) &&
+ new=$(git hash-object --literally -t commit -w --stdin <commit-NUL-header) &&
test_when_finished "remove_object $new" &&
git update-ref refs/heads/bogus "$new" &&
test_when_finished "git update-ref -d refs/heads/bogus" &&
@@ -292,7 +292,7 @@
git cat-file tree $T &&
git cat-file tree $T
) |
- git hash-object -w -t tree --stdin
+ git hash-object --literally -w -t tree --stdin
) &&
test_must_fail git fsck 2>out &&
test_i18ngrep "error in tree .*contains duplicate file entries" out
@@ -426,7 +426,7 @@
This is an invalid tag.
EOF
- tag=$(git hash-object -t tag -w --stdin <wrong-tag) &&
+ tag=$(git hash-object --literally -t tag -w --stdin <wrong-tag) &&
test_when_finished "remove_object $tag" &&
echo $tag >.git/refs/tags/wrong &&
test_when_finished "git update-ref -d refs/tags/wrong" &&
@@ -558,7 +558,7 @@
test_expect_success 'force fsck to ignore double author' '
git cat-file commit HEAD >basis &&
sed "s/^author .*/&,&/" <basis | tr , \\n >multiple-authors &&
- new=$(git hash-object -t commit -w --stdin <multiple-authors) &&
+ new=$(git hash-object --literally -t commit -w --stdin <multiple-authors) &&
test_when_finished "remove_object $new" &&
git update-ref refs/heads/bogus "$new" &&
test_when_finished "git update-ref -d refs/heads/bogus" &&
@@ -573,7 +573,7 @@
(git init null-blob &&
cd null-blob &&
sha=$(printf "100644 file$_bz$_bzoid" |
- git hash-object -w --stdin -t tree) &&
+ git hash-object --literally -w --stdin -t tree) &&
git fsck 2>out &&
test_i18ngrep "warning.*null sha1" out
)
@@ -583,7 +583,7 @@
(git init null-commit &&
cd null-commit &&
sha=$(printf "160000 submodule$_bz$_bzoid" |
- git hash-object -w --stdin -t tree) &&
+ git hash-object --literally -w --stdin -t tree) &&
git fsck 2>out &&
test_i18ngrep "warning.*null sha1" out
)
@@ -648,7 +648,7 @@
git commit --allow-empty -m "initial commitQNUL after message" &&
git cat-file commit HEAD >original &&
q_to_nul <original >munged &&
- git hash-object -w -t commit --stdin <munged >name &&
+ git hash-object --literally -w -t commit --stdin <munged >name &&
git branch bad $(cat name) &&
test_must_fail git -c fsck.nulInCommit=error fsck 2>warn.1 &&
@@ -794,8 +794,8 @@
git cat-file commit HEAD >basis &&
sed "s/</one/" basis >one &&
sed "s/</foo/" basis >two &&
- one=$(git hash-object -t commit -w one) &&
- two=$(git hash-object -t commit -w two) &&
+ one=$(git hash-object --literally -t commit -w one) &&
+ two=$(git hash-object --literally -t commit -w two) &&
pack=$(
{
echo $one &&
@@ -999,4 +999,28 @@
)
'
+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/t1451-fsck-buffer.sh b/t/t1451-fsck-buffer.sh
new file mode 100755
index 0000000..3413da4
--- /dev/null
+++ b/t/t1451-fsck-buffer.sh
@@ -0,0 +1,142 @@
+#!/bin/sh
+
+test_description='fsck on buffers without NUL termination
+
+The goal here is to make sure that the various fsck parsers never look
+past the end of the buffer they are given, even when encountering broken
+or truncated objects.
+
+We have to use "hash-object" for this because most code paths that read objects
+append an extra NUL for safety after the buffer. But hash-object, since it is
+reading straight from a file (and possibly even mmap-ing it) cannot always do
+so.
+
+These tests _might_ catch such overruns in normal use, but should be run with
+ASan or valgrind for more confidence.
+'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+# the general idea for tags and commits is to build up the "base" file
+# progressively, and then test new truncations on top of it.
+reset () {
+ test_expect_success 'reset input to empty' '
+ >base
+ '
+}
+
+add () {
+ content="$1"
+ type=${content%% *}
+ test_expect_success "add $type line" '
+ echo "$content" >>base
+ '
+}
+
+check () {
+ type=$1
+ fsck=$2
+ content=$3
+ test_expect_success "truncated $type ($fsck, \"$content\")" '
+ # do not pipe into hash-object here; we want to increase
+ # the chance that it uses a fixed-size buffer or mmap,
+ # and a pipe would be read into a strbuf.
+ {
+ cat base &&
+ echo "$content"
+ } >input &&
+ test_must_fail git hash-object -t "$type" input 2>err &&
+ grep "$fsck" err
+ '
+}
+
+test_expect_success 'create valid objects' '
+ git commit --allow-empty -m foo &&
+ commit=$(git rev-parse --verify HEAD) &&
+ tree=$(git rev-parse --verify HEAD^{tree})
+'
+
+reset
+check commit missingTree ""
+check commit missingTree "tr"
+check commit missingTree "tree"
+check commit badTreeSha1 "tree "
+check commit badTreeSha1 "tree 1234"
+add "tree $tree"
+
+# these expect missingAuthor because "parent" is optional
+check commit missingAuthor ""
+check commit missingAuthor "par"
+check commit missingAuthor "parent"
+check commit badParentSha1 "parent "
+check commit badParentSha1 "parent 1234"
+add "parent $commit"
+
+check commit missingAuthor ""
+check commit missingAuthor "au"
+check commit missingAuthor "author"
+ident_checks () {
+ check $1 missingEmail "$2 "
+ check $1 missingEmail "$2 name"
+ check $1 badEmail "$2 name <"
+ check $1 badEmail "$2 name <email"
+ check $1 missingSpaceBeforeDate "$2 name <email>"
+ check $1 badDate "$2 name <email> "
+ check $1 badDate "$2 name <email> 1234"
+ check $1 badTimezone "$2 name <email> 1234 "
+ check $1 badTimezone "$2 name <email> 1234 +"
+}
+ident_checks commit author
+add "author name <email> 1234 +0000"
+
+check commit missingCommitter ""
+check commit missingCommitter "co"
+check commit missingCommitter "committer"
+ident_checks commit committer
+add "committer name <email> 1234 +0000"
+
+reset
+check tag missingObject ""
+check tag missingObject "obj"
+check tag missingObject "object"
+check tag badObjectSha1 "object "
+check tag badObjectSha1 "object 1234"
+add "object $commit"
+
+check tag missingType ""
+check tag missingType "ty"
+check tag missingType "type"
+check tag badType "type "
+check tag badType "type com"
+add "type commit"
+
+check tag missingTagEntry ""
+check tag missingTagEntry "ta"
+check tag missingTagEntry "tag"
+check tag badTagName "tag "
+add "tag foo"
+
+check tag missingTagger ""
+check tag missingTagger "ta"
+check tag missingTagger "tagger"
+ident_checks tag tagger
+
+# trees are a binary format and can't use our earlier helpers
+test_expect_success 'truncated tree (short hash)' '
+ printf "100644 foo\0\1\1\1\1" >input &&
+ test_must_fail git hash-object -t tree input 2>err &&
+ grep badTree err
+'
+
+test_expect_success 'truncated tree (missing nul)' '
+ # these two things are indistinguishable to the parser. The important
+ # thing about this is example is that there are enough bytes to
+ # make up a hash, and that there is no NUL (and we confirm that the
+ # parser does not walk past the end of the buffer).
+ printf "100644 a long filename, or a hash with missing nul?" >input &&
+ test_must_fail git hash-object -t tree input 2>err &&
+ grep badTree err
+'
+
+test_done
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 81de584..37ee509 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -195,7 +195,7 @@
'
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/t1501-work-tree.sh b/t/t1501-work-tree.sh
index b755580..ae6528a 100755
--- a/t/t1501-work-tree.sh
+++ b/t/t1501-work-tree.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test separate work tree'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t1509-root-work-tree.sh b/t/t1509-root-work-tree.sh
index 553a3f6..c799f5b 100755
--- a/t/t1509-root-work-tree.sh
+++ b/t/t1509-root-work-tree.sh
@@ -221,7 +221,8 @@
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 @@
# 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_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 010989f..9368d82 100755
--- a/t/t1600-index.sh
+++ b/t/t1600-index.sh
@@ -65,6 +65,37 @@
)
'
+test_expect_success 'index.skipHash config option' '
+ rm -f .git/index &&
+ git -c index.skipHash=true add a &&
+ test_trailing_hash .git/index >hash &&
+ echo $(test_oid zero) >expect &&
+ test_cmp expect hash &&
+ git fsck &&
+
+ rm -f .git/index &&
+ git -c feature.manyFiles=true add a &&
+ test_trailing_hash .git/index >hash &&
+ cmp expect hash &&
+
+ rm -f .git/index &&
+ git -c feature.manyFiles=true \
+ -c index.skipHash=false add a &&
+ test_trailing_hash .git/index >hash &&
+ ! cmp expect hash &&
+
+ test_commit start &&
+ git -c protocol.file.allow=always submodule add ./ sub &&
+ git config index.skipHash false &&
+ git -C sub config index.skipHash true &&
+ rm -f .git/modules/sub/index &&
+ >sub/file &&
+ git -C sub add a &&
+ test_trailing_hash .git/modules/sub/index >hash &&
+ test_cmp expect hash &&
+ git -C sub fsck
+'
+
test_index_version () {
INDEX_VERSION_CONFIG=$1 &&
FEATURE_MANY_FILES=$2 &&
diff --git a/t/t1800-hook.sh b/t/t1800-hook.sh
index 2ef3579..3506f62 100755
--- a/t/t1800-hook.sh
+++ b/t/t1800-hook.sh
@@ -177,4 +177,22 @@
test_cmp expect actual
'
+test_expect_success 'stdin to hooks' '
+ write_script .git/hooks/test-hook <<-\EOF &&
+ echo BEGIN stdin
+ cat
+ echo END stdin
+ EOF
+
+ cat >expect <<-EOF &&
+ BEGIN stdin
+ hello
+ END stdin
+ EOF
+
+ echo hello >input &&
+ git hook run --to-stdin=input test-hook 2>actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t2012-checkout-last.sh b/t/t2012-checkout-last.sh
index 1f6c4ed..4b6372f 100755
--- a/t/t2012-checkout-last.sh
+++ b/t/t2012-checkout-last.sh
@@ -5,6 +5,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t2016-checkout-patch.sh b/t/t2016-checkout-patch.sh
index a5822e4..747eb55 100755
--- a/t/t2016-checkout-patch.sh
+++ b/t/t2016-checkout-patch.sh
@@ -4,12 +4,6 @@
. ./lib-patch-mode.sh
-if ! test_have_prereq ADD_I_USE_BUILTIN && ! test_have_prereq PERL
-then
- skip_all='skipping interactive add tests, PERL not set'
- test_done
-fi
-
test_expect_success 'setup' '
mkdir dir &&
echo parent > dir/foo &&
diff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh
index 771c3c3..8581ad3 100755
--- a/t/t2018-checkout-branch.sh
+++ b/t/t2018-checkout-branch.sh
@@ -3,6 +3,7 @@
test_description='checkout'
TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Arguments: [!] <branch> <oid> [<checkout options>]
diff --git a/t/t2025-checkout-no-overlay.sh b/t/t2025-checkout-no-overlay.sh
index 8f13341..3832c3d 100755
--- a/t/t2025-checkout-no-overlay.sh
+++ b/t/t2025-checkout-no-overlay.sh
@@ -2,6 +2,7 @@
test_description='checkout --no-overlay <tree-ish> -- <pathspec>'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t2401-worktree-prune.sh b/t/t2401-worktree-prune.sh
index 3d28c7f..568a47e 100755
--- a/t/t2401-worktree-prune.sh
+++ b/t/t2401-worktree-prune.sh
@@ -5,6 +5,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success initialize '
diff --git a/t/t2402-worktree-list.sh b/t/t2402-worktree-list.sh
index 79e0fce..9ad9be0 100755
--- a/t/t2402-worktree-list.sh
+++ b/t/t2402-worktree-list.sh
@@ -5,6 +5,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t2406-worktree-repair.sh b/t/t2406-worktree-repair.sh
index 5c44453..8970780 100755
--- a/t/t2406-worktree-repair.sh
+++ b/t/t2406-worktree-repair.sh
@@ -2,6 +2,7 @@
test_description='test git worktree repair'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t3009-ls-files-others-nonsubmodule.sh b/t/t3009-ls-files-others-nonsubmodule.sh
index 963f346..14218b3 100755
--- a/t/t3009-ls-files-others-nonsubmodule.sh
+++ b/t/t3009-ls-files-others-nonsubmodule.sh
@@ -18,6 +18,7 @@
git repository with a commit and an untracked file
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup: directories' '
diff --git a/t/t3010-ls-files-killed-modified.sh b/t/t3010-ls-files-killed-modified.sh
index 580e158..0541787 100755
--- a/t/t3010-ls-files-killed-modified.sh
+++ b/t/t3010-ls-files-killed-modified.sh
@@ -41,6 +41,8 @@
We should report path0, path1, path2/file2, path3/file3, path7 and path8
modified without reporting path9 and path10. submod1 is also modified.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'git update-index --add to add various paths.' '
diff --git a/t/t3050-subprojects-fetch.sh b/t/t3050-subprojects-fetch.sh
index f1f09ab..3884694 100755
--- a/t/t3050-subprojects-fetch.sh
+++ b/t/t3050-subprojects-fetch.sh
@@ -2,6 +2,7 @@
test_description='fetching and pushing project with subproject'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t3060-ls-files-with-tree.sh b/t/t3060-ls-files-with-tree.sh
index 52f76f7..c4a72ae 100755
--- a/t/t3060-ls-files-with-tree.sh
+++ b/t/t3060-ls-files-with-tree.sh
@@ -8,6 +8,8 @@
This test runs git ls-files --with-tree and in particular in
a scenario known to trigger a crash with some versions of git.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t3104-ls-tree-format.sh b/t/t3104-ls-tree-format.sh
index 3838966..3adb206 100755
--- a/t/t3104-ls-tree-format.sh
+++ b/t/t3104-ls-tree-format.sh
@@ -20,7 +20,6 @@
format=$1 &&
opts=$2 &&
fmtopts=$3 &&
- shift 2 &&
test_expect_success "ls-tree '--format=<$format>' is like options '$opts $fmtopts'" '
git ls-tree $opts -r HEAD >expect &&
@@ -36,6 +35,12 @@
'
}
+test_expect_success "ls-tree --format='%(path) %(path) %(path)' HEAD top-file" '
+ git ls-tree --format="%(path) %(path) %(path)" HEAD top-file.t >actual &&
+ echo top-file.t top-file.t top-file.t >expect &&
+ test_cmp expect actual
+'
+
test_ls_tree_format \
"%(objectmode) %(objecttype) %(objectname)%x09%(path)" \
""
diff --git a/t/t3204-branch-name-interpretation.sh b/t/t3204-branch-name-interpretation.sh
index 793bf4d..3399344 100755
--- a/t/t3204-branch-name-interpretation.sh
+++ b/t/t3204-branch-name-interpretation.sh
@@ -57,6 +57,16 @@
expect_branch refs/heads/refs/heads/qualified two
'
+test_expect_success 'force-copy a branch to itself via @{-1} is no-op' '
+ git branch -t copiable main &&
+ git checkout copiable &&
+ git checkout - &&
+ git branch -C @{-1} copiable &&
+ git config --get-all branch.copiable.merge >actual &&
+ echo refs/heads/main >expect &&
+ test_cmp expect actual
+'
+
test_expect_success 'delete branch via @{-1}' '
git branch previous-del &&
diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh
index 577f32d..07a0ff9 100755
--- a/t/t3210-pack-refs.sh
+++ b/t/t3210-pack-refs.sh
@@ -12,6 +12,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'enable reflogs' '
diff --git a/t/t3405-rebase-malformed.sh b/t/t3405-rebase-malformed.sh
index 2524331..8979bc3 100755
--- a/t/t3405-rebase-malformed.sh
+++ b/t/t3405-rebase-malformed.sh
@@ -5,6 +5,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
diff --git a/t/t3409-rebase-environ.sh b/t/t3409-rebase-environ.sh
index 83ffb39..acaf555 100755
--- a/t/t3409-rebase-environ.sh
+++ b/t/t3409-rebase-environ.sh
@@ -2,6 +2,7 @@
test_description='git rebase interactive environment'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh
index 58371d8..e75b3d0 100755
--- a/t/t3412-rebase-root.sh
+++ b/t/t3412-rebase-root.sh
@@ -7,6 +7,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
log_with_names () {
diff --git a/t/t3413-rebase-hook.sh b/t/t3413-rebase-hook.sh
index 9fab0d7..e845683 100755
--- a/t/t3413-rebase-hook.sh
+++ b/t/t3413-rebase-hook.sh
@@ -5,6 +5,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t3416-rebase-onto-threedots.sh b/t/t3416-rebase-onto-threedots.sh
index ea501f2..f8c4ed7 100755
--- a/t/t3416-rebase-onto-threedots.sh
+++ b/t/t3416-rebase-onto-threedots.sh
@@ -5,6 +5,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY/lib-rebase.sh"
diff --git a/t/t3419-rebase-patch-id.sh b/t/t3419-rebase-patch-id.sh
index 7181f17..6c61f24 100755
--- a/t/t3419-rebase-patch-id.sh
+++ b/t/t3419-rebase-patch-id.sh
@@ -5,6 +5,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
scramble () {
diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
index 6dabb05..4711b37 100755
--- a/t/t3422-rebase-incompatible-options.sh
+++ b/t/t3422-rebase-incompatible-options.sh
@@ -25,11 +25,11 @@
'
#
-# Rebase has lots of useful options like --whitepsace=fix, which are
-# actually all built in terms of flags to git-am. Since neither
-# --merge nor --interactive (nor any options that imply those two) use
-# git-am, using them together will result in flags like --whitespace=fix
-# being ignored. Make sure rebase warns the user and aborts instead.
+# Rebase has a couple options which are specific to the apply backend,
+# and several options which are specific to the merge backend. Flags
+# from the different sets cannot work together, and we do not want to
+# just ignore one of the sets of flags. Make sure rebase warns the
+# user and aborts instead.
#
test_rebase_am_only () {
@@ -50,6 +50,11 @@
test_must_fail git rebase $opt --strategy-option=ours A
"
+ test_expect_success "$opt incompatible with --autosquash" "
+ git checkout B^0 &&
+ test_must_fail git rebase $opt --autosquash A
+ "
+
test_expect_success "$opt incompatible with --interactive" "
git checkout B^0 &&
test_must_fail git rebase $opt --interactive A
@@ -60,9 +65,65 @@
test_must_fail git rebase $opt --exec 'true' A
"
+ test_expect_success "$opt incompatible with --keep-empty" "
+ git checkout B^0 &&
+ test_must_fail git rebase $opt --keep-empty A
+ "
+
+ test_expect_success "$opt incompatible with --empty=..." "
+ git checkout B^0 &&
+ test_must_fail git rebase $opt --empty=ask A
+ "
+
+ test_expect_success "$opt incompatible with --no-reapply-cherry-picks" "
+ git checkout B^0 &&
+ test_must_fail git rebase $opt --no-reapply-cherry-picks A
+ "
+
+ test_expect_success "$opt incompatible with --reapply-cherry-picks" "
+ git checkout B^0 &&
+ test_must_fail git rebase $opt --reapply-cherry-picks A
+ "
+
+ test_expect_success "$opt incompatible with --update-refs" "
+ git checkout B^0 &&
+ test_must_fail git rebase $opt --update-refs A
+ "
+
+ test_expect_success "$opt incompatible with --root without --onto" "
+ git checkout B^0 &&
+ test_must_fail git rebase $opt --root A
+ "
+
+ test_expect_success "$opt incompatible with rebase.autosquash" "
+ git checkout B^0 &&
+ test_must_fail git -c rebase.autosquash=true rebase $opt A 2>err &&
+ grep -e --no-autosquash err
+ "
+
+ test_expect_success "$opt incompatible with rebase.updateRefs" "
+ git checkout B^0 &&
+ test_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&
+ grep -e --no-update-refs err
+ "
+
+ test_expect_success "$opt okay with overridden rebase.autosquash" "
+ test_when_finished \"git reset --hard B^0\" &&
+ git checkout B^0 &&
+ git -c rebase.autosquash=true rebase --no-autosquash $opt A
+ "
+
+ test_expect_success "$opt okay with overridden rebase.updateRefs" "
+ test_when_finished \"git reset --hard B^0\" &&
+ git checkout B^0 &&
+ git -c rebase.updateRefs=true rebase --no-update-refs $opt A
+ "
}
+# Check options which imply --apply
test_rebase_am_only --whitespace=fix
test_rebase_am_only -C4
+# Also check an explicit --apply
+test_rebase_am_only --apply
test_done
diff --git a/t/t3423-rebase-reword.sh b/t/t3423-rebase-reword.sh
index 4859bb8..2fab703 100755
--- a/t/t3423-rebase-reword.sh
+++ b/t/t3423-rebase-reword.sh
@@ -2,6 +2,7 @@
test_description='git rebase interactive with rewording'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
diff --git a/t/t3425-rebase-topology-merges.sh b/t/t3425-rebase-topology-merges.sh
index 63acc1e..a16428b 100755
--- a/t/t3425-rebase-topology-merges.sh
+++ b/t/t3425-rebase-topology-merges.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='rebase topology tests with merges'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
diff --git a/t/t3428-rebase-signoff.sh b/t/t3428-rebase-signoff.sh
index f6993b7..e1b1e94 100755
--- a/t/t3428-rebase-signoff.sh
+++ b/t/t3428-rebase-signoff.sh
@@ -5,6 +5,7 @@
This test runs git rebase --signoff and make sure that it works.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# A simple file to commit
diff --git a/t/t3429-rebase-edit-todo.sh b/t/t3429-rebase-edit-todo.sh
index abd66f3..8e0d039 100755
--- a/t/t3429-rebase-edit-todo.sh
+++ b/t/t3429-rebase-edit-todo.sh
@@ -2,6 +2,7 @@
test_description='rebase should reread the todo file if an exec modifies it'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
diff --git a/t/t3431-rebase-fork-point.sh b/t/t3431-rebase-fork-point.sh
index 70e8136..4bfc779 100755
--- a/t/t3431-rebase-fork-point.sh
+++ b/t/t3431-rebase-fork-point.sh
@@ -8,6 +8,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# A---B---D---E (main)
diff --git a/t/t3432-rebase-fast-forward.sh b/t/t3432-rebase-fast-forward.sh
index 5086e14..7f1a5dd 100755
--- a/t/t3432-rebase-fast-forward.sh
+++ b/t/t3432-rebase-fast-forward.sh
@@ -8,6 +8,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t3433-rebase-across-mode-change.sh b/t/t3433-rebase-across-mode-change.sh
index 05df964..c8172b0 100755
--- a/t/t3433-rebase-across-mode-change.sh
+++ b/t/t3433-rebase-across-mode-change.sh
@@ -2,6 +2,7 @@
test_description='git rebase across mode change'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t3437-rebase-fixup-options.sh b/t/t3437-rebase-fixup-options.sh
index c023fef..274699d 100755
--- a/t/t3437-rebase-fixup-options.sh
+++ b/t/t3437-rebase-fixup-options.sh
@@ -14,6 +14,7 @@
"amend!" upon --autosquash.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
diff --git a/t/t3438-rebase-broken-files.sh b/t/t3438-rebase-broken-files.sh
index b92a3ce..c614c4f 100755
--- a/t/t3438-rebase-broken-files.sh
+++ b/t/t3438-rebase-broken-files.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='rebase behavior when on-disk files are broken'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'set up conflicting branches' '
diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh
index 1f4cfc3..2f3e3e2 100755
--- a/t/t3501-revert-cherry-pick.sh
+++ b/t/t3501-revert-cherry-pick.sh
@@ -13,6 +13,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t3502-cherry-pick-merge.sh b/t/t3502-cherry-pick-merge.sh
index 5495eac..1b2c0d6 100755
--- a/t/t3502-cherry-pick-merge.sh
+++ b/t/t3502-cherry-pick-merge.sh
@@ -11,6 +11,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t3503-cherry-pick-root.sh b/t/t3503-cherry-pick-root.sh
index 95fe4fe..76d393d 100755
--- a/t/t3503-cherry-pick-root.sh
+++ b/t/t3503-cherry-pick-root.sh
@@ -5,6 +5,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t3506-cherry-pick-ff.sh b/t/t3506-cherry-pick-ff.sh
index 7e11bd4..b71bad1 100755
--- a/t/t3506-cherry-pick-ff.sh
+++ b/t/t3506-cherry-pick-ff.sh
@@ -5,6 +5,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t3511-cherry-pick-x.sh b/t/t3511-cherry-pick-x.sh
index 84a587d..dd5d92e 100755
--- a/t/t3511-cherry-pick-x.sh
+++ b/t/t3511-cherry-pick-x.sh
@@ -2,6 +2,7 @@
test_description='Test cherry-pick -x and -s'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
pristine_detach () {
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 5841f28..3a99837 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -7,7 +7,7 @@
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
-if test_have_prereq !ADD_I_USE_BUILTIN,!PERL
+if test_have_prereq !PERL
then
skip_all='skipping add -i (scripted) tests, perl not available'
test_done
@@ -46,6 +46,21 @@
)
}
+test_expect_success 'warn about add.interactive.useBuiltin' '
+ cat >expect <<-\EOF &&
+ warning: the add.interactive.useBuiltin setting has been removed!
+ See its entry in '\''git help config'\'' for details.
+ No changes.
+ EOF
+
+ for v in = =true =false
+ do
+ git -c "add.interactive.useBuiltin$v" add -p >out 2>actual &&
+ test_must_be_empty out &&
+ test_cmp expect actual || return 1
+ done
+'
+
test_expect_success 'setup (initial)' '
echo content >file &&
git add file &&
@@ -547,15 +562,7 @@
! grep "^+15" actual
'
-test_expect_success 'setup ADD_I_USE_BUILTIN check' '
- result=success &&
- if ! test_have_prereq ADD_I_USE_BUILTIN
- then
- result=failure
- fi
-'
-
-test_expect_$result 'split hunk "add -p (no, yes, edit)"' '
+test_expect_success 'split hunk "add -p (no, yes, edit)"' '
test_write_lines 5 10 20 21 30 31 40 50 60 >test &&
git reset &&
# test sequence is s(plit), n(o), y(es), e(dit)
@@ -579,7 +586,7 @@
test_must_fail git grep --cached before
'
-test_expect_$result 'edit, adding lines to the first hunk' '
+test_expect_success 'edit, adding lines to the first hunk' '
test_write_lines 10 11 20 30 40 50 51 60 >test &&
git reset &&
tr _ " " >patch <<-EOF &&
diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh
index e3cf0ff..d3e428f 100755
--- a/t/t3800-mktag.sh
+++ b/t/t3800-mktag.sh
@@ -4,6 +4,7 @@
test_description='git mktag: tag object verify test'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
###########################################################
diff --git a/t/t3920-crlf-messages.sh b/t/t3920-crlf-messages.sh
index 4c661d4..67fd234 100755
--- a/t/t3920-crlf-messages.sh
+++ b/t/t3920-crlf-messages.sh
@@ -12,7 +12,7 @@
cat >.crlf-orig-$branch.txt &&
cat .crlf-orig-$branch.txt | append_cr >.crlf-message-$branch.txt &&
grep 'Subject' .crlf-orig-$branch.txt | tr '\n' ' ' | sed 's/[ ]*$//' | tr -d '\n' >.crlf-subject-$branch.txt &&
- grep 'Body' .crlf-message-$branch.txt >.crlf-body-$branch.txt || true &&
+ grep 'Body' .crlf-orig-$branch.txt | append_cr >.crlf-body-$branch.txt &&
LIB_CRLF_BRANCHES="${LIB_CRLF_BRANCHES} ${branch}" &&
test_tick &&
hash=$(git commit-tree HEAD^{tree} -p HEAD -F .crlf-message-${branch}.txt) &&
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index de1da46..012f155 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -2281,7 +2281,7 @@
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 @@
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/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index f3e20dd..b298f22 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -1638,7 +1638,7 @@
test_cmp expect actual
'
-test_expect_success !SANITIZE_LEAK 'no effect on show from --color-moved with --word-diff' '
+test_expect_success 'no effect on show from --color-moved with --word-diff' '
git show --color-moved --word-diff >actual &&
git show --word-diff >expect &&
test_cmp expect actual
@@ -2024,7 +2024,7 @@
test_cmp expected actual
'
-test_expect_success !SANITIZE_LEAK 'move detection with submodules' '
+test_expect_success 'move detection with submodules' '
test_create_repo bananas &&
echo ripe >bananas/recipe &&
git -C bananas add recipe &&
diff --git a/t/t4018/java-class-brace-on-separate-line b/t/t4018/java-class-brace-on-separate-line
new file mode 100644
index 0000000..8795acd
--- /dev/null
+++ b/t/t4018/java-class-brace-on-separate-line
@@ -0,0 +1,6 @@
+class RIGHT
+{
+ static int ONE;
+ static int TWO;
+ static int ChangeMe;
+}
diff --git a/t/t4018/java-class-space-before-type-parameters b/t/t4018/java-class-space-before-type-parameters
new file mode 100644
index 0000000..0bdef1d
--- /dev/null
+++ b/t/t4018/java-class-space-before-type-parameters
@@ -0,0 +1,6 @@
+class RIGHT <TYPE, PARAMS, AFTER, SPACE> {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ private A ChangeMe;
+}
diff --git a/t/t4018/java-class-type-parameters b/t/t4018/java-class-type-parameters
new file mode 100644
index 0000000..579aa7a
--- /dev/null
+++ b/t/t4018/java-class-type-parameters
@@ -0,0 +1,6 @@
+class RIGHT<A, B> {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ private A ChangeMe;
+}
diff --git a/t/t4018/java-class-type-parameters-implements b/t/t4018/java-class-type-parameters-implements
new file mode 100644
index 0000000..b8038b1
--- /dev/null
+++ b/t/t4018/java-class-type-parameters-implements
@@ -0,0 +1,6 @@
+class RIGHT<A, B> implements List<A> {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ private A ChangeMe;
+}
diff --git a/t/t4018/java-interface-type-parameters b/t/t4018/java-interface-type-parameters
new file mode 100644
index 0000000..a4baa1a
--- /dev/null
+++ b/t/t4018/java-interface-type-parameters
@@ -0,0 +1,6 @@
+interface RIGHT<A, B> {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ public B foo(A ChangeMe);
+}
diff --git a/t/t4018/java-interface-type-parameters-extends b/t/t4018/java-interface-type-parameters-extends
new file mode 100644
index 0000000..31d7fb3
--- /dev/null
+++ b/t/t4018/java-interface-type-parameters-extends
@@ -0,0 +1,6 @@
+interface RIGHT<A, B> extends Function<A, B> {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ public B foo(A ChangeMe);
+}
diff --git a/t/t4018/java-non-sealed b/t/t4018/java-non-sealed
new file mode 100644
index 0000000..069087c
--- /dev/null
+++ b/t/t4018/java-non-sealed
@@ -0,0 +1,8 @@
+public abstract sealed class SealedClass {
+ public static non-sealed class RIGHT extends SealedClass {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ private int ChangeMe;
+ }
+}
diff --git a/t/t4018/java-record b/t/t4018/java-record
new file mode 100644
index 0000000..97aa819
--- /dev/null
+++ b/t/t4018/java-record
@@ -0,0 +1,6 @@
+public record RIGHT(int comp1, double comp2, String comp3) {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ static int ChangeMe;
+}
diff --git a/t/t4018/java-record-space-before-components b/t/t4018/java-record-space-before-components
new file mode 100644
index 0000000..9827f22
--- /dev/null
+++ b/t/t4018/java-record-space-before-components
@@ -0,0 +1,6 @@
+public record RIGHT (String components, String after, String space) {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ static int ChangeMe;
+}
diff --git a/t/t4018/java-record-type-parameters b/t/t4018/java-record-type-parameters
new file mode 100644
index 0000000..f62a035
--- /dev/null
+++ b/t/t4018/java-record-type-parameters
@@ -0,0 +1,6 @@
+public record RIGHT<A, N extends Number>(A comp1, N comp2, int comp3) {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ static int ChangeMe;
+}
diff --git a/t/t4018/java-sealed b/t/t4018/java-sealed
new file mode 100644
index 0000000..785fbc6
--- /dev/null
+++ b/t/t4018/java-sealed
@@ -0,0 +1,7 @@
+public abstract sealed class Sealed { // RIGHT
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ public final class ChangeMe extends Sealed {
+ }
+}
diff --git a/t/t4018/java-sealed-permits b/t/t4018/java-sealed-permits
new file mode 100644
index 0000000..18dd489
--- /dev/null
+++ b/t/t4018/java-sealed-permits
@@ -0,0 +1,6 @@
+public abstract sealed class RIGHT permits PermittedA, PermittedB {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ private int ChangeMe;
+}
diff --git a/t/t4018/java-sealed-type-parameters b/t/t4018/java-sealed-type-parameters
new file mode 100644
index 0000000..e6530c4
--- /dev/null
+++ b/t/t4018/java-sealed-type-parameters
@@ -0,0 +1,6 @@
+public abstract sealed class RIGHT<A, B> {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ private int ChangeMe;
+}
diff --git a/t/t4018/java-sealed-type-parameters-implements-permits b/t/t4018/java-sealed-type-parameters-implements-permits
new file mode 100644
index 0000000..bd6e6d3
--- /dev/null
+++ b/t/t4018/java-sealed-type-parameters-implements-permits
@@ -0,0 +1,6 @@
+public abstract sealed class RIGHT<A, B> implements List<A> permits PermittedA, PermittedB {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ private int ChangeMe;
+}
diff --git a/t/t4018/java-sealed-type-parameters-permits b/t/t4018/java-sealed-type-parameters-permits
new file mode 100644
index 0000000..25a0da6
--- /dev/null
+++ b/t/t4018/java-sealed-type-parameters-permits
@@ -0,0 +1,6 @@
+public abstract sealed class RIGHT<A, B> permits PermittedA, PermittedB {
+ static int ONE;
+ static int TWO;
+ static int THREE;
+ private int ChangeMe;
+}
diff --git a/t/t4023-diff-rename-typechange.sh b/t/t4023-diff-rename-typechange.sh
index 7cb9909..787605c 100755
--- a/t/t4023-diff-rename-typechange.sh
+++ b/t/t4023-diff-rename-typechange.sh
@@ -52,8 +52,8 @@
'
test_expect_success 'cross renames to be detected for regular files' '
-
- git diff-tree five six -r --name-status -B -M | sort >actual &&
+ git diff-tree five six -r --name-status -B -M >out &&
+ sort out >actual &&
{
echo "R100 foo bar" &&
echo "R100 bar foo"
@@ -63,8 +63,8 @@
'
test_expect_success 'cross renames to be detected for typechange' '
-
- git diff-tree one two -r --name-status -B -M | sort >actual &&
+ git diff-tree one two -r --name-status -B -M >out &&
+ sort out >actual &&
{
echo "R100 foo bar" &&
echo "R100 bar foo"
@@ -74,8 +74,8 @@
'
test_expect_success 'moves and renames' '
-
- git diff-tree three four -r --name-status -B -M | sort >actual &&
+ git diff-tree three four -r --name-status -B -M >out &&
+ sort out >actual &&
{
# see -B -M (#6) in t4008
echo "C100 foo bar" &&
diff --git a/t/t4044-diff-index-unique-abbrev.sh b/t/t4044-diff-index-unique-abbrev.sh
index 29e49d2..9f6043d 100755
--- a/t/t4044-diff-index-unique-abbrev.sh
+++ b/t/t4044-diff-index-unique-abbrev.sh
@@ -34,12 +34,12 @@
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 fab351b..9b46c4c 100755
--- a/t/t4045-diff-relative.sh
+++ b/t/t4045-diff-relative.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='diff --relative tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -162,6 +164,35 @@
check_diff_relative_option . 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/t4046-diff-unmerged.sh b/t/t4046-diff-unmerged.sh
index 0ae0cd3..ffaf693 100755
--- a/t/t4046-diff-unmerged.sh
+++ b/t/t4046-diff-unmerged.sh
@@ -86,4 +86,14 @@
test_cmp diff-files-3.expect diff-files-3.actual
'
+test_expect_success 'diff --stat' '
+ for path in $paths
+ do
+ echo " $path | Unmerged" || return 1
+ done >diff-stat.expect &&
+ echo " 0 files changed" >>diff-stat.expect &&
+ git diff --cached --stat >diff-stat.actual &&
+ test_cmp diff-stat.expect diff-stat.actual
+'
+
test_done
diff --git a/t/t4052-stat-output.sh b/t/t4052-stat-output.sh
index b5c281e..3ee27e2 100755
--- a/t/t4052-stat-output.sh
+++ b/t/t4052-stat-output.sh
@@ -8,6 +8,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh
index 3feadf0..4e9fa04 100755
--- a/t/t4053-diff-no-index.sh
+++ b/t/t4053-diff-no-index.sh
@@ -2,6 +2,7 @@
test_description='diff --no-index'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t4054-diff-bogus-tree.sh b/t/t4054-diff-bogus-tree.sh
index 294fb55..05c88f8 100755
--- a/t/t4054-diff-bogus-tree.sh
+++ b/t/t4054-diff-bogus-tree.sh
@@ -10,7 +10,7 @@
bogus_tree=$(
printf "100644 fooQ$name" |
q_to_nul |
- git hash-object -w --stdin -t tree
+ git hash-object --literally -w --stdin -t tree
)
'
diff --git a/t/t4058-diff-duplicates.sh b/t/t4058-diff-duplicates.sh
index 54614b8..2501c89 100755
--- a/t/t4058-diff-duplicates.sh
+++ b/t/t4058-diff-duplicates.sh
@@ -29,7 +29,7 @@
make_tree_entry "$1" "$2" "$3"
shift; shift; shift
done |
- git hash-object -w -t tree --stdin
+ git hash-object --literally -w -t tree --stdin
}
# this is kind of a convoluted setup, but matches
diff --git a/t/t4067-diff-partial-clone.sh b/t/t4067-diff-partial-clone.sh
index 28f42a4..f60f5cb 100755
--- a/t/t4067-diff-partial-clone.sh
+++ b/t/t4067-diff-partial-clone.sh
@@ -2,6 +2,7 @@
test_description='behavior of diff when reading objects in a partial clone'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'git show batches blobs' '
diff --git a/t/t4111-apply-subdir.sh b/t/t4111-apply-subdir.sh
index 1618a6d..e9a87d7 100755
--- a/t/t4111-apply-subdir.sh
+++ b/t/t4111-apply-subdir.sh
@@ -2,6 +2,7 @@
test_description='patching from inconvenient places'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t4115-apply-symlink.sh b/t/t4115-apply-symlink.sh
index d0f3ede..65ac7df 100755
--- a/t/t4115-apply-symlink.sh
+++ b/t/t4115-apply-symlink.sh
@@ -45,4 +45,85 @@
'
+test_expect_success 'symlink setup' '
+ ln -s .git symlink &&
+ git add symlink &&
+ git commit -m "add symlink"
+'
+
+test_expect_success SYMLINKS 'symlink escape when creating new files' '
+ test_when_finished "git reset --hard && git clean -dfx" &&
+
+ cat >patch <<-EOF &&
+ diff --git a/symlink b/renamed-symlink
+ similarity index 100%
+ rename from symlink
+ rename to renamed-symlink
+ --
+ diff --git /dev/null b/renamed-symlink/create-me
+ new file mode 100644
+ index 0000000..039727e
+ --- /dev/null
+ +++ b/renamed-symlink/create-me
+ @@ -0,0 +1,1 @@
+ +busted
+ EOF
+
+ test_must_fail git apply patch 2>stderr &&
+ cat >expected_stderr <<-EOF &&
+ error: affected file ${SQ}renamed-symlink/create-me${SQ} is beyond a symbolic link
+ EOF
+ test_cmp expected_stderr stderr &&
+ ! test_path_exists .git/create-me
+'
+
+test_expect_success SYMLINKS 'symlink escape when modifying file' '
+ test_when_finished "git reset --hard && git clean -dfx" &&
+ touch .git/modify-me &&
+
+ cat >patch <<-EOF &&
+ diff --git a/symlink b/renamed-symlink
+ similarity index 100%
+ rename from symlink
+ rename to renamed-symlink
+ --
+ diff --git a/renamed-symlink/modify-me b/renamed-symlink/modify-me
+ index 1111111..2222222 100644
+ --- a/renamed-symlink/modify-me
+ +++ b/renamed-symlink/modify-me
+ @@ -0,0 +1,1 @@
+ +busted
+ EOF
+
+ test_must_fail git apply patch 2>stderr &&
+ cat >expected_stderr <<-EOF &&
+ error: renamed-symlink/modify-me: No such file or directory
+ EOF
+ test_cmp expected_stderr stderr &&
+ test_must_be_empty .git/modify-me
+'
+
+test_expect_success SYMLINKS 'symlink escape when deleting file' '
+ test_when_finished "git reset --hard && git clean -dfx && rm .git/delete-me" &&
+ touch .git/delete-me &&
+
+ cat >patch <<-EOF &&
+ diff --git a/symlink b/renamed-symlink
+ similarity index 100%
+ rename from symlink
+ rename to renamed-symlink
+ --
+ diff --git a/renamed-symlink/delete-me b/renamed-symlink/delete-me
+ deleted file mode 100644
+ index 1111111..0000000 100644
+ EOF
+
+ test_must_fail git apply patch 2>stderr &&
+ cat >expected_stderr <<-EOF &&
+ error: renamed-symlink/delete-me: No such file or directory
+ EOF
+ test_cmp expected_stderr stderr &&
+ test_path_is_file .git/delete-me
+'
+
test_done
diff --git a/t/t4135-apply-weird-filenames.sh b/t/t4135-apply-weird-filenames.sh
index 6bc3fb9..d3502c6 100755
--- a/t/t4135-apply-weird-filenames.sh
+++ b/t/t4135-apply-weird-filenames.sh
@@ -2,6 +2,7 @@
test_description='git apply with weird postimage filenames'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index cdad4b6..78cf1c8 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -345,6 +345,21 @@
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_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 @@
>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/t4152-am-subjects.sh b/t/t4152-am-subjects.sh
index 4c68245..9f2edba 100755
--- a/t/t4152-am-subjects.sh
+++ b/t/t4152-am-subjects.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test subject preservation with format-patch | am'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
make_patches() {
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
index cd1cab3..fa7f987 100755
--- a/t/t4203-mailmap.sh
+++ b/t/t4203-mailmap.sh
@@ -1022,4 +1022,69 @@
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 e448ef2..4cf8a77 100755
--- a/t/t4205-log-pretty-formats.sh
+++ b/t/t4205-log-pretty-formats.sh
@@ -156,7 +156,7 @@
for r in $revs
do
git show -s --pretty=oneline "$r" >raw &&
- cat raw | lf_to_nul || exit 1
+ cat raw | lf_to_nul || return 1
done >expect &&
# the trailing NUL is already produced so we do not need to
# output another one
@@ -1018,4 +1018,107 @@
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
+'
+
+# pretty-formats note wide char limitations, and add tests
+test_expect_failure 'wide and decomposed characters column counting' '
+
+# from t/lib-unicode-nfc-nfd.sh hex values converted to octal
+ utf8_nfc=$(printf "\303\251") && # e acute combined.
+ utf8_nfd=$(printf "\145\314\201") && # e with a combining acute (i.e. decomposed)
+ utf8_emoji=$(printf "\360\237\221\250") &&
+
+# replacement character when requesting a wide char fits in a single display colum.
+# "half wide" alternative could be a plain ASCII dot `.`
+ utf8_vert_ell=$(printf "\342\213\256") &&
+
+# use ${xxx} here!
+ nfc10="${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}${utf8_nfc}" &&
+ nfd10="${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}${utf8_nfd}" &&
+ emoji5="${utf8_emoji}${utf8_emoji}${utf8_emoji}${utf8_emoji}${utf8_emoji}" &&
+# emoji5 uses 10 display columns
+
+ test_commit "abcdefghij" &&
+ test_commit --no-tag "${nfc10}" &&
+ test_commit --no-tag "${nfd10}" &&
+ test_commit --no-tag "${emoji5}" &&
+ printf "${utf8_emoji}..${utf8_emoji}${utf8_vert_ell}\n${utf8_nfd}..${utf8_nfd}${utf8_nfd}\n${utf8_nfc}..${utf8_nfc}${utf8_nfc}\na..ij\n" >expected &&
+ git log --format="%<(5,mtrunc)%s" -4 >actual &&
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh
index ac9e4d0..c6540e8 100755
--- a/t/t4211-line-log.sh
+++ b/t/t4211-line-log.sh
@@ -315,4 +315,26 @@
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/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh
index 30a2198..e89e1f5 100755
--- a/t/t4212-log-corrupt.sh
+++ b/t/t4212-log-corrupt.sh
@@ -10,7 +10,7 @@
git cat-file commit HEAD |
sed "/^author /s/>/>-<>/" >broken_email.commit &&
- git hash-object -w -t commit broken_email.commit >broken_email.hash &&
+ git hash-object --literally -w -t commit broken_email.commit >broken_email.hash &&
git update-ref refs/heads/broken_email $(cat broken_email.hash)
'
@@ -46,7 +46,7 @@
munge_author_date () {
git cat-file commit "$1" >commit.orig &&
sed "s/^\(author .*>\) [0-9]*/\1 $2/" <commit.orig >commit.munge &&
- git hash-object -w -t commit commit.munge
+ git hash-object --literally -w -t commit commit.munge
}
test_expect_success 'unparsable dates produce sentinel value' '
diff --git a/t/t4213-log-tabexpand.sh b/t/t4213-log-tabexpand.sh
index 53a4af3..590fce9 100755
--- a/t/t4213-log-tabexpand.sh
+++ b/t/t4213-log-tabexpand.sh
@@ -2,6 +2,7 @@
test_description='log/show --expand-tabs'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
HT=" "
diff --git a/t/t4254-am-corrupt.sh b/t/t4254-am-corrupt.sh
index 54be7da..45f1d4f 100755
--- a/t/t4254-am-corrupt.sh
+++ b/t/t4254-am-corrupt.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='git am with corrupt input'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
make_mbox_with_nul () {
diff --git a/t/t4256-am-format-flowed.sh b/t/t4256-am-format-flowed.sh
index 2369c4e..1015273 100755
--- a/t/t4256-am-format-flowed.sh
+++ b/t/t4256-am-format-flowed.sh
@@ -2,6 +2,7 @@
test_description='test format=flowed support of git am'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t4257-am-interactive.sh b/t/t4257-am-interactive.sh
index aed8f4d..f26d7fd 100755
--- a/t/t4257-am-interactive.sh
+++ b/t/t4257-am-interactive.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='am --interactive tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'set up patches to apply' '
diff --git a/t/t4301-merge-tree-write-tree.sh b/t/t4301-merge-tree-write-tree.sh
index cac8559..250f721 100755
--- a/t/t4301-merge-tree-write-tree.sh
+++ b/t/t4301-merge-tree-write-tree.sh
@@ -141,7 +141,7 @@
# 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 @@
# Commit O: foo, olddir/{a,b,c}
# Commit A: delete foo, rename olddir/ -> newdir/, add newdir/bar/file
# Commit B: modify foo & rename foo -> olddir/bar
-# Expected: CONFLICT(content) for for newdir/bar (not olddir/bar or foo)
+# Expected: CONFLICT(content) for newdir/bar (not olddir/bar or foo)
test_expect_success 'directory rename + rename/delete + modify/delete + directory/file conflict' '
# Setup
@@ -860,4 +860,66 @@
test_cmp expect actual
'
+
+test_expect_success '--merge-base is incompatible with --stdin' '
+ test_must_fail git merge-tree --merge-base=side1 --stdin 2>expect &&
+
+ grep "^fatal: --merge-base is incompatible with --stdin" expect
+'
+
+# specify merge-base as parent of branch2
+# git merge-tree --write-tree --merge-base=c2 c1 c3
+# Commit c1: add file1
+# Commit c2: add file2 after c1
+# Commit c3: add file3 after c2
+# Expected: add file3, and file2 does NOT appear
+
+test_expect_success 'specify merge-base as parent of branch2' '
+ # Setup
+ test_when_finished "rm -rf base-b2-p" &&
+ git init base-b2-p &&
+ test_commit -C base-b2-p c1 file1 &&
+ test_commit -C base-b2-p c2 file2 &&
+ test_commit -C base-b2-p c3 file3 &&
+
+ # Testing
+ TREE_OID=$(git -C base-b2-p merge-tree --write-tree --merge-base=c2 c1 c3) &&
+
+ q_to_tab <<-EOF >expect &&
+ 100644 blob $(git -C base-b2-p rev-parse c1:file1)Qfile1
+ 100644 blob $(git -C base-b2-p rev-parse c3:file3)Qfile3
+ EOF
+
+ git -C base-b2-p ls-tree $TREE_OID >actual &&
+ test_cmp expect actual
+'
+
+# Since the earlier tests have verified that individual merge-tree calls
+# are doing the right thing, this test case is only used to verify that
+# we can also trigger merges via --stdin, and that when we do we get
+# the same answer as running a bunch of separate merges.
+
+test_expect_success 'check the input format when --stdin is passed' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo c1 &&
+ test_commit -C repo c2 &&
+ test_commit -C repo c3 &&
+ printf "c1 c3\nc2 -- c1 c3\nc2 c3" | git -C repo merge-tree --stdin >actual &&
+
+ printf "1\0" >expect &&
+ git -C repo merge-tree --write-tree -z c1 c3 >>expect &&
+ printf "\0" >>expect &&
+
+ printf "1\0" >>expect &&
+ git -C repo merge-tree --write-tree -z --merge-base=c2 c1 c3 >>expect &&
+ printf "\0" >>expect &&
+
+ printf "1\0" >>expect &&
+ git -C repo merge-tree --write-tree -z c2 c3 >>expect &&
+ printf "\0" >>expect &&
+
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index d473048..eb3214b 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -402,11 +402,11 @@
test_expect_success 'archive and :(glob)' '
git archive -v HEAD -- ":(glob)**/sh" >/dev/null 2>actual &&
- cat >expect <<EOF &&
-a/
-a/bin/
-a/bin/sh
-EOF
+ cat >expect <<-\EOF &&
+ a/
+ a/bin/
+ a/bin/sh
+ EOF
test_cmp expect actual
'
diff --git a/t/t5001-archive-attr.sh b/t/t5001-archive-attr.sh
index 2f6eef5..04d300e 100755
--- a/t/t5001-archive-attr.sh
+++ b/t/t5001-archive-attr.sh
@@ -3,6 +3,7 @@
test_description='git archive attribute tests'
TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
SUBSTFORMAT='%H (%h)%n'
diff --git a/t/t5004-archive-corner-cases.sh b/t/t5004-archive-corner-cases.sh
index ae508e2..9f2c6da 100755
--- a/t/t5004-archive-corner-cases.sh
+++ b/t/t5004-archive-corner-cases.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test corner cases of git-archive'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# the 10knuls.tar file is used to test for an empty git generated tar
diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh
index b0095ab..f89809b 100755
--- a/t/t5302-pack-index.sh
+++ b/t/t5302-pack-index.sh
@@ -4,6 +4,8 @@
#
test_description='pack index with 64-bit offsets and object CRC'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -263,7 +265,7 @@
This is an invalid tag.
EOF
- tag=$(git hash-object -t tag -w --stdin <wrong-tag) &&
+ tag=$(git hash-object -t tag -w --stdin --literally <wrong-tag) &&
pack1=$(echo $tag $sha | git pack-objects tag-test) &&
echo remove tag object &&
thirtyeight=${tag#??} &&
diff --git a/t/t5306-pack-nobase.sh b/t/t5306-pack-nobase.sh
index 51973f4..846c5ca 100755
--- a/t/t5306-pack-nobase.sh
+++ b/t/t5306-pack-nobase.sh
@@ -6,6 +6,8 @@
test_description='git-pack-object with missing base
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Create A-B chain
diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh
index 6d693ee..7d8dee4 100755
--- a/t/t5310-pack-bitmaps.sh
+++ b/t/t5310-pack-bitmaps.sh
@@ -428,8 +428,9 @@
test_line_count = 2 packs &&
test_line_count = 2 bitmaps &&
- git rev-list --use-bitmap-index HEAD 2>err &&
- grep "ignoring extra bitmap file" err
+ GIT_TRACE2_EVENT=$(pwd)/trace2.txt git rev-list --use-bitmap-index HEAD &&
+ grep "opened bitmap" trace2.txt &&
+ grep "ignoring extra bitmap" trace2.txt
)
'
}
diff --git a/t/t5312-prune-corruption.sh b/t/t5312-prune-corruption.sh
index 9d8e249..230cb38 100755
--- a/t/t5312-prune-corruption.sh
+++ b/t/t5312-prune-corruption.sh
@@ -14,6 +14,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'disable reflogs' '
diff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh
index cc4cfaa..ceaa670 100755
--- a/t/t5313-pack-bounds-checks.sh
+++ b/t/t5313-pack-bounds-checks.sh
@@ -59,7 +59,7 @@
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/t5314-pack-cycle-detection.sh b/t/t5314-pack-cycle-detection.sh
index 73a2417..82734b9 100755
--- a/t/t5314-pack-cycle-detection.sh
+++ b/t/t5314-pack-cycle-detection.sh
@@ -63,13 +63,16 @@
# Note that the two variants of "file" must be similar enough to convince git
# to create the delta.
make_pack () {
- {
- printf '%s\n' "-$(git rev-parse $2)"
- printf '%s dummy\n' "$(git rev-parse $1:dummy)"
- printf '%s file\n' "$(git rev-parse $1:file)"
- } |
- git pack-objects --stdout |
- git index-pack --stdin --fix-thin
+ ln1=$(git rev-parse "$2") &&
+ ln2=$(git rev-parse "$1:dummy") &&
+ ln3=$(git rev-parse "$1:file") &&
+ cat >list <<-EOF
+ -$ln1
+ $ln2 dummy
+ $ln3 file
+ EOF
+ git pack-objects --stdout <list >pack &&
+ git index-pack --stdin --fix-thin <pack
}
test_expect_success 'setup' '
diff --git a/t/t5317-pack-objects-filter-objects.sh b/t/t5317-pack-objects-filter-objects.sh
index bb633c9..b26d476 100755
--- a/t/t5317-pack-objects-filter-objects.sh
+++ b/t/t5317-pack-objects-filter-objects.sh
@@ -5,6 +5,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Test blob:none filter.
@@ -24,8 +25,9 @@
}
test_expect_success 'verify blob count in normal packfile' '
- git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 |
- test_parse_ls_files_stage_oids |
+ git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 \
+ >ls_files_result &&
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
git -C r1 pack-objects --revs --stdout >all.pack <<-EOF &&
@@ -123,8 +125,8 @@
'
test_expect_success 'verify blob count in normal packfile' '
- git -C r2 ls-files -s large.1000 large.10000 |
- test_parse_ls_files_stage_oids |
+ git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
git -C r2 pack-objects --revs --stdout >all.pack <<-EOF &&
@@ -161,8 +163,8 @@
'
test_expect_success 'verify blob:limit=1001' '
- git -C r2 ls-files -s large.1000 |
- test_parse_ls_files_stage_oids |
+ git -C r2 ls-files -s large.1000 >ls_files_result &&
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
git -C r2 pack-objects --revs --stdout --filter=blob:limit=1001 >filter.pack <<-EOF &&
@@ -179,8 +181,8 @@
'
test_expect_success 'verify blob:limit=10001' '
- git -C r2 ls-files -s large.1000 large.10000 |
- test_parse_ls_files_stage_oids |
+ git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
git -C r2 pack-objects --revs --stdout --filter=blob:limit=10001 >filter.pack <<-EOF &&
@@ -197,8 +199,8 @@
'
test_expect_success 'verify blob:limit=1k' '
- git -C r2 ls-files -s large.1000 |
- test_parse_ls_files_stage_oids |
+ git -C r2 ls-files -s large.1000 >ls_files_result &&
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
git -C r2 pack-objects --revs --stdout --filter=blob:limit=1k >filter.pack <<-EOF &&
@@ -215,8 +217,8 @@
'
test_expect_success 'verify explicitly specifying oversized blob in input' '
- git -C r2 ls-files -s large.1000 large.10000 |
- test_parse_ls_files_stage_oids |
+ git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
echo HEAD >objects &&
@@ -233,8 +235,8 @@
'
test_expect_success 'verify blob:limit=1m' '
- git -C r2 ls-files -s large.1000 large.10000 |
- test_parse_ls_files_stage_oids |
+ git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
git -C r2 pack-objects --revs --stdout --filter=blob:limit=1m >filter.pack <<-EOF &&
@@ -264,6 +266,44 @@
test_cmp expected observed
'
+test_expect_success 'verify small limit and big limit results in small limit' '
+ git -C r2 ls-files -s large.1000 >ls_files_result &&
+ test_parse_ls_files_stage_oids <ls_files_result |
+ sort >expected &&
+
+ git -C r2 pack-objects --revs --stdout --filter=blob:limit=1001 \
+ --filter=blob:limit=10001 >filter.pack <<-EOF &&
+ HEAD
+ EOF
+ git -C r2 index-pack ../filter.pack &&
+
+ git -C r2 verify-pack -v ../filter.pack >verify_result &&
+ grep blob verify_result |
+ parse_verify_pack_blob_oid |
+ sort >observed &&
+
+ test_cmp expected observed
+'
+
+test_expect_success 'verify big limit and small limit results in small limit' '
+ git -C r2 ls-files -s large.1000 >ls_files_result &&
+ test_parse_ls_files_stage_oids <ls_files_result |
+ sort >expected &&
+
+ git -C r2 pack-objects --revs --stdout --filter=blob:limit=10001 \
+ --filter=blob:limit=1001 >filter.pack <<-EOF &&
+ HEAD
+ EOF
+ git -C r2 index-pack ../filter.pack &&
+
+ git -C r2 verify-pack -v ../filter.pack >verify_result &&
+ grep blob verify_result |
+ parse_verify_pack_blob_oid |
+ sort >observed &&
+
+ test_cmp expected observed
+'
+
# Test sparse:path=<path> filter.
# !!!!
# NOTE: sparse:path filter support has been dropped for security reasons,
@@ -289,8 +329,9 @@
'
test_expect_success 'verify blob count in normal packfile' '
- git -C r3 ls-files -s sparse1 sparse2 dir1/sparse1 dir1/sparse2 |
- test_parse_ls_files_stage_oids |
+ git -C r3 ls-files -s sparse1 sparse2 dir1/sparse1 dir1/sparse2 \
+ >ls_files_result &&
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
git -C r3 pack-objects --revs --stdout >all.pack <<-EOF &&
@@ -341,8 +382,9 @@
'
test_expect_success 'verify blob count in normal packfile' '
- git -C r4 ls-files -s pattern sparse1 sparse2 dir1/sparse1 dir1/sparse2 |
- test_parse_ls_files_stage_oids |
+ git -C r4 ls-files -s pattern sparse1 sparse2 dir1/sparse1 dir1/sparse2 \
+ >ls_files_result &&
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
git -C r4 pack-objects --revs --stdout >all.pack <<-EOF &&
@@ -359,8 +401,8 @@
'
test_expect_success 'verify sparse:oid=OID' '
- git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 |
- test_parse_ls_files_stage_oids |
+ git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 >ls_files_result &&
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
git -C r4 ls-files -s pattern >staged &&
@@ -379,8 +421,8 @@
'
test_expect_success 'verify sparse:oid=oid-ish' '
- git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 |
- test_parse_ls_files_stage_oids |
+ git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 >ls_files_result &&
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
git -C r4 pack-objects --revs --stdout --filter=sparse:oid=main:pattern >filter.pack <<-EOF &&
@@ -400,8 +442,9 @@
# This models previously omitted objects that we did not receive.
test_expect_success 'setup r1 - delete loose blobs' '
- git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 |
- test_parse_ls_files_stage_oids |
+ git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 \
+ >ls_files_result &&
+ test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
for id in `cat expected | sed "s|..|&/|"`
diff --git a/t/t5330-no-lazy-fetch-with-commit-graph.sh b/t/t5330-no-lazy-fetch-with-commit-graph.sh
index 2cc7fd7..5eb28f0 100755
--- a/t/t5330-no-lazy-fetch-with-commit-graph.sh
+++ b/t/t5330-no-lazy-fetch-with-commit-graph.sh
@@ -2,6 +2,7 @@
test_description='test for no lazy fetch with the commit-graph'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup: prepare a repository with a commit' '
diff --git a/t/t5403-post-checkout-hook.sh b/t/t5403-post-checkout-hook.sh
index 978f240..cfaae54 100755
--- a/t/t5403-post-checkout-hook.sh
+++ b/t/t5403-post-checkout-hook.sh
@@ -7,6 +7,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t5405-send-pack-rewind.sh b/t/t5405-send-pack-rewind.sh
index 11f0323..1686ac1 100755
--- a/t/t5405-send-pack-rewind.sh
+++ b/t/t5405-send-pack-rewind.sh
@@ -5,6 +5,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t5406-remote-rejects.sh b/t/t5406-remote-rejects.sh
index dcbeb42..d6a9946 100755
--- a/t/t5406-remote-rejects.sh
+++ b/t/t5406-remote-rejects.sh
@@ -2,6 +2,7 @@
test_description='remote push rejects are reported by client'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t5502-quickfetch.sh b/t/t5502-quickfetch.sh
index b160f8b..7b3ff21 100755
--- a/t/t5502-quickfetch.sh
+++ b/t/t5502-quickfetch.sh
@@ -5,6 +5,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh
index ac4099c..0b8ab4a 100755
--- a/t/t5504-fetch-receive-strict.sh
+++ b/t/t5504-fetch-receive-strict.sh
@@ -4,6 +4,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup and inject "corrupt or missing" object' '
@@ -138,7 +139,7 @@
EOF
test_expect_success 'setup bogus commit' '
- commit="$(git hash-object -t commit -w --stdin <bogus-commit)"
+ commit="$(git hash-object --literally -t commit -w --stdin <bogus-commit)"
'
test_expect_success 'fsck with no skipList input' '
diff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh
index 5bac03e..0e17617 100755
--- a/t/t5506-remote-groups.sh
+++ b/t/t5506-remote-groups.sh
@@ -99,4 +99,13 @@
! repo_fetched two
'
+test_expect_success 'updating group in parallel with a duplicate remote does not fail (fetch)' '
+ mark fetch-group-duplicate &&
+ update_repo one &&
+ git config --add remotes.duplicate one &&
+ git config --add remotes.duplicate one &&
+ git -c fetch.parallel=2 remote update duplicate &&
+ repo_fetched one
+'
+
test_done
diff --git a/t/t5507-remote-environment.sh b/t/t5507-remote-environment.sh
index e614929..c6a6957 100755
--- a/t/t5507-remote-environment.sh
+++ b/t/t5507-remote-environment.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='check environment showed to remote side of transports'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'set up "remote" push situation' '
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index c0b745e..34a1261 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -806,6 +806,14 @@
)
'
+# fetches from first configured url
+test_expect_success 'fetch from multiple configured URLs in single remote' '
+ git init url1 &&
+ git remote add multipleurls url1 &&
+ git remote set-url --add multipleurls url2 &&
+ git fetch multipleurls
+'
+
# configured prune tests
set_config_tristate () {
diff --git a/t/t5522-pull-symlink.sh b/t/t5522-pull-symlink.sh
index bcff460..394bc60 100755
--- a/t/t5522-pull-symlink.sh
+++ b/t/t5522-pull-symlink.sh
@@ -2,6 +2,7 @@
test_description='pulling from symlinked subdir'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# The scenario we are building:
diff --git a/t/t5523-push-upstream.sh b/t/t5523-push-upstream.sh
index fdb4292..c9acc07 100755
--- a/t/t5523-push-upstream.sh
+++ b/t/t5523-push-upstream.sh
@@ -4,6 +4,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
diff --git a/t/t5527-fetch-odd-refs.sh b/t/t5527-fetch-odd-refs.sh
index e2770e4..98ece27 100755
--- a/t/t5527-fetch-odd-refs.sh
+++ b/t/t5527-fetch-odd-refs.sh
@@ -4,6 +4,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# afterwards we will have:
diff --git a/t/t5529-push-errors.sh b/t/t5529-push-errors.sh
index ce85fd3..0247137 100755
--- a/t/t5529-push-errors.sh
+++ b/t/t5529-push-errors.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='detect some push errors early (before contacting remote)'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup commits' '
diff --git a/t/t5544-pack-objects-hook.sh b/t/t5544-pack-objects-hook.sh
index 54f54f8..1a9e14b 100755
--- a/t/t5544-pack-objects-hook.sh
+++ b/t/t5544-pack-objects-hook.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test custom script in place of pack-objects'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'create some history to fetch' '
diff --git a/t/t5546-receive-limits.sh b/t/t5546-receive-limits.sh
index 0b0e987..eed3c9d 100755
--- a/t/t5546-receive-limits.sh
+++ b/t/t5546-receive-limits.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='check receive input limits'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Let's run tests with different unpack limits: 1 and 10000
diff --git a/t/t5547-push-quarantine.sh b/t/t5547-push-quarantine.sh
index 1876fb3..9f899b8 100755
--- a/t/t5547-push-quarantine.sh
+++ b/t/t5547-push-quarantine.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='check quarantine of objects during push'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'create picky dest repo' '
diff --git a/t/t5554-noop-fetch-negotiator.sh b/t/t5554-noop-fetch-negotiator.sh
index 2ac7b58..06991e8 100755
--- a/t/t5554-noop-fetch-negotiator.sh
+++ b/t/t5554-noop-fetch-negotiator.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test noop fetch negotiator'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'noop negotiator does not emit any "have"' '
diff --git a/t/t5558-clone-bundle-uri.sh b/t/t5558-clone-bundle-uri.sh
index 9155f31..afd5692 100755
--- a/t/t5558-clone-bundle-uri.sh
+++ b/t/t5558-clone-bundle-uri.sh
@@ -285,6 +285,8 @@
'
test_expect_success 'clone bundle list (HTTP, no heuristic)' '
+ test_when_finished rm -f trace*.txt &&
+
cp clone-from/bundle-*.bundle "$HTTPD_DOCUMENT_ROOT_PATH/" &&
cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
[bundle]
@@ -304,12 +306,26 @@
uri = $HTTPD_URL/bundle-4.bundle
EOF
- git clone --bundle-uri="$HTTPD_URL/bundle-list" \
+ GIT_TRACE2_EVENT="$(pwd)/trace-clone.txt" \
+ git clone --bundle-uri="$HTTPD_URL/bundle-list" \
clone-from clone-list-http 2>err &&
! grep "Repository lacks these prerequisite commits" err &&
git -C clone-from for-each-ref --format="%(objectname)" >oids &&
- git -C clone-list-http cat-file --batch-check <oids
+ git -C clone-list-http cat-file --batch-check <oids &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-1.bundle
+ $HTTPD_URL/bundle-2.bundle
+ $HTTPD_URL/bundle-3.bundle
+ $HTTPD_URL/bundle-4.bundle
+ $HTTPD_URL/bundle-list
+ EOF
+
+ # Sort the list, since the order is not well-defined
+ # without a heuristic.
+ test_remote_https_urls <trace-clone.txt | sort >actual &&
+ test_cmp expect actual
'
test_expect_success 'clone bundle list (HTTP, any mode)' '
@@ -350,6 +366,658 @@
test_cmp expect actual
'
+test_expect_success 'clone bundle list (http, creationToken)' '
+ test_when_finished rm -f trace*.txt &&
+
+ cp clone-from/bundle-*.bundle "$HTTPD_DOCUMENT_ROOT_PATH/" &&
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = bundle-1.bundle
+ creationToken = 1
+
+ [bundle "bundle-2"]
+ uri = bundle-2.bundle
+ creationToken = 2
+
+ [bundle "bundle-3"]
+ uri = bundle-3.bundle
+ creationToken = 3
+
+ [bundle "bundle-4"]
+ uri = bundle-4.bundle
+ creationToken = 4
+ EOF
+
+ GIT_TRACE2_EVENT="$(pwd)/trace-clone.txt" git \
+ clone --bundle-uri="$HTTPD_URL/bundle-list" \
+ "$HTTPD_URL/smart/fetch.git" clone-list-http-2 &&
+
+ git -C clone-from for-each-ref --format="%(objectname)" >oids &&
+ git -C clone-list-http-2 cat-file --batch-check <oids &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/bundle-4.bundle
+ $HTTPD_URL/bundle-3.bundle
+ $HTTPD_URL/bundle-2.bundle
+ $HTTPD_URL/bundle-1.bundle
+ EOF
+
+ test_remote_https_urls <trace-clone.txt >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'clone incomplete bundle list (http, creationToken)' '
+ test_when_finished rm -f trace*.txt &&
+
+ cp clone-from/bundle-*.bundle "$HTTPD_DOCUMENT_ROOT_PATH/" &&
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = bundle-1.bundle
+ creationToken = 1
+ EOF
+
+ GIT_TRACE2_EVENT=$(pwd)/trace-clone.txt \
+ git clone --bundle-uri="$HTTPD_URL/bundle-list" \
+ --single-branch --branch=base --no-tags \
+ "$HTTPD_URL/smart/fetch.git" clone-token-http &&
+
+ test_cmp_config -C clone-token-http "$HTTPD_URL/bundle-list" fetch.bundleuri &&
+ test_cmp_config -C clone-token-http 1 fetch.bundlecreationtoken &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/bundle-1.bundle
+ EOF
+
+ test_remote_https_urls <trace-clone.txt >actual &&
+ test_cmp expect actual &&
+
+ # We now have only one bundle ref.
+ git -C clone-token-http for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+ cat >expect <<-\EOF &&
+ refs/bundles/base
+ EOF
+ test_cmp expect refs &&
+
+ # Add remaining bundles, exercising the "deepening" strategy
+ # for downloading via the creationToken heurisitc.
+ cat >>"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle "bundle-2"]
+ uri = bundle-2.bundle
+ creationToken = 2
+
+ [bundle "bundle-3"]
+ uri = bundle-3.bundle
+ creationToken = 3
+
+ [bundle "bundle-4"]
+ uri = bundle-4.bundle
+ creationToken = 4
+ EOF
+
+ GIT_TRACE2_EVENT="$(pwd)/trace1.txt" \
+ git -C clone-token-http fetch origin --no-tags \
+ refs/heads/merge:refs/heads/merge &&
+ test_cmp_config -C clone-token-http 4 fetch.bundlecreationtoken &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/bundle-4.bundle
+ $HTTPD_URL/bundle-3.bundle
+ $HTTPD_URL/bundle-2.bundle
+ EOF
+
+ test_remote_https_urls <trace1.txt >actual &&
+ test_cmp expect actual &&
+
+ # We now have all bundle refs.
+ git -C clone-token-http for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+
+ cat >expect <<-\EOF &&
+ refs/bundles/base
+ refs/bundles/left
+ refs/bundles/merge
+ refs/bundles/right
+ EOF
+ test_cmp expect refs
+'
+
+test_expect_success 'http clone with bundle.heuristic creates fetch.bundleURI' '
+ test_when_finished rm -rf fetch-http-4 trace*.txt &&
+
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = bundle-1.bundle
+ creationToken = 1
+ EOF
+
+ GIT_TRACE2_EVENT="$(pwd)/trace-clone.txt" \
+ git clone --single-branch --branch=base \
+ --bundle-uri="$HTTPD_URL/bundle-list" \
+ "$HTTPD_URL/smart/fetch.git" fetch-http-4 &&
+
+ test_cmp_config -C fetch-http-4 "$HTTPD_URL/bundle-list" fetch.bundleuri &&
+ test_cmp_config -C fetch-http-4 1 fetch.bundlecreationtoken &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/bundle-1.bundle
+ EOF
+
+ test_remote_https_urls <trace-clone.txt >actual &&
+ test_cmp expect actual &&
+
+ # only received base ref from bundle-1
+ git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+ cat >expect <<-\EOF &&
+ refs/bundles/base
+ EOF
+ test_cmp expect refs &&
+
+ cat >>"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle "bundle-2"]
+ uri = bundle-2.bundle
+ creationToken = 2
+ EOF
+
+ # Fetch the objects for bundle-2 _and_ bundle-3.
+ GIT_TRACE2_EVENT="$(pwd)/trace1.txt" \
+ git -C fetch-http-4 fetch origin --no-tags \
+ refs/heads/left:refs/heads/left \
+ refs/heads/right:refs/heads/right &&
+ test_cmp_config -C fetch-http-4 2 fetch.bundlecreationtoken &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/bundle-2.bundle
+ EOF
+
+ test_remote_https_urls <trace1.txt >actual &&
+ test_cmp expect actual &&
+
+ # received left from bundle-2
+ git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+ cat >expect <<-\EOF &&
+ refs/bundles/base
+ refs/bundles/left
+ EOF
+ test_cmp expect refs &&
+
+ # No-op fetch
+ GIT_TRACE2_EVENT="$(pwd)/trace1b.txt" \
+ git -C fetch-http-4 fetch origin --no-tags \
+ refs/heads/left:refs/heads/left \
+ refs/heads/right:refs/heads/right &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ EOF
+ test_remote_https_urls <trace1b.txt >actual &&
+ test_cmp expect actual &&
+
+ cat >>"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle "bundle-3"]
+ uri = bundle-3.bundle
+ creationToken = 3
+
+ [bundle "bundle-4"]
+ uri = bundle-4.bundle
+ creationToken = 4
+ EOF
+
+ # This fetch should skip bundle-3.bundle, since its objects are
+ # already local (we have the requisite commits for bundle-4.bundle).
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ git -C fetch-http-4 fetch origin --no-tags \
+ refs/heads/merge:refs/heads/merge &&
+ test_cmp_config -C fetch-http-4 4 fetch.bundlecreationtoken &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/bundle-4.bundle
+ EOF
+
+ test_remote_https_urls <trace2.txt >actual &&
+ test_cmp expect actual &&
+
+ # received merge ref from bundle-4, but right is missing
+ # because we did not download bundle-3.
+ git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+
+ cat >expect <<-\EOF &&
+ refs/bundles/base
+ refs/bundles/left
+ refs/bundles/merge
+ EOF
+ test_cmp expect refs &&
+
+ # No-op fetch
+ GIT_TRACE2_EVENT="$(pwd)/trace2b.txt" \
+ git -C fetch-http-4 fetch origin &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ EOF
+ test_remote_https_urls <trace2b.txt >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'creationToken heuristic with failed downloads (clone)' '
+ test_when_finished rm -rf download-* trace*.txt &&
+
+ # Case 1: base bundle does not exist, nothing can unbundle
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = fake.bundle
+ creationToken = 1
+
+ [bundle "bundle-2"]
+ uri = bundle-2.bundle
+ creationToken = 2
+
+ [bundle "bundle-3"]
+ uri = bundle-3.bundle
+ creationToken = 3
+
+ [bundle "bundle-4"]
+ uri = bundle-4.bundle
+ creationToken = 4
+ EOF
+
+ GIT_TRACE2_EVENT="$(pwd)/trace-clone-1.txt" \
+ git clone --single-branch --branch=base \
+ --bundle-uri="$HTTPD_URL/bundle-list" \
+ "$HTTPD_URL/smart/fetch.git" download-1 &&
+
+ # Bundle failure does not set these configs.
+ test_must_fail git -C download-1 config fetch.bundleuri &&
+ test_must_fail git -C download-1 config fetch.bundlecreationtoken &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/bundle-4.bundle
+ $HTTPD_URL/bundle-3.bundle
+ $HTTPD_URL/bundle-2.bundle
+ $HTTPD_URL/fake.bundle
+ EOF
+ test_remote_https_urls <trace-clone-1.txt >actual &&
+ test_cmp expect actual &&
+
+ # All bundles failed to unbundle
+ git -C download-1 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+ test_must_be_empty refs &&
+
+ # Case 2: middle bundle does not exist, only two bundles can unbundle
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = bundle-1.bundle
+ creationToken = 1
+
+ [bundle "bundle-2"]
+ uri = fake.bundle
+ creationToken = 2
+
+ [bundle "bundle-3"]
+ uri = bundle-3.bundle
+ creationToken = 3
+
+ [bundle "bundle-4"]
+ uri = bundle-4.bundle
+ creationToken = 4
+ EOF
+
+ GIT_TRACE2_EVENT="$(pwd)/trace-clone-2.txt" \
+ git clone --single-branch --branch=base \
+ --bundle-uri="$HTTPD_URL/bundle-list" \
+ "$HTTPD_URL/smart/fetch.git" download-2 &&
+
+ # Bundle failure does not set these configs.
+ test_must_fail git -C download-2 config fetch.bundleuri &&
+ test_must_fail git -C download-2 config fetch.bundlecreationtoken &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/bundle-4.bundle
+ $HTTPD_URL/bundle-3.bundle
+ $HTTPD_URL/fake.bundle
+ $HTTPD_URL/bundle-1.bundle
+ EOF
+ test_remote_https_urls <trace-clone-2.txt >actual &&
+ test_cmp expect actual &&
+
+ # bundle-1 and bundle-3 could unbundle, but bundle-4 could not
+ git -C download-2 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+ cat >expect <<-EOF &&
+ refs/bundles/base
+ refs/bundles/right
+ EOF
+ test_cmp expect refs &&
+
+ # Case 3: top bundle does not exist, rest unbundle fine.
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = bundle-1.bundle
+ creationToken = 1
+
+ [bundle "bundle-2"]
+ uri = bundle-2.bundle
+ creationToken = 2
+
+ [bundle "bundle-3"]
+ uri = bundle-3.bundle
+ creationToken = 3
+
+ [bundle "bundle-4"]
+ uri = fake.bundle
+ creationToken = 4
+ EOF
+
+ GIT_TRACE2_EVENT="$(pwd)/trace-clone-3.txt" \
+ git clone --single-branch --branch=base \
+ --bundle-uri="$HTTPD_URL/bundle-list" \
+ "$HTTPD_URL/smart/fetch.git" download-3 &&
+
+ # As long as we have continguous successful downloads,
+ # we _do_ set these configs.
+ test_cmp_config -C download-3 "$HTTPD_URL/bundle-list" fetch.bundleuri &&
+ test_cmp_config -C download-3 3 fetch.bundlecreationtoken &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/fake.bundle
+ $HTTPD_URL/bundle-3.bundle
+ $HTTPD_URL/bundle-2.bundle
+ $HTTPD_URL/bundle-1.bundle
+ EOF
+ test_remote_https_urls <trace-clone-3.txt >actual &&
+ test_cmp expect actual &&
+
+ # fake.bundle did not unbundle, but the others did.
+ git -C download-3 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+ cat >expect <<-EOF &&
+ refs/bundles/base
+ refs/bundles/left
+ refs/bundles/right
+ EOF
+ test_cmp expect refs
+'
+
+# Expand the bundle list to include other interesting shapes, specifically
+# interesting for use when fetching from a previous state.
+#
+# ---------------- bundle-7
+# 7
+# _/|\_
+# ---/--|--\------ bundle-6
+# 5 | 6
+# --|---|---|----- bundle-4
+# | 4 |
+# | / \ /
+# --|-|---|/------ bundle-3 (the client will be caught up to this point.)
+# \ | 3
+# ---\|---|------- bundle-2
+# 2 |
+# ----|---|------- bundle-1
+# \ /
+# 1
+# |
+# (previous commits)
+test_expect_success 'expand incremental bundle list' '
+ (
+ cd clone-from &&
+ git checkout -b lefter left &&
+ test_commit 5 &&
+ git checkout -b righter right &&
+ test_commit 6 &&
+ git checkout -b top lefter &&
+ git merge -m "7" merge righter &&
+
+ git bundle create bundle-6.bundle lefter righter --not left right &&
+ git bundle create bundle-7.bundle top --not lefter merge righter &&
+
+ cp bundle-*.bundle "$HTTPD_DOCUMENT_ROOT_PATH/"
+ ) &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/fetch.git" fetch origin +refs/heads/*:refs/heads/*
+'
+
+test_expect_success 'creationToken heuristic with failed downloads (fetch)' '
+ test_when_finished rm -rf download-* trace*.txt &&
+
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = bundle-1.bundle
+ creationToken = 1
+
+ [bundle "bundle-2"]
+ uri = bundle-2.bundle
+ creationToken = 2
+
+ [bundle "bundle-3"]
+ uri = bundle-3.bundle
+ creationToken = 3
+ EOF
+
+ git clone --single-branch --branch=left \
+ --bundle-uri="$HTTPD_URL/bundle-list" \
+ "$HTTPD_URL/smart/fetch.git" fetch-base &&
+ test_cmp_config -C fetch-base "$HTTPD_URL/bundle-list" fetch.bundleURI &&
+ test_cmp_config -C fetch-base 3 fetch.bundleCreationToken &&
+
+ # Case 1: all bundles exist: successful unbundling of all bundles
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = bundle-1.bundle
+ creationToken = 1
+
+ [bundle "bundle-2"]
+ uri = bundle-2.bundle
+ creationToken = 2
+
+ [bundle "bundle-3"]
+ uri = bundle-3.bundle
+ creationToken = 3
+
+ [bundle "bundle-4"]
+ uri = bundle-4.bundle
+ creationToken = 4
+
+ [bundle "bundle-6"]
+ uri = bundle-6.bundle
+ creationToken = 6
+
+ [bundle "bundle-7"]
+ uri = bundle-7.bundle
+ creationToken = 7
+ EOF
+
+ cp -r fetch-base fetch-1 &&
+ GIT_TRACE2_EVENT="$(pwd)/trace-fetch-1.txt" \
+ git -C fetch-1 fetch origin &&
+ test_cmp_config -C fetch-1 7 fetch.bundlecreationtoken &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/bundle-7.bundle
+ $HTTPD_URL/bundle-6.bundle
+ $HTTPD_URL/bundle-4.bundle
+ EOF
+ test_remote_https_urls <trace-fetch-1.txt >actual &&
+ test_cmp expect actual &&
+
+ # Check which bundles have unbundled by refs
+ git -C fetch-1 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+ cat >expect <<-EOF &&
+ refs/bundles/base
+ refs/bundles/left
+ refs/bundles/lefter
+ refs/bundles/merge
+ refs/bundles/right
+ refs/bundles/righter
+ refs/bundles/top
+ EOF
+ test_cmp expect refs &&
+
+ # Case 2: middle bundle does not exist, only bundle-4 can unbundle
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = bundle-1.bundle
+ creationToken = 1
+
+ [bundle "bundle-2"]
+ uri = bundle-2.bundle
+ creationToken = 2
+
+ [bundle "bundle-3"]
+ uri = bundle-3.bundle
+ creationToken = 3
+
+ [bundle "bundle-4"]
+ uri = bundle-4.bundle
+ creationToken = 4
+
+ [bundle "bundle-6"]
+ uri = fake.bundle
+ creationToken = 6
+
+ [bundle "bundle-7"]
+ uri = bundle-7.bundle
+ creationToken = 7
+ EOF
+
+ cp -r fetch-base fetch-2 &&
+ GIT_TRACE2_EVENT="$(pwd)/trace-fetch-2.txt" \
+ git -C fetch-2 fetch origin &&
+
+ # Since bundle-7 fails to unbundle, do not update creation token.
+ test_cmp_config -C fetch-2 3 fetch.bundlecreationtoken &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/bundle-7.bundle
+ $HTTPD_URL/fake.bundle
+ $HTTPD_URL/bundle-4.bundle
+ EOF
+ test_remote_https_urls <trace-fetch-2.txt >actual &&
+ test_cmp expect actual &&
+
+ # Check which bundles have unbundled by refs
+ git -C fetch-2 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+ cat >expect <<-EOF &&
+ refs/bundles/base
+ refs/bundles/left
+ refs/bundles/merge
+ refs/bundles/right
+ EOF
+ test_cmp expect refs &&
+
+ # Case 3: top bundle does not exist, rest unbundle fine.
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = bundle-1.bundle
+ creationToken = 1
+
+ [bundle "bundle-2"]
+ uri = bundle-2.bundle
+ creationToken = 2
+
+ [bundle "bundle-3"]
+ uri = bundle-3.bundle
+ creationToken = 3
+
+ [bundle "bundle-4"]
+ uri = bundle-4.bundle
+ creationToken = 4
+
+ [bundle "bundle-6"]
+ uri = bundle-6.bundle
+ creationToken = 6
+
+ [bundle "bundle-7"]
+ uri = fake.bundle
+ creationToken = 7
+ EOF
+
+ cp -r fetch-base fetch-3 &&
+ GIT_TRACE2_EVENT="$(pwd)/trace-fetch-3.txt" \
+ git -C fetch-3 fetch origin &&
+
+ # As long as we have continguous successful downloads,
+ # we _do_ set the maximum creation token.
+ test_cmp_config -C fetch-3 6 fetch.bundlecreationtoken &&
+
+ # NOTE: the fetch skips bundle-4 since bundle-6 successfully
+ # unbundles itself and bundle-7 failed to download.
+ cat >expect <<-EOF &&
+ $HTTPD_URL/bundle-list
+ $HTTPD_URL/fake.bundle
+ $HTTPD_URL/bundle-6.bundle
+ EOF
+ test_remote_https_urls <trace-fetch-3.txt >actual &&
+ test_cmp expect actual &&
+
+ # Check which bundles have unbundled by refs
+ git -C fetch-3 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+ cat >expect <<-EOF &&
+ refs/bundles/base
+ refs/bundles/left
+ refs/bundles/lefter
+ refs/bundles/right
+ refs/bundles/righter
+ EOF
+ test_cmp expect refs
+'
+
# Do not add tests here unless they use the HTTP server, as they will
# not run unless the HTTP dependencies exist.
diff --git a/t/t5560-http-backend-noserver.sh b/t/t5560-http-backend-noserver.sh
index d30cf4f..f75068d 100755
--- a/t/t5560-http-backend-noserver.sh
+++ b/t/t5560-http-backend-noserver.sh
@@ -4,6 +4,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
HTTPD_DOCUMENT_ROOT_PATH="$TRASH_DIRECTORY"
diff --git a/t/t5561-http-backend.sh b/t/t5561-http-backend.sh
index 9c57d84..e1d3b8c 100755
--- a/t/t5561-http-backend.sh
+++ b/t/t5561-http-backend.sh
@@ -4,6 +4,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-httpd.sh
diff --git a/t/t5562-http-backend-content-length.sh b/t/t5562-http-backend-content-length.sh
index b68ec22..7ee9858 100755
--- a/t/t5562-http-backend-content-length.sh
+++ b/t/t5562-http-backend-content-length.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test git-http-backend respects CONTENT_LENGTH'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_lazy_prereq GZIP 'gzip --version'
diff --git a/t/t5573-pull-verify-signatures.sh b/t/t5573-pull-verify-signatures.sh
index a53dd85..1221ac0 100755
--- a/t/t5573-pull-verify-signatures.sh
+++ b/t/t5573-pull-verify-signatures.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='pull signature verification tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY/lib-gpg.sh"
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index b2524a2..b7d5551 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -772,6 +772,111 @@
git clone --no-reject-shallow $HTTPD_URL/smart/repo.git repo
'
+test_expect_success 'auto-discover bundle URI from HTTP clone' '
+ test_when_finished rm -rf trace.txt repo2 "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" &&
+ git -C src bundle create "$HTTPD_DOCUMENT_ROOT_PATH/everything.bundle" --all &&
+ git clone --bare --no-local src "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" &&
+
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" config \
+ uploadpack.advertiseBundleURIs true &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" config \
+ bundle.version 1 &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" config \
+ bundle.mode all &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo2.git" config \
+ bundle.everything.uri "$HTTPD_URL/everything.bundle" &&
+
+ GIT_TRACE2_EVENT="$(pwd)/trace.txt" \
+ git -c protocol.version=2 \
+ -c transfer.bundleURI=true clone \
+ $HTTPD_URL/smart/repo2.git repo2 &&
+ cat >pattern <<-EOF &&
+ "event":"child_start".*"argv":\["git-remote-https","$HTTPD_URL/everything.bundle"\]
+ EOF
+ grep -f pattern trace.txt
+'
+
+test_expect_success 'auto-discover multiple bundles from HTTP clone' '
+ test_when_finished rm -rf trace.txt repo3 "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" &&
+
+ test_commit -C src new &&
+ git -C src bundle create "$HTTPD_DOCUMENT_ROOT_PATH/new.bundle" HEAD~1..HEAD &&
+ git clone --bare --no-local src "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" &&
+
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" config \
+ uploadpack.advertiseBundleURIs true &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" config \
+ bundle.version 1 &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" config \
+ bundle.mode all &&
+
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" config \
+ bundle.everything.uri "$HTTPD_URL/everything.bundle" &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo3.git" config \
+ bundle.new.uri "$HTTPD_URL/new.bundle" &&
+
+ GIT_TRACE2_EVENT="$(pwd)/trace.txt" \
+ git -c protocol.version=2 \
+ -c transfer.bundleURI=true clone \
+ $HTTPD_URL/smart/repo3.git repo3 &&
+
+ # We should fetch _both_ bundles
+ cat >pattern <<-EOF &&
+ "event":"child_start".*"argv":\["git-remote-https","$HTTPD_URL/everything.bundle"\]
+ EOF
+ grep -f pattern trace.txt &&
+ cat >pattern <<-EOF &&
+ "event":"child_start".*"argv":\["git-remote-https","$HTTPD_URL/new.bundle"\]
+ EOF
+ grep -f pattern trace.txt
+'
+
+test_expect_success 'auto-discover multiple bundles from HTTP clone: creationToken heuristic' '
+ test_when_finished rm -rf "$HTTPD_DOCUMENT_ROOT_PATH/repo4.git" &&
+ test_when_finished rm -rf clone-heuristic trace*.txt &&
+
+ test_commit -C src newest &&
+ git -C src bundle create "$HTTPD_DOCUMENT_ROOT_PATH/newest.bundle" HEAD~1..HEAD &&
+ git clone --bare --no-local src "$HTTPD_DOCUMENT_ROOT_PATH/repo4.git" &&
+
+ cat >>"$HTTPD_DOCUMENT_ROOT_PATH/repo4.git/config" <<-EOF &&
+ [uploadPack]
+ advertiseBundleURIs = true
+
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "everything"]
+ uri = $HTTPD_URL/everything.bundle
+ creationtoken = 1
+
+ [bundle "new"]
+ uri = $HTTPD_URL/new.bundle
+ creationtoken = 2
+
+ [bundle "newest"]
+ uri = $HTTPD_URL/newest.bundle
+ creationtoken = 3
+ EOF
+
+ GIT_TRACE2_EVENT="$(pwd)/trace-clone.txt" \
+ git -c protocol.version=2 \
+ -c transfer.bundleURI=true clone \
+ "$HTTPD_URL/smart/repo4.git" clone-heuristic &&
+
+ cat >expect <<-EOF &&
+ $HTTPD_URL/newest.bundle
+ $HTTPD_URL/new.bundle
+ $HTTPD_URL/everything.bundle
+ EOF
+
+ # We should fetch all bundles in the expected order.
+ test_remote_https_urls <trace-clone.txt >actual &&
+ test_cmp expect actual
+'
+
# DO NOT add non-httpd-specific tests here, because the last part of this
# test script is only executed when httpd is available and enabled.
diff --git a/t/t5604-clone-reference.sh b/t/t5604-clone-reference.sh
index 2734e37..83e3c97 100755
--- a/t/t5604-clone-reference.sh
+++ b/t/t5604-clone-reference.sh
@@ -7,6 +7,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
base_dir=$(pwd)
@@ -344,4 +345,20 @@
test_must_be_empty T--shared.objects-symlinks.raw
'
+test_expect_success SYMLINKS 'clone repo with symlinked objects directory' '
+ test_when_finished "rm -fr sensitive malicious" &&
+
+ mkdir -p sensitive &&
+ echo "secret" >sensitive/file &&
+
+ git init malicious &&
+ rm -fr malicious/.git/objects &&
+ ln -s "$(pwd)/sensitive" ./malicious/.git/objects &&
+
+ test_must_fail git clone --local malicious clone 2>err &&
+
+ test_path_is_missing clone &&
+ grep "failed to start iterator over" err
+'
+
test_done
diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh
index cf221e9..27f9f77 100755
--- a/t/t5606-clone-options.sh
+++ b/t/t5606-clone-options.sh
@@ -4,6 +4,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t5610-clone-detached.sh b/t/t5610-clone-detached.sh
index a7ec21e..022ed3d 100755
--- a/t/t5610-clone-detached.sh
+++ b/t/t5610-clone-detached.sh
@@ -4,6 +4,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
head_is_detached() {
diff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh
index 4b38772..727caff 100755
--- a/t/t5611-clone-config.sh
+++ b/t/t5611-clone-config.sh
@@ -4,6 +4,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'clone -c sets config in cloned repo' '
diff --git a/t/t5613-info-alternate.sh b/t/t5613-info-alternate.sh
index 895f46b..7708cba 100755
--- a/t/t5613-info-alternate.sh
+++ b/t/t5613-info-alternate.sh
@@ -4,6 +4,8 @@
#
test_description='test transitive info/alternate entries'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'preparing first repository' '
diff --git a/t/t5614-clone-submodules-shallow.sh b/t/t5614-clone-submodules-shallow.sh
index 0c85ef8..c2a2bb4 100755
--- a/t/t5614-clone-submodules-shallow.sh
+++ b/t/t5614-clone-submodules-shallow.sh
@@ -2,6 +2,7 @@
test_description='Test shallow cloning of repos with submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
pwd=$(pwd)
diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh
index 037941b..f519d2a 100755
--- a/t/t5616-partial-clone.sh
+++ b/t/t5616-partial-clone.sh
@@ -644,6 +644,49 @@
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/t5617-clone-submodules-remote.sh b/t/t5617-clone-submodules-remote.sh
index 6884338..5a4d793 100755
--- a/t/t5617-clone-submodules-remote.sh
+++ b/t/t5617-clone-submodules-remote.sh
@@ -5,6 +5,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
pwd=$(pwd)
diff --git a/t/t5618-alternate-refs.sh b/t/t5618-alternate-refs.sh
index 3353216..f905db0 100755
--- a/t/t5618-alternate-refs.sh
+++ b/t/t5618-alternate-refs.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test handling of --alternate-refs traversal'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Avoid test_commit because we want a specific and known set of refs:
diff --git a/t/t5619-clone-local-ambiguous-transport.sh b/t/t5619-clone-local-ambiguous-transport.sh
new file mode 100755
index 0000000..cce62bf
--- /dev/null
+++ b/t/t5619-clone-local-ambiguous-transport.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+test_description='test local clone with ambiguous transport'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-httpd.sh"
+
+if ! test_have_prereq SYMLINKS
+then
+ skip_all='skipping test, symlink support unavailable'
+ test_done
+fi
+
+start_httpd
+
+REPO="$HTTPD_DOCUMENT_ROOT_PATH/sub.git"
+URI="$HTTPD_URL/dumb/sub.git"
+
+test_expect_success 'setup' '
+ mkdir -p sensitive &&
+ echo "secret" >sensitive/secret &&
+
+ git init --bare "$REPO" &&
+ test_commit_bulk -C "$REPO" --ref=main 1 &&
+
+ git -C "$REPO" update-ref HEAD main &&
+ git -C "$REPO" update-server-info &&
+
+ git init malicious &&
+ (
+ cd malicious &&
+
+ git submodule add "$URI" &&
+
+ mkdir -p repo/refs &&
+ touch repo/refs/.gitkeep &&
+ printf "ref: refs/heads/a" >repo/HEAD &&
+ ln -s "$(cd .. && pwd)/sensitive" repo/objects &&
+
+ mkdir -p "$HTTPD_URL/dumb" &&
+ ln -s "../../../.git/modules/sub/../../../repo/" "$URI" &&
+
+ git add . &&
+ git commit -m "initial commit"
+ ) &&
+
+ # Delete all of the references in our malicious submodule to
+ # avoid the client attempting to checkout any objects (which
+ # will be missing, and thus will cause the clone to fail before
+ # we can trigger the exploit).
+ git -C "$REPO" for-each-ref --format="delete %(refname)" >in &&
+ git -C "$REPO" update-ref --stdin <in &&
+ git -C "$REPO" update-server-info
+'
+
+test_expect_success 'ambiguous transport does not lead to arbitrary file-inclusion' '
+ git clone malicious clone &&
+ test_must_fail git -C clone submodule update --init 2>err &&
+
+ test_path_is_missing clone/.git/modules/sub/objects/secret &&
+ # We would actually expect "transport .file. not allowed" here,
+ # but due to quirks of the URL detection in Git, we mis-parse
+ # the absolute path as a bogus URL and die before that step.
+ #
+ # This works for now, and if we ever fix the URL detection, it
+ # is OK to change this to detect the transport error.
+ grep "protocol .* is not supported" err
+'
+
+test_done
diff --git a/t/t5701-git-serve.sh b/t/t5701-git-serve.sh
index 1896f67..f21e5e9 100755
--- a/t/t5701-git-serve.sh
+++ b/t/t5701-git-serve.sh
@@ -13,7 +13,7 @@
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 @@
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_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/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh
index b33cd4a..e4db751 100755
--- a/t/t5702-protocol-v2.sh
+++ b/t/t5702-protocol-v2.sh
@@ -1114,7 +1114,7 @@
This commit object intentionally broken
EOF
- BOGUS=$(git -C "$P" hash-object -t commit -w --stdin <bogus-commit) &&
+ BOGUS=$(git -C "$P" hash-object -t commit -w --stdin --literally <bogus-commit) &&
git -C "$P" branch bogus-branch "$BOGUS" &&
echo my-blob >"$P/my-blob" &&
diff --git a/t/t5705-session-id-in-capabilities.sh b/t/t5705-session-id-in-capabilities.sh
index ed38c76..b8a722e 100755
--- a/t/t5705-session-id-in-capabilities.sh
+++ b/t/t5705-session-id-in-capabilities.sh
@@ -2,6 +2,7 @@
test_description='session ID in capabilities'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
REPO="$(pwd)/repo"
diff --git a/t/t5730-protocol-v2-bundle-uri-file.sh b/t/t5730-protocol-v2-bundle-uri-file.sh
new file mode 100755
index 0000000..37bdb72
--- /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 0000000..8add1b3
--- /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 0000000..129daa0
--- /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 c2fe3f9..81bdf58 100755
--- a/t/t5750-bundle-uri-parse.sh
+++ b/t/t5750-bundle-uri-parse.sh
@@ -30,6 +30,58 @@
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_cmp_config_output expect actual
'
+test_expect_success 'parse config format: relative URIs' '
+ cat >in <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ [bundle "one"]
+ uri = bundle.bdl
+ [bundle "two"]
+ uri = ../bundle.bdl
+ [bundle "three"]
+ uri = sub/dir/bundle.bdl
+ EOF
+
+ cat >expect <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ [bundle "one"]
+ uri = <uri>/bundle.bdl
+ [bundle "two"]
+ uri = bundle.bdl
+ [bundle "three"]
+ uri = <uri>/sub/dir/bundle.bdl
+ EOF
+
+ test-tool bundle-uri parse-config in >actual 2>err &&
+ test_must_be_empty err &&
+ test_cmp_config_output expect actual
+'
+
test_expect_success 'parse config format edge cases: empty key or value' '
cat >in1 <<-\EOF &&
= bogus-value
@@ -168,4 +250,41 @@
test_cmp_config_output expect actual
'
+test_expect_success 'parse config format: creationToken heuristic' '
+ cat >expect <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+ [bundle "one"]
+ uri = http://example.com/bundle.bdl
+ creationToken = 123456
+ [bundle "two"]
+ uri = https://example.com/bundle.bdl
+ creationToken = 12345678901234567890
+ [bundle "three"]
+ uri = file:///usr/share/git/bundle.bdl
+ creationToken = 1
+ EOF
+
+ test-tool bundle-uri parse-config expect >actual 2>err &&
+ test_must_be_empty err &&
+ test_cmp_config_output expect actual
+'
+
+test_expect_success 'parse config format edge cases: creationToken heuristic' '
+ cat >expect <<-\EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+ [bundle "one"]
+ uri = http://example.com/bundle.bdl
+ creationToken = bogus
+ EOF
+
+ test-tool bundle-uri parse-config expect >actual 2>err &&
+ grep "could not parse bundle list key creationToken with value '\''bogus'\''" err
+'
+
test_done
diff --git a/t/t5810-proto-disable-local.sh b/t/t5810-proto-disable-local.sh
index c1ef99b..8626102 100755
--- a/t/t5810-proto-disable-local.sh
+++ b/t/t5810-proto-disable-local.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test disabling of local paths in clone/fetch'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY/lib-proto-disable.sh"
diff --git a/t/t5813-proto-disable-ssh.sh b/t/t5813-proto-disable-ssh.sh
index 3f084ee..2e975dc 100755
--- a/t/t5813-proto-disable-ssh.sh
+++ b/t/t5813-proto-disable-ssh.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test disabling of git-over-ssh in clone/fetch'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY/lib-proto-disable.sh"
diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh
index 1f7d7dd..5cf2cee 100755
--- a/t/t6003-rev-list-topo-order.sh
+++ b/t/t6003-rev-list-topo-order.sh
@@ -326,19 +326,16 @@
c3
EOF
-#
-# this test fails on --topo-order - a fix is required
-#
-#test_output_expect_success '--max-age=c3, --topo-order' "git rev-list --topo-order --max-age=$(commit_date c3) l5" <<EOF
-#l5
-#l4
-#l3
-#a4
-#c3
-#b4
-#a3
-#a2
-#EOF
+test_output_expect_success '--max-age=c3, --topo-order' "git rev-list --topo-order --max-age=$(commit_date c3) l5" <<EOF
+l5
+l4
+l3
+a4
+c3
+b4
+a3
+a2
+EOF
test_output_expect_success 'one specified head reachable from another a4, c3, --topo-order' "list_duplicates git rev-list --topo-order a4 c3" <<EOF
EOF
diff --git a/t/t6011-rev-list-with-bad-commit.sh b/t/t6011-rev-list-with-bad-commit.sh
index bad02cf..b2e422c 100755
--- a/t/t6011-rev-list-with-bad-commit.sh
+++ b/t/t6011-rev-list-with-bad-commit.sh
@@ -2,6 +2,7 @@
test_description='git rev-list should notice bad commits'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Note:
diff --git a/t/t6014-rev-list-all.sh b/t/t6014-rev-list-all.sh
index c9bedd2..16b8bd1 100755
--- a/t/t6014-rev-list-all.sh
+++ b/t/t6014-rev-list-all.sh
@@ -2,6 +2,7 @@
test_description='--all includes detached HEADs'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
diff --git a/t/t6020-bundle-misc.sh b/t/t6020-bundle-misc.sh
index 8332051..7d40994 100755
--- a/t/t6020-bundle-misc.sh
+++ b/t/t6020-bundle-misc.sh
@@ -11,6 +11,13 @@
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-bundle.sh
+for cmd in create verify list-heads unbundle
+do
+ test_expect_success "usage: git bundle $cmd needs an argument" '
+ test_expect_code 129 git bundle $cmd
+ '
+done
+
# Create a commit or tag and set the variable with the object ID.
test_commit_setvar () {
notick=
@@ -559,4 +566,44 @@
grep "cannot clone from filtered bundle" err
'
+test_expect_success 'verify catches unreachable, broken prerequisites' '
+ test_when_finished rm -rf clone-from clone-to &&
+ git init clone-from &&
+ (
+ cd clone-from &&
+ git checkout -b base &&
+ test_commit A &&
+ git checkout -b tip &&
+ git commit --allow-empty -m "will drop by shallow" &&
+ git commit --allow-empty -m "will keep by shallow" &&
+ git commit --allow-empty -m "for bundle, not clone" &&
+ git bundle create tip.bundle tip~1..tip &&
+ git reset --hard HEAD~1 &&
+ git checkout base
+ ) &&
+ BAD_OID=$(git -C clone-from rev-parse tip~1) &&
+ TIP_OID=$(git -C clone-from rev-parse tip) &&
+ git clone --depth=1 --no-single-branch \
+ "file://$(pwd)/clone-from" clone-to &&
+ (
+ cd clone-to &&
+
+ # Set up broken history by removing shallow markers
+ git update-ref -d refs/remotes/origin/tip &&
+ rm .git/shallow &&
+
+ # Verify should fail
+ test_must_fail git bundle verify \
+ ../clone-from/tip.bundle 2>err &&
+ grep "some prerequisite commits .* are not connected" err &&
+ test_line_count = 1 err &&
+
+ # Unbundling should fail
+ test_must_fail git bundle unbundle \
+ ../clone-from/tip.bundle 2>err &&
+ grep "some prerequisite commits .* are not connected" err &&
+ test_line_count = 1 err
+ )
+'
+
test_done
diff --git a/t/t6021-rev-list-exclude-hidden.sh b/t/t6021-rev-list-exclude-hidden.sh
index 32b2b09..11c50b7 100755
--- a/t/t6021-rev-list-exclude-hidden.sh
+++ b/t/t6021-rev-list-exclude-hidden.sh
@@ -2,6 +2,7 @@
test_description='git rev-list --exclude-hidden test'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 6dbbe62..3ba4fdf 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -34,6 +34,36 @@
HASH3=
HASH4=
+test_bisect_usage () {
+ local code="$1" &&
+ shift &&
+ cat >expect &&
+ test_expect_code $code "$@" >out 2>actual &&
+ test_must_be_empty out &&
+ test_cmp expect actual
+}
+
+test_expect_success 'bisect usage' "
+ test_bisect_usage 1 git bisect reset extra1 extra2 <<-\EOF &&
+ error: 'git bisect reset' requires either no argument or a commit
+ EOF
+ test_bisect_usage 1 git bisect terms extra1 extra2 <<-\EOF &&
+ error: 'git bisect terms' requires 0 or 1 argument
+ EOF
+ test_bisect_usage 1 git bisect next extra1 <<-\EOF &&
+ error: 'git bisect next' requires 0 arguments
+ EOF
+ test_bisect_usage 1 git bisect log extra1 <<-\EOF &&
+ error: We are not bisecting.
+ EOF
+ test_bisect_usage 1 git bisect replay <<-\EOF &&
+ error: no logfile given
+ EOF
+ test_bisect_usage 1 git bisect run <<-\EOF
+ error: 'git bisect run' failed: no command provided.
+ EOF
+"
+
test_expect_success 'set up basic repo with 1 file (hello) and 4 commits' '
add_line_into_file "1: Hello World" hello &&
HASH1=$(git rev-parse --verify HEAD) &&
@@ -252,6 +282,124 @@
grep $HASH4 my_bisect_log.txt
'
+test_bisect_run_args () {
+ test_when_finished "rm -f run.sh actual" &&
+ >actual &&
+ cat >expect.args &&
+ cat <&6 >expect.out &&
+ cat <&7 >expect.err &&
+ write_script run.sh <<-\EOF &&
+ while test $# != 0
+ do
+ echo "<$1>" &&
+ shift
+ done >actual.args
+ EOF
+
+ test_when_finished "git bisect reset" &&
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ git bisect run ./run.sh $@ >actual.out.raw 2>actual.err &&
+ # Prune just the log output
+ sed -n \
+ -e '/^Author:/d' \
+ -e '/^Date:/d' \
+ -e '/^$/d' \
+ -e '/^commit /d' \
+ -e '/^ /d' \
+ -e 'p' \
+ <actual.out.raw >actual.out &&
+ test_cmp expect.out actual.out &&
+ test_cmp expect.err actual.err &&
+ test_cmp expect.args actual.args
+}
+
+test_expect_success 'git bisect run: args, stdout and stderr with no arguments' "
+ test_bisect_run_args <<-'EOF_ARGS' 6<<-EOF_OUT 7<<-'EOF_ERR'
+ EOF_ARGS
+ running './run.sh'
+ $HASH4 is the first bad commit
+ bisect found first bad commit
+ EOF_OUT
+ EOF_ERR
+"
+
+test_expect_success 'git bisect run: args, stdout and stderr: "--" argument' "
+ test_bisect_run_args -- <<-'EOF_ARGS' 6<<-EOF_OUT 7<<-'EOF_ERR'
+ <-->
+ EOF_ARGS
+ running './run.sh' '--'
+ $HASH4 is the first bad commit
+ bisect found first bad commit
+ EOF_OUT
+ EOF_ERR
+"
+
+test_expect_success 'git bisect run: args, stdout and stderr: "--log foo --no-log bar" arguments' "
+ test_bisect_run_args --log foo --no-log bar <<-'EOF_ARGS' 6<<-EOF_OUT 7<<-'EOF_ERR'
+ <--log>
+ <foo>
+ <--no-log>
+ <bar>
+ EOF_ARGS
+ running './run.sh' '--log' 'foo' '--no-log' 'bar'
+ $HASH4 is the first bad commit
+ bisect found first bad commit
+ EOF_OUT
+ EOF_ERR
+"
+
+test_expect_success 'git bisect run: args, stdout and stderr: "--bisect-start" argument' "
+ test_bisect_run_args --bisect-start <<-'EOF_ARGS' 6<<-EOF_OUT 7<<-'EOF_ERR'
+ <--bisect-start>
+ EOF_ARGS
+ running './run.sh' '--bisect-start'
+ $HASH4 is the first bad commit
+ bisect found first bad commit
+ EOF_OUT
+ EOF_ERR
+"
+
+test_expect_success 'git bisect run: negative exit code' "
+ write_script fail.sh <<-'EOF' &&
+ exit 255
+ EOF
+ cat <<-'EOF' >expect &&
+ bisect run failed: exit code -1 from './fail.sh' is < 0 or >= 128
+ EOF
+ test_when_finished 'git bisect reset' &&
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ ! git bisect run ./fail.sh 2>err &&
+ sed -En 's/.*(bisect.*code) (-?[0-9]+) (from.*)/\1 -1 \3/p' err >actual &&
+ test_cmp expect actual
+"
+
+test_expect_success 'git bisect run: unable to verify on good' "
+ write_script fail.sh <<-'EOF' &&
+ head=\$(git rev-parse --verify HEAD)
+ good=\$(git rev-parse --verify $HASH1)
+ if test "\$head" = "\$good"
+ then
+ exit 255
+ else
+ exit 127
+ fi
+ EOF
+ cat <<-'EOF' >expect &&
+ unable to verify './fail.sh' on good revision
+ EOF
+ test_when_finished 'git bisect reset' &&
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ ! git bisect run ./fail.sh 2>err &&
+ sed -n 's/.*\(unable to verify.*\)/\1/p' err >actual &&
+ test_cmp expect actual
+"
+
# We want to automatically find the commit that
# added "Another" into hello.
test_expect_success '"git bisect run" simple case' '
@@ -910,6 +1058,16 @@
git bisect reset
'
+test_expect_success 'bogus command does not start bisect' '
+ git bisect reset &&
+ test_must_fail git bisect --bisect-terms 1 2 2>out &&
+ ! grep "You need to start" out &&
+ test_must_fail git bisect --bisect-terms 2>out &&
+ ! grep "You need to start" out &&
+ grep "git bisect.*visualize" out &&
+ git bisect reset
+'
+
test_expect_success 'bisect replay with term1 and term2' '
git bisect replay log_to_replay.txt >bisect_result &&
grep "$HASH2 is the first term1 commit" bisect_result &&
@@ -1000,7 +1158,6 @@
test_path_is_missing ".git/BISECT_LOG" &&
test_path_is_missing ".git/BISECT_RUN" &&
test_path_is_missing ".git/BISECT_TERMS" &&
- test_path_is_missing ".git/head-name" &&
test_path_is_missing ".git/BISECT_HEAD" &&
test_path_is_missing ".git/BISECT_START"
'
@@ -1063,4 +1220,14 @@
grep -F "waiting for good commit(s), bad commit known" output
'
+test_expect_success 'verify correct error message' '
+ git bisect reset &&
+ git bisect start $HASH4 $HASH1 &&
+ write_script test_script.sh <<-\EOF &&
+ rm .git/BISECT*
+ EOF
+ test_must_fail git bisect run ./test_script.sh 2>error &&
+ grep "git bisect good.*exited with error code" error
+'
+
test_done
diff --git a/t/t6060-merge-index.sh b/t/t6060-merge-index.sh
index ed449ab..1a8b64c 100755
--- a/t/t6060-merge-index.sh
+++ b/t/t6060-merge-index.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='basic git merge-index / git-merge-one-file tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup diverging branches' '
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index 9a35e78..c9afcef 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -657,4 +657,10 @@
check_describe -C disjoint2 "B-3-gHASH" HEAD
+test_expect_success 'setup misleading taggerdates' '
+ GIT_COMMITTER_DATE="2006-12-12 12:31" git tag -a -m "another tag" newer-tag-older-commit unique-file~1
+'
+
+check_describe newer-tag-older-commit~1 --contains unique-file~2
+
test_done
diff --git a/t/t6132-pathspec-exclude.sh b/t/t6132-pathspec-exclude.sh
index cada952..9fdafeb 100755
--- a/t/t6132-pathspec-exclude.sh
+++ b/t/t6132-pathspec-exclude.sh
@@ -293,11 +293,7 @@
test_cmp expect actual
'
-test_lazy_prereq ADD_I_USE_BUILTIN_OR_PERL '
- test_have_prereq ADD_I_USE_BUILTIN || test_have_prereq PERL
-'
-
-test_expect_success ADD_I_USE_BUILTIN_OR_PERL 'add -p with all negative' '
+test_expect_success 'add -p with all negative' '
H=$(git rev-parse HEAD) &&
git reset --hard $H &&
git clean -f &&
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index fa38b87..c466fd9 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -606,7 +606,7 @@
git tag -a -m "Broken tag" taggerless &&
git tag -f taggerless $(git cat-file tag taggerless |
sed -e "/^tagger /d" |
- git hash-object --stdin -w -t tag)
+ git hash-object --literally --stdin -w -t tag)
'
test_atom refs/tags/taggerless type 'commit'
@@ -1242,6 +1242,24 @@
test_must_fail git for-each-ref --format="%(rest)" refs/heads/main
'
+test_expect_success 'HEAD atom does not take arguments' '
+ test_must_fail git for-each-ref --format="%(HEAD:foo)" 2>err &&
+ echo "fatal: %(HEAD) does not take arguments" >expect &&
+ test_cmp expect err
+'
+
+test_expect_success 'subject atom rejects unknown arguments' '
+ test_must_fail git for-each-ref --format="%(subject:foo)" 2>err &&
+ echo "fatal: unrecognized %(subject) argument: foo" >expect &&
+ test_cmp expect err
+'
+
+test_expect_success 'refname atom rejects unknown arguments' '
+ test_must_fail git for-each-ref --format="%(refname:foo)" 2>err &&
+ echo "fatal: unrecognized %(refname) argument: foo" >expect &&
+ test_cmp expect err
+'
+
test_expect_success 'trailer parsing not fooled by --- line' '
git commit --allow-empty -F - <<-\EOF &&
this is the subject
diff --git a/t/t6301-for-each-ref-errors.sh b/t/t6301-for-each-ref-errors.sh
index 40edf9d..bfda1f4 100755
--- a/t/t6301-for-each-ref-errors.sh
+++ b/t/t6301-for-each-ref-errors.sh
@@ -2,6 +2,7 @@
test_description='for-each-ref errors for broken refs'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
ZEROS=$ZERO_OID
diff --git a/t/t6401-merge-criss-cross.sh b/t/t6401-merge-criss-cross.sh
index 9d5e992..1962310 100755
--- a/t/t6401-merge-criss-cross.sh
+++ b/t/t6401-merge-criss-cross.sh
@@ -8,6 +8,8 @@
test_description='Test criss-cross merge'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'prepare repository' '
diff --git a/t/t6406-merge-attr.sh b/t/t6406-merge-attr.sh
index 8650a88..5e4e4dd 100755
--- a/t/t6406-merge-attr.sh
+++ b/t/t6406-merge-attr.sh
@@ -8,6 +8,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t6407-merge-binary.sh b/t/t6407-merge-binary.sh
index e8a2871..0753fc9 100755
--- a/t/t6407-merge-binary.sh
+++ b/t/t6407-merge-binary.sh
@@ -5,6 +5,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t6415-merge-dir-to-symlink.sh b/t/t6415-merge-dir-to-symlink.sh
index 2655e29..ae00492 100755
--- a/t/t6415-merge-dir-to-symlink.sh
+++ b/t/t6415-merge-dir-to-symlink.sh
@@ -4,6 +4,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'create a commit where dir a/b changed to symlink' '
diff --git a/t/t6421-merge-partial-clone.sh b/t/t6421-merge-partial-clone.sh
index 5413e5d..711b709 100755
--- a/t/t6421-merge-partial-clone.sh
+++ b/t/t6421-merge-partial-clone.sh
@@ -155,7 +155,7 @@
# 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 @@
# 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/t6422-merge-rename-corner-cases.sh b/t/t6422-merge-rename-corner-cases.sh
index 346253c..076b6a7 100755
--- a/t/t6422-merge-rename-corner-cases.sh
+++ b/t/t6422-merge-rename-corner-cases.sh
@@ -1159,7 +1159,6 @@
# 4) There should not be any three~* files in the working
# tree
test_setup_collision_conflict () {
- #test_expect_success "setup simple $sideL/$sideR conflict" '
git init simple_${sideL}_${sideR} &&
(
cd simple_${sideL}_${sideR} &&
@@ -1236,7 +1235,6 @@
fi &&
test_tick && git commit -m R
)
- #'
}
test_expect_success "check simple $sideL/$sideR conflict" '
diff --git a/t/t6426-merge-skip-unneeded-updates.sh b/t/t6426-merge-skip-unneeded-updates.sh
index 2bb8e7f..fd21c1a 100755
--- a/t/t6426-merge-skip-unneeded-updates.sh
+++ b/t/t6426-merge-skip-unneeded-updates.sh
@@ -378,42 +378,30 @@
test_i18ngrep "CONFLICT (.*/add):" out &&
test_must_be_empty err &&
- # Make sure c WAS updated
+ git ls-files -s >index_files &&
+ test_line_count = 2 index_files &&
+
+ # Ensure b was removed
+ test_path_is_missing b &&
+
+ # Make sure c WAS updated...
test-tool chmtime --get c >new-mtime &&
- test $(cat old-mtime) -lt $(cat new-mtime)
+ test $(cat old-mtime) -lt $(cat new-mtime) &&
- # FIXME: rename/add conflicts are horribly broken right now;
- # when I get back to my patch series fixing it and
- # rename/rename(2to1) conflicts to bring them in line with
- # how add/add conflicts behave, then checks like the below
- # could be added. But that patch series is waiting until
- # the rename-directory-detection series lands, which this
- # is part of. And in the mean time, I do not want to further
- # enforce broken behavior. So for now, the main test is the
- # one above that err is an empty file.
+ # ...and has correct index entries and working tree contents
+ git rev-parse >actual :2:c :3:c &&
+ git rev-parse >expect A:c A:b &&
+ test_cmp expect actual &&
- #git ls-files -s >index_files &&
- #test_line_count = 2 index_files &&
-
- #git rev-parse >actual :2:c :3:c &&
- #git rev-parse >expect A:b A:c &&
- #test_cmp expect actual &&
-
- #git cat-file -p A:b >>merged &&
- #git cat-file -p A:c >>merge-me &&
- #>empty &&
- #test_must_fail git merge-file \
- # -L "Temporary merge branch 1" \
- # -L "" \
- # -L "Temporary merge branch 2" \
- # merged empty merge-me &&
- #sed -e "s/^\([<=>]\)/\1\1\1/" merged >merged-internal &&
-
- #git hash-object c >actual &&
- #git hash-object merged-internal >expect &&
- #test_cmp expect actual &&
-
- #test_path_is_missing b
+ git cat-file -p A:b >>merge-me &&
+ git cat-file -p A:c >>merged &&
+ >empty &&
+ test_must_fail git merge-file \
+ -L "HEAD" \
+ -L "" \
+ -L "B^0" \
+ merged empty merge-me &&
+ test_cmp merged c
)
'
diff --git a/t/t6435-merge-sparse.sh b/t/t6435-merge-sparse.sh
index fde4aa3..78628fb 100755
--- a/t/t6435-merge-sparse.sh
+++ b/t/t6435-merge-sparse.sh
@@ -3,6 +3,7 @@
test_description='merge with sparse files'
TEST_CREATE_REPO_NO_TEMPLATE=1
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# test_file $filename $content
diff --git a/t/t6439-merge-co-error-msgs.sh b/t/t6439-merge-co-error-msgs.sh
index 52cf0c8..0cbec57 100755
--- a/t/t6439-merge-co-error-msgs.sh
+++ b/t/t6439-merge-co-error-msgs.sh
@@ -5,6 +5,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
diff --git a/t/t6501-freshen-objects.sh b/t/t6501-freshen-objects.sh
index 1066245..3968b47 100755
--- a/t/t6501-freshen-objects.sh
+++ b/t/t6501-freshen-objects.sh
@@ -28,6 +28,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# We care about reachability, so we do not want to use
diff --git a/t/t7030-verify-tag.sh b/t/t7030-verify-tag.sh
index 10faa64..6f526c3 100755
--- a/t/t7030-verify-tag.sh
+++ b/t/t7030-verify-tag.sh
@@ -115,7 +115,7 @@
test_expect_success GPG 'detect fudged signature' '
git cat-file tag seventh-signed >raw &&
- sed -e "/^tag / s/seventh/7th forged/" raw >forged1 &&
+ sed -e "/^tag / s/seventh/7th-forged/" raw >forged1 &&
git hash-object -w -t tag forged1 >forged1.tag &&
test_must_fail git verify-tag $(cat forged1.tag) 2>actual1 &&
grep "BAD signature from" actual1 &&
diff --git a/t/t7031-verify-tag-signed-ssh.sh b/t/t7031-verify-tag-signed-ssh.sh
index 1cb36b9..36eb86a 100755
--- a/t/t7031-verify-tag-signed-ssh.sh
+++ b/t/t7031-verify-tag-signed-ssh.sh
@@ -125,7 +125,7 @@
test_expect_success GPGSSH 'detect fudged ssh signature' '
test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
git cat-file tag seventh-signed >raw &&
- sed -e "/^tag / s/seventh/7th forged/" raw >forged1 &&
+ sed -e "/^tag / s/seventh/7th-forged/" raw >forged1 &&
git hash-object -w -t tag forged1 >forged1.tag &&
test_must_fail git verify-tag $(cat forged1.tag) 2>actual1 &&
grep "${GPGSSH_BAD_SIGNATURE}" actual1 &&
diff --git a/t/t7103-reset-bare.sh b/t/t7103-reset-bare.sh
index a60153f..18bbd99 100755
--- a/t/t7103-reset-bare.sh
+++ b/t/t7103-reset-bare.sh
@@ -63,7 +63,7 @@
test_must_fail git reset --mixed HEAD^
'
-test_expect_success !SANITIZE_LEAK '"soft" reset is allowed in bare' '
+test_expect_success '"soft" reset is allowed in bare' '
git reset --soft HEAD^ &&
git show --pretty=format:%s >out &&
echo one >expect &&
diff --git a/t/t7105-reset-patch.sh b/t/t7105-reset-patch.sh
index fc2a6cf..9b46da7 100755
--- a/t/t7105-reset-patch.sh
+++ b/t/t7105-reset-patch.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='git reset --patch'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-patch-mode.sh
test_expect_success PERL 'setup' '
diff --git a/t/t7106-reset-unborn-branch.sh b/t/t7106-reset-unborn-branch.sh
index ecb85c3..a0b67a0 100755
--- a/t/t7106-reset-unborn-branch.sh
+++ b/t/t7106-reset-unborn-branch.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='git reset should work on unborn branch'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t7107-reset-pathspec-file.sh b/t/t7107-reset-pathspec-file.sh
index 523efbe..af5ea40 100755
--- a/t/t7107-reset-pathspec-file.sh
+++ b/t/t7107-reset-pathspec-file.sh
@@ -2,6 +2,7 @@
test_description='reset --pathspec-from-file'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_tick
diff --git a/t/t7301-clean-interactive.sh b/t/t7301-clean-interactive.sh
index a07e8b8..d82a321 100755
--- a/t/t7301-clean-interactive.sh
+++ b/t/t7301-clean-interactive.sh
@@ -2,6 +2,7 @@
test_description='git clean -i basic tests'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
diff --git a/t/t7402-submodule-rebase.sh b/t/t7402-submodule-rebase.sh
index ebeca12..b19792b 100755
--- a/t/t7402-submodule-rebase.sh
+++ b/t/t7402-submodule-rebase.sh
@@ -5,6 +5,7 @@
test_description='Test rebasing, stashing, etc. with submodules'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t7403-submodule-sync.sh b/t/t7403-submodule-sync.sh
index ea92ef5..ff09443 100755
--- a/t/t7403-submodule-sync.sh
+++ b/t/t7403-submodule-sync.sh
@@ -11,6 +11,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t7409-submodule-detached-work-tree.sh b/t/t7409-submodule-detached-work-tree.sh
index 374ed48..574a6fc 100755
--- a/t/t7409-submodule-detached-work-tree.sh
+++ b/t/t7409-submodule-detached-work-tree.sh
@@ -13,6 +13,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh
index 2859695..f778321 100755
--- a/t/t7412-submodule-absorbgitdirs.sh
+++ b/t/t7412-submodule-absorbgitdirs.sh
@@ -10,6 +10,7 @@
. ./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 '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 '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 '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 '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_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 '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 '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/t7416-submodule-dash-url.sh b/t/t7416-submodule-dash-url.sh
index 3ebd985..7cf72b9 100755
--- a/t/t7416-submodule-dash-url.sh
+++ b/t/t7416-submodule-dash-url.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='check handling of disallowed .gitmodule urls'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t7450-bad-git-dotfiles.sh b/t/t7450-bad-git-dotfiles.sh
index ba1f569..0d0c3f2 100755
--- a/t/t7450-bad-git-dotfiles.sh
+++ b/t/t7450-bad-git-dotfiles.sh
@@ -12,6 +12,8 @@
- symlinked .gitmodules, etc
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-pack.sh
diff --git a/t/t7504-commit-msg-hook.sh b/t/t7504-commit-msg-hook.sh
index a39de8c..07ca46f 100755
--- a/t/t7504-commit-msg-hook.sh
+++ b/t/t7504-commit-msg-hook.sh
@@ -5,6 +5,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'with no hook' '
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index 2b7ef6c..aed07c5 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -1676,4 +1676,74 @@
! test_is_magic_mtime .git/index
'
+test_expect_success 'setup slow status advice' '
+ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main git init slowstatus &&
+ (
+ cd slowstatus &&
+ cat >.gitignore <<-\EOF &&
+ /actual
+ /expected
+ /out
+ EOF
+ git add .gitignore &&
+ git commit -m "Add .gitignore" &&
+ git config advice.statusuoption true
+ )
+'
+
+test_expect_success 'slow status advice when core.untrackedCache and fsmonitor are unset' '
+ (
+ cd slowstatus &&
+ git config core.untrackedCache false &&
+ git config core.fsmonitor false &&
+ GIT_TEST_UF_DELAY_WARNING=1 git status >actual &&
+ cat >expected <<-\EOF &&
+ On branch main
+
+ It took 3.25 seconds to enumerate untracked files.
+ See '\''git help status'\'' for information on how to improve this.
+
+ nothing to commit, working tree clean
+ EOF
+ test_cmp expected actual
+ )
+'
+
+test_expect_success 'slow status advice when core.untrackedCache true, but not fsmonitor' '
+ (
+ cd slowstatus &&
+ git config core.untrackedCache true &&
+ git config core.fsmonitor false &&
+ GIT_TEST_UF_DELAY_WARNING=1 git status >actual &&
+ cat >expected <<-\EOF &&
+ On branch main
+
+ It took 3.25 seconds to enumerate untracked files.
+ See '\''git help status'\'' for information on how to improve this.
+
+ nothing to commit, working tree clean
+ EOF
+ test_cmp expected actual
+ )
+'
+
+test_expect_success 'slow status advice when core.untrackedCache true, and fsmonitor' '
+ (
+ cd slowstatus &&
+ git config core.untrackedCache true &&
+ git config core.fsmonitor true &&
+ GIT_TEST_UF_DELAY_WARNING=1 git status >actual &&
+ cat >expected <<-\EOF &&
+ On branch main
+
+ It took 3.25 seconds to enumerate untracked files,
+ but the results were cached, and subsequent runs may be faster.
+ See '\''git help status'\'' for information on how to improve this.
+
+ nothing to commit, working tree clean
+ EOF
+ test_cmp expected actual
+ )
+'
+
test_done
diff --git a/t/t7509-commit-authorship.sh b/t/t7509-commit-authorship.sh
index 21c668f..5d89094 100755
--- a/t/t7509-commit-authorship.sh
+++ b/t/t7509-commit-authorship.sh
@@ -105,7 +105,7 @@
test_expect_success '--amend option with missing author' '
git cat-file commit Initial >tmp &&
sed "s/author [^<]* </author </" tmp >malformed &&
- sha=$(git hash-object -t commit -w malformed) &&
+ sha=$(git hash-object --literally -t commit -w malformed) &&
test_when_finished "remove_object $sha" &&
git checkout $sha &&
test_when_finished "git checkout Initial" &&
diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh
index 8593b7e..48f86cb 100755
--- a/t/t7510-signed-commit.sh
+++ b/t/t7510-signed-commit.sh
@@ -202,7 +202,7 @@
git cat-file commit seventh-signed >raw &&
cat raw >forged2 &&
echo Qwik | tr "Q" "\000" >>forged2 &&
- git hash-object -w -t commit forged2 >forged2.commit &&
+ git hash-object --literally -w -t commit forged2 >forged2.commit &&
test_must_fail git verify-commit $(cat forged2.commit) &&
git show --pretty=short --show-signature $(cat forged2.commit) >actual2 &&
grep "BAD signature from" actual2 &&
@@ -387,4 +387,48 @@
! grep "BAD signature from" actual
'
+test_expect_success 'custom `gpg.program`' '
+ write_script fake-gpg <<-\EOF &&
+ args="$*"
+
+ # skip uninteresting options
+ while case "$1" in
+ --status-fd=*|--keyid-format=*) ;; # skip
+ *) break;;
+ esac; do shift; done
+
+ case "$1" in
+ -bsau)
+ test -z "$LET_GPG_PROGRAM_FAIL" || {
+ echo "zOMG signing failed!" >&2
+ exit 1
+ }
+ cat >sign.file
+ echo "[GNUPG:] SIG_CREATED $args" >&2
+ echo "-----BEGIN PGP MESSAGE-----"
+ echo "$args"
+ echo "-----END PGP MESSAGE-----"
+ ;;
+ --verify)
+ cat "$2" >verify.file
+ exit 0
+ ;;
+ *)
+ echo "Unhandled args: $*" >&2
+ exit 1
+ ;;
+ esac
+ EOF
+
+ test_config gpg.program "$(pwd)/fake-gpg" &&
+ git commit -S --allow-empty -m signed-commit &&
+ test_path_exists sign.file &&
+ git show --show-signature &&
+ test_path_exists verify.file &&
+
+ test_must_fail env LET_GPG_PROGRAM_FAIL=1 \
+ git commit -S --allow-empty -m must-fail 2>err &&
+ grep zOMG err
+'
+
test_done
diff --git a/t/t7517-per-repo-email.sh b/t/t7517-per-repo-email.sh
index 163ae80..efc6496 100755
--- a/t/t7517-per-repo-email.sh
+++ b/t/t7517-per-repo-email.sh
@@ -9,6 +9,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup a likely user.useConfigOnly use case' '
diff --git a/t/t7520-ignored-hook-warning.sh b/t/t7520-ignored-hook-warning.sh
index dc57526..184b258 100755
--- a/t/t7520-ignored-hook-warning.sh
+++ b/t/t7520-ignored-hook-warning.sh
@@ -2,6 +2,7 @@
test_description='ignored hook warning'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t7527-builtin-fsmonitor.sh b/t/t7527-builtin-fsmonitor.sh
index 4abc74d..4c0327b 100755
--- a/t/t7527-builtin-fsmonitor.sh
+++ b/t/t7527-builtin-fsmonitor.sh
@@ -866,27 +866,9 @@
# 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.
+# try to start the daemon in the submodule.
-have_t2_error_event () {
- log=$1
- msg="fsmonitor--daemon doesnQt support --super-prefix" &&
-
- tr '\047' Q <$1 | grep -e "$msg"
-}
-
-test_expect_success "stray submodule super-prefix warning" '
+test_expect_success "submodule absorbgitdirs implicitly starts daemon" '
test_when_finished "rm -rf super; \
rm -rf sub; \
rm super-sub.trace" &&
@@ -904,21 +886,31 @@
test_path_is_dir super/dir_1/dir_2/sub/.git &&
+ cwd="$(cd super && pwd)" &&
+ cat >expect <<-EOF &&
+ Migrating git directory of '\''dir_1/dir_2/sub'\'' from
+ '\''$cwd/dir_1/dir_2/sub/.git'\'' to
+ '\''$cwd/.git/modules/dir_1/dir_2/sub'\''
+ EOF
GIT_TRACE2_EVENT="$PWD/super-sub.trace" \
- git -C super submodule absorbgitdirs &&
+ git -C super submodule absorbgitdirs >out 2>actual &&
+ test_cmp expect actual &&
+ test_must_be_empty out &&
- ! have_t2_error_event super-sub.trace
+ # Confirm that the trace2 log contains a record of the
+ # daemon starting.
+ test_subcommand git fsmonitor--daemon start <super-sub.trace
'
# On a case-insensitive file system, confirm that the daemon
# notices when the .git directory is moved/renamed/deleted
-# regardless of how it is spelled in the the FS event.
+# regardless of how it is spelled in the FS event.
# That is, does the FS event receive the spelling of the
# operation or does it receive the spelling preserved with
# the file/directory.
#
test_expect_success CASE_INSENSITIVE_FS 'case insensitive+preserving' '
-# test_when_finished "stop_daemon_delete_repo test_insensitive" &&
+ test_when_finished "stop_daemon_delete_repo test_insensitive" &&
git init test_insensitive &&
@@ -930,8 +922,8 @@
test_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/t7528-signed-commit-ssh.sh b/t/t7528-signed-commit-ssh.sh
index f47e995..065f780 100755
--- a/t/t7528-signed-commit-ssh.sh
+++ b/t/t7528-signed-commit-ssh.sh
@@ -270,7 +270,7 @@
git cat-file commit seventh-signed >raw &&
cat raw >forged2 &&
echo Qwik | tr "Q" "\000" >>forged2 &&
- git hash-object -w -t commit forged2 >forged2.commit &&
+ git hash-object --literally -w -t commit forged2 >forged2.commit &&
test_must_fail git verify-commit $(cat forged2.commit) &&
git show --pretty=short --show-signature $(cat forged2.commit) >actual2 &&
grep "${GPGSSH_BAD_SIGNATURE}" actual2 &&
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index 7c3f6ed..060e145 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -105,7 +105,7 @@
test_write_lines "$@" >mergehead.expected &&
while read sha1 rest
do
- git rev-parse $sha1
+ git rev-parse $sha1 || return 1
done <.git/MERGE_HEAD >mergehead.actual &&
test_cmp mergehead.expected mergehead.actual
}
diff --git a/t/t7605-merge-resolve.sh b/t/t7605-merge-resolve.sh
index 5d56c38..62d935d 100755
--- a/t/t7605-merge-resolve.sh
+++ b/t/t7605-merge-resolve.sh
@@ -4,6 +4,7 @@
Testing the resolve strategy.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t7612-merge-verify-signatures.sh b/t/t7612-merge-verify-signatures.sh
index 61330f7..f5c90cc 100755
--- a/t/t7612-merge-verify-signatures.sh
+++ b/t/t7612-merge-verify-signatures.sh
@@ -4,6 +4,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY/lib-gpg.sh"
diff --git a/t/t7614-merge-signoff.sh b/t/t7614-merge-signoff.sh
index fee258d..cf96a35 100755
--- a/t/t7614-merge-signoff.sh
+++ b/t/t7614-merge-signoff.sh
@@ -8,6 +8,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Setup test files
diff --git a/t/t7701-repack-unpack-unreachable.sh b/t/t7701-repack-unpack-unreachable.sh
index b7ac4f5..ebb2678 100755
--- a/t/t7701-repack-unpack-unreachable.sh
+++ b/t/t7701-repack-unpack-unreachable.sh
@@ -5,6 +5,7 @@
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
fsha1=
diff --git a/t/t8003-blame-corner-cases.sh b/t/t8003-blame-corner-cases.sh
index d751d48..8bcd39e 100755
--- a/t/t8003-blame-corner-cases.sh
+++ b/t/t8003-blame-corner-cases.sh
@@ -201,7 +201,7 @@
some message
EOF
- COMMIT=$(git hash-object -t commit -w badcommit) &&
+ COMMIT=$(git hash-object --literally -t commit -w badcommit) &&
git --no-pager blame $COMMIT -- uno >/dev/null
'
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 1130ef2..323952a 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -2334,6 +2334,12 @@
"$(pwd)/0001-add-main.patch"
'
+test_expect_success $PREREQ 'send-email relays -v 3 to format-patch' '
+ test_when_finished "rm -f out" &&
+ git send-email --dry-run -v 3 -1 >out &&
+ grep "PATCH v3" out
+'
+
test_expect_success $PREREQ 'test that sendmail config is rejected' '
test_config sendmail.program sendmail &&
test_must_fail git send-email \
diff --git a/t/t9003-help-autocorrect.sh b/t/t9003-help-autocorrect.sh
index f00deaf..14a704d 100755
--- a/t/t9003-help-autocorrect.sh
+++ b/t/t9003-help-autocorrect.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='help.autocorrect finding a match'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -60,4 +62,10 @@
test_line_count = 1 actual
'
+test_expect_success 'autocorrect works in work tree created from bare repo' '
+ git clone --bare . bare.git &&
+ git -C bare.git worktree add ../worktree &&
+ git -C worktree -c help.autocorrect=immediate stauts
+'
+
test_done
diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh
index 3cab0b9..bca496c 100755
--- a/t/t9106-git-svn-commit-diff-clobber.sh
+++ b/t/t9106-git-svn-commit-diff-clobber.sh
@@ -3,7 +3,6 @@
# Copyright (c) 2006 Eric Wong
test_description='git svn commit-diff clobber'
-TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
test_expect_success 'initialize repo' '
diff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh
index 419f055..743fbe1 100755
--- a/t/t9115-git-svn-dcommit-funky-renames.sh
+++ b/t/t9115-git-svn-dcommit-funky-renames.sh
@@ -5,7 +5,6 @@
test_description='git svn dcommit can commit renames of files with ugly names'
-TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
test_expect_success 'load repository with strange names' '
diff --git a/t/t9119-git-svn-info.sh b/t/t9119-git-svn-info.sh
index 8201c3e..088d1c5 100755
--- a/t/t9119-git-svn-info.sh
+++ b/t/t9119-git-svn-info.sh
@@ -28,7 +28,7 @@
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/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh
index 79c26ed..09606f1 100755
--- a/t/t9146-git-svn-empty-dirs.sh
+++ b/t/t9146-git-svn-empty-dirs.sh
@@ -4,7 +4,6 @@
test_description='git svn creates empty directories'
-TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
test_expect_success 'initialize repo' '
diff --git a/t/t9148-git-svn-propset.sh b/t/t9148-git-svn-propset.sh
index 6cc76a0..aebb289 100755
--- a/t/t9148-git-svn-propset.sh
+++ b/t/t9148-git-svn-propset.sh
@@ -5,7 +5,6 @@
test_description='git svn propset tests'
-TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
test_expect_success 'setup propset via import' '
diff --git a/t/t9160-git-svn-preserve-empty-dirs.sh b/t/t9160-git-svn-preserve-empty-dirs.sh
index 9cf7a14..36c6b1a 100755
--- a/t/t9160-git-svn-preserve-empty-dirs.sh
+++ b/t/t9160-git-svn-preserve-empty-dirs.sh
@@ -9,7 +9,6 @@
directories, and checks that corresponding directories are created in the
local Git repository with placeholder files.'
-TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
GIT_REPO=git-svn-repo
diff --git a/t/t9164-git-svn-dcommit-concurrent.sh b/t/t9164-git-svn-dcommit-concurrent.sh
index 1465156..c8e6c07 100755
--- a/t/t9164-git-svn-dcommit-concurrent.sh
+++ b/t/t9164-git-svn-dcommit-concurrent.sh
@@ -5,7 +5,6 @@
test_description='concurrent git svn dcommit'
-TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh
diff --git a/t/t9210-scalar.sh b/t/t9210-scalar.sh
index 25f500c..4432a30 100755
--- a/t/t9210-scalar.sh
+++ b/t/t9210-scalar.sh
@@ -104,6 +104,13 @@
test_cmp_config -C test/src true core.fsmonitor
'
+test_expect_success 'scalar register warns when background maintenance fails' '
+ git init register-repo &&
+ GIT_TEST_MAINT_SCHEDULER="crontab:false,launchctl:false,schtasks:false" \
+ scalar register register-repo 2>err &&
+ grep "could not turn on maintenance" err
+'
+
test_expect_success 'scalar unregister' '
git init vanish/src &&
scalar register vanish/src &&
diff --git a/t/t9211-scalar-clone.sh b/t/t9211-scalar-clone.sh
index dd33d87..872ad1c 100755
--- a/t/t9211-scalar-clone.sh
+++ b/t/t9211-scalar-clone.sh
@@ -3,6 +3,7 @@
test_description='test the `scalar clone` subcommand'
. ./test-lib.sh
+. "${TEST_DIRECTORY}/lib-terminal.sh"
GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt,launchctl:true,schtasks:true"
export GIT_TEST_MAINT_SCHEDULER
@@ -148,4 +149,35 @@
cleanup_clone $enlistment
'
+test_expect_success TTY 'progress with tty' '
+ enlistment=progress1 &&
+
+ test_config -C to-clone uploadpack.allowfilter true &&
+ test_config -C to-clone uploadpack.allowanysha1inwant true &&
+
+ test_terminal env GIT_PROGRESS_DELAY=0 \
+ scalar clone "file://$(pwd)/to-clone" "$enlistment" 2>stderr &&
+ grep "Enumerating objects" stderr >actual &&
+ test_line_count = 2 actual &&
+ cleanup_clone $enlistment
+'
+
+test_expect_success 'progress without tty' '
+ enlistment=progress2 &&
+
+ test_config -C to-clone uploadpack.allowfilter true &&
+ test_config -C to-clone uploadpack.allowanysha1inwant true &&
+
+ GIT_PROGRESS_DELAY=0 scalar clone "file://$(pwd)/to-clone" "$enlistment" 2>stderr &&
+ ! grep "Enumerating objects" stderr &&
+ ! grep "Updating files" stderr &&
+ cleanup_clone $enlistment
+'
+
+test_expect_success 'scalar clone warns when background maintenance fails' '
+ GIT_TEST_MAINT_SCHEDULER="crontab:false,launchctl:false,schtasks:false" \
+ scalar clone "file://$(pwd)/to-clone" maint-fail 2>err &&
+ grep "could not turn on maintenance" err
+'
+
test_done
diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh
index ff21a12..26c25c0 100755
--- a/t/t9350-fast-export.sh
+++ b/t/t9350-fast-export.sh
@@ -373,7 +373,7 @@
test_expect_success 'cope with tagger-less tags' '
- TAG=$(git hash-object -t tag -w tag-content) &&
+ TAG=$(git hash-object --literally -t tag -w tag-content) &&
git update-ref refs/tags/sonnenschein $TAG &&
git fast-export -C -C --signed-tags=strip --all > output &&
test $(grep -c "^tag " output) = 4 &&
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 43de868..d6c0478 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -2255,6 +2255,36 @@
EOF
'
+test_expect_success 'checkout does not match ref names of a different case' '
+ test_completion "git checkout M" ""
+'
+
+test_expect_success 'checkout matches case insensitively with GIT_COMPLETION_IGNORE_CASE' '
+ (
+ GIT_COMPLETION_IGNORE_CASE=1 &&
+ test_completion "git checkout M" <<-\EOF
+ main Z
+ mybranch Z
+ mytag Z
+ EOF
+ )
+'
+
+test_expect_success 'checkout completes pseudo refs' '
+ test_completion "git checkout H" <<-\EOF
+ HEAD Z
+ EOF
+'
+
+test_expect_success 'checkout completes pseudo refs case insensitively with GIT_COMPLETION_IGNORE_CASE' '
+ (
+ GIT_COMPLETION_IGNORE_CASE=1 &&
+ test_completion "git checkout h" <<-\EOF
+ HEAD Z
+ EOF
+ )
+'
+
test_expect_success 'git -C <path> checkout uses the right repo' '
test_completion "git -C subdir -C subsubdir -C .. -C ../otherrepo checkout b" <<-\EOF
branch-in-other Z
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 796093a..58cfd2f 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1016,7 +1016,7 @@
fi
case "$1" in
- git|__git*|test-tool|test_terminal)
+ git|__git*|scalar|test-tool|test_terminal)
return 0
;;
*)
@@ -1422,7 +1422,7 @@
BUG "test_bool_env requires two parameters (variable name and default value)"
fi
- git env--helper --type=bool --default="$2" --exit-code "$1"
+ test-tool env-helper --type=bool --default="$2" --exit-code "$1"
ret=$?
case $ret in
0|1) # unset or valid bool value
@@ -1450,72 +1450,6 @@
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 @@
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 @@
return 1
}
+# Poor man's URI escaping. Good enough for the test suite whose trash
+# directory has a space in it. See 93c3fcbe4d4 (git-svn: attempt to
+# mimic SVN 1.7 URL canonicalization, 2012-07-28) for prior art.
+test_uri_escape() {
+ sed 's/ /%20/g'
+}
+
# Check that the given command was invoked as part of the
# trace2-format trace on stdin.
#
@@ -1826,6 +1767,14 @@
return 0
}
+# Given a GIT_TRACE2_EVENT log over stdin, writes to stdout a list of URLs
+# sent to git-remote-https child processes.
+test_remote_https_urls() {
+ grep -e '"event":"child_start".*"argv":\["git-remote-https",".*"\]' |
+ sed -e 's/{"event":"child_start".*"argv":\["git-remote-https","//g' \
+ -e 's/"\]}//g'
+}
+
# Print the destination of symlink(s) provided as arguments. Basically
# the same as the readlink command, but it's not available everywhere.
test_readlink () {
@@ -1875,3 +1824,11 @@
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 6db377f..d272cca 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -1542,8 +1542,8 @@
# Normalize with test_bool_env
passes_sanitize_leak=
- # We need to see TEST_PASSES_SANITIZE_LEAK in "git
- # env--helper" (via test_bool_env)
+ # We need to see TEST_PASSES_SANITIZE_LEAK in "test-tool
+ # env-helper" (via test_bool_env)
export TEST_PASSES_SANITIZE_LEAK
if test_bool_env TEST_PASSES_SANITIZE_LEAK false
then
@@ -1682,7 +1682,7 @@
# The GIT_TEST_FAIL_PREREQS code hooks into test_set_prereq(), and
# thus needs to be set up really early, and set an internal variable
# for convenience so the hot test_set_prereq() codepath doesn't need
-# to call "git env--helper" (via test_bool_env). Only do that work
+# to call "test-tool env-helper" (via test_bool_env). Only do that work
# if needed by seeing if GIT_TEST_FAIL_PREREQS is set at all.
GIT_TEST_FAIL_PREREQS_INTERNAL=
if test -n "$GIT_TEST_FAIL_PREREQS"
@@ -1721,7 +1721,7 @@
test_set_prereq SED_STRIPS_CR
test_set_prereq GREP_STRIPS_CR
test_set_prereq WINDOWS
- GIT_TEST_CMP=mingw_test_cmp
+ GIT_TEST_CMP="GIT_DIR=/dev/null git diff --no-index --ignore-cr-at-eol --"
;;
*CYGWIN*)
test_set_prereq POSIXPERM
@@ -1937,10 +1937,6 @@
esac
'
-test_lazy_prereq ADD_I_USE_BUILTIN '
- test_bool_env GIT_TEST_ADD_I_USE_BUILTIN true
-'
-
# Ensure that no test accidentally triggers a Git command
# that runs the actual maintenance scheduler, affecting a user's
# system permanently.
diff --git a/transport-helper.c b/transport-helper.c
index e95267a..3ea7c2b 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -1267,9 +1267,22 @@
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 c4ca0b7..90ea749 100644
--- a/transport-internal.h
+++ b/transport-internal.h
@@ -27,6 +27,13 @@
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 e7b9719..77a61a9 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_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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
else
ret = finish_connect(data->conn);
data->conn = NULL;
- data->got_remote_heads = 0;
+ data->finished_handshake = 0;
return ret;
}
@@ -887,7 +921,7 @@
{
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 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 @@
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 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 @@
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 @@
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 @@
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 @@
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 b5bf7b3..85150f5 100644
--- a/transport.h
+++ b/transport.h
@@ -62,6 +62,7 @@
TRANSPORT_FAMILY_IPV6
};
+struct bundle_list;
struct transport {
const struct transport_vtable *vtable;
@@ -76,6 +77,18 @@
*/
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 @@
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 8a762aa..3d05e45 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -19,7 +19,6 @@
#include "promisor-remote.h"
#include "entry.h"
#include "parallel-checkout.h"
-#include "sparse-index.h"
/*
* Error messages expected by scripts out of plumbing commands such as
@@ -71,7 +70,7 @@
? ((o)->msgs[(type)]) \
: (unpack_plumbing_errors[(type)]) )
-static const char *super_prefixed(const char *path)
+static const char *super_prefixed(const char *path, const char *super_prefix)
{
/*
* It is necessary and sufficient to have two static buffers
@@ -83,7 +82,6 @@
static unsigned idx = ARRAY_SIZE(buf) - 1;
if (super_prefix_len < 0) {
- const char *super_prefix = get_super_prefix();
if (!super_prefix) {
super_prefix_len = 0;
} else {
@@ -236,7 +234,8 @@
return -1;
if (!o->show_all_errors)
- return error(ERRORMSG(o, e), super_prefixed(path));
+ return error(ERRORMSG(o, e), super_prefixed(path,
+ o->super_prefix));
/*
* Otherwise, insert in a list for future display by
@@ -263,7 +262,8 @@
error_displayed = 1;
for (i = 0; i < rejects->nr; i++)
strbuf_addf(&path, "\t%s\n", rejects->items[i].string);
- error(ERRORMSG(o, e), super_prefixed(path.buf));
+ error(ERRORMSG(o, e), super_prefixed(path.buf,
+ o->super_prefix));
strbuf_release(&path);
}
string_list_clear(rejects, 0);
@@ -290,7 +290,8 @@
warning_displayed = 1;
for (i = 0; i < rejects->nr; i++)
strbuf_addf(&path, "\t%s\n", rejects->items[i].string);
- warning(ERRORMSG(o, e), super_prefixed(path.buf));
+ warning(ERRORMSG(o, e), super_prefixed(path.buf,
+ o->super_prefix));
strbuf_release(&path);
}
string_list_clear(rejects, 0);
@@ -312,7 +313,8 @@
if (o->reset)
flags |= SUBMODULE_MOVE_HEAD_FORCE;
- if (submodule_move_head(ce->name, old_id, new_id, flags))
+ if (submodule_move_head(ce->name, o->super_prefix, old_id, new_id,
+ flags))
return add_rejected_path(o, ERROR_WOULD_LOSE_SUBMODULE, ce->name);
return 0;
}
@@ -415,6 +417,7 @@
int i, pc_workers, pc_threshold;
trace_performance_enter();
+ state.super_prefix = o->super_prefix;
state.force = 1;
state.quiet = 1;
state.refresh_cache = 1;
@@ -445,7 +448,7 @@
if (ce->ce_flags & CE_WT_REMOVE) {
display_progress(progress, ++cnt);
- unlink_entry(ce);
+ unlink_entry(ce, o->super_prefix);
}
}
@@ -1877,7 +1880,8 @@
prepare_repo_settings(repo);
if (repo->settings.command_requires_full_index) {
ensure_full_index(o->src_index);
- ensure_full_index(o->dst_index);
+ if (o->dst_index)
+ ensure_full_index(o->dst_index);
}
if (o->reset == UNPACK_RESET_OVERWRITE_UNTRACKED &&
@@ -1901,7 +1905,7 @@
populate_from_existing_patterns(o, &pl);
}
- memset(&o->result, 0, sizeof(o->result));
+ index_state_init(&o->result, o->src_index->repo);
o->result.initialized = 1;
o->result.timestamp.sec = o->src_index->timestamp.sec;
o->result.timestamp.nsec = o->src_index->timestamp.nsec;
@@ -2959,8 +2963,8 @@
if (a && old)
return o->quiet ? -1 :
error(ERRORMSG(o, ERROR_BIND_OVERLAP),
- super_prefixed(a->name),
- super_prefixed(old->name));
+ super_prefixed(a->name, o->super_prefix),
+ super_prefixed(old->name, o->super_prefix));
if (!a)
return keep_entry(old, o);
else
@@ -3021,7 +3025,7 @@
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 6ab0d74..3a7b3e5 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -75,6 +75,7 @@
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 b615adc..620a648 100644
--- a/urlmatch.c
+++ b/urlmatch.c
@@ -209,7 +209,7 @@
*/
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 @@
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/userdiff.c b/userdiff.c
index 151d9a5..94cca1a 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -170,8 +170,8 @@
"[^<>= \t]+"),
PATTERNS("java",
"!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
- /* Class, enum, and interface declarations */
- "^[ \t]*(([a-z]+[ \t]+)*(class|enum|interface)[ \t]+[A-Za-z][A-Za-z0-9_$]*[ \t]+.*)$\n"
+ /* Class, enum, interface, and record declarations */
+ "^[ \t]*(([a-z-]+[ \t]+)*(class|enum|interface|record)[ \t]+.*)$\n"
/* Method definitions; note that constructor signatures are not */
/* matched because they are indistinguishable from method calls. */
"^[ \t]*(([A-Za-z_<>&][][?&<>.,A-Za-z_0-9]*[ \t]+)+[A-Za-z_][A-Za-z_0-9]*[ \t]*\\([^;]*)$",
@@ -315,7 +315,8 @@
};
static int userdiff_find_by_namelen_cb(struct userdiff_driver *driver,
- enum userdiff_driver_type type, void *priv)
+ enum userdiff_driver_type type UNUSED,
+ void *priv)
{
struct find_by_namelen_data *cb_data = priv;
@@ -412,7 +413,7 @@
check = attr_check_initl("diff", NULL);
if (!path)
return NULL;
- git_check_attr(istate, path, check);
+ git_check_attr(istate, NULL, path, check);
if (ATTR_TRUE(check->items[0].value))
return &driver_true;
diff --git a/utf8.c b/utf8.c
index de4ce5c..6a0dd25 100644
--- a/utf8.c
+++ b/utf8.c
@@ -206,26 +206,34 @@
* 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_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 @@
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 9a16c86..b68efef 100644
--- a/utf8.h
+++ b/utf8.h
@@ -7,7 +7,7 @@
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 6e69877..da3d0e2 100644
--- a/ws.c
+++ b/ws.c
@@ -29,6 +29,7 @@
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 @@
rule |= whitespace_rule_names[i].rule_bits;
break;
}
- if (strncmp(string, "tabwidth=", 9) == 0) {
- unsigned tabwidth = atoi(string + 9);
+ if (skip_prefix(string, "tabwidth=", &arg)) {
+ unsigned tabwidth = atoi(arg);
if (0 < tabwidth && tabwidth < 0100) {
rule &= ~WS_TAB_WIDTH_MASK;
rule |= tabwidth;
}
else
warning("tabwidth %.*s out of range",
- (int)(len - 9), string + 9);
+ (int)(ep - arg), arg);
}
string = ep;
}
@@ -78,7 +79,7 @@
if (!attr_whitespace_rule)
attr_whitespace_rule = attr_check_initl("whitespace", NULL);
- git_check_attr(istate, pathname, attr_whitespace_rule);
+ git_check_attr(istate, NULL, pathname, attr_whitespace_rule);
value = attr_whitespace_rule->items[0].value;
if (ATTR_TRUE(value)) {
/* true (whitespace) */
@@ -252,7 +253,7 @@
return ws_check_emit_1(line, len, ws_rule, NULL, NULL, NULL, NULL);
}
-int ws_blank_line(const char *line, int len, unsigned ws_rule)
+int ws_blank_line(const char *line, int len)
{
/*
* We _might_ want to treat CR differently from other
diff --git a/wt-status.c b/wt-status.c
index 5813174..3162241 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -18,8 +18,10 @@
#include "worktree.h"
#include "lockfile.h"
#include "sequencer.h"
+#include "fsmonitor-settings.h"
#define AB_DELAY_WARNING_IN_MS (2 * 1000)
+#define UF_DELAY_WARNING_IN_MS (2 * 1000)
static const char cut_line[] =
"------------------------ >8 ------------------------\n";
@@ -438,7 +440,7 @@
}
static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
- struct diff_options *options,
+ struct diff_options *options UNUSED,
void *data)
{
struct wt_status *s = data;
@@ -525,7 +527,7 @@
}
static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
- struct diff_options *options,
+ struct diff_options *options UNUSED,
void *data)
{
struct wt_status *s = data;
@@ -1205,6 +1207,13 @@
strbuf_release(&sb);
}
+static int uf_was_slow(struct wt_status *s)
+{
+ if (getenv("GIT_TEST_UF_DELAY_WARNING"))
+ s->untracked_in_ms = 3250;
+ return UF_DELAY_WARNING_IN_MS < s->untracked_in_ms;
+}
+
static void show_merge_in_progress(struct wt_status *s,
const char *color)
{
@@ -1814,6 +1823,7 @@
{
const char *branch_color = color(WT_STATUS_ONBRANCH, s);
const char *branch_status_color = color(WT_STATUS_HEADER, s);
+ enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(s->repo);
if (s->branch) {
const char *on_what = _("On branch ");
@@ -1870,13 +1880,21 @@
wt_longstatus_print_other(s, &s->untracked, _("Untracked files"), "add");
if (s->show_ignored_mode)
wt_longstatus_print_other(s, &s->ignored, _("Ignored files"), "add -f");
- if (advice_enabled(ADVICE_STATUS_U_OPTION) && 2000 < s->untracked_in_ms) {
+ if (advice_enabled(ADVICE_STATUS_U_OPTION) && uf_was_slow(s)) {
status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
+ if (fsm_mode > FSMONITOR_MODE_DISABLED) {
+ status_printf_ln(s, GIT_COLOR_NORMAL,
+ _("It took %.2f seconds to enumerate untracked files,\n"
+ "but the results were cached, and subsequent runs may be faster."),
+ s->untracked_in_ms / 1000.0);
+ } else {
+ status_printf_ln(s, GIT_COLOR_NORMAL,
+ _("It took %.2f seconds to enumerate untracked files."),
+ s->untracked_in_ms / 1000.0);
+ }
status_printf_ln(s, GIT_COLOR_NORMAL,
- _("It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
- "may speed it up, but you have to be careful not to forget to add\n"
- "new files yourself (see 'git help status')."),
- s->untracked_in_ms / 1000.0);
+ _("See 'git help status' for information on how to improve this."));
+ status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
}
} else if (s->committable)
status_printf_ln(s, GIT_COLOR_NORMAL, _("Untracked files not listed%s"),
diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c
index 32652de..344c2df 100644
--- a/xdiff/xdiffi.c
+++ b/xdiff/xdiffi.c
@@ -973,7 +973,7 @@
}
}
-static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+static int xdl_call_hunk_func(xdfenv_t *xe UNUSED, xdchange_t *xscr, xdemitcb_t *ecb,
xdemitconf_t const *xecfg)
{
xdchange_t *xch, *xche;
diff --git a/xdiff/xemit.c b/xdiff/xemit.c
index c4ccd68..75f0fe4 100644
--- a/xdiff/xemit.c
+++ b/xdiff/xemit.c
@@ -95,7 +95,7 @@
}
-static long def_ff(const char *rec, long len, char *buf, long sz, void *priv)
+static long def_ff(const char *rec, long len, char *buf, long sz)
{
if (len > 0 &&
(isalpha((unsigned char)*rec) || /* identifier? */
@@ -117,7 +117,7 @@
const char *rec;
long len = xdl_get_rec(xdf, ri, &rec);
if (!xecfg->find_func)
- return def_ff(rec, len, buf, sz, xecfg->find_func_priv);
+ return def_ff(rec, len, buf, sz);
return xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv);
}