From 6e3acf74829aee7a15c7f149268cbb1c7589bef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Tejero=20Cantero?= Date: Wed, 28 Oct 2026 12:27:38 +0100 Subject: [PATCH 1/2] :recycle: Delete every merged branch, as penpot#12596 decided The merge into iop-vc-preview kept the "Archive this branch" option of the later merge dialog. Juan's decision in penpot#12596 holds instead: a merged branch is always deleted, never archived. The merge and conflict dialogs lose the option, merge-file-branch no longer takes keep-branch, merged branches leave the branch list and count again, and the tests for kept merged branches go, as in the pull request. The merge comment stays. The warning about left-out changes no longer suggests archiving. AI-assisted-by: claude-opus-5-5 --- backend/src/app/rpc/commands/files_branch.clj | 41 +++++----- .../rpc_file_branch_oplog_test.clj | 65 ---------------- .../backend_tests/rpc_file_branch_test.clj | 57 -------------- .../rpc_file_pull_request_test.clj | 75 ------------------- .../src/app/main/data/workspace/branches.cljs | 16 ++-- .../sidebar/branch_review/conflicts.cljs | 13 +--- .../main/ui/workspace/sidebar/branches.cljs | 28 +------ frontend/translations/en.po | 18 +---- 8 files changed, 31 insertions(+), 282 deletions(-) diff --git a/backend/src/app/rpc/commands/files_branch.clj b/backend/src/app/rpc/commands/files_branch.clj index e4596ecfaf..febd911b10 100644 --- a/backend/src/app/rpc/commands/files_branch.clj +++ b/backend/src/app/rpc/commands/files_branch.clj @@ -688,6 +688,7 @@ JOIN file AS sf ON (sf.id = fb.source_file_id) WHERE fb.source_file_id = ? AND fb.deleted_at IS NULL + AND fb.status != 'merged' AND (?::boolean OR fb.status = 'open') ORDER BY fb.created_at DESC") @@ -1077,12 +1078,7 @@ [:expected-main-revn {:optional true} ::sm/int] ;; what the user says about the merge; kept in its record ;; (`file_branch_merge`), shown in the list of merges of main - [:comment {:optional true} [:string {:max 4000}]] - ;; when false (the default) the branch and its file are logically - ;; deleted right after a successful merge, in the SAME transaction (so - ;; merged copies do not pile up); when true the branch is kept as - ;; "merged", staying readable with status merged until it is deleted. - [:keep-branch {:optional true} ::sm/boolean]]) + [:comment {:optional true} [:string {:max 4000}]]]) (sv/defmethod ::merge-file-branch "Merge a branch into its source file (main). @@ -1095,15 +1091,15 @@ `{:status :unsupported}` so no change is silently dropped. Otherwise it takes a safety snapshot of main, applies the merge to main through the production change pipeline, records the merge with its `comment` - (`file_branch_merge`), deletes the branch and its file unless - `keep-branch` asks to keep it, and notifies open clients via msgbus." + (`file_branch_merge`), deletes the branch and its file and notifies + open clients via msgbus." {::doc/added "2.16" ::webhooks/event? true ::sm/params schema:merge-file-branch ::climit/id [[:merge-file-branch/by-profile ::rpc/profile-id] [:merge-file-branch/global]]} [{:keys [::mbus/msgbus] :as cfg} - {:keys [::rpc/profile-id ::rpc/session-id branch-id resolutions expected-main-revn comment keep-branch excluded]}] + {:keys [::rpc/profile-id ::rpc/session-id branch-id resolutions expected-main-revn comment excluded]}] (bp/with-policies (check-branching-enabled!) (let [branch (db/get* cfg :file-branch {:id branch-id})] @@ -1193,16 +1189,13 @@ finish-branch! (fn [ts] - ;; record the merge and mark the branch merged. The - ;; pinned base snapshot stays for a KEPT branch (its - ;; state is derived from that snapshot and the op - ;; log); delete-branch! releases it on the delete - ;; path. Unless the user chose to keep it the branch - ;; and its file are deleted in this same transaction + ;; record the merge, mark the branch merged and + ;; delete it and its file in this same transaction ;; (no client-driven second call, no window where a - ;; crash leaves a stale copy). The record is written - ;; first: it reads the open pull request the lines - ;; below close. + ;; crash leaves a stale copy); delete-branch! + ;; releases the pinned base snapshot. The record is + ;; written first: it reads the open pull request the + ;; lines below close. (record-merge! conn branch {:profile-id profile-id :comment comment :created-at ts}) @@ -1221,10 +1214,9 @@ {:profile-id profile-id :status "merged" :deleted-at (ct/in-future delay)}) - (when-not keep-branch - (vswap! messages conj - (delete-branch! cfg branch {:profile-id profile-id - :session-id session-id})))))] + (vswap! messages conj + (delete-branch! cfg branch {:profile-id profile-id + :session-id session-id}))))] (cond (seq unresolved) (audited {:status :conflicts :conflicts conflicts :main-revn (:revn main-file)} @@ -1918,6 +1910,7 @@ "SELECT count(*) AS count FROM file_branch AS fb WHERE fb.deleted_at IS NULL + AND fb.status != 'merged' AND fb.source_file_id = coalesce((SELECT b.source_file_id FROM file_branch AS b WHERE b.branch_file_id = ? @@ -1925,8 +1918,8 @@ ?)") (sv/defmethod ::get-file-branch-count - "How many branches the file has, archived and merged ones included, as - the listing with `:include-archived` returns them. When the file is + "How many branches the file has, archived ones included, as the + listing with `:include-archived` returns them. When the file is itself a branch, the branches of its source (main) are counted. The workspace header asks this on every file open to decide whether to diff --git a/backend/test/backend_tests/rpc_file_branch_oplog_test.clj b/backend/test/backend_tests/rpc_file_branch_oplog_test.clj index 25087bbd38..b469c63862 100644 --- a/backend/test/backend_tests/rpc_file_branch_oplog_test.clj +++ b/backend/test/backend_tests/rpc_file_branch_oplog_test.clj @@ -15,7 +15,6 @@ [app.common.data :as d] [app.common.features :as cfeat] [app.common.files.migrations :as fmg] - [app.common.time :as ct] [app.common.types.shape :as cts] [app.common.uuid :as uuid] [app.config :as cf] @@ -1412,67 +1411,3 @@ row (th/db-get :file-media-object {:id ref})] (t/is (some? row)) (t/is (= main-id (:file-id row))))))) - -(t/deftest merge-with-keep-branch-leaves-the-branch-readable - (with-redefs [cf/flags (conj cf/flags :branching)] - (let [profile (th/create-profile* 1 {:is-active true}) - proj-id (:default-project-id profile) - file (th/create-file* 1 {:profile-id (:id profile) - :project-id proj-id - :is-shared false}) - create (create-branch* profile (:id file) "oplog-keep") - branch-file-id (:branch-file-id create) - branch-id (:id create) - color-id (uuid/random)] - - (apply-change* profile branch-file-id - {:type :add-color - :color {:id color-id :name "C" :color "#112233" :opacity 1}}) - - (let [out (th/command! {::th/type :merge-file-branch - ::rpc/profile-id (:id profile) - :branch-id branch-id - :keep-branch true})] - (t/is (nil? (:error out))) - (t/is (= :merged (-> out :result :status)))) - - (t/testing "the kept branch still derives its state" - (let [out (th/command! {::th/type :get-file - ::rpc/profile-id (:id profile) - :id branch-file-id})] - (t/is (nil? (:error out))) - (t/is (contains? (-> out :result :data :colors) color-id)))) - - (t/testing "the base pin survives while the branch is kept" - (let [rows (->> (th/db-query :file-change {:file-id (:id file)}) - (filter #(some-> (:label %) (str/starts-with? "branch-base/"))))] - (t/is (seq rows)) - (t/is (every? (fn [{:keys [deleted-at]}] - (or (nil? deleted-at) - (ct/is-after? deleted-at (ct/in-future {:days 400})))) - rows))))))) - -(t/deftest a-kept-merged-branch-refuses-saves - (with-redefs [cf/flags (conj cf/flags :branching)] - (let [profile (th/create-profile* 1 {:is-active true}) - proj-id (:default-project-id profile) - file (th/create-file* 1 {:profile-id (:id profile) - :project-id proj-id - :is-shared false}) - create (create-branch* profile (:id file) "oplog-merged") - branch-file-id (:branch-file-id create) - branch-id (:id create)] - - (th/command! {::th/type :merge-file-branch - ::rpc/profile-id (:id profile) - :branch-id branch-id - :keep-branch true}) - - (let [out (apply-change* profile branch-file-id - {:type :add-color - :color {:id (uuid/random) :name "C" :color "#112233" :opacity 1}})] - (t/is (th/ex-of-type? (:error out) :validation)) - (t/is (th/ex-of-code? (:error out) :branch-merged))) - - (t/testing "nothing reaches the op log" - (t/is (empty? (oplog-rows branch-file-id))))))) diff --git a/backend/test/backend_tests/rpc_file_branch_test.clj b/backend/test/backend_tests/rpc_file_branch_test.clj index 22cdfbc71b..7486638688 100644 --- a/backend/test/backend_tests/rpc_file_branch_test.clj +++ b/backend/test/backend_tests/rpc_file_branch_test.clj @@ -3236,60 +3236,3 @@ (= main-1 (:id %)) (= :deleted (:status %))) (:changes diff)))))))) - -(t/deftest merge-with-keep-branch-archives-it - (with-redefs [cf/flags (conj cf/flags :branching)] - (let [profile (th/create-profile* 1 {:is-active true}) - proj-id (:default-project-id profile) - file (th/create-file* 1 {:profile-id (:id profile) - :project-id proj-id}) - create (:result (th/command! {::th/type :create-file-branch - ::rpc/profile-id (:id profile) - :file-id (:id file) - :name "kept"})) - branch-id (:id create) - branch-file-id (:branch-file-id create)] - - (let [out (th/command! {::th/type :merge-file-branch - ::rpc/profile-id (:id profile) - :branch-id branch-id - :keep-branch true})] - (t/is (nil? (:error out))) - (t/is (= :merged (-> out :result :status)))) - - (let [[row] (th/db-query :file-branch {:id branch-id}) - bf (th/db-get :file {:id branch-file-id})] - (t/is (= "merged" (:status row))) - (t/is (nil? (:deleted-at row))) - (t/is (nil? (:deleted-at bf))))))) - -(t/deftest delete-keeps-the-status-the-branch-carries - ;; deleting a kept merged branch must not rewrite its status: the - ;; record is the only place left that says the branch was merged - (with-redefs [cf/flags (conj cf/flags :branching)] - (let [profile (th/create-profile* 1 {:is-active true}) - proj-id (:default-project-id profile) - file (th/create-file* 1 {:profile-id (:id profile) - :project-id proj-id}) - create (:result (th/command! {::th/type :create-file-branch - ::rpc/profile-id (:id profile) - :file-id (:id file) - :name "kept"})) - branch-id (:id create) - branch-file-id (:branch-file-id create) - cid (uuid/random)] - (apply-change* profile branch-file-id - {:type :add-color :color {:id cid :name "C" :color "#112233" :opacity 1}}) - (let [out (th/command! {::th/type :merge-file-branch - ::rpc/profile-id (:id profile) - :branch-id branch-id - :keep-branch true})] - (t/is (= :merged (-> out :result :status)))) - (let [out (th/command! {::th/type :delete-file-branch - ::rpc/profile-id (:id profile) - :id branch-id})] - (t/is (nil? (:error out))) - (t/is (= :deleted (-> out :result :status)))) - (let [[row] (th/db-query :file-branch {:id branch-id})] - (t/is (= "merged" (:status row))) - (t/is (some? (:deleted-at row))))))) diff --git a/backend/test/backend_tests/rpc_file_pull_request_test.clj b/backend/test/backend_tests/rpc_file_pull_request_test.clj index efc39f3b78..496695650f 100644 --- a/backend/test/backend_tests/rpc_file_pull_request_test.clj +++ b/backend/test/backend_tests/rpc_file_pull_request_test.clj @@ -792,78 +792,3 @@ (t/is (empty? @seen)) (t/is (empty? (th/db-query :file-pull-request {:file-branch-id (:id branch)}))))))) - -(t/deftest create-serializes-against-a-kept-concurrent-merge - ;; same race, but the merge keeps the branch: its row stays readable - ;; with status "merged" and its file is not deleted, so without the - ;; re-read the create sails past the stale status check and INSERTS a - ;; pull request for a branch the merge already closed. - (with-redefs [cf/flags pr-flags] - (let [author (th/create-profile* 1 {:is-active true}) - file (th/create-file* 1 {:profile-id (:id author) - :project-id (:default-project-id author)}) - branch (create-branch* author file "contested") - branch-id (:id branch) - branch-file-id (:branch-file-id branch)] - (add-color* author branch-file-id "Brand") - (let [orig-lock db/xact-lock! - merging? (atom false) - merged (atom nil)] - (with-redefs [db/xact-lock! - (fn [conn id] - ;; the first lock the create takes is the signal: - ;; merge the same branch through the pool in its - ;; own transaction BEFORE the create locks - (when (compare-and-set! merging? false true) - (reset! merged (th/command! {::th/type :merge-file-branch - ::rpc/profile-id (:id author) - :branch-id branch-id - :keep-branch true}))) - (orig-lock conn id))] - (let [out (th/command! {::th/type :create-pull-request - ::rpc/profile-id (:id author) - :branch-id branch-id - :title "review me"})] - (t/is (= :merged (-> @merged :result :status))) - (t/is (= "merged" (:status (first (th/db-query :file-branch {:id branch-id}))))) - (t/is (= :branch-not-open (-> out :error ex-data :code))) - (t/is (= [] (th/db-query :file-pull-request {:file-branch-id branch-id}))))))))) - -(t/deftest reopen-serializes-against-a-kept-concurrent-merge - ;; same race for the reopen: the kept merge leaves the branch row - ;; readable with status "merged", so without the re-read the reopen - ;; sails past the stale status check and REOPENS the closed pull - ;; request over a branch the merge already closed. - (with-redefs [cf/flags pr-flags] - (let [author (th/create-profile* 1 {:is-active true}) - file (th/create-file* 1 {:profile-id (:id author) - :project-id (:default-project-id author)}) - branch (create-branch* author file "contested") - branch-id (:id branch) - branch-file-id (:branch-file-id branch)] - (add-color* author branch-file-id "Brand") - (let [pr (:result (create-pr* author branch-id {}))] - (t/is (nil? (:error (th/command! {::th/type :close-pull-request - ::rpc/profile-id (:id author) - :id (:id pr)})))) - (let [orig-lock db/xact-lock! - merging? (atom false) - merged (atom nil)] - (with-redefs [db/xact-lock! - (fn [conn id] - ;; the first lock the reopen takes is the signal: - ;; merge the same branch through the pool in its - ;; own transaction BEFORE the reopen locks - (when (compare-and-set! merging? false true) - (reset! merged (th/command! {::th/type :merge-file-branch - ::rpc/profile-id (:id author) - :branch-id branch-id - :keep-branch true}))) - (orig-lock conn id))] - (let [out (th/command! {::th/type :reopen-pull-request - ::rpc/profile-id (:id author) - :id (:id pr)})] - (t/is (= :merged (-> @merged :result :status))) - (t/is (= "merged" (:status (first (th/db-query :file-branch {:id branch-id}))))) - (t/is (= :branch-not-open (-> out :error ex-data :code))) - (t/is (= "closed" (:status (first (th/db-query :file-pull-request {:id (:id pr)})))))))))))) diff --git a/frontend/src/app/main/data/workspace/branches.cljs b/frontend/src/app/main/data/workspace/branches.cljs index 6afdc6a67a..725a388849 100644 --- a/frontend/src/app/main/data/workspace/branches.cljs +++ b/frontend/src/app/main/data/workspace/branches.cljs @@ -745,19 +745,14 @@ (defn merge-branch "Merge `branch` (a branch row/context map with at least `:id`) into main. - On success the server records the merge and, unless the branch is kept, - deletes the branch and its file; open clients reload main via the - `:file-merged` msgbus event. + On success the server records the merge and deletes the branch and its + file, and open clients reload main via the `:file-merged` msgbus event. Options: - `:resolutions` — optional `{entity-id (:main|:branch)}` map used to resolve conflicts before integrating. - `:comment` — what the user says about the merge, kept with its record and shown in the reviews dialog. - - `:keep-branch` — when true the merged branch is kept (it shows up - under \"Archived\"); when false (default) the server deletes the - branch and its file in the same transaction, so merged copies do not - pile up and eat disk space. - `:excluded` — the per-change selection (change keys, `app.common.files.branch-merge/change-key`) naming the changes the user deselected, which main keeps its own value for. @@ -769,7 +764,7 @@ before that diff has loaded carry the revn of the conflict answer instead (`conflict-main-revn`). Unresolved conflicts open the resolution modal." ([branch] (merge-branch branch nil)) - ([branch {:keys [resolutions comment keep-branch excluded]}] + ([branch {:keys [resolutions comment excluded]}] (assert (uuid? (:id branch)) "expected a branch row with a valid `:id`") (ptk/reify ::merge-branch ptk/WatchEvent @@ -787,7 +782,6 @@ (seq resolutions) (assoc :resolutions resolutions) (seq excluded) (assoc :excluded excluded) (not (str/blank? comment)) (assoc :comment (str/trim comment)) - keep-branch (assoc :keep-branch true) (some? main-revn) (assoc :expected-main-revn main-revn))) (rx/mapcat (fn [{:keys [status source-file-id conflicts] :as answer}] @@ -795,8 +789,8 @@ ;; the merged result lives in main: take the user there ;; to see it (navigate if elsewhere, hard-reload if ;; already on main). Other clients reload via the - ;; `:file-merged` event; the branch deletion (when not - ;; kept) already happened server-side. + ;; `:file-merged` event; the branch deletion already + ;; happened server-side. :merged (rx/of (ntf/success (tr "workspace.branches.merge.success")) (show-merge-result source-file-id)) :conflicts (rx/of (open-conflict-resolutions {:branch branch diff --git a/frontend/src/app/main/ui/workspace/sidebar/branch_review/conflicts.cljs b/frontend/src/app/main/ui/workspace/sidebar/branch_review/conflicts.cljs index a6f2038767..a66760e6fd 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/branch_review/conflicts.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/branch_review/conflicts.cljs @@ -18,7 +18,6 @@ [app.main.data.workspace.branches :as dwb] [app.main.store :as st] [app.main.ui.ds.buttons.button :refer [button*]] - [app.main.ui.ds.controls.checkbox :refer [checkbox*]] [app.main.ui.ds.foundations.assets.icon :as i] [app.main.ui.ds.foundations.typography :as t] [app.main.ui.ds.foundations.typography.heading :refer [heading*]] @@ -393,8 +392,6 @@ (let [{:keys [diff resolutions status]} (mf/deref branch-diff) loading? (= status :loading) - keep?* (mf/use-state false) - keep? (deref keep?*) selected* (mf/use-state 0) selected (deref selected*) @@ -465,7 +462,7 @@ (when next-ix (reset! selected* next-ix))))) on-apply (mf/use-fn ;; a merge sends again the comment of the merge dialog - (mf/deps branch resolutions mode keep? excluded comment conflicts) + (mf/deps branch resolutions mode excluded comment conflicts) (fn [] (let [ids (into #{} (map :id) conflicts) res (into {} (filter (fn [[id _]] (contains? ids id))) resolutions)] @@ -473,7 +470,6 @@ (dwb/update-branch-from-main branch {:resolutions res :excluded excluded}) (dwb/merge-branch branch {:resolutions res - :keep-branch keep? :comment comment :excluded excluded}))))))] @@ -552,12 +548,7 @@ (if all-done? [:div {:class (stl/css :footer-start)} [:> text* {:as "span" :typography t/body-small :class (stl/css :footer-ready)} - (tr "workspace.branches.conflicts.footer-ready")] - (when (not= mode :update) - [:> checkbox* {:id "conflicts-archive-branch" - :label (tr "workspace.branches.merge.archive-label") - :checked keep? - :on-change #(swap! keep?* not)}])] + (tr "workspace.branches.conflicts.footer-ready")]] [:> text* {:as "span" :typography t/body-small :class (stl/css :footer-blocked)} (tr "workspace.branches.resolve.footer.blocked" (i18n/c pending))])) :footer-end diff --git a/frontend/src/app/main/ui/workspace/sidebar/branches.cljs b/frontend/src/app/main/ui/workspace/sidebar/branches.cljs index 0d3bf21eb2..d335caa37e 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/branches.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/branches.cljs @@ -17,7 +17,6 @@ [app.main.store :as st] [app.main.ui.ds.buttons.button :refer [button*]] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] - [app.main.ui.ds.controls.checkbox :refer [checkbox*]] [app.main.ui.ds.controls.input :refer [input*]] [app.main.ui.ds.foundations.assets.icon :as i] [app.main.ui.ds.layout.menu :refer [menu* menu-item* menu-separator*]] @@ -262,9 +261,7 @@ {::mf/register modal/components ::mf/register-as :merge-branch} [{:keys [branch excluded]}] - (let [archive?* (mf/use-state false) - archive? (deref archive?*) - comment* (mf/use-state "") + (let [comment* (mf/use-state "") comment (deref comment*) ;; what goes in and what stays out, when the compare dialog of this @@ -297,9 +294,6 @@ (str sep (tr "workspace.branches.main") sep))] (str/split msg sep)) - on-toggle-archive - (mf/use-fn #(swap! archive?* not)) - on-comment-change (mf/use-fn #(reset! comment* (dom/get-target-val %))) @@ -308,11 +302,9 @@ on-submit (mf/use-fn - (mf/deps branch archive? comment excluded) + (mf/deps branch comment excluded) (fn [_] - (st/emit! (dwb/merge-branch branch {:keep-branch archive? - :comment comment - :excluded excluded}) + (st/emit! (dwb/merge-branch branch {:comment comment :excluded excluded}) (modal/hide))))] [:> modal* {:is-open true @@ -344,22 +336,10 @@ :on-change on-comment-change :rows 3}]] - [:> checkbox* {:id "merge-archive-branch" - :label (tr "workspace.branches.merge.archive-label") - :checked archive? - :on-change on-toggle-archive}] - - (cond - archive? - [:> context-notification* {:level :warning :type :context} - (tr "workspace.branches.merge.archive-warning")] - + (if (pos? left-out) ;; deleting the branch takes the changes left out with it - (pos? left-out) [:> context-notification* {:level :warning :type :context} (tr "workspace.branches.merge.left-out-lost" (i18n/c left-out))] - - :else [:> context-notification* {:level :info :type :context} (tr "workspace.branches.merge.delete-info")])] diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 3c90ffe581..c608d93c02 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -7323,16 +7323,6 @@ msgstr "" msgid "workspace.branches.merge.confirm-title" msgstr "Merge into main" -#: src/app/main/ui/workspace/sidebar/branches.cljs:370, src/app/main/ui/workspace/sidebar/branches.cljs:1526 -msgid "workspace.branches.merge.archive-label" -msgstr "Archive this branch" - -#: src/app/main/ui/workspace/sidebar/branches.cljs:376 -msgid "workspace.branches.merge.archive-warning" -msgstr "" -"Archived branches keep a full copy of the file so they can be restored " -"later. Keeping many archived branches can take up a lot of storage." - #: src/app/main/data/workspace/branches.cljs:415 #, unused msgid "workspace.branches.merge.conflicts" @@ -7340,9 +7330,7 @@ msgstr "This branch has conflicts that must be resolved before merging" #: src/app/main/ui/workspace/sidebar/branches.cljs:378 msgid "workspace.branches.merge.delete-info" -msgstr "" -"After merging, this branch and its file will be deleted to free up storage. " -"Archive it if you might need to restore it later." +msgstr "After merging, this branch and its file will be deleted to free up storage." #: src/app/main/data/workspace/branches.cljs:451, src/app/main/data/workspace/branches.cljs:459 msgid "workspace.branches.merge.error" @@ -11796,8 +11784,8 @@ msgstr[1] "%s changes are left out." #: src/app/main/ui/workspace/sidebar/branches.cljs msgid "workspace.branches.merge.left-out-lost" msgid_plural "workspace.branches.merge.left-out-lost" -msgstr[0] "The change you left out is lost when the branch is deleted. Archive the branch to keep it." -msgstr[1] "The %s changes you left out are lost when the branch is deleted. Archive the branch to keep them." +msgstr[0] "The change you left out is lost when the branch is deleted." +msgstr[1] "The %s changes you left out are lost when the branch is deleted." #: src/app/main/ui/workspace/sidebar/branch_review/compare.cljs msgid "workspace.branches.review.view.expand" From 5e9238f7731852adeff37aa30ea5c89fe1d3d0f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Tejero=20Cantero?= Date: Wed, 28 Oct 2026 12:29:36 +0100 Subject: [PATCH 2/2] :books: Say in confirm-merge! that a merge deletes the branch The docstring still offered to keep the branch archived, which the merge dialog no longer does. AI-assisted-by: claude-opus-5-5 --- .../main/ui/workspace/sidebar/branch_review/common.cljs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/sidebar/branch_review/common.cljs b/frontend/src/app/main/ui/workspace/sidebar/branch_review/common.cljs index 4d4c5b979f..9af1224186 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/branch_review/common.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/branch_review/common.cljs @@ -24,10 +24,10 @@ ;; --- Merge / update confirmations (defn confirm-merge! - "Open the merge dialog for `branch` (merge into main is irreversible; the - dialog asks for a comment for the merge's record and lets the user choose - whether to keep the branch archived). `excluded` is the compare dialog's - per-change selection, if any." + "Open the merge dialog for `branch` (merge into main is irreversible and + deletes the branch; the dialog asks for a comment for the merge's + record). `excluded` is the compare dialog's per-change selection, if + any." ([branch] (confirm-merge! branch nil)) ([branch excluded] (modal/show! :merge-branch {:branch branch :excluded excluded})))