This commit is contained in:
2026-09-14 08:50:15 +07:00
parent e556654be8
commit 1a13d4e0d7
8 changed files with 60 additions and 24 deletions
+1 -1
View File
@@ -34,7 +34,7 @@ pub use repo::{
repo_tags, root_commit, worktree_branches, worktree_current_branch, worktree_ref_exists, repo_tags, root_commit, worktree_branches, worktree_current_branch, worktree_ref_exists,
worktree_ref_state, worktree_ref_state,
}; };
pub use scan::find_git_repos; pub use scan::{LocalRepo, find_git_repos};
pub use worktree::{ pub use worktree::{
WorktreeSnapshot, find_readme, worktree_checkout_branch, worktree_checkout_tag, WorktreeSnapshot, find_readme, worktree_checkout_branch, worktree_checkout_tag,
worktree_commits_ahead, worktree_dirty, worktree_entries, worktree_read, worktree_snapshot, worktree_commits_ahead, worktree_dirty, worktree_entries, worktree_read, worktree_snapshot,
+18 -3
View File
@@ -2,12 +2,21 @@ use std::path::{Path, PathBuf};
use ignore::WalkBuilder; use ignore::WalkBuilder;
use crate::nip34::{Nip34Binding, detect_nip34};
/// Caps nesting so pathological trees can't stall the scan. /// Caps nesting so pathological trees can't stall the scan.
const SCAN_MAX_DEPTH: usize = 12; const SCAN_MAX_DEPTH: usize = 12;
/// Walk `root` recursively and collect the paths of git repositories below it. /// A git repository discovered under a scan root.
/// `.gitignore` and `.ignore` files are honoured. #[derive(Debug, Clone)]
pub fn find_git_repos(root: &Path) -> Vec<PathBuf> { pub struct LocalRepo {
pub path: PathBuf,
/// `None` for a plain repository.
pub nip34: Option<Nip34Binding>,
}
/// Walk `root` recursively and collect the git repositories below it.
pub fn find_git_repos(root: &Path) -> Vec<LocalRepo> {
if !root.is_dir() { if !root.is_dir() {
return Vec::new(); return Vec::new();
} }
@@ -38,4 +47,10 @@ pub fn find_git_repos(root: &Path) -> Vec<PathBuf> {
} }
roots roots
.into_iter()
.map(|path| {
let nip34 = detect_nip34(&path);
LocalRepo { path, nip34 }
})
.collect()
} }
+4 -1
View File
@@ -42,7 +42,10 @@ fn find_git_repos_discovers_repositories_recursively() {
std::fs::create_dir_all(outer.join(".git")).unwrap(); std::fs::create_dir_all(outer.join(".git")).unwrap();
std::fs::create_dir_all(outer.join("sub/other/.git")).unwrap(); std::fs::create_dir_all(outer.join("sub/other/.git")).unwrap();
let mut found = find_git_repos(root); let mut found: Vec<PathBuf> = find_git_repos(root)
.into_iter()
.map(|repo| repo.path)
.collect();
found.sort(); found.sort();
let mut expected = vec![ let mut expected = vec![
+3 -1
View File
@@ -350,7 +350,9 @@ impl CheckoutsStore {
// //
// The facts are the origin URL and the root commit, both CLI reads. // The facts are the origin URL and the root commit, both CLI reads.
let mut facts: Vec<(PathBuf, Option<String>, Option<String>)> = Vec::new(); let mut facts: Vec<(PathBuf, Option<String>, Option<String>)> = Vec::new();
for path in scanned.iter() { for scanned in scanned.iter() {
let path = &scanned.path;
// The browser's mirror clones share the announce URLs and EUCs. They are not user checkouts. // The browser's mirror clones share the announce URLs and EUCs. They are not user checkouts.
if cache_root if cache_root
.as_ref() .as_ref()
+2 -1
View File
@@ -16,12 +16,13 @@ use git_store::set_git_cache;
pub use git_store::{ensure_repo_mirror, open_repo_mirror, repo_mirror_path}; pub use git_store::{ensure_repo_mirror, open_repo_mirror, repo_mirror_path};
use gpui::{App, AppContext}; use gpui::{App, AppContext};
pub use inbox::{Inbox, query_inbox}; pub use inbox::{Inbox, query_inbox};
pub use local_repos::LocalReposStore; pub use local_repos::{LocalReposStore, local_repo_addr};
pub use nostr_sdk::prelude::Timestamp; pub use nostr_sdk::prelude::Timestamp;
pub use profile::{Profile, ProfileStore}; pub use profile::{Profile, ProfileStore};
pub use refresh::{RefreshGate, RefreshRequest}; pub use refresh::{RefreshGate, RefreshRequest};
pub use repo::RepoStore; pub use repo::RepoStore;
pub use repos::{RepoActivityCounts, RepoListStore}; pub use repos::{RepoActivityCounts, RepoListStore};
pub use signed_git::{GraspSignals, LocalRepo, Nip34Binding, Nip34Kind};
use signed_nostr::new_backend; use signed_nostr::new_backend;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
+16 -5
View File
@@ -3,7 +3,8 @@ use std::sync::Arc;
use anyhow::Error; use anyhow::Error;
use gpui::{App, AppContext, Context, Entity, Global, Task}; use gpui::{App, AppContext, Context, Entity, Global, Task};
use signed_git::find_git_repos; use signed_core::{RepoAddr, repo_addr};
use signed_git::{LocalRepo, find_git_repos};
struct GlobalLocalReposStore(Entity<LocalReposStore>); struct GlobalLocalReposStore(Entity<LocalReposStore>);
@@ -13,7 +14,7 @@ impl Global for GlobalLocalReposStore {}
pub struct LocalReposStore { pub struct LocalReposStore {
pub roots: Arc<Vec<PathBuf>>, pub roots: Arc<Vec<PathBuf>>,
/// Git repositories discovered under [`Self::roots`], sorted by path. /// Git repositories discovered under [`Self::roots`], sorted by path.
pub repos: Arc<Vec<PathBuf>>, pub repos: Arc<Vec<LocalRepo>>,
pub scanning: bool, pub scanning: bool,
scan_dirty: bool, scan_dirty: bool,
} }
@@ -48,7 +49,7 @@ impl LocalReposStore {
self.repos = Arc::new( self.repos = Arc::new(
self.repos self.repos
.iter() .iter()
.filter(|repo| repo.as_path() != path) .filter(|repo| repo.path.as_path() != path)
.cloned() .cloned()
.collect(), .collect(),
); );
@@ -75,8 +76,8 @@ impl LocalReposStore {
for root in roots.iter() { for root in roots.iter() {
repos.extend(find_git_repos(root)); repos.extend(find_git_repos(root));
} }
repos.sort(); repos.sort_by(|a, b| a.path.cmp(&b.path));
repos.dedup(); repos.dedup_by(|a, b| a.path == b.path);
repos repos
}); });
@@ -103,3 +104,13 @@ impl LocalReposStore {
task.detach(); task.detach();
} }
} }
/// The NIP-34 coordinate a repository's detection resolved, when both the owner
/// and the identifier were recovered.
pub fn local_repo_addr(repo: &LocalRepo) -> Option<RepoAddr> {
let binding = repo.nip34.as_ref()?;
let owner = binding.owner?;
let identifier = binding.identifier.as_deref()?;
Some(repo_addr(owner, identifier))
}
+3 -3
View File
@@ -132,13 +132,13 @@ impl SidebarPanel {
.read(cx) .read(cx)
.repos .repos
.iter() .iter()
.filter(|path| { .filter(|repo| {
let Some(name) = path.file_name() else { let Some(name) = repo.path.file_name() else {
return true; return true;
}; };
!ids.contains(&identifier_from_name(&name.to_string_lossy())) !ids.contains(&identifier_from_name(&name.to_string_lossy()))
}) })
.cloned() .map(|repo| repo.path.clone())
.collect() .collect()
}; };
+13 -9
View File
@@ -1,6 +1,6 @@
# Local repository NIP-34 detection # Local repository NIP-34 detection
Status: plan, not yet implemented. Status: Phases 12 implemented. Phases 35 pending.
## Motivation ## Motivation
@@ -195,18 +195,22 @@ Implemented.
### Phase 2 — thread `LocalRepo` through the state layer ### Phase 2 — thread `LocalRepo` through the state layer
- [ ] `crates/signed_git/src/scan.rs`: change `find_git_repos` to `Vec<LocalRepo>`; in the walk, Implemented.
- [x] `crates/signed_git/src/scan.rs`: change `find_git_repos` to `Vec<LocalRepo>`; in the walk,
`detect_nip34` each kept path. `detect_nip34` each kept path.
- [ ] `crates/signed_git/src/tests.rs`: update `find_git_repos_*` expectations to the new type - [x] `crates/signed_git/src/tests.rs`: update `find_git_repos_*` expectations to the new type
(compare `.path`). (compare `.path`).
- [ ] `crates/signed_state/src/local_repos.rs`: `repos: Arc<Vec<LocalRepo>>`; `remove(&path)` - [x] `crates/signed_state/src/local_repos.rs`: `repos: Arc<Vec<LocalRepo>>`; `remove(&path)`
filters on `.path`; the background scan already returns the richer vector unchanged. filters on `.path`; the background scan already returns the richer vector unchanged.
- [ ] `crates/signed_state/src/checkouts.rs` (~`run_refresh`): iterate `.path` instead of bare - [x] `crates/signed_state/src/checkouts.rs` (`run_refresh`): iterate `.path` instead of bare
paths when collecting `scanned`. paths when collecting `scanned`.
- [ ] `crates/signed_state/src/lib.rs`: re-export `LocalRepo`, `Nip34Binding`, `Nip34Kind` (and - [x] `crates/signed_state/src/lib.rs`: re-export `LocalRepo`, `Nip34Binding`, `Nip34Kind` and
`GraspSignals` if the UI needs the flags). `GraspSignals`.
- [ ] `crates/signed_state/src/local_repos.rs`: add a helper that resolves a binding to a - [x] `crates/signed_state/src/local_repos.rs`: `local_repo_addr(&LocalRepo) -> Option<RepoAddr>`
`RepoAddr` (`signed_core::repo_addr(owner, identifier)`) when both are known. resolves a binding when both owner and identifier are known.
- [x] `crates/workspace/src/views/sidebar/mod.rs`: minimal adaptation of the existing folder-name
dedupe to the new entry type so the tree compiles; the badge/dedupe rewrite is Phase 3.
### Phase 3 — sidebar: derive the local list and link to announcements ### Phase 3 — sidebar: derive the local list and link to announcements