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

> Discover how Dive sorts its file tree. Learn about alphabetical and size-descending ordering strategies and their implementation for efficient Docker image analysis.

- Repository: [Alex Goodman/dive](https://github.com/wagoodman/dive)
- Tags: deep-dive
- Published: 2026-03-07

---

**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](https://github.com/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`](https://github.com/wagoodman/dive/blob/main/dive/filetree/file_tree.go), the struct definition includes:

```go
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:

```go
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`](https://github.com/wagoodman/dive/blob/main/dive/filetree/order_strategy.go). This function maps the current `SortOrder` enum value to a concrete implementation:

```go
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:

```go
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`](https://github.com/wagoodman/dive/blob/main/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:

```go
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`](https://github.com/wagoodman/dive/blob/main/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`](https://github.com/wagoodman/dive/blob/main/dive/filetree/file_tree.go) (lines 71-73), the `String()` method obtains the sorter and retrieves ordered keys before processing children:

```go
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`](https://github.com/wagoodman/dive/blob/main/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`](https://github.com/wagoodman/dive/blob/main/cmd/dive/cli/internal/ui/v1/viewmodel/filetree.go):

```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`](https://github.com/wagoodman/dive/blob/main/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

```go
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

```go
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

```go
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`](https://github.com/wagoodman/dive/blob/main/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`](https://github.com/wagoodman/dive/blob/main/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`](https://github.com/wagoodman/dive/blob/main/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.