Skip to content

Commit 78a1c35

Browse files
committed
Port the most important tests for asserting cherry-picking behaviour.
1 parent f51d556 commit 78a1c35

File tree

5 files changed

+323
-59
lines changed

5 files changed

+323
-59
lines changed

crates/but-core/src/commit.rs

Lines changed: 55 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,59 @@ pub struct HeadersV2 {
2222
pub conflicted: Option<u64>,
2323
}
2424

25+
impl HeadersV2 {
26+
/// Extract header information from the given `commit`, or return `None` if not present.
27+
pub fn try_from_commit(commit: &gix::objs::Commit) -> Option<Self> {
28+
if let Some(header) = commit.extra_headers().find(HEADERS_VERSION_FIELD) {
29+
let version = header.to_owned();
30+
31+
if version == HEADERS_VERSION {
32+
let change_id = commit.extra_headers().find(HEADERS_CHANGE_ID_FIELD)?;
33+
let change_id = change_id.to_str().ok()?.to_string();
34+
35+
let conflicted = commit
36+
.extra_headers()
37+
.find(HEADERS_CONFLICTED_FIELD)
38+
.and_then(|value| value.to_str().ok()?.parse::<u64>().ok());
39+
40+
Some(HeadersV2 {
41+
change_id,
42+
conflicted,
43+
})
44+
} else {
45+
tracing::warn!(
46+
"Ignoring unknown commit header version '{version}' in commit {commit:?}",
47+
);
48+
None
49+
}
50+
} else {
51+
// Parse v1 headers
52+
let change_id = commit.extra_headers().find("change-id")?;
53+
let change_id = change_id.to_str().ok()?.to_string();
54+
let headers = HeadersV1 { change_id };
55+
Some(headers.into())
56+
}
57+
}
58+
59+
/// Write the values from this instance to the given `commit`, fully replacing any header
60+
/// that might have been there before.
61+
pub fn set_in_commit(&self, commit: &mut gix::objs::Commit) {
62+
for field in [
63+
HEADERS_VERSION_FIELD,
64+
HEADERS_CHANGE_ID_FIELD,
65+
HEADERS_CONFLICTED_FIELD,
66+
] {
67+
if let Some(pos) = commit.extra_headers().find_pos(field) {
68+
commit.extra_headers.remove(pos);
69+
}
70+
}
71+
72+
commit
73+
.extra_headers
74+
.extend(Vec::<(BString, BString)>::from(self));
75+
}
76+
}
77+
2578
impl Default for HeadersV2 {
2679
fn default() -> Self {
2780
HeadersV2 {
@@ -123,18 +176,7 @@ impl<'repo> Commit<'repo> {
123176
impl Commit<'_> {
124177
/// Set this commit to use the given `headers`, completely replacing the ones it might currently have.
125178
pub fn set_headers(&mut self, header: &HeadersV2) {
126-
for field in [
127-
HEADERS_VERSION_FIELD,
128-
HEADERS_CHANGE_ID_FIELD,
129-
HEADERS_CONFLICTED_FIELD,
130-
] {
131-
if let Some(pos) = self.extra_headers().find_pos(field) {
132-
self.extra_headers.remove(pos);
133-
}
134-
}
135-
136-
self.extra_headers
137-
.extend(Vec::<(BString, BString)>::from(header));
179+
header.set_in_commit(self)
138180
}
139181
}
140182

@@ -203,36 +245,6 @@ impl<'repo> Commit<'repo> {
203245

204246
/// Return our custom headers, of present.
205247
pub fn headers(&self) -> Option<HeadersV2> {
206-
let decoded = &self.inner;
207-
if let Some(header) = decoded.extra_headers().find(HEADERS_VERSION_FIELD) {
208-
let version = header.to_owned();
209-
210-
if version == HEADERS_VERSION {
211-
let change_id = decoded.extra_headers().find(HEADERS_CHANGE_ID_FIELD)?;
212-
let change_id = change_id.to_str().ok()?.to_string();
213-
214-
let conflicted = decoded
215-
.extra_headers()
216-
.find(HEADERS_CONFLICTED_FIELD)
217-
.and_then(|value| value.to_str().ok()?.parse::<u64>().ok());
218-
219-
Some(HeadersV2 {
220-
change_id,
221-
conflicted,
222-
})
223-
} else {
224-
tracing::warn!(
225-
"Ignoring unknown commit header version '{version}' in commit {}",
226-
self.id
227-
);
228-
None
229-
}
230-
} else {
231-
// Parse v1 headers
232-
let change_id = decoded.extra_headers().find("change-id")?;
233-
let change_id = change_id.to_str().ok()?.to_string();
234-
let headers = HeadersV1 { change_id };
235-
Some(headers.into())
236-
}
248+
HeadersV2::try_from_commit(&self.inner)
237249
}
238250
}

crates/but-rebase/src/cherry_pick.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,13 @@ pub(crate) mod function {
119119
let (base, theirs) = if to_rebase.is_conflicted() {
120120
let base = to_rebase
121121
.tree_id_by_kind(TreeKind::Base)?
122-
.expect("A conflicted tree always has a base");
122+
.expect("A conflicted tree always has a base (of the original commit to rebase)");
123123
// TODO: as long as only cherry-picking is creating these trees, THEIRS
124124
// is the original 'to_rebase'. However, if that changes we must know
125125
// what created the special merge commit.
126-
let theirs = to_rebase
127-
.tree_id_by_kind(TreeKind::Theirs)?
128-
.expect("A conflicted tree always has 'our' side");
126+
let theirs = to_rebase.tree_id_by_kind(TreeKind::Theirs)?.expect(
127+
"A conflicted tree always has 'their' side (which is our side in a cherry-pick)",
128+
);
129129
(base, theirs)
130130
} else {
131131
let base = match to_rebase.parents.first() {

crates/but-rebase/src/merge.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ pub fn octopus(
7575
ours = merge.tree.write()?.detach();
7676
}
7777
target_merge_commit.tree = ours;
78+
if but_core::commit::HeadersV2::try_from_commit(&target_merge_commit).is_none() {
79+
but_core::commit::HeadersV2::default().set_in_commit(&mut target_merge_commit);
80+
}
7881
if target_merge_commit
7982
.extra_headers()
8083
.pgp_signature()

0 commit comments

Comments
 (0)