Azure DevOps pull request shows changes already in the target branch
The Files tab of an Azure Repos pull request does not compare the source branch with the target as it is now. It compares the source with the merge base, the last commit the two branches share. Changes that reached the target some other way, most often a squash merge of an earlier pull request from the same branch, are not in that shared history, so they show again. Completing still changes only what is new. Below: how to see what completing would really change, and how to make the pull request show only that.
What Azure DevOps compares
Microsoft's Merge strategies and squash merge page says the Files tab detects changes by a three-side comparison: the last commit of the target branch, the last commit of the source branch, and their common merge base. The file list is what the source changed since that merge base.
What completing would actually change is on another screen. On the pull request's Overview, open More options (…) at the upper right and choose View merge changes: that diff is of the preview merge commit. If the target branch moved since, Restart merge makes a new preview (Microsoft's Complete, abandon, or revert pull requests).
The same two views on your machine:
git fetch
git diff --stat origin/main...origin/feature
git diff --stat origin/main $(git merge-tree --write-tree origin/main origin/feature | head -1)
The first is the Files tab's list; the second, which needs git 2.38 or later, is what the merge would change. If the first lists more files, the extra ones are already in the target.
A branch that was squash merged before
A squash merge adds one new commit to the target, and that commit has no reference to the source branch, so the merge base stays where it was. Microsoft recommends deleting the source branch after a squash merge "to prevent any confusion"; the Complete pull request pane has Delete <branch> after merging for that. When the branch is kept and gets more commits, the next pull request from it shows the squashed changes again. In our test on November 2 (git 2.53), five files were squash merged and one more was committed: the three-dot diff listed 6 files, the real merge 1.
To fix the branch once, merge the target into it and push. Microsoft gives the same two ways to tie branches to a single common ancestor: rebase your branch on the target, or merge the target into your branch.
git switch feature
git merge origin/main
git push
The target's newest commit then becomes the merge base, and the pull request shows only the new commit (6 files became 1 in the same test). If some of the squashed files changed again on the branch after the squash merge, the merge stops with a conflict on them (in our test, CONFLICT (add/add)). Keep the branch's version of those files: during this merge, git checkout --ours <file> takes the branch's side. Then git add them and git commit. For good, squash merge only branches that are deleted afterwards, and merge long-lived branches with a merge commit (Azure DevOps merge types).
A branch built on a branch that was squash merged
feature-b was started from feature-a. feature-a was squash merged into main, and now the pull request for feature-b shows feature-a's changes too. Changing its target to main does not help, because the merge base is still the old one. Move only feature-b's own commits onto main:
git switch feature-b
git rebase --onto origin/main {last commit of feature-a} feature-b
git push --force-with-lease
The last commit of feature-a is the newest one on the completed pull request's Commits tab. In our test, the diff went from 2 files to 1. The push rewrites the branch, so it needs the Force push (rewrite history, delete branches and tags) permission; without it, Azure DevOps refuses with TF401027.
"Multiple merge bases detected"
When Azure DevOps finds more than one merge base, the pull request says "Multiple merge bases detected. The list of commits displayed might be incomplete." Microsoft lists the usual causes: cross-merges between branches, one branch merged into two others, reverts, and active reuse of feature branches. Its check does not ask whether a candidate base was already merged, so the message can appear even when git merge-base --all origin/main origin/feature prints a single line.
Microsoft treats this as a security matter as much as a display one. Changes that are already in the target show in the Files tab but might not trigger branch policies mapped to folders, such as required reviewers for a path. The fix is the same as above: merge the target into the branch, or rebase it on the target, then check the Files tab again.
Azure DevOps pull requests in IntelliJ IDEA and Rider
Azure DevOps Pull Requests, a plugin by Lesstep for JetBrains IDEs 2024.1 or later and Android Studio, shows a pull request's files in the IDE's diff viewer, compared from the merge base like the Files tab, on Azure DevOps Services and Azure DevOps Server. It can check out the pull request's branch (Checkout), where the commands above run, and after a push it shows what changed since your last review. The review inbox and diffs are free; voting, commenting, completing and creating are Pro (US$1 a month, with a 30-day trial).
Get it on JetBrains Marketplace
Azure DevOps and Azure Repos are trademarks of Microsoft Corporation; IntelliJ IDEA and Rider are trademarks of JetBrains s.r.o. This page is not affiliated with or endorsed by Microsoft or JetBrains.
More: Azure DevOps merge types · what changed since your last review · merge conflicts · 日本語