diff options
author | cos <cos> | 2023-09-19 18:52:06 +0200 |
---|---|---|
committer | cos <cos> | 2023-09-19 19:02:45 +0200 |
commit | 7d10b50fb585154f07d2b1bf07a1b0d36238f7ab (patch) | |
tree | 645316df596eba94c57b6e664df3b0eb6fd55e01 | |
parent | 783557400209703d08006ee9728842baa9e674d7 (diff) | |
download | gfold-7d10b50fb585154f07d2b1bf07a1b0d36238f7ab.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.rs | 3 | ||||
-rw-r--r-- | lib/libgfold/src/status.rs | 47 |
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> { |