diff --git a/docs/config/README.md b/docs/config/README.md index c94ae348..53d3f8a5 100644 --- a/docs/config/README.md +++ b/docs/config/README.md @@ -1043,17 +1043,19 @@ truncation_symbol = "" ## Git Commit -The `git_commit` module shows the current commit hash of the repo in your current directory. +The `git_commit` module shows the current commit hash and also the tag (if any) of the repo in your current directory. ### Options -| Option | Default | Description | -| -------------------- | ------------------------ | ----------------------------------------------------- | -| `commit_hash_length` | `7` | The length of the displayed git commit hash. | -| `format` | `'[\($hash\)]($style) '` | The format for the module. | -| `style` | `"bold green"` | The style for the module. | -| `only_detached` | `true` | Only show git commit hash when in detached HEAD state | -| `disabled` | `false` | Disables the `git_commit` module. | +| Option | Default | Description | +| -------------------- | ---------------------------------------------- | ----------------------------------------------------- | +| `commit_hash_length` | `7` | The length of the displayed git commit hash. | +| `format` | `"[\\($hash\\)]($style) [\\($tag\\)]($style)"` | The format for the module. | +| `style` | `"bold green"` | The style for the module. | +| `only_detached` | `true` | Only show git commit hash when in detached HEAD state | +| `tag_disabled` | `true` | Disables showing tag info in `git_commit` module. | +| `tag_symbol` | `"🏷 "` | Tag symbol prefixing the info shown | +| `disabled` | `false` | Disables the `git_commit` module. | ### Variables @@ -1071,6 +1073,7 @@ The `git_commit` module shows the current commit hash of the repo in your curren [git_commit] commit_hash_length = 4 +tag_symbol = "🔖 " ``` ## Git State diff --git a/src/configs/git_commit.rs b/src/configs/git_commit.rs index 4a9ec22a..0276db3d 100644 --- a/src/configs/git_commit.rs +++ b/src/configs/git_commit.rs @@ -9,6 +9,8 @@ pub struct GitCommitConfig<'a> { pub style: &'a str, pub only_detached: bool, pub disabled: bool, + pub tag_symbol: &'a str, + pub tag_disabled: bool, } impl<'a> RootModuleConfig<'a> for GitCommitConfig<'a> { @@ -16,10 +18,12 @@ impl<'a> RootModuleConfig<'a> for GitCommitConfig<'a> { GitCommitConfig { // be consistent with git by default, which has DEFAULT_ABBREV set to 7 commit_hash_length: 7, - format: "[\\($hash\\)]($style) ", + format: "[\\($hash$tag\\)]($style) ", style: "green bold", only_detached: true, disabled: false, + tag_symbol: "🏷 ", + tag_disabled: true, } } } diff --git a/src/modules/git_commit.rs b/src/modules/git_commit.rs index ae80ee97..3864bc07 100644 --- a/src/modules/git_commit.rs +++ b/src/modules/git_commit.rs @@ -24,7 +24,9 @@ pub fn module<'a>(context: &'a Context) -> Option> { let head_commit = git_head.peel_to_commit().ok()?; let commit_oid = head_commit.id(); - let parsed = StringFormatter::new(config.format).and_then(|formatter| { + let mut parsed; + + parsed = StringFormatter::new(config.format).and_then(|formatter| { formatter .map_style(|variable| match variable { "style" => Some(Ok(config.style)), @@ -40,6 +42,48 @@ pub fn module<'a>(context: &'a Context) -> Option> { .parse(None) }); + if !config.tag_disabled { + // Let's get repo tags names + let tag_names = git_repo.tag_names(None).ok()?; + let tag_and_refs = tag_names.iter().flat_map(|name| { + let full_tag = format!("refs/tags/{}", name.unwrap()); + git_repo + .find_reference(&full_tag) + .map(|reference| (String::from(name.unwrap()), reference)) + }); + + let mut tag_name = String::new(); + // Let's check if HEAD has some tag. If several, only gets first... + for (name, reference) in tag_and_refs { + if commit_oid == reference.peel_to_commit().ok()?.id() { + tag_name = name; + break; + } + } + // If we have tag... + if !tag_name.is_empty() { + parsed = StringFormatter::new(config.format).and_then(|formatter| { + formatter + .map_style(|variable| match variable { + "style" => Some(Ok(config.style)), + _ => None, + }) + .map(|variable| match variable { + "hash" => Some(Ok(id_to_hex_abbrev( + commit_oid.as_bytes(), + config.commit_hash_length, + ))), + _ => None, + }) + .map(|variable| match variable { + "tag" => Some(Ok(format!(" {}{}", &config.tag_symbol, &tag_name))), + _ => None, + }) + .parse(None) + }); + } + }; + module.set_segments(match parsed { Ok(segments) => segments, Err(error) => { @@ -196,4 +240,99 @@ mod tests { assert_eq!(expected, actual); repo_dir.close() } + + #[test] + fn test_render_commit_hash_with_tag_enabled() -> io::Result<()> { + let repo_dir = fixture_repo(FixtureProvider::GIT)?; + + let mut git_commit = Command::new("git") + .args(&["rev-parse", "HEAD"]) + .current_dir(&repo_dir.path()) + .output()? + .stdout; + git_commit.truncate(7); + let commit_output = str::from_utf8(&git_commit).unwrap().trim(); + + let git_tag = Command::new("git") + .args(&["describe", "--tags", "--exact-match", "HEAD"]) + .current_dir(&repo_dir.path()) + .output()? + .stdout; + let tag_output = str::from_utf8(&git_tag).unwrap().trim(); + + let expected_output = format!("{} {}", commit_output, tag_output); + + let actual = ModuleRenderer::new("git_commit") + .config(toml::toml! { + [git_commit] + only_detached = false + tag_disabled = false + tag_symbol = "" + }) + .path(&repo_dir.path()) + .collect(); + + let expected = Some(format!( + "{} ", + Color::Green + .bold() + .paint(format!("({})", expected_output.trim())) + .to_string() + )); + + assert_eq!(expected, actual); + Ok(()) + } + + #[test] + fn test_render_commit_hash_only_detached_on_detached_with_tag_enabled() -> io::Result<()> { + let repo_dir = fixture_repo(FixtureProvider::GIT)?; + + Command::new("git") + .args(&["checkout", "@~1"]) + .current_dir(&repo_dir.path()) + .output()?; + + Command::new("git") + .args(&["tag", "tagOnDetached", "-m", "Testing tags on detached"]) + .current_dir(&repo_dir.path()) + .output()?; + + let mut git_commit = Command::new("git") + .args(&["rev-parse", "HEAD"]) + .current_dir(&repo_dir.path()) + .output()? + .stdout; + git_commit.truncate(7); + let commit_output = str::from_utf8(&git_commit).unwrap().trim(); + + let git_tag = Command::new("git") + .args(&["describe", "--tags", "--exact-match", "HEAD"]) + .current_dir(&repo_dir.path()) + .output()? + .stdout; + let tag_output = str::from_utf8(&git_tag).unwrap().trim(); + + let expected_output = format!("{} {}", commit_output, tag_output); + + let actual = ModuleRenderer::new("git_commit") + .config(toml::toml! { + [git_commit] + tag_disabled = false + tag_symbol = "" + }) + .path(&repo_dir.path()) + .collect(); + + let expected = Some(format!( + "{} ", + Color::Green + .bold() + .paint(format!("({})", expected_output.trim())) + .to_string() + )); + + assert_eq!(expected, actual); + Ok(()) + } } diff --git a/src/modules/mod.rs b/src/modules/mod.rs index aedebced..28cf2882 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -155,7 +155,7 @@ pub fn description(module: &str) -> &'static str { "erlang" => "Current OTP version", "gcloud" => "The current GCP client configuration", "git_branch" => "The active branch of the repo in your current directory", - "git_commit" => "The active commit of the repo in your current directory", + "git_commit" => "The active commit (and tag if any) of the repo in your current directory", "git_state" => "The current git operation, and it's progress", "git_status" => "Symbol representing the state of the repo", "golang" => "The currently installed version of Golang",