mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-04 07:33:18 +00:00
Hide local merge for unsupported remote bases
This commit is contained in:
@@ -6,7 +6,7 @@ import {
|
||||
actionsWithoutPrimary,
|
||||
buildGitActions,
|
||||
isCurrentBranchBase,
|
||||
isCurrentBranchLocalMergeTarget,
|
||||
canMergeIntoLocalBase,
|
||||
type BuildGitActionsInput,
|
||||
} from "./policy";
|
||||
import { deriveMergeCapability, type ForgeSpecificStatusFacts } from "./merge-capability";
|
||||
@@ -78,7 +78,7 @@ function createInput(
|
||||
hasRemote: false,
|
||||
isPaseoOwnedWorktree: false,
|
||||
isOnBaseBranch: true,
|
||||
isOnLocalMergeTarget: false,
|
||||
canMergeLocally: true,
|
||||
hasUncommittedChanges: false,
|
||||
baseRefAvailable: true,
|
||||
baseRefLabel: "main",
|
||||
@@ -221,9 +221,9 @@ describe("git-actions-policy", () => {
|
||||
expect(isCurrentBranchBase("release", "release")).toBe(true);
|
||||
expect(isCurrentBranchBase("release", "origin/release")).toBe(true);
|
||||
expect(isCurrentBranchBase(null, "refs/heads/release")).toBe(false);
|
||||
expect(isCurrentBranchLocalMergeTarget("release", "refs/heads/release")).toBe(true);
|
||||
expect(isCurrentBranchLocalMergeTarget("feature", "refs/remotes/origin/release")).toBe(false);
|
||||
expect(isCurrentBranchLocalMergeTarget("release", "refs/remotes/upstream/release")).toBe(false);
|
||||
expect(canMergeIntoLocalBase("release", "refs/heads/release")).toBe(false);
|
||||
expect(canMergeIntoLocalBase("feature", "refs/remotes/origin/release")).toBe(true);
|
||||
expect(canMergeIntoLocalBase("release", "refs/remotes/upstream/release")).toBe(false);
|
||||
});
|
||||
|
||||
it("offers local merge when the selected local base starts with origin slash", () => {
|
||||
@@ -231,10 +231,7 @@ describe("git-actions-policy", () => {
|
||||
createInput({
|
||||
isPaseoOwnedWorktree: true,
|
||||
isOnBaseBranch: isCurrentBranchBase("release", "refs/heads/origin/release"),
|
||||
isOnLocalMergeTarget: isCurrentBranchLocalMergeTarget(
|
||||
"release",
|
||||
"refs/heads/origin/release",
|
||||
),
|
||||
canMergeLocally: canMergeIntoLocalBase("release", "refs/heads/origin/release"),
|
||||
baseRefLabel: "origin/release",
|
||||
aheadCount: 1,
|
||||
shipDefault: "merge",
|
||||
@@ -243,15 +240,30 @@ describe("git-actions-policy", () => {
|
||||
expect(actions.primary?.id).toBe("merge-branch");
|
||||
});
|
||||
|
||||
it("offers update but not local merge for a non-origin remote base", () => {
|
||||
const actions = buildGitActions(
|
||||
createInput({
|
||||
isPaseoOwnedWorktree: true,
|
||||
isOnBaseBranch: false,
|
||||
canMergeLocally: canMergeIntoLocalBase("feature", "refs/remotes/upstream/release"),
|
||||
baseRefLabel: "upstream/release",
|
||||
aheadCount: 1,
|
||||
behindBaseCount: 1,
|
||||
aheadOfOrigin: null,
|
||||
behindOfOrigin: null,
|
||||
shipDefault: "merge",
|
||||
}),
|
||||
);
|
||||
expect(actions.primary?.id).toBe("merge-from-base");
|
||||
expect(actionsWithoutPrimary(actions).map((action) => action.id)).not.toContain("merge-branch");
|
||||
});
|
||||
|
||||
it("does not offer merging into the current local branch when the remote base shares its name", () => {
|
||||
const actions = buildGitActions(
|
||||
createInput({
|
||||
isPaseoOwnedWorktree: true,
|
||||
isOnBaseBranch: false,
|
||||
isOnLocalMergeTarget: isCurrentBranchLocalMergeTarget(
|
||||
"release",
|
||||
"refs/remotes/origin/release",
|
||||
),
|
||||
canMergeLocally: canMergeIntoLocalBase("release", "refs/remotes/origin/release"),
|
||||
baseRefLabel: "release",
|
||||
aheadCount: 1,
|
||||
behindBaseCount: 1,
|
||||
|
||||
@@ -61,13 +61,16 @@ export function isCurrentBranchBase(
|
||||
return currentBranch === baseRef.replace(/^origin\//, "");
|
||||
}
|
||||
|
||||
export function isCurrentBranchLocalMergeTarget(
|
||||
export function canMergeIntoLocalBase(
|
||||
currentBranch: string | null | undefined,
|
||||
baseRef: string | null | undefined,
|
||||
): boolean {
|
||||
if (!currentBranch || !baseRef) return false;
|
||||
if (baseRef.startsWith("refs/remotes/") && !baseRef.startsWith("refs/remotes/origin/")) {
|
||||
return false;
|
||||
}
|
||||
const localTarget = baseRef.replace(/^(?:refs\/heads\/|refs\/remotes\/origin\/|origin\/)/, "");
|
||||
return currentBranch === localTarget;
|
||||
return currentBranch !== localTarget;
|
||||
}
|
||||
|
||||
export interface BuildGitActionsInput {
|
||||
@@ -88,7 +91,7 @@ export interface BuildGitActionsInput {
|
||||
hasRemote: boolean;
|
||||
isPaseoOwnedWorktree: boolean;
|
||||
isOnBaseBranch: boolean;
|
||||
isOnLocalMergeTarget: boolean;
|
||||
canMergeLocally: boolean;
|
||||
hasUncommittedChanges: boolean;
|
||||
baseRefAvailable: boolean;
|
||||
baseRefLabel: string;
|
||||
@@ -370,7 +373,7 @@ function getPrimaryActionId(input: BuildGitActionsInput): GitActionId | null {
|
||||
if (input.shipDefault === "pr" && canUsePullRequestActionAsShipDefault(input)) {
|
||||
return "pr";
|
||||
}
|
||||
if (!input.isOnBaseBranch && !input.isOnLocalMergeTarget && input.aheadCount > 0) {
|
||||
if (!input.isOnBaseBranch && input.canMergeLocally && input.aheadCount > 0) {
|
||||
return "merge-branch";
|
||||
}
|
||||
if (!input.isOnBaseBranch && canMergeFromBase(input)) {
|
||||
@@ -401,7 +404,7 @@ function getPullRequestActionIds(filter: {
|
||||
function getFeatureActionIds(input: BuildGitActionsInput): GitActionId[] {
|
||||
return [
|
||||
"merge-from-base",
|
||||
...(!input.isOnLocalMergeTarget ? (["merge-branch"] as const) : []),
|
||||
...(input.canMergeLocally ? (["merge-branch"] as const) : []),
|
||||
...getPullRequestActionIds({ roles: ["status", "direct", "auto"], input }),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import { type CheckoutPrStatusPayload, useCheckoutPrStatusQuery } from "@/git/us
|
||||
import {
|
||||
buildGitActions,
|
||||
isCurrentBranchBase,
|
||||
isCurrentBranchLocalMergeTarget,
|
||||
canMergeIntoLocalBase,
|
||||
narrowPullRequestState,
|
||||
type BuildGitActionsInput,
|
||||
type GitAction,
|
||||
@@ -131,7 +131,7 @@ interface DerivedGitActionsState {
|
||||
hasRemote: boolean;
|
||||
isPaseoOwnedWorktree: boolean;
|
||||
isOnBaseBranch: boolean;
|
||||
isOnLocalMergeTarget: boolean;
|
||||
canMergeLocally: boolean;
|
||||
shouldPromoteArchive: boolean;
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ function deriveGitActionsState(args: DeriveGitActionsStateArgs): DerivedGitActio
|
||||
hasRemote: gitStatus?.hasRemote ?? false,
|
||||
isPaseoOwnedWorktree,
|
||||
isOnBaseBranch: isCurrentBranchBase(gitStatus?.currentBranch, baseRef),
|
||||
isOnLocalMergeTarget: isCurrentBranchLocalMergeTarget(gitStatus?.currentBranch, baseRef),
|
||||
canMergeLocally: canMergeIntoLocalBase(gitStatus?.currentBranch, baseRef),
|
||||
shouldPromoteArchive: computeShouldPromoteArchive({
|
||||
hasUncommittedChanges,
|
||||
postShipArchiveSuggested,
|
||||
@@ -669,7 +669,7 @@ export function useGitActions({ serverId, cwd, icons }: UseGitActionsInput): Use
|
||||
hasRemote,
|
||||
isPaseoOwnedWorktree,
|
||||
isOnBaseBranch,
|
||||
isOnLocalMergeTarget,
|
||||
canMergeLocally,
|
||||
shouldPromoteArchive,
|
||||
} = derived;
|
||||
|
||||
@@ -700,7 +700,7 @@ export function useGitActions({ serverId, cwd, icons }: UseGitActionsInput): Use
|
||||
hasRemote,
|
||||
isPaseoOwnedWorktree,
|
||||
isOnBaseBranch,
|
||||
isOnLocalMergeTarget,
|
||||
canMergeLocally,
|
||||
hasUncommittedChanges,
|
||||
baseRefAvailable: Boolean(baseRef),
|
||||
baseRefLabel,
|
||||
@@ -818,7 +818,7 @@ export function useGitActions({ serverId, cwd, icons }: UseGitActionsInput): Use
|
||||
behindBaseCount,
|
||||
isPaseoOwnedWorktree,
|
||||
isOnBaseBranch,
|
||||
isOnLocalMergeTarget,
|
||||
canMergeLocally,
|
||||
githubFeaturesEnabled,
|
||||
forge,
|
||||
githubAutoMergeActionsEnabled,
|
||||
|
||||
Reference in New Issue
Block a user