Skip to content

Commit 50d3964

Browse files
remiqueheiskane
authored andcommitted
Allow copying multiple commits (gitui-org#1288)
1 parent 2eeb0cb commit 50d3964

File tree

4 files changed

+101
-14
lines changed

4 files changed

+101
-14
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020
* Multiline commit messages [[@heiskane](https://github.com/heiskane)] ([#1171](https://github.com/extrawurst/gitui/issues/1171))
2121
* 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)]
2222
* add `vendor-openssl` feature to allow building without vendored openssl [[@jirutka](https://github.com/jirutka)]
23+
* allow copying marked commits [[@remique](https://github.com/remique)] ([#1288](https://github.com/extrawurst/gitui/issues/1288))
2324

2425
### Fixes
2526
* remove insecure dependency `ansi_term` ([#1290](https://github.com/extrawurst/gitui/issues/1290))

src/components/commitlist.rs

Lines changed: 91 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pub struct CommitList {
3434
branch: Option<String>,
3535
count_total: usize,
3636
items: ItemBatch,
37-
marked: Vec<CommitId>,
37+
marked: Vec<(usize, CommitId)>,
3838
scroll_state: (Instant, f32),
3939
tags: Option<Tags>,
4040
current_size: Cell<(u16, u16)>,
@@ -134,7 +134,7 @@ impl CommitList {
134134
}
135135

136136
///
137-
pub fn marked(&self) -> &[CommitId] {
137+
pub fn marked(&self) -> &[(usize, CommitId)] {
138138
&self.marked
139139
}
140140

@@ -143,11 +143,85 @@ impl CommitList {
143143
self.marked.clear();
144144
}
145145

146+
///
147+
pub fn marked_indexes(&self) -> Vec<usize> {
148+
let (indexes, _): (Vec<usize>, Vec<_>) =
149+
self.marked.iter().copied().unzip();
150+
151+
indexes
152+
}
153+
154+
///
155+
pub fn marked_commits(&self) -> Vec<CommitId> {
156+
let (_, commits): (Vec<_>, Vec<CommitId>) =
157+
self.marked.iter().copied().unzip();
158+
159+
commits
160+
}
161+
162+
fn marked_consecutive(&self) -> bool {
163+
let marked = self.marked_indexes();
164+
165+
for i in 1..marked.len() {
166+
if marked[i - 1] + 1 != marked[i] {
167+
return false;
168+
}
169+
}
170+
171+
true
172+
}
173+
174+
pub fn copy_marked_hashes(&self) -> Result<()> {
175+
if self.marked_consecutive() {
176+
let m = self.marked_indexes();
177+
178+
let first = self.items.iter().nth(m[0]);
179+
180+
let last = self.items.iter().nth(m[m.len() - 1]);
181+
182+
if let (Some(f), Some(l)) = (first, last) {
183+
let yank =
184+
format!("{}^..{}", f.hash_short, l.hash_short);
185+
crate::clipboard::copy_string(&yank)?;
186+
};
187+
} else {
188+
let separate = self
189+
.marked_indexes()
190+
.iter()
191+
.map(|e| {
192+
self.items
193+
.iter()
194+
.nth(*e)
195+
.map_or_else(String::new, |le| {
196+
le.hash_short.to_string()
197+
})
198+
})
199+
.join(" ");
200+
201+
crate::clipboard::copy_string(&separate)?;
202+
}
203+
204+
Ok(())
205+
}
206+
146207
pub fn copy_entry_hash(&self) -> Result<()> {
147-
if let Some(e) = self.items.iter().nth(
148-
self.selection.saturating_sub(self.items.index_offset()),
149-
) {
150-
crate::clipboard::copy_string(&e.hash_short)?;
208+
match self.marked_count() {
209+
0 => {
210+
if let Some(e) = self.items.iter().nth(
211+
self.selection
212+
.saturating_sub(self.items.index_offset()),
213+
) {
214+
crate::clipboard::copy_string(&e.hash_short)?;
215+
}
216+
}
217+
1 => {
218+
if let Some(e) =
219+
self.items.iter().nth(self.marked_indexes()[0])
220+
{
221+
crate::clipboard::copy_string(&e.hash_short)?;
222+
}
223+
}
224+
_ => {}
151225
}
152226
Ok(())
153227
}
@@ -191,10 +265,17 @@ impl CommitList {
191265
fn mark(&mut self) {
192266
if let Some(e) = self.selected_entry() {
193267
let id = e.id;
268+
let selected = self
269+
.selection
270+
.saturating_sub(self.items.index_offset());
194271
if self.is_marked(&id).unwrap_or_default() {
195-
self.marked.retain(|marked| marked != &id);
272+
self.marked.retain(|marked| marked.1 != id);
196273
} else {
197-
self.marked.push(id);
274+
self.marked.push((selected, id));
275+
276+
self.marked.sort_unstable_by(|first, second| {
277+
first.0.cmp(&second.0)
278+
});
198279
}
199280
}
200281
}
@@ -227,7 +308,8 @@ impl CommitList {
227308
if self.marked.is_empty() {
228309
None
229310
} else {
230-
let found = self.marked.iter().any(|entry| entry == id);
311+
let found =
312+
self.marked.iter().any(|entry| entry.1 == *id);
231313
Some(found)
232314
}
233315
}

src/tabs/revlog.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,11 @@ impl Revlog {
165165
}
166166

167167
fn copy_commit_hash(&self) -> Result<()> {
168-
self.list.copy_entry_hash()?;
168+
if self.list.marked_count() > 1 {
169+
self.list.copy_marked_hashes()?;
170+
} else {
171+
self.list.copy_entry_hash()?;
172+
}
169173
Ok(())
170174
}
171175

@@ -328,7 +332,7 @@ impl Component for Revlog {
328332
self.queue.push(InternalEvent::OpenPopup(
329333
StackablePopupOpen::CompareCommits(
330334
InspectCommitOpen::new(
331-
self.list.marked()[0],
335+
self.list.marked()[0].1,
332336
),
333337
),
334338
));
@@ -339,8 +343,8 @@ impl Component for Revlog {
339343
self.queue.push(InternalEvent::OpenPopup(
340344
StackablePopupOpen::CompareCommits(
341345
InspectCommitOpen {
342-
commit_id: marked[0],
343-
compare_id: Some(marked[1]),
346+
commit_id: marked[0].1,
347+
compare_id: Some(marked[1].1),
344348
tags: None,
345349
},
346350
),

src/tabs/stashlist.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ impl StashList {
7878
fn drop_stash(&mut self) {
7979
if self.list.marked_count() > 0 {
8080
self.queue.push(InternalEvent::ConfirmAction(
81-
Action::StashDrop(self.list.marked().to_vec()),
81+
Action::StashDrop(self.list.marked_commits()),
8282
));
8383
} else if let Some(e) = self.list.selected_entry() {
8484
self.queue.push(InternalEvent::ConfirmAction(

0 commit comments

Comments
 (0)