feat(source): support cherry-picking commits for git sources

- add source.cherry_pick (and cherry-pick alias) to package spec parsing/serialization
- apply listed cherry-picks in order after git checkout
- reject cherry_pick on non-git sources with explicit error
- include cherry_pick in interactive spec TOML output
- add parsing/behavior tests and update Source test fixtures
This commit is contained in:
2026-02-28 02:13:46 -06:00
parent 7e7bd84a43
commit 73a994ac53
15 changed files with 229 additions and 1 deletions
+33
View File
@@ -116,6 +116,7 @@ pub fn copy_manual_sources(spec: &PackageSpec, cache_dir: &Path, build_dir: &Pat
extract_dir: "manual-source".to_string(),
patches: Vec::new(),
post_extract: Vec::new(),
cherry_pick: Vec::new(),
};
let fetched =
fetcher::fetch_archive(spec, &source, &cache_dir.join("manual"))?;
@@ -299,6 +300,11 @@ fn prepare_one(
) -> Result<PathBuf> {
let url = spec.expand_vars(&source.url);
let extract_dir_name = spec.expand_vars(&source.extract_dir);
let cherry_pick_revs: Vec<String> = source
.cherry_pick
.iter()
.map(|rev| spec.expand_vars(rev))
.collect();
// Local file:// handling (directories or archives)
if let Some(path_str) = url.strip_prefix("file://") {
@@ -358,11 +364,19 @@ fn prepare_one(
&checkout_dir,
&cache_dir.join("git"),
&spec.package.name,
&cherry_pick_revs,
)?;
hooks::post_extract(spec, source, &checkout_dir, cache_dir)?;
return Ok(checkout_dir);
}
if !source.cherry_pick.is_empty() {
bail!(
"source.cherry_pick is only supported for git sources (got URL: {})",
source.url
);
}
let src_dir = build_dir.join(&extract_dir_name);
if src_dir.exists() && src_dir.join(".depot_state").exists() {
crate::log_info!(
@@ -458,6 +472,7 @@ mod tests {
extract_dir: "src".into(),
patches: Vec::new(),
post_extract: Vec::new(),
cherry_pick: Vec::new(),
}],
build: Build {
build_type: BuildType::Custom,
@@ -517,6 +532,7 @@ mod tests {
extract_dir: "json-$version".into(),
patches: Vec::new(),
post_extract: Vec::new(),
cherry_pick: Vec::new(),
}],
build: Build {
build_type: BuildType::Custom,
@@ -539,6 +555,23 @@ mod tests {
assert_eq!(rev, "0123456789abcdef");
}
#[test]
fn prepare_one_rejects_cherry_pick_for_non_git_sources() {
let tmp = tempfile::tempdir().unwrap();
let cache_dir = tmp.path().join("cache");
let build_dir = tmp.path().join("build");
let mut spec = mk_spec_with_manuals(PathBuf::from("."), Vec::new());
spec.source[0].url = "https://example.com/foo.tar.gz".into();
spec.source[0].cherry_pick = vec!["deadbeef".into()];
let err = prepare_one(&spec, &spec.source[0], &cache_dir, &build_dir)
.expect_err("non-git source with cherry_pick must be rejected");
assert!(
err.to_string()
.contains("source.cherry_pick is only supported for git sources")
);
}
#[test]
fn verify_file_hash_accepts_multiple_algorithms() {
use sha2::{Digest, Sha256, Sha512};