mirror of
https://github.com/extrawurst/gitui.git
synced 2024-12-29 12:06:26 +03:00
Add command for tagging commit
This commit is contained in:
parent
aa068f8004
commit
70423c5b83
@ -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 tag’s 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(())
|
||||
}
|
||||
}
|
||||
|
@ -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};
|
||||
|
13
src/app.rs
13
src/app.rs
@ -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(())
|
||||
}
|
||||
|
@ -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;
|
||||
|
134
src/components/tag_commit.rs
Normal file
134
src/components/tag_commit.rs
Normal 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,
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -49,6 +49,8 @@ pub enum InternalEvent {
|
||||
///
|
||||
InspectCommit(CommitId, Option<CommitTags>),
|
||||
///
|
||||
TagCommit(CommitId),
|
||||
///
|
||||
OpenExternalEditor(Option<String>),
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user