@@ -12,7 +12,7 @@ use super::CommitId;
12
12
pub fn rebase_branch (
13
13
repo_path : & str ,
14
14
branch : & str ,
15
- ) -> Result < CommitId > {
15
+ ) -> Result < RebaseState > {
16
16
scope_time ! ( "rebase_branch" ) ;
17
17
18
18
let repo = utils:: repo ( repo_path) ?;
@@ -23,13 +23,13 @@ pub fn rebase_branch(
23
23
fn rebase_branch_repo (
24
24
repo : & Repository ,
25
25
branch_name : & str ,
26
- ) -> Result < CommitId > {
26
+ ) -> Result < RebaseState > {
27
27
let branch = repo. find_branch ( branch_name, BranchType :: Local ) ?;
28
28
29
29
let annotated =
30
30
repo. reference_to_annotated_commit ( & branch. into_reference ( ) ) ?;
31
31
32
- conflict_free_rebase ( repo, & annotated)
32
+ rebase ( repo, & annotated)
33
33
}
34
34
35
35
/// rebase attempt which aborts and undo's rebase if any conflict appears
@@ -66,16 +66,133 @@ pub fn conflict_free_rebase(
66
66
} )
67
67
}
68
68
69
+ ///
70
+ #[ derive( PartialEq , Debug ) ]
71
+ pub enum RebaseState {
72
+ ///
73
+ Finished ,
74
+ ///
75
+ Conflicted ,
76
+ }
77
+
78
+ /// rebase
79
+ pub fn rebase (
80
+ repo : & git2:: Repository ,
81
+ commit : & git2:: AnnotatedCommit ,
82
+ ) -> Result < RebaseState > {
83
+ let mut rebase = repo. rebase ( None , Some ( commit) , None , None ) ?;
84
+ let signature =
85
+ crate :: sync:: commit:: signature_allow_undefined_name ( repo) ?;
86
+
87
+ while let Some ( op) = rebase. next ( ) {
88
+ let _op = op?;
89
+ // dbg!(op.id());
90
+
91
+ if repo. index ( ) ?. has_conflicts ( ) {
92
+ return Ok ( RebaseState :: Conflicted ) ;
93
+ }
94
+
95
+ rebase. commit ( None , & signature, None ) ?;
96
+ }
97
+
98
+ if repo. index ( ) ?. has_conflicts ( ) {
99
+ return Ok ( RebaseState :: Conflicted ) ;
100
+ }
101
+
102
+ rebase. finish ( Some ( & signature) ) ?;
103
+
104
+ Ok ( RebaseState :: Finished )
105
+ }
106
+
107
+ /// continue pending rebase
108
+ pub fn continue_rebase (
109
+ repo : & git2:: Repository ,
110
+ ) -> Result < RebaseState > {
111
+ let mut rebase = repo. open_rebase ( None ) ?;
112
+ let signature =
113
+ crate :: sync:: commit:: signature_allow_undefined_name ( repo) ?;
114
+
115
+ if repo. index ( ) ?. has_conflicts ( ) {
116
+ return Ok ( RebaseState :: Conflicted ) ;
117
+ }
118
+
119
+ // try commit current rebase step
120
+ if !repo. index ( ) ?. is_empty ( ) {
121
+ rebase. commit ( None , & signature, None ) ?;
122
+ }
123
+
124
+ while let Some ( op) = rebase. next ( ) {
125
+ let _op = op?;
126
+ // dbg!(op.id());
127
+
128
+ if repo. index ( ) ?. has_conflicts ( ) {
129
+ return Ok ( RebaseState :: Conflicted ) ;
130
+ }
131
+
132
+ rebase. commit ( None , & signature, None ) ?;
133
+ }
134
+
135
+ if repo. index ( ) ?. has_conflicts ( ) {
136
+ return Ok ( RebaseState :: Conflicted ) ;
137
+ }
138
+
139
+ rebase. finish ( Some ( & signature) ) ?;
140
+
141
+ Ok ( RebaseState :: Finished )
142
+ }
143
+
144
+ ///
145
+ #[ derive( PartialEq , Debug ) ]
146
+ pub struct RebaseProgress {
147
+ ///
148
+ pub steps : usize ,
149
+ ///
150
+ pub current : usize ,
151
+ ///
152
+ pub current_commit : Option < CommitId > ,
153
+ }
154
+
155
+ ///
156
+ pub fn get_rebase_progress (
157
+ repo : & git2:: Repository ,
158
+ ) -> Result < RebaseProgress > {
159
+ let mut rebase = repo. open_rebase ( None ) ?;
160
+
161
+ let current_commit: Option < CommitId > = rebase
162
+ . operation_current ( )
163
+ . and_then ( |idx| rebase. nth ( idx) )
164
+ . map ( |op| op. id ( ) . into ( ) ) ;
165
+
166
+ let progress = RebaseProgress {
167
+ steps : rebase. len ( ) ,
168
+ current : rebase. operation_current ( ) . unwrap_or_default ( ) ,
169
+ current_commit,
170
+ } ;
171
+
172
+ Ok ( progress)
173
+ }
174
+
175
+ ///
176
+ pub fn abort_rebase ( repo : & git2:: Repository ) -> Result < ( ) > {
177
+ let mut rebase = repo. open_rebase ( None ) ?;
178
+
179
+ rebase. abort ( ) ?;
180
+
181
+ Ok ( ( ) )
182
+ }
183
+
69
184
#[ cfg( test) ]
70
- mod tests {
185
+ mod test_conflict_free_rebase {
71
186
use crate :: sync:: {
72
187
checkout_branch, create_branch,
73
- rebase:: rebase_branch,
188
+ rebase:: { rebase_branch, RebaseState } ,
74
189
repo_state,
75
190
tests:: { repo_init, write_commit_file} ,
76
- CommitId , RepoState ,
191
+ utils , CommitId , RepoState ,
77
192
} ;
78
- use git2:: Repository ;
193
+ use git2:: { BranchType , Repository } ;
194
+
195
+ use super :: conflict_free_rebase;
79
196
80
197
fn parent_ids ( repo : & Repository , c : CommitId ) -> Vec < CommitId > {
81
198
let foo = repo
@@ -88,6 +205,23 @@ mod tests {
88
205
foo
89
206
}
90
207
208
+ ///
209
+ fn test_rebase_branch_repo (
210
+ repo_path : & str ,
211
+ branch_name : & str ,
212
+ ) -> CommitId {
213
+ let repo = utils:: repo ( repo_path) . unwrap ( ) ;
214
+
215
+ let branch =
216
+ repo. find_branch ( branch_name, BranchType :: Local ) . unwrap ( ) ;
217
+
218
+ let annotated = repo
219
+ . reference_to_annotated_commit ( & branch. into_reference ( ) )
220
+ . unwrap ( ) ;
221
+
222
+ conflict_free_rebase ( & repo, & annotated) . unwrap ( )
223
+ }
224
+
91
225
#[ test]
92
226
fn test_smoke ( ) {
93
227
let ( _td, repo) = repo_init ( ) . unwrap ( ) ;
@@ -111,7 +245,7 @@ mod tests {
111
245
112
246
checkout_branch ( repo_path, "refs/heads/foo" ) . unwrap ( ) ;
113
247
114
- let r = rebase_branch ( repo_path, "master" ) . unwrap ( ) ;
248
+ let r = test_rebase_branch_repo ( repo_path, "master" ) ;
115
249
116
250
assert_eq ! ( parent_ids( & repo, r) , vec![ c3] ) ;
117
251
}
@@ -136,7 +270,64 @@ mod tests {
136
270
137
271
let res = rebase_branch ( repo_path, "master" ) ;
138
272
139
- assert ! ( res. is_err( ) ) ;
273
+ assert ! ( matches!( res. unwrap( ) , RebaseState :: Conflicted ) ) ;
274
+
275
+ assert_eq ! ( repo_state( repo_path) . unwrap( ) , RepoState :: Rebase ) ;
276
+ }
277
+ }
278
+
279
+ #[ cfg( test) ]
280
+ mod test_rebase {
281
+ use crate :: sync:: {
282
+ checkout_branch, create_branch,
283
+ rebase:: {
284
+ abort_rebase, get_rebase_progress, RebaseProgress ,
285
+ RebaseState ,
286
+ } ,
287
+ rebase_branch, repo_state,
288
+ tests:: { repo_init, write_commit_file} ,
289
+ RepoState ,
290
+ } ;
291
+
292
+ #[ test]
293
+ fn test_conflicted_abort ( ) {
294
+ let ( _td, repo) = repo_init ( ) . unwrap ( ) ;
295
+ let root = repo. path ( ) . parent ( ) . unwrap ( ) ;
296
+ let repo_path = root. as_os_str ( ) . to_str ( ) . unwrap ( ) ;
297
+
298
+ write_commit_file ( & repo, "test.txt" , "test1" , "commit1" ) ;
299
+
300
+ create_branch ( repo_path, "foo" ) . unwrap ( ) ;
301
+
302
+ let c =
303
+ write_commit_file ( & repo, "test.txt" , "test2" , "commit2" ) ;
304
+
305
+ checkout_branch ( repo_path, "refs/heads/master" ) . unwrap ( ) ;
306
+
307
+ write_commit_file ( & repo, "test.txt" , "test3" , "commit3" ) ;
308
+
309
+ checkout_branch ( repo_path, "refs/heads/foo" ) . unwrap ( ) ;
310
+
311
+ assert ! ( get_rebase_progress( & repo) . is_err( ) ) ;
312
+
313
+ // rebase
314
+
315
+ let r = rebase_branch ( repo_path, "master" ) . unwrap ( ) ;
316
+
317
+ assert_eq ! ( r, RebaseState :: Conflicted ) ;
318
+ assert_eq ! ( repo_state( repo_path) . unwrap( ) , RepoState :: Rebase ) ;
319
+ assert_eq ! (
320
+ get_rebase_progress( & repo) . unwrap( ) ,
321
+ RebaseProgress {
322
+ current: 0 ,
323
+ steps: 1 ,
324
+ current_commit: Some ( c)
325
+ }
326
+ ) ;
327
+
328
+ // abort
329
+
330
+ abort_rebase ( & repo) . unwrap ( ) ;
140
331
141
332
assert_eq ! ( repo_state( repo_path) . unwrap( ) , RepoState :: Clean ) ;
142
333
}
0 commit comments