diff --git a/libgit2-sys/lib.rs b/libgit2-sys/lib.rs index 0909d15994..383db6c37e 100644 --- a/libgit2-sys/lib.rs +++ b/libgit2-sys/lib.rs @@ -3506,6 +3506,13 @@ extern "C" { input_array: *const git_oid, ) -> c_int; + pub fn git_merge_base_octopus( + out: *mut git_oid, + repo: *mut git_repository, + length: size_t, + input_array: *const git_oid, + ) -> c_int; + pub fn git_merge_bases( out: *mut git_oidarray, repo: *mut git_repository, diff --git a/src/repo.rs b/src/repo.rs index 3924b2aa14..cae4e90b2f 100644 --- a/src/repo.rs +++ b/src/repo.rs @@ -2467,6 +2467,38 @@ impl Repository { } /// Find a merge base given a list of commits + /// + /// This behaves similar to [`git merge-base`](https://git-scm.com/docs/git-merge-base#_discussion). + /// Given three commits `a`, `b`, and `c`, `merge_base_many(&[a, b, c])` + /// will compute a hypothetical commit `m`, which is a merge between `b` + /// and `c`. + /// + /// For example, with the following topology: + /// ```text + /// o---o---o---o---C + /// / + /// / o---o---o---B + /// / / + /// ---2---1---o---o---o---A + /// ``` + /// + /// the result of `merge_base_many(&[a, b, c])` is 1. This is because the + /// equivalent topology with a merge commit `m` between `b` and `c` would + /// is: + /// ```text + /// o---o---o---o---o + /// / \ + /// / o---o---o---o---M + /// / / + /// ---2---1---o---o---o---A + /// ``` + /// + /// and the result of `merge_base_many(&[a, m])` is 1. + /// + /// --- + /// + /// If you're looking to recieve the common merge base between all the + /// given commits, use [`Self::merge_base_octopus`]. pub fn merge_base_many(&self, oids: &[Oid]) -> Result { let mut raw = raw::git_oid { id: [0; raw::GIT_OID_RAWSZ], @@ -2483,6 +2515,23 @@ impl Repository { } } + /// Find a common merge base between all given a list of commits + pub fn merge_base_octopus(&self, oids: &[Oid]) -> Result { + let mut raw = raw::git_oid { + id: [0; raw::GIT_OID_RAWSZ], + }; + + unsafe { + try_call!(raw::git_merge_base_octopus( + &mut raw, + self.raw, + oids.len() as size_t, + oids.as_ptr() as *const raw::git_oid + )); + Ok(Binding::from_raw(&raw as *const _)) + } + } + /// Find all merge bases between two commits pub fn merge_bases(&self, one: Oid, two: Oid) -> Result { let mut arr = raw::git_oidarray { @@ -3825,6 +3874,10 @@ mod tests { // the merge base of (oid2,oid3,oid4) should be oid1 let merge_base = repo.merge_base_many(&[oid2, oid3, oid4]).unwrap(); assert_eq!(merge_base, oid1); + + // the octopus merge base of (oid2,oid3,oid4) should be oid1 + let merge_base = repo.merge_base_octopus(&[oid2, oid3, oid4]).unwrap(); + assert_eq!(merge_base, oid1); } /// create an octopus: