Git and Version Control for People Who Have Avoided It
The mental model first, then the eight commands you actually use daily, and how to undo things without fear.
Git is intimidating because it is usually taught as a list of thirty commands. It is actually one idea plus a handful of commands you will use daily. The one idea is this: your project is a series of complete snapshots, each one a commit, and Git is extremely good at telling you the difference between two of them.
Once that clicks, most of what looks like Git folklore — why we commit so much, why branches are cheap, why nobody merges with pull in a hurry — follows naturally.
The four places
| Area | What lives there | The command that moves things in |
|---|---|---|
| Working directory | The files as they are on disk right now | Your editor |
| Staging area | The changes you have chosen to include in the next commit | git add |
| Local repository | The full history of commits | git commit |
| Remote repository | The shared history, usually on a hosting service | git push, git fetch, git pull |
The staging area is the part people find pointless, and it exists for a good reason: it lets you commit a coherent unit of change. You have edited four unrelated files; staging two of them gives you a commit that says what it means, and leaves the other two for a different commit. Committing everything at once is how you end up with a history nobody can read.
The commands you will use daily
-
git status
Always run this first. It shows what is modified, what is staged, and what branch you are on. It is the only command with no risk of losing work.
-
git add
Stage specific files, or use git add . to stage everything in the current directory. Review the staged diff before committing; it is the last moment you can catch a stray file.
-
git commit
Record the staged changes with a message. Write the message in the imperative — "fix the null check in the parser" — because it reads correctly when someone greps the log.
-
git diff
What have I actually changed. Plain git diff shows unstaged changes; git diff --staged shows what is about to be committed.
-
git log --oneline --graph
The history, compactly, with branch structure visible. Add --all to see every branch.
-
git switch
Move between branches, creating one if it does not exist. It replaced git checkout for this purpose, which is a genuine improvement, because it means the name of a branch is not a special file.
-
git pull
Fetch and merge in one step. Configure it to use a rebase and the history stays readable: git config --global pull.rebase true.
-
git push
Send your commits to the remote. Set upstream once with git push -u origin main.
Branches are free
In a traditional version control system a branch was a copy, expensive enough that you created a few. In Git a branch is a movable label pointing at a commit, so creating one costs nothing. That is the actual reason for the branching workflow: a branch per feature, a branch per fix, and a merge when it is done.
The daily loop is short: create a branch, commit small pieces of work, push it, open a pull request, get it reviewed, merge, delete it. Small pieces of work with messages that explain them is a history that remains useful in a year, and it is also why Git works well for writing: you can see how the document changed and why.
Undoing things
This is the part people avoid, and the honest answer is that most mistakes in Git are recoverable for longer than you think. Nothing you do with the commands below destroys committed work.
| I want to | Command | Note |
|---|---|---|
| Discard changes to a file | git restore <file> | Permanent. The content is gone |
| Unstage a file | git restore --staged <file> | Safe. The change is still in the file |
| Amend the last commit | git commit --amend | Only safe before you have pushed |
| See what a commit did | git show <sha> | The most useful command in Git and the least used |
| Go back but keep the changes | git reset --soft <sha> | Turns commits into staged changes |
| Go back and discard commits | git reset --hard <sha> | Dangerous. Commits after this point are unreachable |
| Recover a lost commit | git reflog, then git reset --hard <sha> | Why "unreachable" is not the same as "gone" |
| Undo a pushed commit | git revert <sha> | Creates a new commit that undoes it. The safe choice on shared branches |
Good commit messages
A commit message has a subject line under about fifty characters, then a blank line, then a body explaining why. The subject says what changed; the body says why, which is the part that cannot be reconstructed from the diff six months later. The test is whether the message makes sense to somebody who did not write it and cannot ask you.
Committing small and often is worth more than committing perfectly. A history of thirty well-described commits is navigable. A history of six commits that each contain six unrelated changes is not, whatever the messages say.
Common questions
Frequently asked questions
Do I need Git if I am not a developer?
Very possibly. It is the best tool available for anything that evolves over time and where you care about history: a website, a spreadsheet, a writing project, a folder of design files, a set of automation scripts. It gives you the ability to see what changed, when, and to go back to any previous state. That last property is what makes it worth learning for a non-technical project.
What is the difference between Git and GitHub?
Git is the version control program, which runs entirely on your machine. GitHub, GitLab and Bitbucket are hosting services that store your repository remotely and add collaboration features. You can use Git with no remote at all, and for a private personal project that is a perfectly reasonable choice.
Should I commit large binary files?
Generally not to a normal repository. Git stores every version of every file, so a large binary that changes often makes the repository grow quickly and slowly. Large media, or anything that changes on every run, belongs in object storage or a file store, with a pointer in the repository.
How do I undo the last commit but keep the changes?
git reset --soft HEAD~1 moves the branch back one commit and leaves everything staged. --mixed, the default, unstages them instead. Neither touches your files, and both only affect commits that have not been pushed.
Where to go next
These are the services referenced above. Compare them on your own terms before you sign up for anything.