Skip to content

Commit

Permalink
Show line stats in git panel
Browse files Browse the repository at this point in the history
  • Loading branch information
SomeoneToIgnore committed Jan 22, 2025
1 parent f0b5b0b commit 6cb2afb
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 5 deletions.
58 changes: 58 additions & 0 deletions crates/git/src/repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use collections::{HashMap, HashSet};
use git2::BranchType;
use gpui::SharedString;
use parking_lot::Mutex;
use regex::Regex;
use rope::Rope;
use std::borrow::Borrow;
use std::sync::LazyLock;
Expand All @@ -26,6 +27,12 @@ pub struct Branch {
pub unix_timestamp: Option<i64>,
}

#[derive(Clone, Debug, Default)]
pub struct RepositoryStats {
pub insertions: u32,
pub deletions: u32,
}

pub trait GitRepository: Send + Sync {
fn reload_index(&self);

Expand Down Expand Up @@ -63,6 +70,8 @@ pub trait GitRepository: Send + Sync {
fn unstage_paths(&self, paths: &[RepoPath]) -> Result<()>;

fn commit(&self, message: &str) -> Result<()>;

fn repository_stats(&self) -> Result<RepositoryStats>;
}

impl std::fmt::Debug for dyn GitRepository {
Expand Down Expand Up @@ -300,6 +309,51 @@ impl GitRepository for RealGitRepository {
}
Ok(())
}

fn repository_stats(&self) -> Result<RepositoryStats> {
let working_directory = self
.repository
.lock()
.workdir()
.context("failed to read git work directory")?
.to_path_buf();

let cmd = new_std_command(&self.git_binary_path)
.current_dir(&working_directory)
.args(["diff", "--shortstat"])
.output()?;
let stdout = String::from_utf8_lossy(&cmd.stdout);
let stdout = stdout.trim();
if !cmd.status.success() {
let stderr = String::from_utf8_lossy(&cmd.stderr);
return Err(anyhow!(
"Failed to get repository stats. Stdout: '{stdout}', Stderr: '{stderr}'"
));
}

static GIT_STATS_REGEX: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(
r"(\d+) files? changed(?:, (\d+) insertions?\(\+\))?(?:, (\d+) deletions?\(-\))?",
)
.unwrap()
});
let Some(capture) = GIT_STATS_REGEX.captures(stdout) else {
return Ok(RepositoryStats::default());
};

let insertions = capture
.get(2)
.map(|m| m.as_str().parse::<u32>())
.unwrap_or(Ok(0))?;
let deletions = capture
.get(3)
.map(|m| m.as_str().parse::<u32>())
.unwrap_or(Ok(0))?;
Ok(RepositoryStats {
insertions,
deletions,
})
}
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -447,6 +501,10 @@ impl GitRepository for FakeGitRepository {
fn commit(&self, _message: &str) -> Result<()> {
unimplemented!()
}

fn repository_stats(&self) -> Result<RepositoryStats> {
Ok(RepositoryStats::default())
}
}

fn check_path_to_repo_path_errors(relative_file_path: &Path) -> Result<()> {
Expand Down
39 changes: 34 additions & 5 deletions crates/git_ui/src/git_panel.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use crate::git_panel_settings::StatusStyle;
use crate::{git_panel_settings::GitPanelSettings, git_status_icon};
use anyhow::Result;
use anyhow::{Context as _, Result};
use db::kvp::KEY_VALUE_STORE;
use editor::actions::MoveToEnd;
use editor::scroll::ScrollbarAutoHide;
use editor::{Editor, EditorMode, EditorSettings, MultiBuffer, ShowScrollbar};
use futures::channel::mpsc;
use futures::StreamExt as _;
use git::repository::RepoPath;
use git::repository::{RepoPath, RepositoryStats};
use git::status::FileStatus;
use git::{CommitAllChanges, CommitChanges, RevertAll, StageAll, ToggleStaged, UnstageAll};
use gpui::*;
Expand All @@ -17,7 +17,10 @@ use project::{Fs, Project, ProjectPath};
use serde::{Deserialize, Serialize};
use settings::Settings as _;
use std::sync::atomic::{AtomicBool, Ordering};
use std::{collections::HashSet, ops::Range, path::PathBuf, sync::Arc, time::Duration, usize};
use std::{
collections::HashSet, fmt::Write as _, ops::Range, path::PathBuf, sync::Arc, time::Duration,
usize,
};
use theme::ThemeSettings;
use ui::{
prelude::*, Checkbox, Divider, DividerColor, ElevationIndex, Scrollbar, ScrollbarState, Tooltip,
Expand Down Expand Up @@ -97,6 +100,7 @@ pub struct GitPanel {
all_staged: Option<bool>,
width: Option<Pixels>,
err_sender: mpsc::Sender<anyhow::Error>,
repository_stats: Option<RepositoryStats>,
}

fn commit_message_editor(
Expand Down Expand Up @@ -177,6 +181,9 @@ impl GitPanel {
if let Some(this) = handle.upgrade() {
this.update(&mut cx, |this, cx| {
this.update_visible_entries(cx);
this.update_repo_stats()
.context("Git panel update")
.log_err();
let active_repository = this.active_repository.as_ref();
this.commit_editor =
cx.new_view(|cx| commit_message_editor(active_repository, cx));
Expand Down Expand Up @@ -210,6 +217,7 @@ impl GitPanel {
selected_entry: None,
show_scrollbar: false,
hide_scrollbar_task: None,
repository_stats: None,
active_repository,
scroll_handle,
fs,
Expand Down Expand Up @@ -773,6 +781,13 @@ impl GitPanel {
workspace.show_toast(toast, cx);
});
}

pub fn update_repo_stats(&mut self) -> Result<()> {
if let Some(active_repository) = &self.active_repository {
self.repository_stats = Some(active_repository.repository_stats()?);
}
Ok(())
}
}

// GitPanel –– Render
Expand Down Expand Up @@ -806,11 +821,25 @@ impl GitPanel {
.as_ref()
.map_or(0, RepositoryHandle::entry_count);

let changes_string = match entry_count {
let mut changes_string = match entry_count {
0 => "No changes".to_string(),
1 => "1 change".to_string(),
n => format!("{} changes", n),
n => format!("{n} changes"),
};
if let Some(stats) = &self.repository_stats {
if stats.deletions > 0 && stats.insertions > 0 {
write!(
&mut changes_string,
" (+{}/-{})",
stats.insertions, stats.deletions
)
.unwrap();
} else if stats.deletions > 0 {
write!(&mut changes_string, " (-{})", stats.deletions).unwrap();
} else if stats.insertions > 0 {
write!(&mut changes_string, " (+{})", stats.insertions).unwrap();
}
}

// for our use case treat None as false
let all_staged = self.all_staged.unwrap_or(false);
Expand Down
5 changes: 5 additions & 0 deletions crates/project/src/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::{Project, ProjectPath};
use anyhow::anyhow;
use futures::channel::mpsc;
use futures::{SinkExt as _, StreamExt as _};
use git::repository::RepositoryStats;
use git::{
repository::{GitRepository, RepoPath},
status::{GitSummary, TrackedSummary},
Expand Down Expand Up @@ -358,4 +359,8 @@ impl RepositoryHandle {
});
Ok(())
}

pub fn repository_stats(&self) -> anyhow::Result<RepositoryStats> {
self.git_repo.repository_stats()
}
}

0 comments on commit 6cb2afb

Please sign in to comment.