# How lazygit Handles Large Repositories with Many Files and Commits

> Discover how lazygit efficiently manages large repositories by lazy-loading commit history and optimizing git log output for a smooth experience.

- Repository: [Jesse Duffield/lazygit](https://github.com/jesseduffield/lazygit)
- Tags: performance
- Published: 2026-03-02

---

**lazygit handles large repositories by lazy-loading commit history and limiting initial `git log` output to 300 commits, automatically expanding the view when users scroll past a 200-commit threshold.**

The lazygit terminal UI is engineered to remain responsive even when navigating repositories containing thousands of files and tens of thousands of commits. According to the lazygit source code, this performance is achieved through a **lazy-loading** architecture that defers expensive Git operations until explicitly requested by the user.

## The Lazy-Loading Architecture

### Limiting Initial Commit History

When lazygit opens a repository, it does not immediately load the entire commit graph. Instead, the `CommitLoader` in [`pkg/commands/git_commands/commit_loader.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/commands/git_commands/commit_loader.go) constructs a `git log` command that returns only the most recent **300 commits** by default.

The command builder uses conditional arguments to inject the limit flag only when the `Limit` option is enabled:

```go
func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) *oscommands.CmdObj {
    cmdArgs := NewGitCmd("log").
        Arg(opts.RefName).
        ArgIf(opts.All, "--all").
        Arg("--oneline").
        Arg(prettyFormat).
        ArgIf(opts.Limit, "-300") // <-- limits output to 300 commits
    return self.cmd.New(cmdArgs).DontLog()
}

```

*Source: [commit_loader.go#L89-L99](https://github.com/jesseduffield/lazygit/blob/master/pkg/commands/git_commands/commit_loader.go)*

### The Commit Threshold Mechanism

To balance responsiveness with access to full history, lazygit implements a **threshold constant** of **200 commits**. This heuristic is defined in [`pkg/gui/controllers/local_commits_controller.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/gui/controllers/local_commits_controller.go) as `COMMIT_THRESHOLD`.

When a user scrolls through the commit list and the selected index exceeds this threshold, the controller automatically disables the limit flag and refreshes the view to load the complete commit history.

## Key Implementation Details

### Building the Limited Git Command

The `CommitLoader` orchestrates the lazy-loading process. In [`pkg/commands/git_commands/commit_loader.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/commands/git_commands/commit_loader.go), the `loadCommits` function executes the constructed command and parses the output into `models.Commit` structs. By default, the UI initializes with `GetCommitsOptions{Limit: true}`, ensuring that massive repositories do not freeze the terminal on startup.

### Monitoring User Navigation

The `LocalCommitsController` in [`pkg/gui/controllers/local_commits_controller.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/gui/controllers/local_commits_controller.go) monitors focus events to detect when the user approaches the end of the limited commit range:

```go
func (self *LocalCommitsController) GetOnFocus() func(types.OnFocusOpts) {
    return func(types.OnFocusOpts) {
        context := self.context()
        if context.GetSelectedLineIdx() > COMMIT_THRESHOLD && context.GetLimitCommits() {
            context.SetLimitCommits(false)               // remove the limit
            self.c.Refresh(types.RefreshOptions{
                Mode:  types.ASYNC,
                Scope: []types.RefreshableView{types.COMMITS},
            })
        }
    }
}

```

*Source: [local_commits_controller.go#L9-L15](https://github.com/jesseduffield/lazygit/blob/master/pkg/gui/controllers/local_commits_controller.go)*

### Managing UI State

The limit flag is stored in the `LocalCommitsContext` view model defined in [`pkg/gui/context/local_commits_context.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/gui/context/local_commits_context.go). This context provides `SetLimitCommits` and `GetLimitCommits` methods that the controller uses to toggle between restricted and full history modes.

```go
func (self *LocalCommitsViewModel) SetLimitCommits(value bool) { self.limitCommits = value }
func (self *LocalCommitsViewModel) GetLimitCommits() bool    { return self.limitCommits }

```

*Source: [local_commits_context.go#L30-L36](https://github.com/jesseduffield/lazygit/blob/master/pkg/gui/context/local_commits_context.go)*

## Automatic Expansion Triggers

### Scrolling Past the Threshold

When the selected commit index exceeds the `COMMIT_THRESHOLD` of 200, lazygit assumes the user intends to browse deeper history. The controller clears the `limitCommits` flag and triggers an asynchronous refresh, causing the next `git log` execution to omit the `-300` argument and stream the full commit graph.

### Show Whole Git Graph Toggle

Users can manually force a full load by toggling the "Show whole git graph" option. This action calls `SetShowWholeGitGraph` on the context, which internally disables the commit limit:

```go
self.context().SetShowWholeGitGraph(!self.context().GetShowWholeGitGraph())
if self.context().GetShowWholeGitGraph() {
    self.context().SetLimitCommits(false) // load everything
}

```

*Source: [local_commits_controller.go](https://github.com/jesseduffield/lazygit/blob/master/pkg/gui/controllers/local_commits_controller.go)*

## File-Level Optimizations

Beyond commit history, lazygit applies similar lazy-loading principles to file operations. When displaying working tree changes, the UI only executes `git diff` for the specific file currently under the cursor. This on-demand diff generation, implemented in [`pkg/commands/git_commands/working_tree.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/commands/git_commands/working_tree.go), prevents the terminal from freezing when repositories contain thousands of modified files.

## Summary

- **lazygit** limits initial commit history to **300 commits** using a conditional `-300` flag in `git log` commands constructed by `CommitLoader`.
- The **200-commit threshold** (`COMMIT_THRESHOLD`) triggers automatic expansion when users scroll deep into the history.
- **UI state management** via `LocalCommitsContext` tracks whether the view is limited, allowing controllers to toggle between restricted and full modes.
- **Manual overrides** like "Show whole git graph" immediately disable limits for users who need complete visibility.
- **File-level lazy loading** ensures diffs are computed only for selected files, keeping the UI responsive in large working trees.

## Frequently Asked Questions

### How many commits does lazygit load by default?

By default, lazygit loads only the **300 most recent commits** when you open a repository. This limit is applied via the `-300` argument passed to the `git log` command in [`pkg/commands/git_commands/commit_loader.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/commands/git_commands/commit_loader.go). The restriction keeps the UI responsive on repositories with tens of thousands of commits.

### At what point does lazygit load the full commit history?

lazygit loads the full commit history when you scroll past the **200-commit threshold** (defined as `COMMIT_THRESHOLD` in [`pkg/gui/controllers/local_commits_controller.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/gui/controllers/local_commits_controller.go)). Once your cursor moves beyond the 200th commit, the controller automatically clears the limit flag and refreshes the view to stream the complete Git history.

### Can I force lazygit to show all commits immediately?

Yes. You can toggle the **"Show whole git graph"** option, which calls `SetShowWholeGitGraph` on the `LocalCommitsContext`. This action disables the commit limit immediately, causing the next refresh to execute a full `git log` without the `-300` restriction. Alternatively, simply scrolling past the 200-commit threshold triggers the same behavior automatically.

### How does lazygit handle repositories with thousands of modified files?

lazygit uses **on-demand diff generation** for file operations. Rather than computing diffs for every modified file upfront, the UI only executes `git diff` for the specific file currently selected in the working tree view. This implementation in [`pkg/commands/git_commands/working_tree.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/commands/git_commands/working_tree.go) ensures that viewing the status of a repository with thousands of pending changes remains fluid and responsive.