Merge pull request #4256 from gitbutlerapp/sep-vb-crate

start moving virtual_branches to separate crate
This commit is contained in:
Kiril Videlov 2024-07-07 14:03:42 +02:00 committed by GitHub
commit 2cd91be6f4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
41 changed files with 164 additions and 64 deletions

View File

@ -19,6 +19,7 @@ jobs:
gitbutler-git: ${{ steps.filter.outputs.gitbutler-git }}
gitbutler-cli: ${{ steps.filter.outputs.gitbutler-cli }}
gitbutler-watcher: ${{ steps.filter.outputs.gitbutler-watcher }}
gitbutler-branch: ${{ steps.filter.outputs.gitbutler-branch }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
@ -55,6 +56,9 @@ jobs:
gitbutler-watcher:
- *rust
- 'crates/gitbutler-watcher/**'
gitbutler-branch:
- *rust
- 'crates/gitbutler-branch/**'
lint-node:
needs: changes
@ -182,6 +186,30 @@ jobs:
features: ${{ toJson(matrix.features) }}
action: ${{ matrix.action }}
check-gitbutler-branch:
needs: changes
if: ${{ needs.changes.outputs.gitbutler-branch == 'true' }}
runs-on: ubuntu-latest
container:
image: ghcr.io/gitbutlerapp/ci-base-image:latest
strategy:
matrix:
action:
- test
- check
features:
- ''
- '*'
- []
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/init-env-rust
- uses: ./.github/actions/check-crate
with:
crate: gitbutler-branch
features: ${{ toJson(matrix.features) }}
action: ${{ matrix.action }}
check-gitbutler-cli:
needs: changes
if: ${{ needs.changes.outputs.gitbutler-cli == 'true' }}
@ -238,6 +266,7 @@ jobs:
- changes
- check-gitbutler-tauri
- check-gitbutler-core
- check-gitbutler-branch
- check-gitbutler-git
- check-gitbutler-cli
- check-gitbutler-watcher

21
Cargo.lock generated
View File

@ -2102,6 +2102,25 @@ dependencies = [
"thiserror",
]
[[package]]
name = "gitbutler-branch"
version = "0.0.0"
dependencies = [
"anyhow",
"git2",
"gitbutler-core",
"gitbutler-git",
"gitbutler-testsupport",
"glob",
"itertools 0.13.0",
"once_cell",
"pretty_assertions",
"serial_test",
"tempfile",
"tokio",
"tracing",
]
[[package]]
name = "gitbutler-cli"
version = "0.0.0"
@ -2209,6 +2228,7 @@ dependencies = [
"dirs 5.0.1",
"futures",
"git2",
"gitbutler-branch",
"gitbutler-core",
"gitbutler-testsupport",
"gitbutler-watcher",
@ -2256,6 +2276,7 @@ dependencies = [
"anyhow",
"backoff",
"futures",
"gitbutler-branch",
"gitbutler-core",
"gitbutler-notify-debouncer",
"gix",

View File

@ -6,7 +6,8 @@ members = [
"crates/gitbutler-watcher",
"crates/gitbutler-watcher/vendor/debouncer",
"crates/gitbutler-testsupport",
"crates/gitbutler-cli",
"crates/gitbutler-cli",
"crates/gitbutler-branch",
]
resolver = "2"
@ -25,6 +26,7 @@ gitbutler-core = { path = "crates/gitbutler-core" }
gitbutler-watcher = { path = "crates/gitbutler-watcher" }
gitbutler-testsupport = { path = "crates/gitbutler-testsupport" }
gitbutler-cli ={ path = "crates/gitbutler-cli" }
gitbutler-branch = { path = "crates/gitbutler-branch" }
[profile.release]
codegen-units = 1 # Compile crates one after another so the compiler can optimize better

View File

@ -0,0 +1,27 @@
[package]
name = "gitbutler-branch"
version = "0.0.0"
edition = "2021"
authors = ["GitButler <gitbutler@gitbutler.com>"]
publish = false
[dependencies]
tracing = "0.1.40"
anyhow = "1.0.86"
git2.workspace = true
tokio.workspace = true
gitbutler-core.workspace = true
[[test]]
name="branches"
path = "tests/virtual_branches/mod.rs"
[dev-dependencies]
once_cell = "1.19"
pretty_assertions = "1.4"
gitbutler-testsupport.workspace = true
gitbutler-git = { workspace = true, features = ["test-askpass-path"] }
glob = "0.3.1"
serial_test = "3.1.1"
tempfile = "3.10"
itertools = "0.13"

View File

@ -1,20 +1,22 @@
use crate::{
use anyhow::Result;
use gitbutler_core::{
git::{credentials::Helper, BranchExt},
ops::entry::{OperationKind, SnapshotDetails},
project_repository::Repository,
projects::FetchResult,
types::ReferenceName,
};
use anyhow::Result;
use std::{path::Path, sync::Arc};
use tokio::sync::Semaphore;
use super::{
use gitbutler_core::virtual_branches;
use gitbutler_core::virtual_branches::{
branch::{BranchId, BranchOwnershipClaims},
target, BaseBranch, NameConflitResolution, RemoteBranchFile, VirtualBranchesHandle,
};
use crate::{
use gitbutler_core::{
git,
projects::{self, Project},
};
@ -44,7 +46,7 @@ impl Controller {
self.permit(project.ignore_project_semaphore).await;
let project_repository = open_with_verify(project)?;
let snapshot_tree = project_repository.project().prepare_snapshot();
let result = super::commit(
let result = virtual_branches::commit(
&project_repository,
branch_id,
message,
@ -69,28 +71,32 @@ impl Controller {
branch_name: &git::RemoteRefname,
) -> Result<bool> {
let project_repository = Repository::open(project)?;
super::is_remote_branch_mergeable(&project_repository, branch_name).map_err(Into::into)
virtual_branches::is_remote_branch_mergeable(&project_repository, branch_name)
.map_err(Into::into)
}
pub async fn list_virtual_branches(
&self,
project: &Project,
) -> Result<(Vec<super::VirtualBranch>, Vec<git::diff::FileDiff>)> {
) -> Result<(
Vec<virtual_branches::VirtualBranch>,
Vec<git::diff::FileDiff>,
)> {
self.permit(project.ignore_project_semaphore).await;
let project_repository = open_with_verify(project)?;
super::list_virtual_branches(&project_repository).map_err(Into::into)
virtual_branches::list_virtual_branches(&project_repository).map_err(Into::into)
}
pub async fn create_virtual_branch(
&self,
project: &Project,
create: &super::branch::BranchCreateRequest,
create: &virtual_branches::branch::BranchCreateRequest,
) -> Result<BranchId> {
self.permit(project.ignore_project_semaphore).await;
let project_repository = open_with_verify(project)?;
let branch_id = super::create_virtual_branch(&project_repository, create)?.id;
let branch_id = virtual_branches::create_virtual_branch(&project_repository, create)?.id;
Ok(branch_id)
}
@ -102,12 +108,13 @@ impl Controller {
self.permit(project.ignore_project_semaphore).await;
let project_repository = open_with_verify(project)?;
super::create_virtual_branch_from_branch(&project_repository, branch).map_err(Into::into)
virtual_branches::create_virtual_branch_from_branch(&project_repository, branch)
.map_err(Into::into)
}
pub async fn get_base_branch_data(&self, project: &Project) -> Result<BaseBranch> {
let project_repository = Repository::open(project)?;
super::get_base_branch_data(&project_repository)
virtual_branches::get_base_branch_data(&project_repository)
}
pub async fn list_remote_commit_files(
@ -116,7 +123,8 @@ impl Controller {
commit_oid: git2::Oid,
) -> Result<Vec<RemoteBranchFile>> {
let project_repository = Repository::open(project)?;
super::list_remote_commit_files(project_repository.repo(), commit_oid).map_err(Into::into)
virtual_branches::list_remote_commit_files(project_repository.repo(), commit_oid)
.map_err(Into::into)
}
pub async fn set_base_branch(
@ -128,12 +136,12 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::SetBaseBranch));
super::set_base_branch(&project_repository, target_branch)
virtual_branches::set_base_branch(&project_repository, target_branch)
}
pub async fn set_target_push_remote(&self, project: &Project, push_remote: &str) -> Result<()> {
let project_repository = Repository::open(project)?;
super::set_target_push_remote(&project_repository, push_remote)
virtual_branches::set_target_push_remote(&project_repository, push_remote)
}
pub async fn integrate_upstream_commits(
@ -147,7 +155,8 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::MergeUpstream));
super::integrate_upstream_commits(&project_repository, branch_id).map_err(Into::into)
virtual_branches::integrate_upstream_commits(&project_repository, branch_id)
.map_err(Into::into)
}
pub async fn update_base_branch(&self, project: &Project) -> Result<Vec<ReferenceName>> {
@ -157,7 +166,7 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::UpdateWorkspaceBase));
super::update_base_branch(&project_repository)
virtual_branches::update_base_branch(&project_repository)
.map(|unapplied_branches| {
unapplied_branches
.iter()
@ -170,7 +179,7 @@ impl Controller {
pub async fn update_virtual_branch(
&self,
project: &Project,
branch_update: super::branch::BranchUpdateRequest,
branch_update: virtual_branches::branch::BranchUpdateRequest,
) -> Result<()> {
self.permit(project.ignore_project_semaphore).await;
@ -180,7 +189,7 @@ impl Controller {
.project()
.virtual_branches()
.get_branch(branch_update.id)?;
let result = super::update_branch(&project_repository, &branch_update);
let result = virtual_branches::update_branch(&project_repository, &branch_update);
let _ = snapshot_tree.and_then(|snapshot_tree| {
project_repository.project().snapshot_branch_update(
snapshot_tree,
@ -200,7 +209,7 @@ impl Controller {
self.permit(project.ignore_project_semaphore).await;
let project_repository = open_with_verify(project)?;
super::delete_branch(&project_repository, branch_id)
virtual_branches::delete_branch(&project_repository, branch_id)
}
pub async fn unapply_ownership(
@ -214,7 +223,7 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::DiscardHunk));
super::unapply_ownership(&project_repository, ownership).map_err(Into::into)
virtual_branches::unapply_ownership(&project_repository, ownership).map_err(Into::into)
}
pub async fn reset_files(&self, project: &Project, files: &Vec<String>) -> Result<()> {
@ -224,7 +233,7 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::DiscardFile));
super::reset_files(&project_repository, files).map_err(Into::into)
virtual_branches::reset_files(&project_repository, files).map_err(Into::into)
}
pub async fn amend(
@ -240,7 +249,7 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::AmendCommit));
super::amend(&project_repository, branch_id, commit_oid, ownership)
virtual_branches::amend(&project_repository, branch_id, commit_oid, ownership)
}
pub async fn move_commit_file(
@ -257,7 +266,7 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::MoveCommitFile));
super::move_commit_file(
virtual_branches::move_commit_file(
&project_repository,
branch_id,
from_commit_oid,
@ -278,7 +287,8 @@ impl Controller {
let project_repository = open_with_verify(project)?;
let snapshot_tree = project_repository.project().prepare_snapshot();
let result: Result<()> =
super::undo_commit(&project_repository, branch_id, commit_oid).map_err(Into::into);
virtual_branches::undo_commit(&project_repository, branch_id, commit_oid)
.map_err(Into::into);
let _ = snapshot_tree.and_then(|snapshot_tree| {
project_repository.project().snapshot_commit_undo(
snapshot_tree,
@ -302,7 +312,7 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::InsertBlankCommit));
super::insert_blank_commit(&project_repository, branch_id, commit_oid, offset)
virtual_branches::insert_blank_commit(&project_repository, branch_id, commit_oid, offset)
.map_err(Into::into)
}
@ -319,7 +329,7 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::ReorderCommit));
super::reorder_commit(&project_repository, branch_id, commit_oid, offset)
virtual_branches::reorder_commit(&project_repository, branch_id, commit_oid, offset)
.map_err(Into::into)
}
@ -335,7 +345,8 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::UndoCommit));
super::reset_branch(&project_repository, branch_id, target_commit_oid).map_err(Into::into)
virtual_branches::reset_branch(&project_repository, branch_id, target_commit_oid)
.map_err(Into::into)
}
pub async fn convert_to_real_branch(
@ -348,9 +359,12 @@ impl Controller {
let project_repository = open_with_verify(project)?;
let snapshot_tree = project_repository.project().prepare_snapshot();
let result =
super::convert_to_real_branch(&project_repository, branch_id, name_conflict_resolution)
.map_err(Into::into);
let result = virtual_branches::convert_to_real_branch(
&project_repository,
branch_id,
name_conflict_resolution,
)
.map_err(Into::into);
let _ = snapshot_tree.and_then(|snapshot_tree| {
project_repository
.project()
@ -369,21 +383,24 @@ impl Controller {
self.permit(project.ignore_project_semaphore).await;
let helper = Helper::default();
let project_repository = open_with_verify(project)?;
super::push(&project_repository, branch_id, with_force, &helper, askpass)
virtual_branches::push(&project_repository, branch_id, with_force, &helper, askpass)
}
pub async fn list_remote_branches(&self, project: Project) -> Result<Vec<super::RemoteBranch>> {
pub async fn list_remote_branches(
&self,
project: Project,
) -> Result<Vec<virtual_branches::RemoteBranch>> {
let project_repository = Repository::open(&project)?;
super::list_remote_branches(&project_repository)
virtual_branches::list_remote_branches(&project_repository)
}
pub async fn get_remote_branch_data(
&self,
project: &Project,
refname: &git::Refname,
) -> Result<super::RemoteBranchData> {
) -> Result<virtual_branches::RemoteBranchData> {
let project_repository = Repository::open(project)?;
super::get_branch_data(&project_repository, refname)
virtual_branches::get_branch_data(&project_repository, refname)
}
pub async fn squash(
@ -398,7 +415,7 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::SquashCommit));
super::squash(&project_repository, branch_id, commit_oid).map_err(Into::into)
virtual_branches::squash(&project_repository, branch_id, commit_oid).map_err(Into::into)
}
pub async fn update_commit_message(
@ -413,7 +430,7 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::UpdateCommitMessage));
super::update_commit_message(&project_repository, branch_id, commit_oid, message)
virtual_branches::update_commit_message(&project_repository, branch_id, commit_oid, message)
.map_err(Into::into)
}
@ -472,7 +489,8 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::MoveCommit));
super::move_commit(&project_repository, target_branch_id, commit_oid).map_err(Into::into)
virtual_branches::move_commit(&project_repository, target_branch_id, commit_oid)
.map_err(Into::into)
}
async fn permit(&self, ignore: bool) {
@ -484,7 +502,7 @@ impl Controller {
fn open_with_verify(project: &Project) -> Result<Repository> {
let project_repository = Repository::open(project)?;
super::integration::verify_branch(&project_repository)?;
virtual_branches::integration::verify_branch(&project_repository)?;
Ok(project_repository)
}

View File

@ -0,0 +1,3 @@
//! GitButler internal library containing functionaliry related to branches, i.e. the virtual branches implementation
pub mod controller;
pub use controller::Controller;

View File

@ -0,0 +1 @@
mod virtual_branches;

View File

@ -1,11 +1,12 @@
use std::path::PathBuf;
use std::{fs, path, str::FromStr};
use gitbutler_branch::Controller;
use gitbutler_core::error::Marker;
use gitbutler_core::{
git,
projects::{self, Project, ProjectId},
virtual_branches::{branch, Controller},
virtual_branches::branch,
};
use tempfile::TempDir;

View File

@ -1,6 +1,6 @@
use gitbutler_core::virtual_branches::{branch, BranchId};
use crate::suite::virtual_branches::Test;
use super::Test;
#[tokio::test]
async fn no_diffs() {

View File

@ -2,7 +2,7 @@ use std::fs;
use gitbutler_core::virtual_branches::branch;
use crate::suite::virtual_branches::Test;
use super::Test;
#[tokio::test]
async fn to_head() {

View File

@ -2,7 +2,7 @@ use std::fs;
use gitbutler_core::virtual_branches::{branch, branch::BranchOwnershipClaims};
use crate::suite::virtual_branches::Test;
use super::Test;
#[tokio::test]
async fn should_unapply_with_commits() {

View File

@ -46,7 +46,7 @@ impl Project {
/// Prepares a snapshot of the current state of the working directory as well as GitButler data.
/// Returns a tree hash of the snapshot. The snapshot is not discoverable until it is committed with [`commit_snapshot`](Self::commit_snapshot())
/// If there are files that are untracked and larger than `SNAPSHOT_FILE_LIMIT_BYTES`, they are excluded from snapshot creation and restoring.
pub(crate) fn prepare_snapshot(&self) -> Result<git2::Oid> {
pub fn prepare_snapshot(&self) -> Result<git2::Oid> {
let worktree_dir = self.path.as_path();
let repo = git2::Repository::open(worktree_dir)?;
@ -172,7 +172,7 @@ impl Project {
///
/// Returns `Some(snapshot_commit_id)` if it was created or `None` if nothing changed between the previous oplog
/// commit and the current one (after comparing trees).
pub(crate) fn commit_snapshot(
pub fn commit_snapshot(
&self,
snapshot_tree_id: git2::Oid,
details: SnapshotDetails,

View File

@ -11,7 +11,7 @@ use super::entry::Trailer;
/// Snapshot functionality
impl Project {
pub(crate) fn snapshot_branch_unapplied(
pub fn snapshot_branch_unapplied(
&self,
snapshot_tree: git2::Oid,
result: Result<&git2::Branch, &anyhow::Error>,
@ -22,7 +22,7 @@ impl Project {
self.commit_snapshot(snapshot_tree, details)?;
Ok(())
}
pub(crate) fn snapshot_commit_undo(
pub fn snapshot_commit_undo(
&self,
snapshot_tree: git2::Oid,
result: Result<&(), &anyhow::Error>,
@ -34,7 +34,7 @@ impl Project {
self.commit_snapshot(snapshot_tree, details)?;
Ok(())
}
pub(crate) fn snapshot_commit_creation(
pub fn snapshot_commit_creation(
&self,
snapshot_tree: git2::Oid,
error: Option<&anyhow::Error>,
@ -60,7 +60,7 @@ impl Project {
self.commit_snapshot(snapshot_tree, details)?;
Ok(())
}
pub(crate) fn snapshot_branch_creation(&self, branch_name: String) -> anyhow::Result<()> {
pub fn snapshot_branch_creation(&self, branch_name: String) -> anyhow::Result<()> {
let details =
SnapshotDetails::new(OperationKind::CreateBranch).with_trailers(vec![Trailer {
key: "name".to_string(),
@ -69,7 +69,7 @@ impl Project {
self.create_snapshot(details)?;
Ok(())
}
pub(crate) fn snapshot_branch_deletion(&self, branch_name: String) -> anyhow::Result<()> {
pub fn snapshot_branch_deletion(&self, branch_name: String) -> anyhow::Result<()> {
let details =
SnapshotDetails::new(OperationKind::DeleteBranch).with_trailers(vec![Trailer {
key: "name".to_string(),
@ -79,7 +79,7 @@ impl Project {
self.create_snapshot(details)?;
Ok(())
}
pub(crate) fn snapshot_branch_update(
pub fn snapshot_branch_update(
&self,
snapshot_tree: git2::Oid,
old_branch: &Branch,

View File

@ -11,9 +11,6 @@ pub use integration::GITBUTLER_INTEGRATION_REFERENCE;
mod base;
pub use base::*;
pub mod controller;
pub use controller::Controller;
mod r#virtual;
pub use r#virtual::*;

View File

@ -1,6 +1,5 @@
mod suite {
mod projects;
mod virtual_branches;
}
mod git;

View File

@ -48,6 +48,7 @@ tracing-appender = "0.2.3"
tracing-subscriber = "0.3.17"
gitbutler-core.workspace = true
gitbutler-watcher.workspace = true
gitbutler-branch.workspace = true
open = "5"
[dependencies.tauri]

View File

@ -135,7 +135,7 @@ fn main() {
let git_credentials_controller = git::credentials::Helper::default();
app_handle.manage(git_credentials_controller.clone());
app_handle.manage(gitbutler_core::virtual_branches::controller::Controller::default());
app_handle.manage(gitbutler_branch::controller::Controller::default());
let remotes_controller = gitbutler_core::remotes::controller::Controller::new(
projects_controller.clone(),

View File

@ -1,6 +1,7 @@
pub mod commands {
use crate::error::Error;
use anyhow::{anyhow, Context};
use gitbutler_branch::Controller;
use gitbutler_core::{
assets,
error::Code,
@ -9,7 +10,6 @@ pub mod commands {
types::ReferenceName,
virtual_branches::{
branch::{self, BranchId, BranchOwnershipClaims},
controller::Controller,
BaseBranch, NameConflitResolution, RemoteBranch, RemoteBranchData, RemoteBranchFile,
VirtualBranches,
},

View File

@ -3,7 +3,7 @@ use std::sync::Arc;
use anyhow::{Context, Result};
use futures::executor::block_on;
use gitbutler_core::projects::{self, Project, ProjectId};
use gitbutler_core::{assets, users, virtual_branches};
use gitbutler_core::{assets, users};
use tauri::{AppHandle, Manager};
use tracing::instrument;
@ -76,7 +76,7 @@ pub struct Watchers {
fn handler_from_app(app: &AppHandle) -> anyhow::Result<gitbutler_watcher::Handler> {
let projects = app.state::<projects::Controller>().inner().clone();
let users = app.state::<users::Controller>().inner().clone();
let vbranches = app.state::<virtual_branches::Controller>().inner().clone();
let vbranches = app.state::<gitbutler_branch::Controller>().inner().clone();
let assets_proxy = app.state::<assets::Proxy>().inner().clone();
Ok(gitbutler_watcher::Handler::new(

View File

@ -10,6 +10,7 @@ doctest = false
[dependencies]
gitbutler-core.workspace = true
gitbutler-branch.workspace = true
thiserror.workspace = true
anyhow = "1.0.86"
futures = "0.3.30"

View File

@ -7,7 +7,7 @@ use gitbutler_core::ops::entry::{OperationKind, SnapshotDetails};
use gitbutler_core::projects::ProjectId;
use gitbutler_core::synchronize::sync_with_gitbutler;
use gitbutler_core::virtual_branches::VirtualBranches;
use gitbutler_core::{assets, git, project_repository, projects, users, virtual_branches};
use gitbutler_core::{assets, git, project_repository, projects, users};
use tracing::instrument;
use super::{events, Change};
@ -24,7 +24,7 @@ pub struct Handler {
// need extra protection.
projects: projects::Controller,
users: users::Controller,
vbranch_controller: virtual_branches::Controller,
vbranch_controller: gitbutler_branch::Controller,
assets_proxy: assets::Proxy,
/// A function to send events - decoupled from app-handle for testing purposes.
@ -38,7 +38,7 @@ impl Handler {
pub fn new(
projects: projects::Controller,
users: users::Controller,
vbranch_controller: virtual_branches::Controller,
vbranch_controller: gitbutler_branch::Controller,
assets_proxy: assets::Proxy,
send_event: impl Fn(Change) -> Result<()> + Send + Sync + 'static,
) -> Self {