Git and GitHub Guide: Essential Git Commands Every Developer Should Know

Git commands are the small set of instructions that record every change in a project, let you undo mistakes and allow a whole team to work on the same code. I have managed web projects since 2012. In my experience, the health of a project shows up first in its version control history. This guide covers the commands you use every day, branching strategies, pull requests and merge conflicts.
I am not covering the open source contribution workflow here. Forks, upstream remotes and contributor guidelines deserve their own article. Instead, the focus is on working safely with Git and GitHub in your own projects or in a company team. You can find related technical articles in the software category.
What are Git commands, and how is Git different from GitHub?
Git commands are terminal instructions for Git, the distributed version control system: they save snapshots, show history, create branches and sync with remote repositories. GitHub, by contrast, is a hosting platform for Git repositories that adds team features such as pull requests, code review, issues and automated workflows.
This distinction matters because many beginners treat the two as the same thing. First, Git is a program on your machine, and it works fully offline. GitHub, on the other hand, is a service, and GitLab and Bitbucket offer similar ones. In other words, once you learn Git, your skills carry over to every platform.
Linus Torvalds wrote Git in 2005 to manage development of the Linux kernel. The official reference and the free Pro Git book live on git-scm.com. Whenever a command confuses you, that is the place to check first.
Why should every developer use version control?
Working without version control is like writing a novel in an editor with no save history. For example, if you break a file, you cannot return to yesterday's version. Worse, when two people edit the same file, nobody knows whose changes got lost.
The most expensive mistakes I have seen came from projects run on folders named "final_final_v3.zip". On one client site, an old copy overwrote a live fix, the bug came back and nobody noticed for days. That scenario is almost impossible in a team using Git. Because Git records who changed what, when and why, the history always tells the story.
Here is what version control gives you:
- A dated, annotated history of every change.
- The ability to undo a bad change in minutes.
- Safe parallel work on several features at once.
- Code review before anything reaches production.
- A solid base for automated deployment.
These benefits apply to solo freelancers too. When a client dislikes a change, rolling back takes seconds. Likewise, if your laptop dies, the remote copy keeps your work and its full history. Put simply, version control is insurance, whatever the size of your team.
How do you install Git and set it up for the first time?
Install Git for your operating system first. On macOS, the Xcode command line tools or Homebrew work well. On Windows, use Git for Windows, and on Linux your package manager. Then run git --version in a terminal to confirm the install.
Next, introduce yourself to Git. Git signs every commit with this identity, so use the right email address. Also, if it matches your GitHub account, your commits will link to your profile.
- Set your name with git config --global user.name "Your Name".
- Add your email with git config --global user.email "you@example.com".
- Choose the default branch name with git config --global init.defaultBranch main.
- Review everything with git config --list.
The branch name setting looks minor. However, it keeps your team consistent, and GitHub also uses main as the default for new repositories. As a result, you avoid a naming mismatch between local and remote.
Which Git commands start a new repository?
You have two ways to begin: create a repository from scratch or copy an existing one. For a new project, open the folder and run git init. This command creates a hidden .git directory, and Git keeps the entire history there.
To join an existing project, use git clone with the repository URL. In practice, it downloads the files, the history and the remote connection in one step. For example, a developer joining an agency team usually runs just this command on day one.
Right after that, add a .gitignore file. Specifically, it lists files Git should never track: dependency folders such as node_modules, build output and, above all, .env files with secrets. I have watched teams push a password by accident. Removing it from history later takes far more effort than blocking it up front. Use a password generator for strong credentials, but never commit them.
What are the essential Git commands for daily work?
Every change in Git passes through three areas: the working directory, the staging area and the repository history. You edit a file, stage the parts you want to save, then commit them. This three step model feels heavy at first. In practice, it lets you choose exactly which change goes into which commit.
| Command | What it does | When to use it |
|---|---|---|
| git status | Shows modified, staged and untracked files | Before and after every step |
| git add | Moves changes into the staging area | When selecting what to commit |
| git commit -m | Saves staged changes with a message | When a logical piece of work is done |
| git log --oneline | Lists history in short form | To recall what happened |
| git diff | Shows line by line differences | To review before committing |
| git push | Sends commits to the remote | When your work is ready to share |
| git pull | Fetches and merges remote changes | At the start of the day and before pushing |
As a habit, I run status and then diff before every commit. Those two commands stop most accidental commits of debug lines or stray test files.
How do you write a good commit message?
A commit message is a note to yourself six months from now. Messages like "fix" or "update" feel fine today. Later, when you hunt for the source of a bug, they tell you nothing. Therefore, a good message states what changed and why.
The Git community broadly agrees on these conventions:
- Keep the summary line short; around 50 characters is the common guideline.
- Write the summary in the imperative: "Add phone validation to checkout form".
- Leave a blank line, then explain details in the body if needed.
- Keep one logical change per commit.
- Reference the related ticket number.
Some teams follow the Conventional Commits format and prefix messages with a type such as feat, fix or docs. That format makes automated release notes easier. A small team does not strictly need it. Still, you will feel the benefit as the project grows.
What are Git branches, and how do you create them?
A branch is an independent line of work that splits off from the main code. As a result, you can build a feature without touching main. When the work is done, you merge the branch back. If the experiment fails, you delete the branch and move on.
Branches in Git are cheap because a branch is just a lightweight pointer to a commit. So open a separate branch for every task. These are the branch commands you will use most:
- List local branches with git branch.
- Create and switch to a new branch with git switch -c feature/contact-form.
- Return to the main line with git switch main.
- Delete a merged branch with git branch -d plus its name.
Older tutorials use git checkout for these tasks. Git 2.23 introduced switch and restore, which split the two jobs checkout used to handle. I recommend switch to beginners because it carries less risk of wiping out file changes by mistake. For clean, readable branch names, a slug generator can help too.
Which branching strategy should your team choose?
A branching strategy is the shared rule for naming, opening and merging branches. The right choice depends on team size and release frequency. No single option wins everywhere. That said, a poor fit brings both delays and confusion.
| Strategy | Core idea | Best fit |
|---|---|---|
| GitHub Flow | One main branch, short lived feature branches, pull requests | Web teams that ship continuously |
| Git Flow | main, develop, feature, release and hotfix branches | Software with scheduled, versioned releases |
| Trunk based development | Very short branches merged into main several times a day | Teams with strong test automation |
For websites and web apps, I mostly use GitHub Flow. The rules are simple: main is always deployable, every task lives on its own branch and merges happen through pull requests. Git Flow makes more sense for products with version numbers, such as mobile apps.
Whichever you pick, write the rules down. After all, a new developer should learn your branch naming on day one.
Should you merge or rebase when combining branches?
Both bring changes from one branch into another, but they record history differently. Merge creates a new merge commit where the branches meet and preserves the true history. Rebase replays your commits on top of the target branch, which gives you a straight line.
In practice, the difference looks like this:
- Merge is safe because it never touches existing commits.
- Rebase gives cleaner history, but it rewrites commit IDs.
- Rebasing a shared branch breaks your teammates' history.
- Rebasing your own local branch to catch up is very handy.
My rule is simple. I rebase only branches that belong to me and that nobody else has pulled. On shared branches, however, I always merge. The Pro Git book states the same golden rule: do not rewrite commits that other people build on.
How do you work with a remote repository on GitHub?
A remote is the server copy of your project, and the team syncs through it. When you clone, Git adds the remote automatically under the name origin. If you started locally, create an empty repository on GitHub first. Then connect it with git remote add origin and the URL.
Three remote commands often get mixed up. Fetch downloads remote changes but leaves your files alone. Pull, in contrast, runs a fetch and then merges. Push sends your commits to the server. When I am unsure, I fetch first and inspect the difference.
You can authenticate with HTTPS and a personal access token, or with an SSH key. GitHub stopped accepting account passwords for Git operations in 2021. Therefore, you cannot push with your login password anymore. I prefer SSH keys because, once set up, they never prompt you again.
What is a pull request, and how do you open one?
A pull request is a formal proposal to merge the changes on one branch into another. GitHub shows the changed lines, lets reviewers comment and runs automated checks. In short, the pull request is the gate where the team looks at code before it ships.
A typical pull request flows like this:
- Create a branch and commit your work there.
- Push it with git push -u origin branch-name.
- Click "Compare and pull request" on GitHub.
- Explain what you changed and why in the title and description.
- Assign reviewers and wait for the checks.
- Merge after approval, then delete the branch.
Also, never leave the description empty. When reviewers know what to look for, the review gets faster and deeper. For interface changes, screenshots help a lot. The GitHub pull request documentation covers every option in detail.
What should you look for in a code review?
Code review is less about hunting bugs and more about building shared understanding. The reviewer checks whether the code works, reads well and follows project conventions. Good review comments target the code, not the person.
These are the questions I ask when I review. Does the change actually solve the problem in the description? Did the author consider edge cases? Are there tests, and do they also cover failures? Would someone reading this in six months understand it? I also watch for changes that affect performance. On websites, a tiny change can slow a page down. To measure that, see my guide on running a Google Lighthouse performance test.
Pull request size also shapes review quality. Nobody can carefully read a proposal that touches hundreds of files. So split the work into small pieces and open a separate pull request for each one.
Why do merge conflicts happen?
A conflict appears when two branches change the same lines of the same file in different ways. Git cannot decide which version to keep, so it hands the decision to you. You also get a conflict when one branch deletes a file that the other one edits.
A conflict is not an error. Instead, it is a natural result of parallel work. Still, you can make conflicts rarer:
- Keep branches short lived and merge within a few days.
- Pull changes from main into your branch every morning.
- Talk to teammates who work on the same files.
- Run the same code formatter with the same settings across the team.
The last point looks small, yet it solves a problem I see often. One developer's editor reformats a whole file. As a result, every line now counts as changed, and meaningless conflicts follow.
How do you resolve a merge conflict step by step?
No need to panic when Git reports a conflict. First, run git status to see which files conflict. Next, open them and you will find markers that Git inserted. The line of less than signs marks your version, the row of equals signs separates the two, and the greater than signs close the incoming version.
- Open the conflicting file and read both versions carefully.
- Write the correct result by hand; sometimes one side wins, sometimes you combine both.
- Delete all conflict marker lines.
- Mark the file as resolved with git add.
- Finish with git commit for a merge, or git rebase --continue for a rebase.
If things get messy, back out. The command git merge --abort returns you to the state before the merge started. After resolving, always run and test the application. The syntax may look fine while the logic is broken. GitHub offers a browser editor for simple conflicts. For complex ones, resolving locally is safer.
Which Git commands undo a bad change?
The right undo depends on where the change lives. For example, discarding an unsaved edit is one thing. Reverting a commit your team already pulled is another. Picking the right command keeps your teammates' history intact.
| Situation | Command | Note |
|---|---|---|
| Discard uncommitted edits in a file | git restore file | The edit is gone for good |
| Unstage a file | git restore --staged file | Your edit stays in place |
| Fix the last commit message | git commit --amend | Only before pushing |
| Undo a shared commit | git revert commit-id | Adds a new inverse commit |
| Drop local commits | git reset --hard | Careful: you can lose work |
In team work, my rule is clear: revert for pushed commits, reset for local work. Revert does not erase history; it adds a corrective commit on top, so everyone's repository stays compatible. And if you lose something by accident, git reflog can save the day. It remembers every position your branch pointed to for a while.
How do you park unfinished work with git stash?
Imagine you are halfway through a feature when an urgent bug report arrives. Your code is not ready to commit, yet you need to switch branches. Stash exists for exactly this moment. It shelves your changes and cleans the working directory.
In practice, usage is simple. Save the work with git stash push -m "form validation half done", fix the urgent bug, then return to your branch. After that, run git stash pop to pick up where you left off. If the shelf holds several entries, git stash list shows them all.
Do not treat stash as long term storage, though. Changes that sit on the shelf for weeks get forgotten and then collide with main. If a task will last longer than a day, open a branch and make a temporary commit instead.
How do you find the source of a bug in Git history?
When a bug shows up, the first question is usually simple: when did this break? Git history offers several strong tools to answer it. The log command shows who changed what and when. Blame, meanwhile, shows who last touched each line of a file.
For harder cases, use bisect. Bisect runs a binary search between a good commit and a bad one. At each step, you mark the current version as good or bad, and Git halves the range. As a result, you find the guilty change among hundreds of commits in a handful of steps.
These tools only work as well as your commits do. Therefore, small, descriptive commits make bisect fast and precise. On the other hand, a giant commit called "this week's work" stops the search cold. Then you are back to reading line by line.
Why do branch protection and automated checks matter on GitHub?
Branch protection rules block direct pushes to main and set conditions for merging. For example, you can require at least one approval, passing tests and an up to date branch. That way, even your most senior developer cannot ship unchecked code on a tired evening.
With GitHub Actions, every pull request can run tests, format checks and builds automatically. On web projects, I also add link and SEO checks. One example is confirming that robots.txt does not block the whole site by accident. A robots.txt generator helps you write that file correctly.
Automated checks do not replace human review. Instead, they free reviewers from repetitive work. Reviewers can then focus on architecture and logic instead of commas. The machine checks the boring parts, and people check the meaningful ones.
How do you connect Git to deployment on web projects?
Git's biggest payoff is repeatable deployment. Instead of uploading files one by one over FTP, every change merged into main goes to the server automatically. This approach is fast, and you always know which version is live.
My usual setup has three environments: local development, a staging server and the live site. First, a merged pull request updates staging. After checks pass, I tag a release and ship it to production. You create release tags with git tag. If something breaks, going back to the previous tag takes minutes.
This discipline matters even more during a redesign. If URLs change, review the redirects in the same pull request. My website migration SEO checklist covers that part. On larger codebases split into independent parts, such as micro frontends, repository structure deserves early planning. For business sites, I build this pipeline from the start as part of my web design service.
How do you set up aliases for Git commands?
Shortening commands you type dozens of times a day saves real time. Git offers a feature called alias for this. For example, after git config --global alias.st status, typing git st does the same job as status.
In practice, these are the aliases I find most useful:
- st for status, ci for commit and sw for switch.
- lg for log --oneline --graph --all, which draws history as a graph.
- undo for reset --soft HEAD~1, which moves the last commit back to staging.
Still, keep it modest. Learn the real Git commands before you learn the shortcuts. On another machine or a teammate's laptop, your aliases will not exist. Moreover, documentation and error messages always use full command names. An alias is a comfort tool, not a replacement for the basics.
What Git mistakes do beginners make most often?
The mistakes I see in teams look remarkably alike over the years. Fortunately, each one has a simple fix. These come up most often:
- Working directly on main and pushing everything there.
- Coding for days without a commit, then pushing one enormous change.
- Committing passwords, API keys or .env files.
- Force pushing to a shared branch and wiping out someone else's work.
- Pushing without pulling first and ignoring the warning.
- Writing vague commit messages that make history unreadable.
Be especially careful with force pushes. If you must force push after rebasing your own branch, use git push --force-with-lease. This option stops the push when the remote branch has changes you have not seen. As a result, you cannot silently overwrite a colleague's work.
What is the best way to learn Git commands?
You learn Git by using it, not by memorizing it. Start with the seven Git commands of the daily loop: status, add, commit, log, diff, pull and push. Once those feel natural, move on to branches, then merging and conflict resolution. Rebase, bisect and reflog will come as you need them.
For example, create a throwaway repository and cause a conflict on purpose. Change the same line on two branches and try to merge them. Living through a resolution once, in a calm setting, keeps you from panicking on a real project. Graphical clients help too. Even so, knowing which command runs underneath will rescue you when something goes wrong.
Finally, write a short team guide covering branch naming, commit format, a pull request template and the merge method. It rarely needs more than a page. However, it makes every newcomer's first week easier. A team that uses Git well has fewer emergencies and more predictable releases.




