]> arthur.barton.de Git - bup.git/commitdiff
Add support for "bup restore --exclude-rx <pattern> ...".
authorRob Browning <rlb@defaultvalue.org>
Sat, 18 Aug 2012 20:35:23 +0000 (15:35 -0500)
committerRob Browning <rlb@defaultvalue.org>
Sun, 24 Mar 2013 00:57:29 +0000 (19:57 -0500)
When --exclude-rx <pattern> is provided to bup restore, don't restore
any path matching <pattern>, which must be a Python regular expression
(http://docs.python.org/library/re.html).  The pattern will be
compared against the full path, without anchoring, so "x/y" will match
"ox/yard" or "box/yards".  To exclude the contents of /tmp, but not
the directory itself, use "^/tmp/.".

You may check the behavior at runtime by setting BUP_DEBUG=2 in the
environment.

Thanks to Zoran Zaric <zz@zoranzaric.de> for reporting a bug in an
earlier version of this patch.

Signed-off-by: Rob Browning <rlb@defaultvalue.org>
Reviewed-by: Zoran Zaric <zz@zoranzaric.de>
Documentation/bup-restore.md
cmd/restore-cmd.py
t/test.sh

index 986240744fd4771c429ecffdc19eb066e1d92008..ab98c00b9e402d054f3096efa9c895cad7a796f2 100644 (file)
@@ -8,7 +8,8 @@ bup-restore - extract files from a backup set
 
 # SYNOPSIS
 
-bup restore [\--outdir=*outdir*] [-v] [-q] \<paths...\>;
+bup restore [\--outdir=*outdir*] [\--exclude-rx *pattern*] [-v] [-q]
+\<paths...\>
 
 # DESCRIPTION
 
@@ -83,6 +84,28 @@ See the EXAMPLES section for a demonstration.
 \--numeric-ids
 :   restore numeric IDs (user, group, etc.) rather than names.
 
+\--exclude-rx=*pattern*
+:   exclude any path matching *pattern*, which must be a Python
+    regular expression (http://docs.python.org/library/re.html).  The
+    pattern will be compared against the full path rooted at the top
+    of the restore tree, without anchoring, so "x/y" will match
+    "ox/yard" or "box/yards".  To exclude the contents of /tmp, but
+    not the directory itself, use "^/tmp/.". (can be specified more
+    than once)
+
+    Note that the root of the restore tree (which matches '^/') is the
+    top of the archive tree being restored, and has nothing to do with
+    the filesystem destination.  Given "restore ... /foo/latest/etc/",
+    the pattern '^/passwd$' would match if a file named passwd had
+    been saved as '/foo/latest/etc/passwd'.
+
+    Examples:
+
+      * '/foo$' - exclude any file named foo
+      * '/foo/$' - exclude any directory named foo
+      * '/foo/.' - exclude the content of any directory named foo
+      * '^/tmp/.' - exclude root-level /tmp's content, but not /tmp itself
+
 -v, \--verbose
 :   increase log output.  Given once, prints every
     directory as it is restored; given twice, prints every
index 750b82f0d8ff2242ec88ef4b53befe6416d9b061..124578e42685cb65a3dab622750850121060b9ef 100755 (executable)
@@ -1,5 +1,5 @@
 #!/usr/bin/env python
-import errno, sys, stat
+import errno, sys, stat, re
 from bup import options, git, metadata, vfs
 from bup.helpers import *
 
@@ -8,6 +8,7 @@ bup restore [-C outdir] </branch/revision/path/to/dir ...>
 --
 C,outdir=   change to given outdir before extracting files
 numeric-ids restore numeric IDs (user, group, etc.) rather than names
+exclude-rx= skip paths that match the unanchored regular expression
 v,verbose   increase log output (can be used more than once)
 q,quiet     don't show progress meter
 """
@@ -187,6 +188,12 @@ def do_node(top, n, meta=None):
     meta_stream = None
     try:
         fullname = n.fullname(stop_at=top)
+        # Match behavior of index --exclude-rx with respect to paths.
+        exclude_candidate = '/' + fullname
+        if(stat.S_ISDIR(n.mode)):
+            exclude_candidate += '/'
+        if should_rx_exclude_path(exclude_candidate, exclude_rxs):
+            return
         # If this is a directory, its metadata is the first entry in
         # any .bupm file inside the directory.  Get it.
         if(stat.S_ISDIR(n.mode)):
@@ -222,6 +229,7 @@ def do_node(top, n, meta=None):
         if meta_stream:
             meta_stream.close()
 
+
 handle_ctrl_c()
 
 o = options.Options(optspec)
@@ -233,6 +241,8 @@ top = vfs.RefList(None)
 if not extra:
     o.fatal('must specify at least one filename to restore')
     
+exclude_rxs = parse_rx_excludes(flags, o.fatal)
+
 if opt.outdir:
     mkdirp(opt.outdir)
     os.chdir(opt.outdir)
index 362c51922016becbce8ec7e82e65123ae5ea7b74..58c9cca25e153bc831cdf7232d4784a4f25e9f2d 100755 (executable)
--- a/t/test.sh
+++ b/t/test.sh
@@ -795,3 +795,89 @@ $D/
 ./sub
 ./sub/foo"
 ) || WVFAIL
+
+
+# bup restore --exclude-rx ...
+(
+    set -e
+    export BUP_DIR="$TOP/buptest.tmp"
+    D=bupdata.tmp
+
+    WVSTART "restore --exclude-rx '^/foo' (root anchor)"
+    rm -rf "$D" "$BUP_DIR" buprestore.tmp
+    WVPASS bup init
+    mkdir $D
+    touch $D/a
+    touch $D/b
+    mkdir $D/sub1
+    mkdir $D/sub2
+    touch $D/sub1/a
+    touch $D/sub2/b
+    WVPASS bup index -u $D
+    bup save --strip -n bupdir $D
+    bup restore -C buprestore.tmp --exclude-rx "^/sub1/" /bupdir/latest/
+    WVPASSEQ "$(cd buprestore.tmp && find . | sort)" ".
+./a
+./b
+./sub2
+./sub2/b"
+
+    WVSTART "restore --exclude-rx '/foo$' (non-dir, tail anchor)"
+    rm -rf "$D" "$BUP_DIR" buprestore.tmp
+    WVPASS bup init
+    mkdir $D
+    touch $D/a
+    touch $D/b
+    touch $D/foo
+    mkdir $D/sub
+    mkdir $D/sub/foo
+    touch $D/sub/foo/a
+    WVPASS bup index -u $D
+    bup save --strip -n bupdir $D
+    bup restore -C buprestore.tmp --exclude-rx '/foo$' /bupdir/latest/
+    WVPASSEQ "$(cd buprestore.tmp && find . | sort)" ".
+./a
+./b
+./sub
+./sub/foo
+./sub/foo/a"
+
+    WVSTART "restore --exclude-rx '/foo/$' (dir, tail anchor)"
+    rm -rf "$D" "$BUP_DIR" buprestore.tmp
+    WVPASS bup init
+    mkdir $D
+    touch $D/a
+    touch $D/b
+    touch $D/foo
+    mkdir $D/sub
+    mkdir $D/sub/foo
+    touch $D/sub/foo/a
+    WVPASS bup index -u $D
+    bup save --strip -n bupdir $D
+    bup restore -C buprestore.tmp --exclude-rx '/foo/$' /bupdir/latest/
+    WVPASSEQ "$(cd buprestore.tmp && find . | sort)" ".
+./a
+./b
+./foo
+./sub"
+
+    WVSTART "restore --exclude-rx '/foo/.' (dir content)"
+    rm -rf "$D" "$BUP_DIR" buprestore.tmp
+    WVPASS bup init
+    mkdir $D
+    touch $D/a
+    touch $D/b
+    touch $D/foo
+    mkdir $D/sub
+    mkdir $D/sub/foo
+    touch $D/sub/foo/a
+    WVPASS bup index -u $D
+    bup save --strip -n bupdir $D
+    bup restore -C buprestore.tmp --exclude-rx '/foo/.' /bupdir/latest/
+    WVPASSEQ "$(cd buprestore.tmp && find . | sort)" ".
+./a
+./b
+./foo
+./sub
+./sub/foo"
+) || WVFAIL