aboutsummaryrefslogtreecommitdiff
path: root/src/repo
diff options
context:
space:
mode:
authorMax Audron <audron@cocaine.farm>2025-04-08 16:48:57 +0200
committerMax Audron <audron@cocaine.farm>2025-04-08 16:48:57 +0200
commit9be68804bae9833a4749377e05bfaebf7d64f2b1 (patch)
tree5231887860d2cded23a931ea8d76a6c07d54c72c /src/repo
parentdelete files removed from index during checkout (diff)
only checkout repo if clean
Diffstat (limited to 'src/repo')
-rw-r--r--src/repo/git/mod.rs42
-rw-r--r--src/repo/mod.rs8
2 files changed, 46 insertions, 4 deletions
diff --git a/src/repo/git/mod.rs b/src/repo/git/mod.rs
index b88a13f..24b1e1a 100644
--- a/src/repo/git/mod.rs
+++ b/src/repo/git/mod.rs
@@ -2,12 +2,12 @@ use super::{LocalRepoState, Repo, RepoError};
use anyhow::Context;
use gix::{
- bstr::BString,
+ bstr::{BString, ByteSlice},
refs::{
transaction::{LogChange, PreviousValue, RefEdit},
FullName,
},
- remote, Id, ObjectId, Remote,
+ remote, Id, ObjectId, Reference, Remote,
};
use tracing::debug;
@@ -33,6 +33,34 @@ impl Repo {
return Ok(LocalRepoState::UnbornHead);
}
+ let head = self.repo()?.head().unwrap();
+ let branch = head.referent_name().unwrap();
+ let default_branch = self.default_branch()?;
+
+ if !branch.as_bstr().contains_str(default_branch) {
+ return Ok(LocalRepoState::NonDefaultBranch);
+ }
+
+ let default_ref = self.default_remote_ref()?.into_fully_peeled_id().unwrap();
+
+ let head_ref = repo
+ .head_ref()
+ .map_err(|_| RepoError::NoHead)?
+ .ok_or(RepoError::NoHead)?
+ .into_fully_peeled_id()
+ .unwrap();
+
+ let unpushed_commits = head_ref
+ .ancestors()
+ .with_boundary([default_ref])
+ .all()
+ .unwrap()
+ .count();
+
+ if default_ref != head_ref && unpushed_commits > 0 {
+ return Ok(LocalRepoState::UnpushedCommits(unpushed_commits));
+ }
+
Ok(LocalRepoState::Clean)
}
}
@@ -45,7 +73,7 @@ impl Repo {
.context("fetch: failed to find default remote")?)
}
- pub fn default_branch(&self) -> Result<BString, RepoError> {
+ pub fn default_remote_ref(&self) -> Result<Reference, RepoError> {
let repo = self.repo()?;
let remote = self.default_remote()?;
let remote_name = remote.name().context("remote does not have name")?;
@@ -56,6 +84,14 @@ impl Repo {
debug!("got ref to origin: {:?}", origin_ref);
+ Ok(origin_ref)
+ }
+
+ pub fn default_branch(&self) -> Result<BString, RepoError> {
+ let remote = self.default_remote()?;
+ let remote_name = remote.name().context("remote does not have name")?;
+ let origin_ref = self.default_remote_ref()?;
+
if let Some(origin_ref) = origin_ref.target().try_name() {
let shortened = origin_ref.shorten().to_string();
diff --git a/src/repo/mod.rs b/src/repo/mod.rs
index 538bf31..480a486 100644
--- a/src/repo/mod.rs
+++ b/src/repo/mod.rs
@@ -8,8 +8,8 @@ use tracing::error;
use crate::forge::Project;
mod aggregate;
-mod repostate;
mod git;
+mod repostate;
pub use aggregate::*;
pub use repostate::*;
@@ -47,6 +47,8 @@ pub enum RepoError {
NoLocalRepo,
#[error("local git repo does not have a remote")]
NoRemoteFound,
+ #[error("no head found")]
+ NoHead,
#[error("could not determine default branch based on remote HEAD")]
NoDefaultBranch,
#[error("repo is not checked out")]
@@ -65,6 +67,10 @@ pub enum RepoError {
pub enum LocalRepoState {
#[error("operation in progress: {0:?}")]
InProgress(gix::state::InProgress),
+ #[error("currently checked out branch is not default")]
+ NonDefaultBranch,
+ #[error("{0} unpushed commits")]
+ UnpushedCommits(usize),
#[error("head is detached")]
DetachedHead,
#[error("head is unborn")]