A Living Checkout: Updates, Security, and Reversibility

Oh My Zsh is not finished when the installer creates a .zshrc. Its startup code can check for updates, the checkout can change, contributors can alter plugins and themes, and the user may eventually decide to remove it. This chapter follows that lifecycle to separate freshness from safety, examine the project’s maintenance controls, and clarify what the backup and uninstall paths can realistically restore. The result is a trust model for a shell that changes over time.

An update is part of startup behavior

The update checker runs near the beginning of initialization, but it does not immediately pull the latest code. It first decides whether an update check is allowed and useful. The user can choose an update mode, a checking frequency, and a cooldown that delays adoption of very recent changes. If updates are disabled, the checker exits. It also exits when the installation is not writable or owned by the user, when the process is not running interactively, when the configured interval has not elapsed, when Git is unavailable, or when the installation is not a Git worktree.

These gates give the update path a different status from the rest of startup. A user can still load the framework when an update is skipped. A read-only installation can remain usable even though it cannot update itself. A non-interactive shell can avoid prompting or changing a checkout. The same code that makes maintenance convenient therefore tries to avoid treating every shell launch as permission to modify files.

When the installation uses the official repository, the checker can compare the local branch with the remote commit through the GitHub API. When a cooldown is configured, it instead fetches and looks for a remote first-parent commit old enough to satisfy that delay. If the remote is not the official GitHub repository, the checker cannot make the same official-repository comparison and takes a more conservative path. These choices are implementation boundaries, not guarantees that a network check will always succeed.

If an update is due and available, the runtime still has several possible transitions. In reminder mode it tells the user that an update is available and leaves the checkout alone. In the ordinary prompt mode it asks for confirmation unless the user has already typed input, in which case it avoids interrupting the command line and displays a reminder. In automatic mode it proceeds without asking. An experimental background mode can perform the work away from the immediate prompt and report the result later.

The update path uses a lock directory so two shell starts do not run the update logic simultaneously. It records the last check and, after an attempt, stores success or error information in the cache. That stored result matters for background operation, but it also makes update state persistent across sessions. A later prompt may be reporting the result of an earlier update process rather than performing new work at that moment.

The separate upgrade script performs the repository operation. It reads the configured remote and branch, repairs known legacy upstream settings, and applies Git configuration intended to avoid some common update problems. Without a cooldown it pulls with rebase; with a cooldown it fetches and merges a sufficiently old commit.