Add command for tagging commit

This commit is contained in:
Christoph Rüßler 2020-07-08 20:54:36 +02:00 committed by Stephan Dilly
parent aa068f8004
commit 70423c5b83
9 changed files with 244 additions and 4 deletions

View File

@ -1,6 +1,6 @@
use super::{get_head, utils::repo, CommitId};
use crate::error::Result;
use git2::{ErrorCode, Repository, Signature};
use git2::{ErrorCode, ObjectType, Repository, Signature};
use scopetime::scope_time;
///
@ -80,17 +80,39 @@ pub fn commit(repo_path: &str, msg: &str) -> Result<CommitId> {
.into())
}
/// Tag a commit.
///
/// This function will return an `Err(…)` variant if the tags name is refused
/// by git or if the tag already exists.
pub fn tag(
repo_path: &str,
commit_id: &CommitId,
tag: &str,
) -> Result<CommitId> {
scope_time!("tag");
let repo = repo(repo_path)?;
let signature = signature_allow_undefined_name(&repo)?;
let object_id = commit_id.get_oid();
let target =
repo.find_object(object_id, Some(ObjectType::Commit))?;
Ok(repo.tag(tag, &target, &signature, "", false)?.into())
}
#[cfg(test)]
mod tests {
use crate::error::Result;
use crate::sync::{
commit, get_commit_details, get_commit_files, stage_add_file,
tags::get_tags,
tests::{get_statuses, repo_init, repo_init_empty},
utils::get_head,
LogWalker,
};
use commit::amend;
use commit::{amend, tag};
use git2::Repository;
use std::{fs::File, io::Write, path::Path};
@ -185,4 +207,42 @@ mod tests {
Ok(())
}
#[test]
fn test_tag() -> Result<()> {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
File::create(&root.join(file_path))?
.write_all(b"test\nfoo")?;
stage_add_file(repo_path, file_path)?;
let new_id = commit(repo_path, "commit msg")?;
tag(repo_path, &new_id, "tag")?;
assert_eq!(
get_tags(repo_path).unwrap()[&new_id],
vec!["tag"]
);
assert!(matches!(tag(repo_path, &new_id, "tag"), Err(_)));
assert_eq!(
get_tags(repo_path).unwrap()[&new_id],
vec!["tag"]
);
tag(repo_path, &new_id, "second-tag")?;
assert_eq!(
get_tags(repo_path).unwrap()[&new_id],
vec!["second-tag", "tag"]
);
Ok(())
}
}

View File

@ -18,7 +18,7 @@ pub mod utils;
pub(crate) use branch::get_branch_name;
pub use commit::{amend, commit};
pub use commit::{amend, commit, tag};
pub use commit_details::{get_commit_details, CommitDetails};
pub use commit_files::get_commit_files;
pub use commits_info::{get_commits_info, CommitId, CommitInfo};

View File

@ -5,7 +5,7 @@ use crate::{
event_pump, CommandBlocking, CommandInfo, CommitComponent,
Component, DrawableComponent, ExternalEditorComponent,
HelpComponent, InspectCommitComponent, MsgComponent,
ResetComponent, StashMsgComponent,
ResetComponent, StashMsgComponent, TagCommitComponent,
},
input::{Input, InputEvent, InputState},
keys,
@ -40,6 +40,7 @@ pub struct App {
stashmsg_popup: StashMsgComponent,
inspect_commit_popup: InspectCommitComponent,
external_editor_popup: ExternalEditorComponent,
tag_commit_popup: TagCommitComponent,
cmdbar: RefCell<CommandBar>,
tab: usize,
revlog: Revlog,
@ -85,6 +86,10 @@ impl App {
external_editor_popup: ExternalEditorComponent::new(
theme.clone(),
),
tag_commit_popup: TagCommitComponent::new(
queue.clone(),
theme.clone(),
),
do_quit: false,
cmdbar: RefCell::new(CommandBar::new(theme.clone())),
help: HelpComponent::new(theme.clone()),
@ -296,6 +301,7 @@ impl App {
stashmsg_popup,
inspect_commit_popup,
external_editor_popup,
tag_commit_popup,
help,
revlog,
status_tab,
@ -419,6 +425,9 @@ impl App {
self.stashmsg_popup.options(opts);
self.stashmsg_popup.show()?
}
InternalEvent::TagCommit(id) => {
self.tag_commit_popup.open(id)?;
}
InternalEvent::TabSwitch => self.set_tab(0)?,
InternalEvent::InspectCommit(id, tags) => {
self.inspect_commit_popup.open(id, tags)?;
@ -484,6 +493,7 @@ impl App {
|| self.stashmsg_popup.is_visible()
|| self.inspect_commit_popup.is_visible()
|| self.external_editor_popup.is_visible()
|| self.tag_commit_popup.is_visible()
}
fn draw_popups<B: Backend>(
@ -508,6 +518,7 @@ impl App {
self.msg.draw(f, size)?;
self.inspect_commit_popup.draw(f, size)?;
self.external_editor_popup.draw(f, size)?;
self.tag_commit_popup.draw(f, size)?;
Ok(())
}

View File

@ -11,6 +11,7 @@ mod inspect_commit;
mod msg;
mod reset;
mod stashmsg;
mod tag_commit;
mod textinput;
mod utils;
@ -30,6 +31,8 @@ pub use inspect_commit::InspectCommitComponent;
pub use msg::MsgComponent;
pub use reset::ResetComponent;
pub use stashmsg::StashMsgComponent;
pub use tag_commit::TagCommitComponent;
pub use textinput::TextInputComponent;
pub use utils::filetree::FileTreeItemKind;
use crate::ui::style::Theme;

View File

@ -0,0 +1,134 @@
use super::{
textinput::TextInputComponent, visibility_blocking,
CommandBlocking, CommandInfo, Component, DrawableComponent,
};
use crate::{
queue::{InternalEvent, NeedsUpdate, Queue},
strings::{self, commands},
ui::style::SharedTheme,
};
use anyhow::Result;
use asyncgit::{
sync::{self, CommitId},
CWD,
};
use crossterm::event::{Event, KeyCode};
use tui::{backend::Backend, layout::Rect, Frame};
pub struct TagCommitComponent {
input: TextInputComponent,
commit_id: Option<CommitId>,
queue: Queue,
}
impl DrawableComponent for TagCommitComponent {
fn draw<B: Backend>(
&self,
f: &mut Frame<B>,
rect: Rect,
) -> Result<()> {
self.input.draw(f, rect)?;
Ok(())
}
}
impl Component for TagCommitComponent {
fn commands(
&self,
out: &mut Vec<CommandInfo>,
force_all: bool,
) -> CommandBlocking {
if self.is_visible() || force_all {
self.input.commands(out, force_all);
out.push(CommandInfo::new(
commands::TAG_COMMIT_CONFIRM_MSG,
true,
true,
));
}
visibility_blocking(self)
}
fn event(&mut self, ev: Event) -> Result<bool> {
if self.is_visible() {
if self.input.event(ev)? {
return Ok(true);
}
if let Event::Key(e) = ev {
if let KeyCode::Enter = e.code {
self.tag()
}
return Ok(true);
}
}
Ok(false)
}
fn is_visible(&self) -> bool {
self.input.is_visible()
}
fn hide(&mut self) {
self.input.hide()
}
fn show(&mut self) -> Result<()> {
self.input.show()?;
Ok(())
}
}
impl TagCommitComponent {
///
pub fn new(queue: Queue, theme: SharedTheme) -> Self {
Self {
queue,
input: TextInputComponent::new(
theme,
strings::TAG_COMMIT_POPUP_TITLE,
strings::TAG_COMMIT_POPUP_MSG,
),
commit_id: None,
}
}
///
pub fn open(&mut self, id: CommitId) -> Result<()> {
self.commit_id = Some(id);
self.show()?;
Ok(())
}
///
pub fn tag(&mut self) {
if let Some(commit_id) = self.commit_id {
match sync::tag(CWD, &commit_id, self.input.get_text()) {
Ok(_) => {
self.input.clear();
self.hide();
self.queue.borrow_mut().push_back(
InternalEvent::Update(NeedsUpdate::ALL),
);
}
Err(e) => {
self.hide();
log::error!("e: {}", e,);
self.queue.borrow_mut().push_back(
InternalEvent::ShowErrorMsg(format!(
"tag error:\n{}",
e,
)),
);
}
}
}
}
}

View File

@ -66,5 +66,6 @@ pub const STASH_DROP: KeyEvent =
with_mod(KeyCode::Char('D'), KeyModifiers::SHIFT);
pub const CMD_BAR_TOGGLE: KeyEvent = no_mod(KeyCode::Char('.'));
pub const LOG_COMMIT_DETAILS: KeyEvent = no_mod(KeyCode::Enter);
pub const LOG_TAG_COMMIT: KeyEvent = no_mod(KeyCode::Char('t'));
pub const COMMIT_AMEND: KeyEvent =
with_mod(KeyCode::Char('a'), KeyModifiers::CONTROL);

View File

@ -49,6 +49,8 @@ pub enum InternalEvent {
///
InspectCommit(CommitId, Option<CommitTags>),
///
TagCommit(CommitId),
///
OpenExternalEditor(Option<String>),
}

View File

@ -27,6 +27,10 @@ pub static CONFIRM_MSG_STASHDROP: &str = "confirm stash drop?";
pub static CONFIRM_MSG_RESETHUNK: &str = "confirm reset hunk?";
pub static LOG_TITLE: &str = "Commit";
pub static TAG_COMMIT_POPUP_TITLE: &str = "Tag";
pub static TAG_COMMIT_POPUP_MSG: &str = "type tag";
pub static STASHLIST_TITLE: &str = "Stashes";
pub static HELP_TITLE: &str = "Help: all commands";
@ -295,4 +299,10 @@ pub mod commands {
"inspect selected commit in detail",
CMD_GROUP_LOG,
);
///
pub static LOG_TAG_COMMIT: CommandText =
CommandText::new("Tag [t]", "tag commit", CMD_GROUP_LOG);
///
pub static TAG_COMMIT_CONFIRM_MSG: CommandText =
CommandText::new("Tag [enter]", "tag commit", CMD_GROUP_LOG);
}

View File

@ -207,6 +207,19 @@ impl Component for Revlog {
return Ok(true);
}
Event::Key(keys::LOG_TAG_COMMIT) => {
return if let Some(id) =
self.selected_commit()
{
self.queue.borrow_mut().push_back(
InternalEvent::TagCommit(id),
);
Ok(true)
} else {
Ok(false)
};
}
Event::Key(keys::FOCUS_RIGHT)
if self.commit_details.is_visible() =>
{
@ -257,6 +270,12 @@ impl Component for Revlog {
|| force_all,
));
out.push(CommandInfo::new(
commands::LOG_TAG_COMMIT,
true,
self.visible || force_all,
));
visibility_blocking(self)
}