From 28f15e8be3e4e6205e2c6ce09ab71ea71f358d6e Mon Sep 17 00:00:00 2001

From: DanConwayDev

Date: Wed, 27 Aug 2025 16:38:12 +0100

Subject: [PATCH] remote: fix out of sync git servers during list

so maintainers don't need to to run `ngit sync` to fix a repo

created in response to issue:

nostr:nevent1qvzqqqqx25pzpslz866785q0rze0favgmrxmc4yxfzl8vx7ajzqjrpklgcpa0j4fqqs2qt4vxhr37c9u4q9xy4u7ln2w96anv7h4965kef6d0kdygp82zscy5sc79

---

src/bin/git_remote_nostr/list.rs | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------

src/bin/git_remote_nostr/main.rs | 20 ++++++++++++++++----

src/lib/git/mod.rs | 10 ++++++++++

3 files changed, 131 insertions(+), 31 deletions(-)

diff --git a/src/bin/git_remote_nostr/list.rs b/src/bin/git_remote_nostr/list.rs

index be83991..9735f72 100644

--- a/src/bin/git_remote_nostr/list.rs

+++ b/src/bin/git_remote_nostr/list.rs

@@ -10,6 +10,7 @@ use ngit::{

git_events::{KIND_PULL_REQUEST, KIND_PULL_REQUEST_UPDATE, event_to_cover_letter, tag_value},

list::{get_ahead_behind, list_from_remotes},

login::get_curent_user,

+ push::push_to_remote,

repo_ref::{self},

utils::{get_all_proposals, get_open_or_draft_proposals, get_short_git_server_name},

};

@@ -20,6 +21,7 @@ use crate::{fetch::make_commits_for_proposal, git::Repo};

pub async fn run_list(

git_repo: &Repo,

repo_ref: &RepoRef,

+ user_is_known_and_maintainer: bool,

for_push: bool,

) -> Result, bool)>> {

let nostr_state = (get_state_from_cache(Some(git_repo.get_path()?), repo_ref).await).ok();

@@ -35,33 +37,14 @@ pub async fn run_list(

);

let mut state = if let Some(nostr_state) = nostr_state {

- for (name, value) in &nostr_state.state {

- for (url, (remote_state, _is_grasp_server)) in &remote_states {

- let remote_name = get_short_git_server_name(git_repo, url);

- if let Some(remote_value) = remote_state.get(name) {

- if value.ne(remote_value) {

- term.write_line(

- format!(

- "WARNING: {remote_name} {name} is {} nostr ",

- if let Ok((ahead, behind)) =

- get_ahead_behind(git_repo, value, remote_value)

- {

- format!("{} ahead {} behind", ahead.len(), behind.len())

- } else {

- "out of sync with".to_string()

- }

- )

- .as_str(),

- )?;

- }

- } else {

- term.write_line(

- format!("WARNING: {remote_name} {name} is missing but tracked on nostr")

- .as_str(),

- )?;

- }

- }

- }

+ sync_git_servers_with_nostr_state(

+ git_repo,

+ repo_ref,

+ &term,

+ &nostr_state.state,

+ &remote_states,

+ user_is_known_and_maintainer,

+ )?;

nostr_state.state

} else {

let (state, _is_grasp_server) = repo_ref

@@ -104,6 +87,101 @@ pub async fn run_list(

Ok(remote_states)

}

+fn sync_git_servers_with_nostr_state(

+ git_repo: &Repo,

+ repo_ref: &RepoRef,

+ term: &console::Term,

+ nostr_state: &HashMap,

+ remote_states: &HashMap, bool)>,

+ user_is_known_and_maintainer: bool,

+) -> Result<()> {

+ let mut to_immediately_sync: HashMap, bool)> = HashMap::new();

+ for (name, value) in nostr_state {

+ for (url, (remote_state, is_grasp_server)) in remote_states {

+ let remote_name = get_short_git_server_name(git_repo, url);

+ if let Some(remote_value) = remote_state.get(name) {

+ if value.ne(remote_value) {

+ if let Ok((ahead, behind)) = get_ahead_behind(git_repo, value, remote_value) {

+ // TODO maybe we should try and fix it and only warn if we cant?

+ term.write_line(

+ format!(

+ "WARNING: {remote_name} {name} is {} ahead {} behind nostr",

+ ahead.len(),

+ behind.len()

+ )

+ .as_str(),

+ )?;

+ if *is_grasp_server {

+ if git_repo.does_oid_exist(value).is_ok_and(|v| v) {

+ to_immediately_sync

+ .entry(url.to_string())

+ .or_insert((Vec::new(), *is_grasp_server))

+ .0

+ .push(format!(

+ "{}{value}:{name}",

+ if *is_grasp_server { "+" } else { "" }

+ ));

+ }

+ } else if behind.is_empty() {

+ if user_is_known_and_maintainer {

+ to_immediately_sync

+ .entry(url.to_string())

+ .or_insert((Vec::new(), *is_grasp_server))

+ .0

+ .push(format!("{value}:{name}"));

+ } else {

+ // ff push to non-grasp server by someone with

+ // keys required

+ }

+ } else if user_is_known_and_maintainer {

+ // TODO warn maintainer about conflicts and

+ // suggesting running `ngit sync --force`

+ }

+ } else {

+ // here we probably dont have the git data to push

+ term.write_line(

+ format!("WARNING: {remote_name} {name} is out of sync with nostr ")

+ .as_str(),

+ )?;

+ }

+ }

+ } else {

+ term.write_line(

+ format!("WARNING: {remote_name} {name} is missing but tracked on nostr")

+ .as_str(),

+ )?;

+ to_immediately_sync

+ .entry(url.to_string())

+ .or_insert((Vec::new(), *is_grasp_server))

+ .0

+ .push(format!("{value}:{name}"));

+ }

+ }

+ }

+ if !to_immediately_sync.is_empty() {

+ term.write_line("attempting to align git servers with nostr state")?;

+

+ for (url, (remote_refspecs, is_grasp_server)) in to_immediately_sync {

+ if let Err(error) = push_to_remote(

+ git_repo,

+ &url,

+ &repo_ref.nostr_git_url.clone().context("run_list should only be called in git-remote-nostr where nostr_git_url is always set")?,

+ &remote_refspecs,

+ term,

+ is_grasp_server,

+ ) {

+ let remote_name = get_short_git_server_name(git_repo, &url);

+

+ term.write_line(

+ format!(" - failed to align {remote_name} with nostr state: {error}")

+ .as_str(),

+ )?;

+ }

+ }

+ }

+ Ok(())

+}

+

/// fetches branches and tags from git servers so patch parent commits can be

/// used to build patches with correct commit ids

async fn get_open_and_draft_proposals_state(

diff --git a/src/bin/git_remote_nostr/main.rs b/src/bin/git_remote_nostr/main.rs

index aac626b..c544d76 100644

--- a/src/bin/git_remote_nostr/main.rs

+++ b/src/bin/git_remote_nostr/main.rs

@@ -38,7 +38,7 @@ async fn main() -> Result<()> {

let mut client = Client::new(Params::with_git_config_relay_defaults(&Some(&git_repo)));

- if let Ok((signer, _, _)) = load_existing_login(

+ let user_ref = if let Ok((signer, user_ref, _)) = load_existing_login(

&Some(&git_repo),

&None,

&None,

@@ -52,7 +52,10 @@ async fn main() -> Result<()> {

{

// signer for to respond to relay auth request

client.set_signer(signer).await;

- }

+ Some(user_ref)

+ } else {

+ None

+ };

fetching_with_report_for_helper(git_repo_path, &client, &decoded_nostr_url.coordinate).await?;

@@ -61,6 +64,9 @@ async fn main() -> Result<()> {

repo_ref.set_nostr_git_url(decoded_nostr_url.clone());

+ let user_is_known_and_maintainer =

+ user_ref.is_some_and(|u| repo_ref.maintainers.contains(&u.public_key));

+

let _ = set_git_timeout();

let stdin = io::stdin();

@@ -98,10 +104,16 @@ async fn main() -> Result<()> {

.await?;

}

["list"] => {

- list_outputs = Some(list::run_list(&git_repo, &repo_ref, false).await?);

+ list_outputs = Some(

+ list::run_list(&git_repo, &repo_ref, user_is_known_and_maintainer, false)

+ .await?,

+ );

}

["list", "for-push"] => {

- list_outputs = Some(list::run_list(&git_repo, &repo_ref, true).await?);

+ list_outputs = Some(

+ list::run_list(&git_repo, &repo_ref, user_is_known_and_maintainer, true)

+ .await?,

+ );

}

[] => {

return Ok(());

diff --git a/src/lib/git/mod.rs b/src/lib/git/mod.rs

index 3d5297f..f161605 100644

--- a/src/lib/git/mod.rs

+++ b/src/lib/git/mod.rs

@@ -50,6 +50,7 @@ pub trait RepoActions {

fn get_commit_or_tip_of_reference(&self, reference: &str) -> Result;

fn get_root_commit(&self) -> Result;

fn does_commit_exist(&self, commit: &str) -> Result;

+ fn does_oid_exist(&self, commit: &str) -> Result;

fn get_head_commit(&self) -> Result;

fn get_commit_parent(&self, commit: &Sha1Hash) -> Result;

fn get_commit_message(&self, commit: &Sha1Hash) -> Result;

@@ -275,6 +276,15 @@ impl RepoActions for Repo {

}

}

+ fn does_oid_exist(&self, oid: &str) -> Result {

+ let oid_obj = Oid::from_str(oid).context("not an oid hash")?;

+ if self.git_repo.find_object(oid_obj, None).is_ok() {

+ Ok(true)

+ } else {

+ Ok(false)

+ }

+ }

+

fn get_head_commit(&self) -> Result {

let head = self

.git_repo

--

libgit2 1.9.1

Reply to this note

Please Login to reply.

Discussion

Test reply