diff --git a/cli/command/image/tree.go b/cli/command/image/tree.go index ec4ef7b4e4b4..038f56a84195 100644 --- a/cli/command/image/tree.go +++ b/cli/command/image/tree.go @@ -14,6 +14,7 @@ import ( imagetypes "github.com/docker/docker/api/types/image" "github.com/docker/docker/pkg/stringid" "github.com/docker/go-units" + "github.com/fvbommel/sortorder" "github.com/morikuni/aec" ) @@ -82,16 +83,46 @@ func runTree(ctx context.Context, dockerCLI command.Cli, opts treeOptions) error details.ContentSize = units.HumanSizeWithPrecision(float64(totalContent), 3) - view.images = append(view.images, topImage{ - Names: img.RepoTags, - Details: details, - Children: children, - created: img.Created, - }) + if len(img.RepoTags) == 0 { + // Untagged image + view.images = append(view.images, topImage{ + Names: img.RepoTags, + Details: details, + Children: children, + created: img.Created, + }) + } else { + // Present images tagged under multiple names as separate images. + for _, n := range img.RepoTags { + view.images = append(view.images, topImage{ + Names: []string{n}, // Consider changing Names to be a single name for purpose of this presentation. + Details: details, + Children: children, + created: img.Created, + }) + } + } } + // Sort images alphabetically using natural-sort, with untagged images last. sort.Slice(view.images, func(i, j int) bool { - return view.images[i].created > view.images[j].created + iUntagged, jUntagged := len(view.images[i].Names) == 0, len(view.images[j].Names) == 0 + if iUntagged || jUntagged { + switch { + case iUntagged && jUntagged: + // Both untagged images; sort by created date (desc) + return view.images[i].created > view.images[j].created + case iUntagged: + // Sort untagged images last + return false + case jUntagged: + // Sort untagged images last + return true + } + } + + // Sort alphabetically, ascending + return sortorder.NaturalLess(view.images[i].Names[0], view.images[j].Names[0]) }) return printImageTree(dockerCLI, view)