Skip to content

Checkout commit #1499

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

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
@@ -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))

57 changes: 57 additions & 0 deletions asyncgit/src/sync/branch/mod.rs
Original file line number Diff line number Diff line change
@@ -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,
@@ -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::*;
7 changes: 4 additions & 3 deletions asyncgit/src/sync/mod.rs
Original file line number Diff line number Diff line change
@@ -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,
26 changes: 25 additions & 1 deletion src/components/commitlist.rs
Original file line number Diff line number Diff line change
@@ -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;
@@ -31,6 +34,7 @@ const ELEMENTS_PER_LINE: usize = 9;

///
pub struct CommitList {
repo: RepoPathRef,
title: Box<str>,
selection: usize,
count_total: usize,
@@ -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,
@@ -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();

@@ -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
};
2 changes: 2 additions & 0 deletions src/keys/key_list.rs
Original file line number Diff line number Diff line change
@@ -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,
@@ -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()),
2 changes: 2 additions & 0 deletions src/keys/key_list_file.rs
Original file line number Diff line number Diff line change
@@ -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>,
@@ -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),
13 changes: 13 additions & 0 deletions src/strings.rs
Original file line number Diff line number Diff line change
@@ -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 {
7 changes: 7 additions & 0 deletions src/tabs/revlog.rs
Original file line number Diff line number Diff line change
@@ -62,6 +62,7 @@ impl Revlog {
key_config.clone(),
),
list: CommitList::new(
repo.clone(),
&strings::log_title(&key_config),
theme,
queue.clone(),
@@ -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,
1 change: 1 addition & 0 deletions src/tabs/stashlist.rs
Original file line number Diff line number Diff line change
@@ -32,6 +32,7 @@ impl StashList {
Self {
visible: false,
list: CommitList::new(
repo.clone(),
&strings::stashlist_title(&key_config),
theme,
queue.clone(),