Skip to content

Commit 85bf792

Browse files
committed
Make gix::Repository extension available in oxidize.
It has minimal dependencies and can be used everywhere.
1 parent 398b2aa commit 85bf792

File tree

16 files changed

+160
-150
lines changed

16 files changed

+160
-150
lines changed

crates/gitbutler-branch-actions/src/base.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ use anyhow::{anyhow, bail, Context, Result};
1111
use gitbutler_branch::GITBUTLER_WORKSPACE_REFERENCE;
1212
use gitbutler_command_context::CommandContext;
1313
use gitbutler_error::error::Marker;
14-
use gitbutler_oxidize::{git2_to_gix_object_id, gix_to_git2_oid};
14+
use gitbutler_oxidize::{git2_to_gix_object_id, gix_to_git2_oid, GixRepositoryExt};
1515
use gitbutler_project::FetchResult;
1616
use gitbutler_reference::{Refname, RemoteRefname};
17-
use gitbutler_repo::{GixRepositoryExt, LogUntil, RepositoryExt};
17+
use gitbutler_repo::{LogUntil, RepositoryExt};
1818
use gitbutler_repo_actions::RepoActionsExt;
1919
use gitbutler_stack::{BranchOwnershipClaims, Stack, Target, VirtualBranchesHandle};
2020
use serde::Serialize;

crates/gitbutler-branch-actions/src/branch.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ use gitbutler_branch::BranchIdentity;
77
use gitbutler_branch::ReferenceExtGix;
88
use gitbutler_command_context::CommandContext;
99
use gitbutler_diff::DiffByPathMap;
10-
use gitbutler_oxidize::{git2_to_gix_object_id, gix_to_git2_oid};
10+
use gitbutler_oxidize::{git2_to_gix_object_id, gix_to_git2_oid, GixRepositoryExt};
1111
use gitbutler_project::access::WorktreeReadPermission;
1212
use gitbutler_reference::normalize_branch_name;
1313
use gitbutler_reference::RemoteRefname;
14-
use gitbutler_repo::{GixRepositoryExt, RepositoryExt as _};
14+
use gitbutler_repo::RepositoryExt as _;
1515
use gitbutler_serde::BStringForFrontend;
1616
use gitbutler_stack::{Stack as GitButlerBranch, StackId, Target};
1717
use gix::object::tree::diff::Action;

crates/gitbutler-branch-actions/src/branch_manager/branch_creation.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1+
use super::BranchManager;
12
use crate::r#virtual as vbranch;
3+
use crate::{
4+
conflicts::RepoConflictsExt, hunk::VirtualBranchHunk, integration::update_workspace_commit,
5+
VirtualBranchesExt,
6+
};
27
use anyhow::{anyhow, bail, Context, Result};
38
use gitbutler_branch::BranchCreateRequest;
49
use gitbutler_branch::{self, dedup};
510
use gitbutler_cherry_pick::RepositoryExt as _;
611
use gitbutler_commit::{commit_ext::CommitExt, commit_headers::HasCommitHeaders};
712
use gitbutler_error::error::Marker;
813
use gitbutler_oplog::SnapshotExt;
14+
use gitbutler_oxidize::GixRepositoryExt;
915
use gitbutler_project::access::WorktreeWritePermission;
1016
use gitbutler_reference::{Refname, RemoteRefname};
11-
use gitbutler_repo::GixRepositoryExt;
1217
use gitbutler_repo::{
1318
rebase::{cherry_rebase_group, gitbutler_merge_commits},
1419
LogUntil, RepositoryExt,
@@ -19,12 +24,6 @@ use gitbutler_time::time::now_since_unix_epoch_ms;
1924
use gitbutler_workspace::checkout_branch_trees;
2025
use tracing::instrument;
2126

22-
use super::BranchManager;
23-
use crate::{
24-
conflicts::RepoConflictsExt, hunk::VirtualBranchHunk, integration::update_workspace_commit,
25-
VirtualBranchesExt,
26-
};
27-
2827
impl BranchManager<'_> {
2928
#[instrument(level = tracing::Level::DEBUG, skip(self, perm), err(Debug))]
3029
pub fn create_virtual_branch(

crates/gitbutler-branch-actions/src/branch_manager/branch_removal.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@ use git2::Commit;
55
use gitbutler_branch::BranchExt;
66
use gitbutler_commit::commit_headers::CommitHeadersV2;
77
use gitbutler_oplog::SnapshotExt;
8-
use gitbutler_oxidize::git2_to_gix_object_id;
98
use gitbutler_oxidize::gix_to_git2_oid;
9+
use gitbutler_oxidize::{git2_to_gix_object_id, GixRepositoryExt};
1010
use gitbutler_project::access::WorktreeWritePermission;
1111
use gitbutler_reference::{normalize_branch_name, ReferenceName, Refname};
12-
use gitbutler_repo::GixRepositoryExt;
1312
use gitbutler_repo::RepositoryExt;
1413
use gitbutler_repo::SignaturePurpose;
1514
use gitbutler_repo_actions::RepoActionsExt;

crates/gitbutler-branch-actions/src/integration.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ use gitbutler_command_context::CommandContext;
99
use gitbutler_commit::commit_ext::CommitExt;
1010
use gitbutler_error::error::Marker;
1111
use gitbutler_operating_modes::OPEN_WORKSPACE_REFS;
12-
use gitbutler_oxidize::{git2_to_gix_object_id, gix_to_git2_oid};
12+
use gitbutler_oxidize::{git2_to_gix_object_id, gix_to_git2_oid, GixRepositoryExt};
1313
use gitbutler_project::access::WorktreeWritePermission;
14-
use gitbutler_repo::{GixRepositoryExt, SignaturePurpose};
14+
use gitbutler_repo::SignaturePurpose;
1515
use gitbutler_repo::{LogUntil, RepositoryExt};
1616
use gitbutler_stack::{Stack, VirtualBranchesHandle};
1717
use tracing::instrument;

crates/gitbutler-branch-actions/src/upstream_integration.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ use anyhow::{anyhow, bail, Result};
44
use gitbutler_cherry_pick::RepositoryExt;
55
use gitbutler_command_context::CommandContext;
66
use gitbutler_commit::commit_ext::CommitExt as _;
7-
use gitbutler_oxidize::{git2_to_gix_object_id, gix_to_git2_oid};
7+
use gitbutler_oxidize::{git2_to_gix_object_id, gix_to_git2_oid, GixRepositoryExt};
88
use gitbutler_project::access::WorktreeWritePermission;
99
use gitbutler_repo::RepositoryExt as _;
1010
use gitbutler_repo::{
1111
rebase::{cherry_rebase_group, gitbutler_merge_commits},
12-
GixRepositoryExt, LogUntil,
12+
LogUntil,
1313
};
1414
use gitbutler_repo_actions::RepoActionsExt as _;
1515
use gitbutler_stack::stack_context::StackContext;

crates/gitbutler-branch-actions/src/virtual.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@ use gitbutler_commit::{commit_ext::CommitExt, commit_headers::HasCommitHeaders};
2121
use gitbutler_diff::{trees, GitHunk, Hunk};
2222
use gitbutler_error::error::Code;
2323
use gitbutler_operating_modes::assure_open_workspace_mode;
24-
use gitbutler_oxidize::{git2_signature_to_gix_signature, git2_to_gix_object_id, gix_to_git2_oid};
24+
use gitbutler_oxidize::{
25+
git2_signature_to_gix_signature, git2_to_gix_object_id, gix_to_git2_oid, GixRepositoryExt,
26+
};
2527
use gitbutler_project::access::WorktreeWritePermission;
2628
use gitbutler_reference::{normalize_branch_name, Refname, RemoteRefname};
2729
use gitbutler_repo::{
2830
rebase::{cherry_rebase, cherry_rebase_group},
29-
GixRepositoryExt, LogUntil, RepositoryExt,
31+
LogUntil, RepositoryExt,
3032
};
3133
use gitbutler_repo_actions::RepoActionsExt;
3234
use gitbutler_stack::{

crates/gitbutler-cherry-pick/src/lib.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -108,18 +108,13 @@ impl GixRepositoryExt for gix::Repository {
108108
ConflictedTreeKey::Theirs,
109109
)?;
110110

111+
use gitbutler_oxidize::GixRepositoryExt;
111112
self.merge_trees(
112113
base,
113114
ours,
114115
theirs,
115-
gix::merge::blob::builtin_driver::text::Labels {
116-
ancestor: Some("base".into()),
117-
current: Some("ours".into()),
118-
other: Some("theirs".into()),
119-
},
120-
self.tree_merge_options()?
121-
.with_tree_favor(Some(gix::merge::tree::TreeFavor::Ours))
122-
.with_file_favor(Some(gix::merge::tree::FileFavor::Ours)),
116+
self.default_merge_labels(),
117+
self.merge_options_force_ours()?,
123118
)
124119
.context("failed to merge trees for cherry pick")
125120
}

crates/gitbutler-oplog/src/oplog.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ use anyhow::{anyhow, bail, Context, Result};
1515
use git2::FileMode;
1616
use gitbutler_command_context::RepositoryExtLite;
1717
use gitbutler_diff::{hunks_by_filepath, FileDiff};
18-
use gitbutler_oxidize::{git2_to_gix_object_id, gix_time_to_git2, gix_to_git2_oid};
18+
use gitbutler_oxidize::{
19+
git2_to_gix_object_id, gix_time_to_git2, gix_to_git2_oid, GixRepositoryExt,
20+
};
1921
use gitbutler_project::{
2022
access::{WorktreeReadPermission, WorktreeWritePermission},
2123
Project,
2224
};
25+
use gitbutler_repo::RepositoryExt;
2326
use gitbutler_repo::SignaturePurpose;
24-
use gitbutler_repo::{GixRepositoryExt, RepositoryExt};
2527
use gitbutler_stack::{Stack, VirtualBranchesHandle, VirtualBranchesState};
2628
use gix::bstr::ByteSlice;
2729
use gix::object::tree::diff::Change;

crates/gitbutler-oxidize/src/ext.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
use crate::git2_to_gix_object_id;
2+
use anyhow::{Context, Result};
3+
use gix::merge::tree::{Options, TreatAsUnresolved};
4+
5+
pub trait GixRepositoryExt: Sized {
6+
/// Configure the repository for diff operations between trees.
7+
/// This means it needs an object cache relative to the amount of files in the repository.
8+
fn for_tree_diffing(self) -> Result<Self>;
9+
10+
/// Returns `true` if the merge between `our_tree` and `their_tree` is free of conflicts.
11+
/// Conflicts entail content merges with conflict markers, or anything else that doesn't merge cleanly in the tree.
12+
///
13+
/// # Important
14+
///
15+
/// Make sure the repository is configured [`with_object_memory()`](gix::Repository::with_object_memory()).
16+
fn merges_cleanly_compat(
17+
&self,
18+
ancestor_tree: git2::Oid,
19+
our_tree: git2::Oid,
20+
their_tree: git2::Oid,
21+
) -> Result<bool>;
22+
23+
/// Just like the above, but with `gix` types.
24+
fn merges_cleanly(
25+
&self,
26+
ancestor_tree: gix::ObjectId,
27+
our_tree: gix::ObjectId,
28+
their_tree: gix::ObjectId,
29+
) -> Result<bool>;
30+
31+
/// Return default label names when merging trees.
32+
///
33+
/// Note that these should probably rather be branch names, but that's for another day.
34+
fn default_merge_labels(&self) -> gix::merge::blob::builtin_driver::text::Labels<'static> {
35+
gix::merge::blob::builtin_driver::text::Labels {
36+
ancestor: Some("base".into()),
37+
current: Some("ours".into()),
38+
other: Some("theirs".into()),
39+
}
40+
}
41+
42+
/// Tree merge options that enforce undecidable conflicts to be forcefully resolved
43+
/// to favor ours, both when dealing with content merges and with tree merges.
44+
fn merge_options_force_ours(&self) -> Result<gix::merge::tree::Options>;
45+
46+
/// Return options suitable for merging so that the merge stops immediately after the first conflict.
47+
/// It also returns the conflict kind to use when checking for unresolved conflicts.
48+
fn merge_options_fail_fast(
49+
&self,
50+
) -> Result<(
51+
gix::merge::tree::Options,
52+
gix::merge::tree::TreatAsUnresolved,
53+
)>;
54+
55+
/// Just like [`Self::merge_options_fail_fast()`], but additionally don't perform rename tracking.
56+
/// This is useful if the merge result isn't going to be used, and we are only interested in knowing
57+
/// if a merge would succeed.
58+
fn merge_options_no_rewrites_fail_fast(
59+
&self,
60+
) -> Result<(gix::merge::tree::Options, TreatAsUnresolved)>;
61+
}
62+
63+
impl GixRepositoryExt for gix::Repository {
64+
fn for_tree_diffing(mut self) -> anyhow::Result<Self> {
65+
let bytes = self.compute_object_cache_size_for_tree_diffs(&***self.index_or_empty()?);
66+
self.object_cache_size_if_unset(bytes);
67+
Ok(self)
68+
}
69+
70+
fn merges_cleanly_compat(
71+
&self,
72+
ancestor_tree: git2::Oid,
73+
our_tree: git2::Oid,
74+
their_tree: git2::Oid,
75+
) -> Result<bool> {
76+
self.merges_cleanly(
77+
git2_to_gix_object_id(ancestor_tree),
78+
git2_to_gix_object_id(our_tree),
79+
git2_to_gix_object_id(their_tree),
80+
)
81+
}
82+
83+
fn merges_cleanly(
84+
&self,
85+
ancestor_tree: gix::ObjectId,
86+
our_tree: gix::ObjectId,
87+
their_tree: gix::ObjectId,
88+
) -> Result<bool> {
89+
let (options, conflict_kind) = self.merge_options_no_rewrites_fail_fast()?;
90+
let merge_outcome = self
91+
.merge_trees(
92+
ancestor_tree,
93+
our_tree,
94+
their_tree,
95+
Default::default(),
96+
options,
97+
)
98+
.context("failed to merge trees")?;
99+
Ok(!merge_outcome.has_unresolved_conflicts(conflict_kind))
100+
}
101+
102+
fn merge_options_force_ours(&self) -> Result<Options> {
103+
Ok(self
104+
.tree_merge_options()?
105+
.with_tree_favor(Some(gix::merge::tree::TreeFavor::Ours))
106+
.with_file_favor(Some(gix::merge::tree::FileFavor::Ours)))
107+
}
108+
109+
fn merge_options_fail_fast(&self) -> Result<(gix::merge::tree::Options, TreatAsUnresolved)> {
110+
let conflict_kind = TreatAsUnresolved::forced_resolution();
111+
let options = self
112+
.tree_merge_options()?
113+
.with_fail_on_conflict(Some(conflict_kind));
114+
Ok((options, conflict_kind))
115+
}
116+
117+
fn merge_options_no_rewrites_fail_fast(
118+
&self,
119+
) -> Result<(gix::merge::tree::Options, TreatAsUnresolved)> {
120+
let (options, conflict_kind) = self.merge_options_fail_fast()?;
121+
Ok((options.with_rewrites(None), conflict_kind))
122+
}
123+
}

crates/gitbutler-oxidize/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ use anyhow::Context;
44
use gix::bstr::ByteSlice;
55
use std::borrow::Borrow;
66

7+
mod ext;
8+
pub use ext::GixRepositoryExt;
9+
710
pub fn gix_time_to_git2(time: gix::date::Time) -> git2::Time {
811
git2::Time::new(time.seconds, time.offset)
912
}

crates/gitbutler-repo/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ pub use commands::{FileInfo, RepoCommands};
55
pub use remote::GitRemote;
66

77
mod repository_ext;
8-
pub use repository_ext::{GixRepositoryExt, LogUntil, RepositoryExt};
8+
pub use repository_ext::{LogUntil, RepositoryExt};
99

1010
pub mod credentials;
1111

crates/gitbutler-repo/src/rebase.rs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use gitbutler_commit::{
99
commit_ext::CommitExt,
1010
commit_headers::{CommitHeadersV2, HasCommitHeaders},
1111
};
12-
use gitbutler_oxidize::{git2_to_gix_object_id, gix_to_git2_oid};
12+
use gitbutler_oxidize::{git2_to_gix_object_id, gix_to_git2_oid, GixRepositoryExt as _};
1313
use serde::{Deserialize, Serialize};
1414

1515
/// cherry-pick based rebase, which handles empty commits
@@ -325,15 +325,8 @@ pub fn gitbutler_merge_commits<'repository>(
325325
git2_to_gix_object_id(base_tree.id()),
326326
git2_to_gix_object_id(incoming_merge_tree.id()),
327327
git2_to_gix_object_id(target_merge_tree.id()),
328-
gix::merge::blob::builtin_driver::text::Labels {
329-
ancestor: Some("base".into()),
330-
current: Some("ours".into()),
331-
other: Some("theirs".into()),
332-
},
333-
gix_repo
334-
.tree_merge_options()?
335-
.with_tree_favor(Some(gix::merge::tree::TreeFavor::Ours))
336-
.with_file_favor(Some(gix::merge::tree::FileFavor::Ours)),
328+
gix_repo.default_merge_labels(),
329+
gix_repo.merge_options_force_ours()?,
337330
)?;
338331
let merged_tree_id = merge_result.tree.write()?;
339332

0 commit comments

Comments
 (0)