From a95332485b690c9147c3265f272898ce503ad643 Mon Sep 17 00:00:00 2001 From: arcnmx Date: Thu, 30 Dec 2021 13:12:53 -0800 Subject: [PATCH] perf(git_status): tweak exec flags to omit unnecessary info (#3287) * perf(git_status): tweak flags to omit extra info `git status` can be prohibitively slow on some repos, so allow the config to influence what flags are passed to git. For instance, if there is no configured symbol for untracked files, tell git to omit them from its output. This can easily result in a 2~10x speedup in many cases, but requires the user to opt-in to hiding information from the prompt. * docs(git_status): add ignore_submodules option --- docs/config/README.md | 33 +++++++++++++------------- src/configs/git_status.rs | 2 ++ src/modules/git_status.rs | 50 ++++++++++++++++++++++++++------------- 3 files changed, 53 insertions(+), 32 deletions(-) diff --git a/docs/config/README.md b/docs/config/README.md index 1a08e8c7..6113747b 100644 --- a/docs/config/README.md +++ b/docs/config/README.md @@ -1407,22 +1407,23 @@ current directory. ### Options -| Option | Default | Description | -| ------------ | --------------------------------------------- | ----------------------------------- | -| `format` | `'([\[$all_status$ahead_behind\]]($style) )'` | The default format for `git_status` | -| `conflicted` | `"="` | This branch has merge conflicts. | -| `ahead` | `"⇡"` | The format of `ahead` | -| `behind` | `"⇣"` | The format of `behind` | -| `diverged` | `"⇕"` | The format of `diverged` | -| `up_to_date` | `""` | The format of `up_to_date` | -| `untracked` | `"?"` | The format of `untracked` | -| `stashed` | `"$"` | The format of `stashed` | -| `modified` | `"!"` | The format of `modified` | -| `staged` | `"+"` | The format of `staged` | -| `renamed` | `"»"` | The format of `renamed` | -| `deleted` | `"✘"` | The format of `deleted` | -| `style` | `"bold red"` | The style for the module. | -| `disabled` | `false` | Disables the `git_status` module. | +| Option | Default | Description | +| ------------------- | --------------------------------------------- | ----------------------------------- | +| `format` | `'([\[$all_status$ahead_behind\]]($style) )'` | The default format for `git_status` | +| `conflicted` | `"="` | This branch has merge conflicts. | +| `ahead` | `"⇡"` | The format of `ahead` | +| `behind` | `"⇣"` | The format of `behind` | +| `diverged` | `"⇕"` | The format of `diverged` | +| `up_to_date` | `""` | The format of `up_to_date` | +| `untracked` | `"?"` | The format of `untracked` | +| `stashed` | `"$"` | The format of `stashed` | +| `modified` | `"!"` | The format of `modified` | +| `staged` | `"+"` | The format of `staged` | +| `renamed` | `"»"` | The format of `renamed` | +| `deleted` | `"✘"` | The format of `deleted` | +| `style` | `"bold red"` | The style for the module. | +| `ignore_submodules` | `false` | Ignore changes to submodules. | +| `disabled` | `false` | Disables the `git_status` module. | ### Variables diff --git a/src/configs/git_status.rs b/src/configs/git_status.rs index 213ad214..8a014044 100644 --- a/src/configs/git_status.rs +++ b/src/configs/git_status.rs @@ -18,6 +18,7 @@ pub struct GitStatusConfig<'a> { pub modified: &'a str, pub staged: &'a str, pub untracked: &'a str, + pub ignore_submodules: bool, pub disabled: bool, } @@ -37,6 +38,7 @@ impl<'a> Default for GitStatusConfig<'a> { modified: "!", staged: "+", untracked: "?", + ignore_submodules: false, disabled: false, } } diff --git a/src/modules/git_status.rs b/src/modules/git_status.rs index 7a5f20ab..db02083f 100644 --- a/src/modules/git_status.rs +++ b/src/modules/git_status.rs @@ -27,11 +27,11 @@ const ALL_STATUS_FORMAT: &str = "$conflicted$stashed$deleted$renamed$modified$st /// - `»` — A renamed file has been added to the staging area /// - `✘` — A file's deletion has been added to the staging area pub fn module<'a>(context: &'a Context) -> Option> { - let info = Arc::new(GitStatusInfo::load(context)); - let mut module = context.new_module("git_status"); let config: GitStatusConfig = GitStatusConfig::try_load(module.config); + let info = Arc::new(GitStatusInfo::load(context, config.clone())); + //Return None if not in git repository context.get_repo().ok()?; @@ -116,14 +116,16 @@ pub fn module<'a>(context: &'a Context) -> Option> { struct GitStatusInfo<'a> { context: &'a Context<'a>, + config: GitStatusConfig<'a>, repo_status: OnceCell>, stashed_count: OnceCell>, } impl<'a> GitStatusInfo<'a> { - pub fn load(context: &'a Context) -> Self { + pub fn load(context: &'a Context, config: GitStatusConfig<'a>) -> Self { Self { context, + config, repo_status: OnceCell::new(), stashed_count: OnceCell::new(), } @@ -135,7 +137,7 @@ impl<'a> GitStatusInfo<'a> { pub fn get_repo_status(&self) -> &Option { self.repo_status - .get_or_init(|| match get_repo_status(self.context) { + .get_or_init(|| match get_repo_status(self.context, &self.config) { Some(repo_status) => Some(repo_status), None => { log::debug!("get_repo_status: git status execution failed"); @@ -181,21 +183,37 @@ impl<'a> GitStatusInfo<'a> { } /// Gets the number of files in various git states (staged, modified, deleted, etc...) -fn get_repo_status(context: &Context) -> Option { +fn get_repo_status(context: &Context, config: &GitStatusConfig) -> Option { log::debug!("New repo status created"); let mut repo_status = RepoStatus::default(); - let status_output = context.exec_cmd( - "git", - &[ - OsStr::new("-C"), - context.current_dir.as_os_str(), - OsStr::new("--no-optional-locks"), - OsStr::new("status"), - OsStr::new("--porcelain=2"), - OsStr::new("--branch"), - ], - )?; + let mut args = vec![ + OsStr::new("-C"), + context.current_dir.as_os_str(), + OsStr::new("--no-optional-locks"), + OsStr::new("status"), + OsStr::new("--porcelain=2"), + ]; + + // for performance reasons, only pass flags if necessary... + let has_ahead_behind = !config.ahead.is_empty() || !config.behind.is_empty(); + let has_up_to_date_diverged = !config.up_to_date.is_empty() || !config.diverged.is_empty(); + if has_ahead_behind || has_up_to_date_diverged { + args.push(OsStr::new("--branch")); + } + + // ... and add flags that omit information the user doesn't want + let has_untracked = !config.untracked.is_empty(); + if !has_untracked { + args.push(OsStr::new("--untracked-files=no")); + } + if config.ignore_submodules { + args.push(OsStr::new("--ignore-submodules=dirty")); + } else if !has_untracked { + args.push(OsStr::new("--ignore-submodules=untracked")); + } + + let status_output = context.exec_cmd("git", &args)?; let statuses = status_output.stdout.lines(); statuses.for_each(|status| {