Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 20 additions & 20 deletions crates/but-api/src/legacy/worktree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,18 @@ pub fn worktree_new(
reference: gix::refs::FullName,
) -> Result<NewWorktreeOutcome> {
let guard = ctx.shared_worktree_access();
let (repo, ws, _) = ctx.workspace_and_db_with_perm(guard.read_permission())?;

but_worktrees::new::worktree_new(ctx, guard.read_permission(), reference.as_ref())
but_worktrees::new::worktree_new(&repo, &ws, &ctx.project_data_dir(), reference.as_ref())
}

#[but_api]
#[instrument(err(Debug))]
pub fn worktree_list(ctx: &mut but_ctx::Context) -> Result<ListWorktreeOutcome> {
let guard = ctx.shared_worktree_access();
let _guard = ctx.shared_worktree_access();
let repo = ctx.repo.get()?;

but_worktrees::list::worktree_list(ctx, guard.read_permission())
but_worktrees::list::worktree_list(&repo)
}

#[but_api]
Expand All @@ -34,11 +36,14 @@ pub fn worktree_integration_status(
id: WorktreeId,
target: gix::refs::FullName,
) -> Result<WorktreeIntegrationStatus> {
let guard = ctx.exclusive_worktree_access();
let mut guard = ctx.exclusive_worktree_access();
let mut meta = ctx.meta()?;
let (repo, mut ws, _) = ctx.workspace_mut_and_db_with_perm(guard.write_permission())?;

but_worktrees::integrate::worktree_integration_status(
ctx,
guard.read_permission(),
&repo,
&mut ws,
&mut meta,
&id,
target.as_ref(),
)
Expand All @@ -52,13 +57,10 @@ pub fn worktree_integrate(
target: gix::refs::FullName,
) -> Result<()> {
let mut guard = ctx.exclusive_worktree_access();
let mut meta = ctx.meta()?;
let (repo, mut ws, _) = ctx.workspace_mut_and_db_with_perm(guard.write_permission())?;

but_worktrees::integrate::worktree_integrate(
ctx,
guard.write_permission(),
&id,
target.as_ref(),
)
but_worktrees::integrate::worktree_integrate(&repo, &mut ws, &mut meta, &id, target.as_ref())
}

#[but_api]
Expand All @@ -67,9 +69,10 @@ pub fn worktree_destroy_by_id(
ctx: &mut but_ctx::Context,
id: WorktreeId,
) -> Result<DestroyWorktreeOutcome> {
let mut guard = ctx.exclusive_worktree_access();
let _guard = ctx.exclusive_worktree_access();
let repo = ctx.repo.get()?;

but_worktrees::destroy::worktree_destroy_by_id(ctx, guard.write_permission(), &id)
but_worktrees::destroy::worktree_destroy_by_id(&repo, &id)
}

#[but_api]
Expand All @@ -78,11 +81,8 @@ pub fn worktree_destroy_by_reference(
ctx: &mut but_ctx::Context,
reference: gix::refs::FullName,
) -> Result<DestroyWorktreeOutcome> {
let mut guard = ctx.exclusive_worktree_access();
let _guard = ctx.exclusive_worktree_access();
let repo = ctx.repo.get()?;

but_worktrees::destroy::worktree_destroy_by_reference(
ctx,
guard.write_permission(),
reference.as_ref(),
)
but_worktrees::destroy::worktree_destroy_by_reference(&repo, reference.as_ref())
}
9 changes: 8 additions & 1 deletion crates/but-serde/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,21 @@ pub use bstring::BStringForFrontend;
/// ```
pub mod bstring_lossy {
use bstr::{BString, ByteSlice};
use serde::Serialize;
use serde::{Deserialize, Deserializer, Serialize};

pub fn serialize<S>(v: &BString, s: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
v.to_str_lossy().serialize(s)
}

pub fn deserialize<'de, D>(d: D) -> Result<BString, D::Error>
where
D: Deserializer<'de>,
{
Ok(String::deserialize(d)?.into())
}
}

/// Use on `gix::refs::FullName` fields that should serialize as JSON strings.
Expand Down
11 changes: 3 additions & 8 deletions crates/but-worktrees/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,20 @@ rust-version.workspace = true
doctest = false

[dependencies]
but-workspace.workspace = true
but-meta = { workspace = true, features = ["legacy"] }
but-rebase.workspace = true
but-core.workspace = true
but-ctx.workspace = true

gitbutler-stack.workspace = true
gitbutler-workspace.workspace = true
gitbutler-branch-actions.workspace = true
but-graph.workspace = true

anyhow.workspace = true
gix.workspace = true
serde.workspace = true
but-serde.workspace = true
uuid.workspace = true
bstr.workspace = true
tracing.workspace = true

[dev-dependencies]
but-oxidize.workspace = true
but-ctx.workspace = true
but-testsupport.workspace = true

insta.workspace = true
Expand Down
13 changes: 5 additions & 8 deletions crates/but-worktrees/src/destroy.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use anyhow::Result;
use but_ctx::{Context, access::RepoExclusive};
use serde::Serialize;

use crate::{WorktreeId, git::git_worktree_remove, list::worktree_list};
Expand All @@ -13,12 +12,11 @@ pub struct DestroyWorktreeOutcome {

/// Destroys a worktree by its ID.
pub fn worktree_destroy_by_id(
ctx: &mut Context,
_perm: &RepoExclusive,
repo: &gix::Repository,
id: &WorktreeId,
) -> Result<DestroyWorktreeOutcome> {
// Remove the git worktree (force=true to handle uncommitted changes)
git_worktree_remove(ctx.repo.get()?.common_dir(), id, true)?;
git_worktree_remove(repo.common_dir(), id, true)?;

Ok(DestroyWorktreeOutcome {
destroyed_ids: vec![id.clone()],
Expand All @@ -27,12 +25,11 @@ pub fn worktree_destroy_by_id(

/// Destroys all worktrees created from a given reference.
pub fn worktree_destroy_by_reference(
ctx: &mut Context,
perm: &RepoExclusive,
repo: &gix::Repository,
reference: &gix::refs::FullNameRef,
) -> Result<DestroyWorktreeOutcome> {
// Use the existing list function to get all worktrees
let list_outcome = worktree_list(ctx, perm.read_permission())?;
let list_outcome = worktree_list(repo)?;

// Filter for worktrees created from the specified reference
let worktrees_to_destroy: Vec<_> = list_outcome
Expand All @@ -51,7 +48,7 @@ pub fn worktree_destroy_by_reference(
// Destroy each matching worktree
for worktree in worktrees_to_destroy {
// Remove the git worktree (force=true to handle uncommitted changes)
git_worktree_remove(ctx.repo.get()?.common_dir(), &worktree.id, true)?;
git_worktree_remove(repo.common_dir(), &worktree.id, true)?;

destroyed_ids.push(worktree.id);
}
Expand Down
38 changes: 18 additions & 20 deletions crates/but-worktrees/src/git.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,38 @@
use std::path::Path;

use anyhow::{Result, bail};
use bstr::BString;

use crate::WorktreeId;

/// Creates a git worktree.
///
/// Git does not accept fully qualified branch names. The given partial ref will
/// be written out under `refs/heads`
///
/// Returns the full reference.
pub(crate) fn git_worktree_add(
project_path: &Path,
path: &Path,
branch_name: &gix::refs::PartialNameRef,
commit: gix::ObjectId,
) -> Result<gix::refs::FullName> {
) -> Result<()> {
let output =
std::process::Command::from(gix::command::prepare(gix::path::env::exe_invocation()))
.current_dir(project_path)
.arg("worktree")
.arg("add")
.args(["-B", &branch_name.to_string()])
.arg("--detach")
.arg(path.as_os_str())
.arg(commit.to_string())
.stderr(std::process::Stdio::piped())
.output()?;

tracing::info!("{}", str::from_utf8(&output.stdout)?);
tracing::error!("{}", str::from_utf8(&output.stderr)?);
tracing::debug!(
stdout = %String::from_utf8_lossy(&output.stdout),
stderr = %String::from_utf8_lossy(&output.stderr),
"git worktree add"
);

if output.status.success() {
let mut out = BString::from(b"refs/heads/");
out.extend_from_slice(branch_name.as_bstr());
Ok(gix::refs::FullName::try_from(out)?)
Ok(())
} else {
bail!(
"Failed to create worktree\n\n{}",
str::from_utf8(&output.stderr).unwrap_or("")
String::from_utf8_lossy(&output.stderr)
)
}
}
Expand All @@ -55,17 +50,20 @@ pub(crate) fn git_worktree_remove(project_path: &Path, id: &WorktreeId, force: b
command.arg("--force");
}

let output = command.output()?;
let output = command.stderr(std::process::Stdio::piped()).output()?;

tracing::info!("{}", str::from_utf8(&output.stdout)?);
tracing::error!("{}", str::from_utf8(&output.stderr)?);
tracing::debug!(
stdout = %String::from_utf8_lossy(&output.stdout),
stderr = %String::from_utf8_lossy(&output.stderr),
"git worktree remove"
);

if output.status.success() {
Ok(())
} else {
bail!(
"Failed to create worktree\n\n{}",
str::from_utf8(&output.stderr).unwrap_or("")
"Failed to remove worktree\n\n{}",
String::from_utf8_lossy(&output.stderr)
)
}
}
Loading
Loading