mirror of
https://github.com/restic/restic.git
synced 2026-09-26 22:10:30 +00:00
snapshots: revert default --lastest behavior to pre-0.19.0
the changed behavior now only applies when using `--group-by`.
This commit is contained in:
@@ -0,0 +1,11 @@
|
|||||||
|
Bugfix: Restore old behavior of `snapshots --latest <n>` without `--group-by`
|
||||||
|
|
||||||
|
Restic 0.19.0 changed the behavior of `snapshots --latest <n>` to no longer
|
||||||
|
group snapshots by default.
|
||||||
|
|
||||||
|
`snapshots --latest <n>` has been reverted to the old behavior if `--group-by`
|
||||||
|
is not specified. When specifying `--group-by`, the output is still grouped as
|
||||||
|
requested like in restic 0.19.0.
|
||||||
|
|
||||||
|
https://github.com/restic/restic/issues/21869
|
||||||
|
https://github.com/restic/restic/pull/21875
|
||||||
@@ -107,7 +107,11 @@ func runSnapshots(ctx context.Context, opts SnapshotOptions, gopts global.Option
|
|||||||
}
|
}
|
||||||
|
|
||||||
if opts.Latest > 0 {
|
if opts.Latest > 0 {
|
||||||
list = filterLatestSnapshotsInGroup(list, opts.Latest)
|
if grouped {
|
||||||
|
list = filterLatestSnapshotsInGroup(list, opts.Latest)
|
||||||
|
} else {
|
||||||
|
list = filterLatestSnapshots(list, opts.Latest)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sort.Sort(sort.Reverse(list))
|
sort.Sort(sort.Reverse(list))
|
||||||
snapshotGroups[k] = list
|
snapshotGroups[k] = list
|
||||||
@@ -141,6 +145,43 @@ func runSnapshots(ctx context.Context, opts SnapshotOptions, gopts global.Option
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// filterLastSnapshotsKey is used by FilterLastSnapshots.
|
||||||
|
type filterLastSnapshotsKey struct {
|
||||||
|
Hostname string
|
||||||
|
JoinedPaths string
|
||||||
|
}
|
||||||
|
|
||||||
|
// newFilterLastSnapshotsKey initializes a filterLastSnapshotsKey from a Snapshot
|
||||||
|
func newFilterLastSnapshotsKey(sn *data.Snapshot) filterLastSnapshotsKey {
|
||||||
|
// Shallow slice copy
|
||||||
|
var paths = make([]string, len(sn.Paths))
|
||||||
|
copy(paths, sn.Paths)
|
||||||
|
sort.Strings(paths)
|
||||||
|
return filterLastSnapshotsKey{sn.Hostname, strings.Join(paths, "|")}
|
||||||
|
}
|
||||||
|
|
||||||
|
// filterLatestSnapshots filters a list of snapshots to only return
|
||||||
|
// the limit last entries for each hostname and path. If the snapshot
|
||||||
|
// contains multiple paths, they will be joined and treated as one
|
||||||
|
// item.
|
||||||
|
func filterLatestSnapshots(list data.Snapshots, limit int) data.Snapshots {
|
||||||
|
// Sort the snapshots so that the newer ones are listed first
|
||||||
|
sort.SliceStable(list, func(i, j int) bool {
|
||||||
|
return list[i].Time.After(list[j].Time)
|
||||||
|
})
|
||||||
|
|
||||||
|
var results data.Snapshots
|
||||||
|
seen := make(map[filterLastSnapshotsKey]int)
|
||||||
|
for _, sn := range list {
|
||||||
|
key := newFilterLastSnapshotsKey(sn)
|
||||||
|
if seen[key] < limit {
|
||||||
|
seen[key]++
|
||||||
|
results = append(results, sn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
// filterLatestSnapshotsInGroup filters a list of snapshots to only return
|
// filterLatestSnapshotsInGroup filters a list of snapshots to only return
|
||||||
// the `limit` last entries. It is assumed that the snapshot list only contains
|
// the `limit` last entries. It is assumed that the snapshot list only contains
|
||||||
// one group of snapshots.
|
// one group of snapshots.
|
||||||
|
|||||||
@@ -35,10 +35,7 @@ func testRunSnapshots(t testing.TB, gopts global.Options) (newest *Snapshot, sna
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSnapshotsGroupByAndLatest(t *testing.T) {
|
func snapshotsGroupTestData(t *testing.T, env *testEnvironment, keepPath bool) string {
|
||||||
env, cleanup := withTestEnvironment(t)
|
|
||||||
defer cleanup()
|
|
||||||
|
|
||||||
testSetupBackupData(t, env)
|
testSetupBackupData(t, env)
|
||||||
// two backups on the same host but with different paths
|
// two backups on the same host but with different paths
|
||||||
opts := BackupOptions{Host: "testhost", TimeStamp: time.Now().Format(time.DateTime)}
|
opts := BackupOptions{Host: "testhost", TimeStamp: time.Now().Format(time.DateTime)}
|
||||||
@@ -46,9 +43,22 @@ func TestSnapshotsGroupByAndLatest(t *testing.T) {
|
|||||||
// Use later timestamp for second backup
|
// Use later timestamp for second backup
|
||||||
opts.TimeStamp = time.Now().Add(time.Second).Format(time.DateTime)
|
opts.TimeStamp = time.Now().Add(time.Second).Format(time.DateTime)
|
||||||
snapshotsIDs := loadSnapshotMap(t, env.gopts)
|
snapshotsIDs := loadSnapshotMap(t, env.gopts)
|
||||||
testRunBackup(t, filepath.Dir(env.testdata), []string{"testdata/0"}, opts, env.gopts)
|
|
||||||
|
targets := []string{"testdata/0"}
|
||||||
|
if keepPath {
|
||||||
|
targets = []string{"testdata"}
|
||||||
|
}
|
||||||
|
testRunBackup(t, filepath.Dir(env.testdata), targets, opts, env.gopts)
|
||||||
_, secondSnapshotID := lastSnapshot(snapshotsIDs, loadSnapshotMap(t, env.gopts))
|
_, secondSnapshotID := lastSnapshot(snapshotsIDs, loadSnapshotMap(t, env.gopts))
|
||||||
|
|
||||||
|
return secondSnapshotID
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSnapshotsGroupByAndLatest(t *testing.T) {
|
||||||
|
env, cleanup := withTestEnvironment(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
secondSnapshotID := snapshotsGroupTestData(t, env, false)
|
||||||
buf, err := withCaptureStdout(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
|
buf, err := withCaptureStdout(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
|
||||||
gopts.JSON = true
|
gopts.JSON = true
|
||||||
// only group by host but not path
|
// only group by host but not path
|
||||||
@@ -65,3 +75,21 @@ func TestSnapshotsGroupByAndLatest(t *testing.T) {
|
|||||||
rtest.Assert(t, len(snapshots[0].Snapshots) == 1, "expected only one latest snapshot, got %d", len(snapshots[0].Snapshots))
|
rtest.Assert(t, len(snapshots[0].Snapshots) == 1, "expected only one latest snapshot, got %d", len(snapshots[0].Snapshots))
|
||||||
rtest.Equals(t, snapshots[0].Snapshots[0].ID.String(), secondSnapshotID, "unexpected snapshot ID")
|
rtest.Equals(t, snapshots[0].Snapshots[0].ID.String(), secondSnapshotID, "unexpected snapshot ID")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSnapshotsLatest(t *testing.T) {
|
||||||
|
env, cleanup := withTestEnvironment(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
secondSnapshotID := snapshotsGroupTestData(t, env, true)
|
||||||
|
|
||||||
|
buf, err := withCaptureStdout(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
|
||||||
|
gopts.JSON = true
|
||||||
|
opts := SnapshotOptions{Latest: 1}
|
||||||
|
return runSnapshots(ctx, opts, gopts, []string{}, gopts.Term)
|
||||||
|
})
|
||||||
|
rtest.OK(t, err)
|
||||||
|
snapshots := []Snapshot{}
|
||||||
|
rtest.OK(t, json.Unmarshal(buf.Bytes(), &snapshots))
|
||||||
|
rtest.Assert(t, len(snapshots) == 1, "expected only one snapshot, got %d", len(snapshots))
|
||||||
|
rtest.Equals(t, snapshots[0].ID.String(), secondSnapshotID, "unexpected snapshot ID")
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user