Jesse A. Green / notebook

Lazygit Administrator & Developer Guide

Generated May 2026 from jesseduffield/lazygit at d167063

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

Lazygit is a terminal user interface (TUI) for git written in Go. It wraps the git CLI with an interactive, keyboard-driven interface that handles staging, committing, branching, rebasing, merge conflict resolution, cherry-picking, bisecting, worktree management, and more. This guide covers architecture, configuration internals, deployment, security considerations, developer workflows, and the full reference surface for administrators and contributors.


1. Architecture Overview

What the System Does

Lazygit is a single-binary Go application that renders a multi-panel terminal UI using the gocui TUI library (backed by tcell). It does not implement its own git logic; instead, it constructs and executes git CLI commands as subprocesses, parses their output, and presents the results in interactive panels. The application manages state through a combination of in-memory models, filesystem-based git state (.git/ directory), and a persisted YAML state file.

The architecture follows a controller-context-command layered pattern: controllers handle user input and business logic, contexts manage panel state and focus, and command structs encapsulate all git subprocess interactions.

Architecture Diagram

+-----------------------------------------------------------------+
|                        main.go (LDFLAGS)                        |
|                              |                                  |
|                         app.Start()                             |
|                              |                                  |
|           +------------------+------------------+               |
|           |                                     |               |
|     pkg/config                            pkg/app               |
|   (YAML config,                     (CLI parsing via            |
|    migrations,                       flaggy, bootstrap,         |
|    editor presets)                    git version check)         |
|           |                                     |               |
|           +------------------+------------------+               |
|                              |                                  |
|                         pkg/gui                                 |
|                    (gocui TUI engine)                            |
|           +------------------+------------------+               |
|           |                  |                  |               |
|    gui/controllers     gui/context        gui/services          |
|    (64 controllers)   (panel state,    (custom commands,        |
|                        focus mgmt)      background tasks)       |
|           |                                                     |
|    pkg/commands                                                 |
|    (GitCommand facade)                                          |
|           |                                                     |
|    pkg/commands/git_commands                                    |
|    (22 command structs + 9 loaders)                             |
|           |                                                     |
|    oscommands.ICmdObjBuilder                                    |
|    (subprocess execution)                                       |
|           |                                                     |
|        git CLI                                                  |
+-----------------------------------------------------------------+

Component Inventory

Component Path Role
Entry point main.go Sets LDFLAGS build info, calls app.Start()
CLI parser pkg/app/entry_point.go 14 CLI flags via flaggy library
App bootstrap pkg/app/app.go Config load, git version check, repo setup, GUI init
Configuration pkg/config/ YAML config loading, validation, automatic migrations, editor presets
GUI engine pkg/gui/ gocui-based TUI: layout, views, event loop, background routines
Controllers pkg/gui/controllers/ 64 controllers, one per feature area (files, branches, commits, etc.)
Contexts pkg/gui/context/ Panel state management, focus tracking, view-to-context mapping
Custom commands pkg/gui/services/custom_commands/ User-defined commands with Go template prompts
Git facade pkg/commands/git.go GitCommand struct aggregating all command structs
Git commands pkg/commands/git_commands/ 22 command structs (Branch, Commit, Rebase, Sync, etc.)
Git loaders pkg/commands/git_commands/ 9 data loaders (BranchLoader, CommitLoader, etc.)
Models pkg/commands/models/ Data models: Commit, Branch, File, Remote, Tag, etc.
Patch system pkg/commands/patch/ Interactive patch building for partial staging/unstaging
i18n pkg/i18n/ 9 languages via Crowdin (en, zh-CN, zh-TW, pl, nl, ja, ko, ru, pt)
Theming pkg/theme/ Color attribute parsing, theme management
Utilities pkg/utils/ Rebase-todo manipulation, string helpers, fuzzy matching
Update checker pkg/updates/ GitHub release polling, self-update (goreleaser builds)
Constants pkg/constants/ URL links for docs, issues, discussions, releases
Schema schema/ JSON Schema for config validation
Integration tests pkg/integration/ Full TUI-driven test framework with robot driver
Demo scripts demo/ Automated demo recordings
Snake game pkg/snake/ Easter egg built into the TUI

Technology Stack

Layer Technology Version
Language Go 1.25.0
TUI framework gocui (jesseduffield fork) v0.3.1+
Terminal backend tcell v2.13.8
CLI parsing flaggy v1.8.0
Config format YAML gopkg.in/yaml.v3
XDG paths adrg/xdg v0.5.3
Clipboard atotto/clipboard v0.1.4
Filesystem spf13/afero v1.15.0
Logging logrus v1.9.3
GitHub integration cli/go-gh v2.13.0
Fuzzy search sahilm/fuzzy + fuzzy-patricia v0.1.1
Diff display gookit/color v1.4.2
Deadlock detection sasha-s/go-deadlock v0.3.9

Data Flow

  1. Startup: main.go passes LDFLAGS build info to app.Start(). Entry point parses CLI flags via flaggy. Config loads from YAML (with automatic migrations). Git version is validated (>= 2.32.0). Repo paths are resolved.
  2. GUI initialization: gui.NewGui() creates the gocui instance, registers all controllers and contexts, starts background routines (auto-fetch, auto-refresh).
  3. User interaction: Keypresses are dispatched through gocui to the active context's controller. Controllers invoke git command structs to execute operations.
  4. Git operations: Command structs construct git CLI arguments via NewGitCmd() builder, execute them as subprocesses, and parse output. Results update in-memory models.
  5. Refresh cycle: After each operation, affected views are refreshed by re-running the appropriate loaders (BranchLoader, CommitLoader, etc.).
  6. Background: Auto-fetch runs on a configurable interval (refresher.fetchInterval, default 60s). Auto-refresh watches for file changes (refresher.refreshInterval, default 10s).

Note

Lazygit does not embed or link against libgit2. All git operations are performed by shelling out to the git CLI binary. This means the system's installed git version directly determines available features and behavior.


2. System Requirements

Requirement Detail
Git >= 2.32.0 (validated at startup; exits with error if older)
OS Linux, macOS, Windows, FreeBSD
Terminal Any terminal emulator supporting ANSI escape codes; 256-color or true-color recommended
Go (build only) 1.25.0
Disk Minimal; single binary ~20MB
Memory Depends on repository size; typical usage 50-200MB

Important

The minimum git version of 2.32.0 is enforced at startup via app.validateGitVersion(). If you see "Please upgrade to git 2.32.0 or later", this is a hard requirement, not a suggestion.

Optional Dependencies

Feature Requires
git-flow commands git-flow installed and initialized in repo
GitHub PR integration GitHub CLI auth token (via go-gh library), or network access to GitHub GraphQL API
Custom pagers delta, diff-so-fancy, or any pager binary in $PATH
External diff tools difftastic, meld, or tool of choice

3. Configuration Reference

Config File Locations

Lazygit resolves its configuration file in this order:

  1. LG_CONFIG_FILE environment variable (comma-separated list of paths)
  2. CONFIG_DIR environment variable followed by $CONFIG_DIR/config.yml
  3. XDG legacy path: $XDG_CONFIG_HOME/jesseduffield/lazygit/config.yml
  4. XDG standard path: $XDG_CONFIG_HOME/lazygit/config.yml (default: ~/.config/lazygit/config.yml)

If no config file exists at the resolved path, lazygit creates an empty one automatically.

Tip

Use LG_CONFIG_FILE to load multiple config files in order. Values in later files override earlier ones, except customCommands which are appended.

Config Files Table

File Format Location Purpose
config.yml YAML ~/.config/lazygit/ (XDG) Primary user configuration
state.yml YAML ~/.local/state/lazygit/ (XDG) Persisted app state (recent repos, PR cache)
.lazygit.yml YAML Repository root Per-repo config overlay (loaded after global)

Environment Variables

Variable Type Default Description Required?
LG_CONFIG_FILE string (none) Comma-separated list of config file paths No
CONFIG_DIR string (none) Override config directory No
LAZYGIT_LOG_PATH string (none) Override log file path No
GIT_DIR string (none) Override git directory No
GIT_WORK_TREE string (none) Override working tree path No
DEBUG bool false Enable debug logging No
LOG_LEVEL string (none) Set log verbosity No
GIT_EDITOR string (none) Git editor override (checked before VISUAL/EDITOR) No
VISUAL string (none) Visual editor fallback No
EDITOR string (none) Editor fallback No
CONTAINER string (none) Container detection for Linux config paths No
SHOW_RECENT_REPOS bool false Force recent repos prompt (testing) No
FISH_VERSION string (none) Detected by editor preset logic for fish shell No
NU_VERSION string (none) Detected by editor preset logic for nushell No

Test/CI-Only Environment Variables

Variable Purpose
LAZYGIT_HEADLESS Enable headless mode for integration tests
INPUT_DELAY Milliseconds between simulated keypresses
PARALLEL_TOTAL Total parallel test shards
PARALLEL_INDEX Current shard index
LAZYGIT_RACE_DETECTOR Enable Go race detector for tests
LAZYGIT_GOCOVERDIR Code coverage output directory

CLI Flags

Lazygit uses the flaggy library (not Cobra) for CLI argument parsing.

Flag Type Default Description
-d, --debug bool false Run in debug mode
-v, --version bool false Print version info
-p, --path string . Path to git repo
-f, --filter string (none) Filter files by path
-g, --git-dir string (none) Override $GIT_DIR
-w, --work-tree string (none) Override $GIT_WORK_TREE
--use-config-dir string (none) Override config directory
--use-config-file string (none) Comma-separated config files
-l, --log bool false Tail the lazygit log (requires debug mode)
--print-config-dir bool false Print config directory and exit
--print-default-config bool false Print default config and exit

Positional argument: lazygit <status|branch|log|stash> opens the corresponding panel on startup.

Automatic Config Migrations

Lazygit automatically migrates config files on startup when deprecated keys are detected. The following migrations are applied:

Old Key New Key Migration Type
gui.skipUnstageLineWarning gui.skipDiscardChangeWarning Rename
keybinding.universal.executeCustomCommand keybinding.universal.executeShellCommand Rename
gui.windowSize gui.screenMode Rename
keybinding.files.openMergeTool keybinding.files.openMergeOptions Rename
null keybindings <disabled> Value change
git.commitPrefix (object) git.commitPrefix (array) Structure change
git.commitPrefixes (objects) git.commitPrefixes (arrays) Structure change
subprocess/stream/showOutput output enum Custom command field merge
git.allBranchesLogCmd git.allBranchesLogCmds (array) Singular to plural
git.paging (object) git.pagers (array) Structure change

Warning

Config migrations write back to disk automatically. If you use version-controlled config files, expect automatic modifications on first run after upgrading lazygit. The migration output is printed to stdout before the GUI starts.

Editor Presets

Lazygit ships with 16+ editor presets configured in pkg/config/editor_presets.go:

Preset Editor Edit Command Edit at Line
vi vi vi -- {{filename}} vi +{{line}} -- {{filename}}
vim vim vim -- {{filename}} vim +{{line}} -- {{filename}}
nvim Neovim nvim -- {{filename}} nvim +{{line}} -- {{filename}}
nvim-remote Neovim (remote) nvim --server "$NVIM" --remote-tab {{filename}} (with +{{line}})
lvim LunarVim lvim -- {{filename}} lvim +{{line}} -- {{filename}}
emacs Emacs emacs -- {{filename}} emacs +{{line}} -- {{filename}}
micro micro micro -- {{filename}} micro +{{line}} -- {{filename}}
nano nano nano -- {{filename}} nano +{{line}} -- {{filename}}
kakoune Kakoune kak -- {{filename}} kak +{{line}} -- {{filename}}
helix Helix hx -- {{filename}} hx {{filename}}:{{line}}
vscode VS Code code --reuse-window -- {{filename}} code ... --goto {{filename}}:{{line}}
sublime Sublime Text subl -- {{filename}} subl -- {{filename}}:{{line}}
bbedit BBEdit bbedit -- {{filename}} bbedit +{{line}} -- {{filename}}
xcode Xcode open -a Xcode {{filename}} (same)
zed Zed zed -- {{filename}} zed {{filename}}:{{line}}
acme Acme acme -- {{filename}} acme -- {{filename}}:{{line}}

Custom Commands Schema

Custom commands are defined in config.yml under customCommands: and support the following fields:

Field Type Description
key string Keybinding to trigger the command
command string Shell command (Go templates supported)
context string Panel context where this command is available
description string Human-readable description
output enum none (default), terminal, log, logWithPty, popup
prompts array Interactive prompts before execution
loadingText string Status bar text while running
after object Post-execution hooks
commandMenu array Sub-menu of commands (mutually exclusive with other fields except key and description)

Output enum values:

Value Behavior
none Run silently, refresh UI afterward
terminal Suspend TUI and run in terminal (replaces old subprocess: true)
log Stream output to command log panel
logWithPty Stream with pseudo-terminal (preserves colors)
popup Show output in popup dialog

4. Deployment & Scaling

Build from Source

go install github.com/jesseduffield/lazygit@latest

Or build with version info:

go build -ldflags "\
  -X main.version=0.44.1 \
  -X main.commit=$(git rev-parse HEAD) \
  -X main.date=$(date -u +%Y-%m-%dT%H:%M:%SZ) \
  -X main.buildSource=source" \
  -o lazygit

Distribution Methods

Lazygit is distributed as a single static binary. Primary distribution channels:

Method Command
Homebrew (macOS/Linux) brew install lazygit
Scoop (Windows) scoop install lazygit
Chocolatey (Windows) choco install lazygit
Arch Linux pacman -S lazygit
Nix nix-env -iA nixpkgs.lazygit
Go install go install github.com/jesseduffield/lazygit@latest
Binary release GitHub Releases (goreleaser)

Note

The buildSource LDFLAGS value indicates how the binary was built. Goreleaser builds set it to binaryBuild. This affects the update checker behavior.

Self-Update Mechanism

The pkg/updates/ module checks GitHub releases periodically. When a newer version is found, lazygit prompts the user to update. The update mechanism downloads the new binary and replaces the current one. This only works for goreleaser-built binaries (where buildSource == "binaryBuild"). Package-manager installs should be updated through their respective package managers.


5. Operations

State Management

Lazygit persists runtime state in state.yml (XDG state directory):

Field Type Purpose
LastUpdateCheck int64 Unix timestamp of last update check
RecentRepos string[] Recently opened repository paths
StartupPopupVersion int Tracks which startup popups have been shown
DidShowHunkStagingHint bool Whether hunk staging hint was shown
LastVersion string Last lazygit version used (for release notes)
ShellCommandsHistory string[] Shell command history (in YAML: customcommandshistory)
HideCommandLog bool Whether command log panel is hidden
GithubPullRequests map Cached PR data per repo path

Tip

The GithubPullRequests cache allows PR info to display instantly on startup before the async GitHub API refresh completes. This cache is keyed by repo path.

Background Routines

The BackgroundRoutineMgr in pkg/gui/background.go manages two primary background routines:

  1. Auto-fetch (git.autoFetch: true): Runs git fetch at refresher.fetchInterval seconds (default: 60). An immediate fetch is triggered on startup and when switching repos.
  2. Auto-refresh (git.autoRefresh: true): Polls the working tree for changes at refresher.refreshInterval seconds (default: 10).

Both routines pause when the GUI is suspended (e.g., during subprocess execution). In debug mode, memory usage is logged every 10 seconds.

Note

Setting refresher.fetchInterval or refresher.refreshInterval to 0 or negative disables the respective background routine and logs a warning.

Logging

Mode Log Path Content
Production (none) No file logging; only fatal errors to stderr
Debug (-d) $LAZYGIT_LOG_PATH or ~/.local/state/lazygit/development.log Verbose logrus output

Tail logs in a separate terminal:

lazygit --debug &
lazygit --log

Command Log

All git commands executed by lazygit are logged to the built-in command log panel (toggle visibility with the backtick key). Each entry shows the action name and the exact git command with arguments. This is invaluable for understanding what lazygit did and for debugging unexpected behavior.


6. Performance

Refresh Strategy

Lazygit uses a targeted refresh system. Each operation declares which views need refreshing via RefreshOptions.Scope:

Scope What's Refreshed
FILES Working tree status, staged/unstaged files
BRANCHES Branch list, current branch
COMMITS Commit log, commit graph
REMOTES Remote tracking branches
TAGS Tag list
PULL_REQUESTS GitHub PR data
STASH Stash entries

This avoids full-UI refreshes after every operation. The refresh mode can be SYNC (blocking) or ASYNC (non-blocking).

Large Repository Considerations

  • Commit loading: Uses git log with --oneline and pagination. Large histories load incrementally.
  • File tree: Can be displayed flat or as a tree. Flat mode is faster for repos with many files.
  • Auto-fetch: On very large remotes, background fetch can cause noticeable pauses. Increase refresher.fetchInterval or set git.autoFetch: false.
  • Pagers: External pagers like delta add overhead per diff. For very large diffs, consider disabling custom pagers.

Tip

If lazygit feels sluggish on a large repo, start by increasing refresher.fetchInterval to 300 (5 minutes) and check if auto-refresh is the bottleneck by temporarily setting git.autoRefresh: false.


7. Security

Subprocess Execution

Lazygit executes git commands as subprocesses. All command arguments are constructed programmatically via the NewGitCmd() builder pattern, which prevents shell injection. Commands are not passed through a shell interpreter unless explicitly requested by the user (custom commands, shell commands).

Caution

Custom commands (customCommands in config) and shell commands (: prompt) execute arbitrary shell commands with the user's permissions. Treat config files from untrusted sources as potentially dangerous, especially per-repo .lazygit.yml files in cloned repositories.

Credential Handling

Lazygit delegates all credential handling to git's built-in credential system. It does not store, cache, or manage credentials directly. Authentication for push/pull/fetch operations uses whatever credential helper git is configured with (credential.helper).

GitHub Integration

The GitHub PR feature uses the go-gh library to query the GitHub GraphQL API. Authentication uses tokens from the GitHub CLI (gh auth). PR data is cached in state.yml per repo path.

Warning

The GitHub PR fetch runs automatically on startup and refresh. If you work with repositories where you lack GitHub access (e.g., submodules in different organizations), this can cause 401 errors. See issue #5516. There is currently no config option to disable PR fetching per-repo.

Hosting Service Detection

Lazygit detects the hosting service from remote URLs to generate correct web links (for opening PRs, issues, etc.). Supported services:

Service URL Pattern
GitHub github.com
GitLab gitlab.com
Bitbucket bitbucket.org
Azure DevOps dev.azure.com, ssh.dev.azure.com
Gitea Configurable via services config
Codeberg codeberg.org
Gogs Configurable via services config

8. Developer Internals

Repository Structure

lazygit/
  main.go                          Entry point (LDFLAGS)
  cmd/
    i18n/main.go                   i18n JSON export tool
    integration_test/main.go       Integration test runner
  pkg/
    app/                           App bootstrap, CLI parsing, error handling
    commands/
      git.go                       GitCommand facade (aggregates all command structs)
      git_commands/                 22 command structs + 9 loaders + models
      hosting_service/             URL parsing for GitHub/GitLab/Bitbucket/etc.
      models/                      Data models (Commit, Branch, File, Remote, etc.)
      oscommands/                  OS-level command execution, platform detection
      patch/                       Interactive patch building
    config/                        YAML config, migrations, validation, editor presets
    gui/
      gui.go                       Main GUI struct and initialization
      layout.go                    Panel layout calculation
      background.go                Background fetch/refresh routines
      controllers/                 64 feature controllers
      context/                     Panel contexts and focus management
      services/custom_commands/    Custom command execution engine
      presentation/                Rendering: graph, icons, authors, formatting
      keybindings/                 Keybinding registration
      popup/                       Dialog/prompt management
      modes/                       UI modes (cherrypicking, diffing, filtering)
      mergeconflicts/              Merge conflict resolution UI
      patch_exploring/             Patch navigation and selection
      filetree/                    File tree data structure
      style/                       Text styling utilities
      status/                      Status bar management
      types/                       GUI type definitions and interfaces
    common/                        Shared Common struct (Log, Tr, AppState, Fs)
    constants/                     URLs and links
    i18n/                          Translation sets, language detection
    integration/                   Integration test framework
    logs/                          Logger initialization
    snake/                         Easter egg snake game
    tasks/                         Async task management
    theme/                         Theme loading and color parsing
    updates/                       Self-update mechanism
    utils/                         String helpers, rebase-todo manipulation, fuzzy
  schema/                          JSON Schema for config validation
  docs/                            User-facing documentation
  demo/                            Demo recording scripts
  test/                            Test fixtures and results
  scripts/                         Development scripts

Architecture Deep Dive

Controller Pattern

Each feature area has a dedicated controller in pkg/gui/controllers/. Controllers implement the IController interface and register keybindings for their context. The 64 controllers include:

Controller File Handles
FilesController files_controller.go File staging, unstaging, discarding
BranchesController branches_controller.go Branch operations
LocalCommitsController local_commits_controller.go Commit rewriting, rebase
RemotesController remotes_controller.go Remote management
TagsController tags_controller.go Tag creation, deletion
StashController stash_controller.go Stash operations
SubmodulesController submodules_controller.go Submodule management
WorktreesController worktrees_controller.go Worktree operations
BisectController bisect_controller.go Git bisect workflow
MergeConflictsController merge_conflicts_controller.go Conflict resolution
StagingController staging_controller.go Hunk/line staging
PatchBuildingController patch_building_controller.go Partial commit extraction
GitFlowController git_flow_controller.go git-flow commands
UndoController undo_controller.go Undo/redo via reflog
SyncController sync_controller.go Push/pull/fetch
SnakeController snake_controller.go Easter egg game

Command Struct Pattern

The GitCommand struct in pkg/commands/git.go is a facade that aggregates 22 specialized command structs:

GitCommand
  .Blame          -> BlameCommands
  .Branch         -> BranchCommands
  .Commit         -> CommitCommands
  .Config         -> ConfigCommands
  .Custom         -> CustomCommands
  .Diff           -> DiffCommands
  .File           -> FileCommands
  .Flow           -> FlowCommands
  .Patch          -> PatchCommands
  .Rebase         -> RebaseCommands
  .Remote         -> RemoteCommands
  .Stash          -> StashCommands
  .Status         -> StatusCommands
  .Submodule      -> SubmoduleCommands
  .Sync           -> SyncCommands
  .Tag            -> TagCommands
  .WorkingTree    -> WorkingTreeCommands
  .Bisect         -> BisectCommands
  .Worktree       -> WorktreeCommands
  .Version        -> GitVersion
  .GitHub          -> GitHubCommands
  .HostingService -> HostingService

Each command struct shares a GitCommon base that provides access to the command builder, OS commands, repo paths, and config. Commands are constructed using the fluent NewGitCmd() builder:

cmdArgs := NewGitCmd("branch").
    Arg("--sort=-committerdate").
    ArgIf(containsField, "--format=%(HEAD)...").
    ToArgv()

Data Loaders

Nine loader structs handle parsing git output into model objects:

Loader Parses Output Model
BranchLoader git branch, git for-each-ref Branch
CommitLoader git log with custom format Commit
CommitFileLoader git diff-tree CommitFile
FileLoader git status --porcelain File
ReflogCommitLoader git reflog Commit
RemoteLoader git remote, git branch -r Remote, RemoteBranch
StashLoader git stash list StashEntry
TagLoader git tag --sort Tag
WorktreeLoader git worktree list Worktree

Code Patterns and Conventions

  • Dependency injection: All command structs receive dependencies through constructors, enabling isolated testing.
  • Fluent command builder: NewGitCmd("...") with .Arg(), .ArgIf(), .ToArgv() chain.
  • Error handling: Uses go-errors/errors for stack traces. Known errors mapped in pkg/app/errors.go.
  • Concurrency: Background routines use goroutines with channels. GUI updates are dispatched to the UI thread via onUIThread().
  • Deadlock detection: Uses sasha-s/go-deadlock in debug builds to detect mutex deadlocks.
  • File operations: Uses spf13/afero filesystem abstraction, enabling in-memory filesystem for tests.

Testing Strategy

Lazygit employs a comprehensive multi-level testing approach:

Unit tests (*_test.go files throughout):

  • Config migration tests in pkg/config/app_config_test.go
  • Command builder tests in pkg/commands/git_commands/*_test.go
  • Utility function tests in pkg/utils/*_test.go

Integration tests (pkg/integration/):

  • Full TUI-driven tests with a robot driver that simulates user input
  • Tests live in pkg/integration/tests/ organized by feature area
  • Three execution modes:
    • CLI: go run cmd/integration_test/main.go cli [--slow] [--sandbox] <tests>
    • TUI: go run cmd/integration_test/main.go tui (interactive test browser)
    • Go test: go test pkg/integration/clients/*.go (CI/headless)
# Run all integration tests
go run cmd/integration_test/main.go cli

# Run specific test
go run cmd/integration_test/main.go cli commit/new_branch

# Run in sandbox mode (setup runs, then you control lazygit)
go run cmd/integration_test/main.go cli --sandbox commit/new_branch

# Run with slow input for debugging
go run cmd/integration_test/main.go cli --slow commit/new_branch

# Debug with dlv
go run cmd/integration_test/main.go cli -debug tag/reset.go

Tip

The TUI test runner (go run cmd/integration_test/main.go tui) is the easiest way to browse and run tests. Press t for slow mode, s for sandbox mode, d for debugger mode.

Test results are stored in test/_results/ where you can inspect the repository state after a failed test.

Testing against old git versions: Use rtx (or mise) to install older git versions:

rtx plugin add git
rtx install git 2.32.0
rtx local git 2.32.0

Potential Improvements

Based on open issues and code analysis:

  • PR fetch opt-out (#5516): GitHub PR fetching cannot be disabled per-repo, causing 401 errors with submodules in different organizations
  • MainBranches globbing (#5246): mainBranches config only accepts exact names, not glob patterns
  • Quit during search (#5520): The quit keybinding does not work while the search panel is active
  • Conventional commits (#4414): No built-in support for conventional commit format; achievable via git.commitPrefix config
  • Alt/Opt modifier (#2166): Limited ability to map Alt/Option key combinations in keybindings

9. Integration Points

External Editor Integration

Lazygit launches external editors for commit message editing, file editing, and interactive rebase. The editor is resolved in this order:

  1. os.editPreset config maps to preset command templates
  2. os.edit / os.editAtLine / os.editAtLineAndWait config for custom templates
  3. $GIT_EDITOR then $VISUAL then $EDITOR then system default

Editor templates support Go template variables: {{filename}}, {{line}}.

Pager Integration

Custom pagers for diff display are configured via git.pagers:

git:
  pagers:
    - colorArg: always
      pager: delta --dark --paging=never

Hosting Service Integration

Lazygit generates web URLs for the configured hosting service. Custom services (Gitea, Gogs) can be configured:

services:
  "git.example.com": "gitea:https://git.example.com"

Daemon Mode

Lazygit uses a daemon subprocess pattern for operations that need to run as $GIT_EDITOR or $GIT_SEQUENCE_EDITOR. The main lazygit process spawns a child lazygit with special daemon environment variables. The child process performs a specific task (e.g., modifying a rebase-todo file) and exits. This is how interactive rebase operations are handled without requiring manual editor interaction.


10. Troubleshooting

Error: Please upgrade to git X.XX.X or later

Cause: Lazygit requires git >= 2.32.0. Your installed version is older.

Solution:

  1. Check version: git --version
  2. Upgrade git through your package manager
  3. Verify: git --version shows >= 2.32.0

Error: The config at <path> couldn't be parsed

Cause: Invalid YAML syntax in configuration file.

Solution:

  1. Validate YAML: python3 -c "import yaml; yaml.safe_load(open('~/.config/lazygit/config.yml'))"
  2. Check for tab characters (YAML requires spaces)
  3. Use lazygit --print-default-config as a reference

Prevention: Use an editor with YAML syntax checking.

Warning

If config parsing fails, lazygit will exit before the GUI starts. There is no fallback to defaults on parse error.

Error: The config at <path> has a validation error

Cause: Config has valid YAML but contains invalid values (e.g., unknown enum value, wrong type).

Solution:

  1. Read the error message carefully; it names the invalid key and allowed values
  2. Cross-reference with the JSON schema in schema/config.json
  3. Fix the value and restart

Error: You need to install git-flow and enable it in this repo

Cause: Attempted to use git-flow features without git-flow installed or initialized.

Solution:

  1. Install git-flow: brew install git-flow (macOS) or apt install git-flow (Debian/Ubuntu)
  2. Initialize in repo: git flow init

Error: GraphQL query failed with status: <status>

Cause: GitHub API request failed. Common with 401 (unauthorized) or 403 (rate limited).

Solution:

  1. Authenticate with GitHub CLI: gh auth login
  2. Check rate limits: gh api rate_limit
  3. If the error occurs on submodule switching, this is a known issue (#5516)

Note

GitHub PR integration errors are non-fatal. Lazygit will continue to function without PR data displayed.

Crash on startup in specific terminals

Cause: Terminal emulator incompatibility with tcell terminal detection. Known to have affected Ghostty (#5511, fixed).

Solution:

  1. Update your terminal emulator to the latest version
  2. Try setting $TERM explicitly: TERM=xterm-256color lazygit
  3. Try running in a different terminal to isolate the issue

Cause: Starting lazygit from a symlinked directory that points into a git repo can cause path resolution issues (#3015).

Solution: Navigate to the actual directory rather than the symlink, or use lazygit --path /actual/path.