Skip to content

Commit 61e85a6

Browse files
authored
Merge pull request #12825 from gitbutlerapp/david/gb-1134-dont-buffer-status-output-in-one-shot-mode
fix(cli): don't buffer status output in one shot mode
2 parents f2d0693 + beaaabc commit 61e85a6

File tree

4 files changed

+159
-134
lines changed

4 files changed

+159
-134
lines changed

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

Lines changed: 55 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -157,13 +157,16 @@ pub(crate) async fn worktree(
157157
return Ok(());
158158
};
159159

160-
let status_output = build_status_output(ctx, &status_ctx)?;
161160
match render_mode {
162161
StatusRenderMode::Oneshot => {
163-
render_oneshot::render_oneshot(status_output, human_out)?;
162+
let mut output = StatusOutput::Immediate { out: human_out };
163+
build_status_output(ctx, &status_ctx, &mut output)?;
164164
}
165165
StatusRenderMode::Tui { debug } => {
166-
tui::render_tui(ctx, out, &mode, flags, status_output, debug).await?;
166+
let mut lines = Vec::new();
167+
let mut output = StatusOutput::Buffer { lines: &mut lines };
168+
build_status_output(ctx, &status_ctx, &mut output)?;
169+
tui::render_tui(ctx, out, &mode, flags, lines, debug).await?;
167170
}
168171
}
169172

@@ -347,26 +350,26 @@ fn truncation_policy(render_mode: StatusRenderMode, is_paged: bool) -> bool {
347350
fn build_status_output(
348351
ctx: &mut Context,
349352
status_ctx: &StatusContext<'_>,
350-
) -> anyhow::Result<StatusOutput> {
351-
let mut output = StatusOutput::default();
352-
print_update_notice(ctx, status_ctx, &mut output)?;
353-
print_worktree_status(ctx, status_ctx, &mut output)?;
354-
print_upstream_state(ctx, status_ctx, &mut output)?;
355-
print_common_merge_base_summary(status_ctx, &mut output)?;
353+
output: &mut StatusOutput<'_>,
354+
) -> anyhow::Result<()> {
355+
print_update_notice(ctx, status_ctx, output)?;
356+
print_worktree_status(ctx, status_ctx, output)?;
357+
print_upstream_state(ctx, status_ctx, output)?;
358+
print_common_merge_base_summary(status_ctx, output)?;
356359
let not_on_workspace = matches!(
357360
status_ctx.mode,
358361
gitbutler_operating_modes::OperatingMode::OutsideWorkspace(_)
359362
);
360-
print_outside_workspace_warning(not_on_workspace, &mut output)?;
361-
print_hint(status_ctx, not_on_workspace, &mut output)?;
362-
Ok(output)
363+
print_outside_workspace_warning(not_on_workspace, output)?;
364+
print_hint(status_ctx, not_on_workspace, output)?;
365+
Ok(())
363366
}
364367

365368
/// Print update information for human output when a newer `but` version is available.
366369
fn print_update_notice(
367370
ctx: &mut Context,
368371
status_ctx: &StatusContext<'_>,
369-
output: &mut StatusOutput,
372+
output: &mut StatusOutput<'_>,
370373
) -> anyhow::Result<()> {
371374
let cache = ctx.app_cache.get_cache()?;
372375
if let Ok(Some(update)) = but_update::available_update(&cache) {
@@ -378,8 +381,8 @@ fn print_update_notice(
378381
)
379382
.into_iter()
380383
.collect(),
381-
);
382-
output.connector(Vec::from([Span::raw("")]));
384+
)?;
385+
output.connector(Vec::from([Span::raw("")]))?;
383386
}
384387

385388
Ok(())
@@ -388,15 +391,15 @@ fn print_update_notice(
388391
/// Print a warning when operating outside the GitButler workspace.
389392
fn print_outside_workspace_warning(
390393
not_on_workspace: bool,
391-
output: &mut StatusOutput,
394+
output: &mut StatusOutput<'_>,
392395
) -> anyhow::Result<()> {
393396
if not_on_workspace {
394397
output.warning(Vec::from([Span::raw(
395398
"⚠️ You are in plain Git mode, directly on a branch. Some commands may be unavailable. ⚠️",
396-
)]));
399+
)]))?;
397400
output.warning(Vec::from([Span::raw(
398401
"⚠️ More info: https://github.com/gitbutlerapp/gitbutler/issues/11866 ⚠️",
399-
)]));
402+
)]))?;
400403
}
401404

402405
Ok(())
@@ -406,13 +409,13 @@ fn print_outside_workspace_warning(
406409
fn print_hint(
407410
status_ctx: &StatusContext<'_>,
408411
not_on_workspace: bool,
409-
output: &mut StatusOutput,
412+
output: &mut StatusOutput<'_>,
410413
) -> anyhow::Result<()> {
411414
if !status_ctx.flags.hint {
412415
return Ok(());
413416
}
414417

415-
output.connector(Vec::from([Span::raw("")]));
418+
output.connector(Vec::from([Span::raw("")]))?;
416419

417420
// Determine what hint to show based on workspace state
418421
let has_uncommitted_files = !status_ctx.worktree_changes.is_empty();
@@ -427,7 +430,7 @@ fn print_hint(
427430
"Hint: run `but help` for all commands"
428431
};
429432

430-
output.hint(Vec::from([Span::styled(hint_text, Style::default().dim())]));
433+
output.hint(Vec::from([Span::styled(hint_text, Style::default().dim())]))?;
431434

432435
Ok(())
433436
}
@@ -436,7 +439,7 @@ fn print_hint(
436439
fn print_upstream_state(
437440
ctx: &mut Context,
438441
status_ctx: &StatusContext<'_>,
439-
output: &mut StatusOutput,
442+
output: &mut StatusOutput<'_>,
440443
) -> anyhow::Result<()> {
441444
let Some(upstream) = &status_ctx.upstream_state else {
442445
return Ok(());
@@ -470,7 +473,7 @@ fn print_upstream_state(
470473
Style::default().dim(),
471474
));
472475
}
473-
output.upstream_changes(Vec::from([Span::raw("┊╭┄")]), upstream_summary);
476+
output.upstream_changes(Vec::from([Span::raw("┊╭┄")]), upstream_summary)?;
474477

475478
// Display detailed list of upstream commits
476479
if let Some(base_branch) = &status_ctx.base_branch
@@ -493,7 +496,7 @@ fn print_upstream_state(
493496
Span::raw(" "),
494497
Span::styled(truncated_msg, Style::default().dim()),
495498
]),
496-
);
499+
)?;
497500
}
498501
let hidden_commits = base_branch.behind.saturating_sub(8);
499502
if hidden_commits > 0 {
@@ -503,10 +506,10 @@ fn print_upstream_state(
503506
format!("and {hidden_commits} more…"),
504507
Style::default().dim(),
505508
)]),
506-
);
509+
)?;
507510
}
508511
}
509-
output.connector(Vec::from([Span::raw("┊┊")]));
512+
output.connector(Vec::from([Span::raw("┊┊")]))?;
510513
} else {
511514
// Without --upstream, show the summary with latest commit info
512515
let mut upstream_summary = Vec::from([
@@ -523,7 +526,7 @@ fn print_upstream_state(
523526
output.upstream_changes(
524527
Vec::from([Span::raw("┊"), dot, Span::raw(" ")]),
525528
upstream_summary,
526-
);
529+
)?;
527530
}
528531

529532
Ok(())
@@ -532,7 +535,7 @@ fn print_upstream_state(
532535
/// Print the common merge-base summary line at the bottom of the status tree.
533536
fn print_common_merge_base_summary(
534537
status_ctx: &StatusContext<'_>,
535-
output: &mut StatusOutput,
538+
output: &mut StatusOutput<'_>,
536539
) -> anyhow::Result<()> {
537540
let first_line = status_ctx
538541
.common_merge_base_data
@@ -566,15 +569,15 @@ fn print_common_merge_base_summary(
566569
Span::raw(" "),
567570
Span::raw(first_line.to_string()),
568571
]),
569-
);
572+
)?;
570573
Ok(())
571574
}
572575

573576
/// Print per-stack status sections for human-readable output.
574577
fn print_worktree_status(
575578
ctx: &mut Context,
576579
status_ctx: &StatusContext<'_>,
577-
output: &mut StatusOutput,
580+
output: &mut StatusOutput<'_>,
578581
) -> anyhow::Result<()> {
579582
for (i, (stack_id, (stack_with_id, assignments))) in status_ctx.stack_details.iter().enumerate()
580583
{
@@ -651,14 +654,14 @@ fn print_assignments(
651654
branch_name: Option<&BStr>,
652655
assignments: &[FileAssignment],
653656
unstaged: bool,
654-
output: &mut StatusOutput,
657+
output: &mut StatusOutput<'_>,
655658
) -> anyhow::Result<()> {
656659
// if there are no assignments and we're in the unstaged section, print "(no changes)" and return
657660
if assignments.is_empty() && unstaged {
658661
output.no_assignments_unstaged(
659662
Vec::from([Span::raw("┊ ")]),
660663
Vec::from([Span::styled("no changes", Style::default().dim().italic())]),
661-
);
664+
)?;
662665
return Ok(());
663666
}
664667

@@ -687,7 +690,7 @@ fn print_assignments(
687690
Span::raw("]"),
688691
]),
689692
staged_changes_cli_id,
690-
);
693+
)?;
691694
}
692695

693696
let max_id_width = assignments
@@ -759,14 +762,14 @@ fn print_assignments(
759762
}
760763

761764
if unstaged {
762-
output.unstaged_file(Vec::from([Span::raw("┊ ")]), file_line, file_cli_id);
765+
output.unstaged_file(Vec::from([Span::raw("┊ ")]), file_line, file_cli_id)?;
763766
} else {
764-
output.staged_file(Vec::from([Span::raw("┊ │ ")]), file_line, file_cli_id);
767+
output.staged_file(Vec::from([Span::raw("┊ │ ")]), file_line, file_cli_id)?;
765768
}
766769
}
767770

768771
if !unstaged && !assignments.is_empty() {
769-
output.connector(Vec::from([Span::raw("┊ │")]));
772+
output.connector(Vec::from([Span::raw("┊ │")]))?;
770773
}
771774

772775
Ok(())
@@ -779,7 +782,7 @@ fn print_group(
779782
assignments: &[FileAssignment],
780783
stack_mark: &mut Option<Span<'static>>,
781784
first: bool,
782-
output: &mut StatusOutput,
785+
output: &mut StatusOutput<'_>,
783786
) -> anyhow::Result<()> {
784787
let repo = ctx
785788
.legacy_project
@@ -790,7 +793,7 @@ fn print_group(
790793
for segment in &stack_with_id.segments {
791794
let notch = if first { "╭" } else { "├" };
792795
if !first {
793-
output.connector(Vec::from([Span::raw("┊│")]));
796+
output.connector(Vec::from([Span::raw("┊│")]))?;
794797
}
795798

796799
let no_commits = if segment.workspace_commits.is_empty() {
@@ -901,7 +904,7 @@ fn print_group(
901904
Vec::from([Span::raw(format!("┊{notch}┄"))]),
902905
branch_line,
903906
branch_cli_id,
904-
);
907+
)?;
905908

906909
*stack_mark = None; // Only show the stack mark for the first branch
907910
first = false;
@@ -913,14 +916,14 @@ fn print_group(
913916
.as_ref()
914917
.and_then(|rtb| rtb.as_bstr().strip_prefix(b"refs/remotes/"))
915918
.unwrap_or(b"unknown");
916-
output.connector(Vec::from([Span::raw("┊┊")]));
919+
output.connector(Vec::from([Span::raw("┊┊")]))?;
917920
output.upstream_changes(
918921
Vec::from([Span::raw("┊╭┄┄")]),
919922
Vec::from([Span::styled(
920923
format!("(upstream: on {})", BStr::new(tracking_branch)),
921924
Style::default().yellow(),
922925
)]),
923-
);
926+
)?;
924927
}
925928
for commit in &segment.remote_commits {
926929
let details =
@@ -938,7 +941,7 @@ fn print_group(
938941
)?;
939942
}
940943
if !segment.remote_commits.is_empty() {
941-
output.connector(Vec::from([Span::raw("┊-")]));
944+
output.connector(Vec::from([Span::raw("┊-")]))?;
942945
}
943946
for commit in segment.workspace_commits.iter() {
944947
let marked = crate::command::legacy::mark::commit_marked(
@@ -989,13 +992,13 @@ fn print_group(
989992
line.push(Span::raw(" "));
990993
line.push(stack_mark.clone());
991994
}
992-
output.unstaged_changes(Vec::from([Span::raw("╭┄")]), line, cli_id.clone());
995+
output.unstaged_changes(Vec::from([Span::raw("╭┄")]), line, cli_id.clone())?;
993996
print_assignments(&repo, status_ctx, None, None, assignments, true, output)?;
994997
}
995998
if !first {
996-
output.connector(Vec::from([Span::raw("├╯")]));
999+
output.connector(Vec::from([Span::raw("├╯")]))?;
9971000
}
998-
output.connector(Vec::from([Span::raw("┊")]));
1001+
output.connector(Vec::from([Span::raw("┊")]))?;
9991002
Ok(())
10001003
}
10011004

@@ -1081,7 +1084,7 @@ fn print_commit(
10811084
classification: CommitClassification,
10821085
marked: bool,
10831086
review_url: Option<String>,
1084-
output: &mut StatusOutput,
1087+
output: &mut StatusOutput<'_>,
10851088
) -> anyhow::Result<()> {
10861089
let dot = match classification {
10871090
CommitClassification::Upstream => Span::styled("●", Style::default().yellow()),
@@ -1181,7 +1184,7 @@ fn print_commit(
11811184
)
11821185
.collect(),
11831186
commit_cli_id.clone(),
1184-
);
1187+
)?;
11851188
let (message, is_empty_message) = commit_message_display_cli(
11861189
&commit.message,
11871190
status_ctx.flags.verbose,
@@ -1196,9 +1199,9 @@ fn print_commit(
11961199
);
11971200
let line = Vec::from([message]);
11981201
if is_empty_message {
1199-
output.empty_commit_message(Vec::from([Span::raw("┊│ ")]), line);
1202+
output.empty_commit_message(Vec::from([Span::raw("┊│ ")]), line)?;
12001203
} else {
1201-
output.commit_message(Vec::from([Span::raw("┊│ ")]), line);
1204+
output.commit_message(Vec::from([Span::raw("┊│ ")]), line)?;
12021205
}
12031206
} else {
12041207
output.commit(
@@ -1226,7 +1229,7 @@ fn print_commit(
12261229
)
12271230
.collect(),
12281231
commit_cli_id.clone(),
1229-
);
1232+
)?;
12301233
}
12311234
if status_ctx.flags.show_files {
12321235
match commit_changes {
@@ -1248,7 +1251,7 @@ fn print_commit(
12481251
.chain(inner.display_cli(false, status_ctx.should_truncate_for_terminal))
12491252
.collect(),
12501253
file_cli_id,
1251-
);
1254+
)?;
12521255
}
12531256
}
12541257
CommitChanges::Remote(tree_changes) => {
@@ -1260,7 +1263,7 @@ fn print_commit(
12601263
.into_iter()
12611264
.collect(),
12621265
commit_cli_id.clone(),
1263-
);
1266+
)?;
12641267
}
12651268
}
12661269
}

0 commit comments

Comments
 (0)