Domain 2: Design and Implement a Source Control Strategy
Exam Weight: 10–15% 📁 ← Back to Home
📑 Table of Contents
2.1 Design and Implement Branching Strategies
Branch Strategy Comparison
| Strategy | Long-lived Branches | Best For | Complexity |
|---|---|---|---|
| Trunk-Based Development | main only |
Continuous delivery, experienced teams | Low |
| Feature Branch | main + short-lived feature branches |
Most teams | Low–Medium |
| GitHub Flow | main + feature branches |
Web apps, continuous delivery | Low |
| Git Flow | main, develop, release, hotfix, feature |
Versioned software, scheduled releases | High |
| Release Branch | main + release/x.x branches |
Products with multiple supported versions | Medium |
Trunk-Based Development
Core principle: All developers commit to a single shared branch (main/trunk) multiple times per day.
1
2
3
4
5
main ──●──●──●──●──●──●──►
│ │
short short
feature feature
(< 2d) (< 2d)
Key practices:
- Feature flags to hide incomplete features in production
- Very small, frequent commits — avoid long-running branches
- Strong CI gates — every commit must not break the build
- Pair programming or continuous code review
When to use:
- High-performing, experienced teams
- Continuous delivery pipelines
- Microservices architecture
Feature Branch Workflow
1
2
3
4
main ─────────────────────────────────────────►
↑ ↑
feature/login ──●──●──●── PR ──► merge
(squash or merge commit)
Naming conventions:
1
2
3
4
feature/AZ-1234-user-authentication
bugfix/AZ-5678-null-reference
hotfix/payment-gateway-timeout
release/2.4.0
Benefits:
- Isolated development — broken code doesn’t affect
main - Natural PR review point
- Easy to abandon work (just delete the branch)
Release Branching
Used when maintaining multiple versions in production simultaneously.
1
2
3
4
5
main ──────────────────────────────────────────►
│ │
release/1.0 ──●──●── release/2.0 ──●──●──
↑ ↑
patch fixes patch fixes
Pattern:
- Branch from
mainwhen feature freeze begins - Only bug fixes cherry-picked onto the release branch
- Tag the release branch at each release point
- New features go to
mainfor the next release
Pull Request Workflow
PR Lifecycle:
1
2
1. Create branch → 2. Develop & commit → 3. Open PR → 4. Review →
5. CI checks pass → 6. Approve → 7. Merge → 8. Delete branch
Branch Policies (Azure Repos):
| Policy | Description |
|---|---|
| Require minimum reviewers | e.g., 2 approvals required before merge |
| Check for linked work items | Enforce traceability — PR must link to a work item |
| Check for comment resolution | All PR comments must be resolved before merge |
| Limit merge types | Allow only squash merge, no merge commits |
| Build validation | Run specific pipeline as a required check |
| Status checks | Require external status checks (SonarQube, etc.) |
Branch Protections (GitHub):
| Protection | Description |
|---|---|
| Require pull request reviews | Minimum approvals required |
| Require status checks | CI must pass before merge |
| Require branches to be up to date | Branch must include latest from base |
| Require signed commits | GPG-signed commits only |
| Include administrators | Rules apply even to admins |
| Restrict who can push | Limit push access to specific users/teams |
⭐ Exam tip: Azure DevOps calls them Branch Policies; GitHub calls them Branch Protection Rules. Both achieve the same goals but have different UIs and feature sets.
Branch Merging Restrictions
Merge strategies:
| Strategy | Description | Use Case |
|---|---|---|
| Merge Commit | Preserves full history, adds merge commit | Long-lived feature branches |
| Squash Merge | All commits combined into one | Cleaner main branch history |
| Rebase and Fast-Forward | Linear history, no merge commit | Teams preferring linear git log |
| Semi-Linear (Rebase + Merge) | Rebase then creates merge commit | Balance of history and clarity |
CODEOWNERS file (GitHub/Azure DevOps):
1
2
3
4
5
# Syntax: <pattern> <owner(s)>
/docs/ @docs-team
*.ts @frontend-team @lead-developer
/infrastructure/ @devops-team
/src/auth/ @security-team
When a PR touches a file matching a pattern, the listed owners are automatically requested for review.
2.2 Configure and Manage Repositories
Managing Large Files
Git Large File Storage (LFS)
Standard Git is not designed for large binary files (videos, datasets, compiled binaries). Git LFS replaces large files with text pointer files and stores the actual files on a separate server.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Install Git LFS
git lfs install
# Track file types
git lfs track "*.psd"
git lfs track "*.mp4"
git lfs track "*.zip"
# This creates/updates .gitattributes:
# *.psd filter=lfs diff=lfs merge=lfs -text
# Commit and push as normal
git add .gitattributes
git add large-file.psd
git commit -m "Add design assets"
git push origin main
Azure Repos LFS: Supported natively. Enable in repo settings.
GitHub LFS: Free up to 1 GB storage + 1 GB bandwidth/month. Additional packs available.
git-fat
An alternative to Git LFS using an external object store (rsync, S3, etc.). Less commonly used but still referenced in the exam.
1
2
3
# Configure git-fat (in .gitfat file)
[rsync]
remote = your-server:/fat-store
Scaling and Optimizing Git Repositories
Scalar
Scalar is a tool (built by Microsoft, now part of Git) that dramatically improves performance of large repositories through:
| Feature | Description |
|---|---|
| Partial clone | Only download objects you actually access |
| Sparse checkout | Only check out the subdirectories you need |
| Background maintenance | Automatic git fetch and repo maintenance |
| File system monitor | Faster git status using OS file watching |
| Commit graph | Pre-computed graph speeds up git log and git blame |
1
2
3
4
5
# Clone a repo with Scalar
scalar clone https://github.com/org/large-repo
# Optimize an existing repo
scalar register
Scalar was originally developed for the Windows OS repository (one of the largest Git repos in the world at ~300 GB).
Cross-Repository Sharing
| Technique | Description |
|---|---|
| Git Submodules | Embed a reference to another repo at a specific commit |
| Git Subtree | Copy another repo’s history into a subdirectory |
| NuGet/npm packages | Share code as packages (preferred for libraries) |
| Azure Artifacts | Host packages internally for cross-repo sharing |
| Sparse checkout | Check out only certain directories of a monorepo |
Git Submodule example:
1
2
3
4
5
6
7
8
# Add a submodule
git submodule add https://github.com/org/shared-lib ./libs/shared
# Clone including submodules
git clone --recurse-submodules https://github.com/org/project
# Update all submodules
git submodule update --remote --merge
Permissions in Source Control
Azure Repos Permissions
Permission levels (most → least privileged):
1
2
3
4
5
Organization Owner
└── Project Administrator
└── Contributor
└── Reader
└── Stakeholder
Repository-level permissions:
| Permission | Description |
|---|---|
| Contribute | Push to non-protected branches |
| Create branch | Create new branches |
| Delete or disable repository | Destructive action |
| Force push | Overwrite history (dangerous — restrict this!) |
| Manage permissions | Change repo ACLs |
| Read | Clone and view |
Branch-level permissions:
- Override branch policies on a per-user/group basis
- Grant “Exempt from policy enforcement” sparingly
GitHub Repository Permissions
| Role | Permissions |
|---|---|
| Read | View and clone |
| Triage | Manage issues/PRs, no write access |
| Write | Push to non-protected branches, manage issues |
| Maintain | Manage repo (no destructive actions) |
| Admin | Full control including settings and deletion |
GitHub Teams: Group users and assign repo roles at the team level.
Tags
Tags mark specific points in repository history — typically used for releases.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Lightweight tag (just a pointer)
git tag v1.0.0
# Annotated tag (recommended — includes metadata)
git tag -a v2.1.0 -m "Release 2.1.0 - adds payment gateway"
# Tag a specific commit
git tag -a v1.9.5 abc1234 -m "Hotfix release"
# Push tags to remote
git push origin v2.1.0 # Push single tag
git push origin --tags # Push all tags
# List tags
git tag --list "v2.*"
# Delete a tag
git tag -d v1.0.0 # Delete locally
git push origin :v1.0.0 # Delete remotely
Semantic Versioning (SemVer):
1
2
3
4
5
v MAJOR . MINOR . PATCH
^ ^ ^
│ │ └── Bug fixes (backwards-compatible)
│ └────────── New features (backwards-compatible)
└────────────────── Breaking changes
Recovering Data with Git Commands
| Scenario | Command |
|---|---|
| Undo last commit, keep changes staged | git reset --soft HEAD~1 |
| Undo last commit, keep changes unstaged | git reset --mixed HEAD~1 |
| Undo last commit, discard all changes | git reset --hard HEAD~1 |
| Recover a deleted branch | git checkout -b <branch> <last-commit-sha> |
| Undo a specific commit (safe, creates new commit) | git revert <commit-sha> |
| Restore a deleted file | git checkout HEAD -- path/to/file |
| Find a lost commit | git reflog |
| Apply a single commit from another branch | git cherry-pick <commit-sha> |
Using git reflog to recover:
1
2
3
4
5
6
7
8
9
10
# Show all recent HEAD movements
git reflog
# Output:
# abc1234 HEAD@{0}: commit: WIP: new feature
# def5678 HEAD@{1}: reset: moving to HEAD~1
# ghi9012 HEAD@{2}: commit: remove old code ← restore this?
# Restore to a previous state
git reset --hard HEAD@{2}
⭐ Exam tip:
git reflogis your safety net. It keeps a local log of all changes to HEAD for ~90 days.
Removing Data from Source Control
⚠️ Warning: These operations rewrite history and are destructive. Always coordinate with the team.
Remove a file from all history (e.g., accidentally committed secrets)
Option 1: BFG Repo Cleaner (recommended)
1
2
3
4
# Install BFG (Java tool)
java -jar bfg.jar --delete-files secret.env repo.git
java -jar bfg.jar --replace-text passwords.txt repo.git
git push --force
Option 2: git filter-repo (modern alternative to filter-branch)
1
2
3
4
5
# Remove a file from all history
git filter-repo --path secret.env --invert-paths
# Remove all files matching a pattern
git filter-repo --path-glob "*.pem" --invert-paths
Option 3: Azure DevOps — Contact Microsoft Support to permanently remove data from the server-side cache after a force push.
After removing sensitive data:
- Force push the rewritten history
- Rotate all exposed secrets immediately — assume they are compromised
- Notify all collaborators to re-clone (their local copies still have the data)
- Clear GitHub/Azure DevOps cached views (may require support ticket)
🧠 Key Exam Tips for Domain 2
| Scenario | Best Answer |
|---|---|
| Large binary files slowing down repo | Git LFS |
| Speed up a monorepo clone | Scalar with partial clone + sparse checkout |
| Require 2 approvals before merge (Azure) | Branch Policy: Minimum number of reviewers |
| Auto-assign reviewers based on file path | CODEOWNERS file |
| Released code in production; need to undo safely | git revert (creates a new commit — safe for shared branches) |
| Undo local commits before pushing | git reset |
| Accidentally committed a password to Git | BFG Repo Cleaner or git filter-repo, then rotate the secret |
| Share a library across multiple repos | Azure Artifacts or Git submodules |
| Teams maintaining multiple active versions | Release branching strategy |
| Daily deployments, experienced team | Trunk-Based Development |