aboutsummaryrefslogtreecommitdiff
path: root/src/repo/git/mod.rs
diff options
context:
space:
mode:
authorMax Audron <audron@cocaine.farm>2024-03-27 13:40:42 +0100
committerMax Audron <audron@cocaine.farm>2024-03-27 13:40:42 +0100
commitf7aca5859b3a40ec00c8789383e8ab84d7821ae6 (patch)
tree9a242aef7f0b0547949f4fefcff97eeaef1af356 /src/repo/git/mod.rs
parentimplement basic cloning and updating with gix (diff)
reorganize repo git impls
Diffstat (limited to 'src/repo/git/mod.rs')
-rw-r--r--src/repo/git/mod.rs113
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()))
+ }
+}