Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T03:30:25.776Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

created_worktrees.rs

151 lines · 5.7 KB · rust
1//! A persistent registry of git worktrees that Zed itself created.
2//!
3//! Thread archival deletes a worktree's directory from disk, so it must be
4//! certain the worktree was created by Zed rather than by the user. This
5//! module records each Zed-created worktree in the local database, keyed by
6//! its path (and remote host, for remote projects), along with the creation
7//! time of the worktree's git metadata directory at the time Zed created it.
8//!
9//! Before deleting a worktree, callers re-stat that directory and compare
10//! against the recorded time. A mismatch means the worktree was removed and
11//! recreated outside Zed, so deletion must be skipped. Every failure mode
12//! (no record, unreadable creation time, mismatched time) fails safe by
13//! leaving the directory untouched.
14//!
15//! Because the registry lives in the local database, worktrees created by a
16//! different Zed install (e.g. another release channel, or another machine
17//! connecting to the same remote host) are treated as manually created and
18//! never archived. That is intentional: when in doubt, don't delete.
19
20use std::{
21    future::Future,
22    path::Path,
23    time::{Duration, SystemTime, UNIX_EPOCH},
24};
25
26use anyhow::{Context as _, Result};
27use db::kvp::KeyValueStore;
28use gpui::{App, AsyncApp, Entity};
29use project::git_store::Repository;
30use remote::{RemoteConnectionOptions, remote_connection_identity};
31use serde::{Deserialize, Serialize};
32use util::ResultExt as _;
33
34const NAMESPACE: &str = "created_git_worktrees";
35
36#[derive(Serialize, Deserialize)]
37struct CreatedWorktreeRecord {
38    created_at_seconds: u64,
39    created_at_subsec_nanos: u32,
40}
41
42fn record_key(worktree_path: &Path, remote: Option<&RemoteConnectionOptions>) -> String {
43    let host = match remote {
44        None => "local".to_string(),
45        Some(options) => remote_connection_identity(options).persistence_key(),
46    };
47    // Paths cannot contain newlines in practice, so this separator is
48    // unambiguous.
49    format!("{host}\n{}", worktree_path.display())
50}
51
52/// Records that Zed created the worktree at `worktree_path`, along with the
53/// creation time of its git metadata directory.
54pub fn record_created_worktree(
55    worktree_path: &Path,
56    remote: Option<&RemoteConnectionOptions>,
57    created_at: SystemTime,
58    cx: &App,
59) -> impl Future<Output = Result<()>> + use<> {
60    let store = KeyValueStore::global(cx);
61    let key = record_key(worktree_path, remote);
62    let value = created_at
63        .duration_since(UNIX_EPOCH)
64        .context("worktree creation time predates the unix epoch")
65        .and_then(|duration| {
66            serde_json::to_string(&CreatedWorktreeRecord {
67                created_at_seconds: duration.as_secs(),
68                created_at_subsec_nanos: duration.subsec_nanos(),
69            })
70            .context("failed to serialize created worktree record")
71        });
72    async move { store.scoped(NAMESPACE).write(key, value?).await }
73}
74
75/// Returns the recorded creation time for a worktree Zed created, or `None`
76/// if Zed has no record of creating it.
77pub fn recorded_created_at(
78    worktree_path: &Path,
79    remote: Option<&RemoteConnectionOptions>,
80    cx: &App,
81) -> Option<SystemTime> {
82    let store = KeyValueStore::global(cx);
83    let value = store
84        .scoped(NAMESPACE)
85        .read(&record_key(worktree_path, remote))
86        .log_err()??;
87    let record: CreatedWorktreeRecord = serde_json::from_str(&value).log_err()?;
88    Some(UNIX_EPOCH + Duration::new(record.created_at_seconds, record.created_at_subsec_nanos))
89}
90
91/// Looks up the creation time of a freshly created worktree's git metadata
92/// directory through the repository and records it in the registry.
93///
94/// Failures are logged and swallowed: the worktree just won't be eligible
95/// for automatic archival.
96pub async fn record_created_worktree_for_repo(
97    repo: &Entity<Repository>,
98    worktree_path: &Path,
99    remote: Option<&RemoteConnectionOptions>,
100    cx: &mut AsyncApp,
101) {
102    let receiver = repo.update(cx, |repo, _cx| {
103        repo.worktree_created_at(worktree_path.to_path_buf())
104    });
105    let created_at = match receiver.await {
106        Ok(Ok(Some(created_at))) => created_at,
107        Ok(Ok(None)) => {
108            log::warn!(
109                "Newly created worktree {} not found on disk; \
110                 it won't be eligible for automatic archival",
111                worktree_path.display()
112            );
113            return;
114        }
115        Ok(Err(error)) => {
116            log::warn!(
117                "Couldn't determine creation time for worktree {}; \
118                 it won't be eligible for automatic archival: {error:#}",
119                worktree_path.display()
120            );
121            return;
122        }
123        Err(_) => {
124            log::warn!(
125                "Worktree creation time lookup was canceled for {}",
126                worktree_path.display()
127            );
128            return;
129        }
130    };
131    let record = cx.update(|cx| record_created_worktree(worktree_path, remote, created_at, cx));
132    if let Err(error) = record.await {
133        log::warn!(
134            "Failed to record created worktree {}: {error:#}",
135            worktree_path.display()
136        );
137    }
138}
139
140/// Removes the record for a worktree, either because Zed deleted it or
141/// because the directory on disk turned out not to be the one Zed created.
142pub fn forget_created_worktree(
143    worktree_path: &Path,
144    remote: Option<&RemoteConnectionOptions>,
145    cx: &App,
146) -> impl Future<Output = Result<()>> + use<> {
147    let store = KeyValueStore::global(cx);
148    let key = record_key(worktree_path, remote);
149    async move { store.scoped(NAMESPACE).delete(key).await }
150}
151
Served at tenant.openagents/omega Member data and write actions are omitted.