1
1
use std:: collections:: HashMap ;
2
2
use std:: process:: Command ;
3
3
4
- use anyhow:: { anyhow, bail} ;
4
+ use anyhow:: { anyhow, bail, Context } ;
5
5
use console:: style;
6
6
use dialoguer:: { Confirm , Select } ;
7
7
use git2:: { Branch , Commit , Diff , Object , ObjectType , Oid , Rebase , Repository } ;
@@ -20,21 +20,42 @@ pub fn instafix(
20
20
) -> Result < ( ) , anyhow:: Error > {
21
21
let repo = Repository :: open ( "." ) ?;
22
22
let diff = create_diff ( & repo) ?;
23
- let head = repo
24
- . head ( )
25
- . map_err ( |e| anyhow ! ( "HEAD is not pointing at a valid branch: {}" , e) ) ?;
23
+ let head = repo. head ( ) . context ( "finding head commit" ) ?;
26
24
let head_branch = Branch :: wrap ( head) ;
27
- println ! ( "head_branch: {:?}" , head_branch. name( ) . unwrap( ) . unwrap( ) ) ;
28
25
let upstream = get_upstream ( & repo, & head_branch, upstream_branch_name) ?;
29
26
let commit_to_amend = select_commit_to_amend ( & repo, upstream, max_commits, & message_pattern) ?;
27
+ eprintln ! ( "Selected {}" , disp( & commit_to_amend) ) ;
30
28
do_fixup_commit ( & repo, & head_branch, & commit_to_amend, false ) ?;
31
- println ! ( "selected: {}" , disp( & commit_to_amend) ) ;
32
29
let current_branch = Branch :: wrap ( repo. head ( ) ?) ;
33
30
do_rebase ( & repo, & current_branch, & commit_to_amend, & diff) ?;
34
31
35
32
Ok ( ( ) )
36
33
}
37
34
35
+ pub fn rebase_onto ( onto : & str ) -> Result < ( ) , anyhow:: Error > {
36
+ let repo = Repository :: open ( "." ) ?;
37
+ let onto = repo
38
+ . reference_to_annotated_commit (
39
+ & repo
40
+ . find_branch ( onto, git2:: BranchType :: Local )
41
+ . context ( "Chosing parent" ) ?
42
+ . get ( ) ,
43
+ )
44
+ . context ( "creating onto annotated commit" ) ?;
45
+ let head = repo
46
+ . reference_to_annotated_commit ( & repo. head ( ) . context ( "finding head" ) ?)
47
+ . context ( "choosing branch" ) ?;
48
+ let rebase = & mut repo
49
+ . rebase ( Some ( & head) , None , Some ( & onto) , None )
50
+ . context ( "creating rebase" ) ?;
51
+
52
+ if let Ok ( _) = do_rebase_inner ( & repo, rebase, None ) {
53
+ rebase. finish ( None ) . context ( "finishing" ) ?;
54
+ }
55
+
56
+ Ok ( ( ) )
57
+ }
58
+
38
59
fn do_rebase (
39
60
repo : & Repository ,
40
61
branch : & Branch ,
@@ -48,8 +69,11 @@ fn do_rebase(
48
69
49
70
let rebase = & mut repo
50
71
. rebase ( Some ( & branch_commit) , Some ( & first_parent) , None , None )
51
- . map_err ( |e| anyhow ! ( "Error starting rebase: {}" , e) ) ?;
52
- match do_rebase_inner ( repo, rebase, diff, fixup_message) {
72
+ . context ( "starting rebase" ) ?;
73
+
74
+ apply_diff_in_rebase ( repo, rebase, diff) ?;
75
+
76
+ match do_rebase_inner ( repo, rebase, fixup_message) {
53
77
Ok ( _) => {
54
78
rebase. finish ( None ) ?;
55
79
Ok ( ( ) )
@@ -66,21 +90,11 @@ fn do_rebase(
66
90
}
67
91
}
68
92
69
- fn do_rebase_inner (
93
+ fn apply_diff_in_rebase (
70
94
repo : & Repository ,
71
95
rebase : & mut Rebase ,
72
96
diff : & Diff ,
73
- fixup_message : Option < & str > ,
74
97
) -> Result < ( ) , anyhow:: Error > {
75
- let sig = repo. signature ( ) ?;
76
-
77
- let mut branches: HashMap < Oid , Branch > = HashMap :: new ( ) ;
78
- for ( branch, _type) in repo. branches ( Some ( git2:: BranchType :: Local ) ) ?. flatten ( ) {
79
- let oid = branch. get ( ) . peel_to_commit ( ) ?. id ( ) ;
80
- // TODO: handle multiple branches pointing to the same commit
81
- branches. insert ( oid, branch) ;
82
- }
83
-
84
98
match rebase. next ( ) {
85
99
Some ( ref res) => {
86
100
let op = res. as_ref ( ) . map_err ( |e| anyhow ! ( "No commit: {}" , e) ) ?;
@@ -98,11 +112,26 @@ fn do_rebase_inner(
98
112
git2:: ResetType :: Soft ,
99
113
None ,
100
114
) ?;
101
-
102
- rewrit_id
103
115
}
104
116
None => bail ! ( "Unable to start rebase: no first step in rebase" ) ,
105
117
} ;
118
+ Ok ( ( ) )
119
+ }
120
+
121
+ /// Do a rebase, pulling all intermediate branches along the way
122
+ fn do_rebase_inner (
123
+ repo : & Repository ,
124
+ rebase : & mut Rebase ,
125
+ fixup_message : Option < & str > ,
126
+ ) -> Result < ( ) , anyhow:: Error > {
127
+ let sig = repo. signature ( ) ?;
128
+
129
+ let mut branches: HashMap < Oid , Branch > = HashMap :: new ( ) ;
130
+ for ( branch, _type) in repo. branches ( Some ( git2:: BranchType :: Local ) ) ?. flatten ( ) {
131
+ let oid = branch. get ( ) . peel_to_commit ( ) ?. id ( ) ;
132
+ // TODO: handle multiple branches pointing to the same commit
133
+ branches. insert ( oid, branch) ;
134
+ }
106
135
107
136
while let Some ( ref res) = rebase. next ( ) {
108
137
use git2:: RebaseOperationType :: * ;
@@ -111,12 +140,17 @@ fn do_rebase_inner(
111
140
match op. kind ( ) {
112
141
Some ( Pick ) => {
113
142
let commit = repo. find_commit ( op. id ( ) ) ?;
114
- if commit. message ( ) != fixup_message {
143
+ let message = commit. message ( ) ;
144
+ if message. is_some ( ) && message != fixup_message {
115
145
let new_id = rebase. commit ( None , & sig, None ) ?;
116
146
if let Some ( branch) = branches. get_mut ( & commit. id ( ) ) {
117
- branch
118
- . get_mut ( )
119
- . set_target ( new_id, "git-fixup retarget historical branch" ) ?;
147
+ // Don't retarget the last branch, rebase.finish does that for us
148
+ // TODO: handle multiple branches
149
+ if rebase. operation_current ( ) != Some ( rebase. len ( ) - 1 ) {
150
+ branch
151
+ . get_mut ( )
152
+ . set_target ( new_id, "git-fixup retarget historical branch" ) ?;
153
+ }
120
154
}
121
155
}
122
156
}
0 commit comments