Skip to content

Commit 549ad74

Browse files
Merge pull request #10648 from gitbutlerapp/but-new-review-feedback
Address review feedback
2 parents 0afe08e + 01734b3 commit 549ad74

File tree

13 files changed

+114
-56
lines changed

13 files changed

+114
-56
lines changed

crates/but-api/src/commands/worktree.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,15 @@ use tracing::instrument;
1010
#[api_cmd]
1111
#[cfg_attr(feature = "tauri", tauri::command(async))]
1212
#[instrument(err(Debug))]
13-
pub fn worktree_new(project_id: ProjectId, reference: String) -> Result<NewWorktreeOutcome, Error> {
13+
pub fn worktree_new(
14+
project_id: ProjectId,
15+
reference: gix::refs::PartialName,
16+
) -> Result<NewWorktreeOutcome, Error> {
1417
let project = gitbutler_project::get(project_id)?;
1518
let guard = project.exclusive_worktree_access();
1619
let mut ctx = CommandContext::open(&project, AppSettings::load_from_default_path_creating()?)?;
1720

18-
but_worktrees::new::worktree_new(&mut ctx, guard.read_permission(), &reference)
21+
but_worktrees::new::worktree_new(&mut ctx, guard.read_permission(), reference.as_ref())
1922
.map_err(Into::into)
2023
}
2124

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-- Revert worktrees table path and reference columns back to TEXT
2+
3+
-- Create old table structure with TEXT columns
4+
CREATE TABLE `worktrees_old`(
5+
`path` TEXT NOT NULL PRIMARY KEY,
6+
`reference` TEXT NOT NULL,
7+
`base` TEXT NOT NULL,
8+
`source` TEXT NOT NULL
9+
);
10+
11+
-- Drop current table
12+
DROP TABLE worktrees;
13+
14+
-- Rename old table to original name
15+
ALTER TABLE worktrees_old RENAME TO worktrees;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
-- Change worktrees table path and reference columns to BLOB
2+
-- SQLite doesn't support ALTER COLUMN, so we need to recreate the table
3+
4+
-- Create new table with BLOB columns
5+
CREATE TABLE `worktrees_new`(
6+
`path` BLOB NOT NULL PRIMARY KEY,
7+
`reference` BLOB NOT NULL,
8+
`base` TEXT NOT NULL,
9+
`source` TEXT NOT NULL
10+
);
11+
12+
-- Drop old table
13+
DROP TABLE worktrees;
14+
15+
-- Rename new table to original name
16+
ALTER TABLE worktrees_new RENAME TO worktrees;

crates/but-db/src/schema.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,8 @@ diesel::table! {
8989

9090
diesel::table! {
9191
worktrees (path) {
92-
path -> Text,
93-
reference -> Text,
92+
path -> Binary,
93+
reference -> Binary,
9494
base -> Text,
9595
source -> Text,
9696
}

crates/but-db/src/worktrees.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,16 @@ use serde::{Deserialize, Serialize};
1212
#[diesel(table_name = crate::schema::worktrees)]
1313
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
1414
#[diesel(primary_key(path))]
15+
/// This struct should not be consumed directly. Instead use the
16+
/// [but_worktrees::Worktree] struct.
1517
pub struct Worktree {
16-
pub path: String,
17-
pub reference: String,
18+
/// A canonicalized path represented in bytes
19+
pub path: Vec<u8>,
20+
/// A fully qualified reference serialized from gix::refs::FullNameRef
21+
pub reference: Vec<u8>,
22+
/// A commit oid serialized as hexidecimal string
1823
pub base: String,
24+
/// JSON stringification of [but_worktrees::WorktreeSource]
1925
pub source: String,
2026
}
2127

@@ -37,15 +43,15 @@ impl WorktreesHandle<'_> {
3743
Ok(())
3844
}
3945

40-
pub fn get(&mut self, path: &str) -> Result<Option<Worktree>, diesel::result::Error> {
46+
pub fn get(&mut self, path: &[u8]) -> Result<Option<Worktree>, diesel::result::Error> {
4147
let worktree = worktrees
4248
.filter(crate::schema::worktrees::path.eq(path))
4349
.first::<Worktree>(&mut self.db.conn)
4450
.optional()?;
4551
Ok(worktree)
4652
}
4753

48-
pub fn delete(&mut self, path: &str) -> Result<(), diesel::result::Error> {
54+
pub fn delete(&mut self, path: &[u8]) -> Result<(), diesel::result::Error> {
4955
diesel::delete(worktrees.filter(crate::schema::worktrees::path.eq(path)))
5056
.execute(&mut self.db.conn)?;
5157
Ok(())

crates/but-worktrees/src/db.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Database operations for worktrees.
22
33
use anyhow::Result;
4+
use bstr::BString;
45
use gitbutler_command_context::CommandContext;
56
use std::path::Path;
67

@@ -16,7 +17,7 @@ pub fn save_worktree(ctx: &mut CommandContext, worktree: Worktree) -> Result<()>
1617
#[allow(unused)]
1718
pub fn get_worktree(ctx: &mut CommandContext, path: &Path) -> Result<Option<Worktree>> {
1819
let path_str = path.to_string_lossy();
19-
let worktree = ctx.db()?.worktrees().get(&path_str)?;
20+
let worktree = ctx.db()?.worktrees().get(&gix::path::into_bstr(path))?;
2021
match worktree {
2122
Some(w) => Ok(Some(w.try_into()?)),
2223
None => Ok(None),
@@ -26,8 +27,7 @@ pub fn get_worktree(ctx: &mut CommandContext, path: &Path) -> Result<Option<Work
2627
/// Delete a worktree from the database.
2728
#[allow(unused)]
2829
pub fn delete_worktree(ctx: &mut CommandContext, path: &Path) -> Result<()> {
29-
let path_str = path.to_string_lossy();
30-
ctx.db()?.worktrees().delete(&path_str)?;
30+
ctx.db()?.worktrees().delete(&gix::path::into_bstr(path))?;
3131
Ok(())
3232
}
3333

@@ -46,11 +46,11 @@ impl TryFrom<but_db::Worktree> for Worktree {
4646
fn try_from(value: but_db::Worktree) -> Result<Self, Self::Error> {
4747
let source: WorktreeSource = serde_json::from_str(&value.source)?;
4848
let base = gix::ObjectId::from_hex(value.base.as_bytes())?;
49-
let path = std::path::PathBuf::from(value.path);
49+
let path = gix::path::from_byte_slice(&value.path).to_owned();
5050

5151
Ok(Worktree {
5252
path,
53-
reference: value.reference,
53+
reference: gix::refs::FullName::try_from(BString::from(value.reference))?,
5454
base,
5555
source,
5656
})
@@ -63,11 +63,10 @@ impl TryFrom<Worktree> for but_db::Worktree {
6363
fn try_from(value: Worktree) -> Result<Self, Self::Error> {
6464
let source = serde_json::to_string(&value.source)?;
6565
let base = value.base.to_hex().to_string();
66-
let path = value.path.to_string_lossy().to_string();
6766

6867
Ok(but_db::Worktree {
69-
path,
70-
reference: value.reference,
68+
path: gix::path::into_bstr(&value.path).to_vec(),
69+
reference: value.reference.as_bstr().to_vec(),
7170
base,
7271
source,
7372
})

crates/but-worktrees/src/gc.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use crate::{Worktree, WorktreeHealthStatus, WorktreeSource};
22
use anyhow::Result;
3-
use bstr::BStr;
43
use but_workspace::ui::StackHeadInfo;
54

65
pub fn get_health(
@@ -9,7 +8,9 @@ pub fn get_health(
98
heads: &[StackHeadInfo],
109
) -> Result<WorktreeHealthStatus> {
1110
if !heads.iter().any(|h| match &worktree.source {
12-
WorktreeSource::Branch(b) => h.name == BStr::new(&b),
11+
WorktreeSource::Branch(b) => gix::refs::PartialName::try_from(h.name.clone())
12+
.map(|n| n == b.clone())
13+
.unwrap_or(false),
1314
}) {
1415
return Ok(WorktreeHealthStatus::WorkspaceBranchMissing);
1516
};
@@ -30,7 +31,7 @@ pub fn get_health(
3031
if !worktree_repo
3132
.head()?
3233
.referent_name()
33-
.map(|n| n.as_bstr() == worktree.reference)
34+
.map(|n| n == worktree.reference.as_ref())
3435
.unwrap_or(false)
3536
{
3637
return Ok(WorktreeHealthStatus::BranchNotCheckedOut);

crates/but-worktrees/src/git.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,25 @@
11
use std::path::Path;
22

33
use anyhow::{Result, bail};
4-
use tracing::{Level, event};
54

65
pub(crate) fn git_worktree_add(
76
project_path: &Path,
87
path: &Path,
9-
branch_name: &str,
8+
branch_name: &gix::refs::PartialNameRef,
109
commit: gix::ObjectId,
1110
) -> Result<()> {
1211
let output =
1312
std::process::Command::from(gix::command::prepare(gix::path::env::exe_invocation()))
1413
.current_dir(project_path)
1514
.arg("worktree")
1615
.arg("add")
17-
.args(["-B", branch_name])
16+
.args(["-B", &branch_name.to_string()])
1817
.arg(path.as_os_str())
1918
.arg(commit.to_string())
2019
.output()?;
2120

22-
event!(Level::INFO, "{}", str::from_utf8(&output.stdout)?);
23-
event!(Level::ERROR, "{}", str::from_utf8(&output.stderr)?);
21+
tracing::info!("{}", str::from_utf8(&output.stdout)?);
22+
tracing::error!("{}", str::from_utf8(&output.stderr)?);
2423

2524
if output.status.success() {
2625
Ok(())

crates/but-worktrees/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub mod new;
1919
pub enum WorktreeSource {
2020
/// The worktree was created from a branch. This is the given name of the
2121
/// branch
22-
Branch(String),
22+
Branch(gix::refs::PartialName),
2323
}
2424

2525
/// A worktree entry representing a Git worktree.
@@ -32,7 +32,7 @@ pub struct Worktree {
3232
pub path: PathBuf,
3333
/// The git reference this worktree was created from. This is a fully
3434
/// qualified reference
35-
pub reference: String,
35+
pub reference: gix::refs::FullName,
3636
/// The base commit (ObjectId) from which this worktree was created.
3737
pub base: gix::ObjectId,
3838
/// The source from which this worktree was created.

crates/but-worktrees/src/new.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use std::path::PathBuf;
22

33
use anyhow::{Context, Result};
4-
use bstr::BStr;
54
use but_graph::VirtualBranchesTomlMetadata;
65
use but_workspace::{StacksFilter, stacks_v3};
76
use gitbutler_command_context::CommandContext;
@@ -21,7 +20,7 @@ pub struct NewWorktreeOutcome {
2120
pub fn worktree_new(
2221
ctx: &mut CommandContext,
2322
_perm: &WorktreeReadPermission,
24-
refname: &str,
23+
refname: &gix::refs::PartialNameRef,
2524
) -> Result<NewWorktreeOutcome> {
2625
let repo = ctx.gix_repo_for_merging()?;
2726
let meta = VirtualBranchesTomlMetadata::from_path(
@@ -31,22 +30,26 @@ pub fn worktree_new(
3130
let head = stacks
3231
.into_iter()
3332
.flat_map(|s| s.heads)
34-
.find(|h| h.name == BStr::new(refname))
33+
.find(|h| {
34+
gix::refs::PartialName::try_from(h.name.clone())
35+
.map(|n| n.as_ref() == refname)
36+
.unwrap_or(false)
37+
})
3538
.context("Failed to find matching head")?;
3639

3740
// Used as a method of generating the path & refrence name.
3841
let id = uuid::Uuid::new_v4();
3942

4043
let path = worktree_path(ctx.project(), id);
41-
let branch_name = worktree_branch_name(id);
44+
let branch_name = gix::refs::PartialName::try_from(format!("gitbutler/worktree/{}", id))?;
4245

43-
git_worktree_add(&ctx.project().path, &path, &branch_name, head.tip)?;
46+
git_worktree_add(&ctx.project().path, &path, branch_name.as_ref(), head.tip)?;
4447

4548
let worktree = Worktree {
4649
path: path.canonicalize()?,
47-
reference: format!("refs/heads/{}", branch_name),
50+
reference: gix::refs::FullName::try_from(format!("refs/heads/gitbutler/worktree/{}", id))?,
4851
base: head.tip,
49-
source: WorktreeSource::Branch(head.name.to_string()),
52+
source: WorktreeSource::Branch(refname.to_owned()),
5053
};
5154
save_worktree(ctx, worktree.clone())?;
5255

@@ -56,7 +59,3 @@ pub fn worktree_new(
5659
fn worktree_path(project: &Project, id: uuid::Uuid) -> PathBuf {
5760
project.gb_dir().join("worktrees").join(id.to_string())
5861
}
59-
60-
fn worktree_branch_name(id: uuid::Uuid) -> String {
61-
format!("gitbutler/worktree/{}", id)
62-
}

0 commit comments

Comments
 (0)