From 08dad7c02e3f1ffd4d9e94478369ef997e3b4cae Mon Sep 17 00:00:00 2001 From: Rob Browning Date: Mon, 14 Oct 2013 21:29:45 -0500 Subject: [PATCH] Add "bup cat-file [--meta|--bupm] /branch/revision/[path]". Signed-off-by: Rob Browning --- Documentation/bup-cat-file.md | 51 ++++++++++++++++++++++++++++ Documentation/bup-join.md | 2 +- Makefile | 1 + cmd/cat-file-cmd.py | 62 +++++++++++++++++++++++++++++++++++ t/test-cat-file.sh | 37 +++++++++++++++++++++ 5 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 Documentation/bup-cat-file.md create mode 100755 cmd/cat-file-cmd.py create mode 100755 t/test-cat-file.sh diff --git a/Documentation/bup-cat-file.md b/Documentation/bup-cat-file.md new file mode 100644 index 0000000..8f5585e --- /dev/null +++ b/Documentation/bup-cat-file.md @@ -0,0 +1,51 @@ +% bup-cat-file(1) Bup %BUP_VERSION% +% Rob Browning +% %BUP_DATE% + +# NAME + +bup-cat-file - extract archive content (low-level) + +# SYNOPSIS + +bup cat-file [--meta|--bupm] <*path*> + +# DESCRIPTION + +`bup cat-file` extracts content associated with *path* from the +archive and dumps it to standard output. If nothing special is +requested, the actual data contained by *path* (which must be a +regular file) will be dumped. + +# OPTIONS + +\--meta +: retrieve the metadata entry associated with *path*. Note that + currently this does not return the raw bytes for the entry + recorded in the relevant .bupm in the archive, but rather a + decoded and then re-encoded version. When that matters, it should + be possible (though awkward) to use `--bupm` on the parent + directory and then find the relevant entry in the output. + +\--bupm +: retrieve the .bupm file associated with *path*, which must be a + directory. + +# EXAMPLES + + # Retrieve the content of somefile. + $ bup cat-file /foo/latest/somefile > somefile-content + + # Examine the metadata associated with something. + $ bup cat-file --meta /foo/latest/something | bup meta -tvvf - + + # Examine the metadata for somedir, including the items it contains. + $ bup cat-file --bupm /foo/latest/somedir | bup meta -tvvf - + +# SEE ALSO + +`bup-join`(1), `bup-meta`(1) + +# BUP + +Part of the `bup`(1) suite. diff --git a/Documentation/bup-join.md b/Documentation/bup-join.md index b5332f1..ba60a83 100644 --- a/Documentation/bup-join.md +++ b/Documentation/bup-join.md @@ -49,7 +49,7 @@ join` reads them from stdin instead. # SEE ALSO -`bup-split`(1), `bup-save`(1), `ssh_config`(5) +`bup-split`(1), `bup-save`(1), `bup-cat-file`, `ssh_config`(5) # BUP diff --git a/Makefile b/Makefile index c0310b0..cc01fcc 100644 --- a/Makefile +++ b/Makefile @@ -86,6 +86,7 @@ runtests-python: all $(PYTHON) wvtest.py lib/bup/t/tmetadata.py runtests-cmdline: all + t/test-cat-file.sh t/test-index-check-device.sh t/test-meta.sh t/test-restore-single-file.sh diff --git a/cmd/cat-file-cmd.py b/cmd/cat-file-cmd.py new file mode 100755 index 0000000..8948ff2 --- /dev/null +++ b/cmd/cat-file-cmd.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +import sys, stat +from bup import options, git, vfs +from bup.helpers import * + +optspec = """ +bup cat-file [--meta|--bupm] /branch/revision/[path] +-- +meta print the target's metadata entry (decoded then reencoded) to stdout +bupm print the target directory's .bupm file directly to stdout +""" + +handle_ctrl_c() + +o = options.Options(optspec) +(opt, flags, extra) = o.parse(sys.argv[1:]) + +git.check_repo_or_die() +top = vfs.RefList(None) + +if not extra: + o.fatal('must specify a target') +if len(extra) > 1: + o.fatal('only one target file allowed') +if opt.bupm and opt.meta: + o.fatal('--meta and --bupm are incompatible') + +target = extra[0] + +if not re.match(r'/*[^/]+/[^/]+', target): + o.fatal("path %r doesn't include a branch and revision" % target) + +try: + n = top.lresolve(target) +except vfs.NodeError, e: + o.fatal(e) + +if isinstance(n, vfs.FakeSymlink): + # Source is actually /foo/what, i.e. a top-level commit + # like /foo/latest, which is a symlink to ../.commit/SHA. + # So dereference it. + target = n.dereference() + +if opt.bupm: + if not stat.S_ISDIR(n.mode): + o.fatal('%r is not a directory' % target) + mfile = n.metadata_file() # VFS file -- cannot close(). + if mfile: + meta_stream = mfile.open() + sys.stdout.write(meta_stream.read()) +elif opt.meta: + sys.stdout.write(n.metadata().encode()) +else: + if stat.S_ISREG(n.mode): + for b in chunkyreader(n.open()): + sys.stdout.write(b) + else: + o.fatal('%r is not a plain file' % target) + +if saved_errors: + log('warning: %d errors encountered\n' % len(saved_errors)) + sys.exit(1) diff --git a/t/test-cat-file.sh b/t/test-cat-file.sh new file mode 100755 index 0000000..804546a --- /dev/null +++ b/t/test-cat-file.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +. ./wvtest-bup.sh + +set -eo pipefail + +top="$(pwd)" +tmpdir="$(wvmktempdir)" +export BUP_DIR="$tmpdir/bup" +export GIT_DIR="$tmpdir/bup" + +bup() { "$top/bup" "$@"; } + +bup init +cd "$tmpdir" + +WVSTART "cat-file" +mkdir src +date > src/foo +WVPASS bup index src +WVPASS bup save -n src src +WVPASS bup cat-file "src/latest/$(pwd)/src/foo" > cat-foo +WVPASS diff -u src/foo cat-foo + +WVSTART "cat-file --meta" +WVPASS bup meta --create --no-paths src/foo > src-foo.meta +WVPASS bup cat-file --meta "src/latest/$(pwd)/src/foo" > cat-foo.meta +WVPASS cmp -b src-foo.meta cat-foo.meta + +WVSTART "cat-file --bupm" +WVPASS bup cat-file --bupm "src/latest/$(pwd)/src/" > bup-cat-bupm +src_hash=$(bup ls -s "src/latest/$(pwd)" | cut -d' ' -f 1) +bupm_hash=$(git ls-tree "$src_hash" | grep -F .bupm | cut -d' ' -f 3) +bupm_hash=$(echo "$bupm_hash" | cut -d' ' -f 1) +git cat-file blob "$bupm_hash" > git-cat-bupm +WVPASS cmp -b git-cat-bupm bup-cat-bupm + +rm -rf "$tmpdir" -- 2.39.2