Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 62 additions & 2 deletions asyncgit/src/sync/commit.rs
Original file line number Diff line number Diff line change
@@ -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;

///
Expand Down Expand Up @@ -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};

Expand Down Expand Up @@ -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(())
}
}
2 changes: 1 addition & 1 deletion asyncgit/src/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
13 changes: 12 additions & 1 deletion src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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()),
Expand Down Expand Up @@ -296,6 +301,7 @@ impl App {
stashmsg_popup,
inspect_commit_popup,
external_editor_popup,
tag_commit_popup,
help,
revlog,
status_tab,
Expand Down Expand Up @@ -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)?;
Expand Down Expand Up @@ -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>(
Expand All @@ -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(())
}
Expand Down
3 changes: 3 additions & 0 deletions src/components/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod inspect_commit;
mod msg;
mod reset;
mod stashmsg;
mod tag_commit;
mod textinput;
mod utils;

Expand All @@ -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;
Expand Down
134 changes: 134 additions & 0 deletions src/components/tag_commit.rs
Original file line number Diff line number Diff line change
@@ -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,
)),
);
}
}
}
}
}
1 change: 1 addition & 0 deletions src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
2 changes: 2 additions & 0 deletions src/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ pub enum InternalEvent {
///
InspectCommit(CommitId, Option<CommitTags>),
///
TagCommit(CommitId),
///
OpenExternalEditor(Option<String>),
}

Expand Down
10 changes: 10 additions & 0 deletions src/strings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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);
}
19 changes: 19 additions & 0 deletions src/tabs/revlog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() =>
{
Expand Down Expand Up @@ -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)
}

Expand Down