detached e9394b7

feat(api): let a merge delete the source branch

commit
e9394b717bc2afe3b4ef998d8c4df50aca16bc6a
author
brice · · 2026-10-11 05:29 UTC
parent
303f037
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +45 -9
M gen/ermite/merge_request.pb.go +35 -9
@@ -1266,9 +1266,15 @@ type MergeMergeRequestRequest struct {
12661266 // HeadId is the full id of the commit the caller reviewed, that is the
12671267 // MergeRequest.head_id it was shown. Required. The merge happens only if the
12681268 // source branch still points to it.
1269- HeadId string `protobuf:"bytes,4,opt,name=head_id,json=headId,proto3" json:"head_id,omitempty"`
1270- unknownFields protoimpl.UnknownFields
1271- sizeCache protoimpl.SizeCache
1269+ HeadId string `protobuf:"bytes,4,opt,name=head_id,json=headId,proto3" json:"head_id,omitempty"`
1270+ // DeleteSourceBranch deletes the source branch after the merge. The merge
1271+ // is refused before anything changes when the source branch is the default
1272+ // branch of the repository or the source or target of another open merge
1273+ // request. Servers without the "merge_requests.delete_branch" feature ignore
1274+ // it.
1275+ DeleteSourceBranch bool `protobuf:"varint,5,opt,name=delete_source_branch,json=deleteSourceBranch,proto3" json:"delete_source_branch,omitempty"`
1276+ unknownFields protoimpl.UnknownFields
1277+ sizeCache protoimpl.SizeCache
12721278 }
12731279
12741280 func (x *MergeMergeRequestRequest) Reset() {
@@ -1329,15 +1335,26 @@ func (x *MergeMergeRequestRequest) GetHeadId() string {
13291335 return ""
13301336 }
13311337
1338+func (x *MergeMergeRequestRequest) GetDeleteSourceBranch() bool {
1339+ if x != nil {
1340+ return x.DeleteSourceBranch
1341+ }
1342+ return false
1343+}
1344+
13321345 // MergeMergeRequestResponse holds the merged merge request.
13331346 type MergeMergeRequestResponse struct {
13341347 state protoimpl.MessageState `protogen:"open.v1"`
13351348 // MergeRequest is the merge request after the merge: state MERGED,
13361349 // merged_commit_id set to the new tip of the target branch and merge_mode
13371350 // set to the mode used.
1338- MergeRequest *MergeRequest `protobuf:"bytes,1,opt,name=merge_request,json=mergeRequest,proto3" json:"merge_request,omitempty"`
1339- unknownFields protoimpl.UnknownFields
1340- sizeCache protoimpl.SizeCache
1351+ MergeRequest *MergeRequest `protobuf:"bytes,1,opt,name=merge_request,json=mergeRequest,proto3" json:"merge_request,omitempty"`
1352+ // SourceBranchDeleted is true when delete_source_branch was set and the
1353+ // branch is gone. It is false when the deletion failed after the merge (the
1354+ // branch moved in between, say): the merge stands and the branch is kept.
1355+ SourceBranchDeleted bool `protobuf:"varint,2,opt,name=source_branch_deleted,json=sourceBranchDeleted,proto3" json:"source_branch_deleted,omitempty"`
1356+ unknownFields protoimpl.UnknownFields
1357+ sizeCache protoimpl.SizeCache
13411358 }
13421359
13431360 func (x *MergeMergeRequestResponse) Reset() {
@@ -1377,6 +1394,13 @@ func (x *MergeMergeRequestResponse) GetMergeRequest() *MergeRequest {
13771394 return nil
13781395 }
13791396
1397+func (x *MergeMergeRequestResponse) GetSourceBranchDeleted() bool {
1398+ if x != nil {
1399+ return x.SourceBranchDeleted
1400+ }
1401+ return false
1402+}
1403+
13801404 // MarkReadRequest records a read position.
13811405 type MarkReadRequest struct {
13821406 state protoimpl.MessageState `protogen:"open.v1"`
@@ -1568,14 +1592,16 @@ const file_ermite_merge_request_proto_rawDesc = "" +
15681592 "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +
15691593 "\x06number\x18\x02 \x01(\x03R\x06number\"V\n" +
15701594 "\x19CloseMergeRequestResponse\x129\n" +
1571- "\rmerge_request\x18\x01 \x01(\v2\x14.ermite.MergeRequestR\fmergeRequest\"\x86\x01\n" +
1595+ "\rmerge_request\x18\x01 \x01(\v2\x14.ermite.MergeRequestR\fmergeRequest\"\xb8\x01\n" +
15721596 "\x18MergeMergeRequestRequest\x12\x12\n" +
15731597 "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +
15741598 "\x06number\x18\x02 \x01(\x03R\x06number\x12%\n" +
15751599 "\x04mode\x18\x03 \x01(\x0e2\x11.ermite.MergeModeR\x04mode\x12\x17\n" +
1576- "\ahead_id\x18\x04 \x01(\tR\x06headId\"V\n" +
1600+ "\ahead_id\x18\x04 \x01(\tR\x06headId\x120\n" +
1601+ "\x14delete_source_branch\x18\x05 \x01(\bR\x12deleteSourceBranch\"\x8a\x01\n" +
15771602 "\x19MergeMergeRequestResponse\x129\n" +
1578- "\rmerge_request\x18\x01 \x01(\v2\x14.ermite.MergeRequestR\fmergeRequest\"V\n" +
1603+ "\rmerge_request\x18\x01 \x01(\v2\x14.ermite.MergeRequestR\fmergeRequest\x122\n" +
1604+ "\x15source_branch_deleted\x18\x02 \x01(\bR\x13sourceBranchDeleted\"V\n" +
15791605 "\x0fMarkReadRequest\x12\x12\n" +
15801606 "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +
15811607 "\x06number\x18\x02 \x01(\x03R\x06number\x12\x17\n" +
@@ -1266,9 +1266,15 @@ type MergeMergeRequestRequest struct {
1266 // HeadId is the full id of the commit the caller reviewed, that is the1266 // HeadId is the full id of the commit the caller reviewed, that is the
1267 // MergeRequest.head_id it was shown. Required. The merge happens only if the1267 // MergeRequest.head_id it was shown. Required. The merge happens only if the
1268 // source branch still points to it.1268 // source branch still points to it.
1269- HeadId string `protobuf:"bytes,4,opt,name=head_id,json=headId,proto3" json:"head_id,omitempty"`1269+ HeadId string `protobuf:"bytes,4,opt,name=head_id,json=headId,proto3" json:"head_id,omitempty"`
1270- unknownFields protoimpl.UnknownFields1270+ // DeleteSourceBranch deletes the source branch after the merge. The merge
1271- sizeCache protoimpl.SizeCache1271+ // is refused before anything changes when the source branch is the default
1272+ // branch of the repository or the source or target of another open merge
1273+ // request. Servers without the "merge_requests.delete_branch" feature ignore
1274+ // it.
1275+ DeleteSourceBranch bool `protobuf:"varint,5,opt,name=delete_source_branch,json=deleteSourceBranch,proto3" json:"delete_source_branch,omitempty"`
1276+ unknownFields protoimpl.UnknownFields
1277+ sizeCache protoimpl.SizeCache
1272 }1278 }
1273 1279
1274 func (x *MergeMergeRequestRequest) Reset() {1280 func (x *MergeMergeRequestRequest) Reset() {
@@ -1329,15 +1335,26 @@ func (x *MergeMergeRequestRequest) GetHeadId() string {
1329 return ""1335 return ""
1330 }1336 }
1331 1337
1338+func (x *MergeMergeRequestRequest) GetDeleteSourceBranch() bool {
1339+ if x != nil {
1340+ return x.DeleteSourceBranch
1341+ }
1342+ return false
1343+}
1344+
1332 // MergeMergeRequestResponse holds the merged merge request.1345 // MergeMergeRequestResponse holds the merged merge request.
1333 type MergeMergeRequestResponse struct {1346 type MergeMergeRequestResponse struct {
1334 state protoimpl.MessageState `protogen:"open.v1"`1347 state protoimpl.MessageState `protogen:"open.v1"`
1335 // MergeRequest is the merge request after the merge: state MERGED,1348 // MergeRequest is the merge request after the merge: state MERGED,
1336 // merged_commit_id set to the new tip of the target branch and merge_mode1349 // merged_commit_id set to the new tip of the target branch and merge_mode
1337 // set to the mode used.1350 // set to the mode used.
1338- MergeRequest *MergeRequest `protobuf:"bytes,1,opt,name=merge_request,json=mergeRequest,proto3" json:"merge_request,omitempty"`1351+ MergeRequest *MergeRequest `protobuf:"bytes,1,opt,name=merge_request,json=mergeRequest,proto3" json:"merge_request,omitempty"`
1339- unknownFields protoimpl.UnknownFields1352+ // SourceBranchDeleted is true when delete_source_branch was set and the
1340- sizeCache protoimpl.SizeCache1353+ // branch is gone. It is false when the deletion failed after the merge (the
1354+ // branch moved in between, say): the merge stands and the branch is kept.
1355+ SourceBranchDeleted bool `protobuf:"varint,2,opt,name=source_branch_deleted,json=sourceBranchDeleted,proto3" json:"source_branch_deleted,omitempty"`
1356+ unknownFields protoimpl.UnknownFields
1357+ sizeCache protoimpl.SizeCache
1341 }1358 }
1342 1359
1343 func (x *MergeMergeRequestResponse) Reset() {1360 func (x *MergeMergeRequestResponse) Reset() {
@@ -1377,6 +1394,13 @@ func (x *MergeMergeRequestResponse) GetMergeRequest() *MergeRequest {
1377 return nil1394 return nil
1378 }1395 }
1379 1396
1397+func (x *MergeMergeRequestResponse) GetSourceBranchDeleted() bool {
1398+ if x != nil {
1399+ return x.SourceBranchDeleted
1400+ }
1401+ return false
1402+}
1403+
1380 // MarkReadRequest records a read position.1404 // MarkReadRequest records a read position.
1381 type MarkReadRequest struct {1405 type MarkReadRequest struct {
1382 state protoimpl.MessageState `protogen:"open.v1"`1406 state protoimpl.MessageState `protogen:"open.v1"`
@@ -1568,14 +1592,16 @@ const file_ermite_merge_request_proto_rawDesc = "" +
1568 "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +1592 "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +
1569 "\x06number\x18\x02 \x01(\x03R\x06number\"V\n" +1593 "\x06number\x18\x02 \x01(\x03R\x06number\"V\n" +
1570 "\x19CloseMergeRequestResponse\x129\n" +1594 "\x19CloseMergeRequestResponse\x129\n" +
1571- "\rmerge_request\x18\x01 \x01(\v2\x14.ermite.MergeRequestR\fmergeRequest\"\x86\x01\n" +1595+ "\rmerge_request\x18\x01 \x01(\v2\x14.ermite.MergeRequestR\fmergeRequest\"\xb8\x01\n" +
1572 "\x18MergeMergeRequestRequest\x12\x12\n" +1596 "\x18MergeMergeRequestRequest\x12\x12\n" +
1573 "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +1597 "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +
1574 "\x06number\x18\x02 \x01(\x03R\x06number\x12%\n" +1598 "\x06number\x18\x02 \x01(\x03R\x06number\x12%\n" +
1575 "\x04mode\x18\x03 \x01(\x0e2\x11.ermite.MergeModeR\x04mode\x12\x17\n" +1599 "\x04mode\x18\x03 \x01(\x0e2\x11.ermite.MergeModeR\x04mode\x12\x17\n" +
1576- "\ahead_id\x18\x04 \x01(\tR\x06headId\"V\n" +1600+ "\ahead_id\x18\x04 \x01(\tR\x06headId\x120\n" +
1601+ "\x14delete_source_branch\x18\x05 \x01(\bR\x12deleteSourceBranch\"\x8a\x01\n" +
1577 "\x19MergeMergeRequestResponse\x129\n" +1602 "\x19MergeMergeRequestResponse\x129\n" +
1578- "\rmerge_request\x18\x01 \x01(\v2\x14.ermite.MergeRequestR\fmergeRequest\"V\n" +1603+ "\rmerge_request\x18\x01 \x01(\v2\x14.ermite.MergeRequestR\fmergeRequest\x122\n" +
1604+ "\x15source_branch_deleted\x18\x02 \x01(\bR\x13sourceBranchDeleted\"V\n" +
1579 "\x0fMarkReadRequest\x12\x12\n" +1605 "\x0fMarkReadRequest\x12\x12\n" +
1580 "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +1606 "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +
1581 "\x06number\x18\x02 \x01(\x03R\x06number\x12\x17\n" +1607 "\x06number\x18\x02 \x01(\x03R\x06number\x12\x17\n" +
M proto/ermite/merge_request.proto +10 -0
@@ -345,6 +345,12 @@ message MergeMergeRequestRequest {
345345 // MergeRequest.head_id it was shown. Required. The merge happens only if the
346346 // source branch still points to it.
347347 string head_id = 4;
348+ // DeleteSourceBranch deletes the source branch after the merge. The merge
349+ // is refused before anything changes when the source branch is the default
350+ // branch of the repository or the source or target of another open merge
351+ // request. Servers without the "merge_requests.delete_branch" feature ignore
352+ // it.
353+ bool delete_source_branch = 5;
348354 }
349355
350356 // MergeMergeRequestResponse holds the merged merge request.
@@ -353,6 +359,10 @@ message MergeMergeRequestResponse {
353359 // merged_commit_id set to the new tip of the target branch and merge_mode
354360 // set to the mode used.
355361 MergeRequest merge_request = 1;
362+ // SourceBranchDeleted is true when delete_source_branch was set and the
363+ // branch is gone. It is false when the deletion failed after the merge (the
364+ // branch moved in between, say): the merge stands and the branch is kept.
365+ bool source_branch_deleted = 2;
356366 }
357367
358368 // MarkReadRequest records a read position.
@@ -345,6 +345,12 @@ message MergeMergeRequestRequest {
345 // MergeRequest.head_id it was shown. Required. The merge happens only if the345 // MergeRequest.head_id it was shown. Required. The merge happens only if the
346 // source branch still points to it.346 // source branch still points to it.
347 string head_id = 4;347 string head_id = 4;
348+ // DeleteSourceBranch deletes the source branch after the merge. The merge
349+ // is refused before anything changes when the source branch is the default
350+ // branch of the repository or the source or target of another open merge
351+ // request. Servers without the "merge_requests.delete_branch" feature ignore
352+ // it.
353+ bool delete_source_branch = 5;
348 }354 }
349 355
350 // MergeMergeRequestResponse holds the merged merge request.356 // MergeMergeRequestResponse holds the merged merge request.
@@ -353,6 +359,10 @@ message MergeMergeRequestResponse {
353 // merged_commit_id set to the new tip of the target branch and merge_mode359 // merged_commit_id set to the new tip of the target branch and merge_mode
354 // set to the mode used.360 // set to the mode used.
355 MergeRequest merge_request = 1;361 MergeRequest merge_request = 1;
362+ // SourceBranchDeleted is true when delete_source_branch was set and the
363+ // branch is gone. It is false when the deletion failed after the merge (the
364+ // branch moved in between, say): the merge stands and the branch is kept.
365+ bool source_branch_deleted = 2;
356 }366 }
357 367
358 // MarkReadRequest records a read position.368 // MarkReadRequest records a read position.

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 2

Tags 18

↑↓ moveenter openesc close