Compare commits

...

112 Commits

Author SHA1 Message Date
David Peter 29936f0fba
Update sponsors.md 2024-05-16 16:56:22 +02:00
David Peter bfc16a1dee
Remove sponsorship message 2024-05-16 16:55:35 +02:00
Thayne McCombs 289a68bac3
Merge pull request #1555 from tmccombs/fmt-readme
Add --format to help in README
2024-05-08 00:40:02 -06:00
Thayne McCombs fcaff0f385 Add --format to help in README 2024-05-08 00:38:45 -06:00
Thayne McCombs 36163f9c3a
Merge pull request #1554 from tmccombs/release-10.1.0
Prepare for 10.1.0 release
2024-05-08 00:36:34 -06:00
Thayne McCombs d90ec1758e Prepare for 10.1.0 release 2024-05-08 00:20:43 -06:00
Thayne McCombs ea22cbd712
Merge pull request #1043 from tmccombs/format-option
Implement option for printing custom formats
2024-05-07 23:10:12 -06:00
Thayne McCombs d44badc190 Implement option for printing custom formats 2024-05-07 22:52:49 -06:00
Thayne McCombs 6becb66185
Merge pull request #1445 from tmccombs/cwd-prefix
feat: Add option to always include cwd prefix
2024-05-07 21:27:45 -06:00
Thayne McCombs 1a1f057e5d
Merge pull request #1552 from tmccombs/mac-m1-build
Attempt to add aarch64 osx build to CI
2024-05-07 10:41:09 -06:00
Thayne McCombs 10a269bd3f
Merge pull request #1553 from tmccombs/changelog-update
Add upcoming to changelog
2024-05-07 00:24:37 -06:00
Thayne McCombs 90d3381814 docs: Make auto option for --strip-cwd-prefix more clear 2024-05-07 00:24:15 -06:00
Thayne McCombs b1f83a0bb0 feat: Add option to always include cwd prefix
Fixes: #1243
Fixes: #1331
2024-05-07 00:24:06 -06:00
Thayne McCombs 3bc70925a9 Add upcoming to changelog 2024-05-07 00:16:56 -06:00
Thayne McCombs f287f08b9f Attempt to add aarch64 osx build to CI
Fixes: #1088
2024-05-07 00:14:52 -06:00
Tavian Barnes 0e4488e9dc
Merge pull request #1549 from tavianator/jemalloc-aarch64
Fix #1085 for real
2024-05-06 12:39:49 -04:00
Tavian Barnes d7d63eddbe Fix #1085 for real
cross doesn't pass all environment variables through into the build
container, so JEMALLOC_SYS_WITH_LG_PAGE=16 wasn't being picked up from
the host.  Instead, set it explicitly in Cross.toml.

Link: https://github.com/cross-rs/cross/wiki/Configuration
2024-05-06 12:29:11 -04:00
Thayne McCombs 8acd7722f0
Merge pull request #1548 from tmccombs/release-9.1.0
Release 10.0.0
2024-05-06 00:01:31 -06:00
Thayne McCombs 92fab6e058 Prepare for 10.0.0 release 2024-05-05 23:52:50 -06:00
Thayne McCombs a0ee0856db Add @timestamp to --help 2024-05-05 23:52:50 -06:00
Thayne McCombs b8df500a70 Add script to automate some of release process. 2024-05-05 23:52:50 -06:00
Thayne McCombs cd96ca071d
Merge pull request #1547 from tavianator/jemalloc-aarch64
ci: Support large page sizes on AArch64
2024-05-03 21:46:40 -06:00
Tavian Barnes 216472ff9f ci: Support large page sizes on AArch64
Fixes #1085.
2024-05-03 15:23:41 -04:00
Thayne McCombs 3680d10e5c
Merge pull request #1544 from sharkdp/dependabot/cargo/libc-0.2.154
build(deps): bump libc from 0.2.153 to 0.2.154
2024-05-01 12:03:31 -06:00
dependabot[bot] abe3b9cd78
build(deps): bump libc from 0.2.153 to 0.2.154
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.153 to 0.2.154.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.153...0.2.154)

---
updated-dependencies:
- dependency-name: libc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-01 17:47:51 +00:00
Thayne McCombs 7aad6c9edf
Merge pull request #1543 from sharkdp/dependabot/cargo/chrono-0.4.38
build(deps): bump chrono from 0.4.37 to 0.4.38
2024-05-01 11:47:39 -06:00
Thayne McCombs ddd3aae249
Merge pull request #1542 from sharkdp/dependabot/cargo/nix-0.28.0
build(deps): bump nix from 0.27.1 to 0.28.0
2024-05-01 11:46:53 -06:00
dependabot[bot] 6d3bb68faf
build(deps): bump chrono from 0.4.37 to 0.4.38
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.37 to 0.4.38.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.37...v0.4.38)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-01 17:31:34 +00:00
dependabot[bot] 21d50dae8c
build(deps): bump nix from 0.27.1 to 0.28.0
Bumps [nix](https://github.com/nix-rust/nix) from 0.27.1 to 0.28.0.
- [Changelog](https://github.com/nix-rust/nix/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nix-rust/nix/compare/v0.27.1...v0.28.0)

---
updated-dependencies:
- dependency-name: nix
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-01 17:31:29 +00:00
David Peter 9279b1f0af Fix typo in CHANGELOG 2024-04-30 15:56:12 +02:00
Thayne McCombs 6647085015 Add test for gitignore workaround
And make sure it works for user-supplied path of "."

Also add changelog entry
2024-04-30 15:56:12 +02:00
Thayne McCombs 6af8f092ee Workaround for bug with gitinored files
This is a workaround for
https://github.com/BurntSushi/ripgrep/issues/2711

Fixes: #1461
2024-04-30 15:56:12 +02:00
Thayne McCombs c4094c7a05
Merge pull request #1492 from AlbydST/master
Add installation instructions for more Linux distributions in README
2024-04-29 13:13:38 -06:00
AlbydS 6d58df5f0c
Remove unnecessary sudo in front of commands
Removed unnecessary "sudo" in front of all commands, also removed the "replace sudo with doas..." instruction at the end.
2024-04-29 18:04:39 +00:00
AlbydS ffecccf209
Merge branch 'sharkdp:master' into master 2024-04-29 17:58:24 +00:00
Thayne McCombs 31f2839751
Merge pull request #1540 from tmccombs/revert-git-ignore
Stop ignore .git folders by default
2024-04-28 20:33:03 -06:00
Thayne McCombs e10a4eab2b Stop ignore .git folders by default
Fixes: #1457
See: #1396, #1403
2024-04-28 20:23:25 -06:00
Thayne McCombs 8eb047945e
Merge pull request #1541 from tavianator/clippy-is-empty
filesystem: Remove a redundant as_str()
2024-04-28 20:22:10 -06:00
Tavian Barnes 1031325cca filesystem: Remove a redundant as_str() 2024-04-28 17:30:44 -04:00
Tavian Barnes 9fc2167cf9
Merge pull request #1505 from leuven65/master
set default path separator to '/' in UCRT64 of MSYS
2024-04-28 17:23:43 -04:00
Tavian Barnes ae1de4de24
Merge pull request #1539 from tmccombs/fix-clippy
Fix deprecation warning.
2024-04-28 17:21:41 -04:00
Thayne McCombs 7e5d14b733 Fix deprecation warning. 2024-04-27 21:58:54 -06:00
Thayne McCombs 85cbea8dcb
Merge pull request #1494 from nabellows/master
Add support for @%s time format
2024-04-26 22:37:54 -06:00
Thayne McCombs bc6782624e
Merge pull request #1520 from tmccombs/clippy
Add clippy using latest rust version to CI
2024-04-15 00:17:20 -06:00
Thayne McCombs cf6ff87c7d Add clippy using latest rust version to CI
Fixes: #1511
2024-04-14 23:33:40 -06:00
Thayne McCombs 3cd73d7927
Merge pull request #1536 from bin-ly/master
Use 'MAIN_SEPARATOR_STR' instead of '&MAIN_SEPARATOR.to_string()'
2024-04-14 20:58:12 -06:00
binlingyu 7794c4aae5 Use 'MAIN_SEPARATOR_STR' instead of '&MAIN_SEPARATOR.to_string()' 2024-04-15 10:36:10 +08:00
Thayne McCombs 8c7a84ea30
Merge pull request #1534 from tmccombs/msrv-update
Update MSRV to 1.77.2
2024-04-11 23:06:41 -06:00
Thayne McCombs e262ade74e Update MSRV to 1.77.2
Prior to this version, `Command` didn't properly escape command line
arguments on windows.

Although, the risk of attacker controlled arguments passed through fd is
relatively small, I think it is best to upgrade to a version that fixes
this.

The biggest risk for users is probably running fd with `--exec` or
`--exec-batch` on directories that contain files with names controlled
by another party (for example a git repo that you cloned).

See: https://blog.rust-lang.org/2024/04/09/cve-2024-24576.html
Security: CVE-2024-24576
2024-04-11 21:14:11 -06:00
Thayne McCombs 11069e284a
Merge pull request #1530 from sharkdp/dependabot/cargo/lscolors-0.17.0
build(deps): bump lscolors from 0.16.0 to 0.17.0
2024-04-01 15:22:35 -06:00
Thayne McCombs 6e2e86decb
Merge pull request #1529 from sharkdp/dependabot/cargo/aho-corasick-1.1.3
build(deps): bump aho-corasick from 1.1.2 to 1.1.3
2024-04-01 15:22:15 -06:00
Thayne McCombs 15d3b63ccc
Merge pull request #1531 from sharkdp/dependabot/cargo/crossbeam-channel-0.5.12
build(deps): bump crossbeam-channel from 0.5.11 to 0.5.12
2024-04-01 15:21:48 -06:00
dependabot[bot] 453577651e
build(deps): bump crossbeam-channel from 0.5.11 to 0.5.12
Bumps [crossbeam-channel](https://github.com/crossbeam-rs/crossbeam) from 0.5.11 to 0.5.12.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-channel-0.5.11...crossbeam-channel-0.5.12)

---
updated-dependencies:
- dependency-name: crossbeam-channel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-01 17:31:12 +00:00
dependabot[bot] 39c07b7b4c
build(deps): bump lscolors from 0.16.0 to 0.17.0
Bumps [lscolors](https://github.com/sharkdp/lscolors) from 0.16.0 to 0.17.0.
- [Release notes](https://github.com/sharkdp/lscolors/releases)
- [Commits](https://github.com/sharkdp/lscolors/compare/0.16.0...v0.17.0)

---
updated-dependencies:
- dependency-name: lscolors
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-01 17:31:06 +00:00
dependabot[bot] 5910285db0
build(deps): bump aho-corasick from 1.1.2 to 1.1.3
Bumps [aho-corasick](https://github.com/BurntSushi/aho-corasick) from 1.1.2 to 1.1.3.
- [Commits](https://github.com/BurntSushi/aho-corasick/compare/1.1.2...1.1.3)

---
updated-dependencies:
- dependency-name: aho-corasick
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-01 17:31:00 +00:00
Thayne McCombs 68fe31da3f
Merge pull request #1517 from one230six/master
refactor: Optimize code based on cargo clippy suggestions
2024-03-13 22:27:48 -06:00
Jian Wang f875ea9a52 Set default path separator to '/' in MSYS/MSYS2
The environments of MSYS: [[https://www.msys2.org/docs/environments/]]
2024-03-13 19:32:13 +01:00
one230six 138919907b refactor: Optimize code based on cargo clippy suggestions
Signed-off-by: one230six <723682061@qq.com>
2024-03-13 15:37:24 +08:00
Thayne McCombs b8744626e7
Merge pull request #1515 from sharkdp/dependabot/github_actions/softprops/action-gh-release-2
build(deps): bump softprops/action-gh-release from 1 to 2
2024-03-11 11:55:43 -06:00
dependabot[bot] b08d78f6fc
build(deps): bump softprops/action-gh-release from 1 to 2
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 1 to 2.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/v1...v2)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-11 17:45:08 +00:00
Tavian Barnes 4efc05ef27
Merge pull request #1510 from garlic-hub/clippy
Clean up clippy warnings
2024-03-08 16:23:38 -05:00
garlic-hub 0788c43c3f
Clean up clippy warnings 2024-03-08 10:46:24 -08:00
Thayne McCombs 3b2fd158b5
Merge pull request #1502 from sharkdp/dependabot/cargo/tempfile-3.10.1
build(deps): bump tempfile from 3.9.0 to 3.10.1
2024-03-01 11:16:34 -07:00
Thayne McCombs c38dbacbd0
Merge pull request #1500 from sharkdp/dependabot/cargo/libc-0.2.153
build(deps): bump libc from 0.2.151 to 0.2.153
2024-03-01 11:15:55 -07:00
Thayne McCombs 728b3200c0
Merge pull request #1503 from sharkdp/dependabot/cargo/chrono-0.4.34
build(deps): bump chrono from 0.4.31 to 0.4.34
2024-03-01 11:15:23 -07:00
dependabot[bot] 7f74cd9e56
build(deps): bump chrono from 0.4.31 to 0.4.34
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.31 to 0.4.34.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.31...v0.4.34)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-01 17:42:05 +00:00
dependabot[bot] 6ae8da6a39
build(deps): bump tempfile from 3.9.0 to 3.10.1
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.9.0 to 3.10.1.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.9.0...v3.10.1)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-01 17:41:57 +00:00
dependabot[bot] f699c8bb6a
build(deps): bump libc from 0.2.151 to 0.2.153
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.151 to 0.2.153.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.151...0.2.153)

---
updated-dependencies:
- dependency-name: libc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-01 17:41:45 +00:00
Nathan Bellows ffde94c10e Run formatting 2024-02-12 02:12:58 -08:00
Nathan Bellows b0a8848f68 Add support for @%s time format 2024-02-12 02:05:41 -08:00
AlbydS d651a595d4
Add installation instructions for more Linux distributions in README
Added installation instructions for Arch AUR, ALT Linux and Solus and did a few minor changes
2024-02-11 21:25:33 +01:00
David Peter 969316cc0e
More whitespace 2024-02-07 09:32:16 +01:00
David Peter 5b46867507 Add Terminal Trove sponsorship 2024-02-07 09:30:20 +01:00
Thayne McCombs e117a373a7
Merge pull request #1488 from sharkdp/dependabot/cargo/nu-ansi-term-0.50.0
build(deps): bump nu-ansi-term from 0.49.0 to 0.50.0
2024-02-01 15:18:00 -07:00
dependabot[bot] a4aed14337
build(deps): bump nu-ansi-term from 0.49.0 to 0.50.0
Bumps [nu-ansi-term](https://github.com/nushell/nu-ansi-term) from 0.49.0 to 0.50.0.
- [Release notes](https://github.com/nushell/nu-ansi-term/releases)
- [Changelog](https://github.com/nushell/nu-ansi-term/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nushell/nu-ansi-term/compare/v0.49.0...v0.50.0)

---
updated-dependencies:
- dependency-name: nu-ansi-term
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-01 20:50:41 +00:00
Thayne McCombs 9cde3c12a2
Merge pull request #1490 from sharkdp/dependabot/cargo/clap_complete-4.4.9
build(deps): bump clap_complete from 4.4.6 to 4.4.9
2024-02-01 13:50:25 -07:00
Thayne McCombs 906e7a933e
Merge pull request #1489 from sharkdp/dependabot/cargo/ignore-0.4.22
build(deps): bump ignore from 0.4.21 to 0.4.22
2024-02-01 13:50:04 -07:00
dependabot[bot] 077d28d13a
build(deps): bump ignore from 0.4.21 to 0.4.22
Bumps [ignore](https://github.com/BurntSushi/ripgrep) from 0.4.21 to 0.4.22.
- [Release notes](https://github.com/BurntSushi/ripgrep/releases)
- [Changelog](https://github.com/BurntSushi/ripgrep/blob/master/CHANGELOG.md)
- [Commits](https://github.com/BurntSushi/ripgrep/compare/ignore-0.4.21...ignore-0.4.22)

---
updated-dependencies:
- dependency-name: ignore
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-01 18:34:09 +00:00
dependabot[bot] b55bb1e9be
build(deps): bump clap_complete from 4.4.6 to 4.4.9
Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.4.6 to 4.4.9.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.4.6...clap_complete-v4.4.9)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-01 18:33:20 +00:00
Thayne McCombs 7a6cc92d6d
Merge pull request #1486 from sharkdp/dependabot/cargo/regex-1.10.3
build(deps): bump regex from 1.10.2 to 1.10.3
2024-02-01 11:33:08 -07:00
Thayne McCombs b694c6e673
Merge pull request #1487 from sharkdp/dependabot/cargo/crossbeam-channel-0.5.11
build(deps): bump crossbeam-channel from 0.5.10 to 0.5.11
2024-02-01 11:32:41 -07:00
dependabot[bot] 17895538a0
build(deps): bump crossbeam-channel from 0.5.10 to 0.5.11
Bumps [crossbeam-channel](https://github.com/crossbeam-rs/crossbeam) from 0.5.10 to 0.5.11.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-channel-0.5.10...crossbeam-channel-0.5.11)

---
updated-dependencies:
- dependency-name: crossbeam-channel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-01 17:54:58 +00:00
dependabot[bot] 72ff1f9a87
build(deps): bump regex from 1.10.2 to 1.10.3
Bumps [regex](https://github.com/rust-lang/regex) from 1.10.2 to 1.10.3.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.10.2...1.10.3)

---
updated-dependencies:
- dependency-name: regex
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-01 17:54:48 +00:00
Thayne McCombs ef3194a510
Merge pull request #1483 from ognevny/patch-1
Makefile: use `--locked` for build
2024-01-26 00:25:04 -07:00
Maksim Bondarenkov 8773402246
Makefile: use `--locked` for build
to be ensured that Cargo.lock is up to date. also it's needed for disributions
2024-01-20 17:44:38 +03:00
Rob ff3fc81db4 Add target aarch64-unknown-linux-musl 2024-01-16 20:32:41 +01:00
Tavian Barnes 0dc3342c33
Merge pull request #1472 from tmccombs/clap-update
Update clap version
2024-01-08 10:36:43 -05:00
Thayne McCombs c66fc812ac Update clap version
Fixes: #1407
2024-01-08 02:03:39 -07:00
Thayne McCombs 14ed023875
Merge pull request #1464 from Ato2207/dir_alias
Added alias --type dir
2024-01-07 12:03:24 -07:00
Tavian Barnes 58284b8dbe
Merge pull request #1470 from sharkdp/dependabot/cargo/crossbeam-channel-0.5.10
build(deps): bump crossbeam-channel from 0.5.8 to 0.5.10
2024-01-01 23:38:31 -05:00
Tavian Barnes 60889d0b99
Merge pull request #1468 from sharkdp/dependabot/cargo/tempfile-3.9.0
build(deps): bump tempfile from 3.8.1 to 3.9.0
2024-01-01 23:36:01 -05:00
dependabot[bot] 7e19bad0a4
build(deps): bump tempfile from 3.8.1 to 3.9.0
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.8.1 to 3.9.0.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.8.1...v3.9.0)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-01 18:07:49 +00:00
Thayne McCombs 4b1d73d39d
Merge pull request #1469 from sharkdp/dependabot/cargo/filetime-0.2.23
build(deps): bump filetime from 0.2.22 to 0.2.23
2024-01-01 11:07:14 -07:00
dependabot[bot] 03e19a1ad2
build(deps): bump crossbeam-channel from 0.5.8 to 0.5.10
Bumps [crossbeam-channel](https://github.com/crossbeam-rs/crossbeam) from 0.5.8 to 0.5.10.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-channel-0.5.8...crossbeam-channel-0.5.10)

---
updated-dependencies:
- dependency-name: crossbeam-channel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-01 18:04:12 +00:00
Thayne McCombs 8fb9499c20
Merge pull request #1466 from sharkdp/dependabot/cargo/libc-0.2.151
build(deps): bump libc from 0.2.150 to 0.2.151
2024-01-01 11:03:48 -07:00
Thayne McCombs 38fb6a5958
Merge pull request #1467 from sharkdp/dependabot/cargo/clap_complete-4.4.5
build(deps): bump clap_complete from 4.4.4 to 4.4.5
2024-01-01 11:03:17 -07:00
dependabot[bot] 49cd62d65e
build(deps): bump filetime from 0.2.22 to 0.2.23
Bumps [filetime](https://github.com/alexcrichton/filetime) from 0.2.22 to 0.2.23.
- [Commits](https://github.com/alexcrichton/filetime/compare/0.2.22...0.2.23)

---
updated-dependencies:
- dependency-name: filetime
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-01 17:33:54 +00:00
dependabot[bot] 24bb5216bb
build(deps): bump clap_complete from 4.4.4 to 4.4.5
Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.4.4 to 4.4.5.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.4.4...clap_complete-v4.4.5)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-01 17:33:45 +00:00
dependabot[bot] 7f8760fd1f
build(deps): bump libc from 0.2.150 to 0.2.151
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.150 to 0.2.151.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.150...0.2.151)

---
updated-dependencies:
- dependency-name: libc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-01 17:33:39 +00:00
Alexandru-Constantin Atomei 3cb6b9d93a Updated CHANGELOG.md, man page and made alias visible in fd -h too 2024-01-01 17:14:32 +02:00
Atomei Alexandru c591106b86
Merge branch 'master' into dir_alias 2023-12-30 22:58:10 +02:00
Alexandru-Constantin Atomei 9f096737db Added explicit documentation about the possibility of using dir 2023-12-30 22:54:57 +02:00
Alexandru-Constantin Atomei 1bda165b25 Added alias --type dir 2023-12-30 22:14:53 +02:00
Thayne McCombs f48372624d
Merge pull request #1463 from Roshanjossey/patch-1
Format notes in Readme
2023-12-27 22:25:00 -07:00
Roshan Jossy 5cd15536b6
Format notes in Readme 2023-12-28 00:50:02 +01:00
Sayan Goswami aeb4a5fdad Fixes typo in README
Fixes a tiny typo: ~defautl~ to default.
2023-12-27 14:32:00 -05:00
Thayne McCombs 9529f30129 fix: Respect NO_COLOR with --list-details option
Fixes: #1455
2023-12-23 21:32:33 +01:00
Thayne McCombs 266311ca33
Merge pull request #1454 from tavianator/thats-a-lot-of-issues
CHANGELOG: Fix issue number typo
2023-12-19 13:37:47 -07:00
Tavian Barnes 954a3900b9 CHANGELOG: Fix issue number typo 2023-12-19 14:04:01 -05:00
David Peter 07343b5baf Update troubleshooting section 2023-12-19 11:45:09 +01:00
David Peter a03ed8b300 Update license information 2023-12-19 11:44:20 +01:00
David Peter 13a93e5cbe Add new unreleased section 2023-12-19 11:42:27 +01:00
27 changed files with 1002 additions and 604 deletions

View File

@ -44,6 +44,16 @@ jobs:
- uses: actions/checkout@v4
- run: cargo fmt -- --check
lint_check:
name: Ensure 'cargo clippy' has no warnings
runs-on: ubuntu-latest
steps:
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: actions/checkout@v4
- run: cargo clippy --all-targets --all-features -- -Dwarnings
min_version:
name: Minimum supported rust version
runs-on: ubuntu-20.04
@ -71,12 +81,14 @@ jobs:
matrix:
job:
- { target: aarch64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
- { target: aarch64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
- { target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true }
- { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true }
- { target: i686-pc-windows-msvc , os: windows-2019 }
- { target: i686-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
- { target: i686-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
- { target: x86_64-apple-darwin , os: macos-12 }
- { target: aarch64-apple-darwin , os: macos-14 }
- { target: x86_64-pc-windows-gnu , os: windows-2019 }
- { target: x86_64-pc-windows-msvc , os: windows-2019 }
- { target: x86_64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
@ -330,7 +342,7 @@ jobs:
echo "IS_RELEASE=${IS_RELEASE}" >> $GITHUB_OUTPUT
- name: Publish archives and packages
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
if: steps.is-release.outputs.IS_RELEASE
with:
files: |

View File

@ -1,9 +1,49 @@
# 10.1.0
## Features
- Allow passing an optional argument to `--strip-cwd-prefix` of "always", "never", or "auto". to force whether the cwd prefix is stripped or not.
- Add a `--format` option which allows using a format template for direct ouput similar to the template used for `--exec`. (#1043)
## Bugfixes
- Fix aarch64 page size again. This time it should actually work. (#1085, #1549) (@tavianator)
## Other
- aarch64-apple-darwin target added to builds on the release page. Note that this is a tier 2 rust target.
# v10.0.0
## Features
- Add `dir` as an alias to `directory` when using `-t` \ `--type`, see #1460 and #1464 (@Ato2207).
- Add support for @%s date format in time filters similar to GNU date (seconds since Unix epoch for --older/--newer), see #1493 (@nabellows)
- Breaking: No longer automatically ignore `.git` when using `--hidden` with vcs ignore enabled. This reverts the change in v9.0.0. While this feature
was often useful, it also broke some existing workflows, and there wasn't a good way to opt out of it. And there isn't really a good way for us to add
a way to opt out of it. And you can easily get similar behavior by adding `.git/` to your global fdignore file.
See #1457.
## Bugfixes
- Respect NO_COLOR environment variable with `--list-details` option. (#1455)
- Fix bug that would cause hidden files to be included despite gitignore rules
if search path is "." (#1461, BurntSushi/ripgrep#2711).
- aarch64 builds now use 64k page sizes with jemalloc. This fixes issues on some systems, such as ARM Macs that
have a larger system page size than the system that the binary was built on. (#1547)
- Address [CVE-2024-24576](https://blog.rust-lang.org/2024/04/09/cve-2024-24576.html), by increasing minimum rust version.
## Changes
- Minimum supported rust version is now 1.77.2
# v9.0.0
## Performance
- Performance has been *significantly improved*, both due to optimizations in the underlying `ignore`
crate (#1429), and in `fd` itself (#1422, #1408, #13620) - @tavianator.
crate (#1429), and in `fd` itself (#1422, #1408, #1362) - @tavianator.
[Benchmarks results](https://gist.github.com/tavianator/32edbe052f33ef60570cf5456b59de81) show gains
of 6-8x for full traversals of smaller directories (100k files) and up to 13x for larger directories (1M files).

488
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -16,9 +16,9 @@ license = "MIT OR Apache-2.0"
name = "fd-find"
readme = "README.md"
repository = "https://github.com/sharkdp/fd"
version = "9.0.0"
version = "10.1.0"
edition= "2021"
rust-version = "1.70.0"
rust-version = "1.77.2"
[badges.appveyor]
repository = "sharkdp/fd"
@ -34,11 +34,11 @@ path = "src/main.rs"
version_check = "0.9"
[dependencies]
aho-corasick = "1.0"
nu-ansi-term = "0.49"
aho-corasick = "1.1"
nu-ansi-term = "0.50"
argmax = "0.3.1"
ignore = "0.4.21"
regex = "1.9.6"
ignore = "0.4.22"
regex = "1.10.3"
regex-syntax = "0.8"
ctrlc = "3.2"
humantime = "2.1"
@ -46,26 +46,26 @@ globset = "0.4"
anyhow = "1.0"
etcetera = "0.8"
normpath = "1.1.1"
crossbeam-channel = "0.5.8"
clap_complete = {version = "4.4.4", optional = true}
crossbeam-channel = "0.5.12"
clap_complete = {version = "4.4.9", optional = true}
faccess = "0.2.4"
[dependencies.clap]
version = "4.4.10"
version = "4.4.13"
features = ["suggestions", "color", "wrap_help", "cargo", "derive"]
[dependencies.chrono]
version = "0.4.31"
version = "0.4.38"
default-features = false
features = ["std", "clock"]
[dependencies.lscolors]
version = "0.16"
version = "0.17"
default-features = false
features = ["nu-ansi-term"]
[target.'cfg(unix)'.dependencies]
nix = { version = "0.27.1", default-features = false, features = ["signal", "user"] }
nix = { version = "0.28.0", default-features = false, features = ["signal", "user"] }
[target.'cfg(all(unix, not(target_os = "redox")))'.dependencies]
libc = "0.2"
@ -78,7 +78,7 @@ jemallocator = {version = "0.5.4", optional = true}
[dev-dependencies]
diff = "0.1"
tempfile = "3.8"
tempfile = "3.10"
filetime = "0.2"
test-case = "3.3"

6
Cross.toml Normal file
View File

@ -0,0 +1,6 @@
# https://github.com/sharkdp/fd/issues/1085
[target.aarch64-unknown-linux-gnu.env]
passthrough = ["JEMALLOC_SYS_WITH_LG_PAGE=16"]
[target.aarch64-unknown-linux-musl.env]
passthrough = ["JEMALLOC_SYS_WITH_LG_PAGE=16"]

View File

@ -6,7 +6,7 @@ datadir=$(prefix)/share
exe_name=fd
$(EXE): Cargo.toml src/**/*.rs
cargo build --profile $(PROFILE)
cargo build --profile $(PROFILE) --locked
.PHONY: completions
completions: autocomplete/fd.bash autocomplete/fd.fish autocomplete/fd.ps1 autocomplete/_fd

View File

@ -10,10 +10,7 @@ It is a simple, fast and user-friendly alternative to [`find`](https://www.gnu.o
While it does not aim to support all of `find`'s powerful functionality, it provides sensible
(opinionated) defaults for a majority of use cases.
Quick links:
* [How to use](#how-to-use)
* [Installation](#installation)
* [Troubleshooting](#troubleshooting)
[Installation](#installation) • [How to use](#how-to-use) • [Troubleshooting](#troubleshooting)
## Features
@ -261,12 +258,17 @@ To make exclude-patterns like these permanent, you can create a `.fdignore` file
/mnt/external-drive
*.bak
```
Note: `fd` also supports `.ignore` files that are used by other programs such as `rg` or `ag`.
> [!NOTE]
> `fd` also supports `.ignore` files that are used by other programs such as `rg` or `ag`.
If you want `fd` to ignore these patterns globally, you can put them in `fd`'s global ignore file.
This is usually located in `~/.config/fd/ignore` in macOS or Linux, and `%APPDATA%\fd\ignore` in
Windows.
You may wish to include `.git/` in your `fd/ignore` file so that `.git` directories, and their contents
are not included in output if you use the `--hidden` option.
### Deleting files
You can use `fd` to remove all files and directories that are matched by your search pattern.
@ -284,7 +286,8 @@ option:
If you also want to remove a certain class of directories, you can use the same technique. You will
have to use `rm`s `--recursive`/`-r` flag to remove directories.
Note: there are scenarios where using `fd … -X rm -r` can cause race conditions: if you have a
> [!NOTE]
> There are scenarios where using `fd … -X rm -r` can cause race conditions: if you have a
path like `…/foo/bar/foo/…` and want to remove all directories named `foo`, you can end up in a
situation where the outer `foo` directory is removed first, leading to (harmless) *"'foo/bar/foo':
No such file or directory"* errors in the `rm` call.
@ -313,7 +316,7 @@ Options:
-p, --full-path Search full abs. path (default: filename only)
-d, --max-depth <depth> Set maximum search depth (default: none)
-E, --exclude <pattern> Exclude entries that match the given glob pattern
-t, --type <filetype> Filter by type: file (f), directory (d), symlink (l),
-t, --type <filetype> Filter by type: file (f), directory (d/dir), symlink (l),
executable (x), empty (e), socket (s), pipe (p), char-device
(c), block-device (b)
-e, --extension <ext> Filter by file extension
@ -321,6 +324,7 @@ Options:
--changed-within <date|dur> Filter by file modification time (newer than)
--changed-before <date|dur> Filter by file modification time (older than)
-o, --owner <user:group> Filter by owning user and/or group
--format <fmt> Print results according to template
-x, --exec <cmd>... Execute a command for each search result
-X, --exec-batch <cmd>... Execute a command with all search results at once
-c, --color <when> When to use colors [default: auto] [possible values: auto,
@ -351,7 +355,7 @@ Benchmark 2: find ~ -iname '*[0-9].jpg'
```
Now let's try the same for `fd`. Note that `fd` performs a regular expression
search by defautl. The options `-u`/`--unrestricted` option is needed here for
search by default. The options `-u`/`--unrestricted` option is needed here for
a fair comparison. Otherwise `fd` does not have to traverse hidden folders and
ignored paths (see below):
```
@ -373,6 +377,15 @@ also used in [ripgrep](https://github.com/BurntSushi/ripgrep) (check it out!).
## Troubleshooting
### `fd` does not find my file!
Remember that `fd` ignores hidden directories and files by default. It also ignores patterns
from `.gitignore` files. If you want to make sure to find absolutely every possible file, always
use the options `-u`/`--unrestricted` option (or `-HI` to enable hidden and ignored files):
``` bash
> fd -u …
```
### Colorized output
`fd` can colorize files by extension, just like `ls`. In order for this to work, the environment
@ -386,15 +399,6 @@ for alternative, more complete (or more colorful) variants, see [here](https://g
`fd` also honors the [`NO_COLOR`](https://no-color.org/) environment variable.
### `fd` does not find my file!
Remember that `fd` ignores hidden directories and files by default. It also ignores patterns
from `.gitignore` files. If you want to make sure to find absolutely every possible file, always
use the options `-u`/`--unrestricted` option (or `-HI` to enable hidden and ignored files):
``` bash
> fd -u …
```
### `fd` doesn't seem to interpret my regex pattern correctly
A lot of special regex characters (like `[]`, `^`, `$`, ..) are also special characters in your
@ -517,7 +521,7 @@ newlines). In the same way, the `-0` option of `xargs` tells it to read the inpu
If you run Ubuntu 19.04 (Disco Dingo) or newer, you can install the
[officially maintained package](https://packages.ubuntu.com/fd-find):
```
sudo apt install fd-find
apt install fd-find
```
Note that the binary is called `fdfind` as the binary name `fd` is already used by another package.
It is recommended that after installation, you add a link to `fd` by executing command
@ -527,7 +531,7 @@ Make sure that `$HOME/.local/bin` is in your `$PATH`.
If you use an older version of Ubuntu, you can download the latest `.deb` package from the
[release page](https://github.com/sharkdp/fd/releases) and install it via:
``` bash
sudo dpkg -i fd_9.0.0_amd64.deb # adapt version number and architecture
dpkg -i fd_9.0.0_amd64.deb # adapt version number and architecture
```
### On Debian
@ -535,7 +539,7 @@ sudo dpkg -i fd_9.0.0_amd64.deb # adapt version number and architecture
If you run Debian Buster or newer, you can install the
[officially maintained Debian package](https://tracker.debian.org/pkg/rust-fd-find):
```
sudo apt-get install fd-find
apt-get install fd-find
```
Note that the binary is called `fdfind` as the binary name `fd` is already used by another package.
It is recommended that after installation, you add a link to `fd` by executing command
@ -563,6 +567,8 @@ You can install [the fd package](https://www.archlinux.org/packages/community/x8
```
pacman -S fd
```
You can also install fd [from the AUR](https://aur.archlinux.org/packages/fd-git).
### On Gentoo Linux
You can use [the fd ebuild](https://packages.gentoo.org/packages/sys-apps/fd) from the official repo:
@ -584,6 +590,20 @@ You can install `fd` via xbps-install:
xbps-install -S fd
```
### On ALT Linux
You can install [the fd package](https://packages.altlinux.org/en/sisyphus/srpms/fd/) from the official repo:
```
apt-get install fd
```
### On Solus
You can install [the fd package](https://github.com/getsolus/packages/tree/main/packages/f/fd) from the official repo:
```
eopkg install fd
```
### On RedHat Enterprise Linux 8/9 (RHEL8/9), Almalinux 8/9, EuroLinux 8/9 or Rocky Linux 8/9
You can install [the `fd` package](https://copr.fedorainfracloud.org/coprs/tkbcopr/fd/) from Fedora Copr.
@ -604,7 +624,7 @@ brew install fd
… or with MacPorts:
```
sudo port install fd
port install fd
```
### On Windows
@ -649,7 +669,7 @@ pkg install fd-find
### From npm
On linux and macOS, you can install the [fd-find](https://npm.im/fd-find) package:
On Linux and macOS, you can install the [fd-find](https://npm.im/fd-find) package:
```
npm install -g fd-find
@ -661,7 +681,7 @@ With Rust's package manager [cargo](https://github.com/rust-lang/cargo), you can
```
cargo install fd-find
```
Note that rust version *1.70.0* or later is required.
Note that rust version *1.77.2* or later is required.
`make` is also needed for the build.
@ -692,8 +712,6 @@ cargo install --path .
## License
Copyright (c) 2017-2021 The fd developers
`fd` is distributed under the terms of both the MIT License and the Apache License 2.0.
See the [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) files for license details.

View File

@ -162,7 +162,7 @@ _fd() {
$no'(*)*--search-path=[set search path (instead of positional <path> arguments)]:directory:_files -/'
+ strip-cwd-prefix
$no'(strip-cwd-prefix exec-cmds)--strip-cwd-prefix[Strip ./ prefix when output is redirected]'
$no'(strip-cwd-prefix exec-cmds)--strip-cwd-prefix=[When to strip ./]:when:(always never auto)'
+ and
'--and=[additional required search path]:pattern'

122
doc/fd.1 vendored
View File

@ -33,16 +33,14 @@ with the '\-\-glob' option.
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. For convenenience, '.git' is treated as if it
was always included in gitignore rules. These files can be included with options such as
'\-\-hidden' and '\-\-no\-ignore'.
or ignore rules in .ignore or .fdignore files.
.SH OPTIONS
.TP
.B \-H, \-\-hidden
Include hidden files and directories in the search results
(default: hidden files and directories are skipped). The flag can be overridden with '--no-hidden'.
.IP
Ignored files and .git/ are still excluded unless \-\-no\-ignore or \-\-no\-ignore\-vcs
Ignored files are still excluded unless \-\-no\-ignore or \-\-no\-ignore\-vcs
is also used.
.TP
.B \-I, \-\-no\-ignore
@ -79,7 +77,6 @@ and the global gitignore configuration
.RI ( core.excludesFile
git setting, which defaults to
.IR $HOME/.config/git/ignore ).
The pattern ".git/" is automatically added to the list of VCS ignore rules.
The flag can be overridden with '--ignore-vcs'.
.TP
.B \-\-no\-require\-git
@ -159,9 +156,20 @@ can be used as an alias.
Enable the display of filesystem errors for situations such as insufficient
permissions or dead symlinks.
.TP
.B \-\-strip-cwd-prefix
By default, relative paths are prefixed with './' when the output goes to a non interactive terminal
(TTY). Use this flag to disable this behaviour.
.B \-\-strip-cwd-prefix [when]
By default, relative paths are prefixed with './' when -x/--exec,
-X/--exec-batch, or -0/--print0 are given, to reduce the risk of a
path starting with '-' being treated as a command line option. Use
this flag to change this behavior. If this flag is used without a value,
it is equivalent to passing "always". Possible values are:
.RS
.IP never
Never strip the ./ at the beginning of paths
.IP always
Always strip the ./ at the beginning of paths
.IP auto
Only strip if used with --exec, --exec-batch, or --print0. That is, it resets to the default behavior.
.RE
.TP
.B \-\-one\-file\-system, \-\-mount, \-\-xdev
By default, fd will traverse the file system tree as far as other options dictate. With this flag, fd ensures that it does not descend into a different file system than the one it started in. Comparable to the -mount or -xdev filters of find(1).
@ -191,7 +199,7 @@ Filter search by type:
.RS
.IP "f, file"
regular files
.IP "d, directory"
.IP "d, dir, directory"
directories
.IP "l, symlink"
symbolic links
@ -312,8 +320,9 @@ tebibytes
Filter results based on the file modification time.
Files with modification times greater than the argument will be returned.
The argument can be provided as a duration (\fI10h, 1d, 35min\fR) or as a specific point
in time in either full RFC3339 format with time zone, or as a date or datetime in the
local time zone (\fIYYYY-MM-DD\fR or \fIYYYY-MM-DD HH:MM:SS\fR).
in time as full RFC3339 format with time zone, as a date or datetime in the
local time zone (\fIYYYY-MM-DD\fR or \fIYYYY-MM-DD HH:MM:SS\fR), or as the prefix '@'
followed by the number of seconds since the Unix epoch (@[0-9]+).
\fB\-\-change-newer-than\fR,
.B --newer
or
@ -324,13 +333,15 @@ Examples:
\-\-changed-within 2weeks
\-\-change-newer-than "2018-10-27 10:00:00"
\-\-newer 2018-10-27
\-\-changed-after @1704067200
.TP
.BI "\-\-changed-before " date|duration
Filter results based on the file modification time.
Files with modification times less than the argument will be returned.
The argument can be provided as a duration (\fI10h, 1d, 35min\fR) or as a specific point
in time in either full RFC3339 format with time zone, or as a date or datetime in the
local time zone (\fIYYYY-MM-DD\fR or \fIYYYY-MM-DD HH:MM:SS\fR).
in time as full RFC3339 format with time zone, as a date or datetime in the
local time zone (\fIYYYY-MM-DD\fR or \fIYYYY-MM-DD HH:MM:SS\fR), or as the prefix '@'
followed by the number of seconds since the Unix epoch (@[0-9]+).
.B --change-older-than
or
.B --older
@ -339,6 +350,7 @@ can be used as aliases.
Examples:
\-\-changed-before "2018-10-27 10:00:00"
\-\-change-older-than 2weeks
\-\-older @1704067200
.TP
.BI "-o, \-\-owner " [user][:group]
Filter files by their user and/or group. Format: [(user|uid)][:(group|gid)]. Either side
@ -363,6 +375,30 @@ Set the path separator to use when printing file paths. The default is the OS-sp
Provide paths to search as an alternative to the positional \fIpath\fR argument. Changes the usage to
\'fd [FLAGS/OPTIONS] \-\-search\-path PATH \-\-search\-path PATH2 [PATTERN]\'
.TP
.BI "\-\-format " fmt
Specify a template string that is used for printing a line for each file found.
The following placeholders are substituted into the string for each file before printing:
.RS
.IP {}
path (of the current search result)
.IP {/}
basename
.IP {//}
parent directory
.IP {.}
path without file extension
.IP {/.}
basename without file extension
.IP {{
literal '{' (an escape sequence)
.IP }}
literal '}' (an escape sequence)
.P
Notice that you can use "{{" and "}}" to escape "{" and "}" respectively, which is especially
useful if you need to include the literal text of one of the above placeholders.
.RE
.TP
.BI "\-x, \-\-exec " command
.RS
Execute
@ -383,29 +419,12 @@ If parallelism is enabled, the order commands will be executed in is non-determi
--threads=1, the order is determined by the operating system and may not be what you expect. Thus, it is
recommended that you don't rely on any ordering of the results.
The following placeholders are substituted before the command is executed:
.RS
.IP {}
path (of the current search result)
.IP {/}
basename
.IP {//}
parent directory
.IP {.}
path without file extension
.IP {/.}
basename without file extension
.IP {{
literal '{' (an escape sequence)
.IP }}
literal '}' (an escape sequence)
.RE
Before executing the command, any placeholder patterns in the command are replaced with the
corresponding values for the current file. The same placeholders are used as in the "\-\-format"
option.
If no placeholder is present, an implicit "{}" at the end is assumed.
Notice that you can use "{{" and "}}" to escape "{" and "}" respectively, which is especially
useful if you need to include the literal text of one of the above placeholders.
Examples:
- find all *.zip files and unzip them:
@ -429,19 +448,9 @@ once, with all search results as arguments.
The order of the arguments is non-deterministic and should not be relied upon.
One of the following placeholders is substituted before the command is executed:
.RS
.IP {}
path (of all search results)
.IP {/}
basename
.IP {//}
parent directory
.IP {.}
path without file extension
.IP {/.}
basename without file extension
.RE
This uses the same placeholders as "\-\-format" and "\-\-exec", but instead of expanding
once per command invocation each argument containing a placeholder is expanding for every
file in a batch and passed as separate arguments.
If no placeholder is present, an implicit "{}" at the end is assumed.
@ -490,6 +499,17 @@ is set, use
.IR $XDG_CONFIG_HOME/fd/ignore .
Otherwise, use
.IR $HOME/.config/fd/ignore .
.SH FILES
.TP
.B .fdignore
This file works similarly to a .gitignore file anywhere in the searched tree and specifies patterns
that should be excluded from the search. However, this file is specific to fd, and will be used even
if the --no-ignore-vcs option is used.
.TP
.B $XDG_CONFIG_HOME/fd/ignore
Global ignore file. Unless ignore mode is turned off (such as with --no-ignore)
ignore entries in this file will be ignored, as if it was an .fdignore file in the
current directory.
.SH EXAMPLES
.TP
.RI "Find files and directories that match the pattern '" needle "':"
@ -503,6 +523,16 @@ $ fd -e py
.TP
.RI "Open all search results with vim:"
$ fd pattern -X vim
.SH Tips and Tricks
.IP \[bu]
If you add ".git/" to your global ignore file ($XDG_CONFIG_HOME/fd/ignore), then
".git" folders will be ignored by default, even when the --hidden option is used.
.IP \[bu]
You can use a shell alias or a wrapper script in order to pass desired flags to fd
by default. For example if you do not like the default behavior of respecting gitignore,
you can use
`alias fd="/usr/bin/fd --no-ignore-vcs"`
in your .bashrc to create an alias for fd that doesn't ignore git files by default.
.SH BUGS
Bugs can be reported on GitHub: https://github.com/sharkdp/fd/issues
.SH SEE ALSO

12
doc/sponsors.md vendored Normal file
View File

@ -0,0 +1,12 @@
## Sponsors
`fd` development is sponsored by many individuals and companies. Thank you very much!
Please note, that being sponsored does not affect the individuality of the `fd`
project or affect the maintainers' actions in any way.
We remain impartial and continue to assess pull requests solely on merit - the
features added, bugs solved, and effect on the overall complexity of the code.
No issue will have a different priority based on sponsorship status of the
reporter.
Contributions from anybody are most welcomed, please see our [`CONTRIBUTING.md`](../CONTRIBUTING.md) guide.

5
doc/sponsors/terminal_trove_green.svg vendored Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.2 KiB

22
scripts/version-bump.sh Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/bash
set -eu
# This script automates the "Version bump" section
version="$1"
if [[ -z $version ]]; then
echo "Usage: must supply version as first argument" >&2
exit 1
fi
git switch -C "release-$version"
sed -i -e "0,/^\[badges/{s/^version =.*/version = \"$version\"/}" Cargo.toml
msrv="$(grep -F rust-version Cargo.toml | sed -e 's/^rust-version= "\(.*\)"/\1/')"
sed -i -e "s/Note that rust version \*[0-9.]+\* or later/Note that rust version *$msrv* or later/" README.md
sed -i -e "s/^# Upcoming release/# $version/" CHANGELOG.md

View File

@ -49,8 +49,7 @@ pub struct Opts {
no_hidden: (),
/// Show search results from files and directories that would otherwise be
/// ignored by '.gitignore', '.ignore', '.fdignore', the global ignore file,
/// or the default rule that excludes .git/.
/// ignored by '.gitignore', '.ignore', '.fdignore', or the global ignore file,
/// The flag can be overridden with --ignore.
#[arg(
long,
@ -64,7 +63,7 @@ pub struct Opts {
#[arg(long, overrides_with = "no_ignore", hide = true, action = ArgAction::SetTrue)]
ignore: (),
///Show search results from '.git/' folders and files and directories that
///Show search results from files and directories that
///would otherwise be ignored by '.gitignore' files.
///The flag can be overridden with --ignore-vcs.
#[arg(
@ -314,7 +313,7 @@ pub struct Opts {
/// Filter the search by type:
/// {n} 'f' or 'file': regular files
/// {n} 'd' or 'directory': directories
/// {n} 'd' or 'dir' or 'directory': directories
/// {n} 'l' or 'symlink': symbolic links
/// {n} 's' or 'socket': socket
/// {n} 'p' or 'pipe': named pipe (FIFO)
@ -352,7 +351,7 @@ pub struct Opts {
value_name = "filetype",
hide_possible_values = true,
value_enum,
help = "Filter by type: file (f), directory (d), symlink (l), \
help = "Filter by type: file (f), directory (d/dir), symlink (l), \
executable (x), empty (e), socket (s), pipe (p), \
char-device (c), block-device (b)",
long_help
@ -399,7 +398,7 @@ pub struct Opts {
/// Filter results based on the file modification time. Files with modification times
/// greater than the argument are returned. The argument can be provided
/// as a specific point in time (YYYY-MM-DD HH:MM:SS) or as a duration (10h, 1d, 35min).
/// as a specific point in time (YYYY-MM-DD HH:MM:SS or @timestamp) or as a duration (10h, 1d, 35min).
/// If the time is not specified, it defaults to 00:00:00.
/// '--change-newer-than', '--newer', or '--changed-after' can be used as aliases.
///
@ -421,7 +420,7 @@ pub struct Opts {
/// Filter results based on the file modification time. Files with modification times
/// less than the argument are returned. The argument can be provided
/// as a specific point in time (YYYY-MM-DD HH:MM:SS) or as a duration (10h, 1d, 35min).
/// as a specific point in time (YYYY-MM-DD HH:MM:SS or @timestamp) or as a duration (10h, 1d, 35min).
/// '--change-older-than' or '--older' can be used as aliases.
///
/// Examples:
@ -453,6 +452,20 @@ pub struct Opts {
)]
pub owner: Option<OwnerFilter>,
/// Instead of printing the file normally, print the format string with the following placeholders replaced:
/// '{}': path (of the current search result)
/// '{/}': basename
/// '{//}': parent directory
/// '{.}': path without file extension
/// '{/.}': basename without file extension
#[arg(
long,
value_name = "fmt",
help = "Print results according to template",
conflicts_with = "list_details"
)]
pub format: Option<String>,
#[command(flatten)]
pub exec: Exec,
@ -618,9 +631,10 @@ pub struct Opts {
/// By default, relative paths are prefixed with './' when -x/--exec,
/// -X/--exec-batch, or -0/--print0 are given, to reduce the risk of a
/// path starting with '-' being treated as a command line option. Use
/// this flag to disable this behaviour.
#[arg(long, conflicts_with_all(&["path", "search_path"]), hide_short_help = true, long_help)]
pub strip_cwd_prefix: bool,
/// this flag to change this behavior. If this flag is used without a value,
/// it is equivalent to passing "always".
#[arg(long, conflicts_with_all(&["path", "search_path"]), value_name = "when", hide_short_help = true, require_equals = true, long_help)]
strip_cwd_prefix: Option<Option<StripCwdWhen>>,
/// By default, fd will traverse the file system tree as far as other options
/// dictate. With this flag, fd ensures that it does not descend into a
@ -643,7 +657,7 @@ impl Opts {
} else if !self.search_path.is_empty() {
&self.search_path
} else {
let current_directory = Path::new(".");
let current_directory = Path::new("./");
ensure_current_directory_exists(current_directory)?;
return Ok(vec![self.normalize_path(current_directory)]);
};
@ -666,6 +680,9 @@ impl Opts {
fn normalize_path(&self, path: &Path) -> PathBuf {
if self.absolute_path {
filesystem::absolute_path(path.normalize().unwrap().as_path()).unwrap()
} else if path == Path::new(".") {
// Change "." to "./" as a workaround for https://github.com/BurntSushi/ripgrep/pull/2711
PathBuf::from("./")
} else {
path.to_path_buf()
}
@ -698,6 +715,16 @@ impl Opts {
.or_else(|| self.max_one_result.then_some(1))
}
pub fn strip_cwd_prefix<P: FnOnce() -> bool>(&self, auto_pred: P) -> bool {
use self::StripCwdWhen::*;
self.no_search_paths()
&& match self.strip_cwd_prefix.map_or(Auto, |o| o.unwrap_or(Always)) {
Auto => auto_pred(),
Always => true,
Never => false,
}
}
#[cfg(feature = "completions")]
pub fn gen_completions(&self) -> anyhow::Result<Option<Shell>> {
self.gen_completions
@ -729,7 +756,7 @@ fn default_num_threads() -> NonZeroUsize {
pub enum FileType {
#[value(alias = "f")]
File,
#[value(alias = "d")]
#[value(alias = "d", alias = "dir")]
Directory,
#[value(alias = "l")]
Symlink,
@ -758,15 +785,14 @@ pub enum ColorWhen {
Never,
}
impl ColorWhen {
pub fn as_str(&self) -> &'static str {
use ColorWhen::*;
match *self {
Auto => "auto",
Never => "never",
Always => "always",
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, ValueEnum)]
pub enum StripCwdWhen {
/// Use the default behavior
Auto,
/// Always strip the ./ at the beginning of paths
Always,
/// Never strip the ./
Never,
}
// there isn't a derive api for getting grouped values yet,

View File

@ -8,6 +8,7 @@ use crate::filetypes::FileTypes;
#[cfg(unix)]
use crate::filter::OwnerFilter;
use crate::filter::{SizeFilter, TimeFilter};
use crate::fmt::FormatTemplate;
/// Configuration options for *fd*.
pub struct Config {
@ -85,6 +86,9 @@ pub struct Config {
/// The value (if present) will be a lowercase string without leading dots.
pub extensions: Option<RegexSet>,
/// A format string to use to format results, similarly to exec
pub format: Option<FormatTemplate>,
/// If a value is supplied, each item found will be used to generate and execute commands.
pub command: Option<Arc<CommandSet>>,

View File

@ -113,7 +113,7 @@ impl Eq for DirEntry {}
impl PartialOrd for DirEntry {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.path().partial_cmp(other.path())
Some(self.cmp(other))
}
}

View File

@ -1,13 +1,10 @@
mod command;
mod input;
mod job;
mod token;
use std::borrow::Cow;
use std::ffi::{OsStr, OsString};
use std::ffi::OsString;
use std::io;
use std::iter;
use std::path::{Component, Path, PathBuf, Prefix};
use std::path::{Path, PathBuf};
use std::process::Stdio;
use std::sync::Mutex;
@ -15,11 +12,10 @@ use anyhow::{bail, Result};
use argmax::Command;
use crate::exit_codes::{merge_exitcodes, ExitCode};
use crate::fmt::{FormatTemplate, Token};
use self::command::{execute_commands, handle_cmd_error};
use self::input::{basename, dirname, remove_extension};
pub use self::job::{batch, job};
use self::token::{tokenize, Token};
/// Execution mode of the command
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -131,7 +127,7 @@ impl CommandSet {
#[derive(Debug)]
struct CommandBuilder {
pre_args: Vec<OsString>,
path_arg: ArgumentTemplate,
path_arg: FormatTemplate,
post_args: Vec<OsString>,
cmd: Command,
count: usize,
@ -220,7 +216,7 @@ impl CommandBuilder {
/// `generate_and_execute()` method will be used to generate a command and execute it.
#[derive(Debug, Clone, PartialEq)]
struct CommandTemplate {
args: Vec<ArgumentTemplate>,
args: Vec<FormatTemplate>,
}
impl CommandTemplate {
@ -235,7 +231,7 @@ impl CommandTemplate {
for arg in input {
let arg = arg.as_ref();
let tmpl = tokenize(arg);
let tmpl = FormatTemplate::parse(arg);
has_placeholder |= tmpl.has_tokens();
args.push(tmpl);
}
@ -251,7 +247,7 @@ impl CommandTemplate {
// If a placeholder token was not supplied, append one at the end of the command.
if !has_placeholder {
args.push(ArgumentTemplate::Tokens(vec![Token::Placeholder]));
args.push(FormatTemplate::Tokens(vec![Token::Placeholder]));
}
Ok(CommandTemplate { args })
@ -274,111 +270,6 @@ impl CommandTemplate {
}
}
/// Represents a template for a single command argument.
///
/// The argument is either a collection of `Token`s including at least one placeholder variant, or
/// a fixed text.
#[derive(Clone, Debug, PartialEq)]
enum ArgumentTemplate {
Tokens(Vec<Token>),
Text(String),
}
impl ArgumentTemplate {
pub fn has_tokens(&self) -> bool {
matches!(self, ArgumentTemplate::Tokens(_))
}
/// Generate an argument from this template. If path_separator is Some, then it will replace
/// the path separator in all placeholder tokens. Text arguments and tokens are not affected by
/// path separator substitution.
pub fn generate(&self, path: impl AsRef<Path>, path_separator: Option<&str>) -> OsString {
use self::Token::*;
let path = path.as_ref();
match *self {
ArgumentTemplate::Tokens(ref tokens) => {
let mut s = OsString::new();
for token in tokens {
match *token {
Basename => s.push(Self::replace_separator(basename(path), path_separator)),
BasenameNoExt => s.push(Self::replace_separator(
&remove_extension(basename(path).as_ref()),
path_separator,
)),
NoExt => s.push(Self::replace_separator(
&remove_extension(path),
path_separator,
)),
Parent => s.push(Self::replace_separator(&dirname(path), path_separator)),
Placeholder => {
s.push(Self::replace_separator(path.as_ref(), path_separator))
}
Text(ref string) => s.push(string),
}
}
s
}
ArgumentTemplate::Text(ref text) => OsString::from(text),
}
}
/// Replace the path separator in the input with the custom separator string. If path_separator
/// is None, simply return a borrowed Cow<OsStr> of the input. Otherwise, the input is
/// interpreted as a Path and its components are iterated through and re-joined into a new
/// OsString.
fn replace_separator<'a>(path: &'a OsStr, path_separator: Option<&str>) -> Cow<'a, OsStr> {
// fast-path - no replacement necessary
if path_separator.is_none() {
return Cow::Borrowed(path);
}
let path_separator = path_separator.unwrap();
let mut out = OsString::with_capacity(path.len());
let mut components = Path::new(path).components().peekable();
while let Some(comp) = components.next() {
match comp {
// Absolute paths on Windows are tricky. A Prefix component is usually a drive
// letter or UNC path, and is usually followed by RootDir. There are also
// "verbatim" prefixes beginning with "\\?\" that skip normalization. We choose to
// ignore verbatim path prefixes here because they're very rare, might be
// impossible to reach here, and there's no good way to deal with them. If users
// are doing something advanced involving verbatim windows paths, they can do their
// own output filtering with a tool like sed.
Component::Prefix(prefix) => {
if let Prefix::UNC(server, share) = prefix.kind() {
// Prefix::UNC is a parsed version of '\\server\share'
out.push(path_separator);
out.push(path_separator);
out.push(server);
out.push(path_separator);
out.push(share);
} else {
// All other Windows prefix types are rendered as-is. This results in e.g. "C:" for
// drive letters. DeviceNS and Verbatim* prefixes won't have backslashes converted,
// but they're not returned by directories fd can search anyway so we don't worry
// about them.
out.push(comp.as_os_str());
}
}
// Root directory is always replaced with the custom separator.
Component::RootDir => out.push(path_separator),
// Everything else is joined normally, with a trailing separator if we're not last
_ => {
out.push(comp.as_os_str());
if components.peek().is_some() {
out.push(path_separator);
}
}
}
}
Cow::Owned(out)
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -398,9 +289,9 @@ mod tests {
CommandSet {
commands: vec![CommandTemplate {
args: vec![
ArgumentTemplate::Text("echo".into()),
ArgumentTemplate::Text("${SHELL}:".into()),
ArgumentTemplate::Tokens(vec![Token::Placeholder]),
FormatTemplate::Text("echo".into()),
FormatTemplate::Text("${SHELL}:".into()),
FormatTemplate::Tokens(vec![Token::Placeholder]),
]
}],
mode: ExecutionMode::OneByOne,
@ -415,8 +306,8 @@ mod tests {
CommandSet {
commands: vec![CommandTemplate {
args: vec![
ArgumentTemplate::Text("echo".into()),
ArgumentTemplate::Tokens(vec![Token::NoExt]),
FormatTemplate::Text("echo".into()),
FormatTemplate::Tokens(vec![Token::NoExt]),
],
}],
mode: ExecutionMode::OneByOne,
@ -431,8 +322,8 @@ mod tests {
CommandSet {
commands: vec![CommandTemplate {
args: vec![
ArgumentTemplate::Text("echo".into()),
ArgumentTemplate::Tokens(vec![Token::Basename]),
FormatTemplate::Text("echo".into()),
FormatTemplate::Tokens(vec![Token::Basename]),
],
}],
mode: ExecutionMode::OneByOne,
@ -447,8 +338,8 @@ mod tests {
CommandSet {
commands: vec![CommandTemplate {
args: vec![
ArgumentTemplate::Text("echo".into()),
ArgumentTemplate::Tokens(vec![Token::Parent]),
FormatTemplate::Text("echo".into()),
FormatTemplate::Tokens(vec![Token::Parent]),
],
}],
mode: ExecutionMode::OneByOne,
@ -463,8 +354,8 @@ mod tests {
CommandSet {
commands: vec![CommandTemplate {
args: vec![
ArgumentTemplate::Text("echo".into()),
ArgumentTemplate::Tokens(vec![Token::BasenameNoExt]),
FormatTemplate::Text("echo".into()),
FormatTemplate::Tokens(vec![Token::BasenameNoExt]),
],
}],
mode: ExecutionMode::OneByOne,
@ -494,9 +385,9 @@ mod tests {
CommandSet {
commands: vec![CommandTemplate {
args: vec![
ArgumentTemplate::Text("cp".into()),
ArgumentTemplate::Tokens(vec![Token::Placeholder]),
ArgumentTemplate::Tokens(vec![
FormatTemplate::Text("cp".into()),
FormatTemplate::Tokens(vec![Token::Placeholder]),
FormatTemplate::Tokens(vec![
Token::BasenameNoExt,
Token::Text(".ext".into())
]),
@ -514,8 +405,8 @@ mod tests {
CommandSet {
commands: vec![CommandTemplate {
args: vec![
ArgumentTemplate::Text("echo".into()),
ArgumentTemplate::Tokens(vec![Token::NoExt]),
FormatTemplate::Text("echo".into()),
FormatTemplate::Tokens(vec![Token::NoExt]),
],
}],
mode: ExecutionMode::Batch,
@ -540,7 +431,7 @@ mod tests {
#[test]
fn generate_custom_path_separator() {
let arg = ArgumentTemplate::Tokens(vec![Token::Placeholder]);
let arg = FormatTemplate::Tokens(vec![Token::Placeholder]);
macro_rules! check {
($input:expr, $expected:expr) => {
assert_eq!(arg.generate($input, Some("#")), OsString::from($expected));
@ -555,7 +446,7 @@ mod tests {
#[cfg(windows)]
#[test]
fn generate_custom_path_separator_windows() {
let arg = ArgumentTemplate::Tokens(vec![Token::Placeholder]);
let arg = FormatTemplate::Tokens(vec![Token::Placeholder]);
macro_rules! check {
($input:expr, $expected:expr) => {
assert_eq!(arg.generate($input, Some("#")), OsString::from($expected));

View File

@ -1,98 +0,0 @@
use aho_corasick::AhoCorasick;
use std::fmt::{self, Display, Formatter};
use std::sync::OnceLock;
use super::ArgumentTemplate;
/// Designates what should be written to a buffer
///
/// Each `Token` contains either text, or a placeholder variant, which will be used to generate
/// commands after all tokens for a given command template have been collected.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Token {
Placeholder,
Basename,
Parent,
NoExt,
BasenameNoExt,
Text(String),
}
impl Display for Token {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match *self {
Token::Placeholder => f.write_str("{}")?,
Token::Basename => f.write_str("{/}")?,
Token::Parent => f.write_str("{//}")?,
Token::NoExt => f.write_str("{.}")?,
Token::BasenameNoExt => f.write_str("{/.}")?,
Token::Text(ref string) => f.write_str(string)?,
}
Ok(())
}
}
static PLACEHOLDERS: OnceLock<AhoCorasick> = OnceLock::new();
pub(super) fn tokenize(input: &str) -> ArgumentTemplate {
// NOTE: we assume that { and } have the same length
const BRACE_LEN: usize = '{'.len_utf8();
let mut tokens = Vec::new();
let mut remaining = input;
let mut buf = String::new();
let placeholders = PLACEHOLDERS.get_or_init(|| {
AhoCorasick::new(&["{{", "}}", "{}", "{/}", "{//}", "{.}", "{/.}"]).unwrap()
});
while let Some(m) = placeholders.find(remaining) {
match m.pattern().as_u32() {
0 | 1 => {
// we found an escaped {{ or }}, so add
// everything up to the first char to the buffer
// then skip the second one.
buf += &remaining[..m.start() + BRACE_LEN];
remaining = &remaining[m.end()..];
}
id if !remaining[m.end()..].starts_with('}') => {
buf += &remaining[..m.start()];
if !buf.is_empty() {
tokens.push(Token::Text(std::mem::take(&mut buf)));
}
tokens.push(token_from_pattern_id(id));
remaining = &remaining[m.end()..];
}
_ => {
// We got a normal pattern, but the final "}"
// is escaped, so add up to that to the buffer, then
// skip the final }
buf += &remaining[..m.end()];
remaining = &remaining[m.end() + BRACE_LEN..];
}
}
}
// Add the rest of the string to the buffer, and add the final buffer to the tokens
if !remaining.is_empty() {
buf += remaining;
}
if tokens.is_empty() {
// No placeholders were found, so just return the text
return ArgumentTemplate::Text(buf);
}
// Add final text segment
if !buf.is_empty() {
tokens.push(Token::Text(buf));
}
debug_assert!(!tokens.is_empty());
ArgumentTemplate::Tokens(tokens)
}
fn token_from_pattern_id(id: u32) -> Token {
use Token::*;
match id {
2 => Placeholder,
3 => Basename,
4 => Parent,
5 => NoExt,
6 => BasenameNoExt,
_ => unreachable!(),
}
}

View File

@ -128,13 +128,11 @@ pub fn strip_current_dir(path: &Path) -> &Path {
pub fn default_path_separator() -> Option<String> {
if cfg!(windows) {
let msystem = env::var("MSYSTEM").ok()?;
match msystem.as_str() {
"MINGW64" | "MINGW32" | "MSYS" => Some("/".to_owned()),
_ => None,
if !msystem.is_empty() {
return Some("/".to_owned());
}
} else {
None
}
None
}
#[cfg(test)]

View File

@ -31,6 +31,10 @@ impl TimeFilter {
.and_local_timezone(Local)
.latest()
})
.or_else(|| {
let timestamp_secs = s.strip_prefix('@')?.parse().ok()?;
DateTime::from_timestamp(timestamp_secs, 0).map(Into::into)
})
.map(|dt| dt.into())
})
}
@ -135,5 +139,32 @@ mod tests {
assert!(!TimeFilter::after(&ref_time, t10s_before)
.unwrap()
.applies_to(&t1m_ago));
let ref_timestamp = 1707723412u64; // Mon Feb 12 07:36:52 UTC 2024
let ref_time = DateTime::parse_from_rfc3339("2024-02-12T07:36:52+00:00")
.unwrap()
.into();
let t1m_ago = ref_time - Duration::from_secs(60);
let t1s_later = ref_time + Duration::from_secs(1);
// Timestamp only supported via '@' prefix
assert!(TimeFilter::before(&ref_time, &ref_timestamp.to_string()).is_none());
assert!(
TimeFilter::before(&ref_time, &format!("@{}", ref_timestamp))
.unwrap()
.applies_to(&t1m_ago)
);
assert!(
!TimeFilter::before(&ref_time, &format!("@{}", ref_timestamp))
.unwrap()
.applies_to(&t1s_later)
);
assert!(
!TimeFilter::after(&ref_time, &format!("@{}", ref_timestamp))
.unwrap()
.applies_to(&t1m_ago)
);
assert!(TimeFilter::after(&ref_time, &format!("@{}", ref_timestamp))
.unwrap()
.applies_to(&t1s_later));
}
}

View File

@ -34,10 +34,10 @@ pub fn dirname(path: &Path) -> OsString {
#[cfg(test)]
mod path_tests {
use super::*;
use std::path::MAIN_SEPARATOR;
use std::path::MAIN_SEPARATOR_STR;
fn correct(input: &str) -> String {
input.replace('/', &MAIN_SEPARATOR.to_string())
input.replace('/', MAIN_SEPARATOR_STR)
}
macro_rules! func_tests {

281
src/fmt/mod.rs Normal file
View File

@ -0,0 +1,281 @@
mod input;
use std::borrow::Cow;
use std::ffi::{OsStr, OsString};
use std::fmt::{self, Display, Formatter};
use std::path::{Component, Path, Prefix};
use std::sync::OnceLock;
use aho_corasick::AhoCorasick;
use self::input::{basename, dirname, remove_extension};
/// Designates what should be written to a buffer
///
/// Each `Token` contains either text, or a placeholder variant, which will be used to generate
/// commands after all tokens for a given command template have been collected.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Token {
Placeholder,
Basename,
Parent,
NoExt,
BasenameNoExt,
Text(String),
}
impl Display for Token {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match *self {
Token::Placeholder => f.write_str("{}")?,
Token::Basename => f.write_str("{/}")?,
Token::Parent => f.write_str("{//}")?,
Token::NoExt => f.write_str("{.}")?,
Token::BasenameNoExt => f.write_str("{/.}")?,
Token::Text(ref string) => f.write_str(string)?,
}
Ok(())
}
}
/// A parsed format string
///
/// This is either a collection of `Token`s including at least one placeholder variant,
/// or a fixed text.
#[derive(Clone, Debug, PartialEq)]
pub enum FormatTemplate {
Tokens(Vec<Token>),
Text(String),
}
static PLACEHOLDERS: OnceLock<AhoCorasick> = OnceLock::new();
impl FormatTemplate {
pub fn has_tokens(&self) -> bool {
matches!(self, FormatTemplate::Tokens(_))
}
pub fn parse(fmt: &str) -> Self {
// NOTE: we assume that { and } have the same length
const BRACE_LEN: usize = '{'.len_utf8();
let mut tokens = Vec::new();
let mut remaining = fmt;
let mut buf = String::new();
let placeholders = PLACEHOLDERS.get_or_init(|| {
AhoCorasick::new(["{{", "}}", "{}", "{/}", "{//}", "{.}", "{/.}"]).unwrap()
});
while let Some(m) = placeholders.find(remaining) {
match m.pattern().as_u32() {
0 | 1 => {
// we found an escaped {{ or }}, so add
// everything up to the first char to the buffer
// then skip the second one.
buf += &remaining[..m.start() + BRACE_LEN];
remaining = &remaining[m.end()..];
}
id if !remaining[m.end()..].starts_with('}') => {
buf += &remaining[..m.start()];
if !buf.is_empty() {
tokens.push(Token::Text(std::mem::take(&mut buf)));
}
tokens.push(token_from_pattern_id(id));
remaining = &remaining[m.end()..];
}
_ => {
// We got a normal pattern, but the final "}"
// is escaped, so add up to that to the buffer, then
// skip the final }
buf += &remaining[..m.end()];
remaining = &remaining[m.end() + BRACE_LEN..];
}
}
}
// Add the rest of the string to the buffer, and add the final buffer to the tokens
if !remaining.is_empty() {
buf += remaining;
}
if tokens.is_empty() {
// No placeholders were found, so just return the text
return FormatTemplate::Text(buf);
}
// Add final text segment
if !buf.is_empty() {
tokens.push(Token::Text(buf));
}
debug_assert!(!tokens.is_empty());
FormatTemplate::Tokens(tokens)
}
/// Generate a result string from this template. If path_separator is Some, then it will replace
/// the path separator in all placeholder tokens. Fixed text and tokens are not affected by
/// path separator substitution.
pub fn generate(&self, path: impl AsRef<Path>, path_separator: Option<&str>) -> OsString {
use Token::*;
let path = path.as_ref();
match *self {
Self::Tokens(ref tokens) => {
let mut s = OsString::new();
for token in tokens {
match token {
Basename => s.push(Self::replace_separator(basename(path), path_separator)),
BasenameNoExt => s.push(Self::replace_separator(
&remove_extension(basename(path).as_ref()),
path_separator,
)),
NoExt => s.push(Self::replace_separator(
&remove_extension(path),
path_separator,
)),
Parent => s.push(Self::replace_separator(&dirname(path), path_separator)),
Placeholder => {
s.push(Self::replace_separator(path.as_ref(), path_separator))
}
Text(ref string) => s.push(string),
}
}
s
}
Self::Text(ref text) => OsString::from(text),
}
}
/// Replace the path separator in the input with the custom separator string. If path_separator
/// is None, simply return a borrowed Cow<OsStr> of the input. Otherwise, the input is
/// interpreted as a Path and its components are iterated through and re-joined into a new
/// OsString.
fn replace_separator<'a>(path: &'a OsStr, path_separator: Option<&str>) -> Cow<'a, OsStr> {
// fast-path - no replacement necessary
if path_separator.is_none() {
return Cow::Borrowed(path);
}
let path_separator = path_separator.unwrap();
let mut out = OsString::with_capacity(path.len());
let mut components = Path::new(path).components().peekable();
while let Some(comp) = components.next() {
match comp {
// Absolute paths on Windows are tricky. A Prefix component is usually a drive
// letter or UNC path, and is usually followed by RootDir. There are also
// "verbatim" prefixes beginning with "\\?\" that skip normalization. We choose to
// ignore verbatim path prefixes here because they're very rare, might be
// impossible to reach here, and there's no good way to deal with them. If users
// are doing something advanced involving verbatim windows paths, they can do their
// own output filtering with a tool like sed.
Component::Prefix(prefix) => {
if let Prefix::UNC(server, share) = prefix.kind() {
// Prefix::UNC is a parsed version of '\\server\share'
out.push(path_separator);
out.push(path_separator);
out.push(server);
out.push(path_separator);
out.push(share);
} else {
// All other Windows prefix types are rendered as-is. This results in e.g. "C:" for
// drive letters. DeviceNS and Verbatim* prefixes won't have backslashes converted,
// but they're not returned by directories fd can search anyway so we don't worry
// about them.
out.push(comp.as_os_str());
}
}
// Root directory is always replaced with the custom separator.
Component::RootDir => out.push(path_separator),
// Everything else is joined normally, with a trailing separator if we're not last
_ => {
out.push(comp.as_os_str());
if components.peek().is_some() {
out.push(path_separator);
}
}
}
}
Cow::Owned(out)
}
}
// Convert the id from an aho-corasick match to the
// appropriate token
fn token_from_pattern_id(id: u32) -> Token {
use Token::*;
match id {
2 => Placeholder,
3 => Basename,
4 => Parent,
5 => NoExt,
6 => BasenameNoExt,
_ => unreachable!(),
}
}
#[cfg(test)]
mod fmt_tests {
use super::*;
use std::path::PathBuf;
#[test]
fn parse_no_placeholders() {
let templ = FormatTemplate::parse("This string has no placeholders");
assert_eq!(
templ,
FormatTemplate::Text("This string has no placeholders".into())
);
}
#[test]
fn parse_only_brace_escapes() {
let templ = FormatTemplate::parse("This string only has escapes like {{ and }}");
assert_eq!(
templ,
FormatTemplate::Text("This string only has escapes like { and }".into())
);
}
#[test]
fn all_placeholders() {
use Token::*;
let templ = FormatTemplate::parse(
"{{path={} \
basename={/} \
parent={//} \
noExt={.} \
basenameNoExt={/.} \
}}",
);
assert_eq!(
templ,
FormatTemplate::Tokens(vec![
Text("{path=".into()),
Placeholder,
Text(" basename=".into()),
Basename,
Text(" parent=".into()),
Parent,
Text(" noExt=".into()),
NoExt,
Text(" basenameNoExt=".into()),
BasenameNoExt,
Text(" }".into()),
])
);
let mut path = PathBuf::new();
path.push("a");
path.push("folder");
path.push("file.txt");
let expanded = templ.generate(&path, Some("/")).into_string().unwrap();
assert_eq!(
expanded,
"{path=a/folder/file.txt \
basename=file.txt \
parent=a/folder \
noExt=a/folder/file \
basenameNoExt=file }"
);
}
}

View File

@ -7,6 +7,7 @@ mod exit_codes;
mod filesystem;
mod filetypes;
mod filter;
mod fmt;
mod output;
mod regex_helper;
mod walk;
@ -299,6 +300,10 @@ fn construct_config(mut opts: Opts, pattern_regexps: &[String]) -> Result<Config
.build()
})
.transpose()?,
format: opts
.format
.as_deref()
.map(crate::fmt::FormatTemplate::parse),
command: command.map(Arc::new),
batch_size: opts.batch_size,
exclude_patterns: opts.exclude.iter().map(|p| String::from("!") + p).collect(),
@ -311,8 +316,7 @@ fn construct_config(mut opts: Opts, pattern_regexps: &[String]) -> Result<Config
path_separator,
actual_path_separator,
max_results: opts.max_results(),
strip_cwd_prefix: (opts.no_search_paths()
&& (opts.strip_cwd_prefix || !(opts.null_separator || has_command))),
strip_cwd_prefix: opts.strip_cwd_prefix(|| !(opts.null_separator || has_command)),
})
}
@ -325,18 +329,22 @@ fn extract_command(opts: &mut Opts, colored_output: bool) -> Result<Option<Comma
if !opts.list_details {
return None;
}
let color_arg = format!("--color={}", opts.color.as_str());
let res = determine_ls_command(&color_arg, colored_output)
let res = determine_ls_command(colored_output)
.map(|cmd| CommandSet::new_batch([cmd]).unwrap());
Some(res)
})
.transpose()
}
fn determine_ls_command(color_arg: &str, colored_output: bool) -> Result<Vec<&str>> {
fn determine_ls_command(colored_output: bool) -> Result<Vec<&'static str>> {
#[allow(unused)]
let gnu_ls = |command_name| {
let color_arg = if colored_output {
"--color=always"
} else {
"--color=never"
};
// Note: we use short options here (instead of --long-options) to support more
// platforms (like BusyBox).
vec![

View File

@ -7,6 +7,7 @@ use crate::config::Config;
use crate::dir_entry::DirEntry;
use crate::error::print_error;
use crate::exit_codes::ExitCode;
use crate::fmt::FormatTemplate;
fn replace_path_separator(path: &str, new_path_separator: &str) -> String {
path.replace(std::path::MAIN_SEPARATOR, new_path_separator)
@ -14,7 +15,10 @@ 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) {
let r = if let Some(ref ls_colors) = config.ls_colors {
// TODO: use format if supplied
let r = if let Some(ref format) = config.format {
print_entry_format(stdout, entry, config, format)
} else if let Some(ref ls_colors) = config.ls_colors {
print_entry_colorized(stdout, entry, config, ls_colors)
} else {
print_entry_uncolorized(stdout, entry, config)
@ -54,6 +58,22 @@ fn print_trailing_slash<W: Write>(
Ok(())
}
// TODO: this function is performance critical and can probably be optimized
fn print_entry_format<W: Write>(
stdout: &mut W,
entry: &DirEntry,
config: &Config,
format: &FormatTemplate,
) -> io::Result<()> {
let separator = if config.null_separator { "\0" } else { "\n" };
let output = format.generate(
entry.stripped_path(config),
config.path_separator.as_deref(),
);
// TODO: support writing raw bytes on unix?
write!(stdout, "{}{}", output.to_string_lossy(), separator)
}
// TODO: this function is performance critical and can probably be optimized
fn print_entry_colorized<W: Write>(
stdout: &mut W,

View File

@ -16,7 +16,7 @@ fn hir_has_uppercase_char(hir: &Hir) -> bool {
use regex_syntax::hir::*;
match hir.kind() {
HirKind::Literal(Literal(bytes)) => match std::str::from_utf8(&bytes) {
HirKind::Literal(Literal(bytes)) => match std::str::from_utf8(bytes) {
Ok(s) => s.chars().any(|c| c.is_uppercase()),
Err(_) => bytes.iter().any(|b| char::from(*b).is_uppercase()),
},

View File

@ -12,7 +12,7 @@ use anyhow::{anyhow, Result};
use crossbeam_channel::{bounded, Receiver, RecvTimeoutError, SendError, Sender};
use etcetera::BaseStrategy;
use ignore::overrides::{Override, OverrideBuilder};
use ignore::{self, WalkBuilder, WalkParallel, WalkState};
use ignore::{WalkBuilder, WalkParallel, WalkState};
use regex::bytes::Regex;
use crate::config::Config;
@ -250,7 +250,7 @@ impl<'a, W: Write> ReceiverBuffer<'a, W> {
/// Output a path.
fn print(&mut self, entry: &DirEntry) -> Result<(), ExitCode> {
output::print_entry(&mut self.stdout, entry, &self.config);
output::print_entry(&mut self.stdout, entry, self.config);
if self.interrupt_flag.load(Ordering::Relaxed) {
// Ignore any errors on flush, because we're about to exit anyway
@ -334,10 +334,6 @@ impl WorkerState {
.map_err(|e| anyhow!("Malformed exclude pattern: {}", e))?;
}
if config.read_vcsignore {
builder.add("!.git/").expect("Invalid exclude pattern");
}
builder
.build()
.map_err(|_| anyhow!("Mismatch in exclude patterns"))
@ -413,7 +409,7 @@ impl WorkerState {
// This will be set to `Some` if the `--exec` argument was supplied.
if let Some(ref cmd) = config.command {
if cmd.in_batch_mode() {
exec::batch(rx.into_iter().flatten(), cmd, &config)
exec::batch(rx.into_iter().flatten(), cmd, config)
} else {
let out_perm = Mutex::new(());
@ -426,7 +422,7 @@ impl WorkerState {
// 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));
.spawn(|| exec::job(rx.into_iter().flatten(), cmd, &out_perm, config));
// Push the handle of the spawned thread into the vector for later joining.
handles.push(handle);

View File

@ -129,7 +129,7 @@ fn normalize_output(s: &str, trim_start: bool, normalize_line: bool) -> String {
.lines()
.map(|line| {
let line = if trim_start { line.trim_start() } else { line };
let line = line.replace('/', &std::path::MAIN_SEPARATOR.to_string());
let line = line.replace('/', std::path::MAIN_SEPARATOR_STR);
if normalize_line {
let mut words: Vec<_> = line.split_whitespace().collect();
words.sort_unstable();

View File

@ -1311,7 +1311,8 @@ fn test_type_executable() {
let te = TestEnv::new(DEFAULT_DIRS, DEFAULT_FILES);
fs::OpenOptions::new()
.create(true)
.create_new(true)
.truncate(true)
.write(true)
.mode(0o777)
.open(te.test_root().join("executable-file.sh"))
@ -1319,6 +1320,7 @@ fn test_type_executable() {
fs::OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.mode(0o645)
.open(te.test_root().join("not-user-executable-file.sh"))
@ -1622,6 +1624,66 @@ fn test_excludes() {
);
}
#[test]
fn format() {
let te = TestEnv::new(DEFAULT_DIRS, DEFAULT_FILES);
te.assert_output(
&["--format", "path={}", "--path-separator=/"],
"path=a.foo
path=e1 e2
path=one
path=one/b.foo
path=one/two
path=one/two/C.Foo2
path=one/two/c.foo
path=one/two/three
path=one/two/three/d.foo
path=one/two/three/directory_foo
path=symlink",
);
te.assert_output(
&["foo", "--format", "noExt={.}", "--path-separator=/"],
"noExt=a
noExt=one/b
noExt=one/two/C
noExt=one/two/c
noExt=one/two/three/d
noExt=one/two/three/directory_foo",
);
te.assert_output(
&["foo", "--format", "basename={/}", "--path-separator=/"],
"basename=a.foo
basename=b.foo
basename=C.Foo2
basename=c.foo
basename=d.foo
basename=directory_foo",
);
te.assert_output(
&["foo", "--format", "name={/.}", "--path-separator=/"],
"name=a
name=b
name=C
name=c
name=d
name=directory_foo",
);
te.assert_output(
&["foo", "--format", "parent={//}", "--path-separator=/"],
"parent=.
parent=one
parent=one/two
parent=one/two
parent=one/two/three
parent=one/two/three",
);
}
/// Shell script execution (--exec)
#[test]
fn test_exec() {
@ -2571,7 +2633,14 @@ fn test_git_dir() {
],
);
te.assert_output(&["--hidden", "foo"], "");
te.assert_output(
&["--hidden", "foo"],
".git/one/foo.a
.git/.foo
.git/a.foo
other_dir/.git/foo1
nested/dir/.git/foo2",
);
te.assert_output(&["--no-ignore", "foo"], "");
te.assert_output(
&["--hidden", "--no-ignore", "foo"],
@ -2590,3 +2659,16 @@ fn test_git_dir() {
nested/dir/.git/foo2",
);
}
#[test]
fn test_gitignore_parent() {
let te = TestEnv::new(&["sub"], &[".abc", "sub/.abc"]);
fs::File::create(te.test_root().join(".gitignore"))
.unwrap()
.write_all(b".abc\n")
.unwrap();
te.assert_output_subdirectory("sub", &["--hidden"], "");
te.assert_output_subdirectory("sub", &["--hidden", "--search-path", "."], "");
}