DevToolBoxGRATIS
Blog

Guía Completa de Conventional Commits 2026: Especificación, Herramientas y Releases Automatizados

24 min de lecturapor DevToolBox Team

TL;DR

Conventional Commits is a specification for writing standardized commit messages that follows the format type(scope): description. It enables automated versioning with Semantic Versioning (SemVer), automatic CHANGELOG generation, and streamlined CI/CD pipelines. With tools like Commitlint, Commitizen, Semantic Release, and Release Please, teams can fully automate their release workflow. In 2026, Conventional Commits has become the de facto standard adopted by Angular, Vue, Babel, Electron, and thousands of open-source projects and enterprises.

Key Takeaways

  • Conventional Commits follows the format type(scope): description with optional body and footer sections
  • Commit types like feat and fix directly map to SemVer MINOR and PATCH version bumps
  • Breaking changes (BREAKING CHANGE footer or ! after type) trigger a MAJOR version bump
  • Commitlint + Husky enforces the convention locally; GitHub Actions enforces it in CI
  • Semantic Release and Release Please fully automate versioning, changelogs, and npm publishing
  • Monorepo tools like Changesets, Lerna, and Turborepo integrate natively with Conventional Commits

Conventional Commits is a lightweight specification layered on top of commit messages that provides a structured format for communicating the nature of changes in a codebase. By following a simple convention, teams unlock automated versioning, changelog generation, and release management. This guide covers everything from the specification itself to full CI/CD automation with real configuration files you can copy into your projects today.

What Are Conventional Commits and Why They Matter

Conventional Commits is a specification that defines a standard format for commit messages. The format is simple: each commit message consists of a type, an optional scope, and a description. The commit body and footer provide additional context. This structure makes commits machine-readable, enabling tools to automate versioning and changelog generation.

Without a commit convention, changelogs must be written manually, version bumps are guesswork, and the commit history becomes an unreadable stream of messages like "fix stuff", "wip", and "final update". Conventional Commits solves all of this by giving every commit a clear semantic meaning.

The specification was inspired by the Angular commit guidelines and has since become its own independent standard. In 2026, it is adopted by thousands of projects including Vue.js, Babel, Electron, Yargs, Lerna, and many enterprise codebases.

The Specification Format

Every conventional commit message follows this structure. The type and description are required. The scope, body, and footer are optional. The header (first line) should not exceed 72 characters.

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]

# Examples:
feat: add user registration endpoint
fix(auth): resolve token expiration bug
docs(readme): update installation instructions
feat(api)!: change response format for /users

BREAKING CHANGE: The /users endpoint now returns
an object instead of an array.

Commit Types Reference

The specification defines two required types (feat and fix) and recommends several others. Here is the complete list of commonly used commit types and their purpose:

TypeDescriptionSemVer
featA new feature for the userMINOR
fixA bug fix for the userPATCH
docsDocumentation only changesNone
styleCode style (formatting, semicolons)None
refactorCode change (no bug fix, no feature)None
perfPerformance improvementPATCH
testAdding or correcting testsNone
buildBuild system or dependenciesNone
ciCI configuration and scriptsNone
choreRoutine tasks (no src/test change)None
revertReverts a previous commitVaries

Scope Conventions and Best Practices

The scope provides additional context about what part of the codebase is affected. It is enclosed in parentheses after the type. Scopes should be consistent across the project and documented in your contributing guide.

Common scope strategies include using module names (auth, api, ui), layer names (controller, service, model), or feature areas (checkout, onboarding, dashboard). In monorepos, the package name is typically used as the scope.

# Module-based scopes
feat(auth): add two-factor authentication
fix(payments): correct tax calculation
refactor(database): optimize query builder

# Layer-based scopes
fix(controller): validate request params
refactor(service): extract email service
test(repository): add integration tests

# Monorepo package scopes
feat(@myorg/ui): add DatePicker component
fix(@myorg/api): handle timeout errors
build(@myorg/shared): update tsconfig

Breaking Changes

Breaking changes are the most important thing to communicate in a commit message because they trigger a MAJOR version bump. There are two ways to indicate a breaking change:

The first method uses the BREAKING CHANGE footer (must be uppercase) in the commit footer. The second method appends an exclamation mark (!) after the type or scope. Both methods can be combined for maximum clarity.

# Method 1: BREAKING CHANGE footer
feat(api): change user response format

The /api/users endpoint now returns paginated results
instead of a flat array.

BREAKING CHANGE: The response shape changed from
User[] to { data: User[], meta: { page, total } }.
All API consumers must update their parsing logic.

# Method 2: ! after type/scope
feat(api)!: change user response format

# Method 3: Both combined (recommended)
feat(api)!: change user response format

BREAKING CHANGE: Response changed from User[] to
{ data: User[], meta: { page, total } }.

Real-World Commit Message Examples

Here are examples of good and bad commit messages. Good messages follow the convention and provide clear context. Bad messages are vague, inconsistent, or missing the type prefix.

Good vs Bad Commit Messages

# GOOD — follows the convention
feat(auth): add OAuth 2.0 login with Google
fix(cart): prevent negative quantity on update
docs(api): add OpenAPI spec for /orders endpoint
perf(search): add database index for full-text queries
build(deps): upgrade React from 18 to 19
revert: feat(auth): add OAuth 2.0 login with Google

# BAD — avoid these patterns
added new feature          # missing type prefix
fix: fix stuff             # too vague
fix: add dark mode toggle  # wrong type (should be feat)
feat: add login and fix header  # multiple changes
FEAT: add search feature   # uppercase type

Semantic Versioning Integration

Conventional Commits maps directly to Semantic Versioning (SemVer). This mapping is what makes automated version bumps possible. Understanding this relationship is crucial for setting up automation.

When a release is generated, the tool scans all commits since the last release. If any commit has a BREAKING CHANGE, the major version is bumped. Otherwise, if any commit has type feat, the minor version is bumped. Otherwise, fix and perf commits cause a patch bump. Types like docs, style, refactor, test, build, ci, and chore do not trigger any release by default.

CommitVersion BumpExample
fix(...): ...PATCH (1.0.0 -> 1.0.1)Bug fixes, patches
feat(...): ...MINOR (1.0.0 -> 1.1.0)New features
feat(...)!: ... or BREAKING CHANGEMAJOR (1.0.0 -> 2.0.0)Breaking API changes
docs, style, refactor, test, ciNo releaseInternal changes
perf(...): ...PATCH (1.0.0 -> 1.0.1)Performance improvements

Commitlint Setup

Commitlint is a linter for commit messages. It checks that every commit message follows the Conventional Commits specification. Combined with Husky, it runs automatically on every commit, preventing non-conforming messages from entering the repository.

Install commitlint and the conventional config, then create a configuration file and set up a Husky commit-msg hook:

Installation

npm install --save-dev @commitlint/cli @commitlint/config-conventional

# Or with pnpm
pnpm add -D @commitlint/cli @commitlint/config-conventional

commitlint.config.js

// commitlint.config.js
export default {
  extends: ['@commitlint/config-conventional'],
  rules: {
    // Type must be one of these
    'type-enum': [
      2,
      'always',
      [
        'feat', 'fix', 'docs', 'style', 'refactor',
        'perf', 'test', 'build', 'ci', 'chore', 'revert',
      ],
    ],
    // Scope must be lowercase
    'scope-case': [2, 'always', 'lower-case'],
    // Header max length 100 characters
    'header-max-length': [2, 'always', 100],
    // Description must not be empty
    'subject-empty': [2, 'never'],
    // Type must not be empty
    'type-empty': [2, 'never'],
  },
};

Husky commit-msg Hook

# Install Husky
npm install --save-dev husky
npx husky init

# Create the commit-msg hook
echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg
chmod +x .husky/commit-msg

Commitizen (cz-cli) for Interactive Commits

Commitizen provides an interactive prompt for writing conventional commit messages. Developers answer questions about type, scope, and description instead of typing the format manually. This eliminates formatting errors and makes onboarding new team members easier.

# Install Commitizen globally
npm install -g commitizen

# Initialize with conventional changelog adapter
commitizen init cz-conventional-changelog --save-dev --save-exact

# Or configure manually in package.json:
# "config": {
#   "commitizen": {
#     "path": "cz-conventional-changelog"
#   }
# }

# Now use "cz" or "git cz" instead of "git commit"
# The interactive prompt guides you through:
# type -> scope -> description -> body -> breaking changes -> issues

.czrc Configuration

{
  "path": "cz-conventional-changelog",
  "maxHeaderWidth": 100,
  "maxLineWidth": 100,
  "defaultType": "feat",
  "defaultScope": ""
}

Standard Version and Release Please

Standard Version is a utility for versioning that follows SemVer and the Conventional Commits specification. It reads your commit history, determines the version bump, updates package.json, generates or updates CHANGELOG.md, and creates a git tag. Note: Standard Version is now deprecated in favor of Release Please.

Release Please is the Google-maintained successor to Standard Version. It creates release pull requests that include version bumps and changelog updates. When the PR is merged, the release is published.

Release Please GitHub Action

# .github/workflows/release-please.yml
name: Release Please

on:
  push:
    branches: [main]

permissions:
  contents: write
  pull-requests: write

jobs:
  release-please:
    runs-on: ubuntu-latest
    steps:
      - uses: googleapis/release-please-action@v4
        with:
          release-type: node
          token: \${{ secrets.GITHUB_TOKEN }}

Semantic Release: Fully Automated Publishing

Semantic Release takes automation to the maximum. It analyzes commits, determines the version bump, generates release notes, publishes to npm (or other registries), creates GitHub releases, and updates changelogs. It is the gold standard for fully automated publishing in 2026.

Here is a complete Semantic Release configuration for a typical Node.js project:

# Install Semantic Release and plugins
npm install --save-dev semantic-release @semantic-release/commit-analyzer \
  @semantic-release/release-notes-generator @semantic-release/changelog \
  @semantic-release/npm @semantic-release/github @semantic-release/git

release.config.js

// release.config.js
export default {
  branches: ['main'],
  plugins: [
    // Analyze commits to determine version bump
    '@semantic-release/commit-analyzer',
    // Generate release notes from commits
    '@semantic-release/release-notes-generator',
    // Update CHANGELOG.md
    ['@semantic-release/changelog', {
      changelogFile: 'CHANGELOG.md',
    }],
    // Publish to npm
    '@semantic-release/npm',
    // Create GitHub release with notes
    '@semantic-release/github',
    // Commit CHANGELOG.md and package.json back
    ['@semantic-release/git', {
      assets: ['CHANGELOG.md', 'package.json'],
      message: 'chore(release): \${nextRelease.version} [skip ci]',
    }],
  ],
};

Semantic Release GitHub Action

# .github/workflows/semantic-release.yml
name: Semantic Release
on:
  push:
    branches: [main]
permissions:
  contents: write
  issues: write
  pull-requests: write
jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }
      - uses: actions/setup-node@v4
        with: { node-version: 22, cache: npm }
      - run: npm ci
      - run: npx semantic-release
        env:
          GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
          NPM_TOKEN: \${{ secrets.NPM_TOKEN }}

GitHub Actions for Commit Linting in CI

Even with local hooks, CI-level enforcement is essential because hooks can be bypassed. A GitHub Actions workflow that lints commit messages ensures no non-conforming commits reach the main branch.

# .github/workflows/commitlint.yml
name: Commitlint
on:
  push: { branches: [main] }
  pull_request: { branches: [main] }
jobs:
  commitlint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - run: npm install @commitlint/cli @commitlint/config-conventional
      - run: npx commitlint --from \${{ github.event.pull_request.base.sha || 'HEAD~1' }} --to HEAD --verbose

Monorepo Conventions

Monorepos present unique challenges for Conventional Commits because a single repository contains multiple packages. The scope field becomes essential for identifying which package a commit affects. Here are the major approaches:

Changesets is a popular tool for monorepo versioning. It uses a separate changeset file instead of relying solely on commit messages. Each PR adds a changeset file that describes the change and its impact. During release, Changesets reads all pending changesets, bumps versions, and generates changelogs for each affected package.

Monorepo Commit Examples

# Lerna / Nx monorepo with package scopes
feat(web): add dashboard page
fix(api): handle rate limiting correctly
build(shared): upgrade TypeScript to 5.7
test(mobile): add login screen snapshot tests

# Turborepo with app/package scopes
feat(apps/web): integrate Stripe payments
fix(packages/ui): correct Button hover state
ci(turbo): add remote caching to pipeline

# Changesets workflow
npx changeset           # create changeset file
git add .changeset/ && git commit -m "feat(api): add rate limiting"
npx changeset version   # bump versions when ready
npx changeset publish   # publish to npm

Angular vs Conventional Commits vs Gitmoji

Three commit conventions are widely used in 2026. Angular Commit Convention is the original inspiration for Conventional Commits and uses the same type(scope): description format. Conventional Commits is a standalone specification that is lighter and more flexible. Gitmoji uses emoji prefixes instead of text types. Here is how they compare:

AspectAngularConventional CommitsGitmoji
Formattype(scope): subjecttype(scope): description:emoji: description
ToolingCommitlint, ng-commitCommitlint, Commitizen, SRgitmoji-cli, gitmoji-changelog
SemVer mappingYes (strict)Yes (built-in)Partial
AdoptionAngular ecosystemUniversal (most popular)Growing niche
ReadabilityGoodGoodVisual (emoji-dependent)
FlexibilityStrictExtensibleFlexible

Auto-Generating CHANGELOG.md

Conventional Commits enables fully automatic changelog generation. Tools read the commit history, group changes by type, and produce a well-formatted markdown file. Here is what the tools generate:

# Auto-generated CHANGELOG.md example:

## [2.3.0](https://github.com/org/repo/compare/v2.2.0...v2.3.0) (2026-02-28)

### Features

* **auth:** add two-factor authentication ([#142](https://github.com/org/repo/issues/142))
* **search:** implement full-text search with Elasticsearch ([abc1234](https://github.com/org/repo/commit/abc1234))

### Bug Fixes

* **payments:** correct tax calculation for EU ([#156](https://github.com/org/repo/issues/156))
* **ui:** fix modal z-index on Safari ([def5678](https://github.com/org/repo/commit/def5678))

### Performance Improvements

* **database:** add composite index for user queries ([ghi9012](https://github.com/org/repo/commit/ghi9012))

PR Title Conventions

Many teams enforce Conventional Commits format on pull request titles as well. When using squash merging, the PR title becomes the merge commit message, making PR title linting just as important as commit message linting. GitHub Actions can enforce this:

# .github/workflows/pr-title.yml
name: PR Title Lint
on:
  pull_request:
    types: [opened, edited, synchronize]
jobs:
  lint-pr-title:
    runs-on: ubuntu-latest
    steps:
      - uses: amannn/action-semantic-pull-request@v5
        env:
          GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
        with:
          types: |
            feat
            fix
            docs
            style
            refactor
            perf
            test
            build
            ci
            chore
            revert
          requireScope: false

Team Adoption Guide

Adopting Conventional Commits across a team requires a gradual rollout. Forcing a new convention overnight leads to frustration and resistance. Here is a proven four-phase adoption strategy:

  • Phase 1 (Week 1-2): Introduce the specification in a team meeting. Share this guide. Add a CONTRIBUTING.md section explaining the convention. No enforcement yet — just awareness.
  • Phase 2 (Week 3-4): Add Commitizen so developers can use the interactive prompt. Set up Commitlint with a warning-only mode. Create a shared .gitmessage template. Developers can opt in gradually.
  • Phase 3 (Month 2): Enable Commitlint enforcement locally with Husky. Add CI linting as a required check. All new commits must follow the convention. Old history is not retroactively changed.
  • Phase 4 (Month 3+): Automate releases with Semantic Release or Release Please. Generate changelogs automatically. The convention now pays dividends through full automation.

IDE Integrations

VS Code and JetBrains IDEs offer extensions that make writing conventional commits easier. These integrations provide autocomplete for types, scope suggestions, and inline validation.

VS Code: Install the Conventional Commits extension (vivaxy.vscode-conventional-commits). It adds a source control panel widget that guides you through writing a commit message with type selection, optional scope, description, body, and breaking change fields. The extension also supports custom types and scopes via .vscode/settings.json.

JetBrains (IntelliJ, WebStorm, PyCharm): Install the Conventional Commit plugin from the marketplace. It provides a popup dialog when committing that ensures the message follows the convention. It supports custom scopes from your commitlint configuration.

// .vscode/settings.json — custom scopes for VS Code extension
{
  "conventionalCommits.scopes": [
    "auth",
    "api",
    "ui",
    "database",
    "payments",
    "search",
    "config"
  ],
  "conventionalCommits.showNewVersionNotes": false
}

Custom Commit Types and Extending the Spec

The Conventional Commits specification is intentionally minimal — it only mandates feat and fix. Teams are free to add custom types for their specific workflows. This is done by extending the commitlint configuration:

Some popular custom types include deps (dependency updates), release (release commits), wip (work in progress — usually rejected in CI), security (security-related changes), and i18n (internationalization changes).

// commitlint.config.js — extending with custom types
export default {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [
      2,
      'always',
      [
        // Standard types
        'feat', 'fix', 'docs', 'style', 'refactor',
        'perf', 'test', 'build', 'ci', 'chore', 'revert',
        // Custom types
        'deps',      // dependency updates
        'security',  // security patches
        'i18n',      // internationalization
        'a11y',      // accessibility
        'release',   // release commits
      ],
    ],
    // Enforce allowed scopes
    'scope-enum': [
      2,
      'always',
      ['auth', 'api', 'ui', 'db', 'payments', 'search', 'config', 'deps'],
    ],
  },
};

Commit Message Templates (.gitmessage)

A git commit message template helps developers remember the format without memorizing the specification. Create a .gitmessage file in your project root and configure git to use it:

.gitmessage Template

# .gitmessage
# <type>(<scope>): <description>
# [optional body]
# [optional footer(s)]
#
# Types: feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert
# Rules: header <= 72 chars, imperative mood, no capital, no period
# Breaking: add BREAKING CHANGE: footer or ! after type
# Example: feat(auth): add two-factor authentication

Configure Git to Use the Template

# Set the template for this repository
git config commit.template .gitmessage

# Or set it globally
git config --global commit.template ~/.gitmessage

Pre-Commit Hooks with Husky + lint-staged

Husky manages git hooks, and lint-staged runs linters on staged files. Together with Commitlint, they create a robust local enforcement pipeline. Here is the complete setup:

# Install dependencies
npm install --save-dev husky lint-staged @commitlint/cli @commitlint/config-conventional

npx husky init
echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg
echo "npx lint-staged" > .husky/pre-commit

package.json Configuration

{
  "scripts": { "prepare": "husky", "commit": "cz" },
  "lint-staged": {
    "*.{js,ts,tsx}": ["eslint --fix", "prettier --write"],
    "*.{css,scss,json,md}": ["prettier --write"]
  }
}

Common Mistakes and How to Fix Them

Even experienced developers make mistakes with Conventional Commits. Here are the most common errors and their solutions:

  • Using uppercase types — Type must be lowercase: feat, not Feat or FEAT. Fix: commitlint enforces this by default.
  • Missing colon after type — The format is type: description not type description. The colon and space after the type (or scope) are required.
  • Writing too-long headers — Keep the first line under 72 characters. Move details to the commit body. Commitlint has a header-max-length rule (default 100).
  • Using wrong types — Using fix when the change is actually a refactor, or feat when it is docs. Each type has specific semantics tied to SemVer. Misusing them causes incorrect version bumps.
  • Forgetting BREAKING CHANGE footer — Adding ! after the type is not enough for some tools. Always include the BREAKING CHANGE: footer with a description of what breaks and the migration path.
  • Inconsistent scopes — Using auth in one commit and authentication in another. Document your allowed scopes in commitlint config and enforce them.
  • Giant commits with multiple changes — One commit should represent one logical change. If you need "feat(auth): add login and fix(ui): fix button" in one commit, it should be two separate commits.

Best Practices for Commit Granularity

Each commit should represent a single, atomic, logical change. A good test: can you describe the commit in one sentence without using "and"? If not, it should probably be multiple commits.

For features, break the work into small commits: one for the data model, one for the API endpoint, one for the UI component, and one for the tests. This makes code review easier, bisecting simpler, and reverting safer.

Never mix formatting changes with logic changes. If you need to reformat a file and fix a bug, do them in separate commits: style(utils): format helper functions followed by fix(utils): correct null check in parseConfig. This way, the formatting commit can be safely ignored during code review.

# Good: atomic commits with clear purpose
feat(auth): add User model with password hashing
feat(auth): add POST /api/auth/register endpoint
feat(auth): add registration form component
test(auth): add unit tests for registration flow
docs(auth): add registration API documentation

# Bad: one giant commit
feat(auth): add complete user registration system
# (includes model, endpoint, UI, tests, docs all in one)

Frequently Asked Questions

What is the Conventional Commits specification?

Conventional Commits is a convention for writing standardized commit messages. Each message follows the format type(scope): description, where type indicates the nature of the change (feat, fix, docs, etc.), scope optionally identifies the affected area, and description summarizes the change. The convention enables automated versioning, changelog generation, and CI/CD automation.

How do Conventional Commits relate to Semantic Versioning?

Conventional Commits maps directly to SemVer. A feat commit triggers a MINOR version bump (1.0.0 to 1.1.0). A fix commit triggers a PATCH bump (1.0.0 to 1.0.1). A commit with BREAKING CHANGE triggers a MAJOR bump (1.0.0 to 2.0.0). Other types like docs, style, and refactor do not trigger any version change by default.

How do I set up Commitlint with Husky?

Install @commitlint/cli and @commitlint/config-conventional with npm. Create a commitlint.config.js file that extends the conventional config. Then install Husky, run npx husky init, and create a .husky/commit-msg hook that runs npx commitlint --edit. Every commit message will now be validated against the Conventional Commits specification.

What is the difference between Semantic Release and Release Please?

Semantic Release is a fully automated tool that determines versions, generates changelogs, publishes to npm, and creates GitHub releases on every CI run. Release Please (by Google) creates a release pull request that accumulates changes; the release only happens when you merge the PR. Release Please gives you a review step before publishing.

Can I use Conventional Commits in a monorepo?

Yes. Use the scope field to identify which package is affected (e.g., feat(api): add endpoint). Tools like Changesets, Lerna, and Release Please have built-in monorepo support. They can independently version each package based on its scoped commits and generate per-package changelogs.

What happens if I make a mistake in a commit message?

If you have not pushed yet, use git commit --amend to rewrite the last commit message. For older commits, use git rebase -i to edit specific commit messages. If the commit is already pushed and merged, it is best to leave it and ensure future commits follow the convention. Commitlint with Husky prevents these mistakes proactively.

Should I use Conventional Commits for PR titles too?

Yes, especially if your team uses squash merging. When PRs are squash-merged, the PR title becomes the merge commit message. Tools like action-semantic-pull-request can lint PR titles in CI. This ensures the final commit on the main branch follows the convention even if individual commits in the PR branch do not.

How do I handle revert commits with Conventional Commits?

Use the revert type followed by the original commit description: revert: feat(auth): add OAuth login. In the commit body, include the hash of the reverted commit: This reverts commit abc1234. Some tools like Semantic Release will detect the revert and adjust the version accordingly, potentially removing a feature from the next release.

𝕏 Twitterin LinkedIn
¿Fue útil?

Mantente actualizado

Recibe consejos de desarrollo y nuevas herramientas.

Sin spam. Cancela cuando quieras.

Prueba estas herramientas relacionadas

{ }JSON Formatter

Artículos relacionados

Guía Git Avanzado: Rebase Interactivo, Reflog, Bisect, Git Hooks, Submodulos y Monorepo

Domina técnicas avanzadas de Git. Internals de Git, rebase interactivo, recuperación con reflog, cherry-pick, bisect para depuración, estrategias de merge, hooks con Husky, submodulos vs subtrees, worktrees.

Guía de Flujo de Trabajo Git: Internals, Estrategias de Ramas, Rebase Interactivo, Hooks y CI/CD

Guía completa de flujo de trabajo Git: internals, estrategias de ramas, rebase interactivo, hooks, submódulos, reflog, LFS y CI/CD.

Guía Completa de Lazygit 2026: Interfaz Terminal para Git

Domina Lazygit: atajos, staging interactivo, rebase, resolución de conflictos, comandos personalizados e integración Neovim.