Repository: sharkdp/fd
Pull request #1155 (2022-11-01 19:56:01)

Preparations for fd 8.5
# Release checklist

## Version bump

- [x] Create a new branch for the required changes for this release.
- [x] Update version in `Cargo.toml`. Run `cargo build` to update `Cargo.lock`.
      Make sure to `git add` the `Cargo.lock` changes as well.
- [x] Find the current min. supported Rust version by running
      `grep '^\s*MIN_SUPPORTED_RUST_VERSION' .github/workflows/CICD.yml`.
- [x] Update the `fd` version and the min. supported Rust version in `README.md`.
- [x] Update `CHANGELOG.md`. Change the heading of the *"Upcoming release"* section
      to the version of this release.

## Pre-release checks and updates

- [x] Install the latest version (`cargo install --locked -f --path .`) and make
      sure that it is available on the `PATH` (`fd --version` should show the
      new version).
- [x] Review `-h`, `--help`, and the `man` page.
- [x] Run `fd -h` and copy the output to the *"Command-line options"* section in
      the README
- [x] Push all changes and wait for CI to succeed (before continuing with the
      next section).
- [x] Optional: manually test the new features and command-line options described
      in the `CHANGELOG.md`.
- [x] Run `cargo publish --dry-run` to make sure that it will succeed later
      (after creating the GitHub release).

## Release

- [x] Create a tag and push it: `git tag vX.Y.Z; git push origin tag vX.Y.Z`.
      This will trigger the deployment via GitHub Actions.
      REMINDER: If your `origin` is a fork, don't forget to push to e.g. `upstream`
      instead.
- [ ] Go to https://github.com/sharkdp/fd/releases/new to create the new
      release. Select the new tag and also use it as the release title. For the
      release notes, copy the corresponding section from `CHANGELOG.md` and
      possibly add additional remarks for package maintainers.
      Publish the release.
- [ ] Check if the binary deployment works (archives and Debian packages should
      appear when the CI run *for the Git tag* has finished).
- [ ] Publish to crates.io by running `cargo publish` in a *clean* repository.
      One way to do this is to clone a fresh copy.

## Post-release

- [ ] Prepare a new *"Upcoming release"* section at the top of `CHANGELOG.md`.
      Put this at the top:
      ```
      # Upcoming release

      ## Features

      ## Bugfixes

      ## Changes

      ## Other

      ```



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ba7c9c9..3f24f68 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-# Upcoming release
+# v8.5.0
 
 ## Features
 
@@ -25,6 +25,8 @@
 - fd now colorizes paths in parallel, significantly improving performance, see #1148 (@tavianator)
 - fd can now avoid `stat` syscalls even when colorizing paths, as long as the color scheme doesn't
   require metadata, see #1148 (@tavianator)
+- The statically linked `musl` versions of `fd` now use `jmalloc`, leading to a significant performance
+  improvement, see #1062 (@tavianator)
 
 ## Other
 
diff --git a/Cargo.lock b/Cargo.lock
index 80c9b24..261e2f1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -308,7 +308,7 @@ dependencies = [
 
 [[package]]
 name = "fd-find"
-version = "8.4.0"
+version = "8.5.0"
 dependencies = [
  "ansi_term",
  "anyhow",
diff --git a/Cargo.toml b/Cargo.toml
index a75a005..6e88966 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,7 +16,7 @@ license = "MIT/Apache-2.0"
 name = "fd-find"
 readme = "README.md"
 repository = "https://github.com/sharkdp/fd"
-version = "8.4.0"
+version = "8.5.0"
 edition= "2021"
 
 [badges.appveyor]
diff --git a/README.md b/README.md
index c2c4b3d..d73f10b 100644
--- a/README.md
+++ b/README.md
@@ -295,39 +295,37 @@ This is the output of `fd -h`. To see the full set of command-line options, use
 also includes a much more detailed help text.
 
 ```
-USAGE:
-    fd [FLAGS/OPTIONS] [<pattern>] [<path>...]
-
-FLAGS:
-    -H, --hidden            Search hidden files and directories
-    -I, --no-ignore         Do not respect .(git|fd)ignore files
-    -s, --case-sensitive    Case-sensitive search (default: smart case)
-    -i, --ignore-case       Case-insensitive search (default: smart case)
-    -g, --glob              Glob-based search (default: regular expression)
-    -a, --absolute-path     Show absolute instead of relative paths
-    -l, --list-details      Use a long listing format with file metadata
-    -L, --follow            Follow symbolic links
-    -p, --full-path         Search full abs. path (default: filename only)
-    -h, --help              Prints help information
-    -V, --version           Prints version information
-
-OPTIONS:
-    -d, --max-depth <depth>            Set maximum search depth (default: none)
-    -t, --type <filetype>...           Filter by type: file (f), directory (d), symlink (l),
-                                       executable (x), empty (e), socket (s), pipe (p)
-    -e, --extension <ext>...           Filter by file extension
-    -x, --exec <cmd>                   Execute a command for each search result
-    -X, --exec-batch <cmd>             Execute a command with all search results at once
-    -E, --exclude <pattern>...         Exclude entries that match the given glob pattern
-    -c, --color <when>                 When to use colors: never, *auto*, always
-    -S, --size <size>...               Limit results based on the size of files
-        --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
-
-ARGS:
-    <pattern>    the search pattern (a regular expression, unless '--glob' is used; optional)
-    <path>...    the root directory for the filesystem search (optional)
+Usage: fd [OPTIONS] [pattern] [path]...
+
+Arguments:
+  [pattern]  the search pattern (a regular expression, unless '--glob' is used; optional)
+  [path]...  the root directories for the filesystem search (optional)
+
+Options:
+  -H, --hidden                     Search hidden files and directories
+  -I, --no-ignore                  Do not respect .(git|fd)ignore files
+  -s, --case-sensitive             Case-sensitive search (default: smart case)
+  -i, --ignore-case                Case-insensitive search (default: smart case)
+  -g, --glob                       Glob-based search (default: regular expression)
+  -a, --absolute-path              Show absolute instead of relative paths
+  -l, --list-details               Use a long listing format with file metadata
+  -L, --follow                     Follow symbolic links
+  -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),
+                                   executable (x), empty (e), socket (s), pipe (p)
+  -e, --extension <ext>            Filter by file extension
+  -S, --size <size>                Limit results based on the size of files
+      --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
+  -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,
+                                   always, never]
+  -h, --help                       Print help information (use `--help` for more detail)
+  -V, --version                    Print version information
 ```
 
 ## Benchmark
@@ -542,7 +540,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_8.4.0_amd64.deb  # adapt version number and architecture
+sudo dpkg -i fd_8.5.0_amd64.deb  # adapt version number and architecture
 ```
 
 ### On Debian
@@ -676,7 +674,7 @@ With Rust's package manager [cargo](https://github.com/rust-lang/cargo), you can
 ```
 cargo install fd-find
 ```
-Note that rust version *1.57.0* or later is required.
+Note that rust version *1.60.0* or later is required.
 
 `make` is also needed for the build.
 
diff --git a/src/cli.rs b/src/cli.rs
index fa69fba..97b0ffe 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -83,8 +83,11 @@ impl clap::Args for Negations {
 
 #[derive(Parser)]
 #[command(
+    name = "fd",
     version,
+    about = "A program to find entries in your filesystem",
     after_long_help = "Bugs can be reported on GitHub: https://github.com/sharkdp/fd/issues",
+    max_term_width = 98,
     args_override_self = true,
     group(ArgGroup::new("execs").args(&["exec", "exec_batch", "list_details"]).conflicts_with_all(&[
             "max_results", "has_results", "count"])),
@@ -275,6 +278,20 @@ pub struct Opts {
         )]
     exact_depth: Option<usize>,
 
+    /// Exclude entries that match the given glob pattern
+    #[arg(
+        long,
+        short = 'E',
+        value_name = "pattern",
+        long_help = "Exclude files/directories that match the given glob pattern. This \
+                         overrides any other ignore logic. Multiple exclude patterns can be \
+                         specified.\n\n\
+                         Examples:\n  \
+                           --exclude '*.pyc'\n  \
+                           --exclude node_modules"
+    )]
+    pub exclude: Vec<String>,
+
     /// Do not traverse into directories that match the search criteria. If
     /// you want to exclude specific directories, use the '--exclude=…' option.
     #[arg(long, hide_short_help = true, conflicts_with_all(&["size", "exact_depth"]),
@@ -336,65 +353,6 @@ pub struct Opts {
     )]
     pub extensions: Option<Vec<String>>,
 
-    #[command(flatten)]
-    pub exec: Exec,
-
-    /// Max number of arguments to run as a batch size with -X
-    #[arg(
-        long,
-        value_name = "size",
-        hide_short_help = true,
-        requires("exec_batch"),
-        value_parser = value_parser!(usize),
-        default_value_t,
-        long_help = "Maximum number of arguments to pass to the command given with -X. \
-                If the number of results is greater than the given size, \
-                the command given with -X is run again with remaining arguments. \
-                A batch size of zero means there is no limit (default), but note \
-                that batching might still happen due to OS restrictions on the \
-                maximum length of command lines.",
-    )]
-    pub batch_size: usize,
-
-    /// Exclude entries that match the given glob pattern
-    #[arg(
-        long,
-        short = 'E',
-        value_name = "pattern",
-        long_help = "Exclude files/directories that match the given glob pattern. This \
-                         overrides any other ignore logic. Multiple exclude patterns can be \
-                         specified.\n\n\
-                         Examples:\n  \
-                           --exclude '*.pyc'\n  \
-                           --exclude node_modules"
-    )]
-    pub exclude: Vec<String>,
-
-    /// Add a custom ignore-file in '.gitignore' format
-    #[arg(
-        long,
-        value_name = "path",
-        hide_short_help = true,
-        long_help = "Add a custom ignore-file in '.gitignore' format. These files have a low precedence."
-    )]
-    pub ignore_file: Vec<PathBuf>,
-
-    /// When to use colors
-    #[arg(
-        long,
-        short = 'c',
-        value_enum,
-        default_value_t = ColorWhen::Auto,
-        value_name = "when",
-        long_help = "Declare when to use color for the pattern match output",
-    )]
-    pub color: ColorWhen,
-
-    /// Set number of threads to use for searching & executing (default: number
-    /// of available CPU cores)
-    #[arg(long, short = 'j', value_name = "num", hide_short_help = true, value_parser = 1..)]
-    pub threads: Option<u32>,
-
     /// Limit results based on the size of files
     #[arg(long, short = 'S', value_parser = SizeFilter::from_string, allow_hyphen_values = true, verbatim_doc_comment, value_name = "size",
         long_help = "Limit results based on the size of files using the format <+-><NUM><UNIT>.\n   \
@@ -416,13 +374,6 @@ pub struct Opts {
         )]
     pub size: Vec<SizeFilter>,
 
-    /// Milliseconds to buffer before streaming search results to console
-    ///
-    /// Amount of time in milliseconds to buffer, before streaming the search
-    /// results to the console.
-    #[arg(long, hide = true, value_parser = parse_millis)]
-    pub max_buffer_time: Option<Duration>,
-
     /// Filter by file modification time (newer than)
     #[arg(
         long,
@@ -456,6 +407,71 @@ pub struct Opts {
     )]
     pub changed_before: Option<String>,
 
+    /// Filter by owning user and/or group
+    #[cfg(unix)]
+    #[arg(long, short = 'o', value_parser = OwnerFilter::from_string, value_name = "user:group",
+        long_help = "Filter files by their user and/or group. \
+                     Format: [(user|uid)][:(group|gid)]. Either side is optional. \
+                     Precede either side with a '!' to exclude files instead.\n\
+                     Examples:\n    \
+                         --owner john\n    \
+                         --owner :students\n    \
+                         --owner '!john:students'"
+        )]
+    pub owner: Option<OwnerFilter>,
+
+    #[command(flatten)]
+    pub exec: Exec,
+
+    /// Max number of arguments to run as a batch size with -X
+    #[arg(
+        long,
+        value_name = "size",
+        hide_short_help = true,
+        requires("exec_batch"),
+        value_parser = value_parser!(usize),
+        default_value_t,
+        long_help = "Maximum number of arguments to pass to the command given with -X. \
+                If the number of results is greater than the given size, \
+                the command given with -X is run again with remaining arguments. \
+                A batch size of zero means there is no limit (default), but note \
+                that batching might still happen due to OS restrictions on the \
+                maximum length of command lines.",
+    )]
+    pub batch_size: usize,
+
+    /// Add a custom ignore-file in '.gitignore' format
+    #[arg(
+        long,
+        value_name = "path",
+        hide_short_help = true,
+        long_help = "Add a custom ignore-file in '.gitignore' format. These files have a low precedence."
+    )]
+    pub ignore_file: Vec<PathBuf>,
+
+    /// When to use colors
+    #[arg(
+        long,
+        short = 'c',
+        value_enum,
+        default_value_t = ColorWhen::Auto,
+        value_name = "when",
+        long_help = "Declare when to use color for the pattern match output",
+    )]
+    pub color: ColorWhen,
+
+    /// Set number of threads to use for searching & executing (default: number
+    /// of available CPU cores)
+    #[arg(long, short = 'j', value_name = "num", hide_short_help = true, value_parser = 1..)]
+    pub threads: Option<u32>,
+
+    /// Milliseconds to buffer before streaming search results to console
+    ///
+    /// Amount of time in milliseconds to buffer, before streaming the search
+    /// results to the console.
+    #[arg(long, hide = true, value_parser = parse_millis)]
+    pub max_buffer_time: Option<Duration>,
+
     /// Limit number of search results
     #[arg(
         long,
@@ -565,18 +581,6 @@ pub struct Opts {
     )]
     pub strip_cwd_prefix: bool,
 
-    /// Filter by owning user and/or group
-    #[cfg(unix)]
-    #[arg(long, short = 'o', value_parser = OwnerFilter::from_string, value_name = "user:group",
-        long_help = "Filter files by their user and/or group. \
-                     Format: [(user|uid)][:(group|gid)]. Either side is optional. \
-                     Precede either side with a '!' to exclude files instead.\n\
-                     Examples:\n    \
-                         --owner john\n    \
-                         --owner :students\n    \
-                         --owner '!john:students'"
-        )]
-    pub owner: Option<OwnerFilter>,
     #[cfg(any(unix, windows))]
     #[arg(long, aliases(&["mount", "xdev"]), hide_short_help = true,
         long_help = "By default, fd will traverse the file system tree as far as other options \


Test patch:
diff --git a/tests/tests.rs b/tests/tests.rs
index 466818e..4d5b151 100644
--- a/tests/tests.rs
+++ b/tests/tests.rs
@@ -1481,7 +1481,7 @@ fn test_exec_batch() {
             &["foo", "--exec-batch", "echo", "{}", "{}"],
             "error: Only one placeholder allowed for batch commands\n\
             \n\
-            Usage: fd-find [OPTIONS] [pattern] [path]...\n\
+            Usage: fd [OPTIONS] [pattern] [path]...\n\
             \n\
             For more information try '--help'\n\
             ",
@@ -1501,7 +1501,7 @@ fn test_exec_batch() {
             &["foo", "--exec-batch", "echo {}"],
             "error: First argument of exec-batch is expected to be a fixed executable\n\
             \n\
-            Usage: fd-find [OPTIONS] [pattern] [path]...\n\
+            Usage: fd [OPTIONS] [pattern] [path]...\n\
             \n\
             For more information try '--help'\n\
             ",