Dive Filetree Package: Core Engine for Docker Image Layer Analysis and Visualization
The dive/filetree package provides the core data structures and algorithms for modeling, comparing, stacking, and rendering hierarchical filesystem views of Docker image layers, enabling the interactive ASCII tree visualization in the Dive CLI tool.
The dive/filetree package in the wagoodman/dive repository serves as the foundational engine for analyzing container image layers. It handles everything from parsing tar headers to generating the interactive file tree displayed in the terminal. Whether you are building a Docker image analyzer or need to programmatically compare filesystem states, this package offers robust primitives for tree construction, layer stacking, and diff visualization.
Core Functionalities of the Dive Filetree Package
Hierarchical File Tree Modeling
At the heart of the package are the FileTree and FileNode structs defined in dive/filetree/file_tree.go and dive/filetree/file_node.go. A FileTree represents a complete filesystem snapshot as a tree of FileNode instances, where each node holds metadata via FileInfo and UI state via ViewInfo.
The FileInfo struct, implemented in dive/filetree/file_info.go, captures essential file attributes extracted from tar headers, including path, size, permissions, and content hashes. The function NewFileInfoFromTarHeader parses tar archive headers to populate these fields, enabling accurate filesystem representation from Docker image layers.
Layer Stacking and Overlay Filesystem Support
The package implements Docker's overlay filesystem semantics through the Stack method in dive/filetree/file_tree.go. This functionality merges an upper layer onto a lower layer, handling white-out files (.wh.) that indicate deletions in Docker overlay storage.
When stacking, the algorithm walks the upper tree and applies changes to the lower tree: deleting paths marked by white-out files and adding or replacing other entries. This produces a cumulative view of the filesystem as it would appear after applying multiple image layers, which is essential for analyzing image efficiency and bloat.
Diff Calculation and Change Detection
The CompareAndMark method in dive/filetree/file_tree.go enables precise comparison between two file trees, marking each node with a DiffType indicating whether it was Added, Removed, Modified, or left Unmodified.
The comparison algorithm walks the new (upper) tree and compares each node against its counterpart in the reference tree using the compare function. Results are stored in FileNode.Data.DiffType, with parent nodes inheriting status from children through deriveDiffType. This diff engine powers Dive's ability to highlight exactly which files changed between image layers.
Tree Traversal and Sorting Strategies
The package provides flexible traversal utilities through VisitDepthChildFirst and VisitDepthParentFirst in dive/filetree/file_tree.go. These depth-first visitors support optional evaluator functions for filtering nodes during traversal, enabling efficient searches and transformations.
Sorting behavior is controlled through the OrderStrategy interface implemented in dive/filetree/order_strategy.go. The package offers two built-in strategies: orderByNameStrategy for alphabetical ordering and orderBySizeDescStrategy for descending size sorting. The FileTree.SortOrder field selects the active strategy, which the rendering engine uses when displaying child nodes.
ASCII Rendering and View State Management
The rendering pipeline in dive/filetree/file_tree.go generates human-readable ASCII tree representations through renderStringTreeBetween, String, and StringBetween methods. These functions produce branch graphics (├─, └─, etc.) and optional metadata columns for terminal display.
Each FileNode maintains UI state through the ViewInfo struct defined in dive/filetree/view_info.go, tracking whether nodes are Collapsed or Hidden. This separation of model and view concerns allows the package to support interactive features like directory collapsing and filtered views without mutating the underlying filesystem data.
Cached Layer Comparison
The Comparer type in dive/filetree/comparer.go provides high-level caching for multi-layer analysis. It generates and caches combined trees for arbitrary layer ranges, exposing GetTree for retrieving cumulative views and GetPathErrors for accessing processing errors.
This cache layer optimizes performance when navigating between different image layers in the UI, avoiding redundant restacking operations. The Comparer coordinates the Stack and CompareAndMark operations to present a consistent, navigable view of the image's layer history.
Working with the Filetree Package: Code Examples
Creating a File Tree from Paths
The following example demonstrates constructing a FileTree programmatically using the AddPath method:
import (
"github.com/wagoodman/dive/dive/filetree"
)
func buildDemoTree() *filetree.FileTree {
tree := filetree.NewFileTree()
// Simulate three files
tree.AddPath("/etc/hosts", filetree.FileInfo{Path: "/etc/hosts", IsDir: false, Size: 1024})
tree.AddPath("/usr/bin/bash", filetree.FileInfo{Path: "/usr/bin/bash", IsDir: false, Size: 123456})
tree.AddPath("/var/log", filetree.FileInfo{Path: "/var/log", IsDir: true})
return tree
}
This example utilizes NewFileTree defined in dive/filetree/file_tree.go and the AddPath method to populate the tree structure with FileInfo metadata.
Stacking Layers and Calculating Diffs
To analyze Docker image layers, you can stack an upper layer onto a lower layer and compute differences:
// lower and upper are *filetree.FileTree built from separate image layers
lower := buildDemoTree()
upper := filetree.NewFileTree()
upper.AddPath("/etc/hosts", filetree.FileInfo{Path: "/etc/hosts", IsDir: false, Size: 2048}) // modified size
upper.AddPath("/usr/bin/ls", filetree.FileInfo{Path: "/usr/bin/ls", IsDir: false, Size: 54321}) // added file
// Simulate a white‑out (deletion) of /var/log
upper.AddPath("/var/log/.wh..wh..", filetree.FileInfo{Path: "/var/log/.wh..wh..", IsDir: false})
stacked, _, _ := lower.Stack(upper) // `Stack` handles white‑outs
_ = stacked.CompareAndMark(upper) // annotate diff types
fmt.Println(stacked.String(true)) // render with attributes
The Stack method in dive/filetree/file_tree.go handles Docker overlay white-out files, while CompareAndMark annotates each node with DiffType indicators showing whether files were added, removed, or modified.
Rendering Interactive Views
For UI applications, the package supports view models that handle collapsing and paging:
vm, _ := viewmodel.NewFileTreeViewModel(cfg, 0) // cfg supplies the comparer and trees
vm.Setup(0, 20) // visible height = 20 lines
vm.ToggleCollapseAll() // collapse everything
_ = vm.Update(nil, 80, 20) // no regex filter, width 80
_ = vm.Render()
fmt.Println(vm.Buffer.String())
This example demonstrates integration with the view model defined in cmd/dive/cli/internal/ui/v1/viewmodel/filetree.go, which wraps the core dive/filetree types to provide interactive features like directory collapsing and filtered views.
Key Source Files in the dive/filetree Package
Understanding the package structure requires familiarity with these core files:
-
dive/filetree/file_tree.go— Contains theFileTreestruct and core methods includingAddPath,Stack,CompareAndMark, and rendering functions likeStringandrenderStringTreeBetween. -
dive/filetree/file_node.go— Defines theFileNodestruct representing individual files or directories, including metadata formatting and size aggregation logic. -
dive/filetree/file_info.go— ImplementsFileInfofor tar header parsing viaNewFileInfoFromTarHeaderand content hash generation. -
dive/filetree/diff.go— Defines theDiffTypeenumeration (Added, Removed, Modified, Unmodified) used for marking changes between trees. -
dive/filetree/view_info.go— ProvidesViewInfofor UI state management, trackingCollapsedandHiddenflags per node. -
dive/filetree/comparer.go— Implements theComparertype with caching logic for multi-layer tree composition viaGetTreeandGetPathErrors. -
dive/filetree/order_strategy.go— Contains sorting strategies includingorderByNameStrategyandorderBySizeDescStrategyfor controlling node traversal order. -
cmd/dive/cli/internal/ui/v1/viewmodel/filetree.go— Wraps the core package for interactive UI use, handling view updates, collapsing, and rendering buffers.
Summary
The dive/filetree package delivers a complete toolkit for Docker image filesystem analysis:
- Tree Modeling — Represents filesystems as hierarchical
FileNodestructures withFileInfometadata extracted from tar headers. - Layer Operations — Stacks Docker image layers using overlay filesystem semantics, handling white-out deletions via the
Stackmethod. - Diff Engine — Calculates precise changes between layers using
CompareAndMark, categorizing nodes as Added, Removed, Modified, or Unmodified. - Rendering System — Generates ASCII tree visualizations with branch graphics and metadata columns through
StringandrenderStringTreeBetween. - UI Integration — Supports interactive features like collapsing and filtering via
ViewInfoand theComparercache for efficient layer navigation.
Frequently Asked Questions
How does the dive/filetree package handle Docker overlay white-out files?
The package recognizes white-out files (prefixed with .wh.) through the Stack method in dive/filetree/file_tree.go. When stacking an upper layer onto a lower layer, the algorithm detects these special markers and removes the corresponding paths from the resulting tree, accurately simulating how Docker's overlay filesystem handles file deletions across layers.
What is the difference between FileTree and FileNode in the dive/filetree package?
FileTree acts as the container and manager for the entire filesystem representation, providing methods like AddPath, Stack, and CompareAndMark in dive/filetree/file_tree.go. FileNode represents individual files or directories within that tree, storing metadata via FileInfo and UI state via ViewInfo in dive/filetree/file_node.go. While FileTree handles operations on the whole structure, FileNode maintains the hierarchical relationships and individual attributes.
How does the CompareAndMark method determine file modifications?
The CompareAndMark method in dive/filetree/file_tree.go walks the upper (newer) tree and compares each node against its counterpart in the reference tree using the compare function. It examines FileInfo attributes such as size, permissions, and content hashes to detect changes. The method then marks each node with a DiffType—Added, Removed, Modified, or Unmodified—and propagates these statuses upward through parent nodes via deriveDiffType to indicate aggregate changes in directories.
Can the dive/filetree package be used independently of the Dive CLI?
Yes, the dive/filetree package functions as a standalone library for filesystem tree manipulation and comparison. You can import github.com/wagoodman/dive/dive/filetree to build trees programmatically using NewFileTree and AddPath, stack layers with Stack, and generate ASCII representations with String. While the package includes UI-specific types like ViewInfo and integrates with the Comparer cache for layer navigation, these components are optional when using the core tree modeling and diffing capabilities for custom applications.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →