Git & Team Workflows
Git internals, branching strategies, merge vs rebase, PR etiquette, and commit hygiene
Git Internals (Commit Tree Concept)
Git stores your project as a DAG (Directed Acyclic Graph) of commits. Each commit is a snapshot β not a diff.
o--o--o--o (main) \ o--o (feature-branch)Object Types
Git stores everything as objects in .git/objects/:
| Object | What it stores |
|---|---|
| blob | File contents |
| tree | Directory listing (points to blobs + other trees) |
| commit | Snapshot metadata (tree + parent + author + message) |
| tag | Named reference to a commit |
# Inspect a commitgit cat-file -p HEAD
# Inspect the tree at HEADgit cat-file -p HEAD^{tree}
# See what objects existgit fsck
# See where HEAD pointscat .git/HEADcat .git/refs/heads/mainWhat git commit Actually Does
- Hashes all changed files β creates blob objects
- Creates tree objects (directories)
- Creates a commit object pointing to the root tree + parent commit
- Moves
HEAD(and current branch ref) to new commit SHA
Branching Strategies
Trunk-Based Development
Everyone commits to main (the βtrunkβ) frequently β multiple times per day. Feature flags control whatβs exposed to users.
main: o--o--o--o--o--o--o (continuous commits) βfeature flagsβPros: Fast integration, no long-lived branches, mirrors CI/CD best practices Cons: Requires feature flags, discipline, and good test coverage Used by: Google, Facebook, Netflix
Feature Branch Workflow (GitFlow-lite)
Developers work on branches, merge via PRs, branches are short-lived.
main: o-----------o-----------o βPR βPRfeature1: o---ofeature2: o---o---oPros: Isolated work, code review via PRs, easy rollback Cons: Integration hell if branches are long-lived Used by: Most teams
GitFlow (Full)
Has main, develop, feature/*, release/*, hotfix/* branches.
Verdict: Overcomplicated for most teams. Use trunk-based or simple feature branches instead.
Merge vs Rebase (When & Why)
git merge
Creates a merge commit β a commit with two parents. Preserves full history.
Before:main: A--B--Cfeature: D--E
After git merge:main: A--B--C--M (M = merge commit) /feature: D--Egit checkout maingit merge feature-branch
# Merge without fast-forward (always creates merge commit)git merge --no-ff feature-branchUse merge when: You want to preserve the exact history of when branches diverged and merged.
git rebase
Moves your commits to apply after the target branch. Creates a linear history.
Before:main: A--B--Cfeature: D--E
After git rebase main:main: A--B--Cfeature: D'--E' (new commits, same changes)git checkout feature-branchgit rebase main
# Interactive rebase β rewrite historygit rebase -i HEAD~3 # rewrite last 3 commitsUse rebase when: You want a clean, linear history. Common pattern: rebase locally before opening a PR.
Merge vs Rebase Decision
| Situation | Use |
|---|---|
| Merging a feature PR to main | Merge (preserve PR history) |
| Keeping feature branch up-to-date with main | Rebase (cleaner) |
| Fixing up commits before PR | Interactive rebase |
| Public/shared branch | NEVER rebase (rewrites history) |
Golden rule: Never rebase commits that have been pushed to a shared branch.
Conflict Resolution
# When a merge/rebase has conflicts:# 1. Git marks conflicted filesgit status # shows "both modified: file.txt"
# 2. Edit conflicted files β look for markers:# <<<<<<< HEAD# your version# =======# their version# >>>>>>> feature-branch
# 3. Mark resolvedgit add file.txt
# 4. Complete the merge/rebasegit merge --continue# orgit rebase --continue
# To abort and go back to beforegit merge --abortgit rebase --abortGitHub / GitLab / Bitbucket
Key Concepts
| Concept | Description |
|---|---|
| Fork | Your own copy of someone elseβs repo |
| Pull Request (PR) | Request to merge your branch into another |
| Merge Request (MR) | GitLabβs name for PR |
| Issue | Tracked bug/feature request |
| Protected branch | Branch that requires PR + review to merge |
| CI/CD checks | Automated tests that must pass before merge |
# GitHub CLI β work with PRs from terminalgh pr create --title "Add logging" --body "Adds structured logging"gh pr listgh pr view 42gh pr merge 42 --squash
# Clone via HTTPS or SSHgit clone https://github.com/user/repo.gitgit clone git@github.com:user/repo.gitPR Review Etiquette
As the Author
- Keep PRs small and focused (< 400 lines when possible)
- Write a clear description: what changed and why
- Add screenshots for UI changes
- Self-review before requesting review
- Respond to all comments before merging
- Donβt leave reviewer guessing β explain non-obvious choices
As the Reviewer
- Review within agreed SLA (usually same day or next day)
- Distinguish blocking issues from suggestions:
nit:= cosmetic, non-blockingsuggestion:= nice to have- (no prefix) = blocking
- Ask questions rather than demanding changes: βWhat do you think aboutβ¦?β vs βChange thisβ
- Approve when ready β donβt leave PRs in limbo
Commit Hygiene
Good Commit Messages
# Format: <type>(<scope>): <short description>## Types: feat, fix, docs, style, refactor, test, chore, ci# Scope: optional, e.g. (auth), (api), (deploy)
feat(auth): add JWT token refresh endpoint
Add POST /auth/refresh that accepts a valid refresh tokenand returns a new access + refresh token pair.
Closes #142Conventional Commits standard:
| Type | When to use |
|---|---|
feat | New feature |
fix | Bug fix |
docs | Documentation only |
refactor | Code change without new feature or fix |
test | Adding or fixing tests |
chore | Build process, dependency updates |
ci | CI/CD pipeline changes |
perf | Performance improvement |
Useful Git Commands for Clean History
# Amend last commit (message or files) β only if not pushedgit commit --amend
# Squash last N commits interactivelygit rebase -i HEAD~3
# In the interactive editor, change "pick" to:# s / squash = combine with previous commit# f / fixup = combine but discard this commit message# r / reword = keep commit but edit message# d / drop = delete this commit
# Unstage a filegit restore --staged file.txt
# Discard changes in working directorygit restore file.txt
# Move changes to stashgit stashgit stash popgit stash listTagging & Releases
# Create a lightweight taggit tag v1.2.3
# Create an annotated tag (with message β use this for releases)git tag -a v1.2.3 -m "Release version 1.2.3"
# Push tags to remotegit push origin v1.2.3git push origin --tags # push all tags
# List tagsgit tag -lgit tag -l "v1.*"
# Delete taggit tag -d v1.2.3git push origin --delete v1.2.3Semantic versioning (semver): MAJOR.MINOR.PATCH
- MAJOR: breaking changes
- MINOR: new features, backward compatible
- PATCH: bug fixes, backward compatible
README Writing (Engineering Communication)
A good README is the first thing a new team member reads. Treat it like documentation that will be read at 2am when things are broken.
Structure
# Project Name
One sentence describing what this does.
## Quick Start
The minimum steps to get it running locally.
## Prerequisites
What you need installed before starting.
## Configuration
Environment variables and their meanings.
## Deployment
How to deploy to different environments.
## Architecture
High-level diagram or description of how components connect.
## Troubleshooting
Common problems and their solutions.What Makes a README Useful
- Copy-paste commands that actually work β test them
- Explain WHY not just WHAT β future readers need context
- Keep it updated β stale docs are worse than no docs
- Link to deeper docs β donβt bloat the README, link out
- Include the error messages β helps people find the troubleshooting section via search