Git & GitHub

Git Stash, Tags & Advanced Tips

3 min read
Focus: GIT

TL;DR — Quick Summary

  • git stash temporarily shelves uncommitted work; git stash pop restores it.
  • Annotated tags (git tag -a) mark releases permanently — they must be pushed separately with git push origin --tags.
  • git bisect does a binary search through history to find the commit that introduced a bug.
  • git blame shows line-by-line authorship; git log -S finds when a string was added/removed.

Lesson Overview

🗃️ Git Stash — Shelve Work Temporarily

You're in the middle of coding a feature when your manager asks you to urgently fix a different bug. Your work isn't ready to commit, but you need a clean working directory. Git stash is the solution — it saves your uncommitted changes to a temporary stack and cleans your working directory.

🏷️ Git Tags — Marking Milestones

Tags are like branches that never move. They permanently mark specific commits — most commonly used for release versions. Unlike branches, tags don't advance when you commit. Once a tag points to a commit, it always will.

  • Lightweight tags: Just a pointer to a commit. git tag v1.0.0
  • Annotated tags: A full Git object with message, author, and date. git tag -a v1.0.0 -m "First stable release"

🔍 Git Bisect — Find the Commit That Broke Everything

You have a bug in production but don't know which of the 50 recent commits introduced it. git bisect uses binary search to find the culprit commit — checking the midpoint each time and narrowing down in O(log n) steps.

🔎 Searching Git History

Git's history is a powerful database of your entire project's evolution. You can search it in many ways:

  • Search commit messages: git log --grep="payment"
  • Search code changes: git log -S "functionName" (pickaxe)
  • See who edited each line: git blame filename

Conceptual Deep Dive

Think of stash as a clipboard for your code changes. You copy your messy in-progress work to the clipboard (git stash), get your working directory clean, do your urgent task, commit it, and then paste your in-progress work back (git stash pop). You can even have multiple items on your stash stack. Git tags are like sticky notes permanently glued to specific commits. v1.0.0, v2.3.1 — these tags will always point to exactly those commits, even years later. This is how you give users download links to specific releases.

Implementation Lab

Git Stash — Full Workflow
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# You're mid-feature with unsaved work
git status
# modified: src/feature.js
# modified: src/styles.css
 
# Stash everything (staged + unstaged)
git stash
# Saved working directory and index state WIP on main: ...
 
# With a descriptive name (recommended for multiple stashes)
git stash push -m "WIP: adding notification system"
 
# Your working directory is clean — now fix the urgent bug
git checkout -b hotfix/login-crash
# ... fix, commit, merge ...
 
# Return to your feature and restore stashed changes
git checkout feature/notifications
git stash pop  # apply the most recent stash AND remove it from stack
# Or apply without removing from stack (reusable):
git stash apply
 
# See all stashed work
git stash list
# stash@{0}: On feature/notifications: WIP: adding notification system
# stash@{1}: On main: WIP: started dashboard redesign
 
# Apply a specific stash
git stash pop stash@{1}
 
# Delete a stash without applying it
git stash drop stash@{0}
 
# Stash including untracked (new) files
git stash push -u -m "Include new files too"new files too"
Tagging Releases
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Create a lightweight tag at current commit
git tag v1.0.0
 
# Create an annotated tag with a message (preferred for releases)
git tag -a v1.0.0 -m "First stable release — adds user auth and dashboard"
 
# Tag a specific past commit by hash
git tag -a v0.9.0 9f3a2c1 -m "Beta release"
 
# See all tags
git tag
# v0.9.0
# v1.0.0
 
# See details of an annotated tag
git show v1.0.0
 
# Push tags to GitHub (not pushed by default!)
git push origin v1.0.0        # push one tag
git push origin --tags        # push ALL tags
 
# Delete a local tag
git tag -d v0.9.0
# Delete a remote tag
git push origin :refs/tags/v0.9.0
Git Bisect — Binary Search for Bugs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# A bug exists in the current HEAD but worked at some point
# Use bisect to find the exact culprit commit
 
# Start bisect
git bisect start
 
# Mark the current commit as 'bad' (has the bug)
git bisect bad
 
# Mark a known good commit (e.g., last week's release tag)
git bisect good v1.2.0
 
# Git checks out a commit in the middle
# TEST the bug:
# - If bug exists: git bisect bad
# - If bug doesn't exist: git bisect good
git bisect bad
# Git checks out another midpoint...
git bisect good
# After ~6-7 steps Git reports:
# 'a1b2c3d is the first bad commit'
# commit a1b2c3d Author: Dev <dev@co.com>
# Date: Mon Jan 6 ...
# Refactor user session handling
 
# Now you know which commit caused the bug!
 
# End bisect session (returns to original HEAD)
git bisect reset
Searching Git History
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Search commit messages for a keyword
git log --grep="payment" --oneline
 
# Find which commits added or removed a specific string in code
git log -S "getUserById" --oneline
# This is the 'pickaxe' search — incredibly useful for tracking down when a function appeared
 
# See who last edited each line of a file (blame)
git blame src/auth.js
# Shows: hash | author | date | line content
 
# Blame a specific line range
git blame -L 20,40 src/auth.js
 
# Search all commits that changed a specific file
git log --follow --oneline src/auth.js
 
# Find deleted files
git log --all --full-history -- "**/deletedFile.js"

Pro Tips — Senior Dev Insights

1

Automate bisect: git bisect run ./test.sh — Git runs your script on each commit and marks it good/bad based on exit code. Fully hands-free.

2

Use git log -S "functionName" -p to find exactly when a function was added and see the full diff of that change — fantastic for debugging.

3

Create a GitHub Release (on top of a tag) to attach binaries, changelogs, and release notes — much more professional than a raw tag.

4

git shortlog -sn gives a contributor leaderboard showing commit counts per author — fun for team retrospectives.

Common Developer Pitfalls

!

Accumulating many stashes without descriptive names and forgetting what each one contains.

!

Forgetting to push tags to remote — your team won't see them and GitHub won't show them in releases.

!
Using git stash apply instead of git stash pop — apply keeps the stash in the stack, causing duplicate stashes over time.
!
Not using git bisect reset after finding a bug — you'll be left in a detached HEAD state.

Interview Mastery

git stash temporarily saves your uncommitted changes (both staged and unstaged) onto a stack and restores your working directory to a clean state. Use it when: (1) you need to urgently switch branches or contexts without committing half-done work, (2) you want to pull the latest changes but have local modifications that would conflict, (3) you want to test something on a clean state. git stash pop restores the most recent stash.

A lightweight tag (git tag v1.0) is just a pointer to a commit — like a branch that never moves. An annotated tag (git tag -a v1.0 -m "msg") is a full Git object stored in the database with its own SHA, including the tagger's name, email, date, and a message. Annotated tags are preferred for releases because they capture who tagged it and when.

git bisect performs a binary search through your commit history to find the specific commit that introduced a bug. You tell it a 'bad' commit (has the bug) and a 'good' commit (doesn't have it). Git checks out the midpoint commit between them and asks you to test. Based on your answer (good/bad), it halves the search space again. After O(log n) steps, it identifies the exact commit that introduced the regression.

Real-World Blueprint

A senior developer notices a performance regression in the dashboard — it was fast 2 weeks ago but now loads slowly. They run git bisect start, mark HEAD as bad and the release from 2 weeks ago as good. After 7 rounds of testing, bisect pinpoints a commit from 5 days ago: 'Refactor data fetching — remove caching layer.' Mystery solved in 15 minutes instead of hours of manual code archaeology.

Hands-on Lab Exercises

1

Modify 3 files without committing, stash them with a descriptive message, make an unrelated commit, then restore the stash.

2

Create 3 annotated tags on different commits and push them to GitHub. View them on the GitHub releases page.

3
Set up a repo with 10 commits where one commit introduces a bug (a specific string in a file). Practice using git bisect to find it.
4
Use git blame on a file in any public GitHub repo to find who last edited a specific function.

Real-World Practice Scenarios

You have unfinished work on a feature branch and need to quickly review a colleague's PR (which requires checking out their branch). What do you do with your WIP?

You need to give a user a download link for version 1.4.2 of your software, which was released 3 months ago. How do tags help?

A bug was reported that worked 3 weeks ago. There are 80 commits since then. How would you use Git tooling to find the culprit commit efficiently?

A colleague says 'Who wrote this terrible code?' about a function. Show them the Git way to answer that question professionally.