Detailed changes
@@ -117,6 +117,7 @@ CREATE TABLE "project_repositories" (
"is_deleted" BOOL NOT NULL,
"current_merge_conflicts" VARCHAR,
"branch_summary" VARCHAR,
+ "head_commit_details" VARCHAR,
PRIMARY KEY (project_id, id)
);
@@ -348,9 +348,10 @@ impl Database {
.unwrap(),
)),
- // Old clients do not use abs path or entry ids.
+ // Old clients do not use abs path, entry ids or head_commit_details.
abs_path: ActiveValue::set(String::new()),
entry_ids: ActiveValue::set("[]".into()),
+ head_commit_details: ActiveValue::set(None),
}
}),
)
@@ -490,6 +491,12 @@ impl Database {
.as_ref()
.map(|summary| serde_json::to_string(summary).unwrap()),
),
+ head_commit_details: ActiveValue::Set(
+ update
+ .head_commit_details
+ .as_ref()
+ .map(|details| serde_json::to_string(details).unwrap()),
+ ),
current_merge_conflicts: ActiveValue::Set(Some(
serde_json::to_string(&update.current_merge_conflicts).unwrap(),
)),
@@ -505,6 +512,7 @@ impl Database {
project_repository::Column::EntryIds,
project_repository::Column::AbsPath,
project_repository::Column::CurrentMergeConflicts,
+ project_repository::Column::HeadCommitDetails,
])
.to_owned(),
)
@@ -928,6 +936,13 @@ impl Database {
.transpose()?
.unwrap_or_default();
+ let head_commit_details = db_repository_entry
+ .head_commit_details
+ .as_ref()
+ .map(|head_commit_details| serde_json::from_str(&head_commit_details))
+ .transpose()?
+ .unwrap_or_default();
+
let entry_ids = serde_json::from_str(&db_repository_entry.entry_ids)
.context("failed to deserialize repository's entry ids")?;
@@ -954,6 +969,7 @@ impl Database {
removed_statuses: Vec::new(),
current_merge_conflicts,
branch_summary,
+ head_commit_details,
scan_id: db_repository_entry.scan_id as u64,
is_last_update: true,
});
@@ -755,6 +755,13 @@ impl Database {
.transpose()?
.unwrap_or_default();
+ let head_commit_details = db_repository
+ .head_commit_details
+ .as_ref()
+ .map(|head_commit_details| serde_json::from_str(&head_commit_details))
+ .transpose()?
+ .unwrap_or_default();
+
let entry_ids = serde_json::from_str(&db_repository.entry_ids)
.context("failed to deserialize repository's entry ids")?;
@@ -778,6 +785,7 @@ impl Database {
removed_statuses,
current_merge_conflicts,
branch_summary,
+ head_commit_details,
project_id: project_id.to_proto(),
id: db_repository.id as u64,
abs_path: db_repository.abs_path,
@@ -18,6 +18,8 @@ pub struct Model {
pub current_merge_conflicts: Option<String>,
// A JSON object representing the current Branch values
pub branch_summary: Option<String>,
+ // A JSON object representing the current Head commit values
+ pub head_commit_details: Option<String>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
@@ -2938,6 +2938,7 @@ impl GitPanel {
let expand_tooltip_focus_handle = editor_focus_handle.clone();
let branch = active_repository.read(cx).branch.clone();
+ let head_commit = active_repository.read(cx).head_commit.clone();
let footer_size = px(32.);
let gap = px(9.0);
@@ -2966,6 +2967,7 @@ impl GitPanel {
.child(PanelRepoFooter::new(
display_name,
branch,
+ head_commit,
Some(git_panel.clone()),
))
.child(
@@ -4291,6 +4293,8 @@ impl Render for GitPanelMessageTooltip {
pub struct PanelRepoFooter {
active_repository: SharedString,
branch: Option<Branch>,
+ head_commit: Option<CommitDetails>,
+
// Getting a GitPanel in previews will be difficult.
//
// For now just take an option here, and we won't bind handlers to buttons in previews.
@@ -4301,11 +4305,13 @@ impl PanelRepoFooter {
pub fn new(
active_repository: SharedString,
branch: Option<Branch>,
+ head_commit: Option<CommitDetails>,
git_panel: Option<Entity<GitPanel>>,
) -> Self {
Self {
active_repository,
branch,
+ head_commit,
git_panel,
}
}
@@ -4314,6 +4320,7 @@ impl PanelRepoFooter {
Self {
active_repository,
branch,
+ head_commit: None,
git_panel: None,
}
}
@@ -4339,11 +4346,26 @@ impl RenderOnce for PanelRepoFooter {
const MAX_BRANCH_LEN: usize = 16;
const MAX_REPO_LEN: usize = 16;
const LABEL_CHARACTER_BUDGET: usize = MAX_BRANCH_LEN + MAX_REPO_LEN;
+ const MAX_SHORT_SHA_LEN: usize = 8;
- let branch = self.branch.clone();
- let branch_name = branch
+ let branch_name = self
+ .branch
.as_ref()
- .map_or(" (no branch)".into(), |branch| branch.name.clone());
+ .map(|branch| branch.name.clone())
+ .or_else(|| {
+ self.head_commit.as_ref().map(|commit| {
+ SharedString::from(
+ commit
+ .sha
+ .chars()
+ .take(MAX_SHORT_SHA_LEN)
+ .collect::<String>(),
+ )
+ })
+ })
+ .unwrap_or_else(|| SharedString::from(" (no branch)"));
+ let show_separator = self.branch.is_some() || self.head_commit.is_some();
+
let active_repo_name = self.active_repository.clone();
let branch_actual_len = branch_name.len();
@@ -4449,7 +4471,7 @@ impl RenderOnce for PanelRepoFooter {
),
)
.child(repo_selector)
- .when_some(branch.clone(), |this, _| {
+ .when(show_separator, |this| {
this.child(
div()
.text_color(cx.theme().colors().text_muted)
@@ -231,6 +231,7 @@ pub struct RepositorySnapshot {
pub statuses_by_path: SumTree<StatusEntry>,
pub work_directory_abs_path: Arc<Path>,
pub branch: Option<Branch>,
+ pub head_commit: Option<CommitDetails>,
pub merge_conflicts: TreeSet<RepoPath>,
pub merge_head_shas: Vec<SharedString>,
pub scan_id: u64,
@@ -2426,6 +2427,7 @@ impl RepositorySnapshot {
statuses_by_path: Default::default(),
work_directory_abs_path,
branch: None,
+ head_commit: None,
merge_conflicts: Default::default(),
merge_head_shas: Default::default(),
scan_id: 0,
@@ -2435,6 +2437,7 @@ impl RepositorySnapshot {
fn initial_update(&self, project_id: u64) -> proto::UpdateRepository {
proto::UpdateRepository {
branch_summary: self.branch.as_ref().map(branch_to_proto),
+ head_commit_details: self.head_commit.as_ref().map(commit_details_to_proto),
updated_statuses: self
.statuses_by_path
.iter()
@@ -2499,6 +2502,7 @@ impl RepositorySnapshot {
proto::UpdateRepository {
branch_summary: self.branch.as_ref().map(branch_to_proto),
+ head_commit_details: self.head_commit.as_ref().map(commit_details_to_proto),
updated_statuses,
removed_statuses,
current_merge_conflicts: self
@@ -3748,6 +3752,11 @@ impl Repository {
.map(|path| RepoPath(Path::new(&path).into())),
);
self.snapshot.branch = update.branch_summary.as_ref().map(proto_to_branch);
+ self.snapshot.head_commit = update
+ .head_commit_details
+ .as_ref()
+ .map(proto_to_commit_details);
+
self.snapshot.merge_conflicts = conflicted_paths;
let edits = update
@@ -4322,6 +4331,26 @@ fn proto_to_branch(proto: &proto::Branch) -> git::repository::Branch {
}
}
+fn commit_details_to_proto(commit: &CommitDetails) -> proto::GitCommitDetails {
+ proto::GitCommitDetails {
+ sha: commit.sha.to_string(),
+ message: commit.message.to_string(),
+ commit_timestamp: commit.commit_timestamp,
+ author_email: commit.author_email.to_string(),
+ author_name: commit.author_name.to_string(),
+ }
+}
+
+fn proto_to_commit_details(proto: &proto::GitCommitDetails) -> CommitDetails {
+ CommitDetails {
+ sha: proto.sha.clone().into(),
+ message: proto.message.clone().into(),
+ commit_timestamp: proto.commit_timestamp,
+ author_email: proto.author_email.clone().into(),
+ author_name: proto.author_name.clone().into(),
+ }
+}
+
async fn compute_snapshot(
id: RepositoryId,
work_directory_abs_path: Arc<Path>,
@@ -4377,6 +4406,12 @@ async fn compute_snapshot(
events.push(RepositoryEvent::MergeHeadsChanged);
}
+ // Useful when branch is None in detached head state
+ let head_commit = match backend.head_sha() {
+ Some(head_sha) => backend.show(head_sha).await.ok(),
+ None => None,
+ };
+
let snapshot = RepositorySnapshot {
id,
merge_message,
@@ -4384,6 +4419,7 @@ async fn compute_snapshot(
work_directory_abs_path,
scan_id: prev_snapshot.scan_id + 1,
branch,
+ head_commit,
merge_conflicts,
merge_head_shas,
};
@@ -120,6 +120,7 @@ message UpdateRepository {
repeated string current_merge_conflicts = 8;
uint64 scan_id = 9;
bool is_last_update = 10;
+ optional GitCommitDetails head_commit_details = 11;
}
message RemoveRepository {
@@ -46,6 +46,7 @@ pub use stories::*;
const MAX_PROJECT_NAME_LENGTH: usize = 40;
const MAX_BRANCH_NAME_LENGTH: usize = 40;
+const MAX_SHORT_SHA_LENGTH: usize = 8;
const BOOK_ONBOARDING: &str = "https://dub.sh/zed-c-onboarding";
@@ -513,8 +514,23 @@ impl TitleBar {
pub fn render_project_branch(&self, cx: &mut Context<Self>) -> Option<impl IntoElement> {
let repository = self.project.read(cx).active_repository(cx)?;
let workspace = self.workspace.upgrade()?;
- let branch_name = repository.read(cx).branch.as_ref()?.name.clone();
- let branch_name = util::truncate_and_trailoff(&branch_name, MAX_BRANCH_NAME_LENGTH);
+ let branch_name = {
+ let repo = repository.read(cx);
+ repo.branch
+ .as_ref()
+ .map(|branch| branch.name.clone())
+ .map(|name| util::truncate_and_trailoff(&name, MAX_BRANCH_NAME_LENGTH))
+ .or_else(|| {
+ repo.head_commit.as_ref().map(|commit| {
+ commit
+ .sha
+ .chars()
+ .take(MAX_SHORT_SHA_LENGTH)
+ .collect::<String>()
+ })
+ })
+ }?;
+
Some(
Button::new("project_branch_trigger", branch_name)
.color(Color::Muted)