Git & Azure DevOps Guide
Companion guide for the data team working with Power BI (PBIP format) and Azure DevOps. This document does not repeat general Git fundamentals β refer to the Git & GitHub Training Material for those. Everything here is specific to our tools, branching model, naming conventions, and review process.
Table of Contentsβ
- How This Differs from the Developer Guide
- Our Tools & Environment
- Understanding PBIP Format
- Getting PBIP Changes into the Repo
- Branching Strategy
- Naming Conventions
- Ticket Workflow β From Request to Production
- Daily Workflow β Step by Step
- Keeping Your Branch Up to Date
- Pull Requests in Azure DevOps
- Branch Policies & Approval Rules
- Code Review Expectations
- Common Mistakes & Fixes (Data Team Edition)
- Quick Reference
1. How This Differs from the Developer Guideβ
The main training material covers Git with GitHub for a programming team. Our world is different in a few important ways:
- We use Azure DevOps (Azure Repos + Azure Boards), not GitHub. The Git commands are identical, but the web interface, pull request workflow, and work item linking are different.
- Our "code" is Power BI projects saved in PBIP format β folders of JSON and TMDL files, not traditional source code. Merge conflicts look different, and some files must be ignored.
- Most tickets are small visual changes (fix a title, move a table, add a slicer) that take 1β2 minutes. Our branching model accounts for this β we use one commit per ticket, not one branch per ticket.
- Code review focuses on DAX measures and data model changes, not visual layout. The client handles the visual side; we ensure the logic is sound. DAX standards are covered in a separate DAX & Semantic Model Standards document.
2. Our Tools & Environmentβ
| Tool | Purpose |
|---|---|
| Power BI Desktop | Author reports, save as PBIP |
| VS Code (recommended) | View diffs, stage commits, manage branches, resolve conflicts |
| Git CLI (Git Bash / terminal) | Alternative to VS Code for Git operations |
| Azure DevOps | Remote repository, pull requests, work items (Azure Boards) |
Our Azure DevOps project:
https://dev.azure.com/srg-dev/SRG%20Power%20BI
Clone URL (SSH or HTTPS) β find it in Azure DevOps: Repos β Files β Clone (top right button) β copy the URL.
Tip for beginners: VS Code has a built-in Git panel (the Source Control icon in the left sidebar). It lets you stage, commit, push, pull, and resolve conflicts without touching the command line. If you are new to Git, start here.
3. Understanding PBIP Formatβ
When you save a Power BI file as a .pbip (Power BI Project), Power BI Desktop breaks the monolithic .pbix binary into a folder of human-readable text files:
MyReport/
βββ MyReport.Report/
β βββ report.json β page layouts, visuals, filters
β βββ definition.pbir β report pointer
β βββ ...
βββ MyReport.SemanticModel/
β βββ definition/
β β βββ tables/ β one file per table (TMDL)
β β βββ relationships.tmdl
β β βββ ...
β βββ definition.pbism β model pointer
β βββ ...
βββ .gitignore β auto-generated by PBI Desktop
βββ MyReport.pbip β project pointer file
Why this matters for Git:
- Each measure, table, and relationship is stored as a separate text file. Git can diff and track changes at the individual object level.
- When you change a single measure, only that measure's file shows up as modified β reviewers see exactly what changed.
- The
report.jsonfile changes frequently and can be large. Visual-only changes (moving a chart, resizing a table) show up here.
Important: Always open the
.pbipfile in Power BI Desktop going forward β not a.pbix. The PBIP is your working format now.
4. Getting PBIP Changes into the Repoβ
How it works:
- Clone the repo to your machine
- Open the
.pbipfile in Power BI Desktop - Make changes, save (Ctrl+S) β Power BI updates the text files on disk
- Switch to VS Code β review diffs β stage β commit β push
5. Branching Strategyβ
We use a three-tier branching model tailored to how data development actually works.
main
βββ feature/asset-management/dev β full report build
β βββ feature/asset-management/batch-1 β tickets 101β108
β βββ feature/asset-management/batch-2 β tickets 109β115
β βββ ...
β
βββ hotfix/191-fix-sales-total β urgent fix, straight to main
Tier 1 β mainβ
- Always matches what is deployed to the production Power BI environment.
- No one pushes directly to
main. All changes arrive via pull request.
Tier 2 β Feature branches (feature/report-name)β
- Created when building a new report or major feature addition.
- Lives until the entire report is complete and reviewed, then merges into
mainvia PR. - Example:
feature/asset-management,feature/director-dashboard
Tier 3 β Batch branches (feature/report-name/batch-N)β
- Created off the feature branch to group a set of related tickets.
- Each ticket gets its own commit (not its own branch) with the work item ID in the message.
- When the batch is done, open a PR from the batch branch into the feature branch.
- This is where code review happens for new DAX measures.
Hotfix branches (hotfix/ID-description)β
- Created directly off
mainfor urgent fixes. - Merged back into
mainvia PR. - For a cluster of small urgent fixes:
hotfix/sales-q1-fixeswith a commit per ticket.
When to use whichβ
| Situation | Branch type |
|---|---|
| Building a brand new report from scratch | feature/report-name β batch branches |
| Adding a major new section to an existing report | feature/report-name β batch branches |
| Fixing a typo, broken visual, or wrong total in production | hotfix/ID-description |
| 15 small production fixes in one day | hotfix/descriptive-name with one commit per fix |
6. Naming Conventionsβ
Branch namesβ
| Type | Pattern | Example |
|---|---|---|
| Feature (report) | feature/report-name | feature/asset-management |
| Batch | feature/report-name/batch-N | feature/asset-management/batch-1 |
| Hotfix | hotfix/ID-description | hotfix/191-fix-sales-total |
Rules:
- All lowercase
- Use hyphens to separate words (not underscores or spaces)
- Include the work item ID when it is a single-ticket hotfix
Commit messagesβ
Every commit must reference the Azure Boards work item ID so the change is traceable.
[#101] Fix Move-Ins title wording on page 3
[#102] Add Property Type slicer to overview page
[#103] Correct occupancy rate calculation in DAX measure
Format: [#work-item-ID] Short present-tense description
Rules:
- Use
#only in the commit message β never in a branch name - Present tense: "Fix" not "Fixed", "Add" not "Added"
- Keep the subject line under 72 characters
- Reference the work item ID so Azure DevOps auto-links the commit
Pull request titlesβ
| PR type | Title format | Example |
|---|---|---|
| Batch β Feature branch | Batch N: Short summary | Batch 1: Occupancy measures and slicers |
| Feature β main | Feature: Report Name | Feature: Asset Management |
| Hotfix β main | Hotfix: Short description | Hotfix: Fix sales total calculation |
7. Ticket Workflow β From Request to Productionβ
This section explains how work moves from a request to a finished, deployed change. If you are new to the team, read this first β it is the context for everything else in this document.
Work item hierarchy in Azure Boardsβ
We use three work item types in Azure Boards:
| Work Item Type | What it represents | Example |
|---|---|---|
| Epic | A full report or major deliverable | Asset Management, Director Dashboard, Sales Analytics |
| Task | An individual piece of work under an Epic | Add Property Type slicer to overview page, Create YTD Move-Ins measure |
| Issue | A bug or problem that needs fixing | Move-Ins total not tying out on page 3, Slicer not filtering correctly |
Every Task and Issue lives under an Epic. The Epic is the report.
Where tickets come fromβ
Tickets arrive from two sources:
Client-created tickets: The client has direct access to Azure Boards and creates Tasks or Issues themselves. These typically have a title and a description β often short and to the point (e.g., "Move-Ins isn't tying out in this visual" or "Add a slicer for Property Type"). The client does not fill in technical details, priority, or effort estimates β that is our responsibility.
Internally-created tickets: When we are building a new report from scratch, working on internal documentation, or managing database changes, the data team writes their own tickets. These should follow the same structure as client tickets but will generally include more technical detail since we are writing them for ourselves.
What to do when you receive a client ticketβ
When a new Task or Issue appears under your Epic:
- Read the title and description β understand what the client is asking for
- Fill in the missing fields:
- Assigned To β assign it to yourself (since each report is generally owned by one developer)
- State β move it from
NewtoActivewhen you start working on it - Priority β set based on urgency and impact (coordinate with your lead if unclear)
- Description (add detail if needed) β if the client's description is vague, add a comment clarifying what you understood and what you plan to do. This creates a paper trail if questions come up later
- Link it to the correct Epic β if the client didn't do this, drag it under the right Epic on the board or set the parent link manually
How tickets map to Gitβ
This is where the ticket process connects to the branching strategy from Section 6:
Epic: Asset Management
β
βββ Task #101: Add Property Type slicer βββ commit [#101] on batch branch
βββ Task #102: Create YTD Move-Ins measure βββ commit [#102] on batch branch
βββ Issue #103: Move-Ins total not tying out βββ commit [#103] on batch branch
β ... (batch branch merges into feature branch via PR)
β
βββ Issue #191: Sales total wrong on page 1 βββ commit [#191] on hotfix branch
... (hotfix branch merges into main via PR)
For new report development (Epic with many Tasks):
- The Epic maps to a
feature/report-namebranch - Groups of Tasks become batch branches (
feature/report-name/batch-N) - Each Task or Issue is a single commit with the work item ID in the message
For production fixes (Issues against a live report):
- Individual Issues become
hotfix/ID-descriptionbranches offmain - Or group several small fixes into
hotfix/descriptive-namewith a commit per Issue
Ticket lifecycleβ
A ticket moves through these states:
| State | What it means | Who moves it |
|---|---|---|
| New | Client or team member just created it | Creator |
| Active | A developer is working on it | Developer (when they start) |
| Resolved | The work is done and merged to the target branch | Developer (after PR is merged) |
| Closed | Verified in production or confirmed by client | Developer or lead |
Move the ticket to Resolved when the PR containing its commit is merged. Move it to Closed once the change is confirmed working in the production environment.
Writing good tickets (for internally-created work)β
When you create your own tickets for internal work, include enough context that someone else on the team could pick it up if needed:
β
Title: Create fiscal year date table for Asset Management model
Description: The current calendar table uses calendar year. Asset Management
reporting requires fiscal year starting April 1. Add a FiscalYear, FiscalQuarter,
and FiscalMonth column to the date table. Mark as the model's date table.
β
Title: Refactor occupancy measures to use REMOVEFILTERS
Description: Current occupancy measures use ALL() to clear filters on the
denominator. Switch to REMOVEFILTERS for clarity and consistency with our
DAX standards. Affects: Occupancy Rate %, Vacant Units %, Net Absorption.
β Title: Fix measures
Description: (empty)
β Title: Update stuff
Description: Various changes needed
8. Daily Workflow β Step by Stepβ
This is the loop you will repeat every day. Instructions are shown for both VS Code and the command line.
Starting your day β pull the latest changesβ
VS Code: Open Source Control (Ctrl+Shift+G) β click ... menu β Pull
Command line:
# Switch to your working branch
git checkout feature/asset-management/batch-1
# Pull the latest from the remote
git pull origin feature/asset-management/batch-1
Making changesβ
- Open the
.pbipfile in Power BI Desktop - Make your changes (fix a visual, add a measure, etc.)
- Save in Power BI Desktop (Ctrl+S) β this writes the changes to the text files on disk
- Close Power BI Desktop or at minimum save before switching to VS Code β PBI holds file locks
Reviewing and committingβ
VS Code:
- Open the project folder in VS Code
- Click the Source Control icon (left sidebar) β you'll see changed files listed
- Click on a file to see the diff (what changed)
- Review the changes β make sure nothing unexpected is there
- Stage the files: click the
+icon next to each file (or+next to "Changes" to stage all) - Type your commit message in the text box:
[#101] Fix Move-Ins title wording - Click the checkmark β to commit
Command line:
# See what changed
git status
# Review the actual changes (optional but recommended)
git diff
# Stage all changes
git add .
# Commit with work item reference
git commit -m "[#101] Fix Move-Ins title wording on page 3"
Pushing to Azure DevOpsβ
VS Code: Click ... menu in Source Control β Push (or click the sync icon in the status bar)
Command line:
git push origin feature/asset-management/batch-1
Multiple tickets in one sessionβ
When knocking out many small tickets in a row:
# Make changes for ticket 101, save in PBI Desktop
git add .
git commit -m "[#101] Fix Move-Ins title wording"
# Make changes for ticket 102, save in PBI Desktop
git add .
git commit -m "[#102] Add Property Type slicer"
# Make changes for ticket 103, save in PBI Desktop
git add .
git commit -m "[#103] Move summary table below chart on page 2"
# Push all commits at once
git push
Each ticket gets its own commit, but you only push once when you are ready.
9. Keeping Your Branch Up to Dateβ
When multiple people are working, main or the parent feature branch may get updated while you are on your batch branch. You need to pull those changes in regularly to avoid large merge conflicts later.
Syncing your batch branch with the feature branchβ
# Make sure your work is committed first
git status # should say "nothing to commit"
# Fetch the latest from the remote
git fetch origin
# Merge the parent feature branch into your batch branch
git merge origin/feature/asset-management
If there are conflicts, VS Code will highlight them. For PBIP files:
report.jsonconflicts are common and usually safe to resolve by accepting the incoming version (the visual side is the client's domain)- TMDL/measure conflicts need careful manual review β both versions may have valid logic changes
Syncing the feature branch with main (before final PR)β
git checkout feature/asset-management
git pull origin feature/asset-management
git merge origin/main
# Resolve any conflicts, commit, push
10. Pull Requests in Azure DevOpsβ
A pull request (PR) is how changes move between branches. Here is the step-by-step process in Azure DevOps.
Creating a PR from the web portalβ
- Go to Azure DevOps β
https://dev.azure.com/srg-dev/SRG%20Power%20BI - Navigate to Repos β Pull Requests
- Click New Pull Request
- Set the source branch (your branch, e.g.
feature/asset-management/batch-1) - Set the target branch (where it's merging into, e.g.
feature/asset-management) - Fill in the Title following the naming convention (e.g.
Batch 1: Occupancy measures and slicers) - Write a Description β summarize what changed and why:
- List the work item IDs addressed
- Call out any new DAX measures that need review
- Note any known issues or things to watch for
- Link work items: In the "Work items to link" section, search for and link all relevant work items
- Add reviewers: Add at least one team member. For PRs containing new measures, this is required.
- Click Create
Creating a PR from VS Codeβ
- Install the Azure Repos extension from the VS Code marketplace
- Open the command palette (Ctrl+Shift+P) β search "Azure Repos: Create Pull Request"
- Select source branch, target branch, fill in title and description
- The extension creates the PR and gives you a link to view it in the browser
PR description templateβ
Use this structure for consistency:
## Summary
Brief description of what this batch/hotfix accomplishes.
## Work Items
- [#101] Fix Move-Ins title wording
- [#102] Add Property Type slicer
- [#103] Correct occupancy rate measure
## New or Modified DAX Measures
- `Occupancy Rate` β new measure, calculates occupied units / total units
- `YTD Move-Ins` β modified, added fiscal year filter
## Testing Notes
- Verified totals match source data for JanβMar 2026
- Tested slicer interactions on all pages
## Screenshots (if applicable)
Attach before/after screenshots for visual changes.
Completing (merging) a PRβ
- Once approved, click Complete on the PR page
- Merge type: select Merge (no fast-forward) β this preserves individual commit history
- Optionally check "Delete source branch after merging" (recommended for batch branches, not for feature branches until the full report is done)
- Click Complete merge
11. Branch Policies & Approval Rulesβ
Configure these in Azure DevOps: Repos β Branches β click ... next to the branch β Branch policies.
Protections for mainβ
| Policy | Setting |
|---|---|
| Require a pull request before merging | β Enabled |
| Minimum number of reviewers | 1 (for PRs with DAX/model changes) |
| Allow requestors to approve their own changes | β Disabled |
| Check for linked work items | β Required |
| Check for comment resolution | β Required |
| Allow bypass with reason | β Enabled β for typo fixes and emergency hotfixes |
Bypass policy for emergenciesβ
Sometimes a typo fix or critical hotfix needs to go through immediately without waiting for a reviewer. Azure DevOps allows "bypass with reason" β the person completing the PR must type a justification. This is logged and auditable.
When bypass is acceptable:
- Cosmetic-only changes (typo in a title, label correction) with no measure or model changes
- Emergency hotfix during off-hours when no reviewer is available
Future: Automated review with Claude agentβ
We plan to add an automated review step where a Claude agent scans PRs for DAX best practices. This will run as part of the PR validation and flag issues like:
- Measures missing descriptions
- Use of FILTER on fact tables where CALCULATE would be more efficient
- Inconsistent naming conventions
- Missing error handling (IFERROR / DIVIDE with alternate result)
12. Code Review Expectationsβ
Note: Our DAX naming conventions, measure documentation requirements, display folder standards, and formatting rules are covered in the separate DAX & Semantic Model Standards document. Reviewers should be familiar with that document before reviewing PRs that contain measure changes.
What reviewers should checkβ
Always review (new or modified DAX measures):
- Does the measure have a description?
- Is the DAX logic correct? Does it handle edge cases (nulls, empty tables, division by zero)?
- Is DIVIDE used instead of
/for division (to avoid divide-by-zero errors)? - Are CALCULATE filter contexts correct? Are there unintended filter propagation issues?
- Are variable names clear and descriptive?
- Does the measure follow our naming conventions?
- Are inline comments present for non-trivial logic?
Skim or skip (visual/layout changes):
- Visual position changes in
report.jsonβ these are the client's domain - Formatting changes to existing visuals (colors, fonts, sizes)
How to leave good review commentsβ
- Ask questions: "What happens to this measure when Property Type is filtered to null?"
- Suggest alternatives: "Could this use CALCULATE + REMOVEFILTERS instead of FILTER on the fact table?"
- Approve when the logic is correct, even if you would have written it differently
Responding to review feedbackβ
- Address all comments β either fix the code or explain why you disagree
- Reply "Fixed in [commit hash]" when done
- Push the fixes as a new commit (don't amend β the reviewer needs to see what changed)
13. Common Mistakes & Fixes (Data Team Edition)β
"I see changes but I didn't edit anything"β
This happens when you open the PBIP and Power BI Desktop updates cache files or internal metadata. Check your .gitignore β the .pbi/ folder and *.pbip.user should be listed. If they are already ignored and you still see phantom changes, look for auto-generated timestamp fields in report.json and consider ignoring those specific patterns.
# Discard all unstaged changes (when you know they are just PBI noise)
git checkout -- .
# Modern syntax
git restore .
"I committed to the wrong branch"β
# Move the last commit to the correct branch
git branch correct-branch-name # create a branch at current commit
git reset HEAD~1 --soft # undo commit on current branch (keep changes)
git stash # stash the changes
git checkout correct-branch-name # switch to correct branch
git stash pop # restore changes
git commit -m "[#101] Your message" # recommit on the right branch
"report.json has a merge conflict"β
This is common because report.json is a large file that changes with any visual edit. If the conflict is only in visual/layout sections (not measure references or data bindings):
# Accept the incoming version (the other branch's changes)
git checkout --theirs MyReport.Report/report.json
git add MyReport.Report/report.json
git commit -m "merge: accept incoming visual layout changes"
If the conflict involves data bindings or measure references, open both versions side by side in VS Code and merge carefully.
"I need to undo my last push"β
# DON'T use git reset --force on shared branches
# Instead, create a new commit that reverses the change
git revert HEAD
git push
"Power BI says the file is in use"β
Close Power BI Desktop before running Git operations that modify files on disk (pull, merge, checkout). PBI holds file locks that prevent Git from updating files.
14. Quick Referenceβ
Commands you'll use dailyβ
| Task | Command |
|---|---|
| Check what changed | git status |
| See the actual diff | git diff |
| Stage all changes | git add . |
| Commit with work item link | git commit -m "[#101] Description" |
| Push to Azure DevOps | git push |
| Pull latest changes | git pull |
| Create and switch to a new branch | git checkout -b feature/report-name/batch-1 |
| Switch to an existing branch | git checkout feature/report-name |
| Merge parent branch into yours | git merge origin/feature/report-name |
| Discard all uncommitted changes | git restore . |
| Stash work in progress | git stash |
| Restore stashed work | git stash pop |
| View commit history | git log --oneline |
Azure DevOps URLsβ
| Page | URL |
|---|---|
| Project home | https://dev.azure.com/srg-dev/SRG%20Power%20BI |
| Repos (files & branches) | https://dev.azure.com/srg-dev/SRG%20Power%20BI/_git |
| Pull requests | https://dev.azure.com/srg-dev/SRG%20Power%20BI/_git/pullrequests |
| Boards (work items) | https://dev.azure.com/srg-dev/SRG%20Power%20BI/_boards |
Key principlesβ
- Save in PBI Desktop, then commit in VS Code β this is the rhythm
- One commit per ticket β reference the work item ID every time
- Never push directly to
mainβ always use a pull request - Follow the DAX & Semantic Model Standards β see the separate document for measure naming, descriptions, and formatting
- Close PBI Desktop before pulling or merging β avoid file lock conflicts
- When in doubt,
git statusβ it tells you exactly where you are