mirror of
https://github.com/rustic-rs/rustic.git
synced 2025-10-26 11:18:51 +00:00
Merge pull request #336 from rustic-rs/path-file
Allow file with SNAP:PATH syntax
This commit is contained in:
commit
d182a193fb
@ -1,8 +1,9 @@
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::OsStr;
|
||||
use std::mem;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use crossbeam_channel::{bounded, unbounded, Receiver, Sender};
|
||||
use derive_getters::Getters;
|
||||
use indicatif::ProgressBar;
|
||||
@ -12,7 +13,7 @@ use crate::crypto::hash;
|
||||
use crate::id::Id;
|
||||
use crate::index::IndexedBackend;
|
||||
|
||||
use super::Node;
|
||||
use super::{Metadata, Node, NodeType};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Getters)]
|
||||
pub struct Tree {
|
||||
@ -54,22 +55,25 @@ impl Tree {
|
||||
Ok(serde_json::from_slice(&data)?)
|
||||
}
|
||||
|
||||
pub fn subtree_id(be: &impl IndexedBackend, mut id: Id, path: &Path) -> Result<Id> {
|
||||
for p in path.iter() {
|
||||
let p = p.to_str().unwrap();
|
||||
// TODO: check for root instead
|
||||
if p == "/" {
|
||||
continue;
|
||||
pub fn node_from_path(be: &impl IndexedBackend, id: Id, path: &Path) -> Result<Node> {
|
||||
let mut node = Node::new_node(OsStr::new(""), NodeType::Dir, Metadata::default());
|
||||
node.set_subtree(id);
|
||||
for p in path.components() {
|
||||
match p {
|
||||
Component::RootDir | Component::Prefix(_) => {}
|
||||
Component::Normal(p) => {
|
||||
let id = node.subtree().ok_or_else(|| anyhow!("{p:?} is no dir"))?;
|
||||
let tree = Tree::from_backend(be, id)?;
|
||||
node = tree
|
||||
.nodes
|
||||
.into_iter()
|
||||
.find(|node| node.name() == p)
|
||||
.ok_or_else(|| anyhow!("{p:?} not found"))?;
|
||||
}
|
||||
_ => bail!("path should not contain current or parent dir, path: {path:?}"),
|
||||
}
|
||||
let tree = Tree::from_backend(be, id)?;
|
||||
let node = tree
|
||||
.nodes()
|
||||
.iter()
|
||||
.find(|node| node.name() == p)
|
||||
.ok_or_else(|| anyhow!("{} not found", p))?;
|
||||
id = node.subtree().ok_or_else(|| anyhow!("{} is no dir", p))?;
|
||||
}
|
||||
Ok(id)
|
||||
Ok(node)
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,8 +101,14 @@ impl<BE> NodeStreamer<BE>
|
||||
where
|
||||
BE: IndexedBackend,
|
||||
{
|
||||
pub fn new(be: BE, id: Id) -> Result<Self> {
|
||||
let inner = Tree::from_backend(&be, id)?.nodes.into_iter();
|
||||
pub fn new(be: BE, node: &Node) -> Result<Self> {
|
||||
let inner = if node.is_dir() {
|
||||
Tree::from_backend(&be, node.subtree.unwrap())?
|
||||
.nodes
|
||||
.into_iter()
|
||||
} else {
|
||||
vec![node.clone()].into_iter()
|
||||
};
|
||||
Ok(Self {
|
||||
inner,
|
||||
open_iterators: Vec::new(),
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{anyhow, Result};
|
||||
use clap::{Parser, Subcommand};
|
||||
use indicatif::ProgressBar;
|
||||
|
||||
@ -93,7 +93,8 @@ fn cat_tree(
|
||||
let (id, path) = opts.snap.split_once(':').unwrap_or((&opts.snap, ""));
|
||||
let snap = SnapshotFile::from_str(be, id, |sn| sn.matches(&opts.filter), progress_counter(""))?;
|
||||
let index = IndexBackend::new(be, progress_counter(""))?;
|
||||
let id = Tree::subtree_id(&index, snap.tree, Path::new(path))?;
|
||||
let node = Tree::node_from_path(&index, snap.tree, Path::new(path))?;
|
||||
let id = node.subtree.ok_or_else(|| anyhow!("{path} is no dir"))?;
|
||||
let data = index.blob_from_backend(&BlobType::Tree, &id)?;
|
||||
println!("{}", String::from_utf8(data.to_vec())?);
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use clap::Parser;
|
||||
|
||||
use super::{progress_counter, RusticConfig};
|
||||
@ -46,12 +46,12 @@ pub(super) fn execute(
|
||||
let snap2 = &snaps[1];
|
||||
|
||||
let index = IndexBackend::new(be, progress_counter(""))?;
|
||||
let id1 = Tree::subtree_id(&index, snap1.tree, Path::new(path1))?;
|
||||
let id2 = Tree::subtree_id(&index, snap2.tree, Path::new(path2))?;
|
||||
let node1 = Tree::node_from_path(&index, snap1.tree, Path::new(path1))?;
|
||||
let node2 = Tree::node_from_path(&index, snap2.tree, Path::new(path2))?;
|
||||
|
||||
diff(
|
||||
NodeStreamer::new(index.clone(), id1)?,
|
||||
NodeStreamer::new(index, id2)?,
|
||||
NodeStreamer::new(index.clone(), &node1)?,
|
||||
NodeStreamer::new(index, &node2)?,
|
||||
true,
|
||||
)
|
||||
}
|
||||
@ -64,14 +64,25 @@ pub(super) fn execute(
|
||||
p.finish();
|
||||
|
||||
let index = IndexBackend::new(be, progress_counter(""))?;
|
||||
let id1 = Tree::subtree_id(&index, snap1.tree, Path::new(path1))?;
|
||||
let node1 = Tree::node_from_path(&index, snap1.tree, Path::new(path1))?;
|
||||
let path2 = PathBuf::from(path2);
|
||||
let is_dir = path2
|
||||
.metadata()
|
||||
.with_context(|| format!("Error accessing {path2:?}"))?
|
||||
.is_dir();
|
||||
let src = LocalSource::new(opts.ignore_opts, path2.clone())?.map(|item| {
|
||||
let (path, node) = item?;
|
||||
Ok((path.strip_prefix(&path2)?.to_path_buf(), node))
|
||||
let path = if is_dir {
|
||||
// remove given path prefix for dirs as local path
|
||||
path.strip_prefix(&path2)?.to_path_buf()
|
||||
} else {
|
||||
// ensure that we really get the filename if local path is a file
|
||||
path2.file_name().unwrap().into()
|
||||
};
|
||||
Ok((path, node))
|
||||
});
|
||||
|
||||
diff(NodeStreamer::new(index, id1)?, src, false)
|
||||
diff(NodeStreamer::new(index, &node1)?, src, false)
|
||||
}
|
||||
(None, _) => bail!("cannot use local path as first argument"),
|
||||
}
|
||||
|
||||
@ -29,9 +29,9 @@ pub(super) fn execute(
|
||||
let (id, path) = opts.snap.split_once(':').unwrap_or((&opts.snap, ""));
|
||||
let snap = SnapshotFile::from_str(be, id, |sn| sn.matches(&opts.filter), progress_counter(""))?;
|
||||
let index = IndexBackend::new(be, progress_counter(""))?;
|
||||
let tree = Tree::subtree_id(&index, snap.tree, Path::new(path))?;
|
||||
let node = Tree::node_from_path(&index, snap.tree, Path::new(path))?;
|
||||
|
||||
for item in NodeStreamer::new(index, tree)? {
|
||||
for item in NodeStreamer::new(index, &node)? {
|
||||
let (path, _) = item?;
|
||||
println!("{:?} ", path);
|
||||
}
|
||||
|
||||
@ -79,12 +79,12 @@ pub(super) fn execute(
|
||||
let snap = SnapshotFile::from_str(be, id, |sn| sn.matches(&opts.filter), progress_counter(""))?;
|
||||
|
||||
let index = IndexBackend::new(be, progress_counter(""))?;
|
||||
let tree = Tree::subtree_id(&index, snap.tree, Path::new(path))?;
|
||||
let node = Tree::node_from_path(&index, snap.tree, Path::new(path))?;
|
||||
|
||||
let dest = LocalBackend::new(&opts.dest)?;
|
||||
|
||||
let p = progress_spinner("collecting file information...");
|
||||
let file_infos = allocate_and_collect(&dest, index.clone(), tree, &opts)?;
|
||||
let file_infos = allocate_and_collect(&dest, index.clone(), &node, &opts)?;
|
||||
p.finish();
|
||||
info!("total restore size: {}", bytes(file_infos.total_size));
|
||||
if file_infos.matched_size > 0 {
|
||||
@ -113,7 +113,7 @@ pub(super) fn execute(
|
||||
|
||||
if !opts.dry_run {
|
||||
let p = progress_spinner("setting metadata...");
|
||||
restore_metadata(&dest, index, tree, &opts)?;
|
||||
restore_metadata(&dest, index, &node, &opts)?;
|
||||
p.finish();
|
||||
}
|
||||
|
||||
@ -125,7 +125,7 @@ pub(super) fn execute(
|
||||
fn allocate_and_collect(
|
||||
dest: &LocalBackend,
|
||||
index: impl IndexedBackend + Unpin,
|
||||
tree: Id,
|
||||
node: &Node,
|
||||
opts: &Opts,
|
||||
) -> Result<FileInfos> {
|
||||
let dest_path = Path::new(&opts.dest);
|
||||
@ -231,7 +231,7 @@ fn allocate_and_collect(
|
||||
.filter_map(Result::ok); // TODO: print out the ignored error
|
||||
let mut next_dst = dst_iter.next();
|
||||
|
||||
let mut node_streamer = NodeStreamer::new(index.clone(), tree)?;
|
||||
let mut node_streamer = NodeStreamer::new(index.clone(), node)?;
|
||||
let mut next_node = node_streamer.next().transpose()?;
|
||||
|
||||
loop {
|
||||
@ -353,11 +353,11 @@ fn restore_contents(
|
||||
fn restore_metadata(
|
||||
dest: &LocalBackend,
|
||||
index: impl IndexedBackend + Unpin,
|
||||
tree: Id,
|
||||
node: &Node,
|
||||
opts: &Opts,
|
||||
) -> Result<()> {
|
||||
// walk over tree in repository and compare with tree in dest
|
||||
let mut node_streamer = NodeStreamer::new(index, tree)?;
|
||||
let mut node_streamer = NodeStreamer::new(index, node)?;
|
||||
let mut dir_stack = Vec::new();
|
||||
while let Some((path, node)) = node_streamer.next().transpose()? {
|
||||
match node.node_type() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user