summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcos <cos>2023-09-19 18:52:06 +0200
committercos <cos>2023-09-19 19:02:45 +0200
commit7d10b50fb585154f07d2b1bf07a1b0d36238f7ab (patch)
tree645316df596eba94c57b6e664df3b0eb6fd55e01
parent783557400209703d08006ee9728842baa9e674d7 (diff)
downloadgfold-topic/diverged_status.zip
Add new statuses when tracking remote branchtopic/diverged_status
The first new status, 'Behind', indicates when the remote is detected to have commits not on the local branch. Can be considered the opposite of 'Unpushed'. The second new status, 'Diverged', occurs when both the local and the remote branch have commits unrepresented on the other side.
-rw-r--r--bin/gfold/src/display/color.rs3
-rw-r--r--lib/libgfold/src/status.rs47
2 files changed, 45 insertions, 5 deletions
diff --git a/bin/gfold/src/display/color.rs b/bin/gfold/src/display/color.rs
index f264cd2..c2eb01f 100644
--- a/bin/gfold/src/display/color.rs
+++ b/bin/gfold/src/display/color.rs
@@ -28,7 +28,8 @@ impl ColorHarness {
pub fn write_status(&self, status: &Status, status_width: usize) -> io::Result<()> {
let mut stdout = StandardStream::stdout(self.color_choice);
stdout.set_color(ColorSpec::new().set_fg(Some(match status {
- Status::Bare | Status::Unknown => Color::Red,
+ Status::Bare | Status::Diverged | Status::Unknown => Color::Red,
+ Status::Behind => Color::Yellow,
Status::Clean => Color::Green,
Status::Unpushed => Color::Blue,
Status::Unclean => Color::Yellow,
diff --git a/lib/libgfold/src/status.rs b/lib/libgfold/src/status.rs
index 7ffd638..4a080c5 100644
--- a/lib/libgfold/src/status.rs
+++ b/lib/libgfold/src/status.rs
@@ -1,6 +1,6 @@
//! This module contains the [`crate::status::Status`] type.
-use git2::{ErrorCode, Reference, Remote, Repository, StatusOptions};
+use git2::{BranchType, ErrorClass, ErrorCode, Reference, Remote, Repository, StatusOptions};
use log::debug;
use serde::{Deserialize, Serialize};
use thiserror::Error;
@@ -20,8 +20,12 @@ pub type StatusResult<T> = Result<T, StatusError>;
pub enum Status {
/// Corresponds to a "bare" working tree.
Bare,
+ /// Indicates that there is at least one remote commit not yet merged to local branch.
+ Behind,
/// Corresponds to a "clean" working tree.
Clean,
+ /// Indicates that local and remote both have commits unique to their side.
+ Diverged,
/// Corresponds to an "unclean" working tree.
Unclean,
/// Provided if the state of the working tree could neither be found nor determined.
@@ -35,7 +39,9 @@ impl Status {
pub fn as_str(&self) -> &'static str {
match self {
Self::Bare => "bare",
+ Self::Behind => "behind",
Self::Clean => "clean",
+ Self::Diverged => "diverged",
Self::Unclean => "unclean",
Self::Unknown => "unknown",
Self::Unpushed => "unpushed",
@@ -73,9 +79,13 @@ impl Status {
let status = match repo.statuses(Some(&mut opts)) {
Ok(v) if v.is_empty() => match &head {
Some(head) => match remote_name {
- Some(remote_name) => match Self::is_unpushed(repo, head, &remote_name)? {
- true => Status::Unpushed,
- false => Status::Clean,
+ Some(remote_name) => match (Self::is_unpushed(repo, head, &remote_name)?,
+ Self::is_unmerged(repo, head)?)
+ {
+ (true, false) => Status::Unpushed,
+ (false, false) => Status::Clean,
+ (false, true) => Status::Behind,
+ (true, true) => Status::Diverged,
},
None => Status::Clean,
},
@@ -119,6 +129,35 @@ impl Status {
)
}
+ // Checks if remote commit(s) on the tracking branch have not yet been merged locally.
+ fn is_unmerged(
+ repo: &Repository,
+ head: &Reference<'_>,
+ ) -> Result<bool, git2::Error> {
+ if head.is_branch() {
+ let branch = repo.find_branch(head.shorthand()
+ .ok_or(git2::Error::new(
+ ErrorCode::User, ErrorClass::Invalid, "Branch name contains invalid UTF-8.")
+ )?, BranchType::Local
+ )?;
+ match branch.upstream() {
+ Err(_) => {
+ debug!("assuming merged; could not determine upstream branch");
+ Ok(false)
+ },
+ Ok(upstream) => {
+ let number_unique_commits = repo.graph_ahead_behind(
+ branch.get().peel_to_commit()?.id(),
+ upstream.into_reference().peel_to_commit().unwrap().id()
+ )?;
+ Ok(number_unique_commits.1 > 0)
+ },
+ }
+ } else {
+ Ok(false)
+ }
+ }
+
fn choose_remote_greedily(
repository: &Repository,
) -> Result<(Option<Remote<'_>>, Option<String>), git2::Error> {