diff --git a/libgit2-sys/lib.rs b/libgit2-sys/lib.rs index b605a5800b..41fa5041b8 100644 --- a/libgit2-sys/lib.rs +++ b/libgit2-sys/lib.rs @@ -1830,6 +1830,15 @@ extern { old_tree: *mut git_tree, opts: *const git_diff_options) -> c_int; + + pub fn git_graph_ahead_behind(ahead: *mut size_t, behind: *mut size_t, + repo: *mut git_repository, + local: *const git_oid, upstream: *const git_oid) + -> c_int; + + pub fn git_graph_descendant_of(repo: *mut git_repository, + commit: *const git_oid, ancestor: *const git_oid) + -> c_int; } #[test] diff --git a/src/repo.rs b/src/repo.rs index 341f4b2020..a07f6ae15a 100644 --- a/src/repo.rs +++ b/src/repo.rs @@ -1020,6 +1020,35 @@ impl Repository { Ok(Oid::from_raw(&raw)) } } + + /// Count the number of unique commits between two commit objects + /// + /// There is no need for branches containing the commits to have any + /// upstream relationship, but it helps to think of one as a branch and the + /// other as its upstream, the ahead and behind values will be what git + /// would report for the branches. + pub fn graph_ahead_behind(&self, local: Oid, upstream: Oid) + -> Result<(uint, uint), Error> { + unsafe { + let mut ahead: size_t = 0; + let mut behind: size_t = 0; + try_call!(raw::git_graph_ahead_behind(&mut ahead, &mut behind, + self.raw(), local.raw(), + upstream.raw())); + Ok((ahead as uint, behind as uint)) + } + } + + /// Determine if a commit is the descendant of another commit + pub fn graph_descendant_of(&self, commit: Oid, ancestor: Oid) + -> Result { + unsafe { + let rv = try_call!(raw::git_graph_descendant_of(self.raw(), + commit.raw(), + ancestor.raw())); + Ok(rv != 0) + } + } } #[unsafe_destructor] @@ -1269,4 +1298,49 @@ mod tests { let repo = Repository::discover(subdir.path()).unwrap(); assert!(repo.path() == *td.path()); } + + fn graph_repo_init() -> (TempDir, Repository) { + let (_td, repo) = ::test::repo_init(); + { + let head = repo.head().unwrap().target().unwrap(); + let head = repo.find_commit(head).unwrap(); + + let mut index = repo.index().unwrap(); + let id = index.write_tree().unwrap(); + + let tree = repo.find_tree(id).unwrap(); + let sig = repo.signature().unwrap(); + repo.commit(Some("HEAD"), &sig, &sig, "second", + &tree, &[&head]).unwrap(); + } + (_td, repo) + } + + #[test] + fn smoke_graph_ahead_behind() { + let (_td, repo) = graph_repo_init(); + let head = repo.head().unwrap().target().unwrap(); + let head = repo.find_commit(head).unwrap(); + let head_id = head.id(); + let head_parent_id = head.parent(0).unwrap().id(); + let (ahead, behind) = repo.graph_ahead_behind(head_id, + head_parent_id).unwrap(); + assert_eq!(ahead, 1); + assert_eq!(behind, 0); + let (ahead, behind) = repo.graph_ahead_behind(head_parent_id, + head_id).unwrap(); + assert_eq!(ahead, 0); + assert_eq!(behind, 1); + } + + #[test] + fn smoke_graph_descendant_of() { + let (_td, repo) = graph_repo_init(); + let head = repo.head().unwrap().target().unwrap(); + let head = repo.find_commit(head).unwrap(); + let head_id = head.id(); + let head_parent_id = head.parent(0).unwrap().id(); + assert!(repo.graph_descendant_of(head_id, head_parent_id).unwrap()); + assert!(!repo.graph_descendant_of(head_parent_id, head_id).unwrap()); + } }