mirror of
https://github.com/sharkdp/fd.git
synced 2026-09-01 15:12:46 +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: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "monthly"
|
interval: "monthly"
|
||||||
|
cooldown:
|
||||||
|
default-days: 7
|
||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: "github-actions"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "daily"
|
||||||
|
cooldown:
|
||||||
|
default-days: 7
|
||||||
|
|||||||
+94
-47
@@ -13,12 +13,17 @@ on:
|
|||||||
tags:
|
tags:
|
||||||
- '*'
|
- '*'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
crate_metadata:
|
crate_metadata:
|
||||||
name: Extract crate metadata
|
name: Extract crate metadata
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
- name: Extract crate information
|
- name: Extract crate information
|
||||||
id: crate_metadata
|
id: crate_metadata
|
||||||
run: |
|
run: |
|
||||||
@@ -41,7 +46,9 @@ jobs:
|
|||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
components: rustfmt
|
components: rustfmt
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
- run: cargo fmt -- --check
|
- run: cargo fmt -- --check
|
||||||
|
|
||||||
lint_check:
|
lint_check:
|
||||||
@@ -51,7 +58,9 @@ jobs:
|
|||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
components: clippy
|
components: clippy
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
- run: cargo clippy --all-targets --all-features -- -Dwarnings
|
- run: cargo clippy --all-targets --all-features -- -Dwarnings
|
||||||
|
|
||||||
min_version:
|
min_version:
|
||||||
@@ -60,7 +69,9 @@ jobs:
|
|||||||
needs: crate_metadata
|
needs: crate_metadata
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout source code
|
- 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 }})
|
- name: Install rust toolchain (v${{ needs.crate_metadata.outputs.msrv }})
|
||||||
uses: dtolnay/rust-toolchain@master
|
uses: dtolnay/rust-toolchain@master
|
||||||
@@ -68,41 +79,49 @@ jobs:
|
|||||||
toolchain: ${{ needs.crate_metadata.outputs.msrv }}
|
toolchain: ${{ needs.crate_metadata.outputs.msrv }}
|
||||||
components: clippy
|
components: clippy
|
||||||
- name: Run clippy (on minimum supported rust version to prevent warnings we can't fix)
|
- 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
|
- name: Run tests
|
||||||
run: cargo test --locked ${{ env.MSRV_FEATURES }}
|
run: cargo test --locked "${MSRV_FEATURES}"
|
||||||
|
|
||||||
build:
|
build:
|
||||||
name: ${{ matrix.job.target }} (${{ matrix.job.os }})
|
name: '${{ matrix.job.target }} (${{ matrix.job.os }})'
|
||||||
runs-on: ${{ matrix.job.os }}
|
runs-on: ${{ matrix.job.os }}
|
||||||
needs: crate_metadata
|
needs: crate_metadata
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
contents: write
|
||||||
|
attestations: write
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
job:
|
job:
|
||||||
- { target: aarch64-unknown-linux-gnu , 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-22.04, use-cross: true }
|
- { target: aarch64-unknown-linux-musl , os: ubuntu-24.04, use-cross: true }
|
||||||
- { target: arm-unknown-linux-gnueabihf , os: ubuntu-22.04, use-cross: true }
|
- { target: arm-unknown-linux-gnueabihf , os: ubuntu-24.04, use-cross: true }
|
||||||
- { target: arm-unknown-linux-musleabihf, os: ubuntu-22.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-pc-windows-msvc , os: windows-2022 }
|
||||||
- { target: i686-unknown-linux-gnu , os: ubuntu-22.04, use-cross: true }
|
- { target: i686-unknown-linux-gnu , os: ubuntu-24.04, use-cross: true }
|
||||||
- { target: i686-unknown-linux-musl , os: ubuntu-22.04, use-cross: true }
|
- { target: i686-unknown-linux-musl , os: ubuntu-24.04, use-cross: true }
|
||||||
- { target: x86_64-apple-darwin , os: macos-13 }
|
|
||||||
- { target: aarch64-apple-darwin , os: macos-14 }
|
- { target: aarch64-apple-darwin , os: macos-14 }
|
||||||
- { target: x86_64-pc-windows-gnu , os: windows-2022 }
|
- { target: x86_64-pc-windows-gnu , os: windows-2022 }
|
||||||
- { target: x86_64-pc-windows-msvc , 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: aarch64-pc-windows-msvc , os: windows-11-arm }
|
||||||
- { target: x86_64-unknown-linux-musl , os: ubuntu-22.04, use-cross: true }
|
- { 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:
|
env:
|
||||||
BUILD_CMD: cargo
|
BUILD_CMD: "${{ matrix.job.use-cross && 'cross' || 'cargo' }}"
|
||||||
|
target: ${{ matrix.job.target }}
|
||||||
|
name: ${{ needs.crate_metadata.outputs.name }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Install prerequisites
|
- name: Install prerequisites
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
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 ;;
|
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 ;;
|
aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;;
|
||||||
esac
|
esac
|
||||||
@@ -111,21 +130,22 @@ jobs:
|
|||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
targets: ${{ matrix.job.target }}
|
targets: ${{ matrix.job.target }}
|
||||||
# On windows, for now build with 1.77.2, so that it works on windows 7.
|
toolchain: "stable"
|
||||||
# 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' }}"
|
|
||||||
|
|
||||||
- name: Install cross
|
- name: Install cross
|
||||||
if: matrix.job.use-cross
|
if: matrix.job.use-cross
|
||||||
uses: taiki-e/install-action@v2
|
env:
|
||||||
with:
|
cross_version: "v0.2.5"
|
||||||
tool: cross
|
package_name: "cross-x86_64-unknown-linux-gnu.tar.gz"
|
||||||
|
GH_TOKEN: "${{ github.token }}"
|
||||||
- name: Overwrite build command env variable
|
run: |
|
||||||
if: matrix.job.use-cross
|
dir="$HOME/.local/bin/"
|
||||||
shell: bash
|
mkdir -p "$dir"
|
||||||
run: echo "BUILD_CMD=cross" >> $GITHUB_ENV
|
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)
|
- name: Show version information (Rust, cargo, GCC)
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@@ -138,7 +158,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
shell: bash
|
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
|
- name: Set binary name & path
|
||||||
id: bin
|
id: bin
|
||||||
@@ -146,13 +166,13 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
# Figure out suffix of binary
|
# Figure out suffix of binary
|
||||||
EXE_suffix=""
|
EXE_suffix=""
|
||||||
case ${{ matrix.job.target }} in
|
case ${target} in
|
||||||
*-pc-windows-*) EXE_suffix=".exe" ;;
|
*-pc-windows-*) EXE_suffix=".exe" ;;
|
||||||
esac;
|
esac;
|
||||||
|
|
||||||
# Setup paths
|
# Setup paths
|
||||||
BIN_NAME="${{ needs.crate_metadata.outputs.name }}${EXE_suffix}"
|
BIN_NAME="${name}${EXE_suffix}"
|
||||||
BIN_PATH="target/${{ matrix.job.target }}/release/${BIN_NAME}"
|
BIN_PATH="target/${target}/release/${BIN_NAME}"
|
||||||
|
|
||||||
# Let subsequent steps know where to find the binary
|
# Let subsequent steps know where to find the binary
|
||||||
echo "BIN_PATH=${BIN_PATH}" >> $GITHUB_OUTPUT
|
echo "BIN_PATH=${BIN_PATH}" >> $GITHUB_OUTPUT
|
||||||
@@ -164,12 +184,17 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
# test only library unit tests and binary for arm-type targets
|
# test only library unit tests and binary for arm-type targets
|
||||||
unset CARGO_TEST_OPTIONS
|
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
|
echo "CARGO_TEST_OPTIONS=${CARGO_TEST_OPTIONS}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
shell: bash
|
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
|
- name: Generate completions
|
||||||
id: completions
|
id: completions
|
||||||
@@ -179,31 +204,37 @@ jobs:
|
|||||||
- name: Create tarball
|
- name: Create tarball
|
||||||
id: package
|
id: package
|
||||||
shell: bash
|
shell: bash
|
||||||
|
env:
|
||||||
|
BIN_PATH: ${{ steps.bin.outputs.BIN_PATH }}
|
||||||
|
version: ${{ needs.crate_metadata.outputs.version }}
|
||||||
run: |
|
run: |
|
||||||
PKG_suffix=".tar.gz" ; case ${{ matrix.job.target }} in *-pc-windows-*) PKG_suffix=".zip" ;; esac;
|
PKG_suffix=".tar.gz"
|
||||||
PKG_BASENAME=${{ needs.crate_metadata.outputs.name }}-v${{ needs.crate_metadata.outputs.version }}-${{ matrix.job.target }}
|
case ${target} in
|
||||||
|
*-pc-windows-*) PKG_suffix=".zip" ;;
|
||||||
|
esac
|
||||||
|
PKG_BASENAME=${name}-v${version}-${target}
|
||||||
PKG_NAME=${PKG_BASENAME}${PKG_suffix}
|
PKG_NAME=${PKG_BASENAME}${PKG_suffix}
|
||||||
echo "PKG_NAME=${PKG_NAME}" >> $GITHUB_OUTPUT
|
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}/"
|
ARCHIVE_DIR="${PKG_STAGING}/${PKG_BASENAME}/"
|
||||||
mkdir -p "${ARCHIVE_DIR}"
|
mkdir -p "${ARCHIVE_DIR}"
|
||||||
|
|
||||||
# Binary
|
# Binary
|
||||||
cp "${{ steps.bin.outputs.BIN_PATH }}" "$ARCHIVE_DIR"
|
cp "${BIN_PATH}" "$ARCHIVE_DIR"
|
||||||
|
|
||||||
# README, LICENSE and CHANGELOG files
|
# README, LICENSE and CHANGELOG files
|
||||||
cp "README.md" "LICENSE-MIT" "LICENSE-APACHE" "CHANGELOG.md" "$ARCHIVE_DIR"
|
cp "README.md" "LICENSE-MIT" "LICENSE-APACHE" "CHANGELOG.md" "$ARCHIVE_DIR"
|
||||||
|
|
||||||
# Man page
|
# Man page
|
||||||
cp 'doc/${{ needs.crate_metadata.outputs.name }}.1' "$ARCHIVE_DIR"
|
cp "doc/${name}.1" "$ARCHIVE_DIR"
|
||||||
|
|
||||||
# Autocompletion files
|
# Autocompletion files
|
||||||
cp -r autocomplete "${ARCHIVE_DIR}"
|
cp -r autocomplete "${ARCHIVE_DIR}"
|
||||||
|
|
||||||
# base compressed package
|
# base compressed package
|
||||||
pushd "${PKG_STAGING}/" >/dev/null
|
pushd "${PKG_STAGING}/" >/dev/null
|
||||||
case ${{ matrix.job.target }} in
|
case ${target} in
|
||||||
*-pc-windows-*) 7z -y a "${PKG_NAME}" "${PKG_BASENAME}"/* | tail -2 ;;
|
*-pc-windows-*) 7z -y a "${PKG_NAME}" "${PKG_BASENAME}"/* | tail -2 ;;
|
||||||
*) tar czf "${PKG_NAME}" "${PKG_BASENAME}"/* ;;
|
*) tar czf "${PKG_NAME}" "${PKG_BASENAME}"/* ;;
|
||||||
esac;
|
esac;
|
||||||
@@ -223,13 +254,15 @@ jobs:
|
|||||||
BIN_PATH: ${{ steps.bin.outputs.BIN_PATH }}
|
BIN_PATH: ${{ steps.bin.outputs.BIN_PATH }}
|
||||||
|
|
||||||
- name: "Artifact upload: tarball"
|
- name: "Artifact upload: tarball"
|
||||||
uses: actions/upload-artifact@master
|
id: upload-tarball
|
||||||
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
|
||||||
with:
|
with:
|
||||||
name: ${{ steps.package.outputs.PKG_NAME }}
|
name: ${{ steps.package.outputs.PKG_NAME }}
|
||||||
path: ${{ steps.package.outputs.PKG_PATH }}
|
path: ${{ steps.package.outputs.PKG_PATH }}
|
||||||
|
|
||||||
- name: "Artifact upload: Debian package"
|
- 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
|
if: steps.debian-package.outputs.DPKG_NAME
|
||||||
with:
|
with:
|
||||||
name: ${{ steps.debian-package.outputs.DPKG_NAME }}
|
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
|
unset IS_RELEASE ; if [[ $GITHUB_REF =~ ^refs/tags/v[0-9].* ]]; then IS_RELEASE='true' ; fi
|
||||||
echo "IS_RELEASE=${IS_RELEASE}" >> $GITHUB_OUTPUT
|
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
|
- 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
|
if: steps.is-release.outputs.IS_RELEASE
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
@@ -258,7 +305,7 @@ jobs:
|
|||||||
needs: build
|
needs: build
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
steps:
|
steps:
|
||||||
- uses: vedantmgoyal2009/winget-releaser@v2
|
- uses: vedantmgoyal9/winget-releaser@4ffc7888bffd451b357355dc214d43bb9f23917e # v2
|
||||||
with:
|
with:
|
||||||
identifier: sharkdp.fd
|
identifier: sharkdp.fd
|
||||||
installers-regex: '-pc-windows-msvc\.zip$'
|
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
|
## Features
|
||||||
|
|
||||||
@@ -15,9 +46,18 @@
|
|||||||
- 'M' no longer means "month", as that could be confusing with minutes. Use "mo", "mos", "month" or "months" instead.
|
- '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
|
- 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.
|
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
|
## 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
|
# 10.2.0
|
||||||
|
|
||||||
|
|||||||
Generated
+483
-212
File diff suppressed because it is too large
Load Diff
+46
-21
@@ -1,6 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
authors = ["David Peter <mail@david-peter.de>"]
|
authors = ["David Peter <mail@david-peter.de>"]
|
||||||
build = "build.rs"
|
|
||||||
categories = ["command-line-utilities"]
|
categories = ["command-line-utilities"]
|
||||||
description = "fd is a simple, fast and user-friendly alternative to find."
|
description = "fd is a simple, fast and user-friendly alternative to find."
|
||||||
exclude = ["/benchmarks/*"]
|
exclude = ["/benchmarks/*"]
|
||||||
@@ -17,9 +16,9 @@ license = "MIT OR Apache-2.0"
|
|||||||
name = "fd-find"
|
name = "fd-find"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/sharkdp/fd"
|
repository = "https://github.com/sharkdp/fd"
|
||||||
version = "10.2.0"
|
version = "10.4.2"
|
||||||
edition= "2021"
|
edition= "2024"
|
||||||
rust-version = "1.77.2"
|
rust-version = "1.90.0"
|
||||||
|
|
||||||
[badges.appveyor]
|
[badges.appveyor]
|
||||||
repository = "sharkdp/fd"
|
repository = "sharkdp/fd"
|
||||||
@@ -31,37 +30,34 @@ repository = "sharkdp/fd"
|
|||||||
name = "fd"
|
name = "fd"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
version_check = "0.9"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aho-corasick = "1.1"
|
aho-corasick = "1.1"
|
||||||
nu-ansi-term = "0.50"
|
nu-ansi-term = "0.50"
|
||||||
argmax = "0.3.1"
|
argmax = "0.4.0"
|
||||||
ignore = "0.4.23"
|
ignore = "0.4.25"
|
||||||
regex = "1.11.1"
|
regex = "1.12.2"
|
||||||
regex-syntax = "0.8"
|
regex-syntax = "0.8"
|
||||||
ctrlc = "3.2"
|
ctrlc = "3.5"
|
||||||
globset = "0.4"
|
globset = "0.4"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
etcetera = "0.10"
|
etcetera = "0.11"
|
||||||
normpath = "1.1.1"
|
normpath = "1.1.1"
|
||||||
crossbeam-channel = "0.5.15"
|
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"
|
faccess = "0.2.4"
|
||||||
jiff = "0.2.5"
|
jiff = "0.2.18"
|
||||||
|
|
||||||
[dependencies.clap]
|
[dependencies.clap]
|
||||||
version = "4.5.35"
|
version = "4.5.54"
|
||||||
features = ["suggestions", "color", "wrap_help", "cargo", "derive"]
|
features = ["suggestions", "color", "wrap_help", "cargo", "derive"]
|
||||||
|
|
||||||
[dependencies.lscolors]
|
[dependencies.lscolors]
|
||||||
version = "0.20"
|
version = "0.21"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["nu-ansi-term"]
|
features = ["nu-ansi-term"]
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[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]
|
[target.'cfg(all(unix, not(target_os = "redox")))'.dependencies]
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
@@ -69,22 +65,51 @@ libc = "0.2"
|
|||||||
# FIXME: Re-enable jemalloc on macOS
|
# FIXME: Re-enable jemalloc on macOS
|
||||||
# jemalloc is currently disabled on macOS due to a bug in jemalloc in combination with 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.
|
# 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]
|
# This has to be kept in sync with src/main.rs where the allocator for
|
||||||
jemallocator = {version = "0.5.4", optional = true}
|
# 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]
|
[dev-dependencies]
|
||||||
diff = "0.1"
|
diff = "0.1"
|
||||||
tempfile = "3.19"
|
tempfile = "3.24"
|
||||||
filetime = "0.2"
|
filetime = "0.2"
|
||||||
test-case = "3.3"
|
test-case = "3.3"
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
debug = "line-tables-only"
|
||||||
|
|
||||||
|
[profile.dev.package."*"]
|
||||||
|
debug = false
|
||||||
|
|
||||||
|
[profile.debugging]
|
||||||
|
inherits = "dev"
|
||||||
|
debug = true
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
strip = true
|
strip = true
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
use-jemalloc = ["jemallocator"]
|
use-jemalloc = ["tikv-jemallocator"]
|
||||||
completions = ["clap_complete"]
|
completions = ["clap_complete"]
|
||||||
base = ["use-jemalloc"]
|
base = ["use-jemalloc"]
|
||||||
default = ["use-jemalloc", "completions"]
|
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
|
cargo build --profile $(PROFILE) --locked
|
||||||
|
|
||||||
.PHONY: completions
|
.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
|
comp_dir=@mkdir -p autocomplete
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ autocomplete/fd.fish: $(EXE)
|
|||||||
$(comp_dir)
|
$(comp_dir)
|
||||||
$(EXE) --gen-completions fish > $@
|
$(EXE) --gen-completions fish > $@
|
||||||
|
|
||||||
autocomplete/fd.ps1: $(EXE)
|
autocomplete/_fd.ps1: $(EXE)
|
||||||
$(comp_dir)
|
$(comp_dir)
|
||||||
$(EXE) --gen-completions powershell > $@
|
$(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
|
* The command name is *50%* shorter[\*](https://github.com/ggreer/the_silver_searcher) than
|
||||||
`find` :-).
|
`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
|
## 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.
|
also includes a much more detailed help text.
|
||||||
|
|
||||||
```
|
```
|
||||||
Usage: fd [OPTIONS] [pattern] [path]...
|
Usage: fd [OPTIONS] [pattern [path]...]
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
[pattern] the search pattern (a regular expression, unless '--glob' is used; optional)
|
[pattern] the search pattern (a regular expression, unless '--glob' is used; optional)
|
||||||
@@ -331,10 +344,13 @@ Options:
|
|||||||
always, never]
|
always, never]
|
||||||
--hyperlink[=<when>] Add hyperlinks to output paths [default: never] [possible
|
--hyperlink[=<when>] Add hyperlinks to output paths [default: never] [possible
|
||||||
values: auto, always, never]
|
values: auto, always, never]
|
||||||
|
--ignore-contain <name> Ignore directories containing the named entry
|
||||||
-h, --help Print help (see more with '--help')
|
-h, --help Print help (see more with '--help')
|
||||||
-V, --version Print version
|
-V, --version Print version
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note that options can be given after the pattern and/or path as well.
|
||||||
|
|
||||||
## Benchmark
|
## Benchmark
|
||||||
|
|
||||||
Let's search my home folder for files that end in `[0-9].jpg`. It contains ~750.000
|
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
|
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.
|
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
|
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
|
### On NixOS / via Nix
|
||||||
|
|
||||||
You can use the [Nix package manager](https://nixos.org/nix/) to install `fd`:
|
You can use the [Nix package manager](https://nixos.org/nix/) to install `fd`:
|
||||||
@@ -722,6 +745,37 @@ cargo test
|
|||||||
cargo install --path .
|
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
|
## Maintainers
|
||||||
|
|
||||||
- [sharkdp](https://github.com/sharkdp)
|
- [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 ]
|
.IR num ]
|
||||||
.RB [ \-x
|
.RB [ \-x
|
||||||
.IR cmd ]
|
.IR cmd ]
|
||||||
.RI [ pattern ]
|
.RI [ pattern
|
||||||
.RI [ path... ]
|
.RI [ path... ]]
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
.B fd
|
.B fd
|
||||||
is a simple, fast and user-friendly alternative to
|
is a simple, fast and user-friendly alternative to
|
||||||
@@ -34,6 +34,12 @@ By default
|
|||||||
.B fd
|
.B fd
|
||||||
will exclude hidden files and directories, as well as any files that match gitignore rules
|
will exclude hidden files and directories, as well as any files that match gitignore rules
|
||||||
or ignore rules in .ignore or .fdignore files.
|
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
|
.SH OPTIONS
|
||||||
.TP
|
.TP
|
||||||
.B \-H, \-\-hidden
|
.B \-H, \-\-hidden
|
||||||
@@ -259,6 +265,10 @@ Examples:
|
|||||||
\-\-exclude '*.pyc'
|
\-\-exclude '*.pyc'
|
||||||
\-\-exclude node_modules
|
\-\-exclude node_modules
|
||||||
.TP
|
.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
|
.BI "\-\-ignore-file " path
|
||||||
Add a custom ignore-file in '.gitignore' format.
|
Add a custom ignore-file in '.gitignore' format.
|
||||||
These files have a low precedence.
|
These files have a low precedence.
|
||||||
@@ -379,7 +389,7 @@ Examples:
|
|||||||
\-\-owner :students
|
\-\-owner :students
|
||||||
\-\-owner "!john:students"
|
\-\-owner "!john:students"
|
||||||
.TP
|
.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
|
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
|
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
|
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 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:
|
Examples:
|
||||||
|
|
||||||
- find all *.zip files and unzip them:
|
- find all *.zip files and unzip them:
|
||||||
@@ -456,6 +469,10 @@ Examples:
|
|||||||
- Convert all *.jpg files to *.png files:
|
- Convert all *.jpg files to *.png files:
|
||||||
|
|
||||||
fd -e jpg -x convert {} {.}.png
|
fd -e jpg -x convert {} {.}.png
|
||||||
|
|
||||||
|
- Run stat for each *.txt file, separated by null characters
|
||||||
|
|
||||||
|
fd -0 -e txt -x stat
|
||||||
.RE
|
.RE
|
||||||
.TP
|
.TP
|
||||||
.BI "\-X, \-\-exec-batch " command
|
.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.
|
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
|
Like \-\-exec, this can be used multiple times, in which case each command will be run in
|
||||||
the order given.
|
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 anyhow::anyhow;
|
||||||
use clap::{
|
use clap::{
|
||||||
error::ErrorKind, value_parser, Arg, ArgAction, ArgGroup, ArgMatches, Command, Parser,
|
Arg, ArgAction, ArgGroup, ArgMatches, Command, Parser, ValueEnum, error::ErrorKind,
|
||||||
ValueEnum,
|
value_parser,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "completions")]
|
#[cfg(feature = "completions")]
|
||||||
use clap_complete::Shell;
|
use clap_complete::Shell;
|
||||||
@@ -528,6 +528,10 @@ pub struct Opts {
|
|||||||
)]
|
)]
|
||||||
pub hyperlink: HyperlinkWhen,
|
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
|
/// Set number of threads to use for searching & executing (default: number
|
||||||
/// of available CPU cores)
|
/// of available CPU cores)
|
||||||
#[arg(long, short = 'j', value_name = "num", hide_short_help = true, value_parser = str::parse::<NonZeroUsize>)]
|
#[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.
|
/// relative to this directory.
|
||||||
#[arg(
|
#[arg(
|
||||||
long,
|
long,
|
||||||
|
short = 'C',
|
||||||
value_name = "path",
|
value_name = "path",
|
||||||
hide_short_help = true,
|
hide_short_help = true,
|
||||||
help = "Change current working directory",
|
help = "Change current working directory",
|
||||||
|
|||||||
@@ -130,6 +130,9 @@ pub struct Config {
|
|||||||
|
|
||||||
/// Whether or not to use hyperlinks on paths
|
/// Whether or not to use hyperlinks on paths
|
||||||
pub hyperlink: bool,
|
pub hyperlink: bool,
|
||||||
|
|
||||||
|
/// Names that should stop traversal down their parent. (e.g. https://bford.info/cachedir/).
|
||||||
|
pub ignore_contain: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
|
|||||||
+18
-13
@@ -1,6 +1,5 @@
|
|||||||
use std::io;
|
use std::io;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::sync::Mutex;
|
|
||||||
|
|
||||||
use argmax::Command;
|
use argmax::Command;
|
||||||
|
|
||||||
@@ -11,15 +10,15 @@ struct Outputs {
|
|||||||
stdout: Vec<u8>,
|
stdout: Vec<u8>,
|
||||||
stderr: Vec<u8>,
|
stderr: Vec<u8>,
|
||||||
}
|
}
|
||||||
struct OutputBuffer<'a> {
|
pub struct OutputBuffer {
|
||||||
output_permission: &'a Mutex<()>,
|
null_separator: bool,
|
||||||
outputs: Vec<Outputs>,
|
outputs: Vec<Outputs>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> OutputBuffer<'a> {
|
impl OutputBuffer {
|
||||||
fn new(output_permission: &'a Mutex<()>) -> Self {
|
pub fn new(null_separator: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
output_permission,
|
null_separator,
|
||||||
outputs: Vec::new(),
|
outputs: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,17 +28,19 @@ impl<'a> OutputBuffer<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn write(self) {
|
fn write(self) {
|
||||||
// avoid taking the lock if there is nothing to do
|
// Avoid taking the lock if there is nothing to do.
|
||||||
if self.outputs.is_empty() {
|
// 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;
|
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 stdout = io::stdout();
|
||||||
let stderr = io::stderr();
|
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 stdout = stdout.lock();
|
||||||
let mut stderr = stderr.lock();
|
let mut stderr = stderr.lock();
|
||||||
|
|
||||||
@@ -47,16 +48,20 @@ impl<'a> OutputBuffer<'a> {
|
|||||||
let _ = stdout.write_all(&output.stdout);
|
let _ = stdout.write_all(&output.stdout);
|
||||||
let _ = stderr.write_all(&output.stderr);
|
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.
|
/// Executes a command.
|
||||||
pub fn execute_commands<I: Iterator<Item = io::Result<Command>>>(
|
pub fn execute_commands<I: Iterator<Item = io::Result<Command>>>(
|
||||||
cmds: I,
|
cmds: I,
|
||||||
out_perm: &Mutex<()>,
|
mut output_buffer: OutputBuffer,
|
||||||
enable_output_buffering: bool,
|
enable_output_buffering: bool,
|
||||||
) -> ExitCode {
|
) -> ExitCode {
|
||||||
let mut output_buffer = OutputBuffer::new(out_perm);
|
|
||||||
for result in cmds {
|
for result in cmds {
|
||||||
let mut cmd = match result {
|
let mut cmd = match result {
|
||||||
Ok(cmd) => cmd,
|
Ok(cmd) => cmd,
|
||||||
|
|||||||
+2
-5
@@ -1,8 +1,6 @@
|
|||||||
use std::sync::Mutex;
|
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::error::print_error;
|
use crate::error::print_error;
|
||||||
use crate::exit_codes::{merge_exitcodes, ExitCode};
|
use crate::exit_codes::{ExitCode, merge_exitcodes};
|
||||||
use crate::walk::WorkerResult;
|
use crate::walk::WorkerResult;
|
||||||
|
|
||||||
use super::CommandSet;
|
use super::CommandSet;
|
||||||
@@ -13,7 +11,6 @@ use super::CommandSet;
|
|||||||
pub fn job(
|
pub fn job(
|
||||||
results: impl IntoIterator<Item = WorkerResult>,
|
results: impl IntoIterator<Item = WorkerResult>,
|
||||||
cmd: &CommandSet,
|
cmd: &CommandSet,
|
||||||
out_perm: &Mutex<()>,
|
|
||||||
config: &Config,
|
config: &Config,
|
||||||
) -> ExitCode {
|
) -> ExitCode {
|
||||||
// Output should be buffered when only running a single thread
|
// Output should be buffered when only running a single thread
|
||||||
@@ -37,7 +34,7 @@ pub fn job(
|
|||||||
let code = cmd.execute(
|
let code = cmd.execute(
|
||||||
dir_entry.stripped_path(config),
|
dir_entry.stripped_path(config),
|
||||||
config.path_separator.as_deref(),
|
config.path_separator.as_deref(),
|
||||||
out_perm,
|
config.null_separator,
|
||||||
buffer_output,
|
buffer_output,
|
||||||
);
|
);
|
||||||
ret = merge_exitcodes([ret, code]);
|
ret = merge_exitcodes([ret, code]);
|
||||||
|
|||||||
+5
-5
@@ -6,12 +6,12 @@ use std::io;
|
|||||||
use std::iter;
|
use std::iter;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
use std::sync::Mutex;
|
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{Result, bail};
|
||||||
use argmax::Command;
|
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 crate::fmt::{FormatTemplate, Token};
|
||||||
|
|
||||||
use self::command::{execute_commands, handle_cmd_error};
|
use self::command::{execute_commands, handle_cmd_error};
|
||||||
@@ -80,14 +80,14 @@ impl CommandSet {
|
|||||||
&self,
|
&self,
|
||||||
input: &Path,
|
input: &Path,
|
||||||
path_separator: Option<&str>,
|
path_separator: Option<&str>,
|
||||||
out_perm: &Mutex<()>,
|
null_separator: bool,
|
||||||
buffer_output: bool,
|
buffer_output: bool,
|
||||||
) -> ExitCode {
|
) -> ExitCode {
|
||||||
let commands = self
|
let commands = self
|
||||||
.commands
|
.commands
|
||||||
.iter()
|
.iter()
|
||||||
.map(|c| c.generate(input, path_separator));
|
.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
|
pub fn execute_batch<I>(&self, paths: I, limit: usize, path_separator: Option<&str>) -> ExitCode
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use nix::sys::signal::{raise, signal, SigHandler, Signal};
|
use nix::sys::signal::{SigHandler, Signal, raise, signal};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum ExitCode {
|
pub enum ExitCode {
|
||||||
|
|||||||
+2
-2
@@ -100,13 +100,13 @@ pub fn is_pipe(_: fs::FileType) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(unix, target_os = "redox"))]
|
#[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;
|
use std::os::unix::ffi::OsStrExt;
|
||||||
Cow::Borrowed(input.as_bytes())
|
Cow::Borrowed(input.as_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[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();
|
let string = input.to_string_lossy();
|
||||||
|
|
||||||
match string {
|
match string {
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
use anyhow::{anyhow, Result};
|
use anyhow::{Result, anyhow};
|
||||||
use nix::unistd::{Group, User};
|
use nix::unistd::{Group, User};
|
||||||
use std::fs;
|
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};
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
@@ -114,16 +114,22 @@ mod tests {
|
|||||||
assert!(!TimeFilter::before("2min").unwrap().applies_to(&t1m_ago));
|
assert!(!TimeFilter::before("2min").unwrap().applies_to(&t1m_ago));
|
||||||
|
|
||||||
let t10s_before = "2010-10-10 10:10:00";
|
let t10s_before = "2010-10-10 10:10:00";
|
||||||
assert!(!TimeFilter::before(t10s_before)
|
assert!(
|
||||||
.unwrap()
|
!TimeFilter::before(t10s_before)
|
||||||
.applies_to(&ref_time));
|
.unwrap()
|
||||||
assert!(TimeFilter::before(t10s_before)
|
.applies_to(&ref_time)
|
||||||
.unwrap()
|
);
|
||||||
.applies_to(&t1m_ago));
|
assert!(
|
||||||
|
TimeFilter::before(t10s_before)
|
||||||
|
.unwrap()
|
||||||
|
.applies_to(&t1m_ago)
|
||||||
|
);
|
||||||
|
|
||||||
assert!(TimeFilter::after(t10s_before)
|
assert!(
|
||||||
.unwrap()
|
TimeFilter::after(t10s_before)
|
||||||
.applies_to(&ref_time));
|
.unwrap()
|
||||||
|
.applies_to(&ref_time)
|
||||||
|
);
|
||||||
assert!(!TimeFilter::after(t10s_before).unwrap().applies_to(&t1m_ago));
|
assert!(!TimeFilter::after(t10s_before).unwrap().applies_to(&t1m_ago));
|
||||||
|
|
||||||
let same_day = "2010-10-10";
|
let same_day = "2010-10-10";
|
||||||
@@ -142,16 +148,22 @@ mod tests {
|
|||||||
ref_time = test_time.timestamp();
|
ref_time = test_time.timestamp();
|
||||||
let t1m_ago = ref_time - Duration::from_secs(60);
|
let t1m_ago = ref_time - Duration::from_secs(60);
|
||||||
let t10s_before = "2010-10-10T10:10:00+00:00";
|
let t10s_before = "2010-10-10T10:10:00+00:00";
|
||||||
assert!(!TimeFilter::before(t10s_before)
|
assert!(
|
||||||
.unwrap()
|
!TimeFilter::before(t10s_before)
|
||||||
.applies_to(&ref_time));
|
.unwrap()
|
||||||
assert!(TimeFilter::before(t10s_before)
|
.applies_to(&ref_time)
|
||||||
.unwrap()
|
);
|
||||||
.applies_to(&t1m_ago));
|
assert!(
|
||||||
|
TimeFilter::before(t10s_before)
|
||||||
|
.unwrap()
|
||||||
|
.applies_to(&t1m_ago)
|
||||||
|
);
|
||||||
|
|
||||||
assert!(TimeFilter::after(t10s_before)
|
assert!(
|
||||||
.unwrap()
|
TimeFilter::after(t10s_before)
|
||||||
.applies_to(&ref_time));
|
.unwrap()
|
||||||
|
.applies_to(&ref_time)
|
||||||
|
);
|
||||||
assert!(!TimeFilter::after(t10s_before).unwrap().applies_to(&t1m_ago));
|
assert!(!TimeFilter::after(t10s_before).unwrap().applies_to(&t1m_ago));
|
||||||
|
|
||||||
let ref_timestamp = 1707723412u64; // Mon Feb 12 07:36:52 UTC 2024
|
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);
|
let t1s_later = ref_time + Duration::from_secs(1);
|
||||||
// Timestamp only supported via '@' prefix
|
// Timestamp only supported via '@' prefix
|
||||||
assert!(TimeFilter::before(&ref_timestamp.to_string()).is_none());
|
assert!(TimeFilter::before(&ref_timestamp.to_string()).is_none());
|
||||||
assert!(TimeFilter::before(&format!("@{ref_timestamp}"))
|
assert!(
|
||||||
.unwrap()
|
TimeFilter::before(&format!("@{ref_timestamp}"))
|
||||||
.applies_to(&t1m_ago));
|
.unwrap()
|
||||||
assert!(!TimeFilter::before(&format!("@{ref_timestamp}"))
|
.applies_to(&t1m_ago)
|
||||||
.unwrap()
|
);
|
||||||
.applies_to(&t1s_later));
|
assert!(
|
||||||
assert!(!TimeFilter::after(&format!("@{ref_timestamp}"))
|
!TimeFilter::before(&format!("@{ref_timestamp}"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.applies_to(&t1m_ago));
|
.applies_to(&t1s_later)
|
||||||
assert!(TimeFilter::after(&format!("@{ref_timestamp}"))
|
);
|
||||||
.unwrap()
|
assert!(
|
||||||
.applies_to(&t1s_later));
|
!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 => {
|
Placeholder => {
|
||||||
s.push(Self::replace_separator(path.as_ref(), path_separator))
|
s.push(Self::replace_separator(path.as_ref(), path_separator))
|
||||||
}
|
}
|
||||||
Text(ref string) => s.push(string),
|
Text(string) => s.push(string),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s
|
s
|
||||||
|
|||||||
+1
-1
@@ -58,7 +58,7 @@ fn host() -> &'static str {
|
|||||||
|
|
||||||
#[cfg(not(unix))]
|
#[cfg(not(unix))]
|
||||||
const fn host() -> &'static str {
|
const fn host() -> &'static str {
|
||||||
""
|
"/"
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
+7
-3
@@ -18,7 +18,7 @@ use std::io::IsTerminal;
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Context, Result};
|
use anyhow::{Context, Result, anyhow, bail};
|
||||||
use clap::{CommandFactory, Parser};
|
use clap::{CommandFactory, Parser};
|
||||||
use globset::GlobBuilder;
|
use globset::GlobBuilder;
|
||||||
use lscolors::LsColors;
|
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
|
// 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
|
// 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(
|
#[cfg(all(
|
||||||
not(windows),
|
not(windows),
|
||||||
not(target_os = "android"),
|
not(target_os = "android"),
|
||||||
not(target_os = "macos"),
|
not(target_os = "macos"),
|
||||||
not(target_os = "freebsd"),
|
not(target_os = "freebsd"),
|
||||||
not(target_os = "openbsd"),
|
not(target_os = "openbsd"),
|
||||||
|
not(target_os = "illumos"),
|
||||||
not(all(target_env = "musl", target_pointer_width = "32")),
|
not(all(target_env = "musl", target_pointer_width = "32")),
|
||||||
not(target_arch = "riscv64"),
|
not(target_arch = "riscv64"),
|
||||||
feature = "use-jemalloc"
|
feature = "use-jemalloc"
|
||||||
))]
|
))]
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
|
static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
|
||||||
|
|
||||||
// vivid --color-mode 8-bit generate molokai
|
// vivid --color-mode 8-bit generate molokai
|
||||||
const DEFAULT_LS_COLORS: &str = "
|
const DEFAULT_LS_COLORS: &str = "
|
||||||
@@ -323,6 +326,7 @@ fn construct_config(mut opts: Opts, pattern_regexps: &[String]) -> Result<Config
|
|||||||
actual_path_separator,
|
actual_path_separator,
|
||||||
max_results: opts.max_results(),
|
max_results: opts.max_results(),
|
||||||
strip_cwd_prefix: opts.strip_cwd_prefix(|| !(opts.null_separator || has_command)),
|
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 \
|
"{}\n\nNote: You can use the '--fixed-strings' option to search for a \
|
||||||
literal string instead of a regular expression. Alternatively, you can \
|
literal string instead of a regular expression. Alternatively, you can \
|
||||||
also use the '--glob' option to match on a glob pattern.",
|
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
|
// 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<()> {
|
pub fn print_entry<W: Write>(stdout: &mut W, entry: &DirEntry, config: &Config) -> io::Result<()> {
|
||||||
let mut has_hyperlink = false;
|
let mut has_hyperlink = false;
|
||||||
if config.hyperlink {
|
if config.hyperlink
|
||||||
if let Some(url) = PathUrl::new(entry.path()) {
|
&& let Some(url) = PathUrl::new(entry.path())
|
||||||
write!(stdout, "\x1B]8;;{url}\x1B\\")?;
|
{
|
||||||
has_hyperlink = true;
|
write!(stdout, "\x1B]8;;{url}\x1B\\")?;
|
||||||
}
|
has_hyperlink = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref format) = config.format {
|
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::ParserBuilder;
|
||||||
|
use regex_syntax::hir::Hir;
|
||||||
|
|
||||||
/// Determine if a regex pattern contains a literal uppercase character.
|
/// Determine if a regex pattern contains a literal uppercase character.
|
||||||
pub fn pattern_has_uppercase_char(pattern: &str) -> bool {
|
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::thread;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{Result, anyhow};
|
||||||
use crossbeam_channel::{bounded, Receiver, RecvTimeoutError, SendError, Sender};
|
use crossbeam_channel::{Receiver, RecvTimeoutError, SendError, Sender, bounded};
|
||||||
use etcetera::BaseStrategy;
|
use etcetera::BaseStrategy;
|
||||||
use ignore::overrides::{Override, OverrideBuilder};
|
use ignore::overrides::{Override, OverrideBuilder};
|
||||||
use ignore::{WalkBuilder, WalkParallel, WalkState};
|
use ignore::{WalkBuilder, WalkParallel, WalkState};
|
||||||
@@ -19,7 +19,7 @@ use crate::config::Config;
|
|||||||
use crate::dir_entry::DirEntry;
|
use crate::dir_entry::DirEntry;
|
||||||
use crate::error::print_error;
|
use crate::error::print_error;
|
||||||
use crate::exec;
|
use crate::exec;
|
||||||
use crate::exit_codes::{merge_exitcodes, ExitCode};
|
use crate::exit_codes::{ExitCode, merge_exitcodes};
|
||||||
use crate::filesystem;
|
use crate::filesystem;
|
||||||
use crate::output;
|
use crate::output;
|
||||||
|
|
||||||
@@ -218,10 +218,10 @@ impl<'a, W: Write> ReceiverBuffer<'a, W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.num_results += 1;
|
self.num_results += 1;
|
||||||
if let Some(max_results) = self.config.max_results {
|
if let Some(max_results) = self.config.max_results
|
||||||
if self.num_results >= max_results {
|
&& self.num_results >= max_results
|
||||||
return self.stop();
|
{
|
||||||
}
|
return self.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WorkerResult::Error(err) => {
|
WorkerResult::Error(err) => {
|
||||||
@@ -250,11 +250,11 @@ impl<'a, W: Write> ReceiverBuffer<'a, W> {
|
|||||||
|
|
||||||
/// Output a path.
|
/// Output a path.
|
||||||
fn print(&mut self, entry: &DirEntry) -> Result<(), ExitCode> {
|
fn print(&mut self, entry: &DirEntry) -> Result<(), ExitCode> {
|
||||||
if let Err(e) = output::print_entry(&mut self.stdout, entry, self.config) {
|
if let Err(e) = output::print_entry(&mut self.stdout, entry, self.config)
|
||||||
if e.kind() != ::std::io::ErrorKind::BrokenPipe {
|
&& e.kind() != ::std::io::ErrorKind::BrokenPipe
|
||||||
print_error(format!("Could not write to output: {e}"));
|
{
|
||||||
return Err(ExitCode::GeneralError);
|
print_error(format!("Could not write to output: {e}"));
|
||||||
}
|
return Err(ExitCode::GeneralError);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.interrupt_flag.load(Ordering::Relaxed) {
|
if self.interrupt_flag.load(Ordering::Relaxed) {
|
||||||
@@ -368,18 +368,18 @@ impl WorkerState {
|
|||||||
builder.add_custom_ignore_filename(".fdignore");
|
builder.add_custom_ignore_filename(".fdignore");
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.read_global_ignore {
|
if config.read_global_ignore
|
||||||
if let Ok(basedirs) = etcetera::choose_base_strategy() {
|
&& 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 global_ignore_file = basedirs.config_dir().join("fd").join("ignore");
|
||||||
let result = builder.add_ignore(global_ignore_file);
|
if global_ignore_file.is_file() {
|
||||||
match result {
|
let result = builder.add_ignore(global_ignore_file);
|
||||||
Some(ignore::Error::Partial(_)) => (),
|
match result {
|
||||||
Some(err) => {
|
Some(ignore::Error::Partial(_)) => (),
|
||||||
print_error(format!("Malformed pattern in global ignore file. {err}."));
|
Some(err) => {
|
||||||
}
|
print_error(format!("Malformed pattern in global ignore file. {err}."));
|
||||||
None => (),
|
|
||||||
}
|
}
|
||||||
|
None => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -413,8 +413,6 @@ impl WorkerState {
|
|||||||
if cmd.in_batch_mode() {
|
if cmd.in_batch_mode() {
|
||||||
exec::batch(rx.into_iter().flatten(), cmd, config)
|
exec::batch(rx.into_iter().flatten(), cmd, config)
|
||||||
} else {
|
} else {
|
||||||
let out_perm = Mutex::new(());
|
|
||||||
|
|
||||||
thread::scope(|scope| {
|
thread::scope(|scope| {
|
||||||
// Each spawned job will store its thread handle in here.
|
// Each spawned job will store its thread handle in here.
|
||||||
let threads = config.threads;
|
let threads = config.threads;
|
||||||
@@ -423,8 +421,8 @@ impl WorkerState {
|
|||||||
let rx = rx.clone();
|
let rx = rx.clone();
|
||||||
|
|
||||||
// Spawn a job thread that will listen for and execute inputs.
|
// Spawn a job thread that will listen for and execute inputs.
|
||||||
let handle = scope
|
let handle =
|
||||||
.spawn(|| exec::job(rx.into_iter().flatten(), cmd, &out_perm, config));
|
scope.spawn(|| exec::job(rx.into_iter().flatten(), cmd, config));
|
||||||
|
|
||||||
// Push the handle of the spawned thread into the vector for later joining.
|
// Push the handle of the spawned thread into the vector for later joining.
|
||||||
handles.push(handle);
|
handles.push(handle);
|
||||||
@@ -449,11 +447,12 @@ impl WorkerState {
|
|||||||
let quit_flag = self.quit_flag.as_ref();
|
let quit_flag = self.quit_flag.as_ref();
|
||||||
|
|
||||||
let mut limit = 0x100;
|
let mut limit = 0x100;
|
||||||
if let Some(cmd) = &config.command {
|
if let Some(cmd) = &config.command
|
||||||
if !cmd.in_batch_mode() && config.threads > 1 {
|
&& !cmd.in_batch_mode()
|
||||||
// Evenly distribute work between multiple receivers
|
&& config.threads > 1
|
||||||
limit = 1;
|
{
|
||||||
}
|
// Evenly distribute work between multiple receivers
|
||||||
|
limit = 1;
|
||||||
}
|
}
|
||||||
let mut tx = BatchSender::new(tx.clone(), limit);
|
let mut tx = BatchSender::new(tx.clone(), limit);
|
||||||
|
|
||||||
@@ -462,11 +461,28 @@ impl WorkerState {
|
|||||||
return WalkState::Quit;
|
return WalkState::Quit;
|
||||||
}
|
}
|
||||||
|
|
||||||
let entry = match entry {
|
if let Ok(e) = &entry {
|
||||||
Ok(ref e) if e.depth() == 0 => {
|
// 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.
|
// Skip the root directory entry.
|
||||||
return WalkState::Continue;
|
return WalkState::Continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
let entry = match entry {
|
||||||
Ok(e) => DirEntry::normal(e),
|
Ok(e) => DirEntry::normal(e),
|
||||||
Err(ignore::Error::WithPath {
|
Err(ignore::Error::WithPath {
|
||||||
path,
|
path,
|
||||||
@@ -488,21 +504,21 @@ impl WorkerState {
|
|||||||
})) {
|
})) {
|
||||||
Ok(_) => WalkState::Continue,
|
Ok(_) => WalkState::Continue,
|
||||||
Err(_) => WalkState::Quit,
|
Err(_) => WalkState::Quit,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
return match tx.send(WorkerResult::Error(err)) {
|
return match tx.send(WorkerResult::Error(err)) {
|
||||||
Ok(_) => WalkState::Continue,
|
Ok(_) => WalkState::Continue,
|
||||||
Err(_) => WalkState::Quit,
|
Err(_) => WalkState::Quit,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(min_depth) = config.min_depth {
|
if let Some(min_depth) = config.min_depth
|
||||||
if entry.depth().map_or(true, |d| d < min_depth) {
|
&& entry.depth().is_none_or(|d| d < min_depth)
|
||||||
return WalkState::Continue;
|
{
|
||||||
}
|
return WalkState::Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the name first, since it doesn't require metadata
|
// Check the name first, since it doesn't require metadata
|
||||||
@@ -542,10 +558,10 @@ impl WorkerState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Filter out unwanted file types.
|
// Filter out unwanted file types.
|
||||||
if let Some(ref file_types) = config.file_types {
|
if let Some(ref file_types) = config.file_types
|
||||||
if file_types.should_ignore(&entry) {
|
&& file_types.should_ignore(&entry)
|
||||||
return WalkState::Continue;
|
{
|
||||||
}
|
return WalkState::Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
@@ -584,24 +600,24 @@ impl WorkerState {
|
|||||||
// Filter out unwanted modification times
|
// Filter out unwanted modification times
|
||||||
if !config.time_constraints.is_empty() {
|
if !config.time_constraints.is_empty() {
|
||||||
let mut matched = false;
|
let mut matched = false;
|
||||||
if let Some(metadata) = entry.metadata() {
|
if let Some(metadata) = entry.metadata()
|
||||||
if let Ok(modified) = metadata.modified() {
|
&& let Ok(modified) = metadata.modified()
|
||||||
matched = config
|
{
|
||||||
.time_constraints
|
matched = config
|
||||||
.iter()
|
.time_constraints
|
||||||
.all(|tf| tf.applies_to(&modified));
|
.iter()
|
||||||
}
|
.all(|tf| tf.applies_to(&modified));
|
||||||
}
|
}
|
||||||
if !matched {
|
if !matched {
|
||||||
return WalkState::Continue;
|
return WalkState::Continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.is_printing() {
|
if config.is_printing()
|
||||||
if let Some(ls_colors) = &config.ls_colors {
|
&& let Some(ls_colors) = &config.ls_colors
|
||||||
// Compute colors in parallel
|
{
|
||||||
entry.style(ls_colors);
|
// Compute colors in parallel
|
||||||
}
|
entry.style(ls_colors);
|
||||||
}
|
}
|
||||||
|
|
||||||
let send_result = tx.send(WorkerResult::Entry(entry));
|
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.
|
/// Find the *fd* executable.
|
||||||
fn find_fd_exe() -> PathBuf {
|
fn find_fd_exe() -> PathBuf {
|
||||||
// Tests exe is in target/debug/deps, the *fd* exe is in target/debug
|
// Read the location of the fd executable from the environment
|
||||||
let root = env::current_exe()
|
PathBuf::from(env::var("CARGO_BIN_EXE_fd").unwrap_or(env!("CARGO_BIN_EXE_fd").to_string()))
|
||||||
.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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Format an error message for when *fd* did not exit successfully.
|
/// 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]
|
#[test]
|
||||||
fn test_exec_multi() {
|
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);
|
let (te, abs_path) = get_test_env_with_abs_path(DEFAULT_DIRS, DEFAULT_FILES);
|
||||||
|
|
||||||
te.assert_output(
|
te.assert_output(
|
||||||
@@ -1813,9 +1811,11 @@ fn test_exec_multi() {
|
|||||||
e1 e2",
|
e1 e2",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// We use printf here because we need to suppress a newline and
|
||||||
|
// echo -n is not POSIX-compliant.
|
||||||
te.assert_output(
|
te.assert_output(
|
||||||
&[
|
&[
|
||||||
"foo", "--exec", "echo", "-n", "{/}: ", ";", "--exec", "echo", "{//}",
|
"foo", "--exec", "printf", "%s", "{/}: ", ";", "--exec", "printf", "%s\\n", "{//}",
|
||||||
],
|
],
|
||||||
"a.foo: .
|
"a.foo: .
|
||||||
b.foo: ./one
|
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]
|
#[test]
|
||||||
fn test_exec_batch() {
|
fn test_exec_batch() {
|
||||||
let (te, abs_path) = get_test_env_with_abs_path(DEFAULT_DIRS, DEFAULT_FILES);
|
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("--max-results=1");
|
||||||
assert_just_one_result_with_option("-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", "--max-results=0", "--exec=cat"]);
|
||||||
te.assert_failure(&["thing", "-1", "--exec=cat"]);
|
te.assert_failure(&["thing", "-1", "--exec=cat"]);
|
||||||
te.assert_failure(&["thing", "--max-results=1", "-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
|
/// When fd is ran from a non-existent working directory, but an existent
|
||||||
/// directory is passed in the arguments, it should still run fine
|
/// directory is passed in the arguments, it should still run fine
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(windows))]
|
#[cfg(all(not(windows), not(target_os = "illumos")))]
|
||||||
fn test_invalid_cwd() {
|
fn test_invalid_cwd() {
|
||||||
let te = TestEnv::new(&[], &[]);
|
let te = TestEnv::new(&[], &[]);
|
||||||
|
|
||||||
@@ -2682,7 +2697,7 @@ fn test_hyperlink() {
|
|||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let hostname = nix::unistd::gethostname().unwrap().into_string().unwrap();
|
let hostname = nix::unistd::gethostname().unwrap().into_string().unwrap();
|
||||||
#[cfg(not(unix))]
|
#[cfg(not(unix))]
|
||||||
let hostname = "";
|
let hostname = "/";
|
||||||
|
|
||||||
let expected = format!(
|
let expected = format!(
|
||||||
"\x1b]8;;file://{}{}/a.foo\x1b\\a.foo\x1b]8;;\x1b\\",
|
"\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);
|
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