Skip to repository content151 lines · 5.7 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T03:30:25.776Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
created_worktrees.rs
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