@@ -14,8 +14,8 @@ use crate::{
14
14
} ;
15
15
use anyhow:: Result ;
16
16
use asyncgit:: sync:: {
17
- self , checkout_commit, BranchDetails , BranchInfo , CommitId ,
18
- RepoPathRef , Tags ,
17
+ self , checkout_commit, revwalk , BranchDetails , BranchInfo ,
18
+ CommitId , RepoPathRef , Sort , Tags ,
19
19
} ;
20
20
use chrono:: { DateTime , Local } ;
21
21
use crossterm:: event:: Event ;
@@ -29,8 +29,8 @@ use ratatui::{
29
29
Frame ,
30
30
} ;
31
31
use std:: {
32
- borrow:: Cow , cell:: Cell , cmp, collections:: BTreeMap , rc :: Rc ,
33
- time:: Instant ,
32
+ borrow:: Cow , cell:: Cell , cmp, collections:: BTreeMap , ops :: Bound ,
33
+ rc :: Rc , time:: Instant ,
34
34
} ;
35
35
36
36
const ELEMENTS_PER_LINE : usize = 9 ;
@@ -137,37 +137,52 @@ impl CommitList {
137
137
}
138
138
139
139
/// Build string of marked or selected (if none are marked) commit ids
140
- fn concat_selected_commit_ids ( & self ) -> Option < String > {
140
+ fn concat_selected_commit_ids ( & self ) -> Result < Option < String > > {
141
141
match self . marked . as_slice ( ) {
142
- [ ] => self
142
+ [ ] => Ok ( self
143
143
. items
144
144
. iter ( )
145
145
. nth (
146
146
self . selection
147
147
. saturating_sub ( self . items . index_offset ( ) ) ,
148
148
)
149
- . map ( |e| e. id . to_string ( ) ) ,
150
- [ latest, .., earliest]
151
- if self
152
- . marked ( )
153
- . windows ( 2 )
154
- . all ( |w| w[ 0 ] . 0 + 1 == w[ 1 ] . 0 ) =>
155
- {
156
- Some ( format ! ( "{}^..{}" , earliest. 1 , latest. 1 ) )
149
+ . map ( |e| e. id . to_string ( ) ) ) ,
150
+ [ ( _idx, commit) ] => Ok ( Some ( commit. to_string ( ) ) ) ,
151
+ [ latest, .., earliest] => {
152
+ let marked_rev = self . marked . iter ( ) . rev ( ) ;
153
+ let marked_topo_consecutive = revwalk (
154
+ & self . repo . borrow ( ) ,
155
+ Bound :: Excluded ( & earliest. 1 ) ,
156
+ Bound :: Included ( & latest. 1 ) ,
157
+ Sort :: TOPOLOGICAL | Sort :: REVERSE ,
158
+ |revwalk| {
159
+ revwalk. zip ( marked_rev) . try_fold (
160
+ true ,
161
+ |acc, ( r, m) | {
162
+ let revwalked = CommitId :: new ( r?) ;
163
+ let marked = m. 1 ;
164
+ Ok ( acc && ( revwalked == marked) )
165
+ } ,
166
+ )
167
+ } ,
168
+ ) ?;
169
+ let yank = if marked_topo_consecutive {
170
+ format ! ( "{}^..{}" , earliest. 1 , latest. 1 )
171
+ } else {
172
+ self . marked
173
+ . iter ( )
174
+ . map ( |( _idx, commit) | commit. to_string ( ) )
175
+ . join ( " " )
176
+ } ;
177
+ Ok ( Some ( yank) )
157
178
}
158
- marked => Some (
159
- marked
160
- . iter ( )
161
- . map ( |( _idx, commit) | commit. to_string ( ) )
162
- . join ( " " ) ,
163
- ) ,
164
179
}
165
180
}
166
181
167
182
/// Copy currently marked or selected (if none are marked) commit ids
168
183
/// to clipboard
169
184
pub fn copy_commit_hash ( & self ) -> Result < ( ) > {
170
- if let Some ( yank) = self . concat_selected_commit_ids ( ) {
185
+ if let Some ( yank) = self . concat_selected_commit_ids ( ) ? {
171
186
crate :: clipboard:: copy_string ( & yank) ?;
172
187
self . queue . push ( InternalEvent :: ShowInfoMsg (
173
188
strings:: copy_success ( & yank) ,
@@ -1014,7 +1029,9 @@ mod tests {
1014
1029
#[ test]
1015
1030
fn test_copy_commit_list_empty ( ) {
1016
1031
assert_eq ! (
1017
- CommitList :: default ( ) . concat_selected_commit_ids( ) ,
1032
+ CommitList :: default ( )
1033
+ . concat_selected_commit_ids( )
1034
+ . unwrap( ) ,
1018
1035
None
1019
1036
) ;
1020
1037
}
@@ -1029,7 +1046,7 @@ mod tests {
1029
1046
// offset by two, so we expect commit id 2 for
1030
1047
// selection = 4
1031
1048
assert_eq ! (
1032
- cl. concat_selected_commit_ids( ) ,
1049
+ cl. concat_selected_commit_ids( ) . unwrap ( ) ,
1033
1050
Some ( String :: from(
1034
1051
"0000000000000000000000000000000000000002"
1035
1052
) )
@@ -1044,35 +1061,37 @@ mod tests {
1044
1061
..cl
1045
1062
} ;
1046
1063
assert_eq ! (
1047
- cl. concat_selected_commit_ids( ) ,
1064
+ cl. concat_selected_commit_ids( ) . unwrap ( ) ,
1048
1065
Some ( String :: from(
1049
1066
"0000000000000000000000000000000000000001" ,
1050
1067
) )
1051
1068
) ;
1052
1069
}
1053
1070
1054
1071
#[ test]
1072
+ #[ ignore = "needs real repository to run test. Will be moved to revwalk module." ]
1055
1073
fn test_copy_commit_range_marked ( ) {
1056
1074
let cl = build_commit_list_with_some_commits ( ) ;
1057
1075
let cl = CommitList {
1058
1076
marked : build_marked_from_indices ( & cl, & [ 4 , 5 , 6 , 7 ] ) ,
1059
1077
..cl
1060
1078
} ;
1061
1079
assert_eq ! (
1062
- cl. concat_selected_commit_ids( ) ,
1080
+ cl. concat_selected_commit_ids( ) . unwrap ( ) ,
1063
1081
Some ( String :: from( "0000000000000000000000000000000000000005^..0000000000000000000000000000000000000002" ) )
1064
1082
) ;
1065
1083
}
1066
1084
1067
1085
#[ test]
1086
+ #[ ignore = "needs real repository to run test. Will be moved to revwalk module." ]
1068
1087
fn test_copy_commit_random_marked ( ) {
1069
1088
let cl = build_commit_list_with_some_commits ( ) ;
1070
1089
let cl = CommitList {
1071
1090
marked : build_marked_from_indices ( & cl, & [ 4 , 7 ] ) ,
1072
1091
..cl
1073
1092
} ;
1074
1093
assert_eq ! (
1075
- cl. concat_selected_commit_ids( ) ,
1094
+ cl. concat_selected_commit_ids( ) . unwrap ( ) ,
1076
1095
Some ( String :: from( concat!(
1077
1096
"0000000000000000000000000000000000000002 " ,
1078
1097
"0000000000000000000000000000000000000005"
0 commit comments