Merge pull request #4258 from gitbutlerapp/Move-virtual-branches-implementation-out-of-core-and-into-gitbutler-branch-crate

Move virtual branches implementation out of core and into gitbutler-branch crate
This commit is contained in:
Kiril Videlov 2024-07-07 17:43:03 +02:00 committed by GitHub
commit 832ef485c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 2443 additions and 2399 deletions

10
Cargo.lock generated
View File

@ -2107,18 +2107,27 @@ name = "gitbutler-branch"
version = "0.0.0"
dependencies = [
"anyhow",
"bstr",
"diffy",
"futures",
"git2",
"git2-hooks",
"gitbutler-core",
"gitbutler-git",
"gitbutler-testsupport",
"glob",
"hex",
"itertools 0.13.0",
"md5",
"once_cell",
"pretty_assertions",
"regex",
"serde",
"serial_test",
"tempfile",
"tokio",
"tracing",
"url",
]
[[package]]
@ -2261,6 +2270,7 @@ version = "0.0.0"
dependencies = [
"anyhow",
"git2",
"gitbutler-branch",
"gitbutler-core",
"keyring",
"once_cell",

View File

@ -11,6 +11,15 @@ anyhow = "1.0.86"
git2.workspace = true
tokio.workspace = true
gitbutler-core.workspace = true
serde = { workspace = true, features = ["std"]}
bstr = "1.9.1"
diffy = "0.3.0"
hex = "0.4.3"
regex = "1.10"
git2-hooks = "0.3"
url = { version = "2.5.2", features = ["serde"] }
md5 = "0.7.0"
futures = "0.3"
[[test]]
name="branches"

View File

@ -0,0 +1,71 @@
use futures::future::join_all;
use crate::{base::BaseBranch, VirtualBranch, VirtualBranchCommit};
#[derive(Clone)]
pub struct Proxy {
core_proxy: gitbutler_core::assets::Proxy,
}
impl Proxy {
pub fn new(core_proxy: gitbutler_core::assets::Proxy) -> Self {
Proxy { core_proxy }
}
pub async fn proxy_virtual_branches(&self, branches: Vec<VirtualBranch>) -> Vec<VirtualBranch> {
join_all(
branches
.into_iter()
.map(|branch| self.proxy_virtual_branch(branch))
.collect::<Vec<_>>(),
)
.await
}
pub async fn proxy_virtual_branch(&self, branch: VirtualBranch) -> VirtualBranch {
VirtualBranch {
commits: join_all(
branch
.commits
.iter()
.map(|commit| self.proxy_virtual_branch_commit(commit.clone()))
.collect::<Vec<_>>(),
)
.await,
..branch
}
}
async fn proxy_virtual_branch_commit(
&self,
commit: VirtualBranchCommit,
) -> VirtualBranchCommit {
VirtualBranchCommit {
author: self.core_proxy.proxy_author(commit.author).await,
..commit
}
}
pub async fn proxy_base_branch(&self, base_branch: BaseBranch) -> BaseBranch {
BaseBranch {
recent_commits: join_all(
base_branch
.clone()
.recent_commits
.into_iter()
.map(|commit| self.core_proxy.proxy_remote_commit(commit))
.collect::<Vec<_>>(),
)
.await,
upstream_commits: join_all(
base_branch
.clone()
.upstream_commits
.into_iter()
.map(|commit| self.core_proxy.proxy_remote_commit(commit))
.collect::<Vec<_>>(),
)
.await,
..base_branch.clone()
}
}
}

View File

@ -4,15 +4,15 @@ use anyhow::{anyhow, Context, Result};
use git2::Index;
use serde::Serialize;
use super::{
branch, convert_to_real_branch,
integration::{
get_workspace_head, update_gitbutler_integration, GITBUTLER_INTEGRATION_REFERENCE,
},
target, BranchId, RemoteCommit, VirtualBranchHunk, VirtualBranchesHandle,
use super::r#virtual as vb;
use super::r#virtual::convert_to_real_branch;
use crate::integration::{get_workspace_head, update_gitbutler_integration};
use crate::VirtualBranchHunk;
use gitbutler_core::virtual_branches::{
branch, target, BranchId, RemoteCommit, VirtualBranchesHandle, GITBUTLER_INTEGRATION_REFERENCE,
};
use crate::{error::Marker, git::RepositoryExt, rebase::cherry_rebase};
use crate::{
use gitbutler_core::{error::Marker, git::RepositoryExt, rebase::cherry_rebase};
use gitbutler_core::{
git::{self, diff},
project_repository::{self, LogUntil},
projects::FetchResult,
@ -27,9 +27,9 @@ pub struct BaseBranch {
pub remote_url: String,
pub push_remote_name: Option<String>,
pub push_remote_url: String,
#[serde(with = "crate::serde::oid")]
#[serde(with = "gitbutler_core::serde::oid")]
pub base_sha: git2::Oid,
#[serde(with = "crate::serde::oid")]
#[serde(with = "gitbutler_core::serde::oid")]
pub current_sha: git2::Oid,
pub behind: usize,
pub upstream_commits: Vec<RemoteCommit>,
@ -209,7 +209,7 @@ pub fn set_base_branch(
},
);
let now_ms = crate::time::now_ms();
let now_ms = gitbutler_core::time::now_ms();
let (upstream, upstream_head) = if let git::Refname::Local(head_name) = &head_name {
let upstream_name = target_branch_ref.with_branch(head_name.branch());
@ -246,7 +246,7 @@ pub fn set_base_branch(
created_timestamp_ms: now_ms,
updated_timestamp_ms: now_ms,
head: current_head_commit.id(),
tree: super::write_tree_onto_commit(
tree: vb::write_tree_onto_commit(
project_repository,
current_head_commit.id(),
diff::diff_files_into_hunks(wd_diff),
@ -364,7 +364,7 @@ pub fn update_base_branch(
// try to update every branch
let updated_vbranches =
super::get_status_by_branch(project_repository, Some(&integration_commit))?
vb::get_status_by_branch(project_repository, Some(&integration_commit))?
.0
.into_iter()
.map(|(branch, _)| branch)
@ -568,14 +568,14 @@ pub fn update_base_branch(
})?;
// Rewriting the integration commit is necessary after changing target sha.
super::integration::update_gitbutler_integration(&vb_state, project_repository)?;
crate::integration::update_gitbutler_integration(&vb_state, project_repository)?;
Ok(unapplied_branch_names)
}
pub fn target_to_base_branch(
project_repository: &project_repository::Repository,
target: &target::Target,
) -> Result<super::BaseBranch> {
) -> Result<BaseBranch> {
let repo = project_repository.repo();
let branch = repo
.find_branch_by_refname(&target.branch.clone().into())?
@ -588,7 +588,7 @@ pub fn target_to_base_branch(
.log(oid, project_repository::LogUntil::Commit(target.sha))
.context("failed to get upstream commits")?
.iter()
.map(super::commit_to_remote_commit)
.map(gitbutler_core::virtual_branches::commit_to_remote_commit)
.collect::<Vec<_>>();
// get some recent commits
@ -596,7 +596,7 @@ pub fn target_to_base_branch(
.log(target.sha, LogUntil::Take(20))
.context("failed to get recent commits")?
.iter()
.map(super::commit_to_remote_commit)
.map(gitbutler_core::virtual_branches::commit_to_remote_commit)
.collect::<Vec<_>>();
// there has got to be a better way to do this.
@ -611,7 +611,7 @@ pub fn target_to_base_branch(
None => target.remote_url.clone(),
};
let base = super::BaseBranch {
let base = BaseBranch {
branch_name: format!("{}/{}", target.branch.remote(), target.branch.branch()),
remote_name: target.branch.remote().to_string(),
remote_url: target.remote_url.clone(),

View File

@ -10,11 +10,16 @@ use std::{path::Path, sync::Arc};
use tokio::sync::Semaphore;
use crate::base::{
get_base_branch_data, set_base_branch, set_target_push_remote, update_base_branch, BaseBranch,
};
use super::r#virtual as branch;
use gitbutler_core::virtual_branches;
use gitbutler_core::virtual_branches::{
branch::{BranchId, BranchOwnershipClaims},
target, BaseBranch, NameConflitResolution, RemoteBranchFile, VirtualBranchesHandle,
target, RemoteBranchFile, VirtualBranchesHandle,
};
use gitbutler_core::{
git,
@ -46,7 +51,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 = virtual_branches::commit(
let result = branch::commit(
&project_repository,
branch_id,
message,
@ -71,21 +76,17 @@ impl Controller {
branch_name: &git::RemoteRefname,
) -> Result<bool> {
let project_repository = Repository::open(project)?;
virtual_branches::is_remote_branch_mergeable(&project_repository, branch_name)
.map_err(Into::into)
branch::is_remote_branch_mergeable(&project_repository, branch_name).map_err(Into::into)
}
pub async fn list_virtual_branches(
&self,
project: &Project,
) -> Result<(
Vec<virtual_branches::VirtualBranch>,
Vec<git::diff::FileDiff>,
)> {
) -> Result<(Vec<branch::VirtualBranch>, Vec<git::diff::FileDiff>)> {
self.permit(project.ignore_project_semaphore).await;
let project_repository = open_with_verify(project)?;
virtual_branches::list_virtual_branches(&project_repository).map_err(Into::into)
branch::list_virtual_branches(&project_repository).map_err(Into::into)
}
pub async fn create_virtual_branch(
@ -96,7 +97,7 @@ impl Controller {
self.permit(project.ignore_project_semaphore).await;
let project_repository = open_with_verify(project)?;
let branch_id = virtual_branches::create_virtual_branch(&project_repository, create)?.id;
let branch_id = branch::create_virtual_branch(&project_repository, create)?.id;
Ok(branch_id)
}
@ -108,13 +109,12 @@ impl Controller {
self.permit(project.ignore_project_semaphore).await;
let project_repository = open_with_verify(project)?;
virtual_branches::create_virtual_branch_from_branch(&project_repository, branch)
.map_err(Into::into)
branch::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)?;
virtual_branches::get_base_branch_data(&project_repository)
get_base_branch_data(&project_repository)
}
pub async fn list_remote_commit_files(
@ -136,12 +136,12 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::SetBaseBranch));
virtual_branches::set_base_branch(&project_repository, target_branch)
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)?;
virtual_branches::set_target_push_remote(&project_repository, push_remote)
set_target_push_remote(&project_repository, push_remote)
}
pub async fn integrate_upstream_commits(
@ -155,8 +155,7 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::MergeUpstream));
virtual_branches::integrate_upstream_commits(&project_repository, branch_id)
.map_err(Into::into)
branch::integrate_upstream_commits(&project_repository, branch_id).map_err(Into::into)
}
pub async fn update_base_branch(&self, project: &Project) -> Result<Vec<ReferenceName>> {
@ -166,7 +165,7 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::UpdateWorkspaceBase));
virtual_branches::update_base_branch(&project_repository)
update_base_branch(&project_repository)
.map(|unapplied_branches| {
unapplied_branches
.iter()
@ -189,7 +188,7 @@ impl Controller {
.project()
.virtual_branches()
.get_branch(branch_update.id)?;
let result = virtual_branches::update_branch(&project_repository, &branch_update);
let result = branch::update_branch(&project_repository, &branch_update);
let _ = snapshot_tree.and_then(|snapshot_tree| {
project_repository.project().snapshot_branch_update(
snapshot_tree,
@ -209,7 +208,7 @@ impl Controller {
self.permit(project.ignore_project_semaphore).await;
let project_repository = open_with_verify(project)?;
virtual_branches::delete_branch(&project_repository, branch_id)
branch::delete_branch(&project_repository, branch_id)
}
pub async fn unapply_ownership(
@ -223,7 +222,7 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::DiscardHunk));
virtual_branches::unapply_ownership(&project_repository, ownership).map_err(Into::into)
branch::unapply_ownership(&project_repository, ownership).map_err(Into::into)
}
pub async fn reset_files(&self, project: &Project, files: &Vec<String>) -> Result<()> {
@ -233,7 +232,7 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::DiscardFile));
virtual_branches::reset_files(&project_repository, files).map_err(Into::into)
branch::reset_files(&project_repository, files).map_err(Into::into)
}
pub async fn amend(
@ -249,7 +248,7 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::AmendCommit));
virtual_branches::amend(&project_repository, branch_id, commit_oid, ownership)
branch::amend(&project_repository, branch_id, commit_oid, ownership)
}
pub async fn move_commit_file(
@ -266,7 +265,7 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::MoveCommitFile));
virtual_branches::move_commit_file(
branch::move_commit_file(
&project_repository,
branch_id,
from_commit_oid,
@ -287,8 +286,7 @@ impl Controller {
let project_repository = open_with_verify(project)?;
let snapshot_tree = project_repository.project().prepare_snapshot();
let result: Result<()> =
virtual_branches::undo_commit(&project_repository, branch_id, commit_oid)
.map_err(Into::into);
branch::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,
@ -312,7 +310,7 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::InsertBlankCommit));
virtual_branches::insert_blank_commit(&project_repository, branch_id, commit_oid, offset)
branch::insert_blank_commit(&project_repository, branch_id, commit_oid, offset)
.map_err(Into::into)
}
@ -329,7 +327,7 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::ReorderCommit));
virtual_branches::reorder_commit(&project_repository, branch_id, commit_oid, offset)
branch::reorder_commit(&project_repository, branch_id, commit_oid, offset)
.map_err(Into::into)
}
@ -345,21 +343,20 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::UndoCommit));
virtual_branches::reset_branch(&project_repository, branch_id, target_commit_oid)
.map_err(Into::into)
branch::reset_branch(&project_repository, branch_id, target_commit_oid).map_err(Into::into)
}
pub async fn convert_to_real_branch(
&self,
project: &Project,
branch_id: BranchId,
name_conflict_resolution: NameConflitResolution,
name_conflict_resolution: branch::NameConflitResolution,
) -> Result<ReferenceName> {
self.permit(project.ignore_project_semaphore).await;
let project_repository = open_with_verify(project)?;
let snapshot_tree = project_repository.project().prepare_snapshot();
let result = virtual_branches::convert_to_real_branch(
let result = branch::convert_to_real_branch(
&project_repository,
branch_id,
name_conflict_resolution,
@ -383,7 +380,7 @@ impl Controller {
self.permit(project.ignore_project_semaphore).await;
let helper = Helper::default();
let project_repository = open_with_verify(project)?;
virtual_branches::push(&project_repository, branch_id, with_force, &helper, askpass)
branch::push(&project_repository, branch_id, with_force, &helper, askpass)
}
pub async fn list_remote_branches(
@ -415,7 +412,7 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::SquashCommit));
virtual_branches::squash(&project_repository, branch_id, commit_oid).map_err(Into::into)
branch::squash(&project_repository, branch_id, commit_oid).map_err(Into::into)
}
pub async fn update_commit_message(
@ -430,7 +427,7 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::UpdateCommitMessage));
virtual_branches::update_commit_message(&project_repository, branch_id, commit_oid, message)
branch::update_commit_message(&project_repository, branch_id, commit_oid, message)
.map_err(Into::into)
}
@ -489,8 +486,7 @@ impl Controller {
let _ = project_repository
.project()
.create_snapshot(SnapshotDetails::new(OperationKind::MoveCommit));
virtual_branches::move_commit(&project_repository, target_branch_id, commit_oid)
.map_err(Into::into)
branch::move_commit(&project_repository, target_branch_id, commit_oid).map_err(Into::into)
}
async fn permit(&self, ignore: bool) {
@ -502,7 +498,7 @@ impl Controller {
fn open_with_verify(project: &Project) -> Result<Repository> {
let project_repository = Repository::open(project)?;
virtual_branches::integration::verify_branch(&project_repository)?;
crate::integration::verify_branch(&project_repository)?;
Ok(project_repository)
}

View File

@ -2,25 +2,20 @@ use std::{path::PathBuf, vec};
use anyhow::{anyhow, bail, Context, Result};
use bstr::ByteSlice;
use lazy_static::lazy_static;
use super::VirtualBranchesHandle;
use crate::error::Marker;
use crate::git::RepositoryExt;
use crate::{
git::{self, CommitExt},
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,
};
use gitbutler_core::{
git::CommitExt,
project_repository::{self, conflicts, LogUntil},
virtual_branches::branch::BranchCreateRequest,
};
lazy_static! {
pub static ref GITBUTLER_INTEGRATION_REFERENCE: git::LocalRefname =
git::LocalRefname::new("gitbutler/integration", None);
}
const WORKSPACE_HEAD: &str = "Workspace Head";
pub const GITBUTLER_INTEGRATION_COMMIT_AUTHOR_NAME: &str = "GitButler";
pub const GITBUTLER_INTEGRATION_COMMIT_AUTHOR_EMAIL: &str = "gitbutler@gitbutler.com";
pub fn get_integration_commiter<'a>() -> Result<git2::Signature<'a>> {
Ok(git2::Signature::now(
@ -302,7 +297,13 @@ pub fn verify_branch(project_repository: &project_repository::Repository) -> Res
Ok(())
}
impl project_repository::Repository {
pub trait Verify {
fn verify_head_is_set(&self) -> Result<&Self>;
fn verify_current_branch_name(&self) -> Result<&Self>;
fn verify_head_is_clean(&self) -> Result<&Self>;
}
impl Verify for project_repository::Repository {
fn verify_head_is_set(&self) -> Result<&Self> {
match self.get_head().context("failed to get head")?.name() {
Some(refname) if *refname == GITBUTLER_INTEGRATION_REFERENCE.to_string() => Ok(self),

View File

@ -1,3 +1,12 @@
//! GitButler internal library containing functionaliry related to branches, i.e. the virtual branches implementation
pub mod controller;
pub use controller::Controller;
pub mod r#virtual;
pub use r#virtual::*;
pub mod assets;
pub mod base;
pub mod integration;

View File

@ -4,7 +4,6 @@ use std::os::unix::prelude::PermissionsExt;
use std::time::SystemTime;
use std::{
collections::HashMap,
hash::Hash,
path::{Path, PathBuf},
time, vec,
};
@ -15,26 +14,28 @@ use diffy::{apply_bytes as diffy_apply, Line, Patch};
use git2::build::TreeUpdateBuilder;
use git2::ErrorCode;
use git2_hooks::HookResult;
use gitbutler_core::virtual_branches::Author;
use hex::ToHex;
use regex::Regex;
use serde::{Deserialize, Serialize};
use super::integration::{get_integration_commiter, get_workspace_head};
use super::{
use crate::integration::{get_integration_commiter, get_workspace_head};
use gitbutler_core::error::Code;
use gitbutler_core::error::Marker;
use gitbutler_core::git::diff::GitHunk;
use gitbutler_core::git::diff::{diff_files_into_hunks, trees, FileDiff};
use gitbutler_core::git::{
normalize_branch_name, CommitExt, CommitHeadersV2, HasCommitHeaders, RepositoryExt,
};
use gitbutler_core::rebase::{cherry_rebase, cherry_rebase_group};
use gitbutler_core::time::now_since_unix_epoch_ms;
use gitbutler_core::virtual_branches::branch::HunkHash;
use gitbutler_core::virtual_branches::{
branch::{
self, Branch, BranchCreateRequest, BranchId, BranchOwnershipClaims, Hunk, OwnershipClaim,
},
branch_to_remote_branch, target, RemoteBranch, VirtualBranchesHandle,
};
use crate::error::Code;
use crate::error::Marker;
use crate::git::diff::GitHunk;
use crate::git::diff::{diff_files_into_hunks, trees, FileDiff};
use crate::git::{CommitExt, CommitHeadersV2, HasCommitHeaders, RepositoryExt};
use crate::rebase::{cherry_rebase, cherry_rebase_group};
use crate::time::now_since_unix_epoch_ms;
use crate::virtual_branches::branch::HunkHash;
use crate::{
use gitbutler_core::{
dedup::{dedup, dedup_fmt},
git::{
self,
@ -73,13 +74,13 @@ pub struct VirtualBranch {
pub updated_at: u128,
pub selected_for_changes: bool,
pub allow_rebasing: bool,
#[serde(with = "crate::serde::oid")]
#[serde(with = "gitbutler_core::serde::oid")]
pub head: git2::Oid,
/// The merge base between the target branch and the virtual branch
#[serde(with = "crate::serde::oid")]
#[serde(with = "gitbutler_core::serde::oid")]
pub merge_base: git2::Oid,
/// The fork point between the target branch and the virtual branch
#[serde(with = "crate::serde::oid_opt", default)]
#[serde(with = "gitbutler_core::serde::oid_opt", default)]
pub fork_point: Option<git2::Oid>,
}
@ -101,16 +102,16 @@ pub struct VirtualBranches {
#[derive(Debug, PartialEq, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct VirtualBranchCommit {
#[serde(with = "crate::serde::oid")]
#[serde(with = "gitbutler_core::serde::oid")]
pub id: git2::Oid,
#[serde(serialize_with = "crate::serde::as_string_lossy")]
#[serde(serialize_with = "gitbutler_core::serde::as_string_lossy")]
pub description: BString,
pub created_at: u128,
pub author: Author,
pub is_remote: bool,
pub files: Vec<VirtualBranchFile>,
pub is_integrated: bool,
#[serde(with = "crate::serde::oid_vec")]
#[serde(with = "gitbutler_core::serde::oid_vec")]
pub parent_ids: Vec<git2::Oid>,
pub branch_id: BranchId,
pub change_id: Option<String>,
@ -150,11 +151,11 @@ pub struct VirtualBranchFile {
#[serde(rename_all = "camelCase")]
pub struct VirtualBranchHunk {
pub id: String,
#[serde(serialize_with = "crate::serde::as_string_lossy")]
#[serde(serialize_with = "gitbutler_core::serde::as_string_lossy")]
pub diff: BString,
pub modified_at: u128,
pub file_path: PathBuf,
#[serde(serialize_with = "crate::serde::hash_to_hex")]
#[serde(serialize_with = "gitbutler_core::serde::hash_to_hex")]
pub hash: HunkHash,
pub old_start: u32,
pub start: u32,
@ -194,33 +195,6 @@ impl VirtualBranchHunk {
}
}
#[derive(Debug, Serialize, Hash, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct Author {
pub name: String,
pub email: String,
pub gravatar_url: url::Url,
}
impl From<git2::Signature<'_>> for Author {
fn from(value: git2::Signature) -> Self {
let name = value.name().unwrap_or_default().to_string();
let email = value.email().unwrap_or_default().to_string();
let gravatar_url = url::Url::parse(&format!(
"https://www.gravatar.com/avatar/{:x}?s=100&r=g&d=retro",
md5::compute(email.to_lowercase())
))
.unwrap();
Author {
name,
email,
gravatar_url,
}
}
}
#[derive(Default, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", tag = "type", content = "value")]
pub enum NameConflitResolution {
@ -230,11 +204,6 @@ pub enum NameConflitResolution {
Overwrite,
}
pub fn normalize_branch_name(name: &str) -> String {
let pattern = Regex::new("[^A-Za-z0-9_/.#]+").unwrap();
pattern.replace_all(name, "-").to_string()
}
pub fn unapply_ownership(
project_repository: &project_repository::Repository,
ownership: &BranchOwnershipClaims,
@ -330,7 +299,7 @@ pub fn unapply_ownership(
.checkout()
.context("failed to checkout tree")?;
super::integration::update_gitbutler_integration(&vb_state, project_repository)?;
crate::integration::update_gitbutler_integration(&vb_state, project_repository)?;
Ok(())
}
@ -480,7 +449,7 @@ pub fn convert_to_real_branch(
// Ensure we still have a default target
ensure_selected_for_changes(&vb_state).context("failed to ensure selected for changes")?;
super::integration::update_gitbutler_integration(&vb_state, project_repository)?;
crate::integration::update_gitbutler_integration(&vb_state, project_repository)?;
Ok(real_branch)
}
@ -515,7 +484,7 @@ pub fn list_virtual_branches(
.context("failed to get default target")?;
let integration_commit_id =
super::integration::get_workspace_head(&vb_state, project_repository)?;
crate::integration::get_workspace_head(&vb_state, project_repository)?;
let integration_commit = project_repository
.repo()
.find_commit(integration_commit_id)
@ -843,7 +812,7 @@ pub fn create_virtual_branch(
}
}
let now = crate::time::now_ms();
let now = gitbutler_core::time::now_ms();
let mut branch = Branch {
id: BranchId::generate(),
@ -1023,7 +992,7 @@ pub fn integrate_upstream_commits(
.checkout()?;
};
super::integration::update_gitbutler_integration(&vb_state, project_repository)?;
crate::integration::update_gitbutler_integration(&vb_state, project_repository)?;
Ok(())
}
@ -1842,7 +1811,7 @@ pub fn reset_branch(
let old_head = get_workspace_head(&vb_state, project_repository)?;
branch.head = target_commit_id;
branch.updated_timestamp_ms = crate::time::now_ms();
branch.updated_timestamp_ms = gitbutler_core::time::now_ms();
vb_state.set_branch(branch.clone())?;
let updated_head = get_workspace_head(&vb_state, project_repository)?;
@ -1879,7 +1848,7 @@ pub fn reset_branch(
.set_branch(branch)
.context("failed to write branch")?;
super::integration::update_gitbutler_integration(&vb_state, project_repository)
crate::integration::update_gitbutler_integration(&vb_state, project_repository)
.context("failed to update gitbutler integration")?;
Ok(())
@ -2178,10 +2147,10 @@ pub fn commit(
let vb_state = project_repository.project().virtual_branches();
branch.tree = tree_oid;
branch.head = commit_oid;
branch.updated_timestamp_ms = crate::time::now_ms();
branch.updated_timestamp_ms = gitbutler_core::time::now_ms();
vb_state.set_branch(branch.clone())?;
super::integration::update_gitbutler_integration(&vb_state, project_repository)
crate::integration::update_gitbutler_integration(&vb_state, project_repository)
.context("failed to update gitbutler integration")?;
Ok(commit_oid)
@ -2583,7 +2552,7 @@ pub fn move_commit_file(
if upstream_commits.is_empty() {
target_branch.head = commit_oid;
vb_state.set_branch(target_branch.clone())?;
super::integration::update_gitbutler_integration(&vb_state, project_repository)?;
crate::integration::update_gitbutler_integration(&vb_state, project_repository)?;
return Ok(commit_oid);
}
@ -2600,7 +2569,7 @@ pub fn move_commit_file(
if let Some(new_head) = new_head {
target_branch.head = new_head;
vb_state.set_branch(target_branch.clone())?;
super::integration::update_gitbutler_integration(&vb_state, project_repository)?;
crate::integration::update_gitbutler_integration(&vb_state, project_repository)?;
Ok(commit_oid)
} else {
Err(anyhow!("rebase failed"))
@ -2639,7 +2608,7 @@ pub fn amend(
let default_target = vb_state.get_default_target()?;
let integration_commit_id =
super::integration::get_workspace_head(&vb_state, project_repository)?;
crate::integration::get_workspace_head(&vb_state, project_repository)?;
let (mut applied_statuses, _) = get_applied_status(
project_repository,
@ -2735,7 +2704,7 @@ pub fn amend(
if upstream_commits.is_empty() {
target_branch.head = commit_oid;
vb_state.set_branch(target_branch.clone())?;
super::integration::update_gitbutler_integration(&vb_state, project_repository)?;
crate::integration::update_gitbutler_integration(&vb_state, project_repository)?;
return Ok(commit_oid);
}
@ -2751,7 +2720,7 @@ pub fn amend(
if let Some(new_head) = new_head {
target_branch.head = new_head;
vb_state.set_branch(target_branch.clone())?;
super::integration::update_gitbutler_integration(&vb_state, project_repository)?;
crate::integration::update_gitbutler_integration(&vb_state, project_repository)?;
Ok(commit_oid)
} else {
Err(anyhow!("rebase failed"))
@ -2803,10 +2772,10 @@ pub fn reorder_commit(
let new_head = cherry_rebase_group(project_repository, parent_oid, &mut ids_to_rebase)
.context("rebase failed")?;
branch.head = new_head;
branch.updated_timestamp_ms = crate::time::now_ms();
branch.updated_timestamp_ms = gitbutler_core::time::now_ms();
vb_state.set_branch(branch.clone())?;
super::integration::update_gitbutler_integration(&vb_state, project_repository)
crate::integration::update_gitbutler_integration(&vb_state, project_repository)
.context("failed to update gitbutler integration")?;
} else {
// move commit down
@ -2840,10 +2809,10 @@ pub fn reorder_commit(
.context("rebase failed")?;
branch.head = new_head;
branch.updated_timestamp_ms = crate::time::now_ms();
branch.updated_timestamp_ms = gitbutler_core::time::now_ms();
vb_state.set_branch(branch.clone())?;
super::integration::update_gitbutler_integration(&vb_state, project_repository)
crate::integration::update_gitbutler_integration(&vb_state, project_repository)
.context("failed to update gitbutler integration")?;
}
@ -2878,7 +2847,7 @@ pub fn insert_blank_commit(
if commit.id() == branch.head && offset < 0 {
// inserting before the first commit
branch.head = blank_commit_oid;
super::integration::update_gitbutler_integration(&vb_state, project_repository)
crate::integration::update_gitbutler_integration(&vb_state, project_repository)
.context("failed to update gitbutler integration")?;
} else {
// rebase all commits above it onto the new commit
@ -2890,7 +2859,7 @@ pub fn insert_blank_commit(
) {
Ok(Some(new_head)) => {
branch.head = new_head;
super::integration::update_gitbutler_integration(&vb_state, project_repository)
crate::integration::update_gitbutler_integration(&vb_state, project_repository)
.context("failed to update gitbutler integration")?;
}
Ok(None) => bail!("no rebase happened"),
@ -2899,7 +2868,7 @@ pub fn insert_blank_commit(
}
}
}
branch.updated_timestamp_ms = crate::time::now_ms();
branch.updated_timestamp_ms = gitbutler_core::time::now_ms();
vb_state.set_branch(branch.clone())?;
Ok(())
@ -2947,10 +2916,10 @@ pub fn undo_commit(
if new_commit_oid != commit_oid {
branch.head = new_commit_oid;
branch.updated_timestamp_ms = crate::time::now_ms();
branch.updated_timestamp_ms = gitbutler_core::time::now_ms();
vb_state.set_branch(branch.clone())?;
super::integration::update_gitbutler_integration(&vb_state, project_repository)
crate::integration::update_gitbutler_integration(&vb_state, project_repository)
.context("failed to update gitbutler integration")?;
}
@ -3042,10 +3011,10 @@ pub fn squash(
Ok(new_head_id) => {
// save new branch head
branch.head = new_head_id;
branch.updated_timestamp_ms = crate::time::now_ms();
branch.updated_timestamp_ms = gitbutler_core::time::now_ms();
vb_state.set_branch(branch.clone())?;
super::integration::update_gitbutler_integration(&vb_state, project_repository)
crate::integration::update_gitbutler_integration(&vb_state, project_repository)
.context("failed to update gitbutler integration")?;
Ok(())
}
@ -3126,10 +3095,10 @@ pub fn update_commit_message(
.map_err(|err| err.context("rebase error"))?;
// save new branch head
branch.head = new_head_id;
branch.updated_timestamp_ms = crate::time::now_ms();
branch.updated_timestamp_ms = gitbutler_core::time::now_ms();
vb_state.set_branch(branch.clone())?;
super::integration::update_gitbutler_integration(&vb_state, project_repository)
crate::integration::update_gitbutler_integration(&vb_state, project_repository)
.context("failed to update gitbutler integration")?;
Ok(())
}
@ -3157,7 +3126,7 @@ pub fn move_commit(
let default_target = vb_state.get_default_target()?;
let integration_commit_id =
super::integration::get_workspace_head(&vb_state, project_repository)?;
crate::integration::get_workspace_head(&vb_state, project_repository)?;
let (mut applied_statuses, _) = get_applied_status(
project_repository,
@ -3269,7 +3238,7 @@ pub fn move_commit(
vb_state.set_branch(destination_branch.clone())?;
}
super::integration::update_gitbutler_integration(&vb_state, project_repository)
crate::integration::update_gitbutler_integration(&vb_state, project_repository)
.context("failed to update gitbutler integration")?;
Ok(())
@ -3505,7 +3474,7 @@ pub fn create_virtual_branch_from_branch(
}
}
super::integration::update_gitbutler_integration(&vb_state, project_repository)?;
crate::integration::update_gitbutler_integration(&vb_state, project_repository)?;
Ok(branch.name)
}
@ -3566,7 +3535,7 @@ pub fn create_virtual_branch_from_branch(
.any(|b| b.selected_for_changes.is_some()))
.then_some(now_since_unix_epoch_ms());
let now = crate::time::now_ms();
let now = gitbutler_core::time::now_ms();
// add file ownership based off the diff
let target_commit = repo.find_commit(default_target.sha)?;

View File

@ -1 +1,3 @@
mod virtual_branches;
mod extra;

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,5 @@
use gitbutler_core::{
id::Id,
virtual_branches::{Branch, VirtualBranch},
};
use gitbutler_branch::VirtualBranch;
use gitbutler_core::{id::Id, virtual_branches::Branch};
use super::*;

View File

@ -7,9 +7,7 @@ use url::Url;
use crate::{
users,
virtual_branches::{
Author, BaseBranch, RemoteBranchData, RemoteCommit, VirtualBranch, VirtualBranchCommit,
},
virtual_branches::{Author, RemoteBranchData, RemoteCommit},
};
#[derive(Clone)]
@ -40,40 +38,6 @@ impl Proxy {
user
}
async fn proxy_virtual_branch_commit(
&self,
commit: VirtualBranchCommit,
) -> VirtualBranchCommit {
VirtualBranchCommit {
author: self.proxy_author(commit.author).await,
..commit
}
}
pub async fn proxy_virtual_branch(&self, branch: VirtualBranch) -> VirtualBranch {
VirtualBranch {
commits: join_all(
branch
.commits
.iter()
.map(|commit| self.proxy_virtual_branch_commit(commit.clone()))
.collect::<Vec<_>>(),
)
.await,
..branch
}
}
pub async fn proxy_virtual_branches(&self, branches: Vec<VirtualBranch>) -> Vec<VirtualBranch> {
join_all(
branches
.into_iter()
.map(|branch| self.proxy_virtual_branch(branch))
.collect::<Vec<_>>(),
)
.await
}
pub async fn proxy_remote_branch_data(&self, branch: RemoteBranchData) -> RemoteBranchData {
RemoteBranchData {
commits: join_all(
@ -88,7 +52,7 @@ impl Proxy {
}
}
async fn proxy_author(&self, author: Author) -> Author {
pub async fn proxy_author(&self, author: Author) -> Author {
Author {
gravatar_url: self.proxy(&author.gravatar_url).await.unwrap_or_else(|error| {
tracing::error!(gravatar_url = %author.gravatar_url, ?error, "failed to proxy gravatar url");
@ -98,37 +62,13 @@ impl Proxy {
}
}
async fn proxy_remote_commit(&self, commit: RemoteCommit) -> RemoteCommit {
pub async fn proxy_remote_commit(&self, commit: RemoteCommit) -> RemoteCommit {
RemoteCommit {
author: self.proxy_author(commit.author).await,
..commit
}
}
pub async fn proxy_base_branch(&self, base_branch: BaseBranch) -> BaseBranch {
BaseBranch {
recent_commits: join_all(
base_branch
.clone()
.recent_commits
.into_iter()
.map(|commit| self.proxy_remote_commit(commit))
.collect::<Vec<_>>(),
)
.await,
upstream_commits: join_all(
base_branch
.clone()
.upstream_commits
.into_iter()
.map(|commit| self.proxy_remote_commit(commit))
.collect::<Vec<_>>(),
)
.await,
..base_branch.clone()
}
}
// takes a url of a remote assets, downloads it into cache and returns a url that points to the cached file
pub async fn proxy(&self, src: &Url) -> Result<Url> {
#[cfg(unix)]

View File

@ -1,10 +1,10 @@
pub(crate) fn dedup(existing: &[&str], new: &str) -> String {
pub fn dedup(existing: &[&str], new: &str) -> String {
dedup_fmt(existing, new, " ")
}
/// Makes sure that _new_ is not in _existing_ by adding a number to it.
/// the number is increased until the name is unique.
pub(crate) fn dedup_fmt(existing: &[&str], new: &str, separator: &str) -> String {
pub fn dedup_fmt(existing: &[&str], new: &str, separator: &str) -> String {
existing
.iter()
.filter_map(|x| {

View File

@ -1,2 +1,8 @@
mod refname;
pub use refname::{LocalRefname, Refname, RemoteRefname, VirtualRefname};
use regex::Regex;
pub fn normalize_branch_name(name: &str) -> String {
let pattern = Regex::new("[^A-Za-z0-9_/.#]+").unwrap();
pattern.replace_all(name, "-").to_string()
}

View File

@ -3,7 +3,7 @@ use std::{fmt, str::FromStr};
use serde::{Deserialize, Serialize};
use super::error::Error;
use crate::virtual_branches::{normalize_branch_name, Branch};
use crate::{git::normalize_branch_name, virtual_branches::Branch};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Refname {

View File

@ -10,10 +10,9 @@ use anyhow::Result;
use tracing::instrument;
use crate::git::diff::FileDiff;
use crate::virtual_branches::integration::{
GITBUTLER_INTEGRATION_COMMIT_AUTHOR_EMAIL, GITBUTLER_INTEGRATION_COMMIT_AUTHOR_NAME,
use crate::virtual_branches::{
Branch, GITBUTLER_INTEGRATION_COMMIT_AUTHOR_EMAIL, GITBUTLER_INTEGRATION_COMMIT_AUTHOR_NAME,
};
use crate::virtual_branches::Branch;
use crate::{git::diff::hunks_by_filepath, git::RepositoryExt, projects::Project};
use super::{

View File

@ -1,12 +1,13 @@
use crate::fs::write;
use crate::{
fs::write,
virtual_branches::{
GITBUTLER_INTEGRATION_COMMIT_AUTHOR_EMAIL, GITBUTLER_INTEGRATION_COMMIT_AUTHOR_NAME,
},
};
use anyhow::{Context, Result};
use gix::config::tree::Key;
use std::path::Path;
use crate::virtual_branches::integration::{
GITBUTLER_INTEGRATION_COMMIT_AUTHOR_EMAIL, GITBUTLER_INTEGRATION_COMMIT_AUTHOR_NAME,
};
/// Sets a reference to the oplog head commit such that snapshots are reachable and will not be garbage collected.
/// We want to achieve 2 things:
/// - The oplog must not be visible in `git log --all` as branch

View File

@ -0,0 +1,28 @@
use serde::Serialize;
#[derive(Debug, Serialize, Hash, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct Author {
pub name: String,
pub email: String,
pub gravatar_url: url::Url,
}
impl From<git2::Signature<'_>> for Author {
fn from(value: git2::Signature) -> Self {
let name = value.name().unwrap_or_default().to_string();
let email = value.email().unwrap_or_default().to_string();
let gravatar_url = url::Url::parse(&format!(
"https://www.gravatar.com/avatar/{:x}?s=100&r=g&d=retro",
md5::compute(email.to_lowercase())
))
.unwrap();
Author {
name,
email,
gravatar_url,
}
}
}

View File

@ -5,18 +5,21 @@ pub mod target;
mod files;
pub use files::*;
pub mod integration;
pub use integration::GITBUTLER_INTEGRATION_REFERENCE;
mod base;
pub use base::*;
mod r#virtual;
pub use r#virtual::*;
mod remote;
pub use remote::*;
mod state;
pub use state::VirtualBranches as VirtualBranchesState;
pub use state::VirtualBranchesHandle;
mod author;
pub use author::Author;
use lazy_static::lazy_static;
lazy_static! {
pub static ref GITBUTLER_INTEGRATION_REFERENCE: crate::git::LocalRefname =
crate::git::LocalRefname::new("gitbutler/integration", None);
}
pub const GITBUTLER_INTEGRATION_COMMIT_AUTHOR_NAME: &str = "GitButler";
pub const GITBUTLER_INTEGRATION_COMMIT_AUTHOR_EMAIL: &str = "gitbutler@gitbutler.com";

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,8 @@
pub mod commands {
use crate::error::Error;
use anyhow::{anyhow, Context};
use gitbutler_branch::Controller;
use gitbutler_branch::base::BaseBranch;
use gitbutler_branch::{Controller, NameConflitResolution, VirtualBranches};
use gitbutler_core::{
assets,
error::Code,
@ -10,8 +11,7 @@ pub mod commands {
types::ReferenceName,
virtual_branches::{
branch::{self, BranchId, BranchOwnershipClaims},
BaseBranch, NameConflitResolution, RemoteBranch, RemoteBranchData, RemoteBranchFile,
VirtualBranches,
RemoteBranch, RemoteBranchData, RemoteBranchFile,
},
};
use tauri::{AppHandle, Manager};
@ -50,7 +50,8 @@ pub mod commands {
.list_virtual_branches(&project)
.await?;
let proxy = handle.state::<assets::Proxy>();
let proxy =
gitbutler_branch::assets::Proxy::new(handle.state::<assets::Proxy>().inner().clone());
let branches = proxy.proxy_virtual_branches(branches).await;
Ok(VirtualBranches {
branches,
@ -118,7 +119,9 @@ pub mod commands {
.get_base_branch_data(&project)
.await
{
let proxy = handle.state::<assets::Proxy>();
let proxy = gitbutler_branch::assets::Proxy::new(
handle.state::<assets::Proxy>().inner().clone(),
);
let base_branch = proxy.proxy_base_branch(base_branch).await;
return Ok(Some(base_branch));
}
@ -141,10 +144,10 @@ pub mod commands {
.state::<Controller>()
.set_base_branch(&project, &branch_name)
.await?;
let base_branch = handle
.state::<assets::Proxy>()
.proxy_base_branch(base_branch)
.await;
let proxy =
gitbutler_branch::assets::Proxy::new(handle.state::<assets::Proxy>().inner().clone());
let base_branch = proxy.proxy_base_branch(base_branch).await;
// if they also sent a different push remote, set that too
if let Some(push_remote) = push_remote {

View File

@ -18,3 +18,4 @@ tempfile = "3.10.1"
keyring.workspace = true
serde_json = "1.0"
gitbutler-core = { path = "../gitbutler-core" }
gitbutler-branch = { path = "../gitbutler-branch" }

View File

@ -42,7 +42,7 @@ pub mod virtual_branches {
})
.expect("failed to write target");
virtual_branches::integration::update_gitbutler_integration(&vb_state, project_repository)
gitbutler_branch::integration::update_gitbutler_integration(&vb_state, project_repository)
.expect("failed to update integration");
Ok(())

View File

@ -1,8 +1,8 @@
use std::fmt::Display;
use std::path::PathBuf;
use gitbutler_branch::VirtualBranches;
use gitbutler_core::projects::ProjectId;
use gitbutler_core::virtual_branches;
/// An event for internal use, as merge between [super::file_monitor::Event] and [Action].
#[derive(Debug)]
@ -98,6 +98,6 @@ pub enum Change {
GitActivity(ProjectId),
VirtualBranches {
project_id: ProjectId,
virtual_branches: virtual_branches::VirtualBranches,
virtual_branches: VirtualBranches,
},
}

View File

@ -2,11 +2,11 @@ use std::path::PathBuf;
use std::sync::Arc;
use anyhow::{Context, Result};
use gitbutler_branch::VirtualBranches;
use gitbutler_core::error::Marker;
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};
use tracing::instrument;
@ -25,7 +25,7 @@ pub struct Handler {
projects: projects::Controller,
users: users::Controller,
vbranch_controller: gitbutler_branch::Controller,
assets_proxy: assets::Proxy,
assets_proxy: gitbutler_branch::assets::Proxy,
/// A function to send events - decoupled from app-handle for testing purposes.
#[allow(clippy::type_complexity)]
@ -46,7 +46,7 @@ impl Handler {
projects,
users,
vbranch_controller,
assets_proxy,
assets_proxy: gitbutler_branch::assets::Proxy::new(assets_proxy),
send_event: Arc::new(send_event),
}
}