mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-08-17 00:00:31 +03:00
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:
commit
832ef485c4
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -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",
|
||||
|
@ -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"
|
||||
|
71
crates/gitbutler-branch/src/assets.rs
Normal file
71
crates/gitbutler-branch/src/assets.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
@ -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(),
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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),
|
@ -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;
|
||||
|
@ -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)?;
|
@ -1 +1,3 @@
|
||||
mod virtual_branches;
|
||||
|
||||
mod extra;
|
||||
|
2141
crates/gitbutler-branch/tests/extra/mod.rs
Normal file
2141
crates/gitbutler-branch/tests/extra/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -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::*;
|
||||
|
||||
|
@ -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)]
|
||||
|
@ -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| {
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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::{
|
||||
|
@ -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
|
||||
|
28
crates/gitbutler-core/src/virtual_branches/author.rs
Normal file
28
crates/gitbutler-core/src/virtual_branches/author.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
@ -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
@ -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 {
|
||||
|
@ -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" }
|
||||
|
@ -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(())
|
||||
|
@ -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,
|
||||
},
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user