diff options
Diffstat (limited to 'src/repo/git/mod.rs')
| -rw-r--r-- | src/repo/git/mod.rs | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/src/repo/git/mod.rs b/src/repo/git/mod.rs new file mode 100644 index 0000000..7370ee8 --- /dev/null +++ b/src/repo/git/mod.rs @@ -0,0 +1,113 @@ +use super::{LocalRepoState, Repo, RepoError}; + +use anyhow::Context; +use gix::{ + bstr::BString, + refs::{ + transaction::{LogChange, PreviousValue, RefEdit}, + FullName, + }, + remote, Id, ObjectId, Remote, +}; + +mod checkout; +mod fetch; + +impl Repo { + #[tracing::instrument(level = "debug")] + pub fn is_clean(&self) -> Result<LocalRepoState, RepoError> { + let repo = self.repo()?; + + if let Some(state) = repo.state() { + Ok(LocalRepoState::InProgress(state)) + } else { + let head = repo.head().unwrap(); + + if head.is_detached() { + return Ok(LocalRepoState::DetachedHead); + } + + if head.is_unborn() { + return Ok(LocalRepoState::UnbornHead); + } + + Ok(LocalRepoState::Clean) + } + } + + pub fn default_remote(&self) -> Result<Remote, RepoError> { + Ok(self + .repo()? + .find_default_remote(gix::remote::Direction::Fetch) + .ok_or(RepoError::NoRemoteFound)? + .context("fetch: failed to find default remote")?) + } + + pub fn default_branch(&self) -> Result<BString, RepoError> { + let repo = self.repo()?; + let remote = self.default_remote()?; + let remote_name = remote.name().context("remote does not have name")?; + + let origin_ref = repo + .find_reference(&format!("remotes/{}/HEAD", remote_name.as_bstr())) + .context("the remotes HEAD references does not exist")?; + + if let Some(origin_ref) = origin_ref.target().try_name() { + Ok(origin_ref.shorten().to_owned()) + } else { + Err(RepoError::NoDefaultBranch) + } + } + + pub fn refedit(target: ObjectId, name: &str, message: &str) -> RefEdit { + RefEdit { + change: gix::refs::transaction::Change::Update { + log: LogChange { + mode: gix::refs::transaction::RefLog::AndReference, + force_create_reflog: false, + message: message.into(), + }, + expected: PreviousValue::Any, + new: gix::refs::Target::Peeled(target), + }, + name: FullName::try_from(name).unwrap(), + deref: true, + } + } + + pub fn update_default_branch_ref( + &self, + remote: remote::Name, + head: Id, + ) -> Result<(), RepoError> { + let default_branch = self.default_branch()?; + let repo = self.repo()?; + + repo.edit_reference(Repo::refedit( + head.into(), + &format!("heads/{}", default_branch), + &format!("checkout: {}/HEAD with gtree", remote.as_bstr()), + )) + .context("checkout: failed to edit ref")?; + + Ok(()) + } + + pub fn default_remote_head(&self) -> Result<(remote::Name, Id), RepoError> { + let repo = self.repo()?; + + let remote = repo + .find_fetch_remote(None) + .context("could not find remote to fetch")?; + let remote = remote.name().context("remote does not have name")?; + + let head_ref = repo + .find_reference(&format!("remotes/{}/HEAD", remote.as_bstr())) + .context("the remotes HEAD references does not exist")?; + let head = head_ref + .into_fully_peeled_id() + .context("failed to peel ref")?; + + Ok((remote.to_owned(), head.to_owned())) + } +} |
