Skip to content

Commit f02b626

Browse files
committed
Handle commiting to workspace correctly
Stuff and things
1 parent f6ea6b0 commit f02b626

File tree

9 files changed

+287
-112
lines changed

9 files changed

+287
-112
lines changed

Cargo.lock

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/but-workspace/Cargo.toml

+10-1
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,22 @@ git2.workspace = true
1515
but-core.workspace = true
1616
but-rebase.workspace = true
1717
gitbutler-id.workspace = true
18-
gix = { workspace = true, features = ["worktree-mutation"] }
18+
gix = { workspace = true, features = [
19+
"dirwalk",
20+
"credentials",
21+
"parallel",
22+
"serde",
23+
"status",
24+
"revision",
25+
] }
1926
gitbutler-stack.workspace = true
2027
gitbutler-command-context.workspace = true
2128
gitbutler-oxidize.workspace = true
2229
gitbutler-project.workspace = true
2330
gitbutler-commit.workspace = true
2431
gitbutler-repo.workspace = true
32+
gitbutler-operating-modes.workspace = true
33+
gitbutler-diff.workspace = true
2534
serde = { workspace = true, features = ["std"] }
2635
gitbutler-serde.workspace = true
2736
itertools = "0.14"

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

+4-109
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,21 @@
11
use std::{path::PathBuf, vec};
22

33
use anyhow::{anyhow, Context, Result};
4-
use bstr::ByteSlice;
5-
use gitbutler_branch::BranchCreateRequest;
64
use gitbutler_branch::{self, GITBUTLER_WORKSPACE_REFERENCE};
75
use gitbutler_cherry_pick::RepositoryExt as _;
86
use gitbutler_command_context::CommandContext;
9-
use gitbutler_commit::commit_ext::CommitExt;
107
use gitbutler_error::error::Marker;
118
use gitbutler_operating_modes::OPEN_WORKSPACE_REFS;
12-
use gitbutler_oxidize::{git2_to_gix_object_id, gix_to_git2_oid, GixRepositoryExt, RepoExt};
9+
use gitbutler_oxidize::{git2_to_gix_object_id, gix_to_git2_oid, GixRepositoryExt, RepoExt as _};
1310
use gitbutler_project::access::WorktreeWritePermission;
14-
use gitbutler_repo::logging::{LogUntil, RepositoryExt as _};
15-
use gitbutler_repo::RepositoryExt;
1611
use gitbutler_repo::SignaturePurpose;
1712
use gitbutler_stack::{Stack, VirtualBranchesHandle};
1813
use tracing::instrument;
1914

20-
use crate::{branch_manager::BranchManagerExt, VirtualBranchesExt};
15+
use crate::{workspace_commit::resolve_commits_above, VirtualBranchesExt};
2116

2217
const WORKSPACE_HEAD: &str = "Workspace Head";
23-
const GITBUTLER_INTEGRATION_COMMIT_TITLE: &str = "GitButler Integration Commit";
18+
pub const GITBUTLER_INTEGRATION_COMMIT_TITLE: &str = "GitButler Integration Commit";
2419
pub const GITBUTLER_WORKSPACE_COMMIT_TITLE: &str = "GitButler Workspace Commit";
2520

2621
/// Creates and returns a merge commit of all active branch heads.
@@ -287,7 +282,7 @@ pub fn update_workspace_commit(
287282
pub fn verify_branch(ctx: &CommandContext, perm: &mut WorktreeWritePermission) -> Result<()> {
288283
verify_current_branch_name(ctx)
289284
.and_then(verify_head_is_set)
290-
.and_then(|()| verify_head_is_clean(ctx, perm))
285+
.and_then(|()| resolve_commits_above(ctx, perm))
291286
.context(Marker::VerificationFailure)?;
292287
Ok(())
293288
}
@@ -317,106 +312,6 @@ fn verify_current_branch_name(ctx: &CommandContext) -> Result<&CommandContext> {
317312
}
318313
}
319314

320-
// TODO(ST): Probably there should not be an implicit vbranch creation here.
321-
fn verify_head_is_clean(ctx: &CommandContext, perm: &mut WorktreeWritePermission) -> Result<()> {
322-
let head_commit = ctx
323-
.repo()
324-
.head()
325-
.context("failed to get head")?
326-
.peel_to_commit()
327-
.context("failed to peel to commit")?;
328-
329-
let vb_handle = VirtualBranchesHandle::new(ctx.project().gb_dir());
330-
let default_target = vb_handle
331-
.get_default_target()
332-
.context("failed to get default target")?;
333-
334-
let commits = ctx
335-
.repo()
336-
.log(
337-
head_commit.id(),
338-
LogUntil::Commit(default_target.sha),
339-
false,
340-
)
341-
.context("failed to get log")?;
342-
343-
let workspace_index = commits
344-
.iter()
345-
.position(|commit| {
346-
commit.message().is_some_and(|message| {
347-
message.starts_with(GITBUTLER_WORKSPACE_COMMIT_TITLE)
348-
|| message.starts_with(GITBUTLER_INTEGRATION_COMMIT_TITLE)
349-
})
350-
})
351-
.context("GitButler workspace commit not found")?;
352-
let workspace_commit = &commits[workspace_index];
353-
let mut extra_commits = commits[..workspace_index].to_vec();
354-
extra_commits.reverse();
355-
356-
if extra_commits.is_empty() {
357-
// no extra commits found, so we're good
358-
return Ok(());
359-
}
360-
361-
ctx.repo()
362-
.reset(workspace_commit.as_object(), git2::ResetType::Soft, None)
363-
.context("failed to reset to workspace commit")?;
364-
365-
let branch_manager = ctx.branch_manager();
366-
let mut new_branch = branch_manager
367-
.create_virtual_branch(
368-
&BranchCreateRequest {
369-
name: extra_commits
370-
.last()
371-
.map(|commit| commit.message_bstr().to_string()),
372-
..Default::default()
373-
},
374-
perm,
375-
)
376-
.context("failed to create virtual branch")?;
377-
378-
// rebasing the extra commits onto the new branch
379-
let gix_repo = ctx.repo().to_gix()?;
380-
let mut head = new_branch.head(&gix_repo)?;
381-
for commit in extra_commits {
382-
let new_branch_head = ctx
383-
.repo()
384-
.find_commit(head)
385-
.context("failed to find new branch head")?;
386-
387-
let rebased_commit_oid = ctx
388-
.repo()
389-
.commit_with_signature(
390-
None,
391-
&commit.author(),
392-
&commit.committer(),
393-
&commit.message_bstr().to_str_lossy(),
394-
&commit.tree().unwrap(),
395-
&[&new_branch_head],
396-
None,
397-
)
398-
.context(format!(
399-
"failed to rebase commit {} onto new branch",
400-
commit.id()
401-
))?;
402-
403-
let rebased_commit = ctx.repo().find_commit(rebased_commit_oid).context(format!(
404-
"failed to find rebased commit {}",
405-
rebased_commit_oid
406-
))?;
407-
408-
new_branch.set_stack_head(
409-
&vb_handle,
410-
&gix_repo,
411-
rebased_commit.id(),
412-
Some(rebased_commit.tree_id()),
413-
)?;
414-
415-
head = rebased_commit.id();
416-
}
417-
Ok(())
418-
}
419-
420315
fn invalid_head_err(head_name: &str) -> anyhow::Error {
421316
anyhow!(
422317
"project is on {head_name}. Please checkout {} to continue",

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

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub mod upstream_integration;
3939

4040
mod integration;
4141
pub use integration::{update_workspace_commit, verify_branch};
42+
pub mod workspace_commit;
4243

4344
mod file;
4445
pub use file::{Get, RemoteBranchFile};

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

+5-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use crate::{
77
remote::branch_to_remote_branch,
88
stack::stack_series,
99
status::{get_applied_status, get_applied_status_cached},
10-
RemoteBranchData, VirtualBranchHunkRange, VirtualBranchHunkRangeMap, VirtualBranchesExt,
10+
verify_branch, RemoteBranchData, VirtualBranchHunkRange, VirtualBranchHunkRangeMap,
11+
VirtualBranchesExt,
1112
};
1213
use anyhow::{anyhow, bail, Context, Result};
1314
use bstr::{BString, ByteSlice};
@@ -321,6 +322,9 @@ pub fn list_virtual_branches_cached(
321322
) -> Result<StackListResult> {
322323
assure_open_workspace_mode(ctx)
323324
.context("Listing virtual branches requires open workspace mode")?;
325+
// Make sure that the workspace commit is the head of the branch before listing.
326+
verify_branch(ctx, perm)?;
327+
324328
let mut branches: Vec<VirtualBranch> = Vec::new();
325329

326330
let vb_state = ctx.project().virtual_branches();

0 commit comments

Comments
 (0)