mirror of
https://github.com/sharkdp/fd.git
synced 2026-09-01 04:28:07 +08:00
Compare commits
194 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7027d45303 | |||
| bf6489a79b | |||
| cb3fad48bb | |||
| a4d86cc1f5 | |||
| b691572928 | |||
| ec25d2bc81 | |||
| 4ea110d8dd | |||
| d766e76351 | |||
| aa35ef4e45 | |||
| b87d919244 | |||
| bb3d57c433 | |||
| ff1f66cceb | |||
| d9fc3272db | |||
| c60d0076db | |||
| 801ccd7620 | |||
| a07ad51751 | |||
| 6067f72742 | |||
| 73e6f95416 | |||
| 40d8eb30bc | |||
| 38986aa42c | |||
| 171438be2d | |||
| fe0e173e9e | |||
| 9a9c4b921d | |||
| efd9f05fcb | |||
| ccafbbb08a | |||
| 74c389f847 | |||
| 2fc76da442 | |||
| b22a46c103 | |||
| 1586dabeee | |||
| 0a094a883f | |||
| 4410dda6e4 | |||
| bf81fb98fa | |||
| 9508153471 | |||
| 1a37650b2c | |||
| dcc5ff84cd | |||
| 9c9eeda209 | |||
| 4ba10c7837 | |||
| 2efd99bc52 | |||
| cd8fedc183 | |||
| f3a8bb7ce6 | |||
| 1df183e0cd | |||
| 1993c76971 | |||
| 5f95a78121 | |||
| 49ded38b8a | |||
| 9907632b89 | |||
| 8ec9d3115e | |||
| 96502d2611 | |||
| 6c5267e240 | |||
| 84789bfe57 | |||
| 19dcbff562 | |||
| e311e503ba | |||
| bd33bc0eb6 | |||
| f5b4171631 | |||
| 16e5d9b816 | |||
| c90994b36a | |||
| 728b380c67 | |||
| 2d9c8dab31 | |||
| 161f3ba7cd | |||
| e2ae9e88b2 | |||
| a06417d355 | |||
| d05a718be5 | |||
| e3a6f8b7e7 | |||
| 76046b23b0 | |||
| 098bb568e1 | |||
| 1fb1cafc15 | |||
| 8661ac473c | |||
| b6af107ac6 | |||
| 491baaa2d1 | |||
| 68fa2dbaed | |||
| 15b57b9e78 | |||
| 3b32256715 | |||
| 32f9fd3314 | |||
| cfc4ace4f4 | |||
| 7fdfd40107 | |||
| d7bfcf48b8 | |||
| 942b68ea3b | |||
| e08038c34c | |||
| 2f5ab52897 | |||
| 19e752d184 | |||
| c3652afc2b | |||
| 7014993561 | |||
| 72e54e8e9e | |||
| cd95cfcfe0 | |||
| 78a34dd1e9 | |||
| e7e78d7b6a | |||
| a19c874fee | |||
| 90c94a2ea9 | |||
| 73a470d076 | |||
| 7c2584b11d | |||
| 1d85f3be67 | |||
| a836d865f0 | |||
| f360a772b0 | |||
| 202db498c0 | |||
| d23ac06e40 | |||
| dbd3e3c14c | |||
| da731c48d9 | |||
| 5558e407be | |||
| f77b693db2 | |||
| 9842656b2b | |||
| 97b3b0404e | |||
| 54fa0112f5 | |||
| c7a05504f4 | |||
| 80d0fb2b70 | |||
| 3263141223 | |||
| 98a4233e14 | |||
| 4e6f46b191 | |||
| 8c9c6bec75 | |||
| ee5332d0d3 | |||
| 2ae1154be4 | |||
| e89650f169 | |||
| ba2dde80f3 | |||
| 7f3dd94092 | |||
| 10d1ecf2a2 | |||
| 3f77968f1d | |||
| 92bdd78e56 | |||
| 27d90dca17 | |||
| 47cedbb44e | |||
| a32e6a8d3e | |||
| b3e3977c2b | |||
| 8d254c5337 | |||
| 5c4477c9d3 | |||
| 4344a26540 | |||
| 37b67767a2 | |||
| 1c316becf3 | |||
| 1ccb6fd2f2 | |||
| e1cb0f44a3 | |||
| 009d776db5 | |||
| 32558eaf4e | |||
| 5be8cc0167 | |||
| e6cf1875ed | |||
| 377c442c4e | |||
| 13d79ffd41 | |||
| ddf144b596 | |||
| fb3ffca4c7 | |||
| 725b296ce4 | |||
| a13e0b9926 | |||
| 9ed1ce98a9 | |||
| e13dd192f1 | |||
| 498ea1cc77 | |||
| 0d3261daf7 | |||
| d38148f0aa | |||
| 04832f1ac4 | |||
| fd06e69ed2 | |||
| 456afaef88 | |||
| 377f8d6ef7 | |||
| d2d1a8580e | |||
| acb7f72254 | |||
| 677823227c | |||
| d856e78591 | |||
| 0c71954017 | |||
| f58e562809 | |||
| 221e5dc894 | |||
| 6dfbbe86fc | |||
| c8f4a6b546 | |||
| d5894ba4f3 | |||
| be94ec6136 | |||
| 6465ce6d65 | |||
| abb188ef37 | |||
| f91d97e475 | |||
| 377c2c05d8 | |||
| a406b0c5de | |||
| edb12d22a2 | |||
| 275b56c168 | |||
| b108817d31 | |||
| 1cadc21b42 | |||
| db6aefa5b8 | |||
| b7fe2ce91b | |||
| e76e7ffbd3 | |||
| 5c66878fb4 | |||
| 1857ffff0c | |||
| 56ca42aed0 | |||
| ba35c5ea67 | |||
| 3d704fb4b9 | |||
| fd5ec5fed5 | |||
| b2c7cd1e3e | |||
| 851fa4e5bb | |||
| 9f2cf9205d | |||
| f9eaf7e4b9 | |||
| 0e73fe9aa5 | |||
| 8bd92f0572 | |||
| 16fb0cb9d3 | |||
| dbea8a68a8 | |||
| 8b91260815 | |||
| 9bd1647f86 | |||
| c4250eca2a | |||
| cb150c4fd4 | |||
| e389e5d275 | |||
| efe499ac0d | |||
| 0ada245e37 | |||
| 402d4e7c12 | |||
| 26795f6a55 | |||
| bc79395bcf | |||
| ad75d79a52 | |||
| 494eef044c |
@@ -0,0 +1,9 @@
|
||||
# On Windows MSVC, statically link the C runtime so that the resulting EXE does
|
||||
# not depend on the vcruntime DLL.
|
||||
#
|
||||
# See: https://github.com/sharkdp/fd/issues/1874
|
||||
|
||||
[target.x86_64-pc-windows-msvc]
|
||||
rustflags = ["-C", "target-feature=+crt-static"]
|
||||
[target.i686-pc-windows-msvc]
|
||||
rustflags = ["-C", "target-feature=+crt-static"]
|
||||
@@ -4,7 +4,11 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
|
||||
+94
-47
@@ -13,12 +13,17 @@ on:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
crate_metadata:
|
||||
name: Extract crate metadata
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Extract crate information
|
||||
id: crate_metadata
|
||||
run: |
|
||||
@@ -41,7 +46,9 @@ jobs:
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
- run: cargo fmt -- --check
|
||||
|
||||
lint_check:
|
||||
@@ -51,7 +58,9 @@ jobs:
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: clippy
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
- run: cargo clippy --all-targets --all-features -- -Dwarnings
|
||||
|
||||
min_version:
|
||||
@@ -60,7 +69,9 @@ jobs:
|
||||
needs: crate_metadata
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install rust toolchain (v${{ needs.crate_metadata.outputs.msrv }})
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
@@ -68,41 +79,49 @@ jobs:
|
||||
toolchain: ${{ needs.crate_metadata.outputs.msrv }}
|
||||
components: clippy
|
||||
- name: Run clippy (on minimum supported rust version to prevent warnings we can't fix)
|
||||
run: cargo clippy --locked --all-targets ${{ env.MSRV_FEATURES }}
|
||||
run: cargo clippy --locked --all-targets "${MSRV_FEATURES}"
|
||||
- name: Run tests
|
||||
run: cargo test --locked ${{ env.MSRV_FEATURES }}
|
||||
run: cargo test --locked "${MSRV_FEATURES}"
|
||||
|
||||
build:
|
||||
name: ${{ matrix.job.target }} (${{ matrix.job.os }})
|
||||
name: '${{ matrix.job.target }} (${{ matrix.job.os }})'
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
needs: crate_metadata
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: write
|
||||
attestations: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
job:
|
||||
- { target: aarch64-unknown-linux-gnu , os: ubuntu-22.04, use-cross: true }
|
||||
- { target: aarch64-unknown-linux-musl , os: ubuntu-22.04, use-cross: true }
|
||||
- { target: arm-unknown-linux-gnueabihf , os: ubuntu-22.04, use-cross: true }
|
||||
- { target: arm-unknown-linux-musleabihf, os: ubuntu-22.04, use-cross: true }
|
||||
- { target: aarch64-unknown-linux-gnu , os: ubuntu-24.04, use-cross: true }
|
||||
- { target: aarch64-unknown-linux-musl , os: ubuntu-24.04, use-cross: true }
|
||||
- { target: arm-unknown-linux-gnueabihf , os: ubuntu-24.04, use-cross: true }
|
||||
- { target: arm-unknown-linux-musleabihf, os: ubuntu-24.04, use-cross: true }
|
||||
- { target: i686-pc-windows-msvc , os: windows-2022 }
|
||||
- { target: i686-unknown-linux-gnu , os: ubuntu-22.04, use-cross: true }
|
||||
- { target: i686-unknown-linux-musl , os: ubuntu-22.04, use-cross: true }
|
||||
- { target: x86_64-apple-darwin , os: macos-13 }
|
||||
- { target: i686-unknown-linux-gnu , os: ubuntu-24.04, use-cross: true }
|
||||
- { target: i686-unknown-linux-musl , os: ubuntu-24.04, use-cross: true }
|
||||
- { target: aarch64-apple-darwin , os: macos-14 }
|
||||
- { target: x86_64-pc-windows-gnu , os: windows-2022 }
|
||||
- { target: x86_64-pc-windows-msvc , os: windows-2022 }
|
||||
- { target: x86_64-unknown-linux-gnu , os: ubuntu-22.04, use-cross: true }
|
||||
- { target: x86_64-unknown-linux-musl , os: ubuntu-22.04, use-cross: true }
|
||||
- { target: aarch64-pc-windows-msvc , os: windows-11-arm }
|
||||
- { target: x86_64-unknown-linux-gnu , os: ubuntu-24.04, use-cross: true }
|
||||
- { target: x86_64-unknown-linux-musl , os: ubuntu-24.04, use-cross: true }
|
||||
env:
|
||||
BUILD_CMD: cargo
|
||||
BUILD_CMD: "${{ matrix.job.use-cross && 'cross' || 'cargo' }}"
|
||||
target: ${{ matrix.job.target }}
|
||||
name: ${{ needs.crate_metadata.outputs.name }}
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install prerequisites
|
||||
shell: bash
|
||||
run: |
|
||||
case ${{ matrix.job.target }} in
|
||||
case ${target} in
|
||||
arm-unknown-linux-*) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;;
|
||||
aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;;
|
||||
esac
|
||||
@@ -111,21 +130,22 @@ jobs:
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.job.target }}
|
||||
# On windows, for now build with 1.77.2, so that it works on windows 7.
|
||||
# When we update the MSRV again, we'll need to revisit this, and probably drop support for Win7
|
||||
toolchain: "${{ contains(matrix.job.target, 'windows-') && '1.77.2' || 'stable' }}"
|
||||
toolchain: "stable"
|
||||
|
||||
- name: Install cross
|
||||
if: matrix.job.use-cross
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cross
|
||||
|
||||
- name: Overwrite build command env variable
|
||||
if: matrix.job.use-cross
|
||||
shell: bash
|
||||
run: echo "BUILD_CMD=cross" >> $GITHUB_ENV
|
||||
|
||||
env:
|
||||
cross_version: "v0.2.5"
|
||||
package_name: "cross-x86_64-unknown-linux-gnu.tar.gz"
|
||||
GH_TOKEN: "${{ github.token }}"
|
||||
run: |
|
||||
dir="$HOME/.local/bin/"
|
||||
mkdir -p "$dir"
|
||||
gh release download --repo cross-rs/cross \
|
||||
--pattern "${package_name}" -O - "${cross_version}" \
|
||||
| tar -C "$dir" -xz
|
||||
echo "$dir" >> $GITHUB_PATH
|
||||
echo "Installed cross $cross_version" >> $GITHUB_STEP_SUMMARY
|
||||
- name: Show version information (Rust, cargo, GCC)
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -138,7 +158,7 @@ jobs:
|
||||
|
||||
- name: Build
|
||||
shell: bash
|
||||
run: $BUILD_CMD build --locked --release --target=${{ matrix.job.target }}
|
||||
run: $BUILD_CMD build --locked --release --target="${target}"
|
||||
|
||||
- name: Set binary name & path
|
||||
id: bin
|
||||
@@ -146,13 +166,13 @@ jobs:
|
||||
run: |
|
||||
# Figure out suffix of binary
|
||||
EXE_suffix=""
|
||||
case ${{ matrix.job.target }} in
|
||||
case ${target} in
|
||||
*-pc-windows-*) EXE_suffix=".exe" ;;
|
||||
esac;
|
||||
|
||||
# Setup paths
|
||||
BIN_NAME="${{ needs.crate_metadata.outputs.name }}${EXE_suffix}"
|
||||
BIN_PATH="target/${{ matrix.job.target }}/release/${BIN_NAME}"
|
||||
BIN_NAME="${name}${EXE_suffix}"
|
||||
BIN_PATH="target/${target}/release/${BIN_NAME}"
|
||||
|
||||
# Let subsequent steps know where to find the binary
|
||||
echo "BIN_PATH=${BIN_PATH}" >> $GITHUB_OUTPUT
|
||||
@@ -164,12 +184,17 @@ jobs:
|
||||
run: |
|
||||
# test only library unit tests and binary for arm-type targets
|
||||
unset CARGO_TEST_OPTIONS
|
||||
unset CARGO_TEST_OPTIONS ; case ${{ matrix.job.target }} in arm-* | aarch64-*) CARGO_TEST_OPTIONS="--bin ${{ needs.crate_metadata.outputs.name }}" ;; esac;
|
||||
case ${target} in
|
||||
arm-* | aarch64-*)
|
||||
CARGO_TEST_OPTIONS="--bin=${name}" ;;
|
||||
esac
|
||||
echo "CARGO_TEST_OPTIONS=${CARGO_TEST_OPTIONS}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
run: $BUILD_CMD test --locked --target=${{ matrix.job.target }} ${{ steps.test-options.outputs.CARGO_TEST_OPTIONS}}
|
||||
env:
|
||||
cargo_test_options: ${{ steps.test-options.outputs.CARGO_TEST_OPTIONS}}
|
||||
run: $BUILD_CMD test --locked --target="${target}" "${cargo_test_options}"
|
||||
|
||||
- name: Generate completions
|
||||
id: completions
|
||||
@@ -179,31 +204,37 @@ jobs:
|
||||
- name: Create tarball
|
||||
id: package
|
||||
shell: bash
|
||||
env:
|
||||
BIN_PATH: ${{ steps.bin.outputs.BIN_PATH }}
|
||||
version: ${{ needs.crate_metadata.outputs.version }}
|
||||
run: |
|
||||
PKG_suffix=".tar.gz" ; case ${{ matrix.job.target }} in *-pc-windows-*) PKG_suffix=".zip" ;; esac;
|
||||
PKG_BASENAME=${{ needs.crate_metadata.outputs.name }}-v${{ needs.crate_metadata.outputs.version }}-${{ matrix.job.target }}
|
||||
PKG_suffix=".tar.gz"
|
||||
case ${target} in
|
||||
*-pc-windows-*) PKG_suffix=".zip" ;;
|
||||
esac
|
||||
PKG_BASENAME=${name}-v${version}-${target}
|
||||
PKG_NAME=${PKG_BASENAME}${PKG_suffix}
|
||||
echo "PKG_NAME=${PKG_NAME}" >> $GITHUB_OUTPUT
|
||||
|
||||
PKG_STAGING="${{ env.CICD_INTERMEDIATES_DIR }}/package"
|
||||
PKG_STAGING="${CICD_INTERMEDIATES_DIR}/package"
|
||||
ARCHIVE_DIR="${PKG_STAGING}/${PKG_BASENAME}/"
|
||||
mkdir -p "${ARCHIVE_DIR}"
|
||||
|
||||
# Binary
|
||||
cp "${{ steps.bin.outputs.BIN_PATH }}" "$ARCHIVE_DIR"
|
||||
cp "${BIN_PATH}" "$ARCHIVE_DIR"
|
||||
|
||||
# README, LICENSE and CHANGELOG files
|
||||
cp "README.md" "LICENSE-MIT" "LICENSE-APACHE" "CHANGELOG.md" "$ARCHIVE_DIR"
|
||||
|
||||
# Man page
|
||||
cp 'doc/${{ needs.crate_metadata.outputs.name }}.1' "$ARCHIVE_DIR"
|
||||
cp "doc/${name}.1" "$ARCHIVE_DIR"
|
||||
|
||||
# Autocompletion files
|
||||
cp -r autocomplete "${ARCHIVE_DIR}"
|
||||
|
||||
# base compressed package
|
||||
pushd "${PKG_STAGING}/" >/dev/null
|
||||
case ${{ matrix.job.target }} in
|
||||
case ${target} in
|
||||
*-pc-windows-*) 7z -y a "${PKG_NAME}" "${PKG_BASENAME}"/* | tail -2 ;;
|
||||
*) tar czf "${PKG_NAME}" "${PKG_BASENAME}"/* ;;
|
||||
esac;
|
||||
@@ -223,13 +254,15 @@ jobs:
|
||||
BIN_PATH: ${{ steps.bin.outputs.BIN_PATH }}
|
||||
|
||||
- name: "Artifact upload: tarball"
|
||||
uses: actions/upload-artifact@master
|
||||
id: upload-tarball
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
|
||||
with:
|
||||
name: ${{ steps.package.outputs.PKG_NAME }}
|
||||
path: ${{ steps.package.outputs.PKG_PATH }}
|
||||
|
||||
- name: "Artifact upload: Debian package"
|
||||
uses: actions/upload-artifact@master
|
||||
id: upload-deb
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
|
||||
if: steps.debian-package.outputs.DPKG_NAME
|
||||
with:
|
||||
name: ${{ steps.debian-package.outputs.DPKG_NAME }}
|
||||
@@ -242,8 +275,22 @@ jobs:
|
||||
unset IS_RELEASE ; if [[ $GITHUB_REF =~ ^refs/tags/v[0-9].* ]]; then IS_RELEASE='true' ; fi
|
||||
echo "IS_RELEASE=${IS_RELEASE}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: "Attest artifact: tarball"
|
||||
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4
|
||||
if: steps.is-release.outputs.IS_RELEASE
|
||||
with:
|
||||
subject-name: ${{ steps.package.outputs.PKG_NAME }}
|
||||
subject-digest: sha256:${{ steps.upload-tarball.outputs.artifact-digest }}
|
||||
|
||||
- name: "Attest artifact: Debian package"
|
||||
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4
|
||||
if: 'steps.is-release.outputs.IS_RELEASE && steps.debian-package.outputs.DPKG_NAME'
|
||||
with:
|
||||
subject-name: ${{ steps.debian-package.outputs.DPKG_NAME }}
|
||||
subject-digest: sha256:${{ steps.upload-deb.outputs.artifact-digest }}
|
||||
|
||||
- name: Publish archives and packages
|
||||
uses: softprops/action-gh-release@v2
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
if: steps.is-release.outputs.IS_RELEASE
|
||||
with:
|
||||
files: |
|
||||
@@ -258,7 +305,7 @@ jobs:
|
||||
needs: build
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
steps:
|
||||
- uses: vedantmgoyal2009/winget-releaser@v2
|
||||
- uses: vedantmgoyal9/winget-releaser@4ffc7888bffd451b357355dc214d43bb9f23917e # v2
|
||||
with:
|
||||
identifier: sharkdp.fd
|
||||
installers-regex: '-pc-windows-msvc\.zip$'
|
||||
|
||||
+41
-1
@@ -1,4 +1,35 @@
|
||||
# Upcoming release
|
||||
# 10.4.2
|
||||
|
||||
## Bugfixes
|
||||
- Fixed performance regression due to `--ignore-contain`; see #1913 and #1914
|
||||
|
||||
# 10.4.1
|
||||
|
||||
This is just a re-release of 10.4.0 due to an issue with the 10.4.0 release.
|
||||
|
||||
# 10.4.0
|
||||
|
||||
## Features
|
||||
- Add `--ignore-contain` option to ignore directories containing a named entry (e.g. to ignore [`CACHEDIR.TAG`](https://bford.info/cachedir/)); see #1727 (@fischman).
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Fix Windows hyperlink generation for paths with spaces. (#1872)
|
||||
|
||||
- `--print0` combined with `--exec` will now print a `\0` between the output of each entry. Note that if there are multiple instances
|
||||
of `--exec`, the `\0` will be between each _set_ of commands, _not_ between each individual command run. Fixes #1797.
|
||||
|
||||
- Several bugs were fixed by an update to the `ignore` library used for handling ignore rules
|
||||
- #1506
|
||||
- #1667
|
||||
- #1813
|
||||
|
||||
## Changes
|
||||
|
||||
- Minimum required rust version has been increased to 1.90.0. Notably, this means dropping fully support for intel Mac and Windows 7.
|
||||
- Statically link the CRT for MSVC builds via Cargo config to avoid runtime DLL dependencies, see #1874 (@FidelSch)
|
||||
|
||||
# 10.3.0
|
||||
|
||||
## Features
|
||||
|
||||
@@ -15,9 +46,18 @@
|
||||
- 'M' no longer means "month", as that could be confusing with minutes. Use "mo", "mos", "month" or "months" instead.
|
||||
- month and year now account for variability in the calander rather than being a hard-coded number of seconds. That is probably
|
||||
what you would expect, but it is a slight change in behavior.
|
||||
- aarch64 Windows was added to CI and release artifacts
|
||||
- Many dependencies were updated
|
||||
- Better support building on Illumos (there is no automated testing, but some known issues were fixed)
|
||||
|
||||
## Other
|
||||
|
||||
This will be the last release that has been tested on x86_64 Mac OS, since GitHub is
|
||||
dropping support for runners with that hardware.
|
||||
|
||||
It may also be the last release to use a version of Rust with tier-1 support for
|
||||
x86_64/intel Macs and Windows 7.
|
||||
|
||||
|
||||
# 10.2.0
|
||||
|
||||
|
||||
Generated
+483
-212
File diff suppressed because it is too large
Load Diff
+46
-21
@@ -1,6 +1,5 @@
|
||||
[package]
|
||||
authors = ["David Peter <mail@david-peter.de>"]
|
||||
build = "build.rs"
|
||||
categories = ["command-line-utilities"]
|
||||
description = "fd is a simple, fast and user-friendly alternative to find."
|
||||
exclude = ["/benchmarks/*"]
|
||||
@@ -17,9 +16,9 @@ license = "MIT OR Apache-2.0"
|
||||
name = "fd-find"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/sharkdp/fd"
|
||||
version = "10.2.0"
|
||||
edition= "2021"
|
||||
rust-version = "1.77.2"
|
||||
version = "10.4.2"
|
||||
edition= "2024"
|
||||
rust-version = "1.90.0"
|
||||
|
||||
[badges.appveyor]
|
||||
repository = "sharkdp/fd"
|
||||
@@ -31,37 +30,34 @@ repository = "sharkdp/fd"
|
||||
name = "fd"
|
||||
path = "src/main.rs"
|
||||
|
||||
[build-dependencies]
|
||||
version_check = "0.9"
|
||||
|
||||
[dependencies]
|
||||
aho-corasick = "1.1"
|
||||
nu-ansi-term = "0.50"
|
||||
argmax = "0.3.1"
|
||||
ignore = "0.4.23"
|
||||
regex = "1.11.1"
|
||||
argmax = "0.4.0"
|
||||
ignore = "0.4.25"
|
||||
regex = "1.12.2"
|
||||
regex-syntax = "0.8"
|
||||
ctrlc = "3.2"
|
||||
ctrlc = "3.5"
|
||||
globset = "0.4"
|
||||
anyhow = "1.0"
|
||||
etcetera = "0.10"
|
||||
etcetera = "0.11"
|
||||
normpath = "1.1.1"
|
||||
crossbeam-channel = "0.5.15"
|
||||
clap_complete = {version = "4.5.47", optional = true}
|
||||
clap_complete = {version = "4.5.62", optional = true}
|
||||
faccess = "0.2.4"
|
||||
jiff = "0.2.5"
|
||||
jiff = "0.2.18"
|
||||
|
||||
[dependencies.clap]
|
||||
version = "4.5.35"
|
||||
version = "4.5.54"
|
||||
features = ["suggestions", "color", "wrap_help", "cargo", "derive"]
|
||||
|
||||
[dependencies.lscolors]
|
||||
version = "0.20"
|
||||
version = "0.21"
|
||||
default-features = false
|
||||
features = ["nu-ansi-term"]
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
nix = { version = "0.29.0", default-features = false, features = ["signal", "user", "hostname"] }
|
||||
nix = { version = "0.31.1", default-features = false, features = ["signal", "user", "hostname"] }
|
||||
|
||||
[target.'cfg(all(unix, not(target_os = "redox")))'.dependencies]
|
||||
libc = "0.2"
|
||||
@@ -69,22 +65,51 @@ libc = "0.2"
|
||||
# FIXME: Re-enable jemalloc on macOS
|
||||
# jemalloc is currently disabled on macOS due to a bug in jemalloc in combination with macOS
|
||||
# Catalina. See https://github.com/sharkdp/fd/issues/498 for details.
|
||||
[target.'cfg(all(not(windows), not(target_os = "android"), not(target_os = "macos"), not(target_os = "freebsd"), not(target_os = "openbsd"), not(all(target_env = "musl", target_pointer_width = "32")), not(target_arch = "riscv64")))'.dependencies]
|
||||
jemallocator = {version = "0.5.4", optional = true}
|
||||
# This has to be kept in sync with src/main.rs where the allocator for
|
||||
# the program is set.
|
||||
[target.'cfg(all(not(windows), not(target_os = "android"), not(target_os = "macos"), not(target_os = "freebsd"), not(target_os = "openbsd"), not(target_os = "illumos"), not(all(target_env = "musl", target_pointer_width = "32")), not(target_arch = "riscv64")))'.dependencies]
|
||||
tikv-jemallocator = {version = "0.6.0", optional = true}
|
||||
|
||||
[dev-dependencies]
|
||||
diff = "0.1"
|
||||
tempfile = "3.19"
|
||||
tempfile = "3.24"
|
||||
filetime = "0.2"
|
||||
test-case = "3.3"
|
||||
|
||||
[profile.dev]
|
||||
debug = "line-tables-only"
|
||||
|
||||
[profile.dev.package."*"]
|
||||
debug = false
|
||||
|
||||
[profile.debugging]
|
||||
inherits = "dev"
|
||||
debug = true
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
strip = true
|
||||
codegen-units = 1
|
||||
|
||||
[features]
|
||||
use-jemalloc = ["jemallocator"]
|
||||
use-jemalloc = ["tikv-jemallocator"]
|
||||
completions = ["clap_complete"]
|
||||
base = ["use-jemalloc"]
|
||||
default = ["use-jemalloc", "completions"]
|
||||
|
||||
[package.metadata.binstall]
|
||||
pkg-url = "{ repo }/releases/download/v{ version }/{ name }-v{ version }-{ target }.{ archive-format }"
|
||||
bin-dir = "{ bin }-v{ version }-{ target }/{ bin }{ binary-ext }"
|
||||
pkg-fmt = "tgz"
|
||||
|
||||
[package.metadata.binstall.overrides.x86_64-pc-windows-msvc]
|
||||
pkg-fmt = "zip"
|
||||
|
||||
[package.metadata.binstall.overrides.x86_64-pc-windows-gnu]
|
||||
pkg-fmt = "zip"
|
||||
|
||||
[package.metadata.binstall.overrides.i686-pc-windows-msvc]
|
||||
pkg-fmt = "zip"
|
||||
|
||||
[package.metadata.binstall.overrides.aarch64-pc-windows-msvc]
|
||||
pkg-fmt = "zip"
|
||||
|
||||
@@ -9,7 +9,7 @@ $(EXE): Cargo.toml src/**/*.rs
|
||||
cargo build --profile $(PROFILE) --locked
|
||||
|
||||
.PHONY: completions
|
||||
completions: autocomplete/fd.bash autocomplete/fd.fish autocomplete/fd.ps1 autocomplete/_fd
|
||||
completions: autocomplete/fd.bash autocomplete/fd.fish autocomplete/_fd.ps1 autocomplete/_fd
|
||||
|
||||
comp_dir=@mkdir -p autocomplete
|
||||
|
||||
@@ -21,7 +21,7 @@ autocomplete/fd.fish: $(EXE)
|
||||
$(comp_dir)
|
||||
$(EXE) --gen-completions fish > $@
|
||||
|
||||
autocomplete/fd.ps1: $(EXE)
|
||||
autocomplete/_fd.ps1: $(EXE)
|
||||
$(comp_dir)
|
||||
$(EXE) --gen-completions powershell > $@
|
||||
|
||||
|
||||
@@ -27,6 +27,19 @@ While it does not aim to support all of `find`'s powerful functionality, it prov
|
||||
* The command name is *50%* shorter[\*](https://github.com/ggreer/the_silver_searcher) than
|
||||
`find` :-).
|
||||
|
||||
## Sponsors
|
||||
|
||||
A special *thank you* goes to our biggest <a href="doc/sponsors.md">sponsor</a>:<br>
|
||||
|
||||
<a href="https://tuple.app/fd">
|
||||
<img src="doc/sponsors/tuple-logo.png" width="200" alt="Tuple">
|
||||
<br>
|
||||
<strong>Tuple, the premier screen sharing app for developers</strong>
|
||||
<br>
|
||||
<sub>Available for MacOS & Windows</sub>
|
||||
</a>
|
||||
|
||||
|
||||
## Demo
|
||||
|
||||

|
||||
@@ -298,7 +311,7 @@ This is the output of `fd -h`. To see the full set of command-line options, use
|
||||
also includes a much more detailed help text.
|
||||
|
||||
```
|
||||
Usage: fd [OPTIONS] [pattern] [path]...
|
||||
Usage: fd [OPTIONS] [pattern [path]...]
|
||||
|
||||
Arguments:
|
||||
[pattern] the search pattern (a regular expression, unless '--glob' is used; optional)
|
||||
@@ -331,10 +344,13 @@ Options:
|
||||
always, never]
|
||||
--hyperlink[=<when>] Add hyperlinks to output paths [default: never] [possible
|
||||
values: auto, always, never]
|
||||
--ignore-contain <name> Ignore directories containing the named entry
|
||||
-h, --help Print help (see more with '--help')
|
||||
-V, --version Print version
|
||||
```
|
||||
|
||||
Note that options can be given after the pattern and/or path as well.
|
||||
|
||||
## Benchmark
|
||||
|
||||
Let's search my home folder for files that end in `[0-9].jpg`. It contains ~750.000
|
||||
@@ -615,7 +631,7 @@ You can install [the fd package](https://github.com/getsolus/packages/tree/main/
|
||||
eopkg install fd
|
||||
```
|
||||
|
||||
### On RedHat Enterprise Linux 8/9 (RHEL8/9), Almalinux 8/9, EuroLinux 8/9 or Rocky Linux 8/9
|
||||
### On RedHat Enterprise Linux (RHEL) 8/9/10, Almalinux 8/9/10, EuroLinux 8/9 or Rocky Linux 8/9/10
|
||||
|
||||
You can install [the `fd` package](https://copr.fedorainfracloud.org/coprs/tkbcopr/fd/) from Fedora Copr.
|
||||
|
||||
@@ -664,6 +680,13 @@ You can install [the fd package](https://guix.gnu.org/en/packages/fd-8.1.1/) fro
|
||||
guix install fd
|
||||
```
|
||||
|
||||
### On Mise
|
||||
|
||||
You can use [mise](https://github.com/jdx/mise) to install `fd` with a command like this:
|
||||
```
|
||||
mise use -g fd@latest
|
||||
```
|
||||
|
||||
### On NixOS / via Nix
|
||||
|
||||
You can use the [Nix package manager](https://nixos.org/nix/) to install `fd`:
|
||||
@@ -722,6 +745,37 @@ cargo test
|
||||
cargo install --path .
|
||||
```
|
||||
|
||||
### Completions
|
||||
|
||||
#### From Release Archives
|
||||
|
||||
Pre-built completion files are included in the release archives (`.tar.gz`/`.zip`) on the
|
||||
[Releases page](https://github.com/sharkdp/fd/releases), in the `autocomplete` directory.
|
||||
To use these completions:
|
||||
|
||||
- **bash**: Source the `fd.bash` file in your `~/.bashrc`, or place it in a directory that gets sourced automatically.
|
||||
- **zsh**: Move `_fd` to a directory in your `fpath` (e.g., `~/.zfunc`).
|
||||
- **fish**: Copy `fd.fish` to `~/.config/fish/completions/`.
|
||||
- **powershell**: Source `_fd.ps1` from one of your [profile scripts](https://learn.microsoft.com/en-us/powershell/scripting/learn/shell/creating-profiles?view=powershell-7.5).
|
||||
|
||||
#### Generate from fd
|
||||
|
||||
You can also generate completions directly using `fd --gen-completions <shell>`:
|
||||
|
||||
```bash
|
||||
# Bash
|
||||
fd --gen-completions bash > ~/.local/share/bash-completion/completions/fd
|
||||
|
||||
# Zsh (ensure ~/.zfunc is in your fpath)
|
||||
fd --gen-completions zsh > ~/.zfunc/_fd
|
||||
|
||||
# Fish
|
||||
fd --gen-completions fish > ~/.config/fish/completions/fd.fish
|
||||
|
||||
# PowerShell
|
||||
fd --gen-completions powershell >> $PROFILE
|
||||
```
|
||||
|
||||
## Maintainers
|
||||
|
||||
- [sharkdp](https://github.com/sharkdp)
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
# Security Reporting
|
||||
|
||||
If you wish to report a security vulnerability privately, we appreciate your diligence. Please follow the guidelines below to submit your report.
|
||||
|
||||
## Reporting
|
||||
|
||||
To report a security vulnerability, please provide the following information:
|
||||
|
||||
1. **PROJECT**
|
||||
- Include the URL of the project repository - Example: <https://github.com/sharkdp/fd>
|
||||
|
||||
2. **PUBLIC**
|
||||
- Indicate whether this vulnerability has already been publicly discussed or disclosed.
|
||||
- If so, provide relevant links.
|
||||
|
||||
3. **DESCRIPTION**
|
||||
- Provide a detailed description of the security vulnerability.
|
||||
- Include as much information as possible to help us understand and address the issue.
|
||||
|
||||
Send this information, along with any additional relevant details, to our [vulnerability reporting form](https://github.com/sharkdp/fd/security/advisories/new).
|
||||
|
||||
## Confidentiality
|
||||
|
||||
We kindly ask you to keep the report confidential until a public announcement is made.
|
||||
|
||||
## Notes
|
||||
|
||||
- Vulnerabilities will be handled on a best-effort basis.
|
||||
- You may request an advance copy of the patched release, but we cannot guarantee early access before the public release.
|
||||
- You will be notified via email simultaneously with the public announcement.
|
||||
- We will respond within a few weeks to confirm whether your report has been accepted or rejected.
|
||||
|
||||
Thank you for helping to improve the security of our project!
|
||||
@@ -1,12 +0,0 @@
|
||||
fn main() {
|
||||
let min_version = "1.64";
|
||||
|
||||
match version_check::is_min_version(min_version) {
|
||||
Some(true) => {}
|
||||
// rustc version too small or can't figure it out
|
||||
_ => {
|
||||
eprintln!("'fd' requires rustc >= {min_version}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,8 @@ fd \- find entries in the filesystem
|
||||
.IR num ]
|
||||
.RB [ \-x
|
||||
.IR cmd ]
|
||||
.RI [ pattern ]
|
||||
.RI [ path... ]
|
||||
.RI [ pattern
|
||||
.RI [ path... ]]
|
||||
.SH DESCRIPTION
|
||||
.B fd
|
||||
is a simple, fast and user-friendly alternative to
|
||||
@@ -34,6 +34,12 @@ By default
|
||||
.B fd
|
||||
will exclude hidden files and directories, as well as any files that match gitignore rules
|
||||
or ignore rules in .ignore or .fdignore files.
|
||||
.P
|
||||
If you wish to search all files in a specific directory, you can use '' or . as the search pattern,
|
||||
to match all files. Or you can use the '\-\-search\-path' option to specify the path(s) instead of
|
||||
the positional parameter.
|
||||
.P
|
||||
Options may be given anywhere on the command line.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B \-H, \-\-hidden
|
||||
@@ -259,6 +265,10 @@ Examples:
|
||||
\-\-exclude '*.pyc'
|
||||
\-\-exclude node_modules
|
||||
.TP
|
||||
.BI "\-\-ignore-contain " name
|
||||
Exclude directories that (directly) contain the given name.
|
||||
This option can be specified multiple times.
|
||||
.TP
|
||||
.BI "\-\-ignore-file " path
|
||||
Add a custom ignore-file in '.gitignore' format.
|
||||
These files have a low precedence.
|
||||
@@ -379,7 +389,7 @@ Examples:
|
||||
\-\-owner :students
|
||||
\-\-owner "!john:students"
|
||||
.TP
|
||||
.BI "\-\-base\-directory " path
|
||||
.BI "-C, \-\-base\-directory " path
|
||||
Change the current working directory of fd to the provided path. This means that search results will
|
||||
be shown with respect to the given base path. Note that relative paths which are passed to fd via the
|
||||
positional \fIpath\fR argument or the \fB\-\-search\-path\fR option will also be resolved relative to
|
||||
@@ -443,6 +453,9 @@ option.
|
||||
|
||||
If no placeholder is present, an implicit "{}" at the end is assumed.
|
||||
|
||||
If --print0 is also given, then a null character (\\0) will be printed between the output for each found entry.
|
||||
This allows another program to easily distinguish the output for each file if the command(s) produce multiple lines.
|
||||
|
||||
Examples:
|
||||
|
||||
- find all *.zip files and unzip them:
|
||||
@@ -456,6 +469,10 @@ Examples:
|
||||
- Convert all *.jpg files to *.png files:
|
||||
|
||||
fd -e jpg -x convert {} {.}.png
|
||||
|
||||
- Run stat for each *.txt file, separated by null characters
|
||||
|
||||
fd -0 -e txt -x stat
|
||||
.RE
|
||||
.TP
|
||||
.BI "\-X, \-\-exec-batch " command
|
||||
@@ -472,6 +489,10 @@ file in a batch and passed as separate arguments.
|
||||
|
||||
If no placeholder is present, an implicit "{}" at the end is assumed.
|
||||
|
||||
Like \-\-exec, subsequent arguments are assumed to be part of
|
||||
.I command
|
||||
until either the end of command arguments or a ';' argument. See above.
|
||||
|
||||
Like \-\-exec, this can be used multiple times, in which case each command will be run in
|
||||
the order given.
|
||||
|
||||
|
||||
-5
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 7.2 KiB |
Vendored
BIN
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
+7
-2
@@ -4,8 +4,8 @@ use std::time::Duration;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use clap::{
|
||||
error::ErrorKind, value_parser, Arg, ArgAction, ArgGroup, ArgMatches, Command, Parser,
|
||||
ValueEnum,
|
||||
Arg, ArgAction, ArgGroup, ArgMatches, Command, Parser, ValueEnum, error::ErrorKind,
|
||||
value_parser,
|
||||
};
|
||||
#[cfg(feature = "completions")]
|
||||
use clap_complete::Shell;
|
||||
@@ -528,6 +528,10 @@ pub struct Opts {
|
||||
)]
|
||||
pub hyperlink: HyperlinkWhen,
|
||||
|
||||
/// Ignore directories containing the named entry.
|
||||
#[arg(long, value_name = "name")]
|
||||
pub ignore_contain: Vec<String>,
|
||||
|
||||
/// Set number of threads to use for searching & executing (default: number
|
||||
/// of available CPU cores)
|
||||
#[arg(long, short = 'j', value_name = "num", hide_short_help = true, value_parser = str::parse::<NonZeroUsize>)]
|
||||
@@ -594,6 +598,7 @@ pub struct Opts {
|
||||
/// relative to this directory.
|
||||
#[arg(
|
||||
long,
|
||||
short = 'C',
|
||||
value_name = "path",
|
||||
hide_short_help = true,
|
||||
help = "Change current working directory",
|
||||
|
||||
@@ -130,6 +130,9 @@ pub struct Config {
|
||||
|
||||
/// Whether or not to use hyperlinks on paths
|
||||
pub hyperlink: bool,
|
||||
|
||||
/// Names that should stop traversal down their parent. (e.g. https://bford.info/cachedir/).
|
||||
pub ignore_contain: Vec<String>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
|
||||
+18
-13
@@ -1,6 +1,5 @@
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use argmax::Command;
|
||||
|
||||
@@ -11,15 +10,15 @@ struct Outputs {
|
||||
stdout: Vec<u8>,
|
||||
stderr: Vec<u8>,
|
||||
}
|
||||
struct OutputBuffer<'a> {
|
||||
output_permission: &'a Mutex<()>,
|
||||
pub struct OutputBuffer {
|
||||
null_separator: bool,
|
||||
outputs: Vec<Outputs>,
|
||||
}
|
||||
|
||||
impl<'a> OutputBuffer<'a> {
|
||||
fn new(output_permission: &'a Mutex<()>) -> Self {
|
||||
impl OutputBuffer {
|
||||
pub fn new(null_separator: bool) -> Self {
|
||||
Self {
|
||||
output_permission,
|
||||
null_separator,
|
||||
outputs: Vec::new(),
|
||||
}
|
||||
}
|
||||
@@ -29,17 +28,19 @@ impl<'a> OutputBuffer<'a> {
|
||||
}
|
||||
|
||||
fn write(self) {
|
||||
// avoid taking the lock if there is nothing to do
|
||||
if self.outputs.is_empty() {
|
||||
// Avoid taking the lock if there is nothing to do.
|
||||
// If null_separator is true, then we still need to write the
|
||||
// null separator, because the output may have been written directly
|
||||
// to stdout
|
||||
if self.outputs.is_empty() && !self.null_separator {
|
||||
return;
|
||||
}
|
||||
// While this lock is active, this thread will be the only thread allowed
|
||||
// to write its outputs.
|
||||
let _lock = self.output_permission.lock().unwrap();
|
||||
|
||||
let stdout = io::stdout();
|
||||
let stderr = io::stderr();
|
||||
|
||||
// While we hold these locks, only this thread will be able
|
||||
// to write its outputs.
|
||||
let mut stdout = stdout.lock();
|
||||
let mut stderr = stderr.lock();
|
||||
|
||||
@@ -47,16 +48,20 @@ impl<'a> OutputBuffer<'a> {
|
||||
let _ = stdout.write_all(&output.stdout);
|
||||
let _ = stderr.write_all(&output.stderr);
|
||||
}
|
||||
if self.null_separator {
|
||||
// If null_separator is enabled, then we should write a \0 at the end
|
||||
// of the output for this entry
|
||||
let _ = stdout.write_all(b"\0");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes a command.
|
||||
pub fn execute_commands<I: Iterator<Item = io::Result<Command>>>(
|
||||
cmds: I,
|
||||
out_perm: &Mutex<()>,
|
||||
mut output_buffer: OutputBuffer,
|
||||
enable_output_buffering: bool,
|
||||
) -> ExitCode {
|
||||
let mut output_buffer = OutputBuffer::new(out_perm);
|
||||
for result in cmds {
|
||||
let mut cmd = match result {
|
||||
Ok(cmd) => cmd,
|
||||
|
||||
+2
-5
@@ -1,8 +1,6 @@
|
||||
use std::sync::Mutex;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::error::print_error;
|
||||
use crate::exit_codes::{merge_exitcodes, ExitCode};
|
||||
use crate::exit_codes::{ExitCode, merge_exitcodes};
|
||||
use crate::walk::WorkerResult;
|
||||
|
||||
use super::CommandSet;
|
||||
@@ -13,7 +11,6 @@ use super::CommandSet;
|
||||
pub fn job(
|
||||
results: impl IntoIterator<Item = WorkerResult>,
|
||||
cmd: &CommandSet,
|
||||
out_perm: &Mutex<()>,
|
||||
config: &Config,
|
||||
) -> ExitCode {
|
||||
// Output should be buffered when only running a single thread
|
||||
@@ -37,7 +34,7 @@ pub fn job(
|
||||
let code = cmd.execute(
|
||||
dir_entry.stripped_path(config),
|
||||
config.path_separator.as_deref(),
|
||||
out_perm,
|
||||
config.null_separator,
|
||||
buffer_output,
|
||||
);
|
||||
ret = merge_exitcodes([ret, code]);
|
||||
|
||||
+5
-5
@@ -6,12 +6,12 @@ use std::io;
|
||||
use std::iter;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Stdio;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use anyhow::{Result, bail};
|
||||
use argmax::Command;
|
||||
|
||||
use crate::exit_codes::{merge_exitcodes, ExitCode};
|
||||
use crate::exec::command::OutputBuffer;
|
||||
use crate::exit_codes::{ExitCode, merge_exitcodes};
|
||||
use crate::fmt::{FormatTemplate, Token};
|
||||
|
||||
use self::command::{execute_commands, handle_cmd_error};
|
||||
@@ -80,14 +80,14 @@ impl CommandSet {
|
||||
&self,
|
||||
input: &Path,
|
||||
path_separator: Option<&str>,
|
||||
out_perm: &Mutex<()>,
|
||||
null_separator: bool,
|
||||
buffer_output: bool,
|
||||
) -> ExitCode {
|
||||
let commands = self
|
||||
.commands
|
||||
.iter()
|
||||
.map(|c| c.generate(input, path_separator));
|
||||
execute_commands(commands, out_perm, buffer_output)
|
||||
execute_commands(commands, OutputBuffer::new(null_separator), buffer_output)
|
||||
}
|
||||
|
||||
pub fn execute_batch<I>(&self, paths: I, limit: usize, path_separator: Option<&str>) -> ExitCode
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
use std::process;
|
||||
|
||||
#[cfg(unix)]
|
||||
use nix::sys::signal::{raise, signal, SigHandler, Signal};
|
||||
use nix::sys::signal::{SigHandler, Signal, raise, signal};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ExitCode {
|
||||
|
||||
+2
-2
@@ -100,13 +100,13 @@ pub fn is_pipe(_: fs::FileType) -> bool {
|
||||
}
|
||||
|
||||
#[cfg(any(unix, target_os = "redox"))]
|
||||
pub fn osstr_to_bytes(input: &OsStr) -> Cow<[u8]> {
|
||||
pub fn osstr_to_bytes(input: &OsStr) -> Cow<'_, [u8]> {
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
Cow::Borrowed(input.as_bytes())
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn osstr_to_bytes(input: &OsStr) -> Cow<[u8]> {
|
||||
pub fn osstr_to_bytes(input: &OsStr) -> Cow<'_, [u8]> {
|
||||
let string = input.to_string_lossy();
|
||||
|
||||
match string {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use anyhow::{Result, anyhow};
|
||||
use nix::unistd::{Group, User};
|
||||
use std::fs;
|
||||
|
||||
|
||||
+51
-31
@@ -1,4 +1,4 @@
|
||||
use jiff::{civil::DateTime, tz::TimeZone, Span, Timestamp, Zoned};
|
||||
use jiff::{Span, Timestamp, Zoned, civil::DateTime, tz::TimeZone};
|
||||
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
@@ -114,16 +114,22 @@ mod tests {
|
||||
assert!(!TimeFilter::before("2min").unwrap().applies_to(&t1m_ago));
|
||||
|
||||
let t10s_before = "2010-10-10 10:10:00";
|
||||
assert!(!TimeFilter::before(t10s_before)
|
||||
.unwrap()
|
||||
.applies_to(&ref_time));
|
||||
assert!(TimeFilter::before(t10s_before)
|
||||
.unwrap()
|
||||
.applies_to(&t1m_ago));
|
||||
assert!(
|
||||
!TimeFilter::before(t10s_before)
|
||||
.unwrap()
|
||||
.applies_to(&ref_time)
|
||||
);
|
||||
assert!(
|
||||
TimeFilter::before(t10s_before)
|
||||
.unwrap()
|
||||
.applies_to(&t1m_ago)
|
||||
);
|
||||
|
||||
assert!(TimeFilter::after(t10s_before)
|
||||
.unwrap()
|
||||
.applies_to(&ref_time));
|
||||
assert!(
|
||||
TimeFilter::after(t10s_before)
|
||||
.unwrap()
|
||||
.applies_to(&ref_time)
|
||||
);
|
||||
assert!(!TimeFilter::after(t10s_before).unwrap().applies_to(&t1m_ago));
|
||||
|
||||
let same_day = "2010-10-10";
|
||||
@@ -142,16 +148,22 @@ mod tests {
|
||||
ref_time = test_time.timestamp();
|
||||
let t1m_ago = ref_time - Duration::from_secs(60);
|
||||
let t10s_before = "2010-10-10T10:10:00+00:00";
|
||||
assert!(!TimeFilter::before(t10s_before)
|
||||
.unwrap()
|
||||
.applies_to(&ref_time));
|
||||
assert!(TimeFilter::before(t10s_before)
|
||||
.unwrap()
|
||||
.applies_to(&t1m_ago));
|
||||
assert!(
|
||||
!TimeFilter::before(t10s_before)
|
||||
.unwrap()
|
||||
.applies_to(&ref_time)
|
||||
);
|
||||
assert!(
|
||||
TimeFilter::before(t10s_before)
|
||||
.unwrap()
|
||||
.applies_to(&t1m_ago)
|
||||
);
|
||||
|
||||
assert!(TimeFilter::after(t10s_before)
|
||||
.unwrap()
|
||||
.applies_to(&ref_time));
|
||||
assert!(
|
||||
TimeFilter::after(t10s_before)
|
||||
.unwrap()
|
||||
.applies_to(&ref_time)
|
||||
);
|
||||
assert!(!TimeFilter::after(t10s_before).unwrap().applies_to(&t1m_ago));
|
||||
|
||||
let ref_timestamp = 1707723412u64; // Mon Feb 12 07:36:52 UTC 2024
|
||||
@@ -166,17 +178,25 @@ mod tests {
|
||||
let t1s_later = ref_time + Duration::from_secs(1);
|
||||
// Timestamp only supported via '@' prefix
|
||||
assert!(TimeFilter::before(&ref_timestamp.to_string()).is_none());
|
||||
assert!(TimeFilter::before(&format!("@{ref_timestamp}"))
|
||||
.unwrap()
|
||||
.applies_to(&t1m_ago));
|
||||
assert!(!TimeFilter::before(&format!("@{ref_timestamp}"))
|
||||
.unwrap()
|
||||
.applies_to(&t1s_later));
|
||||
assert!(!TimeFilter::after(&format!("@{ref_timestamp}"))
|
||||
.unwrap()
|
||||
.applies_to(&t1m_ago));
|
||||
assert!(TimeFilter::after(&format!("@{ref_timestamp}"))
|
||||
.unwrap()
|
||||
.applies_to(&t1s_later));
|
||||
assert!(
|
||||
TimeFilter::before(&format!("@{ref_timestamp}"))
|
||||
.unwrap()
|
||||
.applies_to(&t1m_ago)
|
||||
);
|
||||
assert!(
|
||||
!TimeFilter::before(&format!("@{ref_timestamp}"))
|
||||
.unwrap()
|
||||
.applies_to(&t1s_later)
|
||||
);
|
||||
assert!(
|
||||
!TimeFilter::after(&format!("@{ref_timestamp}"))
|
||||
.unwrap()
|
||||
.applies_to(&t1m_ago)
|
||||
);
|
||||
assert!(
|
||||
TimeFilter::after(&format!("@{ref_timestamp}"))
|
||||
.unwrap()
|
||||
.applies_to(&t1s_later)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -131,7 +131,7 @@ impl FormatTemplate {
|
||||
Placeholder => {
|
||||
s.push(Self::replace_separator(path.as_ref(), path_separator))
|
||||
}
|
||||
Text(ref string) => s.push(string),
|
||||
Text(string) => s.push(string),
|
||||
}
|
||||
}
|
||||
s
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ fn host() -> &'static str {
|
||||
|
||||
#[cfg(not(unix))]
|
||||
const fn host() -> &'static str {
|
||||
""
|
||||
"/"
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
+7
-3
@@ -18,7 +18,7 @@ use std::io::IsTerminal;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use clap::{CommandFactory, Parser};
|
||||
use globset::GlobBuilder;
|
||||
use lscolors::LsColors;
|
||||
@@ -36,18 +36,21 @@ use crate::regex_helper::{pattern_has_uppercase_char, pattern_matches_strings_wi
|
||||
|
||||
// We use jemalloc for performance reasons, see https://github.com/sharkdp/fd/pull/481
|
||||
// FIXME: re-enable jemalloc on macOS, see comment in Cargo.toml file for more infos
|
||||
// This has to be kept in sync with the Cargo.toml file section that declares a
|
||||
// dependency on tikv-jemallocator.
|
||||
#[cfg(all(
|
||||
not(windows),
|
||||
not(target_os = "android"),
|
||||
not(target_os = "macos"),
|
||||
not(target_os = "freebsd"),
|
||||
not(target_os = "openbsd"),
|
||||
not(target_os = "illumos"),
|
||||
not(all(target_env = "musl", target_pointer_width = "32")),
|
||||
not(target_arch = "riscv64"),
|
||||
feature = "use-jemalloc"
|
||||
))]
|
||||
#[global_allocator]
|
||||
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
|
||||
static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
|
||||
|
||||
// vivid --color-mode 8-bit generate molokai
|
||||
const DEFAULT_LS_COLORS: &str = "
|
||||
@@ -323,6 +326,7 @@ fn construct_config(mut opts: Opts, pattern_regexps: &[String]) -> Result<Config
|
||||
actual_path_separator,
|
||||
max_results: opts.max_results(),
|
||||
strip_cwd_prefix: opts.strip_cwd_prefix(|| !(opts.null_separator || has_command)),
|
||||
ignore_contain: opts.ignore_contain,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -482,7 +486,7 @@ fn build_regex(pattern_regex: String, config: &Config) -> Result<regex::bytes::R
|
||||
"{}\n\nNote: You can use the '--fixed-strings' option to search for a \
|
||||
literal string instead of a regular expression. Alternatively, you can \
|
||||
also use the '--glob' option to match on a glob pattern.",
|
||||
e.to_string()
|
||||
e
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
+5
-5
@@ -15,11 +15,11 @@ fn replace_path_separator(path: &str, new_path_separator: &str) -> String {
|
||||
// TODO: this function is performance critical and can probably be optimized
|
||||
pub fn print_entry<W: Write>(stdout: &mut W, entry: &DirEntry, config: &Config) -> io::Result<()> {
|
||||
let mut has_hyperlink = false;
|
||||
if config.hyperlink {
|
||||
if let Some(url) = PathUrl::new(entry.path()) {
|
||||
write!(stdout, "\x1B]8;;{url}\x1B\\")?;
|
||||
has_hyperlink = true;
|
||||
}
|
||||
if config.hyperlink
|
||||
&& let Some(url) = PathUrl::new(entry.path())
|
||||
{
|
||||
write!(stdout, "\x1B]8;;{url}\x1B\\")?;
|
||||
has_hyperlink = true;
|
||||
}
|
||||
|
||||
if let Some(ref format) = config.format {
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
use regex_syntax::hir::Hir;
|
||||
use regex_syntax::ParserBuilder;
|
||||
use regex_syntax::hir::Hir;
|
||||
|
||||
/// Determine if a regex pattern contains a literal uppercase character.
|
||||
pub fn pattern_has_uppercase_char(pattern: &str) -> bool {
|
||||
|
||||
+72
-56
@@ -8,8 +8,8 @@ use std::sync::{Arc, Mutex, MutexGuard};
|
||||
use std::thread;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use crossbeam_channel::{bounded, Receiver, RecvTimeoutError, SendError, Sender};
|
||||
use anyhow::{Result, anyhow};
|
||||
use crossbeam_channel::{Receiver, RecvTimeoutError, SendError, Sender, bounded};
|
||||
use etcetera::BaseStrategy;
|
||||
use ignore::overrides::{Override, OverrideBuilder};
|
||||
use ignore::{WalkBuilder, WalkParallel, WalkState};
|
||||
@@ -19,7 +19,7 @@ use crate::config::Config;
|
||||
use crate::dir_entry::DirEntry;
|
||||
use crate::error::print_error;
|
||||
use crate::exec;
|
||||
use crate::exit_codes::{merge_exitcodes, ExitCode};
|
||||
use crate::exit_codes::{ExitCode, merge_exitcodes};
|
||||
use crate::filesystem;
|
||||
use crate::output;
|
||||
|
||||
@@ -218,10 +218,10 @@ impl<'a, W: Write> ReceiverBuffer<'a, W> {
|
||||
}
|
||||
|
||||
self.num_results += 1;
|
||||
if let Some(max_results) = self.config.max_results {
|
||||
if self.num_results >= max_results {
|
||||
return self.stop();
|
||||
}
|
||||
if let Some(max_results) = self.config.max_results
|
||||
&& self.num_results >= max_results
|
||||
{
|
||||
return self.stop();
|
||||
}
|
||||
}
|
||||
WorkerResult::Error(err) => {
|
||||
@@ -250,11 +250,11 @@ impl<'a, W: Write> ReceiverBuffer<'a, W> {
|
||||
|
||||
/// Output a path.
|
||||
fn print(&mut self, entry: &DirEntry) -> Result<(), ExitCode> {
|
||||
if let Err(e) = output::print_entry(&mut self.stdout, entry, self.config) {
|
||||
if e.kind() != ::std::io::ErrorKind::BrokenPipe {
|
||||
print_error(format!("Could not write to output: {e}"));
|
||||
return Err(ExitCode::GeneralError);
|
||||
}
|
||||
if let Err(e) = output::print_entry(&mut self.stdout, entry, self.config)
|
||||
&& e.kind() != ::std::io::ErrorKind::BrokenPipe
|
||||
{
|
||||
print_error(format!("Could not write to output: {e}"));
|
||||
return Err(ExitCode::GeneralError);
|
||||
}
|
||||
|
||||
if self.interrupt_flag.load(Ordering::Relaxed) {
|
||||
@@ -368,18 +368,18 @@ impl WorkerState {
|
||||
builder.add_custom_ignore_filename(".fdignore");
|
||||
}
|
||||
|
||||
if config.read_global_ignore {
|
||||
if let Ok(basedirs) = etcetera::choose_base_strategy() {
|
||||
let global_ignore_file = basedirs.config_dir().join("fd").join("ignore");
|
||||
if global_ignore_file.is_file() {
|
||||
let result = builder.add_ignore(global_ignore_file);
|
||||
match result {
|
||||
Some(ignore::Error::Partial(_)) => (),
|
||||
Some(err) => {
|
||||
print_error(format!("Malformed pattern in global ignore file. {err}."));
|
||||
}
|
||||
None => (),
|
||||
if config.read_global_ignore
|
||||
&& let Ok(basedirs) = etcetera::choose_base_strategy()
|
||||
{
|
||||
let global_ignore_file = basedirs.config_dir().join("fd").join("ignore");
|
||||
if global_ignore_file.is_file() {
|
||||
let result = builder.add_ignore(global_ignore_file);
|
||||
match result {
|
||||
Some(ignore::Error::Partial(_)) => (),
|
||||
Some(err) => {
|
||||
print_error(format!("Malformed pattern in global ignore file. {err}."));
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -413,8 +413,6 @@ impl WorkerState {
|
||||
if cmd.in_batch_mode() {
|
||||
exec::batch(rx.into_iter().flatten(), cmd, config)
|
||||
} else {
|
||||
let out_perm = Mutex::new(());
|
||||
|
||||
thread::scope(|scope| {
|
||||
// Each spawned job will store its thread handle in here.
|
||||
let threads = config.threads;
|
||||
@@ -423,8 +421,8 @@ impl WorkerState {
|
||||
let rx = rx.clone();
|
||||
|
||||
// Spawn a job thread that will listen for and execute inputs.
|
||||
let handle = scope
|
||||
.spawn(|| exec::job(rx.into_iter().flatten(), cmd, &out_perm, config));
|
||||
let handle =
|
||||
scope.spawn(|| exec::job(rx.into_iter().flatten(), cmd, config));
|
||||
|
||||
// Push the handle of the spawned thread into the vector for later joining.
|
||||
handles.push(handle);
|
||||
@@ -449,11 +447,12 @@ impl WorkerState {
|
||||
let quit_flag = self.quit_flag.as_ref();
|
||||
|
||||
let mut limit = 0x100;
|
||||
if let Some(cmd) = &config.command {
|
||||
if !cmd.in_batch_mode() && config.threads > 1 {
|
||||
// Evenly distribute work between multiple receivers
|
||||
limit = 1;
|
||||
}
|
||||
if let Some(cmd) = &config.command
|
||||
&& !cmd.in_batch_mode()
|
||||
&& config.threads > 1
|
||||
{
|
||||
// Evenly distribute work between multiple receivers
|
||||
limit = 1;
|
||||
}
|
||||
let mut tx = BatchSender::new(tx.clone(), limit);
|
||||
|
||||
@@ -462,11 +461,28 @@ impl WorkerState {
|
||||
return WalkState::Quit;
|
||||
}
|
||||
|
||||
let entry = match entry {
|
||||
Ok(ref e) if e.depth() == 0 => {
|
||||
if let Ok(e) = &entry {
|
||||
// If the entry is a directory that contains a
|
||||
// "ignore contain" file", we want to skip this
|
||||
// directory.
|
||||
// Check the filetype first to avoid unnecessary
|
||||
// syscalls.
|
||||
if e.file_type().is_some_and(|t| t.is_dir()) {
|
||||
let entry_path = e.path();
|
||||
if config
|
||||
.ignore_contain
|
||||
.iter()
|
||||
.any(|ic| entry_path.join(ic).exists())
|
||||
{
|
||||
return WalkState::Skip;
|
||||
}
|
||||
}
|
||||
if e.depth() == 0 {
|
||||
// Skip the root directory entry.
|
||||
return WalkState::Continue;
|
||||
}
|
||||
}
|
||||
let entry = match entry {
|
||||
Ok(e) => DirEntry::normal(e),
|
||||
Err(ignore::Error::WithPath {
|
||||
path,
|
||||
@@ -488,21 +504,21 @@ impl WorkerState {
|
||||
})) {
|
||||
Ok(_) => WalkState::Continue,
|
||||
Err(_) => WalkState::Quit,
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
return match tx.send(WorkerResult::Error(err)) {
|
||||
Ok(_) => WalkState::Continue,
|
||||
Err(_) => WalkState::Quit,
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(min_depth) = config.min_depth {
|
||||
if entry.depth().map_or(true, |d| d < min_depth) {
|
||||
return WalkState::Continue;
|
||||
}
|
||||
if let Some(min_depth) = config.min_depth
|
||||
&& entry.depth().is_none_or(|d| d < min_depth)
|
||||
{
|
||||
return WalkState::Continue;
|
||||
}
|
||||
|
||||
// Check the name first, since it doesn't require metadata
|
||||
@@ -542,10 +558,10 @@ impl WorkerState {
|
||||
}
|
||||
|
||||
// Filter out unwanted file types.
|
||||
if let Some(ref file_types) = config.file_types {
|
||||
if file_types.should_ignore(&entry) {
|
||||
return WalkState::Continue;
|
||||
}
|
||||
if let Some(ref file_types) = config.file_types
|
||||
&& file_types.should_ignore(&entry)
|
||||
{
|
||||
return WalkState::Continue;
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
@@ -584,24 +600,24 @@ impl WorkerState {
|
||||
// Filter out unwanted modification times
|
||||
if !config.time_constraints.is_empty() {
|
||||
let mut matched = false;
|
||||
if let Some(metadata) = entry.metadata() {
|
||||
if let Ok(modified) = metadata.modified() {
|
||||
matched = config
|
||||
.time_constraints
|
||||
.iter()
|
||||
.all(|tf| tf.applies_to(&modified));
|
||||
}
|
||||
if let Some(metadata) = entry.metadata()
|
||||
&& let Ok(modified) = metadata.modified()
|
||||
{
|
||||
matched = config
|
||||
.time_constraints
|
||||
.iter()
|
||||
.all(|tf| tf.applies_to(&modified));
|
||||
}
|
||||
if !matched {
|
||||
return WalkState::Continue;
|
||||
}
|
||||
}
|
||||
|
||||
if config.is_printing() {
|
||||
if let Some(ls_colors) = &config.ls_colors {
|
||||
// Compute colors in parallel
|
||||
entry.style(ls_colors);
|
||||
}
|
||||
if config.is_printing()
|
||||
&& let Some(ls_colors) = &config.ls_colors
|
||||
{
|
||||
// Compute colors in parallel
|
||||
entry.style(ls_colors);
|
||||
}
|
||||
|
||||
let send_result = tx.send(WorkerResult::Entry(entry));
|
||||
|
||||
+2
-12
@@ -74,18 +74,8 @@ fn create_config_directory_with_global_ignore(ignore_file_content: &str) -> io::
|
||||
|
||||
/// Find the *fd* executable.
|
||||
fn find_fd_exe() -> PathBuf {
|
||||
// Tests exe is in target/debug/deps, the *fd* exe is in target/debug
|
||||
let root = env::current_exe()
|
||||
.expect("tests executable")
|
||||
.parent()
|
||||
.expect("tests executable directory")
|
||||
.parent()
|
||||
.expect("fd executable directory")
|
||||
.to_path_buf();
|
||||
|
||||
let exe_name = if cfg!(windows) { "fd.exe" } else { "fd" };
|
||||
|
||||
root.join(exe_name)
|
||||
// Read the location of the fd executable from the environment
|
||||
PathBuf::from(env::var("CARGO_BIN_EXE_fd").unwrap_or(env!("CARGO_BIN_EXE_fd").to_string()))
|
||||
}
|
||||
|
||||
/// Format an error message for when *fd* did not exit successfully.
|
||||
|
||||
+74
-8
@@ -1765,12 +1765,10 @@ fn test_exec() {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO test for windows
|
||||
#[cfg(not(windows))]
|
||||
#[test]
|
||||
fn test_exec_multi() {
|
||||
// TODO test for windows
|
||||
if cfg!(windows) {
|
||||
return;
|
||||
}
|
||||
let (te, abs_path) = get_test_env_with_abs_path(DEFAULT_DIRS, DEFAULT_FILES);
|
||||
|
||||
te.assert_output(
|
||||
@@ -1813,9 +1811,11 @@ fn test_exec_multi() {
|
||||
e1 e2",
|
||||
);
|
||||
|
||||
// We use printf here because we need to suppress a newline and
|
||||
// echo -n is not POSIX-compliant.
|
||||
te.assert_output(
|
||||
&[
|
||||
"foo", "--exec", "echo", "-n", "{/}: ", ";", "--exec", "echo", "{//}",
|
||||
"foo", "--exec", "printf", "%s", "{/}: ", ";", "--exec", "printf", "%s\\n", "{//}",
|
||||
],
|
||||
"a.foo: .
|
||||
b.foo: ./one
|
||||
@@ -1826,6 +1826,21 @@ fn test_exec_multi() {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
#[test]
|
||||
fn test_exec_nulls() {
|
||||
let te = TestEnv::new(DEFAULT_DIRS, DEFAULT_FILES);
|
||||
te.assert_output(
|
||||
&["foo", "--print0", "--exec", "printf", "p=%s"],
|
||||
"p=./a.fooNULL
|
||||
p=./one/b.fooNULL
|
||||
p=./one/two/C.Foo2NULL
|
||||
p=./one/two/c.fooNULL
|
||||
p=./one/two/three/d.fooNULL
|
||||
p=./one/two/three/directory_fooNULL",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exec_batch() {
|
||||
let (te, abs_path) = get_test_env_with_abs_path(DEFAULT_DIRS, DEFAULT_FILES);
|
||||
@@ -2450,7 +2465,7 @@ fn test_max_results() {
|
||||
assert_just_one_result_with_option("--max-results=1");
|
||||
assert_just_one_result_with_option("-1");
|
||||
|
||||
// check that --max-results & -1 conflic with --exec
|
||||
// check that --max-results & -1 conflict with --exec
|
||||
te.assert_failure(&["thing", "--max-results=0", "--exec=cat"]);
|
||||
te.assert_failure(&["thing", "-1", "--exec=cat"]);
|
||||
te.assert_failure(&["thing", "--max-results=1", "-1", "--exec=cat"]);
|
||||
@@ -2601,7 +2616,7 @@ fn test_strip_cwd_prefix() {
|
||||
/// When fd is ran from a non-existent working directory, but an existent
|
||||
/// directory is passed in the arguments, it should still run fine
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
#[cfg(all(not(windows), not(target_os = "illumos")))]
|
||||
fn test_invalid_cwd() {
|
||||
let te = TestEnv::new(&[], &[]);
|
||||
|
||||
@@ -2682,7 +2697,7 @@ fn test_hyperlink() {
|
||||
#[cfg(unix)]
|
||||
let hostname = nix::unistd::gethostname().unwrap().into_string().unwrap();
|
||||
#[cfg(not(unix))]
|
||||
let hostname = "";
|
||||
let hostname = "/";
|
||||
|
||||
let expected = format!(
|
||||
"\x1b]8;;file://{}{}/a.foo\x1b\\a.foo\x1b]8;;\x1b\\",
|
||||
@@ -2692,3 +2707,54 @@ fn test_hyperlink() {
|
||||
|
||||
te.assert_output(&["--hyperlink=always", "a.foo"], &expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ignore_contain() {
|
||||
let te = TestEnv::new(
|
||||
&["include", "exclude", "exclude/sub", "other"],
|
||||
&[
|
||||
"top",
|
||||
"include/foo",
|
||||
"exclude/CACHEDIR.TAG",
|
||||
"exclude/sub/nope",
|
||||
"other/ignoremyparent",
|
||||
],
|
||||
);
|
||||
let expected = "include/
|
||||
include/foo
|
||||
symlink
|
||||
top";
|
||||
te.assert_output(
|
||||
&[
|
||||
"--ignore-contain=CACHEDIR.TAG",
|
||||
"--ignore-contain=ignoremyparent",
|
||||
".",
|
||||
],
|
||||
expected,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ignore_contain_precedence_over_depth_check() {
|
||||
let te = TestEnv::new(
|
||||
&["include", "exclude", "exclude/sub"],
|
||||
&[
|
||||
"top",
|
||||
"include/foo",
|
||||
"exclude/CACHEDIR.TAG",
|
||||
"exclude/sub/nope",
|
||||
],
|
||||
);
|
||||
let expected = "include/foo";
|
||||
te.assert_output(
|
||||
&["--ignore-contain=CACHEDIR.TAG", "--min-depth=2", "."],
|
||||
expected,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ignore_contain_precedence_over_root_check() {
|
||||
let te = TestEnv::new(&["include"], &["CACHEDIR.TAG", "top", "include/foo"]);
|
||||
let expected = "";
|
||||
te.assert_output(&["--ignore-contain=CACHEDIR.TAG", "."], expected);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user