mirror of
https://github.com/gitui-org/gitui
synced 2026-05-24 09:28:21 +00:00
feat: toggle hiding merge commits in revlog
Press Shift+M on the log tab to filter merge commits (like git log --no-merges). Rebuilds the async log walk with a parent-count filter. Fixes #2830 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
8619c07f3f
commit
a426515d2b
7 changed files with 99 additions and 9 deletions
|
|
@ -145,6 +145,13 @@ impl AsyncLog {
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
pub fn invalidate(&self) {
|
||||||
|
if let Ok(mut head) = self.current_head.lock() {
|
||||||
|
*head = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
pub fn fetch(&self) -> Result<FetchStatus> {
|
pub fn fetch(&self) -> Result<FetchStatus> {
|
||||||
self.background.store(false, Ordering::Relaxed);
|
self.background.store(false, Ordering::Relaxed);
|
||||||
|
|
|
||||||
|
|
@ -222,3 +222,15 @@ pub fn filter_commit_by_search(
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
pub fn filter_commits_exclude_merges() -> SharedCommitFilterFn {
|
||||||
|
Arc::new(Box::new(
|
||||||
|
move |repo: &Repository,
|
||||||
|
commit_id: &CommitId|
|
||||||
|
-> Result<bool> {
|
||||||
|
let commit = repo.find_commit((*commit_id).into())?;
|
||||||
|
Ok(commit.parent_count() <= 1)
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,8 @@ pub use commit_details::{
|
||||||
};
|
};
|
||||||
pub use commit_files::get_commit_files;
|
pub use commit_files::get_commit_files;
|
||||||
pub use commit_filter::{
|
pub use commit_filter::{
|
||||||
diff_contains_file, filter_commit_by_search, LogFilterSearch,
|
diff_contains_file, filter_commit_by_search,
|
||||||
|
filter_commits_exclude_merges, LogFilterSearch,
|
||||||
LogFilterSearchOptions, SearchFields, SearchOptions,
|
LogFilterSearchOptions, SearchFields, SearchOptions,
|
||||||
SharedCommitFilterFn,
|
SharedCommitFilterFn,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,10 @@ pub struct CommitList {
|
||||||
|
|
||||||
impl CommitList {
|
impl CommitList {
|
||||||
///
|
///
|
||||||
|
pub fn set_title(&mut self, title: impl Into<Box<str>>) {
|
||||||
|
self.title = title.into();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new(env: &Environment, title: &str) -> Self {
|
pub fn new(env: &Environment, title: &str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
repo: env.repo.clone(),
|
repo: env.repo.clone(),
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ pub struct KeysList {
|
||||||
pub log_reset_commit: GituiKeyEvent,
|
pub log_reset_commit: GituiKeyEvent,
|
||||||
pub log_reword_commit: GituiKeyEvent,
|
pub log_reword_commit: GituiKeyEvent,
|
||||||
pub log_find: GituiKeyEvent,
|
pub log_find: GituiKeyEvent,
|
||||||
|
pub log_hide_merges: GituiKeyEvent,
|
||||||
pub find_commit_sha: GituiKeyEvent,
|
pub find_commit_sha: GituiKeyEvent,
|
||||||
pub commit_amend: GituiKeyEvent,
|
pub commit_amend: GituiKeyEvent,
|
||||||
pub toggle_signoff: GituiKeyEvent,
|
pub toggle_signoff: GituiKeyEvent,
|
||||||
|
|
@ -186,6 +187,7 @@ impl Default for KeysList {
|
||||||
log_reset_commit: GituiKeyEvent { code: KeyCode::Char('R'), modifiers: KeyModifiers::SHIFT },
|
log_reset_commit: GituiKeyEvent { code: KeyCode::Char('R'), modifiers: KeyModifiers::SHIFT },
|
||||||
log_reword_commit: GituiKeyEvent { code: KeyCode::Char('r'), modifiers: KeyModifiers::empty() },
|
log_reword_commit: GituiKeyEvent { code: KeyCode::Char('r'), modifiers: KeyModifiers::empty() },
|
||||||
log_find: GituiKeyEvent { code: KeyCode::Char('f'), modifiers: KeyModifiers::empty() },
|
log_find: GituiKeyEvent { code: KeyCode::Char('f'), modifiers: KeyModifiers::empty() },
|
||||||
|
log_hide_merges: GituiKeyEvent::new(KeyCode::Char('M'), KeyModifiers::SHIFT),
|
||||||
find_commit_sha: GituiKeyEvent::new(KeyCode::Char('j'), KeyModifiers::CONTROL),
|
find_commit_sha: GituiKeyEvent::new(KeyCode::Char('j'), KeyModifiers::CONTROL),
|
||||||
commit_amend: GituiKeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL),
|
commit_amend: GituiKeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL),
|
||||||
toggle_signoff: GituiKeyEvent::new(KeyCode::Char('s'), KeyModifiers::CONTROL),
|
toggle_signoff: GituiKeyEvent::new(KeyCode::Char('s'), KeyModifiers::CONTROL),
|
||||||
|
|
|
||||||
|
|
@ -300,8 +300,12 @@ pub fn confirm_msg_force_push(
|
||||||
"Confirm force push to branch '{branch_ref}' ? This may rewrite history."
|
"Confirm force push to branch '{branch_ref}' ? This may rewrite history."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
pub fn log_title(_key_config: &SharedKeyConfig) -> String {
|
pub fn log_title(hide_merge_commits: bool) -> String {
|
||||||
"Commit".to_string()
|
if hide_merge_commits {
|
||||||
|
"Commit (no merges)".to_string()
|
||||||
|
} else {
|
||||||
|
"Commit".to_string()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn file_log_title(
|
pub fn file_log_title(
|
||||||
file_path: &str,
|
file_path: &str,
|
||||||
|
|
@ -1576,6 +1580,24 @@ pub mod commands {
|
||||||
CMD_GROUP_LOG,
|
CMD_GROUP_LOG,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
pub fn log_toggle_hide_merges(
|
||||||
|
key_config: &SharedKeyConfig,
|
||||||
|
hide_merge_commits: bool,
|
||||||
|
) -> CommandText {
|
||||||
|
let action = if hide_merge_commits {
|
||||||
|
"show merge commits"
|
||||||
|
} else {
|
||||||
|
"hide merge commits"
|
||||||
|
};
|
||||||
|
CommandText::new(
|
||||||
|
format!(
|
||||||
|
"No merges [{}]",
|
||||||
|
key_config.get_hint(key_config.keys.log_hide_merges),
|
||||||
|
),
|
||||||
|
action,
|
||||||
|
CMD_GROUP_LOG,
|
||||||
|
)
|
||||||
|
}
|
||||||
pub fn log_find_commit(
|
pub fn log_find_commit(
|
||||||
key_config: &SharedKeyConfig,
|
key_config: &SharedKeyConfig,
|
||||||
) -> CommandText {
|
) -> CommandText {
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ use anyhow::Result;
|
||||||
use asyncgit::{
|
use asyncgit::{
|
||||||
asyncjob::AsyncSingleJob,
|
asyncjob::AsyncSingleJob,
|
||||||
sync::{
|
sync::{
|
||||||
self, filter_commit_by_search, CommitId, LogFilterSearch,
|
self, filter_commit_by_search, filter_commits_exclude_merges,
|
||||||
LogFilterSearchOptions, RepoPathRef,
|
CommitId, LogFilterSearch, LogFilterSearchOptions, RepoPathRef,
|
||||||
},
|
},
|
||||||
AsyncBranchesJob, AsyncCommitFilterJob, AsyncGitNotification,
|
AsyncBranchesJob, AsyncCommitFilterJob, AsyncGitNotification,
|
||||||
AsyncLog, AsyncTags, CommitFilesParams, FetchStatus,
|
AsyncLog, AsyncTags, CommitFilesParams, FetchStatus,
|
||||||
|
|
@ -66,6 +66,7 @@ pub struct Revlog {
|
||||||
list: CommitList,
|
list: CommitList,
|
||||||
git_log: AsyncLog,
|
git_log: AsyncLog,
|
||||||
search: LogSearch,
|
search: LogSearch,
|
||||||
|
hide_merge_commits: bool,
|
||||||
git_tags: AsyncTags,
|
git_tags: AsyncTags,
|
||||||
git_local_branches: AsyncSingleJob<AsyncBranchesJob>,
|
git_local_branches: AsyncSingleJob<AsyncBranchesJob>,
|
||||||
git_remote_branches: AsyncSingleJob<AsyncBranchesJob>,
|
git_remote_branches: AsyncSingleJob<AsyncBranchesJob>,
|
||||||
|
|
@ -83,16 +84,14 @@ impl Revlog {
|
||||||
repo: env.repo.clone(),
|
repo: env.repo.clone(),
|
||||||
queue: env.queue.clone(),
|
queue: env.queue.clone(),
|
||||||
commit_details: CommitDetailsComponent::new(env),
|
commit_details: CommitDetailsComponent::new(env),
|
||||||
list: CommitList::new(
|
list: CommitList::new(env, &strings::log_title(false)),
|
||||||
env,
|
|
||||||
&strings::log_title(&env.key_config),
|
|
||||||
),
|
|
||||||
git_log: AsyncLog::new(
|
git_log: AsyncLog::new(
|
||||||
env.repo.borrow().clone(),
|
env.repo.borrow().clone(),
|
||||||
&env.sender_git,
|
&env.sender_git,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
search: LogSearch::Off,
|
search: LogSearch::Off,
|
||||||
|
hide_merge_commits: false,
|
||||||
git_tags: AsyncTags::new(
|
git_tags: AsyncTags::new(
|
||||||
env.repo.borrow().clone(),
|
env.repo.borrow().clone(),
|
||||||
&env.sender_git,
|
&env.sender_git,
|
||||||
|
|
@ -404,6 +403,34 @@ impl Revlog {
|
||||||
fn can_start_search(&self) -> bool {
|
fn can_start_search(&self) -> bool {
|
||||||
!self.git_log.is_pending() && !self.is_search_pending()
|
!self.git_log.is_pending() && !self.is_search_pending()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn can_toggle_hide_merges(&self) -> bool {
|
||||||
|
matches!(self.search, LogSearch::Off) && !self.git_log.is_pending()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toggle_hide_merge_commits(&mut self) {
|
||||||
|
if !self.can_toggle_hide_merges() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.hide_merge_commits = !self.hide_merge_commits;
|
||||||
|
let filter = if self.hide_merge_commits {
|
||||||
|
Some(filter_commits_exclude_merges())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
self.git_log = AsyncLog::new(
|
||||||
|
self.repo.borrow().clone(),
|
||||||
|
&self.sender,
|
||||||
|
filter,
|
||||||
|
);
|
||||||
|
self.git_log.invalidate();
|
||||||
|
self.list.clear();
|
||||||
|
self.list
|
||||||
|
.set_title(strings::log_title(self.hide_merge_commits));
|
||||||
|
let _ = self.git_log.fetch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrawableComponent for Revlog {
|
impl DrawableComponent for Revlog {
|
||||||
|
|
@ -575,6 +602,13 @@ impl Component for Revlog {
|
||||||
self.queue
|
self.queue
|
||||||
.push(InternalEvent::OpenLogSearchPopup);
|
.push(InternalEvent::OpenLogSearchPopup);
|
||||||
return Ok(EventState::Consumed);
|
return Ok(EventState::Consumed);
|
||||||
|
} else if key_match(
|
||||||
|
k,
|
||||||
|
self.key_config.keys.log_hide_merges,
|
||||||
|
) && self.can_toggle_hide_merges()
|
||||||
|
{
|
||||||
|
self.toggle_hide_merge_commits();
|
||||||
|
return Ok(EventState::Consumed);
|
||||||
} else if key_match(
|
} else if key_match(
|
||||||
k,
|
k,
|
||||||
self.key_config.keys.compare_commits,
|
self.key_config.keys.compare_commits,
|
||||||
|
|
@ -729,6 +763,14 @@ impl Component for Revlog {
|
||||||
self.can_start_search(),
|
self.can_start_search(),
|
||||||
self.visible || force_all,
|
self.visible || force_all,
|
||||||
));
|
));
|
||||||
|
out.push(CommandInfo::new(
|
||||||
|
strings::commands::log_toggle_hide_merges(
|
||||||
|
&self.key_config,
|
||||||
|
self.hide_merge_commits,
|
||||||
|
),
|
||||||
|
self.can_toggle_hide_merges(),
|
||||||
|
self.visible || force_all,
|
||||||
|
));
|
||||||
|
|
||||||
visibility_blocking(self)
|
visibility_blocking(self)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue