disallow amending if force pushing is not ok

This commit is contained in:
Nikita Galaiko 2023-12-11 15:49:43 +01:00 committed by GitButler
parent 937749d886
commit 768bde4f27
11 changed files with 119 additions and 33 deletions

View File

@ -180,8 +180,27 @@ pub enum IsVirtualBranchMergeable {
Other(#[from] anyhow::Error),
}
#[derive(Debug)]
pub struct ForcePushNotAllowedError {
pub project_id: ProjectId,
}
impl From<ForcePushNotAllowedError> for Error {
fn from(value: ForcePushNotAllowedError) -> Self {
Error::UserError {
code: crate::error::Code::Branches,
message: format!(
"Action will lead to force pushing, which is not allowed for project {}",
value.project_id
),
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum AmendError {
#[error("force push not allowed")]
ForcePushNotAllowed(ForcePushNotAllowedError),
#[error("target ownership not found")]
TargetOwnerhshipNotFound(Ownership),
#[error("branch has no commits")]
@ -563,6 +582,7 @@ impl From<UnapplyOwnershipError> for Error {
impl From<AmendError> for Error {
fn from(value: AmendError) -> Self {
match value {
AmendError::ForcePushNotAllowed(error) => error.into(),
AmendError::Conflict(error) => error.into(),
AmendError::BranchNotFound(error) => error.into(),
AmendError::BranchHasNoCommits => Error::UserError {

View File

@ -2491,6 +2491,15 @@ pub fn amend(
})
})?;
if target_branch.upstream.is_some() && !project_repository.project().ok_with_force_push {
// amending to a pushed head commit will cause a force push that is not allowed
return Err(errors::AmendError::ForcePushNotAllowed(
errors::ForcePushNotAllowedError {
project_id: project_repository.project().id,
},
));
}
if project_repository
.l(
target_branch.head,

View File

@ -3379,6 +3379,52 @@ mod amend {
));
}
#[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();
{
// create commit
fs::write(repository.path().join("file.txt"), "content").unwrap();
controller
.create_commit(&project_id, &branch_id, "commit one", None)
.await
.unwrap();
};
controller
.push_virtual_branch(&project_id, &branch_id, false)
.await
.unwrap();
{
fs::write(repository.path().join("file2.txt"), "content2").unwrap();
let to_amend: branch::Ownership = "file2.txt:1-2".parse().unwrap();
assert!(matches!(
controller
.amend(&project_id, &branch_id, &to_amend)
.await
.unwrap_err(),
ControllerError::Action(errors::AmendError::ForcePushNotAllowed(_))
));
}
}
#[tokio::test]
async fn non_locked_hunk() {
let Test {

View File

@ -58,7 +58,7 @@
<div class="scroll-contents" bind:this={contents}>
<Board
{branchController}
{projectId}
project={$project$}
{cloud}
base={$base$}
branches={$activeBranches$}

View File

@ -9,8 +9,9 @@
import type { GitHubIntegrationContext } from '$lib/github/types';
import type { PrService } from '$lib/github/pullrequest';
import { cloneNode } from '$lib/utils/draggable';
import type { Project } from '$lib/backend/projects';
export let projectId: string;
export let project: Project;
export let projectPath: string;
export let branches: Branch[] | undefined;
@ -107,7 +108,7 @@
>
<BranchLane
{branch}
{projectId}
{project}
{base}
{cloud}
{branchController}

View File

@ -27,10 +27,11 @@
import { persisted } from '@square/svelte-store';
import { SETTINGS_CONTEXT, type SettingsStore } from '$lib/settings/userSettings';
import BranchCommits from './BranchCommits.svelte';
import type { Project } from '$lib/backend/projects';
export let branch: Branch;
export let readonly = false;
export let projectId: string;
export let project: Project;
export let base: BaseBranch | undefined | null;
export let cloud: ReturnType<typeof getCloudApiClient>;
export let branchController: BranchController;
@ -43,13 +44,13 @@
const allExpanded = writable(false);
const allCollapsed = writable(false);
const aiGenEnabled = projectAiGenEnabled(projectId);
const aiGenEnabled = projectAiGenEnabled(project.id);
let rsViewport: HTMLElement;
let commitsScrollable = false;
const userSettings = getContext<SettingsStore>(SETTINGS_CONTEXT);
const defaultBranchWidthRem = persisted<number | undefined>(24, 'defaulBranchWidth' + projectId);
const defaultBranchWidthRem = persisted<number | undefined>(24, 'defaulBranchWidth' + project.id);
const laneWidthKey = 'laneWidth_';
let laneWidth: number;
@ -161,7 +162,7 @@
{branch}
{allCollapsed}
{allExpanded}
{projectId}
projectId={project.id}
on:action={(e) => {
if (e.detail == 'expand') {
handleExpandAll();
@ -179,7 +180,7 @@
branchId={branch.id}
{branchController}
{branchCount}
{projectId}
projectId={project.id}
{base}
/>
{/if}
@ -223,7 +224,7 @@
/>
{#if branch.active}
<CommitDialog
{projectId}
projectId={project.id}
{branchController}
{branch}
{cloud}
@ -251,7 +252,7 @@
{base}
{branch}
{githubContext}
{projectId}
{project}
{prService}
{branchController}
{readonly}

View File

@ -1,4 +1,5 @@
<script lang="ts">
import type { Project } from '$lib/backend/projects';
import ScrollableContainer from '$lib/components/ScrollableContainer.svelte';
import type { PrService } from '$lib/github/pullrequest';
import type { GitHubIntegrationContext } from '$lib/github/types';
@ -6,7 +7,7 @@
import type { BaseBranch, Branch } from '$lib/vbranches/types';
import CommitList from './CommitList.svelte';
export let projectId: string;
export let project: Project;
export let branch: Branch;
export let base: BaseBranch | undefined | null;
export let prService: PrService;
@ -26,7 +27,7 @@
{branch}
{base}
{githubContext}
{projectId}
{project}
{branchController}
{prService}
{readonly}
@ -36,7 +37,7 @@
{branch}
{base}
{githubContext}
{projectId}
{project}
{branchController}
{prService}
{readonly}
@ -46,7 +47,7 @@
{branch}
{base}
{githubContext}
{projectId}
{project}
{branchController}
{prService}
{readonly}

View File

@ -8,10 +8,11 @@
import { writable } from 'svelte/store';
import { Ownership } from '$lib/vbranches/ownership';
import type { PrService } from '$lib/github/pullrequest';
import type { Project } from '$lib/backend/projects';
export let branch: Branch;
export let readonly = false;
export let projectId: string;
export let project: Project;
export let base: BaseBranch | undefined | null;
export let cloud: ReturnType<typeof getCloudApiClient>;
export let branchController: BranchController;
@ -39,7 +40,7 @@
<BranchCard
{branch}
{readonly}
{projectId}
{project}
{base}
{cloud}
{branchController}
@ -56,7 +57,7 @@
conflicted={selected.conflicted}
branchId={branch.id}
file={selected}
{projectId}
projectId={project.id}
{projectPath}
{branchController}
{selectedOwnership}

View File

@ -7,11 +7,12 @@
import CommitListHeader from './CommitListHeader.svelte';
import type { CommitType } from './commitList';
import CommitListFooter from './CommitListFooter.svelte';
import type { Project } from '$lib/backend/projects';
export let branch: Branch;
export let githubContext: GitHubIntegrationContext | undefined;
export let base: BaseBranch | undefined | null;
export let projectId: string;
export let project: Project;
export let branchController: BranchController;
export let type: CommitType;
export let prService: PrService;
@ -48,7 +49,7 @@
{branchController}
{commit}
{base}
{projectId}
{project}
{readonly}
isChained={idx != commits.length - 1}
isHeadCommit={commit.id === headCommit?.id}
@ -63,7 +64,7 @@
{base}
{githubContext}
{readonly}
{projectId}
projectId={project.id}
/>
</div>
{/if}

View File

@ -1,4 +1,5 @@
<script lang="ts">
import type { Project } from '$lib/backend/projects';
import {
isDraggableHunk,
type DraggableCommit,
@ -13,7 +14,7 @@
import CommitCard from './CommitCard.svelte';
export let branch: Branch;
export let projectId: string;
export let project: Project;
export let commit: Commit;
export let base: BaseBranch | undefined | null;
export let isHeadCommit: boolean;
@ -23,17 +24,22 @@
function acceptAmend(commit: Commit) {
return (data: any) => {
if (
isDraggableHunk(data) &&
data.branchId == branch.id &&
commit.id == branch.commits.at(0)?.id
) {
if (!project.ok_with_force_push && commit.isRemote) {
return false;
}
if (commit.isIntegrated) {
return false;
}
// only allow to amend the head commit
if (commit.id != branch.commits.at(0)?.id) {
return false;
}
if (isDraggableHunk(data) && data.branchId == branch.id) {
return true;
} else if (
isDraggableFile(data) &&
data.branchId == branch.id &&
commit.id == branch.commits.at(0)?.id
) {
} else if (isDraggableFile(data) && data.branchId == branch.id) {
return true;
} else {
return false;
@ -116,7 +122,7 @@
<CommitCard
{commit}
{projectId}
projectId={project.id}
commitUrl={base?.commitUrl(commit.id)}
{isHeadCommit}
{resetHeadCommit}

View File

@ -84,7 +84,7 @@
{branchController}
base={$baseBranch$}
{cloud}
{projectId}
project={$project$}
maximized={false}
readonly={true}
githubContext={$githubContext$}