aboutsummaryrefslogtreecommitdiff
path: root/src/app/api/worktrees.rs
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-08-28 18:25:07 +0300
committerGitHub <noreply@github.com>2026-08-28 18:25:07 +0300
commit095f1337d6502081658973ae3487dee8c6b34e1a (patch)
tree4174cac82dd51dabcc1fcef57b45df2462ef1918 /src/app/api/worktrees.rs
parent13a9f6402fb77a31499bf758b56ca42b79936706 (diff)
fix: trust worktree repositories per request (#3344)
refs #3044
Diffstat (limited to 'src/app/api/worktrees.rs')
-rw-r--r--src/app/api/worktrees.rs101
1 files changed, 77 insertions, 24 deletions
diff --git a/src/app/api/worktrees.rs b/src/app/api/worktrees.rs
index 774b981a..4a3e23a8 100644
--- a/src/app/api/worktrees.rs
+++ b/src/app/api/worktrees.rs
@@ -50,11 +50,18 @@ impl App {
id: String,
params: WorktreeListParams,
) -> String {
- let source = match self.resolve_worktree_list_source(params.workspace_id, params.cwd) {
+ let source = match self.resolve_worktree_list_source(
+ params.workspace_id,
+ params.cwd,
+ params.trust_repository,
+ ) {
Ok(source) => source,
Err(err) => return encode_error(id, err.code, err.message),
};
- let entries = match crate::worktree::list_existing_worktrees(&source.source_repo_root) {
+ let entries = match crate::worktree::list_existing_worktrees(
+ &source.source_repo_root,
+ params.trust_repository,
+ ) {
Ok(entries) => entries,
Err(err) => return encode_error(id, "worktree_list_failed", err),
};
@@ -88,7 +95,12 @@ impl App {
Ok(source) => source,
Err(err) => return encode_error(id, err.code, err.message),
};
- let entry = match self.find_worktree_entry(&source, params.path, params.branch) {
+ let entry = match self.find_worktree_entry(
+ &source,
+ params.path,
+ params.branch,
+ params.trust_repository,
+ ) {
Ok(entry) => entry,
Err(err) => return encode_error(id, err.code, err.message),
};
@@ -235,6 +247,7 @@ impl App {
&mut self,
workspace_id: Option<String>,
cwd: Option<String>,
+ trust_repository: bool,
) -> Result<WorktreeSource, ApiFailure> {
if workspace_id.is_some() && cwd.is_some() {
return Err(ApiFailure::new(
@@ -250,7 +263,7 @@ impl App {
format!("workspace {workspace_id} not found"),
));
};
- return self.worktree_list_source_from_workspace(ws_idx);
+ return self.worktree_list_source_from_workspace(ws_idx, trust_repository);
}
if let Some(cwd) = cwd {
@@ -261,8 +274,13 @@ impl App {
"Herdr worktree actions require a path inside a Git work tree",
)
})?;
- let workspace_idx = self.list_source_workspace_idx_for_space(&space);
- return Ok(worktree_source_from_space(space, workspace_idx, true));
+ let workspace_idx = self.list_source_workspace_idx_for_space(&space, trust_repository);
+ return Ok(worktree_source_from_space(
+ space,
+ workspace_idx,
+ true,
+ trust_repository,
+ ));
}
let Some(ws_idx) = self.state.active.or_else(|| {
@@ -276,7 +294,7 @@ impl App {
"workspace_id or cwd is required when no workspace is active",
));
};
- self.worktree_list_source_from_workspace(ws_idx)
+ self.worktree_list_source_from_workspace(ws_idx, trust_repository)
}
fn worktree_source_from_workspace(&self, ws_idx: usize) -> Result<WorktreeSource, ApiFailure> {
@@ -331,6 +349,7 @@ impl App {
fn worktree_list_source_from_workspace(
&self,
ws_idx: usize,
+ trust_repository: bool,
) -> Result<WorktreeSource, ApiFailure> {
let Some(ws) = self.state.workspaces.get(ws_idx) else {
return Err(ApiFailure::new(
@@ -370,11 +389,16 @@ impl App {
));
};
let workspace_idx = if space.is_linked_worktree {
- self.list_source_workspace_idx_for_space(&space)
+ self.list_source_workspace_idx_for_space(&space, trust_repository)
} else {
Some(ws_idx)
};
- Ok(worktree_source_from_space(space, workspace_idx, true))
+ Ok(worktree_source_from_space(
+ space,
+ workspace_idx,
+ true,
+ trust_repository,
+ ))
}
fn ensure_source_parent_membership(
@@ -415,9 +439,10 @@ impl App {
fn list_source_workspace_idx_for_space(
&self,
space: &crate::workspace::GitSpaceMetadata,
+ trust_repository: bool,
) -> Option<usize> {
if space.is_linked_worktree {
- let parent_checkout = parent_checkout_path_for_space(space);
+ let parent_checkout = parent_checkout_path_for_space(space, trust_repository);
self.open_workspace_idx_for_checkout(&parent_checkout)
} else {
self.find_parent_workspace_for_space(space)
@@ -475,9 +500,11 @@ impl App {
source: &WorktreeSource,
path: Option<String>,
branch: Option<String>,
+ trust_repository: bool,
) -> Result<crate::worktree::ExistingWorktree, ApiFailure> {
- let entries = crate::worktree::list_existing_worktrees(&source.source_repo_root)
- .map_err(|err| ApiFailure::new("worktree_list_failed", err))?;
+ let entries =
+ crate::worktree::list_existing_worktrees(&source.source_repo_root, trust_repository)
+ .map_err(|err| ApiFailure::new("worktree_list_failed", err))?;
if let Some(path) = path {
let expected = absolute_user_path(&path)?;
let expected = crate::worktree::canonical_or_original(&expected);
@@ -667,9 +694,10 @@ fn worktree_source_from_space(
space: crate::workspace::GitSpaceMetadata,
workspace_idx: Option<usize>,
allow_linked: bool,
+ trust_repository: bool,
) -> WorktreeSource {
let source_checkout_path = if allow_linked {
- parent_checkout_path_for_space(&space)
+ parent_checkout_path_for_space(&space, trust_repository)
} else {
space.repo_root.clone()
};
@@ -682,12 +710,15 @@ fn worktree_source_from_space(
}
}
-fn parent_checkout_path_for_space(space: &crate::workspace::GitSpaceMetadata) -> PathBuf {
+fn parent_checkout_path_for_space(
+ space: &crate::workspace::GitSpaceMetadata,
+ trust_repository: bool,
+) -> PathBuf {
if !space.is_linked_worktree {
return space.repo_root.clone();
}
- crate::worktree::list_existing_worktrees(&space.repo_root)
+ crate::worktree::list_existing_worktrees(&space.repo_root, trust_repository)
.ok()
.and_then(|entries| {
entries.into_iter().find_map(|entry| {
@@ -957,8 +988,12 @@ mod tests {
for (_, runtime) in app.terminal_runtimes.drain() {
runtime.shutdown();
}
- let remove =
- crate::worktree::build_worktree_remove_command(&repo, Path::new(&worktree.path), false);
+ let remove = crate::worktree::build_worktree_remove_command(
+ &repo,
+ Path::new(&worktree.path),
+ false,
+ false,
+ );
crate::worktree::run_worktree_command(&remove).unwrap();
let _ = std::fs::remove_dir_all(worktree_root);
let _ = std::fs::remove_dir_all(repo);
@@ -1271,8 +1306,12 @@ mod tests {
for (_, runtime) in app.terminal_runtimes.drain() {
runtime.shutdown();
}
- let remove =
- crate::worktree::build_worktree_remove_command(&repo, Path::new(&worktree.path), false);
+ let remove = crate::worktree::build_worktree_remove_command(
+ &repo,
+ Path::new(&worktree.path),
+ false,
+ false,
+ );
crate::worktree::run_worktree_command(&remove).unwrap();
let _ = std::fs::remove_dir_all(worktree_root);
let _ = std::fs::remove_dir_all(repo);
@@ -1440,7 +1479,7 @@ mod tests {
)
}));
- let remove = crate::worktree::build_worktree_remove_command(&repo, &checkout, false);
+ let remove = crate::worktree::build_worktree_remove_command(&repo, &checkout, false, false);
crate::worktree::run_worktree_command(&remove).unwrap();
let _ = std::fs::remove_dir_all(repo);
}
@@ -1518,7 +1557,7 @@ mod tests {
)
}));
- let remove = crate::worktree::build_worktree_remove_command(&repo, &checkout, false);
+ let remove = crate::worktree::build_worktree_remove_command(&repo, &checkout, false, false);
crate::worktree::run_worktree_command(&remove).unwrap();
let _ = std::fs::remove_dir_all(repo);
}
@@ -1603,6 +1642,7 @@ mod tests {
method: crate::api::schema::Method::WorktreeList(WorktreeListParams {
workspace_id: Some(app.state.workspaces[0].id.clone()),
cwd: None,
+ trust_repository: false,
}),
});
@@ -1620,7 +1660,7 @@ mod tests {
);
assert!(entry.is_linked_worktree);
- let remove = crate::worktree::build_worktree_remove_command(&repo, &checkout, false);
+ let remove = crate::worktree::build_worktree_remove_command(&repo, &checkout, false, false);
crate::worktree::run_worktree_command(&remove).unwrap();
let _ = std::fs::remove_dir_all(repo);
}
@@ -1653,10 +1693,12 @@ mod tests {
crate::api::schema::Method::WorktreeList(WorktreeListParams {
workspace_id: Some(child_id),
cwd: None,
+ trust_repository: false,
}),
crate::api::schema::Method::WorktreeList(WorktreeListParams {
workspace_id: None,
cwd: Some(checkout.display().to_string()),
+ trust_repository: false,
}),
] {
let response = app.handle_api_request(Request {
@@ -1681,7 +1723,7 @@ mod tests {
}));
}
- let remove = crate::worktree::build_worktree_remove_command(&repo, &checkout, false);
+ let remove = crate::worktree::build_worktree_remove_command(&repo, &checkout, false, false);
crate::worktree::run_worktree_command(&remove).unwrap();
let _ = std::fs::remove_dir_all(repo);
}
@@ -1710,6 +1752,7 @@ mod tests {
method: crate::api::schema::Method::WorktreeList(WorktreeListParams {
workspace_id: Some(app.state.workspaces[0].id.clone()),
cwd: None,
+ trust_repository: false,
}),
});
@@ -1767,6 +1810,7 @@ mod tests {
method: crate::api::schema::Method::WorktreeRemove(WorktreeRemoveParams {
workspace_id: child_id.clone(),
force: false,
+ trust_repository: false,
}),
},
);
@@ -1782,6 +1826,7 @@ mod tests {
method: crate::api::schema::Method::WorktreeRemove(WorktreeRemoveParams {
workspace_id: child_id,
force: true,
+ trust_repository: false,
}),
},
);
@@ -1838,6 +1883,7 @@ mod tests {
method: crate::api::schema::Method::WorktreeRemove(WorktreeRemoveParams {
workspace_id: child_id.clone(),
force: false,
+ trust_repository: false,
}),
},
);
@@ -1923,6 +1969,7 @@ mod tests {
method: crate::api::schema::Method::WorktreeRemove(WorktreeRemoveParams {
workspace_id: child_id.clone(),
force: false,
+ trust_repository: false,
}),
},
respond_to,
@@ -1996,6 +2043,7 @@ mod tests {
method: crate::api::schema::Method::WorktreeRemove(WorktreeRemoveParams {
workspace_id: child_id.clone(),
force: false,
+ trust_repository: false,
}),
},
first_tx,
@@ -2006,6 +2054,7 @@ mod tests {
method: crate::api::schema::Method::WorktreeRemove(WorktreeRemoveParams {
workspace_id: child_id,
force: false,
+ trust_repository: false,
}),
},
second_tx,
@@ -2065,6 +2114,7 @@ mod tests {
method: crate::api::schema::Method::WorktreeRemove(WorktreeRemoveParams {
workspace_id: first_id,
force: true,
+ trust_repository: false,
}),
},
first_tx,
@@ -2075,6 +2125,7 @@ mod tests {
method: crate::api::schema::Method::WorktreeRemove(WorktreeRemoveParams {
workspace_id: second_id,
force: true,
+ trust_repository: false,
}),
},
second_tx,
@@ -2110,6 +2161,7 @@ mod tests {
path: Some(checkout.display().to_string()),
label: None,
focus: false,
+ trust_repository: false,
}),
},
respond_to,
@@ -2164,6 +2216,7 @@ mod tests {
method: crate::api::schema::Method::WorktreeRemove(WorktreeRemoveParams {
workspace_id: child_id,
force: false,
+ trust_repository: false,
}),
},
respond_to,
@@ -2175,7 +2228,7 @@ mod tests {
let error: ErrorResponse = serde_json::from_str(&response).unwrap();
assert_eq!(error.error.code, "worktree_operation_in_progress");
assert!(app.event_rx.try_recv().is_err());
- let remove = crate::worktree::build_worktree_remove_command(&repo, &checkout, true);
+ let remove = crate::worktree::build_worktree_remove_command(&repo, &checkout, true, false);
let _ = crate::worktree::run_worktree_command(&remove);
let _ = std::fs::remove_dir_all(repo);
}