How Dive's File Tree Sorting Works: A Deep Dive into the Implementation

Dive sorts file tree nodes using a pluggable strategy pattern that supports alphabetical (ByName) and size-descending (BySizeDesc) ordering, storing the current mode in the SortOrder field of the FileTree struct and applying it during every tree traversal and render operation.

The open-source tool wagoodman/dive provides an interactive interface for exploring Docker image layers. Central to this experience is the file tree view, which organizes image contents into a navigable hierarchy. Understanding how Dive's file tree sorting works reveals a clean strategy pattern implementation that separates sorting logic from tree structure.

Sort Order Storage and Configuration

The FileTree struct maintains the current sort preference in a dedicated field. Located in dive/filetree/file_tree.go, the struct definition includes:

type FileTree struct {
    …
    SortOrder SortOrder   // ← current order
}

By default, every new tree initializes with alphabetical ordering. The constructor explicitly sets this value at line 42 of the same file:

tree.SortOrder = ByName

The available strategies are defined as constants representing the SortOrder type, with NumSortOrderConventions tracking the total count for UI cycling purposes.

The Sorting Strategy Pattern

Dive delegates actual sorting logic to strategy objects selected by the GetSortOrderStrategy function in dive/filetree/order_strategy.go. This function maps the current SortOrder enum value to a concrete implementation:

func GetSortOrderStrategy(sortOrder SortOrder) OrderStrategy {
    switch sortOrder {
    case ByName:
        return orderByNameStrategy{}
    case BySizeDesc:
        return orderBySizeDescStrategy{}
    }
    return orderByNameStrategy{}
}

Each strategy implements the orderKeys(files map[string]*FileNode) []string method, returning a deterministically ordered slice of child keys.

Alphabetical Sorting (ByName)

The orderByNameStrategy relies on Go's standard library for stable alphabetical sorting:

func (orderByNameStrategy) orderKeys(files map[string]*FileNode) []string {
    var keys []string
    for k := range files {
        keys = append(keys, k)
    }
    sort.Strings(keys)          // ← Go's stable alphabetical sort
    return keys
}

This implementation appears in dive/filetree/order_strategy.go (lines 32-40) and provides predictable, path-based ordering suitable for general exploration.

Size-Descending Sorting (BySizeDesc)

For analyzing image bloat, orderBySizeDescStrategy sorts by file size in descending order, using the name as a deterministic tie-breaker:

func (orderBySizeDescStrategy) orderKeys(files map[string]*FileNode) []string {
    var keys []string
    for k := range files {
        keys = append(keys, k)
    }
    sort.Slice(keys, func(i, j int) bool {
        ki, kj := keys[i], keys[j]
        ni, nj := files[ki], files[kj]
        if ni.GetSize() == nj.GetSize() {
            return ki < kj                     // deterministic tie‑breaker
        }
        return ni.GetSize() > nj.GetSize()    // larger first
    })
    return keys
}

This code (lines 45-60 in order_strategy.go) ensures that the largest files appear first in the tree view, making it immediately obvious which assets consume the most layer space.

How Sorting Is Applied During Tree Traversal

The sorting strategy is invoked whenever Dive renders the tree or walks its nodes. In dive/filetree/file_tree.go (lines 71-73), the String() method obtains the sorter and retrieves ordered keys before processing children:

sorter := GetSortOrderStrategy(tree.SortOrder)
keys   := sorter.orderKeys(currentParams.node.Children)

This same pattern applies to visitor traversals including VisitDepthParentFirst and VisitDepthChildFirst, guaranteeing that any programmatic walk of the tree respects the chosen ordering. The FileNode implementation in dive/filetree/file_node.go also calls GetSortOrderStrategy(ByName) for internal traversal defaults when no explicit order is specified.

Changing Sort Order via the UI

Users toggle between strategies using Ctrl + O, which cycles through available conventions modulo NumSortOrderConventions. The UI layer implements this in cmd/dive/cli/internal/ui/v1/viewmodel/filetree.go:

func (vm *FileTreeViewModel) ToggleSortOrder() error {
    vm.ModelTree.SortOrder = (vm.ModelTree.SortOrder + 1) % filetree.NumSortOrderConventions
    return nil
}

This method (lines 368-371) increments the enum value, causing the next render cycle to instantiate a different strategy via GetSortOrderStrategy. The key binding itself is defined in cmd/dive/cli/internal/ui/v1/view/filetree.go, wiring the Ctrl + O keystroke directly to vm.ToggleSortOrder().

Programmatic Code Examples

Setting Size-Descending Order Programmatically

import (
    "github.com/wagoodman/dive/dive/filetree"
)

func setSizeDesc(tree *filetree.FileTree) {
    // Switch to “largest first” ordering
    tree.SortOrder = filetree.BySizeDesc
}

Walking the Tree in Current Order

func walk(tree *filetree.FileTree) error {
    // The visitor will receive nodes ordered per tree.SortOrder
    return tree.VisitDepthParentFirst(
        func(node *filetree.FileNode) error {
            fmt.Println(node.Path(), node.GetSize())
            return nil
        },
        nil,
    )
}

Testing the UI Toggle

func TestToggleSortOrder(t *testing.T) {
    vm := newFileTreeViewModel() // builds a model with a FileTree
    original := vm.ModelTree.SortOrder

    if err := vm.ToggleSortOrder(); err != nil {
        t.Fatalf("toggle failed: %v", err)
    }

    if vm.ModelTree.SortOrder == original {
        t.Fatalf("sort order did not change")
    }
}

Summary

  • Strategy Pattern: Dive encapsulates sorting logic in interchangeable strategy objects (orderByNameStrategy, orderBySizeDescStrategy) selected by GetSortOrderStrategy in dive/filetree/order_strategy.go.
  • State Storage: Each FileTree instance stores its current SortOrder preference, defaulting to ByName upon initialization.
  • Consistent Application: The orderKeys method is invoked during rendering and all visitor traversals, ensuring the entire UI respects the selected ordering.
  • User Control: The Ctrl + O shortcut cycles through available strategies by incrementing SortOrder modulo NumSortOrderConventions, implemented in the viewmodel at cmd/dive/cli/internal/ui/v1/viewmodel/filetree.go.
  • Determinism: Both strategies provide stable ordering; the size-descending strategy uses file path as a tie-breaker when sizes match.

Frequently Asked Questions

How do I change the sort order in Dive's file tree view?

Press Ctrl + O to cycle between available sorting strategies. This keybinding triggers ToggleSortOrder() in the viewmodel, which increments the SortOrder field modulo the number of available conventions, switching between alphabetical and size-descending modes.

What sorting algorithms does Dive use for file tree display?

Dive uses Go's sort.Strings() for alphabetical ordering (ByName) and sort.Slice() with a custom comparator for size-descending ordering (BySizeDesc). The size comparator places larger files first and uses file path comparison as a deterministic tie-breaker when sizes are equal.

Where is the file tree sort order stored in Dive's codebase?

The sort order is stored in the SortOrder field of the FileTree struct defined in dive/filetree/file_tree.go (line 31). The UI viewmodel maintains a reference to this tree and modifies the field via ToggleSortOrder(), while rendering logic reads it to select the appropriate strategy via GetSortOrderStrategy().

Can I programmatically sort a FileTree in Dive?

Yes. Set tree.SortOrder = filetree.BySizeDesc (or ByName) before calling traversal methods like VisitDepthParentFirst() or String(). The tree will use GetSortOrderStrategy() to obtain the correct sorter and apply it to child nodes during the walk.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →