cli: add 'node restore' command for undeleting nodes

Restores a deleted node to its last non-deleted content with proper
version continuity (version number continues from absolute latest,
content from last live version).

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
Kent Overstreet 2026-04-15 01:40:29 -04:00
parent 290505fc51
commit 6a5b840db3
3 changed files with 23 additions and 1 deletions

View file

@ -32,6 +32,17 @@ pub async fn cmd_node_rename(old_key: &str, new_key: &str) -> Result<()> {
Ok(())
}
pub async fn cmd_node_restore(key: &[String]) -> Result<()> {
if key.is_empty() {
bail!("node-restore requires a key");
}
super::check_dry_run();
let key = key.join(" ");
let result = memory::memory_restore(None, &key).await?;
println!("{}", result);
Ok(())
}
pub async fn cmd_render(key: &[String]) -> Result<()> {
if key.is_empty() {
bail!("render requires a key");

View file

@ -298,6 +298,7 @@ memory_tool!(memory_search, ref, keys: [Vec<String>], max_hops: [Option<u32>], e
memory_tool!(memory_link_set, mut, source: [str], target: [str], strength: [f32]);
memory_tool!(memory_link_add, mut, source: [str], target: [str]);
memory_tool!(memory_delete, mut, key: [str]);
memory_tool!(memory_restore, mut, key: [str]);
memory_tool!(memory_history, ref, key: [str], full: [Option<bool>]);
memory_tool!(memory_weight_set, mut, key: [str], weight: [f32]);
memory_tool!(memory_rename, mut, old_key: [str], new_key: [str]);

View file

@ -175,6 +175,11 @@ enum NodeCmd {
/// Node key
key: Vec<String>,
},
/// Restore a deleted node to its last live state
Restore {
/// Node key
key: Vec<String>,
},
/// Rename a node key
Rename {
/// Old key
@ -309,6 +314,9 @@ enum AdminCmd {
Topology,
/// Run consistency checks and repair
Fsck,
/// Rebuild index from capnp logs (use after fsck finds issues)
#[command(name = "repair-index")]
RepairIndex,
/// Find and merge duplicate nodes (same key, multiple UUIDs)
Dedup {
/// Apply the merge (default: dry run)
@ -397,7 +405,8 @@ impl Run for Command {
impl Run for NodeCmd {
async fn run(self) -> anyhow::Result<()> {
match self {
Self::Delete { key } => cli::node::cmd_node_delete(&key).await,
Self::Delete { key } => cli::node::cmd_node_delete(&key).await,
Self::Restore { key } => cli::node::cmd_node_restore(&key).await,
Self::Rename { old_key, new_key } => cli::node::cmd_node_rename(&old_key, &new_key).await,
}
}
@ -445,6 +454,7 @@ impl Run for AdminCmd {
Self::Health => cli::admin::cmd_health().await,
Self::Topology => cli::admin::cmd_topology().await,
Self::Fsck => cli::admin::cmd_fsck().await,
Self::RepairIndex => cli::admin::cmd_repair_index().await,
Self::Dedup { apply } => cli::admin::cmd_dedup(apply).await,
Self::DailyCheck => cli::admin::cmd_daily_check().await,
Self::LoadContext { stats } => cli::node::cmd_load_context(stats).await,