Learn/software dev/Git Workflows
Beginner~20 min read

Git Workflows

What Git is, the staging area and commits, core commands, branching and merging, remotes, merge vs rebase, resolving conflicts, .gitignore, team workflows, pull requests, undoing changes, and tags.

BranchingMergeRebaseRemotes

What is Git?

Git is a distributed version control system — it records snapshots of your project over time so you can review history, undo mistakes, and collaborate without overwriting each other's work. "Distributed" means every clone is a full copy of the repository, including its entire history; you can commit, branch, and browse history completely offline.

A repository (repo) is a project folder that Git is tracking. It lives in a hidden .git directory that stores every commit, branch, and configuration.

The Three Areas: Working Tree, Staging, Repository

Understanding Git's three areas removes almost all early confusion:

  • Working tree — the actual files you edit on disk.
  • Staging area (index) — a holding area where you assemble exactly what the next commit will contain.
  • Repository — the committed history stored in .git.

You edit files (working tree), pick which changes to include with git add (staging), then seal them into history with git commit. A commit is an immutable snapshot with an author, timestamp, message, and a unique SHA hash.

Why staging exists

Staging lets you craft focused commits. If you fixed a bug and also tweaked docs, you can stage and commit them separately — one clean change per commit makes history readable and easy to revert.

Core Commands

bash
# Start tracking a new project
git init

# Copy an existing remote repo to your machine
git clone https://github.com/org/repo.git

# See what changed and what's staged
git status

# Stage changes for the next commit
git add file.js          # one file
git add .                 # everything in current dir

# Record staged changes with a message
git commit -m "Add login validation"

# Stage tracked changes AND commit in one step
git commit -am "Fix typo"

# Review history
git log --oneline --graph --all

# See unstaged vs staged changes
git diff                  # working tree vs staging
git diff --staged         # staging vs last commit

Branching & Merging

A branch is a movable pointer to a commit — a cheap, independent line of work. You create a branch for each feature or fix so the main line stays stable, then merge it back when done.

bash
# Create and switch to a new branch
git switch -c feature/login     # modern (git 2.23+)
git checkout -b feature/login   # older, equivalent

# List and switch branches
git branch
git switch main

# Merge feature back into main
git switch main
git merge feature/login

# Delete a merged branch
git branch -d feature/login

A fast-forward merge just moves the pointer forward when main hasn't changed. When both branches advanced, Git creates a merge commit that ties the two histories together.

Remotes: push, pull, fetch

A remote is a shared copy of the repo hosted elsewhere (GitHub, GitLab). The default remote is named origin.

bash
# See configured remotes
git remote -v

# Download remote changes WITHOUT merging (safe to inspect first)
git fetch origin

# Fetch AND merge into current branch
git pull

# Upload local commits, setting upstream the first time
git push -u origin feature/login
git push                 # subsequent pushes
CommandDirectionTouches working tree?
git fetchRemote → local repoNo (just updates refs)
git pullRemote → local + mergeYes (fetch + merge)
git pushLocal → remoteNo

Merge vs Rebase

Both integrate changes from one branch into another, but differently. Merge preserves history exactly and adds a merge commit. Rebase replays your commits on top of another branch, producing a clean, linear history — but it rewrites commit hashes.

bash
# Merge: keeps both histories, adds a merge commit
git switch feature
git merge main

# Rebase: move feature's commits on top of latest main
git switch feature
git rebase main
# resolve any conflicts, then:
git rebase --continue
AspectMergeRebase
HistoryNon-linear, true recordLinear, tidy
Commit hashesPreservedRewritten
Safe on shared branchesYesNo — never rebase pushed history
Best forIntegrating shared branchesCleaning up your own local work

The golden rule of rebase

Never rebase commits that others may have already pulled. Rewriting shared history forces everyone else into painful conflicts. Rebase only your own local, unpushed commits.

Resolving Conflicts

A conflict happens when two branches change the same lines. Git pauses and marks the file with conflict markers; you edit it to the correct result, then stage and continue.

text
<<<<<<< HEAD
const timeout = 30;     // your change
=======
const timeout = 60;     // incoming change
>>>>>>> feature/login

# After editing to the final version, remove the markers, then:
git add config.js
git commit              # (for merge) or: git rebase --continue

# Give up and go back to before the merge
git merge --abort

.gitignore

A .gitignore file lists patterns Git should not track — build artifacts, dependencies, secrets, and editor files. Keep it committed so the whole team shares it.

bash
# dependencies
node_modules/
# build output
dist/
build/
# secrets and env
.env
*.key
# OS / editor cruft
.DS_Store
.vscode/

Ignore rules only affect untracked files. If a file is already committed, add it to .gitignore and run git rm --cached file to stop tracking it.

Common Team Workflows

Feature Branch Workflow

Create a branch per task, commit there, open a pull request, merge into main after review. This is the foundation of the others.

GitHub Flow

A lightweight flow: main is always deployable. Branch off main, open a PR, get review, merge, deploy. Ideal for continuous delivery and most teams in 2026.

Git Flow

A heavier model with long-lived main and develop branches plus feature/, release/, and hotfix/ branches. Suited to versioned software with scheduled releases; usually overkill for web apps that deploy continuously.

Pull Requests

A pull request (PR, or merge request) proposes merging one branch into another. It's where code review happens: teammates comment, CI runs tests, and once approved the branch is merged. The typical loop:

bash
git switch -c feature/checkout
# ... make changes, commit ...
git push -u origin feature/checkout
# open a PR on GitHub, address review feedback,
# push follow-up commits, then merge (squash is common)

Undoing Changes

Git gives you several tools to undo; the right one depends on whether the work is committed and whether it's already been shared.

bash
# Discard unstaged changes to a file
git restore file.js

# Unstage a file (keep the edits)
git restore --staged file.js

# Move HEAD back, keep changes staged/unstaged
git reset --soft HEAD~1     # undo commit, keep staged
git reset HEAD~1            # undo commit, keep in working tree
git reset --hard HEAD~1     # DANGER: discard commit AND changes

# Safely undo a PUBLISHED commit by adding an inverse commit
git revert <commit>

# Temporarily shelve work to switch tasks
git stash
git stash pop               # reapply and remove from stash
git stash list
CommandUse when
git restoreThrow away uncommitted edits
git resetUndo LOCAL commits not yet pushed
git revertUndo a commit that's already shared
git stashSet work aside without committing

Tags

A tag marks a specific commit, usually to label a release. Annotated tags store a message and author and are preferred for releases.

bash
git tag v1.4.0                         # lightweight
git tag -a v1.4.0 -m "Release 1.4.0"   # annotated
git push origin v1.4.0                 # tags aren't pushed by default
git push origin --tags                 # push all tags

Practice Exercises

  1. Initialize a new repo, create three commits, and view the history with git log --oneline --graph.
  2. Create a feature branch, make a change, and merge it back into main; observe whether it was a fast-forward.
  3. Deliberately create a merge conflict by editing the same line on two branches, then resolve it.
  4. On a local feature branch, rebase onto an updated main and compare the resulting history to a merge.
  5. Make a bad commit, then undo it two ways: with git reset (local) and with git revert (as if shared).
  6. Stash in-progress work, switch branches to fix an urgent bug, then pop the stash and continue.
  7. Create an annotated tag for a release and push it to the remote.

Section navigation