dirstate
Dirstate in Mercurial: The Definitive Industry Review of Version Control State Management
In the landscape of distributed version control systems, few mechanisms are as critical—and as overlooked—as dirstate. As the backbone of Mercurial (commonly known as Hg), dirstate is what allows the system to instantly report which files in your working directory have been modified, added, removed, or exist in conflict. For development teams managing large monorepos or complex build pipelines, understanding dirstate is not merely academic; it is a prerequisite for diagnosing performance bottlenecks, resolving state corruption issues, and making informed toolchain decisions.
This comprehensive review examines dirstate from architectural, practical, and comparative perspectives—evaluating its strengths and limitations against competing version control paradigms.
Key Takeaways
- Dirstate is Mercurial's working-copy metadata store. It lives in the
.hg/dirstatefile and tracks per-file status (normal, modified, added, removed, unresolved) without scanning the filesystem on every operation. - Performance advantage is significant. Unlike Git, which walks the working tree to compute status, Mercurial reads cached dirstate entries—making
hg statusandhg difforders of magnitude faster on large repositories. - The trade-off is state fragility. Dirstate is a single-binary cache; manual filesystem edits outside Mercurial can desynchronize it, leading to phantom modifications or missed changes unless
hg rebuildstatusis invoked. - Dirstate design influenced modern VCS thinking. Its separation of index (metadata) from content (manifest) represents an early and elegant implementation of the index-file paradigm later adopted in various forms by other tools.
- For teams on Mercurial, dirstate hygiene is operational discipline. Knowing when to run
hg forget,hg addremove, andhg status --unknownprevents the most common source of developer frustration.
What Is Dirstate?
Definition and Core Concept
Dirstate (short for directory state) is Mercurial's internal data structure and on-disk format that records the relationship between the canonical revision-controlled files in a repository and the actual files present on the developer's working directory. Think of it as a fingerprint index: it tells Mercurial, for every path in the tree, whether the working copy matches the parent revision, has been altered, or requires manual resolution.
Where Dirstate Lives
The dirstate information is stored in a single file at .hg/dirstate within the repository root. This file uses Mercurial's binary pack format (since Mercurial 4.4) or the older flat format (in legacy repositories), and is managed entirely by the Mercurial process—developers should never edit it by hand.
What Each Dirstate Entry Contains
| Field | Description |
|---|---|
| Path | Relative file path within the repository |
| Mtime | Modification timestamp of the working-copy file |
| Msize | File size at last dirstate evaluation |
| Mode | Unix file mode bits (executability, symlink flag) |
| Type | File type indicator: 0 = normal, 1 = added, 2 = removed, 3 = resolved, 4 = unresolved (merge conflict) |
| P1 / P2 | Parent revision hashes (for merge tracking) |
| State flags | Additional markers: copied, ignored, symlink, symlink target |
How Dirstate Works Under the Hood
The Two-Layer Model: Manifest + Dirstate
Mercurial's architecture separates content tracking from file-status tracking through two distinct indexes:
- Manifest: Records the set of files and their content hashes at each revision checkpoint. This is the "what exists at this commit" layer.
- Dirstate: Records the "what is different in my working copy right now" layer. It references the manifest's parent revision and flags deviations from it.
This two-layer model is dirstate's most important design decision. When you run hg status, Mercurial does not hash every file on disk. Instead, it loads the dirstate cache, compares mtime/size against the current filesystem, and reports any discrepancies. Files whose metadata has not changed are considered unchanged—no disk I/O beyond the cache read is required.
The Validation Cycle
Every time Mercurial performs an operation that touches the working copy—commit, pull, merge, update—it validates the dirstate entries it needs and updates entries it creates. The system uses a lazy validation strategy: dirstate entries are checked on-demand rather than pre-scanned, which keeps startup time sub-second even for repositories with tens of thousands of files.
Mtime/Size as Change Proxies
Rather than computing cryptographic hashes for every file on every status check, dirstate uses modification time and file size as fast proxies for content change. If both match the cached values, the file is declared unchanged. Only when a discrepancy is detected does Mercurial fall back to content hashing. This optimization is the primary reason Mercurial outperforms many contemporaries on large codebases.
Advantages of Dirstate
1. Exceptional Performance on Large Repositories
The single greatest advantage of dirstate is speed. In benchmarks comparing equivalent operations:
hg statuson a repository with 50,000 files typically completes in under 100 milliseconds.git statuson a comparable repository often takes several seconds to minutes, depending on filesystem characteristics and ignore rules.
For CI/CD pipelines and developers working in massive monorepos (think automotive, semiconductor, or large-scale web platforms), this difference is not marginal—it is workflow-defining.
2. Precise Merge-Conflict Tracking
Dirstate natively supports a four-state conflict model (normal, added, removed, unresolved). When a merge produces conflicts, each conflicting path receives a dedicated unresolved marker. This is more granular than Git's binary "staged vs. unstaged" approach and integrates directly with Mercurial's conflict-resolution commands (hg resolve).
3. Copy Tracking and File Renames
Dirstate includes copy-operation metadata. When a file is moved or copied within the working tree, Mercurial records the relationship, enabling accurate history traversal across renames—a feature many VCS tools handle poorly or not at all.
4. Predictable Status Semantics
Because dirstate explicitly distinguishes between unknown files (not under version control) and ignored files (matched by .hgignore), status output is semantically precise. Developers can run hg status --unknown to audit all untracked files or hg status --ignored to verify ignore rules—capabilities that map directly to the dirstate type flags.
5. Low Memory Footprint
The dirstate file scales linearly with the number of tracked paths and is typically a few megabytes even for very large repositories. This contrasts with systems that maintain in-memory object graphs for every working-tree file, which can consume hundreds