Development

Git Commands: The Ones That Actually Matter in Everyday Development

Git has hundreds of options, but everyday version control relies on a smaller set of commands. Learn how repositories, commits, branches, remotes, rebasing, conflicts, and common Git workflows fit together.

Git Commands: The Ones That Actually Matter in Everyday Development

I didn’t find Git particularly difficult when I first started using it.

I found it difficult when something went wrong.

git add, git commit, and git push were easy enough. Then I committed something to the wrong branch. A merge conflict appeared. Git told me my local branch had diverged from the remote, and suddenly every command felt capable of deleting six hours of work.

Git becomes easier once the commands stop feeling like isolated instructions.

Most Git commands are really ways of moving changes between a few places: your working files, staging area, local repository, branches, and remote repositories.

Once that model clicks, Git becomes much less mysterious.

CommandWhat it does
git statusShows changed, staged, and untracked files
git add <file>Stages a file
git commit -m "message"Creates a commit
git diffShows unstaged changes
git diff --stagedShows staged changes
git switch -c <branch>Creates and switches to a branch
git merge <branch>Merges another branch into the current branch
git fetchDownloads remote changes without integrating them
git pullDownloads and integrates remote changes
git pushSends local commits to a remote
git restore <file>Restores working-tree changes
git revert <commit>Creates a commit reversing another commit
git stashTemporarily stores unfinished changes
git log --onelineShows compact commit history

Git Starts With a Repository

A Git repository is simply a project whose changes Git is tracking.

For a new project:

git init

turns the current directory into a Git repository.

For an existing remote project:

git clone <repository-url>

creates a local copy.

Before doing much else, Git also needs to know who is creating commits.

git config --global user.name "Your Name"
git config --global user.email "you@example.com"

You can inspect your configuration with:

git config --list

Now Git has a repository and an identity.

The interesting part begins when files start changing.

Staging Is the Step That Makes Git Make Sense

Suppose I modify three files but only want two of them in my next commit.

Running:

git status

shows what has changed.

Then:

git add app.js
git add tests.js

moves those changes into the staging area.

Or:

git add .

stages everything under the current directory.

This staging area is one of Git’s most useful ideas.

It lets you decide what belongs in the next snapshot rather than automatically committing every modification you have made.

You can inspect what is staged with:

git diff --staged

Then create the commit:

git commit -m "Add validation for customer form"

A commit isn’t simply “saving the project.”

It is creating a recorded point in the repository’s history.

Git Log, Diff, Show, and Blame Commands

Once a project has dozens or thousands of commits, history becomes incredibly useful.

The basic command is:

git log

For something more compact:

git log --oneline

And for a visual representation of branches:

git log --oneline --graph --all

History answers questions that eventually appear in every software project.

Who changed this?

When did it change?

What did this commit introduce?

Git can go deeper with commands such as:

git show <commit>

and:

git blame <file>

Despite its slightly aggressive name, git blame is really about tracing individual lines back through history.

The value of version control isn’t simply keeping old files.

It is preserving how the software became what it is.

Branches Give Changes Somewhere Safe to Develop

Most teams don’t want every unfinished change happening directly on the main branch.

That’s where branches help.

Create a branch:

git branch feature/login

Switch to it:

git switch feature/login

Or create and switch in one command:

git switch -c feature/login

Older Git workflows often use:

git checkout -b feature/login

The idea is the same.

A branch gives a line of development its own place to evolve.

You can experiment, commit several changes, and eventually bring the finished work back into the main line.

Merging Brings Work Back Together

Suppose the login feature is complete.

Switch back:

git switch main

Then merge:

git merge feature/login

Git combines the histories where it can.

If both branches changed unrelated areas, this can happen automatically.

If both changed the same part of the same file, Git may not know which version should win.

Now you have a merge conflict.

This sounds worse than it usually is.

A conflict simply means Git has reached a decision it cannot safely make for you.

The developer needs to examine both versions, decide what the final code should look like, remove the conflict markers, stage the corrected file, and complete the merge.

Git isn’t broken.

It is asking for human judgment.

Remotes Connect Your Repository to Everyone Else

So far, everything can happen locally.

A remote connects your local repository to another Git repository, commonly one hosted on a shared platform.

View configured remotes:

git remote -v

Add one:

git remote add origin <repository-url>

This is where three commands that are often confused become important:

fetch, pull, and push.

They all interact with remote repositories, but they do different things.

Git Fetch, Pull, and Push Commands

git fetch retrieves information and changes from a remote repository without automatically integrating them into your current branch.

git fetch origin

It is essentially:

“Show me what has changed remotely.”

git pull goes further.

git pull

It retrieves remote changes and integrates them into your current work.

Think:

fetch + integrate.

Then there is:

git push

which sends your local commits to the remote repository.

The direction is useful to remember:

fetch/pull → remote to local

push → local to remote

Much of everyday collaborative Git is simply keeping those two histories synchronized.

Git Commands for Undoing Changes

This is where Git starts making people nervous.

“I need to undo this.”

The first question should be:

Where is the change currently sitting?

If you changed a file but haven’t staged it, you might restore it:

git restore file.txt

If you staged something accidentally:

git restore --staged file.txt

If a commit needs to be undone without rewriting shared history:

git revert <commit>

creates another commit that reverses its effect.

Then there is git reset.

Reset is powerful because it can move branch history itself.

For example:

git reset --soft HEAD~1

moves back one commit while preserving the changes for recommitting.

A hard reset:

git reset --hard HEAD~1

also discards changes.

That is why memorizing “undo commands” is risky.

The safer habit is understanding whether the change is unstaged, staged, committed locally, or already shared with other people.

Stashing Handles the Awkward Work in Between

Sometimes you are halfway through something when another task appears.

Your work isn’t ready for a commit, but you need a clean working directory.

Git stash gives you a temporary shelf.

git stash

Your unfinished changes disappear from the working directory.

Do the other work.

Then bring them back:

git stash pop

You can see stored stashes with:

git stash list

Stashing is useful, but I wouldn’t treat it as permanent storage.

If work matters and represents a meaningful state, a commit usually tells a better story.

Tags Give Important Commits Names

Branches keep moving as new commits arrive.

Sometimes we want a permanent label for one particular point.

That is what tags provide.

For example:

git tag v2.0.0

can mark the commit representing version 2.0.0.

Annotated tags can contain additional information:

git tag -a v2.0.0 -m "Version 2.0 release"

Tags are particularly useful around releases because they turn an otherwise forgettable commit hash into a recognizable milestone.

Rebasing Rewrites the Route

Rebasing is one of those Git features that sounds intimidating until you understand what it is trying to achieve.

Imagine your feature branch started from main.

Other developers then added several commits to main.

Your history now has two lines.

Running something like:

git rebase main

takes the commits from your branch and reapplies them on top of the newer main.

The resulting history can be cleaner and more linear.

But there is an important consequence.

Rebasing rewrites commit history.

That makes it useful for cleaning up local work, but potentially disruptive when rewriting commits other people already depend on.

A useful rule is simple:

Be cautious about rebasing shared history.

Merge Conflicts Are About Competing Intentions

Conflicts can appear during both merges and rebases.

Suppose one developer changes:

timeout = 30

to:

timeout = 60

while another changes it to:

timeout = 15

Git cannot know whether the correct answer is 15, 60, or something else.

Only the developers understand the intention behind those changes.

That is why resolving conflicts isn’t really a Git skill.

It is a software-understanding skill.

The commands can tell you where the conflict happened.

They cannot decide what the application should do.

Git Commands Eventually Become a Workflow

Once the individual commands make sense, Git becomes less about remembering syntax and more about choosing a version control workflow.

A small team might use short-lived feature branches:

branch → commit → push → review → merge

Another team might work very close to the main branch with frequent small integrations.

Larger organizations may have release branches, protected branches, pull requests, automated tests, and deployment rules.

The exact workflow matters less than the principles behind it.

Changes should be understandable.

History should be useful.

Integration should happen frequently enough to avoid enormous conflicts.

Shared branches should be treated carefully.

And Git should support collaboration rather than become a ceremony everyone performs without understanding.

Git Gets Easier When You Stop Memorizing Git

There are hundreds of Git commands, flags, and combinations.

Most developers don’t need to remember all of them.

The more useful mental model is knowing where your changes are.

Working directory → staging area → commit → branch → remote

Then the commands become actions on that model.

git add prepares changes.

git commit records them.

Branches separate lines of work.

merge and rebase bring histories together in different ways.

fetch, pull, and push synchronize local and remote repositories.

restore, revert, and reset undo different kinds of mistakes.

stash temporarily puts unfinished work aside.

Tags mark important points.

Once you see Git this way, an error message stops feeling like the repository has entered some mysterious state.

Usually, Git is simply telling you that the history in one place no longer matches the history somewhere else.

And once you know where your changes are and where you want them to go, the right Git command becomes much easier to find.