Home
DevOps & Cloud Engineering / Lesson 5 — Git — Beyond Add, Commit, Push

Git — Beyond Add, Commit, Push

Branches, rebases, conflict resolution, and the workflows real teams use.


The Mental Model

Git is a distributed version control system. The single most important thing to internalize: Git tracks SNAPSHOTS, not diffs.

When you commit, Git takes a complete snapshot of every tracked file. Each commit has:
• A unique SHA-1 hash that identifies it
• A pointer to its parent commit (or commits, for merges)
• A snapshot of the entire working tree
• An author, committer, timestamp, and message

A repository's history is a directed graph of commits. Branches are just named pointers to specific commits. HEAD is a pointer to your current location in this graph.

Text
                       (main)
                          ↓
   A ──── B ──── C ──── D
                  \
                   E ──── F
                          ↑
                      (feature)

When you switch branches, Git just moves a pointer and updates files. When you merge, Git creates a new commit with two parents. When you rebase, Git rewrites history by replaying commits on a different base.

This model is why Git operations are fast — most are just pointer manipulation. And it's why understanding the model makes the commands click.

We assume basic familiarity (git add, git commit, git push). This lesson covers what makes someone genuinely effective with Git.


Branching Strategies

Creating a branch is essentially free:

Bash
git checkout -b feature-login     # create AND switch
git switch -c feature-login       # newer alternative
git branch -d feature-login       # delete (after merge)
git branch -D feature-login       # force delete (lose unmerged work)

The two dominant workflows in 2026:

GitHub Flow (recommended for most teams):

Text
1. main is always deployable
2. Create a branch off main for any new work
3. Open a Pull Request when ready
4. Discuss / review / iterate
5. Merge to main → automatically deploy

Trunk-Based Development (high-throughput teams):

Text
1. main is the only long-lived branch
2. Feature branches are short — under 1 day
3. Multiple deploys per day from main
4. Feature flags hide incomplete features in production

Avoid Git Flow (with develop, release/, hotfix/ branches) unless you have scheduled releases. It's complexity that modern continuous-deploy teams don't need.

For this series, focus on GitHub Flow. It's what most cloud-native teams use.


Merging vs Rebasing

When your feature branch is done and main has moved on, you need to integrate them. Two ways:

Merge:

Bash
git checkout main
git merge feature-login

Creates a merge commit with two parents. Preserves history exactly as it happened.

Rebase:

Bash
git checkout feature-login
git rebase main

Replays your commits ON TOP OF main, creating new commits with new SHAs. Linear history.

Text
   Before:                          After rebase:
   A — B — C — D    (main)          A — B — C — D    (main)
        \                                        \
         E — F      (feature)                     E' — F'  (feature)

The Golden Rule: NEVER rebase a branch that others have based work on. Rebasing rewrites commits with new SHAs. If someone else's commits depend on the old SHAs, their history breaks.

Safe: your local feature branch.
Unsafe: a shared develop branch.

A clean workflow combining both:

Bash
# Keep your feature branch up to date with main
git checkout feature-login
git fetch origin
git rebase origin/main          # safe — only your branch is rewritten

# When done, merge to main via PR
# (use "Squash and merge" in GitHub for a clean main history)

"Squash and merge" combines all feature-branch commits into one clean commit on main. Best of both worlds: clean main history, full review history in the PR.


Resolving Conflicts

Conflicts happen when Git can't automatically combine changes — both branches changed the same lines.

Bash
$ git merge feature-login
CONFLICT (content): Merge conflict in src/auth.js

When you open the file:

Text
<<<<<<< HEAD
const TIMEOUT = 5000;
=======
const TIMEOUT = 10000;
>>>>>>> feature-login

Edit to resolve — keep what you want, delete the markers. Then:

Bash
git add src/auth.js              # mark resolved
git status                        # check for more
git commit                        # complete the merge
git merge --abort                 # or abort the whole thing

Tips for fewer conflicts:
• Merge / rebase main into your branch frequently
• Keep PRs small (smaller diffs = less chance of conflict)
• Communicate when refactoring shared files
• Use feature flags for incomplete work, avoiding long-lived branches


Daily Commands That Pay Off

Inspecting:

Bash
git log --oneline                # one line per commit
git log --graph --oneline --all  # visual graph of all branches
git log -p src/auth.js           # patches that touched this file
git blame src/auth.js            # who wrote each line
git diff main..feature           # what's different between branches

Undoing:

Bash
git restore file.txt             # undo unstaged changes
git restore --staged file.txt    # unstage (keep changes)
git reset --soft HEAD~1          # undo last commit, keep changes staged
git reset --hard HEAD~1          # undo last commit, discard changes (DANGER)
git commit --amend               # add to your previous commit (don't push if shared!)

Stashing — temporarily set aside changes:

Bash
git stash                        # save uncommitted work
git stash pop                    # restore it
git stash list                   # see all stashes

Pushing safely:

Bash
git push                         # current branch
git push --force-with-lease      # safer than --force (refuses if remote moved)

Cherry-pick — copy a single commit from elsewhere:

Bash
git cherry-pick abc123           # apply that commit to current branch

Useful for porting a fix from main to a release branch.

Bisect — find which commit introduced a bug:

Bash
git bisect start
git bisect bad                   # current state is broken
git bisect good v1.0             # this version was fine
# Git checks out a midpoint; test it and mark good or bad
# Repeat until Git pinpoints the offending commit
git bisect reset

Reflog — your safety net:

Bash
git reflog                       # every HEAD movement of the last 90 days
git reset --hard HEAD@{2}        # back to where HEAD was 2 movements ago

Even commits you "lost" via bad reset/rebase are usually still in the reflog. This has saved more careers than I can count.


Pull Requests

A Pull Request is a code review workflow on top of Git. The author proposes a change, reviewers comment, the author iterates, and eventually it merges.

What makes a good PR:
• Small — fewer than ~400 lines of diff. Big PRs get cursory reviews.
• Single-purpose — one logical change. Don't mix refactoring with features.
• Descriptive title — what changed. "Fix login redirect on mobile" not "fix bug".
• Body explains WHY — link to the issue, describe the approach, note alternatives.
• Tests included.
• CI green before requesting review.

What reviewers check:
• Does it solve the problem correctly?
• Will it break anything in production?
• Is the code readable in 6 months?
• Edge cases or security issues?
• Tests adequate?

Review automation that helps:
• CI runs tests automatically — failed tests = no merge
• Required reviewers (CODEOWNERS file maps paths to required reviewers)
• Branch protection — main can't be pushed to directly, only via merged PRs
• Required status checks before merge


Secrets & .gitignore

Some files should never be committed: build artifacts, dependencies, secrets, logs, IDE configs.

Standard .gitignore:

Text
# Dependencies
node_modules/
__pycache__/
*.pyc

# Build output
dist/
build/

# Logs
*.log

# OS / Editor
.DS_Store
.idea/
.vscode/

# Secrets — NEVER commit these
.env
.env.local
*.pem
*.key

GitHub publishes useful templates per language at github.com/github/gitignore.

Critical: secrets in Git are forever

If you commit a secret and push it, the commit is in the history of every clone. Tools like git-secrets, truffleHog, and gitleaks scan for them.

Recovery if you committed a secret:
1. ROTATE THE SECRET IMMEDIATELY — the value is compromised
2. Use git filter-repo or BFG Repo-Cleaner to remove from history
3. Force-push and notify everyone with a clone to re-clone

Better: prevent it. The pre-commit framework runs scanners before each commit:

YAML
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.5.0
    hooks:
      - id: trailing-whitespace
      - id: check-merge-conflict
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.0
    hooks:
      - id: gitleaks

Cheap insurance.

The next lesson covers the workflow that ties Git into deployment: continuous integration. Once you have Git discipline AND CI, the rest of DevOps automation falls into place.


⁂ Back to all modules