My personal project and infrastructure archive
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
nomicon/apps/servers/octopus/supergit/src/commit.rs

124 lines
3.4 KiB

use crate::{files::FileTree, Diff, HashId};
use git2::Repository;
use std::{
fmt::{self, Debug, Formatter},
sync::Arc,
};
/// Represent a commit on a repository
///
/// When creating a commit object, it is guaranteed that it exists in
/// the repository.
#[derive(Clone)]
pub struct Commit {
pub id: HashId,
repo: Arc<Repository>,
}
impl Debug for Commit {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.id)
}
}
impl Commit {
/// Create a commit object and check if it exists in the repo
pub(crate) fn new(r: &Arc<Repository>, id: HashId) -> Option<Self> {
r.find_commit(id.to_oid()).ok().map(|_| Self {
id,
repo: Arc::clone(r),
})
}
/// Get a utf-8 string representation of the commit ID
pub fn id_str(&self) -> String {
self.id.to_string()
}
/// Get the summary line as a utf-8 string
pub fn summary(&self) -> String {
self.find().summary().unwrap().into()
}
/// Get the number of parents
pub fn parent_count(&self) -> usize {
self.repo
.find_commit(self.id.to_oid())
.unwrap()
.parent_count()
}
/// Return the first parent, if it exists
pub fn first_parent(&self) -> Option<Self> {
self.find()
.parent(0)
.ok()
.and_then(|c| Self::new(&self.repo, c.id().into()))
}
/// Get a specific parent, if it exists
pub fn parent(&self, num: usize) -> Option<Self> {
self.find()
.parent(num)
.ok()
.and_then(|c| Self::new(&self.repo, c.id().into()))
}
/// Get the set of parents as a vector
///
/// Use this function if you suspect a commit has more than one
/// parent.
pub fn parents(&self) -> Vec<Commit> {
self.find()
.parents()
.map(|c| Self::new(&self.repo, c.id().into()).unwrap())
.collect()
}
/// Get the file tree for this commit
pub fn tree(&self) -> FileTree {
FileTree::new(Arc::clone(&self.repo), self.id.clone())
}
/// Get the list of paths in the repository touched by this commit
///
/// Using this function directly is often not what you want.
/// Instead, use the `get_history(...)` function on `FileTree`,
/// which uses this function.
pub fn get_paths(&self) -> Vec<String> {
self.get_diff()
.map(|d| Diff::from(d))
.map_or(vec![], |d| d.get_paths())
}
/// Utility function to get a merged diff from all parents
fn get_diff(&self) -> Option<git2::Diff> {
// Get all diffs with parents
let stree = self.find().tree().unwrap();
let mut vec = self
.parents()
.into_iter()
.filter_map(|p| {
self.repo
.diff_tree_to_tree(Some(&stree), Some(p.find().tree().as_ref().unwrap()), None)
.ok()
})
.collect::<Vec<_>>();
// If there are no parents
if vec.len() == 0 {
vec = vec![self.repo.diff_tree_to_tree(Some(&stree), None, None).ok()?];
}
// Take the first and merge onto
let first = vec.remove(0);
Some(vec.iter().fold(first, |mut acc, diff| {
acc.merge(diff).unwrap();
acc
}))
}
fn find(&self) -> git2::Commit {
self.repo.find_commit(self.id.to_oid()).unwrap()
}
}