upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2026-02-27 15:51:01 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2026-02-27 15:53:11 +0000
commit71cd982e5ca3e6f60fdf33fd41d0db3eabdbf39f (patch)
treec74494e8251e876a962413e73b5b5552e4b57e51 /src
parent28ad5440c7184de9833f8448bc90153ee4499c83 (diff)
feat: ngit sync --force republishes state event even with no ref changes
allows users to repair repos whose state event is missing ^{} peeled refs for annotated tags (or any other corruption) without needing to push a new ref. the new event is signed with a fresh timestamp and broadcast to all repo relays and the user's write relays.
Diffstat (limited to 'src')
-rw-r--r--src/bin/ngit/sub_commands/sync.rs60
1 files changed, 59 insertions, 1 deletions
diff --git a/src/bin/ngit/sub_commands/sync.rs b/src/bin/ngit/sub_commands/sync.rs
index 4d7e799..146bcbc 100644
--- a/src/bin/ngit/sub_commands/sync.rs
+++ b/src/bin/ngit/sub_commands/sync.rs
@@ -78,6 +78,64 @@ pub async fn launch(args: &SubCommandArgs) -> Result<()> {
78 78
79 let nostr_state = get_state_from_cache(Some(git_repo_path), &repo_ref).await?; 79 let nostr_state = get_state_from_cache(Some(git_repo_path), &repo_ref).await?;
80 80
81 // When --force is given, rebuild and republish the state event even if
82 // nothing has changed. This lets users repair repos whose state event is
83 // missing ^{} peeled refs for annotated tags (or any other corruption)
84 // without needing to push a new ref. A fresh event is signed (new
85 // created_at) and broadcast to all repo relays and the user's write relays.
86 if args.force {
87 let (signer, user_ref, _) = load_existing_login(
88 &Some(&git_repo),
89 &None,
90 &None,
91 &None,
92 Some(&client),
93 false, // not silent — we need the user to authenticate if required
94 false, // prompt_for_password
95 false, // fetch_profile_updates
96 )
97 .await
98 .context("authentication required to republish state; run 'ngit account login' first")?;
99 client.set_signer(signer.clone()).await;
100 // Backfill any missing ^{} peeled refs before rebuilding — the existing
101 // state event may predate the fix that started storing them.
102 let mut state = nostr_state.state.clone();
103 let tag_refs: Vec<(String, String)> = state
104 .iter()
105 .filter(|(k, _)| k.starts_with("refs/tags/") && !k.ends_with("^{}"))
106 .map(|(k, v)| (k.clone(), v.clone()))
107 .collect();
108 for (ref_name, tag_oid) in tag_refs {
109 let peeled_key = format!("{ref_name}^{{}}");
110 if state.contains_key(&peeled_key) {
111 continue;
112 }
113 if let Ok(oid) = git2::Oid::from_str(&tag_oid) {
114 if git_repo
115 .git_repo
116 .find_object(oid, Some(git2::ObjectType::Tag))
117 .is_ok()
118 {
119 if let Ok(commit_oid) = git_repo.get_commit_or_tip_of_reference(&ref_name) {
120 state.insert(peeled_key, commit_oid.to_string());
121 }
122 }
123 }
124 }
125 let new_state = RepoState::build(repo_ref.identifier.clone(), state, &signer).await?;
126 send_events(
127 &client,
128 Some(git_repo_path),
129 vec![new_state.event],
130 user_ref.relays.write(),
131 repo_ref.relays.clone(),
132 true,
133 false,
134 )
135 .await?;
136 println!("state event republished");
137 }
138
81 // Publish the current state event to any grasp server relays that are 139 // Publish the current state event to any grasp server relays that are
82 // missing it or have a stale version. Grasp servers reject git pushes 140 // missing it or have a stale version. Grasp servers reject git pushes
83 // unless the state event is already present on their relay, so we must 141 // unless the state event is already present on their relay, so we must
@@ -129,7 +187,7 @@ pub async fn launch(args: &SubCommandArgs) -> Result<()> {
129 ) 187 )
130 .await 188 .await
131 { 189 {
132 client.set_signer(signer).await; 190 client.set_signer(signer.clone()).await;
133 } 191 }
134 // Send only to the specific grasp relays that are missing or have a 192 // Send only to the specific grasp relays that are missing or have a
135 // stale state event — no user write relays. 193 // stale state event — no user write relays.