From b482ae85f0233e6a597bb98d17bf222612c32920 Mon Sep 17 00:00:00 2001 From: cloudhead Date: Mon, 18 Sep 2023 17:58:21 +0200 Subject: [PATCH] remote-helper: Run sync on `git-fetch` Changes the behavior of `git fetch` by running a radicle network-level fetch (ie. `rad sync --fetch`) before fetching refs from storage. This avoids having to run `rad sync` before `git fetch`; and tries to make sure that refs are always fresh. --- radicle-cli/examples/git/git-pull.md | 1 + radicle-cli/examples/git/git-push-diverge.md | 3 - radicle-cli/examples/rad-remote.md | 2 +- .../workflow/5-patching-maintainer.md | 7 +- .../workflow/6-pulling-contributor.md | 2 +- radicle-remote-helper/src/lib.rs | 84 +++++++++++++++++++ 6 files changed, 90 insertions(+), 9 deletions(-) diff --git a/radicle-cli/examples/git/git-pull.md b/radicle-cli/examples/git/git-pull.md index 652254b7..1a0cb81e 100644 --- a/radicle-cli/examples/git/git-pull.md +++ b/radicle-cli/examples/git/git-pull.md @@ -13,6 +13,7 @@ f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 refs/heads/master ``` (stderr) $ git fetch alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi +✓ Synced with 1 peer(s) From rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi * [new branch] alice/1 -> alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/alice/1 ``` diff --git a/radicle-cli/examples/git/git-push-diverge.md b/radicle-cli/examples/git/git-push-diverge.md index c866ff53..ffe0a71e 100644 --- a/radicle-cli/examples/git/git-push-diverge.md +++ b/radicle-cli/examples/git/git-push-diverge.md @@ -33,9 +33,6 @@ As Alice, we fetch that code, but commit on top of our own master, which is no longer canonical, since Bob pushed a more recent commit, and the threshold is 1: ``` ~alice -$ rad sync --fetch -✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkt67…v4N1tRk.. -✓ Fetched repository from 1 seed(s) $ git fetch bob@z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk $ git branch -arv bob@z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk/master 319a7dc Third commit diff --git a/radicle-cli/examples/rad-remote.md b/radicle-cli/examples/rad-remote.md index 2dae29ba..f7b31531 100644 --- a/radicle-cli/examples/rad-remote.md +++ b/radicle-cli/examples/rad-remote.md @@ -20,7 +20,7 @@ remote of the project. For the remote-tracking branch to work, we fetch bob: -``` (stderr) +``` RAD_SOCKET=/dev/null (stderr) $ git fetch bob From rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk * [new branch] master -> bob/master diff --git a/radicle-cli/examples/workflow/5-patching-maintainer.md b/radicle-cli/examples/workflow/5-patching-maintainer.md index be788d44..e9707353 100644 --- a/radicle-cli/examples/workflow/5-patching-maintainer.md +++ b/radicle-cli/examples/workflow/5-patching-maintainer.md @@ -1,13 +1,11 @@ Back to being the project maintainer. -Changes have been proposed by another person (or peer) via a radicle patch. To follow changes by another, we must 'track' them. +Changes have been proposed by another person (or peer) via a radicle patch. To +follow changes by another, we must 'track' them. ``` $ rad track did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --alias bob ✓ Tracking policy updated for z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (bob) -$ rad sync --fetch -✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkt67…v4N1tRk.. -✓ Fetched repository from 1 seed(s) ``` Additionally, we need to add a new 'git remote' to our working copy for the @@ -21,6 +19,7 @@ $ rad remote add z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --name bob ``` (stderr) $ git fetch bob +✓ Synced with 1 peer(s) From rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk * [new branch] master -> bob/master * [new branch] patches/69e881c606639691330051d7d8f013854f32fb87 -> bob/patches/69e881c606639691330051d7d8f013854f32fb87 diff --git a/radicle-cli/examples/workflow/6-pulling-contributor.md b/radicle-cli/examples/workflow/6-pulling-contributor.md index 486e2e20..2e57b5c7 100644 --- a/radicle-cli/examples/workflow/6-pulling-contributor.md +++ b/radicle-cli/examples/workflow/6-pulling-contributor.md @@ -19,7 +19,7 @@ Now let's checkout `master` and pull the maintainer's changes: $ git checkout master Your branch is up to date with 'rad/master'. ``` -``` (stderr) +``` (stderr) RAD_SOCKET=/dev/null $ git pull --all --ff From rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji f2de534..f567f69 master -> rad/master diff --git a/radicle-remote-helper/src/lib.rs b/radicle-remote-helper/src/lib.rs index a797a4f9..b96d7b58 100644 --- a/radicle-remote-helper/src/lib.rs +++ b/radicle-remote-helper/src/lib.rs @@ -7,13 +7,18 @@ mod fetch; mod list; mod push; +use std::io::IsTerminal as _; use std::path::PathBuf; use std::str::FromStr; +use std::time; use std::{env, io}; use thiserror::Error; use radicle::git; +use radicle::node::FetchResults; +use radicle::node::Handle as _; +use radicle::prelude::Id; use radicle::storage::git::transport::local::{Url, UrlError}; use radicle::storage::{ReadRepository, WriteStorage}; use radicle_cli::terminal as cli; @@ -100,6 +105,7 @@ pub fn run(profile: radicle::Profile) -> Result<(), Error> { let stdin = io::stdin(); let mut line = String::new(); let mut opts = Options::default(); + let mut sync = SyncState::new(url.repo); loop { let tokens = read_line(&stdin, &mut line)?; @@ -158,6 +164,8 @@ pub fn run(profile: radicle::Profile) -> Result<(), Error> { let oid = git::Oid::from_str(oid)?; let refstr = git::RefString::try_from(*refstr)?; + sync.run(&opts, &profile).ok(); + return fetch::run(vec![(oid, refstr)], &working, url, stored, &stdin) .map_err(Error::from); } @@ -174,6 +182,8 @@ pub fn run(profile: radicle::Profile) -> Result<(), Error> { .map_err(Error::from); } ["list"] => { + sync.run(&opts, &profile).ok(); + list::for_fetch(&url, &stored)?; } ["list", "for-push"] => { @@ -202,3 +212,77 @@ pub(crate) fn read_line<'a>(stdin: &io::Stdin, line: &'a mut String) -> io::Resu Ok(tokens) } + +/// Keeps track of sync state. +struct SyncState { + /// Whether we tried to fetch already. + fetched: bool, + /// The repo id. + rid: Id, +} + +impl SyncState { + fn new(rid: Id) -> Self { + Self { + fetched: false, + rid, + } + } + + /// Connect to local node and try to fetch refs from the network. + /// If our node is not running, or the "no-sync" option is enabled, + /// we simply do nothing. + fn run( + &mut self, + opts: &Options, + profile: &radicle::Profile, + ) -> Result<(), radicle::node::Error> { + if opts.no_sync || self.fetched { + return Ok(()); + } + let mut node = radicle::Node::new(profile.socket()); + if !node.is_running() { + return Ok(()); + } + + let target = 3; + let timeout = time::Duration::from_secs(6); + let seeds = node.seeds(self.rid)?; + let connected = seeds.connected().map(|s| s.nid).collect::>(); + + if connected.is_empty() { + return Ok(()); + } + let message = format!("Syncing with {} node(s)..", connected.len().min(target)); + let mut spinner = if io::stderr().is_terminal() { + cli::spinner_to(message, io::stderr(), io::stderr()) + } else { + cli::spinner_to(message, io::stderr(), io::sink()) + }; + + // Fetch from connected seeds. + let mut results = FetchResults::default(); + for seed in connected.into_iter() { + spinner.message(format!("Syncing with {seed}..")); + + let result = node.fetch(self.rid, seed, timeout)?; + results.push(seed, result); + + if results.success().count() >= target { + break; + } + } + + let success = results.success().count(); + if success > 0 { + spinner.message(format!("Synced with {success} peer(s)")); + spinner.finish(); + } else { + spinner.failed(); + } + // Make sure we don't try to fetch again while this program is running. + self.fetched = true; + + Ok(()) + } +}