This domain is 10-15% of AZ-400. Questions mix Git commands with the two platform controls: branch policies in Azure Repos and branch protection rules / rulesets in GitHub. You need to know which setting enforces what, and how to fix common Git accidents.
Branching strategies
| Strategy | How it works | Fits | Weak point |
|---|---|---|---|
| Trunk-based development | Everyone commits to main (trunk) in tiny changes, or through very short-lived branches (hours to a day or two). Unfinished work hides behind feature flags |
Teams with strong automated tests who deploy often | Needs mature CI, flags and discipline |
| Feature branch (GitHub Flow style) | One short-lived branch per feature or fix, merged to main by pull request |
Most teams, continuous delivery | Long-lived branches cause merge pain |
| Release branch | A branch cut from main per release (release/1.4) that receives only stabilising fixes and hotfixes, which are also merged back |
Several supported versions at once, scheduled releases, on-premises products | Merge overhead, backporting |
| GitFlow | Long-lived main and develop, plus feature, release and hotfix branches |
Formal scheduled releases | Heavy, slows CI/CD |
| Forking workflow | Contributors fork the repo, push to the fork, and open a pull request to the upstream | Open source, outside contributors | More steps |
Choosing
- Continuous deployment, small team, mature tests -> trunk-based or GitHub Flow.
- Multiple versions in the field (customers pinned on 1.x and 2.x) -> release branches.
- Many external contributors -> fork workflow.
- Need to ship unfinished code safely -> trunk-based with feature flags (App Configuration feature management).
Branch naming and hygiene
- Use prefixes such as
feature/,bugfix/,hotfix/,release/, and include a work item number where helpful (feature/1234-login). - Delete branches after merge (a setting in both platforms).
- Keep branches short-lived. A long-lived feature branch hides integration problems.
- Merge vs rebase: merge keeps history and creates a merge commit; rebase rewrites the branch so it is linear. Never rebase shared, already-pushed commits.
Pull request workflow
A pull request (PR) proposes merging one branch into another, hosts review discussion and runs checks.
Typical design:
- Create a branch from
main. - Push commits and open a PR (draft first if early).
- Automatic checks run (build, tests, linters, security scans).
- Reviewers are added automatically (code owners, required reviewers).
- Comments are resolved, and the PR is approved.
- The PR merges with the allowed merge type, and the branch is deleted.
Ways to improve a PR process: pull request templates (.github/pull_request_template.md, or pull_request_template.md in Azure Repos), small PRs, required comment resolution, auto-complete, and linking work items.
Merge types
| Type | Result | When |
|---|---|---|
| Merge commit (no fast-forward) | All branch commits plus a merge commit | Keep full history |
| Squash merge | One commit on the target | Clean main history, noisy branch commits |
| Rebase and fast-forward (GitHub: rebase and merge; Azure Repos: rebase and fast-forward) | Branch commits replayed on top of target, no merge commit | Linear history keeping each commit |
| Semi-linear merge (Azure Repos) | Rebase then a merge commit | Linear-ish history with a marker |
Azure Repos branch policies
Branch policies are set on a branch (or a pattern such as release/*) in Repos > Branches > Branch policies. They apply to a pull request into that branch, and direct pushes are blocked once a policy is on.
| Policy | What it does |
|---|---|
| Require a minimum number of reviewers | N approvals. Options: allow requestors to approve their own changes, prohibit the most recent pusher from approving, allow completion even if some reviewers vote to wait or reject, reset votes on new pushes |
| Check for linked work items | Blocks completion if no work item is linked (required or optional) |
| Check for comment resolution | All comments must be resolved |
| Limit merge types | Allow only the chosen merge strategies (for example squash only) |
| Build validation | Runs a pipeline on PR changes. Choose trigger (automatic or manual), policy requirement (required or optional), build expiration (when the target branch moves), and path filters |
| Status checks | Require an external service to post a status |
| Automatically included reviewers | Add reviewers (people or groups) when files match a path, with required or optional status. Works like code owners |
Branch policy facts that show up:
- Policies apply to the target branch.
- Administrators can bypass policies only with the Bypass policies when pushing or Bypass policies when completing pull requests permissions. Grant them rarely.
- Auto-complete lets the author set a PR to merge by itself once all policies pass.
- Branch policy for
release/*uses the wildcard option, and cross-repo policies at project level let you enforce for the same branch name in all repos. - Branch permissions (Security on the branch) control who can push, force push, rewrite history, manage permissions or contribute.
GitHub branch protection rules and rulesets
GitHub has two overlapping mechanisms. Rulesets are the newer, more flexible one; branch protection rules are the classic one. You can use both.
Protection options (both support these in some form):
- Require a pull request before merging: number of approvals, dismiss stale approvals on new commits, require review from Code Owners, require approval of the most recent push, require conversation resolution.
- Require status checks to pass: choose the checks by name (workflow job name), and optionally require branches to be up to date before merging.
- Require signed commits.
- Require linear history (no merge commits).
- Require merge queue: serialises merges and tests each against the latest base.
- Require deployments to succeed before merging.
- Lock branch (read-only).
- Do not allow bypassing the above settings (apply to administrators), or list bypass actors.
- Restrict who can push to matching branches.
- Allow force pushes and allow deletions are off by default for protected branches.
- Restrict creations / updates / deletions, and block force pushes.
Rulesets add: multiple rulesets layered together; enforcement status (Active, Evaluate, Disabled) for a trial run; bypass lists by role, team or app; targeting branches, tags and, at organisation level, many repositories at once; and metadata restrictions (commit message or branch name patterns).
CODEOWNERS
A CODEOWNERS file (in .github/, root or docs/) maps paths to users or teams. Combined with “Require review from Code Owners”, a change to /payments/** needs approval from the payments team. Azure Repos achieves the same with automatically included reviewers by path.
Branch merging restrictions
| Goal | Azure Repos | GitHub |
|---|---|---|
No direct push to main |
A branch policy on main |
Require pull request before merging |
| Two approvals | Minimum reviewers: 2 | Required approvals: 2 |
| Author can’t self-approve | Untick allow requestors to approve their own changes | Branch protection requires approval from someone other than the last pusher |
| Tests must pass | Build validation | Required status checks |
| Specific team reviews | Auto-included reviewers | CODEOWNERS plus require code owner review |
| Clean history | Limit merge types to squash | Allow only squash merge in repository settings or require linear history |
| Only some people merge or push | Branch security | Restrict who can push, or rulesets bypass list |
Managing large files
Git stores every version of every file. Big binaries (images, models, videos, compiled assets) bloat the repository and slow clones.
Git LFS
Git Large File Storage (LFS) replaces large files in the repo with small pointer files and stores the content on a separate LFS server.
git lfs install
git lfs track "*.psd" # writes the rule to .gitattributes
git add .gitattributes
git add design.psd
git commit -m "Add design file"
git push
- The patterns live in
.gitattributes, which must be committed. - Both Azure Repos and GitHub support LFS. GitHub has storage and bandwidth quotas beyond the free allowance (and a per-file size limit that depends on the plan), and standard Git objects over 100 MB are rejected, so use LFS for those.
git lfs migrate import --include="*.zip" --everythingrewrites history to move existing large files into LFS.- Clones fetch pointers, then download the needed LFS content at checkout (
GIT_LFS_SKIP_SMUDGE=1skips it). - Locks (
git lfs lock) prevent conflicting edits of unmergeable binaries.
git-fat
git-fat is an older third-party extension that stores large files in an external store (such as S3 or SSH) and keeps placeholders in Git. It exists for the exam as the alternative to LFS. Prefer Git LFS because it is supported natively by the hosting services.
Other approaches
- Don’t commit build output or dependencies. Use
.gitignore. - Store binaries as package feed artifacts (Azure Artifacts Universal Packages) or in Blob storage, and download them in the pipeline.
- Use shallow clone (
--depth 1) orfetch-depthin pipelines.
Scaling and optimising repositories
Large monorepos with years of history and many files make git status, clone and fetch slow.
| Technique | Effect |
|---|---|
| Scalar | A Microsoft tool (now part of Git, scalar command) that configures a repo for large scale: partial clone, sparse checkout, background maintenance, the filesystem monitor, commit-graph, multi-pack index |
Partial clone (--filter=blob:none) |
Download blobs on demand |
Sparse checkout (git sparse-checkout) |
Check out only chosen directories |
Shallow clone (--depth) |
Limit history |
git maintenance / git gc |
Pack and prune objects in the background |
| Commit-graph | Faster history traversal |
| Split into multiple repos | Reduce size but you now need ways to share code |
scalar clone https://dev.azure.com/org/project/_git/bigrepo
Scalar clone enables partial clone and sparse checkout by default, and registers the repository for background maintenance.
Cross-repository sharing
When code is shared across repositories you have these options:
| Option | Behaviour | Notes |
|---|---|---|
| Git submodules | A repo is pinned at a specific commit inside another | Explicit version, but the extra git submodule update --init step trips people up |
| Git subtree | Another repo’s content is copied into a subdirectory with history | No special clone steps, but harder to push changes back |
| Packages (NuGet, npm, Maven, PyPI) | Shared code published to Azure Artifacts or GitHub Packages and referenced by version | Best for libraries, supports SemVer |
| Multi-repo checkout in pipelines | resources: repositories and - checkout: other-repo |
Build from several repos in one pipeline |
| Monorepo | One repo for everything | Simple sharing, needs scale tooling |
Repository permissions
Azure Repos
- Permissions are set on project, repository and branch/folder levels. Security groups: Project Administrators, Contributors, Readers, plus custom groups. Each permission has Allow, Deny, Not set; Deny overrides Allow (except for Project Collection Administrators, where inheritance rules apply).
- Common repo permissions: Contribute, Create branch, Create tag, Force push, Manage permissions, Bypass policies, Read, Delete repository, Rename repository, Contribute to pull requests.
- Use Azure Repos > Security to set this per repository, and per branch under the branch’s Security.
- Assign to Microsoft Entra groups, not individuals.
- Disable a repository or make it read-only to freeze it.
- The Build Service identity needs Contribute if the pipeline pushes to the repo.
GitHub
| Role | Capabilities |
|---|---|
| Read | View and clone, open issues and PRs (non-code contributors) |
| Triage | Manage issues and PRs without write access |
| Write | Push to non-protected branches, merge PRs |
| Maintain | Manage the repo without sensitive or destructive actions |
| Admin | Full control, including settings and security |
| Custom repository roles | Enterprise Cloud feature: pick specific permissions on top of a base role |
- Organisation roles: Owner, Member, plus billing manager, security manager.
- Assign access through teams to scale.
- An outside collaborator is a non-member given access to specific repositories only.
- Repository visibility: public, private, internal (enterprise).
Tags
A tag points to a commit permanently, unlike a branch. Tags mark releases and versions.
| Type | Details |
|---|---|
| Lightweight | git tag v1.0.0 is just a pointer |
| Annotated | git tag -a v1.0.0 -m "Release 1.0.0" has tagger, date and message, and can be signed (-s) |
git tag -a v1.2.0 -m "Release 1.2.0"
git push origin v1.2.0 # tags are not pushed by default
git push origin --tags # push all tags
git tag -d v1.2.0 # delete locally
git push origin --delete v1.2.0 # delete remote
Use annotated tags for releases. Tag patterns can trigger pipelines (on: push: tags: ['v*'] in GitHub Actions; trigger: tags: include: in Azure Pipelines) and drive release creation. Protect tags with rulesets or tag protection so nobody can move or delete release tags. In Azure Repos you need the Create tag permission.
Recovering data with Git
| Problem | Command |
|---|---|
| Undo the last commit but keep changes | git reset --soft HEAD~1 |
| Discard last commit and changes | git reset --hard HEAD~1 |
| Undo a pushed commit safely | git revert <sha> creates a new inverse commit |
| Restore a file from a commit | git restore --source=<sha> -- path (or git checkout <sha> -- path) |
| Recover a lost commit or deleted branch | git reflog, find the SHA, then git branch recover <sha> |
| Find which commit broke something | git bisect start, then good/bad |
| Who changed this line | git blame path |
| Stash work in progress | git stash, git stash pop |
| Apply one commit elsewhere | git cherry-pick <sha> (used for hotfix backports) |
| Abort a bad merge or rebase | git merge --abort, git rebase --abort |
Reflog records where HEAD and branch tips pointed locally, so commits “lost” after reset --hard or a deleted branch are recoverable for about 90 days by default (unreachable ones are pruned after 30 days, per gc.reflogExpireUnreachable). Reflog is local; it doesn’t exist on the server. On Azure Repos a deleted branch can be restored from Branches > Deleted (search deleted branches) and GitHub offers Restore branch on a closed PR.
git revert vs git reset: on a shared branch use revert because reset rewrites history and requires a force push.
Removing specific data from source control
Deleting a file in a new commit doesn’t remove it from history. To purge a secret or huge file:
- Rotate the secret first. Treat it as compromised since anyone with a clone has it.
- Rewrite history. Use
git filter-repo(the recommended tool) or BFG Repo-Cleaner;git filter-branchis deprecated and slow. - Force push all affected branches and tags.
- Ask collaborators to re-clone or rebase.
- On GitHub, contact support to purge cached views and PR refs (
refs/pull/*). Pull requests hold read-only refs that history rewriting can’t touch. Also remove the sensitive data from forks. - On Azure Repos, the cleanest option is to create a new repo from cleaned history, or run
git filter-repoand force push; deleted content may remain until garbage collection.
git filter-repo --path secrets.txt --invert-paths
git filter-repo --replace-text expressions.txt
Prevention: push protection (GitHub secret scanning blocks pushes containing known secret patterns), pre-commit hooks (git-secrets, detect-secrets, gitleaks), .gitignore, and GitHub Advanced Security for Azure DevOps push protection.
Exam checklist
- You can match each strategy (trunk-based, feature, release branch, GitFlow, fork) to its scenario, and know feature flags make trunk-based safe.
- You know the Azure Repos policies: minimum reviewers, linked work items, comment resolution, limit merge types, build validation, status checks and auto-included reviewers.
- You know GitHub branch protection options, rulesets (evaluate mode, bypass list, many repos), CODEOWNERS and merge queue.
- You can pick merge, squash or rebase for a history requirement.
- You know Git LFS uses
.gitattributesand pointers, and git-fat is the older alternative. - You know Scalar, partial clone and sparse checkout help big repos, and submodules, subtrees and packages share code.
- You can describe Azure Repos permission groups and GitHub roles (Read, Triage, Write, Maintain, Admin) and outside collaborators.
- You know annotated tags, that tags need an explicit push, and that tags can trigger pipelines.
- You can recover with
reflog,revert,restore,cherry-pick, and purge secrets usinggit filter-repoor BFG, rotating the secret first.