Git workflow for C# developers: how to stop fearing merges and start living
Have you ever spent half a day figuring out who and when rewrote your favorite CalculateOrderTotal() method? Or maybe you pushed a "hotfix" straight to master, and then production crashed with a NullReferenceException? Sound familiar? Welcome to the club of C# developers trying to survive Git chaos without a clear workflow.
If you think Git is just git add . and git push, you're missing the point. A proper Git workflow is not a religious debate, but a tool that saves nerves, time, and money. Especially in the world of C#, where we have solutions (.sln), projects (.csproj), and a bunch of NuGet packages that love to conflict.
In this article, I'll show you working branching strategies, real C# code examples, and how to automate the routine. Let's go!
Why do C# projects require a special approach to Git?
When working with C#, you're dealing with more than just text files. You have:
- Solution (.sln) — one file that references all projects. If two developers add a new project simultaneously, a merge conflict is guaranteed.
- .csproj files — XML files that update when adding NuGet packages or changing the Target Framework. Merging these files manually is painful.
- appsettings.json and web.config — configs that often contain connection strings. They shouldn't be committed, but everyone forgets.
- bin/ and obj/ folders — if you commit them once, your colleagues will curse you forever.
Therefore, your Git workflow for C# must be carefully thought out. And it starts with a proper .gitignore.
Step 1: Setting up .gitignore for C#
The standard .gitignore for C# can be downloaded from GitHub/gitignore, but I'll add a few important lines that are often missed:
# Configuration files with secrets (do not commit!)appsettings.Development.jsonappsettings.Local.jsonsecrets.json
# Files generated by ReSharper or Rider*.DotSettings.user*.DotSettings
# NuGet packages (cache)packages/*.nupkg
# Test resultsTestResults/*.trx
# Visual Studio folders.vs/*.suo*.user*.sln.docstatesWhy is this important? Imagine you commit appsettings.Development.json with a local database connection string. A colleague pulls it, and their application crashes because they don't have that database. It seems like a small oversight, but it wastes an hour of work time.
Choosing a branching strategy for your C# team
There are several popular Git workflows. For C# projects, I recommend two main strategies: Git Flow and Trunk-Based Development. Let's break them down with examples.
Option 1: Git Flow (classic for large projects)
Git Flow is like SOLID for Git. It's ideal when you have clear releases (e.g., version 2.1.0, 2.2.0). The branches look like this:
main— only stable code.develop— integration branch.feature/XXX-123-add-new-api— features fromdevelop.release/2.1.0— release preparation.hotfix/2.0.1-critical-bug— urgent fixes frommain.
Example for C#: you're working on a new feature "Calculate discount by promo code".
# Create a feature from developgit checkout developgit pullgit checkout -b feature/ABC-42-add-promo-discount
# Write C# code// DiscountService.cspublic class DiscountService{ public decima