September 13, 2026

Decoding Git History: A Comprehensive Guide to Lazygit’s Four Merge Strategies and Their Long-Term Impact on Codebases

decoding-git-history-a-comprehensive-guide-to-lazygits-four-merge-strategies-and-their-long-term-impact-on-codebases

decoding-git-history-a-comprehensive-guide-to-lazygits-four-merge-strategies-and-their-long-term-impact-on-codebases

In the modern developer’s toolkit, few utilities command as much daily loyalty as lazygit. Serving as a streamlined, terminal-based user interface for Git, it abstracts away complex command-line syntax into intuitive keystrokes. For millions of software engineers, pressing M within the branches panel has long been an automatic reflex—a simple, transactional command to merge code from a source branch into a target branch.

However, beneath this singular keystroke lies a crucial fork in the road of software development: the M menu opens a gateway to four distinct merge options (as of version 0.62.2). While each option achieves the fundamental goal of integrating code, they leave behind vastly different commit graphs. These structural differences directly influence project history, auditability, debugging workflows, and long-term codebase maintainability.

This deep dive explores the anatomy of lazygit’s merge menu, examining the mechanics of each strategy, their downstream implications, and how developers can utilize native Git commands to audit the historical shape of any repository.


Main Facts: The Anatomy of Lazygit’s Merge Menu

When a developer navigates to lazygit’s branches panel, places their cursor over a source branch, and presses M, a prompt titled Merge appears. This interface presents four unique execution paths:

  1. Regular Merge (Fast-Forward) — Triggered via the m option.
  2. Regular Merge (With Merge Commit) — Triggered via the n option.
  3. Squash Merge (Interactive/Staged) — Triggered via the s option.
  4. Squash Merge (Direct Commit) — Triggered via the S option.

Each of these choices alters the underlying directed acyclic graph (DAG) of the Git repository in a unique way. Understanding these variations transitions a developer from passive code integration to deliberate history curation.

Setting Up the Experimental Environment

To truly comprehend how these options manipulate a repository, developers can construct a throwaway environment locally. The experiment requires only standard Git and lazygit (installable via package managers like Homebrew or APT):

mkdir /tmp/merge-modes && cd /tmp/merge-modes
git init && git commit --allow-empty -m "init"

# Create a feature branch with three distinct commits
git checkout -b feature
echo one > a.txt && git add . && git commit -m "feature: step one"
echo two > b.txt && git add . && git commit -m "feature: step two"
echo three > c.txt && git add . && git commit -m "feature: step three"

git checkout main

By opening lazygit within this repository, executing each merge strategy individually, and inspecting the resulting graphs using git log --oneline --graph, engineers can demystify what happens under the hood.


Chronology: Exploring the Four Merge Options Step-by-Step

1. Regular Merge (Fast-Forward) — The m Option

The default selection in the M menu behaves dynamically depending on the real-time state of the target branch relative to the source branch.

  • The Mechanism: If the target branch has not received any new commits since the feature branch was branched off, Git simply advances the target branch pointer to point directly to the tip of the feature branch.
  • The Result: In our throwaway repository, executing m produces a strictly linear graph:
* feature: step three
* feature: step two
* feature: step one
* init
  • The Trade-off: The source branch itself leaves zero trace in the history. While this yields a clean, uninterrupted straight line, it obscures contextual boundaries. Nothing in the history indicates that these three commits arrived together as a single, reviewed unit of work. This option is optimal for short-lived, trivial feature branches where the target branch remained static. The moment the target branch diverges, however, this option automatically falls back to generating a merge commit graph.

2. Regular Merge (With Merge Commit) — The n Option

Unlike the fast-forward approach, the n option forces the creation of a dedicated merge commit with two distinct parents: the previous tip of the target branch (main) and the tip of the source branch (feature).

  • The Mechanism: Regardless of whether the target branch has moved or remained idle, choosing n explicitly instructs Git to weave the histories together permanently.
  • The Result: The graph captures the exact structural bifurcation and reunification:
*   merge branch 'feature' into main
|
| * (feature) feature: step three
| * feature: step two
| * feature: step one
|/
* init
  • The Trade-off: All granular commits from the feature branch survive intact, accompanied by a permanent marker declaring that a branch landed at this specific juncture. This approach enables advanced history traversal techniques, such as git log --first-parent, which lets maintainers read a repository as a chronological series of landed feature integrations. The primary cost is visual complexity: the graph accumulates merge knots, which can occasionally complicate automated revert operations or linear refactoring flows.

3. Squash Merges — The s and S Options

Squashing fundamentally alters the narrative of a codebase by collapsing multiple granular steps into a single, consolidated unit of work.

  • The Mechanism: Both options take all intermediate commits on the source branch and fold their net file changes into a single new commit on the target branch. The individual stepping-stone commits are discarded from the target branch’s lineage.

  • The Result (S Option – Direct Commit): The repository history displays a singular, automated entry:

  • squash merge feature into main

    Lazygit's M Key Hides Four Merge Options — Here's the Git History Each One Leaves Behind
  • init

  • The Result (s Option – Staged Changes): The git log remains completely untouched until the developer manually crafts a commit message. The changes are placed directly into the working tree, staged but uncommitted.

  • The Trade-off: Developers gain an impeccably clean, one-commit-per-change history, but entirely sacrifice the detailed record of the development journey. In professional practice, the interactive s option is heavily favored because it allows engineers to author meaningful commit messages that survive as the sole historical record of the feature. The capital S variant offers speed at the expense of descriptive clarity.


Supporting Data: Reading and Auditing Repository Merge Shapes

As engineering teams scale, repositories often devolve into a chaotic patchwork of mixed merge strategies. Fortunately, Git provides native diagnostic tools to audit the historical shape of any codebase. By analyzing a repository with a combination of flags, maintainers can instantly identify which strategies dominate their workflow.

Running the following commands reveals the underlying architectural footprint of a project:

git log --oneline --merges -20
git log --oneline --first-parent -20

Comparative Analysis of Repository Audit Patterns

--merges Output Pattern --first-parent Output Pattern Inferred Merge Style / Strategy
Many merge commits present Each merge is represented as a single distinct entry Merge Commits (Explicit non-fast-forward, or n / dynamic m options)
Few or zero merge commits One clean commit per landed branch, featuring descriptive "squash"-style messages Squash Strategy (s or S options heavily utilized)
Zero merge commits, highly granular commits Perfectly linear history Fast-Forward (m option during quiet target periods, or direct linear pushes)

When developers audit mature repositories, they frequently discover a hybrid anomaly—a random distribution of squashes, merge commits, and fast-forwards. This variance typically occurs organically when teams fail to standardize their integration practices, leaving behind an undocumented and unpredictable historical record.


Official Responses and Best Practices: Consistency over Convenience

Industry veterans and maintainers consistently advocate for a singular foundational rule regarding Git workflows: Pick one merge shape, standardize it across the team, and commit to it.

Six months down the line, when an engineer must perform a complex git bisect, track down a regression, or execute a targeted revert, the clarity of the codebase relies entirely on historical consistency. A repository with a predictable, uniform graph structure dramatically reduces cognitive overhead during debugging and incident response.

Recovery and Contingency Protocols

Selecting the wrong merge strategy or encountering a mistake is an inevitable reality of software engineering. Lazygit and Git provide robust safety nets for error recovery:

  • In the Shell: If a merge yields an undesired graph shape, developers can instantly reset their position using:
    git reset --hard HEAD@1
  • In Lazygit: From the commits panel, an engineer can navigate to an erroneous commit and press z to execute a soft reset.
  • Handling Merge Conflicts: If a merge triggers a conflict, there is no immediate "abort and walk away" shortcut—the workflow requires resolving the conflict forward. Lazygit opens an intuitive conflict-resolution interface where developers resolve individual hunks, press Esc to return to the files panel, and seamlessly resume the merge flow.

Implications: Designing for Future Maintainers

The hidden depth of lazygit’s M panel serves as a microcosm of software craftsmanship. Tools that abstract complexity are invaluable for productivity, but unexamined automation can lead to architectural debt in our commit logs.

Every time a developer presses M, they are writing history for an audience of one: their future self and their engineering teammates. By slowing down, understanding the four distinct trajectories of the merge menu, and deliberately selecting the strategy that aligns with long-term project goals, engineering teams can transform their repository history from an accidental by-product into a precise, readable narrative of software evolution.


This article expands upon technical explorations originally featured in issue #7 of Shellcraft, a publication dedicated to mastering terminal-based development craft.