Skip to content

Commit 4cde1f1

Browse files
committed
Add new feature toggle for apply/unapply
1 parent c052677 commit 4cde1f1

File tree

7 files changed

+73
-51
lines changed

7 files changed

+73
-51
lines changed

crates/but-graph/src/init/overlay.rs

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use but_core::{RefMetadata, ref_metadata};
44
use gix::prelude::ReferenceExt;
55
use gix::refs::Target;
66
use std::borrow::Cow;
7-
use std::collections::BTreeSet;
7+
use std::collections::{BTreeMap, BTreeSet};
88

99
impl Overlay {
1010
/// Serve the given `refs` from memory, as if they would exist.
@@ -73,19 +73,22 @@ impl Overlay {
7373
T: RefMetadata,
7474
{
7575
let Overlay {
76-
mut nonoverriding_references,
77-
mut overriding_references,
76+
nonoverriding_references,
77+
overriding_references,
7878
meta_branches,
7979
workspace,
8080
entrypoint,
8181
} = self;
82-
// Make sure that duplicates from later determine the value.
83-
nonoverriding_references.reverse();
84-
overriding_references.reverse();
8582
(
8683
OverlayRepo {
87-
nonoverriding_references: nonoverriding_references.into_iter().collect(),
88-
overriding_references: overriding_references.into_iter().collect(),
84+
nonoverriding_references: nonoverriding_references
85+
.into_iter()
86+
.map(|r| (r.name.clone(), r))
87+
.collect(),
88+
overriding_references: overriding_references
89+
.into_iter()
90+
.map(|r| (r.name.clone(), r))
91+
.collect(),
8992
inner: repo,
9093
},
9194
OverlayMetadata {
@@ -100,8 +103,8 @@ impl Overlay {
100103

101104
pub(crate) struct OverlayRepo<'repo> {
102105
inner: &'repo gix::Repository,
103-
nonoverriding_references: BTreeSet<gix::refs::Reference>,
104-
overriding_references: BTreeSet<gix::refs::Reference>,
106+
nonoverriding_references: BTreeMap<gix::refs::FullName, gix::refs::Reference>,
107+
overriding_references: BTreeMap<gix::refs::FullName, gix::refs::Reference>,
105108
}
106109

107110
/// Note that functions with `'repo` in their return value technically leak the bare repo, and it's
@@ -115,19 +118,11 @@ impl<'repo> OverlayRepo<'repo> {
115118
&self,
116119
ref_name: &gix::refs::FullNameRef,
117120
) -> anyhow::Result<Option<gix::Reference<'repo>>> {
118-
if let Some(r) = self
119-
.overriding_references
120-
.iter()
121-
.find(|r| r.name.as_ref() == ref_name)
122-
{
121+
if let Some(r) = self.overriding_references.get(ref_name) {
123122
Ok(Some(r.clone().attach(self.inner)))
124123
} else if let Some(rn) = self.inner.try_find_reference(ref_name)? {
125124
Ok(Some(rn))
126-
} else if let Some(r) = self
127-
.nonoverriding_references
128-
.iter()
129-
.find(|r| r.name.as_ref() == ref_name)
130-
{
125+
} else if let Some(r) = self.nonoverriding_references.get(ref_name) {
131126
Ok(Some(r.clone().attach(self.inner)))
132127
} else {
133128
Ok(None)
@@ -138,11 +133,7 @@ impl<'repo> OverlayRepo<'repo> {
138133
&self,
139134
ref_name: &gix::refs::FullNameRef,
140135
) -> anyhow::Result<gix::Reference<'repo>> {
141-
if let Some(r) = self
142-
.overriding_references
143-
.iter()
144-
.find(|r| r.name.as_ref() == ref_name)
145-
{
136+
if let Some(r) = self.overriding_references.get(ref_name) {
146137
return Ok(r.clone().attach(self.inner));
147138
}
148139
Ok(self
@@ -151,11 +142,7 @@ impl<'repo> OverlayRepo<'repo> {
151142
.or_else(|err| match err {
152143
gix::reference::find::existing::Error::Find(_) => Err(err),
153144
gix::reference::find::existing::Error::NotFound { .. } => {
154-
if let Some(r) = self
155-
.nonoverriding_references
156-
.iter()
157-
.find(|r| r.name.as_ref() == ref_name)
158-
{
145+
if let Some(r) = self.nonoverriding_references.get(ref_name) {
159146
Ok(r.clone().attach(self.inner))
160147
} else {
161148
Err(err)
@@ -239,7 +226,7 @@ impl<'repo> OverlayRepo<'repo> {
239226
// apply overrides - they are seen first and take the spot of everything.
240227
for (commit_id, git_reference) in self
241228
.overriding_references
242-
.iter()
229+
.values()
243230
.filter(|rn| rn.name.as_bstr().starts_with(prefix.as_bytes()))
244231
.filter_map(|rn| ref_filter(rn.clone().attach(self.inner)))
245232
{
@@ -263,7 +250,7 @@ impl<'repo> OverlayRepo<'repo> {
263250
// apply overrides (new only)
264251
for (commit_id, git_reference) in self
265252
.nonoverriding_references
266-
.iter()
253+
.values()
267254
.filter(|rn| rn.name.as_bstr().starts_with(prefix.as_bytes()))
268255
.filter_map(|rn| ref_filter(rn.clone().attach(self.inner)))
269256
{

crates/but-settings/assets/defaults.jsonc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
"oauthClientId": "cd51880daa675d9e6452"
1919
},
2020
"featureFlags": {
21+
/// Use the V3 version of apply and unapply.
22+
"apply3": false,
2123
// Enables the v3 safe checkout.
2224
"cv3": false,
2325
/// Enable the usage of V3 workspace APIs.

crates/but-settings/src/app_settings.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ pub struct FeatureFlags {
2727
pub ws3: bool,
2828
/// Turn on the set a v3 version of checkout
2929
pub cv3: bool,
30+
/// Use the V3 version of apply and unapply.
31+
pub apply3: bool,
3032
/// Enable undo/redo support.
3133
///
3234
/// ### Progression for implementation

crates/but-testing/src/command/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ pub mod stacks {
372372
let app_settings = AppSettings {
373373
feature_flags: but_settings::app_settings::FeatureFlags {
374374
ws3,
375+
apply3: false,
375376
cv3: false,
376377
undo: false,
377378
actions: false,

crates/but-workspace/src/branch/apply.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,15 @@ pub(crate) mod function {
7979
use crate::ext::ObjectStorageExt;
8080
use crate::ref_info::WorkspaceExt;
8181
use anyhow::{Context, bail};
82+
use but_core::RefMetadata;
8283
use but_core::ref_metadata::Workspace;
83-
use but_core::{RefMetadata, ref_metadata};
8484
use but_graph::init::Overlay;
8585
use but_graph::projection::WorkspaceKind;
8686
use gitbutler_oxidize::GixRepositoryExt;
8787
use gix::refs::transaction::{Change, LogChange, PreviousValue, RefEdit, RefLog};
8888
use gix::refs::{FullNameRef, Target};
8989
use std::borrow::Cow;
90+
use tracing::instrument;
9091

9192
/// Apply `branch` to the given `workspace`, and possibly create the workspace reference in `repo`
9293
/// along with its `meta`-data if it doesn't exist yet.
@@ -107,6 +108,7 @@ pub(crate) mod function {
107108
///
108109
/// Note that options have no effect if `branch` is already in the workspace, so `apply` is *not* a way
109110
/// to alter certain aspects of the workspace by applying the same branch again.
111+
#[instrument(level = tracing::Level::DEBUG, skip(workspace, repo, meta), err(Debug))]
110112
pub fn apply<'graph>(
111113
branch: &gix::refs::FullNameRef,
112114
workspace: &but_graph::projection::Workspace<'graph>,
@@ -258,7 +260,7 @@ pub(crate) mod function {
258260

259261
let mut ws_md = meta.workspace(workspace_ref_name_to_update.as_ref())?;
260262
{
261-
let ws_mut: &mut ref_metadata::Workspace = &mut ws_md;
263+
let ws_mut: &mut Workspace = &mut ws_md;
262264
for rn in &branches_to_apply {
263265
ws_mut.add_or_insert_new_stack_if_not_present(rn, order);
264266
}
@@ -370,11 +372,6 @@ pub(crate) mod function {
370372
overlay.with_entrypoint(new_head_id, Some(workspace_ref_name_to_update.clone())),
371373
)?;
372374
persist_metadata(meta, &branches_to_apply, &ws_md)?;
373-
set_head_to_reference(
374-
repo,
375-
new_head_id,
376-
(!ws_ref_exists).then_some(workspace_ref_name_to_update.as_ref()),
377-
)?;
378375
crate::branch::safe_checkout(
379376
prev_head_id,
380377
new_head_id,
@@ -384,6 +381,11 @@ pub(crate) mod function {
384381
},
385382
)?;
386383

384+
set_head_to_reference(
385+
repo,
386+
new_head_id,
387+
(!ws_ref_exists).then_some(workspace_ref_name_to_update.as_ref()),
388+
)?;
387389
Ok(Outcome {
388390
graph: Cow::Owned(graph),
389391
workspace_ref_created: needs_ws_ref_creation,

crates/but-workspace/src/commit.rs

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -77,23 +77,15 @@ pub mod merge {
7777
.filter_map(|s| s.branches.first())
7878
.filter_map(|top_segment| {
7979
let stack_tip_name = top_segment.ref_name.as_ref();
80-
match graph.named_segment_by_ref_name(stack_tip_name) {
80+
match graph.segment_and_commit_by_ref_name(stack_tip_name) {
8181
None => {
8282
missing_stacks.push(top_segment.ref_name.to_owned());
8383
None
8484
}
85-
Some(segment) => graph
86-
.tip_skip_empty(segment.id)
87-
.map(|c| Ok((stack_tip_name, c.id, segment.id)))
88-
.or_else(|| {
89-
Some(Err(anyhow::anyhow!(
90-
"Base segment {id} didn't have a single commit reachable",
91-
id = segment.id.index()
92-
)))
93-
}),
85+
Some((segment, commit)) => Some((stack_tip_name, commit.id, segment.id)),
9486
}
9587
})
96-
.collect::<Result<_, _>>()?;
88+
.collect();
9789

9890
let conflicting_stacks = Vec::new();
9991
let mut prev_base_sidx = None;
@@ -139,7 +131,7 @@ pub mod merge {
139131
merge_options.clone(),
140132
)?;
141133
if merge.has_unresolved_conflicts(conflict_kind) {
142-
todo!("conflict handling - hero or not: {hero_stack:?}");
134+
bail!("TODO: conflict handling with hero-special: {hero_stack:?}");
143135
}
144136
merge_tree_id = merge.tree.write()?.detach().into();
145137
}

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ use super::BranchManager;
22
use crate::r#virtual as vbranch;
33
use crate::{hunk::VirtualBranchHunk, integration::update_workspace_commit, VirtualBranchesExt};
44
use anyhow::{anyhow, bail, Context, Result};
5+
use but_workspace::branch::apply::{IntegrationMode, WorkspaceReferenceNaming};
6+
use but_workspace::branch::checkout::UncommitedWorktreeChanges;
7+
use but_workspace::branch::OnWorkspaceMergeConflict;
58
use but_workspace::stack_ext::StackExt;
69
use gitbutler_branch::BranchCreateRequest;
710
use gitbutler_branch::{self, dedup};
@@ -139,6 +142,39 @@ impl BranchManager<'_> {
139142
pr_number: Option<usize>,
140143
perm: &mut WorktreeWritePermission,
141144
) -> Result<(StackId, Vec<StackId>)> {
145+
// Assume that this is always about 'apply' and hijack the entire method.
146+
// That way we'd learn what's missing.
147+
if self.ctx.app_settings().feature_flags.apply3 {
148+
let (repo, mut meta, graph) = self.ctx.graph_and_meta_mut_and_repo(perm)?;
149+
let ws = graph.to_workspace()?;
150+
let target = target.to_string();
151+
let branch_to_apply = target.as_str().try_into()?;
152+
let out = but_workspace::branch::apply(
153+
branch_to_apply,
154+
&ws,
155+
&repo,
156+
&mut *meta,
157+
but_workspace::branch::apply::Options {
158+
integration_mode: IntegrationMode::AlwaysMerge,
159+
on_workspace_conflict:
160+
OnWorkspaceMergeConflict::MaterializeAndReportConflictingStacks,
161+
workspace_reference_naming: WorkspaceReferenceNaming::Default,
162+
uncommitted_changes: UncommitedWorktreeChanges::KeepAndAbortOnConflict,
163+
order: None,
164+
},
165+
)?;
166+
let ws = out.graph.to_workspace()?;
167+
let unapplied_stacks_to_be_done = Vec::new();
168+
let applied_branch_stack_id = ws
169+
.find_segment_and_stack_by_refname(branch_to_apply)
170+
.with_context(||
171+
format!("Failed to apply branch and we probably have to handle more of the outcome\n{out:?}")
172+
)?
173+
.0
174+
.id
175+
.context("BUG: newly applied stacks should always have a stack id")?;
176+
return Ok((applied_branch_stack_id, unapplied_stacks_to_be_done));
177+
}
142178
let old_cwd = (!self.ctx.app_settings().feature_flags.cv3)
143179
.then(|| self.ctx.repo().create_wd_tree(0).map(|tree| tree.id()))
144180
.transpose()?;

0 commit comments

Comments
 (0)