Chapter 17Lesson 02~155 minutes

Branches, Pull Requests, Merge Requests, and Edition-Aware Analysis: Guided Hands-On Workflow

Create a two-branch Git fixture, run Community Build-compatible baseline and simulation analyses, preserve branch/PR identity evidence, and diagnose shallow history safely.

BranchesPull requestsSCMNew CodeCI/CD

Learning objectives

  • Create a disposable two-branch Git repository with a known main SHA, feature SHA, and merge base.
  • Run a real Community Build analysis on main and a clearly labeled feature-revision simulation without pretending it is branch/PR analysis.
  • Build the exact Developer+ branch/PR parameter evidence packet without requiring a commercial license.
  • Reproduce a shallow-clone defect and repair it by restoring full-enough history.
  • Preserve task IDs and separate SonarQube processing evidence from provider-decoration evidence.
  • Use a project-analysis token and guarded cleanup.

1. Lab contract and version assumptions

Mandatory path: local/free. Use synthetic code, a local Git repository, and SonarQube Community Build 26.9.0.129388. The Community Build path does not execute sonar.branch.name or sonar.pullrequest.*. It models those identities explicitly and optionally executes them only on an authorized Developer+ test instance/CI workspace.
State Lab value
Server Community Build 26.9.0.129388 at http://localhost:9000
Scanner SonarScanner CLI 8.1.0.6389
Main project sq-ch17-main
Feature simulation project sq-ch17-feature-sim — deliberately a separate project, not a branch
Branches main and feature/demo
Simulated PR key 17, source feature/demo, target main

2. Build a two-branch repository and local origin

mkdir -p sq-ch17-lab && cd sq-ch17-lab
mkdir author && cd author
git init -b main
git config user.email "learner@example.invalid"
git config user.name "SonarQube Learner"

mkdir -p src
cat > src/app.py <<'PYCODE'
def normalize_name(name: str) -> str:
    return name.strip().lower()
PYCODE

cat > sonar-project.properties <<'EOF'
sonar.projectName=SQ Chapter 17 Lab
sonar.sources=src
sonar.sourceEncoding=UTF-8
EOF

cat > .gitignore <<'EOF'
.scannerwork/
evidence/
EOF

git add .
git commit -m "main baseline"
MAIN_SHA="$(git rev-parse HEAD)"

git switch -c feature/demo
cat >> src/app.py <<'PYCODE'

def risky_branch(value: str) -> str:
    # Intentionally simple change for branch identity; not an exploit fixture.
    return value + "-feature"
PYCODE
git add src/app.py
git commit -m "feature change"
FEATURE_SHA="$(git rev-parse HEAD)"
MERGE_BASE="$(git merge-base main HEAD)"

printf 'main=%s\nfeature=%s\nmerge_base=%s\n' "$MAIN_SHA" "$FEATURE_SHA" "$MERGE_BASE"
git log --oneline --decorate --graph --all

The merge base should equal the baseline main commit because the feature branch has one commit on top of main. That predictable topology is the reference for every later comparison.

3. Create a disposable origin so shallow-clone behavior is real

cd ..
git clone --bare author origin.git
cd author
git remote add origin "../origin.git"
git push -u origin main
git push -u origin feature/demo

mkdir -p evidence
git show-ref | tee evidence/full-show-ref.txt
git rev-parse --is-shallow-repository | tee evidence/full-is-shallow.txt

A local bare origin lets the exercise reproduce CI-like checkout behavior without touching GitHub/GitLab/Bitbucket/Azure or any external repository.

4. Prepare two narrowly scoped Community Build projects

Create two disposable projects in the local SonarQube UI:

  • sq-ch17-main — the real main-branch baseline.
  • sq-ch17-feature-sim — a simulation project that will analyze the feature revision as its own main branch.

Create one project-analysis token for each project. Export them only in the current shell; do not put them in Git or sonar-project.properties.

export SONAR_HOST_URL="http://localhost:9000"
read -rsp "Main project token: " SONAR_TOKEN_MAIN; echo
read -rsp "Feature simulation token: " SONAR_TOKEN_FEATURE; echo
export SONAR_TOKEN_MAIN SONAR_TOKEN_FEATURE

curl -fsS "$SONAR_HOST_URL/api/system/status"
sonar-scanner --version

5. Run and preserve the real main-branch analysis

cd ../author
git switch main
mkdir -p evidence/main

git rev-parse HEAD | tee evidence/main/revision.txt
git rev-parse --is-shallow-repository | tee evidence/main/is-shallow.txt

SONAR_TOKEN="$SONAR_TOKEN_MAIN" sonar-scanner -X \
  -Dsonar.projectKey=sq-ch17-main \
  2>&1 | tee evidence/main/scanner.log
cp .scannerwork/report-task.txt evidence/main/report-task.txt

MAIN_TASK="$(sed -n 's/^ceTaskId=//p' .scannerwork/report-task.txt)"
curl -fsS -H "Authorization: Bearer $SONAR_TOKEN_MAIN" \
  "$SONAR_HOST_URL/api/ce/task?id=$MAIN_TASK" \
  | tee evidence/main/ce-task.json

Wait for the task to finish before recording Quality Gate and measures. This is a normal Community Build project analysis. It establishes a real SonarQube baseline but does not license multi-branch semantics.

6. Analyze the feature revision without mislabeling it

Community Build cannot retain feature/demo as a branch of sq-ch17-main. To satisfy the executable learning goal without overwriting main, analyze the feature revision into the second disposable project:

git switch feature/demo
mkdir -p evidence/feature-sim

git rev-parse HEAD | tee evidence/feature-sim/revision.txt
git merge-base main HEAD | tee evidence/feature-sim/merge-base.txt
git diff --name-status main...HEAD | tee evidence/feature-sim/diff.txt

SONAR_TOKEN="$SONAR_TOKEN_FEATURE" sonar-scanner -X \
  -Dsonar.projectKey=sq-ch17-feature-sim \
  2>&1 | tee evidence/feature-sim/scanner.log
cp .scannerwork/report-task.txt evidence/feature-sim/report-task.txt
Interpretation boundary. sq-ch17-feature-sim is an independent project whose “main” happens to contain the feature revision. Its overall metrics are not PR New Code metrics and must not be compared as if SonarQube had analyzed a commercial branch.

7. Build the faithful Developer+ identity manifest

mkdir -p evidence/commercial-simulation
cat > evidence/commercial-simulation/pr-identity.properties <<'EOF'
sonar.projectKey=sq-ch17-main
sonar.pullrequest.key=17
sonar.pullrequest.branch=feature/demo
sonar.pullrequest.base=main
EOF

cat > evidence/commercial-simulation/branch-identity.properties <<'EOF'
sonar.projectKey=sq-ch17-main
sonar.branch.name=feature/demo
EOF

git rev-parse HEAD > evidence/commercial-simulation/feature-sha.txt
git rev-parse main > evidence/commercial-simulation/target-sha.txt
git merge-base main HEAD > evidence/commercial-simulation/merge-base.txt
git diff --name-status main...HEAD > evidence/commercial-simulation/changed-files.txt

Those files are the free simulation of the commercial identity contract. They are useful evidence even on Developer+ because they let you prove what the CI job intended to analyze.

8. Optional Developer+ execution path

If you have an authorized Developer/Enterprise/Data Center test instance and a CI workspace with the feature branch checked out and main fetched, you may execute one of these separate analyses:

# Branch analysis (Developer+), not a PR:
SONAR_TOKEN="$SONAR_TOKEN" sonar-scanner \
  -Dsonar.projectKey=sq-ch17-licensed \
  -Dsonar.branch.name=feature/demo

# Pull/Merge-request analysis (Developer+) in a CI workspace:
SONAR_TOKEN="$SONAR_TOKEN" sonar-scanner \
  -Dsonar.projectKey=sq-ch17-licensed \
  -Dsonar.pullrequest.key=17 \
  -Dsonar.pullrequest.branch=feature/demo \
  -Dsonar.pullrequest.base=main

The target/main branch should already have a successful analysis. On supported CI providers, prefer automatic branch/PR detection when configured correctly; manual PR parameters override that detection.

9. Deliberately break SCM history, then repair it

cd ..
rm -rf shallow
ORIGIN_URL="file://$(pwd)/origin.git"
git clone --depth 1 --branch feature/demo "$ORIGIN_URL" shallow
cd shallow

mkdir -p evidence
# Prediction: true
git rev-parse --is-shallow-repository | tee evidence/is-shallow-before.txt
# main may be absent; preserve the failure instead of hiding it:
git merge-base HEAD origin/main 2>&1 | tee evidence/merge-base-before.txt || true

# Least-destructive repair: fetch full history and the target branch.
git fetch --unshallow origin
git fetch origin main:refs/remotes/origin/main

git rev-parse --is-shallow-repository | tee evidence/is-shallow-after.txt
git merge-base HEAD origin/main | tee evidence/merge-base-after.txt
git diff --name-status origin/main...HEAD | tee evidence/diff-after.txt

Do not delete .git, rewrite commits, or fabricate target refs. The repair restores the missing evidence layer.

10. Challenge: choose the owning layer

A Developer+ PR analysis has a valid ceTaskId and green Quality Gate in SonarQube, but Azure DevOps shows no pull-request status. What should you investigate?

Answer before revealing the next lesson: provider integration/binding, app/PAT permissions, repository identity, and PR metadata—not scanner source indexing or database state first.

Knowledge check

Why use a second Community Build project for the feature simulation?

May you compare the feature-simulation project’s overall issue count to PR New Code issue count?

Why create two project-analysis tokens rather than one administrator token?

What proves the shallow-clone defect was repaired?

Should branch and pull-request identity parameters be combined in one scan?

Next lesson

Choose branch and PR analysis patterns deliberately

Lesson 3 converts the workflow into durable branch, PR, CI and provider design decisions.

Official references and version notes

Version and compatibility note

Rechecked 2026-09-08. Mandatory examples target SonarQube Community Build 26.9.0.129388 and SonarScanner CLI 8.1.0.6389. Current commercial reference points are SonarQube Server 2026 Release 4.1 and 2026.1.5 LTA. Current product packaging places analysis of feature/maintenance branches, pull/merge requests, and PR quality-gate decoration in Developer Edition and above. Pull-request analysis must run in a CI pipeline; the source branch must be checked out, the target fetched, valid .git metadata retained, and full-enough history available. SonarSource recommends full depth; for GitHub Actions the documented pattern is fetch-depth: 0. Supported CI systems can auto-detect branch/PR parameters; manually supplied PR properties override automatic detection. Recheck provider-specific integration limits and CI auto-detection behavior before production rollout.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.