mirror of
https://github.com/extrawurst/gitui.git
synced 2024-12-27 02:53:50 +03:00
Allow copying multiple commits (#1288)
This commit is contained in:
parent
9534e4c2f9
commit
e0fa63c6c9
@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
* file blame at right revision from commit-details [[@heiskane](https://github.com/heiskane)] ([#1122](https://github.com/extrawurst/gitui/issues/1122))
|
||||
* add `regex-fancy` and `regex-onig` features to allow building Syntect with Onigumara regex engine instead of the default engine based on fancy-regex [[@jirutka](https://github.com/jirutka)]
|
||||
* add `vendor-openssl` feature to allow building without vendored openssl [[@jirutka](https://github.com/jirutka)]
|
||||
* allow copying marked commits [[@remique](https://github.com/remique)] ([#1288](https://github.com/extrawurst/gitui/issues/1288))
|
||||
|
||||
### Fixes
|
||||
* remove insecure dependency `ansi_term` ([#1290](https://github.com/extrawurst/gitui/issues/1290))
|
||||
|
@ -34,7 +34,7 @@ pub struct CommitList {
|
||||
branch: Option<String>,
|
||||
count_total: usize,
|
||||
items: ItemBatch,
|
||||
marked: Vec<CommitId>,
|
||||
marked: Vec<(usize, CommitId)>,
|
||||
scroll_state: (Instant, f32),
|
||||
tags: Option<Tags>,
|
||||
current_size: Cell<(u16, u16)>,
|
||||
@ -134,7 +134,7 @@ impl CommitList {
|
||||
}
|
||||
|
||||
///
|
||||
pub fn marked(&self) -> &[CommitId] {
|
||||
pub fn marked(&self) -> &[(usize, CommitId)] {
|
||||
&self.marked
|
||||
}
|
||||
|
||||
@ -143,11 +143,85 @@ impl CommitList {
|
||||
self.marked.clear();
|
||||
}
|
||||
|
||||
///
|
||||
pub fn marked_indexes(&self) -> Vec<usize> {
|
||||
let (indexes, _): (Vec<usize>, Vec<_>) =
|
||||
self.marked.iter().copied().unzip();
|
||||
|
||||
indexes
|
||||
}
|
||||
|
||||
///
|
||||
pub fn marked_commits(&self) -> Vec<CommitId> {
|
||||
let (_, commits): (Vec<_>, Vec<CommitId>) =
|
||||
self.marked.iter().copied().unzip();
|
||||
|
||||
commits
|
||||
}
|
||||
|
||||
fn marked_consecutive(&self) -> bool {
|
||||
let marked = self.marked_indexes();
|
||||
|
||||
for i in 1..marked.len() {
|
||||
if marked[i - 1] + 1 != marked[i] {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn copy_marked_hashes(&self) -> Result<()> {
|
||||
if self.marked_consecutive() {
|
||||
let m = self.marked_indexes();
|
||||
|
||||
let first = self.items.iter().nth(m[0]);
|
||||
|
||||
let last = self.items.iter().nth(m[m.len() - 1]);
|
||||
|
||||
if let (Some(f), Some(l)) = (first, last) {
|
||||
let yank =
|
||||
format!("{}^..{}", f.hash_short, l.hash_short);
|
||||
crate::clipboard::copy_string(&yank)?;
|
||||
};
|
||||
} else {
|
||||
let separate = self
|
||||
.marked_indexes()
|
||||
.iter()
|
||||
.map(|e| {
|
||||
self.items
|
||||
.iter()
|
||||
.nth(*e)
|
||||
.map_or_else(String::new, |le| {
|
||||
le.hash_short.to_string()
|
||||
})
|
||||
})
|
||||
.join(" ");
|
||||
|
||||
crate::clipboard::copy_string(&separate)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn copy_entry_hash(&self) -> Result<()> {
|
||||
if let Some(e) = self.items.iter().nth(
|
||||
self.selection.saturating_sub(self.items.index_offset()),
|
||||
) {
|
||||
crate::clipboard::copy_string(&e.hash_short)?;
|
||||
match self.marked_count() {
|
||||
0 => {
|
||||
if let Some(e) = self.items.iter().nth(
|
||||
self.selection
|
||||
.saturating_sub(self.items.index_offset()),
|
||||
) {
|
||||
crate::clipboard::copy_string(&e.hash_short)?;
|
||||
}
|
||||
}
|
||||
1 => {
|
||||
if let Some(e) =
|
||||
self.items.iter().nth(self.marked_indexes()[0])
|
||||
{
|
||||
crate::clipboard::copy_string(&e.hash_short)?;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -191,10 +265,17 @@ impl CommitList {
|
||||
fn mark(&mut self) {
|
||||
if let Some(e) = self.selected_entry() {
|
||||
let id = e.id;
|
||||
let selected = self
|
||||
.selection
|
||||
.saturating_sub(self.items.index_offset());
|
||||
if self.is_marked(&id).unwrap_or_default() {
|
||||
self.marked.retain(|marked| marked != &id);
|
||||
self.marked.retain(|marked| marked.1 != id);
|
||||
} else {
|
||||
self.marked.push(id);
|
||||
self.marked.push((selected, id));
|
||||
|
||||
self.marked.sort_unstable_by(|first, second| {
|
||||
first.0.cmp(&second.0)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -227,7 +308,8 @@ impl CommitList {
|
||||
if self.marked.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let found = self.marked.iter().any(|entry| entry == id);
|
||||
let found =
|
||||
self.marked.iter().any(|entry| entry.1 == *id);
|
||||
Some(found)
|
||||
}
|
||||
}
|
||||
|
@ -165,7 +165,11 @@ impl Revlog {
|
||||
}
|
||||
|
||||
fn copy_commit_hash(&self) -> Result<()> {
|
||||
self.list.copy_entry_hash()?;
|
||||
if self.list.marked_count() > 1 {
|
||||
self.list.copy_marked_hashes()?;
|
||||
} else {
|
||||
self.list.copy_entry_hash()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -328,7 +332,7 @@ impl Component for Revlog {
|
||||
self.queue.push(InternalEvent::OpenPopup(
|
||||
StackablePopupOpen::CompareCommits(
|
||||
InspectCommitOpen::new(
|
||||
self.list.marked()[0],
|
||||
self.list.marked()[0].1,
|
||||
),
|
||||
),
|
||||
));
|
||||
@ -339,8 +343,8 @@ impl Component for Revlog {
|
||||
self.queue.push(InternalEvent::OpenPopup(
|
||||
StackablePopupOpen::CompareCommits(
|
||||
InspectCommitOpen {
|
||||
commit_id: marked[0],
|
||||
compare_id: Some(marked[1]),
|
||||
commit_id: marked[0].1,
|
||||
compare_id: Some(marked[1].1),
|
||||
tags: None,
|
||||
},
|
||||
),
|
||||
|
@ -78,7 +78,7 @@ impl StashList {
|
||||
fn drop_stash(&mut self) {
|
||||
if self.list.marked_count() > 0 {
|
||||
self.queue.push(InternalEvent::ConfirmAction(
|
||||
Action::StashDrop(self.list.marked().to_vec()),
|
||||
Action::StashDrop(self.list.marked_commits()),
|
||||
));
|
||||
} else if let Some(e) = self.list.selected_entry() {
|
||||
self.queue.push(InternalEvent::ConfirmAction(
|
||||
|
Loading…
Reference in New Issue
Block a user