Skip to content
Merged
388 changes: 199 additions & 189 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ resolver = "2"
[workspace.dependencies]
bstr = "1.11.0"
# Add the `tracing` or `tracing-detail` features to see more of gitoxide in the logs. Useful to see which programs it invokes.
gix = { version = "0.68.0", default-features = false, features = [] }
gix = { git = "https://github.com/GitoxideLabs/gitoxide", rev = "520c832cfcfb34eb7617be55ebe2719ab35595fd", default-features = false, features = [] }
git2 = { version = "0.19.0", features = [
"vendored-openssl",
"vendored-libgit2",
Expand Down
4 changes: 2 additions & 2 deletions crates/gitbutler-branch-actions/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ use anyhow::{anyhow, bail, Context, Result};
use gitbutler_branch::GITBUTLER_WORKSPACE_REFERENCE;
use gitbutler_command_context::CommandContext;
use gitbutler_error::error::Marker;
use gitbutler_oxidize::{git2_to_gix_object_id, gix_to_git2_oid};
use gitbutler_oxidize::{git2_to_gix_object_id, gix_to_git2_oid, GixRepositoryExt};
use gitbutler_project::FetchResult;
use gitbutler_reference::{Refname, RemoteRefname};
use gitbutler_repo::{GixRepositoryExt, LogUntil, RepositoryExt};
use gitbutler_repo::{LogUntil, RepositoryExt};
use gitbutler_repo_actions::RepoActionsExt;
use gitbutler_stack::{BranchOwnershipClaims, Stack, Target, VirtualBranchesHandle};
use serde::Serialize;
Expand Down
4 changes: 2 additions & 2 deletions crates/gitbutler-branch-actions/src/branch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ use gitbutler_branch::BranchIdentity;
use gitbutler_branch::ReferenceExtGix;
use gitbutler_command_context::CommandContext;
use gitbutler_diff::DiffByPathMap;
use gitbutler_oxidize::{git2_to_gix_object_id, gix_to_git2_oid};
use gitbutler_oxidize::{git2_to_gix_object_id, gix_to_git2_oid, GixRepositoryExt};
use gitbutler_project::access::WorktreeReadPermission;
use gitbutler_reference::normalize_branch_name;
use gitbutler_reference::RemoteRefname;
use gitbutler_repo::{GixRepositoryExt, RepositoryExt as _};
use gitbutler_repo::RepositoryExt as _;
use gitbutler_serde::BStringForFrontend;
use gitbutler_stack::{Stack as GitButlerBranch, StackId, Target};
use gix::object::tree::diff::Action;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
use super::BranchManager;
use crate::r#virtual as vbranch;
use crate::{
conflicts::RepoConflictsExt, hunk::VirtualBranchHunk, integration::update_workspace_commit,
VirtualBranchesExt,
};
use anyhow::{anyhow, bail, Context, Result};
use gitbutler_branch::BranchCreateRequest;
use gitbutler_branch::{self, dedup};
use gitbutler_cherry_pick::RepositoryExt as _;
use gitbutler_commit::{commit_ext::CommitExt, commit_headers::HasCommitHeaders};
use gitbutler_error::error::Marker;
use gitbutler_oplog::SnapshotExt;
use gitbutler_oxidize::GixRepositoryExt;
use gitbutler_project::access::WorktreeWritePermission;
use gitbutler_reference::{Refname, RemoteRefname};
use gitbutler_repo::GixRepositoryExt;
use gitbutler_repo::{
rebase::{cherry_rebase_group, gitbutler_merge_commits},
LogUntil, RepositoryExt,
Expand All @@ -19,12 +24,6 @@ use gitbutler_time::time::now_since_unix_epoch_ms;
use gitbutler_workspace::checkout_branch_trees;
use tracing::instrument;

use super::BranchManager;
use crate::{
conflicts::RepoConflictsExt, hunk::VirtualBranchHunk, integration::update_workspace_commit,
VirtualBranchesExt,
};

impl BranchManager<'_> {
#[instrument(level = tracing::Level::DEBUG, skip(self, perm), err(Debug))]
pub fn create_virtual_branch(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@ use git2::Commit;
use gitbutler_branch::BranchExt;
use gitbutler_commit::commit_headers::CommitHeadersV2;
use gitbutler_oplog::SnapshotExt;
use gitbutler_oxidize::git2_to_gix_object_id;
use gitbutler_oxidize::gix_to_git2_oid;
use gitbutler_oxidize::{git2_to_gix_object_id, GixRepositoryExt};
use gitbutler_project::access::WorktreeWritePermission;
use gitbutler_reference::{normalize_branch_name, ReferenceName, Refname};
use gitbutler_repo::GixRepositoryExt;
use gitbutler_repo::RepositoryExt;
use gitbutler_repo::SignaturePurpose;
use gitbutler_repo_actions::RepoActionsExt;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1083,7 +1083,7 @@ mod test {
fn hard_reset_to_externally_amended_commit() {
let test_repository = TestingRepository::open();

let base_commit = dbg!(test_repository.commit_tree(None, &[]));
let base_commit = test_repository.commit_tree(None, &[]);
let local_a = test_repository.commit_tree_with_message(
Some(&base_commit),
"A",
Expand Down Expand Up @@ -1145,7 +1145,7 @@ mod test {
fn hard_reset_to_externally_removed_commit() {
let test_repository = TestingRepository::open();

let base_commit = dbg!(test_repository.commit_tree(None, &[]));
let base_commit = test_repository.commit_tree(None, &[]);
let local_a = test_repository.commit_tree_with_message(
Some(&base_commit),
"A",
Expand Down Expand Up @@ -1212,7 +1212,7 @@ mod test {
fn hard_reset_to_externally_amended_branch() {
let test_repository = TestingRepository::open();

let base_commit = dbg!(test_repository.commit_tree(None, &[]));
let base_commit = test_repository.commit_tree(None, &[]);
let local_a = test_repository.commit_tree_with_message(
Some(&base_commit),
"A",
Expand Down
4 changes: 2 additions & 2 deletions crates/gitbutler-branch-actions/src/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ use gitbutler_command_context::CommandContext;
use gitbutler_commit::commit_ext::CommitExt;
use gitbutler_error::error::Marker;
use gitbutler_operating_modes::OPEN_WORKSPACE_REFS;
use gitbutler_oxidize::{git2_to_gix_object_id, gix_to_git2_oid};
use gitbutler_oxidize::{git2_to_gix_object_id, gix_to_git2_oid, GixRepositoryExt};
use gitbutler_project::access::WorktreeWritePermission;
use gitbutler_repo::{GixRepositoryExt, SignaturePurpose};
use gitbutler_repo::SignaturePurpose;
use gitbutler_repo::{LogUntil, RepositoryExt};
use gitbutler_stack::{Stack, VirtualBranchesHandle};
use tracing::instrument;
Expand Down
4 changes: 2 additions & 2 deletions crates/gitbutler-branch-actions/src/upstream_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ use anyhow::{anyhow, bail, Context, Result};
use gitbutler_cherry_pick::RepositoryExt;
use gitbutler_command_context::CommandContext;
use gitbutler_commit::commit_ext::CommitExt as _;
use gitbutler_oxidize::{git2_to_gix_object_id, gix_to_git2_oid};
use gitbutler_oxidize::{git2_to_gix_object_id, gix_to_git2_oid, GixRepositoryExt};
use gitbutler_project::access::WorktreeWritePermission;
use gitbutler_repo::RepositoryExt as _;
use gitbutler_repo::{
rebase::{cherry_rebase_group, gitbutler_merge_commits},
GixRepositoryExt, LogUntil,
LogUntil,
};
use gitbutler_repo_actions::RepoActionsExt as _;
use gitbutler_stack::stack_context::StackContext;
Expand Down
6 changes: 4 additions & 2 deletions crates/gitbutler-branch-actions/src/virtual.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ use gitbutler_diff::{trees, GitHunk, Hunk};
use gitbutler_error::error::Code;
use gitbutler_hunk_dependency::RangeCalculationError;
use gitbutler_operating_modes::assure_open_workspace_mode;
use gitbutler_oxidize::{git2_signature_to_gix_signature, git2_to_gix_object_id, gix_to_git2_oid};
use gitbutler_oxidize::{
git2_signature_to_gix_signature, git2_to_gix_object_id, gix_to_git2_oid, GixRepositoryExt,
};
use gitbutler_project::access::WorktreeWritePermission;
use gitbutler_reference::{normalize_branch_name, Refname, RemoteRefname};
use gitbutler_repo::{
rebase::{cherry_rebase, cherry_rebase_group},
GixRepositoryExt, LogUntil, RepositoryExt,
LogUntil, RepositoryExt,
};
use gitbutler_repo_actions::RepoActionsExt;
use gitbutler_stack::{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ fn detect_integrated_commits() {
.into_iter()
.find(|b| b.id == branch1_id)
.unwrap();
repository.merge(&branch.upstream.as_ref().unwrap().name);
repository
.merge(&branch.upstream.as_ref().unwrap().name)
.unwrap();
repository.fetch();
}

Expand Down
11 changes: 1 addition & 10 deletions crates/gitbutler-branch/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,18 @@ version = "0.0.0"
edition = "2021"
authors = ["GitButler <[email protected]>"]
publish = false
autotests = false

[dependencies]
anyhow = "1.0.93"
git2.workspace = true
gix = { workspace = true, features = [] }
gitbutler-reference.workspace = true
gitbutler-serde.workspace = true
gitbutler-id.workspace = true
gitbutler-error.workspace = true
gitbutler-fs.workspace = true
gitbutler-diff.workspace = true
gitbutler-oxidize.workspace = true
gitbutler-time.workspace = true
gitbutler-stack.workspace = true
itertools = "0.13"
toml.workspace = true
serde = { workspace = true, features = ["std"] }
bstr.workspace = true
md5 = "0.7.0"
hex = "0.4.3"
tracing.workspace = true
lazy_static = "1.4.0"

[[test]]
Expand Down
2 changes: 2 additions & 0 deletions crates/gitbutler-cherry-pick/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ publish = false
[dependencies]
gitbutler-commit.workspace = true
git2.workspace = true
gitbutler-oxidize.workspace = true
gix.workspace = true
anyhow.workspace = true
123 changes: 77 additions & 46 deletions crates/gitbutler-cherry-pick/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
// tree_writer.insert(".conflict-side-0", side0.id(), 0o040000)?;
// tree_writer.insert(".conflict-side-1", side1.id(), 0o040000)?;
// tree_writer.insert(".conflict-base-0", base_tree.id(), 0o040000)?;
// tree_writer.insert(".auto-resolution", resolved_tree_id, 0o040000)?;
// tree_writer.insert(".conflict-files", conflicted_files_blob, 0o100644)?;

use std::ops::Deref;

use anyhow::Context;
use git2::MergeOptions;
use anyhow::{Context, Result};
use gitbutler_commit::commit_ext::CommitExt;
use gitbutler_oxidize::git2_to_gix_object_id;

#[derive(Default)]
pub enum ConflictedTreeKey {
Expand Down Expand Up @@ -40,68 +34,105 @@ impl Deref for ConflictedTreeKey {
}

pub trait RepositoryExt {
fn cherry_pick_gitbutler(
&self,
/// Find the real tree of a commit, which is the tree of the commit if it's not in a conflicted state
/// or the tree according to `side` if it is conflicted.
///
/// Unless you want to find a particular side, you likely want to pass Default::default()
/// as the [`side`](ConflictedTreeKey) which will give the automatically resolved resolution
fn find_real_tree(&self, commit: &git2::Commit, side: ConflictedTreeKey) -> Result<git2::Tree>;
}

pub trait GixRepositoryExt {
/// Cherry-pick, but understands GitButler conflicted states.
/// Note that it will automatically resolve conflicts in *our* favor, so any tree produced
/// here can be used.
///
/// This method *should* always be used in favour of native functions.
fn cherry_pick_gitbutler<'repo>(
&'repo self,
head: &git2::Commit,
to_rebase: &git2::Commit,
merge_options: Option<&MergeOptions>,
) -> Result<git2::Index, anyhow::Error>;
fn find_real_tree(
&self,
commit: &git2::Commit,
) -> Result<gix::merge::tree::Outcome<'repo>>;

/// Find the real tree of a commit, which is the tree of the commit if it's not in a conflicted state
/// or the tree according to `side` if it is conflicted.
///
/// Unless you want to find a particular side, you likely want to pass Default::default()
/// as the [`side`](ConflictedTreeKey) which will give the automatically resolved resolution
fn find_real_tree<'repo>(
&'repo self,
commit_id: &gix::oid,
side: ConflictedTreeKey,
) -> Result<git2::Tree, anyhow::Error>;
) -> Result<gix::Id<'repo>>;
}

impl RepositoryExt for git2::Repository {
/// cherry-pick, but understands GitButler conflicted states
///
/// cherry_pick_gitbutler should always be used in favour of libgit2 or gitoxide
/// cherry pick functions
fn cherry_pick_gitbutler(
&self,
fn find_real_tree(&self, commit: &git2::Commit, side: ConflictedTreeKey) -> Result<git2::Tree> {
let tree = commit.tree()?;
if commit.is_conflicted() {
let conflicted_side = tree
.get_name(&side)
.context("Failed to get conflicted side of commit")?;
self.find_tree(conflicted_side.id())
.context("failed to find subtree")
} else {
self.find_tree(tree.id()).context("failed to find subtree")
}
}
}

impl GixRepositoryExt for gix::Repository {
fn cherry_pick_gitbutler<'repo>(
&'repo self,
head: &git2::Commit,
to_rebase: &git2::Commit,
merge_options: Option<&MergeOptions>,
) -> Result<git2::Index, anyhow::Error> {
) -> Result<gix::merge::tree::Outcome<'repo>> {
// we need to do a manual 3-way patch merge
// find the base, which is the parent of to_rebase
let base = if to_rebase.is_conflicted() {
// Use to_rebase's recorded base
self.find_real_tree(to_rebase, ConflictedTreeKey::Base)?
self.find_real_tree(
&git2_to_gix_object_id(to_rebase.id()),
ConflictedTreeKey::Base,
)?
} else {
let base_commit = to_rebase.parent(0)?;
// Use the parent's auto-resolution
self.find_real_tree(&base_commit, Default::default())?
self.find_real_tree(&git2_to_gix_object_id(base_commit.id()), Default::default())?
};
// Get the auto-resolution
let ours = self.find_real_tree(head, Default::default())?;
let ours = self.find_real_tree(&git2_to_gix_object_id(head.id()), Default::default())?;
// Get the original theirs
let thiers = self.find_real_tree(to_rebase, ConflictedTreeKey::Theirs)?;
let theirs = self.find_real_tree(
&git2_to_gix_object_id(to_rebase.id()),
ConflictedTreeKey::Theirs,
)?;

self.merge_trees(&base, &ours, &thiers, merge_options)
.context("failed to merge trees for cherry pick")
use gitbutler_oxidize::GixRepositoryExt;
self.merge_trees(
base,
ours,
theirs,
self.default_merge_labels(),
self.merge_options_force_ours()?,
)
.context("failed to merge trees for cherry pick")
}

/// Find the real tree of a commit, which is the tree of the commit if it's not in a conflicted state
/// or the parent parent tree if it is in a conflicted state
///
/// Unless you want to find a particular side, you likly want to pass Default::default()
/// as the ConfclitedTreeKey which will give the automatically resolved resolution
fn find_real_tree(
&self,
commit: &git2::Commit,
fn find_real_tree<'repo>(
&'repo self,
commit_id: &gix::oid,
side: ConflictedTreeKey,
) -> Result<git2::Tree, anyhow::Error> {
let tree = commit.tree()?;
if commit.is_conflicted() {
) -> Result<gix::Id<'repo>> {
let commit = self.find_commit(commit_id)?;
Ok(if commit.is_conflicted() {
let tree = commit.tree()?;
let conflicted_side = tree
.get_name(&side)
.find_entry(&*side)
.context("Failed to get conflicted side of commit")?;
self.find_tree(conflicted_side.id())
.context("failed to find subtree")
conflicted_side.id()
} else {
self.find_tree(tree.id()).context("failed to find subtree")
}
commit.tree_id()?
})
}
}
1 change: 1 addition & 0 deletions crates/gitbutler-commit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ publish = false

[dependencies]
git2.workspace = true
gix.workspace = true
bstr.workspace = true
uuid.workspace = true
23 changes: 23 additions & 0 deletions crates/gitbutler-commit/src/commit_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,29 @@ impl CommitExt for git2::Commit<'_> {
}
}

impl CommitExt for gix::Commit<'_> {
fn message_bstr(&self) -> &BStr {
self.message_raw()
.expect("valid commit that can be parsed: TODO - allow it to return errors?")
}

fn change_id(&self) -> Option<String> {
self.gitbutler_headers().map(|headers| headers.change_id)
}

fn is_signed(&self) -> bool {
self.decode().map_or(false, |decoded| {
decoded.extra_headers().pgp_signature().is_some()
})
}

fn is_conflicted(&self) -> bool {
self.gitbutler_headers()
.and_then(|headers| headers.conflicted.map(|conflicted| conflicted > 0))
.unwrap_or(false)
}
}

fn contains<'a, I>(iter: I, item: &git2::Commit<'a>) -> bool
where
I: IntoIterator<Item = git2::Commit<'a>>,
Expand Down
Loading
Loading