Skip to content

Commit 88b36d4

Browse files
committed
Make new safe_checkout available behind the v3 feature toggle.
This also makes the `v3` toggle re-usable, and it can mean many things as we make progress, changing scope over time.
1 parent 37ee911 commit 88b36d4

File tree

9 files changed

+54
-17
lines changed

9 files changed

+54
-17
lines changed

apps/desktop/src/lib/config/appSettingsV2.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ export type TelemetrySettings = {
158158
};
159159

160160
export type FeatureFlags = {
161+
/** Enable everything next-gen */
162+
v3: boolean;
161163
/** Enable the usage of the V3 workspace API */
162164
ws3: boolean;
163165
/** Enable the usage of GitButler Acitions. */

apps/desktop/src/routes/+layout.svelte

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@
124124
'w s 3': () => {
125125
settingsService.updateFeatureFlags({ ws3: !$settingsStore?.featureFlags.ws3 });
126126
},
127+
// Toggle next-gen (v3)
128+
'v 3': () => {
129+
settingsService.updateFeatureFlags({ v3: !$settingsStore?.featureFlags.v3 });
130+
},
127131
// Show commit graph visualization
128132
'd o t': async () => {
129133
const projectId = page.params.projectId;

crates/but-settings/assets/defaults.jsonc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"oauthClientId": "cd51880daa675d9e6452"
1919
},
2020
"featureFlags": {
21-
// Enables the v3 design, as well as the purgatory mode (no uncommitted diff ownership assignments).
21+
// Enables the v3 safe checkout, apply and unapply.
2222
"v3": false,
2323
/// Enable the usage of V3 workspace APIs.
2424
"ws3": false,

crates/but-settings/src/api.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub struct TelemetryUpdate {
1515
#[serde(rename_all = "camelCase")]
1616
/// Update request for [`crate::app_settings::FeatureFlags`].
1717
pub struct FeatureFlagsUpdate {
18+
pub v3: Option<bool>,
1819
pub ws3: Option<bool>,
1920
pub actions: Option<bool>,
2021
pub butbot: Option<bool>,
@@ -65,6 +66,7 @@ impl AppSettingsWithDiskSync {
6566
pub fn update_feature_flags(
6667
&self,
6768
FeatureFlagsUpdate {
69+
v3,
6870
ws3,
6971
actions,
7072
butbot,
@@ -73,6 +75,9 @@ impl AppSettingsWithDiskSync {
7375
}: FeatureFlagsUpdate,
7476
) -> Result<()> {
7577
let mut settings = self.get_mut_enforce_save()?;
78+
if let Some(v3) = v3 {
79+
settings.feature_flags.v3 = v3;
80+
}
7681
if let Some(ws3) = ws3 {
7782
settings.feature_flags.ws3 = ws3;
7883
}

crates/but-settings/src/app_settings.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ pub struct FeatureFlags {
2626
/// Enable the usage of V3 workspace APIs.
2727
#[serde(default = "default_true")]
2828
pub ws3: bool,
29+
/// Turn on the set of v3 functions currently being roled out:
30+
/// - safe checkout
31+
/// - apply
32+
/// - unapply
33+
pub v3: bool,
2934
/// Enable undo/redo support.
3035
///
3136
/// ### Progression for implementation

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ pub mod stacks {
334334
let app_settings = AppSettings {
335335
feature_flags: but_settings::app_settings::FeatureFlags {
336336
ws3,
337+
v3: false,
337338
undo: false,
338339
actions: false,
339340
butbot: false,

crates/but-workspace/src/branch/checkout/function.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use gix::objs::TreeRefIter;
77
use gix::prelude::ObjectIdExt as _;
88
use gix::refs::Target;
99
use gix::refs::transaction::{Change, LogChange, PreviousValue, RefEdit, RefLog};
10+
use tracing::instrument;
1011

1112
/// Given the `current_head_id^{tree}` for the tree that matches what `HEAD` points to, perform all file operations necessary
1213
/// to turn the *worktree* of `repo` into `new_head_id^{tree}`. Note that the current *worktree* is assumed to be at the state of
@@ -15,7 +16,7 @@ use gix::refs::transaction::{Change, LogChange, PreviousValue, RefEdit, RefLog};
1516
/// Note that we don't care if the worktree actually matches the `new_head_id^{tree}`, we only care about the operations from
1617
/// `current_head_id^{tree}` to be performed, and if there are none, we will do nothing.
1718
///
18-
/// If `new_head_id` is a commit, we will also set `HEAD` (or the ref it points to if symbolic) to the `new_head_id`.
19+
/// If `new_head_id` is a *commit*, we will also set `HEAD` (or the ref it points to if symbolic) to the `new_head_id`.
1920
/// We will also update the `.git/index` to match the `new_head_id^{tree}`.
2021
/// Note that the value for [`UncommitedWorktreeChanges`] is critical to determine what happens if a change would be overwritten.
2122
///
@@ -27,6 +28,7 @@ use gix::refs::transaction::{Change, LogChange, PreviousValue, RefEdit, RefLog};
2728
/// To keep it simpler, we don't do rename tracking, so deletions and additions are always treated separately.
2829
/// If this changes, then the source sid of a rename could also cause conflicts, maybe? It's a bit unclear what it would mean
2930
/// in practice, but I guess that we bring deleted files back instead of conflicting.
31+
#[instrument(skip(repo), err(Debug))]
3032
pub fn safe_checkout(
3133
current_head_id: gix::ObjectId,
3234
new_head_id: gix::ObjectId,

crates/but-workspace/src/branch/checkout/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub enum UncommitedWorktreeChanges {
88
/// Place the files that would be altered, AND at least one conflicts when brought back, into a snapshot based
99
/// on the current `HEAD`, and overwrite them.
1010
/// Note that uncommitted changes that aren't affected will just be left as is.
11+
// TODO: Add a ref-name with which to associate the snapshot commit for safekeeping, but needs UI support.
1112
KeepConflictingInSnapshotAndOverwrite,
1213
}
1314

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

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ use crate::{
77
VirtualBranchesExt,
88
};
99
use anyhow::{anyhow, Context, Result};
10+
use but_workspace::branch::checkout::UncommitedWorktreeChanges;
1011
use gitbutler_branch::GITBUTLER_WORKSPACE_REFERENCE;
1112
use gitbutler_command_context::CommandContext;
1213
use gitbutler_error::error::Marker;
13-
use gitbutler_oxidize::ObjectIdExt;
14+
use gitbutler_oxidize::{ObjectIdExt, OidExt};
1415
use gitbutler_project::FetchResult;
1516
use gitbutler_reference::{Refname, RemoteRefname};
1617
use gitbutler_repo::{
@@ -53,24 +54,40 @@ pub fn get_base_branch_data(ctx: &CommandContext) -> Result<BaseBranch> {
5354
Ok(base)
5455
}
5556

57+
#[instrument(skip(ctx), err(Debug))]
5658
fn go_back_to_integration(ctx: &CommandContext, default_target: &Target) -> Result<BaseBranch> {
5759
let gix_repo = ctx.gix_repo_for_merging()?;
58-
let (mut outcome, conflict_kind) =
59-
but_workspace::merge_worktree_with_workspace(ctx, &gix_repo)?;
60-
61-
if outcome.has_unresolved_conflicts(conflict_kind) {
62-
return Err(anyhow!("Conflicts while going back to gitbutler/workspace"))
63-
.context(Marker::ProjectConflict);
64-
}
60+
if ctx.app_settings().feature_flags.v3 {
61+
let workspace_commit_to_checkout = but_workspace::head(ctx)?;
62+
let tree_to_checkout_to_avoid_ref_update = gix_repo
63+
.find_commit(workspace_commit_to_checkout.to_gix())?
64+
.tree_id()?;
65+
but_workspace::branch::safe_checkout(
66+
gix_repo.head_id()?.detach(),
67+
tree_to_checkout_to_avoid_ref_update.detach(),
68+
&gix_repo,
69+
but_workspace::branch::checkout::Options {
70+
uncommitted_changes: UncommitedWorktreeChanges::KeepAndAbortOnConflict,
71+
},
72+
)?;
73+
} else {
74+
let (mut outcome, conflict_kind) =
75+
but_workspace::merge_worktree_with_workspace(ctx, &gix_repo)?;
76+
77+
if outcome.has_unresolved_conflicts(conflict_kind) {
78+
return Err(anyhow!("Conflicts while going back to gitbutler/workspace"))
79+
.context(Marker::ProjectConflict);
80+
}
6581

66-
let final_tree_id = outcome.tree.write()?.detach();
82+
let final_tree_id = outcome.tree.write()?.detach();
6783

68-
let repo = ctx.repo();
69-
let final_tree = repo.find_tree(final_tree_id.to_git2())?;
70-
repo.checkout_tree_builder(&final_tree)
71-
.force()
72-
.checkout()
73-
.context("failed to checkout tree")?;
84+
let repo = ctx.repo();
85+
let final_tree = repo.find_tree(final_tree_id.to_git2())?;
86+
repo.checkout_tree_builder(&final_tree)
87+
.force()
88+
.checkout()
89+
.context("failed to checkout tree")?;
90+
}
7491

7592
let base = target_to_base_branch(ctx, default_target)?;
7693
let vb_state = VirtualBranchesHandle::new(ctx.project().gb_dir());

0 commit comments

Comments
 (0)