use crate::core::index::{CommitEntryReader, CommitReader, CommitWriter, RefReader, Stager};
use crate::error::OxenError;
use crate::model::{Commit, CommitEntry, LocalRepository, StagedData};
use crate::opts::LogOpts;
use crate::view::{PaginatedCommits, StatusMessage};
use crate::{api, util};
use rayon::prelude::*;
use std::path::Path;
pub fn latest_commit(repo: &LocalRepository) -> Result<Commit, OxenError> {
let reader = CommitReader::new(repo)?;
reader.latest_commit()
}
pub fn head_commit(repo: &LocalRepository) -> Result<Commit, OxenError> {
let reader = CommitReader::new(repo)?;
reader.head_commit()
}
pub fn root_commit(repo: &LocalRepository) -> Result<Commit, OxenError> {
let committer = CommitReader::new(repo)?;
let commit = committer.root_commit()?;
Ok(commit)
}
pub fn get_by_id(repo: &LocalRepository, commit_id: &str) -> Result<Option<Commit>, OxenError> {
let reader = CommitReader::new(repo)?;
reader.get_commit_by_id(commit_id)
}
pub fn get_by_message(
repo: &LocalRepository,
msg: impl AsRef<str>,
) -> Result<Vec<Commit>, OxenError> {
let commits = list_all(repo)?;
let filtered: Vec<Commit> = commits
.into_iter()
.filter(|commit| commit.message == msg.as_ref())
.collect();
Ok(filtered)
}
pub fn first_by_message(
repo: &LocalRepository,
msg: impl AsRef<str>,
) -> Result<Option<Commit>, OxenError> {
let committer = CommitReader::new(repo)?;
let commits = committer.history_from_head()?;
Ok(commits
.into_iter()
.find(|commit| commit.message == msg.as_ref()))
}
pub fn get_parents(repo: &LocalRepository, commit: &Commit) -> Result<Vec<Commit>, OxenError> {
let committer = CommitReader::new(repo)?;
let mut commits: Vec<Commit> = vec![];
for commit_id in commit.parent_ids.iter() {
if let Some(commit) = committer.get_commit_by_id(commit_id)? {
commits.push(commit)
} else {
return Err(OxenError::commit_db_corrupted(commit_id));
}
}
Ok(commits)
}
pub fn commit_content_size(repo: &LocalRepository, commit: &Commit) -> Result<u64, OxenError> {
let reader = CommitEntryReader::new(repo, commit)?;
let entries = reader.list_entries()?;
Ok(compute_entries_size(&entries))
}
pub fn compute_entries_size(entries: &[CommitEntry]) -> u64 {
entries.par_iter().map(|entry| entry.num_bytes).sum::<u64>()
}
pub fn commit_from_branch_or_commit_id<S: AsRef<str>>(
repo: &LocalRepository,
val: S,
) -> Result<Option<Commit>, OxenError> {
let val = val.as_ref();
let commit_reader = CommitReader::new(repo)?;
if let Some(commit) = commit_reader.get_commit_by_id(val)? {
return Ok(Some(commit));
}
let ref_reader = RefReader::new(repo)?;
if let Some(branch) = ref_reader.get_branch_by_name(val)? {
if let Some(commit) = commit_reader.get_commit_by_id(branch.commit_id)? {
return Ok(Some(commit));
}
}
Ok(None)
}
pub fn commit_with_no_files(repo: &LocalRepository, message: &str) -> Result<Commit, OxenError> {
let mut status = StagedData::empty();
let commit = commit(repo, &mut status, message)?;
println!("Initial commit {}", commit.id);
Ok(commit)
}
pub fn commit(
repo: &LocalRepository,
status: &mut StagedData,
message: &str,
) -> Result<Commit, OxenError> {
let stager = Stager::new(repo)?;
let commit_writer = CommitWriter::new(repo)?;
let commit = commit_writer.commit(status, message)?;
stager.unstage()?;
Ok(commit)
}
pub fn create_commit_object(
repo_dir: &Path,
branch_name: impl AsRef<str>,
commit: &Commit,
) -> Result<(), OxenError> {
log::debug!("Create commit obj: {} -> '{}'", commit.id, commit.message);
let repo = LocalRepository::from_dir(repo_dir)?;
if let Ok(root) = root_commit(&repo) {
if commit.parent_ids.is_empty() && root.id != commit.id {
log::error!("Root commit does not match {} != {}", root.id, commit.id);
return Err(OxenError::root_commit_does_not_match(commit.to_owned()));
}
}
let result = CommitWriter::new(&repo);
match result {
Ok(commit_writer) => match commit_writer.add_commit_to_db(commit) {
Ok(_) => {
log::debug!("Successfully added commit [{}] to db", commit.id);
api::local::branches::update(&repo, branch_name.as_ref(), &commit.id)?;
}
Err(err) => {
log::error!("Error adding commit to db: {:?}", err);
}
},
Err(err) => {
log::error!("Error creating commit writer: {:?}", err);
}
};
Ok(())
}
pub fn list(repo: &LocalRepository) -> Result<Vec<Commit>, OxenError> {
let committer = CommitReader::new(repo)?;
let commits = committer.history_from_head()?;
Ok(commits)
}
pub fn list_all(repo: &LocalRepository) -> Result<Vec<Commit>, OxenError> {
let committer = CommitReader::new(repo)?;
let commits = committer.list_all()?;
Ok(commits)
}
pub async fn list_with_opts(
repo: &LocalRepository,
opts: &LogOpts,
) -> Result<Vec<Commit>, OxenError> {
if opts.remote {
let remote_repo = api::remote::repositories::get_default_remote(repo).await?;
let revision = if let Some(revision) = &opts.revision {
revision.to_owned()
} else {
api::local::branches::current_branch(repo)?.unwrap().name
};
let commits = api::remote::commits::list_commit_history(&remote_repo, &revision).await?;
Ok(commits)
} else {
let committer = CommitReader::new(repo)?;
let commits = if let Some(revision) = &opts.revision {
let commit = api::local::revisions::get(repo, revision)?
.ok_or(OxenError::revision_not_found(revision.to_string().into()))?;
committer.history_from_commit_id(&commit.id)?
} else {
committer.history_from_head()?
};
Ok(commits)
}
}
pub fn list_from(repo: &LocalRepository, revision: &str) -> Result<Vec<Commit>, OxenError> {
log::debug!("list_from: {}", revision);
let committer = CommitReader::new(repo)?;
if revision.contains("..") {
let split: Vec<&str> = revision.split("..").collect();
let base = split[0];
let head = split[1];
let base_commit_id = match api::local::branches::get_commit_id(repo, base)? {
Some(branch_commit_id) => branch_commit_id,
None => String::from(base),
};
let head_commit_id = match api::local::branches::get_commit_id(repo, head)? {
Some(branch_commit_id) => branch_commit_id,
None => String::from(head),
};
log::debug!(
"list_from: base_commit_id: {} head_commit_id: {}",
base_commit_id,
head_commit_id
);
return match committer.history_from_base_to_head(&base_commit_id, &head_commit_id) {
Ok(commits) => Ok(commits),
Err(_) => Err(OxenError::local_revision_not_found(revision)),
};
}
let commit_id = match api::local::branches::get_commit_id(repo, revision)? {
Some(branch_commit_id) => branch_commit_id,
None => String::from(revision),
};
log::debug!("list_from: commit_id: {}", commit_id);
match committer.history_from_commit_id(&commit_id) {
Ok(commits) => Ok(commits),
Err(_) => Err(OxenError::local_revision_not_found(revision)),
}
}
pub fn list_from_paginated(
repo: &LocalRepository,
revision: &str,
page_number: usize,
page_size: usize,
) -> Result<PaginatedCommits, OxenError> {
let commits = list_from(repo, revision)?;
let (commits, pagination) = util::paginate(commits, page_number, page_size);
Ok(PaginatedCommits {
status: StatusMessage::resource_found(),
commits,
pagination,
})
}