A compatibility question

8 views
Skip to first unread message

Mark Hewitt

unread,
Jul 6, 2026, 6:11:18 AMJul 6
to bup-...@googlegroups.com
Currently, main will not save to an older bup server - I was just
wondering what the migration path was likely to be - either to insist
upon updating servers (which will presumably support older clients) or
have a fallback operating mode for later bup clients to support older
servers.

Of course, it may be that this is just another of those pesky __del()__
assertions that could be ignored with a warning, but has not yet been
tackled in main!

This is what I see when testing a remote save to an older (0.33.10) bup
server with a client built from main:

> # /usr/local/bin/bup
> --bup-dir=/bupsys/var/bupcache/remote-bupman/local.bup/disker.bup save
> --verbose --commit --compress=9 --name=disker-root
> --indexfile=/bupsys/var/bupcache/remote-bupman/indexes/disker-root.index
> --verbose
> --remote=bup@bupman:/bupsys/data/External-013/bupdata/repository/disker /
> error: server does not appear to provide config-get commandException
> ignored in: <function RemoteRepo.__del__ at 0x7fffa5825a80>
> Traceback (most recent call last):
>   File "/usr/local/lib/bup/bup/repo/remote.py", line 47, in __del__
>     def __del__(self): assert self.closed
> AssertionError:
> Exception ignored in: <function Client.__del__ at 0x7fffa580c220>
> Traceback (most recent call last):
>   File "/usr/local/lib/bup/bup/client.py", line 352, in __del__
>     def __del__(self): assert self.closed
> AssertionError:
> Exception ignored in: <function Client.ViaSsh.__del__ at 0x7fffa57ffb00>
> Traceback (most recent call last):
>   File "/usr/local/lib/bup/bup/client.py", line 217, in __del__
>     def __del__(self): assert self._closed
> AssertionError:
> Exception ignored in: <function BaseConn.__del__ at 0x7fffa60693a0>
> Traceback (most recent call last):
>   File "/usr/local/lib/bup/bup/helpers.py", line 431, in __del__
>     def __del__(self): assert self._base_closed
> AssertionError:
> #

--
Mark J Hewitt

Greg Troxel

unread,
Jul 6, 2026, 7:30:17 AMJul 6
to Mark Hewitt, bup-...@googlegroups.com
Mark Hewitt <mjh.br...@gmail.com> writes:

> Currently, main will not save to an older bup server - I was just
> wondering what the migration path was likely to be - either to insist
> upon updating servers (which will presumably support older clients) or
> have a fallback operating mode for later bup clients to support older
> servers.

Does it really not save, or just save and emit a pesky assertion?

I would say that it is a requirement that main can save to 0.33.x.
Users would see it as a serious regression if not. But I think it
actually can.


> Of course, it may be that this is just another of those pesky
> __del()__ assertions that could be ignored with a warning, but has not
> yet been tackled in main!
>
> This is what I see when testing a remote save to an older (0.33.10)
> bup server with a client built from main:

I see this too.

Rob Browning

unread,
Jul 6, 2026, 1:26:41 PMJul 6
to Mark Hewitt, bup-...@googlegroups.com
Mark Hewitt <mjh.br...@gmail.com> writes:

> Of course, it may be that this is just another of those pesky __del()__
> assertions that could be ignored with a warning, but has not yet been
> tackled in main!

Right, those should all be ignorable, though I hadn't realized you'd see
quite so many when talking to an older server.

I'll want to double-check the config-get exception.

In any case, main is not currently intended to be backward incompatible
with 0.33. Someday we might need to break backward compaibility, but
I don't think so now.

> This is what I see when testing a remote save to an older (0.33.10) bup
> server with a client built from main:

Thanks (to you and Greg) for testing this.

--
Rob Browning
rlb @defaultvalue.org and @debian.org
GPG as of 2011-07-10 E6A9 DA3C C9FD 1FF8 C676 D2C4 C0F0 39E9 ED1B 597A
GPG as of 2002-11-03 14DD 432F AE39 534D B592 F9A0 25C8 D377 8C7E 73A4

Rob Browning

unread,
Jul 6, 2026, 1:29:28 PMJul 6
to Mark Hewitt, bup-...@googlegroups.com
Rob Browning <r...@defaultvalue.org> writes:

> Mark Hewitt <mjh.br...@gmail.com> writes:
>
>> Of course, it may be that this is just another of those pesky __del()__
>> assertions that could be ignored with a warning, but has not yet been
>> tackled in main!
>
> Right, those should all be ignorable, though I hadn't realized you'd see
> quite so many when talking to an older server.

Oh, and when you saw that output, were you running "save -r" or
something else?

Greg Troxel

unread,
Jul 6, 2026, 1:38:59 PMJul 6
to Rob Browning, Mark Hewitt, bup-...@googlegroups.com
Rob Browning <r...@defaultvalue.org> writes:

> Oh, and when you saw that output, were you running "save -r" or
> something else?

That's when I see it.

In general I feel that python programs show exceptions to the user as a
normal practice, but that's like C programs dumping core. If the remote
gives an error for a config fetch because it doesn't do that, then it
would be good to have that silently handled, perhaps with a single
warning line. I don't know how hard that is of course.

Mark Hewitt

unread,
Jul 6, 2026, 2:04:20 PMJul 6
to Rob Browning, bup-...@googlegroups.com
On 06/07/2026 18:29, Rob Browning wrote:
> Rob Browning <r...@defaultvalue.org> writes:
>
> Oh, and when you saw that output, were you running "save -r" or
> something else?
>
The command line is in the original post - but yes, save --remote.

Mark


Johannes Berg

unread,
Jul 6, 2026, 3:31:03 PMJul 6
to Mark Hewitt, bup-...@googlegroups.com
On Mon, 2026-07-06 at 11:11 +0100, Mark Hewitt wrote:
> Currently, main will not save to an older bup server - I was just
> wondering what the migration path was likely to be - either to insist
> upon updating servers (which will presumably support older clients) or
> have a fallback operating mode for later bup clients to support older
> servers.

> > error: server does not appear to provide config-get command

That's my fault, I guess, and not really intentional, and I think we'll
want to fix it, which shouldn't really be difficult, we'll just need to
figure out what the best way is.

But I also want to say that you probably _do_ want to upgrade the server
as well, because that gives each repo a real identifier (bup.repo.id) to
be used for the idx cache, so that hostname/path changes etc. no longer
cause the idx cache to be duplicated/rebuilt.

johannes

Rob Browning

unread,
Jul 6, 2026, 5:45:30 PMJul 6
to Johannes Berg, Mark Hewitt, bup-...@googlegroups.com
Johannes Berg <joha...@sipsolutions.net> writes:

> ...and I think we'll want to fix it, which shouldn't really be
> difficult, we'll just need to figure out what the best way is.

...after a bit of discussion with Johannes, I think we may have a fix
soonish.

> But I also want to say that you probably _do_ want to upgrade the server
> as well, because that gives each repo a real identifier (bup.repo.id) to
> be used for the idx cache, so that hostname/path changes etc. no longer
> cause the idx cache to be duplicated/rebuilt.

Agreed. Matching the versions for 0.34, if you can, is going to provide
a number of improvments --- for example, respecting the remote
packsizelimit and compression settings, being able to change the
hashsplit granularity, and being able to enable treesplitting.

Rob Browning

unread,
Jul 6, 2026, 5:46:29 PMJul 6
to Greg Troxel, Mark Hewitt, bup-...@googlegroups.com
Greg Troxel <g...@lexort.com> writes:

> In general I feel that python programs show exceptions to the user as a
> normal practice, but that's like C programs dumping core. If the remote
> gives an error for a config fetch because it doesn't do that, then it
> would be good to have that silently handled, perhaps with a single
> warning line. I don't know how hard that is of course.

I generally agree, and we've been improving some of these cases, but I'm
sure not all yet.

Greg Troxel

unread,
Jul 6, 2026, 6:25:01 PMJul 6
to Rob Browning, Johannes Berg, Mark Hewitt, bup-...@googlegroups.com
Rob Browning <r...@defaultvalue.org> writes:

> Johannes Berg <joha...@sipsolutions.net> writes:
>
>> But I also want to say that you probably _do_ want to upgrade the server
>> as well, because that gives each repo a real identifier (bup.repo.id) to
>> be used for the idx cache, so that hostname/path changes etc. no longer
>> cause the idx cache to be duplicated/rebuilt.
>
> Agreed. Matching the versions for 0.34, if you can, is going to provide
> a number of improvments --- for example, respecting the remote
> packsizelimit and compression settings, being able to change the
> hashsplit granularity, and being able to enable treesplitting.

I never meant to suggest that any choice other than upgrading was wise.
Just that it should work if you don't, so that if you control one and
not the other, or the upgrades are async for various reasons (different
packaging systems).

(I'm already using 0.34 on my desktop, and 0.33.x on the rest.)

Greg Troxel

unread,
Jul 7, 2026, 12:45:45 PMJul 7
to bup-...@googlegroups.com
Here's a trace for a different case.

- Python 3.13 all around.
- bup 0.33.10 on NetBSD 9 i386, save -r.
- Save destination is "bup main" ish, NetBSD 10 amd64.

Pattern is consistent with other hosts running bup 0.33.10, and saves to
NetBSD 10 amd64 with 0.33.10 do not print errors.



Exception ignored in: <function PackMidx.__del__ at 0x7218dd7d7050>
Traceback (most recent call last):
File "/usr/pkg/lib/bup/bup/midx.py", line 102, in __del__
assert self.closed
AssertionError:
Exception ignored in: <function mmap.__del__ at 0x7218dd6d9210>
Traceback (most recent call last):
File "/usr/pkg/lib/bup/bup/io.py", line 340, in __del__
assert self._bup_closed
AssertionError:
Exception ignored in: <function PackMidx.__del__ at 0x7218dd7d7050>
Traceback (most recent call last):
File "/usr/pkg/lib/bup/bup/midx.py", line 102, in __del__
assert self.closed
AssertionError:
Exception ignored in: <function mmap.__del__ at 0x7218dd6d9210>
Traceback (most recent call last):
File "/usr/pkg/lib/bup/bup/io.py", line 340, in __del__
assert self._bup_closed
AssertionError:
Exception ignored in: <function PackMidx.__del__ at 0x75c7373d4050>
Traceback (most recent call last):
File "/usr/pkg/lib/bup/bup/midx.py", line 102, in __del__
assert self.closed
AssertionError:
Exception ignored in: <function mmap.__del__ at 0x75c738547210>
Traceback (most recent call last):
File "/usr/pkg/lib/bup/bup/io.py", line 340, in __del__
assert self._bup_closed
AssertionError:
Exception ignored in: <function PackMidx.__del__ at 0x75c7373d4050>
Traceback (most recent call last):
File "/usr/pkg/lib/bup/bup/midx.py", line 102, in __del__
assert self.closed
AssertionError:
Exception ignored in: <function mmap.__del__ at 0x75c738547210>
Traceback (most recent call last):
File "/usr/pkg/lib/bup/bup/io.py", line 340, in __del__
assert self._bup_closed
AssertionError:

Rob Browning

unread,
Jul 7, 2026, 5:20:32 PMJul 7
to bup-...@googlegroups.com, Mark Hewitt
Don't crash when the remote doesn't support the new config-get
command, just provide suitable substitute values (None should work for
all currently valid options) from a RemoteRepo via a
_fallback_config_get().

Move the valid options list to protocol.valid_config_opts so we can
share it between Server.config_get() and _fallback_config_get().

Add some initial cross-version tests by adding an "internal only"
BUP_TEST_SSH_BUP_PATH that can specify a path to the remote
bup (instead of just 'bup') and set that to BUP_TEST_OTHER_BUP when
BUP_TEST_OTHER_BUP has a value, in order to provide some simple ls,
save, and restore tests with ./bup as the client and
BUP_TEST_OTHER_BUP as the server.

Thanks to Mark Hewitt for reporting the issue, and Johannes Berg for
help evaluating the solution.

Signed-off-by: Rob Browning <r...@defaultvalue.org>
Tested-by: Rob Browning <r...@defaultvalue.org>
---

Pushed to main.

lib/bup/client.py | 8 ++++----
lib/bup/protocol.py | 20 ++++++++++++++------
lib/bup/repo/remote.py | 18 +++++++++++++++++-
lib/bup/ssh.py | 14 ++++++++++----
test/ext/test-ls-from-other-bup | 15 +++++++++++++++
test/ext/test-save-restore | 24 +++++++++++++++++++++---
6 files changed, 81 insertions(+), 18 deletions(-)
create mode 100755 test/ext/test-ls-from-other-bup

diff --git a/lib/bup/client.py b/lib/bup/client.py
index 1485d9c1..1f1ea3dc 100644
--- a/lib/bup/client.py
+++ b/lib/bup/client.py
@@ -276,10 +276,7 @@ class Client:
# dirs when the remote repo has one (that can be accessed).
repo_id = None
if b'config-get' in self._available_commands:
- try:
- repo_id = self.config_get(b'bup.repo.id')
- except PermissionError:
- pass
+ repo_id = self.config_get(b'bup.repo.id')
legacy = index_cache(legacy_id)
if repo_id is None:
return legacy
@@ -394,6 +391,9 @@ class Client:
raise ClientError('server does not appear to provide %s command'
% name.decode('ascii'))

+ def supports(self, command):
+ return command in self._available_commands
+
def _list_indexes(self):
with self._line_based_call('list-indexes') as call:
for line in call.lines():
diff --git a/lib/bup/protocol.py b/lib/bup/protocol.py
index f812da46..1aadb984 100644
--- a/lib/bup/protocol.py
+++ b/lib/bup/protocol.py
@@ -11,6 +11,19 @@ from bup.vint import \
from bup.vfs import Item, Chunky, RevList, Root, Tags, Commit, FakeLink


+# See also RemoteRepo._config_get_fallback(). The current assumption
+# is that None is the appropriate value for an option whenever the
+# server does not support config-get.
+
+valid_config_opts = \
+ frozenset((b'bup.repo.id',
+ b'bup.split.trees',
+ b'bup.split.files',
+ b'pack.packsizelimit',
+ b'core.compression',
+ b'pack.compression'))
+
+
def read_item(port):
"""Read an encoded VFS item from port. Throw EOFError for EOF."""
def read_oid(port, kind):
@@ -450,12 +463,7 @@ class Server:
# pylint: disable-next=unbalanced-tuple-unpacking
key, opttype = vint.recv(self.conn, 'ss')
# git is case-insensitve, and the client sends lower-case
- if key in (b'bup.repo.id',
- b'bup.split.trees',
- b'bup.split.files',
- b'pack.packsizelimit',
- b'core.compression',
- b'pack.compression'):
+ if key in valid_config_opts:
opttype = None if not opttype else opttype.decode('ascii')
val = self.repo.config_get(key, opttype=opttype)
if val is None:
diff --git a/lib/bup/repo/remote.py b/lib/bup/repo/remote.py
index a399a3a1..46ce34bb 100644
--- a/lib/bup/repo/remote.py
+++ b/lib/bup/repo/remote.py
@@ -3,6 +3,7 @@ from binascii import hexlify
import re

from bup import client, git
+from bup.protocol import valid_config_opts
from bup.repo.base import _make_base, RepoProtocol


@@ -17,7 +18,10 @@ class RemoteRepo(RepoProtocol):
self.closed = True # in case Client instantiation fails
self.client = client.Client(location, create=create)
self.closed = False
- self.config_get = self.client.config_get
+ if self.client.supports(b'config-get'):
+ self.config_get = self.client.config_get
+ else:
+ self.config_get = self._config_get_fallback
self._base = _make_base(self.config_get, compression_level,
max_pack_size, max_pack_objects)
self.write_symlink = self.write_data
@@ -48,6 +52,18 @@ class RemoteRepo(RepoProtocol):
def __enter__(self): return self
def __exit__(self, type, value, traceback): self.close()

+ def _config_get_fallback(self, name, opttype=None):
+ """Return an appropriate value when (older) remote does not
+ support config-get.
+
+ """
+ assert isinstance(name, bytes)
+ name = name.lower() # git is case insensitive here
+ assert opttype in ('int', 'bool', None)
+ if name not in valid_config_opts:
+ raise PermissionError(f'remote access to {name} is not allowed')
+ return None
+
def update_ref(self, refname, newval, oldval):
self.finish_writing()
return self.client.update_ref(refname, newval, oldval)
diff --git a/lib/bup/ssh.py b/lib/bup/ssh.py
index 9a721e6d..c231a10f 100644
--- a/lib/bup/ssh.py
+++ b/lib/bup/ssh.py
@@ -8,6 +8,7 @@ import re

from bup import path
from bup.compat import environ
+from bup.helpers import quote
from bup.io import buglvl, debug1


@@ -21,22 +22,27 @@ def connect(destination, port, subcmd, stderr=None):

"""
assert re.fullmatch(br'[-_a-zA-Z0-9]+', subcmd), subcmd
+
if not destination:
if b'BUP_TEST_LEVEL' not in environ:
raise Exception('no ssh destination')
- argv = [path.exe(), subcmd]
+ argv = [environ.get(b'BUP_TEST_SSH_BUP_PATH', path.exe()), subcmd]
elif destination == b'-':
if b'BUP_TEST_LEVEL' not in environ:
raise Exception('invalid ssh destination "-"')
- argv = [path.exe(), subcmd]
+ argv = [environ.get(b'BUP_TEST_SSH_BUP_PATH', path.exe()), subcmd]
else:
+ bup = environ.get(b'BUP_TEST_SSH_BUP_PATH')
+ if bup and b'BUP_TEST_LEVEL' not in environ:
+ raise Exception('BUP_TEST_SSH_BUP_PATH only allowed when testing')
+ bup = bup or b'bup'
force_tty = int(environ.get(b'BUP_FORCE_TTY', 0))
argv = [b'ssh']
if port:
argv.extend((b'-p', port))
argv.extend((destination, b'--',
- b"sh -c 'BUP_DEBUG=%d BUP_FORCE_TTY=%d bup %s'"
- % (buglvl, force_tty, subcmd)))
+ b"sh -c \"BUP_DEBUG=%d BUP_FORCE_TTY=%d %s %s\""
+ % (buglvl, force_tty, quote(bup), subcmd)))
debug1(f'ssh: {argv!r}\n')
return Popen(argv, stdin=PIPE, stdout=PIPE, stderr=stderr,
start_new_session=True)
diff --git a/test/ext/test-ls-from-other-bup b/test/ext/test-ls-from-other-bup
new file mode 100755
index 00000000..561e0afb
--- /dev/null
+++ b/test/ext/test-ls-from-other-bup
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+. wvtest.sh || exit $?
+. wvtest-bup.sh || exit $?
+
+set -o pipefail
+
+if test -z "$BUP_TEST_OTHER_BUP"; then
+ WVSKIP 'no BUP_TEST_OTHER_BUP'
+ exit 0
+fi
+
+export BUP_TEST_REMOTE_REPO=t # as per test-ls-remote
+export BUP_TEST_SSH_BUP_PATH="$BUP_TEST_OTHER_BUP"
+
+test/ext/test-ls
diff --git a/test/ext/test-save-restore b/test/ext/test-save-restore
index 24fe7941..f1d6ffcd 100755
--- a/test/ext/test-save-restore
+++ b/test/ext/test-save-restore
@@ -22,9 +22,9 @@ validate-local-and-remote-restore()
WVPASS bup restore -r "-:$BUP_DIR" -C "$dest" "$src"
WVPASS "$top/dev/compare-trees" "$cmp_src" "$cmp_dest"
if test "${BUP_TEST_LOCAL_SSH:-}"; then
- force-delete "$dest"
- WVPASS bup restore -r "$BUP_TEST_LOCAL_SSH:$BUP_DIR" -C "$dest" "$src"
- WVPASS "$top/dev/compare-trees" "$cmp_src" "$cmp_dest"
+ force-delete "$dest"
+ WVPASS bup restore -r "$BUP_TEST_LOCAL_SSH:$BUP_DIR" -C "$dest" "$src"
+ WVPASS "$top/dev/compare-trees" "$cmp_src" "$cmp_dest"
fi
}

@@ -196,5 +196,23 @@ WVSTART "save disjoint top-level directories"
) || exit $?


+if ! test "$BUP_TEST_OTHER_BUP"; then
+ WVSKIP "BUP_TEST_OTHER_BUP not set, skipping related tests"
+else
+ WVSTART "save/restore compatibility between ./bup and $BUP_TEST_OTHER_BUP"
+ WVPASSEQ '' "$BUP_TEST_SSH_BUP_PATH"
+ force-delete src repo restore
+ export BUP_DIR="$(pwd)/repo"
+ WVPASS bup init
+ WVPASS bup index "$top/test/sampledata"
+ export BUP_TEST_SSH_BUP_PATH="$BUP_TEST_OTHER_BUP"
+ WVPASS bup save -n save --strip "$top/test/sampledata"
+ WVPASS bup restore -r "-:$BUP_DIR" -C restore save/latest
+ unset BUP_TEST_SSH_BUP_PATH
+ WVPASS "$top/dev/compare-trees" "$top/test/sampledata/" restore/latest
+ force-delete src repo restore
+fi
+
+
WVPASS cd "$top"
WVPASS rm -rf "$tmpdir"
--
2.47.3

Rob Browning

unread,
Jul 7, 2026, 6:06:53 PMJul 7
to Greg Troxel, bup-...@googlegroups.com
Greg Troxel <g...@lexort.com> writes:

> Here's a trace for a different case.
>
> - Python 3.13 all around.
> - bup 0.33.10 on NetBSD 9 i386, save -r.
> - Save destination is "bup main" ish, NetBSD 10 amd64.
>
> Pattern is consistent with other hosts running bup 0.33.10, and saves to
> NetBSD 10 amd64 with 0.33.10 do not print errors.

I assume those errors are coming from the remote, but you could
double-check if you like by adding messages to some of the asserts, e.g.

assert self._closed, 'not closed (main)'

I couldn't (trivially) reproduce it here, but imagine it's because
the test repo's too small.

Thanks

Mark Hewitt

unread,
Jul 8, 2026, 6:50:30 AMJul 8
to Rob Browning, bup-...@googlegroups.com
On 07/07/2026 22:20, Rob Browning wrote:
Don't crash when the remote doesn't support the new config-get
command, just provide suitable substitute values (None should work for
all currently valid options) from a RemoteRepo via a
_fallback_config_get().

Thank you - adding this fallback allows my test of main into a 0.33.10 server to complete, and the repository appears correct (I can retrieve what I saved!).

I assume that 0.34 will include config-get - I don't think I'd want to replace a server system with just a latest commit to main - any thoughts on when this might be released?

Mark. 

Greg Troxel

unread,
Jul 8, 2026, 9:02:22 AMJul 8
to Mark Hewitt, Rob Browning, bup-...@googlegroups.com
main includes this, and therefore 0.34 will have it. Now newer main
should not spew backtraces when saving to 0.33.x. I don't see a
reason to change 0.33.10 to main because of this commit; 0.33.10 won't
manifest the problem.

How close we are to 0.34, beyond Winston's "the release of 0.34 is
within measurable distance of the end", is a good question!

Rob Browning

unread,
Jul 8, 2026, 1:46:18 PMJul 8
to Mark Hewitt, bup-...@googlegroups.com
Mark Hewitt <mjh.br...@gmail.com> writes:

> I assume that 0.34 will include config-get - I don't think I'd want to
> replace a server system with just a latest commit to main - any
> thoughts on when this might be released?

Yes, everything going in to main is for 0.34 right now.

And while of course it'll depend on "what comes up", I'm doing some
"larger" local testing now, before making the broader call for testing.
(That has provoked some of the recent patches.)

I imagine broader testing may reveal additional issues we have to
address, and then we'll be ready.

Rob Browning

unread,
Jul 9, 2026, 5:03:57 PMJul 9
to bup-...@googlegroups.com
Signed-off-by: Rob Browning <r...@defaultvalue.org>
Tested-by: Rob Browning <r...@defaultvalue.org>
---

Pushed to main.

lib/bup/repo/remote.py | 1 -
1 file changed, 1 deletion(-)

diff --git a/lib/bup/repo/remote.py b/lib/bup/repo/remote.py
index 46ce34bb..f4594810 100644
--- a/lib/bup/repo/remote.py
+++ b/lib/bup/repo/remote.py
@@ -62,7 +62,6 @@ class RemoteRepo(RepoProtocol):
assert opttype in ('int', 'bool', None)
if name not in valid_config_opts:
raise PermissionError(f'remote access to {name} is not allowed')
- return None

def update_ref(self, refname, newval, oldval):
self.finish_writing()
--
2.47.3

Rob Browning

unread,
Jul 9, 2026, 5:03:57 PMJul 9
to bup-...@googlegroups.com
According to POSIX, "ls -l symlink" must not follow symlinks.

Signed-off-by: Rob Browning <r...@defaultvalue.org>
Tested-by: Rob Browning <r...@defaultvalue.org>
---

Pushed to main.

lib/bup/ls.py | 2 +-
note/main.md | 4 ++++
test/ext/test-empty-metadata | 4 ++--
test/ext/test-ls | 30 ++++++++++++++++++------------
4 files changed, 25 insertions(+), 15 deletions(-)

diff --git a/lib/bup/ls.py b/lib/bup/ls.py
index eeab54f3..5cc50672 100644
--- a/lib/bup/ls.py
+++ b/lib/bup/ls.py
@@ -149,7 +149,7 @@ def within_repo(repo, opt, out, pwd=b''):
if last_n > 0:
out.write(b'%s:\n' % printpath)

- if opt.directory:
+ if opt.directory or opt.l:
resolved = vfs.resolve(repo, path, follow=False)
else:
resolved = vfs.try_resolve(repo, path, want_meta=want_meta)
diff --git a/note/main.md b/note/main.md
index 039a64ad..c9963f82 100644
--- a/note/main.md
+++ b/note/main.md
@@ -18,6 +18,10 @@ May require attention
commits if the `DEST` was not itself a commit (the parent would be
whatever `DEST` initially pointed to).

+* Following POSIX `ls`, `bup ls -l` no longer dereferences symlinks,
+ for example for `bup ls -l save/latest` (`bup ls -l save/latest/`
+ still does).
+
* `bup` now prefers the XDG cache location (typically
`~/.cache/bup/remote/`) for the client index cache, but existing
`$BUP_DIR/index-cache` directories will take precedence. See
diff --git a/test/ext/test-empty-metadata b/test/ext/test-empty-metadata
index 23dd0ef8..261835cf 100755
--- a/test/ext/test-empty-metadata
+++ b/test/ext/test-empty-metadata
@@ -27,7 +27,7 @@ WVPASS bup index src
WVPASS bup save --strip -n src src
WVPASS bup join src:.bupm > bupm

-wv-match-rx "$(bup ls -l src/latest | tr -s ' ' ' ')" \
+wv-match-rx "$(bup ls -l src/latest/ | tr -s ' ' ' ')" \
'^-rw-r--r-- [^/]+/.* 2 [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2} 1
-rw-r--r-- [^/]+/.* 2 [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2} 2
prw-r--r-- [^/]+/.* 0 [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2} 3$'
@@ -40,7 +40,7 @@ new_tree_oid="$(echo "$new_tree" | WVPASS git mktree)"
new_save=$(WVPASS git commit-tree "$new_tree_oid" -p src -m 'empty metadata')
WVPASS git branch -f src "$new_save"

-wv-match-rx "$(bup ls -l src/latest | tr -s ' ' ' ')" \
+wv-match-rx "$(bup ls -l src/latest/ | tr -s ' ' ' ')" \
'^-rw------- \?/\? 2 \?\?\?\?-\?\?-\?\? \?\?:\?\? 1
-rw-r--r-- [^/]+/.* 2 [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2} 2
-rw------- \?/\? 0 \?\?\?\?-\?\?-\?\? \?\?:\?\? 3$'
diff --git a/test/ext/test-ls b/test/ext/test-ls
index f34b808a..b9987068 100755
--- a/test/ext/test-ls
+++ b/test/ext/test-ls
@@ -164,7 +164,7 @@ socket_mode="$(WVPASS ls -l src/socket | cut -b -10)" || exit $?

bad_symlink_mode="$(WVPASS ls -l src/bad-symlink | cut -b -10)" || exit $?

-bad_symlink_bup_info="$(WVPASS bup-ls -l src/latest | grep bad-symlink)" \
+bad_symlink_bup_info="$(WVPASS bup-ls -l src/latest/ | grep bad-symlink)" \
|| exit $?
bad_symlink_date="$(WVPASS echo "$bad_symlink_bup_info" \
| WVPASS perl -ne 'm/.*? (\d+) (\d\d\d\d-\d\d-\d\d \d\d:\d\d)/ and print $2')" \
@@ -185,7 +185,7 @@ fi

symlink_mode="$(WVPASS ls -l src/symlink | cut -b -10)" || exit $?

-symlink_bup_info="$(WVPASS bup-ls -l src/latest | grep -E '[^-]symlink')" \
+symlink_bup_info="$(WVPASS bup-ls -l src/latest/ | grep -E '[^-]symlink')" \
|| exit $?
symlink_date="$(WVPASS echo "$symlink_bup_info" \
| WVPASS perl -ne 'm/.*? (\d+) (\d\d\d\d-\d\d-\d\d \d\d:\d\d)/ and print $2')" \
@@ -203,7 +203,7 @@ else
|| exit $?
fi

-WVPASSEQ "$(bup-ls -l src/latest | tr -s ' ' ' ')" \
+WVPASSEQ "$(bup-ls -l src/latest/ | tr -s ' ' ' ')" \
"$bad_symlink_mode $user/$group $bad_symlink_size $bad_symlink_date bad-symlink -> not-there
-rwx------ $user/$group 0 2009-10-03 23:48 executable
prw------- $user/$group 0 2009-10-03 23:48 fifo
@@ -211,7 +211,7 @@ prw------- $user/$group 0 2009-10-03 23:48 fifo
$socket_mode $user/$group 0 2009-10-03 23:48 socket
$symlink_mode $user/$group $symlink_size $symlink_date symlink -> file"

-WVPASSEQ "$(bup-ls -la src/latest | tr -s ' ' ' ')" \
+WVPASSEQ "$(bup-ls -la src/latest/ | tr -s ' ' ' ')" \
"drwx------ $user/$group 0 2009-10-03 23:48 .
drwx------ $user/$group 0 2009-10-03 23:48 ..
-rw------- $user/$group 0 2009-10-03 23:48 .1st
@@ -223,7 +223,7 @@ prw------- $user/$group 0 2009-10-03 23:48 fifo
$socket_mode $user/$group 0 2009-10-03 23:48 socket
$symlink_mode $user/$group $symlink_size $symlink_date symlink -> file"

-WVPASSEQ "$(bup-ls -lA src/latest | tr -s ' ' ' ')" \
+WVPASSEQ "$(bup-ls -lA src/latest/ | tr -s ' ' ' ')" \
"-rw------- $user/$group 0 2009-10-03 23:48 .1st
-rw------- $user/$group 0 2009-10-03 23:48 .dotfile
$bad_symlink_mode $user/$group $bad_symlink_size $bad_symlink_date bad-symlink -> not-there
@@ -233,7 +233,7 @@ prw------- $user/$group 0 2009-10-03 23:48 fifo
$socket_mode $user/$group 0 2009-10-03 23:48 socket
$symlink_mode $user/$group $symlink_size $symlink_date symlink -> file"

-WVPASSEQ "$(bup-ls -lF src/latest | tr -s ' ' ' ')" \
+WVPASSEQ "$(bup-ls -lF src/latest/ | tr -s ' ' ' ')" \
"$bad_symlink_mode $user/$group $bad_symlink_size $bad_symlink_date bad-symlink@ -> not-there
-rwx------ $user/$group 0 2009-10-03 23:48 executable*
prw------- $user/$group 0 2009-10-03 23:48 fifo|
@@ -241,7 +241,7 @@ prw------- $user/$group 0 2009-10-03 23:48 fifo|
$socket_mode $user/$group 0 2009-10-03 23:48 socket=
$symlink_mode $user/$group $symlink_size $symlink_date symlink@ -> file"

-WVPASSEQ "$(bup-ls -l --file-type src/latest | tr -s ' ' ' ')" \
+WVPASSEQ "$(bup-ls -l --file-type src/latest/ | tr -s ' ' ' ')" \
"$bad_symlink_mode $user/$group $bad_symlink_size $bad_symlink_date bad-symlink@ -> not-there
-rwx------ $user/$group 0 2009-10-03 23:48 executable
prw------- $user/$group 0 2009-10-03 23:48 fifo|
@@ -249,7 +249,7 @@ prw------- $user/$group 0 2009-10-03 23:48 fifo|
$socket_mode $user/$group 0 2009-10-03 23:48 socket=
$symlink_mode $user/$group $symlink_size $symlink_date symlink@ -> file"

-WVPASSEQ "$(bup-ls -ln src/latest | tr -s ' ' ' ')" \
+WVPASSEQ "$(bup-ls -ln src/latest/ | tr -s ' ' ' ')" \
"$bad_symlink_mode $uid/$gid $bad_symlink_size $bad_symlink_date bad-symlink -> not-there
-rwx------ $uid/$gid 0 2009-10-03 23:48 executable
prw------- $uid/$gid 0 2009-10-03 23:48 fifo
@@ -257,9 +257,15 @@ prw------- $uid/$gid 0 2009-10-03 23:48 fifo
$socket_mode $uid/$gid 0 2009-10-03 23:48 socket
$symlink_mode $uid/$gid $symlink_size $symlink_date symlink -> file"

+WVPASSEQ "$(bup-ls -l "src/latest" | tr -s ' ' ' ')" \
+"lrwxr-xr-x ?/? 17 ????-??-?? ??:?? src/latest -> 1977-09-05-125600"
+
WVPASSEQ "$(bup-ls -ld "src/latest" | tr -s ' ' ' ')" \
"lrwxr-xr-x ?/? 17 ????-??-?? ??:?? src/latest -> 1977-09-05-125600"

+WVPASSEQ "$(bup-ls -ld "src/latest/" | tr -s ' ' ' ')" \
+"drwx------ $user/$group 0 2009-10-03 23:48 src/latest"
+

WVSTART "$ls_cmd_desc (backup set - long)"
WVPASSEQ "$(bup-ls -l --numeric-ids src | cut -d' ' -f 1-2)" \
@@ -276,13 +282,13 @@ WVPASSEQ "$(bup-ls -ds --commit-hash "src/1977-09-05-125600" | tr -s ' ' ' ')" \

WVSTART "$ls_cmd_desc (dates TZ != UTC)"
export TZ=America/Chicago
-bad_symlink_date_central="$(bup-ls -l src/latest | grep bad-symlink)"
+bad_symlink_date_central="$(bup-ls -l src/latest/ | grep bad-symlink)"
bad_symlink_date_central="$(echo "$bad_symlink_date_central" \
| perl -ne 'm/.*? (\d+) (\d\d\d\d-\d\d-\d\d \d\d:\d\d)/ and print $2')"
-symlink_date_central="$(bup-ls -l src/latest | grep -E '[^-]symlink')"
+symlink_date_central="$(bup-ls -l src/latest/ | grep -E '[^-]symlink')"
symlink_date_central="$(echo "$symlink_date_central" \
| perl -ne 'm/.*? (\d+) (\d\d\d\d-\d\d-\d\d \d\d:\d\d)/ and print $2')"
-WVPASSEQ "$(bup-ls -ln src/latest | tr -s ' ' ' ')" \
+WVPASSEQ "$(bup-ls -ln src/latest/ | tr -s ' ' ' ')" \
"$bad_symlink_mode $uid/$gid $bad_symlink_size $bad_symlink_date_central bad-symlink -> not-there
-rwx------ $uid/$gid 0 2009-10-03 18:48 executable
prw------- $uid/$gid 0 2009-10-03 18:48 fifo
@@ -309,7 +315,7 @@ else
echo src/some-longer-name-{1..3000} | WVPASS xargs touch
WVPASS bup index src
WVPASS bup save -n src --strip src
- WVPASS with-tty "$top/bup" ls -l src/latest | WVPASS wc -l > ls-rows
+ WVPASS with-tty "$top/bup" ls -l src/latest/ | WVPASS wc -l > ls-rows
WVPASSEQ 3000 $(<ls-rows)
fi

--
2.47.3

Reply all
Reply to author
Forward
0 new messages