consider force push preference when updating commit message

This commit is contained in:
Nikita Galaiko 2023-12-12 10:47:00 +01:00 committed by GitButler
parent dae28410c0
commit 0bb1b786ff
3 changed files with 149 additions and 12 deletions

View File

@ -246,6 +246,8 @@ pub enum SquashError {
#[derive(Debug, thiserror::Error)]
pub enum UpdateCommitMessageError {
#[error("force push not allowed")]
ForcePushNotAllowed(ForcePushNotAllowedError),
#[error("empty message")]
EmptyMessage,
#[error("default target not set")]
@ -263,6 +265,7 @@ pub enum UpdateCommitMessageError {
impl From<UpdateCommitMessageError> for Error {
fn from(value: UpdateCommitMessageError) -> Self {
match value {
UpdateCommitMessageError::ForcePushNotAllowed(error) => error.into(),
UpdateCommitMessageError::EmptyMessage => Error::UserError {
message: "Commit message can not be empty".to_string(),
code: crate::error::Code::Branches,

View File

@ -2837,9 +2837,7 @@ pub fn squash(
.parent(0)
.context("failed to find parent commit")?;
if branch
.upstream_head
.map_or_else(
let pushed_commit_oids = branch.upstream_head.map_or_else(
|| Ok(vec![]),
|upstream_head| {
project_repository.l(
@ -2847,8 +2845,9 @@ pub fn squash(
project_repository::LogUntil::Commit(default_target.sha),
)
},
)?
.contains(&parent_commit.id())
)?;
if pushed_commit_oids.contains(&parent_commit.id())
&& !project_repository.project().ok_with_force_push
{
// squashing into a pushed commit will cause a force push that is not allowed
@ -3025,6 +3024,26 @@ pub fn update_commit_message(
return Err(errors::UpdateCommitMessageError::CommitNotFound(commit_oid));
}
let pushed_commit_oids = branch.upstream_head.map_or_else(
|| Ok(vec![]),
|upstream_head| {
project_repository.l(
upstream_head,
project_repository::LogUntil::Commit(default_target.sha),
)
},
)?;
if pushed_commit_oids.contains(&commit_oid) && !project_repository.project().ok_with_force_push
{
// updating the message of a pushed commit will cause a force push that is not allowed
return Err(errors::UpdateCommitMessageError::ForcePushNotAllowed(
errors::ForcePushNotAllowedError {
project_id: project_repository.project().id,
},
));
}
let target_commit = project_repository
.git_repository
.find_commit(commit_oid)

View File

@ -4383,6 +4383,121 @@ mod update_commit_message {
);
}
#[tokio::test]
async fn forcepush_allowed() {
let Test {
repository,
project_id,
controller,
projects,
..
} = Test::default();
controller
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
.await
.unwrap();
projects
.update(&projects::UpdateRequest {
id: project_id,
ok_with_force_push: Some(true),
..Default::default()
})
.await
.unwrap();
let branch_id = controller
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
.await
.unwrap();
let commit_one_oid = {
fs::write(repository.path().join("file one.txt"), "").unwrap();
controller
.create_commit(&project_id, &branch_id, "commit one", None)
.await
.unwrap()
};
controller
.push_virtual_branch(&project_id, &branch_id, false)
.await
.unwrap();
controller
.update_commit_message(
&project_id,
&branch_id,
commit_one_oid,
"commit one updated",
)
.await
.unwrap();
let branch = controller
.list_virtual_branches(&project_id)
.await
.unwrap()
.into_iter()
.find(|b| b.id == branch_id)
.unwrap();
let descriptions = branch
.commits
.iter()
.map(|c| c.description.clone())
.collect::<Vec<_>>();
assert_eq!(descriptions, vec!["commit one updated"]);
assert!(branch.requires_force);
}
#[tokio::test]
async fn forcepush_forbidden() {
let Test {
repository,
project_id,
controller,
..
} = Test::default();
controller
.set_base_branch(&project_id, &"refs/remotes/origin/master".parse().unwrap())
.await
.unwrap();
let branch_id = controller
.create_virtual_branch(&project_id, &branch::BranchCreateRequest::default())
.await
.unwrap();
let commit_one_oid = {
fs::write(repository.path().join("file one.txt"), "").unwrap();
controller
.create_commit(&project_id, &branch_id, "commit one", None)
.await
.unwrap()
};
controller
.push_virtual_branch(&project_id, &branch_id, false)
.await
.unwrap();
assert!(matches!(
controller
.update_commit_message(
&project_id,
&branch_id,
commit_one_oid,
"commit one updated",
)
.await
.unwrap_err(),
ControllerError::Action(errors::UpdateCommitMessageError::ForcePushNotAllowed(_))
));
}
#[tokio::test]
async fn root() {
let Test {