SonarQube Branch Analysis: Track Code Quality Across Feature Branches
SonarQube branch analysis creates a separate quality view for each branch. You can see the quality gate status of feature branches independently from main, compare coverage and issues per branch, and set branch-specific quality gates.
Key Takeaways
Branch analysis requires Developer Edition or higher. Community Edition only supports the main branch. SonarCloud includes branch analysis at all tiers.
Pass sonar.branch.name to identify the branch. Most CI plugins detect this automatically from environment variables.
Short-lived vs long-lived branches behave differently. Feature branches are short-lived and compared against the base branch. Release/maintenance branches are long-lived and get full independent analysis.
Branch analysis doesn't create new projects. All branches of a project live under the same project key in SonarQube.
New Code Period is per-branch. Each branch tracks its own "new code" window for coverage and issue counting.
Branch Analysis Concepts
Short-lived vs Long-lived Branches
Short-lived branches (feature branches, PR branches):
- Compared against the parent branch
- "New code" = code introduced since branching from parent
- Intended to be merged and then deleted
- Lifecycle: appears when analyzed, cleaned up after inactivity
Long-lived branches (main, develop, release/, hotfix/):
- Analyzed independently with their own full history
- "New code" period set per-branch (date, version, or previous analysis)
- Shown permanently in the project UI
Configuring Branch Types
By default, SonarQube auto-classifies based on branch name patterns. Customize in Administration → Configuration → Analysis Scope → Branch Types:
Long-lived branch patterns:
- main
- master
- develop
- release/*
- hotfix/*Everything else is treated as short-lived.
CI Configuration
Passing Branch Name
# Maven
mvn sonar:sonar \
-Dsonar.branch.name=feature/my-feature \
-Dsonar.branch.target=main # optional: what to compare against
# Gradle
./gradlew sonarqube \
-Dsonar.branch.name=$(git branch --show-current)
# sonar-scanner
sonar-scanner \
-Dsonar.branch.name=$CI_COMMIT_BRANCHGitHub Actions (Auto-Detection)
- name: SonarQube Analysis
uses: SonarSource/sonarqube-scan-action@master
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
# sonar.branch.name is auto-detected from GITHUB_REFThe official SonarQube GitHub Action auto-detects the branch from GITHUB_REF. Manual override:
with:
args: -Dsonar.branch.name=${{ github.ref_name }}GitLab CI
sonarqube:
script:
- sonar-scanner
-Dsonar.branch.name=$CI_COMMIT_BRANCHGitLab CI exposes $CI_COMMIT_BRANCH automatically.
Viewing Branch Results
In the SonarQube project UI:
- The branch selector (top-left of the project) shows all analyzed branches
- Select any branch to see its quality gate, issues, coverage, and duplications
- The "New Code" tab shows issues introduced on this branch vs the baseline
- Quality gate status is per-branch
Branch badges in README:
[](https://sonarqube.example.com/dashboard?id=myproject&branch=main)Branch-Specific Quality Gates
Different quality gates for different branches:
# Assign a stricter quality gate to main
curl -X POST "http://sonarqube/api/qualityprofiles/add_project" \
-u admin:admin \
-d "language=java&qualityProfile=Strict-Production-Gate&project=myproject"Note: Quality gates apply to the project, not per-branch, in current SonarQube versions. Use different projects for fundamentally different branch policies.
Managing Branch Cleanup
Old branches accumulate. Configure automatic cleanup:
Administration → Configuration → Analysis Scope → Branches and Pull Requests:
Short-lived branches TTL: 30 days (inactive branches deleted after 30 days)
Long-lived branches TTL: 0 days (never auto-deleted)Or delete branches via API:
curl -X DELETE "http://sonarqube/api/project_branches/delete" \
-u admin:admin \
-d "project=myproject&branch=feature%2Fold-feature"Comparing Branch Coverage
SonarQube doesn't have a built-in branch comparison UI, but you can use the API:
# Coverage on main
curl "http://sonarqube/api/measures/component?component=myproject&branch=main&metricKeys=coverage"
# Coverage on feature branch
curl "http://sonarqube/api/measures/component?component=myproject&branch=feature%2Fmy-feature&metricKeys=coverage"Response:
{
"component": {
"measures": [
{"metric": "coverage", "value": "78.5"}
]
}
}Script to fail CI if branch coverage drops more than 5% below main:
MAIN_COVERAGE=$(curl -s "http://sonarqube/api/measures/component?component=myproject&branch=main&metricKeys=coverage" \
| jq -r '.component.measures[0].value')
BRANCH_COVERAGE=$(curl -s "http://sonarqube/api/measures/component?component=myproject&branch=feature/my-feature&metricKeys=coverage" \
| jq -r '.component.measures[0].value')
echo "Main: $MAIN_COVERAGE%, Branch: $BRANCH_COVERAGE%"
if (( $(echo "$BRANCH_COVERAGE < $MAIN_COVERAGE - 5" | bc -l) )); then
echo "ERROR: Coverage dropped more than 5% compared to main"
exit 1
fiNew Code Period Per Branch
Each branch can have its own new code period. Useful for release branches that need a different baseline:
Project Settings → New Code → Per branch settings
main: Previous version (last SonarQube analysis)
release/1.x: Specific date (start of the release branch)
feature/*: (auto) compared to parent branchBranch analysis gives each team member visibility into their branch's quality without polluting the main branch dashboard.