@ -1,6 +1,6 @@
use crate ::{ Commit , HashId } ;
use git2 ::Repository ;
use std ::sync ::Arc ;
use std ::{ mem , sync ::Arc } ;
/// Abstraction for a branch history slice
///
@ -100,6 +100,10 @@ impl BranchIter {
}
}
pub fn current ( & self ) -> Commit {
Commit ::new ( & self . repo , self . last . clone ( ) ) . unwrap ( )
}
/// Get a commit object, if it exists
fn find_commit ( & self , id : & HashId ) -> Option < Commit > {
Commit ::new ( & self . repo , id . clone ( ) )
@ -113,23 +117,59 @@ impl BranchIter {
}
/// Get the parent, set the last, and return BranchCommit (maybe)
fn get_parent ( & self , last : Option < Commit > ) -> Option < ( BranchCommit , IterCmd ) > {
fn get_parent ( & self , last : Option < Commit > , cmd : IterCmd ) -> Option < ( BranchCommit , IterCmd ) > {
if let Some ( id ) = cmd . take ( ) {
let commit = Commit ::new ( & self . repo , id ) . unwrap ( ) ;
return match commit . parent_count ( ) {
// Special case: if the previous commit was a merge,
// but this was the first commit in the history, we
// need to return it here or else it will be forgotten
// about!
0 | 1 = > Some ( ( BranchCommit ::Commit ( commit ) , IterCmd ::Step ) ) ,
2 = > {
let p1 = commit . first_parent ( ) . unwrap ( ) ;
let p2 = commit . parent ( 1 ) . unwrap ( ) ;
Some ( (
BranchCommit ::Merge (
// Here we return a commit via the merge
// field, because otherwise it will be
// dropped! Because we just skipped
// because of a merge (because merges are
// normal commit parents).
Some ( commit . clone ( ) ) ,
Branch ::without_name ( & self . repo , p2 . id ) ,
) ,
IterCmd ::Skip ( p1 . id ) ,
) )
}
_ = > todo! ( ) ,
} ;
}
// This code is only entered when we are checking for the parents
last . and_then ( | c | match c . parent_count ( ) {
// No parent means we've reached the end of the branch
0 = > None ,
// One parent is a normal commit
1 = > Some ( (
BranchCommit ::Commit ( c . first_parent ( ) . unwrap ( ) ) ,
IterCmd ::Step ,
) ) ,
1 = > {
let parent = c . first_parent ( ) . unwrap ( ) ;
Some ( ( BranchCommit ::Commit ( parent ) , IterCmd ::Step ) )
}
// Two parents is a normal merge commit
2 = > Some ( (
BranchCommit ::Merge (
c . clone ( ) ,
Branch ::without_name ( & self . repo , c . parent ( 1 ) . unwrap ( ) . id ) ,
) ,
IterCmd ::Skip ( c . parent ( 0 ) . unwrap ( ) . id ) ,
) ) ,
2 = > {
let p1 = c . first_parent ( ) . unwrap ( ) ;
let p2 = c . parent ( 1 ) . unwrap ( ) ;
Some ( (
// Set the Merge commit field to None because it's
// used to communicate special states (like a
// merge after another merge).
BranchCommit ::Merge ( None , Branch ::without_name ( & self . repo , p2 . id ) ) ,
IterCmd ::Skip ( p1 . id ) ,
) )
}
// More or negative parents means the universe is ending
_ = > panic! ( "Octopus merges are not implemented yet!" ) ,
} )
@ -140,20 +180,17 @@ impl Iterator for BranchIter {
type Item = BranchCommit ;
fn next ( & mut self ) -> Option < Self ::Item > {
let cid = std ::mem ::replace ( & mut self . cmd , IterCmd ::Step )
. take ( )
. unwrap_or_else ( | | self . last . clone ( ) ) ;
let cmd = mem ::replace ( & mut self . cmd , IterCmd ::Step ) ;
let last = self . find_commit ( & self . last ) ;
let last = self . find_commit ( & cid ) ;
match self . limit {
// Get commits forever
SegLimit ::None = > self . get_parent ( last ) . map ( | bc | self . set_last ( bc ) ) ,
SegLimit ::None = > self . get_parent ( last , cmd ) . map ( | bc | self . set_last ( bc ) ) ,
// Get commits until hitting a certain ID
SegLimit ::Commit ( ended , _ ) if ended = > None ,
SegLimit ::Commit ( _ , ref c ) = > {
let c = c . clone ( ) ;
self . get_parent ( last )
self . get_parent ( last , cmd )
. map ( | ( bc , cmd ) | {
// Set iterator to "done" if we have reached the commit
if bc . id ( ) = = c {
@ -173,13 +210,14 @@ impl Iterator for BranchIter {
}
* curr + = 1 ;
self . get_parent ( last ) . map ( | bc | self . set_last ( bc ) )
self . get_parent ( last , cmd ) . map ( | bc | self . set_last ( bc ) )
}
}
}
}
/// Specify how to trace actions on the iterator
#[ derive(Debug) ]
enum IterCmd {
/// Set the last commit to an ID
Step ,
@ -215,7 +253,7 @@ pub enum BranchCommit {
/// A single commit
Commit ( Commit ) ,
/// A merge commit from one other branch
Merge ( Commit , Branch ) ,
Merge ( Option < Commit > , Branch ) ,
/// An octopus merge with multiple branches
Octopus ( Commit , Vec < Branch > ) ,
}
@ -225,7 +263,7 @@ impl BranchCommit {
use BranchCommit ::* ;
match self {
Commit ( ref c ) = > & c . id ,
Merge ( ref c , _ ) = > & c . i d,
Merge ( _ , ref b ) = > & b . hea d,
Octopus ( ref c , _ ) = > & c . id ,
}
. clone ( )