@@ -4,28 +4,32 @@ use anyhow::{anyhow, bail, Context};
4
4
use console:: style;
5
5
use dialoguer:: { Confirm , Select } ;
6
6
use git2:: {
7
- Branch , Commit , Diff , DiffFormat , DiffStatsFormat , Object , ObjectType , Oid , Rebase , Repository ,
7
+ Branch , BranchType , Commit , Diff , DiffFormat , DiffStatsFormat , Object , Oid , Rebase , Repository ,
8
8
} ;
9
9
use syntect:: easy:: HighlightLines ;
10
10
use syntect:: highlighting:: ThemeSet ;
11
11
use syntect:: parsing:: SyntaxSet ;
12
12
use syntect:: util:: as_24_bit_terminal_escaped;
13
13
14
+ const DEFAULT_UPSTREAM_BRANCHES : & [ & str ] = & [ "main" , "master" , "develop" , "trunk" ] ;
15
+
14
16
pub fn instafix (
15
17
squash : bool ,
16
18
max_commits : usize ,
17
19
message_pattern : Option < String > ,
18
20
upstream_branch_name : Option < & str > ,
19
21
require_newline : bool ,
20
22
) -> Result < ( ) , anyhow:: Error > {
21
- let repo = Repository :: open ( "." ) ?;
22
- let diff = create_diff ( & repo, require_newline) ?;
23
+ let repo = Repository :: open ( "." ) . context ( "opening repo" ) ?;
24
+ let diff = create_diff ( & repo, require_newline) . context ( "creating diff" ) ?;
23
25
let head = repo. head ( ) . context ( "finding head commit" ) ?;
24
26
let head_branch = Branch :: wrap ( head) ;
25
- let upstream = get_upstream ( & repo, & head_branch, upstream_branch_name) ?;
26
- let commit_to_amend = select_commit_to_amend ( & repo, upstream, max_commits, & message_pattern) ?;
27
+ let upstream =
28
+ get_merge_base ( & repo, & head_branch, upstream_branch_name) . context ( "creating merge base" ) ?;
29
+ let commit_to_amend = select_commit_to_amend ( & repo, upstream, max_commits, & message_pattern)
30
+ . context ( "selecting commit to amend" ) ?;
27
31
eprintln ! ( "Selected {}" , disp( & commit_to_amend) ) ;
28
- do_fixup_commit ( & repo, & head_branch, & commit_to_amend, squash) ?;
32
+ do_fixup_commit ( & repo, & head_branch, & commit_to_amend, squash) . context ( "doing fixup commit" ) ?;
29
33
let needs_stash = worktree_is_dirty ( & repo) ?;
30
34
if needs_stash {
31
35
// TODO: is it reasonable to create a new repo to work around lifetime issues?
@@ -190,24 +194,21 @@ fn disp(commit: &Commit) -> String {
190
194
)
191
195
}
192
196
193
- fn get_upstream < ' a > (
197
+ fn get_merge_base < ' a > (
194
198
repo : & ' a Repository ,
195
199
head_branch : & ' a Branch ,
196
200
upstream_name : Option < & str > ,
197
201
) -> Result < Option < Object < ' a > > , anyhow:: Error > {
198
- let upstream = if let Some ( upstream_name) = upstream_name {
199
- let branch = repo
200
- . branches ( None ) ?
201
- . filter_map ( |branch| branch. ok ( ) . map ( |( b, _type) | b) )
202
- . find ( |b| {
203
- b. name ( )
204
- . map ( |n| n. expect ( "valid utf8 branchname" ) == upstream_name)
205
- . unwrap_or ( false )
206
- } )
207
- . ok_or_else ( || anyhow ! ( "cannot find branch with name {:?}" , upstream_name) ) ?;
208
- branch. into_reference ( ) . peel ( ObjectType :: Commit ) ?
202
+ let upstream = if let Some ( explicit_upstream_name) = upstream_name {
203
+ let branch = repo. find_branch ( explicit_upstream_name, BranchType :: Local ) ?;
204
+ branch. into_reference ( ) . peel_to_commit ( ) ?
205
+ } else if let Some ( branch) = DEFAULT_UPSTREAM_BRANCHES
206
+ . iter ( )
207
+ . find_map ( |b| repo. find_branch ( b, BranchType :: Local ) . ok ( ) )
208
+ {
209
+ branch. into_reference ( ) . peel_to_commit ( ) ?
209
210
} else if let Ok ( upstream) = head_branch. upstream ( ) {
210
- upstream. into_reference ( ) . peel ( ObjectType :: Commit ) ?
211
+ upstream. into_reference ( ) . peel_to_commit ( ) ?
211
212
} else {
212
213
return Ok ( None ) ;
213
214
} ;
@@ -335,6 +336,8 @@ fn select_commit_to_amend<'a>(
335
336
} )
336
337
. collect ( ) ;
337
338
if let Some ( message_pattern) = message_pattern. as_ref ( ) {
339
+ let first = commit_id_and_summary ( & commits, commits. len ( ) - 1 ) ;
340
+ let last = commit_id_and_summary ( & commits, 0 ) ;
338
341
commits
339
342
. into_iter ( )
340
343
. find ( |commit| {
@@ -343,7 +346,13 @@ fn select_commit_to_amend<'a>(
343
346
. map ( |s| s. contains ( message_pattern) )
344
347
. unwrap_or ( false )
345
348
} )
346
- . ok_or_else ( || anyhow:: anyhow!( "No commit contains the pattern in its summary" ) )
349
+ . ok_or_else ( || {
350
+ anyhow:: anyhow!(
351
+ "No commit contains the pattern in its summary between {}..{}" ,
352
+ first,
353
+ last
354
+ )
355
+ } )
347
356
} else {
348
357
let rev_aliases = commits
349
358
. iter ( )
@@ -375,6 +384,20 @@ fn select_commit_to_amend<'a>(
375
384
}
376
385
}
377
386
387
+ fn commit_id_and_summary ( commits : & [ Commit < ' _ > ] , idx : usize ) -> String {
388
+ let first = commits
389
+ . get ( idx)
390
+ . map ( |c| {
391
+ format ! (
392
+ "{} ({})" ,
393
+ & c. id( ) . to_string( ) [ ..10 ] ,
394
+ c. summary( ) . unwrap_or( "<unknown>" )
395
+ )
396
+ } )
397
+ . unwrap_or_else ( || "<unknown>" . into ( ) ) ;
398
+ first
399
+ }
400
+
378
401
fn format_ref ( rf : & git2:: Reference < ' _ > ) -> Result < String , anyhow:: Error > {
379
402
let shorthand = rf. shorthand ( ) . unwrap_or ( "<unnamed>" ) ;
380
403
let sha = rf. peel_to_commit ( ) ?. id ( ) . to_string ( ) ;
0 commit comments