@@ -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 ;
@@ -131,37 +131,52 @@ impl CommitList {
131
131
}
132
132
133
133
/// Build string of marked or selected (if none are marked) commit ids
134
- fn concat_selected_commit_ids ( & self ) -> Option < String > {
134
+ fn concat_selected_commit_ids ( & self ) -> Result < Option < String > > {
135
135
match self . marked . as_slice ( ) {
136
- [ ] => self
136
+ [ ] => Ok ( self
137
137
. items
138
138
. iter ( )
139
139
. nth (
140
140
self . selection
141
141
. saturating_sub ( self . items . index_offset ( ) ) ,
142
142
)
143
- . map ( |e| e. id . to_string ( ) ) ,
144
- [ latest, .., earliest]
145
- if self
146
- . marked ( )
147
- . windows ( 2 )
148
- . all ( |w| w[ 0 ] . 0 + 1 == w[ 1 ] . 0 ) =>
149
- {
150
- Some ( format ! ( "{}^..{}" , earliest. 1 , latest. 1 ) )
143
+ . map ( |e| e. id . to_string ( ) ) ) ,
144
+ [ ( _idx, commit) ] => Ok ( Some ( commit. to_string ( ) ) ) ,
145
+ [ latest, .., earliest] => {
146
+ let marked_rev = self . marked . iter ( ) . rev ( ) ;
147
+ let marked_topo_consecutive = revwalk (
148
+ & self . repo . borrow ( ) ,
149
+ Bound :: Excluded ( & earliest. 1 ) ,
150
+ Bound :: Included ( & latest. 1 ) ,
151
+ Sort :: TOPOLOGICAL | Sort :: REVERSE ,
152
+ |revwalk| {
153
+ revwalk. zip ( marked_rev) . try_fold (
154
+ true ,
155
+ |acc, ( r, m) | {
156
+ let revwalked = CommitId :: new ( r?) ;
157
+ let marked = m. 1 ;
158
+ Ok ( acc && ( revwalked == marked) )
159
+ } ,
160
+ )
161
+ } ,
162
+ ) ?;
163
+ let yank = if marked_topo_consecutive {
164
+ format ! ( "{}^..{}" , earliest. 1 , latest. 1 )
165
+ } else {
166
+ self . marked
167
+ . iter ( )
168
+ . map ( |( _idx, commit) | commit. to_string ( ) )
169
+ . join ( " " )
170
+ } ;
171
+ Ok ( Some ( yank) )
151
172
}
152
- marked => Some (
153
- marked
154
- . iter ( )
155
- . map ( |( _idx, commit) | commit. to_string ( ) )
156
- . join ( " " ) ,
157
- ) ,
158
173
}
159
174
}
160
175
161
176
/// Copy currently marked or selected (if none are marked) commit ids
162
177
/// to clipboard
163
178
pub fn copy_commit_hash ( & self ) -> Result < ( ) > {
164
- if let Some ( yank) = self . concat_selected_commit_ids ( ) {
179
+ if let Some ( yank) = self . concat_selected_commit_ids ( ) ? {
165
180
crate :: clipboard:: copy_string ( & yank) ?;
166
181
self . queue . push ( InternalEvent :: ShowInfoMsg (
167
182
strings:: copy_success ( & yank) ,
@@ -1008,7 +1023,9 @@ mod tests {
1008
1023
#[ test]
1009
1024
fn test_copy_commit_list_empty ( ) {
1010
1025
assert_eq ! (
1011
- CommitList :: default ( ) . concat_selected_commit_ids( ) ,
1026
+ CommitList :: default ( )
1027
+ . concat_selected_commit_ids( )
1028
+ . unwrap( ) ,
1012
1029
None
1013
1030
) ;
1014
1031
}
@@ -1023,7 +1040,7 @@ mod tests {
1023
1040
// offset by two, so we expect commit id 2 for
1024
1041
// selection = 4
1025
1042
assert_eq ! (
1026
- cl. concat_selected_commit_ids( ) ,
1043
+ cl. concat_selected_commit_ids( ) . unwrap ( ) ,
1027
1044
Some ( String :: from(
1028
1045
"0000000000000000000000000000000000000002"
1029
1046
) )
@@ -1038,35 +1055,37 @@ mod tests {
1038
1055
..cl
1039
1056
} ;
1040
1057
assert_eq ! (
1041
- cl. concat_selected_commit_ids( ) ,
1058
+ cl. concat_selected_commit_ids( ) . unwrap ( ) ,
1042
1059
Some ( String :: from(
1043
1060
"0000000000000000000000000000000000000001" ,
1044
1061
) )
1045
1062
) ;
1046
1063
}
1047
1064
1048
1065
#[ test]
1066
+ #[ ignore = "needs real repository to run test. Will be moved to revwalk module." ]
1049
1067
fn test_copy_commit_range_marked ( ) {
1050
1068
let cl = build_commit_list_with_some_commits ( ) ;
1051
1069
let cl = CommitList {
1052
1070
marked : build_marked_from_indices ( & cl, & [ 4 , 5 , 6 , 7 ] ) ,
1053
1071
..cl
1054
1072
} ;
1055
1073
assert_eq ! (
1056
- cl. concat_selected_commit_ids( ) ,
1074
+ cl. concat_selected_commit_ids( ) . unwrap ( ) ,
1057
1075
Some ( String :: from( "0000000000000000000000000000000000000005^..0000000000000000000000000000000000000002" ) )
1058
1076
) ;
1059
1077
}
1060
1078
1061
1079
#[ test]
1080
+ #[ ignore = "needs real repository to run test. Will be moved to revwalk module." ]
1062
1081
fn test_copy_commit_random_marked ( ) {
1063
1082
let cl = build_commit_list_with_some_commits ( ) ;
1064
1083
let cl = CommitList {
1065
1084
marked : build_marked_from_indices ( & cl, & [ 4 , 7 ] ) ,
1066
1085
..cl
1067
1086
} ;
1068
1087
assert_eq ! (
1069
- cl. concat_selected_commit_ids( ) ,
1088
+ cl. concat_selected_commit_ids( ) . unwrap ( ) ,
1070
1089
Some ( String :: from( concat!(
1071
1090
"0000000000000000000000000000000000000002 " ,
1072
1091
"0000000000000000000000000000000000000005"
0 commit comments