Skip to content

Commit 863e157

Browse files
committed
bring ref_info Commit into the right position
1 parent e0ff0ad commit 863e157

File tree

5 files changed

+200
-200
lines changed

5 files changed

+200
-200
lines changed

crates/but-workspace/src/changeset.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@
66
77
use crate::{
88
RefInfo,
9-
ui::{
10-
PushStatus,
11-
ref_info::{LocalCommit, LocalCommitRelation},
12-
},
9+
ref_info::{LocalCommit, LocalCommitRelation},
10+
ui::PushStatus,
1311
};
1412
use bstr::{BStr, BString, ByteSlice, ByteVec};
1513
use but_core::{ChangeState, commit::TreeKind};
@@ -93,7 +91,7 @@ impl RefInfo {
9391
repo,
9492
upstream_commits.iter().filter_map(|id| {
9593
but_core::Commit::from_id(id.attach(repo))
96-
.map(crate::ui::ref_info::Commit::from)
94+
.map(crate::ref_info::Commit::from)
9795
.ok()
9896
}),
9997
cost_info,
@@ -261,7 +259,7 @@ impl PushStatus {
261259

262260
fn changeset_identifier(
263261
repo: &gix::Repository,
264-
commit: Option<&crate::ui::ref_info::Commit>,
262+
commit: Option<&crate::ref_info::Commit>,
265263
elapsed: &mut Duration,
266264
) -> anyhow::Result<Option<Identifier>> {
267265
let Some(commit) = commit else {
@@ -295,7 +293,7 @@ enum ChangeId {
295293

296294
fn lookup_similar<'a>(
297295
map: &'a Identity,
298-
commit: &crate::ui::ref_info::Commit,
296+
commit: &crate::ref_info::Commit,
299297
expensive: Option<&Identifier>,
300298
change_id: ChangeId,
301299
) -> Option<&'a gix::ObjectId> {
@@ -311,7 +309,7 @@ fn lookup_similar<'a>(
311309
/// Returns the fully-loaded commits suitable to be passed to UI, to have better re-use.
312310
fn create_similarity_lut(
313311
repo: &gix::Repository,
314-
commits: impl Iterator<Item = impl Borrow<crate::ui::ref_info::Commit>>,
312+
commits: impl Iterator<Item = impl Borrow<crate::ref_info::Commit>>,
315313
(max_commits, num_tracked_files): (usize, usize),
316314
expensive: bool,
317315
) -> anyhow::Result<Identity> {
@@ -632,7 +630,7 @@ enum Identifier {
632630
ChangesetId(ChangesetID),
633631
}
634632

635-
fn commit_data_id(c: &crate::ui::ref_info::Commit) -> anyhow::Result<Identifier> {
633+
fn commit_data_id(c: &crate::ref_info::Commit) -> anyhow::Result<Identifier> {
636634
let mut hasher = gix::hash::hasher(gix::hash::Kind::Sha1);
637635

638636
let gix::actor::Signature {

crates/but-workspace/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ pub struct RefInfo {
222222
pub struct AncestorWorkspaceCommit {
223223
/// The commits along the first parent that are between the managed workspace reference and the managed workspace commit.
224224
/// The vec is never empty.
225-
pub commits_outside: Vec<ui::ref_info::Commit>,
225+
pub commits_outside: Vec<crate::ref_info::Commit>,
226226
/// The index of the segment that actually holds the managed workspace commit.
227227
pub segment_with_managed_commit: SegmentIndex,
228228
/// The index of the workspace commit within the `commits` array in its parent segment.

crates/but-workspace/src/ref_info.rs

Lines changed: 190 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,189 @@
22
// TODO: rename this module to `workspace`, make it private, and pub-use all content in the top-level, as we now literally
33
// get the workspace, while possibly processing it for use in the UI.
44

5-
use crate::ui::ref_info::{Commit, LocalCommit};
5+
use bstr::BString;
66
use but_core::ref_metadata;
77
use but_graph::SegmentIndex;
8+
use but_graph::projection::StackCommitFlags;
89
use gix::Repository;
910
use std::borrow::Cow;
11+
use std::ops::{Deref, DerefMut};
12+
13+
/// A commit with must useful information extracted from the Git commit itself.
14+
///
15+
/// Note that additional information can be computed and placed in the [`LocalCommit`] and [`RemoteCommit`]
16+
#[derive(Clone, Eq, PartialEq)]
17+
pub struct Commit {
18+
/// The hash of the commit.
19+
pub id: gix::ObjectId,
20+
/// The IDs of the parent commits, but may be empty if this is the first commit.
21+
pub parent_ids: Vec<gix::ObjectId>,
22+
/// The hash of the tree associated with the object.
23+
pub tree_id: gix::ObjectId,
24+
/// The complete message, verbatim.
25+
pub message: BString,
26+
/// The signature at which the commit was authored.
27+
pub author: gix::actor::Signature,
28+
/// The references pointing to this commit, even after dereferencing tag objects.
29+
/// These can be names of tags and branches.
30+
pub refs: Vec<gix::refs::FullName>,
31+
/// Additional properties to help classify this commit.
32+
pub flags: StackCommitFlags,
33+
/// Whether the commit is in a conflicted state, a GitButler concept.
34+
/// GitButler will perform rebasing/reordering etc. without interruptions and flag commits as conflicted if needed.
35+
/// Conflicts are resolved via the Edit Mode mechanism.
36+
///
37+
/// Note that even though GitButler won't push branches with conflicts, the user can still push such branches at will.
38+
pub has_conflicts: bool,
39+
/// The GitButler assigned change-id that we hold on to for convenience to avoid duplicate decoding of commits
40+
/// when trying to associate remote commits with local ones.
41+
pub change_id: Option<but_core::commit::ChangeId>,
42+
}
43+
44+
impl std::fmt::Debug for Commit {
45+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46+
write!(
47+
f,
48+
"Commit({hash}, {msg:?}{flags})",
49+
hash = self.id.to_hex_with_len(7),
50+
msg = self.message,
51+
flags = self.flags.debug_string()
52+
)
53+
}
54+
}
55+
56+
impl From<but_core::Commit<'_>> for Commit {
57+
fn from(value: but_core::Commit<'_>) -> Self {
58+
let has_conflicts = value.is_conflicted();
59+
let change_id = value.headers().map(|hdr| hdr.change_id);
60+
Commit {
61+
id: value.id.into(),
62+
tree_id: value.tree,
63+
parent_ids: value.parents.iter().cloned().collect(),
64+
message: value.inner.message,
65+
author: value.inner.author,
66+
has_conflicts,
67+
change_id,
68+
refs: Vec::new(),
69+
flags: StackCommitFlags::empty(),
70+
}
71+
}
72+
}
73+
74+
impl Commit {
75+
/// A special constructor for very specific case.
76+
pub(crate) fn from_commit_ahead_of_workspace_commit(
77+
commit: gix::objs::Commit,
78+
graph_commit: &but_graph::Commit,
79+
) -> Self {
80+
let hdr = but_core::commit::HeadersV2::try_from_commit(&commit);
81+
Commit {
82+
id: graph_commit.id,
83+
parent_ids: commit.parents.into_iter().collect(),
84+
tree_id: commit.tree,
85+
message: commit.message,
86+
has_conflicts: hdr.as_ref().is_some_and(|hdr| hdr.is_conflicted()),
87+
author: commit
88+
.author
89+
.to_ref(&mut gix::date::parse::TimeBuf::default())
90+
.into(),
91+
refs: graph_commit.refs.clone(),
92+
flags: graph_commit.flags.into(),
93+
change_id: hdr.map(|hdr| hdr.change_id),
94+
}
95+
}
96+
}
97+
98+
/// A commit that is reachable through the *local tracking branch*, with additional, computed information.
99+
#[derive(Clone, Eq, PartialEq)]
100+
pub struct LocalCommit {
101+
/// The simple commit.
102+
pub inner: Commit,
103+
/// Provide additional information on how this commit relates to other points of reference, like its remote branch,
104+
/// or the target branch to integrate with.
105+
pub relation: LocalCommitRelation,
106+
}
107+
108+
impl std::fmt::Debug for LocalCommit {
109+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110+
let refs = self
111+
.refs
112+
.iter()
113+
.map(|rn| format!("►{}", rn.shorten()))
114+
.collect::<Vec<_>>()
115+
.join(", ");
116+
write!(
117+
f,
118+
"LocalCommit({conflict}{hash}, {msg:?}, {relation}{refs})",
119+
conflict = if self.has_conflicts { "💥" } else { "" },
120+
hash = self.id.to_hex_with_len(7),
121+
msg = self.message,
122+
relation = self.relation.display(self.id),
123+
refs = if refs.is_empty() {
124+
"".to_string()
125+
} else {
126+
format!(", {refs}")
127+
}
128+
)
129+
}
130+
}
131+
132+
/// The state of the [local commit](LocalCommit) in relation to its remote tracking branch or its integration branch.
133+
#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
134+
pub enum LocalCommitRelation {
135+
/// The commit is only local
136+
#[default]
137+
LocalOnly,
138+
/// The commit is also present in the remote tracking branch.
139+
///
140+
/// This is the case if:
141+
/// - The commit has been pushed to the remote
142+
/// - The commit has been copied from a remote commit (when applying a remote branch)
143+
///
144+
/// This variant carries the remote commit id.
145+
/// The `remote_commit_id` may be the same as the `id` or it may be different if the local commit has been rebased
146+
/// or updated in another way.
147+
LocalAndRemote(gix::ObjectId),
148+
/// The commit is considered integrated, using the given hash as the commit that contains this one.
149+
/// Note that this can be a 1:1 relation in case of rebased commits, or an N:1 relation in case of squash commits.
150+
/// If the id of this value is the same as the owning commit, this means it's included in the ancestry
151+
/// of the target branch.
152+
/// This should happen when the commit or the contents of this commit is already part of the base.
153+
Integrated(gix::ObjectId),
154+
}
155+
156+
impl LocalCommitRelation {
157+
/// Convert this relation into something displaying, mainly for debugging.
158+
pub fn display(&self, id: gix::ObjectId) -> Cow<'static, str> {
159+
match self {
160+
LocalCommitRelation::LocalOnly => Cow::Borrowed("local"),
161+
LocalCommitRelation::LocalAndRemote(remote_id) => {
162+
if *remote_id == id {
163+
"local/remote(identity)".into()
164+
} else {
165+
format!("local/remote({})", remote_id.to_hex_with_len(7)).into()
166+
}
167+
}
168+
LocalCommitRelation::Integrated(id) => {
169+
format!("integrated({})", id.to_hex_with_len(7)).into()
170+
}
171+
}
172+
}
173+
}
174+
175+
impl Deref for LocalCommit {
176+
type Target = Commit;
177+
178+
fn deref(&self) -> &Self::Target {
179+
&self.inner
180+
}
181+
}
182+
183+
impl DerefMut for LocalCommit {
184+
fn deref_mut(&mut self) -> &mut Self::Target {
185+
&mut self.inner
186+
}
187+
}
10188

11189
/// Additional workspace functionality that can't easily be implemented in `but-graph`.
12190
pub trait WorkspaceExt {
@@ -156,8 +334,8 @@ impl std::fmt::Debug for Segment {
156334
}
157335

158336
pub(crate) mod function {
337+
use crate::ref_info::{LocalCommit, LocalCommitRelation};
159338
use crate::ui::PushStatus;
160-
use crate::ui::ref_info::{self, LocalCommit, LocalCommitRelation};
161339
use crate::{AncestorWorkspaceCommit, RefInfo, WorkspaceCommit, branch};
162340
use anyhow::{Context, bail};
163341
use but_core::ref_metadata::ValueInfo;
@@ -231,10 +409,12 @@ pub(crate) mod function {
231409
sidx_and_cidx = Some((s.id, cidx));
232410
return true;
233411
}
234-
commits_outside.push(ref_info::Commit::from_commit_ahead_of_workspace_commit(
235-
commit.inner,
236-
graph_commit,
237-
));
412+
commits_outside.push(
413+
crate::ref_info::Commit::from_commit_ahead_of_workspace_commit(
414+
commit.inner,
415+
graph_commit,
416+
),
417+
);
238418
}
239419
false
240420
});
@@ -362,16 +542,15 @@ pub(crate) mod function {
362542
let commits_on_remote: Vec<_> = commits_on_remote
363543
.into_iter()
364544
.map(|c| {
365-
but_core::Commit::from_id(c.id.attach(repo))
366-
.map(crate::ui::ref_info::Commit::from)
545+
but_core::Commit::from_id(c.id.attach(repo)).map(crate::ref_info::Commit::from)
367546
})
368547
.collect::<Result<_, _>>()?;
369548
let commits_outside = commits_outside
370549
.map(|v| {
371550
v.into_iter()
372551
.map(|c| {
373552
but_core::Commit::from_id(c.id.attach(repo))
374-
.map(crate::ui::ref_info::Commit::from)
553+
.map(crate::ref_info::Commit::from)
375554
})
376555
.collect::<Result<Vec<_>, _>>()
377556
})
@@ -402,7 +581,8 @@ pub(crate) mod function {
402581
refs,
403582
} = c;
404583
use but_graph::projection::StackCommitFlags;
405-
let mut inner: ref_info::Commit = but_core::Commit::from_id(id.attach(repo))?.into();
584+
let mut inner: crate::ref_info::Commit =
585+
but_core::Commit::from_id(id.attach(repo))?.into();
406586
inner.refs = refs;
407587
inner.flags = flags;
408588
Ok(LocalCommit {

crates/but-workspace/src/stacks.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::integrated::IsCommitIntegrated;
22
use crate::ref_info::Segment;
3-
use crate::ui::ref_info::{Commit, LocalCommit, LocalCommitRelation};
3+
use crate::ref_info::{Commit, LocalCommit, LocalCommitRelation};
44
use crate::ui::{CommitState, PushStatus, StackDetails};
55
use crate::{RefInfo, StacksFilter, branch, head_info, ref_info, state_handle, ui};
66
use anyhow::{Context, bail};

0 commit comments

Comments
 (0)