Contents

Best Practices for Commits, Branching and Versioning

Presenting conventional commits, branching models, semantic versioning, and source-based versioning in YAML format.


Conventional Commits

Specification

website:
  - https://www.conventionalcommits.org
  - https://github.com/commitizen/conventional-commit-types/blob/master/index.json

conventional_commits:
  feat:     new feature
  fix:      bug fix
  docs:     documentation only changes
  style:    changes that do not affect the meaning of the code, e.g., white-space, formatting, missing semi-colons
  refactor: code change that neither fixes a bug nor adds a feature
  perf:     code change that improves performance
  test:     adding missing tests or correcting existing tests
  build:    changes that affect the build system or external dependencies, e.g., gulp, broccoli, npm
  ci:       changes to our CI configuration files and scripts, e.g., Travis, Circle, BrowserStack, SauceLabs
  chore:    other changes that do not modify src or test files
  revert:   reverts a previous commit

structure: <type>(scope): <subject>

verb_aliases:
  fix:
    - resolve
    - handle
    - correct
    - prevent
    - update

Examples

  • With description and breaking change footer

    feat: allow provided config object to extend other configs
    
    BREAKING CHANGE: `extends` key in config file is now used for extending other config files
  • With ! to draw attention to breaking change

    feat!: send an email to the customer when a product is shipped
  • With scope and ! to draw attention to breaking change

    feat(api)!: send an email to the customer when a product is shipped
  • With both ! and BREAKING CHANGE footer

    chore!: drop support for Node 6
    
    BREAKING CHANGE: use JavaScript features not available in Node 6.
  • With no body

    docs: correct spelling of CHANGELOG
  • With scope

    feat(lang): add Polish language
  • With multi-paragraph body and multiple footers

    fix: prevent racing of requests
    
    Introduce a request id and a reference to latest request. Dismiss
    incoming responses other than from latest request.
    
    Remove timeouts which were used to mitigate the racing issue but are
    obsolete now.
    
    Reviewed-by: Z
    Refs: #123
  • With a footer that references the commit SHA that is being reverted

    revert: feat(lang): add Polish language
    
    This reverts commit 667ecc1654a317a13331b17617d973392f415f02.

Branching Models

Gitflow

Gitflow:
  website:
    - https://nvie.com/posts/a-successful-git-branching-model/
    - https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow
  description: Traditional workflow, suitable for projects with clear release cycles and multi-person collaboration
  branches:
    feature/*: feature development branches, e.g., feature/login, feature/JIRA-985-login
    bugfix/*:  fix known non-urgent defects, e.g., bugfix/sso-auth, bugfix/JIRA-211-sso-auth
    develop:   development integration branch, merge multiple features and bugfixes
    release/*: release candidate branches, e.g., release/1.6.2
    hotfix/*:  production hotfixes, e.g., hotfix/1.6.3
    main:      production stable branch
  workflow:
    # Development flow: feature/bugfix branches from develop, merge back to develop, then through release to main, and tag on main
    develop → feature/bugfix → develop → release → main → tag(vX.Y.Z)[recommended]
    # Emergency fix: hotfix branches from main, merge back to develop and main, and tag on main
    main → hotfix
              ↘ develop
              ↘ main → tag(vX.Y.Z)[recommended]
  advantages:
    - Clear branch roles, suitable for version management
    - Easy to manage release and fix processes
  disadvantages:
    - Many branches, complex CI/CD processes
    - Not suitable for high-frequency continuous delivery
  extensions:
    branches:
      user/<name>/*: personal branches, e.g., user/john/login, user/alice/sso-auth
      env/*:         environment branches, e.g., env/uat, env/prod
    notes:
      - user/<name>/* only for personal experiments or independent development, no guarantee of merge, avoid polluting formal feature branches
      - env/* only for tracking current deployment versions in each environment, no direct code development, updates through merge commits

GitHub Flow

GitHubFlow:
  website: https://docs.github.com/en/get-started/using-github/github-flow
  description: Simplified workflow, suitable for continuous delivery and small teams
  branches:
    main:      the only long-term branch, always in deployable state, whether to deploy depends on team strategy
    feature/*: feature branches derived from main
  workflow:
    # Feature development: feature branches from main, merge back to main through Pull/Merge Request
    # Release version: tag on main to mark deployable versions
    main → feature → main → tag(vX.Y.Z)[recommended]
  advantages:
    - Simple process, suitable for rapid iteration
    - Combined with Pull/Merge Request review
  disadvantages:
    - Lacks stable development integration branch
    - Version control depends on tags

Trunk Based Development

TrunkBasedDevelopment:
  website: https://trunkbaseddevelopment.com
  description: Trunk-based development, minimalist workflow, suitable for continuous integration/daily builds
  branches:
    main:      the only trunk branch
    feature/*: very short-term feature branches, quickly merge back to main
  workflow:
    # Feature development: feature branches from main, short-cycle development then quickly merge back to main
    # Continuous release: each main merge can trigger build and release
    main → feature → main → tag(vX.Y.Z)[optional]
  notes:
    - Due to frequent merges, usually use `datetime + commit_hash` as version number, e.g., 2025.07.30.19.06.3f9a7c1d
    - Semantic versioning (vX.Y.Z) only for important milestone tags
  advantages:
    - Supports high-frequency releases, automation CI/CD friendly
    - Avoids code drift from long-term branches
  disadvantages:
    - Requires strong testing and automation support
    - High requirements for team collaboration

GitLab Flow

GitLabFlow:
  website: https://about.gitlab.com/blog/gitlab-flow-duo/
  description: GitLab's officially recommended workflow, combines trunk development with environment branches, suitable for multi-environment CI/CD deployment
  branches:
    feature/*:  feature development branches, derived from main, merge back to main through Merge Request
    main:       main branch, for integration and testing, always in mergeable state
    staging:    pre-release environment branch, for pre-launch validation
    production: production environment branch, tracks current online deployment version
  workflow:
    # Feature development: feature branches from main, merge back to main after completion
    # Deployment process: main merge to staging triggers pre-release, main merge to production triggers production deployment
    # Optional release: tag on production to mark production version
    main → feature → main
                       ↘ staging
                       ↘ production → tag(vX.Y.Z)[recommended]
  notes:
    - staging/production branches for tracking current deployment versions in each environment
    - Environment branch updates through merge commits, no direct code development on these branches
    - Recommend tagging when main merges to production to mark official release version
  advantages:
    - Supports multi-environment deployment, CI/CD friendly
    - Manages releases through environment branches, intuitive tracking of current deployment status
    - Keeps main branch clean, features controlled through Merge Request
  disadvantages:
    - Requires strict merge policies and CI/CD conventions
    - May be overly complex for small projects

Version Management

Label Naming

version_label:
  snapshot: development snapshot
  alpha:    internal testing
  beta:     public testing
  rc:       release candidate
  release:  official release
  hotfix:   emergency fix

Semantic Versioning

semantic_versioning:
  website: https://semver.org
  format: MAJOR.MINOR.PATCH
  initial_development_version: 0.1.0
  description:
    MAJOR:
      description: major version number
      scenario: incompatible changes
      example: 0.12.8 → 1.0.0
      rule: major version +1, minor and patch versions reset to zero
    MINOR:
      description: minor version number
      scenario: backward-compatible feature additions
      example: 1.5.1 → 1.6.0
      rule: minor version +1, patch version resets to zero
    PATCH:
      description: patch version number
      scenario: backward-compatible bug fixes
      example: 1.6.1 → 1.6.2
      rule: patch version +1

  pre_release_versions:
    format: MAJOR.MINOR.PATCH-<label>[.<identifier>]
    labels:
      alpha: internal testing version
      beta:  public testing version
      rc:    release candidate
    examples:
      - 1.12.6-alpha
      - 1.12.6-beta
      - 1.12.6-rc.1
      - 1.12.6-rc.2

  metadata:
    format: MAJOR.MINOR.PATCH[-<label>]+<metadata>
    examples:
      - 1.12.6-alpha+3f9a7c1d
      - 1.12.7+20250730

  version_evolution_example: |
    0.1.0 → 0.1.1 → ...
     ...
    0.12.8
    1.0.0
     ...
    1.5.1
    1.6.0 → 1.6.1 → 1.6.2
     ...
    1.12.6-alpha(1.12.6-alpha+3f9a7c1d) → 1.12.6-beta → 1.12.6-rc.1 → 1.12.6-rc.2
    1.12.7(1.12.7+20250730)

Source-based Versioning

source_based_versioning:
  format: <branch>-YYYYMMDDHHmm-<commit_hash>
  description:
    branch: branch name
    YYYYMMDDHHmm: build date and time
    commit_hash: short hash value (first 8 characters)
  examples:
    - feature-login-202507281802-c4d8b21e
    - develop-202507291752-b17e5a9f
    - main-202507301906-3f9a7c1d
  advantages:
    - Directly shows build source branch
    - Each version is unique and traceable to specific commit
    - Suitable for automated continuous integration
  extended_rules:
    with_release_labels:
      format: <branch>-YYYYMMDDHHmm-<commit_hash>-<label>
      labels:
        alpha: internal testing version
        beta:  public testing version
        rc:    release candidate
      examples:
        - main-202507301906-3f9a7c1d-alpha

GitLab CI Version Generation

# .gitlab-ci.magic-version.yml

variables:
  TAG_PREFIX: "v"                     # Define tag prefix, used to remove 'v' from tags like v1.2.3

# Version based on tag
version_tag:
  stage: magic_version
  rules:
    - if: $CI_COMMIT_TAG =~ /^.+/     # Only run when tag exists, /^.+/ means return true if there's any non-empty character
  script:
    # Get current tag, remove TAG_PREFIX, e.g., v1.2.3 -> 1.2.3
    - export MAGIC_VERSION=${CI_COMMIT_TAG#*"$TAG_PREFIX"}
    # Write version number to build.env for subsequent Jobs
    - echo "MAGIC_VERSION=$MAGIC_VERSION" >> build.env
  artifacts:
    reports:
      dotenv: build.env               # Export build.env as dotenv for subsequent Jobs to use $MAGIC_VERSION variable

# Version based on branch
version_branch:
  stage: magic_version
  rules:
    - if: $CI_COMMIT_BRANCH           # Run when building based on branch
  script:
    # Generate datetime in YYYYMMDDHHmm format, e.g., 202507301906
    - VERSION_DATETIME=$(date +'%Y%m%d%H%M')
    # Concatenate version number: <branch>-YYYYMMDDHHmm-<commit_hash>
    # Example: main-202507301906-3f9a7c1d
    # Variable descriptions:
    #   $CI_COMMIT_REF_SLUG  -> branch name slug, e.g., feature-login / main
    #   $CI_COMMIT_SHORT_SHA -> current commit short hash (first 8 characters)
    - export MAGIC_VERSION=${CI_COMMIT_REF_SLUG}-${VERSION_DATETIME}-${CI_COMMIT_SHORT_SHA}
    # Write version number to build.env for subsequent Jobs
    - echo "MAGIC_VERSION=$MAGIC_VERSION" >> build.env
  artifacts:
    reports:
      dotenv: build.env               # Export build.env as dotenv for subsequent Jobs to use $MAGIC_VERSION variable