1 use std
::{marker, ptr, mem, str}
;
4 use {raw, Oid, Error, Signature, MergeOptions, Index}
;
5 use build
::CheckoutBuilder
;
10 /// Use to tell the rebase machinery how to operate.
11 pub struct RebaseOptions
<'cb
> {
12 raw
: raw
::git_rebase_options
,
13 merge_options
: Option
<MergeOptions
>,
14 checkout_options
: Option
<CheckoutBuilder
<'cb
>>,
17 impl<'cb
> Default
for RebaseOptions
<'cb
> {
18 fn default() -> Self {
23 impl<'cb
> RebaseOptions
<'cb
> {
24 /// Creates a new default set of rebase options.
25 pub fn new() -> RebaseOptions
<'cb
> {
26 let mut opts
= RebaseOptions
{
27 raw
: unsafe { mem::zeroed() }
,
29 checkout_options
: None
,
32 raw
::git_rebase_init_options(&mut opts
.raw
, 1)
37 /// Used by `Repository::rebase`, this will instruct other clients working on this
38 /// rebase that you want a quiet rebase experience, which they may choose to
39 /// provide in an application-specific manner. This has no effect upon
40 /// libgit2 directly, but is provided for interoperability between Git
42 pub fn quiet(&mut self, quiet
: bool
) -> &mut RebaseOptions
<'cb
> {
43 self.raw
.quiet
= quiet
as i32;
47 /// Used by `Repository::rebase`, this will begin an in-memory rebase,
48 /// which will allow callers to step through the rebase operations and
49 /// commit the rebased changes, but will not rewind HEAD or update the
50 /// repository to be in a rebasing state. This will not interfere with
51 /// the working directory (if there is one).
52 pub fn inmemory(&mut self, inmemory
: bool
) -> &mut RebaseOptions
<'cb
> {
53 self.raw
.inmemory
= inmemory
as i32;
57 /// Used by `finish()`, this is the name of the notes reference
58 /// used to rewrite notes for rebased commits when finishing the rebase;
59 /// if NULL, the contents of the configuration option `notes.rewriteRef`
60 /// is examined, unless the configuration option `notes.rewrite.rebase`
61 /// is set to false. If `notes.rewriteRef` is also NULL, notes will
63 pub fn rewrite_notes_ref(&mut self, rewrite_notes_ref
: &str) -> &mut RebaseOptions
<'cb
> {
64 let s
= CString
::new(rewrite_notes_ref
).unwrap().as_ptr();
65 self.raw
.rewrite_notes_ref
= s
;
69 /// Options to control how trees are merged during `next()`.
70 pub fn merge_options(&mut self, opts
: MergeOptions
) -> &mut RebaseOptions
<'cb
> {
71 self.merge_options
= Some(opts
);
75 /// Options to control how files are written during `Repository::rebase`,
76 /// `next()` and `abort()`. Note that a minimum strategy of
77 /// `GIT_CHECKOUT_SAFE` is defaulted in `init` and `next`, and a minimum
78 /// strategy of `GIT_CHECKOUT_FORCE` is defaulted in `abort` to match git
80 pub fn checkout_options(&mut self, opts
: CheckoutBuilder
<'cb
>) -> &mut RebaseOptions
<'cb
> {
81 self.checkout_options
= Some(opts
);
85 /// Acquire a pointer to the underlying raw options.
86 pub fn raw(&mut self) -> *const raw
::git_rebase_options
{
88 if let Some(opts
) = self.merge_options
.as_mut().take() {
89 ptr
::copy_nonoverlapping(opts
.raw(), &mut self.raw
.merge_options
, 1);
92 if let Some(opts
) = self.checkout_options
.as_mut() {
93 opts
.configure(&mut self.raw
.checkout_options
);
101 /// Representation of a rebase
102 pub struct Rebase
<'repo
> {
103 raw
: *mut raw
::git_rebase
,
104 _marker
: marker
::PhantomData
<&'repo raw
::git_rebase
>,
107 impl <'repo
> Rebase
<'repo
> {
108 /// Gets the count of rebase operations that are to be applied.
109 pub fn len(&self) -> usize {
110 unsafe { raw::git_rebase_operation_entrycount(self.raw) }
113 /// Gets the rebase operation specified by the given index.
114 pub fn nth(&mut self, n
: usize) -> Option
<RebaseOperation
> {
116 let op
= raw
::git_rebase_operation_byindex(self.raw
, n
);
120 Some(RebaseOperation
::from_raw(op
))
125 /// Gets the index of the rebase operation that is currently being applied.
126 /// If the first operation has not yet been applied (because you have called
127 /// `init` but not yet `next`) then this returns None.
128 pub fn operation_current(&mut self) -> Option
<usize> {
129 let cur
= unsafe { raw::git_rebase_operation_current(self.raw) }
;
130 if cur
== raw
::GIT_REBASE_NO_OPERATION
{
137 /// Gets the index produced by the last operation, which is the result of
138 /// `next()` and which will be committed by the next invocation of
139 /// `commit()`. This is useful for resolving conflicts in an in-memory
140 /// rebase before committing them.
142 /// This is only applicable for in-memory rebases; for rebases within a
143 /// working directory, the changes were applied to the repository's index.
144 pub fn inmemory_index(&mut self) -> Result
<Index
, Error
> {
145 let mut idx
= ptr
::null_mut();
147 try_call
!(raw
::git_rebase_inmemory_index(&mut idx
, self.raw
));
148 Ok(Binding
::from_raw(idx
))
152 /// Commits the current patch. You must have resolved any conflicts that
153 /// were introduced during the patch application from the `git_rebase_next`
155 pub fn commit(&mut self, author
: &Signature
, committer
: &Signature
, message
: &str) -> Result
<Oid
, Error
> {
156 let mut id
: raw
::git_oid
= unsafe { mem::zeroed() }
;
157 let message
= try
!(CString
::new(message
));
159 try_call
!(raw
::git_rebase_commit(&mut id
, self.raw
, author
.raw(), committer
.raw(), ptr
::null(), message
));
160 Ok(Binding
::from_raw(&id
as *const _
))
164 /// Aborts a rebase that is currently in progress, resetting the repository
165 /// and working directory to their state before rebase began.
166 pub fn abort(&mut self) -> Result
<(), Error
> {
168 try_call
!(raw
::git_rebase_abort(self.raw
));
174 /// Finishes a rebase that is currently in progress once all patches have
176 pub fn finish(&mut self, signature
: &Signature
) -> Result
<(), Error
> {
178 try_call
!(raw
::git_rebase_finish(self.raw
, signature
.raw()));
185 impl <'rebase
> Iterator
for Rebase
<'rebase
> {
186 type Item
= Result
<RebaseOperation
<'rebase
>, Error
>;
188 /// Performs the next rebase operation and returns the information about it.
189 /// If the operation is one that applies a patch (which is any operation except
190 /// GitRebaseOperation::Exec) then the patch will be applied and the index and
191 /// working directory will be updated with the changes. If there are conflicts,
192 /// you will need to address those before committing the changes.
193 fn next(&mut self) -> Option
<Result
<RebaseOperation
<'rebase
>, Error
>> {
194 let mut out
= ptr
::null_mut();
196 try_call_iter
!(raw
::git_rebase_next(&mut out
, self.raw
));
197 Some(Ok(RebaseOperation
::from_raw(out
)))
203 impl<'repo
> Binding
for Rebase
<'repo
> {
204 type Raw
= *mut raw
::git_rebase
;
205 unsafe fn from_raw(raw
: *mut raw
::git_rebase
)
209 _marker
: marker
::PhantomData
,
212 fn raw(&self) -> *mut raw
::git_rebase { self.raw }
216 impl<'repo
> Drop
for Rebase
<'repo
> {
218 unsafe { raw::git_rebase_free(self.raw) }
222 /// A rebase operation
224 /// Describes a single instruction/operation to be performed during the
226 #[derive(Debug, PartialEq)]
227 pub enum RebaseOperationType
{
228 /// The given commit is to be cherry-picked. The client should commit the
229 /// changes and continue if there are no conflicts.
232 /// The given commit is to be cherry-picked, but the client should prompt
233 /// the user to provide an updated commit message.
236 /// The given commit is to be cherry-picked, but the client should stop to
237 /// allow the user to edit the changes before committing them.
240 /// The given commit is to be squashed into the previous commit. The commit
241 /// message will be merged with the previous message.
244 /// The given commit is to be squashed into the previous commit. The commit
245 /// message from this commit will be discarded.
248 /// No commit will be cherry-picked. The client should run the given command
249 /// and (if successful) continue.
253 impl RebaseOperationType
{
254 /// Convert from the int into an enum. Returns None if invalid.
255 pub fn from_raw(raw
: raw
::git_rebase_operation_t
) -> Option
<RebaseOperationType
> {
257 raw
::GIT_REBASE_OPERATION_PICK
=> Some(RebaseOperationType
::Pick
),
258 raw
::GIT_REBASE_OPERATION_REWORD
=> Some(RebaseOperationType
::Reword
),
259 raw
::GIT_REBASE_OPERATION_EDIT
=> Some(RebaseOperationType
::Edit
),
260 raw
::GIT_REBASE_OPERATION_SQUASH
=> Some(RebaseOperationType
::Squash
),
261 raw
::GIT_REBASE_OPERATION_FIXUP
=> Some(RebaseOperationType
::Fixup
),
262 raw
::GIT_REBASE_OPERATION_EXEC
=> Some(RebaseOperationType
::Exec
),
268 /// A rebase operation
270 /// Describes a single instruction/operation to be performed during the
273 pub struct RebaseOperation
<'rebase
> {
274 raw
: *const raw
::git_rebase_operation
,
275 _marker
: marker
::PhantomData
<Rebase
<'rebase
>>,
278 impl<'rebase
> RebaseOperation
<'rebase
> {
279 /// The type of rebase operation
280 pub fn kind(&self) -> Option
<RebaseOperationType
> {
282 RebaseOperationType
::from_raw((*self.raw
).kind
)
286 /// The commit ID being cherry-picked. This will be populated for all
287 /// operations except those of type `GIT_REBASE_OPERATION_EXEC`.
288 pub fn id(&self) -> Oid
{
290 Binding
::from_raw(&(*self.raw
).id
as *const _
)
294 ///The executable the user has requested be run. This will only
295 /// be populated for operations of type RebaseOperationType::Exec
296 pub fn exec(&self) -> Option
<&str> {
298 str::from_utf8(::opt_bytes(self, (*self.raw
).exec
).unwrap()).ok()
303 impl<'rebase
> Binding
for RebaseOperation
<'rebase
> {
304 type Raw
= *const raw
::git_rebase_operation
;
305 unsafe fn from_raw(raw
: *const raw
::git_rebase_operation
) -> RebaseOperation
<'rebase
> {
308 _marker
: marker
::PhantomData
,
311 fn raw(&self) -> *const raw
::git_rebase_operation { self.raw }
316 use {RebaseOptions, RebaseOperationType}
;
320 let (_td
, repo
) = ::test
::repo_init();
321 let head_target
= repo
.head().unwrap().target().unwrap();
322 let tip
= repo
.find_commit(head_target
).unwrap();
323 let sig
= tip
.author();
324 let tree
= tip
.tree().unwrap();
326 // We just want to see the iteration work so we can create commits with
328 let c1
= repo
.commit(Some("refs/heads/master"), &sig
, &sig
, "foo", &tree
, &[&tip
]).unwrap();
329 let c1
= repo
.find_commit(c1
).unwrap();
330 let c2
= repo
.commit(Some("refs/heads/master"), &sig
, &sig
, "foo", &tree
, &[&c1
]).unwrap();
332 let branch
= repo
.find_annotated_commit(c2
).unwrap();
333 let upstream
= repo
.find_annotated_commit(tip
.id()).unwrap();
334 let mut opts
: RebaseOptions
= Default
::default();
336 let mut rebase
= repo
.rebase(Some(&branch
), Some(&upstream
), None
, Some(&mut opts
)).unwrap();
338 assert_eq
!(rebase
.len(), 2);
340 let op
= rebase
.next().unwrap().unwrap();
341 assert_eq
!(op
.kind(), Some(RebaseOperationType
::Pick
));
342 assert_eq
!(op
.id(), c1
.id());
345 let op
= rebase
.next().unwrap().unwrap();
346 assert_eq
!(op
.kind(), Some(RebaseOperationType
::Pick
));
347 assert_eq
!(op
.id(), c2
);
350 let op
= rebase
.next();
351 assert
!(op
.is_none());