Git Workflows and Best Practices
By the end of this final Git lesson you'll run the real workflow professional teams use — feature branches, pull requests and code review — and know when to rebase vs merge , how to stash and tag , and the habits that keep a shared repo healthy.
Learn Git Workflows and Best Practices in our free Git course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…
Part of the free Git course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
- • Run the feature-branch workflow : branch, commit, merge through review
- • Open a pull request and respond to code review
- • Choose git rebase vs git merge — and obey the golden rule of rebasing
- • Shelve work in progress with git stash
- • Mark releases with git tag and semantic versioning
- • Apply pro habits: small commits, clear messages, .gitignore , protected main
1️⃣ The Feature-Branch Workflow
This is the workflow almost every team uses, and it rests on three rules: main is always deployable , every change lives on its own branch , and work reaches main only through a reviewed pull request . A branch is just an isolated line of work, so your half-finished feature can never break what's live. Read this worked example, then you'll run your own.
Your turn. The cycle below is almost complete — fill in the two blanks marked ___ using the hints in the comments, then run it.
2️⃣ Pull Requests & Code Review
A pull request (PR) is a request to merge your branch into main , with a built-in place for discussion. It's where code review happens (a teammate reads your changes and leaves comments) and where CI runs the tests automatically. You address feedback with ordinary commits — the PR updates itself. The single biggest factor in good review is size : small PRs get read carefully, giant ones get rubber-stamped.
3️⃣ Rebase vs Merge (and the Golden Rule)
Both merge and rebase combine two branches, but in opposite styles. Merge records a truthful, branching history and adds a merge commit. Rebase replays your commits on top of another branch to make history a clean straight line — but it rewrites commit IDs . That rewrite is exactly why there's a golden rule: never rebase history other people already have. Rebase your own private branch to tidy it; never rebase a shared branch like main .
4️⃣ Stashing Work & Tagging Releases
git stash shelves uncommitted changes so your working tree is instantly clean — perfect for "I'm mid-edit but must switch branches right now." git tag marks a commit as a named release so you can always find exactly what shipped. Use semantic versioning — MAJOR.MINOR.PATCH — so the version number itself tells people how big a change is.
Now you try. Fill in the four blanks to shelve some work, restore it, and tag a release — then run it.
5️⃣ Best Practices That Make You a Pro
Tools are only half the job — habits are the rest. Small, focused commits are easy to review and easy to revert. Clear messages in the imperative mood ("fix: stop double-charging on retry") turn your history into documentation. A good .gitignore keeps secrets and build junk out of the repo, and a protected main branch (no direct pushes, review required) stops accidents reaching production.
Pro Tips
- 💡 Keep branches short-lived. A branch that lives a day or two merges cleanly; one that lives three weeks becomes a conflict minefield.
- 💡 Rebase only your own branch. A quick git rebase main on your private feature branch before the PR keeps history tidy — never rebase shared branches.
- 💡 Pull before you push. git pull first so you're building on the latest work and avoid "rejected, non-fast-forward" errors.
- 💡 Use git lg . The alias log --oneline --graph --all draws your whole branch and merge history as a picture.
Common Errors (and the fix)
- Rebasing a shared branch — you ran git rebase on main (or any branch teammates have), and now everyone's history disagrees with yours. The fix: don't. Rebase only private branches; on shared ones, use git merge .
- "Updates were rejected (non-fast-forward)" → reaching for git push --force — force-pushing to a shared branch overwrites teammates' commits and is how work gets destroyed. Instead git pull to integrate, then push. If you truly must force on your own branch, use the safer git push --force-with-lease .
- Giant commits — a single commit touching 40 files with the message "updates" is impossible to review or revert. Make one small commit per logical change, with a clear message.
- Working directly on main — committing straight to main skips review and can break production. Always git checkout -b feature/... first, and protect main so it's enforced for everyone.
- "Committed a secret to .env " — once pushed, a key in history is compromised. Rotate the key immediately, then add the file to .gitignore so it never happens again.
📋 Quick Reference
Task
Command
Result
New feature branch
git checkout -b feature/x
branch + switch
Push & track
git push -u origin feature/x
ready for a PR
Merge a branch
git merge feature/x
keeps history
Tidy your branch
git rebase main
straight line
Shelve changes
git stash
clean tree
Restore changes
git stash pop
re-apply + drop
Tag a release
git tag -a v1.2.0 -m "..."
named release
Push a tag
git push origin v1.2.0
tag on remote
Frequently Asked Questions
Mini-Challenge: Ship a Feature Properly
No blanks this time — just a brief and an outline. Carry out a complete feature-branch cycle end to end in a test repo, then check your output against the example in the comments. This is exactly how real features get shipped.
🎉 Course Complete!
That's the whole Git course — from your first commit to running the same workflow professional teams use every day. You can now:
- ✅ Work the feature-branch workflow : branch, commit small, merge via review
- ✅ Open pull requests and respond to code review with normal commits
- ✅ Choose merge vs rebase — and never rebase shared history
- ✅ Shelve work with git stash and mark releases with annotated git tag
- ✅ Keep a repo healthy: small commits, clear messages, .gitignore , protected main
Where next? Put it into practice: open a pull request on a real GitHub project — your own, or your first open-source contribution. Then go deeper with GitHub Actions for CI/CD automation, and explore git bisect (find the commit that introduced a bug) and git cherry-pick (copy one commit between branches). You've got the foundation to collaborate on any codebase with confidence.
Practice quiz
In the feature-branch workflow, what is always true of main?
- It holds only experiments
- It should always be deployable
- It is never merged into
- It is a private branch
Answer: It should always be deployable. A core rule of the feature-branch workflow is that main stays always deployable; work happens on branches.
What is a pull request (PR)?
- A command that downloads commits
- A way to delete a branch
- A request to merge your branch into main, with built-in review
- A type of git tag
Answer: A request to merge your branch into main, with built-in review. A PR proposes merging your branch into main and is where code review and CI happen.
How do you address review feedback on an open PR?
- Make normal commits and push; the PR updates automatically
- Close the PR and open a new one
- Force-push over main
- Email the reviewer the diff
Answer: Make normal commits and push; the PR updates automatically. You push ordinary follow-up commits to the branch and the PR refreshes itself.
What does git rebase do to history?
- Adds a merge commit tying two branches
- Deletes commits permanently
- Pushes to the remote
- Replays your commits on top of another branch for a straight, linear history
Answer: Replays your commits on top of another branch for a straight, linear history. Rebase replays your commits onto the tip of another branch, producing a clean linear history but new commit IDs.
What is the golden rule of rebasing?
- Always rebase before every commit
- Never rebase commits other people already have
- Rebase main daily
- Rebase only shared branches
Answer: Never rebase commits other people already have. Rebasing rewrites commit IDs, so rebasing shared history breaks everyone else's copy.
What does git stash do?
- Shelves uncommitted changes so your working tree is clean
- Commits all changes
- Tags a release
- Pushes to origin
Answer: Shelves uncommitted changes so your working tree is clean. git stash tucks away uncommitted changes without a commit, perfect for switching branches quickly.
Are tags pushed to the remote by a normal git push?
- Yes, always
- Only annotated tags
- No — you must push them explicitly
- Only on the first push
Answer: No — you must push them explicitly. A normal push sends branch commits only; push a tag with git push origin v1.2.0 or all with --tags.
Which command creates an annotated release tag?
- git tag v1.2.0
- git tag --release v1.2.0
- git release v1.2.0
- git tag -a v1.2.0 -m "Release 1.2.0"
Answer: git tag -a v1.2.0 -m "Release 1.2.0". git tag -a -m creates an annotated tag that stores the author, date, and message, recommended for releases.
In semantic versioning MAJOR.MINOR.PATCH, what does a bump to MAJOR signal?
- A breaking change
- A small bug fix
- A documentation tweak
- A new patch release
Answer: A breaking change. MAJOR increments for breaking changes; MINOR for new features; PATCH for bug fixes.
Which is a best practice for a healthy shared repo?
- Commit secrets so the team can share them
- Push giant 40-file commits
- Protect main and require review plus passing CI before merging
- Always work directly on main
Answer: Protect main and require review plus passing CI before merging. Protecting main with required review and CI keeps unreviewed or broken code out of production.
Continue this course
- Previous: Resolving Merge Conflicts
- Next: The Staging Area & git diff