By admin , 29 April 2026

The cost of full clones

A full clone of a long-lived repository can be hundreds of megabytes. On every CI job. Multiplied by every push. The bandwidth and time costs are real, especially in monorepos with five years of history.

Shallow clone basics

git clone --depth=1 https://example.com/repo.git

This fetches only the tip commit and its tree. The local repo is "shallow" - it has no parent commits.

By admin , 29 April 2026

The CI checkout

CI environments check out your code afresh on every job. The choices you make about how they check out determine cache hits, build speed, and security. The defaults are rarely optimal.

Shallow clone for speed

git clone --depth=1 https://example.com/repo.git
# GitHub Actions:
- uses: actions/checkout@v4
  with:
    fetch-depth: 1

A depth-1 clone fetches only the tip commit - sufficient for build-and-test on modest repos. For tools that need history (versioning, blame, changelogs), unshallow on demand:

By admin , 29 April 2026

Tags as release markers

A Git tag is an immutable label on a commit. Annotated tags carry a tagger name, date, and message; lightweight tags are just a name. Use annotated tags for releases - they are first-class objects, GPG-signable, and queryable.

Creating tags

# Annotated
git tag -a v1.4.0 -m "Release 1.4.0"

# Signed (recommended for releases)
git tag -s v1.4.0 -m "Release 1.4.0"

# Lightweight (not recommended for releases)
git tag v1.4.0-alpha

Pushing tags

Tags are not pushed by default:

By admin , 29 April 2026

LFS is not the only answer

Git LFS solves one problem - keeping binaries out of pack files - but introduces others: extra infrastructure, hosting fees, and a soft dependency on a server that must be online for clones. Several alternatives suit different scenarios.

Object storage with manifests

Store the binaries in S3, GCS, or any object store; commit only a manifest with hashes and URLs.

By admin , 29 April 2026

Why Git struggles with large files

Git stores complete object snapshots. When you commit a 100 MB binary, the entire 100 MB is added to the repo. Edit it ten times and your .git directory has a gigabyte of binary content that diff and pack badly. Clones become slow, GitHub rejects pushes over its 100 MB limit, and CI grinds to a halt.

By admin , 29 April 2026

The reflog: your safety net

Git records every movement of HEAD in the reflog - resets, checkouts, rebases, amends, merges. Even commits no branch points to are kept reachable for ~90 days (configurable via gc.reflogExpire). When you "lose" a commit, it is almost always still in the reflog.

By admin , 29 April 2026

Binary search through history

git bisect performs binary search across commits. Tell it a known-good commit and a known-bad commit; it checks out the midpoint and asks you to mark it good or bad. After log2(N) steps, it identifies the first bad commit. For 1024 commits, that is ten checks.

By admin , 29 April 2026

The problem with --force

A plain git push --force overwrites the remote branch with whatever you have locally. If a teammate pushed in between your last fetch and your push, their commits are silently destroyed.

What --force-with-lease does

--force-with-lease only force-pushes if the remote branch is at the SHA you last fetched. If anything new has appeared on the remote, the push is rejected and you can investigate before clobbering work.

By admin , 29 April 2026

The three trees

Git tracks three "trees": the HEAD commit, the index (staging area), and the working tree (your files on disk). git reset moves the branch pointer and optionally rewrites the index and working tree. The flag determines how far down the chain it cascades.

--soft: move HEAD only

git reset --soft <target> moves the branch pointer but leaves the index and working tree untouched. The result: the changes from the abandoned commits appear as staged changes, ready to be re-committed.

By admin , 29 April 2026

What stash is for

git stash shelves your uncommitted changes so you can return your working tree to a clean state, then bring them back later. It is the right tool when you need to switch context briefly - someone needs a hotfix, you want to pull main, you want to test a clean tree - without committing half-baked work.