Quick answer: Stamp every crash report with the build version and git commit it came from. When a crash appears, you can see which build introduced it and which commit shipped in that build, turning a mystery into a bisect. The crash data points you at the change that caused it instead of leaving you to guess across your whole history.

A crash report without a version is half a clue. You know the game broke, but not which of the last fifty commits did it, so you are reduced to reading code and guessing. The fix is to connect crashes to your version control: stamp each report with the build it came from and the git commit that build was made from. Then a fresh crash is not an open-ended mystery but a bounded question, which commits shipped in the build where this first appeared. This post covers how to wire crash reports to your git history so regressions point back to the change that caused them.

The question every crash should answer

When a new crash shows up, the most valuable thing you can know is what changed. Software does not usually break on its own; a crash that was not happening last week is almost always the result of a recent change. If your crash reports do not carry a build or commit reference, you cannot ask that question of the data, so you fall back to reading the stack trace cold and reasoning about which of many recent changes could be responsible. That is slow and error-prone, especially under launch pressure.

Attaching a version to every report flips the dynamic. Now the crash itself tells you when it started, and when it started tells you which changes are suspects. A crash that first appears in build forty-two narrows your search to whatever landed between build forty-one and forty-two, which on an indie cadence might be a handful of commits rather than your entire history. The crash data has done the hard part of localizing the bug in time, leaving you only to localize it in code.

Stamping builds with a commit hash

The mechanism is straightforward: at build time, bake the current git commit hash and a build version into the artifact, and have your crash reporter include both with every report. Most build pipelines can inject these values as constants, whether through a generated source file, an environment variable, or build flags. The key is that the running game knows precisely which commit it was built from, so when it crashes, that identity travels with the report instead of being lost the moment the binary leaves your machine.

Be disciplined about uniqueness. Every shipped build should map to exactly one commit, which means building from a clean, committed tree rather than from a working directory with uncommitted changes. If you ship a build made from dirty local edits, the commit hash lies, and your crash-to-commit link becomes untrustworthy at the worst possible moment. Tag releases, build from those tags, and the commit stamped into each crash report will point at a real, reproducible state of your codebase that you can check out and inspect.

Turning a regression into a bisect

Once crashes carry commit identity, a regression becomes a bisect problem, which is the most tractable kind of debugging there is. You know the crash was absent in one build and present in a later one, so the offending change lies somewhere between their two commits. On a small indie project that range is often short enough to read directly, and even when it is long, git bisect can binary-search it for you, halving the suspect set with each test. Either way you converge on the guilty commit fast.

The leverage here is enormous compared to debugging blind. Without version data you might spend a day forming and discarding theories; with it you spend an hour walking a short list of commits and usually spot the culprit by inspection. The crash report has reframed the problem from where in all my code is this bug into which of these few changes introduced it. That reframing is the whole point of connecting crashes to commits, and it is most valuable exactly when you are most stressed, right after a release.

Grouping crashes across versions

Version data also sharpens how you read a crash over time. The same stack trace might span several builds, and seeing the per-version breakdown tells you a real story: did this crash start in a specific build, has a fix in a later build actually reduced it, is it confined to one platform's build. Grouping identical crashes while preserving which versions they appeared in turns your crash list into a timeline of stability, not just a snapshot of the current moment, which is what you need to confirm fixes.

This matters because verifying a fix is as important as making one. After you patch the offending commit and ship a new build, you want to watch the crash's occurrences in that new version drop to zero while older versions still show it. If the new build keeps producing the crash, your fix missed and you need to revisit the commit. Without per-version crash data you are guessing whether the patch worked; with it you have direct evidence tied to the exact build that should have contained the fix.

Setting it up with Bugnet

Bugnet captures crashes with stack traces and device context, and you can include the build version and git commit as captured attributes on every report, so each crash arrives stamped with the exact source state it came from. Identical crashes are grouped into one issue with an occurrence count, while the per-version breakdown shows which builds the crash appeared in. That combination lets you open a crash, read the commit it first showed up in, and walk a short range of changes in your dashboard instead of bisecting your entire history by hand.

Because the commit travels as a plain attribute, nothing stops you from linking it straight back to your repository. Paste the hash into your git host and you land on the exact diff that build shipped, so the path from a crash on someone else's machine to the line that caused it is a couple of clicks. After you patch and release, the per-version view confirms the fix: the new build's occurrences fall to zero while the older builds still carry the crash. The same grouped issue that localized the bug in time now proves your fix landed, all without you ever leaving the captured report.

Making it a release habit

The payoff only materializes if the version stamping is automatic and consistent. Bake commit and build identity into every release as a fixed step in your build pipeline, never something you remember to do by hand, because the one build you forget to stamp is the one that produces the crash you most need to trace. Tagging releases and building from tags makes this reliable, and it costs nothing once it is wired into the pipeline. Consistency is what makes the crash-to-commit link trustworthy in a crisis.

Once it is a habit, debugging regressions stops feeling like detective work and starts feeling like lookups. A crash arrives, you read its first-seen build, you check the handful of commits in that build, and you usually have a suspect within minutes. That speed compounds across a game's life, because every regression you ship over the years becomes cheaper to trace. Connecting crash reports to your git commits is a small upfront investment that quietly pays back every single time something breaks after a release.

A crash without a version is half a clue. Stamp every build with its commit and a regression becomes a short bisect instead of a guessing game.