Skip to content

Commit c72c17a

Browse files
committed
Add a feature toggle with the actual implementation for the new workspace.
1 parent e977dc4 commit c72c17a

File tree

3 files changed

+123
-24
lines changed

3 files changed

+123
-24
lines changed

crates/gitbutler-branch-actions/src/branch.rs

Lines changed: 89 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,36 @@ pub fn list_branches(
8888
}
8989

9090
let vb_handle = ctx.project().virtual_branches();
91-
let stacks = vb_handle.list_all_stacks()?;
9291
let remote_names = repo.remote_names();
93-
branches.extend(
94-
stacks
92+
let stacks = if ctx.app_settings().feature_flags.ws3 {
93+
if let Some(workspace_ref) = repo.try_find_reference("refs/heads/gitbutler/workspace")? {
94+
// Let's get this here for convenience, and hope this isn't ever called by a writer (or there will be a deadlock).
95+
let read_guard = ctx.project().shared_worktree_access();
96+
let meta = ctx.meta(read_guard.read_permission())?;
97+
let info = but_workspace::ref_info(
98+
workspace_ref,
99+
&*meta,
100+
but_workspace::ref_info::Options {
101+
traversal: but_graph::init::Options::limited(),
102+
expensive_commit_info: false,
103+
},
104+
)?;
105+
info.stacks
106+
.into_iter()
107+
.map(|s| GitButlerStack::new(s, &remote_names))
108+
.collect::<Result<Vec<_>, _>>()?
109+
} else {
110+
Vec::new()
111+
}
112+
} else {
113+
vb_handle
114+
.list_all_stacks()?
95115
.iter()
96-
.map(|s| GitButlerStack::new(s, &remote_names).map(GroupBranch::Virtual))
97-
.collect::<Result<Vec<_>, _>>()?,
98-
);
116+
.map(|s| GitButlerStack::new_from_old(s, &remote_names))
117+
.collect::<Result<Vec<_>, _>>()?
118+
};
119+
120+
branches.extend(stacks.iter().map(|s| GroupBranch::Virtual(s.clone())));
99121
let mut branches = combine_branches(branches, &repo, vb_handle.get_default_target()?)?;
100122

101123
// Apply the filter
@@ -135,13 +157,13 @@ pub fn list_branches(
135157
// To do this, we build up a list of all the branch identities that are
136158
// part of a stack and then filter out any branches that have been grouped
137159
// without a stack and match one of these identities.
138-
let branch_identities_to_exclude = stacks
139-
.iter()
160+
let branch_identities_to_exclude: HashSet<BString> = stacks
161+
.into_iter()
140162
.flat_map(|s| {
141-
s.branches()
163+
s.unarchived_segments
142164
.into_iter()
143-
.map(|b| BString::from(b.name().to_owned()))
144-
.chain([BString::from(s.name.to_owned())])
165+
.map(|b| b.short_name().into())
166+
.chain(Some(s.name.into()))
145167
})
146168
.collect::<HashSet<_>>();
147169

@@ -337,7 +359,7 @@ enum GroupBranch<'a> {
337359
}
338360

339361
/// A type to just keep the parts we currently need.
340-
#[derive(Debug)]
362+
#[derive(Debug, Clone)]
341363
struct GitButlerStack {
342364
id: StackId,
343365
/// `true` if the stack is applied to the workspace.
@@ -355,7 +377,7 @@ struct GitButlerStack {
355377
unarchived_segments: Vec<GitbutlerStackSegment>,
356378
}
357379

358-
#[derive(Debug)]
380+
#[derive(Debug, Clone)]
359381
struct GitbutlerStackSegment {
360382
/// The name of the segment, without support for these to be anonymous (which is a problem).
361383
tip: gix::refs::FullName,
@@ -370,16 +392,62 @@ impl GitbutlerStackSegment {
370392
}
371393

372394
impl GitButlerStack {
373-
fn new(s: &Stack, names: &gix::remote::Names) -> anyhow::Result<Self> {
395+
fn new(
396+
stack: but_workspace::branch::Stack,
397+
names: &gix::remote::Names,
398+
) -> anyhow::Result<Self> {
399+
let first_segment = stack.segments.first();
374400
Ok(GitButlerStack {
375-
id: s.id,
376-
in_workspace: s.in_workspace,
377-
name: s.name.clone(),
378-
source_refname: s
401+
id: stack.id.context("Can't handle stacks without ID yet")?,
402+
// The ones we have reachable are never
403+
in_workspace: true,
404+
name: stack
405+
.name()
406+
.map(|rn| rn.shorten().to_string())
407+
// Hack it - the datastructure isn't suitable and this needs a `gitbutler->but` port.
408+
.unwrap_or_default(),
409+
source_refname: stack.ref_name().map(|rn| rn.to_owned()),
410+
upstream: first_segment
411+
.and_then(|s| {
412+
s.remote_tracking_ref_name.as_ref().map(|rn| {
413+
but_workspace::ui::ref_info::RemoteTrackingReference::for_ui(
414+
rn.clone(),
415+
names,
416+
)
417+
})
418+
})
419+
.transpose()?,
420+
updated_timestamp_ms: first_segment
421+
.and_then(|s| {
422+
let md = s.metadata.as_ref()?;
423+
Some(md.ref_info.updated_at?.seconds as u128 * 1_000)
424+
})
425+
.unwrap_or_default(),
426+
unarchived_segments: stack
427+
.segments
428+
.iter()
429+
.map(|s| GitbutlerStackSegment {
430+
tip: s.ref_name.clone().unwrap_or_else(|| {
431+
gix::refs::FullName::try_from(
432+
"refs/heads/unnamed-ref-and-we-fake-a-name-fix-me",
433+
)
434+
.expect("known to be valid statically")
435+
}),
436+
pr_or_mr: s.metadata.as_ref().and_then(|md| md.review.pull_request),
437+
})
438+
.collect(),
439+
})
440+
}
441+
fn new_from_old(stack: &Stack, names: &gix::remote::Names) -> anyhow::Result<Self> {
442+
Ok(GitButlerStack {
443+
id: stack.id,
444+
in_workspace: stack.in_workspace,
445+
name: stack.name.clone(),
446+
source_refname: stack
379447
.source_refname
380448
.as_ref()
381449
.and_then(|r| r.to_string().try_into().ok()),
382-
upstream: s
450+
upstream: stack
383451
.upstream
384452
.as_ref()
385453
.and_then(|r| {
@@ -388,8 +456,8 @@ impl GitButlerStack {
388456
})
389457
})
390458
.transpose()?,
391-
updated_timestamp_ms: s.updated_timestamp_ms,
392-
unarchived_segments: s
459+
updated_timestamp_ms: stack.updated_timestamp_ms,
460+
unarchived_segments: stack
393461
.branches()
394462
.iter()
395463
// The tip is at the bottom here.

crates/gitbutler-branch-actions/tests/virtual_branches/list.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ fn one_vbranch_in_workspace_one_commit() -> Result<()> {
4545
#[test]
4646
fn two_vbranches_in_workspace_one_commit() -> Result<()> {
4747
init_env();
48-
let ctx = project_ctx("two-vbranches-in-workspace-one-applied")?;
48+
let ctx = project_ctx_without_ws3("two-vbranches-in-workspace-one-applied")?;
4949
let list = list_branches(
5050
&ctx,
5151
Some(BranchListingFilter {
@@ -92,7 +92,7 @@ fn two_vbranches_in_workspace_one_commit() -> Result<()> {
9292
#[test]
9393
fn one_feature_branch_and_one_vbranch_in_workspace_one_commit() -> Result<()> {
9494
init_env();
95-
let ctx = project_ctx("a-vbranch-named-like-target-branch-short-name")?;
95+
let ctx = project_ctx_without_ws3("a-vbranch-named-like-target-branch-short-name")?;
9696
let list = list_branches(&ctx, None)?;
9797
assert_eq!(
9898
list.len(),
@@ -118,7 +118,7 @@ fn one_feature_branch_and_one_vbranch_in_workspace_one_commit() -> Result<()> {
118118
#[test]
119119
fn one_branch_in_workspace_multiple_remotes() -> Result<()> {
120120
init_env();
121-
let ctx = project_ctx("one-vbranch-in-workspace-two-remotes")?;
121+
let ctx = project_ctx_without_ws3("one-vbranch-in-workspace-two-remotes")?;
122122
let list = list_branches(&ctx, None)?;
123123
assert_eq!(list.len(), 1, "a single virtual branch");
124124

@@ -138,6 +138,8 @@ fn one_branch_in_workspace_multiple_remotes() -> Result<()> {
138138

139139
mod util {
140140
use anyhow::Result;
141+
use but_settings::app_settings::FeatureFlags;
142+
use but_settings::AppSettings;
141143
use gitbutler_branch::BranchIdentity;
142144
use gitbutler_branch_actions::{BranchListing, BranchListingFilter};
143145
use gitbutler_command_context::CommandContext;
@@ -221,6 +223,17 @@ mod util {
221223
gitbutler_testsupport::read_only::fixture("for-listing.sh", name)
222224
}
223225

226+
pub fn project_ctx_without_ws3(name: &str) -> Result<CommandContext> {
227+
gitbutler_testsupport::read_only::fixture_with_features(
228+
"for-listing.sh",
229+
name,
230+
FeatureFlags {
231+
ws3: false,
232+
..AppSettings::default().feature_flags
233+
},
234+
)
235+
}
236+
224237
pub fn list_branches(
225238
ctx: &CommandContext,
226239
filter: Option<BranchListingFilter>,
@@ -230,4 +243,5 @@ mod util {
230243
Ok(branches)
231244
}
232245
}
246+
use crate::virtual_branches::list::util::project_ctx_without_ws3;
233247
pub use util::{assert_equal, init_env, list_branches, project_ctx, ExpectedBranchListing};

crates/gitbutler-testsupport/src/lib.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ pub fn stack_details(ctx: &CommandContext) -> Vec<(StackId, StackDetails)> {
209209

210210
pub mod read_only {
211211
use crate::DRIVER;
212+
use but_settings::app_settings::FeatureFlags;
212213
use but_settings::AppSettings;
213214
use gitbutler_command_context::CommandContext;
214215
use gitbutler_project::{Project, ProjectId};
@@ -228,6 +229,22 @@ pub mod read_only {
228229
CommandContext::open(&project, AppSettings::default())
229230
}
230231

232+
/// As [fixture()], but allows setting `features` in the app settings
233+
pub fn fixture_with_features(
234+
script_name: &str,
235+
project_directory: &str,
236+
features: FeatureFlags,
237+
) -> anyhow::Result<CommandContext> {
238+
let project = fixture_project(script_name, project_directory)?;
239+
CommandContext::open(
240+
&project,
241+
AppSettings {
242+
feature_flags: features,
243+
..Default::default()
244+
},
245+
)
246+
}
247+
231248
/// Like [`fixture()`], but will return only the `Project` at `project_directory` after executing `script_name`.
232249
pub fn fixture_project(script_name: &str, project_directory: &str) -> anyhow::Result<Project> {
233250
static IS_VALID_PROJECT: Lazy<Mutex<BTreeSet<(String, String)>>> =

0 commit comments

Comments
 (0)