]> arthur.barton.de Git - bup.git/commitdiff
Add "bup cat-file [--meta|--bupm] /branch/revision/[path]".
authorRob Browning <rlb@defaultvalue.org>
Tue, 15 Oct 2013 02:29:45 +0000 (21:29 -0500)
committerRob Browning <rlb@defaultvalue.org>
Tue, 22 Oct 2013 23:24:39 +0000 (18:24 -0500)
Signed-off-by: Rob Browning <rlb@defaultvalue.org>
Documentation/bup-cat-file.md [new file with mode: 0644]
Documentation/bup-join.md
Makefile
cmd/cat-file-cmd.py [new file with mode: 0755]
t/test-cat-file.sh [new file with mode: 0755]

diff --git a/Documentation/bup-cat-file.md b/Documentation/bup-cat-file.md
new file mode 100644 (file)
index 0000000..8f5585e
--- /dev/null
@@ -0,0 +1,51 @@
+% bup-cat-file(1) Bup %BUP_VERSION%
+% Rob Browning <rlb@defaultvalue.org>
+% %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.
index b5332f10c07fff29095f41171c7a2955f36eb2b8..ba60a83a7f6c1ae945893a05a41d06473cd8a597 100644 (file)
@@ -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
 
index c0310b04b02331bc691ba9e235a7852ba2d8dfef..cc01fcca0c33f29c917184636a85ac2b3e9f9e5a 100644 (file)
--- 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 (executable)
index 0000000..8948ff2
--- /dev/null
@@ -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 (executable)
index 0000000..804546a
--- /dev/null
@@ -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"