aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/repo/git/mod.rs42
-rw-r--r--src/repo/mod.rs8
-rw-r--r--src/update/mod.rs28
3 files changed, 63 insertions, 15 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")]
diff --git a/src/update/mod.rs b/src/update/mod.rs
index e5e9f4c..e43f108 100644
--- a/src/update/mod.rs
+++ b/src/update/mod.rs
@@ -1,5 +1,6 @@
use std::fmt::{Debug, Display};
+use gix::bstr::ByteSlice;
use tracing::debug;
use crate::{
@@ -43,16 +44,23 @@ impl Repo {
let mut progress = gix::progress::Discard {};
- let _fetched = self.fetch()?;
- let (remote, head) = self.default_remote_head()?;
- debug!("default remote and head: {:?} {:?}", remote, head);
- // TODO check out only if the default branch is currently checked out
- self.checkout(&remote, head, &mut progress)?;
- debug!("finished checkout");
+ let fetched = self.fetch()?;
- // TODO do not update if there are unpushed commits
- self.update_default_branch_ref(&remote, head)?;
- debug!("updated default branch reference");
+ if fetched {
+ let (remote, head_id) = self.default_remote_head()?;
+ debug!("default remote and head: {:?} {:?}", remote, head_id);
+
+ self.checkout(&remote, head_id, &mut progress)?;
+ debug!("finished checkout");
+
+ // TODO do not update if there are unpushed commits
+ self.update_default_branch_ref(&remote, head_id)?;
+ debug!("updated default branch reference");
+
+ return Ok(UpdateResult::merged(self.name.clone()));
+ }
+
+ Ok(UpdateResult::no_changes(self.name.clone()))
// let merged = repo.branches(Some(BranchType::Local))?
// .filter_map(|x| x.ok())
@@ -77,8 +85,6 @@ impl Repo {
// } else {
// Ok(UpdateResult::no_changes(self.name.clone()))
// }
-
- Ok(UpdateResult::no_changes(self.name.clone()))
}
}