diff options
| author | seth <[email protected]> | 2024-06-16 07:15:13 -0400 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-06-16 07:15:13 -0400 |
| commit | d25129d829e0ebd70b4e60e399fe91c0d80aa1ad (patch) | |
| tree | 2a62992f2980f9fed2204ef5ef708a0228998cf1 /crates/git-tracker/src | |
| parent | a0bfcc1587e3cef1b8f6fa0508a280fc48c82231 (diff) | |
use libgit2 to track PRs (#10)v0.2.0
* nix: don't depend on registry for nixpkgs input
* use libgit2 to track PRs
* nix: don't use ci devShell as defaul
* crates: bump serenity from `9ad74d4` to `0.12.2
* nix: fix cross compiled builds
* crates: split more from client
* bot-jobs: update remote refs more efficiently
* git-tracker: account for HEAD commits
* bot-config: use nixpkgs branches from environment
* bot-commands: don't display branches prs haven't landed in
* git-tracker: return false when commits aren't found
this is annoying as a hard error since it turns out github will report
garbage merge commit SHAs for PRs that *haven't* been merged yet. yay
* bot: improve docs in some places
* bot-client: display invite link on start
* bot-http: add TeawieClientExt
* bot-commands: add /about
* docs: update readme todos
* nix: enable StateDirectory in module
* crates: bump to 0.2.0
Diffstat (limited to 'crates/git-tracker/src')
| -rw-r--r-- | crates/git-tracker/src/lib.rs | 4 | ||||
| -rw-r--r-- | crates/git-tracker/src/tracker.rs | 109 |
2 files changed, 113 insertions, 0 deletions
diff --git a/crates/git-tracker/src/lib.rs b/crates/git-tracker/src/lib.rs new file mode 100644 index 0000000..cb0907b --- /dev/null +++ b/crates/git-tracker/src/lib.rs @@ -0,0 +1,4 @@ +//! A library that helps you track commits and branches in a Git repository + +mod tracker; +pub use tracker::{Error, Tracker}; diff --git a/crates/git-tracker/src/tracker.rs b/crates/git-tracker/src/tracker.rs new file mode 100644 index 0000000..e26f82d --- /dev/null +++ b/crates/git-tracker/src/tracker.rs @@ -0,0 +1,109 @@ +use std::path::Path; + +use git2::{Branch, BranchType, Commit, ErrorCode, Oid, Reference, Repository}; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("libgit2 error")] + Git(#[from] git2::Error), + #[error("Repository path not found at `{0}`")] + RepositoryPathNotFound(String), +} + +/// Helper struct for tracking Git objects +pub struct Tracker { + repository: Repository, +} + +impl Tracker { + /// Create a new [`Tracker`] using the repository at [`path`] + /// + /// # Errors + /// + /// Will return [`Err`] if the repository can not be opened + pub fn from_path(path: &str) -> Result<Self, Error> { + let repository_path = Path::new(path); + if repository_path.exists() { + let repository = Repository::open(repository_path)?; + Ok(Self { repository }) + } else { + Err(Error::RepositoryPathNotFound(path.to_string())) + } + } + + /// Finds a branch of name [`name`] + /// + /// # Errors + /// + /// Will return [`Err`] if the branch cannot be found locally + pub fn branch_by_name(&self, name: &str) -> Result<Branch, Error> { + Ok(self.repository.find_branch(name, BranchType::Remote)?) + } + + /// Finds a commit with a SHA match [`sha`] + /// + /// # Errors + /// + /// Will return [`Err`] if [`sha`] cannot be converted an [`Oid`] or + /// a commit matching it cannot be found + pub fn commit_by_sha(&self, sha: &str) -> Result<Commit, Error> { + let oid = Oid::from_str(sha)?; + let commit = self.repository.find_commit(oid)?; + + Ok(commit) + } + + /// Check if [`Reference`] [`ref`] contains [`Commit`] [`commit`] + /// + /// # Errors + /// + /// Will return [`Err`] if the reference cannot be resolved to a commit or the descendants + /// of the reference cannot be resolved + pub fn ref_contains_commit( + &self, + reference: &Reference, + commit: &Commit, + ) -> Result<bool, Error> { + let head = reference.peel_to_commit()?; + + // NOTE: we have to check this as `Repository::graph_descendant_of()` (like the name says) + // only finds *descendants* of it's parent commit, and will not tell us if the parent commit + // *is* the child commit. i have no idea why i didn't think of this, but that's why this + // comment is here now + let is_head = head.id() == commit.id(); + + let has_commit = self + .repository + .graph_descendant_of(head.id(), commit.id())?; + + Ok(has_commit || is_head) + } + + /// Check if a [`Branch`] named [`branch_name`] has a commit with the SHA [`commit_sha`] + /// + /// # Errors + /// + /// Will return [`Err`] if the commit SHA cannot be resolved to an object id, the branch name cannot + /// be resolved to a branch, or the descendants of the resolved branch cannot be resolved + pub fn branch_contains_sha(&self, branch_name: &str, commit_sha: &str) -> Result<bool, Error> { + let commit = match self.commit_by_sha(commit_sha) { + Ok(commit) => commit, + Err(why) => { + // NOTE: we assume commits not found are just not in the branch *yet*, not an error + // this is because github decides to report merge commit shas for unmerged PRs...yeah + if let Error::Git(git_error) = &why { + if git_error.code() == ErrorCode::NotFound { + return Ok(false); + } + } + + return Err(why); + } + }; + + let branch = self.branch_by_name(branch_name)?; + let has_pr = self.ref_contains_commit(&branch.into_reference(), &commit)?; + + Ok(has_pr) + } +} |
