Check we are on the right branch in verify_branch

Ensures we don't accidentally run commands while HEAD is pointing to something other than the gitbutler/integration branch.
This commit is contained in:
Mattias Granlund 2024-05-07 18:37:28 +01:00
parent 2911155e33
commit e7f25e29c5
5 changed files with 61 additions and 9 deletions

View File

@ -608,8 +608,8 @@ pub fn update_base_branch(
..target
})?;
// Rewriting the integration commit is necessary after changing target sha.
super::integration::update_gitbutler_integration(&vb_state, project_repository)?;
Ok(())
}

View File

@ -65,9 +65,13 @@ pub enum VerifyError {
DetachedHead,
#[error("head is {0}")]
InvalidHead(String),
#[error("head not found")]
HeadNotFound,
#[error("integration commit not found")]
NoIntegrationCommit,
#[error(transparent)]
GitError(#[from] git::Error),
#[error(transparent)]
Other(#[from] anyhow::Error),
}
@ -93,6 +97,12 @@ impl ErrorWithContext for VerifyError {
Code::ProjectHead,
"GibButler's integration commit not found on head.",
),
VerifyError::HeadNotFound => {
error::Context::new_static(Code::Validation, "Repo HEAD is unavailable")
}
VerifyError::GitError(error) => {
error::Context::new(Code::Validation, error.to_string())
}
VerifyError::Other(error) => return error.custom_context_or_root_cause().into(),
})
}

View File

@ -4,7 +4,7 @@ use anyhow::{anyhow, Context, Result};
use bstr::ByteSlice;
use lazy_static::lazy_static;
use super::{errors, VirtualBranchesHandle};
use super::{errors::VerifyError, VirtualBranchesHandle};
use crate::{
git::{self},
project_repository::{self, LogUntil},
@ -274,7 +274,8 @@ pub fn update_gitbutler_integration(
pub fn verify_branch(
project_repository: &project_repository::Repository,
) -> Result<(), errors::VerifyError> {
) -> Result<(), VerifyError> {
verify_current_branch_name(project_repository)?;
verify_head_is_set(project_repository)?;
verify_head_is_clean(project_repository)?;
Ok(())
@ -282,7 +283,7 @@ pub fn verify_branch(
fn verify_head_is_clean(
project_repository: &project_repository::Repository,
) -> Result<(), errors::VerifyError> {
) -> Result<(), VerifyError> {
let head_commit = project_repository
.git_repository
.head()
@ -303,7 +304,7 @@ fn verify_head_is_clean(
if integration_commit.is_none() {
// no integration commit found
return Err(errors::VerifyError::NoIntegrationCommit);
return Err(VerifyError::NoIntegrationCommit);
}
if extra_commits.is_empty() {
@ -377,17 +378,32 @@ fn verify_head_is_clean(
fn verify_head_is_set(
project_repository: &project_repository::Repository,
) -> Result<(), errors::VerifyError> {
) -> Result<(), VerifyError> {
match project_repository
.get_head()
.context("failed to get head")
.map_err(errors::VerifyError::Other)?
.map_err(VerifyError::Other)?
.name()
{
Some(refname) if refname.to_string() == GITBUTLER_INTEGRATION_REFERENCE.to_string() => {
Ok(())
}
None => Err(errors::VerifyError::DetachedHead),
Some(head_name) => Err(errors::VerifyError::InvalidHead(head_name.to_string())),
None => Err(VerifyError::DetachedHead),
Some(head_name) => Err(VerifyError::InvalidHead(head_name.to_string())),
}
}
// Returns an error if repo head is not pointing to the integration branch.
pub fn verify_current_branch_name(
project_repository: &project_repository::Repository,
) -> Result<bool, VerifyError> {
match project_repository.get_head()?.name() {
Some(head) => {
if head.to_string() != GITBUTLER_INTEGRATION_REFERENCE.to_string() {
return Err(VerifyError::InvalidHead(head.to_string()));
}
Ok(true)
}
None => Err(VerifyError::HeadNotFound),
}
}

View File

@ -72,6 +72,7 @@ mod undo_commit;
mod update_base_branch;
mod update_commit_message;
mod upstream;
mod verify_branch;
#[tokio::test]
async fn resolve_conflict_flow() {

View File

@ -0,0 +1,25 @@
use gitbutler_core::virtual_branches::errors::VerifyError;
use super::*;
// Ensures that `verify_branch` returns an error when not on the integration branch.
#[tokio::test]
async fn should_fail_on_incorrect_branch() {
let Test {
repository,
project_id,
controller,
..
} = &Test::default();
let branch_name: git::LocalRefname = "refs/heads/somebranch".parse().unwrap();
repository.checkout(&branch_name);
let result = controller.list_virtual_branches(project_id).await;
let error = result.err();
assert!(&error.is_some());
let error = error.unwrap();
let error = error.downcast_ref::<VerifyError>();
assert!(matches!(error, Some(VerifyError::InvalidHead(_))));
}