
A while back I built a tool that could analyze .NET Source Files as well as assemblies, and generate an Object Model Diagram. I found having OMDs of the APIs you're working on is a really great way to quickly get an overview of what an API looks like and how you could work with it. The public API surface should generally not change once you ship it, so getting it right the first time is important. In addition it was able to also compare two assemblies or two sets of source folders and just give you the difference. This allowed you to review just what is getting added, and if there are any breaking changes that might not have been intended. The end goal was to one day have these OMDs in the PR itself. You can get this tool today as a dotnet tool by running the following command:
dotnet tool install --global dotMorten.OmdGenerator
For a long time, I’d wanted one specific feature in my .NET Object Model Diagram Generator: the ability to compare API surface directly between git refs. This would simplify comparing against a PR.
As mentioned, the tool could already compare one set of source files against another, and it could generate a clean object model diff from that. But what I really wanted was something more natural for real-world development: compare the code in my working tree to a tag, compare one branch to another, or compare two commits from a remote repository without having to manually check out folders, create temp copies, or script around the tool.
It was one of those ideas that sat on my “I should really add this someday” list for years.
This time, I finally built it — mostly because with AI agents now it's so much faster to iterate on without having to do all the manual work of actually typing :-) So with GitHub Copilot helping me work through the design, implementation, tests, and documentation, I was able to add the feature much faster than I would have otherwise. Copilot didn’t magically do the work for me, but it absolutely helped me turn a long-delayed idea into a finished feature.
What the new feature does
The generator can now compare source code against a specific commit, branch, or tag.
That means you can now:
- compare your current checkout against a release tag
- compare two branches
- compare two commits
- compare two refs from a remote git repository
The key new arguments are:
- gitRepo — a local repository path or remote git URL
- sourceRef — the git ref for the “new” side of the diff
- compareRef — the git ref for the “old” side of the diff
- source — the source path to analyze
The important detail is that source still tells the tool what part of the repository to analyze. The git ref options tell it which versions of that source to compare.
Comparing API changes between two refs
Here’s the simplest case: compare your current source tree against a tag from the same repository.
generateomd --source=C:\github\dotnet\runtime\src\libraries\System.Text.Json\src --compareRef=v8.0.0 --format=html
That says:
- analyze the current contents of System.Text.Json
- compare them against the v8.0.0 tag
- emit html output (you can use md if you want markdown format)
If you want to compare two refs from a remote repository, you can do that too:
generateomd --source=src/libraries/System.Text.Json/src --gitRepo=https://github.com/dotnet/runtime.git --sourceRef=main --compareRef=v8.0.0 --format=md
In this case:
- gitRepo points to the remote repo
- source is now a repo-relative path
- sourceRef is the “new” side
- compareRef is the baseline
You can also compare two commits directly:
generateomd --source=src/MyLibrary --gitRepo=https://github.com/your-org/your-repo.git --sourceRef=9f4f4cf --compareRef=28630aaf8d27367248358ef064b57d6214b0f103 --format=md
That gives you a markdown API diff suitable for release notes, PR discussions, or review workflows.
Why this is useful
This ends up being much more practical than folder-to-folder comparisons.
Instead of manually exporting source trees or checking out branches into separate directories, you can compare API
shape straight from version control. That makes it far easier to answer questions like:
- What public API changed in this pull request?
- What changed between this release tag and main?
- Did this commit actually alter the public surface area?
- Are these changes additive, breaking, or just refactorings?
For a tool built around API visualization and change tracking, git-based comparison feels like the missing piece.
Using it in GitHub Actions
Once the tool can compare two git refs, the next obvious step is automation.
A really nice workflow is to generate an API diff for every pull request and post it as a PR comment. That gives reviewers a focused view of API changes without having to inspect every file manually.
The flow looks like this:
- trigger on pull_request
- compare the PR head SHA to the PR base SHA
- generate markdown output
- detect whether the markdown actually contains API changes
- create or update a PR comment with the result
At a high level, the command looks like this inside the workflow:
generateomd \
--source=src/MyLibrary \
--gitRepo=${{ github.server_url }}/${{ github.repository }} \
--sourceRef=${{ github.event.pull_request.head.sha }} \
--compareRef=${{ github.event.pull_request.base.sha }} \
--format=md \
--output=api-diff
This is a great fit for pull requests because GitHub already gives you the exact two SHAs you care about:
- github.event.pull_request.head.sha
- github.event.pull_request.base.sha
That means the comment always reflects the actual API delta introduced by the PR.
The important GitHub Action pieces
Here are the parts of the workflow that matter most.
1. Install the tool
- uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- run: dotnet tool install --global dotMorten.OmdGenerator
That makes generateomd available in the workflow.
2. Generate markdown output
- name: Generate API diff
run: |
generateomd \
--source=src/MyLibrary \
--gitRepo=${{ github.server_url }}/${{ github.repository }} \
--sourceRef=${{ github.event.pull_request.head.sha }} \
--compareRef=${{ github.event.pull_request.base.sha }} \
--format=md \
--output=api-diff
The output is written to api-diff.md.
3. Only comment when there’s an actual API diff
The workflow checks whether the generated markdown contains any namespace entries:
- name: Check whether API changes were found
id: api_diff
run: |
if grep -q '^namespace ' api-diff.md; then
echo "has_changes=true" >> "$GITHUB_OUTPUT"
else
echo "has_changes=false" >> "$GITHUB_OUTPUT"
fi
That means the bot won’t spam pull requests when nothing in the API surface actually changed.
Auto-updating the original PR comment
This is the part I especially like.
The workflow uses a hidden marker in the PR comment body, such as:
<!-- dotnet-omd-api-diff -->
When the action runs again, it looks for an existing bot comment containing that marker. If it finds one, it updates that comment instead of creating a new one.
That gives you two nice behaviors:
- if new commits change the API diff, the original comment is refreshed.
- if the API changes are later reverted, the existing comment can be updated to say that no API changes are detected any longer.
That second behavior matters a lot. It means the bot comment tracks the current truth of the PR, not just the first interesting state it saw.
In practice, the logic becomes:
- if there are API changes and no prior comment, create one
- if there are API changes and a prior comment exists, update it
- if there are no API changes but a prior comment exists, update it to say so
- if there are no API changes and no prior comment exists, do nothing
That keeps noise low while still making the automation feel smart and stateful.
Why this workflow is valuable
I like this approach because it adds useful review context without adding much maintenance overhead.
Reviewers get a dedicated summary of API changes. Authors get immediate feedback if they accidentally alter public surface area. And because the comment updates in place, the pull request stays clean instead of filling up with bot spam every time a new commit is pushed.
It also turns the tool from something you run manually into something that can enforce visibility around API changes as part of normal team workflow.
Closing
This git-based diff support makes the Object Model Diagram Generator much more useful in day-to-day development. Comparing API shape between commits, branches, and tags is now built in, and the GitHub Actions integration makes it easy to surface those changes directly in pull requests.
You can see the full github action documented here: https://github.com/dotMorten/DotNetOMDGenerator/tree/main#github-actions-comment-pr-api-changes
And here's an example of one of my repos now using this action to validate PRs: https://github.com/dotMorten/WinUIEx/blob/main/.github/workflows/apidiff.yml