If you've used Git for a while, you probably know the basics:
git add
git commit
git pull
git push
These commands are enough to get started.
But as your projects become bigger and you start working with branches, pull requests, conflicts, and other developers, Git starts getting a little more interesting.
Here are 10 Git commands and techniques that I found especially useful for moving beyond the basics.
1. git merge
Let's say you have a feature branch and the main branch has received some new changes.
You can bring those changes into your current branch using:
git merge main
A simplified history might look like:
A---B---C main
\
D---E feature
After merging:
A---B---C------M
\ /
D---E---
M is the merge commit.
When is merge useful?
Use merge when you want to preserve the branch history and clearly show that two lines of development were combined.
2. git rebase
Rebase takes a different approach.
Instead of creating a merge commit, Git moves your commits and reapplies them on top of another branch.
git rebase main
The previous history:
A---B---C main
\
D---E feature
can become:
A---B---C---D'---E'
The result is often easier to read.
But there's an important thing to remember:
Rebase rewrites commit history.
So a simple rule is:
Rebase your own local work carefully, especially before sharing it with others.
If you're rebasing a shared branch, you can create unnecessary problems for teammates.
3. git commit --amend
Ever made a commit and immediately realized you forgot one small change?
Instead of creating another unnecessary commit, you can add the change to the previous commit.
git add forgotten-file.js
git commit --amend --no-edit
The --no-edit option keeps your previous commit message.
If you also want to change the message:
git commit --amend
Important
Amending creates a new commit, which means the commit hash changes.
So this is safest when the commit hasn't already been shared with other people.
4. git push --force-with-lease
Sometimes you rewrite your local history using:
- rebase
- amend
- interactive rebase
- squash
Then a normal push may be rejected.
You might be tempted to use:
git push --force
But there's a safer option:
git push --force-with-lease
The difference is important.
--force basically says:
Replace the remote branch with my version.
--force-with-lease checks whether the remote branch has changed before replacing it.
That makes it a much safer choice when you actually need to force-push.
5. git rebase -i
This is one of my favorite Git commands for cleaning up commits.
git rebase -i HEAD~5
This lets you interact with your last five commits.
You'll see something similar to:
pick a111111 Add login page
pick b222222 Add validation
pick c333333 Fix typo
pick d444444 Fix validation
pick e555555 Remove debug code
You can change pick to commands such as:
pick
Keep the commit as it is.
reword
Change only the commit message.
edit
Pause at that commit so you can modify it.
squash
Combine the commit with the previous one while keeping its commit message available for editing.
fixup
Combine the commit with the previous one and discard its commit message.
drop
Remove the commit.
For example:
pick a111111 Add login page
fixup b222222 Fix typo
fixup c333333 Remove debug code
can turn several messy commits into a much cleaner history.
6. git stash
Imagine you're working on a feature and suddenly need to switch to another task.
Your current code isn't ready to commit yet.
That's where git stash helps.
git stash
Git temporarily stores your uncommitted changes and gives you a clean working directory.
You can then switch branches:
git switch main
Later, you can bring your changes back.
Include untracked files
If you've also created new files:
git stash -u
You can even give your stash a useful name:
git stash push -u -m "WIP authentication feature"
To see your stashes:
git stash list
To restore one:
git stash apply stash@{0}
Or:
git stash pop
The simple difference:
apply = restore the changes
pop = restore + remove the stash
7. git cherry-pick
Sometimes you don't need an entire branch.
You only need one specific commit.
That's where cherry-pick comes in:
git cherry-pick <commit-hash>
For example:
git cherry-pick a1b2c3d
Git takes the changes from that commit and applies them to your current branch.
This is useful when:
- A bug fix exists on another branch
- You need one specific feature
- You don't want to merge the entire branch
One important detail:
Cherry-pick creates a new commit with a different hash.
8. git reset
git reset is useful when you want to move your branch backwards.
There are three common versions:
git reset --soft HEAD~1
git reset --mixed HEAD~1
git reset --hard HEAD~1
The easiest way to remember them:
--soft → keep changes staged
--mixed → keep changes unstaged
--hard → discard the changes
For example:
Soft reset
git reset --soft HEAD~1
Removes the last commit but keeps its changes staged.
Mixed reset
git reset HEAD~1
Removes the last commit and keeps the changes in your working directory.
Hard reset
git reset --hard HEAD~1
Moves the branch backwards and removes the changes from the working tree.
⚠️ Be careful with --hard.
Make sure you understand what you're about to discard before using it.
9. git reflog
This command can be extremely useful when you think you've lost a commit.
git reflog
While git log shows your reachable commit history, reflog records movements of references such as HEAD.
You might see:
e35fa12 HEAD@{0}: reset: moving to HEAD~2
821cd77 HEAD@{1}: commit: Add authentication
f992ab1 HEAD@{2}: commit: Add login page
That previously "lost" commit may still be accessible.
Instead of immediately resetting everything again, you can create a recovery branch:
git branch rescue 821cd77
Then inspect it safely.
Remember
reflog isn't a magic backup.
If your work was never committed or otherwise stored by Git, reflog cannot recover it.
10. git revert
Here's an important situation.
You pushed a commit to a shared branch, and that commit introduced a problem.
Should you rewrite the branch history?
Usually, no.
Instead, use:
git revert <commit-hash>
Suppose your history is:
A---B---C
And C introduced the problem.
Running:
git revert C
creates another commit:
A---B---C---D
D contains the opposite changes needed to undo C.
The original commit remains visible in history.
That's why revert is generally a better choice for undoing changes on shared branches.
Quick Cheat Sheet
| Command | Main purpose |
|---|---|
git merge |
Combine branch histories |
git rebase |
Reapply commits on top of another branch |
git commit --amend |
Modify the previous commit |
git push --force-with-lease |
Safer force push |
git rebase -i |
Clean up recent commits |
git stash |
Temporarily save unfinished changes |
git cherry-pick |
Apply one specific commit |
git reset |
Move branch history backwards |
git reflog |
Find previous HEAD/reference states |
git revert |
Safely undo a shared commit |
Final Thoughts
Git becomes much easier once you stop thinking of it as just a collection of commands and start understanding what happens to your repository history after each command.
If you're currently learning Git, I'd recommend becoming comfortable with these first:
git stash
git merge
git rebase
git revert
git reset
git reflog
Then gradually add:
git rebase -i
git cherry-pick
git commit --amend
git push --force-with-lease
The more you understand Git's history, the less scary those inevitable conflicts and mistakes become.
Which Git command has saved you the most time? 👇
Top comments (1)
Which Git command has saved you the most time? 👇