mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-28 20:15:20 +03:00
disallow amending if force pushing is not ok
This commit is contained in:
parent
937749d886
commit
768bde4f27
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -58,7 +58,7 @@
|
||||
<div class="scroll-contents" bind:this={contents}>
|
||||
<Board
|
||||
{branchController}
|
||||
{projectId}
|
||||
project={$project$}
|
||||
{cloud}
|
||||
base={$base$}
|
||||
branches={$activeBranches$}
|
||||
|
@ -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}
|
||||
|
@ -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}
|
||||
|
@ -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}
|
||||
|
@ -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}
|
||||
|
@ -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}
|
||||
|
@ -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}
|
||||
|
@ -84,7 +84,7 @@
|
||||
{branchController}
|
||||
base={$baseBranch$}
|
||||
{cloud}
|
||||
{projectId}
|
||||
project={$project$}
|
||||
maximized={false}
|
||||
readonly={true}
|
||||
githubContext={$githubContext$}
|
||||
|
Loading…
Reference in New Issue
Block a user