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,
worktree_ref_state,
};
pub use scan::find_git_repos;
pub use scan::{LocalRepo, find_git_repos};
pub use worktree::{
WorktreeSnapshot, find_readme, worktree_checkout_branch, worktree_checkout_tag,
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 crate::nip34::{Nip34Binding, detect_nip34};
/// Caps nesting so pathological trees can't stall the scan.
const SCAN_MAX_DEPTH: usize = 12;
/// Walk `root` recursively and collect the paths of git repositories below it.
/// `.gitignore` and `.ignore` files are honoured.
pub fn find_git_repos(root: &Path) -> Vec<PathBuf> {
/// A git repository discovered under a scan root.
#[derive(Debug, Clone)]
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() {
return Vec::new();
}
@@ -38,4 +47,10 @@ pub fn find_git_repos(root: &Path) -> Vec<PathBuf> {
}
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("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();
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.
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.
if cache_root
.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};
use gpui::{App, AppContext};
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 profile::{Profile, ProfileStore};
pub use refresh::{RefreshGate, RefreshRequest};
pub use repo::RepoStore;
pub use repos::{RepoActivityCounts, RepoListStore};
pub use signed_git::{GraspSignals, LocalRepo, Nip34Binding, Nip34Kind};
use signed_nostr::new_backend;
#[cfg(not(target_arch = "wasm32"))]
+16 -5
View File
@@ -3,7 +3,8 @@ use std::sync::Arc;
use anyhow::Error;
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>);
@@ -13,7 +14,7 @@ impl Global for GlobalLocalReposStore {}
pub struct LocalReposStore {
pub roots: Arc<Vec<PathBuf>>,
/// Git repositories discovered under [`Self::roots`], sorted by path.
pub repos: Arc<Vec<PathBuf>>,
pub repos: Arc<Vec<LocalRepo>>,
pub scanning: bool,
scan_dirty: bool,
}
@@ -48,7 +49,7 @@ impl LocalReposStore {
self.repos = Arc::new(
self.repos
.iter()
.filter(|repo| repo.as_path() != path)
.filter(|repo| repo.path.as_path() != path)
.cloned()
.collect(),
);
@@ -75,8 +76,8 @@ impl LocalReposStore {
for root in roots.iter() {
repos.extend(find_git_repos(root));
}
repos.sort();
repos.dedup();
repos.sort_by(|a, b| a.path.cmp(&b.path));
repos.dedup_by(|a, b| a.path == b.path);
repos
});
@@ -103,3 +104,13 @@ impl LocalReposStore {
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)
.repos
.iter()
.filter(|path| {
let Some(name) = path.file_name() else {
.filter(|repo| {
let Some(name) = repo.path.file_name() else {
return true;
};
!ids.contains(&identifier_from_name(&name.to_string_lossy()))
})
.cloned()
.map(|repo| repo.path.clone())
.collect()
};
+13 -9
View File
@@ -1,6 +1,6 @@
# Local repository NIP-34 detection
Status: plan, not yet implemented.
Status: Phases 12 implemented. Phases 35 pending.
## Motivation
@@ -195,18 +195,22 @@ Implemented.
### 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.
- [ ] `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`).
- [ ] `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.
- [ ] `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`.
- [ ] `crates/signed_state/src/lib.rs`: re-export `LocalRepo`, `Nip34Binding`, `Nip34Kind` (and
`GraspSignals` if the UI needs the flags).
- [ ] `crates/signed_state/src/local_repos.rs`: add a helper that resolves a binding to a
`RepoAddr` (`signed_core::repo_addr(owner, identifier)`) when both are known.
- [x] `crates/signed_state/src/lib.rs`: re-export `LocalRepo`, `Nip34Binding`, `Nip34Kind` and
`GraspSignals`.
- [x] `crates/signed_state/src/local_repos.rs`: `local_repo_addr(&LocalRepo) -> Option<RepoAddr>`
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