From c6ca14bac1e848d9414841d7181b7c7cdc85caa2 Mon Sep 17 00:00:00 2001 From: Rob Browning Date: Mon, 21 Dec 2015 13:35:24 -0600 Subject: [PATCH] index: always return at least the root for filter prefixes Return at least an entry for the prefix itself for each prefix passed to filter. Otherwise something like "bup save ... x/y" will fail when x is up to date because filter will return nothing, save will traverse nothing in its main loop, and bup will crash with an assertion failure: File "/home/rlb/src/bup/main/cmd/bup-save", line 440, in assert(len(shalists) == 1) Instead "bup save ... x/y" should (and now will) produce a new save containing x/y. Thanks to Simon Persson for helping formulate a good test case. Signed-off-by: Rob Browning Tested-by: Rob Browning --- Makefile | 1 + lib/bup/index.py | 16 ++++++++++++- t/test-save-with-valid-parent.sh | 40 ++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100755 t/test-save-with-valid-parent.sh diff --git a/Makefile b/Makefile index 51d04ac..79fbcb3 100644 --- a/Makefile +++ b/Makefile @@ -165,6 +165,7 @@ cmdline_tests := \ t/test-restore-map-owner.sh \ t/test-restore-single-file.sh \ t/test-rm-between-index-and-save.sh \ + t/test-save-with-valid-parent.sh \ t/test-sparse-files.sh \ t/test-command-without-init-fails.sh \ t/test-redundant-saves.sh \ diff --git a/lib/bup/index.py b/lib/bup/index.py index 0829764..8eabfbb 100644 --- a/lib/bup/index.py +++ b/lib/bup/index.py @@ -406,6 +406,11 @@ class Reader: def __iter__(self): return self.iter() + def find(self, name): + return next((e for e in self.iter(name, wantrecurse=lambda x : True) + if e.name == name), + None) + def exists(self): return self.m @@ -422,11 +427,20 @@ class Reader: def filter(self, prefixes, wantrecurse=None): for (rp, path) in reduce_paths(prefixes): + any_entries = False for e in self.iter(rp, wantrecurse=wantrecurse): + any_entries = True assert(e.name.startswith(rp)) name = path + e.name[len(rp):] yield (name, e) - + if not any_entries: + # Always return at least the top for each prefix. + # Otherwise something like "save x/y" will produce + # nothing if x is up to date. + pe = self.find(rp) + assert(pe) + name = path + pe.name[len(rp):] + yield (name, pe) # FIXME: this function isn't very generic, because it splits the filename # in an odd way and depends on a terminating '/' to indicate directories. diff --git a/t/test-save-with-valid-parent.sh b/t/test-save-with-valid-parent.sh new file mode 100755 index 0000000..f817165 --- /dev/null +++ b/t/test-save-with-valid-parent.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +. ./wvtest-bup.sh || exit $? +. t/lib.sh || exit $? + +set -o pipefail + +top="$(WVPASS pwd)" || exit $? +tmpdir="$(WVPASS wvmktempdir)" || exit $? + +export BUP_DIR="$tmpdir/bup" +export GIT_DIR="$tmpdir/bup" + +bup() { "$top/bup" "$@"; } +compare-trees() { "$top/t/compare-trees" "$@"; } + +WVPASS cd "$tmpdir" + +# Make sure that we can explicitly save a path whose parent is up to +# date. + +WVSTART "save path with up to date parent" +WVPASS bup init + +WVPASS mkdir -p src/a src/b +WVPASS touch src/a/1 src/b/2 +WVPASS bup index -u src +WVPASS bup save -n src src + +WVPASS bup save -n src src/b +WVPASS bup restore -C restore "src/latest/$(pwd)/" +WVPASS test ! -e restore/src/a +WVPASS "$top/t/compare-trees" -c src/b/ restore/src/b/ + +WVPASS bup save -n src src/a/1 +WVPASS rm -r restore +WVPASS bup restore -C restore "src/latest/$(pwd)/" +WVPASS test ! -e restore/src/b +WVPASS "$top/t/compare-trees" -c src/a/ restore/src/a/ + +WVPASS rm -rf "$tmpdir" -- 2.39.2