
Most developers don't learn Git by reading the manual. They learn it the first time they delete something important, commit the wrong files, or discover that the branch they thought they were working on was not the branch they were actually working on.
That is not necessarily a bad way to learn. Git becomes much easier once you've seen what these commands are trying to protect you from.
The problem is that Git has accumulated a reputation for being complicated because there are dozens of commands, countless flags, and a lot of terminology that makes perfect sense once you already understand Git. When you're new to it, though, add, commit, push, pull, reset, restore, and revert can look like variations of the same thing.
They aren't.
This guide focuses on the commands you'll actually use in day-to-day development and, more importantly, the situations that tell you which one to reach for. You don't need to memorize every Git flag to be productive. You need a good mental model, a handful of reliable commands, and enough understanding to avoid turning a small mistake into a much bigger one.
The Git Mental Model
Before getting into the commands, it helps to understand the four places your code can exist during normal Git work.
flowchart TD
A[Working Tree]
A -->|git add| B[Staging Area]
B -->|git commit| C[Local Repository]
C -->|git push| D[Remote Repository]Your working tree is where you actually edit files. The staging area is where you decide which of those changes belong in your next commit. A commit records that staged state in your local repository. The remote repository — often hosted on GitHub, GitLab, or another server — is where you share those commits with other people or systems.
Most of the Git commands in this article are really just ways of inspecting, moving, combining, or undoing changes between those states.
Once that clicks, Git stops feeling like a collection of unrelated commands.
1. git init
When you're starting a project from scratch and want Git to track its history, you create a repository with git init.
git initGit creates a hidden .git directory inside the project. That directory is where Git keeps the information it needs to manage the repository: commits, references, configuration, and the other internal data that makes version control possible.
It is worth separating two ideas here because beginners often conflate them. Running git init creates the repository; it does not automatically add every file in the directory to version control. Your files are still untracked until you stage them.
A typical first setup therefore looks like this:
git init
git add .
git commit -m "Initial commit"You can also ask Git to create the project directory for you:
git init my-new-projectYou generally only need to initialize a repository once. When you're not sure whether you're already inside one, git status is a safer check than running git init again.
2. git clone
git clone is the starting point for an existing project. Instead of creating a new repository, you're making a local copy of one that already exists somewhere else.
git clone https://github.com/username/repository.gitA normal clone gives you the project's files and repository history and sets up information about the remote repository so you can fetch and push changes later. It is the command you'll usually run when joining a team, contributing to an open-source project, or moving an existing repository onto a new machine.
Git normally creates a directory based on the repository name, but you can choose your own:
git clone https://github.com/username/repository.git my-projectThere is one practical consideration worth knowing about. A repository with ten years of commits can be considerably larger than the current source code suggests, because you're bringing historical data along with it. When you only need a limited amount of history, a shallow clone can be useful:
git clone --depth 1 https://github.com/username/repository.gitThat gives you a much smaller starting point, although it also means you don't have the complete history available locally.
The simple rule is:
Use init when you're creating the repository. Use clone when the repository already exists.
3. git status
If there is one Git command worth learning so well that you stop thinking about it, it is git status.
git statusWhen you're not sure what Git thinks is happening, ask Git. The command shows your current branch and tells you about changes that are modified, staged, or untracked.
A simplified example might look like this:
On branch main
Changes not staged for commit:
modified: index.html
Untracked files:
style.cssIn this case, index.html is already part of the repository but has local changes that haven't been staged. style.css is a new file that Git isn't tracking yet.
git status is deliberately boring, and that's exactly why it is useful. It doesn't change your files or history. Run it before you commit. Run it when you've just pulled someone else's changes. Run it before a reset. Run it after you've been switching branches and can no longer remember what you were doing.
When Git starts feeling confusing, don't guess. Start with git status.
4. git add
The staging area is one of Git's most useful ideas, and also one of the first things beginners find strange.
Suppose you've changed three files, but only two of those changes belong in the next commit. Git gives you the option to decide exactly what gets recorded.
That's what git add does.
git add filename.txtYou can stage everything below the current directory:
git add .or stage matching files:
git add *.cssThink of staging as preparing a package before you send it. Your working tree can contain many changes, but only the staged changes are going into the next commit.
That distinction becomes particularly useful when you're doing unrelated work in the same project. You don't have to create a giant commit simply because several files happen to be modified at once.
One caution: git add . is convenient, but convenience can hide mistakes. It may stage files you didn't intend to include, which is why .gitignore should be set up early for things such as dependency directories, local environment files, generated output, and operating-system clutter.
5. git commit
A commit is a recorded snapshot of the changes you've staged.
git commit -m "Add user authentication"This is the point where your work becomes part of the repository's history. You can compare it later, inspect it, revert it, or use it as a known point to return to.
That makes commit quality matter more than many beginners realize. A repository filled with messages like changes, fix, and final technically works, but it is much harder to understand months later.
Compare:
Fix bugwith:
Fix null pointer when cart is emptyThe second message tells the next person — often future you — what the commit was actually about.
There is also a shortcut worth knowing:
git commit -am "Fix navbar spacing on mobile"The -a option stages modifications and deletions to files Git already tracks, then creates the commit. It does not include new files, so a brand-new file still needs git add first.
And when the latest commit is almost right but not quite, --amend lets you update it:
git commit --amend -m "Add user authentication and tests"Amending is handy while a commit is still local. Once you've pushed it and other people may have based work on it, rewriting that commit becomes a different conversation.
6. git branch
Branches are how you isolate work without putting unfinished changes directly onto the branch other people may be using.
git branchlists your local branches.
To create one:
git branch new-featureTo delete a branch that has already been safely merged:
git branch -d old-featureAnd when you know you want to remove it regardless of whether Git considers it merged:
git branch -D old-featureYou can rename a branch with:
git branch -m new-nameA useful thing to understand is that a branch isn't a separate copy of your entire project. It is essentially a movable reference to a commit, which is why creating branches is cheap.
There is one beginner mistake worth remembering: git branch new-feature creates the branch, but it does not switch you to it. To create and move to a branch in one step, modern Git gives you:
git switch -c new-featureThat leads naturally to the next command family.
7. git checkout, git switch, and git restore
For years, git checkout handled several unrelated jobs. You could use it to switch branches, create a branch, or restore a file to an earlier state.
git checkout new-feature
git checkout -b another-feature
git checkout -- filename.txtThe command still works, and you will continue to encounter it in older documentation and existing projects. The problem is that one command having several very different meanings makes it harder for beginners to understand what is happening.
Modern Git separates those operations.
Use git switch when you're working with branches:
git switch new-feature
git switch -c another-feature
git switch -The last form switches back to the branch you were previously on, which is surprisingly useful when you're jumping between two tasks.
Use git restore when you're working with files:
git restore filename.txtThat discards unstaged changes to the file.
To remove a file from the staging area without throwing away its edits:
git restore --staged filename.txtSo the simplest mental model is:
git switch → branches
git restore → files
git checkout → older multi-purpose commandYou don't need to treat checkout as obsolete. Just recognize that switch and restore make your intention clearer when you're writing new commands.
8. git merge
Eventually, isolated work needs to come back together. That's what merging does.
For example:
git switch main
git merge new-featureIf Git can reconcile the two histories automatically, the merge may be uneventful. When it can't, you'll get a conflict.
A conflict usually means that both sides changed overlapping parts of a file, or made changes Git cannot safely combine on its own. The repository hasn't been corrupted; Git has simply reached a decision that requires a human.
You'll see something like:
<<<<<<< HEAD
your version
=======
their version
>>>>>>> new-featureOpen the file, decide what the final version should actually be, remove the conflict markers, and then stage the resolved file:
git add filename.txt
git commitThe first few merge conflicts can feel alarming because the file suddenly contains strange markers and Git seems to stop in the middle of the operation. In practice, conflict resolution is a normal part of collaborative development. The important thing is to understand that Git is waiting for a decision, not reporting a fatal failure.
9. git push
Your commits are local until you send them somewhere shared.
git push origin mainThat command pushes your local commits to the main branch on the origin remote.
When you create a new branch and push it for the first time, you will often use:
git push -u origin new-featureThe -u option establishes the upstream relationship between your local branch and the remote branch. After that, a plain:
git pushis usually enough.
Force-pushing is where things get serious.
git push --forcecan replace remote history, including commits you weren't aware another developer had added. There are legitimate reasons to rewrite history, particularly on personal branches after an amend or rebase, but it should never be a reflex.
When a force-push is genuinely necessary, this is generally safer:
git push --force-with-leaseIt checks that the remote branch is still in the state your local repository expects. If someone else has moved it since your last update, the push can be rejected instead of silently overwriting their work.
Even with that protection, shared branches deserve caution.
10. git pull
git pull is the command you use when the remote repository has changes you need to bring into your current branch.
git pull origin mainConceptually, the operation starts by fetching updates from the remote and then integrating the relevant remote branch into your current branch. Exactly how that integration happens depends on your configuration and options.
A common alternative is rebasing:
git pull --rebase origin mainInstead of creating a merge as part of the pull, Git replays your local commits on top of the updated remote history. That can produce a cleaner, more linear history, but rebasing also rewrites the commits being replayed.
Neither workflow is universally right. Some teams merge, some rebase, and some use stricter fast-forward policies. The important thing is consistency: know what your project expects before changing the default behavior.
For example:
git config pull.rebase truecan make rebase the default for the relevant configuration scope.
One subtle point is worth remembering: git pull origin main does not necessarily mean “update my local main branch.” It integrates origin/main into whichever branch you're currently on. That distinction matters when you're working on a feature branch.
11. git log
Every Git repository eventually reaches the point where you need to know not just what the code looks like now, but how it got there.
That's what git log is for.
git logThe default output contains commit metadata as well as the commit message, which can get noisy in a large repository. In practice, you'll often want a more compact view:
git log --onelineOr, when you're trying to understand branching and merges:
git log --oneline --graph --allA few flags cover most everyday needs:
Flag | What it does |
|---|---|
| Compact commit summary |
| Draw an ASCII representation of branches and merges |
| Show the changes introduced by commits |
| Filter commits by author |
| Show only the latest five commits |
The real value of git log isn't the list itself. It is the context. When a piece of code looks strange and nobody remembers why it exists, history can often answer the question faster than another hour of searching through the codebase.
12. git diff
Before you commit a change, you should know what you're actually about to record.
That's the job of git diff.
git diffA simple example:
- <h1>Welcome</h1>
+ <h1>Welcome to My Site</h1>The minus line represents the old content; the plus line represents the new content.
There is a second version you'll use often:
git diff --stagedThat shows what is currently in the staging area and therefore what the next commit is likely to contain.
You can also compare two branch tips directly:
git diff branch1 branch2A five-second diff check before committing is one of the best habits you can build. It catches accidental formatting changes, forgotten debugging statements, edits to the wrong file, and all the tiny mistakes that otherwise end up being discovered during code review.
13. git stash
Stash is useful when your current work is unfinished but your working tree needs to be clean.
Maybe a production issue needs attention. Maybe you need to inspect another branch. Maybe you simply aren't ready to create a commit yet.
You can temporarily put your work aside with:
git stashGit stores the changes and returns the working tree to a clean state.
You can see what you've saved with:
git stash listand bring the most recent stash back with:
git stash popIf you'd rather reapply it without removing the stash entry:
git stash applyAnd when you know you no longer need one:
git stash dropOne detail surprises people: untracked files aren't included in a normal stash. When you need those as well, use:
git stash -uIf you regularly have several stashes in flight, name them:
git stash push -m "search filter work"That small bit of discipline makes a huge difference when you come back to the stash list later and discover that every entry is just another anonymous WIP.
14. git reset and git revert
These two commands are often introduced together because both can be used to undo work. They are not interchangeable, and the difference becomes particularly important once your commits are being shared with other people.
git reset
Reset moves the current branch reference backward.
git reset --soft HEAD~1
git reset --mixed HEAD~1
git reset --hard HEAD~1The three modes mainly differ in what happens to your index and working tree:
Mode | Branch | Staging area | Working tree |
|---|---|---|---|
| Moves | Keeps changes staged | Keeps changes |
| Moves | Unstages changes | Keeps changes |
| Moves | Resets | Resets |
--mixed is the default.
The command that deserves the most respect is:
git reset --hard HEAD~1It can discard changes from tracked files in your working tree. If you haven't checked what Git thinks is going to happen, this is a very bad time to start experimenting.
The other important consideration is whether the commit has already been shared.
If the commit exists only locally, resetting can be perfectly reasonable. If you've already pushed it and other people may have based their work on it, moving the branch backward creates a history rewrite that affects everyone downstream.
git revert
For an already-shared commit, git revert is often the safer choice:
git revert HEADRather than moving the branch backward, it creates a new commit that reverses the earlier change.
The easiest way to remember the distinction is:
reset → move the branch backward
revert → add a new commit that undoes an earlier oneGit also keeps a reflog locally, which can sometimes help recover from mistakes involving commands such as reset. That's useful to know, but it shouldn't be treated as permission to run destructive commands carelessly.
15. git remote
A Git repository can work entirely locally, but most development eventually involves a remote repository somewhere else. git remote lets you inspect and manage those connections.
To see what is configured:
git remote -vTo add a remote:
git remote add origin https://github.com/user/repo.gitTo remove one:
git remote remove originTo rename one:
git remote rename origin upstreamorigin is a convention, not a special Git command or keyword. When you clone a repository, Git normally names the remote origin simply because that is the conventional default.
Fork-based workflows often use another common convention:
origin → your fork
upstream → original repositoryThat arrangement lets you push your own work to origin while fetching updates from the original project through upstream.
Five Habits That Make Git Much Easier
Knowing commands is useful. Knowing when to stop and inspect the repository is even more useful.
Check before you change things
When you're uncertain, start here:
git status
git diffThose two commands answer a remarkable number of “what just happened?” questions without changing anything themselves.
Keep commits focused
Small commits are easier to review and easier to undo. They also make git log useful instead of turning it into a museum of commits named final, final2, and really-final-this-time.
Use .gitignore early
A typical project might ignore things such as:
node_modules/
.env
.DS_Store
dist/Just remember that .gitignore doesn't remove a file that has already been committed. If a credential or API key has already been pushed to a remote repository, assume it has been exposed and rotate it.
Treat force-push as an exception
Before using:
git push --forceask yourself whether anyone else could have based work on the remote branch.
On a private feature branch, rewriting history may be perfectly reasonable. On a shared branch, it can create a mess for everyone.
Practice recovery, not memorization
The fastest way to understand Git is to use it somewhere safe.
Create a throwaway repository. Make a few commits. Create a branch. Modify a file. Stash it. Undo something. Create a merge conflict and resolve it.
Once you've recovered from a mistake in a repository you don't care about, the same commands feel much less intimidating in a repository you do.
Git Commands by Situation
This is the part worth bookmarking because it answers the question beginners actually have: “What do I type now?”
Situation | Reach for |
|---|---|
Starting a project |
|
Getting an existing project |
|
Not sure what's happening |
|
Choosing what belongs in the next commit |
|
Saving a checkpoint |
|
Starting isolated work |
|
Moving between branches |
|
Throwing away a local file change |
|
Combining branch histories |
|
Sharing your commits |
|
Getting remote changes |
|
Investigating history |
|
Inspecting exactly what changed |
|
Temporarily setting work aside |
|
Undoing an unshared commit |
|
Undoing a shared commit |
|
Checking repository connections |
|
Quick-Reference Cheat Sheet
# | Command | What it does |
|---|---|---|
1 |
| Create a repository |
2 |
| Copy an existing repository |
3 |
| Inspect repository state |
4 |
| Stage changes |
5 |
| Record a snapshot |
6 |
| Manage branches |
7 |
| Switch branches or restore files |
8 |
| Combine branch histories |
9 |
| Send commits to a remote |
10 |
| Fetch and integrate remote changes |
11 |
| Inspect commit history |
12 |
| Compare changes |
13 |
| Temporarily store unfinished work |
14 |
| Undo changes in different ways |
15 |
| Manage remote connections |
Where to Go From Here
You don't need to memorize all fifteen commands before you start building things.
For most everyday work, you'll spend a surprising amount of time with just:
git status
git add
git commit
git push
git pullBranching becomes important as soon as you're working on multiple features or collaborating with other developers. stash, reset, and revert become important when something gets messy.
That's actually the better way to learn Git: let the situations teach you the commands instead of trying to memorize the entire manual up front.
And when you do get into trouble, remember the most useful Git habit of all:
Don't guess what Git is doing. Inspect the state first.
git status
git diffGit isn't really about memorizing commands.
It's about knowing where your changes are, understanding what you want to happen next, and choosing the command that gets you there without making the problem worse.
Once that mental model becomes second nature, Git stops feeling mysterious.
It becomes a safety net.
And that's what it was supposed to be all along.
Comments (0)
Login to post a comment.