mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-08-17 00:00:31 +03:00
Merge pull request #4262 from gitbutlerapp/feature-add-virtual-branches-access-trait
Move branch state to a separate crate
This commit is contained in:
commit
b72e3de178
29
.github/workflows/push.yaml
vendored
29
.github/workflows/push.yaml
vendored
@ -22,6 +22,7 @@ jobs:
|
||||
gitbutler-branch: ${{ steps.filter.outputs.gitbutler-branch }}
|
||||
gitbutler-sync: ${{ steps.filter.outputs.gitbutler-sync }}
|
||||
gitbutler-oplog: ${{ steps.filter.outputs.gitbutler-oplog }}
|
||||
gitbutler-branchstate: ${{ steps.filter.outputs.gitbutler-branchstate }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dorny/paths-filter@v3
|
||||
@ -67,6 +68,9 @@ jobs:
|
||||
gitbutler-oplog:
|
||||
- *rust
|
||||
- 'crates/gitbutler-oplog/**'
|
||||
gitbutler-branchstate:
|
||||
- *rust
|
||||
- 'crates/gitbutler-branchstate/**'
|
||||
|
||||
lint-node:
|
||||
needs: changes
|
||||
@ -266,6 +270,30 @@ jobs:
|
||||
features: ${{ toJson(matrix.features) }}
|
||||
action: ${{ matrix.action }}
|
||||
|
||||
check-gitbutler-branchstate:
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.gitbutler-branchstate == '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-branchstate
|
||||
features: ${{ toJson(matrix.features) }}
|
||||
action: ${{ matrix.action }}
|
||||
|
||||
check-gitbutler-cli:
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.gitbutler-cli == 'true' }}
|
||||
@ -325,6 +353,7 @@ jobs:
|
||||
- check-gitbutler-branch
|
||||
- check-gitbutler-sync
|
||||
- check-gitbutler-oplog
|
||||
- check-gitbutler-branchstate
|
||||
- check-gitbutler-git
|
||||
- check-gitbutler-cli
|
||||
- check-gitbutler-watcher
|
||||
|
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -2112,6 +2112,7 @@ dependencies = [
|
||||
"futures",
|
||||
"git2",
|
||||
"git2-hooks",
|
||||
"gitbutler-branchstate",
|
||||
"gitbutler-core",
|
||||
"gitbutler-git",
|
||||
"gitbutler-oplog",
|
||||
@ -2131,6 +2132,17 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gitbutler-branchstate"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gitbutler-core",
|
||||
"itertools 0.13.0",
|
||||
"serde",
|
||||
"toml 0.8.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gitbutler-cli"
|
||||
version = "0.0.0"
|
||||
@ -2234,6 +2246,7 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"git2",
|
||||
"gitbutler-branchstate",
|
||||
"gitbutler-core",
|
||||
"gix",
|
||||
"itertools 0.13.0",
|
||||
@ -2251,6 +2264,7 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"git2",
|
||||
"gitbutler-branchstate",
|
||||
"gitbutler-core",
|
||||
"gitbutler-oplog",
|
||||
"itertools 0.13.0",
|
||||
@ -2303,6 +2317,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"git2",
|
||||
"gitbutler-branch",
|
||||
"gitbutler-branchstate",
|
||||
"gitbutler-core",
|
||||
"keyring",
|
||||
"once_cell",
|
||||
|
@ -9,7 +9,8 @@ members = [
|
||||
"crates/gitbutler-cli",
|
||||
"crates/gitbutler-branch",
|
||||
"crates/gitbutler-sync",
|
||||
"crates/gitbutler-oplog",
|
||||
"crates/gitbutler-oplog",
|
||||
"crates/gitbutler-branchstate",
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
@ -31,6 +32,7 @@ gitbutler-cli ={ path = "crates/gitbutler-cli" }
|
||||
gitbutler-branch = { path = "crates/gitbutler-branch" }
|
||||
gitbutler-sync = { path = "crates/gitbutler-sync" }
|
||||
gitbutler-oplog = { path = "crates/gitbutler-oplog" }
|
||||
gitbutler-branchstate = { path = "crates/gitbutler-branchstate" }
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1 # Compile crates one after another so the compiler can optimize better
|
||||
|
@ -12,6 +12,7 @@ git2.workspace = true
|
||||
tokio.workspace = true
|
||||
gitbutler-core.workspace = true
|
||||
gitbutler-oplog.workspace = true
|
||||
gitbutler-branchstate.workspace = true
|
||||
serde = { workspace = true, features = ["std"]}
|
||||
bstr = "1.9.1"
|
||||
diffy = "0.3.0"
|
||||
|
@ -2,6 +2,7 @@ use std::{path::Path, time};
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use git2::Index;
|
||||
use gitbutler_branchstate::{VirtualBranchesAccess, VirtualBranchesHandle};
|
||||
use serde::Serialize;
|
||||
|
||||
use super::r#virtual as vb;
|
||||
@ -9,9 +10,7 @@ use super::r#virtual::convert_to_real_branch;
|
||||
use crate::integration::{get_workspace_head, update_gitbutler_integration};
|
||||
use crate::remote::{commit_to_remote_commit, RemoteCommit};
|
||||
use crate::VirtualBranchHunk;
|
||||
use gitbutler_core::virtual_branches::{
|
||||
branch, target, BranchId, VirtualBranchesHandle, GITBUTLER_INTEGRATION_REFERENCE,
|
||||
};
|
||||
use gitbutler_core::virtual_branches::{branch, target, BranchId, GITBUTLER_INTEGRATION_REFERENCE};
|
||||
use gitbutler_core::{error::Marker, git::RepositoryExt, rebase::cherry_rebase};
|
||||
use gitbutler_core::{
|
||||
git::{self, diff},
|
||||
|
@ -1,4 +1,5 @@
|
||||
use anyhow::Result;
|
||||
use gitbutler_branchstate::{VirtualBranchesAccess, VirtualBranchesHandle};
|
||||
use gitbutler_core::{
|
||||
git::{credentials::Helper, BranchExt},
|
||||
project_repository::Repository,
|
||||
@ -28,7 +29,7 @@ use gitbutler_core::virtual_branches;
|
||||
use crate::files::RemoteBranchFile;
|
||||
use gitbutler_core::virtual_branches::{
|
||||
branch::{BranchId, BranchOwnershipClaims},
|
||||
target, VirtualBranchesHandle,
|
||||
target,
|
||||
};
|
||||
use gitbutler_core::{
|
||||
git,
|
||||
|
@ -3,11 +3,12 @@ use std::{path::PathBuf, vec};
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use bstr::ByteSlice;
|
||||
|
||||
use gitbutler_branchstate::{VirtualBranchesAccess, VirtualBranchesHandle};
|
||||
use gitbutler_core::error::Marker;
|
||||
use gitbutler_core::git::RepositoryExt;
|
||||
use gitbutler_core::virtual_branches::{
|
||||
VirtualBranchesHandle, GITBUTLER_INTEGRATION_COMMIT_AUTHOR_EMAIL,
|
||||
GITBUTLER_INTEGRATION_COMMIT_AUTHOR_NAME, GITBUTLER_INTEGRATION_REFERENCE,
|
||||
GITBUTLER_INTEGRATION_COMMIT_AUTHOR_EMAIL, GITBUTLER_INTEGRATION_COMMIT_AUTHOR_NAME,
|
||||
GITBUTLER_INTEGRATION_REFERENCE,
|
||||
};
|
||||
use gitbutler_core::{
|
||||
git::CommitExt,
|
||||
|
@ -2,9 +2,10 @@ use std::path::Path;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use bstr::BString;
|
||||
use gitbutler_branchstate::VirtualBranchesHandle;
|
||||
use serde::Serialize;
|
||||
|
||||
use gitbutler_core::virtual_branches::{target, Author, VirtualBranchesHandle};
|
||||
use gitbutler_core::virtual_branches::{target, Author};
|
||||
use gitbutler_core::{
|
||||
git::{self, CommitExt, RepositoryExt},
|
||||
project_repository::{self, LogUntil},
|
||||
|
@ -1,3 +1,4 @@
|
||||
use gitbutler_branchstate::{VirtualBranchesAccess, VirtualBranchesHandle};
|
||||
use gitbutler_oplog::snapshot::Snapshot;
|
||||
use std::borrow::Borrow;
|
||||
#[cfg(target_family = "unix")]
|
||||
@ -35,7 +36,7 @@ use gitbutler_core::virtual_branches::{
|
||||
branch::{
|
||||
self, Branch, BranchCreateRequest, BranchId, BranchOwnershipClaims, Hunk, OwnershipClaim,
|
||||
},
|
||||
target, VirtualBranchesHandle,
|
||||
target,
|
||||
};
|
||||
use gitbutler_core::{
|
||||
dedup::{dedup, dedup_fmt},
|
||||
|
@ -17,6 +17,7 @@ use gitbutler_branch::r#virtual::{
|
||||
commit, create_virtual_branch, create_virtual_branch_from_branch, integrate_upstream_commits,
|
||||
is_remote_branch_mergeable, list_virtual_branches, unapply_ownership, update_branch,
|
||||
};
|
||||
use gitbutler_branchstate::VirtualBranchesAccess;
|
||||
use gitbutler_core::{
|
||||
git::{self, CommitExt, RepositoryExt},
|
||||
virtual_branches::branch::{BranchCreateRequest, BranchOwnershipClaims, BranchUpdateRequest},
|
||||
|
@ -1,4 +1,5 @@
|
||||
use super::*;
|
||||
use gitbutler_branchstate::VirtualBranchesAccess;
|
||||
use gitbutler_oplog::oplog::Oplog;
|
||||
use itertools::Itertools;
|
||||
use std::io::Write;
|
||||
|
13
crates/gitbutler-branchstate/Cargo.toml
Normal file
13
crates/gitbutler-branchstate/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "gitbutler-branchstate"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
authors = ["GitButler <gitbutler@gitbutler.com>"]
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
gitbutler-core.workspace = true
|
||||
itertools = "0.13"
|
||||
anyhow = "1.0.86"
|
||||
toml = "0.8.13"
|
||||
serde = { workspace = true, features = ["std"]}
|
4
crates/gitbutler-branchstate/src/lib.rs
Normal file
4
crates/gitbutler-branchstate/src/lib.rs
Normal file
@ -0,0 +1,4 @@
|
||||
mod state;
|
||||
pub use state::VirtualBranches as VirtualBranchesState;
|
||||
pub use state::VirtualBranchesAccess;
|
||||
pub use state::VirtualBranchesHandle;
|
@ -3,13 +3,12 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use crate::{error::Code, fs::read_toml_file_or_default};
|
||||
use anyhow::{anyhow, Result};
|
||||
use gitbutler_core::{error::Code, fs::read_toml_file_or_default, projects::Project};
|
||||
use itertools::Itertools;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{target::Target, Branch};
|
||||
use crate::virtual_branches::BranchId;
|
||||
use gitbutler_core::virtual_branches::{target::Target, Branch, BranchId};
|
||||
|
||||
/// The state of virtual branches data, as persisted in a TOML file.
|
||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||
@ -29,6 +28,16 @@ pub struct VirtualBranchesHandle {
|
||||
file_path: PathBuf,
|
||||
}
|
||||
|
||||
pub trait VirtualBranchesAccess {
|
||||
fn virtual_branches(&self) -> VirtualBranchesHandle;
|
||||
}
|
||||
|
||||
impl VirtualBranchesAccess for Project {
|
||||
fn virtual_branches(&self) -> VirtualBranchesHandle {
|
||||
VirtualBranchesHandle::new(self.gb_dir())
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtualBranchesHandle {
|
||||
/// Creates a new concurrency-safe handle to the state of virtual branches.
|
||||
pub fn new<P: AsRef<Path>>(base_path: P) -> Self {
|
||||
@ -162,5 +171,5 @@ impl VirtualBranchesHandle {
|
||||
}
|
||||
|
||||
fn write<P: AsRef<Path>>(file_path: P, virtual_branches: &VirtualBranches) -> Result<()> {
|
||||
crate::fs::write(file_path, toml::to_string(&virtual_branches)?)
|
||||
gitbutler_core::fs::write(file_path, toml::to_string(&virtual_branches)?)
|
||||
}
|
@ -5,7 +5,7 @@ use std::{
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{id::Id, types::default_true::DefaultTrue, virtual_branches::VirtualBranchesHandle};
|
||||
use crate::{id::Id, types::default_true::DefaultTrue};
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@ -117,11 +117,6 @@ impl Project {
|
||||
self.path.join(".git").join("gitbutler")
|
||||
}
|
||||
|
||||
/// Returns a handle to the virtual branches manager of the project.
|
||||
pub fn virtual_branches(&self) -> VirtualBranchesHandle {
|
||||
VirtualBranchesHandle::new(self.gb_dir())
|
||||
}
|
||||
|
||||
pub fn snapshot_lines_threshold(&self) -> usize {
|
||||
self.snapshot_lines_threshold.unwrap_or(20)
|
||||
}
|
||||
|
@ -2,10 +2,6 @@ pub mod branch;
|
||||
pub use branch::{Branch, BranchId};
|
||||
pub mod target;
|
||||
|
||||
mod state;
|
||||
pub use state::VirtualBranches as VirtualBranchesState;
|
||||
pub use state::VirtualBranchesHandle;
|
||||
|
||||
mod author;
|
||||
pub use author::Author;
|
||||
|
||||
|
@ -1,105 +0,0 @@
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use anyhow::Result;
|
||||
use gitbutler_core::virtual_branches;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use gitbutler_testsupport::{Case, Suite};
|
||||
|
||||
static TEST_INDEX: Lazy<AtomicUsize> = Lazy::new(|| AtomicUsize::new(0));
|
||||
|
||||
fn new_test_branch() -> virtual_branches::branch::Branch {
|
||||
TEST_INDEX.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
virtual_branches::branch::Branch {
|
||||
id: virtual_branches::BranchId::generate(),
|
||||
name: format!("branch_name_{}", TEST_INDEX.load(Ordering::Relaxed)),
|
||||
notes: String::new(),
|
||||
applied: true,
|
||||
upstream: Some(
|
||||
format!(
|
||||
"refs/remotes/origin/upstream_{}",
|
||||
TEST_INDEX.load(Ordering::Relaxed)
|
||||
)
|
||||
.parse()
|
||||
.unwrap(),
|
||||
),
|
||||
upstream_head: None,
|
||||
created_timestamp_ms: TEST_INDEX.load(Ordering::Relaxed) as u128,
|
||||
updated_timestamp_ms: (TEST_INDEX.load(Ordering::Relaxed) + 100) as u128,
|
||||
head: format!(
|
||||
"0123456789abcdef0123456789abcdef0123456{}",
|
||||
TEST_INDEX.load(Ordering::Relaxed)
|
||||
)
|
||||
.parse()
|
||||
.unwrap(),
|
||||
tree: format!(
|
||||
"0123456789abcdef0123456789abcdef012345{}",
|
||||
TEST_INDEX.load(Ordering::Relaxed) + 10
|
||||
)
|
||||
.parse()
|
||||
.unwrap(),
|
||||
ownership: virtual_branches::branch::BranchOwnershipClaims::default(),
|
||||
order: TEST_INDEX.load(Ordering::Relaxed),
|
||||
selected_for_changes: Some(1),
|
||||
allow_rebasing: true,
|
||||
}
|
||||
}
|
||||
|
||||
static TEST_TARGET_INDEX: Lazy<AtomicUsize> = Lazy::new(|| AtomicUsize::new(0));
|
||||
|
||||
fn new_test_target() -> virtual_branches::target::Target {
|
||||
virtual_branches::target::Target {
|
||||
branch: format!(
|
||||
"refs/remotes/branch name{}/remote name {}",
|
||||
TEST_TARGET_INDEX.load(Ordering::Relaxed),
|
||||
TEST_TARGET_INDEX.load(Ordering::Relaxed)
|
||||
)
|
||||
.parse()
|
||||
.unwrap(),
|
||||
remote_url: format!("remote url {}", TEST_TARGET_INDEX.load(Ordering::Relaxed)),
|
||||
sha: format!(
|
||||
"0123456789abcdef0123456789abcdef0123456{}",
|
||||
TEST_TARGET_INDEX.load(Ordering::Relaxed)
|
||||
)
|
||||
.parse()
|
||||
.unwrap(),
|
||||
push_remote_name: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_iterator() -> Result<()> {
|
||||
let suite = Suite::default();
|
||||
let Case { project, .. } = &suite.new_case();
|
||||
|
||||
let vb_state = project.virtual_branches();
|
||||
let iter = vb_state.list_branches()?;
|
||||
|
||||
assert_eq!(iter.len(), 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iterate_all() -> Result<()> {
|
||||
let suite = Suite::default();
|
||||
let Case { project, .. } = &suite.new_case();
|
||||
|
||||
let vb_state = project.virtual_branches();
|
||||
vb_state.set_default_target(new_test_target())?;
|
||||
let branch_1 = new_test_branch();
|
||||
vb_state.set_branch(branch_1.clone())?;
|
||||
let branch_2 = new_test_branch();
|
||||
vb_state.set_branch(branch_2.clone())?;
|
||||
let branch_3 = new_test_branch();
|
||||
vb_state.set_branch(branch_3.clone())?;
|
||||
|
||||
let iter = vb_state.list_branches()?;
|
||||
assert_eq!(iter.len(), 3);
|
||||
assert!(iter.contains(&branch_1));
|
||||
assert!(iter.contains(&branch_2));
|
||||
assert!(iter.contains(&branch_3));
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,2 +1 @@
|
||||
mod branch;
|
||||
mod iterator;
|
||||
|
@ -9,6 +9,7 @@ publish = false
|
||||
anyhow = "1.0.86"
|
||||
git2.workspace = true
|
||||
gitbutler-core.workspace = true
|
||||
gitbutler-branchstate.workspace = true
|
||||
serde = { workspace = true, features = ["std"]}
|
||||
itertools = "0.13"
|
||||
strum = { version = "0.26", features = ["derive"] }
|
||||
|
@ -1,5 +1,6 @@
|
||||
use anyhow::{anyhow, bail, Context};
|
||||
use git2::{DiffOptions, FileMode};
|
||||
use gitbutler_branchstate::{VirtualBranchesAccess, VirtualBranchesState};
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::str::{from_utf8, FromStr};
|
||||
@ -807,8 +808,7 @@ fn tree_from_applied_vbranches(
|
||||
.find_blob(vb_toml_entry.id())
|
||||
.context("failed to convert virtual_branches tree entry to blob")?;
|
||||
|
||||
let vbs_from_toml: gitbutler_core::virtual_branches::VirtualBranchesState =
|
||||
toml::from_str(from_utf8(vb_toml_blob.content())?)?;
|
||||
let vbs_from_toml: VirtualBranchesState = toml::from_str(from_utf8(vb_toml_blob.content())?)?;
|
||||
let applied_branch_trees: Vec<git2::Oid> = vbs_from_toml
|
||||
.branches
|
||||
.values()
|
||||
|
@ -12,3 +12,4 @@ itertools = "0.13"
|
||||
git2.workspace = true
|
||||
gitbutler-core.workspace = true
|
||||
gitbutler-oplog.workspace = true
|
||||
gitbutler-branchstate.workspace = true
|
||||
|
@ -1,6 +1,7 @@
|
||||
use std::time;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use gitbutler_branchstate::VirtualBranchesAccess;
|
||||
use gitbutler_core::id::Id;
|
||||
use gitbutler_core::{
|
||||
git::{self},
|
||||
|
@ -19,3 +19,4 @@ keyring.workspace = true
|
||||
serde_json = "1.0"
|
||||
gitbutler-core = { path = "../gitbutler-core" }
|
||||
gitbutler-branch = { path = "../gitbutler-branch" }
|
||||
gitbutler-branchstate = { path = "../gitbutler-branchstate" }
|
||||
|
@ -18,6 +18,7 @@ pub mod paths {
|
||||
}
|
||||
|
||||
pub mod virtual_branches {
|
||||
use gitbutler_branchstate::VirtualBranchesAccess;
|
||||
use gitbutler_core::{project_repository, virtual_branches};
|
||||
|
||||
use crate::empty_bare_repository;
|
||||
|
Loading…
Reference in New Issue
Block a user