Skip to content

Commit ac96c21

Browse files
committed
Stack Entry: Carry associated review information
Stack entries also know the associtated forge review ID.
1 parent 7ad4653 commit ac96c21

File tree

7 files changed

+55
-8
lines changed

7 files changed

+55
-8
lines changed

crates/but-api/src/legacy/virtual_branches.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,16 @@ pub fn create_virtual_branch(ctx: &mut Context, branch: BranchCreateRequest) ->
5959
.context("BUG: didn't find a stack that was just created")?;
6060
let stack = &new_ws.stacks[stack_idx];
6161
let tip = stack.segments[segment_idx].tip().unwrap_or(repo.object_hash().null());
62+
let review_id = stack.segments[segment_idx]
63+
.metadata
64+
.as_ref()
65+
.and_then(|meta| meta.review.pull_request);
6266

6367
let out = StackEntryNoOpt {
6468
id: stack.id.context("BUG: all new stacks are created with an ID")?,
6569
heads: vec![StackHeadInfo {
6670
name: new_ref.shorten().into(),
71+
review_id,
6772
tip,
6873
is_checked_out: false,
6974
}],

crates/but-workspace/src/legacy/stacks.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,15 @@ fn id_from_name_v2_to_v3_opt(
5252
}))
5353
}
5454

55+
/// Get the associated forge review information out of the metadata, if any.
56+
fn review_id_from_meta(
57+
name: &gix::refs::FullNameRef,
58+
meta: &VirtualBranchesTomlMetadata,
59+
) -> anyhow::Result<Option<usize>> {
60+
let ref_meta = meta.branch(name)?;
61+
Ok(ref_meta.review.pull_request)
62+
}
63+
5564
/// Returns the list of branch information for the branches in a stack.
5665
pub fn stack_heads_info(stack: &Stack, repo: &gix::Repository) -> anyhow::Result<Vec<StackHeadInfo>> {
5766
let branches = stack
@@ -62,6 +71,7 @@ pub fn stack_heads_info(stack: &Stack, repo: &gix::Repository) -> anyhow::Result
6271
let tip = branch.head_oid(repo).ok()?;
6372
Some(StackHeadInfo {
6473
name: branch.name().to_owned().into(),
74+
review_id: branch.pr_number,
6575
tip,
6676
is_checked_out: false,
6777
})
@@ -84,6 +94,7 @@ fn try_from_stack_v3(
8494
.segments
8595
.into_iter()
8696
.map(|segment| -> anyhow::Result<_> {
97+
let review_id = segment.metadata.and_then(|meta| meta.review.pull_request);
8798
let ref_name = segment
8899
.ref_info
89100
.context("This type can't represent this state and it shouldn't have to")?
@@ -95,6 +106,7 @@ fn try_from_stack_v3(
95106
.and_then(|r| r.try_id())
96107
.map(|id| id.detach())
97108
.unwrap_or(repo.object_hash().null()),
109+
review_id,
98110
name: ref_name.shorten().into(),
99111
is_checked_out: segment.is_entrypoint,
100112
})
@@ -160,6 +172,7 @@ pub fn stacks_v3(
160172
heads: vec![StackHeadInfo {
161173
name: ref_name.shorten().into(),
162174
tip,
175+
review_id: review_id_from_meta(ref_name.as_ref(), meta)?,
163176
is_checked_out: false,
164177
}],
165178
tip,

crates/but-workspace/src/legacy/ui.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ pub struct StackHeadInfo {
1818
#[serde(with = "but_serde::object_id")]
1919
#[cfg_attr(feature = "export-ts", ts(type = "string"))]
2020
pub tip: gix::ObjectId,
21+
/// The associated forge review with this branch, e.g. GitHub PRs or GitLab MRs
22+
pub review_id: Option<usize>,
2123
/// If `true`, then this head is checked directly so `HEAD` points to it, and this is only ever `true` for a single head.
2224
/// This is `false` if the worktree is checked out.
2325
pub is_checked_out: bool,

crates/but-workspace/tests/workspace/ref_info/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ fn unborn_untracked() -> anyhow::Result<()> {
6969
StackHeadInfo {
7070
name: "main",
7171
tip: Sha1(0000000000000000000000000000000000000000),
72+
review_id: None,
7273
is_checked_out: false,
7374
},
7475
],
@@ -228,6 +229,7 @@ fn conflicted_in_local_branch() -> anyhow::Result<()> {
228229
StackHeadInfo {
229230
name: "main",
230231
tip: Sha1(84503317a1e1464381fcff65ece14bc1f4315b7c),
232+
review_id: None,
231233
is_checked_out: false,
232234
},
233235
],
@@ -347,6 +349,7 @@ fn single_branch() -> anyhow::Result<()> {
347349
StackHeadInfo {
348350
name: "main",
349351
tip: Sha1(b5743a3aa79957bcb7f654d7d4ad11d995ad5303),
352+
review_id: None,
350353
is_checked_out: false,
351354
},
352355
],
@@ -521,26 +524,31 @@ fn single_branch_multiple_segments() -> anyhow::Result<()> {
521524
StackHeadInfo {
522525
name: "main",
523526
tip: Sha1(b5743a3aa79957bcb7f654d7d4ad11d995ad5303),
527+
review_id: None,
524528
is_checked_out: false,
525529
},
526530
StackHeadInfo {
527531
name: "nine",
528532
tip: Sha1(344e3209e344c1eb90bedb4b00b4d4999a84406c),
533+
review_id: None,
529534
is_checked_out: false,
530535
},
531536
StackHeadInfo {
532537
name: "six",
533538
tip: Sha1(c4f2a356d6ed7250bab3dd7c58e1922b95f288c5),
539+
review_id: None,
534540
is_checked_out: false,
535541
},
536542
StackHeadInfo {
537543
name: "three",
538544
tip: Sha1(281da9454d5b41844d28e453e80b24925a7c8c7a),
545+
review_id: None,
539546
is_checked_out: false,
540547
},
541548
StackHeadInfo {
542549
name: "one",
543550
tip: Sha1(3d57fc18d679a1ba45bc7f79e394a5e2606719ee),
551+
review_id: None,
544552
is_checked_out: false,
545553
},
546554
],

crates/but-workspace/tests/workspace/ref_info/with_workspace_commit/legacy.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,13 @@ mod stacks {
3838
StackHeadInfo {
3939
name: "C-on-A",
4040
tip: Sha1(5f37dbfd4b1c3d2ee75f216665ab4edf44c843cb),
41+
review_id: None,
4142
is_checked_out: false,
4243
},
4344
StackHeadInfo {
4445
name: "A",
4546
tip: Sha1(d79bba960b112dbd25d45921c47eeda22288022b),
47+
review_id: None,
4648
is_checked_out: false,
4749
},
4850
],
@@ -58,11 +60,13 @@ mod stacks {
5860
StackHeadInfo {
5961
name: "B-on-A",
6062
tip: Sha1(4e5484ac0f1da1909414b1e16bd740c1a3599509),
63+
review_id: None,
6164
is_checked_out: false,
6265
},
6366
StackHeadInfo {
6467
name: "A",
6568
tip: Sha1(d79bba960b112dbd25d45921c47eeda22288022b),
69+
review_id: None,
6670
is_checked_out: false,
6771
},
6872
],
@@ -85,11 +89,13 @@ mod stacks {
8589
StackHeadInfo {
8690
name: "C-on-A",
8791
tip: Sha1(5f37dbfd4b1c3d2ee75f216665ab4edf44c843cb),
92+
review_id: None,
8893
is_checked_out: false,
8994
},
9095
StackHeadInfo {
9196
name: "A",
9297
tip: Sha1(d79bba960b112dbd25d45921c47eeda22288022b),
98+
review_id: None,
9399
is_checked_out: false,
94100
},
95101
],
@@ -105,11 +111,13 @@ mod stacks {
105111
StackHeadInfo {
106112
name: "B-on-A",
107113
tip: Sha1(4e5484ac0f1da1909414b1e16bd740c1a3599509),
114+
review_id: None,
108115
is_checked_out: false,
109116
},
110117
StackHeadInfo {
111118
name: "A",
112119
tip: Sha1(d79bba960b112dbd25d45921c47eeda22288022b),
120+
review_id: None,
113121
is_checked_out: false,
114122
},
115123
],
@@ -137,6 +145,7 @@ mod stacks {
137145
StackHeadInfo {
138146
name: "A",
139147
tip: Sha1(d79bba960b112dbd25d45921c47eeda22288022b),
148+
review_id: None,
140149
is_checked_out: true,
141150
},
142151
],
@@ -229,6 +238,7 @@ mod stacks {
229238
StackHeadInfo {
230239
name: "main",
231240
tip: Sha1(c166d42d4ef2e5e742d33554d03805cfb0b24d11),
241+
review_id: None,
232242
is_checked_out: false,
233243
},
234244
],

crates/but/src/command/legacy/forge/review.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ pub async fn create_pr(
8686
Some(get_branch_names(&ctx.legacy_project, &branch_id)?)
8787
} else {
8888
// Find branches without PRs
89-
let branches_without_prs = get_branches_without_prs(&review_map, &applied_stacks);
89+
let branches_without_prs = get_branches_without_prs(&review_map, &applied_stacks)?;
9090

9191
if branches_without_prs.is_empty() {
9292
if let Some(out) = out.for_human() {
@@ -169,19 +169,25 @@ async fn ensure_forge_authentication(ctx: &mut Context) -> Result<(), anyhow::Er
169169
fn get_branches_without_prs(
170170
review_map: &std::collections::HashMap<String, Vec<but_forge::ForgeReview>>,
171171
applied_stacks: &[but_workspace::legacy::ui::StackEntry],
172-
) -> Vec<String> {
172+
) -> anyhow::Result<Vec<String>> {
173173
let mut branches_without_prs = Vec::new();
174174
for stack_entry in applied_stacks {
175175
for head in &stack_entry.heads {
176-
let branch_name = head.name.to_string();
177-
if !review_map.contains_key(&branch_name)
178-
|| review_map.get(&branch_name).map(|v| v.is_empty()).unwrap_or(true)
176+
let branch_name = &head.name.to_string();
177+
if !review_map.contains_key(branch_name)
178+
|| review_map.get(branch_name).map(|v| v.is_empty()).unwrap_or(true)
179179
{
180-
branches_without_prs.push(branch_name);
180+
// This means that there are no associated reviews that are open, but that doesn't mean that there are
181+
// no associated reviews.
182+
// Check whether there's an associated forge review.
183+
if head.review_id.is_none() {
184+
// If there's no associated review, the append the branch
185+
branches_without_prs.push(branch_name.to_owned());
186+
}
181187
}
182188
}
183189
}
184-
branches_without_prs
190+
Ok(branches_without_prs)
185191
}
186192

187193
fn get_branch_names(project: &Project, branch_id: &str) -> anyhow::Result<Vec<String>> {
@@ -776,7 +782,6 @@ pub fn get_review_map(
776782
cache_config: Option<but_forge::CacheConfig>,
777783
) -> anyhow::Result<std::collections::HashMap<String, Vec<but_forge::ForgeReview>>> {
778784
let reviews = but_api::legacy::forge::list_reviews(ctx, cache_config).unwrap_or_default();
779-
780785
let branch_review_map = reviews
781786
.into_iter()
782787
.fold(std::collections::HashMap::new(), |mut acc, r| {

packages/core/src/generated/workspace/legacy/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ export type StackHeadInfo = {
4141
* The tip of the branch.
4242
*/
4343
tip: string;
44+
/**
45+
* The associated forge review with this branch, e.g. GitHub PRs or GitLab MRs
46+
*/
47+
reviewId: number | null;
4448
/**
4549
* If `true`, then this head is checked directly so `HEAD` points to it, and this is only ever `true` for a single head.
4650
* This is `false` if the worktree is checked out.

0 commit comments

Comments
 (0)