Skip to content

Commit f693afd

Browse files
committed
feat(cli): add JSON output to rub, stage, amend, and squash handlers
Commands that previously only produced human-readable output (rub, stage, amend, move, squash) now emit structured JSON when --json is used. This makes the 'result' field in --status-after JSON responses meaningful instead of null. - stage/assign: {"ok": true} - amend: {"ok": true, "newCommitId": "..."} - squash: {"ok": true, "newCommitId": "...", "squashedCount": N} - move: {"ok": true}
1 parent 3bf5964 commit f693afd

File tree

7 files changed

+104
-17
lines changed

7 files changed

+104
-17
lines changed

crates/but/src/command/legacy/rub/amend.rs

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,21 @@ pub(crate) fn uncommitted_to_commit(
2626
.collect();
2727

2828
let mut guard = ctx.exclusive_worktree_access();
29-
let new_commit = amend_diff_specs(ctx, diff_specs, stack_id, *oid, guard.write_permission())?
30-
.new_commit
31-
.map(|c| {
32-
let s = c.to_string();
33-
format!("{}{}", s[..2].blue().underline(), s[2..7].blue())
34-
})
35-
.unwrap_or_default();
29+
let outcome = amend_diff_specs(ctx, diff_specs, stack_id, *oid, guard.write_permission())?;
3630
if let Some(out) = out.for_human() {
31+
let new_commit = outcome
32+
.new_commit
33+
.map(|c| {
34+
let s = c.to_string();
35+
format!("{}{}", s[..2].blue().underline(), s[2..7].blue())
36+
})
37+
.unwrap_or_default();
3738
writeln!(out, "Amended {} → {}", description, new_commit)?;
39+
} else if let Some(out) = out.for_json() {
40+
out.write_value(serde_json::json!({
41+
"ok": true,
42+
"newCommitId": outcome.new_commit.map(|c| c.to_string()),
43+
}))?;
3844
}
3945
Ok(())
4046
}
@@ -52,15 +58,15 @@ pub(crate) fn assignments_to_commit(
5258
.map(|assignment| assignment.into())
5359
.collect();
5460
let mut guard = ctx.exclusive_worktree_access();
55-
let new_commit = amend_diff_specs(ctx, diff_specs, stack_id, *oid, guard.write_permission())?
56-
.new_commit
57-
.map(|c| {
58-
let s = c.to_string();
59-
format!("{}{}", s[..2].blue().underline(), s[2..7].blue())
60-
})
61-
.unwrap_or_default();
62-
61+
let outcome = amend_diff_specs(ctx, diff_specs, stack_id, *oid, guard.write_permission())?;
6362
if let Some(out) = out.for_human() {
63+
let new_commit = outcome
64+
.new_commit
65+
.map(|c| {
66+
let s = c.to_string();
67+
format!("{}{}", s[..2].blue().underline(), s[2..7].blue())
68+
})
69+
.unwrap_or_default();
6470
if let Some(branch_name) = branch_name {
6571
writeln!(
6672
out,
@@ -71,6 +77,11 @@ pub(crate) fn assignments_to_commit(
7177
} else {
7278
writeln!(out, "Amended unassigned files → {new_commit}")?;
7379
}
80+
} else if let Some(out) = out.for_json() {
81+
out.write_value(serde_json::json!({
82+
"ok": true,
83+
"newCommitId": outcome.new_commit.map(|c| c.to_string()),
84+
}))?;
7485
}
7586
Ok(())
7687
}

crates/but/src/command/legacy/rub/assign.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ pub(crate) fn assign_uncommitted_to_branch(
2222
do_assignments(ctx, reqs, out)?;
2323
if let Some(out) = out.for_human() {
2424
writeln!(out, "Staged {} → {}.", description, format!("[{branch_name}]").green())?;
25+
} else if let Some(out) = out.for_json() {
26+
out.write_value(serde_json::json!({"ok": true}))?;
2527
}
2628
Ok(())
2729
}
@@ -53,6 +55,8 @@ pub(crate) fn assign_uncommitted_to_stack(
5355
description,
5456
format!("[{}]", stack_id).green()
5557
)?;
58+
} else if let Some(out) = out.for_json() {
59+
out.write_value(serde_json::json!({"ok": true}))?;
5660
}
5761
Ok(())
5862
}
@@ -72,6 +76,8 @@ pub(crate) fn unassign_uncommitted(
7276
do_assignments(ctx, reqs, out)?;
7377
if let Some(out) = out.for_human() {
7478
writeln!(out, "Unstaged {description}")?;
79+
} else if let Some(out) = out.for_json() {
80+
out.write_value(serde_json::json!({"ok": true}))?;
7581
}
7682
Ok(())
7783
}
@@ -163,6 +169,8 @@ fn assign_all_inner(
163169
.unwrap_or_else(|| "unstaged".to_string().bold())
164170
)?;
165171
}
172+
} else if let Some(out) = out.for_json() {
173+
out.write_value(serde_json::json!({"ok": true}))?;
166174
}
167175
Ok(())
168176
}

crates/but/src/command/legacy/rub/commits.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ pub fn commited_file_to_another_commit(
3434

3535
if let Some(out) = out.for_human() {
3636
writeln!(out, "Moved files between commits!")?;
37+
} else if let Some(out) = out.for_json() {
38+
out.write_value(serde_json::json!({"ok": true}))?;
3739
}
3840

3941
Ok(())
@@ -73,6 +75,8 @@ pub fn uncommit_file(
7375

7476
if let Some(out) = out.for_human() {
7577
writeln!(out, "Uncommitted changes")?;
78+
} else if let Some(out) = out.for_json() {
79+
out.write_value(serde_json::json!({"ok": true}))?;
7680
}
7781

7882
Ok(())

crates/but/src/command/legacy/rub/move.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,16 @@ fn move_to_commit(
249249
" {} Reposition to the desired location (use the new commit ID from step 1)",
250250
"↳".cyan()
251251
)?;
252+
} else if let Some(out) = out.for_json() {
253+
out.write_value(serde_json::json!({
254+
"ok": false,
255+
"error": "cross_stack_position",
256+
"hint": format!(
257+
"Move to branch first with 'but move {} {}', then reposition",
258+
&source_oid.to_string()[..7],
259+
target_stack_head_branch
260+
),
261+
}))?;
252262
}
253263

254264
bail!("Cannot move commit to specific position in another stack");
@@ -311,14 +321,16 @@ fn move_to_branch(
311321
let cmd = format!("but move {} <target-commit> [--after]", &source_str[..7]);
312322
writeln!(out, " {}", cmd.green())?;
313323
}
324+
} else if let Some(out) = out.for_json() {
325+
out.write_value(serde_json::json!({"ok": true}))?;
314326
}
315327
} else {
316328
// Different stack - use move_commit API
317329
if let Some(illegal_move) =
318330
gitbutler_branch_actions::move_commit(ctx, target_stack_id, source_oid.to_git2(), source_stack_id)?
319331
{
320332
if let Some(out) = out.for_human() {
321-
match illegal_move {
333+
match &illegal_move {
322334
gitbutler_branch_actions::MoveCommitIllegalAction::DependsOnCommits(deps) => {
323335
writeln!(
324336
out,
@@ -343,6 +355,24 @@ fn move_to_branch(
343355
)?;
344356
}
345357
}
358+
} else if let Some(out) = out.for_json() {
359+
let (reason, deps) = match &illegal_move {
360+
gitbutler_branch_actions::MoveCommitIllegalAction::DependsOnCommits(deps) => {
361+
("depends_on_commits", Some(deps.clone()))
362+
}
363+
gitbutler_branch_actions::MoveCommitIllegalAction::HasDependentChanges(deps) => {
364+
("has_dependent_changes", Some(deps.clone()))
365+
}
366+
gitbutler_branch_actions::MoveCommitIllegalAction::HasDependentUncommittedChanges => {
367+
("has_dependent_uncommitted_changes", None)
368+
}
369+
};
370+
out.write_value(serde_json::json!({
371+
"ok": false,
372+
"error": "illegal_move",
373+
"reason": reason,
374+
"dependencies": deps,
375+
}))?;
346376
}
347377
bail!("Illegal move");
348378
}
@@ -371,6 +401,8 @@ fn move_to_branch(
371401
let cmd = format!("but move {} <target-commit> [--after]", &source_str[..7]);
372402
writeln!(out, " {}", cmd.green())?;
373403
}
404+
} else if let Some(out) = out.for_json() {
405+
out.write_value(serde_json::json!({"ok": true}))?;
374406
}
375407
}
376408

@@ -415,6 +447,8 @@ fn move_within_stack(
415447
if git2_source_oid == git2_target_oid {
416448
if let Some(out) = out.for_human() {
417449
writeln!(out, "Source and target are the same commit. Nothing to do.")?;
450+
} else if let Some(out) = out.for_json() {
451+
out.write_value(serde_json::json!({"ok": true}))?;
418452
}
419453
return Ok(());
420454
}
@@ -457,6 +491,8 @@ fn move_within_stack(
457491
position_desc,
458492
target_oid.to_string()[..7].blue()
459493
)?;
494+
} else if let Some(out) = out.for_json() {
495+
out.write_value(serde_json::json!({"ok": true}))?;
460496
}
461497

462498
Ok(())

crates/but/src/command/legacy/rub/move_commit.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pub(crate) fn to_branch(
3434
gitbutler_branch_actions::move_commit(ctx, target_stack_id, oid.to_git2(), source_stack_id)?
3535
{
3636
if let Some(out) = out.for_human() {
37-
match illegal_move {
37+
match &illegal_move {
3838
gitbutler_branch_actions::MoveCommitIllegalAction::DependsOnCommits(deps) => {
3939
writeln!(
4040
out,
@@ -58,6 +58,24 @@ pub(crate) fn to_branch(
5858
)
5959
}
6060
}?;
61+
} else if let Some(out) = out.for_json() {
62+
let (reason, deps) = match &illegal_move {
63+
gitbutler_branch_actions::MoveCommitIllegalAction::DependsOnCommits(deps) => {
64+
("depends_on_commits", Some(deps.clone()))
65+
}
66+
gitbutler_branch_actions::MoveCommitIllegalAction::HasDependentChanges(deps) => {
67+
("has_dependent_changes", Some(deps.clone()))
68+
}
69+
gitbutler_branch_actions::MoveCommitIllegalAction::HasDependentUncommittedChanges => {
70+
("has_dependent_uncommitted_changes", None)
71+
}
72+
};
73+
out.write_value(serde_json::json!({
74+
"ok": false,
75+
"error": "illegal_move",
76+
"reason": reason,
77+
"dependencies": deps,
78+
}))?;
6179
}
6280
bail!("Illegal move")
6381
}
@@ -68,6 +86,8 @@ pub(crate) fn to_branch(
6886
oid.to_string()[..7].blue(),
6987
format!("[{branch_name}]").green()
7088
)?;
89+
} else if let Some(out) = out.for_json() {
90+
out.write_value(serde_json::json!({"ok": true}))?;
7191
}
7292
Ok(())
7393
}

crates/but/src/command/legacy/rub/squash.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,12 @@ fn squash_commits_internal(
256256
final_commit_oid.to_gix().to_string()[..7].blue()
257257
)?
258258
}
259+
} else if let Some(out) = out.for_json() {
260+
out.write_value(serde_json::json!({
261+
"ok": true,
262+
"newCommitId": final_commit_oid.to_gix().to_string(),
263+
"squashedCount": source_oids.len(),
264+
}))?;
259265
}
260266
Ok(())
261267
}

crates/but/src/command/legacy/rub/undo.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ pub(crate) fn commit(ctx: &mut Context, oid: &ObjectId, out: &mut OutputChannel)
1010
gitbutler_branch_actions::undo_commit(ctx, stack_id_by_commit_id(ctx, oid)?, oid.to_git2())?;
1111
if let Some(out) = out.for_human() {
1212
writeln!(out, "Uncommitted {}", oid.to_string()[..7].blue())?;
13+
} else if let Some(out) = out.for_json() {
14+
out.write_value(serde_json::json!({"ok": true}))?;
1315
}
1416
Ok(())
1517
}

0 commit comments

Comments
 (0)