diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs index d9bfc0dcfba898f27c8bfff6a3e03fcc7d84739c..31f4a5d4c45eba420594c704a8000cd3e67b0b35 100644 --- a/crates/git/src/repository.rs +++ b/crates/git/src/repository.rs @@ -116,6 +116,7 @@ pub struct CommitSummary { pub subject: SharedString, /// This is a unix timestamp pub commit_timestamp: i64, + pub has_parent: bool, } #[derive(Clone, Debug, Hash, PartialEq, Eq)] @@ -536,6 +537,7 @@ impl GitRepository for RealGitRepository { let fields = [ "%(HEAD)", "%(objectname)", + "%(parent)", "%(refname)", "%(upstream)", "%(upstream:track)", @@ -1292,6 +1294,7 @@ fn parse_branch_input(input: &str) -> Result> { let mut fields = line.split('\x00'); let is_current_branch = fields.next().context("no HEAD")? == "*"; let head_sha: SharedString = fields.next().context("no objectname")?.to_string().into(); + let parent_sha: SharedString = fields.next().context("no parent")?.to_string().into(); let ref_name: SharedString = fields .next() .context("no refname")? @@ -1315,6 +1318,7 @@ fn parse_branch_input(input: &str) -> Result> { sha: head_sha, subject, commit_timestamp: commiterdate, + has_parent: !parent_sha.is_empty(), }), upstream: if upstream_name.is_empty() { None @@ -1367,7 +1371,7 @@ fn parse_upstream_track(upstream_track: &str) -> Result { fn test_branches_parsing() { // suppress "help: octal escapes are not supported, `\0` is always null" #[allow(clippy::octal_escapes)] - let input = "*\0060964da10574cd9bf06463a53bf6e0769c5c45e\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\0generated protobuf\n"; assert_eq!( parse_branch_input(&input).unwrap(), vec![Branch { @@ -1384,6 +1388,7 @@ fn test_branches_parsing() { sha: "060964da10574cd9bf06463a53bf6e0769c5c45e".into(), subject: "generated protobuf".into(), commit_timestamp: 1733187470, + has_parent: false, }) }] ) diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index a0abe15bafae30d36255fd0184da9df7630f349b..28ce505ab3bac8e15b95b5126f43a89f1fd2be81 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -3004,20 +3004,28 @@ impl GitPanel { }), ) .child(div().flex_1()) - .child( - panel_icon_button("undo", IconName::Undo) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) - .tooltip(Tooltip::for_action_title( - if self.has_staged_changes() { - "git reset HEAD^ --soft" - } else { - "git reset HEAD^" - }, - &git::Uncommit, - )) - .on_click(cx.listener(|this, _, window, cx| this.uncommit(window, cx))), - ), + .when(commit.has_parent, |this| { + let has_unstaged = self.has_unstaged_changes(); + this.child( + panel_icon_button("undo", IconName::Undo) + .icon_size(IconSize::Small) + .icon_color(Color::Muted) + .tooltip(move |window, cx| { + Tooltip::with_meta( + "Uncommit", + Some(&git::Uncommit), + if has_unstaged { + "git reset HEAD^ --soft" + } else { + "git reset HEAD^" + }, + window, + cx, + ) + }) + .on_click(cx.listener(|this, _, window, cx| this.uncommit(window, cx))), + ) + }), ) } @@ -4202,6 +4210,7 @@ impl ComponentPreview for PanelRepoFooter { sha: "abc123".into(), subject: "Modify stuff".into(), commit_timestamp: 1710932954, + has_parent: true, }), } } @@ -4218,6 +4227,7 @@ impl ComponentPreview for PanelRepoFooter { sha: "abc123".into(), subject: "Modify stuff".into(), commit_timestamp: 1710932954, + has_parent: true, }), } } diff --git a/crates/worktree/src/worktree.rs b/crates/worktree/src/worktree.rs index 15bd5cf8fb964cb04e0a77435249e74b2df1dcef..a4d36e8c90fdbc8262efa10300e6601db64e8b25 100644 --- a/crates/worktree/src/worktree.rs +++ b/crates/worktree/src/worktree.rs @@ -386,6 +386,7 @@ pub 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, + has_parent: true, } }), }