Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Checkout commit #1499

Merged
merged 12 commits into from
Feb 4, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
* changes in commit message inside external editor [[@bc-universe]](https://github.com/bc-universe) ([#1420](https://github.com/extrawurst/gitui/issues/1420))
* allow detaching HEAD and checking out specific commit from log view ([#1499](https://github.com/extrawurst/gitui/pull/1499))
* add no-verify option on commits to not run hooks [[@dam5h]](https://github.com/dam5h) ([#1374](https://github.com/extrawurst/gitui/issues/1374))
* allow `fetch` on status tab [[@alensiljak]](https://github.com/alensiljak) ([#1471](https://github.com/extrawurst/gitui/issues/1471))

Expand Down
57 changes: 57 additions & 0 deletions asyncgit/src/sync/branch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,36 @@ pub fn checkout_branch(
}
}

/// Detach HEAD to point to a commit then checkout HEAD, does not work if there are uncommitted changes
pub fn checkout_commit(
repo_path: &RepoPath,
commit_hash: CommitId,
) -> Result<()> {
scope_time!("checkout_commit");

let repo = repo(repo_path)?;
let cur_ref = repo.head()?;
let statuses = repo.statuses(Some(
git2::StatusOptions::new().include_ignored(false),
))?;

if statuses.is_empty() {
repo.set_head_detached(commit_hash.into())?;

if let Err(e) = repo.checkout_head(Some(
git2::build::CheckoutBuilder::new().force(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will overwrite changes made to the index, right? I am not sure we want force to be true

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested, it in fact will fail with an Err if there are uncommitted changes. se more comments below

)) {
repo.set_head(
bytes2string(cur_ref.name_bytes())?.as_str(),
)?;
return Err(Error::Git(e));
}
Ok(())
} else {
Err(Error::UncommittedChanges)
}
}

///
pub fn checkout_remote_branch(
repo_path: &RepoPath,
Expand Down Expand Up @@ -665,6 +695,33 @@ mod tests_checkout {
}
}

#[cfg(test)]
mod tests_checkout_commit {
use super::*;
use crate::sync::tests::{repo_init, write_commit_file};
use crate::sync::RepoPath;

#[test]
fn test_smoke() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();

let commit =
write_commit_file(&repo, "test_1.txt", "test", "commit1");
write_commit_file(&repo, "test_2.txt", "test", "commit2");

checkout_commit(repo_path, commit).unwrap();

assert!(repo.head_detached().unwrap());
assert_eq!(
repo.head().unwrap().target().unwrap(),
commit.get_oid()
);
}
}

#[cfg(test)]
mod test_delete_branch {
use super::*;
Expand Down
7 changes: 4 additions & 3 deletions asyncgit/src/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ pub mod utils;

pub use blame::{blame_file, BlameHunk, FileBlame};
pub use branch::{
branch_compare_upstream, checkout_branch, config_is_pull_rebase,
create_branch, delete_branch, get_branch_remote,
get_branches_info, merge_commit::merge_upstream_commit,
branch_compare_upstream, checkout_branch, checkout_commit,
config_is_pull_rebase, create_branch, delete_branch,
get_branch_remote, get_branches_info,
merge_commit::merge_upstream_commit,
merge_ff::branch_merge_upstream_fastforward,
merge_rebase::merge_upstream_rebase, rename::rename_branch,
validate_branch_name, BranchCompare, BranchInfo,
Expand Down
26 changes: 25 additions & 1 deletion src/components/commitlist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ use crate::{
keys::{key_match, SharedKeyConfig},
queue::{InternalEvent, Queue},
strings::{self, symbol},
try_or_popup,
ui::style::{SharedTheme, Theme},
ui::{calc_scroll_top, draw_scrollbar, Orientation},
};
use anyhow::Result;
use asyncgit::sync::{BranchInfo, CommitId, Tags};
use asyncgit::sync::{
checkout_commit, BranchInfo, CommitId, RepoPathRef, Tags,
};
use chrono::{DateTime, Local};
use crossterm::event::Event;
use itertools::Itertools;
Expand All @@ -31,6 +34,7 @@ const ELEMENTS_PER_LINE: usize = 9;

///
pub struct CommitList {
repo: RepoPathRef,
title: Box<str>,
selection: usize,
count_total: usize,
Expand All @@ -49,12 +53,14 @@ pub struct CommitList {
impl CommitList {
///
pub fn new(
repo: RepoPathRef,
title: &str,
theme: SharedTheme,
queue: Queue,
key_config: SharedKeyConfig,
) -> Self {
Self {
repo,
items: ItemBatch::default(),
marked: Vec::with_capacity(2),
selection: 0,
Expand Down Expand Up @@ -435,6 +441,18 @@ impl CommitList {
self.selection = position;
}

pub fn checkout(&mut self) {
if let Some(commit_hash) =
self.selected_entry().map(|entry| entry.id)
{
try_or_popup!(
self,
"failed to checkout commit:",
checkout_commit(&self.repo.borrow(), commit_hash)
);
}
}

pub fn set_branches(&mut self, branches: Vec<BranchInfo>) {
self.branches.clear();

Expand Down Expand Up @@ -538,6 +556,12 @@ impl Component for CommitList {
) {
self.mark();
true
} else if key_match(
k,
self.key_config.keys.log_checkout_commit,
) {
self.checkout();
true
} else {
false
};
Expand Down
2 changes: 2 additions & 0 deletions src/keys/key_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ pub struct KeysList {
pub cmd_bar_toggle: GituiKeyEvent,
pub log_tag_commit: GituiKeyEvent,
pub log_mark_commit: GituiKeyEvent,
pub log_checkout_commit: GituiKeyEvent,
pub commit_amend: GituiKeyEvent,
pub toggle_verify: GituiKeyEvent,
pub copy: GituiKeyEvent,
Expand Down Expand Up @@ -171,6 +172,7 @@ impl Default for KeysList {
cmd_bar_toggle: GituiKeyEvent::new(KeyCode::Char('.'), KeyModifiers::empty()),
log_tag_commit: GituiKeyEvent::new(KeyCode::Char('t'), KeyModifiers::empty()),
log_mark_commit: GituiKeyEvent::new(KeyCode::Char(' '), KeyModifiers::empty()),
log_checkout_commit: GituiKeyEvent { code: KeyCode::Char('S'), modifiers: KeyModifiers::SHIFT },
commit_amend: GituiKeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL),
toggle_verify: GituiKeyEvent::new(KeyCode::Char('f'), KeyModifiers::CONTROL),
copy: GituiKeyEvent::new(KeyCode::Char('y'), KeyModifiers::empty()),
Expand Down
2 changes: 2 additions & 0 deletions src/keys/key_list_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ pub struct KeysListFile {
pub cmd_bar_toggle: Option<GituiKeyEvent>,
pub log_tag_commit: Option<GituiKeyEvent>,
pub log_mark_commit: Option<GituiKeyEvent>,
pub log_checkout_commit: Option<GituiKeyEvent>,
pub commit_amend: Option<GituiKeyEvent>,
pub toggle_verify: Option<GituiKeyEvent>,
pub copy: Option<GituiKeyEvent>,
Expand Down Expand Up @@ -151,6 +152,7 @@ impl KeysListFile {
cmd_bar_toggle: self.cmd_bar_toggle.unwrap_or(default.cmd_bar_toggle),
log_tag_commit: self.log_tag_commit.unwrap_or(default.log_tag_commit),
log_mark_commit: self.log_mark_commit.unwrap_or(default.log_mark_commit),
log_checkout_commit: self.log_checkout_commit.unwrap_or(default.log_checkout_commit),
commit_amend: self.commit_amend.unwrap_or(default.commit_amend),
toggle_verify: self.toggle_verify.unwrap_or(default.toggle_verify),
copy: self.copy.unwrap_or(default.copy),
Expand Down
13 changes: 13 additions & 0 deletions src/strings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1195,6 +1195,19 @@ pub mod commands {
CMD_GROUP_LOG,
)
}
pub fn log_checkout_commit(
key_config: &SharedKeyConfig,
) -> CommandText {
CommandText::new(
format!(
"Checkout [{}]",
key_config
.get_hint(key_config.keys.log_checkout_commit),
),
"checkout commit",
CMD_GROUP_LOG,
)
}
pub fn inspect_file_tree(
key_config: &SharedKeyConfig,
) -> CommandText {
Expand Down
7 changes: 7 additions & 0 deletions src/tabs/revlog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ impl Revlog {
key_config.clone(),
),
list: CommitList::new(
repo.clone(),
&strings::log_title(&key_config),
theme,
queue.clone(),
Expand Down Expand Up @@ -418,6 +419,12 @@ impl Component for Revlog {
self.visible || force_all,
));

out.push(CommandInfo::new(
strings::commands::log_checkout_commit(&self.key_config),
self.selected_commit().is_some(),
self.visible || force_all,
));

out.push(CommandInfo::new(
strings::commands::open_tags_popup(&self.key_config),
true,
Expand Down
1 change: 1 addition & 0 deletions src/tabs/stashlist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ impl StashList {
Self {
visible: false,
list: CommitList::new(
repo.clone(),
&strings::stashlist_title(&key_config),
theme,
queue.clone(),
Expand Down