Blog
Mert Özen's blog posts on software development, backend, frontend, and DevOps.
Looking at Git History: git log, git show, git blame and Aliases
We move around history with git log's --oneline, --graph, --stat, -p, --format, --author, --since, --grep and --follow options and see the difference between -S and -G on a line whose price changed. We inspect a single commit and an old file version with git show, trace lines with git blame -L and shorten long commands with aliases.
Tracking Files with Git: .gitignore, git rm --cached and git mv
We look at .gitignore patterns, exceptions with ! and how git check-ignore -v shows which rule applies. We see why an already committed .env stays tracked despite .gitignore and how git rm --cached untracks it, then cover global excludes, git mv and line endings with .gitattributes.
Inside a Git Commit: Snapshots, Objects and Hashes
We open commit, tree and blob objects with git cat-file to see that a commit is a snapshot, not a diff, compute a hash by hand with shasum and find out why an unchanged file is not stored twice. We cover the rules of a good commit message and why git commit --amend creates a new commit.
Your First Git Repo: git init, the Staging Area and the First Commit
We build the bookstore repo from scratch with git init and take a first look inside the .git folder. We walk through the working tree, staging area and repository with git status, git add and git commit, and learn to read the two columns of status -s and why an MM file commits its staged version.
Installing Git and First Settings: Starting Right with git config
We install Git on macOS, Windows and Linux, check the version and look at the system/global/local config layers, user.name and user.email, why init.defaultBranch main matters, core.editor and core.autocrlf. We compare the git config list/get/set/unset subcommands (2.46+) with the old --get form.
What Are Git and GitHub? An Introduction to Version Control
Why version control exists, how centralized and distributed tools differ and why Git and GitHub are not the same thing. We try distributed work by reading history and committing in a clone whose server is gone, then look at Git's short history, its place in DevOps and how the series is organized.
Dapper.AOT: Dapper with Source Generators, Interceptors and Native AOT
Why does vanilla Dapper blow up even under dotnet run once PublishAot is on? Generating the code at build time with Dapper.AOT 1.1.0: [module: DapperAot], InterceptorsNamespaces, the generated interceptor code, Native AOT publish and a startup time going from 49.8 ms to 4.3 ms, type handlers as attributes, the DAP analyzers and limitations such as QueryMultiple.
Dapper Performance: Query Cache, GetRowParser and Bulk Alternatives with SqlBulkCopy and COPY
What Dapper's query cache holds and why it draws a saw blade, when CommandFlags.NoCache is needed, choosing a type per row with GetRowParser, and for the bulk operations Dapper lacks: a transaction on SQLite, SqlBulkCopy on SQL Server and COPY on PostgreSQL, all with real measurements, closing with BenchmarkDotNet.
Dynamic SQL with Dapper: SqlBuilder, Safe Filters and Contrib, Dommel, Dapper Plus
Building optional filters without string concatenation: Dapper.SqlBuilder's templates and placeholders, count and page queries from one builder, safe sorting with a whitelist and the OrWhere surprise. Then a balanced look at Dapper.Contrib, Rainbow, Dommel and the commercial Dapper Plus as of November 2026.
Dapper and SQL Server: TVPs, ANSI Strings, {=literal} and Parameter Sniffing
Queries that return the right result but scan the whole index: the implicit conversion nvarchar parameters cause on varchar columns, Dapper's 4000-character rule, IN lists multiplying plans, table-valued parameters, filtered indexes with {=literal} and parameter sniffing, all with real logical-reads measurements.
Stored Procedures with Dapper: Output, Return Value and the Auto-Detection Trap of 2.1.11
SQLite has no stored procedures, so we move to SQL Server in docker: CommandType.StoredProcedure, output and return values with DynamicParameters, how SET NOCOUNT affects Execute, single-word commands mistaken for procedures since 2.1.11, and the function vs procedure difference in PostgreSQL.
Type Handlers and Custom Mapping in Dapper: Guid, DateTimeOffset and CustomPropertyTypeMap
Why does Dapper blow up when reading Guid, DateTimeOffset and TimeSpan on SQLite? We write interpreters with SqlMapper.TypeHandler<T>, see from the source why SetValue is never called, and cover the DateOnly/TimeOnly story of 2.1.86 and column mapping with CustomPropertyTypeMap.