git_ui: Show author name on commits in branch picker (#36812)

Lev Zakharov and Conrad Irwin created

See related discussion
https://github.com/zed-industries/zed/discussions/36511.

<img alt="zed"
src="https://github.com/user-attachments/assets/da7fc30d-2504-48f4-a392-7c8d5cd8acb1"
/>

Release Notes:

- Added option to show the author name in a branch picker commit
information

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>

Change summary

assets/settings/default.json           |  4 +++
crates/git/src/repository.rs           |  7 +++++
crates/git_ui/src/blame_ui.rs          |  7 +++++
crates/git_ui/src/branch_picker.rs     | 23 ++++++++++++++++---
crates/git_ui/src/commit_tooltip.rs    |  1 
crates/git_ui/src/git_panel.rs         |  2 +
crates/git_ui/src/project_diff.rs      |  1 
crates/project/src/git_store.rs        |  2 +
crates/project/src/project_settings.rs | 22 ++++++++++++++++++
crates/proto/proto/git.proto           |  1 
docs/src/configuring-zed.md            | 33 ++++++++++++++++++++++++++++
11 files changed, 98 insertions(+), 5 deletions(-)

Detailed changes

assets/settings/default.json 🔗

@@ -1199,6 +1199,10 @@
       // The minimum column number to show the inline blame information at
       "min_column": 0
     },
+    // Control which information is shown in the branch picker.
+    "branch_picker": {
+      "show_author_name": true
+    },
     // How git hunks are displayed visually in the editor.
     // This setting can take two values:
     //

crates/git/src/repository.rs 🔗

@@ -150,6 +150,7 @@ pub struct CommitSummary {
     pub subject: SharedString,
     /// This is a unix timestamp
     pub commit_timestamp: i64,
+    pub author_name: SharedString,
     pub has_parent: bool,
 }
 
@@ -987,6 +988,7 @@ impl GitRepository for RealGitRepository {
                     "%(upstream)",
                     "%(upstream:track)",
                     "%(committerdate:unix)",
+                    "%(authorname)",
                     "%(contents:subject)",
                 ]
                 .join("%00");
@@ -2022,6 +2024,7 @@ fn parse_branch_input(input: &str) -> Result<Vec<Branch>> {
         let upstream_name = fields.next().context("no upstream")?.to_string();
         let upstream_tracking = parse_upstream_track(fields.next().context("no upstream:track")?)?;
         let commiterdate = fields.next().context("no committerdate")?.parse::<i64>()?;
+        let author_name = fields.next().context("no authorname")?.to_string().into();
         let subject: SharedString = fields
             .next()
             .context("no contents:subject")?
@@ -2035,6 +2038,7 @@ fn parse_branch_input(input: &str) -> Result<Vec<Branch>> {
                 sha: head_sha,
                 subject,
                 commit_timestamp: commiterdate,
+                author_name: author_name,
                 has_parent: !parent_sha.is_empty(),
             }),
             upstream: if upstream_name.is_empty() {
@@ -2345,7 +2349,7 @@ mod tests {
     fn test_branches_parsing() {
         // suppress "help: octal escapes are not supported, `\0` is always null"
         #[allow(clippy::octal_escapes)]
-        let input = "*\0060964da10574cd9bf06463a53bf6e0769c5c45e\0\0refs/heads/zed-patches\0refs/remotes/origin/zed-patches\0\01733187470\0generated protobuf\n";
+        let input = "*\0060964da10574cd9bf06463a53bf6e0769c5c45e\0\0refs/heads/zed-patches\0refs/remotes/origin/zed-patches\0\01733187470\0John Doe\0generated protobuf\n";
         assert_eq!(
             parse_branch_input(input).unwrap(),
             vec![Branch {
@@ -2362,6 +2366,7 @@ mod tests {
                     sha: "060964da10574cd9bf06463a53bf6e0769c5c45e".into(),
                     subject: "generated protobuf".into(),
                     commit_timestamp: 1733187470,
+                    author_name: SharedString::new("John Doe"),
                     has_parent: false,
                 })
             }]

crates/git_ui/src/blame_ui.rs 🔗

@@ -90,6 +90,11 @@ impl BlameRenderer for GitBlameRenderer {
                                 sha: blame_entry.sha.to_string().into(),
                                 subject: blame_entry.summary.clone().unwrap_or_default().into(),
                                 commit_timestamp: blame_entry.committer_time.unwrap_or_default(),
+                                author_name: blame_entry
+                                    .committer_name
+                                    .clone()
+                                    .unwrap_or_default()
+                                    .into(),
                                 has_parent: true,
                             },
                             repository.downgrade(),
@@ -229,6 +234,7 @@ impl BlameRenderer for GitBlameRenderer {
                         .into()
                 }),
             commit_timestamp: commit_details.commit_time.unix_timestamp(),
+            author_name: commit_details.author_name.clone(),
             has_parent: false,
         };
 
@@ -374,6 +380,7 @@ impl BlameRenderer for GitBlameRenderer {
                 sha: blame_entry.sha.to_string().into(),
                 subject: blame_entry.summary.clone().unwrap_or_default().into(),
                 commit_timestamp: blame_entry.committer_time.unwrap_or_default(),
+                author_name: blame_entry.committer_name.unwrap_or_default().into(),
                 has_parent: true,
             },
             repository.downgrade(),

crates/git_ui/src/branch_picker.rs 🔗

@@ -10,6 +10,8 @@ use gpui::{
 };
 use picker::{Picker, PickerDelegate, PickerEditorPosition};
 use project::git_store::Repository;
+use project::project_settings::ProjectSettings;
+use settings::Settings;
 use std::sync::Arc;
 use time::OffsetDateTime;
 use time_format::format_local_timestamp;
@@ -454,7 +456,7 @@ impl PickerDelegate for BranchListDelegate {
     ) -> Option<Self::ListItem> {
         let entry = &self.matches[ix];
 
-        let (commit_time, subject) = entry
+        let (commit_time, author_name, subject) = entry
             .branch
             .most_recent_commit
             .as_ref()
@@ -467,9 +469,10 @@ impl PickerDelegate for BranchListDelegate {
                     OffsetDateTime::now_utc(),
                     time_format::TimestampFormat::Relative,
                 );
-                (Some(formatted_time), Some(subject))
+                let author = commit.author_name.clone();
+                (Some(formatted_time), Some(author), Some(subject))
             })
-            .unwrap_or_else(|| (None, None));
+            .unwrap_or_else(|| (None, None, None));
 
         let icon = if let Some(default_branch) = self.default_branch.clone()
             && entry.is_new
@@ -550,7 +553,19 @@ impl PickerDelegate for BranchListDelegate {
                                         "based off the current branch".to_string()
                                     }
                                 } else {
-                                    subject.unwrap_or("no commits found".into()).to_string()
+                                    let show_author_name = ProjectSettings::get_global(cx)
+                                        .git
+                                        .branch_picker
+                                        .unwrap_or_default()
+                                        .show_author_name;
+
+                                    subject.map_or("no commits found".into(), |subject| {
+                                        if show_author_name && author_name.is_some() {
+                                            format!("{} • {}", author_name.unwrap(), subject)
+                                        } else {
+                                            subject.to_string()
+                                        }
+                                    })
                                 };
                                 Label::new(message)
                                     .size(LabelSize::Small)

crates/git_ui/src/commit_tooltip.rs 🔗

@@ -229,6 +229,7 @@ impl Render for CommitTooltip {
                         .into()
                 }),
             commit_timestamp: self.commit.commit_time.unix_timestamp(),
+            author_name: self.commit.author_name.clone(),
             has_parent: false,
         };
 

crates/git_ui/src/git_panel.rs 🔗

@@ -5017,6 +5017,7 @@ impl Component for PanelRepoFooter {
                     sha: "abc123".into(),
                     subject: "Modify stuff".into(),
                     commit_timestamp: 1710932954,
+                    author_name: "John Doe".into(),
                     has_parent: true,
                 }),
             }
@@ -5034,6 +5035,7 @@ impl Component for PanelRepoFooter {
                     sha: "abc123".into(),
                     subject: "Modify stuff".into(),
                     commit_timestamp: 1710932954,
+                    author_name: "John Doe".into(),
                     has_parent: true,
                 }),
             }

crates/git_ui/src/project_diff.rs 🔗

@@ -1220,6 +1220,7 @@ mod preview {
                         sha: "abc123".into(),
                         subject: "Modify stuff".into(),
                         commit_timestamp: 1710932954,
+                        author_name: "John Doe".into(),
                         has_parent: true,
                     }),
                 }

crates/project/src/git_store.rs 🔗

@@ -4752,6 +4752,7 @@ fn branch_to_proto(branch: &git::repository::Branch) -> proto::Branch {
                 sha: commit.sha.to_string(),
                 subject: commit.subject.to_string(),
                 commit_timestamp: commit.commit_timestamp,
+                author_name: commit.author_name.to_string(),
             }),
     }
 }
@@ -4781,6 +4782,7 @@ fn proto_to_branch(proto: &proto::Branch) -> git::repository::Branch {
                 sha: commit.sha.to_string().into(),
                 subject: commit.subject.to_string().into(),
                 commit_timestamp: commit.commit_timestamp,
+                author_name: commit.author_name.to_string().into(),
                 has_parent: true,
             }
         }),

crates/project/src/project_settings.rs 🔗

@@ -405,6 +405,10 @@ pub struct GitSettings {
     ///
     /// Default: on
     pub inline_blame: Option<InlineBlameSettings>,
+    /// Which information to show in the branch picker.
+    ///
+    /// Default: on
+    pub branch_picker: Option<BranchPickerSettings>,
     /// How hunks are displayed visually in the editor.
     ///
     /// Default: staged_hollow
@@ -509,6 +513,24 @@ impl Default for InlineBlameSettings {
     }
 }
 
+#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub struct BranchPickerSettings {
+    /// Whether to show author name as part of the commit information.
+    ///
+    /// Default: false
+    #[serde(default)]
+    pub show_author_name: bool,
+}
+
+impl Default for BranchPickerSettings {
+    fn default() -> Self {
+        Self {
+            show_author_name: true,
+        }
+    }
+}
+
 #[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, Hash)]
 pub struct BinarySettings {
     pub path: Option<String>,

crates/proto/proto/git.proto 🔗

@@ -95,6 +95,7 @@ message CommitSummary {
     string sha = 1;
     string subject = 2;
     int64 commit_timestamp = 3;
+    string author_name = 4;
 }
 
 message GitBranches {

docs/src/configuring-zed.md 🔗

@@ -2023,6 +2023,9 @@ To interpret all `.c` files as C++, files called `MyLockFile` as TOML and files
     "inline_blame": {
       "enabled": true
     },
+    "branch_picker": {
+      "show_author_name": true
+    },
     "hunk_style": "staged_hollow"
   }
 }
@@ -2154,6 +2157,36 @@ Example:
 }
 ```
 
+### Branch Picker
+
+- Description: Configuration related to the branch picker.
+- Setting: `branch_picker`
+- Default:
+
+```json
+{
+  "git": {
+    "branch_picker": {
+      "show_author_name": false
+    }
+  }
+}
+```
+
+**Options**
+
+1. Show the author name in the branch picker:
+
+```json
+{
+  "git": {
+    "branch_picker": {
+      "show_author_name": true
+    }
+  }
+}
+```
+
 ### Hunk Style
 
 - Description: What styling we should use for the diff hunks.