cli: `git pull` to update patches in working copy
We enable users to pull updates to a patch by calling `git pull` on the patch branch.
This commit is contained in:
parent
6b143f5f8f
commit
3898fece08
|
|
@ -0,0 +1,117 @@
|
|||
Let's look at how patch updates work.
|
||||
|
||||
Alice creates a project and Bob clones it.
|
||||
|
||||
``` ~alice
|
||||
$ rad init --name heartwood --description "radicle heartwood protocol & stack" --no-confirm --announce
|
||||
|
||||
Initializing radicle 👾 project in .
|
||||
|
||||
✓ Project heartwood created
|
||||
✓ Syncing inventory..
|
||||
✓ Announcing inventory..
|
||||
|
||||
Your project's Repository ID (RID) is rad:zhbMU4DUXrzB8xT6qAJh6yZ7bFMK.
|
||||
You can show it any time by running `rad .`
|
||||
```
|
||||
|
||||
``` ~bob
|
||||
$ rad clone rad:zhbMU4DUXrzB8xT6qAJh6yZ7bFMK
|
||||
✓ Tracking relationship established for rad:zhbMU4DUXrzB8xT6qAJh6yZ7bFMK with scope 'all'
|
||||
✓ Fetching rad:zhbMU4DUXrzB8xT6qAJh6yZ7bFMK from z6MknSL…StBU8Vi..
|
||||
✓ Forking under z6Mkt67…v4N1tRk..
|
||||
✓ Creating checkout in ./heartwood..
|
||||
✓ Remote alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi added
|
||||
✓ Remote-tracking branch alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSL…StBU8Vi
|
||||
✓ Repository successfully cloned under [..]/heartwood/
|
||||
```
|
||||
|
||||
We wait for Alice to sync our fork.
|
||||
|
||||
``` ~bob
|
||||
$ rad node events -n 1 --timeout 1
|
||||
{"type":"refsSynced","remote":"z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi","rid":"rad:zhbMU4DUXrzB8xT6qAJh6yZ7bFMK"}
|
||||
```
|
||||
|
||||
Bob then opens a patch.
|
||||
|
||||
``` ~bob (stderr)
|
||||
$ cd heartwood
|
||||
$ git checkout -b bob/feature -q
|
||||
$ git commit --allow-empty -m "Bob's commit #1" -q
|
||||
$ git push rad -o sync -o patch.message="Bob's patch" HEAD:refs/patches
|
||||
✓ Patch 627477fdb46b9aaf3f0677c415b569cd21227b76 opened
|
||||
✓ Synced with 1 node(s)
|
||||
To rad://zhbMU4DUXrzB8xT6qAJh6yZ7bFMK/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
|
||||
* [new reference] HEAD -> refs/patches
|
||||
```
|
||||
``` ~bob
|
||||
$ git status --short --branch
|
||||
## bob/feature...rad/patches/627477fdb46b9aaf3f0677c415b569cd21227b76
|
||||
```
|
||||
|
||||
Alice checks it out.
|
||||
|
||||
``` ~alice
|
||||
$ rad patch checkout 627477f
|
||||
✓ Switched to branch patch/627477f
|
||||
✓ Branch patch/627477f setup to track rad/patches/627477fdb46b9aaf3f0677c415b569cd21227b76
|
||||
$ git show
|
||||
commit bdcdb30b3c0f513620dd0f1c24ff8f4f71de956b
|
||||
Author: radicle <radicle@localhost>
|
||||
Date: Thu Dec 15 17:28:04 2022 +0000
|
||||
|
||||
Bob's commit #1
|
||||
```
|
||||
|
||||
Bob then updates the patch.
|
||||
|
||||
``` ~bob (stderr)
|
||||
$ git commit --allow-empty -m "Bob's commit #2" -q
|
||||
$ git push rad -o sync -o patch.message="Updated."
|
||||
✓ Patch 627477f updated to c4114446af35501300c68571cfb07a6f5c7e1eef
|
||||
✓ Synced with 1 node(s)
|
||||
To rad://zhbMU4DUXrzB8xT6qAJh6yZ7bFMK/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
|
||||
bdcdb30..cad2666 bob/feature -> patches/627477fdb46b9aaf3f0677c415b569cd21227b76
|
||||
```
|
||||
|
||||
Alice pulls the update.
|
||||
|
||||
``` ~alice
|
||||
$ rad patch show 627477f
|
||||
╭──────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Title Bob's patch │
|
||||
│ Patch 627477fdb46b9aaf3f0677c415b569cd21227b76 │
|
||||
│ Author did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk │
|
||||
│ Head cad2666a8a2250e4dee175ed5044be2c251ff08b │
|
||||
│ Commits ahead 2, behind 0 │
|
||||
│ Status open │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ cad2666 Bob's commit #2 │
|
||||
│ bdcdb30 Bob's commit #1 │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ ● opened by bob (z6Mkt67…v4N1tRk) [ ... ] │
|
||||
│ ↑ updated to c4114446af35501300c68571cfb07a6f5c7e1eef (cad2666) [ ... ] │
|
||||
╰──────────────────────────────────────────────────────────────────────────────╯
|
||||
$ git ls-remote rad
|
||||
f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 refs/heads/master
|
||||
cad2666a8a2250e4dee175ed5044be2c251ff08b refs/heads/patches/627477fdb46b9aaf3f0677c415b569cd21227b76
|
||||
```
|
||||
``` ~alice
|
||||
$ git fetch rad
|
||||
$ git status --short --branch
|
||||
## patch/627477f...rad/patches/627477fdb46b9aaf3f0677c415b569cd21227b76 [behind 1]
|
||||
```
|
||||
``` ~alice
|
||||
$ git pull
|
||||
Updating bdcdb30..cad2666
|
||||
Fast-forward
|
||||
```
|
||||
``` ~alice
|
||||
$ git show
|
||||
commit cad2666a8a2250e4dee175ed5044be2c251ff08b
|
||||
Author: radicle <radicle@localhost>
|
||||
Date: Thu Dec 15 17:28:04 2022 +0000
|
||||
|
||||
Bob's commit #2
|
||||
```
|
||||
|
|
@ -62,7 +62,7 @@ f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 refs/remotes/rad/master
|
|||
42d894a83c9c356552a57af09ccdbd5587a99045 refs/remotes/rad/patches/2647168c23e7c2b2c1936d695443944e143bc3f7
|
||||
```
|
||||
```
|
||||
$ git ls-remote rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi 'refs/heads/patches/*'
|
||||
$ git ls-remote rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji 'refs/heads/patches/*'
|
||||
42d894a83c9c356552a57af09ccdbd5587a99045 refs/heads/patches/2647168c23e7c2b2c1936d695443944e143bc3f7
|
||||
$ git ls-remote rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi 'refs/cobs/*'
|
||||
2647168c23e7c2b2c1936d695443944e143bc3f7 refs/cobs/xyz.radicle.patch/2647168c23e7c2b2c1936d695443944e143bc3f7
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ Your branch is up to date with 'rad/master'.
|
|||
$ git pull --all --ff
|
||||
From rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
f2de534..f567f69 master -> rad/master
|
||||
27857ec..f567f69 patches/50e29a111972f3b7d2123c5057de5bdf09bc7b1c -> rad/patches/50e29a111972f3b7d2123c5057de5bdf09bc7b1c
|
||||
From rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
f2de534..f567f69 master -> alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master
|
||||
```
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ pub fn run(
|
|||
let patch_branch =
|
||||
// SAFETY: Patch IDs are valid refstrings.
|
||||
git::refname!("patch").join(RefString::try_from(term::format::cob(patch_id)).unwrap());
|
||||
let commit = find_patch_commit(&patch, &patch_branch, stored, working)?;
|
||||
let commit = find_patch_commit(&patch, stored, working)?;
|
||||
|
||||
// Create patch branch and switch to it.
|
||||
working.branch(patch_branch.as_str(), &commit, true)?;
|
||||
|
|
@ -53,7 +53,6 @@ pub fn run(
|
|||
/// fetch it from storage first.
|
||||
fn find_patch_commit<'a>(
|
||||
patch: &Patch,
|
||||
patch_branch: &RefString,
|
||||
stored: &Repository,
|
||||
working: &'a git::raw::Repository,
|
||||
) -> anyhow::Result<git::raw::Commit<'a>> {
|
||||
|
|
@ -62,26 +61,10 @@ fn find_patch_commit<'a>(
|
|||
match working.find_commit(patch_head.into()) {
|
||||
Ok(commit) => Ok(commit),
|
||||
Err(e) if git::ext::is_not_found_err(&e) => {
|
||||
let (_, rev) = patch.latest();
|
||||
let author = *rev.author().id();
|
||||
let remote = stored.remote(&author)?;
|
||||
let url = git::url::File::new(stored.path());
|
||||
|
||||
// Find a ref in storage that points to our patch, so that we can fetch the patch
|
||||
// objects into our working copy.
|
||||
let (refstr, _) = remote
|
||||
.refs
|
||||
.iter()
|
||||
.find(|(_, o)| **o == patch_head)
|
||||
.ok_or(anyhow!("patch ref for {patch_head} not found in storage"))?;
|
||||
let remote_branch = git::refs::workdir::remote_branch(
|
||||
&RefString::try_from(author.as_key().to_human())?,
|
||||
patch_branch,
|
||||
);
|
||||
let url = git::Url::from(stored.id).with_namespace(*author);
|
||||
|
||||
// Fetch only the ref pointing to the patch revision.
|
||||
working.remote_anonymous(url.to_string().as_str())?.fetch(
|
||||
&[&format!("{refstr}:{remote_branch}")],
|
||||
&[patch_head.to_string()],
|
||||
None,
|
||||
None,
|
||||
)?;
|
||||
|
|
|
|||
|
|
@ -1000,6 +1000,39 @@ fn rad_merge_after_update() {
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rad_patch_pull_update() {
|
||||
logger::init(log::Level::Debug);
|
||||
|
||||
let mut environment = Environment::new();
|
||||
let alice = environment.node(Config::new(Alias::new("alice")));
|
||||
let bob = environment.node(Config::new(Alias::new("bob")));
|
||||
let working = environment.tmp().join("working");
|
||||
|
||||
fixtures::repository(working.join("alice"));
|
||||
|
||||
let alice = alice.spawn();
|
||||
let mut bob = bob.spawn();
|
||||
|
||||
bob.connect(&alice).converge([&alice]);
|
||||
|
||||
formula(&environment.tmp(), "examples/rad-patch-pull-update.md")
|
||||
.unwrap()
|
||||
.home(
|
||||
"alice",
|
||||
working.join("alice"),
|
||||
[("RAD_HOME", alice.home.path().display())],
|
||||
)
|
||||
.home(
|
||||
"bob",
|
||||
bob.home.path(),
|
||||
[("RAD_HOME", bob.home.path().display())],
|
||||
)
|
||||
.run()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn framework_home() {
|
||||
logger::init(log::Level::Debug);
|
||||
|
||||
|
|
|
|||
|
|
@ -95,9 +95,16 @@ impl<'de> Deserialize<'de> for ObjectId {
|
|||
|
||||
impl From<&ObjectId> for Component<'_> {
|
||||
fn from(id: &ObjectId) -> Self {
|
||||
let refstr = RefString::try_from(id.0.to_string())
|
||||
.expect("collaborative object id's are valid ref strings");
|
||||
let refstr = RefString::from(*id);
|
||||
|
||||
Component::from_refstr(refstr)
|
||||
.expect("collaborative object id's are valid refname components")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ObjectId> for RefString {
|
||||
fn from(id: ObjectId) -> Self {
|
||||
RefString::try_from(id.0.to_string())
|
||||
.expect("collaborative object id's are valid ref strings")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
|
|
@ -27,7 +28,7 @@ pub enum Error {
|
|||
|
||||
/// Run a git fetch command.
|
||||
pub fn run<R: ReadRepository>(
|
||||
mut refs: Vec<String>,
|
||||
mut refs: Vec<(git::Oid, git::RefString)>,
|
||||
working: &Path,
|
||||
url: Url,
|
||||
stored: R,
|
||||
|
|
@ -38,8 +39,11 @@ pub fn run<R: ReadRepository>(
|
|||
loop {
|
||||
let tokens = read_line(stdin, &mut line)?;
|
||||
match tokens.as_slice() {
|
||||
["fetch", _oid, refstr] => {
|
||||
refs.push(refstr.to_string());
|
||||
["fetch", oid, refstr] => {
|
||||
let oid = git::Oid::from_str(oid)?;
|
||||
let refstr = git::RefString::try_from(*refstr)?;
|
||||
|
||||
refs.push((oid, refstr));
|
||||
}
|
||||
// An empty line means end of input.
|
||||
[] => break,
|
||||
|
|
@ -50,12 +54,12 @@ pub fn run<R: ReadRepository>(
|
|||
|
||||
// Verify them and prepare the final refspecs.
|
||||
let mut refspecs = Vec::new();
|
||||
for refstr in refs {
|
||||
let refstr = git::RefString::try_from(refstr)?;
|
||||
for (oid, refstr) in refs {
|
||||
if let Some(nid) = url.namespace {
|
||||
refspecs.push(nid.to_namespace().join(refstr).to_string());
|
||||
} else {
|
||||
refspecs.push(refstr.to_string());
|
||||
// Just fetch the object directly in this case, it's simpler and faster.
|
||||
refspecs.push(oid.to_string());
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ mod list;
|
|||
mod push;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::{env, io};
|
||||
|
||||
use thiserror::Error;
|
||||
|
|
@ -40,6 +41,9 @@ pub enum Error {
|
|||
/// Git error.
|
||||
#[error("git: {0}")]
|
||||
Git(#[from] git::raw::Error),
|
||||
/// Invalid reference name.
|
||||
#[error("invalid ref: {0}")]
|
||||
InvalidRef(#[from] radicle::git::fmt::Error),
|
||||
/// Storage error.
|
||||
#[error(transparent)]
|
||||
Storage(#[from] radicle::storage::Error),
|
||||
|
|
@ -90,6 +94,8 @@ pub fn run(profile: radicle::Profile) -> Result<(), Error> {
|
|||
let working = env::var("GIT_DIR")
|
||||
.map(PathBuf::from)
|
||||
.map_err(|_| Error::NoGitDir)?;
|
||||
// Whether we should output debug logs.
|
||||
let debug = env::var("RAD_DEBUG").is_ok();
|
||||
|
||||
let stdin = io::stdin();
|
||||
let mut line = String::new();
|
||||
|
|
@ -98,6 +104,10 @@ pub fn run(profile: radicle::Profile) -> Result<(), Error> {
|
|||
loop {
|
||||
let tokens = read_line(&stdin, &mut line)?;
|
||||
|
||||
if debug {
|
||||
eprintln!("git-remote-rad: {:?}", &tokens);
|
||||
}
|
||||
|
||||
match tokens.as_slice() {
|
||||
["capabilities"] => {
|
||||
println!("option");
|
||||
|
|
@ -144,8 +154,11 @@ pub fn run(profile: radicle::Profile) -> Result<(), Error> {
|
|||
["option", ..] => {
|
||||
println!("unsupported");
|
||||
}
|
||||
["fetch", _oid, refstr] => {
|
||||
return fetch::run(vec![refstr.to_string()], &working, url, stored, &stdin)
|
||||
["fetch", oid, refstr] => {
|
||||
let oid = git::Oid::from_str(oid)?;
|
||||
let refstr = git::RefString::try_from(*refstr)?;
|
||||
|
||||
return fetch::run(vec![(oid, refstr)], &working, url, stored, &stdin)
|
||||
.map_err(Error::from);
|
||||
}
|
||||
["push", refspec] => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use thiserror::Error;
|
||||
|
||||
use radicle::cob;
|
||||
use radicle::git;
|
||||
use radicle::storage::git::transport::local::Url;
|
||||
use radicle::storage::ReadRepository;
|
||||
|
|
@ -16,10 +17,13 @@ pub enum Error {
|
|||
/// Git error.
|
||||
#[error(transparent)]
|
||||
Git(#[from] radicle::git::ext::Error),
|
||||
/// COB store error.
|
||||
#[error(transparent)]
|
||||
CobStore(#[from] cob::store::Error),
|
||||
}
|
||||
|
||||
/// List refs for fetching (`git fetch` and `git ls-remote`).
|
||||
pub fn for_fetch<R: ReadRepository>(url: &Url, stored: &R) -> Result<(), Error> {
|
||||
pub fn for_fetch<R: ReadRepository + cob::Store>(url: &Url, stored: &R) -> Result<(), Error> {
|
||||
if let Some(namespace) = url.namespace {
|
||||
// Listing namespaced refs.
|
||||
for (name, oid) in stored.references_of(&namespace)? {
|
||||
|
|
@ -36,6 +40,11 @@ pub fn for_fetch<R: ReadRepository>(url: &Url, stored: &R) -> Result<(), Error>
|
|||
println!("{oid} {name}");
|
||||
}
|
||||
}
|
||||
// List the patch refs, but don't abort if there's an error, as this would break
|
||||
// all fetch behavior. Instead, just output an error to the user.
|
||||
if let Err(e) = patch_refs(stored) {
|
||||
eprintln!("remote: error listing patch refs: {e}");
|
||||
}
|
||||
}
|
||||
println!();
|
||||
|
||||
|
|
@ -46,9 +55,24 @@ pub fn for_fetch<R: ReadRepository>(url: &Url, stored: &R) -> Result<(), Error>
|
|||
pub fn for_push<R: ReadRepository>(profile: &Profile, stored: &R) -> Result<(), Error> {
|
||||
// Only our own refs can be pushed to.
|
||||
for (name, oid) in stored.references_of(profile.id())? {
|
||||
println!("{oid} {name}");
|
||||
// Only branches and tags can be pushed to.
|
||||
if name.starts_with(git::refname!("refs/heads").as_str())
|
||||
|| name.starts_with(git::refname!("refs/tags").as_str())
|
||||
{
|
||||
println!("{oid} {name}");
|
||||
}
|
||||
}
|
||||
println!();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// List canonical patch references. These are magic refs that can be used to pull patch updates.
|
||||
fn patch_refs<R: ReadRepository + cob::Store>(stored: &R) -> Result<(), Error> {
|
||||
let patches = radicle::cob::patch::Patches::open(stored)?;
|
||||
for patch in patches.all()? {
|
||||
let (id, patch) = patch?;
|
||||
println!("{} {}", patch.head(), git::refs::storage::patch(&id));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -318,7 +318,7 @@ fn patch_open<G: Signer>(
|
|||
//
|
||||
// refs/namespaces/<nid>/refs/heads/patches/<patch-id>
|
||||
//
|
||||
let refname = git::refs::storage::patch(nid, &patch);
|
||||
let refname = git::refs::storage::patch(&patch).with_namespace(nid.into());
|
||||
let _ = stored.raw().reference(
|
||||
refname.as_str(),
|
||||
commit.id(),
|
||||
|
|
@ -493,7 +493,7 @@ fn patch_merge<G: Signer>(
|
|||
// Note that we don't return an error if we can't delete the refs, since it's
|
||||
// not critical.
|
||||
let nid = signer.public_key();
|
||||
let stored_ref = git::refs::storage::patch(nid, &patch.id);
|
||||
let stored_ref = git::refs::storage::patch(&patch.id).with_namespace(nid.into());
|
||||
let working_ref = git::refs::workdir::patch_upstream(&patch.id);
|
||||
|
||||
stored
|
||||
|
|
|
|||
|
|
@ -230,15 +230,14 @@ pub mod refs {
|
|||
|
||||
/// A patch reference.
|
||||
///
|
||||
/// `refs/namespaces/<remote>/refs/heads/patches/<object_id>`
|
||||
/// `refs/heads/patches/<object_id>`
|
||||
///
|
||||
pub fn patch<'a>(remote: &RemoteId, object_id: &cob::ObjectId) -> Namespaced<'a> {
|
||||
pub fn patch<'a>(object_id: &cob::ObjectId) -> Qualified<'a> {
|
||||
Qualified::from_components(
|
||||
component!("heads"),
|
||||
component!("patches"),
|
||||
Some(object_id.into()),
|
||||
)
|
||||
.with_namespace(remote.into())
|
||||
}
|
||||
|
||||
/// Draft references.
|
||||
|
|
|
|||
|
|
@ -408,6 +408,7 @@ impl Seeds {
|
|||
}
|
||||
|
||||
/// Announcement result returned by [`Node::announce`].
|
||||
#[derive(Debug)]
|
||||
pub struct AnnounceResult {
|
||||
/// Nodes that timed out.
|
||||
pub timeout: Vec<NodeId>,
|
||||
|
|
@ -416,6 +417,7 @@ pub struct AnnounceResult {
|
|||
}
|
||||
|
||||
/// A sync event, emitted by [`Node::announce`].
|
||||
#[derive(Debug)]
|
||||
pub enum AnnounceEvent {
|
||||
/// Refs were synced with the given node.
|
||||
RefsSynced { remote: NodeId },
|
||||
|
|
@ -640,7 +642,16 @@ impl Node {
|
|||
stream.set_read_timeout(Some(timeout))?;
|
||||
|
||||
Ok(BufReader::new(stream).lines().map(move |l| {
|
||||
let l = l?;
|
||||
let l = l.map_err(|e| {
|
||||
if e.kind() == io::ErrorKind::WouldBlock {
|
||||
io::Error::new(
|
||||
io::ErrorKind::TimedOut,
|
||||
"timed out reading from control socket",
|
||||
)
|
||||
} else {
|
||||
e
|
||||
}
|
||||
})?;
|
||||
let v = json::from_str(&l).map_err(|e| CallError::InvalidJson {
|
||||
response: l,
|
||||
error: e,
|
||||
|
|
@ -680,7 +691,7 @@ impl Node {
|
|||
}
|
||||
Ok(_) => {}
|
||||
|
||||
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
Err(e) if e.kind() == io::ErrorKind::TimedOut => {
|
||||
timeout.extend(seeds.iter());
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue