Skip to content

Commit 57e5f2f

Browse files
authored
Merge pull request #13327 from gitbutlerapp/dp-remove-branch-mode
Create branches directly with `b` in TUI
2 parents d1dd339 + 67c7f98 commit 57e5f2f

13 files changed

Lines changed: 313 additions & 782 deletions

File tree

crates/but/src/command/legacy/status/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ impl FilesStatusFlag {
9898
}
9999
}
100100

101+
#[expect(dead_code)]
101102
pub fn is_none(self) -> bool {
102103
matches!(self, Self::None)
103104
}

crates/but/src/command/legacy/status/tui/cursor/mod.rs

Lines changed: 3 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@ use crate::{
88
command::legacy::status::{
99
FilesStatusFlag, StatusOutputLine,
1010
output::StatusOutputLineData,
11-
tui::{
12-
Mode, SelectAfterReload, branch_operation_display, commit_operation_display,
13-
move_operation_display,
14-
},
11+
tui::{Mode, SelectAfterReload, commit_operation_display, move_operation_display},
1512
},
1613
};
1714

@@ -217,7 +214,7 @@ impl Cursor {
217214
Some(Self(idx))
218215
}
219216

220-
/// Selects the merge-base line.
217+
/// Select the merge-base line.
221218
pub(super) fn select_merge_base(lines: &[StatusOutputLine]) -> Option<Self> {
222219
let idx = lines
223220
.iter()
@@ -392,91 +389,6 @@ impl Cursor {
392389
.find(|(_, line)| is_jump_target_in_mode(line, mode, show_files))?;
393390
Some(Self(target_idx))
394391
}
395-
396-
/// Returns the cursor position for the closest branch based on the currently selected row.
397-
#[must_use]
398-
pub(super) fn closest_branch_cursor(self, lines: &[StatusOutputLine]) -> Option<Self> {
399-
if self.0 >= lines.len() {
400-
return None;
401-
}
402-
403-
let selected_line = lines.get(self.0)?;
404-
405-
if matches!(selected_line.data, StatusOutputLineData::MergeBase) {
406-
return Some(self);
407-
}
408-
409-
let selected_cli_id = selected_line.data.cli_id().map(|id| &**id)?;
410-
411-
let target_idx = match (&selected_line.data, selected_cli_id) {
412-
(StatusOutputLineData::Branch { .. }, CliId::Branch { .. }) => Some(self.0),
413-
(StatusOutputLineData::Commit { stack_id, .. }, CliId::Commit { .. }) => stack_id
414-
.and_then(|stack_id| branch_index_for_stack(lines, stack_id))
415-
.or_else(|| previous_branch_index(lines, self.0)),
416-
(_, CliId::CommittedFile { .. }) | (_, CliId::Commit { .. }) => {
417-
previous_branch_index(lines, self.0)
418-
}
419-
(StatusOutputLineData::StagedChanges { .. }, _)
420-
| (StatusOutputLineData::StagedFile { .. }, _) => selected_cli_id
421-
.stack_id()
422-
.and_then(|stack_id| branch_index_for_stack(lines, stack_id))
423-
.or_else(|| first_branch_index(lines)),
424-
_ => first_branch_index(lines),
425-
};
426-
427-
target_idx.or_else(|| merge_base_index(lines)).map(Self)
428-
}
429-
}
430-
431-
/// Returns the index of the first branch line, if any.
432-
fn first_branch_index(lines: &[StatusOutputLine]) -> Option<usize> {
433-
lines.iter().position(|line| {
434-
matches!(
435-
line.data.cli_id().map(|id| &**id),
436-
Some(CliId::Branch { .. })
437-
)
438-
})
439-
}
440-
441-
/// Returns the index of the merge-base line, if any.
442-
fn merge_base_index(lines: &[StatusOutputLine]) -> Option<usize> {
443-
lines
444-
.iter()
445-
.position(|line| matches!(line.data, StatusOutputLineData::MergeBase))
446-
}
447-
448-
/// Returns the index of the nearest preceding branch line before or at `from_idx`.
449-
fn previous_branch_index(lines: &[StatusOutputLine], from_idx: usize) -> Option<usize> {
450-
lines
451-
.iter()
452-
.enumerate()
453-
.take(from_idx + 1)
454-
.rev()
455-
.find(|(_, line)| {
456-
matches!(
457-
line.data.cli_id().map(|id| &**id),
458-
Some(CliId::Branch { .. })
459-
)
460-
})
461-
.map(|(idx, _)| idx)
462-
}
463-
464-
/// Returns the index of the first branch line that belongs to `stack_id`.
465-
fn branch_index_for_stack(
466-
lines: &[StatusOutputLine],
467-
stack_id: gitbutler_stack::StackId,
468-
) -> Option<usize> {
469-
lines.iter().position(|line| {
470-
if let Some(CliId::Branch {
471-
stack_id: Some(branch_stack_id),
472-
..
473-
}) = line.data.cli_id().map(|id| &**id)
474-
{
475-
*branch_stack_id == stack_id
476-
} else {
477-
false
478-
}
479-
})
480392
}
481393

482394
/// Returns true if a line marks the boundary of a commit list within a branch section.
@@ -509,7 +421,6 @@ fn is_section_header(line: &StatusOutputLine, mode: &Mode) -> bool {
509421
| Mode::Command(..)
510422
| Mode::Commit(..)
511423
| Mode::Move(..)
512-
| Mode::Branch
513424
| Mode::Details => {
514425
matches!(
515426
line.data,
@@ -572,11 +483,7 @@ pub(super) fn is_selectable_in_mode(
572483
return true;
573484
}
574485
}
575-
Mode::Command(..)
576-
| Mode::InlineReword(..)
577-
| Mode::Normal
578-
| Mode::Branch
579-
| Mode::Details => {}
486+
Mode::Command(..) | Mode::InlineReword(..) | Mode::Normal | Mode::Details => {}
580487
}
581488

582489
match mode {
@@ -598,7 +505,6 @@ pub(super) fn is_selectable_in_mode(
598505
.is_some_and(|cli_id| rub_mode.available_targets.contains(cli_id)),
599506
Mode::Commit(commit_mode) => commit_operation_display(&line.data, commit_mode).is_some(),
600507
Mode::Move(move_mode) => move_operation_display(&line.data, move_mode).is_some(),
601-
Mode::Branch => branch_operation_display(&line.data).is_some(),
602508
Mode::InlineReword(..) | Mode::Command(..) => {
603509
// you can't actually move the selection in these modes
604510
// but returning `false` would dim every line which hurts UX

crates/but/src/command/legacy/status/tui/cursor/tests.rs

Lines changed: 1 addition & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ fn branch_cli_id(name: &str, id: &str, stack_id: Option<StackId>) -> Arc<CliId>
5656
})
5757
}
5858

59+
#[expect(dead_code)]
5960
fn stack_cli_id(id: &str, stack_id: StackId) -> Arc<CliId> {
6061
Arc::new(CliId::Stack {
6162
id: id.into(),
@@ -877,7 +878,6 @@ fn movement_does_not_panic_or_move_when_cursor_is_out_of_bounds() {
877878
cursor = new_cursor;
878879
}
879880

880-
assert_eq!(cursor.closest_branch_cursor(&lines), None);
881881
assert_eq!(cursor, Cursor(99));
882882
}
883883

@@ -1044,163 +1044,6 @@ fn move_previous_section_does_not_move_when_on_first_jump_target() {
10441044
assert_eq!(cursor, Cursor(0));
10451045
}
10461046

1047-
#[test]
1048-
fn closest_branch_cursor_from_commit_selects_nearest_preceding_branch() {
1049-
let lines = vec![
1050-
line(StatusOutputLineData::Branch {
1051-
cli_id: branch_cli_id("main", "b0", None),
1052-
}),
1053-
line(StatusOutputLineData::Commit {
1054-
cli_id: commit_cli_id("1111111111111111111111111111111111111111", "c0"),
1055-
stack_id: None,
1056-
classification: CommitClassification::LocalOnly,
1057-
}),
1058-
line(StatusOutputLineData::Branch {
1059-
cli_id: branch_cli_id("feature", "b1", None),
1060-
}),
1061-
line(StatusOutputLineData::Commit {
1062-
cli_id: commit_cli_id("2222222222222222222222222222222222222222", "c1"),
1063-
stack_id: None,
1064-
classification: CommitClassification::LocalOnly,
1065-
}),
1066-
];
1067-
1068-
let cursor = Cursor(3);
1069-
1070-
assert_eq!(cursor.closest_branch_cursor(&lines), Some(Cursor(2)));
1071-
}
1072-
1073-
#[test]
1074-
fn closest_branch_cursor_from_committed_file_selects_nearest_preceding_branch() {
1075-
let lines = vec![
1076-
line(StatusOutputLineData::Branch {
1077-
cli_id: branch_cli_id("main", "b0", None),
1078-
}),
1079-
line(StatusOutputLineData::Commit {
1080-
cli_id: commit_cli_id("1111111111111111111111111111111111111111", "c0"),
1081-
stack_id: None,
1082-
classification: CommitClassification::LocalOnly,
1083-
}),
1084-
line(StatusOutputLineData::File {
1085-
cli_id: committed_file_cli_id(
1086-
"1111111111111111111111111111111111111111",
1087-
"src/lib.rs",
1088-
"f0",
1089-
),
1090-
}),
1091-
];
1092-
1093-
let cursor = Cursor(2);
1094-
1095-
assert_eq!(cursor.closest_branch_cursor(&lines), Some(Cursor(0)));
1096-
}
1097-
1098-
#[test]
1099-
fn closest_branch_cursor_from_branch_is_noop() {
1100-
let lines = vec![line(StatusOutputLineData::Branch {
1101-
cli_id: branch_cli_id("main", "b0", None),
1102-
})];
1103-
1104-
let cursor = Cursor(0);
1105-
1106-
assert_eq!(cursor.closest_branch_cursor(&lines), Some(Cursor(0)));
1107-
}
1108-
1109-
#[test]
1110-
fn closest_branch_cursor_from_unassigned_changes_selects_first_branch() {
1111-
let lines = vec![
1112-
line(StatusOutputLineData::UnassignedChanges {
1113-
cli_id: unassigned("u0"),
1114-
}),
1115-
line(StatusOutputLineData::Branch {
1116-
cli_id: branch_cli_id("first", "b0", None),
1117-
}),
1118-
line(StatusOutputLineData::Branch {
1119-
cli_id: branch_cli_id("second", "b1", None),
1120-
}),
1121-
];
1122-
1123-
let cursor = Cursor(0);
1124-
1125-
assert_eq!(cursor.closest_branch_cursor(&lines), Some(Cursor(1)));
1126-
}
1127-
1128-
#[test]
1129-
fn closest_branch_cursor_from_unassigned_file_selects_first_branch() {
1130-
let lines = vec![
1131-
line(StatusOutputLineData::File {
1132-
cli_id: unassigned("u0"),
1133-
}),
1134-
line(StatusOutputLineData::Branch {
1135-
cli_id: branch_cli_id("first", "b0", None),
1136-
}),
1137-
];
1138-
1139-
let cursor = Cursor(0);
1140-
1141-
assert_eq!(cursor.closest_branch_cursor(&lines), Some(Cursor(1)));
1142-
}
1143-
1144-
#[test]
1145-
fn closest_branch_cursor_from_staged_file_prefers_branch_for_matching_stack() {
1146-
let stack_a = StackId::generate();
1147-
let stack_b = StackId::generate();
1148-
let lines = vec![
1149-
line(StatusOutputLineData::Branch {
1150-
cli_id: branch_cli_id("a", "b0", Some(stack_a)),
1151-
}),
1152-
line(StatusOutputLineData::Branch {
1153-
cli_id: branch_cli_id("b", "b1", Some(stack_b)),
1154-
}),
1155-
line(StatusOutputLineData::StagedFile {
1156-
cli_id: stack_cli_id("s0", stack_b),
1157-
}),
1158-
];
1159-
1160-
let cursor = Cursor(2);
1161-
1162-
assert_eq!(cursor.closest_branch_cursor(&lines), Some(Cursor(1)));
1163-
}
1164-
1165-
#[test]
1166-
fn closest_branch_cursor_from_merge_base_is_noop() {
1167-
let lines = vec![
1168-
line(StatusOutputLineData::Branch {
1169-
cli_id: branch_cli_id("main", "b0", None),
1170-
}),
1171-
line(StatusOutputLineData::MergeBase),
1172-
];
1173-
1174-
let cursor = Cursor(1);
1175-
1176-
assert_eq!(cursor.closest_branch_cursor(&lines), Some(Cursor(1)));
1177-
}
1178-
1179-
#[test]
1180-
fn closest_branch_cursor_falls_back_to_merge_base_when_no_branch_exists() {
1181-
let lines = vec![
1182-
line(StatusOutputLineData::UnassignedChanges {
1183-
cli_id: unassigned("u0"),
1184-
}),
1185-
line(StatusOutputLineData::MergeBase),
1186-
];
1187-
1188-
let cursor = Cursor(0);
1189-
1190-
assert_eq!(cursor.closest_branch_cursor(&lines), Some(Cursor(1)));
1191-
}
1192-
1193-
#[test]
1194-
fn closest_branch_cursor_is_none_when_no_branch_exists() {
1195-
let lines = vec![line(StatusOutputLineData::UnassignedChanges {
1196-
cli_id: unassigned("u0"),
1197-
})];
1198-
1199-
let cursor = Cursor(0);
1200-
1201-
assert_eq!(cursor.closest_branch_cursor(&lines), None);
1202-
}
1203-
12041047
#[test]
12051048
fn move_up_in_rub_mode_skips_unavailable_targets() {
12061049
let allowed = Arc::new(CliId::Branch {

crates/but/src/command/legacy/status/tui/details/mod.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ use crate::{
4444
id::{UncommittedCliId, UncommittedHunk},
4545
};
4646

47-
use super::{BranchMessage, RubSource};
47+
use super::RubSource;
4848

4949
mod details_cursor;
5050

@@ -209,6 +209,7 @@ impl Details {
209209
| Message::MoveCursorNextSection
210210
| Message::SelectUnassigned
211211
| Message::Reload(_)
212+
| Message::NewBranch
212213
| Message::RunAfterConfirmation(_) => true,
213214

214215
Message::Commit(commit_message) => match commit_message {
@@ -238,10 +239,6 @@ impl Details {
238239
MoveMessage::Start | MoveMessage::SetInsertSide(_) => false,
239240
MoveMessage::Confirm => true,
240241
},
241-
Message::Branch(branch_message) => match branch_message {
242-
BranchMessage::Start => false,
243-
BranchMessage::New => true,
244-
},
245242
Message::Details(details_message) => match details_message {
246243
DetailsMessage::Unlock // `unlock` sets the dirty flag if necessary
247244
| DetailsMessage::Deselect

0 commit comments

Comments
 (0)