@@ -61,37 +61,134 @@ impl CliId {
61
61
}
62
62
}
63
63
64
+ fn find_branches_by_name ( ctx : & CommandContext , name : & str ) -> anyhow:: Result < Vec < Self > > {
65
+ let stacks = crate :: log:: stacks ( ctx) ?;
66
+ let mut matches = Vec :: new ( ) ;
67
+
68
+ for stack in stacks {
69
+ for head in & stack. heads {
70
+ let branch_name = head. name . to_string ( ) ;
71
+ // Exact match or partial match
72
+ if branch_name == name || branch_name. contains ( name) {
73
+ matches. push ( CliId :: branch ( & branch_name) ) ;
74
+ }
75
+ }
76
+ }
77
+
78
+ Ok ( matches)
79
+ }
80
+
81
+ fn find_commits_by_sha ( ctx : & CommandContext , sha_prefix : & str ) -> anyhow:: Result < Vec < Self > > {
82
+ let mut matches = Vec :: new ( ) ;
83
+
84
+ // Only try SHA matching if the input looks like a hex string
85
+ if sha_prefix. chars ( ) . all ( |c| c. is_ascii_hexdigit ( ) ) && sha_prefix. len ( ) >= 4 {
86
+ let all_commits = crate :: log:: all_commits ( ctx) ?;
87
+ for commit_id in all_commits {
88
+ if let CliId :: Commit { oid } = & commit_id {
89
+ let sha_string = oid. to_string ( ) ;
90
+ if sha_string. starts_with ( sha_prefix) {
91
+ matches. push ( commit_id) ;
92
+ }
93
+ }
94
+ }
95
+ }
96
+
97
+ Ok ( matches)
98
+ }
99
+
64
100
pub fn matches ( & self , s : & str ) -> bool {
65
101
s == self . to_string ( )
66
102
}
67
103
104
+ pub fn matches_prefix ( & self , s : & str ) -> bool {
105
+ match self {
106
+ CliId :: Commit { oid } => {
107
+ let oid_hash = hash ( & oid. to_string ( ) ) ;
108
+ oid_hash. starts_with ( s)
109
+ }
110
+ _ => self . to_string ( ) . starts_with ( s) ,
111
+ }
112
+ }
113
+
68
114
pub fn from_str ( ctx : & mut CommandContext , s : & str ) -> anyhow:: Result < Vec < Self > > {
69
115
if s. len ( ) < 2 {
70
- return Err ( anyhow:: anyhow!( "Id needs to be 3 characters long: {}" , s) ) ;
116
+ return Err ( anyhow:: anyhow!(
117
+ "Id needs to be at least 2 characters long: {}" ,
118
+ s
119
+ ) ) ;
71
120
}
72
- let s = & s[ ..2 ] ;
73
- let mut everything = Vec :: new ( ) ;
74
- crate :: status:: all_files ( ctx) ?
75
- . into_iter ( )
76
- . filter ( |id| id. matches ( s) )
77
- . for_each ( |id| everything. push ( id) ) ;
78
- crate :: status:: all_branches ( ctx) ?
79
- . into_iter ( )
80
- . filter ( |id| id. matches ( s) )
81
- . for_each ( |id| everything. push ( id) ) ;
82
- crate :: log:: all_commits ( ctx) ?
83
- . into_iter ( )
84
- . filter ( |id| id. matches ( s) )
85
- . for_each ( |id| everything. push ( id) ) ;
86
- everything. push ( CliId :: unassigned ( ) ) ;
87
121
88
122
let mut matches = Vec :: new ( ) ;
89
- for id in everything {
90
- if id. matches ( s) {
91
- matches. push ( id) ;
123
+
124
+ // First, try exact branch name match
125
+ if let Ok ( branch_matches) = Self :: find_branches_by_name ( ctx, s) {
126
+ matches. extend ( branch_matches) ;
127
+ }
128
+
129
+ // Then try partial SHA matches (for commits)
130
+ if let Ok ( commit_matches) = Self :: find_commits_by_sha ( ctx, s) {
131
+ matches. extend ( commit_matches) ;
132
+ }
133
+
134
+ // Then try CliId matching (both prefix and exact)
135
+ if s. len ( ) > 2 {
136
+ // For longer strings, try prefix matching on CliIds
137
+ let mut cli_matches = Vec :: new ( ) ;
138
+ crate :: status:: all_files ( ctx) ?
139
+ . into_iter ( )
140
+ . filter ( |id| id. matches_prefix ( s) )
141
+ . for_each ( |id| cli_matches. push ( id) ) ;
142
+ crate :: status:: all_committed_files ( ctx) ?
143
+ . into_iter ( )
144
+ . filter ( |id| id. matches_prefix ( s) )
145
+ . for_each ( |id| cli_matches. push ( id) ) ;
146
+ crate :: status:: all_branches ( ctx) ?
147
+ . into_iter ( )
148
+ . filter ( |id| id. matches_prefix ( s) )
149
+ . for_each ( |id| cli_matches. push ( id) ) ;
150
+ crate :: log:: all_commits ( ctx) ?
151
+ . into_iter ( )
152
+ . filter ( |id| id. matches_prefix ( s) )
153
+ . for_each ( |id| cli_matches. push ( id) ) ;
154
+ if CliId :: unassigned ( ) . matches_prefix ( s) {
155
+ cli_matches. push ( CliId :: unassigned ( ) ) ;
156
+ }
157
+ matches. extend ( cli_matches) ;
158
+ } else {
159
+ // For 2-character strings, try exact CliId matching
160
+ let mut cli_matches = Vec :: new ( ) ;
161
+ crate :: status:: all_files ( ctx) ?
162
+ . into_iter ( )
163
+ . filter ( |id| id. matches ( s) )
164
+ . for_each ( |id| cli_matches. push ( id) ) ;
165
+ crate :: status:: all_committed_files ( ctx) ?
166
+ . into_iter ( )
167
+ . filter ( |id| id. matches ( s) )
168
+ . for_each ( |id| cli_matches. push ( id) ) ;
169
+ crate :: status:: all_branches ( ctx) ?
170
+ . into_iter ( )
171
+ . filter ( |id| id. matches ( s) )
172
+ . for_each ( |id| cli_matches. push ( id) ) ;
173
+ crate :: log:: all_commits ( ctx) ?
174
+ . into_iter ( )
175
+ . filter ( |id| id. matches ( s) )
176
+ . for_each ( |id| cli_matches. push ( id) ) ;
177
+ if CliId :: unassigned ( ) . matches ( s) {
178
+ cli_matches. push ( CliId :: unassigned ( ) ) ;
92
179
}
180
+ matches. extend ( cli_matches) ;
93
181
}
94
- Ok ( matches)
182
+
183
+ // Remove duplicates while preserving order
184
+ let mut unique_matches = Vec :: new ( ) ;
185
+ for m in matches {
186
+ if !unique_matches. contains ( & m) {
187
+ unique_matches. push ( m) ;
188
+ }
189
+ }
190
+
191
+ Ok ( unique_matches)
95
192
}
96
193
}
97
194
@@ -117,6 +214,8 @@ impl Display for CliId {
117
214
write ! ( f, "00" )
118
215
}
119
216
CliId :: Commit { oid } => {
217
+ // let oid_str = oid.to_string();
218
+ // write!(f, "{}", hash(&oid_str))
120
219
let oid = oid. to_string ( ) ;
121
220
write ! ( f, "{}" , & oid[ ..2 ] )
122
221
}
@@ -129,12 +228,15 @@ pub(crate) fn hash(input: &str) -> String {
129
228
for byte in input. bytes ( ) {
130
229
hash = hash. wrapping_mul ( 31 ) . wrapping_add ( byte as u64 ) ;
131
230
}
132
- // Convert to base 36 (0-9, a-z)
133
- let chars = "0123456789abcdefghijklmnopqrstuvwxyz" ;
134
- let mut result = String :: new ( ) ;
135
- for _ in 0 ..2 {
136
- result. push ( chars. chars ( ) . nth ( ( hash % 36 ) as usize ) . unwrap ( ) ) ;
137
- hash /= 36 ;
138
- }
139
- result
231
+
232
+ // First character: g-z (20 options)
233
+ let first_chars = "ghijklmnopqrstuvwxyz" ;
234
+ let first_char = first_chars. chars ( ) . nth ( ( hash % 20 ) as usize ) . unwrap ( ) ;
235
+ hash /= 20 ;
236
+
237
+ // Second character: 0-9,a-z (36 options)
238
+ let second_chars = "0123456789abcdefghijklmnopqrstuvwxyz" ;
239
+ let second_char = second_chars. chars ( ) . nth ( ( hash % 36 ) as usize ) . unwrap ( ) ;
240
+
241
+ format ! ( "{first_char}{second_char}" )
140
242
}
0 commit comments