Git is the distributed version control system used by virtually every software team in the world. Created by Linus Torvalds in 2005 to manage the Linux kernel, it became the universal standard for tracking code changes, coordinating teams, and shipping software safely. In 2026, understanding Git is not optional — it is the baseline expectation for every developer role.
What changed in 2026
- GitHub Copilot and AI tools integrate with Git. AI assistants now summarize diffs, suggest commit messages, and explain merge conflicts in plain language. The underlying Git concepts remain the same; the tooling got friendlier.
- Trunk-based development is the dominant workflow. Long-lived feature branches are rarer in high-performing teams; short-lived branches merged daily into
main with feature flags are the norm.
- Merge queues are standard on GitHub and GitLab. Protected branches with merge queues serialize merges and re-run CI against the merged result, eliminating the "passed CI but broke main" problem.
- Git 2.45+ improvements. Sparse checkout, partial clone, and
git maintenance became polished features. Large monorepos use sparse checkout to check out only the directories they need.
The mental model: three areas
Working Directory → Staging Area → Repository
(your file edits) (git add) (git commit)
↕ push / pull
Remote Repository
(GitHub, GitLab)
Working directory: your files as they exist on disk. You edit them freely.
Staging area (index): a holding area where you select which changes to include in the next commit. This is what makes Git powerful — you can commit part of your changes, not all of them.
Repository (.git directory): the full history of committed snapshots.
Core commands you use every day
# Check what changed
git status # what is staged, what is not, untracked files
git diff # unstaged changes
git diff --staged # staged changes (what will be in next commit)
# Record changes
git add file.py # stage a specific file
git add -p # interactively stage hunks (not whole files)
git commit -m "add user creation endpoint"
# Undo things
git restore file.py # discard unstaged changes to a file
git restore --staged file.py # unstage (keep changes in working dir)
# Branching
git branch feature/login # create branch
git switch feature/login # switch to it
git switch -c feature/login # create AND switch (common shortcut)
# Merging / rebasing
git merge feature/login # merge into current branch
git rebase main # replay your branch on top of main
# Remote operations
git push origin feature/login
git pull # fetch + merge (or fetch + rebase if configured)
git fetch # download remote changes without merging
How commits work
A commit is a snapshot — not a diff. Git stores the complete state of every tracked file at the moment of commit. When you ask for a diff between two commits, Git computes it by comparing snapshots.
git log --oneline # one line per commit
# output:
# a3f91c2 add password reset endpoint
# 88d3e01 add user creation endpoint
# 4f2b110 initial commit
git show a3f91c2 # show the commit: message + diff from parent
git log --graph --oneline # ASCII graph of branch topology
Each commit contains: a pointer to the snapshot (tree), a pointer to its parent commit(s), the author, the committer, the timestamp, and the commit message. The commit hash (SHA-1) is computed from all of this — change any part, and you get a different hash.
Branching and merging
# Common branch workflow
git switch main
git pull # get latest
git switch -c feature/payment # new branch from main
# ... make changes, add, commit ...
git switch main
git pull # get any changes that landed while you worked
git merge feature/payment # merge your branch
# or via pull request (more common):
git push origin feature/payment # push branch to remote
# open PR on GitHub → review → merge → delete branch
Merge vs rebase:
| Method |
Result |
When to use |
git merge |
Preserves branch history with a merge commit |
Shared branches, PRs |
git rebase |
Replays commits on top of target; linear history |
Local cleanup before PR |
git squash merge |
Combines all PR commits into one |
Keeping main history clean |
How to pick a branching strategy
| Strategy |
Best for |
Characteristics |
| Trunk-based development |
High-deployment-frequency teams |
Short branches, feature flags, CI on main |
| GitHub flow |
Most teams |
One main branch, feature branches, PRs |
| GitFlow |
Versioned releases, long support cycles |
develop, main, release, hotfix branches |
Most teams doing continuous delivery use GitHub Flow or trunk-based development. GitFlow is appropriate for software with explicit versioned releases (libraries, mobile apps with app store delays).
The four things that go wrong and how to fix them
1. Committed to the wrong branch:
git log --oneline -3 # note the commit hash
git switch correct-branch
git cherry-pick <hash> # apply that commit to this branch
git switch wrong-branch
git reset HEAD~1 # remove last commit from wrong branch (keeps changes)
2. Merge conflict:
<<<<<<< HEAD
return calculate_v1(amount)
=======
return calculate_v2(amount, currency)
>>>>>>> feature/currency
Edit the file to keep what you want, remove the markers, then git add and git commit.
3. Accidentally staged a file with secrets:
git restore --staged .env # unstage it
echo ".env" >> .gitignore # make sure it is ignored going forward
4. Need to undo the last commit (not yet pushed):
git reset HEAD~1 # undo commit, keep changes staged
git reset HEAD~1 --soft # same
git reset HEAD~1 --mixed # undo commit, unstage changes (default)
Common mistakes
Writing uninformative commit messages. "fix bug" or "wip" tells future-you and your team nothing. Write a message that explains what changed and why.
Giant commits. A commit with 40 changed files across 5 features is hard to review and impossible to bisect when something breaks. Commit small, focused changes.
Committing secrets. Once a secret is in Git history, it is compromised — even if you delete the file in the next commit. Use .gitignore, environment variables, and a secret scanner in CI.
Merging instead of rebasing in local cleanup. Before opening a PR, rebase your branch on main so the reviewer sees a clean linear history. Save merges for the actual integration.
What to skip
- Force-pushing to main or shared branches — this rewrites history and causes confusion for everyone who has pulled the branch.
- Long-lived feature branches — the longer a branch lives, the worse the merge conflict. Aim to merge within 1–2 days.
- Storing large binary files in Git — use Git LFS (Large File Storage) for large assets. Plain Git handles binary files poorly.
FAQ
What is the difference between git pull and git fetch?
git fetch downloads remote changes to your local copy of the remote branch but does not modify your working directory or local branches. git pull is git fetch followed by git merge (or git rebase if configured). Use git fetch when you want to see what changed before deciding what to do.
What is HEAD?
HEAD is a pointer to your current position in the repository — usually the latest commit on your current branch. When you switch branches, HEAD moves. "Detached HEAD" means HEAD points to a specific commit, not a branch.
What is the difference between git reset and git revert?
git reset moves the branch pointer backward, rewriting history. git revert creates a new commit that undoes a previous commit, preserving history. Use git revert on shared branches; use git reset only on local, unpushed commits.
How do I find who changed a specific line?
git blame <file> annotates each line with the commit and author that last changed it. git log -S "search string" --oneline finds commits that added or removed a specific string.
Where to go next