fix: repository sync (#29)
Reviewed-on: #29
This commit was merged in pull request #29.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use std::collections::{BTreeSet, HashMap, HashSet};
|
||||
use std::collections::{BTreeSet, HashSet};
|
||||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Stdio;
|
||||
@@ -62,16 +62,13 @@ impl Repo {
|
||||
pub fn init(path: &Path, name: &str, description: &str) -> Result<String> {
|
||||
use gix::refs::transaction::{Change, LogChange, PreviousValue, RefEdit, RefLog};
|
||||
|
||||
std::fs::create_dir_all(path)
|
||||
.with_context(|| format!("failed to create {}", path.display()))?;
|
||||
|
||||
std::fs::create_dir_all(path)?;
|
||||
let repo = gix::init(path)?;
|
||||
|
||||
let (signature, mut time_buf) = Self::repository_signature();
|
||||
let signature = signature.to_ref(&mut time_buf);
|
||||
|
||||
let head = gix::refs::FullName::try_from("HEAD")
|
||||
.map_err(|e| anyhow::anyhow!("invalid ref name: {e}"))?;
|
||||
let signature = signature.to_ref(&mut time_buf);
|
||||
let head = gix::refs::FullName::try_from("HEAD")?;
|
||||
|
||||
repo.edit_references_as(
|
||||
[RefEdit {
|
||||
@@ -99,7 +96,7 @@ impl Repo {
|
||||
format!("# {name}\n\n{description}\n")
|
||||
};
|
||||
|
||||
std::fs::write(path.join("README.md"), &readme).context("failed to write README.md")?;
|
||||
std::fs::write(path.join("README.md"), &readme)?;
|
||||
|
||||
let blob = repo.write_object(gix::objs::Blob {
|
||||
data: readme.into_bytes(),
|
||||
@@ -1192,11 +1189,13 @@ impl Repo {
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
self.inner
|
||||
.find_remote("origin")?
|
||||
.connect(gix::remote::Direction::Fetch)?
|
||||
.prepare_fetch(Discard, options)?
|
||||
.receive(Discard, &IS_INTERRUPTED)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1263,55 +1262,6 @@ impl Repo {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Checks that `url` advertises exactly the expected ref oids.
|
||||
pub fn remote_has_refs(&self, url: &str, expected: &[(String, String)]) -> Result<bool> {
|
||||
if expected.is_empty() {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
let url = Self::transport_url(url);
|
||||
|
||||
let refspecs = expected
|
||||
.iter()
|
||||
.map(|(name, _)| {
|
||||
gix::refspec::parse(
|
||||
gix::bstr::BStr::new(format!("+{name}:{name}").as_bytes()),
|
||||
gix::refspec::parse::Operation::Fetch,
|
||||
)
|
||||
.map(|spec| spec.to_owned())
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.into_anyhow()
|
||||
.context("invalid refspec")?;
|
||||
|
||||
let options = gix::remote::ref_map::Options {
|
||||
extra_refspecs: refspecs,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let (refs, _) = self
|
||||
.inner
|
||||
.remote_at(url.as_str())
|
||||
.with_context(|| format!("cannot use remote {url}"))?
|
||||
.connect(gix::remote::Direction::Fetch)
|
||||
.with_context(|| format!("cannot connect to {url}"))?
|
||||
.ref_map(Discard, options)
|
||||
.with_context(|| format!("listing refs of {url} failed"))?;
|
||||
|
||||
let advertised: HashMap<String, String> = refs
|
||||
.remote_refs
|
||||
.iter()
|
||||
.filter_map(|reference| {
|
||||
let (name, object, _peeled) = reference.unpack();
|
||||
object.map(|oid| (String::from_utf8_lossy(name).into_owned(), oid.to_string()))
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(expected
|
||||
.iter()
|
||||
.all(|(name, oid)| advertised.get(name.as_str()) == Some(oid)))
|
||||
}
|
||||
|
||||
/// Sets the `origin` URL when the repository has no remote yet.
|
||||
pub fn ensure_origin(&self, url: &str) -> Result<()> {
|
||||
if self.inner.find_remote("origin").is_ok() {
|
||||
|
||||
@@ -56,39 +56,61 @@ fn push_all_mirrors_branches_and_tags() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remote_has_refs_reports_whether_pushed_refs_landed() {
|
||||
let server = tempfile::tempdir().unwrap();
|
||||
bare_server(server.path(), "npub1test", "my-repo");
|
||||
fn ref_state_matches_the_git_cli_ref_listing() {
|
||||
let (dir, repo) = fixture(&[("a.txt", b"one")]);
|
||||
commit_all(&repo, "initial");
|
||||
let dir = dir.path();
|
||||
let main = git_in(dir, &["rev-parse", "refs/heads/main"]).expect("main oid");
|
||||
let url = format!("file://{}/npub1test/my-repo.git", server.path().display());
|
||||
let expected = vec![("refs/heads/main".to_owned(), main.clone())];
|
||||
let repo = Repo::open(dir).expect("open");
|
||||
|
||||
assert!(!repo.remote_has_refs(&url, &expected).expect("probe"));
|
||||
git_run(dir, &["branch", "browse-group"]);
|
||||
git_run(dir, &["checkout", "-q", "-b", "new-search"]);
|
||||
std::fs::write(dir.join("b.txt"), b"two").expect("write");
|
||||
commit_all(&repo, "search work");
|
||||
git_run(dir, &["checkout", "-q", "-"]);
|
||||
git_run(dir, &["tag", "v1"]);
|
||||
|
||||
repo.push_all(
|
||||
&format!("file://{}", server.path().display()),
|
||||
"npub1test",
|
||||
"my-repo",
|
||||
)
|
||||
.expect("push");
|
||||
let listing = |repo: &Repo| {
|
||||
let mut refs = repo.ref_state().expect("ref state").refs;
|
||||
refs.sort();
|
||||
refs
|
||||
};
|
||||
|
||||
assert!(repo.remote_has_refs(&url, &expected).expect("probe"));
|
||||
// Loose refs.
|
||||
let loose = listing(&Repo::open(dir).expect("open"));
|
||||
|
||||
let stale = vec![("refs/heads/main".to_owned(), "0".repeat(40))];
|
||||
assert!(!repo.remote_has_refs(&url, &stale).expect("probe"));
|
||||
// Packed refs.
|
||||
git_run(dir, &["pack-refs", "--all"]);
|
||||
let packed = listing(&Repo::open(dir).expect("open"));
|
||||
|
||||
git_run(dir, &["tag", "v1.0"]);
|
||||
repo.push_all(
|
||||
&format!("file://{}", server.path().display()),
|
||||
"npub1test",
|
||||
"my-repo",
|
||||
)
|
||||
.expect("push");
|
||||
assert!(repo.remote_has_refs(&url, &expected).expect("probe"));
|
||||
let output = git_run(
|
||||
dir,
|
||||
&[
|
||||
"for-each-ref",
|
||||
"--format=%(refname) %(objectname)",
|
||||
"refs/heads",
|
||||
"refs/tags",
|
||||
],
|
||||
);
|
||||
let mut cli = String::from_utf8_lossy(&output.stdout)
|
||||
.lines()
|
||||
.map(|line| {
|
||||
let (name, oid) = line.split_once(' ').expect("for-each-ref format");
|
||||
(name.to_owned(), oid.to_owned())
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
cli.sort();
|
||||
|
||||
assert_eq!(loose, cli);
|
||||
assert_eq!(packed, cli);
|
||||
assert!(
|
||||
loose
|
||||
.iter()
|
||||
.any(|(name, _)| name == "refs/heads/new-search")
|
||||
);
|
||||
assert!(
|
||||
loose
|
||||
.iter()
|
||||
.any(|(name, _)| name == "refs/heads/browse-group")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user