diff --git a/apps/desktop/src/components/forge/ReviewCreation.svelte b/apps/desktop/src/components/forge/ReviewCreation.svelte
index aee97304454..50238f7a20d 100644
--- a/apps/desktop/src/components/forge/ReviewCreation.svelte
+++ b/apps/desktop/src/components/forge/ReviewCreation.svelte
@@ -302,7 +302,8 @@
// If we now have two or more pull requests we add a stack table to the description.
prNumbers[currentIndex] = pr.number;
const definedPrNumbers = prNumbers.filter(isDefined);
- if (definedPrNumbers.length > 0) {
+ const enableStackFooter = $appSettings?.reviews.enableStackFooter ?? true;
+ if (enableStackFooter && definedPrNumbers.length > 0) {
updatePrStackInfo(prService, projectId, definedPrNumbers, forgeInfo?.unit.symbol);
}
diff --git a/apps/desktop/src/components/settings/IntegrationsSettings.svelte b/apps/desktop/src/components/settings/IntegrationsSettings.svelte
index aedd75addc6..4d5f7b55f24 100644
--- a/apps/desktop/src/components/settings/IntegrationsSettings.svelte
+++ b/apps/desktop/src/components/settings/IntegrationsSettings.svelte
@@ -14,6 +14,12 @@
autoFillPrDescriptionFromCommit: !$appSettings?.reviews.autoFillPrDescriptionFromCommit,
});
}
+
+ async function toggleStackFooter() {
+ await settingsService.updateReviews({
+ enableStackFooter: !($appSettings?.reviews.enableStackFooter ?? true),
+ });
+ }
@@ -36,4 +42,19 @@
/>
{/snippet}
+
+ {#snippet title()}
+ Add GitButler stack footer to PR/MR descriptions
+ {/snippet}
+ {#snippet caption()}
+ Append the list of pull requests in the same stack to the description.
+ {/snippet}
+ {#snippet actions()}
+
+ {/snippet}
+
diff --git a/apps/desktop/src/lib/dragging/dropHandlers/branchDropHandler.ts b/apps/desktop/src/lib/dragging/dropHandlers/branchDropHandler.ts
index 387f61f9ffd..c0b0a550bd0 100644
--- a/apps/desktop/src/lib/dragging/dropHandlers/branchDropHandler.ts
+++ b/apps/desktop/src/lib/dragging/dropHandlers/branchDropHandler.ts
@@ -1,10 +1,12 @@
import { FileChangeDropData, FolderChangeDropData, HunkDropDataV3 } from "$lib/dragging/draggables";
import { updateStackPrs } from "$lib/forge/shared/prFooter";
import { UNCOMMITTED_SERVICE } from "$lib/selection/uncommittedService.svelte";
+import { SETTINGS_SERVICE } from "$lib/settings/appSettings";
import { normalizeReferenceSubject } from "$lib/stacks/commitMovePlacement";
import { STACK_SERVICE } from "$lib/stacks/stackService.svelte";
import { UI_STATE } from "$lib/state/uiState.svelte";
import { inject } from "@gitbutler/core/context";
+import { get } from "svelte/store";
import type { DropResult } from "$lib/dragging/dropResult";
import type { DropzoneHandler } from "$lib/dragging/handler";
import type { PrService } from "$lib/forge/prService.svelte";
@@ -27,6 +29,7 @@ export class BranchDropData {
export class MoveBranchDzHandler implements DropzoneHandler {
private readonly stackService = inject(STACK_SERVICE);
+ private readonly settingsService = inject(SETTINGS_SERVICE);
constructor(
private readonly prService: PrService | undefined,
@@ -58,7 +61,10 @@ export class MoveBranchDzHandler implements DropzoneHandler {
targetBranch: normalizeReferenceSubject(this.branchName),
});
- if (this.prService && this.baseBranchName) {
+ const enableStackFooter =
+ get(this.settingsService.appSettings)?.reviews.enableStackFooter ?? true;
+
+ if (enableStackFooter && this.prService && this.baseBranchName) {
if (!sourceStackDeleted) {
const branchDetails = await this.stackService.fetchBranches(this.projectId, data.stackId);
await updateStackPrs(
diff --git a/apps/desktop/src/lib/dragging/dropHandlers/stackDropHandler.ts b/apps/desktop/src/lib/dragging/dropHandlers/stackDropHandler.ts
index f7a03f5119b..5faed2a0f88 100644
--- a/apps/desktop/src/lib/dragging/dropHandlers/stackDropHandler.ts
+++ b/apps/desktop/src/lib/dragging/dropHandlers/stackDropHandler.ts
@@ -9,11 +9,14 @@ import { BranchDropData } from "$lib/dragging/dropHandlers/branchDropHandler";
import { CommitDropData } from "$lib/dragging/dropHandlers/commitDropHandler";
import { classify } from "$lib/error/errorClassification";
import { unstackPRs, updateStackPrs } from "$lib/forge/shared/prFooter";
+import { SETTINGS_SERVICE } from "$lib/settings/appSettings";
import { toCommitMovePlacement } from "$lib/stacks/commitMovePlacement";
import StackMacros from "$lib/stacks/macros";
import { toMoveBranchWarning } from "$lib/stacks/stack";
import { withStackBusy } from "$lib/state/uiState.svelte";
+import { inject } from "@gitbutler/core/context";
import { untrack } from "svelte";
+import { get } from "svelte/store";
import type { DropResult } from "$lib/dragging/dropResult";
import type { DropzoneHandler } from "$lib/dragging/handler";
import type { PrService } from "$lib/forge/prService.svelte";
@@ -26,6 +29,7 @@ import type { HunkAssignmentTarget } from "@gitbutler/but-sdk";
/** Handler when drop changes on a special outside lanes dropzone. */
export class OutsideLaneDzHandler implements DropzoneHandler {
private macros: StackMacros;
+ private readonly settingsService = inject(SETTINGS_SERVICE);
constructor(
private stackService: StackService,
@@ -282,6 +286,9 @@ export class OutsideLaneDzHandler implements DropzoneHandler {
}
await unstackPRs(this.prService, this.projectId, [data.prNumber], this.baseBranchName);
+ const enableStackFooter =
+ get(this.settingsService.appSettings)?.reviews.enableStackFooter ?? true;
+ if (!enableStackFooter) return;
const branchDetails = await this.stackService.fetchBranches(this.projectId, data.stackId);
await updateStackPrs(
this.prService,
diff --git a/crates/but-settings/assets/defaults.jsonc b/crates/but-settings/assets/defaults.jsonc
index c56402d5d7f..bb1879d5ad7 100644
--- a/crates/but-settings/assets/defaults.jsonc
+++ b/crates/but-settings/assets/defaults.jsonc
@@ -61,7 +61,9 @@
// Settings related to code reviews and pull requests.
"reviews": {
// Whether to auto-fill PR title and description from the first commit when a branch has only one commit.
- "autoFillPrDescriptionFromCommit": true
+ "autoFillPrDescriptionFromCommit": true,
+ // Whether to append the GitButler stack footer to pull request descriptions.
+ "enableStackFooter": true
},
// UI settings.
"ui": {
diff --git a/crates/but-settings/src/api.rs b/crates/but-settings/src/api.rs
index 74ce1801610..7431ef3be43 100644
--- a/crates/but-settings/src/api.rs
+++ b/crates/but-settings/src/api.rs
@@ -38,6 +38,7 @@ pub struct ClaudeUpdate {
/// Update request for [`crate::app_settings::Reviews`].
pub struct ReviewsUpdate {
pub auto_fill_pr_description_from_commit: Option,
+ pub enable_stack_footer: Option,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
@@ -163,6 +164,9 @@ impl AppSettingsWithDiskSync {
settings.reviews.auto_fill_pr_description_from_commit =
auto_fill_pr_description_from_commit;
}
+ if let Some(enable_stack_footer) = update.enable_stack_footer {
+ settings.reviews.enable_stack_footer = enable_stack_footer;
+ }
settings.save()
}
diff --git a/crates/but-settings/src/app_settings.rs b/crates/but-settings/src/app_settings.rs
index c7c3ec4ab16..da3c67e2684 100644
--- a/crates/but-settings/src/app_settings.rs
+++ b/crates/but-settings/src/app_settings.rs
@@ -96,6 +96,8 @@ but_schemars::register_sdk_type!(Claude);
pub struct Reviews {
/// Whether to auto-fill PR title and description from the first commit when a branch has only one commit.
pub auto_fill_pr_description_from_commit: bool,
+ /// Whether to append the GitButler stack footer to pull request descriptions.
+ pub enable_stack_footer: bool,
}
but_schemars::register_sdk_type!(Reviews);
diff --git a/crates/but-testsupport/src/sandbox.rs b/crates/but-testsupport/src/sandbox.rs
index 1c7d9b692c4..d30d3897c6f 100644
--- a/crates/but-testsupport/src/sandbox.rs
+++ b/crates/but-testsupport/src/sandbox.rs
@@ -532,6 +532,7 @@ impl Sandbox {
},
reviews: Reviews {
auto_fill_pr_description_from_commit: false,
+ enable_stack_footer: true,
},
ui: UiSettings {
use_native_title_bar: false,
diff --git a/packages/but-sdk/src/generated/graph/index.d.ts b/packages/but-sdk/src/generated/graph/index.d.ts
index 92ec51d5253..38375fa7b1a 100644
--- a/packages/but-sdk/src/generated/graph/index.d.ts
+++ b/packages/but-sdk/src/generated/graph/index.d.ts
@@ -630,6 +630,7 @@ export interface WatcherEvent {
* `callback` receives watcher events shaped as `{ name, payload }`.
*/
export declare function watcherStart(projectId: string, callback: ((err: Error | null, arg: WatcherEvent) => any)): Promise
+
// Auto-generated by but-ts. Do not edit manually.
// Generated from JSON schemas registered by #[but_api] functions.
@@ -2416,6 +2417,8 @@ export type ReviewTemplateInfo = {
export type Reviews = {
/** Whether to auto-fill PR title and description from the first commit when a branch has only one commit. */
autoFillPrDescriptionFromCommit: boolean;
+ /** Whether to append the GitButler stack footer to pull request descriptions. */
+ enableStackFooter: boolean;
};
/** A segment of a commit graph, representing a set of commits exclusively. */
diff --git a/packages/but-sdk/src/generated/linear/index.d.ts b/packages/but-sdk/src/generated/linear/index.d.ts
index b874fd1df14..b1ed6f4d3a1 100644
--- a/packages/but-sdk/src/generated/linear/index.d.ts
+++ b/packages/but-sdk/src/generated/linear/index.d.ts
@@ -630,6 +630,7 @@ export interface WatcherEvent {
* `callback` receives watcher events shaped as `{ name, payload }`.
*/
export declare function watcherStart(projectId: string, callback: ((err: Error | null, arg: WatcherEvent) => any)): Promise
+
// Auto-generated by but-ts. Do not edit manually.
// Generated from JSON schemas registered by #[but_api] functions.
@@ -2416,6 +2417,8 @@ export type ReviewTemplateInfo = {
export type Reviews = {
/** Whether to auto-fill PR title and description from the first commit when a branch has only one commit. */
autoFillPrDescriptionFromCommit: boolean;
+ /** Whether to append the GitButler stack footer to pull request descriptions. */
+ enableStackFooter: boolean;
};
/** A segment of a commit graph, representing a set of commits exclusively. */