PYYUPSK

a self-taught dev from Thailand

Pongsakorn Thipayanate · Samutsakhon, TH ·webring

tutorial

Git from Zero to Pull Request: A Practical Guide

This guide covers practical Git knowledge from init to pull request. It skips jargon and cheat sheets, and covers only what you need on day one.

view .mdopen in claudeopen in chatgpt

You will use Git every day as a developer. Most tutorials either hand you a cheat sheet with no context, or they walk you through theory you will never need. This guide does neither. It gives you the practical knowledge I wish someone had given me on day one. It covers what Git does, how to use it, and why the commands work the way they do.

What Git Is and Why It Matters

Git is a version control system. It tracks changes to your files over time. Every change you save becomes part of a history. You can browse, search, and undo that history.

Teams use Git because it solves three problems at once. Git keeps a complete history of every change. Git lets multiple people work on the same codebase without overwriting each other. Git also makes rollbacks easy when something breaks.

One distinction matters early on. Git is the tool that runs on your machine. GitHub is a hosting platform where you store Git repositories online. You can use Git without GitHub. Most teams use both together.

Installation and Setup

On macOS with Homebrew:

brew install git

On Arch Linux:

sudo pacman -S git

On Ubuntu or Debian:

sudo apt install git

On Windows, download the installer from git-scm.com and follow the prompts. The defaults work well.

After installing, configure your identity. Git attaches this information to every commit you make:

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

Verify everything works:

git --version

Repositories

A repository is a project tracked by Git. You create one in two ways.

git init creates a new repository in the current directory:

mkdir my-project
cd my-project
git init

git clone copies an existing repository from a remote URL:

git clone https://github.com/user/repo.git

Both methods create a hidden .git/ directory inside your project. This directory is the repository. It holds the entire history of your project. If you delete this directory, you lose the history. The rest of your project files make up the working directory.

Git tracks files through three states. The working directory is where you edit files. The staging area is where you prepare changes for a commit. The repository is where Git stores committed snapshots permanently. This flow of edit, stage, and commit is the mental model that makes every other Git command make sense.

Tracking Files and Making Commits

Check what Git sees:

git status

This command shows which files changed, which files are staged, and which files are untracked. Run this command often. It is your orientation command.

Stage files for a commit:

git add index.html           # stage one file
git add src/                 # stage an entire directory
git add .                    # stage everything

Create a commit, which is a snapshot of your staged changes:

git commit -m "Add homepage layout"

A commit is a snapshot, not a diff. Git stores the complete state of your staged files at that point in time. Git computes diffs later by comparing snapshots.

A good commit message uses imperative mood with a short subject line, such as “Add login form,” “Fix null check in user service,” or “Remove deprecated API endpoint.” Describe what the commit does, not what you did. Keep the subject line under 50 characters. If you need more detail, leave a blank line and add a body.

View your commit history:

git log
git log --oneline           # compact view

Some files must never be tracked, such as build artifacts, environment variables, and dependency directories. Create a .gitignore file in your project root:

node_modules/
.env
dist/
*.log

Git ignores anything that matches these patterns. Add .gitignore early in the project. Removing a file from Git after you commit it takes more work than preventing the tracking in the first place.

Using GitHub

Create a repository on GitHub through the web interface. If you already have local commits, do not initialize the repository with a README. This creates a conflict.

Link your local repository to the remote:

git remote add origin https://github.com/your-username/your-repo.git
git push -u origin main

The -u flag sets origin main as the default upstream. After this, future pushes only need git push.

GitHub supports two authentication methods: HTTPS and SSH. HTTPS prompts for credentials. Use a personal access token instead of your password. SSH uses a key pair and never prompts after setup. SSH causes less friction day to day. Set it up once with ssh-keygen, then add the public key to your GitHub settings.

Clone someone else’s repository to get a local copy:

git clone https://github.com/other-user/their-repo.git

Beyond code hosting, GitHub adds a social layer. README files describe the project. Issues track bugs and feature requests. Stars bookmark repositories you find useful. These are GitHub features, not Git features.

Branching

A branch is an independent line of development. The default branch is main. Every other branch diverges from it and can be merged back later.

Create and list branches:

git branch                  # list branches
git branch feature/login    # create a branch

Switch to a branch:

git switch feature/login

Create and switch in one step:

git switch -c feature/login

I use git switch instead of git checkout for branch operations. Git added switch specifically for this purpose, and it causes less confusion. checkout does too many different things.

Branching gives you isolation. You can work on a feature without affecting main. You can experiment without risk and delete a branch if it does not work out. Your teammates do the same, so nobody interferes with anyone else’s work.

Name branches descriptively: feature/user-profile, fix/login-redirect, chore/update-deps. The prefix tells reviewers what kind of change to expect.

Pull Requests

A pull request asks to merge your branch into another branch, usually main. This is where code review happens.

Push your branch to GitHub first:

git push -u origin feature/login

Then create the pull request, often shortened to PR, on GitHub. Write a clear description that covers what changed, why it changed, and how to test it. Assign reviewers. Wait for the CI (continuous integration) checks to pass.

Keep pull requests small and focused. Reviewers read a 50-line pull request carefully. Reviewers skim a 500-line pull request instead. If you work on a large feature, break it into smaller pull requests that build on each other.

Respond to review feedback by pushing more commits to the same branch. The pull request updates automatically.

Merging and Resolving Conflicts

When a reviewer approves a pull request, you merge it. GitHub offers several merge strategies. You will encounter two of them most often:

A fast-forward merge moves the branch pointer forward when there is no divergence between branches. The history stays linear. This happens when main did not change since you created your branch.

A merge commit creates a new commit that combines two branches. This happens when both branches have new commits. The merge commit has two parents, one from each branch.

Conflicts happen when two branches change the same lines in the same file. Git cannot decide which version to keep, so it marks the file:

<<<<<<< HEAD (Current Change)
const greeting = "Hello";
=======
const greeting = "Hi there";
>>>>>>> feature/login (Incoming Change)

Everything between <<<<<<< HEAD and ======= is your current branch. Everything between ======= and >>>>>>> is the incoming branch.

To resolve the conflict:

  1. Delete the markers.
  2. Keep the code you want.
  3. Stage the file.
  4. Commit the change.
git add src/greeting.ts
git commit -m "Resolve greeting conflict"

If you want to start over, run this command:

git merge --abort

This command resets everything to the state before the merge attempt. Nothing is lost.

Advanced Git

git stash shelves your uncommitted changes, so you can switch branches without committing half-finished work. git stash saves the changes, and git stash pop restores them. I use this command several times a day. When someone asks me to review their pull request, I stash my work, switch branches, and come back later.

git rebase replays your branch’s commits on top of another branch and creates a linear history. I use rebase for local cleanup before I push, and I use merge to combine shared branches. Follow this rule: do not rebase commits that other people already based their work on.

git cherry-pick applies one specific commit from one branch to another, without merging the entire branch. This helps when a bug fix on a feature branch must land on main immediately. The command git cherry-pick abc1234 copies just that one commit.

git bisect runs a binary search through your commit history to find which commit introduced a bug. You mark one known good commit and one known bad commit. Git then walks you through the midpoints until it finds the exact commit. On large repositories with hundreds of commits between releases, this command saves real time.

Interactive rebase, run as git rebase -i HEAD~5, lets you edit, squash, reorder, or drop recent commits before you push. I use this command to turn a messy series of WIP commits into a clear history. Squash the fixup commits, reword the messages, and push a clean branch.

Most junior developers do not use these commands every day. But knowing they exist means you will reach for them when a situation calls for it, instead of working around the problem by hand.

← All writings