Git

Git - Medium

Page 1 of 2

The Staging Area, Deeper

At the basic tier, staging looks like a simple stop on the way to a commit. In practice it's a precision tool, and the command that makes that obvious is git diff, which has two distinct modes people confuse constantly.

git diff                # working directory vs staging area (unstaged changes)
git diff --staged       # staging area vs last commit (what your NEXT commit will contain)

If you stage a file and then keep editing it, those two diffs show different things - git diff shows only what you typed after staging, git diff --staged shows only what's locked in for the commit. Running the wrong one is a common source of "wait, why isn't this in my commit?" confusion.

You can also stage part of a file instead of all of it:

git add -p file.txt     # interactively choose which chunks ("hunks") to stage

This is genuinely useful for keeping commits focused - you fixed a bug and also cleaned up an unrelated comment in the same file, and you want those to be two separate, reviewable commits.


Branching and Merging in Practice

Real branching workflows involve more moving pieces than the basic tier shows. A typical loop:

git switch -c fix-timeout origin/main   # branch off the latest main
# ... work, commit ...
git switch main
git pull                                 # make sure local main is current
git merge fix-timeout
git branch -d fix-timeout                # delete the now-merged branch

git branch -d refuses to delete a branch with unmerged commits, which is a deliberate safety net - Git is telling you "this work isn't reachable from anywhere else yet, are you sure?" -D (capital) forces it anyway, and is worth treating as a small warning sign whenever you type it.


Resolving Merge Conflicts Step by Step

A happens when Git can't automatically combine changes - typically because two branches edited the same lines of the same file differently. Git doesn't guess; it stops and asks you to decide.

git merge feature-x
# CONFLICT (content): Merge conflict in config.yaml

Open the file and you'll see conflict markers:

<<<<<<< HEAD
timeout: 30
=======
timeout: 60
>>>>>>> feature-x

Everything between <<<<<<< HEAD and ======= is your current branch's version; everything between ======= and >>>>>>> is the incoming branch's version. You edit the file to what it should say, delete the markers, then:

git add config.yaml    # mark the conflict as resolved
git commit              # completes the merge

The most common beginner mistake here is leaving the <<<<<<< markers in the committed file by accident - always re-read the file before staging it.


Rebasing vs Merging

Rebasing is an alternative to merging for combining branches, and it produces a different-shaped history for the same underlying work.

git switch feature-x
git rebase main       # replay feature-x's commits on top of the latest main

Merging preserves exactly what happened, including a merge commit showing two lines of history joining. Rebasing rewrites history to look as if you'd branched off the latest main and worked in a straight line the whole time - no merge commit, a cleaner linear log.

The tradeoff: rebasing rewrites commit hashes. That's harmless on a branch only you're using, but rewriting history on a branch other people have already pulled causes their local copy and the rewritten remote copy to diverge painfully. The rule of thumb: rebase freely on private branches, avoid rebasing anything already shared/pushed and being used by others.


Stashing Work

Sometimes you're mid-change and need to switch branches immediately - a production bug just landed - but you're not ready to commit. Stashing shelves your uncommitted changes so your working directory goes back to clean.

git stash              # save uncommitted changes, revert working directory to last commit
git switch main
# ... fix the urgent thing ...
git switch feature-x
git stash pop           # reapply the stashed changes and remove them from the stash

git stash list shows you everything stashed (you can stash multiple times); git stash pop applies and removes the most recent one, while git stash apply applies without removing - useful if you want to apply the same stash to more than one branch.


Cherry-Picking

Cherry-picking takes one specific commit from another branch and applies it to your current branch, without merging everything else that branch has.

git cherry-pick a3f9c2d

The classic use case: a critical fix lands on main, and you need that exact fix on a release-2.1 branch too, without pulling in unrelated main changes. Cherry-picking creates a new commit with the same changes but a different hash - it's a copy, not a move, so the original commit still exists wherever it was.


Undoing Things Properly

reset and revert both undo work, but they work completely differently, and picking wrong matters.

git reset --soft HEAD~1    # undo the last commit, keep changes staged
git reset --mixed HEAD~1   # undo the last commit, keep changes unstaged (default mode)
git reset --hard HEAD~1    # undo the last commit, DISCARD the changes entirely

reset moves your branch pointer backward and rewrites history - fine for commits you haven't pushed. --hard is the one to be careful with: it throws away work with no confirmation.

git revert a3f9c2d          # create a NEW commit that undoes commit a3f9c2d

revert doesn't rewrite history at all - it adds a new commit that cancels out an old one. That makes it the correct choice for undoing something that's already been pushed and shared, since it doesn't disturb commits other people may have already pulled.


    Welcome to OpsQuiz!

    Real scenario-based DevOps questions, hands-on practice, and clear explanations for every answer.