Skip to content

Commit ca71c2a

Browse files
Prashant Palmeta-codesync[bot]
authored andcommitted
Implement commit_git_mutation_history
Summary: Implement the `commit_git_mutation_history` SCS method, replacing the stub from the previous diff with the actual logic. The method returns the immediate predecessor(s) of the queried commit. Full chain walking (loading predecessor commits recursively) can be added later if needed --- Refer this for more details: https://pxl.cl/9khH9 Reviewed By: RajivTS Differential Revision: D99834574 fbshipit-source-id: dfbf1e8cf332e93a53506a63906acdfa61bc370a
1 parent 98f4d08 commit ca71c2a

File tree

1 file changed

+197
-4
lines changed
  • eden/mononoke/servers/scs/scs_methods/src/methods

1 file changed

+197
-4
lines changed

eden/mononoke/servers/scs/scs_methods/src/methods/commit.rs

Lines changed: 197 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1761,11 +1761,64 @@ impl SourceControlServiceImpl {
17611761
/// predecessor/predecessor-op extra headers on the commit object.
17621762
pub(crate) async fn commit_git_mutation_history(
17631763
&self,
1764-
_ctx: CoreContext,
1765-
_commit: thrift::CommitSpecifier,
1766-
_params: thrift::CommitGitMutationHistoryParams,
1764+
ctx: CoreContext,
1765+
commit: thrift::CommitSpecifier,
1766+
params: thrift::CommitGitMutationHistoryParams,
17671767
) -> Result<thrift::CommitGitMutationHistoryResponse, scs_errors::ServiceError> {
1768-
Err(scs_errors::internal_error("commit_git_mutation_history not yet implemented").into())
1768+
let (_repo, changeset) = self.repo_changeset(ctx, &commit).await?;
1769+
1770+
let git_extra_headers = changeset.git_extra_headers().await?;
1771+
let (predecessors, op) = extract_git_predecessors(&git_extra_headers);
1772+
1773+
let git_mutation_history = match params.format {
1774+
thrift::MutationHistoryFormat::COMMIT_ID => {
1775+
let commit_ids = predecessors
1776+
.into_iter()
1777+
.map(|sha1_bytes| thrift::CommitId::git(sha1_bytes))
1778+
.collect();
1779+
thrift::GitMutationHistory::commit_ids(commit_ids)
1780+
}
1781+
thrift::MutationHistoryFormat::GIT_MUTATION => {
1782+
if predecessors.is_empty() {
1783+
thrift::GitMutationHistory::git_mutations(vec![])
1784+
} else {
1785+
// Get the successor (current commit) git identity
1786+
let successor_id = changeset
1787+
.git_sha1()
1788+
.await?
1789+
.map(|sha1| thrift::CommitId::git(sha1.as_ref().to_vec()))
1790+
.unwrap_or_else(|| {
1791+
// Fall back to bonsai if no git identity
1792+
thrift::CommitId::bonsai(changeset.id().as_ref().to_vec())
1793+
});
1794+
1795+
let predecessor_ids: Vec<thrift::CommitId> = predecessors
1796+
.into_iter()
1797+
.map(|sha1_bytes| thrift::CommitId::git(sha1_bytes))
1798+
.collect();
1799+
1800+
let mutation = thrift::GitMutation {
1801+
successor: successor_id,
1802+
predecessors: predecessor_ids,
1803+
op,
1804+
..Default::default()
1805+
};
1806+
thrift::GitMutationHistory::git_mutations(vec![mutation])
1807+
}
1808+
}
1809+
unknown => {
1810+
return Err(scs_errors::invalid_request(format!(
1811+
"invalid mutation history format: {:?}",
1812+
unknown
1813+
))
1814+
.into());
1815+
}
1816+
};
1817+
1818+
Ok(thrift::CommitGitMutationHistoryResponse {
1819+
git_mutation_history,
1820+
..Default::default()
1821+
})
17691822
}
17701823

17711824
/// Returns the directory branch clusters for a commit
@@ -1941,3 +1994,143 @@ impl SourceControlServiceImpl {
19411994
})
19421995
}
19431996
}
1997+
1998+
/// Extract predecessor SHA1s and operation from git extra headers.
1999+
/// Returns (predecessor_hex_bytes, operation_string).
2000+
fn extract_git_predecessors<K: AsRef<[u8]>, V: AsRef<[u8]>>(
2001+
headers: &Option<Vec<(K, V)>>,
2002+
) -> (Vec<Vec<u8>>, String) {
2003+
let mut predecessors: Vec<Vec<u8>> = Vec::new();
2004+
let mut op = String::new();
2005+
2006+
if let Some(headers) = headers {
2007+
for (key, value) in headers {
2008+
if key.as_ref() == b"predecessor" {
2009+
if let Ok(value_str) = std::str::from_utf8(value.as_ref()) {
2010+
for hex_sha1 in value_str.split(',') {
2011+
let hex_sha1 = hex_sha1.trim();
2012+
if !hex_sha1.is_empty() {
2013+
predecessors.push(hex_sha1.as_bytes().to_vec());
2014+
}
2015+
}
2016+
}
2017+
} else if key.as_ref() == b"predecessor-op" {
2018+
if let Ok(value_str) = std::str::from_utf8(value.as_ref()) {
2019+
op = value_str.to_string();
2020+
}
2021+
}
2022+
}
2023+
}
2024+
2025+
(predecessors, op)
2026+
}
2027+
2028+
#[cfg(test)]
2029+
mod test_git_mutation {
2030+
use super::*;
2031+
2032+
#[test]
2033+
fn test_extract_no_headers() {
2034+
let headers: Option<Vec<(Vec<u8>, Vec<u8>)>> = None;
2035+
let (preds, op) = extract_git_predecessors(&headers);
2036+
assert!(preds.is_empty());
2037+
assert!(op.is_empty());
2038+
}
2039+
2040+
#[test]
2041+
fn test_extract_empty_headers() {
2042+
let headers: Option<Vec<(Vec<u8>, Vec<u8>)>> = Some(vec![]);
2043+
let (preds, op) = extract_git_predecessors(&headers);
2044+
assert!(preds.is_empty());
2045+
assert!(op.is_empty());
2046+
}
2047+
2048+
#[test]
2049+
fn test_extract_no_predecessor_headers() {
2050+
let headers = Some(vec![
2051+
(b"mergetag".to_vec(), b"some-tag-data".to_vec()),
2052+
(b"gpgsig".to_vec(), b"signature-data".to_vec()),
2053+
]);
2054+
let (preds, op) = extract_git_predecessors(&headers);
2055+
assert!(preds.is_empty());
2056+
assert!(op.is_empty());
2057+
}
2058+
2059+
#[test]
2060+
fn test_extract_single_predecessor() {
2061+
let sha1 = "da39a3ee5e6b4b0d3255bfef95601890afd80709";
2062+
let headers = Some(vec![
2063+
(b"predecessor".to_vec(), sha1.as_bytes().to_vec()),
2064+
(b"predecessor-op".to_vec(), b"amend".to_vec()),
2065+
]);
2066+
let (preds, op) = extract_git_predecessors(&headers);
2067+
assert_eq!(preds.len(), 1);
2068+
assert_eq!(preds[0], sha1.as_bytes());
2069+
assert_eq!(op, "amend");
2070+
}
2071+
2072+
#[test]
2073+
fn test_extract_multiple_predecessors_comma_separated() {
2074+
let sha1_a = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
2075+
let sha1_b = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
2076+
let value = format!("{},{}", sha1_a, sha1_b);
2077+
let headers = Some(vec![
2078+
(b"predecessor".to_vec(), value.as_bytes().to_vec()),
2079+
(b"predecessor-op".to_vec(), b"fold".to_vec()),
2080+
]);
2081+
let (preds, op) = extract_git_predecessors(&headers);
2082+
assert_eq!(preds.len(), 2);
2083+
assert_eq!(preds[0], sha1_a.as_bytes());
2084+
assert_eq!(preds[1], sha1_b.as_bytes());
2085+
assert_eq!(op, "fold");
2086+
}
2087+
2088+
#[test]
2089+
fn test_extract_rebase_op() {
2090+
let sha1 = "1234567890abcdef1234567890abcdef12345678";
2091+
let headers = Some(vec![
2092+
(b"predecessor".to_vec(), sha1.as_bytes().to_vec()),
2093+
(b"predecessor-op".to_vec(), b"rebase".to_vec()),
2094+
]);
2095+
let (preds, op) = extract_git_predecessors(&headers);
2096+
assert_eq!(preds.len(), 1);
2097+
assert_eq!(op, "rebase");
2098+
}
2099+
2100+
#[test]
2101+
fn test_extract_cherry_pick_op() {
2102+
let sha1 = "abcdef1234567890abcdef1234567890abcdef12";
2103+
let headers = Some(vec![
2104+
(b"predecessor".to_vec(), sha1.as_bytes().to_vec()),
2105+
(b"predecessor-op".to_vec(), b"cherry-pick".to_vec()),
2106+
]);
2107+
let (preds, op) = extract_git_predecessors(&headers);
2108+
assert_eq!(preds.len(), 1);
2109+
assert_eq!(op, "cherry-pick");
2110+
}
2111+
2112+
#[test]
2113+
fn test_extract_predecessor_without_op() {
2114+
let sha1 = "da39a3ee5e6b4b0d3255bfef95601890afd80709";
2115+
let headers = Some(vec![(b"predecessor".to_vec(), sha1.as_bytes().to_vec())]);
2116+
let (preds, op) = extract_git_predecessors(&headers);
2117+
assert_eq!(preds.len(), 1);
2118+
assert_eq!(preds[0], sha1.as_bytes());
2119+
assert!(op.is_empty());
2120+
}
2121+
2122+
#[test]
2123+
fn test_extract_with_other_headers_intermixed() {
2124+
let sha1 = "da39a3ee5e6b4b0d3255bfef95601890afd80709";
2125+
let headers = Some(vec![
2126+
(b"mergetag".to_vec(), b"tag-data".to_vec()),
2127+
(b"predecessor".to_vec(), sha1.as_bytes().to_vec()),
2128+
(b"gpgsig".to_vec(), b"sig-data".to_vec()),
2129+
(b"predecessor-op".to_vec(), b"amend".to_vec()),
2130+
]);
2131+
let (preds, op) = extract_git_predecessors(&headers);
2132+
assert_eq!(preds.len(), 1);
2133+
assert_eq!(preds[0], sha1.as_bytes());
2134+
assert_eq!(op, "amend");
2135+
}
2136+
}

0 commit comments

Comments
 (0)