Skip to content

Commit 89f73d2

Browse files
naseschwarzNaseschwarz
and
Naseschwarz
authored
Disable dotted range commit yanking (#2577)
The algorithm for computing marked_consecutive assumes that commits are consecutive in the commit graph if they are consecutive in the linearized log used in` commitlist.rs`. That is not universally correct, as siblings may also be displayed consecutively in this list. For now, this just removes generating commit lists in dotted range notation. Co-authored-by: Naseschwarz <[email protected]>
1 parent 1563811 commit 89f73d2

File tree

4 files changed

+182
-23
lines changed

4 files changed

+182
-23
lines changed

Diff for: CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2424
* set the terminal title to `gitui ({repo_path})` [[@acuteenvy](https://github.com/acuteenvy)] ([#2462](https://github.com/gitui-org/gitui/issues/2462))
2525
* respect `.mailmap` [[@acuteenvy](https://github.com/acuteenvy)] ([#2406](https://github.com/gitui-org/gitui/issues/2406))
2626

27+
### Fixes
28+
* yanking commit ranges no longer generates incorrect dotted range notations, but lists each individual commit [[@naseschwarz](https://github.com/naseschwarz)] (https://github.com/gitui-org/gitui/issues/2576)
29+
2730
## [0.27.0] - 2024-01-14
2831

2932
**new: manage remotes**

Diff for: asyncgit/src/sync/commits_info.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,18 @@ impl CommitId {
4949
let commit_obj = repo.revparse_single(revision)?;
5050
Ok(commit_obj.id().into())
5151
}
52+
53+
/// Tries to convert a &str representation of a commit id into
54+
/// a `CommitId`
55+
pub fn from_str_unchecked(commit_id_str: &str) -> Result<Self> {
56+
match Oid::from_str(commit_id_str) {
57+
Err(e) => Err(crate::Error::Generic(format!(
58+
"Could not convert {}",
59+
e.message()
60+
))),
61+
Ok(v) => Ok(Self::new(v)),
62+
}
63+
}
5264
}
5365

5466
impl Display for CommitId {
@@ -73,7 +85,7 @@ impl From<Oid> for CommitId {
7385
}
7486

7587
///
76-
#[derive(Debug)]
88+
#[derive(Debug, Clone)]
7789
pub struct CommitInfo {
7890
///
7991
pub message: String,

Diff for: src/components/commitlist.rs

+165-21
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,9 @@ impl CommitList {
132132
commits
133133
}
134134

135-
///
136-
pub fn copy_commit_hash(&self) -> Result<()> {
137-
let marked = self.marked.as_slice();
138-
let yank: Option<String> = match marked {
135+
/// Build string of marked or selected (if none are marked) commit ids
136+
fn concat_selected_commit_ids(&self) -> Option<String> {
137+
match self.marked.as_slice() {
139138
[] => self
140139
.items
141140
.iter()
@@ -144,24 +143,19 @@ impl CommitList {
144143
.saturating_sub(self.items.index_offset()),
145144
)
146145
.map(|e| e.id.to_string()),
147-
[(_idx, commit)] => Some(commit.to_string()),
148-
[first, .., last] => {
149-
let marked_consecutive =
150-
marked.windows(2).all(|w| w[0].0 + 1 == w[1].0);
151-
152-
let yank = if marked_consecutive {
153-
format!("{}^..{}", first.1, last.1)
154-
} else {
155-
marked
156-
.iter()
157-
.map(|(_idx, commit)| commit.to_string())
158-
.join(" ")
159-
};
160-
Some(yank)
161-
}
162-
};
146+
marked => Some(
147+
marked
148+
.iter()
149+
.map(|(_idx, commit)| commit.to_string())
150+
.join(" "),
151+
),
152+
}
153+
}
163154

164-
if let Some(yank) = yank {
155+
/// Copy currently marked or selected (if none are marked) commit ids
156+
/// to clipboard
157+
pub fn copy_commit_hash(&self) -> Result<()> {
158+
if let Some(yank) = self.concat_selected_commit_ids() {
165159
crate::clipboard::copy_string(&yank)?;
166160
self.queue.push(InternalEvent::ShowInfoMsg(
167161
strings::copy_success(&yank),
@@ -893,8 +887,36 @@ impl Component for CommitList {
893887

894888
#[cfg(test)]
895889
mod tests {
890+
use asyncgit::sync::CommitInfo;
891+
896892
use super::*;
897893

894+
impl Default for CommitList {
895+
fn default() -> Self {
896+
Self {
897+
title: String::from("").into_boxed_str(),
898+
selection: 0,
899+
highlighted_selection: Option::None,
900+
highlights: Option::None,
901+
tags: Option::None,
902+
items: ItemBatch::default(),
903+
commits: IndexSet::default(),
904+
marked: Vec::default(),
905+
scroll_top: Cell::default(),
906+
local_branches: BTreeMap::default(),
907+
remote_branches: BTreeMap::default(),
908+
theme: SharedTheme::default(),
909+
key_config: SharedKeyConfig::default(),
910+
scroll_state: (Instant::now(), 0.0),
911+
current_size: Cell::default(),
912+
repo: RepoPathRef::new(sync::RepoPath::Path(
913+
std::path::PathBuf::default(),
914+
)),
915+
queue: Queue::default(),
916+
}
917+
}
918+
}
919+
898920
#[test]
899921
fn test_string_width_align() {
900922
assert_eq!(string_width_align("123", 3), "123");
@@ -916,4 +938,126 @@ mod tests {
916938
"Jon Grythe Stødle "
917939
);
918940
}
941+
942+
/// Build a commit list with a few commits loaded
943+
fn build_commit_list_with_some_commits() -> CommitList {
944+
let mut items = ItemBatch::default();
945+
let basic_commit_info = CommitInfo {
946+
message: String::default(),
947+
time: 0,
948+
author: String::default(),
949+
id: CommitId::default(),
950+
};
951+
// This just creates a sequence of fake ordered ids
952+
// 0000000000000000000000000000000000000000
953+
// 0000000000000000000000000000000000000001
954+
// 0000000000000000000000000000000000000002
955+
// ...
956+
items.set_items(
957+
2, /* randomly choose an offset */
958+
(0..20)
959+
.map(|idx| CommitInfo {
960+
id: CommitId::from_str_unchecked(&format!(
961+
"{idx:040}",
962+
))
963+
.unwrap(),
964+
..basic_commit_info.clone()
965+
})
966+
.collect(),
967+
None,
968+
);
969+
CommitList {
970+
items,
971+
selection: 4, // Randomly select one commit
972+
..Default::default()
973+
}
974+
}
975+
976+
/// Build a value for cl.marked based on indices into cl.items
977+
fn build_marked_from_indices(
978+
cl: &CommitList,
979+
marked_indices: &[usize],
980+
) -> Vec<(usize, CommitId)> {
981+
let offset = cl.items.index_offset();
982+
marked_indices
983+
.iter()
984+
.map(|idx| {
985+
(*idx, cl.items.iter().nth(*idx - offset).unwrap().id)
986+
})
987+
.collect()
988+
}
989+
990+
#[test]
991+
fn test_copy_commit_list_empty() {
992+
assert_eq!(
993+
CommitList::default().concat_selected_commit_ids(),
994+
None
995+
);
996+
}
997+
998+
#[test]
999+
fn test_copy_commit_none_marked() {
1000+
let cl = CommitList {
1001+
selection: 4,
1002+
..build_commit_list_with_some_commits()
1003+
};
1004+
// ids from build_commit_list_with_some_commits() are
1005+
// offset by two, so we expect commit id 2 for
1006+
// selection = 4
1007+
assert_eq!(
1008+
cl.concat_selected_commit_ids(),
1009+
Some(String::from(
1010+
"0000000000000000000000000000000000000002"
1011+
))
1012+
);
1013+
}
1014+
1015+
#[test]
1016+
fn test_copy_commit_one_marked() {
1017+
let cl = build_commit_list_with_some_commits();
1018+
let cl = CommitList {
1019+
marked: build_marked_from_indices(&cl, &[3]),
1020+
..cl
1021+
};
1022+
assert_eq!(
1023+
cl.concat_selected_commit_ids(),
1024+
Some(String::from(
1025+
"0000000000000000000000000000000000000001",
1026+
))
1027+
);
1028+
}
1029+
1030+
#[test]
1031+
fn test_copy_commit_range_marked() {
1032+
let cl = build_commit_list_with_some_commits();
1033+
let cl = CommitList {
1034+
marked: build_marked_from_indices(&cl, &[4, 5, 6, 7]),
1035+
..cl
1036+
};
1037+
assert_eq!(
1038+
cl.concat_selected_commit_ids(),
1039+
Some(String::from(concat!(
1040+
"0000000000000000000000000000000000000002 ",
1041+
"0000000000000000000000000000000000000003 ",
1042+
"0000000000000000000000000000000000000004 ",
1043+
"0000000000000000000000000000000000000005"
1044+
)))
1045+
);
1046+
}
1047+
1048+
#[test]
1049+
fn test_copy_commit_random_marked() {
1050+
let cl = build_commit_list_with_some_commits();
1051+
let cl = CommitList {
1052+
marked: build_marked_from_indices(&cl, &[4, 7]),
1053+
..cl
1054+
};
1055+
assert_eq!(
1056+
cl.concat_selected_commit_ids(),
1057+
Some(String::from(concat!(
1058+
"0000000000000000000000000000000000000002 ",
1059+
"0000000000000000000000000000000000000005"
1060+
)))
1061+
);
1062+
}
9191063
}

Diff for: src/queue.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ pub enum InternalEvent {
160160
}
161161

162162
/// single threaded simple queue for components to communicate with each other
163-
#[derive(Clone)]
163+
#[derive(Clone, Default)]
164164
pub struct Queue {
165165
data: Rc<RefCell<VecDeque<InternalEvent>>>,
166166
}

0 commit comments

Comments
 (0)