Home/Blog/Git for Developers: The Commands, Workflows, and Mental Models You Actually Need
CareerPlacementsInterview

Git for Developers: The Commands, Workflows, and Mental Models You Actually Need

Git is the one tool every developer uses daily but most students learn only superficially. This guide teaches Git properly β€” from how commits actually work to branch strategies, resolving merge conflicts, and the commands that save you in real situations.

S
SCS TeamΒ·17 February 2026Β·10 min read

The Git knowledge gap is real: most CS students know git add, git commit, git push β€” and nothing else. Then they join a team, encounter a merge conflict or a broken branch, and freeze.

This guide closes that gap. By the end, you'll understand Git's mental model (not just the commands), handle merge conflicts confidently, and use the commands that actually matter at work.


The Mental Model That Makes Everything Click

Git doesn't store differences between files. It stores snapshots.

Every commit is a complete snapshot of your project at that moment, plus a pointer to the parent commit(s). This makes Git operations fast and branching cheap β€” a branch is just a pointer to a commit.

A ← B ← C ← D  (main)
              ↑
              E ← F  (feature-branch)

main points to D. feature-branch points to F. Both branches share commits A, B, C, D β€” no duplication, just pointers.

The three areas:

Working Directory β†’ [git add] β†’ Staging Area β†’ [git commit] β†’ Repository
  • Working directory: Your actual files (what you see in the folder)
  • Staging area (index): Files you've added, ready to commit
  • Repository: Committed history, stored in .git/

The Commands You'll Use Every Day

# Configure once
git config --global user.name "Your Name"
git config --global user.email "you@email.com"

# Start a project
git init                    # new repo
git clone <url>             # copy existing repo

# Daily workflow
git status                  # what's changed?
git diff                    # what exactly changed? (unstaged)
git diff --staged           # what's staged?
git add <file>              # stage a file
git add .                   # stage everything
git commit -m "message"     # commit staged changes
git push origin main        # push to remote
git pull                    # fetch + merge remote changes

# Branching
git branch                  # list branches
git branch feature-login    # create branch
git checkout feature-login  # switch to branch
git checkout -b feature-login  # create + switch in one step
git switch feature-login    # modern alternative to checkout
git merge feature-login     # merge into current branch
git branch -d feature-login # delete merged branch
git branch -D feature-login # force delete (not merged)

Commits That Actually Help Your Team

A good commit message makes code review easier, helps you understand changes 6 months later, and makes git log useful.

Bad:

fix bug
update stuff
wip
changes

Good:

Fix null pointer in user authentication when email is not set

When a user signs up via Google OAuth, the email field can be null
if the user has hidden it from Google. This caused a NPE in the
profile creation flow.

Fixes #247

Format: Imperative present tense for the first line ("Fix null pointer" not "Fixed null pointer"). Keep first line under 72 characters. Leave a blank line before the body. Explain why, not what (the diff shows what).

Conventional commits (used at many companies):

feat: add user profile picture upload
fix: resolve null email crash on Google OAuth
docs: update API authentication guide
refactor: extract email validation into utility function
test: add coverage for empty cart checkout
chore: upgrade Next.js to 14.2.3

Branching Strategies

Git Flow

For teams with scheduled releases:

main           β€” production code only
develop        β€” integration branch
feature/*      β€” one branch per feature
release/*      β€” release preparation
hotfix/*       β€” emergency production fixes
git checkout -b feature/user-login develop   # branch from develop
# ... work ...
git checkout develop
git merge feature/user-login                 # merge back to develop

Trunk-Based Development

For fast-moving teams with CI/CD:

main  β€” everyone integrates here frequently (multiple times per day)

Feature flags hide incomplete features in production. Short-lived branches (1-2 days max) merge directly to main. Requires strong CI/CD and test coverage.

Most modern product companies use trunk-based development.


Resolving Merge Conflicts

Conflicts happen when two branches modify the same lines. Don't fear them β€” understand them.

git merge feature-login
# Auto-merging auth.py
# CONFLICT (content): Merge conflict in auth.py
# Automatic merge failed; fix conflicts and then commit the result.

The conflict markers:

<<<<<<< HEAD
def authenticate(email, password):
    if not email:
        raise ValueError("Email required")
=======
def authenticate(email, password):
    if not email or not password:
        raise ValueError("Email and password required")
>>>>>>> feature-login
  • <<<<<<< HEAD to ======= β€” your current branch's version
  • ======= to >>>>>>> β€” the incoming branch's version

Resolution: Edit the file to keep what you want. Remove ALL conflict markers. Then:

git add auth.py
git commit -m "Merge feature-login: combine email+password validation"

Recommended tool: Use git mergetool or your editor's built-in merge tool (VS Code shows both versions side by side with Accept/Reject buttons).


The Commands That Save You

Undo a staged file (before commit)

git restore --staged auth.py   # unstage, keep changes
# Old syntax:
git reset HEAD auth.py

Undo local changes (before staging)

git restore auth.py   # discard changes, restore from last commit
# Old syntax:
git checkout -- auth.py

Undo last commit (keep changes staged)

git reset --soft HEAD~1   # un-commit, keep files staged

Undo last commit (keep changes unstaged)

git reset --mixed HEAD~1  # default: un-commit, keep files changed but unstaged

Undo last commit and discard changes

git reset --hard HEAD~1   # DANGEROUS: permanently deletes changes

Fix last commit message

git commit --amend -m "Correct message"
# Only if not pushed yet!

Temporarily save work without committing

git stash           # save current changes to a stash
git stash list      # see all stashes
git stash pop       # restore most recent stash
git stash pop stash@{1}  # restore specific stash
git stash drop      # delete most recent stash

Use stash when: You need to switch branches but aren't ready to commit.


Rebase vs Merge

Both integrate changes from one branch into another. Different results:

Merge: Creates a new "merge commit" that combines both histories.

A - B - C - D (main)
     \   /
      E - F  (feature, after merge)

Rebase: Moves the feature branch commits on top of main, rewriting history.

A - B - C - D - E' - F' (feature, after rebase)

E' and F' are new commits with the same changes as E and F but different hashes.

When to use which:

  • merge for integrating complete features into main (preserves history)
  • rebase for incorporating upstream changes into your feature branch (clean linear history)
  • Never rebase commits that are on shared branches β€” rewriting shared history causes chaos
# Update your feature branch with latest main (clean approach)
git checkout feature-login
git rebase main
# If conflicts: fix them, git add, git rebase --continue

# Interactive rebase: clean up commits before merging
git rebase -i HEAD~3  # opens editor to squash/reword/drop last 3 commits

Tags and Releases

git tag v1.0.0                        # lightweight tag
git tag -a v1.0.0 -m "Version 1.0.0"  # annotated tag (recommended)
git push origin v1.0.0                 # push tag to remote
git push origin --tags                 # push all tags
git tag                                # list all tags

The .gitignore File

Every project needs one. Never commit:

# Python
__pycache__/
*.pyc
.env
venv/

# Node.js
node_modules/
.next/
dist/

# Editors
.vscode/
.idea/
*.swp

# OS
.DS_Store
Thumbs.db

# Secrets (most important!)
.env
.env.local
*.pem
*secret*

Use gitignore.io to generate language-specific .gitignore files.


What Interviewers Ask About Git

"How do you handle merge conflicts?"

"I run git status to see conflicting files, open them in VS Code which highlights the conflict regions, decide which version to keep (or combine both), remove the markers, git add the resolved files, and git commit. For complex conflicts I use git mergetool or look at both sides' history with git log to understand the intent."

"What's the difference between rebase and merge?"

"Both integrate changes. Merge creates a merge commit preserving branch history. Rebase moves my commits on top of the target branch, creating a linear history. I use merge for integrating features into main, and rebase to update my feature branch with upstream changes before making a PR. I avoid rebasing shared branches."

"How do you undo a commit that's already been pushed?"

"I use git revert, which creates a new commit that undoes the changes β€” this is safe on shared branches because it doesn't rewrite history. git reset --hard would work but requires force-pushing, which can cause problems for teammates who've already pulled."

Build the habit: From your next project, use a proper branching workflow. Create a feature branch for each feature, write meaningful commit messages, and practice resolving a deliberate merge conflict. This is more impressive on a resume than yet another certification.

Ready to practice what you just learned?

Apply these concepts with AI-powered tools built for CS students.