upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--src/bin/git_remote_nostr/push.rs31
-rw-r--r--src/lib/repo_state.rs7
3 files changed, 37 insertions, 2 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cbf5312..f52d353 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
18 18
19### Fixed 19### Fixed
20 20
21- Annotated tags missing from `git-remote-nostr` list output; peeled `^{}` refs were stripped when parsing the nostr state event, so git could not resolve the tag to a commit and `git fetch --prune` deleted it; existing repos with affected state events are self-healed on the next push
21- Fallback signer relays updated: replaced `nsec.app` with `bucket.coracle.social` and `nos.lol` for nostrconnect resilience 22- Fallback signer relays updated: replaced `nsec.app` with `bucket.coracle.social` and `nos.lol` for nostrconnect resilience
22- `merge-base` tag in PR events generated by `git push` of a `pr/` branch was set to the parent of the PR tip instead of the actual base commit; multi-commit PRs showed only 1 commit when applied via `ngit apply` 23- `merge-base` tag in PR events generated by `git push` of a `pr/` branch was set to the parent of the PR tip instead of the actual base commit; multi-commit PRs showed only 1 commit when applied via `ngit apply`
23- `git-remote-nostr` list now advertises the newest state event whose OIDs are all confirmed present on a git server or locally, rather than unconditionally using the latest nostr state event; this prevents catastrophic fetch/clone failures when a state event was published before the corresponding git push completed 24- `git-remote-nostr` list now advertises the newest state event whose OIDs are all confirmed present on a git server or locally, rather than unconditionally using the latest nostr state event; this prevents catastrophic fetch/clone failures when a state event was published before the corresponding git push completed
diff --git a/src/bin/git_remote_nostr/push.rs b/src/bin/git_remote_nostr/push.rs
index 870f22d..ed0f7df 100644
--- a/src/bin/git_remote_nostr/push.rs
+++ b/src/bin/git_remote_nostr/push.rs
@@ -925,6 +925,37 @@ fn generate_updated_state(
925) -> Result<HashMap<String, String>> { 925) -> Result<HashMap<String, String>> {
926 let mut new_state = existing_state.clone(); 926 let mut new_state = existing_state.clone();
927 927
928 // Backfill missing ^{} peeled refs for any annotated tags already in the
929 // state. State events published before this fix only stored the tag object
930 // OID; without the corresponding ^{} entry git cannot resolve the tag to a
931 // commit and treats it as missing (git fetch --prune deletes it). We fix
932 // this opportunistically on every push so affected repos self-heal without
933 // requiring manual intervention.
934 let tag_refs: Vec<(String, String)> = new_state
935 .iter()
936 .filter(|(k, _)| k.starts_with("refs/tags/") && !k.ends_with("^{}"))
937 .map(|(k, v)| (k.clone(), v.clone()))
938 .collect();
939 for (ref_name, tag_oid) in tag_refs {
940 let peeled_key = format!("{ref_name}^{{}}");
941 if new_state.contains_key(&peeled_key) {
942 continue;
943 }
944 // check if the stored OID is a tag object (annotated tag)
945 if let Ok(oid) = git2::Oid::from_str(&tag_oid) {
946 if git_repo
947 .git_repo
948 .find_object(oid, Some(git2::ObjectType::Tag))
949 .is_ok()
950 {
951 // peel to the commit the annotated tag points to
952 if let Ok(commit_oid) = git_repo.get_commit_or_tip_of_reference(&ref_name) {
953 new_state.insert(peeled_key, commit_oid.to_string());
954 }
955 }
956 }
957 }
958
928 for refspec in refspecs { 959 for refspec in refspecs {
929 let (from, to) = refspec_to_from_to(refspec)?; 960 let (from, to) = refspec_to_from_to(refspec)?;
930 if from.is_empty() { 961 if from.is_empty() {
diff --git a/src/lib/repo_state.rs b/src/lib/repo_state.rs
index 345f05c..223fe56 100644
--- a/src/lib/repo_state.rs
+++ b/src/lib/repo_state.rs
@@ -22,11 +22,14 @@ impl RepoState {
22 let mut state = HashMap::new(); 22 let mut state = HashMap::new();
23 for tag in event.tags.iter() { 23 for tag in event.tags.iter() {
24 if let Some(name) = tag.as_slice().first() { 24 if let Some(name) = tag.as_slice().first() {
25 // include ^{} peeled refs for annotated tags: git requires
26 // both "<tag-oid> refs/tags/v1.0.0" and
27 // "<commit-oid> refs/tags/v1.0.0^{}" in the list output so
28 // it can resolve the tag to a commit. without the ^{} line
29 // git fetch --prune deletes the tag as unresolvable.
25 if ["refs/heads/", "refs/tags", "HEAD"] 30 if ["refs/heads/", "refs/tags", "HEAD"]
26 .iter() 31 .iter()
27 .any(|s| name.starts_with(*s)) 32 .any(|s| name.starts_with(*s))
28 // dont include dereferenced tags
29 && !name.ends_with("^{}")
30 { 33 {
31 if let Some(value) = tag.as_slice().get(1) { 34 if let Some(value) = tag.as_slice().get(1) {
32 if Oid::from_str(value).is_ok() || value.contains("ref: refs/") { 35 if Oid::from_str(value).is_ok() || value.contains("ref: refs/") {