diff --git a/cmd/dive/cli/internal/command/export/testdata/snapshots/export_test.snap b/cmd/dive/cli/internal/command/export/testdata/snapshots/export_test.snap index 454c34a..25d6b10 100755 --- a/cmd/dive/cli/internal/command/export/testdata/snapshots/export_test.snap +++ b/cmd/dive/cli/internal/command/export/testdata/snapshots/export_test.snap @@ -20,7 +20,7 @@ "sizeBytes": 6405 } ], - "inefficientBytes": 32025, + "inefficientBytes": 19215, "sizeBytes": 1220598 }, "layer": [ diff --git a/cmd/dive/cli/internal/ui/v1/view/image_details.go b/cmd/dive/cli/internal/ui/v1/view/image_details.go index a119ba7..ddcbaa5 100644 --- a/cmd/dive/cli/internal/ui/v1/view/image_details.go +++ b/cmd/dive/cli/internal/ui/v1/view/image_details.go @@ -86,7 +86,7 @@ func (v *ImageDetails) Render() error { var wastedSpace int64 for idx := 0; idx < len(v.inefficiencies); idx++ { data := v.inefficiencies[len(v.inefficiencies)-1-idx] - wastedSpace += data.CumulativeSize + wastedSpace += data.WastedSize() inefficiencyReport += fmt.Sprintf(analysisTemplate, strconv.Itoa(len(data.Nodes)), humanize.Bytes(uint64(data.CumulativeSize)), data.Path) } diff --git a/dive/filetree/efficiency.go b/dive/filetree/efficiency.go index 02035a6..8e80252 100644 --- a/dive/filetree/efficiency.go +++ b/dive/filetree/efficiency.go @@ -14,6 +14,17 @@ type EfficiencyData struct { minDiscoveredSize int64 } +// WastedSize is the estimated reclaimable size for this path. +// Exactly one discovered copy must remain, so only bytes beyond the smallest +// discovered copy count as removable. +func (data *EfficiencyData) WastedSize() int64 { + wasted := data.CumulativeSize - data.minDiscoveredSize + if wasted < 0 { + return 0 + } + return wasted +} + // EfficiencySlice represents an ordered set of EfficiencyData data structures. type EfficiencySlice []*EfficiencyData diff --git a/dive/image/analysis.go b/dive/image/analysis.go index d2b0f04..eb36824 100644 --- a/dive/image/analysis.go +++ b/dive/image/analysis.go @@ -30,7 +30,7 @@ func Analyze(ctx context.Context, img *Image) (*Analysis, error) { var wastedBytes uint64 for _, file := range inefficiencies { - wastedBytes += uint64(file.CumulativeSize) + wastedBytes += uint64(file.WastedSize()) } return &Analysis{ diff --git a/dive/image/docker/image_archive_analysis_test.go b/dive/image/docker/image_archive_analysis_test.go index afc3825..0652b8e 100644 --- a/dive/image/docker/image_archive_analysis_test.go +++ b/dive/image/docker/image_archive_analysis_test.go @@ -14,7 +14,7 @@ func Test_Analysis(t *testing.T) { wastedPercent float64 path string }{ - "docker-image": {0.9844212134184309, 1220598, 66237, 32025, 0.4834911001404049, "../../../.data/test-docker-image.tar"}, + "docker-image": {0.9844212134184309, 1220598, 66237, 19215, 0.29009466008424295, "../../../.data/test-docker-image.tar"}, } for name, test := range table {